//@"\$(RepresentationID|Time|Time(%0([\d]+)([dx])?)?|Number|Number(%0([\d]+)([dx])?)?|Bandwidth)\$"); public static IList <IList <(Representation Video, Representation Audio)> > Parse( string[] manifestLines, string playlistUrl) { using var memstream = new MemoryStream(Encoding.UTF8.GetBytes(string.Join("\n", manifestLines))); var xmldoc = new XmlDocument(); xmldoc.Load(new XmlTextReader(memstream) { Namespaces = false }); if (xmldoc.DocumentElement?.Name != "MPD") { throw new Exception("Missing MPD start tag: " + xmldoc.DocumentElement.Name); } if (xmldoc.DocumentElement.Attributes["type"]?.Value == "dynamic") { throw new Exception("Manifest type dynamic is not supported"); } if (xmldoc.DocumentElement.SelectSingleNode("descendant::ContentProtection") != null) { throw new Exception("Encrypted manifest"); } var mediaList = new List <IList <(Representation Video, Representation Audio)> >(); var mediaPresentationDuration = DashUtil.ParseXsDuration(xmldoc.DocumentElement.Attributes["mediaPresentationDuration"]?.Value ?? "0"); var baseUrl = new Uri(playlistUrl); var baseUrlNodeRoot = xmldoc.DocumentElement.SelectSingleNode("child::BaseURL"); if (baseUrlNodeRoot != null) { baseUrl = UrlResolver.Resolve(baseUrl, baseUrlNodeRoot.InnerText); } var periods = xmldoc.DocumentElement.SelectNodes("child::Period"); if (periods == null || periods.Count < 1) { throw new Exception("No period found!"); } if (periods.Count > 1) { var periodDurations = CalculatePeriodDurationsIfMissing(periods, mediaPresentationDuration); for (var i = 0; i < periods.Count; i++) { var period = periods[i]; mediaList.Add(ParsePeriod(period, baseUrl, periodDurations[i])); } } else if (periods.Count == 1) { mediaList.Add(ParsePeriod(periods[0], baseUrl, mediaPresentationDuration)); } return(mediaList); }
public static IList <long> CalculatePeriodDurationsIfMissing(XmlNodeList periods, long mediaPresentationDuration) { var stack = new Stack <XmlNode>(); foreach (XmlNode node in periods) { stack.Push(node); } var list = new List <long>(periods.Count); var last = mediaPresentationDuration; var count = stack.Count; for (int i = 0; i < count; i++) { var node = stack.Pop(); var sduration = node.Attributes["duration"]?.Value; var sstart = node.Attributes["start"]?.Value; if (sstart == null && sduration == null) { throw new Exception("Both period start and duration is missing"); } if (sduration != null) { var duration = DashUtil.ParseXsDuration(sduration); list.Add(duration); last = mediaPresentationDuration - duration; continue; } if (sstart == null && i == stack.Count - 1) { sstart = "PT0S"; } var start = DashUtil.ParseXsDuration(sstart); list.Add(last - start); last = start; } list.Reverse(); return(list); }