produce valid mpeg dash manifest
This commit is contained in:
parent
daacf3f3db
commit
52776df012
|
@ -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;
|
||||
|
|
|
@ -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
|
|||
/// <value>The segment id.</value>
|
||||
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("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
|
||||
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,
|
||||
state.SegmentLength.ToString(CultureInfo.InvariantCulture));
|
||||
|
||||
|
@ -116,9 +118,13 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||
builder.Append("<AdaptationSet segmentAlignment=\"true\">");
|
||||
|
||||
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);
|
||||
|
||||
|
@ -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
|
||||
"<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 codecs = GetVideoCodecDescriptor(state) + "," + GetAudioCodecDescriptor(state);
|
||||
|
||||
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)
|
||||
|
@ -147,7 +240,7 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||
var seconds = TimeSpan.FromTicks(state.RunTimeTicks ?? 0).TotalSeconds;
|
||||
|
||||
builder.Append("<SegmentList timescale=\"1000\" duration=\"10000\">");
|
||||
|
||||
|
||||
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("<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;
|
||||
index++;
|
||||
|
|
Loading…
Reference in New Issue
Block a user