Skip to content

Commit

Permalink
Implemented on_receive mode for event registration
Browse files Browse the repository at this point in the history
  • Loading branch information
FritzHeiden committed Nov 9, 2020
1 parent d79b767 commit 39dc91e
Show file tree
Hide file tree
Showing 9 changed files with 189 additions and 28 deletions.
38 changes: 29 additions & 9 deletions samples/advanced/listening-to-SCTE-EMSG-events.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,35 +13,46 @@
var player;
const URL = "https://livesim.dashif.org/livesim/scte35_2/testpic_2s/Manifest.mpd";
const SCHEMEIDURI = "urn:scte:scte35:2013:xml";
const EVENT_MODE_ON_START = dashjs.MediaPlayer.events.EVENT_MODE_ON_START;
const EVENT_MODE_ON_RECEIVE = dashjs.MediaPlayer.events.EVENT_MODE_ON_RECEIVE;

function init() {
player = dashjs.MediaPlayer().create();
player.updateSettings({ 'debug': { 'logLevel': dashjs.Debug.LOG_LEVEL_NONE }});
player.on(SCHEMEIDURI, showEvent);
player.on(SCHEMEIDURI, showStartEvent, null, { mode: EVENT_MODE_ON_START });
player.on(SCHEMEIDURI, showReceiveEvent, null, { mode: EVENT_MODE_ON_RECEIVE });
player.initialize(document.querySelector("video"), URL, true);
}

function showEvent(e) {
log("EVENT RECEIVED: " + e.type);
function showStartEvent(e) {
showEvent(e, "start");
}

function showReceiveEvent(e) {
showEvent(e, "receive");
}

function showEvent(e, output) {
/* We double process in order to pretty-print. Only two levels of object properties are exposed. */
for (var name in e) {
if (typeof e[name] != 'object') {
log(" " + name + ": " + e[name]);
log(" " + name + ": " + e[name], output);
}
}
for (name in e) {
if (typeof e[name] == 'object' ) {
log(" " + name + ":");
log(" " + name + ":", output);
for (name2 in e[name]) {
log(" " + name2 + ": " + JSON.stringify(e[name][name2]));
log(" " + name2 + ": " + JSON.stringify(e[name][name2]), output);
}
}
}
log("", output);
}

function log(msg) {
function log(msg, output) {
msg = msg.length > 200 ? msg.substring(0, 200) + "..." : msg; /* to avoid repeated wrapping with large objects */
var tracePanel = document.getElementById("trace");
var tracePanel = document.getElementById(output);
tracePanel.innerHTML += msg + "\n";
tracePanel.scrollTop = tracePanel.scrollHeight;
console.log(msg);
Expand All @@ -68,7 +79,16 @@
<div>
<video controls="true">
</video>
<textarea id="trace" placeholder="Trapped events will be displayed here"></textarea>
</div>
<div style="display:flex">
<div>
<p>Received Events</p>
<textarea id="receive" placeholder="Trapped on_receive events will be displayed here"></textarea>
</div>
<div>
<p>Started Events</p>
<textarea id="start" placeholder="Trapped on_start events will be displayed here"></textarea>
</div>
</div>
<script>
document.addEventListener("DOMContentLoaded", function () {
Expand Down
26 changes: 17 additions & 9 deletions src/core/EventBus.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
import FactoryMaker from './FactoryMaker';
import { EVENT_MODE_ON_START, EVENT_MODE_ON_RECEIVE } from '../streaming/MediaPlayerEvents';

const EVENT_PRIORITY_LOW = 0;
const EVENT_PRIORITY_HIGH = 5000;
Expand All @@ -37,7 +38,7 @@ function EventBus() {

let handlers = {};

function on(type, listener, scope, priority = EVENT_PRIORITY_LOW) {
function on(type, listener, scope, options = {}) {

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

let priority = options.priority || EVENT_PRIORITY_LOW;
let mode = options.mode || EVENT_MODE_ON_START;

if (getHandlerIdx(type, listener, scope) >= 0) return;

handlers[type] = handlers[type] || [];

const handler = {
callback: listener,
scope: scope,
priority: priority
priority: priority,
mode
};

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

let isEventStart = true;
if (typeof filters.isEventStart === 'boolean') isEventStart = filters.isEventStart;

payload = payload || {};

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

payload.type = type;

handlers[type] = handlers[type].filter((item) => item);
const eventHandlers = handlers[type].filter(item => {
if (filters.streamId && item.streamId && item.streamId !== filters.streamId) return false;
if (filters.mediaType && item.mediaType && item.mediaType !== filters.mediaType) return false;
return true;
});
const mode = isEventStart ? EVENT_MODE_ON_START : EVENT_MODE_ON_RECEIVE;

eventHandlers.forEach(handler => handler && handler.callback.call(handler.scope, payload));
handlers[type]
.filter((item) => item)
.filter(item => !(filters.streamId && item.streamId && item.streamId !== filters.streamId))
.filter(item => !(filters.mediaType && item.mediaType && item.mediaType !== filters.mediaType))
.filter(handler => handler.mode === mode)
.forEach(handler => handler && handler.callback.call(handler.scope, payload));
}

function getHandlerIdx(type, listener, scope) {
Expand Down
7 changes: 4 additions & 3 deletions src/streaming/MediaPlayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -406,11 +406,12 @@ function MediaPlayer() {
* @param {string} type - {@link MediaPlayerEvents}
* @param {Function} listener - callback method when the event fires.
* @param {Object} scope - context of the listener so it can be removed properly.
* @param {Object} options - object to define various options such as scope, priority and mode
* @memberof module:MediaPlayer
* @instance
*/
function on(type, listener, scope) {
eventBus.on(type, listener, scope);
function on(type, listener, scope, options) {
eventBus.on(type, listener, scope, options);
}

/**
Expand Down Expand Up @@ -1736,7 +1737,7 @@ function MediaPlayer() {
manifestLoader.reset();
};

eventBus.on(Events.INTERNAL_MANIFEST_LOADED, handler, self);
eventBus.on(Events.INTERNAL_MANIFEST_LOADED, handler, { scope: self });

uriFragmentModel.initialize(url);
manifestLoader.load(url);
Expand Down
12 changes: 12 additions & 0 deletions src/streaming/MediaPlayerEvents.js
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,18 @@ class MediaPlayerEvents extends EventsBase {
* @event MediaPlayerEvents#GAP_CAUSED_SEEK_TO_PERIOD_END
*/
this.GAP_CAUSED_SEEK_TO_PERIOD_END = 'gapCausedSeekToPeriodEnd';

/**
* Dash events are triggered at their respective start points on the timeline.
* @event MediaPlayerEvents#EVENT_MODE_ON_START
*/
this.EVENT_MODE_ON_START = 'onStart';

/**
* Dash events are triggered as soon as they were parsed.
* @event MediaPlayerEvents#EVENT_MODE_ON_RECEIVE
*/
this.EVENT_MODE_ON_RECEIVE = 'onReceive';
}
}

Expand Down
13 changes: 9 additions & 4 deletions src/streaming/controllers/EventController.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ function EventController() {
let event = values[i];
inlineEvents[event.id] = event;
logger.debug('Add inline event with id ' + event.id);
_startEvent(event.id, event, values);
}
}
logger.debug(`Added ${values.length} inline events`);
Expand All @@ -145,6 +146,7 @@ function EventController() {
}
inbandEvents[event.id] = event;
logger.debug('Add inband event with id ' + event.id);
_startEvent(event.id, event, values);
} else {
logger.debug('Repeated event with id ' + event.id);
}
Expand Down Expand Up @@ -207,6 +209,7 @@ function EventController() {
function _onEventTimer() {
try {
if (!eventHandlingInProgress) {
eventHandlingInProgress = true;
const currentVideoTime = playbackController.getTime();
let presentationTimeThreshold = (currentVideoTime - lastEventTimerCall);

Expand All @@ -218,8 +221,8 @@ function EventController() {
_removeEvents();

lastEventTimerCall = currentVideoTime;
eventHandlingInProgress = false;
}
eventHandlingInProgress = false;
} catch (e) {
eventHandlingInProgress = false;
}
Expand Down Expand Up @@ -310,8 +313,10 @@ function EventController() {
function _startEvent(eventId, event, events) {
try {
const currentVideoTime = playbackController.getTime();
const calculatedPresentationTimeInSeconds = event.calculatedPresentationTime / event.eventStream.timescale;
const isEventStart = Math.floor(currentVideoTime) === calculatedPresentationTimeInSeconds;

if (event.duration > 0) {
if (isEventStart && event.duration > 0) {
activeEvents[eventId] = event;
}

Expand All @@ -325,10 +330,10 @@ function EventController() {
_sendCallbackRequest(event.messageData);
} else {
logger.debug(`Starting event ${eventId} at ${currentVideoTime}`);
eventBus.trigger(event.eventStream.schemeIdUri, { event: event });
eventBus.trigger(event.eventStream.schemeIdUri, { event: event }, { isEventStart });
}

delete events[eventId];
if (isEventStart) delete events[eventId];
} catch (e) {
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/streaming/controllers/PlaybackController.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ function PlaybackController() {
eventBus.on(Events.BUFFER_LEVEL_STATE_CHANGED, onBufferLevelStateChanged, this);
eventBus.on(Events.PLAYBACK_PROGRESS, onPlaybackProgression, this);
eventBus.on(Events.PLAYBACK_TIME_UPDATED, onPlaybackProgression, this);
eventBus.on(Events.PLAYBACK_ENDED, onPlaybackEnded, this, EventBus.EVENT_PRIORITY_HIGH);
eventBus.on(Events.PLAYBACK_ENDED, onPlaybackEnded, this, { priority: EventBus.EVENT_PRIORITY_HIGH });
eventBus.on(Events.STREAM_INITIALIZING, onStreamInitializing, this);

if (playOnceInitialized) {
Expand Down
18 changes: 17 additions & 1 deletion test/unit/core.EventBus.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import EventBus from '../../src/core/EventBus';
import { EVENT_MODE_ON_START, EVENT_MODE_ON_RECEIVE } from '../../src/streaming/MediaPlayerEvents';

const chai = require('chai');
const sinon = require('sinon');
Expand Down Expand Up @@ -31,12 +32,27 @@ describe('EventBus', function () {
const spy2 = sinon.spy();

eventBus.on('EVENT_TEST', spy);
eventBus.on('EVENT_TEST', spy2, this, EventBus.EVENT_PRIORITY_HIGH);
eventBus.on('EVENT_TEST', spy2, this, { priority: EventBus.EVENT_PRIORITY_HIGH });

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

assert.equal(spy.calledOnce, true);
assert.equal(spy2.calledOnce, true);
assert.equal(spy2.calledBefore(spy), true);
});

it('should respect mode parameter in the on function in order to trigger events according to isEventStart option in trigger function', function () {
const spy = sinon.spy();
const spy2 = sinon.spy();

eventBus.on('EVENT_TEST', spy, null, { mode: EVENT_MODE_ON_START });
eventBus.on('EVENT_TEST', spy2, null, { mode: EVENT_MODE_ON_RECEIVE });

eventBus.trigger('EVENT_TEST', {}, { isEventStart: false });
eventBus.trigger('EVENT_TEST', {}, { isEventStart: true });

assert.equal(spy.calledOnce, true);
assert.equal(spy2.calledOnce, true);
assert.equal(spy2.calledBefore(spy), true);
});
});
7 changes: 6 additions & 1 deletion test/unit/mocks/PlaybackControllerMock.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class PlaybackControllerMock {
this.playing = false;
this.seeking = false;
this.isDynamic = false;
this.time = 0;
this.streamController = new StreamControllerMock();
}

Expand Down Expand Up @@ -60,7 +61,11 @@ class PlaybackControllerMock {
}

getTime() {
return null;
return this.time;
}

setTime(time) {
this.time = time;
}

getNormalizedTime() {
Expand Down
Loading

0 comments on commit 39dc91e

Please sign in to comment.