Fix time correction.. some more (#712)
* Fudge segments that are reported as having a zero-second duration * The fetcher logic basically ignores segments with a duration of zero. Give them a tiny duration so that the fetcher will "see" these segments. * Added tests for and fixed getSegmentBufferedPercent_ calculations * Now returns the percent buffered of the entire segment duration instead of the "adjusted" duration * Handles segments reported as having a zero-duration * Reduced the number of segments that we will attempt to "timeCorrect" when the segment chosen by `checkBuffer_` is already more than 90% buffered to 1 * No longer trigger errors from `timeCorrection_` handling, returning to the previous behavior * Use the tech's setCurrentTime function in segment loaders * Moved getSegmentBufferedPercent to `Ranges` module * Moved correction for zero-duration segments from parse-stream to parser proper
Showing
9 changed files
with
263 additions
and
99 deletions
... | @@ -108,10 +108,17 @@ export default class Parser extends Stream { | ... | @@ -108,10 +108,17 @@ export default class Parser extends Stream { |
108 | message: 'defaulting discontinuity sequence to zero' | 108 | message: 'defaulting discontinuity sequence to zero' |
109 | }); | 109 | }); |
110 | } | 110 | } |
111 | if (entry.duration >= 0) { | 111 | if (entry.duration > 0) { |
112 | currentUri.duration = entry.duration; | 112 | currentUri.duration = entry.duration; |
113 | } | 113 | } |
114 | 114 | ||
115 | if (entry.duration === 0) { | ||
116 | currentUri.duration = 0.01; | ||
117 | this.trigger('info', { | ||
118 | message: 'updating zero segment duration to a small value' | ||
119 | }); | ||
120 | } | ||
121 | |||
115 | this.manifest.segments = uris; | 122 | this.manifest.segments = uris; |
116 | }, | 123 | }, |
117 | key() { | 124 | key() { | ... | ... |
... | @@ -72,7 +72,7 @@ export default class MasterPlaylistController extends videojs.EventTarget { | ... | @@ -72,7 +72,7 @@ export default class MasterPlaylistController extends videojs.EventTarget { |
72 | withCredentials: this.withCredentials, | 72 | withCredentials: this.withCredentials, |
73 | seekable: () => this.seekable(), | 73 | seekable: () => this.seekable(), |
74 | seeking: () => this.tech_.seeking(), | 74 | seeking: () => this.tech_.seeking(), |
75 | setCurrentTime: (a) => this.setCurrentTime(a), | 75 | setCurrentTime: (a) => this.tech_.setCurrentTime(a), |
76 | hasPlayed: () => this.tech_.played().length !== 0, | 76 | hasPlayed: () => this.tech_.played().length !== 0, |
77 | bandwidth | 77 | bandwidth |
78 | }; | 78 | }; | ... | ... |
... | @@ -10,6 +10,16 @@ import videojs from 'video.js'; | ... | @@ -10,6 +10,16 @@ import videojs from 'video.js'; |
10 | // Fudge factor to account for TimeRanges rounding | 10 | // Fudge factor to account for TimeRanges rounding |
11 | const TIME_FUDGE_FACTOR = 1 / 30; | 11 | const TIME_FUDGE_FACTOR = 1 / 30; |
12 | 12 | ||
13 | /** | ||
14 | * Clamps a value to within a range | ||
15 | * @param {Number} num - the value to clamp | ||
16 | * @param {Number} start - the start of the range to clamp within, inclusive | ||
17 | * @param {Number} end - the end of the range to clamp within, inclusive | ||
18 | * @return {Number} | ||
19 | */ | ||
20 | const clamp = function(num, [start, end]) { | ||
21 | return Math.min(Math.max(start, num), end); | ||
22 | }; | ||
13 | const filterRanges = function(timeRanges, predicate) { | 23 | const filterRanges = function(timeRanges, predicate) { |
14 | let results = []; | 24 | let results = []; |
15 | let i; | 25 | let i; |
... | @@ -184,27 +194,81 @@ const bufferIntersection = function(bufferA, bufferB) { | ... | @@ -184,27 +194,81 @@ const bufferIntersection = function(bufferA, bufferB) { |
184 | /** | 194 | /** |
185 | * Calculates the percentage of `segmentRange` that overlaps the | 195 | * Calculates the percentage of `segmentRange` that overlaps the |
186 | * `buffered` time ranges. | 196 | * `buffered` time ranges. |
187 | * @param {TimeRanges} segmentRange - the time range that the segment covers | 197 | * @param {TimeRanges} segmentRange - the time range that the segment |
198 | * covers adjusted according to currentTime | ||
199 | * @param {TimeRanges} referenceRange - the original time range that the | ||
200 | * segment covers | ||
188 | * @param {TimeRanges} buffered - the currently buffered time ranges | 201 | * @param {TimeRanges} buffered - the currently buffered time ranges |
189 | * @returns {Number} percent of the segment currently buffered | 202 | * @returns {Number} percent of the segment currently buffered |
190 | */ | 203 | */ |
191 | const calculateBufferedPercent = function(segmentRange, buffered) { | 204 | const calculateBufferedPercent = function(segmentRange, referenceRange, buffered) { |
205 | let referenceDuration = referenceRange.end(0) - referenceRange.start(0); | ||
192 | let segmentDuration = segmentRange.end(0) - segmentRange.start(0); | 206 | let segmentDuration = segmentRange.end(0) - segmentRange.start(0); |
193 | let intersection = bufferIntersection(segmentRange, buffered); | 207 | let intersection = bufferIntersection(segmentRange, buffered); |
194 | let overlapDuration = 0; | ||
195 | let count = intersection.length; | 208 | let count = intersection.length; |
196 | 209 | ||
197 | while (count--) { | 210 | while (count--) { |
198 | overlapDuration += intersection.end(count) - intersection.start(count); | 211 | segmentDuration -= intersection.end(count) - intersection.start(count); |
212 | } | ||
213 | return (referenceDuration - segmentDuration) / referenceDuration * 100; | ||
214 | }; | ||
215 | |||
216 | /** | ||
217 | * Return the amount of a segment specified by the mediaIndex overlaps | ||
218 | * the current buffered content. | ||
219 | * | ||
220 | * @param {Number} startOfSegment - the time where the segment begins | ||
221 | * @param {Number} segmentDuration - the duration of the segment in seconds | ||
222 | * @param {TimeRanges} buffered - the state of the buffer | ||
223 | * @returns {Number} percentage of the segment's time range that is | ||
224 | * already in `buffered` | ||
225 | */ | ||
226 | const getSegmentBufferedPercent = function(startOfSegment, | ||
227 | segmentDuration, | ||
228 | currentTime, | ||
229 | buffered) { | ||
230 | let endOfSegment = startOfSegment + segmentDuration; | ||
231 | |||
232 | // The entire time range of the segment | ||
233 | let originalSegmentRange = videojs.createTimeRanges([[ | ||
234 | startOfSegment, | ||
235 | endOfSegment | ||
236 | ]]); | ||
237 | |||
238 | // The adjusted segment time range that is setup such that it starts | ||
239 | // no earlier than currentTime | ||
240 | // Flash has no notion of a back-buffer so adjustedSegmentRange adjusts | ||
241 | // for that and the function will still return 100% if a only half of a | ||
242 | // segment is actually in the buffer as long as the currentTime is also | ||
243 | // half-way through the segment | ||
244 | let adjustedSegmentRange = videojs.createTimeRanges([[ | ||
245 | clamp(startOfSegment, [currentTime, endOfSegment]), | ||
246 | endOfSegment | ||
247 | ]]); | ||
248 | |||
249 | // This condition happens when the currentTime is beyond the segment's | ||
250 | // end time | ||
251 | if (adjustedSegmentRange.start(0) === adjustedSegmentRange.end(0)) { | ||
252 | return 0; | ||
253 | } | ||
254 | |||
255 | let percent = calculateBufferedPercent(adjustedSegmentRange, | ||
256 | originalSegmentRange, | ||
257 | buffered); | ||
258 | |||
259 | // If the segment is reported as having a zero duration, return 0% | ||
260 | // since it is likely that we will need to fetch the segment | ||
261 | if (isNaN(percent) || percent === Infinity || percent === -Infinity) { | ||
262 | return 0; | ||
199 | } | 263 | } |
200 | 264 | ||
201 | return (overlapDuration / segmentDuration) * 100; | 265 | return percent; |
202 | }; | 266 | }; |
203 | 267 | ||
204 | export default { | 268 | export default { |
205 | findRange, | 269 | findRange, |
206 | findNextRange, | 270 | findNextRange, |
207 | findSoleUncommonTimeRangesEnd, | 271 | findSoleUncommonTimeRangesEnd, |
208 | calculateBufferedPercent, | 272 | getSegmentBufferedPercent, |
209 | TIME_FUDGE_FACTOR | 273 | TIME_FUDGE_FACTOR |
210 | }; | 274 | }; | ... | ... |
... | @@ -313,29 +313,6 @@ export default class SegmentLoader extends videojs.EventTarget { | ... | @@ -313,29 +313,6 @@ export default class SegmentLoader extends videojs.EventTarget { |
313 | } | 313 | } |
314 | 314 | ||
315 | /** | 315 | /** |
316 | * Return the amount of a segment specified by the mediaIndex overlaps | ||
317 | * the current buffered content. | ||
318 | * | ||
319 | * @param {Object} playlist the playlist object to fetch segments from | ||
320 | * @param {Number} mediaIndex the index of the segment in the playlist | ||
321 | * @param {TimeRanges} buffered the state of the buffer | ||
322 | * @returns {Number} percentage of the segment's time range that is | ||
323 | * already in `buffered` | ||
324 | */ | ||
325 | getSegmentBufferedPercent_(playlist, mediaIndex, currentTime, buffered) { | ||
326 | let segment = playlist.segments[mediaIndex]; | ||
327 | let startOfSegment = duration(playlist, | ||
328 | playlist.mediaSequence + mediaIndex, | ||
329 | this.expired_); | ||
330 | let segmentRange = videojs.createTimeRanges([[ | ||
331 | Math.max(currentTime, startOfSegment), | ||
332 | startOfSegment + segment.duration | ||
333 | ]]); | ||
334 | |||
335 | return Ranges.calculateBufferedPercent(segmentRange, buffered); | ||
336 | } | ||
337 | |||
338 | /** | ||
339 | * Determines what segment request should be made, given current | 316 | * Determines what segment request should be made, given current |
340 | * playback state. | 317 | * playback state. |
341 | * | 318 | * |
... | @@ -432,7 +409,9 @@ export default class SegmentLoader extends videojs.EventTarget { | ... | @@ -432,7 +409,9 @@ export default class SegmentLoader extends videojs.EventTarget { |
432 | // to the source buffer | 409 | // to the source buffer |
433 | timestampOffset, | 410 | timestampOffset, |
434 | // The timeline that the segment is in | 411 | // The timeline that the segment is in |
435 | timeline: segment.timeline | 412 | timeline: segment.timeline, |
413 | // The expected duration of the segment in seconds | ||
414 | duration: segment.duration | ||
436 | }; | 415 | }; |
437 | } | 416 | } |
438 | 417 | ||
... | @@ -471,12 +450,23 @@ export default class SegmentLoader extends videojs.EventTarget { | ... | @@ -471,12 +450,23 @@ export default class SegmentLoader extends videojs.EventTarget { |
471 | return; | 450 | return; |
472 | } | 451 | } |
473 | 452 | ||
453 | if (request.mediaIndex === this.playlist_.segments.length - 1 && | ||
454 | this.mediaSource_.readyState === 'ended' && | ||
455 | !this.seeking_()) { | ||
456 | return; | ||
457 | } | ||
458 | |||
459 | let segment = this.playlist_.segments[request.mediaIndex]; | ||
460 | let startOfSegment = duration(this.playlist_, | ||
461 | this.playlist_.mediaSequence + request.mediaIndex, | ||
462 | this.expired_); | ||
463 | |||
474 | // Sanity check the segment-index determining logic by calcuating the | 464 | // Sanity check the segment-index determining logic by calcuating the |
475 | // percentage of the chosen segment that is buffered. If more than 90% | 465 | // percentage of the chosen segment that is buffered. If more than 90% |
476 | // of the segment is buffered then fetching it will likely not help in | 466 | // of the segment is buffered then fetching it will likely not help in |
477 | // any way | 467 | // any way |
478 | let percentBuffered = this.getSegmentBufferedPercent_(this.playlist_, | 468 | let percentBuffered = Ranges.getSegmentBufferedPercent(startOfSegment, |
479 | request.mediaIndex, | 469 | segment.duration, |
480 | this.currentTime_(), | 470 | this.currentTime_(), |
481 | this.sourceUpdater_.buffered()); | 471 | this.sourceUpdater_.buffered()); |
482 | 472 | ||
... | @@ -484,9 +474,9 @@ export default class SegmentLoader extends videojs.EventTarget { | ... | @@ -484,9 +474,9 @@ export default class SegmentLoader extends videojs.EventTarget { |
484 | // Increment the timeCorrection_ variable to push the fetcher forward | 474 | // Increment the timeCorrection_ variable to push the fetcher forward |
485 | // in time and hopefully skip any gaps or flaws in our understanding | 475 | // in time and hopefully skip any gaps or flaws in our understanding |
486 | // of the media | 476 | // of the media |
487 | this.incrementTimeCorrection_(this.playlist_.targetDuration); | 477 | let correctionApplied = this.incrementTimeCorrection_(this.playlist_.targetDuration, 1); |
488 | 478 | ||
489 | if (!this.paused()) { | 479 | if (correctionApplied && !this.paused()) { |
490 | this.fillBuffer_(); | 480 | this.fillBuffer_(); |
491 | } | 481 | } |
492 | 482 | ||
... | @@ -761,7 +751,8 @@ export default class SegmentLoader extends videojs.EventTarget { | ... | @@ -761,7 +751,8 @@ export default class SegmentLoader extends videojs.EventTarget { |
761 | 751 | ||
762 | // add segment metadata if it we have gained information during the | 752 | // add segment metadata if it we have gained information during the |
763 | // last append | 753 | // last append |
764 | this.updateTimeline_(segmentInfo); | 754 | let timelineUpdated = this.updateTimeline_(segmentInfo); |
755 | |||
765 | this.trigger('progress'); | 756 | this.trigger('progress'); |
766 | 757 | ||
767 | let currentMediaIndex = segmentInfo.mediaIndex; | 758 | let currentMediaIndex = segmentInfo.mediaIndex; |
... | @@ -805,9 +796,24 @@ export default class SegmentLoader extends videojs.EventTarget { | ... | @@ -805,9 +796,24 @@ export default class SegmentLoader extends videojs.EventTarget { |
805 | 796 | ||
806 | this.state = 'READY'; | 797 | this.state = 'READY'; |
807 | 798 | ||
799 | if (timelineUpdated) { | ||
800 | this.timeCorrection_ = 0; | ||
808 | if (!this.paused()) { | 801 | if (!this.paused()) { |
809 | this.fillBuffer_(); | 802 | this.fillBuffer_(); |
810 | } | 803 | } |
804 | return; | ||
805 | } | ||
806 | |||
807 | // the last segment append must have been entirely in the | ||
808 | // already buffered time ranges. adjust the timeCorrection | ||
809 | // offset to fetch forward until we find a segment that adds | ||
810 | // to the buffered time ranges and improves subsequent media | ||
811 | // index calculations. | ||
812 | let correctionApplied = this.incrementTimeCorrection_(segmentInfo.duration, 4); | ||
813 | |||
814 | if (correctionApplied && !this.paused()) { | ||
815 | this.fillBuffer_(); | ||
816 | } | ||
811 | } | 817 | } |
812 | 818 | ||
813 | /** | 819 | /** |
... | @@ -820,8 +826,7 @@ export default class SegmentLoader extends videojs.EventTarget { | ... | @@ -820,8 +826,7 @@ export default class SegmentLoader extends videojs.EventTarget { |
820 | updateTimeline_(segmentInfo) { | 826 | updateTimeline_(segmentInfo) { |
821 | let segment; | 827 | let segment; |
822 | let segmentEnd; | 828 | let segmentEnd; |
823 | let timelineUpdated; | 829 | let timelineUpdated = false; |
824 | let segmentLength = this.playlist_.targetDuration; | ||
825 | let playlist = segmentInfo.playlist; | 830 | let playlist = segmentInfo.playlist; |
826 | let currentMediaIndex = segmentInfo.mediaIndex; | 831 | let currentMediaIndex = segmentInfo.mediaIndex; |
827 | 832 | ||
... | @@ -837,19 +842,9 @@ export default class SegmentLoader extends videojs.EventTarget { | ... | @@ -837,19 +842,9 @@ export default class SegmentLoader extends videojs.EventTarget { |
837 | timelineUpdated = updateSegmentMetadata(playlist, | 842 | timelineUpdated = updateSegmentMetadata(playlist, |
838 | currentMediaIndex, | 843 | currentMediaIndex, |
839 | segmentEnd); | 844 | segmentEnd); |
840 | segmentLength = segment.duration; | ||
841 | } | 845 | } |
842 | 846 | ||
843 | // the last segment append must have been entirely in the | 847 | return timelineUpdated; |
844 | // already buffered time ranges. adjust the timeCorrection | ||
845 | // offset to fetch forward until we find a segment that adds | ||
846 | // to the buffered time ranges and improves subsequent media | ||
847 | // index calculations. | ||
848 | if (!timelineUpdated) { | ||
849 | this.incrementTimeCorrection_(segmentLength); | ||
850 | } else { | ||
851 | this.timeCorrection_ = 0; | ||
852 | } | ||
853 | } | 848 | } |
854 | 849 | ||
855 | /** | 850 | /** |
... | @@ -862,17 +857,18 @@ export default class SegmentLoader extends videojs.EventTarget { | ... | @@ -862,17 +857,18 @@ export default class SegmentLoader extends videojs.EventTarget { |
862 | * @private | 857 | * @private |
863 | * @param {Number} secondsToIncrement number of seconds to add to the | 858 | * @param {Number} secondsToIncrement number of seconds to add to the |
864 | * timeCorrection_ variable | 859 | * timeCorrection_ variable |
860 | * @param {Number} maxSegmentsToWalk maximum number of times we allow this | ||
861 | * function to walk forward | ||
865 | */ | 862 | */ |
866 | incrementTimeCorrection_(secondsToIncrement) { | 863 | incrementTimeCorrection_(secondsToIncrement, maxSegmentsToWalk) { |
867 | // If we have already incremented timeCorrection_ beyond the limit, | 864 | // If we have already incremented timeCorrection_ beyond the limit, |
868 | // then stop trying to find a segment, pause fetching, and emit an | 865 | // stop searching for a segment and reset timeCorrection_ |
869 | // error event | 866 | if (this.timeCorrection_ >= this.playlist_.targetDuration * maxSegmentsToWalk) { |
870 | if (this.timeCorrection_ >= this.playlist_.targetDuration * 5) { | ||
871 | this.timeCorrection_ = 0; | 867 | this.timeCorrection_ = 0; |
872 | this.pause(); | 868 | return false; |
873 | return this.trigger('error'); | ||
874 | } | 869 | } |
875 | 870 | ||
876 | this.timeCorrection_ += secondsToIncrement; | 871 | this.timeCorrection_ += secondsToIncrement; |
872 | return true; | ||
877 | } | 873 | } |
878 | } | 874 | } | ... | ... |
... | @@ -91,3 +91,78 @@ QUnit.test('detects time range end-point changed by updates', function() { | ... | @@ -91,3 +91,78 @@ QUnit.test('detects time range end-point changed by updates', function() { |
91 | edge = Ranges.findSoleUncommonTimeRangesEnd(createTimeRanges([[0, 11]]), null); | 91 | edge = Ranges.findSoleUncommonTimeRangesEnd(createTimeRanges([[0, 11]]), null); |
92 | QUnit.strictEqual(edge, null, 'treat null update buffer as an empty TimeRanges object'); | 92 | QUnit.strictEqual(edge, null, 'treat null update buffer as an empty TimeRanges object'); |
93 | }); | 93 | }); |
94 | |||
95 | QUnit.module('Segment Percent Buffered Calculations'); | ||
96 | |||
97 | QUnit.test('calculates the percent buffered for segments', function() { | ||
98 | let segmentStart = 10; | ||
99 | let segmentDuration = 10; | ||
100 | let currentTime = 0; | ||
101 | let buffered = createTimeRanges([[15, 19]]); | ||
102 | let percentBuffered = Ranges.getSegmentBufferedPercent( | ||
103 | segmentStart, | ||
104 | segmentDuration, | ||
105 | currentTime, | ||
106 | buffered); | ||
107 | |||
108 | QUnit.equal(percentBuffered, 40, 'calculated the buffered amount correctly'); | ||
109 | }); | ||
110 | |||
111 | QUnit.test('calculates the percent buffered for segments taking into account ' + | ||
112 | 'currentTime', function() { | ||
113 | let segmentStart = 10; | ||
114 | let segmentDuration = 10; | ||
115 | let currentTime = 15; | ||
116 | let buffered = createTimeRanges([[15, 19]]); | ||
117 | let percentBuffered = Ranges.getSegmentBufferedPercent( | ||
118 | segmentStart, | ||
119 | segmentDuration, | ||
120 | currentTime, | ||
121 | buffered); | ||
122 | |||
123 | QUnit.equal(percentBuffered, 90, 'calculated the buffered amount correctly'); | ||
124 | }); | ||
125 | |||
126 | QUnit.test('calculates the percent buffered for segments with multiple buffered ' + | ||
127 | 'regions', function() { | ||
128 | let segmentStart = 10; | ||
129 | let segmentDuration = 10; | ||
130 | let currentTime = 0; | ||
131 | let buffered = createTimeRanges([[0, 11], [12, 19]]); | ||
132 | let percentBuffered = Ranges.getSegmentBufferedPercent( | ||
133 | segmentStart, | ||
134 | segmentDuration, | ||
135 | currentTime, | ||
136 | buffered); | ||
137 | |||
138 | QUnit.equal(percentBuffered, 80, 'calculated the buffered amount correctly'); | ||
139 | }); | ||
140 | |||
141 | QUnit.test('calculates the percent buffered for segments with multiple buffered ' + | ||
142 | 'regions taking into account currentTime', function() { | ||
143 | let segmentStart = 10; | ||
144 | let segmentDuration = 10; | ||
145 | let currentTime = 12; | ||
146 | let buffered = createTimeRanges([[0, 11], [12, 19]]); | ||
147 | let percentBuffered = Ranges.getSegmentBufferedPercent( | ||
148 | segmentStart, | ||
149 | segmentDuration, | ||
150 | currentTime, | ||
151 | buffered); | ||
152 | |||
153 | QUnit.equal(percentBuffered, 90, 'calculated the buffered amount correctly'); | ||
154 | }); | ||
155 | |||
156 | QUnit.test('calculates the percent buffered as 0 for zero-length segments', function() { | ||
157 | let segmentStart = 10; | ||
158 | let segmentDuration = 0; | ||
159 | let currentTime = 0; | ||
160 | let buffered = createTimeRanges([[0, 19]]); | ||
161 | let percentBuffered = Ranges.getSegmentBufferedPercent( | ||
162 | segmentStart, | ||
163 | segmentDuration, | ||
164 | currentTime, | ||
165 | buffered); | ||
166 | |||
167 | QUnit.equal(percentBuffered, 0, 'calculated the buffered amount correctly'); | ||
168 | }); | ... | ... |
... | @@ -2,43 +2,8 @@ import QUnit from 'qunit'; | ... | @@ -2,43 +2,8 @@ import QUnit from 'qunit'; |
2 | import SegmentLoader from '../src/segment-loader'; | 2 | import SegmentLoader from '../src/segment-loader'; |
3 | import videojs from 'video.js'; | 3 | import videojs from 'video.js'; |
4 | import xhrFactory from '../src/xhr'; | 4 | import xhrFactory from '../src/xhr'; |
5 | import {useFakeEnvironment, useFakeMediaSource} from './test-helpers.js'; | ||
6 | import Config from '../src/config'; | 5 | import Config from '../src/config'; |
7 | 6 | import { playlistWithDuration, useFakeEnvironment, useFakeMediaSource } from './test-helpers.js'; | |
8 | const playlistWithDuration = function(time, conf) { | ||
9 | let result = { | ||
10 | targetDuration: 10, | ||
11 | mediaSequence: conf && conf.mediaSequence ? conf.mediaSequence : 0, | ||
12 | discontinuityStarts: [], | ||
13 | segments: [], | ||
14 | endList: true | ||
15 | }; | ||
16 | let count = Math.floor(time / 10); | ||
17 | let remainder = time % 10; | ||
18 | let i; | ||
19 | let isEncrypted = conf && conf.isEncrypted; | ||
20 | |||
21 | for (i = 0; i < count; i++) { | ||
22 | result.segments.push({ | ||
23 | uri: i + '.ts', | ||
24 | resolvedUri: i + '.ts', | ||
25 | duration: 10 | ||
26 | }); | ||
27 | if (isEncrypted) { | ||
28 | result.segments[i].key = { | ||
29 | uri: i + '-key.php', | ||
30 | resolvedUri: i + '-key.php' | ||
31 | }; | ||
32 | } | ||
33 | } | ||
34 | if (remainder) { | ||
35 | result.segments.push({ | ||
36 | uri: i + '.ts', | ||
37 | duration: remainder | ||
38 | }); | ||
39 | } | ||
40 | return result; | ||
41 | }; | ||
42 | 7 | ||
43 | let currentTime; | 8 | let currentTime; |
44 | let mediaSource; | 9 | let mediaSource; |
... | @@ -428,8 +393,8 @@ QUnit.test('adjusts the playlist offset even when segment.end is set if no' + | ... | @@ -428,8 +393,8 @@ QUnit.test('adjusts the playlist offset even when segment.end is set if no' + |
428 | QUnit.equal(this.requests[0].url, '1.ts', 'moved ahead a segment'); | 393 | QUnit.equal(this.requests[0].url, '1.ts', 'moved ahead a segment'); |
429 | }); | 394 | }); |
430 | 395 | ||
431 | QUnit.test('adjusts the playlist offset if no buffering progress is made after' + | 396 | QUnit.test('adjusts the playlist offset if no buffering progress is made after ' + |
432 | ' five consecutive attempts', function() { | 397 | 'several consecutive attempts', function() { |
433 | let sourceBuffer; | 398 | let sourceBuffer; |
434 | let playlist; | 399 | let playlist; |
435 | let errors = 0; | 400 | let errors = 0; |
... | @@ -452,7 +417,7 @@ QUnit.test('adjusts the playlist offset if no buffering progress is made after' | ... | @@ -452,7 +417,7 @@ QUnit.test('adjusts the playlist offset if no buffering progress is made after' |
452 | sourceBuffer.buffered = videojs.createTimeRanges([[0, 10]]); | 417 | sourceBuffer.buffered = videojs.createTimeRanges([[0, 10]]); |
453 | sourceBuffer.trigger('updateend'); | 418 | sourceBuffer.trigger('updateend'); |
454 | 419 | ||
455 | for (let i = 1; i <= 6; i++) { | 420 | for (let i = 1; i <= 5; i++) { |
456 | // the next segment doesn't increase the buffer at all | 421 | // the next segment doesn't increase the buffer at all |
457 | QUnit.equal(this.requests[0].url, (i + '.ts'), 'requested the next segment'); | 422 | QUnit.equal(this.requests[0].url, (i + '.ts'), 'requested the next segment'); |
458 | this.clock.tick(1); | 423 | this.clock.tick(1); |
... | @@ -460,10 +425,8 @@ QUnit.test('adjusts the playlist offset if no buffering progress is made after' | ... | @@ -460,10 +425,8 @@ QUnit.test('adjusts the playlist offset if no buffering progress is made after' |
460 | this.requests.shift().respond(200, null, ''); | 425 | this.requests.shift().respond(200, null, ''); |
461 | sourceBuffer.trigger('updateend'); | 426 | sourceBuffer.trigger('updateend'); |
462 | } | 427 | } |
463 | 428 | this.clock.tick(1); | |
464 | // so the loader should try the next segment | 429 | QUnit.equal(this.requests.length, 0, 'no more requests are made'); |
465 | QUnit.equal(errors, 1, 'emitted error'); | ||
466 | QUnit.ok(loader.paused(), 'loader is paused'); | ||
467 | }); | 430 | }); |
468 | 431 | ||
469 | QUnit.test('cancels outstanding requests on abort', function() { | 432 | QUnit.test('cancels outstanding requests on abort', function() { | ... | ... |
... | @@ -292,3 +292,38 @@ export const absoluteUrl = function(relativeUrl) { | ... | @@ -292,3 +292,38 @@ export const absoluteUrl = function(relativeUrl) { |
292 | .join('/') | 292 | .join('/') |
293 | ); | 293 | ); |
294 | }; | 294 | }; |
295 | |||
296 | export const playlistWithDuration = function(time, conf) { | ||
297 | let result = { | ||
298 | targetDuration: 10, | ||
299 | mediaSequence: conf && conf.mediaSequence ? conf.mediaSequence : 0, | ||
300 | discontinuityStarts: [], | ||
301 | segments: [], | ||
302 | endList: true | ||
303 | }; | ||
304 | let count = Math.floor(time / 10); | ||
305 | let remainder = time % 10; | ||
306 | let i; | ||
307 | let isEncrypted = conf && conf.isEncrypted; | ||
308 | |||
309 | for (i = 0; i < count; i++) { | ||
310 | result.segments.push({ | ||
311 | uri: i + '.ts', | ||
312 | resolvedUri: i + '.ts', | ||
313 | duration: 10 | ||
314 | }); | ||
315 | if (isEncrypted) { | ||
316 | result.segments[i].key = { | ||
317 | uri: i + '-key.php', | ||
318 | resolvedUri: i + '-key.php' | ||
319 | }; | ||
320 | } | ||
321 | } | ||
322 | if (remainder) { | ||
323 | result.segments.push({ | ||
324 | uri: i + '.ts', | ||
325 | duration: remainder | ||
326 | }); | ||
327 | } | ||
328 | return result; | ||
329 | }; | ... | ... |
utils/manifest/zeroDuration.js
0 → 100644
-
Please register or sign in to post a comment