/** * 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 isNullOrUndefined from "../../../../../utils/is_null_or_undefined"; import startsWith from "../../../../../utils/starts_with"; import type { ITNode } from "../../../../../utils/xml-parser"; import type { IBaseUrlIntermediateRepresentation, IContentProtectionIntermediateRepresentation, IMPDAttributes, IMPDChildren, IMPDIntermediateRepresentation, IPeriodIntermediateRepresentation, IScheme, } from "../../node_parser_types"; import parseBaseURL from "./BaseURL"; import parseContentProtection from "./ContentProtection"; import { createPeriodIntermediateRepresentation } from "./Period"; import { parseDateTime, parseDuration, parseScheme, textContent, ValueParser, } from "./utils"; /** * Parse children of the MPD's root into a simple object. * @param {Array.} mpdChildren * @returns {Array.} */ function parseMPDChildren( mpdChildren: Array, fullMpd: string, ): [IMPDChildren, Error[]] { const baseURLs: IBaseUrlIntermediateRepresentation[] = []; const locations: string[] = []; const periods: IPeriodIntermediateRepresentation[] = []; const utcTimings: IScheme[] = []; const contentProtections: IContentProtectionIntermediateRepresentation[] = []; let warnings: Error[] = []; for (let i = 0; i < mpdChildren.length; i++) { const currentNode = mpdChildren[i]; if (typeof currentNode === "string") { continue; } switch (currentNode.tagName) { case "BaseURL": { const [baseURLObj, baseURLWarnings] = parseBaseURL(currentNode); if (baseURLObj !== undefined) { baseURLs.push(baseURLObj); } warnings = warnings.concat(baseURLWarnings); break; } case "Location": locations.push(textContent(currentNode.children)); break; case "Period": { const [period, periodWarnings] = createPeriodIntermediateRepresentation( currentNode, fullMpd, ); periods.push(period); warnings = warnings.concat(periodWarnings); break; } case "UTCTiming": { const utcTiming = parseScheme(currentNode); utcTimings.push(utcTiming); break; } case "ContentProtection": { const [contentProtection, contentProtectionWarnings] = parseContentProtection(currentNode); if (contentProtectionWarnings.length > 0) { warnings = warnings.concat(contentProtectionWarnings); } if (contentProtection !== undefined) { contentProtections.push(contentProtection); } break; } } } return [{ baseURLs, locations, periods, utcTimings, contentProtections }, warnings]; } /** * @param {Object} root * @returns {Array.} */ function parseMPDAttributes(root: ITNode): [IMPDAttributes, Error[]] { const res: IMPDAttributes = {}; const warnings: Error[] = []; const parseValue = ValueParser(res, warnings); for (const attributeName of Object.keys(root.attributes)) { const attributeVal = root.attributes[attributeName]; if (isNullOrUndefined(attributeVal)) { continue; } switch (attributeName) { case "id": res.id = attributeVal; break; case "profiles": res.profiles = attributeVal; break; case "type": res.type = attributeVal; break; case "availabilityStartTime": parseValue(attributeVal, { asKey: "availabilityStartTime", parser: parseDateTime, dashName: "availabilityStartTime", }); break; case "availabilityEndTime": parseValue(attributeVal, { asKey: "availabilityEndTime", parser: parseDateTime, dashName: "availabilityEndTime", }); break; case "publishTime": parseValue(attributeVal, { asKey: "publishTime", parser: parseDateTime, dashName: "publishTime", }); break; case "mediaPresentationDuration": parseValue(attributeVal, { asKey: "duration", parser: parseDuration, dashName: "mediaPresentationDuration", }); break; case "minimumUpdatePeriod": parseValue(attributeVal, { asKey: "minimumUpdatePeriod", parser: parseDuration, dashName: "minimumUpdatePeriod", }); break; case "minBufferTime": parseValue(attributeVal, { asKey: "minBufferTime", parser: parseDuration, dashName: "minBufferTime", }); break; case "timeShiftBufferDepth": parseValue(attributeVal, { asKey: "timeShiftBufferDepth", parser: parseDuration, dashName: "timeShiftBufferDepth", }); break; case "suggestedPresentationDelay": parseValue(attributeVal, { asKey: "suggestedPresentationDelay", parser: parseDuration, dashName: "suggestedPresentationDelay", }); break; case "maxSegmentDuration": parseValue(attributeVal, { asKey: "maxSegmentDuration", parser: parseDuration, dashName: "maxSegmentDuration", }); break; case "maxSubsegmentDuration": parseValue(attributeVal, { asKey: "maxSubsegmentDuration", parser: parseDuration, dashName: "maxSubsegmentDuration", }); break; default: if (startsWith(attributeName, "xmlns:")) { if (res.namespaces === undefined) { res.namespaces = []; } res.namespaces.push({ key: attributeName.substring(6), value: attributeVal, }); } break; } } return [res, warnings]; } /** * @param {Object} root * @param {string} fullMpd * @returns {Array.} */ export function createMPDIntermediateRepresentation( root: ITNode, fullMpd: string, ): [IMPDIntermediateRepresentation, Error[]] { const [children, childrenWarnings] = parseMPDChildren(root.children, fullMpd); const [attributes, attrsWarnings] = parseMPDAttributes(root); const warnings = childrenWarnings.concat(attrsWarnings); return [{ children, attributes }, warnings]; }