Skip to content

Commit

Permalink
Update thumbnails API (#3297)
Browse files Browse the repository at this point in the history
* Thumbnails Improvments :

- Update index.d.ts file

- Rename getThumbnail by provideThumbnail : In some cases (SegmentBase for images), thumbnails getting is asynchronous and is get using callback. To uniformize the behaviour, thubnails are always provided using a callback.

- Display thumbnails bitrate list in control bar

* For IE11 and some others old browsers, the verification of autoswitch parameter is done in function

* Verificy that callback parameter is a function
  • Loading branch information
jeremco committed Jun 18, 2020
1 parent 76cd1ca commit a700684
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 36 deletions.
21 changes: 16 additions & 5 deletions contrib/akamai/controlbar/ControlBar.js
Expand Up @@ -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
Expand Down Expand Up @@ -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 + ')' : '';
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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';
Expand Down Expand Up @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions index.d.ts
Expand Up @@ -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[];
Expand Down Expand Up @@ -803,7 +803,7 @@ declare namespace dashjs {
}

export class BitrateInfo {
mediaType: 'video' | 'audio';
mediaType: 'video' | 'audio' | 'image';
bitrate: number;
width: number;
height: number;
Expand Down
22 changes: 12 additions & 10 deletions src/streaming/MediaPlayer.js
Expand Up @@ -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);
}

/*
Expand Down Expand Up @@ -2271,7 +2273,7 @@ function MediaPlayer() {
displayCaptionsOnTop: displayCaptionsOnTop,
attachTTMLRenderingDiv: attachTTMLRenderingDiv,
getCurrentTextTrackIndex: getCurrentTextTrackIndex,
getThumbnail: getThumbnail,
provideThumbnail: provideThumbnail,
getDashAdapter: getDashAdapter,
getOfflineController: getOfflineController,
getSettings: getSettings,
Expand Down
18 changes: 10 additions & 8 deletions src/streaming/thumbnail/ThumbnailController.js
Expand Up @@ -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
Expand All @@ -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) {
Expand All @@ -98,9 +102,7 @@ function ThumbnailController(config) {
thumbnail.url = request.url;
track.segmentDuration = NaN;
}
if (callback)
callback(thumbnail);
return thumbnail;
callback(thumbnail);
}
}

Expand Down Expand Up @@ -142,7 +144,7 @@ function ThumbnailController(config) {
}

instance = {
get: getThumbnail,
provide: provideThumbnail,
setTrackByIndex: setTrackByIndex,
getCurrentTrackIndex: getCurrentTrackIndex,
getBitrateList: getBitrateList,
Expand Down
24 changes: 13 additions & 11 deletions test/unit/streaming.thumbnail.ThumbnailController.js
Expand Up @@ -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
});
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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
});
});
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -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);
Expand Down

0 comments on commit a700684

Please sign in to comment.