DynamicHlsController.cs 99 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797
  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.Attributes;
  12. using Jellyfin.Api.Constants;
  13. using Jellyfin.Api.Helpers;
  14. using Jellyfin.Api.Models.PlaybackDtos;
  15. using Jellyfin.Api.Models.StreamingDtos;
  16. using MediaBrowser.Common.Configuration;
  17. using MediaBrowser.Common.Extensions;
  18. using MediaBrowser.Controller.Configuration;
  19. using MediaBrowser.Controller.Devices;
  20. using MediaBrowser.Controller.Dlna;
  21. using MediaBrowser.Controller.Library;
  22. using MediaBrowser.Controller.MediaEncoding;
  23. using MediaBrowser.Controller.Net;
  24. using MediaBrowser.Model.Configuration;
  25. using MediaBrowser.Model.Dlna;
  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 ILogger<DynamicHlsController> _logger;
  56. private readonly EncodingHelper _encodingHelper;
  57. private readonly DynamicHlsHelper _dynamicHlsHelper;
  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="logger">Instance of the <see cref="ILogger{DynamicHlsController}"/> interface.</param>
  75. /// <param name="dynamicHlsHelper">Instance of <see cref="DynamicHlsHelper"/>.</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. ILogger<DynamicHlsController> logger,
  90. DynamicHlsHelper dynamicHlsHelper)
  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. _logger = logger;
  105. _dynamicHlsHelper = dynamicHlsHelper;
  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="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>
  113. /// <param name="params">The streaming parameters.</param>
  114. /// <param name="tag">The tag.</param>
  115. /// <param name="deviceProfileId">Optional. The dlna device profile id to utilize.</param>
  116. /// <param name="playSessionId">The play session id.</param>
  117. /// <param name="segmentContainer">The segment container.</param>
  118. /// <param name="segmentLength">The segment length.</param>
  119. /// <param name="minSegments">The minimum number of segments.</param>
  120. /// <param name="mediaSourceId">The media version id, if playing an alternate version.</param>
  121. /// <param name="deviceId">The device id of the client requesting. Used to stop encoding processes when needed.</param>
  122. /// <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>
  123. /// <param name="enableAutoStreamCopy">Whether or not to allow automatic stream copy if requested values match the original source. Defaults to true.</param>
  124. /// <param name="allowVideoStreamCopy">Whether or not to allow copying of the video stream url.</param>
  125. /// <param name="allowAudioStreamCopy">Whether or not to allow copying of the audio stream url.</param>
  126. /// <param name="breakOnNonKeyFrames">Optional. Whether to break on non key frames.</param>
  127. /// <param name="audioSampleRate">Optional. Specify a specific audio sample rate, e.g. 44100.</param>
  128. /// <param name="maxAudioBitDepth">Optional. The maximum audio bit depth.</param>
  129. /// <param name="audioBitRate">Optional. Specify an audio bitrate to encode to, e.g. 128000. If omitted this will be left to encoder defaults.</param>
  130. /// <param name="audioChannels">Optional. Specify a specific number of audio channels to encode to, e.g. 2.</param>
  131. /// <param name="maxAudioChannels">Optional. Specify a maximum number of audio channels to encode to, e.g. 2.</param>
  132. /// <param name="profile">Optional. Specify a specific an encoder profile (varies by encoder), e.g. main, baseline, high.</param>
  133. /// <param name="level">Optional. Specify a level for the encoder profile (varies by encoder), e.g. 3, 3.1.</param>
  134. /// <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>
  135. /// <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>
  136. /// <param name="copyTimestamps">Whether or not to copy timestamps when transcoding with an offset. Defaults to false.</param>
  137. /// <param name="startTimeTicks">Optional. Specify a starting offset, in ticks. 1 tick = 10000 ms.</param>
  138. /// <param name="width">Optional. The fixed horizontal resolution of the encoded video.</param>
  139. /// <param name="height">Optional. The fixed vertical resolution of the encoded video.</param>
  140. /// <param name="videoBitRate">Optional. Specify a video bitrate to encode to, e.g. 500000. If omitted this will be left to encoder defaults.</param>
  141. /// <param name="subtitleStreamIndex">Optional. The index of the subtitle stream to use. If omitted no subtitles will be used.</param>
  142. /// <param name="subtitleMethod">Optional. Specify the subtitle delivery method.</param>
  143. /// <param name="maxRefFrames">Optional.</param>
  144. /// <param name="maxVideoBitDepth">Optional. The maximum video bit depth.</param>
  145. /// <param name="requireAvc">Optional. Whether to require avc.</param>
  146. /// <param name="deInterlace">Optional. Whether to deinterlace the video.</param>
  147. /// <param name="requireNonAnamorphic">Optional. Whether to require a non anamorphic stream.</param>
  148. /// <param name="transcodingMaxAudioChannels">Optional. The maximum number of audio channels to transcode.</param>
  149. /// <param name="cpuCoreLimit">Optional. The limit of how many cpu cores to use.</param>
  150. /// <param name="liveStreamId">The live stream id.</param>
  151. /// <param name="enableMpegtsM2TsMode">Optional. Whether to enable the MpegtsM2Ts mode.</param>
  152. /// <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>
  153. /// <param name="subtitleCodec">Optional. Specify a subtitle codec to encode to.</param>
  154. /// <param name="transcodingReasons">Optional. The transcoding reason.</param>
  155. /// <param name="audioStreamIndex">Optional. The index of the audio stream to use. If omitted the first audio stream will be used.</param>
  156. /// <param name="videoStreamIndex">Optional. The index of the video stream to use. If omitted the first video stream will be used.</param>
  157. /// <param name="context">Optional. The <see cref="EncodingContext"/>.</param>
  158. /// <param name="streamOptions">Optional. The streaming options.</param>
  159. /// <param name="enableAdaptiveBitrateStreaming">Enable adaptive bitrate streaming.</param>
  160. /// <response code="200">Video stream returned.</response>
  161. /// <returns>A <see cref="FileResult"/> containing the playlist file.</returns>
  162. [HttpGet("Videos/{itemId}/master.m3u8")]
  163. [HttpHead("Videos/{itemId}/master.m3u8", Name = "HeadMasterHlsVideoPlaylist")]
  164. [ProducesResponseType(StatusCodes.Status200OK)]
  165. [ProducesPlaylistFile]
  166. public async Task<ActionResult> GetMasterHlsVideoPlaylist(
  167. [FromRoute, Required] Guid itemId,
  168. [FromQuery] bool? @static,
  169. [FromQuery] string? @params,
  170. [FromQuery] string? tag,
  171. [FromQuery] string? deviceProfileId,
  172. [FromQuery] string? playSessionId,
  173. [FromQuery] string? segmentContainer,
  174. [FromQuery] int? segmentLength,
  175. [FromQuery] int? minSegments,
  176. [FromQuery, Required] string mediaSourceId,
  177. [FromQuery] string? deviceId,
  178. [FromQuery] string? audioCodec,
  179. [FromQuery] bool? enableAutoStreamCopy,
  180. [FromQuery] bool? allowVideoStreamCopy,
  181. [FromQuery] bool? allowAudioStreamCopy,
  182. [FromQuery] bool? breakOnNonKeyFrames,
  183. [FromQuery] int? audioSampleRate,
  184. [FromQuery] int? maxAudioBitDepth,
  185. [FromQuery] int? audioBitRate,
  186. [FromQuery] int? audioChannels,
  187. [FromQuery] int? maxAudioChannels,
  188. [FromQuery] string? profile,
  189. [FromQuery] string? level,
  190. [FromQuery] float? framerate,
  191. [FromQuery] float? maxFramerate,
  192. [FromQuery] bool? copyTimestamps,
  193. [FromQuery] long? startTimeTicks,
  194. [FromQuery] int? width,
  195. [FromQuery] int? height,
  196. [FromQuery] int? videoBitRate,
  197. [FromQuery] int? subtitleStreamIndex,
  198. [FromQuery] SubtitleDeliveryMethod subtitleMethod,
  199. [FromQuery] int? maxRefFrames,
  200. [FromQuery] int? maxVideoBitDepth,
  201. [FromQuery] bool? requireAvc,
  202. [FromQuery] bool? deInterlace,
  203. [FromQuery] bool? requireNonAnamorphic,
  204. [FromQuery] int? transcodingMaxAudioChannels,
  205. [FromQuery] int? cpuCoreLimit,
  206. [FromQuery] string? liveStreamId,
  207. [FromQuery] bool? enableMpegtsM2TsMode,
  208. [FromQuery] string? videoCodec,
  209. [FromQuery] string? subtitleCodec,
  210. [FromQuery] string? transcodingReasons,
  211. [FromQuery] int? audioStreamIndex,
  212. [FromQuery] int? videoStreamIndex,
  213. [FromQuery] EncodingContext context,
  214. [FromQuery] Dictionary<string, string> streamOptions,
  215. [FromQuery] bool enableAdaptiveBitrateStreaming = true)
  216. {
  217. var streamingRequest = new HlsVideoRequestDto
  218. {
  219. Id = itemId,
  220. Static = @static ?? true,
  221. Params = @params,
  222. Tag = tag,
  223. DeviceProfileId = deviceProfileId,
  224. PlaySessionId = playSessionId,
  225. SegmentContainer = segmentContainer,
  226. SegmentLength = segmentLength,
  227. MinSegments = minSegments,
  228. MediaSourceId = mediaSourceId,
  229. DeviceId = deviceId,
  230. AudioCodec = audioCodec,
  231. EnableAutoStreamCopy = enableAutoStreamCopy ?? true,
  232. AllowAudioStreamCopy = allowAudioStreamCopy ?? true,
  233. AllowVideoStreamCopy = allowVideoStreamCopy ?? true,
  234. BreakOnNonKeyFrames = breakOnNonKeyFrames ?? false,
  235. AudioSampleRate = audioSampleRate,
  236. MaxAudioChannels = maxAudioChannels,
  237. AudioBitRate = audioBitRate,
  238. MaxAudioBitDepth = maxAudioBitDepth,
  239. AudioChannels = audioChannels,
  240. Profile = profile,
  241. Level = level,
  242. Framerate = framerate,
  243. MaxFramerate = maxFramerate,
  244. CopyTimestamps = copyTimestamps ?? true,
  245. StartTimeTicks = startTimeTicks,
  246. Width = width,
  247. Height = height,
  248. VideoBitRate = videoBitRate,
  249. SubtitleStreamIndex = subtitleStreamIndex,
  250. SubtitleMethod = subtitleMethod,
  251. MaxRefFrames = maxRefFrames,
  252. MaxVideoBitDepth = maxVideoBitDepth,
  253. RequireAvc = requireAvc ?? true,
  254. DeInterlace = deInterlace ?? true,
  255. RequireNonAnamorphic = requireNonAnamorphic ?? true,
  256. TranscodingMaxAudioChannels = transcodingMaxAudioChannels,
  257. CpuCoreLimit = cpuCoreLimit,
  258. LiveStreamId = liveStreamId,
  259. EnableMpegtsM2TsMode = enableMpegtsM2TsMode ?? true,
  260. VideoCodec = videoCodec,
  261. SubtitleCodec = subtitleCodec,
  262. TranscodeReasons = transcodingReasons,
  263. AudioStreamIndex = audioStreamIndex,
  264. VideoStreamIndex = videoStreamIndex,
  265. Context = context,
  266. StreamOptions = streamOptions,
  267. EnableAdaptiveBitrateStreaming = enableAdaptiveBitrateStreaming
  268. };
  269. return await _dynamicHlsHelper.GetMasterHlsPlaylist(_transcodingJobType, streamingRequest, enableAdaptiveBitrateStreaming).ConfigureAwait(false);
  270. }
  271. /// <summary>
  272. /// Gets an audio hls playlist stream.
  273. /// </summary>
  274. /// <param name="itemId">The item id.</param>
  275. /// <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>
  276. /// <param name="params">The streaming parameters.</param>
  277. /// <param name="tag">The tag.</param>
  278. /// <param name="deviceProfileId">Optional. The dlna device profile id to utilize.</param>
  279. /// <param name="playSessionId">The play session id.</param>
  280. /// <param name="segmentContainer">The segment container.</param>
  281. /// <param name="segmentLength">The segment length.</param>
  282. /// <param name="minSegments">The minimum number of segments.</param>
  283. /// <param name="mediaSourceId">The media version id, if playing an alternate version.</param>
  284. /// <param name="deviceId">The device id of the client requesting. Used to stop encoding processes when needed.</param>
  285. /// <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>
  286. /// <param name="enableAutoStreamCopy">Whether or not to allow automatic stream copy if requested values match the original source. Defaults to true.</param>
  287. /// <param name="allowVideoStreamCopy">Whether or not to allow copying of the video stream url.</param>
  288. /// <param name="allowAudioStreamCopy">Whether or not to allow copying of the audio stream url.</param>
  289. /// <param name="breakOnNonKeyFrames">Optional. Whether to break on non key frames.</param>
  290. /// <param name="audioSampleRate">Optional. Specify a specific audio sample rate, e.g. 44100.</param>
  291. /// <param name="maxAudioBitDepth">Optional. The maximum audio bit depth.</param>
  292. /// <param name="maxStreamingBitrate">Optional. The maximum streaming bitrate.</param>
  293. /// <param name="audioBitRate">Optional. Specify an audio bitrate to encode to, e.g. 128000. If omitted this will be left to encoder defaults.</param>
  294. /// <param name="audioChannels">Optional. Specify a specific number of audio channels to encode to, e.g. 2.</param>
  295. /// <param name="maxAudioChannels">Optional. Specify a maximum number of audio channels to encode to, e.g. 2.</param>
  296. /// <param name="profile">Optional. Specify a specific an encoder profile (varies by encoder), e.g. main, baseline, high.</param>
  297. /// <param name="level">Optional. Specify a level for the encoder profile (varies by encoder), e.g. 3, 3.1.</param>
  298. /// <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>
  299. /// <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>
  300. /// <param name="copyTimestamps">Whether or not to copy timestamps when transcoding with an offset. Defaults to false.</param>
  301. /// <param name="startTimeTicks">Optional. Specify a starting offset, in ticks. 1 tick = 10000 ms.</param>
  302. /// <param name="width">Optional. The fixed horizontal resolution of the encoded video.</param>
  303. /// <param name="height">Optional. The fixed vertical resolution of the encoded video.</param>
  304. /// <param name="videoBitRate">Optional. Specify a video bitrate to encode to, e.g. 500000. If omitted this will be left to encoder defaults.</param>
  305. /// <param name="subtitleStreamIndex">Optional. The index of the subtitle stream to use. If omitted no subtitles will be used.</param>
  306. /// <param name="subtitleMethod">Optional. Specify the subtitle delivery method.</param>
  307. /// <param name="maxRefFrames">Optional.</param>
  308. /// <param name="maxVideoBitDepth">Optional. The maximum video bit depth.</param>
  309. /// <param name="requireAvc">Optional. Whether to require avc.</param>
  310. /// <param name="deInterlace">Optional. Whether to deinterlace the video.</param>
  311. /// <param name="requireNonAnamorphic">Optional. Whether to require a non anamorphic stream.</param>
  312. /// <param name="transcodingMaxAudioChannels">Optional. The maximum number of audio channels to transcode.</param>
  313. /// <param name="cpuCoreLimit">Optional. The limit of how many cpu cores to use.</param>
  314. /// <param name="liveStreamId">The live stream id.</param>
  315. /// <param name="enableMpegtsM2TsMode">Optional. Whether to enable the MpegtsM2Ts mode.</param>
  316. /// <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>
  317. /// <param name="subtitleCodec">Optional. Specify a subtitle codec to encode to.</param>
  318. /// <param name="transcodingReasons">Optional. The transcoding reason.</param>
  319. /// <param name="audioStreamIndex">Optional. The index of the audio stream to use. If omitted the first audio stream will be used.</param>
  320. /// <param name="videoStreamIndex">Optional. The index of the video stream to use. If omitted the first video stream will be used.</param>
  321. /// <param name="context">Optional. The <see cref="EncodingContext"/>.</param>
  322. /// <param name="streamOptions">Optional. The streaming options.</param>
  323. /// <param name="enableAdaptiveBitrateStreaming">Enable adaptive bitrate streaming.</param>
  324. /// <response code="200">Audio stream returned.</response>
  325. /// <returns>A <see cref="FileResult"/> containing the playlist file.</returns>
  326. [HttpGet("Audio/{itemId}/master.m3u8")]
  327. [HttpHead("Audio/{itemId}/master.m3u8", Name = "HeadMasterHlsAudioPlaylist")]
  328. [ProducesResponseType(StatusCodes.Status200OK)]
  329. [ProducesPlaylistFile]
  330. public async Task<ActionResult> GetMasterHlsAudioPlaylist(
  331. [FromRoute, Required] Guid itemId,
  332. [FromQuery] bool? @static,
  333. [FromQuery] string? @params,
  334. [FromQuery] string? tag,
  335. [FromQuery] string? deviceProfileId,
  336. [FromQuery] string? playSessionId,
  337. [FromQuery] string? segmentContainer,
  338. [FromQuery] int? segmentLength,
  339. [FromQuery] int? minSegments,
  340. [FromQuery, Required] string mediaSourceId,
  341. [FromQuery] string? deviceId,
  342. [FromQuery] string? audioCodec,
  343. [FromQuery] bool? enableAutoStreamCopy,
  344. [FromQuery] bool? allowVideoStreamCopy,
  345. [FromQuery] bool? allowAudioStreamCopy,
  346. [FromQuery] bool? breakOnNonKeyFrames,
  347. [FromQuery] int? audioSampleRate,
  348. [FromQuery] int? maxAudioBitDepth,
  349. [FromQuery] int? maxStreamingBitrate,
  350. [FromQuery] int? audioBitRate,
  351. [FromQuery] int? audioChannels,
  352. [FromQuery] int? maxAudioChannels,
  353. [FromQuery] string? profile,
  354. [FromQuery] string? level,
  355. [FromQuery] float? framerate,
  356. [FromQuery] float? maxFramerate,
  357. [FromQuery] bool? copyTimestamps,
  358. [FromQuery] long? startTimeTicks,
  359. [FromQuery] int? width,
  360. [FromQuery] int? height,
  361. [FromQuery] int? videoBitRate,
  362. [FromQuery] int? subtitleStreamIndex,
  363. [FromQuery] SubtitleDeliveryMethod subtitleMethod,
  364. [FromQuery] int? maxRefFrames,
  365. [FromQuery] int? maxVideoBitDepth,
  366. [FromQuery] bool? requireAvc,
  367. [FromQuery] bool? deInterlace,
  368. [FromQuery] bool? requireNonAnamorphic,
  369. [FromQuery] int? transcodingMaxAudioChannels,
  370. [FromQuery] int? cpuCoreLimit,
  371. [FromQuery] string? liveStreamId,
  372. [FromQuery] bool? enableMpegtsM2TsMode,
  373. [FromQuery] string? videoCodec,
  374. [FromQuery] string? subtitleCodec,
  375. [FromQuery] string? transcodingReasons,
  376. [FromQuery] int? audioStreamIndex,
  377. [FromQuery] int? videoStreamIndex,
  378. [FromQuery] EncodingContext context,
  379. [FromQuery] Dictionary<string, string> streamOptions,
  380. [FromQuery] bool enableAdaptiveBitrateStreaming = true)
  381. {
  382. var streamingRequest = new HlsAudioRequestDto
  383. {
  384. Id = itemId,
  385. Static = @static ?? true,
  386. Params = @params,
  387. Tag = tag,
  388. DeviceProfileId = deviceProfileId,
  389. PlaySessionId = playSessionId,
  390. SegmentContainer = segmentContainer,
  391. SegmentLength = segmentLength,
  392. MinSegments = minSegments,
  393. MediaSourceId = mediaSourceId,
  394. DeviceId = deviceId,
  395. AudioCodec = audioCodec,
  396. EnableAutoStreamCopy = enableAutoStreamCopy ?? true,
  397. AllowAudioStreamCopy = allowAudioStreamCopy ?? true,
  398. AllowVideoStreamCopy = allowVideoStreamCopy ?? true,
  399. BreakOnNonKeyFrames = breakOnNonKeyFrames ?? false,
  400. AudioSampleRate = audioSampleRate,
  401. MaxAudioChannels = maxAudioChannels,
  402. AudioBitRate = audioBitRate ?? maxStreamingBitrate,
  403. MaxAudioBitDepth = maxAudioBitDepth,
  404. AudioChannels = audioChannels,
  405. Profile = profile,
  406. Level = level,
  407. Framerate = framerate,
  408. MaxFramerate = maxFramerate,
  409. CopyTimestamps = copyTimestamps ?? true,
  410. StartTimeTicks = startTimeTicks,
  411. Width = width,
  412. Height = height,
  413. VideoBitRate = videoBitRate,
  414. SubtitleStreamIndex = subtitleStreamIndex,
  415. SubtitleMethod = subtitleMethod,
  416. MaxRefFrames = maxRefFrames,
  417. MaxVideoBitDepth = maxVideoBitDepth,
  418. RequireAvc = requireAvc ?? true,
  419. DeInterlace = deInterlace ?? true,
  420. RequireNonAnamorphic = requireNonAnamorphic ?? true,
  421. TranscodingMaxAudioChannels = transcodingMaxAudioChannels,
  422. CpuCoreLimit = cpuCoreLimit,
  423. LiveStreamId = liveStreamId,
  424. EnableMpegtsM2TsMode = enableMpegtsM2TsMode ?? true,
  425. VideoCodec = videoCodec,
  426. SubtitleCodec = subtitleCodec,
  427. TranscodeReasons = transcodingReasons,
  428. AudioStreamIndex = audioStreamIndex,
  429. VideoStreamIndex = videoStreamIndex,
  430. Context = context,
  431. StreamOptions = streamOptions,
  432. EnableAdaptiveBitrateStreaming = enableAdaptiveBitrateStreaming
  433. };
  434. return await _dynamicHlsHelper.GetMasterHlsPlaylist(_transcodingJobType, streamingRequest, enableAdaptiveBitrateStreaming).ConfigureAwait(false);
  435. }
  436. /// <summary>
  437. /// Gets a video stream using HTTP live streaming.
  438. /// </summary>
  439. /// <param name="itemId">The item id.</param>
  440. /// <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>
  441. /// <param name="params">The streaming parameters.</param>
  442. /// <param name="tag">The tag.</param>
  443. /// <param name="deviceProfileId">Optional. The dlna device profile id to utilize.</param>
  444. /// <param name="playSessionId">The play session id.</param>
  445. /// <param name="segmentContainer">The segment container.</param>
  446. /// <param name="segmentLength">The segment length.</param>
  447. /// <param name="minSegments">The minimum number of segments.</param>
  448. /// <param name="mediaSourceId">The media version id, if playing an alternate version.</param>
  449. /// <param name="deviceId">The device id of the client requesting. Used to stop encoding processes when needed.</param>
  450. /// <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>
  451. /// <param name="enableAutoStreamCopy">Whether or not to allow automatic stream copy if requested values match the original source. Defaults to true.</param>
  452. /// <param name="allowVideoStreamCopy">Whether or not to allow copying of the video stream url.</param>
  453. /// <param name="allowAudioStreamCopy">Whether or not to allow copying of the audio stream url.</param>
  454. /// <param name="breakOnNonKeyFrames">Optional. Whether to break on non key frames.</param>
  455. /// <param name="audioSampleRate">Optional. Specify a specific audio sample rate, e.g. 44100.</param>
  456. /// <param name="maxAudioBitDepth">Optional. The maximum audio bit depth.</param>
  457. /// <param name="audioBitRate">Optional. Specify an audio bitrate to encode to, e.g. 128000. If omitted this will be left to encoder defaults.</param>
  458. /// <param name="audioChannels">Optional. Specify a specific number of audio channels to encode to, e.g. 2.</param>
  459. /// <param name="maxAudioChannels">Optional. Specify a maximum number of audio channels to encode to, e.g. 2.</param>
  460. /// <param name="profile">Optional. Specify a specific an encoder profile (varies by encoder), e.g. main, baseline, high.</param>
  461. /// <param name="level">Optional. Specify a level for the encoder profile (varies by encoder), e.g. 3, 3.1.</param>
  462. /// <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>
  463. /// <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>
  464. /// <param name="copyTimestamps">Whether or not to copy timestamps when transcoding with an offset. Defaults to false.</param>
  465. /// <param name="startTimeTicks">Optional. Specify a starting offset, in ticks. 1 tick = 10000 ms.</param>
  466. /// <param name="width">Optional. The fixed horizontal resolution of the encoded video.</param>
  467. /// <param name="height">Optional. The fixed vertical resolution of the encoded video.</param>
  468. /// <param name="videoBitRate">Optional. Specify a video bitrate to encode to, e.g. 500000. If omitted this will be left to encoder defaults.</param>
  469. /// <param name="subtitleStreamIndex">Optional. The index of the subtitle stream to use. If omitted no subtitles will be used.</param>
  470. /// <param name="subtitleMethod">Optional. Specify the subtitle delivery method.</param>
  471. /// <param name="maxRefFrames">Optional.</param>
  472. /// <param name="maxVideoBitDepth">Optional. The maximum video bit depth.</param>
  473. /// <param name="requireAvc">Optional. Whether to require avc.</param>
  474. /// <param name="deInterlace">Optional. Whether to deinterlace the video.</param>
  475. /// <param name="requireNonAnamorphic">Optional. Whether to require a non anamorphic stream.</param>
  476. /// <param name="transcodingMaxAudioChannels">Optional. The maximum number of audio channels to transcode.</param>
  477. /// <param name="cpuCoreLimit">Optional. The limit of how many cpu cores to use.</param>
  478. /// <param name="liveStreamId">The live stream id.</param>
  479. /// <param name="enableMpegtsM2TsMode">Optional. Whether to enable the MpegtsM2Ts mode.</param>
  480. /// <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>
  481. /// <param name="subtitleCodec">Optional. Specify a subtitle codec to encode to.</param>
  482. /// <param name="transcodingReasons">Optional. The transcoding reason.</param>
  483. /// <param name="audioStreamIndex">Optional. The index of the audio stream to use. If omitted the first audio stream will be used.</param>
  484. /// <param name="videoStreamIndex">Optional. The index of the video stream to use. If omitted the first video stream will be used.</param>
  485. /// <param name="context">Optional. The <see cref="EncodingContext"/>.</param>
  486. /// <param name="streamOptions">Optional. The streaming options.</param>
  487. /// <response code="200">Video stream returned.</response>
  488. /// <returns>A <see cref="FileResult"/> containing the audio file.</returns>
  489. [HttpGet("Videos/{itemId}/main.m3u8")]
  490. [ProducesResponseType(StatusCodes.Status200OK)]
  491. [ProducesPlaylistFile]
  492. public async Task<ActionResult> GetVariantHlsVideoPlaylist(
  493. [FromRoute, Required] Guid itemId,
  494. [FromQuery] bool? @static,
  495. [FromQuery] string? @params,
  496. [FromQuery] string? tag,
  497. [FromQuery] string? deviceProfileId,
  498. [FromQuery] string? playSessionId,
  499. [FromQuery] string? segmentContainer,
  500. [FromQuery] int? segmentLength,
  501. [FromQuery] int? minSegments,
  502. [FromQuery] string? mediaSourceId,
  503. [FromQuery] string? deviceId,
  504. [FromQuery] string? audioCodec,
  505. [FromQuery] bool? enableAutoStreamCopy,
  506. [FromQuery] bool? allowVideoStreamCopy,
  507. [FromQuery] bool? allowAudioStreamCopy,
  508. [FromQuery] bool? breakOnNonKeyFrames,
  509. [FromQuery] int? audioSampleRate,
  510. [FromQuery] int? maxAudioBitDepth,
  511. [FromQuery] int? audioBitRate,
  512. [FromQuery] int? audioChannels,
  513. [FromQuery] int? maxAudioChannels,
  514. [FromQuery] string? profile,
  515. [FromQuery] string? level,
  516. [FromQuery] float? framerate,
  517. [FromQuery] float? maxFramerate,
  518. [FromQuery] bool? copyTimestamps,
  519. [FromQuery] long? startTimeTicks,
  520. [FromQuery] int? width,
  521. [FromQuery] int? height,
  522. [FromQuery] int? videoBitRate,
  523. [FromQuery] int? subtitleStreamIndex,
  524. [FromQuery] SubtitleDeliveryMethod subtitleMethod,
  525. [FromQuery] int? maxRefFrames,
  526. [FromQuery] int? maxVideoBitDepth,
  527. [FromQuery] bool? requireAvc,
  528. [FromQuery] bool? deInterlace,
  529. [FromQuery] bool? requireNonAnamorphic,
  530. [FromQuery] int? transcodingMaxAudioChannels,
  531. [FromQuery] int? cpuCoreLimit,
  532. [FromQuery] string? liveStreamId,
  533. [FromQuery] bool? enableMpegtsM2TsMode,
  534. [FromQuery] string? videoCodec,
  535. [FromQuery] string? subtitleCodec,
  536. [FromQuery] string? transcodingReasons,
  537. [FromQuery] int? audioStreamIndex,
  538. [FromQuery] int? videoStreamIndex,
  539. [FromQuery] EncodingContext context,
  540. [FromQuery] Dictionary<string, string> streamOptions)
  541. {
  542. var cancellationTokenSource = new CancellationTokenSource();
  543. var streamingRequest = new VideoRequestDto
  544. {
  545. Id = itemId,
  546. Static = @static ?? true,
  547. Params = @params,
  548. Tag = tag,
  549. DeviceProfileId = deviceProfileId,
  550. PlaySessionId = playSessionId,
  551. SegmentContainer = segmentContainer,
  552. SegmentLength = segmentLength,
  553. MinSegments = minSegments,
  554. MediaSourceId = mediaSourceId,
  555. DeviceId = deviceId,
  556. AudioCodec = audioCodec,
  557. EnableAutoStreamCopy = enableAutoStreamCopy ?? true,
  558. AllowAudioStreamCopy = allowAudioStreamCopy ?? true,
  559. AllowVideoStreamCopy = allowVideoStreamCopy ?? true,
  560. BreakOnNonKeyFrames = breakOnNonKeyFrames ?? false,
  561. AudioSampleRate = audioSampleRate,
  562. MaxAudioChannels = maxAudioChannels,
  563. AudioBitRate = audioBitRate,
  564. MaxAudioBitDepth = maxAudioBitDepth,
  565. AudioChannels = audioChannels,
  566. Profile = profile,
  567. Level = level,
  568. Framerate = framerate,
  569. MaxFramerate = maxFramerate,
  570. CopyTimestamps = copyTimestamps ?? true,
  571. StartTimeTicks = startTimeTicks,
  572. Width = width,
  573. Height = height,
  574. VideoBitRate = videoBitRate,
  575. SubtitleStreamIndex = subtitleStreamIndex,
  576. SubtitleMethod = subtitleMethod,
  577. MaxRefFrames = maxRefFrames,
  578. MaxVideoBitDepth = maxVideoBitDepth,
  579. RequireAvc = requireAvc ?? true,
  580. DeInterlace = deInterlace ?? true,
  581. RequireNonAnamorphic = requireNonAnamorphic ?? true,
  582. TranscodingMaxAudioChannels = transcodingMaxAudioChannels,
  583. CpuCoreLimit = cpuCoreLimit,
  584. LiveStreamId = liveStreamId,
  585. EnableMpegtsM2TsMode = enableMpegtsM2TsMode ?? true,
  586. VideoCodec = videoCodec,
  587. SubtitleCodec = subtitleCodec,
  588. TranscodeReasons = transcodingReasons,
  589. AudioStreamIndex = audioStreamIndex,
  590. VideoStreamIndex = videoStreamIndex,
  591. Context = context,
  592. StreamOptions = streamOptions
  593. };
  594. return await GetVariantPlaylistInternal(streamingRequest, "main", cancellationTokenSource)
  595. .ConfigureAwait(false);
  596. }
  597. /// <summary>
  598. /// Gets an audio stream using HTTP live streaming.
  599. /// </summary>
  600. /// <param name="itemId">The item id.</param>
  601. /// <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>
  602. /// <param name="params">The streaming parameters.</param>
  603. /// <param name="tag">The tag.</param>
  604. /// <param name="deviceProfileId">Optional. The dlna device profile id to utilize.</param>
  605. /// <param name="playSessionId">The play session id.</param>
  606. /// <param name="segmentContainer">The segment container.</param>
  607. /// <param name="segmentLength">The segment length.</param>
  608. /// <param name="minSegments">The minimum number of segments.</param>
  609. /// <param name="mediaSourceId">The media version id, if playing an alternate version.</param>
  610. /// <param name="deviceId">The device id of the client requesting. Used to stop encoding processes when needed.</param>
  611. /// <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>
  612. /// <param name="enableAutoStreamCopy">Whether or not to allow automatic stream copy if requested values match the original source. Defaults to true.</param>
  613. /// <param name="allowVideoStreamCopy">Whether or not to allow copying of the video stream url.</param>
  614. /// <param name="allowAudioStreamCopy">Whether or not to allow copying of the audio stream url.</param>
  615. /// <param name="breakOnNonKeyFrames">Optional. Whether to break on non key frames.</param>
  616. /// <param name="audioSampleRate">Optional. Specify a specific audio sample rate, e.g. 44100.</param>
  617. /// <param name="maxAudioBitDepth">Optional. The maximum audio bit depth.</param>
  618. /// <param name="maxStreamingBitrate">Optional. The maximum streaming bitrate.</param>
  619. /// <param name="audioBitRate">Optional. Specify an audio bitrate to encode to, e.g. 128000. If omitted this will be left to encoder defaults.</param>
  620. /// <param name="audioChannels">Optional. Specify a specific number of audio channels to encode to, e.g. 2.</param>
  621. /// <param name="maxAudioChannels">Optional. Specify a maximum number of audio channels to encode to, e.g. 2.</param>
  622. /// <param name="profile">Optional. Specify a specific an encoder profile (varies by encoder), e.g. main, baseline, high.</param>
  623. /// <param name="level">Optional. Specify a level for the encoder profile (varies by encoder), e.g. 3, 3.1.</param>
  624. /// <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>
  625. /// <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>
  626. /// <param name="copyTimestamps">Whether or not to copy timestamps when transcoding with an offset. Defaults to false.</param>
  627. /// <param name="startTimeTicks">Optional. Specify a starting offset, in ticks. 1 tick = 10000 ms.</param>
  628. /// <param name="width">Optional. The fixed horizontal resolution of the encoded video.</param>
  629. /// <param name="height">Optional. The fixed vertical resolution of the encoded video.</param>
  630. /// <param name="videoBitRate">Optional. Specify a video bitrate to encode to, e.g. 500000. If omitted this will be left to encoder defaults.</param>
  631. /// <param name="subtitleStreamIndex">Optional. The index of the subtitle stream to use. If omitted no subtitles will be used.</param>
  632. /// <param name="subtitleMethod">Optional. Specify the subtitle delivery method.</param>
  633. /// <param name="maxRefFrames">Optional.</param>
  634. /// <param name="maxVideoBitDepth">Optional. The maximum video bit depth.</param>
  635. /// <param name="requireAvc">Optional. Whether to require avc.</param>
  636. /// <param name="deInterlace">Optional. Whether to deinterlace the video.</param>
  637. /// <param name="requireNonAnamorphic">Optional. Whether to require a non anamorphic stream.</param>
  638. /// <param name="transcodingMaxAudioChannels">Optional. The maximum number of audio channels to transcode.</param>
  639. /// <param name="cpuCoreLimit">Optional. The limit of how many cpu cores to use.</param>
  640. /// <param name="liveStreamId">The live stream id.</param>
  641. /// <param name="enableMpegtsM2TsMode">Optional. Whether to enable the MpegtsM2Ts mode.</param>
  642. /// <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>
  643. /// <param name="subtitleCodec">Optional. Specify a subtitle codec to encode to.</param>
  644. /// <param name="transcodingReasons">Optional. The transcoding reason.</param>
  645. /// <param name="audioStreamIndex">Optional. The index of the audio stream to use. If omitted the first audio stream will be used.</param>
  646. /// <param name="videoStreamIndex">Optional. The index of the video stream to use. If omitted the first video stream will be used.</param>
  647. /// <param name="context">Optional. The <see cref="EncodingContext"/>.</param>
  648. /// <param name="streamOptions">Optional. The streaming options.</param>
  649. /// <response code="200">Audio stream returned.</response>
  650. /// <returns>A <see cref="FileResult"/> containing the audio file.</returns>
  651. [HttpGet("Audio/{itemId}/main.m3u8")]
  652. [ProducesResponseType(StatusCodes.Status200OK)]
  653. [ProducesPlaylistFile]
  654. public async Task<ActionResult> GetVariantHlsAudioPlaylist(
  655. [FromRoute, Required] Guid itemId,
  656. [FromQuery] bool? @static,
  657. [FromQuery] string? @params,
  658. [FromQuery] string? tag,
  659. [FromQuery] string? deviceProfileId,
  660. [FromQuery] string? playSessionId,
  661. [FromQuery] string? segmentContainer,
  662. [FromQuery] int? segmentLength,
  663. [FromQuery] int? minSegments,
  664. [FromQuery] string? mediaSourceId,
  665. [FromQuery] string? deviceId,
  666. [FromQuery] string? audioCodec,
  667. [FromQuery] bool? enableAutoStreamCopy,
  668. [FromQuery] bool? allowVideoStreamCopy,
  669. [FromQuery] bool? allowAudioStreamCopy,
  670. [FromQuery] bool? breakOnNonKeyFrames,
  671. [FromQuery] int? audioSampleRate,
  672. [FromQuery] int? maxAudioBitDepth,
  673. [FromQuery] int? maxStreamingBitrate,
  674. [FromQuery] int? audioBitRate,
  675. [FromQuery] int? audioChannels,
  676. [FromQuery] int? maxAudioChannels,
  677. [FromQuery] string? profile,
  678. [FromQuery] string? level,
  679. [FromQuery] float? framerate,
  680. [FromQuery] float? maxFramerate,
  681. [FromQuery] bool? copyTimestamps,
  682. [FromQuery] long? startTimeTicks,
  683. [FromQuery] int? width,
  684. [FromQuery] int? height,
  685. [FromQuery] int? videoBitRate,
  686. [FromQuery] int? subtitleStreamIndex,
  687. [FromQuery] SubtitleDeliveryMethod subtitleMethod,
  688. [FromQuery] int? maxRefFrames,
  689. [FromQuery] int? maxVideoBitDepth,
  690. [FromQuery] bool? requireAvc,
  691. [FromQuery] bool? deInterlace,
  692. [FromQuery] bool? requireNonAnamorphic,
  693. [FromQuery] int? transcodingMaxAudioChannels,
  694. [FromQuery] int? cpuCoreLimit,
  695. [FromQuery] string? liveStreamId,
  696. [FromQuery] bool? enableMpegtsM2TsMode,
  697. [FromQuery] string? videoCodec,
  698. [FromQuery] string? subtitleCodec,
  699. [FromQuery] string? transcodingReasons,
  700. [FromQuery] int? audioStreamIndex,
  701. [FromQuery] int? videoStreamIndex,
  702. [FromQuery] EncodingContext context,
  703. [FromQuery] Dictionary<string, string> streamOptions)
  704. {
  705. var cancellationTokenSource = new CancellationTokenSource();
  706. var streamingRequest = new StreamingRequestDto
  707. {
  708. Id = itemId,
  709. Static = @static ?? true,
  710. Params = @params,
  711. Tag = tag,
  712. DeviceProfileId = deviceProfileId,
  713. PlaySessionId = playSessionId,
  714. SegmentContainer = segmentContainer,
  715. SegmentLength = segmentLength,
  716. MinSegments = minSegments,
  717. MediaSourceId = mediaSourceId,
  718. DeviceId = deviceId,
  719. AudioCodec = audioCodec,
  720. EnableAutoStreamCopy = enableAutoStreamCopy ?? true,
  721. AllowAudioStreamCopy = allowAudioStreamCopy ?? true,
  722. AllowVideoStreamCopy = allowVideoStreamCopy ?? true,
  723. BreakOnNonKeyFrames = breakOnNonKeyFrames ?? false,
  724. AudioSampleRate = audioSampleRate,
  725. MaxAudioChannels = maxAudioChannels,
  726. AudioBitRate = audioBitRate ?? maxStreamingBitrate,
  727. MaxAudioBitDepth = maxAudioBitDepth,
  728. AudioChannels = audioChannels,
  729. Profile = profile,
  730. Level = level,
  731. Framerate = framerate,
  732. MaxFramerate = maxFramerate,
  733. CopyTimestamps = copyTimestamps ?? true,
  734. StartTimeTicks = startTimeTicks,
  735. Width = width,
  736. Height = height,
  737. VideoBitRate = videoBitRate,
  738. SubtitleStreamIndex = subtitleStreamIndex,
  739. SubtitleMethod = subtitleMethod,
  740. MaxRefFrames = maxRefFrames,
  741. MaxVideoBitDepth = maxVideoBitDepth,
  742. RequireAvc = requireAvc ?? true,
  743. DeInterlace = deInterlace ?? true,
  744. RequireNonAnamorphic = requireNonAnamorphic ?? true,
  745. TranscodingMaxAudioChannels = transcodingMaxAudioChannels,
  746. CpuCoreLimit = cpuCoreLimit,
  747. LiveStreamId = liveStreamId,
  748. EnableMpegtsM2TsMode = enableMpegtsM2TsMode ?? true,
  749. VideoCodec = videoCodec,
  750. SubtitleCodec = subtitleCodec,
  751. TranscodeReasons = transcodingReasons,
  752. AudioStreamIndex = audioStreamIndex,
  753. VideoStreamIndex = videoStreamIndex,
  754. Context = context,
  755. StreamOptions = streamOptions
  756. };
  757. return await GetVariantPlaylistInternal(streamingRequest, "main", cancellationTokenSource)
  758. .ConfigureAwait(false);
  759. }
  760. /// <summary>
  761. /// Gets a video stream using HTTP live streaming.
  762. /// </summary>
  763. /// <param name="itemId">The item id.</param>
  764. /// <param name="playlistId">The playlist id.</param>
  765. /// <param name="segmentId">The segment id.</param>
  766. /// <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>
  767. /// <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>
  768. /// <param name="params">The streaming parameters.</param>
  769. /// <param name="tag">The tag.</param>
  770. /// <param name="deviceProfileId">Optional. The dlna device profile id to utilize.</param>
  771. /// <param name="playSessionId">The play session id.</param>
  772. /// <param name="segmentContainer">The segment container.</param>
  773. /// <param name="segmentLength">The segment lenght.</param>
  774. /// <param name="minSegments">The minimum number of segments.</param>
  775. /// <param name="mediaSourceId">The media version id, if playing an alternate version.</param>
  776. /// <param name="deviceId">The device id of the client requesting. Used to stop encoding processes when needed.</param>
  777. /// <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>
  778. /// <param name="enableAutoStreamCopy">Whether or not to allow automatic stream copy if requested values match the original source. Defaults to true.</param>
  779. /// <param name="allowVideoStreamCopy">Whether or not to allow copying of the video stream url.</param>
  780. /// <param name="allowAudioStreamCopy">Whether or not to allow copying of the audio stream url.</param>
  781. /// <param name="breakOnNonKeyFrames">Optional. Whether to break on non key frames.</param>
  782. /// <param name="audioSampleRate">Optional. Specify a specific audio sample rate, e.g. 44100.</param>
  783. /// <param name="maxAudioBitDepth">Optional. The maximum audio bit depth.</param>
  784. /// <param name="audioBitRate">Optional. Specify an audio bitrate to encode to, e.g. 128000. If omitted this will be left to encoder defaults.</param>
  785. /// <param name="audioChannels">Optional. Specify a specific number of audio channels to encode to, e.g. 2.</param>
  786. /// <param name="maxAudioChannels">Optional. Specify a maximum number of audio channels to encode to, e.g. 2.</param>
  787. /// <param name="profile">Optional. Specify a specific an encoder profile (varies by encoder), e.g. main, baseline, high.</param>
  788. /// <param name="level">Optional. Specify a level for the encoder profile (varies by encoder), e.g. 3, 3.1.</param>
  789. /// <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>
  790. /// <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>
  791. /// <param name="copyTimestamps">Whether or not to copy timestamps when transcoding with an offset. Defaults to false.</param>
  792. /// <param name="startTimeTicks">Optional. Specify a starting offset, in ticks. 1 tick = 10000 ms.</param>
  793. /// <param name="width">Optional. The fixed horizontal resolution of the encoded video.</param>
  794. /// <param name="height">Optional. The fixed vertical resolution of the encoded video.</param>
  795. /// <param name="videoBitRate">Optional. Specify a video bitrate to encode to, e.g. 500000. If omitted this will be left to encoder defaults.</param>
  796. /// <param name="subtitleStreamIndex">Optional. The index of the subtitle stream to use. If omitted no subtitles will be used.</param>
  797. /// <param name="subtitleMethod">Optional. Specify the subtitle delivery method.</param>
  798. /// <param name="maxRefFrames">Optional.</param>
  799. /// <param name="maxVideoBitDepth">Optional. The maximum video bit depth.</param>
  800. /// <param name="requireAvc">Optional. Whether to require avc.</param>
  801. /// <param name="deInterlace">Optional. Whether to deinterlace the video.</param>
  802. /// <param name="requireNonAnamorphic">Optional. Whether to require a non anamorphic stream.</param>
  803. /// <param name="transcodingMaxAudioChannels">Optional. The maximum number of audio channels to transcode.</param>
  804. /// <param name="cpuCoreLimit">Optional. The limit of how many cpu cores to use.</param>
  805. /// <param name="liveStreamId">The live stream id.</param>
  806. /// <param name="enableMpegtsM2TsMode">Optional. Whether to enable the MpegtsM2Ts mode.</param>
  807. /// <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>
  808. /// <param name="subtitleCodec">Optional. Specify a subtitle codec to encode to.</param>
  809. /// <param name="transcodingReasons">Optional. The transcoding reason.</param>
  810. /// <param name="audioStreamIndex">Optional. The index of the audio stream to use. If omitted the first audio stream will be used.</param>
  811. /// <param name="videoStreamIndex">Optional. The index of the video stream to use. If omitted the first video stream will be used.</param>
  812. /// <param name="context">Optional. The <see cref="EncodingContext"/>.</param>
  813. /// <param name="streamOptions">Optional. The streaming options.</param>
  814. /// <response code="200">Video stream returned.</response>
  815. /// <returns>A <see cref="FileResult"/> containing the audio file.</returns>
  816. [HttpGet("Videos/{itemId}/hls1/{playlistId}/{segmentId}.{container}")]
  817. [ProducesResponseType(StatusCodes.Status200OK)]
  818. [ProducesVideoFile]
  819. [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "playlistId", Justification = "Imported from ServiceStack")]
  820. public async Task<ActionResult> GetHlsVideoSegment(
  821. [FromRoute, Required] Guid itemId,
  822. [FromRoute, Required] string playlistId,
  823. [FromRoute, Required] int segmentId,
  824. [FromRoute] string container,
  825. [FromQuery] bool? @static,
  826. [FromQuery] string? @params,
  827. [FromQuery] string? tag,
  828. [FromQuery] string? deviceProfileId,
  829. [FromQuery] string? playSessionId,
  830. [FromQuery] string? segmentContainer,
  831. [FromQuery] int? segmentLength,
  832. [FromQuery] int? minSegments,
  833. [FromQuery] string? mediaSourceId,
  834. [FromQuery] string? deviceId,
  835. [FromQuery] string? audioCodec,
  836. [FromQuery] bool? enableAutoStreamCopy,
  837. [FromQuery] bool? allowVideoStreamCopy,
  838. [FromQuery] bool? allowAudioStreamCopy,
  839. [FromQuery] bool? breakOnNonKeyFrames,
  840. [FromQuery] int? audioSampleRate,
  841. [FromQuery] int? maxAudioBitDepth,
  842. [FromQuery] int? audioBitRate,
  843. [FromQuery] int? audioChannels,
  844. [FromQuery] int? maxAudioChannels,
  845. [FromQuery] string? profile,
  846. [FromQuery] string? level,
  847. [FromQuery] float? framerate,
  848. [FromQuery] float? maxFramerate,
  849. [FromQuery] bool? copyTimestamps,
  850. [FromQuery] long? startTimeTicks,
  851. [FromQuery] int? width,
  852. [FromQuery] int? height,
  853. [FromQuery] int? videoBitRate,
  854. [FromQuery] int? subtitleStreamIndex,
  855. [FromQuery] SubtitleDeliveryMethod subtitleMethod,
  856. [FromQuery] int? maxRefFrames,
  857. [FromQuery] int? maxVideoBitDepth,
  858. [FromQuery] bool? requireAvc,
  859. [FromQuery] bool? deInterlace,
  860. [FromQuery] bool? requireNonAnamorphic,
  861. [FromQuery] int? transcodingMaxAudioChannels,
  862. [FromQuery] int? cpuCoreLimit,
  863. [FromQuery] string? liveStreamId,
  864. [FromQuery] bool? enableMpegtsM2TsMode,
  865. [FromQuery] string? videoCodec,
  866. [FromQuery] string? subtitleCodec,
  867. [FromQuery] string? transcodingReasons,
  868. [FromQuery] int? audioStreamIndex,
  869. [FromQuery] int? videoStreamIndex,
  870. [FromQuery] EncodingContext context,
  871. [FromQuery] Dictionary<string, string> streamOptions)
  872. {
  873. var streamingRequest = new VideoRequestDto
  874. {
  875. Id = itemId,
  876. Container = container,
  877. Static = @static ?? true,
  878. Params = @params,
  879. Tag = tag,
  880. DeviceProfileId = deviceProfileId,
  881. PlaySessionId = playSessionId,
  882. SegmentContainer = segmentContainer,
  883. SegmentLength = segmentLength,
  884. MinSegments = minSegments,
  885. MediaSourceId = mediaSourceId,
  886. DeviceId = deviceId,
  887. AudioCodec = audioCodec,
  888. EnableAutoStreamCopy = enableAutoStreamCopy ?? true,
  889. AllowAudioStreamCopy = allowAudioStreamCopy ?? true,
  890. AllowVideoStreamCopy = allowVideoStreamCopy ?? true,
  891. BreakOnNonKeyFrames = breakOnNonKeyFrames ?? false,
  892. AudioSampleRate = audioSampleRate,
  893. MaxAudioChannels = maxAudioChannels,
  894. AudioBitRate = audioBitRate,
  895. MaxAudioBitDepth = maxAudioBitDepth,
  896. AudioChannels = audioChannels,
  897. Profile = profile,
  898. Level = level,
  899. Framerate = framerate,
  900. MaxFramerate = maxFramerate,
  901. CopyTimestamps = copyTimestamps ?? true,
  902. StartTimeTicks = startTimeTicks,
  903. Width = width,
  904. Height = height,
  905. VideoBitRate = videoBitRate,
  906. SubtitleStreamIndex = subtitleStreamIndex,
  907. SubtitleMethod = subtitleMethod,
  908. MaxRefFrames = maxRefFrames,
  909. MaxVideoBitDepth = maxVideoBitDepth,
  910. RequireAvc = requireAvc ?? true,
  911. DeInterlace = deInterlace ?? true,
  912. RequireNonAnamorphic = requireNonAnamorphic ?? true,
  913. TranscodingMaxAudioChannels = transcodingMaxAudioChannels,
  914. CpuCoreLimit = cpuCoreLimit,
  915. LiveStreamId = liveStreamId,
  916. EnableMpegtsM2TsMode = enableMpegtsM2TsMode ?? true,
  917. VideoCodec = videoCodec,
  918. SubtitleCodec = subtitleCodec,
  919. TranscodeReasons = transcodingReasons,
  920. AudioStreamIndex = audioStreamIndex,
  921. VideoStreamIndex = videoStreamIndex,
  922. Context = context,
  923. StreamOptions = streamOptions
  924. };
  925. return await GetDynamicSegment(streamingRequest, segmentId)
  926. .ConfigureAwait(false);
  927. }
  928. /// <summary>
  929. /// Gets a video stream using HTTP live streaming.
  930. /// </summary>
  931. /// <param name="itemId">The item id.</param>
  932. /// <param name="playlistId">The playlist id.</param>
  933. /// <param name="segmentId">The segment id.</param>
  934. /// <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>
  935. /// <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>
  936. /// <param name="params">The streaming parameters.</param>
  937. /// <param name="tag">The tag.</param>
  938. /// <param name="deviceProfileId">Optional. The dlna device profile id to utilize.</param>
  939. /// <param name="playSessionId">The play session id.</param>
  940. /// <param name="segmentContainer">The segment container.</param>
  941. /// <param name="segmentLength">The segment length.</param>
  942. /// <param name="minSegments">The minimum number of segments.</param>
  943. /// <param name="mediaSourceId">The media version id, if playing an alternate version.</param>
  944. /// <param name="deviceId">The device id of the client requesting. Used to stop encoding processes when needed.</param>
  945. /// <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>
  946. /// <param name="enableAutoStreamCopy">Whether or not to allow automatic stream copy if requested values match the original source. Defaults to true.</param>
  947. /// <param name="allowVideoStreamCopy">Whether or not to allow copying of the video stream url.</param>
  948. /// <param name="allowAudioStreamCopy">Whether or not to allow copying of the audio stream url.</param>
  949. /// <param name="breakOnNonKeyFrames">Optional. Whether to break on non key frames.</param>
  950. /// <param name="audioSampleRate">Optional. Specify a specific audio sample rate, e.g. 44100.</param>
  951. /// <param name="maxAudioBitDepth">Optional. The maximum audio bit depth.</param>
  952. /// <param name="maxStreamingBitrate">Optional. The maximum streaming bitrate.</param>
  953. /// <param name="audioBitRate">Optional. Specify an audio bitrate to encode to, e.g. 128000. If omitted this will be left to encoder defaults.</param>
  954. /// <param name="audioChannels">Optional. Specify a specific number of audio channels to encode to, e.g. 2.</param>
  955. /// <param name="maxAudioChannels">Optional. Specify a maximum number of audio channels to encode to, e.g. 2.</param>
  956. /// <param name="profile">Optional. Specify a specific an encoder profile (varies by encoder), e.g. main, baseline, high.</param>
  957. /// <param name="level">Optional. Specify a level for the encoder profile (varies by encoder), e.g. 3, 3.1.</param>
  958. /// <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>
  959. /// <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>
  960. /// <param name="copyTimestamps">Whether or not to copy timestamps when transcoding with an offset. Defaults to false.</param>
  961. /// <param name="startTimeTicks">Optional. Specify a starting offset, in ticks. 1 tick = 10000 ms.</param>
  962. /// <param name="width">Optional. The fixed horizontal resolution of the encoded video.</param>
  963. /// <param name="height">Optional. The fixed vertical resolution of the encoded video.</param>
  964. /// <param name="videoBitRate">Optional. Specify a video bitrate to encode to, e.g. 500000. If omitted this will be left to encoder defaults.</param>
  965. /// <param name="subtitleStreamIndex">Optional. The index of the subtitle stream to use. If omitted no subtitles will be used.</param>
  966. /// <param name="subtitleMethod">Optional. Specify the subtitle delivery method.</param>
  967. /// <param name="maxRefFrames">Optional.</param>
  968. /// <param name="maxVideoBitDepth">Optional. The maximum video bit depth.</param>
  969. /// <param name="requireAvc">Optional. Whether to require avc.</param>
  970. /// <param name="deInterlace">Optional. Whether to deinterlace the video.</param>
  971. /// <param name="requireNonAnamorphic">Optional. Whether to require a non anamorphic stream.</param>
  972. /// <param name="transcodingMaxAudioChannels">Optional. The maximum number of audio channels to transcode.</param>
  973. /// <param name="cpuCoreLimit">Optional. The limit of how many cpu cores to use.</param>
  974. /// <param name="liveStreamId">The live stream id.</param>
  975. /// <param name="enableMpegtsM2TsMode">Optional. Whether to enable the MpegtsM2Ts mode.</param>
  976. /// <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>
  977. /// <param name="subtitleCodec">Optional. Specify a subtitle codec to encode to.</param>
  978. /// <param name="transcodingReasons">Optional. The transcoding reason.</param>
  979. /// <param name="audioStreamIndex">Optional. The index of the audio stream to use. If omitted the first audio stream will be used.</param>
  980. /// <param name="videoStreamIndex">Optional. The index of the video stream to use. If omitted the first video stream will be used.</param>
  981. /// <param name="context">Optional. The <see cref="EncodingContext"/>.</param>
  982. /// <param name="streamOptions">Optional. The streaming options.</param>
  983. /// <response code="200">Video stream returned.</response>
  984. /// <returns>A <see cref="FileResult"/> containing the audio file.</returns>
  985. [HttpGet("Audio/{itemId}/hls1/{playlistId}/{segmentId}.{container}")]
  986. [ProducesResponseType(StatusCodes.Status200OK)]
  987. [ProducesAudioFile]
  988. [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "playlistId", Justification = "Imported from ServiceStack")]
  989. public async Task<ActionResult> GetHlsAudioSegment(
  990. [FromRoute, Required] Guid itemId,
  991. [FromRoute, Required] string playlistId,
  992. [FromRoute, Required] int segmentId,
  993. [FromRoute] string container,
  994. [FromQuery] bool? @static,
  995. [FromQuery] string? @params,
  996. [FromQuery] string? tag,
  997. [FromQuery] string? deviceProfileId,
  998. [FromQuery] string? playSessionId,
  999. [FromQuery] string? segmentContainer,
  1000. [FromQuery] int? segmentLength,
  1001. [FromQuery] int? minSegments,
  1002. [FromQuery] string? mediaSourceId,
  1003. [FromQuery] string? deviceId,
  1004. [FromQuery] string? audioCodec,
  1005. [FromQuery] bool? enableAutoStreamCopy,
  1006. [FromQuery] bool? allowVideoStreamCopy,
  1007. [FromQuery] bool? allowAudioStreamCopy,
  1008. [FromQuery] bool? breakOnNonKeyFrames,
  1009. [FromQuery] int? audioSampleRate,
  1010. [FromQuery] int? maxAudioBitDepth,
  1011. [FromQuery] int? maxStreamingBitrate,
  1012. [FromQuery] int? audioBitRate,
  1013. [FromQuery] int? audioChannels,
  1014. [FromQuery] int? maxAudioChannels,
  1015. [FromQuery] string? profile,
  1016. [FromQuery] string? level,
  1017. [FromQuery] float? framerate,
  1018. [FromQuery] float? maxFramerate,
  1019. [FromQuery] bool? copyTimestamps,
  1020. [FromQuery] long? startTimeTicks,
  1021. [FromQuery] int? width,
  1022. [FromQuery] int? height,
  1023. [FromQuery] int? videoBitRate,
  1024. [FromQuery] int? subtitleStreamIndex,
  1025. [FromQuery] SubtitleDeliveryMethod subtitleMethod,
  1026. [FromQuery] int? maxRefFrames,
  1027. [FromQuery] int? maxVideoBitDepth,
  1028. [FromQuery] bool? requireAvc,
  1029. [FromQuery] bool? deInterlace,
  1030. [FromQuery] bool? requireNonAnamorphic,
  1031. [FromQuery] int? transcodingMaxAudioChannels,
  1032. [FromQuery] int? cpuCoreLimit,
  1033. [FromQuery] string? liveStreamId,
  1034. [FromQuery] bool? enableMpegtsM2TsMode,
  1035. [FromQuery] string? videoCodec,
  1036. [FromQuery] string? subtitleCodec,
  1037. [FromQuery] string? transcodingReasons,
  1038. [FromQuery] int? audioStreamIndex,
  1039. [FromQuery] int? videoStreamIndex,
  1040. [FromQuery] EncodingContext context,
  1041. [FromQuery] Dictionary<string, string> streamOptions)
  1042. {
  1043. var streamingRequest = new StreamingRequestDto
  1044. {
  1045. Id = itemId,
  1046. Container = container,
  1047. Static = @static ?? true,
  1048. Params = @params,
  1049. Tag = tag,
  1050. DeviceProfileId = deviceProfileId,
  1051. PlaySessionId = playSessionId,
  1052. SegmentContainer = segmentContainer,
  1053. SegmentLength = segmentLength,
  1054. MinSegments = minSegments,
  1055. MediaSourceId = mediaSourceId,
  1056. DeviceId = deviceId,
  1057. AudioCodec = audioCodec,
  1058. EnableAutoStreamCopy = enableAutoStreamCopy ?? true,
  1059. AllowAudioStreamCopy = allowAudioStreamCopy ?? true,
  1060. AllowVideoStreamCopy = allowVideoStreamCopy ?? true,
  1061. BreakOnNonKeyFrames = breakOnNonKeyFrames ?? false,
  1062. AudioSampleRate = audioSampleRate,
  1063. MaxAudioChannels = maxAudioChannels,
  1064. AudioBitRate = audioBitRate ?? maxStreamingBitrate,
  1065. MaxAudioBitDepth = maxAudioBitDepth,
  1066. AudioChannels = audioChannels,
  1067. Profile = profile,
  1068. Level = level,
  1069. Framerate = framerate,
  1070. MaxFramerate = maxFramerate,
  1071. CopyTimestamps = copyTimestamps ?? true,
  1072. StartTimeTicks = startTimeTicks,
  1073. Width = width,
  1074. Height = height,
  1075. VideoBitRate = videoBitRate,
  1076. SubtitleStreamIndex = subtitleStreamIndex,
  1077. SubtitleMethod = subtitleMethod,
  1078. MaxRefFrames = maxRefFrames,
  1079. MaxVideoBitDepth = maxVideoBitDepth,
  1080. RequireAvc = requireAvc ?? true,
  1081. DeInterlace = deInterlace ?? true,
  1082. RequireNonAnamorphic = requireNonAnamorphic ?? true,
  1083. TranscodingMaxAudioChannels = transcodingMaxAudioChannels,
  1084. CpuCoreLimit = cpuCoreLimit,
  1085. LiveStreamId = liveStreamId,
  1086. EnableMpegtsM2TsMode = enableMpegtsM2TsMode ?? true,
  1087. VideoCodec = videoCodec,
  1088. SubtitleCodec = subtitleCodec,
  1089. TranscodeReasons = transcodingReasons,
  1090. AudioStreamIndex = audioStreamIndex,
  1091. VideoStreamIndex = videoStreamIndex,
  1092. Context = context,
  1093. StreamOptions = streamOptions
  1094. };
  1095. return await GetDynamicSegment(streamingRequest, segmentId)
  1096. .ConfigureAwait(false);
  1097. }
  1098. private async Task<ActionResult> GetVariantPlaylistInternal(StreamingRequestDto streamingRequest, string name, CancellationTokenSource cancellationTokenSource)
  1099. {
  1100. using var state = await StreamingHelpers.GetStreamingState(
  1101. streamingRequest,
  1102. Request,
  1103. _authContext,
  1104. _mediaSourceManager,
  1105. _userManager,
  1106. _libraryManager,
  1107. _serverConfigurationManager,
  1108. _mediaEncoder,
  1109. _fileSystem,
  1110. _subtitleEncoder,
  1111. _configuration,
  1112. _dlnaManager,
  1113. _deviceManager,
  1114. _transcodingJobHelper,
  1115. _transcodingJobType,
  1116. cancellationTokenSource.Token)
  1117. .ConfigureAwait(false);
  1118. Response.Headers.Add(HeaderNames.Expires, "0");
  1119. var segmentLengths = GetSegmentLengths(state);
  1120. var builder = new StringBuilder();
  1121. builder.AppendLine("#EXTM3U")
  1122. .AppendLine("#EXT-X-PLAYLIST-TYPE:VOD")
  1123. .AppendLine("#EXT-X-VERSION:3")
  1124. .Append("#EXT-X-TARGETDURATION:")
  1125. .Append(Math.Ceiling(segmentLengths.Length > 0 ? segmentLengths.Max() : state.SegmentLength))
  1126. .AppendLine()
  1127. .AppendLine("#EXT-X-MEDIA-SEQUENCE:0");
  1128. var index = 0;
  1129. var segmentExtension = GetSegmentFileExtension(streamingRequest.SegmentContainer);
  1130. var queryString = Request.QueryString;
  1131. foreach (var length in segmentLengths)
  1132. {
  1133. builder.Append("#EXTINF:")
  1134. .Append(length.ToString("0.0000", CultureInfo.InvariantCulture))
  1135. .AppendLine(", nodesc")
  1136. .Append("hls1/")
  1137. .Append(name)
  1138. .Append('/')
  1139. .Append(index++)
  1140. .Append(segmentExtension)
  1141. .Append(queryString)
  1142. .AppendLine();
  1143. }
  1144. builder.AppendLine("#EXT-X-ENDLIST");
  1145. return new FileContentResult(Encoding.UTF8.GetBytes(builder.ToString()), MimeTypes.GetMimeType("playlist.m3u8"));
  1146. }
  1147. private async Task<ActionResult> GetDynamicSegment(StreamingRequestDto streamingRequest, int segmentId)
  1148. {
  1149. if ((streamingRequest.StartTimeTicks ?? 0) > 0)
  1150. {
  1151. throw new ArgumentException("StartTimeTicks is not allowed.");
  1152. }
  1153. var cancellationTokenSource = new CancellationTokenSource();
  1154. var cancellationToken = cancellationTokenSource.Token;
  1155. using var state = await StreamingHelpers.GetStreamingState(
  1156. streamingRequest,
  1157. Request,
  1158. _authContext,
  1159. _mediaSourceManager,
  1160. _userManager,
  1161. _libraryManager,
  1162. _serverConfigurationManager,
  1163. _mediaEncoder,
  1164. _fileSystem,
  1165. _subtitleEncoder,
  1166. _configuration,
  1167. _dlnaManager,
  1168. _deviceManager,
  1169. _transcodingJobHelper,
  1170. _transcodingJobType,
  1171. cancellationTokenSource.Token)
  1172. .ConfigureAwait(false);
  1173. var playlistPath = Path.ChangeExtension(state.OutputFilePath, ".m3u8");
  1174. var segmentPath = GetSegmentPath(state, playlistPath, segmentId);
  1175. var segmentExtension = GetSegmentFileExtension(state.Request.SegmentContainer);
  1176. TranscodingJobDto? job;
  1177. if (System.IO.File.Exists(segmentPath))
  1178. {
  1179. job = _transcodingJobHelper.OnTranscodeBeginRequest(playlistPath, _transcodingJobType);
  1180. _logger.LogDebug("returning {0} [it exists, try 1]", segmentPath);
  1181. return await GetSegmentResult(state, playlistPath, segmentPath, segmentExtension, segmentId, job, cancellationToken).ConfigureAwait(false);
  1182. }
  1183. var transcodingLock = _transcodingJobHelper.GetTranscodingLock(playlistPath);
  1184. await transcodingLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
  1185. var released = false;
  1186. var startTranscoding = false;
  1187. try
  1188. {
  1189. if (System.IO.File.Exists(segmentPath))
  1190. {
  1191. job = _transcodingJobHelper.OnTranscodeBeginRequest(playlistPath, _transcodingJobType);
  1192. transcodingLock.Release();
  1193. released = true;
  1194. _logger.LogDebug("returning {0} [it exists, try 2]", segmentPath);
  1195. return await GetSegmentResult(state, playlistPath, segmentPath, segmentExtension, segmentId, job, cancellationToken).ConfigureAwait(false);
  1196. }
  1197. else
  1198. {
  1199. var currentTranscodingIndex = GetCurrentTranscodingIndex(playlistPath, segmentExtension);
  1200. var segmentGapRequiringTranscodingChange = 24 / state.SegmentLength;
  1201. if (currentTranscodingIndex == null)
  1202. {
  1203. _logger.LogDebug("Starting transcoding because currentTranscodingIndex=null");
  1204. startTranscoding = true;
  1205. }
  1206. else if (segmentId < currentTranscodingIndex.Value)
  1207. {
  1208. _logger.LogDebug("Starting transcoding because requestedIndex={0} and currentTranscodingIndex={1}", segmentId, currentTranscodingIndex);
  1209. startTranscoding = true;
  1210. }
  1211. else if (segmentId - currentTranscodingIndex.Value > segmentGapRequiringTranscodingChange)
  1212. {
  1213. _logger.LogDebug("Starting transcoding because segmentGap is {0} and max allowed gap is {1}. requestedIndex={2}", segmentId - currentTranscodingIndex.Value, segmentGapRequiringTranscodingChange, segmentId);
  1214. startTranscoding = true;
  1215. }
  1216. if (startTranscoding)
  1217. {
  1218. // If the playlist doesn't already exist, startup ffmpeg
  1219. try
  1220. {
  1221. await _transcodingJobHelper.KillTranscodingJobs(streamingRequest.DeviceId, streamingRequest.PlaySessionId, p => false)
  1222. .ConfigureAwait(false);
  1223. if (currentTranscodingIndex.HasValue)
  1224. {
  1225. DeleteLastFile(playlistPath, segmentExtension, 0);
  1226. }
  1227. streamingRequest.StartTimeTicks = GetStartPositionTicks(state, segmentId);
  1228. state.WaitForPath = segmentPath;
  1229. var encodingOptions = _serverConfigurationManager.GetEncodingOptions();
  1230. job = await _transcodingJobHelper.StartFfMpeg(
  1231. state,
  1232. playlistPath,
  1233. GetCommandLineArguments(playlistPath, encodingOptions, state, true, segmentId),
  1234. Request,
  1235. _transcodingJobType,
  1236. cancellationTokenSource).ConfigureAwait(false);
  1237. }
  1238. catch
  1239. {
  1240. state.Dispose();
  1241. throw;
  1242. }
  1243. // await WaitForMinimumSegmentCount(playlistPath, 1, cancellationTokenSource.Token).ConfigureAwait(false);
  1244. }
  1245. else
  1246. {
  1247. job = _transcodingJobHelper.OnTranscodeBeginRequest(playlistPath, _transcodingJobType);
  1248. if (job?.TranscodingThrottler != null)
  1249. {
  1250. await job.TranscodingThrottler.UnpauseTranscoding().ConfigureAwait(false);
  1251. }
  1252. }
  1253. }
  1254. }
  1255. finally
  1256. {
  1257. if (!released)
  1258. {
  1259. transcodingLock.Release();
  1260. }
  1261. }
  1262. _logger.LogDebug("returning {0} [general case]", segmentPath);
  1263. job ??= _transcodingJobHelper.OnTranscodeBeginRequest(playlistPath, _transcodingJobType);
  1264. return await GetSegmentResult(state, playlistPath, segmentPath, segmentExtension, segmentId, job, cancellationToken).ConfigureAwait(false);
  1265. }
  1266. private double[] GetSegmentLengths(StreamState state)
  1267. {
  1268. var result = new List<double>();
  1269. var ticks = state.RunTimeTicks ?? 0;
  1270. var segmentLengthTicks = TimeSpan.FromSeconds(state.SegmentLength).Ticks;
  1271. while (ticks > 0)
  1272. {
  1273. var length = ticks >= segmentLengthTicks ? segmentLengthTicks : ticks;
  1274. result.Add(TimeSpan.FromTicks(length).TotalSeconds);
  1275. ticks -= length;
  1276. }
  1277. return result.ToArray();
  1278. }
  1279. private string GetCommandLineArguments(string outputPath, EncodingOptions encodingOptions, StreamState state, bool isEncoding, int startNumber)
  1280. {
  1281. var videoCodec = _encodingHelper.GetVideoEncoder(state, encodingOptions);
  1282. var threads = _encodingHelper.GetNumberOfThreads(state, encodingOptions, videoCodec);
  1283. if (state.BaseRequest.BreakOnNonKeyFrames)
  1284. {
  1285. // FIXME: this is actually a workaround, as ideally it really should be the client which decides whether non-keyframe
  1286. // breakpoints are supported; but current implementation always uses "ffmpeg input seeking" which is liable
  1287. // to produce a missing part of video stream before first keyframe is encountered, which may lead to
  1288. // awkward cases like a few starting HLS segments having no video whatsoever, which breaks hls.js
  1289. _logger.LogInformation("Current HLS implementation doesn't support non-keyframe breaks but one is requested, ignoring that request");
  1290. state.BaseRequest.BreakOnNonKeyFrames = false;
  1291. }
  1292. var inputModifier = _encodingHelper.GetInputModifier(state, encodingOptions);
  1293. // If isEncoding is true we're actually starting ffmpeg
  1294. var startNumberParam = isEncoding ? startNumber.ToString(CultureInfo.InvariantCulture) : "0";
  1295. var mapArgs = state.IsOutputVideo ? _encodingHelper.GetMapArgs(state) : string.Empty;
  1296. var directory = Path.GetDirectoryName(outputPath) ?? throw new ArgumentException($"Provided path ({outputPath}) is not valid.", nameof(outputPath));
  1297. var outputTsArg = Path.Combine(directory, Path.GetFileNameWithoutExtension(outputPath)) + "%d" + GetSegmentFileExtension(state.Request.SegmentContainer);
  1298. var segmentFormat = GetSegmentFileExtension(state.Request.SegmentContainer).TrimStart('.');
  1299. if (string.Equals(segmentFormat, "ts", StringComparison.OrdinalIgnoreCase))
  1300. {
  1301. segmentFormat = "mpegts";
  1302. }
  1303. var maxMuxingQueueSize = encodingOptions.MaxMuxingQueueSize > 128
  1304. ? encodingOptions.MaxMuxingQueueSize.ToString(CultureInfo.InvariantCulture)
  1305. : "128";
  1306. return string.Format(
  1307. CultureInfo.InvariantCulture,
  1308. "{0} {1} -map_metadata -1 -map_chapters -1 -threads {2} {3} {4} {5} -copyts -avoid_negative_ts disabled -max_muxing_queue_size {6} -f hls -max_delay 5000000 -hls_time {7} -individual_header_trailer 0 -hls_segment_type {8} -start_number {9} -hls_segment_filename \"{10}\" -hls_playlist_type vod -hls_list_size 0 -y \"{11}\"",
  1309. inputModifier,
  1310. _encodingHelper.GetInputArgument(state, encodingOptions),
  1311. threads,
  1312. mapArgs,
  1313. GetVideoArguments(state, encodingOptions, startNumber),
  1314. GetAudioArguments(state, encodingOptions),
  1315. maxMuxingQueueSize,
  1316. state.SegmentLength.ToString(CultureInfo.InvariantCulture),
  1317. segmentFormat,
  1318. startNumberParam,
  1319. outputTsArg,
  1320. outputPath).Trim();
  1321. }
  1322. private string GetAudioArguments(StreamState state, EncodingOptions encodingOptions)
  1323. {
  1324. var audioCodec = _encodingHelper.GetAudioEncoder(state);
  1325. if (!state.IsOutputVideo)
  1326. {
  1327. if (EncodingHelper.IsCopyCodec(audioCodec))
  1328. {
  1329. return "-acodec copy";
  1330. }
  1331. var audioTranscodeParams = new List<string>();
  1332. audioTranscodeParams.Add("-acodec " + audioCodec);
  1333. if (state.OutputAudioBitrate.HasValue)
  1334. {
  1335. audioTranscodeParams.Add("-ab " + state.OutputAudioBitrate.Value.ToString(CultureInfo.InvariantCulture));
  1336. }
  1337. if (state.OutputAudioChannels.HasValue)
  1338. {
  1339. audioTranscodeParams.Add("-ac " + state.OutputAudioChannels.Value.ToString(CultureInfo.InvariantCulture));
  1340. }
  1341. if (state.OutputAudioSampleRate.HasValue)
  1342. {
  1343. audioTranscodeParams.Add("-ar " + state.OutputAudioSampleRate.Value.ToString(CultureInfo.InvariantCulture));
  1344. }
  1345. audioTranscodeParams.Add("-vn");
  1346. return string.Join(' ', audioTranscodeParams);
  1347. }
  1348. if (EncodingHelper.IsCopyCodec(audioCodec))
  1349. {
  1350. var videoCodec = _encodingHelper.GetVideoEncoder(state, encodingOptions);
  1351. if (EncodingHelper.IsCopyCodec(videoCodec) && state.EnableBreakOnNonKeyFrames(videoCodec))
  1352. {
  1353. return "-codec:a:0 copy -copypriorss:a:0 0";
  1354. }
  1355. return "-codec:a:0 copy";
  1356. }
  1357. var args = "-codec:a:0 " + audioCodec;
  1358. var channels = state.OutputAudioChannels;
  1359. if (channels.HasValue)
  1360. {
  1361. args += " -ac " + channels.Value;
  1362. }
  1363. var bitrate = state.OutputAudioBitrate;
  1364. if (bitrate.HasValue)
  1365. {
  1366. args += " -ab " + bitrate.Value.ToString(CultureInfo.InvariantCulture);
  1367. }
  1368. if (state.OutputAudioSampleRate.HasValue)
  1369. {
  1370. args += " -ar " + state.OutputAudioSampleRate.Value.ToString(CultureInfo.InvariantCulture);
  1371. }
  1372. args += " " + _encodingHelper.GetAudioFilterParam(state, encodingOptions, true);
  1373. return args;
  1374. }
  1375. private string GetVideoArguments(StreamState state, EncodingOptions encodingOptions, int startNumber)
  1376. {
  1377. if (!state.IsOutputVideo)
  1378. {
  1379. return string.Empty;
  1380. }
  1381. var codec = _encodingHelper.GetVideoEncoder(state, encodingOptions);
  1382. var args = "-codec:v:0 " + codec;
  1383. // if (state.EnableMpegtsM2TsMode)
  1384. // {
  1385. // args += " -mpegts_m2ts_mode 1";
  1386. // }
  1387. // See if we can save come cpu cycles by avoiding encoding
  1388. if (EncodingHelper.IsCopyCodec(codec))
  1389. {
  1390. if (state.VideoStream != null && !string.Equals(state.VideoStream.NalLengthSize, "0", StringComparison.OrdinalIgnoreCase))
  1391. {
  1392. string bitStreamArgs = _encodingHelper.GetBitStreamArgs(state.VideoStream);
  1393. if (!string.IsNullOrEmpty(bitStreamArgs))
  1394. {
  1395. args += " " + bitStreamArgs;
  1396. }
  1397. }
  1398. // args += " -flags -global_header";
  1399. }
  1400. else
  1401. {
  1402. var gopArg = string.Empty;
  1403. var keyFrameArg = string.Format(
  1404. CultureInfo.InvariantCulture,
  1405. " -force_key_frames:0 \"expr:gte(t,{0}+n_forced*{1})\"",
  1406. startNumber * state.SegmentLength,
  1407. state.SegmentLength);
  1408. var framerate = state.VideoStream?.RealFrameRate;
  1409. if (framerate.HasValue)
  1410. {
  1411. // This is to make sure keyframe interval is limited to our segment,
  1412. // as forcing keyframes is not enough.
  1413. // Example: we encoded half of desired length, then codec detected
  1414. // scene cut and inserted a keyframe; next forced keyframe would
  1415. // be created outside of segment, which breaks seeking
  1416. // -sc_threshold 0 is used to prevent the hardware encoder from post processing to break the set keyframe
  1417. gopArg = string.Format(
  1418. CultureInfo.InvariantCulture,
  1419. " -g {0} -keyint_min {0} -sc_threshold 0",
  1420. Math.Ceiling(state.SegmentLength * framerate.Value));
  1421. }
  1422. args += " " + _encodingHelper.GetVideoQualityParam(state, codec, encodingOptions, "veryfast");
  1423. // Unable to force key frames using these hw encoders, set key frames by GOP
  1424. if (string.Equals(codec, "h264_qsv", StringComparison.OrdinalIgnoreCase)
  1425. || string.Equals(codec, "h264_nvenc", StringComparison.OrdinalIgnoreCase)
  1426. || string.Equals(codec, "h264_amf", StringComparison.OrdinalIgnoreCase))
  1427. {
  1428. args += " " + gopArg;
  1429. }
  1430. else
  1431. {
  1432. args += " " + keyFrameArg + gopArg;
  1433. }
  1434. // args += " -mixed-refs 0 -refs 3 -x264opts b_pyramid=0:weightb=0:weightp=0";
  1435. var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
  1436. // This is for graphical subs
  1437. if (hasGraphicalSubs)
  1438. {
  1439. args += _encodingHelper.GetGraphicalSubtitleParam(state, encodingOptions, codec);
  1440. }
  1441. // Add resolution params, if specified
  1442. else
  1443. {
  1444. args += _encodingHelper.GetOutputSizeParam(state, encodingOptions, codec);
  1445. }
  1446. // -start_at_zero is necessary to use with -ss when seeking,
  1447. // otherwise the target position cannot be determined.
  1448. if (!(state.SubtitleStream != null && state.SubtitleStream.IsExternal && !state.SubtitleStream.IsTextSubtitleStream))
  1449. {
  1450. args += " -start_at_zero";
  1451. }
  1452. // args += " -flags -global_header";
  1453. }
  1454. if (!string.IsNullOrEmpty(state.OutputVideoSync))
  1455. {
  1456. args += " -vsync " + state.OutputVideoSync;
  1457. }
  1458. args += _encodingHelper.GetOutputFFlags(state);
  1459. return args;
  1460. }
  1461. private string GetSegmentFileExtension(string? segmentContainer)
  1462. {
  1463. if (!string.IsNullOrWhiteSpace(segmentContainer))
  1464. {
  1465. return "." + segmentContainer;
  1466. }
  1467. return ".ts";
  1468. }
  1469. private string GetSegmentPath(StreamState state, string playlist, int index)
  1470. {
  1471. var folder = Path.GetDirectoryName(playlist) ?? throw new ArgumentException($"Provided path ({playlist}) is not valid.", nameof(playlist));
  1472. var filename = Path.GetFileNameWithoutExtension(playlist);
  1473. return Path.Combine(folder, filename + index.ToString(CultureInfo.InvariantCulture) + GetSegmentFileExtension(state.Request.SegmentContainer));
  1474. }
  1475. private async Task<ActionResult> GetSegmentResult(
  1476. StreamState state,
  1477. string playlistPath,
  1478. string segmentPath,
  1479. string segmentExtension,
  1480. int segmentIndex,
  1481. TranscodingJobDto? transcodingJob,
  1482. CancellationToken cancellationToken)
  1483. {
  1484. var segmentExists = System.IO.File.Exists(segmentPath);
  1485. if (segmentExists)
  1486. {
  1487. if (transcodingJob != null && transcodingJob.HasExited)
  1488. {
  1489. // Transcoding job is over, so assume all existing files are ready
  1490. _logger.LogDebug("serving up {0} as transcode is over", segmentPath);
  1491. return GetSegmentResult(state, segmentPath, segmentIndex, transcodingJob);
  1492. }
  1493. var currentTranscodingIndex = GetCurrentTranscodingIndex(playlistPath, segmentExtension);
  1494. // If requested segment is less than transcoding position, we can't transcode backwards, so assume it's ready
  1495. if (segmentIndex < currentTranscodingIndex)
  1496. {
  1497. _logger.LogDebug("serving up {0} as transcode index {1} is past requested point {2}", segmentPath, currentTranscodingIndex, segmentIndex);
  1498. return GetSegmentResult(state, segmentPath, segmentIndex, transcodingJob);
  1499. }
  1500. }
  1501. var nextSegmentPath = GetSegmentPath(state, playlistPath, segmentIndex + 1);
  1502. if (transcodingJob != null)
  1503. {
  1504. while (!cancellationToken.IsCancellationRequested && !transcodingJob.HasExited)
  1505. {
  1506. // To be considered ready, the segment file has to exist AND
  1507. // either the transcoding job should be done or next segment should also exist
  1508. if (segmentExists)
  1509. {
  1510. if (transcodingJob.HasExited || System.IO.File.Exists(nextSegmentPath))
  1511. {
  1512. _logger.LogDebug("serving up {0} as it deemed ready", segmentPath);
  1513. return GetSegmentResult(state, segmentPath, segmentIndex, transcodingJob);
  1514. }
  1515. }
  1516. else
  1517. {
  1518. segmentExists = System.IO.File.Exists(segmentPath);
  1519. if (segmentExists)
  1520. {
  1521. continue; // avoid unnecessary waiting if segment just became available
  1522. }
  1523. }
  1524. await Task.Delay(100, cancellationToken).ConfigureAwait(false);
  1525. }
  1526. if (!System.IO.File.Exists(segmentPath))
  1527. {
  1528. _logger.LogWarning("cannot serve {0} as transcoding quit before we got there", segmentPath);
  1529. }
  1530. else
  1531. {
  1532. _logger.LogDebug("serving {0} as it's on disk and transcoding stopped", segmentPath);
  1533. }
  1534. cancellationToken.ThrowIfCancellationRequested();
  1535. }
  1536. else
  1537. {
  1538. _logger.LogWarning("cannot serve {0} as it doesn't exist and no transcode is running", segmentPath);
  1539. }
  1540. return GetSegmentResult(state, segmentPath, segmentIndex, transcodingJob);
  1541. }
  1542. private ActionResult GetSegmentResult(StreamState state, string segmentPath, int index, TranscodingJobDto? transcodingJob)
  1543. {
  1544. var segmentEndingPositionTicks = GetEndPositionTicks(state, index);
  1545. Response.OnCompleted(() =>
  1546. {
  1547. _logger.LogDebug("finished serving {0}", segmentPath);
  1548. if (transcodingJob != null)
  1549. {
  1550. transcodingJob.DownloadPositionTicks = Math.Max(transcodingJob.DownloadPositionTicks ?? segmentEndingPositionTicks, segmentEndingPositionTicks);
  1551. _transcodingJobHelper.OnTranscodeEndRequest(transcodingJob);
  1552. }
  1553. return Task.CompletedTask;
  1554. });
  1555. return FileStreamResponseHelpers.GetStaticFileResult(segmentPath, MimeTypes.GetMimeType(segmentPath)!, false, HttpContext);
  1556. }
  1557. private long GetEndPositionTicks(StreamState state, int requestedIndex)
  1558. {
  1559. double startSeconds = 0;
  1560. var lengths = GetSegmentLengths(state);
  1561. if (requestedIndex >= lengths.Length)
  1562. {
  1563. var msg = string.Format(
  1564. CultureInfo.InvariantCulture,
  1565. "Invalid segment index requested: {0} - Segment count: {1}",
  1566. requestedIndex,
  1567. lengths.Length);
  1568. throw new ArgumentException(msg);
  1569. }
  1570. for (var i = 0; i <= requestedIndex; i++)
  1571. {
  1572. startSeconds += lengths[i];
  1573. }
  1574. return TimeSpan.FromSeconds(startSeconds).Ticks;
  1575. }
  1576. private int? GetCurrentTranscodingIndex(string playlist, string segmentExtension)
  1577. {
  1578. var job = _transcodingJobHelper.GetTranscodingJob(playlist, _transcodingJobType);
  1579. if (job == null || job.HasExited)
  1580. {
  1581. return null;
  1582. }
  1583. var file = GetLastTranscodingFile(playlist, segmentExtension, _fileSystem);
  1584. if (file == null)
  1585. {
  1586. return null;
  1587. }
  1588. var playlistFilename = Path.GetFileNameWithoutExtension(playlist);
  1589. var indexString = Path.GetFileNameWithoutExtension(file.Name).Substring(playlistFilename.Length);
  1590. return int.Parse(indexString, NumberStyles.Integer, CultureInfo.InvariantCulture);
  1591. }
  1592. private static FileSystemMetadata? GetLastTranscodingFile(string playlist, string segmentExtension, IFileSystem fileSystem)
  1593. {
  1594. var folder = Path.GetDirectoryName(playlist);
  1595. var filePrefix = Path.GetFileNameWithoutExtension(playlist) ?? string.Empty;
  1596. try
  1597. {
  1598. return fileSystem.GetFiles(folder, new[] { segmentExtension }, true, false)
  1599. .Where(i => Path.GetFileNameWithoutExtension(i.Name).StartsWith(filePrefix, StringComparison.OrdinalIgnoreCase))
  1600. .OrderByDescending(fileSystem.GetLastWriteTimeUtc)
  1601. .FirstOrDefault();
  1602. }
  1603. catch (IOException)
  1604. {
  1605. return null;
  1606. }
  1607. }
  1608. private void DeleteLastFile(string playlistPath, string segmentExtension, int retryCount)
  1609. {
  1610. var file = GetLastTranscodingFile(playlistPath, segmentExtension, _fileSystem);
  1611. if (file != null)
  1612. {
  1613. DeleteFile(file.FullName, retryCount);
  1614. }
  1615. }
  1616. private void DeleteFile(string path, int retryCount)
  1617. {
  1618. if (retryCount >= 5)
  1619. {
  1620. return;
  1621. }
  1622. _logger.LogDebug("Deleting partial HLS file {path}", path);
  1623. try
  1624. {
  1625. _fileSystem.DeleteFile(path);
  1626. }
  1627. catch (IOException ex)
  1628. {
  1629. _logger.LogError(ex, "Error deleting partial stream file(s) {path}", path);
  1630. var task = Task.Delay(100);
  1631. Task.WaitAll(task);
  1632. DeleteFile(path, retryCount + 1);
  1633. }
  1634. catch (Exception ex)
  1635. {
  1636. _logger.LogError(ex, "Error deleting partial stream file(s) {path}", path);
  1637. }
  1638. }
  1639. private long GetStartPositionTicks(StreamState state, int requestedIndex)
  1640. {
  1641. double startSeconds = 0;
  1642. var lengths = GetSegmentLengths(state);
  1643. if (requestedIndex >= lengths.Length)
  1644. {
  1645. var msg = string.Format(
  1646. CultureInfo.InvariantCulture,
  1647. "Invalid segment index requested: {0} - Segment count: {1}",
  1648. requestedIndex,
  1649. lengths.Length);
  1650. throw new ArgumentException(msg);
  1651. }
  1652. for (var i = 0; i < requestedIndex; i++)
  1653. {
  1654. startSeconds += lengths[i];
  1655. }
  1656. var position = TimeSpan.FromSeconds(startSeconds).Ticks;
  1657. return position;
  1658. }
  1659. }
  1660. }