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.
Showing
2 changed files
with
80 additions
and
81 deletions
... | @@ -73,34 +73,36 @@ videojs.Hls = videojs.extends(Component, { | ... | @@ -73,34 +73,36 @@ 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 | canHandleSource: function(srcObj) { | 79 | * automatically to the appropriate tech based on the capabilities of |
80 | var mpegurlRE = /^application\/(?:x-|vnd\.apple\.)mpegurl/i; | 80 | * the browser it is running in. It is not necessary to use or modify |
81 | return mpegurlRE.test(srcObj.type); | 81 | * this object in normal usage. |
82 | }, | 82 | */ |
83 | handleSource: function(source, tech) { | 83 | videojs.HlsSourceHandler = { |
84 | tech.hls = new videojs.Hls(tech, source); | 84 | canHandleSource: function(srcObj) { |
85 | tech.hls.src(source.src); | 85 | var mpegurlRE = /^application\/(?:x-|vnd\.apple\.)mpegurl/i; |
86 | return tech.hls; | 86 | |
87 | // favor native HLS support if it's available | ||
88 | if (videojs.Hls.supportsNativeHls) { | ||
89 | return false; | ||
87 | } | 90 | } |
88 | }); | 91 | return mpegurlRE.test(srcObj.type); |
92 | }, | ||
93 | handleSource: function(source, tech) { | ||
94 | tech.hls = new videojs.Hls(tech, source); | ||
95 | tech.hls.src(source.src); | ||
96 | return tech.hls; | ||
97 | } | ||
98 | }; | ||
99 | // register with the appropriate tech | ||
100 | if (videojs.MediaSource.supportsNativeMediaSources()) { | ||
101 | videojs.getComponent('Html5').registerSourceHandler(videojs.HlsSourceHandler); | ||
89 | } else { | 102 | } else { |
90 | videojs.getComponent('Flash').registerSourceHandler({ | 103 | videojs.getComponent('Flash').registerSourceHandler(videojs.HlsSourceHandler); |
91 | canHandleSource: function(srcObj) { | ||
92 | var mpegurlRE = /^application\/(?:x-|vnd\.apple\.)mpegurl/i; | ||
93 | return mpegurlRE.test(srcObj.type); | ||
94 | }, | ||
95 | handleSource: function(source, tech) { | ||
96 | tech.hls = new videojs.Hls(tech, source); | ||
97 | tech.hls.src(source.src); | ||
98 | return tech.hls; | ||
99 | } | ||
100 | }); | ||
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(){ | ... | ... |
-
Please register or sign in to post a comment