1// Copyright (c) 2013 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. 4"use strict"; 5 6(function() { 7 window.__eventReporter = {}; 8 window.__testState = {}; 9 10 var logger = new Logger(); 11 window.__logger = logger; 12 13 var video_format = 'video/mp4; codecs="avc1.640028"'; 14 var audio_format = 'audio/mp4; codecs="mp4a.40.2"'; 15 16 var audio1MB = createAudioDef( 17 'http://localhost:8000/files/car-audio-1MB-trunc.mp4', 1048576, 65.875); 18 var video1MB = createVideoDef( 19 'http://localhost:8000/files/test-video-1MB.mp4', 1031034, 1.04); 20 21 // Utility functions. 22 function Logger() { 23 var logs = []; 24 25 this.log = function(log_string) { 26 logs.push(log_string); 27 }; 28 29 this.toString = function() { 30 var output = ''; 31 for (var i in logs) 32 output += logs[i]; 33 34 return output; 35 } 36 } 37 38 function createAudioDef(src, size, duration) { 39 return { 40 type: 'audio', 41 format: audio_format, 42 size: size, 43 src: src, 44 duration: duration, 45 bps: Math.floor(size / duration) 46 }; 47 } 48 49 function createVideoDef(src, size, duration) { 50 return { 51 type: 'video', 52 format: video_format, 53 size: size, 54 src: src, 55 duration: duration, 56 bps: Math.floor(size / duration) 57 }; 58 } 59 60 function createMediaSource() { 61 if (typeof MediaSource !== 'undefined') 62 return new MediaSource(); 63 else 64 return new WebKitMediaSource(); 65 } 66 67 function createVideo() { 68 return document.createElement('video'); 69 } 70 71 function setupVideoAndMs(onSourceopen) { 72 var temp_video = createVideo(); 73 var ms = createMediaSource(); 74 ms.addEventListener('webkitsourceopen', onSourceopen); 75 var ms_url = window.URL.createObjectURL(ms); 76 temp_video.src = ms_url; 77 return { 78 'video': temp_video, 79 'ms': ms 80 }; 81 } 82 83 function XHRWrapper(file, onLoad, onError, start, length) { 84 self = this; 85 86 self.file = file; 87 self.onLoad = onLoad; 88 self.onError = onError; 89 self.start = start; 90 self.length = length; 91 92 this.getResponseData = function() { 93 var result = new Uint8Array(this.xhr.response); 94 if (start != null) { 95 return result.subarray(start, start + length); 96 } 97 return result; 98 }; 99 100 this.abort = function() { 101 this.xhr.abort(); 102 }; 103 104 this.send = function() { 105 this.xhr.send(); 106 }; 107 108 this.xhr = new XMLHttpRequest(); 109 this.xhr.open('GET', file, true); 110 111 this.xhr.addEventListener('load', function(e) { 112 self.onLoad(e); 113 }); 114 115 this.xhr.addEventListener('error', function(e) { 116 logger.log('XHR errored.'); 117 self.onError(e); 118 }); 119 120 this.xhr.addEventListener('timeout', function(e) { 121 logger.log('XHR timed out.'); 122 self.onError(e); 123 }); 124 125 this.xhr.responseType = 'arraybuffer'; 126 if (length != null) { 127 start = start || 0; 128 this.xhr.setRequestHeader( 129 'Range', 130 'bytes=' + start + '-' + (start + length - 1) 131 ); 132 } 133 } 134 135 function approxEq(a, b) { 136 return Math.abs(a - b) < 0.5; 137 }; 138 139 // MSE tests. 140 window.__testAttach = function() { 141 var ms = createMediaSource(); 142 ms.addEventListener('webkitsourceopen', function() { 143 window.__eventReporter['sourceopen'] = true; 144 }); 145 146 var video = document.getElementById('main_player'); 147 video.src = window.URL.createObjectURL(ms); 148 video.load(); 149 }; 150 151 window.__testAddSourceBuffer = function() { 152 var vm = setupVideoAndMs(function() { 153 try { 154 var return_value = true; 155 return_value &= vm.ms.sourceBuffers.length === 0; 156 vm.ms.addSourceBuffer(audio_format); 157 return_value &= vm.ms.sourceBuffers.length === 1; 158 vm.ms.addSourceBuffer(video_format); 159 return_value &= vm.ms.sourceBuffers.length === 2; 160 161 window.__testState['addSourceBuffer'] = !!return_value; 162 } 163 catch (e) { 164 window.__testState['addSourceBuffer'] = false; 165 } 166 }); 167 }; 168 169 window.__testAddSupportedFormats = function() { 170 var formats = [ 171 audio_format, 172 video_format, 173 ]; 174 175 var vm = setupVideoAndMs(function() { 176 for (var i = 0; i < formats.length; ++i) { 177 try { 178 vm.ms.addSourceBuffer(formats[i]); 179 } catch (e) { 180 window.__testState['addSupportedFormats'] = false; 181 return; 182 } 183 } 184 window.__testState['addSupportedFormats'] = true; 185 }); 186 }; 187 188 window.__testAddSourceBufferException = function() { 189 var vm = setupVideoAndMs(function() { 190 try { 191 vm.ms.addSourceBuffer('^^^'); 192 window.__testState['addSourceBufferException'] = false; 193 return; 194 } 195 catch (e) { 196 if (e.code !== DOMException.NOT_SUPPORTED_ERR) { 197 window.__testState['addSourceBufferException'] = false; 198 return; 199 } 200 } 201 202 try { 203 var temp_media_source = new WebKitMediaSource(); 204 temp_media_source.addSourceBuffer(audio_format); 205 window.__testState['addSourceBufferException'] = false; 206 return; 207 } 208 catch (e) { 209 if (e.code !== DOMException.INVALID_STATE_ERR) { 210 window.__testState['addSourceBufferException'] = false; 211 return; 212 } 213 } 214 window.__testState['addSourceBufferException'] = true; 215 }); 216 }; 217 218 window.__testInitialVideoState = function() { 219 var temp_video = createVideo(); 220 221 var test_result = isNaN(temp_video.duration); 222 test_result &= temp_video.videoWidth === 0; 223 test_result &= temp_video.videoHeight === 0; 224 test_result &= temp_video.readyState === HTMLMediaElement.HAVE_NOTHING; 225 test_result &= temp_video.src === ''; 226 test_result &= temp_video.currentSrc === ''; 227 228 window.__testState['initialVideoState'] = !!test_result; 229 }; 230 231 window.__testInitialMSState = function() { 232 var vm = setupVideoAndMs( 233 function() { 234 var test_result = true; 235 test_result = test_result && isNaN(vm.ms.duration); 236 test_result = test_result && vm.ms.readyState === 'open'; 237 window.__testState['initialMSState'] = test_result; 238 }); 239 }; 240 241 function appendTestTemplate( 242 test_name, media, test_func, abort, start, length, offset) { 243 var vm = setupVideoAndMs(function() { 244 var sb = vm.ms.addSourceBuffer(media.format); 245 var xhr = new XHRWrapper( 246 media.src, 247 function(e) { 248 var response_data = xhr.getResponseData(); 249 250 if (offset != null) 251 sb.timestampOffset = offset; 252 253 sb.append(response_data); 254 255 if (abort != null) { 256 sb.abort(); 257 sb.append(response_data); 258 } 259 260 var test_result = test_func(sb, media); 261 window.__testState[test_name] = test_result; 262 }, 263 function(e) { 264 window.__testState[test_name] = false; 265 }, start, length); 266 267 xhr.send(); 268 }); 269 } 270 271 function appendInnerTest(sb, media) { 272 return sb.buffered.length === 1 && sb.buffered.start(0) === 0 && 273 approxEq(sb.buffered.end(0), media.duration); 274 } 275 276 window.__testAppend_audio = function() { 277 appendTestTemplate('append_audio', audio1MB, appendInnerTest); 278 }; 279 280 window.__testAppend_video = function() { 281 appendTestTemplate('append_video', video1MB, appendInnerTest); 282 }; 283 284 function appendAbortInnerTest(sb, media) { 285 return sb.buffered.length === 1 && sb.buffered.start(0) === 0 && 286 sb.buffered.end(0) > 0; 287 } 288 289 window.__testAppendAbort_audio = function() { 290 appendTestTemplate( 291 'appendAbort_audio', audio1MB, appendAbortInnerTest, true, 0, 200000); 292 }; 293 294 window.__testAppendAbort_video = function() { 295 appendTestTemplate( 296 'appendAbort_video', video1MB, appendAbortInnerTest, true, 0, 200000); 297 }; 298 299 var TIMESTAMP_BUFFERED_OFFSET = 5; 300 function appendTimestampOffsetTest(sb, media) { 301 302 return sb.buffered.length === 1 && 303 sb.buffered.start(0) === TIMESTAMP_BUFFERED_OFFSET && 304 approxEq(sb.buffered.end(0), media.duration + TIMESTAMP_BUFFERED_OFFSET); 305 } 306 307 window.__testAppendTimestampOffset_audio = function() { 308 appendTestTemplate( 309 'appendTimestampOffset_audio', audio1MB, appendTimestampOffsetTest, 310 null, null, null, TIMESTAMP_BUFFERED_OFFSET); 311 }; 312 313 window.__testAppendTimestampOffset_video = function() { 314 appendTestTemplate( 315 'appendTimestampOffset_video', video1MB, appendTimestampOffsetTest, 316 null, null, null, TIMESTAMP_BUFFERED_OFFSET); 317 }; 318 319 window.__testDuration = function() { 320 var vm = setupVideoAndMs( 321 function() { 322 var DURATION_TIME = 10; 323 vm.ms.duration = DURATION_TIME; 324 window.setTimeout(function() { 325 window.__testState['duration'] = vm.ms.duration === DURATION_TIME; 326 }, 20); 327 }); 328 }; 329 330 function testDurationAfterAppend(test_name, media) { 331 var vm = setupVideoAndMs(function() { 332 var sb = vm.ms.addSourceBuffer(media.format); 333 334 function onDurationChange() { 335 window.__testState[test_name] = approxEq( 336 vm.ms.duration, sb.buffered.end(0)); 337 } 338 339 var xhr = new XHRWrapper(media.src, function() { 340 var response_data = xhr.getResponseData(); 341 sb.append(response_data); 342 sb.abort(); 343 vm.ms.duration = sb.buffered.end(0) / 2; 344 vm.video.addEventListener('durationchange', onDurationChange); 345 sb.append(response_data); 346 }); 347 348 xhr.send(); 349 }); 350 }; 351 352 window.__testDurationAfterAppend_audio = function() { 353 testDurationAfterAppend('durationAfterAppend_audio', audio1MB); 354 }; 355 356 window.__testDurationAfterAppend_video = function() { 357 testDurationAfterAppend('durationAfterAppend_video', video1MB); 358 }; 359 360 window.__testSourceRemove = function() { 361 var vm = setupVideoAndMs( 362 function() { 363 var sbAudio = vm.ms.addSourceBuffer(audio_format); 364 var result = vm.ms.sourceBuffers.length === 1; 365 vm.ms.removeSourceBuffer(sbAudio); 366 result &= vm.ms.sourceBuffers.length === 0; 367 368 sbAudio = vm.ms.addSourceBuffer(audio_format); 369 result &= vm.ms.sourceBuffers.length === 1; 370 for (var i = 0; i < 10; ++i) { 371 var sbVideo = vm.ms.addSourceBuffer(video_format); 372 result &= vm.ms.sourceBuffers.length === 2; 373 vm.ms.removeSourceBuffer(sbVideo); 374 result &= vm.ms.sourceBuffers.length === 1; 375 } 376 377 vm.ms.removeSourceBuffer(sbAudio); 378 result &= vm.ms.sourceBuffers.length === 0; 379 380 window.__testState['sourceRemove'] = !!result; 381 }); 382 }; 383 384 // EME tests. 385 window.__testCanPlayWebM = function() { 386 var tempVideo = createVideo(); 387 return tempVideo.canPlayType( 388 'video/webm; codecs="vp8,vorbis"') === 'probably' && 389 tempVideo.canPlayType( 390 'audio/webm; codecs="vorbis"') === 'probably'; 391 }; 392 393 window.__testCanPlayClearKey = function() { 394 var tempVideo = createVideo(); 395 return tempVideo.canPlayType( 396 'video/mp4; codecs="avc1.640028"', 397 'webkit-org.w3.clearkey') === 'probably' && 398 tempVideo.canPlayType( 399 'audio/mp4; codecs="mp4a.40.2"', 400 'webkit-org.w3.clearkey') === 'probably'; 401 }; 402 403 window.__testCanNotPlayPlayReady = function() { 404 var tempVideo = createVideo(); 405 return tempVideo.canPlayType( 406 'video/mp4; codecs="avc1.640028"', 407 'com.youtube.playready') !== 'probably' && 408 tempVideo.canPlayType( 409 'audio/mp4; codecs="mp4a.40.2"', 410 'com.youtube.playready') !== 'probably'; 411 }; 412 413 window.__testCanPlayWidevine = function() { 414 function createWidevineTest(mediaType) { 415 var tempVideo = createVideo(); 416 417 return function(codecs, keySystem, criteria) { 418 var codecString = mediaType; 419 if (codecs != null) 420 codecString += '; codecs="' + codecs + '"'; 421 422 var testResult = tempVideo.canPlayType(codecString, keySystem); 423 424 if (criteria === null) 425 return testResult === 'probably' || testResult === 'maybe'; 426 else if (typeof(criteria) === 'string') 427 return testResult === criteria; 428 else if (criteria.length) { 429 var checks = false; 430 for (var i in criteria) 431 checks |= testResult === criteria[i]; 432 return !!checks; 433 } 434 return false; 435 } 436 } 437 438 var audioTest = createWidevineTest('audio/webm'); 439 var videoTest = createWidevineTest('video/webm'); 440 441 var result = true; 442 443 // Supported video formats. 444 result &= videoTest(null, 'com.widevine.alpha', 'maybe'); 445 result &= videoTest(null, 'com.widevine', 'maybe'); 446 result &= videoTest('vp8', 'com.widevine.alpha', 'probably'); 447 result &= videoTest('vp8', 'com.widevine', 'probably'); 448 result &= videoTest('vp8.0', 'com.widevine.alpha', 'probably'); 449 result &= videoTest('vp8.0', 'com.widevine', 'probably'); 450 result &= videoTest('vorbis', 'com.widevine.alpha', 'probably'); 451 result &= videoTest('vorbis', 'com.widevine', 'probably'); 452 result &= videoTest('vp8,vp8.0,vorbis', 'com.widevine.alpha', 'probably'); 453 result &= videoTest('vp8,vp8.0,vorbis', 'com.widevine', 'probably'); 454 455 // Supported audio formats. 456 result &= audioTest(null, 'com.widevine.alpha', 'maybe'); 457 result &= audioTest(null, 'com.widevine', 'maybe'); 458 result &= audioTest('vorbis', 'com.widevine.alpha', 'probably'); 459 result &= audioTest('vorbis', 'com.widevine', 'probably'); 460 461 // Unsupported video formats. 462 result &= videoTest('codecs="vp8"', 'com.widevine.', ''); 463 result &= videoTest('codecs="vp8"', 'com.widevine.foo', ''); 464 result &= videoTest('codecs="vp8"', 'com.widevine.alpha.', ''); 465 result &= videoTest('codecs="vp8"', 'com.widevine.alpha.foo', ''); 466 result &= videoTest('codecs="vp8"', 'com.widevine.alph', ''); 467 result &= videoTest('codecs="vp8"', 'com.widevine.alphb', ''); 468 result &= videoTest('codecs="vp8"', 'com.widevine.alphaa', ''); 469 result &= videoTest('codecs="avc1.640028"', 'com.widevine.alpha', ''); 470 result &= videoTest('codecs="mp4a"', 'com.widevine.alpha', ''); 471 472 // Unsported audio formats. 473 result &= audioTest('codecs="vp8"', 'com.widevine', ''); 474 result &= audioTest('codecs="vp8,vorbis"', 'com.widevine.alpha', ''); 475 result &= audioTest('codecs="vp8,vorbis"', 'com.widevine.alpha', ''); 476 477 return result; 478 }; 479})(); 480