rx-player
Version:
Canal+ HTML5 Video Player
383 lines (362 loc) • 13.4 kB
text/typescript
/**
* 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 {
IAdaptationSetAttributes,
IAdaptationSetChildren,
ISegmentListIntermediateRepresentation,
} from "../../../node_parser_types";
import type { IAttributeParser, IChildrenParser } from "../parsers_stack";
import type ParsersStack from "../parsers_stack";
import { AttributeName, TagName } from "../types";
import { parseFloatOrBool, parseString } from "../utils";
import { generateBaseUrlAttrParser } from "./BaseURL";
import { generateContentComponentAttrParser } from "./ContentComponent";
import { generateContentProtectionAttrParser } from "./ContentProtection";
import { generateLabelElementParser } from "./Label";
import {
generateRepresentationAttrParser,
generateRepresentationChildrenParser,
} from "./Representation";
import { generateSchemeAttrParser } from "./Scheme";
import { generateSegmentBaseAttrParser } from "./SegmentBase";
import { generateSegmentListChildrenParser } from "./SegmentList";
import { generateSegmentTemplateAttrParser } from "./SegmentTemplate";
/**
* Generate a "children parser" once inside a `AdaptationSet` node.
* @param {Object} adaptationSetChildren
* @param {WebAssembly.Memory} linearMemory
* @param {ParsersStack} parsersStack
* @returns {Function}
*/
export function generateAdaptationSetChildrenParser(
adaptationSetChildren: IAdaptationSetChildren,
linearMemory: WebAssembly.Memory,
parsersStack: ParsersStack,
): IChildrenParser {
return function onRootChildren(nodeId: number) {
switch (nodeId) {
case TagName.Accessibility: {
const accessibility = {};
if (adaptationSetChildren.accessibilities === undefined) {
adaptationSetChildren.accessibilities = [];
}
adaptationSetChildren.accessibilities.push(accessibility);
const schemeAttrParser = generateSchemeAttrParser(accessibility, linearMemory);
parsersStack.pushParsers(nodeId, noop, schemeAttrParser);
break;
}
case TagName.BaseURL: {
const baseUrl = { value: "", attributes: {} };
adaptationSetChildren.baseURLs.push(baseUrl);
const attributeParser = generateBaseUrlAttrParser(baseUrl, linearMemory);
parsersStack.pushParsers(nodeId, noop, attributeParser);
break;
}
case TagName.ContentComponent: {
const contentComponent = {};
adaptationSetChildren.contentComponent = contentComponent;
parsersStack.pushParsers(
nodeId,
noop,
generateContentComponentAttrParser(contentComponent, linearMemory),
);
break;
}
case TagName.ContentProtection: {
const contentProtection = {
children: { cencPssh: [] },
attributes: {},
};
if (adaptationSetChildren.contentProtections === undefined) {
adaptationSetChildren.contentProtections = [];
}
adaptationSetChildren.contentProtections.push(contentProtection);
const contentProtAttrParser = generateContentProtectionAttrParser(
contentProtection,
linearMemory,
);
parsersStack.pushParsers(nodeId, noop, contentProtAttrParser);
break;
}
case TagName.EssentialProperty: {
const essentialProperty = {};
if (adaptationSetChildren.essentialProperties === undefined) {
adaptationSetChildren.essentialProperties = [];
}
adaptationSetChildren.essentialProperties.push(essentialProperty);
const childrenParser = noop; // EssentialProperty have no sub-element
const attributeParser = generateSchemeAttrParser(essentialProperty, linearMemory);
parsersStack.pushParsers(nodeId, childrenParser, attributeParser);
break;
}
case TagName.InbandEventStream: {
const inbandEvent = {};
if (adaptationSetChildren.inbandEventStreams === undefined) {
adaptationSetChildren.inbandEventStreams = [];
}
adaptationSetChildren.inbandEventStreams.push(inbandEvent);
const childrenParser = noop; // InbandEventStream have no sub-element
const attributeParser = generateSchemeAttrParser(inbandEvent, linearMemory);
parsersStack.pushParsers(nodeId, childrenParser, attributeParser);
break;
}
case TagName.Representation: {
const representationObj = {
children: { baseURLs: [] },
attributes: {},
};
adaptationSetChildren.representations.push(representationObj);
const childrenParser = generateRepresentationChildrenParser(
representationObj.children,
linearMemory,
parsersStack,
);
const attributeParser = generateRepresentationAttrParser(
representationObj.attributes,
linearMemory,
);
parsersStack.pushParsers(nodeId, childrenParser, attributeParser);
break;
}
case TagName.Role: {
const role = {};
if (adaptationSetChildren.roles === undefined) {
adaptationSetChildren.roles = [];
}
adaptationSetChildren.roles.push(role);
const attributeParser = generateSchemeAttrParser(role, linearMemory);
parsersStack.pushParsers(nodeId, noop, attributeParser);
break;
}
case TagName.SupplementalProperty: {
const supplementalProperty = {};
if (adaptationSetChildren.supplementalProperties === undefined) {
adaptationSetChildren.supplementalProperties = [];
}
adaptationSetChildren.supplementalProperties.push(supplementalProperty);
const attributeParser = generateSchemeAttrParser(
supplementalProperty,
linearMemory,
);
parsersStack.pushParsers(nodeId, noop, attributeParser);
break;
}
case TagName.SegmentBase: {
const segmentBaseObj = {};
adaptationSetChildren.segmentBase = segmentBaseObj;
const attributeParser = generateSegmentBaseAttrParser(
segmentBaseObj,
linearMemory,
);
parsersStack.pushParsers(nodeId, noop, attributeParser);
break;
}
case TagName.SegmentList: {
const segmentListObj: ISegmentListIntermediateRepresentation = {
list: [],
};
adaptationSetChildren.segmentList = segmentListObj;
const childrenParser = generateSegmentListChildrenParser(
segmentListObj,
linearMemory,
parsersStack,
);
// Re-use SegmentBase attribute parse as we should have the same attributes
const attributeParser = generateSegmentBaseAttrParser(
segmentListObj,
linearMemory,
);
parsersStack.pushParsers(nodeId, childrenParser, attributeParser);
break;
}
case TagName.SegmentTemplate: {
const stObj = {};
adaptationSetChildren.segmentTemplate = stObj;
parsersStack.pushParsers(
nodeId,
noop, // SegmentTimeline as treated like an attribute
generateSegmentTemplateAttrParser(stObj, linearMemory),
);
break;
}
case TagName.Label: {
parsersStack.pushParsers(
nodeId,
noop, // Label as treated like an attribute
generateLabelElementParser(adaptationSetChildren, linearMemory),
);
break;
}
default:
// Allows to make sure we're not mistakenly closing a re-opened
// tag.
parsersStack.pushParsers(nodeId, noop, noop);
break;
}
};
}
/**
* @param {Object} adaptationAttrs
* @param {WebAssembly.Memory} linearMemory
* @returns {Function}
*/
export function generateAdaptationSetAttrParser(
adaptationAttrs: IAdaptationSetAttributes,
linearMemory: WebAssembly.Memory,
): IAttributeParser {
const textDecoder = new TextDecoder();
return function onAdaptationSetAttribute(attr: number, ptr: number, len: number) {
const dataView = new DataView(linearMemory.buffer);
switch (attr) {
case AttributeName.Id:
adaptationAttrs.id = parseString(textDecoder, linearMemory.buffer, ptr, len);
break;
case AttributeName.Group:
adaptationAttrs.group = dataView.getFloat64(ptr, true);
break;
case AttributeName.Language:
adaptationAttrs.language = parseString(
textDecoder,
linearMemory.buffer,
ptr,
len,
);
break;
case AttributeName.ContentType:
adaptationAttrs.contentType = parseString(
textDecoder,
linearMemory.buffer,
ptr,
len,
);
break;
case AttributeName.Par:
adaptationAttrs.par = parseString(textDecoder, linearMemory.buffer, ptr, len);
break;
case AttributeName.MinBandwidth:
adaptationAttrs.minBitrate = dataView.getFloat64(ptr, true);
break;
case AttributeName.MaxBandwidth:
adaptationAttrs.maxBitrate = dataView.getFloat64(ptr, true);
break;
case AttributeName.MinWidth:
adaptationAttrs.minWidth = dataView.getFloat64(ptr, true);
break;
case AttributeName.MaxWidth:
adaptationAttrs.maxWidth = dataView.getFloat64(ptr, true);
break;
case AttributeName.MinHeight:
adaptationAttrs.minHeight = dataView.getFloat64(ptr, true);
break;
case AttributeName.MaxHeight:
adaptationAttrs.maxHeight = dataView.getFloat64(ptr, true);
break;
case AttributeName.MinFrameRate:
adaptationAttrs.minFrameRate = dataView.getFloat64(ptr, true);
break;
case AttributeName.MaxFrameRate:
adaptationAttrs.maxFrameRate = dataView.getFloat64(ptr, true);
break;
case AttributeName.SelectionPriority:
adaptationAttrs.selectionPriority = dataView.getFloat64(ptr, true);
break;
case AttributeName.SegmentAlignment:
adaptationAttrs.segmentAlignment = parseFloatOrBool(
dataView.getFloat64(ptr, true),
);
break;
case AttributeName.SubsegmentAlignment:
adaptationAttrs.subsegmentAlignment = parseFloatOrBool(
dataView.getFloat64(ptr, true),
);
break;
case AttributeName.BitstreamSwitching:
adaptationAttrs.bitstreamSwitching = dataView.getFloat64(ptr, true) !== 0;
break;
case AttributeName.AudioSamplingRate:
adaptationAttrs.audioSamplingRate = parseString(
textDecoder,
linearMemory.buffer,
ptr,
len,
);
break;
case AttributeName.Codecs:
adaptationAttrs.codecs = parseString(textDecoder, linearMemory.buffer, ptr, len);
break;
case AttributeName.SupplementalCodecs:
adaptationAttrs.supplementalCodecs = parseString(
textDecoder,
linearMemory.buffer,
ptr,
len,
);
break;
case AttributeName.Profiles:
adaptationAttrs.profiles = parseString(
textDecoder,
linearMemory.buffer,
ptr,
len,
);
break;
case AttributeName.SegmentProfiles:
adaptationAttrs.segmentProfiles = parseString(
textDecoder,
linearMemory.buffer,
ptr,
len,
);
break;
case AttributeName.MimeType:
adaptationAttrs.mimeType = parseString(
textDecoder,
linearMemory.buffer,
ptr,
len,
);
break;
case AttributeName.CodingDependency:
adaptationAttrs.codingDependency = dataView.getFloat64(ptr, true) !== 0;
break;
case AttributeName.FrameRate:
adaptationAttrs.frameRate = dataView.getFloat64(ptr, true);
break;
case AttributeName.Height:
adaptationAttrs.height = dataView.getFloat64(ptr, true);
break;
case AttributeName.Width:
adaptationAttrs.width = dataView.getFloat64(ptr, true);
break;
case AttributeName.MaxPlayoutRate:
adaptationAttrs.maxPlayoutRate = dataView.getFloat64(ptr, true);
break;
case AttributeName.MaxSAPPeriod:
adaptationAttrs.maximumSAPPeriod = dataView.getFloat64(ptr, true);
break;
case AttributeName.AvailabilityTimeOffset:
adaptationAttrs.availabilityTimeOffset = dataView.getFloat64(ptr, true);
break;
case AttributeName.AvailabilityTimeComplete:
adaptationAttrs.availabilityTimeComplete = dataView.getUint8(0) === 0;
break;
// TODO
// case AttributeName.StartsWithSap:
// adaptationAttrs.startsWithSap = dataView.getFloat64(ptr, true);
}
};
}