rx-player
Version:
Canal+ HTML5 Video Player
216 lines (209 loc) • 8.02 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 type { IRepresentationIndex, IRepresentation } from "../../../../manifest";
import objectAssign from "../../../../utils/object_assign";
import type { IEMSG } from "../../../containers/isobmff";
import type {
IAdaptationSetIntermediateRepresentation,
IRepresentationIntermediateRepresentation,
ISegmentTemplateIntermediateRepresentation,
IScheme,
} from "../node_parser_types";
import type {
IBaseIndexContextArgument,
IListIndexContextArgument,
ITemplateIndexContextArgument,
ITimelineIndexContextArgument,
} from "./indexes";
import {
BaseRepresentationIndex,
ListRepresentationIndex,
TemplateRepresentationIndex,
TimelineRepresentationIndex,
} from "./indexes";
import type ManifestBoundsCalculator from "./manifest_bounds_calculator";
import type { IResolvedBaseUrl } from "./resolve_base_urls";
/**
* Parse the specific segment indexing information found in a representation
* into a IRepresentationIndex implementation.
* @param {Array.<Object>} representation
* @param {Object} context
* @returns {Array.<Object>}
*/
export default function parseRepresentationIndex(
representation: IRepresentationIntermediateRepresentation,
context: IRepresentationIndexContext,
): IRepresentationIndex {
const {
availabilityTimeOffset,
manifestBoundsCalculator,
isDynamic,
end: periodEnd,
start: periodStart,
receivedTime,
unsafelyBaseOnPreviousRepresentation,
inbandEventStreams,
isLastPeriod,
} = context;
const isEMSGWhitelisted = (inbandEvent: IEMSG): boolean => {
if (inbandEventStreams === undefined) {
return false;
}
return inbandEventStreams.some(
({ schemeIdUri }) => schemeIdUri === inbandEvent.schemeIdUri,
);
};
const reprIndexCtxt:
| ITimelineIndexContextArgument
| ITemplateIndexContextArgument
| IListIndexContextArgument
| IBaseIndexContextArgument = {
availabilityTimeComplete: undefined,
availabilityTimeOffset,
unsafelyBaseOnPreviousRepresentation,
isEMSGWhitelisted,
isLastPeriod,
manifestBoundsCalculator,
isDynamic,
periodEnd,
periodStart,
receivedTime,
representationBitrate: representation.attributes.bitrate,
representationId: representation.attributes.id,
};
let representationIndex: IRepresentationIndex;
if (representation.children.segmentBase !== undefined) {
const { segmentBase } = representation.children;
representationIndex = new BaseRepresentationIndex(segmentBase, reprIndexCtxt);
} else if (representation.children.segmentList !== undefined) {
const { segmentList } = representation.children;
representationIndex = new ListRepresentationIndex(segmentList, reprIndexCtxt);
} else if (
representation.children.segmentTemplate !== undefined ||
context.parentSegmentTemplates.length > 0
) {
const segmentTemplates = context.parentSegmentTemplates.slice();
const childSegmentTemplate = representation.children.segmentTemplate;
if (childSegmentTemplate !== undefined) {
segmentTemplates.push(childSegmentTemplate);
}
const segmentTemplate = objectAssign(
{},
...(segmentTemplates as [
ISegmentTemplateIntermediateRepresentation,
]) /* Ugly TS Hack */,
);
if (
segmentTemplate.availabilityTimeOffset !== undefined ||
context.availabilityTimeOffset !== undefined
) {
reprIndexCtxt.availabilityTimeOffset =
(segmentTemplate.availabilityTimeOffset ?? 0) +
(context.availabilityTimeOffset ?? 0);
}
if (
segmentTemplate.availabilityTimeComplete !== undefined ||
context.availabilityTimeComplete !== undefined
) {
reprIndexCtxt.availabilityTimeComplete =
segmentTemplate.availabilityTimeComplete ?? context.availabilityTimeComplete;
}
representationIndex = TimelineRepresentationIndex.isTimelineIndexArgument(
segmentTemplate,
)
? new TimelineRepresentationIndex(segmentTemplate, reprIndexCtxt)
: new TemplateRepresentationIndex(segmentTemplate, reprIndexCtxt);
} else {
const adaptationChildren = context.adaptation.children;
if (adaptationChildren.segmentBase !== undefined) {
const { segmentBase } = adaptationChildren;
representationIndex = new BaseRepresentationIndex(segmentBase, reprIndexCtxt);
} else if (adaptationChildren.segmentList !== undefined) {
const { segmentList } = adaptationChildren;
representationIndex = new ListRepresentationIndex(segmentList, reprIndexCtxt);
} else {
representationIndex = new TemplateRepresentationIndex(
{
duration: Number.MAX_VALUE,
timescale: 1,
startNumber: 0,
media: "",
},
reprIndexCtxt,
);
}
}
return representationIndex;
}
/** Supplementary context needed to parse a RepresentationIndex. */
export interface IRepresentationIndexContext {
/** Parsed AdaptationSet which contains the Representation. */
adaptation: IAdaptationSetIntermediateRepresentation;
/**
* If `false`, declared segments in the MPD might still be not completely generated.
* If `true`, they are completely generated.
*
* If `undefined`, the corresponding property was not set in the MPD and it is
* thus assumed that they are all generated.
* It might however be semantically different than `true` in the RxPlayer as it
* means that the packager didn't include that information in the MPD.
*/
availabilityTimeComplete: boolean | undefined;
/**
* availability time offset of the concerned Adaptation.
*
* If `undefined`, the corresponding property was not set in the MPD and it is
* thus assumed to be equal to `0`.
* It might however be semantically different than `0` in the RxPlayer as it
* means that the packager didn't include that information in the MPD.
*/
availabilityTimeOffset: number | undefined;
/** Eventual URLs from which every relative URL will be based on. */
baseURLs: IResolvedBaseUrl[];
/** End time of the current Period, in seconds. */
end?: number | undefined;
/** List of inband event streams that are present on the representation */
inbandEventStreams: IScheme[] | undefined;
/**
* Set to `true` if the linked Period is the chronologically last one in the
* Manifest.
*/
isLastPeriod: boolean;
/** Allows to obtain the first/last available position of a dynamic content. */
manifestBoundsCalculator: ManifestBoundsCalculator;
/** Whether the Manifest can evolve with time. */
isDynamic: boolean;
/**
* Parent parsed SegmentTemplate elements.
* Sorted by provenance from higher level (e.g. Period) to lower-lever (e.g.
* AdaptationSet).
*/
parentSegmentTemplates: ISegmentTemplateIntermediateRepresentation[];
/**
* Time (as the monotonically-raising timestamp used by the RxPlayer) at which
* the XML file containing this Representation was received.
*/
receivedTime?: number | undefined;
/** Start time of the current period, in seconds. */
start: number;
/**
* The parser should take this Representation - which is the same as this one
* parsed at an earlier time - as a base to speed-up the parsing process.
* /!\ If unexpected differences exist between both, there is a risk of
* de-synchronization with what is actually on the server.
*/
unsafelyBaseOnPreviousRepresentation: IRepresentation | null;
}