Skip to content

Commit fc3afeb

Browse files
authored
fix: Fix v1 emsg box start/end times (shaka-project#3198)
The start/end times of emsg event must be in presentation time units. Fix the bug where times are incorrect when the timestamps do not start at 0. For emsg v1 the entries in the box are in media time units. Convert them back to presentation time units by subtracing the offset before raising the event.
1 parent d882d28 commit fc3afeb

File tree

4 files changed

+103
-13
lines changed

4 files changed

+103
-13
lines changed

lib/media/streaming_engine.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1613,7 +1613,7 @@ shaka.media.StreamingEngine = class {
16131613
} else {
16141614
timescale = box.reader.readUint32();
16151615
const pts = box.reader.readUint64();
1616-
startTime = pts / timescale;
1616+
startTime = (pts / timescale) + reference.timestampOffset;
16171617
presentationTimeDelta = startTime - reference.startTime;
16181618
eventDuration = box.reader.readUint32();
16191619
id = box.reader.readUint32();

test/media/streaming_engine_unit.js

+91-8
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,10 @@ describe('StreamingEngine', () => {
120120
jasmine.clock().mockDate();
121121
});
122122

123-
/** @param {boolean=} trickMode */
124-
function setupVod(trickMode) {
123+
/** @param {boolean=} trickMode
124+
* @param {number=} mediaOffset The offset from 0 for the segment start times
125+
*/
126+
function setupVod(trickMode, mediaOffset) {
125127
// For VOD, we fake a presentation that has 2 Periods of equal duration
126128
// (20 seconds), where each Period has 1 Variant and 1 text stream.
127129
//
@@ -132,6 +134,11 @@ describe('StreamingEngine', () => {
132134
// first Period, and 2 audio, 2 video, and 2 text for the second Period.
133135
// All media segments are (by default) 10 seconds long.
134136

137+
const offset = mediaOffset || 0;
138+
// timestampOffset is -ve since it is added to bring the timeline to 0.
139+
// -0 and 0 are not same so explicitly set to 0.
140+
const timestampOffset = offset === 0 ? 0 : -offset;
141+
135142
// Create SegmentData map for FakeMediaSourceEngine.
136143
const initSegmentSizeAudio = initSegmentRanges[ContentType.AUDIO][1] -
137144
initSegmentRanges[ContentType.AUDIO][0] + 1;
@@ -151,8 +158,9 @@ describe('StreamingEngine', () => {
151158
makeBuffer(segmentSizes[ContentType.AUDIO]),
152159
makeBuffer(segmentSizes[ContentType.AUDIO]),
153160
],
154-
segmentStartTimes: [0, 10, 20, 30],
161+
segmentStartTimes: [offset, offset+10, offset+20, offset+30],
155162
segmentDuration: 10,
163+
timestampOffset: timestampOffset,
156164
},
157165
video: {
158166
initSegments: [
@@ -165,8 +173,9 @@ describe('StreamingEngine', () => {
165173
makeBuffer(segmentSizes[ContentType.VIDEO]),
166174
makeBuffer(segmentSizes[ContentType.VIDEO]),
167175
],
168-
segmentStartTimes: [0, 10, 20, 30],
176+
segmentStartTimes: [offset, offset+10, offset+20, offset+30],
169177
segmentDuration: 10,
178+
timestampOffset: timestampOffset,
170179
},
171180
text: {
172181
initSegments: [],
@@ -176,8 +185,9 @@ describe('StreamingEngine', () => {
176185
makeBuffer(segmentSizes[ContentType.TEXT]),
177186
makeBuffer(segmentSizes[ContentType.TEXT]),
178187
],
179-
segmentStartTimes: [0, 10, 20, 30],
188+
segmentStartTimes: [offset, offset+10, offset+20, offset+30],
180189
segmentDuration: 10,
190+
timestampOffset: timestampOffset,
181191
},
182192
};
183193
if (trickMode) {
@@ -192,8 +202,9 @@ describe('StreamingEngine', () => {
192202
makeBuffer(segmentSizes[ContentType.VIDEO]),
193203
makeBuffer(segmentSizes[ContentType.VIDEO]),
194204
],
195-
segmentStartTimes: [0, 10, 20, 30],
205+
segmentStartTimes: [offset, offset+10, offset+20, offset+30],
196206
segmentDuration: 10,
207+
timestampOffset: timestampOffset,
197208
};
198209
}
199210

@@ -372,14 +383,24 @@ describe('StreamingEngine', () => {
372383
video: segmentData[ContentType.VIDEO].segmentDuration,
373384
text: segmentData[ContentType.TEXT].segmentDuration,
374385
};
386+
387+
const timestampOffsets = {
388+
audio: segmentData[ContentType.AUDIO].timestampOffset,
389+
video: segmentData[ContentType.VIDEO].timestampOffset,
390+
text: segmentData[ContentType.TEXT].timestampOffset,
391+
};
392+
375393
if (segmentData['trickvideo']) {
376394
segmentDurations['trickvideo'] =
377395
segmentData['trickvideo'].segmentDuration;
396+
timestampOffsets['trickvideo'] =
397+
segmentData['trickvideo'].timestampOffset;
378398
}
379399
manifest = shaka.test.StreamingEngineUtil.createManifest(
380400
/** @type {!shaka.media.PresentationTimeline} */(timeline),
381401
[firstPeriodStartTime, secondPeriodStartTime],
382-
presentationDuration, segmentDurations, initSegmentRanges);
402+
presentationDuration, segmentDurations, initSegmentRanges,
403+
timestampOffsets);
383404

384405
audioStream = manifest.variants[0].audio;
385406
videoStream = manifest.variants[0].video;
@@ -2616,7 +2637,7 @@ describe('StreamingEngine', () => {
26162637
expect(event.detail).toEqual(emsgObj);
26172638
});
26182639

2619-
it('raises an event for registered embedded v1 emsg boxes ', async () => {
2640+
it('raises an event for registered embedded v1 emsg boxes', async () => {
26202641
// same event but verison 1.
26212642
const v1EmsgSegment = Uint8ArrayUtils.fromHex(
26222643
'0000003f656d7367010000000000000100000000000000080000ffff00000001' +
@@ -2717,6 +2738,68 @@ describe('StreamingEngine', () => {
27172738
});
27182739
});
27192740

2741+
describe('embedded emsg boxes with non zero timestamps', () => {
2742+
const emsgSegment = Uint8ArrayUtils.fromHex(
2743+
'0000003b656d736700000000666f6f3a6261723a637573746f6d646174617363' +
2744+
'68656d6500310000000001000000080000ffff0000000174657374');
2745+
const emsgObj = {
2746+
startTime: 8,
2747+
endTime: 0xffff + 8,
2748+
schemeIdUri: 'foo:bar:customdatascheme',
2749+
value: '1',
2750+
timescale: 1,
2751+
presentationTimeDelta: 8,
2752+
eventDuration: 0xffff,
2753+
id: 1,
2754+
messageData: new Uint8Array([0x74, 0x65, 0x73, 0x74]),
2755+
};
2756+
2757+
beforeEach(() => {
2758+
// setup an offset for the timestamps.
2759+
setupVod(false, 10);
2760+
mediaSourceEngine = new shaka.test.FakeMediaSourceEngine(segmentData);
2761+
createStreamingEngine();
2762+
});
2763+
2764+
it('event start matches presentation times for emsg boxes', async () => {
2765+
segmentData[ContentType.VIDEO].segments[0] = emsgSegment;
2766+
videoStream.emsgSchemeIdUris = [emsgObj.schemeIdUri];
2767+
2768+
// Here we go!
2769+
streamingEngine.switchVariant(variant);
2770+
streamingEngine.switchTextStream(textStream);
2771+
await streamingEngine.start();
2772+
playing = true;
2773+
await runTest();
2774+
2775+
expect(onEvent).toHaveBeenCalledTimes(1);
2776+
2777+
const event = onEvent.calls.argsFor(0)[0];
2778+
expect(event.detail).toEqual(emsgObj);
2779+
});
2780+
2781+
it('event start matches presentation time for v1 emsg boxes', async () => {
2782+
// same event but verison 1. start time is 18.
2783+
const v1EmsgSegment = Uint8ArrayUtils.fromHex(
2784+
'0000003f656d7367010000000000000100000000000000120000ffff00000001' +
2785+
'666f6f3a6261723a637573746f6d64617461736368656d6500310074657374');
2786+
segmentData[ContentType.VIDEO].segments[0] = v1EmsgSegment;
2787+
videoStream.emsgSchemeIdUris = [emsgObj.schemeIdUri];
2788+
2789+
// Here we go!
2790+
streamingEngine.switchVariant(variant);
2791+
streamingEngine.switchTextStream(textStream);
2792+
await streamingEngine.start();
2793+
playing = true;
2794+
await runTest();
2795+
2796+
expect(onEvent).toHaveBeenCalledTimes(1);
2797+
2798+
const event = onEvent.calls.argsFor(0)[0];
2799+
expect(event.detail).toEqual(emsgObj);
2800+
});
2801+
});
2802+
27202803
describe('network downgrading', () => {
27212804
/** @type {shaka.extern.Variant} */
27222805
let initialVariant;

test/test/util/fake_media_source_engine.js

+6-2
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ shaka.test.FakeMediaSourceEngine = class {
5252
this.initSegments[type] = data.initSegments.map(() => false);
5353
this.segments[type] = data.segments.map(() => false);
5454

55-
this.timestampOffsets_[type] = 0;
55+
this.timestampOffsets_[type] = data.timestampOffset || 0;
5656
}
5757

5858
/** @type {!jasmine.Spy} */
@@ -416,7 +416,8 @@ shaka.test.FakeMediaSourceEngine = class {
416416
* initSegments: !Array.<!BufferSource>,
417417
* segments: !Array.<!BufferSource>,
418418
* segmentStartTimes: !Array.<number>,
419-
* segmentDuration: number
419+
* segmentDuration: number,
420+
* timestampOffset: number,
420421
* }}
421422
*
422423
* @property {!Array.<!BufferSource>} initSegments
@@ -429,5 +430,8 @@ shaka.test.FakeMediaSourceEngine = class {
429430
* baseMediaDecodeTime (or equivalent) values.
430431
* @property {number} segmentDuration
431432
* The duration of each media segment.
433+
* @property {number=} timestampOffset
434+
* The offset to the segment start times that is added to create
435+
* the media timeline.
432436
*/
433437
shaka.test.FakeMediaSourceEngine.SegmentData;

test/test/util/streaming_engine_util.js

+5-2
Original file line numberDiff line numberDiff line change
@@ -162,11 +162,13 @@ shaka.test.StreamingEngineUtil = class {
162162
* type of segment.
163163
* @param {!Object.<string, !Array.<number>>} initSegmentRanges The byte
164164
* ranges for each type of init segment.
165+
* @param {!Object.<string,number>=} timestampOffsets The timestamp offset
166+
* for each type of segment
165167
* @return {shaka.extern.Manifest}
166168
*/
167169
static createManifest(
168170
presentationTimeline, periodStartTimes, presentationDuration,
169-
segmentDurations, initSegmentRanges) {
171+
segmentDurations, initSegmentRanges, timestampOffsets) {
170172
const Util = shaka.test.Util;
171173

172174
/**
@@ -263,6 +265,7 @@ shaka.test.StreamingEngineUtil = class {
263265
const d = segmentDurations[type];
264266
const getUris = () => [periodIndex + '_' + type + '_' + position];
265267
const periodStart = periodStartTimes[periodIndex];
268+
const timestampOffset = (timestampOffsets && timestampOffsets[type]) || 0;
266269
const appendWindowStart = periodStartTimes[periodIndex];
267270
const appendWindowEnd = periodIndex == periodStartTimes.length - 1?
268271
presentationDuration : periodStartTimes[periodIndex + 1];
@@ -274,7 +277,7 @@ shaka.test.StreamingEngineUtil = class {
274277
/* startByte= */ 0,
275278
/* endByte= */ null,
276279
initSegmentReference,
277-
/* timestampOffset= */ 0,
280+
timestampOffset,
278281
appendWindowStart,
279282
appendWindowEnd);
280283
};

0 commit comments

Comments
 (0)