produce valid mpeg dash manifest

pull/702/head
Luke Pulverenti 10 years ago
parent daacf3f3db
commit 52776df012

@ -825,6 +825,23 @@ namespace MediaBrowser.Api.Playback
return MediaEncoder.GetInputArgument(inputPath, protocol); 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) private async Task AcquireResources(StreamState state, CancellationTokenSource cancellationTokenSource)
{ {
if (state.VideoType == VideoType.Iso && state.IsoType.HasValue && IsoManager.CanMount(state.MediaPath)) 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)) if (!string.IsNullOrEmpty(streamInfo.Path))
{ {
state.MediaPath = streamInfo.Path; state.MediaPath = streamInfo.Path;
state.InputProtocol = MediaProtocol.File;
await Task.Delay(1500, cancellationTokenSource.Token).ConfigureAwait(false);
} }
else if (!string.IsNullOrEmpty(streamInfo.Url)) else if (!string.IsNullOrEmpty(streamInfo.Url))
{ {
state.MediaPath = 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); AttachMediaStreamInfo(state, streamInfo.MediaStreams, state.VideoRequest, state.RequestedUrl);
checkCodecs = true; checkCodecs = true;
} }
@ -869,16 +885,15 @@ namespace MediaBrowser.Api.Playback
if (!string.IsNullOrEmpty(streamInfo.Path)) if (!string.IsNullOrEmpty(streamInfo.Path))
{ {
state.MediaPath = streamInfo.Path; state.MediaPath = streamInfo.Path;
state.InputProtocol = MediaProtocol.File;
await Task.Delay(1500, cancellationTokenSource.Token).ConfigureAwait(false);
} }
else if (!string.IsNullOrEmpty(streamInfo.Url)) else if (!string.IsNullOrEmpty(streamInfo.Url))
{ {
state.MediaPath = 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); AttachMediaStreamInfo(state, streamInfo.MediaStreams, state.VideoRequest, state.RequestedUrl);
checkCodecs = true; checkCodecs = true;
} }
@ -991,6 +1006,16 @@ namespace MediaBrowser.Api.Playback
await Task.Delay(100, cancellationTokenSource.Token).ConfigureAwait(false); 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; return transcodingJob;
} }
@ -1610,11 +1635,6 @@ namespace MediaBrowser.Api.Playback
{ {
state.InputTimestamp = mediaSource.Timestamp.Value; state.InputTimestamp = mediaSource.Timestamp.Value;
} }
if (video.IsShortcut)
{
state.MediaPath = File.ReadAllText(video.Path);
}
} }
state.RunTimeTicks = mediaSource.RunTimeTicks; state.RunTimeTicks = mediaSource.RunTimeTicks;

@ -1,4 +1,5 @@
using MediaBrowser.Common.IO; using System.Security;
using MediaBrowser.Common.IO;
using MediaBrowser.Common.Net; using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
@ -41,7 +42,7 @@ namespace MediaBrowser.Api.Playback.Hls
/// <value>The segment id.</value> /// <value>The segment id.</value>
public string SegmentId { get; set; } public string SegmentId { get; set; }
} }
public class MpegDashService : BaseHlsService public class MpegDashService : BaseHlsService
{ {
protected INetworkManager NetworkManager { get; private set; } protected INetworkManager NetworkManager { get; private set; }
@ -104,8 +105,9 @@ namespace MediaBrowser.Api.Playback.Hls
var duration = "PT0H02M11.00S"; var duration = "PT0H02M11.00S";
builder.Append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
builder.AppendFormat( builder.AppendFormat(
"<MPD type=\"static\" minBufferTime=\"PT2S\" mediaPresentationDuration=\"{0}\" profiles=\"urn:mpeg:dash:profile:mp2t-simple:2011\" xmlns=\"urn:mpeg:DASH:schema:MPD:2011\" maxSegmentDuration=\"PT{1}S\">", "<MPD xmlns=\"urn:mpeg:dash:schema:mpd:2011\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"urn:mpeg:dash:schema:mpd:2011 http://standards.iso.org/ittf/PubliclyAvailableStandards/MPEG-DASH_schema_files/DASH-MPD.xsd\" minBufferTime=\"PT2.00S\" mediaPresentationDuration=\"{0}\" maxSegmentDuration=\"PT{1}S\" type=\"static\" profiles=\"urn:mpeg:dash:profile:mp2t-simple:2011\">",
duration, duration,
state.SegmentLength.ToString(CultureInfo.InvariantCulture)); state.SegmentLength.ToString(CultureInfo.InvariantCulture));
@ -116,9 +118,13 @@ namespace MediaBrowser.Api.Playback.Hls
builder.Append("<AdaptationSet segmentAlignment=\"true\">"); builder.Append("<AdaptationSet segmentAlignment=\"true\">");
builder.Append("<ContentComponent id=\"1\" contentType=\"video\"/>"); builder.Append("<ContentComponent id=\"1\" contentType=\"video\"/>");
builder.Append("<ContentComponent id=\"2\" contentType=\"audio\" lang=\"eng\"/>");
builder.Append(GetRepresentationOpenElement(state)); var lang = state.AudioStream != null ? state.AudioStream.Language : null;
if (string.IsNullOrWhiteSpace(lang)) lang = "und";
builder.AppendFormat("<ContentComponent id=\"2\" contentType=\"audio\" lang=\"{0}\"/>", lang);
builder.Append(GetRepresentationOpenElement(state, lang));
AppendSegmentList(state, builder); AppendSegmentList(state, builder);
@ -131,10 +137,97 @@ namespace MediaBrowser.Api.Playback.Hls
return builder.ToString(); return builder.ToString();
} }
private string GetRepresentationOpenElement(StreamState state) private string GetRepresentationOpenElement(StreamState state, string language)
{ {
return var codecs = GetVideoCodecDescriptor(state) + "," + GetAudioCodecDescriptor(state);
"<Representation id=\"1\" mimeType=\"video/mp2t\" codecs=\"avc1.640028,mp4a.40.02\" width=\"1280\" height=\"1024\" sampleRate=\"44100\" numChannels=\"2\" lang=\"und\" startWithSAP=\"1\" bandwidth=\"317599\">";
var xml = "<Representation id=\"1\" mimeType=\"video/mp2t\" startWithSAP=\"1\" codecs=\"" + codecs + "\"";
if (state.OutputWidth.HasValue)
{
xml += " width=\"" + state.OutputWidth.Value.ToString(UsCulture) + "\"";
}
if (state.OutputHeight.HasValue)
{
xml += " height=\"" + state.OutputHeight.Value.ToString(UsCulture) + "\"";
}
if (state.OutputAudioSampleRate.HasValue)
{
xml += " sampleRate=\"" + state.OutputAudioSampleRate.Value.ToString(UsCulture) + "\"";
}
if (state.TotalOutputBitrate.HasValue)
{
xml += " bandwidth=\"" + state.TotalOutputBitrate.Value.ToString(UsCulture) + "\"";
}
xml += ">";
return xml;
}
private string GetVideoCodecDescriptor(StreamState state)
{
// https://developer.apple.com/library/ios/documentation/networkinginternet/conceptual/streamingmediaguide/FrequentlyAskedQuestions/FrequentlyAskedQuestions.html
var level = state.TargetVideoLevel ?? 0;
var profile = state.TargetVideoProfile ?? string.Empty;
if (profile.IndexOf("high", StringComparison.OrdinalIgnoreCase) != -1)
{
if (level >= 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) public object Get(GetDashSegment request)
@ -147,7 +240,7 @@ namespace MediaBrowser.Api.Playback.Hls
var seconds = TimeSpan.FromTicks(state.RunTimeTicks ?? 0).TotalSeconds; var seconds = TimeSpan.FromTicks(state.RunTimeTicks ?? 0).TotalSeconds;
builder.Append("<SegmentList timescale=\"1000\" duration=\"10000\">"); builder.Append("<SegmentList timescale=\"1000\" duration=\"10000\">");
var queryStringIndex = Request.RawUrl.IndexOf('?'); var queryStringIndex = Request.RawUrl.IndexOf('?');
var queryString = queryStringIndex == -1 ? string.Empty : Request.RawUrl.Substring(queryStringIndex); var queryString = queryStringIndex == -1 ? string.Empty : Request.RawUrl.Substring(queryStringIndex);
@ -155,7 +248,7 @@ namespace MediaBrowser.Api.Playback.Hls
while (seconds > 0) while (seconds > 0)
{ {
builder.AppendFormat("<SegmentURL media=\"{0}.ts{1}\"/>", index.ToString(UsCulture), queryString); builder.AppendFormat("<SegmentURL media=\"{0}.ts{1}\"/>", index.ToString(UsCulture), SecurityElement.Escape(queryString));
seconds -= state.SegmentLength; seconds -= state.SegmentLength;
index++; index++;

Loading…
Cancel
Save