c7eda3a9 by jrivera

Implement blacklisting of playlists in response to some common errors

1 parent 7c6736ca
...@@ -7,17 +7,18 @@ ...@@ -7,17 +7,18 @@
7 'use strict'; 7 'use strict';
8 8
9 var 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.2, 12 bandwidthVariance = 1.2,
13 blacklistDuration = 5 * 60 * 1000, // 2 minute blacklist
14 TIME_FUDGE_FACTOR = 1 / 60, // Fudge factor to account for TimeRanges rounding
13 Component = videojs.getComponent('Component'), 15 Component = videojs.getComponent('Component'),
14 16
15 // the amount of time to wait between checking the state of the buffer 17 // The amount of time to wait between checking the state of the buffer
16 bufferCheckInterval = 500, 18 bufferCheckInterval = 500,
17 19
18 keyFailed, 20 keyFailed,
19 resolveUrl, 21 resolveUrl;
20 TIME_FUDGE_FACTOR = 1 / 60;
21 22
22 // returns true if a key has failed to download within a certain amount of retries 23 // returns true if a key has failed to download within a certain amount of retries
23 keyFailed = function(key) { 24 keyFailed = function(key) {
...@@ -179,15 +180,7 @@ videojs.HlsHandler.prototype.src = function(src) { ...@@ -179,15 +180,7 @@ videojs.HlsHandler.prototype.src = function(src) {
179 }.bind(this)); 180 }.bind(this));
180 181
181 this.playlists.on('error', function() { 182 this.playlists.on('error', function() {
182 // close the media source with the appropriate error type 183 this.blacklistCurrentPlaylist_(this.playlists.error.code);
183 if (this.playlists.error.code === 2) {
184 this.mediaSource.endOfStream('network');
185 } else if (this.playlists.error.code === 4) {
186 this.mediaSource.endOfStream('decode');
187 }
188
189 // if this error is unrecognized, pass it along to the tech
190 this.tech_.error(this.playlists.error);
191 }.bind(this)); 184 }.bind(this));
192 185
193 this.playlists.on('loadedplaylist', function() { 186 this.playlists.on('loadedplaylist', function() {
...@@ -916,6 +909,26 @@ videojs.HlsHandler.prototype.setBandwidth = function(xhr) { ...@@ -916,6 +909,26 @@ videojs.HlsHandler.prototype.setBandwidth = function(xhr) {
916 this.tech_.trigger('bandwidthupdate'); 909 this.tech_.trigger('bandwidthupdate');
917 }; 910 };
918 911
912 videojs.HlsHandler.prototype.blacklistCurrentPlaylist_ = function(error) {
913 var nextPlaylist;
914
915 // Blacklist this playlist
916 this.playlists.media().excludeUntil = Date.now() + blacklistDuration;
917
918 // Select a new playlist
919 nextPlaylist = this.selectPlaylist();
920
921 if (nextPlaylist) {
922 videojs.log.warn('Problem encountered with the current HLS playlist. Switching to another playlist.');
923 return this.playlists.media(nextPlaylist);
924 } else {
925 videojs.log.warn('Problem encountered with the current HLS playlist. No suitable alternatives found.');
926 // We have no more playlists we can select so we must fail
927 this.error = error;
928 return this.mediaSource.endOfStream('network');
929 }
930 };
931
919 videojs.HlsHandler.prototype.loadSegment = function(segmentInfo) { 932 videojs.HlsHandler.prototype.loadSegment = function(segmentInfo) {
920 var 933 var
921 self = this, 934 self = this,
...@@ -930,7 +943,11 @@ videojs.HlsHandler.prototype.loadSegment = function(segmentInfo) { ...@@ -930,7 +943,11 @@ videojs.HlsHandler.prototype.loadSegment = function(segmentInfo) {
930 this.segmentXhr_ = videojs.Hls.xhr({ 943 this.segmentXhr_ = videojs.Hls.xhr({
931 uri: segmentInfo.uri, 944 uri: segmentInfo.uri,
932 responseType: 'arraybuffer', 945 responseType: 'arraybuffer',
933 withCredentials: this.source_.withCredentials 946 withCredentials: this.source_.withCredentials,
947 // Set xhr timeout to 150% of the segment duration to allow us
948 // some time to switch renditions in the event of a catastrophic
949 // decrease in network performance or a server issue.
950 timeout: (segment.duration * 1.5) * 1000
934 }, function(error, request) { 951 }, function(error, request) {
935 // the segment request is no longer outstanding 952 // the segment request is no longer outstanding
936 self.segmentXhr_ = null; 953 self.segmentXhr_ = null;
...@@ -943,13 +960,12 @@ videojs.HlsHandler.prototype.loadSegment = function(segmentInfo) { ...@@ -943,13 +960,12 @@ videojs.HlsHandler.prototype.loadSegment = function(segmentInfo) {
943 960
944 // otherwise, trigger a network error 961 // otherwise, trigger a network error
945 if (!request.aborted && error) { 962 if (!request.aborted && error) {
946 self.error = { 963 self.pendingSegment_ = null;
964 return self.blacklistCurrentPlaylist_({
947 status: request.status, 965 status: request.status,
948 message: 'HLS segment request error at URL: ' + segmentInfo.uri, 966 message: 'HLS segment request error at URL: ' + segmentInfo.uri,
949 code: (request.status >= 500) ? 4 : 2 967 code: (request.status >= 500) ? 4 : 2
950 }; 968 });
951
952 return self.mediaSource.endOfStream('network');
953 } 969 }
954 970
955 // stop processing if the request was aborted 971 // stop processing if the request was aborted
...@@ -1021,9 +1037,10 @@ videojs.HlsHandler.prototype.drainBuffer = function(event) { ...@@ -1021,9 +1037,10 @@ videojs.HlsHandler.prototype.drainBuffer = function(event) {
1021 // if the key download failed, we want to skip this segment 1037 // if the key download failed, we want to skip this segment
1022 // but if the key hasn't downloaded yet, we want to try again later 1038 // but if the key hasn't downloaded yet, we want to try again later
1023 if (keyFailed(segment.key)) { 1039 if (keyFailed(segment.key)) {
1024 videojs.log.warn('Network error retrieving key from "' + 1040 return this.blacklistCurrentPlaylist_({
1025 segment.key.uri + '"'); 1041 message: 'HLS segment key request error.',
1026 return this.mediaSource.endOfStream('network'); 1042 code: 4
1043 });
1027 } else if (!segment.key.bytes) { 1044 } else if (!segment.key.bytes) {
1028 1045
1029 // waiting for the key bytes, try again later 1046 // waiting for the key bytes, try again later
......