using System.Collections.Generic; using System.Dynamic; using System.Linq; using System.Text.RegularExpressions; using CRD.Utils.Parser.Utils; namespace CRD.Utils.Parser.Segments; public class SegmentTemplate{ public static List SegmentsFromTemplate(dynamic attributes, List segmentTimeline){ dynamic templateValues = new ExpandoObject(); templateValues.RepresentationID = ObjectUtilities.GetMemberValue(attributes,"id"); templateValues.Bandwidth = ObjectUtilities.GetMemberValue(attributes,"bandwidth") ?? 0; dynamic initialization = attributes.initialization ?? new{ sourceURL = string.Empty, range = string.Empty }; dynamic mapSegment = UrlType.UrlTypeToSegment(new{ baseUrl = ObjectUtilities.GetMemberValue(attributes,"baseUrl"), source = ConstructTemplateUrl(initialization.sourceURL, templateValues), range = ObjectUtilities.GetMemberValue(initialization,"range") }); List segments = ParseTemplateInfo(attributes, segmentTimeline); return segments.Select(segment => { templateValues.Number = ObjectUtilities.GetMemberValue(segment,"number"); templateValues.Time = ObjectUtilities.GetMemberValue(segment,"time"); var uri = ConstructTemplateUrl(ObjectUtilities.GetMemberValue(attributes,"media") ?? "", templateValues); var timescale = ObjectUtilities.GetMemberValue(attributes,"timescale") ?? 1; var presentationTimeOffset = ObjectUtilities.GetMemberValue(attributes,"presentationTimeOffset") ?? 0; double presentationTime = ObjectUtilities.GetMemberValue(attributes,"periodStart") + ((ObjectUtilities.GetMemberValue(segment,"time") - presentationTimeOffset) / (double) timescale); dynamic map = new ExpandoObject(); map.uri = uri; map.timeline = ObjectUtilities.GetMemberValue(segment,"timeline"); map.duration = ObjectUtilities.GetMemberValue(segment,"duration"); map.resolvedUri = UrlUtils.ResolveUrl(ObjectUtilities.GetMemberValue(attributes,"baseUrl") ?? "", uri); map.map = mapSegment; map.number = ObjectUtilities.GetMemberValue(segment,"number"); map.presentationTime = presentationTime; return map; }).ToList(); } private static readonly Regex IdentifierPattern = new Regex(@"\$([A-Za-z]*)(?:(%0)([0-9]+)d)?\$", RegexOptions.Compiled); public static string ConstructTemplateUrl(string url, dynamic values){ // Convert dynamic to IDictionary for easier handling var valuesDictionary = (IDictionary)values; return IdentifierPattern.Replace(url, match => IdentifierReplacement(match, valuesDictionary)); } private static string IdentifierReplacement(Match match, IDictionary values){ if (match.Value == "$$"){ // escape sequence return "$"; } var identifier = match.Groups[1].Value; var format = match.Groups[2].Value; var widthStr = match.Groups[3].Value; if (!values.ContainsKey(identifier)){ return match.Value; } var value = values[identifier]?.ToString() ?? ""; if (identifier == "RepresentationID"){ // Format tag shall not be present with RepresentationID return value; } int width = string.IsNullOrEmpty(format) ? 1 : int.Parse(widthStr); if (value.Length >= width){ return value; } return value.PadLeft(width, '0'); } public static List ParseTemplateInfo(dynamic attributes, List segmentTimeline){ // Check if duration and SegmentTimeline are not present if (ObjectUtilities.GetMemberValue(attributes,"duration") == null && segmentTimeline == null){ // Exactly one media segment expected return new List{ new{ number = ObjectUtilities.GetMemberValue(attributes,"startNumber") ?? 1, duration = ObjectUtilities.GetMemberValue(attributes,"sourceDuration"), time = 0, timeline = ObjectUtilities.GetMemberValue(attributes,"periodStart") } }; } if (ObjectUtilities.GetMemberValue(attributes,"duration") != null){ // Parse segments based on duration return DurationTimeParser.ParseByDuration(attributes); } // Parse segments based on SegmentTimeline return TimelineTimeParser.ParseByTimeline(attributes, segmentTimeline); } }