StreamInfo.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518
  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. namespace MediaBrowser.Model.Dlna
  10. {
  11. /// <summary>
  12. /// Class StreamInfo.
  13. /// </summary>
  14. public class StreamInfo
  15. {
  16. public string ItemId { get; set; }
  17. public PlayMethod PlayMethod { get; set; }
  18. public DlnaProfileType MediaType { get; set; }
  19. public string Container { get; set; }
  20. public string Protocol { get; set; }
  21. public long StartPositionTicks { get; set; }
  22. public string VideoCodec { get; set; }
  23. public string VideoProfile { get; set; }
  24. public string AudioCodec { get; set; }
  25. public int? AudioStreamIndex { get; set; }
  26. public int? SubtitleStreamIndex { get; set; }
  27. public int? MaxAudioChannels { get; set; }
  28. public int? AudioBitrate { get; set; }
  29. public int? VideoBitrate { get; set; }
  30. public int? VideoLevel { get; set; }
  31. public int? MaxWidth { get; set; }
  32. public int? MaxHeight { get; set; }
  33. public float? MaxFramerate { get; set; }
  34. public string DeviceProfileId { get; set; }
  35. public string DeviceId { get; set; }
  36. public long? RunTimeTicks { get; set; }
  37. public TranscodeSeekInfo TranscodeSeekInfo { get; set; }
  38. public bool EstimateContentLength { get; set; }
  39. public MediaSourceInfo MediaSource { get; set; }
  40. public SubtitleDeliveryMethod SubtitleDeliveryMethod { get; set; }
  41. public string SubtitleFormat { get; set; }
  42. public string MediaSourceId
  43. {
  44. get
  45. {
  46. return MediaSource == null ? null : MediaSource.Id;
  47. }
  48. }
  49. public bool IsDirectStream
  50. {
  51. get { return PlayMethod == PlayMethod.DirectStream; }
  52. }
  53. public string ToUrl(string baseUrl)
  54. {
  55. return ToDlnaUrl(baseUrl);
  56. }
  57. public string ToDlnaUrl(string baseUrl)
  58. {
  59. if (string.IsNullOrEmpty(baseUrl))
  60. {
  61. throw new ArgumentNullException(baseUrl);
  62. }
  63. string dlnaCommand = BuildDlnaParam(this);
  64. string extension = string.IsNullOrEmpty(Container) ? string.Empty : "." + Container;
  65. baseUrl = baseUrl.TrimEnd('/');
  66. if (MediaType == DlnaProfileType.Audio)
  67. {
  68. return string.Format("{0}/audio/{1}/stream{2}?{3}", baseUrl, ItemId, extension, dlnaCommand);
  69. }
  70. if (StringHelper.EqualsIgnoreCase(Protocol, "hls"))
  71. {
  72. return string.Format("{0}/videos/{1}/master.m3u8?{2}", baseUrl, ItemId, dlnaCommand);
  73. }
  74. return string.Format("{0}/videos/{1}/stream{2}?{3}", baseUrl, ItemId, extension, dlnaCommand);
  75. }
  76. private static string BuildDlnaParam(StreamInfo item)
  77. {
  78. List<string> list = new List<string>
  79. {
  80. item.DeviceProfileId ?? string.Empty,
  81. item.DeviceId ?? string.Empty,
  82. item.MediaSourceId ?? string.Empty,
  83. (item.IsDirectStream).ToString().ToLower(),
  84. item.VideoCodec ?? string.Empty,
  85. item.AudioCodec ?? string.Empty,
  86. item.AudioStreamIndex.HasValue ? StringHelper.ToStringCultureInvariant(item.AudioStreamIndex.Value) : string.Empty,
  87. item.SubtitleStreamIndex.HasValue && item.SubtitleDeliveryMethod != SubtitleDeliveryMethod.External ? StringHelper.ToStringCultureInvariant(item.SubtitleStreamIndex.Value) : string.Empty,
  88. item.VideoBitrate.HasValue ? StringHelper.ToStringCultureInvariant(item.VideoBitrate.Value) : string.Empty,
  89. item.AudioBitrate.HasValue ? StringHelper.ToStringCultureInvariant(item.AudioBitrate.Value) : string.Empty,
  90. item.MaxAudioChannels.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxAudioChannels.Value) : string.Empty,
  91. item.MaxFramerate.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxFramerate.Value) : string.Empty,
  92. item.MaxWidth.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxWidth.Value) : string.Empty,
  93. item.MaxHeight.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxHeight.Value) : string.Empty,
  94. StringHelper.ToStringCultureInvariant(item.StartPositionTicks),
  95. item.VideoLevel.HasValue ? StringHelper.ToStringCultureInvariant(item.VideoLevel.Value) : string.Empty
  96. };
  97. return string.Format("Params={0}", string.Join(";", list.ToArray()));
  98. }
  99. public List<SubtitleStreamInfo> GetExternalSubtitles(string baseUrl)
  100. {
  101. if (string.IsNullOrEmpty(baseUrl))
  102. {
  103. throw new ArgumentNullException(baseUrl);
  104. }
  105. List<SubtitleStreamInfo> list = new List<SubtitleStreamInfo>();
  106. if (SubtitleDeliveryMethod != SubtitleDeliveryMethod.External)
  107. {
  108. return list;
  109. }
  110. if (!SubtitleStreamIndex.HasValue)
  111. {
  112. return list;
  113. }
  114. // HLS will preserve timestamps so we can just grab the full subtitle stream
  115. long startPositionTicks = StringHelper.EqualsIgnoreCase(Protocol, "hls")
  116. ? 0
  117. : StartPositionTicks;
  118. string url = string.Format("{0}/Videos/{1}/{2}/Subtitles/{3}/{4}/Stream.{5}",
  119. baseUrl,
  120. ItemId,
  121. MediaSourceId,
  122. StringHelper.ToStringCultureInvariant(SubtitleStreamIndex.Value),
  123. StringHelper.ToStringCultureInvariant(startPositionTicks),
  124. SubtitleFormat);
  125. foreach (MediaStream stream in MediaSource.MediaStreams)
  126. {
  127. if (stream.Type == MediaStreamType.Subtitle && stream.Index == SubtitleStreamIndex.Value)
  128. {
  129. list.Add(new SubtitleStreamInfo
  130. {
  131. Url = url,
  132. IsForced = stream.IsForced,
  133. Language = stream.Language,
  134. Name = stream.Language ?? "Unknown",
  135. Format = SubtitleFormat
  136. });
  137. }
  138. }
  139. return list;
  140. }
  141. /// <summary>
  142. /// Returns the audio stream that will be used
  143. /// </summary>
  144. public MediaStream TargetAudioStream
  145. {
  146. get
  147. {
  148. if (MediaSource != null)
  149. {
  150. if (AudioStreamIndex.HasValue)
  151. {
  152. foreach (MediaStream i in MediaSource.MediaStreams)
  153. {
  154. if (i.Index == AudioStreamIndex.Value && i.Type == MediaStreamType.Audio)
  155. return i;
  156. }
  157. return null;
  158. }
  159. return MediaSource.DefaultAudioStream;
  160. }
  161. return null;
  162. }
  163. }
  164. /// <summary>
  165. /// Returns the video stream that will be used
  166. /// </summary>
  167. public MediaStream TargetVideoStream
  168. {
  169. get
  170. {
  171. if (MediaSource != null)
  172. {
  173. return MediaSource.VideoStream;
  174. }
  175. return null;
  176. }
  177. }
  178. /// <summary>
  179. /// Predicts the audio sample rate that will be in the output stream
  180. /// </summary>
  181. public int? TargetAudioSampleRate
  182. {
  183. get
  184. {
  185. MediaStream stream = TargetAudioStream;
  186. return stream == null ? null : stream.SampleRate;
  187. }
  188. }
  189. /// <summary>
  190. /// Predicts the audio sample rate that will be in the output stream
  191. /// </summary>
  192. public int? TargetVideoBitDepth
  193. {
  194. get
  195. {
  196. MediaStream stream = TargetVideoStream;
  197. return stream == null || !IsDirectStream ? null : stream.BitDepth;
  198. }
  199. }
  200. /// <summary>
  201. /// Predicts the audio sample rate that will be in the output stream
  202. /// </summary>
  203. public float? TargetFramerate
  204. {
  205. get
  206. {
  207. MediaStream stream = TargetVideoStream;
  208. return MaxFramerate.HasValue && !IsDirectStream
  209. ? MaxFramerate
  210. : stream == null ? null : stream.AverageFrameRate ?? stream.RealFrameRate;
  211. }
  212. }
  213. /// <summary>
  214. /// Predicts the audio sample rate that will be in the output stream
  215. /// </summary>
  216. public double? TargetVideoLevel
  217. {
  218. get
  219. {
  220. MediaStream stream = TargetVideoStream;
  221. return VideoLevel.HasValue && !IsDirectStream
  222. ? VideoLevel
  223. : stream == null ? null : stream.Level;
  224. }
  225. }
  226. /// <summary>
  227. /// Predicts the audio sample rate that will be in the output stream
  228. /// </summary>
  229. public int? TargetPacketLength
  230. {
  231. get
  232. {
  233. MediaStream stream = TargetVideoStream;
  234. return !IsDirectStream
  235. ? null
  236. : stream == null ? null : stream.PacketLength;
  237. }
  238. }
  239. /// <summary>
  240. /// Predicts the audio sample rate that will be in the output stream
  241. /// </summary>
  242. public string TargetVideoProfile
  243. {
  244. get
  245. {
  246. MediaStream stream = TargetVideoStream;
  247. return !string.IsNullOrEmpty(VideoProfile) && !IsDirectStream
  248. ? VideoProfile
  249. : stream == null ? null : stream.Profile;
  250. }
  251. }
  252. /// <summary>
  253. /// Predicts the audio bitrate that will be in the output stream
  254. /// </summary>
  255. public int? TargetAudioBitrate
  256. {
  257. get
  258. {
  259. MediaStream stream = TargetAudioStream;
  260. return AudioBitrate.HasValue && !IsDirectStream
  261. ? AudioBitrate
  262. : stream == null ? null : stream.BitRate;
  263. }
  264. }
  265. /// <summary>
  266. /// Predicts the audio channels that will be in the output stream
  267. /// </summary>
  268. public int? TargetAudioChannels
  269. {
  270. get
  271. {
  272. MediaStream stream = TargetAudioStream;
  273. int? streamChannels = stream == null ? null : stream.Channels;
  274. return MaxAudioChannels.HasValue && !IsDirectStream
  275. ? (streamChannels.HasValue ? Math.Min(MaxAudioChannels.Value, streamChannels.Value) : MaxAudioChannels.Value)
  276. : streamChannels;
  277. }
  278. }
  279. /// <summary>
  280. /// Predicts the audio codec that will be in the output stream
  281. /// </summary>
  282. public string TargetAudioCodec
  283. {
  284. get
  285. {
  286. MediaStream stream = TargetAudioStream;
  287. return IsDirectStream
  288. ? (stream == null ? null : stream.Codec)
  289. : AudioCodec;
  290. }
  291. }
  292. /// <summary>
  293. /// Predicts the audio channels that will be in the output stream
  294. /// </summary>
  295. public long? TargetSize
  296. {
  297. get
  298. {
  299. if (IsDirectStream)
  300. {
  301. return MediaSource.Size;
  302. }
  303. if (RunTimeTicks.HasValue)
  304. {
  305. int? totalBitrate = TargetTotalBitrate;
  306. double totalSeconds = RunTimeTicks.Value;
  307. // Convert to ms
  308. totalSeconds /= 10000;
  309. // Convert to seconds
  310. totalSeconds /= 1000;
  311. return totalBitrate.HasValue ?
  312. Convert.ToInt64(totalBitrate.Value * totalSeconds) :
  313. (long?)null;
  314. }
  315. return null;
  316. }
  317. }
  318. public int? TargetVideoBitrate
  319. {
  320. get
  321. {
  322. MediaStream stream = TargetVideoStream;
  323. return VideoBitrate.HasValue && !IsDirectStream
  324. ? VideoBitrate
  325. : stream == null ? null : stream.BitRate;
  326. }
  327. }
  328. public TransportStreamTimestamp TargetTimestamp
  329. {
  330. get
  331. {
  332. TransportStreamTimestamp defaultValue = StringHelper.EqualsIgnoreCase(Container, "m2ts")
  333. ? TransportStreamTimestamp.Valid
  334. : TransportStreamTimestamp.None;
  335. return !IsDirectStream
  336. ? defaultValue
  337. : MediaSource == null ? defaultValue : MediaSource.Timestamp ?? TransportStreamTimestamp.None;
  338. }
  339. }
  340. public int? TargetTotalBitrate
  341. {
  342. get
  343. {
  344. return (TargetAudioBitrate ?? 0) + (TargetVideoBitrate ?? 0);
  345. }
  346. }
  347. public bool? IsTargetAnamorphic
  348. {
  349. get
  350. {
  351. if (IsDirectStream)
  352. {
  353. return TargetVideoStream == null ? null : TargetVideoStream.IsAnamorphic;
  354. }
  355. return false;
  356. }
  357. }
  358. public int? TargetWidth
  359. {
  360. get
  361. {
  362. MediaStream videoStream = TargetVideoStream;
  363. if (videoStream != null && videoStream.Width.HasValue && videoStream.Height.HasValue)
  364. {
  365. ImageSize size = new ImageSize
  366. {
  367. Width = videoStream.Width.Value,
  368. Height = videoStream.Height.Value
  369. };
  370. double? maxWidth = MaxWidth.HasValue ? (double)MaxWidth.Value : (double?)null;
  371. double? maxHeight = MaxHeight.HasValue ? (double)MaxHeight.Value : (double?)null;
  372. ImageSize newSize = DrawingUtils.Resize(size,
  373. null,
  374. null,
  375. maxWidth,
  376. maxHeight);
  377. return Convert.ToInt32(newSize.Width);
  378. }
  379. return MaxWidth;
  380. }
  381. }
  382. public int? TargetHeight
  383. {
  384. get
  385. {
  386. MediaStream videoStream = TargetVideoStream;
  387. if (videoStream != null && videoStream.Width.HasValue && videoStream.Height.HasValue)
  388. {
  389. ImageSize size = new ImageSize
  390. {
  391. Width = videoStream.Width.Value,
  392. Height = videoStream.Height.Value
  393. };
  394. double? maxWidth = MaxWidth.HasValue ? (double)MaxWidth.Value : (double?)null;
  395. double? maxHeight = MaxHeight.HasValue ? (double)MaxHeight.Value : (double?)null;
  396. ImageSize newSize = DrawingUtils.Resize(size,
  397. null,
  398. null,
  399. maxWidth,
  400. maxHeight);
  401. return Convert.ToInt32(newSize.Height);
  402. }
  403. return MaxHeight;
  404. }
  405. }
  406. }
  407. public enum SubtitleDeliveryMethod
  408. {
  409. /// <summary>
  410. /// The encode
  411. /// </summary>
  412. Encode = 0,
  413. /// <summary>
  414. /// The embed
  415. /// </summary>
  416. Embed = 1,
  417. /// <summary>
  418. /// The external
  419. /// </summary>
  420. External = 2,
  421. /// <summary>
  422. /// The HLS
  423. /// </summary>
  424. Hls = 3
  425. }
  426. public class SubtitleStreamInfo
  427. {
  428. public string Url { get; set; }
  429. public string Language { get; set; }
  430. public string Name { get; set; }
  431. public bool IsForced { get; set; }
  432. public string Format { get; set; }
  433. }
  434. }