853ec020 by David LaPalomento

Fix up tests

Remove lots of transmuxing-related test harness code and get more of the tests working. Discontinuities, seeking, and PTS-based duration are still unresolved and so those tests are still failing.
1 parent 4c5ee7a6
...@@ -73,23 +73,21 @@ videojs.Hls = videojs.extends(Component, { ...@@ -73,23 +73,21 @@ videojs.Hls = videojs.extends(Component, {
73 } 73 }
74 }); 74 });
75 75
76 // add HLS as a source handler 76 /**
77 if (videojs.MediaSource.supportsNativeMediaSources()) { 77 * The Source Handler object, which informs video.js what additional
78 videojs.getComponent('Html5').registerSourceHandler({ 78 * MIME types are supported and sets up playback. It is registered
79 * automatically to the appropriate tech based on the capabilities of
80 * the browser it is running in. It is not necessary to use or modify
81 * this object in normal usage.
82 */
83 videojs.HlsSourceHandler = {
79 canHandleSource: function(srcObj) { 84 canHandleSource: function(srcObj) {
80 var mpegurlRE = /^application\/(?:x-|vnd\.apple\.)mpegurl/i; 85 var mpegurlRE = /^application\/(?:x-|vnd\.apple\.)mpegurl/i;
81 return mpegurlRE.test(srcObj.type); 86
82 }, 87 // favor native HLS support if it's available
83 handleSource: function(source, tech) { 88 if (videojs.Hls.supportsNativeHls) {
84 tech.hls = new videojs.Hls(tech, source); 89 return false;
85 tech.hls.src(source.src);
86 return tech.hls;
87 } 90 }
88 });
89 } else {
90 videojs.getComponent('Flash').registerSourceHandler({
91 canHandleSource: function(srcObj) {
92 var mpegurlRE = /^application\/(?:x-|vnd\.apple\.)mpegurl/i;
93 return mpegurlRE.test(srcObj.type); 91 return mpegurlRE.test(srcObj.type);
94 }, 92 },
95 handleSource: function(source, tech) { 93 handleSource: function(source, tech) {
...@@ -97,10 +95,14 @@ if (videojs.MediaSource.supportsNativeMediaSources()) { ...@@ -97,10 +95,14 @@ if (videojs.MediaSource.supportsNativeMediaSources()) {
97 tech.hls.src(source.src); 95 tech.hls.src(source.src);
98 return tech.hls; 96 return tech.hls;
99 } 97 }
100 }); 98 };
99 // register with the appropriate tech
100 if (videojs.MediaSource.supportsNativeMediaSources()) {
101 videojs.getComponent('Html5').registerSourceHandler(videojs.HlsSourceHandler);
102 } else {
103 videojs.getComponent('Flash').registerSourceHandler(videojs.HlsSourceHandler);
101 } 104 }
102 105
103
104 // the desired length of video to maintain in the buffer, in seconds 106 // the desired length of video to maintain in the buffer, in seconds
105 videojs.Hls.GOAL_BUFFER_LENGTH = 30; 107 videojs.Hls.GOAL_BUFFER_LENGTH = 30;
106 108
...@@ -368,9 +370,18 @@ videojs.Hls.prototype.setupFirstPlay = function() { ...@@ -368,9 +370,18 @@ videojs.Hls.prototype.setupFirstPlay = function() {
368 media = this.playlists.media(); 370 media = this.playlists.media();
369 371
370 // check that everything is ready to begin buffering 372 // check that everything is ready to begin buffering
373
374 // 1) the video is a live stream of unknown duration
371 if (this.duration() === Infinity && 375 if (this.duration() === Infinity &&
376
377 // 2) the player has not played before and is not paused
372 this.tech_.played().length === 0 && 378 this.tech_.played().length === 0 &&
379 !this.tech_.paused() &&
380
381 // 3) the Media Source and Source Buffers are ready
373 this.sourceBuffer && 382 this.sourceBuffer &&
383
384 // 4) the active media playlist is available
374 media) { 385 media) {
375 386
376 // seek to the latest media position for live videos 387 // seek to the latest media position for live videos
...@@ -870,13 +881,6 @@ videojs.Hls.prototype.drainBuffer = function(event) { ...@@ -870,13 +881,6 @@ videojs.Hls.prototype.drainBuffer = function(event) {
870 return; 881 return;
871 } 882 }
872 883
873 // transition the sourcebuffer to the ended state if we've hit the end of
874 // the playlist
875 if (this.duration() !== Infinity &&
876 this.mediaIndex === this.playlists.media().segments.length) {
877 this.mediaSource.endOfStream();
878 }
879
880 segmentInfo = segmentBuffer[0]; 884 segmentInfo = segmentBuffer[0];
881 885
882 mediaIndex = segmentInfo.mediaIndex; 886 mediaIndex = segmentInfo.mediaIndex;
...@@ -976,6 +980,13 @@ videojs.Hls.prototype.drainBuffer = function(event) { ...@@ -976,6 +980,13 @@ videojs.Hls.prototype.drainBuffer = function(event) {
976 } 980 }
977 this.sourceBuffer.appendBuffer(bytes); 981 this.sourceBuffer.appendBuffer(bytes);
978 982
983 // transition the sourcebuffer to the ended state if we've hit the end of
984 // the playlist
985 if (this.duration() !== Infinity &&
986 mediaIndex + 1 === this.playlists.media().segments.length) {
987 this.mediaSource.endOfStream();
988 }
989
979 // we're done processing this segment 990 // we're done processing this segment
980 segmentBuffer.shift(); 991 segmentBuffer.shift();
981 992
......
...@@ -42,8 +42,9 @@ var ...@@ -42,8 +42,9 @@ var
42 // patch over some methods of the provided tech so it can be tested 42 // patch over some methods of the provided tech so it can be tested
43 // synchronously with sinon's fake timers 43 // synchronously with sinon's fake timers
44 mockTech = function(tech) { 44 mockTech = function(tech) {
45 tech.currentTime_ = tech.currentTime;
45 tech.currentTime = function() { 46 tech.currentTime = function() {
46 return tech.currentTime_ === undefined ? tech.currentTime : tech.currentTime_; 47 return tech.time_ === undefined ? tech.currentTime_() : tech.time_;
47 }; 48 };
48 49
49 tech.setSrc = function(src) { 50 tech.setSrc = function(src) {
...@@ -61,7 +62,7 @@ var ...@@ -61,7 +62,7 @@ var
61 }; 62 };
62 63
63 tech.setCurrentTime = function(time) { 64 tech.setCurrentTime = function(time) {
64 tech.currentTime_ = time; 65 tech.time_ = time;
65 66
66 setTimeout(function() { 67 setTimeout(function() {
67 tech.trigger('seeking'); 68 tech.trigger('seeking');
...@@ -1784,6 +1785,8 @@ test('segment 500 should trigger MEDIA_ERR_ABORTED', function () { ...@@ -1784,6 +1785,8 @@ test('segment 500 should trigger MEDIA_ERR_ABORTED', function () {
1784 }); 1785 });
1785 1786
1786 test('seeking in an empty playlist is a non-erroring noop', function() { 1787 test('seeking in an empty playlist is a non-erroring noop', function() {
1788 var requestsLength;
1789
1787 player.src({ 1790 player.src({
1788 src: 'manifest/empty-live.m3u8', 1791 src: 'manifest/empty-live.m3u8',
1789 type: 'application/vnd.apple.mpegurl' 1792 type: 'application/vnd.apple.mpegurl'
...@@ -1792,8 +1795,11 @@ test('seeking in an empty playlist is a non-erroring noop', function() { ...@@ -1792,8 +1795,11 @@ test('seeking in an empty playlist is a non-erroring noop', function() {
1792 1795
1793 requests.shift().respond(200, null, '#EXTM3U\n'); 1796 requests.shift().respond(200, null, '#EXTM3U\n');
1794 1797
1795 player.currentTime(183); 1798 requestsLength = requests.length;
1796 equal(player.currentTime(), 0, 'remains at time zero'); 1799 player.tech.setCurrentTime(183);
1800 clock.tick(1);
1801
1802 equal(requests.length, requestsLength, 'made no additional requests');
1797 }); 1803 });
1798 1804
1799 test('duration is Infinity for live playlists', function() { 1805 test('duration is Infinity for live playlists', function() {
...@@ -1867,10 +1873,12 @@ test('live playlist starts three target durations before live', function() { ...@@ -1867,10 +1873,12 @@ test('live playlist starts three target durations before live', function() {
1867 1873
1868 equal(requests.length, 0, 'no outstanding segment request'); 1874 equal(requests.length, 0, 'no outstanding segment request');
1869 1875
1876 player.tech.paused = function() { return false; };
1870 player.tech.trigger('play'); 1877 player.tech.trigger('play');
1878 clock.tick(1);
1871 mediaPlaylist = player.tech.hls.playlists.media(); 1879 mediaPlaylist = player.tech.hls.playlists.media();
1872 equal(player.tech.hls.mediaIndex, 1, 'mediaIndex is updated at play'); 1880 equal(player.tech.hls.mediaIndex, 1, 'mediaIndex is updated at play');
1873 equal(player.currentTime(), player.seekable().end(0), 'seeked to the seekable end'); 1881 equal(player.currentTime(), player.tech.hls.seekable().end(0), 'seeked to the seekable end');
1874 1882
1875 equal(requests.length, 1, 'begins buffering'); 1883 equal(requests.length, 1, 'begins buffering');
1876 }); 1884 });
...@@ -1904,7 +1912,9 @@ test('live playlist starts with correct currentTime value', function() { ...@@ -1904,7 +1912,9 @@ test('live playlist starts with correct currentTime value', function() {
1904 1912
1905 player.tech.hls.playlists.trigger('loadedmetadata'); 1913 player.tech.hls.playlists.trigger('loadedmetadata');
1906 1914
1915 player.tech.paused = function() { return false; };
1907 player.tech.trigger('play'); 1916 player.tech.trigger('play');
1917 clock.tick(1);
1908 1918
1909 strictEqual(player.currentTime(), 1919 strictEqual(player.currentTime(),
1910 videojs.Hls.Playlist.seekable(player.tech.hls.playlists.media()).end(0), 1920 videojs.Hls.Playlist.seekable(player.tech.hls.playlists.media()).end(0),
...@@ -2181,6 +2191,7 @@ test('clears the segment buffer on seek', function() { ...@@ -2181,6 +2191,7 @@ test('clears the segment buffer on seek', function() {
2181 // seek back to the beginning 2191 // seek back to the beginning
2182 player.currentTime(0); 2192 player.currentTime(0);
2183 tags.push({ pts: 0, bytes: new Uint8Array(1) }); 2193 tags.push({ pts: 0, bytes: new Uint8Array(1) });
2194 clock.tick(1);
2184 standardXHRResponse(requests.pop()); 2195 standardXHRResponse(requests.pop());
2185 strictEqual(aborts, 1, 'aborted once for the seek'); 2196 strictEqual(aborts, 1, 'aborted once for the seek');
2186 2197
...@@ -2383,31 +2394,22 @@ test('aborts the source buffer on disposal', function() { ...@@ -2383,31 +2394,22 @@ test('aborts the source buffer on disposal', function() {
2383 strictEqual(aborts, 1, 'aborted the source buffer'); 2394 strictEqual(aborts, 1, 'aborted the source buffer');
2384 }); 2395 });
2385 2396
2386 test('only supports HLS MIME types', function() { 2397 test('the source handler supports HLS mime types', function() {
2387 var Flash = videojs.getComponent('Flash'); 2398 ok(videojs.HlsSourceHandler.canHandleSource({
2388
2389 ok(Flash.canPlaySource({
2390 type: 'aPplicatiOn/x-MPegUrl' 2399 type: 'aPplicatiOn/x-MPegUrl'
2391 }), 'supports x-mpegurl'); 2400 }), 'supports x-mpegurl');
2392 ok(Flash.canPlaySource({ 2401 ok(videojs.HlsSourceHandler.canHandleSource({
2393 type: 'aPplicatiOn/VnD.aPPle.MpEgUrL' 2402 type: 'aPplicatiOn/VnD.aPPle.MpEgUrL'
2394 }), 'supports vnd.apple.mpegurl'); 2403 }), 'supports vnd.apple.mpegurl');
2395 2404
2396 ok(!(Flash.selectSourceHandler({ 2405 ok(!(videojs.HlsSourceHandler.canHandleSource({
2397 type: 'video/mp4' 2406 type: 'video/mp4'
2398 }) instanceof videojs.Hls), 'does not support mp4'); 2407 }) instanceof videojs.Hls), 'does not support mp4');
2399 ok(!(Flash.selectSourceHandler({ 2408 ok(!(videojs.HlsSourceHandler.canHandleSource({
2400 type: 'video/x-flv' 2409 type: 'video/x-flv'
2401 }) instanceof videojs.Hls), 'does not support flv'); 2410 }) instanceof videojs.Hls), 'does not support flv');
2402 }); 2411 });
2403 2412
2404 test('adds HLS to the Flash tech', function() {
2405 ok(videojs.getComponent('Flash').canPlaySource({
2406 src: 'example.m3u8',
2407 type: 'application/x-mpegURL'
2408 }), 'registered the HLS source handler');
2409 });
2410
2411 test('has no effect if native HLS is available', function() { 2413 test('has no effect if native HLS is available', function() {
2412 var player; 2414 var player;
2413 videojs.Hls.supportsNativeHls = true; 2415 videojs.Hls.supportsNativeHls = true;
...@@ -2698,6 +2700,7 @@ test('seeking should abort an outstanding key request and create a new one', fun ...@@ -2698,6 +2700,7 @@ test('seeking should abort an outstanding key request and create a new one', fun
2698 standardXHRResponse(requests.shift()); // segment 1 2700 standardXHRResponse(requests.shift()); // segment 1
2699 2701
2700 player.currentTime(11); 2702 player.currentTime(11);
2703 clock.tick(1);
2701 ok(requests[0].aborted, 'the key XHR should be aborted'); 2704 ok(requests[0].aborted, 'the key XHR should be aborted');
2702 requests.shift(); // aborted key 1 2705 requests.shift(); // aborted key 1
2703 2706
...@@ -2736,22 +2739,16 @@ test('retries key requests once upon failure', function() { ...@@ -2736,22 +2739,16 @@ test('retries key requests once upon failure', function() {
2736 }); 2739 });
2737 2740
2738 test('skip segments if key requests fail more than once', function() { 2741 test('skip segments if key requests fail more than once', function() {
2739 var bytes = [], 2742 var bytes = [];
2740 tags = [{ pts: 0, bytes: new Uint8Array(1) }];
2741
2742 videojs.Hls.SegmentParser = mockSegmentParser(tags);
2743 window.videojs.SourceBuffer = function() {
2744 this.appendBuffer = function(chunk) {
2745 bytes.push(chunk);
2746 };
2747 this.abort = function() {};
2748 };
2749 2743
2750 player.src({ 2744 player.src({
2751 src: 'https://example.com/encrypted-media.m3u8', 2745 src: 'https://example.com/encrypted-media.m3u8',
2752 type: 'application/vnd.apple.mpegurl' 2746 type: 'application/vnd.apple.mpegurl'
2753 }); 2747 });
2754 openMediaSource(player); 2748 openMediaSource(player);
2749 player.tech.hls.sourceBuffer.appendBuffer = function(chunk) {
2750 bytes.push(chunk);
2751 };
2755 player.tech.trigger('play'); 2752 player.tech.trigger('play');
2756 2753
2757 requests.shift().respond(200, null, 2754 requests.shift().respond(200, null,
...@@ -2766,11 +2763,9 @@ test('skip segments if key requests fail more than once', function() { ...@@ -2766,11 +2763,9 @@ test('skip segments if key requests fail more than once', function() {
2766 requests.shift().respond(404); // fail key 2763 requests.shift().respond(404); // fail key
2767 requests.shift().respond(404); // fail key, again 2764 requests.shift().respond(404); // fail key, again
2768 2765
2769 tags.length = 0;
2770 tags.push({pts: 0, bytes: new Uint8Array([1]) });
2771 player.tech.hls.checkBuffer_(); 2766 player.tech.hls.checkBuffer_();
2772 standardXHRResponse(requests.shift()); // segment 2 2767 standardXHRResponse(requests.shift()); // segment 2
2773 equal(bytes.length, 1, 'bytes from the ts segments should not be added'); 2768 equal(bytes.length, 0, 'did not append encrypted bytes');
2774 2769
2775 // key for second segment 2770 // key for second segment
2776 requests[0].response = new Uint32Array([0,0,0,0]).buffer; 2771 requests[0].response = new Uint32Array([0,0,0,0]).buffer;
...@@ -2779,8 +2774,8 @@ test('skip segments if key requests fail more than once', function() { ...@@ -2779,8 +2774,8 @@ test('skip segments if key requests fail more than once', function() {
2779 player.tech.hls.segmentBuffer_[0].bytes = new Uint8Array(16); 2774 player.tech.hls.segmentBuffer_[0].bytes = new Uint8Array(16);
2780 player.tech.hls.checkBuffer_(); 2775 player.tech.hls.checkBuffer_();
2781 2776
2782 equal(bytes.length, 2, 'bytes from the second ts segment should be added'); 2777 equal(bytes.length, 1, 'appended cleartext bytes from the second segment');
2783 deepEqual(bytes[1], new Uint8Array([1]), 'the bytes from the second segment are added and not the first'); 2778 deepEqual(bytes[0], new Uint8Array(16), 'appended bytes from the second segment, not the first');
2784 }); 2779 });
2785 2780
2786 test('the key is supplied to the decrypter in the correct format', function() { 2781 test('the key is supplied to the decrypter in the correct format', function() {
...@@ -2915,19 +2910,15 @@ test('resolves relative key URLs against the playlist', function() { ...@@ -2915,19 +2910,15 @@ test('resolves relative key URLs against the playlist', function() {
2915 }); 2910 });
2916 2911
2917 test('treats invalid keys as a key request failure', function() { 2912 test('treats invalid keys as a key request failure', function() {
2918 var tags = [{ pts: 0, bytes: new Uint8Array(1) }], bytes = []; 2913 var bytes = [];
2919 videojs.Hls.SegmentParser = mockSegmentParser(tags);
2920 window.videojs.SourceBuffer = function() {
2921 this.appendBuffer = function(chunk) {
2922 bytes.push(chunk);
2923 };
2924 this.abort = function() {};
2925 };
2926 player.src({ 2914 player.src({
2927 src: 'https://example.com/media.m3u8', 2915 src: 'https://example.com/media.m3u8',
2928 type: 'application/vnd.apple.mpegurl' 2916 type: 'application/vnd.apple.mpegurl'
2929 }); 2917 });
2930 openMediaSource(player); 2918 openMediaSource(player);
2919 player.tech.hls.sourceBuffer.appendBuffer = function(chunk) {
2920 bytes.push(chunk);
2921 };
2931 player.tech.trigger('play'); 2922 player.tech.trigger('play');
2932 requests.shift().respond(200, null, 2923 requests.shift().respond(200, null,
2933 '#EXTM3U\n' + 2924 '#EXTM3U\n' +
...@@ -2952,17 +2943,14 @@ test('treats invalid keys as a key request failure', function() { ...@@ -2952,17 +2943,14 @@ test('treats invalid keys as a key request failure', function() {
2952 2943
2953 // the first segment should be dropped and playback moves on 2944 // the first segment should be dropped and playback moves on
2954 player.tech.hls.checkBuffer_(); 2945 player.tech.hls.checkBuffer_();
2955 equal(bytes.length, 1, 'did not append bytes'); 2946 equal(bytes.length, 0, 'did not append bytes');
2956 equal(bytes[0], 'flv', 'appended the flv header');
2957 2947
2958 tags.length = 0;
2959 tags.push({ pts: 2833, bytes: new Uint8Array([1]) },
2960 { pts: 4833, bytes: new Uint8Array([2]) });
2961 // second segment request 2948 // second segment request
2962 standardXHRResponse(requests.shift()); 2949 requests[0].response = new Uint8Array([1, 2])
2950 requests.shift().respond(200, null, '');
2963 2951
2964 equal(bytes.length, 2, 'appended bytes'); 2952 equal(bytes.length, 1, 'appended bytes');
2965 deepEqual(bytes[1], new Uint8Array([1, 2]), 'skipped to the second segment'); 2953 deepEqual(bytes[0], new Uint8Array([1, 2]), 'skipped to the second segment');
2966 }); 2954 });
2967 2955
2968 test('live stream should not call endOfStream', function(){ 2956 test('live stream should not call endOfStream', function(){
......