static public ManifestTimingData GetManifestTimingData(CloudMediaContext context, IAsset asset, TraceWriter log)
        // Parse the manifest and get data from it
        {
            ManifestTimingData response = new ManifestTimingData()
            {
                IsLive = false, Error = false, TimestampOffset = 0, TimestampList = new List <ulong>(), DiscontinuityDetected = false
            };

            try
            {
                ILocator mytemplocator = null;
                Uri      myuri         = MediaServicesHelper.GetValidOnDemandURI(context, asset);
                if (myuri == null)
                {
                    mytemplocator = MediaServicesHelper.CreatedTemporaryOnDemandLocator(asset);
                    myuri         = MediaServicesHelper.GetValidOnDemandURI(context, asset);
                }
                if (myuri != null)
                {
                    log.Info($"Asset URI {myuri.ToString()}");

                    XDocument manifest = XDocument.Load(myuri.ToString());

                    //log.Info($"manifest {manifest}");
                    var smoothmedia = manifest.Element("SmoothStreamingMedia");
                    var videotrack  = smoothmedia.Elements("StreamIndex").Where(a => a.Attribute("Type").Value == "video");

                    // TIMESCALE
                    string timescalefrommanifest = smoothmedia.Attribute("TimeScale").Value;
                    if (videotrack.FirstOrDefault().Attribute("TimeScale") != null) // there is timescale value in the video track. Let's take this one.
                    {
                        timescalefrommanifest = videotrack.FirstOrDefault().Attribute("TimeScale").Value;
                    }
                    ulong timescale = ulong.Parse(timescalefrommanifest);
                    response.TimeScale = (ulong?)timescale;

                    // Timestamp offset
                    if (videotrack.FirstOrDefault().Element("c").Attribute("t") != null)
                    {
                        response.TimestampOffset = ulong.Parse(videotrack.FirstOrDefault().Element("c").Attribute("t").Value);
                    }
                    else
                    {
                        response.TimestampOffset = 0; // no timestamp, so it should be 0
                    }

                    ulong totalduration         = 0;
                    ulong durationpreviouschunk = 0;
                    ulong durationchunk;
                    int   repeatchunk;
                    foreach (var chunk in videotrack.Elements("c"))
                    {
                        durationchunk = chunk.Attribute("d") != null?ulong.Parse(chunk.Attribute("d").Value) : 0;

                        log.Info($"duration d {durationchunk}");

                        repeatchunk = chunk.Attribute("r") != null?int.Parse(chunk.Attribute("r").Value) : 1;

                        log.Info($"repeat r {repeatchunk}");

                        if (chunk.Attribute("t") != null)
                        {
                            ulong tvalue = ulong.Parse(chunk.Attribute("t").Value);
                            response.TimestampList.Add(tvalue);
                            if (tvalue != response.TimestampOffset)
                            {
                                totalduration = tvalue - response.TimestampOffset; // Discountinuity ? We calculate the duration from the offset
                                response.DiscontinuityDetected = true;             // let's flag it
                            }
                        }
                        else
                        {
                            response.TimestampList.Add(response.TimestampList[response.TimestampList.Count() - 1] + durationpreviouschunk);
                        }

                        totalduration += durationchunk * (ulong)repeatchunk;

                        for (int i = 1; i < repeatchunk; i++)
                        {
                            response.TimestampList.Add(response.TimestampList[response.TimestampList.Count() - 1] + durationchunk);
                        }

                        durationpreviouschunk = durationchunk;
                    }
                    response.TimestampEndLastChunk = response.TimestampList[response.TimestampList.Count() - 1] + durationpreviouschunk;

                    if (smoothmedia.Attribute("IsLive") != null && smoothmedia.Attribute("IsLive").Value == "TRUE")
                    { // Live asset.... No duration to read (but we can read scaling and compute duration if no gap)
                        response.IsLive        = true;
                        response.AssetDuration = TimeSpan.FromSeconds((double)totalduration / ((double)timescale));
                    }
                    else
                    {
                        totalduration          = ulong.Parse(smoothmedia.Attribute("Duration").Value);
                        response.AssetDuration = TimeSpan.FromSeconds((double)totalduration / ((double)timescale));
                    }
                }
                else
                {
                    response.Error = true;
                }
                if (mytemplocator != null)
                {
                    mytemplocator.Delete();
                }
            }
            catch (Exception ex)
            {
                response.Error = true;
            }
            return(response);
        }