rx-player
Version:
Canal+ HTML5 Video Player
225 lines (209 loc) • 7 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 { canRelyOnRequestMediaKeySystemAccess } from "../../../compat/can_rely_on_request_media_key_system_access";
import getEmeApiImplementation from "../../../compat/eme";
import {
DUMMY_PLAY_READY_HEADER,
generatePlayReadyInitData,
} from "../../../compat/generate_init_data";
import log from "./log";
import probeDecodingInfos from "./probers/decodingInfo";
import probeHDCPPolicy from "./probers/HDCPPolicy";
import probeTypeWithFeatures from "./probers/isTypeSupportedWithFeatures";
import probeContentType from "./probers/mediaContentType";
import probeMatchMedia from "./probers/mediaDisplayInfos";
import type {
IDisplayConfiguration,
IMediaConfiguration,
IMediaKeySystemConfiguration,
} from "./types";
/**
* A set of API to probe media capabilites.
* Each API allow to evalute a specific feature (HDCP support, decoding infos, etc)
* and relies on different browser API to probe capabilites.
*/
const mediaCapabilitiesProber = {
/**
* Set logger level
* @param {string} level
*/
set LogLevel(level: string) {
log.setLevel(level, log.getFormat());
},
/**
* Get logger level
* @returns {string}
*/
get LogLevel(): string {
return log.getLevel();
},
/**
* @param {string} keySystemType - Reverse domain name of the wanted key
* system.
* @param {Array.<MediaKeySystemConfiguration>} keySystemConfiguration - DRM
* configuration wanted.
* @returns {Promise.<MediaKeySystemConfiguration>} - Resolved the
* MediaKeySystemConfiguration actually obtained if the given configuration is
* compatible or a rejected Promise if not.
*/
async checkDrmConfiguration(
keySystemType: string,
keySystemConfiguration: IMediaKeySystemConfiguration[],
): Promise<MediaKeySystemConfiguration> {
const eme = getEmeApiImplementation("auto");
if (eme === null) {
const error = new Error("EME not supported in current environment");
throw error;
}
const mksa = await eme.requestMediaKeySystemAccess(
keySystemType,
keySystemConfiguration,
);
if (!canRelyOnRequestMediaKeySystemAccess(keySystemType)) {
try {
const mediaKeys = await mksa.createMediaKeys();
const session = mediaKeys.createSession();
const initData = generatePlayReadyInitData(DUMMY_PLAY_READY_HEADER);
await session.generateRequest("cenc", initData);
session.close().catch(() => {
log.warn("MediaCapabilitiesProber", "Failed to close the dummy session");
});
return mksa.getConfiguration();
} catch (err) {
log.debug(
"MediaCapabilitiesProber",
"KeySystemAccess was granted but it is not usable",
);
return Promise.reject(
new Error(
"Failed to generare a license-request with this keySystem: " +
(err instanceof Error ? err.toString() : "Unknown Error"),
),
);
}
} else {
return mksa.getConfiguration();
}
},
/**
* Get HDCP status. Evaluates if current equipement support given
* HDCP revision.
* @param {string} hdcpVersion - The wanted HDCP version to test (e.g.
* `"1.1"`).
* @returns {Promise.<string>} - Returns a Promise which rejects if the
* browser API we need to rely on unexpectedly fail.
*
* If they resolve, resolve with one of the following string:
*
* - `"Supported"`: The HDCP version is probably supported
*
* - `"NotSupported"`: The HDCP version is probably not supported
*
* - `"Unknown"`: It is unknown if the HDCP version may be supported or not.
*/
async getStatusForHDCP(hdcpVersion: string): Promise<string> {
if (hdcpVersion === undefined || hdcpVersion.length === 0) {
throw new Error("Bad Arguments: No HDCP Policy specified.");
}
try {
const res = await probeTypeWithFeatures({ hdcp: hdcpVersion });
switch (res) {
case "NotSupported":
case "Supported":
return res;
case "Unknown":
// continue
break;
}
} catch {
// We do not care about this call erroring
}
return probeHDCPPolicy(hdcpVersion);
},
/**
* Get decoding capabilities from a given video and/or audio
* configuration.
* TODO check alongside key system.
* @param {Object} mediaConfig
* @returns {Promise.<string>}
*/
async getDecodingCapabilities(
mediaConfig: IMediaConfiguration,
): Promise<"Supported" | "Unknown" | "NotSupported"> {
const config = {
type: mediaConfig.type,
video: mediaConfig.video,
audio: mediaConfig.audio,
};
let isCodecSupported = false;
try {
const status = probeContentType(config);
if (status === "Supported") {
isCodecSupported = true;
} else if (status === "NotSupported") {
return "NotSupported";
}
} catch (err) {
const error = err instanceof Error ? err : new Error("Unknown error");
log.error("MediaCapabilitiesProber", "probeContentType failed", error);
}
try {
const res = await probeTypeWithFeatures(config);
if (res === "NotSupported") {
return "NotSupported";
}
} catch {
// We do not care here
}
try {
const res = await probeDecodingInfos(config);
return res;
} catch {
return isCodecSupported ? "Supported" : "Unknown";
}
},
/**
* Get display capabilites. Tells if display can output
* with specific video and/or audio constrains.
* TODO I'm sure there's now newer APIs we can use for this (e.g. CSS etc.)
* @param {Object} displayConfig
* @returns {Promise.<string>}
*/
async getDisplayCapabilities(displayConfig: IDisplayConfiguration): Promise<string> {
const config = { display: displayConfig };
let matchMediaSupported: boolean | undefined;
try {
const status = probeMatchMedia(config);
if (status === "Supported") {
matchMediaSupported = true;
} else {
return "NotSupported";
}
} catch {
// We do not care here
}
try {
const res = await probeTypeWithFeatures(config);
if (res === "NotSupported") {
return "NotSupported";
}
} catch {
// We do not care here
}
return matchMediaSupported === true ? "Supported" : "Unknown";
},
};
export default mediaCapabilitiesProber;