Skip to content

Commit 264c842

Browse files
committed
fix(image): Fix HLS image track issues
This makes the HLS parser honor more attributes for image tracks. It also makes some changes to player.getImageTracks, so that the returned track shows the size of a single thumbnail rather than the entire sheet. Closes shaka-project#3840 Change-Id: I2ae096f455864201e08a85e29f0f02a3e06eb07f
1 parent 13a870f commit 264c842

File tree

6 files changed

+80
-9
lines changed

6 files changed

+80
-9
lines changed

lib/hls/hls_parser.js

+30
Original file line numberDiff line numberDiff line change
@@ -1281,6 +1281,30 @@ shaka.hls.HlsParser = class {
12811281
if (this.uriToStreamInfosMap_.has(verbatimImagePlaylistUri)) {
12821282
return this.uriToStreamInfosMap_.get(verbatimImagePlaylistUri);
12831283
}
1284+
1285+
// Parse misc attributes.
1286+
const resolution = tag.getAttributeValue('RESOLUTION');
1287+
if (resolution) {
1288+
// The RESOLUTION tag represents the resolution of a single thumbnail, not
1289+
// of the entire sheet at once (like we expect in the output).
1290+
// So multiply by the layout size.
1291+
1292+
const reference = streamInfo.stream.segmentIndex.get(0);
1293+
const layout = reference.getTilesLayout();
1294+
if (layout) {
1295+
streamInfo.stream.width =
1296+
Number(resolution.split('x')[0]) * Number(layout.split('x')[0]);
1297+
streamInfo.stream.height =
1298+
Number(resolution.split('x')[1]) * Number(layout.split('x')[1]);
1299+
// TODO: What happens if there are multiple grids, with different
1300+
// layout sizes, inside this image stream?
1301+
}
1302+
}
1303+
const bandwidth = tag.getAttributeValue('BANDWIDTH');
1304+
if (bandwidth) {
1305+
streamInfo.stream.bandwidth = Number(bandwidth);
1306+
}
1307+
12841308
this.uriToStreamInfosMap_.set(verbatimImagePlaylistUri, streamInfo);
12851309
return streamInfo;
12861310
}
@@ -1814,13 +1838,18 @@ shaka.hls.HlsParser = class {
18141838
}
18151839

18161840
let tilesLayout = '';
1841+
let tileDuration = null;
18171842
if (type == shaka.util.ManifestParserUtils.ContentType.IMAGE) {
18181843
// By default in HLS the tilesLayout is 1x1
18191844
tilesLayout = '1x1';
18201845
const tilesTag =
18211846
shaka.hls.Utils.getFirstTagWithName(tags, 'EXT-X-TILES');
18221847
if (tilesTag) {
18231848
tilesLayout = tilesTag.getRequiredAttrValue('LAYOUT');
1849+
const duration = tilesTag.getAttributeValue('DURATION');
1850+
if (duration) {
1851+
tileDuration = Number(duration);
1852+
}
18241853
}
18251854
}
18261855

@@ -1836,6 +1865,7 @@ shaka.hls.HlsParser = class {
18361865
/* appendWindowEnd= */ Infinity,
18371866
partialSegmentRefs,
18381867
tilesLayout,
1868+
tileDuration,
18391869
);
18401870
}
18411871

lib/media/segment_index.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,8 @@ shaka.media.SegmentIndex = class {
347347
lastReference.appendWindowStart,
348348
lastReference.appendWindowEnd,
349349
lastReference.partialReferences,
350-
lastReference.tilesLayout);
350+
lastReference.tilesLayout,
351+
lastReference.tileDuration);
351352
}
352353

353354

lib/media/segment_reference.js

+19-1
Original file line numberDiff line numberDiff line change
@@ -155,11 +155,15 @@ shaka.media.SegmentReference = class {
155155
* The value is a grid-item-dimension consisting of two positive decimal
156156
* integers in the format: column-x-row ('4x3'). It describes the
157157
* arrangement of Images in a Grid. The minimum valid LAYOUT is '1x1'.
158+
* @param {?number=} tileDuration
159+
* The explicit duration of an individual tile within the tiles grid.
160+
* If not provided, the duration should be automatically calculated based on
161+
* the duration of the reference.
158162
*/
159163
constructor(
160164
startTime, endTime, uris, startByte, endByte, initSegmentReference,
161165
timestampOffset, appendWindowStart, appendWindowEnd,
162-
partialReferences = [], tilesLayout = '') {
166+
partialReferences = [], tilesLayout = '', tileDuration = null) {
163167
// A preload hinted Partial Segment has the same startTime and endTime.
164168
goog.asserts.assert(startTime <= endTime,
165169
'startTime must be less than or equal to endTime');
@@ -206,6 +210,9 @@ shaka.media.SegmentReference = class {
206210

207211
/** @type {?string} */
208212
this.tilesLayout = tilesLayout;
213+
214+
/** @type {?number} */
215+
this.tileDuration = tileDuration;
209216
}
210217

211218
/**
@@ -290,6 +297,17 @@ shaka.media.SegmentReference = class {
290297
getTilesLayout() {
291298
return this.tilesLayout;
292299
}
300+
301+
/**
302+
* Returns the segment's explicit tile duration.
303+
* Only defined in image segments.
304+
*
305+
* @return {?number}
306+
* @export
307+
*/
308+
getTileDuration() {
309+
return this.tileDuration;
310+
}
293311
};
294312

295313

lib/player.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -3613,7 +3613,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
36133613
const height = fullImageHeight / rows;
36143614
const totalImages = columns * rows;
36153615
const segmentDuration = reference.trueEndTime - reference.startTime;
3616-
const thumbnailDuration = segmentDuration / totalImages;
3616+
const thumbnailDuration =
3617+
reference.getTileDuration() || (segmentDuration / totalImages);
36173618
let thumbnailTime = reference.startTime;
36183619
let positionX = 0;
36193620
let positionY = 0;

lib/util/stream_utils.js

+21-2
Original file line numberDiff line numberDiff line change
@@ -989,6 +989,25 @@ shaka.util.StreamUtils = class {
989989
static imageStreamToTrack(stream) {
990990
const ContentType = shaka.util.ManifestParserUtils.ContentType;
991991

992+
let width = stream.width || null;
993+
let height = stream.height || null;
994+
995+
// The stream width and height represent the size of the entire thumbnail
996+
// sheet, so divide by the layout.
997+
const reference = stream.segmentIndex.get(0);
998+
let layout = stream.tilesLayout;
999+
if (reference) {
1000+
layout = reference.getTilesLayout() || layout;
1001+
}
1002+
if (layout && width != null) {
1003+
width /= Number(layout.split('x')[0]);
1004+
}
1005+
if (layout && height != null) {
1006+
height /= Number(layout.split('x')[1]);
1007+
}
1008+
// TODO: What happens if there are multiple grids, with different
1009+
// layout sizes, inside this image stream?
1010+
9921011
/** @type {shaka.extern.Track} */
9931012
const track = {
9941013
id: stream.id,
@@ -998,8 +1017,8 @@ shaka.util.StreamUtils = class {
9981017
language: '',
9991018
label: null,
10001019
kind: null,
1001-
width: stream.width || null,
1002-
height: stream.height || null,
1020+
width,
1021+
height,
10031022
frameRate: null,
10041023
pixelAspectRatio: null,
10051024
hdr: null,

test/player_unit.js

+6-4
Original file line numberDiff line numberDiff line change
@@ -1092,8 +1092,8 @@ describe('Player', () => {
10921092
// Image tracks
10931093
manifest.addImageStream(53, (stream) => {
10941094
stream.originalId = 'thumbnail';
1095-
stream.width = 100;
1096-
stream.height = 200;
1095+
stream.width = 200;
1096+
stream.height = 400;
10971097
stream.bandwidth = 10;
10981098
stream.mimeType = 'image/jpeg';
10991099
stream.tilesLayout = '1x1';
@@ -1503,8 +1503,8 @@ describe('Player', () => {
15031503
audioBandwidth: null,
15041504
videoBandwidth: null,
15051505
bandwidth: 10,
1506-
width: 100,
1507-
height: 200,
1506+
width: 200,
1507+
height: 400,
15081508
frameRate: null,
15091509
pixelAspectRatio: null,
15101510
hdr: null,
@@ -3537,6 +3537,8 @@ describe('Player', () => {
35373537

35383538
await player.load(fakeManifestUri, 0, fakeMimeType);
35393539

3540+
expect(player.getImageTracks()[0].width).toBe(100);
3541+
expect(player.getImageTracks()[0].height).toBe(50);
35403542
const thumbnail0 = await player.getThumbnails(5, 0);
35413543
const thumbnail1 = await player.getThumbnails(5, 11);
35423544
const thumbnail2 = await player.getThumbnails(5, 21);

0 commit comments

Comments
 (0)