Skip to content

Fragments between key frames - #3449

Closed
robspychala wants to merge 571 commits into
androidx:releasefrom
robspychala:fragments-between-key-frames
Closed

robspychala wants to merge 571 commits into
androidx:releasefrom
robspychala:fragments-between-key-frames

Conversation

@robspychala

Copy link
Copy Markdown

No description provided.

copybara-github and others added 30 commits September 3, 2026 00:57
Returning an incomplete ListenableFuture from onGetLibraryRoot() or
onConnectAsync() previously deadlocked the service main thread because
Futures.addCallback posted the completion callback to the application
looper while onGetRoot() was synchronously blocking that same looper.

Pre-resolve controller parameters and command buttons up-front, and
invoke the completion callback using MoreExecutors.directExecutor() to
unblock the main thread immediately upon future completion.

Issue: androidx#3393
#cherrypick
PiperOrigin-RevId: 975693221
See: https://developer.android.com/jetpack/androidx/releases/compose-ui#1.13.0-alpha01 that brought minSdk up to 24 which we now pull into the demo-effect via material3-alpha26 dependency.
PiperOrigin-RevId: 975709854
This change ensures nextTrackId is consistently initialized to 1 and
uses track.id directly when writing tfra boxes and processing tracks,
preventing off-by-one track ID discrepancies in multi-track fragmented
MP4 containers.

PiperOrigin-RevId: 975869862
Reverts `MiniController` to use the standard `bitmapLoader` overload rather than the custom artwork slot. When sharing an artwork composable between the `ContentFrame` and the `MiniController`, using `fillMaxSize` was interfering with the sizing that `MiniControllerTokens.ArtworkSize` tries to enforce.

PiperOrigin-RevId: 975958834
ClippingSlider(progress, clippingRange, ...) overload that became unused when ClippingSlider switched to `RangeSliderState`.

Add a TODO explaining that once `onValueChange` is supported by
`RangeSliderState`, the `minRangeDelta` clamping logic can be moved to that
callback instead of observing snapshotFlow.

PiperOrigin-RevId: 976019967
This adds support for 32-bit floating-point PCM audio tracks in
Mp4Muxer and FragmentedMp4Muxer by writing the standard fpcm
sample entry and pcmC configuration box per ISO/IEC 23003-5.

Issue: androidx#3389
PiperOrigin-RevId: 976225331
On devices with strict EGL drivers (such as Qualcomm Adreno 740 on
Samsung Galaxy Z Flip 5), attempting to create an EGL window surface
on a destroyed or invalid Surface throws an IllegalArgumentException.
Adding a surface.isValid() check before surface creation prevents this
crash and treats the debug preview render as a safe no-op.

PiperOrigin-RevId: 976229076
This change appends outgoing MediaQueueItem payloads with unique synthetic IDs, to uniquely identify them when returned back by the Cast receivers.

PiperOrigin-RevId: 976276156
Material3-alpha25 brought the following:
- Update `rememberModalBottomSheetState` to include `rememberBottomSheetState` with `initialValue = SheetValue.Hidden`.
- Update `ListItem` usages to use the trailing lambda syntax for `headlineContent`.

PiperOrigin-RevId: 976287011
Introduced the general-purpose, decoupled PlaybackState (and rememberPlaybackState helper) to reactively expose core player states (isBuffering, isEnded, hasError, isPlaying).

Implemented BufferingIndicator Composable which renders an circular spinner.

PiperOrigin-RevId: 976291702
Storing a Job in mutableStateOf causes recompositin every time hideJob.value is updated.

Since scheduleHideControls() assigns a new Job on every pointer move event (which fires continuously at the screen refresh rate), this will cause severe recomposition thrashing and degrade performance.

To prevent unnecessary recompositions, hold the Job in a plain mutable reference inside remember that isn't backed by Compose state.

PiperOrigin-RevId: 977407604
In MediaRouteButtonContainer, mediaRouteSelectorListener is registered with cast via registerListenerAndGetCurrentSelector(), but it is never unregistered when the composable leaves composition. If the composable is removed from composition before Cast is initialized, mediaRouteSelectorListener remains in Cast.pendingMediaRouteSelectorListeners, causing a listener memory leak and potentially triggering callbacks on a disposed state.

PiperOrigin-RevId: 977418685
* Add an optional field `location` in `DataSpec`, and set the `serviceLocation` on the `DataSpec` for DASH media chunk loading.
* Add a package-private `ServiceLocationFilteringThroughputEstimator` within DASH module, which:
  * Receives transfer events as `TransferListener` from the chunk download `DataSource`;
  * Assigns individual estimator per `serviceLocation`, each estimator calculates estimates based on a sliding window weighted average of the throughput samples;
  * Provides throughputs estimate for a `serviceLocation` via `getThroughputEstimate(serviceLocation)`.

PiperOrigin-RevId: 977423616
Updates the playback position thumb height to be 1.1 times the slider track height.
To prevent the thumb's height from being clamped by the parent constraints, the slider modifier is updated to use unbounded `wrapContentHeight` instead of `fillMaxSize`.

PiperOrigin-RevId: 977424770
PiperOrigin-RevId: 977451848
This adds an @ignore test documenting an issue where frames with
truncated microsecond timestamps produced by demuxers (such as
standard MP4 extractors scaling sample timestamps with
RoundingMode.DOWN) are discarded as stale by FrameAggregator when
retiming against a virtual clock rounded to the nearest microsecond
(RoundingMode.HALF_UP).

PiperOrigin-RevId: 977509920
PiperOrigin-RevId: 977512392
This change adds GPS altitude support to Mp4LocationData and updates
BoxParser to extract latitude, longitude, and optional altitude
metadata from xyz atoms.

PiperOrigin-RevId: 977530327
Removes stale TODOs for the following reasons:
*  Position thumb's height is fixed.
*  RTL locales: Looking more into this, media-related components should remain in LTR. More info [here](https://m3.material.io/foundations/layout/bidirectionality-rtl#7f9f392d-40d6-4186-86b6-9e59626f963e:~:text=Copy%20link-,Media%20players,-Media%20controls%20for).

PiperOrigin-RevId: 977530732
This change adds GPS altitude support to Mp4Muxer, allowing the muxer
to write 3-component ISO-6709 coordinate strings (+lat+long+alt/) in
the udta xyz box when Mp4LocationData includes an altitude.

PiperOrigin-RevId: 977552154
Update documentation for `ClippingSlider`, `ClippingRangeSlider`, and `EditingPlayerScreen` to clarify that the preview bitmap list length must remain constant to avoid layout resizing issues.

PiperOrigin-RevId: 977566808
Dynamic scheduling tests in ExoPlayerScrubbingTest previously relied on
MediaCodecVideoRenderer and asynchronous decoding via FakeClock, leading
to timing races in Robolectric when running under high concurrency.

This change switches to a deterministic
ForwardingDurationToProgressRenderer that directly returns a fixed
durationToProgressUs, matching the test pattern used in ExoPlayerTest.

PiperOrigin-RevId: 977887947
Document backwards compatibility and interoperability behavior across
MediaSession, MediaController, MediaBrowser, MediaLibraryService, and
MediaSessionService when interacting with platform or legacy controllers
and sessions.

PiperOrigin-RevId: 977911614
Unlike HLS, DASH allows definitions of service location (pathway) for individual elements (eg. Representation). Within the library, the selection of service locations for manifest update is maintained in `DashMediaSource`, while the selection logic for baseUrls for segment fetching is well maintained in `BaseUrlExclusionList`.

Instead of pursuing the unity between HLS and DASH `ContentSteeringTracker` implementations as much as possible, it's better to respect their difference, while keeping a small set of shared logics in `BaseContentSteeringTracker`.

In `DashContentSteeringTracker`:

* We directly propagate the `serviceLocationsPriority` (pathwayPriority) to its callback, and the `DashMediaSource` and `BaseUrlExclusionList` will use it for their selection logics.
* We require the owner of the tracker (`DashMediaSource`) to update the `DashManifest` to the tracker, so that the tracker can always track the up-to-date available service locations (this will be useful for pathway clone).
* We require a set of `SteeringQueryParameterProvider`s to give a snapshot of selected service locations for any `Location`s or `BaseUrl`s managed under them.

PiperOrigin-RevId: 977929176
Introduce InFlightFrameManager to encapsulate tracking and releasing of in-flight Frames. Migrate CompositionVideoPacketReleaseControl and PacketConsumerVideoSampleExporter to delegate in-flight frame management to InFlightFrameManager.

PiperOrigin-RevId: 978422178
Conflating **range dragging** with **general user interaction** caused progress
scrubbing (`onProgressChange` setting `state.isUserInteracting = true`)
to anchor `activeValueRange` to `preDragClippingRange`. On trimmed media,
this abruptly shifted the progress slider's bounds and `valueRange`
mid-touch, canceling Compose's drag gesture detector and restricting
interaction to tap-to-seek only. Additionally, `preDragClippingRange` was
not updated when a clipping drag completed.

This change:
* Introduces a dedicated `isClipping` flag in `ClippingSliderState` to
  track when clipping handles are actively being dragged.
* Anchors `activeValueRange` to `preDragClippingRange` only while
  `isClipping` is true, ensuring the layout remains fixed during progress
  scrubs.
* Updates `preDragClippingRange = clippingRange` and resets `isClipping`
  in `onValueChangeFinished`.

PiperOrigin-RevId: 978488962
Align backwards compatibility behavior with methods like
setMediaButtonPreferences:
- Unconditionally update the platform session on setSessionActivity, and
  ensure standalone sessions without an active media notification
  controller correctly update and recover the platform session on
  setPlaybackException.
- Forward sessionExtras, sessionActivity, and active playback exceptions
  configured on AcceptedResultBuilder when the media notification
  controller connects.
PiperOrigin-RevId: 978514749
Googler and others added 29 commits October 2, 2026 14:21
PiperOrigin-RevId: 992534876
Replace `ListenableFuture.addListener` callbacks and the custom
`browser` getter in `MainActivity` and `PlayableFolderActivity` with
`kotlinx.coroutines.guava.await()` and `repeatOnLifecycle`, matching
`PlayerActivity`.

PiperOrigin-RevId: 992550891
Add `MediaMetadata.categories` and
`MediaMetadata.Builder.setCategories(List<String>)`.

Add `EXTRAS_KEY_SUGGESTED_TYPE`, `EXTRAS_KEY_TTL_SECONDS`,
`EXTRAS_KEY_PLAYLIST_TITLE`, `EXTRAS_KEY_PLAYLIST_ARTWORK_URI`, and
`EXTRAS_KEY_CATEGORIES` to `MediaConstants`, along with legacy Media1
conversion support.

PiperOrigin-RevId: 992626093
When ExoPlayer seeks during audio offload playback, it flushes the
audio sink, which triggers `AudioTrackAudioOutput.release()`. Previously,
`releaseAudioTrackAsync()` scheduled both `audioTrack.flush()` and
`audioTrack.release()` to execute asynchronously after a 20ms delay
(`AUDIO_TRACK_VOLUME_RAMP_TIME_MS`).

However, when a new `AudioTrack` is immediately created after the seek
for the same offload stream, AudioPolicy reuses the same underlying
HAL offload stream. If the delayed asynchronous flush from the old
track executes after the new track has already started writing data,
it clears the HAL buffers that the new track just filled.

This causes the new stream to freeze and starve, triggering spurious
timestamp mismatch warnings in `AudioTrackPositionTracker`.

Flushing before release is only needed to prevent state retention on
legacy PCM devices. Offload tracks do not need a flush
before release. By skipping `audioTrack.flush()` when releasing offloaded
tracks, the shared HAL stream is never flushed by the old track,
preventing the race condition while preserving the release delay.

PiperOrigin-RevId: 993603427
Pass Period.assetIdentifier to the Period constructor in
DashManifest.copy and add unit test coverage in DashManifestTest.

PiperOrigin-RevId: 993661238
Using `_` as an identifier has been a reserved keyword since Java 9 (and
is only valid for unnamed variables in Java 22+), causing `javac`
warnings during Gradle builds targeting Java 11 (`--release 11`).

Replace `_` local variable names in `SubripParserTest`,
`ProgressiveMediaPeriodTest`, and
`AudioCapabilitiesReceiverWrongThreadTest` with `unused`.

PiperOrigin-RevId: 993675408
`ExoPlayerMinimalModuleProguard` references `ExoPlayer`, whose compiled
class file contains `@RestrictTo(Scope.LIBRARY_GROUP)` annotations.
Because `:lib-exoplayer` and `:lib-common` declare
`libs.androidx.annotation` as an `implementation` dependency rather than
`api`, `androidx.annotation.RestrictTo$Scope` is not exposed
transitively on `:test-proguard-minimal`'s compile classpath, causing
`javac` to emit `unknown enum constant Scope.LIBRARY_GROUP` warnings
when compiling `:test-proguard-minimal`.

```
> Task :test-proguard-minimal:compileDebugJavaWithJavac
warning: unknown enum constant Scope.LIBRARY_GROUP
  reason: class file for androidx.annotation.RestrictTo$Scope not found
```

Add `compileOnly(libs.androidx.annotation)` to `:test-proguard-minimal`
so `javac` can resolve class-retention annotations from `:lib-exoplayer`
at compile time without adding extra dependencies to the minimal R8 test
APK packaging classpath.

PiperOrigin-RevId: 993676892
Adds `Flags.FLAG_SKIP_AUDIO_TRACK_FLUSH_BEFORE_RELEASE` (enabled in
Canary Mode) to skip calling `AudioTrack.flush()` before
`AudioTrack.release()` in
`AudioTrackAudioOutput.releaseAudioTrackAsync()` on API 29+.

Calling `flush()` before `release()` was introduced in 2015 as a
workaround for a legacy passthrough HAL issue where recreated tracks
retained the previous playback head position. On API 29+,
`AudioFlinger`'s `DirectOutputThread` unconditionally flushes the HAL
stream when adding a new track, and skipping the asynchronous flush
before release avoids racing with a newly created track sharing the same
direct or offload HAL stream.

PiperOrigin-RevId: 993680508
Passing `-DHAVE_FSEEKO` on the compiler command line defines the macro
as `1` (`#define HAVE_FSEEKO 1`). On 64-bit Android ABIs (`arm64-v8a`
and `x86_64`), libflac's `config.h.cmake` (`#cmakedefine HAVE_FSEEKO`)
generates `#define HAVE_FSEEKO` with an empty replacement list, causing
`-Wmacro-redefined` warnings when `config.h` is included.

Using `-DHAVE_FSEEKO=` defines `HAVE_FSEEKO` with an empty replacement
list (`#define HAVE_FSEEKO`), which still satisfies `#ifndef
HAVE_FSEEKO` in `compat.h` on 32-bit Android ABIs with `minSdk 24` while
matching `config.h` identically on 64-bit ABIs.

PiperOrigin-RevId: 993684639
Previously, addMediaItems operations were discarded if the receiver had no media items in queue or no operations in flight to add to the queue.
This change allows AddMediaItemsOperation to initialize playback when the receiver queue is empty.

Issue: androidx#2402
PiperOrigin-RevId: 993704828
When a decoder transiently reports an invalid output format (such as
0x0 on start or flush), onOutputFormatChanged ignores it and leaves
pendingVideoSinkInputStreamChange set to true. If the input format
queue was already polled, subsequent output buffers may reach
processOutputBuffer with pendingVideoSinkInputStreamChange still true.

Re-query the codec output format in processOutputBuffer when
pendingVideoSinkInputStreamChange is true, and return false if the
format is still invalid so the frame is not sent to the VideoSink
before onInputStreamChanged is called.

This is a follow up on androidx@fa65d31 which didn't fully address the test
failure.

PiperOrigin-RevId: 993721203
In the Matroska specification (RFC 9559), `ChapterAtom`
(`\Segment\Chapters\EditionEntry\+ChapterAtom`) is a recursive master
element that can contain nested `ChapterAtom` children.
`MatroskaExtractor` previously tracked only a single active
`ChapterEntry` field, causing nested `ChapterAtom` elements to
overwrite the parent `ChapterEntry` and throw a `NullPointerException`
when the parent `ChapterAtom` ended.

This change tracks active `ChapterAtom` elements on a stack
(`ArrayDeque`) so that nested chapters do not overwrite their enclosing
parent chapter.

Issue: androidx#3438
PiperOrigin-RevId: 993722199
When `MediaController.setMediaItems` or `prepare` is called while a
legacy `MediaSessionCompat` is paused or idle (`!playWhenReady`),
`canInitializeLegacyPlaylist` decides whether to eagerly initialize
the playlist now via `prepare` / `prepareFrom*` (setting
`hasInitializedLegacyPlaylist = true` so a subsequent `play()` only
sends `onPlay()`), or wait until `playWhenReady == true` to initialize
via `play` / `playFrom*`:
- Check `PlaybackStateCompat.getActions()` for the specific prepare
  action matching the current `MediaItem` (`ACTION_PREPARE` when the
  item is already known to the session, or `ACTION_PREPARE_FROM_URI`,
  `ACTION_PREPARE_FROM_SEARCH`, or `ACTION_PREPARE_FROM_MEDIA_ID`)
  instead of the coarse `Player.COMMAND_PREPARE` flag (which is
  enabled whenever any single prepare action is supported).
- Reset `hasInitializedLegacyPlaylist = false` when the new playlist
  cannot be eagerly initialized while paused, so a subsequent `play()`
  call dispatches `onPlayFrom*` instead of a plain `onPlay()`.
- In `updateControllerInfo`, check `isLegacyPlaylistInitialized`
  (`playbackState != Player.STATE_IDLE`, non-empty timeline, and a
  platform-known `queueId != QueueItem.UNKNOWN_ID`) rather than
  `canInitializeLegacyPlaylist`, so already-paused platform playlists
  are marked initialized on connection while pending controller-set
  items stay uninitialized until prepared or played.

PiperOrigin-RevId: 993761019
All methods on `InFlightFrameManager` (`trackIfSuccessful`,
`onFrameProcessed`, and `releaseAll`) are confined to a single
playback/internal thread per instance:

* In `CompositionPlayer` (`CompositionVideoPacketReleaseControl`),
  `trackIfSuccessful` is only called from `onRender` and `redraw`,
  `onFrameProcessed` is dispatched via `playbackThreadExecutor`, and
  `releaseAll` is called from
  `CompositionPlayerInternal.releaseInternal`, all on
  `CompositionPlaybackThread`. When
  `PlayerListener.onPlaybackStateChanged` calls
  `FrameAggregator.queueEndOfStream` on the application thread,
  `CompositionVideoPacketReleaseControl.queue` only enqueues packets
  into `packetQueue`, which is later drained in `onRender` on
  `CompositionPlaybackThread`.
* In `Transformer` (`PacketConsumerVideoSampleExporter`),
  `HardwareBufferSampleConsumer`'s `frameConsumer` posts to
  `handlerWrapper`, and `ComponentListener` callbacks (`onWakeup` and
  `onFrameProcessed`) along with `release` all execute on
  `Transformer:Internal`.

PiperOrigin-RevId: 993784339
- Remove SDK_INT < 24 checks and redundant @RequiresApi(24) annotations
- Inline code from nested Api24 wrapper classes

PiperOrigin-RevId: 993818116
When container HAGC metadata is not being applied and the stream
is high bitdepth AV1 on SDK >= 37, parse AV1 OBUs to extract
in-band HAGC metadata and set it explicitly on `MediaCodec` with
`setParameters`. This will make sure that we don't rely on each
individual MediaCodec implementation to pass this metadata
through. Also rewrite all T.35 metadata OBUs as "unknown" so that
`MediaCodec` will ignore them.

PiperOrigin-RevId: 994034813
This adds support for 24-bit and 32-bit integer PCM audio tracks
in Mp4Muxer and FragmentedMp4Muxer by writing the standard ipcm sample
entry and pcmC configuration box per ISO/IEC 23003-5.

PiperOrigin-RevId: 994150191
Handle `STATE_PROCESS_FULL_INPUT` in
`TrimOptimizedExportOperation.getProgress()` and clear `mediaItemInfo`
when falling back on `ERROR_CODE_MUXING_APPEND`. Previously, when a
trim-optimized export fell back to full input processing, `getProgress()`
either remained stuck at `PROGRESS_STATE_WAITING_FOR_AVAILABILITY` (when
abandoned before `mediaItemInfo` was assigned) or reported a non-zero
starting percentage (when falling back on `ERROR_CODE_MUXING_APPEND`
after `mediaItemInfo` was assigned).

PiperOrigin-RevId: 994174333
When expanding an X-ASSET-LIST into multiple ads in AdPlaybackState,
apply X-PLAYOUT-LIMIT across the expanded ad durations in the AdGroup
and mark any trailing ads with zero remaining playout duration as
skipped.

Keep X-PLAYOUT-LIMIT separate from the interstitial duration in
resolveInterstitialDurationUs and only cap the fallback content resume
offset by X-PLAYOUT-LIMIT when X-RESUME-OFFSET is omitted and
X-PLAYOUT-LIMIT is less than the resolved interstitial duration. When
the interstitial duration is unknown (for example before an
X-ASSET-LIST JSON response is fetched or when an X-ASSET-URI omits
duration attributes), default the fallback content resume offset to 0
even if X-PLAYOUT-LIMIT is set, because X-PLAYOUT-LIMIT is an upper
bound on playout time rather than the interstitial duration (RFC
8216bis-22, Appendix D.2).

PiperOrigin-RevId: 994272056
`seekBackwards_withDurationLessRawAac_doesNotAdjustSeek` and
`seekBackwards_withDurationLessAmr_doesNotAdjustSeek` previously used a
custom 2,000ms `pollingWaitUntilCondition` helper to wait for playback
to reach 500ms before seeking. On cold emulator starts, `MediaCodecList`
initialization can exceed 2,000ms, causing the wait to time out while
the uncancelled `postDelayed` polling loop continued running on the main
thread and dereferenced a nulled player reference during `tearDown()`.

Replace `pollingWaitUntilCondition` with
`PlayerFence.passesContentPosition(500)` to match the rest of
`CompositionPlayerSeekTest` and ensure listeners and callbacks are
cleaned up when the test finishes.

PiperOrigin-RevId: 994328318
Ensure `PreloadMediaSource.preload()` only calls
`loadControl.onPrepared(PlayerId.PRELOAD)` on the first preload call,
matching the single `loadControl.onReleased(PlayerId.PRELOAD)` call in
`releasePreloadMediaSource()`.

Issue: androidx#3445
PiperOrigin-RevId: 994333792
1. Why `maxOutputNits = 500.0` was problematic:
Pure SDR output buffers (`[0.0, 1.0]` in BT.709) are display-referred
relative to the OS brightness slider (`1.0` = `255 / 255` sRGB = 100%
of current SDR UI white) and have no headroom above `1.0`. Previously,
`bt2020DisplayLinearNitsToBt709DisplayLinear` normalized against
`maxOutputNits = 500.0` (with an 80% electrical knee at `292.5` nits).
Reserving `171-255 / 255` sRGB for `>203`-nit highlights baked a fixed
`203.15 / 500.0 = 40.6%` linear attenuation into the entire `0-292.5`
nit region (`171 / 255` sRGB at diffuse white). Because the viewer's
eyes adapt to the surrounding `255 / 255` SDR UI white—which the display
compositor scales uniformly with the brightness slider—this dimmed
shadows, midtones, and diffuse white by ~60% in linear light and reduced
perceived colorfulness across all screen brightness settings.

2. Why anchoring at `maxOutputNits = 203.1521` is better:
Anchoring `maxOutputNits = 203.1521` at ITU-R BT.2408 diffuse white
aligns `1.0` with the SDR reference white point expected by the display
compositor, letting the OS brightness slider scale physical brightness
uniformly with the UI. Placing the 80% electrical inflection point at
`118.9` nits (`0.80^2.4 * 203.1521`):

3. Increase BT.2020 to BT.709 soft gamut compression knee from 0.80 to
   0.95:
Because max-RGB distance `d = (ach - C) / ach` is measured in linear
light while sRGB encoding follows a `~1/2.4` power law, a linear knee
at `t0 = 0.80` (`C = 0.20` -> `124 / 255` sRGB) preserved only
`131 / 255` (`51.5%`) of the perceptual sRGB saturation span from white
uncompressed, desaturating the remaining `124 / 255` (`48.5%`) span.
Increasing the linear knee threshold to `t0 = 0.95` (`C = 0.05` ->
`63 / 255` sRGB, `h = 0.05`, `a = 0.9223432`) preserves `192 / 255`
(`75.2%`) of the perceptual sRGB saturation span at 1:1 without
desaturation while softly rolling off out-of-gamut BT.2020 primaries
into `[0.0, 1.0]`.

PiperOrigin-RevId: 994338584
In `protobuf-gradle-plugin`, `task.plugins` configures external
`protoc-gen-*` plugin binaries (such as `grpc`), whereas
`task.builtins` configures code generators built directly into
`protoc` (such as `java`). Putting `"java"` inside `task.plugins`
causes the plugin to log:

```
  protoc plugin 'java' not defined. Trying to use 'protoc-gen-java'
  from system path
```

Switch `task.plugins` to `task.builtins` so `protobuf-gradle-plugin`
invokes the built-in `java` generator directly.

See:
- https://github.com/google/protobuf-gradle-plugin#configure-what-to-generate
- https://developer.android.com/jetpack/androidx/releases/datastore#protobuf-serialization
PiperOrigin-RevId: 994373928
When multiple HLS interstitials share the same AdGroup and one of them
is an X-ASSET-LIST interstitial, resolving the asset list previously
overwrote the entire AdGroup.contentResumeOffsetUs with the resume
offset of the asset list interstitial alone.

Instead, subtract the interstitial's prior unresolved contribution to
the group's contentResumeOffsetUs (clamped at 0 to handle single-ad
group resets) and add the newly resolved resume offset, preserving the
contributions of any other interstitials in the same AdGroup. Also
remove any stale pending snap-in resolution for the interstitial if
its newly resolved duration no longer requires deferred snapping.

PiperOrigin-RevId: 994391990
Test parameter fields annotated with `@BindFlag` or `@BindCanaryMode`
are frequently read only via reflection by `Media3FlagsRule`, which
triggers ErrorProne `UnusedVariable` warnings unless manually
suppressed on every field.

Meta-annotate `@BindFlag` and `@BindCanaryMode` with ErrorProne's
`@Keep` annotation so annotated fields are recognized as used without
requiring `@SuppressWarnings("unused")`.

PiperOrigin-RevId: 994396820
Add an overload for `PlayerStateObserver` and `Player.observeState` accepting
a `stateUpdater: (Player, Player.Events) -> Unit` callback that triggers
initially with empty `Player.Events` and forwards the `Player.Events`
whenever one of the configured events occurs.

Migrate `PresentationState` to use `player?.observeState` with this new
overload, extracting `updateState(player, events)` to handle initial state
sync and selectively update pixelWidthHeightRatio-adjusted video size, shutter
visibility on first rendered frame, and track selections.

PiperOrigin-RevId: 994436807
Route `maybeUpdateSourceInfo` through `ComponentListener` in
`AdsMediaSource.onChildSourceInfoRefreshed` instead of storing a
nullable `playerHandler` field on `AdsMediaSource`, avoiding a race
condition where `releaseSourceInternal` clears the handler before the
`mainHandler` callback runs.

Issue: androidx#3442
PiperOrigin-RevId: 994459054
Unnamed variables (`_`) are a Java 22 feature and cannot be used in the
Media3 Gradle build yet. Replace `boolean _` with `boolean unused` in
`AudioTrackAudioOutputTest`.

PiperOrigin-RevId: 994472016
Allow FragmentedMp4Muxer to emit duration-based fragments when video key frames are farther apart. Keep the default behavior unchanged and avoid non-key cuts when sample reordering is possible.

Add focused tests for enabled and disabled behavior, reordered video, and empty samples.
@robspychala robspychala closed this Oct 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.