2e41348d by David LaPalomento

Make sure endOfStream is called

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