Merge pull request #199 from videojs/configurable-initial-bw
Allow a user-provided initial bandwidth estimate
Showing
3 changed files
with
48 additions
and
18 deletions
... | @@ -113,9 +113,16 @@ This value is updated after each segment download completes. | ... | @@ -113,9 +113,16 @@ This value is updated after each segment download completes. |
113 | Type: `number` | 113 | Type: `number` |
114 | 114 | ||
115 | The number of bits downloaded per second in the last segment download. | 115 | The number of bits downloaded per second in the last segment download. |
116 | This value is used by the default implementation of `selectPlaylist` | 116 | This value is used by the default implementation of `selectPlaylist` |
117 | to select an appropriate bitrate to play. | 117 | to select an appropriate bitrate to play. |
118 | 118 | ||
119 | Before the first video segment has been downloaded, it's hard to | ||
120 | estimate bandwidth accurately. The HLS tech uses a heuristic based on | ||
121 | the playlist download times to do this estimation by default. If you | ||
122 | have a more accurate source of bandwidth information, you can override | ||
123 | this value as soon as the HLS tech has loaded to provide an initial | ||
124 | bandwidth estimate. | ||
125 | |||
119 | #### player.hls.bytesReceived | 126 | #### player.hls.bytesReceived |
120 | Type: `number` | 127 | Type: `number` |
121 | 128 | ... | ... |
... | @@ -138,14 +138,21 @@ videojs.Hls.prototype.handleSourceOpen = function() { | ... | @@ -138,14 +138,21 @@ videojs.Hls.prototype.handleSourceOpen = function() { |
138 | }; | 138 | }; |
139 | 139 | ||
140 | oldMediaPlaylist = this.playlists.media(); | 140 | oldMediaPlaylist = this.playlists.media(); |
141 | // the bandwidth estimate for the first segment is based on round trip | 141 | |
142 | // time for the master playlist. the master playlist is almost always | 142 | // the bandwidth estimate for the first segment is based on round |
143 | // tiny so the round trip time is dominated by latency and so the | 143 | // trip time for the master playlist. the master playlist is |
144 | // computed bandwidth is much lower than steady-state. to account for | 144 | // almost always tiny so the round-trip time is dominated by |
145 | // this, we scale the bandwidth estimate from the master playlist. | 145 | // latency and the computed bandwidth is much lower than |
146 | this.setBandwidth({ | 146 | // steady-state. if the the downstream developer has a better way |
147 | bandwidth: this.playlists.bandwidth * 5 | 147 | // of detecting bandwidth and provided a number, use that instead. |
148 | }); | 148 | if (this.bandwidth === undefined) { |
149 | // we're going to have to estimate initial bandwidth | ||
150 | // ourselves. scale the bandwidth estimate to account for the | ||
151 | // relatively high round-trip time from the master playlist. | ||
152 | this.setBandwidth({ | ||
153 | bandwidth: this.playlists.bandwidth * 5 | ||
154 | }); | ||
155 | } | ||
149 | 156 | ||
150 | selectedPlaylist = this.selectPlaylist(); | 157 | selectedPlaylist = this.selectPlaylist(); |
151 | oldBitrate = oldMediaPlaylist.attributes && | 158 | oldBitrate = oldMediaPlaylist.attributes && | ... | ... |
... | @@ -348,10 +348,9 @@ test('downloads media playlists after loading the master', function() { | ... | @@ -348,10 +348,9 @@ test('downloads media playlists after loading the master', function() { |
348 | }); | 348 | }); |
349 | openMediaSource(player); | 349 | openMediaSource(player); |
350 | 350 | ||
351 | // set bandwidth to an appropriate number so we don't switch | ||
352 | player.hls.bandwidth = 200000; | ||
351 | standardXHRResponse(requests[0]); | 353 | standardXHRResponse(requests[0]); |
352 | |||
353 | // set bandwidth to a high number, so, we don't switch; | ||
354 | player.hls.bandwidth = 500000; | ||
355 | standardXHRResponse(requests[1]); | 354 | standardXHRResponse(requests[1]); |
356 | standardXHRResponse(requests[2]); | 355 | standardXHRResponse(requests[2]); |
357 | 356 | ||
... | @@ -507,9 +506,8 @@ test('moves to the next segment if there is a network error', function() { | ... | @@ -507,9 +506,8 @@ test('moves to the next segment if there is a network error', function() { |
507 | }); | 506 | }); |
508 | openMediaSource(player); | 507 | openMediaSource(player); |
509 | 508 | ||
509 | player.hls.bandwidth = 20000; | ||
510 | standardXHRResponse(requests[0]); | 510 | standardXHRResponse(requests[0]); |
511 | |||
512 | player.hls.bandwidth = 3000000; | ||
513 | standardXHRResponse(requests[1]); | 511 | standardXHRResponse(requests[1]); |
514 | 512 | ||
515 | mediaIndex = player.hls.mediaIndex; | 513 | mediaIndex = player.hls.mediaIndex; |
... | @@ -562,9 +560,9 @@ test('downloads additional playlists if required', function() { | ... | @@ -562,9 +560,9 @@ test('downloads additional playlists if required', function() { |
562 | }); | 560 | }); |
563 | openMediaSource(player); | 561 | openMediaSource(player); |
564 | 562 | ||
563 | player.hls.bandwidth = 20000; | ||
565 | standardXHRResponse(requests[0]); | 564 | standardXHRResponse(requests[0]); |
566 | 565 | ||
567 | player.hls.bandwidth = 3000000; | ||
568 | standardXHRResponse(requests[1]); | 566 | standardXHRResponse(requests[1]); |
569 | // before an m3u8 is downloaded, no segments are available | 567 | // before an m3u8 is downloaded, no segments are available |
570 | player.hls.selectPlaylist = function() { | 568 | player.hls.selectPlaylist = function() { |
... | @@ -579,7 +577,9 @@ test('downloads additional playlists if required', function() { | ... | @@ -579,7 +577,9 @@ test('downloads additional playlists if required', function() { |
579 | // the playlist selection is revisited after a new segment is downloaded | 577 | // the playlist selection is revisited after a new segment is downloaded |
580 | player.trigger('timeupdate'); | 578 | player.trigger('timeupdate'); |
581 | 579 | ||
582 | standardXHRResponse(requests[2]); | 580 | requests[2].bandwidth = 3000000; |
581 | requests[2].response = new Uint8Array([0]); | ||
582 | requests[2].respond(200, null, ''); | ||
583 | standardXHRResponse(requests[3]); | 583 | standardXHRResponse(requests[3]); |
584 | 584 | ||
585 | strictEqual(4, requests.length, 'requests were made'); | 585 | strictEqual(4, requests.length, 'requests were made'); |
... | @@ -633,6 +633,22 @@ test('scales the bandwidth estimate for the first segment', function() { | ... | @@ -633,6 +633,22 @@ test('scales the bandwidth estimate for the first segment', function() { |
633 | equal(player.hls.bandwidth, 500 * 5, 'scaled the bandwidth estimate by 5'); | 633 | equal(player.hls.bandwidth, 500 * 5, 'scaled the bandwidth estimate by 5'); |
634 | }); | 634 | }); |
635 | 635 | ||
636 | test('allows initial bandwidth to be provided', function() { | ||
637 | player.src({ | ||
638 | src: 'manifest/master.m3u8', | ||
639 | type: 'application/vnd.apple.mpegurl' | ||
640 | }); | ||
641 | player.hls.bandwidth = 500; | ||
642 | openMediaSource(player); | ||
643 | |||
644 | requests[0].bandwidth = 1; | ||
645 | requests.shift().respond(200, null, | ||
646 | '#EXTM3U\n' + | ||
647 | '#EXT-X-PLAYLIST-TYPE:VOD\n' + | ||
648 | '#EXT-X-TARGETDURATION:10\n'); | ||
649 | equal(player.hls.bandwidth, 500, 'prefers user-specified intial bandwidth'); | ||
650 | }); | ||
651 | |||
636 | test('raises the minimum bitrate for a stream proportionially', function() { | 652 | test('raises the minimum bitrate for a stream proportionially', function() { |
637 | var playlist; | 653 | var playlist; |
638 | player.src({ | 654 | player.src({ |
... | @@ -1268,9 +1284,9 @@ test('resets the switching algorithm if a request times out', function() { | ... | @@ -1268,9 +1284,9 @@ test('resets the switching algorithm if a request times out', function() { |
1268 | type: 'application/vnd.apple.mpegurl' | 1284 | type: 'application/vnd.apple.mpegurl' |
1269 | }); | 1285 | }); |
1270 | openMediaSource(player); | 1286 | openMediaSource(player); |
1271 | standardXHRResponse(requests.shift()); // master | 1287 | player.hls.bandwidth = 20000; |
1272 | 1288 | ||
1273 | player.hls.bandwidth = 3000000; | 1289 | standardXHRResponse(requests.shift()); // master |
1274 | standardXHRResponse(requests.shift()); // media.m3u8 | 1290 | standardXHRResponse(requests.shift()); // media.m3u8 |
1275 | // simulate a segment timeout | 1291 | // simulate a segment timeout |
1276 | requests[0].timedout = true; | 1292 | requests[0].timedout = true; | ... | ... |
-
Please register or sign in to post a comment