| <!DOCTYPE html> |
| <html> |
| <head> |
| <script src="/w3c/resources/testharness.js"></script> |
| <script src="/w3c/resources/testharnessreport.js"></script> |
| <script src="mediasource-util.js"></script> |
| <link rel='stylesheet' href='/w3c/resources/testharness.css'> |
| </head> |
| <body> |
| <div id="log"></div> |
| <script> |
| function mediasource_duration_below_currentTime_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 reducedDuration = seekTo / 2.0; |
| |
| var receivedTimeupdate = false; |
| var timeupdateEventHandler = test.step_func(function(event) { receivedTimeupdate = true; }); |
| |
| mediaElement.play(); |
| |
| // Append all the segments |
| test.expectEvent(sourceBuffer, 'updateend', 'sourceBuffer append completed'); |
| test.expectEvent(mediaElement, 'playing', 'Playing triggered'); |
| sourceBuffer.appendBuffer(mediaData); |
| |
| |
| test.waitForExpectedEvents(function() |
| { |
| assert_equals(mediaElement.duration, fullDuration, 'mediaElement duration matches fullDuration'); |
| assert_equals(mediaSource.duration, fullDuration, 'mediaSource duration matches fullDuration'); |
| |
| test.expectEvent(mediaElement, 'seeking', 'seeking to seekTo'); |
| // Intentionally not expecting exactly 1 timeupdate here. currentTime is moving, so extra events could occur. |
| mediaElement.addEventListener('timeupdate', timeupdateEventHandler, { once: true }); |
| test.expectEvent(mediaElement, 'seeked', 'seeked to seekTo'); |
| |
| mediaElement.currentTime = seekTo; |
| |
| assert_true(mediaElement.seeking, 'mediaElement.seeking (to seekTo)'); |
| }); |
| |
| test.waitForExpectedEvents(function() |
| { |
| assert_true(receivedTimeupdate, 'mediaElement timeupdate occurred at least once since playing and through starting, doing and completing seek'); |
| assert_greater_than_equal(mediaElement.currentTime, seekTo, 'Playback time has reached seekTo'); |
| assert_equals(mediaElement.duration, fullDuration, 'mediaElement duration matches fullDuration after seekTo'); |
| assert_equals(mediaSource.duration, fullDuration, 'mediaSource duration matches fullDuration after seekTo'); |
| assert_false(mediaElement.seeking, 'mediaElement.seeking after seeked to seekTo'); |
| |
| // Explicitly remove buffered media beyond the new reduced duration prior to reducing duration. |
| // Implicit removal of buffered media as part of duration reduction is disallowed as of |
| // https://github.com/w3c/media-source/pull/65/ |
| test.expectEvent(sourceBuffer, 'updateend', 'sourceBuffer range removal completed'); |
| sourceBuffer.remove(reducedDuration, fullDuration); |
| assert_true(sourceBuffer.updating, 'sourceBuffer.updating during range removal'); |
| }); |
| |
| test.waitForExpectedEvents(function() |
| { |
| assert_false(sourceBuffer.updating, 'sourceBuffer.updating after range removal'); |
| assert_greater_than_equal(mediaElement.currentTime, seekTo, |
| 'Playback time is still at least seekTo after range removal'); |
| |
| test.expectEvent(mediaElement, 'seeking', 'Seeking to reduced duration'); |
| mediaSource.duration = reducedDuration; |
| assert_true(mediaElement.seeking, 'Seeking after setting reducedDuration'); |
| }); |
| |
| test.waitForExpectedEvents(function() |
| { |
| assert_equals(mediaElement.currentTime, reducedDuration, |
| 'Playback time is reducedDuration while seeking'); |
| assert_true(mediaElement.seeking, 'mediaElement.seeking while seeking to reducedDuration'); |
| assert_equals(mediaElement.duration, reducedDuration, |
| 'mediaElement duration matches reducedDuration during seek to it'); |
| assert_equals(mediaSource.duration, reducedDuration, |
| 'mediaSource duration matches reducedDuration during seek to it'); |
| |
| // FIXME: Confirm 'waiting' and then 'stalled' fire here. See http://crbug.com/266592. |
| |
| testFunction(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData, |
| reducedDuration); |
| }); |
| }, description, options); |
| } |
| |
| mediasource_duration_below_currentTime_seek_test(function(test, mediaElement, mediaSource, segmentInfo, |
| sourceBuffer, mediaData, reducedDuration) |
| { |
| // Tests that duration reduction below current playback position |
| // starts seek to new duration. |
| test.done(); |
| }, 'Test seek starts on duration reduction below currentTime'); |
| |
| mediasource_duration_below_currentTime_seek_test(function(test, mediaElement, mediaSource, segmentInfo, |
| sourceBuffer, mediaData, reducedDuration) |
| { |
| // The duration has been reduced at this point, and there is an |
| // outstanding seek pending. |
| test.expectEvent(sourceBuffer, 'updateend', 'updateend after appending more data'); |
| |
| // FIXME: Confirm 'playing' fires here. See http://crbug.com/266592. |
| |
| test.expectEvent(mediaElement, 'timeupdate', 'timeupdate while finishing seek to reducedDuration'); |
| test.expectEvent(mediaElement, 'seeked', 'seeked to reducedDuration'); |
| |
| // Allow seek to complete by appending more data beginning at the |
| // reduced duration timestamp. |
| sourceBuffer.timestampOffset = reducedDuration; |
| sourceBuffer.appendBuffer(mediaData); |
| |
| test.waitForExpectedEvents(function() |
| { |
| assert_greater_than_equal(mediaElement.currentTime, reducedDuration, |
| 'Playback time has reached reducedDuration'); |
| assert_approx_equals(mediaElement.duration, reducedDuration + 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 reducedDuration'); |
| |
| test.done(); |
| }); |
| }, 'Test appendBuffer completes previous seek to reduced duration'); |
| |
| mediasource_duration_below_currentTime_seek_test(function(test, mediaElement, mediaSource, segmentInfo, |
| sourceBuffer, mediaData, reducedDuration) |
| { |
| // The duration has been reduced at this point, and there is an |
| // outstanding seek pending. |
| test.expectEvent(mediaSource, 'sourceended', 'endOfStream acknowledged'); |
| |
| // FIXME: Investigate if 'playing' should fire here. See http://crbug.com/266592. |
| |
| test.expectEvent(mediaElement, 'timeupdate', 'timeupdate while finishing seek to reducedDuration'); |
| test.expectEvent(mediaElement, 'seeked', 'seeked to reducedDuration'); |
| |
| // Call endOfStream() to complete the pending seek. |
| mediaSource.endOfStream(); |
| |
| test.waitForExpectedEvents(function() |
| { |
| assert_equals(mediaElement.currentTime, reducedDuration, |
| 'Playback time has reached reducedDuration'); |
| assert_equals(mediaElement.duration, reducedDuration, |
| 'mediaElement duration matches reducedDuration after seek to it'); |
| assert_equals(mediaSource.duration, reducedDuration, |
| 'mediaSource duration matches reducedDuration after seek to it'); |
| assert_false(mediaElement.seeking, 'mediaElement.seeking after seeked to reducedDuration'); |
| |
| test.done(); |
| }); |
| }, 'Test endOfStream completes previous seek to reduced 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 twiceFullDuration = fullDuration * 2; |
| var expectedDuration = fullDuration; |
| |
| var durationchangeEventCounter = 0; |
| var durationchangeEventHandler = test.step_func(function(event) |
| { |
| assert_equals(mediaElement.duration, expectedDuration, 'mediaElement duration matches expectedDuration'); |
| assert_equals(mediaSource.duration, expectedDuration, 'mediaSource duration matches expectedDuration'); |
| durationchangeEventCounter++; |
| }); |
| |
| mediaElement.addEventListener('durationchange', durationchangeEventHandler); |
| |
| // Append all the segments |
| test.expectEvent(sourceBuffer, 'updateend', 'sourceBuffer append completed'); |
| test.expectEvent(mediaElement, 'durationchange', 'mediaElement durationchange as part of reaching loadedmetadata'); |
| test.expectEvent(mediaElement, 'loadedmetadata', 'mediaElement loadedmetadata'); |
| sourceBuffer.appendBuffer(mediaData); |
| |
| test.waitForExpectedEvents(function() |
| { |
| assert_equals(mediaElement.duration, fullDuration, 'mediaElement duration matches fullDuration'); |
| assert_equals(mediaSource.duration, fullDuration, 'mediaSource duration matches fullDuration'); |
| assert_equals(mediaElement.currentTime, 0, 'mediaElement currentTime check - we\'re not playing'); |
| |
| // The first append's init segment triggers an initial 'durationchange' event, and |
| // the expectEvent for durationchange, above, should ensure this assertion passes. |
| // Note that the init segment in mediaData should have a duration value as large |
| // as or larger than the duration of media in mediaData, so that append should |
| // trigger precisely 1 'durationchange' event. |
| assert_equals(durationchangeEventCounter, 1, 'Should have exactly 1 "durationchange" event processed here'); |
| |
| // Increase duration. This should result in a second 'durationchange' fired. |
| expectedDuration = twiceFullDuration; |
| mediaSource.duration = twiceFullDuration; |
| assert_false(sourceBuffer.updating, 'sourceBuffer.updating after duration set to twiceFullDuration'); |
| assert_equals(mediaElement.duration, twiceFullDuration, 'mediaElement duration matches twiceFullDuration'); |
| |
| // Set duration again. Later, we verify this doesn't trigger another |
| // 'durationchange' event. |
| mediaSource.duration = twiceFullDuration; |
| |
| assert_false(sourceBuffer.updating, 'sourceBuffer.updating after duration set again to twiceFullDuration'); |
| assert_equals(mediaElement.duration, twiceFullDuration, 'mediaElement duration matches twiceFullDuration after mediaSource duration set again to twiceFullDuration'); |
| |
| // Use a seek to let any currently queued 'durationchange' events fire. |
| assert_false(mediaElement.seeking, 'mediaElement should not be seeking after setting twiceFullDuration twice'); |
| assert_equals(mediaElement.currentTime, 0, 'mediaElement currentTime before seeking after setting twiceFullDuration twice'); |
| test.expectEvent(mediaElement, 'seeked', 'mediaElement seeked after setting twiceFullDuration twice'); |
| mediaElement.currentTime = fullDuration / 2; |
| }); |
| |
| test.waitForExpectedEvents(function() |
| { |
| assert_equals(durationchangeEventCounter, 2, 'durationchange count check before endOfStream()'); |
| |
| // Mark endOfStream to trigger duration reduction. |
| test.expectEvent(mediaSource, 'sourceended', 'endOfStream acknowledged'); |
| expectedDuration = fullDuration; |
| mediaSource.endOfStream(); |
| |
| // endOfStream should reduce the duration back to fullDuration (and queue another 'durationchange'). |
| assert_equals(mediaElement.duration, fullDuration, 'mediaElement duration returns to fullDuration after endOfStream()'); |
| assert_equals(mediaSource.duration, fullDuration, 'mediaSource duration returns to fullDuration after endOfStream()'); |
| |
| // Use another seek to a distinct time to let any currently queued |
| // 'durationchange' events fire. |
| assert_false(mediaElement.seeking, 'mediaElement should not be seeking after calling endOfStream()'); |
| test.expectEvent(mediaElement, 'seeked', 'mediaElement completed final seek'); |
| mediaElement.currentTime -= fullDuration / 4; |
| }); |
| |
| test.waitForExpectedEvents(function() |
| { |
| mediaElement.removeEventListener('durationchange', durationchangeEventHandler); |
| // Counter should be 3 because each of the following should have triggered |
| // durationchange: |
| // 1) initial appendBuffer()'s causing transition to loadedmetadata |
| // 2) explicitly increasing mediaSource.duration (to same value twice, so just |
| // one corresponding durationchange should have been triggered). |
| // 3) endOfStream() reducing the duration |
| assert_equals(durationchangeEventCounter, 3, 'final durationchange count check'); |
| 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; |
| |
| // Append all the segments |
| test.expectEvent(sourceBuffer, 'updateend', 'sourceBuffer'); |
| |
| // TODO(wolenetz): Remove this flakiness workaround. See https://crbug.com/641121#c2. |
| test.expectEvent(mediaElement, 'loadedmetadata', 'mediaElement'); |
| |
| sourceBuffer.appendBuffer(mediaData); |
| |
| test.waitForExpectedEvents(function() |
| { |
| assert_equals(mediaElement.duration, fullDuration, 'mediaElement duration matches fullDuration'); |
| assert_equals(mediaSource.duration, fullDuration, 'mediaSource duration matches fullDuration'); |
| |
| // TODO(wolenetz): Fine-tune this test to use the buffered attribute's highest end time |
| // instead of fullDuration once Chrome correctly reports buffered PTS, not DTS. |
| // See https://crbug.com/398130. |
| |
| // Setting duration to same as current, or increasing it, should not trigger exception. |
| mediaSource.duration = fullDuration; |
| mediaSource.duration = fullDuration + 1; |
| |
| // Reducing duration to below the highest buffered PTS should trigger exception. |
| assert_throws_dom('InvalidStateError', |
| function() { mediaSource.duration = fullDuration - 0.05; }, |
| 'Duration reduction that truncates at least one whole coded frame throws an exception.'); |
| |
| assert_equals(mediaSource.duration, fullDuration + 1, 'mediaSource duration matches fullDuration+1'); |
| |
| // Reducing duration without truncating any buffered media should not trigger exception. |
| mediaSource.duration = fullDuration; |
| |
| // Reducing duration by less than the minimum of the last test audio and video frame |
| // durations should not trigger exception. |
| mediaSource.duration = fullDuration - 0.001; |
| test.done(); |
| }); |
| }, 'Test duration reduction below highest buffered presentation time is disallowed'); |
| |
| </script> |
| </body> |
| </html> |