Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(378)

Side by Side Diff: chrome/test/data/media/encrypted_media_utils.js

Issue 182113005: Update EME browser tests to include EME WD (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Changes + shadi's fix Created 6 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 var keySystem = QueryString.keySystem; 5 var keySystem = QueryString.keySystem;
6 var mediaFile = QueryString.mediaFile; 6 var mediaFile = QueryString.mediaFile;
7 var mediaType = QueryString.mediaType || 'video/webm; codecs="vorbis, vp8"'; 7 var mediaType = QueryString.mediaType || 'video/webm; codecs="vorbis, vp8"';
8 var useMSE = QueryString.useMSE == 1; 8 var useMSE = QueryString.useMSE == 1;
9 var usePrefixedEME = QueryString.usePrefixedEME == 1;
9 var forceInvalidResponse = QueryString.forceInvalidResponse == 1; 10 var forceInvalidResponse = QueryString.forceInvalidResponse == 1;
10 var sessionToLoad = QueryString.sessionToLoad; 11 var sessionToLoad = QueryString.sessionToLoad;
11 var licenseServerURL = QueryString.licenseServerURL; 12 var licenseServerURL = QueryString.licenseServerURL;
12 // Maximum license request attempts that the media element can make to get a 13 // Maximum license request attempts that the media element can make to get a
13 // valid license response from the license server. 14 // valid license response from the license server.
14 // This is used to avoid server boot up delays since there is no direct way 15 // This is used to avoid server boot up delays since there is no direct way
15 // to know if it is ready crbug.com/339289. 16 // to know if it is ready crbug.com/339289.
16 var MAX_LICENSE_REQUEST_ATTEMPTS = 3; 17 var MAX_LICENSE_REQUEST_ATTEMPTS = 3;
17 // Delay in ms between retries to get a license from license server. 18 // Delay in ms between retries to get a license from license server.
18 var LICENSE_REQUEST_RETRY_DELAY_MS = 3000; 19 var LICENSE_REQUEST_RETRY_DELAY_MS = 3000;
(...skipping 25 matching lines...) Expand all
44 function hasPrefix(msg, prefix) { 45 function hasPrefix(msg, prefix) {
45 if (msg.length < prefix.length) 46 if (msg.length < prefix.length)
46 return false; 47 return false;
47 for (var i = 0; i < prefix.length; ++i) { 48 for (var i = 0; i < prefix.length; ++i) {
48 if (String.fromCharCode(msg[i]) != prefix[i]) 49 if (String.fromCharCode(msg[i]) != prefix[i])
49 return false; 50 return false;
50 } 51 }
51 return true; 52 return true;
52 } 53 }
53 54
55 // JWK routines copied from third_party/WebKit/LayoutTests/media/
56 // encrypted-media/encrypted-media-utils.js
57 //
58 // Encodes data (Uint8Array) into base64 string without trailing '='.
59 // TODO: Update once the EME spec is updated to say base64url encoding.
60 function base64Encode(data)
61 {
62 var result = btoa(String.fromCharCode.apply(null, data));
63 return result.replace(/=+$/g, '');
64 }
65
66 // Creates a JWK from raw key ID and key.
67 function createJWK(keyId, key)
68 {
69 var jwk = "{\"kty\":\"oct\",\"kid\":\"";
70 jwk += base64Encode(keyId);
71 jwk += "\",\"k\":\"";
72 jwk += base64Encode(key);
73 jwk += "\"}";
74 return jwk;
75 }
76
77 // Creates a JWK Set from an array of JWK(s).
78 function createJWKSet()
79 {
80 var jwkSet = "{\"keys\":[";
81 for (var i = 0; i < arguments.length; i++) {
82 if (i != 0)
83 jwkSet += ",";
84 jwkSet += arguments[i];
85 }
86 jwkSet += "]}";
87 return jwkSet;
88 }
89
54 function isHeartbeatMessage(msg) { 90 function isHeartbeatMessage(msg) {
55 return hasPrefix(msg, HEART_BEAT_HEADER); 91 return hasPrefix(msg, HEART_BEAT_HEADER);
56 } 92 }
57 93
58 function isFileIOTestMessage(msg) { 94 function isFileIOTestMessage(msg) {
59 return hasPrefix(msg, FILE_IO_TEST_RESULT_HEADER); 95 return hasPrefix(msg, FILE_IO_TEST_RESULT_HEADER);
60 } 96 }
61 97
62 function loadEncryptedMediaFromURL(video) { 98 function loadEncryptedMediaFromURL(video) {
63 return loadEncryptedMedia(video, mediaFile, keySystem, KEY, useMSE); 99 return loadEncryptedMedia(video, mediaFile, keySystem, KEY, useMSE,
100 usePrefixedEME);
64 } 101 }
65 102
66 function loadEncryptedMedia(video, mediaFile, keySystem, key, useMSE, 103 function loadEncryptedMedia(video, mediaFile, keySystem, key, useMSE,
67 appendSourceCallbackFn) { 104 usePrefixedEME, appendSourceCallbackFn) {
68 var keyRequested = false; 105 var keyRequested = false;
69 var sourceOpened = false; 106 var sourceOpened = false;
107 var mediaKeys;
108 var mediaKeySession;
70 // Add properties to enable verification that events occurred. 109 // Add properties to enable verification that events occurred.
71 video.receivedKeyAdded = false; 110 video.receivedKeyAdded = false;
72 video.receivedHeartbeat = false; 111 video.receivedHeartbeat = false;
73 video.isHeartbeatExpected = keySystem === EXTERNAL_CLEAR_KEY_KEY_SYSTEM; 112 video.isHeartbeatExpected = keySystem === EXTERNAL_CLEAR_KEY_KEY_SYSTEM;
74 video.receivedKeyMessage = false; 113 video.receivedKeyMessage = false;
75 114
76 if (!(video && mediaFile && keySystem && key)) { 115 if (!(video && mediaFile && keySystem && key)) {
77 failTest('Missing parameters in loadEncryptedMedia().'); 116 failTest('Missing parameters in loadEncryptedMedia().');
78 return; 117 return;
79 } 118 }
80 119
120 // Shared by prefixed and unprefixed EME.
81 function onNeedKey(e) { 121 function onNeedKey(e) {
82 if (keyRequested) 122 if (keyRequested)
83 return; 123 return;
84 keyRequested = true; 124 keyRequested = true;
85 console.log('onNeedKey', e); 125 console.log('onNeedKey', e);
86 126
87 var initData = sessionToLoad ? stringToUint8Array( 127 var initData = sessionToLoad ? stringToUint8Array(
88 PREFIXED_API_LOAD_SESSION_HEADER + sessionToLoad) : e.initData; 128 PREFIXED_API_LOAD_SESSION_HEADER + sessionToLoad) : e.initData;
89 try { 129 try {
90 video.webkitGenerateKeyRequest(keySystem, initData); 130 if (usePrefixedEME) {
131 video.webkitGenerateKeyRequest(keySystem, initData);
132 } else {
133 mediaKeySession = mediaKeys.createSession(e.contentType, initData);
134 mediaKeySession.addEventListener('message', onMessage);
135 mediaKeySession.addEventListener('error', function() {
136 setResultInTitle("KeyError");
137 });
138 mediaKeySession.addEventListener('ready', onReady);
139 }
91 } 140 }
92 catch(error) { 141 catch(error) {
93 setResultInTitle(error.name); 142 setResultInTitle(error.name);
94 } 143 }
95 } 144 }
96 145
146 // Prefixed EME callbacks.
97 function onKeyAdded(e) { 147 function onKeyAdded(e) {
98 e.target.receivedKeyAdded = true; 148 e.target.receivedKeyAdded = true;
99 } 149 }
100 150
101 function onKeyMessage(message) { 151 function onKeyMessage(message) {
102 video.receivedKeyMessage = true; 152 video.receivedKeyMessage = true;
153
103 if (!message.keySystem || message.keySystem != keySystem) { 154 if (!message.keySystem || message.keySystem != keySystem) {
104 failTest('Message with unexpected keySystem: ' + message.keySystem); 155 failTest('Message with unexpected keySystem: ' + message.keySystem);
105 return; 156 return;
106 } 157 }
107 158
108 if (!message.sessionId) { 159 if (!message.sessionId) {
109 failTest('Message without a sessionId: ' + message.sessionId); 160 failTest('Message without a sessionId: ' + message.sessionId);
110 return; 161 return;
111 } 162 }
112 163
164 processMessage(message, message.keySystem, message.defaultURL);
165 }
166
167 // Unprefixed EME callbacks.
168 function onReady(e) {
169 }
170
171 function onMessage(message) {
172 processMessage(message, keySystem, message.destinationURL);
173 }
174
175 // Shared by prefixed and unprefixed EME.
176 function processMessage(message, keySystem, url) {
177 video.receivedKeyMessage = true;
178
113 if (!message.message) { 179 if (!message.message) {
114 failTest('Message without a message content: ' + message.message); 180 failTest('Message without a message content: ' + message.message);
115 return; 181 return;
116 } 182 }
117 183
118 if (isHeartbeatMessage(message.message)) { 184 if (isHeartbeatMessage(message.message)) {
119 console.log('onKeyMessage - heartbeat', message); 185 console.log('processMessage - heartbeat', message);
120 message.target.receivedHeartbeat = true; 186 video.receivedHeartbeat = true;
121 verifyHeartbeatMessage(message); 187 verifyHeartbeatMessage(keySystem, url);
122 return; 188 return;
123 } 189 }
124 190
125 if (isFileIOTestMessage(message.message)) { 191 if (isFileIOTestMessage(message.message)) {
126 var success = getFileIOTestResult(message); 192 var success = getFileIOTestResult(keySystem, message);
127 console.log('onKeyMessage - CDM file IO test: ' + 193 console.log('processMessage - CDM file IO test: ' +
128 (success ? 'Success' : 'Fail')); 194 (success ? 'Success' : 'Fail'));
129 if (success) 195 if (success)
130 setResultInTitle("FILEIOTESTSUCCESS"); 196 setResultInTitle("FILEIOTESTSUCCESS");
131 else 197 else
132 setResultInTitle("FAILED"); 198 setResultInTitle("FAILED");
133 return; 199 return;
134 } 200 }
135 201
136 // For FileIOTest key system, no need to start playback. 202 // For FileIOTest key system, no need to start playback.
137 if (message.keySystem == EXTERNAL_CLEAR_KEY_FILE_IO_TEST_KEY_SYSTEM) 203 if (keySystem == EXTERNAL_CLEAR_KEY_FILE_IO_TEST_KEY_SYSTEM)
138 return; 204 return;
139 205
140 // No tested key system returns defaultURL in for key request messages. 206 // No tested key system returns defaultURL in for key request messages.
141 if (message.defaultURL) { 207 if (url) {
142 failTest('Message unexpectedly has defaultURL: ' + message.defaultURL); 208 failTest('Message unexpectedly has URL: ' + url);
143 return; 209 return;
144 } 210 }
145 211
146 // When loading a session, no need to call webkitAddKey(). 212 // When loading a session, no need to call update()/webkitAddKey().
147 if (sessionToLoad) 213 if (sessionToLoad)
148 return; 214 return;
149 215
150 console.log('onKeyMessage - key request', message); 216 console.log('processMessage - key request', message);
151 if (forceInvalidResponse) { 217 if (forceInvalidResponse) {
152 console.log('Forcing an invalid onKeyMessage response.'); 218 console.log('processMessage - Forcing an invalid response.');
153 var invalidData = new Uint8Array([0xAA]); 219 var invalidData = new Uint8Array([0xAA]);
154 video.webkitAddKey(keySystem, invalidData, invalidData); 220 if (usePrefixedEME) {
221 video.webkitAddKey(keySystem, invalidData, invalidData);
222 } else {
223 var jwkSet = stringToUint8Array(
224 createJWKSet(createJWK(invalidData, invalidData)));
225 mediaKeySession.update(jwkSet);
226 }
155 return; 227 return;
156 } 228 }
157 // Check if should send request to locally running license server. 229 // Check if should send request to locally running license server.
158 if (licenseServerURL) { 230 if (licenseServerURL) {
159 requestLicense(message); 231 requestLicense(message);
160 return; 232 return;
161 } 233 }
162 console.log('Respond to onKeyMessage with test key.'); 234 console.log('processMessage - Respond with test key.');
163 var initData = message.message; 235 var initData = message.message;
164 if (mediaType.indexOf('mp4') != -1) 236 if (mediaType.indexOf('mp4') != -1)
165 initData = KEY_ID; // Temporary hack for Clear Key in v0.1. 237 initData = KEY_ID; // Temporary hack for Clear Key in v0.1.
166 video.webkitAddKey(keySystem, key, initData); 238 if (usePrefixedEME) {
239 video.webkitAddKey(keySystem, key, initData);
240 } else {
241 var jwkSet = stringToUint8Array(createJWKSet(createJWK(initData, key)));
242 mediaKeySession.update(jwkSet);
243 }
167 } 244 }
168 245
169 function verifyHeartbeatMessage(message) { 246 function verifyHeartbeatMessage(keySystem, url) {
170 String.prototype.startsWith = function(prefix) { 247 String.prototype.startsWith = function(prefix) {
171 return this.indexOf(prefix) === 0; 248 return this.indexOf(prefix) === 0;
172 } 249 }
173 250
174 function isExternalClearKey(keySystem) { 251 function isExternalClearKey(keySystem) {
175 return keySystem == EXTERNAL_CLEAR_KEY_KEY_SYSTEM || 252 return keySystem == EXTERNAL_CLEAR_KEY_KEY_SYSTEM ||
176 keySystem.startsWith(EXTERNAL_CLEAR_KEY_KEY_SYSTEM + '.'); 253 keySystem.startsWith(EXTERNAL_CLEAR_KEY_KEY_SYSTEM + '.');
177 } 254 }
178 255
179 // Only External Clear Key sends a HEARTBEAT message. 256 // Only External Clear Key sends a HEARTBEAT message.
180 if (!isExternalClearKey(message.keySystem)) { 257 if (!isExternalClearKey(keySystem)) {
181 failTest('Unexpected heartbeat from ' + message.keySystem); 258 failTest('Unexpected heartbeat from ' + keySystem);
182 return; 259 return;
183 } 260 }
184 261
185 if (message.defaultURL != EXTERNAL_CLEAR_KEY_HEARTBEAT_URL) { 262 if (url != EXTERNAL_CLEAR_KEY_HEARTBEAT_URL) {
186 failTest('Heartbeat message with unexpected defaultURL: ' + 263 failTest('Heartbeat message with unexpected URL: ' + url);
187 message.defaultURL);
188 return; 264 return;
189 } 265 }
190 } 266 }
191 267
192 function getFileIOTestResult(e) { 268 function getFileIOTestResult(keySystem, e) {
193 // Only External Clear Key sends a FILEIOTESTRESULT message. 269 // Only External Clear Key sends a FILEIOTESTRESULT message.
194 if (e.keySystem != EXTERNAL_CLEAR_KEY_FILE_IO_TEST_KEY_SYSTEM) { 270 if (keySystem != EXTERNAL_CLEAR_KEY_FILE_IO_TEST_KEY_SYSTEM) {
195 failTest('Unexpected CDM file IO test result from ' + e.keySystem); 271 failTest('Unexpected CDM file IO test result from ' + keySystem);
196 return false; 272 return false;
197 } 273 }
198 274
199 // The test result is either '0' or '1' appended to the header. 275 // The test result is either '0' or '1' appended to the header.
200 if (e.message.length != FILE_IO_TEST_RESULT_HEADER.length + 1) 276 if (e.message.length != FILE_IO_TEST_RESULT_HEADER.length + 1)
201 return false; 277 return false;
202 278
203 var result_index = FILE_IO_TEST_RESULT_HEADER.length; 279 var result_index = FILE_IO_TEST_RESULT_HEADER.length;
204 return String.fromCharCode(e.message[result_index]) == 1; 280 return String.fromCharCode(e.message[result_index]) == 1;
205 } 281 }
206 282
207 video.addEventListener('webkitneedkey', onNeedKey); 283 if (usePrefixedEME) {
208 video.addEventListener('webkitkeymessage', onKeyMessage); 284 video.addEventListener('webkitneedkey', onNeedKey);
209 video.addEventListener('webkitkeyerror', function() { 285 video.addEventListener('webkitkeymessage', onKeyMessage);
210 setResultInTitle("KeyError"); 286 video.addEventListener('webkitkeyerror', function() {
211 }); 287 setResultInTitle("KeyError");
212 video.addEventListener('webkitkeyadded', onKeyAdded); 288 });
289 video.addEventListener('webkitkeyadded', onKeyAdded);
290 } else {
291 video.addEventListener('needkey', onNeedKey);
292 }
213 installTitleEventHandler(video, 'error'); 293 installTitleEventHandler(video, 'error');
214 294
215 if (useMSE) { 295 if (useMSE) {
216 var mediaSource = loadMediaSource(mediaFile, mediaType, 296 var mediaSource = loadMediaSource(mediaFile, mediaType,
217 appendSourceCallbackFn); 297 appendSourceCallbackFn);
218 video.src = window.URL.createObjectURL(mediaSource); 298 video.src = window.URL.createObjectURL(mediaSource);
219 } else { 299 } else {
220 video.src = mediaFile; 300 video.src = mediaFile;
221 } 301 }
302 if (!usePrefixedEME) {
303 mediaKeys = new MediaKeys(keySystem);
304 video.setMediaKeys(mediaKeys);
shadi1 2014/03/06 01:16:21 Out of curiosity, should this always be after vide
jrummell 2014/03/06 01:55:07 Nope. But the tests timeout if it is before (http:
305 }
222 } 306 }
223 307
224 function getInitDataFromKeyId(keyID) { 308 function getInitDataFromKeyId(keyID) {
225 var init_key_id = new Uint8Array(keyID.length); 309 var init_key_id = new Uint8Array(keyID.length);
226 for(var i = 0; i < keyID.length; i++) { 310 for(var i = 0; i < keyID.length; i++) {
227 init_key_id[i] = keyID.charCodeAt(i); 311 init_key_id[i] = keyID.charCodeAt(i);
228 } 312 }
229 return init_key_id; 313 return init_key_id;
230 } 314 }
231 315
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
279 return false; 363 return false;
280 var http = new XMLHttpRequest(); 364 var http = new XMLHttpRequest();
281 http.open('HEAD', url, false); 365 http.open('HEAD', url, false);
282 try { 366 try {
283 http.send(); 367 http.send();
284 return http.status != 404; 368 return http.status != 404;
285 } catch (e) { 369 } catch (e) {
286 return false; 370 return false;
287 } 371 }
288 } 372 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698