StreamInfo.cs 37 KB

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