It's expected that the subtitle load errors are emitted as a non-fatal
error, but it's not defined exactly when they will be emitted. By
consistently ignoring these every time we 'advance' the player, this
de-flakes the test (currently it fails when `untilFullyBuffered()`
triggers a non-fatal error).
PiperOrigin-RevId: 704219317
This is inspired from TestExoPlayerBuilder.
Adding this class has a few advantages:
- It makes testing easier for apps by configuring Transformer for unit
tests (for example, by setting the clock).
- It removes the Transformer setters that apps should not use for unit
testing (for example, the video MIME type should not be set because
it would cause Transformer to transcode.
- It allows us to add additional setters in the future, for example to
build a Transformer that always fails.
PiperOrigin-RevId: 704181927
Add FrameExtraction.setMediaItem() method
Prefer non-vendor software codecs by default because
vendor codecs sometimes fail when we attempt reuse.
PiperOrigin-RevId: 703486235
The test complements the existing E2E test for HLS in `HlsPlaybackTest` by verifying similar functionality for DASH in `DashPlaybackTest`.
PiperOrigin-RevId: 703454388
After removing `Transformer.Builder.setFlattenForSlowMotion()`, there is no need to keep `flattenForSlowMotion` in Transformer since it's now set in `EditedMediaItem`.
The change also includes making `audioProcessors` and `videoEffects` attributes final.
PiperOrigin-RevId: 703076023
`equals`, `hashCode`, and `getPeriodByUid` are correctly implemented on
`Timeline`. Overriding these in a way that maintains correctness is
fiddly, so this CL prevents that for the 'simple' case of subclasses of
`ForwardingTimeline`. Implementations of `Timeline` that need to
override these methods for performance should extend `Timeline` or
`AbstractConcatenatedTimeline` instead of `ForwardingTimeline`.
PiperOrigin-RevId: 703035721
* Select an encoder that supports HDR editing.
* Set KEY_PROFILE to an HDR10 option
* Use DecodeOneFrameUtil test util to return the MediaCodec format,
which includes HDR_STATIC_INFO
PiperOrigin-RevId: 702752639
The method previously discarded the cue that was active at `timeUs`,
meaning it had started before but had not ended by `timeUs`.
Issue: androidx/media#1939
#cherrypick
PiperOrigin-RevId: 702707611
With this CL, PlaybackVideoGraphWrapper doesn't call the
VideoFrameRenderControl directly anymore (which is one of the goals of
DefaultVideoSink).
PiperOrigin-RevId: 702673821
This code previously passed null or 'default' for every parameter.
Now it tries to mock non-final types, and recursively constructor final
types if possible, eventually giving up and passing null instead.
The previous null-passing behaviour led to a quite confusing failure in
`ForwardingPlayer.addListener` when writing 25c927e9f3 due to an NPE
when trying to compare parameter equality in `Mockito.verify` [1].
With this change, the failure is much clearer [2].
There's a relatively simple case this code still doesn't handle: A final
type like `PlaybackParameters` where the constructor parameters **have**
to be non-default primitives (greater than zero in that case).
-------
[1]
```
java.lang.reflect.InvocationTargetException
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:569)
at androidx.media3.test.utils.TestUtil.assertForwardingClassForwardsAllMethodsExcept(TestUtil.java:687)
at androidx.media3.common.ForwardingPlayerTest.forwardingPlayer_forwardsAllPlayerMethods(ForwardingPlayerTest.java:110)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:569)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.robolectric.RobolectricTestRunner$HelperTestRunner$1.evaluate(RobolectricTestRunner.java:588)
at org.robolectric.internal.SandboxTestRunner$2.lambda$evaluate$2(SandboxTestRunner.java:290)
at org.robolectric.internal.bytecode.Sandbox.lambda$runOnMainThread$0(Sandbox.java:101)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
at java.base/java.lang.Thread.run(Thread.java:840)
Caused by: java.lang.NullPointerException: Cannot invoke "Object.hashCode()" because "this.listener" is null
at androidx.media3.common.ForwardingPlayer$ForwardingListener.hashCode(ForwardingPlayer.java:1140)
at java.base/java.lang.Object.toString(Object.java:256)
at java.base/java.lang.String.valueOf(String.java:4220)
at org.mockito.internal.verification.argumentmatching.ArgumentMatchingTool.toStringEquals(ArgumentMatchingTool.java:54)
at org.mockito.internal.verification.argumentmatching.ArgumentMatchingTool.getSuspiciouslyNotMatchingArgsIndexes(ArgumentMatchingTool.java:36)
at org.mockito.internal.verification.checkers.MissingInvocationChecker.checkMissingInvocation(MissingInvocationChecker.java:45)
at org.mockito.internal.verification.Times.verify(Times.java:37)
at org.mockito.internal.verification.MockAwareVerificationMode.verify(MockAwareVerificationMode.java:30)
at org.mockito.internal.handler.MockHandlerImpl.handle(MockHandlerImpl.java:75)
at org.mockito.internal.handler.NullResultGuardian.handle(NullResultGuardian.java:29)
at org.mockito.internal.handler.InvocationNotifierHandler.handle(InvocationNotifierHandler.java:34)
at org.mockito.internal.creation.bytebuddy.MockMethodInterceptor.doIntercept(MockMethodInterceptor.java:82)
at org.mockito.internal.creation.bytebuddy.MockMethodInterceptor.doIntercept(MockMethodInterceptor.java:56)
at org.mockito.internal.creation.bytebuddy.MockMethodInterceptor$DispatcherDefaultingToRealMethod.interceptAbstract(MockMethodInterceptor.java:161)
at androidx.media3.common.Player$MockitoMock$1276619531.addListener(Unknown Source)
... 22 more
```
----
[2]
```
Argument(s) are different! Wanted:
player.addListener(
Mock for Listener, hashCode: 107929032
);
-> at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
Actual invocations have different arguments:
player.addListener(
androidx.media3.common.ForwardingPlayer$ForwardingListener@af47cec0
);
-> at androidx.media3.common.ForwardingPlayer.addListener(ForwardingPlayer.java:81)
```
PiperOrigin-RevId: 702378861
This only affects the default SDK level that Robolectric tests run at.
Also upgrade the Robolectric version to 4.14.1 to pick up
4f32042afe
which is needed for async `ShadowMediaCodec` support (the default in
ExoPlayer from API 31+).
Some tests fail on Robolectric at API 31. This change configures them to
continue running on API 30, so the failures can be investigated and
fixed in follow-up changes.
PiperOrigin-RevId: 702357124
Earlier this year, we explored frame extraction built on top of Transformer
or ImageReader.
We decided against these approaches because random-access of frames
is a key requirement. And ImageReader behaviour is unreliable.
PiperOrigin-RevId: 702303490
This means we can complete preparation (and trigger track selection)
before opening a `DataSource`, which then means we only end up loading
the data for a selected subtitle track (instead of all tracks as
currently happens).
By making preparation trivial in this case (with no reasonable cause
of error), we can also remove the `suppressPrepareError` option added in
b3290eff10.
This change also fixes the implementation of
`ProgressiveMediaPeriod.maybeStartDeferredRetry` to only short-circuit
return `false` if the chosen track is not audio or video **and** there
is at least one audio or video track in this period.
Issue: androidx/media#1721
PiperOrigin-RevId: 702275968
This method was taking positionUs and elapsedRealtimeUs parameters and
throwing a VideoSinkException because it was calling render() to make
room for a new input frame. Instead of doing that, this CL makes sure
render() is called before handleInputFrame() in the renderer (even
though it may not make a significant difference).
PiperOrigin-RevId: 702273587
Add missing ones for `ForwardingExtractorInput` and `ForwardingSeekMap`.
`ForwardingTimeline` is a bit more fiddly, so it's left for a follow-up
change.
PiperOrigin-RevId: 701988492
Previously cancelling one FrameExtractor.getFrame
ListenableFuture caused the following requests to fail.
Implement ability to cancel Futures, and correctly issue
next player seek commands after all past requests complete.
Removes uses of SettableFuture, and replaces them with
CallbackToFutureAdapter.
Adds a dependency on androidx.concurrent:concurrent-futures
PiperOrigin-RevId: 701941403
Frame Extractor was getting stuck with SeekParameters.CLOSEST_SYNC.
onPositionDiscontinuity callback was sometimes being called with a
non-adjusted new position.
Fix this by monitoring player state ready.
For the player to become ready, we have to override renderer isReady.
PiperOrigin-RevId: 701924752
When a session sets a custom layout, we currently convert it to the
media button preferences in MediaControllerImplBase and convert it back
to a custom layout for consumers of MediaController.getCustomLayout.
To avoid unnecessary conversions and potential changes, we can
directly use the provided custom layout to get the same logic as
before. This also means we avoid notifying a change to the custom layout
if only the implicit slots changed that weren't relevant before
introducing them (=we can remove some tests for this behavior).
PiperOrigin-RevId: 701903475
The isLastBuffer flag passed to MediaCodecRenderer.processOutputBuffer()
is unreliable. It is true for the last frames (plural) of a video
stream, which makes it possible for the video renderer to end before all
the frames have been rendered to the output surface.
PiperOrigin-RevId: 700941685
This aligns the behavior with `MediaExtractor`, which sets this key in its [`MediaFormat`](https://developer.android.com/reference/android/media/MediaExtractor#getTrackFormat(int)) output.
Additionally, unnecessary `selectTrack` calls have been removed from the existing `getTrackFormat_...` tests, as they are not required to fetch the track format.
PiperOrigin-RevId: 700741326
Currently, `size` is cached in `SampleMetadata` when samples are committed in `SampleQueue`, which worked because we didn't support reading files with `CryptoInfo`. This change prepares for future `CryptoInfo` support, as the size changes when reading samples with encrypted data. Caching the size at commit time could lead to incorrect buffer `size` calculations.
Existing tests verify the correctness of the `getSampleSize()` API.
PiperOrigin-RevId: 700714178
The Android SKD version 30 is stable, allowing us to remove the `@SuppressLint("Override")` annotations from `InputReaderAdapterV30` and `OutputConsumerAdapterV30`.
PiperOrigin-RevId: 700681115
Introduced a `mediaSourceFactory` attribute to CompositionPlayer. This allows for the use of a custom MediaSource.Factory when creating media sources, providing more flexibility and control over media source creation. Previously, the default `MediaSource.Factory` was used in all cases.
PiperOrigin-RevId: 700391911
The previous code assumed that the `VBRI` Table of Contents (ToC)
covers all the MP3 data in the file. In a file with an invalid VBRI ToC
where this isn't the case, this results in playback silently stopping
mid-playback (and either advancing to the next item, or continuing to
count up the playback clock forever). This change considers the `bytes`
field to determine the end of the MP3 data, in addition to deriving it
from the ToC. If they disagree we log a warning and take the max value.
This is because we handle accidentally reading non-MP3 data at the end
(or hitting EoF) better than stopping reading valid MP3 data partway
through.
Issue: androidx/media#1904
#cherrypick
PiperOrigin-RevId: 700319250
The current code assumes that the first Table of Contents segment
includes the `VBRI` frame, but I don't think this is correct and it
should only include real/audible MP3 ata - so this change updates the
logic to assume the first ToC segment starts at the frame **after** the
`VBRI` frame.
Issue: androidx/media#1904
#cherrypick
PiperOrigin-RevId: 700269811
This was hand-crafted with a 4-entry ToC by modifying
`bear-vbr-xing-header.mp3` in a hex editor.
The output difference from 117 samples to 116 samples is due to the
calculation in `VbriSeeker` assuming that the ToC includes the VBRI
frame itself, which I don't think is correct (fix is in a follow-up
change).
Issue: androidx/media#1904
#cherrypick
PiperOrigin-RevId: 700254516
Support extracting frames from HDR input by tone mapping
to SDR (BT.709).
ExperimentalFrameExtractor must be public because HDR tests
live in a different package.
PiperOrigin-RevId: 699994112
Adds a configuration option to Frame Extractor to choose MediaCodecSelector.
Add a MediaCodecSelector that lists software decoders first
PiperOrigin-RevId: 699962365
Renderers join when they can start processing but not produce output.
In CompositionPlayer before this change, the VideoSink will be flushed when
position is reset regardless. This is useful for seeking, as seeking triggers position reset. But with pre-warming, a video renderer can be joining - it is
enabled (which also triggers position reset) before the previous image renderer
is disabled. At this moment, as the image renderer is still producing frames,
we should not flush the video sink just now.
PiperOrigin-RevId: 699943882
We are currently waiting until stream 1 is completely rendered on screen
before registering stream 2 but that is not necessary anymore because
VideoFrameProcessor supports registering stream 2 before stream 1 is
fully processed.
PiperOrigin-RevId: 699929943
Before this CL, the following scenario could happen:
- A new input stream is registered to the DefaultVideoFrameProcessor.
- Before the pipeline is reconfigured, a seek is issued andd the
DefaultVideoFrameProcessor is flushed.
- As a result, the new input stream registration is never taken into
account.
As a result:
- If an input stream is registered after the seek (before queueing any
frame), registerInputStream will block indefinitely because
inputStreamRegisteredCondition will be closed.
- If a frame is queued after the seek, it will be linked to the input
stream information of the previous frames.
This CL makes sure that any pending input stream is registered after a
flush.
PiperOrigin-RevId: 698736866
This call does 2 things:
1. It's used to estimate the frame rate early but this is incorrect as
the frame rate can be changed by effects. I have tested playback on
my device and I don't see any difference.
2. For ExoPlayer.setVideoEffects() only: it allows dropping all the
frames until the next key frame in case the input frame is "very
late". This doesn't seem really necessary though because because we
do the same thing after applying effects.
This change is also a step towards moving all the calls to
VideoFrameRelease/RenderControl to DefaultVideoSink.
PiperOrigin-RevId: 698732161
Changes includes making sure the same `defaultMediaSourceFactory` is used when creating a new media source in `setPrimaryPlayerSequence` and `setSecondaryPlayerSequence`.
This is pre-work for introducing a new attribute `mediaSourceFactory` that can be optionally used instead of the default one. This will allow developers to pass a customized media source factory to CompositionPlayer.
PiperOrigin-RevId: 698721139
This CL also aligns supported color space in `media3 common` and `media3 muxer`.
Earlier `muxer` would take even those values which are considered invalid
in `media3` in general.
Earlier muxer would throw if a given `color standard` is not recognized
but with the new change, it will rather put default `unspecified` value.
#cherrypick
PiperOrigin-RevId: 698683312
Adds a Frame extractor test that verifies decoder
respects rotation metadata from the mp4 container.
Do not rely on the MediaCodec decoder rotate the input.
Rotate via a video effect instead.
PiperOrigin-RevId: 698381282
This non-functional refactor inlines and simplifies the logic of
`BaseAudioProcessor` used by `SpeedChangingAudioProcessor`, and makes
the latter implement `AudioProcessor` directly.
`SpeedChangingAudioProcessor` acts as a wrapper over
`SonicAudioProcessor` and does not actually modify any samples,
so it had very little use for the affordances provided by
`BaseAudioProcessor`.
PiperOrigin-RevId: 698342962
3P app reported it's slow to see the first image frame on the screen, when
`playWhenReady` is false. This is because the player would wake up less
frequently when `playWhenReady` is false. This CL adds a runnable to wake up
the player any time a new frame is made available.
PiperOrigin-RevId: 698066887
SpeedChangingAudioProcessor is redundantly bypassing SonicAudioProcessor
when the speed is set to 1f. SpeedChangingAudioProcessor should not make
assumptions about the underlying audio processing and moreover should
not be bypassing any AudioProcessor based on parameter values. Default
parameter values are a valid state for an active AudioProcessor.
Sonic already handles the "default case" state by just copying the input
buffer onto the output buffer.
This CL also simplifies SonicAudioProcessor, which would mark itself as
inactive when configured with a valid set of default parameters. The API
contract for `isActive()` makes no mention about parameter state, which
makes changes in `isActive()` after applying new valid parameters quite
unintuitive.
PiperOrigin-RevId: 698000500
The following was happening:
- VideoFrameRenderControl.render() was calling
VideoFrameReleaseAction.getFrameReleaseAction().
- VideoFrameReleaseAction.getFrameReleaseAction() was calling
FrameTimingEvaluator.shouldIgnoreFrame(), which is implemented in
MediaCodecVideoRenderer.
- MediaCodecVideoRenderer.shouldIgnoreFrame(), when returning true,
was also flushing the VideoSink, which was flushing the
VideoFrameRenderControl.
- VideoFrameRenderControl.render() was removing the frame from the
presentationTimestampsUs queue, but the frame had already been removed
by the flush operation, causing an exception to be thrown.
This fix removes the last step.
PiperOrigin-RevId: 697915692