EncodingJobInfo.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693
  1. #nullable disable
  2. #pragma warning disable CS1591, SA1401
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Globalization;
  6. using System.Linq;
  7. using Jellyfin.Data.Entities;
  8. using MediaBrowser.Model.Dlna;
  9. using MediaBrowser.Model.Drawing;
  10. using MediaBrowser.Model.Dto;
  11. using MediaBrowser.Model.Entities;
  12. using MediaBrowser.Model.MediaInfo;
  13. using MediaBrowser.Model.Net;
  14. using MediaBrowser.Model.Session;
  15. namespace MediaBrowser.Controller.MediaEncoding
  16. {
  17. // For now, a common base class until the API and MediaEncoding classes are unified
  18. public class EncodingJobInfo
  19. {
  20. public int? OutputAudioBitrate;
  21. public int? OutputAudioChannels;
  22. private TranscodeReason[] _transcodeReasons = null;
  23. public EncodingJobInfo(TranscodingJobType jobType)
  24. {
  25. TranscodingType = jobType;
  26. RemoteHttpHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
  27. SupportedAudioCodecs = Array.Empty<string>();
  28. SupportedVideoCodecs = Array.Empty<string>();
  29. SupportedSubtitleCodecs = Array.Empty<string>();
  30. }
  31. public TranscodeReason[] TranscodeReasons
  32. {
  33. get
  34. {
  35. if (_transcodeReasons == null)
  36. {
  37. if (BaseRequest.TranscodeReasons == null)
  38. {
  39. return Array.Empty<TranscodeReason>();
  40. }
  41. _transcodeReasons = BaseRequest.TranscodeReasons
  42. .Split(',')
  43. .Where(i => !string.IsNullOrEmpty(i))
  44. .Select(v => (TranscodeReason)Enum.Parse(typeof(TranscodeReason), v, true))
  45. .ToArray();
  46. }
  47. return _transcodeReasons;
  48. }
  49. }
  50. public IProgress<double> Progress { get; set; }
  51. public MediaStream VideoStream { get; set; }
  52. public VideoType VideoType { get; set; }
  53. public Dictionary<string, string> RemoteHttpHeaders { get; set; }
  54. public string OutputVideoCodec { get; set; }
  55. public MediaProtocol InputProtocol { get; set; }
  56. public string MediaPath { get; set; }
  57. public bool IsInputVideo { get; set; }
  58. public string OutputAudioCodec { get; set; }
  59. public int? OutputVideoBitrate { get; set; }
  60. public MediaStream SubtitleStream { get; set; }
  61. public SubtitleDeliveryMethod SubtitleDeliveryMethod { get; set; }
  62. public string[] SupportedSubtitleCodecs { get; set; }
  63. public int InternalSubtitleStreamOffset { get; set; }
  64. public MediaSourceInfo MediaSource { get; set; }
  65. public User User { get; set; }
  66. public long? RunTimeTicks { get; set; }
  67. public bool ReadInputAtNativeFramerate { get; set; }
  68. public string OutputFilePath { get; set; }
  69. public string MimeType { get; set; }
  70. public bool IgnoreInputDts => MediaSource.IgnoreDts;
  71. public bool IgnoreInputIndex => MediaSource.IgnoreIndex;
  72. public bool GenPtsInput => MediaSource.GenPtsInput;
  73. public bool DiscardCorruptFramesInput => false;
  74. public bool EnableFastSeekInput => false;
  75. public bool GenPtsOutput => false;
  76. public string OutputContainer { get; set; }
  77. public string OutputVideoSync { get; set; }
  78. public string AlbumCoverPath { get; set; }
  79. public string InputAudioSync { get; set; }
  80. public string InputVideoSync { get; set; }
  81. public TransportStreamTimestamp InputTimestamp { get; set; }
  82. public MediaStream AudioStream { get; set; }
  83. public string[] SupportedAudioCodecs { get; set; }
  84. public string[] SupportedVideoCodecs { get; set; }
  85. public string InputContainer { get; set; }
  86. public IsoType? IsoType { get; set; }
  87. public BaseEncodingJobOptions BaseRequest { get; set; }
  88. public bool IsVideoRequest { get; set; }
  89. public TranscodingJobType TranscodingType { get; set; }
  90. public long? StartTimeTicks => BaseRequest.StartTimeTicks;
  91. public bool CopyTimestamps => BaseRequest.CopyTimestamps;
  92. public bool IsSegmentedLiveStream
  93. => TranscodingType != TranscodingJobType.Progressive && !RunTimeTicks.HasValue;
  94. public int? TotalOutputBitrate => (OutputAudioBitrate ?? 0) + (OutputVideoBitrate ?? 0);
  95. public int? OutputWidth
  96. {
  97. get
  98. {
  99. if (VideoStream != null && VideoStream.Width.HasValue && VideoStream.Height.HasValue)
  100. {
  101. var size = new ImageDimensions(VideoStream.Width.Value, VideoStream.Height.Value);
  102. var newSize = DrawingUtils.Resize(
  103. size,
  104. BaseRequest.Width ?? 0,
  105. BaseRequest.Height ?? 0,
  106. BaseRequest.MaxWidth ?? 0,
  107. BaseRequest.MaxHeight ?? 0);
  108. return newSize.Width;
  109. }
  110. if (!IsVideoRequest)
  111. {
  112. return null;
  113. }
  114. return BaseRequest.MaxWidth ?? BaseRequest.Width;
  115. }
  116. }
  117. public int? OutputHeight
  118. {
  119. get
  120. {
  121. if (VideoStream != null && VideoStream.Width.HasValue && VideoStream.Height.HasValue)
  122. {
  123. var size = new ImageDimensions(VideoStream.Width.Value, VideoStream.Height.Value);
  124. var newSize = DrawingUtils.Resize(
  125. size,
  126. BaseRequest.Width ?? 0,
  127. BaseRequest.Height ?? 0,
  128. BaseRequest.MaxWidth ?? 0,
  129. BaseRequest.MaxHeight ?? 0);
  130. return newSize.Height;
  131. }
  132. if (!IsVideoRequest)
  133. {
  134. return null;
  135. }
  136. return BaseRequest.MaxHeight ?? BaseRequest.Height;
  137. }
  138. }
  139. public int? OutputAudioSampleRate
  140. {
  141. get
  142. {
  143. if (BaseRequest.Static
  144. || EncodingHelper.IsCopyCodec(OutputAudioCodec))
  145. {
  146. if (AudioStream != null)
  147. {
  148. return AudioStream.SampleRate;
  149. }
  150. }
  151. else if (BaseRequest.AudioSampleRate.HasValue)
  152. {
  153. // Don't exceed what the encoder supports
  154. // Seeing issues of attempting to encode to 88200
  155. return BaseRequest.AudioSampleRate.Value;
  156. }
  157. return null;
  158. }
  159. }
  160. public int? OutputAudioBitDepth
  161. {
  162. get
  163. {
  164. if (BaseRequest.Static
  165. || EncodingHelper.IsCopyCodec(OutputAudioCodec))
  166. {
  167. if (AudioStream != null)
  168. {
  169. return AudioStream.BitDepth;
  170. }
  171. }
  172. return null;
  173. }
  174. }
  175. /// <summary>
  176. /// Gets the target video level.
  177. /// </summary>
  178. public double? TargetVideoLevel
  179. {
  180. get
  181. {
  182. if (BaseRequest.Static || EncodingHelper.IsCopyCodec(OutputVideoCodec))
  183. {
  184. return VideoStream?.Level;
  185. }
  186. var level = GetRequestedLevel(ActualOutputVideoCodec);
  187. if (!string.IsNullOrEmpty(level)
  188. && double.TryParse(level, NumberStyles.Any, CultureInfo.InvariantCulture, out var result))
  189. {
  190. return result;
  191. }
  192. return null;
  193. }
  194. }
  195. /// <summary>
  196. /// Gets the target video bit depth.
  197. /// </summary>
  198. public int? TargetVideoBitDepth
  199. {
  200. get
  201. {
  202. if (BaseRequest.Static
  203. || EncodingHelper.IsCopyCodec(OutputVideoCodec))
  204. {
  205. return VideoStream?.BitDepth;
  206. }
  207. return null;
  208. }
  209. }
  210. /// <summary>
  211. /// Gets the target reference frames.
  212. /// </summary>
  213. /// <value>The target reference frames.</value>
  214. public int? TargetRefFrames
  215. {
  216. get
  217. {
  218. if (BaseRequest.Static
  219. || EncodingHelper.IsCopyCodec(OutputVideoCodec))
  220. {
  221. return VideoStream?.RefFrames;
  222. }
  223. return null;
  224. }
  225. }
  226. /// <summary>
  227. /// Gets the target framerate.
  228. /// </summary>
  229. public float? TargetFramerate
  230. {
  231. get
  232. {
  233. if (BaseRequest.Static
  234. || EncodingHelper.IsCopyCodec(OutputVideoCodec))
  235. {
  236. return VideoStream == null ? null : (VideoStream.AverageFrameRate ?? VideoStream.RealFrameRate);
  237. }
  238. return BaseRequest.MaxFramerate ?? BaseRequest.Framerate;
  239. }
  240. }
  241. public TransportStreamTimestamp TargetTimestamp
  242. {
  243. get
  244. {
  245. if (BaseRequest.Static)
  246. {
  247. return InputTimestamp;
  248. }
  249. return string.Equals(OutputContainer, "m2ts", StringComparison.OrdinalIgnoreCase) ?
  250. TransportStreamTimestamp.Valid :
  251. TransportStreamTimestamp.None;
  252. }
  253. }
  254. /// <summary>
  255. /// Gets the target packet length.
  256. /// </summary>
  257. public int? TargetPacketLength
  258. {
  259. get
  260. {
  261. if (BaseRequest.Static || EncodingHelper.IsCopyCodec(OutputVideoCodec))
  262. {
  263. return VideoStream?.PacketLength;
  264. }
  265. return null;
  266. }
  267. }
  268. /// <summary>
  269. /// Gets the target video profile.
  270. /// </summary>
  271. public string TargetVideoProfile
  272. {
  273. get
  274. {
  275. if (BaseRequest.Static || EncodingHelper.IsCopyCodec(OutputVideoCodec))
  276. {
  277. return VideoStream?.Profile;
  278. }
  279. var requestedProfile = GetRequestedProfiles(ActualOutputVideoCodec).FirstOrDefault();
  280. if (!string.IsNullOrEmpty(requestedProfile))
  281. {
  282. return requestedProfile;
  283. }
  284. return null;
  285. }
  286. }
  287. public string TargetVideoCodecTag
  288. {
  289. get
  290. {
  291. if (BaseRequest.Static
  292. || EncodingHelper.IsCopyCodec(OutputVideoCodec))
  293. {
  294. return VideoStream?.CodecTag;
  295. }
  296. return null;
  297. }
  298. }
  299. public bool? IsTargetAnamorphic
  300. {
  301. get
  302. {
  303. if (BaseRequest.Static
  304. || EncodingHelper.IsCopyCodec(OutputVideoCodec))
  305. {
  306. return VideoStream?.IsAnamorphic;
  307. }
  308. return false;
  309. }
  310. }
  311. public string ActualOutputVideoCodec
  312. {
  313. get
  314. {
  315. if (VideoStream == null)
  316. {
  317. return null;
  318. }
  319. if (EncodingHelper.IsCopyCodec(OutputVideoCodec))
  320. {
  321. return VideoStream.Codec;
  322. }
  323. return OutputVideoCodec;
  324. }
  325. }
  326. public string ActualOutputAudioCodec
  327. {
  328. get
  329. {
  330. if (AudioStream == null)
  331. {
  332. return null;
  333. }
  334. if (EncodingHelper.IsCopyCodec(OutputAudioCodec))
  335. {
  336. return AudioStream.Codec;
  337. }
  338. return OutputAudioCodec;
  339. }
  340. }
  341. public bool? IsTargetInterlaced
  342. {
  343. get
  344. {
  345. if (BaseRequest.Static
  346. || EncodingHelper.IsCopyCodec(OutputVideoCodec))
  347. {
  348. return VideoStream?.IsInterlaced;
  349. }
  350. if (DeInterlace(ActualOutputVideoCodec, true))
  351. {
  352. return false;
  353. }
  354. return VideoStream?.IsInterlaced;
  355. }
  356. }
  357. public bool? IsTargetAVC
  358. {
  359. get
  360. {
  361. if (BaseRequest.Static || EncodingHelper.IsCopyCodec(OutputVideoCodec))
  362. {
  363. return VideoStream?.IsAVC;
  364. }
  365. return false;
  366. }
  367. }
  368. public int? TargetVideoStreamCount
  369. {
  370. get
  371. {
  372. if (BaseRequest.Static)
  373. {
  374. return GetMediaStreamCount(MediaStreamType.Video, int.MaxValue);
  375. }
  376. return GetMediaStreamCount(MediaStreamType.Video, 1);
  377. }
  378. }
  379. public int? TargetAudioStreamCount
  380. {
  381. get
  382. {
  383. if (BaseRequest.Static)
  384. {
  385. return GetMediaStreamCount(MediaStreamType.Audio, int.MaxValue);
  386. }
  387. return GetMediaStreamCount(MediaStreamType.Audio, 1);
  388. }
  389. }
  390. public int HlsListSize => 0;
  391. public bool EnableBreakOnNonKeyFrames(string videoCodec)
  392. {
  393. if (TranscodingType != TranscodingJobType.Progressive)
  394. {
  395. if (IsSegmentedLiveStream)
  396. {
  397. return false;
  398. }
  399. return BaseRequest.BreakOnNonKeyFrames && EncodingHelper.IsCopyCodec(videoCodec);
  400. }
  401. return false;
  402. }
  403. private int? GetMediaStreamCount(MediaStreamType type, int limit)
  404. {
  405. var count = MediaSource.GetStreamCount(type);
  406. if (count.HasValue)
  407. {
  408. count = Math.Min(count.Value, limit);
  409. }
  410. return count;
  411. }
  412. public string GetMimeType(string outputPath, bool enableStreamDefault = true)
  413. {
  414. if (!string.IsNullOrEmpty(MimeType))
  415. {
  416. return MimeType;
  417. }
  418. if (enableStreamDefault)
  419. {
  420. return MimeTypes.GetMimeType(outputPath);
  421. }
  422. return MimeTypes.GetMimeType(outputPath, null);
  423. }
  424. public bool DeInterlace(string videoCodec, bool forceDeinterlaceIfSourceIsInterlaced)
  425. {
  426. var videoStream = VideoStream;
  427. var isInputInterlaced = videoStream != null && videoStream.IsInterlaced;
  428. if (!isInputInterlaced)
  429. {
  430. return false;
  431. }
  432. // Support general param
  433. if (BaseRequest.DeInterlace)
  434. {
  435. return true;
  436. }
  437. if (!string.IsNullOrEmpty(videoCodec))
  438. {
  439. if (string.Equals(BaseRequest.GetOption(videoCodec, "deinterlace"), "true", StringComparison.OrdinalIgnoreCase))
  440. {
  441. return true;
  442. }
  443. }
  444. return forceDeinterlaceIfSourceIsInterlaced;
  445. }
  446. public string[] GetRequestedProfiles(string codec)
  447. {
  448. if (!string.IsNullOrEmpty(BaseRequest.Profile))
  449. {
  450. return BaseRequest.Profile.Split(new[] { '|', ',' }, StringSplitOptions.RemoveEmptyEntries);
  451. }
  452. if (!string.IsNullOrEmpty(codec))
  453. {
  454. var profile = BaseRequest.GetOption(codec, "profile");
  455. if (!string.IsNullOrEmpty(profile))
  456. {
  457. return profile.Split(new[] { '|', ',' }, StringSplitOptions.RemoveEmptyEntries);
  458. }
  459. }
  460. return Array.Empty<string>();
  461. }
  462. public string GetRequestedLevel(string codec)
  463. {
  464. if (!string.IsNullOrEmpty(BaseRequest.Level))
  465. {
  466. return BaseRequest.Level;
  467. }
  468. if (!string.IsNullOrEmpty(codec))
  469. {
  470. return BaseRequest.GetOption(codec, "level");
  471. }
  472. return null;
  473. }
  474. public int? GetRequestedMaxRefFrames(string codec)
  475. {
  476. if (BaseRequest.MaxRefFrames.HasValue)
  477. {
  478. return BaseRequest.MaxRefFrames;
  479. }
  480. if (!string.IsNullOrEmpty(codec))
  481. {
  482. var value = BaseRequest.GetOption(codec, "maxrefframes");
  483. if (!string.IsNullOrEmpty(value)
  484. && int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var result))
  485. {
  486. return result;
  487. }
  488. }
  489. return null;
  490. }
  491. public int? GetRequestedVideoBitDepth(string codec)
  492. {
  493. if (BaseRequest.MaxVideoBitDepth.HasValue)
  494. {
  495. return BaseRequest.MaxVideoBitDepth;
  496. }
  497. if (!string.IsNullOrEmpty(codec))
  498. {
  499. var value = BaseRequest.GetOption(codec, "videobitdepth");
  500. if (!string.IsNullOrEmpty(value)
  501. && int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var result))
  502. {
  503. return result;
  504. }
  505. }
  506. return null;
  507. }
  508. public int? GetRequestedAudioBitDepth(string codec)
  509. {
  510. if (BaseRequest.MaxAudioBitDepth.HasValue)
  511. {
  512. return BaseRequest.MaxAudioBitDepth;
  513. }
  514. if (!string.IsNullOrEmpty(codec))
  515. {
  516. var value = BaseRequest.GetOption(codec, "audiobitdepth");
  517. if (!string.IsNullOrEmpty(value)
  518. && int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var result))
  519. {
  520. return result;
  521. }
  522. }
  523. return null;
  524. }
  525. public int? GetRequestedAudioChannels(string codec)
  526. {
  527. if (!string.IsNullOrEmpty(codec))
  528. {
  529. var value = BaseRequest.GetOption(codec, "audiochannels");
  530. if (!string.IsNullOrEmpty(value)
  531. && int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var result))
  532. {
  533. return result;
  534. }
  535. }
  536. if (BaseRequest.MaxAudioChannels.HasValue)
  537. {
  538. return BaseRequest.MaxAudioChannels;
  539. }
  540. if (BaseRequest.AudioChannels.HasValue)
  541. {
  542. return BaseRequest.AudioChannels;
  543. }
  544. if (BaseRequest.TranscodingMaxAudioChannels.HasValue)
  545. {
  546. return BaseRequest.TranscodingMaxAudioChannels;
  547. }
  548. return null;
  549. }
  550. public virtual void ReportTranscodingProgress(TimeSpan? transcodingPosition, float? framerate, double? percentComplete, long? bytesTranscoded, int? bitRate)
  551. {
  552. Progress.Report(percentComplete.Value);
  553. }
  554. }
  555. }