Merge pull request #223 from videojs/hotfix/src-changes
Clean up the playlist loader when sources change
Showing
2 changed files
with
95 additions
and
47 deletions
... | @@ -10,6 +10,9 @@ var | ... | @@ -10,6 +10,9 @@ var |
10 | // a fudge factor to apply to advertised playlist bitrates to account for | 10 | // a fudge factor to apply to advertised playlist bitrates to account for |
11 | // temporary flucations in client bandwidth | 11 | // temporary flucations in client bandwidth |
12 | bandwidthVariance = 1.1, | 12 | bandwidthVariance = 1.1, |
13 | |||
14 | // the amount of time to wait between checking the state of the buffer | ||
15 | bufferCheckInterval = 500, | ||
13 | keyXhr, | 16 | keyXhr, |
14 | keyFailed, | 17 | keyFailed, |
15 | resolveUrl; | 18 | resolveUrl; |
... | @@ -36,6 +39,11 @@ videojs.Hls = videojs.Flash.extend({ | ... | @@ -36,6 +39,11 @@ videojs.Hls = videojs.Flash.extend({ |
36 | this.currentTime = videojs.Hls.prototype.currentTime; | 39 | this.currentTime = videojs.Hls.prototype.currentTime; |
37 | this.setCurrentTime = videojs.Hls.prototype.setCurrentTime; | 40 | this.setCurrentTime = videojs.Hls.prototype.setCurrentTime; |
38 | 41 | ||
42 | // periodically check if new data needs to be downloaded or | ||
43 | // buffered data should be appended to the source buffer | ||
44 | this.segmentBuffer_ = []; | ||
45 | this.startCheckingBuffer_(); | ||
46 | |||
39 | videojs.Hls.prototype.src.call(this, options.source && options.source.src); | 47 | videojs.Hls.prototype.src.call(this, options.source && options.source.src); |
40 | } | 48 | } |
41 | }); | 49 | }); |
... | @@ -49,7 +57,10 @@ videojs.Hls.GOAL_BUFFER_LENGTH = 30; | ... | @@ -49,7 +57,10 @@ videojs.Hls.GOAL_BUFFER_LENGTH = 30; |
49 | videojs.Hls.prototype.src = function(src) { | 57 | videojs.Hls.prototype.src = function(src) { |
50 | var | 58 | var |
51 | tech = this, | 59 | tech = this, |
60 | player = this.player(), | ||
61 | settings = player.options().hls || {}, | ||
52 | mediaSource, | 62 | mediaSource, |
63 | oldMediaPlaylist, | ||
53 | source; | 64 | source; |
54 | 65 | ||
55 | // do nothing if the src is falsey | 66 | // do nothing if the src is falsey |
... | @@ -115,46 +126,13 @@ videojs.Hls.prototype.src = function(src) { | ... | @@ -115,46 +126,13 @@ videojs.Hls.prototype.src = function(src) { |
115 | // load the MediaSource into the player | 126 | // load the MediaSource into the player |
116 | this.mediaSource.addEventListener('sourceopen', videojs.bind(this, this.handleSourceOpen)); | 127 | this.mediaSource.addEventListener('sourceopen', videojs.bind(this, this.handleSourceOpen)); |
117 | 128 | ||
118 | this.player().ready(function() { | 129 | // cleanup the old playlist loader, if necessary |
119 | // do nothing if the tech has been disposed already | ||
120 | // this can occur if someone sets the src in player.ready(), for instance | ||
121 | if (!tech.el()) { | ||
122 | return; | ||
123 | } | ||
124 | tech.el().vjs_src(source.src); | ||
125 | }); | ||
126 | }; | ||
127 | |||
128 | videojs.Hls.setMediaIndexForLive = function(selectedPlaylist) { | ||
129 | var tailIterator = selectedPlaylist.segments.length, | ||
130 | tailDuration = 0, | ||
131 | targetTail = (selectedPlaylist.targetDuration || 10) * 3; | ||
132 | |||
133 | while (tailDuration < targetTail && tailIterator > 0) { | ||
134 | tailDuration += selectedPlaylist.segments[tailIterator - 1].duration; | ||
135 | tailIterator--; | ||
136 | } | ||
137 | |||
138 | return tailIterator; | ||
139 | }; | ||
140 | |||
141 | videojs.Hls.prototype.handleSourceOpen = function() { | ||
142 | // construct the video data buffer and set the appropriate MIME type | ||
143 | var | ||
144 | player = this.player(), | ||
145 | settings = player.options().hls || {}, | ||
146 | sourceBuffer = this.mediaSource.addSourceBuffer('video/flv; codecs="vp6,aac"'), | ||
147 | oldMediaPlaylist; | ||
148 | |||
149 | this.sourceBuffer = sourceBuffer; | ||
150 | sourceBuffer.appendBuffer(this.segmentParser_.getFlvHeader()); | ||
151 | |||
152 | this.mediaIndex = 0; | ||
153 | |||
154 | if (this.playlists) { | 130 | if (this.playlists) { |
155 | this.playlists.dispose(); | 131 | this.playlists.dispose(); |
156 | } | 132 | } |
157 | 133 | ||
134 | this.mediaIndex = 0; | ||
135 | |||
158 | this.playlists = new videojs.Hls.PlaylistLoader(this.src_, settings.withCredentials); | 136 | this.playlists = new videojs.Hls.PlaylistLoader(this.src_, settings.withCredentials); |
159 | 137 | ||
160 | this.playlists.on('loadedmetadata', videojs.bind(this, function() { | 138 | this.playlists.on('loadedmetadata', videojs.bind(this, function() { |
... | @@ -164,11 +142,7 @@ videojs.Hls.prototype.handleSourceOpen = function() { | ... | @@ -164,11 +142,7 @@ videojs.Hls.prototype.handleSourceOpen = function() { |
164 | setupEvents = function() { | 142 | setupEvents = function() { |
165 | this.fillBuffer(); | 143 | this.fillBuffer(); |
166 | 144 | ||
167 | // periodically check if new data needs to be downloaded or | 145 | |
168 | // buffered data should be appended to the source buffer | ||
169 | player.on('timeupdate', videojs.bind(this, this.fillBuffer)); | ||
170 | player.on('timeupdate', videojs.bind(this, this.drainBuffer)); | ||
171 | player.on('waiting', videojs.bind(this, this.drainBuffer)); | ||
172 | 146 | ||
173 | player.trigger('loadedmetadata'); | 147 | player.trigger('loadedmetadata'); |
174 | }; | 148 | }; |
... | @@ -255,6 +229,39 @@ videojs.Hls.prototype.handleSourceOpen = function() { | ... | @@ -255,6 +229,39 @@ videojs.Hls.prototype.handleSourceOpen = function() { |
255 | player.trigger('mediachange'); | 229 | player.trigger('mediachange'); |
256 | })); | 230 | })); |
257 | 231 | ||
232 | this.player().ready(function() { | ||
233 | // do nothing if the tech has been disposed already | ||
234 | // this can occur if someone sets the src in player.ready(), for instance | ||
235 | if (!tech.el()) { | ||
236 | return; | ||
237 | } | ||
238 | tech.el().vjs_src(source.src); | ||
239 | }); | ||
240 | }; | ||
241 | |||
242 | videojs.Hls.setMediaIndexForLive = function(selectedPlaylist) { | ||
243 | var tailIterator = selectedPlaylist.segments.length, | ||
244 | tailDuration = 0, | ||
245 | targetTail = (selectedPlaylist.targetDuration || 10) * 3; | ||
246 | |||
247 | while (tailDuration < targetTail && tailIterator > 0) { | ||
248 | tailDuration += selectedPlaylist.segments[tailIterator - 1].duration; | ||
249 | tailIterator--; | ||
250 | } | ||
251 | |||
252 | return tailIterator; | ||
253 | }; | ||
254 | |||
255 | videojs.Hls.prototype.handleSourceOpen = function() { | ||
256 | // construct the video data buffer and set the appropriate MIME type | ||
257 | var | ||
258 | player = this.player(), | ||
259 | sourceBuffer = this.mediaSource.addSourceBuffer('video/flv; codecs="vp6,aac"'); | ||
260 | |||
261 | this.sourceBuffer = sourceBuffer; | ||
262 | sourceBuffer.appendBuffer(this.segmentParser_.getFlvHeader()); | ||
263 | |||
264 | |||
258 | // if autoplay is enabled, begin playback. This is duplicative of | 265 | // if autoplay is enabled, begin playback. This is duplicative of |
259 | // code in video.js but is required because play() must be invoked | 266 | // code in video.js but is required because play() must be invoked |
260 | // *after* the media source has opened. | 267 | // *after* the media source has opened. |
... | @@ -377,12 +384,7 @@ videojs.Hls.prototype.cancelSegmentXhr = function() { | ... | @@ -377,12 +384,7 @@ videojs.Hls.prototype.cancelSegmentXhr = function() { |
377 | * Abort all outstanding work and cleanup. | 384 | * Abort all outstanding work and cleanup. |
378 | */ | 385 | */ |
379 | videojs.Hls.prototype.dispose = function() { | 386 | videojs.Hls.prototype.dispose = function() { |
380 | var player = this.player(); | 387 | this.stopCheckingBuffer_(); |
381 | |||
382 | // remove event handlers | ||
383 | player.off('timeupdate', this.fillBuffer); | ||
384 | player.off('timeupdate', this.drainBuffer); | ||
385 | player.off('waiting', this.drainBuffer); | ||
386 | 388 | ||
387 | if (this.playlists) { | 389 | if (this.playlists) { |
388 | this.playlists.dispose(); | 390 | this.playlists.dispose(); |
... | @@ -468,6 +470,46 @@ videojs.Hls.prototype.selectPlaylist = function () { | ... | @@ -468,6 +470,46 @@ videojs.Hls.prototype.selectPlaylist = function () { |
468 | }; | 470 | }; |
469 | 471 | ||
470 | /** | 472 | /** |
473 | * Periodically request new segments and append video data. | ||
474 | */ | ||
475 | videojs.Hls.prototype.checkBuffer_ = function() { | ||
476 | // calling this method directly resets any outstanding buffer checks | ||
477 | if (this.checkBufferTimeout_) { | ||
478 | window.clearTimeout(this.checkBufferTimeout_); | ||
479 | this.checkBufferTimeout_ = null; | ||
480 | } | ||
481 | |||
482 | this.fillBuffer(); | ||
483 | this.drainBuffer(); | ||
484 | |||
485 | // wait awhile and try again | ||
486 | this.checkBufferTimeout_ = window.setTimeout(videojs.bind(this, this.checkBuffer_), | ||
487 | bufferCheckInterval); | ||
488 | }; | ||
489 | |||
490 | /** | ||
491 | * Setup a periodic task to request new segments if necessary and | ||
492 | * append bytes into the SourceBuffer. | ||
493 | */ | ||
494 | videojs.Hls.prototype.startCheckingBuffer_ = function() { | ||
495 | // if the player ever stalls, check if there is video data available | ||
496 | // to append immediately | ||
497 | this.player().on('waiting', videojs.bind(this, this.drainBuffer)); | ||
498 | |||
499 | this.checkBuffer_(); | ||
500 | }; | ||
501 | |||
502 | /** | ||
503 | * Stop the periodic task requesting new segments and feeding the | ||
504 | * SourceBuffer. | ||
505 | */ | ||
506 | videojs.Hls.prototype.stopCheckingBuffer_ = function() { | ||
507 | window.clearTimeout(this.checkBufferTimeout_); | ||
508 | this.checkBufferTimeout_ = null; | ||
509 | this.player().off('waiting', this.drainBuffer); | ||
510 | }; | ||
511 | |||
512 | /** | ||
471 | * Determines whether there is enough video data currently in the buffer | 513 | * Determines whether there is enough video data currently in the buffer |
472 | * and downloads a new segment if the buffered time is less than the goal. | 514 | * and downloads a new segment if the buffered time is less than the goal. |
473 | * @param offset (optional) {number} the offset into the downloaded segment | 515 | * @param offset (optional) {number} the offset into the downloaded segment |
... | @@ -481,6 +523,11 @@ videojs.Hls.prototype.fillBuffer = function(offset) { | ... | @@ -481,6 +523,11 @@ videojs.Hls.prototype.fillBuffer = function(offset) { |
481 | segment, | 523 | segment, |
482 | segmentUri; | 524 | segmentUri; |
483 | 525 | ||
526 | // if a video has not been specified, do nothing | ||
527 | if (!player.currentSrc() || !this.playlists) { | ||
528 | return; | ||
529 | } | ||
530 | |||
484 | // if there is a request already in flight, do nothing | 531 | // if there is a request already in flight, do nothing |
485 | if (this.segmentXhr_) { | 532 | if (this.segmentXhr_) { |
486 | return; | 533 | return; |
... | @@ -488,6 +535,7 @@ videojs.Hls.prototype.fillBuffer = function(offset) { | ... | @@ -488,6 +535,7 @@ videojs.Hls.prototype.fillBuffer = function(offset) { |
488 | 535 | ||
489 | // if no segments are available, do nothing | 536 | // if no segments are available, do nothing |
490 | if (this.playlists.state === "HAVE_NOTHING" || | 537 | if (this.playlists.state === "HAVE_NOTHING" || |
538 | !this.playlists.media() || | ||
491 | !this.playlists.media().segments) { | 539 | !this.playlists.media().segments) { |
492 | return; | 540 | return; |
493 | } | 541 | } | ... | ... |
This diff is collapsed.
Click to expand it.
-
Please register or sign in to post a comment