2020/media/mseCodecTest.js (1,340 lines of code) (raw):
/**
* @license
* Copyright 2019 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 Codec Test Suite.
* @class
*/
var MsecodecTest = 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';
var fields = ['passes', 'failures', 'timeouts'];
/**
* @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 createCodecTest =
function(testId, name, category = 'General', mandatory = true, streams = []) {
var t = createMSTest(testId, name, category, mandatory, 'MSE Codec Tests');
t.prototype.index = tests.length;
t.prototype.setStreams(streams);
tests.push(t);
return t;
};
/**
* Test appendBuffer for specified mimetype by appending twice in a row.
* When the first append happens, the sourceBuffer becomes temporarily unusable
* and it's updating should be set to true, which makes the second appends
* unsuccessful and throws INVALID_STATE_ERR exception.
* However, sometimes the update happens so fast that the second append manage
* as well.
*/
var createAppendTest = function(testId, stream, unused_stream, mandatory) {
var test = createCodecTest(
testId,
'Append' + stream.codec + util.MakeCapitalName(stream.mediatype),
'MSE (' + stream.codec + ')',
mandatory,
[stream]);
test.prototype.title =
`Test if we can append 1MB of a ${stream.mediatype} file.`;
test.prototype.onsourceopen = function() {
var runner = this.runner;
var sb = this.ms.addSourceBuffer(stream.mimetype);
var unused_sb = this.ms.addSourceBuffer(unused_stream.mimetype);
var xhr = runner.XHRManager.createRequest(stream.src, function(e) {
var data = xhr.getResponseData();
function updateEnd(e) {
runner.checkEq(sb.buffered.length, 1, 'Source buffer number');
runner.checkEq(sb.buffered.start(0), 0, 'Range start');
runner.checkApproxEq(sb.buffered.end(0), stream.duration, 'Range end');
// Try appending twice in a row --
// this should throw an INVALID_STATE_ERR exception.
var caught = false;
try {
sb.removeEventListener('updateend', updateEnd);
sb.appendBuffer(data);
sb.appendBuffer(data);
}
catch (e) {
if (e.code === e.INVALID_STATE_ERR) {
runner.succeed();
} else {
runner.fail('Invalid error on double append: ' + e);
}
caught = true;
}
if (!caught) {
// We may have updated so fast that we didn't encounter the error.
if (sb.updating) {
// Not a great check due to race conditions, but will have to do.
runner.fail('Implementation did not throw INVALID_STATE_ERR.');
} else {
runner.succeed();
}
}
}
sb.addEventListener('updateend', updateEnd);
sb.appendBuffer(data);
}, 0, stream.size);
xhr.send();
};
};
/**
* Ensure sourceBuffer can abort current segment and end up with correct value.
*/
var createAbortTest = function(testId, stream, unused_stream, mandatory) {
var test = createCodecTest(
testId,
'Abort' + stream.codec + util.MakeCapitalName(stream.mediatype),
'MSE (' + stream.codec + ')',
mandatory,
[stream]);
test.prototype.title = 'Test if we can abort the current segment.';
test.prototype.onsourceopen = function() {
var runner = this.runner;
var sb = this.ms.addSourceBuffer(stream.mimetype);
var unused_sb = this.ms.addSourceBuffer(unused_stream.mimetype);
var xhr = runner.XHRManager.createRequest(stream.src, function(e) {
var responseData = xhr.getResponseData();
var abortEnded = function(e) {
sb.removeEventListener('updateend', abortEnded);
sb.addEventListener('update', function(e) {
runner.checkEq(sb.buffered.length, 1, 'Source buffer number');
runner.checkEq(sb.buffered.start(0), 0, 'Range start');
runner.checkGr(sb.buffered.end(0), 0, 'Range end');
runner.succeed();
});
sb.appendBuffer(responseData);
}
var appendStarted = function(e) {
sb.removeEventListener('update', appendStarted);
sb.addEventListener('updateend', abortEnded);
sb.abort();
}
sb.addEventListener('update', appendStarted);
sb.appendBuffer(responseData);
}, 0, stream.size);
xhr.send();
};
};
/**
* Ensure timestamp offset can be set.
*/
var createTimestampOffsetTest = function(
testId, stream, unused_stream, mandatory) {
var test = createCodecTest(
testId,
'TimestampOffset' + stream.codec +
util.MakeCapitalName(stream.mediatype),
'MSE (' + stream.codec + ')',
mandatory,
[stream]);
test.prototype.title = 'Test if we can set timestamp offset.';
test.prototype.onsourceopen = function() {
var runner = this.runner;
var sb = this.ms.addSourceBuffer(stream.mimetype);
var unused_sb = this.ms.addSourceBuffer(unused_stream.mimetype);
var xhr = runner.XHRManager.createRequest(stream.src, function(e) {
sb.timestampOffset = 5;
sb.appendBuffer(xhr.getResponseData());
sb.addEventListener('updateend', function() {
runner.checkEq(sb.buffered.length, 1, 'Source buffer number');
runner.checkEq(sb.buffered.start(0), 5, 'Range start');
runner.checkApproxEq(sb.buffered.end(0), stream.duration + 5,
'Range end');
runner.succeed();
});
});
xhr.send();
};
};
/**
* Test the sourceBuffer DASH switch latency.
* Validate it's less than 1 second.
*/
var createDASHLatencyTest = function(
testId, videoStream, audioStream, mandatory) {
var test = createCodecTest(
testId,
'DASHLatency' + videoStream.codec,
'MSE (' + videoStream.codec + ')',
mandatory,
[videoStream, audioStream]);
test.prototype.title = 'Test SourceBuffer DASH switch latency';
test.prototype.onsourceopen = function() {
var self = this;
var runner = this.runner;
var videoSb = this.ms.addSourceBuffer(videoStream.mimetype);
var audioSb = this.ms.addSourceBuffer(audioStream.mimetype);
var video = this.video;
var videoXhr = runner.XHRManager.createRequest(videoStream.src,
function(e) {
var videoContent = videoXhr.getResponseData();
var expectedTime = 0;
var loopCount = 0;
var MAX_ITER = 300;
var OVERFLOW_OFFSET = 1.0;
var onBufferFull = function() {
var bufferSize = loopCount * videoStream.size / 1048576;
self.log('Buffer size: ' + Math.round(bufferSize) + 'MB');
var DASH_MAX_LATENCY = 1;
var newContentStartTime = videoSb.buffered.start(0) + 2;
self.log('Source buffer updated as exceeding buffer limit');
video.addEventListener('timeupdate', function onTimeUpdate(e) {
if (video.currentTime > newContentStartTime + DASH_MAX_LATENCY) {
video.removeEventListener('timeupdate', onTimeUpdate);
runner.succeed();
}
});
video.play();
}
videoSb.addEventListener('update', function onUpdate() {
expectedTime += videoStream.duration;
videoSb.timestampOffset = expectedTime;
loopCount++;
if (loopCount > MAX_ITER) {
videoSb.removeEventListener('update', onUpdate);
runner.fail('Failed to fill up source buffer.');
return;
}
// Fill up the buffer such that it overflow implementations.
if (expectedTime > videoSb.buffered.end(0) + OVERFLOW_OFFSET) {
videoSb.removeEventListener('update', onUpdate);
onBufferFull();
}
try {
videoSb.appendBuffer(videoContent);
} catch (e) {
videoSb.removeEventListener('update', onUpdate);
var QUOTA_EXCEEDED_ERROR_CODE = 22;
if (e.code == QUOTA_EXCEEDED_ERROR_CODE) {
onBufferFull();
} else {
runner.fail(e);
}
}
});
videoSb.appendBuffer(videoContent);;
});
var audioXhr = runner.XHRManager.createRequest(audioStream.src,
function(e) {
var audioContent = audioXhr.getResponseData();
audioSb.appendBuffer(audioContent);
videoXhr.send();
});
audioXhr.send();
};
};
/**
* Ensure valid duration change after append buffer by halving the duration.
*/
var createDurationAfterAppendTest = function(
testId, stream, unused_stream, mandatory) {
var test = createCodecTest(
testId,
'DurationAfterAppend' + stream.codec +
util.MakeCapitalName(stream.mediatype),
'MSE (' + stream.codec + ')',
mandatory,
[stream]);
test.prototype.title = 'Test if the duration expands after appending data.';
test.prototype.onsourceopen = function() {
var runner = this.runner;
var media = this.video;
var ms = this.ms;
var sb = ms.addSourceBuffer(stream.mimetype);
var unused_sb = ms.addSourceBuffer(unused_stream.mimetype);
var self = this;
var xhr = runner.XHRManager.createRequest(stream.src,
function(e) {
var data = xhr.getResponseData();
var updateCb = function() {
var halfDuration;
var durationChanged = false;
var sbUpdated = false;
sb.removeEventListener('updateend', updateCb);
sb.abort();
if (sb.updating) {
runner.fail();
return;
}
media.addEventListener(
'durationchange', function onDurationChange() {
media.removeEventListener('durationchange', onDurationChange);
self.log('Duration change complete.');
runner.checkApproxEq(ms.duration, halfDuration, 'ms.duration');
durationChanged = true;
if (durationChanged && sbUpdated) {
runner.succeed();
}
});
halfDuration = sb.buffered.end(0) / 2;
setDuration(halfDuration, ms, sb, function() {
self.log('Remove() complete.');
runner.checkApproxEq(ms.duration, halfDuration, 'ms.duration');
runner.checkApproxEq(sb.buffered.end(0), halfDuration,
'sb.buffered.end(0)');
sb.addEventListener('updateend', function onUpdate() {
sb.removeEventListener('updateend', onUpdate);
runner.checkApproxEq(ms.duration, sb.buffered.end(0),
'ms.duration');
sbUpdated = true;
if (durationChanged && sbUpdated) {
runner.succeed();
}
});
sb.appendBuffer(data);
});
};
sb.addEventListener('updateend', updateCb);
sb.appendBuffer(data);
});
xhr.send();
};
};
/**
* Test pause state before or after appending data to sourceBuffer.
*/
var createPausedTest = function(testId, stream, mandatory) {
var test = createCodecTest(
testId,
'PausedStateWith' + stream.codec +
util.MakeCapitalName(stream.mediatype),
'MSE (' + stream.codec + ')',
mandatory,
[stream]);
test.prototype.title = 'Test if the paused state is correct before or ' +
' after appending data.';
test.prototype.onsourceopen = function() {
var runner = this.runner;
var media = this.video;
var ms = this.ms;
var sb = ms.addSourceBuffer(stream.mimetype);
runner.checkEq(media.paused, true, 'media.paused');
var xhr = runner.XHRManager.createRequest(stream.src,
function(e) {
runner.checkEq(media.paused, true, 'media.paused');
sb.appendBuffer(xhr.getResponseData());
runner.checkEq(media.paused, true, 'media.paused');
sb.addEventListener('updateend', function() {
runner.checkEq(media.paused, true, 'media.paused');
runner.succeed();
});
});
xhr.send();
};
};
/**
* Test if video dimension is correct before or after appending data.
*/
var createVideoDimensionTest = function(
testId, videoStream, audioStream, mandatory) {
var test = createCodecTest(
testId,
'VideoDimension' + videoStream.codec,
'MSE (' + videoStream.codec + ')',
mandatory,
[videoStream, audioStream]);
test.prototype.title =
'Test if video dimension is correct before or after appending data.';
test.prototype.onsourceopen = function() {
var runner = this.runner;
var media = this.video;
var videoChain = new ResetInit(new FixedAppendSize(
new FileSource(videoStream.src, runner.XHRManager, runner.timeouts),
65536));
var videoSb = this.ms.addSourceBuffer(videoStream.mimetype);
var audioSb = this.ms.addSourceBuffer(audioStream.mimetype);
var self = this;
runner.checkEq(media.videoWidth, 0, 'video width');
runner.checkEq(media.videoHeight, 0, 'video height');
var totalSuccess = 0;
function checkSuccess() {
totalSuccess++;
if (totalSuccess == 2)
runner.succeed();
}
media.addEventListener('loadedmetadata', function(e) {
self.log('loadedmetadata called');
runner.checkEq(media.videoWidth, 640, 'video width');
runner.checkEq(media.videoHeight, 360, 'video height');
checkSuccess();
});
runner.checkEq(media.readyState, media.HAVE_NOTHING, 'readyState');
var audioXhr = runner.XHRManager.createRequest(audioStream.src,
function(e) {
var audioContent = audioXhr.getResponseData();
audioSb.appendBuffer(audioContent);
appendInit(media, videoSb, videoChain, 0, checkSuccess);
});
audioXhr.send();
};
};
/**
* Test if the playback state transition is correct.
*/
var createPlaybackStateTest = function(testId, stream, mandatory) {
var test = createCodecTest(
testId,
'PlaybackState' + stream.codec,
'MSE (' + stream.codec + ')',
mandatory,
[stream]);
test.prototype.title = 'Test if the playback state transition is correct.';
test.prototype.onsourceopen = function() {
var runner = this.runner;
var media = this.video;
var videoStream = stream;
var audioStream = Media.AAC.AudioTiny;
var videoChain = new ResetInit(new FixedAppendSize(
new FileSource(videoStream.src, runner.XHRManager, runner.timeouts),
65536));
var videoSb = this.ms.addSourceBuffer(videoStream.mimetype);
var audioChain = new ResetInit(new FixedAppendSize(
new FileSource(audioStream.src, runner.XHRManager, runner.timeouts),
65536));
var audioSb = this.ms.addSourceBuffer(audioStream.mimetype);
var self = this;
media.play();
runner.checkEq(media.currentTime, 0, 'media.currentTime');
media.pause();
runner.checkEq(media.currentTime, 0, 'media.currentTime');
appendInit(media, audioSb, audioChain, 0, function() {
appendInit(media, videoSb, videoChain, 0, function() {
callAfterLoadedMetaData(media, function() {
media.play();
runner.checkEq(media.currentTime, 0, 'media.currentTime');
media.pause();
runner.checkEq(media.currentTime, 0, 'media.currentTime');
media.play();
appendUntil(
runner.timeouts, media, audioSb, audioChain, 5, function() {
appendUntil(
runner.timeouts, media, videoSb, videoChain, 5, function() {
playThrough(runner.timeouts, media, 1, 2, audioSb,
audioChain, videoSb, videoChain, function() {
var time = media.currentTime;
media.pause();
runner.checkApproxEq(
media.currentTime, time, 'media.currentTime');
runner.succeed();
});
});
});
});
});
});
};
};
/**
* Ensure we can play a partially appended video segment.
*/
var createPlayPartialSegmentTest = function(testId, stream, mandatory) {
var test = createCodecTest(
testId,
'PlayPartial' + stream.codec + 'Segment',
'MSE (' + stream.codec + ')',
mandatory,
[stream]);
test.prototype.title =
'Test if we can play a partially appended video segment.';
test.prototype.onsourceopen = function() {
var runner = this.runner;
var video = this.video;
var videoStream = stream;
var audioStream = Media.AAC.AudioTiny;
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 (!video.paused && video.currentTime >= 2) {
runner.succeed();
}
});
video.play();
}, 0, 1500000);
var audioXhr = runner.XHRManager.createRequest(
audioStream.src, function(e) {
audioSb.appendBuffer(this.getResponseData());
videoXhr.send();
}, 0, 500000);
audioXhr.send();
};
};
/**
* Ensure we can play a partially appended audio segment.
*/
var createIncrementalAudioTest = function(testId, stream) {
var test = createCodecTest(
testId,
'Incremental' + stream.codec + 'Audio',
'MSE (' + stream.codec + ')',
true,
[stream]);
test.prototype.title =
'Test if we can play a partially appended audio segment.';
test.prototype.onsourceopen = function() {
var runner = this.runner;
var sb = this.ms.addSourceBuffer(stream.mimetype);
var unused_sb = this.ms.addSourceBuffer(Media.VP9.mimetype);
var xhr = runner.XHRManager.createRequest(stream.src, function(e) {
sb.appendBuffer(xhr.getResponseData());
sb.addEventListener('updateend', function() {
runner.checkEq(sb.buffered.length, 1, 'Source buffer number');
runner.checkEq(sb.buffered.start(0), 0, 'Range start');
runner.checkApproxEq(
sb.buffered.end(0), stream.get(200000), 'Range end');
runner.succeed();
});
}, 0, 200000);
xhr.send();
};
};
var createLimitedAudioTest = function(testId, stream) {
var test = createCodecTest(
testId,
'Limited' + stream.codec + 'Audio',
'MSE (' + stream.codec + ')',
true,
[stream]);
test.prototype.title =
'Test if we can play an audio segment of only 0.5 seconds.';
test.prototype.onsourceopen = function() {
var runner = this.runner;
var video = this.video;
var videoStream = Media.H264.VideoNormal;
var audioStream = stream;
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('playing', function(e) {
if (!video.paused) {
video.pause();
runner.succeed();
}
});
video.play();
}, 0, 1500000);
var audioXhr = runner.XHRManager.createRequest(stream.src, function(e) {
audioSb.addEventListener('updateend', function() {
runner.checkEq(audioSb.buffered.length, 1, 'Source buffer number');
runner.checkEq(audioSb.buffered.start(0), 0, 'Range start');
runner.checkApproxEq(audioSb.buffered.end(0), 0.5, 'Range end', 0.02);
});
audioSb.appendBuffer(this.getResponseData());
videoXhr.send();
}, 0, stream.get('halfSecondRangeEnd'));
audioXhr.send();
};
};
var createIncrementalLimitedAudioTest = function(testId, stream) {
var test = createCodecTest(
testId,
'IncrementalLimited' + stream.codec + 'Audio',
'MSE (' + stream.codec + ')',
true,
[stream]);
test.prototype.title =
'Test if we can append and play 0.5s of audio at a time.';
test.prototype.onsourceopen = function() {
var runner = this.runner;
var video = this.video;
var videoStream = Media.H264.VideoNormal;
var audioStream = stream;
var videoSb = this.ms.addSourceBuffer(videoStream.mimetype);
var audioSb = this.ms.addSourceBuffer(audioStream.mimetype);
var ms = this.ms;
var videoPromise = new Promise(function(resolve, reject) {
var videoXhr = runner.XHRManager.createRequest(
videoStream.src, function(e) {
videoSb.appendBuffer(this.getResponseData());
video.addEventListener('timeupdate', function(e) {
if (!video.paused &&
video.currentTime >= stream.get('halfSecondDurationEnd')) {
runner.succeed();
}
});
resolve();
}, 0, 1500000);
videoXhr.send();
});
var audioPromise = new Promise(function(resolve, reject) {
var nextAudioXhr = function(byteIndex) {
var startBytes = audioStream.get('halfSecondBytes')[byteIndex];
var endBytes = audioStream.get('halfSecondBytes')[byteIndex + 1] - 1;
var bytesLength = endBytes - startBytes + 1;
var xhr = runner.XHRManager.createRequest(audioStream.src, function(e) {
audioSb.addEventListener('updateend', function callXhr() {
audioSb.removeEventListener('updateend', callXhr);
if (byteIndex < audioStream.get('halfSecondBytes').length - 2) {
resolve();
nextAudioXhr(byteIndex + 1);
} else {
ms.endOfStream();
}
});
audioSb.appendBuffer(this.getResponseData());
}, startBytes, bytesLength);
xhr.send();
};
nextAudioXhr(0);
});
Promise.all([videoPromise, audioPromise]).then(function() {
video.play();
});
};
};
/**
* Ensure we can append audio data with an explicit offset.
*/
var createAppendAudioOffsetTest = function(testId, stream1, stream2) {
var test = createCodecTest(
testId,
'Append' + stream1.codec + 'AudioOffset',
'MSE (' + stream1.codec + ')',
true,
[stream1, stream2]);
test.prototype.title =
'Test if we can append audio data with an explicit offset.';
test.prototype.onsourceopen = function() {
var runner = this.runner;
var video = this.video;
var unused_sb = this.ms.addSourceBuffer(Media.VP9.mimetype);
var sb = this.ms.addSourceBuffer(stream1.mimetype);
var xhr = runner.XHRManager.createRequest(stream1.src, function(e) {
sb.timestampOffset = 5;
sb.appendBuffer(this.getResponseData());
sb.addEventListener('updateend', function callXhr2() {
sb.removeEventListener('updateend', callXhr2);
xhr2.send();
});
}, 0, 200000);
var xhr2 = runner.XHRManager.createRequest(stream2.src, function(e) {
sb.abort();
sb.timestampOffset = 0;
sb.appendBuffer(this.getResponseData());
sb.addEventListener('updateend', function() {
runner.checkEq(sb.buffered.length, 1, 'Source buffer number');
runner.checkEq(sb.buffered.start(0), 0, 'Range start');
runner.checkApproxEq(
sb.buffered.end(0), stream2.get('appendAudioOffset'), 'Range end');
runner.succeed();
});
}, 0, 200000);
xhr.send();
};
};
/**
* Ensure we can append video data with an explicit offset.
*/
var createAppendVideoOffsetTest = function(
testId, stream1, stream2, audioStream, mandatory) {
var test = createCodecTest(
testId,
'Append' + stream1.codec + 'VideoOffset',
'MSE (' + stream1.codec + ')',
mandatory,
[stream1, stream2]);
test.prototype.title =
'Test if we can append video data with an explicit offset.';
test.prototype.onsourceopen = function() {
var self = this;
var runner = this.runner;
var video = this.video;
var sb = this.ms.addSourceBuffer(stream1.mimetype);
var audioSb = this.ms.addSourceBuffer(audioStream.mimetype);
var xhr = runner.XHRManager.createRequest(stream1.src, function(e) {
sb.timestampOffset = 5;
sb.appendBuffer(this.getResponseData());
sb.addEventListener('update', function callXhr2() {
sb.removeEventListener('update', callXhr2);
xhr2.send();
});
}, 0, 200000);
var xhr2 = runner.XHRManager.createRequest(stream2.src, function(e) {
sb.abort();
sb.timestampOffset = 0;
sb.appendBuffer(this.getResponseData());
sb.addEventListener('updateend', function() {
runner.checkEq(sb.buffered.length, 1, 'Source buffer number');
runner.checkEq(sb.buffered.start(0), 0, 'Range start');
runner.checkApproxEq(sb.buffered.end(0),
stream2.get('videoChangeRate'), 'Range end');
callAfterLoadedMetaData(video, function() {
video.addEventListener('seeked', function(e) {
self.log('seeked called');
video.addEventListener('timeupdate', function(e) {
self.log('timeupdate called with ' + video.currentTime);
if (!video.paused && video.currentTime >= 6) {
runner.succeed();
}
});
});
video.currentTime = 6;
});
});
video.play();
}, 0, 400000);
this.ms.duration = 100000000; // Ensure that we can seek to any position.
var audioXhr = runner.XHRManager.createRequest(audioStream.src,
function(e) {
var audioContent = audioXhr.getResponseData();
audioSb.appendBuffer(audioContent);
xhr.send();
});
audioXhr.send();
};
};
/**
* Ensure we can append multiple init segments.
*/
var createAppendMultipleInitTest = function(
testId, stream, unused_stream, mandatory) {
var test = createCodecTest(
testId,
'AppendMultipleInit' + stream.codec +
util.MakeCapitalName(stream.mediatype),
'MSE (' + stream.codec + ')',
mandatory,
[stream]);
test.prototype.title = 'Test if we can append multiple init segments.';
test.prototype.onsourceopen = function() {
var runner = this.runner;
var media = this.video;
var chain = new FileSource(stream.src, runner.XHRManager, runner.timeouts,
0, stream.size, stream.size);
var src = this.ms.addSourceBuffer(stream.mimetype);
var unused_sb = this.ms.addSourceBuffer(unused_stream.mimetype);
var init;
function getEventAppend(cb, endCb) {
var chainCount = 0;
return function() {
if (chainCount < 10) {
++chainCount;
cb();
} else {
endCb();
}
};
}
chain.init(0, function(buf) {
init = buf;
chain.pull(function(buf) {
var firstAppend = getEventAppend(function() {
src.appendBuffer(init);
}, function() {
src.removeEventListener('update', firstAppend);
src.addEventListener('update', function abortAppend() {
src.removeEventListener('update', abortAppend);
src.abort();
var end = src.buffered.end(0);
var secondAppend = getEventAppend(function() {
src.appendBuffer(init);
}, function() {
runner.checkEq(src.buffered.end(0), end, 'Range end');
runner.succeed();
});
src.addEventListener('update', secondAppend);
secondAppend();
});
src.appendBuffer(buf);
});
src.addEventListener('update', firstAppend);
firstAppend();
});
});
};
};
/**
* Test appending segments out of order.
*/
var createAppendOutOfOrderTest = function(
testId, stream, unused_stream, mandatory) {
var test = createCodecTest(
testId,
'Append' + stream.codec + util.MakeCapitalName(stream.mediatype) +
'OutOfOrder',
'MSE (' + stream.codec + ')',
mandatory,
[stream]);
test.prototype.title = 'Test appending segments out of order.';
test.prototype.onsourceopen = function() {
var runner = this.runner;
var media = this.video;
var chain = new FileSource(stream.src, runner.XHRManager, runner.timeouts);
var sb = this.ms.addSourceBuffer(stream.mimetype);
var unused_sb = this.ms.addSourceBuffer(unused_stream.mimetype);
var bufs = [];
var i = 0;
// Append order of the segments.
var appendOrder = [0, 2, 1, 4, 3];
// Number of segments given the append order, since segments get merged.
var bufferedLength = [0, 1, 1, 2, 1];
sb.addEventListener('updateend', function() {
runner.checkEq(sb.buffered.length, bufferedLength[i],
'Source buffer number');
if (i == 1) {
runner.checkGr(sb.buffered.start(0), 0, 'Range start');
} else if (i > 0) {
runner.checkEq(sb.buffered.start(0), 0, 'Range start');
}
i++;
if (i >= bufs.length) {
runner.succeed();
} else {
sb.appendBuffer(bufs[appendOrder[i]]);
}
});
chain.init(0, function(buf) {
bufs.push(buf);
chain.pull(function(buf) {
bufs.push(buf);
chain.pull(function(buf) {
bufs.push(buf);
chain.pull(function(buf) {
bufs.push(buf);
chain.pull(function(buf) {
bufs.push(buf);
sb.appendBuffer(bufs[0]);
});
});
});
});
});
};
};
/**
* Test SourceBuffer.buffered get updated correctly after feeding data.
*/
var createBufferedRangeTest = function(testId, stream, unused_stream, mandatory) {
var test = createCodecTest(
testId,
'BufferedRange' + stream.codec + util.MakeCapitalName(stream.mediatype),
'MSE (' + stream.codec + ')',
mandatory,
[stream]);
test.prototype.title =
'Test SourceBuffer.buffered get updated correctly after feeding data.';
test.prototype.onsourceopen = function() {
var runner = this.runner;
var media = this.video;
var chain = new ResetInit(
new FileSource(stream.src, runner.XHRManager, runner.timeouts));
var sb = this.ms.addSourceBuffer(stream.mimetype);
var unused_sb = this.ms.addSourceBuffer(unused_stream.mimetype);
runner.checkEq(sb.buffered.length, 0, 'Source buffer number');
appendInit(media, sb, chain, 0, function() {
runner.checkEq(sb.buffered.length, 0, 'Source buffer number');
appendUntil(runner.timeouts, media, sb, chain, 5, function() {
runner.checkEq(sb.buffered.length, 1, 'Source buffer number');
runner.checkEq(sb.buffered.start(0), 0, 'Source buffer number');
runner.checkGE(sb.buffered.end(0), 5, 'Range end');
runner.succeed();
});
});
};
};
/**
* Ensure the duration on MediaSource can be set and retrieved sucessfully.
*/
var createMediaSourceDurationTest =
function(testId, videoStream, audioStream, mandatory) {
var test = createCodecTest(
testId,
'MediaSourceDuration' + videoStream.codec,
'MSE (' + videoStream.codec + ')',
mandatory,
[videoStream, audioStream]);
test.prototype.title = 'Test if the duration on MediaSource can be set ' +
'and retrieved sucessfully.';
test.prototype.onsourceopen = function() {
var runner = this.runner;
var media = this.video;
var ms = this.ms;
var videoFileSource = new FileSource(
videoStream.src, runner.XHRManager, runner.timeouts);
var videoChain = new ResetInit(videoFileSource);
var videoSb = this.ms.addSourceBuffer(videoStream.mimetype);
var audioSb = this.ms.addSourceBuffer(audioStream.mimetype);
var self = this;
var onsourceclose = function() {
self.log('onsourceclose called');
runner.assert(isNaN(ms.duration));
runner.succeed();
};
var appendVideo = function() {
runner.assert(isNaN(media.duration), 'Initial media duration not NaN');
media.play();
appendInit(media, videoSb, videoChain, 0, function() {
var duration1 = videoFileSource.segs[1].time;
var duration2 = videoFileSource.segs[2].time;
var eps = 0.01;
appendUntil(runner.timeouts, media, videoSb, videoChain, duration2,
function() {
setDuration(duration1, ms, [videoSb, audioSb], function() {
runner.checkApproxEq(ms.duration, duration1, 'ms.duration', eps);
runner.checkApproxEq(
media.duration, duration1, 'media.duration', eps);
runner.checkLE(
videoSb.buffered.end(0), duration1 + 0.1, 'Range end');
videoSb.abort();
videoChain.seek(0);
appendInit(media, videoSb, videoChain, 0, function() {
appendUntil(runner.timeouts, media, videoSb, videoChain,
duration2, function() {
runner.checkApproxEq(
ms.duration, duration2, 'ms.duration', eps);
setDuration(duration1, ms, [videoSb, audioSb], function() {
if (videoSb.updating) {
runner.fail(
'Source buffer is updating on duration change');
return;
}
var duration = videoSb.buffered.end(0);
ms.endOfStream();
runner.checkApproxEq(
ms.duration, duration, 'ms.duration', eps);
ms.addEventListener('sourceended', function() {
runner.checkApproxEq(
ms.duration, duration, 'ms.duration', eps);
runner.checkApproxEq(
media.duration, duration, 'media.duration', eps);
ms.addEventListener('sourceclose', onsourceclose);
media.removeAttribute('src');
media.load();
});
media.play();
});
});
});
});
});
});
};
var audioXhr = runner.XHRManager.createRequest(audioStream.src,
function(e) {
audioSb.addEventListener('updateend', function onAudioUpdate() {
audioSb.removeEventListener('updateend', onAudioUpdate);
appendVideo();
});
var audioContent = audioXhr.getResponseData();
audioSb.appendBuffer(audioContent);
});
audioXhr.send();
};
};
/**
* Validate media data with overlap is merged into one range.
*/
var createOverlapTest = function(testId, stream, unused_stream, mandatory) {
var test = createCodecTest(
testId,
stream.codec + util.MakeCapitalName(stream.mediatype) + 'WithOverlap',
'MSE (' + stream.codec + ')',
mandatory,
[stream]);
test.prototype.title =
'Test if media data with overlap will be merged into one range.';
test.prototype.onsourceopen = function() {
var runner = this.runner;
var media = this.video;
var chain = new ResetInit(
new FileSource(stream.src, runner.XHRManager, runner.timeouts));
var sb = this.ms.addSourceBuffer(stream.mimetype);
var unused_sb = this.ms.addSourceBuffer(unused_stream.mimetype);
var GAP = 0.1;
appendInit(media, sb, chain, 0, function() {
chain.pull(function(buf) {
sb.addEventListener('update', function appendOuter() {
sb.removeEventListener('update', appendOuter);
runner.checkEq(sb.buffered.length, 1, 'Source buffer number');
var segmentDuration = sb.buffered.end(0);
sb.timestampOffset = segmentDuration - GAP;
chain.seek(0);
chain.pull(function(buf) {
sb.addEventListener('update', function appendMiddle() {
sb.removeEventListener('update', appendMiddle);
chain.pull(function(buf) {
sb.addEventListener('update', function appendInner() {
runner.checkEq(
sb.buffered.length, 1, 'Source buffer number');
runner.checkApproxEq(sb.buffered.end(0),
segmentDuration * 2 - GAP, 'Range end');
runner.succeed();
});
runner.assert(safeAppend(sb, buf), 'safeAppend failed');
});
});
runner.assert(safeAppend(sb, buf), 'safeAppend failed');
});
});
runner.assert(safeAppend(sb, buf), 'safeAppend failed');
});
});
};
};
/**
* Validate media data with a gap smaller than an media frame size is merged
* into one buffered range.
*/
var createSmallGapTest = function(testId, stream, unused_stream, mandatory) {
var test = createCodecTest(
testId,
stream.codec + util.MakeCapitalName(stream.mediatype) + 'WithSmallGap',
'MSE (' + stream.codec + ')',
mandatory,
[stream]);
test.prototype.title =
'Test if media data with a gap smaller than an media frame size ' +
'will be merged into one buffered range.';
test.prototype.onsourceopen = function() {
var runner = this.runner;
var media = this.video;
var chain = new ResetInit(
new FileSource(stream.src, runner.XHRManager, runner.timeouts));
var sb = this.ms.addSourceBuffer(stream.mimetype);
var unused_sb = this.ms.addSourceBuffer(unused_stream.mimetype);
var GAP = 0.01;
appendInit(media, sb, chain, 0, function() {
chain.pull(function(buf) {
sb.addEventListener('update', function appendOuter() {
sb.removeEventListener('update', appendOuter);
runner.checkEq(sb.buffered.length, 1, 'Source buffer number');
var segmentDuration = sb.buffered.end(0);
sb.timestampOffset = segmentDuration + GAP;
chain.seek(0);
chain.pull(function(buf) {
sb.addEventListener('update', function appendMiddle() {
sb.removeEventListener('update', appendMiddle);
chain.pull(function(buf) {
sb.addEventListener('update', function appendInner() {
runner.checkEq(
sb.buffered.length, 1, 'Source buffer number');
runner.checkApproxEq(sb.buffered.end(0),
segmentDuration * 2 + GAP, 'Range end');
runner.succeed();
});
runner.assert(safeAppend(sb, buf), 'safeAppend failed');
});
});
runner.assert(safeAppend(sb, buf), 'safeAppend failed');
});
});
runner.assert(safeAppend(sb, buf), 'safeAppend failed');
});
});
};
};
/**
* Validate media data with a gap larger than an media frame size will not be
* merged into one buffered range.
*/
var createLargeGapTest = function(testId, stream, unused_stream, mandatory) {
var test = createCodecTest(
testId,
stream.codec + util.MakeCapitalName(stream.mediatype) + 'WithLargeGap',
'MSE (' + stream.codec + ')',
mandatory,
[stream]);
test.prototype.title =
'Test if media data with a gap larger than an media frame size ' +
'will not be merged into one buffered range.';
test.prototype.onsourceopen = function() {
var runner = this.runner;
var media = this.video;
var chain = new ResetInit(
new FileSource(stream.src, runner.XHRManager, runner.timeouts));
var sb = this.ms.addSourceBuffer(stream.mimetype);
var unused_sb = this.ms.addSourceBuffer(unused_stream.mimetype);
var GAP = 0.3;
appendInit(media, sb, chain, 0, function() {
chain.pull(function(buf) {
sb.addEventListener('update', function appendOuter() {
sb.removeEventListener('update', appendOuter);
runner.checkEq(sb.buffered.length, 1, 'Source buffer number');
var segmentDuration = sb.buffered.end(0);
sb.timestampOffset = segmentDuration + GAP;
chain.seek(0);
chain.pull(function(buf) {
sb.addEventListener('update', function appendMiddle() {
sb.removeEventListener('update', appendMiddle);
chain.pull(function(buf) {
sb.addEventListener('update', function appendInner() {
runner.checkEq(
sb.buffered.length, 2, 'Source buffer number');
runner.succeed();
});
runner.assert(safeAppend(sb, buf), 'safeAppend failed');
});
});
runner.assert(safeAppend(sb, buf), 'safeAppend failed');
});
});
runner.assert(safeAppend(sb, buf), 'safeAppend failed');
});
});
};
};
/**
* Validate we can seek during playing. It also tests if the implementation
* properly supports seek operation fired immediately after another seek that
* hasn't been completed.
*/
var createSeekTest = function(testId, videoStream, mandatory) {
var test = createCodecTest(
testId,
'Seek' + videoStream.codec,
'MSE (' + videoStream.codec + ')',
mandatory,
[videoStream]);
test.prototype.title = 'Test if we can seek during playing. It' +
' also tests if the implementation properly supports seek operation' +
' fired immediately after another seek that hasn\'t been completed.';
test.prototype.onsourceopen = function() {
var runner = this.runner;
var media = this.video;
var audioStream = Media.AAC.AudioNormal;
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);
var self = this;
this.ms.duration = 100000000; // Ensure that we can seek to any position.
appendUntil(runner.timeouts, media, videoSb, videoChain, 20, function() {
appendUntil(runner.timeouts, media, audioSb, audioChain, 20, function() {
self.log('Seek to 17s');
callAfterLoadedMetaData(media, function() {
media.currentTime = 17;
media.play();
playThrough(
runner.timeouts, media, 10, 19,
videoSb, videoChain, audioSb, audioChain, function() {
runner.checkGE(media.currentTime, 19, 'currentTime');
self.log('Seek to 28s');
media.currentTime = 53;
media.currentTime = 58;
playThrough(
runner.timeouts, media, 10, 60,
videoSb, videoChain, audioSb, audioChain, function() {
runner.checkGE(media.currentTime, 60, 'currentTime');
self.log('Seek to 7s');
media.currentTime = 0;
media.currentTime = 7;
videoChain.seek(7, videoSb);
audioChain.seek(7, audioSb);
playThrough(runner.timeouts, media, 10, 9,
videoSb, videoChain, audioSb, audioChain, function() {
runner.checkGE(media.currentTime, 9, 'currentTime');
runner.succeed();
});
});
});
});
});
});
};
};
/**
* Seek into and out of a buffered region.
*/
var createBufUnbufSeekTest = function(testId, videoStream, mandatory) {
var test = createCodecTest(
testId,
'BufUnbufSeek' + videoStream.codec,
'MSE (' + videoStream.codec + ')',
mandatory,
[videoStream]);
test.prototype.title = 'Seek into and out of a buffered region.';
test.prototype.onsourceopen = function() {
var runner = this.runner;
var media = this.video;
var audioStream = Media.AAC.AudioNormal;
var videoSb = this.ms.addSourceBuffer(videoStream.mimetype);
var audioSb = this.ms.addSourceBuffer(audioStream.mimetype);
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() {
var N = 30;
function loop(i) {
if (i > N) {
media.currentTime = 1.005;
media.addEventListener('timeupdate', function(e) {
if (!media.paused && media.currentTime > 3)
runner.succeed();
});
return;
}
media.currentTime = (i++ % 2) * 1.0e6 + 1;
runner.timeouts.setTimeout(loop.bind(null, i), 50);
}
media.play();
media.addEventListener('play', loop.bind(null, 0));
});
}, 0, 100000);
xhr2.send();
}, 0, 1000000);
this.ms.duration = 100000000; // Ensure that we can seek to any position.
xhr.send();
};
};
/**
* Ensure we can play properly when there is not enough audio or video data.
* The play should resume once src data is appended.
*/
var createDelayedTest = function(testId, delayed, nonDelayed, mandatory) {
var test = createCodecTest(
testId,
'Delayed' + delayed.codec + util.MakeCapitalName(delayed.mediatype),
'MSE (' + delayed.codec + ')',
mandatory,
[delayed, nonDelayed]);
test.prototype.title = 'Test if we can play properly when there' +
' is not enough ' + delayed.mediatype +
' data. The play should resume once ' +
delayed.mediatype + ' data is appended.';
test.prototype.onsourceopen = function() {
var runner = this.runner;
var media = this.video;
// Chrome allows for 3 seconds of underflow for streams that have audio
// but are video starved.
// See code.google.com/p/chromium/issues/detail?id=423801
var underflowTime = 0.0;
if (delayed.mediatype == 'video') {
underflowTime = 3.0;
}
var chain = new FixedAppendSize(
new ResetInit(
new FileSource(nonDelayed.src, runner.XHRManager, runner.timeouts)
), 16384);
var src = this.ms.addSourceBuffer(nonDelayed.mimetype);
var delayedChain = new FixedAppendSize(
new ResetInit(
new FileSource(delayed.src, runner.XHRManager, runner.timeouts)
), 16384);
var delayedSrc = this.ms.addSourceBuffer(delayed.mimetype);
var self = this;
var ontimeupdate = function(e) {
if (!media.paused) {
var end = delayedSrc.buffered.end(0);
runner.checkLE(media.currentTime, end + 1.0 + underflowTime,
'media.currentTime (' + media.readyState + ')');
}
};
appendUntil(runner.timeouts, media, src, chain, 15, function() {
appendUntil(runner.timeouts, media, delayedSrc, delayedChain, 8,
function() {
var end = delayedSrc.buffered.end(0);
self.log('Start play when there is only ' + end + ' seconds of ' +
test.prototype.name + ' data.');
media.play();
media.addEventListener('timeupdate', ontimeupdate);
waitUntil(runner.timeouts, media, end + 3, function() {
runner.checkLE(media.currentTime, end + 1.0 + underflowTime,
'media.currentTime');
runner.checkGr(media.currentTime, end - 1.0 - underflowTime,
'media.currentTime');
runner.succeed();
});
});
});
};
};
/**
* Test to check if audio-less or audio-only can be playback properly.
*/
var createSingleSourceBufferPlaybackTest = function(testId, stream, mandatory) {
var test = createCodecTest(
testId,
'PlaybackOnly' + stream.codec + util.MakeCapitalName(stream.mediatype),
'MSE (' + stream.codec + ')',
mandatory,
[stream]);
test.prototype.title = 'Test if we can playback a single source buffer.';
test.prototype.onsourceopen = function() {
var runner = this.runner;
var video = this.video;
var videoSb = this.ms.addSourceBuffer(stream.mimetype);
var videoXhr = runner.XHRManager.createRequest(stream.src, function(e) {
videoSb.appendBuffer(this.getResponseData());
video.addEventListener('timeupdate', function(e) {
if (video.currentTime > 5) {
runner.succeed();
}
});
video.play();
}, 0, 300000);
videoXhr.send();
};
};
// Opus Specific tests.
createAppendTest('2.1.1.1', Media.Opus.SantaHigh, Media.VP9.Video1MB);
createAbortTest('2.1.2.1', Media.Opus.SantaHigh, Media.VP9.Video1MB);
createTimestampOffsetTest('2.1.3.1', Media.Opus.CarLow, Media.VP9.Video1MB);
createDurationAfterAppendTest('2.1.4.1', Media.Opus.CarLow, Media.VP9.Video1MB);
createPausedTest('2.1.5.1', Media.Opus.CarLow);
createIncrementalAudioTest('2.1.6.1', Media.Opus.CarMed);
createLimitedAudioTest('2.1.6.2', Media.Opus.CarMed);
createIncrementalLimitedAudioTest('2.1.6.3', Media.Opus.CarMed);
createAppendAudioOffsetTest('2.1.7.1', Media.Opus.CarMed, Media.Opus.CarHigh);
createAppendMultipleInitTest('2.1.8.1', Media.Opus.CarLow, Media.VP9.Video1MB);
createAppendOutOfOrderTest('2.1.9.1', Media.Opus.CarMed, Media.VP9.Video1MB);
createBufferedRangeTest('2.1.10.1', Media.Opus.CarMed, Media.VP9.Video1MB);
createOverlapTest('2.1.11.1', Media.Opus.CarMed, Media.VP9.Video1MB);
createSmallGapTest('2.1.12.1', Media.Opus.CarMed, Media.VP9.Video1MB);
createLargeGapTest('2.1.13.1', Media.Opus.CarMed, Media.VP9.Video1MB);
createDelayedTest('2.1.14.1', Media.Opus.CarMed, Media.VP9.VideoNormal);
createSingleSourceBufferPlaybackTest('2.1.15.1', Media.Opus.SantaHigh)
// AAC Specific tests.
createAppendTest('2.2.1.1', Media.AAC.Audio1MB, Media.H264.Video1MB);
createAbortTest('2.2.2.1', Media.AAC.Audio1MB, Media.H264.Video1MB);
createTimestampOffsetTest('2.2.3.1', Media.AAC.Audio1MB, Media.H264.Video1MB);
createDurationAfterAppendTest(
'2.2.4.1', Media.AAC.Audio1MB, Media.H264.Video1MB);
createPausedTest('2.2.5.1', Media.AAC.Audio1MB);
createIncrementalAudioTest(
'2.2.6.1', Media.AAC.AudioNormal, Media.H264.Video1MB);
createLimitedAudioTest('2.2.6.2', Media.AAC.AudioNormal);
createIncrementalLimitedAudioTest('2.2.6.3', Media.AAC.AudioNormal);
createAppendAudioOffsetTest(
'2.2.7.1', Media.AAC.AudioNormal, Media.AAC.AudioHuge);
createAppendMultipleInitTest(
'2.2.8.1', Media.AAC.Audio1MB, Media.H264.Video1MB);
createAppendOutOfOrderTest(
'2.2.9.1', Media.AAC.AudioNormal, Media.H264.Video1MB);
createBufferedRangeTest('2.2.10.1', Media.AAC.AudioNormal, Media.H264.Video1MB);
createOverlapTest('2.2.11.1', Media.AAC.AudioNormal, Media.H264.Video1MB);
createSmallGapTest('2.2.12.1', Media.AAC.AudioNormal, Media.H264.Video1MB);
createLargeGapTest('2.2.13.1', Media.AAC.AudioNormal, Media.H264.Video1MB);
createDelayedTest('2.2.14.1', Media.AAC.AudioNormal, Media.VP9.VideoNormal);
createSingleSourceBufferPlaybackTest('2.2.15.1', Media.AAC.Audio1MB)
// VP9 Specific tests.
createAppendTest('2.3.1.1', Media.VP9.Video1MB, Media.AAC.Audio1MB);
createAbortTest('2.3.2.1', Media.VP9.Video1MB, Media.AAC.Audio1MB);
createTimestampOffsetTest('2.3.3.1', Media.VP9.Video1MB, Media.AAC.Audio1MB);
createDASHLatencyTest('2.3.4.1', Media.VP9.VideoTiny, Media.AAC.Audio1MB);
createDurationAfterAppendTest(
'2.3.5.1', Media.VP9.Video1MB, Media.AAC.Audio1MB);
createPausedTest('2.3.6.1', Media.VP9.Video1MB);
createVideoDimensionTest(
'2.3.7.1', Media.VP9.VideoNormal, Media.AAC.AudioNormal);
createPlaybackStateTest('2.3.8.1', Media.VP9.VideoNormal);
createPlayPartialSegmentTest('2.3.9.1', Media.VP9.VideoTiny);
createAppendVideoOffsetTest('2.3.10.1',
Media.VP9.VideoNormal, Media.VP9.VideoTiny, Media.AAC.AudioNormal);
createAppendMultipleInitTest(
'2.3.11.1', Media.VP9.Video1MB, Media.AAC.Audio1MB);
createAppendOutOfOrderTest(
'2.3.12.1', Media.VP9.VideoNormal, Media.AAC.AudioNormal);
createBufferedRangeTest(
'2.3.13.1', Media.VP9.VideoNormal, Media.AAC.AudioNormal);
createMediaSourceDurationTest(
'2.3.14.1', Media.VP9.VideoNormal, Media.AAC.AudioNormal);
createOverlapTest('2.3.15.1', Media.VP9.VideoNormal, Media.AAC.AudioNormal);
createSmallGapTest('2.3.16.1', Media.VP9.VideoNormal, Media.AAC.AudioNormal);
createLargeGapTest('2.3.17.1', Media.VP9.VideoNormal, Media.AAC.AudioNormal);
createSeekTest('2.3.18.1', Media.VP9.VideoNormal);
createBufUnbufSeekTest('2.3.19.1', Media.VP9.VideoNormal);
createDelayedTest('2.3.20.1', Media.VP9.VideoNormal, Media.AAC.AudioNormal);
createSingleSourceBufferPlaybackTest('2.3.21.1', Media.VP9.VideoTiny)
// H264 Specific tests.
createAppendTest('2.4.1.1', Media.H264.Video1MB, Media.AAC.Audio1MB);
createAbortTest('2.4.2.1', Media.H264.Video1MB, Media.AAC.Audio1MB);
createTimestampOffsetTest('2.4.3.1', Media.H264.Video1MB, Media.AAC.Audio1MB);
createDASHLatencyTest('2.4.4.1', Media.H264.VideoTiny, Media.AAC.Audio1MB);
createDurationAfterAppendTest(
'2.4.5.1', Media.H264.Video1MB, Media.AAC.Audio1MB);
createPausedTest('2.4.6.1', Media.H264.Video1MB);
createVideoDimensionTest('2.4.7.1', Media.H264.VideoNormal, Media.AAC.Audio1MB);
createPlaybackStateTest('2.4.8.1', Media.H264.VideoNormal);
createPlayPartialSegmentTest('2.4.9.1', Media.H264.VideoTiny);
createAppendVideoOffsetTest('2.4.10.1',
Media.H264.VideoNormal, Media.H264.VideoTiny, Media.AAC.Audio1MB);
createAppendMultipleInitTest(
'2.4.11.1', Media.H264.Video1MB, Media.AAC.Audio1MB);
createAppendOutOfOrderTest(
'2.4.12.1', Media.H264.CarMedium, Media.AAC.Audio1MB);
createBufferedRangeTest('2.4.13.1', Media.H264.VideoNormal, Media.AAC.Audio1MB);
createMediaSourceDurationTest(
'2.4.14.1', Media.H264.VideoNormal, Media.AAC.Audio1MB);
createOverlapTest('2.4.15.1', Media.H264.VideoNormal, Media.AAC.Audio1MB);
createSmallGapTest('2.4.16.1', Media.H264.VideoNormal, Media.AAC.Audio1MB);
createLargeGapTest('2.4.17.1', Media.H264.VideoNormal, Media.AAC.Audio1MB);
createSeekTest('2.4.18.1', Media.H264.VideoNormal);
createBufUnbufSeekTest('2.4.19.1', Media.H264.VideoNormal);
createDelayedTest('2.4.20.1', Media.H264.VideoNormal, Media.AAC.AudioNormal);
createSingleSourceBufferPlaybackTest('2.4.21.1', Media.H264.VideoTiny)
// AV1 Specific tests.
createAppendTest('2.5.1.1', Media.AV1.Video1MB, Media.AAC.Audio1MB,
util.requireAV1());
createAbortTest('2.5.2.1', Media.AV1.Video1MB, Media.AAC.Audio1MB,
util.requireAV1());
createTimestampOffsetTest(
'2.5.3.1', Media.AV1.Bunny144p30fps, Media.AAC.Audio1MB,
util.requireAV1());
createDASHLatencyTest(
'2.5.4.1', Media.AV1.Bunny240p30fps, Media.AAC.Audio1MB,
util.requireAV1());
createDurationAfterAppendTest(
'2.5.5.1', Media.AV1.VideoSmall, Media.AAC.Audio1MB, util.requireAV1());
createPausedTest(
'2.5.6.1', Media.AV1.Bunny144p30fps, util.requireAV1());
createVideoDimensionTest(
'2.5.7.1', Media.AV1.Bunny360p30fps, Media.AAC.Audio1MB,
util.requireAV1());
createPlaybackStateTest(
'2.5.8.1', Media.AV1.Bunny360p30fps, util.requireAV1());
createPlayPartialSegmentTest(
'2.5.9.1', Media.AV1.Bunny240p30fps, util.requireAV1());
createAppendVideoOffsetTest(
'2.5.10.1', Media.AV1.Bunny360p30fps, Media.AV1.Bunny240p30fps,
Media.AAC.Audio1MB, util.requireAV1());
createAppendMultipleInitTest(
'2.5.11.1', Media.AV1.Bunny144p30fps, Media.AAC.Audio1MB,
util.requireAV1());
createAppendOutOfOrderTest(
'2.5.12.1', Media.AV1.Bunny360p30fps, Media.AAC.Audio1MB,
util.requireAV1());
createBufferedRangeTest(
'2.5.13.1', Media.AV1.Bunny360p30fps, Media.AAC.Audio1MB,
util.requireAV1());
createMediaSourceDurationTest(
'2.5.14.1', Media.AV1.Bunny360p30fps, Media.AAC.Audio1MB,
util.requireAV1());
createOverlapTest(
'2.5.15.1', Media.AV1.Bunny360p30fps, Media.AAC.Audio1MB,
util.requireAV1());
createSmallGapTest(
'2.5.16.1', Media.AV1.Bunny360p30fps, Media.AAC.Audio1MB,
util.requireAV1());
createLargeGapTest(
'2.5.17.1', Media.AV1.Bunny360p30fps, Media.AAC.Audio1MB,
util.requireAV1());
createSeekTest('2.5.18.1', Media.AV1.Bunny360p30fps, util.requireAV1());
createBufUnbufSeekTest(
'2.5.19.1', Media.AV1.Bunny360p30fps, util.requireAV1());
createDelayedTest(
'2.5.20.1', Media.AV1.Bunny360p30fps, Media.AAC.AudioNormal,
util.requireAV1());
createSingleSourceBufferPlaybackTest(
'2.5.21.1', Media.AV1.Bunny240p30fps, util.requireAV1());
return {tests: tests, info: info, fields: fields, viewType: 'default'};
};
try {
exports.getTest = MsecodecTest;
} catch (e) {
// do nothing, this function is not supposed to work for browser, but it's for
// Node js to generate json file instead.
}