52799413 by David LaPalomento

Calculate bandwidth when downloading segments

Track the XHR duration and use that to figure out bandwidth when downloading segments. Ensure a fresh instance is assigned to player.hls on each plugin invocation so that state attached to the plugin object is not retained across loads.
1 parent 23f8e047
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
10 10
11 videojs.hls = {}; 11 videojs.hls = {};
12 12
13 videojs.plugin('hls', function(options) { 13 var init = function(options) {
14 var 14 var
15 mediaSource = new videojs.MediaSource(), 15 mediaSource = new videojs.MediaSource(),
16 segmentParser = new videojs.hls.SegmentParser(), 16 segmentParser = new videojs.hls.SegmentParser(),
...@@ -53,7 +53,8 @@ videojs.plugin('hls', function(options) { ...@@ -53,7 +53,8 @@ videojs.plugin('hls', function(options) {
53 var 53 var
54 xhr = new window.XMLHttpRequest(), 54 xhr = new window.XMLHttpRequest(),
55 segment = player.hls.currentPlaylist.segments[player.hls.currentMediaIndex], 55 segment = player.hls.currentPlaylist.segments[player.hls.currentMediaIndex],
56 segmentUri = segment.uri; 56 segmentUri = segment.uri,
57 startTime;
57 if (!(/^([A-z]*:)?\/\//).test(segmentUri)) { 58 if (!(/^([A-z]*:)?\/\//).test(segmentUri)) {
58 // the segment URI is relative to the manifest 59 // the segment URI is relative to the manifest
59 segmentUri = url.split('/').slice(0, -1).concat(segmentUri).join('/'); 60 segmentUri = url.split('/').slice(0, -1).concat(segmentUri).join('/');
...@@ -61,15 +62,24 @@ videojs.plugin('hls', function(options) { ...@@ -61,15 +62,24 @@ videojs.plugin('hls', function(options) {
61 xhr.open('GET', segmentUri); 62 xhr.open('GET', segmentUri);
62 xhr.responseType = 'arraybuffer'; 63 xhr.responseType = 'arraybuffer';
63 xhr.onreadystatechange = function() { 64 xhr.onreadystatechange = function() {
65 var elapsed;
64 if (xhr.readyState === 4) { 66 if (xhr.readyState === 4) {
65 player.hls.currentMediaIndex++; 67 // calculate the download bandwidth
68 elapsed = ((+new Date()) - startTime) * 1000;
69 player.hls.bandwidth = xhr.response.byteLength / elapsed;
70
71 // transmux the segment data from M2TS to FLV
66 segmentParser.parseSegmentBinaryData(new Uint8Array(xhr.response)); 72 segmentParser.parseSegmentBinaryData(new Uint8Array(xhr.response));
67 while (segmentParser.tagsAvailable()) { 73 while (segmentParser.tagsAvailable()) {
68 player.hls.sourceBuffer.appendBuffer(segmentParser.getNextTag().bytes, 74 player.hls.sourceBuffer.appendBuffer(segmentParser.getNextTag().bytes,
69 player); 75 player);
70 } 76 }
77
78 // update the segment index
79 player.hls.currentMediaIndex++;
71 } 80 }
72 }; 81 };
82 startTime = +new Date();
73 xhr.send(null); 83 xhr.send(null);
74 }; 84 };
75 player.on('loadedmetadata', fillBuffer); 85 player.on('loadedmetadata', fillBuffer);
...@@ -102,6 +112,16 @@ videojs.plugin('hls', function(options) { ...@@ -102,6 +112,16 @@ videojs.plugin('hls', function(options) {
102 src: videojs.URL.createObjectURL(mediaSource), 112 src: videojs.URL.createObjectURL(mediaSource),
103 type: "video/flv" 113 type: "video/flv"
104 }); 114 });
115 };
116
117 videojs.plugin('hls', function() {
118 var initialize = function() {
119 return function() {
120 this.hls = initialize();
121 init.apply(this, arguments);
122 };
123 };
124 initialize().apply(this, arguments);
105 }); 125 });
106 126
107 })(window, window.videojs); 127 })(window, window.videojs);
......
...@@ -42,6 +42,7 @@ module('HLS', { ...@@ -42,6 +42,7 @@ module('HLS', {
42 manifestName = manifestName[1]; 42 manifestName = manifestName[1];
43 } 43 }
44 this.responseText = window.manifests[manifestName || xhrParams[1]]; 44 this.responseText = window.manifests[manifestName || xhrParams[1]];
45 this.response = new Uint8Array([1]).buffer;
45 46
46 this.readyState = 4; 47 this.readyState = 4;
47 this.onreadystatechange(); 48 this.onreadystatechange();
...@@ -93,7 +94,7 @@ test('starts downloading a segment on loadedmetadata', function() { ...@@ -93,7 +94,7 @@ test('starts downloading a segment on loadedmetadata', function() {
93 strictEqual(xhrParams[1], 'manifest/00001.ts', 'the first segment is requested'); 94 strictEqual(xhrParams[1], 'manifest/00001.ts', 'the first segment is requested');
94 }); 95 });
95 96
96 test('recognizes absolute URIs and uses them umnodified', function() { 97 test('recognizes absolute URIs and requests them unmodified', function() {
97 player.hls('manifest/absoluteUris.m3u8'); 98 player.hls('manifest/absoluteUris.m3u8');
98 videojs.mediaSources[player.currentSrc()].trigger({ 99 videojs.mediaSources[player.currentSrc()].trigger({
99 type: 'sourceopen' 100 type: 'sourceopen'
...@@ -104,6 +105,28 @@ test('recognizes absolute URIs and uses them umnodified', function() { ...@@ -104,6 +105,28 @@ test('recognizes absolute URIs and uses them umnodified', function() {
104 'the first segment is requested'); 105 'the first segment is requested');
105 }); 106 });
106 107
108 test('re-initializes the plugin for each source', function() {
109 var firstInit, secondInit;
110 player.hls('manifest/master.m3u8');
111 firstInit = player.hls;
112 player.hls('manifest/master.m3u8');
113 secondInit = player.hls;
114
115 notStrictEqual(firstInit, secondInit, 'the plugin object is replaced');
116
117 });
118
119 test('calculates the bandwidth after downloading a segment', function() {
120 player.hls('manifest/media.m3u8');
121 videojs.mediaSources[player.currentSrc()].trigger({
122 type: 'sourceopen'
123 });
124
125 ok(player.hls.bandwidth, 'bandwidth is calculated');
126 ok(player.hls.bandwidth > 0,
127 'bandwidth is positive: ' + player.hls.bandwidth);
128 });
129
107 module('segment controller', { 130 module('segment controller', {
108 setup: function() { 131 setup: function() {
109 segmentController = new window.videojs.hls.SegmentController(); 132 segmentController = new window.videojs.hls.SegmentController();
......