Skip to content

Commit

Permalink
fix(HLS): Support AES-128 in init segment according the RFC (#5677)
Browse files Browse the repository at this point in the history
Fixes #5667

Backported to v4.3.x
  • Loading branch information
avelad authored and joeyparrish committed Oct 4, 2023
1 parent a7d2f9b commit d30c571
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 8 deletions.
40 changes: 32 additions & 8 deletions lib/hls/hls_parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -2371,13 +2371,13 @@ shaka.hls.HlsParser = class {

/**
* Get the InitSegmentReference for a segment if it has a EXT-X-MAP tag.
* @param {string} playlistUri The absolute uri of the media playlist.
* @param {!shaka.hls.Playlist} playlist
* @param {!Array.<!shaka.hls.Tag>} tags Segment tags
* @param {!Map.<string, string>} variables
* @return {shaka.media.InitSegmentReference}
* @private
*/
getInitSegmentReference_(playlistUri, tags, variables) {
getInitSegmentReference_(playlist, tags, variables) {
/** @type {?shaka.hls.Tag} */
const mapTag = shaka.hls.Utils.getFirstTagWithName(tags, 'EXT-X-MAP');

Expand All @@ -2388,16 +2388,26 @@ shaka.hls.HlsParser = class {
const verbatimInitSegmentUri = mapTag.getRequiredAttrValue('URI');
const absoluteInitSegmentUri = this.variableSubstitution_(
shaka.hls.Utils.constructAbsoluteUri(
playlistUri, verbatimInitSegmentUri),
playlist.absoluteUri, verbatimInitSegmentUri),
variables);

const mapTagKey = [
absoluteInitSegmentUri,
mapTag.getAttributeValue('BYTERANGE', ''),
].join('-');
if (!this.mapTagToInitSegmentRefMap_.has(mapTagKey)) {
/** @type {shaka.extern.HlsAes128Key|undefined} */
let aes128Key = undefined;
for (const tag of tags) {
if (tag.name == 'EXT-X-KEY') {
if (tag.getRequiredAttrValue('METHOD') == 'AES-128' &&
tag.id < mapTag.id) {
aes128Key = this.parseAES128DrmTag_(tag, playlist);
}
}
}
const initSegmentRef = this.createInitSegmentReference_(
absoluteInitSegmentUri, mapTag);
absoluteInitSegmentUri, mapTag, aes128Key);
this.mapTagToInitSegmentRefMap_.set(mapTagKey, initSegmentRef);
}
return this.mapTagToInitSegmentRefMap_.get(mapTagKey);
Expand All @@ -2408,10 +2418,11 @@ shaka.hls.HlsParser = class {
* playlist.
* @param {string} absoluteInitSegmentUri
* @param {!shaka.hls.Tag} mapTag EXT-X-MAP
* @param {shaka.extern.HlsAes128Key=} aes128Key
* @return {!shaka.media.InitSegmentReference}
* @private
*/
createInitSegmentReference_(absoluteInitSegmentUri, mapTag) {
createInitSegmentReference_(absoluteInitSegmentUri, mapTag, aes128Key) {
let startByte = 0;
let endByte = null;
const byterange = mapTag.getAttributeValue('BYTERANGE');
Expand All @@ -2422,12 +2433,25 @@ shaka.hls.HlsParser = class {
const byteLength = Number(blocks[0]);
startByte = Number(blocks[1]);
endByte = startByte + byteLength - 1;

if (aes128Key) {
// MAP segment encrypted with method 'AES-128', when served with
// HTTP Range, has the unencrypted size specified in the range.
// See: https://tools.ietf.org/html/draft-pantos-hls-rfc8216bis-08#section-6.3.6
const length = (endByte + 1) - startByte;
if (length % 16) {
endByte += (16 - (length % 16));
}
}
}

const initSegmentRef = new shaka.media.InitSegmentReference(
() => [absoluteInitSegmentUri],
startByte,
endByte);
endByte,
/* mediaQuality= */ null,
/* timescale= */ null,
aes128Key);
return initSegmentRef;
}

Expand Down Expand Up @@ -2738,8 +2762,8 @@ shaka.hls.HlsParser = class {

mediaSequenceToStartTime.set(position, startTime);

initSegmentRef = this.getInitSegmentReference_(playlist.absoluteUri,
item.tags, variables);
initSegmentRef = this.getInitSegmentReference_(playlist, item.tags,
variables);

// If the stream is low latency and the user has not configured the
// lowLatencyMode, but if it has been configured to activate the
Expand Down
2 changes: 2 additions & 0 deletions test/hls/hls_parser_unit.js
Original file line number Diff line number Diff line change
Expand Up @@ -3075,8 +3075,10 @@ describe('HlsParser', () => {

const firstMp4Segment = mp4AesEncryptionVideo.segmentIndex.get(0);
expect(firstMp4Segment.hlsAes128Key).toBeDefined();
expect(firstMp4Segment.initSegmentReference.hlsAes128Key).toBeDefined();
const secondMp4Segment = mp4AesEncryptionVideo.segmentIndex.get(1);
expect(secondMp4Segment.hlsAes128Key).toBeNull();
expect(secondMp4Segment.initSegmentReference.hlsAes128Key).toBeDefined();

const tsAesEncryptionVideo = actual.variants[2].video;
await tsAesEncryptionVideo.createSegmentIndex();
Expand Down

0 comments on commit d30c571

Please sign in to comment.