fix: Handle ID3 EMSG duration according to AOM spec (#9757)

Fixes https://github.com/shaka-project/shaka-player/issues/9753
This commit is contained in:
Álvaro Velad Galván
2026-02-24 16:37:07 +01:00
committed by GitHub
parent 5f390393a4
commit 7a32fa2e4d
2 changed files with 71 additions and 1 deletions
+7 -1
View File
@@ -1042,7 +1042,13 @@ shaka.media.MediaSourceEngine = class {
} else {
// All other schemes are dispatched as a general 'emsg' event.
const endTime = startTime + (eventDuration / timescale);
let endTime = startTime;
// See: https://aomediacodec.github.io/id3-emsg/
// ID3 EMSG events do not carry a duration
if (schemeId !== 'https://aomedia.org/emsg/ID3' ||
eventDuration !== 0xFFFFFFFF) {
endTime += eventDuration / timescale;
}
/** @type {shaka.extern.EmsgInfo} */
const emsg = {
startTime: startTime,
@@ -801,6 +801,35 @@ describe('MediaSourceEngine', () => {
'53 68 61 6b 61 33 44 49 03 00 40 00 00 00 1b'
).replace(/\s/g, ''));
const emsgSegmentV0ID3WithoutDuration = Uint8ArrayUtils.fromHex((
// 105 bytes emsg box v0, flags 0
'00 00 00 69 65 6d 73 67 00 00 00 00' +
// scheme id uri (13 bytes) 'https://aomedia.org/emsg/ID3'
'68 74 74 70 73 3a 2f 2f 61 6f 6d 65 64 69 61 2e' +
'6f 72 67 2f 65 6d 73 67 2f 49 44 33 00' +
// value (1 byte) ''
'00' +
// timescale (4 bytes) 1
'00 00 00 01' +
// presentation time delta (4 bytes) 8
'00 00 00 08' +
// event duration (4 bytes) 255
'ff ff ff ff' +
// id (4 bytes) 51
'00 00 00 33' +
// message data (47 bytes)
'49 44 33 03 00 40 00 00 00 1b 00 00 00 06 00 00' +
'00 00 00 02 54 58 58 58 00 00 00 07 e0 00 03 00' +
'53 68 61 6b 61 33 44 49 03 00 40 00 00 00 1b'
).replace(/\s/g, ''));
const id3SchemeUri = 'https://aomedia.org/emsg/ID3';
const emsgObj = {
@@ -839,6 +868,18 @@ describe('MediaSourceEngine', () => {
messageData: new Uint8Array([0x74, 0x65, 0x73, 0x74]),
};
const emsgObjSegmentV0ID3WithoutDuration = {
startTime: 8,
endTime: 8,
schemeIdUri: 'https://aomedia.org/emsg/ID3',
value: '',
timescale: 1,
presentationTimeDelta: 8,
eventDuration: 4294967295,
id: 51,
messageData: jasmine.any(Uint8Array),
};
const initSegmentReference = new shaka.media.InitSegmentReference(
/* uris= */ () => [],
/* startByte= */ 0,
@@ -989,6 +1030,29 @@ describe('MediaSourceEngine', () => {
expect(onMetadata).toHaveBeenCalled();
});
// eslint-disable-next-line @stylistic/max-len
it('triggers both emsg event and metadata event for ID3 without duration', () => {
const videoStream =
shaka.test.StreamingEngineUtil.createMockVideoStream(1);
videoStream.emsgSchemeIdUris = [id3SchemeUri];
onEvent.and.callFake((emsgEvent) => {
expect(emsgEvent.type).toBe('emsg');
});
mediaSourceEngine.getTimestampAndDispatchMetadata(
ContentType.VIDEO,
emsgSegmentV0ID3WithoutDuration,
reference,
videoStream,
/* mimeType= */ 'video/mp4');
expect(onEmsg).toHaveBeenCalledTimes(1);
const emsgInfo = onEmsg.calls.argsFor(0)[0];
expect(emsgInfo).toEqual(emsgObjSegmentV0ID3WithoutDuration);
expect(onMetadata).toHaveBeenCalled();
});
it('event start matches presentation time', () => {
const videoStream =
shaka.test.StreamingEngineUtil.createMockVideoStream(1);