Skip to content

Releases: canalplus/rx-player

v3.19.0

11 Mar 16:37
ffbca09
Compare
Choose a tag to compare

Release v3.19.0 (2020-03-11)

Overview

The v3.19.0 brings several new improvements:

  • the RxPlayer will now be able to consider multiple URL per segment (for DASH contents)

  • we've made several improvements related to the performance of the RxPlayer's Manifest parsing logic

  • we've added yet again another external tool (still experimental) called createMetaPlaylist which allows to easily concatenate multiple DASH/Smooth (or even MetaPlaylist!) contents into a single MetaPlaylist content

  • the RxPlayer now exports more types (for TypeScript) related to audio/video/text track choices, with 6 new types

  • we fixed several minor issues. Most being related to recently released experimental tools and features

Multiple URLs per segment

DASH's MPD can declare multiple URL per segment through the declaration of multiple BaseURL elements.

This allows for example to fallback to another CDN when the main one become unavailable. It may also sometimes be used when a first source might not have all media segments yet.

The strategy taken by the RxPlayer when encountering multiple URL linked to a segment is the following:

  1. It will try to request the first URL (the one described by the first BaseURL element encountered in the MPD)
  2. If it fails, it will immediately request the second URL
  3. If it also fail, it will immediately request the third one, and so on
  4. If all URL fails, the usual retry logic starts: depending on your configuration, the RxPlayer will wait a small delay - which will grow exponentially each time the batch of requests fails - and after that delay it will try again to request each URL, one immediately after another (it's actually a little smarter than that, only requests which have failed with an error considered as retry-able will be retried. e.g., an URL returning an HTTP 403 Forbidden will not be retried).
  5. If all requests have failed and either:
    - we have done the maximum number of retry configured
    - or all URLs failed on an error that the RxPlayer judged as not retry-able (like a 403)
    Then, the player will stop on error and emit the usual PIPELINE_LOAD_ERROR error event

So basically, the usual retry logic is not done between different URL (as those will usually be linked to different CDNs). All requests are batched together and the retry logic is done when all this batch fails.

This was done as it seems the most logical way to go with such use cases.

Please open an issue if that behavior does not suit you. In that case we will see whether we could update it or at least add supplementary configuration options to adapt to it.

Manifest parsing improvements

Many changes in this release are linked to performance improvements on the RxPlayer's Manifest-parsing logic.

We tried to:

  1. reduce the time taken by the RxPlayer to parse a Manifest
  2. do that parsing logic less often
  3. split that work in multiple parts, so we avoid blocking the main thread for too long

but why?

We were confronted to the need to improve on those points because we were having performance issues on some "embedded" devices (more specifically on devices with low memory such as low-end set-top-boxes and limited devices like the ChromeCast).

This was also linked to the fact that Canal+ Group - the company which started the RxPlayer development and which employs the main RxPlayer contributors - distributes live DASH contents with a pretty big timeshift window (usually set to 8 hours).

We think that huge Manifests like those is in fine a good thing for end users, because it means an increased possibility to seek in previously broadcasted contents - contents which may not be available elsewhere.
Moreover, we just feel that it should be part of our job to better handle large contents without issues - much like we consider it would be part of the job of a text editor to handle large enough text files while still being fully usable (not thinking about any editor in particular here, this is just a - perhaps loosy - analogy).

After this long introduction to why we did some improvements let's jump into what we did.

Doing updates through a "shorter" Manifest through manifestUpdateUrl

One of the most effective change we did to reduce Manifest parsing time (more specifically update time for live contents) was to allow the consideration of two version of the Manifest for the same content:

  1. The regular full Manifest
  2. A shortened version of it with a smaller timeshift window

This sadly means that, to profit from that improvement, you will surely need to update the packaging logic of your streams.
But rest assured: the content does not have to be re-encoded. Both Manifest stay linked to the same content, just the size of their timeshift window change (such as DASH's timeShiftBufferDepth attribute).

In the RxPlayer, the first version of the Manifest (the full one) will in most cases only be used to perform the initial load of the content. It also may need to be requested at other times, e.g. when the RxPlayer thinks that its internal Representation of it is completely out-of-sync, but those cases are very rare and will in most cases not happen.

For regular Manifest updates, if needed, the RxPlayer will only request the shorter version.
Because it will still have information about newly generated segments from it and because the RxPlayer can just "guess" which old segments are not available anymore, no information will be lost compared to when the RxPlayer use the full version instead - as it did until now and still do if no short version of the Manifest is provided.

This optimization has a huge effect on performance. To simplify, we could say that the more there is a size difference between the full and the short version, the more that improvement grows.

Still, it does not improve the initial load time, as it only impacts Manifest updates.

If you want to profit from that optimization, you will have to set in loadVideo the new transportOptions property called manifestUpdateUrl. Every properties from transportOptions are documented here

DASH "lazy-parsing"

The RxPlayer will now only parse some parts of a DASH MPD at the time it needs them.
For example, only Period that it plays and only AdaptationSets (tracks) that it needs will be parsed, the rest won't be until we switch to the corresponding Period or AdaptationSet.

We called this behavior "lazy-parsing" as it is inspired from the lazy evaluation concept we can find in programming languages like Haskell.

It is generally beneficial but has some setbacks:

  • the RxPlayer still needs a lot of information at parsing time, meaning that in many cases the improvement will be minimal (for example, it might need to parse every SegmentTimeline element in the last Period to calculate the available "edge" of a live content)
  • the code become less predictable when it comes to performance issues. We could now be in a case where a simple harmless function call can trigger that lazy-parsing and lead us to several seconds of parsing (in the worst cases)

We found that this new behavior provided the most improvements on contents with multiple Periods. The more Periods a DASH MPD contained, the better the improvement was.

Also, we saw visible improvements with both the initial loading time as well as Manifest updates. This also can have an effect on VoD contents (as long as those have multiple Periods, which is usually not the case).

As this feature is always on, there is no option linked to it. As long as you have the right version, you will profit from it.

Adaptive delay between Manifest updates

While investigating what we could do to improve our performance on embedded device. we at first looked at what other DASH video players were doing.

One interesting thing we were seeing is that the shaka-player, another open-source player, didn't have as much trouble playing Canal+ DASH streams than the RxPlayer had.
The curious thing is that, by looking at their MPD-parsing logic, we didn't see much differences to ours in terms of efficiency. The improvement had to be from elsewhere.

After several false lead on how they were doing it, we finally spotted the main reason: a reduction in the frequency of MPD updates. On the shaka-player, when parsing the MPD took too much time, the next updates could be postponed. For example, instead of refreshing our Manifest every 2 or 3 seconds (a low interval, but that's how our contents were), they were on some devices doing it every 12 seconds.

This was a pretty smart thing to do.
On some devices, the RxPlayer spent almost all its time parsing the MPD, leading to visible repercussion on content playback (which appeared jerky), the delay after which user interactions were taken into account and the stability of the device as a whole.

The RxPlayer has now integrated a similar logic, where long parsing time can raise the delay we will wait until we do the next Manifest update.

What's next?

All those improvements lead to a much better experience on low-end devices.
But we can still do better, most notably for the initial loading time which can still be long for some type of contents.

To improve in that regard, we are still doing experimentation.
For example we're looking if we can even improve the impact lazy-parsing can have and even the possibility of using webassembly on the Manifest-parsing logic.

This is however still in an experimental stage and we cannot tell with certitude that such features will be available in future versions.

createMetaplaylist tool

A new experimental tool, createMetaplaylist has bee...

Read more

v3.18.0

30 Jan 19:36
549aa38
Compare
Choose a tag to compare

Release v3.18.0 (2020-01-30)

Overview

The v3.18.0 is a big release with its share of important new features:

  • we improved our support of directfile contents by now making them compatible with most of our track management APIs

  • we added two events: "seeking" and "seeked" which are triggered each time that a seek began and finished respectively, even when the seek was done to an already buffered part.

  • we added a new possible transport, called "local". It allows to play downloaded DASH, Smooth or MetaPlaylist contents.

  • we added a new external tool, the TextTrackRenderer, which allows to parse and display subtitles on top of a video element.

  • we added another external tool, parseBifThumbnails, which parses BIF files (thumbnails containers)

  • we improved our TTML subtitles support (complex style inter-inheritance, correct handling of rgba colors)

  • we should now be easier to import from node (if you want to include this library in your server-side-rendering strategy)

To prepare a potential "v4", we also deprecated some APIs (see corresponding chapter below).

You might have guessed it, this is going to be a very long release note :-o

Deprecated APIs

We'll begin with the sad part. We have deprecated a small number of APIs, which means that they will remain supported until their deletion which will coincide with a future major release - probably the v4.0.0 (no date for that one for now, but we begin to think about it).

The following loadVideo options have been deprecated:

  • supplementaryTextTracks, which can be replaced by using the new TextTrackRenderer tool (described below)
  • supplementaryImageTracks, here you can use the new parseBifThumbnails tool instead.

Speaking of image APIs, the getImageTrackData method has also been deprecated as well as the imageTrackUpdate event. For now, thumbnails management has entirely been moved out of the RxPlayer. Most of those can still be replaced by doing a regular HTTP request and a call to the new parseBifThumbnails function.

Lastly, we deprecated the hideNativeSubtitles loadVideo option, due to it being close to useless since we allow for the "html" textTrackMode also as a loadVideo option.

Every information on why those were deprecated and how to replace them is available in the corresponding documentation page.

Improvement of directfile API support

Before this release, multiple RxPlayer properties, methods or events had no effect when playing a directfile content:

  • getAvailable{Video,Audio,Text}Tracks always returned an empty array
  • get{Video,Audio,Text}Track returned undefined
  • set{Video,Audio,Text}Track had no effect
  • the track change events available{Video,Audio,Text}TracksChange and {video,audio,text}TrackChange were never emitted
  • setPreferred{Audio,Text}Tracks and the corresponding constructor options had no effect
  • getPreferred{Audio,Text}Tracks returned an empty array

This is because [audio, video or text] track management when playing a content in a streaming protocol (like DASH or Smooth) is radically different than when playing directly a single file:

  • In a streaming protocol, it is mostly a task performed by the RxPlayer
  • In directfile mode, the RxPlayer has to rely on native browser APIs (through these native API, you can get and set native tracks, as well as receive events on track removal/addition/change).

The RxPlayer was mostly used with streaming protocols. As such, we didn't take the time yet to plug those native APIs to the RxPlayer's methods and events.
However, we realized that it was becoming more and more used in an hybrid mode, playing both type of contents (HLS being supported in directfile mode on Apple browsers is one of the reasons why).

That's why we've now connected the RxPlayer track APIs and events to these native track API.

From now, only on browsers that offer the right native APIs, you should be able to load directfile contents with several tracks and manage them. The player will now also take the preferred tracks settings into account.

Please note that if those native APIs are not present in the current browser, those features will still have no effect.
Sadly, we found that chrome and firefox (if the right flags were not enabled) only provided text tracks APIs, which means that switching audio or video tracks on those might still not work.

But to end on a good note, you should now be able to play HLS contents on Safari and benefit from RxPlayer track management.

seeking and seeked events

We added two new events "seeking" and "seeked", which are triggered respectively when the player begin a "seek" operation (it moves to another position) and when the player finished that operation.

Before those events, an application had to listen to the playerStateChange events to check when the state went in and out of the SEEKING state for the same kind of indication.
However, the player might not switch to the SEEKING state if a seek is done in an already-buffered part of the content. This is because, in those cases, the seek could be instantaneous (and as such, the player would stay in its PLAYING or PAUSED state).

local manifests: Playing downloaded contents

We've worked for some time on the ability to download DASH, Smooth or Metaplaylist contents and play them afterward.
This could be useful for example to be able to play those contents when offline.

We splitted that feature into two tasks:

  1. a tool, independent to the RxPlayer, to download and store contents
  2. the possiblity to play those downloaded contents through the RxPlayer

In this release we bring the second part, which means that you can now play contents stored locally (or anywhere, really), as long as you create the right custom manifest format, called the "local manifest".

More information on this feature can be found in the corresponding documentation.

Please note that we're still working on a default implementation of a content downloader to work with it.
It will take the form of a tool, and will rely by default on IndexedDB, a native browser API allowing to store large amount of data in the browser. If you want to take a look at what it will look like, you can follow the corresponding PR: #548.

TextTrackRenderer: Displaying synchronized subtitles on top of any content

The TextTrackRenderer is a new tool which allows to parse subtitles in various formats (srt, TTML, webVTT or SAMI) and display them on top of a video element (even when the content is not played through the RxPlayer).

This tool is completely independent of the RxPlayer and can be used alone. You can look at its documentation here.

You can use this tool as a replacement of the supplementaryTextTracks loadVideo option

parseBifThumbnails: parse BIF files (thumbnails containers)

Another tool, parseBifThumbnails, has also been added in this release.

This tool is a simple function which can parse BIF files.
BIF files are simple containers for video thumbnails, it is usually used to provide an indication of where the user will seek when he/she/it hovers the progress bar.

This same format is used at Canal+ as well as Netflix, Amazon Prime or Disney+ as far as we know.

The parseBifThumbnails is totally independent of the RxPlayer and can be imported without it. Its documentation has been added here.

This tool can replace most deprecated image APIs

Importing us from node

Before this release, there was an issue when importing the library in node, a strategy sometimes used on applications with server-side rendering (SSR).

This is because of two elements:

  1. The RxPlayer depend on the window object in the global scope to initialize some browser-specific constants that it will need to use often.
    In node.js, the window object is not defined. As SSR rely on node.js to pre-calculate the first rendering step, it could break if the RxPlayer was in some way imported here.

  2. Many exported files are written with ES6-styles imports and exports. This is usually not a problem as most module bundler process those without any issue but it became one when node.js was directly used without a bundling step before. Node.js usually rely on another style for imports and exports, the "commonJS" style.

We've decided to work around those issues, thus improving our compatibility with SSR.
From now, the RxPlayer finds out if it is in a node environnment, by detecting the lack of window in the global scope. In that case, none of the browser compatibility strategies will be applied and the RxPlayer will stay in a mode with reduced features.

For the second problem, we could theoretically fix it in several ways: generate CommonJS imports and exports instead or signal that we were "JavaScript modules" in some ways seem to be possible solutions.
However, as we didn't have time to check on those possibilities for now. we advise people in SSR scenarios to use the esm npm module for now, as a quick and easy solution.

Changelog

Features

  • directfile: support most audio tracks API when playing a directfile content
  • directfile: support most text tracks API when playing a directfile content
  • directfile: support most video tracks API when playing a directfile content
  • api: add seeking and seeked events which anounce the beginning and end of a seek, e...
Read more

v3.17.1

20 Dec 16:37
e8a9306
Compare
Choose a tag to compare

Release v3.17.1 (2019-12-20)

Overview

The v3.17.1 is a small release containing minor bug fixes:

  • we fixed an issue which affects mostly live DASH multi-period contents, when the MPD is refreshed

  • we fixed a subtitles issue which can arise when manual garbage collection is enabled through the maxBufferBehind or maxBufferAhead options

  • we fixed issues with Directfile playback on iPhones, iPads and iPods.

  • lastly, we added some demo improvements

Fixed issue with dynamic multi-Period MPD

DASH' Manifest, the MPD, has a concept called "Period" which basically allows to divide a content into multiple sub-contents, each having its own tracks and available qualities.

When playing a dynamic DASH content - typically a live stream - the RxPlayer might have to refresh the MPD to know about the new available segments.
When that happens - and when the MPD has multiple Periods - the player has many tasks to do behind the scene:

  • for Periods that it already knew about, it has to extract the new information obtained
  • for Periods that weren't known of previously (e.g. a new program), it has to add it to its internal representation of the MPD
  • for Periods that aren't here anymore (e.g. a program which is now too old, or which has been removed), it has to remove it from that same internal representation

Because the RxPlayer accounts for very rare cases, like Period inserted in-between previously known Periods, the logic behind MPD updates is easy to get wrong.

And that's exactly what happened! We detected a bug when some old Periods were removed which led toward the RxPlayer not updating later Periods, ultimately leading to an infinite rebuffering stream. The user could get out of this situation by seeking far away of the current position, but that was not an acceptable resolution of the issue!

Bugs in that area is not something that is new, and we already had a very similar issue resolved in the v3.16.1. We fixed that new one and added more unit tests on top of it for previously untested cases. We hope that this is the end of such bugs!

Less aggressive subtitles garbage collection

As the RxPlayer is present on memory-constrained devices, we had to include memory-related options and methods in our API.

More specifically options linked to media garbage collection. What we mean by that is the automatic removal of media segments which are either already played or considered too far ahead from the current position.
This is generally the job of the web browser, but we still found that logic insufficient on some devices.

This is the reason why we added the setMaxBufferAhead and setMaxBufferBehind methods, which set the amount of time in seconds the RxPlayer should keep in its buffer (removing everything else), in our v2.3.0 (in mid-2017!).

When looked upon in the API, those methods just announce that they are "removing buffer". What we really mean here is that the RxPlayer will be removing ALL type of media from its buffer: audio, video and subtitles.

Audio and video is for the most part handled by the browsers, the RxPlayer here just has to call a remove method with the right arguments and that's all.

For subtitles however, the situation is more complex:

  • Either we're on the default native textTrackMode where text tracks are handled by the browser, in which case there were no problem
  • Either we're on the more powerful html textTrackMode where text tracks is entirely handled by the RxPlayer.

When in that second situation, we had an issue if garbage collection was enabled.

Depending on multiple factors, such as the amount garbage collected, the size of the segments containing subtitles (basically the more garbage collection was aggressive and the longer the segments last the more "chance" you had to see the bug) and the actions of the users, the RxPlayer could decide to not replace garbage collected subtitles once they are needed again.

We fixed that issue in two ways:

  1. We're less aggressive when garbage collecting subtitles.
    Before, we removed all text cues as soon as it overlapped with the interval we had to clean, even when the amount of time it had to be displayed continued out of that interval. This could lead to the removal of subtitles that were in fact still useful. Now, we only remove those cues once they are entirely contained in that "cleanable interval".

  2. We now handle some forgotten edge cases when re-adding subtitles on top of garbage collected one.
    Basically, we could have to re-download a partially or totally garbage collected segment if we need it again. This action of adding again a segment on top of a partial one lead to a complex logic where the RxPlayer has to make a choice between either combine or replace both segments (there even can be more than two segments involved!).
    We mis-handled some cases, which meant that re-adding segments would in some cases not actually really add the corresponding cues to the buffer.

Directfile fixes on iOS, iPad and iPod

The directfile transport in the RxPlayer API allows to play contents which are natively understood by the web browser (like webm, mp4 but also HLS on apple's browsers) while still using the RxPlayer's API.

On such contents, the RxPlayer rely on specific browser behaviors and properties. Among them, we expect the browser to "preload" a media content even before the user has asked for it to "play".

Sadly, we found that this is not always the case on browsers that we can found on Apple mobile devices (iPhone, iPad and iPod).
This means that on those browsers, the RxPlayer could stay in a LOADING state indefinitely, until the play method is called. As this is not intuitive, we now switch to the LOADED state as soon as metadata (such as the media duration) have been obtained on those devices .
Please note that this means that a BUFFERING state (or even SEEKING, due to the initial seek) can be received just afterwards, as LOADED does not mean that data is available anymore here.

Possibility to set a custom key system in the demo

In the demo, we previously restricted the choice of key systems to either PlayReady, Widevine or Clearkey.

As demo users could need to specify more specific key systems, we now also allow them to specify one by hand (please note that the entire key system name - in reverse domain name form - is expected).

Changelog

Bug fixes

  • dash/metaplaylist: fix infinite rebuffering issue when refreshing multi-Period contents under specific conditions
  • buffer: be less aggressive when garbage collecting subtitles (if the maxBufferAhead/maxBufferBehind options are set) to avoid useful subtitles being removed
  • directfile/compat: for directfile contents, trigger directly the LOADED state on iOS/iPad/iPod browsers as those are not preloaded there

Other improvements

  • demo: display clickable "play" button on the video element when autoplay is blocked due to browser policies - to help users unlock the situation
  • demo: add "Other" key system to allow specifying a custom key system in the demo page

v3.17.0

09 Dec 18:38
756d957
Compare
Choose a tag to compare

Release v3.17.0 (2019-12-09)

Overview

The v3.17.0 adds a lot of features:

  • We added multiple APIs related to DRMs, especially to improve the experience when playing contents with multiple keys and/or multiple licenses
  • We added a new transportOptions, checkMediaSegmentIntegrity, which allows the RxPlayer to retry a segment request if that segment appears corrupted
  • Another new transportOptions, minimumManifestUpdateInterval, allows to limit the frequency of Manifest refresh (which can be useful on low-end devices)
  • We improved TTML support for proportional sizes (e.g. font-size, padding, origin etc.)
  • a new dub boolean, returned by APIs related to audio tracks, allow to announce if the corresponding track is a dubbing audio track.
  • we better handle sudden fall in bandwidth when using a segmentLoader through the corresponding transportOptions, by allowing this custom function to report the progress of a request while it is still pending, through a new progress argument.
  • and multiple minor bug fixes...

Improvements for contents with multiple keys and/or multiple licenses

Some contents may be linked to several decryption keys which may be contained in one or more licenses. For example, 240p video segments may need less copy protection guarantees than 4k video segment does, thus both can have two separate keys and/or licenses.

The RxPlayer already handled contents with multiple keys and multiple licenses without problems, but stopped on error as soon as a license or key was refused.

The more sensible strategy would be to fallback from contents with a refused key or license to a decipherable content instead.
To take our previous example, devices with limited copy protection guarantees would not be able to play 4k content, but they could automatically fallback to 240p if this quality is decipherable.

That's what we worked on with this new release.
Unfortunately, a failure to obtain decryption keys can arise at different steps, depending on technology choices and the DRM strategy:

  • before playing the content
  • announced by the license server, which refuses to deliver a license
  • announced by the CDM/Browser, by stating that a decryption key is unusable

Also, depending on your strategy, you might even want to just fail as soon as keys or licenses are refused.

That's the reason why we chose to let you decide with a complete API, through the usual keySystems loadVideo option.

For example, to automatically fallback when a key is both refused due to an internal CDM errors or due to output restrictions, you can do:

rxPlayer.loadVideo({
  // ...
  keySystems: [{
    // ...
    fallbackOn: {
      keyInternalError: true,
      keyOutputRestricted: true,
    }
});

But that's not all, you can also for example fallback even before any key is obtained, for example when the license server refuse to deliver you a license, through the fallbackOnLastTry option.

As usual, all those new APIs have been added to the loadVideo API documentation. To help you implement the code especially linked to content with multiple keys we added a new DRM tutorial which contains a whole chapter on that subject.

checkMediaSegmentIntegrity

This release adds two new transportOptions, among which checkMediaSegmentIntegrity.

This new option allows the RxPlayer to check if a downloaded segment appears corrupted after doing a request. This had to be added because we observed issues with segment corruptions on multiple occasions (both due to browser bugs on embedded devices or to delivery issues).

For example, there can be some issues with live content delivery, as the live nature of the encoding implies that content may present some unexpected defects. In the RxPlayer, we check that expected data is in line with the loaded content. (e.g. the data length announced in ISOBMFF container compared to real data length).

With this option enabled, the RxPlayer will retry a segment request as soon as it detects an issue with it, with the same retry rules than the usual HTTP 404 error.

You can opt-in for this check like this:

rxPlayer.loadVideo({
  // ...
  transportOptions: {
    checkMediaSegmentIntegrity: true,
  }
});

This option was actually already present (but hidden) in the precedent release. We only communicated it to specific people at Canal+ to verify if this new option could reduce issues such as BUFFER_APPEND_ERROR errors at a large enough scale before definitively adding it to our API.
Because we observed a consequent fall in BUFFER_APPEND_ERROR after those tests without impacting other errors, we decided to go on and officially add this option in the v3.17.0.

For the moment, the RxPlayer is only able to check ISOBMFF segments (other types of segments are always accepted for now). More informations on this option can be found on the transportOptions documentation.

minimumManifestUpdateInterval

The second transportOptions added in this release is minimumManifestUpdateInterval.

This option allows to reduce the pace at which Manifest refresh are done.
This is usually not needed for most devices.

We added that option because we observed that on some low-end devices, a high Manifest refresh pace (i.e. every 2 seconds) could make the video stutter enough for it to become unplayable.
This was probably due to the fact that the decompression and parsing done by the browser of 8 hours+ multi-lingual Manifests with short segments took a lot of memory and that some devices can't afford that much.

Allowing a greater interval between Manifest refresh did improve drastically the experience on those devices, even if this comes with its own caveats (like being aware of new segments or of other Manifest change later).
That's why we usually advise people to rely on other tactics if that becomes a problem, such as either using a numbering-scheme when using DASH or making a better usage of the r attribute on SegmentTimeline segment indexes.

This is however not always doable. If you understand the risks and are able to mitigate them (like by playing farther from the live edge), you can check this new option in the transportOptions documentation. Here is an example:

rxPlayer.loadVideo({
  // ...
  transportOptions: {
    // keep at minimum 5 seconds between manifest updates
    minimumManifestUpdateInterval: 5,
  }
});

dub boolean for audio tracks

The method getAvailableAudioTracks and getAudioTrack, the availableAudioTracksChange event and the second argument given to the representationFilter transportOptions can now return objects with a new boolean property called dub (or isDub for the representationFilter API).

If set to true, it indicates that the linked audio track is a dubbing audio track (and not the original one). We can infer this information if specified in the DASH's MPD.

The documentation of all those APIs has been updated to include this new property.

Note about the length between releases

The previous release was done a little more than two months ago which was a little unusual for us: we usually wait no more than roughly a month between releases.

The fact that we waited that long before releasing the v3.17.0 was due to some good news: we didn't encounter neither a major bug which might need a fix right away neither a major feature which would need to be released as soon as possible.

But waiting that long also means larger releases, with much longer testing periods. We had to remove several features during our review and testing period, as we didn't feel those were ready enough.
All this means that both the list of features included in this version and its release date were much harder to predict than for previous versions.

This is why we will now try to keep roughly one month (or less if a major bug or deadline appears) between releases. Maybe we will still be a little late sometimes, but at least that will be the new target we set for ourselves.

Changelog

Features

  • eme/api: add keySystems.fallbackOn property to loadVideo to allow fallbacking to other qualities when encountering various key errors
  • eme/api: allow to set fallbackOnLastTry on a getLicense Error to be able to fallback on other qualities when a license request is on error
  • eme/api: add NO_PLAYABLE_REPRESENTATION MediaError for when every video or audio quality cannot be played
  • manifest/api: add decipherable property to a Representation object
  • api: add decipherabilityUpdate event triggered when a Representation's decipherability status is updated
  • languages/api: add dub boolean to audio tracks (through getAudioTrack and getAvailableAudioTracks) to tell if this is a dubbed track
  • languages/ttml: with TTML subtitles, support length relative to the Computed Cell Size for tts:fontSize, tts:padding, tts:extent, tts:origin and tts:lineHeight
  • transports/api: add checkMediaSegmentIntegrity transportOptions to automatically retry media segments which appear corrupted
  • transports/api: add `m...
Read more

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