Merge pull request #267 from videojs/disc-action-insertion
Discontinuity insertion race
Showing
5 changed files
with
160 additions
and
60 deletions
... | @@ -44,7 +44,7 @@ | ... | @@ -44,7 +44,7 @@ |
44 | }, | 44 | }, |
45 | "dependencies": { | 45 | "dependencies": { |
46 | "pkcs7": "^0.2.2", | 46 | "pkcs7": "^0.2.2", |
47 | "videojs-contrib-media-sources": "^0.3.0", | 47 | "videojs-contrib-media-sources": "^1.0.0", |
48 | "videojs-swf": "^4.6.0" | 48 | "videojs-swf": "^4.6.0" |
49 | } | 49 | } |
50 | } | 50 | } | ... | ... |
1 | /** | ||
2 | * An object that stores the bytes of an FLV tag and methods for | ||
3 | * querying and manipulating that data. | ||
4 | * @see http://download.macromedia.com/f4v/video_file_format_spec_v10_1.pdf | ||
5 | */ | ||
1 | (function(window) { | 6 | (function(window) { |
2 | 7 | ||
3 | window.videojs = window.videojs || {}; | 8 | window.videojs = window.videojs || {}; |
... | @@ -358,4 +363,29 @@ hls.FlvTag.frameTime = function(tag) { | ... | @@ -358,4 +363,29 @@ hls.FlvTag.frameTime = function(tag) { |
358 | return pts; | 363 | return pts; |
359 | }; | 364 | }; |
360 | 365 | ||
366 | /** | ||
367 | * Calculate the media timeline duration represented by an array of | ||
368 | * tags. This function assumes the tags are already pre-sorted by | ||
369 | * presentation timestamp (PTS), in ascending order. Returns zero if | ||
370 | * there are less than two FLV tags to inspect. | ||
371 | * @param tags {array} the FlvTag objects to query | ||
372 | * @return the number of milliseconds between the display time of the | ||
373 | * first tag and the last tag. | ||
374 | */ | ||
375 | hls.FlvTag.durationFromTags = function(tags) { | ||
376 | if (tags.length < 2) { | ||
377 | return 0; | ||
378 | } | ||
379 | |||
380 | var | ||
381 | first = tags[0], | ||
382 | last = tags[tags.length - 1], | ||
383 | frameDuration; | ||
384 | |||
385 | // use the interval between the last two tags or assume 24 fps | ||
386 | frameDuration = last.pts - tags[tags.length - 2].pts || (1/24); | ||
387 | |||
388 | return (last.pts - first.pts) + frameDuration; | ||
389 | }; | ||
390 | |||
361 | })(this); | 391 | })(this); | ... | ... |
... | @@ -714,17 +714,24 @@ videojs.Hls.prototype.drainBuffer = function(event) { | ... | @@ -714,17 +714,24 @@ videojs.Hls.prototype.drainBuffer = function(event) { |
714 | tags, | 714 | tags, |
715 | bytes, | 715 | bytes, |
716 | segment, | 716 | segment, |
717 | durationOffset, | ||
718 | decrypter, | 717 | decrypter, |
719 | segIv, | 718 | segIv, |
720 | ptsTime, | 719 | ptsTime, |
721 | segmentOffset = 0, | 720 | segmentOffset = 0, |
722 | segmentBuffer = this.segmentBuffer_; | 721 | segmentBuffer = this.segmentBuffer_; |
723 | 722 | ||
723 | // if the buffer is empty or the source buffer hasn't been created | ||
724 | // yet, do nothing | ||
724 | if (!segmentBuffer.length || !this.sourceBuffer) { | 725 | if (!segmentBuffer.length || !this.sourceBuffer) { |
725 | return; | 726 | return; |
726 | } | 727 | } |
727 | 728 | ||
729 | // we can't append more data if the source buffer is busy processing | ||
730 | // what we've already sent | ||
731 | if (this.sourceBuffer.updating) { | ||
732 | return; | ||
733 | } | ||
734 | |||
728 | segmentInfo = segmentBuffer[0]; | 735 | segmentInfo = segmentBuffer[0]; |
729 | 736 | ||
730 | mediaIndex = segmentInfo.mediaIndex; | 737 | mediaIndex = segmentInfo.mediaIndex; |
... | @@ -780,23 +787,11 @@ videojs.Hls.prototype.drainBuffer = function(event) { | ... | @@ -780,23 +787,11 @@ videojs.Hls.prototype.drainBuffer = function(event) { |
780 | tags.push(this.segmentParser_.getNextTag()); | 787 | tags.push(this.segmentParser_.getNextTag()); |
781 | } | 788 | } |
782 | 789 | ||
783 | // This block of code uses the presentation timestamp of the ts segment to calculate its exact duration, since this | 790 | // Use the presentation timestamp of the ts segment to calculate its |
784 | // may differ by fractions of a second from what is reported. Using the exact, calculated 'preciseDuration' allows | 791 | // exact duration, since this may differ by fractions of a second |
785 | // for smoother seeking and calculation of the total playlist duration, which previously (especially in short videos) | 792 | // from what is reported in the playlist |
786 | // was reported erroneously and made the play head overrun the end of the progress bar. | ||
787 | if (tags.length > 0) { | 793 | if (tags.length > 0) { |
788 | segment.preciseTimestamp = tags[tags.length - 1].pts; | 794 | segment.preciseDuration = videojs.Hls.FlvTag.durationFromTags(tags) * 0.001; |
789 | |||
790 | if (playlist.segments[mediaIndex - 1]) { | ||
791 | if (playlist.segments[mediaIndex - 1].preciseTimestamp) { | ||
792 | durationOffset = playlist.segments[mediaIndex - 1].preciseTimestamp; | ||
793 | } else { | ||
794 | durationOffset = (playlist.targetDuration * (mediaIndex - 1) + playlist.segments[mediaIndex - 1].duration) * 1000; | ||
795 | } | ||
796 | segment.preciseDuration = (segment.preciseTimestamp - durationOffset) / 1000; | ||
797 | } else if (mediaIndex === 0) { | ||
798 | segment.preciseDuration = segment.preciseTimestamp / 1000; | ||
799 | } | ||
800 | } | 795 | } |
801 | 796 | ||
802 | this.updateDuration(this.playlists.media()); | 797 | this.updateDuration(this.playlists.media()); |
... | @@ -825,13 +820,18 @@ videojs.Hls.prototype.drainBuffer = function(event) { | ... | @@ -825,13 +820,18 @@ videojs.Hls.prototype.drainBuffer = function(event) { |
825 | this.el().vjs_discontinuity(); | 820 | this.el().vjs_discontinuity(); |
826 | } | 821 | } |
827 | 822 | ||
828 | for (i = 0; i < tags.length; i++) { | 823 | (function() { |
829 | // queue up the bytes to be appended to the SourceBuffer | 824 | var segmentByteLength = 0, j, segment; |
830 | // the queue gives control back to the browser between tags | 825 | for (i = 0; i < tags.length; i++) { |
831 | // so that large segments don't cause a "hiccup" in playback | 826 | segmentByteLength += tags[i].bytes.byteLength; |
832 | 827 | } | |
833 | this.sourceBuffer.appendBuffer(tags[i].bytes, this.player()); | 828 | segment = new Uint8Array(segmentByteLength); |
834 | } | 829 | for (i = 0, j = 0; i < tags.length; i++) { |
830 | segment.set(tags[i].bytes, j); | ||
831 | j += tags[i].bytes.byteLength; | ||
832 | } | ||
833 | this.sourceBuffer.appendBuffer(segment); | ||
834 | }).call(this); | ||
835 | 835 | ||
836 | // we're done processing this segment | 836 | // we're done processing this segment |
837 | segmentBuffer.shift(); | 837 | segmentBuffer.shift(); | ... | ... |
... | @@ -57,4 +57,32 @@ test('writeBytes grows the internal byte array dynamically', function() { | ... | @@ -57,4 +57,32 @@ test('writeBytes grows the internal byte array dynamically', function() { |
57 | } | 57 | } |
58 | }); | 58 | }); |
59 | 59 | ||
60 | })(this); | 60 | test('calculates the duration of a tag array from PTS values', function() { |
61 | var tags = [], count = 20, i; | ||
62 | |||
63 | for (i = 0; i < count; i++) { | ||
64 | tags[i] = new FlvTag(FlvTag.VIDEO_TAG); | ||
65 | tags[i].pts = i * 1000; | ||
66 | } | ||
67 | |||
68 | equal(FlvTag.durationFromTags(tags), count * 1000, 'calculated duration from PTS values'); | ||
69 | }); | ||
70 | |||
71 | test('durationFromTags() assumes 24fps if the last frame duration cannot be calculated', function() { | ||
72 | var tags = [ | ||
73 | new FlvTag(FlvTag.VIDEO_TAG), | ||
74 | new FlvTag(FlvTag.VIDEO_TAG), | ||
75 | new FlvTag(FlvTag.VIDEO_TAG) | ||
76 | ]; | ||
77 | tags[0].pts = 0; | ||
78 | tags[1].pts = tags[2].pts = 1000; | ||
79 | |||
80 | equal(FlvTag.durationFromTags(tags), 1000 + (1/24) , 'assumes 24fps video'); | ||
81 | }); | ||
82 | |||
83 | test('durationFromTags() returns zero if there are less than two frames', function() { | ||
84 | equal(FlvTag.durationFromTags([]), 0, 'returns zero for empty input'); | ||
85 | equal(FlvTag.durationFromTags([new FlvTag(FlvTag.VIDEO_TAG)]), 0, 'returns zero for a singleton input'); | ||
86 | }); | ||
87 | |||
88 | })(this); | ... | ... |
... | @@ -953,6 +953,29 @@ test('only makes one segment request at a time', function() { | ... | @@ -953,6 +953,29 @@ test('only makes one segment request at a time', function() { |
953 | strictEqual(1, requests.length, 'only one XHR is made'); | 953 | strictEqual(1, requests.length, 'only one XHR is made'); |
954 | }); | 954 | }); |
955 | 955 | ||
956 | test('only appends one segment at a time', function() { | ||
957 | var appends = 0, tags = [{ pts: 0, bytes: new Uint8Array(1) }]; | ||
958 | videojs.Hls.SegmentParser = mockSegmentParser(tags); | ||
959 | player.src({ | ||
960 | src: 'manifest/media.m3u8', | ||
961 | type: 'application/vnd.apple.mpegurl' | ||
962 | }); | ||
963 | openMediaSource(player); | ||
964 | standardXHRResponse(requests.pop()); // media.m3u8 | ||
965 | standardXHRResponse(requests.pop()); // segment 0 | ||
966 | |||
967 | player.hls.sourceBuffer.updating = true; | ||
968 | player.hls.sourceBuffer.appendBuffer = function() { | ||
969 | appends++; | ||
970 | }; | ||
971 | tags.push({ pts: 0, bytes: new Uint8Array(1) }); | ||
972 | |||
973 | player.hls.checkBuffer_(); | ||
974 | standardXHRResponse(requests.pop()); // segment 1 | ||
975 | player.hls.checkBuffer_(); // should be a no-op | ||
976 | equal(appends, 0, 'did not append while updating'); | ||
977 | }); | ||
978 | |||
956 | test('cancels outstanding XHRs when seeking', function() { | 979 | test('cancels outstanding XHRs when seeking', function() { |
957 | player.src({ | 980 | player.src({ |
958 | src: 'manifest/media.m3u8', | 981 | src: 'manifest/media.m3u8', |
... | @@ -1063,22 +1086,12 @@ test('flushes the parser after each segment', function() { | ... | @@ -1063,22 +1086,12 @@ test('flushes the parser after each segment', function() { |
1063 | strictEqual(flushes, 1, 'tags are flushed at the end of a segment'); | 1086 | strictEqual(flushes, 1, 'tags are flushed at the end of a segment'); |
1064 | }); | 1087 | }); |
1065 | 1088 | ||
1066 | test('calculates preciseTimestamp and preciseDuration for a new segment', function() { | 1089 | test('calculates preciseDuration for a new segment', function() { |
1067 | // mock out the segment parser | 1090 | var tags = [ |
1068 | videojs.Hls.SegmentParser = function() { | 1091 | { pts : 200 * 1000, bytes: new Uint8Array(1) }, |
1069 | var tagsAvailable = true, | 1092 | { pts : 300 * 1000, bytes: new Uint8Array(1) } |
1070 | tag = { pts : 200000 }; | 1093 | ]; |
1071 | this.getFlvHeader = function() { | 1094 | videojs.Hls.SegmentParser = mockSegmentParser(tags); |
1072 | return []; | ||
1073 | }; | ||
1074 | this.parseSegmentBinaryData = function() {}; | ||
1075 | this.flushTags = function() {}; | ||
1076 | this.tagsAvailable = function() { return tagsAvailable; }; | ||
1077 | this.getNextTag = function() { tagsAvailable = false; return tag; }; | ||
1078 | this.metadataStream = { | ||
1079 | on: Function.prototype | ||
1080 | }; | ||
1081 | }; | ||
1082 | 1095 | ||
1083 | player.src({ | 1096 | player.src({ |
1084 | src: 'manifest/media.m3u8', | 1097 | src: 'manifest/media.m3u8', |
... | @@ -1089,11 +1102,40 @@ test('calculates preciseTimestamp and preciseDuration for a new segment', functi | ... | @@ -1089,11 +1102,40 @@ test('calculates preciseTimestamp and preciseDuration for a new segment', functi |
1089 | standardXHRResponse(requests[0]); | 1102 | standardXHRResponse(requests[0]); |
1090 | strictEqual(player.duration(), 40, 'player duration is read from playlist on load'); | 1103 | strictEqual(player.duration(), 40, 'player duration is read from playlist on load'); |
1091 | standardXHRResponse(requests[1]); | 1104 | standardXHRResponse(requests[1]); |
1092 | strictEqual(player.hls.playlists.media().segments[0].preciseTimestamp, 200000, 'preciseTimestamp is calculated and stored'); | ||
1093 | strictEqual(player.hls.playlists.media().segments[0].preciseDuration, 200, 'preciseDuration is calculated and stored'); | 1105 | strictEqual(player.hls.playlists.media().segments[0].preciseDuration, 200, 'preciseDuration is calculated and stored'); |
1094 | strictEqual(player.duration(), 230, 'player duration is calculated using preciseDuration'); | 1106 | strictEqual(player.duration(), 230, 'player duration is calculated using preciseDuration'); |
1095 | }); | 1107 | }); |
1096 | 1108 | ||
1109 | test('calculates preciseDuration correctly around discontinuities', function() { | ||
1110 | var tags = []; | ||
1111 | videojs.Hls.SegmentParser = mockSegmentParser(tags); | ||
1112 | player.src({ | ||
1113 | src: 'manifest/media.m3u8', | ||
1114 | type: 'application/vnd.apple.mpegurl' | ||
1115 | }); | ||
1116 | openMediaSource(player); | ||
1117 | requests.shift().respond(200, null, | ||
1118 | '#EXTM3U\n' + | ||
1119 | '#EXTINF:10,\n' + | ||
1120 | '0.ts\n' + | ||
1121 | '#EXT-X-DISCONTINUITY\n' + | ||
1122 | '#EXTINF:10,\n' + | ||
1123 | '1.ts\n' + | ||
1124 | '#EXT-X-ENDLIST\n'); | ||
1125 | tags.push({ pts: 10 * 1000, bytes: new Uint8Array(1) }); | ||
1126 | standardXHRResponse(requests.shift()); // segment 0 | ||
1127 | player.hls.checkBuffer_(); | ||
1128 | |||
1129 | // the PTS value of the second segment is *earlier* than the first | ||
1130 | tags.push({ pts: 0 * 1000, bytes: new Uint8Array(1) }); | ||
1131 | tags.push({ pts: 5 * 1000, bytes: new Uint8Array(1) }); | ||
1132 | standardXHRResponse(requests.shift()); // segment 1 | ||
1133 | |||
1134 | equal(player.hls.playlists.media().segments[1].preciseDuration, | ||
1135 | 5 + 5, // duration includes the time to display the second tag | ||
1136 | 'duration is independent of previous segments'); | ||
1137 | }); | ||
1138 | |||
1097 | test('exposes in-band metadata events as cues', function() { | 1139 | test('exposes in-band metadata events as cues', function() { |
1098 | var track; | 1140 | var track; |
1099 | player.src({ | 1141 | player.src({ |
... | @@ -1168,7 +1210,7 @@ test('drops tags before the target timestamp when seeking', function() { | ... | @@ -1168,7 +1210,7 @@ test('drops tags before the target timestamp when seeking', function() { |
1168 | }; | 1210 | }; |
1169 | 1211 | ||
1170 | // push a tag into the buffer | 1212 | // push a tag into the buffer |
1171 | tags.push({ pts: 0, bytes: 0 }); | 1213 | tags.push({ pts: 0, bytes: new Uint8Array(1) }); |
1172 | 1214 | ||
1173 | player.src({ | 1215 | player.src({ |
1174 | src: 'manifest/media.m3u8', | 1216 | src: 'manifest/media.m3u8', |
... | @@ -1183,20 +1225,20 @@ test('drops tags before the target timestamp when seeking', function() { | ... | @@ -1183,20 +1225,20 @@ test('drops tags before the target timestamp when seeking', function() { |
1183 | while (i--) { | 1225 | while (i--) { |
1184 | tags.unshift({ | 1226 | tags.unshift({ |
1185 | pts: i * 1000, | 1227 | pts: i * 1000, |
1186 | bytes: i | 1228 | bytes: new Uint8Array([i]) |
1187 | }); | 1229 | }); |
1188 | } | 1230 | } |
1189 | player.currentTime(7); | 1231 | player.currentTime(7); |
1190 | standardXHRResponse(requests[2]); | 1232 | standardXHRResponse(requests[2]); |
1191 | 1233 | ||
1192 | deepEqual(bytes, [7,8,9], 'three tags are appended'); | 1234 | deepEqual(bytes, [new Uint8Array([7,8,9])], 'three tags are appended'); |
1193 | }); | 1235 | }); |
1194 | 1236 | ||
1195 | test('calls abort() on the SourceBuffer before seeking', function() { | 1237 | test('calls abort() on the SourceBuffer before seeking', function() { |
1196 | var | 1238 | var |
1197 | aborts = 0, | 1239 | aborts = 0, |
1198 | bytes = [], | 1240 | bytes = [], |
1199 | tags = [{ pts: 0, bytes: 0 }]; | 1241 | tags = [{ pts: 0, bytes: new Uint8Array(1) }]; |
1200 | 1242 | ||
1201 | 1243 | ||
1202 | // track calls to abort() | 1244 | // track calls to abort() |
... | @@ -1221,8 +1263,8 @@ test('calls abort() on the SourceBuffer before seeking', function() { | ... | @@ -1221,8 +1263,8 @@ test('calls abort() on the SourceBuffer before seeking', function() { |
1221 | 1263 | ||
1222 | // drainBuffer() uses the first PTS value to account for any timestamp discontinuities in the stream | 1264 | // drainBuffer() uses the first PTS value to account for any timestamp discontinuities in the stream |
1223 | // adding a tag with a PTS of zero looks like a stream with no discontinuities | 1265 | // adding a tag with a PTS of zero looks like a stream with no discontinuities |
1224 | tags.push({ pts: 0, bytes: 0 }); | 1266 | tags.push({ pts: 0, bytes: new Uint8Array(1) }); |
1225 | tags.push({ pts: 7000, bytes: 7 }); | 1267 | tags.push({ pts: 7000, bytes: new Uint8Array([7]) }); |
1226 | // seek to 7s | 1268 | // seek to 7s |
1227 | player.currentTime(7); | 1269 | player.currentTime(7); |
1228 | standardXHRResponse(requests[2]); | 1270 | standardXHRResponse(requests[2]); |
... | @@ -1474,7 +1516,7 @@ test('calls vjs_discontinuity() before appending bytes at a discontinuity', func | ... | @@ -1474,7 +1516,7 @@ test('calls vjs_discontinuity() before appending bytes at a discontinuity', func |
1474 | player.hls.checkBuffer_(); | 1516 | player.hls.checkBuffer_(); |
1475 | strictEqual(discontinuities, 0, 'no discontinuities before the segment is received'); | 1517 | strictEqual(discontinuities, 0, 'no discontinuities before the segment is received'); |
1476 | 1518 | ||
1477 | tags.push({}); | 1519 | tags.push({ pts: 0, bytes: new Uint8Array(1) }); |
1478 | standardXHRResponse(requests.pop()); | 1520 | standardXHRResponse(requests.pop()); |
1479 | strictEqual(discontinuities, 1, 'signals a discontinuity'); | 1521 | strictEqual(discontinuities, 1, 'signals a discontinuity'); |
1480 | }); | 1522 | }); |
... | @@ -1521,7 +1563,7 @@ test('clears the segment buffer on seek', function() { | ... | @@ -1521,7 +1563,7 @@ test('clears the segment buffer on seek', function() { |
1521 | 1563 | ||
1522 | // seek back to the beginning | 1564 | // seek back to the beginning |
1523 | player.currentTime(0); | 1565 | player.currentTime(0); |
1524 | tags.push({ pts: 0, bytes: 0 }); | 1566 | tags.push({ pts: 0, bytes: new Uint8Array(1) }); |
1525 | standardXHRResponse(requests.pop()); | 1567 | standardXHRResponse(requests.pop()); |
1526 | strictEqual(aborts, 1, 'aborted once for the seek'); | 1568 | strictEqual(aborts, 1, 'aborted once for the seek'); |
1527 | 1569 | ||
... | @@ -1571,7 +1613,7 @@ test('continues playing after seek to discontinuity', function() { | ... | @@ -1571,7 +1613,7 @@ test('continues playing after seek to discontinuity', function() { |
1571 | 1613 | ||
1572 | // seek to the discontinuity | 1614 | // seek to the discontinuity |
1573 | player.currentTime(10); | 1615 | player.currentTime(10); |
1574 | tags.push({ pts: 0, bytes: 0 }); | 1616 | tags.push({ pts: 0, bytes: new Uint8Array(1) }); |
1575 | standardXHRResponse(requests.pop()); | 1617 | standardXHRResponse(requests.pop()); |
1576 | strictEqual(aborts, 1, 'aborted once for the seek'); | 1618 | strictEqual(aborts, 1, 'aborted once for the seek'); |
1577 | 1619 | ||
... | @@ -1853,8 +1895,8 @@ test('drainBuffer will not proceed with empty source buffer', function() { | ... | @@ -1853,8 +1895,8 @@ test('drainBuffer will not proceed with empty source buffer', function() { |
1853 | }; | 1895 | }; |
1854 | 1896 | ||
1855 | player.hls.sourceBuffer = undefined; | 1897 | player.hls.sourceBuffer = undefined; |
1856 | compareBuffer = [{mediaIndex: 0, playlist: newMedia, offset: 0, bytes: [0,0,0]}]; | 1898 | compareBuffer = [{mediaIndex: 0, playlist: newMedia, offset: 0, bytes: new Uint8Array(3)}]; |
1857 | player.hls.segmentBuffer_ = [{mediaIndex: 0, playlist: newMedia, offset: 0, bytes: [0,0,0]}]; | 1899 | player.hls.segmentBuffer_ = [{mediaIndex: 0, playlist: newMedia, offset: 0, bytes: new Uint8Array(3)}]; |
1858 | 1900 | ||
1859 | player.hls.drainBuffer(); | 1901 | player.hls.drainBuffer(); |
1860 | 1902 | ||
... | @@ -2018,7 +2060,7 @@ test('retries key requests once upon failure', function() { | ... | @@ -2018,7 +2060,7 @@ test('retries key requests once upon failure', function() { |
2018 | 2060 | ||
2019 | test('skip segments if key requests fail more than once', function() { | 2061 | test('skip segments if key requests fail more than once', function() { |
2020 | var bytes = [], | 2062 | var bytes = [], |
2021 | tags = [{ pts: 0, bytes: 0 }]; | 2063 | tags = [{ pts: 0, bytes: new Uint8Array(1) }]; |
2022 | 2064 | ||
2023 | videojs.Hls.SegmentParser = mockSegmentParser(tags); | 2065 | videojs.Hls.SegmentParser = mockSegmentParser(tags); |
2024 | window.videojs.SourceBuffer = function() { | 2066 | window.videojs.SourceBuffer = function() { |
... | @@ -2047,7 +2089,7 @@ test('skip segments if key requests fail more than once', function() { | ... | @@ -2047,7 +2089,7 @@ test('skip segments if key requests fail more than once', function() { |
2047 | requests.shift().respond(404); // fail key, again | 2089 | requests.shift().respond(404); // fail key, again |
2048 | 2090 | ||
2049 | tags.length = 0; | 2091 | tags.length = 0; |
2050 | tags.push({pts: 0, bytes: 1}); | 2092 | tags.push({pts: 0, bytes: new Uint8Array([1]) }); |
2051 | player.hls.checkBuffer_(); | 2093 | player.hls.checkBuffer_(); |
2052 | standardXHRResponse(requests.shift()); // segment 2 | 2094 | standardXHRResponse(requests.shift()); // segment 2 |
2053 | equal(bytes.length, 1, 'bytes from the ts segments should not be added'); | 2095 | equal(bytes.length, 1, 'bytes from the ts segments should not be added'); |
... | @@ -2060,7 +2102,7 @@ test('skip segments if key requests fail more than once', function() { | ... | @@ -2060,7 +2102,7 @@ test('skip segments if key requests fail more than once', function() { |
2060 | player.hls.checkBuffer_(); | 2102 | player.hls.checkBuffer_(); |
2061 | 2103 | ||
2062 | equal(bytes.length, 2, 'bytes from the second ts segment should be added'); | 2104 | equal(bytes.length, 2, 'bytes from the second ts segment should be added'); |
2063 | equal(bytes[1], 1, 'the bytes from the second segment are added and not the first'); | 2105 | deepEqual(bytes[1], new Uint8Array([1]), 'the bytes from the second segment are added and not the first'); |
2064 | }); | 2106 | }); |
2065 | 2107 | ||
2066 | test('the key is supplied to the decrypter in the correct format', function() { | 2108 | test('the key is supplied to the decrypter in the correct format', function() { |
... | @@ -2191,7 +2233,7 @@ test('resolves relative key URLs against the playlist', function() { | ... | @@ -2191,7 +2233,7 @@ test('resolves relative key URLs against the playlist', function() { |
2191 | }); | 2233 | }); |
2192 | 2234 | ||
2193 | test('treats invalid keys as a key request failure', function() { | 2235 | test('treats invalid keys as a key request failure', function() { |
2194 | var tags = [{ pts: 0, bytes: 0 }], bytes = []; | 2236 | var tags = [{ pts: 0, bytes: new Uint8Array(1) }], bytes = []; |
2195 | videojs.Hls.SegmentParser = mockSegmentParser(tags); | 2237 | videojs.Hls.SegmentParser = mockSegmentParser(tags); |
2196 | window.videojs.SourceBuffer = function() { | 2238 | window.videojs.SourceBuffer = function() { |
2197 | this.appendBuffer = function(chunk) { | 2239 | this.appendBuffer = function(chunk) { |
... | @@ -2231,12 +2273,12 @@ test('treats invalid keys as a key request failure', function() { | ... | @@ -2231,12 +2273,12 @@ test('treats invalid keys as a key request failure', function() { |
2231 | equal(bytes[0], 'flv', 'appended the flv header'); | 2273 | equal(bytes[0], 'flv', 'appended the flv header'); |
2232 | 2274 | ||
2233 | tags.length = 0; | 2275 | tags.length = 0; |
2234 | tags.push({ pts: 1, bytes: 1 }); | 2276 | tags.push({ pts: 1, bytes: new Uint8Array([1]) }); |
2235 | // second segment request | 2277 | // second segment request |
2236 | standardXHRResponse(requests.shift()); | 2278 | standardXHRResponse(requests.shift()); |
2237 | 2279 | ||
2238 | equal(bytes.length, 2, 'appended bytes'); | 2280 | equal(bytes.length, 2, 'appended bytes'); |
2239 | equal(1, bytes[1], 'skipped to the second segment'); | 2281 | deepEqual(new Uint8Array([1]), bytes[1], 'skipped to the second segment'); |
2240 | }); | 2282 | }); |
2241 | 2283 | ||
2242 | test('live stream should not call endOfStream', function(){ | 2284 | test('live stream should not call endOfStream', function(){ | ... | ... |
-
Please register or sign in to post a comment