Currently ExoPlayer schedules its main work loop on a 10 ms interval. When renderers cannot make any more progress (ex: hardware buffers are fully written with audio data), ExoPlayer should be able to schedule the next work task further than 10ms out into the future.
Through `experimentalSetDynamicSchedulingEnabled` and these changes to `MediaCodecAudioRenderer`, ExoPlayer can use the data provided by the audio renderer to dynamically schedule its work tasks based on when it is expected that progress can be made.
PiperOrigin-RevId: 638677454
Currently ExoPlayer schedules its main work loop on a 10 ms interval. When renderers cannot make any more progress(ex: hardware buffers are fully written with audio data), ExoPlayer should be able to schedule the next work task further than 10Ms out.
Through `experimentalSetDynamicSchedulingEnabled`, ExoPlayer will dynamically schedule its work tasks based on when renderers are expected to be able to make progress.
PiperOrigin-RevId: 638676318
If subtitle-parsing-during-extraction is enabled (now defaults to on),
the 'outer' extractor class name is often
`SubtitleTranscodingExtractor`, leading to some slightly useless error
messages like:
`None of the available extractors (FragmentedMp4Extractor, Mp4Extractor, SubtitleTranscodingExtractor, SubtitleTranscodingExtractor, SubtitleTranscodingExtractor, SubtitleTranscodingExtractor, SubtitleTranscodingExtractor, SubtitleTranscodingExtractor, TsExtractor, MatroskaExtractor, SubtitleTranscodingExtractor, SubtitleTranscodingExtractor, SubtitleTranscodingExtractor, SubtitleTranscodingExtractor, AviExtractor, SubtitleTranscodingExtractor, SubtitleTranscodingExtractor, SubtitleTranscodingExtractor, SubtitleTranscodingExtractor, SubtitleTranscodingExtractor, SubtitleTranscodingExtractor)`
PiperOrigin-RevId: 636834354
If MediaCodec allocates passes an image buffer with a cropped region,
SurfaceTexture.getTransformMatrix will cut off 2 pixels from each dimensions.
The resulting videos will appear a little stretched.
This patch inspects the SurfaceTexture transform matrix, and guesses what the
unscaled transform matrix should be.
Behind experimentalAdjustSurfaceTextureTransformationMatrix flag
PiperOrigin-RevId: 635721267
This case is most likely to happen when re-preparing a multi-period
live stream after an error. The live timeline can easily move on to
new periods in the meantime, creating this type of update.
The behavior before this change has two bugs:
- The player resolves the new start position to a subsequent period
that existed in the old timeline, or ends playback if that cannot
be found. The more useful behavior is to restart playback in the
same live item if it still exists.
- MaskingMediaSource creates a pending MaskingMediaPeriod using the
old timeline and then attempts to create the real period from the
updated source. This fails because MediaSource.createPeriod is
called with a periodUid that does no longer exist at this point.
We already have logic to not override the start position and need
to extend this to also not prepare the real source.
Issue: androidx/media#1329
PiperOrigin-RevId: 634833030
The two affected tests where playing until a specific
position to enable the player to read ahead. The method
pauses at exactly the target position, but then has
temporarily undetermined behavior because the playback
thread uses player.getClock().onThreadBlocked() that lets
the playback thread make progress in parallel to the test
thread. The tests were flaky because they sometimes made
so much progress that they ended playback before we could
query the updated renderer state.
This can be fixed by using
run(player).untilBackgroundThreadCondition instead, which
is guaranteed to be fully deterministic, but may not be able
to stop at exactly the desired position (which we don't
really need anyway for this test).
PiperOrigin-RevId: 634699752
The method is only allowed to be called on prepared items.
This check was currently missing and also causing the
corresponding test to be flaky in ExoPlayerTest.
PiperOrigin-RevId: 634694077
Before this change, if a playback error is thrown the test fails with a
timeout and no additional info:
```
java.util.concurrent.TimeoutException
at androidx.media3.exoplayer.e2etest.WebvttPlaybackTest.stallPlayerUntilCondition(WebvttPlaybackTest.java:361)
```
After this change, the test failure includes a much more useful stack
trace, e.g. from 0352db9a37:
```
Caused by: java.lang.IllegalStateException: Legacy decoding is disabled, can't handle text/vtt samples (expected application/x-media3-cues).
at androidx.media3.common.util.Assertions.checkState(Assertions.java:100)
at androidx.media3.exoplayer.text.TextRenderer.assertLegacyDecodingEnabledIfRequired(TextRenderer.java:587)
at androidx.media3.exoplayer.text.TextRenderer.onStreamChanged(TextRenderer.java:210)
```
PiperOrigin-RevId: 634672138
To override this change, and go back to parsing during rendering,
apps must make two method calls:
1. `MediaSource.Factory.experimentalParseSubtitlesDuringExtraction(false)`
2. `TextRenderer.experimentalSetLegacyDecodingEnabled(true)`
PiperOrigin-RevId: 634262798
The test currently resets the time too far in the past and then has
to run through ~30000 additional iterations of doSomeWork to reach
the end, sometimes triggering the test timeout.
Fix it by resetting the time to the intended start position when
transitioning items.
PiperOrigin-RevId: 633918706
This annotation is only needed on public classes.
This change also removes the `/* package */` comment from some `public`
classes.
PiperOrigin-RevId: 633864544
This means the content source is 'prepared' instantly with a
placeholder, enabling all further preparation steps (e.g. loading
preroll ads) while the actual content is still preparing. This
improvement can speed up the start time for prerolls in manifest-based
content that doesn't have a zero-time preparation step like progressive
media.
Issue: androidx/media#1358
PiperOrigin-RevId: 633640746
This allows apps to better detect when the platform
reclaims a codec. This requires adding the error code
to MediaCodecDecoderException.
PiperOrigin-RevId: 633588914
The class currently tracks the input format itself, updating it too
early in onConfigure() instead of onFlush(). This causes issues when
the format changes and the new values are applied to the silence
skipping logic of the old format. The fix is to use the base class
input format handling instead.
Issue: androidx/media#1352
PiperOrigin-RevId: 633232368
Due to loading a MediaSourceFactory via reflection:
Before this change
* the content type was logged as an integer, rather than a human-readable string
* `ClassNotFoundException` was swallowed silently by `maybeLoadSupplier` without telling the user what module they were missing
After:
* ClassNotFoundException is swallowed silently ONLY when determining supported types
* ClassNotFoundException is bubbled up when we are trying to play media without the corresponding module properly loaded
PiperOrigin-RevId: 632568989
The same doSomeWork iteration that triggers the silence skipping
discontinuity may already have another discontinuities (like
AUTO_TRANSITION), which should take precedence over the silence
skipping.
PiperOrigin-RevId: 632432851
When the period has loaded to the end of the source, the `period.getBufferedPositionUs` will be set to `C.TIME_END_OF_SOURCE`, which is a negative value. Thus, the original `continueLoadingPredicate` will never turn to `false`, as the `bufferedPositionUs` is definitely less than the target preload position that is expected to be positive.
In this change, we added `PreloadMediaSource.PreloadControl.onLoadedToTheEndOfSource(PreloadMediaSource)` to indicate that the source has loaded to the end. This allows the `DefaultPreloadManager` and the custom `PreloadMediaSource.PreloadControl` implementations to preload the next source or take other actions.
This bug was not revealed by the the `DefaultPreloadManagerTest` because the related tests were all using the `FakeMediaSource` and only setting the preload target to `STAGE_TIMELINE_REFRESHED`. Thus, the tests for testing the `invalidate()` behaviors were modified to use the real progressive media whenever possible, unless we have to use `FakeMediaSource` to squeeze a chance to do more operations between the preloading of sources to test some special scenarios.
PiperOrigin-RevId: 631776442
For offloaded playback, reset the tracking field for stream completion in `DefaultAudioSink` prior to calling `AudioTrack.stop()` so that `AudioTrack.StreamEventCallback#onPresentationEnded` correctly identifies when all pending data has been played.
#minor-release
PiperOrigin-RevId: 631744805
When the PlaybackParameters are set to their DEFAULT value, we
currently bypass the audio processor chain when determining the
output media position, under the assumption that no timestamp
change happens in the audio processors. This assumption may not
be true as the audio processors can change playout durations on
their own accord independent of the provided PlaybackParameters.
To correctly reflect any updated playout duration, we can just
always check the audio processor chain. The default implementation
will continue to assume that only the SonicAudioProcessor changes
the playout duration.
PiperOrigin-RevId: 631726112
Without it, the DAC doesn't render the full sentence. And the link is not actually linking to the proper constructors.
#minor-release
PiperOrigin-RevId: 630395271
Set `VideoSink`'s offset during seeking in `MCVR.onPositionReset()`
This one is necessary in some cases, where `onProcessedStreamChange()` is not
invoked during a seek. For example, when seeking when playback has ended.
PiperOrigin-RevId: 630056723
To find the column of an index in a matrix the formula "column = index % width" should be used, not "column = index % height"
If inputFormat.tileCountVertical was equal to 1 then it would not throw an error, but instead result in the first tile of the bitmap always being returned. If inputFormat.tileCountVertical was larger than 1 then Bitmap.createBitmap() would throw an error as it would attempt to go outside the bounds of outputBitmap
ImageRenderTest has been updated to test for 2x3 images so that tileCountVertical != tileCountHorizontal. These tests passed previously because they were equal, so using tileCountVertical produced the same results as tileCountHorizontal
The last rebuffer time was being updated erroneously, even in the absence of rebuffering events, resulting in incorrect `bs` (buffer starvation) key in CMCD.
Issue: androidx/media#1124
PiperOrigin-RevId: 629731796
These subtitles were skipped because they are marked as shouldBeSkipped
based on their timestamps. The fix removes this flag entirely in
SimpleSubtitleDecoder because TextRenderer handles potential skipping
if needed.
PiperOrigin-RevId: 629717970
`updateRebufferingState` is invoked immediately preceding this `if-else`, with no alteration of state occurring in between, making this invocation unnecessary.
PiperOrigin-RevId: 629694531
This lets apps update the task manager priority and send the
priority message to all renderers so that they can adjust their
resources if needed.
PiperOrigin-RevId: 629426058
This change adds a 'free-form' text genre to the existing
`bear-id3.mp3` sample, and adds a new sample with a 'numeric' genre ([9
is Metal](https://mutagen-specs.readthedocs.io/en/latest/id3/id3v2.4.0-frames.html#appendix-a-genre-list-from-id3v1)).
The samples were modified with:
```shell
$ id3edit --set-genre "Gorpcore" bear-id3.mp3
$ id3edit --set-genre "9" bear-id3-numeric-genre.mp3
```
Reading the numeric genre with `exiftool` shows the mapping back to
'Metal':
```
$ exiftool bear-id3-numeric-genre.mp3 | grep Genre
Genre : Metal
```
The playback dumps don't contain the genre because it's not yet
propagated to `MediaMetadata.genre`. That is done in a follow-up
change.
Issue: androidx/media#1305
PiperOrigin-RevId: 629043506
This removes a window of inconsistency between the timeline
known to the player and the most recent timeline in the
`MediaSourceList` by removing the `MSG_DO_SOME_WORK` when
sending `MSG_PLAYLIST_UPDATE_REQUESTED`. `MSG_DO_SOME_WORK`
is then sent again when the playlist update is requested.
PiperOrigin-RevId: 629021752
Some test cases are still using `ConcatenatingMediaSource`
even if they do not test specific features of the concatenating
source. Apparently these test have a slightly different timing
behavior when it comes to updating the `MediaPeriodQueue` and
emitting change events with analytics.
Using the playlist API ensures testing the future-proof code path.
PiperOrigin-RevId: 628413460
The first has a string genre, and various other values set, generated
from `sample.mp4` with the command below [1].
The second has a numeric genre, to test `gnre` atom parsing. This
parsing is currently broken, the fix is in a follow-up change. This
file was also generated from `sample.mp4` with the command below [2].
This change also includes `CommentFrame.text` in its `toString`
representation, otherwise there's no difference between e.g. different
levels of `ITUNESADVISORY` in the extractor dump files.
Issue: androidx/media#1305
-----
[1]
```shell
$ AP_PADDING="DEFAULT_PAD=0" \
AtomicParsley sample.mp4 \
--artist "Test Artist" \
--album "Test Album" \
--tracknum 2/12 \
--disk 2/3 \
--year 2024 \
--genre "Gorpcore" \
--bpm 120 \
--compilation true \
--advisory clean \
--gapless true \
--sortOrder artist "Sorting Artist" \
--sortOrder album "Sorting Album" \
--preventOptimizing \
-o sample_with_metadata.mp4
```
[2]
```shell
$ AP_PADDING="DEFAULT_PAD=0" \
AtomicParsley sample.mp4 \
--genre "Metal" \
--preventOptimizing \
-o sample_with_numeric_genre.mp4
```
PiperOrigin-RevId: 628345458
Before this CL, the video sink was not reconfigured for the second video
in a sequence with video-image-video. For example, the stream offset
and listener were not set for the second video.
PiperOrigin-RevId: 628065991
Instead of initializing the video sink outside the renderer with an
empty format for composition preview, we initialize it in the renderer
with the input format for video.
PiperOrigin-RevId: 627313708
When playing a Composition, the color transfer of an image is incorrectly
passed down to be SMPTE170M, but it should be SRGB.
PiperOrigin-RevId: 626425396
For audio with more than 12 channels, no channel mask was determined, which
meant that the code to check spatializability would throw because of creating
an invalid audio format.
Return early if the channel mask was invalid instead (and assume spatialization
isn't possible).
PiperOrigin-RevId: 625618683
Before this CL, the FrameMetadataListener was set in
onReadyToInitializeCodec while it had already been set when handling
message MSG_SET_VIDEO_FRAME_METADATA_LISTENER.
PiperOrigin-RevId: 624940824
Some seek operations are currently filtered in the base classes if
the target index is not explicitly specified and the implicitly
assumed operation doesn't have an obvious target index. (Example:
calling seekToNext() at the last item in a playlist)
This is too opinionated because the actual player implementation
using this base class may be able to handle this seek request (e.g.
by adding new items on the fly or using other logic to select
the most suitable next item).
This can be solved by forwarding all seek requests to the respective
handler methods even if they are a presumed no-op. Also clarify the
Javadoc that the provided index is just an assumption if it wasn't
provided explicitly in the method call.
PiperOrigin-RevId: 624887116
Before this CL, the offset was set:
- in onPositionReset for composition preview
- in onReadyToInitializeCodec for ExoPlayer
The code flow is easier to reason about if the path for both use cases
is as shared as possible.
PiperOrigin-RevId: 624870150
This removes the TODOs without updating the links, because the
DAC-hosted images are not co-located with the hosted javadoc (unlike
when these images were referenced on exoplayer.dev before 10342507f7),
and therefore we would need to include the full path anyway, at which
point it seems clearer and more robust to keep using a fully-qualified
URL with the domain too.
PiperOrigin-RevId: 623452217
Compared to `release`, the `reset` method doesn't release the preload manager instance. This applies to the use case that an app wants to discard all the sources but keep the preload manager active for later usage.
Also rename the `releaseSourceInternal` to `removeSourceInternal`, as the latter sounds more generic for different preload manager implementations.
PiperOrigin-RevId: 623148723
In offloaded audio playback, the `DefaultAudioSink` should use the `AudioTrack.StreamEventCallback` `onPresentationEnded` to note whether the AudioTrack has completed playing all pending data.
PiperOrigin-RevId: 622885399
Both `remove(MediaItem)` and `remove(MediaSource)` return a boolean suggesting that whether the preload manager is holding the corresponding `MediaSource` and it has been removed.
PiperOrigin-RevId: 622185427
This change makes sure the `DefaultLoadControl` would work
when passed to multiple players. It makes sure and unit tests
that the loading state of a player is maintained for each player
that is using `DefaultLoadControl`.
The targetBufferSize of the `DefaultAllocator` is increased
linearly for each player and memory is allocated in a simple
first-come-first-serve manner.
PiperOrigin-RevId: 622126523
The join mode is used for two cases: surface switching and mid-playback
enabling of video.
In both cases, we want to pretend to be ready despite not having rendered
a new "first frame". So far, we also avoided force-rendering the first
frame immediately because it causes a stuttering effect for the
mid-playback enable case. The surface switch case doesn't have this
stuttering issue as the same codec is used without interruption. Not
force-rendering the frame immediately causes the first-frame rendered
callback to arrive too early though, which may lead to cases where
apps hide shutter views too quickly.
This problem can be solved by only avoiding the force-render for the
mid-playback enabling case, but not for the surface switching case.
PiperOrigin-RevId: 622105916
Also explicitly use a synchronized list to collect cues. The previous
bare `ArrayList` was probably fine, because the `ConditionVariable`
creates a memory barrier between the main thread and the test thread,
but this seems more explicit (and any performance implications of the
synchronization don't matter for this test).
PiperOrigin-RevId: 621523824
The workaround check is now part of CTS and we should be able
to rely on the PerformancePoints values (or at least can be sure
that they cover all CDD requirements)
#minor-release
PiperOrigin-RevId: 619201331
Some media can read color info values from the bitstream
and may partially set some of the SDR default values in
Format.ColorInfo. Setting these default values for SDR can
confuse some codecs and may also prevent adaptive ABR
switches if not all ColorInfo values are set in exactly the
same way.
We can avoid any influence of HDR color info handling by
disabling setting the color info MediaFormat keys for SDR
video and also avoid codec reset at format changes if both
formats are SDR with slightly different ColorInfo settings.
To identify "SDR" ColorInfo instances, we need to do some
fuzzy matching as many of the default values are assumed to
match the SDR profile even if not set.
Issue: androidx/media#1158
PiperOrigin-RevId: 617473937
Add a new method `onUsedByPlayer(PreloadMediaSource)` for `PreloadMediaSource.PreloadControl`, which will be invoked when the player starts to use the `PreloadMediaSource`, or calling `PreloadMediaSource.preload` while the player is already using that source. `DefaultPreloadManager` will immediately preload the next source when receiving `onUsedByPlayer` event.
PiperOrigin-RevId: 616789121
Confirmed that the HLG extension displays properly on Pixel 7 Pro, API 34 and Samsung S24, API 34. On these devices, this fixes the washed out HLG preview issue on API 34, where the PQ ext had a washed out look, and where the HLG ext is supported (this bug didn't occur on API 33, only 34).
More info on manual tests done to sanity-check:
* Test cases: Transformer debug SurfaceView and ExoPlayer.setVideoEffects
* Test inputs: HLG and PQ
* Test devices: Pixel 7 Pro (API 33 & 34), Samsung Galaxy S24 (API 34)
* Added debugging: Logging colorInfo used in GlUtil.createEglSurface
No regressions were seen. HLG extension is used more in API 34, and behavior stays the same on API 33. Only human-visible change without logging is that HLG content looks better on API 34, for Samsung S24 and Pixel 7 Pro.
PiperOrigin-RevId: 616131192
Renderers may be enabled for subsequent media items as soon as the current media item's renderer's isEnded() returns true. Currently, when a player is set to pause, it stops all renderers that are `STATE_STARTED`. When a player is set to play, it starts all renderers that are enabled. This would include renderers that were enabled early for the subsequent media item. The solution is to only start renderers that are enabled by the current playing period.
Issue: androidx/media#1017
PiperOrigin-RevId: 614734437
This should already be the default, but some devices seem
to not adhere to this contract and assume the default is unset.
Issue: androidx/media#1169
PiperOrigin-RevId: 614697283
Some devices just don't work very well with the synchronous
model, but are currently still excluded from our approximate
API 31 check. This change allows to include additional devices
or device groups by passing in the Context to the default
adapter.
It also adopts the workaround added in ebceee08c6
for Fire TV Smart devices that exhibit this issue.
PiperOrigin-RevId: 614642545
Some FireOS6 devices ask to force the external surround global
flag and ignore any signals from the HDMI connection.
This is the equivalent change of e341944d1e
PiperOrigin-RevId: 614634499
Using the more accurate check available on later API versions
first is likely better than falling back to a fallback solution
from older API versions.
PiperOrigin-RevId: 614612628
These input types include images, video without audio, and video with audio.
While playing these inputs, the video frame presentation is always synced with
audio.
PiperOrigin-RevId: 613921719
Also remove intermediate object allocations in
`Util.toByteArray(int...)`.
`Util.toByteArray(InputStream)` is kept (but deprecated) because it's
been part of the library for a while and might be in use by some apps.
The others are much newer, so the chance of usages outside the library
is very low, so we just remove them directly.
PiperOrigin-RevId: 613878453
Renderers may be enabled for subsequent media items as soon as the current media item's renderer's isEnded() returns true. When a renderer is being enabled and the player is 'playing', that renderer is also started. When playing a mixed playlist of images and content with audio & video, the player may skip some image items because the early-starting of the audio renderer causes a clock update.
A solution is to only start the "early-enabled" renderers at the point of media transition and add a condition on DefaultMediaClock to use the standalone clock when reading-ahead and the renderer clock source is not in a started state.
Issue: androidx/media#1017
PiperOrigin-RevId: 613231227
The old methods are deprecated and are called from the new
method for backwards compatibility of custom implementations.
'DefaultLoadControl' is unchanged, but `ExoPlayerImplInternal`
already calls the new methods passing in the `PlayerId`,
PiperOrigin-RevId: 613197190
`BasePreloadManager` coordinates the preloading for multiple sources based on the priorities defined by their `rankingData`. Customization is possible by extending this class. Apps will implement `TargetPreloadStatusControl` to return preload manager the target preload status for a given `rankingData` of the source.
`DefaultPreloadManager` extends from the above base class and uses `PreloadMediaSource` to preload media samples of the sources into memory. It also uses an integer `rankingData` that indicates the index of an item on the UI, and the priority of the items is determined by their adjacency to the current playing item. Apps can set the index of current playing item via `DefaultPreloadManager.setCurrentPlayingIndex` when the user swiping is detected.
PiperOrigin-RevId: 612829642
This implementation generates lint errors because neither the `first`
nor `second` parameters are used, and that's generally
unexpected/incorrect for a `Comparator` implementation since it should
always consider both its parameters.
PiperOrigin-RevId: 611039632
This change aims to prioritise tracks that have a 'smooth enough for
video' frame rate, without always selecting the track with the highest
frame rate.
In particular MP4 files extracted from motion photos sometimes have two
HEVC tracks, with the higher-res one having a very low frame rate (not
intended for use in video playback). Before this change
`DefaultTrackSelector` would pick the low-fps, high-res track.
This change adds a somewhat arbitrary 10fps threshold for "smooth video
playback", meaning any tracks above this threshold are selected in
preference to tracks below it. Within the tracks above the threshold
other attributes are used to select the preferred track. We deliberately
don't pick the highest-fps track (over pixel count and bitrate), because
most users would prefer to see a 30fps 4k track over a 60fps 720p track.
This change also includes a test MP4 file, extracted from the existing
`jpeg/pixel-motion-photo-2-hevc-tracks.jpg` file by logging
`mp4StartPosition` in
[`MotionPhotoDescription.getMotionPhotoMetadata`](b930b40a16/libraries/extractor/src/main/java/androidx/media3/extractor/jpeg/MotionPhotoDescription.java (L123))
and then using `dd`:
```
mp4StartPosition=2603594
$ dd if=jpeg/pixel-motion-photo-2-hevc-tracks.jpg \
of=mp4/pixel-motion-photo-2-hevc-tracks.mp4 \
bs=1 \
skip=2603594
```
----
This solution is in addition to the `JpegMotionPhotoExtractor` change
made specifically for these two-track motion photos in
5266c71b3a.
We will keep both changes, even though that change is not strictly
needed after this one, because adding the role flags helps to
communicate more clearly the intended usage of these tracks. This
change to consider FPS seems like a generally useful improvement to
`DefaultTrackSelector`, since it seems unlikely we would prefer a 5fps
video track over a 30fps one.
Issue: androidx/media#1051
PiperOrigin-RevId: 611015459
These audio offload failure recovery tests model the DefaultAudioSink failing at audio track init and write operations in offload mode. Playback should recover and try again as DefaultAudioSink will disable offload mode.
PiperOrigin-RevId: 610372935
Some devices supporting Performance Points for decoder coverage are missing coverage over the CDD requirements for H264. For these cases ExoPlayer should fall back to legacy resolution and frame rate support checks. If there is a stream evaluated as a PerformancePointCoverageResult of COVERAGE_RESULT_NO, then ExoPlayer checks for coverage of the 720p H264 CDD requirement.
Issue: google/ExoPlayer#10898
Issue: androidx/media#693
Issue: androidx/media#966
PiperOrigin-RevId: 609740128
Even when there's no display surface, MCVR can render frames to VFP, becuase by
the time the frame is processed:
- If there's still no surface, VFP will drop the frame;
- If there's surface, the processed frame would be rendered.
In short, placeholder surface is not needed in effect enabled playback. FWIW,
it is used to swallow frames directly from MediaCodec when there's no output
surface.
PiperOrigin-RevId: 609705222
This change makes ExoPlayerImplInternal.releaseInternal() unblock the
app thread if a runtime exception is thrown while releasing components
from the playback thread.
Before this change, if a runtime exception occurred during releasing
components in the playback thread, ExoPlayer.release() would wait for
`releaseTimeoutMs` and then raise a player error. With this change,
the player error is reported only when the playback thread is blocked
but if there is a runtime exception, the application thread is
unblocked.
The impact of this change is potentially fewer ANRs on
ExoPlayer.release() at the expense of less error reporting.
PiperOrigin-RevId: 609702549
We forgot to add it when we added AudioSink.release(). The commit
includes a test that ensures ForwardingAudioSink overrides all the
methods defined in the AudioSink interface.
PiperOrigin-RevId: 609402258
If the reading period has already advanced and a track reselection procs that only affects the reading period media, then ExoPlayer may try and apply the reading period's track selection incorrectly unto the playing period. ExoPlayer should apply the playing period's track selection to the playing period instead.
PiperOrigin-RevId: 609375077
If render error occurs due to AudioTrack initialization failure in offload mode, then ExoPlayer should allow retry as subsequent attempt will be with DefaultAudioSink disabling offload.
PiperOrigin-RevId: 609304897
The `PreloadMediaPeriod.selectTracksForPreloading` can be called for multiple times at the preloading stage (before the period is being played). For example, when the period resumes preloading. This change fix the assertion failure in `ProgressiveMediaPeriod.selectTracks` caused by the wrong implementation of `PreloadMediaPeriod.selectTracksForPreloading` when it is trying to retain the previously preloaded streams.
Also the `TrackSelectorResult` parameter is changed to a list of `ExoTrackSelection`. We should compare the selections only rather than considering the `RendererConfiguration` in the `TrackSelectorResult` to decide whether to retain the streams, as for preloading case the renderers haven't consumed the samples yet.
PiperOrigin-RevId: 609126868
This change:
1. Updates the implementation of
`FrameworkMediaDrm.requiresSecureDecoder` to include the
'force allow insecure decoder' workaround logic.
2. Removes the 'force allow insecure decoder' logic from MCR
3. Removes the `requiresSecureDecoder` field from MCR (it can just
be a local derived from `codecDrmSession` now).
PiperOrigin-RevId: 607664186
Also put back a comment in DownloadTracker that is still relevant on API 19.
Also deprecate PlaceholderSurface.newInstanceV17() in favour of just
newInstance()
`Uri.appendQueryParameter` is documented to encode its arguments, so
calling `Uri.encode` beforehand results in double-encoding.
Issue: androidx/media#1075
#minor-release
PiperOrigin-RevId: 604995441
Relax the regex to only check for hyphen which is required by the specification.
Issue: androidx/media#1028
#minor-release
PiperOrigin-RevId: 604719300
These are often the same for image tracks, since we usually drop the
whole image file (both the container and actual encoded image bytes)
into a single sample, but there are cases where we emit a track with
`containerMimeType=image/jpeg` but **no** samples (from
`JpegMotionPhotoExtractor`, to carry some metadata about the image +
video byte offsets).
It's therefore more correct to implement the `supportsFormat` check
based on `sampleMimeType`, so that these 'empty' image tracks are not
considered 'supported' by `ImageRenderer`.
#minor-release
PiperOrigin-RevId: 604672331
This was already done for the TrackGroup ids in <unknown commit>,
but in some scenarios only the Format instances are known and
it's helpful to be able to identify where they came from.
Issue: androidx/media#883
#minor-release
PiperOrigin-RevId: 604644039
Add a test for this consistency in `CompositeSequenceableLoaderTest`,
and also make the
`CompositeSequenceableLoaderTest.FakeSequenceableLoader` implementation
more realistic.
#minor-release
PiperOrigin-RevId: 604604103
Transformer export and ExoPlayer previewing both read inputColorInfo from
registerInputStream now, instead of maintaining a consistent input color
throughout multiple streams in a sequence.
Therefore, we can remove inputColor-related arguments and methods now.
PiperOrigin-RevId: 603423509
If seeking between last image sample and end of the file where the current stream is not final, then EoS sample will not be provided to `ImageRenderer`. ImageRenderer must still produce the last image sample.
PiperOrigin-RevId: 603312090
This happens when using `ExoPlayer.setVideoEffects()`.
This CL also fixes the first frame not rendered problem, originally solved in
7e65cce967, but rolled back in 5056dfaa2b because the solution introduces
the flash that is observed in b/292111083.
Before media3 1.1 release, the output size of `VideoFrameProcessor` is not
reported to the app. This was changed later after introducing
`CompositingVideoSinkProvider`, where the video size after processing **is**
reported to the app. After this CL, the size is again, not reported.
PiperOrigin-RevId: 602345087
The updated algorithm has two main improvements:
- The silence padding is not constant but a ratio of the original
silence (up to a defined max) to more naturally represent the
original gaps.
- The silence is not instantly going to zero, but uses a ramp down
and up for a smooth volume transition and also retains a small
percentage of the original noise for more natural "silences" that
still contain some background noise.
#minor-release
Issue: google/ExoPlayer#7423
PiperOrigin-RevId: 602322442
The return value of onConfigure must not be ignored as it specifies
the output format of the processor, which may be different from the
input format.
#minor-release
PiperOrigin-RevId: 601799440
Many usages are needed to support other deprecations and some
can be replaced by the recommended direct alternative.
Also replace links to deprecated/redirected dev site
PiperOrigin-RevId: 601795998
It's likely that we will merge these back into their `XXXDecoder`
implementations, but this smaller change allows us to avoid including
these public symbols in the upcoming release.
#minor-release
PiperOrigin-RevId: 601432629
This method works by reflectively loading classes from the `lib-effect`
module, in order to avoid a hard dependency on this module for ExoPlayer
users that don't want video effect functionality. This change ensures
that a failure to load the necessary classes fails immediately, instead
of on a later thread inside `MediaCodecVideoRenderer` when the
reflection currently happens.
Also update the javadoc to make the dependency requirement clear.
#minor-release
PiperOrigin-RevId: 601387957
DASH: `DashMediaSource.Factory` would only propagate it to `DashChunkSource.Factory` -> `BundledChunkExtractor.Factory`
SS: `SSMediaSource.Factory` -> `SsChunkSource.Factory`
HLS: `HlsMediaSource.Factory` -> `HlsExtractorFactory`
Remove nullability of SubtitleParser.Factory across the stack
#minor-release
PiperOrigin-RevId: 601250013
The `SubtitleParser.Factory` is no longer @Nullable and the experimenting toggle is used to enable/disable the use of this factory for subtitle parsing during extraction.
The three places that will hold the "truth" for the `SubtitleParser.Factory` are: BundledChunkExtractor.Factory, SsChunkSource.Factory, DefaultHlsExtractorFactory
DASH: `DashMediaSource.Factory` would only propagate it to `DashChunkSource.Factory` -> `BundledChunkExtractor.Factory`
SS: `SSMediaSource.Factory` -> `SsChunkSource.Factory`
HLS: `HlsMediaSource.Factory` -> `HlsExtractorFactory`
#minor-release
PiperOrigin-RevId: 601151615
Those classes only needed to have access to a `SubtitleParser.Factory` to get a potentially updated `Format` for TrackGroups. The `SubtitleParser.Factory` was only used to check the support for the `mimeType` and getting some cue-related behaviour.
This introduced complexity in a way that both Periods and Extractors needed to have the same `SubtitleParser.Factory` in their individual stacks. To ensure that the sample queue would get the same transcoded/original format.
Instead, now we expose `getOutputTextFormat` methods on `ChunkExtractor.Factory`, `SsChunkSource.Factory` and `HlsExtractorFactory`. Those are the dependencies that Hls/Ss/DashMediaPeriod can make use of to delegate the format-updating logic to.
#minor-release
PiperOrigin-RevId: 601130714
These symbols in `lib-effect` are referenced via reflection from
`CompositingVideoSinkProvider` in `lib-exoplayer` in order to avoid
a hard dependency from `lib-exoplayer` to `lib-effect`. Without this
keep rule, the symbols can get renamed by R8 resulting in the
invocations failing.
#minor-release
PiperOrigin-RevId: 601074636
We keep the previous parsing-during-rendering tests, even though they
can be a bit flaky, because this is an important regression test. The
regression risk is lower for this instrumentation test compared to
robolectric tests with different `ShadowLooper` behaviour.
#minor-release
PiperOrigin-RevId: 600781035
We can just continue to assume that we don't know the current device.
This case happens on the latest Robolectric release where this method
call isn't implemented yet. As we not generally assume that the
method can throw, this workaround can be removed once Robolectric
is updated again.
#minor-release
PiperOrigin-RevId: 600426851
From API 23, we may have a preferred device that is most likely used
as the output device.
From API 24, the AudioTrack tells us the actual routed device that is
used for output and we can listen to changes happening mid-playback.
From API 33, we can directly query the default device that will
be used for audio output for the current attributes.
If the routed device is known by any of the methods above, we can add
more targeted checks in methods like isBluetoothConnected to avoid
iterating over all devices that are not relevant.
The knowledge about the routed device will also be useful to check
advanced output capabilities in the future (e.g. for lossless
playback)
PiperOrigin-RevId: 600384923
This allows us to inject a videoFrameProcessorFactory into
MediaCodecVideoRenderer, without issues about creating the
VideoFrameReleaseControl in the MediaCodecVideoRenderer.
Unfortunately, this does result in more complex CVSP state, where
VideoFrameReleaseControl is no longer final, may be null, and may potentially
change. However, this tries to be careful with assertions to guarantee good
state, and is cleaner than modifying the long-standing MediaCodecVideoRenderer
interface.
Tested that this works on the ExoPlayer demo with setVideoEffects applied, and
using a playlist with SDR->HDR and HDR->SDR items.
PiperOrigin-RevId: 599823412
This also fixes issue introduced by frames being released from a prior version of a
GlShaderProgram
Tested by seeking within a playlist with one SDR then one HDR video.
PiperOrigin-RevId: 599475959
When we default to 'parse during extraction', we will flip the default
of this, to ensure that apps know they are using an
incompatible/deprecated flow for subtitle handling.
PiperOrigin-RevId: 599109304
`Surface`s don't expose their size via Java APIs. Recommend apps pass a
`SurfaceView` (which is preferable to `TextureView` as it's more efficient)
or `SurfaceHolder` so they benefit from the player automatically passing the
size down to the video renderer via `MSG_SET_VIDEO_OUTPUT_RESOLUTION`.
PiperOrigin-RevId: 598804258
MatroskaExtractor will no longer be wrapped in SubtitleTranscodingExtractor, but instead use SubtitleTranscodingExtractorOutput under the hood.
FLAG_EMIT_RAW_SUBTITLE_DATA flag will be used to toggle between subtitle parsing during extraction (before the sample queue) or during decoding (after the sample queue).
The new extractor dump files generated by `MatroskaExtractorTest` now follow the new parsing logic and hence have mimeType as `x-media3-cues`.
PiperOrigin-RevId: 598616231
Subclasses of this component can customize it by wrapping with the
decorator pattern, and a custom `CompositeSequencableLoaderFactory`
allows access to the list of delegate `SequenceableLoader` instances.
The `final` keyword was removed as part of <unknown commit> but this
component never ended up being subclassed within the library.
Making this class `final` makes upcoming changes easier to reason
about.
PiperOrigin-RevId: 597853429
7e65cce967 introduced a regression on ExoPlayer.setVideoEffects()
where there is flash on the screen after the first few frames are shown.
Before 7e65cce967, the first frames of the content are missed, until
MediaCodecVideoRenderer sends the onVideoSizeChanged() callback. The
first frame is processed but not shown, the onVideoSizeChanged() is
triggered and the renderer receives a video output resolution message as
a response from the UI.
7e65cce967 fixed the missed first frames by setting a surface on the
CompositingVideoSinkProvider before the provider is initialized, and as
as result:
- the first frames are rendered
- the MediaCodecVideoRenderer sends the the onVideoSizeChanged() after
frames are shown
- the UI sends a video output resolution change
- the MediaCodecVideoRenderer updates the CompositingVideoSinkProvider
which causes the flash.
The underlying problem is with onVideoSizeChanged() not being
triggered early enough, before the first frame is shown.
Because the flashing is a regression, this commit is reverting
7e65cce967 but keeps the test that was added as ignored, so that the
test is re-enabled as soon as we address the underlying issue.
PiperOrigin-RevId: 597814013
Originally a `PlayerId` has to be passed and it should be the same id of the player who is going to play the sources, but it turns out to be unnecessary.
When preloading, we can set a `PlayerId.UNSET` inside of the `PreloadMediaSource`, as there is no ongoing playback. And when the source is handed over to player, player will set the player's `PlayerId`.
PiperOrigin-RevId: 597475119
This involves promoting `setTextTranscodingEnabled` to
`ExtractorsFactory`, and also making it experimental, to indicate it's a
short-lived method.
PiperOrigin-RevId: 597235252
The capabilities change depending on the attributes, so we should
pass down the actual attributes used during playback and update
the capabilities whenever these change.
PiperOrigin-RevId: 597197507
We introduce SubtitleParser.Factory that supports no formats to be used FragmentedMp4Extractors that will not do any subtitle parsing on the extraction side. We also slowly move away from using SubtitleTranscodingExtractor to SubtitleTranscodingExtractorOutput (hence making it public).
This is required by individual Extractor impls so that they can start using SubtitleTranscodingExtractorOutput rather than be wrapped by SubtitleTranscodingExtractor. The latter is to be deprecated after all the subtitle related Extractors have achieved this migration.
PiperOrigin-RevId: 596942147
This behavior was previously available as opt-in via
`MediaItem.DrmConfiguration.Builder.setPlayClearContentWithoutKey` and
`DefaultDrmSessionManager.Builder.setPlayClearSamplesWithoutKeys`. This
change flips the default of both these properties to true.
This should speed up the time for playback to start when playing DRM
content with a 'clear lead' of unencrypted samples at the start.
Previously playback would wait until the keys for the later encrypted
samples were ready. The new behaviour could result in mid-playback
stalls/rebuffers if the keys are not ready yet by the transition from
clear to encrypted samples, but this is not really a regression since
previously playback wouldn't have started at all at this point.
PiperOrigin-RevId: 595992727
This file is CBR encoded with LAME, so it has an `Info` header (the CBR
equivalent to `Xing`).
A follow-up change will use this file in `Mp3ExtractorTest`.
Issue: androidx/media#878
PiperOrigin-RevId: 595938327
These were missing in 5211ff0dc1
Without these, the `": null"` is still logged (but the `"Unexpected
IllegalStateException"` bit is not), because the **whole** string
concatenation is compared to null as the boolean condition of the
ternary, and this condition is always false (the result of a string
concatentation is never `null`) i.e. (with excess parentheses for
clarity):
```
(("Unexpected " + cause.getClass().getSimpleName() + cause.getMessage()) != null)
? ": " + cause.getMessage()
: ""
```
Also add a test because obviously this isn't as simple as I'd thought.
PiperOrigin-RevId: 593079975
The previous code led me to misread this stack trace as a null pointer
exception, but it's really an index out of bounds exception:
```
Caused by: androidx.media3.exoplayer.upstream.Loader$UnexpectedLoaderException: Unexpected IllegalArgumentException: null
at androidx.media3.exoplayer.upstream.Loader$LoadTask.run(Loader.java:435)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:644)
at java.lang.Thread.run(Thread.java:1012)
Caused by: java.lang.IllegalArgumentException
at androidx.media3.common.util.Assertions.checkArgument(Assertions.java:40)
at androidx.media3.common.util.ParsableByteArray.setPosition(ParsableByteArray.java:164)
at androidx.media3.extractor.text.cea.Cea608Parser.parse(Cea608Parser.java:440)
```
PiperOrigin-RevId: 592876546
getAudioCapabilities currently creates the receiver and returns
the current capabilities. This is error-prone because the
capabilities are also available as a class field.
This can made cleaner by letting the method just create the receiver
and all access to the capabilities can be made via class field.
PiperOrigin-RevId: 592591590
This is a refactoring that allows the `MediaPeriodQueue` to
create media period holders without all the collaborators
being passes in to `enqueueNextMediaPeriodHolder(...)`.
The factory is presumably also helpful in unit test to
know whether and when exactly a holder is created in
the preloading process.
PiperOrigin-RevId: 592301400
We deliberately left `Cea608Decoder` and `Cea708Decoder` intact when
creating the respective `SubtitleParser` implementations (in
27caeb8038
and
94e45eb4ad
respectively).
However we didn't correctly change the behaviour of
`SubtitleDecoderFactory.DEFAULT` in order to use these 'legacy'
implementations. We firstly left the `Cea608Decoder` instantiation
**after** the `DefaultSubtitleParserFactory.supportsFormat()` check,
meaning that code would never be reached (since `supportsFormat` now
returns true for CEA-608). Then in the second change (which was supposed
to only be about CEA-708) we removed all instantiations of **both**
`Cea608Decoder` and `Cea708Decoder`. This change puts the decoder
instantiations back and moves them above the
`DefaultSubtitleParserFactory.supportsFormat` check, meaning they will
be used in preference to `DelegatingSubtitleDecoder`.
This resolves the immediately-disappearing subtitles tracked by
Issue: androidx/media#904.
PiperOrigin-RevId: 592217937
This allows us to remove the additional thread we create
for asynchronous buffer queuing and use a synchronized
queueing approach again.
This API looks strictly beneficial on all tested devices,
but since this code path in MediaCodec has not been used
widely, we leave an opt-out flag for now.
PiperOrigin-RevId: 591867472
The parameters may change the decoding behavior of the
following samples and it's suprising/wrong to apply them
from another thread where we can't guarantee from which
sample they apply.
PiperOrigin-RevId: 591808760
Move `inputColorInfo` from `VideoFrameProcessor`'s `Factory.create` to `FrameInfo`,
input via `registerInputStream`.
Also, manually tested on exoplayer demo that setVideoEffects still works.
PiperOrigin-RevId: 591273545
When an adaptive source has been preloaded, the track selection
of the player should possibly not override the preloaded selection
to avoid discarding preloaded data.
PiperOrigin-RevId: 591256334
The number of empty image byte arrays written is one less than the
total number of tiles in the image. The empty byte arrays act as
placeholders for individual tiles inside ImageRenderer.
PiperOrigin-RevId: 591231432
This is because currently
1. Player sets a surfaceView to render to
2. Player intializes the renderer
3. MCVR initializes the VideoSinkProvider, by extension VideoGraph
But when 1 happens, MCVR doesn't set the surfaceView on the VideoGraph because
it's not initialized. Consequently after VideoGraph is initialized, it doesn't
have a surface to render to, and thus dropping the first a few frames.
Also adds a test for first frame to verify the correct first frame is rendered.
PiperOrigin-RevId: 591228174
When track is changed during playback, `playbackPositionUs` may be in middle of a chunk and `loadPositionUs` should be the start of that chunk. In this situation `loadPositionUs` can be less than the current `playbackPositionUs`, resulting into negative `bufferedDurationUs`. It translates to having no buffer and hence we should send `0` for `bufferedDurationUs` when creating new instances of `CmcdData.Factory`.
Issue: androidx/media#888
#minor-release
PiperOrigin-RevId: 591099785
Vertical and horizontal tile counts inside Format used by ImageRenderer
used to be required to equal 1 and were set to 1 regardless of what
the manifest said.
This change removes the above requirement and sets the tile counts to
the values from the manifest.
PiperOrigin-RevId: 590608353
Using 0 as the unset prepare position is the root cause of a number of issues,
as outliine in the ExoPlayer issue https://github.com/google/ExoPlayer/issues/7975
The premise of this fix is that once the prepare override is used (the initial call
to `selectTracks()`) it is never needed again, so simply invalidate it after use.
There is a bug in CompositionVideoSinkProvider where the rotation
effect is applied only on the first video and the same effect
is applied on subsequent videos without checking if the next
items in the playlist must be rotated.
The bug applies only on devices with API < 21.
PiperOrigin-RevId: 589823797
We currently pause playback to prevent further progress while the
app thread runs assertion and triggers additional actions. This is
not ideal because we do not actually want to pause playback in
almost all cases where this method used.
This can be improved by keeping the playback thread blocked and only
unblock it the next time the app thread waits for the player (either
via RobolectricUtil methods or by blocking the thread itself). To
add this automatic handling, this change introduces a new util class
for the tests that can keep the list of waiting threads statically
(because the access to this logic is spread across multiple independent
classes).
PiperOrigin-RevId: 589784204
Fix a bug where CompositingVideoSinkProvider.isInitialized() returns
true even after releasing the CompositingVideoSinkProvider.
PiperOrigin-RevId: 588481744
This change moves the instantiation of the CompositingVideoSinkProvider
out of MediaCodecVideoRenderer so that the composition preview player can
re-use the CompositingVideoSinkProvider instance for driving the rendering of
images.
The main point of the change is the ownership of the
VideoFrameReleaseControl, which decides when a frame should be rendered
and so far was owned by the MediaCodecVideoRenderer. With this change,
in the context of composition preview, the VideoFrameReleaseControl
is no longer owned by MediaCodecVideoRenderer, but provided to it.
This way, the CompositingVideoSinkProvider instance, hence the
VideoFrameReleaseControl can be re-used to funnel images into the
video pipeline and render the pipeline from elsewhere (and not
MediaCodecVideoRenderer).
PiperOrigin-RevId: 588459007
This image has two video tracks in the MP4 data, one is a 'real' video
which we want to play by default, and the other is a low-fps video track
which isn't intended to be directly played, it's encoded in HEVC for
compression and decoding efficiency.
This test demonstrates ExoPlayer's current behaviour default extraction
and playback, which results in selecting the high-res, low-fps track
(actually single sample in this example), instead of playing the actual
video.
PiperOrigin-RevId: 588068908
As Opus decoders skip some bytes prior to playback during a seek, the renderer for bypass playback should send samples to the decoder even if they would be decode-only. However, the renderer should not send samples with time preceding that range. This change adds that constraint.
#minor-release
PiperOrigin-RevId: 588014983
Both the extension OPUS decoder and the OMX/C2 MediaCodec
implementations for OPUS and VORBIS decode into the channel
layout defined by VORBIS. See
https://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-140001.2.3
While this is technically correct for a stand-alone OPUS or VORBIS
decoder, it doesn't match the channel layout expected by Android.
See https://developer.android.com/reference/android/media/AudioFormat#channelMask
The fix is to apply the channel mapping after decoding if needed.
Also add e2e tests with audio dumps for the extension renderer,
including a new 5.1 channel test file.
Issue: google/ExoPlayer#8396
#minor-release
PiperOrigin-RevId: 588004832
`mediaCrypto` is initialized before `codec` in
`maybeInitCodecOrBypass`. Before this change, it was possible for
`maybeInitCodecOrBypass` to complete with `mediaCrypto != null` and
`codec == null`, in particular if it was run as part of clearing the
player surface (since in that case, no video codec is initialized).
This inconsistent state then causes issues during a later invocation of
`maybeInitCodecOrBypass`, when `mediaCrypto` is still non-null, and
`mediaCryptoRequiresSecureDecoder = true`, but the
content has been changed to unencrypted with no associated DRM session.
This results in a playback error, because a secure decoder is
initialized but there's no DRM session available to work with it.
This change ensures that when `maybeInitCodecOrBypass` completes,
either both `mediaCrypto != null` and `codec != null` (i.e. codec
initialization was completed) or `mediaCrypto == null` and
`codec == null` (i.e. codec initialization was not completed). We also
ensure that when nulling out `mediaCrypto` we also set
`maybeInitCodecOrBypass = false`. A later change should be able to
demote `maybeInitCodecOrBypass` from a field to a local in order to
remove any risk of that part of state becoming out of sync. This
resolves the issue, because during the second invocation of
`maybeInitCodecOrBypass` an insecure decoder is now (correctly)
initialized and the unencrypted content is successfully played.
#minor-release
PiperOrigin-RevId: 587713911
We set the `playerId` only in the constructor currently. But in the case of this source doesn't have `preload` called, the player's release of this source will set the playerId to `null`, which makes this source un-reusable with a null `playerId`.
PiperOrigin-RevId: 587698214
Add an check when loading progressive media in case the load
is canceled. If the player is released very early, the progressive
media period may carry on with the initial loading unnecessarily.
PiperOrigin-RevId: 586288385
At least some Android 14 devices still have the same invalid URL issue
when using ClearKey DRM, so might as well apply the check to newer
versions of Android as well.
We don't get the buffer back after decoding to make a proper decision
about whether dropping the buffer is needed, so we do the next best
thing and tell the codec to drop the buffer if its input timestamp is
less than the intended start time.
PiperOrigin-RevId: 584863144
Move the reflective loading of VideoFrameProcessor from
MediaCodecVideoRenderer to the CompositingVideoSinkProvider. This is so
that all reflective code lives in one place. The
CompositingVideoSinkProvider already has reflective code to load the
default PreviewingVideoGraph.Factory.
PiperOrigin-RevId: 584852547
Split CompositingVideoSinkProvider.VideoSinkImpl in two classes:
- VideoSinkImpl now only receives input from MediaCodecVideoRenderer and
forwards frames to its connected VideoFrameProcessor
- VideoFrameRenderControl takes composited frames out of the VideoGraph
and schedules the rendering of those.
- CompositingVideoSinkProvider connects VideoSinkImpl with
VideoFramesRenderer.
PiperOrigin-RevId: 584605078
The live offset override is used to replace the media-defined
live offset after user seeks to ensure the live adjustment adjusts
to the new user-provided live offset and doesn't go back to the
original one.
However, the code currently clips the override to the min/max
live offsets defined in LiveConfiguration. This is useful to
clip the default value (in case of inconsistent values in the media),
but the clipping shouldn't be applied to user overrides as
the player will then adjust the position back to the min/max
and doesn't stay at the desired user position.
See 2416d99857 (r132871601)
#minor-release
PiperOrigin-RevId: 584311004
Currently, ads are only defined by a single URL, which makes it
impossible to define additional fields needed to play ads correctly.
This can be fixed by using a full MediaItem in AdPlaybackState,
replacing the previous Uri field.
PiperOrigin-RevId: 582331588
MIME types are case-insensitive, but none of the many existing
comparisons across our code base take this into account. The
code can be made more robust by normalizing all MIME types at the
moment they are first set into a class/builder and adding toLowerCase
as part of the normalization.
Most concretely, this fixes an issue with playing HLS streams via
the IMA SDK where the stream MIME type is indicated with all lower
case "application/x-mpegurl", which failed the MIME type comparison
in DefaultMediaSourceFactory.
PiperOrigin-RevId: 582317261
BundledChunkExtractor creates a JpegExtractor or a PngExtractor if the
chunk's MIME type is JPEG or PNG respectively.
The JpegExtractor is instantiated to load an image track.
PiperOrigin-RevId: 582005828
Instantiate an ImageRenderer and add it to the list returned by
DefaultRenderersFactory.buildRenderers.
Add Renderer.MessageType.MSG_SET_IMAGE_OUTPUT and
ExoPlayer.setImageOutput to enable setting a custom
ImageRenderer.imageOutput.
Add ImageRenderer.handleMessage to process messages sent to the
renderer.
PiperOrigin-RevId: 581962451
Both interfaces are not really needed as the methods can be called
on the respective classes directly. This also avoid error-prone
situations where classes define to/fromBundle methods with parameters
that should be used instead of the parameter-less version.
Also deprecate all existing CREATOR static instances and make the
corresponding fromBundle method public where needed.
PiperOrigin-RevId: 579189766
DefaultTrackSelector now has all logic necessary for selecting an
image track.
If isPrioritizeImageOverVideoEnabled is set to true, image track will
try to be selected first and a video track will only be selected if no
image track is available. If isPrioritizeImageOverVideoEnabled is set
to false, image track will be selected only if video track wasn't
selected.
PiperOrigin-RevId: 578806006
BundleableUtil is the only class that really depends on the type
inheritence from Bundleable. However, it only needs this as a way
to define Function<T, Bundle> and Function<Bundle, T>, which could
just be passed in as parameters as it's already done for some of
these methods.
Also rename the class to BundleCollectionUtil accordingly.
PiperOrigin-RevId: 578791946
This change applies to standalone TTML files linked directly from the manifest.
As a result, we no longer have the flakiness in the DashPlaybackTest which uses sidecar-loaded (standalone file) TTML subtitles. We experimentally opt into parsing subtitles during extraction and use SubtitleExtractor in hybrid mode.
PiperOrigin-RevId: 577457256
This CL adds an isPrioritizeImageOverVideoEnabled flag to
TrackSelectionParameters and an API to set the flag value.
The flag will be used by DefaultTrackSelector to determine whether
to select an image track if both an image track and a video track are available.
PiperOrigin-RevId: 576175162
The events happens in the following order, assuming two media items:
1. First media item is fully decoded, record the last frame's pts
- Note frame processing is still ongoing for this media item
2. Renderer sends `onOutputFormatChanged()` to signal the second media item
3. **Block sending the frames of the second media item to the `VideoSink`**
4. Frame processing finishes on the first media item
5. The last frame of the first media item is released
6. **Reconfigure the `VideoSink` to apply new effects**
7. **Start sending the frames of the second media item to the `VideoSink`**
This CL implements the **events in bold**
PiperOrigin-RevId: 576098798
Some devices supporting Performance Points for decoder coverage are missing coverage over the CDD requirements for H264. For these cases ExoPlayer should fall back to legacy resolution and frame rate support checks. If there is an H264 stream evaluated as a `PerformancePointCoverageResult` of `COVERAGE_RESULT_NO`, then ExoPlayer checks for coverage of the [720p CDD requirement](https://source.android.com/docs/compatibility/10/android-10-cdd#5_3_4_h_264).
Issue: google/ExoPlayer#10898
Issue: androidx/media#693
PiperOrigin-RevId: 575768836
Since DEFAULT_MAX_CHANNEL_COUNT was increased from 8 to 10,
getMaxSupportedChannelCountForPassthrough always throws if its loop
enters its second iteration (channelCount of 9). This is due to
Util.getAudioTrackChannelConfig returning CHANNEL_INVALID when passed a
channelCount of 9, and setting CHANNEL_INVALID as the AudioFormat's
channel mask throws an exception.
This change skips each iteration where CHANNEL_INVALID is returned.
As Opus decoders skip some bytes prior to playback during a seek, the renderer for bypass playback should send samples to the decoder even if they would be decode-only.
#minor-release
PiperOrigin-RevId: 574494666
The `PreloadMediaSource` has below two new public methods that suppose to be called by the app:
* `preload(long)` allows the apps to preload the source at the passed start position before playback. The preload efforts include preparing the source for a `Timeline`, creating and caching a `MediaPeriod`, preparing the period, selecting tracks on the period and continuing loading the data on the period.
* `releasePreloadMediaSource()` allows the apps to release the preloaded progress.
The `PreloadMediaPeriod` is designed to facilitate the `PreloadMediaSource` for the preloading work. It has a new package-private method `selectTracksForPreloading` which will cache the `SampleStream` that corresponds to the track selection made during the preloading, and when the `PreloadMediaPeriod.selectTracks` is called for playback, it will uses the preloaded streams if the new selection is equal to the selection made during the preloading.
Also add a shortform demo module to demo the usage of `PreloadMediaSource` with the short-form content use case.
PiperOrigin-RevId: 574439529
This change moves the release timestamp adjustment logic out of
MediaCodecVideoRenderer and into a standalone component, the
VideoFrameReleaseControl. The plan for VideoFrameReleaseControl is to use
it:
- from MediaCodecVideoRenderer, when ExoPlayer plays video in standalone
mode.
- from the CompositionPlayer's DefaultVideoSink, when CompositionPlayer
supports multiple sequences.
- (temporarily) from the CompositionPlayer's custom ImageRenderer while
we are implementing single-sequence preview, which is an intermediate
milestone for composition preview.
PiperOrigin-RevId: 574420427
This currently only applies to subtitles muxed into mp4 segments, and
not standalone text files linked directly from the manifest.
Issue: androidx/media#288
#minor-release
PiperOrigin-RevId: 572263764
In particular:
- Add allowAudioNonSeamlessAdaptiveness parameter (default true, same
as video and as already implemented by default)
- Forward mixedMimeTypeAdaptation support to AudioTrackScore
(as for VideoTrackScore) and adapt mixed MIME type adaptive
support accordingly
- Check adaptive support when deciding whether a track is allowed for
adaptation (also same check as for video). This takes the new
parameter into account.
#minor-release
PiperOrigin-RevId: 572191308
After 4fad529433, MediaCodecVideoRenderer does not report if frames
are dropped from the VideoSink. This commit fixes this.
#minor-release
PiperOrigin-RevId: 571905721
In some streaming scenarios, like offload, the sink may finish writing buffers a bit before playback reaches the end of the track. In this case a player may pause while in this 'stopping' state.
The AudioTrackPositionTracker needs to update the cached values it uses to calculate position in the `PLAYSTATE_STOPPED`/`PLAYSTATE_STOPPING` states if pause/play are called during this period.
PiperOrigin-RevId: 571345914
In offload mode, `AudioTrack#stop()` will put the track in `PLAYSTATE_STOPPING` rather than `PLAYSTATE_STOPPED`. The difference in state means that `AudioTrack` can be paused and played during this 'stopping' period.
Currently, if `AudioTrackPositionTracker#handleEndOfStream()` has been called then `DefaultAudioSink` in `pause()` won't call `AudioTrack#pause()`. `AudioTrack#pause()` should be called in this case if in offload mode.
#minor-release
PiperOrigin-RevId: 571335108
This belongs in the resolver, because it depends on the resolution
algorithm (and therefore the logic can't live in `TextRenderer`).
This also fixes a bug in `TextRenderer` where we were doing arithmetic
with `cues.durationUs` without checking if it was `TIME_UNSET` first.
#minor-release
PiperOrigin-RevId: 571332750
The flag is no longer used by our components and only set and checked
in a few places to guarantee compatiblity with existing renderers and
decoders that still use it.
The flag will be removed in the future due to its design limitations.
#minor-release
PiperOrigin-RevId: 571291168
The existing `Subtitle` handling code is left intact to support the
legacy post-`SampleQueue` decoding path for now.
This also includes full support for merging overlapping `CuesWithTiming`
instances, which explains the test dump file changes, and which should
resolve the following issues (if used with the
decoder-before-`SampleQueue` subtitle logic added in
5d453fcf37):
* Issue: google/ExoPlayer#10295
* Issue: google/ExoPlayer#4794
It should also help resolve Issue: androidx/media#288, but that will also require
some changes in the DASH module to enable pre-`SampleQueue` subtitle
parsing (which should happen soon).
#minor-release
PiperOrigin-RevId: 571021417
The aim of this test is to make sure the image is onscreen for the right amount of time, so to drive down flakes from the decoder taking too long, change this to an atLeast check
#minor-release
PiperOrigin-RevId: 570988044
This gets rid of the reliance on the decode only flag that is still
set on input buffers to the decoder if they are less than the start
time.
We still need to set and check the decode-only flag in SimpleDecoder
to ensure compatbility with custom decoders that use the flag while
it's not fully removed.
PiperOrigin-RevId: 570736692
The interface requires the implementation to return null if the
decode-only flag is set. So instead of setting the flag and returning
null, we can simply not call the method and assume it's null.
The only reason where this wouldn't work is if the metadata format
has keyframe-like logic and requires previous metadata to decode
the next one. This is not something we came across before and it seems
ignorable. If that feature is needed in the future, we should instead
add a method to MetadataDecoder to set the first output timestamp.
#minor-release
PiperOrigin-RevId: 570399838
While sleeping for offload, position is estimated based on time playing. If asleep and AudioTrack is reused, then the position will keep incrementing as the subsequent item plays. That is until wakeup when playing position is updated to the timestamp of the second item. Offload scheduling should be disabled until track transitions fully.
PiperOrigin-RevId: 570397140
When seeking, we must first flush the video sink so it stops
using any SurfaceTextures before flushing MediaCodec.
#minor-release
PiperOrigin-RevId: 570015998
It seems likely we will define a new "image decoder" interface that
returns `ListenableFuture<Bitmap>`, and naming that will be
hard/annoying if we need to keep this interface working too.
It's also not really clear what a non-test implementation of this
interface would be expected to do, since `DefaultImageDecoder` is
documented to always decode using `BitmapFactory`.
#minor-release
PiperOrigin-RevId: 569206325
Changes
---
- Added `removeOfflineLicense(byte[])` and `getOfflineLicenseKeySetIds` and consumed them in their implementations
Background
---
- These APIs will help in addressing an increasing amount of `java.lang.IllegalArgumentException: Failed to restore keys: BAD_VALUE` which is our top playback error in our app
- Based on our discussion with Widevine team and [this exoplayer issue](https://github.com/google/ExoPlayer/issues/11202#issuecomment-1708792594)
- TL;DR: The failure occurs on startup if the user has 200+ offline licenses, we would like to add the functionality to remove offline licenses
**Note: Why we want these APIs in ExoMediaDrm and not in OfflineLicenseHelper**
- As per the issue above, we would like to access these 2 public APIs in MediaDrm that don’t exist in `OfflineLicenseHelper` or `ExoMediaDrm`
- APIs interested in:
- [MediaDrm#removeOfflineLicense()](https://developer.android.com/reference/android/media/MediaDrm#removeOfflineLicense(byte%5B%5D)): To remove offline license
- [MediaDrm#getOfflineLicenseKeySetIds()](https://developer.android.com/reference/android/media/MediaDrm#getOfflineLicenseKeySetIds()): To see number of offline licenses on startup
- We use `OfflineLicenseHelper` to download license for L1 and we don't interact with `ExoMediaDrm` directly. But for the alternate Widevine integration, we directly depend on `ExoMediaDrm` APIs to override and call CDM Native APIs.
- We would like to have the functionality of removing offline licenses for both integration which would need access to above APIs in `ExoMediaDrm`.
Links
---
- https://github.com/androidx/media/issues/659
MediaCodecRenderer has already been updated to not rely on the
input stream to mark its samples as decode-only and instead use
a simple time-based comparison to achieve the same effect.
This change makes the same update for all other renderers that
either use the flag directly or forward to a "decoder" instance.
PiperOrigin-RevId: 568232212
The current code only set the videoEffects when CVSP is initialized, which
happens after `player.prepare()`. But it's valid that videoEffects are set
before calling `prepare()`.
PiperOrigin-RevId: 566941216
Move audio offload mode related interfaces and definitions from `TrackSelectionParameters` to a new `AudioOffloadModePreferences` class.
PiperOrigin-RevId: 566905017
This change uses the new incremental overloads of `SubtitleParser` to
avoid introducing the performance regression caused by the original
change requiring all cues to be fully parsed before the first could be
shown on screen.
`TtmlDecoder` which used to be `SimpleSubtitleDecoder` will now be
called `TtmlParser` and implement `SubtitleParser` interface. For
backwards compatibility, we will have the same functionality provided
by `DelegatingSubtitleDecoder` backed-up by a new `TtmlParser`
instance.
PiperOrigin-RevId: 566671398
This change introduces two new types of method to `SubtitleParser`:
1. `parse()` methods that take a `Consumer<CuesWithTiming>` and return `void`
2. `parseToLegacySubtitle` method that returns `Subtitle`
(1) ensures that in the new 'parse before SampleQueue' world we can
write cues to the `SampleQueue` as soon as they're ready - this is
especially important when parsing monolithic text files, e.g. for a
whole movie.
(2) ensures that during the transition, the legacy 'parse after
SampleQueue' behaviour doesn't see any regressions in 'time to first
cue being shown'. Previously we had a single implementation to convert
from `List<CuesWithTiming>` to `Subtitle`, but this relies on the
complete list of cues being available, which can take a long time for
large files in some formats (with ExoPlayer's current parsing logic).
By allowing implementations to customise the way they create a
`Subtitle`, we can directly re-use the existing logic, so that the
'time to first cue being shown' should stay the same.
This change migrates all **usages** to the new methods, but doesn't
migrate any **implementations**. I will migrate the implementations in
follow-up CLs before deleting the old list-returning `parse()` methods.
PiperOrigin-RevId: 565057945
Removed `ExoPlayer.experimentalSetOffloadSchedulingEnabled` as scheduling will be enabled by default when offload is enabled for audio-only playback.
In addition, the `experimental` key word was taken out of the following
method signatures:
* `ExoPlayer.experimentalIsSleepingForOffload`
* `AudioOffloadListener.onExperimentalSleepingForOffloadChanged`
* `AudioOffloadListener.onExperimentalOffloadedPlayback`
PiperOrigin-RevId: 565035289
There is no known use case left that needs to be solved by this
class. All other use cases are better solved by alternative
approaches (Player playlist methods or ConcatenatingMediaSource2).
PiperOrigin-RevId: 563713384
The class currently disallows offsets of periods in their windows
except for the very first window. This is not necessary because
we can use TimeOffsetMediaPeriod to eliminate the offset if needed.
This makes the class more useful for many use cases, in particular
for using it with ClippingMediaSource.
Issue: google/ExoPlayer#11226
PiperOrigin-RevId: 563702120
This callback asks to correct the media time in a composition, but doesn't
provide the MediaPeriodId as the necessary context to do this.
PiperOrigin-RevId: 563699344
We have an existing logic for recovering the `AudioTrack` from the [ERROR_DEAD_OBJECT](https://developer.android.com/reference/android/media/AudioTrack#ERROR_DEAD_OBJECT)
throws. However, this only applies to the situation when the
`writtenEncodedFrames > 0`.
However, we have a case when `ERROR_DEAD_OBJECT` throws while writing
PCM data. When the `tunneling` is turned on, the `AudioTrack` is set the
flag `AUDIO_OUTPUT_FLAG_HW_AV_SYNC`, and further because of this flag,
it is forced the flag `AUDIO_OUTPUT_FLAG_DIRECT`. When the platform
invalidates the `AudioTrack`, the `AudioTrack` won't be restored by the
platform due to `AUDIO_OUTPUT_FLAG_DIRECT` flag, and `ERROR_DEAD_OBJECT`
is thrown.
Issue: androidx/media#431
PiperOrigin-RevId: 562582451
This is so CompositionPlayer can customize the Renderers and figure
out when a transition to a MediaItem is made hence it can configure the
effects pipeline with the effects that are specific to the MediaItem.
PiperOrigin-RevId: 562530774
Created new method `AudioSink#setOffloadDelayPadding` that will set delay and padding data onto an `AudioTrack` using `AudioTrack#setOffloadDelayPadding`. This feature adds support for offloaded, gapless Opus playback as the content requires the capability to set padding data post-`AudioSink#configure`.
PiperOrigin-RevId: 562518193
also improves tests to hash bitmap now that we know the bitmaps are decoded in roboelectic tests,
as well as some minor fixes in the DefaultImageDecoder
PiperOrigin-RevId: 561946073
This wasn't properly documented yet (unlike the longer setMediaItems
method that already includes this documentation).
Issue: androidx/media#607
PiperOrigin-RevId: 561910073
Currently, we only support sending Common Media Client Data (CMCD) data through custom HTTP request headers, added capability to configure and transmit it as HTTP query parameters.
PiperOrigin-RevId: 561591246
At this stage, the image renderer is designed to be able to render one input stream containing one sample. The renderer supports seeking inside that stream, which is a no-op because the stream comprises for exactly one sample.
PiperOrigin-RevId: 561307859
If the loading thread of the second media item progresses slow, then when the playback of the first item approaches to the end before the second item is prepared, the stream will be set "final" and the renderers are disabled during such transition. In this case, `AudioSink.handleDiscontinuity` never gets called, which introduces the flakiness into the `test_bypassOffThenOn`.
Similarly for `test_subtitle`, if the loading thread of extracting the subtitle is slow, then when the renderer attempts to queue the input buffer, the data hasn't been available in the stream. And when the extracting finished, the renderer already advanced to the time after the subtitle end time.
To de-flake the tests, we have to make sure that the period of the second item has fully loaded before starting the playback.
PiperOrigin-RevId: 560080119
The first call to method `registerInputStream` doesn't block.
Later successive calls to the method blocks until the previous register call
finishes.
PiperOrigin-RevId: 559694490
Replace Queue<Long> with LongArrayQueue which provides queue semantics
for long primitives. LongArrayQueue is forked from IntArrayQueue which
in turn was forked from Androidx CircularIntArray.
IntArrayQueue is deleted and we now use CircularIntArray directly from
Androidx Collection.
PiperOrigin-RevId: 559129744
Based on the Common Media Client Data (CMCD) specification key-value pairs should be sequenced in alphabetical order of the key name in order to reduce the fingerprinting surface exposed by the player.
PiperOrigin-RevId: 558296264
Added more comprehensive Javadoc around setting custom data and verification on key format.
Changed adding custom data as a `String` to `List<String>`. This would enable us to sort all keys to reduce the fingerprinting surface.
PiperOrigin-RevId: 558291240
It was moved there temporarily to support another temporary move
of ExoPlaybackException. See <unknown commit>.
Since then, ExoPlaybackException has been moved back to ExoPlayer
and we can do the same with MediaPeriodId, which only makes sense
in the context of the ExoPlayer module.
PiperOrigin-RevId: 557159381
This aligns the bypass code with the MediaCodec logic to not
use buffer.isDecodeOnly and instead rely on a timestamp
comparison with the last reset time. Overall, the change should
not introduce a functional difference and is covered by existing
end-to-end tests with bypass playbacks.
Splitting bypass batch buffers by decoder-only flag is
technically wrong for formats where not every buffer is a
keyframe, and this change also leaves a TODO to explain this
known shortcoming.
PiperOrigin-RevId: 556800038
The method was recently introduced and only searched for matching
samples up to index==length. However, SampleQueue uses a circular
array to index its data and the search should continue until
relativeStartIndex+length, while also handling the overflow
in the circular array.
The tests for seeking didn't cover these overflow cases yet
because they always started writing data in an empty SampleQueue.
This can be fixed by prewarming the queue with placeholder data
to force using the overflow logic in the various seek methods.
PiperOrigin-RevId: 555963011
Use `C.RATE_UNSET_INT` instead of introducing `Long.MIN_VALUE` as the default value for rate parameters. Modifies default behaviour of the `TrackSelection.getLatestBitrateEstimate()` method and the `measuredThroughputInKbps` parameter in `CmcdHeadersFactory.CmcdRequest`.
PiperOrigin-RevId: 555939420
Optimises the construction of Common Media Client Data (CMCD) headers by eliminating the use of trailing commas. Instead of appending values directly to a StringBuilder, it now adds values to a list and joins them using a comma separator. While the primary focus is on enhancing readability and robustness, it's worth noting that in certain instances, removing usages of `formatInvariant`, there are also performance improvements.
PiperOrigin-RevId: 555911498