a3c1c2e9 by David LaPalomento

Merge pull request #199 from videojs/configurable-initial-bw

Allow a user-provided initial bandwidth estimate
2 parents 538acc35 ec716297
...@@ -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;
......