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)