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