public bool Parse() { try { #if DEBUG Logger.Log("Begin parsing DASH manifest"); #endif ParseMPDElement(); UpdateBaseUrl(); var periods = document.DocumentElement.ChildNodes.Where(node => node.NodeName.Equals("Period")); #if DEBUG Logger.Log(periods.Count().ToString() + " Periods found"); #endif foreach (var period in periods) { Period dashPeriod = new Period(); periodStart = TimeSpan.FromSeconds(0); ParsePeriodElement(period, ref dashPeriod); var adaptationSets = period.ChildNodes.Where(node => node.NodeName.Equals("AdaptationSet")); #if DEBUG Logger.Log(adaptationSets.Count().ToString() + " Adaptation Sets found"); #endif foreach (var adaptationSet in adaptationSets) { MediaStream stream = new MediaStream(); if (adaptationSet.Attributes.GetNamedItem("mimeType") != null) { stream.MimeType = adaptationSet.Attributes.GetNamedItem("mimeType").InnerText; } if (adaptationSet.Attributes.GetNamedItem("lang") != null) { stream.Language = adaptationSet.Attributes.GetNamedItem("lang").InnerText.Replace(" ", string.Empty); } var representations = adaptationSet.ChildNodes.Where(node => node.NodeName.Equals("Representation")); //We are only grabbing one Representation in an Adaptation Set (the lowest one, just to be safe) //(No support for adaptive streaming) IXmlNode representation = GetLowestBitrateRepresentation(representations); #if DEBUG Logger.Log(representations.Count().ToString() + " Representations in " + stream.MimeType + " Adaptation Set with bitrate " + uint.Parse(representation.Attributes.GetNamedItem("bandwidth").InnerText)); #endif //Parse Representation Element uint bitrate; string repID = string.Empty; ParseRepresentationElement(representation, out bitrate, out repID); var segmentTemplateQuery = adaptationSet.ChildNodes.Where(node => node.NodeName.Equals("SegmentTemplate")); var segmentListQuery = adaptationSet.ChildNodes.Where(node => node.NodeName.Equals("SegmentList")); var segmentBaseQuery = adaptationSet.ChildNodes.Where(node => node.NodeName.Equals("SegmentBase")); if (segmentTemplateQuery.Count() != 0) { stream.SegmentInfoType = MediaStream.SegmentInformationType.Template; //Parse Segment Template Element UInt64 timescale = 1; string initializationFormat = string.Empty; string mediaFormat = string.Empty; UInt64 segmentDuration = 0; uint startNumber = 1; UInt64 presentationTimeOffset = 0; var segmentTemplate = segmentTemplateQuery.First(); ParseSegmentTemplateElement(segmentTemplate, ref timescale, ref initializationFormat, ref mediaFormat, ref segmentDuration, ref startNumber, ref presentationTimeOffset); DeterminePeriodStart(presentationTimeOffset, period, ref dashPeriod); string initializationUrl; //$Number$ and $Time$ will never be present in the initialization URL ExpandDASHUrlSegmentTemplate(initializationFormat, manifest.BaseUrl, bitrate, repID, 0, 0, out initializationUrl); stream.InitSegmentUrl = initializationUrl; var segmentTimelineQuery = segmentTemplate.ChildNodes.Where(node => node.NodeName.Equals("SegmentTimeline")); MediaSegmentInformation segmentInfo = new MediaSegmentInformation(); segmentInfo.Timescale = timescale; segmentInfo.RepresentationID = repID; segmentInfo.Bitrate = bitrate; //No timeline present. //Instead of populating the segments in the stream, we will give just enough information to produce //the information to construct media segment urls on the fly in the SourceBufferManager if (segmentTimelineQuery.Count() == 0) { #if DEBUG Logger.Log("Making number base segment info from SegmentTemplate"); #endif stream.SegmentTemplateBase = MediaStream.SegmentTemplateIdentifier.Number; segmentInfo.Duration = segmentDuration; segmentInfo.UrlTemplate = mediaFormat; UInt64 segmentNumber = startNumber; if (manifest.IsLive) { var hnsFragmentDuration = ((segmentDuration * 10000000) / timescale); CalculateNumberIdentifierForLive(hnsFragmentDuration, ref segmentNumber); } segmentInfo.StartNumber = segmentNumber; segmentInfo.StartTimestamp = segmentDuration * (segmentNumber - 1); stream.CanGenerateSegmentsDynamically = true; } else { stream.SegmentTemplateBase = MediaStream.SegmentTemplateIdentifier.Time; MakeTimelineInfo(segmentTimelineQuery.First(), bitrate, timescale, repID, startNumber, presentationTimeOffset, mediaFormat, ref stream); } stream.SegmentInformation = segmentInfo; } else if (segmentListQuery.Count() != 0) { #if DEBUG Logger.Log("Manifest parsing error: " + "Does not support a SegmentList implementation"); #endif stream.SegmentInfoType = MediaStream.SegmentInformationType.List; } else { #if DEBUG Logger.Log("Manifest parsing error: " + "Does not support a SegmentBase or BaseUrl implementation"); #endif stream.SegmentInfoType = MediaStream.SegmentInformationType.Base; } dashPeriod.AddStream(stream); } // each adaptation set manifest.AddPeriod(dashPeriod); }// each period manifest.SetFirstSelectedPeriod(); } catch (Exception e) { #if DEBUG Logger.Log("Manifest parsing error " + Logger.Display(e)); #endif return(false); } return(true); }