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(); }
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()); }
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); }
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; }
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(); }