diff --git a/contrib/akamai/controlbar/ControlBar.js b/contrib/akamai/controlbar/ControlBar.js index b7fdbbcf7d..e80fc1001d 100644 --- a/contrib/akamai/controlbar/ControlBar.js +++ b/contrib/akamai/controlbar/ControlBar.js @@ -278,7 +278,7 @@ var ControlBar = function (dashjsMediaPlayer, displayUTCTimeCodes) { } // Get thumbnail information - player.getThumbnail(mouseTime, function (thumbnail) { + player.provideThumbnail(mouseTime, function (thumbnail) { if (!thumbnail) return; // Adjust left variable for positioning thumbnail with regards to its viewport @@ -505,7 +505,8 @@ var ControlBar = function (dashjsMediaPlayer, displayUTCTimeCodes) { var availableBitrates = { menuType: 'bitrate' }; availableBitrates.audio = player.getBitrateInfoListFor('audio') || []; availableBitrates.video = player.getBitrateInfoListFor('video') || []; - if (availableBitrates.audio.length > 1 || availableBitrates.video.length > 1) { + availableBitrates.images = player.getBitrateInfoListFor('image') || []; + if (availableBitrates.audio.length > 1 || availableBitrates.video.length > 1 || availableBitrates.images.length > 1) { contentFunc = function (element, index) { var result = isNaN(index) ? ' Auto Switch' : Math.floor(element.bitrate / 1000) + ' kbps'; result += element && element.width && element.height ? ' (' + element.width + 'x' + element.height + ')' : ''; @@ -578,6 +579,11 @@ var ControlBar = function (dashjsMediaPlayer, displayUTCTimeCodes) { el = createMenuContent(el, getMenuContent(menuType, info.audio, contentFunc), 'audio', 'audio-' + menuType + '-list'); setMenuItemsState(getMenuInitialIndex(info.audio, menuType, 'audio'), 'audio-' + menuType + '-list'); } + if (info.images.length > 1) { + el.appendChild(createMediaTypeMenu('image')); + el = createMenuContent(el, getMenuContent(menuType, info.images, contentFunc, false), 'image', 'image-' + menuType + '-list'); + setMenuItemsState(getMenuInitialIndex(info.images, menuType, 'image'), 'image-' + menuType + '-list'); + } break; } @@ -619,12 +625,14 @@ var ControlBar = function (dashjsMediaPlayer, displayUTCTimeCodes) { return (sameId && sameViewpoint && sameLang && sameRoles && sameAccessibility && sameAudioChannelConfiguration); }; - var getMenuContent = function (type, arr, contentFunc) { + var getMenuContent = function (type, arr, contentFunc, autoswitch) { + autoswitch = (autoswitch !== undefined) ? autoswitch : true; + var content = []; arr.forEach(function (element, index) { content.push(contentFunc(element, index)); }); - if (type !== 'track') { + if (type !== 'track' && autoswitch) { content.unshift(contentFunc(null, NaN)); } return content; @@ -655,7 +663,7 @@ var ControlBar = function (dashjsMediaPlayer, displayUTCTimeCodes) { div.id = type; - title.textContent = type === 'video' ? 'Video' : 'Audio'; + title.textContent = type.charAt(0).toUpperCase() + type.slice(1); title.classList.add('menu-sub-menu-title'); content.id = type + 'Content'; @@ -749,6 +757,9 @@ var ControlBar = function (dashjsMediaPlayer, displayUTCTimeCodes) { player.updateSettings(cfg); } break; + case 'image-bitrate-list': + player.setQualityFor(self.mediaType, self.index); + break; case 'caption-list': player.setTextTrack(self.index - 1); break; diff --git a/index.d.ts b/index.d.ts index d960c6b5a7..46ee0424f3 100644 --- a/index.d.ts +++ b/index.d.ts @@ -283,7 +283,7 @@ declare namespace dashjs { setTextDefaultLanguage(lang: string): void; getTextDefaultEnabled(): boolean | undefined; setTextDefaultEnabled(enable: boolean): void; - getThumbnail(time: number): Thumbnail; + provideThumbnail(time: number, callback: (thumbnail: Thumbnail | null) => void): void; getBitrateInfoListFor(type: MediaType): BitrateInfo[]; getStreamsFromManifest(manifest: object): StreamInfo[]; getTracksFor(type: MediaType): MediaInfo[]; @@ -803,7 +803,7 @@ declare namespace dashjs { } export class BitrateInfo { - mediaType: 'video' | 'audio'; + mediaType: 'video' | 'audio' | 'image'; bitrate: number; width: number; height: number; diff --git a/src/streaming/MediaPlayer.js b/src/streaming/MediaPlayer.js index 22633639e3..6f72125918 100644 --- a/src/streaming/MediaPlayer.js +++ b/src/streaming/MediaPlayer.js @@ -1652,31 +1652,33 @@ function MediaPlayer() { */ /** - * Return the thumbnail at time position. - * @returns {Thumbnail|null} - Thumbnail for the given time position. It returns null in case there are is not a thumbnails representation or - * if it doesn't contain a thumbnail for the given time position. + * Provide the thumbnail at time position. This can be asynchronous, so you must provide a callback ro retrieve thumbnails informations * @param {number} time - A relative time, in seconds, based on the return value of the {@link module:MediaPlayer#duration duration()} method is expected - * @param {function} callback - A Callback function provided when retrieving thumbnail + * @param {function} callback - A Callback function provided when retrieving thumbnail the given time position. Thumbnail object is null in case there are is not a thumbnails representation or + * if it doesn't contain a thumbnail for the given time position. * @memberof module:MediaPlayer * @instance */ - function getThumbnail(time, callback) { + function provideThumbnail(time, callback) { + if (typeof callback !== 'function') { + return; + } if (time < 0) { - return null; + callback(null); } const s = playbackController.getIsDynamic() ? getDVRSeekOffset(time) : time; const stream = streamController.getStreamForTime(s); if (stream === null) { - return null; + callback(null); } const thumbnailController = stream.getThumbnailController(); if (!thumbnailController) { - return null; + callback(null); } const timeInPeriod = streamController.getTimeRelativeToStreamId(s, stream.getId()); - return thumbnailController.get(timeInPeriod, callback); + return thumbnailController.provide(timeInPeriod, callback); } /* @@ -2271,7 +2273,7 @@ function MediaPlayer() { displayCaptionsOnTop: displayCaptionsOnTop, attachTTMLRenderingDiv: attachTTMLRenderingDiv, getCurrentTextTrackIndex: getCurrentTextTrackIndex, - getThumbnail: getThumbnail, + provideThumbnail: provideThumbnail, getDashAdapter: getDashAdapter, getOfflineController: getOfflineController, getSettings: getSettings, diff --git a/src/streaming/thumbnail/ThumbnailController.js b/src/streaming/thumbnail/ThumbnailController.js index 096d69c155..9ac5c60cae 100644 --- a/src/streaming/thumbnail/ThumbnailController.js +++ b/src/streaming/thumbnail/ThumbnailController.js @@ -57,12 +57,17 @@ function ThumbnailController(config) { }); } - function getThumbnail(time, callback) { + function provideThumbnail(time, callback) { + + if (typeof callback !== 'function') { + return; + } const track = thumbnailTracks.getCurrentTrack(); let offset, request; if (!track || track.segmentDuration <= 0 || time === undefined || time === null) { - return null; + callback(null); + return; } // Calculate index of the sprite given a time @@ -87,8 +92,7 @@ function ThumbnailController(config) { if ('readThumbnail' in track) { return track.readThumbnail(time, (url) => { thumbnail.url = url; - if (callback) - callback(thumbnail); + callback(thumbnail); }); } else { if (!request) { @@ -98,9 +102,7 @@ function ThumbnailController(config) { thumbnail.url = request.url; track.segmentDuration = NaN; } - if (callback) - callback(thumbnail); - return thumbnail; + callback(thumbnail); } } @@ -142,7 +144,7 @@ function ThumbnailController(config) { } instance = { - get: getThumbnail, + provide: provideThumbnail, setTrackByIndex: setTrackByIndex, getCurrentTrackIndex: getCurrentTrackIndex, getBitrateList: getBitrateList, diff --git a/test/unit/streaming.thumbnail.ThumbnailController.js b/test/unit/streaming.thumbnail.ThumbnailController.js index 9ccbc5d9e7..fa889de588 100644 --- a/test/unit/streaming.thumbnail.ThumbnailController.js +++ b/test/unit/streaming.thumbnail.ThumbnailController.js @@ -86,8 +86,9 @@ describe('Thumbnails', function () { }); it('should return null if not initialized', function () { - const thumbnail = thumbnailController.get(0); - expect(thumbnail).to.be.null; // jshint ignore:line + thumbnailController.provide(0, (thumbnail) => { + expect(thumbnail).to.be.null; // jshint ignore:line + }); expect(thumbnailController.getBitrateList()).to.be.empty; // jshint ignore:line }); @@ -116,10 +117,11 @@ describe('Thumbnails', function () { }); it('should return a thumbnail', function () { - let thumbnail = thumbnailController.get(); - expect(thumbnail).to.be.null; // jshint ignore:line + thumbnailController.provide(undefined, thumbnail => { + expect(thumbnail).to.be.null; // jshint ignore:line + }); - thumbnailController.get(0, thumbnail => { + thumbnailController.provide(0, thumbnail => { expect(thumbnail).to.be.not.null; // jshint ignore:line expect(thumbnail.x).to.equal(0); expect(thumbnail.y).to.equal(0); @@ -128,7 +130,7 @@ describe('Thumbnails', function () { expect(thumbnail.url).to.equal('http://media/rep_id/1.jpg'); }); - thumbnailController.get(11, thumbnail => { + thumbnailController.provide(11, thumbnail => { expect(thumbnail).to.be.not.null; // jshint ignore:line expect(thumbnail.x).to.equal(320); expect(thumbnail.y).to.equal(0); @@ -137,7 +139,7 @@ describe('Thumbnails', function () { expect(thumbnail.url).to.equal('http://media/rep_id/1.jpg'); }); - thumbnailController.get(101, thumbnail => { + thumbnailController.provide(101, thumbnail => { expect(thumbnail).to.be.not.null; // jshint ignore:line expect(thumbnail.x).to.equal(0); expect(thumbnail.y).to.equal(0); @@ -149,7 +151,7 @@ describe('Thumbnails', function () { it('shouldn\'t return any thumbnail after reset', function () { thumbnailController.reset(); - thumbnailController.get(0, thumbnail => { + thumbnailController.provide(0, thumbnail => { expect(thumbnail).to.be.null; // jshint ignore:line }); }); @@ -191,7 +193,7 @@ describe('Thumbnails', function () { }); it('should return a thumbnail when using multiple rows sprites ', function () { - thumbnailController.get(0, thumbnail => { + thumbnailController.provide(0, thumbnail => { expect(thumbnail).to.be.not.null; // jshint ignore:line expect(thumbnail.x).to.equal(0); expect(thumbnail.y).to.equal(0); @@ -201,7 +203,7 @@ describe('Thumbnails', function () { }); - thumbnailController.get(15, thumbnail => { + thumbnailController.provide(15, thumbnail => { expect(thumbnail).to.be.not.null; // jshint ignore:line expect(thumbnail.x).to.equal(409.6); expect(thumbnail.y).to.equal(0); @@ -210,7 +212,7 @@ describe('Thumbnails', function () { expect(thumbnail.url).to.equal('http://media/rep_id/1.jpg'); }); - thumbnailController.get(40, thumbnail => { + thumbnailController.provide(40, thumbnail => { expect(thumbnail).to.be.not.null; // jshint ignore:line expect(thumbnail.x).to.equal(204.8); expect(thumbnail.y).to.equal(57.6);