76a27259 by David LaPalomento

Merge pull request #96 from videojs/feature/metrics

Expose more diagnostic info
2 parents 56043011 9edf081a
...@@ -116,6 +116,11 @@ The number of bits downloaded per second in the last segment download. ...@@ -116,6 +116,11 @@ 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 #### player.hls.bytesReceived
120 Type: `number`
121
122 The total number of content bytes downloaded by the HLS tech.
123
119 #### player.hls.selectPlaylist 124 #### player.hls.selectPlaylist
120 Type: `function` 125 Type: `function`
121 126
...@@ -136,6 +141,13 @@ Fired immediately after a new master or media playlist has been ...@@ -136,6 +141,13 @@ Fired immediately after a new master or media playlist has been
136 downloaded. By default, the tech only downloads playlists as they 141 downloaded. By default, the tech only downloads playlists as they
137 are needed. 142 are needed.
138 143
144 #### mediachange
145
146 Fired when a new playlist becomes the active media playlist. Note that
147 the actual rendering quality change does not occur simultaneously with
148 this event; a new segment must be requested and the existing buffer
149 depleted first.
150
139 ### Testing 151 ### Testing
140 152
141 For testing, you can either run `npm test` or use `grunt` directly. 153 For testing, you can either run `npm test` or use `grunt` directly.
......
...@@ -131,6 +131,7 @@ ...@@ -131,6 +131,7 @@
131 * object to switch to 131 * object to switch to
132 */ 132 */
133 loader.media = function(playlist) { 133 loader.media = function(playlist) {
134 var mediaChange = false;
134 // getter 135 // getter
135 if (!playlist) { 136 if (!playlist) {
136 return media; 137 return media;
...@@ -150,19 +151,27 @@ ...@@ -150,19 +151,27 @@
150 playlist = loader.master.playlists[playlist]; 151 playlist = loader.master.playlists[playlist];
151 } 152 }
152 153
154 mediaChange = playlist.uri !== media.uri;
155
153 // switch to fully loaded playlists immediately 156 // switch to fully loaded playlists immediately
154 if (loader.master.playlists[playlist.uri].endList) { 157 if (loader.master.playlists[playlist.uri].endList) {
158 // abort outstanding playlist requests
155 if (request) { 159 if (request) {
156 request.abort(); 160 request.abort();
157 request = null; 161 request = null;
158 } 162 }
159 loader.state = 'HAVE_METADATA'; 163 loader.state = 'HAVE_METADATA';
160 media = playlist; 164 media = playlist;
165
166 // trigger media change if the active media has been updated
167 if (mediaChange) {
168 loader.trigger('mediachange');
169 }
161 return; 170 return;
162 } 171 }
163 172
164 // switching to the active playlist is a no-op 173 // switching to the active playlist is a no-op
165 if (playlist.uri === media.uri) { 174 if (!mediaChange) {
166 return; 175 return;
167 } 176 }
168 177
...@@ -185,6 +194,7 @@ ...@@ -185,6 +194,7 @@
185 withCredentials: withCredentials 194 withCredentials: withCredentials
186 }, function(error) { 195 }, function(error) {
187 haveMetadata(error, this, playlist.uri); 196 haveMetadata(error, this, playlist.uri);
197 loader.trigger('mediachange');
188 }); 198 });
189 }; 199 };
190 200
......
...@@ -433,6 +433,7 @@ var ...@@ -433,6 +433,7 @@ var
433 // calculate the download bandwidth 433 // calculate the download bandwidth
434 player.hls.segmentXhrTime = (+new Date()) - startTime; 434 player.hls.segmentXhrTime = (+new Date()) - startTime;
435 player.hls.bandwidth = (this.response.byteLength / player.hls.segmentXhrTime) * 8 * 1000; 435 player.hls.bandwidth = (this.response.byteLength / player.hls.segmentXhrTime) * 8 * 1000;
436 player.hls.bytesReceived += this.response.byteLength;
436 437
437 // transmux the segment data from MP2T to FLV 438 // transmux the segment data from MP2T to FLV
438 segmentParser.parseSegmentBinaryData(new Uint8Array(this.response)); 439 segmentParser.parseSegmentBinaryData(new Uint8Array(this.response));
...@@ -575,6 +576,9 @@ var ...@@ -575,6 +576,9 @@ var
575 updatedPlaylist); 576 updatedPlaylist);
576 oldMediaPlaylist = updatedPlaylist; 577 oldMediaPlaylist = updatedPlaylist;
577 }); 578 });
579 player.hls.playlists.on('mediachange', function() {
580 player.trigger('mediachange');
581 });
578 }); 582 });
579 }; 583 };
580 584
...@@ -591,6 +595,8 @@ videojs.Hls = videojs.Flash.extend({ ...@@ -591,6 +595,8 @@ videojs.Hls = videojs.Flash.extend({
591 options.swf = settings.flash.swf; 595 options.swf = settings.flash.swf;
592 videojs.Flash.call(this, player, options, ready); 596 videojs.Flash.call(this, player, options, ready);
593 options.source = source; 597 options.source = source;
598 this.bytesReceived = 0;
599
594 videojs.Hls.prototype.src.call(this, options.source && options.source.src); 600 videojs.Hls.prototype.src.call(this, options.source && options.source.src);
595 } 601 }
596 }); 602 });
......
...@@ -510,4 +510,45 @@ ...@@ -510,4 +510,45 @@
510 strictEqual(errors, 1, 'fired one error'); 510 strictEqual(errors, 1, 'fired one error');
511 strictEqual(loader.error.code, 2, 'fired a network error'); 511 strictEqual(loader.error.code, 2, 'fired a network error');
512 }); 512 });
513
514 test('triggers an event when the active media changes', function() {
515 var
516 loader = new videojs.Hls.PlaylistLoader('master.m3u8'),
517 mediaChanges = 0;
518 loader.on('mediachange', function() {
519 mediaChanges++;
520 });
521 requests.pop().respond(200, null,
522 '#EXTM3U\n' +
523 '#EXT-X-STREAM-INF:BANDWIDTH=1\n' +
524 'low.m3u8\n' +
525 '#EXT-X-STREAM-INF:BANDWIDTH=2\n' +
526 'high.m3u8\n');
527 requests.shift().respond(200, null,
528 '#EXTM3U\n' +
529 '#EXT-X-MEDIA-SEQUENCE:0\n' +
530 '#EXTINF:10,\n' +
531 'low-0.ts\n' +
532 '#EXT-X-ENDLIST\n');
533 strictEqual(mediaChanges, 0, 'initial selection is not a media change');
534
535 loader.media('high.m3u8');
536 strictEqual(mediaChanges, 0, 'mediachange does not fire immediately');
537
538 requests.shift().respond(200, null,
539 '#EXTM3U\n' +
540 '#EXT-X-MEDIA-SEQUENCE:0\n' +
541 '#EXTINF:10,\n' +
542 'high-0.ts\n' +
543 '#EXT-X-ENDLIST\n');
544 strictEqual(mediaChanges, 1, 'fired a mediachange');
545
546 // switch back to an already loaded playlist
547 loader.media('low.m3u8');
548 strictEqual(mediaChanges, 2, 'fired a mediachange');
549
550 // trigger a no-op switch
551 loader.media('low.m3u8');
552 strictEqual(mediaChanges, 2, 'ignored a no-op media change');
553 });
513 })(window); 554 })(window);
......
...@@ -1227,4 +1227,55 @@ test('has no effect if native HLS is available', function() { ...@@ -1227,4 +1227,55 @@ test('has no effect if native HLS is available', function() {
1227 player.dispose(); 1227 player.dispose();
1228 }); 1228 });
1229 1229
1230 test('tracks the bytes downloaded', function() {
1231 player.src({
1232 src: 'http://example.com/media.m3u8',
1233 type: 'application/vnd.apple.mpegurl'
1234 });
1235 player.hls.mediaSource.trigger({
1236 type: 'sourceopen'
1237 });
1238
1239 strictEqual(player.hls.bytesReceived, 0, 'no bytes received');
1240
1241 requests.shift().respond(200, null,
1242 '#EXTM3U\n' +
1243 '#EXTINF:10,\n' +
1244 '0.ts\n' +
1245 '#EXTINF:10,\n' +
1246 '1.ts\n' +
1247 '#EXT-X-ENDLIST\n');
1248 // transmit some segment bytes
1249 requests[0].response = new ArrayBuffer(17);
1250 requests.shift().respond(200, null, '');
1251
1252 strictEqual(player.hls.bytesReceived, 17, 'tracked bytes received');
1253
1254 player.trigger('timeupdate');
1255
1256 // transmit some more
1257 requests[0].response = new ArrayBuffer(5);
1258 requests.shift().respond(200, null, '');
1259
1260 strictEqual(player.hls.bytesReceived, 22, 'tracked more bytes');
1261 });
1262
1263 test('re-emits mediachange events', function() {
1264 var mediaChanges = 0;
1265 player.on('mediachange', function() {
1266 mediaChanges++;
1267 });
1268
1269 player.src({
1270 src: 'http://example.com/media.m3u8',
1271 type: 'application/vnd.apple.mpegurl'
1272 });
1273 player.hls.mediaSource.trigger({
1274 type: 'sourceopen'
1275 });
1276
1277 player.hls.playlists.trigger('mediachange');
1278 strictEqual(mediaChanges, 1, 'fired mediachange');
1279 });
1280
1230 })(window, window.videojs); 1281 })(window, window.videojs);
......