Skip to content

Commit

Permalink
fix: Dispatch all emsg boxes, even if they are ID3 (#5428)
Browse files Browse the repository at this point in the history
The 'metadata' event was introduced in v4.3, and ID3 metadata from
'emsg' boxes dispatched through that event was no longer dispatched
through the more general 'emsg' events.

Now we will no longer exclude ID3 emsg boxes from the more general
'emsg' event. This restores compatibility with Shaka v4.2.
  • Loading branch information
joeyparrish authored Jul 21, 2023
1 parent bccfdbc commit 25ecfa7
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 20 deletions.
49 changes: 32 additions & 17 deletions lib/media/streaming_engine.js
Original file line number Diff line number Diff line change
Expand Up @@ -1982,7 +1982,6 @@ shaka.media.StreamingEngine = class {
const messageData = box.reader.readBytes(
box.reader.getLength() - box.reader.getPosition());


// See DASH sec. 5.10.3.3.1
// If a DASH client detects an event message box with a scheme that is not
// defined in MPD, the client is expected to ignore it.
Expand All @@ -1992,23 +1991,8 @@ shaka.media.StreamingEngine = class {
// A special scheme in DASH used to signal manifest updates.
if (schemeId == 'urn:mpeg:dash:event:2012') {
this.playerInterface_.onManifestUpdate();
} else if (schemeId == 'https://aomedia.org/emsg/ID3' ||
schemeId == 'https://developer.apple.com/streaming/emsg-id3') {
// See https://aomediacodec.github.io/id3-emsg/
const frames = shaka.util.Id3Utils.getID3Frames(messageData);
if (frames.length && reference) {
/** @private {shaka.extern.ID3Metadata} */
const metadata = {
cueTime: reference.startTime,
data: messageData,
frames: frames,
dts: reference.startTime,
pts: reference.startTime,
};
this.playerInterface_.onMetadata(
[metadata], /* offset= */ 0, reference.endTime);
}
} else {
// All other schemes are dispatched as a general 'emsg' event.
/** @type {shaka.extern.EmsgInfo} */
const emsg = {
startTime: startTime,
Expand All @@ -2026,7 +2010,38 @@ shaka.media.StreamingEngine = class {
const eventName = shaka.util.FakeEvent.EventName.Emsg;
const data = (new Map()).set('detail', emsg);
const event = new shaka.util.FakeEvent(eventName, data);
// A user can call preventDefault() on a cancelable event.
event.cancelable = true;

this.playerInterface_.onEvent(event);

if (event.defaultPrevented) {
// If the caller uses preventDefault() on the 'emsg' event, don't
// process any further, and don't generate an ID3 'metadata' event
// for the same data.
return;
}

// Additionally, ID3 events generate a 'metadata' event. This is a
// pre-parsed version of the metadata blob already dispatched in the
// 'emsg' event.
if (schemeId == 'https://aomedia.org/emsg/ID3' ||
schemeId == 'https://developer.apple.com/streaming/emsg-id3') {
// See https://aomediacodec.github.io/id3-emsg/
const frames = shaka.util.Id3Utils.getID3Frames(messageData);
if (frames.length && reference) {
/** @private {shaka.extern.ID3Metadata} */
const metadata = {
cueTime: reference.startTime,
data: messageData,
frames: frames,
dts: reference.startTime,
pts: reference.startTime,
};
this.playerInterface_.onMetadata(
[metadata], /* offset= */ 0, reference.endTime);
}
}
}
}
}
Expand Down
4 changes: 3 additions & 1 deletion lib/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,9 @@ goog.requireType('shaka.routing.Payload');

/**
* @event shaka.Player.EmsgEvent
* @description Fired when a non-typical emsg is found in a segment.
* @description Fired when an emsg box is found in a segment.
* If the application calls preventDefault() on this event, further parsing
* will not happen, and no 'metadata' event will be raised for ID3 payloads.
* @property {string} type
* 'emsg'
* @property {shaka.extern.EmsgInfo} detail
Expand Down
28 changes: 26 additions & 2 deletions test/media/streaming_engine_unit.js
Original file line number Diff line number Diff line change
Expand Up @@ -3130,21 +3130,45 @@ describe('StreamingEngine', () => {
expect(onManifestUpdate).toHaveBeenCalled();
});

it('triggers metadata event', async () => {
it('triggers both emsg event and metadata event for ID3', async () => {
setSegment0(emsgSegmentV0ID3);
videoStream.emsgSchemeIdUris = [id3SchemeUri];

onEvent.and.callFake((emsgEvent) => {
expect(emsgEvent.type).toBe('emsg');
});

// Here we go!
streamingEngine.switchVariant(variant);
streamingEngine.switchTextStream(textStream);
await streamingEngine.start();
playing = true;
await runTest();

expect(onEvent).not.toHaveBeenCalled();
expect(onEvent).toHaveBeenCalled();
expect(onMetadata).toHaveBeenCalled();
});

it('only triggers emsg event for ID3 if event canceled', async () => {
setSegment0(emsgSegmentV0ID3);
videoStream.emsgSchemeIdUris = [id3SchemeUri];

onEvent.and.callFake((emsgEvent) => {
expect(emsgEvent.type).toBe('emsg');
emsgEvent.preventDefault();
});

// Here we go!
streamingEngine.switchVariant(variant);
streamingEngine.switchTextStream(textStream);
await streamingEngine.start();
playing = true;
await runTest();

expect(onEvent).toHaveBeenCalled();
expect(onMetadata).not.toHaveBeenCalled();
});

it('event start matches presentation time', async () => {
// This box has a non-zero event time, which doesn't matter.
setSegment0(emsgSegmentV1NonZeroStart);
Expand Down

0 comments on commit 25ecfa7

Please sign in to comment.