StreamInfo.cs 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092
  1. using MediaBrowser.Model.Drawing;
  2. using MediaBrowser.Model.Dto;
  3. using MediaBrowser.Model.Entities;
  4. using MediaBrowser.Model.Extensions;
  5. using MediaBrowser.Model.MediaInfo;
  6. using MediaBrowser.Model.Session;
  7. using System;
  8. using System.Collections.Generic;
  9. using System.Globalization;
  10. using System.Linq;
  11. namespace MediaBrowser.Model.Dlna
  12. {
  13. /// <summary>
  14. /// Class StreamInfo.
  15. /// </summary>
  16. public class StreamInfo
  17. {
  18. public StreamInfo()
  19. {
  20. AudioCodecs = Array.Empty<string>();
  21. VideoCodecs = Array.Empty<string>();
  22. SubtitleCodecs = Array.Empty<string>();
  23. TranscodeReasons = Array.Empty<TranscodeReason>();
  24. StreamOptions = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
  25. }
  26. public void SetOption(string qualifier, string name, string value)
  27. {
  28. if (string.IsNullOrEmpty(qualifier))
  29. {
  30. SetOption(name, value);
  31. }
  32. else
  33. {
  34. SetOption(qualifier + "-" + name, value);
  35. }
  36. }
  37. public void SetOption(string name, string value)
  38. {
  39. StreamOptions[name] = value;
  40. }
  41. public string GetOption(string qualifier, string name)
  42. {
  43. var value = GetOption(qualifier + "-" + name);
  44. if (string.IsNullOrEmpty(value))
  45. {
  46. value = GetOption(name);
  47. }
  48. return value;
  49. }
  50. public string GetOption(string name)
  51. {
  52. string value;
  53. if (StreamOptions.TryGetValue(name, out value))
  54. {
  55. return value;
  56. }
  57. return null;
  58. }
  59. public Guid ItemId { get; set; }
  60. public PlayMethod PlayMethod { get; set; }
  61. public EncodingContext Context { get; set; }
  62. public DlnaProfileType MediaType { get; set; }
  63. public string Container { get; set; }
  64. public string SubProtocol { get; set; }
  65. public long StartPositionTicks { get; set; }
  66. public int? SegmentLength { get; set; }
  67. public int? MinSegments { get; set; }
  68. public bool BreakOnNonKeyFrames { get; set; }
  69. public bool RequireAvc { get; set; }
  70. public bool RequireNonAnamorphic { get; set; }
  71. public bool CopyTimestamps { get; set; }
  72. public bool EnableMpegtsM2TsMode { get; set; }
  73. public bool EnableSubtitlesInManifest { get; set; }
  74. public string[] AudioCodecs { get; set; }
  75. public string[] VideoCodecs { get; set; }
  76. public int? AudioStreamIndex { get; set; }
  77. public int? SubtitleStreamIndex { get; set; }
  78. public int? TranscodingMaxAudioChannels { get; set; }
  79. public int? GlobalMaxAudioChannels { get; set; }
  80. public int? AudioBitrate { get; set; }
  81. public int? VideoBitrate { get; set; }
  82. public int? MaxWidth { get; set; }
  83. public int? MaxHeight { get; set; }
  84. public float? MaxFramerate { get; set; }
  85. public DeviceProfile DeviceProfile { get; set; }
  86. public string DeviceProfileId { get; set; }
  87. public string DeviceId { get; set; }
  88. public long? RunTimeTicks { get; set; }
  89. public TranscodeSeekInfo TranscodeSeekInfo { get; set; }
  90. public bool EstimateContentLength { get; set; }
  91. public MediaSourceInfo MediaSource { get; set; }
  92. public string[] SubtitleCodecs { get; set; }
  93. public SubtitleDeliveryMethod SubtitleDeliveryMethod { get; set; }
  94. public string SubtitleFormat { get; set; }
  95. public string PlaySessionId { get; set; }
  96. public TranscodeReason[] TranscodeReasons { get; set; }
  97. public Dictionary<string, string> StreamOptions { get; private set; }
  98. public string MediaSourceId
  99. {
  100. get
  101. {
  102. return MediaSource == null ? null : MediaSource.Id;
  103. }
  104. }
  105. public bool IsDirectStream
  106. {
  107. get
  108. {
  109. return PlayMethod == PlayMethod.DirectStream ||
  110. PlayMethod == PlayMethod.DirectPlay;
  111. }
  112. }
  113. public string ToUrl(string baseUrl, string accessToken)
  114. {
  115. if (PlayMethod == PlayMethod.DirectPlay)
  116. {
  117. return MediaSource.Path;
  118. }
  119. if (string.IsNullOrEmpty(baseUrl))
  120. {
  121. throw new ArgumentNullException(baseUrl);
  122. }
  123. List<string> list = new List<string>();
  124. foreach (NameValuePair pair in BuildParams(this, accessToken))
  125. {
  126. if (string.IsNullOrEmpty(pair.Value))
  127. {
  128. continue;
  129. }
  130. // Try to keep the url clean by omitting defaults
  131. if (StringHelper.EqualsIgnoreCase(pair.Name, "StartTimeTicks") &&
  132. StringHelper.EqualsIgnoreCase(pair.Value, "0"))
  133. {
  134. continue;
  135. }
  136. if (StringHelper.EqualsIgnoreCase(pair.Name, "SubtitleStreamIndex") &&
  137. StringHelper.EqualsIgnoreCase(pair.Value, "-1"))
  138. {
  139. continue;
  140. }
  141. if (StringHelper.EqualsIgnoreCase(pair.Name, "Static") &&
  142. StringHelper.EqualsIgnoreCase(pair.Value, "false"))
  143. {
  144. continue;
  145. }
  146. var encodedValue = pair.Value.Replace(" ", "%20");
  147. list.Add(string.Format("{0}={1}", pair.Name, encodedValue));
  148. }
  149. string queryString = string.Join("&", list.ToArray());
  150. return GetUrl(baseUrl, queryString);
  151. }
  152. private string GetUrl(string baseUrl, string queryString)
  153. {
  154. if (string.IsNullOrEmpty(baseUrl))
  155. {
  156. throw new ArgumentNullException(baseUrl);
  157. }
  158. string extension = string.IsNullOrEmpty(Container) ? string.Empty : "." + Container;
  159. baseUrl = baseUrl.TrimEnd('/');
  160. if (MediaType == DlnaProfileType.Audio)
  161. {
  162. if (StringHelper.EqualsIgnoreCase(SubProtocol, "hls"))
  163. {
  164. return string.Format("{0}/audio/{1}/master.m3u8?{2}", baseUrl, ItemId, queryString);
  165. }
  166. return string.Format("{0}/audio/{1}/stream{2}?{3}", baseUrl, ItemId, extension, queryString);
  167. }
  168. if (StringHelper.EqualsIgnoreCase(SubProtocol, "hls"))
  169. {
  170. return string.Format("{0}/videos/{1}/master.m3u8?{2}", baseUrl, ItemId, queryString);
  171. }
  172. return string.Format("{0}/videos/{1}/stream{2}?{3}", baseUrl, ItemId, extension, queryString);
  173. }
  174. private static List<NameValuePair> BuildParams(StreamInfo item, string accessToken)
  175. {
  176. List<NameValuePair> list = new List<NameValuePair>();
  177. string audioCodecs = item.AudioCodecs.Length == 0 ?
  178. string.Empty :
  179. string.Join(",", item.AudioCodecs);
  180. string videoCodecs = item.VideoCodecs.Length == 0 ?
  181. string.Empty :
  182. string.Join(",", item.VideoCodecs);
  183. list.Add(new NameValuePair("DeviceProfileId", item.DeviceProfileId ?? string.Empty));
  184. list.Add(new NameValuePair("DeviceId", item.DeviceId ?? string.Empty));
  185. list.Add(new NameValuePair("MediaSourceId", item.MediaSourceId ?? string.Empty));
  186. list.Add(new NameValuePair("Static", item.IsDirectStream.ToString().ToLower()));
  187. list.Add(new NameValuePair("VideoCodec", videoCodecs));
  188. list.Add(new NameValuePair("AudioCodec", audioCodecs));
  189. list.Add(new NameValuePair("AudioStreamIndex", item.AudioStreamIndex.HasValue ? item.AudioStreamIndex.Value.ToString(CultureInfo.InvariantCulture) : string.Empty));
  190. list.Add(new NameValuePair("SubtitleStreamIndex", item.SubtitleStreamIndex.HasValue && item.SubtitleDeliveryMethod != SubtitleDeliveryMethod.External ? item.SubtitleStreamIndex.Value.ToString(CultureInfo.InvariantCulture) : string.Empty));
  191. list.Add(new NameValuePair("VideoBitrate", item.VideoBitrate.HasValue ? item.VideoBitrate.Value.ToString(CultureInfo.InvariantCulture) : string.Empty));
  192. list.Add(new NameValuePair("AudioBitrate", item.AudioBitrate.HasValue ? item.AudioBitrate.Value.ToString(CultureInfo.InvariantCulture) : string.Empty));
  193. list.Add(new NameValuePair("MaxFramerate", item.MaxFramerate.HasValue ? item.MaxFramerate.Value.ToString(CultureInfo.InvariantCulture) : string.Empty));
  194. list.Add(new NameValuePair("MaxWidth", item.MaxWidth.HasValue ? item.MaxWidth.Value.ToString(CultureInfo.InvariantCulture) : string.Empty));
  195. list.Add(new NameValuePair("MaxHeight", item.MaxHeight.HasValue ? item.MaxHeight.Value.ToString(CultureInfo.InvariantCulture) : string.Empty));
  196. long startPositionTicks = item.StartPositionTicks;
  197. var isHls = StringHelper.EqualsIgnoreCase(item.SubProtocol, "hls");
  198. if (isHls)
  199. {
  200. list.Add(new NameValuePair("StartTimeTicks", string.Empty));
  201. }
  202. else
  203. {
  204. list.Add(new NameValuePair("StartTimeTicks", startPositionTicks.ToString(CultureInfo.InvariantCulture)));
  205. }
  206. list.Add(new NameValuePair("PlaySessionId", item.PlaySessionId ?? string.Empty));
  207. list.Add(new NameValuePair("api_key", accessToken ?? string.Empty));
  208. string liveStreamId = item.MediaSource == null ? null : item.MediaSource.LiveStreamId;
  209. list.Add(new NameValuePair("LiveStreamId", liveStreamId ?? string.Empty));
  210. list.Add(new NameValuePair("SubtitleMethod", item.SubtitleStreamIndex.HasValue && item.SubtitleDeliveryMethod != SubtitleDeliveryMethod.External ? item.SubtitleDeliveryMethod.ToString() : string.Empty));
  211. if (!item.IsDirectStream)
  212. {
  213. if (item.RequireNonAnamorphic)
  214. {
  215. list.Add(new NameValuePair("RequireNonAnamorphic", item.RequireNonAnamorphic.ToString().ToLower()));
  216. }
  217. list.Add(new NameValuePair("TranscodingMaxAudioChannels", item.TranscodingMaxAudioChannels.HasValue ? item.TranscodingMaxAudioChannels.Value.ToString(CultureInfo.InvariantCulture) : string.Empty));
  218. if (item.EnableSubtitlesInManifest)
  219. {
  220. list.Add(new NameValuePair("EnableSubtitlesInManifest", item.EnableSubtitlesInManifest.ToString().ToLower()));
  221. }
  222. if (item.EnableMpegtsM2TsMode)
  223. {
  224. list.Add(new NameValuePair("EnableMpegtsM2TsMode", item.EnableMpegtsM2TsMode.ToString().ToLower()));
  225. }
  226. if (item.EstimateContentLength)
  227. {
  228. list.Add(new NameValuePair("EstimateContentLength", item.EstimateContentLength.ToString().ToLower()));
  229. }
  230. if (item.TranscodeSeekInfo != TranscodeSeekInfo.Auto)
  231. {
  232. list.Add(new NameValuePair("TranscodeSeekInfo", item.TranscodeSeekInfo.ToString().ToLower()));
  233. }
  234. if (item.CopyTimestamps)
  235. {
  236. list.Add(new NameValuePair("CopyTimestamps", item.CopyTimestamps.ToString().ToLower()));
  237. }
  238. list.Add(new NameValuePair("RequireAvc", item.RequireAvc.ToString().ToLower()));
  239. }
  240. list.Add(new NameValuePair("Tag", item.MediaSource.ETag ?? string.Empty));
  241. string subtitleCodecs = item.SubtitleCodecs.Length == 0 ?
  242. string.Empty :
  243. string.Join(",", item.SubtitleCodecs);
  244. list.Add(new NameValuePair("SubtitleCodec", item.SubtitleStreamIndex.HasValue && item.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Embed ? subtitleCodecs : string.Empty));
  245. if (isHls)
  246. {
  247. list.Add(new NameValuePair("SegmentContainer", item.Container ?? string.Empty));
  248. if (item.SegmentLength.HasValue)
  249. {
  250. list.Add(new NameValuePair("SegmentLength", item.SegmentLength.Value.ToString(CultureInfo.InvariantCulture)));
  251. }
  252. if (item.MinSegments.HasValue)
  253. {
  254. list.Add(new NameValuePair("MinSegments", item.MinSegments.Value.ToString(CultureInfo.InvariantCulture)));
  255. }
  256. list.Add(new NameValuePair("BreakOnNonKeyFrames", item.BreakOnNonKeyFrames.ToString()));
  257. }
  258. foreach (var pair in item.StreamOptions)
  259. {
  260. if (string.IsNullOrEmpty(pair.Value))
  261. {
  262. continue;
  263. }
  264. // strip spaces to avoid having to encode h264 profile names
  265. list.Add(new NameValuePair(pair.Key, pair.Value.Replace(" ", "")));
  266. }
  267. if (!item.IsDirectStream)
  268. {
  269. list.Add(new NameValuePair("TranscodeReasons", string.Join(",", item.TranscodeReasons.Distinct().Select(i => i.ToString()).ToArray())));
  270. }
  271. return list;
  272. }
  273. public List<SubtitleStreamInfo> GetExternalSubtitles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, string baseUrl, string accessToken)
  274. {
  275. return GetExternalSubtitles(transcoderSupport, includeSelectedTrackOnly, false, baseUrl, accessToken);
  276. }
  277. public List<SubtitleStreamInfo> GetExternalSubtitles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, bool enableAllProfiles, string baseUrl, string accessToken)
  278. {
  279. List<SubtitleStreamInfo> list = GetSubtitleProfiles(transcoderSupport, includeSelectedTrackOnly, enableAllProfiles, baseUrl, accessToken);
  280. List<SubtitleStreamInfo> newList = new List<SubtitleStreamInfo>();
  281. // First add the selected track
  282. foreach (SubtitleStreamInfo stream in list)
  283. {
  284. if (stream.DeliveryMethod == SubtitleDeliveryMethod.External)
  285. {
  286. newList.Add(stream);
  287. }
  288. }
  289. return newList;
  290. }
  291. public List<SubtitleStreamInfo> GetSubtitleProfiles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, string baseUrl, string accessToken)
  292. {
  293. return GetSubtitleProfiles(transcoderSupport, includeSelectedTrackOnly, false, baseUrl, accessToken);
  294. }
  295. public List<SubtitleStreamInfo> GetSubtitleProfiles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, bool enableAllProfiles, string baseUrl, string accessToken)
  296. {
  297. List<SubtitleStreamInfo> list = new List<SubtitleStreamInfo>();
  298. // HLS will preserve timestamps so we can just grab the full subtitle stream
  299. long startPositionTicks = StringHelper.EqualsIgnoreCase(SubProtocol, "hls")
  300. ? 0
  301. : (PlayMethod == PlayMethod.Transcode && !CopyTimestamps ? StartPositionTicks : 0);
  302. // First add the selected track
  303. if (SubtitleStreamIndex.HasValue)
  304. {
  305. foreach (MediaStream stream in MediaSource.MediaStreams)
  306. {
  307. if (stream.Type == MediaStreamType.Subtitle && stream.Index == SubtitleStreamIndex.Value)
  308. {
  309. AddSubtitleProfiles(list, stream, transcoderSupport, enableAllProfiles, baseUrl, accessToken, startPositionTicks);
  310. }
  311. }
  312. }
  313. if (!includeSelectedTrackOnly)
  314. {
  315. foreach (MediaStream stream in MediaSource.MediaStreams)
  316. {
  317. if (stream.Type == MediaStreamType.Subtitle && (!SubtitleStreamIndex.HasValue || stream.Index != SubtitleStreamIndex.Value))
  318. {
  319. AddSubtitleProfiles(list, stream, transcoderSupport, enableAllProfiles, baseUrl, accessToken, startPositionTicks);
  320. }
  321. }
  322. }
  323. return list;
  324. }
  325. private void AddSubtitleProfiles(List<SubtitleStreamInfo> list, MediaStream stream, ITranscoderSupport transcoderSupport, bool enableAllProfiles, string baseUrl, string accessToken, long startPositionTicks)
  326. {
  327. if (enableAllProfiles)
  328. {
  329. foreach (SubtitleProfile profile in DeviceProfile.SubtitleProfiles)
  330. {
  331. SubtitleStreamInfo info = GetSubtitleStreamInfo(stream, baseUrl, accessToken, startPositionTicks, new[] { profile }, transcoderSupport);
  332. list.Add(info);
  333. }
  334. }
  335. else
  336. {
  337. SubtitleStreamInfo info = GetSubtitleStreamInfo(stream, baseUrl, accessToken, startPositionTicks, DeviceProfile.SubtitleProfiles, transcoderSupport);
  338. list.Add(info);
  339. }
  340. }
  341. private SubtitleStreamInfo GetSubtitleStreamInfo(MediaStream stream, string baseUrl, string accessToken, long startPositionTicks, SubtitleProfile[] subtitleProfiles, ITranscoderSupport transcoderSupport)
  342. {
  343. SubtitleProfile subtitleProfile = StreamBuilder.GetSubtitleProfile(MediaSource, stream, subtitleProfiles, PlayMethod, transcoderSupport, Container, SubProtocol);
  344. SubtitleStreamInfo info = new SubtitleStreamInfo
  345. {
  346. IsForced = stream.IsForced,
  347. Language = stream.Language,
  348. Name = stream.Language ?? "Unknown",
  349. Format = subtitleProfile.Format,
  350. Index = stream.Index,
  351. DeliveryMethod = subtitleProfile.Method,
  352. DisplayTitle = stream.DisplayTitle
  353. };
  354. if (info.DeliveryMethod == SubtitleDeliveryMethod.External)
  355. {
  356. if (MediaSource.Protocol == MediaProtocol.File || !StringHelper.EqualsIgnoreCase(stream.Codec, subtitleProfile.Format) || !stream.IsExternal)
  357. {
  358. info.Url = string.Format("{0}/Videos/{1}/{2}/Subtitles/{3}/{4}/Stream.{5}",
  359. baseUrl,
  360. ItemId,
  361. MediaSourceId,
  362. stream.Index.ToString(CultureInfo.InvariantCulture),
  363. startPositionTicks.ToString(CultureInfo.InvariantCulture),
  364. subtitleProfile.Format);
  365. if (!string.IsNullOrEmpty(accessToken))
  366. {
  367. info.Url += "?api_key=" + accessToken;
  368. }
  369. info.IsExternalUrl = false;
  370. }
  371. else
  372. {
  373. info.Url = stream.Path;
  374. info.IsExternalUrl = true;
  375. }
  376. }
  377. return info;
  378. }
  379. /// <summary>
  380. /// Returns the audio stream that will be used
  381. /// </summary>
  382. public MediaStream TargetAudioStream
  383. {
  384. get
  385. {
  386. if (MediaSource != null)
  387. {
  388. return MediaSource.GetDefaultAudioStream(AudioStreamIndex);
  389. }
  390. return null;
  391. }
  392. }
  393. /// <summary>
  394. /// Returns the video stream that will be used
  395. /// </summary>
  396. public MediaStream TargetVideoStream
  397. {
  398. get
  399. {
  400. if (MediaSource != null)
  401. {
  402. return MediaSource.VideoStream;
  403. }
  404. return null;
  405. }
  406. }
  407. /// <summary>
  408. /// Predicts the audio sample rate that will be in the output stream
  409. /// </summary>
  410. public int? TargetAudioSampleRate
  411. {
  412. get
  413. {
  414. MediaStream stream = TargetAudioStream;
  415. return stream == null ? null : stream.SampleRate;
  416. }
  417. }
  418. /// <summary>
  419. /// Predicts the audio sample rate that will be in the output stream
  420. /// </summary>
  421. public int? TargetAudioBitDepth
  422. {
  423. get
  424. {
  425. if (IsDirectStream)
  426. {
  427. return TargetAudioStream == null ? (int?)null : TargetAudioStream.BitDepth;
  428. }
  429. var targetAudioCodecs = TargetAudioCodec;
  430. var audioCodec = targetAudioCodecs.Length == 0 ? null : targetAudioCodecs[0];
  431. if (!string.IsNullOrEmpty(audioCodec))
  432. {
  433. return GetTargetAudioBitDepth(audioCodec);
  434. }
  435. return TargetAudioStream == null ? (int?)null : TargetAudioStream.BitDepth;
  436. }
  437. }
  438. /// <summary>
  439. /// Predicts the audio sample rate that will be in the output stream
  440. /// </summary>
  441. public int? TargetVideoBitDepth
  442. {
  443. get
  444. {
  445. if (IsDirectStream)
  446. {
  447. return TargetVideoStream == null ? (int?)null : TargetVideoStream.BitDepth;
  448. }
  449. var targetVideoCodecs = TargetVideoCodec;
  450. var videoCodec = targetVideoCodecs.Length == 0 ? null : targetVideoCodecs[0];
  451. if (!string.IsNullOrEmpty(videoCodec))
  452. {
  453. return GetTargetVideoBitDepth(videoCodec);
  454. }
  455. return TargetVideoStream == null ? (int?)null : TargetVideoStream.BitDepth;
  456. }
  457. }
  458. /// <summary>
  459. /// Gets the target reference frames.
  460. /// </summary>
  461. /// <value>The target reference frames.</value>
  462. public int? TargetRefFrames
  463. {
  464. get
  465. {
  466. if (IsDirectStream)
  467. {
  468. return TargetVideoStream == null ? (int?)null : TargetVideoStream.RefFrames;
  469. }
  470. var targetVideoCodecs = TargetVideoCodec;
  471. var videoCodec = targetVideoCodecs.Length == 0 ? null : targetVideoCodecs[0];
  472. if (!string.IsNullOrEmpty(videoCodec))
  473. {
  474. return GetTargetRefFrames(videoCodec);
  475. }
  476. return TargetVideoStream == null ? (int?)null : TargetVideoStream.RefFrames;
  477. }
  478. }
  479. /// <summary>
  480. /// Predicts the audio sample rate that will be in the output stream
  481. /// </summary>
  482. public float? TargetFramerate
  483. {
  484. get
  485. {
  486. MediaStream stream = TargetVideoStream;
  487. return MaxFramerate.HasValue && !IsDirectStream
  488. ? MaxFramerate
  489. : stream == null ? null : stream.AverageFrameRate ?? stream.RealFrameRate;
  490. }
  491. }
  492. /// <summary>
  493. /// Predicts the audio sample rate that will be in the output stream
  494. /// </summary>
  495. public double? TargetVideoLevel
  496. {
  497. get
  498. {
  499. if (IsDirectStream)
  500. {
  501. return TargetVideoStream == null ? (double?)null : TargetVideoStream.Level;
  502. }
  503. var targetVideoCodecs = TargetVideoCodec;
  504. var videoCodec = targetVideoCodecs.Length == 0 ? null : targetVideoCodecs[0];
  505. if (!string.IsNullOrEmpty(videoCodec))
  506. {
  507. return GetTargetVideoLevel(videoCodec);
  508. }
  509. return TargetVideoStream == null ? (double?)null : TargetVideoStream.Level;
  510. }
  511. }
  512. public int? GetTargetVideoBitDepth(string codec)
  513. {
  514. var value = GetOption(codec, "videobitdepth");
  515. if (string.IsNullOrEmpty(value))
  516. {
  517. return null;
  518. }
  519. int result;
  520. if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out result))
  521. {
  522. return result;
  523. }
  524. return null;
  525. }
  526. public int? GetTargetAudioBitDepth(string codec)
  527. {
  528. var value = GetOption(codec, "audiobitdepth");
  529. if (string.IsNullOrEmpty(value))
  530. {
  531. return null;
  532. }
  533. int result;
  534. if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out result))
  535. {
  536. return result;
  537. }
  538. return null;
  539. }
  540. public double? GetTargetVideoLevel(string codec)
  541. {
  542. var value = GetOption(codec, "level");
  543. if (string.IsNullOrEmpty(value))
  544. {
  545. return null;
  546. }
  547. double result;
  548. if (double.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out result))
  549. {
  550. return result;
  551. }
  552. return null;
  553. }
  554. public int? GetTargetRefFrames(string codec)
  555. {
  556. var value = GetOption(codec, "maxrefframes");
  557. if (string.IsNullOrEmpty(value))
  558. {
  559. return null;
  560. }
  561. int result;
  562. if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out result))
  563. {
  564. return result;
  565. }
  566. return null;
  567. }
  568. /// <summary>
  569. /// Predicts the audio sample rate that will be in the output stream
  570. /// </summary>
  571. public int? TargetPacketLength
  572. {
  573. get
  574. {
  575. MediaStream stream = TargetVideoStream;
  576. return !IsDirectStream
  577. ? null
  578. : stream == null ? null : stream.PacketLength;
  579. }
  580. }
  581. /// <summary>
  582. /// Predicts the audio sample rate that will be in the output stream
  583. /// </summary>
  584. public string TargetVideoProfile
  585. {
  586. get
  587. {
  588. if (IsDirectStream)
  589. {
  590. return TargetVideoStream == null ? null : TargetVideoStream.Profile;
  591. }
  592. var targetVideoCodecs = TargetVideoCodec;
  593. var videoCodec = targetVideoCodecs.Length == 0 ? null : targetVideoCodecs[0];
  594. if (!string.IsNullOrEmpty(videoCodec))
  595. {
  596. return GetOption(videoCodec, "profile");
  597. }
  598. return TargetVideoStream == null ? null : TargetVideoStream.Profile;
  599. }
  600. }
  601. /// <summary>
  602. /// Gets the target video codec tag.
  603. /// </summary>
  604. /// <value>The target video codec tag.</value>
  605. public string TargetVideoCodecTag
  606. {
  607. get
  608. {
  609. MediaStream stream = TargetVideoStream;
  610. return !IsDirectStream
  611. ? null
  612. : stream == null ? null : stream.CodecTag;
  613. }
  614. }
  615. /// <summary>
  616. /// Predicts the audio bitrate that will be in the output stream
  617. /// </summary>
  618. public int? TargetAudioBitrate
  619. {
  620. get
  621. {
  622. MediaStream stream = TargetAudioStream;
  623. return AudioBitrate.HasValue && !IsDirectStream
  624. ? AudioBitrate
  625. : stream == null ? null : stream.BitRate;
  626. }
  627. }
  628. /// <summary>
  629. /// Predicts the audio channels that will be in the output stream
  630. /// </summary>
  631. public int? TargetAudioChannels
  632. {
  633. get
  634. {
  635. if (IsDirectStream)
  636. {
  637. return TargetAudioStream == null ? (int?)null : TargetAudioStream.Channels;
  638. }
  639. var targetAudioCodecs = TargetAudioCodec;
  640. var codec = targetAudioCodecs.Length == 0 ? null : targetAudioCodecs[0];
  641. if (!string.IsNullOrEmpty(codec))
  642. {
  643. return GetTargetRefFrames(codec);
  644. }
  645. return TargetAudioStream == null ? (int?)null : TargetAudioStream.Channels;
  646. }
  647. }
  648. public int? GetTargetAudioChannels(string codec)
  649. {
  650. var defaultValue = GlobalMaxAudioChannels;
  651. var value = GetOption(codec, "audiochannels");
  652. if (string.IsNullOrEmpty(value))
  653. {
  654. return defaultValue;
  655. }
  656. int result;
  657. if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out result))
  658. {
  659. return Math.Min(result, defaultValue ?? result);
  660. }
  661. return defaultValue;
  662. }
  663. /// <summary>
  664. /// Predicts the audio codec that will be in the output stream
  665. /// </summary>
  666. public string[] TargetAudioCodec
  667. {
  668. get
  669. {
  670. MediaStream stream = TargetAudioStream;
  671. string inputCodec = stream == null ? null : stream.Codec;
  672. if (IsDirectStream)
  673. {
  674. return string.IsNullOrEmpty(inputCodec) ? new string[] { } : new[] { inputCodec };
  675. }
  676. foreach (string codec in AudioCodecs)
  677. {
  678. if (StringHelper.EqualsIgnoreCase(codec, inputCodec))
  679. {
  680. return string.IsNullOrEmpty(codec) ? new string[] { } : new[] { codec };
  681. }
  682. }
  683. return AudioCodecs;
  684. }
  685. }
  686. public string[] TargetVideoCodec
  687. {
  688. get
  689. {
  690. MediaStream stream = TargetVideoStream;
  691. string inputCodec = stream == null ? null : stream.Codec;
  692. if (IsDirectStream)
  693. {
  694. return string.IsNullOrEmpty(inputCodec) ? new string[] { } : new[] { inputCodec };
  695. }
  696. foreach (string codec in VideoCodecs)
  697. {
  698. if (StringHelper.EqualsIgnoreCase(codec, inputCodec))
  699. {
  700. return string.IsNullOrEmpty(codec) ? new string[] { } : new[] { codec };
  701. }
  702. }
  703. return VideoCodecs;
  704. }
  705. }
  706. /// <summary>
  707. /// Predicts the audio channels that will be in the output stream
  708. /// </summary>
  709. public long? TargetSize
  710. {
  711. get
  712. {
  713. if (IsDirectStream)
  714. {
  715. return MediaSource.Size;
  716. }
  717. if (RunTimeTicks.HasValue)
  718. {
  719. int? totalBitrate = TargetTotalBitrate;
  720. double totalSeconds = RunTimeTicks.Value;
  721. // Convert to ms
  722. totalSeconds /= 10000;
  723. // Convert to seconds
  724. totalSeconds /= 1000;
  725. return totalBitrate.HasValue ?
  726. Convert.ToInt64(totalBitrate.Value * totalSeconds) :
  727. (long?)null;
  728. }
  729. return null;
  730. }
  731. }
  732. public int? TargetVideoBitrate
  733. {
  734. get
  735. {
  736. MediaStream stream = TargetVideoStream;
  737. return VideoBitrate.HasValue && !IsDirectStream
  738. ? VideoBitrate
  739. : stream == null ? null : stream.BitRate;
  740. }
  741. }
  742. public TransportStreamTimestamp TargetTimestamp
  743. {
  744. get
  745. {
  746. TransportStreamTimestamp defaultValue = StringHelper.EqualsIgnoreCase(Container, "m2ts")
  747. ? TransportStreamTimestamp.Valid
  748. : TransportStreamTimestamp.None;
  749. return !IsDirectStream
  750. ? defaultValue
  751. : MediaSource == null ? defaultValue : MediaSource.Timestamp ?? TransportStreamTimestamp.None;
  752. }
  753. }
  754. public int? TargetTotalBitrate
  755. {
  756. get
  757. {
  758. return (TargetAudioBitrate ?? 0) + (TargetVideoBitrate ?? 0);
  759. }
  760. }
  761. public bool? IsTargetAnamorphic
  762. {
  763. get
  764. {
  765. if (IsDirectStream)
  766. {
  767. return TargetVideoStream == null ? null : TargetVideoStream.IsAnamorphic;
  768. }
  769. return false;
  770. }
  771. }
  772. public bool? IsTargetInterlaced
  773. {
  774. get
  775. {
  776. if (IsDirectStream)
  777. {
  778. return TargetVideoStream == null ? (bool?)null : TargetVideoStream.IsInterlaced;
  779. }
  780. var targetVideoCodecs = TargetVideoCodec;
  781. var videoCodec = targetVideoCodecs.Length == 0 ? null : targetVideoCodecs[0];
  782. if (!string.IsNullOrEmpty(videoCodec))
  783. {
  784. if (string.Equals(GetOption(videoCodec, "deinterlace"), "true", StringComparison.OrdinalIgnoreCase))
  785. {
  786. return false;
  787. }
  788. }
  789. return TargetVideoStream == null ? (bool?)null : TargetVideoStream.IsInterlaced;
  790. }
  791. }
  792. public bool? IsTargetAVC
  793. {
  794. get
  795. {
  796. if (IsDirectStream)
  797. {
  798. return TargetVideoStream == null ? null : TargetVideoStream.IsAVC;
  799. }
  800. return true;
  801. }
  802. }
  803. public int? TargetWidth
  804. {
  805. get
  806. {
  807. MediaStream videoStream = TargetVideoStream;
  808. if (videoStream != null && videoStream.Width.HasValue && videoStream.Height.HasValue)
  809. {
  810. ImageSize size = new ImageSize
  811. {
  812. Width = videoStream.Width.Value,
  813. Height = videoStream.Height.Value
  814. };
  815. double? maxWidth = MaxWidth.HasValue ? (double)MaxWidth.Value : (double?)null;
  816. double? maxHeight = MaxHeight.HasValue ? (double)MaxHeight.Value : (double?)null;
  817. ImageSize newSize = DrawingUtils.Resize(size,
  818. 0,
  819. 0,
  820. maxWidth ?? 0,
  821. maxHeight ?? 0);
  822. return Convert.ToInt32(newSize.Width);
  823. }
  824. return MaxWidth;
  825. }
  826. }
  827. public int? TargetHeight
  828. {
  829. get
  830. {
  831. MediaStream videoStream = TargetVideoStream;
  832. if (videoStream != null && videoStream.Width.HasValue && videoStream.Height.HasValue)
  833. {
  834. ImageSize size = new ImageSize
  835. {
  836. Width = videoStream.Width.Value,
  837. Height = videoStream.Height.Value
  838. };
  839. double? maxWidth = MaxWidth.HasValue ? (double)MaxWidth.Value : (double?)null;
  840. double? maxHeight = MaxHeight.HasValue ? (double)MaxHeight.Value : (double?)null;
  841. ImageSize newSize = DrawingUtils.Resize(size,
  842. 0,
  843. 0,
  844. maxWidth ?? 0,
  845. maxHeight ?? 0);
  846. return Convert.ToInt32(newSize.Height);
  847. }
  848. return MaxHeight;
  849. }
  850. }
  851. public int? TargetVideoStreamCount
  852. {
  853. get
  854. {
  855. if (IsDirectStream)
  856. {
  857. return GetMediaStreamCount(MediaStreamType.Video, int.MaxValue);
  858. }
  859. return GetMediaStreamCount(MediaStreamType.Video, 1);
  860. }
  861. }
  862. public int? TargetAudioStreamCount
  863. {
  864. get
  865. {
  866. if (IsDirectStream)
  867. {
  868. return GetMediaStreamCount(MediaStreamType.Audio, int.MaxValue);
  869. }
  870. return GetMediaStreamCount(MediaStreamType.Audio, 1);
  871. }
  872. }
  873. private int? GetMediaStreamCount(MediaStreamType type, int limit)
  874. {
  875. var count = MediaSource.GetStreamCount(type);
  876. if (count.HasValue)
  877. {
  878. count = Math.Min(count.Value, limit);
  879. }
  880. return count;
  881. }
  882. public List<MediaStream> GetSelectableAudioStreams()
  883. {
  884. return GetSelectableStreams(MediaStreamType.Audio);
  885. }
  886. public List<MediaStream> GetSelectableSubtitleStreams()
  887. {
  888. return GetSelectableStreams(MediaStreamType.Subtitle);
  889. }
  890. public List<MediaStream> GetSelectableStreams(MediaStreamType type)
  891. {
  892. List<MediaStream> list = new List<MediaStream>();
  893. foreach (MediaStream stream in MediaSource.MediaStreams)
  894. {
  895. if (type == stream.Type)
  896. {
  897. list.Add(stream);
  898. }
  899. }
  900. return list;
  901. }
  902. }
  903. }