diff options
Diffstat (limited to 'testing/web-platform/tests/media-source')
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 & 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 & 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 & 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 & 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 & 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 & 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 & 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 & 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 Binary files differnew file mode 100644 index 000000000..fc7832a5b --- /dev/null +++ b/testing/web-platform/tests/media-source/mp4/test-a-128k-44100Hz-1ch.mp4 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 Binary files differnew file mode 100644 index 000000000..864a87d25 --- /dev/null +++ b/testing/web-platform/tests/media-source/mp4/test-a-192k-44100Hz-1ch.mp4 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 Binary files differnew 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 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 Binary files differnew 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 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 Binary files differnew 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 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 Binary files differnew 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 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 Binary files differnew file mode 100644 index 000000000..cc55f40fa --- /dev/null +++ b/testing/web-platform/tests/media-source/mp4/test-v-128k-320x240-24fps-8kfr.mp4 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 Binary files differnew file mode 100644 index 000000000..68d02cdfe --- /dev/null +++ b/testing/web-platform/tests/media-source/mp4/test-v-128k-320x240-30fps-10kfr.mp4 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 Binary files differnew file mode 100644 index 000000000..c4f47f035 --- /dev/null +++ b/testing/web-platform/tests/media-source/mp4/test-v-128k-640x480-30fps-10kfr.mp4 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 Binary files differnew file mode 100644 index 000000000..6dc4972fd --- /dev/null +++ b/testing/web-platform/tests/media-source/mp4/test-v-256k-320x240-30fps-10kfr.mp4 diff --git a/testing/web-platform/tests/media-source/mp4/test.mp4 b/testing/web-platform/tests/media-source/mp4/test.mp4 Binary files differnew file mode 100644 index 000000000..1b0e7b56a --- /dev/null +++ b/testing/web-platform/tests/media-source/mp4/test.mp4 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 Binary files differnew file mode 100644 index 000000000..c5b064deb --- /dev/null +++ b/testing/web-platform/tests/media-source/webm/test-a-128k-44100Hz-1ch.webm 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 Binary files differnew file mode 100644 index 000000000..53814d3bd --- /dev/null +++ b/testing/web-platform/tests/media-source/webm/test-a-192k-44100Hz-1ch.webm 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 Binary files differnew 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 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 Binary files differnew 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 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 Binary files differnew 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 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 Binary files differnew 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 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 Binary files differnew file mode 100644 index 000000000..189c472f9 --- /dev/null +++ b/testing/web-platform/tests/media-source/webm/test-v-128k-320x240-24fps-8kfr.webm 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 Binary files differnew file mode 100644 index 000000000..18b2bafc3 --- /dev/null +++ b/testing/web-platform/tests/media-source/webm/test-v-128k-320x240-30fps-10kfr.webm 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 Binary files differnew file mode 100644 index 000000000..75e38b0bf --- /dev/null +++ b/testing/web-platform/tests/media-source/webm/test-v-128k-640x480-30fps-10kfr.webm 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 Binary files differnew file mode 100644 index 000000000..0250d26fa --- /dev/null +++ b/testing/web-platform/tests/media-source/webm/test-v-256k-320x240-30fps-10kfr.webm 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 Binary files differnew file mode 100644 index 000000000..c626f86e3 --- /dev/null +++ b/testing/web-platform/tests/media-source/webm/test-vp8-vorbis-webvtt.webm diff --git a/testing/web-platform/tests/media-source/webm/test.webm b/testing/web-platform/tests/media-source/webm/test.webm Binary files differnew file mode 100644 index 000000000..3a601805d --- /dev/null +++ b/testing/web-platform/tests/media-source/webm/test.webm |