getOrInsertComputed is not available at init, until polyfills are used,
but the problem that this PR fixes is that there is a call that uses it
before calling the polyfill.
Fixes a UI alignment bug where percentage-based ad markers and
interstitials were shifted horizontally. This regression was introduced
in PR #10097 due to how CSS handles 'background-position' when using
percentage values.
Now, the seek bar uses color stops within a full-width linear gradient
for dynamic percentage markers to guarantee exact boundaries, while
preserving the original centering logic for absolute-width chapter
markers.
Add integration tests for `SimpleAbrManager`.
Uses the same `StreamGenerator` class already used in other integration
tests to serve media segments from memory, avoiding unpredictable
network conditions. Throughput is simulated deterministically by
deriving each segment's download time from a target bitrate in
NetworkingEngine's onDownloaded callback.
Related #9918 , also adds integration tests for the dropped frame
protection feature. Since real frame drops can't be reproduced in test
environment, so used `getVidoePlaybackQuality` to override on the real
video element to inject controlled drop ratios.
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Ensure generated bundles contain a single license header instead of
repeating the same notice across bundled modules.
Normalize all copyright years to 2016 and remove
duplicate license headers from generated bundles.
This reduces the final bundle size while preserving license information
in the distributed artifacts.
macOS 15 (Sequoia) Local Network Privacy blocks the GUI test browser
from reaching lab hosts (the karma server) on the local subnet, failing
tests with net::ERR_ADDRESS_UNREACHABLE. The per-app permission cannot
be relied on: it is keyed to the Mach-O UUID of the browser binary, so
it is revoked on every Chrome update, there is no MDM or profile
control, and the node runs unattended.
Add a loopback HTTP CONNECT proxy that runs as a root LaunchDaemon. Root
daemons are exempt from Local Network Privacy, so the proxy can reach
lab hosts; the browser connects only to loopback (also exempt) via a
PAC, so it never needs the grant and survives Chrome updates. It uses
HTTP CONNECT rather than SOCKS because WebSocket over SOCKS fails with
ERR_WS_UPGRADE, and the karma control channel is a WebSocket.
See also https://github.com/shaka-project/shaka-lab/pull/84
- Exposed and renamed `shaka.util.FakeEventTarget.ALL_EVENTS_` to
`ALL_EVENTS`, updating its value to `__shaka_all_events__` to prevent
string collisions.
- Refactored `CastProxy` and `CastReceiver` to subscribe directly to
this wildcard event for both the Player and the AdManager instances.
- Removed the hardcoded `shaka.cast.CastUtils.AdManagerEvents` array and
eliminated the loops that iterated over static event lists.
This makes the casting event proxy layer completely dynamic, cleaner,
and future-proof against new event additions to either the Player or the
AdManager.
`loadInner_` was skipping `chooseVariant_()` whenever a prefetched
variant was available.
Because `SimpleAbrManager.chooseVariant()` is the only place that sets
`lastTimeChosenMs_` (`trySuggestStreams` does that too but I doubt the
intent), the reused AbrManager (whose state was reset to `null` by
`stop()` during unload) never got reinitialized.
The guard in `segmentDownloaded` (`lastTimeChosenMs_ != null`) then
blocked every subsequent `suggestStreams_` call indefinitely.
This is an alternative fix that does not rely on `trySuggestStreams` (as
originally suggested by the issue report) but rather runs
`chooseVariant_()` unconditionally during load so the Player's
AbrManager always registers an initial selection. The prefetched variant
is still preferred for playback so prefetched segments aren't wasted.
Fixes#10045.
The player UI previously moved from a Material Design icons font to
inline SVGs, but the demo still loaded the "Material Icons Round" web
font from Google Fonts. Convert the demo to inline SVGs and drop the
font dependency.
- Add demo/icons.js: the SVG path data the demo needs plus a small
makeSvgIcon helper, mirroring shaka.ui.Icon but self-contained so the
demo does not depend on UI-internal classes.
- Convert the demo's own icon usages (config, input_container,
asset_card, custom, and the drawer close button in index.html) to inline
SVGs.
- Render MDL's drawer toggle button (a settings gear, set via MDL's
MENU_ICON) by appending an inline SVG to MDL's icon element in
setupPlayer_, instead of relying on the font.
- Remove the Google Fonts stylesheet link and the now-dead fonts.gstatic
preconnect from both demo HTML files, and drop the .material-icons
font-family override and line-height fix from demo.less.
Co-authored-by: Claude Code (Opus 4.8) <noreply@anthropic.com>
The build system is updated to generate two CSS outputs:
A legacy stylesheet (.css) where CSS custom properties are flattened
using postcss-custom-properties for compatibility with older browsers. A
modern stylesheet (.modern.css) that preserves CSS custom properties for
modern browsers.
Both outputs are generated from the same LESS source and processed
through Autoprefixer and cssnano, ensuring consistent styling while
supporting different browser capabilities. This enables a gradual
migration path between legacy and modern UI styling without breaking
existing consumers.
Close https://github.com/shaka-project/shaka-player/issues/10145
Fixes https://github.com/shaka-project/shaka-player/issues/10149
PR #9998 (`perf(HLS): skip merging known segments on live playlist
updates`) introduced a regression where low-latency HLS live streams
stall after ~10 seconds of playback. The video and audio simply stop
with no error.
### Root Cause
`createSegments_()` keeps a `mediaSequenceToStartTime` map to track
which segments it has already seen. The optimization only passes **new**
segments (those not yet in the map) to `mergeAndEvict()`:
```js
// line 4838
if (isNewSegment) {
newReferences.push(reference);
}
```
However, in LL-HLS, a segment's partial references **grow** across
playlist refreshes — e.g., a segment starts with 2 partials, then 4,
then 8, until the segment is complete. The `firstReferenceToCreate`
logic at line 4665 correctly detects segments with active partials and
triggers a **rebuild**, but the rebuilt reference is **not** included in
`newReferences` because its position already exists in
`mediaSequenceToStartTime`. So `mergeAndEvict()` never receives the
updated reference, the segment index retains **stale partial data**, and
the player stalls once it exhausts those stale partials — roughly 10
seconds of content.
### The Fix
**1 line, no behavioral impact on VOD or non-low-latency streams.**
```diff
- if (isNewSegment) {
+ if (isNewSegment || !isNewStream) {
```
During live updates (`isNewStream = false`), **all** rebuilt segments
are now passed to `mergeAndEvict`, not just the truly new ones. This is
correct because rebuilt segments (triggered by partials or EXTINF
changes) have updated content that must replace the stale data in the
segment index.
**No performance regression:** When there are truly no changes (all
segments known, no partials, no EXTINF changes),
`firstReferenceToCreate` stays at `hlsSegments.length`, so all segments
are skipped, `references` is empty, and `mergeAndEvict` is never called
— preserving the optimization.
### Steps to Reproduce
1. Play any LL-HLS live stream (e.g., with `#EXT-X-SERVER-CONTROL` and
partial segments)
2. Observe playback stops after ~8–12 seconds with no error
- Add postcss-cli, autoprefixer, and cssnano to the build pipeline.
- Remove legacy '--clean-css' minification from the lessc step.
- Configure BROWSERSLIST env var for Chrome 38, Safari 8 and Firefox 42.
- Clean up redundant vendor prefixes (-webkit-, -moz-, etc.) from LESS
files.
Related to https://github.com/shaka-project/shaka-player/issues/10145
This adds back the Tengwar font to support an upcoming complete and
maintained translation into Sindarin, powered by Claude Code and
automated workflows.
This is even less invasive than the method I had originally. It does not
touch the library CSS at all.
Force form elements (button, input, select, textarea) to inherit
font-family, since browsers default them to the OS system font rather
than inheriting from ancestors. This makes the Shaka UI font apply
consistently to all elements under .shaka-video-container.
Remove now-redundant explicit font-family declarations from
.shaka-overflow-quality-mark and the tooltip ::after pseudo-element,
which were workarounds for the same inheritance gap.
Now it is possible for an application to override the font style of the
UI with a single declaration.
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
If you open Shaka Player with Hungarian as the UI language setting
(https://shaka-project.github.io/shaka-player-release/demo/#uilang=hu)
it will currently display the UI in English, because Hungarian is not
one of the built-in-by-default languages for the project. It does still
work for a built-in language like German. (Compare to
https://shaka-project.github.io/shaka-player-release/demo/#uilang=de)
We used to have a feature in the demo to load localizations from the
server dynamically for languages where we have translations, but don't
build them in by default.
The feature to lazy-load localizations for the player UI was
acccidentally dropped in e8e2807 (PR #5665) while abandoning translation
of the demo itself. This PR restores that feature in a simplified
implementation. Ex:
https://joeyparrish.github.io/shaka-player/demo/#uilang=hu
This PR introduces a Web Worker for transmuxing resolving
https://github.com/shaka-project/shaka-player/issues/1735
- The worker bundle is compiled separately
- The build output is embedded as a string constant and then wrapped in
a Blob to create an inline Worker URL (HLS.js does this very similarly)
- `TransmuxerProxy` is created wrapping a real transmuxer, but no worker
is started yet - on the first `transmux()` call, it checks if the device
supports worker transmuxing
- For each transmux() call: the buffer is copied, then zero-copy
transferred to the worker. A PublicPromise is stored under a reqId with
a timeout timer, and the main thread awaits it.
- The worker transmuxes and posts back transmuxed (or error). The shared
message listener routes the response to the right proxy instance by id,
which resolves the promise and cancels the timer.
- When the last proxy instance is destroyed, the worker is terminated
and the blob URL is revoked.
loaded inside the worker.
- Some low-end devices have been excluded since their Worker support is
questionable
There most likely is a better way to do this - please let me know
Improve readability and separate responsibilities. Use Intl.DisplayNames
when available and fallback to mozilla.LanguageMapping otherwise.
Note: this is in preparation for addressing
https://github.com/shaka-project/shaka-player/issues/9694
---------
Co-authored-by: Matthias Van Parijs <matvp91@gmail.com>