Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /**
- * @license
- * Copyright 2018 Google Inc. All rights reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- 'use strict';
- /**
- * MSE Conformance Test Suite.
- * @class
- */
- var ConformanceTest = function() {
- var mseVersion = 'Current Editor\'s Draft';
- var webkitPrefix = MediaSource.prototype.version.indexOf('webkit') >= 0;
- var tests = [];
- var info = 'No MSE Support!';
- if (window.MediaSource) {
- info = 'MSE Spec Version: ' + mseVersion;
- info += ' | webkit prefix: ' + webkitPrefix.toString();
- }
- info += ' | Default Timeout: ' + TestBase.timeout + 'ms';
- /**
- * @param {!string} name
- * @param {?string} category
- * @param {?boolean} mandatory
- * @param {?Array<Object>} streams If any stream is unsupported, test is marked
- * optional and fails.
- */
- var createConformanceTest =
- function (testId, name, category = 'General', mandatory = true,
- streams = []) {
- var t = createMSTest(testId, name, category, mandatory, 'MSE Conformance Tests');
- t.prototype.index = tests.length;
- t.prototype.setStreams(streams);
- tests.push(t);
- return t;
- };
- /**
- * Test if the value of video state is expected when onsourceopen
- * event happens.
- */
- var createInitialMediaStateTest = function (testId, state, value, check) {
- var test = createConformanceTest(testId,
- 'InitialMedia' + util.MakeCapitalName(state), 'Media Element Core');
- check = typeof(check) === 'undefined' ? 'checkEq' : check;
- test.prototype.title = 'Test if the state ' + state +
- ' is correct when onsourceopen is called';
- test.prototype.onsourceopen = function() {
- this.runner[check](this.video[state], value, state);
- this.runner.succeed();
- };
- };
- createInitialMediaStateTest('1.1.1.1', 'duration', NaN);
- createInitialMediaStateTest('1.1.2.1', 'videoWidth', 0);
- createInitialMediaStateTest('1.1.3.1', 'videoHeight', 0);
- createInitialMediaStateTest('1.1.4.1', 'readyState', HTMLMediaElement.HAVE_NOTHING);
- createInitialMediaStateTest('1.1.5.1', 'src', '', 'checkNE');
- createInitialMediaStateTest('1.1.6.1', 'currentSrc', '', 'checkNE');
- /**
- * Validate the XHR request can send Uint8Array.
- */
- var testXHRUint8Array = createConformanceTest('1.2.1.1', 'XHRUint8Array', 'XHR');
- testXHRUint8Array.prototype.title = 'Ensure that XHR can send an Uint8Array';
- testXHRUint8Array.prototype.timeout = 10000;
- testXHRUint8Array.prototype.start = function(runner, video) {
- var s = 'XHR DATA';
- var buf = new ArrayBuffer(s.length);
- var view = new Uint8Array(buf);
- for (var i = 0; i < s.length; i++) {
- view[i] = s.charCodeAt(i);
- }
- var xhr = runner.XHRManager.createPostRequest('/echo',
- function(e) {
- runner.checkEq(String.fromCharCode.apply(null, xhr.getResponseData()),
- s, 'XHR response');
- runner.succeed();
- },
- view.length);
- xhr.send(view);
- };
- /**
- * Ensure that XHR aborts actually abort by issuing an absurd number of them
- * and then aborting all but one.
- */
- var testXHRAbort = createConformanceTest('1.2.2.1', 'XHRAbort', 'XHR');
- testXHRAbort.prototype.title = 'Ensure that XHR aborts actually abort by ' +
- 'issuing an absurd number of them and then aborting all but one.';
- testXHRAbort.prototype.start = function(runner, video) {
- var N = 100;
- var startTime = Date.now();
- var lastAbortTime;
- function startXHR(i) {
- var xhr = runner.XHRManager.createRequest(
- Media.VP9.VideoNormal.src + '?x=' + Date.now() + '.' + i, function() {
- if (i >= N) {
- xhr.getResponseData(); // This will verify status internally.
- runner.succeed();
- }
- });
- if (i < N) {
- runner.timeouts.setTimeout(xhr.abort.bind(xhr), 10);
- runner.timeouts.setTimeout(startXHR.bind(null, i + 1), 1);
- lastAbortTime = Date.now();
- }
- xhr.send();
- };
- startXHR(0);
- };
- /**
- * Ensure XMLHttpRequest.open does not reset XMLHttpRequest.responseType.
- */
- var testXHROpenState = createConformanceTest('1.2.3.1', 'XHROpenState', 'XHR');
- testXHROpenState.prototype.title = 'Ensure XMLHttpRequest.open does not ' +
- 'reset XMLHttpRequest.responseType';
- testXHROpenState.prototype.start = function(runner, video) {
- var xhr = new XMLHttpRequest;
- // It should not be an error to set responseType before calling open
- xhr.responseType = 'arraybuffer';
- xhr.open('GET', 'http://google.com', true);
- runner.checkEq(xhr.responseType, 'arraybuffer', 'XHR responseType');
- runner.succeed();
- };
- /**
- * Validate existence of MediaSource object.
- */
- var testPresence = createConformanceTest('1.3.1.1', 'Presence', 'MSE Core');
- testPresence.prototype.title = 'Test if MediaSource object is present.';
- testPresence.prototype.start = function(runner, video) {
- if (!window.MediaSource)
- return runner.fail('No MediaSource object available.');
- var ms = new MediaSource();
- if (!ms)
- return runner.fail('Found MediaSource but could not create one');
- if (ms.version)
- this.log('Media source version reported as ' + ms.version);
- else
- this.log('No media source version reported');
- runner.succeed();
- };
- /**
- * Ensure MediaSource object can be attached to video.
- */
- var testAttach = createConformanceTest('1.3.2.1', 'Attach', 'MSE Core');
- testAttach.prototype.timeout = 2000;
- testAttach.prototype.title =
- 'Test if MediaSource object can be attached to video.';
- testAttach.prototype.start = function(runner, video) {
- this.ms = new MediaSource();
- this.ms.addEventListener('sourceopen', function() {
- runner.succeed();
- });
- video.src = window.URL.createObjectURL(this.ms);
- video.load();
- };
- /**
- * Test addSourceBuffer is working correctly.
- */
- var testAddSourceBuffer = createConformanceTest('1.3.3.1', 'AddSourceBuffer',
- 'MSE Core');
- testAddSourceBuffer.prototype.title =
- 'Test if we can add source buffer';
- testAddSourceBuffer.prototype.onsourceopen = function() {
- try {
- this.runner.checkEq(
- this.ms.sourceBuffers.length, 0, 'Source buffer number');
- this.ms.addSourceBuffer(Media.AAC.mimetype);
- this.runner.checkEq(
- this.ms.sourceBuffers.length, 1, 'Source buffer number');
- this.ms.addSourceBuffer(Media.VP9.mimetype);
- this.runner.checkEq(
- this.ms.sourceBuffers.length, 2, 'Source buffer number');
- } catch (e) {
- this.runner.fail(e);
- }
- this.runner.succeed();
- };
- /**
- * Ensure add incorrect source buffer type will fire the correct exceptions.
- */
- var testAddSourceBufferException =
- createConformanceTest('1.3.4.1', 'AddSBException', 'MSE Core');
- testAddSourceBufferException.prototype.title = 'Test if add incorrect ' +
- 'source buffer type will fire the correct exceptions.';
- testAddSourceBufferException.prototype.onsourceopen = function() {
- var runner = this.runner;
- var self = this;
- runner.checkException(function() {
- self.ms.addSourceBuffer('^^^');
- }, DOMException.NOT_SUPPORTED_ERR);
- runner.checkException(function() {
- var ms = new MediaSource;
- ms.addSourceBuffer(Media.AAC.mimetype);
- }, DOMException.INVALID_STATE_ERR);
- runner.succeed();
- };
- /**
- * Test addSourceBuffer and removeSourceBuffer are working correctly.
- */
- var testSourceRemove = createConformanceTest('1.3.5.1', 'RemoveSourceBuffer',
- 'MSE Core');
- testSourceRemove.prototype.title = 'Test if we can add/remove source buffers';
- testSourceRemove.prototype.onsourceopen = function() {
- var sb = this.ms.addSourceBuffer(Media.AAC.mimetype);
- this.ms.removeSourceBuffer(sb);
- this.runner.checkEq(this.ms.sourceBuffers.length, 0, 'Source buffer number');
- this.ms.addSourceBuffer(Media.AAC.mimetype);
- this.runner.checkEq(this.ms.sourceBuffers.length, 1, 'Source buffer number');
- for (var i = 0; i < 10; ++i) {
- try {
- sb = this.ms.addSourceBuffer(Media.VP9.mimetype);
- this.runner.checkEq(this.ms.sourceBuffers.length, 2,
- 'Source buffer number');
- this.ms.removeSourceBuffer(sb);
- this.runner.checkEq(this.ms.sourceBuffers.length, 1,
- 'Source buffer number');
- } catch (e) {
- return this.runner.fail(e);
- }
- }
- this.runner.succeed();
- };
- /**
- * Ensure MediaSource state has the expected value when onsourceopen happens.
- */
- var createInitialMSStateTest = function(testId, state, value, check) {
- var test = createConformanceTest(testId,
- 'InitialMS' + util.MakeCapitalName(state), 'MSE Core');
- check = typeof(check) === 'undefined' ? 'checkEq' : check;
- test.prototype.title = 'Test if the state ' + state +
- ' is correct when onsourceopen is called';
- test.prototype.onsourceopen = function() {
- this.runner[check](this.ms[state], value, state);
- this.runner.succeed();
- };
- };
- createInitialMSStateTest('1.3.6.1', 'duration', NaN);
- createInitialMSStateTest('1.3.7.1', 'readyState', 'open');
- /**
- * Ensure we can set MediaSource.duration.
- */
- var testDuration = createConformanceTest('1.3.8.1', 'Duration', 'MSE Core');
- testDuration.prototype.title =
- 'Test if we can set duration.';
- testDuration.prototype.onsourceopen = function() {
- this.ms.duration = 10;
- this.runner.checkEq(this.ms.duration, 10, 'ms.duration');
- this.runner.succeed();
- };
- /**
- * Test events on the MediaElement.
- */
- var mediaElementEvents =
- createConformanceTest('1.3.9.1', 'MediaElementEvents', 'MSE Core');
- mediaElementEvents.prototype.title = 'Test events on the MediaElement.';
- mediaElementEvents.prototype.onsourceopen = function() {
- var runner = this.runner;
- var media = this.video;
- var ms = this.ms;
- var audioStream = Media.AAC.Audio1MB;
- var videoStream = Media.VP9.Video1MB;
- var audioSb = this.ms.addSourceBuffer(audioStream.mimetype);
- var videoSb = this.ms.addSourceBuffer(videoStream.mimetype);
- var self = this;
- var videoXhr = runner.XHRManager.createRequest(videoStream.src, function(e) {
- self.log('onload called');
- var onUpdate = function() {
- videoSb.removeEventListener('update', onUpdate);
- setDuration(1.0, ms, [videoSb, audioSb], function() {
- if (audioSb.updating || videoSb.updating) {
- runner.fail('Source buffers are updating on duration change.');
- return;
- }
- ms.endOfStream();
- media.play();
- });
- }
- videoSb.addEventListener('update', onUpdate);
- videoSb.appendBuffer(videoXhr.getResponseData());
- });
- var audioXhr = runner.XHRManager.createRequest(audioStream.src, function(e) {
- self.log('onload called');
- var onAudioUpdate = function() {
- audioSb.removeEventListener('update', onAudioUpdate);
- videoXhr.send();
- }
- audioSb.addEventListener('update', onAudioUpdate);
- audioSb.appendBuffer(audioXhr.getResponseData());
- });
- media.addEventListener('ended', function() {
- self.log('onended called');
- runner.succeed();
- });
- audioXhr.send();
- };
- /**
- * Test if the events on MediaSource are correct.
- */
- var mediaSourceEvents = createConformanceTest('1.3.10.1', 'MediaSourceEvents',
- 'MSE Core');
- mediaSourceEvents.prototype.title =
- 'Test if the events on MediaSource are correct.';
- mediaSourceEvents.prototype.onsourceopen = function() {
- var runner = this.runner;
- var media = this.video;
- var ms = this.ms;
- var audioStream = Media.AAC.Audio1MB;
- var videoStream = Media.VP9.Video1MB;
- var audioSb = this.ms.addSourceBuffer(audioStream.mimetype);
- var videoSb = this.ms.addSourceBuffer(videoStream.mimetype);
- var lastState = 'open';
- var self = this;
- var videoXhr = runner.XHRManager.createRequest(videoStream.src, function(e) {
- self.log('onload called');
- videoSb.appendBuffer(videoXhr.getResponseData());
- videoSb.abort();
- ms.endOfStream();
- });
- var audioXhr = runner.XHRManager.createRequest(audioStream.src, function(e) {
- self.log('onload called');
- audioSb.appendBuffer(audioXhr.getResponseData());
- audioSb.abort();
- videoXhr.send();
- });
- ms.addEventListener('sourceclose', function() {
- self.log('onsourceclose called');
- runner.checkEq(lastState, 'ended', 'The previous state');
- runner.succeed();
- });
- ms.addEventListener('sourceended', function() {
- self.log('onsourceended called');
- runner.checkEq(lastState, 'open', 'The previous state');
- lastState = 'ended';
- media.removeAttribute('src');
- media.load();
- });
- audioXhr.send();
- };
- /**
- * Append to buffer until exceeding the quota error.
- */
- var testBufferSize = createConformanceTest('1.3.11.1', 'VideoBufferSize',
- 'MSE Core');
- testBufferSize.prototype.title = 'Determines video buffer sizes by ' +
- 'appending incrementally until discard occurs, and tests that it meets ' +
- 'the minimum requirements for streaming.';
- testBufferSize.prototype.onsourceopen = function() {
- var runner = this.runner;
- // The test clip has a bitrate which is nearly exactly 1MB/sec, and
- // lasts 1s. We start appending it repeatedly until we get eviction.
- var videoStream = Media.VP9.Video1MB;
- var sb = this.ms.addSourceBuffer(videoStream.mimetype);
- var audioStream = Media.AAC.Audio1MB;
- var unused_audioSb = this.ms.addSourceBuffer(audioStream.mimetype);
- var self = this;
- var MIN_SIZE = 12 * 1024 * 1024;
- var ESTIMATED_MIN_TIME = 12;
- var xhr = runner.XHRManager.createRequest(videoStream.src, function(e) {
- var onBufferFull = function() {
- runner.checkGE(expectedTime - sb.buffered.start(0), ESTIMATED_MIN_TIME,
- 'Estimated source buffer size');
- runner.succeed();
- };
- var expectedTime = 0;
- var expectedSize = 0;
- var appendCount = 0;
- sb.addEventListener('updateend', function onUpdate() {
- appendCount++;
- self.log('Append count ' + appendCount);
- if (sb.buffered.start(0) > 0 || expectedTime > sb.buffered.end(0)) {
- sb.removeEventListener('updateend', onUpdate);
- onBufferFull();
- } else {
- expectedTime += videoStream.duration;
- expectedSize += videoStream.size;
- // Pass the test if the UA can handle 10x more than expected.
- if (expectedSize > (10 * MIN_SIZE)) {
- sb.removeEventListener('updateend', onUpdate);
- onBufferFull();
- return;
- }
- sb.timestampOffset = expectedTime;
- try {
- sb.appendBuffer(xhr.getResponseData());
- } catch (e) {
- var QUOTA_EXCEEDED_ERROR_CODE = 22;
- if (e.code == QUOTA_EXCEEDED_ERROR_CODE) {
- sb.removeEventListener('updateend', onUpdate);
- onBufferFull();
- } else {
- runner.fail(e);
- }
- }
- }
- });
- sb.appendBuffer(xhr.getResponseData());
- });
- xhr.send();
- };
- /**
- * Ensure we can start play before feeding any data. The play should
- * start automatically after data is appended.
- */
- var testStartPlayWithoutData = createConformanceTest('1.3.12.1',
- 'StartPlayWithoutData', 'MSE Core');
- testStartPlayWithoutData.prototype.title =
- 'Test if we can start play before feeding any data. The play should ' +
- 'start automatically after data is appended';
- testStartPlayWithoutData.prototype.onsourceopen = function() {
- var runner = this.runner;
- var media = this.video;
- var audioStream = Media.AAC.AudioHuge;
- var videoStream = Media.VP9.VideoHuge;
- var videoChain = new ResetInit(
- new FileSource(videoStream.src, runner.XHRManager, runner.timeouts));
- var videoSb = this.ms.addSourceBuffer(videoStream.mimetype);
- var audioChain = new ResetInit(
- new FileSource(audioStream.src, runner.XHRManager, runner.timeouts));
- var audioSb = this.ms.addSourceBuffer(audioStream.mimetype);
- media.play();
- appendUntil(runner.timeouts, media, videoSb, videoChain, 1, function() {
- appendUntil(runner.timeouts, media, audioSb, audioChain, 1, function() {
- playThrough(
- runner.timeouts, media, 1, 2,
- videoSb, videoChain, audioSb, audioChain, function() {
- runner.checkGE(media.currentTime, 2, 'currentTime');
- runner.succeed();
- });
- });
- });
- };
- /**
- * Ensure we can start playback from a non-zero position.
- */
- var createStartPlayAtNonZeroPositionTest = function(
- testId, audioStream, audioSegments, videoStream, videoSegments, startAtSec) {
- var test = createConformanceTest(testId,
- `StartPlayAtTimeGt0${videoStream.codec}+${audioStream.codec}`, 'MSE Core',
- true, [videoStream,
- audioStream]);
- test.prototype.title =
- 'Test if we can start playback from time > 0.';
- test.prototype.onsourceopen = function() {
- var runner = this.runner;
- var video = this.video;
- var videoSb = this.ms.addSourceBuffer(videoStream.mimetype);
- var audioSb = this.ms.addSourceBuffer(audioStream.mimetype);
- var fetchStream = function(stream, callBack, start, end) {
- var xhr =
- runner.XHRManager.createRequest(stream.src, callBack, start, end);
- xhr.send();
- }
- var appendLoop = function(stream, sourceBuffer, segments, playAndSeek) {
- var parsedData;
- var segmentIdx = 0;
- var maxSegments = segments.length;
- fetchStream(stream, function() {
- if (stream.codec == 'H264' || stream.codec == 'AAC') {
- parsedData = parseMp4(this.getResponseData());
- } else if(stream.codec == 'VP9' || stream.codec == 'Opus') {
- parsedData = parseWebM(this.getResponseData().buffer);
- } else {
- runner.fail('Unsupported codec in appendLoop.');
- }
- fetchStream(stream, function() {
- sourceBuffer.addEventListener('updateend', function append() {
- if (playAndSeek && segmentIdx == 0) {
- video.play();
- }
- if (maxSegments - segmentIdx <= 0) {
- sourceBuffer.removeEventListener('updateend', append);
- if (playAndSeek) {
- video.currentTime = playAndSeek;
- }
- return;
- }
- fetchStream(stream, function() {
- sourceBuffer.appendBuffer(this.getResponseData());
- segmentIdx += 1;
- },
- parsedData[segments[segmentIdx]].offset,
- parsedData[segments[segmentIdx]].size);
- });
- sourceBuffer.appendBuffer(this.getResponseData());
- }, 0, parsedData[0].offset); // Init segment.
- }, 0, 32 * 1024); // Enough data to parse the stream.
- };
- video.addEventListener('timeupdate', function timeupdate() {
- if (!video.paused && video.currentTime > startAtSec + 5) {
- runner.succeed();
- }
- });
- appendLoop(audioStream, audioSb, audioSegments);
- appendLoop(videoStream, videoSb, videoSegments, startAtSec);
- };
- };
- createStartPlayAtNonZeroPositionTest(
- '1.3.13.1', Media.AAC.AudioNormal, [1, 2], Media.H264.VideoNormal, [2, 3, 4], 12);
- createStartPlayAtNonZeroPositionTest(
- '1.3.14.1', Media.Opus.CarLow, [1, 2], Media.VP9.VideoNormal, [2, 3, 4], 12);
- /**
- * Ensure event timestamp is relative to the initial page load.
- */
- var testEventTimestamp = createConformanceTest('1.3.15.1', 'EventTimestamp',
- 'MSE Core');
- testEventTimestamp.prototype.title = 'Test Event Timestamp is relative to ' +
- 'the initial page load';
- testEventTimestamp.prototype.onsourceopen = function() {
- var runner = this.runner;
- var video = this.video;
- var videoStream = Media.VP9.VideoTiny;
- var audioStream = Media.AAC.AudioTiny;
- var videoSb = this.ms.addSourceBuffer(videoStream.mimetype);
- var audioSb = this.ms.addSourceBuffer(audioStream.mimetype);
- runner.checkGr(Date.now(), 1360000000000, 'Date.now()');
- var lastTime = 0.0;
- var requestCounter = 0;
- var audioXhr = runner.XHRManager.createRequest(audioStream.src, function(e) {
- audioSb.appendBuffer(this.getResponseData());
- video.addEventListener('timeupdate', function(e) {
- runner.checkGE(e.timeStamp, lastTime, 'event.timeStamp');
- lastTime = e.timeStamp;
- if (!video.paused && video.currentTime >= 2 && requestCounter >= 3) {
- runner.succeed();
- }
- requestCounter++;
- });
- video.play();
- }, 0, 500000);
- var videoXhr = runner.XHRManager.createRequest(videoStream.src, function(e) {
- videoSb.appendBuffer(this.getResponseData());
- audioXhr.send();
- }, 0, 1500000);
- videoXhr.send();
- };
- /**
- * Ensure timeupdate event fired with correct currentTime value after seeking.
- */
- var testSeekTimeUpdate = createConformanceTest('1.3.16.1', 'SeekTimeUpdate',
- 'MSE Core');
- testSeekTimeUpdate.prototype.title =
- 'Timeupdate event fired with correct currentTime after seeking.';
- testSeekTimeUpdate.prototype.onsourceopen = function() {
- var runner = this.runner;
- var media = this.video;
- var videoStream = Media.VP9.VideoNormal;
- var audioStream = Media.AAC.AudioNormal;
- var videoSb = this.ms.addSourceBuffer(videoStream.mimetype);
- var audioSb = this.ms.addSourceBuffer(audioStream.mimetype);
- var lastTime = 0;
- var updateCount = 0;
- var xhr = runner.XHRManager.createRequest(videoStream.src, function() {
- videoSb.appendBuffer(xhr.getResponseData());
- var xhr2 = runner.XHRManager.createRequest(audioStream.src, function() {
- audioSb.appendBuffer(xhr2.getResponseData());
- callAfterLoadedMetaData(media, function() {
- media.addEventListener('timeupdate', function(e) {
- if (!media.paused) {
- ++updateCount;
- runner.checkGE(media.currentTime, lastTime, 'media.currentTime');
- if (updateCount > 3) {
- updateCount = 0;
- lastTime += 10;
- if (lastTime >= 35)
- runner.succeed();
- else
- media.currentTime = lastTime + 6;
- }
- }
- });
- media.play();
- });
- }, 0, 1000000);
- xhr2.send();
- }, 0, 5000000);
- this.ms.duration = 100000000; // Ensure that we can seek to any position.
- xhr.send();
- };
- /**
- * Test SourceBuffer.appendWindowStart can be correctly set and applied.
- */
- var testAppendWindowStart =
- createConformanceTest('1.3.17.1', 'AppendWindowStart', 'MSE Core');
- testAppendWindowStart.prototype.title =
- 'Test if SourceBuffer respects appendWindowStart for appending.';
- testAppendWindowStart.prototype.onsourceopen = function() {
- var runner = this.runner;
- var start = 3.4;
- var videoStream = Media.VP9.VideoNormal;
- var videoSb = this.ms.addSourceBuffer(videoStream.mimetype);
- // appendWindowStart cannot be smaller than 0
- // or greater than or equal to appendWindowEnd.
- try {
- videoSb.appendWindowStart = -1;
- } catch (e) {
- runner.checkEq(e.name, 'TypeError', 'Expected error');
- }
- try {
- videoSb.appendWindowEnd = 10;
- videoSb.appendWindowStart = 11;
- } catch (e) {
- runner.checkEq(e.name, 'TypeError', 'Expected error');
- }
- runner.checkEq(videoSb.appendWindowStart, 0, 'appendWindowStart');
- videoSb.appendWindowStart = start;
- var xhr = runner.XHRManager.createRequest(videoStream.src, function(e) {
- videoSb.appendBuffer(this.getResponseData());
- videoSb.addEventListener('updateend', function() {
- runner.checkEq(videoSb.appendWindowStart, start, 'appendWindowStart');
- // More frames maybe dropped due to missing key frame.
- runner.checkGE(videoSb.buffered.start(0), start, 'Buffered range start');
- runner.succeed();
- });
- }, 0, 3000000);
- xhr.send();
- };
- /**
- * Test SourceBuffer.appendWindowEnd can be correctly set and applied.
- */
- var testAppendWindowEnd =
- createConformanceTest('1.3.18.1', 'AppendWindowEnd', 'MSE Core');
- testAppendWindowEnd.prototype.title =
- 'Test if SourceBuffer respects appendWindowEnd for appending.';
- testAppendWindowEnd.prototype.onsourceopen = function() {
- var runner = this.runner;
- var end = 5.3;
- var videoStream = Media.VP9.VideoNormal;
- var videoSb = this.ms.addSourceBuffer(videoStream.mimetype);
- // appendWindowEnd cannot be smaller than appendWindowStart.
- try {
- videoSb.appendWindowStart = 2;
- videoSb.appendWindowEnd = 1;
- } catch (e) {
- runner.checkEq(e.name, 'TypeError', 'Expected error');
- }
- runner.checkEq(videoSb.appendWindowEnd, 'Infinity', 'appendWindowEnd');
- videoSb.appendWindowStart = 0;
- videoSb.appendWindowEnd = end;
- var xhr = runner.XHRManager.createRequest(videoStream.src, function(e) {
- videoSb.appendBuffer(this.getResponseData());
- videoSb.addEventListener('updateend', function() {
- runner.checkEq(videoSb.appendWindowEnd, end, 'appendWindowEnd');
- runner.checkApproxEq(
- videoSb.buffered.end(0), end, 'Buffered range end', 0.05);
- runner.succeed();
- });
- }, 0, 3000000);
- xhr.send();
- };
- var createSourceBufferChangeTypeTest = function(testId, fromStream, toStream) {
- var test = createConformanceTest(testId,
- `ChangeType.${fromStream.codec}.${toStream.codec}`, 'MSE Core', false, [fromStream,
- toStream]);
- test.prototype.title =
- `Test SourceBuffer.changeType() from ${fromStream.codec} ` +
- `to ${toStream.codec}`;
- test.prototype.onsourceopen = function() {
- var video = this.video;
- var ms = this.ms;
- var runner = this.runner;
- var videoStreams = [fromStream, toStream];
- function feedVideoElement(streamIndex) {
- var secondsToBuffer = 2;
- if (streamIndex == videoStreams.length) {
- ms.endOfStream();
- video.addEventListener('timeupdate', function(e) {
- if (!video.paused && video.currentTime >= secondsToBuffer * 2) {
- runner.succeed();
- }
- });
- video.play();
- return;
- }
- try {
- var sourceBuffer;
- var videoStream = videoStreams[streamIndex];
- if (ms.sourceBuffers.length == 0) {
- sourceBuffer = ms.addSourceBuffer(videoStream.mimetype);
- sourceBuffer.mode = 'sequence';
- } else {
- sourceBuffer = ms.sourceBuffers[0];
- sourceBuffer.changeType(videoStream.mimetype);
- }
- var xhr = runner.XHRManager.createRequest(videoStream.src, function(e) {
- sourceBuffer.appendBuffer(this.getResponseData());
- sourceBuffer.addEventListener('updateend', function() {
- feedVideoElement(++streamIndex);
- }, { once: true });
- }, 0, videoStream.bps * secondsToBuffer);
- xhr.send();
- } catch(error) {
- runner.fail(error);
- }
- }
- feedVideoElement(0);
- }
- }
- createSourceBufferChangeTypeTest('1.3.19.1', Media.H264.VideoTiny, Media.VP9.VideoTiny);
- createSourceBufferChangeTypeTest('1.3.20.1', Media.H264.VideoTiny,
- Media.AV1.Bunny144p30fps);
- createSourceBufferChangeTypeTest('1.3.21.1', Media.VP9.VideoTiny, Media.H264.VideoTiny);
- createSourceBufferChangeTypeTest('1.3.22.1', Media.VP9.VideoTiny,
- Media.AV1.Bunny144p30fps);
- createSourceBufferChangeTypeTest('1.3.23.1', Media.AV1.Bunny144p30fps,
- Media.H264.VideoTiny);
- createSourceBufferChangeTypeTest('1.3.24.1', Media.AV1.Bunny144p30fps,
- Media.VP9.VideoTiny);
- const testPlaybackRateChange = createConformanceTest('1.3.25.1',
- 'PlaybackRateChange',
- 'MSE Core');
- testPlaybackRateChange.prototype.title =
- 'Test if playback rate can be changed in the middle of stream.';
- testPlaybackRateChange.prototype.onsourceopen = function() {
- const runner = this.runner;
- const media = this.video;
- const videoStream = Media.VP9.VideoNormal;
- const audioStream = Media.AAC.AudioNormal;
- const videoSb = this.ms.addSourceBuffer(videoStream.mimetype);
- const audioSb = this.ms.addSourceBuffer(audioStream.mimetype);
- let baseMediaTime = 0.0;
- let baseRealTime = 0.0;
- const videoPerfMetrics = new VideoPerformanceMetrics(media);
- let isBaseCaptured = false;
- let isRateChanged = false;
- let isBaseUpdated = false;
- let updateCount = 0;
- const xhr = runner.XHRManager.createRequest(videoStream.src, function() {
- videoSb.appendBuffer(xhr.getResponseData());
- const xhr2 = runner.XHRManager.createRequest(audioStream.src, function() {
- audioSb.appendBuffer(xhr2.getResponseData());
- callAfterLoadedMetaData(media, function() {
- media.addEventListener('timeupdate', function(e) {
- // Limit updates to once every 500ms (once every 2 events).
- updateCount++;
- if (updateCount % 2 === 0) {
- return;
- }
- let time = media.currentTime;
- if (time < 0.5) {
- if(!isBaseCaptured) {
- isBaseCaptured = true;
- baseRealTime = Date.now();
- baseMediaTime = time;
- console.log(`Starting measurements at mediaTime ${baseMediaTime}`);
- }
- } else if (time < 1.5) {
- console.log(`Taking playback measurement at mediaTime ${time}`);
- let playbackRate1x =
- getActualPlaybackRate(time, Date.now(), baseMediaTime,
- baseRealTime);
- runner.checkLE(playbackRate1x, 1.05, 'Playback rate');
- } else if (time < 4) {
- if (!isRateChanged) {
- isRateChanged = true;
- media.playbackRate = 2.0;
- console.log(`Changing playback rate to 2x at mediaTime ${time}`);
- }
- } else if (time < 5) {
- if (!isBaseUpdated) {
- isBaseUpdated = true;
- baseRealTime = Date.now();
- baseMediaTime = time;
- console.log(`Recapturing baseline measurements at mediaTime ${baseMediaTime}`);
- }
- } else if (time < 7) {
- console.log(`Taking playback measurement at mediaTime ${time}`);
- let playbackRate2x =
- getActualPlaybackRate(time, Date.now(), baseMediaTime,
- baseRealTime);
- runner.checkGE(playbackRate2x, 1.95, 'Playback rate');
- } else {
- runner.checkLE(videoPerfMetrics.getDroppedVideoFrames(), 1,
- 'Total dropped frames');
- runner.succeed();
- }
- });
- media.play();
- });
- }, 0, 1000000);
- xhr2.send();
- }, 0, 5000000);
- xhr.send();
- };
- function getActualPlaybackRate(currentMediaTime, currentRealTime, baseMediaTime, baseRealTime) {
- let mediaTimeElapsed = currentMediaTime - baseMediaTime;
- let realTimeElapsed = (currentRealTime - baseRealTime) / 1000.0;
- return (mediaTimeElapsed / realTimeElapsed).toFixed(3);
- }
- /**
- * Creates a MSE currentTime Accuracy test to validate if the media.currentTime
- * is accurate to within 250 milliseconds during active playback. This can
- * be used for video features a standard frame rate or a high frame rate.
- */
- var createCurrentTimeAccuracyTest =
- function(testId, videoStream, audioStream, frameRate) {
- var test = createConformanceTest(testId, frameRate + 'Accuracy',
- 'MSE currentTime');
- test.prototype.title = 'Test the currentTime granularity.';
- test.prototype.onsourceopen = function() {
- var runner = this.runner;
- var video = this.video;
- var maxTimeDiff = 0;
- var baseTimeDiff = 0;
- let zeroTimeDiff = 0;
- var times = 0;
- var videoSb = this.ms.addSourceBuffer(videoStream.mimetype);
- var audioSb = this.ms.addSourceBuffer(audioStream.mimetype);
- var videoXhr = runner.XHRManager.createRequest(
- videoStream.src, function(e) {
- videoSb.appendBuffer(this.getResponseData());
- video.addEventListener('timeupdate', function(e) {
- if (times === 0) {
- zeroTimeDiff = util.ElapsedTimeInS() - video.currentTime;
- }
- else if (times === 1) {
- baseTimeDiff = util.ElapsedTimeInS() - video.currentTime;
- // Make sure the warmup time is less than 500 ms as stated in the
- // spec.
- runner.checkLE(
- baseTimeDiff - zeroTimeDiff, 0.50, 'intial warm up time');
- } else {
- let timeDiff = util.ElapsedTimeInS() - video.currentTime;
- maxTimeDiff = Math.max(
- Math.abs(timeDiff - baseTimeDiff), maxTimeDiff);
- }
- if (times > 500 || video.currentTime > 10) {
- runner.checkLE(
- maxTimeDiff, 0.25, 'media.currentTime diff during playback');
- runner.succeed();
- }
- ++times;
- });
- video.addEventListener('canplaythrough', function(e) {
- video.play();
- });
- }, 0, 2500000);
- var audioXhr = runner.XHRManager.createRequest(
- audioStream.src, function(e) {
- audioSb.appendBuffer(this.getResponseData());
- videoXhr.send();
- }, 0, 2500000);
- audioXhr.send();
- };
- };
- createCurrentTimeAccuracyTest(
- '1.4.1.1', Media.H264.Webgl720p30fps, Media.AAC.AudioNormal, 'SFR');
- createCurrentTimeAccuracyTest(
- '1.4.2.1', Media.H264.Webgl720p60fps, Media.AAC.AudioNormal, 'HFR');
- /**
- * Creates a copy of the currentTimeAccuracy test due to b/266064197.
- * The spec of when the first timeUpdate should be fired is not clear,
- * so we kept a separate test to keep track of that. This test can be
- * waived, but we should still run this against all partners.
- * b/270470795 is filed for tracking future changes.
- */
- let createCurrentTimeAccuracySpecTest =
- function(testId, videoStream, audioStream, frameRate) {
- let test = createConformanceTest(testId, frameRate + 'SpecAccuracy',
- 'MSE currentTime');
- test.prototype.title = 'Test the currentTime granularity.';
- test.prototype.onsourceopen = function() {
- let runner = this.runner;
- let video = this.video;
- let maxTimeDiff = 0;
- let baseTimeDiff = 0;
- let times = 0;
- let videoSb = this.ms.addSourceBuffer(videoStream.mimetype);
- let audioSb = this.ms.addSourceBuffer(audioStream.mimetype);
- let videoXhr = runner.XHRManager.createRequest(
- videoStream.src, function(e) {
- videoSb.appendBuffer(this.getResponseData());
- video.addEventListener('timeupdate', function(e) {
- if (times === 0) {
- baseTimeDiff = util.ElapsedTimeInS() - video.currentTime;
- } else {
- let timeDiff = util.ElapsedTimeInS() - video.currentTime;
- maxTimeDiff = Math.max(
- Math.abs(timeDiff - baseTimeDiff), maxTimeDiff);
- runner.checkLE(
- maxTimeDiff, 0.25, 'media.currentTime diff during playback');
- runner.succeed();
- }
- ++times;
- });
- video.addEventListener('canplaythrough', function(e) {
- video.play();
- });
- }, 0, 2500000);
- let audioXhr = runner.XHRManager.createRequest(
- audioStream.src, function(e) {
- audioSb.appendBuffer(this.getResponseData());
- videoXhr.send();
- }, 0, 2500000);
- audioXhr.send();
- };
- };
- createCurrentTimeAccuracySpecTest(
- '1.4.1.2', Media.H264.Webgl720p30fps, Media.AAC.AudioNormal, 'SFR');
- createCurrentTimeAccuracySpecTest(
- '1.4.2.2', Media.H264.Webgl720p60fps, Media.AAC.AudioNormal, 'HFR');
- /**
- * Creates a MSE currentTime PausedAccuracy test to validate if
- * the media.currentTime is accurate to within the given threshold when content
- * is paused. This can be used for video features a standard frame rate
- * or a high frame rate. Test checks the accuracy of media.currentTime at
- * two events: when content is paused and when content is played again after
- * the pause, if either one meets the threshold, test passes.
- */
- var createCurrentTimePausedAccuracyTest =
- function(testId, videoStream, audioStream, frameRate, maxDiffInS) {
- const testName = frameRate + 'PausedAccuracy'
- + parseFloat(maxDiffInS * 1000).toFixed(0) + 'ms';
- var test = createConformanceTest(testId, testName, 'MSE currentTime', false);
- test.prototype.title = 'Test the currentTime granularity when pause.';
- test.prototype.onsourceopen = function() {
- var runner = this.runner;
- var video = this.video;
- var baseTimeDiff = 0;
- var times = 0;
- var assertTimeAtPlay = false;
- var currentTimeIsAccurate = false;
- var self = this;
- var videoSb = this.ms.addSourceBuffer(videoStream.mimetype);
- var audioSb = this.ms.addSourceBuffer(audioStream.mimetype);
- var videoXhr = runner.XHRManager.createRequest(
- videoStream.src, function(e) {
- videoSb.appendBuffer(this.getResponseData());
- function onTimeUpdate(e) {
- if (times === 1) {
- baseTimeDiff = util.ElapsedTimeInS() - video.currentTime;
- }
- if (times > 500 || video.currentTime > 10) {
- video.removeEventListener('timeupdate', onTimeUpdate);
- video.pause();
- }
- ++times;
- };
- video.addEventListener('play', function() {
- if (assertTimeAtPlay) {
- var timeDiff = util.ElapsedTimeInS() - video.currentTime;
- var currentTimeDiff = Math.abs(baseTimeDiff - timeDiff);
- self.log('media.currentTime is ' + currentTimeDiff + 's different' +
- ' from actual time when video is played after a pause.');
- currentTimeIsAccurate =
- currentTimeIsAccurate || (currentTimeDiff <= maxDiffInS);
- runner.checkEq(
- currentTimeIsAccurate,
- true,
- 'media.currentTime diff is within ' + maxDiffInS + 's');
- assertTimeAtPlay = false;
- runner.succeed();
- }
- });
- video.addEventListener('pause', function(e) {
- var timeDiff = util.ElapsedTimeInS() - video.currentTime;
- var currentTimeDiff = Math.abs(baseTimeDiff - timeDiff);
- runner.checkEq(video.paused, true, 'media.paused');
- self.log('meida.currentTime is ' + currentTimeDiff +
- 's different from actual time when video is paused.');
- currentTimeIsAccurate = currentTimeDiff <= maxDiffInS;
- assertTimeAtPlay = true;
- video.play();
- });
- video.addEventListener('timeupdate', onTimeUpdate);
- video.addEventListener('canplaythrough', function(e) {
- video.play();
- })
- }, 0, 2500000);
- var audioXhr = runner.XHRManager.createRequest(
- audioStream.src, function(e) {
- audioSb.appendBuffer(this.getResponseData());
- videoXhr.send();
- }, 0, 2500000);
- audioXhr.send();
- };
- };
- createCurrentTimePausedAccuracyTest(
- '1.4.3.1', Media.VP9.Webgl720p30fps, Media.AAC.AudioNormal, 'SFR', 0.032);
- createCurrentTimePausedAccuracyTest(
- '1.4.3.2', Media.VP9.Webgl720p30fps, Media.AAC.AudioNormal, 'SFR', 0.100);
- createCurrentTimePausedAccuracyTest(
- '1.4.4.1', Media.VP9.Webgl720p60fps, Media.AAC.AudioNormal, 'HFR', 0.032);
- createCurrentTimePausedAccuracyTest(
- '1.4.4.2', Media.VP9.Webgl720p60fps, Media.AAC.AudioNormal, 'HFR', 0.100);
- /**
- * Validate specified mimetype is supported.
- */
- var createSupportTest = function(testId, mimetype, desc, mandatory) {
- var test = createConformanceTest(testId, desc + 'Support', 'MSE Formats',
- mandatory);
- test.prototype.title =
- 'Test if we support ' + desc + ' with mimetype: ' + mimetype;
- test.prototype.onsourceopen = function() {
- try {
- this.log('Trying format ' + mimetype);
- var src = this.ms.addSourceBuffer(mimetype);
- } catch (e) {
- return this.runner.fail(e);
- }
- this.runner.succeed();
- };
- };
- createSupportTest('1.5.1.1', Media.AAC.mimetype, 'AAC');
- createSupportTest('1.5.2.1', Media.H264.mimetype, 'H264');
- createSupportTest('1.5.3.1', Media.VP9.mimetype, 'VP9');
- createSupportTest('1.5.4.1', Media.Opus.mimetype, 'Opus');
- createSupportTest('1.5.5.1', Media.AV1.mimetype, 'AV1', true);
- /**
- * Test media with mismatched frame duration and segment timing.
- */
- var frameTestOnSourceOpen = function() {
- var runner = this.runner;
- var media = this.video;
- var audioStream = Media.AAC.AudioNormal;
- var videoChain = new FixedAppendSize(new ResetInit(
- new FileSource(this.filename, runner.XHRManager, runner.timeouts)));
- var videoSb = this.ms.addSourceBuffer(Media.H264.mimetype);
- var audioChain = new FixedAppendSize(new ResetInit(
- new FileSource(audioStream.src, runner.XHRManager, runner.timeouts)));
- var audioSb = this.ms.addSourceBuffer(audioStream.mimetype);
- media.play();
- playThrough(runner.timeouts, media, 5, 18, videoSb, videoChain,
- audioSb, audioChain, runner.succeed.bind(runner));
- };
- var testFrameGaps = createConformanceTest('1.8.1.1', 'H264FrameGaps', 'Media');
- testFrameGaps.prototype.title = 'Test media with frame durations of 24FPS ' +
- 'but segment timing corresponding to 23.976FPS';
- testFrameGaps.prototype.filename = Media.H264.FrameGap.src;
- testFrameGaps.prototype.onsourceopen = frameTestOnSourceOpen;
- var testFrameOverlaps = createConformanceTest('1.8.2.1', 'H264FrameOverlaps',
- 'Media');
- testFrameOverlaps.prototype.title = 'Test media with frame durations of ' +
- '23.976FPS but segment timing corresponding to 24FPS';
- testFrameOverlaps.prototype.filename = Media.H264.FrameOverlap.src;
- testFrameOverlaps.prototype.onsourceopen = frameTestOnSourceOpen;
- /**
- * Test playback of HE-AAC (High-Efficiency Advanced Audio Coding) with
- * specified type of SBR signaling.
- */
- var createHeAacTest = function(testId, audioStream) {
- var test = createConformanceTest(testId, 'HE-AAC/' +
- audioStream.get('sbrSignaling') + 'SBR', 'Media');
- test.prototype.title = 'Test playback of HE-AAC with ' +
- audioStream.get('sbrSignaling') + ' SBR signaling.';
- test.prototype.onsourceopen = function() {
- var runner = this.runner;
- var media = this.video;
- var ms = this.ms;
- var videoStream = Media.H264.VideoHeAac;
- var audioSb = this.ms.addSourceBuffer(audioStream.mimetype);
- var videoSb = this.ms.addSourceBuffer(videoStream.mimetype);
- var xhr = runner.XHRManager.createRequest(audioStream.src, function(e) {
- audioSb.addEventListener('update', function() {
- var xhr2 = runner.XHRManager.createRequest(videoStream.src,
- function(e) {
- videoSb.addEventListener('update', function() {
- ms.endOfStream();
- media.addEventListener('ended', function(e) {
- if (media.currentTime > audioStream.duration + 1) {
- runner.fail();
- } else {
- runner.checkApproxEq(media.currentTime, audioStream.duration,
- 'media.currentTime');
- runner.succeed();
- }
- });
- media.play();
- });
- videoSb.appendBuffer(xhr2.getResponseData());
- }, 0, videoStream.size);
- xhr2.send();
- });
- audioSb.appendBuffer(xhr.getResponseData());
- }, 0, audioStream.size);
- xhr.send();
- }
- }
- createHeAacTest('1.8.3.1', Media.AAC.AudioLowExplicitHE);
- createHeAacTest('1.8.4.1', Media.AAC.AudioLowImplicitHE);
- return {tests: tests, info: info, viewType: 'default'};
- };
- window.ConformanceTest = ConformanceTest;
- try {
- exports.getTest = ConformanceTest;
- } catch (e) {
- // do nothing, this function is not supposed to work for browser, but it's for
- // Node js to generate json file instead.
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement