I went ahead and implemented the full structured preference system that
was discussed in
https://github.com/shaka-project/shaka-player/issues/1591.
Instead of just expanding languages to arrays, I replaced all 14
individual preference fields with 3 structured arrays:
```tsx
preferredAudio (language, role, label, channelCount, codec, spatialAudio)
preferredText (language, role, format, forced)
preferredVideo (label, role, codec, hdrLevel, layout)
```
Each array entry works as an AND filter - so you can say things like "I
want Korean with 5.1 surround, but if not available, English is fine
too":
```tsx
player.configure('preferredAudio', [
{language: 'ko', channelCount: 6},
{language: 'ko'},
{language: 'en'},
]);
```
<img width="1728" height="965" alt="image"
src="https://github.com/user-attachments/assets/7b088150-139b-475e-bdba-5bc77dd4e524"
/>
**Config** - Replaced the 14 individual fields with 3 arrays of typed
preference objects (AudioPreference, TextPreference, VideoPreference).
The old fields still work at runtime with a deprecation warning, so
existing apps won't break immediately.
**Demo** - The demo config UI now shows inline expandable preference
lists instead of flat text inputs. You can add/remove entries and
configure each field per entry. URL hash serialization was updated to
use JSON format, with legacy param fallbacks preserved.
The goal is to simplify and abstract feature logic detection. Currently
lots of places depend on various calls to `shaka.util.Platform` and
mainteinance of this is hard & not easy to read.
By introducing device API ideally rest of the player logic would look
into device features instead of directly checking platform. Additionally
we can more easily cache needed values, so we won't have to parse user
agent several times anymore.
---------
Co-authored-by: Álvaro Velad Galván <ladvan91@hotmail.com>
Related to
https://github.com/shaka-project/shaka-player/issues/7602#issuecomment-2479518970
From 23009-1:
The value of the minimum buffer time does not provide any instructions
to the client on how long
to buffer the media. The value however describes how much buffer a
client should have under
ideal network conditions. As such, MBT is not describing the burstiness
or jitter in the network,
it is describing the burstiness or jitter in the content encoding.
Together with the BW value, it is
a property of the content. Using the "leaky bucket" model, it is the
size of the bucket that makes
BW true, given the way the content is encoded
This deduplicates a platform support check that was run in
player_integration.js, and declares the pre-existing central support map
in an extern so we can clean up its use.
This stops a DRM integration test from timing out on FirefoxWindows. It
still gets skipped, though, due to a failing Widevine check, so there
will be follow-on work for that.
Issue #7449
In some cases, Fuchsia Chromecast tests will fail with the error: "The
play() request was interrupted because video-only background media was
paused to save power."
This resolves the issue by ensuring tests run un-muted on that platform,
based on this Chrome code, which indicates the "paused to save power"
logic does not activate when sound is playing:
https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/platform/media/web_media_player_impl.cc;l=3535;drc=d23075f3
This also fixes two places in our tests where the `createVideoElement()`
was bypassed. This should always be used, because it is a central place
to apply workarounds such as this.
The offline DRM tests have begun failing on Android at a rate of 80% or
more. This was eventually determined to be related to
https://crbug.com/1108158 and the timeout workaround in DrmEngine.
When close() hangs and we time out and move on, we leave sessions open
that consume hardware resources at the OEMCrypto level in Android. This
eventually leads to failures like `Failed to execute 'createMediaKeys'
on 'MediaKeySystemAccess': MediaDrmBridge creation failed`. Logs from
`adb logcat` show that 17 sessions are open when this fails, which
likely means our lab device has a limit of 16 open sessions.
Initially, delays between these offline DRM test cases were found to be
an effective workaround, but full test runs showed this to be
ineffective after all.
The only recourse, until Chrome and Widevine fix their bug, is to skip
these tests on Android.
A previous PR, #5950, added support for variants that contain multiple
different codecs.
It was supposed to also add support for variants with multiple
mimeTypes, but that part didn't work correctly. This reworks a lot of
#5950 and #6047, to change how they handle such complicated variants.
This has the side-effect of allowing the stream utils to differentiate
between content that has multiple codecs because of type changes, and
content that has multiple codecs because of being muxed video+audio.
Fixes#6010
---------
Co-authored-by: Álvaro Velad Galván <ladvan91@hotmail.com>
The state engine mechanism, designed for the player class, was
over-engineered. The structure of the class makes debugging player
errors unnecessarily annoying, by obfuscating the code-path the error
followed, and in general
has created a significant amount of technical debt.
This changes the player to use an async-await setup for the top-level
operations, laying things out much more cleanly
and linearly.
---------
Co-authored-by: Álvaro Velad Galván <ladvan91@hotmail.com>
Power-saving features on Chrome and Edge were subtly interfering with
playback tests. Timers could be throttled, and both video-only media and
media in occluded windows could be paused by the browser.
This was discovered only after awaiting play() Promises in all tests.
These Promises were being rejected with useful error messages that led
to these discoveries.
Awaiting play() requires us to disable stall detection during playback
tests. This is because on some platforms, stalls get resolved by calling
pause() and then play(), which would cause the original awaited play()
Promise to be rejected.
Finally, some Player tests created additional Player instances that were
unnecessary. Removing those allowed me to centralize most of the
configuration to disable stall detection.
Shaka in most of places normalizes tracks' language code to be compliant with ISO 639-1 when possible. However, it does not do that all the time (i.e. normalization is missing in MSS parser) and there is no way to get value that has been explicitly set in a manifest. Moreover, documentation is misleading, as it claims that value is taken directly from a manifest.
Normalization should take place, specifically to easify PeriodCombiner algorithm and also to not break existing applications.
However, original value can be desired for some implementations.
This PR introduces new field to get original language value from the manifest.
Add capability to re-use persistent license sessions across sessions.
DrmEngine will now always:
- try to start stored persistent sessions before trying to fetch a
license, as-to be able to check if all needed keys are already loaded.
- ask for a new license when the persistent session doesn't have the
needed keys for playback,
Given the flag `persistentSessionOnlinePlayback` is true, DrmEngine:
- won't remove the persistent session from the device at the end of the
playback,
- won't throw an error when the persistent session isn't found on the
device,
For now, it needs Shaka's users to persist session information by
themselves (localStorage, IndexDB, ...) before giving it back for the
next session. Still, it lays foundation to develop the feature to fully
handling it on Shaka's side.
Related to #1956
In some cases, indexedDB.open() can end up calling neither callback.
When this does happen, according to my initial testing, it happens
consistently when reloading the page, so it's not a one-off fluke but
presumably some sort of implementation or browser install problem. If
that does happen, the init promise of the storage muxer hangs forever,
potentially blocking other operations from happening. This adds a
timeout to the invocation of indexedDB.open(), after which the operation
fails with a new error.
Shorten a StreamingEngine integration test to work around an issue with
ChromeMac where playback doesn't always occur at 1x. This is an issue
with Selenium or Chrome itself (unknown) that is out of our control. By
making the test somewhat shorter, we can have it complete on time, even
when this issue is present on a device.
Also increase timeouts on offline playback tests.