// Copyright (c) 2013 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. "use strict"; (function() { window.__eventReporter = {}; window.__testState = {}; var logger = new Logger(); window.__logger = logger; var video_format = 'video/mp4; codecs="avc1.640028"'; var audio_format = 'audio/mp4; codecs="mp4a.40.2"'; var audio1MB = createAudioDef( 'http://localhost:8000/files/car-audio-1MB-trunc.mp4', 1048576, 65.875); var video1MB = createVideoDef( 'http://localhost:8000/files/test-video-1MB.mp4', 1031034, 1.04); // Utility functions. function Logger() { var logs = []; this.log = function(log_string) { logs.push(log_string); }; this.toString = function() { var output = ''; for (var i in logs) output += logs[i]; return output; } } function createAudioDef(src, size, duration) { return { type: 'audio', format: audio_format, size: size, src: src, duration: duration, bps: Math.floor(size / duration) }; } function createVideoDef(src, size, duration) { return { type: 'video', format: video_format, size: size, src: src, duration: duration, bps: Math.floor(size / duration) }; } function createMediaSource() { if (typeof MediaSource !== 'undefined') return new MediaSource(); else return new WebKitMediaSource(); } function createVideo() { return document.createElement('video'); } function setupVideoAndMs(onSourceopen) { var temp_video = createVideo(); var ms = createMediaSource(); ms.addEventListener('webkitsourceopen', onSourceopen); var ms_url = window.URL.createObjectURL(ms); temp_video.src = ms_url; return { 'video': temp_video, 'ms': ms }; } function XHRWrapper(file, onLoad, onError, start, length) { self = this; self.file = file; self.onLoad = onLoad; self.onError = onError; self.start = start; self.length = length; this.getResponseData = function() { var result = new Uint8Array(this.xhr.response); if (start != null) { return result.subarray(start, start + length); } return result; }; this.abort = function() { this.xhr.abort(); }; this.send = function() { this.xhr.send(); }; this.xhr = new XMLHttpRequest(); this.xhr.open('GET', file, true); this.xhr.addEventListener('load', function(e) { self.onLoad(e); }); this.xhr.addEventListener('error', function(e) { logger.log('XHR errored.'); self.onError(e); }); this.xhr.addEventListener('timeout', function(e) { logger.log('XHR timed out.'); self.onError(e); }); this.xhr.responseType = 'arraybuffer'; if (length != null) { start = start || 0; this.xhr.setRequestHeader( 'Range', 'bytes=' + start + '-' + (start + length - 1) ); } } function approxEq(a, b) { return Math.abs(a - b) < 0.5; }; // MSE tests. window.__testAttach = function() { var ms = createMediaSource(); ms.addEventListener('webkitsourceopen', function() { window.__eventReporter['sourceopen'] = true; }); var video = document.getElementById('main_player'); video.src = window.URL.createObjectURL(ms); video.load(); }; window.__testAddSourceBuffer = function() { var vm = setupVideoAndMs(function() { try { var return_value = true; return_value &= vm.ms.sourceBuffers.length === 0; vm.ms.addSourceBuffer(audio_format); return_value &= vm.ms.sourceBuffers.length === 1; vm.ms.addSourceBuffer(video_format); return_value &= vm.ms.sourceBuffers.length === 2; window.__testState['addSourceBuffer'] = !!return_value; } catch (e) { window.__testState['addSourceBuffer'] = false; } }); }; window.__testAddSupportedFormats = function() { var formats = [ audio_format, video_format, ]; var vm = setupVideoAndMs(function() { for (var i = 0; i < formats.length; ++i) { try { vm.ms.addSourceBuffer(formats[i]); } catch (e) { window.__testState['addSupportedFormats'] = false; return; } } window.__testState['addSupportedFormats'] = true; }); }; window.__testAddSourceBufferException = function() { var vm = setupVideoAndMs(function() { try { vm.ms.addSourceBuffer('^^^'); window.__testState['addSourceBufferException'] = false; return; } catch (e) { if (e.code !== DOMException.NOT_SUPPORTED_ERR) { window.__testState['addSourceBufferException'] = false; return; } } try { var temp_media_source = new WebKitMediaSource(); temp_media_source.addSourceBuffer(audio_format); window.__testState['addSourceBufferException'] = false; return; } catch (e) { if (e.code !== DOMException.INVALID_STATE_ERR) { window.__testState['addSourceBufferException'] = false; return; } } window.__testState['addSourceBufferException'] = true; }); }; window.__testInitialVideoState = function() { var temp_video = createVideo(); var test_result = isNaN(temp_video.duration); test_result &= temp_video.videoWidth === 0; test_result &= temp_video.videoHeight === 0; test_result &= temp_video.readyState === HTMLMediaElement.HAVE_NOTHING; test_result &= temp_video.src === ''; test_result &= temp_video.currentSrc === ''; window.__testState['initialVideoState'] = !!test_result; }; window.__testInitialMSState = function() { var vm = setupVideoAndMs( function() { var test_result = true; test_result = test_result && isNaN(vm.ms.duration); test_result = test_result && vm.ms.readyState === 'open'; window.__testState['initialMSState'] = test_result; }); }; function appendTestTemplate( test_name, media, test_func, abort, start, length, offset) { var vm = setupVideoAndMs(function() { var sb = vm.ms.addSourceBuffer(media.format); var xhr = new XHRWrapper( media.src, function(e) { var response_data = xhr.getResponseData(); if (offset != null) sb.timestampOffset = offset; sb.append(response_data); if (abort != null) { sb.abort(); sb.append(response_data); } var test_result = test_func(sb, media); window.__testState[test_name] = test_result; }, function(e) { window.__testState[test_name] = false; }, start, length); xhr.send(); }); } function appendInnerTest(sb, media) { return sb.buffered.length === 1 && sb.buffered.start(0) === 0 && approxEq(sb.buffered.end(0), media.duration); } window.__testAppend_audio = function() { appendTestTemplate('append_audio', audio1MB, appendInnerTest); }; window.__testAppend_video = function() { appendTestTemplate('append_video', video1MB, appendInnerTest); }; function appendAbortInnerTest(sb, media) { return sb.buffered.length === 1 && sb.buffered.start(0) === 0 && sb.buffered.end(0) > 0; } window.__testAppendAbort_audio = function() { appendTestTemplate( 'appendAbort_audio', audio1MB, appendAbortInnerTest, true, 0, 200000); }; window.__testAppendAbort_video = function() { appendTestTemplate( 'appendAbort_video', video1MB, appendAbortInnerTest, true, 0, 200000); }; var TIMESTAMP_BUFFERED_OFFSET = 5; function appendTimestampOffsetTest(sb, media) { return sb.buffered.length === 1 && sb.buffered.start(0) === TIMESTAMP_BUFFERED_OFFSET && approxEq(sb.buffered.end(0), media.duration + TIMESTAMP_BUFFERED_OFFSET); } window.__testAppendTimestampOffset_audio = function() { appendTestTemplate( 'appendTimestampOffset_audio', audio1MB, appendTimestampOffsetTest, null, null, null, TIMESTAMP_BUFFERED_OFFSET); }; window.__testAppendTimestampOffset_video = function() { appendTestTemplate( 'appendTimestampOffset_video', video1MB, appendTimestampOffsetTest, null, null, null, TIMESTAMP_BUFFERED_OFFSET); }; window.__testDuration = function() { var vm = setupVideoAndMs( function() { var DURATION_TIME = 10; vm.ms.duration = DURATION_TIME; window.setTimeout(function() { window.__testState['duration'] = vm.ms.duration === DURATION_TIME; }, 20); }); }; function testDurationAfterAppend(test_name, media) { var vm = setupVideoAndMs(function() { var sb = vm.ms.addSourceBuffer(media.format); function onDurationChange() { window.__testState[test_name] = approxEq( vm.ms.duration, sb.buffered.end(0)); } var xhr = new XHRWrapper(media.src, function() { var response_data = xhr.getResponseData(); sb.append(response_data); sb.abort(); vm.ms.duration = sb.buffered.end(0) / 2; vm.video.addEventListener('durationchange', onDurationChange); sb.append(response_data); }); xhr.send(); }); }; window.__testDurationAfterAppend_audio = function() { testDurationAfterAppend('durationAfterAppend_audio', audio1MB); }; window.__testDurationAfterAppend_video = function() { testDurationAfterAppend('durationAfterAppend_video', video1MB); }; window.__testSourceRemove = function() { var vm = setupVideoAndMs( function() { var sbAudio = vm.ms.addSourceBuffer(audio_format); var result = vm.ms.sourceBuffers.length === 1; vm.ms.removeSourceBuffer(sbAudio); result &= vm.ms.sourceBuffers.length === 0; sbAudio = vm.ms.addSourceBuffer(audio_format); result &= vm.ms.sourceBuffers.length === 1; for (var i = 0; i < 10; ++i) { var sbVideo = vm.ms.addSourceBuffer(video_format); result &= vm.ms.sourceBuffers.length === 2; vm.ms.removeSourceBuffer(sbVideo); result &= vm.ms.sourceBuffers.length === 1; } vm.ms.removeSourceBuffer(sbAudio); result &= vm.ms.sourceBuffers.length === 0; window.__testState['sourceRemove'] = !!result; }); }; // EME tests. window.__testCanPlayWebM = function() { var tempVideo = createVideo(); return tempVideo.canPlayType( 'video/webm; codecs="vp8,vorbis"') === 'probably' && tempVideo.canPlayType( 'audio/webm; codecs="vorbis"') === 'probably'; }; window.__testCanPlayClearKey = function() { var tempVideo = createVideo(); return tempVideo.canPlayType( 'video/mp4; codecs="avc1.640028"', 'webkit-org.w3.clearkey') === 'probably' && tempVideo.canPlayType( 'audio/mp4; codecs="mp4a.40.2"', 'webkit-org.w3.clearkey') === 'probably'; }; window.__testCanNotPlayPlayReady = function() { var tempVideo = createVideo(); return tempVideo.canPlayType( 'video/mp4; codecs="avc1.640028"', 'com.youtube.playready') !== 'probably' && tempVideo.canPlayType( 'audio/mp4; codecs="mp4a.40.2"', 'com.youtube.playready') !== 'probably'; }; window.__testCanPlayWidevine = function() { function createWidevineTest(mediaType) { var tempVideo = createVideo(); return function(codecs, keySystem, criteria) { var codecString = mediaType; if (codecs != null) codecString += '; codecs="' + codecs + '"'; var testResult = tempVideo.canPlayType(codecString, keySystem); if (criteria === null) return testResult === 'probably' || testResult === 'maybe'; else if (typeof(criteria) === 'string') return testResult === criteria; else if (criteria.length) { var checks = false; for (var i in criteria) checks |= testResult === criteria[i]; return !!checks; } return false; } } var audioTest = createWidevineTest('audio/webm'); var videoTest = createWidevineTest('video/webm'); var result = true; // Supported video formats. result &= videoTest(null, 'com.widevine.alpha', 'maybe'); result &= videoTest(null, 'com.widevine', 'maybe'); result &= videoTest('vp8', 'com.widevine.alpha', 'probably'); result &= videoTest('vp8', 'com.widevine', 'probably'); result &= videoTest('vp8.0', 'com.widevine.alpha', 'probably'); result &= videoTest('vp8.0', 'com.widevine', 'probably'); result &= videoTest('vorbis', 'com.widevine.alpha', 'probably'); result &= videoTest('vorbis', 'com.widevine', 'probably'); result &= videoTest('vp8,vp8.0,vorbis', 'com.widevine.alpha', 'probably'); result &= videoTest('vp8,vp8.0,vorbis', 'com.widevine', 'probably'); // Supported audio formats. result &= audioTest(null, 'com.widevine.alpha', 'maybe'); result &= audioTest(null, 'com.widevine', 'maybe'); result &= audioTest('vorbis', 'com.widevine.alpha', 'probably'); result &= audioTest('vorbis', 'com.widevine', 'probably'); // Unsupported video formats. result &= videoTest('codecs="vp8"', 'com.widevine.', ''); result &= videoTest('codecs="vp8"', 'com.widevine.foo', ''); result &= videoTest('codecs="vp8"', 'com.widevine.alpha.', ''); result &= videoTest('codecs="vp8"', 'com.widevine.alpha.foo', ''); result &= videoTest('codecs="vp8"', 'com.widevine.alph', ''); result &= videoTest('codecs="vp8"', 'com.widevine.alphb', ''); result &= videoTest('codecs="vp8"', 'com.widevine.alphaa', ''); result &= videoTest('codecs="avc1.640028"', 'com.widevine.alpha', ''); result &= videoTest('codecs="mp4a"', 'com.widevine.alpha', ''); // Unsported audio formats. result &= audioTest('codecs="vp8"', 'com.widevine', ''); result &= audioTest('codecs="vp8,vorbis"', 'com.widevine.alpha', ''); result &= audioTest('codecs="vp8,vorbis"', 'com.widevine.alpha', ''); return result; }; })();