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