using System; using System.Globalization; using System.Security; using System.Text; namespace MediaBrowser.Api.Playback.Dash { public class ManifestBuilder { protected readonly CultureInfo UsCulture = new CultureInfo("en-US"); public string GetManifestText(StreamState state, string playlistUrl) { var builder = new StringBuilder(); var time = TimeSpan.FromTicks(state.RunTimeTicks.Value); var duration = "PT" + time.Hours.ToString("00", UsCulture) + "H" + time.Minutes.ToString("00", UsCulture) + "M" + time.Seconds.ToString("00", UsCulture) + ".00S"; builder.Append(""); builder.AppendFormat( "", duration); builder.Append(""); builder.Append(""); builder.Append(""); builder.Append(GetVideoAdaptationSet(state, playlistUrl)); builder.Append(GetAudioAdaptationSet(state, playlistUrl)); builder.Append(""); builder.Append(""); return builder.ToString(); } private string GetVideoAdaptationSet(StreamState state, string playlistUrl) { var builder = new StringBuilder(); builder.Append(""); builder.Append(GetVideoRepresentationOpenElement(state)); AppendSegmentList(state, builder, "0", playlistUrl); builder.Append(""); builder.Append(""); return builder.ToString(); } private string GetAudioAdaptationSet(StreamState state, string playlistUrl) { var builder = new StringBuilder(); builder.Append(""); builder.Append(GetAudioRepresentationOpenElement(state)); builder.Append(""); AppendSegmentList(state, builder, "1", playlistUrl); builder.Append(""); builder.Append(""); return builder.ToString(); } private string GetVideoRepresentationOpenElement(StreamState state) { var codecs = GetVideoCodecDescriptor(state); var mime = "video/mp4"; 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.42E01E"; } 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"; } private void AppendSegmentList(StreamState state, StringBuilder builder, string type, string playlistUrl) { var extension = ".m4s"; var seconds = TimeSpan.FromTicks(state.RunTimeTicks ?? 0).TotalSeconds; var queryStringIndex = playlistUrl.IndexOf('?'); var queryString = queryStringIndex == -1 ? string.Empty : playlistUrl.Substring(queryStringIndex); var index = 0; var duration = 1000000 * state.SegmentLength; builder.AppendFormat("", duration.ToString(CultureInfo.InvariantCulture)); while (seconds > 0) { var filename = index == 0 ? "init" : (index - 1).ToString(UsCulture); var segmentUrl = string.Format("dash/{3}/{0}{1}{2}", filename, extension, SecurityElement.Escape(queryString), type); if (index == 0) { builder.AppendFormat("", segmentUrl); } else { builder.AppendFormat("", segmentUrl); } seconds -= state.SegmentLength; index++; } builder.Append(""); } } }