/** * Copyright 2015 CANAL+ Group * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import noop from "../../../../../../utils/noop"; import type { IMPDAttributes, IMPDChildren } from "../../../node_parser_types"; import type { IAttributeParser, IChildrenParser } from "../parsers_stack"; import type ParsersStack from "../parsers_stack"; import { AttributeName, TagName } from "../types"; import { parseString } from "../utils"; import { generateBaseUrlAttrParser } from "./BaseURL"; import { generateContentProtectionAttrParser } from "./ContentProtection"; import { generatePeriodAttrParser, generatePeriodChildrenParser } from "./Period"; import { generateSchemeAttrParser } from "./Scheme"; /** * Generate a "children parser" once inside an `MPD` node. * @param {Object} mpdChildren * @param {WebAssembly.Memory} linearMemory * @param {ParsersStack} parsersStack * @param {ArrayBuffer} fullMpd * @returns {Function} */ export function generateMPDChildrenParser( mpdChildren: IMPDChildren, linearMemory: WebAssembly.Memory, parsersStack: ParsersStack, fullMpd: ArrayBufferLike, ): IChildrenParser { return function onRootChildren(nodeId: number) { switch (nodeId) { case TagName.BaseURL: { const baseUrl = { value: "", attributes: {} }; mpdChildren.baseURLs.push(baseUrl); const childrenParser = noop; // BaseURL have no sub-element const attributeParser = generateBaseUrlAttrParser(baseUrl, linearMemory); parsersStack.pushParsers(nodeId, childrenParser, attributeParser); break; } case TagName.Period: { const period = { children: { adaptations: [], baseURLs: [], eventStreams: [] }, attributes: {}, }; mpdChildren.periods.push(period); const childrenParser = generatePeriodChildrenParser( period.children, linearMemory, parsersStack, fullMpd, ); const attributeParser = generatePeriodAttrParser(period.attributes, linearMemory); parsersStack.pushParsers(nodeId, childrenParser, attributeParser); break; } case TagName.UtcTiming: { const utcTiming = {}; mpdChildren.utcTimings.push(utcTiming); const childrenParser = noop; // UTCTiming have no sub-element const attributeParser = generateSchemeAttrParser(utcTiming, linearMemory); parsersStack.pushParsers(nodeId, childrenParser, attributeParser); break; } case TagName.ContentProtection: { const contentProtection = { children: { cencPssh: [] }, attributes: {}, }; if (mpdChildren.contentProtections === undefined) { mpdChildren.contentProtections = []; } mpdChildren.contentProtections.push(contentProtection); const contentProtAttrParser = generateContentProtectionAttrParser( contentProtection, linearMemory, ); parsersStack.pushParsers(nodeId, noop, contentProtAttrParser); break; } default: // Allows to make sure we're not mistakenly closing a re-opened // tag. parsersStack.pushParsers(nodeId, noop, noop); break; } }; } export function generateMPDAttrParser( mpdChildren: IMPDChildren, mpdAttrs: IMPDAttributes, linearMemory: WebAssembly.Memory, ): IAttributeParser { let dataView; const textDecoder = new TextDecoder(); return function onMPDAttribute(attr: number, ptr: number, len: number) { switch (attr) { case AttributeName.Id: mpdAttrs.id = parseString(textDecoder, linearMemory.buffer, ptr, len); break; case AttributeName.Profiles: mpdAttrs.profiles = parseString(textDecoder, linearMemory.buffer, ptr, len); break; case AttributeName.Type: mpdAttrs.type = parseString(textDecoder, linearMemory.buffer, ptr, len); break; case AttributeName.AvailabilityStartTime: { const startTime = parseString(textDecoder, linearMemory.buffer, ptr, len); mpdAttrs.availabilityStartTime = new Date(startTime).getTime() / 1000; break; } case AttributeName.AvailabilityEndTime: { const endTime = parseString(textDecoder, linearMemory.buffer, ptr, len); mpdAttrs.availabilityEndTime = new Date(endTime).getTime() / 1000; break; } case AttributeName.PublishTime: { const publishTime = parseString(textDecoder, linearMemory.buffer, ptr, len); mpdAttrs.publishTime = new Date(publishTime).getTime() / 1000; break; } case AttributeName.MediaPresentationDuration: dataView = new DataView(linearMemory.buffer); mpdAttrs.duration = dataView.getFloat64(ptr, true); break; case AttributeName.MinimumUpdatePeriod: dataView = new DataView(linearMemory.buffer); mpdAttrs.minimumUpdatePeriod = dataView.getFloat64(ptr, true); break; case AttributeName.MinBufferTime: dataView = new DataView(linearMemory.buffer); mpdAttrs.minBufferTime = dataView.getFloat64(ptr, true); break; case AttributeName.TimeShiftBufferDepth: dataView = new DataView(linearMemory.buffer); mpdAttrs.timeShiftBufferDepth = dataView.getFloat64(ptr, true); break; case AttributeName.SuggestedPresentationDelay: dataView = new DataView(linearMemory.buffer); mpdAttrs.suggestedPresentationDelay = dataView.getFloat64(ptr, true); break; case AttributeName.MaxSegmentDuration: dataView = new DataView(linearMemory.buffer); mpdAttrs.maxSegmentDuration = dataView.getFloat64(ptr, true); break; case AttributeName.MaxSubsegmentDuration: dataView = new DataView(linearMemory.buffer); mpdAttrs.maxSubsegmentDuration = dataView.getFloat64(ptr, true); break; case AttributeName.Location: { const location = parseString(textDecoder, linearMemory.buffer, ptr, len); mpdChildren.locations.push(location); break; } case AttributeName.Namespace: { const xmlNs = { key: "", value: "" }; dataView = new DataView(linearMemory.buffer); let offset = ptr; const keySize = dataView.getUint32(offset); offset += 4; xmlNs.key = parseString(textDecoder, linearMemory.buffer, offset, keySize); offset += keySize; const valSize = dataView.getUint32(offset); offset += 4; xmlNs.value = parseString(textDecoder, linearMemory.buffer, offset, valSize); if (mpdAttrs.namespaces === undefined) { mpdAttrs.namespaces = [xmlNs]; } else { mpdAttrs.namespaces.push(xmlNs); } break; } } }; }