summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/media-source
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /testing/web-platform/tests/media-source
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'testing/web-platform/tests/media-source')
-rw-r--r--testing/web-platform/tests/media-source/OWNERS3
-rw-r--r--testing/web-platform/tests/media-source/SourceBuffer-abort-readyState.html72
-rw-r--r--testing/web-platform/tests/media-source/SourceBuffer-abort-removed.html52
-rw-r--r--testing/web-platform/tests/media-source/SourceBuffer-abort-updating.html92
-rw-r--r--testing/web-platform/tests/media-source/SourceBuffer-abort.html34
-rw-r--r--testing/web-platform/tests/media-source/URL-createObjectURL-null.html19
-rw-r--r--testing/web-platform/tests/media-source/URL-createObjectURL-revoke.html59
-rw-r--r--testing/web-platform/tests/media-source/URL-createObjectURL.html20
-rwxr-xr-xtesting/web-platform/tests/media-source/import_tests.sh52
-rw-r--r--testing/web-platform/tests/media-source/interfaces.html143
-rw-r--r--testing/web-platform/tests/media-source/manifest.txt55
-rw-r--r--testing/web-platform/tests/media-source/mediasource-activesourcebuffers.html238
-rw-r--r--testing/web-platform/tests/media-source/mediasource-addsourcebuffer-mode.html31
-rw-r--r--testing/web-platform/tests/media-source/mediasource-addsourcebuffer.html154
-rw-r--r--testing/web-platform/tests/media-source/mediasource-append-buffer.html539
-rw-r--r--testing/web-platform/tests/media-source/mediasource-appendbuffer-quota-exceeded.html43
-rw-r--r--testing/web-platform/tests/media-source/mediasource-appendwindow.html176
-rw-r--r--testing/web-platform/tests/media-source/mediasource-attach-stops-delaying-load-event.html49
-rw-r--r--testing/web-platform/tests/media-source/mediasource-avtracks.html124
-rw-r--r--testing/web-platform/tests/media-source/mediasource-buffered.html233
-rw-r--r--testing/web-platform/tests/media-source/mediasource-closed.html140
-rw-r--r--testing/web-platform/tests/media-source/mediasource-config-change-mp4-a-bitrate.html17
-rw-r--r--testing/web-platform/tests/media-source/mediasource-config-change-mp4-av-audio-bitrate.html17
-rw-r--r--testing/web-platform/tests/media-source/mediasource-config-change-mp4-av-framesize.html17
-rw-r--r--testing/web-platform/tests/media-source/mediasource-config-change-mp4-av-video-bitrate.html17
-rw-r--r--testing/web-platform/tests/media-source/mediasource-config-change-mp4-v-bitrate.html17
-rw-r--r--testing/web-platform/tests/media-source/mediasource-config-change-mp4-v-framerate.html17
-rw-r--r--testing/web-platform/tests/media-source/mediasource-config-change-mp4-v-framesize.html17
-rw-r--r--testing/web-platform/tests/media-source/mediasource-config-change-webm-a-bitrate.html17
-rw-r--r--testing/web-platform/tests/media-source/mediasource-config-change-webm-av-audio-bitrate.html17
-rw-r--r--testing/web-platform/tests/media-source/mediasource-config-change-webm-av-framesize.html17
-rw-r--r--testing/web-platform/tests/media-source/mediasource-config-change-webm-av-video-bitrate.html17
-rw-r--r--testing/web-platform/tests/media-source/mediasource-config-change-webm-v-bitrate.html17
-rw-r--r--testing/web-platform/tests/media-source/mediasource-config-change-webm-v-framerate.html17
-rw-r--r--testing/web-platform/tests/media-source/mediasource-config-change-webm-v-framesize.html17
-rw-r--r--testing/web-platform/tests/media-source/mediasource-config-changes.js113
-rw-r--r--testing/web-platform/tests/media-source/mediasource-detach.html50
-rw-r--r--testing/web-platform/tests/media-source/mediasource-duration-boundaryconditions.html63
-rw-r--r--testing/web-platform/tests/media-source/mediasource-duration.html383
-rw-r--r--testing/web-platform/tests/media-source/mediasource-endofstream-invaliderror.html53
-rw-r--r--testing/web-platform/tests/media-source/mediasource-endofstream.html76
-rw-r--r--testing/web-platform/tests/media-source/mediasource-errors.html234
-rw-r--r--testing/web-platform/tests/media-source/mediasource-getvideoplaybackquality.html117
-rw-r--r--testing/web-platform/tests/media-source/mediasource-is-type-supported.html87
-rw-r--r--testing/web-platform/tests/media-source/mediasource-liveseekable.html137
-rw-r--r--testing/web-platform/tests/media-source/mediasource-multiple-attach.html114
-rw-r--r--testing/web-platform/tests/media-source/mediasource-play-then-seek-back.html57
-rw-r--r--testing/web-platform/tests/media-source/mediasource-play.html54
-rw-r--r--testing/web-platform/tests/media-source/mediasource-preload.html72
-rw-r--r--testing/web-platform/tests/media-source/mediasource-redundant-seek.html73
-rw-r--r--testing/web-platform/tests/media-source/mediasource-remove.html324
-rw-r--r--testing/web-platform/tests/media-source/mediasource-removesourcebuffer.html146
-rw-r--r--testing/web-platform/tests/media-source/mediasource-seek-beyond-duration.html105
-rw-r--r--testing/web-platform/tests/media-source/mediasource-seek-during-pending-seek.html189
-rw-r--r--testing/web-platform/tests/media-source/mediasource-seekable.html64
-rw-r--r--testing/web-platform/tests/media-source/mediasource-sequencemode-append-buffer.html140
-rw-r--r--testing/web-platform/tests/media-source/mediasource-sourcebuffer-mode-timestamps.html52
-rw-r--r--testing/web-platform/tests/media-source/mediasource-sourcebuffer-mode.html142
-rw-r--r--testing/web-platform/tests/media-source/mediasource-sourcebuffer-trackdefaults.html78
-rw-r--r--testing/web-platform/tests/media-source/mediasource-sourcebufferlist.html97
-rw-r--r--testing/web-platform/tests/media-source/mediasource-timestamp-offset.html125
-rw-r--r--testing/web-platform/tests/media-source/mediasource-trackdefault.html101
-rw-r--r--testing/web-platform/tests/media-source/mediasource-trackdefaultlist.html60
-rw-r--r--testing/web-platform/tests/media-source/mediasource-util.js407
-rw-r--r--testing/web-platform/tests/media-source/mp4/test-a-128k-44100Hz-1ch-manifest.json4
-rw-r--r--testing/web-platform/tests/media-source/mp4/test-a-128k-44100Hz-1ch.mp4bin0 -> 17408 bytes
-rw-r--r--testing/web-platform/tests/media-source/mp4/test-a-192k-44100Hz-1ch-manifest.json4
-rw-r--r--testing/web-platform/tests/media-source/mp4/test-a-192k-44100Hz-1ch.mp4bin0 -> 17685 bytes
-rw-r--r--testing/web-platform/tests/media-source/mp4/test-av-384k-44100Hz-1ch-320x240-30fps-10kfr-manifest.json4
-rw-r--r--testing/web-platform/tests/media-source/mp4/test-av-384k-44100Hz-1ch-320x240-30fps-10kfr.mp4bin0 -> 81565 bytes
-rw-r--r--testing/web-platform/tests/media-source/mp4/test-av-384k-44100Hz-1ch-640x480-30fps-10kfr-manifest.json4
-rw-r--r--testing/web-platform/tests/media-source/mp4/test-av-384k-44100Hz-1ch-640x480-30fps-10kfr.mp4bin0 -> 69474 bytes
-rw-r--r--testing/web-platform/tests/media-source/mp4/test-av-448k-44100Hz-1ch-640x480-30fps-10kfr-manifest.json4
-rw-r--r--testing/web-platform/tests/media-source/mp4/test-av-448k-44100Hz-1ch-640x480-30fps-10kfr.mp4bin0 -> 69948 bytes
-rw-r--r--testing/web-platform/tests/media-source/mp4/test-av-640k-44100Hz-1ch-640x480-30fps-10kfr-manifest.json4
-rw-r--r--testing/web-platform/tests/media-source/mp4/test-av-640k-44100Hz-1ch-640x480-30fps-10kfr.mp4bin0 -> 95171 bytes
-rw-r--r--testing/web-platform/tests/media-source/mp4/test-v-128k-320x240-24fps-8kfr-manifest.json4
-rw-r--r--testing/web-platform/tests/media-source/mp4/test-v-128k-320x240-24fps-8kfr.mp4bin0 -> 38738 bytes
-rw-r--r--testing/web-platform/tests/media-source/mp4/test-v-128k-320x240-30fps-10kfr-manifest.json4
-rw-r--r--testing/web-platform/tests/media-source/mp4/test-v-128k-320x240-30fps-10kfr.mp4bin0 -> 34009 bytes
-rw-r--r--testing/web-platform/tests/media-source/mp4/test-v-128k-640x480-30fps-10kfr-manifest.json4
-rw-r--r--testing/web-platform/tests/media-source/mp4/test-v-128k-640x480-30fps-10kfr.mp4bin0 -> 27764 bytes
-rw-r--r--testing/web-platform/tests/media-source/mp4/test-v-256k-320x240-30fps-10kfr-manifest.json4
-rw-r--r--testing/web-platform/tests/media-source/mp4/test-v-256k-320x240-30fps-10kfr.mp4bin0 -> 55293 bytes
-rw-r--r--testing/web-platform/tests/media-source/mp4/test.mp4bin0 -> 187227 bytes
-rw-r--r--testing/web-platform/tests/media-source/webm/test-a-128k-44100Hz-1ch-manifest.json4
-rw-r--r--testing/web-platform/tests/media-source/webm/test-a-128k-44100Hz-1ch.webmbin0 -> 9840 bytes
-rw-r--r--testing/web-platform/tests/media-source/webm/test-a-192k-44100Hz-1ch-manifest.json4
-rw-r--r--testing/web-platform/tests/media-source/webm/test-a-192k-44100Hz-1ch.webmbin0 -> 10735 bytes
-rw-r--r--testing/web-platform/tests/media-source/webm/test-av-384k-44100Hz-1ch-320x240-30fps-10kfr-manifest.json4
-rw-r--r--testing/web-platform/tests/media-source/webm/test-av-384k-44100Hz-1ch-320x240-30fps-10kfr.webmbin0 -> 76501 bytes
-rw-r--r--testing/web-platform/tests/media-source/webm/test-av-384k-44100Hz-1ch-640x480-30fps-10kfr-manifest.json4
-rw-r--r--testing/web-platform/tests/media-source/webm/test-av-384k-44100Hz-1ch-640x480-30fps-10kfr.webmbin0 -> 80692 bytes
-rw-r--r--testing/web-platform/tests/media-source/webm/test-av-448k-44100Hz-1ch-640x480-30fps-10kfr-manifest.json4
-rw-r--r--testing/web-platform/tests/media-source/webm/test-av-448k-44100Hz-1ch-640x480-30fps-10kfr.webmbin0 -> 81467 bytes
-rw-r--r--testing/web-platform/tests/media-source/webm/test-av-640k-44100Hz-1ch-640x480-30fps-10kfr-manifest.json4
-rw-r--r--testing/web-platform/tests/media-source/webm/test-av-640k-44100Hz-1ch-640x480-30fps-10kfr.webmbin0 -> 132509 bytes
-rw-r--r--testing/web-platform/tests/media-source/webm/test-v-128k-320x240-24fps-8kfr-manifest.json4
-rw-r--r--testing/web-platform/tests/media-source/webm/test-v-128k-320x240-24fps-8kfr.webmbin0 -> 38195 bytes
-rw-r--r--testing/web-platform/tests/media-source/webm/test-v-128k-320x240-30fps-10kfr-manifest.json4
-rw-r--r--testing/web-platform/tests/media-source/webm/test-v-128k-320x240-30fps-10kfr.webmbin0 -> 39228 bytes
-rw-r--r--testing/web-platform/tests/media-source/webm/test-v-128k-640x480-30fps-10kfr-manifest.json4
-rw-r--r--testing/web-platform/tests/media-source/webm/test-v-128k-640x480-30fps-10kfr.webmbin0 -> 48190 bytes
-rw-r--r--testing/web-platform/tests/media-source/webm/test-v-256k-320x240-30fps-10kfr-manifest.json4
-rw-r--r--testing/web-platform/tests/media-source/webm/test-v-256k-320x240-30fps-10kfr.webmbin0 -> 64318 bytes
-rw-r--r--testing/web-platform/tests/media-source/webm/test-vp8-vorbis-webvtt.webmbin0 -> 143662 bytes
-rw-r--r--testing/web-platform/tests/media-source/webm/test.webmbin0 -> 190970 bytes
107 files changed, 6359 insertions, 0 deletions
diff --git a/testing/web-platform/tests/media-source/OWNERS b/testing/web-platform/tests/media-source/OWNERS
new file mode 100644
index 000000000..5315c9b71
--- /dev/null
+++ b/testing/web-platform/tests/media-source/OWNERS
@@ -0,0 +1,3 @@
+@shishimaru
+@sideshowbarker
+@wolenetz
diff --git a/testing/web-platform/tests/media-source/SourceBuffer-abort-readyState.html b/testing/web-platform/tests/media-source/SourceBuffer-abort-readyState.html
new file mode 100644
index 000000000..159ee60a2
--- /dev/null
+++ b/testing/web-platform/tests/media-source/SourceBuffer-abort-readyState.html
@@ -0,0 +1,72 @@
+<!doctype html>
+<html>
+<head>
+ <meta charset='utf-8'>
+ <title>SourceBuffer#abort() when readyState attribute is not in the "open"</title>
+ <meta name="timeout" content="long">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<div id="log"></div>
+
+<script>
+var contents = {'/media/white.webm': 'video/webm; codecs="vorbis,vp8"',
+ '/media/white.mp4' : 'video/mp4'};
+
+//check the browser supports the MIME used in this test
+function isTypeSupported(mime) {
+ if(!MediaSource.isTypeSupported(mime)) {
+ this.step(function() {
+ assert_unreached("Browser doesn't support the MIME used in this test: " + mime);
+ });
+ this.done();
+ return false;
+ }
+ return true;
+}
+function GET(url, processBody) {
+ var xhr = new XMLHttpRequest();
+ xhr.open('GET', url, true);
+ xhr.responseType = 'arraybuffer';
+ xhr.send();
+ xhr.onload = function(e) {
+ if (xhr.status != 200) {
+ alert("Unexpected status code " + xhr.status + " for " + url);
+ return false;
+ }
+ processBody(new Uint8Array(xhr.response));
+ };
+}
+function mediaTest(file, mime) {
+ async_test(function(t) {
+ if(!isTypeSupported.bind(t)(mime)) {
+ return;
+ }
+ GET(file, function(data) {
+ var mediaSource = new MediaSource();
+ var sourceBuffer = null;
+ mediaSource.addEventListener('sourceopen', function(e) {
+ sourceBuffer = mediaSource.addSourceBuffer(mime);
+ mediaSource.endOfStream();
+ assert_equals(mediaSource.readyState, 'ended',
+ 'mediaSource.readyState is "ended" after endOfStream()');
+ });
+ mediaSource.addEventListener('sourceended', t.step_func_done(function(e) {
+ assert_throws({name: 'InvalidStateError'}, function() {
+ sourceBuffer.abort();
+ });
+ }));
+ var video = document.createElement('video');
+ video.src = window.URL.createObjectURL(mediaSource);
+ });
+ }, 'SourceBuffer#abort() (' + mime + ') : If the readyState attribute ' +
+ 'of the parent media source is not in the "open" state then throw ' +
+ 'an INVALID_STATE_ERR exception and abort these steps.');
+}
+for(var file in contents) {
+ mediaTest(file, contents[file]);
+}
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/media-source/SourceBuffer-abort-removed.html b/testing/web-platform/tests/media-source/SourceBuffer-abort-removed.html
new file mode 100644
index 000000000..00d65c133
--- /dev/null
+++ b/testing/web-platform/tests/media-source/SourceBuffer-abort-removed.html
@@ -0,0 +1,52 @@
+<!doctype html>
+<html>
+<head>
+ <meta charset='utf-8'>
+ <title>SourceBuffer#abort() for already removed buffer from parent media source</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<div id="log"></div>
+
+<script>
+var mimes = ['video/webm; codecs="vorbis,vp8"', 'video/mp4'];
+
+//check the browser supports the MIME used in this test
+function isTypeSupported(mime) {
+ if(!MediaSource.isTypeSupported(mime)) {
+ this.step(function() {
+ assert_unreached("Browser doesn't support the MIME used in this test: " + mime);
+ });
+ this.done();
+ return false;
+ }
+ return true;
+}
+function mediaTest(mime) {
+ async_test(function(t) {
+ if(!isTypeSupported.bind(t)(mime)) {
+ return;
+ }
+ var mediaSource = new MediaSource();
+ mediaSource.addEventListener('sourceopen', t.step_func_done(function(e) {
+ var sourceBuffer = mediaSource.addSourceBuffer(mime);
+ mediaSource.removeSourceBuffer(sourceBuffer);
+ assert_throws({name: 'InvalidStateError'},
+ function() {
+ sourceBuffer.abort();
+ },
+ 'SourceBuffer#abort() after removing the SourceBuffer object');
+ }), false);
+ var video = document.createElement('video');
+ video.src = window.URL.createObjectURL(mediaSource);
+ }, 'SourceBuffer#abort (' + mime + ') : ' +
+ 'if this object has been removed from the sourceBuffers attribute of the parent media source, ' +
+ 'then throw an INVALID_STATE_ERR exception and abort these steps.');
+}
+mimes.forEach(function(mime) {
+ mediaTest(mime);
+});
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/media-source/SourceBuffer-abort-updating.html b/testing/web-platform/tests/media-source/SourceBuffer-abort-updating.html
new file mode 100644
index 000000000..1132d1466
--- /dev/null
+++ b/testing/web-platform/tests/media-source/SourceBuffer-abort-updating.html
@@ -0,0 +1,92 @@
+<!doctype html>
+<html>
+<head>
+ <meta charset='utf-8'>
+ <title>Check SourceBuffer#abort() when the updating attribute is true</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<div id="log"></div>
+
+<script>
+var contents = {'/media/white.webm': 'video/webm; codecs="vorbis,vp8"',
+ '/media/white.mp4' : 'video/mp4'};
+
+function GET(url, processBody) {
+ var xhr = new XMLHttpRequest();
+ xhr.open('GET', url, true);
+ xhr.responseType = 'arraybuffer';
+ xhr.send();
+ xhr.onload = function(e) {
+ if (xhr.status != 200) {
+ alert("Unexpected status code " + xhr.status + " for " + url);
+ return false;
+ }
+ processBody(new Uint8Array(xhr.response));
+ };
+}
+//check the browser supports the MIME used in this test
+function isTypeSupported(mime) {
+ if(!MediaSource.isTypeSupported(mime)) {
+ this.step(function() {
+ assert_unreached("Browser doesn't support the MIME used in this test: " + mime);
+ });
+ this.done();
+ return false;
+ }
+ return true;
+}
+function mediaTest(file, mime) {
+ async_test(function(t) {
+ if(!isTypeSupported.bind(t)(mime)) {
+ return;
+ }
+ GET(file, function(data) {
+ var mediaSource = new MediaSource();
+ var num_updateend = 0;
+ var events = [];
+ mediaSource.addEventListener('sourceopen', t.step_func(function(e) {
+ var sourceBuffer = mediaSource.addSourceBuffer(mime);
+ assert_equals(sourceBuffer.updating, false);
+ sourceBuffer.addEventListener('updatestart', t.step_func(function(e) {
+ events.push('updatestart');
+ //abort when sourceBuffer#updating is true
+ sourceBuffer.abort();
+
+ assert_equals(sourceBuffer.updating, false,
+ 'Check updating value after calling abort.');
+ assert_equals(sourceBuffer.appendWindowStart, 0);
+ assert_equals(sourceBuffer.appendWindowEnd, Number.POSITIVE_INFINITY);
+ }));
+ sourceBuffer.addEventListener('update', t.step_func(function(e) {
+ assert_unreached("Can't touch this");
+ }));
+ sourceBuffer.addEventListener('updateend', function(e) {
+ events.push('updateend');
+ mediaSource.endOfStream();
+ });
+ sourceBuffer.addEventListener('abort', function(e) {
+ events.push('abort');
+ });
+ sourceBuffer.addEventListener('error', t.step_func(function(e) {
+ assert_unreached("Can't touch this");
+ }));
+ sourceBuffer.appendBuffer(data);
+ }));
+ mediaSource.addEventListener('sourceended', t.step_func_done(function(e) {
+ assert_array_equals(events,
+ ['updatestart', 'abort', 'updateend'],
+ 'Check the sequence of fired events.');
+ }));
+ var video = document.createElement('video');
+ video.src = window.URL.createObjectURL(mediaSource);
+ });
+ }, 'SourceBuffer#abort() (' + mime + ') : Check the algorithm when the updating attribute is true.');
+}
+for(var file in contents) {
+ mediaTest(file, contents[file]);
+}
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/media-source/SourceBuffer-abort.html b/testing/web-platform/tests/media-source/SourceBuffer-abort.html
new file mode 100644
index 000000000..7d7c9ff1d
--- /dev/null
+++ b/testing/web-platform/tests/media-source/SourceBuffer-abort.html
@@ -0,0 +1,34 @@
+<!doctype html>
+<html>
+<head>
+ <meta charset='utf-8'>
+ <title>Check the values of appendWindowStart and appendWindowEnd after abort()</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<div id="log"></div>
+
+<script>
+var mimes = ['video/webm; codecs="vorbis,vp8"', 'video/mp4'];
+
+mimes.forEach(function(mime) {
+ async_test(function() {
+ assert_true(MediaSource.isTypeSupported(mime),
+ "Browser doesn't support the MIME used in this test: " + mime);
+
+ var mediaSource = new MediaSource();
+ mediaSource.addEventListener('sourceopen', this.step_func_done(function(e) {
+ var sourceBuffer = mediaSource.addSourceBuffer(mime);
+ sourceBuffer.abort();
+ assert_equals(sourceBuffer.appendWindowStart, 0);
+ assert_equals(sourceBuffer.appendWindowEnd, Number.POSITIVE_INFINITY);
+ }));
+
+ var video = document.createElement('video');
+ video.src = window.URL.createObjectURL(mediaSource);
+ }, 'SourceBuffer#abort() (' + mime + '): Check the values of appendWindowStart and appendWindowEnd.');
+});
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/media-source/URL-createObjectURL-null.html b/testing/web-platform/tests/media-source/URL-createObjectURL-null.html
new file mode 100644
index 000000000..a4177dd84
--- /dev/null
+++ b/testing/web-platform/tests/media-source/URL-createObjectURL-null.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<html>
+<head>
+ <meta charset='utf-8'>
+ <title>URL.createObjectURL(null)</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<script>
+test(function() {
+ assert_throws(new TypeError(), function() {
+ window.URL.createObjectURL(null);
+ });
+}, "URL.createObjectURL(null)");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/media-source/URL-createObjectURL-revoke.html b/testing/web-platform/tests/media-source/URL-createObjectURL-revoke.html
new file mode 100644
index 000000000..c5e18d4fd
--- /dev/null
+++ b/testing/web-platform/tests/media-source/URL-createObjectURL-revoke.html
@@ -0,0 +1,59 @@
+<!doctype html>
+<html>
+<head>
+ <meta charset='utf-8'>
+ <title>Revoking a created URL with URL.revokeObjectURL(url)</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<script>
+async_test(function(t) {
+ var mediaSource = new MediaSource();
+ var url = window.URL.createObjectURL(mediaSource);
+ window.URL.revokeObjectURL(url);
+ mediaSource.addEventListener('sourceopen',
+ t.unreached_func("url should not reference MediaSource."));
+ var video = document.createElement('video');
+ video.src = url;
+ video.addEventListener('error', t.step_func_done(function(e) {
+ assert_equals(e.target.error.code,
+ MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED,
+ 'Expected error code');
+ assert_equals(mediaSource.readyState, 'closed');
+ }));
+}, "Check revoking behavior of URL.revokeObjectURL(url).");
+async_test(function(t) {
+ var mediaSource = new MediaSource();
+ var url = window.URL.createObjectURL(mediaSource);
+ var video = document.createElement('video');
+ var unexpectedErrorHandler = t.unreached_func("Unexpected error.")
+ video.addEventListener('error', unexpectedErrorHandler);
+ video.src = url;
+ window.URL.revokeObjectURL(url);
+ mediaSource.addEventListener('sourceopen', t.step_func_done(function(e) {
+ assert_equals(mediaSource.readyState, 'open');
+ mediaSource.endOfStream();
+ video.removeEventListener('error', unexpectedErrorHandler);
+ }));
+}, "Check referenced MediaSource can open after URL.revokeObjectURL(url).");
+async_test(function(t) {
+ var mediaSource = new MediaSource();
+ var url = window.URL.createObjectURL(mediaSource);
+ setTimeout(function() {
+ mediaSource.addEventListener('sourceopen',
+ t.unreached_func("url should not reference MediaSource."));
+ var video = document.createElement('video');
+ video.src = url;
+ video.addEventListener('error', t.step_func_done(function(e) {
+ assert_equals(e.target.error.code,
+ MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED,
+ 'Expected error code');
+ assert_equals(mediaSource.readyState, 'closed');
+ }));
+ }, 0);
+}, "Check auto-revoking behavior with URL.createObjectURL(MediaSource).");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/media-source/URL-createObjectURL.html b/testing/web-platform/tests/media-source/URL-createObjectURL.html
new file mode 100644
index 000000000..da8280634
--- /dev/null
+++ b/testing/web-platform/tests/media-source/URL-createObjectURL.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<html>
+<head>
+ <meta charset='utf-8'>
+ <title>URL.createObjectURL(mediaSource)</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<script>
+test(function() {
+ var mediaSource = new MediaSource();
+ var url = window.URL.createObjectURL(mediaSource);
+ assert_true(url != null);
+ assert_true(url.match(/^blob:.+/) != null);
+}, "URL.createObjectURL(mediaSource) should return a unique Blob URI.");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/media-source/import_tests.sh b/testing/web-platform/tests/media-source/import_tests.sh
new file mode 100755
index 000000000..a87619c02
--- /dev/null
+++ b/testing/web-platform/tests/media-source/import_tests.sh
@@ -0,0 +1,52 @@
+#!/bin/bash
+
+if [ $# -lt 1 ]
+then
+ echo "Usage: $0 <Blink directory>"
+ exit -1
+fi
+
+BLINK_ROOT=$1
+LAYOUT_TEST_DIR=$BLINK_ROOT/LayoutTests
+HTTP_MEDIA_TEST_DIR=$LAYOUT_TEST_DIR/http/tests/media
+
+if [ ! -d "$BLINK_ROOT" ]
+then
+ echo "$BLINK_ROOT is not a directory or doesn't exist"
+ exit -1
+fi
+
+if [ ! -d "$LAYOUT_TEST_DIR" ]
+then
+ echo "$LAYOUT_TEST_DIR is not a directory or doesn't exist"
+ exit -1
+fi
+
+#rm -rf *.html *.js webm mp4 manifest.txt
+
+cp $HTTP_MEDIA_TEST_DIR/media-source/mediasource-*.html $HTTP_MEDIA_TEST_DIR/media-source/mediasource-*.js .
+cp -r $HTTP_MEDIA_TEST_DIR/resources/media-source/webm .
+cp -r $HTTP_MEDIA_TEST_DIR/resources/media-source/mp4 .
+
+# Remove Blink-specific files
+rm mediasource-gc-after-decode-error-crash.html
+
+sed -i 's/\/w3c\/resources\//\/resources\//g' *.html
+sed -i 's/\/media\/resources\/media-source\///g' *.html
+sed -i 's/\/media\/resources\/media-source\///g' *.js
+sed -i 's/\/media\/resources\/media-source\///g' webm/*
+
+
+for TEST_FILE in `ls *.html`
+do
+ if [ "$TEST_FILE" = "index.html" ]
+ then
+ continue
+ fi
+ echo -e "$TEST_FILE" >> manifest.txt
+done
+
+cp import_tests-template.txt index.html
+
+chmod -R a+r *.html *.js webm mp4 manifest.txt
+chmod a+rx webm mp4
diff --git a/testing/web-platform/tests/media-source/interfaces.html b/testing/web-platform/tests/media-source/interfaces.html
new file mode 100644
index 000000000..f7f6d7c83
--- /dev/null
+++ b/testing/web-platform/tests/media-source/interfaces.html
@@ -0,0 +1,143 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Media Source Extensions IDL tests</title>
+<div id=log></div>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/resources/WebIDLParser.js></script>
+<script src=/resources/idlharness.js></script>
+<script type=text/plain class=untested>
+interface EventTarget {
+ void addEventListener(DOMString type, EventListener? callback, optional boolean capture /* = false */);
+ void removeEventListener(DOMString type, EventListener? callback, optional boolean capture /* = false */);
+ boolean dispatchEvent(Event event);
+};
+interface EventHandler {};
+interface URL {};
+interface HTMLVideoElement {};
+interface AudioTrack {};
+interface AudioTrackList {};
+interface VideoTrack {};
+interface VideoTrackList {};
+interface TextTrack {};
+interface TextTrackList {};
+interface TimeRanges {};
+typedef double DOMHighResTimeStamp;
+</script>
+<script type=text/plain>
+[Constructor]
+interface MediaSource : EventTarget {
+ readonly attribute SourceBufferList sourceBuffers;
+ readonly attribute SourceBufferList activeSourceBuffers;
+ readonly attribute ReadyState readyState;
+ attribute unrestricted double duration;
+ attribute EventHandler onsourceopen;
+ attribute EventHandler onsourceended;
+ attribute EventHandler onsourceclose;
+ SourceBuffer addSourceBuffer(DOMString type);
+ void removeSourceBuffer(SourceBuffer sourceBuffer);
+ void endOfStream(optional EndOfStreamError error);
+ void setLiveSeekableRange(double start, double end);
+ void clearLiveSeekableRange();
+ static boolean isTypeSupported(DOMString type);
+};
+
+interface SourceBuffer : EventTarget {
+ attribute AppendMode mode;
+ readonly attribute boolean updating;
+ readonly attribute TimeRanges buffered;
+ attribute double timestampOffset;
+ readonly attribute AudioTrackList audioTracks;
+ readonly attribute VideoTrackList videoTracks;
+ readonly attribute TextTrackList textTracks;
+ attribute double appendWindowStart;
+ attribute unrestricted double appendWindowEnd;
+ attribute EventHandler onupdatestart;
+ attribute EventHandler onupdate;
+ attribute EventHandler onupdateend;
+ attribute EventHandler onerror;
+ attribute EventHandler onabort;
+ void appendBuffer(BufferSource data);
+ void abort();
+ void remove(double start, unrestricted double end);
+};
+
+interface SourceBufferList : EventTarget {
+ readonly attribute unsigned long length;
+ attribute EventHandler onaddsourcebuffer;
+ attribute EventHandler onremovesourcebuffer;
+ getter SourceBuffer (unsigned long index);
+};
+
+[Exposed=Window,DedicatedWorker,SharedWorker]
+partial interface URL {
+ static DOMString createObjectURL(MediaSource mediaSource);
+};
+
+partial interface AudioTrack {
+ readonly attribute SourceBuffer? sourceBuffer;
+};
+
+partial interface VideoTrack {
+ readonly attribute SourceBuffer? sourceBuffer;
+};
+
+partial interface TextTrack {
+ readonly attribute SourceBuffer? sourceBuffer;
+};
+
+enum EndOfStreamError {
+ "network",
+ "decode"
+};
+
+enum AppendMode {
+ "segments",
+ "sequence"
+};
+
+enum ReadyState {
+ "closed",
+ "open",
+ "ended"
+};
+</script>
+<script>
+"use strict";
+setup({ explicit_done: true });
+var mediaSource;
+var sourceBuffer;
+var video = document.createElement("video");
+var idlCheck = function() {
+ var idlArray = new IdlArray();
+ [].forEach.call(document.querySelectorAll("script[type=text\\/plain]"), function(node) {
+ if (node.className == "untested") {
+ idlArray.add_untested_idls(node.textContent);
+ } else {
+ idlArray.add_idls(node.textContent);
+ }
+ });
+ idlArray.add_objects({
+ MediaSource: ['mediaSource'],
+ SourceBuffer: ['sourceBuffer'],
+ SourceBufferList: ['mediaSource.sourceBuffers']
+ });
+ idlArray.test();
+ done();
+}
+mediaSource = new MediaSource();
+video.src = URL.createObjectURL(mediaSource);
+mediaSource.addEventListener("sourceopen", function () {
+ var defaultType ='video/webm;codecs="vp8,vorbis"';
+ if (video.canPlayType(defaultType)) {
+ sourceBuffer = mediaSource.addSourceBuffer(defaultType);
+ } else {
+ sourceBuffer = mediaSource.addSourceBuffer('video/mp4');
+ }
+ sourceBuffer.addEventListener("updateend", function (e) {
+ mediaSource.endOfStream();
+ idlCheck();
+ });
+ sourceBuffer.appendBuffer(new ArrayBuffer());
+});
+</script>
diff --git a/testing/web-platform/tests/media-source/manifest.txt b/testing/web-platform/tests/media-source/manifest.txt
new file mode 100644
index 000000000..3ca784f17
--- /dev/null
+++ b/testing/web-platform/tests/media-source/manifest.txt
@@ -0,0 +1,55 @@
+interfaces.html
+mediasource-activesourcebuffers.html
+mediasource-addsourcebuffer.html
+mediasource-addsourcebuffer-mode.html
+mediasource-append-buffer.html
+mediasource-appendbuffer-quota-exceeded.html
+mediasource-appendwindow.html
+mediasource-attach-stops-delaying-load-event.html
+mediasource-avtracks.html
+mediasource-buffered.html
+mediasource-closed.html
+mediasource-config-change-mp4-a-bitrate.html
+mediasource-config-change-mp4-av-audio-bitrate.html
+mediasource-config-change-mp4-av-framesize.html
+mediasource-config-change-mp4-av-video-bitrate.html
+mediasource-config-change-mp4-v-bitrate.html
+mediasource-config-change-mp4-v-framerate.html
+mediasource-config-change-mp4-v-framesize.html
+mediasource-config-change-webm-a-bitrate.html
+mediasource-config-change-webm-av-audio-bitrate.html
+mediasource-config-change-webm-av-framesize.html
+mediasource-config-change-webm-av-video-bitrate.html
+mediasource-config-change-webm-v-bitrate.html
+mediasource-config-change-webm-v-framerate.html
+mediasource-config-change-webm-v-framesize.html
+mediasource-detach.html
+mediasource-duration-boundaryconditions.html
+mediasource-duration.html
+mediasource-endofstream.html
+mediasource-endofstream-invaliderror.html
+mediasource-errors.html
+mediasource-is-type-supported.html
+mediasource-liveseekable.html
+mediasource-multiple-attach.html
+mediasource-play.html
+mediasource-play-then-seek-back.html
+mediasource-preload.html
+mediasource-redundant-seek.html
+mediasource-remove.html
+mediasource-removesourcebuffer.html
+mediasource-seekable.html
+mediasource-seek-beyond-duration.html
+mediasource-seek-during-pending-seek.html
+mediasource-sequencemode-append-buffer.html
+mediasource-sourcebufferlist.html
+mediasource-sourcebuffer-mode.html
+mediasource-sourcebuffer-mode-timestamps.html
+mediasource-timestamp-offset.html
+SourceBuffer-abort.html
+SourceBuffer-abort-readyState.html
+SourceBuffer-abort-removed.html
+SourceBuffer-abort-updating.html
+URL-createObjectURL.html
+URL-createObjectURL-null.html
+URL-createObjectURL-revoke.html
diff --git a/testing/web-platform/tests/media-source/mediasource-activesourcebuffers.html b/testing/web-platform/tests/media-source/mediasource-activesourcebuffers.html
new file mode 100644
index 000000000..02ebecc77
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mediasource-activesourcebuffers.html
@@ -0,0 +1,238 @@
+<!DOCTYPE html>
+<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
+<html>
+ <head>
+ <title>Checks MediaSource.activeSourceBuffers and changes to selected/enabled track state</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="mediasource-util.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ // Audio / Video files supported by the user agent under test
+ var subType = MediaSourceUtil.getSubType(MediaSourceUtil.AUDIO_ONLY_TYPE);
+ var manifestFilenameAudio = subType + "/test-a-128k-44100Hz-1ch-manifest.json";
+ var manifestFilenameVideo = subType + "/test-v-128k-320x240-30fps-10kfr-manifest.json";
+ var manifestFilenameAV = subType + "/test-av-384k-44100Hz-1ch-320x240-30fps-10kfr-manifest.json";
+
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ mediaElement.addEventListener("error", test.unreached_func("Unexpected event 'error'"));
+ MediaSourceUtil.fetchManifestAndData(test, manifestFilenameAudio, function (typeAudio, dataAudio)
+ {
+ var sourceBuffer = mediaSource.addSourceBuffer(typeAudio);
+ assert_equals(mediaSource.sourceBuffers.length, 1,
+ "sourceBuffers list contains one SourceBuffer");
+ assert_equals(mediaSource.activeSourceBuffers.length, 0,
+ "activeSourceBuffers is empty to start with");
+
+ test.expectEvent(mediaSource.activeSourceBuffers, "addsourcebuffer");
+ test.expectEvent(mediaElement, "loadedmetadata");
+ sourceBuffer.appendBuffer(dataAudio);
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_equals(mediaSource.activeSourceBuffers.length, 1,
+ "activeSourceBuffers updated when media element is loaded");
+ assert_equals(mediaSource.activeSourceBuffers[0], sourceBuffer,
+ "activeSourceBuffers contains sourceBuffer when media element is loaded");
+ test.done();
+ });
+ });
+ }, "SourceBuffer added to activeSourceBuffers list when its only audio track gets loaded (and thus becomes enabled).");
+
+
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ mediaElement.addEventListener("error", test.unreached_func("Unexpected event 'error'"));
+ MediaSourceUtil.fetchManifestAndData(test, manifestFilenameVideo, function (typeVideo, dataVideo)
+ {
+ var sourceBuffer = mediaSource.addSourceBuffer(typeVideo);
+ assert_equals(mediaSource.sourceBuffers.length, 1,
+ "sourceBuffers list contains one SourceBuffer");
+ assert_equals(mediaSource.activeSourceBuffers.length, 0,
+ "activeSourceBuffers is empty to start with");
+
+ test.expectEvent(mediaSource.activeSourceBuffers, "addsourcebuffer");
+ test.expectEvent(mediaElement, "loadedmetadata");
+ sourceBuffer.appendBuffer(dataVideo);
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_equals(mediaSource.activeSourceBuffers.length, 1,
+ "activeSourceBuffers updated when media element is loaded");
+ assert_equals(mediaSource.activeSourceBuffers[0], sourceBuffer,
+ "activeSourceBuffers contains sourceBuffer when media element is loaded");
+ test.done();
+ });
+ });
+ }, "SourceBuffer added to activeSourceBuffers list when its only video track gets loaded (and thus becomes selected).");
+
+ function mediaSourceActiveSourceBufferOrderTest(addAudioFirst, appendAudioFirst)
+ {
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ mediaElement.addEventListener("error", test.unreached_func("Unexpected event 'error'"));
+ MediaSourceUtil.fetchManifestAndData(test, manifestFilenameAudio, function (typeAudio, dataAudio)
+ {
+ MediaSourceUtil.fetchManifestAndData(test, manifestFilenameVideo, function (typeVideo, dataVideo)
+ {
+ var sourceBufferAudio, sourceBufferVideo, expectedFirstSB, expectedSecondSB;
+ if (addAudioFirst) {
+ expectedFirstSB = sourceBufferAudio = mediaSource.addSourceBuffer(typeAudio);
+ expectedSecondSB = sourceBufferVideo = mediaSource.addSourceBuffer(typeVideo);
+ } else {
+ expectedFirstSB = sourceBufferVideo = mediaSource.addSourceBuffer(typeVideo);
+ expectedSecondSB = sourceBufferAudio = mediaSource.addSourceBuffer(typeAudio);
+ }
+
+ assert_equals(mediaSource.activeSourceBuffers.length, 0,
+ "activeSourceBuffers is empty to start with");
+ assert_equals(mediaSource.sourceBuffers.length, 2,
+ "sourceBuffers list contains both SourceBuffers");
+ assert_equals(mediaSource.sourceBuffers[0], expectedFirstSB,
+ "first SourceBuffer matches expectation");
+ assert_equals(mediaSource.sourceBuffers[1], expectedSecondSB,
+ "second SourceBuffer matches expectation");
+ test.expectEvent(mediaSource.activeSourceBuffers, "addsourcebuffer");
+ test.expectEvent(mediaSource.activeSourceBuffers, "addsourcebuffer");
+ test.expectEvent(mediaElement, "loadedmetadata");
+ if (appendAudioFirst) {
+ sourceBufferAudio.appendBuffer(dataAudio);
+ sourceBufferVideo.appendBuffer(dataVideo);
+ } else {
+ sourceBufferVideo.appendBuffer(dataVideo);
+ sourceBufferAudio.appendBuffer(dataAudio);
+ }
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_equals(mediaSource.activeSourceBuffers.length, 2,
+ "activeSourceBuffers list updated when tracks are loaded");
+ assert_equals(mediaSource.activeSourceBuffers[0], mediaSource.sourceBuffers[0],
+ "first active SourceBuffer matches first SourceBuffer");
+ assert_equals(mediaSource.activeSourceBuffers[1], mediaSource.sourceBuffers[1],
+ "second active SourceBuffer matches second SourceBuffer");
+ test.done();
+ });
+ });
+ });
+ },
+ "Active SourceBuffers must appear in the same order as they appear in the sourceBuffers attribute: " +
+ (addAudioFirst ? "audio is first sourceBuffer" : "video is first sourceBuffer") + ", " +
+ (appendAudioFirst ? "audio media appended first" : "video media appended first"));
+ }
+
+ mediaSourceActiveSourceBufferOrderTest(true, true);
+ mediaSourceActiveSourceBufferOrderTest(true, false);
+ mediaSourceActiveSourceBufferOrderTest(false, true);
+ mediaSourceActiveSourceBufferOrderTest(false, false);
+
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ mediaElement.addEventListener("error", test.unreached_func("Unexpected event 'error'"));
+ MediaSourceUtil.fetchManifestAndData(test, manifestFilenameAudio, function (typeAudio, dataAudio)
+ {
+ MediaSourceUtil.fetchManifestAndData(test, manifestFilenameVideo, function (typeVideo, dataVideo)
+ {
+ var sourceBufferAudio = mediaSource.addSourceBuffer(typeAudio);
+ var sourceBufferVideo = mediaSource.addSourceBuffer(typeVideo);
+
+ test.expectEvent(sourceBufferAudio.audioTracks, "addtrack");
+ test.expectEvent(sourceBufferVideo.videoTracks, "addtrack");
+ sourceBufferAudio.appendBuffer(dataAudio);
+ sourceBufferVideo.appendBuffer(dataVideo);
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_equals(mediaSource.activeSourceBuffers.length, 2,
+ "activeSourceBuffers list updated when tracks are loaded");
+ assert_equals(sourceBufferAudio.audioTracks.length, 1,
+ "audio track list contains loaded audio track");
+ assert_equals(sourceBufferVideo.videoTracks.length, 1,
+ "video track list contains loaded video track");
+
+ test.expectEvent(mediaSource.activeSourceBuffers, "removesourcebuffer");
+ sourceBufferAudio.audioTracks[0].enabled = false;
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_equals(mediaSource.activeSourceBuffers.length, 1,
+ "audio source buffer no longer in the activeSourceBuffers list");
+ assert_equals(mediaSource.activeSourceBuffers[0], sourceBufferVideo,
+ "activeSourceBuffers list only contains the video SourceBuffer");
+
+ test.expectEvent(mediaSource.activeSourceBuffers, "addsourcebuffer");
+ test.expectEvent(mediaSource.activeSourceBuffers, "removesourcebuffer");
+ sourceBufferAudio.audioTracks[0].enabled = true;
+ sourceBufferVideo.videoTracks[0].selected = false;
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_equals(mediaSource.activeSourceBuffers.length, 1,
+ "video source buffer no longer in the activeSourceBuffers list");
+ assert_equals(mediaSource.activeSourceBuffers[0], sourceBufferAudio,
+ "activeSourceBuffers list only contains the audio SourceBuffer");
+ test.done();
+ });
+ });
+ });
+ }, "Active SourceBuffers list reflects changes to selected audio/video tracks associated with separate SourceBuffers.");
+
+
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ mediaElement.addEventListener("error", test.unreached_func("Unexpected event 'error'"));
+ MediaSourceUtil.fetchManifestAndData(test, manifestFilenameAV, function (typeAV, dataAV)
+ {
+ var sourceBuffer = mediaSource.addSourceBuffer(typeAV);
+
+ test.expectEvent(sourceBuffer.audioTracks, "addtrack");
+ test.expectEvent(sourceBuffer.videoTracks, "addtrack");
+ sourceBuffer.appendBuffer(dataAV);
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_equals(mediaSource.activeSourceBuffers.length, 1,
+ "activeSourceBuffers list updated when tracks are loaded");
+ assert_equals(sourceBuffer.audioTracks.length, 1,
+ "audio track list contains loaded audio track");
+ assert_equals(sourceBuffer.videoTracks.length, 1,
+ "video track list contains loaded video track");
+
+ mediaSource.activeSourceBuffers.addEventListener("removesourcebuffer", test.unreached_func(
+ "Unexpected removal from activeSourceBuffers list"));
+ mediaSource.activeSourceBuffers.addEventListener("addsourcebuffer", test.unreached_func(
+ "Unexpected insertion in activeSourceBuffers list"));
+
+ // Changes should only trigger events at the
+ // AudioTrack/VideoTrack instance
+ test.expectEvent(sourceBuffer.audioTracks, "change");
+ sourceBuffer.audioTracks[0].enabled = false;
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_equals(mediaSource.activeSourceBuffers.length, 1,
+ "activeSourceBuffers list unchanged");
+
+ test.expectEvent(sourceBuffer.videoTracks, "change");
+ sourceBuffer.audioTracks[0].enabled = true;
+ sourceBuffer.videoTracks[0].selected = false;
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_equals(mediaSource.activeSourceBuffers.length, 1,
+ "activeSourceBuffers list unchanged");
+ test.done();
+ });
+ });
+ }, "Active SourceBuffers list ignores changes to selected audio/video tracks " +
+ "that do not affect the activation of the SourceBuffer.");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/media-source/mediasource-addsourcebuffer-mode.html b/testing/web-platform/tests/media-source/mediasource-addsourcebuffer-mode.html
new file mode 100644
index 000000000..cf7f57f8e
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mediasource-addsourcebuffer-mode.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Checks MediaSource.addSourceBuffer() sets SourceBuffer.mode appropriately</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="mediasource-util.js"></script>
+<script>
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ // Note all mime types in mediasource-util.js
+ // set the "generate timestamps flag" to false
+ var mime = MediaSourceUtil.VIDEO_ONLY_TYPE;
+ var sourceBuffer = mediaSource.addSourceBuffer(mime);
+ assert_equals(sourceBuffer.mode, "segments");
+ test.done();
+ }, "addSourceBuffer() sets SourceBuffer.mode to 'segments' when the generate timestamps flag is false");
+
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ var mime = 'audio/aac';
+ if (!MediaSource.isTypeSupported(mime)) {
+ mime = 'audio/mpeg';
+ if (!MediaSource.isTypeSupported(mime)) {
+ assert_unreached("Browser does not support the audio/aac and audio/mpeg MIME types used in this test");
+ }
+ }
+ sourceBuffer = mediaSource.addSourceBuffer(mime);
+ assert_equals(sourceBuffer.mode, "sequence");
+ test.done();
+ }, "addSourceBuffer() sets SourceBuffer.mode to 'sequence' when the generate timestamps flag is true");
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/media-source/mediasource-addsourcebuffer.html b/testing/web-platform/tests/media-source/mediasource-addsourcebuffer.html
new file mode 100644
index 000000000..9fc946324
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mediasource-addsourcebuffer.html
@@ -0,0 +1,154 @@
+<!DOCTYPE html>
+<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
+<html>
+ <head>
+ <title>MediaSource.addSourceBuffer() test cases</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="mediasource-util.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ mediaSource.endOfStream();
+ assert_throws("InvalidStateError",
+ function() { mediaSource.addSourceBuffer(MediaSourceUtil.AUDIO_VIDEO_TYPE); },
+ "addSourceBuffer() threw an exception when in 'ended' state.");
+ test.done();
+ }, "Test addSourceBuffer() in 'ended' state.");
+
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ assert_throws(new TypeError(),
+ function() { mediaSource.addSourceBuffer(""); },
+ "addSourceBuffer() threw an exception when passed an empty string.");
+ test.done();
+ }, "Test addSourceBuffer() with empty type");
+
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ assert_throws("NotSupportedError",
+ function() { mediaSource.addSourceBuffer(null); },
+ "addSourceBuffer() threw an exception when passed null.");
+ test.done();
+ }, "Test addSourceBuffer() with null");
+
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ assert_throws("NotSupportedError",
+ function() { mediaSource.addSourceBuffer("invalidType"); },
+ "addSourceBuffer() threw an exception for an unsupported type.");
+ test.done();
+ }, "Test addSourceBuffer() with unsupported type");
+
+
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ var mimetype = 'video/webm;codecs="vp8,vorbis"';
+
+ assert_true(MediaSource.isTypeSupported(mimetype), mimetype + " is supported");
+
+ var sourceBuffer = mediaSource.addSourceBuffer(mimetype);
+ assert_true(sourceBuffer != null, "New SourceBuffer returned");
+ assert_equals(mediaSource.sourceBuffers[0], sourceBuffer, "SourceBuffer is in mediaSource.sourceBuffers");
+ assert_equals(mediaSource.activeSourceBuffers.length, 0, "SourceBuffer is not in mediaSource.activeSourceBuffers");
+ test.done();
+ }, "Test addSourceBuffer() with Vorbis and VP8");
+
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ var videoMimetype = 'video/webm;codecs="vp8"';
+ var audioMimetype = 'audio/webm;codecs="vorbis"';
+
+ assert_true(MediaSource.isTypeSupported(videoMimetype), videoMimetype + " is supported");
+ assert_true(MediaSource.isTypeSupported(audioMimetype), audioMimetype + " is supported");
+
+ var sourceBufferA = mediaSource.addSourceBuffer(videoMimetype);
+ var sourceBufferB = mediaSource.addSourceBuffer(audioMimetype);
+ assert_equals(mediaSource.sourceBuffers[0], sourceBufferA, "sourceBufferA is in mediaSource.sourceBuffers");
+ assert_equals(mediaSource.activeSourceBuffers.length, 0, "SourceBufferA is not in mediaSource.activeSourceBuffers");
+ assert_equals(mediaSource.sourceBuffers[1], sourceBufferB, "sourceBufferB is in mediaSource.sourceBuffers");
+ assert_equals(mediaSource.activeSourceBuffers.length, 0, "SourceBufferB is not in mediaSource.activeSourceBuffers");
+ test.done();
+ }, "Test addSourceBuffer() with Vorbis and VP8 in separate SourceBuffers");
+
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ var mimetype = MediaSourceUtil.VIDEO_ONLY_TYPE;
+
+ assert_true(MediaSource.isTypeSupported(mimetype), mimetype + " is supported");
+
+ var sourceBuffer = mediaSource.addSourceBuffer(mimetype);
+ assert_true(sourceBuffer != null, "New SourceBuffer returned");
+ assert_equals(mediaSource.sourceBuffers[0], sourceBuffer, "SourceBuffer is in mediaSource.sourceBuffers");
+ assert_equals(mediaSource.activeSourceBuffers.length, 0, "SourceBuffer is in mediaSource.activeSourceBuffers");
+ test.done();
+ }, "Test addSourceBuffer() video only");
+
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ var mimetype = MediaSourceUtil.AUDIO_ONLY_TYPE;
+
+ assert_true(MediaSource.isTypeSupported(mimetype), mimetype + " is supported");
+
+ var sourceBuffer = mediaSource.addSourceBuffer(mimetype);
+ assert_true(sourceBuffer != null, "New SourceBuffer returned");
+ assert_equals(mediaSource.sourceBuffers[0], sourceBuffer, "SourceBuffer is in mediaSource.sourceBuffers");
+ assert_equals(mediaSource.activeSourceBuffers.length, 0, "SourceBuffer is in mediaSource.activeSourceBuffers");
+ test.done();
+ }, "Test addSourceBuffer() audio only");
+
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ var mimetype = 'video/mp4;codecs="avc1.4D4001,mp4a.40.2"';
+
+ assert_true(MediaSource.isTypeSupported(mimetype), mimetype + " is supported");
+
+ var sourceBuffer = mediaSource.addSourceBuffer(mimetype);
+ assert_true(sourceBuffer != null, "New SourceBuffer returned");
+ assert_equals(mediaSource.sourceBuffers[0], sourceBuffer, "SourceBuffer is in mediaSource.sourceBuffers");
+ assert_equals(mediaSource.activeSourceBuffers.length, 0, "SourceBuffer is not in mediaSource.activeSourceBuffers");
+ test.done();
+ }, "Test addSourceBuffer() with AAC and H.264");
+
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ var videoMimetype = 'video/mp4;codecs="avc1.4D4001"';
+ var audioMimetype = 'audio/mp4;codecs="mp4a.40.2"';
+
+ assert_true(MediaSource.isTypeSupported(videoMimetype), videoMimetype + " is supported");
+ assert_true(MediaSource.isTypeSupported(audioMimetype), audioMimetype + " is supported");
+
+ var sourceBufferA = mediaSource.addSourceBuffer(videoMimetype);
+ var sourceBufferB = mediaSource.addSourceBuffer(audioMimetype);
+ assert_equals(mediaSource.sourceBuffers[0], sourceBufferA, "sourceBufferA is in mediaSource.sourceBuffers");
+ assert_equals(mediaSource.activeSourceBuffers.length, 0, "SourceBufferA is not in mediaSource.activeSourceBuffers");
+ assert_equals(mediaSource.sourceBuffers[1], sourceBufferB, "sourceBufferB is in mediaSource.sourceBuffers");
+ assert_equals(mediaSource.activeSourceBuffers.length, 0, "SourceBufferB is not in mediaSource.activeSourceBuffers");
+ test.done();
+ }, "Test addSourceBuffer() with AAC and H.264 in separate SourceBuffers");
+
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ var reachedLimit = false;
+
+ // The 20 here is an arbitrary upper limit to make sure the test terminates. This test
+ // assumes that implementations won't support more than 20 SourceBuffers simultaneously.
+ for (var i = 0; i < 20; ++i) {
+ try {
+ mediaSource.addSourceBuffer(MediaSourceUtil.AUDIO_VIDEO_TYPE);
+ } catch(e) {
+ assert_equals(e.name, "QuotaExceededError");
+ reachedLimit = true;
+ break;
+ }
+ }
+ assert_true(reachedLimit, "Reached SourceBuffer limit.");
+ test.done();
+ }, "Test addSourceBuffer() QuotaExceededError.");
+
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/media-source/mediasource-append-buffer.html b/testing/web-platform/tests/media-source/mediasource-append-buffer.html
new file mode 100644
index 000000000..bca3f8951
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mediasource-append-buffer.html
@@ -0,0 +1,539 @@
+<!DOCTYPE html>
+<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
+<html>
+ <head>
+ <title>SourceBuffer.appendBuffer() test cases</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="mediasource-util.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ mediaElement.addEventListener('error', test.unreached_func("Unexpected event 'error'"));
+
+ test.expectEvent(sourceBuffer, "updatestart", "Append started.");
+ test.expectEvent(sourceBuffer, "update", "Append success.");
+ test.expectEvent(sourceBuffer, "updateend", "Append ended.");
+ sourceBuffer.appendBuffer(mediaData);
+
+ assert_true(sourceBuffer.updating, "updating attribute is true");
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_false(sourceBuffer.updating, "updating attribute is false");
+ test.done();
+ });
+ }, "Test SourceBuffer.appendBuffer() event dispatching.");
+
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ mediaElement.addEventListener('error', test.unreached_func("Unexpected event 'error'"));
+
+ test.expectEvent(sourceBuffer, "updatestart", "Append started.");
+ test.expectEvent(sourceBuffer, "update", "Append success.");
+ test.expectEvent(sourceBuffer, "updateend", "Append ended.");
+ sourceBuffer.appendBuffer(mediaData);
+
+ assert_true(sourceBuffer.updating, "updating attribute is true");
+
+ assert_throws("InvalidStateError",
+ function() { sourceBuffer.appendBuffer(mediaData); },
+ "appendBuffer() throws an exception there is a pending append.");
+
+ assert_true(sourceBuffer.updating, "updating attribute is true");
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_false(sourceBuffer.updating, "updating attribute is false");
+ test.done();
+ });
+ }, "Test SourceBuffer.appendBuffer() call during a pending appendBuffer().");
+
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ mediaElement.addEventListener('error', test.unreached_func("Unexpected event 'error'"));
+
+ test.expectEvent(sourceBuffer, "updatestart", "Append started.");
+ test.expectEvent(sourceBuffer, "abort", "Append aborted.");
+ test.expectEvent(sourceBuffer, "updateend", "Append ended.");
+ sourceBuffer.appendBuffer(mediaData);
+
+ assert_true(sourceBuffer.updating, "updating attribute is true");
+
+ sourceBuffer.abort();
+
+ assert_false(sourceBuffer.updating, "updating attribute is false");
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_false(sourceBuffer.updating, "updating attribute is false");
+ test.done();
+ });
+ }, "Test SourceBuffer.abort() call during a pending appendBuffer().");
+
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ mediaElement.addEventListener('error', test.unreached_func("Unexpected event 'error'"));
+
+ test.expectEvent(sourceBuffer, "updatestart", "Append started.");
+ test.expectEvent(sourceBuffer, "update", "Append success.");
+ test.expectEvent(sourceBuffer, "updateend", "Append ended.");
+ sourceBuffer.appendBuffer(mediaData);
+ assert_true(sourceBuffer.updating, "updating attribute is true");
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_false(sourceBuffer.updating, "updating attribute is false");
+
+ test.expectEvent(mediaSource, "sourceended", "MediaSource sourceended event");
+ mediaSource.endOfStream();
+ assert_equals(mediaSource.readyState, "ended", "MediaSource readyState is 'ended'");
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_equals(mediaSource.readyState, "ended", "MediaSource readyState is 'ended'");
+
+ test.expectEvent(mediaSource, "sourceopen", "MediaSource sourceopen event");
+ test.expectEvent(sourceBuffer, "updatestart", "Append started.");
+ test.expectEvent(sourceBuffer, "update", "Append success.");
+ test.expectEvent(sourceBuffer, "updateend", "Append ended.");
+ sourceBuffer.appendBuffer(mediaData);
+
+ assert_equals(mediaSource.readyState, "open", "MediaSource readyState is 'open'");
+ assert_true(sourceBuffer.updating, "updating attribute is true");
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_equals(mediaSource.readyState, "open", "MediaSource readyState is 'open'");
+ assert_false(sourceBuffer.updating, "updating attribute is false");
+ test.done();
+ });
+ }, "Test SourceBuffer.appendBuffer() triggering an 'ended' to 'open' transition.");
+
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ test.expectEvent(sourceBuffer, "updatestart", "Append started.");
+ test.expectEvent(sourceBuffer, "update", "Append success.");
+ test.expectEvent(sourceBuffer, "updateend", "Append ended.");
+ sourceBuffer.appendBuffer(mediaData);
+ assert_true(sourceBuffer.updating, "updating attribute is true");
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_false(sourceBuffer.updating, "updating attribute is false");
+
+ test.expectEvent(mediaSource, "sourceended", "MediaSource sourceended event");
+ mediaSource.endOfStream();
+ assert_equals(mediaSource.readyState, "ended", "MediaSource readyState is 'ended'");
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_equals(mediaSource.readyState, "ended", "MediaSource readyState is 'ended'");
+
+ test.expectEvent(mediaSource, "sourceopen", "MediaSource sourceopen event");
+ test.expectEvent(sourceBuffer, "updatestart", "Append started.");
+ test.expectEvent(sourceBuffer, "update", "Append success.");
+ test.expectEvent(sourceBuffer, "updateend", "Append ended.");
+ sourceBuffer.appendBuffer(new Uint8Array(0));
+
+ assert_equals(mediaSource.readyState, "open", "MediaSource readyState is 'open'");
+ assert_true(sourceBuffer.updating, "updating attribute is true");
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_equals(mediaSource.readyState, "open", "MediaSource readyState is 'open'");
+ assert_false(sourceBuffer.updating, "updating attribute is false");
+ test.done();
+ });
+ }, "Test zero byte SourceBuffer.appendBuffer() call triggering an 'ended' to 'open' transition.");
+
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ mediaElement.addEventListener('error', test.unreached_func("Unexpected event 'error'"));
+
+ test.expectEvent(sourceBuffer, "updatestart", "Append started.");
+ test.expectEvent(sourceBuffer, "abort", "Append aborted.");
+ test.expectEvent(sourceBuffer, "updateend", "Append ended.");
+ sourceBuffer.appendBuffer(mediaData);
+
+ assert_true(sourceBuffer.updating, "updating attribute is true");
+ assert_equals(mediaSource.activeSourceBuffers.length, 0, "activeSourceBuffers.length");
+
+ test.expectEvent(mediaSource.sourceBuffers, "removesourcebuffer", "sourceBuffers");
+ mediaSource.removeSourceBuffer(sourceBuffer);
+
+ assert_false(sourceBuffer.updating, "updating attribute is false");
+
+ assert_throws("InvalidStateError",
+ function() { sourceBuffer.appendBuffer(mediaData); },
+ "appendBuffer() throws an exception because it isn't attached to the mediaSource anymore.");
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_false(sourceBuffer.updating, "updating attribute is false");
+ test.done();
+ });
+ }, "Test MediaSource.removeSourceBuffer() call during a pending appendBuffer().");
+
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ mediaElement.addEventListener('error', test.unreached_func("Unexpected event 'error'"));
+
+ test.expectEvent(sourceBuffer, "updatestart", "Append started.");
+ test.expectEvent(sourceBuffer, "updateend", "Append ended.");
+ sourceBuffer.appendBuffer(mediaData);
+
+ assert_true(sourceBuffer.updating, "updating attribute is true");
+
+ assert_throws("InvalidStateError",
+ function() { mediaSource.duration = 1.0; },
+ "set duration throws an exception when updating attribute is true.");
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_false(sourceBuffer.updating, "updating attribute is false");
+ test.done();
+ });
+ }, "Test set MediaSource.duration during a pending appendBuffer() for one of its SourceBuffers.");
+
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ mediaElement.addEventListener("error", test.unreached_func("Unexpected event 'error'"));
+ mediaSource.addEventListener("sourceended", test.unreached_func("Unexpected event 'sourceended'"));
+
+ test.expectEvent(sourceBuffer, "updatestart", "Append started.");
+ test.expectEvent(sourceBuffer, "updateend", "Append ended.");
+ sourceBuffer.appendBuffer(mediaData);
+
+ assert_true(sourceBuffer.updating, "updating attribute is true");
+
+ assert_throws("InvalidStateError",
+ function() { mediaSource.endOfStream(); },
+ "endOfStream() throws an exception when updating attribute is true.");
+
+ assert_equals(mediaSource.readyState, "open");
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_false(sourceBuffer.updating, "updating attribute is false");
+ assert_equals(mediaSource.readyState, "open");
+ test.done();
+ });
+ }, "Test MediaSource.endOfStream() during a pending appendBuffer() for one of its SourceBuffers.");
+
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ mediaElement.addEventListener('error', test.unreached_func("Unexpected event 'error'"));
+
+ test.expectEvent(sourceBuffer, "updatestart", "Append started.");
+ test.expectEvent(sourceBuffer, "updateend", "Append ended.");
+ sourceBuffer.appendBuffer(mediaData);
+
+ assert_true(sourceBuffer.updating, "updating attribute is true");
+
+ assert_throws("InvalidStateError",
+ function() { sourceBuffer.timestampOffset = 10.0; },
+ "set timestampOffset throws an exception when updating attribute is true.");
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_false(sourceBuffer.updating, "updating attribute is false");
+ test.done();
+ });
+ }, "Test set SourceBuffer.timestampOffset during a pending appendBuffer().");
+
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ var sourceBuffer = mediaSource.addSourceBuffer(MediaSourceUtil.VIDEO_ONLY_TYPE);
+
+ test.expectEvent(sourceBuffer, "updatestart", "Append started.");
+ test.expectEvent(sourceBuffer, "update", "Append success.");
+ test.expectEvent(sourceBuffer, "updateend", "Append ended.");
+ sourceBuffer.appendBuffer(new Uint8Array(0));
+
+ assert_true(sourceBuffer.updating, "updating attribute is true");
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_false(sourceBuffer.updating, "updating attribute is false");
+ test.done();
+ });
+ }, "Test appending an empty ArrayBufferView.");
+
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ test.expectEvent(sourceBuffer, "updatestart", "Append started.");
+ test.expectEvent(sourceBuffer, "update", "Append success.");
+ test.expectEvent(sourceBuffer, "updateend", "Append ended.");
+
+ var arrayBufferView = new Uint8Array(mediaData);
+
+ assert_equals(arrayBufferView.length, mediaData.length, "arrayBufferView.length before transfer.");
+
+ // Send the buffer as in a message so it gets neutered.
+ window.postMessage( "test", "*", [arrayBufferView.buffer]);
+
+ assert_equals(arrayBufferView.length, 0, "arrayBufferView.length after transfer.");
+
+ sourceBuffer.appendBuffer(arrayBufferView);
+
+ assert_true(sourceBuffer.updating, "updating attribute is true");
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_false(sourceBuffer.updating, "updating attribute is false");
+ test.done();
+ });
+ }, "Test appending a neutered ArrayBufferView.");
+
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ var sourceBuffer = mediaSource.addSourceBuffer(MediaSourceUtil.VIDEO_ONLY_TYPE);
+
+ test.expectEvent(sourceBuffer, "updatestart", "Append started.");
+ test.expectEvent(sourceBuffer, "update", "Append success.");
+ test.expectEvent(sourceBuffer, "updateend", "Append ended.");
+ sourceBuffer.appendBuffer(new ArrayBuffer(0));
+
+ assert_true(sourceBuffer.updating, "updating attribute is true");
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_false(sourceBuffer.updating, "updating attribute is false");
+ test.done();
+ });
+ }, "Test appending an empty ArrayBuffer.");
+
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ test.expectEvent(sourceBuffer, "updatestart", "Append started.");
+ test.expectEvent(sourceBuffer, "update", "Append success.");
+ test.expectEvent(sourceBuffer, "updateend", "Append ended.");
+
+ var arrayBuffer = mediaData.buffer.slice(0);
+
+ assert_equals(arrayBuffer.byteLength, mediaData.buffer.byteLength, "arrayBuffer.byteLength before transfer.");
+
+ // Send the buffer as in a message so it gets neutered.
+ window.postMessage( "test", "*", [arrayBuffer]);
+
+ assert_equals(arrayBuffer.byteLength, 0, "arrayBuffer.byteLength after transfer.");
+
+ sourceBuffer.appendBuffer(arrayBuffer);
+
+ assert_true(sourceBuffer.updating, "updating attribute is true");
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_false(sourceBuffer.updating, "updating attribute is false");
+ test.done();
+ });
+ }, "Test appending a neutered ArrayBuffer.");
+
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ var initSegment = MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.init);
+ var halfIndex = (initSegment.length + 1) / 2;
+ var partialInitSegment = initSegment.subarray(0, halfIndex);
+ var remainingInitSegment = initSegment.subarray(halfIndex);
+ var mediaSegment = MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.media[0]);
+
+ test.expectEvent(sourceBuffer, "updateend", "partialInitSegment append ended.");
+ sourceBuffer.appendBuffer(partialInitSegment);
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_equals(mediaElement.readyState, mediaElement.HAVE_NOTHING);
+ assert_equals(mediaSource.duration, Number.NaN);
+ test.expectEvent(sourceBuffer, "updateend", "remainingInitSegment append ended.");
+ test.expectEvent(mediaElement, "loadedmetadata", "loadedmetadata event received.");
+ sourceBuffer.appendBuffer(remainingInitSegment);
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_equals(mediaElement.readyState, mediaElement.HAVE_METADATA);
+ assert_equals(mediaSource.duration, segmentInfo.duration);
+ test.expectEvent(sourceBuffer, "updateend", "mediaSegment append ended.");
+ test.expectEvent(mediaElement, "loadeddata", "loadeddata fired.");
+ sourceBuffer.appendBuffer(mediaSegment);
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_greater_than_equal(mediaElement.readyState, mediaElement.HAVE_CURRENT_DATA);
+ assert_equals(sourceBuffer.updating, false);
+ assert_equals(mediaSource.readyState, "open");
+ test.done();
+ });
+ }, "Test appendBuffer with partial init segments.");
+
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ var initSegment = MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.init);
+ var mediaSegment = MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.media[0]);
+ var halfIndex = (mediaSegment.length + 1) / 2;
+ var partialMediaSegment = mediaSegment.subarray(0, halfIndex);
+ var remainingMediaSegment = mediaSegment.subarray(halfIndex);
+
+ test.expectEvent(sourceBuffer, "updateend", "InitSegment append ended.");
+ test.expectEvent(mediaElement, "loadedmetadata", "loadedmetadata done.");
+ sourceBuffer.appendBuffer(initSegment);
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_equals(mediaElement.readyState, mediaElement.HAVE_METADATA);
+ assert_equals(mediaSource.duration, segmentInfo.duration);
+ test.expectEvent(sourceBuffer, "updateend", "partial media segment append ended.");
+ sourceBuffer.appendBuffer(partialMediaSegment);
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ test.expectEvent(sourceBuffer, "updateend", "mediaSegment append ended.");
+ test.expectEvent(mediaElement, "loadeddata", "loadeddata fired.");
+ sourceBuffer.appendBuffer(remainingMediaSegment);
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_greater_than_equal(mediaElement.readyState, mediaElement.HAVE_CURRENT_DATA);
+ assert_equals(mediaSource.readyState, "open");
+ assert_equals(sourceBuffer.updating, false);
+ test.done();
+ });
+ }, "Test appendBuffer with partial media segments.");
+
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ var initSegment = MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.init);
+ var partialInitSegment = initSegment.subarray(0, initSegment.length / 2);
+ var mediaSegment = MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.media[0]);
+
+ test.expectEvent(sourceBuffer, "updateend", "partialInitSegment append ended.");
+ sourceBuffer.appendBuffer(partialInitSegment);
+
+ test.waitForExpectedEvents(function()
+ {
+ // Call abort to reset the parser.
+ sourceBuffer.abort();
+
+ // Append the full intiialization segment.
+ test.expectEvent(sourceBuffer, "updateend", "initSegment append ended.");
+ sourceBuffer.appendBuffer(initSegment);
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ test.expectEvent(sourceBuffer, "updateend", "mediaSegment append ended.");
+ test.expectEvent(mediaElement, "loadeddata", "loadeddata fired.");
+ sourceBuffer.appendBuffer(mediaSegment);
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ test.done();
+ });
+ }, "Test abort in the middle of an initialization segment.");
+
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ test.expectEvent(mediaSource.sourceBuffers, "removesourcebuffer", "SourceBuffer removed.");
+ mediaSource.removeSourceBuffer(sourceBuffer);
+ test.waitForExpectedEvents(function()
+ {
+ assert_throws("InvalidStateError",
+ function() { sourceBuffer.abort(); },
+ "sourceBuffer.abort() throws an exception for InvalidStateError.");
+
+ test.done();
+ });
+ }, "Test abort after removing sourcebuffer.");
+
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ var initSegment = MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.init);
+ var mediaSegment = MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.media[0]);
+
+ test.expectEvent(sourceBuffer, "updateend", "initSegment append ended.");
+ sourceBuffer.appendBuffer(initSegment);
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_equals(mediaSource.readyState, "open", "readyState is open after init segment appended.");
+ test.expectEvent(sourceBuffer, "updateend", "mediaSegment append ended.");
+ sourceBuffer.appendBuffer(mediaSegment);
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_equals(sourceBuffer.buffered.length, 1, "sourceBuffer has a buffered range");
+ assert_equals(mediaSource.readyState, "open", "readyState is open after media segment appended.");
+ test.expectEvent(mediaSource, "sourceended", "source ended");
+ mediaSource.endOfStream();
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_equals(mediaSource.readyState, "ended", "readyState is ended.");
+ assert_throws("InvalidStateError",
+ function() { sourceBuffer.abort(); },
+ "sourceBuffer.abort() throws an exception for InvalidStateError.");
+ test.done();
+ });
+
+ }, "Test abort after readyState is ended following init segment and media segment.");
+
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ test.expectEvent(sourceBuffer, "updatestart", "Append started.");
+ test.expectEvent(sourceBuffer, "updateend", "Append ended.");
+ sourceBuffer.appendWindowStart = 1;
+ sourceBuffer.appendWindowEnd = 100;
+ sourceBuffer.appendBuffer(mediaData);
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_false(sourceBuffer.updating, "updating attribute is false");
+ sourceBuffer.abort();
+ assert_equals(sourceBuffer.appendWindowStart, 0, "appendWindowStart is reset to 0");
+ assert_equals(sourceBuffer.appendWindowEnd, Number.POSITIVE_INFINITY,
+ "appendWindowEnd is reset to +INFINITY");
+ test.done();
+ });
+ }, "Test abort after appendBuffer update ends.");
+
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ var sourceBuffer = mediaSource.addSourceBuffer(MediaSourceUtil.VIDEO_ONLY_TYPE);
+
+ test.expectEvent(sourceBuffer, "updatestart", "Append started.");
+ test.expectEvent(sourceBuffer, "update", "Append success.");
+ test.expectEvent(sourceBuffer, "updateend", "Append ended.");
+
+ assert_throws( { name: "TypeError"} ,
+ function() { sourceBuffer.appendBuffer(null); },
+ "appendBuffer(null) throws an exception.");
+ test.done();
+ }, "Test appending null.");
+
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ mediaSource.removeSourceBuffer(sourceBuffer);
+
+ assert_throws( { name: "InvalidStateError"} ,
+ function() { sourceBuffer.appendBuffer(mediaData); },
+ "appendBuffer() throws an exception when called after removeSourceBuffer().");
+ test.done();
+ }, "Test appending after removeSourceBuffer().");
+
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/media-source/mediasource-appendbuffer-quota-exceeded.html b/testing/web-platform/tests/media-source/mediasource-appendbuffer-quota-exceeded.html
new file mode 100644
index 000000000..25eb941db
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mediasource-appendbuffer-quota-exceeded.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="mediasource-util.js"></script>
+<script>
+ var subType = MediaSourceUtil.getSubType(MediaSourceUtil.AUDIO_ONLY_TYPE);
+ var manifestFilenameAudio = subType + "/test-a-128k-44100Hz-1ch-manifest.json";
+
+ // Fill up a given SourceBuffer by appending data repeatedly via doAppendDataFunc until
+ // an exception is thrown. The thrown exception is passed to onCaughtExceptionCallback.
+ function fillUpSourceBuffer(test, sourceBuffer, doAppendDataFunc, onCaughtExceptionCallback) {
+ // We are appending data repeatedly in sequence mode, there should be no gaps.
+ assert_false(sourceBuffer.buffered.length > 1, "unexpected gap in buffered ranges.");
+ try {
+ doAppendDataFunc();
+ } catch(ex) {
+ onCaughtExceptionCallback(ex);
+ }
+ test.expectEvent(sourceBuffer, 'updateend', 'append ended.');
+ test.waitForExpectedEvents(function() { fillUpSourceBuffer(test, sourceBuffer, doAppendDataFunc, onCaughtExceptionCallback); });
+ }
+
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ mediaElement.addEventListener("error", test.unreached_func("Unexpected event 'error'"));
+ MediaSourceUtil.fetchManifestAndData(test, manifestFilenameAudio, function(typeAudio, dataAudio)
+ {
+ var sourceBuffer = mediaSource.addSourceBuffer(typeAudio);
+ sourceBuffer.mode = 'sequence';
+ fillUpSourceBuffer(test, sourceBuffer,
+ function () { // doAppendDataFunc
+ sourceBuffer.appendBuffer(dataAudio);
+ },
+ function (ex) { // onCaughtExceptionCallback
+ assert_equals(ex.name, 'QuotaExceededError');
+ test.done();
+ });
+ });
+ }, 'Appending data repeatedly should fill up the buffer and throw a QuotaExceededError when buffer is full.');
+</script>
diff --git a/testing/web-platform/tests/media-source/mediasource-appendwindow.html b/testing/web-platform/tests/media-source/mediasource-appendwindow.html
new file mode 100644
index 000000000..272031a6e
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mediasource-appendwindow.html
@@ -0,0 +1,176 @@
+<!DOCTYPE html>
+<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
+<html>
+ <head>
+ <title>SourceBuffer.appendWindowStart and SourceBuffer.appendWindowEnd test cases.</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="mediasource-util.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ var sourceBuffer = mediaSource.addSourceBuffer(MediaSourceUtil.AUDIO_VIDEO_TYPE);
+ assert_true(sourceBuffer != null, "New SourceBuffer returned");
+
+ sourceBuffer.appendWindowStart = 100.0;
+ sourceBuffer.appendWindowEnd = 500.0;
+ assert_equals(sourceBuffer.appendWindowStart, 100.0, "appendWindowStart is correctly set'");
+ assert_equals(sourceBuffer.appendWindowEnd, 500.0, "appendWindowEnd is correctly set'");
+
+ sourceBuffer.appendWindowStart = 200.0;
+ sourceBuffer.appendWindowEnd = 400.0;
+ assert_equals(sourceBuffer.appendWindowStart, 200.0, "appendWindowStart is correctly reset'");
+ assert_equals(sourceBuffer.appendWindowEnd, 400.0, "appendWindowEnd is correctly reset'");
+ test.done();
+ }, "Test correctly reset appendWindowStart and appendWindowEnd values");
+
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ var sourceBuffer = mediaSource.addSourceBuffer(MediaSourceUtil.AUDIO_VIDEO_TYPE);
+ assert_true(sourceBuffer != null, "New SourceBuffer returned");
+ sourceBuffer.appendWindowEnd = 500.0;
+
+ assert_throws(new TypeError(),
+ function() { sourceBuffer.appendWindowStart = Number.NEGATIVE_INFINITY; },
+ "set appendWindowStart throws an exception for Number.NEGATIVE_INFINITY.");
+
+ assert_throws(new TypeError(),
+ function() { sourceBuffer.appendWindowStart = Number.POSITIVE_INFINITY; },
+ "set appendWindowStart throws an exception for Number.POSITIVE_INFINITY.");
+
+ assert_throws(new TypeError(),
+ function() { sourceBuffer.appendWindowStart = Number.NaN; },
+ "set appendWindowStart throws an exception for Number.NaN.");
+
+ assert_throws(new TypeError(),
+ function() { sourceBuffer.appendWindowStart = 600.0; },
+ "set appendWindowStart throws an exception when greater than appendWindowEnd.");
+
+ assert_throws(new TypeError(),
+ function() { sourceBuffer.appendWindowStart = sourceBuffer.appendWindowEnd; },
+ "set appendWindowStart throws an exception when equal to appendWindowEnd.");
+
+ assert_throws(new TypeError(),
+ function() { sourceBuffer.appendWindowEnd = sourceBuffer.appendWindowStart; },
+ "set appendWindowEnd throws an exception when equal to appendWindowStart.");
+
+ assert_throws(new TypeError(),
+ function() { sourceBuffer.appendWindowEnd = sourceBuffer.appendWindowStart - 1; },
+ "set appendWindowEnd throws an exception if less than appendWindowStart.");
+
+ assert_throws(new TypeError(),
+ function() { sourceBuffer.appendWindowStart = -100.0; },
+ "set appendWindowStart throws an exception when less than 0.");
+
+ assert_throws(new TypeError(),
+ function() { sourceBuffer.appendWindowEnd = -100.0; },
+ "set appendWindowEnd throws an exception when less than 0.");
+
+ assert_throws(new TypeError(),
+ function() { sourceBuffer.appendWindowEnd = Number.NaN; },
+ "set appendWindowEnd throws an exception if NaN.");
+
+ assert_throws(new TypeError(),
+ function() { sourceBuffer.appendWindowEnd = undefined; },
+ "set appendWindowEnd throws an exception if undefined.");
+
+ assert_throws({name: "TypeError"},
+ function() { sourceBuffer.appendWindowStart = undefined; },
+ "set appendWindowStart throws an exception if undefined.");
+
+ test.done();
+ }, "Test set wrong values to appendWindowStart and appendWindowEnd.");
+
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ var sourceBuffer = mediaSource.addSourceBuffer(MediaSourceUtil.AUDIO_VIDEO_TYPE);
+ assert_true(sourceBuffer != null, "New SourceBuffer returned");
+
+ sourceBuffer.appendWindowStart = "";
+ assert_true(sourceBuffer.appendWindowStart == 0, "appendWindowStart is 0");
+
+ sourceBuffer.appendWindowStart = "10";
+ assert_true(sourceBuffer.appendWindowStart == 10, "appendWindowStart is 10");
+
+ sourceBuffer.appendWindowStart = null;
+ assert_true(sourceBuffer.appendWindowStart == 0, "appendWindowStart is 0");
+
+ sourceBuffer.appendWindowStart = true;
+ assert_true(sourceBuffer.appendWindowStart == 1, "appendWindowStart is 1");
+
+ sourceBuffer.appendWindowStart = false;
+ assert_true(sourceBuffer.appendWindowStart == 0, "appendWindowStart is 0");
+
+ sourceBuffer.appendWindowEnd = "100";
+ assert_true(sourceBuffer.appendWindowEnd == 100, "appendWindowEnd is 100");
+
+ test.done();
+
+ }, "Test set correct values to appendWindowStart and appendWindowEnd.");
+
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ mediaSource.removeSourceBuffer(sourceBuffer);
+ assert_throws("InvalidStateError",
+ function() { sourceBuffer.appendWindowStart = 100.0; },
+ "set appendWindowStart throws an exception when mediasource object is not associated with a buffer.");
+
+ assert_throws("InvalidStateError",
+ function() { sourceBuffer.appendWindowEnd = 500.0; },
+ "set appendWindowEnd throws an exception when mediasource object is not associated with a buffer.");
+ test.done();
+
+ }, "Test appendwindow throw error when mediasource object is not associated with a sourebuffer.");
+
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ test.expectEvent(sourceBuffer, "updateend", "sourceBuffer");
+ sourceBuffer.appendBuffer(mediaData);
+ assert_true(sourceBuffer.updating, "updating attribute is true");
+
+ assert_throws("InvalidStateError",
+ function() { sourceBuffer.appendWindowStart = 100.0; },
+ "set appendWindowStart throws an exception when there is a pending append.");
+
+ assert_throws("InvalidStateError",
+ function() { sourceBuffer.appendWindowEnd = 500.0; },
+ "set appendWindowEnd throws an exception when there is a pending append.");
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_false(sourceBuffer.updating, "updating attribute is false");
+ test.done();
+ });
+ }, "Test set appendWindowStart and appendWindowEnd when source buffer updating.");
+
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ test.expectEvent(sourceBuffer, "updateend", "sourceBuffer");
+ sourceBuffer.appendBuffer(mediaData);
+ assert_true(sourceBuffer.updating, "updating attribute is true");
+
+ sourceBuffer.abort();
+ assert_equals(sourceBuffer.appendWindowStart, 0, "appendWindowStart is 0 after an abort'");
+ assert_equals(sourceBuffer.appendWindowEnd, Number.POSITIVE_INFINITY,
+ "appendWindowStart is POSITIVE_INFINITY after an abort");
+ test.waitForExpectedEvents(function()
+ {
+ assert_false(sourceBuffer.updating, "updating attribute is false");
+ test.done();
+ });
+ }, "Test appendWindowStart and appendWindowEnd value after a sourceBuffer.abort().");
+
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ assert_equals(sourceBuffer.appendWindowStart, 0, "appendWindowStart is 0 initially");
+ assert_equals(sourceBuffer.appendWindowEnd, Number.POSITIVE_INFINITY,
+ "appendWindowStart is POSITIVE_INFINITY initially");
+ test.done();
+ }, "Test read appendWindowStart and appendWindowEnd initial values.");
+
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/media-source/mediasource-attach-stops-delaying-load-event.html b/testing/web-platform/tests/media-source/mediasource-attach-stops-delaying-load-event.html
new file mode 100644
index 000000000..6b5d5b0ad
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mediasource-attach-stops-delaying-load-event.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
+<title>Tests that MediaSource attachment stops delaying the load event.</title>
+<link rel="author" title="Matthew Wolenetz" href="mailto:wolenetz@chromium.org"/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+async_test(function(test)
+{
+ var receivedLoadEvent = false;
+ var receivedSourceOpenEvent = false;
+
+ window.addEventListener("load", test.step_func(function() {
+ assert_false(receivedLoadEvent, "window should not receive multiple load events");
+ receivedLoadEvent = true;
+ assert_equals(document.readyState, "complete", "document should be complete");
+ if (receivedLoadEvent && receivedSourceOpenEvent) {
+ test.done();
+ }
+ }));
+
+ assert_equals(document.readyState, "loading", "document should not be complete yet");
+ var video = document.createElement("video");
+ var mediaSource = new MediaSource();
+
+ // |video| should stop delaying the load event long and complete the MediaSource attachment
+ // before either a "progress", "stalled" or "suspend" event are enqueued.
+ video.addEventListener("suspend", test.unreached_func("unexpected 'suspend' event"));
+ video.addEventListener("stalled", test.unreached_func("unexpected 'stalled' event"));
+ video.addEventListener("progress", test.unreached_func("unexpected 'progress' event"));
+
+ // No error is expected.
+ video.addEventListener("error", test.unreached_func("unexpected 'error' event"));
+
+ mediaSource.addEventListener("sourceopen", test.step_func(function() {
+ assert_false(receivedSourceOpenEvent, "only one sourceopen event should occur in this test");
+ receivedSourceOpenEvent = true;
+ assert_equals(video.networkState, video.NETWORK_LOADING);
+ assert_equals(video.readyState, video.HAVE_NOTHING);
+ if (receivedLoadEvent && receivedSourceOpenEvent) {
+ test.done();
+ }
+ }));
+
+ var mediaSourceURL = URL.createObjectURL(mediaSource);
+ video.src = mediaSourceURL;
+ test.add_cleanup(function() { URL.revokeObjectURL(mediaSourceURL); });
+}, "MediaSource attachment should immediately stop delaying the load event");
+</script>
diff --git a/testing/web-platform/tests/media-source/mediasource-avtracks.html b/testing/web-platform/tests/media-source/mediasource-avtracks.html
new file mode 100644
index 000000000..26ae5bcfe
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mediasource-avtracks.html
@@ -0,0 +1,124 @@
+<!DOCTYPE html>
+<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="mediasource-util.js"></script>
+<script>
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ var initSegment = MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.init);
+ sourceBuffer.appendBuffer(initSegment);
+ test.expectEvent(sourceBuffer.audioTracks, "addtrack", "sourceBuffer.audioTracks addtrack event");
+ test.expectEvent(sourceBuffer.videoTracks, "addtrack", "sourceBuffer.videoTracks addtrack event");
+ test.expectEvent(mediaElement.audioTracks, "addtrack", "mediaElement.audioTracks addtrack event");
+ test.expectEvent(mediaElement.videoTracks, "addtrack", "mediaElement.videoTracks addtrack event");
+ test.expectEvent(mediaElement, "loadedmetadata", "loadedmetadata done.");
+ test.expectEvent(sourceBuffer, "updateend", "initSegment append ended.");
+ test.waitForExpectedEvents(function()
+ {
+ assert_equals(sourceBuffer.videoTracks.length, 1, "videoTracks.length");
+ assert_equals(sourceBuffer.videoTracks[0].kind, "main", "videoTrack.kind");
+ assert_equals(sourceBuffer.videoTracks[0].label, "", "videoTrack.label");
+ assert_equals(sourceBuffer.videoTracks[0].language, "eng", "videoTrack.language");
+ assert_equals(sourceBuffer.videoTracks[0].sourceBuffer, sourceBuffer, "videoTrack.sourceBuffer");
+ // The first video track is selected by default.
+ assert_true(sourceBuffer.videoTracks[0].selected, "sourceBuffer.videoTracks[0].selected");
+
+ assert_equals(sourceBuffer.audioTracks.length, 1, "audioTracks.length");
+ assert_equals(sourceBuffer.audioTracks[0].kind, "main", "audioTrack.kind");
+ assert_equals(sourceBuffer.audioTracks[0].label, "", "audioTrack.label");
+ assert_equals(sourceBuffer.audioTracks[0].language, "eng", "audioTrack.language");
+ assert_equals(sourceBuffer.audioTracks[0].sourceBuffer, sourceBuffer, "audioTrack.sourceBuffer");
+ // The first audio track is enabled by default.
+ assert_true(sourceBuffer.audioTracks[0].enabled, "sourceBuffer.audioTracks[0].enabled");
+
+ assert_not_equals(sourceBuffer.audioTracks[0].id, sourceBuffer.videoTracks[0].id, "track ids must be unique");
+
+ assert_equals(mediaElement.videoTracks.length, 1, "videoTracks.length");
+ assert_equals(mediaElement.videoTracks[0], sourceBuffer.videoTracks[0], "mediaElement.videoTrack == sourceBuffer.videoTrack");
+
+ assert_equals(mediaElement.audioTracks.length, 1, "audioTracks.length");
+ assert_equals(mediaElement.audioTracks[0], sourceBuffer.audioTracks[0], "mediaElement.audioTrack == sourceBuffer.audioTrack");
+
+ test.done();
+ });
+ }, "Check that media tracks and their properties are populated properly");
+
+ function verifyTrackRemoval(test, mediaElement, mediaSource, sourceBuffer, trackRemovalAction, successCallback) {
+ assert_equals(sourceBuffer.audioTracks.length, 1, "audioTracks.length");
+ assert_true(sourceBuffer.audioTracks[0].enabled, "sourceBuffer.audioTracks[0].enabled");
+ assert_equals(sourceBuffer.videoTracks.length, 1, "videoTracks.length");
+ assert_true(sourceBuffer.videoTracks[0].selected, "sourceBuffer.videoTracks[0].selected");
+
+ var audioTrack = sourceBuffer.audioTracks[0];
+ var videoTrack = sourceBuffer.videoTracks[0];
+
+ // Verify removetrack events.
+ test.expectEvent(sourceBuffer.audioTracks, "removetrack", "sourceBuffer.audioTracks removetrack event");
+ test.expectEvent(sourceBuffer.videoTracks, "removetrack", "sourceBuffer.videoTracks removetrack event");
+ test.expectEvent(mediaElement.audioTracks, "removetrack", "mediaElement.audioTracks removetrack event");
+ test.expectEvent(mediaElement.videoTracks, "removetrack", "mediaElement.videoTracks removetrack event");
+
+ // Removing enabled audio track and selected video track should fire "change" events on mediaElement track lists.
+ test.expectEvent(mediaElement.audioTracks, "change", "mediaElement.audioTracks changed.");
+ test.expectEvent(mediaElement.videoTracks, "change", "mediaElement.videoTracks changed.");
+
+ trackRemovalAction();
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_equals(mediaSource.sourceBuffers.length, 0, "mediaSource.sourceBuffers.length");
+ assert_equals(mediaElement.videoTracks.length, 0, "videoTracks.length");
+ assert_equals(mediaElement.audioTracks.length, 0, "audioTracks.length");
+ assert_equals(sourceBuffer.videoTracks.length, 0, "videoTracks.length");
+ assert_equals(sourceBuffer.audioTracks.length, 0, "audioTracks.length");
+ // Since audio and video tracks have been removed, their .sourceBuffer property should be null now.
+ assert_equals(audioTrack.sourceBuffer, null, "audioTrack.sourceBuffer");
+ assert_equals(videoTrack.sourceBuffer, null, "videoTrack.sourceBuffer");
+ test.done();
+ });
+ }
+
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ var initSegment = MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.init);
+ sourceBuffer.appendBuffer(initSegment);
+ test.expectEvent(sourceBuffer, "updateend", "initSegment append ended.");
+ test.waitForExpectedEvents(function()
+ {
+ verifyTrackRemoval(test, mediaElement, mediaSource, sourceBuffer, test.step_func(function ()
+ {
+ mediaSource.removeSourceBuffer(sourceBuffer);
+ }));
+ });
+ }, "Media tracks must be removed when the SourceBuffer is removed from the MediaSource");
+
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ var initSegment = MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.init);
+ sourceBuffer.appendBuffer(initSegment);
+ test.expectEvent(sourceBuffer, "updateend", "initSegment append ended.");
+ test.waitForExpectedEvents(function()
+ {
+ verifyTrackRemoval(test, mediaElement, mediaSource, sourceBuffer, test.step_func(function ()
+ {
+ mediaElement.src = "";
+ }));
+ });
+ }, "Media tracks must be removed when the HTMLMediaElement.src is changed");
+
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ var initSegment = MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.init);
+ sourceBuffer.appendBuffer(initSegment);
+ test.expectEvent(sourceBuffer, "updateend", "initSegment append ended.");
+ test.waitForExpectedEvents(function()
+ {
+ verifyTrackRemoval(test, mediaElement, mediaSource, sourceBuffer, test.step_func(function ()
+ {
+ mediaElement.load();
+ }));
+ });
+ }, "Media tracks must be removed when HTMLMediaElement.load() is called");
+</script>
diff --git a/testing/web-platform/tests/media-source/mediasource-buffered.html b/testing/web-platform/tests/media-source/mediasource-buffered.html
new file mode 100644
index 000000000..38cd1717e
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mediasource-buffered.html
@@ -0,0 +1,233 @@
+<!DOCTYPE html>
+<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
+<html>
+ <head>
+ <title>SourceBuffer.buffered test cases.</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="mediasource-util.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ var subType = MediaSourceUtil.getSubType(MediaSourceUtil.AUDIO_ONLY_TYPE);
+
+ var manifestFilenameA = subType + "/test-a-128k-44100Hz-1ch-manifest.json";
+ var manifestFilenameB = subType + "/test-v-128k-320x240-30fps-10kfr-manifest.json";
+
+ // Audio track expectations
+ var expectationsA = {
+ webm: "{ [0.000, 2.023) }",
+ mp4: "{ [0.000, 2.043) }",
+ };
+
+ // Video track expectations
+ var expectationsB = {
+ webm: "{ [0.000, 2.001) }",
+ mp4: "{ [0.067, 2.067) }",
+ };
+
+ // Audio and Video intersection expectations.
+ // https://w3c.github.io/media-source/index.html#dom-sourcebuffer-buffered
+ // When mediaSource.readyState is "ended", then set the end time on the last range in track ranges to highest end time.
+ var expectationsC = {
+ webm: ["{ [0.000, 2.001) }", "{ [0.000, 2.023) }"],
+ mp4: ["{ [0.067, 2.043) }", "{ [0.067, 2.067) }"]
+ };
+
+ function mediaSourceDemuxedTest(callback, description)
+ {
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ mediaElement.pause();
+ mediaElement.addEventListener("error", test.unreached_func("Unexpected event 'error'"));
+ mediaElement.addEventListener("ended", test.step_func_done());
+
+ MediaSourceUtil.fetchManifestAndData(test, manifestFilenameA, function(typeA, dataA)
+ {
+ MediaSourceUtil.fetchManifestAndData(test, manifestFilenameB, function(typeB, dataB)
+ {
+ mediaSource.addSourceBuffer(typeA);
+ mediaSource.addSourceBuffer(typeB);
+ assert_equals(mediaSource.sourceBuffers.length, 2);
+
+ callback(test, mediaElement, mediaSource, dataA, dataB);
+ });
+ });
+ }, description);
+ };
+
+ function appendData(test, mediaSource, dataA, dataB, callback)
+ {
+ var sourceBufferA = mediaSource.sourceBuffers[0];
+ var sourceBufferB = mediaSource.sourceBuffers[1];
+
+ test.expectEvent(sourceBufferA, "update");
+ test.expectEvent(sourceBufferA, "updateend");
+ sourceBufferA.appendBuffer(dataA);
+
+ test.expectEvent(sourceBufferB, "update");
+ test.expectEvent(sourceBufferB, "updateend");
+ sourceBufferB.appendBuffer(dataB);
+
+ test.waitForExpectedEvents(function()
+ {
+ callback();
+ });
+ }
+
+ mediaSourceDemuxedTest(function(test, mediaElement, mediaSource, dataA, dataB) {
+ test.expectEvent(mediaElement, "loadedmetadata");
+ appendData(test, mediaSource, dataA, dataB, function()
+ {
+ var expectedBeforeEndOfStreamIntersection = expectationsC[subType][0];
+ var expectedAfterEndOfStreamIntersection = expectationsC[subType][1];
+
+ assertBufferedEquals(mediaSource.activeSourceBuffers[0], expectationsA[subType], "mediaSource.activeSourceBuffers[0]");
+ assertBufferedEquals(mediaSource.activeSourceBuffers[1], expectationsB[subType], "mediaSource.activeSourceBuffers[1]");
+ assertBufferedEquals(mediaElement, expectedBeforeEndOfStreamIntersection, "mediaElement.buffered");
+
+ mediaSource.endOfStream();
+
+ assertBufferedEquals(mediaSource.activeSourceBuffers[0], expectationsA[subType], "mediaSource.activeSourceBuffers[0]");
+ assertBufferedEquals(mediaSource.activeSourceBuffers[1], expectationsB[subType], "mediaSource.activeSourceBuffers[1]");
+ assertBufferedEquals(mediaElement, expectedAfterEndOfStreamIntersection, "mediaElement.buffered");
+
+ test.done();
+ });
+ }, "Demuxed content with different lengths");
+
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ mediaElement.pause();
+ mediaElement.addEventListener("error", test.unreached_func("Unexpected event 'error'"));
+ mediaElement.addEventListener("ended", test.step_func_done());
+
+ MediaSourceUtil.fetchManifestAndData(test, subType + "/test-av-384k-44100Hz-1ch-320x240-30fps-10kfr-manifest.json", function(type, data)
+ {
+ var sourceBuffer = mediaSource.addSourceBuffer(type);
+ test.expectEvent(sourceBuffer, "update");
+ test.expectEvent(sourceBuffer, "updateend");
+ sourceBuffer.appendBuffer(data);
+
+ test.waitForExpectedEvents(function()
+ {
+ var expectationsAV = {
+ webm: ["{ [0.003, 2.004) }", "{ [0.003, 2.023) }"],
+ mp4: ["{ [0.067, 2.043) }", "{ [0.067, 2.067) }"],
+ };
+
+ var expectedBeforeEndOfStream = expectationsAV[subType][0];
+ var expectedAfterEndOfStream = expectationsAV[subType][1];
+
+ assertBufferedEquals(mediaSource.activeSourceBuffers[0], expectedBeforeEndOfStream, "mediaSource.activeSourceBuffers[0]");
+ assertBufferedEquals(mediaElement, expectedBeforeEndOfStream, "mediaElement.buffered");
+
+ mediaSource.endOfStream();
+
+ assertBufferedEquals(mediaSource.activeSourceBuffers[0], expectedAfterEndOfStream, "mediaSource.activeSourceBuffers[0]");
+ assertBufferedEquals(mediaElement, expectedAfterEndOfStream, "mediaElement.buffered");
+
+ test.done();
+ });
+ });
+ }, "Muxed content with different lengths");
+
+ mediaSourceDemuxedTest(function(test, mediaElement, mediaSource, dataA, dataB) {
+ var dataBSize = {
+ webm: 318,
+ mp4: 835,
+ };
+ test.expectEvent(mediaElement, "loadedmetadata");
+ appendData(test, mediaSource, dataA, dataB.subarray(0, dataBSize[subType]), function()
+ {
+ assertBufferedEquals(mediaSource.activeSourceBuffers[0], expectationsA[subType], "mediaSource.activeSourceBuffers[0]");
+ assertBufferedEquals(mediaSource.activeSourceBuffers[1], "{ }", "mediaSource.activeSourceBuffers[1]");
+ assertBufferedEquals(mediaElement, "{ }", "mediaElement.buffered");
+
+ mediaSource.endOfStream();
+
+ assertBufferedEquals(mediaSource.activeSourceBuffers[0], expectationsA[subType], "mediaSource.activeSourceBuffers[0]");
+ assertBufferedEquals(mediaSource.activeSourceBuffers[1], "{ }", "mediaSource.activeSourceBuffers[1]");
+ assertBufferedEquals(mediaElement, "{ }", "mediaElement.buffered");
+
+ test.done();
+ });
+ }, "Demuxed content with an empty buffered range on one SourceBuffer");
+
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ mediaElement.pause();
+ mediaElement.addEventListener("error", test.unreached_func("Unexpected event 'error'"));
+ mediaElement.addEventListener("ended", test.step_func_done());
+
+ MediaSourceUtil.fetchManifestAndData(test, subType + "/test-av-384k-44100Hz-1ch-320x240-30fps-10kfr-manifest.json", function(type, data)
+ {
+ var sourceBuffer = mediaSource.addSourceBuffer(type);
+ test.expectEvent(mediaElement, "loadedmetadata");
+ test.expectEvent(sourceBuffer, "update");
+ test.expectEvent(sourceBuffer, "updateend");
+ sourceBuffer.appendBuffer(data.subarray(0, 4052));
+
+ test.waitForExpectedEvents(function()
+ {
+ assertBufferedEquals(mediaSource.activeSourceBuffers[0], "{ }", "mediaSource.activeSourceBuffers[0]");
+ assertBufferedEquals(mediaElement, "{ }", "mediaElement.buffered");
+
+ mediaSource.endOfStream();
+
+ assertBufferedEquals(mediaSource.activeSourceBuffers[0], "{ }", "mediaSource.activeSourceBuffers[0]");
+ assertBufferedEquals(mediaElement, "{ }", "mediaElement.buffered");
+
+ test.done();
+ });
+ });
+ }, "Muxed content empty buffered ranges.");
+
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ mediaElement.pause();
+ mediaElement.addEventListener("error", test.unreached_func("Unexpected event 'error'"));
+ mediaElement.addEventListener("ended", test.step_func_done());
+
+ var sourceBuffer = mediaSource.addSourceBuffer(MediaSourceUtil.AUDIO_ONLY_TYPE);
+
+ assertBufferedEquals(mediaSource.sourceBuffers[0], "{ }", "mediaSource.sourceBuffers[0]");
+ assertBufferedEquals(mediaElement, "{ }", "mediaElement.buffered");
+ test.done();
+
+ }, "Get buffered range when sourcebuffer is empty.");
+
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ var initSegment = MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.init);
+
+ test.expectEvent(mediaElement, "loadedmetadata");
+ test.expectEvent(sourceBuffer, "updateend", "initSegment append ended.");
+ sourceBuffer.appendBuffer(initSegment);
+ test.waitForExpectedEvents(function()
+ {
+ assertBufferedEquals(mediaSource.sourceBuffers[0], "{ }", "mediaSource.sourceBuffers[0]");
+ assertBufferedEquals(mediaSource.activeSourceBuffers[0], "{ }", "mediaSource.activeSourceBuffers[0]");
+ assertBufferedEquals(mediaElement, "{ }", "mediaElement.buffered");
+ test.done();
+ });
+
+ }, "Get buffered range when only init segment is appended.");
+
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ test.expectEvent(mediaSource.sourceBuffers, "removesourcebuffer", "SourceBuffer removed.");
+ mediaSource.removeSourceBuffer(sourceBuffer);
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_throws("InvalidStateError",
+ function() { sourceBuffer.buffered; },
+ "get sourceBuffer.buffered throws an exception for InvalidStateError.");
+ test.done();
+ });
+ }, "Get buffered range after removing sourcebuffer.");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/media-source/mediasource-closed.html b/testing/web-platform/tests/media-source/mediasource-closed.html
new file mode 100644
index 000000000..4b22cae85
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mediasource-closed.html
@@ -0,0 +1,140 @@
+<!DOCTYPE html>
+<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
+<html>
+ <head>
+ <title>MediaSource.readyState equals "closed" test cases.</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="mediasource-util.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ test(function ()
+ {
+ var mediaSource = new MediaSource();
+ assert_equals(mediaSource.sourceBuffers.length, 0, "sourceBuffers is empty");
+ assert_equals(mediaSource.activeSourceBuffers.length, 0, "activeSourceBuffers is empty");
+ assert_equals(mediaSource.readyState, "closed", "readyState is 'closed'");
+ assert_true(isNaN(mediaSource.duration), "duration is NaN");
+ }, "Test attribute values on a closed MediaSource object.");
+
+ test(function ()
+ {
+ var mediaSource = new MediaSource();
+ assert_throws("InvalidStateError",
+ function() { mediaSource.addSourceBuffer(MediaSourceUtil.VIDEO_ONLY_TYPE); },
+ "addSourceBuffer() throws an exception when closed.");
+ }, "Test addSourceBuffer() while closed.");
+
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ var sourceBuffer = mediaSource.addSourceBuffer(MediaSourceUtil.AUDIO_ONLY_TYPE);
+
+ // Setup a handler to run when the MediaSource closes.
+ mediaSource.addEventListener('sourceclose', test.step_func(function (event)
+ {
+ assert_equals(mediaSource.sourceBuffers.length, 0, "sourceBuffers is empty");
+ assert_equals(mediaSource.activeSourceBuffers.length, 0, "activeSourceBuffers is empty");
+ assert_equals(mediaSource.readyState, "closed", "readyState is 'closed'");
+ assert_throws("NotFoundError",
+ function() { mediaSource.removeSourceBuffer(sourceBuffer); },
+ "removeSourceBuffer() throws an exception when closed.");
+ test.done();
+ }));
+
+ // Trigger the MediaSource to close.
+ mediaElement.src = "";
+ }, "Test removeSourceBuffer() while closed.");
+
+ test(function ()
+ {
+ var mediaSource = new MediaSource();
+ assert_throws("InvalidStateError",
+ function() { mediaSource.endOfStream(); },
+ "endOfStream() throws an exception when closed.");
+ }, "Test endOfStream() while closed.");
+
+ test(function ()
+ {
+ var mediaSource = new MediaSource();
+ assert_throws("InvalidStateError",
+ function() { mediaSource.endOfStream("decode"); },
+ "endOfStream(decode) throws an exception when closed.");
+ }, "Test endOfStream(decode) while closed.");
+
+ test(function ()
+ {
+ var mediaSource = new MediaSource();
+ assert_throws("InvalidStateError",
+ function() { mediaSource.endOfStream("network"); },
+ "endOfStream(network) throws an exception when closed.");
+ }, "Test endOfStream(network) while closed.");
+
+ test(function ()
+ {
+ var mediaSource = new MediaSource();
+ assert_throws("InvalidStateError",
+ function() { mediaSource.duration = 10; },
+ "Setting duration throws an exception when closed.");
+ }, "Test setting duration while closed.");
+
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ var sourceBuffer = mediaSource.addSourceBuffer(MediaSourceUtil.AUDIO_ONLY_TYPE);
+
+ assert_equals(mediaSource.readyState, "open", "readyState is 'open'");
+ // Setup a handler to run when the MediaSource closes.
+ mediaSource.addEventListener("sourceclose", test.step_func(function (event)
+ {
+ assert_equals(mediaSource.readyState, "closed", "readyState is 'closed'");
+ assert_throws("InvalidStateError",
+ function() { mediaSource.duration = 10; },
+ "Setting duration when closed throws an exception");
+ test.done();
+ }));
+
+ // Trigger the MediaSource to close.
+ mediaElement.src = "";
+ }, "Test setting duration while open->closed.");
+
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ var sourceBuffer = mediaSource.addSourceBuffer(MediaSourceUtil.AUDIO_ONLY_TYPE);
+
+ assert_equals(mediaSource.readyState, "open", "readyState is 'open'");
+ // Setup a handler to run when the MediaSource closes.
+ mediaSource.addEventListener("sourceclose", test.step_func(function (event)
+ {
+ assert_equals(mediaSource.readyState, "closed", "readyState is 'closed'");
+ assert_true(isNaN(mediaSource.duration), "duration is NaN");
+ test.done();
+ }));
+
+ // Trigger the MediaSource to close.
+ mediaElement.src = "";
+ }, "Test getting duration while open->closed.");
+
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ var sourceBuffer = mediaSource.addSourceBuffer(MediaSourceUtil.AUDIO_ONLY_TYPE);
+
+ assert_equals(mediaSource.readyState, "open", "readyState is open");
+
+ // Setup a handler to run when the MediaSource closes.
+ mediaSource.addEventListener("sourceclose", test.step_func(function (event)
+ {
+ assert_equals(mediaSource.readyState, "closed", "readyState is closed");
+ assert_throws("InvalidStateError",
+ function() { sourceBuffer.abort(); },
+ "sourceBuffer.abort() throws INVALID_STATE_ERROR");
+ test.done();
+ }));
+
+ // Trigger the MediaSource to close.
+ mediaElement.src = "";
+ }, "Test sourcebuffer.abort when closed.");
+
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/media-source/mediasource-config-change-mp4-a-bitrate.html b/testing/web-platform/tests/media-source/mediasource-config-change-mp4-a-bitrate.html
new file mode 100644
index 000000000..e5fe45f3a
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mediasource-config-change-mp4-a-bitrate.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
+<html>
+ <head>
+ <title>MP4 audio-only bitrate change.</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="mediasource-util.js"></script>
+ <script src="mediasource-config-changes.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ mediaSourceConfigChangeTest("mp4", "a-128k-44100Hz-1ch", "a-192k-44100Hz-1ch", "Tests mp4 audio-only bitrate changes.");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/media-source/mediasource-config-change-mp4-av-audio-bitrate.html b/testing/web-platform/tests/media-source/mediasource-config-change-mp4-av-audio-bitrate.html
new file mode 100644
index 000000000..007026b75
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mediasource-config-change-mp4-av-audio-bitrate.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
+<html>
+ <head>
+ <title>MP4 muxed audio &amp; video with an audio bitrate change.</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="mediasource-util.js"></script>
+ <script src="mediasource-config-changes.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ mediaSourceConfigChangeTest("mp4", "av-384k-44100Hz-1ch-640x480-30fps-10kfr", "av-448k-44100Hz-1ch-640x480-30fps-10kfr", "Tests mp4 audio bitrate changes in multiplexed content.");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/media-source/mediasource-config-change-mp4-av-framesize.html b/testing/web-platform/tests/media-source/mediasource-config-change-mp4-av-framesize.html
new file mode 100644
index 000000000..202e09dbc
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mediasource-config-change-mp4-av-framesize.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
+<html>
+ <head>
+ <title>MP4 muxed audio &amp; video with a video frame size change.</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="mediasource-util.js"></script>
+ <script src="mediasource-config-changes.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ mediaSourceConfigChangeTest("mp4", "av-384k-44100Hz-1ch-320x240-30fps-10kfr", "av-384k-44100Hz-1ch-640x480-30fps-10kfr", "Tests mp4 frame size changes in multiplexed content.");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/media-source/mediasource-config-change-mp4-av-video-bitrate.html b/testing/web-platform/tests/media-source/mediasource-config-change-mp4-av-video-bitrate.html
new file mode 100644
index 000000000..640a816de
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mediasource-config-change-mp4-av-video-bitrate.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
+<html>
+ <head>
+ <title>MP4 muxed audio &amp; video with a video bitrate change.</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="mediasource-util.js"></script>
+ <script src="mediasource-config-changes.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ mediaSourceConfigChangeTest("mp4", "av-384k-44100Hz-1ch-640x480-30fps-10kfr", "av-640k-44100Hz-1ch-640x480-30fps-10kfr", "Tests mp4 video bitrate changes in multiplexed content.");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/media-source/mediasource-config-change-mp4-v-bitrate.html b/testing/web-platform/tests/media-source/mediasource-config-change-mp4-v-bitrate.html
new file mode 100644
index 000000000..705c5bd3c
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mediasource-config-change-mp4-v-bitrate.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
+<html>
+ <head>
+ <title>MP4 video-only bitrate change.</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="mediasource-util.js"></script>
+ <script src="mediasource-config-changes.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ mediaSourceConfigChangeTest("mp4", "v-128k-320x240-30fps-10kfr", "v-256k-320x240-30fps-10kfr", "Tests mp4 video-only bitrate changes.");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/media-source/mediasource-config-change-mp4-v-framerate.html b/testing/web-platform/tests/media-source/mediasource-config-change-mp4-v-framerate.html
new file mode 100644
index 000000000..078236cd1
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mediasource-config-change-mp4-v-framerate.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
+<html>
+ <head>
+ <title>MP4 video-only frame rate change.</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="mediasource-util.js"></script>
+ <script src="mediasource-config-changes.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ mediaSourceConfigChangeTest("mp4", "v-128k-320x240-24fps-8kfr", "v-128k-320x240-30fps-10kfr", "Tests mp4 video-only frame rate changes.");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/media-source/mediasource-config-change-mp4-v-framesize.html b/testing/web-platform/tests/media-source/mediasource-config-change-mp4-v-framesize.html
new file mode 100644
index 000000000..cb83908c4
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mediasource-config-change-mp4-v-framesize.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
+<html>
+ <head>
+ <title>MP4 video-only frame size change.</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="mediasource-util.js"></script>
+ <script src="mediasource-config-changes.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ mediaSourceConfigChangeTest("mp4", "v-128k-320x240-30fps-10kfr", "v-128k-640x480-30fps-10kfr", "Tests mp4 video-only frame size changes.");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/media-source/mediasource-config-change-webm-a-bitrate.html b/testing/web-platform/tests/media-source/mediasource-config-change-webm-a-bitrate.html
new file mode 100644
index 000000000..cc351cd30
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mediasource-config-change-webm-a-bitrate.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
+<html>
+ <head>
+ <title>WebM audio-only bitrate change.</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="mediasource-util.js"></script>
+ <script src="mediasource-config-changes.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ mediaSourceConfigChangeTest("webm", "a-128k-44100Hz-1ch", "a-192k-44100Hz-1ch", "Tests webm audio-only bitrate changes.");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/media-source/mediasource-config-change-webm-av-audio-bitrate.html b/testing/web-platform/tests/media-source/mediasource-config-change-webm-av-audio-bitrate.html
new file mode 100644
index 000000000..082e5a813
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mediasource-config-change-webm-av-audio-bitrate.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
+<html>
+ <head>
+ <title>WebM muxed audio &amp; video with an audio bitrate change.</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="mediasource-util.js"></script>
+ <script src="mediasource-config-changes.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ mediaSourceConfigChangeTest("webm", "av-384k-44100Hz-1ch-640x480-30fps-10kfr", "av-448k-44100Hz-1ch-640x480-30fps-10kfr", "Tests webm audio bitrate changes in multiplexed content.");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/media-source/mediasource-config-change-webm-av-framesize.html b/testing/web-platform/tests/media-source/mediasource-config-change-webm-av-framesize.html
new file mode 100644
index 000000000..c37f8c2ed
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mediasource-config-change-webm-av-framesize.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
+<html>
+ <head>
+ <title>WebM muxed audio &amp; video with a video frame size change.</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="mediasource-util.js"></script>
+ <script src="mediasource-config-changes.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ mediaSourceConfigChangeTest("webm", "av-384k-44100Hz-1ch-320x240-30fps-10kfr", "av-384k-44100Hz-1ch-640x480-30fps-10kfr", "Tests webm frame size changes in multiplexed content.");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/media-source/mediasource-config-change-webm-av-video-bitrate.html b/testing/web-platform/tests/media-source/mediasource-config-change-webm-av-video-bitrate.html
new file mode 100644
index 000000000..0fe6c358e
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mediasource-config-change-webm-av-video-bitrate.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
+<html>
+ <head>
+ <title>WebM muxed audio &amp; video with a video bitrate change.</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="mediasource-util.js"></script>
+ <script src="mediasource-config-changes.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ mediaSourceConfigChangeTest("webm", "av-384k-44100Hz-1ch-640x480-30fps-10kfr", "av-640k-44100Hz-1ch-640x480-30fps-10kfr", "Tests webm video bitrate changes in multiplexed content.");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/media-source/mediasource-config-change-webm-v-bitrate.html b/testing/web-platform/tests/media-source/mediasource-config-change-webm-v-bitrate.html
new file mode 100644
index 000000000..e194e267d
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mediasource-config-change-webm-v-bitrate.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
+<html>
+ <head>
+ <title>WebM video-only bitrate change.</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="mediasource-util.js"></script>
+ <script src="mediasource-config-changes.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ mediaSourceConfigChangeTest("webm", "v-128k-320x240-30fps-10kfr", "v-256k-320x240-30fps-10kfr", "Tests webm video-only bitrate changes.");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/media-source/mediasource-config-change-webm-v-framerate.html b/testing/web-platform/tests/media-source/mediasource-config-change-webm-v-framerate.html
new file mode 100644
index 000000000..fb0100c7b
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mediasource-config-change-webm-v-framerate.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
+<html>
+ <head>
+ <title>WebM video-only frame rate change.</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="mediasource-util.js"></script>
+ <script src="mediasource-config-changes.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ mediaSourceConfigChangeTest("webm", "v-128k-320x240-24fps-8kfr", "v-128k-320x240-30fps-10kfr", "Tests webm video-only frame rate changes.");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/media-source/mediasource-config-change-webm-v-framesize.html b/testing/web-platform/tests/media-source/mediasource-config-change-webm-v-framesize.html
new file mode 100644
index 000000000..ca13c78a3
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mediasource-config-change-webm-v-framesize.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
+<html>
+ <head>
+ <title>WebM video-only frame size change.</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="mediasource-util.js"></script>
+ <script src="mediasource-config-changes.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ mediaSourceConfigChangeTest("webm", "v-128k-320x240-30fps-10kfr", "v-128k-640x480-30fps-10kfr", "Tests webm video-only frame size changes.");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/media-source/mediasource-config-changes.js b/testing/web-platform/tests/media-source/mediasource-config-changes.js
new file mode 100644
index 000000000..ea99b8ba5
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mediasource-config-changes.js
@@ -0,0 +1,113 @@
+// Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang).
+
+// Extract & return the resolution string from a filename, if any.
+function resolutionFromFilename(filename)
+{
+ resolution = filename.replace(/^.*[^0-9]([0-9]+x[0-9]+)[^0-9].*$/, "$1");
+ if (resolution != filename) {
+ return resolution;
+ }
+ return "";
+}
+
+function appendBuffer(test, sourceBuffer, data)
+{
+ test.expectEvent(sourceBuffer, "update");
+ test.expectEvent(sourceBuffer, "updateend");
+ sourceBuffer.appendBuffer(data);
+}
+
+function mediaSourceConfigChangeTest(directory, idA, idB, description)
+{
+ var manifestFilenameA = directory + "/test-" + idA + "-manifest.json";
+ var manifestFilenameB = directory + "/test-" + idB + "-manifest.json";
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ mediaElement.pause();
+ mediaElement.addEventListener('error', test.unreached_func("Unexpected event 'error'"));
+ var expectResizeEvents = resolutionFromFilename(manifestFilenameA) != resolutionFromFilename(manifestFilenameB);
+ var expectedResizeEventCount = 0;
+
+ MediaSourceUtil.fetchManifestAndData(test, manifestFilenameA, function(typeA, dataA)
+ {
+ MediaSourceUtil.fetchManifestAndData(test, manifestFilenameB, function(typeB, dataB)
+ {
+ assert_equals(typeA, typeB, "Media format types match");
+
+ var sourceBuffer = mediaSource.addSourceBuffer(typeA);
+
+ appendBuffer(test, sourceBuffer, dataA);
+ ++expectedResizeEventCount;
+
+ test.waitForExpectedEvents(function()
+ {
+ // Add the second buffer starting at 0.5 second.
+ sourceBuffer.timestampOffset = 0.5;
+ appendBuffer(test, sourceBuffer, dataB);
+ ++expectedResizeEventCount;
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ // Add the first buffer starting at 1 second.
+ sourceBuffer.timestampOffset = 1;
+ appendBuffer(test, sourceBuffer, dataA);
+ ++expectedResizeEventCount;
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ // Add the second buffer starting at 1.5 second.
+ sourceBuffer.timestampOffset = 1.5;
+ appendBuffer(test, sourceBuffer, dataB);
+ ++expectedResizeEventCount;
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_false(sourceBuffer.updating, "updating");
+
+ // Truncate the presentation to a duration of 2 seconds.
+ sourceBuffer.remove(2, Infinity);
+
+ assert_true(sourceBuffer.updating, "updating");
+ test.expectEvent(sourceBuffer, 'updatestart', 'sourceBuffer');
+ test.expectEvent(sourceBuffer, 'update', 'sourceBuffer');
+ test.expectEvent(sourceBuffer, 'updateend', 'sourceBuffer');
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_false(sourceBuffer.updating, "updating");
+ assert_greater_than(mediaSource.duration, 2, "duration");
+
+ // Truncate the presentation to a duration of 2 seconds.
+ mediaSource.duration = 2;
+
+ test.expectEvent(mediaElement, "durationchange");
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_false(sourceBuffer.updating, "updating");
+
+ mediaSource.endOfStream();
+
+ assert_false(sourceBuffer.updating, "updating");
+
+ if (expectResizeEvents) {
+ for (var i = 0; i < expectedResizeEventCount; ++i) {
+ test.expectEvent(mediaElement, "resize");
+ }
+ }
+ test.expectEvent(mediaElement, "ended");
+ mediaElement.play();
+ });
+
+ test.waitForExpectedEvents(function() {
+ test.done();
+ });
+ });
+ });
+ }, description);
+};
diff --git a/testing/web-platform/tests/media-source/mediasource-detach.html b/testing/web-platform/tests/media-source/mediasource-detach.html
new file mode 100644
index 000000000..b25b5c6f0
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mediasource-detach.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="mediasource-util.js"></script>
+<script>
+ function mediasource_detach_test(testFunction, description)
+ {
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ var segmentInfo = MediaSourceUtil.SEGMENT_INFO;
+ var sourceBuffer = mediaSource.addSourceBuffer(segmentInfo.type);
+
+ assert_equals(mediaElement.readyState, HTMLMediaElement.HAVE_NOTHING);
+ assert_equals(mediaSource.readyState, 'open');
+
+ mediaSource.addEventListener('sourceclose', test.step_func(function (event)
+ {
+ assert_equals(mediaSource.sourceBuffers.length, 0, 'sourceBuffers is empty');
+ assert_equals(mediaSource.activeSourceBuffers.length, 0, 'activeSourceBuffers is empty');
+ assert_equals(mediaSource.readyState, 'closed', 'readyState is "closed"');
+ assert_true(Number.isNaN(mediaSource.duration), 'duration is NaN');
+ test.done();
+ }));
+
+ MediaSourceUtil.loadBinaryData(test, segmentInfo.url, function(mediaData)
+ {
+ testFunction(test, mediaElement, mediaSource, sourceBuffer, mediaData);
+ });
+ }, description);
+ }
+
+ mediasource_detach_test(function(test, mediaElement, mediaSource, sourceBuffer, mediaData)
+ {
+ mediaElement.load();
+ }, 'Test media.load() before appending data will trigger MediaSource detaching from a media element.');
+
+ mediasource_detach_test(function(test, mediaElement, mediaSource, sourceBuffer, mediaData)
+ {
+ sourceBuffer.addEventListener('updateend', test.step_func(function (event)
+ {
+ assert_greater_than(mediaElement.readyState, HTMLMediaElement.HAVE_NOTHING, 'media readyState is greater than "HAVE_NOTHING"')
+ assert_false(sourceBuffer.updating, 'updating attribute is false');
+ mediaElement.load();
+ }));
+
+ sourceBuffer.appendBuffer(mediaData);
+ }, 'Test media.load() after appending data will trigger MediaSource detaching from a media element.');
+</script>
diff --git a/testing/web-platform/tests/media-source/mediasource-duration-boundaryconditions.html b/testing/web-platform/tests/media-source/mediasource-duration-boundaryconditions.html
new file mode 100644
index 000000000..b95e495cb
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mediasource-duration-boundaryconditions.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
+<html>
+ <head>
+ <title>MediaSource.duration boundary condition test cases.</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="mediasource-util.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ function DurationBoundaryConditionTest(testDurationValue, expectedError, description)
+ {
+ return mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ // Append initialization segment.
+ test.expectEvent(sourceBuffer, "updateend", "sourceBuffer");
+ test.expectEvent(mediaElement, "loadedmetadata", "mediaElement");
+ sourceBuffer.appendBuffer(MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.init));
+ test.waitForExpectedEvents(function()
+ {
+ if (expectedError) {
+ assert_throws(expectedError,
+ function() { mediaSource.duration = testDurationValue; },
+ "mediaSource.duration assignment throws an exception for " + testDurationValue);
+ test.done();
+ return;
+ }
+
+ mediaSource.duration = testDurationValue;
+
+ assert_equals(mediaSource.duration, testDurationValue, "mediaSource.duration");
+ assert_equals(mediaElement.duration, testDurationValue, "mediaElement.duration");
+
+ test.expectEvent(mediaElement, "durationchange", "mediaElement");
+ test.waitForExpectedEvents(function()
+ {
+ assert_equals(mediaSource.duration, testDurationValue, "mediaSource.duration");
+ assert_equals(mediaElement.duration, testDurationValue, "mediaElement.duration");
+ test.done();
+ });
+ });
+
+ }, description);
+ }
+
+ DurationBoundaryConditionTest(Math.pow(2, 31) - 1, null, "Set duration to 2^31 - 1");
+ DurationBoundaryConditionTest(1, null, "Set duration to 1");
+ DurationBoundaryConditionTest(Number.MAX_VALUE, null, "Set duration to Number.MAX_VALUE");
+ DurationBoundaryConditionTest(Number.MIN_VALUE, null, "Set duration to Number.MIN_VALUE");
+ DurationBoundaryConditionTest(Number.MAX_VALUE - 1, null, "Set duration to Number.MAX_VALUE - 1");
+ DurationBoundaryConditionTest(Number.MIN_VALUE - 1, new TypeError(), "Set duration to Number.MIN_VALUE - 1");
+ DurationBoundaryConditionTest(Number.POSITIVE_INFINITY, null, "Set duration to Number.POSITIVE_INFINITY");
+ DurationBoundaryConditionTest(Number.NEGATIVE_INFINITY, new TypeError(), "Set duration to Number.NEGATIVE_INFINITY");
+ DurationBoundaryConditionTest(-1 * Number.MAX_VALUE, new TypeError(), "Set duration to lowest value.");
+ DurationBoundaryConditionTest(-101.9, new TypeError(), "Set duration to a negative double.");
+ DurationBoundaryConditionTest(101.9, null, "Set duration to a positive double.");
+ DurationBoundaryConditionTest(0, null, "Set duration to zero");
+ DurationBoundaryConditionTest(NaN, new TypeError(), "Set duration to NaN");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/media-source/mediasource-duration.html b/testing/web-platform/tests/media-source/mediasource-duration.html
new file mode 100644
index 000000000..4bc0fb2d7
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mediasource-duration.html
@@ -0,0 +1,383 @@
+<!DOCTYPE html>
+<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
+<html>
+ <head>
+ <title>MediaSource.duration &amp; HTMLMediaElement.duration test cases.</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="mediasource-util.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+
+ var subType = MediaSourceUtil.getSubType(MediaSourceUtil.AUDIO_ONLY_TYPE);
+ var manifestFilenameAudio = subType + "/test-a-128k-44100Hz-1ch-manifest.json";
+ var manifestFilenameVideo = subType + "/test-v-128k-320x240-30fps-10kfr-manifest.json";
+
+ function mediasource_truncated_duration_seek_test(testFunction, description, options)
+ {
+ return mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ assert_greater_than(segmentInfo.duration, 2, 'Sufficient test media duration');
+
+ var fullDuration = segmentInfo.duration;
+ var seekTo = fullDuration / 2.0;
+ var truncatedDuration = seekTo / 2.0;
+
+ mediaElement.play();
+
+ // Append all the segments
+ test.expectEvent(sourceBuffer, 'updateend', 'sourceBuffer');
+ test.expectEvent(mediaElement, 'playing', 'Playing triggered');
+ sourceBuffer.appendBuffer(mediaData);
+
+ test.waitForExpectedEvents(function()
+ {
+ test.expectEvent(mediaElement, 'seeking', 'seeking to seekTo');
+ test.expectEvent(mediaElement, 'timeupdate', 'timeupdate while seeking to seekTo');
+ test.expectEvent(mediaElement, 'seeked', 'seeked to seekTo');
+ mediaElement.currentTime = seekTo;
+ assert_true(mediaElement.seeking, 'mediaElement.seeking (to seekTo)');
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_greater_than_equal(mediaElement.currentTime, seekTo, 'Playback time has reached seekTo');
+ assert_false(mediaElement.seeking, 'mediaElement.seeking after seeked to seekTo');
+
+ assert_false(sourceBuffer.updating, 'sourceBuffer.updating');
+
+ sourceBuffer.remove(truncatedDuration, Infinity);
+
+ assert_true(sourceBuffer.updating, 'sourceBuffer.updating');
+ test.expectEvent(sourceBuffer, 'updatestart', 'sourceBuffer');
+ test.expectEvent(sourceBuffer, 'update', 'sourceBuffer');
+ test.expectEvent(sourceBuffer, 'updateend', 'sourceBuffer');
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_greater_than_equal(mediaElement.currentTime, seekTo, 'Playback time has reached seekTo');
+ assert_false(sourceBuffer.updating, 'sourceBuffer.updating');
+
+ // Remove will not remove partial frames, so the resulting duration is the highest end time
+ // of the track buffer ranges, and is greater than or equal to the highest coded frame
+ // presentation time across all track buffer ranges. We first obtain the intersected track buffer
+ // ranges end time and set the duration to that value.
+ truncatedDuration = sourceBuffer.buffered.end(sourceBuffer.buffered.length-1);
+ assert_less_than(truncatedDuration, seekTo,
+ 'remove has removed the current playback position from at least one track buffer');
+
+ mediaSource.duration = truncatedDuration;
+ test.expectEvent(mediaElement, 'seeking', 'Seeking to truncated duration');
+
+ // The actual duration may be slightly higher than truncatedDuration because the
+ // duration is adjusted upwards if necessary to be the highest end time across all track buffer
+ // ranges. Allow that increase here.
+ assert_less_than_equal(truncatedDuration, mediaSource.duration,
+ 'Duration should not be less than what was set');
+ // Here, we assume no test media coded frame duration is longer than 100ms.
+ assert_less_than(mediaSource.duration - truncatedDuration, 0.1);
+
+ // Update our truncatedDuration to be the actual new duration.
+ truncatedDuration = mediaSource.duration;
+
+ assert_true(mediaElement.seeking, 'Seeking after setting truncatedDuration');
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_equals(mediaElement.currentTime, truncatedDuration,
+ 'Playback time is truncatedDuration while seeking');
+ assert_true(mediaElement.seeking, 'mediaElement.seeking while seeking to truncatedDuration');
+ assert_equals(mediaElement.duration, truncatedDuration,
+ 'mediaElement truncatedDuration during seek to it');
+ assert_equals(mediaSource.duration, truncatedDuration,
+ 'mediaSource truncatedDuration during seek to it');
+
+ testFunction(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData,
+ truncatedDuration);
+ });
+ }, description, options);
+ }
+
+ mediasource_truncated_duration_seek_test(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer,
+ mediaData, truncatedDuration)
+ {
+ // Tests that duration truncation below current playback position
+ // starts seek to new duration.
+ test.done();
+ }, 'Test seek starts on duration truncation below currentTime');
+
+ mediasource_truncated_duration_seek_test(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer,
+ mediaData, truncatedDuration)
+ {
+ // The duration has been truncated at this point, and there is an
+ // outstanding seek pending.
+ test.expectEvent(sourceBuffer, 'updateend', 'updateend after appending more data');
+
+ test.expectEvent(mediaElement, 'timeupdate', 'timeupdate while finishing seek to truncatedDuration');
+ test.expectEvent(mediaElement, 'seeked', 'seeked to truncatedDuration');
+
+ // Allow seek to complete by appending more data beginning at the
+ // truncated duration timestamp.
+ sourceBuffer.timestampOffset = truncatedDuration;
+ sourceBuffer.appendBuffer(mediaData);
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_greater_than_equal(mediaElement.currentTime, truncatedDuration,
+ 'Playback time has reached truncatedDuration');
+ assert_approx_equals(mediaElement.duration, truncatedDuration + segmentInfo.duration, 0.05,
+ 'mediaElement duration increased by new append');
+ assert_equals(mediaSource.duration, mediaElement.duration,
+ 'mediaSource duration increased by new append');
+ assert_false(mediaElement.seeking, 'mediaElement.seeking after seeked to truncatedDuration');
+
+ test.done();
+ });
+ }, 'Test appendBuffer completes previous seek to truncated duration');
+
+ mediasource_truncated_duration_seek_test(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer,
+ mediaData, truncatedDuration)
+ {
+ // The duration has been truncated at this point, and there is an
+ // outstanding seek pending.
+ test.expectEvent(mediaSource, 'sourceended', 'endOfStream acknowledged');
+
+ test.expectEvent(mediaElement, 'timeupdate', 'timeupdate while finishing seek to truncatedDuration');
+ test.expectEvent(mediaElement, 'seeked', 'seeked to truncatedDuration');
+
+ // Call endOfStream() to complete the pending seek.
+ mediaSource.endOfStream();
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_greater_than_equal(mediaElement.currentTime, truncatedDuration,
+ 'Playback time has reached truncatedDuration');
+ // The mediaSource.readyState is "ended". Buffered ranges have been adjusted to the longest track.
+ truncatedDuration = sourceBuffer.buffered.end(sourceBuffer.buffered.length-1);
+ assert_equals(mediaElement.duration, truncatedDuration,
+ 'mediaElement truncatedDuration after seek to it');
+ assert_equals(mediaSource.duration, truncatedDuration,
+ 'mediaSource truncatedDuration after seek to it');
+ assert_false(mediaElement.seeking, 'mediaElement.seeking after seeked to truncatedDuration');
+
+ test.done();
+ });
+ }, 'Test endOfStream completes previous seek to truncated duration');
+
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ assert_greater_than(segmentInfo.duration, 2, 'Sufficient test media duration');
+
+ var fullDuration = segmentInfo.duration;
+ var newDuration = 0.5;
+
+ var expectedDurationChangeEventCount = 1;
+ var durationchangeEventCounter = 0;
+ var durationchangeEventHandler = test.step_func(function(event)
+ {
+ assert_equals(mediaElement.duration, mediaSource.duration, 'mediaElement newDuration');
+ // Final duration may be greater than originally set as per MSE's 2.4.6 Duration change
+ // Adjust newDuration accordingly.
+ assert_less_than_equal(newDuration, mediaSource.duration, 'mediaSource newDuration');
+ durationchangeEventCounter++;
+ });
+
+ mediaElement.play();
+
+ // Append all the segments
+ test.expectEvent(sourceBuffer, 'updateend', 'sourceBuffer');
+ test.expectEvent(mediaElement, 'playing', 'Playing triggered');
+ sourceBuffer.appendBuffer(mediaData);
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_less_than(mediaElement.currentTime, newDuration / 2, 'mediaElement currentTime');
+
+ assert_false(sourceBuffer.updating, "updating");
+
+ // Truncate duration. This should result in one 'durationchange' fired.
+ sourceBuffer.remove(newDuration, Infinity);
+
+ assert_true(sourceBuffer.updating, "updating");
+ test.expectEvent(sourceBuffer, 'updatestart', 'sourceBuffer');
+ test.expectEvent(sourceBuffer, 'update', 'sourceBuffer');
+ test.expectEvent(sourceBuffer, 'updateend', 'sourceBuffer');
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ // Media load also fires 'durationchange' event, so only start counting them now.
+ mediaElement.addEventListener('durationchange', durationchangeEventHandler);
+
+ assert_false(sourceBuffer.updating, "updating");
+
+ // Truncate duration. This should result in one 'durationchange' fired.
+ mediaSource.duration = newDuration;
+
+ // Final duration may be greater than originally set as per MSE's 2.4.6 Duration change
+ // Adjust newDuration accordingly.
+ assert_true(newDuration <= mediaSource.duration, 'adjusted duration');
+ newDuration = mediaSource.duration;
+
+ // Set duration again to make sure it does not trigger another 'durationchange' event.
+ mediaSource.duration = newDuration;
+
+ // Mark endOfStream so that playback can reach 'ended' at the new duration.
+ test.expectEvent(mediaSource, 'sourceended', 'endOfStream acknowledged');
+ mediaSource.endOfStream();
+
+ // endOfStream can change duration slightly.
+ // Allow for one more 'durationchange' event only in this case.
+ var currentDuration = mediaSource.duration;
+ if (currentDuration != newDuration) {
+ newDuration = currentDuration;
+ ++expectedDurationChangeEventCount;
+ }
+
+ // Allow media to play to end while counting 'durationchange' events.
+ test.expectEvent(mediaElement, 'ended', 'Playback ended');
+ test.waitForExpectedEvents(function()
+ {
+ mediaElement.removeEventListener('durationchange', durationchangeEventHandler);
+ assert_equals(durationchangeEventCounter, expectedDurationChangeEventCount, 'durationchanges');
+ test.done();
+ });
+ });
+ }, 'Test setting same duration multiple times does not fire duplicate durationchange');
+
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ assert_greater_than(segmentInfo.duration, 2, 'Sufficient test media duration');
+
+ var fullDuration = segmentInfo.duration;
+ var newDuration = fullDuration / 2;
+
+ // Append all the segments
+ test.expectEvent(sourceBuffer, 'updateend', 'sourceBuffer');
+ test.expectEvent(mediaElement, 'loadedmetadata', 'mediaElement');
+ sourceBuffer.appendBuffer(mediaData);
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_false(sourceBuffer.updating, "updating");
+
+ assert_throws("InvalidStateError", function()
+ {
+ mediaSource.duration = newDuration;
+ }, "duration");
+
+ test.done();
+ });
+ }, 'Test setting the duration to less than the highest starting presentation timestamp will throw');
+
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ mediaElement.addEventListener("error", test.unreached_func("Unexpected event 'error'"));
+ MediaSourceUtil.fetchManifestAndData(test, manifestFilenameAudio, function(typeAudio, dataAudio)
+ {
+ MediaSourceUtil.fetchManifestAndData(test, manifestFilenameVideo, function(typeVideo, dataVideo)
+ {
+ var sourceBufferAudio = mediaSource.addSourceBuffer(typeAudio);
+ var sourceBufferVideo = mediaSource.addSourceBuffer(typeVideo);
+ var newDuration = 1.2;
+
+ sourceBufferAudio.appendWindowEnd = 2.0;
+ sourceBufferAudio.appendWindowStart = newDuration / 2.0;
+ sourceBufferAudio.appendBuffer(dataAudio);
+
+ sourceBufferVideo.appendWindowEnd = 2.0;
+ sourceBufferVideo.appendWindowStart = newDuration * 1.3;
+ sourceBufferVideo.appendBuffer(dataVideo);
+
+ test.expectEvent(sourceBufferAudio, "updateend");
+ test.expectEvent(sourceBufferVideo, "updateend");
+ test.waitForExpectedEvents(function()
+ {
+ assert_equals(sourceBufferAudio.buffered.length, 1);
+ assert_equals(sourceBufferVideo.buffered.length, 1);
+ assert_less_than(sourceBufferAudio.buffered.start(0), newDuration);
+ assert_greater_than(sourceBufferVideo.buffered.start(0), newDuration);
+ assert_throws("InvalidStateError", function () { mediaSource.duration = newDuration; });
+ test.done();
+ });
+ });
+ });
+ }, "Truncating the duration throws an InvalidStateError exception when new duration is less than the highest buffered range start time of one of the track buffers");
+
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ mediaElement.addEventListener("error", test.unreached_func("Unexpected event 'error'"));
+ MediaSourceUtil.fetchManifestAndData(test, manifestFilenameAudio, function(typeAudio, dataAudio)
+ {
+ MediaSourceUtil.fetchManifestAndData(test, manifestFilenameVideo, function(typeVideo, dataVideo)
+ {
+ var sourceBufferAudio = mediaSource.addSourceBuffer(typeAudio);
+ var sourceBufferVideo = mediaSource.addSourceBuffer(typeVideo);
+
+ // Buffer audio [0.8,1.8)
+ sourceBufferAudio.timestampOffset = 0.8;
+ sourceBufferAudio.appendWindowEnd = 1.8;
+ sourceBufferAudio.appendBuffer(dataAudio);
+
+ // Buffer video [1.5,3)
+ sourceBufferVideo.timestampOffset = 1.5;
+ sourceBufferVideo.appendWindowEnd = 3;
+ sourceBufferVideo.appendBuffer(dataVideo);
+
+ test.expectEvent(sourceBufferAudio, "updateend");
+ test.expectEvent(sourceBufferVideo, "updateend");
+ test.waitForExpectedEvents(function()
+ {
+ var newDuration = 2.0;
+
+ // Verify the test setup
+ assert_equals(sourceBufferAudio.buffered.length, 1);
+ assert_equals(sourceBufferVideo.buffered.length, 1);
+ assert_greater_than(sourceBufferAudio.buffered.end(0), 1.5);
+ assert_less_than(sourceBufferAudio.buffered.end(0), newDuration);
+ assert_less_than(sourceBufferVideo.buffered.start(0), newDuration);
+ assert_greater_than(sourceBufferVideo.buffered.end(0), newDuration + 0.5);
+
+ // Verify the expected error
+ // We assume relocated test video has at least one coded
+ // frame presentation interval which fits in [>2.0,>2.5)
+ assert_throws("InvalidStateError", function () { mediaSource.duration = newDuration; });
+ test.done();
+ });
+ });
+ });
+ }, "Truncating the duration throws an InvalidStateError exception when new duration is less than a buffered coded frame presentation time");
+
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ assert_less_than(segmentInfo.duration, 60, 'Sufficient test media duration');
+ sourceBuffer.appendBuffer(mediaData);
+ test.expectEvent(sourceBuffer, 'updateend', 'Media data appended to the SourceBuffer');
+ test.waitForExpectedEvents(function()
+ {
+ mediaSource.duration = 60;
+ assert_false(sourceBuffer.updating, 'No SourceBuffer update when duration is increased');
+ test.done();
+ });
+ }, 'Increasing the duration does not trigger any SourceBuffer update');
+
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ assert_greater_than(segmentInfo.duration, 2, 'Sufficient test media duration');
+ mediaElement.play();
+ sourceBuffer.appendBuffer(mediaData);
+ test.expectEvent(sourceBuffer, 'updateend', 'Media data appended to the SourceBuffer');
+ test.waitForExpectedEvents(function()
+ {
+ mediaSource.duration = 60;
+ assert_false(sourceBuffer.updating, 'No SourceBuffer update when duration is increased');
+ test.done();
+ });
+ }, 'Increasing the duration during media playback does not trigger any SourceBuffer update');
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/media-source/mediasource-endofstream-invaliderror.html b/testing/web-platform/tests/media-source/mediasource-endofstream-invaliderror.html
new file mode 100644
index 000000000..0327e44cc
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mediasource-endofstream-invaliderror.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
+<html>
+ <head>
+ <title>Invalid MediaSource.endOfStream() parameter test cases.</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="mediasource-util.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ mediaElement.addEventListener('error', test.unreached_func("Unexpected event 'error'"));
+ assert_equals(mediaSource.readyState, 'open');
+
+ assert_throws(new TypeError(),
+ function() { mediaSource.endOfStream('garbage'); },
+ 'endOfStream(\'garbage\') throws TypeError');
+
+ assert_equals(mediaSource.readyState, 'open');
+ test.done();
+ }, 'Test MediaSource.endOfStream() with invalid non-empty error string.');
+
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ mediaElement.addEventListener('error', test.unreached_func("Unexpected event 'error'"));
+ assert_equals(mediaSource.readyState, 'open');
+
+ assert_throws(new TypeError(),
+ function() { mediaSource.endOfStream(''); },
+ 'endOfStream(\'\') throws TypeError');
+
+ assert_equals(mediaSource.readyState, 'open');
+ test.done();
+ }, 'Test MediaSource.endOfStream() with invalid empty error string.');
+
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ mediaElement.addEventListener('error', test.unreached_func("Unexpected event 'error'"));
+ assert_equals(mediaSource.readyState, 'open');
+
+ assert_throws(new TypeError(),
+ function() { mediaSource.endOfStream(null); },
+ 'endOfStream(null) throws TypeError');
+
+ assert_equals(mediaSource.readyState, 'open');
+ test.done();
+ }, 'Test MediaSource.endOfStream() with invalid null error parameter.');
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/media-source/mediasource-endofstream.html b/testing/web-platform/tests/media-source/mediasource-endofstream.html
new file mode 100644
index 000000000..bca80af81
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mediasource-endofstream.html
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Calls to MediaSource.endOfStream() without error</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="mediasource-util.js"></script>
+<script>
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ mediaSource.duration = 2;
+ mediaSource.endOfStream();
+ assert_equals(mediaSource.duration, 0);
+ test.done();
+ }, 'MediaSource.endOfStream(): duration truncated to 0 when there are no buffered coded frames');
+
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ sourceBuffer.appendBuffer(mediaData);
+ test.expectEvent(sourceBuffer, 'updateend',
+ 'Media buffer appended to SourceBuffer');
+ test.waitForExpectedEvents(function()
+ {
+ mediaSource.endOfStream();
+ test.expectEvent(mediaElement, 'canplaythrough',
+ 'Media element may render the media content until the end');
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_equals(mediaElement.readyState, HTMLMediaElement.HAVE_ENOUGH_DATA,
+ 'Media element has enough data to render the content');
+ test.done();
+ });
+ }, 'MediaSource.endOfStream(): media element notified that it now has all of the media data');
+
+ function threeDecimalPlaces(number)
+ {
+ return Number(number.toFixed(3));
+ }
+
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ sourceBuffer.appendBuffer(mediaData);
+ test.expectEvent(sourceBuffer, 'updateend',
+ 'Media buffer appended to SourceBuffer');
+ test.waitForExpectedEvents(function()
+ {
+ assert_equals(sourceBuffer.buffered.length, 1,
+ 'Media data properly buffered');
+ var highestEndTime = sourceBuffer.buffered.end(0);
+
+ // Note that segmentInfo.duration is expected to also be the
+ // highest track buffer range end time. Therefore, endOfStream() should
+ // not change duration with this media.
+ assert_equals(threeDecimalPlaces(segmentInfo.duration), threeDecimalPlaces(mediaSource.duration));
+ assert_less_than_equal(highestEndTime, mediaSource.duration,
+ 'Media duration may be slightly longer than intersected track buffered ranges');
+
+ // Set the duration even higher, then confirm that endOfStream() drops it back to be
+ // the highest track buffer range end time.
+ mediaSource.duration += 10;
+ mediaSource.endOfStream();
+
+ assert_equals(sourceBuffer.buffered.length, 1,
+ 'Media data properly buffered after endOfStream');
+
+ assert_equals(threeDecimalPlaces(segmentInfo.duration), threeDecimalPlaces(mediaSource.duration));
+ assert_less_than_equal(highestEndTime, mediaSource.duration,
+ 'Media duration may be slightly longer than intersected track buffered ranges');
+ assert_equals(sourceBuffer.buffered.end(0), mediaSource.duration,
+ 'After endOfStream(), highest buffered range end time must be the highest track buffer range end time');
+
+ test.done();
+ });
+ }, 'MediaSource.endOfStream(): duration and buffered range end time before and after endOfStream');
+</script>
diff --git a/testing/web-platform/tests/media-source/mediasource-errors.html b/testing/web-platform/tests/media-source/mediasource-errors.html
new file mode 100644
index 000000000..3df1dedcc
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mediasource-errors.html
@@ -0,0 +1,234 @@
+<!DOCTYPE html>
+<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="mediasource-util.js"></script>
+<script>
+ function ErrorTest(testFunction, description)
+ {
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ var segmentInfo = MediaSourceUtil.SEGMENT_INFO;
+
+ if (!segmentInfo) {
+ assert_unreached("No segment info compatible with this MediaSource implementation.");
+ return;
+ }
+
+ var sourceBuffer = mediaSource.addSourceBuffer(segmentInfo.type);
+ MediaSourceUtil.loadBinaryData(test, segmentInfo.url, function(mediaData)
+ {
+ testFunction(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData);
+ });
+ }, description);
+ }
+
+ ErrorTest(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ var mediaSegment = MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.media[0]);
+
+ test.expectEvent(sourceBuffer, "error", "sourceBuffer error.");
+ test.expectEvent(sourceBuffer, "updateend", "mediaSegment append ended.");
+ test.expectEvent(mediaElement, "error", "mediaElement error.");
+ test.expectEvent(mediaSource, "sourceended", "mediaSource ended.");
+ test.expectEvent(mediaSource, "sourceclose", "mediaSource closed.");
+ sourceBuffer.appendBuffer(mediaSegment);
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_true(mediaElement.error != null);
+ assert_equals(mediaElement.error.code, MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED);
+
+ assert_equals(mediaSource.sourceBuffers.length, 0);
+ assert_equals(mediaSource.readyState, "closed");
+ test.done();
+ });
+ }, "Appending media segment before the first initialization segment.");
+
+ ErrorTest(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ assert_equals(mediaElement.readyState, HTMLMediaElement.HAVE_NOTHING);
+
+ // Fail if the append error algorithm occurs, since the decode
+ // error will be provided by us directly via endOfStream().
+ sourceBuffer.addEventListener("error", test.unreached_func("'error' should not be fired on sourceBuffer"));
+
+ test.expectEvent(mediaElement, "error", "mediaElement error.");
+ test.expectEvent(mediaSource, "sourceended", "mediaSource ended.");
+ test.expectEvent(mediaSource, "sourceclose", "mediaSource closed.");
+
+ mediaSource.endOfStream("decode");
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_true(mediaElement.error != null);
+ assert_equals(mediaElement.error.code, MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED);
+
+ assert_equals(mediaSource.sourceBuffers.length, 0);
+ assert_equals(mediaSource.readyState, "closed");
+
+ // Give a short time for a broken implementation to errantly fire
+ // "error" on sourceBuffer.
+ test.step_timeout(test.step_func_done(), 0);
+ });
+ }, "Signaling 'decode' error via endOfStream() before initialization segment has been appended.");
+
+ ErrorTest(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ assert_equals(mediaElement.readyState, HTMLMediaElement.HAVE_NOTHING);
+
+ // Fail if the append error algorithm occurs, since the network
+ // error will be provided by us directly via endOfStream().
+ sourceBuffer.addEventListener("error", test.unreached_func("'error' should not be fired on sourceBuffer"));
+
+ test.expectEvent(mediaElement, "error", "mediaElement error.");
+ test.expectEvent(mediaSource, "sourceended", "mediaSource ended.");
+ test.expectEvent(mediaSource, "sourceclose", "mediaSource closed.");
+
+ mediaSource.endOfStream("network");
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_true(mediaElement.error != null);
+ assert_equals(mediaElement.error.code, MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED);
+
+ assert_equals(mediaSource.sourceBuffers.length, 0);
+ assert_equals(mediaSource.readyState, "closed");
+
+ // Give a short time for a broken implementation to errantly fire
+ // "error" on sourceBuffer.
+ test.step_timeout(test.step_func_done(), 0);
+ });
+ }, "Signaling 'network' error via endOfStream() before initialization segment has been appended.");
+
+ ErrorTest(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ var initSegment = MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.init);
+ assert_equals(mediaElement.readyState, HTMLMediaElement.HAVE_NOTHING);
+
+ // Fail if the append error algorithm occurs, since the decode
+ // error will be provided by us directly via endOfStream().
+ sourceBuffer.addEventListener("error", test.unreached_func("'error' should not be fired on sourceBuffer"));
+
+ test.expectEvent(sourceBuffer, "updateend", "mediaSegment append ended.");
+ test.expectEvent(mediaElement, "loadedmetadata", "mediaElement metadata.");
+ sourceBuffer.appendBuffer(initSegment);
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_equals(mediaElement.readyState, HTMLMediaElement.HAVE_METADATA);
+
+ test.expectEvent(mediaElement, "error", "mediaElement error.");
+ test.expectEvent(mediaSource, "sourceended", "mediaSource ended.");
+ mediaSource.endOfStream("decode");
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_true(mediaElement.error != null);
+ assert_equals(mediaElement.error.code, MediaError.MEDIA_ERR_DECODE);
+ assert_equals(mediaSource.readyState, "ended");
+
+ // Give a short time for a broken implementation to errantly fire
+ // "error" on sourceBuffer.
+ test.step_timeout(test.step_func_done(), 0);
+ });
+
+ }, "Signaling 'decode' error via endOfStream() after initialization segment has been appended and the HTMLMediaElement has reached HAVE_METADATA.");
+
+ ErrorTest(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ var initSegment = MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.init);
+ assert_equals(mediaElement.readyState, HTMLMediaElement.HAVE_NOTHING);
+
+ // Fail if the append error algorithm occurs, since the network
+ // error will be provided by us directly via endOfStream().
+ sourceBuffer.addEventListener("error", test.unreached_func("'error' should not be fired on sourceBuffer"));
+
+ test.expectEvent(sourceBuffer, "updateend", "mediaSegment append ended.");
+ test.expectEvent(mediaElement, "loadedmetadata", "mediaElement metadata.");
+ sourceBuffer.appendBuffer(initSegment);
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_equals(mediaElement.readyState, HTMLMediaElement.HAVE_METADATA);
+ test.expectEvent(mediaElement, "error", "mediaElement error.");
+ test.expectEvent(mediaSource, "sourceended", "mediaSource ended.");
+ mediaSource.endOfStream("network");
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_true(mediaElement.error != null);
+ assert_equals(mediaElement.error.code, MediaError.MEDIA_ERR_NETWORK);
+ assert_equals(mediaSource.readyState, "ended");
+
+ // Give a short time for a broken implementation to errantly fire
+ // "error" on sourceBuffer.
+ test.step_timeout(test.step_func_done(), 0);
+ });
+ }, "Signaling 'network' error via endOfStream() after initialization segment has been appended and the HTMLMediaElement has reached HAVE_METADATA.");
+
+ ErrorTest(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ assert_equals(mediaElement.readyState, HTMLMediaElement.HAVE_NOTHING);
+
+ var initSegment = MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.init);
+ test.expectEvent(sourceBuffer, "updateend", "mediaSegment append ended.");
+ test.expectEvent(mediaElement, "loadedmetadata", "mediaElement metadata.");
+ sourceBuffer.appendBuffer(initSegment);
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_equals(mediaElement.readyState, HTMLMediaElement.HAVE_METADATA);
+ var mediaSegment = MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.media[0]);
+ var index = segmentInfo.init.size + (mediaSegment.length - 1) / 2;
+ // Corrupt the media data from index of mediaData, so it can signal 'decode' error.
+ // Here use mediaSegment to replace the original mediaData[index, index + mediaSegment.length]
+ mediaData.set(mediaSegment, index);
+
+ test.expectEvent(sourceBuffer, "error", "sourceBuffer error.");
+ test.expectEvent(sourceBuffer, "updateend", "mediaSegment append ended.");
+ test.expectEvent(mediaElement, "error", "mediaElement error.");
+ test.expectEvent(mediaSource, "sourceended", "mediaSource ended.");
+ sourceBuffer.appendBuffer(mediaData);
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_true(mediaElement.error != null);
+ assert_equals(mediaElement.error.code, MediaError.MEDIA_ERR_DECODE);
+ test.done();
+ });
+ }, "Signaling 'decode' error via segment parser loop algorithm after initialization segment has been appended.");
+
+ ErrorTest(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ assert_equals(mediaElement.readyState, HTMLMediaElement.HAVE_NOTHING);
+
+ // Fail if the append error algorithm occurs, since the network
+ // error will be provided by us directly via endOfStream().
+ mediaElement.addEventListener("loadedmetadata", test.unreached_func("'loadedmetadata' should not be fired on mediaElement"));
+
+ var mediaSegment = MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.media[0]);
+ var index = segmentInfo.init.size + (mediaSegment.length - 1) / 2;
+ // Corrupt the media data from index of mediaData, so it can signal 'decode' error.
+ // Here use mediaSegment to replace the original mediaData[index, index + mediaSegment.length]
+ mediaData.set(mediaSegment, index);
+
+ test.expectEvent(sourceBuffer, "error", "sourceBuffer error.");
+ test.expectEvent(sourceBuffer, "updateend", "mediaSegment append ended.");
+ test.expectEvent(mediaElement, "error", "mediaElement error.");
+ test.expectEvent(mediaSource, "sourceended", "mediaSource ended.");
+ sourceBuffer.appendBuffer(mediaData);
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_equals(mediaElement.readyState, HTMLMediaElement.HAVE_NOTHING);
+ assert_true(mediaElement.error != null);
+ assert_equals(mediaElement.error.code, MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED);
+ test.done();
+ });
+ }, "Signaling 'decode' error via segment parser loop algorithm.");
+</script>
diff --git a/testing/web-platform/tests/media-source/mediasource-getvideoplaybackquality.html b/testing/web-platform/tests/media-source/mediasource-getvideoplaybackquality.html
new file mode 100644
index 000000000..f92fdbc3c
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mediasource-getvideoplaybackquality.html
@@ -0,0 +1,117 @@
+<!DOCTYPE html>
+<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
+<html>
+ <head>
+ <title>HTMLVideoElement.getVideoPlaybackQuality() test cases.</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="mediasource-util.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ var previousQuality = mediaElement.getVideoPlaybackQuality();
+ var timeUpdateCount = 0;
+ mediaElement.addEventListener("timeupdate", test.step_func(function (e)
+ {
+ var videoElement = e.target;
+ var newQuality = videoElement.getVideoPlaybackQuality();
+ var now = window.performance.now();
+
+ assert_not_equals(previousQuality, newQuality,
+ "New quality object is different from the previous one");
+ assert_greater_than(newQuality.creationTime, previousQuality.creationTime,
+ "creationTime increases monotonically");
+ assert_approx_equals(newQuality.creationTime, now, 100,
+ "creationTime roughly equals current time");
+
+ assert_greater_than_equal(newQuality.totalVideoFrames, 0, "totalVideoFrames >= 0");
+ assert_greater_than_equal(newQuality.totalVideoFrames, previousQuality.totalVideoFrames,
+ "totalVideoFrames increases monotonically");
+ assert_less_than(newQuality.totalVideoFrames, 300,
+ "totalVideoFrames should remain low as duration is less than 10s and framerate less than 30fps");
+
+ assert_greater_than_equal(newQuality.droppedVideoFrames, 0, "droppedVideoFrames >= 0");
+ assert_greater_than_equal(newQuality.droppedVideoFrames, previousQuality.droppedVideoFrames,
+ "droppedVideoFrames increases monotonically");
+ assert_less_than_equal(newQuality.droppedVideoFrames, newQuality.totalVideoFrames,
+ "droppedVideoFrames is only a portion of totalVideoFrames");
+
+ assert_greater_than_equal(newQuality.corruptedVideoFrames, 0, "corruptedVideoFrames >= 0");
+ assert_greater_than_equal(newQuality.corruptedVideoFrames, previousQuality.corruptedVideoFrames,
+ "corruptedVideoFrames increases monotonically");
+ assert_less_than_equal(newQuality.corruptedVideoFrames, newQuality.totalVideoFrames,
+ "corruptedVideoFrames is only a portion of totalVideoFrames");
+
+ previousQuality = newQuality;
+ timeUpdateCount++;
+ }));
+
+ mediaElement.addEventListener('error', test.unreached_func("Unexpected event 'error'"));
+
+ sourceBuffer.appendBuffer(mediaData);
+ test.expectEvent(sourceBuffer, 'updateend', 'sourceBuffer');
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_false(sourceBuffer.updating, "updating");
+ mediaSource.endOfStream();
+ assert_less_than(mediaSource.duration, 10, "duration");
+ mediaElement.play();
+ test.expectEvent(mediaElement, 'ended', 'mediaElement');
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_greater_than(timeUpdateCount, 2, "timeUpdateCount");
+ test.done();
+ });
+ }, "Test HTMLVideoElement.getVideoPlaybackQuality() with MediaSource API");
+
+
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ var previousQuality = mediaElement.getVideoPlaybackQuality();
+ var timeUpdateCount = 0;
+ var startTime = 0;
+ mediaElement.addEventListener("timeupdate", test.step_func(function (e)
+ {
+ var videoElement = e.target;
+ var newQuality = videoElement.getVideoPlaybackQuality();
+ var now = window.performance.now();
+
+ assert_greater_than_equal(newQuality.totalFrameDelay, 0, "totalFrameDelay >= 0");
+ assert_greater_than_equal(newQuality.totalFrameDelay, previousQuality.totalFrameDelay,
+ "totalFrameDelay increases monotonically");
+ assert_less_than(newQuality.totalFrameDelay, (now - startTime) / 1000,
+ "totalFrameDelay does not exceed the time elapsed since playback started");
+
+ previousQuality = newQuality;
+ timeUpdateCount++;
+ }));
+
+ mediaElement.addEventListener('error', test.unreached_func("Unexpected event 'error'"));
+
+ sourceBuffer.appendBuffer(mediaData);
+ test.expectEvent(sourceBuffer, 'updateend', 'sourceBuffer');
+ test.waitForExpectedEvents(function()
+ {
+ assert_false(sourceBuffer.updating, "updating");
+ mediaSource.endOfStream();
+ assert_less_than(mediaSource.duration, 10, "duration");
+ startTime = window.performance.now();
+ mediaElement.play();
+ test.expectEvent(mediaElement, 'ended', 'mediaElement');
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_greater_than(timeUpdateCount, 2, "timeUpdateCount");
+ test.done();
+ });
+ }, "Test the totalFrameDelay attribute of HTMLVideoElement.getVideoPlaybackQuality() with MediaSource API");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/media-source/mediasource-is-type-supported.html b/testing/web-platform/tests/media-source/mediasource-is-type-supported.html
new file mode 100644
index 000000000..985e20626
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mediasource-is-type-supported.html
@@ -0,0 +1,87 @@
+<!DOCTYPE html>
+<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
+<html>
+ <head>
+ <title>MediaSource.isTypeSupported() test cases.</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="mediasource-util.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ // Generate a distinct test for each type in types
+ function test_type_support(types, expectation, description)
+ {
+ for (var i = 0; i < types.length; ++i) {
+ test(function()
+ {
+ assert_equals(MediaSource.isTypeSupported(types[i]),
+ expectation, 'supported');
+ }, description + ' "' + types[i] + '"');
+ }
+ };
+
+ test_type_support([
+ 'video',
+ 'video/',
+ 'video/webm',
+ 'video/webm;',
+ 'video/webm;codecs',
+ 'video/webm;codecs=',
+ 'video/webm;codecs="',
+ 'video/webm;codecs=""',
+ 'video/webm;codecs=","',
+ '',
+ null
+ ], false, 'Test invalid MIME format');
+
+ test_type_support([
+ 'xxx',
+ 'text/html',
+ 'image/jpeg'
+ ], false, 'Test invalid MSE MIME media type');
+
+ test_type_support([
+ 'audio/webm;codecs="vp8"',
+ 'audio/mp4;codecs="avc1.4d001e"',
+ ], false, 'Test invalid mismatch between major type and codec ID');
+
+ test_type_support([
+ 'audio/mp4;codecs="vorbis"',
+ 'audio/webm;codecs="mp4a.40.2"',
+ 'video/mp4;codecs="vp8"',
+ 'video/webm;codecs="mp4a.40.2"',
+ 'video/mp4;codecs="vorbis"',
+ ], false, 'Test invalid mismatch between minor type and codec ID');
+
+ test_type_support([
+ 'audio/mp4;codecs="mp4a"',
+ 'audio/mp4;codecs="mp4a.40"',
+ 'audio/mp4;codecs="mp4a.40."',
+ 'audio/mp4;codecs="mp4a.67.3"'
+ ], false, 'Test invalid codec ID');
+
+ test_type_support([
+ 'video/webm;codecs="vp8"',
+ 'video/webm;codecs="vorbis"',
+ 'video/webm;codecs="vp8,vorbis"',
+ 'video/webm;codecs="vorbis, vp8"',
+ 'audio/webm;codecs="vorbis"',
+ 'AUDIO/WEBM;CODECS="vorbis"',
+ ], true, 'Test valid WebM type');
+
+ test_type_support([
+ 'video/mp4;codecs="avc1.4d001e"', // H.264 Main Profile level 3.0
+ 'video/mp4;codecs="avc1.42001e"', // H.264 Baseline Profile level 3.0
+ 'audio/mp4;codecs="mp4a.40.2"', // MPEG4 AAC-LC
+ 'audio/mp4;codecs="mp4a.40.5"', // MPEG4 HE-AAC
+ 'audio/mp4;codecs="mp4a.67"', // MPEG2 AAC-LC
+ 'video/mp4;codecs="mp4a.40.2"',
+ 'video/mp4;codecs="avc1.4d001e,mp4a.40.2"',
+ 'video/mp4;codecs="mp4a.40.2 , avc1.4d001e "',
+ 'video/mp4;codecs="avc1.4d001e,mp4a.40.5"',
+ ], true, 'Test valid MP4 type');
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/media-source/mediasource-liveseekable.html b/testing/web-platform/tests/media-source/mediasource-liveseekable.html
new file mode 100644
index 000000000..a2f05a9cd
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mediasource-liveseekable.html
@@ -0,0 +1,137 @@
+<!DOCTYPE html>
+<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
+<meta charset="utf-8">
+<title>Checks setting/clearing the live seekable range and HTMLMediaElement.seekable</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="mediasource-util.js"></script>
+<script>
+test(function(test)
+{
+ var mediaSource = new MediaSource();
+ assert_equals(mediaSource.readyState, "closed", "media source is closed.");
+ assert_throws("InvalidStateError", function() { mediaSource.setLiveSeekableRange(0, 1); });
+}, "setLiveSeekableRange throws an InvalidStateError exception if the readyState attribute is not 'open'");
+
+
+test(function(test)
+{
+ var mediaSource = new MediaSource();
+ assert_equals(mediaSource.readyState, "closed", "media source is closed.");
+ assert_throws("InvalidStateError", function() { mediaSource.clearLiveSeekableRange(); });
+}, "clearLiveSeekableRange throws an InvalidStateError exception if the readyState attribute is not 'open'");
+
+
+mediasource_test(function(test, mediaElement, mediaSource)
+{
+ mediaElement.addEventListener('error', test.unreached_func("Unexpected event 'error'"));
+ var mimetype = MediaSourceUtil.AUDIO_VIDEO_TYPE;
+ var sourceBuffer = mediaSource.addSourceBuffer(mimetype);
+ sourceBuffer.appendBuffer(new Uint8Array(0));
+ assert_true(sourceBuffer.updating, "Updating set when a buffer is appended.");
+ mediaSource.setLiveSeekableRange(0, 1);
+ test.done();
+}, "setLiveSeekableRange does not restrict to not currently updating");
+
+
+mediasource_test(function(test, mediaElement, mediaSource)
+{
+ mediaElement.addEventListener('error', test.unreached_func("Unexpected event 'error'"));
+ var mimetype = MediaSourceUtil.AUDIO_VIDEO_TYPE;
+ var sourceBuffer = mediaSource.addSourceBuffer(mimetype);
+ sourceBuffer.appendBuffer(new Uint8Array(0));
+ assert_true(sourceBuffer.updating, "Updating set when a buffer is appended.");
+ mediaSource.clearLiveSeekableRange();
+ test.done();
+}, "clearLiveSeekableRange does not restrict to not currently updating");
+
+
+mediasource_test(function(test, mediaElement, mediaSource)
+{
+ mediaElement.addEventListener('error', test.unreached_func("Unexpected event 'error'"));
+ assert_throws(new TypeError(), function() { mediaSource.setLiveSeekableRange(-1, 1); });
+ test.done();
+}, "setLiveSeekableRange throws a TypeError if start is negative");
+
+
+mediasource_test(function(test, mediaElement, mediaSource)
+{
+ mediaElement.addEventListener('error', test.unreached_func("Unexpected event 'error'"));
+ assert_throws(new TypeError(), function() { mediaSource.setLiveSeekableRange(2, 1); });
+ test.done();
+}, "setLiveSeekableRange throws a TypeError if start is greater than end");
+
+
+mediasource_test(function(test, mediaElement, mediaSource)
+{
+ mediaElement.addEventListener('error', test.unreached_func("Unexpected event 'error'"));
+ mediaSource.setLiveSeekableRange(0, 1);
+ test.done();
+}, "setLiveSeekableRange returns with no error when conditions are correct");
+
+
+mediasource_test(function(test, mediaElement, mediaSource)
+{
+ mediaElement.addEventListener('error', test.unreached_func("Unexpected event 'error'"));
+ mediaSource.clearLiveSeekableRange();
+ test.done();
+}, "clearLiveSeekableRange returns with no error when conditions are correct");
+
+
+mediasource_test(function(test, mediaElement, mediaSource)
+{
+ mediaSource.duration = +Infinity;
+ mediaSource.setLiveSeekableRange(1, 2);
+ assert_equals(mediaElement.seekable.length, 1,
+ 'The seekable attribute contains a single range.');
+ assertSeekableEquals(mediaElement, '{ [1.000, 2.000) }',
+ 'The seekable attribute returns the correct range.');
+
+ mediaSource.clearLiveSeekableRange();
+ assertSeekableEquals(mediaElement, '{ }',
+ 'The seekable attribute now returns an empty range.');
+ test.done();
+}, "HTMLMediaElement.seekable returns the live seekable range or an empty range if that range was cleared when nothing is buffered");
+
+
+mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+{
+ var initSegment = MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.init);
+ test.expectEvent(sourceBuffer, 'updateend', 'Init segment appended to SourceBuffer.');
+ sourceBuffer.appendBuffer(initSegment);
+ test.waitForExpectedEvents(function()
+ {
+ mediaSource.duration = +Infinity;
+ mediaSource.setLiveSeekableRange(40, 42);
+
+ // Append a segment that starts after 1s to ensure seekable
+ // won't use 0 as starting point.
+ var midSegment = MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.media[5]);
+ test.expectEvent(sourceBuffer, 'updateend');
+ sourceBuffer.appendBuffer(midSegment);
+ test.waitForExpectedEvents(function()
+ {
+ assert_equals(mediaElement.seekable.length, 1,
+ 'The seekable attribute contains a single range.');
+ assert_equals(mediaElement.buffered.length, 1,
+ 'The buffered attribute contains a single range.');
+ assert_not_equals(mediaElement.seekable.start(0), 0,
+ 'The range starts after 0.');
+ assert_equals(mediaElement.seekable.start(0), mediaElement.buffered.start(0),
+ 'The start time is the start time of the buffered range.');
+ assert_equals(mediaElement.seekable.end(0), 42,
+ 'The end time is the end time of the seekable range.');
+
+ mediaSource.clearLiveSeekableRange();
+ assert_equals(mediaElement.seekable.length, 1,
+ 'The seekable attribute contains a single range.');
+ assert_equals(mediaElement.seekable.start(0), 0,
+ 'The start time is now 0.');
+ assert_equals(mediaElement.seekable.end(0), mediaElement.buffered.end(0),
+ 'The end time is now the end time of the buffered range.');
+
+ test.done();
+ });
+ });
+}, 'HTMLMediaElement.seekable returns the union of the buffered range and the live seekable range, when set');
+</script>
diff --git a/testing/web-platform/tests/media-source/mediasource-multiple-attach.html b/testing/web-platform/tests/media-source/mediasource-multiple-attach.html
new file mode 100644
index 000000000..4a95a42e8
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mediasource-multiple-attach.html
@@ -0,0 +1,114 @@
+<!DOCTYPE html>
+<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
+<html>
+ <head>
+ <title>Test Attaching a MediaSource to multiple HTMLMediaElements.</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="mediasource-util.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ function twoMediaElementTest(testFunction, description)
+ {
+ media_test(function(test)
+ {
+ var firstMediaTag = document.createElement('video');
+ var secondMediaTag = document.createElement('video');
+ document.body.appendChild(firstMediaTag);
+ document.body.appendChild(secondMediaTag);
+
+ // Overload done() so that elements added to the document can be
+ // removed.
+ var removeMediaElements = true;
+ var oldTestDone = test.done.bind(test);
+ test.done = function()
+ {
+ if (removeMediaElements) {
+ document.body.removeChild(secondMediaTag);
+ document.body.removeChild(firstMediaTag);
+ removeMediaElements = false;
+ }
+ oldTestDone();
+ };
+
+ testFunction(test, firstMediaTag, secondMediaTag);
+ }, description);
+ }
+
+ twoMediaElementTest(function(test, firstMediaTag, secondMediaTag)
+ {
+ // When attachment of mediaSource to two MediaElements is done
+ // without an intervening stable state, exactly one of the two
+ // MediaElements should successfully attach, and the other one
+ // should get error event due to mediaSource already in 'open'
+ // readyState.
+ var mediaSource = new MediaSource();
+ var mediaSourceURL = URL.createObjectURL(mediaSource);
+ var gotSourceOpen = false;
+ var gotError = false;
+ var doneIfFinished = test.step_func(function()
+ {
+ if (gotSourceOpen && gotError)
+ test.done();
+ });
+ var errorHandler = test.step_func(function(e)
+ {
+ firstMediaTag.removeEventListener('error', errorHandler);
+ secondMediaTag.removeEventListener('error', errorHandler);
+
+ var eventTarget = e.target;
+ var otherTarget;
+ if (eventTarget == firstMediaTag) {
+ otherTarget = secondMediaTag;
+ } else {
+ assert_equals(eventTarget, secondMediaTag, 'Error target check');
+ otherTarget = firstMediaTag;
+ }
+
+ assert_true(eventTarget.error != null, 'Error state on one tag');
+ assert_equals(eventTarget.error.code, MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED, 'Expected error code');
+ assert_equals(otherTarget.error, null, 'No error on other tag');
+
+ assert_equals(eventTarget.networkState, HTMLMediaElement.NETWORK_NO_SOURCE,
+ 'Tag with error state networkState');
+ assert_equals(otherTarget.networkState, HTMLMediaElement.NETWORK_LOADING,
+ 'Tag without error state networkState');
+
+ gotError = true;
+ doneIfFinished();
+ });
+
+ test.expectEvent(mediaSource, 'sourceopen', 'An attachment succeeded');
+ firstMediaTag.addEventListener('error', errorHandler);
+ secondMediaTag.addEventListener('error', errorHandler);
+
+ firstMediaTag.src = mediaSourceURL;
+ secondMediaTag.src = mediaSourceURL;
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_equals(mediaSource.readyState, 'open', 'Source is opened');
+ gotSourceOpen = true;
+ doneIfFinished();
+ });
+ }, 'Test exactly one succeeds when two MediaElements attach to same MediaSource');
+
+ mediasource_test(function(test, mediaElement, mediaSource) {
+ assert_equals(mediaSource.readyState, 'open', 'Source open');
+ // Set the tag's src attribute. This should close mediaSource,
+ // reattach it to the tag, and initiate source reopening.
+ test.expectEvent(mediaSource, 'sourceopen', 'Source attached again');
+ mediaElement.src = URL.createObjectURL(mediaSource);
+ assert_equals(mediaSource.readyState, 'closed', 'Source closed');
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_equals(mediaSource.readyState, 'open', 'Source reopened');
+ test.done();
+ });
+ }, 'Test that MediaSource can reattach if closed first');
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/media-source/mediasource-play-then-seek-back.html b/testing/web-platform/tests/media-source/mediasource-play-then-seek-back.html
new file mode 100644
index 000000000..66fdbe810
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mediasource-play-then-seek-back.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
+<html>
+ <head>
+ <title>Simple MediaSource playback &amp; seek test case.</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="mediasource-util.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+
+ mediaElement.play();
+ // Append all the segments
+ test.expectEvent(sourceBuffer, 'updateend', 'sourceBuffer');
+ test.expectEvent(mediaElement, 'playing', 'Playing triggered');
+ sourceBuffer.appendBuffer(mediaData);
+
+ function confirmPlayThenEnd()
+ {
+ test.waitForCurrentTimeChange(mediaElement, function ()
+ {
+ assert_greater_than(mediaElement.currentTime, 0.0, 'Playback has started after seek.');
+ test.done();
+ });
+ }
+
+ function finishSeekThenPlay()
+ {
+ test.expectEvent(mediaElement, 'seeked', 'mediaElement finished seek');
+
+ test.waitForExpectedEvents(confirmPlayThenEnd);
+ }
+
+ function delayedPlayHandler()
+ {
+ assert_greater_than(mediaElement.currentTime, 0.0, 'Playback has started.');
+ test.expectEvent(mediaElement, 'seeking', 'mediaElement');
+ mediaElement.currentTime = 0.0;
+ assert_true(mediaElement.seeking, 'mediaElement is seeking');
+
+ test.waitForExpectedEvents(finishSeekThenPlay);
+ }
+
+ test.waitForExpectedEvents(function()
+ {
+ test.waitForCurrentTimeChange(mediaElement, delayedPlayHandler);
+ });
+
+ }, 'Test playing then seeking back.');
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/media-source/mediasource-play.html b/testing/web-platform/tests/media-source/mediasource-play.html
new file mode 100644
index 000000000..5bbfa29d7
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mediasource-play.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
+<html>
+ <head>
+ <title>Simple MediaSource playback test case.</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="mediasource-util.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ mediaElement.addEventListener('error', test.unreached_func("Unexpected event 'error'"));
+ mediaElement.addEventListener('ended', test.step_func_done());
+
+ test.expectEvent(sourceBuffer, 'updatestart', 'sourceBuffer');
+ test.expectEvent(sourceBuffer, 'update', 'sourceBuffer');
+ test.expectEvent(sourceBuffer, 'updateend', 'sourceBuffer');
+
+ sourceBuffer.appendBuffer(mediaData);
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_false(sourceBuffer.updating, "updating");
+
+ sourceBuffer.remove(1, Infinity);
+
+ assert_true(sourceBuffer.updating, "updating");
+ test.expectEvent(sourceBuffer, 'updatestart', 'sourceBuffer');
+ test.expectEvent(sourceBuffer, 'update', 'sourceBuffer');
+ test.expectEvent(sourceBuffer, 'updateend', 'sourceBuffer');
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_false(sourceBuffer.updating, "updating");
+ assert_greater_than(mediaSource.duration, 1, "duration");
+
+ mediaSource.duration = 1;
+
+ test.expectEvent(mediaElement, "durationchange");
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ mediaSource.endOfStream();
+ mediaElement.play();
+ });
+ }, "Test normal playback case with MediaSource API");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/media-source/mediasource-preload.html b/testing/web-platform/tests/media-source/mediasource-preload.html
new file mode 100644
index 000000000..e387b6373
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mediasource-preload.html
@@ -0,0 +1,72 @@
+<!DOCTYPE html>
+<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
+<html>
+ <head>
+ <title>Various MediaSource HTMLMediaElement preload tests.</title>
+ <link rel="author" title="Matthew Wolenetz" href="mailto:wolenetz@chromium.org"/>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <script>
+ function attachWithPreloadTest(preload)
+ {
+ async_test(function(test)
+ {
+ var video = document.createElement("video");
+ var mediaSource = new MediaSource();
+ var mediaSourceURL = URL.createObjectURL(mediaSource);
+
+ video.preload = preload;
+ document.body.appendChild(video);
+ test.add_cleanup(function() {
+ document.body.removeChild(video);
+ URL.revokeObjectURL(mediaSourceURL);
+ });
+
+ mediaSource.addEventListener("sourceopen", test.step_func_done());
+ video.src = mediaSourceURL;
+ }, "sourceopen occurs with element preload=" + preload);
+ }
+
+ attachWithPreloadTest("auto");
+ attachWithPreloadTest("metadata");
+ attachWithPreloadTest("none");
+
+ function errorWithPreloadTest(preload, bogusURLStyle)
+ {
+ async_test(function(test)
+ {
+ var mediaSource = new MediaSource();
+ var bogusURL = URL.createObjectURL(mediaSource);
+
+ if (bogusURLStyle == "corrupted") {
+ var goodURL = bogusURL;
+ test.add_cleanup(function() { URL.revokeObjectURL(goodURL); });
+ bogusURL += "0";
+ } else if (bogusURLStyle == "revoked") {
+ URL.revokeObjectURL(bogusURL);
+ } else {
+ assert_unreached("invalid case");
+ }
+
+ var video = document.createElement("video");
+ video.preload = preload;
+ document.body.appendChild(video);
+ test.add_cleanup(function() { document.body.removeChild(video); });
+
+ mediaSource.addEventListener("sourceopen", test.unreached_func("'sourceopen' should not be fired"));
+
+ video.onerror = test.step_func_done();
+ video.src = bogusURL;
+ }, "error occurs with bogus blob URL (" + bogusURLStyle + " MediaSource object URL) and element preload=" + preload);
+ }
+
+ errorWithPreloadTest("auto", "revoked");
+ errorWithPreloadTest("metadata", "revoked");
+
+ errorWithPreloadTest("auto", "corrupted");
+ errorWithPreloadTest("metadata", "corrupted");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/media-source/mediasource-redundant-seek.html b/testing/web-platform/tests/media-source/mediasource-redundant-seek.html
new file mode 100644
index 000000000..05eae9714
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mediasource-redundant-seek.html
@@ -0,0 +1,73 @@
+<!DOCTYPE html>
+<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
+<html>
+ <head>
+ <title>Test MediaSource behavior when receiving multiple seek requests during a pending seek.</title>
+ <meta name="timeout" content="long">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="mediasource-util.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ mediaElement.play();
+
+ // Append all media data for complete playback.
+ test.expectEvent(sourceBuffer, 'updateend', 'sourceBuffer end update.');
+ test.expectEvent(mediaElement, 'loadedmetadata', 'Reached HAVE_METADATA');
+ test.expectEvent(mediaElement, 'playing', 'Playing media.');
+ sourceBuffer.appendBuffer(mediaData);
+
+ test.waitForExpectedEvents(function()
+ {
+ var bufferedRanges = mediaElement.buffered;
+
+ assert_greater_than_equal(mediaElement.duration, 4.0, 'Duration is >= 4.0s');
+ assert_equals(bufferedRanges.length, 1, 'Just one buffered range');
+ assert_less_than_equal(bufferedRanges.start(0), 1.0, 'Buffered range starts <= 1.0s');
+ assert_greater_than_equal(bufferedRanges.end(0), 4.0, 'Buffered range ends >= 4.0s');
+
+ test.expectEvent(mediaElement, 'seeking', 'seeking');
+ test.expectEvent(mediaElement, 'timeupdate', 'timeupdate');
+ test.expectEvent(mediaElement, 'seeked', 'seeked');
+
+ // Request seeks.
+ mediaElement.currentTime = 1.0;
+
+ // This 'ephemeral' seek should be invisible to javascript, except any latency incurred in its processing.
+ mediaElement.currentTime = 3.0;
+
+ mediaElement.currentTime = 1.0;
+
+ assert_true(mediaElement.seeking, 'Element is seeking');
+ assert_equals(mediaElement.currentTime, 1.0, 'Element time is at last seek time');
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ // No more seeking or seeked events should occur.
+ mediaElement.addEventListener('seeking', test.unreached_func("Unexpected event 'seeking'"));
+ mediaElement.addEventListener('seeked', test.unreached_func("Unexpected event 'seeked'"));
+
+ assert_false(mediaElement.seeking, 'Element is not seeking');
+ assert_greater_than_equal(mediaElement.currentTime, 1.0, 'Element time is at or after last seek time');
+ assert_less_than(mediaElement.currentTime, 3.0, 'Element time is before the ephemeral seek time');
+
+ var timeBeforeWait = mediaElement.currentTime;
+ test.waitForCurrentTimeChange(mediaElement, function()
+ {
+ // Time should have advanced a little, but not yet reached the ephemeral seek time.
+ assert_greater_than(mediaElement.currentTime, timeBeforeWait, 'Element time has increased');
+ assert_less_than(mediaElement.currentTime, 3.0, 'Element time is still before the ephemeral seek time');
+ test.done();
+ });
+ });
+ }, 'Test redundant fully prebuffered seek');
+
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/media-source/mediasource-remove.html b/testing/web-platform/tests/media-source/mediasource-remove.html
new file mode 100644
index 000000000..9a4eb3fc5
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mediasource-remove.html
@@ -0,0 +1,324 @@
+<!DOCTYPE html>
+<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>SourceBuffer.remove() test cases.</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="mediasource-util.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ var sourceBuffer = mediaSource.addSourceBuffer(MediaSourceUtil.AUDIO_VIDEO_TYPE);
+
+ mediaSource.duration = 10;
+
+ assert_throws(new TypeError(), function()
+ {
+ sourceBuffer.remove(-1, 2);
+ }, "remove");
+
+ test.done();
+ }, "Test remove with an negative start.");
+
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ var sourceBuffer = mediaSource.addSourceBuffer(MediaSourceUtil.AUDIO_VIDEO_TYPE);
+
+ mediaSource.duration = 10;
+
+ [ undefined, NaN, Infinity, -Infinity ].forEach(function(item)
+ {
+ assert_throws(new TypeError(), function()
+ {
+ sourceBuffer.remove(item, 2);
+ }, "remove");
+ });
+
+ test.done();
+ }, "Test remove with non-finite start.");
+
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ var sourceBuffer = mediaSource.addSourceBuffer(MediaSourceUtil.AUDIO_VIDEO_TYPE);
+
+ mediaSource.duration = 10;
+
+ assert_throws(new TypeError(), function()
+ {
+ sourceBuffer.remove(11, 12);
+ }, "remove");
+
+ test.done();
+ }, "Test remove with a start beyond the duration.");
+
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ var sourceBuffer = mediaSource.addSourceBuffer(MediaSourceUtil.AUDIO_VIDEO_TYPE);
+
+ mediaSource.duration = 10;
+
+ assert_throws(new TypeError(), function()
+ {
+ sourceBuffer.remove(2, 1);
+ }, "remove");
+
+ test.done();
+ }, "Test remove with a start larger than the end.");
+
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ var sourceBuffer = mediaSource.addSourceBuffer(MediaSourceUtil.AUDIO_VIDEO_TYPE);
+
+ mediaSource.duration = 10;
+
+ assert_throws(new TypeError(), function()
+ {
+ sourceBuffer.remove(0, Number.NEGATIVE_INFINITY);
+ }, "remove");
+
+ test.done();
+ }, "Test remove with a NEGATIVE_INFINITY end.");
+
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ var sourceBuffer = mediaSource.addSourceBuffer(MediaSourceUtil.AUDIO_VIDEO_TYPE);
+
+ mediaSource.duration = 10;
+
+ assert_throws(new TypeError(), function()
+ {
+ sourceBuffer.remove(0, Number.NaN);
+ }, "remove");
+
+ test.done();
+ }, "Test remove with a NaN end.");
+
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ var sourceBuffer = mediaSource.addSourceBuffer(MediaSourceUtil.AUDIO_VIDEO_TYPE);
+
+ mediaSource.duration = 10;
+
+ mediaSource.removeSourceBuffer(sourceBuffer);
+
+ assert_throws("InvalidStateError", function()
+ {
+ sourceBuffer.remove(1, 2);
+ }, "remove");
+
+ test.done();
+ }, "Test remove after SourceBuffer removed from mediaSource.");
+
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ var sourceBuffer = mediaSource.addSourceBuffer(MediaSourceUtil.AUDIO_VIDEO_TYPE);
+
+ assert_false(sourceBuffer.updating, "updating is false");
+ assert_equals(mediaSource.duration, NaN, "duration isn't set");
+
+ assert_throws(new TypeError(), function()
+ {
+ sourceBuffer.remove(0, 0);
+ }, "remove");
+
+ test.done();
+ }, "Test remove with a NaN duration.");
+
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ var sourceBuffer = mediaSource.addSourceBuffer(MediaSourceUtil.AUDIO_VIDEO_TYPE);
+
+ mediaSource.duration = 10;
+
+ test.expectEvent(sourceBuffer, "updatestart");
+ test.expectEvent(sourceBuffer, "update");
+ test.expectEvent(sourceBuffer, "updateend");
+ sourceBuffer.remove(1, 2);
+
+ assert_true(sourceBuffer.updating, "updating");
+
+ assert_throws("InvalidStateError", function()
+ {
+ sourceBuffer.remove(3, 4);
+ }, "remove");
+
+ test.waitForExpectedEvents(function()
+ {
+ test.done();
+ });
+ }, "Test remove while update pending.");
+
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ var sourceBuffer = mediaSource.addSourceBuffer(MediaSourceUtil.AUDIO_VIDEO_TYPE);
+
+ mediaSource.duration = 10;
+
+ test.expectEvent(sourceBuffer, "updatestart");
+ test.expectEvent(sourceBuffer, "update");
+ test.expectEvent(sourceBuffer, "updateend");
+ sourceBuffer.remove(1, 2);
+
+ assert_true(sourceBuffer.updating, "updating");
+
+ assert_throws("InvalidStateError", function()
+ {
+ sourceBuffer.abort();
+ }, "abort");
+
+ assert_true(sourceBuffer.updating, "updating");
+
+ test.waitForExpectedEvents(function()
+ {
+ test.done();
+ });
+ }, "Test aborting a remove operation.");
+
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ sourceBuffer.appendBuffer(mediaData);
+
+ test.expectEvent(sourceBuffer, "updatestart");
+ test.expectEvent(sourceBuffer, "update");
+ test.expectEvent(sourceBuffer, "updateend");
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_less_than(mediaSource.duration, 10)
+
+ mediaSource.duration = 10;
+
+ sourceBuffer.remove(mediaSource.duration, mediaSource.duration + 2);
+
+ assert_true(sourceBuffer.updating, "updating");
+ test.expectEvent(sourceBuffer, "updatestart");
+ test.expectEvent(sourceBuffer, "update");
+ test.expectEvent(sourceBuffer, "updateend");
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ test.done();
+ });
+
+ }, "Test remove with a start at the duration.");
+
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ test.expectEvent(sourceBuffer, "updatestart");
+ test.expectEvent(sourceBuffer, "update");
+ test.expectEvent(sourceBuffer, "updateend");
+ sourceBuffer.appendBuffer(mediaData);
+
+ test.waitForExpectedEvents(function()
+ {
+ mediaSource.endOfStream();
+
+ assert_equals(mediaSource.readyState, "ended");
+
+ test.expectEvent(sourceBuffer, "updatestart");
+ test.expectEvent(sourceBuffer, "update");
+ test.expectEvent(sourceBuffer, "updateend");
+ test.expectEvent(mediaSource, "sourceopen");
+ sourceBuffer.remove(1, 2);
+
+ assert_true(sourceBuffer.updating, "updating");
+ assert_equals(mediaSource.readyState, "open");
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_false(sourceBuffer.updating, "updating");
+ test.done();
+ });
+ }, "Test remove transitioning readyState from 'ended' to 'open'.");
+
+ function removeAppendedDataTests(callback, description)
+ {
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ test.expectEvent(sourceBuffer, "updatestart");
+ test.expectEvent(sourceBuffer, "update");
+ test.expectEvent(sourceBuffer, "updateend");
+ sourceBuffer.appendBuffer(mediaData);
+
+ test.waitForExpectedEvents(function()
+ {
+ mediaSource.endOfStream();
+ assert_false(sourceBuffer.updating, "updating");
+
+ var start = Math.max(segmentInfo.media[0].timev, segmentInfo.media[0].timea).toFixed(3);
+ var duration = mediaElement.duration.toFixed(3);
+ var subType = MediaSourceUtil.getSubType(segmentInfo.type);
+
+ assertBufferedEquals(sourceBuffer, "{ [" + start + ", " + duration + ") }", "Initial buffered range.");
+ callback(test, mediaSource, sourceBuffer, duration, subType, segmentInfo);
+ });
+ }, description);
+ };
+ function removeAndCheckBufferedRanges(test, mediaSource, sourceBuffer, start, end, expected)
+ {
+ test.expectEvent(sourceBuffer, "updatestart");
+ test.expectEvent(sourceBuffer, "update");
+ test.expectEvent(sourceBuffer, "updateend");
+ sourceBuffer.remove(start, end);
+
+ test.waitForExpectedEvents(function()
+ {
+ mediaSource.endOfStream();
+ assert_false(sourceBuffer.updating, "updating");
+
+ assertBufferedEquals(sourceBuffer, expected, "Buffered ranges after remove().");
+ test.done();
+ });
+ }
+
+ removeAppendedDataTests(function(test, mediaSource, sourceBuffer, duration, subType, segmentInfo)
+ {
+ removeAndCheckBufferedRanges(test, mediaSource, sourceBuffer, 0, Number.POSITIVE_INFINITY, "{ }");
+ }, "Test removing all appended data.");
+
+ removeAppendedDataTests(function(test, mediaSource, sourceBuffer, duration, subType, segmentInfo)
+ {
+ var expectations = {
+ webm: ("{ [3.315, " + duration + ") }"),
+ mp4: ("{ [3.298, " + duration + ") }"),
+ };
+
+ // Note: Range doesn't start exactly at the end of the remove range because there isn't
+ // a keyframe there. The resulting range starts at the first keyframe >= the end time.
+ removeAndCheckBufferedRanges(test, mediaSource, sourceBuffer, 0, 3, expectations[subType]);
+ }, "Test removing beginning of appended data.");
+
+ removeAppendedDataTests(function(test, mediaSource, sourceBuffer, duration, subType, segmentInfo)
+ {
+ var start = Math.max(segmentInfo.media[0].timev, segmentInfo.media[0].timea).toFixed(3);
+ var expectations = {
+ webm: ("{ [" + start + ", 1.005) [3.315, " + duration + ") }"),
+ mp4: ("{ [" + start + ", 0.997) [3.298, " + duration + ") }"),
+ };
+
+ // Note: The first resulting range ends slightly after start because the removal algorithm only removes
+ // frames with a timestamp >= the start time. If a frame starts before and ends after the remove() start
+ // timestamp, then it stays in the buffer.
+ removeAndCheckBufferedRanges(test, mediaSource, sourceBuffer, 1, 3, expectations[subType]);
+ }, "Test removing the middle of appended data.");
+
+ removeAppendedDataTests(function(test, mediaSource, sourceBuffer, duration, subType, segmentInfo)
+ {
+ var start = Math.max(segmentInfo.media[0].timev, segmentInfo.media[0].timea).toFixed(3);
+ var expectations = {
+ webm: "{ [" + start + ", 1.013) }",
+ mp4: "{ [" + start + ", 1.022) }",
+ };
+
+ removeAndCheckBufferedRanges(test, mediaSource, sourceBuffer, 1, Number.POSITIVE_INFINITY, expectations[subType]);
+ }, "Test removing the end of appended data.");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/media-source/mediasource-removesourcebuffer.html b/testing/web-platform/tests/media-source/mediasource-removesourcebuffer.html
new file mode 100644
index 000000000..038856532
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mediasource-removesourcebuffer.html
@@ -0,0 +1,146 @@
+<!DOCTYPE html>
+<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
+<html>
+ <head>
+ <title>MediaSource.removeSourceBuffer() test cases.</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="mediasource-util.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ var sourceBuffer = mediaSource.addSourceBuffer(MediaSourceUtil.AUDIO_VIDEO_TYPE);
+ assert_class_string(sourceBuffer, "SourceBuffer", "New SourceBuffer returned");
+
+ mediaSource.removeSourceBuffer(sourceBuffer);
+
+ var sourceBuffer2 = mediaSource.addSourceBuffer(MediaSourceUtil.AUDIO_VIDEO_TYPE);
+ assert_class_string(sourceBuffer2, "SourceBuffer", "New SourceBuffer returned");
+ assert_not_equals(sourceBuffer, sourceBuffer2, "SourceBuffers are different instances.");
+ assert_equals(mediaSource.sourceBuffers.length, 1, "sourceBuffers.length == 1");
+
+ test.done();
+ }, "Test addSourceBuffer(), removeSourceBuffer(), addSourceBuffer() sequence.");
+
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ assert_throws(new TypeError(),
+ function() { mediaSource.removeSourceBuffer(null); },
+ "removeSourceBuffer() threw an exception when passed null.");
+ test.done();
+ }, "Test removeSourceBuffer() with null");
+
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ var sourceBuffer = mediaSource.addSourceBuffer(MediaSourceUtil.AUDIO_VIDEO_TYPE);
+ assert_class_string(sourceBuffer, "SourceBuffer", "New SourceBuffer returned");
+
+ mediaSource.removeSourceBuffer(sourceBuffer);
+
+ assert_throws("NotFoundError",
+ function() { mediaSource.removeSourceBuffer(sourceBuffer); },
+ "removeSourceBuffer() threw an exception for a SourceBuffer that was already removed.");
+
+ test.done();
+ }, "Test calling removeSourceBuffer() twice with the same object.");
+
+ mediasource_test(function(test, mediaElement1, mediaSource1)
+ {
+ var sourceBuffer1 = mediaSource1.addSourceBuffer(MediaSourceUtil.AUDIO_ONLY_TYPE);
+ assert_class_string(sourceBuffer1, "SourceBuffer", "New SourceBuffer returned");
+
+ var mediaElement2 = document.createElement("video");
+ document.body.appendChild(mediaElement2);
+ test.add_cleanup(function() { document.body.removeChild(mediaElement2); });
+
+ var mediaSource2 = new MediaSource();
+ var mediaSource2URL = URL.createObjectURL(mediaSource2);
+ mediaElement2.src = mediaSource2URL;
+ test.expectEvent(mediaSource2, "sourceopen", "Second MediaSource opened");
+ test.waitForExpectedEvents(function()
+ {
+ URL.revokeObjectURL(mediaSource2URL);
+
+ var sourceBuffer2 = mediaSource2.addSourceBuffer(MediaSourceUtil.VIDEO_ONLY_TYPE);
+ assert_class_string(sourceBuffer2, "SourceBuffer", "Second new SourceBuffer returned");
+ assert_not_equals(mediaSource1, mediaSource2, "MediaSources are different instances");
+ assert_not_equals(sourceBuffer1, sourceBuffer2, "SourceBuffers are different instances");
+ assert_equals(mediaSource1.sourceBuffers[0], sourceBuffer1);
+ assert_equals(mediaSource2.sourceBuffers[0], sourceBuffer2);
+ assert_throws("NotFoundError",
+ function() { mediaSource1.removeSourceBuffer(sourceBuffer2); },
+ "MediaSource1.removeSourceBuffer() threw an exception for SourceBuffer2");
+ assert_throws("NotFoundError",
+ function() { mediaSource2.removeSourceBuffer(sourceBuffer1); },
+ "MediaSource2.removeSourceBuffer() threw an exception for SourceBuffer1");
+ mediaSource1.removeSourceBuffer(sourceBuffer1);
+ mediaSource2.removeSourceBuffer(sourceBuffer2);
+ test.done();
+ });
+ }, "Test calling removeSourceBuffer() for a sourceBuffer belonging to a different mediaSource instance.");
+
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ var sourceBuffer = mediaSource.addSourceBuffer(MediaSourceUtil.AUDIO_VIDEO_TYPE);
+ assert_class_string(sourceBuffer, "SourceBuffer", "New SourceBuffer returned");
+
+ mediaSource.endOfStream();
+ assert_equals(mediaSource.readyState, "ended", "MediaSource in ended state");
+ mediaSource.removeSourceBuffer(sourceBuffer);
+
+ assert_equals(mediaSource.sourceBuffers.length, 0, "MediaSource.sourceBuffers is empty");
+ assert_equals(mediaSource.activeSourceBuffers.length, 0, "MediaSource.activesourceBuffers is empty");
+
+ test.done();
+ }, "Test calling removeSourceBuffer() in ended state.");
+
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ var initSegment = MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.init);
+
+ test.expectEvent(sourceBuffer, "updateend", "initSegment append ended.");
+ test.expectEvent(mediaElement, "loadedmetadata", "loadedmetadata done.");
+ sourceBuffer.appendBuffer(initSegment);
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_equals(mediaSource.sourceBuffers.length, 1, "MediaSource.sourceBuffers is not empty");
+ assert_equals(mediaSource.activeSourceBuffers.length, 1, "MediaSource.activesourceBuffers is not empty");
+ assert_equals(mediaElement.readyState, mediaElement.HAVE_METADATA);
+ assert_equals(mediaSource.duration, segmentInfo.duration);
+ test.expectEvent(mediaSource.activeSourceBuffers, "removesourcebuffer", "SourceBuffer removed from activeSourceBuffers.");
+ test.expectEvent(mediaSource.sourceBuffers, "removesourcebuffer", "SourceBuffer removed.");
+ mediaSource.removeSourceBuffer(sourceBuffer);
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_equals(mediaSource.sourceBuffers.length, 0, "MediaSource.sourceBuffers is empty");
+ assert_equals(mediaSource.activeSourceBuffers.length, 0, "MediaSource.activesourceBuffers is empty");
+ test.done();
+ });
+ }, "Test removesourcebuffer event on activeSourceBuffers.");
+
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ mediaElement.addEventListener('error', test.unreached_func("Unexpected event 'error'"));
+ var mimetype = MediaSourceUtil.AUDIO_VIDEO_TYPE;
+ var sourceBuffer = mediaSource.addSourceBuffer(mimetype);
+ sourceBuffer.appendBuffer(new Uint8Array(0));
+ assert_true(sourceBuffer.updating, "Updating flag set when a buffer is appended.");
+ test.expectEvent(sourceBuffer, 'abort');
+ test.expectEvent(sourceBuffer, 'updateend');
+
+ mediaSource.removeSourceBuffer(sourceBuffer);
+ assert_false(sourceBuffer.updating, "Updating flag reset after abort.");
+ test.waitForExpectedEvents(function()
+ {
+ test.done();
+ });
+ }, "Test abort event when removeSourceBuffer() called while SourceBuffer is updating");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/media-source/mediasource-seek-beyond-duration.html b/testing/web-platform/tests/media-source/mediasource-seek-beyond-duration.html
new file mode 100644
index 000000000..8b07c9f80
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mediasource-seek-beyond-duration.html
@@ -0,0 +1,105 @@
+<!DOCTYPE html>
+<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
+<html>
+ <head>
+ <title>Test MediaSource behavior when seeking beyond the duration of the clip.</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="mediasource-util.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+
+ function seekToSpecifiedTimeSetEOSAndVerifyDone(test, mediaElement, mediaSource, seekToTime)
+ {
+ assert_less_than(mediaElement.currentTime, mediaElement.duration, 'Not at the end yet.');
+ test.expectEvent(mediaElement, 'seeking', 'mediaElement seeking');
+ // Seek to specified time.
+ mediaElement.currentTime = seekToTime;
+ if (seekToTime >= mediaSource.duration) {
+ assert_equals(mediaElement.currentTime, mediaSource.duration, 'Current time equals duration.');
+ } else {
+ assert_equals(mediaElement.currentTime, seekToTime, 'Current time equals specified seek time.');
+ }
+
+ test.waitForExpectedEvents(function()
+ {
+ test.expectEvent(mediaElement, 'timeupdate', 'mediaElement time updated.');
+ test.expectEvent(mediaElement, 'seeked', 'mediaElement seeked');
+ test.expectEvent(mediaElement, 'ended', 'mediaElement ended.');
+ test.expectEvent(mediaSource, 'sourceended', 'mediaSource ended.');
+ mediaSource.endOfStream();
+ assert_true(mediaElement.seeking, 'mediaElement seeking.');
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_equals(mediaElement.currentTime, mediaSource.duration, 'Current time equals duration.');
+ test.done();
+ });
+ };
+
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ mediaElement.play();
+ var initSegment = MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.init);
+
+ // Append the initialization segment to trigger a transition to HAVE_METADATA.
+ test.expectEvent(sourceBuffer, 'updateend', 'sourceBuffer end update.');
+ test.expectEvent(mediaElement, 'loadedmetadata', 'Reached HAVE_METADATA');
+ sourceBuffer.appendBuffer(initSegment);
+
+ test.waitForExpectedEvents(function()
+ {
+ // Add sufficient segments to have at least 2s of play-time.
+ var playbackData = MediaSourceUtil.getMediaDataForPlaybackTime(mediaData, segmentInfo, 2.0);
+ test.expectEvent(sourceBuffer, 'updateend', 'sourceBuffer');
+ test.expectEvent(mediaElement, 'playing', 'Playing media.');
+ sourceBuffer.appendBuffer(playbackData);
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_equals(mediaElement.duration, segmentInfo.duration);
+ assert_greater_than_equal(mediaElement.duration, 2.0, 'Duration is >2.0s.');
+
+ test.expectEvent(sourceBuffer, "updateend");
+ sourceBuffer.remove(1.5, Infinity);
+ assert_true(sourceBuffer.updating, "updating");
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_false(sourceBuffer.updating, "updating");
+ test.waitForCurrentTimeChange(mediaElement, function()
+ {
+ // Update duration.
+ mediaSource.duration = 1.5;
+ seekToSpecifiedTimeSetEOSAndVerifyDone(test, mediaElement, mediaSource, 1.8);
+ });
+ });
+ }, 'Test seeking beyond updated media duration.');
+
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ mediaElement.play();
+
+ // Append all media data for complete playback.
+ test.expectEvent(sourceBuffer, 'updateend', 'sourceBuffer end update.');
+ test.expectEvent(mediaElement, 'loadedmetadata', 'Reached HAVE_METADATA');
+ test.expectEvent(mediaElement, 'playing', 'Playing media.');
+ sourceBuffer.appendBuffer(mediaData);
+
+ test.waitForExpectedEvents(function()
+ {
+ test.waitForCurrentTimeChange(mediaElement, function()
+ {
+ seekToSpecifiedTimeSetEOSAndVerifyDone(test, mediaElement, mediaSource, mediaSource.duration, mediaSource.duration + 0.1);
+ });
+ });
+
+ }, 'Test seeking beyond media duration.');
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/media-source/mediasource-seek-during-pending-seek.html b/testing/web-platform/tests/media-source/mediasource-seek-during-pending-seek.html
new file mode 100644
index 000000000..60c5eec1c
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mediasource-seek-during-pending-seek.html
@@ -0,0 +1,189 @@
+<!DOCTYPE html>
+<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
+<html>
+ <head>
+ <title>Test MediaSource behavior when a seek is requested while another seek is pending.</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="mediasource-util.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ mediaElement.play();
+
+ var initSegment = MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.init);
+ var firstSegment = MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.media[0]);
+ var segmentIndex = 2;
+ var secondSegmentInfo = segmentInfo.media[segmentIndex];
+
+ // Append the initialization segment to trigger a transition to HAVE_METADATA.
+ test.expectEvent(sourceBuffer, 'updateend', 'sourceBuffer');
+ test.expectEvent(mediaElement, 'loadedmetadata', 'Reached HAVE_METADATA');
+ sourceBuffer.appendBuffer(initSegment);
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_false(mediaElement.seeking, 'mediaElement is not seeking');
+ assert_equals(mediaElement.readyState, mediaElement.HAVE_METADATA, 'Still in HAVE_METADATA');
+
+ // Seek to a new position before letting the initial seek to 0 completes.
+ test.expectEvent(mediaElement, 'seeking', 'mediaElement');
+ mediaElement.currentTime = Math.max(secondSegmentInfo.timev, secondSegmentInfo.timea);
+ assert_true(mediaElement.seeking, 'mediaElement is seeking');
+
+ // Append media data for time 0.
+ test.expectEvent(sourceBuffer, 'updateend', 'sourceBuffer');
+ sourceBuffer.appendBuffer(firstSegment);
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ // Verify that the media data didn't trigger a 'seeking' event or a transition beyond HAVE_METADATA.
+ assert_true(mediaElement.seeking, 'mediaElement is still seeking');
+ assert_equals(mediaElement.readyState, mediaElement.HAVE_METADATA, 'Still in HAVE_METADATA');
+
+ // Append media data for the current position until the element starts playing.
+ test.expectEvent(mediaElement, 'seeked', 'mediaElement finished seek');
+ test.expectEvent(mediaElement, 'playing', 'mediaElement playing');
+
+ MediaSourceUtil.appendUntilEventFires(test, mediaElement, 'playing', sourceBuffer, mediaData, segmentInfo, segmentIndex);
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ if (sourceBuffer.updating)
+ {
+ // The event playing was fired prior to the appendBuffer completing.
+ test.expectEvent(sourceBuffer, 'updateend', 'sourceBuffer');
+ test.waitForExpectedEvents(function()
+ {
+ assert_false(sourceBuffer.updating, 'append have compleded');
+ test.expectEvent(mediaSource, 'sourceended', 'mediaSource ended');
+ mediaSource.endOfStream();
+ });
+ }
+ else
+ {
+ test.expectEvent(mediaSource, 'sourceended', 'mediaSource ended');
+ mediaSource.endOfStream();
+ }
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ // Note: we just completed the seek. However, we only have less than a second worth of data to play. It is possible that
+ // playback has reached the end since the seek completed.
+ if (!mediaElement.paused)
+ {
+ assert_greater_than_equal(mediaElement.readyState, mediaElement.HAVE_CURRENT_DATA, 'Greater or equal than HAVE_CURRENT_DATA');
+ }
+ else
+ {
+ assert_true(mediaElement.ended);
+ }
+ test.done();
+ });
+
+ }, 'Test seeking to a new location before transitioning beyond HAVE_METADATA.');
+
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ mediaElement.play();
+
+ var initSegment = MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.init);
+ var firstSegment = MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.media[0]);
+ var secondSegmentInfo = segmentInfo.media[2];
+ var secondSegment = MediaSourceUtil.extractSegmentData(mediaData, secondSegmentInfo);
+ var segmentIndex = 4;
+ var thirdSegmentInfo = segmentInfo.media[segmentIndex];
+
+ // Append the initialization segment to trigger a transition to HAVE_METADATA.
+ test.expectEvent(sourceBuffer, 'updateend', 'sourceBuffer');
+ test.expectEvent(mediaElement, 'loadedmetadata', 'Reached HAVE_METADATA');
+ sourceBuffer.appendBuffer(initSegment);
+
+ test.waitForExpectedEvents(function()
+ {
+ test.expectEvent(sourceBuffer, 'updateend', 'sourceBuffer');
+ test.expectEvent(mediaElement, 'playing', 'mediaElement playing');
+ sourceBuffer.appendBuffer(firstSegment);
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_greater_than(mediaElement.readyState, mediaElement.HAVE_CURRENT_DATA, 'Greater than HAVE_CURRENT_DATA');
+
+ // Seek to a new position.
+ test.expectEvent(mediaElement, 'seeking', 'mediaElement');
+ mediaElement.currentTime = Math.max(secondSegmentInfo.timev, secondSegmentInfo.timea);
+ assert_true(mediaElement.seeking, 'mediaElement is seeking');
+
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_true(mediaElement.seeking, 'mediaElement is still seeking');
+
+ // Seek to a second position while the first seek is still pending.
+ test.expectEvent(mediaElement, 'seeking', 'mediaElement');
+ mediaElement.currentTime = Math.max(thirdSegmentInfo.timev, thirdSegmentInfo.timea);
+ assert_true(mediaElement.seeking, 'mediaElement is seeking');
+
+ // Append media data for the first seek position.
+ test.expectEvent(sourceBuffer, 'updateend', 'sourceBuffer');
+ sourceBuffer.appendBuffer(secondSegment);
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ // Note that we can't assume that the element is still seeking
+ // when the seeking event is fired as the operation is asynchronous.
+
+ // Append media data for the second seek position.
+ test.expectEvent(mediaElement, 'seeked', 'mediaElement finished seek');
+ MediaSourceUtil.appendUntilEventFires(test, mediaElement, 'seeked', sourceBuffer, mediaData, segmentInfo, segmentIndex);
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_false(mediaElement.seeking, 'mediaElement is no longer seeking');
+
+ if (sourceBuffer.updating)
+ {
+ // The event seeked was fired prior to the appendBuffer completing.
+ test.expectEvent(sourceBuffer, 'updateend', 'sourceBuffer');
+ test.waitForExpectedEvents(function()
+ {
+ assert_false(sourceBuffer.updating, 'append have compleded');
+ test.expectEvent(mediaSource, 'sourceended', 'mediaSource ended');
+ mediaSource.endOfStream();
+ });
+ }
+ else
+ {
+ test.expectEvent(mediaSource, 'sourceended', 'mediaSource ended');
+ mediaSource.endOfStream();
+ }
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ // Note: we just completed the seek. However, we only have less than a second worth of data to play. It is possible that
+ // playback has reached the end since the seek completed.
+ if (!mediaElement.paused)
+ {
+ assert_greater_than_equal(mediaElement.readyState, mediaElement.HAVE_CURRENT_DATA, 'Greater or equal than HAVE_CURRENT_DATA');
+ }
+ else
+ {
+ assert_true(mediaElement.ended);
+ }
+ test.done();
+ });
+ }, 'Test seeking to a new location during a pending seek.');
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/media-source/mediasource-seekable.html b/testing/web-platform/tests/media-source/mediasource-seekable.html
new file mode 100644
index 000000000..c379a63ba
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mediasource-seekable.html
@@ -0,0 +1,64 @@
+<!DOCTYPE html>
+<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="mediasource-util.js"></script>
+<script>
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ mediaElement.addEventListener('error', test.unreached_func("Unexpected event 'error'"));
+ mediaElement.addEventListener('ended', test.step_func_done(function () {}));
+
+ var sourceBuffer = mediaSource.addSourceBuffer(MediaSourceUtil.AUDIO_ONLY_TYPE);
+
+ assertSeekableEquals(mediaElement, '{ }', 'mediaElement.seekable');
+ test.done();
+ }, 'Get seekable time ranges when the sourcebuffer is empty.');
+
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ var initSegment = MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.init);
+ test.expectEvent(mediaElement, 'durationchange', 'mediaElement got duration');
+ sourceBuffer.appendBuffer(initSegment);
+ test.waitForExpectedEvents(function()
+ {
+ assert_equals(mediaElement.duration, segmentInfo.duration);
+ assertSeekableEquals(mediaElement, '{ [0.000, ' + segmentInfo.duration.toFixed(3) + ') }', 'mediaElement.seekable');
+ test.done();
+ });
+ }, 'Get seekable time ranges after init segment received.');
+
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ var initSegment = MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.init);
+ test.expectEvent(mediaElement, 'durationchange', 'mediaElement got duration after initsegment');
+ sourceBuffer.appendBuffer(initSegment);
+ test.waitForExpectedEvents(function()
+ {
+ test.expectEvent(mediaElement, 'durationchange', 'mediaElement got infinity duration');
+ mediaSource.duration = Infinity;
+ test.waitForExpectedEvents(function()
+ {
+ assertSeekableEquals(mediaElement, '{ }', 'mediaElement.seekable');
+
+ // Append a segment from the middle of the stream to make sure that seekable does not use buffered.start(0) or duration as first or last value
+ var midSegment = MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.media[2]);
+ test.expectEvent(sourceBuffer, 'update');
+ test.expectEvent(sourceBuffer, 'updateend');
+ sourceBuffer.appendBuffer(midSegment);
+ test.waitForExpectedEvents(function()
+ {
+ assert_equals(mediaElement.seekable.length, 1, 'mediaElement.seekable.length');
+ assert_equals(mediaElement.buffered.length, 1, 'mediaElement.buffered.length');
+ assert_not_equals(mediaElement.seekable.start(0), mediaElement.buffered.start(0));
+ assert_equals(mediaElement.seekable.start(0), 0);
+ assert_not_equals(mediaElement.seekable.end(0), mediaElement.duration);
+ assert_not_equals(mediaElement.seekable.end(0), mediaElement.buffered.start(0));
+ assert_equals(mediaElement.seekable.end(0), mediaElement.buffered.end(0));
+ test.done();
+ });
+ });
+ });
+ }, 'Get seekable time ranges on an infinite stream.');
+</script>
diff --git a/testing/web-platform/tests/media-source/mediasource-sequencemode-append-buffer.html b/testing/web-platform/tests/media-source/mediasource-sequencemode-append-buffer.html
new file mode 100644
index 000000000..92a01abcc
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mediasource-sequencemode-append-buffer.html
@@ -0,0 +1,140 @@
+<!DOCTYPE html>
+<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
+<html>
+ <head>
+ <title>SourceBuffer.mode == "sequence" test cases.</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="mediasource-util.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ function mediasource_sequencemode_test(testFunction, description, options)
+ {
+ return mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ assert_greater_than(segmentInfo.media.length, 3, "at least 3 media segments for supported type");
+ mediaElement.addEventListener("error", test.unreached_func("Unexpected event 'error'"));
+ sourceBuffer.mode = "sequence";
+ assert_equals(sourceBuffer.mode, "sequence", "mode after setting it to \"sequence\"");
+
+ var initSegment = MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.init);
+ test.expectEvent(sourceBuffer, "updatestart", "initSegment append started.");
+ test.expectEvent(sourceBuffer, "update", "initSegment append success.");
+ test.expectEvent(sourceBuffer, "updateend", "initSegment append ended.");
+ sourceBuffer.appendBuffer(initSegment);
+ test.waitForExpectedEvents(function()
+ {
+ assert_equals(sourceBuffer.timestampOffset, 0, "timestampOffset initially 0");
+ testFunction(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData);
+ });
+ }, description, options);
+ }
+
+ function append_segment(test, sourceBuffer, mediaData, info, callback)
+ {
+ var mediaSegment = MediaSourceUtil.extractSegmentData(mediaData, info);
+ test.expectEvent(sourceBuffer, "updatestart", "media segment append started.");
+ test.expectEvent(sourceBuffer, "update", "media segment append success.");
+ test.expectEvent(sourceBuffer, "updateend", "media segment append ended.");
+ sourceBuffer.appendBuffer(mediaSegment);
+ test.waitForExpectedEvents(callback);
+ }
+
+ function threeDecimalPlaces(number)
+ {
+ return Number(number.toFixed(3));
+ }
+
+ // Verifies expected times to 3 decimal places before and after mediaSource.endOfStream(),
+ // and calls |callback| on success.
+ function verify_offset_and_buffered(test, mediaSource, sourceBuffer,
+ expectedTimestampOffset, expectedBufferedRangeStartTime,
+ expectedBufferedRangeMaxEndTimeBeforeEOS,
+ expectedBufferedRangeEndTimeAfterEOS,
+ callback) {
+ assert_equals(threeDecimalPlaces(sourceBuffer.timestampOffset),
+ threeDecimalPlaces(expectedTimestampOffset),
+ "expectedTimestampOffset");
+
+ // Prior to EOS, the buffered range end time may not have fully reached the next media
+ // segment's timecode (adjusted by any timestampOffset). It should not exceed it though.
+ // Therefore, an exact assertBufferedEquals() will not work here.
+ assert_greater_than(sourceBuffer.buffered.length, 0, "sourceBuffer.buffered has at least 1 range before EOS");
+ assert_equals(threeDecimalPlaces(sourceBuffer.buffered.start(0)),
+ threeDecimalPlaces(expectedBufferedRangeStartTime),
+ "sourceBuffer.buffered range begins where expected before EOS");
+ assert_less_than_equal(threeDecimalPlaces(sourceBuffer.buffered.end(0)),
+ threeDecimalPlaces(expectedBufferedRangeMaxEndTimeBeforeEOS),
+ "sourceBuffer.buffered range ends at or before expected upper bound before EOS");
+
+ test.expectEvent(mediaSource, "sourceended", "mediaSource endOfStream");
+ mediaSource.endOfStream();
+ test.waitForExpectedEvents(function()
+ {
+ assertBufferedEquals(sourceBuffer,
+ "{ [" + expectedBufferedRangeStartTime.toFixed(3) + ", " + expectedBufferedRangeEndTimeAfterEOS.toFixed(3) + ") }",
+ "sourceBuffer.buffered after EOS");
+ callback();
+ });
+ }
+
+ mediasource_sequencemode_test(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ var offset = Math.min(segmentInfo.media[0].timev, segmentInfo.media[0].timea);
+ var expectedStart = Math.max(segmentInfo.media[0].timev, segmentInfo.media[0].timea) - offset;
+ var expectedEnd = Math.min(segmentInfo.media[0].endtimev, segmentInfo.media[0].endtimea) - offset;
+ var expectedEndEOS = Math.max(segmentInfo.media[0].endtimev, segmentInfo.media[0].endtimea) - offset;
+ append_segment(test, sourceBuffer, mediaData, segmentInfo.media[0], function()
+ {
+ verify_offset_and_buffered(test, mediaSource, sourceBuffer,
+ -offset, expectedStart,
+ expectedEnd, expectedEndEOS,
+ test.done);
+ });
+ }, "Test sequence AppendMode appendBuffer(first media segment)");
+
+ mediasource_sequencemode_test(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ var offset = Math.min(segmentInfo.media[1].timev, segmentInfo.media[1].timea);
+ var expectedStart = Math.max(segmentInfo.media[1].timev, segmentInfo.media[1].timea) - offset;
+ var expectedEnd = Math.min(segmentInfo.media[1].endtimev, segmentInfo.media[1].endtimea) - offset;
+ var expectedEndEOS = Math.max(segmentInfo.media[1].endtimev, segmentInfo.media[1].endtimea) - offset;
+ assert_greater_than(Math.min(segmentInfo.media[1].timev, segmentInfo.media[1].timea), 0,
+ "segment starts after time 0");
+ append_segment(test, sourceBuffer, mediaData, segmentInfo.media[1], function()
+ {
+ verify_offset_and_buffered(test, mediaSource, sourceBuffer,
+ -offset, expectedStart,
+ expectedEnd, expectedEndEOS,
+ test.done);
+ });
+ }, "Test sequence AppendMode appendBuffer(second media segment)");
+
+ mediasource_sequencemode_test(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ assert_greater_than(Math.min(segmentInfo.media[1].timev, segmentInfo.media[1].timea), 0,
+ "segment starts after time 0");
+ append_segment(test, sourceBuffer, mediaData, segmentInfo.media[1], function()
+ {
+ append_segment(test, sourceBuffer, mediaData, segmentInfo.media[0], function()
+ {
+ var firstOffset = Math.min(segmentInfo.media[1].timev, segmentInfo.media[1].timea);
+ var secondOffset = Math.max(segmentInfo.media[1].endtimev, segmentInfo.media[1].endtimea) - firstOffset;
+ var expectedStart = Math.max(segmentInfo.media[1].timev, segmentInfo.media[1].timea) - firstOffset;
+ var expectedEnd = Math.min(segmentInfo.media[0].endtimev, segmentInfo.media[0].endtimea) + secondOffset;
+ var expectedEndEOS = Math.max(segmentInfo.media[0].endtimev, segmentInfo.media[0].endtimea) + secondOffset;
+ // Current timestampOffset should reflect offset required to put media[0]
+ // immediately after media[1]'s highest frame end timestamp (as was adjusted
+ // by an earlier timestampOffset).
+ verify_offset_and_buffered(test, mediaSource, sourceBuffer,
+ secondOffset, expectedStart,
+ expectedEnd, expectedEndEOS,
+ test.done);
+ })
+ });
+ }, "Test sequence AppendMode appendBuffer(second media segment, then first media segment)");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/media-source/mediasource-sourcebuffer-mode-timestamps.html b/testing/web-platform/tests/media-source/mediasource-sourcebuffer-mode-timestamps.html
new file mode 100644
index 000000000..c5816968b
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mediasource-sourcebuffer-mode-timestamps.html
@@ -0,0 +1,52 @@
+<!doctype html>
+<html>
+<head>
+ <meta charset='utf-8'>
+ <title>SourceBuffer#mode with generate timestamps flag true</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<div id="log"></div>
+
+<script>
+var mimes = ['audio/aac', 'audio/mpeg'];
+
+//check the browser supports the MIME used in this test
+function isTypeSupported(mime) {
+ if(!MediaSource.isTypeSupported(mime)) {
+ this.step(function() {
+ assert_unreached("Browser doesn't support the MIME used in this test: " + mime);
+ });
+ this.done();
+ return false;
+ }
+ return true;
+}
+function mediaTest(mime) {
+ async_test(function(t) {
+ if(!isTypeSupported.bind(t)(mime)) {
+ return;
+ }
+ var mediaSource = new MediaSource();
+ mediaSource.addEventListener('sourceopen', t.step_func_done(function(e) {
+ var sourceBuffer = mediaSource.addSourceBuffer(mime);
+ assert_equals(sourceBuffer.updating, false, "SourceBuffer.updating is false");
+ assert_throws({name: 'TypeError'},
+ function() {
+ sourceBuffer.mode = "segments";
+ },
+ 'SourceBuffer#mode with generate timestamps flag true');
+ }), false);
+ var video = document.createElement('video');
+ video.src = window.URL.createObjectURL(mediaSource);
+ }, mime + ' : ' +
+ 'If generate timestamps flag equals true and new mode equals "segments", ' +
+ 'then throw a TypeError exception and abort these steps.');
+}
+mimes.forEach(function(mime) {
+ mediaTest(mime);
+});
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/media-source/mediasource-sourcebuffer-mode.html b/testing/web-platform/tests/media-source/mediasource-sourcebuffer-mode.html
new file mode 100644
index 000000000..521fa7f92
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mediasource-sourcebuffer-mode.html
@@ -0,0 +1,142 @@
+<!DOCTYPE html>
+<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>SourceBuffer.mode test cases.</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="mediasource-util.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ assert_equals(sourceBuffer.mode, 'segments', 'default append mode should be \'segments\'');
+ test.done();
+ }, 'Test initial value of SourceBuffer.mode is "segments"');
+
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ sourceBuffer.mode = 'sequence';
+ assert_equals(sourceBuffer.mode, 'sequence', 'mode after setting it to \'sequence\'');
+
+ // Setting a mode that is not in AppendMode IDL enum should be ignored and not cause exception.
+ sourceBuffer.mode = 'invalidmode';
+ sourceBuffer.mode = null;
+ sourceBuffer.mode = '';
+ sourceBuffer.mode = 'Segments';
+ assert_equals(sourceBuffer.mode, 'sequence', 'mode unchanged by attempts to set invalid modes');
+
+ sourceBuffer.mode = 'segments';
+ assert_equals(sourceBuffer.mode, 'segments', 'mode after setting it to \'segments\'');
+ test.done();
+ }, 'Test setting SourceBuffer.mode');
+
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ mediaSource.removeSourceBuffer(sourceBuffer);
+ assert_throws('InvalidStateError',
+ function() { sourceBuffer.mode = 'segments'; },
+ 'Setting valid sourceBuffer.mode on removed SourceBuffer should throw InvalidStateError.');
+ test.done();
+ }, 'Test setting a removed SourceBuffer\'s mode');
+
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ sourceBuffer.appendBuffer(mediaData);
+ assert_true(sourceBuffer.updating, 'updating attribute is true');
+ assert_throws('InvalidStateError',
+ function() { sourceBuffer.mode = 'segments'; },
+ 'Setting valid sourceBuffer.mode on updating SourceBuffer threw InvalidStateError.');
+ test.done();
+ }, 'Test setting SourceBuffer.mode while still updating');
+
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ test.expectEvent(sourceBuffer, 'updateend', 'Append ended.');
+ sourceBuffer.appendBuffer(mediaData);
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_false(sourceBuffer.updating, 'updating attribute is false');
+
+ test.expectEvent(mediaSource, 'sourceended', 'MediaSource sourceended event');
+ mediaSource.endOfStream();
+ assert_equals(mediaSource.readyState, 'ended', 'MediaSource readyState is \'ended\'');
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_equals(mediaSource.readyState, 'ended', 'MediaSource readyState is \'ended\'');
+
+ test.expectEvent(mediaSource, 'sourceopen', 'MediaSource sourceopen event');
+ sourceBuffer.mode = 'segments';
+
+ assert_equals(mediaSource.readyState, 'open', 'MediaSource readyState is \'open\'');
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_equals(mediaSource.readyState, 'open', 'MediaSource readyState is \'open\'');
+ test.done();
+ });
+ }, 'Test setting SourceBuffer.mode triggers parent MediaSource \'ended\' to \'open\' transition.');
+
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ var initSegment = MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.init);
+ var fullMediaSegment = MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.media[0]);
+ var truncateAt = Math.floor(segmentInfo.media[0].size * 0.5); // Pick first 50% of segment bytes.
+ var partialMediaSegment = fullMediaSegment.subarray(0, truncateAt);
+ var mediaSegmentRemainder = fullMediaSegment.subarray(truncateAt);
+
+ // Append init segment.
+ test.expectEvent(sourceBuffer, 'updateend', 'Init segment append ended.');
+ sourceBuffer.appendBuffer(initSegment);
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_false(sourceBuffer.updating, 'updating attribute is false');
+ assert_equals(sourceBuffer.mode, 'segments');
+ sourceBuffer.mode = 'segments'; // No exception should occur.
+ assert_equals(sourceBuffer.timestampOffset, 0.0);
+ sourceBuffer.timestampOffset = 10.123456789; // No exception should occur.
+ assert_equals(sourceBuffer.timestampOffset, 10.123456789); // Super-precise offsets should round-trip.
+
+ // Append first part of media segment.
+ test.expectEvent(sourceBuffer, 'updateend', 'Partial media segment append ended.');
+ sourceBuffer.appendBuffer(partialMediaSegment);
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_false(sourceBuffer.updating, 'updating attribute is false');
+ assert_equals(sourceBuffer.mode, 'segments');
+ assert_throws('InvalidStateError',
+ function() { sourceBuffer.mode = 'segments'; },
+ 'Setting valid sourceBuffer.mode while still parsing media segment threw InvalidStateError.');
+ assert_equals(sourceBuffer.timestampOffset, 10.123456789);
+ assert_throws('InvalidStateError',
+ function() { sourceBuffer.timestampOffset = 20.0; },
+ 'Setting valid sourceBuffer.timestampOffset while still parsing media segment threw InvalidStateError.');
+
+ // Append remainder of media segment.
+ test.expectEvent(sourceBuffer, 'updateend', 'Append ended of remainder of media segment.');
+ sourceBuffer.appendBuffer(mediaSegmentRemainder);
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_false(sourceBuffer.updating, 'updating attribute is false');
+ assert_equals(sourceBuffer.mode, 'segments');
+ sourceBuffer.mode = 'segments'; // No exception should occur.
+ assert_equals(sourceBuffer.timestampOffset, 10.123456789);
+ sourceBuffer.timestampOffset = 20.0; // No exception should occur.
+ test.done();
+ });
+ }, 'Test setting SourceBuffer.mode and SourceBuffer.timestampOffset while parsing media segment.');
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/media-source/mediasource-sourcebuffer-trackdefaults.html b/testing/web-platform/tests/media-source/mediasource-sourcebuffer-trackdefaults.html
new file mode 100644
index 000000000..0eb9d2643
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mediasource-sourcebuffer-trackdefaults.html
@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="mediasource-util.js"></script>
+<script>
+ function sourceBufferTrackDefaultsTest(callback, description)
+ {
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ var sourceBuffer = mediaSource.addSourceBuffer(MediaSourceUtil.AUDIO_VIDEO_TYPE);
+ assert_array_equals(sourceBuffer.trackDefaults, [], "Empty initial SourceBuffer.trackDefaults");
+ callback(test, mediaElement, mediaSource, sourceBuffer);
+ }, description);
+ };
+
+ sourceBufferTrackDefaultsTest(function(test, mediaElement, mediaSource, sourceBuffer)
+ {
+ var emptyList = new TrackDefaultList([]);
+ assert_not_equals(sourceBuffer.trackDefaults, emptyList, "Initial trackDefaults object differs from new empty list");
+
+ sourceBuffer.trackDefaults = emptyList;
+
+ assert_array_equals(sourceBuffer.trackDefaults, [], "Round-tripped empty trackDefaults");
+ assert_equals(sourceBuffer.trackDefaults, emptyList, "Round-tripped the empty TrackDefaultList object");
+ test.done();
+ }, "Test round-trip of empty SourceBuffer.trackDefaults");
+
+ sourceBufferTrackDefaultsTest(function(test, mediaElement, mediaSource, sourceBuffer)
+ {
+ var trackDefault = new TrackDefault("audio", "en-US", "audio label", ["main"], "1");
+ var trackDefaults = new TrackDefaultList([ trackDefault ]);
+
+ sourceBuffer.trackDefaults = trackDefaults;
+
+ assert_array_equals(sourceBuffer.trackDefaults, trackDefaults, "Round-tripped non-empty trackDefaults");
+ assert_equals(sourceBuffer.trackDefaults.length, 1, "Confirmed non-empty trackDefaults");
+ assert_equals(sourceBuffer.trackDefaults, trackDefaults, "Round-tripped the non-empty TrackDefaultList object");
+ test.done();
+ }, "Test round-trip of non-empty SourceBuffer.trackDefaults");
+
+ sourceBufferTrackDefaultsTest(function(test, mediaElement, mediaSource, sourceBuffer)
+ {
+ mediaSource.removeSourceBuffer(sourceBuffer);
+ assert_throws("InvalidStateError",
+ function() { sourceBuffer.trackDefaults = new TrackDefaultList([]); },
+ "Exception thrown when setting trackDefaults on SourceBuffer that is removed from MediaSource");
+ test.done();
+ }, "Test setting trackDefaults on an already-removed SourceBuffer");
+
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ assert_array_equals(sourceBuffer.trackDefaults, [], "Empty initial SourceBuffer.trackDefaults");
+ test.expectEvent(sourceBuffer, "updateend", "Append ended");
+ sourceBuffer.appendBuffer(mediaData);
+ assert_true(sourceBuffer.updating, "SourceBuffer is updating");
+
+ assert_throws("InvalidStateError",
+ function() { sourceBuffer.trackDefaults = new TrackDefaultList([]); },
+ "Exception thrown when setting trackDefaults on SourceBuffer that is updating");
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_false(sourceBuffer.updating, "SourceBuffer is not updating");
+ sourceBuffer.trackDefaults = new TrackDefaultList([]);
+ test.done();
+ });
+ }, "Test setting trackDefaults on a SourceBuffer that is updating");
+
+ sourceBufferTrackDefaultsTest(function(test, mediaElement, mediaSource, sourceBuffer)
+ {
+ assert_throws(new TypeError(),
+ function() { sourceBuffer.trackDefaults = null; },
+ "null should be disallowed by trackDefaults setter");
+ test.done();
+ }, "Test setting null SourceBuffer.trackDefaults");
+</script>
diff --git a/testing/web-platform/tests/media-source/mediasource-sourcebufferlist.html b/testing/web-platform/tests/media-source/mediasource-sourcebufferlist.html
new file mode 100644
index 000000000..760e6df46
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mediasource-sourcebufferlist.html
@@ -0,0 +1,97 @@
+<!DOCTYPE html>
+<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
+<html>
+ <head>
+ <title>SourceBufferList test cases.</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="mediasource-util.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ function verifySourceBufferLists(mediaSource, expected)
+ {
+ assert_equals(mediaSource.sourceBuffers.length, expected.length, "sourceBuffers length");
+ assert_equals(mediaSource.activeSourceBuffers.length, 0, "activeSourceBuffers length");
+ for (var i = 0; i < expected.length; ++i) {
+ assert_equals(mediaSource.sourceBuffers[i], expected[i], "Verifying mediaSource.sourceBuffers[" + i + "]");
+ }
+ assert_equals(mediaSource.sourceBuffers[expected.length], undefined,
+ "If index is greater than or equal to the length attribute then return undefined.");
+ }
+
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ var sourceBufferA = mediaSource.addSourceBuffer(MediaSourceUtil.VIDEO_ONLY_TYPE);
+ verifySourceBufferLists(mediaSource, [sourceBufferA]);
+
+ var sourceBufferB = mediaSource.addSourceBuffer(MediaSourceUtil.AUDIO_ONLY_TYPE);
+ verifySourceBufferLists(mediaSource, [sourceBufferA, sourceBufferB]);
+ test.done();
+ }, "Test SourceBufferList getter method");
+
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ test.expectEvent(mediaSource.sourceBuffers, "addsourcebuffer", "sourceBuffers");
+ var sourceBufferA = mediaSource.addSourceBuffer(MediaSourceUtil.VIDEO_ONLY_TYPE);
+ var sourceBufferB = null;
+
+ test.waitForExpectedEvents(function()
+ {
+ test.expectEvent(mediaSource.sourceBuffers, "addsourcebuffer", "sourceBuffers");
+ sourceBufferB = mediaSource.addSourceBuffer(MediaSourceUtil.AUDIO_ONLY_TYPE);
+
+ verifySourceBufferLists(mediaSource, [sourceBufferA, sourceBufferB]);
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ test.expectEvent(mediaSource.sourceBuffers, "removesourcebuffer", "sourceBuffers");
+ mediaSource.removeSourceBuffer(sourceBufferA);
+
+ verifySourceBufferLists(mediaSource, [sourceBufferB]);
+
+ test.expectEvent(mediaSource.sourceBuffers, "addsourcebuffer", "sourceBuffers");
+ sourceBufferA = mediaSource.addSourceBuffer(MediaSourceUtil.VIDEO_ONLY_TYPE);
+
+ verifySourceBufferLists(mediaSource, [sourceBufferB, sourceBufferA]);
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ test.done();
+ });
+ }, "Test SourceBufferList event dispatching.");
+
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ test.expectEvent(mediaSource.sourceBuffers, "addsourcebuffer", "sourceBuffers");
+ test.expectEvent(mediaSource.sourceBuffers, "addsourcebuffer", "sourceBuffers");
+ var sourceBufferA = mediaSource.addSourceBuffer(MediaSourceUtil.VIDEO_ONLY_TYPE);
+ var sourceBufferB = mediaSource.addSourceBuffer(MediaSourceUtil.AUDIO_ONLY_TYPE);
+
+ verifySourceBufferLists(mediaSource, [sourceBufferA, sourceBufferB]);
+
+ test.waitForExpectedEvents(function()
+ {
+ verifySourceBufferLists(mediaSource, [sourceBufferA, sourceBufferB]);
+
+ // Force the media element to close the MediaSource object.
+ test.expectEvent(mediaSource.sourceBuffers, "removesourcebuffer", "sourceBuffers");
+ test.expectEvent(mediaSource, "sourceclose", "mediaSource closing");
+ mediaElement.src = "";
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_equals(mediaSource.readyState, "closed", "mediaSource is closed.");
+
+ verifySourceBufferLists(mediaSource, []);
+ test.done();
+ });
+ }, "Test that only 1 removesourcebuffer event fires on each SourceBufferList when the MediaSource closes.");
+
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/media-source/mediasource-timestamp-offset.html b/testing/web-platform/tests/media-source/mediasource-timestamp-offset.html
new file mode 100644
index 000000000..22e429286
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mediasource-timestamp-offset.html
@@ -0,0 +1,125 @@
+<!DOCTYPE html>
+<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
+<html>
+ <head>
+ <title>SourceBuffer.timestampOffset test cases.</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="mediasource-util.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ function simpleTimestampOffsetTest(value, expected, description)
+ {
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ var segmentInfo = MediaSourceUtil.SEGMENT_INFO;
+ var sourceBuffer = mediaSource.addSourceBuffer(segmentInfo.type);
+
+ assert_equals(sourceBuffer.timestampOffset, 0,
+ "Initial timestampOffset of a SourceBuffer is 0");
+
+ if (expected == "TypeError") {
+ assert_throws({name: "TypeError"},
+ function() { sourceBuffer.timestampOffset = value; },
+ "setting timestampOffset to " + description + " throws an exception.");
+ } else {
+ sourceBuffer.timestampOffset = value;
+ assert_equals(sourceBuffer.timestampOffset, expected);
+ }
+
+ test.done();
+ }, "Test setting SourceBuffer.timestampOffset to " + description + ".");
+ }
+
+ simpleTimestampOffsetTest(10.5, 10.5, "a positive number");
+ simpleTimestampOffsetTest(-10.4, -10.4, "a negative number");
+ simpleTimestampOffsetTest(0, 0, "zero");
+ simpleTimestampOffsetTest(Number.POSITIVE_INFINITY, "TypeError", "positive infinity");
+ simpleTimestampOffsetTest(Number.NEGATIVE_INFINITY, "TypeError", "negative infinity");
+ simpleTimestampOffsetTest(Number.NaN, "TypeError", "NaN");
+ simpleTimestampOffsetTest(undefined, "TypeError", "undefined");
+ simpleTimestampOffsetTest(null, 0, "null");
+ simpleTimestampOffsetTest(false, 0, "false");
+ simpleTimestampOffsetTest(true, 1, "true");
+ simpleTimestampOffsetTest("10.5", 10.5, "a number string");
+ simpleTimestampOffsetTest("", 0, "an empty string");
+
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ var initSegment = MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.init);
+ var mediaSegment = MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.media[0]);
+
+ test.expectEvent(sourceBuffer, "updateend", "initSegment append ended.");
+ sourceBuffer.appendBuffer(initSegment);
+
+ test.waitForExpectedEvents(function()
+ {
+ test.expectEvent(sourceBuffer, "updateend", "mediaSegment append ended.");
+ sourceBuffer.appendBuffer(mediaSegment);
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ mediaSource.endOfStream();
+
+ assert_equals(mediaSource.readyState, "ended");
+
+ mediaSource.sourceBuffers[0].timestampOffset = 2;
+
+ assert_equals(mediaSource.readyState, "open");
+
+ test.expectEvent(mediaSource, "sourceopen", "mediaSource fired 'sourceopen' event.");
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ test.done();
+ });
+ }, "Test setting timestampOffset in 'ended' state causes a transition to 'open'.");
+
+ mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
+ {
+ var initSegment = MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.init);
+ var mediaSegment = MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.media[0]);
+
+ test.expectEvent(sourceBuffer, "updateend", "initSegment append ended.");
+ sourceBuffer.appendBuffer(initSegment);
+ assert_equals(mediaSource.sourceBuffers[0].timestampOffset, 0, "read initial value");
+
+ test.waitForExpectedEvents(function()
+ {
+ test.expectEvent(sourceBuffer, "updateend", "mediaSegment append ended.");
+ sourceBuffer.appendBuffer(mediaSegment);
+ assert_equals(mediaSource.sourceBuffers[0].timestampOffset, 0,
+ "No change to timestampoffset after segments mode init segment append");
+ });
+
+ test.waitForExpectedEvents(function()
+ {
+ assert_equals(mediaSource.sourceBuffers[0].timestampOffset, 0,
+ "No change to timestampoffset after segments mode media segment append");
+ test.done();
+ });
+ }, "Test getting the initial value of timestampOffset.");
+
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ var sourceBuffer = mediaSource.addSourceBuffer(MediaSourceUtil.AUDIO_VIDEO_TYPE);
+ assert_true(sourceBuffer != null, "New SourceBuffer returned");
+
+ mediaSource.removeSourceBuffer(sourceBuffer);
+ assert_true(mediaSource.sourceBuffers.length == 0, "MediaSource.sourceBuffers is empty");
+ assert_true(mediaSource.activeSourceBuffers.length == 0, "MediaSource.activesourceBuffers is empty");
+
+ assert_throws("InvalidStateError", function()
+ {
+ sourceBuffer.timestampOffset = 10;
+ });
+
+ test.done();
+ }, "Test setting timestampoffset after removing the sourcebuffer.");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/media-source/mediasource-trackdefault.html b/testing/web-platform/tests/media-source/mediasource-trackdefault.html
new file mode 100644
index 000000000..5fc2e5e97
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mediasource-trackdefault.html
@@ -0,0 +1,101 @@
+<!DOCTYPE html>
+<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ function checkConstructionSucceeds(type, language, label, kinds, byteStreamTrackID)
+ {
+ var trackDefault = new TrackDefault(type, language, label, kinds, byteStreamTrackID);
+ assert_equals(trackDefault.type, type, "type");
+ assert_equals(trackDefault.language, language, "language");
+ assert_equals(trackDefault.label, label, "label");
+ assert_equals(trackDefault.byteStreamTrackID, byteStreamTrackID, "byteStreamTrackID");
+ assert_array_equals(trackDefault.kinds, kinds, "kinds");
+ }
+
+ function checkConstructionFails(type, language, label, kinds, byteStreamTrackID)
+ {
+ assert_throws(new TypeError(),
+ function() { new TrackDefault(type, language, label, kinds, byteStreamTrackID); },
+ "TrackDefault construction threw an exception");
+ }
+
+ function trackDefaultConstructionTest(type, language, label, kinds, byteStreamTrackID, expectation, description)
+ {
+ test(function()
+ {
+ if (expectation)
+ checkConstructionSucceeds(type, language, label, kinds, byteStreamTrackID);
+ else
+ checkConstructionFails(type, language, label, kinds, byteStreamTrackID);
+ }, description + ": type '" + type + "', language '" + language + "', label '" + label + "', multiple kinds, byteStreamTrackID '" + byteStreamTrackID + "'");
+
+ // If all of |kinds| are expected to succeed, also test each kind individually.
+ if (!expectation || kinds.length <= 1)
+ return;
+ for (var i = 0; i < kinds.length; ++i) {
+ test(function()
+ {
+ checkConstructionSucceeds(type, language, label, new Array(kinds[i]), byteStreamTrackID);
+ }, description + ": type '" + type + "', language '" + language + "', label '" + label + "', kind '" + kinds[i] + "', byteStreamTrackID '" + byteStreamTrackID + "'");
+ }
+ }
+
+ var VALID_AUDIO_TRACK_KINDS = [
+ "alternative",
+ "descriptions",
+ "main",
+ "main-desc",
+ "translation",
+ "commentary",
+ "",
+ ];
+
+ var VALID_VIDEO_TRACK_KINDS = [
+ "alternative",
+ "captions",
+ "main",
+ "sign",
+ "subtitles",
+ "commentary",
+ "",
+ ];
+
+ var VALID_TEXT_TRACK_KINDS = [
+ "subtitles",
+ "captions",
+ "descriptions",
+ "chapters",
+ "metadata",
+ ];
+
+ trackDefaultConstructionTest("audio", "en-US", "audio label", VALID_AUDIO_TRACK_KINDS, "1", true, "Test valid audio kinds");
+
+ trackDefaultConstructionTest("video", "en-US", "video label", VALID_VIDEO_TRACK_KINDS, "1", true, "Test valid video kinds");
+
+ trackDefaultConstructionTest("text", "en-US", "text label", VALID_TEXT_TRACK_KINDS, "1", true, "Test valid text kinds");
+
+ trackDefaultConstructionTest("audio", "en-US", "audio label", VALID_VIDEO_TRACK_KINDS, "1", false, "Test mixed valid and invalid audio kinds");
+
+ trackDefaultConstructionTest("video", "en-US", "video label", VALID_AUDIO_TRACK_KINDS, "1", false, "Test mixed valid and invalid video kinds");
+
+ trackDefaultConstructionTest("text", "en-US", "text label", VALID_VIDEO_TRACK_KINDS, "1", false, "Test mixed valid and invalid text kinds");
+
+ trackDefaultConstructionTest("invalid type", "en-US", "label", VALID_AUDIO_TRACK_KINDS, "1", false, "Test invalid 'type' parameter type passed to TrackDefault constructor");
+
+ test(function()
+ {
+ checkConstructionFails("audio", "en-US", "label", "this is not a valid sequence", "1");
+ }, "Test invalid 'kinds' parameter type passed to TrackDefault constructor");
+
+ test(function()
+ {
+ var trackDefault = new TrackDefault("audio", "en-US", "label", VALID_AUDIO_TRACK_KINDS, "1");
+ var kinds = trackDefault.kinds;
+ kinds[0] = "invalid kind";
+ assert_equals(kinds[0], "invalid kind", "local kinds is updated");
+ assert_equals(VALID_AUDIO_TRACK_KINDS[0], "alternative", "local original kinds unchanged");
+ assert_array_equals(trackDefault.kinds, VALID_AUDIO_TRACK_KINDS, "trackDefault kinds unchanged");
+ }, "Test updating the retval of TrackDefault.kinds does not modify TrackDefault.kinds");
+</script>
diff --git a/testing/web-platform/tests/media-source/mediasource-trackdefaultlist.html b/testing/web-platform/tests/media-source/mediasource-trackdefaultlist.html
new file mode 100644
index 000000000..cceda8be4
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mediasource-trackdefaultlist.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ test(function()
+ {
+ var originalTrackDefaults = [
+ // Same everything, but different byteStreamTrackID, should be allowed by the constructor.
+ new TrackDefault("audio", "en-US", "label", ["main"], ""),
+ new TrackDefault("audio", "en-US", "label", ["main"], "1"),
+ new TrackDefault("audio", "en-US", "label", ["main"], "2"),
+ new TrackDefault("audio", "en-US", "label", [""], "3"),
+
+ // Same everything, but different type, should be allowed by the constructor.
+ new TrackDefault("video", "en-US", "label", ["main"], ""),
+ new TrackDefault("video", "en-US", "label", ["main"], "1"),
+ new TrackDefault("video", "en-US", "label", ["main"], "2"),
+ new TrackDefault("video", "en-US", "label", [""], "3")
+ ];
+
+ // Get a new array containing the same objects as |originalTrackDefaults|.
+ var trackDefaults = originalTrackDefaults.slice();
+
+ var trackDefaultList = new TrackDefaultList(trackDefaults);
+ assert_array_equals(trackDefaultList, originalTrackDefaults, "construction and read-back succeeded");
+ assert_equals(trackDefaultList[trackDefaultList.length], undefined, "out of range indexed property getter result is undefined");
+ assert_equals(trackDefaultList[trackDefaultList.length + 1], undefined, "out of range indexed property getter result is undefined");
+
+ // Introduce same-type, same-empty-string-byteStreamTrackId conflict in trackDefaults[0 vs 4].
+ trackDefaults[4] = new TrackDefault("audio", "en-US", "label", ["main"], "");
+ assert_equals(trackDefaults[0].type, trackDefaults[4].type, "same-type conflict setup");
+ assert_equals(trackDefaults[0].byteStreamTrackID, trackDefaults[4].byteStreamTrackID, "same-byteStreamTrackID conflict setup");
+ assert_throws("InvalidAccessError",
+ function() { new TrackDefaultList(trackDefaults); },
+ "TrackDefaultList construction should throw exception due to same type and byteStreamTrackID across at least 2 items in trackDefaults");
+
+ // Introduce same-type, same-non-empty-string-byteStreamTrackId conflict in trackDefaults[4 vs 5].
+ trackDefaults[4] = new TrackDefault("video", "en-US", "label", ["main"], "1");
+ assert_equals(trackDefaults[4].type, trackDefaults[5].type, "same-type conflict setup");
+ assert_equals(trackDefaults[4].byteStreamTrackID, trackDefaults[5].byteStreamTrackID, "same-byteStreamTrackID conflict setup");
+ assert_throws("InvalidAccessError",
+ function() { new TrackDefaultList(trackDefaults); },
+ "TrackDefaultList construction should throw exception due to same type and byteStreamTrackID across at least 2 items in trackDefaults");
+
+ // Confirm the constructed TrackDefaultList makes a shallow copy of the supplied TrackDefault sequence.
+ assert_array_equals(trackDefaultList, originalTrackDefaults, "read-back of original trackDefaultList unchanged");
+
+ }, "Test track default list construction, length, and indexed property getter");
+
+ test(function()
+ {
+ var trackDefaultList = new TrackDefaultList();
+ assert_array_equals(trackDefaultList, [], "empty list constructable without supplying optional trackDefaults parameter");
+
+ trackDefaultList = new TrackDefaultList([]);
+ assert_array_equals(trackDefaultList, [], "empty list constructable by supplying empty sequence as optional trackDefaults parameter");
+ }, "Test empty track default list construction with and without optional trackDefaults parameter");
+</script>
diff --git a/testing/web-platform/tests/media-source/mediasource-util.js b/testing/web-platform/tests/media-source/mediasource-util.js
new file mode 100644
index 000000000..15a56e83f
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mediasource-util.js
@@ -0,0 +1,407 @@
+(function(window) {
+ var SEGMENT_INFO_LIST = [
+ {
+ url: 'mp4/test.mp4',
+ type: 'video/mp4; codecs="mp4a.40.2,avc1.4d400d"',
+ duration: 6.549,
+ init: { offset: 0, size: 1413 },
+ media: [
+ { offset: 1413, size: 24034, timev: 0.095000, timea: 0, endtimev: 0.896666, endtimea: 0.882358 },
+ { offset: 25447, size: 21757, timev: 0.896666, timea: 0.882358, endtimev: 1.696666, endtimea: 1.671836 },
+ { offset: 47204, size: 23591, timev: 1.696666, timea: 1.671836, endtimev: 2.498333, endtimea: 2.461315 },
+ { offset: 70795, size: 22614, timev: 2.498333, timea: 2.461315, endtimev: 3.298333, endtimea: 3.297233 },
+ { offset: 93409, size: 18353, timev: 3.298333, timea: 3.297233, endtimev: 4.100000, endtimea: 4.086712},
+ { offset: 111762, size: 23935, timev: 4.100000, timea: 4.086712, endtimev: 4.900000, endtimea: 4.876190 },
+ { offset: 135697, size: 21911, timev: 4.900000, timea: 4.876190, endtimev: 5.701666, endtimea: 5.665668 },
+ { offset: 157608, size: 23776, timev: 5.701666, timea: 5.665668, endtimev: 6.501666, endtimea: 6.501587 },
+ { offset: 181384, size: 5843, timev: 6.501666, timea: 6.501587, endtimev: 6.501666, endtimea: 6.501678 },
+ ]
+ },
+ {
+ url: 'webm/test.webm',
+ type: 'video/webm; codecs="vp8, vorbis"',
+ duration: 6.552,
+ init: { offset: 0, size: 4116 },
+ media: [
+ { offset: 4116, size: 26583, timev: 0.112000, timea: 0, endtimev: 0.913000, endtimea: 0.912000 },
+ { offset: 30699, size: 20555, timev: 0.913000, timea: 0.912000, endtimev: 1.714000, endtimea: 1.701000 },
+ { offset: 51254, size: 22668, timev: 1.714000, timea: 1.701000, endtimev: 2.515000, endtimea: 2.514000 },
+ { offset: 73922, size: 21943, timev: 2.515000, timea: 2.514000, endtimev: 3.315000, endtimea: 3.303000 },
+ { offset: 95865, size: 23015, timev: 3.315000, timea: 3.303000, endtimev: 4.116000, endtimea: 4.093000},
+ { offset: 118880, size: 20406, timev: 4.116000, timea: 4.093000, endtimev: 4.917000, endtimea: 4.906000 },
+ { offset: 139286, size: 21537, timev: 4.917000, timea: 4.906000, endtimev: 5.718000, endtimea: 5.695000 },
+ { offset: 160823, size: 24027, timev: 5.718000, timea: 5.695000, endtimev: 6.519000, endtimea: 6.508000 },
+ { offset: 184850, size: 5955, timev: 6.519000, timea: 6.508000, endtimev: 6.577000, endtimea: 6.577000},
+ ],
+ }
+ ];
+ EventExpectationsManager = function(test)
+ {
+ this.test_ = test;
+ this.eventTargetList_ = [];
+ this.waitCallbacks_ = [];
+ };
+
+ EventExpectationsManager.prototype.expectEvent = function(object, eventName, description)
+ {
+ var eventInfo = { 'target': object, 'type': eventName, 'description': description};
+ var expectations = this.getExpectations_(object);
+ expectations.push(eventInfo);
+
+ var t = this;
+ var waitHandler = this.test_.step_func(this.handleWaitCallback_.bind(this));
+ var eventHandler = this.test_.step_func(function(event)
+ {
+ object.removeEventListener(eventName, eventHandler);
+ var expected = expectations[0];
+ assert_equals(event.target, expected.target, "Event target match.");
+ assert_equals(event.type, expected.type, "Event types match.");
+ assert_equals(eventInfo.description, expected.description, "Descriptions match for '" + event.type + "'.");
+
+ expectations.shift(1);
+ if (t.waitCallbacks_.length > 1)
+ setTimeout(waitHandler, 0);
+ else if (t.waitCallbacks_.length == 1) {
+ // Immediately call the callback.
+ waitHandler();
+ }
+ });
+ object.addEventListener(eventName, eventHandler);
+ };
+
+ EventExpectationsManager.prototype.waitForExpectedEvents = function(callback)
+ {
+ this.waitCallbacks_.push(callback);
+ setTimeout(this.test_.step_func(this.handleWaitCallback_.bind(this)), 0);
+ };
+
+ EventExpectationsManager.prototype.expectingEvents = function()
+ {
+ for (var i = 0; i < this.eventTargetList_.length; ++i) {
+ if (this.eventTargetList_[i].expectations.length > 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ EventExpectationsManager.prototype.handleWaitCallback_ = function()
+ {
+ if (this.waitCallbacks_.length == 0 || this.expectingEvents())
+ return;
+ var callback = this.waitCallbacks_.shift(1);
+ callback();
+ };
+
+ EventExpectationsManager.prototype.getExpectations_ = function(target)
+ {
+ for (var i = 0; i < this.eventTargetList_.length; ++i) {
+ var info = this.eventTargetList_[i];
+ if (info.target == target) {
+ return info.expectations;
+ }
+ }
+ var expectations = [];
+ this.eventTargetList_.push({ 'target': target, 'expectations': expectations });
+ return expectations;
+ };
+
+ function loadData_(test, url, callback, isBinary)
+ {
+ var request = new XMLHttpRequest();
+ request.open("GET", url, true);
+ if (isBinary) {
+ request.responseType = 'arraybuffer';
+ }
+ request.onload = test.step_func(function(event)
+ {
+ if (request.status != 200) {
+ assert_unreached("Unexpected status code : " + request.status);
+ return;
+ }
+ var response = request.response;
+ if (isBinary) {
+ response = new Uint8Array(response);
+ }
+ callback(response);
+ });
+ request.onerror = test.step_func(function(event)
+ {
+ assert_unreached("Unexpected error");
+ });
+ request.send();
+ }
+
+ function openMediaSource_(test, mediaTag, callback)
+ {
+ var mediaSource = new MediaSource();
+ var mediaSourceURL = URL.createObjectURL(mediaSource);
+
+ var eventHandler = test.step_func(onSourceOpen);
+ function onSourceOpen(event)
+ {
+ mediaSource.removeEventListener('sourceopen', eventHandler);
+ URL.revokeObjectURL(mediaSourceURL);
+ callback(mediaSource);
+ }
+
+ mediaSource.addEventListener('sourceopen', eventHandler);
+ mediaTag.src = mediaSourceURL;
+ }
+
+ var MediaSourceUtil = {};
+
+ MediaSourceUtil.loadTextData = function(test, url, callback)
+ {
+ loadData_(test, url, callback, false);
+ };
+
+ MediaSourceUtil.loadBinaryData = function(test, url, callback)
+ {
+ loadData_(test, url, callback, true);
+ };
+
+ MediaSourceUtil.fetchManifestAndData = function(test, manifestFilename, callback)
+ {
+ var baseURL = '';
+ var manifestURL = baseURL + manifestFilename;
+ MediaSourceUtil.loadTextData(test, manifestURL, function(manifestText)
+ {
+ var manifest = JSON.parse(manifestText);
+
+ assert_true(MediaSource.isTypeSupported(manifest.type), manifest.type + " is supported.");
+
+ var mediaURL = baseURL + manifest.url;
+ MediaSourceUtil.loadBinaryData(test, mediaURL, function(mediaData)
+ {
+ callback(manifest.type, mediaData);
+ });
+ });
+ };
+
+ MediaSourceUtil.extractSegmentData = function(mediaData, info)
+ {
+ var start = info.offset;
+ var end = start + info.size;
+ return mediaData.subarray(start, end);
+ }
+
+ MediaSourceUtil.getMediaDataForPlaybackTime = function(mediaData, segmentInfo, playbackTimeToAdd)
+ {
+ assert_less_than_equal(playbackTimeToAdd, segmentInfo.duration);
+ var mediaInfo = segmentInfo.media;
+ var start = mediaInfo[0].offset;
+ var numBytes = 0;
+ var segmentIndex = 0;
+ while (segmentIndex < mediaInfo.length
+ && Math.min(mediaInfo[segmentIndex].timev, mediaInfo[segmentIndex].timea) <= playbackTimeToAdd)
+ {
+ numBytes += mediaInfo[segmentIndex].size;
+ ++segmentIndex;
+ }
+ return mediaData.subarray(start, numBytes + start);
+ }
+
+ function getFirstSupportedType(typeList)
+ {
+ for (var i = 0; i < typeList.length; ++i) {
+ if (window.MediaSource && MediaSource.isTypeSupported(typeList[i]))
+ return typeList[i];
+ }
+ return "";
+ }
+
+ function getSegmentInfo()
+ {
+ for (var i = 0; i < SEGMENT_INFO_LIST.length; ++i) {
+ var segmentInfo = SEGMENT_INFO_LIST[i];
+ if (window.MediaSource && MediaSource.isTypeSupported(segmentInfo.type)) {
+ return segmentInfo;
+ }
+ }
+ return null;
+ }
+
+ var audioOnlyTypes = ['audio/mp4;codecs="mp4a.40.2"', 'audio/webm;codecs="vorbis"'];
+ var videoOnlyTypes = ['video/mp4;codecs="avc1.4D4001"', 'video/webm;codecs="vp8"'];
+ var audioVideoTypes = ['video/mp4;codecs="avc1.4D4001,mp4a.40.2"', 'video/webm;codecs="vp8,vorbis"'];
+ MediaSourceUtil.AUDIO_ONLY_TYPE = getFirstSupportedType(audioOnlyTypes);
+ MediaSourceUtil.VIDEO_ONLY_TYPE = getFirstSupportedType(videoOnlyTypes);
+ MediaSourceUtil.AUDIO_VIDEO_TYPE = getFirstSupportedType(audioVideoTypes);
+ MediaSourceUtil.SEGMENT_INFO = getSegmentInfo();
+
+ MediaSourceUtil.getSubType = function(mimetype) {
+ var slashIndex = mimetype.indexOf("/");
+ var semicolonIndex = mimetype.indexOf(";");
+ if (slashIndex <= 0) {
+ assert_unreached("Invalid mimetype '" + mimetype + "'");
+ return;
+ }
+
+ var start = slashIndex + 1;
+ if (semicolonIndex >= 0) {
+ if (semicolonIndex <= start) {
+ assert_unreached("Invalid mimetype '" + mimetype + "'");
+ return;
+ }
+
+ return mimetype.substr(start, semicolonIndex - start)
+ }
+
+ return mimetype.substr(start);
+ };
+
+ MediaSourceUtil.append = function(test, sourceBuffer, data, callback)
+ {
+ function onUpdate() {
+ sourceBuffer.removeEventListener("update", onUpdate);
+ callback();
+ }
+ sourceBuffer.addEventListener("update", onUpdate);
+
+ sourceBuffer.addEventListener('error', test.unreached_func("Unexpected event 'error'"));
+
+ sourceBuffer.appendBuffer(data);
+ };
+
+ MediaSourceUtil.appendUntilEventFires = function(test, mediaElement, eventName, sourceBuffer, mediaData, segmentInfo, startingIndex)
+ {
+ var eventFired = false;
+ function onEvent() {
+ mediaElement.removeEventListener(eventName, onEvent);
+ eventFired = true;
+ }
+ mediaElement.addEventListener(eventName, onEvent);
+
+ var i = startingIndex;
+ var onAppendDone = function() {
+ if (eventFired || (i >= (segmentInfo.media.length - 1)))
+ return;
+
+ i++;
+ if (i < segmentInfo.media.length)
+ {
+ MediaSourceUtil.append(test, sourceBuffer, MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.media[i]), onAppendDone);
+ }
+ };
+ MediaSourceUtil.append(test, sourceBuffer, MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.media[i]), onAppendDone);
+
+ };
+
+ function addExtraTestMethods(test)
+ {
+ test.eventExpectations_ = new EventExpectationsManager(test);
+ test.expectEvent = function(object, eventName, description)
+ {
+ test.eventExpectations_.expectEvent(object, eventName, description);
+ };
+
+ test.waitForExpectedEvents = function(callback)
+ {
+ test.eventExpectations_.waitForExpectedEvents(callback);
+ };
+
+ test.waitForCurrentTimeChange = function(mediaElement, callback)
+ {
+ var initialTime = mediaElement.currentTime;
+
+ var onTimeUpdate = test.step_func(function()
+ {
+ if (mediaElement.currentTime != initialTime) {
+ mediaElement.removeEventListener('timeupdate', onTimeUpdate);
+ callback();
+ }
+ });
+
+ mediaElement.addEventListener('timeupdate', onTimeUpdate);
+ }
+
+ var oldTestDone = test.done.bind(test);
+ test.done = function()
+ {
+ if (test.status == test.PASS) {
+ assert_false(test.eventExpectations_.expectingEvents(), "No pending event expectations.");
+ }
+ oldTestDone();
+ };
+ };
+
+ window['MediaSourceUtil'] = MediaSourceUtil;
+ window['media_test'] = function(testFunction, description, options)
+ {
+ options = options || {};
+ return async_test(function(test)
+ {
+ addExtraTestMethods(test);
+ testFunction(test);
+ }, description, options);
+ };
+ window['mediasource_test'] = function(testFunction, description, options)
+ {
+ return media_test(function(test)
+ {
+ var mediaTag = document.createElement("video");
+ if (!document.body) {
+ document.body = document.createElement("body");
+ }
+ document.body.appendChild(mediaTag);
+
+ test.removeMediaElement_ = true;
+ test.add_cleanup(function()
+ {
+ if (test.removeMediaElement_) {
+ document.body.removeChild(mediaTag);
+ test.removeMediaElement_ = false;
+ }
+ });
+
+ openMediaSource_(test, mediaTag, function(mediaSource)
+ {
+ testFunction(test, mediaTag, mediaSource);
+ });
+ }, description, options);
+ };
+
+ window['mediasource_testafterdataloaded'] = function(testFunction, description, options)
+ {
+ mediasource_test(function(test, mediaElement, mediaSource)
+ {
+ var segmentInfo = MediaSourceUtil.SEGMENT_INFO;
+
+ if (!segmentInfo) {
+ assert_unreached("No segment info compatible with this MediaSource implementation.");
+ return;
+ }
+
+ mediaElement.addEventListener('error', test.unreached_func("Unexpected event 'error'"));
+
+ var sourceBuffer = mediaSource.addSourceBuffer(segmentInfo.type);
+ MediaSourceUtil.loadBinaryData(test, segmentInfo.url, function(mediaData)
+ {
+ testFunction(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData);
+ });
+ }, description, options);
+ }
+
+ function timeRangesToString(ranges)
+ {
+ var s = "{";
+ for (var i = 0; i < ranges.length; ++i) {
+ s += " [" + ranges.start(i).toFixed(3) + ", " + ranges.end(i).toFixed(3) + ")";
+ }
+ return s + " }";
+ }
+
+ window['assertBufferedEquals'] = function(obj, expected, description)
+ {
+ var actual = timeRangesToString(obj.buffered);
+ assert_equals(actual, expected, description);
+ };
+
+ window['assertSeekableEquals'] = function(obj, expected, description)
+ {
+ var actual = timeRangesToString(obj.seekable);
+ assert_equals(actual, expected, description);
+ };
+
+})(window);
diff --git a/testing/web-platform/tests/media-source/mp4/test-a-128k-44100Hz-1ch-manifest.json b/testing/web-platform/tests/media-source/mp4/test-a-128k-44100Hz-1ch-manifest.json
new file mode 100644
index 000000000..f3caa460e
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mp4/test-a-128k-44100Hz-1ch-manifest.json
@@ -0,0 +1,4 @@
+{
+ "url": "mp4/test-a-128k-44100Hz-1ch.mp4",
+ "type": "audio/mp4;codecs=\"mp4a.40.2\""
+} \ No newline at end of file
diff --git a/testing/web-platform/tests/media-source/mp4/test-a-128k-44100Hz-1ch.mp4 b/testing/web-platform/tests/media-source/mp4/test-a-128k-44100Hz-1ch.mp4
new file mode 100644
index 000000000..fc7832a5b
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mp4/test-a-128k-44100Hz-1ch.mp4
Binary files differ
diff --git a/testing/web-platform/tests/media-source/mp4/test-a-192k-44100Hz-1ch-manifest.json b/testing/web-platform/tests/media-source/mp4/test-a-192k-44100Hz-1ch-manifest.json
new file mode 100644
index 000000000..41a6f339b
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mp4/test-a-192k-44100Hz-1ch-manifest.json
@@ -0,0 +1,4 @@
+{
+ "url": "mp4/test-a-192k-44100Hz-1ch.mp4",
+ "type": "audio/mp4;codecs=\"mp4a.40.2\""
+} \ No newline at end of file
diff --git a/testing/web-platform/tests/media-source/mp4/test-a-192k-44100Hz-1ch.mp4 b/testing/web-platform/tests/media-source/mp4/test-a-192k-44100Hz-1ch.mp4
new file mode 100644
index 000000000..864a87d25
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mp4/test-a-192k-44100Hz-1ch.mp4
Binary files differ
diff --git a/testing/web-platform/tests/media-source/mp4/test-av-384k-44100Hz-1ch-320x240-30fps-10kfr-manifest.json b/testing/web-platform/tests/media-source/mp4/test-av-384k-44100Hz-1ch-320x240-30fps-10kfr-manifest.json
new file mode 100644
index 000000000..7731e3170
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mp4/test-av-384k-44100Hz-1ch-320x240-30fps-10kfr-manifest.json
@@ -0,0 +1,4 @@
+{
+ "url": "mp4/test-av-384k-44100Hz-1ch-320x240-30fps-10kfr.mp4",
+ "type": "video/mp4;codecs=\"avc1.4D4001,mp4a.40.2\""
+} \ No newline at end of file
diff --git a/testing/web-platform/tests/media-source/mp4/test-av-384k-44100Hz-1ch-320x240-30fps-10kfr.mp4 b/testing/web-platform/tests/media-source/mp4/test-av-384k-44100Hz-1ch-320x240-30fps-10kfr.mp4
new file mode 100644
index 000000000..e623e8ee4
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mp4/test-av-384k-44100Hz-1ch-320x240-30fps-10kfr.mp4
Binary files differ
diff --git a/testing/web-platform/tests/media-source/mp4/test-av-384k-44100Hz-1ch-640x480-30fps-10kfr-manifest.json b/testing/web-platform/tests/media-source/mp4/test-av-384k-44100Hz-1ch-640x480-30fps-10kfr-manifest.json
new file mode 100644
index 000000000..78ded611f
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mp4/test-av-384k-44100Hz-1ch-640x480-30fps-10kfr-manifest.json
@@ -0,0 +1,4 @@
+{
+ "url": "mp4/test-av-384k-44100Hz-1ch-640x480-30fps-10kfr.mp4",
+ "type": "video/mp4;codecs=\"avc1.4D4001,mp4a.40.2\""
+} \ No newline at end of file
diff --git a/testing/web-platform/tests/media-source/mp4/test-av-384k-44100Hz-1ch-640x480-30fps-10kfr.mp4 b/testing/web-platform/tests/media-source/mp4/test-av-384k-44100Hz-1ch-640x480-30fps-10kfr.mp4
new file mode 100644
index 000000000..946167b56
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mp4/test-av-384k-44100Hz-1ch-640x480-30fps-10kfr.mp4
Binary files differ
diff --git a/testing/web-platform/tests/media-source/mp4/test-av-448k-44100Hz-1ch-640x480-30fps-10kfr-manifest.json b/testing/web-platform/tests/media-source/mp4/test-av-448k-44100Hz-1ch-640x480-30fps-10kfr-manifest.json
new file mode 100644
index 000000000..ba46349f9
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mp4/test-av-448k-44100Hz-1ch-640x480-30fps-10kfr-manifest.json
@@ -0,0 +1,4 @@
+{
+ "url": "mp4/test-av-448k-44100Hz-1ch-640x480-30fps-10kfr.mp4",
+ "type": "video/mp4;codecs=\"avc1.4D4001,mp4a.40.2\""
+} \ No newline at end of file
diff --git a/testing/web-platform/tests/media-source/mp4/test-av-448k-44100Hz-1ch-640x480-30fps-10kfr.mp4 b/testing/web-platform/tests/media-source/mp4/test-av-448k-44100Hz-1ch-640x480-30fps-10kfr.mp4
new file mode 100644
index 000000000..ace4bee53
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mp4/test-av-448k-44100Hz-1ch-640x480-30fps-10kfr.mp4
Binary files differ
diff --git a/testing/web-platform/tests/media-source/mp4/test-av-640k-44100Hz-1ch-640x480-30fps-10kfr-manifest.json b/testing/web-platform/tests/media-source/mp4/test-av-640k-44100Hz-1ch-640x480-30fps-10kfr-manifest.json
new file mode 100644
index 000000000..24da9b4ce
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mp4/test-av-640k-44100Hz-1ch-640x480-30fps-10kfr-manifest.json
@@ -0,0 +1,4 @@
+{
+ "url": "mp4/test-av-640k-44100Hz-1ch-640x480-30fps-10kfr.mp4",
+ "type": "video/mp4;codecs=\"avc1.4D4001,mp4a.40.2\""
+} \ No newline at end of file
diff --git a/testing/web-platform/tests/media-source/mp4/test-av-640k-44100Hz-1ch-640x480-30fps-10kfr.mp4 b/testing/web-platform/tests/media-source/mp4/test-av-640k-44100Hz-1ch-640x480-30fps-10kfr.mp4
new file mode 100644
index 000000000..f224a5426
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mp4/test-av-640k-44100Hz-1ch-640x480-30fps-10kfr.mp4
Binary files differ
diff --git a/testing/web-platform/tests/media-source/mp4/test-v-128k-320x240-24fps-8kfr-manifest.json b/testing/web-platform/tests/media-source/mp4/test-v-128k-320x240-24fps-8kfr-manifest.json
new file mode 100644
index 000000000..a31b6d024
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mp4/test-v-128k-320x240-24fps-8kfr-manifest.json
@@ -0,0 +1,4 @@
+{
+ "url": "mp4/test-v-128k-320x240-24fps-8kfr.mp4",
+ "type": "video/mp4;codecs=\"avc1.4D4001\""
+} \ No newline at end of file
diff --git a/testing/web-platform/tests/media-source/mp4/test-v-128k-320x240-24fps-8kfr.mp4 b/testing/web-platform/tests/media-source/mp4/test-v-128k-320x240-24fps-8kfr.mp4
new file mode 100644
index 000000000..cc55f40fa
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mp4/test-v-128k-320x240-24fps-8kfr.mp4
Binary files differ
diff --git a/testing/web-platform/tests/media-source/mp4/test-v-128k-320x240-30fps-10kfr-manifest.json b/testing/web-platform/tests/media-source/mp4/test-v-128k-320x240-30fps-10kfr-manifest.json
new file mode 100644
index 000000000..3e0284410
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mp4/test-v-128k-320x240-30fps-10kfr-manifest.json
@@ -0,0 +1,4 @@
+{
+ "url": "mp4/test-v-128k-320x240-30fps-10kfr.mp4",
+ "type": "video/mp4;codecs=\"avc1.4D4001\""
+} \ No newline at end of file
diff --git a/testing/web-platform/tests/media-source/mp4/test-v-128k-320x240-30fps-10kfr.mp4 b/testing/web-platform/tests/media-source/mp4/test-v-128k-320x240-30fps-10kfr.mp4
new file mode 100644
index 000000000..68d02cdfe
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mp4/test-v-128k-320x240-30fps-10kfr.mp4
Binary files differ
diff --git a/testing/web-platform/tests/media-source/mp4/test-v-128k-640x480-30fps-10kfr-manifest.json b/testing/web-platform/tests/media-source/mp4/test-v-128k-640x480-30fps-10kfr-manifest.json
new file mode 100644
index 000000000..10c4f4bcb
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mp4/test-v-128k-640x480-30fps-10kfr-manifest.json
@@ -0,0 +1,4 @@
+{
+ "url": "mp4/test-v-128k-640x480-30fps-10kfr.mp4",
+ "type": "video/mp4;codecs=\"avc1.4D4001\""
+} \ No newline at end of file
diff --git a/testing/web-platform/tests/media-source/mp4/test-v-128k-640x480-30fps-10kfr.mp4 b/testing/web-platform/tests/media-source/mp4/test-v-128k-640x480-30fps-10kfr.mp4
new file mode 100644
index 000000000..c4f47f035
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mp4/test-v-128k-640x480-30fps-10kfr.mp4
Binary files differ
diff --git a/testing/web-platform/tests/media-source/mp4/test-v-256k-320x240-30fps-10kfr-manifest.json b/testing/web-platform/tests/media-source/mp4/test-v-256k-320x240-30fps-10kfr-manifest.json
new file mode 100644
index 000000000..42d3e1e52
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mp4/test-v-256k-320x240-30fps-10kfr-manifest.json
@@ -0,0 +1,4 @@
+{
+ "url": "mp4/test-v-256k-320x240-30fps-10kfr.mp4",
+ "type": "video/mp4;codecs=\"avc1.4D4001\""
+} \ No newline at end of file
diff --git a/testing/web-platform/tests/media-source/mp4/test-v-256k-320x240-30fps-10kfr.mp4 b/testing/web-platform/tests/media-source/mp4/test-v-256k-320x240-30fps-10kfr.mp4
new file mode 100644
index 000000000..6dc4972fd
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mp4/test-v-256k-320x240-30fps-10kfr.mp4
Binary files differ
diff --git a/testing/web-platform/tests/media-source/mp4/test.mp4 b/testing/web-platform/tests/media-source/mp4/test.mp4
new file mode 100644
index 000000000..1b0e7b56a
--- /dev/null
+++ b/testing/web-platform/tests/media-source/mp4/test.mp4
Binary files differ
diff --git a/testing/web-platform/tests/media-source/webm/test-a-128k-44100Hz-1ch-manifest.json b/testing/web-platform/tests/media-source/webm/test-a-128k-44100Hz-1ch-manifest.json
new file mode 100644
index 000000000..524da8149
--- /dev/null
+++ b/testing/web-platform/tests/media-source/webm/test-a-128k-44100Hz-1ch-manifest.json
@@ -0,0 +1,4 @@
+{
+ "url": "webm/test-a-128k-44100Hz-1ch.webm",
+ "type": "audio/webm;codecs=\"vorbis\""
+} \ No newline at end of file
diff --git a/testing/web-platform/tests/media-source/webm/test-a-128k-44100Hz-1ch.webm b/testing/web-platform/tests/media-source/webm/test-a-128k-44100Hz-1ch.webm
new file mode 100644
index 000000000..c5b064deb
--- /dev/null
+++ b/testing/web-platform/tests/media-source/webm/test-a-128k-44100Hz-1ch.webm
Binary files differ
diff --git a/testing/web-platform/tests/media-source/webm/test-a-192k-44100Hz-1ch-manifest.json b/testing/web-platform/tests/media-source/webm/test-a-192k-44100Hz-1ch-manifest.json
new file mode 100644
index 000000000..7f2fa1e8c
--- /dev/null
+++ b/testing/web-platform/tests/media-source/webm/test-a-192k-44100Hz-1ch-manifest.json
@@ -0,0 +1,4 @@
+{
+ "url": "webm/test-a-192k-44100Hz-1ch.webm",
+ "type": "audio/webm;codecs=\"vorbis\""
+} \ No newline at end of file
diff --git a/testing/web-platform/tests/media-source/webm/test-a-192k-44100Hz-1ch.webm b/testing/web-platform/tests/media-source/webm/test-a-192k-44100Hz-1ch.webm
new file mode 100644
index 000000000..53814d3bd
--- /dev/null
+++ b/testing/web-platform/tests/media-source/webm/test-a-192k-44100Hz-1ch.webm
Binary files differ
diff --git a/testing/web-platform/tests/media-source/webm/test-av-384k-44100Hz-1ch-320x240-30fps-10kfr-manifest.json b/testing/web-platform/tests/media-source/webm/test-av-384k-44100Hz-1ch-320x240-30fps-10kfr-manifest.json
new file mode 100644
index 000000000..af9f07af1
--- /dev/null
+++ b/testing/web-platform/tests/media-source/webm/test-av-384k-44100Hz-1ch-320x240-30fps-10kfr-manifest.json
@@ -0,0 +1,4 @@
+{
+ "url": "webm/test-av-384k-44100Hz-1ch-320x240-30fps-10kfr.webm",
+ "type": "video/webm;codecs=\"vp8,vorbis\""
+} \ No newline at end of file
diff --git a/testing/web-platform/tests/media-source/webm/test-av-384k-44100Hz-1ch-320x240-30fps-10kfr.webm b/testing/web-platform/tests/media-source/webm/test-av-384k-44100Hz-1ch-320x240-30fps-10kfr.webm
new file mode 100644
index 000000000..8b705dbc8
--- /dev/null
+++ b/testing/web-platform/tests/media-source/webm/test-av-384k-44100Hz-1ch-320x240-30fps-10kfr.webm
Binary files differ
diff --git a/testing/web-platform/tests/media-source/webm/test-av-384k-44100Hz-1ch-640x480-30fps-10kfr-manifest.json b/testing/web-platform/tests/media-source/webm/test-av-384k-44100Hz-1ch-640x480-30fps-10kfr-manifest.json
new file mode 100644
index 000000000..f7ec86b3d
--- /dev/null
+++ b/testing/web-platform/tests/media-source/webm/test-av-384k-44100Hz-1ch-640x480-30fps-10kfr-manifest.json
@@ -0,0 +1,4 @@
+{
+ "url": "webm/test-av-384k-44100Hz-1ch-640x480-30fps-10kfr.webm",
+ "type": "video/webm;codecs=\"vp8,vorbis\""
+} \ No newline at end of file
diff --git a/testing/web-platform/tests/media-source/webm/test-av-384k-44100Hz-1ch-640x480-30fps-10kfr.webm b/testing/web-platform/tests/media-source/webm/test-av-384k-44100Hz-1ch-640x480-30fps-10kfr.webm
new file mode 100644
index 000000000..c5e010e3c
--- /dev/null
+++ b/testing/web-platform/tests/media-source/webm/test-av-384k-44100Hz-1ch-640x480-30fps-10kfr.webm
Binary files differ
diff --git a/testing/web-platform/tests/media-source/webm/test-av-448k-44100Hz-1ch-640x480-30fps-10kfr-manifest.json b/testing/web-platform/tests/media-source/webm/test-av-448k-44100Hz-1ch-640x480-30fps-10kfr-manifest.json
new file mode 100644
index 000000000..96a59db58
--- /dev/null
+++ b/testing/web-platform/tests/media-source/webm/test-av-448k-44100Hz-1ch-640x480-30fps-10kfr-manifest.json
@@ -0,0 +1,4 @@
+{
+ "url": "webm/test-av-448k-44100Hz-1ch-640x480-30fps-10kfr.webm",
+ "type": "video/webm;codecs=\"vp8,vorbis\""
+} \ No newline at end of file
diff --git a/testing/web-platform/tests/media-source/webm/test-av-448k-44100Hz-1ch-640x480-30fps-10kfr.webm b/testing/web-platform/tests/media-source/webm/test-av-448k-44100Hz-1ch-640x480-30fps-10kfr.webm
new file mode 100644
index 000000000..62c43288e
--- /dev/null
+++ b/testing/web-platform/tests/media-source/webm/test-av-448k-44100Hz-1ch-640x480-30fps-10kfr.webm
Binary files differ
diff --git a/testing/web-platform/tests/media-source/webm/test-av-640k-44100Hz-1ch-640x480-30fps-10kfr-manifest.json b/testing/web-platform/tests/media-source/webm/test-av-640k-44100Hz-1ch-640x480-30fps-10kfr-manifest.json
new file mode 100644
index 000000000..86723b34a
--- /dev/null
+++ b/testing/web-platform/tests/media-source/webm/test-av-640k-44100Hz-1ch-640x480-30fps-10kfr-manifest.json
@@ -0,0 +1,4 @@
+{
+ "url": "webm/test-av-640k-44100Hz-1ch-640x480-30fps-10kfr.webm",
+ "type": "video/webm;codecs=\"vp8,vorbis\""
+} \ No newline at end of file
diff --git a/testing/web-platform/tests/media-source/webm/test-av-640k-44100Hz-1ch-640x480-30fps-10kfr.webm b/testing/web-platform/tests/media-source/webm/test-av-640k-44100Hz-1ch-640x480-30fps-10kfr.webm
new file mode 100644
index 000000000..93c31b6a9
--- /dev/null
+++ b/testing/web-platform/tests/media-source/webm/test-av-640k-44100Hz-1ch-640x480-30fps-10kfr.webm
Binary files differ
diff --git a/testing/web-platform/tests/media-source/webm/test-v-128k-320x240-24fps-8kfr-manifest.json b/testing/web-platform/tests/media-source/webm/test-v-128k-320x240-24fps-8kfr-manifest.json
new file mode 100644
index 000000000..00e103aca
--- /dev/null
+++ b/testing/web-platform/tests/media-source/webm/test-v-128k-320x240-24fps-8kfr-manifest.json
@@ -0,0 +1,4 @@
+{
+ "url": "webm/test-v-128k-320x240-24fps-8kfr.webm",
+ "type": "video/webm;codecs=\"vp8\""
+} \ No newline at end of file
diff --git a/testing/web-platform/tests/media-source/webm/test-v-128k-320x240-24fps-8kfr.webm b/testing/web-platform/tests/media-source/webm/test-v-128k-320x240-24fps-8kfr.webm
new file mode 100644
index 000000000..189c472f9
--- /dev/null
+++ b/testing/web-platform/tests/media-source/webm/test-v-128k-320x240-24fps-8kfr.webm
Binary files differ
diff --git a/testing/web-platform/tests/media-source/webm/test-v-128k-320x240-30fps-10kfr-manifest.json b/testing/web-platform/tests/media-source/webm/test-v-128k-320x240-30fps-10kfr-manifest.json
new file mode 100644
index 000000000..fdeeb401d
--- /dev/null
+++ b/testing/web-platform/tests/media-source/webm/test-v-128k-320x240-30fps-10kfr-manifest.json
@@ -0,0 +1,4 @@
+{
+ "url": "webm/test-v-128k-320x240-30fps-10kfr.webm",
+ "type": "video/webm;codecs=\"vp8\""
+} \ No newline at end of file
diff --git a/testing/web-platform/tests/media-source/webm/test-v-128k-320x240-30fps-10kfr.webm b/testing/web-platform/tests/media-source/webm/test-v-128k-320x240-30fps-10kfr.webm
new file mode 100644
index 000000000..18b2bafc3
--- /dev/null
+++ b/testing/web-platform/tests/media-source/webm/test-v-128k-320x240-30fps-10kfr.webm
Binary files differ
diff --git a/testing/web-platform/tests/media-source/webm/test-v-128k-640x480-30fps-10kfr-manifest.json b/testing/web-platform/tests/media-source/webm/test-v-128k-640x480-30fps-10kfr-manifest.json
new file mode 100644
index 000000000..4e3046066
--- /dev/null
+++ b/testing/web-platform/tests/media-source/webm/test-v-128k-640x480-30fps-10kfr-manifest.json
@@ -0,0 +1,4 @@
+{
+ "url": "webm/test-v-128k-640x480-30fps-10kfr.webm",
+ "type": "video/webm;codecs=\"vp8\""
+} \ No newline at end of file
diff --git a/testing/web-platform/tests/media-source/webm/test-v-128k-640x480-30fps-10kfr.webm b/testing/web-platform/tests/media-source/webm/test-v-128k-640x480-30fps-10kfr.webm
new file mode 100644
index 000000000..75e38b0bf
--- /dev/null
+++ b/testing/web-platform/tests/media-source/webm/test-v-128k-640x480-30fps-10kfr.webm
Binary files differ
diff --git a/testing/web-platform/tests/media-source/webm/test-v-256k-320x240-30fps-10kfr-manifest.json b/testing/web-platform/tests/media-source/webm/test-v-256k-320x240-30fps-10kfr-manifest.json
new file mode 100644
index 000000000..3470674bf
--- /dev/null
+++ b/testing/web-platform/tests/media-source/webm/test-v-256k-320x240-30fps-10kfr-manifest.json
@@ -0,0 +1,4 @@
+{
+ "url": "webm/test-v-256k-320x240-30fps-10kfr.webm",
+ "type": "video/webm;codecs=\"vp8\""
+} \ No newline at end of file
diff --git a/testing/web-platform/tests/media-source/webm/test-v-256k-320x240-30fps-10kfr.webm b/testing/web-platform/tests/media-source/webm/test-v-256k-320x240-30fps-10kfr.webm
new file mode 100644
index 000000000..0250d26fa
--- /dev/null
+++ b/testing/web-platform/tests/media-source/webm/test-v-256k-320x240-30fps-10kfr.webm
Binary files differ
diff --git a/testing/web-platform/tests/media-source/webm/test-vp8-vorbis-webvtt.webm b/testing/web-platform/tests/media-source/webm/test-vp8-vorbis-webvtt.webm
new file mode 100644
index 000000000..c626f86e3
--- /dev/null
+++ b/testing/web-platform/tests/media-source/webm/test-vp8-vorbis-webvtt.webm
Binary files differ
diff --git a/testing/web-platform/tests/media-source/webm/test.webm b/testing/web-platform/tests/media-source/webm/test.webm
new file mode 100644
index 000000000..3a601805d
--- /dev/null
+++ b/testing/web-platform/tests/media-source/webm/test.webm
Binary files differ