diff --git a/samples/alternative/index.html b/samples/alternative/index.html
index 113d808a06..dc9eaf6942 100644
--- a/samples/alternative/index.html
+++ b/samples/alternative/index.html
@@ -15,7 +15,8 @@
width: 640px;
height: 360px;
}
- #alternativeVideo {
+
+ #alternative-video-element {
display: none;
}
@@ -31,8 +32,8 @@
// url = 'http://localhost:3000/samples/alternative/manifest-replace-clip.mpd';
// url = 'http://localhost:3000/samples/alternative/manifest-replace-start-with-offset.mpd';
- video = document.querySelector('#mainVideo');
-
+ video = document.getElementById('video-element');
+ alternativeVideo = document.getElementById('alternative-video-element');
player = dashjs.MediaPlayer().create();
alternativeVideo = document.querySelector('#alternativeVideo');
player.initialize(video, url, false);
@@ -57,8 +58,8 @@
Alternative Media Presentations
-
-
+
+
diff --git a/samples/alternative/manifest-insert.mpd b/samples/alternative/manifest-insert.mpd
index 64dd3bf6a9..4deba7afec 100644
--- a/samples/alternative/manifest-insert.mpd
+++ b/samples/alternative/manifest-insert.mpd
@@ -2,7 +2,7 @@
https://dash.akamaized.net/akamai/bbb_30fps/
-
+
diff --git a/src/streaming/MediaManager.js b/src/streaming/MediaManager.js
index 83f26b6739..2700ae737c 100644
--- a/src/streaming/MediaManager.js
+++ b/src/streaming/MediaManager.js
@@ -32,6 +32,7 @@ import Events from '../core/events/Events.js';
import MediaPlayerEvents from './MediaPlayerEvents.js';
import MediaPlayer from './MediaPlayer.js';
import FactoryMaker from '../core/FactoryMaker.js';
+import Debug from '../core/Debug.js';
function MediaManager() {
let instance,
@@ -44,9 +45,12 @@ function MediaManager() {
altVideoElement,
alternativeContext,
logger,
+ debug,
prebufferedPlayers = new Map(),
prebufferCleanupInterval = null;
+ const context = this.context;
+
function setConfig(config) {
if (!config) {
return;
@@ -56,8 +60,8 @@ function MediaManager() {
videoModel = config.videoModel;
}
- if (config.logger) {
- logger = config.logger;
+ if (config.debug) {
+ debug = config.debug;
}
if (!!config.playbackController && !playbackController) {
@@ -74,6 +78,12 @@ function MediaManager() {
}
function initialize() {
+ if (!debug) {
+ debug = Debug(context).getInstance();
+ }
+
+ logger = debug.getLogger(instance);
+
if (!fullscreenDiv) {
fullscreenDiv = document.createElement('div');
fullscreenDiv.id = 'fullscreenDiv';
@@ -98,7 +108,7 @@ function MediaManager() {
function prebufferAlternativeContent(playerId, alternativeMpdUrl) {
try {
if (prebufferedPlayers.has(playerId)) {
- return; // Already prebuffered
+ return;
}
logger.info(`Starting prebuffering for player ${playerId}`);
@@ -140,10 +150,6 @@ function MediaManager() {
prebufferedPlayer.player.off(Events.ERROR);
prebufferedPlayer.player.reset();
- if (prebufferedPlayer.videoElement && prebufferedPlayer.videoElement?.parentNode) {
- prebufferedPlayer.videoElement.parentNode?.removeChild(prebufferedPlayer.videoElement);
- }
-
prebufferedPlayers.delete(playerId);
}
logger.debug(`Cleaned up prebuffered content for ${playerId}`);
@@ -187,35 +193,17 @@ function MediaManager() {
const prebufferedContent = prebufferedPlayers.get(playerId);
if (prebufferedContent) {
- // Use prebuffered content
logger.info(`Using prebuffered content for player ${playerId}`);
-
- // Move prebuffered video element to visible area
altPlayer = prebufferedContent.player;
-
- // Remove from prebuffered storage
prebufferedPlayers.delete(playerId);
-
- // Setup video element for display
- altVideoElement.style.display = 'none';
- altVideoElement.controls = !hideAlternativePlayerControls;
-
- if (altVideoElement.parentNode !== fullscreenDiv) {
- fullscreenDiv.appendChild(altVideoElement);
- }
-
- // Insert into DOM if needed
- const videoElement = videoModel.getElement();
- const parentNode = videoElement && videoElement.parentNode;
- if (parentNode && !parentNode.contains(altVideoElement)) {
- parentNode.insertBefore(altVideoElement, videoElement.nextSibling);
- }
-
- altPlayer.attachView(altVideoElement);
} else {
initializeAlternativePlayer(alternativeMpdUrl);
}
+ if (altPlayer && altVideoElement) {
+ altPlayer.attachView(altVideoElement);
+ }
+
videoModel.pause();
logger.debug('Main video paused');
@@ -228,7 +216,7 @@ function MediaManager() {
}
altPlayer.play();
- logger.info('Alternative content playback started');
+ logger.info(`Alternative content playback started for player ${playerId}`);
isSwitching = false;
}
@@ -239,6 +227,11 @@ function MediaManager() {
logger.debug('Switch already in progress - ignoring request');
return
};
+
+ if (!altPlayer) {
+ logger.warn('No alternative player to switch back from');
+ return;
+ }
logger.info('Switching back to main content');
isSwitching = true;
diff --git a/src/streaming/MediaPlayer.js b/src/streaming/MediaPlayer.js
index bfa2701749..f268a4b431 100644
--- a/src/streaming/MediaPlayer.js
+++ b/src/streaming/MediaPlayer.js
@@ -610,7 +610,6 @@ function MediaPlayer() {
}
if (source) {
const playbackTime = time ? time : providedStartTime;
- console.log(playbackTime)
_initializePlayback(playbackTime);
} else {
throw SOURCE_NOT_ATTACHED_ERROR;
diff --git a/src/streaming/controllers/AlternativeMediaController.js b/src/streaming/controllers/AlternativeMediaController.js
index 8d9840c16d..894518d349 100644
--- a/src/streaming/controllers/AlternativeMediaController.js
+++ b/src/streaming/controllers/AlternativeMediaController.js
@@ -76,13 +76,6 @@ function AlternativeMediaController() {
hideAlternativePlayerControls = false,
alternativeVideoElement = null;
- function setup() {
- if (!debug) {
- debug = Debug(context).getInstance();
- }
- logger = debug.getLogger(instance);
- }
-
function setConfig(config) {
if (!config) {
return;
@@ -114,7 +107,10 @@ function AlternativeMediaController() {
}
function initialize() {
- setup();
+ if (!debug) {
+ debug = Debug(context).getInstance();
+ }
+ logger = debug.getLogger(instance);
// Initialize the media manager if not already provided via config
if (!mediaManager) {
diff --git a/test/unit/mocks/VideoModelMock.js b/test/unit/mocks/VideoModelMock.js
index a1f64c0ec1..2ff7095355 100644
--- a/test/unit/mocks/VideoModelMock.js
+++ b/test/unit/mocks/VideoModelMock.js
@@ -111,9 +111,14 @@ class VideoModelMock {
}
pause() {
+ this.isplaying = false;
this.ispaused = true;
}
+ isPlaying() {
+ return this.isplaying;
+ }
+
isPaused() {
return this.ispaused;
}
diff --git a/test/unit/test/streaming/streaming.MediaManager.js b/test/unit/test/streaming/streaming.MediaManager.js
new file mode 100644
index 0000000000..4fb1062304
--- /dev/null
+++ b/test/unit/test/streaming/streaming.MediaManager.js
@@ -0,0 +1,154 @@
+import MediaManager from '../../../../src/streaming/MediaManager.js';
+import VideoModelMock from '../../mocks/VideoModelMock.js';
+import PlaybackControllerMock from '../../mocks/PlaybackControllerMock.js';
+import DebugMock from '../../mocks/DebugMock.js';
+import { expect } from 'chai';
+
+describe('MediaManager', function () {
+ let mediaManager;
+ let videoModelMock;
+ let mockVideoElement;
+ let playbackControllerMock;
+ let debugMock;
+
+ beforeEach(function () {
+ mediaManager = MediaManager().getInstance();
+ videoModelMock = new VideoModelMock();
+ videoModelMock.play();
+ playbackControllerMock = new PlaybackControllerMock();
+ debugMock = new DebugMock();
+ mediaManager.setConfig({
+ videoModel: videoModelMock,
+ playbackController: playbackControllerMock,
+ debug: debugMock,
+ hideAlternativePlayerControls: false,
+ alternativeContext: {}
+ });
+
+ mediaManager.initialize();
+ });
+
+ afterEach(function () {
+ if (mediaManager) {
+ mediaManager.reset();
+ }
+ });
+
+ describe('set the alternative video element', function () {
+ it('should not throw error when setting alternative video element', function () {
+ mockVideoElement = document.createElement('video');
+
+ expect(() => {
+ mediaManager.setAlternativeVideoElement(mockVideoElement);
+ }).to.not.throw();
+ });
+ });
+
+ describe('prebuffer the alternative content', function () {
+ it('should start prebuffering alternative content and log the action', function () {
+ const testUrl = 'http://test.mpd';
+ const testPlayerId = 'testPlayer';
+
+ expect(() => {
+ mediaManager.prebufferAlternativeContent(testPlayerId, testUrl);
+ }).to.not.throw();
+
+ expect(debugMock.log.info).to.equal(`Starting prebuffering for player ${testPlayerId}`);
+ });
+ });
+
+ describe('switch to the alternative content', function () {
+ beforeEach(function () {
+ mockVideoElement = document.createElement('video');
+ mediaManager.setAlternativeVideoElement(mockVideoElement);
+ });
+
+ it('should switch to alternative content without prebuffered content', function () {
+ const testUrl = 'http://test.mpd';
+ const testPlayerId = 'testPlayer';
+
+ mediaManager.switchToAlternativeContent(testPlayerId, testUrl);
+
+ expect(debugMock.log.info).to.equal(`Alternative content playback started for player ${testPlayerId}`);
+ expect(videoModelMock.isPaused()).to.be.true;
+ expect(mockVideoElement.style.display).to.be.equal('block');
+ expect(videoModelMock.getElement().style.display).to.be.equal('none');
+ });
+
+ it('should switch to alternative content with prebuffered content', function () {
+ const testUrl = 'http://test.mpd';
+ const testPlayerId = 'testPlayer';
+
+ mediaManager.prebufferAlternativeContent(testPlayerId, testUrl);
+ mediaManager.switchToAlternativeContent(testPlayerId, testUrl);
+
+ expect(debugMock.log.info).to.equal(`Alternative content playback started for player ${testPlayerId}`);
+ expect(videoModelMock.isPaused()).to.be.true;
+ expect(mockVideoElement.style.display).to.be.equal('block');
+ expect(videoModelMock.getElement().style.display).to.be.equal('none');
+ });
+
+ it('should switch to alternative content and seek to a given time', function () {
+ const testUrl = 'http://test.mpd';
+ const testPlayerId = 'testPlayer';
+ const testTime = 15;
+
+ mediaManager.switchToAlternativeContent(testPlayerId, testUrl, testTime);
+
+ expect(debugMock.log.debug).to.equal(`Seeking alternative content to time: ${testTime}`);
+ expect(debugMock.log.info).to.equal(`Alternative content playback started for player ${testPlayerId}`);
+ expect(videoModelMock.isPaused()).to.be.true;
+ expect(mockVideoElement.style.display).to.be.equal('block');
+ expect(videoModelMock.getElement().style.display).to.be.equal('none');
+ });
+ });
+
+ describe('get alternative player', function () {
+ beforeEach(function () {
+ mockVideoElement = document.createElement('video');
+ mediaManager.setAlternativeVideoElement(mockVideoElement);
+ });
+
+ it('should return null when no alternative player is set', function () {
+ const result = mediaManager.getAlternativePlayer();
+ expect(result).to.be.undefined;
+ });
+
+ it('should return the alternative player when it is set', function () {
+ const testUrl = 'http://test.mpd';
+ const testPlayerId = 'testPlayer';
+
+ mediaManager.switchToAlternativeContent(testPlayerId, testUrl, 0);
+
+ const result = mediaManager.getAlternativePlayer();
+ expect(result).to.not.be.undefined;
+ expect(result).to.be.an('object');
+ });
+ });
+
+ describe('switch back to the main content', function () {
+ beforeEach(function () {
+ mockVideoElement = document.createElement('video');
+ mediaManager.setAlternativeVideoElement(mockVideoElement);
+ });
+
+ it('should warn when no alternative player is set', function () {
+ mediaManager.switchBackToMainContent(10);
+
+ expect(debugMock.log.warn).to.equal('No alternative player to switch back from');
+ });
+
+ it('should switch back to main content', function () {
+ const testUrl = 'http://test.mpd';
+ const testPlayerId = 'testPlayer';
+ const seekTime = 20;
+
+ mediaManager.switchToAlternativeContent(testPlayerId, testUrl, 0);
+ mediaManager.switchBackToMainContent(seekTime);
+
+ expect(debugMock.log.info).to.equal('Main content playback resumed');
+ expect(mediaManager.getAlternativePlayer()).to.be.null;
+ expect(videoModelMock.isPlaying()).to.be.true;
+ });
+ });
+});
\ No newline at end of file