Migration Guide - v7 to v8


We collected a lot of feedback over more than the last one and a half years, since v7 was first released. We tried to incorporate as much as possible into our latest major update, v8. Bitmovin Player v8 provides many new features and capabilities, first and foremost its modularity. As a consequence, there are a some changes in the existing configuration, API and events.

Player Setup

The creation and setup of a new player including the relevant events have been streamlined.

Instead of a generator function which returns a new player object (bitmovin.player(htmlElementId)), a constructor is now provided: bitmovin.player.Player(htmlElement, config). The previous steps of calling the asynchronous setup on the player instance and waiting for its Promise to resolve or the ON_READY event to be fired is not required anymore as the constructor is completely synchronous (therefore also no event is fired anymore). To achieve this, the source was removed from the player configuration.

As a consequence, a source needs to be explicitly set using the player’s load call. As previous player versions, this call is asynchronous as metadata, such as the manifest file, is fetched to provide details like asset duration. The asynchronous task is finished when the load Promise resolved (or rejected) or the PlayerEvent.SourceLoaded was fired.

As the source is not part of the configuration anymore, it's not possible to get the source object using player.getConfig().source. A new API call has been added for this: player.getSource().

bitmovin.player(‘dom-element-id’)new bitmovin.player.Player(domElement, config)
PlayerConfiguration.sourceremoved, load needs to be used
const player = bitmovin.player('domElementId');

const player = bitmovin.player('domElementId');
const player = new bitmovin.player.Player(domElement, configWithoutSource);


The long and cumbersome addEventHandler and removeEventHandler were changed to the shorter on and off, respectively. This would lead to duplicate ON_ prefixes (like player.on(player.EVENT.ON_PLAYBACK_FINISHED, callback)) which is not desirable. Therefore, the prefix was removed from all event names. The new pattern of the exposed enum is now PlayerEvent.PlaybackFinished = ‘playbackfinished’, so the string representations are also closer to native HTML events like readystatechange.

As already explained in the section above, the ON_READY event was removed.

The new PlayerEvent.SourceLoaded is fired when the metadata of an asset was successfully downloaded after a load call. The new PlayerEvent.Ready is fired when enough media data was downloaded to immediately start playback without any additional downloads or bufferings.

The EVENT.ON_PLAYER_RESIZE event was renamed to PlayerEvent.PlayerResized to reflect the timing more correctly.

Every advertising-related error now has only the AdError event associated with it and not a mix of Warning and AdError events.

EVENT.ON_READY after setupRemoved as setup was removed
EVENT.ON_READY after loadPlayerEvent.SourceLoaded
EVENT.ON_SOURCE_LOADED is fired after a load was initiatedPlayerEvent.SourceLoaded is fired after metadata of a new source was successfully downloaded
-PlayerEvent.Ready if enough media data is buffered to start playback immediately without further downloads or stalls
Naming scheme for ALL events:
EVENT.ON_PLAY = ‘onPlay’
New naming scheme for ALL events:
PlayerEvent.PlaybackFinished = ‘playbackfinished’
PlayerEvent.Play = ‘play’
Mix of EVENT.ON_WARNING with codes below 1000 and EVENT.ON_AD_ERRORPlayerEvent.AdError

Changed return values

All API calls which used to return the Player object itself, like pause, now return undefined per default. In addition, there are API calls which return a Promise now, namely play, destroy and unload. This allows us to provide a more developer-friendly way of successful or rejected play results as more and more browsers stop supporting (unmuted) autoplay.

Many API calls return Player object:
pause: Player
API calls which returned Player before now return undefined per default
pause: undefined
play: Playerplay: Promise
unload: Playerunload: Promise
destroy: Playerdestroy: Promise


The API calls setLastSegment and fireEvent were removed, as was the experimental tweaks.search_real_end flag. Furthermore, the ability to save and restore user settings such as subtitle or audio language in the browser’s localStorage was removed.

playback.restoreUserSettings(sample will be provided)
tweak.preferred_audio_codec_orderuse existing PlaybackConfig.audioCodecPriority
tweak.preferred_video_codec_orderuse existing PlaybackConfig.videoCodecPriority

Behavioral changes

In v7, we relied a lot on the HTML5 video element, especially with its events. While we think this is in general still a good idea, there is one particular case which seemed odd not only to us but also to a lot of customers and analytics providers. An ON_PAUSED event was always fired just before the ON_PLAYBACK_FINISHED event. This is no longer the case in v8.

It was also hard to distinguish whether a stall happened due to insufficient bandwidth, initial startup or a seek/timeshift request of the user. This has been streamlined so that StallStarted and StallEnded events only refer to insufficient bandwidth. There are no such events on startup or during seeking/timeshifting anymore. Play is semantically equivalent with StallStarted and Playing with StallEnded for this case. The same is true for Seek/Timeshift and Seeked/Timeshifted, respectively.

Despite having separate mute and setVolume API calls, setVolume also changed the mute state to unmuted. This behavior was changed in v8, so that the API calls only change the respective settings.

As the center of a VR/360 video is commonly the most relevant part at least for the beginning, v8 changes that 0° refers to the center of the video instead of to the left edge. This also affects the start position, which is still 0° but therefore defaults to the middle of the video.

The player.isPlaying() now also reflects if playback was actually started, similar to PlayerEvent.Playing, instead of reflecting the intention to play back.

ON_PAUSED immediately before ON_PLAYBACK_FINISHED eventNo PlayerEvent.Paused immediately before PlayerEvent.PlaybackFinished anymore
ON_PLAYBACK_FINISHED event before post-roll adsPlayerEvent.PlaybackFinished after last post-roll ad
ON_STALL_STARTED and ON_STALL_ENDED events are fired between ON_PLAY and ON_PLAYING (i.e. playback start)No more StallStarted and StallEnded events between Play and Playing (i.e. playback start)
ON_STALL_STARTED and ON_STALL_ENDED events are fired between ON_SEEK/ON_TIMESHIFT and ON_SEEKED/ON_TIMESHIFTED (i.e. playback start)No more StallStarted and StallEnded events between Seek/Timeshift and Seeked/Timeshifted (i.e. playback start)
setVolume() could change from muted to unmuted statesetVolume only changes the volume, but a muted player will stay muted
In VR/360, 0° was defined as the very left edge of the video and could be increased up to 360°In VR/360, 0° is defined as the center of the video and could be decreased to -180° and increased up to 180°.
player.isPlaying() immediately retunred true once playback was initiatedplayer.isPlaying() only returns true once playback has actually started on the media element


Advertising-related APIs were moved into an ads namespace on the player object (player.ads). The ads namespace is only available on the player if an AdvertisingConfig is provided, even if it is an empty one.

player.scheduleAd(adManifest, client, options)player.ads.schedule(adConfig)
-player.ads.list() returns an array with all scheduled ads
-player.ads.getActiveAdBreak() returns the current ad break
-player.ads.getActiveAd() returns the currently played ad
-player.ads.discardAdBreak(adBreakId) removes the specified ad break from the ad schedule


To improve the handling and flexibility of subtitles, a new API replaces the old subtitle API. It was moved into a subtitles namespace on the player to group all the API calls. The new API also adds the possibility to have two subtitle tracks active at the same time, e.g. to provide quasi-open and closed captions at the same time. This can be done by simply using subtitles.enable(trackId, false) to enable an additional subtitle track without disabling all previous ones. An explicit subtitles.disable(trackId) API was added and the almost always present off subtitle track was removed. The getAvailableSubtitles call was renamed to subtitles.list. As multiple subtitle tracks can now be enabled at the same time, the getSubtitle call was removed and an enabled property was added to the subtitle track objects in the subtitles.list call, which allows to get an array of enabled tracks using something like subtitles.list().filter((subtitleTrack) => subtitleTrack.enabled).

setSubtitlesubtitles.enable(subtitleTrackId, true) or subtitles.disable(currentSubtitleTrackId) subtitles.enable(targetSubtitleTrackId, false)
getSubtitlesubtitles.list().filter((subtitleTrack) => subtitleTrack.enabled)
ON_SUBTITLE_CHANGEDPlayerEvent.SubtitleDisable // for each currently active subtitle track
PlayerEvent.SubtitleDisabled // for each currently active subtitle track
The off subtitle trackRemove, use subtitles.disable(subtitleTrackId) instead


The strictly English error messages have been removed, instead each error got a specific ErrorEvent.name. The translation and conversion to user friendly descriptions may vary depending on the application, therefore should be done in the UI. In addition, an optional ErrorEvent.data object was added to provide additional error-specific information.
Please note that the error codes have changed to group them together in a more meaningful way.

ErrorEvent.code is a numberPlease note that the error codes have changed and grouped together as described in the Player API documentation. ErrorEvent.code is still a number.
ErrorEvent.messageThere is no full message anymore, but ErrorEvent.name contains a string which specifies which error occurred. Full user messages should be implemented in an UI, if required.
-ErrorEvent.data is a new optional object to provide error-specific details, like the statusCode: number for failed manifest requests.

Fullscreen and Picture-in-Picture APIs become unified as ViewMode API

There was a quite extensive amount of API calls and events to handle entering and exiting fullscreen and picture-in-picture mode, respectively as well as querying whether one of that view modes are currently active. We have revised these APIs to make it easier to work with. Instead of all the individual calls and events, there is now one set of APIs: setViewMode is used to transition between the normal Inline playback, Fullscreen and PictureInPicture mode. Each transition triggers a ViewModeChanged event. getViewMode can be used to query the currently active view mode. isViewModeAvailable can be used to query if a view mode, such as PictureInPicture, is available on the current system.

enterPictureInPictureRemoved, use setViewMode(ViewMode.PictureInPicture)
exitPictureInPictureRemoved, use setViewMode(ViewMode.Inline) or setViewMode(ViewMode.Fullscreen)
isPictureInPictureRemoved, use getViewMode() === ViewMode.PictureInPicture
isPictureInPictureAvailableRemoved, use isViewModeAvailable(ViewMode.PictureInPicture)
ON_PICTURE_IN_PICTURE_ENTERRemoved, use PlayerEvent.ViewModeChanged
ON_PICTURE_IN_PICTURE_EXITRemoved, use PlayerEvent.ViewModeChanged
enterFullscreenRemoved, use setViewMode(ViewMode.Fullscreen)
enterFullscreen(fullscreenHTMLElement)Removed, use setViewMode(ViewMode.Fullscreen, {fullscreenElement: fullscreenHTMLElement})
exitFullscreenRemoved, use setViewMode(ViewMode.Inline) or setViewMode(ViewMode.PictureInPicture)
isFullscreenRemoved, use getViewMode() === ViewMode.Fullscreen
ON_FULLSCREEN_ENTERRemoved, use PlayerEvent.ViewModeChanged
ON_FULLSCREEN_EXITRemoved, use PlayerEvent.ViewModeChanged

Other changes

A few other changes got introduced with v8:

The HTTP header objects used in configuration properties and APIs through out the player were simplified from arrays of objects with name and value to simple objects where the name of the HTTP header is the property name and the value of the header the property value.

The source.tracks array was split into source.subtitleTracks and source.thumbnailTrack. The main reason is that there should be only one thumbnail track but there could be multiple subtitle tracks.

The Thumbnail API got renamed from getThumb to getThumbnail, w and h in the Thumbnail (previously Thumb) object became width and height. The index was removed.

The API call getDroppedFrames got renamed to getDroppedVideoFrames to be more specific on what is returned.

The configuration necessary to use Flash's native HLS implementation was moved from source.drm.access to config.nativeflash as it is not related to Adobe Access DRM.

Error and Warning codes were changed to group them logically together. Please refer to Player API - ErrorCode and Player API - WarningCode for details.

headers: [{ name: ‘X-Custom-Header-Name’, value: ‘customHeaderValue’}]headers: { ‘X-Custom-Header-Name’: ‘customHeaderValue’ }
getThumb(): Thumb
Thumb { index, start, end, w, h, x, y }
getThumbnail(): Thumbnail
Thumbnail { start, end, width, height, x, y }
EVENT and LOG_LEVEL exports were on the player objectEvent and LogLevel exports are in the bitmovin.player root namespace
error & warning code numbers changed, see Player API docs for details