コード例 #1
0
        public static Stream GenerateClientManifestStream(IEnumerable<Box> Boxes, IEnumerable<ManifestTrack> ManifestTracks)
        {
            SmoothStreamingMedia manifest = new SmoothStreamingMedia();

            var moov = Boxes.SingleOrDefault(b => b.Type == BoxType.Moov);
            var mvhd = moov.InnerBoxes.SingleOrDefault(b => b.Type == BoxType.Mvhd) as MovieHeaderFullBox;

            manifest.Duration = ManifestTracks.Select(t => t.Duration).Max();
            manifest.TimeScale = mvhd.TimeScale;

            manifest.StreamIndex.AddRange(GenerateClientManifestStreamIndexs(ManifestTracks, moov));
            manifest.Protection = GetProtectionHeader(moov);

            return manifest.ToStream();
        }
コード例 #2
0
        public static Stream GenerateClientManifestStream(IEnumerable <Box> Boxes, IEnumerable <ManifestTrack> ManifestTracks)
        {
            SmoothStreamingMedia manifest = new SmoothStreamingMedia();

            var moov = Boxes.SingleOrDefault(b => b.Type == BoxType.Moov);
            var mvhd = moov.InnerBoxes.SingleOrDefault(b => b.Type == BoxType.Mvhd) as MovieHeaderFullBox;

            manifest.Duration  = ManifestTracks.Select(t => t.Duration).Max();
            manifest.TimeScale = mvhd.TimeScale;

            manifest.StreamIndex.AddRange(GenerateClientManifestStreamIndexs(ManifestTracks, moov));
            manifest.Protection = GetProtectionHeader(moov);

            return(manifest.ToStream());
        }
コード例 #3
0
        public static Stream ToStream(this SmoothStreamingMedia media)
        {
            var memStream = new MemoryStream();
            var settings  = new XmlWriterSettings()
            {
                Indent   = true,
                Encoding = System.Text.Encoding.Unicode
            };

            XmlWriter writer = XmlWriter.Create(memStream, settings);

            media.WriteTo(writer);
            writer.Flush();

            memStream.Seek(0, SeekOrigin.Begin);

            return(memStream);
        }
コード例 #4
0
        public static async Task<DashManifestConversionResult> ConvertToSmoothManifest(XDocument source, Uri rootUri)
        {
            var result = new DashManifestConversionResult();

            SmoothStreamingMedia manifest = new SmoothStreamingMedia();
            result.Manifest = manifest;

            var mpd = MPDFactory.LoadMPD(source.Root);

            // Get Duration
            if (mpd.MediaPresentationDuration.HasValue)
            {
                manifest.Duration = (ulong)mpd.MediaPresentationDuration.Value.Ticks;
            }
            manifest.IsLive = (mpd.Type == Presentation.Dynamic);
            //manifest.LookaheadCount = 2;
            if (mpd.AvailabilityEndTime.HasValue && mpd.AvailabilityStartTime.HasValue)
            {
                manifest.DVRWindowLength = (ulong)mpd.AvailabilityEndTime.Value.Subtract(mpd.AvailabilityStartTime.Value).Ticks;
            }

            foreach (var period in mpd.Period)
            {
                foreach (var adaptationSet in period.AdaptationSet)
                {
                    SmoothStreamingMediaStreamIndex streamIndex = null;
                    int representationIndex = 0;
                    foreach (var representation in adaptationSet.Representation)
                    {
                        string mediaUrl = null;
                        Uri initializationUri = null;
                        WebRequestor.Range initializationRange = null;
                        if (adaptationSet.SegmentTemplate != null)
                        {
                            var segmentTemplate = adaptationSet.SegmentTemplate;
                            var initializationTemplateUrl = segmentTemplate.InitializationValue;

                            initializationTemplateUrl = initializationTemplateUrl
                                .Replace("$$", "$")
                                .Replace("$RepresentationId$", representation.Id)
                                .Replace("$Number$", representationIndex.ToString())
                                .Replace("$Bandwidth$", representation.Bandwidth.ToString());
                            initializationUri = new Uri(rootUri, initializationTemplateUrl);
                            mediaUrl = segmentTemplate.Media
                                .Replace("$$", "$")
                                .Replace("$Bandwidth$", "{bitrate}")
                                .Replace("$Time$", "{start time}");
                        }
                        else if (representation.SegmentBase != null)
                        {
                            var baseUrl = representation.BaseURL.First().Value;
                            var segmentBase = representation.SegmentBase;
                            if (segmentBase.Initialization != null)
                            {
                                if (!string.IsNullOrEmpty(segmentBase.Initialization.SourceURL))
                                {
                                    initializationUri = new Uri(rootUri, segmentBase.Initialization.SourceURL);
                                }
                                initializationRange = WebRequestor.Range.FromString(segmentBase.Initialization.Range);
                            }
                            if (initializationUri == null)
                            {
                                initializationUri = new Uri(rootUri, baseUrl);
                            }
                        }
                        else throw new NotImplementedException();

                        var initializationBoxes = await GetBoxesAsync(initializationUri, initializationRange);

                        if (manifest.Protection == null) // support for CENC encryption
                        {
                            var moov = initializationBoxes.SingleOrDefault(b => b.Type == BoxType.Moov);
                            if (moov != null)
                            {
                                manifest.Protection = SmoothFactory.GetProtectionHeader(moov);
                            }
                        }

                        var trackStreamIndex = SmoothFactory.GenerateClientManifestStreamIndex(initializationBoxes);
                        var track = trackStreamIndex.QualityLevel.First();
                        if (streamIndex != null)
                        {
                            track.Index = (uint)streamIndex.QualityLevel.Count; // index is zero based so this is always equal to the current count
                            streamIndex.QualityLevel.Add(track);
                            streamIndex.MaxWidth = Math.Max(trackStreamIndex.MaxWidth, streamIndex.MaxWidth);
                            streamIndex.MaxHeight = Math.Max(trackStreamIndex.MaxHeight, streamIndex.MaxHeight);
                            streamIndex.DisplayWidth = streamIndex.MaxWidth;
                            streamIndex.DisplayHeight = streamIndex.MaxHeight;
                        }
                        else
                        {
                            streamIndex = trackStreamIndex;
                            if (mediaUrl != null) streamIndex.Url = mediaUrl;
                        }

                        track.Bitrate = representation.Bandwidth;

                        // create chunks
                        if (adaptationSet.SegmentTemplate != null)
                        {
                            if (adaptationSet.SegmentTemplate.SegmentTimeline != null)
                            {
                                if (!streamIndex.c.Any())
                                {
                                    streamIndex.c.AddRange(CreateChunks(adaptationSet.SegmentTemplate.SegmentTimeline));
                                    if (!manifest.IsLive) streamIndex.Chunks = (uint)streamIndex.c.Count;
                                }
                            }
                            else throw new NotImplementedException();
                        }
                        else if (representation.SegmentBase != null)
                        {
                            // TODO:/OPTIMIZE: request at the same time as initialization header
                            var segmentBase = representation.SegmentBase;
                            var indexRange = segmentBase.IndexRange.Split('-').Select(r => long.Parse(r)).ToArray();
                            var baseUrl = representation.BaseURL.First().Value;
                            var segmentIndexUri = new Uri(rootUri, baseUrl);
                            var segmentIndexRange = WebRequestor.Range.FromString(segmentBase.IndexRange);
                            var segmentIndexBoxes = await GetBoxesAsync(segmentIndexUri, segmentIndexRange);
                            var sidx = segmentIndexBoxes.OfType<SegmentIndexBox>().First();

                            // remove the track if the sidx durations don't match the chunk durations.
                            //if (streamIndex.c.Any() && sidx.Subsegments.First().Duration != streamIndex.c.First().d)
                            //{
                            //    streamIndex.QualityLevel.Remove(track);
                            //    break;
                            //}

                            track.Bitrate = CalculateBitrate(sidx);

                            if (!streamIndex.c.Any())
                            {
                                streamIndex.c.AddRange(CreateChunks(sidx));
                                if (!manifest.IsLive) streamIndex.Chunks = (uint)streamIndex.c.Count;
                            }
                            foreach (var kvp in GetChunkLookups(streamIndex, track, segmentIndexUri, sidx))
                            {
                                result.ChunkLookup.Add(kvp.Key, kvp.Value);
                            }
                        }
                        else throw new NotImplementedException();

                        representationIndex++;
                    }
                    
                    manifest.StreamIndex.Add(streamIndex);
                }
            }

            return result;
        }
コード例 #5
0
        public static void WriteTo(this SmoothStreamingMedia media, XmlWriter writer)
        {
            // The SmoothStreamingMedia element
            writer.WriteStartElement("SmoothStreamingMedia");
            writer.WriteAttributeString("MajorVersion", media.MajorVersion.ToString());
            writer.WriteAttributeString("MinorVersion", media.MinorVersion.ToString());
            writer.WriteAttributeString("Duration", media.Duration.ToString());
            if (media.TimeScale.HasValue)
            {
                writer.WriteAttributeString("TimeScale", media.TimeScale.ToString());
            }
            if (media.IsLive)
            {
                writer.WriteAttributeString("IsLive", "TRUE");
            }
            if (media.LookaheadCount.HasValue)
            {
                writer.WriteAttributeString("LookAheadFragmentCount", media.LookaheadCount.ToString());
            }
            if (media.DVRWindowLength.HasValue)
            {
                writer.WriteAttributeString("DVRWindowLength", media.DVRWindowLength.ToString());
            }

            // The StreamIndex elements
            foreach (var streamIndex in media.StreamIndex)
            {
                // The StreamIndex element
                writer.WriteStartElement("StreamIndex");
                writer.WriteAttributeString("Type", streamIndex.Type);
                if (streamIndex.Subtype != null)
                {
                    writer.WriteAttributeString("Subtype", streamIndex.Subtype);
                }
                writer.WriteAttributeString("Name", streamIndex.Name);
                writer.WriteAttributeString("Chunks", streamIndex.Chunks.ToString());
                writer.WriteAttributeString("QualityLevels", streamIndex.QualityLevels.ToString());
                if (streamIndex.TimeScale != 10000000)
                {
                    writer.WriteAttributeString("TimeScale", streamIndex.TimeScale.ToString());
                }

                switch (streamIndex.Type)
                {
                case "audio":
                    break;

                case "video":
                    writer.WriteAttributeString("MaxWidth", streamIndex.MaxWidth.ToString());
                    writer.WriteAttributeString("MaxHeight", streamIndex.MaxHeight.ToString());
                    writer.WriteAttributeString("DisplayWidth", streamIndex.DisplayWidth.ToString());
                    writer.WriteAttributeString("DisplayHeight", streamIndex.DisplayHeight.ToString());
                    break;

                case "text":
                    writer.WriteAttributeString("Subtype", "SUBT");
                    break;
                }

                // These values may be empty for some StreamIndexes
                if (!string.IsNullOrEmpty(streamIndex.Url))
                {
                    writer.WriteAttributeString("Url", streamIndex.Url);
                }
                if (!string.IsNullOrEmpty(streamIndex.Language))
                {
                    writer.WriteAttributeString("Language", streamIndex.Language);
                }

                foreach (var qualityLevel in streamIndex.QualityLevel)
                {
                    // The QualityLevel element
                    writer.WriteStartElement("QualityLevel");

                    writer.WriteAttributeString("Index", qualityLevel.Index.ToString());
                    writer.WriteAttributeString("Bitrate", qualityLevel.Bitrate.ToString());

                    if (!string.IsNullOrEmpty(qualityLevel.FourCC))
                    {
                        writer.WriteAttributeString("FourCC", qualityLevel.FourCC.ToString());
                    }

                    switch (streamIndex.Type)
                    {
                    case "audio":
                        writer.WriteAttributeString("SamplingRate", qualityLevel.SamplingRate.ToString());
                        writer.WriteAttributeString("Channels", qualityLevel.Channels.ToString());
                        writer.WriteAttributeString("BitsPerSample", qualityLevel.BitsPerSample.ToString());
                        writer.WriteAttributeString("PacketSize", qualityLevel.PacketSize.ToString());
                        writer.WriteAttributeString("AudioTag", qualityLevel.AudioTag.ToString());
                        break;

                    case "video":
                        writer.WriteAttributeString("MaxWidth", qualityLevel.MaxWidth.ToString());
                        writer.WriteAttributeString("MaxHeight", qualityLevel.MaxHeight.ToString());
                        break;

                    case "text":
                        break;
                    }

                    if (!string.IsNullOrEmpty(qualityLevel.CodecPrivateData))
                    {
                        writer.WriteAttributeString("CodecPrivateData", qualityLevel.CodecPrivateData);
                    }

                    // Close the QualityLevel attribute
                    writer.WriteEndElement();
                }

                int i = 0;
                while (i < streamIndex.c.Count)
                {
                    SmoothStreamingMediaStreamIndexC chunk = streamIndex.c[i];
                    int r = 1;
                    while (i + r < streamIndex.c.Count)
                    {
                        var chunkNext = streamIndex.c[i + r];
                        if (chunk.d == chunkNext.d)
                        {
                            r++;
                        }
                        else
                        {
                            break;
                        }
                    }

                    // The c element
                    writer.WriteStartElement("c");

                    if (chunk.t.HasValue)
                    {
                        writer.WriteAttributeString("t", chunk.t.ToString());
                    }
                    writer.WriteAttributeString("d", chunk.d.ToString());
                    if (r > 1)
                    {
                        writer.WriteAttributeString("r", r.ToString());
                    }

                    // Close the c element
                    writer.WriteEndElement();

                    i += r;
                }

                // Close the StreamIndex element
                writer.WriteEndElement();
            }

            if (media.Protection != null)
            {
                // The Protection element
                writer.WriteStartElement("Protection");

                // The ProtectionHeader element
                writer.WriteStartElement("ProtectionHeader");
                writer.WriteAttributeString("SystemID", media.Protection.ProtectionHeader.SystemID);
                writer.WriteValue(media.Protection.ProtectionHeader.Value);
                writer.WriteEndElement();

                writer.WriteEndElement();
            }

            // Close the SmoothStreamingMedia element
            writer.WriteEndElement();
        }