internal void AddStream(MediaStream stream) { m_streams.Add(stream); }
internal LiveSourceBufferManager(string mimeType, MediaStream stream) : base(mimeType, stream) { isFirstSegment = true; }
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 + 10; 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; }
private void MakeTimelineInfo(IXmlNode segmentTimeline, uint bitrate, ulong timescale, string repID, uint startNumber, ulong presentationTimeOffset, string mediaFormat, ref MediaStream stream) { var timeline = segmentTimeline.ChildNodes.Where(x => x.NodeName == "S"); uint segmentNumber = startNumber; ulong time = 0; //Set time equal to the @t attribute of first element, otherwise it is 0. //This is to replace the value of $Time$ in the media segment url var tAttribute = timeline.First().Attributes.GetNamedItem("t"); if (tAttribute != null) { time = ulong.Parse(tAttribute.InnerText); } foreach (var t in timeline) { ulong duration = ulong.Parse(t.Attributes.GetNamedItem("d").InnerText); var repeatAttribute = t.Attributes.GetNamedItem("r"); long repeats = 0; if (repeatAttribute != null) { repeats = long.Parse(repeatAttribute.InnerText); } for (int i = 0; i < repeats + 1; i++) { MediaSegment segment = new MediaSegment(); segment.Duration = duration; segment.Timestamp = time - presentationTimeOffset; segment.Number = segmentNumber; //Construct the mediaURL string mediaSegmentUrl; ExpandDASHUrlSegmentTemplate(mediaFormat, manifest.BaseUrl, bitrate, repID, segmentNumber, time, out mediaSegmentUrl); segment.SegmentUrl = mediaSegmentUrl; stream.PushBackSegment(segment); time += duration; segmentNumber++; } } double msStreamDuration = (double)time * (1000) / timescale; #if DEBUG Logger.Log("Found " + stream.Segments.Count() + " segments - Duration: " + msStreamDuration.ToString() + " miliseconds"); #endif if (msStreamDuration > manifest.MediaPresentationDuration.TotalMilliseconds) { manifest.MediaPresentationDuration = TimeSpan.FromMilliseconds(msStreamDuration); } }
private void MakeNumberBaseSegmentInfo(uint bitrate, UInt64 timescale, UInt64 segmentDuration, string repID, uint startNumber, string mediaFormat, ref MediaStream stream) { var hnsPresentationDuration = (Convert.ToUInt64(manifest.MediaPresentationDuration.TotalMilliseconds) * Convert.ToUInt64(10000)); var hnsFragmentDuration = ((segmentDuration * 10000000) / timescale); UInt64 count = (uint)(hnsPresentationDuration / hnsFragmentDuration); UInt64 remain = (uint)(hnsPresentationDuration - (hnsFragmentDuration * count)); if (manifest.IsLive && count == 0) { if (manifest.TimeShiftBufferDepth.TotalSeconds > 0 && hnsFragmentDuration > 0) { var hnsTimeShiftBufferDepth = (ulong)(manifest.TimeShiftBufferDepth.TotalSeconds * 10000000); count = hnsTimeShiftBufferDepth / hnsFragmentDuration; } else { count = LiveSegmentCount; } } UInt64 time = 0; UInt64 segmentNumber = startNumber; if (manifest.IsLive) { CalculateNumberIdentifierForLive(hnsFragmentDuration, ref segmentNumber); } //Since we calculated the latest segment number, we will add to the front of the list for (UInt64 i = 0; i < count; i++) { MediaSegment segment = new MediaSegment(); segment.Timestamp = time; segment.Duration = segmentDuration; segment.Number = segmentNumber; //Construct the mediaURL string mediaSegmentUrl; ExpandDASHUrlSegmentTemplate(mediaFormat, manifest.BaseUrl, bitrate, repID, segmentNumber, time, out mediaSegmentUrl); segment.SegmentUrl = mediaSegmentUrl; stream.PushBackSegment(segment); segmentNumber++; time += segmentDuration; } if (remain > 0) { MediaSegment segment = new MediaSegment(); segment.Timestamp = time; segment.Duration = remain / timescale; segment.Number = segmentNumber; string mediaSegmentUrl; ExpandDASHUrlSegmentTemplate(mediaFormat, manifest.BaseUrl, bitrate, repID, segmentNumber, time, out mediaSegmentUrl); segment.SegmentUrl = mediaSegmentUrl; stream.PushBackSegment(segment); #if DEBUG Logger.Log("Adding final segment to stream with MimeType:" + stream.MimeType + ". Segment{ Timestamp: " + time + "Duration: " + segmentDuration + "Number: " + startNumber + "}" + "\nURL: " + segment.SegmentUrl); #endif time += segmentDuration; } }
protected TimeSpan MaxBuffering = TimeSpan.FromSeconds(5.0); // seconds internal SourceBufferManager(string mimeType, MediaStream stream) { this.mimeType = mimeType; this.stream = stream; }