DynamicHlsController.cs 115 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel.DataAnnotations;
  4. using System.Diagnostics.CodeAnalysis;
  5. using System.Globalization;
  6. using System.IO;
  7. using System.Linq;
  8. using System.Text;
  9. using System.Threading;
  10. using System.Threading.Tasks;
  11. using Jellyfin.Api.Constants;
  12. using Jellyfin.Api.Helpers;
  13. using Jellyfin.Api.Models.PlaybackDtos;
  14. using Jellyfin.Api.Models.StreamingDtos;
  15. using MediaBrowser.Common.Configuration;
  16. using MediaBrowser.Common.Net;
  17. using MediaBrowser.Controller.Configuration;
  18. using MediaBrowser.Controller.Devices;
  19. using MediaBrowser.Controller.Dlna;
  20. using MediaBrowser.Controller.Library;
  21. using MediaBrowser.Controller.MediaEncoding;
  22. using MediaBrowser.Controller.Net;
  23. using MediaBrowser.Model.Configuration;
  24. using MediaBrowser.Model.Dlna;
  25. using MediaBrowser.Model.Entities;
  26. using MediaBrowser.Model.IO;
  27. using MediaBrowser.Model.Net;
  28. using Microsoft.AspNetCore.Authorization;
  29. using Microsoft.AspNetCore.Http;
  30. using Microsoft.AspNetCore.Mvc;
  31. using Microsoft.Extensions.Configuration;
  32. using Microsoft.Extensions.Logging;
  33. using Microsoft.Net.Http.Headers;
  34. namespace Jellyfin.Api.Controllers
  35. {
  36. /// <summary>
  37. /// Dynamic hls controller.
  38. /// </summary>
  39. [Route("")]
  40. [Authorize(Policy = Policies.DefaultAuthorization)]
  41. public class DynamicHlsController : BaseJellyfinApiController
  42. {
  43. private readonly ILibraryManager _libraryManager;
  44. private readonly IUserManager _userManager;
  45. private readonly IDlnaManager _dlnaManager;
  46. private readonly IAuthorizationContext _authContext;
  47. private readonly IMediaSourceManager _mediaSourceManager;
  48. private readonly IServerConfigurationManager _serverConfigurationManager;
  49. private readonly IMediaEncoder _mediaEncoder;
  50. private readonly IFileSystem _fileSystem;
  51. private readonly ISubtitleEncoder _subtitleEncoder;
  52. private readonly IConfiguration _configuration;
  53. private readonly IDeviceManager _deviceManager;
  54. private readonly TranscodingJobHelper _transcodingJobHelper;
  55. private readonly INetworkManager _networkManager;
  56. private readonly ILogger<DynamicHlsController> _logger;
  57. private readonly EncodingHelper _encodingHelper;
  58. private readonly TranscodingJobType _transcodingJobType = TranscodingJobType.Hls;
  59. /// <summary>
  60. /// Initializes a new instance of the <see cref="DynamicHlsController"/> class.
  61. /// </summary>
  62. /// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
  63. /// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
  64. /// <param name="dlnaManager">Instance of the <see cref="IDlnaManager"/> interface.</param>
  65. /// <param name="authContext">Instance of the <see cref="IAuthorizationContext"/> interface.</param>
  66. /// <param name="mediaSourceManager">Instance of the <see cref="IMediaSourceManager"/> interface.</param>
  67. /// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
  68. /// <param name="mediaEncoder">Instance of the <see cref="IMediaEncoder"/> interface.</param>
  69. /// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
  70. /// <param name="subtitleEncoder">Instance of the <see cref="ISubtitleEncoder"/> interface.</param>
  71. /// <param name="configuration">Instance of the <see cref="IConfiguration"/> interface.</param>
  72. /// <param name="deviceManager">Instance of the <see cref="IDeviceManager"/> interface.</param>
  73. /// <param name="transcodingJobHelper">Instance of the <see cref="TranscodingJobHelper"/> class.</param>
  74. /// <param name="networkManager">Instance of the <see cref="INetworkManager"/> interface.</param>
  75. /// <param name="logger">Instance of the <see cref="ILogger{DynamicHlsController}"/> interface.</param>
  76. public DynamicHlsController(
  77. ILibraryManager libraryManager,
  78. IUserManager userManager,
  79. IDlnaManager dlnaManager,
  80. IAuthorizationContext authContext,
  81. IMediaSourceManager mediaSourceManager,
  82. IServerConfigurationManager serverConfigurationManager,
  83. IMediaEncoder mediaEncoder,
  84. IFileSystem fileSystem,
  85. ISubtitleEncoder subtitleEncoder,
  86. IConfiguration configuration,
  87. IDeviceManager deviceManager,
  88. TranscodingJobHelper transcodingJobHelper,
  89. INetworkManager networkManager,
  90. ILogger<DynamicHlsController> logger)
  91. {
  92. _libraryManager = libraryManager;
  93. _userManager = userManager;
  94. _dlnaManager = dlnaManager;
  95. _authContext = authContext;
  96. _mediaSourceManager = mediaSourceManager;
  97. _serverConfigurationManager = serverConfigurationManager;
  98. _mediaEncoder = mediaEncoder;
  99. _fileSystem = fileSystem;
  100. _subtitleEncoder = subtitleEncoder;
  101. _configuration = configuration;
  102. _deviceManager = deviceManager;
  103. _transcodingJobHelper = transcodingJobHelper;
  104. _networkManager = networkManager;
  105. _logger = logger;
  106. _encodingHelper = new EncodingHelper(_mediaEncoder, _fileSystem, _subtitleEncoder, _configuration);
  107. }
  108. /// <summary>
  109. /// Gets a video hls playlist stream.
  110. /// </summary>
  111. /// <param name="itemId">The item id.</param>
  112. /// <param name="container">The video container. Possible values are: ts, webm, asf, wmv, ogv, mp4, m4v, mkv, mpeg, mpg, avi, 3gp, wmv, wtv, m2ts, mov, iso, flv. </param>
  113. /// <param name="static">Optional. If true, the original file will be streamed statically without any encoding. Use either no url extension or the original file extension. true/false.</param>
  114. /// <param name="params">The streaming parameters.</param>
  115. /// <param name="tag">The tag.</param>
  116. /// <param name="deviceProfileId">Optional. The dlna device profile id to utilize.</param>
  117. /// <param name="playSessionId">The play session id.</param>
  118. /// <param name="segmentContainer">The segment container.</param>
  119. /// <param name="segmentLength">The segment lenght.</param>
  120. /// <param name="minSegments">The minimum number of segments.</param>
  121. /// <param name="mediaSourceId">The media version id, if playing an alternate version.</param>
  122. /// <param name="deviceId">The device id of the client requesting. Used to stop encoding processes when needed.</param>
  123. /// <param name="audioCodec">Optional. Specify a audio codec to encode to, e.g. mp3. If omitted the server will auto-select using the url's extension. Options: aac, mp3, vorbis, wma.</param>
  124. /// <param name="enableAutoStreamCopy">Whether or not to allow automatic stream copy if requested values match the original source. Defaults to true.</param>
  125. /// <param name="allowVideoStreamCopy">Whether or not to allow copying of the video stream url.</param>
  126. /// <param name="allowAudioStreamCopy">Whether or not to allow copying of the audio stream url.</param>
  127. /// <param name="breakOnNonKeyFrames">Optional. Whether to break on non key frames.</param>
  128. /// <param name="audioSampleRate">Optional. Specify a specific audio sample rate, e.g. 44100.</param>
  129. /// <param name="maxAudioBitDepth">Optional. The maximum audio bit depth.</param>
  130. /// <param name="audioBitRate">Optional. Specify an audio bitrate to encode to, e.g. 128000. If omitted this will be left to encoder defaults.</param>
  131. /// <param name="audioChannels">Optional. Specify a specific number of audio channels to encode to, e.g. 2.</param>
  132. /// <param name="maxAudioChannels">Optional. Specify a maximum number of audio channels to encode to, e.g. 2.</param>
  133. /// <param name="profile">Optional. Specify a specific an encoder profile (varies by encoder), e.g. main, baseline, high.</param>
  134. /// <param name="level">Optional. Specify a level for the encoder profile (varies by encoder), e.g. 3, 3.1.</param>
  135. /// <param name="framerate">Optional. A specific video framerate to encode to, e.g. 23.976. Generally this should be omitted unless the device has specific requirements.</param>
  136. /// <param name="maxFramerate">Optional. A specific maximum video framerate to encode to, e.g. 23.976. Generally this should be omitted unless the device has specific requirements.</param>
  137. /// <param name="copyTimestamps">Whether or not to copy timestamps when transcoding with an offset. Defaults to false.</param>
  138. /// <param name="startTimeTicks">Optional. Specify a starting offset, in ticks. 1 tick = 10000 ms.</param>
  139. /// <param name="width">Optional. The fixed horizontal resolution of the encoded video.</param>
  140. /// <param name="height">Optional. The fixed vertical resolution of the encoded video.</param>
  141. /// <param name="videoBitRate">Optional. Specify a video bitrate to encode to, e.g. 500000. If omitted this will be left to encoder defaults.</param>
  142. /// <param name="subtitleStreamIndex">Optional. The index of the subtitle stream to use. If omitted no subtitles will be used.</param>
  143. /// <param name="subtitleMethod">Optional. Specify the subtitle delivery method.</param>
  144. /// <param name="maxRefFrames">Optional.</param>
  145. /// <param name="maxVideoBitDepth">Optional. The maximum video bit depth.</param>
  146. /// <param name="requireAvc">Optional. Whether to require avc.</param>
  147. /// <param name="deInterlace">Optional. Whether to deinterlace the video.</param>
  148. /// <param name="requireNonAnamorphic">Optional. Whether to require a non anamporphic stream.</param>
  149. /// <param name="transcodingMaxAudioChannels">Optional. The maximum number of audio channels to transcode.</param>
  150. /// <param name="cpuCoreLimit">Optional. The limit of how many cpu cores to use.</param>
  151. /// <param name="liveStreamId">The live stream id.</param>
  152. /// <param name="enableMpegtsM2TsMode">Optional. Whether to enable the MpegtsM2Ts mode.</param>
  153. /// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h265, h264, mpeg4, theora, vpx, wmv.</param>
  154. /// <param name="subtitleCodec">Optional. Specify a subtitle codec to encode to.</param>
  155. /// <param name="transcodingReasons">Optional. The transcoding reason.</param>
  156. /// <param name="audioStreamIndex">Optional. The index of the audio stream to use. If omitted the first audio stream will be used.</param>
  157. /// <param name="videoStreamIndex">Optional. The index of the video stream to use. If omitted the first video stream will be used.</param>
  158. /// <param name="context">Optional. The <see cref="EncodingContext"/>.</param>
  159. /// <param name="streamOptions">Optional. The streaming options.</param>
  160. /// <param name="enableAdaptiveBitrateStreaming">Enable adaptive bitrate streaming.</param>
  161. /// <response code="200">Video stream returned.</response>
  162. /// <returns>A <see cref="FileResult"/> containing the playlist file.</returns>
  163. [HttpGet("Videos/{itemId}/master.m3u8")]
  164. [HttpHead("Videos/{itemId}/master.m3u8", Name = "HeadMasterHlsVideoPlaylist")]
  165. [ProducesResponseType(StatusCodes.Status200OK)]
  166. public async Task<ActionResult> GetMasterHlsVideoPlaylist(
  167. [FromRoute] Guid itemId,
  168. [FromRoute] string? container,
  169. [FromQuery] bool? @static,
  170. [FromQuery] string? @params,
  171. [FromQuery] string? tag,
  172. [FromQuery] string? deviceProfileId,
  173. [FromQuery] string? playSessionId,
  174. [FromQuery] string? segmentContainer,
  175. [FromQuery] int? segmentLength,
  176. [FromQuery] int? minSegments,
  177. [FromQuery, Required] string? mediaSourceId,
  178. [FromQuery] string? deviceId,
  179. [FromQuery] string? audioCodec,
  180. [FromQuery] bool? enableAutoStreamCopy,
  181. [FromQuery] bool? allowVideoStreamCopy,
  182. [FromQuery] bool? allowAudioStreamCopy,
  183. [FromQuery] bool? breakOnNonKeyFrames,
  184. [FromQuery] int? audioSampleRate,
  185. [FromQuery] int? maxAudioBitDepth,
  186. [FromQuery] int? audioBitRate,
  187. [FromQuery] int? audioChannels,
  188. [FromQuery] int? maxAudioChannels,
  189. [FromQuery] string? profile,
  190. [FromQuery] string? level,
  191. [FromQuery] float? framerate,
  192. [FromQuery] float? maxFramerate,
  193. [FromQuery] bool? copyTimestamps,
  194. [FromQuery] long? startTimeTicks,
  195. [FromQuery] int? width,
  196. [FromQuery] int? height,
  197. [FromQuery] int? videoBitRate,
  198. [FromQuery] int? subtitleStreamIndex,
  199. [FromQuery] SubtitleDeliveryMethod subtitleMethod,
  200. [FromQuery] int? maxRefFrames,
  201. [FromQuery] int? maxVideoBitDepth,
  202. [FromQuery] bool? requireAvc,
  203. [FromQuery] bool? deInterlace,
  204. [FromQuery] bool? requireNonAnamorphic,
  205. [FromQuery] int? transcodingMaxAudioChannels,
  206. [FromQuery] int? cpuCoreLimit,
  207. [FromQuery] string? liveStreamId,
  208. [FromQuery] bool? enableMpegtsM2TsMode,
  209. [FromQuery] string? videoCodec,
  210. [FromQuery] string? subtitleCodec,
  211. [FromQuery] string? transcodingReasons,
  212. [FromQuery] int? audioStreamIndex,
  213. [FromQuery] int? videoStreamIndex,
  214. [FromQuery] EncodingContext context,
  215. [FromQuery] Dictionary<string, string> streamOptions,
  216. [FromQuery] bool enableAdaptiveBitrateStreaming = true)
  217. {
  218. var isHeadRequest = Request.Method == System.Net.WebRequestMethods.Http.Head;
  219. var cancellationTokenSource = new CancellationTokenSource();
  220. var streamingRequest = new HlsVideoRequestDto
  221. {
  222. Id = itemId,
  223. Container = container,
  224. Static = @static ?? true,
  225. Params = @params,
  226. Tag = tag,
  227. DeviceProfileId = deviceProfileId,
  228. PlaySessionId = playSessionId,
  229. SegmentContainer = segmentContainer,
  230. SegmentLength = segmentLength,
  231. MinSegments = minSegments,
  232. MediaSourceId = mediaSourceId,
  233. DeviceId = deviceId,
  234. AudioCodec = audioCodec,
  235. EnableAutoStreamCopy = enableAutoStreamCopy ?? true,
  236. AllowAudioStreamCopy = allowAudioStreamCopy ?? true,
  237. AllowVideoStreamCopy = allowVideoStreamCopy ?? true,
  238. BreakOnNonKeyFrames = breakOnNonKeyFrames ?? false,
  239. AudioSampleRate = audioSampleRate,
  240. MaxAudioChannels = maxAudioChannels,
  241. AudioBitRate = audioBitRate,
  242. MaxAudioBitDepth = maxAudioBitDepth,
  243. AudioChannels = audioChannels,
  244. Profile = profile,
  245. Level = level,
  246. Framerate = framerate,
  247. MaxFramerate = maxFramerate,
  248. CopyTimestamps = copyTimestamps ?? true,
  249. StartTimeTicks = startTimeTicks,
  250. Width = width,
  251. Height = height,
  252. VideoBitRate = videoBitRate,
  253. SubtitleStreamIndex = subtitleStreamIndex,
  254. SubtitleMethod = subtitleMethod,
  255. MaxRefFrames = maxRefFrames,
  256. MaxVideoBitDepth = maxVideoBitDepth,
  257. RequireAvc = requireAvc ?? true,
  258. DeInterlace = deInterlace ?? true,
  259. RequireNonAnamorphic = requireNonAnamorphic ?? true,
  260. TranscodingMaxAudioChannels = transcodingMaxAudioChannels,
  261. CpuCoreLimit = cpuCoreLimit,
  262. LiveStreamId = liveStreamId,
  263. EnableMpegtsM2TsMode = enableMpegtsM2TsMode ?? true,
  264. VideoCodec = videoCodec,
  265. SubtitleCodec = subtitleCodec,
  266. TranscodeReasons = transcodingReasons,
  267. AudioStreamIndex = audioStreamIndex,
  268. VideoStreamIndex = videoStreamIndex,
  269. Context = context,
  270. StreamOptions = streamOptions,
  271. EnableAdaptiveBitrateStreaming = enableAdaptiveBitrateStreaming
  272. };
  273. return await GetMasterPlaylistInternal(streamingRequest, isHeadRequest, enableAdaptiveBitrateStreaming, cancellationTokenSource)
  274. .ConfigureAwait(false);
  275. }
  276. /// <summary>
  277. /// Gets an audio hls playlist stream.
  278. /// </summary>
  279. /// <param name="itemId">The item id.</param>
  280. /// <param name="container">The video container. Possible values are: ts, webm, asf, wmv, ogv, mp4, m4v, mkv, mpeg, mpg, avi, 3gp, wmv, wtv, m2ts, mov, iso, flv. </param>
  281. /// <param name="static">Optional. If true, the original file will be streamed statically without any encoding. Use either no url extension or the original file extension. true/false.</param>
  282. /// <param name="params">The streaming parameters.</param>
  283. /// <param name="tag">The tag.</param>
  284. /// <param name="deviceProfileId">Optional. The dlna device profile id to utilize.</param>
  285. /// <param name="playSessionId">The play session id.</param>
  286. /// <param name="segmentContainer">The segment container.</param>
  287. /// <param name="segmentLength">The segment lenght.</param>
  288. /// <param name="minSegments">The minimum number of segments.</param>
  289. /// <param name="mediaSourceId">The media version id, if playing an alternate version.</param>
  290. /// <param name="deviceId">The device id of the client requesting. Used to stop encoding processes when needed.</param>
  291. /// <param name="audioCodec">Optional. Specify a audio codec to encode to, e.g. mp3. If omitted the server will auto-select using the url's extension. Options: aac, mp3, vorbis, wma.</param>
  292. /// <param name="enableAutoStreamCopy">Whether or not to allow automatic stream copy if requested values match the original source. Defaults to true.</param>
  293. /// <param name="allowVideoStreamCopy">Whether or not to allow copying of the video stream url.</param>
  294. /// <param name="allowAudioStreamCopy">Whether or not to allow copying of the audio stream url.</param>
  295. /// <param name="breakOnNonKeyFrames">Optional. Whether to break on non key frames.</param>
  296. /// <param name="audioSampleRate">Optional. Specify a specific audio sample rate, e.g. 44100.</param>
  297. /// <param name="maxAudioBitDepth">Optional. The maximum audio bit depth.</param>
  298. /// <param name="audioBitRate">Optional. Specify an audio bitrate to encode to, e.g. 128000. If omitted this will be left to encoder defaults.</param>
  299. /// <param name="audioChannels">Optional. Specify a specific number of audio channels to encode to, e.g. 2.</param>
  300. /// <param name="maxAudioChannels">Optional. Specify a maximum number of audio channels to encode to, e.g. 2.</param>
  301. /// <param name="profile">Optional. Specify a specific an encoder profile (varies by encoder), e.g. main, baseline, high.</param>
  302. /// <param name="level">Optional. Specify a level for the encoder profile (varies by encoder), e.g. 3, 3.1.</param>
  303. /// <param name="framerate">Optional. A specific video framerate to encode to, e.g. 23.976. Generally this should be omitted unless the device has specific requirements.</param>
  304. /// <param name="maxFramerate">Optional. A specific maximum video framerate to encode to, e.g. 23.976. Generally this should be omitted unless the device has specific requirements.</param>
  305. /// <param name="copyTimestamps">Whether or not to copy timestamps when transcoding with an offset. Defaults to false.</param>
  306. /// <param name="startTimeTicks">Optional. Specify a starting offset, in ticks. 1 tick = 10000 ms.</param>
  307. /// <param name="width">Optional. The fixed horizontal resolution of the encoded video.</param>
  308. /// <param name="height">Optional. The fixed vertical resolution of the encoded video.</param>
  309. /// <param name="videoBitRate">Optional. Specify a video bitrate to encode to, e.g. 500000. If omitted this will be left to encoder defaults.</param>
  310. /// <param name="subtitleStreamIndex">Optional. The index of the subtitle stream to use. If omitted no subtitles will be used.</param>
  311. /// <param name="subtitleMethod">Optional. Specify the subtitle delivery method.</param>
  312. /// <param name="maxRefFrames">Optional.</param>
  313. /// <param name="maxVideoBitDepth">Optional. The maximum video bit depth.</param>
  314. /// <param name="requireAvc">Optional. Whether to require avc.</param>
  315. /// <param name="deInterlace">Optional. Whether to deinterlace the video.</param>
  316. /// <param name="requireNonAnamorphic">Optional. Whether to require a non anamporphic stream.</param>
  317. /// <param name="transcodingMaxAudioChannels">Optional. The maximum number of audio channels to transcode.</param>
  318. /// <param name="cpuCoreLimit">Optional. The limit of how many cpu cores to use.</param>
  319. /// <param name="liveStreamId">The live stream id.</param>
  320. /// <param name="enableMpegtsM2TsMode">Optional. Whether to enable the MpegtsM2Ts mode.</param>
  321. /// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h265, h264, mpeg4, theora, vpx, wmv.</param>
  322. /// <param name="subtitleCodec">Optional. Specify a subtitle codec to encode to.</param>
  323. /// <param name="transcodingReasons">Optional. The transcoding reason.</param>
  324. /// <param name="audioStreamIndex">Optional. The index of the audio stream to use. If omitted the first audio stream will be used.</param>
  325. /// <param name="videoStreamIndex">Optional. The index of the video stream to use. If omitted the first video stream will be used.</param>
  326. /// <param name="context">Optional. The <see cref="EncodingContext"/>.</param>
  327. /// <param name="streamOptions">Optional. The streaming options.</param>
  328. /// <param name="enableAdaptiveBitrateStreaming">Enable adaptive bitrate streaming.</param>
  329. /// <response code="200">Audio stream returned.</response>
  330. /// <returns>A <see cref="FileResult"/> containing the playlist file.</returns>
  331. [HttpGet("Audio/{itemId}/master.m3u8")]
  332. [HttpHead("Audio/{itemId}/master.m3u8", Name = "HeadMasterHlsAudioPlaylist")]
  333. [ProducesResponseType(StatusCodes.Status200OK)]
  334. public async Task<ActionResult> GetMasterHlsAudioPlaylist(
  335. [FromRoute] Guid itemId,
  336. [FromRoute] string? container,
  337. [FromQuery] bool? @static,
  338. [FromQuery] string? @params,
  339. [FromQuery] string? tag,
  340. [FromQuery] string? deviceProfileId,
  341. [FromQuery] string? playSessionId,
  342. [FromQuery] string? segmentContainer,
  343. [FromQuery] int? segmentLength,
  344. [FromQuery] int? minSegments,
  345. [FromQuery, Required] string? mediaSourceId,
  346. [FromQuery] string? deviceId,
  347. [FromQuery] string? audioCodec,
  348. [FromQuery] bool? enableAutoStreamCopy,
  349. [FromQuery] bool? allowVideoStreamCopy,
  350. [FromQuery] bool? allowAudioStreamCopy,
  351. [FromQuery] bool? breakOnNonKeyFrames,
  352. [FromQuery] int? audioSampleRate,
  353. [FromQuery] int? maxAudioBitDepth,
  354. [FromQuery] int? audioBitRate,
  355. [FromQuery] int? audioChannels,
  356. [FromQuery] int? maxAudioChannels,
  357. [FromQuery] string? profile,
  358. [FromQuery] string? level,
  359. [FromQuery] float? framerate,
  360. [FromQuery] float? maxFramerate,
  361. [FromQuery] bool? copyTimestamps,
  362. [FromQuery] long? startTimeTicks,
  363. [FromQuery] int? width,
  364. [FromQuery] int? height,
  365. [FromQuery] int? videoBitRate,
  366. [FromQuery] int? subtitleStreamIndex,
  367. [FromQuery] SubtitleDeliveryMethod subtitleMethod,
  368. [FromQuery] int? maxRefFrames,
  369. [FromQuery] int? maxVideoBitDepth,
  370. [FromQuery] bool? requireAvc,
  371. [FromQuery] bool? deInterlace,
  372. [FromQuery] bool? requireNonAnamorphic,
  373. [FromQuery] int? transcodingMaxAudioChannels,
  374. [FromQuery] int? cpuCoreLimit,
  375. [FromQuery] string? liveStreamId,
  376. [FromQuery] bool? enableMpegtsM2TsMode,
  377. [FromQuery] string? videoCodec,
  378. [FromQuery] string? subtitleCodec,
  379. [FromQuery] string? transcodingReasons,
  380. [FromQuery] int? audioStreamIndex,
  381. [FromQuery] int? videoStreamIndex,
  382. [FromQuery] EncodingContext context,
  383. [FromQuery] Dictionary<string, string> streamOptions,
  384. [FromQuery] bool enableAdaptiveBitrateStreaming = true)
  385. {
  386. var isHeadRequest = Request.Method == System.Net.WebRequestMethods.Http.Head;
  387. var cancellationTokenSource = new CancellationTokenSource();
  388. var streamingRequest = new HlsAudioRequestDto
  389. {
  390. Id = itemId,
  391. Container = container,
  392. Static = @static ?? true,
  393. Params = @params,
  394. Tag = tag,
  395. DeviceProfileId = deviceProfileId,
  396. PlaySessionId = playSessionId,
  397. SegmentContainer = segmentContainer,
  398. SegmentLength = segmentLength,
  399. MinSegments = minSegments,
  400. MediaSourceId = mediaSourceId,
  401. DeviceId = deviceId,
  402. AudioCodec = audioCodec,
  403. EnableAutoStreamCopy = enableAutoStreamCopy ?? true,
  404. AllowAudioStreamCopy = allowAudioStreamCopy ?? true,
  405. AllowVideoStreamCopy = allowVideoStreamCopy ?? true,
  406. BreakOnNonKeyFrames = breakOnNonKeyFrames ?? false,
  407. AudioSampleRate = audioSampleRate,
  408. MaxAudioChannels = maxAudioChannels,
  409. AudioBitRate = audioBitRate,
  410. MaxAudioBitDepth = maxAudioBitDepth,
  411. AudioChannels = audioChannels,
  412. Profile = profile,
  413. Level = level,
  414. Framerate = framerate,
  415. MaxFramerate = maxFramerate,
  416. CopyTimestamps = copyTimestamps ?? true,
  417. StartTimeTicks = startTimeTicks,
  418. Width = width,
  419. Height = height,
  420. VideoBitRate = videoBitRate,
  421. SubtitleStreamIndex = subtitleStreamIndex,
  422. SubtitleMethod = subtitleMethod,
  423. MaxRefFrames = maxRefFrames,
  424. MaxVideoBitDepth = maxVideoBitDepth,
  425. RequireAvc = requireAvc ?? true,
  426. DeInterlace = deInterlace ?? true,
  427. RequireNonAnamorphic = requireNonAnamorphic ?? true,
  428. TranscodingMaxAudioChannels = transcodingMaxAudioChannels,
  429. CpuCoreLimit = cpuCoreLimit,
  430. LiveStreamId = liveStreamId,
  431. EnableMpegtsM2TsMode = enableMpegtsM2TsMode ?? true,
  432. VideoCodec = videoCodec,
  433. SubtitleCodec = subtitleCodec,
  434. TranscodeReasons = transcodingReasons,
  435. AudioStreamIndex = audioStreamIndex,
  436. VideoStreamIndex = videoStreamIndex,
  437. Context = context,
  438. StreamOptions = streamOptions,
  439. EnableAdaptiveBitrateStreaming = enableAdaptiveBitrateStreaming
  440. };
  441. return await GetMasterPlaylistInternal(streamingRequest, isHeadRequest, enableAdaptiveBitrateStreaming, cancellationTokenSource)
  442. .ConfigureAwait(false);
  443. }
  444. /// <summary>
  445. /// Gets a video stream using HTTP live streaming.
  446. /// </summary>
  447. /// <param name="itemId">The item id.</param>
  448. /// <param name="container">The video container. Possible values are: ts, webm, asf, wmv, ogv, mp4, m4v, mkv, mpeg, mpg, avi, 3gp, wmv, wtv, m2ts, mov, iso, flv. </param>
  449. /// <param name="static">Optional. If true, the original file will be streamed statically without any encoding. Use either no url extension or the original file extension. true/false.</param>
  450. /// <param name="params">The streaming parameters.</param>
  451. /// <param name="tag">The tag.</param>
  452. /// <param name="deviceProfileId">Optional. The dlna device profile id to utilize.</param>
  453. /// <param name="playSessionId">The play session id.</param>
  454. /// <param name="segmentContainer">The segment container.</param>
  455. /// <param name="segmentLength">The segment lenght.</param>
  456. /// <param name="minSegments">The minimum number of segments.</param>
  457. /// <param name="mediaSourceId">The media version id, if playing an alternate version.</param>
  458. /// <param name="deviceId">The device id of the client requesting. Used to stop encoding processes when needed.</param>
  459. /// <param name="audioCodec">Optional. Specify a audio codec to encode to, e.g. mp3. If omitted the server will auto-select using the url's extension. Options: aac, mp3, vorbis, wma.</param>
  460. /// <param name="enableAutoStreamCopy">Whether or not to allow automatic stream copy if requested values match the original source. Defaults to true.</param>
  461. /// <param name="allowVideoStreamCopy">Whether or not to allow copying of the video stream url.</param>
  462. /// <param name="allowAudioStreamCopy">Whether or not to allow copying of the audio stream url.</param>
  463. /// <param name="breakOnNonKeyFrames">Optional. Whether to break on non key frames.</param>
  464. /// <param name="audioSampleRate">Optional. Specify a specific audio sample rate, e.g. 44100.</param>
  465. /// <param name="maxAudioBitDepth">Optional. The maximum audio bit depth.</param>
  466. /// <param name="audioBitRate">Optional. Specify an audio bitrate to encode to, e.g. 128000. If omitted this will be left to encoder defaults.</param>
  467. /// <param name="audioChannels">Optional. Specify a specific number of audio channels to encode to, e.g. 2.</param>
  468. /// <param name="maxAudioChannels">Optional. Specify a maximum number of audio channels to encode to, e.g. 2.</param>
  469. /// <param name="profile">Optional. Specify a specific an encoder profile (varies by encoder), e.g. main, baseline, high.</param>
  470. /// <param name="level">Optional. Specify a level for the encoder profile (varies by encoder), e.g. 3, 3.1.</param>
  471. /// <param name="framerate">Optional. A specific video framerate to encode to, e.g. 23.976. Generally this should be omitted unless the device has specific requirements.</param>
  472. /// <param name="maxFramerate">Optional. A specific maximum video framerate to encode to, e.g. 23.976. Generally this should be omitted unless the device has specific requirements.</param>
  473. /// <param name="copyTimestamps">Whether or not to copy timestamps when transcoding with an offset. Defaults to false.</param>
  474. /// <param name="startTimeTicks">Optional. Specify a starting offset, in ticks. 1 tick = 10000 ms.</param>
  475. /// <param name="width">Optional. The fixed horizontal resolution of the encoded video.</param>
  476. /// <param name="height">Optional. The fixed vertical resolution of the encoded video.</param>
  477. /// <param name="videoBitRate">Optional. Specify a video bitrate to encode to, e.g. 500000. If omitted this will be left to encoder defaults.</param>
  478. /// <param name="subtitleStreamIndex">Optional. The index of the subtitle stream to use. If omitted no subtitles will be used.</param>
  479. /// <param name="subtitleMethod">Optional. Specify the subtitle delivery method.</param>
  480. /// <param name="maxRefFrames">Optional.</param>
  481. /// <param name="maxVideoBitDepth">Optional. The maximum video bit depth.</param>
  482. /// <param name="requireAvc">Optional. Whether to require avc.</param>
  483. /// <param name="deInterlace">Optional. Whether to deinterlace the video.</param>
  484. /// <param name="requireNonAnamorphic">Optional. Whether to require a non anamporphic stream.</param>
  485. /// <param name="transcodingMaxAudioChannels">Optional. The maximum number of audio channels to transcode.</param>
  486. /// <param name="cpuCoreLimit">Optional. The limit of how many cpu cores to use.</param>
  487. /// <param name="liveStreamId">The live stream id.</param>
  488. /// <param name="enableMpegtsM2TsMode">Optional. Whether to enable the MpegtsM2Ts mode.</param>
  489. /// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h265, h264, mpeg4, theora, vpx, wmv.</param>
  490. /// <param name="subtitleCodec">Optional. Specify a subtitle codec to encode to.</param>
  491. /// <param name="transcodingReasons">Optional. The transcoding reason.</param>
  492. /// <param name="audioStreamIndex">Optional. The index of the audio stream to use. If omitted the first audio stream will be used.</param>
  493. /// <param name="videoStreamIndex">Optional. The index of the video stream to use. If omitted the first video stream will be used.</param>
  494. /// <param name="context">Optional. The <see cref="EncodingContext"/>.</param>
  495. /// <param name="streamOptions">Optional. The streaming options.</param>
  496. /// <response code="200">Video stream returned.</response>
  497. /// <returns>A <see cref="FileResult"/> containing the audio file.</returns>
  498. [HttpGet("Videos/{itemId}/main.m3u8")]
  499. [ProducesResponseType(StatusCodes.Status200OK)]
  500. public async Task<ActionResult> GetVariantHlsVideoPlaylist(
  501. [FromRoute] Guid itemId,
  502. [FromRoute] string? container,
  503. [FromQuery] bool? @static,
  504. [FromQuery] string? @params,
  505. [FromQuery] string? tag,
  506. [FromQuery] string? deviceProfileId,
  507. [FromQuery] string? playSessionId,
  508. [FromQuery] string? segmentContainer,
  509. [FromQuery] int? segmentLength,
  510. [FromQuery] int? minSegments,
  511. [FromQuery] string? mediaSourceId,
  512. [FromQuery] string? deviceId,
  513. [FromQuery] string? audioCodec,
  514. [FromQuery] bool? enableAutoStreamCopy,
  515. [FromQuery] bool? allowVideoStreamCopy,
  516. [FromQuery] bool? allowAudioStreamCopy,
  517. [FromQuery] bool? breakOnNonKeyFrames,
  518. [FromQuery] int? audioSampleRate,
  519. [FromQuery] int? maxAudioBitDepth,
  520. [FromQuery] int? audioBitRate,
  521. [FromQuery] int? audioChannels,
  522. [FromQuery] int? maxAudioChannels,
  523. [FromQuery] string? profile,
  524. [FromQuery] string? level,
  525. [FromQuery] float? framerate,
  526. [FromQuery] float? maxFramerate,
  527. [FromQuery] bool? copyTimestamps,
  528. [FromQuery] long? startTimeTicks,
  529. [FromQuery] int? width,
  530. [FromQuery] int? height,
  531. [FromQuery] int? videoBitRate,
  532. [FromQuery] int? subtitleStreamIndex,
  533. [FromQuery] SubtitleDeliveryMethod subtitleMethod,
  534. [FromQuery] int? maxRefFrames,
  535. [FromQuery] int? maxVideoBitDepth,
  536. [FromQuery] bool? requireAvc,
  537. [FromQuery] bool? deInterlace,
  538. [FromQuery] bool? requireNonAnamorphic,
  539. [FromQuery] int? transcodingMaxAudioChannels,
  540. [FromQuery] int? cpuCoreLimit,
  541. [FromQuery] string? liveStreamId,
  542. [FromQuery] bool? enableMpegtsM2TsMode,
  543. [FromQuery] string? videoCodec,
  544. [FromQuery] string? subtitleCodec,
  545. [FromQuery] string? transcodingReasons,
  546. [FromQuery] int? audioStreamIndex,
  547. [FromQuery] int? videoStreamIndex,
  548. [FromQuery] EncodingContext context,
  549. [FromQuery] Dictionary<string, string> streamOptions)
  550. {
  551. var cancellationTokenSource = new CancellationTokenSource();
  552. var streamingRequest = new VideoRequestDto
  553. {
  554. Id = itemId,
  555. Container = container,
  556. Static = @static ?? true,
  557. Params = @params,
  558. Tag = tag,
  559. DeviceProfileId = deviceProfileId,
  560. PlaySessionId = playSessionId,
  561. SegmentContainer = segmentContainer,
  562. SegmentLength = segmentLength,
  563. MinSegments = minSegments,
  564. MediaSourceId = mediaSourceId,
  565. DeviceId = deviceId,
  566. AudioCodec = audioCodec,
  567. EnableAutoStreamCopy = enableAutoStreamCopy ?? true,
  568. AllowAudioStreamCopy = allowAudioStreamCopy ?? true,
  569. AllowVideoStreamCopy = allowVideoStreamCopy ?? true,
  570. BreakOnNonKeyFrames = breakOnNonKeyFrames ?? false,
  571. AudioSampleRate = audioSampleRate,
  572. MaxAudioChannels = maxAudioChannels,
  573. AudioBitRate = audioBitRate,
  574. MaxAudioBitDepth = maxAudioBitDepth,
  575. AudioChannels = audioChannels,
  576. Profile = profile,
  577. Level = level,
  578. Framerate = framerate,
  579. MaxFramerate = maxFramerate,
  580. CopyTimestamps = copyTimestamps ?? true,
  581. StartTimeTicks = startTimeTicks,
  582. Width = width,
  583. Height = height,
  584. VideoBitRate = videoBitRate,
  585. SubtitleStreamIndex = subtitleStreamIndex,
  586. SubtitleMethod = subtitleMethod,
  587. MaxRefFrames = maxRefFrames,
  588. MaxVideoBitDepth = maxVideoBitDepth,
  589. RequireAvc = requireAvc ?? true,
  590. DeInterlace = deInterlace ?? true,
  591. RequireNonAnamorphic = requireNonAnamorphic ?? true,
  592. TranscodingMaxAudioChannels = transcodingMaxAudioChannels,
  593. CpuCoreLimit = cpuCoreLimit,
  594. LiveStreamId = liveStreamId,
  595. EnableMpegtsM2TsMode = enableMpegtsM2TsMode ?? true,
  596. VideoCodec = videoCodec,
  597. SubtitleCodec = subtitleCodec,
  598. TranscodeReasons = transcodingReasons,
  599. AudioStreamIndex = audioStreamIndex,
  600. VideoStreamIndex = videoStreamIndex,
  601. Context = context,
  602. StreamOptions = streamOptions
  603. };
  604. return await GetVariantPlaylistInternal(streamingRequest, "main", cancellationTokenSource)
  605. .ConfigureAwait(false);
  606. }
  607. /// <summary>
  608. /// Gets an audio stream using HTTP live streaming.
  609. /// </summary>
  610. /// <param name="itemId">The item id.</param>
  611. /// <param name="container">The video container. Possible values are: ts, webm, asf, wmv, ogv, mp4, m4v, mkv, mpeg, mpg, avi, 3gp, wmv, wtv, m2ts, mov, iso, flv. </param>
  612. /// <param name="static">Optional. If true, the original file will be streamed statically without any encoding. Use either no url extension or the original file extension. true/false.</param>
  613. /// <param name="params">The streaming parameters.</param>
  614. /// <param name="tag">The tag.</param>
  615. /// <param name="deviceProfileId">Optional. The dlna device profile id to utilize.</param>
  616. /// <param name="playSessionId">The play session id.</param>
  617. /// <param name="segmentContainer">The segment container.</param>
  618. /// <param name="segmentLength">The segment lenght.</param>
  619. /// <param name="minSegments">The minimum number of segments.</param>
  620. /// <param name="mediaSourceId">The media version id, if playing an alternate version.</param>
  621. /// <param name="deviceId">The device id of the client requesting. Used to stop encoding processes when needed.</param>
  622. /// <param name="audioCodec">Optional. Specify a audio codec to encode to, e.g. mp3. If omitted the server will auto-select using the url's extension. Options: aac, mp3, vorbis, wma.</param>
  623. /// <param name="enableAutoStreamCopy">Whether or not to allow automatic stream copy if requested values match the original source. Defaults to true.</param>
  624. /// <param name="allowVideoStreamCopy">Whether or not to allow copying of the video stream url.</param>
  625. /// <param name="allowAudioStreamCopy">Whether or not to allow copying of the audio stream url.</param>
  626. /// <param name="breakOnNonKeyFrames">Optional. Whether to break on non key frames.</param>
  627. /// <param name="audioSampleRate">Optional. Specify a specific audio sample rate, e.g. 44100.</param>
  628. /// <param name="maxAudioBitDepth">Optional. The maximum audio bit depth.</param>
  629. /// <param name="audioBitRate">Optional. Specify an audio bitrate to encode to, e.g. 128000. If omitted this will be left to encoder defaults.</param>
  630. /// <param name="audioChannels">Optional. Specify a specific number of audio channels to encode to, e.g. 2.</param>
  631. /// <param name="maxAudioChannels">Optional. Specify a maximum number of audio channels to encode to, e.g. 2.</param>
  632. /// <param name="profile">Optional. Specify a specific an encoder profile (varies by encoder), e.g. main, baseline, high.</param>
  633. /// <param name="level">Optional. Specify a level for the encoder profile (varies by encoder), e.g. 3, 3.1.</param>
  634. /// <param name="framerate">Optional. A specific video framerate to encode to, e.g. 23.976. Generally this should be omitted unless the device has specific requirements.</param>
  635. /// <param name="maxFramerate">Optional. A specific maximum video framerate to encode to, e.g. 23.976. Generally this should be omitted unless the device has specific requirements.</param>
  636. /// <param name="copyTimestamps">Whether or not to copy timestamps when transcoding with an offset. Defaults to false.</param>
  637. /// <param name="startTimeTicks">Optional. Specify a starting offset, in ticks. 1 tick = 10000 ms.</param>
  638. /// <param name="width">Optional. The fixed horizontal resolution of the encoded video.</param>
  639. /// <param name="height">Optional. The fixed vertical resolution of the encoded video.</param>
  640. /// <param name="videoBitRate">Optional. Specify a video bitrate to encode to, e.g. 500000. If omitted this will be left to encoder defaults.</param>
  641. /// <param name="subtitleStreamIndex">Optional. The index of the subtitle stream to use. If omitted no subtitles will be used.</param>
  642. /// <param name="subtitleMethod">Optional. Specify the subtitle delivery method.</param>
  643. /// <param name="maxRefFrames">Optional.</param>
  644. /// <param name="maxVideoBitDepth">Optional. The maximum video bit depth.</param>
  645. /// <param name="requireAvc">Optional. Whether to require avc.</param>
  646. /// <param name="deInterlace">Optional. Whether to deinterlace the video.</param>
  647. /// <param name="requireNonAnamorphic">Optional. Whether to require a non anamporphic stream.</param>
  648. /// <param name="transcodingMaxAudioChannels">Optional. The maximum number of audio channels to transcode.</param>
  649. /// <param name="cpuCoreLimit">Optional. The limit of how many cpu cores to use.</param>
  650. /// <param name="liveStreamId">The live stream id.</param>
  651. /// <param name="enableMpegtsM2TsMode">Optional. Whether to enable the MpegtsM2Ts mode.</param>
  652. /// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h265, h264, mpeg4, theora, vpx, wmv.</param>
  653. /// <param name="subtitleCodec">Optional. Specify a subtitle codec to encode to.</param>
  654. /// <param name="transcodingReasons">Optional. The transcoding reason.</param>
  655. /// <param name="audioStreamIndex">Optional. The index of the audio stream to use. If omitted the first audio stream will be used.</param>
  656. /// <param name="videoStreamIndex">Optional. The index of the video stream to use. If omitted the first video stream will be used.</param>
  657. /// <param name="context">Optional. The <see cref="EncodingContext"/>.</param>
  658. /// <param name="streamOptions">Optional. The streaming options.</param>
  659. /// <response code="200">Audio stream returned.</response>
  660. /// <returns>A <see cref="FileResult"/> containing the audio file.</returns>
  661. [HttpGet("Audio/{itemId}/main.m3u8")]
  662. [ProducesResponseType(StatusCodes.Status200OK)]
  663. public async Task<ActionResult> GetVariantHlsAudioPlaylist(
  664. [FromRoute] Guid itemId,
  665. [FromRoute] string? container,
  666. [FromQuery] bool? @static,
  667. [FromQuery] string? @params,
  668. [FromQuery] string? tag,
  669. [FromQuery] string? deviceProfileId,
  670. [FromQuery] string? playSessionId,
  671. [FromQuery] string? segmentContainer,
  672. [FromQuery] int? segmentLength,
  673. [FromQuery] int? minSegments,
  674. [FromQuery] string? mediaSourceId,
  675. [FromQuery] string? deviceId,
  676. [FromQuery] string? audioCodec,
  677. [FromQuery] bool? enableAutoStreamCopy,
  678. [FromQuery] bool? allowVideoStreamCopy,
  679. [FromQuery] bool? allowAudioStreamCopy,
  680. [FromQuery] bool? breakOnNonKeyFrames,
  681. [FromQuery] int? audioSampleRate,
  682. [FromQuery] int? maxAudioBitDepth,
  683. [FromQuery] int? audioBitRate,
  684. [FromQuery] int? audioChannels,
  685. [FromQuery] int? maxAudioChannels,
  686. [FromQuery] string? profile,
  687. [FromQuery] string? level,
  688. [FromQuery] float? framerate,
  689. [FromQuery] float? maxFramerate,
  690. [FromQuery] bool? copyTimestamps,
  691. [FromQuery] long? startTimeTicks,
  692. [FromQuery] int? width,
  693. [FromQuery] int? height,
  694. [FromQuery] int? videoBitRate,
  695. [FromQuery] int? subtitleStreamIndex,
  696. [FromQuery] SubtitleDeliveryMethod subtitleMethod,
  697. [FromQuery] int? maxRefFrames,
  698. [FromQuery] int? maxVideoBitDepth,
  699. [FromQuery] bool? requireAvc,
  700. [FromQuery] bool? deInterlace,
  701. [FromQuery] bool? requireNonAnamorphic,
  702. [FromQuery] int? transcodingMaxAudioChannels,
  703. [FromQuery] int? cpuCoreLimit,
  704. [FromQuery] string? liveStreamId,
  705. [FromQuery] bool? enableMpegtsM2TsMode,
  706. [FromQuery] string? videoCodec,
  707. [FromQuery] string? subtitleCodec,
  708. [FromQuery] string? transcodingReasons,
  709. [FromQuery] int? audioStreamIndex,
  710. [FromQuery] int? videoStreamIndex,
  711. [FromQuery] EncodingContext context,
  712. [FromQuery] Dictionary<string, string> streamOptions)
  713. {
  714. var cancellationTokenSource = new CancellationTokenSource();
  715. var streamingRequest = new StreamingRequestDto
  716. {
  717. Id = itemId,
  718. Container = container,
  719. Static = @static ?? true,
  720. Params = @params,
  721. Tag = tag,
  722. DeviceProfileId = deviceProfileId,
  723. PlaySessionId = playSessionId,
  724. SegmentContainer = segmentContainer,
  725. SegmentLength = segmentLength,
  726. MinSegments = minSegments,
  727. MediaSourceId = mediaSourceId,
  728. DeviceId = deviceId,
  729. AudioCodec = audioCodec,
  730. EnableAutoStreamCopy = enableAutoStreamCopy ?? true,
  731. AllowAudioStreamCopy = allowAudioStreamCopy ?? true,
  732. AllowVideoStreamCopy = allowVideoStreamCopy ?? true,
  733. BreakOnNonKeyFrames = breakOnNonKeyFrames ?? false,
  734. AudioSampleRate = audioSampleRate,
  735. MaxAudioChannels = maxAudioChannels,
  736. AudioBitRate = audioBitRate,
  737. MaxAudioBitDepth = maxAudioBitDepth,
  738. AudioChannels = audioChannels,
  739. Profile = profile,
  740. Level = level,
  741. Framerate = framerate,
  742. MaxFramerate = maxFramerate,
  743. CopyTimestamps = copyTimestamps ?? true,
  744. StartTimeTicks = startTimeTicks,
  745. Width = width,
  746. Height = height,
  747. VideoBitRate = videoBitRate,
  748. SubtitleStreamIndex = subtitleStreamIndex,
  749. SubtitleMethod = subtitleMethod,
  750. MaxRefFrames = maxRefFrames,
  751. MaxVideoBitDepth = maxVideoBitDepth,
  752. RequireAvc = requireAvc ?? true,
  753. DeInterlace = deInterlace ?? true,
  754. RequireNonAnamorphic = requireNonAnamorphic ?? true,
  755. TranscodingMaxAudioChannels = transcodingMaxAudioChannels,
  756. CpuCoreLimit = cpuCoreLimit,
  757. LiveStreamId = liveStreamId,
  758. EnableMpegtsM2TsMode = enableMpegtsM2TsMode ?? true,
  759. VideoCodec = videoCodec,
  760. SubtitleCodec = subtitleCodec,
  761. TranscodeReasons = transcodingReasons,
  762. AudioStreamIndex = audioStreamIndex,
  763. VideoStreamIndex = videoStreamIndex,
  764. Context = context,
  765. StreamOptions = streamOptions
  766. };
  767. return await GetVariantPlaylistInternal(streamingRequest, "main", cancellationTokenSource)
  768. .ConfigureAwait(false);
  769. }
  770. /// <summary>
  771. /// Gets a video stream using HTTP live streaming.
  772. /// </summary>
  773. /// <param name="itemId">The item id.</param>
  774. /// <param name="playlistId">The playlist id.</param>
  775. /// <param name="segmentId">The segment id.</param>
  776. /// <param name="container">The video container. Possible values are: ts, webm, asf, wmv, ogv, mp4, m4v, mkv, mpeg, mpg, avi, 3gp, wmv, wtv, m2ts, mov, iso, flv. </param>
  777. /// <param name="static">Optional. If true, the original file will be streamed statically without any encoding. Use either no url extension or the original file extension. true/false.</param>
  778. /// <param name="params">The streaming parameters.</param>
  779. /// <param name="tag">The tag.</param>
  780. /// <param name="deviceProfileId">Optional. The dlna device profile id to utilize.</param>
  781. /// <param name="playSessionId">The play session id.</param>
  782. /// <param name="segmentContainer">The segment container.</param>
  783. /// <param name="segmentLength">The segment lenght.</param>
  784. /// <param name="minSegments">The minimum number of segments.</param>
  785. /// <param name="mediaSourceId">The media version id, if playing an alternate version.</param>
  786. /// <param name="deviceId">The device id of the client requesting. Used to stop encoding processes when needed.</param>
  787. /// <param name="audioCodec">Optional. Specify a audio codec to encode to, e.g. mp3. If omitted the server will auto-select using the url's extension. Options: aac, mp3, vorbis, wma.</param>
  788. /// <param name="enableAutoStreamCopy">Whether or not to allow automatic stream copy if requested values match the original source. Defaults to true.</param>
  789. /// <param name="allowVideoStreamCopy">Whether or not to allow copying of the video stream url.</param>
  790. /// <param name="allowAudioStreamCopy">Whether or not to allow copying of the audio stream url.</param>
  791. /// <param name="breakOnNonKeyFrames">Optional. Whether to break on non key frames.</param>
  792. /// <param name="audioSampleRate">Optional. Specify a specific audio sample rate, e.g. 44100.</param>
  793. /// <param name="maxAudioBitDepth">Optional. The maximum audio bit depth.</param>
  794. /// <param name="audioBitRate">Optional. Specify an audio bitrate to encode to, e.g. 128000. If omitted this will be left to encoder defaults.</param>
  795. /// <param name="audioChannels">Optional. Specify a specific number of audio channels to encode to, e.g. 2.</param>
  796. /// <param name="maxAudioChannels">Optional. Specify a maximum number of audio channels to encode to, e.g. 2.</param>
  797. /// <param name="profile">Optional. Specify a specific an encoder profile (varies by encoder), e.g. main, baseline, high.</param>
  798. /// <param name="level">Optional. Specify a level for the encoder profile (varies by encoder), e.g. 3, 3.1.</param>
  799. /// <param name="framerate">Optional. A specific video framerate to encode to, e.g. 23.976. Generally this should be omitted unless the device has specific requirements.</param>
  800. /// <param name="maxFramerate">Optional. A specific maximum video framerate to encode to, e.g. 23.976. Generally this should be omitted unless the device has specific requirements.</param>
  801. /// <param name="copyTimestamps">Whether or not to copy timestamps when transcoding with an offset. Defaults to false.</param>
  802. /// <param name="startTimeTicks">Optional. Specify a starting offset, in ticks. 1 tick = 10000 ms.</param>
  803. /// <param name="width">Optional. The fixed horizontal resolution of the encoded video.</param>
  804. /// <param name="height">Optional. The fixed vertical resolution of the encoded video.</param>
  805. /// <param name="videoBitRate">Optional. Specify a video bitrate to encode to, e.g. 500000. If omitted this will be left to encoder defaults.</param>
  806. /// <param name="subtitleStreamIndex">Optional. The index of the subtitle stream to use. If omitted no subtitles will be used.</param>
  807. /// <param name="subtitleMethod">Optional. Specify the subtitle delivery method.</param>
  808. /// <param name="maxRefFrames">Optional.</param>
  809. /// <param name="maxVideoBitDepth">Optional. The maximum video bit depth.</param>
  810. /// <param name="requireAvc">Optional. Whether to require avc.</param>
  811. /// <param name="deInterlace">Optional. Whether to deinterlace the video.</param>
  812. /// <param name="requireNonAnamorphic">Optional. Whether to require a non anamporphic stream.</param>
  813. /// <param name="transcodingMaxAudioChannels">Optional. The maximum number of audio channels to transcode.</param>
  814. /// <param name="cpuCoreLimit">Optional. The limit of how many cpu cores to use.</param>
  815. /// <param name="liveStreamId">The live stream id.</param>
  816. /// <param name="enableMpegtsM2TsMode">Optional. Whether to enable the MpegtsM2Ts mode.</param>
  817. /// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h265, h264, mpeg4, theora, vpx, wmv.</param>
  818. /// <param name="subtitleCodec">Optional. Specify a subtitle codec to encode to.</param>
  819. /// <param name="transcodingReasons">Optional. The transcoding reason.</param>
  820. /// <param name="audioStreamIndex">Optional. The index of the audio stream to use. If omitted the first audio stream will be used.</param>
  821. /// <param name="videoStreamIndex">Optional. The index of the video stream to use. If omitted the first video stream will be used.</param>
  822. /// <param name="context">Optional. The <see cref="EncodingContext"/>.</param>
  823. /// <param name="streamOptions">Optional. The streaming options.</param>
  824. /// <response code="200">Video stream returned.</response>
  825. /// <returns>A <see cref="FileResult"/> containing the audio file.</returns>
  826. [HttpGet("Videos/{itemId}/hls1/{playlistId}/{segmentId}.{container}")]
  827. [ProducesResponseType(StatusCodes.Status200OK)]
  828. [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "playlistId", Justification = "Imported from ServiceStack")]
  829. public async Task<ActionResult> GetHlsVideoSegment(
  830. [FromRoute] Guid itemId,
  831. [FromRoute] string playlistId,
  832. [FromRoute] int segmentId,
  833. [FromRoute] string container,
  834. [FromQuery] bool? @static,
  835. [FromQuery] string? @params,
  836. [FromQuery] string? tag,
  837. [FromQuery] string? deviceProfileId,
  838. [FromQuery] string? playSessionId,
  839. [FromQuery] string? segmentContainer,
  840. [FromQuery] int? segmentLength,
  841. [FromQuery] int? minSegments,
  842. [FromQuery] string? mediaSourceId,
  843. [FromQuery] string? deviceId,
  844. [FromQuery] string? audioCodec,
  845. [FromQuery] bool? enableAutoStreamCopy,
  846. [FromQuery] bool? allowVideoStreamCopy,
  847. [FromQuery] bool? allowAudioStreamCopy,
  848. [FromQuery] bool? breakOnNonKeyFrames,
  849. [FromQuery] int? audioSampleRate,
  850. [FromQuery] int? maxAudioBitDepth,
  851. [FromQuery] int? audioBitRate,
  852. [FromQuery] int? audioChannels,
  853. [FromQuery] int? maxAudioChannels,
  854. [FromQuery] string? profile,
  855. [FromQuery] string? level,
  856. [FromQuery] float? framerate,
  857. [FromQuery] float? maxFramerate,
  858. [FromQuery] bool? copyTimestamps,
  859. [FromQuery] long? startTimeTicks,
  860. [FromQuery] int? width,
  861. [FromQuery] int? height,
  862. [FromQuery] int? videoBitRate,
  863. [FromQuery] int? subtitleStreamIndex,
  864. [FromQuery] SubtitleDeliveryMethod subtitleMethod,
  865. [FromQuery] int? maxRefFrames,
  866. [FromQuery] int? maxVideoBitDepth,
  867. [FromQuery] bool? requireAvc,
  868. [FromQuery] bool? deInterlace,
  869. [FromQuery] bool? requireNonAnamorphic,
  870. [FromQuery] int? transcodingMaxAudioChannels,
  871. [FromQuery] int? cpuCoreLimit,
  872. [FromQuery] string? liveStreamId,
  873. [FromQuery] bool? enableMpegtsM2TsMode,
  874. [FromQuery] string? videoCodec,
  875. [FromQuery] string? subtitleCodec,
  876. [FromQuery] string? transcodingReasons,
  877. [FromQuery] int? audioStreamIndex,
  878. [FromQuery] int? videoStreamIndex,
  879. [FromQuery] EncodingContext context,
  880. [FromQuery] Dictionary<string, string> streamOptions)
  881. {
  882. var streamingRequest = new VideoRequestDto
  883. {
  884. Id = itemId,
  885. Container = container,
  886. Static = @static ?? true,
  887. Params = @params,
  888. Tag = tag,
  889. DeviceProfileId = deviceProfileId,
  890. PlaySessionId = playSessionId,
  891. SegmentContainer = segmentContainer,
  892. SegmentLength = segmentLength,
  893. MinSegments = minSegments,
  894. MediaSourceId = mediaSourceId,
  895. DeviceId = deviceId,
  896. AudioCodec = audioCodec,
  897. EnableAutoStreamCopy = enableAutoStreamCopy ?? true,
  898. AllowAudioStreamCopy = allowAudioStreamCopy ?? true,
  899. AllowVideoStreamCopy = allowVideoStreamCopy ?? true,
  900. BreakOnNonKeyFrames = breakOnNonKeyFrames ?? false,
  901. AudioSampleRate = audioSampleRate,
  902. MaxAudioChannels = maxAudioChannels,
  903. AudioBitRate = audioBitRate,
  904. MaxAudioBitDepth = maxAudioBitDepth,
  905. AudioChannels = audioChannels,
  906. Profile = profile,
  907. Level = level,
  908. Framerate = framerate,
  909. MaxFramerate = maxFramerate,
  910. CopyTimestamps = copyTimestamps ?? true,
  911. StartTimeTicks = startTimeTicks,
  912. Width = width,
  913. Height = height,
  914. VideoBitRate = videoBitRate,
  915. SubtitleStreamIndex = subtitleStreamIndex,
  916. SubtitleMethod = subtitleMethod,
  917. MaxRefFrames = maxRefFrames,
  918. MaxVideoBitDepth = maxVideoBitDepth,
  919. RequireAvc = requireAvc ?? true,
  920. DeInterlace = deInterlace ?? true,
  921. RequireNonAnamorphic = requireNonAnamorphic ?? true,
  922. TranscodingMaxAudioChannels = transcodingMaxAudioChannels,
  923. CpuCoreLimit = cpuCoreLimit,
  924. LiveStreamId = liveStreamId,
  925. EnableMpegtsM2TsMode = enableMpegtsM2TsMode ?? true,
  926. VideoCodec = videoCodec,
  927. SubtitleCodec = subtitleCodec,
  928. TranscodeReasons = transcodingReasons,
  929. AudioStreamIndex = audioStreamIndex,
  930. VideoStreamIndex = videoStreamIndex,
  931. Context = context,
  932. StreamOptions = streamOptions
  933. };
  934. return await GetDynamicSegment(streamingRequest, segmentId)
  935. .ConfigureAwait(false);
  936. }
  937. /// <summary>
  938. /// Gets a video stream using HTTP live streaming.
  939. /// </summary>
  940. /// <param name="itemId">The item id.</param>
  941. /// <param name="playlistId">The playlist id.</param>
  942. /// <param name="segmentId">The segment id.</param>
  943. /// <param name="container">The video container. Possible values are: ts, webm, asf, wmv, ogv, mp4, m4v, mkv, mpeg, mpg, avi, 3gp, wmv, wtv, m2ts, mov, iso, flv. </param>
  944. /// <param name="static">Optional. If true, the original file will be streamed statically without any encoding. Use either no url extension or the original file extension. true/false.</param>
  945. /// <param name="params">The streaming parameters.</param>
  946. /// <param name="tag">The tag.</param>
  947. /// <param name="deviceProfileId">Optional. The dlna device profile id to utilize.</param>
  948. /// <param name="playSessionId">The play session id.</param>
  949. /// <param name="segmentContainer">The segment container.</param>
  950. /// <param name="segmentLength">The segment lenght.</param>
  951. /// <param name="minSegments">The minimum number of segments.</param>
  952. /// <param name="mediaSourceId">The media version id, if playing an alternate version.</param>
  953. /// <param name="deviceId">The device id of the client requesting. Used to stop encoding processes when needed.</param>
  954. /// <param name="audioCodec">Optional. Specify a audio codec to encode to, e.g. mp3. If omitted the server will auto-select using the url's extension. Options: aac, mp3, vorbis, wma.</param>
  955. /// <param name="enableAutoStreamCopy">Whether or not to allow automatic stream copy if requested values match the original source. Defaults to true.</param>
  956. /// <param name="allowVideoStreamCopy">Whether or not to allow copying of the video stream url.</param>
  957. /// <param name="allowAudioStreamCopy">Whether or not to allow copying of the audio stream url.</param>
  958. /// <param name="breakOnNonKeyFrames">Optional. Whether to break on non key frames.</param>
  959. /// <param name="audioSampleRate">Optional. Specify a specific audio sample rate, e.g. 44100.</param>
  960. /// <param name="maxAudioBitDepth">Optional. The maximum audio bit depth.</param>
  961. /// <param name="audioBitRate">Optional. Specify an audio bitrate to encode to, e.g. 128000. If omitted this will be left to encoder defaults.</param>
  962. /// <param name="audioChannels">Optional. Specify a specific number of audio channels to encode to, e.g. 2.</param>
  963. /// <param name="maxAudioChannels">Optional. Specify a maximum number of audio channels to encode to, e.g. 2.</param>
  964. /// <param name="profile">Optional. Specify a specific an encoder profile (varies by encoder), e.g. main, baseline, high.</param>
  965. /// <param name="level">Optional. Specify a level for the encoder profile (varies by encoder), e.g. 3, 3.1.</param>
  966. /// <param name="framerate">Optional. A specific video framerate to encode to, e.g. 23.976. Generally this should be omitted unless the device has specific requirements.</param>
  967. /// <param name="maxFramerate">Optional. A specific maximum video framerate to encode to, e.g. 23.976. Generally this should be omitted unless the device has specific requirements.</param>
  968. /// <param name="copyTimestamps">Whether or not to copy timestamps when transcoding with an offset. Defaults to false.</param>
  969. /// <param name="startTimeTicks">Optional. Specify a starting offset, in ticks. 1 tick = 10000 ms.</param>
  970. /// <param name="width">Optional. The fixed horizontal resolution of the encoded video.</param>
  971. /// <param name="height">Optional. The fixed vertical resolution of the encoded video.</param>
  972. /// <param name="videoBitRate">Optional. Specify a video bitrate to encode to, e.g. 500000. If omitted this will be left to encoder defaults.</param>
  973. /// <param name="subtitleStreamIndex">Optional. The index of the subtitle stream to use. If omitted no subtitles will be used.</param>
  974. /// <param name="subtitleMethod">Optional. Specify the subtitle delivery method.</param>
  975. /// <param name="maxRefFrames">Optional.</param>
  976. /// <param name="maxVideoBitDepth">Optional. The maximum video bit depth.</param>
  977. /// <param name="requireAvc">Optional. Whether to require avc.</param>
  978. /// <param name="deInterlace">Optional. Whether to deinterlace the video.</param>
  979. /// <param name="requireNonAnamorphic">Optional. Whether to require a non anamporphic stream.</param>
  980. /// <param name="transcodingMaxAudioChannels">Optional. The maximum number of audio channels to transcode.</param>
  981. /// <param name="cpuCoreLimit">Optional. The limit of how many cpu cores to use.</param>
  982. /// <param name="liveStreamId">The live stream id.</param>
  983. /// <param name="enableMpegtsM2TsMode">Optional. Whether to enable the MpegtsM2Ts mode.</param>
  984. /// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h265, h264, mpeg4, theora, vpx, wmv.</param>
  985. /// <param name="subtitleCodec">Optional. Specify a subtitle codec to encode to.</param>
  986. /// <param name="transcodingReasons">Optional. The transcoding reason.</param>
  987. /// <param name="audioStreamIndex">Optional. The index of the audio stream to use. If omitted the first audio stream will be used.</param>
  988. /// <param name="videoStreamIndex">Optional. The index of the video stream to use. If omitted the first video stream will be used.</param>
  989. /// <param name="context">Optional. The <see cref="EncodingContext"/>.</param>
  990. /// <param name="streamOptions">Optional. The streaming options.</param>
  991. /// <response code="200">Video stream returned.</response>
  992. /// <returns>A <see cref="FileResult"/> containing the audio file.</returns>
  993. [HttpGet("Audio/{itemId}/hls1/{playlistId}/{segmentId}.{container}")]
  994. [ProducesResponseType(StatusCodes.Status200OK)]
  995. [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "playlistId", Justification = "Imported from ServiceStack")]
  996. public async Task<ActionResult> GetHlsAudioSegment(
  997. [FromRoute] Guid itemId,
  998. [FromRoute] string playlistId,
  999. [FromRoute] int segmentId,
  1000. [FromRoute] string container,
  1001. [FromQuery] bool? @static,
  1002. [FromQuery] string? @params,
  1003. [FromQuery] string? tag,
  1004. [FromQuery] string? deviceProfileId,
  1005. [FromQuery] string? playSessionId,
  1006. [FromQuery] string? segmentContainer,
  1007. [FromQuery] int? segmentLength,
  1008. [FromQuery] int? minSegments,
  1009. [FromQuery] string? mediaSourceId,
  1010. [FromQuery] string? deviceId,
  1011. [FromQuery] string? audioCodec,
  1012. [FromQuery] bool? enableAutoStreamCopy,
  1013. [FromQuery] bool? allowVideoStreamCopy,
  1014. [FromQuery] bool? allowAudioStreamCopy,
  1015. [FromQuery] bool? breakOnNonKeyFrames,
  1016. [FromQuery] int? audioSampleRate,
  1017. [FromQuery] int? maxAudioBitDepth,
  1018. [FromQuery] int? audioBitRate,
  1019. [FromQuery] int? audioChannels,
  1020. [FromQuery] int? maxAudioChannels,
  1021. [FromQuery] string? profile,
  1022. [FromQuery] string? level,
  1023. [FromQuery] float? framerate,
  1024. [FromQuery] float? maxFramerate,
  1025. [FromQuery] bool? copyTimestamps,
  1026. [FromQuery] long? startTimeTicks,
  1027. [FromQuery] int? width,
  1028. [FromQuery] int? height,
  1029. [FromQuery] int? videoBitRate,
  1030. [FromQuery] int? subtitleStreamIndex,
  1031. [FromQuery] SubtitleDeliveryMethod subtitleMethod,
  1032. [FromQuery] int? maxRefFrames,
  1033. [FromQuery] int? maxVideoBitDepth,
  1034. [FromQuery] bool? requireAvc,
  1035. [FromQuery] bool? deInterlace,
  1036. [FromQuery] bool? requireNonAnamorphic,
  1037. [FromQuery] int? transcodingMaxAudioChannels,
  1038. [FromQuery] int? cpuCoreLimit,
  1039. [FromQuery] string? liveStreamId,
  1040. [FromQuery] bool? enableMpegtsM2TsMode,
  1041. [FromQuery] string? videoCodec,
  1042. [FromQuery] string? subtitleCodec,
  1043. [FromQuery] string? transcodingReasons,
  1044. [FromQuery] int? audioStreamIndex,
  1045. [FromQuery] int? videoStreamIndex,
  1046. [FromQuery] EncodingContext context,
  1047. [FromQuery] Dictionary<string, string> streamOptions)
  1048. {
  1049. var streamingRequest = new StreamingRequestDto
  1050. {
  1051. Id = itemId,
  1052. Container = container,
  1053. Static = @static ?? true,
  1054. Params = @params,
  1055. Tag = tag,
  1056. DeviceProfileId = deviceProfileId,
  1057. PlaySessionId = playSessionId,
  1058. SegmentContainer = segmentContainer,
  1059. SegmentLength = segmentLength,
  1060. MinSegments = minSegments,
  1061. MediaSourceId = mediaSourceId,
  1062. DeviceId = deviceId,
  1063. AudioCodec = audioCodec,
  1064. EnableAutoStreamCopy = enableAutoStreamCopy ?? true,
  1065. AllowAudioStreamCopy = allowAudioStreamCopy ?? true,
  1066. AllowVideoStreamCopy = allowVideoStreamCopy ?? true,
  1067. BreakOnNonKeyFrames = breakOnNonKeyFrames ?? false,
  1068. AudioSampleRate = audioSampleRate,
  1069. MaxAudioChannels = maxAudioChannels,
  1070. AudioBitRate = audioBitRate,
  1071. MaxAudioBitDepth = maxAudioBitDepth,
  1072. AudioChannels = audioChannels,
  1073. Profile = profile,
  1074. Level = level,
  1075. Framerate = framerate,
  1076. MaxFramerate = maxFramerate,
  1077. CopyTimestamps = copyTimestamps ?? true,
  1078. StartTimeTicks = startTimeTicks,
  1079. Width = width,
  1080. Height = height,
  1081. VideoBitRate = videoBitRate,
  1082. SubtitleStreamIndex = subtitleStreamIndex,
  1083. SubtitleMethod = subtitleMethod,
  1084. MaxRefFrames = maxRefFrames,
  1085. MaxVideoBitDepth = maxVideoBitDepth,
  1086. RequireAvc = requireAvc ?? true,
  1087. DeInterlace = deInterlace ?? true,
  1088. RequireNonAnamorphic = requireNonAnamorphic ?? true,
  1089. TranscodingMaxAudioChannels = transcodingMaxAudioChannels,
  1090. CpuCoreLimit = cpuCoreLimit,
  1091. LiveStreamId = liveStreamId,
  1092. EnableMpegtsM2TsMode = enableMpegtsM2TsMode ?? true,
  1093. VideoCodec = videoCodec,
  1094. SubtitleCodec = subtitleCodec,
  1095. TranscodeReasons = transcodingReasons,
  1096. AudioStreamIndex = audioStreamIndex,
  1097. VideoStreamIndex = videoStreamIndex,
  1098. Context = context,
  1099. StreamOptions = streamOptions
  1100. };
  1101. return await GetDynamicSegment(streamingRequest, segmentId)
  1102. .ConfigureAwait(false);
  1103. }
  1104. private async Task<ActionResult> GetMasterPlaylistInternal(
  1105. StreamingRequestDto streamingRequest,
  1106. bool isHeadRequest,
  1107. bool enableAdaptiveBitrateStreaming,
  1108. CancellationTokenSource cancellationTokenSource)
  1109. {
  1110. using var state = await StreamingHelpers.GetStreamingState(
  1111. streamingRequest,
  1112. Request,
  1113. _authContext,
  1114. _mediaSourceManager,
  1115. _userManager,
  1116. _libraryManager,
  1117. _serverConfigurationManager,
  1118. _mediaEncoder,
  1119. _fileSystem,
  1120. _subtitleEncoder,
  1121. _configuration,
  1122. _dlnaManager,
  1123. _deviceManager,
  1124. _transcodingJobHelper,
  1125. _transcodingJobType,
  1126. cancellationTokenSource.Token)
  1127. .ConfigureAwait(false);
  1128. Response.Headers.Add(HeaderNames.Expires, "0");
  1129. if (isHeadRequest)
  1130. {
  1131. return new FileContentResult(Array.Empty<byte>(), MimeTypes.GetMimeType("playlist.m3u8"));
  1132. }
  1133. var totalBitrate = state.OutputAudioBitrate ?? 0 + state.OutputVideoBitrate ?? 0;
  1134. var builder = new StringBuilder();
  1135. builder.AppendLine("#EXTM3U");
  1136. var isLiveStream = state.IsSegmentedLiveStream;
  1137. var queryString = Request.QueryString.ToString();
  1138. // from universal audio service
  1139. if (queryString.IndexOf("SegmentContainer", StringComparison.OrdinalIgnoreCase) == -1 && !string.IsNullOrWhiteSpace(state.Request.SegmentContainer))
  1140. {
  1141. queryString += "&SegmentContainer=" + state.Request.SegmentContainer;
  1142. }
  1143. // from universal audio service
  1144. if (!string.IsNullOrWhiteSpace(state.Request.TranscodeReasons) && queryString.IndexOf("TranscodeReasons=", StringComparison.OrdinalIgnoreCase) == -1)
  1145. {
  1146. queryString += "&TranscodeReasons=" + state.Request.TranscodeReasons;
  1147. }
  1148. // Main stream
  1149. var playlistUrl = isLiveStream ? "live.m3u8" : "main.m3u8";
  1150. playlistUrl += queryString;
  1151. var subtitleStreams = state.MediaSource
  1152. .MediaStreams
  1153. .Where(i => i.IsTextSubtitleStream)
  1154. .ToList();
  1155. var subtitleGroup = subtitleStreams.Count > 0 && (state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Hls || state.VideoRequest!.EnableSubtitlesInManifest)
  1156. ? "subs"
  1157. : null;
  1158. // If we're burning in subtitles then don't add additional subs to the manifest
  1159. if (state.SubtitleStream != null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode)
  1160. {
  1161. subtitleGroup = null;
  1162. }
  1163. if (!string.IsNullOrWhiteSpace(subtitleGroup))
  1164. {
  1165. AddSubtitles(state, subtitleStreams, builder);
  1166. }
  1167. AppendPlaylist(builder, state, playlistUrl, totalBitrate, subtitleGroup);
  1168. if (EnableAdaptiveBitrateStreaming(state, isLiveStream, enableAdaptiveBitrateStreaming))
  1169. {
  1170. var requestedVideoBitrate = state.VideoRequest == null ? 0 : state.VideoRequest.VideoBitRate ?? 0;
  1171. // By default, vary by just 200k
  1172. var variation = GetBitrateVariation(totalBitrate);
  1173. var newBitrate = totalBitrate - variation;
  1174. var variantUrl = ReplaceBitrate(playlistUrl, requestedVideoBitrate, requestedVideoBitrate - variation);
  1175. AppendPlaylist(builder, state, variantUrl, newBitrate, subtitleGroup);
  1176. variation *= 2;
  1177. newBitrate = totalBitrate - variation;
  1178. variantUrl = ReplaceBitrate(playlistUrl, requestedVideoBitrate, requestedVideoBitrate - variation);
  1179. AppendPlaylist(builder, state, variantUrl, newBitrate, subtitleGroup);
  1180. }
  1181. return new FileContentResult(Encoding.UTF8.GetBytes(builder.ToString()), MimeTypes.GetMimeType("playlist.m3u8"));
  1182. }
  1183. private async Task<ActionResult> GetVariantPlaylistInternal(StreamingRequestDto streamingRequest, string name, CancellationTokenSource cancellationTokenSource)
  1184. {
  1185. using var state = await StreamingHelpers.GetStreamingState(
  1186. streamingRequest,
  1187. Request,
  1188. _authContext,
  1189. _mediaSourceManager,
  1190. _userManager,
  1191. _libraryManager,
  1192. _serverConfigurationManager,
  1193. _mediaEncoder,
  1194. _fileSystem,
  1195. _subtitleEncoder,
  1196. _configuration,
  1197. _dlnaManager,
  1198. _deviceManager,
  1199. _transcodingJobHelper,
  1200. _transcodingJobType,
  1201. cancellationTokenSource.Token)
  1202. .ConfigureAwait(false);
  1203. Response.Headers.Add(HeaderNames.Expires, "0");
  1204. var segmentLengths = GetSegmentLengths(state);
  1205. var builder = new StringBuilder();
  1206. builder.AppendLine("#EXTM3U");
  1207. builder.AppendLine("#EXT-X-PLAYLIST-TYPE:VOD");
  1208. builder.AppendLine("#EXT-X-VERSION:3");
  1209. builder.AppendLine("#EXT-X-TARGETDURATION:" + Math.Ceiling(segmentLengths.Length > 0 ? segmentLengths.Max() : state.SegmentLength).ToString(CultureInfo.InvariantCulture));
  1210. builder.AppendLine("#EXT-X-MEDIA-SEQUENCE:0");
  1211. var queryString = Request.QueryString;
  1212. var index = 0;
  1213. var segmentExtension = GetSegmentFileExtension(streamingRequest.SegmentContainer);
  1214. foreach (var length in segmentLengths)
  1215. {
  1216. builder.AppendLine("#EXTINF:" + length.ToString("0.0000", CultureInfo.InvariantCulture) + ", nodesc");
  1217. builder.AppendLine(
  1218. string.Format(
  1219. CultureInfo.InvariantCulture,
  1220. "hls1/{0}/{1}{2}{3}",
  1221. name,
  1222. index.ToString(CultureInfo.InvariantCulture),
  1223. segmentExtension,
  1224. queryString));
  1225. index++;
  1226. }
  1227. builder.AppendLine("#EXT-X-ENDLIST");
  1228. return new FileContentResult(Encoding.UTF8.GetBytes(builder.ToString()), MimeTypes.GetMimeType("playlist.m3u8"));
  1229. }
  1230. private async Task<ActionResult> GetDynamicSegment(StreamingRequestDto streamingRequest, int segmentId)
  1231. {
  1232. if ((streamingRequest.StartTimeTicks ?? 0) > 0)
  1233. {
  1234. throw new ArgumentException("StartTimeTicks is not allowed.");
  1235. }
  1236. var cancellationTokenSource = new CancellationTokenSource();
  1237. var cancellationToken = cancellationTokenSource.Token;
  1238. using var state = await StreamingHelpers.GetStreamingState(
  1239. streamingRequest,
  1240. Request,
  1241. _authContext,
  1242. _mediaSourceManager,
  1243. _userManager,
  1244. _libraryManager,
  1245. _serverConfigurationManager,
  1246. _mediaEncoder,
  1247. _fileSystem,
  1248. _subtitleEncoder,
  1249. _configuration,
  1250. _dlnaManager,
  1251. _deviceManager,
  1252. _transcodingJobHelper,
  1253. _transcodingJobType,
  1254. cancellationTokenSource.Token)
  1255. .ConfigureAwait(false);
  1256. var playlistPath = Path.ChangeExtension(state.OutputFilePath, ".m3u8");
  1257. var segmentPath = GetSegmentPath(state, playlistPath, segmentId);
  1258. var segmentExtension = GetSegmentFileExtension(state.Request.SegmentContainer);
  1259. TranscodingJobDto? job;
  1260. if (System.IO.File.Exists(segmentPath))
  1261. {
  1262. job = _transcodingJobHelper.OnTranscodeBeginRequest(playlistPath, _transcodingJobType);
  1263. _logger.LogDebug("returning {0} [it exists, try 1]", segmentPath);
  1264. return await GetSegmentResult(state, playlistPath, segmentPath, segmentExtension, segmentId, job, cancellationToken).ConfigureAwait(false);
  1265. }
  1266. var transcodingLock = _transcodingJobHelper.GetTranscodingLock(playlistPath);
  1267. await transcodingLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
  1268. var released = false;
  1269. var startTranscoding = false;
  1270. try
  1271. {
  1272. if (System.IO.File.Exists(segmentPath))
  1273. {
  1274. job = _transcodingJobHelper.OnTranscodeBeginRequest(playlistPath, _transcodingJobType);
  1275. transcodingLock.Release();
  1276. released = true;
  1277. _logger.LogDebug("returning {0} [it exists, try 2]", segmentPath);
  1278. return await GetSegmentResult(state, playlistPath, segmentPath, segmentExtension, segmentId, job, cancellationToken).ConfigureAwait(false);
  1279. }
  1280. else
  1281. {
  1282. var currentTranscodingIndex = GetCurrentTranscodingIndex(playlistPath, segmentExtension);
  1283. var segmentGapRequiringTranscodingChange = 24 / state.SegmentLength;
  1284. if (currentTranscodingIndex == null)
  1285. {
  1286. _logger.LogDebug("Starting transcoding because currentTranscodingIndex=null");
  1287. startTranscoding = true;
  1288. }
  1289. else if (segmentId < currentTranscodingIndex.Value)
  1290. {
  1291. _logger.LogDebug("Starting transcoding because requestedIndex={0} and currentTranscodingIndex={1}", segmentId, currentTranscodingIndex);
  1292. startTranscoding = true;
  1293. }
  1294. else if (segmentId - currentTranscodingIndex.Value > segmentGapRequiringTranscodingChange)
  1295. {
  1296. _logger.LogDebug("Starting transcoding because segmentGap is {0} and max allowed gap is {1}. requestedIndex={2}", segmentId - currentTranscodingIndex.Value, segmentGapRequiringTranscodingChange, segmentId);
  1297. startTranscoding = true;
  1298. }
  1299. if (startTranscoding)
  1300. {
  1301. // If the playlist doesn't already exist, startup ffmpeg
  1302. try
  1303. {
  1304. await _transcodingJobHelper.KillTranscodingJobs(streamingRequest.DeviceId, streamingRequest.PlaySessionId, p => false)
  1305. .ConfigureAwait(false);
  1306. if (currentTranscodingIndex.HasValue)
  1307. {
  1308. DeleteLastFile(playlistPath, segmentExtension, 0);
  1309. }
  1310. streamingRequest.StartTimeTicks = GetStartPositionTicks(state, segmentId);
  1311. state.WaitForPath = segmentPath;
  1312. var encodingOptions = _serverConfigurationManager.GetEncodingOptions();
  1313. job = await _transcodingJobHelper.StartFfMpeg(
  1314. state,
  1315. playlistPath,
  1316. GetCommandLineArguments(playlistPath, encodingOptions, state, true, segmentId),
  1317. Request,
  1318. _transcodingJobType,
  1319. cancellationTokenSource).ConfigureAwait(false);
  1320. }
  1321. catch
  1322. {
  1323. state.Dispose();
  1324. throw;
  1325. }
  1326. // await WaitForMinimumSegmentCount(playlistPath, 1, cancellationTokenSource.Token).ConfigureAwait(false);
  1327. }
  1328. else
  1329. {
  1330. job = _transcodingJobHelper.OnTranscodeBeginRequest(playlistPath, _transcodingJobType);
  1331. if (job?.TranscodingThrottler != null)
  1332. {
  1333. await job.TranscodingThrottler.UnpauseTranscoding().ConfigureAwait(false);
  1334. }
  1335. }
  1336. }
  1337. }
  1338. finally
  1339. {
  1340. if (!released)
  1341. {
  1342. transcodingLock.Release();
  1343. }
  1344. }
  1345. _logger.LogDebug("returning {0} [general case]", segmentPath);
  1346. job ??= _transcodingJobHelper.OnTranscodeBeginRequest(playlistPath, _transcodingJobType);
  1347. return await GetSegmentResult(state, playlistPath, segmentPath, segmentExtension, segmentId, job, cancellationToken).ConfigureAwait(false);
  1348. }
  1349. private void AddSubtitles(StreamState state, IEnumerable<MediaStream> subtitles, StringBuilder builder)
  1350. {
  1351. var selectedIndex = state.SubtitleStream == null || state.SubtitleDeliveryMethod != SubtitleDeliveryMethod.Hls ? (int?)null : state.SubtitleStream.Index;
  1352. const string Format = "#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID=\"subs\",NAME=\"{0}\",DEFAULT={1},FORCED={2},AUTOSELECT=YES,URI=\"{3}\",LANGUAGE=\"{4}\"";
  1353. foreach (var stream in subtitles)
  1354. {
  1355. var name = stream.DisplayTitle;
  1356. var isDefault = selectedIndex.HasValue && selectedIndex.Value == stream.Index;
  1357. var isForced = stream.IsForced;
  1358. var url = string.Format(
  1359. CultureInfo.InvariantCulture,
  1360. "{0}/Subtitles/{1}/subtitles.m3u8?SegmentLength={2}&api_key={3}",
  1361. state.Request.MediaSourceId,
  1362. stream.Index.ToString(CultureInfo.InvariantCulture),
  1363. 30.ToString(CultureInfo.InvariantCulture),
  1364. ClaimHelpers.GetToken(Request.HttpContext.User));
  1365. var line = string.Format(
  1366. CultureInfo.InvariantCulture,
  1367. Format,
  1368. name,
  1369. isDefault ? "YES" : "NO",
  1370. isForced ? "YES" : "NO",
  1371. url,
  1372. stream.Language ?? "Unknown");
  1373. builder.AppendLine(line);
  1374. }
  1375. }
  1376. private void AppendPlaylist(StringBuilder builder, StreamState state, string url, int bitrate, string? subtitleGroup)
  1377. {
  1378. builder.Append("#EXT-X-STREAM-INF:BANDWIDTH=")
  1379. .Append(bitrate.ToString(CultureInfo.InvariantCulture))
  1380. .Append(",AVERAGE-BANDWIDTH=")
  1381. .Append(bitrate.ToString(CultureInfo.InvariantCulture));
  1382. AppendPlaylistCodecsField(builder, state);
  1383. AppendPlaylistResolutionField(builder, state);
  1384. AppendPlaylistFramerateField(builder, state);
  1385. if (!string.IsNullOrWhiteSpace(subtitleGroup))
  1386. {
  1387. builder.Append(",SUBTITLES=\"")
  1388. .Append(subtitleGroup)
  1389. .Append('"');
  1390. }
  1391. builder.Append(Environment.NewLine);
  1392. builder.AppendLine(url);
  1393. }
  1394. /// <summary>
  1395. /// Appends a CODECS field containing formatted strings of
  1396. /// the active streams output video and audio codecs.
  1397. /// </summary>
  1398. /// <seealso cref="AppendPlaylist(StringBuilder, StreamState, string, int, string)"/>
  1399. /// <seealso cref="GetPlaylistVideoCodecs(StreamState, string, int)"/>
  1400. /// <seealso cref="GetPlaylistAudioCodecs(StreamState)"/>
  1401. /// <param name="builder">StringBuilder to append the field to.</param>
  1402. /// <param name="state">StreamState of the current stream.</param>
  1403. private void AppendPlaylistCodecsField(StringBuilder builder, StreamState state)
  1404. {
  1405. // Video
  1406. string videoCodecs = string.Empty;
  1407. int? videoCodecLevel = GetOutputVideoCodecLevel(state);
  1408. if (!string.IsNullOrEmpty(state.ActualOutputVideoCodec) && videoCodecLevel.HasValue)
  1409. {
  1410. videoCodecs = GetPlaylistVideoCodecs(state, state.ActualOutputVideoCodec, videoCodecLevel.Value);
  1411. }
  1412. // Audio
  1413. string audioCodecs = string.Empty;
  1414. if (!string.IsNullOrEmpty(state.ActualOutputAudioCodec))
  1415. {
  1416. audioCodecs = GetPlaylistAudioCodecs(state);
  1417. }
  1418. StringBuilder codecs = new StringBuilder();
  1419. codecs.Append(videoCodecs)
  1420. .Append(',')
  1421. .Append(audioCodecs);
  1422. if (codecs.Length > 1)
  1423. {
  1424. builder.Append(",CODECS=\"")
  1425. .Append(codecs)
  1426. .Append('"');
  1427. }
  1428. }
  1429. /// <summary>
  1430. /// Appends a RESOLUTION field containing the resolution of the output stream.
  1431. /// </summary>
  1432. /// <seealso cref="AppendPlaylist(StringBuilder, StreamState, string, int, string)"/>
  1433. /// <param name="builder">StringBuilder to append the field to.</param>
  1434. /// <param name="state">StreamState of the current stream.</param>
  1435. private void AppendPlaylistResolutionField(StringBuilder builder, StreamState state)
  1436. {
  1437. if (state.OutputWidth.HasValue && state.OutputHeight.HasValue)
  1438. {
  1439. builder.Append(",RESOLUTION=")
  1440. .Append(state.OutputWidth.GetValueOrDefault())
  1441. .Append('x')
  1442. .Append(state.OutputHeight.GetValueOrDefault());
  1443. }
  1444. }
  1445. /// <summary>
  1446. /// Appends a FRAME-RATE field containing the framerate of the output stream.
  1447. /// </summary>
  1448. /// <seealso cref="AppendPlaylist(StringBuilder, StreamState, string, int, string)"/>
  1449. /// <param name="builder">StringBuilder to append the field to.</param>
  1450. /// <param name="state">StreamState of the current stream.</param>
  1451. private void AppendPlaylistFramerateField(StringBuilder builder, StreamState state)
  1452. {
  1453. double? framerate = null;
  1454. if (state.TargetFramerate.HasValue)
  1455. {
  1456. framerate = Math.Round(state.TargetFramerate.GetValueOrDefault(), 3);
  1457. }
  1458. else if (state.VideoStream?.RealFrameRate != null)
  1459. {
  1460. framerate = Math.Round(state.VideoStream.RealFrameRate.GetValueOrDefault(), 3);
  1461. }
  1462. if (framerate.HasValue)
  1463. {
  1464. builder.Append(",FRAME-RATE=")
  1465. .Append(framerate.Value);
  1466. }
  1467. }
  1468. private bool EnableAdaptiveBitrateStreaming(StreamState state, bool isLiveStream, bool enableAdaptiveBitrateStreaming)
  1469. {
  1470. // Within the local network this will likely do more harm than good.
  1471. var ip = RequestHelpers.NormalizeIp(Request.HttpContext.Connection.RemoteIpAddress).ToString();
  1472. if (_networkManager.IsInLocalNetwork(ip))
  1473. {
  1474. return false;
  1475. }
  1476. if (!enableAdaptiveBitrateStreaming)
  1477. {
  1478. return false;
  1479. }
  1480. if (isLiveStream || string.IsNullOrWhiteSpace(state.MediaPath))
  1481. {
  1482. // Opening live streams is so slow it's not even worth it
  1483. return false;
  1484. }
  1485. if (EncodingHelper.IsCopyCodec(state.OutputVideoCodec))
  1486. {
  1487. return false;
  1488. }
  1489. if (EncodingHelper.IsCopyCodec(state.OutputAudioCodec))
  1490. {
  1491. return false;
  1492. }
  1493. if (!state.IsOutputVideo)
  1494. {
  1495. return false;
  1496. }
  1497. // Having problems in android
  1498. return false;
  1499. // return state.VideoRequest.VideoBitRate.HasValue;
  1500. }
  1501. /// <summary>
  1502. /// Get the H.26X level of the output video stream.
  1503. /// </summary>
  1504. /// <param name="state">StreamState of the current stream.</param>
  1505. /// <returns>H.26X level of the output video stream.</returns>
  1506. private int? GetOutputVideoCodecLevel(StreamState state)
  1507. {
  1508. string? levelString;
  1509. if (EncodingHelper.IsCopyCodec(state.OutputVideoCodec)
  1510. && state.VideoStream.Level.HasValue)
  1511. {
  1512. levelString = state.VideoStream?.Level.ToString();
  1513. }
  1514. else
  1515. {
  1516. levelString = state.GetRequestedLevel(state.ActualOutputVideoCodec);
  1517. }
  1518. if (int.TryParse(levelString, NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsedLevel))
  1519. {
  1520. return parsedLevel;
  1521. }
  1522. return null;
  1523. }
  1524. /// <summary>
  1525. /// Gets a formatted string of the output audio codec, for use in the CODECS field.
  1526. /// </summary>
  1527. /// <seealso cref="AppendPlaylistCodecsField(StringBuilder, StreamState)"/>
  1528. /// <seealso cref="GetPlaylistVideoCodecs(StreamState, string, int)"/>
  1529. /// <param name="state">StreamState of the current stream.</param>
  1530. /// <returns>Formatted audio codec string.</returns>
  1531. private string GetPlaylistAudioCodecs(StreamState state)
  1532. {
  1533. if (string.Equals(state.ActualOutputAudioCodec, "aac", StringComparison.OrdinalIgnoreCase))
  1534. {
  1535. string? profile = state.GetRequestedProfiles("aac").FirstOrDefault();
  1536. return HlsCodecStringHelpers.GetAACString(profile);
  1537. }
  1538. if (string.Equals(state.ActualOutputAudioCodec, "mp3", StringComparison.OrdinalIgnoreCase))
  1539. {
  1540. return HlsCodecStringHelpers.GetMP3String();
  1541. }
  1542. if (string.Equals(state.ActualOutputAudioCodec, "ac3", StringComparison.OrdinalIgnoreCase))
  1543. {
  1544. return HlsCodecStringHelpers.GetAC3String();
  1545. }
  1546. if (string.Equals(state.ActualOutputAudioCodec, "eac3", StringComparison.OrdinalIgnoreCase))
  1547. {
  1548. return HlsCodecStringHelpers.GetEAC3String();
  1549. }
  1550. return string.Empty;
  1551. }
  1552. /// <summary>
  1553. /// Gets a formatted string of the output video codec, for use in the CODECS field.
  1554. /// </summary>
  1555. /// <seealso cref="AppendPlaylistCodecsField(StringBuilder, StreamState)"/>
  1556. /// <seealso cref="GetPlaylistAudioCodecs(StreamState)"/>
  1557. /// <param name="state">StreamState of the current stream.</param>
  1558. /// <param name="codec">Video codec.</param>
  1559. /// <param name="level">Video level.</param>
  1560. /// <returns>Formatted video codec string.</returns>
  1561. private string GetPlaylistVideoCodecs(StreamState state, string codec, int level)
  1562. {
  1563. if (level == 0)
  1564. {
  1565. // This is 0 when there's no requested H.26X level in the device profile
  1566. // and the source is not encoded in H.26X
  1567. _logger.LogError("Got invalid H.26X level when building CODECS field for HLS master playlist");
  1568. return string.Empty;
  1569. }
  1570. if (string.Equals(codec, "h264", StringComparison.OrdinalIgnoreCase))
  1571. {
  1572. string profile = state.GetRequestedProfiles("h264").FirstOrDefault();
  1573. return HlsCodecStringHelpers.GetH264String(profile, level);
  1574. }
  1575. if (string.Equals(codec, "h265", StringComparison.OrdinalIgnoreCase)
  1576. || string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase))
  1577. {
  1578. string profile = state.GetRequestedProfiles("h265").FirstOrDefault();
  1579. return HlsCodecStringHelpers.GetH265String(profile, level);
  1580. }
  1581. return string.Empty;
  1582. }
  1583. private int GetBitrateVariation(int bitrate)
  1584. {
  1585. // By default, vary by just 50k
  1586. var variation = 50000;
  1587. if (bitrate >= 10000000)
  1588. {
  1589. variation = 2000000;
  1590. }
  1591. else if (bitrate >= 5000000)
  1592. {
  1593. variation = 1500000;
  1594. }
  1595. else if (bitrate >= 3000000)
  1596. {
  1597. variation = 1000000;
  1598. }
  1599. else if (bitrate >= 2000000)
  1600. {
  1601. variation = 500000;
  1602. }
  1603. else if (bitrate >= 1000000)
  1604. {
  1605. variation = 300000;
  1606. }
  1607. else if (bitrate >= 600000)
  1608. {
  1609. variation = 200000;
  1610. }
  1611. else if (bitrate >= 400000)
  1612. {
  1613. variation = 100000;
  1614. }
  1615. return variation;
  1616. }
  1617. private string ReplaceBitrate(string url, int oldValue, int newValue)
  1618. {
  1619. return url.Replace(
  1620. "videobitrate=" + oldValue.ToString(CultureInfo.InvariantCulture),
  1621. "videobitrate=" + newValue.ToString(CultureInfo.InvariantCulture),
  1622. StringComparison.OrdinalIgnoreCase);
  1623. }
  1624. private double[] GetSegmentLengths(StreamState state)
  1625. {
  1626. var result = new List<double>();
  1627. var ticks = state.RunTimeTicks ?? 0;
  1628. var segmentLengthTicks = TimeSpan.FromSeconds(state.SegmentLength).Ticks;
  1629. while (ticks > 0)
  1630. {
  1631. var length = ticks >= segmentLengthTicks ? segmentLengthTicks : ticks;
  1632. result.Add(TimeSpan.FromTicks(length).TotalSeconds);
  1633. ticks -= length;
  1634. }
  1635. return result.ToArray();
  1636. }
  1637. private string GetCommandLineArguments(string outputPath, EncodingOptions encodingOptions, StreamState state, bool isEncoding, int startNumber)
  1638. {
  1639. var videoCodec = _encodingHelper.GetVideoEncoder(state, encodingOptions);
  1640. var threads = _encodingHelper.GetNumberOfThreads(state, encodingOptions, videoCodec);
  1641. if (state.BaseRequest.BreakOnNonKeyFrames)
  1642. {
  1643. // FIXME: this is actually a workaround, as ideally it really should be the client which decides whether non-keyframe
  1644. // breakpoints are supported; but current implementation always uses "ffmpeg input seeking" which is liable
  1645. // to produce a missing part of video stream before first keyframe is encountered, which may lead to
  1646. // awkward cases like a few starting HLS segments having no video whatsoever, which breaks hls.js
  1647. _logger.LogInformation("Current HLS implementation doesn't support non-keyframe breaks but one is requested, ignoring that request");
  1648. state.BaseRequest.BreakOnNonKeyFrames = false;
  1649. }
  1650. var inputModifier = _encodingHelper.GetInputModifier(state, encodingOptions);
  1651. // If isEncoding is true we're actually starting ffmpeg
  1652. var startNumberParam = isEncoding ? startNumber.ToString(CultureInfo.InvariantCulture) : "0";
  1653. var mapArgs = state.IsOutputVideo ? _encodingHelper.GetMapArgs(state) : string.Empty;
  1654. var outputTsArg = Path.Combine(Path.GetDirectoryName(outputPath), Path.GetFileNameWithoutExtension(outputPath)) + "%d" + GetSegmentFileExtension(state.Request.SegmentContainer);
  1655. var segmentFormat = GetSegmentFileExtension(state.Request.SegmentContainer).TrimStart('.');
  1656. if (string.Equals(segmentFormat, "ts", StringComparison.OrdinalIgnoreCase))
  1657. {
  1658. segmentFormat = "mpegts";
  1659. }
  1660. return string.Format(
  1661. CultureInfo.InvariantCulture,
  1662. "{0} {1} -map_metadata -1 -map_chapters -1 -threads {2} {3} {4} {5} -copyts -avoid_negative_ts disabled -f hls -max_delay 5000000 -hls_time {6} -individual_header_trailer 0 -hls_segment_type {7} -start_number {8} -hls_segment_filename \"{9}\" -hls_playlist_type vod -hls_list_size 0 -y \"{10}\"",
  1663. inputModifier,
  1664. _encodingHelper.GetInputArgument(state, encodingOptions),
  1665. threads,
  1666. mapArgs,
  1667. GetVideoArguments(state, encodingOptions, startNumber),
  1668. GetAudioArguments(state, encodingOptions),
  1669. state.SegmentLength.ToString(CultureInfo.InvariantCulture),
  1670. segmentFormat,
  1671. startNumberParam,
  1672. outputTsArg,
  1673. outputPath).Trim();
  1674. }
  1675. private string GetAudioArguments(StreamState state, EncodingOptions encodingOptions)
  1676. {
  1677. var audioCodec = _encodingHelper.GetAudioEncoder(state);
  1678. if (!state.IsOutputVideo)
  1679. {
  1680. if (EncodingHelper.IsCopyCodec(audioCodec))
  1681. {
  1682. return "-acodec copy";
  1683. }
  1684. var audioTranscodeParams = new List<string>();
  1685. audioTranscodeParams.Add("-acodec " + audioCodec);
  1686. if (state.OutputAudioBitrate.HasValue)
  1687. {
  1688. audioTranscodeParams.Add("-ab " + state.OutputAudioBitrate.Value.ToString(CultureInfo.InvariantCulture));
  1689. }
  1690. if (state.OutputAudioChannels.HasValue)
  1691. {
  1692. audioTranscodeParams.Add("-ac " + state.OutputAudioChannels.Value.ToString(CultureInfo.InvariantCulture));
  1693. }
  1694. if (state.OutputAudioSampleRate.HasValue)
  1695. {
  1696. audioTranscodeParams.Add("-ar " + state.OutputAudioSampleRate.Value.ToString(CultureInfo.InvariantCulture));
  1697. }
  1698. audioTranscodeParams.Add("-vn");
  1699. return string.Join(" ", audioTranscodeParams.ToArray());
  1700. }
  1701. if (EncodingHelper.IsCopyCodec(audioCodec))
  1702. {
  1703. var videoCodec = _encodingHelper.GetVideoEncoder(state, encodingOptions);
  1704. if (EncodingHelper.IsCopyCodec(videoCodec) && state.EnableBreakOnNonKeyFrames(videoCodec))
  1705. {
  1706. return "-codec:a:0 copy -copypriorss:a:0 0";
  1707. }
  1708. return "-codec:a:0 copy";
  1709. }
  1710. var args = "-codec:a:0 " + audioCodec;
  1711. var channels = state.OutputAudioChannels;
  1712. if (channels.HasValue)
  1713. {
  1714. args += " -ac " + channels.Value;
  1715. }
  1716. var bitrate = state.OutputAudioBitrate;
  1717. if (bitrate.HasValue)
  1718. {
  1719. args += " -ab " + bitrate.Value.ToString(CultureInfo.InvariantCulture);
  1720. }
  1721. if (state.OutputAudioSampleRate.HasValue)
  1722. {
  1723. args += " -ar " + state.OutputAudioSampleRate.Value.ToString(CultureInfo.InvariantCulture);
  1724. }
  1725. args += " " + _encodingHelper.GetAudioFilterParam(state, encodingOptions, true);
  1726. return args;
  1727. }
  1728. private string GetVideoArguments(StreamState state, EncodingOptions encodingOptions, int startNumber)
  1729. {
  1730. if (!state.IsOutputVideo)
  1731. {
  1732. return string.Empty;
  1733. }
  1734. var codec = _encodingHelper.GetVideoEncoder(state, encodingOptions);
  1735. var args = "-codec:v:0 " + codec;
  1736. // if (state.EnableMpegtsM2TsMode)
  1737. // {
  1738. // args += " -mpegts_m2ts_mode 1";
  1739. // }
  1740. // See if we can save come cpu cycles by avoiding encoding
  1741. if (EncodingHelper.IsCopyCodec(codec))
  1742. {
  1743. if (state.VideoStream != null && !string.Equals(state.VideoStream.NalLengthSize, "0", StringComparison.OrdinalIgnoreCase))
  1744. {
  1745. string bitStreamArgs = _encodingHelper.GetBitStreamArgs(state.VideoStream);
  1746. if (!string.IsNullOrEmpty(bitStreamArgs))
  1747. {
  1748. args += " " + bitStreamArgs;
  1749. }
  1750. }
  1751. // args += " -flags -global_header";
  1752. }
  1753. else
  1754. {
  1755. var gopArg = string.Empty;
  1756. var keyFrameArg = string.Format(
  1757. CultureInfo.InvariantCulture,
  1758. " -force_key_frames:0 \"expr:gte(t,{0}+n_forced*{1})\"",
  1759. startNumber * state.SegmentLength,
  1760. state.SegmentLength);
  1761. var framerate = state.VideoStream?.RealFrameRate;
  1762. if (framerate.HasValue)
  1763. {
  1764. // This is to make sure keyframe interval is limited to our segment,
  1765. // as forcing keyframes is not enough.
  1766. // Example: we encoded half of desired length, then codec detected
  1767. // scene cut and inserted a keyframe; next forced keyframe would
  1768. // be created outside of segment, which breaks seeking
  1769. // -sc_threshold 0 is used to prevent the hardware encoder from post processing to break the set keyframe
  1770. gopArg = string.Format(
  1771. CultureInfo.InvariantCulture,
  1772. " -g {0} -keyint_min {0} -sc_threshold 0",
  1773. Math.Ceiling(state.SegmentLength * framerate.Value));
  1774. }
  1775. args += " " + _encodingHelper.GetVideoQualityParam(state, codec, encodingOptions, "veryfast");
  1776. // Unable to force key frames using these hw encoders, set key frames by GOP
  1777. if (string.Equals(codec, "h264_qsv", StringComparison.OrdinalIgnoreCase)
  1778. || string.Equals(codec, "h264_nvenc", StringComparison.OrdinalIgnoreCase)
  1779. || string.Equals(codec, "h264_amf", StringComparison.OrdinalIgnoreCase))
  1780. {
  1781. args += " " + gopArg;
  1782. }
  1783. else
  1784. {
  1785. args += " " + keyFrameArg + gopArg;
  1786. }
  1787. // args += " -mixed-refs 0 -refs 3 -x264opts b_pyramid=0:weightb=0:weightp=0";
  1788. var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
  1789. // This is for graphical subs
  1790. if (hasGraphicalSubs)
  1791. {
  1792. args += _encodingHelper.GetGraphicalSubtitleParam(state, encodingOptions, codec);
  1793. }
  1794. // Add resolution params, if specified
  1795. else
  1796. {
  1797. args += _encodingHelper.GetOutputSizeParam(state, encodingOptions, codec);
  1798. }
  1799. // -start_at_zero is necessary to use with -ss when seeking,
  1800. // otherwise the target position cannot be determined.
  1801. if (!(state.SubtitleStream != null && state.SubtitleStream.IsExternal && !state.SubtitleStream.IsTextSubtitleStream))
  1802. {
  1803. args += " -start_at_zero";
  1804. }
  1805. // args += " -flags -global_header";
  1806. }
  1807. if (!string.IsNullOrEmpty(state.OutputVideoSync))
  1808. {
  1809. args += " -vsync " + state.OutputVideoSync;
  1810. }
  1811. args += _encodingHelper.GetOutputFFlags(state);
  1812. return args;
  1813. }
  1814. private string GetSegmentFileExtension(string? segmentContainer)
  1815. {
  1816. if (!string.IsNullOrWhiteSpace(segmentContainer))
  1817. {
  1818. return "." + segmentContainer;
  1819. }
  1820. return ".ts";
  1821. }
  1822. private string GetSegmentPath(StreamState state, string playlist, int index)
  1823. {
  1824. var folder = Path.GetDirectoryName(playlist);
  1825. var filename = Path.GetFileNameWithoutExtension(playlist);
  1826. return Path.Combine(folder, filename + index.ToString(CultureInfo.InvariantCulture) + GetSegmentFileExtension(state.Request.SegmentContainer));
  1827. }
  1828. private async Task<ActionResult> GetSegmentResult(
  1829. StreamState state,
  1830. string playlistPath,
  1831. string segmentPath,
  1832. string segmentExtension,
  1833. int segmentIndex,
  1834. TranscodingJobDto? transcodingJob,
  1835. CancellationToken cancellationToken)
  1836. {
  1837. var segmentExists = System.IO.File.Exists(segmentPath);
  1838. if (segmentExists)
  1839. {
  1840. if (transcodingJob != null && transcodingJob.HasExited)
  1841. {
  1842. // Transcoding job is over, so assume all existing files are ready
  1843. _logger.LogDebug("serving up {0} as transcode is over", segmentPath);
  1844. return GetSegmentResult(state, segmentPath, segmentIndex, transcodingJob);
  1845. }
  1846. var currentTranscodingIndex = GetCurrentTranscodingIndex(playlistPath, segmentExtension);
  1847. // If requested segment is less than transcoding position, we can't transcode backwards, so assume it's ready
  1848. if (segmentIndex < currentTranscodingIndex)
  1849. {
  1850. _logger.LogDebug("serving up {0} as transcode index {1} is past requested point {2}", segmentPath, currentTranscodingIndex, segmentIndex);
  1851. return GetSegmentResult(state, segmentPath, segmentIndex, transcodingJob);
  1852. }
  1853. }
  1854. var nextSegmentPath = GetSegmentPath(state, playlistPath, segmentIndex + 1);
  1855. if (transcodingJob != null)
  1856. {
  1857. while (!cancellationToken.IsCancellationRequested && !transcodingJob.HasExited)
  1858. {
  1859. // To be considered ready, the segment file has to exist AND
  1860. // either the transcoding job should be done or next segment should also exist
  1861. if (segmentExists)
  1862. {
  1863. if (transcodingJob.HasExited || System.IO.File.Exists(nextSegmentPath))
  1864. {
  1865. _logger.LogDebug("serving up {0} as it deemed ready", segmentPath);
  1866. return GetSegmentResult(state, segmentPath, segmentIndex, transcodingJob);
  1867. }
  1868. }
  1869. else
  1870. {
  1871. segmentExists = System.IO.File.Exists(segmentPath);
  1872. if (segmentExists)
  1873. {
  1874. continue; // avoid unnecessary waiting if segment just became available
  1875. }
  1876. }
  1877. await Task.Delay(100, cancellationToken).ConfigureAwait(false);
  1878. }
  1879. if (!System.IO.File.Exists(segmentPath))
  1880. {
  1881. _logger.LogWarning("cannot serve {0} as transcoding quit before we got there", segmentPath);
  1882. }
  1883. else
  1884. {
  1885. _logger.LogDebug("serving {0} as it's on disk and transcoding stopped", segmentPath);
  1886. }
  1887. cancellationToken.ThrowIfCancellationRequested();
  1888. }
  1889. else
  1890. {
  1891. _logger.LogWarning("cannot serve {0} as it doesn't exist and no transcode is running", segmentPath);
  1892. }
  1893. return GetSegmentResult(state, segmentPath, segmentIndex, transcodingJob);
  1894. }
  1895. private ActionResult GetSegmentResult(StreamState state, string segmentPath, int index, TranscodingJobDto? transcodingJob)
  1896. {
  1897. var segmentEndingPositionTicks = GetEndPositionTicks(state, index);
  1898. Response.OnCompleted(() =>
  1899. {
  1900. _logger.LogDebug("finished serving {0}", segmentPath);
  1901. if (transcodingJob != null)
  1902. {
  1903. transcodingJob.DownloadPositionTicks = Math.Max(transcodingJob.DownloadPositionTicks ?? segmentEndingPositionTicks, segmentEndingPositionTicks);
  1904. _transcodingJobHelper.OnTranscodeEndRequest(transcodingJob);
  1905. }
  1906. return Task.CompletedTask;
  1907. });
  1908. return FileStreamResponseHelpers.GetStaticFileResult(segmentPath, MimeTypes.GetMimeType(segmentPath)!, false, this);
  1909. }
  1910. private long GetEndPositionTicks(StreamState state, int requestedIndex)
  1911. {
  1912. double startSeconds = 0;
  1913. var lengths = GetSegmentLengths(state);
  1914. if (requestedIndex >= lengths.Length)
  1915. {
  1916. var msg = string.Format(
  1917. CultureInfo.InvariantCulture,
  1918. "Invalid segment index requested: {0} - Segment count: {1}",
  1919. requestedIndex,
  1920. lengths.Length);
  1921. throw new ArgumentException(msg);
  1922. }
  1923. for (var i = 0; i <= requestedIndex; i++)
  1924. {
  1925. startSeconds += lengths[i];
  1926. }
  1927. return TimeSpan.FromSeconds(startSeconds).Ticks;
  1928. }
  1929. private int? GetCurrentTranscodingIndex(string playlist, string segmentExtension)
  1930. {
  1931. var job = _transcodingJobHelper.GetTranscodingJob(playlist, _transcodingJobType);
  1932. if (job == null || job.HasExited)
  1933. {
  1934. return null;
  1935. }
  1936. var file = GetLastTranscodingFile(playlist, segmentExtension, _fileSystem);
  1937. if (file == null)
  1938. {
  1939. return null;
  1940. }
  1941. var playlistFilename = Path.GetFileNameWithoutExtension(playlist);
  1942. var indexString = Path.GetFileNameWithoutExtension(file.Name).Substring(playlistFilename.Length);
  1943. return int.Parse(indexString, NumberStyles.Integer, CultureInfo.InvariantCulture);
  1944. }
  1945. private static FileSystemMetadata? GetLastTranscodingFile(string playlist, string segmentExtension, IFileSystem fileSystem)
  1946. {
  1947. var folder = Path.GetDirectoryName(playlist);
  1948. var filePrefix = Path.GetFileNameWithoutExtension(playlist) ?? string.Empty;
  1949. try
  1950. {
  1951. return fileSystem.GetFiles(folder, new[] { segmentExtension }, true, false)
  1952. .Where(i => Path.GetFileNameWithoutExtension(i.Name).StartsWith(filePrefix, StringComparison.OrdinalIgnoreCase))
  1953. .OrderByDescending(fileSystem.GetLastWriteTimeUtc)
  1954. .FirstOrDefault();
  1955. }
  1956. catch (IOException)
  1957. {
  1958. return null;
  1959. }
  1960. }
  1961. private void DeleteLastFile(string playlistPath, string segmentExtension, int retryCount)
  1962. {
  1963. var file = GetLastTranscodingFile(playlistPath, segmentExtension, _fileSystem);
  1964. if (file != null)
  1965. {
  1966. DeleteFile(file.FullName, retryCount);
  1967. }
  1968. }
  1969. private void DeleteFile(string path, int retryCount)
  1970. {
  1971. if (retryCount >= 5)
  1972. {
  1973. return;
  1974. }
  1975. _logger.LogDebug("Deleting partial HLS file {path}", path);
  1976. try
  1977. {
  1978. _fileSystem.DeleteFile(path);
  1979. }
  1980. catch (IOException ex)
  1981. {
  1982. _logger.LogError(ex, "Error deleting partial stream file(s) {path}", path);
  1983. var task = Task.Delay(100);
  1984. Task.WaitAll(task);
  1985. DeleteFile(path, retryCount + 1);
  1986. }
  1987. catch (Exception ex)
  1988. {
  1989. _logger.LogError(ex, "Error deleting partial stream file(s) {path}", path);
  1990. }
  1991. }
  1992. private long GetStartPositionTicks(StreamState state, int requestedIndex)
  1993. {
  1994. double startSeconds = 0;
  1995. var lengths = GetSegmentLengths(state);
  1996. if (requestedIndex >= lengths.Length)
  1997. {
  1998. var msg = string.Format(
  1999. CultureInfo.InvariantCulture,
  2000. "Invalid segment index requested: {0} - Segment count: {1}",
  2001. requestedIndex,
  2002. lengths.Length);
  2003. throw new ArgumentException(msg);
  2004. }
  2005. for (var i = 0; i < requestedIndex; i++)
  2006. {
  2007. startSeconds += lengths[i];
  2008. }
  2009. var position = TimeSpan.FromSeconds(startSeconds).Ticks;
  2010. return position;
  2011. }
  2012. }
  2013. }