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