Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extract HDR metadata from HLS manifests #3116

Merged
merged 1 commit into from
Feb 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions externs/shaka/manifest.js
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ shaka.extern.CreateSegmentIndexFunction;
* codecs: string,
* frameRate: (number|undefined),
* pixelAspectRatio: (string|undefined),
* hdr: (string|undefined),
* bandwidth: (number|undefined),
* width: (number|undefined),
* height: (number|undefined),
Expand Down Expand Up @@ -276,6 +277,9 @@ shaka.extern.CreateSegmentIndexFunction;
* @property {(string|undefined)} pixelAspectRatio
* <i>Video streams only.</i> <br>
* The Stream's pixel aspect ratio
* @property {(string|undefined)} hdr
* <i>Video streams only.</i> <br>
* The Stream's HDR info
* @property {(number|undefined)} bandwidth
* <i>Audio and video streams only.</i> <br>
* The stream's required bandwidth in bits per second.
Expand Down
3 changes: 3 additions & 0 deletions externs/shaka/offline.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ shaka.extern.ManifestDB;
* codecs: string,
* frameRate: (number|undefined),
* pixelAspectRatio: (string|undefined),
* hdr: (string|undefined),
* kind: (string|undefined),
* language: string,
* label: ?string,
Expand Down Expand Up @@ -138,6 +139,8 @@ shaka.extern.ManifestDB;
* The Stream's framerate in frames per second.
* @property {(string|undefined)} pixelAspectRatio
* The Stream's pixel aspect ratio
* @property {(string|undefined)} hdr
* The Stream's HDR info
* @property {(string|undefined)} kind
* The kind of text stream; undefined for audio/video.
* @property {string} language
Expand Down
3 changes: 3 additions & 0 deletions externs/shaka/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ shaka.extern.BufferedInfo;
* height: ?number,
* frameRate: ?number,
* pixelAspectRatio: ?string,
* hdr: ?string,
* mimeType: ?string,
* codecs: ?string,
* audioCodec: ?string,
Expand Down Expand Up @@ -258,6 +259,8 @@ shaka.extern.BufferedInfo;
* The video framerate provided in the manifest, if present.
* @property {?string} pixelAspectRatio
* The video pixel aspect ratio provided in the manifest, if present.
* @property {?string} hdr
* The video HDR provided in the manifest, if present.
* @property {?string} mimeType
* The MIME type of the content provided in the manifest.
* @property {?string} codecs
Expand Down
16 changes: 12 additions & 4 deletions lib/hls/hls_parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -699,6 +699,8 @@ shaka.hls.HlsParser = class {
const resolution = tag.getAttributeValue('RESOLUTION');
const [width, height] = resolution ? resolution.split('x') : [null, null];

const videoRange = tag.getAttributeValue('VIDEO-RANGE');

const streamInfos = await this.createStreamInfosForVariantTag_(tag,
resolution, frameRate);

Expand All @@ -712,7 +714,8 @@ shaka.hls.HlsParser = class {
bandwidth,
width,
height,
frameRate);
frameRate,
videoRange);
}
// We do not support AES-128 encryption with HLS yet. If the streamInfos
// is null because of AES-128 encryption, do not create variants for that.
Expand Down Expand Up @@ -969,15 +972,18 @@ shaka.hls.HlsParser = class {
* @param {?string} width
* @param {?string} height
* @param {?string} frameRate
* @param {?string} videoRange
* @return {!Array.<!shaka.extern.Variant>}
* @private
*/
createVariants_(audioInfos, videoInfos, bandwidth, width, height, frameRate) {
createVariants_(
audioInfos, videoInfos, bandwidth, width, height, frameRate, videoRange) {
const ContentType = shaka.util.ManifestParserUtils.ContentType;
const DrmEngine = shaka.media.DrmEngine;

for (const info of videoInfos) {
this.addVideoAttributes_(info.stream, width, height, frameRate);
this.addVideoAttributes_(
info.stream, width, height, frameRate, videoRange);
}

// In case of audio-only or video-only content or the audio/video is
Expand Down Expand Up @@ -2440,13 +2446,15 @@ shaka.hls.HlsParser = class {
* @param {?string} width
* @param {?string} height
* @param {?string} frameRate
* @param {?string} videoRange
* @private
*/
addVideoAttributes_(stream, width, height, frameRate) {
addVideoAttributes_(stream, width, height, frameRate, videoRange) {
if (stream) {
stream.width = Number(width) || undefined;
stream.height = Number(height) || undefined;
stream.frameRate = Number(frameRate) || undefined;
stream.hdr = videoRange || undefined;
}
}

Expand Down
1 change: 1 addition & 0 deletions lib/offline/indexeddb/v1_storage_cell.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ shaka.offline.indexeddb.V1StorageCell = class
codecs: old.codecs,
frameRate: old.frameRate,
pixelAspectRatio: undefined,
hdr: undefined,
kind: old.kind,
language: old.language,
label: old.label,
Expand Down
1 change: 1 addition & 0 deletions lib/offline/indexeddb/v2_storage_cell.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ shaka.offline.indexeddb.V2StorageCell = class
codecs: old.codecs,
frameRate: old.frameRate,
pixelAspectRatio: old.pixelAspectRatio,
hdr: undefined,
kind: old.kind,
language: old.language,
label: old.label,
Expand Down
1 change: 1 addition & 0 deletions lib/offline/manifest_converter.js
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ shaka.offline.ManifestConverter = class {
height: streamDB.height || undefined,
frameRate: streamDB.frameRate,
pixelAspectRatio: streamDB.pixelAspectRatio,
hdr: streamDB.hdr,
kind: streamDB.kind,
encrypted: streamDB.encrypted,
drmInfos: [],
Expand Down
1 change: 1 addition & 0 deletions lib/offline/storage.js
Original file line number Diff line number Diff line change
Expand Up @@ -1106,6 +1106,7 @@ shaka.offline.Storage = class {
codecs: stream.codecs,
frameRate: stream.frameRate,
pixelAspectRatio: stream.pixelAspectRatio,
hdr: stream.hdr,
kind: stream.kind,
language: stream.language,
label: stream.label,
Expand Down
3 changes: 3 additions & 0 deletions lib/util/mime_utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ shaka.util.MimeUtils = class {
components.push(mimeKey + '="' + value + '"');
}
});
if (stream.hdr == 'PQ') {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The HLS specification allows: SDR, PQ, HLG. Should we also add HLG to the list?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@michellezhuogg do you have any preference about it?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not familiar with HLG and its according mimetype. Shall we skip HLG until we are sure about how to represent it with mimetype?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I think it's best to skip it for now. So this is ready to be merged whenever you want.

components.push('eotf="smpte2084"');
}

return components.join(';');
}
Expand Down
3 changes: 3 additions & 0 deletions lib/util/stream_utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,7 @@ shaka.util.StreamUtils = class {
height: null,
frameRate: null,
pixelAspectRatio: null,
hdr: null,
mimeType: mimeType,
codecs: codecs.join(', '),
audioCodec: audioCodec,
Expand Down Expand Up @@ -422,6 +423,7 @@ shaka.util.StreamUtils = class {
height: null,
frameRate: null,
pixelAspectRatio: null,
hdr: null,
mimeType: stream.mimeType,
codecs: stream.codecs || null,
audioCodec: null,
Expand Down Expand Up @@ -536,6 +538,7 @@ shaka.util.StreamUtils = class {
height: null,
frameRate: null,
pixelAspectRatio: null,
hdr: null,
mimeType: null,
codecs: null,
audioCodec: null,
Expand Down
61 changes: 61 additions & 0 deletions test/hls/hls_parser_unit.js
Original file line number Diff line number Diff line change
Expand Up @@ -958,6 +958,67 @@ describe('HlsParser', () => {
await testHlsParser(master, media, manifest);
});

it('parses manifest with HDR metadata', async () => {
const master = [
'#EXTM3U\n',
'#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",LANGUAGE="eng",',
'URI="audio"\n',
'#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="sub1",LANGUAGE="eng",',
'FORCED=YES,URI="text"\n',
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1,mp4a",VIDEO-RANGE=PQ,',
'RESOLUTION=960x540,FRAME-RATE=60,AUDIO="aud1",SUBTITLES="sub1"\n',
'video\n',
].join('');

const media = [
'#EXTM3U\n',
'#EXT-X-PLAYLIST-TYPE:VOD\n',
'#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
'#EXTINF:5,\n',
'#EXT-X-BYTERANGE:121090@616\n',
'main.mp4',
].join('');

const textMedia = [
'#EXTM3U\n',
'#EXT-X-PLAYLIST-TYPE:VOD\n',
'#EXTINF:5,\n',
'#EXT-X-BYTERANGE:121090@616\n',
'main.vtt',
].join('');

const manifest = shaka.test.ManifestGenerator.generate((manifest) => {
manifest.anyTimeline();
manifest.addPartialVariant((variant) => {
variant.addPartialStream(ContentType.VIDEO, (stream) => {
stream.mime('video/mp4', 'avc1');
stream.hdr = 'PQ';
});
variant.addPartialStream(ContentType.AUDIO, (stream) => {
stream.mime('audio/mp4', 'mp4a');
});
});
manifest.addPartialTextStream((stream) => {
stream.language = 'en';
stream.forced = true;
stream.kind = TextStreamKind.SUBTITLE;
stream.mime('text/vtt', '');
});
});

fakeNetEngine
.setResponseText('test:/master', master)
.setResponseText('test:/audio', media)
.setResponseText('test:/video', media)
.setResponseText('test:/text', textMedia)
.setResponseText('test:/main.vtt', vttText)
.setResponseValue('test:/init.mp4', initSegmentData)
.setResponseValue('test:/main.mp4', segmentData);

const actual = await parser.start('test:/master', playerInterface);
expect(actual).toEqual(manifest);
});

it('parses manifest with SUBTITLES', async () => {
const master = [
'#EXTM3U\n',
Expand Down
4 changes: 4 additions & 0 deletions test/offline/manifest_convert_unit.js
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,7 @@ describe('ManifestConverter', () => {
codecs: 'avc1.42c01e',
frameRate: 22,
pixelAspectRatio: '59:54',
hdr: undefined,
kind: undefined,
language: '',
label: null,
Expand Down Expand Up @@ -381,6 +382,7 @@ describe('ManifestConverter', () => {
codecs: 'mp4a.40.2',
frameRate: undefined,
pixelAspectRatio: undefined,
hdr: undefined,
kind: undefined,
language: 'en',
label: null,
Expand Down Expand Up @@ -426,6 +428,7 @@ describe('ManifestConverter', () => {
codecs: '',
frameRate: undefined,
pixelAspectRatio: undefined,
hdr: undefined,
kind: undefined,
language: 'en',
label: null,
Expand Down Expand Up @@ -478,6 +481,7 @@ describe('ManifestConverter', () => {
codecs: streamDb.codecs,
frameRate: streamDb.frameRate,
pixelAspectRatio: streamDb.pixelAspectRatio,
hdr: streamDb.hdr,
width: streamDb.width || undefined,
height: streamDb.height || undefined,
kind: streamDb.kind,
Expand Down
2 changes: 2 additions & 0 deletions test/offline/storage_integration.js
Original file line number Diff line number Diff line change
Expand Up @@ -1295,6 +1295,7 @@ filterDescribe('Storage', storageSupport, () => {
height: height,
frameRate: 30,
pixelAspectRatio: '59:54',
hdr: null,
mimeType: 'video/mp4,audio/mp4',
codecs: 'mp4,mp4',
audioCodec: 'mp4',
Expand Down Expand Up @@ -1333,6 +1334,7 @@ filterDescribe('Storage', storageSupport, () => {
height: null,
frameRate: null,
pixelAspectRatio: null,
hdr: null,
mimeType: 'text/vtt',
codecs: 'vtt',
audioCodec: null,
Expand Down
11 changes: 11 additions & 0 deletions test/player_unit.js
Original file line number Diff line number Diff line change
Expand Up @@ -1059,6 +1059,7 @@ describe('Player', () => {
height: 200,
frameRate: 1000000 / 42000,
pixelAspectRatio: '59:54',
hdr: null,
mimeType: 'video/mp4',
codecs: 'avc1.4d401f, mp4a.40.2',
audioCodec: 'mp4a.40.2',
Expand Down Expand Up @@ -1089,6 +1090,7 @@ describe('Player', () => {
height: 400,
frameRate: 24,
pixelAspectRatio: '59:54',
hdr: null,
mimeType: 'video/mp4',
codecs: 'avc1.4d401f, mp4a.40.2',
audioCodec: 'mp4a.40.2',
Expand Down Expand Up @@ -1119,6 +1121,7 @@ describe('Player', () => {
height: 200,
frameRate: 1000000 / 42000,
pixelAspectRatio: '59:54',
hdr: null,
mimeType: 'video/mp4',
codecs: 'avc1.4d401f, mp4a.40.2',
audioCodec: 'mp4a.40.2',
Expand Down Expand Up @@ -1149,6 +1152,7 @@ describe('Player', () => {
height: 400,
frameRate: 24,
pixelAspectRatio: '59:54',
hdr: null,
mimeType: 'video/mp4',
codecs: 'avc1.4d401f, mp4a.40.2',
audioCodec: 'mp4a.40.2',
Expand Down Expand Up @@ -1179,6 +1183,7 @@ describe('Player', () => {
height: 200,
frameRate: 1000000 / 42000,
pixelAspectRatio: '59:54',
hdr: null,
mimeType: 'video/mp4',
codecs: 'avc1.4d401f, mp4a.40.2',
audioCodec: 'mp4a.40.2',
Expand Down Expand Up @@ -1209,6 +1214,7 @@ describe('Player', () => {
height: 400,
frameRate: 24,
pixelAspectRatio: '59:54',
hdr: null,
mimeType: 'video/mp4',
codecs: 'avc1.4d401f, mp4a.40.2',
audioCodec: 'mp4a.40.2',
Expand Down Expand Up @@ -1239,6 +1245,7 @@ describe('Player', () => {
height: 200,
frameRate: 1000000 / 42000,
pixelAspectRatio: '59:54',
hdr: null,
mimeType: 'video/mp4',
codecs: 'avc1.4d401f, mp4a.40.2',
audioCodec: 'mp4a.40.2',
Expand Down Expand Up @@ -1269,6 +1276,7 @@ describe('Player', () => {
height: 400,
frameRate: 24,
pixelAspectRatio: '59:54',
hdr: null,
mimeType: 'video/mp4',
codecs: 'avc1.4d401f, mp4a.40.2',
audioCodec: 'mp4a.40.2',
Expand Down Expand Up @@ -1314,6 +1322,7 @@ describe('Player', () => {
height: null,
frameRate: null,
pixelAspectRatio: null,
hdr: null,
videoId: null,
audioId: null,
originalAudioId: null,
Expand Down Expand Up @@ -1344,6 +1353,7 @@ describe('Player', () => {
height: null,
frameRate: null,
pixelAspectRatio: null,
hdr: null,
videoId: null,
audioId: null,
originalAudioId: null,
Expand Down Expand Up @@ -1374,6 +1384,7 @@ describe('Player', () => {
height: null,
frameRate: null,
pixelAspectRatio: null,
hdr: null,
videoId: null,
audioId: null,
originalAudioId: null,
Expand Down