1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 // The PlayerUtils provides utility functions to binding common media events
6 // to specific player functions. It also provides functions to load media source
7 // base on test configurations.
8 var PlayerUtils = new function() {
11 // Prepares a video element for playback by setting default event handlers
12 // and source attribute.
13 PlayerUtils.registerDefaultEventListeners = function(player) {
14 Utils.timeLog('Registering video event handlers.');
15 // Map from event name to event listener function name. It is common for
16 // event listeners to be named onEventName.
17 var eventListenerMap = {
18 'encrypted': 'onEncrypted',
19 'webkitneedkey': 'onWebkitNeedKey',
20 'webkitkeymessage': 'onWebkitKeyMessage',
21 'webkitkeyadded': 'onWebkitKeyAdded',
22 'webkitkeyerror': 'onWebkitKeyError'
24 for (eventName in eventListenerMap) {
25 var eventListenerFunction = player[eventListenerMap[eventName]];
26 if (eventListenerFunction) {
27 player.video.addEventListener(eventName, function(e) {
28 player[eventListenerMap[e.type]](e);
33 player.video.addEventListener('error', function(error) {
34 // This most likely happens on pipeline failures (e.g. when the CDM
36 Utils.timeLog('onHTMLElementError', error);
37 Utils.failTest(error);
41 // Register the necessary event handlers needed when playing encrypted content
42 // using the unprefixed API. Returns a promise that resolves to the player.
43 PlayerUtils.registerEMEEventListeners = function(player) {
44 player.video.addEventListener('encrypted', function(message) {
46 function addMediaKeySessionListeners(mediaKeySession) {
47 mediaKeySession.addEventListener('message', function(message) {
48 player.video.receivedKeyMessage = true;
49 if (Utils.isRenewalMessage(message)) {
50 Utils.timeLog('MediaKeySession onMessage - renewal', message);
51 player.video.receivedRenewalMessage = true;
53 if (message.messageType != 'license-request') {
54 Utils.failTest('Unexpected message type "' + message.messageType +
56 EME_MESSAGE_UNEXPECTED_TYPE);
59 player.onMessage(message);
64 if (player.testConfig.sessionToLoad) {
65 Utils.timeLog('Loading session: ' + player.testConfig.sessionToLoad);
67 message.target.mediaKeys.createSession('persistent-license');
68 addMediaKeySessionListeners(session);
69 session.load(player.testConfig.sessionToLoad)
73 Utils.failTest('Session not found.', EME_SESSION_NOT_FOUND);
75 function(error) { Utils.failTest(error, EME_LOAD_FAILED); });
77 Utils.timeLog('Creating new media key session for initDataType: ' +
78 message.initDataType + ', initData: ' +
79 Utils.getHexString(new Uint8Array(message.initData)));
80 var session = message.target.mediaKeys.createSession();
81 addMediaKeySessionListeners(session);
82 session.generateRequest(message.initDataType, message.initData)
83 .catch(function(error) {
84 // Ignore the error if a crash is expected. This ensures that
85 // the decoder actually detects and reports the error.
86 if (this.testConfig.keySystem !=
87 'org.chromium.externalclearkey.crash') {
88 Utils.failTest(error, EME_GENERATEREQUEST_FAILED);
97 this.registerDefaultEventListeners(player);
98 player.video.receivedKeyMessage = false;
99 Utils.timeLog('Setting video media keys: ' + player.testConfig.keySystem);
101 if (player.testConfig.sessionToLoad) {
103 persistentState: "required",
104 sessionTypes: ["temporary", "persistent-license"]
107 return navigator.requestMediaKeySystemAccess(
108 player.testConfig.keySystem, [config])
109 .then(function(access) { return access.createMediaKeys(); })
110 .then(function(mediaKeys) {
111 return player.video.setMediaKeys(mediaKeys);
113 .then(function(result) { return player; })
114 .catch(function(error) { Utils.failTest(error, NOTSUPPORTEDERROR); });
117 // Register the necessary event handlers needed when playing encrypted content
118 // using the prefixed API. Even though the prefixed API is all synchronous,
119 // returns a promise that resolves to the player.
120 PlayerUtils.registerPrefixedEMEEventListeners = function(player) {
121 player.video.addEventListener('webkitneedkey', function(message) {
122 var initData = message.initData;
123 if (player.testConfig.sessionToLoad) {
124 Utils.timeLog('Loading session: ' + player.testConfig.sessionToLoad);
126 Utils.convertToUint8Array(PREFIXED_EME_API_LOAD_SESSION_HEADER +
127 player.testConfig.sessionToLoad);
129 Utils.timeLog(player.testConfig.keySystem +
130 ' Generate key request, initData: ' +
131 Utils.getHexString(initData));
133 message.target.webkitGenerateKeyRequest(player.testConfig.keySystem,
140 player.video.addEventListener('webkitkeyadded', function(message) {
141 Utils.timeLog('onWebkitKeyAdded', message);
142 message.target.receivedKeyAdded = true;
145 player.video.addEventListener('webkitkeyerror', function(error) {
146 Utils.timeLog('onWebkitKeyError',
147 'KeySystem: ' + error.keySystem + ', sessionId: ' +
148 error.sessionId + ', errorCode: ' + error.errorCode.code +
149 ', systemCode: ' + error.systemCode);
150 Utils.failTest(error, PREFIXED_EME_ERROR_EVENT);
153 player.video.addEventListener('webkitkeymessage', function(message) {
154 Utils.timeLog('onWebkitKeyMessage', message);
155 message.target.receivedKeyMessage = true;
156 if (Utils.isRenewalMessagePrefixed(message.message)) {
157 Utils.timeLog('onWebkitKeyMessage - renewal', message);
158 message.target.receivedRenewalMessage = true;
162 // The prefixed API is all synchronous, so wrap the calls in a promise.
163 return new Promise(function(resolve, reject) {
164 PlayerUtils.registerDefaultEventListeners(player);
165 player.video.receivedKeyMessage = false;
170 PlayerUtils.setVideoSource = function(player) {
171 if (player.testConfig.useMSE) {
172 Utils.timeLog('Loading media using MSE.');
174 MediaSourceUtils.loadMediaSourceFromTestConfig(player.testConfig);
175 player.video.src = window.URL.createObjectURL(mediaSource);
177 Utils.timeLog('Loading media using src.');
178 player.video.src = player.testConfig.mediaFile;
182 // Initialize the player to play encrypted content. Returns a promise that
183 // resolves to the player.
184 PlayerUtils.initEMEPlayer = function(player) {
185 return player.registerEventListeners().then(function(result) {
186 PlayerUtils.setVideoSource(player);
191 // Return the appropriate player based on test configuration.
192 PlayerUtils.createPlayer = function(video, testConfig) {
193 // Update keySystem if using prefixed Clear Key since it is not available as a
194 // separate key system to choose from; however it can be set in URL query.
195 var usePrefixedEME = testConfig.usePrefixedEME;
196 if (testConfig.keySystem == CLEARKEY && usePrefixedEME)
197 testConfig.keySystem = PREFIXED_CLEARKEY;
199 function getPlayerType(keySystem) {
201 case WIDEVINE_KEYSYSTEM:
203 return PrefixedWidevinePlayer;
204 return WidevinePlayer;
205 case PREFIXED_CLEARKEY:
206 return PrefixedClearKeyPlayer;
207 case EXTERNAL_CLEARKEY:
210 return PrefixedClearKeyPlayer;
211 return ClearKeyPlayer;
212 case FILE_IO_TEST_KEYSYSTEM:
213 return FileIOTestPlayer;
215 Utils.timeLog(keySystem + ' is not a known key system');
217 return PrefixedClearKeyPlayer;
218 return ClearKeyPlayer;
221 var Player = getPlayerType(testConfig.keySystem);
222 return new Player(video, testConfig);