Merge pull request #91 from videojs/discontinuity
Support for EXT-X-DISCONTINUITY
Showing
8 changed files
with
322 additions
and
43 deletions
... | @@ -298,6 +298,9 @@ hls.FlvTag = function(type, extraData) { | ... | @@ -298,6 +298,9 @@ hls.FlvTag = function(type, extraData) { |
298 | this.bytes[ 9] = 0; | 298 | this.bytes[ 9] = 0; |
299 | this.bytes[10] = 0; | 299 | this.bytes[10] = 0; |
300 | 300 | ||
301 | // Sometimes we're at the end of the view and have one slot to write a | ||
302 | // uint32, so, prepareWrite of count 4, since, view is uint8 | ||
303 | prepareWrite(this, 4); | ||
301 | this.view.setUint32(this.length, this.length); | 304 | this.view.setUint32(this.length, this.length); |
302 | this.length += 4; | 305 | this.length += 4; |
303 | this.position += 4; | 306 | this.position += 4; | ... | ... |
... | @@ -302,6 +302,7 @@ | ... | @@ -302,6 +302,7 @@ |
302 | 302 | ||
303 | h264Frame.endNalUnit(); | 303 | h264Frame.endNalUnit(); |
304 | this.tags.push(h264Frame); | 304 | this.tags.push(h264Frame); |
305 | |||
305 | } | 306 | } |
306 | 307 | ||
307 | h264Frame = null; | 308 | h264Frame = null; |
... | @@ -427,7 +428,9 @@ | ... | @@ -427,7 +428,9 @@ |
427 | 428 | ||
428 | // We did not find any start codes. Try again next packet | 429 | // We did not find any start codes. Try again next packet |
429 | state = 1; | 430 | state = 1; |
430 | h264Frame.writeBytes(data, start, length); | 431 | if (h264Frame) { |
432 | h264Frame.writeBytes(data, start, length); | ||
433 | } | ||
431 | return; | 434 | return; |
432 | case 3: | 435 | case 3: |
433 | // The next byte is the first byte of a NAL Unit | 436 | // The next byte is the first byte of a NAL Unit | ... | ... |
... | @@ -273,6 +273,14 @@ | ... | @@ -273,6 +273,14 @@ |
273 | }); | 273 | }); |
274 | return; | 274 | return; |
275 | } | 275 | } |
276 | match = (/^#EXT-X-DISCONTINUITY/).exec(line); | ||
277 | if (match) { | ||
278 | this.trigger('data', { | ||
279 | type: 'tag', | ||
280 | tagType: 'discontinuity' | ||
281 | }); | ||
282 | return; | ||
283 | } | ||
276 | 284 | ||
277 | // unknown tag type | 285 | // unknown tag type |
278 | this.trigger('data', { | 286 | this.trigger('data', { |
... | @@ -399,6 +407,9 @@ | ... | @@ -399,6 +407,9 @@ |
399 | currentUri.attributes = mergeOptions(currentUri.attributes, | 407 | currentUri.attributes = mergeOptions(currentUri.attributes, |
400 | entry.attributes); | 408 | entry.attributes); |
401 | }, | 409 | }, |
410 | 'discontinuity': function() { | ||
411 | currentUri.discontinuity = true; | ||
412 | }, | ||
402 | 'targetduration': function() { | 413 | 'targetduration': function() { |
403 | if (!isFinite(entry.duration) || entry.duration < 0) { | 414 | if (!isFinite(entry.duration) || entry.duration < 0) { |
404 | this.trigger('warn', { | 415 | this.trigger('warn', { | ... | ... |
... | @@ -7,6 +7,7 @@ | ... | @@ -7,6 +7,7 @@ |
7 | */ | 7 | */ |
8 | 8 | ||
9 | (function(window, videojs, document, undefined) { | 9 | (function(window, videojs, document, undefined) { |
10 | 'use strict'; | ||
10 | 11 | ||
11 | var | 12 | var |
12 | 13 | ||
... | @@ -145,22 +146,42 @@ var | ... | @@ -145,22 +146,42 @@ var |
145 | }, | 146 | }, |
146 | 147 | ||
147 | /** | 148 | /** |
149 | * Calculate the duration of a playlist from a given start index to a given | ||
150 | * end index. | ||
151 | * @param playlist {object} a media playlist object | ||
152 | * @param startIndex {number} an inclusive lower boundary for the playlist. | ||
153 | * Defaults to 0. | ||
154 | * @param endIndex {number} an exclusive upper boundary for the playlist. | ||
155 | * Defaults to playlist length. | ||
156 | * @return {number} the duration between the start index and end index. | ||
157 | */ | ||
158 | duration = function(playlist, startIndex, endIndex) { | ||
159 | var dur = 0, | ||
160 | segment, | ||
161 | i; | ||
162 | |||
163 | startIndex = startIndex || 0; | ||
164 | endIndex = endIndex !== undefined ? endIndex : (playlist.segments || []).length; | ||
165 | i = endIndex - 1; | ||
166 | |||
167 | for (; i >= startIndex; i--) { | ||
168 | segment = playlist.segments[i]; | ||
169 | dur += segment.duration || playlist.targetDuration || 0; | ||
170 | } | ||
171 | |||
172 | return dur; | ||
173 | }, | ||
174 | |||
175 | /** | ||
148 | * Calculate the total duration for a playlist based on segment metadata. | 176 | * Calculate the total duration for a playlist based on segment metadata. |
149 | * @param playlist {object} a media playlist object | 177 | * @param playlist {object} a media playlist object |
150 | * @return {number} the currently known duration, in seconds | 178 | * @return {number} the currently known duration, in seconds |
151 | */ | 179 | */ |
152 | totalDuration = function(playlist) { | 180 | totalDuration = function(playlist) { |
153 | var | ||
154 | duration = 0, | ||
155 | segment, | ||
156 | i; | ||
157 | |||
158 | if (!playlist) { | 181 | if (!playlist) { |
159 | return 0; | 182 | return 0; |
160 | } | 183 | } |
161 | 184 | ||
162 | i = (playlist.segments || []).length; | ||
163 | |||
164 | // if present, use the duration specified in the playlist | 185 | // if present, use the duration specified in the playlist |
165 | if (playlist.totalDuration) { | 186 | if (playlist.totalDuration) { |
166 | return playlist.totalDuration; | 187 | return playlist.totalDuration; |
... | @@ -171,11 +192,7 @@ var | ... | @@ -171,11 +192,7 @@ var |
171 | return window.Infinity; | 192 | return window.Infinity; |
172 | } | 193 | } |
173 | 194 | ||
174 | while (i--) { | 195 | return duration(playlist); |
175 | segment = playlist.segments[i]; | ||
176 | duration += segment.duration || playlist.targetDuration || 0; | ||
177 | } | ||
178 | return duration; | ||
179 | }, | 196 | }, |
180 | 197 | ||
181 | resolveUrl, | 198 | resolveUrl, |
... | @@ -184,10 +201,12 @@ var | ... | @@ -184,10 +201,12 @@ var |
184 | var | 201 | var |
185 | segmentParser = new videojs.Hls.SegmentParser(), | 202 | segmentParser = new videojs.Hls.SegmentParser(), |
186 | settings = videojs.util.mergeOptions({}, player.options().hls), | 203 | settings = videojs.util.mergeOptions({}, player.options().hls), |
204 | segmentBuffer = [], | ||
187 | 205 | ||
188 | lastSeekedTime, | 206 | lastSeekedTime, |
189 | segmentXhr, | 207 | segmentXhr, |
190 | fillBuffer, | 208 | fillBuffer, |
209 | drainBuffer, | ||
191 | updateDuration; | 210 | updateDuration; |
192 | 211 | ||
193 | 212 | ||
... | @@ -197,6 +216,7 @@ var | ... | @@ -197,6 +216,7 @@ var |
197 | } | 216 | } |
198 | return this.el().vjs_getProperty('currentTime'); | 217 | return this.el().vjs_getProperty('currentTime'); |
199 | }; | 218 | }; |
219 | |||
200 | player.hls.setCurrentTime = function(currentTime) { | 220 | player.hls.setCurrentTime = function(currentTime) { |
201 | if (!(this.playlists && this.playlists.media())) { | 221 | if (!(this.playlists && this.playlists.media())) { |
202 | // return immediately if the metadata is not ready yet | 222 | // return immediately if the metadata is not ready yet |
... | @@ -219,6 +239,9 @@ var | ... | @@ -219,6 +239,9 @@ var |
219 | segmentXhr.abort(); | 239 | segmentXhr.abort(); |
220 | } | 240 | } |
221 | 241 | ||
242 | // clear out any buffered segments | ||
243 | segmentBuffer = []; | ||
244 | |||
222 | // begin filling the buffer at the new position | 245 | // begin filling the buffer at the new position |
223 | fillBuffer(currentTime * 1000); | 246 | fillBuffer(currentTime * 1000); |
224 | }; | 247 | }; |
... | @@ -367,6 +390,8 @@ var | ... | @@ -367,6 +390,8 @@ var |
367 | responseType: 'arraybuffer', | 390 | responseType: 'arraybuffer', |
368 | withCredentials: settings.withCredentials | 391 | withCredentials: settings.withCredentials |
369 | }, function(error, url) { | 392 | }, function(error, url) { |
393 | var tags; | ||
394 | |||
370 | // the segment request is no longer outstanding | 395 | // the segment request is no longer outstanding |
371 | segmentXhr = null; | 396 | segmentXhr = null; |
372 | 397 | ||
... | @@ -395,45 +420,101 @@ var | ... | @@ -395,45 +420,101 @@ var |
395 | segmentParser.parseSegmentBinaryData(new Uint8Array(this.response)); | 420 | segmentParser.parseSegmentBinaryData(new Uint8Array(this.response)); |
396 | segmentParser.flushTags(); | 421 | segmentParser.flushTags(); |
397 | 422 | ||
398 | // if we're refilling the buffer after a seek, scan through the muxed | 423 | // package up all the work to append the segment |
399 | // FLV tags until we find the one that is closest to the desired | 424 | // if the segment is the start of a timestamp discontinuity, |
400 | // playback time | 425 | // we have to wait until the sourcebuffer is empty before |
401 | if (typeof offset === 'number') { | 426 | // aborting the source buffer processing |
402 | (function() { | 427 | tags = []; |
403 | var tag = segmentParser.getTags()[0]; | ||
404 | |||
405 | for (; tag.pts < offset; tag = segmentParser.getTags()[0]) { | ||
406 | segmentParser.getNextTag(); | ||
407 | } | ||
408 | |||
409 | // tell the SWF where we will be seeking to | ||
410 | player.hls.el().vjs_setProperty('currentTime', tag.pts * 0.001); | ||
411 | lastSeekedTime = null; | ||
412 | })(); | ||
413 | } | ||
414 | |||
415 | while (segmentParser.tagsAvailable()) { | 428 | while (segmentParser.tagsAvailable()) { |
416 | // queue up the bytes to be appended to the SourceBuffer | 429 | tags.push(segmentParser.getNextTag()); |
417 | // the queue gives control back to the browser between tags | ||
418 | // so that large segments don't cause a "hiccup" in playback | ||
419 | |||
420 | player.hls.sourceBuffer.appendBuffer(segmentParser.getNextTag().bytes, | ||
421 | player); | ||
422 | |||
423 | } | 430 | } |
431 | segmentBuffer.push({ | ||
432 | mediaIndex: player.hls.mediaIndex, | ||
433 | playlist: player.hls.playlists.media(), | ||
434 | offset: offset, | ||
435 | tags: tags | ||
436 | }); | ||
437 | drainBuffer(); | ||
424 | 438 | ||
425 | player.hls.mediaIndex++; | 439 | player.hls.mediaIndex++; |
426 | 440 | ||
427 | if (player.hls.mediaIndex === player.hls.playlists.media().segments.length) { | ||
428 | mediaSource.endOfStream(); | ||
429 | } | ||
430 | |||
431 | // figure out what stream the next segment should be downloaded from | 441 | // figure out what stream the next segment should be downloaded from |
432 | // with the updated bandwidth information | 442 | // with the updated bandwidth information |
433 | player.hls.playlists.media(player.hls.selectPlaylist()); | 443 | player.hls.playlists.media(player.hls.selectPlaylist()); |
434 | }); | 444 | }); |
435 | }; | 445 | }; |
436 | 446 | ||
447 | drainBuffer = function(event) { | ||
448 | var | ||
449 | i = 0, | ||
450 | mediaIndex, | ||
451 | playlist, | ||
452 | offset, | ||
453 | tags, | ||
454 | segment, | ||
455 | |||
456 | ptsTime, | ||
457 | segmentOffset; | ||
458 | |||
459 | if (!segmentBuffer.length) { | ||
460 | return; | ||
461 | } | ||
462 | |||
463 | mediaIndex = segmentBuffer[0].mediaIndex; | ||
464 | playlist = segmentBuffer[0].playlist; | ||
465 | offset = segmentBuffer[0].offset; | ||
466 | tags = segmentBuffer[0].tags; | ||
467 | segment = playlist.segments[mediaIndex]; | ||
468 | |||
469 | event = event || {}; | ||
470 | segmentOffset = duration(playlist, 0, mediaIndex) * 1000; | ||
471 | |||
472 | // abort() clears any data queued in the source buffer so wait | ||
473 | // until it empties before calling it when a discontinuity is | ||
474 | // next in the buffer | ||
475 | if (segment.discontinuity) { | ||
476 | if (event.type !== 'waiting') { | ||
477 | return; | ||
478 | } | ||
479 | player.hls.sourceBuffer.abort(); | ||
480 | // tell the SWF where playback is continuing in the stitched timeline | ||
481 | player.hls.el().vjs_setProperty('currentTime', segmentOffset * 0.001); | ||
482 | } | ||
483 | |||
484 | // if we're refilling the buffer after a seek, scan through the muxed | ||
485 | // FLV tags until we find the one that is closest to the desired | ||
486 | // playback time | ||
487 | if (typeof offset === 'number') { | ||
488 | ptsTime = offset - segmentOffset + tags[0].pts; | ||
489 | |||
490 | while (tags[i].pts < ptsTime) { | ||
491 | i++; | ||
492 | } | ||
493 | |||
494 | // tell the SWF where we will be seeking to | ||
495 | player.hls.el().vjs_setProperty('currentTime', (tags[i].pts - tags[0].pts + segmentOffset) * 0.001); | ||
496 | |||
497 | tags = tags.slice(i); | ||
498 | |||
499 | lastSeekedTime = null; | ||
500 | } | ||
501 | |||
502 | for (i = 0; i < tags.length; i++) { | ||
503 | // queue up the bytes to be appended to the SourceBuffer | ||
504 | // the queue gives control back to the browser between tags | ||
505 | // so that large segments don't cause a "hiccup" in playback | ||
506 | |||
507 | player.hls.sourceBuffer.appendBuffer(tags[i].bytes, player); | ||
508 | } | ||
509 | |||
510 | // we're done processing this segment | ||
511 | segmentBuffer.shift(); | ||
512 | |||
513 | if (mediaIndex === playlist.segments.length) { | ||
514 | mediaSource.endOfStream(); | ||
515 | } | ||
516 | }; | ||
517 | |||
437 | // load the MediaSource into the player | 518 | // load the MediaSource into the player |
438 | mediaSource.addEventListener('sourceopen', function() { | 519 | mediaSource.addEventListener('sourceopen', function() { |
439 | // construct the video data buffer and set the appropriate MIME type | 520 | // construct the video data buffer and set the appropriate MIME type |
... | @@ -450,9 +531,12 @@ var | ... | @@ -450,9 +531,12 @@ var |
450 | player.hls.playlists.on('loadedmetadata', function() { | 531 | player.hls.playlists.on('loadedmetadata', function() { |
451 | oldMediaPlaylist = player.hls.playlists.media(); | 532 | oldMediaPlaylist = player.hls.playlists.media(); |
452 | 533 | ||
453 | // periodicaly check if the buffer needs to be refilled | 534 | // periodically check if new data needs to be downloaded or |
535 | // buffered data should be appended to the source buffer | ||
454 | fillBuffer(); | 536 | fillBuffer(); |
455 | player.on('timeupdate', fillBuffer); | 537 | player.on('timeupdate', fillBuffer); |
538 | player.on('timeupdate', drainBuffer); | ||
539 | player.on('waiting', drainBuffer); | ||
456 | 540 | ||
457 | player.trigger('loadedmetadata'); | 541 | player.trigger('loadedmetadata'); |
458 | }); | 542 | }); | ... | ... |
test/manifest/discontinuity.json
0 → 100644
1 | { | ||
2 | "allowCache": true, | ||
3 | "mediaSequence": 0, | ||
4 | "segments": [ | ||
5 | { | ||
6 | "duration": 10, | ||
7 | "uri": "001.ts" | ||
8 | }, | ||
9 | { | ||
10 | "duration": 19, | ||
11 | "uri": "002.ts" | ||
12 | }, | ||
13 | { | ||
14 | "discontinuity": true, | ||
15 | "duration": 10, | ||
16 | "uri": "003.ts" | ||
17 | }, | ||
18 | { | ||
19 | "duration": 11, | ||
20 | "uri": "004.ts" | ||
21 | }, | ||
22 | { | ||
23 | "discontinuity": true, | ||
24 | "duration": 10, | ||
25 | "uri": "005.ts" | ||
26 | }, | ||
27 | { | ||
28 | "duration": 10, | ||
29 | "uri": "006.ts" | ||
30 | }, | ||
31 | { | ||
32 | "duration": 10, | ||
33 | "uri": "007.ts" | ||
34 | }, | ||
35 | { | ||
36 | "discontinuity": true, | ||
37 | "duration": 10, | ||
38 | "uri": "008.ts" | ||
39 | }, | ||
40 | { | ||
41 | "duration": 16, | ||
42 | "uri": "009.ts" | ||
43 | } | ||
44 | ], | ||
45 | "targetDuration": 19, | ||
46 | "endList": true | ||
47 | } |
test/manifest/discontinuity.m3u8
0 → 100644
1 | #EXTM3U | ||
2 | #EXT-X-VERSION:3 | ||
3 | #EXT-X-TARGETDURATION:19 | ||
4 | #EXT-X-MEDIA-SEQUENCE:0 | ||
5 | #EXTINF:10,0 | ||
6 | 001.ts | ||
7 | #EXTINF:19,0 | ||
8 | 002.ts | ||
9 | #EXT-X-DISCONTINUITY | ||
10 | #EXTINF:10,0 | ||
11 | 003.ts | ||
12 | #EXTINF:11,0 | ||
13 | 004.ts | ||
14 | #EXT-X-DISCONTINUITY | ||
15 | #EXTINF:10,0 | ||
16 | 005.ts | ||
17 | #EXTINF:10,0 | ||
18 | 006.ts | ||
19 | #EXTINF:10,0 | ||
20 | 007.ts | ||
21 | #EXT-X-DISCONTINUITY | ||
22 | #EXTINF:10,0 | ||
23 | 008.ts | ||
24 | #EXTINF:16,0 | ||
25 | 009.ts | ||
26 | #EXT-X-ENDLIST |
... | @@ -124,6 +124,8 @@ | ... | @@ -124,6 +124,8 @@ |
124 | 0x12: 'metadata' | 124 | 0x12: 'metadata' |
125 | }; | 125 | }; |
126 | 126 | ||
127 | videojs.log = console.log.bind(console); | ||
128 | |||
127 | original.addEventListener('change', function() { | 129 | original.addEventListener('change', function() { |
128 | var reader = new FileReader(); | 130 | var reader = new FileReader(); |
129 | reader.addEventListener('loadend', function() { | 131 | reader.addEventListener('loadend', function() { | ... | ... |
... | @@ -840,8 +840,11 @@ test('calls abort() on the SourceBuffer before seeking', function() { | ... | @@ -840,8 +840,11 @@ test('calls abort() on the SourceBuffer before seeking', function() { |
840 | standardXHRResponse(requests[0]); | 840 | standardXHRResponse(requests[0]); |
841 | standardXHRResponse(requests[1]); | 841 | standardXHRResponse(requests[1]); |
842 | 842 | ||
843 | // seek to 7s | 843 | // drainBuffer() uses the first PTS value to account for any timestamp discontinuities in the stream |
844 | // adding a tag with a PTS of zero looks like a stream with no discontinuities | ||
845 | tags.push({ pts: 0, bytes: 0 }); | ||
844 | tags.push({ pts: 7000, bytes: 7 }); | 846 | tags.push({ pts: 7000, bytes: 7 }); |
847 | // seek to 7s | ||
845 | player.currentTime(7); | 848 | player.currentTime(7); |
846 | standardXHRResponse(requests[2]); | 849 | standardXHRResponse(requests[2]); |
847 | 850 | ||
... | @@ -1047,6 +1050,106 @@ test('does not break if the playlist has no segments', function() { | ... | @@ -1047,6 +1050,106 @@ test('does not break if the playlist has no segments', function() { |
1047 | strictEqual(requests.length, 1, 'no requests for non-existent segments were queued'); | 1050 | strictEqual(requests.length, 1, 'no requests for non-existent segments were queued'); |
1048 | }); | 1051 | }); |
1049 | 1052 | ||
1053 | test('waits until the buffer is empty before appending bytes at a discontinuity', function() { | ||
1054 | var aborts = 0, setTime, currentTime, bufferEnd; | ||
1055 | |||
1056 | player.src({ | ||
1057 | src: 'disc.m3u8', | ||
1058 | type: 'application/vnd.apple.mpegurl' | ||
1059 | }); | ||
1060 | player.hls.mediaSource.trigger({ | ||
1061 | type: 'sourceopen' | ||
1062 | }); | ||
1063 | player.currentTime = function() { return currentTime; }; | ||
1064 | player.buffered = function() { | ||
1065 | return videojs.createTimeRange(0, bufferEnd); | ||
1066 | }; | ||
1067 | player.hls.sourceBuffer.abort = function() { | ||
1068 | aborts++; | ||
1069 | }; | ||
1070 | player.hls.el().vjs_setProperty = function(name, value) { | ||
1071 | if (name === 'currentTime') { | ||
1072 | return setTime = value; | ||
1073 | } | ||
1074 | }; | ||
1075 | |||
1076 | requests.pop().respond(200, null, | ||
1077 | '#EXTM3U\n' + | ||
1078 | '#EXTINF:10,0\n' + | ||
1079 | '1.ts\n' + | ||
1080 | '#EXT-X-DISCONTINUITY\n' + | ||
1081 | '#EXTINF:10,0\n' + | ||
1082 | '2.ts\n'); | ||
1083 | standardXHRResponse(requests.pop()); | ||
1084 | |||
1085 | // play to 6s to trigger the next segment request | ||
1086 | currentTime = 6; | ||
1087 | bufferEnd = 10; | ||
1088 | player.trigger('timeupdate'); | ||
1089 | strictEqual(aborts, 0, 'no aborts before the buffer empties'); | ||
1090 | |||
1091 | standardXHRResponse(requests.pop()); | ||
1092 | strictEqual(aborts, 0, 'no aborts before the buffer empties'); | ||
1093 | |||
1094 | // pretend the buffer has emptied | ||
1095 | player.trigger('waiting'); | ||
1096 | strictEqual(aborts, 1, 'aborted before appending the new segment'); | ||
1097 | strictEqual(setTime, 10, 'updated the time after crossing the discontinuity'); | ||
1098 | }); | ||
1099 | |||
1100 | test('clears the segment buffer on seek', function() { | ||
1101 | var aborts = 0, tags = [], currentTime, bufferEnd, oldCurrentTime; | ||
1102 | |||
1103 | videojs.Hls.SegmentParser = mockSegmentParser(tags); | ||
1104 | |||
1105 | player.src({ | ||
1106 | src: 'disc.m3u8', | ||
1107 | type: 'application/vnd.apple.mpegurl' | ||
1108 | }); | ||
1109 | player.hls.mediaSource.trigger({ | ||
1110 | type: 'sourceopen' | ||
1111 | }); | ||
1112 | oldCurrentTime = player.currentTime; | ||
1113 | player.currentTime = function(time) { | ||
1114 | if (time !== undefined) { | ||
1115 | return oldCurrentTime.call(player, time); | ||
1116 | } | ||
1117 | return currentTime; | ||
1118 | }; | ||
1119 | player.buffered = function() { | ||
1120 | return videojs.createTimeRange(0, bufferEnd); | ||
1121 | }; | ||
1122 | player.hls.sourceBuffer.abort = function() { | ||
1123 | aborts++; | ||
1124 | }; | ||
1125 | |||
1126 | requests.pop().respond(200, null, | ||
1127 | '#EXTM3U\n' + | ||
1128 | '#EXTINF:10,0\n' + | ||
1129 | '1.ts\n' + | ||
1130 | '#EXT-X-DISCONTINUITY\n' + | ||
1131 | '#EXTINF:10,0\n' + | ||
1132 | '2.ts\n'); | ||
1133 | standardXHRResponse(requests.pop()); | ||
1134 | |||
1135 | // play to 6s to trigger the next segment request | ||
1136 | currentTime = 6; | ||
1137 | bufferEnd = 10; | ||
1138 | player.trigger('timeupdate'); | ||
1139 | |||
1140 | standardXHRResponse(requests.pop()); | ||
1141 | |||
1142 | // seek back to the beginning | ||
1143 | player.currentTime(0); | ||
1144 | tags.push({ pts: 0, bytes: 0 }); | ||
1145 | standardXHRResponse(requests.pop()); | ||
1146 | strictEqual(aborts, 1, 'aborted once for the seek'); | ||
1147 | |||
1148 | // the source buffer empties. is 2.ts still in the segment buffer? | ||
1149 | player.trigger('waiting'); | ||
1150 | strictEqual(aborts, 1, 'cleared the segment buffer on a seek'); | ||
1151 | }); | ||
1152 | |||
1050 | test('disposes the playlist loader', function() { | 1153 | test('disposes the playlist loader', function() { |
1051 | var disposes = 0, player, loaderDispose; | 1154 | var disposes = 0, player, loaderDispose; |
1052 | player = createPlayer(); | 1155 | player = createPlayer(); | ... | ... |
-
Please register or sign in to post a comment