Reload playlists when EXT-X-ENDLIST isn't present
Modify the parser to include an attribute when the endlist tag shows up in a media playlist. Update test playlist parses to add the attribute where appropriate. Trigger a playlist reload and merge if endlist isn't present in the parse. Clean up some formatting.
Showing
40 changed files
with
415 additions
and
236 deletions
... | @@ -345,6 +345,9 @@ | ... | @@ -345,6 +345,9 @@ |
345 | byterange.offset = entry.offset; | 345 | byterange.offset = entry.offset; |
346 | } | 346 | } |
347 | }, | 347 | }, |
348 | 'endlist': function() { | ||
349 | this.manifest.endList = true; | ||
350 | }, | ||
348 | 'inf': function() { | 351 | 'inf': function() { |
349 | if (!('mediaSequence' in this.manifest)) { | 352 | if (!('mediaSequence' in this.manifest)) { |
350 | this.manifest.mediaSequence = 0; | 353 | this.manifest.mediaSequence = 0; |
... | @@ -462,12 +465,14 @@ | ... | @@ -462,12 +465,14 @@ |
462 | */ | 465 | */ |
463 | merge = function(base, update) { | 466 | merge = function(base, update) { |
464 | var | 467 | var |
465 | result = mergeOptions({}, base, update), | 468 | result = mergeOptions({}, base), |
466 | uri = update.segments[0].uri, | 469 | uri = update.segments[0].uri, |
467 | i = base.segments.length, | 470 | i = base.segments ? base.segments.length : 0, |
468 | byterange, | 471 | byterange, |
469 | segment; | 472 | segment; |
470 | 473 | ||
474 | result = mergeOptions(result, update); | ||
475 | |||
471 | // align and apply the updated segments | 476 | // align and apply the updated segments |
472 | while (i--) { | 477 | while (i--) { |
473 | segment = base.segments[i]; | 478 | segment = base.segments[i]; |
... | @@ -488,7 +493,7 @@ | ... | @@ -488,7 +493,7 @@ |
488 | } | 493 | } |
489 | // concatenate the two arrays if there was no overlap | 494 | // concatenate the two arrays if there was no overlap |
490 | if (i < 0) { | 495 | if (i < 0) { |
491 | result.segments = base.segments.concat(update.segments); | 496 | result.segments = (base.segments || []).concat(update.segments); |
492 | } | 497 | } |
493 | return result; | 498 | return result; |
494 | }; | 499 | }; | ... | ... |
... | @@ -341,8 +341,10 @@ var | ... | @@ -341,8 +341,10 @@ var |
341 | variant = bandwidthPlaylists[i]; | 341 | variant = bandwidthPlaylists[i]; |
342 | 342 | ||
343 | // ignore playlists without resolution information | 343 | // ignore playlists without resolution information |
344 | if (!variant.attributes || !variant.attributes.RESOLUTION || | 344 | if (!variant.attributes || |
345 | !variant.attributes.RESOLUTION.width || !variant.attributes.RESOLUTION.height) { | 345 | !variant.attributes.RESOLUTION || |
346 | !variant.attributes.RESOLUTION.width || | ||
347 | !variant.attributes.RESOLUTION.height) { | ||
346 | continue; | 348 | continue; |
347 | } | 349 | } |
348 | 350 | ||
... | @@ -350,7 +352,7 @@ var | ... | @@ -350,7 +352,7 @@ var |
350 | // dimensions less than or equal to the player size is the | 352 | // dimensions less than or equal to the player size is the |
351 | // best | 353 | // best |
352 | if (variant.attributes.RESOLUTION.width <= player.width() && | 354 | if (variant.attributes.RESOLUTION.width <= player.width() && |
353 | variant.attributes.RESOLUTION.height <= player.height()) { | 355 | variant.attributes.RESOLUTION.height <= player.height()) { |
354 | resolutionBestVariant = variant; | 356 | resolutionBestVariant = variant; |
355 | break; | 357 | break; |
356 | } | 358 | } |
... | @@ -375,88 +377,107 @@ var | ... | @@ -375,88 +377,107 @@ var |
375 | var xhr = new window.XMLHttpRequest(); | 377 | var xhr = new window.XMLHttpRequest(); |
376 | xhr.open('GET', url); | 378 | xhr.open('GET', url); |
377 | xhr.onreadystatechange = function() { | 379 | xhr.onreadystatechange = function() { |
378 | var i, parser, playlist, playlistUri; | 380 | var i, parser, playlist, playlistUri, refreshDelay; |
379 | |||
380 | if (xhr.readyState === 4) { | ||
381 | if (xhr.status >= 400 || this.status === 0) { | ||
382 | player.hls.error = { | ||
383 | status: xhr.status, | ||
384 | message: 'HLS playlist request error at URL: ' + url, | ||
385 | code: (xhr.status >= 500) ? 4 : 2 | ||
386 | }; | ||
387 | player.trigger('error'); | ||
388 | return; | ||
389 | } | ||
390 | 381 | ||
391 | // readystate DONE | 382 | // wait until the request completes |
392 | parser = new videojs.m3u8.Parser(); | 383 | if (xhr.readyState !== 4) { |
393 | parser.push(xhr.responseText); | 384 | return; |
385 | } | ||
394 | 386 | ||
395 | // master playlists | 387 | if (xhr.status >= 400 || this.status === 0) { |
396 | if (parser.manifest.playlists) { | 388 | player.hls.error = { |
397 | player.hls.master = parser.manifest; | 389 | status: xhr.status, |
398 | downloadPlaylist(resolveUrl(url, parser.manifest.playlists[0].uri)); | 390 | message: 'HLS playlist request error at URL: ' + url, |
399 | player.trigger('loadedmanifest'); | 391 | code: (xhr.status >= 500) ? 4 : 2 |
400 | return; | 392 | }; |
401 | } | 393 | player.trigger('error'); |
394 | return; | ||
395 | } | ||
402 | 396 | ||
403 | // media playlists | 397 | // readystate DONE |
404 | if (player.hls.master) { | 398 | parser = new videojs.m3u8.Parser(); |
405 | // merge this playlist into the master | 399 | parser.push(xhr.responseText); |
406 | i = player.hls.master.playlists.length; | 400 | |
407 | 401 | // master playlists | |
408 | while (i--) { | 402 | if (parser.manifest.playlists) { |
409 | playlist = player.hls.master.playlists[i]; | 403 | player.hls.master = parser.manifest; |
410 | playlistUri = resolveUrl(srcUrl, playlist.uri); | 404 | downloadPlaylist(resolveUrl(url, parser.manifest.playlists[0].uri)); |
411 | if (playlistUri === url) { | 405 | player.trigger('loadedmanifest'); |
412 | player.hls.master.playlists[i] = | 406 | return; |
413 | videojs.util.mergeOptions(playlist, parser.manifest); | 407 | } |
414 | } | ||
415 | } | ||
416 | } else { | ||
417 | // infer a master playlist if none was previously requested | ||
418 | player.hls.master = { | ||
419 | playlists: [parser.manifest] | ||
420 | }; | ||
421 | } | ||
422 | 408 | ||
423 | // always start playback with the default rendition | 409 | // media playlists |
424 | if (!player.hls.media) { | 410 | refreshDelay = (parser.manifest.targetDuration || 10) * 1000; |
425 | player.hls.media = player.hls.master.playlists[0]; | 411 | if (player.hls.master) { |
412 | // merge this playlist into the master | ||
413 | i = player.hls.master.playlists.length; | ||
414 | |||
415 | while (i--) { | ||
416 | playlist = player.hls.master.playlists[i]; | ||
417 | playlistUri = resolveUrl(srcUrl, playlist.uri); | ||
418 | if (playlistUri === url) { | ||
419 | // if the playlist is unchanged since the last reload, | ||
420 | // try again after half the target duration | ||
421 | // http://tools.ietf.org/html/draft-pantos-http-live-streaming-12#section-6.3.4 | ||
422 | if (playlist.segments && | ||
423 | playlist.segments.length === parser.manifest.segments.length) { | ||
424 | refreshDelay /= 2; | ||
425 | } | ||
426 | 426 | ||
427 | // update the duration | 427 | player.hls.master.playlists[i] = |
428 | if (parser.manifest.totalDuration) { | 428 | videojs.m3u8.merge(playlist, parser.manifest); |
429 | player.duration(parser.manifest.totalDuration); | ||
430 | } else { | ||
431 | player.duration(totalDuration(parser.manifest)); | ||
432 | } | 429 | } |
430 | } | ||
431 | } else { | ||
432 | // infer a master playlist if none was previously requested | ||
433 | player.hls.master = { | ||
434 | playlists: [parser.manifest] | ||
435 | }; | ||
436 | } | ||
433 | 437 | ||
434 | // periodicaly check if the buffer needs to be refilled | 438 | // check the playlist for updates if EXT-X-ENDLIST isn't present |
435 | player.on('timeupdate', fillBuffer); | 439 | if (!parser.manifest.endList) { |
440 | window.setTimeout(function() { | ||
441 | downloadPlaylist(url); | ||
442 | }, refreshDelay); | ||
443 | } | ||
436 | 444 | ||
437 | player.trigger('loadedmanifest'); | 445 | // always start playback with the default rendition |
438 | player.trigger('loadedmetadata'); | 446 | if (!player.hls.media) { |
439 | fillBuffer(); | 447 | player.hls.media = player.hls.master.playlists[0]; |
440 | return; | ||
441 | } | ||
442 | 448 | ||
443 | // select a playlist and download its metadata if necessary | 449 | // update the duration |
444 | playlist = player.hls.selectPlaylist(); | 450 | if (parser.manifest.totalDuration) { |
445 | if (!playlist.segments) { | 451 | player.duration(parser.manifest.totalDuration); |
446 | downloadPlaylist(resolveUrl(srcUrl, playlist.uri)); | ||
447 | } else { | 452 | } else { |
448 | player.hls.media = playlist; | 453 | player.duration(totalDuration(parser.manifest)); |
449 | |||
450 | // update the duration | ||
451 | if (player.hls.media.totalDuration) { | ||
452 | player.duration(player.hls.media.totalDuration); | ||
453 | } else { | ||
454 | player.duration(totalDuration(player.hls.media)); | ||
455 | } | ||
456 | } | 454 | } |
457 | 455 | ||
456 | // periodicaly check if the buffer needs to be refilled | ||
457 | player.on('timeupdate', fillBuffer); | ||
458 | |||
458 | player.trigger('loadedmanifest'); | 459 | player.trigger('loadedmanifest'); |
460 | player.trigger('loadedmetadata'); | ||
461 | fillBuffer(); | ||
462 | return; | ||
459 | } | 463 | } |
464 | |||
465 | // select a playlist and download its metadata if necessary | ||
466 | playlist = player.hls.selectPlaylist(); | ||
467 | if (!playlist.segments) { | ||
468 | downloadPlaylist(resolveUrl(srcUrl, playlist.uri)); | ||
469 | } else { | ||
470 | player.hls.media = playlist; | ||
471 | |||
472 | // update the duration | ||
473 | if (player.hls.media.totalDuration) { | ||
474 | player.duration(player.hls.media.totalDuration); | ||
475 | } else { | ||
476 | player.duration(totalDuration(player.hls.media)); | ||
477 | } | ||
478 | } | ||
479 | |||
480 | player.trigger('loadedmanifest'); | ||
460 | }; | 481 | }; |
461 | xhr.send(null); | 482 | xhr.send(null); |
462 | }; | 483 | }; | ... | ... |
... | @@ -514,7 +514,7 @@ | ... | @@ -514,7 +514,7 @@ |
514 | }); | 514 | }); |
515 | 515 | ||
516 | test('merges a manifest that strictly adds to an earlier one', function() { | 516 | test('merges a manifest that strictly adds to an earlier one', function() { |
517 | var key, base, mid, parsed; | 517 | var key, base, manifest, mid; |
518 | for (key in window.manifests) { | 518 | for (key in window.manifests) { |
519 | if (window.expected[key]) { | 519 | if (window.expected[key]) { |
520 | manifest = window.manifests[key]; | 520 | manifest = window.manifests[key]; | ... | ... |
1 | { | 1 | { |
2 | "allowCache": true, | 2 | "allowCache": true, |
3 | "playlists": [{ | 3 | "playlists": [ |
4 | "attributes": { | 4 | { |
5 | "PROGRAM-ID": 1, | 5 | "attributes": { |
6 | "BANDWIDTH": 240000, | 6 | "PROGRAM-ID": 1, |
7 | "RESOLUTION": { | 7 | "BANDWIDTH": 240000, |
8 | "width": 396, | 8 | "RESOLUTION": { |
9 | "height": 224 | 9 | "width": 396, |
10 | } | 10 | "height": 224 |
11 | } | ||
12 | }, | ||
13 | "uri": "http://c.brightcove.com/services/mobile/streaming/index/rendition.m3u8?assetId=1824686811001&videoId=1824650741001" | ||
11 | }, | 14 | }, |
12 | "uri": "http://c.brightcove.com/services/mobile/streaming/index/rendition.m3u8?assetId=1824686811001&videoId=1824650741001" | 15 | { |
13 | }, { | 16 | "attributes": { |
14 | "attributes": { | 17 | "PROGRAM-ID": 1, |
15 | "PROGRAM-ID": 1, | 18 | "BANDWIDTH": 40000 |
16 | "BANDWIDTH": 40000 | 19 | }, |
20 | "uri": "http://c.brightcove.com/services/mobile/streaming/index/rendition.m3u8?assetId=1824683759001&videoId=1824650741001" | ||
17 | }, | 21 | }, |
18 | "uri": "http://c.brightcove.com/services/mobile/streaming/index/rendition.m3u8?assetId=1824683759001&videoId=1824650741001" | 22 | { |
19 | }, { | 23 | "attributes": { |
20 | "attributes": { | 24 | "PROGRAM-ID": 1, |
21 | "PROGRAM-ID": 1, | 25 | "BANDWIDTH": 440000, |
22 | "BANDWIDTH": 440000, | 26 | "RESOLUTION": { |
23 | "RESOLUTION": { | 27 | "width": 396, |
24 | "width": 396, | 28 | "height": 224 |
25 | "height": 224 | 29 | } |
26 | } | 30 | }, |
31 | "uri": "http://c.brightcove.com/services/mobile/streaming/index/rendition.m3u8?assetId=1824686593001&videoId=1824650741001" | ||
27 | }, | 32 | }, |
28 | "uri": "http://c.brightcove.com/services/mobile/streaming/index/rendition.m3u8?assetId=1824686593001&videoId=1824650741001" | 33 | { |
29 | }, { | 34 | "attributes": { |
30 | "attributes": { | 35 | "PROGRAM-ID": 1, |
31 | "PROGRAM-ID": 1, | 36 | "BANDWIDTH": 1928000, |
32 | "BANDWIDTH": 1928000, | 37 | "RESOLUTION": { |
33 | "RESOLUTION": { | 38 | "width": 960, |
34 | "width": 960, | 39 | "height": 540 |
35 | "height": 540 | 40 | } |
36 | } | 41 | }, |
37 | }, | 42 | "uri": "http://c.brightcove.com/services/mobile/streaming/index/rendition.m3u8?assetId=1824687660001&videoId=1824650741001" |
38 | "uri": "http://c.brightcove.com/services/mobile/streaming/index/rendition.m3u8?assetId=1824687660001&videoId=1824650741001" | 43 | } |
39 | }] | 44 | ] |
40 | } | 45 | } | ... | ... |
... | @@ -20,5 +20,6 @@ | ... | @@ -20,5 +20,6 @@ |
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 | "endList": true | ||
24 | } | 25 | } |
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
... | @@ -27,5 +27,6 @@ | ... | @@ -27,5 +27,6 @@ |
27 | "uri": "/test/ts-files/zencoder/haze/Haze_Mantel_President_encoded_1200-00006.ts" | 27 | "uri": "/test/ts-files/zencoder/haze/Haze_Mantel_President_encoded_1200-00006.ts" |
28 | } | 28 | } |
29 | ], | 29 | ], |
30 | "targetDuration": 10 | 30 | "targetDuration": 10, |
31 | } | 31 | "endList": true |
32 | } | ||
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
1 | { | 1 | { |
2 | "allowCache": true, | 2 | "allowCache": true, |
3 | "playlists": [{ | 3 | "playlists": [ |
4 | "attributes": { | 4 | { |
5 | "PROGRAM-ID": 1, | 5 | "attributes": { |
6 | "BANDWIDTH": 240000, | 6 | "PROGRAM-ID": 1, |
7 | "RESOLUTION": { | 7 | "BANDWIDTH": 240000, |
8 | "width": 396, | 8 | "RESOLUTION": { |
9 | "height": 224 | 9 | "width": 396, |
10 | } | 10 | "height": 224 |
11 | } | ||
12 | }, | ||
13 | "uri": "http://c.brightcove.com/services/mobile/streaming/index/rendition.m3u8?assetId=1824686811001&videoId=1824650741001" | ||
11 | }, | 14 | }, |
12 | "uri": "http://c.brightcove.com/services/mobile/streaming/index/rendition.m3u8?assetId=1824686811001&videoId=1824650741001" | 15 | { |
13 | }, { | 16 | "attributes": { |
14 | "attributes": { | 17 | "PROGRAM-ID": 1, |
15 | "PROGRAM-ID": 1, | 18 | "BANDWIDTH": 40000 |
16 | "BANDWIDTH": 40000 | 19 | }, |
20 | "uri": "http://c.brightcove.com/services/mobile/streaming/index/rendition.m3u8?assetId=1824683759001&videoId=1824650741001" | ||
17 | }, | 21 | }, |
18 | "uri": "http://c.brightcove.com/services/mobile/streaming/index/rendition.m3u8?assetId=1824683759001&videoId=1824650741001" | 22 | { |
19 | }, { | 23 | "attributes": { |
20 | "attributes": { | 24 | "PROGRAM-ID": 1, |
21 | "PROGRAM-ID": 1, | 25 | "BANDWIDTH": 440000, |
22 | "BANDWIDTH": 440000, | 26 | "RESOLUTION": { |
23 | "RESOLUTION": { | 27 | "width": 396, |
24 | "width": 396, | 28 | "height": 224 |
25 | "height": 224 | 29 | } |
26 | } | 30 | }, |
31 | "uri": "http://c.brightcove.com/services/mobile/streaming/index/rendition.m3u8?assetId=1824686593001&videoId=1824650741001" | ||
27 | }, | 32 | }, |
28 | "uri": "http://c.brightcove.com/services/mobile/streaming/index/rendition.m3u8?assetId=1824686593001&videoId=1824650741001" | 33 | { |
29 | }, { | 34 | "attributes": { |
30 | "attributes": { | 35 | "PROGRAM-ID": 1, |
31 | "PROGRAM-ID": 1, | 36 | "BANDWIDTH": 1928000, |
32 | "BANDWIDTH": 1928000, | 37 | "RESOLUTION": { |
33 | "RESOLUTION": { | 38 | "width": 960, |
34 | "width": 960, | 39 | "height": 540 |
35 | "height": 540 | 40 | } |
36 | } | 41 | }, |
37 | }, | 42 | "uri": "http://c.brightcove.com/services/mobile/streaming/index/rendition.m3u8?assetId=1824687660001&videoId=1824650741001" |
38 | "uri": "http://c.brightcove.com/services/mobile/streaming/index/rendition.m3u8?assetId=1824687660001&videoId=1824650741001" | 43 | } |
39 | }] | 44 | ] |
40 | } | 45 | } | ... | ... |
... | @@ -28,5 +28,6 @@ | ... | @@ -28,5 +28,6 @@ |
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 | "endList": true | ||
32 | } | 33 | } |
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
... | @@ -7,5 +7,6 @@ | ... | @@ -7,5 +7,6 @@ |
7 | "uri": "/test/ts-files/tvy7/8a5e2822668b5370f4eb1438b2564fb7ab12ffe1-hi720.ts" | 7 | "uri": "/test/ts-files/tvy7/8a5e2822668b5370f4eb1438b2564fb7ab12ffe1-hi720.ts" |
8 | } | 8 | } |
9 | ], | 9 | ], |
10 | "targetDuration": 8 | 10 | "targetDuration": 8, |
11 | } | 11 | "endList": true |
12 | } | ||
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
... | @@ -20,5 +20,6 @@ | ... | @@ -20,5 +20,6 @@ |
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 | "endList": true | ||
24 | } | 25 | } |
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
... | @@ -27,5 +27,6 @@ | ... | @@ -27,5 +27,6 @@ |
27 | "uri": "/test/ts-files/zencoder/haze/Haze_Mantel_President_encoded_1200-00006.ts" | 27 | "uri": "/test/ts-files/zencoder/haze/Haze_Mantel_President_encoded_1200-00006.ts" |
28 | } | 28 | } |
29 | ], | 29 | ], |
30 | "targetDuration": 10 | 30 | "targetDuration": 10, |
31 | } | 31 | "endList": true |
32 | } | ||
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
... | @@ -23,5 +23,6 @@ | ... | @@ -23,5 +23,6 @@ |
23 | "uri": "/test/ts-files/zencoder/gogo/00005.ts" | 23 | "uri": "/test/ts-files/zencoder/gogo/00005.ts" |
24 | } | 24 | } |
25 | ], | 25 | ], |
26 | "targetDuration": 10 | 26 | "targetDuration": 10, |
27 | } | 27 | "endList": true |
28 | } | ||
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
... | @@ -7,5 +7,6 @@ | ... | @@ -7,5 +7,6 @@ |
7 | "uri": "/test/ts-files/zencoder/gogo/00001.ts" | 7 | "uri": "/test/ts-files/zencoder/gogo/00001.ts" |
8 | } | 8 | } |
9 | ], | 9 | ], |
10 | "targetDuration": 10 | 10 | "targetDuration": 10, |
11 | } | 11 | "endList": true |
12 | } | ||
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
1 | { | 1 | { |
2 | "allowCache": true, | 2 | "allowCache": true, |
3 | "playlists": [{ | 3 | "playlists": [ |
4 | "attributes": { | 4 | { |
5 | "PROGRAM-ID": 1, | 5 | "attributes": { |
6 | "BANDWIDTH": 240000, | 6 | "PROGRAM-ID": 1, |
7 | "RESOLUTION": { | 7 | "BANDWIDTH": 240000, |
8 | "width": 396, | 8 | "RESOLUTION": { |
9 | "height": 224 | 9 | "width": 396, |
10 | } | 10 | "height": 224 |
11 | } | ||
12 | }, | ||
13 | "uri": "media.m3u8" | ||
11 | }, | 14 | }, |
12 | "uri": "media.m3u8" | 15 | { |
13 | }, { | 16 | "attributes": { |
14 | "attributes": { | 17 | "PROGRAM-ID": 1, |
15 | "PROGRAM-ID": 1, | 18 | "BANDWIDTH": 40000 |
16 | "BANDWIDTH": 40000 | 19 | }, |
20 | "uri": "media1.m3u8" | ||
17 | }, | 21 | }, |
18 | "uri": "media1.m3u8" | 22 | { |
19 | }, { | 23 | "attributes": { |
20 | "attributes": { | 24 | "PROGRAM-ID": 1, |
21 | "PROGRAM-ID": 1, | 25 | "BANDWIDTH": 440000, |
22 | "BANDWIDTH": 440000, | 26 | "RESOLUTION": { |
23 | "RESOLUTION": { | 27 | "width": 396, |
24 | "width": 396, | 28 | "height": 224 |
25 | "height": 224 | 29 | } |
26 | } | 30 | }, |
31 | "uri": "media2.m3u8" | ||
27 | }, | 32 | }, |
28 | "uri": "media2.m3u8" | 33 | { |
29 | }, { | 34 | "attributes": { |
30 | "attributes": { | 35 | "PROGRAM-ID": 1, |
31 | "PROGRAM-ID": 1, | 36 | "BANDWIDTH": 1928000, |
32 | "BANDWIDTH": 1928000, | 37 | "RESOLUTION": { |
33 | "RESOLUTION": { | 38 | "width": 960, |
34 | "width": 960, | 39 | "height": 540 |
35 | "height": 540 | 40 | } |
36 | } | 41 | }, |
37 | }, | 42 | "uri": "media3.m3u8" |
38 | "uri": "media3.m3u8" | 43 | } |
39 | }] | 44 | ] |
40 | } | 45 | } | ... | ... |
... | @@ -20,5 +20,6 @@ | ... | @@ -20,5 +20,6 @@ |
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 | "endList": true | ||
24 | } | 25 | } |
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
test/manifest/missingEndlist.json
0 → 100644
test/manifest/missingEndlist.m3u8
0 → 100644
... | @@ -20,5 +20,6 @@ | ... | @@ -20,5 +20,6 @@ |
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 | "endList": true | ||
24 | } | 25 | } |
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
... | @@ -20,5 +20,6 @@ | ... | @@ -20,5 +20,6 @@ |
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 | } | 24 | "endList": true |
25 | } | ||
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
... | @@ -2,16 +2,21 @@ | ... | @@ -2,16 +2,21 @@ |
2 | "allowCache": true, | 2 | "allowCache": true, |
3 | "mediaSequence": 0, | 3 | "mediaSequence": 0, |
4 | "targetDuration": 10, | 4 | "targetDuration": 10, |
5 | "segments": [{ | 5 | "segments": [ |
6 | "uri": "001.ts" | 6 | { |
7 | }, { | 7 | "uri": "001.ts" |
8 | "uri": "002.ts", | 8 | }, |
9 | "duration": 9 | 9 | { |
10 | }, { | 10 | "uri": "002.ts", |
11 | "uri": "003.ts", | 11 | "duration": 9 |
12 | "duration": 7 | 12 | }, |
13 | }, { | 13 | { |
14 | "uri": "004.ts", | 14 | "uri": "003.ts", |
15 | "duration": 10 | 15 | "duration": 7 |
16 | }] | 16 | }, |
17 | { | ||
18 | "uri": "004.ts", | ||
19 | "duration": 10 | ||
20 | } | ||
21 | ] | ||
17 | } | 22 | } | ... | ... |
... | @@ -20,5 +20,6 @@ | ... | @@ -20,5 +20,6 @@ |
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 | "endList": true | ||
24 | } | 25 | } |
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
... | @@ -8,5 +8,6 @@ | ... | @@ -8,5 +8,6 @@ |
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 | "endList": true | ||
12 | } | 13 | } |
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
1 | { | 1 | { |
2 | "allowCache": true, | 2 | "allowCache": true, |
3 | "playlists": [ | 3 | "playlists": [ |
4 | { | 4 | { |
5 | "attributes": { | 5 | "attributes": { |
6 | "PROGRAM-ID": 1 | 6 | "PROGRAM-ID": 1 |
7 | }, | ||
8 | "uri": "media.m3u8" | ||
7 | }, | 9 | }, |
8 | "uri": "media.m3u8" | 10 | { |
9 | }, | 11 | "uri": "media1.m3u8" |
10 | { | 12 | } |
11 | "uri": "media1.m3u8" | ||
12 | } | ||
13 | ] | 13 | ] |
14 | } | 14 | } | ... | ... |
... | @@ -20,5 +20,6 @@ | ... | @@ -20,5 +20,6 @@ |
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 | "endList": true | ||
24 | } | 25 | } |
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
... | @@ -181,7 +181,7 @@ test('calculates the duration if needed', function() { | ... | @@ -181,7 +181,7 @@ test('calculates the duration if needed', function() { |
181 | } | 181 | } |
182 | durations.push(duration); | 182 | durations.push(duration); |
183 | }; | 183 | }; |
184 | player.hls('manifest/liveMissingSegmentDuration.m3u8'); | 184 | player.hls('http://example.com/manifest/liveMissingSegmentDuration.m3u8'); |
185 | videojs.mediaSources[player.currentSrc()].trigger({ | 185 | videojs.mediaSources[player.currentSrc()].trigger({ |
186 | type: 'sourceopen' | 186 | type: 'sourceopen' |
187 | }); | 187 | }); |
... | @@ -873,4 +873,87 @@ test('has no effect if native HLS is available', function() { | ... | @@ -873,4 +873,87 @@ test('has no effect if native HLS is available', function() { |
873 | 'no media source was opened'); | 873 | 'no media source was opened'); |
874 | }); | 874 | }); |
875 | 875 | ||
876 | test('reloads live playlists', function() { | ||
877 | var callbacks = []; | ||
878 | // capture timeouts | ||
879 | window.setTimeout = function(callback, timeout) { | ||
880 | callbacks.push({ callback: callback, timeout: timeout }); | ||
881 | }; | ||
882 | player.hls('manifest/missingEndlist.m3u8'); | ||
883 | videojs.mediaSources[player.currentSrc()].trigger({ | ||
884 | type: 'sourceopen' | ||
885 | }); | ||
886 | |||
887 | strictEqual(1, callbacks.length, 'refresh was scheduled'); | ||
888 | strictEqual(player.hls.media.targetDuration * 1000, | ||
889 | callbacks[0].timeout, | ||
890 | 'waited one target duration'); | ||
891 | }); | ||
892 | |||
893 | test('does not reload playlists with an endlist tag', function() { | ||
894 | var callbacks = []; | ||
895 | // capture timeouts | ||
896 | window.setTimeout = function(callback, timeout) { | ||
897 | callbacks.push({ callback: callback, timeout: timeout }); | ||
898 | }; | ||
899 | player.hls('manifest/media.m3u8'); | ||
900 | videojs.mediaSources[player.currentSrc()].trigger({ | ||
901 | type: 'sourceopen' | ||
902 | }); | ||
903 | |||
904 | strictEqual(0, callbacks.length, 'no refresh was scheduled'); | ||
905 | }); | ||
906 | |||
907 | test('reloads a live playlist after half a target duration if it has not ' + | ||
908 | 'changed since the last request', function() { | ||
909 | var callbacks = []; | ||
910 | // capture timeouts | ||
911 | window.setTimeout = function(callback, timeout) { | ||
912 | callbacks.push({ callback: callback, timeout: timeout }); | ||
913 | }; | ||
914 | player.hls('http://example.com/manifest/missingEndlist.m3u8'); | ||
915 | |||
916 | // an identical manifest has already been parsed | ||
917 | player.hls.media = videojs.util.mergeOptions({}, window.expected['missingEndlist']); | ||
918 | player.hls.media.uri = 'http://example.com/manifest/missingEndlist.m3u8'; | ||
919 | player.hls.master = { | ||
920 | playlists: [player.hls.media] | ||
921 | }; | ||
922 | |||
923 | videojs.mediaSources[player.currentSrc()].trigger({ | ||
924 | type: 'sourceopen' | ||
925 | }); | ||
926 | |||
927 | strictEqual(1, callbacks.length, 'refresh was scheduled'); | ||
928 | strictEqual(player.hls.media.targetDuration / 2 * 1000, | ||
929 | callbacks[0].timeout, | ||
930 | 'waited half a target duration'); | ||
931 | }); | ||
932 | |||
933 | test('merges playlist reloads', function() { | ||
934 | var | ||
935 | realMerge = videojs.m3u8.merge, | ||
936 | merges = 0, | ||
937 | callback; | ||
938 | // capture timeouts and playlist merges | ||
939 | window.setTimeout = function(cb) { | ||
940 | callback = cb; | ||
941 | }; | ||
942 | videojs.m3u8.merge = function(base, update) { | ||
943 | merges++; | ||
944 | return update; | ||
945 | }; | ||
946 | |||
947 | player.hls('http://example.com/manifest/missingEndlist.m3u8'); | ||
948 | videojs.mediaSources[player.currentSrc()].trigger({ | ||
949 | type: 'sourceopen' | ||
950 | }); | ||
951 | player.hls.media.uri = 'http://example.com/manifest/missingEndlist.m3u8'; | ||
952 | |||
953 | callback(); | ||
954 | strictEqual(1, merges, 'reloaded playlist was merged'); | ||
955 | |||
956 | videojs.m3u8.merge = realMerge; | ||
957 | }); | ||
958 | |||
876 | })(window, window.videojs); | 959 | })(window, window.videojs); | ... | ... |
-
Please register or sign in to post a comment