Skip to content

Commit 39dc91e

Browse files
committed
Implemented on_receive mode for event registration
1 parent d79b767 commit 39dc91e

File tree

9 files changed

+189
-28
lines changed

9 files changed

+189
-28
lines changed

samples/advanced/listening-to-SCTE-EMSG-events.html

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,35 +13,46 @@
1313
var player;
1414
const URL = "https://livesim.dashif.org/livesim/scte35_2/testpic_2s/Manifest.mpd";
1515
const SCHEMEIDURI = "urn:scte:scte35:2013:xml";
16+
const EVENT_MODE_ON_START = dashjs.MediaPlayer.events.EVENT_MODE_ON_START;
17+
const EVENT_MODE_ON_RECEIVE = dashjs.MediaPlayer.events.EVENT_MODE_ON_RECEIVE;
1618

1719
function init() {
1820
player = dashjs.MediaPlayer().create();
1921
player.updateSettings({ 'debug': { 'logLevel': dashjs.Debug.LOG_LEVEL_NONE }});
20-
player.on(SCHEMEIDURI, showEvent);
22+
player.on(SCHEMEIDURI, showStartEvent, null, { mode: EVENT_MODE_ON_START });
23+
player.on(SCHEMEIDURI, showReceiveEvent, null, { mode: EVENT_MODE_ON_RECEIVE });
2124
player.initialize(document.querySelector("video"), URL, true);
2225
}
2326

24-
function showEvent(e) {
25-
log("EVENT RECEIVED: " + e.type);
27+
function showStartEvent(e) {
28+
showEvent(e, "start");
29+
}
30+
31+
function showReceiveEvent(e) {
32+
showEvent(e, "receive");
33+
}
34+
35+
function showEvent(e, output) {
2636
/* We double process in order to pretty-print. Only two levels of object properties are exposed. */
2737
for (var name in e) {
2838
if (typeof e[name] != 'object') {
29-
log(" " + name + ": " + e[name]);
39+
log(" " + name + ": " + e[name], output);
3040
}
3141
}
3242
for (name in e) {
3343
if (typeof e[name] == 'object' ) {
34-
log(" " + name + ":");
44+
log(" " + name + ":", output);
3545
for (name2 in e[name]) {
36-
log(" " + name2 + ": " + JSON.stringify(e[name][name2]));
46+
log(" " + name2 + ": " + JSON.stringify(e[name][name2]), output);
3747
}
3848
}
3949
}
50+
log("", output);
4051
}
4152

42-
function log(msg) {
53+
function log(msg, output) {
4354
msg = msg.length > 200 ? msg.substring(0, 200) + "..." : msg; /* to avoid repeated wrapping with large objects */
44-
var tracePanel = document.getElementById("trace");
55+
var tracePanel = document.getElementById(output);
4556
tracePanel.innerHTML += msg + "\n";
4657
tracePanel.scrollTop = tracePanel.scrollHeight;
4758
console.log(msg);
@@ -68,7 +79,16 @@
6879
<div>
6980
<video controls="true">
7081
</video>
71-
<textarea id="trace" placeholder="Trapped events will be displayed here"></textarea>
82+
</div>
83+
<div style="display:flex">
84+
<div>
85+
<p>Received Events</p>
86+
<textarea id="receive" placeholder="Trapped on_receive events will be displayed here"></textarea>
87+
</div>
88+
<div>
89+
<p>Started Events</p>
90+
<textarea id="start" placeholder="Trapped on_start events will be displayed here"></textarea>
91+
</div>
7292
</div>
7393
<script>
7494
document.addEventListener("DOMContentLoaded", function () {

src/core/EventBus.js

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
* POSSIBILITY OF SUCH DAMAGE.
3030
*/
3131
import FactoryMaker from './FactoryMaker';
32+
import { EVENT_MODE_ON_START, EVENT_MODE_ON_RECEIVE } from '../streaming/MediaPlayerEvents';
3233

3334
const EVENT_PRIORITY_LOW = 0;
3435
const EVENT_PRIORITY_HIGH = 5000;
@@ -37,7 +38,7 @@ function EventBus() {
3738

3839
let handlers = {};
3940

40-
function on(type, listener, scope, priority = EVENT_PRIORITY_LOW) {
41+
function on(type, listener, scope, options = {}) {
4142

4243
if (!type) {
4344
throw new Error('event type cannot be null or undefined');
@@ -46,14 +47,18 @@ function EventBus() {
4647
throw new Error('listener must be a function: ' + listener);
4748
}
4849

50+
let priority = options.priority || EVENT_PRIORITY_LOW;
51+
let mode = options.mode || EVENT_MODE_ON_START;
52+
4953
if (getHandlerIdx(type, listener, scope) >= 0) return;
5054

5155
handlers[type] = handlers[type] || [];
5256

5357
const handler = {
5458
callback: listener,
5559
scope: scope,
56-
priority: priority
60+
priority: priority,
61+
mode
5762
};
5863

5964
if (scope && scope.getStreamId) {
@@ -85,20 +90,23 @@ function EventBus() {
8590
function trigger(type, payload = {}, filters = {}) {
8691
if (!type || !handlers[type]) return;
8792

93+
let isEventStart = true;
94+
if (typeof filters.isEventStart === 'boolean') isEventStart = filters.isEventStart;
95+
8896
payload = payload || {};
8997

9098
if (payload.hasOwnProperty('type')) throw new Error('\'type\' is a reserved word for event dispatching');
9199

92100
payload.type = type;
93101

94-
handlers[type] = handlers[type].filter((item) => item);
95-
const eventHandlers = handlers[type].filter(item => {
96-
if (filters.streamId && item.streamId && item.streamId !== filters.streamId) return false;
97-
if (filters.mediaType && item.mediaType && item.mediaType !== filters.mediaType) return false;
98-
return true;
99-
});
102+
const mode = isEventStart ? EVENT_MODE_ON_START : EVENT_MODE_ON_RECEIVE;
100103

101-
eventHandlers.forEach(handler => handler && handler.callback.call(handler.scope, payload));
104+
handlers[type]
105+
.filter((item) => item)
106+
.filter(item => !(filters.streamId && item.streamId && item.streamId !== filters.streamId))
107+
.filter(item => !(filters.mediaType && item.mediaType && item.mediaType !== filters.mediaType))
108+
.filter(handler => handler.mode === mode)
109+
.forEach(handler => handler && handler.callback.call(handler.scope, payload));
102110
}
103111

104112
function getHandlerIdx(type, listener, scope) {

src/streaming/MediaPlayer.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -406,11 +406,12 @@ function MediaPlayer() {
406406
* @param {string} type - {@link MediaPlayerEvents}
407407
* @param {Function} listener - callback method when the event fires.
408408
* @param {Object} scope - context of the listener so it can be removed properly.
409+
* @param {Object} options - object to define various options such as scope, priority and mode
409410
* @memberof module:MediaPlayer
410411
* @instance
411412
*/
412-
function on(type, listener, scope) {
413-
eventBus.on(type, listener, scope);
413+
function on(type, listener, scope, options) {
414+
eventBus.on(type, listener, scope, options);
414415
}
415416

416417
/**
@@ -1736,7 +1737,7 @@ function MediaPlayer() {
17361737
manifestLoader.reset();
17371738
};
17381739

1739-
eventBus.on(Events.INTERNAL_MANIFEST_LOADED, handler, self);
1740+
eventBus.on(Events.INTERNAL_MANIFEST_LOADED, handler, { scope: self });
17401741

17411742
uriFragmentModel.initialize(url);
17421743
manifestLoader.load(url);

src/streaming/MediaPlayerEvents.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,18 @@ class MediaPlayerEvents extends EventsBase {
354354
* @event MediaPlayerEvents#GAP_CAUSED_SEEK_TO_PERIOD_END
355355
*/
356356
this.GAP_CAUSED_SEEK_TO_PERIOD_END = 'gapCausedSeekToPeriodEnd';
357+
358+
/**
359+
* Dash events are triggered at their respective start points on the timeline.
360+
* @event MediaPlayerEvents#EVENT_MODE_ON_START
361+
*/
362+
this.EVENT_MODE_ON_START = 'onStart';
363+
364+
/**
365+
* Dash events are triggered as soon as they were parsed.
366+
* @event MediaPlayerEvents#EVENT_MODE_ON_RECEIVE
367+
*/
368+
this.EVENT_MODE_ON_RECEIVE = 'onReceive';
357369
}
358370
}
359371

src/streaming/controllers/EventController.js

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ function EventController() {
121121
let event = values[i];
122122
inlineEvents[event.id] = event;
123123
logger.debug('Add inline event with id ' + event.id);
124+
_startEvent(event.id, event, values);
124125
}
125126
}
126127
logger.debug(`Added ${values.length} inline events`);
@@ -145,6 +146,7 @@ function EventController() {
145146
}
146147
inbandEvents[event.id] = event;
147148
logger.debug('Add inband event with id ' + event.id);
149+
_startEvent(event.id, event, values);
148150
} else {
149151
logger.debug('Repeated event with id ' + event.id);
150152
}
@@ -207,6 +209,7 @@ function EventController() {
207209
function _onEventTimer() {
208210
try {
209211
if (!eventHandlingInProgress) {
212+
eventHandlingInProgress = true;
210213
const currentVideoTime = playbackController.getTime();
211214
let presentationTimeThreshold = (currentVideoTime - lastEventTimerCall);
212215

@@ -218,8 +221,8 @@ function EventController() {
218221
_removeEvents();
219222

220223
lastEventTimerCall = currentVideoTime;
224+
eventHandlingInProgress = false;
221225
}
222-
eventHandlingInProgress = false;
223226
} catch (e) {
224227
eventHandlingInProgress = false;
225228
}
@@ -310,8 +313,10 @@ function EventController() {
310313
function _startEvent(eventId, event, events) {
311314
try {
312315
const currentVideoTime = playbackController.getTime();
316+
const calculatedPresentationTimeInSeconds = event.calculatedPresentationTime / event.eventStream.timescale;
317+
const isEventStart = Math.floor(currentVideoTime) === calculatedPresentationTimeInSeconds;
313318

314-
if (event.duration > 0) {
319+
if (isEventStart && event.duration > 0) {
315320
activeEvents[eventId] = event;
316321
}
317322

@@ -325,10 +330,10 @@ function EventController() {
325330
_sendCallbackRequest(event.messageData);
326331
} else {
327332
logger.debug(`Starting event ${eventId} at ${currentVideoTime}`);
328-
eventBus.trigger(event.eventStream.schemeIdUri, { event: event });
333+
eventBus.trigger(event.eventStream.schemeIdUri, { event: event }, { isEventStart });
329334
}
330335

331-
delete events[eventId];
336+
if (isEventStart) delete events[eventId];
332337
} catch (e) {
333338
}
334339
}

src/streaming/controllers/PlaybackController.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ function PlaybackController() {
9393
eventBus.on(Events.BUFFER_LEVEL_STATE_CHANGED, onBufferLevelStateChanged, this);
9494
eventBus.on(Events.PLAYBACK_PROGRESS, onPlaybackProgression, this);
9595
eventBus.on(Events.PLAYBACK_TIME_UPDATED, onPlaybackProgression, this);
96-
eventBus.on(Events.PLAYBACK_ENDED, onPlaybackEnded, this, EventBus.EVENT_PRIORITY_HIGH);
96+
eventBus.on(Events.PLAYBACK_ENDED, onPlaybackEnded, this, { priority: EventBus.EVENT_PRIORITY_HIGH });
9797
eventBus.on(Events.STREAM_INITIALIZING, onStreamInitializing, this);
9898

9999
if (playOnceInitialized) {

test/unit/core.EventBus.js

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import EventBus from '../../src/core/EventBus';
2+
import { EVENT_MODE_ON_START, EVENT_MODE_ON_RECEIVE } from '../../src/streaming/MediaPlayerEvents';
23

34
const chai = require('chai');
45
const sinon = require('sinon');
@@ -31,12 +32,27 @@ describe('EventBus', function () {
3132
const spy2 = sinon.spy();
3233

3334
eventBus.on('EVENT_TEST', spy);
34-
eventBus.on('EVENT_TEST', spy2, this, EventBus.EVENT_PRIORITY_HIGH);
35+
eventBus.on('EVENT_TEST', spy2, this, { priority: EventBus.EVENT_PRIORITY_HIGH });
3536

3637
eventBus.trigger('EVENT_TEST', {});
3738

3839
assert.equal(spy.calledOnce, true);
3940
assert.equal(spy2.calledOnce, true);
4041
assert.equal(spy2.calledBefore(spy), true);
4142
});
43+
44+
it('should respect mode parameter in the on function in order to trigger events according to isEventStart option in trigger function', function () {
45+
const spy = sinon.spy();
46+
const spy2 = sinon.spy();
47+
48+
eventBus.on('EVENT_TEST', spy, null, { mode: EVENT_MODE_ON_START });
49+
eventBus.on('EVENT_TEST', spy2, null, { mode: EVENT_MODE_ON_RECEIVE });
50+
51+
eventBus.trigger('EVENT_TEST', {}, { isEventStart: false });
52+
eventBus.trigger('EVENT_TEST', {}, { isEventStart: true });
53+
54+
assert.equal(spy.calledOnce, true);
55+
assert.equal(spy2.calledOnce, true);
56+
assert.equal(spy2.calledBefore(spy), true);
57+
});
4258
});

test/unit/mocks/PlaybackControllerMock.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ class PlaybackControllerMock {
1111
this.playing = false;
1212
this.seeking = false;
1313
this.isDynamic = false;
14+
this.time = 0;
1415
this.streamController = new StreamControllerMock();
1516
}
1617

@@ -60,7 +61,11 @@ class PlaybackControllerMock {
6061
}
6162

6263
getTime() {
63-
return null;
64+
return this.time;
65+
}
66+
67+
setTime(time) {
68+
this.time = time;
6469
}
6570

6671
getNormalizedTime() {

0 commit comments

Comments
 (0)