217e4482 by David LaPalomento

Chunk up decryption

Split up large segments that need AES-128 decryption and hand control back to the browser between segments. Clean up the decryption block loop a bit since it's no longer critical to finish before the next frame is rendered.
1 parent b56951a6
...@@ -38,7 +38,18 @@ ...@@ -38,7 +38,18 @@
38 (function(window, videojs, unpad) { 38 (function(window, videojs, unpad) {
39 'use strict'; 39 'use strict';
40 40
41 var AES, decrypt; 41 var AES, AsyncStream, Decrypter, decrypt, ntoh;
42
43 /**
44 * Convert network-order (big-endian) bytes into their little-endian
45 * representation.
46 */
47 ntoh = function(word) {
48 return (word << 24) |
49 ((word & 0xff00) << 8) |
50 ((word & 0xff0000) >> 8) |
51 (word >>> 24);
52 };
42 53
43 /** 54 /**
44 * Schedule out an AES key for both encryption and decryption. This 55 * Schedule out an AES key for both encryption and decryption. This
...@@ -221,7 +232,7 @@ AES.prototype = { ...@@ -221,7 +232,7 @@ AES.prototype = {
221 decrypt = function(encrypted, key, initVector) { 232 decrypt = function(encrypted, key, initVector) {
222 var 233 var
223 // word-level access to the encrypted bytes 234 // word-level access to the encrypted bytes
224 encrypted32 = new Int32Array(encrypted.buffer), 235 encrypted32 = new Int32Array(encrypted.buffer, encrypted.byteOffset, encrypted.byteLength >> 2),
225 236
226 decipher = new AES(Array.prototype.slice.call(key)), 237 decipher = new AES(Array.prototype.slice.call(key)),
227 238
...@@ -233,7 +244,6 @@ decrypt = function(encrypted, key, initVector) { ...@@ -233,7 +244,6 @@ decrypt = function(encrypted, key, initVector) {
233 // decrypted data 244 // decrypted data
234 init0, init1, init2, init3, 245 init0, init1, init2, init3,
235 encrypted0, encrypted1, encrypted2, encrypted3, 246 encrypted0, encrypted1, encrypted2, encrypted3,
236 decrypted0, decrypted1, decrypted2, decrypted3,
237 247
238 // iteration variable 248 // iteration variable
239 wordIx; 249 wordIx;
...@@ -250,49 +260,25 @@ decrypt = function(encrypted, key, initVector) { ...@@ -250,49 +260,25 @@ decrypt = function(encrypted, key, initVector) {
250 for (wordIx = 0; wordIx < encrypted32.length; wordIx += 4) { 260 for (wordIx = 0; wordIx < encrypted32.length; wordIx += 4) {
251 // convert big-endian (network order) words into little-endian 261 // convert big-endian (network order) words into little-endian
252 // (javascript order) 262 // (javascript order)
253 encrypted0 = (encrypted32[wordIx] << 24) | 263 encrypted0 = ntoh(encrypted32[wordIx]);
254 ((encrypted32[wordIx] & 0xff00) << 8) | 264 encrypted1 = ntoh(encrypted32[wordIx + 1]);
255 ((encrypted32[wordIx] & 0xff0000) >> 8) | 265 encrypted2 = ntoh(encrypted32[wordIx + 2]);
256 (encrypted32[wordIx] >>> 24); 266 encrypted3 = ntoh(encrypted32[wordIx + 3]);
257 encrypted1 = (encrypted32[wordIx + 1] << 24) |
258 ((encrypted32[wordIx + 1] & 0xff00) << 8) |
259 ((encrypted32[wordIx + 1] & 0xff0000) >> 8) |
260 (encrypted32[wordIx + 1] >>> 24);
261 encrypted2 = (encrypted32[wordIx + 2] << 24) |
262 ((encrypted32[wordIx + 2] & 0xff00) << 8) |
263 ((encrypted32[wordIx + 2] & 0xff0000) >> 8) |
264 (encrypted32[wordIx + 2] >>> 24);
265 encrypted3 = (encrypted32[wordIx + 3] << 24) |
266 ((encrypted32[wordIx + 3] & 0xff00) << 8) |
267 ((encrypted32[wordIx + 3] & 0xff0000) >> 8) |
268 (encrypted32[wordIx + 3] >>> 24);
269 267
270 // decrypt the block 268 // decrypt the block
271 decipher.decrypt(encrypted0, encrypted1, encrypted2, encrypted3, decrypted32, wordIx); 269 decipher.decrypt(encrypted0,
270 encrypted1,
271 encrypted2,
272 encrypted3,
273 decrypted32,
274 wordIx);
272 275
273 // XOR with the IV, and restore network byte-order to obtain the 276 // XOR with the IV, and restore network byte-order to obtain the
274 // plaintext 277 // plaintext
275 decrypted0 = decrypted32[wordIx] ^ init0; 278 decrypted32[wordIx] = ntoh(decrypted32[wordIx] ^ init0);
276 decrypted1 = decrypted32[wordIx + 1] ^ init1; 279 decrypted32[wordIx + 1] = ntoh(decrypted32[wordIx + 1] ^ init1);
277 decrypted2 = decrypted32[wordIx + 2] ^ init2; 280 decrypted32[wordIx + 2] = ntoh(decrypted32[wordIx + 2] ^ init2);
278 decrypted3 = decrypted32[wordIx + 3] ^ init3; 281 decrypted32[wordIx + 3] = ntoh(decrypted32[wordIx + 3] ^ init3);
279
280 decrypted32[wordIx] = decrypted0 << 24 |
281 ((decrypted0 & 0xff00) << 8) |
282 ((decrypted0 & 0xff0000) >> 8) |
283 (decrypted0 >>> 24);
284 decrypted32[wordIx + 1] = decrypted1 << 24 |
285 ((decrypted1 & 0xff00) << 8) |
286 ((decrypted1 & 0xff0000) >> 8) |
287 (decrypted1 >>> 24);
288 decrypted32[wordIx + 2] = decrypted2 << 24 |
289 ((decrypted2 & 0xff00) << 8) |
290 ((decrypted2 & 0xff0000) >> 8) |
291 (decrypted2 >>> 24);
292 decrypted32[wordIx + 3] = decrypted3 << 24 |
293 ((decrypted3 & 0xff00) << 8) |
294 ((decrypted3 & 0xff0000) >> 8) |
295 (decrypted3 >>> 24);
296 282
297 // setup the IV for the next round 283 // setup the IV for the next round
298 init0 = encrypted0; 284 init0 = encrypted0;
...@@ -301,11 +287,79 @@ decrypt = function(encrypted, key, initVector) { ...@@ -301,11 +287,79 @@ decrypt = function(encrypted, key, initVector) {
301 init3 = encrypted3; 287 init3 = encrypted3;
302 } 288 }
303 289
304 // remove any padding 290 return decrypted;
305 return unpad(decrypted); 291 };
292
293 AsyncStream = function() {
294 this.jobs = [];
295 this.delay = 1;
296 this.timeout_ = null;
297 };
298 AsyncStream.prototype = new videojs.Hls.Stream();
299 AsyncStream.prototype.processJob_ = function() {
300 this.jobs.shift()();
301 if (this.jobs.length) {
302 this.timeout_ = setTimeout(videojs.bind(this, this.processJob_),
303 this.delay);
304 } else {
305 this.timeout_ = null;
306 }
307 };
308 AsyncStream.prototype.push = function(job) {
309 this.jobs.push(job);
310 if (!this.timeout_) {
311 this.timeout_ = setTimeout(videojs.bind(this, this.processJob_),
312 this.delay);
313 }
314 };
315
316 Decrypter = function(encrypted, key, initVector, done) {
317 var
318 step = Decrypter.STEP,
319 encrypted32 = new Int32Array(encrypted.buffer),
320 decrypted = new Uint8Array(encrypted.byteLength),
321 i = 0;
322 this.asyncStream_ = new AsyncStream();
323
324 // split up the encryption job and do the individual chunks asynchronously
325 this.asyncStream_.push(this.decryptChunk_(encrypted32.subarray(i, i + step),
326 key,
327 initVector,
328 decrypted,
329 i));
330 for (i = step; i < encrypted32.length; i += step) {
331 initVector = new Uint32Array([
332 ntoh(encrypted32[i - 4]),
333 ntoh(encrypted32[i - 3]),
334 ntoh(encrypted32[i - 2]),
335 ntoh(encrypted32[i - 1])
336 ]);
337 this.asyncStream_.push(this.decryptChunk_(encrypted32.subarray(i, i + step),
338 key,
339 initVector,
340 decrypted));
341 }
342 // invoke the done() callback when everything is finished
343 this.asyncStream_.push(function() {
344 // remove pkcs#7 padding from the decrypted bytes
345 done(null, unpad(decrypted));
346 });
347 };
348 Decrypter.prototype = new videojs.Hls.Stream();
349 Decrypter.prototype.decryptChunk_ = function(encrypted, key, initVector, decrypted) {
350 return function() {
351 var bytes = decrypt(encrypted,
352 key,
353 initVector);
354 decrypted.set(bytes, encrypted.byteOffset);
355 };
306 }; 356 };
357 // the maximum number of bytes to process at one time
358 Decrypter.STEP = 4 * 8000;
307 359
308 // exports 360 // exports
309 videojs.Hls.decrypt = decrypt; 361 videojs.Hls.decrypt = decrypt;
362 videojs.Hls.Decrypter = Decrypter;
363 videojs.Hls.AsyncStream = AsyncStream;
310 364
311 })(window, window.videojs, window.pkcs7.unpad); 365 })(window, window.videojs, window.pkcs7.unpad);
......
...@@ -633,6 +633,8 @@ videojs.Hls.prototype.loadSegment = function(segmentUri, offset) { ...@@ -633,6 +633,8 @@ videojs.Hls.prototype.loadSegment = function(segmentUri, offset) {
633 responseType: 'arraybuffer', 633 responseType: 'arraybuffer',
634 withCredentials: settings.withCredentials 634 withCredentials: settings.withCredentials
635 }, function(error, url) { 635 }, function(error, url) {
636 var segmentInfo;
637
636 // the segment request is no longer outstanding 638 // the segment request is no longer outstanding
637 tech.segmentXhr_ = null; 639 tech.segmentXhr_ = null;
638 640
...@@ -665,12 +667,26 @@ videojs.Hls.prototype.loadSegment = function(segmentUri, offset) { ...@@ -665,12 +667,26 @@ videojs.Hls.prototype.loadSegment = function(segmentUri, offset) {
665 // if the segment is the start of a timestamp discontinuity, 667 // if the segment is the start of a timestamp discontinuity,
666 // we have to wait until the sourcebuffer is empty before 668 // we have to wait until the sourcebuffer is empty before
667 // aborting the source buffer processing 669 // aborting the source buffer processing
668 tech.segmentBuffer_.push({ 670 segmentInfo = {
671 // the segment's mediaIndex at the time it was received
669 mediaIndex: tech.mediaIndex, 672 mediaIndex: tech.mediaIndex,
673 // the segment's playlist
670 playlist: tech.playlists.media(), 674 playlist: tech.playlists.media(),
675 // optionally, a time offset to seek to within the segment
671 offset: offset, 676 offset: offset,
672 bytes: new Uint8Array(this.response) 677 // unencrypted bytes of the segment
673 }); 678 bytes: null,
679 // when a key is defined for this segment, the encrypted bytes
680 encryptedBytes: null,
681 // optionally, the decrypter that is unencrypting the segment
682 decrypter: null
683 };
684 if (segmentInfo.playlist.segments[segmentInfo.mediaIndex].key) {
685 segmentInfo.encryptedBytes = new Uint8Array(this.response);
686 } else {
687 segmentInfo.bytes = new Uint8Array(this.response);
688 }
689 tech.segmentBuffer_.push(segmentInfo);
674 player.trigger('progress'); 690 player.trigger('progress');
675 tech.drainBuffer(); 691 tech.drainBuffer();
676 692
...@@ -685,12 +701,15 @@ videojs.Hls.prototype.loadSegment = function(segmentUri, offset) { ...@@ -685,12 +701,15 @@ videojs.Hls.prototype.loadSegment = function(segmentUri, offset) {
685 videojs.Hls.prototype.drainBuffer = function(event) { 701 videojs.Hls.prototype.drainBuffer = function(event) {
686 var 702 var
687 i = 0, 703 i = 0,
704 segmentInfo,
688 mediaIndex, 705 mediaIndex,
689 playlist, 706 playlist,
690 offset, 707 offset,
691 tags, 708 tags,
692 bytes, 709 bytes,
693 segment, 710 segment,
711 decrypter,
712 segIv,
694 713
695 ptsTime, 714 ptsTime,
696 segmentOffset, 715 segmentOffset,
...@@ -700,28 +719,45 @@ videojs.Hls.prototype.drainBuffer = function(event) { ...@@ -700,28 +719,45 @@ videojs.Hls.prototype.drainBuffer = function(event) {
700 return; 719 return;
701 } 720 }
702 721
703 mediaIndex = segmentBuffer[0].mediaIndex; 722 segmentInfo = segmentBuffer[0];
704 playlist = segmentBuffer[0].playlist; 723
705 offset = segmentBuffer[0].offset; 724 mediaIndex = segmentInfo.mediaIndex;
706 bytes = segmentBuffer[0].bytes; 725 playlist = segmentInfo.playlist;
726 offset = segmentInfo.offset;
727 bytes = segmentInfo.bytes;
707 segment = playlist.segments[mediaIndex]; 728 segment = playlist.segments[mediaIndex];
708 729
709 if (segment.key) { 730 if (segment.key && !bytes) {
731
710 // this is an encrypted segment 732 // this is an encrypted segment
711 // if the key download failed, we want to skip this segment 733 // if the key download failed, we want to skip this segment
712 // but if the key hasn't downloaded yet, we want to try again later 734 // but if the key hasn't downloaded yet, we want to try again later
713 if (keyFailed(segment.key)) { 735 if (keyFailed(segment.key)) {
714 return segmentBuffer.shift(); 736 return segmentBuffer.shift();
715 } else if (!segment.key.bytes) { 737 } else if (!segment.key.bytes) {
738
716 // trigger a key request if one is not already in-flight 739 // trigger a key request if one is not already in-flight
717 return this.fetchKeys_(); 740 return this.fetchKeys_();
741
742 } else if (segmentInfo.decrypter) {
743
744 // decryption is in progress, try again later
745 return;
746
718 } else { 747 } else {
719 // if the media sequence is greater than 2^32, the IV will be incorrect 748 // if the media sequence is greater than 2^32, the IV will be incorrect
720 // assuming 10s segments, that would be about 1300 years 749 // assuming 10s segments, that would be about 1300 years
721 var segIv = segment.key.iv || new Uint32Array([0, 0, 0, mediaIndex + playlist.mediaSequence]); 750 segIv = segment.key.iv || new Uint32Array([0, 0, 0, mediaIndex + playlist.mediaSequence]);
722 bytes = videojs.Hls.decrypt(bytes, 751
752 // create a decrypter to incrementally decrypt the segment
753 decrypter = new videojs.Hls.Decrypter(segmentInfo.encryptedBytes,
723 segment.key.bytes, 754 segment.key.bytes,
724 segIv); 755 segIv,
756 function(err, bytes) {
757 segmentInfo.bytes = bytes;
758 });
759 segmentInfo.decrypter = decrypter;
760 return;
725 } 761 }
726 } 762 }
727 763
......
1 (function(window, videojs, undefined) { 1 (function(window, videojs, unpad, undefined) {
2 'use strict'; 2 'use strict';
3 /* 3 /*
4 ======== A Handy Little QUnit Reference ======== 4 ======== A Handy Little QUnit Reference ========
...@@ -46,7 +46,7 @@ test('decrypts a single AES-128 with PKCS7 block', function() { ...@@ -46,7 +46,7 @@ test('decrypts a single AES-128 with PKCS7 block', function() {
46 0x82, 0xa8, 0xf0, 0x67]); 46 0x82, 0xa8, 0xf0, 0x67]);
47 47
48 deepEqual('howdy folks', 48 deepEqual('howdy folks',
49 stringFromBytes(videojs.Hls.decrypt(encrypted, key, initVector)), 49 stringFromBytes(unpad(videojs.Hls.decrypt(encrypted, key, initVector))),
50 'decrypted with a byte array key'); 50 'decrypted with a byte array key');
51 }); 51 });
52 52
...@@ -68,8 +68,100 @@ test('decrypts multiple AES-128 blocks with CBC', function() { ...@@ -68,8 +68,100 @@ test('decrypts multiple AES-128 blocks with CBC', function() {
68 ]); 68 ]);
69 69
70 deepEqual('0123456789abcdef01234', 70 deepEqual('0123456789abcdef01234',
71 stringFromBytes(videojs.Hls.decrypt(encrypted, key, initVector)), 71 stringFromBytes(unpad(videojs.Hls.decrypt(encrypted, key, initVector))),
72 'decrypted multiple blocks'); 72 'decrypted multiple blocks');
73 }); 73 });
74 74
75 })(window, window.videojs); 75 var clock;
76
77 module('Incremental Processing', {
78 setup: function() {
79 clock = sinon.useFakeTimers();
80 },
81 teardown: function() {
82 clock.restore();
83 }
84 });
85
86 test('executes a callback after a timeout', function() {
87 var asyncStream = new videojs.Hls.AsyncStream(),
88 calls = '';
89 asyncStream.push(function() {
90 calls += 'a';
91 });
92
93 clock.tick(asyncStream.delay);
94 equal(calls, 'a', 'invoked the callback once');
95 clock.tick(asyncStream.delay);
96 equal(calls, 'a', 'only invoked the callback once');
97 });
98
99 test('executes callback in series', function() {
100 var asyncStream = new videojs.Hls.AsyncStream(),
101 calls = '';
102 asyncStream.push(function() {
103 calls += 'a';
104 });
105 asyncStream.push(function() {
106 calls += 'b';
107 });
108
109 clock.tick(asyncStream.delay);
110 equal(calls, 'a', 'invoked the first callback');
111 clock.tick(asyncStream.delay);
112 equal(calls, 'ab', 'invoked the second');
113 });
114
115 var decrypter;
116
117 module('Incremental Decryption', {
118 setup: function() {
119 clock = sinon.useFakeTimers();
120 },
121 teardown: function() {
122 clock.restore();
123 }
124 });
125
126 test('asynchronously decrypts a 4-word block', function() {
127 var
128 key = new Uint32Array([0, 0, 0, 0]),
129 initVector = key,
130 // the string "howdy folks" encrypted
131 encrypted = new Uint8Array([
132 0xce, 0x90, 0x97, 0xd0,
133 0x08, 0x46, 0x4d, 0x18,
134 0x4f, 0xae, 0x01, 0x1c,
135 0x82, 0xa8, 0xf0, 0x67]),
136 decrypted;
137
138 decrypter = new videojs.Hls.Decrypter(encrypted, key, initVector, function(error, result) {
139 decrypted = result;
140 });
141 ok(!decrypted, 'asynchronously decrypts');
142
143 clock.tick(decrypter.asyncStream_.delay * 2);
144
145 ok(decrypted, 'completed decryption');
146 deepEqual('howdy folks',
147 stringFromBytes(decrypted),
148 'decrypts and unpads the result');
149 });
150
151 test('breaks up input greater than the step value', function() {
152 var encrypted = new Int32Array(videojs.Hls.Decrypter.STEP + 4),
153 done = false,
154 decrypter = new videojs.Hls.Decrypter(encrypted,
155 new Uint32Array(4),
156 new Uint32Array(4),
157 function(error, result) {
158 done = true;
159 });
160 clock.tick(decrypter.asyncStream_.delay * 2);
161 ok(!done, 'not finished after two ticks');
162
163 clock.tick(decrypter.asyncStream_.delay);
164 ok(done, 'finished after the last chunk is decrypted');
165 });
166
167 })(window, window.videojs, window.pkcs7.unpad);
......
...@@ -152,10 +152,8 @@ module('HLS', { ...@@ -152,10 +152,8 @@ module('HLS', {
152 152
153 oldNativeHlsSupport = videojs.Hls.supportsNativeHls; 153 oldNativeHlsSupport = videojs.Hls.supportsNativeHls;
154 154
155 oldDecrypt = videojs.Hls.decrypt; 155 oldDecrypt = videojs.Hls.Decrypter;
156 videojs.Hls.decrypt = function() { 156 videojs.Hls.Decrypter = function() {};
157 return new Uint8Array([0]);
158 };
159 157
160 // fake XHRs 158 // fake XHRs
161 xhr = sinon.useFakeXMLHttpRequest(); 159 xhr = sinon.useFakeXMLHttpRequest();
...@@ -173,7 +171,7 @@ module('HLS', { ...@@ -173,7 +171,7 @@ module('HLS', {
173 videojs.MediaSource.open = oldMediaSourceOpen; 171 videojs.MediaSource.open = oldMediaSourceOpen;
174 videojs.Hls.SegmentParser = oldSegmentParser; 172 videojs.Hls.SegmentParser = oldSegmentParser;
175 videojs.Hls.supportsNativeHls = oldNativeHlsSupport; 173 videojs.Hls.supportsNativeHls = oldNativeHlsSupport;
176 videojs.Hls.decrypt = oldDecrypt; 174 videojs.Hls.Decrypter = oldDecrypt;
177 videojs.SourceBuffer = oldSourceBuffer; 175 videojs.SourceBuffer = oldSourceBuffer;
178 window.setTimeout = oldSetTimeout; 176 window.setTimeout = oldSetTimeout;
179 window.clearTimeout = oldClearTimeout; 177 window.clearTimeout = oldClearTimeout;
...@@ -1878,6 +1876,10 @@ test('a new key XHR is created when a the segment is received', function() { ...@@ -1878,6 +1876,10 @@ test('a new key XHR is created when a the segment is received', function() {
1878 '#EXT-X-ENDLIST\n'); 1876 '#EXT-X-ENDLIST\n');
1879 standardXHRResponse(requests.shift()); // segment 1 1877 standardXHRResponse(requests.shift()); // segment 1
1880 standardXHRResponse(requests.shift()); // key 1 1878 standardXHRResponse(requests.shift()); // key 1
1879 // "finish" decrypting segment 1
1880 player.hls.segmentBuffer_[0].bytes = new Uint8Array(16);
1881 player.hls.checkBuffer_();
1882
1881 standardXHRResponse(requests.shift()); // segment 2 1883 standardXHRResponse(requests.shift()); // segment 2
1882 1884
1883 strictEqual(requests.length, 1, 'a key XHR is created'); 1885 strictEqual(requests.length, 1, 'a key XHR is created');
...@@ -1983,6 +1985,9 @@ test('skip segments if key requests fail more than once', function() { ...@@ -1983,6 +1985,9 @@ test('skip segments if key requests fail more than once', function() {
1983 // key for second segment 1985 // key for second segment
1984 requests[0].response = new Uint32Array([0,0,0,0]).buffer; 1986 requests[0].response = new Uint32Array([0,0,0,0]).buffer;
1985 requests.shift().respond(200, null, ''); 1987 requests.shift().respond(200, null, '');
1988 // "finish" decryption
1989 player.hls.segmentBuffer_[0].bytes = new Uint8Array(16);
1990 player.hls.checkBuffer_();
1986 1991
1987 equal(bytes.length, 2, 'bytes from the second ts segment should be added'); 1992 equal(bytes.length, 2, 'bytes from the second ts segment should be added');
1988 equal(bytes[1], 1, 'the bytes from the second segment are added and not the first'); 1993 equal(bytes[1], 1, 'the bytes from the second segment are added and not the first');
...@@ -2007,9 +2012,8 @@ test('the key is supplied to the decrypter in the correct format', function() { ...@@ -2007,9 +2012,8 @@ test('the key is supplied to the decrypter in the correct format', function() {
2007 'http://media.example.com/fileSequence52-B.ts\n'); 2012 'http://media.example.com/fileSequence52-B.ts\n');
2008 2013
2009 2014
2010 videojs.Hls.decrypt = function(bytes, key) { 2015 videojs.Hls.Decrypter = function(encrypted, key) {
2011 keys.push(key); 2016 keys.push(key);
2012 return new Uint8Array([0]);
2013 }; 2017 };
2014 2018
2015 standardXHRResponse(requests.shift()); // segment 2019 standardXHRResponse(requests.shift()); // segment
...@@ -2017,7 +2021,7 @@ test('the key is supplied to the decrypter in the correct format', function() { ...@@ -2017,7 +2021,7 @@ test('the key is supplied to the decrypter in the correct format', function() {
2017 requests[0].respond(200, null, ''); 2021 requests[0].respond(200, null, '');
2018 requests.shift(); // key 2022 requests.shift(); // key
2019 2023
2020 equal(keys.length, 1, 'only one call to decrypt was made'); 2024 equal(keys.length, 1, 'only one Decrypter was constructed');
2021 deepEqual(keys[0], 2025 deepEqual(keys[0],
2022 new Uint32Array([0, 0x01000000, 0x02000000, 0x03000000]), 2026 new Uint32Array([0, 0x01000000, 0x02000000, 0x03000000]),
2023 'passed the specified segment key'); 2027 'passed the specified segment key');
...@@ -2042,9 +2046,8 @@ test('supplies the media sequence of current segment as the IV by default, if no ...@@ -2042,9 +2046,8 @@ test('supplies the media sequence of current segment as the IV by default, if no
2042 'http://media.example.com/fileSequence52-B.ts\n'); 2046 'http://media.example.com/fileSequence52-B.ts\n');
2043 2047
2044 2048
2045 videojs.Hls.decrypt = function(bytes, key, iv) { 2049 videojs.Hls.Decrypter = function(encrypted, key, iv) {
2046 ivs.push(iv); 2050 ivs.push(iv);
2047 return new Uint8Array([0]);
2048 }; 2051 };
2049 2052
2050 requests[0].response = new Uint32Array([0,0,0,0]).buffer; 2053 requests[0].response = new Uint32Array([0,0,0,0]).buffer;
...@@ -2052,7 +2055,7 @@ test('supplies the media sequence of current segment as the IV by default, if no ...@@ -2052,7 +2055,7 @@ test('supplies the media sequence of current segment as the IV by default, if no
2052 requests.shift(); 2055 requests.shift();
2053 standardXHRResponse(requests.pop()); 2056 standardXHRResponse(requests.pop());
2054 2057
2055 equal(ivs.length, 1, 'only one call to decrypt was made'); 2058 equal(ivs.length, 1, 'only one Decrypter was constructed');
2056 deepEqual(ivs[0], 2059 deepEqual(ivs[0],
2057 new Uint32Array([0, 0, 0, 5]), 2060 new Uint32Array([0, 0, 0, 5]),
2058 'the IV for the segment is the media sequence'); 2061 'the IV for the segment is the media sequence');
......