Skip to content

Commit

Permalink
add option to cache encrpytion keys in the player
Browse files Browse the repository at this point in the history
  • Loading branch information
mjneil committed Mar 28, 2019
1 parent fbd615c commit f2c2299
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 21 deletions.
15 changes: 4 additions & 11 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions src/bin-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,13 @@ export const initSegmentId = function(initSegment) {
].join(',');
};

/**
*
*/
export const segmentKeyId = function(key) {
return key.resolvedUri;
};

/**
* utils to help dump binary data to the console
*/
Expand Down
6 changes: 4 additions & 2 deletions src/master-playlist-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ export class MasterPlaylistController extends videojs.EventTarget {
blacklistDuration,
enableLowInitialPlaylist,
sourceType,
seekTo
seekTo,
cacheEncryptionKeys
} = options;

if (!url) {
Expand Down Expand Up @@ -125,7 +126,8 @@ export class MasterPlaylistController extends videojs.EventTarget {
syncController: this.syncController_,
decrypter: this.decrypter_,
sourceType: this.sourceType_,
inbandTextTracks: this.inbandTextTracks_
inbandTextTracks: this.inbandTextTracks_,
cacheEncryptionKeys
};

this.masterPlaylistLoader_ = this.sourceType_ === 'dash' ?
Expand Down
8 changes: 5 additions & 3 deletions src/media-segment-request.js
Original file line number Diff line number Diff line change
Expand Up @@ -285,16 +285,18 @@ const decryptSegment = (decrypter, segment, doneFn) => {

decrypter.addEventListener('message', decryptionHandler);

const keyBytes = segment.key.bytes.slice();

// this is an encrypted segment
// incrementally decrypt the segment
decrypter.postMessage(createTransferableMessage({
source: segment.requestId,
encrypted: segment.encryptedBytes,
key: segment.key.bytes,
key: keyBytes,
iv: segment.key.iv
}), [
segment.encryptedBytes.buffer,
segment.key.bytes.buffer
keyBytes.buffer
]);
};

Expand Down Expand Up @@ -432,7 +434,7 @@ export const mediaSegmentRequest = (xhr,
const finishProcessingFn = waitForCompletion(activeXhrs, decryptionWorker, doneFn);

// optionally, request the decryption key
if (segment.key) {
if (segment.key && ! segment.key.bytes) {
const keyRequestOptions = videojs.mergeOptions(xhrOptions, {
uri: segment.key.resolvedUri,
responseType: 'arraybuffer'
Expand Down
49 changes: 44 additions & 5 deletions src/segment-loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import SourceUpdater from './source-updater';
import Config from './config';
import window from 'global/window';
import { removeCuesFromTrack } from './mse/remove-cues-from-track';
import { initSegmentId } from './bin-utils';
import { initSegmentId, segmentKeyId } from './bin-utils';
import { mediaSegmentRequest, REQUEST_ERRORS } from './media-segment-request';
import { TIME_FUDGE_FACTOR, timeUntilRebuffer as timeUntilRebuffer_ } from './ranges';
import { minRebufferMaxBandwidthSelector } from './playlist-selectors';
Expand Down Expand Up @@ -183,6 +183,11 @@ export default class SegmentLoader extends videojs.EventTarget {
// Fragmented mp4 playback
this.activeInitSegmentId_ = null;
this.initSegments_ = {};

// HLSe playback
this.cacheEncryptionKeys_ = settings.cacheEncryptionKeys;
this.keyCache_ = {};

// Fmp4 CaptionParser
this.captionParser_ = new CaptionParser();

Expand Down Expand Up @@ -355,6 +360,34 @@ export default class SegmentLoader extends videojs.EventTarget {
return storedMap || map;
}

/**
* Gets and sets key for the provided key
*
* @param {Object} key
* The key object representing the key to get or set
* @param {Boolean=} set
* If true, the key for the provided key should be saved
* @return {Object}
* Key object for desired key
*/
segmentKey(key, set = false) {
if (!key) {
return null;
}

const id = segmentKeyId(key);
let storedKey = this.keyCache_[id];

if (this.cacheEncryptionKeys_ && set && !storedKey && key.bytes) {
this.keyCache_[id] = storedKey = {
resolvedUri: key.resolvedUri,
bytes: key.bytes
};
}

return storedKey || { resolvedUri: key.resolvedUri };
}

/**
* Returns true if all configuration required for loading is present, otherwise false.
*
Expand Down Expand Up @@ -1048,10 +1081,8 @@ export default class SegmentLoader extends videojs.EventTarget {
0, 0, 0, segmentInfo.mediaIndex + segmentInfo.playlist.mediaSequence
]);

simpleSegment.key = {
resolvedUri: segment.key.resolvedUri,
iv
};
simpleSegment.key = this.segmentKey(segment.key);
simpleSegment.key.iv = iv;

This comment has been minimized.

Copy link
@ldayananda

ldayananda Mar 28, 2019

Contributor

should we be caching the iv as well?

This comment has been minimized.

Copy link
@mjneil

mjneil Mar 28, 2019

Author Contributor

No because this can change per segment with the same key file

This comment has been minimized.

Copy link
@ldayananda

ldayananda Mar 28, 2019

Contributor

As per https://tools.ietf.org/html/draft-pantos-hls-rfc8216bis-03#section-5.2 or v3 the iv in the EXT-X-KEY tag must be used. If the iv attribute is not available then it will definitely change per segment

This comment has been minimized.

Copy link
@mjneil

mjneil Mar 28, 2019

Author Contributor

We are using it if it exists const iv = segment.key.iv || new Uint32Array we just arent caching it incase it changes in the next segment

}

if (segment.map) {
Expand Down Expand Up @@ -1136,6 +1167,11 @@ export default class SegmentLoader extends videojs.EventTarget {
simpleSegment.map = this.initSegment(simpleSegment.map, true);
}

// if this request included a segment key, save that data in the cache
if (simpleSegment.key) {
this.segmentKey(simpleSegment.key, true);
}

this.processSegmentResponse_(simpleSegment);
}

Expand All @@ -1152,6 +1188,9 @@ export default class SegmentLoader extends videojs.EventTarget {
if (simpleSegment.map) {
segmentInfo.segment.map.bytes = simpleSegment.map.bytes;
}
// if (simpleSegment.key) {
// segmentInfo.segment.key.bytes = simpleSegment.key.bytes;
// }

segmentInfo.endOfAllRequests = simpleSegment.endOfAllRequests;

Expand Down
1 change: 1 addition & 0 deletions src/videojs-http-streaming.js
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,7 @@ class HlsHandler extends Component {
this.options_.useBandwidthFromLocalStorage || false;
this.options_.customTagParsers = this.options_.customTagParsers || [];
this.options_.customTagMappers = this.options_.customTagMappers || [];
this.options_.cacheEncryptionKeys = this.options_.cacheEncryptionKeys || false;

if (typeof this.options_.blacklistDuration !== 'number') {
this.options_.blacklistDuration = 5 * 60;
Expand Down

0 comments on commit f2c2299

Please sign in to comment.