Releases: canalplus/rx-player
v3.16.1
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 Period
s 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:
- 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.
- It then looks at the segments already buffered.
- 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.
- 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:
- We take the segments already buffered
- 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)
- We now have a list of buffered segments, with possible "holes", which we will want to fill in
- We calculate from the Manifest the segments that are available
- From those, we remove those that are either already downloaded or who are not overlapping any of the "holes" in the buffer
- 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 differentpreferredTextTracks
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
orpreferredTextTracks
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
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:
- An experimental feature, it can change at any new release
- 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:
- The server's Unix timestamp at a particular point in time in milliseconds
- 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
totransportOptions
(loadVideo
option) - errors: add
code
property to aNetworkError
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
toloadVideo
if themanifestLoader
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
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 toloadVideo
throughnetworkConfig
) was not considered and the default value of4
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 Period
s, each having their own AdaptationSet
s (or tracks), each of which again having their own Representation
s (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 PeriodBuffer
s 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
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 defaultsed
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 thegetLicense
call should be retried on rejection.0
means no retry,Infinity
means infinite retry.3
by defaulttimeout
(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 totrue
, we won't retry to callgetLicense
, stop the current content and emit immediately the correspondingKEY_LOAD_ERROR
through anerror
eventmessage
(string
|undefined
): if set to astring
(note: it should be automatically equal to an Error's message for an Error instance), the correspondingwarning
(ifgetLicense
is retried) orerror
event (if it is not) will have aKEY_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 documentedPIPELINE_LOAD_ERROR
code. - the
PIPELINE_PARSING_ERROR
code could be thrown instead of the documentedPIPELINE_PARSE_ERROR
code - the
ErrorCodes
static property missed three error codes:NONE
,INVALID_KEY_SYSTEM
andINVALID_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 thekeySystems
loadVideo
option, to be able to have much more control over getLicense's behavior - eme: add
noRetry
togetLicense
errors to abort retries when the licence request fails - eme: add
message
togetLicense
andonKeyStatusesChange
errors to allow custom errors when the license request fails - eme: add a new
ENCRYPTED_MEDIA_ERROR
with the codeCREATE_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
orsetVideoBitrate
could be ignored for a content if it was still loading. This is now fixed. - api/abr: a call to
setMaxAutoBitrate
orsetMaxVideoBitrate
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 documentedPIPELINE_LOAD_ERROR
code - errors: replace undocumented
PIPELINE_PARSING_ERROR
code into the proper documentedPIPELINE_PARSE_ERROR
code - errors: add to the
ErrorCodes
static property the previously forgottenNONE
,INVALID_KEY_SYSTEM
andINVALID_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
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 deprecatedthrottleWhenHidden
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 theavailabilityStartTime
), 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 deprecatethrottleWhenHidden
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 unlikethrottleWhenHidden
does not throttle the video bitrate if the media element is in picture-in-picture mode
Deprecated
- api/abr: deprecate
throttleWhenHidden
in profit ofthrottleVideoBitrateWhenHidden
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 thetimeShiftBufferDepth
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
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 thekeySystems
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 user updates its local clock
Date.now
assumes that there's always 86,400,000 milliseconds per day, as such leap seconds are ignored
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 aKEY_LOAD_ERROR
when thegetLicense
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
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 theloadVideo
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!
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
andreceivingTime
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
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
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
andremoveEventListener
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:
preferredAudioTracks
player optionpreferredTextTracks
player optionsetPreferredAudioTracks
methodgetPreferredAudioTracks
methodsetPreferredTextTracks
methodgetPreferredTextTracks
method
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):
availableAudioTracksChange
availableTextTracksChange
availableVideoTracksChange
availableAudioBitratesChange
availableVideoBitratesChange
Changelog
Features
- languages/api: add
preferredAudioTracks
andpreferredTextTracks
player options - languages/api: add
setPreferredAudioTracks
,getPreferredAudioTracks
,setPreferredTextTracks
andgetPreferredTextTracks
methods - languages/api: add
availableAudioTracksChange
,availableTextTracksChange
andavailableVideoTracksChange
events - abr/api: add
availableAudioBitratesChange
andavailableVideoBitratesChange
events - eme: allow playback of mixed encrypted and unencrypted contents on Chrome
- types: export the new
IAudioTrackPreference
andITextTrackPreference
types
Deprecated
- languages/api: deprecate the
defaultAudioTrack
loadVideo
option in favor of thepreferredAudioTracks
player option. - languages/api: deprecate the
defaultTextTrack
loadVideo
option in favor of thepreferredTextTracks
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
orMEDIA_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
andremoveEventListener
v3.10.3
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
becametobottom
). 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