Tests for endOfStream and segment.duration tracking behavior in updateEndHandler
Showing
2 changed files
with
137 additions
and
17 deletions
... | @@ -298,7 +298,7 @@ videojs.Hls.findSoleUncommonTimeRangesEnd_ = function(original, update) { | ... | @@ -298,7 +298,7 @@ videojs.Hls.findSoleUncommonTimeRangesEnd_ = function(original, update) { |
298 | }; | 298 | }; |
299 | 299 | ||
300 | /** | 300 | /** |
301 | * Updates segment with information about it's end-point in time and, optionally, | 301 | * Updates segment with information about its end-point in time and, optionally, |
302 | * the segment duration if we have enough information to determine a segment duration | 302 | * the segment duration if we have enough information to determine a segment duration |
303 | * accurately. | 303 | * accurately. |
304 | * @param playlist {object} a media playlist object | 304 | * @param playlist {object} a media playlist object |
... | @@ -322,7 +322,7 @@ videojs.HlsHandler.prototype.updateSegmentMetadata_ = function(playlist, segment | ... | @@ -322,7 +322,7 @@ videojs.HlsHandler.prototype.updateSegmentMetadata_ = function(playlist, segment |
322 | 322 | ||
323 | // fix up segment durations based on segment end data | 323 | // fix up segment durations based on segment end data |
324 | if (!previousSegment) { | 324 | if (!previousSegment) { |
325 | // first segment is always has a start time of 0 making it's duration | 325 | // first segment is always has a start time of 0 making its duration |
326 | // equal to the segment end | 326 | // equal to the segment end |
327 | segment.duration = segment.end; | 327 | segment.duration = segment.end; |
328 | } else if (previousSegment.end) { | 328 | } else if (previousSegment.end) { |
... | @@ -337,9 +337,9 @@ videojs.HlsHandler.prototype.updateSegmentMetadata_ = function(playlist, segment | ... | @@ -337,9 +337,9 @@ videojs.HlsHandler.prototype.updateSegmentMetadata_ = function(playlist, segment |
337 | * @param playlist {object} a media playlist object | 337 | * @param playlist {object} a media playlist object |
338 | * @param segmentIndex {number} the index of segment we last appended | 338 | * @param segmentIndex {number} the index of segment we last appended |
339 | * @param currentBuffered {object} the buffered region that currentTime resides in | 339 | * @param currentBuffered {object} the buffered region that currentTime resides in |
340 | * @return {boolean} whether or not endOfStream was called on the MediaSource | 340 | * @return {boolean} whether the calling function should call endOfStream on the MediaSource |
341 | */ | 341 | */ |
342 | videojs.HlsHandler.prototype.maybeEndOfStream_ = function(playlist, segmentIndex, currentBuffered) { | 342 | videojs.HlsHandler.prototype.isEndOfStream_ = function(playlist, segmentIndex, currentBuffered) { |
343 | var | 343 | var |
344 | segments = playlist.segments, | 344 | segments = playlist.segments, |
345 | appendedLastSegment, | 345 | appendedLastSegment, |
... | @@ -358,14 +358,9 @@ videojs.HlsHandler.prototype.maybeEndOfStream_ = function(playlist, segmentIndex | ... | @@ -358,14 +358,9 @@ videojs.HlsHandler.prototype.maybeEndOfStream_ = function(playlist, segmentIndex |
358 | // if we've buffered to the end of the video, we need to call endOfStream | 358 | // if we've buffered to the end of the video, we need to call endOfStream |
359 | // so that MediaSources can trigger the `ended` event when it runs out of | 359 | // so that MediaSources can trigger the `ended` event when it runs out of |
360 | // buffered data instead of waiting for me | 360 | // buffered data instead of waiting for me |
361 | if (playlist.endList && | 361 | return playlist.endList && |
362 | this.mediaSource.readyState === 'open' && | 362 | this.mediaSource.readyState === 'open' && |
363 | (appendedLastSegment || | 363 | (appendedLastSegment || bufferedToEnd); |
364 | bufferedToEnd)) { | ||
365 | this.mediaSource.endOfStream(); | ||
366 | return true; | ||
367 | } | ||
368 | return false; | ||
369 | }; | 364 | }; |
370 | 365 | ||
371 | var parseCodecs = function(codecs) { | 366 | var parseCodecs = function(codecs) { |
... | @@ -1310,7 +1305,8 @@ videojs.HlsHandler.prototype.updateEndHandler_ = function () { | ... | @@ -1310,7 +1305,8 @@ videojs.HlsHandler.prototype.updateEndHandler_ = function () { |
1310 | currentMediaIndex, | 1305 | currentMediaIndex, |
1311 | currentBuffered, | 1306 | currentBuffered, |
1312 | seekable, | 1307 | seekable, |
1313 | timelineUpdate; | 1308 | timelineUpdate, |
1309 | isEndOfStream; | ||
1314 | 1310 | ||
1315 | // stop here if the update errored or was aborted | 1311 | // stop here if the update errored or was aborted |
1316 | if (!segmentInfo) { | 1312 | if (!segmentInfo) { |
... | @@ -1330,10 +1326,14 @@ videojs.HlsHandler.prototype.updateEndHandler_ = function () { | ... | @@ -1330,10 +1326,14 @@ videojs.HlsHandler.prototype.updateEndHandler_ = function () { |
1330 | segments = playlist.segments; | 1326 | segments = playlist.segments; |
1331 | currentMediaIndex = segmentInfo.mediaIndex + (segmentInfo.mediaSequence - playlist.mediaSequence); | 1327 | currentMediaIndex = segmentInfo.mediaIndex + (segmentInfo.mediaSequence - playlist.mediaSequence); |
1332 | currentBuffered = this.findBufferedRange_(); | 1328 | currentBuffered = this.findBufferedRange_(); |
1329 | isEndOfStream = this.isEndOfStream_(playlist, currentMediaIndex, currentBuffered); | ||
1333 | 1330 | ||
1334 | // if we switched renditions don't try to add segment timeline | 1331 | // if we switched renditions don't try to add segment timeline |
1335 | // information to the playlist | 1332 | // information to the playlist |
1336 | if (segmentInfo.playlist.uri !== this.playlists.media().uri) { | 1333 | if (segmentInfo.playlist.uri !== this.playlists.media().uri) { |
1334 | if (isEndOfStream) { | ||
1335 | return this.mediaSource.endOfStream(); | ||
1336 | } | ||
1337 | return this.fillBuffer(); | 1337 | return this.fillBuffer(); |
1338 | } | 1338 | } |
1339 | 1339 | ||
... | @@ -1366,8 +1366,8 @@ videojs.HlsHandler.prototype.updateEndHandler_ = function () { | ... | @@ -1366,8 +1366,8 @@ videojs.HlsHandler.prototype.updateEndHandler_ = function () { |
1366 | 1366 | ||
1367 | // If we decide to signal the end of stream, then we can return instead | 1367 | // If we decide to signal the end of stream, then we can return instead |
1368 | // of trying to fetch more segments | 1368 | // of trying to fetch more segments |
1369 | if (this.maybeEndOfStream_(playlist, currentMediaIndex, currentBuffered)) { | 1369 | if (isEndOfStream) { |
1370 | return; | 1370 | return this.mediaSource.endOfStream(); |
1371 | } | 1371 | } |
1372 | 1372 | ||
1373 | if (timelineUpdate !== null || | 1373 | if (timelineUpdate !== null || | ... | ... |
... | @@ -2144,6 +2144,79 @@ test('tracks segment end times as they are buffered', function() { | ... | @@ -2144,6 +2144,79 @@ test('tracks segment end times as they are buffered', function() { |
2144 | equal(player.tech_.hls.mediaSource.duration, 10 + 9.5, 'updated duration'); | 2144 | equal(player.tech_.hls.mediaSource.duration, 10 + 9.5, 'updated duration'); |
2145 | }); | 2145 | }); |
2146 | 2146 | ||
2147 | test('updates first segment duration as it is buffered', function() { | ||
2148 | var bufferEnd = 0; | ||
2149 | player.src({ | ||
2150 | src: 'media.m3u8', | ||
2151 | type: 'application/x-mpegURL' | ||
2152 | }); | ||
2153 | openMediaSource(player); | ||
2154 | |||
2155 | // as new segments are downloaded, the buffer end is updated | ||
2156 | player.tech_.buffered = function() { | ||
2157 | return videojs.createTimeRange(0, bufferEnd); | ||
2158 | }; | ||
2159 | requests.shift().respond(200, null, | ||
2160 | '#EXTM3U\n' + | ||
2161 | '#EXTINF:10,\n' + | ||
2162 | '0.ts\n' + | ||
2163 | '#EXTINF:10,\n' + | ||
2164 | '1.ts\n' + | ||
2165 | '#EXT-X-ENDLIST\n'); | ||
2166 | |||
2167 | // 0.ts is shorter than advertised | ||
2168 | standardXHRResponse(requests.shift()); | ||
2169 | equal(player.tech_.hls.mediaSource.duration, 20, 'original duration is from the m3u8'); | ||
2170 | equal(player.tech_.hls.playlists.media().segments[0].duration, 10, | ||
2171 | 'segment duration initially based on playlist'); | ||
2172 | |||
2173 | bufferEnd = 9.5; | ||
2174 | player.tech_.hls.sourceBuffer.trigger('update'); | ||
2175 | player.tech_.hls.sourceBuffer.trigger('updateend'); | ||
2176 | equal(player.tech_.hls.playlists.media().segments[0].duration, 9.5, | ||
2177 | 'updated segment duration'); | ||
2178 | }); | ||
2179 | |||
2180 | test('updates segment durations as they are buffered', function() { | ||
2181 | var bufferEnd = 0; | ||
2182 | player.src({ | ||
2183 | src: 'media.m3u8', | ||
2184 | type: 'application/x-mpegURL' | ||
2185 | }); | ||
2186 | openMediaSource(player); | ||
2187 | |||
2188 | // as new segments are downloaded, the buffer end is updated | ||
2189 | player.tech_.buffered = function() { | ||
2190 | return videojs.createTimeRange(0, bufferEnd); | ||
2191 | }; | ||
2192 | requests.shift().respond(200, null, | ||
2193 | '#EXTM3U\n' + | ||
2194 | '#EXTINF:10,\n' + | ||
2195 | '0.ts\n' + | ||
2196 | '#EXTINF:10,\n' + | ||
2197 | '1.ts\n' + | ||
2198 | '#EXT-X-ENDLIST\n'); | ||
2199 | |||
2200 | // 0.ts is shorter than advertised | ||
2201 | standardXHRResponse(requests.shift()); | ||
2202 | equal(player.tech_.hls.mediaSource.duration, 20, 'original duration is from the m3u8'); | ||
2203 | |||
2204 | equal(player.tech_.hls.playlists.media().segments[1].duration, 10, | ||
2205 | 'segment duration initially based on playlist'); | ||
2206 | |||
2207 | bufferEnd = 9.5; | ||
2208 | player.tech_.hls.sourceBuffer.trigger('update'); | ||
2209 | player.tech_.hls.sourceBuffer.trigger('updateend'); | ||
2210 | clock.tick(1); | ||
2211 | standardXHRResponse(requests.shift()); | ||
2212 | bufferEnd = 19; | ||
2213 | player.tech_.hls.sourceBuffer.trigger('update'); | ||
2214 | player.tech_.hls.sourceBuffer.trigger('updateend'); | ||
2215 | |||
2216 | equal(player.tech_.hls.playlists.media().segments[1].duration, 9.5, | ||
2217 | 'updated segment duration'); | ||
2218 | }); | ||
2219 | |||
2147 | QUnit.skip('seeking does not fail when targeted between segments', function() { | 2220 | QUnit.skip('seeking does not fail when targeted between segments', function() { |
2148 | var currentTime, segmentUrl; | 2221 | var currentTime, segmentUrl; |
2149 | player.src({ | 2222 | player.src({ |
... | @@ -2428,7 +2501,7 @@ test('can be disposed before finishing initialization', function() { | ... | @@ -2428,7 +2501,7 @@ test('can be disposed before finishing initialization', function() { |
2428 | } | 2501 | } |
2429 | }); | 2502 | }); |
2430 | 2503 | ||
2431 | test('calls ended() on the media source at the end of a playlist', function() { | 2504 | test('calls endOfStream on the media source after appending the last segment', function() { |
2432 | var endOfStreams = 0, buffered = [[]]; | 2505 | var endOfStreams = 0, buffered = [[]]; |
2433 | player.src({ | 2506 | player.src({ |
2434 | src: 'http://example.com/media.m3u8', | 2507 | src: 'http://example.com/media.m3u8', |
... | @@ -2441,11 +2514,15 @@ test('calls ended() on the media source at the end of a playlist', function() { | ... | @@ -2441,11 +2514,15 @@ test('calls ended() on the media source at the end of a playlist', function() { |
2441 | player.tech_.hls.mediaSource.endOfStream = function() { | 2514 | player.tech_.hls.mediaSource.endOfStream = function() { |
2442 | endOfStreams++; | 2515 | endOfStreams++; |
2443 | }; | 2516 | }; |
2517 | player.currentTime(20); | ||
2518 | clock.tick(1); | ||
2444 | // playlist response | 2519 | // playlist response |
2445 | requests.shift().respond(200, null, | 2520 | requests.shift().respond(200, null, |
2446 | '#EXTM3U\n' + | 2521 | '#EXTM3U\n' + |
2447 | '#EXTINF:10,\n' + | 2522 | '#EXTINF:10,\n' + |
2448 | '0.ts\n' + | 2523 | '0.ts\n' + |
2524 | '#EXTINF:10,\n' + | ||
2525 | '1.ts\n' + | ||
2449 | '#EXT-X-ENDLIST\n'); | 2526 | '#EXT-X-ENDLIST\n'); |
2450 | // segment response | 2527 | // segment response |
2451 | requests[0].response = new ArrayBuffer(17); | 2528 | requests[0].response = new ArrayBuffer(17); |
... | @@ -2454,7 +2531,50 @@ test('calls ended() on the media source at the end of a playlist', function() { | ... | @@ -2454,7 +2531,50 @@ test('calls ended() on the media source at the end of a playlist', function() { |
2454 | 2531 | ||
2455 | buffered =[[0, 10]]; | 2532 | buffered =[[0, 10]]; |
2456 | player.tech_.hls.sourceBuffer.trigger('updateend'); | 2533 | player.tech_.hls.sourceBuffer.trigger('updateend'); |
2457 | strictEqual(endOfStreams, 1, 'ended media source'); | 2534 | strictEqual(endOfStreams, 1, 'called endOfStream on the media source'); |
2535 | }); | ||
2536 | |||
2537 | test('calls endOfStream on the media source when the current buffer ends at duration', function() { | ||
2538 | var endOfStreams = 0, buffered = [[]]; | ||
2539 | player.src({ | ||
2540 | src: 'http://example.com/media.m3u8', | ||
2541 | type: 'application/vnd.apple.mpegurl' | ||
2542 | }); | ||
2543 | openMediaSource(player); | ||
2544 | player.tech_.buffered = function() { | ||
2545 | return videojs.createTimeRanges(buffered); | ||
2546 | }; | ||
2547 | player.tech_.hls.mediaSource.endOfStream = function() { | ||
2548 | endOfStreams++; | ||
2549 | }; | ||
2550 | player.currentTime(19); | ||
2551 | clock.tick(1); | ||
2552 | // playlist response | ||
2553 | requests.shift().respond(200, null, | ||
2554 | '#EXTM3U\n' + | ||
2555 | '#EXTINF:10,\n' + | ||
2556 | '0.ts\n' + | ||
2557 | '#EXTINF:10,\n' + | ||
2558 | '1.ts\n' + | ||
2559 | '#EXT-X-ENDLIST\n'); | ||
2560 | // segment response | ||
2561 | requests[0].response = new ArrayBuffer(17); | ||
2562 | requests.shift().respond(200, null, ''); | ||
2563 | strictEqual(endOfStreams, 0, 'waits for the buffer update to finish'); | ||
2564 | |||
2565 | buffered =[[10, 20]]; | ||
2566 | player.tech_.hls.sourceBuffer.trigger('updateend'); | ||
2567 | |||
2568 | player.currentTime(5); | ||
2569 | clock.tick(1); | ||
2570 | // segment response | ||
2571 | requests[0].response = new ArrayBuffer(17); | ||
2572 | requests.shift().respond(200, null, ''); | ||
2573 | |||
2574 | buffered =[[0, 20]]; | ||
2575 | player.tech_.hls.sourceBuffer.trigger('updateend'); | ||
2576 | |||
2577 | strictEqual(endOfStreams, 2, 'called endOfStream on the media source twice'); | ||
2458 | }); | 2578 | }); |
2459 | 2579 | ||
2460 | test('calling play() at the end of a video replays', function() { | 2580 | test('calling play() at the end of a video replays', function() { | ... | ... |
-
Please register or sign in to post a comment