21c692d7 by Steve Heffernan

Merge branch 'hotfix/stream-ended' of https://github.com/videojs/videojs-contrib…

…-hls into reorg-w-114

close #114 closes #115

Conflicts:
	src/videojs-hls.js
2 parents b22f4c3f 99abd78b
...@@ -158,6 +158,19 @@ videojs.Hls.prototype.handleSourceOpen = function() { ...@@ -158,6 +158,19 @@ videojs.Hls.prototype.handleSourceOpen = function() {
158 }); 158 });
159 }; 159 };
160 160
161 /**
162 * Reset the mediaIndex if play() is called after the video has
163 * ended.
164 */
165 videojs.Hls.prototype.play = function() {
166 if (this.ended()) {
167 this.mediaIndex = 0;
168 }
169
170 // delegate back to the Flash implementation
171 return videojs.Flash.prototype.play.apply(this, arguments);
172 };
173
161 videojs.Hls.prototype.duration = function() { 174 videojs.Hls.prototype.duration = function() {
162 var playlists = this.playlists; 175 var playlists = this.playlists;
163 if (playlists) { 176 if (playlists) {
...@@ -458,7 +471,9 @@ videojs.Hls.prototype.drainBuffer = function(event) { ...@@ -458,7 +471,9 @@ videojs.Hls.prototype.drainBuffer = function(event) {
458 // we're done processing this segment 471 // we're done processing this segment
459 segmentBuffer.shift(); 472 segmentBuffer.shift();
460 473
461 if (mediaIndex === playlist.segments.length) { 474 // transition the sourcebuffer to the ended state if we've hit the end of
475 // the playlist
476 if (mediaIndex + 1 === playlist.segments.length) {
462 this.mediaSource.endOfStream(); 477 this.mediaSource.endOfStream();
463 } 478 }
464 }; 479 };
......
...@@ -51,10 +51,18 @@ var ...@@ -51,10 +51,18 @@ var
51 tech.vjs_getProperty = function() {}; 51 tech.vjs_getProperty = function() {};
52 tech.vjs_setProperty = function() {}; 52 tech.vjs_setProperty = function() {};
53 tech.vjs_src = function() {}; 53 tech.vjs_src = function() {};
54 tech.vjs_play = function() {};
54 videojs.Flash.onReady(tech.id); 55 videojs.Flash.onReady(tech.id);
55 56
56 return player; 57 return player;
57 }, 58 },
59 openMediaSource = function(player) {
60 player.hls.mediaSource.trigger({
61 type: 'sourceopen'
62 });
63 // endOfStream triggers an exception if flash isn't available
64 player.hls.mediaSource.endOfStream = function() {};
65 },
58 standardXHRResponse = function(request) { 66 standardXHRResponse = function(request) {
59 if (!request.url) { 67 if (!request.url) {
60 return; 68 return;
...@@ -160,9 +168,7 @@ test('starts playing if autoplay is specified', function() { ...@@ -160,9 +168,7 @@ test('starts playing if autoplay is specified', function() {
160 src: 'manifest/playlist.m3u8', 168 src: 'manifest/playlist.m3u8',
161 type: 'application/vnd.apple.mpegurl' 169 type: 'application/vnd.apple.mpegurl'
162 }); 170 });
163 player.hls.mediaSource.trigger({ 171 openMediaSource(player);
164 type: 'sourceopen'
165 });
166 172
167 standardXHRResponse(requests[0]); 173 standardXHRResponse(requests[0]);
168 strictEqual(1, plays, 'play was called'); 174 strictEqual(1, plays, 'play was called');
...@@ -179,9 +185,7 @@ test('creates a PlaylistLoader on init', function() { ...@@ -179,9 +185,7 @@ test('creates a PlaylistLoader on init', function() {
179 src:'manifest/playlist.m3u8', 185 src:'manifest/playlist.m3u8',
180 type: 'application/vnd.apple.mpegurl' 186 type: 'application/vnd.apple.mpegurl'
181 }); 187 });
182 player.hls.mediaSource.trigger({ 188 openMediaSource(player);
183 type: 'sourceopen'
184 });
185 standardXHRResponse(requests[0]); 189 standardXHRResponse(requests[0]);
186 ok(loadedmetadata, 'loadedmetadata fires'); 190 ok(loadedmetadata, 'loadedmetadata fires');
187 ok(player.hls.playlists.master, 'set the master playlist'); 191 ok(player.hls.playlists.master, 'set the master playlist');
...@@ -204,9 +208,7 @@ test('sets the duration if one is available on the playlist', function() { ...@@ -204,9 +208,7 @@ test('sets the duration if one is available on the playlist', function() {
204 src: 'manifest/media.m3u8', 208 src: 'manifest/media.m3u8',
205 type: 'application/vnd.apple.mpegurl' 209 type: 'application/vnd.apple.mpegurl'
206 }); 210 });
207 player.hls.mediaSource.trigger({ 211 openMediaSource(player);
208 type: 'sourceopen'
209 });
210 212
211 standardXHRResponse(requests[0]); 213 standardXHRResponse(requests[0]);
212 strictEqual(calls, 1, 'duration is set'); 214 strictEqual(calls, 1, 'duration is set');
...@@ -226,9 +228,7 @@ test('calculates the duration if needed', function() { ...@@ -226,9 +228,7 @@ test('calculates the duration if needed', function() {
226 src: 'http://example.com/manifest/missingExtinf.m3u8', 228 src: 'http://example.com/manifest/missingExtinf.m3u8',
227 type: 'application/vnd.apple.mpegurl' 229 type: 'application/vnd.apple.mpegurl'
228 }); 230 });
229 player.hls.mediaSource.trigger({ 231 openMediaSource(player);
230 type: 'sourceopen'
231 });
232 232
233 standardXHRResponse(requests[0]); 233 standardXHRResponse(requests[0]);
234 strictEqual(durations.length, 1, 'duration is set'); 234 strictEqual(durations.length, 1, 'duration is set');
...@@ -245,9 +245,7 @@ test('starts downloading a segment on loadedmetadata', function() { ...@@ -245,9 +245,7 @@ test('starts downloading a segment on loadedmetadata', function() {
245 player.buffered = function() { 245 player.buffered = function() {
246 return videojs.createTimeRange(0, 0); 246 return videojs.createTimeRange(0, 0);
247 }; 247 };
248 player.hls.mediaSource.trigger({ 248 openMediaSource(player);
249 type: 'sourceopen'
250 });
251 249
252 standardXHRResponse(requests[0]); 250 standardXHRResponse(requests[0]);
253 standardXHRResponse(requests[1]); 251 standardXHRResponse(requests[1]);
...@@ -263,9 +261,7 @@ test('recognizes absolute URIs and requests them unmodified', function() { ...@@ -263,9 +261,7 @@ test('recognizes absolute URIs and requests them unmodified', function() {
263 src: 'manifest/absoluteUris.m3u8', 261 src: 'manifest/absoluteUris.m3u8',
264 type: 'application/vnd.apple.mpegurl' 262 type: 'application/vnd.apple.mpegurl'
265 }); 263 });
266 player.hls.mediaSource.trigger({ 264 openMediaSource(player);
267 type: 'sourceopen'
268 });
269 265
270 standardXHRResponse(requests[0]); 266 standardXHRResponse(requests[0]);
271 standardXHRResponse(requests[1]); 267 standardXHRResponse(requests[1]);
...@@ -279,9 +275,7 @@ test('recognizes domain-relative URLs', function() { ...@@ -279,9 +275,7 @@ test('recognizes domain-relative URLs', function() {
279 src: 'manifest/domainUris.m3u8', 275 src: 'manifest/domainUris.m3u8',
280 type: 'application/vnd.apple.mpegurl' 276 type: 'application/vnd.apple.mpegurl'
281 }); 277 });
282 player.hls.mediaSource.trigger({ 278 openMediaSource(player);
283 type: 'sourceopen'
284 });
285 279
286 standardXHRResponse(requests[0]); 280 standardXHRResponse(requests[0]);
287 standardXHRResponse(requests[1]); 281 standardXHRResponse(requests[1]);
...@@ -297,9 +291,7 @@ test('re-initializes the tech for each source', function() { ...@@ -297,9 +291,7 @@ test('re-initializes the tech for each source', function() {
297 src: 'manifest/master.m3u8', 291 src: 'manifest/master.m3u8',
298 type: 'application/vnd.apple.mpegurl' 292 type: 'application/vnd.apple.mpegurl'
299 }); 293 });
300 player.hls.mediaSource.trigger({ 294 openMediaSource(player);
301 type: 'sourceopen'
302 });
303 firstPlaylists = player.hls.playlists; 295 firstPlaylists = player.hls.playlists;
304 firstMSE = player.hls.mediaSource; 296 firstMSE = player.hls.mediaSource;
305 297
...@@ -307,9 +299,7 @@ test('re-initializes the tech for each source', function() { ...@@ -307,9 +299,7 @@ test('re-initializes the tech for each source', function() {
307 src: 'manifest/master.m3u8', 299 src: 'manifest/master.m3u8',
308 type: 'application/vnd.apple.mpegurl' 300 type: 'application/vnd.apple.mpegurl'
309 }); 301 });
310 player.hls.mediaSource.trigger({ 302 openMediaSource(player);
311 type: 'sourceopen'
312 });
313 secondPlaylists = player.hls.playlists; 303 secondPlaylists = player.hls.playlists;
314 secondMSE = player.hls.mediaSource; 304 secondMSE = player.hls.mediaSource;
315 305
...@@ -326,9 +316,7 @@ test('triggers an error when a master playlist request errors', function() { ...@@ -326,9 +316,7 @@ test('triggers an error when a master playlist request errors', function() {
326 src: 'manifest/master.m3u8', 316 src: 'manifest/master.m3u8',
327 type: 'application/vnd.apple.mpegurl' 317 type: 'application/vnd.apple.mpegurl'
328 }); 318 });
329 player.hls.mediaSource.trigger({ 319 openMediaSource(player);
330 type: 'sourceopen'
331 });
332 requests.pop().respond(500); 320 requests.pop().respond(500);
333 321
334 ok(player.error(), 'an error is triggered'); 322 ok(player.error(), 'an error is triggered');
...@@ -341,9 +329,7 @@ test('downloads media playlists after loading the master', function() { ...@@ -341,9 +329,7 @@ test('downloads media playlists after loading the master', function() {
341 src: 'manifest/master.m3u8', 329 src: 'manifest/master.m3u8',
342 type: 'application/vnd.apple.mpegurl' 330 type: 'application/vnd.apple.mpegurl'
343 }); 331 });
344 player.hls.mediaSource.trigger({ 332 openMediaSource(player);
345 type: 'sourceopen'
346 });
347 333
348 standardXHRResponse(requests[0]); 334 standardXHRResponse(requests[0]);
349 standardXHRResponse(requests[1]); 335 standardXHRResponse(requests[1]);
...@@ -367,9 +353,7 @@ test('timeupdates do not check to fill the buffer until a media playlist is read ...@@ -367,9 +353,7 @@ test('timeupdates do not check to fill the buffer until a media playlist is read
367 src: 'manifest/media.m3u8', 353 src: 'manifest/media.m3u8',
368 type: 'application/vnd.apple.mpegurl' 354 type: 'application/vnd.apple.mpegurl'
369 }); 355 });
370 player.hls.mediaSource.trigger({ 356 openMediaSource(player);
371 type: 'sourceopen'
372 });
373 player.trigger('timeupdate'); 357 player.trigger('timeupdate');
374 358
375 strictEqual(1, requests.length, 'one request was made'); 359 strictEqual(1, requests.length, 'one request was made');
...@@ -381,9 +365,7 @@ test('calculates the bandwidth after downloading a segment', function() { ...@@ -381,9 +365,7 @@ test('calculates the bandwidth after downloading a segment', function() {
381 src: 'manifest/media.m3u8', 365 src: 'manifest/media.m3u8',
382 type: 'application/vnd.apple.mpegurl' 366 type: 'application/vnd.apple.mpegurl'
383 }); 367 });
384 player.hls.mediaSource.trigger({ 368 openMediaSource(player);
385 type: 'sourceopen'
386 });
387 369
388 standardXHRResponse(requests[0]); 370 standardXHRResponse(requests[0]);
389 standardXHRResponse(requests[1]); 371 standardXHRResponse(requests[1]);
...@@ -405,9 +387,7 @@ test('selects a playlist after segment downloads', function() { ...@@ -405,9 +387,7 @@ test('selects a playlist after segment downloads', function() {
405 calls++; 387 calls++;
406 return player.hls.playlists.master.playlists[0]; 388 return player.hls.playlists.master.playlists[0];
407 }; 389 };
408 player.hls.mediaSource.trigger({ 390 openMediaSource(player);
409 type: 'sourceopen'
410 });
411 391
412 standardXHRResponse(requests[0]); 392 standardXHRResponse(requests[0]);
413 standardXHRResponse(requests[1]); 393 standardXHRResponse(requests[1]);
...@@ -433,9 +413,7 @@ test('moves to the next segment if there is a network error', function() { ...@@ -433,9 +413,7 @@ test('moves to the next segment if there is a network error', function() {
433 src: 'manifest/master.m3u8', 413 src: 'manifest/master.m3u8',
434 type: 'application/vnd.apple.mpegurl' 414 type: 'application/vnd.apple.mpegurl'
435 }); 415 });
436 player.hls.mediaSource.trigger({ 416 openMediaSource(player);
437 type: 'sourceopen'
438 });
439 417
440 standardXHRResponse(requests[0]); 418 standardXHRResponse(requests[0]);
441 standardXHRResponse(requests[1]); 419 standardXHRResponse(requests[1]);
...@@ -468,9 +446,7 @@ test('updates the duration after switching playlists', function() { ...@@ -468,9 +446,7 @@ test('updates the duration after switching playlists', function() {
468 calls++; 446 calls++;
469 } 447 }
470 }; 448 };
471 player.hls.mediaSource.trigger({ 449 openMediaSource(player);
472 type: 'sourceopen'
473 });
474 450
475 standardXHRResponse(requests[0]); 451 standardXHRResponse(requests[0]);
476 standardXHRResponse(requests[1]); 452 standardXHRResponse(requests[1]);
...@@ -490,9 +466,7 @@ test('downloads additional playlists if required', function() { ...@@ -490,9 +466,7 @@ test('downloads additional playlists if required', function() {
490 src: 'manifest/master.m3u8', 466 src: 'manifest/master.m3u8',
491 type: 'application/vnd.apple.mpegurl' 467 type: 'application/vnd.apple.mpegurl'
492 }); 468 });
493 player.hls.mediaSource.trigger({ 469 openMediaSource(player);
494 type: 'sourceopen'
495 });
496 470
497 standardXHRResponse(requests[0]); 471 standardXHRResponse(requests[0]);
498 standardXHRResponse(requests[1]); 472 standardXHRResponse(requests[1]);
...@@ -531,9 +505,7 @@ test('selects a playlist below the current bandwidth', function() { ...@@ -531,9 +505,7 @@ test('selects a playlist below the current bandwidth', function() {
531 src: 'manifest/master.m3u8', 505 src: 'manifest/master.m3u8',
532 type: 'application/vnd.apple.mpegurl' 506 type: 'application/vnd.apple.mpegurl'
533 }); 507 });
534 player.hls.mediaSource.trigger({ 508 openMediaSource(player);
535 type: 'sourceopen'
536 });
537 509
538 standardXHRResponse(requests[0]); 510 standardXHRResponse(requests[0]);
539 511
...@@ -556,9 +528,7 @@ test('raises the minimum bitrate for a stream proportionially', function() { ...@@ -556,9 +528,7 @@ test('raises the minimum bitrate for a stream proportionially', function() {
556 src: 'manifest/master.m3u8', 528 src: 'manifest/master.m3u8',
557 type: 'application/vnd.apple.mpegurl' 529 type: 'application/vnd.apple.mpegurl'
558 }); 530 });
559 player.hls.mediaSource.trigger({ 531 openMediaSource(player);
560 type: 'sourceopen'
561 });
562 532
563 standardXHRResponse(requests[0]); 533 standardXHRResponse(requests[0]);
564 534
...@@ -581,9 +551,7 @@ test('uses the lowest bitrate if no other is suitable', function() { ...@@ -581,9 +551,7 @@ test('uses the lowest bitrate if no other is suitable', function() {
581 src: 'manifest/master.m3u8', 551 src: 'manifest/master.m3u8',
582 type: 'application/vnd.apple.mpegurl' 552 type: 'application/vnd.apple.mpegurl'
583 }); 553 });
584 player.hls.mediaSource.trigger({ 554 openMediaSource(player);
585 type: 'sourceopen'
586 });
587 555
588 standardXHRResponse(requests[0]); 556 standardXHRResponse(requests[0]);
589 557
...@@ -605,9 +573,7 @@ test('selects the correct rendition by player dimensions', function() { ...@@ -605,9 +573,7 @@ test('selects the correct rendition by player dimensions', function() {
605 type: 'application/vnd.apple.mpegurl' 573 type: 'application/vnd.apple.mpegurl'
606 }); 574 });
607 575
608 player.hls.mediaSource.trigger({ 576 openMediaSource(player);
609 type: 'sourceopen'
610 });
611 577
612 standardXHRResponse(requests[0]); 578 standardXHRResponse(requests[0]);
613 579
...@@ -644,9 +610,7 @@ test('does not download the next segment if the buffer is full', function() { ...@@ -644,9 +610,7 @@ test('does not download the next segment if the buffer is full', function() {
644 player.buffered = function() { 610 player.buffered = function() {
645 return videojs.createTimeRange(0, currentTime + videojs.Hls.GOAL_BUFFER_LENGTH); 611 return videojs.createTimeRange(0, currentTime + videojs.Hls.GOAL_BUFFER_LENGTH);
646 }; 612 };
647 player.hls.mediaSource.trigger({ 613 openMediaSource(player);
648 type: 'sourceopen'
649 });
650 614
651 standardXHRResponse(requests[0]); 615 standardXHRResponse(requests[0]);
652 616
...@@ -660,9 +624,7 @@ test('downloads the next segment if the buffer is getting low', function() { ...@@ -660,9 +624,7 @@ test('downloads the next segment if the buffer is getting low', function() {
660 src: 'manifest/media.m3u8', 624 src: 'manifest/media.m3u8',
661 type: 'application/vnd.apple.mpegurl' 625 type: 'application/vnd.apple.mpegurl'
662 }); 626 });
663 player.hls.mediaSource.trigger({ 627 openMediaSource(player);
664 type: 'sourceopen'
665 });
666 628
667 standardXHRResponse(requests[0]); 629 standardXHRResponse(requests[0]);
668 standardXHRResponse(requests[1]); 630 standardXHRResponse(requests[1]);
...@@ -691,9 +653,7 @@ test('stops downloading segments at the end of the playlist', function() { ...@@ -691,9 +653,7 @@ test('stops downloading segments at the end of the playlist', function() {
691 src: 'manifest/media.m3u8', 653 src: 'manifest/media.m3u8',
692 type: 'application/vnd.apple.mpegurl' 654 type: 'application/vnd.apple.mpegurl'
693 }); 655 });
694 player.hls.mediaSource.trigger({ 656 openMediaSource(player);
695 type: 'sourceopen'
696 });
697 standardXHRResponse(requests[0]); 657 standardXHRResponse(requests[0]);
698 requests = []; 658 requests = [];
699 player.hls.mediaIndex = 4; 659 player.hls.mediaIndex = 4;
...@@ -707,9 +667,7 @@ test('only makes one segment request at a time', function() { ...@@ -707,9 +667,7 @@ test('only makes one segment request at a time', function() {
707 src: 'manifest/media.m3u8', 667 src: 'manifest/media.m3u8',
708 type: 'application/vnd.apple.mpegurl' 668 type: 'application/vnd.apple.mpegurl'
709 }); 669 });
710 player.hls.mediaSource.trigger({ 670 openMediaSource(player);
711 type: 'sourceopen'
712 });
713 standardXHRResponse(requests.pop()); 671 standardXHRResponse(requests.pop());
714 player.trigger('timeupdate'); 672 player.trigger('timeupdate');
715 673
...@@ -723,9 +681,7 @@ test('cancels outstanding XHRs when seeking', function() { ...@@ -723,9 +681,7 @@ test('cancels outstanding XHRs when seeking', function() {
723 src: 'manifest/media.m3u8', 681 src: 'manifest/media.m3u8',
724 type: 'application/vnd.apple.mpegurl' 682 type: 'application/vnd.apple.mpegurl'
725 }); 683 });
726 player.hls.mediaSource.trigger({ 684 openMediaSource(player);
727 type: 'sourceopen'
728 });
729 standardXHRResponse(requests[0]); 685 standardXHRResponse(requests[0]);
730 player.hls.media = { 686 player.hls.media = {
731 segments: [{ 687 segments: [{
...@@ -764,9 +720,7 @@ test('flushes the parser after each segment', function() { ...@@ -764,9 +720,7 @@ test('flushes the parser after each segment', function() {
764 src: 'manifest/media.m3u8', 720 src: 'manifest/media.m3u8',
765 type: 'application/vnd.apple.mpegurl' 721 type: 'application/vnd.apple.mpegurl'
766 }); 722 });
767 player.hls.mediaSource.trigger({ 723 openMediaSource(player);
768 type: 'sourceopen'
769 });
770 724
771 standardXHRResponse(requests[0]); 725 standardXHRResponse(requests[0]);
772 standardXHRResponse(requests[1]); 726 standardXHRResponse(requests[1]);
...@@ -794,9 +748,7 @@ test('drops tags before the target timestamp when seeking', function() { ...@@ -794,9 +748,7 @@ test('drops tags before the target timestamp when seeking', function() {
794 src: 'manifest/media.m3u8', 748 src: 'manifest/media.m3u8',
795 type: 'application/vnd.apple.mpegurl' 749 type: 'application/vnd.apple.mpegurl'
796 }); 750 });
797 player.hls.mediaSource.trigger({ 751 openMediaSource(player);
798 type: 'sourceopen'
799 });
800 standardXHRResponse(requests[0]); 752 standardXHRResponse(requests[0]);
801 standardXHRResponse(requests[1]); 753 standardXHRResponse(requests[1]);
802 754
...@@ -836,9 +788,7 @@ test('calls abort() on the SourceBuffer before seeking', function() { ...@@ -836,9 +788,7 @@ test('calls abort() on the SourceBuffer before seeking', function() {
836 src: 'manifest/media.m3u8', 788 src: 'manifest/media.m3u8',
837 type: 'application/vnd.apple.mpegurl' 789 type: 'application/vnd.apple.mpegurl'
838 }); 790 });
839 player.hls.mediaSource.trigger({ 791 openMediaSource(player);
840 type: 'sourceopen'
841 });
842 792
843 standardXHRResponse(requests[0]); 793 standardXHRResponse(requests[0]);
844 standardXHRResponse(requests[1]); 794 standardXHRResponse(requests[1]);
...@@ -863,9 +813,7 @@ test('playlist 404 should trigger MEDIA_ERR_NETWORK', function() { ...@@ -863,9 +813,7 @@ test('playlist 404 should trigger MEDIA_ERR_NETWORK', function() {
863 src: 'manifest/media.m3u8', 813 src: 'manifest/media.m3u8',
864 type: 'application/vnd.apple.mpegurl' 814 type: 'application/vnd.apple.mpegurl'
865 }); 815 });
866 player.hls.mediaSource.trigger({ 816 openMediaSource(player);
867 type: 'sourceopen'
868 });
869 requests.pop().respond(404); 817 requests.pop().respond(404);
870 818
871 equal(errorTriggered, 819 equal(errorTriggered,
...@@ -883,9 +831,7 @@ test('segment 404 should trigger MEDIA_ERR_NETWORK', function () { ...@@ -883,9 +831,7 @@ test('segment 404 should trigger MEDIA_ERR_NETWORK', function () {
883 type: 'application/vnd.apple.mpegurl' 831 type: 'application/vnd.apple.mpegurl'
884 }); 832 });
885 833
886 player.hls.mediaSource.trigger({ 834 openMediaSource(player);
887 type: 'sourceopen'
888 });
889 835
890 standardXHRResponse(requests[0]); 836 standardXHRResponse(requests[0]);
891 requests[1].respond(404); 837 requests[1].respond(404);
...@@ -899,9 +845,7 @@ test('segment 500 should trigger MEDIA_ERR_ABORTED', function () { ...@@ -899,9 +845,7 @@ test('segment 500 should trigger MEDIA_ERR_ABORTED', function () {
899 type: 'application/vnd.apple.mpegurl' 845 type: 'application/vnd.apple.mpegurl'
900 }); 846 });
901 847
902 player.hls.mediaSource.trigger({ 848 openMediaSource(player);
903 type: 'sourceopen'
904 });
905 849
906 standardXHRResponse(requests[0]); 850 standardXHRResponse(requests[0]);
907 requests[1].respond(500); 851 requests[1].respond(500);
...@@ -914,9 +858,7 @@ test('duration is Infinity for live playlists', function() { ...@@ -914,9 +858,7 @@ test('duration is Infinity for live playlists', function() {
914 src: 'http://example.com/manifest/missingEndlist.m3u8', 858 src: 'http://example.com/manifest/missingEndlist.m3u8',
915 type: 'application/vnd.apple.mpegurl' 859 type: 'application/vnd.apple.mpegurl'
916 }); 860 });
917 player.hls.mediaSource.trigger({ 861 openMediaSource(player);
918 type: 'sourceopen'
919 });
920 862
921 standardXHRResponse(requests[0]); 863 standardXHRResponse(requests[0]);
922 864
...@@ -929,9 +871,7 @@ test('updates the media index when a playlist reloads', function() { ...@@ -929,9 +871,7 @@ test('updates the media index when a playlist reloads', function() {
929 src: 'http://example.com/live-updating.m3u8', 871 src: 'http://example.com/live-updating.m3u8',
930 type: 'application/vnd.apple.mpegurl' 872 type: 'application/vnd.apple.mpegurl'
931 }); 873 });
932 player.hls.mediaSource.trigger({ 874 openMediaSource(player);
933 type: 'sourceopen'
934 });
935 875
936 requests[0].respond(200, null, 876 requests[0].respond(200, null,
937 '#EXTM3U\n' + 877 '#EXTM3U\n' +
...@@ -971,9 +911,7 @@ test('mediaIndex is zero before the first segment loads', function() { ...@@ -971,9 +911,7 @@ test('mediaIndex is zero before the first segment loads', function() {
971 src: 'http://example.com/first-seg-load.m3u8', 911 src: 'http://example.com/first-seg-load.m3u8',
972 type: 'application/vnd.apple.mpegurl' 912 type: 'application/vnd.apple.mpegurl'
973 }); 913 });
974 player.hls.mediaSource.trigger({ 914 openMediaSource(player);
975 type: 'sourceopen'
976 });
977 915
978 strictEqual(player.hls.mediaIndex, 0, 'mediaIndex is zero'); 916 strictEqual(player.hls.mediaIndex, 0, 'mediaIndex is zero');
979 }); 917 });
...@@ -983,9 +921,7 @@ test('reloads out-of-date live playlists when switching variants', function() { ...@@ -983,9 +921,7 @@ test('reloads out-of-date live playlists when switching variants', function() {
983 src: 'http://example.com/master.m3u8', 921 src: 'http://example.com/master.m3u8',
984 type: 'application/vnd.apple.mpegurl' 922 type: 'application/vnd.apple.mpegurl'
985 }); 923 });
986 player.hls.mediaSource.trigger({ 924 openMediaSource(player);
987 type: 'sourceopen'
988 });
989 925
990 player.hls.master = { 926 player.hls.master = {
991 playlists: [{ 927 playlists: [{
...@@ -1026,9 +962,7 @@ test('if withCredentials option is used, withCredentials is set on the XHR objec ...@@ -1026,9 +962,7 @@ test('if withCredentials option is used, withCredentials is set on the XHR objec
1026 src: 'http://example.com/media.m3u8', 962 src: 'http://example.com/media.m3u8',
1027 type: 'application/vnd.apple.mpegurl' 963 type: 'application/vnd.apple.mpegurl'
1028 }); 964 });
1029 player.hls.mediaSource.trigger({ 965 openMediaSource(player);
1030 type: 'sourceopen'
1031 });
1032 ok(requests[0].withCredentials, "with credentials should be set to true if that option is passed in"); 966 ok(requests[0].withCredentials, "with credentials should be set to true if that option is passed in");
1033 }); 967 });
1034 968
...@@ -1038,9 +972,7 @@ test('does not break if the playlist has no segments', function() { ...@@ -1038,9 +972,7 @@ test('does not break if the playlist has no segments', function() {
1038 type: 'application/vnd.apple.mpegurl' 972 type: 'application/vnd.apple.mpegurl'
1039 }); 973 });
1040 try { 974 try {
1041 player.hls.mediaSource.trigger({ 975 openMediaSource(player);
1042 type: 'sourceopen'
1043 });
1044 requests[0].respond(200, null, 976 requests[0].respond(200, null,
1045 '#EXTM3U\n' + 977 '#EXTM3U\n' +
1046 '#EXT-X-PLAYLIST-TYPE:VOD\n' + 978 '#EXT-X-PLAYLIST-TYPE:VOD\n' +
...@@ -1060,9 +992,7 @@ test('waits until the buffer is empty before appending bytes at a discontinuity' ...@@ -1060,9 +992,7 @@ test('waits until the buffer is empty before appending bytes at a discontinuity'
1060 src: 'disc.m3u8', 992 src: 'disc.m3u8',
1061 type: 'application/vnd.apple.mpegurl' 993 type: 'application/vnd.apple.mpegurl'
1062 }); 994 });
1063 player.hls.mediaSource.trigger({ 995 openMediaSource(player);
1064 type: 'sourceopen'
1065 });
1066 player.currentTime = function() { return currentTime; }; 996 player.currentTime = function() { return currentTime; };
1067 player.buffered = function() { 997 player.buffered = function() {
1068 return videojs.createTimeRange(0, bufferEnd); 998 return videojs.createTimeRange(0, bufferEnd);
...@@ -1109,9 +1039,7 @@ test('clears the segment buffer on seek', function() { ...@@ -1109,9 +1039,7 @@ test('clears the segment buffer on seek', function() {
1109 src: 'disc.m3u8', 1039 src: 'disc.m3u8',
1110 type: 'application/vnd.apple.mpegurl' 1040 type: 'application/vnd.apple.mpegurl'
1111 }); 1041 });
1112 player.hls.mediaSource.trigger({ 1042 openMediaSource(player);
1113 type: 'sourceopen'
1114 });
1115 oldCurrentTime = player.currentTime; 1043 oldCurrentTime = player.currentTime;
1116 player.currentTime = function(time) { 1044 player.currentTime = function(time) {
1117 if (time !== undefined) { 1045 if (time !== undefined) {
...@@ -1158,9 +1086,7 @@ test('resets the switching algorithm if a request times out', function() { ...@@ -1158,9 +1086,7 @@ test('resets the switching algorithm if a request times out', function() {
1158 src: 'master.m3u8', 1086 src: 'master.m3u8',
1159 type: 'application/vnd.apple.mpegurl' 1087 type: 'application/vnd.apple.mpegurl'
1160 }); 1088 });
1161 player.hls.mediaSource.trigger({ 1089 openMediaSource(player);
1162 type: 'sourceopen'
1163 });
1164 standardXHRResponse(requests.shift()); // master 1090 standardXHRResponse(requests.shift()); // master
1165 standardXHRResponse(requests.shift()); // media.m3u8 1091 standardXHRResponse(requests.shift()); // media.m3u8
1166 // simulate a segment timeout 1092 // simulate a segment timeout
...@@ -1181,9 +1107,7 @@ test('disposes the playlist loader', function() { ...@@ -1181,9 +1107,7 @@ test('disposes the playlist loader', function() {
1181 src: 'manifest/master.m3u8', 1107 src: 'manifest/master.m3u8',
1182 type: 'application/vnd.apple.mpegurl' 1108 type: 'application/vnd.apple.mpegurl'
1183 }); 1109 });
1184 player.hls.mediaSource.trigger({ 1110 openMediaSource(player);
1185 type: 'sourceopen'
1186 });
1187 loaderDispose = player.hls.playlists.dispose; 1111 loaderDispose = player.hls.playlists.dispose;
1188 player.hls.playlists.dispose = function() { 1112 player.hls.playlists.dispose = function() {
1189 disposes++; 1113 disposes++;
...@@ -1232,9 +1156,7 @@ test('tracks the bytes downloaded', function() { ...@@ -1232,9 +1156,7 @@ test('tracks the bytes downloaded', function() {
1232 src: 'http://example.com/media.m3u8', 1156 src: 'http://example.com/media.m3u8',
1233 type: 'application/vnd.apple.mpegurl' 1157 type: 'application/vnd.apple.mpegurl'
1234 }); 1158 });
1235 player.hls.mediaSource.trigger({ 1159 openMediaSource(player);
1236 type: 'sourceopen'
1237 });
1238 1160
1239 strictEqual(player.hls.bytesReceived, 0, 'no bytes received'); 1161 strictEqual(player.hls.bytesReceived, 0, 'no bytes received');
1240 1162
...@@ -1270,9 +1192,7 @@ test('re-emits mediachange events', function() { ...@@ -1270,9 +1192,7 @@ test('re-emits mediachange events', function() {
1270 src: 'http://example.com/media.m3u8', 1192 src: 'http://example.com/media.m3u8',
1271 type: 'application/vnd.apple.mpegurl' 1193 type: 'application/vnd.apple.mpegurl'
1272 }); 1194 });
1273 player.hls.mediaSource.trigger({ 1195 openMediaSource(player);
1274 type: 'sourceopen'
1275 });
1276 1196
1277 player.hls.playlists.trigger('mediachange'); 1197 player.hls.playlists.trigger('mediachange');
1278 strictEqual(mediaChanges, 1, 'fired mediachange'); 1198 strictEqual(mediaChanges, 1, 'fired mediachange');
...@@ -1302,4 +1222,48 @@ test('can be disposed before finishing initialization', function() { ...@@ -1302,4 +1222,48 @@ test('can be disposed before finishing initialization', function() {
1302 } 1222 }
1303 }); 1223 });
1304 1224
1225 test('calls ended() on the media source at the end of a playlist', function() {
1226 var endOfStreams = 0;
1227 player.src({
1228 src: 'http://example.com/media.m3u8',
1229 type: 'application/vnd.apple.mpegurl'
1230 });
1231 openMediaSource(player);
1232 player.hls.mediaSource.endOfStream = function() {
1233 endOfStreams++;
1234 };
1235 // playlist response
1236 requests.shift().respond(200, null,
1237 '#EXTM3U\n' +
1238 '#EXTINF:10,\n' +
1239 '0.ts\n' +
1240 '#EXT-X-ENDLIST\n');
1241 // segment response
1242 requests[0].response = new ArrayBuffer(17);
1243 requests.shift().respond(200, null, '');
1244
1245 strictEqual(endOfStreams, 1, 'ended media source');
1246 });
1247
1248 test('calling play() at the end of a video resets the media index', function() {
1249 player.src({
1250 src: 'http://example.com/media.m3u8',
1251 type: 'application/vnd.apple.mpegurl'
1252 });
1253 openMediaSource(player);
1254 requests.shift().respond(200, null,
1255 '#EXTM3U\n' +
1256 '#EXTINF:10,\n' +
1257 '0.ts\n' +
1258 '#EXT-X-ENDLIST\n');
1259 standardXHRResponse(requests.shift());
1260
1261 strictEqual(player.hls.mediaIndex, 1, 'index is 1 after the first segment');
1262 player.hls.ended = function() {
1263 return true;
1264 };
1265 player.play();
1266 strictEqual(player.hls.mediaIndex, 0, 'index is 1 after the first segment');
1267 });
1268
1305 })(window, window.videojs); 1269 })(window, window.videojs);
......