a72ed64c by David LaPalomento

Do not calculate totalDuration at parse time

Unless a comment specifies the total duration, do not add the property to the parsed manifest object. Also, do not default it to zero if it is not defined in the manifest. Instead, calculate the total duration from the target duration and segment metadata whenever a new playlist is selected.
1 parent 162eb81e
...@@ -298,8 +298,7 @@ ...@@ -298,8 +298,7 @@
298 298
299 // the manifest is empty until the parse stream begins delivering data 299 // the manifest is empty until the parse stream begins delivering data
300 this.manifest = { 300 this.manifest = {
301 allowCache: true, 301 allowCache: true
302 totalDuration: 0
303 }; 302 };
304 303
305 // update the manifest with the m3u8 entry from the parse stream 304 // update the manifest with the m3u8 entry from the parse stream
...@@ -406,24 +405,6 @@ ...@@ -406,24 +405,6 @@
406 return; 405 return;
407 } 406 }
408 this.manifest.totalDuration = entry.duration; 407 this.manifest.totalDuration = entry.duration;
409 },
410 'endlist': function() {
411 var i, calculatedDuration = 0;
412 if (this.manifest.totalDuration === 0) {
413 for (i = 0; i < this.manifest.segments.length; i++) {
414 if (this.manifest.segments[i].duration) {
415 calculatedDuration += this.manifest.segments[i].duration;
416 } else if (this.manifest.targetDuration > 0) {
417 calculatedDuration += this.manifest.targetDuration;
418 } else {
419 calculatedDuration += 0;
420 }
421 }
422 this.manifest.totalDuration = calculatedDuration;
423 this.trigger('info', {
424 message: 'updating total duration to use a calculated value'
425 });
426 }
427 } 408 }
428 })[entry.tagType] || noop).call(self); 409 })[entry.tagType] || noop).call(self);
429 }, 410 },
......
...@@ -39,7 +39,7 @@ var ...@@ -39,7 +39,7 @@ var
39 * @param time 39 * @param time
40 * @returns int 40 * @returns int
41 */ 41 */
42 getMediaIndexByTime = function (playlist, time) { 42 getMediaIndexByTime = function(playlist, time) {
43 var index, counter, timeRanges, currentSegmentRange; 43 var index, counter, timeRanges, currentSegmentRange;
44 44
45 timeRanges = []; 45 timeRanges = [];
...@@ -59,7 +59,7 @@ var ...@@ -59,7 +59,7 @@ var
59 return -1; 59 return -1;
60 60
61 }, 61 },
62 getPtsByTime = function (segmentParser, time) { 62 getPtsByTime = function(segmentParser, time) {
63 var index = 0; 63 var index = 0;
64 64
65 for (index; index<segmentParser.getTags().length; index++) { 65 for (index; index<segmentParser.getTags().length; index++) {
...@@ -74,6 +74,23 @@ var ...@@ -74,6 +74,23 @@ var
74 }, 74 },
75 75
76 /** 76 /**
77 * Calculate the total duration for a playlist based on segment metadata.
78 * @param playlist {object} a media playlist object
79 * @return {number} the currently known duration, in seconds
80 */
81 totalDuration = function(playlist) {
82 var
83 duration = 0,
84 i = playlist.segments.length,
85 segment;
86 while (i--) {
87 segment = playlist.segments[i];
88 duration += segment.duration || playlist.targetDuration || 0;
89 }
90 return duration;
91 },
92
93 /**
77 * Constructs a new URI by interpreting a path relative to another 94 * Constructs a new URI by interpreting a path relative to another
78 * URI. 95 * URI.
79 * @param basePath {string} a relative or absolute URI 96 * @param basePath {string} a relative or absolute URI
...@@ -254,6 +271,8 @@ var ...@@ -254,6 +271,8 @@ var
254 player.duration(parser.manifest.totalDuration); 271 player.duration(parser.manifest.totalDuration);
255 // Notify the flash layer 272 // Notify the flash layer
256 //player.el().querySelector('.vjs-tech').vjs_setProperty('duration',parser.manifest.totalDuration); 273 //player.el().querySelector('.vjs-tech').vjs_setProperty('duration',parser.manifest.totalDuration);
274 } else {
275 player.duration(totalDuration(parser.manifest));
257 } 276 }
258 player.trigger('loadedmanifest'); 277 player.trigger('loadedmanifest');
259 player.trigger('loadedmetadata'); 278 player.trigger('loadedmetadata');
...@@ -266,9 +285,11 @@ var ...@@ -266,9 +285,11 @@ var
266 downloadPlaylist(resolveUrl(srcUrl, playlist.uri)); 285 downloadPlaylist(resolveUrl(srcUrl, playlist.uri));
267 } else { 286 } else {
268 player.hls.media = playlist; 287 player.hls.media = playlist;
269 if (parser.manifest.totalDuration) { 288 if (player.hls.media.totalDuration) {
270 // update the duration 289 // update the duration
271 player.duration(parser.manifest.totalDuration); 290 player.duration(player.hls.media.totalDuration);
291 } else {
292 player.duration(totalDuration(player.hls.media));
272 } 293 }
273 } 294 }
274 295
......
...@@ -20,6 +20,5 @@ ...@@ -20,6 +20,5 @@
20 "uri": "http://example.com/00004.ts" 20 "uri": "http://example.com/00004.ts"
21 } 21 }
22 ], 22 ],
23 "targetDuration": 10, 23 "targetDuration": 10
24 "totalDuration": 40
25 } 24 }
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -140,6 +140,5 @@ ...@@ -140,6 +140,5 @@
140 "uri": "hls_450k_video.ts" 140 "uri": "hls_450k_video.ts"
141 } 141 }
142 ], 142 ],
143 "targetDuration": 10, 143 "targetDuration": 10
144 "totalDuration": 161.4167
145 } 144 }
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -12,6 +12,5 @@ ...@@ -12,6 +12,5 @@
12 "uri": "hls_450k_video.ts" 12 "uri": "hls_450k_video.ts"
13 } 13 }
14 ], 14 ],
15 "targetDuration": 10, 15 "targetDuration": 10
16 "totalDuration": 10
17 } 16 }
...\ No newline at end of file ...\ No newline at end of file
......
1 { 1 {
2 "allowCache": true, 2 "allowCache": true,
3 "totalDuration": 0,
4 "playlists": [{ 3 "playlists": [{
5 "attributes": { 4 "attributes": {
6 "PROGRAM-ID": 1, 5 "PROGRAM-ID": 1,
......
...@@ -136,6 +136,5 @@ ...@@ -136,6 +136,5 @@
136 "uri": "hls_450k_video.ts" 136 "uri": "hls_450k_video.ts"
137 } 137 }
138 ], 138 ],
139 "targetDuration": 10, 139 "targetDuration": 10
140 "totalDuration": 161.4167
141 } 140 }
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -12,6 +12,5 @@ ...@@ -12,6 +12,5 @@
12 "uri": "hls_450k_video.ts" 12 "uri": "hls_450k_video.ts"
13 } 13 }
14 ], 14 ],
15 "targetDuration": 10, 15 "targetDuration": 10
16 "totalDuration": 10
17 } 16 }
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -20,6 +20,5 @@ ...@@ -20,6 +20,5 @@
20 "uri": "/00004.ts" 20 "uri": "/00004.ts"
21 } 21 }
22 ], 22 ],
23 "targetDuration": 10, 23 "targetDuration": 10
24 "totalDuration": 40
25 } 24 }
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -12,6 +12,5 @@ ...@@ -12,6 +12,5 @@
12 "uri": "hls_450k_video.ts" 12 "uri": "hls_450k_video.ts"
13 } 13 }
14 ], 14 ],
15 "targetDuration": 10, 15 "targetDuration": 10
16 "totalDuration": 10
17 } 16 }
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -20,6 +20,5 @@ ...@@ -20,6 +20,5 @@
20 "uri": "/test/ts-files/tvy7/6cfa378684ffeb1c455a64dae6c103290a1f53d4-hi720.ts" 20 "uri": "/test/ts-files/tvy7/6cfa378684ffeb1c455a64dae6c103290a1f53d4-hi720.ts"
21 } 21 }
22 ], 22 ],
23 "targetDuration": 8, 23 "targetDuration": 8
24 "totalDuration": 24.32
25 } 24 }
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -28,6 +28,5 @@ ...@@ -28,6 +28,5 @@
28 "uri": "/test/ts-files/zencoder/haze/Haze_Mantel_President_encoded_1200-00006.ts" 28 "uri": "/test/ts-files/zencoder/haze/Haze_Mantel_President_encoded_1200-00006.ts"
29 } 29 }
30 ], 30 ],
31 "targetDuration": 10, 31 "targetDuration": 10
32 "totalDuration": 58
33 } 32 }
...\ No newline at end of file ...\ No newline at end of file
......
1 { 1 {
2 "allowCache": true, 2 "allowCache": true,
3 "totalDuration": 0,
4 "playlists": [{ 3 "playlists": [{
5 "attributes": { 4 "attributes": {
6 "PROGRAM-ID": 1, 5 "PROGRAM-ID": 1,
......
...@@ -28,6 +28,5 @@ ...@@ -28,6 +28,5 @@
28 "uri": "/test/ts-files/zencoder/haze/Haze_Mantel_President_encoded_1200-00006.ts" 28 "uri": "/test/ts-files/zencoder/haze/Haze_Mantel_President_encoded_1200-00006.ts"
29 } 29 }
30 ], 30 ],
31 "targetDuration": 10, 31 "targetDuration": 10
32 "totalDuration": 58
33 } 32 }
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -8,6 +8,5 @@ ...@@ -8,6 +8,5 @@
8 "uri": "/test/ts-files/tvy7/8a5e2822668b5370f4eb1438b2564fb7ab12ffe1-hi720.ts" 8 "uri": "/test/ts-files/tvy7/8a5e2822668b5370f4eb1438b2564fb7ab12ffe1-hi720.ts"
9 } 9 }
10 ], 10 ],
11 "targetDuration": 8, 11 "targetDuration": 8
12 "totalDuration": 6.64
13 } 12 }
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -138,6 +138,5 @@ ...@@ -138,6 +138,5 @@
138 "uri": "hls_450k_video.ts" 138 "uri": "hls_450k_video.ts"
139 } 139 }
140 ], 140 ],
141 "targetDuration": 10, 141 "targetDuration": 10
142 "totalDuration": 164.7
143 } 142 }
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -12,6 +12,5 @@ ...@@ -12,6 +12,5 @@
12 "uri": "hls_450k_video.ts" 12 "uri": "hls_450k_video.ts"
13 } 13 }
14 ], 14 ],
15 "targetDuration": 10, 15 "targetDuration": 10
16 "totalDuration": 10
17 } 16 }
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -20,6 +20,5 @@ ...@@ -20,6 +20,5 @@
20 "uri": "/test/ts-files/tvy7/6cfa378684ffeb1c455a64dae6c103290a1f53d4-hi720.ts" 20 "uri": "/test/ts-files/tvy7/6cfa378684ffeb1c455a64dae6c103290a1f53d4-hi720.ts"
21 } 21 }
22 ], 22 ],
23 "targetDuration": 8, 23 "targetDuration": 8
24 "totalDuration": 24.32
25 } 24 }
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -28,6 +28,5 @@ ...@@ -28,6 +28,5 @@
28 "uri": "/test/ts-files/zencoder/haze/Haze_Mantel_President_encoded_1200-00006.ts" 28 "uri": "/test/ts-files/zencoder/haze/Haze_Mantel_President_encoded_1200-00006.ts"
29 } 29 }
30 ], 30 ],
31 "targetDuration": 10, 31 "targetDuration": 10
32 "totalDuration": 58
33 } 32 }
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -139,6 +139,5 @@ ...@@ -139,6 +139,5 @@
139 "duration": 1.4167, 139 "duration": 1.4167,
140 "uri": "hls_450k_video.ts" 140 "uri": "hls_450k_video.ts"
141 } 141 }
142 ], 142 ]
143 "totalDuration": 161.4167
144 } 143 }
...\ No newline at end of file ...\ No newline at end of file
......
1 {
2 "allowCache": true,
3 "mediaSequence": 0,
4 "playlistType": "VOD",
5 "segments": [
6 {
7 "duration": 6.64,
8 "uri": "/test/ts-files/tvy7/8a5e2822668b5370f4eb1438b2564fb7ab12ffe1-hi720.ts"
9 },
10 {
11 "uri": "/test/ts-files/tvy7/56be1cef869a1c0cc8e38864ad1add17d187f051-hi720.ts"
12 },
13 {
14 "uri": "/test/ts-files/tvy7/549c8c77f55f049741a06596e5c1e01dacaa46d0-hi720.ts"
15 }
16 ],
17 "targetDuration": 8
18 }
...\ No newline at end of file ...\ No newline at end of file
1 #EXTM3U
2 #EXT-X-PLAYLIST-TYPE:VOD
3 #EXT-X-MEDIA-SEQUENCE:0
4 #EXT-X-ALLOW-CACHE:YES
5 #EXT-X-TARGETDURATION:8
6 #EXTINF:6.640,{}
7 /test/ts-files/tvy7/8a5e2822668b5370f4eb1438b2564fb7ab12ffe1-hi720.ts
8 /test/ts-files/tvy7/56be1cef869a1c0cc8e38864ad1add17d187f051-hi720.ts
9 /test/ts-files/tvy7/549c8c77f55f049741a06596e5c1e01dacaa46d0-hi720.ts
...@@ -7,6 +7,5 @@ ...@@ -7,6 +7,5 @@
7 "duration": 10, 7 "duration": 10,
8 "uri": "/test/ts-files/zencoder/gogo/00001.ts" 8 "uri": "/test/ts-files/zencoder/gogo/00001.ts"
9 } 9 }
10 ], 10 ]
11 "totalDuration": 10
12 } 11 }
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -24,6 +24,5 @@ ...@@ -24,6 +24,5 @@
24 "uri": "/test/ts-files/zencoder/gogo/00005.ts" 24 "uri": "/test/ts-files/zencoder/gogo/00005.ts"
25 } 25 }
26 ], 26 ],
27 "targetDuration": 10, 27 "targetDuration": 10
28 "totalDuration": 30
29 } 28 }
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -8,6 +8,5 @@ ...@@ -8,6 +8,5 @@
8 "uri": "/test/ts-files/zencoder/gogo/00001.ts" 8 "uri": "/test/ts-files/zencoder/gogo/00001.ts"
9 } 9 }
10 ], 10 ],
11 "targetDuration": 10, 11 "targetDuration": 10
12 "totalDuration": 10
13 } 12 }
...\ No newline at end of file ...\ No newline at end of file
......
1 { 1 {
2 "allowCache": true, 2 "allowCache": true,
3 "totalDuration": 0,
4 "playlists": [{ 3 "playlists": [{
5 "attributes": { 4 "attributes": {
6 "PROGRAM-ID": 1, 5 "PROGRAM-ID": 1,
......
...@@ -20,6 +20,5 @@ ...@@ -20,6 +20,5 @@
20 "uri": "00004.ts" 20 "uri": "00004.ts"
21 } 21 }
22 ], 22 ],
23 "targetDuration": 10, 23 "targetDuration": 10
24 "totalDuration": 40
25 } 24 }
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -20,6 +20,5 @@ ...@@ -20,6 +20,5 @@
20 "uri": "/test/ts-files/tvy7/6cfa378684ffeb1c455a64dae6c103290a1f53d4-hi720.ts" 20 "uri": "/test/ts-files/tvy7/6cfa378684ffeb1c455a64dae6c103290a1f53d4-hi720.ts"
21 } 21 }
22 ], 22 ],
23 "targetDuration": 8, 23 "targetDuration": 8
24 "totalDuration": 24.32
25 } 24 }
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -15,6 +15,5 @@ ...@@ -15,6 +15,5 @@
15 "uri": "hls_450k_video.ts" 15 "uri": "hls_450k_video.ts"
16 } 16 }
17 ], 17 ],
18 "targetDuration": 10, 18 "targetDuration": 10
19 "totalDuration": 30
20 } 19 }
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -20,6 +20,5 @@ ...@@ -20,6 +20,5 @@
20 "uri": "/test/ts-files/tvy7/6cfa378684ffeb1c455a64dae6c103290a1f53d4-hi720.ts" 20 "uri": "/test/ts-files/tvy7/6cfa378684ffeb1c455a64dae6c103290a1f53d4-hi720.ts"
21 } 21 }
22 ], 22 ],
23 "targetDuration": 8, 23 "targetDuration": 8
24 "totalDuration": 24.32
25 } 24 }
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -17,6 +17,5 @@ ...@@ -17,6 +17,5 @@
17 "uri": "/test/ts-files/tvy7/6cfa378684ffeb1c455a64dae6c103290a1f53d4-hi720.ts" 17 "uri": "/test/ts-files/tvy7/6cfa378684ffeb1c455a64dae6c103290a1f53d4-hi720.ts"
18 } 18 }
19 ], 19 ],
20 "targetDuration": 8, 20 "targetDuration": 8
21 "totalDuration": 30.64
22 } 21 }
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -20,6 +20,5 @@ ...@@ -20,6 +20,5 @@
20 "uri": "/test/ts-files/tvy7/6cfa378684ffeb1c455a64dae6c103290a1f53d4-hi720.ts" 20 "uri": "/test/ts-files/tvy7/6cfa378684ffeb1c455a64dae6c103290a1f53d4-hi720.ts"
21 } 21 }
22 ], 22 ],
23 "targetDuration": 8, 23 "targetDuration": 8
24 "totalDuration": 24.32
25 } 24 }
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -140,6 +140,5 @@ ...@@ -140,6 +140,5 @@
140 "uri": "hls_450k_video.ts" 140 "uri": "hls_450k_video.ts"
141 } 141 }
142 ], 142 ],
143 "targetDuration": 10, 143 "targetDuration": 10
144 "totalDuration": 161.4167
145 } 144 }
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -8,6 +8,5 @@ ...@@ -8,6 +8,5 @@
8 "uri": "/test/ts-files/tvy7/8a5e2822668b5370f4eb1438b2564fb7ab12ffe1-hi720.ts" 8 "uri": "/test/ts-files/tvy7/8a5e2822668b5370f4eb1438b2564fb7ab12ffe1-hi720.ts"
9 } 9 }
10 ], 10 ],
11 "targetDuration": 8, 11 "targetDuration": 8
12 "totalDuration": 6.64
13 } 12 }
...\ No newline at end of file ...\ No newline at end of file
......
1 { 1 {
2 "allowCache": true, 2 "allowCache": true,
3 "totalDuration": 0,
4 "playlists": [ 3 "playlists": [
5 { 4 {
6 "attributes": { 5 "attributes": {
......
...@@ -20,6 +20,5 @@ ...@@ -20,6 +20,5 @@
20 "uri": "/test/ts-files/tvy7/6cfa378684ffeb1c455a64dae6c103290a1f53d4-hi720.ts" 20 "uri": "/test/ts-files/tvy7/6cfa378684ffeb1c455a64dae6c103290a1f53d4-hi720.ts"
21 } 21 }
22 ], 22 ],
23 "targetDuration": 8, 23 "targetDuration": 8
24 "totalDuration": 24.32
25 } 24 }
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -8,6 +8,5 @@ ...@@ -8,6 +8,5 @@
8 "uri": "hls_450k_video.ts" 8 "uri": "hls_450k_video.ts"
9 } 9 }
10 ], 10 ],
11 "targetDuration": 10, 11 "targetDuration": 10
12 "totalDuration": 10
13 } 12 }
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -124,6 +124,23 @@ test('sets the duration if one is available on the playlist', function() { ...@@ -124,6 +124,23 @@ test('sets the duration if one is available on the playlist', function() {
124 strictEqual(1, calls, 'duration is set'); 124 strictEqual(1, calls, 'duration is set');
125 }); 125 });
126 126
127 test('calculates the duration if needed', function() {
128 var durations = [];
129 player.duration = function(duration) {
130 if (duration === undefined) {
131 return 0;
132 }
133 durations.push(duration);
134 };
135 player.hls('manifest/liveMissingSegmentDuration.m3u8');
136 videojs.mediaSources[player.currentSrc()].trigger({
137 type: 'sourceopen'
138 });
139
140 strictEqual(durations.length, 1, 'duration is set');
141 strictEqual(durations[0], 6.64 + (2 * 8), 'duration is calculated');
142 });
143
127 test('starts downloading a segment on loadedmetadata', function() { 144 test('starts downloading a segment on loadedmetadata', function() {
128 player.hls('manifest/media.m3u8'); 145 player.hls('manifest/media.m3u8');
129 player.buffered = function() { 146 player.buffered = function() {
...@@ -226,6 +243,32 @@ test('selects a playlist after segment downloads', function() { ...@@ -226,6 +243,32 @@ test('selects a playlist after segment downloads', function() {
226 strictEqual(calls, 2, 'selects after additional segments'); 243 strictEqual(calls, 2, 'selects after additional segments');
227 }); 244 });
228 245
246 test('updates the duration after switching playlists', function() {
247 var
248 calls = 0,
249 selectedPlaylist = false;
250 player.hls('manifest/master.m3u8');
251 player.hls.selectPlaylist = function() {
252 selectPlaylist = true;
253 return player.hls.master.playlists[1];
254 };
255 player.duration = function(duration) {
256 if (duration === undefined) {
257 return 0;
258 }
259 // only track calls that occur after the playlist has been switched
260 if (player.hls.media === player.hls.master.playlists[1]) {
261 calls++;
262 }
263 };
264 videojs.mediaSources[player.currentSrc()].trigger({
265 type: 'sourceopen'
266 });
267
268 ok(selectPlaylist, 'selected playlist');
269 strictEqual(calls, 1, 'updates the duration');
270 });
271
229 test('downloads additional playlists if required', function() { 272 test('downloads additional playlists if required', function() {
230 var 273 var
231 called = false, 274 called = false,
......