diff --git a/samples/overlays/lib/img/dashjs-logo.png b/samples/overlays/lib/img/dashjs-logo.png new file mode 100644 index 0000000000..f208775e85 Binary files /dev/null and b/samples/overlays/lib/img/dashjs-logo.png differ diff --git a/samples/overlays/overlays.html b/samples/overlays/overlays.html new file mode 100644 index 0000000000..d3e16ed96e --- /dev/null +++ b/samples/overlays/overlays.html @@ -0,0 +1,84 @@ + + + + Dash.js Rocks + + + + + + +
+
+ +
+ +
+ + +
+ +
+
+ +
+
+
+ +
+ +
+
+ + + + + diff --git a/src/dash/constants/DashConstants.js b/src/dash/constants/DashConstants.js index 347e33f2c3..95d9cbbb42 100644 --- a/src/dash/constants/DashConstants.js +++ b/src/dash/constants/DashConstants.js @@ -122,6 +122,7 @@ export default { MPD: 'MPD', ORIGINAL_MPD_ID: 'mpdId', ORIGINAL_PUBLISH_TIME: 'originalPublishTime', + OVERLAY: 'OverlayEvent', PATCH_LOCATION: 'PatchLocation', PERIOD: 'Period', PRESENTATION_TIME: 'presentationTime', diff --git a/src/dash/models/DashManifestModel.js b/src/dash/models/DashManifestModel.js index e67615f5ef..63233e7c75 100644 --- a/src/dash/models/DashManifestModel.js +++ b/src/dash/models/DashManifestModel.js @@ -1061,6 +1061,12 @@ function DashManifestModel() { } else { event.id = null; } + if (currentMpdEvent.hasOwnProperty(DashConstants.OVERLAY)) { + event.overlay = currentMpdEvent.OverlayEvent; + if (event.overlay.earliestResolutionTime) { + event.calculatedPresentationTime -= event.overlay.earliestResolutionTime / eventStream.timescale; + } + } if (currentMpdEvent.Signal && currentMpdEvent.Signal.Binary) { // toString is used to manage both regular and namespaced tags diff --git a/src/streaming/MediaPlayer.js b/src/streaming/MediaPlayer.js index e212ce0cc6..38047920a1 100644 --- a/src/streaming/MediaPlayer.js +++ b/src/streaming/MediaPlayer.js @@ -39,6 +39,7 @@ import CatchupController from './controllers/CatchupController.js'; import ServiceDescriptionController from '../dash/controllers/ServiceDescriptionController.js'; import ContentSteeringController from '../dash/controllers/ContentSteeringController.js'; import MediaController from './controllers/MediaController.js'; +import OverlayController from './controllers/OverlayController.js'; import BaseURLController from './controllers/BaseURLController.js'; import ManifestLoader from './ManifestLoader.js'; import ErrorHandler from './utils/ErrorHandler.js'; @@ -144,6 +145,7 @@ function MediaPlayer() { schemeLoaderFactory, timelineConverter, mediaController, + overlayController, protectionController, metricsReportingController, mssHandler, @@ -257,6 +259,9 @@ function MediaPlayer() { if (config.mediaController) { mediaController = config.mediaController; } + if (config.overlayController) { + overlayController = config.overlayController; + } if (config.settings) { settings = config.settings; } @@ -328,6 +333,10 @@ function MediaPlayer() { mediaController = MediaController(context).getInstance(); } + if (!overlayController) { + overlayController = OverlayController(context).getInstance(); + } + if (!streamController) { streamController = StreamController(context).getInstance(); } @@ -413,6 +422,10 @@ function MediaPlayer() { videoModel }); + overlayController.setConfig({ + videoModel + }); + mediaPlayerModel.setConfig({ playbackController, serviceDescriptionController @@ -472,6 +485,9 @@ function MediaPlayer() { if (customParametersModel) { customParametersModel.reset(); } + if (overlayController) { + overlayController.reset() + } settings.reset(); @@ -1468,6 +1484,16 @@ function MediaPlayer() { return videoModel ? videoModel.getTTMLRenderingDiv() : null; } + /** + * Returns instance of Div that was attached by calling attachOverlayRenderingDiv() + * @returns {Object} + * @memberof module:MediaPlayer + * @instance + */ + function getOverlayRenderingDiv() { + return videoModel ? videoModel.getOverlayRenderingDiv() : null; + } + /** * Use this method to attach an HTML5 div for dash.js to render rich TTML subtitles. * @@ -1490,6 +1516,18 @@ function MediaPlayer() { videoModel.setVttRenderingDiv(div); } + function attachOverlayRenderingDiv(overlayDiv) { + const videoElement = videoModel.getElement(); + if (!videoElement) { + throw ELEMENT_NOT_ATTACHED_ERROR; + } + + overlayController.configureVideoElementForOverlay(); + videoModel.setOverlayRenderingDiv(overlayDiv); + + overlayController.setupOverlayEvents(); + } + /* --------------------------------------------------------------------------- @@ -2088,6 +2126,12 @@ function MediaPlayer() { _resetPlaybackControllers(); } + const overlayDiv = videoModel.getOverlayRenderingDiv() + + if (overlayDiv) { + overlayController.reset(); + } + if (isReady()) { _initializePlayback(providedStartTime); } @@ -2740,6 +2784,7 @@ function MediaPlayer() { attachProtectionController, attachSource, attachTTMLRenderingDiv, + attachOverlayRenderingDiv, attachView, attachVttRenderingDiv, clearDefaultUTCTimingSources, @@ -2778,6 +2823,7 @@ function MediaPlayer() { getSource, getStreamsFromManifest, getTTMLRenderingDiv, + getOverlayRenderingDiv, getTargetLiveDelay, getTracksFor, getTracksForTypeFromManifest, diff --git a/src/streaming/constants/Constants.js b/src/streaming/constants/Constants.js index 5ba4ba19e9..f7c99e97b8 100644 --- a/src/streaming/constants/Constants.js +++ b/src/streaming/constants/Constants.js @@ -341,5 +341,14 @@ export default { DTSC: 'dtsc', AVC: 'avc', HEVC: 'hevc' + }, + + OVERLAY: { + SCHEME_ID: 'urn:scte:dash:scte214-events', + START_MODE: 'start', + STOP_MODE: 'stop', + EXTEND_MODE: 'extend', + VIDEO_MIMETYPE: 'video/mp4', + IFRMAE_MIMETYPE: 'text/html' } } diff --git a/src/streaming/controllers/OverlayController.js b/src/streaming/controllers/OverlayController.js new file mode 100644 index 0000000000..f922dbfb99 --- /dev/null +++ b/src/streaming/controllers/OverlayController.js @@ -0,0 +1,326 @@ +/** + * The copyright in this software is being made available under the BSD License, + * included below. This software may be subject to other third party and contributor + * rights, including patent rights, and no such rights are granted under this license. + * + * Copyright (c) 2013, Dash Industry Forum. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * * Neither the name of Dash Industry Forum nor the names of its + * contributors may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +import Constants from '../constants/Constants.js'; +import EventBus from './../../core/EventBus.js'; +import FactoryMaker from '../../core/FactoryMaker.js'; +import PlaybackController from './PlaybackController.js'; +import Utils from '../../core/Utils.js'; + +function OverlayController() { + + const context = this.context; + const eventBus = EventBus(context).getInstance(); + const playbackController = PlaybackController(context).getInstance(); + + let instance, + videoModel, + schedulerInitialized, + resizeObserver, + overlayList = []; + + function setConfig(config) { + if (!config) { + return; + } + + if (config.videoModel) { + videoModel = config.videoModel; + } + } + + function configureVideoElementForOverlay() { + const videoElement = videoModel.getElement(); + videoElement.style.width = '100%'; + videoElement.style.height = '100%'; + videoElement.style.display = 'block'; + videoElement.style.transform = 'scale(1)'; + + const parent = videoElement.parentElement; + parent.style.position = 'relative'; + parent.style.overflow = 'hidden'; + } + + function setupOverlayEvents() { + eventBus.on(Constants.OVERLAY.SCHEME_ID, _handleOverlayEvent); + } + + function reset() { + _stopAllOverlayEvents(); + + overlayList = []; + + clearInterval(schedulerInitialized); + schedulerInitialized = false; + + if (resizeObserver) { + resizeObserver.disconnect(); + } + + _removeOverlayStyles() + } + + function _stopAllOverlayEvents () { + overlayList.forEach((overlay) => { + _stopOverlayEvent(overlay.eventId); + }) + } + + function _removeOverlayStyles() { + const overlayDiv = videoModel.getOverlayRenderingDiv(); + if (!overlayDiv) { + return + } + + videoModel.setOverlayRenderingDiv(overlayDiv); + } + + function _initializeScheduler() { + if (!schedulerInitialized) { + schedulerInitialized = setInterval(_handleScheduleInerval, 100); + } + } + + function _stopScheduler() { + clearInterval(schedulerInitialized); + schedulerInitialized = false; + } + + function _handleScheduleInerval() { + if (overlayList.length) { + _startScheduledOverlay(); + _stopScheduledOverlay(); + } + } + + function _startScheduledOverlay() { + const currentTime = playbackController.getTime(); + overlayList.forEach((scheduledOverlay) => { + const { eventId, duration, presentationTime, overlay, overlayElement } = scheduledOverlay; + if (!scheduledOverlay.started && _canSetOverlayElement(presentationTime, currentTime, duration)) { + scheduledOverlay.started = true; + _stylizeOverlayContainter(overlay); + overlayElement.loop = overlay.loop === 'true'; + videoModel.setOverlayElement(overlayElement, eventId); + } + }); + } + + function _stopScheduledOverlay() { + const currentTime = playbackController.getTime(); + overlayList.forEach((scheduledOverlay) => { + const { eventId, duration, presentationTime } = scheduledOverlay; + if (duration && _overlayEventIsFinished(presentationTime, duration, currentTime)) { + _stopOverlayEvent(eventId); + } + }); + if (!overlayList.length) { + _stopScheduler(); + } + } + + function _handleOverlayEvent(e) { + const { event } = e; + if (!event || !event.overlay) { + return + } + const overlayMode = event.overlay.mode ?? Constants.OVERLAY.START_MODE; + switch (overlayMode) { + case Constants.OVERLAY.START_MODE: + _overlayStartMode(event); + break; + case Constants.OVERLAY.EXTEND_MODE: + _overlayExtendMode(event); + break; + case Constants.OVERLAY.STOP_MODE: + _overlayStopMode(event); + break; + } + } + + function _overlayStartMode(event) { + let overlayElement + if (event.overlay.mimeType === Constants.OVERLAY.VIDEO_MIMETYPE) { + overlayElement = _createVideoOverlayElement(event); + } else if (event.overlay.mimeType === Constants.OVERLAY.IFRMAE_MIMETYPE) { + overlayElement = _createIframeOverlayElement(event); + } + _adaptOverlayElement(overlayElement, event.overlay.uri); + + const eventId = event.id ?? `${Utils.generateUuid()}`; + const presentationTime = event.presentationTime / 1000; + overlayList.push({ + eventId, + duration: event.duration, + presentationTime, + overlay: event.overlay, + overlayElement, + started: false + }); + _initializeScheduler(); + } + + function _overlayExtendMode(event) { + if (event.duration) { + const overlayElement = overlayList.find(element => element.eventId == event.overlay.refId); + if (overlayElement) { + overlayElement.duration = event.duration; + overlayElement.presentationTime = event.presentationTime / 1000; + } + } + } + + function _overlayStopMode(event) { + _stopOverlayEvent(event.overlay.refId); + } + + function _stopOverlayEvent(refId) { + videoModel.removeOverlayElementById(refId); + configureVideoElementForOverlay(); + // Do not filter if we want to reuse the overlays once they end. + overlayList = overlayList.filter((element) => element.eventId != refId); + } + + function _overlayEventIsFinished(presentationTime, duration, currentTime) { + return presentationTime + duration <= currentTime + } + + function _canSetOverlayElement(presentationTime, currentTime, duration) { + return presentationTime <= currentTime && (!_overlayEventIsFinished(presentationTime, duration, currentTime) || !duration); + } + + function _createVideoOverlayElement (event) { + const overlayElement = document.createElement('video'); + overlayElement.preload = 'auto'; + overlayElement.autoplay = true; + _setVideoOverlayEvents(event, overlayElement); + return overlayElement; + } + + function _setVideoOverlayEvents(event, overlayElement) { + eventBus.on(dashjs.MediaPlayer.events.PLAYBACK_PLAYING, function() { + overlayElement.play(); + }); + + eventBus.on(dashjs.MediaPlayer.events.PLAYBACK_PAUSED, function() { + overlayElement.pause(); + }); + + eventBus.on(dashjs.MediaPlayer.events.PLAYBACK_SEEKING, function() { + const presentationTime = event.presentationTime / 1000; + const seekTime = videoModel.getElement().currentTime - presentationTime; + if (seekTime > presentationTime && (!_overlayEventIsFinished(presentationTime, seekTime, event.duration) || !event.duration)) { + overlayElement.currentTime = seekTime; + } else if (seekTime < 0) { + _stopOverlayEvent(event.id); + } + }); + } + + function _createIframeOverlayElement (event) { + const overlayElement = document.createElement('iframe'); + overlayElement.style.border = 'none'; + eventBus.on(dashjs.MediaPlayer.events.PLAYBACK_SEEKING, function() { + const presentationTime = event.presentationTime / 1000; + const seekTime = videoModel.getElement().currentTime - presentationTime; + if (seekTime < 0) { + _stopOverlayEvent(event.id); + } + }); + return overlayElement; + } + + function _adaptOverlayElement(overlayElement, uri) { + overlayElement.src = uri; + overlayElement.style.width = '100%'; + overlayElement.style.height = '100%'; + } + + function _stylizeOverlayContainter(overlayEvent) { + const videoElement = videoModel.getElement(); + const overlayDiv = videoModel.getOverlayRenderingDiv(); + const { Viewport, Size, TopLeft, SqueezeCurrent, z } = overlayEvent; + + if (!isNaN(z)) { + overlayDiv.style['z-index'] = z; + } + + if (SqueezeCurrent && z == -1) { + const squeezeCurrent = SqueezeCurrent.percentage; + videoElement.style.transition = 'transform'; + videoElement.style['transform-origin'] = 'top left'; + videoElement.style.transform = `scale(${squeezeCurrent})`; + } + + if (!Viewport || !Viewport?.x || !Viewport?.y ) { + return; + } + + const { overlaySize, overlayTopLeft } = _calculateOverlayDimensions(Viewport, Size, TopLeft); + const resizeFunction = (entries) => { + const entry = entries[0]; + const { width, height } = entry.contentRect; + overlayDiv.style.width = `${width * overlaySize.x}px`; + overlayDiv.style.height = `${height * overlaySize.y}px`; + overlayDiv.style.left = `${width * overlayTopLeft.x}px`; + overlayDiv.style.top = `${height * overlayTopLeft.y}px`; + }; + resizeObserver = new ResizeObserver(resizeFunction); + resizeObserver.observe(videoElement); + } + + function _calculateOverlayDimensions(viewport, size, topLeft) { + const overlaySize = { + x: size && size.x ? size.x / viewport.x : 1, + y: size && size.y ? size.y / viewport.y : 1 + }; + + const overlayTopLeft = { + x: topLeft && topLeft.x ? topLeft.x / viewport.x : 0, + y: topLeft && topLeft.y ? topLeft.y / viewport.y : 0 + }; + + return { overlaySize, overlayTopLeft }; + } + + instance = { + setConfig, + configureVideoElementForOverlay, + setupOverlayEvents, + reset + }; + + return instance; +} + +OverlayController.__dashjs_factory_name = 'OverlayController'; +const factory = FactoryMaker.getSingletonFactory(OverlayController); +FactoryMaker.updateSingletonFactory(OverlayController.__dashjs_factory_name, factory); +export default factory; diff --git a/src/streaming/models/VideoModel.js b/src/streaming/models/VideoModel.js index 0fbc080c95..a754a3a7bc 100644 --- a/src/streaming/models/VideoModel.js +++ b/src/streaming/models/VideoModel.js @@ -53,9 +53,12 @@ function VideoModel() { setCurrentTimeReadyStateFunction, TTMLRenderingDiv, vttRenderingDiv, + overlayRenderingDiv, previousPlaybackRate, timeout; + let overlayElements = []; + const VIDEO_MODEL_WRONG_ELEMENT_TYPE = 'element is not video or audio DOM type!'; const context = this.context; @@ -213,6 +216,10 @@ function VideoModel() { return vttRenderingDiv; } + function getOverlayRenderingDiv() { + return overlayRenderingDiv; + } + function setTTMLRenderingDiv(div) { TTMLRenderingDiv = div; // The styling will allow the captions to match the video window size and position. @@ -228,6 +235,38 @@ function VideoModel() { vttRenderingDiv = div; } + function setOverlayRenderingDiv(div) { + overlayRenderingDiv = div; + overlayRenderingDiv.style.position = 'absolute'; + overlayRenderingDiv.style.width = '100%'; + overlayRenderingDiv.style.height = '100%'; + overlayRenderingDiv.style.pointerEvents = 'none'; + overlayRenderingDiv.style['z-index'] = 'auto'; + overlayRenderingDiv.style.top = 0; + overlayRenderingDiv.style.left = 0; + } + + function setOverlayElement(element, id) { + overlayRenderingDiv.appendChild(element); + overlayElements.push({ + element, + id, + }); + } + + function getOverlayElementById(id) { + return overlayElements.find((element) => element.id == id); + } + + function removeOverlayElementById(id) { + const overlayElement = overlayElements.find((element) => element.id == id); + if (!overlayElement) { + return; + } + overlayElement.element.remove(); + overlayElements = overlayElements.filter((element) => element.id != id); + } + function setStallState(type, state) { stallStream(type, state); } @@ -495,6 +534,8 @@ function VideoModel() { getReadyState, getSource, getTTMLRenderingDiv, + getOverlayElementById, + getOverlayRenderingDiv, getTextTrack, getTextTracks, getTime, @@ -512,6 +553,7 @@ function VideoModel() { play, removeChild, removeEventListener, + removeOverlayElementById, reset, setCurrentTime, setDisableRemotePlayback, @@ -521,6 +563,8 @@ function VideoModel() { setStallState, setTTMLRenderingDiv, setVttRenderingDiv, + setOverlayElement, + setOverlayRenderingDiv, waitForReadyState, }; diff --git a/test/unit/mocks/VideoElementMock.js b/test/unit/mocks/VideoElementMock.js index 30c2b52e4a..aea58279e5 100644 --- a/test/unit/mocks/VideoElementMock.js +++ b/test/unit/mocks/VideoElementMock.js @@ -26,6 +26,10 @@ class VideoElementMock { this.nodeName = 'VIDEO'; this.videoWidth = 800; this.videoHeight = 600; + this.style = {}; + this.parentElement = { + style: {} + }; } constructor() { diff --git a/test/unit/mocks/VideoModelMock.js b/test/unit/mocks/VideoModelMock.js index a1f64c0ec1..f20a8dafdb 100644 --- a/test/unit/mocks/VideoModelMock.js +++ b/test/unit/mocks/VideoModelMock.js @@ -14,6 +14,7 @@ class VideoModelMock { this.height = 600; this.width = 800; this.events = {}; + this.overlayRenderingDiv = document.createElement('div'); } addEventListener(name, handler) { @@ -181,7 +182,11 @@ class VideoModelMock { } getVttRenderingDiv() { - return + return; + } + + getOverlayRenderingDiv() { + return; } setSource(source) { @@ -199,6 +204,10 @@ class VideoModelMock { setPlaybackRate() { } + + removeOverlayElementById () { + return; + } } export default VideoModelMock; diff --git a/test/unit/test/streaming/streaming.MediaPlayer.js b/test/unit/test/streaming/streaming.MediaPlayer.js index 12dacb5b64..45fa8d0369 100644 --- a/test/unit/test/streaming/streaming.MediaPlayer.js +++ b/test/unit/test/streaming/streaming.MediaPlayer.js @@ -968,6 +968,21 @@ describe('MediaPlayer', function () { expect(areEquals).to.be.true; }); + it('should be able to attach Overlay renderer div', function () { + let overlayRenderer = player.getOverlayRenderingDiv(); + expect(overlayRenderer).to.be.undefined; + + const myOverlatRenderer = { + style: {} + }; + + player.attachOverlayRenderingDiv(myOverlatRenderer); + + overlayRenderer = player.getOverlayRenderingDiv(); + const areEquals = objectUtils.areEqual(overlayRenderer, myOverlatRenderer); + expect(areEquals).to.be.true; + }); + it('Method attachView should throw an exception when attaching a view which is not VIDEO or AUDIO DOM element', function () { player.attachView(null); const myNewView = { diff --git a/test/unit/test/streaming/streaming.controllers.OverlayController.js b/test/unit/test/streaming/streaming.controllers.OverlayController.js new file mode 100644 index 0000000000..595d206344 --- /dev/null +++ b/test/unit/test/streaming/streaming.controllers.OverlayController.js @@ -0,0 +1,132 @@ +import OverlayController from '../../../../src/streaming/controllers/OverlayController.js'; +import EventBus from '../../../../src/core/EventBus.js'; +import VideoModelMock from '../../mocks/VideoModelMock.js'; +import Constants from '../../../../src/streaming/constants/Constants.js'; + +import {expect} from 'chai'; +const context = {}; + +const eventBus = EventBus(context).getInstance(); + +describe('OverlayController', function () { + + let overlayController, + videoModelMock, + videoElement, + parentElement + + beforeEach(function () { + overlayController = OverlayController(context).getInstance(); + videoModelMock = new VideoModelMock(); + + overlayController.setConfig({ + videoModel: videoModelMock + }); + + videoElement = videoModelMock.element + parentElement = document.createElement('div'); + parentElement.appendChild(videoElement); + + overlayController.setupOverlayEvents(); + }); + + this.afterEach(function () { + overlayController.reset() + }) + + describe('configure video element for overlay', function () { + it('should apply the correct style properties to the video element', function () { + overlayController.configureVideoElementForOverlay(); + + expect(videoElement.style.width).to.equal('100%'); + expect(videoElement.style.height).to.equal('100%'); + expect(videoElement.style.transform).to.equal('scale(1)'); + }); + + it('should apply the correct style properties to the parent element', function () { + overlayController.configureVideoElementForOverlay(); + + expect(parentElement.style.position).to.equal('relative'); + expect(parentElement.style.overflow).to.equal('hidden'); + }); + }); + + describe('setupOverlayEvents', function () { + it('should execute without throwing errors', function () { + expect(() => overlayController.setupOverlayEvents()).to.not.throw(); + }); + + it('should handle triggering an empty event without errors', function () { + + expect(() => { + eventBus.trigger(Constants.OVERLAY.SCHEME_ID, {}); + }).to.not.throw(); + }); + + it('should handle triggering the event start mode and video mimetype without errors', function () { + expect(() => { + eventBus.trigger(Constants.OVERLAY.SCHEME_ID, { event: { + overlay: { + mode: Constants.OVERLAY.START_MODE, + mimeType: Constants.OVERLAY.VIDEO_MIMETYPE + } + }}); + }).to.not.throw(); + }); + + it('should handle triggering the event start mode and iframe mimetype without errors', function () { + expect(() => { + eventBus.trigger(Constants.OVERLAY.SCHEME_ID, { event: { + overlay: { + mode: Constants.OVERLAY.START_MODE, + mimeType: Constants.OVERLAY.IFRMAE_MIMETYPE + } + }}); + }).to.not.throw(); + }); + + it('should handle triggering the event extend mode without errors', function () { + expect(() => { + eventBus.trigger(Constants.OVERLAY.SCHEME_ID, { event: { + overlay: { + mode: Constants.OVERLAY.START_MODE, + mimeType: Constants.OVERLAY.VIDEO_MIMETYPE, + }, + duration: 5, + presentationTime: 3000, + id: '1234' + }}); + + eventBus.trigger(Constants.OVERLAY.SCHEME_ID, { event: { + overlay: { + mode: Constants.OVERLAY.EXTEND_MODE, + refId: '1234' + }, + duration: 10, + presentationTime: 4000 + }}); + }).to.not.throw(); + }); + + it('should handle triggering the event stop mode without errors', function () { + expect(() => { + eventBus.trigger(Constants.OVERLAY.SCHEME_ID, { event: { + overlay: { + mode: Constants.OVERLAY.START_MODE, + mimeType: Constants.OVERLAY.VIDEO_MIMETYPE, + }, + duration: 5, + presentationTime: 3000, + id: '1234' + }}); + + eventBus.trigger(Constants.OVERLAY.SCHEME_ID, { event: { + overlay: { + mode: Constants.OVERLAY.STOP_MODE, + refId: '1234' + } + }}); + }).to.not.throw(); + }); + }); +}) \ No newline at end of file