private static AdaptationSet Find(Period p, string language, MediaType type, MediaRole role = MediaRole.Main) { AdaptationSet missingRole = null; foreach (var set in p.Sets) { if (set.Type.Value != type) { continue; } if (language != "und" && set.Lang != language) { continue; } if (set.HasRole(role)) { return(set); } if (set.Roles.Length == 0) { missingRole = set; } } return(missingRole); }
private void GetAvailableStreams(IEnumerable <AdaptationSet> media, AdaptationSet defaultMedia) { // Not perfect algorithm. // check if default media has many representations. if yes, return as available streams // list of default media representation + representations from any media from the same group // if no, return all available medias // TODO(p.galiszewski): add support for: SupplementalProperty schemeIdUri="urn:mpeg:dash:adaptation-set-switching:2016" if (defaultMedia.Representations.Length > 1) { if (defaultMedia.Group.HasValue) { availableStreams = media.Where(o => o.Group == defaultMedia.Group) .SelectMany(o => o.Representations, (parent, repr) => new DashStream(parent, repr)) .OrderByDescending(o => o.Representation.Bandwidth) .ToList(); } else { availableStreams = defaultMedia.Representations.Select(o => new DashStream(defaultMedia, o)) .OrderByDescending(o => o.Representation.Bandwidth) .ToList(); } } else { availableStreams = media.Select(o => new DashStream(o, o.Representations.First())) .OrderByDescending(o => o.Representation.Bandwidth) .ToList(); } }
private static AdaptationSet Find(MpdParser.Period p, string language, MediaType type, MediaRole role = MediaRole.Main) { AdaptationSet res = null; for (int i = 0; i < p.Sets.Length; i++) { if (p.Sets[i].Type.Value != type) { continue; } if (language != null) { if (p.Sets[i].Lang != language) { continue; } } if (p.Sets[i].HasRole(role)) { res = p.Sets[i]; break; } if (p.Sets[i].Roles.Length == 0) { res = p.Sets[i]; break; } } return(res); }
/// <summary> /// Performs on-disk post processing of the generated MPD file. /// Subtitles are added, useless tags removed, etc. /// </summary> private MPD PostProcessMpdFile(string filepath, IEnumerable <SubtitleStreamCommand> subtitles) { MPD.TryLoadFromFile(filepath, out MPD mpd, out Exception ex); mpd.ProgramInformation = new ProgramInformation() { Title = $"DEnc", MoreInformationURL = "https://github.com/bloomtom/DEnc" }; // Get the highest used representation ID so we can increment it for new IDs. int.TryParse(mpd.Period.Max(x => x.AdaptationSet.Max(y => y.Representation.Max(z => z.Id))), out int representationId); representationId++; foreach (var period in mpd.Period) { // Add subtitles to this period. foreach (var sub in subtitles) { AdaptationSet subtitleSet = GenerateSubtitleAdaptationSet(representationId, sub, out int nextRepresentationId); if (subtitleSet != null) { period.AdaptationSet.Add(subtitleSet); representationId = nextRepresentationId; } } } mpd.SaveToFile(filepath); return(mpd); }
private void ParseDrms(AdaptationSet newMedia) { foreach (var descriptor in newMedia.ContentProtections) { var schemeIdUri = descriptor.SchemeIdUri; if (CencUtils.SupportsSchemeIdUri(schemeIdUri)) { ParseCencScheme(descriptor, schemeIdUri); } else if (string.Equals(schemeIdUri, "http://youtube.com/drm/2012/10/10", StringComparison.CurrentCultureIgnoreCase)) { ParseYoutubeScheme(descriptor); } } }
private static AdaptationSet GetDefaultMedia(ICollection <AdaptationSet> medias) { AdaptationSet media = null; if (medias.Count == 1) { media = medias.First(); } if (media == null) { media = medias.FirstOrDefault(o => o.HasRole(MediaRole.Main)); } if (media == null) { media = medias.FirstOrDefault(o => o.Lang == "en"); } return(media ?? medias.FirstOrDefault()); }
public async Task DEBUG_MpdParser() { LoggerBase CreateLogger(string channel, LogLevel level) => new DummyLogger(channel, level); LoggerManager.Configure(CreateLogger); //string url = "http://profficialsite.origin.mediaservices.windows.net/c51358ea-9a5e-4322-8951-897d640fdfd7/tearsofsteel_4k.ism/manifest(format=mpd-time-csf)"; //string url = "http://dash.edgesuite.net/envivio/dashpr/clear/Manifest.mpd"; string url = null; WebClient wc = new WebClient(); String xml; Document doc; try { xml = wc.DownloadString(url); doc = await Document.FromText(xml, url); foreach (var period in doc.Periods) { AdaptationSet audio = Find(period, "en", MediaType.Audio) ?? Find(period, "und", MediaType.Audio); AdaptationSet video = Find(period, "en", MediaType.Video) ?? Find(period, "und", MediaType.Video); if (audio != null && video != null) { return; } } } catch (Exception ex) { return; } return; }
public DashStream(AdaptationSet media, Representation representation) { Media = media; Representation = representation; }
private static Manifest LoadManifest(string manifestString, IFeedbackSink feedback) { var ns = new XmlNamespaceManager(new NameTable()); ns.AddNamespace("mpd", MpdNamespace); var xml = XDocument.Load(new StringReader(manifestString)); // Load <MPD>. var manifest = new Manifest { Document = xml, Namespaces = ns, AvailabilityStartTime = xml.Root.GetAttributeAsDateTimeOffset("availabilityStartTime"), PublishTime = xml.Root.GetAttributeAsDateTimeOffset("publishTime"), PlaybackWindowLength = xml.Root.GetAttributeAsTimeSpan("timeShiftBufferDepth"), ManifestRefreshInterval = xml.Root.GetAttributeAsTimeSpan("minimumUpdatePeriod") }; if (xml.Root.Attribute("type")?.Value != "dynamic") { throw new NotSupportedException("MPD@type must be 'dynamic'"); } if (manifest.PublishTime.Year < 2018) { throw new NotSupportedException("MPD@availabilityStartTime must be at least in 2018 because this validator does not yet implement leap second processing."); } foreach (var clockSyncElement in xml.Root.Elements(UtcTimingName)) { switch (clockSyncElement.Attribute("schemeIdUri").Value) { case "urn:mpeg:dash:utc:http-iso:2014": manifest.TimeSources.Add(new HttpIsoTimeSource(new Uri(clockSyncElement.Attribute("value").Value, UriKind.Absolute))); break; case "urn:mpeg:dash:utc:http-head:2014": manifest.TimeSources.Add(new HttpHeadTimeSource(new Uri(clockSyncElement.Attribute("value").Value, UriKind.Absolute))); break; case "urn:mpeg:dash:utc:direct:2014": manifest.TimeSources.Add(new DirectTimeSource(clockSyncElement.Attribute("value").Value)); break; default: feedback.WillSkipSomeData("Ignoring unsupported clock synchromization method: " + clockSyncElement.Attribute("schemeIdUri").Value); break; } } // Load <Period>. foreach (var periodElement in xml.Root.Elements(PeriodName)) { var period = new Period { Element = periodElement, Manifest = manifest, Id = periodElement.Attribute("id")?.Value, StartOffsetFromAst = periodElement.GetAttributeAsTimeSpan("start") }; manifest.Periods.Add(period); } // Calculate period durations. for (var i = 0; i < manifest.Periods.Count; i++) { if (i == manifest.Periods.Count - 1) { // Last period may have explicit duration in manifest. Otherwise infinite duration. if (manifest.Periods[i].Element.Attribute("duration") != null) { manifest.Periods[i].Duration = manifest.Periods[i].Element.GetAttributeAsTimeSpan("duration"); } } else { // If it is not the last, we always calculate (even if explicit duration is set). manifest.Periods[i].Duration = manifest.Periods[i + 1].Start - manifest.Periods[i].Start; } } // Load <AdaptationSet>. foreach (var period in manifest.Periods) { foreach (var setElement in period.Element.Elements(AdaptationSetName)) { var set = new AdaptationSet { Element = setElement, Period = period, MimeType = setElement.Attribute("mimeType")?.Value, AlignedSegments = setElement.Attribute("segmentAlignment")?.Value == "true" }; var templateElement = setElement.Element(SegmentTemplateName); if (templateElement != null) { set.SegmentTemplate = LoadSegmentTemplate(templateElement); set.SegmentTemplate.AdaptationSet = set; } period.AdaptationSets.Add(set); } } // Load <Representation>. foreach (var set in manifest.Periods.SelectMany(p => p.AdaptationSets)) { foreach (var repElement in set.Element.Elements(RepresentationName)) { var rep = new Representation { Element = repElement, AdaptationSet = set, Id = repElement.Attribute("id")?.Value }; var templateElement = repElement.Element(SegmentTemplateName); if (templateElement != null) { if (set.SegmentTemplate != null) { throw new NotSupportedException("This validator only supports validating manifests where SegmentTemplate is under AdaptationSet or Representation but not both together for the same Representation."); } rep.SegmentTemplate = LoadSegmentTemplate(templateElement); rep.SegmentTemplate.Representation = rep; } else { if (set.SegmentTemplate == null) { throw new NotSupportedException("This validator requires a SegmentTemplate under one of the following: AdaptationSet or Representation."); } } set.Representations.Add(rep); } } return(manifest); }