rx-player
Version:
Canal+ HTML5 Video Player
117 lines (116 loc) • 5.2 kB
JavaScript
/**
* 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 config from "../../../config";
import isNonEmptyString from "../../../utils/is_non_empty_string";
import request from "../../../utils/request/xhr";
import TaskCanceller from "../../../utils/task_canceller";
const iso8601Duration = /^P(([\d.]*)Y)?(([\d.]*)M)?(([\d.]*)D)?T?(([\d.]*)H)?(([\d.]*)M)?(([\d.]*)S)?/;
/**
* Parse MPD ISO8601 duration attributes into seconds.
*
* The returned value is a tuple of two elements where:
* 1. the first value is the parsed value - or `null` if we could not parse
* it
* 2. the second value is a possible error encountered while parsing this
* value - set to `null` if no error was encountered.
* @param {string} val - The value to parse
* @returns {number | null}
*/
function parseDuration(val) {
if (!isNonEmptyString(val)) {
return null;
}
const match = iso8601Duration.exec(val);
if (match === null) {
return null;
}
const duration = parseFloat(isNonEmptyString(match[2]) ? match[2] : "0") * 365 * 24 * 60 * 60 +
parseFloat(isNonEmptyString(match[4]) ? match[4] : "0") * 30 * 24 * 60 * 60 +
parseFloat(isNonEmptyString(match[6]) ? match[6] : "0") * 24 * 60 * 60 +
parseFloat(isNonEmptyString(match[8]) ? match[8] : "0") * 60 * 60 +
parseFloat(isNonEmptyString(match[10]) ? match[10] : "0") * 60 +
parseFloat(isNonEmptyString(match[12]) ? match[12] : "0");
return duration;
}
/**
* Load manifest and get duration from it.
* @param {String} url
* @param {String} transport
* @returns {Promise.<number>}
*/
async function getDurationFromManifest(url, transport) {
var _a;
if (transport !== "dash" && transport !== "smooth" && transport !== "metaplaylist") {
throw new Error("createMetaplaylist: Unknown transport type.");
}
if (transport === "dash" || transport === "smooth") {
const response = await request({
url,
responseType: "document",
timeout: config.getCurrent().DEFAULT_REQUEST_TIMEOUT,
connectionTimeout: config.getCurrent().DEFAULT_CONNECTION_TIMEOUT,
// We won't cancel
cancelSignal: new TaskCanceller(undefined).signal,
});
const { responseData } = response;
const root = responseData.documentElement;
if (transport === "dash") {
const dashDurationAttribute = root.getAttribute("mediaPresentationDuration");
if (dashDurationAttribute === null) {
throw new Error("createMetaplaylist: No duration on DASH content.");
}
const periodElements = root.getElementsByTagName("Period");
const firstDASHStartAttribute = (_a = periodElements[0]) === null || _a === void 0 ? void 0 : _a.getAttribute("start");
const firstDASHStart = firstDASHStartAttribute !== null ? parseDuration(firstDASHStartAttribute) : 0;
const dashDuration = parseDuration(dashDurationAttribute);
if (firstDASHStart === null || dashDuration === null) {
throw new Error("createMetaplaylist: Cannot parse " + "the duration from a DASH content.");
}
return dashDuration - firstDASHStart;
}
// smooth
const smoothDurationAttribute = root.getAttribute("Duration");
const smoothTimeScaleAttribute = root.getAttribute("TimeScale");
if (smoothDurationAttribute === null) {
throw new Error("createMetaplaylist: No duration on smooth content.");
}
const timescale = smoothTimeScaleAttribute !== null
? parseInt(smoothTimeScaleAttribute, 10)
: 10000000;
return parseInt(smoothDurationAttribute, 10) / timescale;
}
// metaplaylist
const response = await request({
url,
responseType: "text",
timeout: config.getCurrent().DEFAULT_REQUEST_TIMEOUT,
connectionTimeout: config.getCurrent().DEFAULT_CONNECTION_TIMEOUT,
// We won't cancel
cancelSignal: new TaskCanceller(undefined).signal,
});
const { responseData } = response;
const metaplaylist = JSON.parse(responseData);
if (metaplaylist.contents === undefined ||
metaplaylist.contents.length === undefined ||
metaplaylist.contents.length === 0) {
throw new Error("createMetaplaylist: No duration on Metaplaylist content.");
}
const { contents } = metaplaylist;
const lastEnd = contents[contents.length - 1].endTime;
const firstStart = contents[0].startTime;
return lastEnd - firstStart;
}
export default getDurationFromManifest;