/** * 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 type { ITNode } from "../../../../../utils/xml-parser"; import type { IRepresentationAttributes, IRepresentationChildren, IRepresentationIntermediateRepresentation, } from "../../node_parser_types"; import parseBaseURL from "./BaseURL"; import parseContentProtection from "./ContentProtection"; import parseSegmentBase from "./SegmentBase"; import parseSegmentList from "./SegmentList"; import parseSegmentTemplate from "./SegmentTemplate"; import { MPDError, parseBoolean, parseMaybeDividedNumber, parseMPDFloat, parseMPDInteger, parseScheme, ValueParser, } from "./utils"; /** * @param {Array.} representationChildren * @returns {Object} */ function parseRepresentationChildren( representationChildren: Array, ): [IRepresentationChildren, Error[]] { const children: IRepresentationChildren = { baseURLs: [], }; const contentProtections = []; let warnings: Error[] = []; for (let i = 0; i < representationChildren.length; i++) { const currentElement = representationChildren[i]; if (typeof currentElement === "string") { continue; } switch (currentElement.tagName) { case "BaseURL": { const [baseURLObj, baseURLWarnings] = parseBaseURL(currentElement); if (baseURLObj !== undefined) { children.baseURLs.push(baseURLObj); } warnings = warnings.concat(baseURLWarnings); break; } case "InbandEventStream": if (children.inbandEventStreams === undefined) { children.inbandEventStreams = []; } children.inbandEventStreams.push(parseScheme(currentElement)); break; case "SegmentBase": { const [segmentBase, segmentBaseWarnings] = parseSegmentBase(currentElement); children.segmentBase = segmentBase; if (segmentBaseWarnings.length > 0) { warnings = warnings.concat(segmentBaseWarnings); } break; } case "SegmentList": { const [segmentList, segmentListWarnings] = parseSegmentList(currentElement); warnings = warnings.concat(segmentListWarnings); children.segmentList = segmentList; break; } case "SegmentTemplate": { const [segmentTemplate, segmentTemplateWarnings] = parseSegmentTemplate(currentElement); warnings = warnings.concat(segmentTemplateWarnings); children.segmentTemplate = segmentTemplate; break; } case "ContentProtection": { const [contentProtection, contentProtectionWarnings] = parseContentProtection(currentElement); if (contentProtectionWarnings.length > 0) { warnings = warnings.concat(contentProtectionWarnings); } if (contentProtection !== undefined) { contentProtections.push(contentProtection); } break; } case "EssentialProperty": if (isNullOrUndefined(children.essentialProperties)) { children.essentialProperties = [parseScheme(currentElement)]; } else { children.essentialProperties.push(parseScheme(currentElement)); } break; case "SupplementalProperty": if (isNullOrUndefined(children.supplementalProperties)) { children.supplementalProperties = [parseScheme(currentElement)]; } else { children.supplementalProperties.push(parseScheme(currentElement)); } break; } } if (contentProtections.length > 0) { children.contentProtections = contentProtections; } return [children, warnings]; } /** * @param {Object} root * @returns {Array} */ function parseRepresentationAttributes( root: ITNode, ): [IRepresentationAttributes, Error[]] { const attributes: IRepresentationAttributes = {}; const warnings: Error[] = []; const parseValue = ValueParser(attributes, warnings); for (const attributeName of Object.keys(root.attributes)) { const attributeVal = root.attributes[attributeName]; if (isNullOrUndefined(attributeVal)) { continue; } switch (attributeName) { case "audioSamplingRate": attributes.audioSamplingRate = attributeVal; break; case "bandwidth": parseValue(attributeVal, { asKey: "bitrate", parser: parseMPDInteger, dashName: "bandwidth", }); break; case "codecs": attributes.codecs = attributeVal; break; case "codingDependency": parseValue(attributeVal, { asKey: "codingDependency", parser: parseBoolean, dashName: "codingDependency", }); break; case "frameRate": parseValue(attributeVal, { asKey: "frameRate", parser: parseMaybeDividedNumber, dashName: "frameRate", }); break; case "height": parseValue(attributeVal, { asKey: "height", parser: parseMPDInteger, dashName: "height", }); break; case "id": attributes.id = attributeVal; break; case "maxPlayoutRate": parseValue(attributeVal, { asKey: "maxPlayoutRate", parser: parseMPDFloat, dashName: "maxPlayoutRate", }); break; case "maximumSAPPeriod": parseValue(attributeVal, { asKey: "maximumSAPPeriod", parser: parseMPDFloat, dashName: "maximumSAPPeriod", }); break; case "mimeType": attributes.mimeType = attributeVal; break; case "profiles": attributes.profiles = attributeVal; break; case "qualityRanking": parseValue(attributeVal, { asKey: "qualityRanking", parser: parseMPDInteger, dashName: "qualityRanking", }); break; case "scte214:supplementalCodecs": attributes.supplementalCodecs = attributeVal; break; case "segmentProfiles": attributes.segmentProfiles = attributeVal; break; case "width": parseValue(attributeVal, { asKey: "width", parser: parseMPDInteger, dashName: "width", }); break; case "availabilityTimeOffset": parseValue(attributeVal, { asKey: "availabilityTimeOffset", parser: parseMPDFloat, dashName: "availabilityTimeOffset", }); break; case "availabilityTimeComplete": parseValue(attributeVal, { asKey: "availabilityTimeComplete", parser: parseBoolean, dashName: "availabilityTimeComplete", }); break; } } if (attributes.bitrate === undefined) { warnings.push(new MPDError("No bitrate found on a Representation")); } return [attributes, warnings]; } /** * @param {Object} representationElement * @returns {Array} */ export function createRepresentationIntermediateRepresentation( representationElement: ITNode, ): [IRepresentationIntermediateRepresentation, Error[]] { const [children, childrenWarnings] = parseRepresentationChildren( representationElement.children, ); const [attributes, attrsWarnings] = parseRepresentationAttributes(representationElement); const warnings = childrenWarnings.concat(attrsWarnings); return [{ children, attributes }, warnings]; }