Overview
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()
.
v7 | v8 |
---|---|
bitmovin.player(‘dom-element-id’) | new bitmovin.player.Player(domElement, config) |
player.setup | removed |
PlayerConfiguration.source | removed, load needs to be used |
OR
|
|
player.getConfig().source | player.getSource() |
Events
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.
v7 | v8 |
---|---|
EVENT.ON_READY after setup | Removed as setup was removed |
EVENT.ON_READY after load | PlayerEvent.SourceLoaded |
EVENT.ON_SOURCE_LOADED is fired after a load was initiated | PlayerEvent.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_PLAYBACK_FINISHED = ‘onPlaybackFinished’ EVENT.ON_PLAY = ‘onPlay’ ... | New naming scheme for ALL events: PlayerEvent.PlaybackFinished = ‘playbackfinished’ PlayerEvent.Play = ‘play’ ... |
EVENT.ON_PLAYER_RESIZE | PlayerEvent.PlayerResized |
Mix of EVENT.ON_WARNING with codes below 1000 and EVENT.ON_AD_ERROR | PlayerEvent.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.
v7 | v8 |
---|---|
Many API calls return Player object:pause: Player ... | API calls which returned Player before now return undefined per defaultpause: undefined ... |
play: Player | play: Promise |
unload: Player | unload: Promise |
destroy: Player | destroy: Promise |
Removals
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.
v7 | v8 |
---|---|
setLastSegment | removed |
fireEvent | removed |
tweak.search_real_end | removed |
playback.restoreUserSettings | (sample will be provided) |
tweak.preferred_audio_codec_order | use existing PlaybackConfig.audioCodecPriority |
tweak.preferred_video_codec_order | use 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.
v7 | v8 |
---|---|
ON_PAUSED immediately before ON_PLAYBACK_FINISHED event | No PlayerEvent.Paused immediately before PlayerEvent.PlaybackFinished anymore |
ON_PLAYBACK_FINISHED event before post-roll ads | PlayerEvent.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 state | setVolume 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 initiated | player.isPlaying() only returns true once playback has actually started on the media element |
Advertising
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.
v7 | v8 |
---|---|
player.isAd() | player.ads.isLinearAdActive() |
player.skipAd() | player.ads.skip() |
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 |
Subtitles
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
).
v7 | v8 |
---|---|
addSubtitle | subtitles.add(subtitleTrack) |
removeSubtitle | subtitles.remove(subtitleTrackId) |
setSubtitle | subtitles.enable(subtitleTrackId, true) or subtitles.disable(currentSubtitleTrackId) subtitles.enable(targetSubtitleTrackId, false) |
getSubtitle | subtitles.list().filter((subtitleTrack) => subtitleTrack.enabled) |
getAvailableSubtitles | subtitles.list() |
ON_SUBTITLE_CHANGED | PlayerEvent.SubtitleDisable // for each currently active subtitle track |
PlayerEvent.SubtitleDisabled // for each currently active subtitle track | |
PlayerEvent.SubtitleEnable | |
PlayerEvent.SubtitleEnabled | |
The off subtitle track | Remove, use subtitles.disable(subtitleTrackId) instead |
Errors
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.
v7 | v8 |
---|---|
ErrorEvent.code is a number | Please note that the error codes have changed and grouped together as described in the Player API documentation. ErrorEvent.code is still a number. |
ErrorEvent.message | There 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.
v7 | v8 |
---|---|
enterPictureInPicture | Removed, use setViewMode(ViewMode.PictureInPicture) |
exitPictureInPicture | Removed, use setViewMode(ViewMode.Inline) or setViewMode(ViewMode.Fullscreen) |
isPictureInPicture | Removed, use getViewMode() === ViewMode.PictureInPicture |
isPictureInPictureAvailable | Removed, use isViewModeAvailable(ViewMode.PictureInPicture) |
ON_PICTURE_IN_PICTURE_ENTER | Removed, use PlayerEvent.ViewModeChanged |
ON_PICTURE_IN_PICTURE_EXIT | Removed, use PlayerEvent.ViewModeChanged |
enterFullscreen | Removed, use setViewMode(ViewMode.Fullscreen) |
enterFullscreen(fullscreenHTMLElement) | Removed, use setViewMode(ViewMode.Fullscreen, {fullscreenElement: fullscreenHTMLElement}) |
exitFullscreen | Removed, use setViewMode(ViewMode.Inline) or setViewMode(ViewMode.PictureInPicture) |
isFullscreen | Removed, use getViewMode() === ViewMode.Fullscreen |
ON_FULLSCREEN_ENTER | Removed, use PlayerEvent.ViewModeChanged |
ON_FULLSCREEN_EXIT | Removed, 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.
v7 | v8 |
---|---|
headers: [{ name: ‘X-Custom-Header-Name’, value: ‘customHeaderValue’}] | headers: { ‘X-Custom-Header-Name’: ‘customHeaderValue’ } |
source.tracks | source.subtitleTracks source.thumbnailTrack |
getThumb(): Thumb Thumb { index, start, end, w, h, x, y } | getThumbnail(): Thumbnail Thumbnail { start, end, width, height, x, y } |
getDroppedFrames | getDroppedVideoFrames |
source.drm.access | config.nativeflash |
PlayerAPINotAvailableException | PlayerAPINotAvailableError |
EVENT and LOG_LEVEL exports were on the player object | Event and LogLevel exports are in the bitmovin.player root namespace |
error & warning code numbers changed, see Player API docs for details |