diff options
Diffstat (limited to 'testing/web-platform/tests/media-source/mediasource-duration.html')
-rw-r--r-- | testing/web-platform/tests/media-source/mediasource-duration.html | 383 |
1 files changed, 383 insertions, 0 deletions
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 0000000000..b4619da38b --- /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_dom("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_dom("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_dom("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> |