Skip to content

Releases: canalplus/rx-player

v3.16.1

03 Oct 16:12
46d8ae0
Compare
Choose a tag to compare

Release v3.16.1 (2019-10-03)

Overview

This release includes bug fixes and improvements:

  • For DASH contents: we continued to improve multi-Period support
  • For MetaPlaylist contents: we improved transition between contents
  • For Smooth contents: we now request the right URL when a {CustomAttributes}
    token is declared in the Manifest
  • we avoid many left case of segment re-downloading (e.g. when there is a time
    difference between the real segment and what is deduced from the Manifest)
  • we fixed some small issues which arised after a "RELOADING" state

DASH Multi-Period and MetaPlaylist improvements

Note about the RxPlayer management of DASH's Periods and MetaPlaylist's contents

A DASH's MPD can contain multiple Period elements. Each of those allow to declare which content to play for a given period of time. They allow complex use-cases such as being able to play multiple concatenated contents - each with their own languages and/or qualities - as a single content.

This is very close to the MetaPlaylist concept of contents (if you're not familiar with what a MetaPlaylist is, it's a feature we recently introduced, documented here).

Because both concepts are very close, they are coalesced into one single concept by the RxPlayer, which we also called Period. In consequence most improvements declared here impact DASH's Periods as well as MetaPlaylist's contents.

Now let's go into the actual improvements.

Fixed multi-Period MPD update

First the most important bug fix: we fixed problems we had with MPD updates.

When a multi-Period MPD was refreshed, it could lead in some cases to the player infinitely re-buffering. This actually happened when Period were added/removed and is due to a bad logic on our part. That bad logic was present since we implemented multi-Period management in the RxPlayer.

Thankfully, our new logic is fully tested and should even be able to handle worst-case scenarios that weren't possible before, like Period introduced in-between other Periods.

Handling of discontinuities between Periods

As a second improvements, we now also better handle discontinuities between DASH's Periods and MetaPlaylist contents.

When Periods/contents are not contiguous, the RxPlayer will automatically detect it when reaching that discontinuity and automatically seek to skip it.

Fix of various problems during Period transitions

Lastly, we realized that an improvement we introduced in the v3.15.1, namely the usage of append windows, could lead to all sort of problems in different browsers:

  • some segments could be re-downloaded infinitely
  • decoding glitches
  • infinite rebuffering

Append windows basically allow to "cut" a segment at certain point in time. For example, a segment beginning at the second 499 could instead be made to begin at the second 500 by using them.
In the RxPlayer, they were used to improve multi-Period transitions, as a Period could start or finish in a middle of a segment.

However, things were not so ideal. We found out that cutting that way a segment could lead to browsers removing even more data than needed and other issues. As a compromise, we now are a little more loose to those append windows by adding some hundreds of milliseconds to them as a security.

Browser support is now much better.

Re-thinking of how we choose which segments to download

Choosing which media segments to download is one of the main role of the RxPlayer.

Before this release, here's how it went down in the RxPlayer:

  1. The player calculate from the Manifest which segments it might need are available. This is usually done by asking segments from the current position to another date several seconds/minutes after that position.
  2. It then looks at the segments already buffered.
  3. The segments obtained through step 1 are each compared to those obtained through step 2. If we find that we already buffered a segment or the same segments but in a better quality, we filter it out.
  4. We put all those segments in a download queue.

That logic worked well in most cases, but did poorly for some specific contents. For example, when a large enough "drift" was present between what a Manifest announces to be a segment's start time and its actual start time when buffered, we could be inclined to think that the segment have been garbage collected. As such, it would be re-downloaded.

To avoid those cases, we first kept everything that way and just added logic to specifically improve the tolerance of those drifts.

However what worked for one content failed for another, we quickly saw that we had to refactor that logic.
The new logic is now as follow:

  1. We take the segments already buffered
  2. We remove from those the segments we do not want anymore (often those of poor quality compared to what we can currently download, or those that are garbage collected)
  3. We now have a list of buffered segments, with possible "holes", which we will want to fill in
  4. We calculate from the Manifest the segments that are available
  5. From those, we remove those that are either already downloaded or who are not overlapping any of the "holes" in the buffer
  6. we put all of those segments in a download queue

This new logic has small subtleties which make it much easier to reason about: Before we wanted to know which individual segment was already downloaded to filter them out, now we look for filling in holes in the buffer.

We also found this new solution to work with much more case. We now notice significantly less cases of segment re-downloading for contents with a high drift.

Generating URLs in the demo

We introduced in the v3.15.1 the possibility to store contents locally in the demo.
This was to allow demo users to store their custom contents' configuration.

We now also introduce the possibility to easily share those custom and stored contents by allowing demo users to easily generate an URL for them.

This can be done by clicking on the link button, which appear only for custom and local contents.

As a bonus, those URLs were especially made to be:

  • readable: you shoud be able to see what the content is just by looking a the URL
  • "copy-pastable": URLs included in that URL (such as a Manifest URL) are not escaped
  • compact enough to be easily shareable by emails

We hope that this new demo feature will allow to improve debugging of the RxPlayer and/or the content themselves by facilitating the exchange of configurations.

CHANGELOG

Bug fixes

  • dash: update timeshiftBufferDepth considered when refreshing the MPD
  • dash: fix infinite rebuffering issue when refreshing a Multi-Period MPD with the oldest Periods removed
  • api: go to "SEEKING" state instead of "BUFFERING" when seeking while the player is in the "BUFFERING" state
  • api: Avoid reinitializing the video, audio and text track choice after a "RELOADING" state
  • api: When going back to a Period on which disableTextTracks was called, keep the text track disabled even if different preferredTextTracks are set
  • smooth: Replace {CustomAttributes} token in a segment URL
  • dash: load the last segment of a Period when it is declared in a SegmentTemplate (with no SegmentTimeline) and when its end is exactly equal to the end of the Period

Other improvements

  • dash/metaplaylist: be more tolerant with the appendWindows set as the previous behavior could lead to infinite rebuffering and segments re-downloading
  • dash/metaplaylist/smooth: Better handle discontinuities in a VoD content
  • dash/metaplaylist: Handle discontinuities between DASH Periods and between MetaPlaylist contents
  • dash/smooth: Avoid requesting multiple time the last segment when the duration given in the Manifest are inexact
  • smooth: Skip without throwing Manifest's StreamIndex with an unrecognized type
  • dash: Improve prediction of when to update a dynamic MPD with xlinks
  • dash: Be more tolerant of differences between a segment's time anounced by the Manifest and the reality to avoid multiple cases of segment re-downloading
  • dash: Guess initialization range for segments defined in a SegmentBase without an Initialization element
  • dash: Throw better error when a sidx with a reference_type 1 is encountered
  • api: Throw a better error when setting a preferredAudioTracks or preferredTextTracks value in the wrong format
  • demo: Allow to export and share demo links with custom contents
  • demo: Fix video track switching in the demo page
  • demo: Fix spinner not hiding when playing on very specific conditions

v3.16.0

16 Sep 13:22
836ffc0
Compare
Choose a tag to compare

Release v3.16.0 (2019-09-16)

Overview

The v3.16.0 has two big features and major improvements for some DASH use cases. It includes:

  • low-latency streaming for DASH contents (using chunked CMAF containers and chunk transfer encoding requests)
  • an experimental new feature - the MetaPlaylist - which allows to play multiple DASH and Smooth contents without interruption or black screen between them
  • A lot of improvements and bug fixes for Multi-Period DASH contents

Low-latency streaming

When playing live contents, a media player encounters generally the following conflict:

  • it wants to play close to the live edge, as that's what most users want. Especially for some specific contents (like sports or other live events).
  • it wants to be able to build enough buffer it front of the current position to avoid re-buffering as much as possible.

Where the former means keeping a small distance between the current position and the live edge, the latter means the exact opposite: the bigger the distance is the more buffer we can "build" in front of us.

On regular live contents, we have to make a compromise. Consequently, we usually start to play at about 10 or 15 seconds before the live edge.
This is fine for most live usage, but for some others it can be too high of a distance.

To reduce that delay, multiple streaming protocols recently defined solutions for what is often called "low-latency streaming".

This new RxPlayer version implements the main DASH way to resolve that problem: chunked CMAF containers and HTTP chunked transfer encoding.

To simplify what is done here: the segments announced in the MPD actually are composed of multiple sub-segments. While we fetch a single segment, new sub-segments become available progressively. Those can be pushed to be decoded in a media buffer while the segment it originates from is still being downloaded.

With this strategy, we both can begin to play more rapidly than before and we are also more able to avoid re-buffering situations.
More information on how DASH low-latency streaming is handled by the RxPlayer can be found in the new low-latency documentation page.

To be able to play low-latency contents actually close to the live edge efficiently, you will need to set the new lowLatencyMode boolean to loadVideo:

rxPlayer.loadVideo({
  url: "https://www.example.com/low-latency-content.mpd",
  transport: "dash",
  lowLatencyMode: true,
})

Note: Some DASH low-latency contents do not use a chunked-transfer-encoding optimization which let us download segments before they have been completely generated. On those, you might see multiple 404/415 HTTP errors for segment requests. If that's the case, you can disable the aggressiveMode transportOptions,
defined in the transportOptions documentation, this will disable that optimization.

The lowLatencyMode option is documented here.

MetaPlaylist or being able to play multiple contents without transitions

We added a new experimental feature which allows to play multiple DASH and Smooth VoD contents - encrypted or not - without any transition between them.
It works through a new transport and Manifest format - which we called MetaPlaylist.

This is a project we have been working on for more than a year. Now, we found it mature enough to share it with everyone.

There is a lot to say about that feature. As such it will be simpler to redirect you to the new metaplaylist documentation page for more information.

Please bear in mind that this is:

  1. An experimental feature, it can change at any new release
  2. A feature not included to a default build, you will have to add that feature in your code if you want to use it. More information on feature switching in the corresponding documentation.

Fixes and improvements for multi-Period DASH contents

Due to a change in the way the player computes the first and last available position, we had some issues with some live multi-Period DASH contents. Typically, if a Period was announced in the future, we considered the start of this Period to be the initial playback time, while there was still no segment available. The result would be a longer waiting time before the content was ready to begin.

We now updated this logic, to better guess the real availability boundaries of a content.

Furthermore, we also improved both transitions between Periods and the detection of the end of a content:
Where we previously mainly considered a Period's end for that kind of logic, we now also rely on the current and future available segments in that Period. The result is improved compatibility with multi-Period DASH contents.

add serverSyncInfos transport option

This new version also comes with a new option adding the possibility have a clock synchronized with the server's. This can for example be useful for low-latency contents, where we might rely on the user's system clock if the content does not indicate one.

To set that option you will need two value:

  1. The server's Unix timestamp at a particular point in time in milliseconds
  2. The DOMHighResTimeStamp relative to the time origin on the client's side at which that server's Unix timestamp was true.

More informations on serverSyncInfos can be found in the transportOptions API documentation.

aggressiveMode transport option now available for DASH contents

The aggressiveMode was previously a transportOptions only available for smooth contents which allowed the RxPlayer to load segments even if we weren't sure they had enough time to be generated on the server's side.

The effects are that you might have much more requests errors with it than without, but with the advantage of being able to buffer more segments in advance for some contents. Depending on your situation, the latter might be much more important than the disagreements provided by the former. This mode has been for example used to improve Peer-to-Peer sharing of media segments when playing Smooth Streaming contents.

We now also made the aggressiveMode also compatible with some DASH dynamic contents (to give more details it only has an effect for those with number-based segment indexing).

More information on aggressiveMode can be found in the transportOptions API documentation.

Note that currently, the lowLatencyMode implicitly enables the aggressiveMode for those DASH contents. This is because the low-latency DASH contents we support actually use the same strategy to reduce... latency.

Changelog

Features

  • dash: add lowLatencyMode loadVideo option to play low-latency DASH contents with chunk-encoded CMAF and chunked transfer encoding close to the live edge efficiently
  • metaplaylist: add the experimental metaplaylist transport, which allows to smoothly play a concatenation of multiple contents
  • api: add serverSyncInfos to transportOptions (loadVideo option)
  • errors: add code property to a NetworkError indicating the corresponding HTTP status

Bug fixes

  • dash: fix minimum time calculation for Multi-Period MPDs with SegmentTemplate segment indexes but no SegmentTimeline
  • dash: play static MPD not declaring any segment for a time of 0 seconds at the minimum possible time by default
  • dash: fix maximum position calculation for live Multi-Period contents where the currently generated period is not the last one declared in the MPD

Other improvements

  • api: authorize to set no url to loadVideo if the manifestLoader transportOption is set
  • smooth: the aggressiveMode option now only allows requests for segments which had time to at least begin to be generated to avoid too much HTTP 412
  • dash: the aggressiveMode now also have an effect for some SegmentTemplate DASH contents (download segments even if they're not finished)
  • code: add pre-commit and pre-push git hooks to automate checking and facilitate bisecting
  • dash: better handle live Multi-Period contents where the currently broadcasted period is not the last one declared in the MPD
  • dash: better infer the end of a Period if the start of the next one is defined
  • api: always start live contents at the live edge if one is defined and not just before the last available segments
  • ci: run integration tests with Travis again

v3.15.1

07 Aug 17:24
249a691
Compare
Choose a tag to compare

Release v3.15.1 (2019-08-07)

Overview

This release provides multiple bug fixes and small improvements:

  • due to a typo, the segmentRetry property (given to loadVideo through networkConfig) was not considered and the default value of 4 was always used instead.
  • when retrying a segment request, we first now check if the segment is still available, to avoid unnecessary requests on removed segments
  • we became more strict when calculating the minimum and maximum position of a live content, to avoid some left case of infinite rebuffering when the user is too far behind
  • the newly introduced (in v3.14.0) throttleVideoBitrateWhenHidden API now wait 60 seconds after the page is hidden, as documented, before switching to a lower bitrate (instead of doing it immediately)
  • we can now refresh the Manifest/MPD when a segment request unexpectedly lead to an error indicating that the segment does not exist yet/anymore

Avoiding unnecessary 404 requests

Live contents often have a "window" concept. They keep data up from a point in time, often corresponding to a set amount of minutes or hours behind the "live edge" (the last generated segments).

For example, let's say it is 04:00PM and that we're playing a live content with a 1 hour window.
In that case we will be able to download the content corresponding from what was broadcasted at 03:00PM up to what is broadcasted now, at 04:00PM.

This also means that the older segment are removed progressively, at the same rate than the live edge advances: 10 minutes later we will be able to play from 03:10PM to 04:10PM.

The RxPlayer already take this into consideration when doing segment requests, but we had one case left where we would request a segment that should be removed: when retrying a segment that was available the first time the request was done.

If we take back our previous example, let's say we're downloading the segment corresponding to 03:00PM. It is available the first time we make the request but the server is unexpectedly down, leading us to retry the request after some delay.
Let's imagine the server issue is fixed 5 minutes later (so at 04:05PM in our example). The window would have advanced by 5 minutes but as our previous retry logic did not take that into account, we would still request that same segment, which will not even be available anymore.

Before retrying, we now check if the segment is still available to avoid those unnecessary requests. If it is not, we just avoid doing that segment request.

Note that this fix shouldn't have much impact on the user, as in most of the concerned cases, their position would most likely be behind the first position announced in the Manifest. The way you could deal with such cases would be for example to seek at rxPlayer.getMinimumPosition() + SOME_SAFE_DELTA as soon as a "MEDIA_TIME_BEFORE_MANIFEST" warning is triggered.

Refreshing the Manifest/MPD on some segment requests errors for live contents

When playing a live content, we have multiple optimizations to avoid requesting the Manifest too often.

With Smooth streaming for example, we very rarely need to refresh it: we usually rely on ISOBMFF boxes instead (such as the tfrf and tfxd boxes) to learn about new segments and we clean-up old segments progressively.

Because we are refreshing the Manifest less often, we often have to guess about which new segments are available and about which segments have been removed since the last time we fetched the Manifest.

Most of the time, this logic goes well, but on exceptional conditions we could be requesting a segment that is not generated yet or has already been removed.
This is why we have now a supplementary safety logic: a Manifest request can now be done if a segment request fails with specific errors (404 for SegmentTimeline-based DASH contents and 404+412 for Smooth contents).

To avoid doing a lot of Manifest requests in a row, we keep an interval of 3 seconds between that request and the last Manifest request.

Avoid segment overlapping for DASH multi-Period contents

DASH contents are divided in one or multiple Periods, each having their own AdaptationSets (or tracks), each of which again having their own Representations (or qualities).

Those Periods correspond to different period of time, they can for example be linked to different programs of a whole live content/recording.

To handle more easily multi-Period contents, the RxPlayer create what it calls a PeriodBuffer for each Period it wants to download.

But it becomes more complicated than that: multiple PeriodBuffer can be active at the same time.
To obtain a gapless transition between Periods, for example, we could pre-load a future Period while still playing and considering the content of the current one. Both PeriodBuffers would be thus active at the same time:
Depending on the situation the first one could decide that it needs to download a segment, in which case the active downloads of the preloading PeriodBuffer would be interrupted - the current PeriodBuffer taking precedence over it.

All of that logic has been present since we officially supported multi-Period contents and it works fairly well.

However, a small edge-case would lead to multiple PeriodBuffer interfering with each other: overlapping segments.

This corresponds to the case where the first segment from a Period starts before the end of the previous one(s) or conversely where the last segment from a Period ends after the beginning of the following one(s).

When we are in this situation, downloading a segment from a later Period could lead the previous Period's segment being overwritten. As such, the latter (the first chronological Period between the two) would re-download the corresponding segment - overwriting in turn the new segment in place.
This would go in a loop and a LOT of segment requests could happen until all but the last PeriodBuffer concerned have finished playing.

To avoid that situation, we now make use of a browser features called append windows. With them, you can define the minimum and maximum bounds a given segment can reach once pushed. By limiting the effect of each segment to its respective Period's bounds, we can avoid such loop to happen.

Changelog

Bug fixes

  • api: fix networkConfig.segmentRetry loadVideo option. Due to a typo, it was forced to the default value (4)
  • api/abr: when the throttleVideoBitrateWhenHidden option is set to true, wait 60 seconds (as documented) after the page is hidden before switching to a lower bitrate
  • dash: fix segment indexing for SegmentList-based MPD with a period start different than 0

Other improvements

  • dash/smooth: check if the segment should still be available before retrying it (avoid unnecessary HTTP 404 errors)
  • dash/smooth: the Manifest can now be refreshed due to unexpected 404 HTTP errors on a segment request (only on particular conditions)
  • dash: better handle segments overlapping multiple periods by using the data that is only within the concerned Period's bounds
  • demo: authorize to play stored contents with an HTTP Manifest in the HTTPS demo

v3.15.0

24 Jul 17:00
c00eed1
Compare
Choose a tag to compare

Release v3.15.0 (2019-07-24)

Overview

The v3.15.0 brings multiple new features. Among them:

  • we improved our adaptive streaming logic. Among other things we now better consider networks with high latencies
  • the license-fetching behavior becomes much more configurable. You can now decide when the request should be retried, how much time maximum it needs to be, after how much time the request should "timeout" and a configurable error message on failure.
  • we profited from an error management refactoring to fix several error-related issues (see the changelog at the bottom of this note)
  • on our demo page, we added the possibility to save a custom content's information to local storage, to be able to retrieve it later
  • our build scripts are now compatible with macOS. A minor difference between GNU's sed utility and the BSD one led to some errors when the latter was used (it's the default sed implementation on macOS).

Adaptive improvements

Since the end of last year, we worked a lot on our adaptive bitrate algorithms, which choose the best possible quality according to the current streaming conditions.

One of the problem with our previous logic was how it behaved in unconventional networks. For example, we had some issues in regions with a very high network latency, where we would display a quality lower than what people there should expect.

Another problem is that we often stayed on a low quality to avoid as much as possible buffering, even when we had a lot of buffer in advance.

Our previous logic was only based on the calculated network bandwidth. We found that we could fix both of those problems by taking the current buffer size into account as well.

After some research, we decided to implement the BOLA algorithm - or more precizely a version of it adapted to our needs - in our adaptive logic. That algorithm basically decide which quality should be chosen depending on the current size of the buffer. The more filled the buffer is, the more it is considered as "healthy", and the more the player chooses a better quality.

We now use both a network-based approach and - when the buffers has grown enough - a hybrid network and BOLA-based algorithm.

We have already been doing that since the beginning of 2019 in a version specially released internally for specific regions. We now consider that the algorithm can be used everywhere - which is why we release it now for everybody.

A basic explanation of our adaptive algorithms can be found in the ABRManager architecture documentation.

getLicense configuration

We improved the control you can have over the getLicense callback, which is used to fetch a license.

Previously, when the callback returned a rejected promise, we retried to call it three other times before failing on error. Moreover, a timeout of 10 seconds was applied on that callback. After those 10 seconds, that getLicense invocation was automatically considered rejected.

This was however not ideal:

  • some applications could want to retry the underlying request less or more than 3 times (even never or infinitely)
  • some applications might not want to have a 10 seconds timeout (often either a higher timeout or no timeout at all)
  • some getLicense errors could mean that the content is not decipherable and as such, retrying the call should be unnecessary

To allow a greater control over how we're supposed to handle that callback, we decided to add the getLicenseConfig property to keySystems - at the same place getLicense is defined.

This property is an object which can contain two values:

  • retry (number|undefined): Number of times the getLicense call should be retried on rejection. 0 means no retry, Infinity means infinite retry. 3 by default
  • timeout (number|undefined): amount of time in milliseconds after which the call should be considered rejected and potentially retried. Set it to -1 to disable any timeout. 10000 by default.

Moreover, we even added the possibility to communicate through getLicense errors.

When getLicense rejects, you can now set the rejected value to be an Object (or an Error) with two properties:

  • noRetry (boolean|undefined): if set to true, we won't retry to call getLicense, stop the current content and emit immediately the corresponding KEY_LOAD_ERROR through an error event
  • message (string|undefined): if set to a string (note: it should be automatically equal to an Error's message for an Error instance), the corresponding warning (if getLicense is retried) or error event (if it is not) will have a KEY_LOAD_ERROR payload with that message.

All of this is documented in the keySystems documentation

Error refactoring

We refactored our error management, to use TypeScript for ensuring that our error API was respected.

When doing that, we found multiple incoherence which have since been fixed:

  • an undocumented PIPELINE_RESOLVE_ERROR code could be thrown (in some very specific undocumented cases that I guess are only considered internally) instead of the documented PIPELINE_LOAD_ERROR code.
  • the PIPELINE_PARSING_ERROR code could be thrown instead of the documented PIPELINE_PARSE_ERROR code
  • the ErrorCodes static property missed three error codes: NONE, INVALID_KEY_SYSTEM and INVALID_ENCRYPTED_EVENT

Content saving in the demo

You can now save your custom contents on our demo page. You will then have them stored and be able to play them again with the same configuration later on.

We make use of the local storage to allow retrieval of those contents even when the page is loaded again. To test it just go into our demo page, choose "Custom content" and the rest should - I hope - be straightforward.

Changelog

Features

  • eme: add getLicenseConfig property to the keySystems loadVideo option, to be able to have much more control over getLicense's behavior
  • eme: add noRetry to getLicense errors to abort retries when the licence request fails
  • eme: add message to getLicense and onKeyStatusesChange errors to allow custom errors when the license request fails
  • eme: add a new ENCRYPTED_MEDIA_ERROR with the code CREATE_MEDIA_KEYS_ERROR for when we cannot create a MediaKeys instance (seen on some Android devices).

Bug fixes

  • api: avoid sending {audio,video...}BitrateChange with a -1 value when starting to play a content
  • api/abr: a call to setAudioBitrate or setVideoBitrate could be ignored for a content if it was still loading. This is now fixed.
  • api/abr: a call to setMaxAutoBitrate or setMaxVideoBitrate could be ignored for a content if it was still loading. This is now fixed.
  • dash: fix maximum position calculation when refreshing a live MPD with a UTCTiming element and no SegmentTimeline.
  • dash/smooth: a MPD/Manifest request failing could still be retried when loading another content
  • eme/compat: on Safari, depend on WebKitMediaKeys even if MediaKeys is defined because of differences of implementations
  • pipelines: always send PIPELINE_LOAD_ERROR warnings when a segment request or a Manifest request is retried
  • errors: replace undocumented PIPELINE_RESOLVE_ERROR code into the proper documented PIPELINE_LOAD_ERROR code
  • errors: replace undocumented PIPELINE_PARSING_ERROR code into the proper documented PIPELINE_PARSE_ERROR code
  • errors: add to the ErrorCodes static property the previously forgotten NONE, INVALID_KEY_SYSTEM and INVALID_ENCRYPTED_EVENT codes.

Other improvements

  • abr: make use of another adaptive algorithm, buffer-based, when enough buffer has been built.
  • demo: allow the user to save custom contents to local storage to be able to reuse them when the page is refreshed
  • eme: throw a better error in onKeyStatusesChange if the Promise is rejected without an Error
  • errors: refactore error management to better correlate the fatal boolean to a playback stop and to better ensure a documented error is always thrown
  • scripts: make our build script compatible with MacOS (handle BSD sed)

v3.14.0

24 Jul 17:14
e321a54
Compare
Choose a tag to compare

Release v3.14.0 (2019-06-26)

Overview

This new release, despite what its numerotation might suggest, brings only minor fixes:

  • we added a throttleVideoBitrateWhenHidden player option which is better defined than the now deprecated throttleWhenHidden option for advanced usages such as Picture-in-Picture. Naming a new API was required to avoid breaking the current API.
  • we updated the limitVideoWidth player option to improve its behavior when Picture-In-Picture is enabled
  • we better handle the BUFFER_FULL_ERROR MEDIA_ERROR for devices with restricted memory
  • For dynamic DASH contents, we now treat MPD without a timeShiftBufferDepth as contents with an infinite buffer (or at least from the availabilityStartTime), as we should.
  • For live Smooth contents, we do the same thing for Manifests without a DVRWindowLength or one set to 0. Again, this is the expected behavior.
  • For dynamic DASH/Smooth contents that just began (less than 10 seconds ago), we now start by default at the minimum position instead of throwing a STARTING_TIME_NOT_FOUND MEDIA_ERROR.
  • Internally, we updated our end-to-end tests to perform real request on a real server instead of mocking all of those. This is closer to a real-life scenario and this also allows us to deploy those tests more easily on other devices.

Picture-In-Picture related fixes to throttleVideoBitrateWhenHidden and limitVideoWidth

We updated the behavior of two player options to better compose with picture-in-picture use cases.

On some browsers such as Chrome and Safari, you can request a video element to be displayed on top of the current page or over any other browser page, as a floating window.
This feature is called Picture-in-Picture.

The problem is that the behavior of two RxPlayer's options would lead to undesirable results when that feature is enabled:

  • limitVideoWidth, which throttle the current quality according to the current width of the video element - to avoid downloading content with an higher bandwidth which would lead to no visual difference.

    The problem here is that the limitation would still be relative to the video element even if the video is displayed not trough it but through a specific Picture-in-Picture window.

    We fixed the behavior to also take into account Picture-in-Picture.

  • throttleWhenHidden, which throttle the current bitrate when the page in which the video is is not displayed for more than a minute.

    The problem here is that we would throttle the bitrate even if the video is still visible in a Picture-in-Picture element, which is most often the opposite of what you want to do.

    Because just changing the behavior here would be a breaking change (the definition of throttleWhenHidden only talk about the current page being hidden), we instead decided to deprecate throttleWhenHidden and instead create a new API, throttleVideoBitrateWhenHidden which is better defined for current and future edge-cases.

    This new API is defined here.

Better BUFFER_FULL_ERROR management

When "pushing" new media chunks to be decoded, multiple RxPlayer errors can arise.

One of them, BUFFER_FULL_ERROR, indicate that the browser currently has not enough memory to store that new chunk.

The previous strategy when encountering that error was to remove some chunks currently buffered (but far enough from the current position) and retry to push the same chunk. This should not be necessary because the browser automatically perform cleaning on such cases, but we found this to not always be true.

However that strategy was not sufficient on some low-memory devices, and we would still throw a BUFFER_FULL_ERROR if the memory available is very low.

We now also dynamically reduce what we call the "buffer goal" for the current quality. This value, which starts at a configurable wantedBufferAhead seconds, is the size of the buffer the RxPlayer will try to reach. Once that amount of buffer ahead of the current position is reached, the player won't try to download new chunks.

By reducing that value each time that a BUFFER_FULL_ERROR is encountered, we reduce the amount of memory taken to store future chunks.

We now use both strategies: first we remove unneeded chunks and if that's not enough we reduce the buffer goal. We now should obtain much better results when very low on memory.

Play new live contents at their minimum position instead of throwing

Previously, live contents streaming from less than roughly 10 seconds would not be "playable": they would throw a STARTING_TIME_NOT_FOUND MEDIA_ERROR.

To avoid this error that doesn't make much sense in that context, we now play at the beginning of such contents instead.

This however can create issues in that you could be too close to the "live edge" (to what is being streamed now), and as such you could encounter rebuffering periods much more easily.

The current solution we provide is to play at the minimum position if the live content just began, and to let you - as a RxPlayer user - decide what to do once the content is loaded.

"Infinite" live buffer for DASH and Smooth Streaming

Live DASH and smooth contents both have in their respective "manifest" (MPD in the case of DASH), a property to describe the "buffer depth" of the current content. The buffer depth - also called dvr window - is the amount of "playable time" that is considered accessible at any point in time.

For example, if it's 09:10AM and we have 5 min of buffer depth, we will only be able to access contents from 09:05AM to 09:10AM. Content before 09:05AM will not be accessible anymore.

But what happens when this property is not set? We previously thought that this means no buffer depth (so only 09:10AM would be accessible in our case). But that's not what we were supposed to do.

In DASH as well as in Smooth, no buffer depth-related property means that the available content corresponds to every chunks generated from the beginning of the live content.
And that is what we've done in that release.

Integration tests now start its own content server

Our previous integration tests and "memory tests" were embedding directly the tested content. Any XMLHttpRequest would then be stubbed to redirect the request on the corresponding content.

This led to a very large testing build which moreover did not do any real-life request.

The new strategy is to create a content server and perform "real" XHRs instead.
Because requests could take a variable amount of time, we re-wrote a simplified version of sinon's FakeServer (which fakes request) but by adding it a lock and a promise-based flush API, allowing us to know when request have been answered and handled.

This is the first step towards having a build specifically designed to check our compatibility and avoid regressions with multiple devices, such as set-top boxes.

Changelog

Features

  • api/abr: add throttleVideoBitrateWhenHidden which unlike throttleWhenHidden does not throttle the video bitrate if the media element is in picture-in-picture mode

Deprecated

  • api/abr: deprecate throttleWhenHidden in profit of throttleVideoBitrateWhenHidden which has a better API definition for some edge cases

Bug fixes

  • api/abr: limitVideoWidth now also considers if the video is in picture-in-picture mode
  • buffer: better prevent the BUFFER_FULL_ERROR MediaError on some memory-constrained devices
  • dash: consider the buffer depth as infinite (until availabilityStartTime) if the timeShiftBufferDepth is not set
  • smooth: consider the buffer depth as infinite if the DVRWindowLength is not set or set to 0
  • init: start live contents that just began (less than 10 seconds ago) at the minimum position instead of throwing a STARTING_TIME_NOT_FOUND MEDIA_ERROR.
  • tests: use web server (local by default) instead of stubbed XHRs to serve tests contents to our integration and memory tests

v3.13.0

15 May 16:45
5d642fd
Compare
Choose a tag to compare

Release v3.13.0 (2019-05-15)

Overview

The v3.13.0 mainly brings minor fixes:

  • we removed most references to the user clock, to resolve an issue that could arise if people updated their system clock while watching a live content
  • we added a property called disableMediaKeysAttachmentLock to the keySystems loadVideo option, to get out of a deadlock when playing encrypted contents in some set-top-boxes
  • we added more default codecs to our MediaKeySystemAccess default configuration. Again to be able to play encrypted contents on more peculiar devices
  • we resolved some API problems for DASH multi-period contents

Limit reliance on the user's clock

To avoid synchronization issues, we try to avoid relying on the user's clock. However, we still stored internally several offsets relative to it.

One of these offsets was the presentationLiveGap. It is the difference between Date.now (which represents unix time) and the current last segment announced in the manifest.
Thanks to that variable, we could guess if new segments had time to be generated in the Manifest before refreshing it, Date.now acting here as a time counter since the time the Manifest was last downloaded.

That trick worked pretty well but there was still an issue we initially didn't consider: the user clock is not always linear. For example, two events can mess with our "time counter":

The second problem was already known but ignored. Leap seconds are rare enough and the disagreement small enough that it basically doesn't matter.

The first one however was less expected. Someone messing with its clock while playing a content could lead to bad situations, like buffering for a very long time.

The current solution found was to stop relying on Date.now which is too much linked to the user's clock, but to rely on performance.now as much as possible instead. performance.now is guaranteed to increase monotonically over time so it's a perfect match here.

disableMediaKeysAttachmentLock

We found an issue with our EME (DRM) implementation on multiple set-top-boxes:

To be able to play contents mixing encrypted and unencrypted media on Chrome, we had to add a lock, which waited for the media element to have decryption capability before pushing media segments to it.
In better terms, it means that the object that provide decryption capabilities (MediaKeys) had to be "attached" to the HTML5 video element before we begin to play.

However, we found that in some peculiar devices (like some set-top boxes) this can create a deadlock: the browser in those sometimes wait for some content to be loaded before validating this attachment.

To allow people to activate or disable this lock we added a disableMediaKeysAttachmentLock boolean to the keySystems option you can set to loadVideo.

You can use it if you experience troubles playing encrypted contents in a specific environment.

This option is documented here.

Changelog

Features

  • eme: add disableMediaKeysAttachmentLock key system option to bypass a deadlock (with possible tradeoffs) when playing encrypted contents on some peculiar devices

Bug fixes

  • dash/smooth: never rely on Date.now when calculating time differences to avoid issues when the user adjusts the system clock while playing a live content
  • eme: throw a better error (avoid toString is not a function messages) for a KEY_LOAD_ERROR when the getLicense function provided fails without a proper error
  • api: fix rare situation with DASH multi-period contents where we reported no available bitrate, Adaptation nor Representation when switching to another Period.

Other improvements

  • eme: add other default contentTypes when calling requestMediaKeySystemAccess to improve device support
  • demo: update the demo UI
  • code: change indentation style of a big chunk of the code to increase readability

v3.12.0

10 Apr 15:10
Compare
Choose a tag to compare

Release v3.12.0 (2019-04-10)

Overview

This new release brings multiple features:

  • dash: add support of some UTCTiming elements, to ensure the clients' clocks are all synchronized to the real live time
  • smooth: add aggressiveMode to the loadVideo transportOptions to request live segments closer to the live edge
  • dash/smooth: update manifestLoader API to allow more complex use cases (URL redirection, more precize manifest polling...) when using an external Manifest loader.
  • dash/smooth: define a referenceDateTime transportOption to set a custom reference starting time for live contents

UTCTiming support

The RxPlayer now supports some UTCTiming elements, which allows to fetch an accurate timing for DASH contents.

This is especially useful when a dynamic MPD rely just on a static SegmentTemplate to announce its segments. In that case, an unsynchronized client could fetch segments too much in the future or in the past, or just not be aligned with other clients, depending on its local clock settings.

For the moment, only UTCTiming elements with the following schemeIdUri are supported:

  • urn:mpeg:dash:utc:http-iso:2014: which triggers an http request to the linked server to fetch the clock.
    The request will only be done if the player absolutely needs it. That is, if both:

    • there's no other, more direct, way to get that timing
    • the MPD absolutely needs to have a synchronized clock (example: live contents without a single SegmentTimeline element).

    As such, we only perform a request if that's absolutely needed.

  • urn:mpeg:dash:utc:direct:2014: which contains directly the time informations

Other schemes are not supported, like urn:mpeg:dash:utc:http-head:2014 or those based on ntp, for simplicity reasons. As the former is still easily doable, do not hesitate to open an issue if its support is important to you.

Smooth aggressive mode

We now added an aggressiveMode property to the loadVideo transportOptions.

This allows to put back an optimization we had on previous RxPlayer versions for live contents, where we would infer which future segments will be available to finally request them before being even sure they had time to be generated.
That way, you will be able to have segments much more closer to the "live edge" of the content you watch.

The problem to that approach, which is also the reason why we choose to disable that optimization by default, is that you risk to have much more segment requests in error.
That is because requests for segments which did not had time to generate usually returns an HTTP 412 error which will trigger a NetworkError.

Depending on your other settings (especially the networkConfig loadVideo options), those errors might just be sent as warnings and the corresponding requests be retried. Another concept to keep in mind, is that that new behavior risks to pollute your cache depending on your caching strategy, as the first request from a client has a much higher chance to get an error response.

This option is documented here in the loadVideo options documentation.

referenceDateTime

The referenceDateTime was previously an undocumented smooth feature, exposed through the loadVideo transportOptions.

It allows to set a starting reference date, as a unix timestamp in seconds, for live contents.

For example let's imagine that a new live channel announce a time relative to its starting time, the 2019-04-10 at 00:00am (Video or audio segments will start with the time set at 0 at that time).
If we play segments for the 2019-04-10 at 00:01am (so one minute after the reference date), the RxPlayer will see a position announcing just 60s.

To be able to translate that back to the real live time (which it can use in various API, such as the getWallClockTime method), we will need an offset of some sort to add to that 60 time. That's the role of the referenceDateTime.
In our previous example, the referenceDateTime will be equal to new Date(2019-04-10) / 1000.

This new option is available for DASH and smooth contents. It is only used if we are in a live content, and the Manifest provide no way to infer that reference date (example: availabilityStartTime for DASH contents).

This option is documented here in the loadVideo options documentation.

Improved manifestLoader plugin

The manifestLoader is a property allowing users to provide their own logic for Manifest/MPD request. This has for example been used for Peer-to-Peer integration.

Our previous specification of that property was fairly simple.
However, we since added multiple new features to the RxPlayer and supplementary information related to the manifest request may be now used, such as:

  • Managing HTTP redirection, as the manifest request has possibly been redirected.
  • Advanced manifest/segment requests strategies, who exploit manifest sending and receiving time.

That's the reason why we now handle three new optional properties (url, sendingTime and receivingTime) from the manifestLoader response.

This is documented here in the plugins documentation.

Deploy demos and docs from our previous versions

To improve documentation and testing for legacy versions, we now store the demo and documentation page from our previous versions.

To refer to them, we created two new pages:

Both of those links have also been added to the README.md.

Logo

At last, we finally have a logo!

RxPlayer's logo

We had many logo propositions and as such it was very hard to choose. We thus first decided to postpone that choice, for an indefinite time!

After some months, we finally have done an internal vote and that is the logo that was chosen!
Many thanks to people sending us logos and to people voting on the one you prefer.

It has been added to the demo page and to the README.md.

Changelog

Features

  • dash: add UTCTiming support
  • smooth: add aggressiveMode transportOption to requests segments in advance
  • dash/smooth: add referenceDateTime transportOption to set a default reference time for live contents

Bug fixes

  • buffer: work around firefox bug leading to infinite rebuffering when seeking many times in a content

Other improvements

  • dash/smooth: add optional url, sendingTime and receivingTime properties in the response given by manifestLoader transportOption
  • misc: deploy documentation pages and demos from our previous versions
  • misc: add new RxPlayer logo to README.md and the demo

v3.11.1

11 Mar 15:39
42f3e61
Compare
Choose a tag to compare

Release v3.11.1 (2019-03-11)

Overview

The publish process of the previous release (v3.11.0) had an error which led to our npm package missing some files.
This was problematic when importing some features individually through the minimal version of the player.

This release just re-publish the rx-player and update the player version, without adding any other modifications.

To avoid this kind of error to happen again, all tagged/announced (on github) release of the rx-player from now will have their corresponding published npm package tested first.

Changelog

Bug fixes

  • npm: publish package again. An error in the previous release led to some files missing on npm

v3.11.0

07 Mar 19:30
Compare
Choose a tag to compare

Release v3.11.0 (2019-03-07)

Overview

This release mainly add two new features:

  • languages/api: add the notion of preferred audio and text tracks, much more flexible than the previous default track configuration.
  • allow playing a mix of unencrypted and crypted contents on Chrome. We're now able to do that at least in Chrome, Firefox and Edge (we did not test that usage on Safari yet).

It also adds multiple bug fixes and other little improvements, the most important ones being:

  • minimumUpdatePeriod DASH attributes were accidentally ignored, we manage that again
  • For smooth contents, you have now much less risk to encounter 412 HTTP requests
  • For TypeScript users, the addEventListener and removeEventListener are now completely type-checked. Each event is linked to its corresponding payload.
  • Each warning and error has now a readable string as their message attributes.

Language preferences

To permit much more control over language preferences, we added 4 new methods (setPreferredAudioTracks, getPreferredAudioTracks, setPreferredTextTracks and getPreferredTextTracks) and 2 new player options (preferredAudioTracks and preferredTextTracks).
Those take or return an array of audio or text track preferences (from the more preferred to the least preferred) and can be changed at any time: when a content plays as well as when no content is playing.

Those two specificities are particularly interesting for multi-period DASH contents, where you can have much more control over languages we automatically choose for subsequent periods.

Those new methods and properties and the corresponding behaviors are all defined in our documentation. Here are quick links for each of them:

Consequently, the defaultAudioTrack and defaultTextTrack loadVideo options are now deprecated. To help you switch from them to the new APIs, we added a chapter in our documentation about deprecated APIs.

Mix of crypted and unencrypted contents on Chrome

We previously were unable to switch from an unencrypted segment to an encrypted segment in Chrome. Those "mixed contents" would buffer indefinitely when doing that switch.

This was because our previous logic tried to perform DRM negociations lazily (only when DRM is needed):
When first playing an unencrypted content we did dot initialize encryption-related logic, this was done just after the first encrypted segments was downloaded.

This strategy sound smart on paper but did not work with how Chrome implemented those APIs. We thus now begin to perform that logic immediately when starting the content (usually at the same time at which we fetch the manifest file) which is what most browsers expect. This behavior works at least with Chrome, Firefox and Edge. Safari was not tested, please let us know if you have related problems on it.

Note: This initialization only happens if the keySystems loadVideo option contains at least one key system, to avoid doing that unnecessary logic for contents we know to be unencrypted.

Less 412 HTTP errors for segment requests with Smooth streaming contents

After our last versions were deployed on multiple Canal+ applications, we saw a huge bump in our request error metrics: a lot of our segment requests were failing with a 412 HTTP error for HSS (Smooth Streaming) contents.

This usually means that we are requesting a segment before it was actually generated on the server-side.
This can be provoked by inferring future segments from the metadata of previous ones, and then requesting them too soon.

We now avoid those errors by doing a better estimation on which segments should be available on the server. This is done by considering the last manifest we downloaded and calculating the amount of elapsed time since.

As an example, we won't download a segment ending at a timestamp of 50 seconds if a manifest downloaded 10 seconds ago ended with a segment at a timestamp of 30 seconds (the max on the server now would be 30+10=40s).

addEventListener and removeEventListener type safety

We worked on improving type safety on the player in this release. If you're using TypeScript, you could now have type errors with addEventListener and removeEventListener depending on how they were used: the payload type is now correlated with the event string.

This brings several advantages:

  • your calls will now be type-checked. You should thus detect more errors if you are using those methods wrongly
  • depending on your editor and plugins, you could now have a nice contextual auto-completion when writing event listeners

Five new player events

Speaking of events, we added five player events we thought were missing. Those are (linked to their corresponding documentation):

Changelog

Features

  • languages/api: add preferredAudioTracks and preferredTextTracks player options
  • languages/api: add setPreferredAudioTracks, getPreferredAudioTracks, setPreferredTextTracks and getPreferredTextTracks methods
  • languages/api: add availableAudioTracksChange, availableTextTracksChange and availableVideoTracksChange events
  • abr/api: add availableAudioBitratesChange and availableVideoBitratesChange events
  • eme: allow playback of mixed encrypted and unencrypted contents on Chrome
  • types: export the new IAudioTrackPreference and ITextTrackPreference types

Deprecated

  • languages/api: deprecate the defaultAudioTrack loadVideo option in favor of the preferredAudioTracks player option.
  • languages/api: deprecate the defaultTextTrack loadVideo option in favor of the preferredTextTracks player option.

Bug fixes

  • dash: fix minimumUpdatePeriod management for DASH contents
  • smooth: better prevent 412 HTTP errors for smooth streaming contents
  • subtitles: ensure subtitles are not visible in Firefox when disabling them in the "native" textTrack mode.
  • errors: avoid sending multiple MEDIA_TIME_BEFORE_MANIFEST or MEDIA_TIME_AFTER_MANIFEST warnings instead of just one
  • api: fix (deprecated) option hideNativeSubtitles

Other improvements

  • errors: set a readable error message for every error and warnings thrown
  • tools/mediaCapabilitiesProber: set logs about unimportant missing APIs as debug-level instead of warn-level
  • types: provide type safety to addEventListener and removeEventListener

v3.10.3

30 Jan 17:59
3b52da5
Compare
Choose a tag to compare

Release v3.10.3 (2019-01-30)

Overview

This release brings multiple minor fixes:

  • dash: fix getMinimumPosition API for live contents
  • webvtt: default classes are now supported. Multiple webvtt-related fixes have also been brought.
  • smooth: we are much more tolerant on the ISOBMFF segments downloaded
  • subtitles: fix bug which could prevent some subtitles from being removed (e.g. when disabling them)
  • abr/buffers: fix a remaining delay in quality changes happening in rare situations (like when updating the maxVideoBirate very quickly multiple times)
  • a lot of other minor fixes (see the Changelog below)

We also continued our efforts on improving unit test coverage by going from a code coverage of 22% in the v3.10.2 to 33.6% now. As written in our previous release, we still target a code coverage as close to 100% as possible to improve our confidence on the new code we release.

fix getMinimumPosition for DASH contents

In the v3.10.1, we improved the code which calculates the minimum position of a manifest. However when doing so we misused an attribute of an MPD, availabilityStartTime.

This little error made the getMinimumPosition API return inexact value when the availabilityStartTime was not set to the unix epoch (1970-01-01 00h00m00s). This API is often used to detect where a user can seek in the current content.

This same issue could also lead the player to send multiple MEDIA_TIME_BEFORE_MANIFEST warnings when it shouldn't be the case.

To prevent the same issue to happening again, new tests have been added with the concerned situation.

WebVTT improvements

We now support default WebVTT classes. These classes (which are text colours and background text colours) can be applied to cue elements on WebVTT, without the need to redeclare the associated styles in the subtitle file.

We also brought several minor fixes (described in the Changelog at the bottom of this release note), which should improve the support of more sophisticated WebVTT :

  • If several style blocks define styles for the same class or tag, all styles are now considered (not only the last), and so applied on the HTML element.
  • If a cue was concerned by both global and class styles, there was a mismatch between the applied styles and the concerned classes/tags. This was leading to the inconsistent visual aspect of HTML subtitles. This is not happening anymore.
  • Before, we removed whitespaces from declared CSS. This was leading to badly formatted styles (to bottom became tobottom). We don't replace whitespaces anymore and apply parsed CSS as it is in the WebVTT.

With those improvements, we also took the opportunity to add unit tests of the webvtt-to-html parser, to fully cover this part of the code.

Smooth segment tolerance

We previously had a very rigid management of ISOBMFF (.mp4) segments when using the "smooth" transport type. Segments which had some metadata in another order than what was expected were often rejected.

To improve our flexibility with such contents, we updated our logic to make much less assumption about how the segments should look like. The player should now handle any segment as long as it is a correctly formed ISOBMFF media (i.e. not incoherent and not missing crucial informations).

...And more

We brought a lot of other minor fixes in that release. This is most notably due to our improvements in testing-related matters. Because it's a good thing, we plan to continue our efforts towards unit test coverage and integration tests.

You can read the Changelog for more information on the corresponding fixes.

Changelog

Bug fixes

  • dash/api: fix getMinimumPosition for MPDs with an availabilityStartTime superior to unix epoch
  • smooth: be more tolerant on downloaded segments (accept ISOBMFF with boxes in any order)
  • buffers/abr: fix issue infrequently leading to a delay in quality changes
  • buffers: improve synchronisation to the SourceBuffer's buffer to avoid cases where the same segment could be downloaded multiple times
  • subtitles: fix bug in the clean-up logic of subtitles in the "html" texttrack mode that would lead to removed subtitles still being displayed
  • compat/subtitles: work-around firefox issue in the "native" texttrack mode to ensure track cues are removed when the content is stopped
  • subtitles/webvtt: support default classes in the WebVTT specification
  • subtitles/webvtt: multiple styles for the same element are now merged into one (instead of considering only the last one)
  • subtitles/webvtt: fix styling issues when both styles applied globally and styles applied on a selector are defined
  • subtitles/webvtt: do not remove whitespaces in styles to keep a sane formatting for some complex values

Other improvements

  • dash: warn through logs when fields are not in the expected format
  • drm: throw more explicative error messages when DRM are not supported in the current target
  • dash/smooth: get more precize duration from ISOBMFF by better handling the default duration taken from the tfhd box
  • tests: continue unit test coverage improvements (from 22% in the v3.10.2 to 33.6%)
  • demo: fix initial text-track selection