Example #1
0
 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;
            
        }