Skip to content

v4.0.0-rc.1

Pre-release
Pre-release
Compare
Choose a tag to compare
@peaBerberian peaBerberian released this 24 Jan 16:54

Release v4.0.0-rc.1 (2024-01-24)

Quick Links:
πŸ“– API documentation - ⏯ Demo - πŸŽ“ Migration guide from v3

πŸ” Overview

After more than three years since its early drafts and one year of open beta releases, it is finally time for the first release candidate of the v4.0.0 of the RxPlayer.

The main ideas behind this new v4 major version are:

  • To provide a more flexible and powerful track and quality selection API, especially more adapted to multi-Period DASH contents.

    For example, it is now not only possible to set a particular quality on a content, you can also allow a subset of multiple qualities from which the RxPlayer will adaptively choose from, based on any of their characteristics exposed by the API.

    You can also indicate what to do if the buffer already contains media data in another quality (reload? Keep it in the buffer? Remove it?).

    Likewise, track switching becomes very configurable, allowing you to directly switch a track of any Period, again with a strategy if another track was previously present in the buffer. The RxPlayer also allow to rewind a little (and let you set by how much), in cases where you want to give back some context (for example when switching the audio track to another language).

    The main API documentation about quality selection can be found here.
    For track switching, you can refer to the API documentation of the concerned API, see for example the setAudioTrack API documentation.

  • To allow a more sane default behavior and more resilience in the RxPlayer.

    For example, we are now automatically reloading if something has been detected to have gone horribly wrong, such as when encountering some exceptional device-related decryption issues or when obtaining a completely different Manifest after updating it.

    Moreover, if the chosen track is linked to Representations (qualities) which are all impossible to decrypt, the RxPlayer will now automatically fallback to a supported track instead (after sending events to indicate that it does so).

    We were previously limited on how we could do those things to stay compatible to old APIs.

  • To facilitate the implementation of ambitious improvements.

    For example the v4.0.0-rc.1 brings the new MULTI_THREAD experimental feature, allowing to run the RxPlayer's main logic in a WebWorker for better adaptive streaming stability and performances.
    This is a huge work we're right now using on a multitude of devices at Canal+, intended to improve the quality of experience, especially on devices with limited resources.

Depending on return we have on our large-scale tests (basically, if no important issue is found with it), this release candidate may become an official v4.0.0 relatively fastly (in the coming weeks).

If you're still relying on a v3.x.x release, we recommend looking up our migration guide to make the switch more easily.

πŸ“‘ Changelog

We decided to compile the full v4.0.0 changelog for this release (instead of comparing it to our latest beta release), which makes it too big for this release note!
So we're only linking you to it here: https://github.com/canalplus/rx-player/blob/bf79b9164f8d530a94e338725f795ec5a5c7b278/CHANGELOG.md

What about the v3?

The v3.33.0 release of the RxPlayer, which was published just before this one, is planned to be our final default release on npm in the v3 major version (unless there is a major issue with it).

Once the official v4.0.0 stable release is published, v4 releases will take its place as a default when installing the package through package managers such as yarn and npm.

Moreover, most incoming features and improvements will be planned for v4.x.x releases first from now on.

v3.x.x releases will still be maintained for some time (we were for example talking about providing at least 1 year of support), but it will mostly be only bug fixes and improvements simple enough to be ported to it. Of course, outside contributions are welcomed if you want to provide improvements on it yourself.

If you want to make the switch but you're afraid of relying on a release candidate right now, we recommend maintaining a branch which will depend on this
v4.0.0-rc.1 release in your project and putting it in production once the v4.0.0 is actually released.

v4 summary

The v4.0.0-rc.1 inherits from both the v4.0.0-beta.3 (and consequently any prior v4 release) and from the just-released v3.33.0 (and consequently all previous v3 releases).

The rest of this release note is going to focus ONLY on features brought in this particular release (which means we're not relisting features already announced in previous v4 and v3 release notes).

You can look at the v4 beta release notes for more information on some earlier v4 features:

And on the the v3.33.0 release note for the just-released features which are also in this one.

The new MULTI_THREAD experimental feature

This release brings the possibility of running most of the RxPlayer main logic in a WebWorker, letting your application to run concurrently with it. This has potentially large positive impacts on performance and adaptive streaming stability (e.g. keeping a stable high video quality).

This new behavior is totally optional and has to be enabled through specific APIs.
The RxPlayer is also able to automatically detect when multithreading is not possible (very old devices), to go back in the regular monothreading mode instead.

Running the RxPlayer without a WebWorker (the default):

+-------------------------------------------------------------------------------+
| Main thread (also running the UI)                                             |
|                                                                               |
| +------------------+     +----------------------+    +----------------------+ |
| |    Application   | --> |  RxPlayer Main [1]   | -> |   RxPlayer Core [2]  | |
| +------------------+     +----------------------+    +----------------------+ |
+-------------------------------------------------------------------------------+

Running with a WebWorker:

+----------------------------------------------------+
| Main thread (also running the UI)                  |
|                                                    |
| +------------------+      +----------------------+ |
| |    Application   | ---> |  RxPlayer Main [1]   | |
| +------------------+      +----------------------+ |
+--------------------------------------|-------------+
                                       | (messages)
+--------------------------------------|-------------+
| WebWorker                            V             |
|                           +----------------------+ |
|                           |  RxPlayer Core [2]   | |
|                           +----------------------+ |
+----------------------------------------------------+


[1] RxPlayer Main: Exposes an API to the application, performs some high-level
media monitoring, handles content decryption, displays text tracks and interacts
with web API that are only usable in the main thread.

[2] RxPlayer Core; Loads and parses the Manifest as well as media segments that
will be be played. Also monitors what's being played and will be played to
ensure a smooth playback.

Schema: Here is some high level schema of how the RxPlayer would roughly work without and with a WebWorker.

There's many things to say about this new feature and we encourage you to check if it improves the experience on your application.
Everything should be detailed in its API documentation, here.

Breaking change: volume APIs

We've changed the behavior of the mute / unMute / isMute methods, so they now mutate the muted property of the media element, whereas they updated the volume property before (by setting it to 0 or resetting it to its previous value).

It means that this change can also lead to issue if you were previously relying only on setVolume to un-mute (now an unMute call will also have to be made in that scenario) or on isMute / a getVolume returning 0 to know if there was no sound (both checks will have to be performed now).

volume.mp4

video: Example scenario in a UI where we would want now to both set the volume and unMute. Before, setting a volume automatically unmuted as both relied on the HTML volume property.

Likewise the volumeChange event now announces both the muted (as a boolean) and volume (as a number) properties, instead of just the volume.

We did those changes as it better maps to the actual HTML5 API for media volume control. By aligning with them, we hope to improve the player compatibility to current and future browsers' behaviors.

A new requestConfig property: connectionTimeout

We've also added a new property to the requestConfig option you can provide to a loadVideo call: connectionTimeout.

This new feature gives developers a finer control over request management by introducing a more granular approach to handling timeouts.
Previously, it was only possible to set a global request timeout for manifests and segments requests (through respectively a manifest.timeout and a segment.timeout property).

The inclusion of the manifest.connectionTimeout and segment.connectionTimeout properties now also allow to abort requests that took too much time to establish connection (i.e: receiving HTTP headers) while still allowing to wait longer for requests that are currently downloading potentially large response data.

Connection
Schema: Very simplified illustration of the difference between the "connection establishment" which is the step that connectionTimeout acts on, and the regular end of the HTTP(S) request which is the step that timeout acts on.

Using this new property should lead to a more efficient hanged requests aborting logic if you encounter them.

It has been documented in the requestConfig documentation.

Seek-back on track switch with relativeResumingPosition

The v4.0.0-rc.1 also adds a new option when switching the audio track (through the setAudioTrack method) or the video track (through the setVideoTrack method): relativeResumingPosition.

With it, you can provoke a seek if the track switching interruption. The main use case for example would be to seek back one second or so when changing the audio track to a new language, allowing the final user to re-hear the corresponding sentence:

output.mp4

video: Example usage of the relativeResumingPosition option in setAudioTrack, the value is configured at -5.
This causes a seek to the position 5 seconds before the current position when performing an audio track change.

For example, if you want to exploit this feature, you may call setAudioTrack this way:

rxPlayer.setAudioTrack({
  trackId: "my-track",

  // Ensures that there's an interruption if a previous audio track
  // was playing
  switchingMode: "direct",
  
  // Seek back 1 second after switching to the new audio track.
  relativeResumingPosition: -1,
})

Note that the relativeResumingPosition option has some intelligence and will probably not actually seek back if it was not currently playing a previous audio track for that Period (either because it was not the current Period being played, because no audio track was previously playing or because we were already currently seeking or buffering), which is what we guess you should want in most of those scenarios.

As such, that option's main purpose is just to give back audio and/or video context when the content is interrupted due to a track switch,

Easier DASH_WASM integration with the EMBEDDED_WASM embed

We profited from the new MULTI_THREAD experimental feature to facilitate management of the separate WebAssembly file that was needed to enable the DASH_WASM feature.

Before, an application had to download, store and serve itself the separate mpd-parser.wasmthat comes with each new release of the RxPlayer. This meant that each time the RxPlayer was updated, this file had to be manually updated.
This was a cumbersome step that we would prefer you to avoid.

Now, the RxPlayer also provides an export which embeds that file in an easy-to-import JavaScript file.
You can now just import that embedded version like this in your code:

import { EMBEDDED_DASH_WASM } from "rx-player/experimental/features/embeds";

Which can then be directly provided to a DASH_WASM.initialize call:

import { DASH_WASM } from "rx-player/features";

DASH_WASM.initialize({ wasmUrl: EMBEDDED_DASH_WASM });

Doing this removes the need to have to update manually the mpd-parser.wasm, with the cost of some minor efficiency loss at initialization time. This new way is described in the updated DASH_WASM API documentation.

Breaking change: A representationFilter's codec is now codecs

The representationFilter API had to be changed a little so its API stay the same even when the new experimental MULTI_THREAD feature is relied on.

The first argument given to the representationFilter, an object describing the corresponding Representation, has now its codec property renamed to codecs. That codecs property is now an array of strings, whereas it was only a string before.

For the great majority of cases, checking the first element of that array is equivalent to the content of the old codec property.

So if you were doing something like:

function representationFilter(representationInfo) {
  if (representationInfo.codec === SOME_CODEC) {
    return false;
  }
  return true;
}

You'll now need to do something like:

function representationFilter(representationInfo) {
  // Note: `codecs` can also be `undefined`
  if (
    Array.isArray(representationInfo.codecs) &&
    representationInfo.codecs[0] === SOME_CODEC
  ) {
    return false;
  }
  return true;
}

There are very rare conditions where you might have multiple codecs in that array. For the moment, it might only happen if you're:

  1. Relying on the MULTI_THREAD experimental feature and a worker
  2. Running on a browser without the MSE-in-Worker feature
  3. Are playing a DASH content
  4. Have a scte214:supplementalCodec in the MPD
  5. The codec support check is not yet cached (meaning that it's the first time since the RxPlayer is imported that we're checking for codec compatibility). Typically, this happens for the first loaded content
  6. the representationFilter function is declared as a string (else we won't be relying on the worker for now)

In that very specific scenario, the RxPlayer may have several candidate codecs for a given Representation (depending on device support) but not yet know which one is actually supported when the representationFilter is called. In that case the codecs array will have multiple entries, from the most wanted codec (e.g. Dolby Vision) to the more compatible (e.g. HEVC).

Samsung TV seek-back handling refactoring

Samsung TVs (we generally refer to those platforms as "Tizen", as that's what the name of their OS is) are historically the devices giving us the most trouble. This is due to a very specific behavior we ended up calling: "Tizen automatic seek-backs".

Let's say that the RxPlayer want to seek at a given position, like 51 seconds.
On all platforms but Samsung TVs, the RxPlayer would tell the browser to seek at the position 51, and then lower-level decoding layers would pick-up playback from that position.

On Samsung TVs however, it may not go that way. Tizen could decide to actually re-provoke by itself a seek to another position, often earlier (let's say at 50 seconds), without telling us, most likely for what it thinks are performance reasons (e.g. because the video frame at 50 seconds is some kind of I-frame).

Yet the RxPlayer often has very good reasons to want to seek to a given precise position. It may be because it wants to go to a specific video frame, or because it wants to go precizely at the beginning of a given time range. On Samsung TVs, we found out this is impossible to expect, as Tizen will have the last say on which position to really seek to.

TizenSeekingLoop(1)
Schema: Illustration of a "seeking loop" due to Tizen behavior when seeking. Here the RxPlayer wants to seek at 51 but Samsung TVs decide to bring playback back to 50 seconds.

The previous strategy was to litter our code with exceptions just for Tizen devices at each place in the code where this "seek-back" behavior led to problems. After 5 or 6 patches, we thought for many years that we had no remaining issues. Sadly, it came biting us back recently in relatively rare situations!

So, in the v4.0.0-rc.1 release, we totally changed the way that Samsung TV behavior was handled. To keep it simple, our logic now makes a difference between the position we're actually playing at the lower-level (so influenced by Tizen "seek-backs"), and the one the RxPlayer wanted to actually play (here not influenced by it) using internally one or the other depending on the situation.

This means that even if some features (like frame-to-frame seeking) should not be possible on those devices (I don't think it actually can be possible here), the RxPlayer should now avoid most of the corresponding logic issues, as it will most of the time just go on with the position it wants to play.

Of course it's a little more complicated than that, yet the gist of it is that we refactored Samsung TV handling to better handle its peculiar behavior and it should be totally transparent for the application.

DASH_WASM, DEBUG_ELEMENT and parseBifThumbnails are not experimental anymore

We profited from this release candidate to remove the "experimental" status of various API, meaning they are now an integral part of the v4.x.x.

This concerns the following features:

  • DASH_WASM (which can now be imported from rx-player/features)
  • DEBUG_ELEMENT (which can now be imported from rx-player/features)

And the following tool:

  • parseBifThumbnails (which can now be imported from rx-player/tools)

This means what was previously written this way:

import { DASH_WASM, DEBUG_ELEMENT } from "rx-player/experimental/features";
import { parseBifThumbnails } from "rx-player/experimental/tools";

Needs now to be written this way:

import { DASH_WASM, DEBUG_ELEMENT } from "rx-player/features";
import { parseBifThumbnails } from "rx-player/tools";

Their API didn't change.

New package export strategy

This v4.0.0-rc.1 made some important updates to how the RxPlayer is exported, yet it should not impact your project in most cases:

  1. The default build exported by the RxPlayer will be an ES2017-compatible build, with corresponding ESM exports (import/export). We also alternatively provide commonJS exports if your project relies on it instead.
    Previously the default "rx-player" path actually exported our bundle, and other RxPlayer exports exported ES5 code with CommonJS-style exports.

    This should have most likely no impact on applications relying on the RxPlayer, beside perhaps needing to transpile the RxPlayer's ES2017 code to an earlier version if you want to support older platforms.

  2. To resolve dependency paths (e.g. "rx-player/features", "rx-player/tools" etc.), we now make use of the exports property in our package.json, whereas it previously followed the real path of our project root directory.

    This should hopefully be transparent for you as this is the new popular default way for declaring complex exports and most tools now read this exports property. However, if you depend on an old (~2021 or less) bundler or TypeScript or linter version and seeing importing issues after using those, you might need to update that dependency to a more recent version.