5448f63f by David LaPalomento

Duration should be infinity for live playlists

Unless an explicit total duration tag is present in the playlist, set the duration to Infinity for live HLS. Document iOS behavior for live streams.
1 parent 83a9f86f
1 # Live HLS Research 1 # Live HLS Research
2 This document is a collection of notes on Live HLS implementations in the wild. 2 This document is a collection of notes on Live HLS implementations in the wild.
3 3
4 There are two varieties of Live HLS. In the first, playlists are
5 persistent and strictly appended to. In the alternative form, the
6 maximum number of segments in a playlist is relatively stable and an
7 old segment is removed every time a new segment becomes available.
8
9 On iOS devices, both stream types report a duration of `Infinity`. The
10 `currentTime` is equal to the amount of the stream that has been
11 played back on the device.
12
4 ## Akamai HD2 13 ## Akamai HD2
5 14
6 ## OnceLIVE 15 ## OnceLIVE
16 "Sliding window" live streams.
7 17
8 ### Variant Playlists 18 ### Variant Playlists
9 Once variant playlists look like standard HLS variant playlists. 19 Once variant playlists look like standard HLS variant playlists.
......
...@@ -132,6 +132,11 @@ var ...@@ -132,6 +132,11 @@ var
132 duration = 0, 132 duration = 0,
133 i = playlist.segments.length, 133 i = playlist.segments.length,
134 segment; 134 segment;
135 // duration should be Infinity for live playlists
136 if (!playlist.endList) {
137 return window.Infinity;
138 }
139
135 while (i--) { 140 while (i--) {
136 segment = playlist.segments[i]; 141 segment = playlist.segments[i];
137 duration += segment.duration || playlist.targetDuration || 0; 142 duration += segment.duration || playlist.targetDuration || 0;
......
...@@ -181,13 +181,15 @@ test('calculates the duration if needed', function() { ...@@ -181,13 +181,15 @@ test('calculates the duration if needed', function() {
181 } 181 }
182 durations.push(duration); 182 durations.push(duration);
183 }; 183 };
184 player.hls('http://example.com/manifest/liveMissingSegmentDuration.m3u8'); 184 player.hls('http://example.com/manifest/missingExtinf.m3u8');
185 videojs.mediaSources[player.currentSrc()].trigger({ 185 videojs.mediaSources[player.currentSrc()].trigger({
186 type: 'sourceopen' 186 type: 'sourceopen'
187 }); 187 });
188 188
189 strictEqual(durations.length, 1, 'duration is set'); 189 strictEqual(durations.length, 1, 'duration is set');
190 strictEqual(durations[0], 6.64 + (2 * 8), 'duration is calculated'); 190 strictEqual(durations[0],
191 player.hls.media.segments.length * 10,
192 'duration is calculated');
191 }); 193 });
192 194
193 test('starts downloading a segment on loadedmetadata', function() { 195 test('starts downloading a segment on loadedmetadata', function() {
...@@ -890,6 +892,15 @@ test('reloads live playlists', function() { ...@@ -890,6 +892,15 @@ test('reloads live playlists', function() {
890 'waited one target duration'); 892 'waited one target duration');
891 }); 893 });
892 894
895 test('duration is Infinity for live playlists', function() {
896 player.hls('manifest/missingEndlist.m3u8');
897 videojs.mediaSources[player.currentSrc()].trigger({
898 type: 'sourceopen'
899 });
900
901 strictEqual(Infinity, player.duration(), 'duration is infinity');
902 });
903
893 test('does not reload playlists with an endlist tag', function() { 904 test('does not reload playlists with an endlist tag', function() {
894 var callbacks = []; 905 var callbacks = [];
895 // capture timeouts 906 // capture timeouts
......