blob: 7013920bd2919e9b96ed1dee032b58da4fe4408d [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// The PlayerUtils provides utility functions to binding common media events
// to specific player functions. It also provides functions to load media source
// base on test configurations.
var PlayerUtils = new function() {
}
// Prepares a video element for playback by setting default event handlers
// and source attribute.
PlayerUtils.registerDefaultEventListeners = function(player) {
Utils.timeLog('Registering video event handlers.');
// Map from event name to event listener function name. It is common for
// event listeners to be named onEventName.
var eventListenerMap = {
'encrypted': 'onEncrypted',
};
for (eventName in eventListenerMap) {
var eventListenerFunction = player[eventListenerMap[eventName]];
if (eventListenerFunction) {
player.video.addEventListener(eventName, function(e) {
player[eventListenerMap[e.type]](e);
});
}
}
player.video.addEventListener('error', function(error) {
// This most likely happens on pipeline failures (e.g. when the CDM
// crashes). Don't report a failure if the test is checking that sessions
// are closed on a crash.
Utils.timeLog('onHTMLElementError', error.target.error.message);
if (player.testConfig.keySystem == CRASH_TEST_KEYSYSTEM) {
// On failure the session should have been closed, so verify.
player.session.closed.then(
function(result) {
Utils.setResultInTitle(EME_SESSION_CLOSED_AND_ERROR);
},
function(error) { Utils.failTest(error); });
} else {
Utils.failTest(error);
}
});
};
// Register the necessary event handlers needed when playing encrypted content.
// Returns a promise that resolves to the player.
PlayerUtils.registerEMEEventListeners = function(player) {
player.video.addEventListener('encrypted', function(message) {
function addMediaKeySessionListeners(mediaKeySession) {
mediaKeySession.addEventListener('message', function(message) {
player.video.receivedKeyMessage = true;
if (Utils.isRenewalMessage(message)) {
Utils.timeLog('MediaKeySession onMessage - renewal', message);
player.video.receivedRenewalMessage = true;
} else {
if (message.messageType != 'license-request') {
Utils.failTest('Unexpected message type "' + message.messageType +
'" received.',
EME_MESSAGE_UNEXPECTED_TYPE);
}
}
player.onMessage(message);
});
}
function getStatusForHdcpPolicy(mediaKeys, hdcpVersion, expectedResult) {
var policy = new MediaKeysPolicy({minHdcpVersion: hdcpVersion});
return mediaKeys.getStatusForPolicy(policy).then(function(keyStatus) {
if (keyStatus == expectedResult) {
return Promise.resolve();
} else {
return Promise.reject(
"keyStatus " + keyStatus + " does not match " + expectedResult);
}
}, function(error) {
if (expectedResult == "rejected") {
return Promise.resolve();
} else {
return Promise.reject("Promise rejected unexpectedly.");
}
});
}
function testGetStatusForHdcpPolicy(mediaKeys) {
const keySystem = this.testConfig.keySystem;
Utils.timeLog('Key system: ' + keySystem);
if (keySystem == EXTERNAL_CLEARKEY) {
return Promise.resolve().then(function() {
return getStatusForHdcpPolicy(mediaKeys, "", "usable");
}).then(function() {
return getStatusForHdcpPolicy(mediaKeys, "hdcp-1.0", "usable");
}).then(function() {
return getStatusForHdcpPolicy(
mediaKeys, "hdcp-2.2", "output-restricted");
});
} else {
return Promise.resolve().then(function() {
return getStatusForHdcpPolicy(mediaKeys, "", "rejected");
}).then(function() {
return getStatusForHdcpPolicy(mediaKeys, "hdcp-1.0", "rejected");
});
}
}
try {
if (player.testConfig.sessionToLoad) {
// Create a session to load using a new MediaKeys.
// TODO(jrummell): Add a test that covers remove().
player.access.createMediaKeys()
.then(function(mediaKeys) {
// As the tests run with a different origin every time, there is
// no way currently to create a session in one test and then load
// it in a subsequent test (https://crbug.com/715349).
// So if |sessionToLoad| is 'LoadableSession', create a session
// that can be loaded and use that session to load. Otherwise
// use the name provided (which allows for testing load() on a
// session which doesn't exist).
if (player.testConfig.sessionToLoad == 'LoadableSession') {
return Utils.createSessionToLoad(
mediaKeys, player.testConfig.sessionToLoad);
} else {
return player.testConfig.sessionToLoad;
}
})
.then(function(sessionId) {
Utils.timeLog('Loading session: ' + sessionId);
player.session =
message.target.mediaKeys.createSession('persistent-license');
addMediaKeySessionListeners(player.session);
return player.session.load(sessionId);
})
.then(
function(result) {
if (!result)
Utils.failTest('Session not found.', EME_SESSION_NOT_FOUND);
},
function(error) {
Utils.failTest(error, EME_LOAD_FAILED);
});
} else if (player.testConfig.policyCheck) {
// TODO(xhwang): We should be able to move all policy check code to a
// new test js file once we figure out an easy way to separate the
// requrestMediaKeySystemAccess() logic from the rest of this file.
Utils.timeLog('Policy check test.');
player.access.createMediaKeys().then(function(mediaKeys) {
// Call getStatusForPolicy() before creating any MediaKeySessions.
return testGetStatusForHdcpPolicy(mediaKeys);
}).then(function(result) {
Utils.timeLog('Policy check test passed.');
Utils.setResultInTitle(UNIT_TEST_SUCCESS);
}).catch(function(error) {
Utils.timeLog('Policy check test failed.');
Utils.failTest(error, UNIT_TEST_FAILURE);
});
} else {
Utils.timeLog('Creating new media key session for initDataType: ' +
message.initDataType + ', initData: ' +
Utils.getHexString(new Uint8Array(message.initData)));
player.session = message.target.mediaKeys.createSession();
addMediaKeySessionListeners(player.session);
player.session.generateRequest(message.initDataType, message.initData)
.catch(function(error) {
// Ignore the error if a crash is expected. This ensures that
// the decoder actually detects and reports the error.
if (this.testConfig.keySystem != CRASH_TEST_KEYSYSTEM) {
Utils.failTest(error, EME_GENERATEREQUEST_FAILED);
}
});
}
} catch (e) {
Utils.failTest(e);
}
});
this.registerDefaultEventListeners(player);
player.video.receivedKeyMessage = false;
player.video.receivedRenewalMessage = false;
Utils.timeLog('Setting video media keys: ' + player.testConfig.keySystem);
var config = {
audioCapabilities: [],
videoCapabilities: [],
persistentState: 'optional',
sessionTypes: ['temporary'],
};
// requestMediaKeySystemAccess() requires at least one of 'audioCapabilities'
// or 'videoCapabilities' to be specified. It also requires only codecs
// specific to the capability, so unlike MSE cannot have both audio and
// video codecs in the contentType.
if (player.testConfig.mediaType) {
if (player.testConfig.mediaType.substring(0, 5) == 'video') {
config.videoCapabilities = [{contentType: player.testConfig.mediaType}];
} else if (player.testConfig.mediaType.substring(0, 5) == 'audio') {
config.audioCapabilities = [{contentType: player.testConfig.mediaType}];
}
// Handle special cases where both audio and video are needed.
if (player.testConfig.mediaType == 'video/webm; codecs="vorbis, vp8"') {
config.audioCapabilities = [{contentType: 'audio/webm; codecs="vorbis"'}];
config.videoCapabilities = [{contentType: 'video/webm; codecs="vp8"'}];
} else if (
player.testConfig.mediaType == 'video/webm; codecs="opus, vp9"') {
config.audioCapabilities = [{contentType: 'audio/webm; codecs="opus"'}];
config.videoCapabilities = [{contentType: 'video/webm; codecs="vp9"'}];
}
} else {
// Some tests (e.g. mse_different_containers.html) specify audio and
// video codecs seperately.
if (player.testConfig.videoFormat == 'ENCRYPTED_MP4' ||
player.testConfig.videoFormat == 'CLEAR_MP4') {
config.videoCapabilities =
[{contentType: 'video/mp4; codecs="avc1.4D000C"'}];
} else if (
player.testConfig.videoFormat == 'ENCRYPTED_WEBM' ||
player.testConfig.videoFormat == 'CLEAR_WEBM') {
config.videoCapabilities = [{contentType: 'video/webm; codecs="vp8"'}];
}
if (player.testConfig.audioFormat == 'ENCRYPTED_MP4' ||
player.testConfig.audioFormat == 'CLEAR_MP4') {
config.audioCapabilities =
[{contentType: 'audio/mp4; codecs="mp4a.40.2"'}];
} else if (
player.testConfig.audioFormat == 'ENCRYPTED_WEBM' ||
player.testConfig.audioFormat == 'CLEAR_WEBM') {
config.audioCapabilities = [{contentType: 'audio/webm; codecs="vorbis"'}];
}
}
// The File IO test requires persistent state support.
if (player.testConfig.keySystem ==
'org.chromium.externalclearkey.fileiotest') {
config.persistentState = 'required';
} else if (player.testConfig.sessionToLoad) {
config.persistentState = 'required';
config.sessionTypes = ['temporary', 'persistent-license'];
}
return navigator
.requestMediaKeySystemAccess(player.testConfig.keySystem, [config])
.then(function(access) {
player.access = access;
return access.createMediaKeys();
})
.then(function(mediaKeys) {
return player.video.setMediaKeys(mediaKeys);
})
.then(function(result) {
return player;
})
.catch(function(error) {
Utils.failTest(error, NOTSUPPORTEDERROR);
});
};
PlayerUtils.setVideoSource = function(player) {
if (player.testConfig.useMSE) {
Utils.timeLog('Loading media using MSE.');
var mediaSource =
MediaSourceUtils.loadMediaSourceFromTestConfig(player.testConfig);
player.video.src = window.URL.createObjectURL(mediaSource);
} else {
Utils.timeLog('Loading media using src.');
player.video.src = player.testConfig.mediaFile;
}
Utils.timeLog('video.src has been set to ' + player.video.src);
};
// Initialize the player to play encrypted content. Returns a promise that
// resolves to the player.
PlayerUtils.initEMEPlayer = function(player) {
return player.registerEventListeners().then(function(result) {
PlayerUtils.setVideoSource(player);
Utils.timeLog('initEMEPlayer() done');
return player;
});
};
// Return the appropriate player based on test configuration.
PlayerUtils.createPlayer = function(video, testConfig) {
function getPlayerType(keySystem) {
switch (keySystem) {
case WIDEVINE_KEYSYSTEM:
return WidevinePlayer;
case CLEARKEY:
case EXTERNAL_CLEARKEY:
case EXTERNAL_CLEARKEY_RENEWAL:
case CRASH_TEST_KEYSYSTEM:
return ClearKeyPlayer;
case FILE_IO_TEST_KEYSYSTEM:
case OUTPUT_PROTECTION_TEST_KEYSYSTEM:
case PLATFORM_VERIFICATION_TEST_KEYSYSTEM:
case VERIFY_HOST_FILES_TEST_KEYSYSTEM:
case STORAGE_ID_TEST_KEYSYSTEM:
return UnitTestPlayer;
default:
Utils.timeLog(keySystem + ' is not a known key system');
return ClearKeyPlayer;
}
}
var Player = getPlayerType(testConfig.keySystem);
return new Player(video, testConfig);
};