From 52776df0129f73f7d0f87e9c51629241c5c4a7de Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Tue, 14 Oct 2014 20:04:44 -0400 Subject: [PATCH] produce valid mpeg dash manifest --- .../Playback/BaseStreamingService.cs | 46 +++++-- .../Playback/Hls/MpegDashService.cs | 113 ++++++++++++++++-- 2 files changed, 136 insertions(+), 23 deletions(-) diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index fa6f88cc41..c188376fe6 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -825,6 +825,23 @@ namespace MediaBrowser.Api.Playback return MediaEncoder.GetInputArgument(inputPath, protocol); } + private MediaProtocol GetProtocol(string path) + { + if (path.StartsWith("Http", StringComparison.OrdinalIgnoreCase)) + { + return MediaProtocol.Http; + } + if (path.StartsWith("Rtsp", StringComparison.OrdinalIgnoreCase)) + { + return MediaProtocol.Rtsp; + } + if (path.StartsWith("Rtmp", StringComparison.OrdinalIgnoreCase)) + { + return MediaProtocol.Rtmp; + } + return MediaProtocol.File; + } + private async Task AcquireResources(StreamState state, CancellationTokenSource cancellationTokenSource) { if (state.VideoType == VideoType.Iso && state.IsoType.HasValue && IsoManager.CanMount(state.MediaPath)) @@ -845,16 +862,15 @@ namespace MediaBrowser.Api.Playback if (!string.IsNullOrEmpty(streamInfo.Path)) { state.MediaPath = streamInfo.Path; - state.InputProtocol = MediaProtocol.File; - - await Task.Delay(1500, cancellationTokenSource.Token).ConfigureAwait(false); } else if (!string.IsNullOrEmpty(streamInfo.Url)) { state.MediaPath = streamInfo.Url; - state.InputProtocol = MediaProtocol.Http; } + await Task.Delay(1500, cancellationTokenSource.Token).ConfigureAwait(false); + + state.InputProtocol = GetProtocol(state.MediaPath); AttachMediaStreamInfo(state, streamInfo.MediaStreams, state.VideoRequest, state.RequestedUrl); checkCodecs = true; } @@ -869,16 +885,15 @@ namespace MediaBrowser.Api.Playback if (!string.IsNullOrEmpty(streamInfo.Path)) { state.MediaPath = streamInfo.Path; - state.InputProtocol = MediaProtocol.File; - - await Task.Delay(1500, cancellationTokenSource.Token).ConfigureAwait(false); } else if (!string.IsNullOrEmpty(streamInfo.Url)) { state.MediaPath = streamInfo.Url; - state.InputProtocol = MediaProtocol.Http; } + await Task.Delay(1500, cancellationTokenSource.Token).ConfigureAwait(false); + + state.InputProtocol = GetProtocol(state.MediaPath); AttachMediaStreamInfo(state, streamInfo.MediaStreams, state.VideoRequest, state.RequestedUrl); checkCodecs = true; } @@ -991,6 +1006,16 @@ namespace MediaBrowser.Api.Playback await Task.Delay(100, cancellationTokenSource.Token).ConfigureAwait(false); } + if (state.IsInputVideo && transcodingJob.Type == Api.TranscodingJobType.Progressive) + { + await Task.Delay(1000, cancellationTokenSource.Token).ConfigureAwait(false); + + if (state.ReadInputAtNativeFramerate) + { + await Task.Delay(1000, cancellationTokenSource.Token).ConfigureAwait(false); + } + } + return transcodingJob; } @@ -1610,11 +1635,6 @@ namespace MediaBrowser.Api.Playback { state.InputTimestamp = mediaSource.Timestamp.Value; } - - if (video.IsShortcut) - { - state.MediaPath = File.ReadAllText(video.Path); - } } state.RunTimeTicks = mediaSource.RunTimeTicks; diff --git a/MediaBrowser.Api/Playback/Hls/MpegDashService.cs b/MediaBrowser.Api/Playback/Hls/MpegDashService.cs index caddbd9a13..1150220158 100644 --- a/MediaBrowser.Api/Playback/Hls/MpegDashService.cs +++ b/MediaBrowser.Api/Playback/Hls/MpegDashService.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Common.IO; +using System.Security; +using MediaBrowser.Common.IO; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Configuration; @@ -41,7 +42,7 @@ namespace MediaBrowser.Api.Playback.Hls /// The segment id. public string SegmentId { get; set; } } - + public class MpegDashService : BaseHlsService { protected INetworkManager NetworkManager { get; private set; } @@ -104,8 +105,9 @@ namespace MediaBrowser.Api.Playback.Hls var duration = "PT0H02M11.00S"; + builder.Append(""); builder.AppendFormat( - "", + "", duration, state.SegmentLength.ToString(CultureInfo.InvariantCulture)); @@ -116,9 +118,13 @@ namespace MediaBrowser.Api.Playback.Hls builder.Append(""); builder.Append(""); - builder.Append(""); - builder.Append(GetRepresentationOpenElement(state)); + var lang = state.AudioStream != null ? state.AudioStream.Language : null; + if (string.IsNullOrWhiteSpace(lang)) lang = "und"; + + builder.AppendFormat("", lang); + + builder.Append(GetRepresentationOpenElement(state, lang)); AppendSegmentList(state, builder); @@ -131,10 +137,97 @@ namespace MediaBrowser.Api.Playback.Hls return builder.ToString(); } - private string GetRepresentationOpenElement(StreamState state) + private string GetRepresentationOpenElement(StreamState state, string language) { - return - ""; + var codecs = GetVideoCodecDescriptor(state) + "," + GetAudioCodecDescriptor(state); + + var xml = "= 4.1) + { + return "avc1.640028"; + } + + if (level >= 4) + { + return "avc1.640028"; + } + + return "avc1.64001f"; + } + + if (profile.IndexOf("main", StringComparison.OrdinalIgnoreCase) != -1) + { + if (level >= 4) + { + return "avc1.4d0028"; + } + + if (level >= 3.1) + { + return "avc1.4d001f"; + } + + return "avc1.4d001e"; + } + + if (level >= 3.1) + { + return "avc1.42001f"; + } + + return "avc1.42001e"; + } + + private string GetAudioCodecDescriptor(StreamState state) + { + // https://developer.apple.com/library/ios/documentation/networkinginternet/conceptual/streamingmediaguide/FrequentlyAskedQuestions/FrequentlyAskedQuestions.html + + if (string.Equals(state.OutputAudioCodec, "mp3", StringComparison.OrdinalIgnoreCase)) + { + return "mp4a.40.34"; + } + + // AAC 5ch + if (state.OutputAudioChannels.HasValue && state.OutputAudioChannels.Value >= 5) + { + return "mp4a.40.5"; + } + + // AAC 2ch + return "mp4a.40.2"; } public object Get(GetDashSegment request) @@ -147,7 +240,7 @@ namespace MediaBrowser.Api.Playback.Hls var seconds = TimeSpan.FromTicks(state.RunTimeTicks ?? 0).TotalSeconds; builder.Append(""); - + var queryStringIndex = Request.RawUrl.IndexOf('?'); var queryString = queryStringIndex == -1 ? string.Empty : Request.RawUrl.Substring(queryStringIndex); @@ -155,7 +248,7 @@ namespace MediaBrowser.Api.Playback.Hls while (seconds > 0) { - builder.AppendFormat("", index.ToString(UsCulture), queryString); + builder.AppendFormat("", index.ToString(UsCulture), SecurityElement.Escape(queryString)); seconds -= state.SegmentLength; index++;