rx-player
Version:
Canal+ HTML5 Video Player
183 lines (174 loc) • 5.41 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 idGenerator from "../../../utils/id_generator";
import getMonotonicTimeStamp from "../../../utils/monotonic_timestamp";
import type {
IContentProtections,
IContentProtectionInitData,
IParsedAdaptation,
IParsedManifest,
IParsedPeriod,
IParsedRepresentation,
} from "../types";
import LocalRepresentationIndex from "./representation_index";
import type {
IContentProtections as ILocalContentProtections,
ILocalAdaptation,
ILocalManifest,
ILocalPeriod,
ILocalRepresentation,
} from "./types";
/**
* @param {Object} localManifest
* @returns {Object}
*/
export default function parseLocalManifest(
localManifest: ILocalManifest,
): IParsedManifest {
if (localManifest.type !== "local") {
throw new Error("Invalid local manifest given. It misses the `type` property.");
}
if (localManifest.version !== "0.2") {
throw new Error(
`The current Local Manifest version (${localManifest.version})` +
" is not compatible with the current version of the RxPlayer",
);
}
const periodIdGenerator = idGenerator();
const { minimumPosition, maximumPosition, isFinished } = localManifest;
const parsedPeriods = localManifest.periods.map((period) =>
parsePeriod(period, { periodIdGenerator }),
);
return {
availabilityStartTime: 0,
expired: localManifest.expired,
transportType: "local",
isDynamic: !isFinished,
isLastPeriodKnown: true,
isLive: false,
uris: [],
timeBounds: {
minimumSafePosition: minimumPosition ?? 0,
timeshiftDepth: null,
maximumTimeData: {
isLinear: false,
maximumSafePosition: maximumPosition,
livePosition: undefined,
time: getMonotonicTimeStamp(),
},
},
periods: parsedPeriods,
};
}
/**
* @param {Object} period
* @param {Object} ctxt
* @returns {Object}
*/
function parsePeriod(
period: ILocalPeriod,
ctxt: { periodIdGenerator: () => string /* Generate next Period's id */ },
): IParsedPeriod {
const adaptationIdGenerator = idGenerator();
return {
id: "period-" + ctxt.periodIdGenerator(),
start: period.start,
end: period.end,
duration: period.end - period.start,
thumbnailTracks: [],
adaptations: period.adaptations.reduce<Partial<Record<string, IParsedAdaptation[]>>>(
(acc, ada) => {
const type = ada.type;
let adaps = acc[type];
if (adaps === undefined) {
adaps = [];
acc[type] = adaps;
}
adaps.push(parseAdaptation(ada, { adaptationIdGenerator }));
return acc;
},
{},
),
};
}
/**
* @param {Object} adaptation
* @param {Object} ctxt
* @returns {Object}
*/
function parseAdaptation(
adaptation: ILocalAdaptation,
ctxt: {
adaptationIdGenerator: () => string /* Generate next Adaptation's id */;
},
): IParsedAdaptation {
const representationIdGenerator = idGenerator();
return {
id: "adaptation-" + ctxt.adaptationIdGenerator(),
type: adaptation.type,
audioDescription: adaptation.audioDescription,
closedCaption: adaptation.closedCaption,
forcedSubtitles: adaptation.forcedSubtitles,
language: adaptation.language,
representations: adaptation.representations.map((representation) =>
parseRepresentation(representation, { representationIdGenerator }),
),
};
}
/**
* @param {Object} representation
* @returns {Object}
*/
function parseRepresentation(
representation: ILocalRepresentation,
ctxt: { representationIdGenerator: () => string },
): IParsedRepresentation {
const id = "representation-" + ctxt.representationIdGenerator();
const contentProtections =
representation.contentProtections === undefined
? undefined
: formatContentProtections(representation.contentProtections);
return {
id,
cdnMetadata: null,
bitrate: representation.bitrate,
height: representation.height,
width: representation.width,
codecs: representation.codecs,
isSpatialAudio: representation.isSpatialAudio,
mimeType: representation.mimeType,
index: new LocalRepresentationIndex(representation.index, id),
contentProtections,
};
}
/**
* Translate Local Manifest's `contentProtections` attribute to the one defined
* for a `Manifest` structure.
* @param {Object} localContentProtections
* @returns {Object}
*/
function formatContentProtections(
localContentProtections: ILocalContentProtections,
): IContentProtections {
const keyIds = localContentProtections.keyIds.map((k) => k.keyId);
const initData: IContentProtectionInitData[] = Object.keys(
localContentProtections.initData,
).map((currType) => {
const localInitData = localContentProtections.initData[currType] ?? [];
return { type: currType, values: localInitData };
});
return { keyIds, initData };
}