diff --git a/src/parsers/manifest/dash/js-parser/node_parsers/AdaptationSet.ts b/src/parsers/manifest/dash/js-parser/node_parsers/AdaptationSet.ts index ef2621f768..530517274e 100644 --- a/src/parsers/manifest/dash/js-parser/node_parsers/AdaptationSet.ts +++ b/src/parsers/manifest/dash/js-parser/node_parsers/AdaptationSet.ts @@ -24,6 +24,7 @@ import type { import parseBaseURL from "./BaseURL.ts"; import parseContentComponent from "./ContentComponent.ts"; import parseContentProtection from "./ContentProtection.ts"; +import parseDescriptor from "./Descriptor.ts"; import { createRepresentationIntermediateRepresentation } from "./Representation.ts"; import parseSegmentBase from "./SegmentBase.ts"; import parseSegmentList from "./SegmentList.ts"; @@ -88,9 +89,12 @@ function parseAdaptationSetChildren( children.ContentComponent.push(parseContentComponent(currentNode)); break; - case "EssentialProperty": - children.EssentialProperty.push(parseScheme(currentNode)); + case "EssentialProperty": { + const [descriptor, descriptorWarnings] = parseDescriptor(currentNode); + children.EssentialProperty.push(descriptor); + warnings.push(...descriptorWarnings); break; + } case "InbandEventStream": children.InbandEventStream.push(parseScheme(currentNode)); @@ -118,9 +122,12 @@ function parseAdaptationSetChildren( children.Role.push(parseScheme(currentNode)); break; - case "SupplementalProperty": - children.SupplementalProperty.push(parseScheme(currentNode)); + case "SupplementalProperty": { + const [descriptor, descriptorWarnings] = parseDescriptor(currentNode); + children.SupplementalProperty.push(descriptor); + warnings.push(...descriptorWarnings); break; + } case "SegmentBase": { const [segmentBase, segmentBaseWarnings] = parseSegmentBase(currentNode); diff --git a/src/parsers/manifest/dash/js-parser/node_parsers/Descriptor.ts b/src/parsers/manifest/dash/js-parser/node_parsers/Descriptor.ts new file mode 100644 index 0000000000..39a3d868c0 --- /dev/null +++ b/src/parsers/manifest/dash/js-parser/node_parsers/Descriptor.ts @@ -0,0 +1,40 @@ +import type { ITNode } from "../../../../../utils/xml-parser.ts"; +import type { IDescriptorIntermediateRepresentation } from "../../node_parser_types.ts"; +import parseUrlQueryInfo from "./UrlQueryInfo.ts"; +import { parseScheme } from "./utils.ts"; + +/** + * Parse an `` or `` descriptor. + */ +export default function parseDescriptor( + root: ITNode, +): [IDescriptorIntermediateRepresentation, Error[]] { + const children: IDescriptorIntermediateRepresentation["children"] = { + UrlQueryInfo: [], + ExtUrlQueryInfo: [], + }; + const property: IDescriptorIntermediateRepresentation = { + attributes: parseScheme(root).attributes, + children, + }; + if (typeof root.attributes.id === "string") { + property.attributes.id = root.attributes.id; + } + const warnings: Error[] = []; + for (const child of root.children) { + if (typeof child === "string") { + continue; + } + // TODO Support other XML prefixes also linked to the `up` namespace. + if (child.tagName === "up:UrlQueryInfo") { + const [queryInfo, queryInfoWarnings] = parseUrlQueryInfo(child); + children.UrlQueryInfo.push(queryInfo); + warnings.push(...queryInfoWarnings); + } else if (child.tagName === "up:ExtUrlQueryInfo") { + const [queryInfo, queryInfoWarnings] = parseUrlQueryInfo(child); + children.ExtUrlQueryInfo.push(queryInfo); + warnings.push(...queryInfoWarnings); + } + } + return [property, warnings]; +} diff --git a/src/parsers/manifest/dash/js-parser/node_parsers/MPD.ts b/src/parsers/manifest/dash/js-parser/node_parsers/MPD.ts index e1c0421d0c..b5d66568e8 100644 --- a/src/parsers/manifest/dash/js-parser/node_parsers/MPD.ts +++ b/src/parsers/manifest/dash/js-parser/node_parsers/MPD.ts @@ -25,6 +25,7 @@ import type { } from "../../node_parser_types.ts"; import parseBaseURL from "./BaseURL.ts"; import parseContentProtection from "./ContentProtection.ts"; +import parseDescriptor from "./Descriptor.ts"; import { createPeriodIntermediateRepresentation } from "./Period.ts"; import { parseDateTime, @@ -45,6 +46,8 @@ function parseMPDChildren( ): [IMPDChildren, Error[]] { const ret: IMPDChildren = { BaseURL: [], + EssentialProperty: [], + SupplementalProperty: [], Location: [], Period: [], UTCTiming: [], @@ -67,6 +70,20 @@ function parseMPDChildren( break; } + case "EssentialProperty": { + const [descriptor, descriptorWarnings] = parseDescriptor(currentNode); + ret.EssentialProperty.push(descriptor); + warnings.push(...descriptorWarnings); + break; + } + + case "SupplementalProperty": { + const [descriptor, descriptorWarnings] = parseDescriptor(currentNode); + ret.SupplementalProperty.push(descriptor); + warnings.push(...descriptorWarnings); + break; + } + case "Location": { ret.Location.push(parseLocation(currentNode)); break; diff --git a/src/parsers/manifest/dash/js-parser/node_parsers/Period.ts b/src/parsers/manifest/dash/js-parser/node_parsers/Period.ts index 688a4790e7..5ea90c515c 100644 --- a/src/parsers/manifest/dash/js-parser/node_parsers/Period.ts +++ b/src/parsers/manifest/dash/js-parser/node_parsers/Period.ts @@ -25,6 +25,7 @@ import type { import { createAdaptationSetIntermediateRepresentation } from "./AdaptationSet.ts"; import parseBaseURL from "./BaseURL.ts"; import parseContentProtection from "./ContentProtection.ts"; +import parseDescriptor from "./Descriptor.ts"; import { createEventStreamIntermediateRepresentation } from "./EventStream.ts"; import parseSegmentTemplate from "./SegmentTemplate.ts"; import { parseBoolean, parseDuration, ValueParser } from "./utils.ts"; @@ -42,6 +43,7 @@ function parsePeriodChildren( AdaptationSet: [], BaseURL: [], ContentProtection: [], + SupplementalProperty: [], SegmentTemplate: [], EventStream: [], }; @@ -98,6 +100,13 @@ function parsePeriodChildren( } break; } + + case "SupplementalProperty": { + const [descriptor, descriptorWarnings] = parseDescriptor(currentElement); + ret.SupplementalProperty.push(descriptor); + warnings.push(...descriptorWarnings); + break; + } } } diff --git a/src/parsers/manifest/dash/js-parser/node_parsers/Representation.ts b/src/parsers/manifest/dash/js-parser/node_parsers/Representation.ts index 66f5af910e..c7320660b3 100644 --- a/src/parsers/manifest/dash/js-parser/node_parsers/Representation.ts +++ b/src/parsers/manifest/dash/js-parser/node_parsers/Representation.ts @@ -23,6 +23,7 @@ import type { } from "../../node_parser_types.ts"; import parseBaseURL from "./BaseURL.ts"; import parseContentProtection from "./ContentProtection.ts"; +import parseDescriptor from "./Descriptor.ts"; import parseSegmentBase from "./SegmentBase.ts"; import parseSegmentList from "./SegmentList.ts"; import parseSegmentTemplate from "./SegmentTemplate.ts"; @@ -103,12 +104,18 @@ function parseRepresentationChildren( } break; } - case "EssentialProperty": - children.EssentialProperty.push(parseScheme(currentElement)); + case "EssentialProperty": { + const [descriptor, descriptorWarnings] = parseDescriptor(currentElement); + children.EssentialProperty.push(descriptor); + warnings.push(...descriptorWarnings); break; - case "SupplementalProperty": - children.SupplementalProperty.push(parseScheme(currentElement)); + } + case "SupplementalProperty": { + const [descriptor, descriptorWarnings] = parseDescriptor(currentElement); + children.SupplementalProperty.push(descriptor); + warnings.push(...descriptorWarnings); break; + } } } return [children, warnings]; diff --git a/src/parsers/manifest/dash/js-parser/node_parsers/UrlQueryInfo.ts b/src/parsers/manifest/dash/js-parser/node_parsers/UrlQueryInfo.ts new file mode 100644 index 0000000000..e2298293e1 --- /dev/null +++ b/src/parsers/manifest/dash/js-parser/node_parsers/UrlQueryInfo.ts @@ -0,0 +1,37 @@ +import isNullOrUndefined from "../../../../../utils/is_null_or_undefined.ts"; +import type { ITNode } from "../../../../../utils/xml-parser.ts"; +import type { IUrlQueryInfoIntermediateRepresentation } from "../../node_parser_types.ts"; +import { parseBoolean, ValueParser } from "./utils.ts"; + +/** Parse a `` or `` element. */ +export default function parseUrlQueryInfo( + root: ITNode, +): [IUrlQueryInfoIntermediateRepresentation, Error[]] { + const attributes: IUrlQueryInfoIntermediateRepresentation["attributes"] = {}; + const warnings: Error[] = []; + const parseValue = ValueParser(attributes, warnings); + for (const attributeName of Object.keys(root.attributes)) { + const attributeValue = root.attributes[attributeName]; + if (isNullOrUndefined(attributeValue)) { + continue; + } + switch (attributeName) { + case "queryString": + attributes.queryString = attributeValue; + break; + case "queryTemplate": + attributes.queryTemplate = attributeValue; + break; + case "includeInRequests": + attributes.includeInRequests = attributeValue; + break; + case "useMPDUrlQuery": + parseValue(attributeValue, { + parser: parseBoolean, + name: "useMpdUrlQuery", + }); + break; + } + } + return [{ attributes }, warnings]; +} diff --git a/src/parsers/manifest/dash/node_parser_types.ts b/src/parsers/manifest/dash/node_parser_types.ts index 1771220063..6c31036f14 100644 --- a/src/parsers/manifest/dash/node_parser_types.ts +++ b/src/parsers/manifest/dash/node_parser_types.ts @@ -43,6 +43,10 @@ export interface IMPDChildren { * from the first encountered to the last encountered. */ BaseURL: IBaseUrlIntermediateRepresentation[]; + /** Root-level `` descriptors. */ + EssentialProperty: IDescriptorIntermediateRepresentation[]; + /** Root-level `` descriptors. */ + SupplementalProperty: IDescriptorIntermediateRepresentation[]; /** * Location(s) at which the Manifest can be refreshed. * @@ -143,6 +147,8 @@ export interface IPeriodChildren { EventStream: IEventStreamIntermediateRepresentation[]; /** Encryption-related metadata. */ ContentProtection: IContentProtectionIntermediateRepresentation[]; + /** Period-level `` descriptors. */ + SupplementalProperty: IDescriptorIntermediateRepresentation[]; } /* Intermediate representation for A Period node's attributes. */ @@ -192,10 +198,10 @@ export interface IAdaptationSetChildren { ContentComponent: IContentComponentIntermediateRepresentation[]; /** Encryption-related metadata. */ ContentProtection: IContentProtectionIntermediateRepresentation[]; - EssentialProperty: ISchemeIntermediateRepresentation[]; + EssentialProperty: IDescriptorIntermediateRepresentation[]; InbandEventStream: ISchemeIntermediateRepresentation[]; Role: ISchemeIntermediateRepresentation[]; - SupplementalProperty: ISchemeIntermediateRepresentation[]; + SupplementalProperty: IDescriptorIntermediateRepresentation[]; SegmentBase: ISegmentBaseIntermediateRepresentation[]; SegmentList: ISegmentListIntermediateRepresentation[]; SegmentTemplate: ISegmentTemplateIntermediateRepresentation[]; @@ -251,8 +257,8 @@ export interface IRepresentationChildren { SegmentBase: ISegmentBaseIntermediateRepresentation[]; SegmentList: ISegmentListIntermediateRepresentation[]; SegmentTemplate: ISegmentTemplateIntermediateRepresentation[]; - SupplementalProperty: ISchemeIntermediateRepresentation[]; - EssentialProperty: ISchemeIntermediateRepresentation[]; + SupplementalProperty: IDescriptorIntermediateRepresentation[]; + EssentialProperty: IDescriptorIntermediateRepresentation[]; } /* Intermediate representation for A Representation node's attributes. */ @@ -438,6 +444,29 @@ export interface ISchemeIntermediateRepresentation { attributes: ISchemeAttributes; } +/** Shared intermediate representation for DASH descriptor elements. */ +export interface IDescriptorIntermediateRepresentation { + attributes: IDescriptorAttributes; + children: { + UrlQueryInfo: IUrlQueryInfoIntermediateRepresentation[]; + ExtUrlQueryInfo: IUrlQueryInfoIntermediateRepresentation[]; + }; +} + +/** Parsed DASH Annex I URL query information. */ +export interface IUrlQueryInfoIntermediateRepresentation { + attributes: { + queryString?: string | undefined; + queryTemplate?: string | undefined; + includeInRequests?: string | undefined; + useMpdUrlQuery?: boolean | undefined; + }; +} + +export interface IDescriptorAttributes extends ISchemeAttributes { + id?: string | undefined; +} + export interface ISchemeAttributes { /** * Content of the `schemeIdUri` attribute for that scheme. diff --git a/src/parsers/manifest/dash/wasm-parser/rs/events.rs b/src/parsers/manifest/dash/wasm-parser/rs/events.rs index acea163773..caca94b252 100644 --- a/src/parsers/manifest/dash/wasm-parser/rs/events.rs +++ b/src/parsers/manifest/dash/wasm-parser/rs/events.rs @@ -95,6 +95,12 @@ pub enum TagName { /// Indicate a `` node. Location = 23, + + /// Indicate a `` node. + UrlQueryInfo = 24, + + /// Indicate an `` node. + ExtUrlQueryInfo = 25, } #[derive(PartialEq, Clone, Copy)] @@ -289,6 +295,11 @@ pub enum AttributeName { EndNumber = 76, // f64 SupplementalCodecs = 77, // string + + QueryTemplate = 81, // String + IncludeInRequests = 82, // String + UseMpdUrlQuery = 83, // Boolean + QueryString = 84, // String } impl TagName { diff --git a/src/parsers/manifest/dash/wasm-parser/rs/processor/attributes.rs b/src/parsers/manifest/dash/wasm-parser/rs/processor/attributes.rs index 61db2e9ba1..766b73dde6 100644 --- a/src/parsers/manifest/dash/wasm-parser/rs/processor/attributes.rs +++ b/src/parsers/manifest/dash/wasm-parser/rs/processor/attributes.rs @@ -269,6 +269,35 @@ pub fn report_scheme_attrs(tag_bs: &quick_xml::events::BytesStart) { } } +pub fn report_descriptor_attrs(tag_bs: &quick_xml::events::BytesStart) { + for res_attr in tag_bs.attributes() { + match res_attr { + Ok(attr) => match attr.key.as_ref() { + b"id" => Id.try_report_as_string(&attr), + b"schemeIdUri" => SchemeIdUri.try_report_as_string(&attr), + b"value" => SchemeValue.try_report_as_string(&attr), + _ => {} + }, + Err(err) => ParsingError::from(err).report_err(), + }; + } +} + +pub fn report_url_query_info_attrs(tag_bs: &quick_xml::events::BytesStart) { + for res_attr in tag_bs.attributes() { + match res_attr { + Ok(attr) => match attr.key.as_ref() { + b"queryTemplate" => QueryTemplate.try_report_as_string(&attr), + b"queryString" => QueryString.try_report_as_string(&attr), + b"includeInRequests" => IncludeInRequests.try_report_as_string(&attr), + b"useMPDUrlQuery" => UseMpdUrlQuery.try_report_as_bool(&attr), + _ => {} + }, + Err(err) => ParsingError::from(err).report_err(), + }; + } +} + pub fn report_segment_url_attrs(tag_bs: &quick_xml::events::BytesStart) { for res_attr in tag_bs.attributes() { match res_attr { diff --git a/src/parsers/manifest/dash/wasm-parser/rs/processor/mod.rs b/src/parsers/manifest/dash/wasm-parser/rs/processor/mod.rs index 167c456e22..2fee0597df 100644 --- a/src/parsers/manifest/dash/wasm-parser/rs/processor/mod.rs +++ b/src/parsers/manifest/dash/wasm-parser/rs/processor/mod.rs @@ -69,7 +69,7 @@ impl MPDProcessor { } b"EssentialProperty" => { TagName::EssentialProperty.report_tag_open(); - attributes::report_scheme_attrs(&tag); + attributes::report_descriptor_attrs(&tag); } b"InbandEventStream" => { TagName::InbandEventStream.report_tag_open(); @@ -81,7 +81,7 @@ impl MPDProcessor { } b"SupplementalProperty" => { TagName::SupplementalProperty.report_tag_open(); - attributes::report_scheme_attrs(&tag); + attributes::report_descriptor_attrs(&tag); } b"SegmentBase" => { TagName::SegmentBase.report_tag_open(); @@ -110,6 +110,16 @@ impl MPDProcessor { attributes::report_scheme_attrs(&tag); } + // TODO Support other XML prefixes also linked to the `up` namespace. + b"up:UrlQueryInfo" => { + TagName::UrlQueryInfo.report_tag_open(); + attributes::report_url_query_info_attrs(&tag); + } + b"up:ExtUrlQueryInfo" => { + TagName::ExtUrlQueryInfo.report_tag_open(); + attributes::report_url_query_info_attrs(&tag); + } + b"BaseURL" => { TagName::BaseURL.report_tag_open(); attributes::report_base_url_attrs(&tag); @@ -153,6 +163,8 @@ impl MPDProcessor { b"SegmentTemplate" => TagName::SegmentTemplate.report_tag_close(), b"Initialization" => TagName::Initialization.report_tag_close(), b"UTCTiming" => TagName::UtcTiming.report_tag_close(), + b"up:UrlQueryInfo" => TagName::UrlQueryInfo.report_tag_close(), + b"up:ExtUrlQueryInfo" => TagName::ExtUrlQueryInfo.report_tag_close(), _ => {} }, Ok(Event::Eof) => { diff --git a/src/parsers/manifest/dash/wasm-parser/ts/generators/AdaptationSet.ts b/src/parsers/manifest/dash/wasm-parser/ts/generators/AdaptationSet.ts index 1d54886a33..95297f9c01 100644 --- a/src/parsers/manifest/dash/wasm-parser/ts/generators/AdaptationSet.ts +++ b/src/parsers/manifest/dash/wasm-parser/ts/generators/AdaptationSet.ts @@ -27,6 +27,7 @@ import { parseFloatOrBool, parseString } from "../utils.ts"; import { generateBaseUrlAttrParser } from "./BaseURL.ts"; import { generateContentComponentAttrParser } from "./ContentComponent.ts"; import { generateContentProtectionAttrParser } from "./ContentProtection.ts"; +import { generateDescriptorParsers } from "./Descriptor.ts"; import { generateLabelElementParser } from "./Label.ts"; import { generateRepresentationAttrParser, @@ -101,16 +102,19 @@ export function generateAdaptationSetChildrenParser( break; } - case TagName.EssentialProperty: { - const essentialProperty = { attributes: {} }; - adaptationSetChildren.EssentialProperty.push(essentialProperty); - - const childrenParser = noop; // EssentialProperty have no sub-element - const attributeParser = generateSchemeAttrParser( - essentialProperty.attributes, - linearMemory, - ); - parsersStack.pushParsers(nodeId, childrenParser, attributeParser); + case TagName.EssentialProperty: + case TagName.SupplementalProperty: { + const descriptor = { + attributes: {}, + children: { UrlQueryInfo: [], ExtUrlQueryInfo: [] }, + }; + const destination = + nodeId === TagName.EssentialProperty + ? adaptationSetChildren.EssentialProperty + : adaptationSetChildren.SupplementalProperty; + destination.push(descriptor); + const parsers = generateDescriptorParsers(descriptor, linearMemory, parsersStack); + parsersStack.pushParsers(nodeId, parsers.children, parsers.attributes); break; } @@ -163,17 +167,6 @@ export function generateAdaptationSetChildrenParser( break; } - case TagName.SupplementalProperty: { - const supplementalProperty = { attributes: {} }; - adaptationSetChildren.SupplementalProperty.push(supplementalProperty); - const attributeParser = generateSchemeAttrParser( - supplementalProperty.attributes, - linearMemory, - ); - parsersStack.pushParsers(nodeId, noop, attributeParser); - break; - } - case TagName.SegmentBase: { const segmentBaseObj = { children: { Initialization: [] }, attributes: {} }; adaptationSetChildren.SegmentBase.push(segmentBaseObj); diff --git a/src/parsers/manifest/dash/wasm-parser/ts/generators/Descriptor.ts b/src/parsers/manifest/dash/wasm-parser/ts/generators/Descriptor.ts new file mode 100644 index 0000000000..58811c345a --- /dev/null +++ b/src/parsers/manifest/dash/wasm-parser/ts/generators/Descriptor.ts @@ -0,0 +1,88 @@ +import noop from "../../../../../../utils/noop.ts"; +import type { + IDescriptorAttributes, + IDescriptorIntermediateRepresentation, + IUrlQueryInfoIntermediateRepresentation, +} from "../../../node_parser_types.ts"; +import type { IAttributeParser, IChildrenParser } from "../parsers_stack.ts"; +import type ParsersStack from "../parsers_stack.ts"; +import { AttributeName, TagName } from "../types.ts"; +import { parseString } from "../utils.ts"; +import { generateSchemeAttrParser } from "./Scheme.ts"; + +export function generateDescriptorParsers( + property: IDescriptorIntermediateRepresentation, + linearMemory: WebAssembly.Memory, + parsersStack: ParsersStack, +): { children: IChildrenParser; attributes: IAttributeParser } { + const childrenParser: IChildrenParser = (childId) => { + if (childId !== TagName.UrlQueryInfo && childId !== TagName.ExtUrlQueryInfo) { + parsersStack.pushParsers(childId, noop, noop); + return; + } + const queryInfo: IUrlQueryInfoIntermediateRepresentation = { attributes: {} }; + const destination = + childId === TagName.UrlQueryInfo + ? property.children.UrlQueryInfo + : property.children.ExtUrlQueryInfo; + destination.push(queryInfo); + parsersStack.pushParsers( + childId, + noop, + generateUrlQueryInfoAttrParser(queryInfo.attributes, linearMemory), + ); + }; + return { + children: childrenParser, + attributes: generateDescriptorAttrParser(property.attributes, linearMemory), + }; +} + +/** Generate parsers for an EssentialProperty/SupplementalProperty descriptor. */ +export function generateDescriptorAttrParser( + attributes: IDescriptorAttributes, + linearMemory: WebAssembly.Memory, +): IAttributeParser { + const textDecoder = new TextDecoder(); + const parseSchemeAttribute = generateSchemeAttrParser(attributes, linearMemory); + return (attribute, ptr, len) => { + if (attribute === AttributeName.Id) { + attributes.id = parseString(textDecoder, linearMemory.buffer, ptr, len); + } else { + parseSchemeAttribute(attribute, ptr, len); + } + }; +} + +function generateUrlQueryInfoAttrParser( + attributes: IUrlQueryInfoIntermediateRepresentation["attributes"], + linearMemory: WebAssembly.Memory, +): IAttributeParser { + const textDecoder = new TextDecoder(); + return (attribute, ptr, len) => { + switch (attribute) { + case AttributeName.QueryString: + attributes.queryString = parseString(textDecoder, linearMemory.buffer, ptr, len); + break; + case AttributeName.QueryTemplate: + attributes.queryTemplate = parseString( + textDecoder, + linearMemory.buffer, + ptr, + len, + ); + break; + case AttributeName.IncludeInRequests: + attributes.includeInRequests = parseString( + textDecoder, + linearMemory.buffer, + ptr, + len, + ); + break; + case AttributeName.UseMpdUrlQuery: + attributes.useMpdUrlQuery = new DataView(linearMemory.buffer).getUint8(0) === 0; + break; + } + }; +} diff --git a/src/parsers/manifest/dash/wasm-parser/ts/generators/MPD.ts b/src/parsers/manifest/dash/wasm-parser/ts/generators/MPD.ts index aa3822fd71..3bd5324aa5 100644 --- a/src/parsers/manifest/dash/wasm-parser/ts/generators/MPD.ts +++ b/src/parsers/manifest/dash/wasm-parser/ts/generators/MPD.ts @@ -28,6 +28,7 @@ import { AttributeName, TagName } from "../types.ts"; import { parseString } from "../utils.ts"; import { generateBaseUrlAttrParser } from "./BaseURL.ts"; import { generateContentProtectionAttrParser } from "./ContentProtection.ts"; +import { generateDescriptorParsers } from "./Descriptor.ts"; import { generateLocationAttrParser } from "./Location.ts"; import { generatePeriodAttrParser, generatePeriodChildrenParser } from "./Period.ts"; import { generateSchemeAttrParser } from "./Scheme.ts"; @@ -58,6 +59,22 @@ export function generateMPDChildrenParser( break; } + case TagName.EssentialProperty: + case TagName.SupplementalProperty: { + const descriptor = { + attributes: {}, + children: { UrlQueryInfo: [], ExtUrlQueryInfo: [] }, + }; + const destination = + nodeId === TagName.EssentialProperty + ? mpdChildren.EssentialProperty + : mpdChildren.SupplementalProperty; + destination.push(descriptor); + const parsers = generateDescriptorParsers(descriptor, linearMemory, parsersStack); + parsersStack.pushParsers(nodeId, parsers.children, parsers.attributes); + break; + } + case TagName.Location: { const location: ILocationIntermediateRepresentation = { value: "", @@ -80,6 +97,7 @@ export function generateMPDChildrenParser( SegmentTemplate: [], EventStream: [], ContentProtection: [], + SupplementalProperty: [], }, attributes: {}, }; diff --git a/src/parsers/manifest/dash/wasm-parser/ts/generators/Period.ts b/src/parsers/manifest/dash/wasm-parser/ts/generators/Period.ts index ad2c2f4820..8cf62296a6 100644 --- a/src/parsers/manifest/dash/wasm-parser/ts/generators/Period.ts +++ b/src/parsers/manifest/dash/wasm-parser/ts/generators/Period.ts @@ -30,6 +30,7 @@ import { } from "./AdaptationSet.ts"; import { generateBaseUrlAttrParser } from "./BaseURL.ts"; import { generateContentProtectionAttrParser } from "./ContentProtection.ts"; +import { generateDescriptorParsers } from "./Descriptor.ts"; import { generateEventStreamAttrParser, generateEventStreamChildrenParser, @@ -146,6 +147,17 @@ export function generatePeriodChildrenParser( break; } + case TagName.SupplementalProperty: { + const descriptor = { + attributes: {}, + children: { UrlQueryInfo: [], ExtUrlQueryInfo: [] }, + }; + periodChildren.SupplementalProperty.push(descriptor); + const parsers = generateDescriptorParsers(descriptor, linearMemory, parsersStack); + parsersStack.pushParsers(nodeId, parsers.children, parsers.attributes); + break; + } + default: // Allows to make sure we're not mistakenly closing a re-opened // tag. diff --git a/src/parsers/manifest/dash/wasm-parser/ts/generators/Representation.ts b/src/parsers/manifest/dash/wasm-parser/ts/generators/Representation.ts index f44a330d44..a8ba6995cf 100644 --- a/src/parsers/manifest/dash/wasm-parser/ts/generators/Representation.ts +++ b/src/parsers/manifest/dash/wasm-parser/ts/generators/Representation.ts @@ -26,6 +26,7 @@ import { AttributeName, TagName } from "../types.ts"; import { parseString } from "../utils.ts"; import { generateBaseUrlAttrParser } from "./BaseURL.ts"; import { generateContentProtectionAttrParser } from "./ContentProtection.ts"; +import { generateDescriptorParsers } from "./Descriptor.ts"; import { generateSchemeAttrParser } from "./Scheme.ts"; import { generateSegmentBaseAttrParser, @@ -87,25 +88,19 @@ export function generateRepresentationChildrenParser( break; } - case TagName.EssentialProperty: { - const essentialProperty = { attributes: {} }; - childrenObj.EssentialProperty.push(essentialProperty); - const attributeParser = generateSchemeAttrParser( - essentialProperty.attributes, - linearMemory, - ); - parsersStack.pushParsers(nodeId, noop, attributeParser); - break; - } - + case TagName.EssentialProperty: case TagName.SupplementalProperty: { - const supplementalProperty = { attributes: {} }; - childrenObj.SupplementalProperty.push(supplementalProperty); - const attributeParser = generateSchemeAttrParser( - supplementalProperty.attributes, - linearMemory, - ); - parsersStack.pushParsers(nodeId, noop, attributeParser); + const descriptor = { + attributes: {}, + children: { UrlQueryInfo: [], ExtUrlQueryInfo: [] }, + }; + const destination = + nodeId === TagName.EssentialProperty + ? childrenObj.EssentialProperty + : childrenObj.SupplementalProperty; + destination.push(descriptor); + const parsers = generateDescriptorParsers(descriptor, linearMemory, parsersStack); + parsersStack.pushParsers(nodeId, parsers.children, parsers.attributes); break; } diff --git a/src/parsers/manifest/dash/wasm-parser/ts/generators/XLink.ts b/src/parsers/manifest/dash/wasm-parser/ts/generators/XLink.ts index 9f16431be1..bcab2d6909 100644 --- a/src/parsers/manifest/dash/wasm-parser/ts/generators/XLink.ts +++ b/src/parsers/manifest/dash/wasm-parser/ts/generators/XLink.ts @@ -45,6 +45,7 @@ export function generateXLinkChildrenParser( SegmentTemplate: [], EventStream: [], ContentProtection: [], + SupplementalProperty: [], }, attributes: {}, }; diff --git a/src/parsers/manifest/dash/wasm-parser/ts/generators/root.ts b/src/parsers/manifest/dash/wasm-parser/ts/generators/root.ts index e9498e9bed..ef967bc303 100644 --- a/src/parsers/manifest/dash/wasm-parser/ts/generators/root.ts +++ b/src/parsers/manifest/dash/wasm-parser/ts/generators/root.ts @@ -39,6 +39,8 @@ export function generateRootChildrenParser( rootObj.mpd = { children: { BaseURL: [], + EssentialProperty: [], + SupplementalProperty: [], Location: [], Period: [], UTCTiming: [], diff --git a/src/parsers/manifest/dash/wasm-parser/ts/types.ts b/src/parsers/manifest/dash/wasm-parser/ts/types.ts index fab2a106e3..795e302036 100644 --- a/src/parsers/manifest/dash/wasm-parser/ts/types.ts +++ b/src/parsers/manifest/dash/wasm-parser/ts/types.ts @@ -117,6 +117,12 @@ export const enum TagName { /// Indicate a `` node. Location = 23, + + /// Indicate a `` node. + UrlQueryInfo = 24, + + /// Indicate an `` node. + ExtUrlQueryInfo = 25, } /** @@ -303,4 +309,9 @@ export const enum AttributeName { EndNumber = 76, // f64 SupplementalCodecs = 77, // String + + QueryTemplate = 81, // String + IncludeInRequests = 82, // String + UseMpdUrlQuery = 83, // Boolean + QueryString = 84, // String } diff --git a/tests/unit/src/parsers/manifest/dash/common/get_http_utc-timing_url.test.ts b/tests/unit/src/parsers/manifest/dash/common/get_http_utc-timing_url.test.ts index 26ca7945b8..3d71ed96ff 100644 --- a/tests/unit/src/parsers/manifest/dash/common/get_http_utc-timing_url.test.ts +++ b/tests/unit/src/parsers/manifest/dash/common/get_http_utc-timing_url.test.ts @@ -11,6 +11,8 @@ describe("DASH Parser - getHTTPUTCTimingURL", () => { const mpdIR: IMPDIntermediateRepresentation = { children: { BaseURL: [], + EssentialProperty: [], + SupplementalProperty: [], Location: [], Period: [], UTCTiming: [], @@ -25,6 +27,8 @@ describe("DASH Parser - getHTTPUTCTimingURL", () => { const mpdIR: IMPDIntermediateRepresentation = { children: { BaseURL: [], + EssentialProperty: [], + SupplementalProperty: [], Location: [], Period: [], UTCTiming: [ @@ -52,6 +56,8 @@ describe("DASH Parser - getHTTPUTCTimingURL", () => { const mpdIR: IMPDIntermediateRepresentation = { children: { BaseURL: [], + EssentialProperty: [], + SupplementalProperty: [], Location: [], Period: [], ContentProtection: [], @@ -83,6 +89,8 @@ describe("DASH Parser - getHTTPUTCTimingURL", () => { const mpdIR: IMPDIntermediateRepresentation = { children: { BaseURL: [], + EssentialProperty: [], + SupplementalProperty: [], Location: [], Period: [], ContentProtection: [], @@ -104,6 +112,8 @@ describe("DASH Parser - getHTTPUTCTimingURL", () => { const mpdIR: IMPDIntermediateRepresentation = { children: { BaseURL: [], + EssentialProperty: [], + SupplementalProperty: [], Location: [], Period: [], ContentProtection: [], @@ -137,6 +147,8 @@ describe("DASH Parser - getHTTPUTCTimingURL", () => { const mpdIR: IMPDIntermediateRepresentation = { children: { BaseURL: [], + EssentialProperty: [], + SupplementalProperty: [], Location: [], Period: [], ContentProtection: [], diff --git a/tests/unit/src/parsers/manifest/dash/js-parser/node_parsers/Descriptor.test.ts b/tests/unit/src/parsers/manifest/dash/js-parser/node_parsers/Descriptor.test.ts new file mode 100644 index 0000000000..8d6a0dec9a --- /dev/null +++ b/tests/unit/src/parsers/manifest/dash/js-parser/node_parsers/Descriptor.test.ts @@ -0,0 +1,70 @@ +import { describe, expect, it } from "vitest"; +import parseDescriptor from "../../../../../../../../src/parsers/manifest/dash/js-parser/node_parsers/Descriptor.ts"; +import type { ITNode } from "../../../../../../../../src/utils/xml-parser.ts"; +import { parseXml } from "../../../../../../../../src/utils/xml-parser.ts"; + +describe("DASH Node Parsers - Descriptor", () => { + it("parses the common descriptor attributes", () => { + const element = parseXml( + '', + )[0] as ITNode; + + expect(parseDescriptor(element)).toEqual([ + { + attributes: { + id: "property-id", + schemeIdUri: "urn:example", + value: "yes", + }, + children: { UrlQueryInfo: [], ExtUrlQueryInfo: [] }, + }, + [], + ]); + }); + + it("ignores unknown attributes and children", () => { + const element = parseXml( + '', + )[0] as ITNode; + + expect(parseDescriptor(element)).toEqual([ + { + attributes: {}, + children: { UrlQueryInfo: [], ExtUrlQueryInfo: [] }, + }, + [], + ]); + }); + + it("parses Annex I URL query information children", () => { + const element = parseXml( + ` + + + `, + )[0] as ITNode; + + const [descriptor, warnings] = parseDescriptor(element); + expect(descriptor.children).toEqual({ + UrlQueryInfo: [{ attributes: { queryString: "token=1", useMpdUrlQuery: true } }], + ExtUrlQueryInfo: [ + { + attributes: { + queryTemplate: "$querypart$", + includeInRequests: "mpd segment", + }, + }, + ], + }); + expect(warnings).toEqual([]); + }); + + it("forwards warnings from Annex I children", () => { + const element = parseXml( + '', + )[0] as ITNode; + + const [, warnings] = parseDescriptor(element); + expect(warnings).toHaveLength(1); + }); +}); diff --git a/tests/unit/src/parsers/manifest/dash/js-parser/node_parsers/MPD.test.ts b/tests/unit/src/parsers/manifest/dash/js-parser/node_parsers/MPD.test.ts new file mode 100644 index 0000000000..e28536b0ce --- /dev/null +++ b/tests/unit/src/parsers/manifest/dash/js-parser/node_parsers/MPD.test.ts @@ -0,0 +1,38 @@ +import { describe, expect, it } from "vitest"; +import { createMPDIntermediateRepresentation } from "../../../../../../../../src/parsers/manifest/dash/js-parser/node_parsers/MPD.ts"; +import type { ITNode } from "../../../../../../../../src/utils/xml-parser.ts"; +import { parseXml } from "../../../../../../../../src/utils/xml-parser.ts"; + +describe("DASH Node Parsers - MPD", () => { + it("parses EssentialProperty and SupplementalProperty descriptors", () => { + const element = parseXml( + ` + + + `, + )[0] as ITNode; + + const [mpd, warnings] = createMPDIntermediateRepresentation(element, ""); + expect(mpd.children.EssentialProperty).toEqual([ + { + attributes: { + id: "foo", + schemeIdUri: "urn:example:essential", + value: "1", + }, + children: { UrlQueryInfo: [], ExtUrlQueryInfo: [] }, + }, + ]); + expect(mpd.children.SupplementalProperty).toEqual([ + { + attributes: { + id: "bar", + schemeIdUri: "urn:example:supplemental", + value: "2", + }, + children: { UrlQueryInfo: [], ExtUrlQueryInfo: [] }, + }, + ]); + expect(warnings).toEqual([]); + }); +}); diff --git a/tests/unit/src/parsers/manifest/dash/js-parser/node_parsers/Period.test.ts b/tests/unit/src/parsers/manifest/dash/js-parser/node_parsers/Period.test.ts new file mode 100644 index 0000000000..944eacb647 --- /dev/null +++ b/tests/unit/src/parsers/manifest/dash/js-parser/node_parsers/Period.test.ts @@ -0,0 +1,30 @@ +import { describe, expect, it } from "vitest"; +import { createPeriodIntermediateRepresentation } from "../../../../../../../../src/parsers/manifest/dash/js-parser/node_parsers/Period.ts"; +import type { ITNode } from "../../../../../../../../src/utils/xml-parser.ts"; +import { parseXml } from "../../../../../../../../src/utils/xml-parser.ts"; + +describe("DASH Node Parsers - Period", () => { + it("parses SupplementalProperty descriptors", () => { + const element = parseXml( + ` + + + + `, + )[0] as ITNode; + + const [period, warnings] = createPeriodIntermediateRepresentation(element, ""); + expect(period.children.SupplementalProperty).toEqual([ + { + attributes: { schemeIdUri: "urn:mpeg:dash:urlparam:2014" }, + children: { + UrlQueryInfo: [ + { attributes: { queryString: "token=1", useMpdUrlQuery: true } }, + ], + ExtUrlQueryInfo: [], + }, + }, + ]); + expect(warnings).toEqual([]); + }); +});