mirror of
https://github.com/androidx/media.git
synced 2025-04-29 22:36:54 +08:00
Compare commits
5 Commits
b046d590bc
...
f261fe187a
Author | SHA1 | Date | |
---|---|---|---|
![]() |
f261fe187a | ||
![]() |
a2265f1dae | ||
![]() |
07be60ed93 | ||
![]() |
ed56ed22fb | ||
![]() |
3205811f23 |
120
RELEASENOTES.md
120
RELEASENOTES.md
@ -3,28 +3,12 @@
|
|||||||
### Unreleased changes
|
### Unreleased changes
|
||||||
|
|
||||||
* Common Library:
|
* Common Library:
|
||||||
* Add `PlaybackParameters.withPitch(float)` method for easily copying a
|
|
||||||
`PlaybackParameters` with a new `pitch` value
|
|
||||||
([#2257](https://github.com/androidx/media/issues/2257)).
|
|
||||||
* ExoPlayer:
|
* ExoPlayer:
|
||||||
* Fix sending `CmcdData` in manifest requests for DASH, HLS, and
|
|
||||||
SmoothStreaming ([#2253](https://github.com/androidx/media/pull/2253)).
|
|
||||||
* Fix issue where media item transition fails due to recoverable renderer
|
|
||||||
error during initialization of the next media item
|
|
||||||
([#2229](https://github.com/androidx/media/issues/2229)).
|
|
||||||
* Add `ExoPlayer.setScrubbingModeEnabled(boolean)` method. This optimizes
|
* Add `ExoPlayer.setScrubbingModeEnabled(boolean)` method. This optimizes
|
||||||
the player for many frequent seeks (for example, from a user dragging a
|
the player for many frequent seeks (for example, from a user dragging a
|
||||||
scrubber bar around). The behavior of scrubbing mode can be customized
|
scrubber bar around). The behavior of scrubbing mode can be customized
|
||||||
with `setScrubbingModeParameters(..)` on `ExoPlayer` and
|
with `setScrubbingModeParameters(..)` on `ExoPlayer` and
|
||||||
`ExoPlayer.Builder`.
|
`ExoPlayer.Builder`.
|
||||||
* `AdPlaybackState.withAdDurationsUs(long[][])` can be used after ad
|
|
||||||
groups have been removed. The user still needs to pass in an array of
|
|
||||||
durations for removed ad groups which can be empty or null
|
|
||||||
([#2267](https://github.com/androidx/media/issues/2267)).
|
|
||||||
* Fix issue where `ProgressiveMediaPeriod` throws an
|
|
||||||
`IllegalStateException` as `PreloadMediaSource` attempts to call its
|
|
||||||
`getBufferedDurationUs()` before it is prepared
|
|
||||||
([#2315](https://github.com/androidx/media/issues/2315)).
|
|
||||||
* Transformer:
|
* Transformer:
|
||||||
* Filling an initial gap (added via `addGap()`) with silent audio now
|
* Filling an initial gap (added via `addGap()`) with silent audio now
|
||||||
requires explicitly setting `experimentalSetForceAudioTrack(true)` in
|
requires explicitly setting `experimentalSetForceAudioTrack(true)` in
|
||||||
@ -36,15 +20,9 @@
|
|||||||
variable bitrate metadata when falling back to constant bitrate seeking
|
variable bitrate metadata when falling back to constant bitrate seeking
|
||||||
due to `FLAG_ENABLE_CONSTANT_BITRATE_SEEKING(_ALWAYS)`
|
due to `FLAG_ENABLE_CONSTANT_BITRATE_SEEKING(_ALWAYS)`
|
||||||
([#2194](https://github.com/androidx/media/issues/2194)).
|
([#2194](https://github.com/androidx/media/issues/2194)).
|
||||||
* MP4: Parse `alternate_group` from the `tkhd` box and expose it as an
|
|
||||||
`Mp4AlternateGroupData` entry in each track's `Format.metadata`
|
|
||||||
([#2242](https://github.com/androidx/media/issues/2242)).
|
|
||||||
* DataSource:
|
* DataSource:
|
||||||
* Audio:
|
* Audio:
|
||||||
* Allow constant power upmixing/downmixing in DefaultAudioMixer.
|
* Allow constant power upmixing/downmixing in DefaultAudioMixer.
|
||||||
* Fix offload issue where position might get stuck when playing a playlist
|
|
||||||
of short content
|
|
||||||
([#1920](https://github.com/androidx/media/issues/1920)).
|
|
||||||
* Video:
|
* Video:
|
||||||
* Add experimental `ExoPlayer` API to include the
|
* Add experimental `ExoPlayer` API to include the
|
||||||
`MediaCodec.BUFFER_FLAG_DECODE_ONLY` flag when queuing decode-only input
|
`MediaCodec.BUFFER_FLAG_DECODE_ONLY` flag when queuing decode-only input
|
||||||
@ -67,25 +45,7 @@
|
|||||||
instead of `MediaCodec.BufferInfo`.
|
instead of `MediaCodec.BufferInfo`.
|
||||||
* IMA extension:
|
* IMA extension:
|
||||||
* Session:
|
* Session:
|
||||||
* Lower aggregation timeout for platform `MediaSession` callbacks from 500
|
|
||||||
to 100 milliseconds and add an experimental setter to allow apps to
|
|
||||||
configure this value.
|
|
||||||
* Fix issue where notifications reappear after they have been dismissed by
|
|
||||||
the user ([#2302](https://github.com/androidx/media/issues/2302)).
|
|
||||||
* Fix a bug where the `PlayerWrapper` returned a single-item timeline when
|
|
||||||
the wrapped player is actually empty. This happened when the wrapped
|
|
||||||
player doesn't have `COMMAND_GET_TIMELINE` available while
|
|
||||||
`COMMAND_GET_CURRENT_MEDIA_ITEM` is available and the wrapped player is
|
|
||||||
empty ([#2320](https://github.com/androidx/media/issues/2320)).
|
|
||||||
* Fix a bug where calling
|
|
||||||
`MediaSessionService.setMediaNotificationProvider` is silently ignored
|
|
||||||
after other interactions with the service like
|
|
||||||
`setForegroundServiceTimeoutMs`
|
|
||||||
([#2305](https://github.com/androidx/media/issues/2305)).
|
|
||||||
* UI:
|
* UI:
|
||||||
* Enable `PlayerSurface` to work with `ExoPlayer.setVideoEffects` and
|
|
||||||
`CompositionPlayer`.
|
|
||||||
* Fix bug where `PlayerSurface` can't be recomposed with a new `Player`.
|
|
||||||
* Downloads:
|
* Downloads:
|
||||||
* Add partial download support for progressive streams. Apps can prepare a
|
* Add partial download support for progressive streams. Apps can prepare a
|
||||||
progressive stream with `DownloadHelper`, and request a
|
progressive stream with `DownloadHelper`, and request a
|
||||||
@ -101,22 +61,19 @@
|
|||||||
* Cronet extension:
|
* Cronet extension:
|
||||||
* RTMP extension:
|
* RTMP extension:
|
||||||
* HLS extension:
|
* HLS extension:
|
||||||
* Fix issue where chunk duration wasn't set in `CmcdData` for HLS media,
|
|
||||||
causing an assertion failure when processing encrypted media segments
|
|
||||||
([#2312](https://github.com/androidx/media/issues/2312)).
|
|
||||||
* DASH extension:
|
* DASH extension:
|
||||||
* Smooth Streaming extension:
|
* Smooth Streaming extension:
|
||||||
* RTSP extension:
|
* RTSP extension:
|
||||||
* Add support for URI with RTSPT scheme as a way to configure the RTSP
|
|
||||||
session to use TCP
|
|
||||||
([#1484](https://github.com/androidx/media/issues/1484)).
|
|
||||||
* Decoder extensions (FFmpeg, VP9, AV1, etc.):
|
* Decoder extensions (FFmpeg, VP9, AV1, etc.):
|
||||||
* MIDI extension:
|
* MIDI extension:
|
||||||
* Leanback extension:
|
* Leanback extension:
|
||||||
* Cast extension:
|
* Cast extension:
|
||||||
* Add support for playlist metadata
|
|
||||||
([#2235](https://github.com/androidx/media/pull/2235)).
|
|
||||||
* Test Utilities:
|
* Test Utilities:
|
||||||
|
* Removed `transformer.TestUtil.addAudioDecoders(String...)`,
|
||||||
|
`transformer.TestUtil.addAudioEncoders(String...)`, and
|
||||||
|
`transformer.TestUtil.addAudioEncoders(ShadowMediaCodec.CodecConfig,
|
||||||
|
String...)`. Use `ShadowMediaCodecConfig` to configure shadow encoders
|
||||||
|
and decoders instead.
|
||||||
* Demo app:
|
* Demo app:
|
||||||
* Add `PlaybackSpeedPopUpButton` Composable UI element to be part of
|
* Add `PlaybackSpeedPopUpButton` Composable UI element to be part of
|
||||||
`ExtraControls` in `demo-compose`.
|
`ExtraControls` in `demo-compose`.
|
||||||
@ -138,6 +95,69 @@
|
|||||||
|
|
||||||
## 1.6
|
## 1.6
|
||||||
|
|
||||||
|
### 1.6.1 (2025-04-14)
|
||||||
|
|
||||||
|
This release includes the following changes since the
|
||||||
|
[1.6.0 release](#160-2025-03-26):
|
||||||
|
|
||||||
|
* Common Library:
|
||||||
|
* Add `PlaybackParameters.withPitch(float)` method for easily copying a
|
||||||
|
`PlaybackParameters` with a new `pitch` value
|
||||||
|
([#2257](https://github.com/androidx/media/issues/2257)).
|
||||||
|
* ExoPlayer:
|
||||||
|
* Fix issue where media item transition fails due to recoverable renderer
|
||||||
|
error during initialization of the next media item
|
||||||
|
([#2229](https://github.com/androidx/media/issues/2229)).
|
||||||
|
* Fix issue where `ProgressiveMediaPeriod` throws an
|
||||||
|
`IllegalStateException` as `PreloadMediaSource` attempts to call its
|
||||||
|
`getBufferedDurationUs()` before it is prepared
|
||||||
|
([#2315](https://github.com/androidx/media/issues/2315)).
|
||||||
|
* Fix sending `CmcdData` in manifest requests for DASH, HLS, and
|
||||||
|
SmoothStreaming ([#2253](https://github.com/androidx/media/pull/2253)).
|
||||||
|
* Ensure `AdPlaybackState.withAdDurationsUs(long[][])` can be used after
|
||||||
|
ad groups have been removed. The user still needs to pass in an array of
|
||||||
|
durations for removed ad groups which can be empty or null
|
||||||
|
([#2267](https://github.com/androidx/media/issues/2267)).
|
||||||
|
* Extractors:
|
||||||
|
* MP4: Parse `alternate_group` from the `tkhd` box and expose it as an
|
||||||
|
`Mp4AlternateGroupData` entry in each track's `Format.metadata`
|
||||||
|
([#2242](https://github.com/androidx/media/issues/2242)).
|
||||||
|
* Audio:
|
||||||
|
* Fix offload issue where the position might get stuck when playing a
|
||||||
|
playlist of short content
|
||||||
|
([#1920](https://github.com/androidx/media/issues/1920)).
|
||||||
|
* Session:
|
||||||
|
* Lower aggregation timeout for platform `MediaSession` callbacks from 500
|
||||||
|
to 100 milliseconds and add an experimental setter to allow apps to
|
||||||
|
configure this value.
|
||||||
|
* Fix issue where notifications reappear after they have been dismissed by
|
||||||
|
the user ([#2302](https://github.com/androidx/media/issues/2302)).
|
||||||
|
* Fix a bug where the session returned a single-item timeline when the
|
||||||
|
wrapped player is actually empty. This happened when the wrapped player
|
||||||
|
doesn't have `COMMAND_GET_TIMELINE` available while
|
||||||
|
`COMMAND_GET_CURRENT_MEDIA_ITEM` is available and the wrapped player is
|
||||||
|
empty ([#2320](https://github.com/androidx/media/issues/2320)).
|
||||||
|
* Fix a bug where calling
|
||||||
|
`MediaSessionService.setMediaNotificationProvider` is silently ignored
|
||||||
|
after other interactions with the service like
|
||||||
|
`setForegroundServiceTimeoutMs`
|
||||||
|
([#2305](https://github.com/androidx/media/issues/2305)).
|
||||||
|
* UI:
|
||||||
|
* Enable `PlayerSurface` to work with `ExoPlayer.setVideoEffects` and
|
||||||
|
`CompositionPlayer`.
|
||||||
|
* Fix bug where `PlayerSurface` can't be recomposed with a new `Player`.
|
||||||
|
* HLS extension:
|
||||||
|
* Fix issue where chunk duration wasn't set in `CmcdData` for HLS media,
|
||||||
|
causing an assertion failure when processing encrypted media segments
|
||||||
|
([#2312](https://github.com/androidx/media/issues/2312)).
|
||||||
|
* RTSP extension:
|
||||||
|
* Add support for URI with RTSPT scheme as a way to configure the RTSP
|
||||||
|
session to use TCP
|
||||||
|
([#1484](https://github.com/androidx/media/issues/1484)).
|
||||||
|
* Cast extension:
|
||||||
|
* Add support for playlist metadata
|
||||||
|
([#2235](https://github.com/androidx/media/pull/2235)).
|
||||||
|
|
||||||
### 1.6.0 (2025-03-26)
|
### 1.6.0 (2025-03-26)
|
||||||
|
|
||||||
This release includes the following changes since the
|
This release includes the following changes since the
|
||||||
@ -2966,6 +2986,10 @@ This release corresponds to the
|
|||||||
* Ad playback / IMA:
|
* Ad playback / IMA:
|
||||||
* Decrease ad polling rate from every 100ms to every 200ms, to line up
|
* Decrease ad polling rate from every 100ms to every 200ms, to line up
|
||||||
with Media Rating Council (MRC) recommendations.
|
with Media Rating Council (MRC) recommendations.
|
||||||
|
* Fix bug where ad groups after the end of a VOD window stalled playback.
|
||||||
|
Ads groups with a start time after the window are not enqueued into the
|
||||||
|
`MediaPeriodQueue` anymore
|
||||||
|
([#2215](https://github.com/androidx/media/issues/2215)).
|
||||||
* FFmpeg extension:
|
* FFmpeg extension:
|
||||||
* Update CMake version to `3.21.0+` to avoid a CMake bug causing
|
* Update CMake version to `3.21.0+` to avoid a CMake bug causing
|
||||||
AndroidStudio's gradle sync to fail
|
AndroidStudio's gradle sync to fail
|
||||||
|
@ -865,7 +865,10 @@ public final class AdPlaybackState {
|
|||||||
|| !getAdGroup(index).shouldPlayAdGroup())) {
|
|| !getAdGroup(index).shouldPlayAdGroup())) {
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
return index < adGroupCount ? index : C.INDEX_UNSET;
|
return index < adGroupCount
|
||||||
|
&& (periodDurationUs == C.TIME_UNSET || getAdGroup(index).timeUs <= periodDurationUs)
|
||||||
|
? index
|
||||||
|
: C.INDEX_UNSET;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns whether the specified ad has been marked as in {@link #AD_STATE_ERROR}. */
|
/** Returns whether the specified ad has been marked as in {@link #AD_STATE_ERROR}. */
|
||||||
|
@ -856,6 +856,10 @@ public class AdPlaybackStateTest {
|
|||||||
state.getAdGroupIndexAfterPositionUs(
|
state.getAdGroupIndexAfterPositionUs(
|
||||||
/* positionUs= */ C.TIME_END_OF_SOURCE, /* periodDurationUs= */ 5000))
|
/* positionUs= */ C.TIME_END_OF_SOURCE, /* periodDurationUs= */ 5000))
|
||||||
.isEqualTo(C.INDEX_UNSET);
|
.isEqualTo(C.INDEX_UNSET);
|
||||||
|
assertThat(
|
||||||
|
state.getAdGroupIndexAfterPositionUs(
|
||||||
|
/* positionUs= */ 1001, /* periodDurationUs= */ 1002))
|
||||||
|
.isEqualTo(C.INDEX_UNSET);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -263,7 +263,7 @@ public final class ExoPlayerTest {
|
|||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public ShadowMediaCodecConfig mediaCodecConfig =
|
public ShadowMediaCodecConfig mediaCodecConfig =
|
||||||
ShadowMediaCodecConfig.forAllSupportedMimeTypes();
|
ShadowMediaCodecConfig.withAllDefaultSupportedCodecs();
|
||||||
|
|
||||||
// The explicit boolean parameter is only used to give clear test names.
|
// The explicit boolean parameter is only used to give clear test names.
|
||||||
@Parameter(0)
|
@Parameter(0)
|
||||||
|
@ -83,7 +83,7 @@ public class ExoPlayerWithPrewarmingRenderersTest {
|
|||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public ShadowMediaCodecConfig mediaCodecConfig =
|
public ShadowMediaCodecConfig mediaCodecConfig =
|
||||||
ShadowMediaCodecConfig.forAllSupportedMimeTypes();
|
ShadowMediaCodecConfig.withAllDefaultSupportedCodecs();
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
|
@ -88,7 +88,7 @@ public class AudioCapabilitiesEndToEndTest {
|
|||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public ShadowMediaCodecConfig shadowMediaCodecConfig =
|
public ShadowMediaCodecConfig shadowMediaCodecConfig =
|
||||||
ShadowMediaCodecConfig.withNoDefaultSupportedMimeTypes();
|
ShadowMediaCodecConfig.withNoDefaultSupportedCodecs();
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
|
@ -61,7 +61,7 @@ public class AudioOffloadEndToEndTest {
|
|||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public ShadowMediaCodecConfig mediaCodecConfig =
|
public ShadowMediaCodecConfig mediaCodecConfig =
|
||||||
ShadowMediaCodecConfig.forAllSupportedMimeTypes();
|
ShadowMediaCodecConfig.withAllDefaultSupportedCodecs();
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setup() {
|
public void setup() {
|
||||||
|
@ -48,7 +48,7 @@ public class AnalyticsListenerPlaybackTest {
|
|||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public ShadowMediaCodecConfig mediaCodecConfig =
|
public ShadowMediaCodecConfig mediaCodecConfig =
|
||||||
ShadowMediaCodecConfig.forAllSupportedMimeTypes();
|
ShadowMediaCodecConfig.withAllDefaultSupportedCodecs();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void loadEventsReportedAsExpected() throws Exception {
|
public void loadEventsReportedAsExpected() throws Exception {
|
||||||
|
@ -69,7 +69,7 @@ public final class ClippingPlaylistPlaybackTest {
|
|||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public ShadowMediaCodecConfig mediaCodecConfig =
|
public ShadowMediaCodecConfig mediaCodecConfig =
|
||||||
ShadowMediaCodecConfig.forAllSupportedMimeTypes();
|
ShadowMediaCodecConfig.withAllDefaultSupportedCodecs();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void playbackWithClippingMediaSources() throws Exception {
|
public void playbackWithClippingMediaSources() throws Exception {
|
||||||
|
@ -57,7 +57,7 @@ public class EndToEndOffloadFailureRecoveryTest {
|
|||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public ShadowMediaCodecConfig mediaCodecConfig =
|
public ShadowMediaCodecConfig mediaCodecConfig =
|
||||||
ShadowMediaCodecConfig.forAllSupportedMimeTypes();
|
ShadowMediaCodecConfig.withAllDefaultSupportedCodecs();
|
||||||
|
|
||||||
public FakeClock fakeClock;
|
public FakeClock fakeClock;
|
||||||
public DefaultTrackSelector trackSelector;
|
public DefaultTrackSelector trackSelector;
|
||||||
|
@ -57,7 +57,7 @@ public class FlacPlaybackTest {
|
|||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public ShadowMediaCodecConfig mediaCodecConfig =
|
public ShadowMediaCodecConfig mediaCodecConfig =
|
||||||
ShadowMediaCodecConfig.forAllSupportedMimeTypes();
|
ShadowMediaCodecConfig.withAllDefaultSupportedCodecs();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void test() throws Exception {
|
public void test() throws Exception {
|
||||||
|
@ -47,7 +47,7 @@ public final class FlvPlaybackTest {
|
|||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public ShadowMediaCodecConfig mediaCodecConfig =
|
public ShadowMediaCodecConfig mediaCodecConfig =
|
||||||
ShadowMediaCodecConfig.forAllSupportedMimeTypes();
|
ShadowMediaCodecConfig.withAllDefaultSupportedCodecs();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void test() throws Exception {
|
public void test() throws Exception {
|
||||||
|
@ -86,7 +86,7 @@ public final class MergingPlaylistPlaybackTest {
|
|||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public ShadowMediaCodecConfig mediaCodecConfig =
|
public ShadowMediaCodecConfig mediaCodecConfig =
|
||||||
ShadowMediaCodecConfig.forAllSupportedMimeTypes();
|
ShadowMediaCodecConfig.withAllDefaultSupportedCodecs();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void transitionBetweenDifferentMergeConfigurations() throws Exception {
|
public void transitionBetweenDifferentMergeConfigurations() throws Exception {
|
||||||
|
@ -50,7 +50,7 @@ public final class MkaPlaybackTest {
|
|||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public ShadowMediaCodecConfig mediaCodecConfig =
|
public ShadowMediaCodecConfig mediaCodecConfig =
|
||||||
ShadowMediaCodecConfig.forAllSupportedMimeTypes();
|
ShadowMediaCodecConfig.withAllDefaultSupportedCodecs();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void test() throws Exception {
|
public void test() throws Exception {
|
||||||
|
@ -64,7 +64,7 @@ public final class MkvPlaybackTest {
|
|||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public ShadowMediaCodecConfig mediaCodecConfig =
|
public ShadowMediaCodecConfig mediaCodecConfig =
|
||||||
ShadowMediaCodecConfig.forAllSupportedMimeTypes();
|
ShadowMediaCodecConfig.withAllDefaultSupportedCodecs();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void test() throws Exception {
|
public void test() throws Exception {
|
||||||
|
@ -51,7 +51,7 @@ public class MotionPhotoPlaybackTest {
|
|||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public ShadowMediaCodecConfig mediaCodecConfig =
|
public ShadowMediaCodecConfig mediaCodecConfig =
|
||||||
ShadowMediaCodecConfig.forAllSupportedMimeTypes();
|
ShadowMediaCodecConfig.withAllDefaultSupportedCodecs();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void test() throws Exception {
|
public void test() throws Exception {
|
||||||
|
@ -54,7 +54,7 @@ public final class Mp3PlaybackTest {
|
|||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public ShadowMediaCodecConfig mediaCodecConfig =
|
public ShadowMediaCodecConfig mediaCodecConfig =
|
||||||
ShadowMediaCodecConfig.forAllSupportedMimeTypes();
|
ShadowMediaCodecConfig.withAllDefaultSupportedCodecs();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void test() throws Exception {
|
public void test() throws Exception {
|
||||||
|
@ -76,7 +76,7 @@ public class Mp4PlaybackTest {
|
|||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public ShadowMediaCodecConfig mediaCodecConfig =
|
public ShadowMediaCodecConfig mediaCodecConfig =
|
||||||
ShadowMediaCodecConfig.forAllSupportedMimeTypes();
|
ShadowMediaCodecConfig.withAllDefaultSupportedCodecs();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void test() throws Exception {
|
public void test() throws Exception {
|
||||||
|
@ -52,7 +52,7 @@ public final class OggOpusPlaybackTest {
|
|||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public ShadowMediaCodecConfig mediaCodecConfig =
|
public ShadowMediaCodecConfig mediaCodecConfig =
|
||||||
ShadowMediaCodecConfig.forAllSupportedMimeTypes();
|
ShadowMediaCodecConfig.withAllDefaultSupportedCodecs();
|
||||||
|
|
||||||
public FakeClock fakeClock;
|
public FakeClock fakeClock;
|
||||||
public OffloadRenderersFactory offloadRenderersFactory;
|
public OffloadRenderersFactory offloadRenderersFactory;
|
||||||
|
@ -50,7 +50,7 @@ public final class OggPlaybackTest {
|
|||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public ShadowMediaCodecConfig mediaCodecConfig =
|
public ShadowMediaCodecConfig mediaCodecConfig =
|
||||||
ShadowMediaCodecConfig.forAllSupportedMimeTypes();
|
ShadowMediaCodecConfig.withAllDefaultSupportedCodecs();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void test() throws Exception {
|
public void test() throws Exception {
|
||||||
|
@ -58,7 +58,7 @@ public class ParseAv1SampleDependenciesPlaybackTest {
|
|||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public ShadowMediaCodecConfig mediaCodecConfig =
|
public ShadowMediaCodecConfig mediaCodecConfig =
|
||||||
ShadowMediaCodecConfig.forAllSupportedMimeTypes();
|
ShadowMediaCodecConfig.withAllDefaultSupportedCodecs();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void playback_withClippedMediaItem_skipNonReferenceInputSamples() throws Exception {
|
public void playback_withClippedMediaItem_skipNonReferenceInputSamples() throws Exception {
|
||||||
|
@ -52,7 +52,7 @@ public final class PlaylistPlaybackTest {
|
|||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public ShadowMediaCodecConfig mediaCodecConfig =
|
public ShadowMediaCodecConfig mediaCodecConfig =
|
||||||
ShadowMediaCodecConfig.forAllSupportedMimeTypes();
|
ShadowMediaCodecConfig.withAllDefaultSupportedCodecs();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void test_bypassOnThenOff() throws Exception {
|
public void test_bypassOnThenOff() throws Exception {
|
||||||
|
@ -56,7 +56,7 @@ public class PrewarmingRendererPlaybackTest {
|
|||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public ShadowMediaCodecConfig mediaCodecConfig =
|
public ShadowMediaCodecConfig mediaCodecConfig =
|
||||||
ShadowMediaCodecConfig.forAllSupportedMimeTypes();
|
ShadowMediaCodecConfig.withAllDefaultSupportedCodecs();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void playback_withTwoMediaItemsAndSecondaryVideoRenderer_dumpsCorrectOutput()
|
public void playback_withTwoMediaItemsAndSecondaryVideoRenderer_dumpsCorrectOutput()
|
||||||
|
@ -37,7 +37,7 @@ public final class SilencePlaybackTest {
|
|||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public ShadowMediaCodecConfig mediaCodecConfig =
|
public ShadowMediaCodecConfig mediaCodecConfig =
|
||||||
ShadowMediaCodecConfig.forAllSupportedMimeTypes();
|
ShadowMediaCodecConfig.withAllDefaultSupportedCodecs();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void test_500ms() throws Exception {
|
public void test_500ms() throws Exception {
|
||||||
|
@ -60,7 +60,7 @@ public class SubtitlePlaybackTest {
|
|||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public ShadowMediaCodecConfig mediaCodecConfig =
|
public ShadowMediaCodecConfig mediaCodecConfig =
|
||||||
ShadowMediaCodecConfig.forAllSupportedMimeTypes();
|
ShadowMediaCodecConfig.withAllDefaultSupportedCodecs();
|
||||||
|
|
||||||
// https://github.com/androidx/media/issues/1721
|
// https://github.com/androidx/media/issues/1721
|
||||||
@Test
|
@Test
|
||||||
|
@ -74,7 +74,7 @@ public class TsPlaybackTest {
|
|||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public ShadowMediaCodecConfig mediaCodecConfig =
|
public ShadowMediaCodecConfig mediaCodecConfig =
|
||||||
ShadowMediaCodecConfig.forAllSupportedMimeTypes();
|
ShadowMediaCodecConfig.withAllDefaultSupportedCodecs();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void test() throws Exception {
|
public void test() throws Exception {
|
||||||
|
@ -51,7 +51,7 @@ public final class Vp9PlaybackTest {
|
|||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public ShadowMediaCodecConfig mediaCodecConfig =
|
public ShadowMediaCodecConfig mediaCodecConfig =
|
||||||
ShadowMediaCodecConfig.forAllSupportedMimeTypes();
|
ShadowMediaCodecConfig.withAllDefaultSupportedCodecs();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void test() throws Exception {
|
public void test() throws Exception {
|
||||||
|
@ -44,7 +44,7 @@ public final class WavPlaybackTest {
|
|||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public ShadowMediaCodecConfig mediaCodecConfig =
|
public ShadowMediaCodecConfig mediaCodecConfig =
|
||||||
ShadowMediaCodecConfig.forAllSupportedMimeTypes();
|
ShadowMediaCodecConfig.withAllDefaultSupportedCodecs();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void test() throws Exception {
|
public void test() throws Exception {
|
||||||
|
@ -68,7 +68,7 @@ public class WebvttPlaybackTest {
|
|||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public ShadowMediaCodecConfig mediaCodecConfig =
|
public ShadowMediaCodecConfig mediaCodecConfig =
|
||||||
ShadowMediaCodecConfig.forAllSupportedMimeTypes();
|
ShadowMediaCodecConfig.withAllDefaultSupportedCodecs();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void test() throws Exception {
|
public void test() throws Exception {
|
||||||
|
@ -104,7 +104,7 @@ public final class ServerSideAdInsertionMediaSourceTest {
|
|||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public ShadowMediaCodecConfig mediaCodecConfig =
|
public ShadowMediaCodecConfig mediaCodecConfig =
|
||||||
ShadowMediaCodecConfig.forAllSupportedMimeTypes();
|
ShadowMediaCodecConfig.withAllDefaultSupportedCodecs();
|
||||||
|
|
||||||
private static final String TEST_ASSET = "asset:///media/mp4/sample.mp4";
|
private static final String TEST_ASSET = "asset:///media/mp4/sample.mp4";
|
||||||
|
|
||||||
|
@ -75,7 +75,7 @@ public final class DashPlaybackTest {
|
|||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public ShadowMediaCodecConfig mediaCodecConfig =
|
public ShadowMediaCodecConfig mediaCodecConfig =
|
||||||
ShadowMediaCodecConfig.forAllSupportedMimeTypes();
|
ShadowMediaCodecConfig.withAllDefaultSupportedCodecs();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void webvttStandaloneFile() throws Exception {
|
public void webvttStandaloneFile() throws Exception {
|
||||||
|
@ -70,7 +70,7 @@ public final class HlsPlaybackTest {
|
|||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public ShadowMediaCodecConfig mediaCodecConfig =
|
public ShadowMediaCodecConfig mediaCodecConfig =
|
||||||
ShadowMediaCodecConfig.forAllSupportedMimeTypes();
|
ShadowMediaCodecConfig.withAllDefaultSupportedCodecs();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void webvttStandaloneSubtitlesFile() throws Exception {
|
public void webvttStandaloneSubtitlesFile() throws Exception {
|
||||||
|
@ -868,7 +868,7 @@ import java.util.Objects;
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (imaAdState == IMA_AD_STATE_NONE
|
if (imaAdState == IMA_AD_STATE_NONE
|
||||||
&& playbackState == Player.STATE_BUFFERING
|
&& (playbackState == Player.STATE_BUFFERING || playbackState == Player.STATE_ENDED)
|
||||||
&& playWhenReady) {
|
&& playWhenReady) {
|
||||||
ensureSentContentCompleteIfAtEndOfStream();
|
ensureSentContentCompleteIfAtEndOfStream();
|
||||||
} else if (imaAdState != IMA_AD_STATE_NONE && playbackState == Player.STATE_ENDED) {
|
} else if (imaAdState != IMA_AD_STATE_NONE && playbackState == Player.STATE_ENDED) {
|
||||||
|
@ -80,7 +80,7 @@ public final class RtspPlaybackTest {
|
|||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public ShadowMediaCodecConfig mediaCodecConfig =
|
public ShadowMediaCodecConfig mediaCodecConfig =
|
||||||
ShadowMediaCodecConfig.forAllSupportedMimeTypes();
|
ShadowMediaCodecConfig.withAllDefaultSupportedCodecs();
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
|
@ -27,6 +27,7 @@ import com.google.common.collect.ImmutableList;
|
|||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.common.primitives.Ints;
|
import com.google.common.primitives.Ints;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.List;
|
||||||
import org.junit.rules.ExternalResource;
|
import org.junit.rules.ExternalResource;
|
||||||
import org.robolectric.shadows.MediaCodecInfoBuilder;
|
import org.robolectric.shadows.MediaCodecInfoBuilder;
|
||||||
import org.robolectric.shadows.ShadowMediaCodec;
|
import org.robolectric.shadows.ShadowMediaCodec;
|
||||||
@ -132,6 +133,8 @@ public final class ShadowMediaCodecConfig extends ExternalResource {
|
|||||||
new CodecInfo(/* codecName= */ "exotest.audio.ac3", MimeTypes.AUDIO_AC3);
|
new CodecInfo(/* codecName= */ "exotest.audio.ac3", MimeTypes.AUDIO_AC3);
|
||||||
public static final CodecInfo CODEC_INFO_AC4 =
|
public static final CodecInfo CODEC_INFO_AC4 =
|
||||||
new CodecInfo(/* codecName= */ "exotest.audio.ac4", MimeTypes.AUDIO_AC4);
|
new CodecInfo(/* codecName= */ "exotest.audio.ac4", MimeTypes.AUDIO_AC4);
|
||||||
|
public static final CodecInfo CODEC_INFO_AMR_NB =
|
||||||
|
new CodecInfo(/* codecName= */ "exotest.audio.amrnb", MimeTypes.AUDIO_AMR_NB);
|
||||||
public static final CodecInfo CODEC_INFO_E_AC3 =
|
public static final CodecInfo CODEC_INFO_E_AC3 =
|
||||||
new CodecInfo(/* codecName= */ "exotest.audio.eac3", MimeTypes.AUDIO_E_AC3);
|
new CodecInfo(/* codecName= */ "exotest.audio.eac3", MimeTypes.AUDIO_E_AC3);
|
||||||
public static final CodecInfo CODEC_INFO_E_AC3_JOC =
|
public static final CodecInfo CODEC_INFO_E_AC3_JOC =
|
||||||
@ -173,7 +176,7 @@ public final class ShadowMediaCodecConfig extends ExternalResource {
|
|||||||
/**
|
/**
|
||||||
* @deprecated Use {@link ShadowMediaCodecConfig#withAllDefaultSupportedCodecs()} instead.
|
* @deprecated Use {@link ShadowMediaCodecConfig#withAllDefaultSupportedCodecs()} instead.
|
||||||
*/
|
*/
|
||||||
// TODO(b/399861060): Remove in Media3 1.8.
|
// TODO(b/406437316): Remove in Media3 1.8.
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public static ShadowMediaCodecConfig forAllSupportedMimeTypes() {
|
public static ShadowMediaCodecConfig forAllSupportedMimeTypes() {
|
||||||
return withAllDefaultSupportedCodecs();
|
return withAllDefaultSupportedCodecs();
|
||||||
@ -194,7 +197,7 @@ public final class ShadowMediaCodecConfig extends ExternalResource {
|
|||||||
/**
|
/**
|
||||||
* @deprecated Use {@link ShadowMediaCodecConfig#withNoDefaultSupportedCodecs()} instead.
|
* @deprecated Use {@link ShadowMediaCodecConfig#withNoDefaultSupportedCodecs()} instead.
|
||||||
*/
|
*/
|
||||||
// TODO(b/399861060): Remove in Media3 1.8.
|
// TODO(b/406437316): Remove in Media3 1.8.
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public static ShadowMediaCodecConfig withNoDefaultSupportedMimeTypes() {
|
public static ShadowMediaCodecConfig withNoDefaultSupportedMimeTypes() {
|
||||||
return withNoDefaultSupportedCodecs();
|
return withNoDefaultSupportedCodecs();
|
||||||
@ -205,6 +208,20 @@ public final class ShadowMediaCodecConfig extends ExternalResource {
|
|||||||
return new ShadowMediaCodecConfig(ImmutableSet.of());
|
return new ShadowMediaCodecConfig(ImmutableSet.of());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {@link ShadowMediaCodecConfig} instance configured with the provided {@code decoders}
|
||||||
|
* and {@code encoders}.
|
||||||
|
*
|
||||||
|
* <p>All codecs will work as passthrough, regardless of type.
|
||||||
|
*/
|
||||||
|
public static ShadowMediaCodecConfig withCodecs(
|
||||||
|
List<CodecInfo> decoders, List<CodecInfo> encoders) {
|
||||||
|
ImmutableSet.Builder<CodecImpl> codecs = new ImmutableSet.Builder<>();
|
||||||
|
codecs.addAll(createDecoders(decoders, /* forcePassthrough= */ true));
|
||||||
|
codecs.addAll(createEncoders(encoders));
|
||||||
|
return new ShadowMediaCodecConfig(codecs.build());
|
||||||
|
}
|
||||||
|
|
||||||
private final ImmutableSet<CodecImpl> defaultCodecs;
|
private final ImmutableSet<CodecImpl> defaultCodecs;
|
||||||
|
|
||||||
private ShadowMediaCodecConfig(ImmutableSet<CodecImpl> defaultCodecs) {
|
private ShadowMediaCodecConfig(ImmutableSet<CodecImpl> defaultCodecs) {
|
||||||
@ -267,6 +284,39 @@ public final class ShadowMediaCodecConfig extends ExternalResource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures and publishes {@linkplain ShadowMediaCodec shadow encoders} based on {@code
|
||||||
|
* encoders}.
|
||||||
|
*
|
||||||
|
* <p>This method configures pass-through encoders.
|
||||||
|
*/
|
||||||
|
public void addEncoders(CodecInfo... encoders) {
|
||||||
|
for (CodecInfo encoderInfo : encoders) {
|
||||||
|
CodecImpl encoder = CodecImpl.createEncoder(encoderInfo);
|
||||||
|
encoder.configure();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures and publishes a {@link ShadowMediaCodec} codec.
|
||||||
|
*
|
||||||
|
* <p>Input buffers are handled according to the {@link ShadowMediaCodec.CodecConfig} provided.
|
||||||
|
*
|
||||||
|
* @param codecInfo Basic codec information.
|
||||||
|
* @param isEncoder Whether the codecs registered are encoders or decoders.
|
||||||
|
* @param codecConfig Codec configuration implementation of the shadow.
|
||||||
|
*/
|
||||||
|
public void addCodec(
|
||||||
|
CodecInfo codecInfo, boolean isEncoder, ShadowMediaCodec.CodecConfig codecConfig) {
|
||||||
|
configureShadowMediaCodec(
|
||||||
|
codecInfo.codecName,
|
||||||
|
codecInfo.mimeType,
|
||||||
|
isEncoder,
|
||||||
|
codecInfo.profileLevels,
|
||||||
|
codecInfo.colorFormats,
|
||||||
|
codecConfig);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void before() throws Throwable {
|
protected void before() throws Throwable {
|
||||||
for (CodecImpl codec : this.defaultCodecs) {
|
for (CodecImpl codec : this.defaultCodecs) {
|
||||||
@ -282,7 +332,7 @@ public final class ShadowMediaCodecConfig extends ExternalResource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static ImmutableSet<CodecImpl> createDecoders(
|
private static ImmutableSet<CodecImpl> createDecoders(
|
||||||
ImmutableList<CodecInfo> decoderInfos, boolean forcePassthrough) {
|
List<CodecInfo> decoderInfos, boolean forcePassthrough) {
|
||||||
ImmutableSet.Builder<CodecImpl> builder = new ImmutableSet.Builder<>();
|
ImmutableSet.Builder<CodecImpl> builder = new ImmutableSet.Builder<>();
|
||||||
for (CodecInfo info : decoderInfos) {
|
for (CodecInfo info : decoderInfos) {
|
||||||
if (!forcePassthrough && MimeTypes.isAudio(info.mimeType)) {
|
if (!forcePassthrough && MimeTypes.isAudio(info.mimeType)) {
|
||||||
@ -294,6 +344,14 @@ public final class ShadowMediaCodecConfig extends ExternalResource {
|
|||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static ImmutableSet<CodecImpl> createEncoders(List<CodecInfo> encoderInfos) {
|
||||||
|
ImmutableSet.Builder<CodecImpl> builder = new ImmutableSet.Builder<>();
|
||||||
|
for (CodecInfo info : encoderInfos) {
|
||||||
|
builder.add(CodecImpl.createEncoder(info));
|
||||||
|
}
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link ShadowMediaCodec.CodecConfig.Codec} that provides pass-through or frame dropping
|
* A {@link ShadowMediaCodec.CodecConfig.Codec} that provides pass-through or frame dropping
|
||||||
* encoders and decoders.
|
* encoders and decoders.
|
||||||
@ -312,6 +370,10 @@ public final class ShadowMediaCodecConfig extends ExternalResource {
|
|||||||
return new CodecImpl(codecInfo, /* isPassthrough= */ true, /* isEncoder= */ false);
|
return new CodecImpl(codecInfo, /* isPassthrough= */ true, /* isEncoder= */ false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static CodecImpl createEncoder(CodecInfo codecInfo) {
|
||||||
|
return new CodecImpl(codecInfo, /* isPassthrough= */ true, /* isEncoder= */ true);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an instance.
|
* Creates an instance.
|
||||||
*
|
*
|
||||||
|
@ -556,8 +556,12 @@ public class TransformerEndToEndTest {
|
|||||||
.build()
|
.build()
|
||||||
.run(testId, editedMediaItem);
|
.run(testId, editedMediaItem);
|
||||||
|
|
||||||
|
// Rarely, MediaCodec decoders output frames in the wrong order.
|
||||||
|
// When the MediaCodec encoder sees frames in the wrong order, fewer output frames are produced.
|
||||||
|
// Use a tolerance when comparing frame counts. See b/343476417#comment5.
|
||||||
assertThat(result.exportResult.videoFrameCount)
|
assertThat(result.exportResult.videoFrameCount)
|
||||||
.isEqualTo(MP4_ASSET_WITH_INCREASING_TIMESTAMPS_320W_240H_15S.videoFrameCount);
|
.isWithin(2)
|
||||||
|
.of(MP4_ASSET_WITH_INCREASING_TIMESTAMPS_320W_240H_15S.videoFrameCount);
|
||||||
assertThat(new File(result.filePath).length()).isGreaterThan(0);
|
assertThat(new File(result.filePath).length()).isGreaterThan(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -583,8 +587,12 @@ public class TransformerEndToEndTest {
|
|||||||
.build()
|
.build()
|
||||||
.run(testId, editedMediaItem);
|
.run(testId, editedMediaItem);
|
||||||
|
|
||||||
|
// Rarely, MediaCodec decoders output frames in the wrong order.
|
||||||
|
// When the MediaCodec encoder sees frames in the wrong order, fewer output frames are produced.
|
||||||
|
// Use a tolerance when comparing frame counts. See b/343476417#comment5.
|
||||||
assertThat(result.exportResult.videoFrameCount)
|
assertThat(result.exportResult.videoFrameCount)
|
||||||
.isEqualTo(MP4_ASSET_WITH_INCREASING_TIMESTAMPS_320W_240H_15S.videoFrameCount);
|
.isWithin(2)
|
||||||
|
.of(MP4_ASSET_WITH_INCREASING_TIMESTAMPS_320W_240H_15S.videoFrameCount);
|
||||||
assertThat(new File(result.filePath).length()).isGreaterThan(0);
|
assertThat(new File(result.filePath).length()).isGreaterThan(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,7 +99,10 @@ public class TransformerPauseResumeTest {
|
|||||||
|
|
||||||
ExportResult exportResult = result.exportResult;
|
ExportResult exportResult = result.exportResult;
|
||||||
assertThat(exportResult.processedInputs).hasSize(4);
|
assertThat(exportResult.processedInputs).hasSize(4);
|
||||||
assertThat(exportResult.videoFrameCount).isEqualTo(MP4_ASSET_FRAME_COUNT);
|
// Rarely, MediaCodec decoders output frames in the wrong order.
|
||||||
|
// When the MediaCodec encoder sees frames in the wrong order, fewer output frames are produced.
|
||||||
|
// Use a tolerance when comparing frame counts. See b/343476417#comment5.
|
||||||
|
assertThat(exportResult.videoFrameCount).isWithin(2).of(MP4_ASSET_FRAME_COUNT);
|
||||||
// The first processed media item corresponds to remuxing previous output video.
|
// The first processed media item corresponds to remuxing previous output video.
|
||||||
assertThat(exportResult.processedInputs.get(0).audioDecoderName).isNull();
|
assertThat(exportResult.processedInputs.get(0).audioDecoderName).isNull();
|
||||||
assertThat(exportResult.processedInputs.get(0).videoDecoderName).isNull();
|
assertThat(exportResult.processedInputs.get(0).videoDecoderName).isNull();
|
||||||
@ -197,8 +200,12 @@ public class TransformerPauseResumeTest {
|
|||||||
.isEqualTo(exportResultWithoutResume.audioEncoderName);
|
.isEqualTo(exportResultWithoutResume.audioEncoderName);
|
||||||
assertThat(exportResultWithResume.videoEncoderName)
|
assertThat(exportResultWithResume.videoEncoderName)
|
||||||
.isEqualTo(exportResultWithoutResume.videoEncoderName);
|
.isEqualTo(exportResultWithoutResume.videoEncoderName);
|
||||||
|
// Rarely, MediaCodec decoders output frames in the wrong order.
|
||||||
|
// When the MediaCodec encoder sees frames in the wrong order, fewer output frames are produced.
|
||||||
|
// Use a tolerance when comparing frame counts. See b/343476417#comment5.
|
||||||
assertThat(exportResultWithResume.videoFrameCount)
|
assertThat(exportResultWithResume.videoFrameCount)
|
||||||
.isEqualTo(exportResultWithoutResume.videoFrameCount);
|
.isWithin(2)
|
||||||
|
.of(exportResultWithoutResume.videoFrameCount);
|
||||||
// TODO: b/306595508 - Remove this expected difference once inconsistent behaviour of audio
|
// TODO: b/306595508 - Remove this expected difference once inconsistent behaviour of audio
|
||||||
// encoder is fixed.
|
// encoder is fixed.
|
||||||
int maxDiffExpectedInDurationMs = 2;
|
int maxDiffExpectedInDurationMs = 2;
|
||||||
@ -252,8 +259,12 @@ public class TransformerPauseResumeTest {
|
|||||||
.isEqualTo(exportResultWithoutResume.audioEncoderName);
|
.isEqualTo(exportResultWithoutResume.audioEncoderName);
|
||||||
assertThat(exportResultWithResume.videoEncoderName)
|
assertThat(exportResultWithResume.videoEncoderName)
|
||||||
.isEqualTo(exportResultWithoutResume.videoEncoderName);
|
.isEqualTo(exportResultWithoutResume.videoEncoderName);
|
||||||
|
// Rarely, MediaCodec decoders output frames in the wrong order.
|
||||||
|
// When the MediaCodec encoder sees frames in the wrong order, fewer output frames are produced.
|
||||||
|
// Use a tolerance when comparing frame counts. See b/343476417#comment5.
|
||||||
assertThat(exportResultWithResume.videoFrameCount)
|
assertThat(exportResultWithResume.videoFrameCount)
|
||||||
.isEqualTo(exportResultWithoutResume.videoFrameCount);
|
.isWithin(2)
|
||||||
|
.of(exportResultWithoutResume.videoFrameCount);
|
||||||
int maxDiffExpectedInDurationMs = 2;
|
int maxDiffExpectedInDurationMs = 2;
|
||||||
assertThat(exportResultWithResume.durationMs - exportResultWithoutResume.durationMs)
|
assertThat(exportResultWithResume.durationMs - exportResultWithoutResume.durationMs)
|
||||||
.isLessThan(maxDiffExpectedInDurationMs);
|
.isLessThan(maxDiffExpectedInDurationMs);
|
||||||
@ -293,7 +304,10 @@ public class TransformerPauseResumeTest {
|
|||||||
ExportResult exportResult = result.exportResult;
|
ExportResult exportResult = result.exportResult;
|
||||||
assertThat(exportResult.processedInputs).hasSize(6);
|
assertThat(exportResult.processedInputs).hasSize(6);
|
||||||
int expectedVideoFrameCount = 2 * MP4_ASSET_FRAME_COUNT;
|
int expectedVideoFrameCount = 2 * MP4_ASSET_FRAME_COUNT;
|
||||||
assertThat(exportResult.videoFrameCount).isEqualTo(expectedVideoFrameCount);
|
// Rarely, MediaCodec decoders output frames in the wrong order.
|
||||||
|
// When the MediaCodec encoder sees frames in the wrong order, fewer output frames are produced.
|
||||||
|
// Use a tolerance when comparing frame counts. See b/343476417#comment5.
|
||||||
|
assertThat(exportResult.videoFrameCount).isWithin(2).of(expectedVideoFrameCount);
|
||||||
// The first processed media item corresponds to remuxing previous output video.
|
// The first processed media item corresponds to remuxing previous output video.
|
||||||
assertThat(exportResult.processedInputs.get(0).audioDecoderName).isNull();
|
assertThat(exportResult.processedInputs.get(0).audioDecoderName).isNull();
|
||||||
assertThat(exportResult.processedInputs.get(0).videoDecoderName).isNull();
|
assertThat(exportResult.processedInputs.get(0).videoDecoderName).isNull();
|
||||||
@ -358,8 +372,12 @@ public class TransformerPauseResumeTest {
|
|||||||
.isEqualTo(exportResultWithoutResume.audioEncoderName);
|
.isEqualTo(exportResultWithoutResume.audioEncoderName);
|
||||||
assertThat(exportResultWithResume.videoEncoderName)
|
assertThat(exportResultWithResume.videoEncoderName)
|
||||||
.isEqualTo(exportResultWithoutResume.videoEncoderName);
|
.isEqualTo(exportResultWithoutResume.videoEncoderName);
|
||||||
|
// Rarely, MediaCodec decoders output frames in the wrong order.
|
||||||
|
// When the MediaCodec encoder sees frames in the wrong order, fewer output frames are produced.
|
||||||
|
// Use a tolerance when comparing frame counts. See b/343476417#comment5.
|
||||||
assertThat(exportResultWithResume.videoFrameCount)
|
assertThat(exportResultWithResume.videoFrameCount)
|
||||||
.isEqualTo(exportResultWithoutResume.videoFrameCount);
|
.isWithin(2)
|
||||||
|
.of(exportResultWithoutResume.videoFrameCount);
|
||||||
int maxDiffExpectedInDurationMs = 2;
|
int maxDiffExpectedInDurationMs = 2;
|
||||||
assertThat(exportResultWithResume.durationMs - exportResultWithoutResume.durationMs)
|
assertThat(exportResultWithResume.durationMs - exportResultWithoutResume.durationMs)
|
||||||
.isLessThan(maxDiffExpectedInDurationMs);
|
.isLessThan(maxDiffExpectedInDurationMs);
|
||||||
|
@ -15,6 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
package androidx.media3.transformer;
|
package androidx.media3.transformer;
|
||||||
|
|
||||||
|
import static androidx.media3.test.utils.robolectric.ShadowMediaCodecConfig.CODEC_INFO_AAC;
|
||||||
|
import static androidx.media3.test.utils.robolectric.ShadowMediaCodecConfig.CODEC_INFO_RAW;
|
||||||
import static androidx.media3.transformer.TestUtil.ASSET_URI_PREFIX;
|
import static androidx.media3.transformer.TestUtil.ASSET_URI_PREFIX;
|
||||||
import static androidx.media3.transformer.TestUtil.FILE_AUDIO_ONLY;
|
import static androidx.media3.transformer.TestUtil.FILE_AUDIO_ONLY;
|
||||||
import static androidx.media3.transformer.TestUtil.FILE_AUDIO_RAW;
|
import static androidx.media3.transformer.TestUtil.FILE_AUDIO_RAW;
|
||||||
@ -23,25 +25,22 @@ import static androidx.media3.transformer.TestUtil.FILE_AUDIO_RAW_VIDEO;
|
|||||||
import static androidx.media3.transformer.TestUtil.FILE_AUDIO_VIDEO;
|
import static androidx.media3.transformer.TestUtil.FILE_AUDIO_VIDEO;
|
||||||
import static androidx.media3.transformer.TestUtil.FILE_AUDIO_VIDEO_INCREASING_TIMESTAMPS_15S;
|
import static androidx.media3.transformer.TestUtil.FILE_AUDIO_VIDEO_INCREASING_TIMESTAMPS_15S;
|
||||||
import static androidx.media3.transformer.TestUtil.FILE_VIDEO_ONLY;
|
import static androidx.media3.transformer.TestUtil.FILE_VIDEO_ONLY;
|
||||||
import static androidx.media3.transformer.TestUtil.addAudioDecoders;
|
|
||||||
import static androidx.media3.transformer.TestUtil.addAudioEncoders;
|
|
||||||
import static androidx.media3.transformer.TestUtil.createAudioEffects;
|
import static androidx.media3.transformer.TestUtil.createAudioEffects;
|
||||||
import static androidx.media3.transformer.TestUtil.createVolumeScalingAudioProcessor;
|
import static androidx.media3.transformer.TestUtil.createVolumeScalingAudioProcessor;
|
||||||
import static androidx.media3.transformer.TestUtil.getCompositionDumpFilePath;
|
import static androidx.media3.transformer.TestUtil.getCompositionDumpFilePath;
|
||||||
import static androidx.media3.transformer.TestUtil.getDumpFileName;
|
import static androidx.media3.transformer.TestUtil.getDumpFileName;
|
||||||
import static androidx.media3.transformer.TestUtil.removeEncodersAndDecoders;
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import androidx.media3.common.MediaItem;
|
import androidx.media3.common.MediaItem;
|
||||||
import androidx.media3.common.MimeTypes;
|
|
||||||
import androidx.media3.common.audio.SonicAudioProcessor;
|
import androidx.media3.common.audio.SonicAudioProcessor;
|
||||||
import androidx.media3.test.utils.DumpFileAsserts;
|
import androidx.media3.test.utils.DumpFileAsserts;
|
||||||
import androidx.media3.test.utils.TestTransformerBuilder;
|
import androidx.media3.test.utils.TestTransformerBuilder;
|
||||||
|
import androidx.media3.test.utils.robolectric.ShadowMediaCodecConfig;
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.rules.TemporaryFolder;
|
import org.junit.rules.TemporaryFolder;
|
||||||
@ -57,15 +56,16 @@ public class CompositionExportTest {
|
|||||||
|
|
||||||
private final Context context = ApplicationProvider.getApplicationContext();
|
private final Context context = ApplicationProvider.getApplicationContext();
|
||||||
|
|
||||||
@Before
|
@Rule
|
||||||
public void setUp() {
|
public ShadowMediaCodecConfig shadowMediaCodecConfig =
|
||||||
addAudioDecoders(MimeTypes.AUDIO_RAW);
|
ShadowMediaCodecConfig.withCodecs(
|
||||||
addAudioEncoders(MimeTypes.AUDIO_AAC);
|
/* decoders= */ ImmutableList.of(CODEC_INFO_RAW),
|
||||||
}
|
/* encoders= */ ImmutableList.of(CODEC_INFO_AAC));
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void tearDown() {
|
public void tearDown() {
|
||||||
removeEncodersAndDecoders();
|
// TODO(b/406463016): Investigate moving this call to ShadowMediaCodecConfig#after() method.
|
||||||
|
EncoderUtil.clearCachedEncoders();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -17,6 +17,9 @@
|
|||||||
package androidx.media3.transformer;
|
package androidx.media3.transformer;
|
||||||
|
|
||||||
import static androidx.media3.test.utils.robolectric.RobolectricUtil.runLooperUntil;
|
import static androidx.media3.test.utils.robolectric.RobolectricUtil.runLooperUntil;
|
||||||
|
import static androidx.media3.test.utils.robolectric.ShadowMediaCodecConfig.CODEC_INFO_AAC;
|
||||||
|
import static androidx.media3.test.utils.robolectric.ShadowMediaCodecConfig.CODEC_INFO_AMR_NB;
|
||||||
|
import static androidx.media3.test.utils.robolectric.ShadowMediaCodecConfig.CODEC_INFO_RAW;
|
||||||
import static androidx.media3.transformer.AssetLoader.SUPPORTED_OUTPUT_TYPE_DECODED;
|
import static androidx.media3.transformer.AssetLoader.SUPPORTED_OUTPUT_TYPE_DECODED;
|
||||||
import static androidx.media3.transformer.AssetLoader.SUPPORTED_OUTPUT_TYPE_ENCODED;
|
import static androidx.media3.transformer.AssetLoader.SUPPORTED_OUTPUT_TYPE_ENCODED;
|
||||||
import static androidx.media3.transformer.ExportResult.CONVERSION_PROCESS_NA;
|
import static androidx.media3.transformer.ExportResult.CONVERSION_PROCESS_NA;
|
||||||
@ -36,12 +39,9 @@ import static androidx.media3.transformer.TestUtil.FILE_VIDEO_ELST_TRIM_IDR_DURA
|
|||||||
import static androidx.media3.transformer.TestUtil.FILE_VIDEO_ONLY;
|
import static androidx.media3.transformer.TestUtil.FILE_VIDEO_ONLY;
|
||||||
import static androidx.media3.transformer.TestUtil.FILE_WITH_SEF_SLOW_MOTION;
|
import static androidx.media3.transformer.TestUtil.FILE_WITH_SEF_SLOW_MOTION;
|
||||||
import static androidx.media3.transformer.TestUtil.FILE_WITH_SUBTITLES;
|
import static androidx.media3.transformer.TestUtil.FILE_WITH_SUBTITLES;
|
||||||
import static androidx.media3.transformer.TestUtil.addAudioDecoders;
|
|
||||||
import static androidx.media3.transformer.TestUtil.addAudioEncoders;
|
|
||||||
import static androidx.media3.transformer.TestUtil.createAudioEffects;
|
import static androidx.media3.transformer.TestUtil.createAudioEffects;
|
||||||
import static androidx.media3.transformer.TestUtil.createPitchChangingAudioProcessor;
|
import static androidx.media3.transformer.TestUtil.createPitchChangingAudioProcessor;
|
||||||
import static androidx.media3.transformer.TestUtil.getDumpFileName;
|
import static androidx.media3.transformer.TestUtil.getDumpFileName;
|
||||||
import static androidx.media3.transformer.TestUtil.removeEncodersAndDecoders;
|
|
||||||
import static androidx.media3.transformer.Transformer.PROGRESS_STATE_AVAILABLE;
|
import static androidx.media3.transformer.Transformer.PROGRESS_STATE_AVAILABLE;
|
||||||
import static androidx.media3.transformer.Transformer.PROGRESS_STATE_NOT_STARTED;
|
import static androidx.media3.transformer.Transformer.PROGRESS_STATE_NOT_STARTED;
|
||||||
import static androidx.media3.transformer.Transformer.PROGRESS_STATE_UNAVAILABLE;
|
import static androidx.media3.transformer.Transformer.PROGRESS_STATE_UNAVAILABLE;
|
||||||
@ -89,6 +89,7 @@ import androidx.media3.extractor.PositionHolder;
|
|||||||
import androidx.media3.test.utils.DumpFileAsserts;
|
import androidx.media3.test.utils.DumpFileAsserts;
|
||||||
import androidx.media3.test.utils.FakeClock;
|
import androidx.media3.test.utils.FakeClock;
|
||||||
import androidx.media3.test.utils.TestTransformerBuilder;
|
import androidx.media3.test.utils.TestTransformerBuilder;
|
||||||
|
import androidx.media3.test.utils.robolectric.ShadowMediaCodecConfig;
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
@ -105,7 +106,6 @@ import java.util.concurrent.TimeoutException;
|
|||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Ignore;
|
import org.junit.Ignore;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@ -125,24 +125,42 @@ import org.robolectric.shadows.ShadowMediaCodec;
|
|||||||
public final class MediaItemExportTest {
|
public final class MediaItemExportTest {
|
||||||
|
|
||||||
private static final long TEST_TIMEOUT_SECONDS = 10;
|
private static final long TEST_TIMEOUT_SECONDS = 10;
|
||||||
|
private static final String EXPECTED_CODEC_EXCEPTION_MESSAGE = "Unexpected format!";
|
||||||
|
private static final ShadowMediaCodec.CodecConfig THROWING_CODEC_CONFIG =
|
||||||
|
new ShadowMediaCodec.CodecConfig(
|
||||||
|
/* inputBufferSize= */ 100_000,
|
||||||
|
/* outputBufferSize= */ 100_000,
|
||||||
|
new ShadowMediaCodec.CodecConfig.Codec() {
|
||||||
|
@Override
|
||||||
|
public void process(ByteBuffer byteBuffer, ByteBuffer byteBuffer1) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onConfigured(
|
||||||
|
MediaFormat format, Surface surface, MediaCrypto crypto, int flags) {
|
||||||
|
throw new IllegalArgumentException(EXPECTED_CODEC_EXCEPTION_MESSAGE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
@Rule public final TemporaryFolder outputDir = new TemporaryFolder();
|
@Rule public final TemporaryFolder outputDir = new TemporaryFolder();
|
||||||
|
|
||||||
private final Context context = ApplicationProvider.getApplicationContext();
|
private final Context context = ApplicationProvider.getApplicationContext();
|
||||||
|
|
||||||
@Before
|
@Rule
|
||||||
public void setUp() {
|
public ShadowMediaCodecConfig shadowMediaCodecConfig =
|
||||||
addAudioDecoders(MimeTypes.AUDIO_RAW);
|
ShadowMediaCodecConfig.withCodecs(
|
||||||
addAudioEncoders(MimeTypes.AUDIO_AAC);
|
/* decoders= */ ImmutableList.of(CODEC_INFO_RAW), /* encoders= */ ImmutableList.of());
|
||||||
}
|
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void tearDown() {
|
public void tearDown() {
|
||||||
removeEncodersAndDecoders();
|
// TODO(b/406463016): Investigate moving this call to ShadowMediaCodecConfig#after() method.
|
||||||
|
EncoderUtil.clearCachedEncoders();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void start_gapOnlyExport_outputsSilence() throws Exception {
|
public void start_gapOnlyExport_outputsSilence() throws Exception {
|
||||||
|
shadowMediaCodecConfig.addEncoders(CODEC_INFO_AAC);
|
||||||
CapturingMuxer.Factory muxerFactory = new CapturingMuxer.Factory(/* handleAudioAsPcm= */ true);
|
CapturingMuxer.Factory muxerFactory = new CapturingMuxer.Factory(/* handleAudioAsPcm= */ true);
|
||||||
Transformer transformer =
|
Transformer transformer =
|
||||||
new TestTransformerBuilder(context).setMuxerFactory(muxerFactory).build();
|
new TestTransformerBuilder(context).setMuxerFactory(muxerFactory).build();
|
||||||
@ -275,6 +293,7 @@ public final class MediaItemExportTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void start_trimOptimizationEnabled_fileNotMp4_fallbackToNormalExport() throws Exception {
|
public void start_trimOptimizationEnabled_fileNotMp4_fallbackToNormalExport() throws Exception {
|
||||||
|
shadowMediaCodecConfig.addEncoders(CODEC_INFO_AAC);
|
||||||
CapturingMuxer.Factory muxerFactory = new CapturingMuxer.Factory(/* handleAudioAsPcm= */ true);
|
CapturingMuxer.Factory muxerFactory = new CapturingMuxer.Factory(/* handleAudioAsPcm= */ true);
|
||||||
Transformer transformer =
|
Transformer transformer =
|
||||||
new TestTransformerBuilder(context)
|
new TestTransformerBuilder(context)
|
||||||
@ -441,6 +460,7 @@ public final class MediaItemExportTest {
|
|||||||
@Test
|
@Test
|
||||||
public void start_forceAudioTrackAndRemoveAudioWithEffects_generatesSilentAudio()
|
public void start_forceAudioTrackAndRemoveAudioWithEffects_generatesSilentAudio()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
|
shadowMediaCodecConfig.addEncoders(CODEC_INFO_AAC);
|
||||||
CapturingMuxer.Factory muxerFactory = new CapturingMuxer.Factory(/* handleAudioAsPcm= */ true);
|
CapturingMuxer.Factory muxerFactory = new CapturingMuxer.Factory(/* handleAudioAsPcm= */ true);
|
||||||
Transformer transformer =
|
Transformer transformer =
|
||||||
new TestTransformerBuilder(context).setMuxerFactory(muxerFactory).build();
|
new TestTransformerBuilder(context).setMuxerFactory(muxerFactory).build();
|
||||||
@ -497,6 +517,7 @@ public final class MediaItemExportTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void start_forceAudioTrackOnVideoOnly_generatesSilentAudio() throws Exception {
|
public void start_forceAudioTrackOnVideoOnly_generatesSilentAudio() throws Exception {
|
||||||
|
shadowMediaCodecConfig.addEncoders(CODEC_INFO_AAC);
|
||||||
CapturingMuxer.Factory muxerFactory = new CapturingMuxer.Factory(/* handleAudioAsPcm= */ true);
|
CapturingMuxer.Factory muxerFactory = new CapturingMuxer.Factory(/* handleAudioAsPcm= */ true);
|
||||||
Transformer transformer =
|
Transformer transformer =
|
||||||
new TestTransformerBuilder(context).setMuxerFactory(muxerFactory).build();
|
new TestTransformerBuilder(context).setMuxerFactory(muxerFactory).build();
|
||||||
@ -521,6 +542,7 @@ public final class MediaItemExportTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void exportAudio_muxerReceivesExpectedNumberOfBytes() throws Exception {
|
public void exportAudio_muxerReceivesExpectedNumberOfBytes() throws Exception {
|
||||||
|
shadowMediaCodecConfig.addEncoders(CODEC_INFO_AAC);
|
||||||
CapturingMuxer.Factory muxerFactory = new CapturingMuxer.Factory(/* handleAudioAsPcm= */ true);
|
CapturingMuxer.Factory muxerFactory = new CapturingMuxer.Factory(/* handleAudioAsPcm= */ true);
|
||||||
AtomicInteger bytesSeenByEffect = new AtomicInteger();
|
AtomicInteger bytesSeenByEffect = new AtomicInteger();
|
||||||
Transformer transformer =
|
Transformer transformer =
|
||||||
@ -540,6 +562,7 @@ public final class MediaItemExportTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void start_adjustSampleRate_completesSuccessfully() throws Exception {
|
public void start_adjustSampleRate_completesSuccessfully() throws Exception {
|
||||||
|
shadowMediaCodecConfig.addEncoders(CODEC_INFO_AAC);
|
||||||
CapturingMuxer.Factory muxerFactory = new CapturingMuxer.Factory(/* handleAudioAsPcm= */ true);
|
CapturingMuxer.Factory muxerFactory = new CapturingMuxer.Factory(/* handleAudioAsPcm= */ true);
|
||||||
SonicAudioProcessor sonicAudioProcessor = new SonicAudioProcessor();
|
SonicAudioProcessor sonicAudioProcessor = new SonicAudioProcessor();
|
||||||
sonicAudioProcessor.setOutputSampleRateHz(48000);
|
sonicAudioProcessor.setOutputSampleRateHz(48000);
|
||||||
@ -569,6 +592,7 @@ public final class MediaItemExportTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void adjustAudioSpeed_toDoubleSpeed_returnsExpectedNumberOfSamples() throws Exception {
|
public void adjustAudioSpeed_toDoubleSpeed_returnsExpectedNumberOfSamples() throws Exception {
|
||||||
|
shadowMediaCodecConfig.addEncoders(CODEC_INFO_AAC);
|
||||||
CapturingMuxer.Factory muxerFactory = new CapturingMuxer.Factory(/* handleAudioAsPcm= */ true);
|
CapturingMuxer.Factory muxerFactory = new CapturingMuxer.Factory(/* handleAudioAsPcm= */ true);
|
||||||
SonicAudioProcessor sonicAudioProcessor = new SonicAudioProcessor();
|
SonicAudioProcessor sonicAudioProcessor = new SonicAudioProcessor();
|
||||||
sonicAudioProcessor.setSpeed(2f);
|
sonicAudioProcessor.setSpeed(2f);
|
||||||
@ -599,6 +623,7 @@ public final class MediaItemExportTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void start_withRawBigEndianAudioInput_completesSuccessfully() throws Exception {
|
public void start_withRawBigEndianAudioInput_completesSuccessfully() throws Exception {
|
||||||
|
shadowMediaCodecConfig.addEncoders(CODEC_INFO_AAC);
|
||||||
CapturingMuxer.Factory muxerFactory = new CapturingMuxer.Factory(/* handleAudioAsPcm= */ true);
|
CapturingMuxer.Factory muxerFactory = new CapturingMuxer.Factory(/* handleAudioAsPcm= */ true);
|
||||||
ToInt16PcmAudioProcessor toInt16PcmAudioProcessor = new ToInt16PcmAudioProcessor();
|
ToInt16PcmAudioProcessor toInt16PcmAudioProcessor = new ToInt16PcmAudioProcessor();
|
||||||
Transformer transformer =
|
Transformer transformer =
|
||||||
@ -622,6 +647,7 @@ public final class MediaItemExportTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void start_singleMediaItemAndTransmux_ignoresTransmux() throws Exception {
|
public void start_singleMediaItemAndTransmux_ignoresTransmux() throws Exception {
|
||||||
|
shadowMediaCodecConfig.addEncoders(CODEC_INFO_AAC);
|
||||||
CapturingMuxer.Factory muxerFactory = new CapturingMuxer.Factory(/* handleAudioAsPcm= */ true);
|
CapturingMuxer.Factory muxerFactory = new CapturingMuxer.Factory(/* handleAudioAsPcm= */ true);
|
||||||
SonicAudioProcessor sonicAudioProcessor = new SonicAudioProcessor();
|
SonicAudioProcessor sonicAudioProcessor = new SonicAudioProcessor();
|
||||||
sonicAudioProcessor.setOutputSampleRateHz(48000);
|
sonicAudioProcessor.setOutputSampleRateHz(48000);
|
||||||
@ -702,6 +728,7 @@ public final class MediaItemExportTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void start_withMultipleListeners_callsEachOnFallback() throws Exception {
|
public void start_withMultipleListeners_callsEachOnFallback() throws Exception {
|
||||||
|
shadowMediaCodecConfig.addEncoders(CODEC_INFO_AAC);
|
||||||
CapturingMuxer.Factory muxerFactory = new CapturingMuxer.Factory(/* handleAudioAsPcm= */ false);
|
CapturingMuxer.Factory muxerFactory = new CapturingMuxer.Factory(/* handleAudioAsPcm= */ false);
|
||||||
ArgumentCaptor<Composition> compositionArgumentCaptor =
|
ArgumentCaptor<Composition> compositionArgumentCaptor =
|
||||||
ArgumentCaptor.forClass(Composition.class);
|
ArgumentCaptor.forClass(Composition.class);
|
||||||
@ -804,29 +831,11 @@ public final class MediaItemExportTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void start_whenCodecFailsToConfigure_completesWithError() throws Exception {
|
public void start_whenCodecFailsToConfigure_completesWithError() throws Exception {
|
||||||
CapturingMuxer.Factory muxerFactory = new CapturingMuxer.Factory(/* handleAudioAsPcm= */ false);
|
shadowMediaCodecConfig.addEncoders(CODEC_INFO_AAC);
|
||||||
String expectedFailureMessage = "Format not valid. AMR NB (3gpp)";
|
|
||||||
ShadowMediaCodec.CodecConfig throwOnConfigureCodecConfig =
|
|
||||||
new ShadowMediaCodec.CodecConfig(
|
|
||||||
/* inputBufferSize= */ 100_000,
|
|
||||||
/* outputBufferSize= */ 100_000,
|
|
||||||
/* codec= */ new ShadowMediaCodec.CodecConfig.Codec() {
|
|
||||||
@Override
|
|
||||||
public void process(ByteBuffer in, ByteBuffer out) {
|
|
||||||
out.put(in);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onConfigured(
|
|
||||||
MediaFormat format, Surface surface, MediaCrypto crypto, int flags) {
|
|
||||||
// MediaCodec#configure documented to throw IAE if format is invalid.
|
|
||||||
throw new IllegalArgumentException(expectedFailureMessage);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add the AMR_NB encoder that throws when configured.
|
// Add the AMR_NB encoder that throws when configured.
|
||||||
addAudioEncoders(throwOnConfigureCodecConfig, MimeTypes.AUDIO_AMR_NB);
|
shadowMediaCodecConfig.addCodec(
|
||||||
|
CODEC_INFO_AMR_NB, /* isEncoder= */ true, THROWING_CODEC_CONFIG);
|
||||||
|
CapturingMuxer.Factory muxerFactory = new CapturingMuxer.Factory(/* handleAudioAsPcm= */ false);
|
||||||
Transformer transformer =
|
Transformer transformer =
|
||||||
new TestTransformerBuilder(context)
|
new TestTransformerBuilder(context)
|
||||||
.setMuxerFactory(muxerFactory)
|
.setMuxerFactory(muxerFactory)
|
||||||
@ -841,7 +850,10 @@ public final class MediaItemExportTest {
|
|||||||
assertThat(exception.errorCode)
|
assertThat(exception.errorCode)
|
||||||
.isEqualTo(ExportException.ERROR_CODE_ENCODING_FORMAT_UNSUPPORTED);
|
.isEqualTo(ExportException.ERROR_CODE_ENCODING_FORMAT_UNSUPPORTED);
|
||||||
assertThat(exception).hasCauseThat().isInstanceOf(IllegalArgumentException.class);
|
assertThat(exception).hasCauseThat().isInstanceOf(IllegalArgumentException.class);
|
||||||
assertThat(exception).hasCauseThat().hasMessageThat().isEqualTo(expectedFailureMessage);
|
assertThat(exception)
|
||||||
|
.hasCauseThat()
|
||||||
|
.hasMessageThat()
|
||||||
|
.isEqualTo(EXPECTED_CODEC_EXCEPTION_MESSAGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -866,11 +878,9 @@ public final class MediaItemExportTest {
|
|||||||
public void
|
public void
|
||||||
start_withAudioFormatUnsupportedByMuxer_ignoresDisabledFallbackAndCompletesSuccessfully()
|
start_withAudioFormatUnsupportedByMuxer_ignoresDisabledFallbackAndCompletesSuccessfully()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
removeEncodersAndDecoders();
|
|
||||||
addAudioDecoders(MimeTypes.AUDIO_RAW);
|
|
||||||
// RAW supported by encoder, unsupported by muxer.
|
// RAW supported by encoder, unsupported by muxer.
|
||||||
// AAC supported by encoder and muxer.
|
// AAC supported by encoder and muxer.
|
||||||
addAudioEncoders(MimeTypes.AUDIO_RAW, MimeTypes.AUDIO_AAC);
|
shadowMediaCodecConfig.addEncoders(CODEC_INFO_RAW, CODEC_INFO_AAC);
|
||||||
|
|
||||||
CapturingMuxer.Factory muxerFactory = new CapturingMuxer.Factory(/* handleAudioAsPcm= */ true);
|
CapturingMuxer.Factory muxerFactory = new CapturingMuxer.Factory(/* handleAudioAsPcm= */ true);
|
||||||
Transformer.Listener mockListener = mock(Transformer.Listener.class);
|
Transformer.Listener mockListener = mock(Transformer.Listener.class);
|
||||||
@ -901,11 +911,9 @@ public final class MediaItemExportTest {
|
|||||||
@Test
|
@Test
|
||||||
public void start_withAudioFormatUnsupportedByMuxer_fallsBackAndCompletesSuccessfully()
|
public void start_withAudioFormatUnsupportedByMuxer_fallsBackAndCompletesSuccessfully()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
removeEncodersAndDecoders();
|
|
||||||
addAudioDecoders(MimeTypes.AUDIO_RAW);
|
|
||||||
// RAW supported by encoder, unsupported by muxer.
|
// RAW supported by encoder, unsupported by muxer.
|
||||||
// AAC supported by encoder and muxer.
|
// AAC supported by encoder and muxer.
|
||||||
addAudioEncoders(MimeTypes.AUDIO_RAW, MimeTypes.AUDIO_AAC);
|
shadowMediaCodecConfig.addEncoders(CODEC_INFO_RAW, CODEC_INFO_AAC);
|
||||||
|
|
||||||
CapturingMuxer.Factory muxerFactory = new CapturingMuxer.Factory(/* handleAudioAsPcm= */ true);
|
CapturingMuxer.Factory muxerFactory = new CapturingMuxer.Factory(/* handleAudioAsPcm= */ true);
|
||||||
Transformer.Listener mockListener = mock(Transformer.Listener.class);
|
Transformer.Listener mockListener = mock(Transformer.Listener.class);
|
||||||
@ -1198,9 +1206,7 @@ public final class MediaItemExportTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void analyze_audioOnlyWithItemEffect_completesSuccessfully() throws Exception {
|
public void analyze_audioOnlyWithItemEffect_completesSuccessfully() throws Exception {
|
||||||
removeEncodersAndDecoders();
|
shadowMediaCodecConfig.addCodec(CODEC_INFO_AAC, /* isEncoder= */ true, THROWING_CODEC_CONFIG);
|
||||||
addAudioDecoders(MimeTypes.AUDIO_RAW);
|
|
||||||
addThrowingAudioEncoder(MimeTypes.AUDIO_AAC);
|
|
||||||
Transformer transformer =
|
Transformer transformer =
|
||||||
ExperimentalAnalyzerModeFactory.buildAnalyzer(
|
ExperimentalAnalyzerModeFactory.buildAnalyzer(
|
||||||
getApplicationContext(), new TestTransformerBuilder(getApplicationContext()).build());
|
getApplicationContext(), new TestTransformerBuilder(getApplicationContext()).build());
|
||||||
@ -1220,9 +1226,7 @@ public final class MediaItemExportTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void analyze_audioOnlyWithCompositionEffect_completesSuccessfully() throws Exception {
|
public void analyze_audioOnlyWithCompositionEffect_completesSuccessfully() throws Exception {
|
||||||
removeEncodersAndDecoders();
|
shadowMediaCodecConfig.addCodec(CODEC_INFO_AAC, /* isEncoder= */ true, THROWING_CODEC_CONFIG);
|
||||||
addAudioDecoders(MimeTypes.AUDIO_RAW);
|
|
||||||
addThrowingAudioEncoder(MimeTypes.AUDIO_AAC);
|
|
||||||
Transformer transformer =
|
Transformer transformer =
|
||||||
ExperimentalAnalyzerModeFactory.buildAnalyzer(
|
ExperimentalAnalyzerModeFactory.buildAnalyzer(
|
||||||
getApplicationContext(), new TestTransformerBuilder(getApplicationContext()).build());
|
getApplicationContext(), new TestTransformerBuilder(getApplicationContext()).build());
|
||||||
@ -1247,9 +1251,7 @@ public final class MediaItemExportTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void analyze_audioOnly_itemAndMixerOutputMatch() throws Exception {
|
public void analyze_audioOnly_itemAndMixerOutputMatch() throws Exception {
|
||||||
removeEncodersAndDecoders();
|
shadowMediaCodecConfig.addCodec(CODEC_INFO_AAC, /* isEncoder= */ true, THROWING_CODEC_CONFIG);
|
||||||
addAudioDecoders(MimeTypes.AUDIO_RAW);
|
|
||||||
addThrowingAudioEncoder(MimeTypes.AUDIO_AAC);
|
|
||||||
Transformer transformer =
|
Transformer transformer =
|
||||||
ExperimentalAnalyzerModeFactory.buildAnalyzer(
|
ExperimentalAnalyzerModeFactory.buildAnalyzer(
|
||||||
getApplicationContext(), new TestTransformerBuilder(getApplicationContext()).build());
|
getApplicationContext(), new TestTransformerBuilder(getApplicationContext()).build());
|
||||||
@ -1567,27 +1569,6 @@ public final class MediaItemExportTest {
|
|||||||
/* modifications...= */ "transmuxed"));
|
/* modifications...= */ "transmuxed"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void addThrowingAudioEncoder(String mimeType) {
|
|
||||||
ShadowMediaCodec.CodecConfig.Codec codec =
|
|
||||||
new ShadowMediaCodec.CodecConfig.Codec() {
|
|
||||||
@Override
|
|
||||||
public void process(ByteBuffer byteBuffer, ByteBuffer byteBuffer1) {
|
|
||||||
throw new IllegalStateException();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onConfigured(
|
|
||||||
MediaFormat format, Surface surface, MediaCrypto crypto, int flags) {
|
|
||||||
throw new IllegalStateException();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
addAudioEncoders(
|
|
||||||
new ShadowMediaCodec.CodecConfig(
|
|
||||||
/* inputBufferSize= */ 100_000, /* outputBufferSize= */ 100_000, codec),
|
|
||||||
mimeType);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static AudioProcessor createByteCountingAudioProcessor(AtomicInteger byteCount) {
|
private static AudioProcessor createByteCountingAudioProcessor(AtomicInteger byteCount) {
|
||||||
return new TeeAudioProcessor(
|
return new TeeAudioProcessor(
|
||||||
new TeeAudioProcessor.AudioBufferSink() {
|
new TeeAudioProcessor.AudioBufferSink() {
|
||||||
|
@ -16,24 +16,23 @@
|
|||||||
|
|
||||||
package androidx.media3.transformer;
|
package androidx.media3.transformer;
|
||||||
|
|
||||||
|
import static androidx.media3.test.utils.robolectric.ShadowMediaCodecConfig.CODEC_INFO_AAC;
|
||||||
|
import static androidx.media3.test.utils.robolectric.ShadowMediaCodecConfig.CODEC_INFO_RAW;
|
||||||
import static androidx.media3.transformer.TestUtil.ASSET_URI_PREFIX;
|
import static androidx.media3.transformer.TestUtil.ASSET_URI_PREFIX;
|
||||||
import static androidx.media3.transformer.TestUtil.FILE_AUDIO_RAW;
|
import static androidx.media3.transformer.TestUtil.FILE_AUDIO_RAW;
|
||||||
import static androidx.media3.transformer.TestUtil.FILE_AUDIO_RAW_VIDEO;
|
import static androidx.media3.transformer.TestUtil.FILE_AUDIO_RAW_VIDEO;
|
||||||
import static androidx.media3.transformer.TestUtil.addAudioDecoders;
|
|
||||||
import static androidx.media3.transformer.TestUtil.addAudioEncoders;
|
|
||||||
import static androidx.media3.transformer.TestUtil.createAudioEffects;
|
import static androidx.media3.transformer.TestUtil.createAudioEffects;
|
||||||
import static androidx.media3.transformer.TestUtil.createPitchChangingAudioProcessor;
|
import static androidx.media3.transformer.TestUtil.createPitchChangingAudioProcessor;
|
||||||
import static androidx.media3.transformer.TestUtil.getSequenceDumpFilePath;
|
import static androidx.media3.transformer.TestUtil.getSequenceDumpFilePath;
|
||||||
import static androidx.media3.transformer.TestUtil.removeEncodersAndDecoders;
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
import static java.util.stream.Collectors.toList;
|
import static java.util.stream.Collectors.toList;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import androidx.media3.common.MediaItem;
|
import androidx.media3.common.MediaItem;
|
||||||
import androidx.media3.common.MimeTypes;
|
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
import androidx.media3.test.utils.DumpFileAsserts;
|
import androidx.media3.test.utils.DumpFileAsserts;
|
||||||
import androidx.media3.test.utils.TestTransformerBuilder;
|
import androidx.media3.test.utils.TestTransformerBuilder;
|
||||||
|
import androidx.media3.test.utils.robolectric.ShadowMediaCodecConfig;
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
import com.google.common.collect.Collections2;
|
import com.google.common.collect.Collections2;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
@ -46,7 +45,6 @@ import java.util.List;
|
|||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.rules.TemporaryFolder;
|
import org.junit.rules.TemporaryFolder;
|
||||||
@ -126,6 +124,12 @@ public final class ParameterizedAudioExportTest {
|
|||||||
|
|
||||||
@Rule public final TemporaryFolder outputDir = new TemporaryFolder();
|
@Rule public final TemporaryFolder outputDir = new TemporaryFolder();
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public ShadowMediaCodecConfig shadowMediaCodecConfig =
|
||||||
|
ShadowMediaCodecConfig.withCodecs(
|
||||||
|
/* decoders= */ ImmutableList.of(CODEC_INFO_RAW),
|
||||||
|
/* encoders= */ ImmutableList.of(CODEC_INFO_AAC));
|
||||||
|
|
||||||
@Parameter public SequenceConfig sequence;
|
@Parameter public SequenceConfig sequence;
|
||||||
|
|
||||||
private final Context context = ApplicationProvider.getApplicationContext();
|
private final Context context = ApplicationProvider.getApplicationContext();
|
||||||
@ -133,15 +137,10 @@ public final class ParameterizedAudioExportTest {
|
|||||||
private final CapturingMuxer.Factory muxerFactory =
|
private final CapturingMuxer.Factory muxerFactory =
|
||||||
new CapturingMuxer.Factory(/* handleAudioAsPcm= */ true);
|
new CapturingMuxer.Factory(/* handleAudioAsPcm= */ true);
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
addAudioDecoders(MimeTypes.AUDIO_RAW);
|
|
||||||
addAudioEncoders(MimeTypes.AUDIO_AAC);
|
|
||||||
}
|
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void tearDown() {
|
public void tearDown() {
|
||||||
removeEncodersAndDecoders();
|
// TODO(b/406463016): Investigate moving this call to ShadowMediaCodecConfig#after() method.
|
||||||
|
EncoderUtil.clearCachedEncoders();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package androidx.media3.transformer;
|
package androidx.media3.transformer;
|
||||||
|
|
||||||
|
import static androidx.media3.test.utils.robolectric.ShadowMediaCodecConfig.CODEC_INFO_AAC;
|
||||||
|
import static androidx.media3.test.utils.robolectric.ShadowMediaCodecConfig.CODEC_INFO_RAW;
|
||||||
import static androidx.media3.transformer.TestUtil.ASSET_URI_PREFIX;
|
import static androidx.media3.transformer.TestUtil.ASSET_URI_PREFIX;
|
||||||
import static androidx.media3.transformer.TestUtil.FILE_AUDIO_AMR_NB;
|
import static androidx.media3.transformer.TestUtil.FILE_AUDIO_AMR_NB;
|
||||||
import static androidx.media3.transformer.TestUtil.FILE_AUDIO_RAW;
|
import static androidx.media3.transformer.TestUtil.FILE_AUDIO_RAW;
|
||||||
@ -23,24 +25,20 @@ import static androidx.media3.transformer.TestUtil.FILE_AUDIO_RAW_STEREO_48000KH
|
|||||||
import static androidx.media3.transformer.TestUtil.FILE_AUDIO_RAW_VIDEO;
|
import static androidx.media3.transformer.TestUtil.FILE_AUDIO_RAW_VIDEO;
|
||||||
import static androidx.media3.transformer.TestUtil.FILE_AUDIO_VIDEO;
|
import static androidx.media3.transformer.TestUtil.FILE_AUDIO_VIDEO;
|
||||||
import static androidx.media3.transformer.TestUtil.FILE_VIDEO_ONLY;
|
import static androidx.media3.transformer.TestUtil.FILE_VIDEO_ONLY;
|
||||||
import static androidx.media3.transformer.TestUtil.addAudioDecoders;
|
|
||||||
import static androidx.media3.transformer.TestUtil.addAudioEncoders;
|
|
||||||
import static androidx.media3.transformer.TestUtil.createAudioEffects;
|
import static androidx.media3.transformer.TestUtil.createAudioEffects;
|
||||||
import static androidx.media3.transformer.TestUtil.createVolumeScalingAudioProcessor;
|
import static androidx.media3.transformer.TestUtil.createVolumeScalingAudioProcessor;
|
||||||
import static androidx.media3.transformer.TestUtil.getDumpFileName;
|
import static androidx.media3.transformer.TestUtil.getDumpFileName;
|
||||||
import static androidx.media3.transformer.TestUtil.removeEncodersAndDecoders;
|
|
||||||
import static org.junit.Assume.assumeFalse;
|
import static org.junit.Assume.assumeFalse;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import androidx.media3.common.MediaItem;
|
import androidx.media3.common.MediaItem;
|
||||||
import androidx.media3.common.MimeTypes;
|
|
||||||
import androidx.media3.test.utils.DumpFileAsserts;
|
import androidx.media3.test.utils.DumpFileAsserts;
|
||||||
import androidx.media3.test.utils.TestTransformerBuilder;
|
import androidx.media3.test.utils.TestTransformerBuilder;
|
||||||
|
import androidx.media3.test.utils.robolectric.ShadowMediaCodecConfig;
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.rules.TemporaryFolder;
|
import org.junit.rules.TemporaryFolder;
|
||||||
@ -93,17 +91,18 @@ public final class ParameterizedItemExportTest {
|
|||||||
|
|
||||||
private final Context context = ApplicationProvider.getApplicationContext();
|
private final Context context = ApplicationProvider.getApplicationContext();
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
// Only add RAW decoder, so non-RAW audio has no options for decoding.
|
// Only add RAW decoder, so non-RAW audio has no options for decoding.
|
||||||
addAudioDecoders(MimeTypes.AUDIO_RAW);
|
|
||||||
// Use an AAC encoder because muxer supports AAC.
|
// Use an AAC encoder because muxer supports AAC.
|
||||||
addAudioEncoders(MimeTypes.AUDIO_AAC);
|
@Rule
|
||||||
}
|
public ShadowMediaCodecConfig shadowMediaCodecConfig =
|
||||||
|
ShadowMediaCodecConfig.withCodecs(
|
||||||
|
/* decoders= */ ImmutableList.of(CODEC_INFO_RAW),
|
||||||
|
/* encoders= */ ImmutableList.of(CODEC_INFO_AAC));
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void tearDown() {
|
public void tearDown() {
|
||||||
removeEncodersAndDecoders();
|
// TODO(b/406463016): Investigate moving this call to ShadowMediaCodecConfig#after() method.
|
||||||
|
EncoderUtil.clearCachedEncoders();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -16,36 +16,34 @@
|
|||||||
|
|
||||||
package androidx.media3.transformer;
|
package androidx.media3.transformer;
|
||||||
|
|
||||||
|
import static androidx.media3.test.utils.robolectric.ShadowMediaCodecConfig.CODEC_INFO_AAC;
|
||||||
|
import static androidx.media3.test.utils.robolectric.ShadowMediaCodecConfig.CODEC_INFO_RAW;
|
||||||
import static androidx.media3.transformer.TestUtil.ASSET_URI_PREFIX;
|
import static androidx.media3.transformer.TestUtil.ASSET_URI_PREFIX;
|
||||||
import static androidx.media3.transformer.TestUtil.FILE_AUDIO_RAW;
|
import static androidx.media3.transformer.TestUtil.FILE_AUDIO_RAW;
|
||||||
import static androidx.media3.transformer.TestUtil.FILE_AUDIO_RAW_STEREO_48000KHZ;
|
import static androidx.media3.transformer.TestUtil.FILE_AUDIO_RAW_STEREO_48000KHZ;
|
||||||
import static androidx.media3.transformer.TestUtil.FILE_AUDIO_RAW_VIDEO;
|
import static androidx.media3.transformer.TestUtil.FILE_AUDIO_RAW_VIDEO;
|
||||||
import static androidx.media3.transformer.TestUtil.FILE_AUDIO_VIDEO;
|
import static androidx.media3.transformer.TestUtil.FILE_AUDIO_VIDEO;
|
||||||
import static androidx.media3.transformer.TestUtil.FILE_AUDIO_VIDEO_INCREASING_TIMESTAMPS_15S;
|
import static androidx.media3.transformer.TestUtil.FILE_AUDIO_VIDEO_INCREASING_TIMESTAMPS_15S;
|
||||||
import static androidx.media3.transformer.TestUtil.addAudioDecoders;
|
|
||||||
import static androidx.media3.transformer.TestUtil.addAudioEncoders;
|
|
||||||
import static androidx.media3.transformer.TestUtil.createAudioEffects;
|
import static androidx.media3.transformer.TestUtil.createAudioEffects;
|
||||||
import static androidx.media3.transformer.TestUtil.createChannelCountChangingAudioProcessor;
|
import static androidx.media3.transformer.TestUtil.createChannelCountChangingAudioProcessor;
|
||||||
import static androidx.media3.transformer.TestUtil.createPitchChangingAudioProcessor;
|
import static androidx.media3.transformer.TestUtil.createPitchChangingAudioProcessor;
|
||||||
import static androidx.media3.transformer.TestUtil.getDumpFileName;
|
import static androidx.media3.transformer.TestUtil.getDumpFileName;
|
||||||
import static androidx.media3.transformer.TestUtil.getSequenceDumpFilePath;
|
import static androidx.media3.transformer.TestUtil.getSequenceDumpFilePath;
|
||||||
import static androidx.media3.transformer.TestUtil.removeEncodersAndDecoders;
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
import static org.junit.Assert.assertThrows;
|
import static org.junit.Assert.assertThrows;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.media3.common.MediaItem;
|
import androidx.media3.common.MediaItem;
|
||||||
import androidx.media3.common.MimeTypes;
|
|
||||||
import androidx.media3.common.audio.SonicAudioProcessor;
|
import androidx.media3.common.audio.SonicAudioProcessor;
|
||||||
import androidx.media3.effect.RgbFilter;
|
import androidx.media3.effect.RgbFilter;
|
||||||
import androidx.media3.test.utils.DumpFileAsserts;
|
import androidx.media3.test.utils.DumpFileAsserts;
|
||||||
import androidx.media3.test.utils.TestTransformerBuilder;
|
import androidx.media3.test.utils.TestTransformerBuilder;
|
||||||
|
import androidx.media3.test.utils.robolectric.ShadowMediaCodecConfig;
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Ignore;
|
import org.junit.Ignore;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@ -69,15 +67,16 @@ public final class SequenceExportTest {
|
|||||||
|
|
||||||
private final Context context = ApplicationProvider.getApplicationContext();
|
private final Context context = ApplicationProvider.getApplicationContext();
|
||||||
|
|
||||||
@Before
|
@Rule
|
||||||
public void setUp() {
|
public ShadowMediaCodecConfig shadowMediaCodecConfig =
|
||||||
addAudioDecoders(MimeTypes.AUDIO_RAW);
|
ShadowMediaCodecConfig.withCodecs(
|
||||||
addAudioEncoders(MimeTypes.AUDIO_AAC);
|
/* decoders= */ ImmutableList.of(CODEC_INFO_RAW),
|
||||||
}
|
/* encoders= */ ImmutableList.of(CODEC_INFO_AAC));
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void tearDown() {
|
public void tearDown() {
|
||||||
removeEncodersAndDecoders();
|
// TODO(b/406463016): Investigate moving this call to ShadowMediaCodecConfig#after() method.
|
||||||
|
EncoderUtil.clearCachedEncoders();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -15,20 +15,14 @@
|
|||||||
*/
|
*/
|
||||||
package androidx.media3.transformer;
|
package androidx.media3.transformer;
|
||||||
|
|
||||||
import static androidx.media3.test.utils.robolectric.ShadowMediaCodecConfig.configureShadowMediaCodec;
|
|
||||||
|
|
||||||
import androidx.media3.common.MimeTypes;
|
|
||||||
import androidx.media3.common.audio.AudioProcessor;
|
import androidx.media3.common.audio.AudioProcessor;
|
||||||
import androidx.media3.common.audio.ChannelMixingAudioProcessor;
|
import androidx.media3.common.audio.ChannelMixingAudioProcessor;
|
||||||
import androidx.media3.common.audio.ChannelMixingMatrix;
|
import androidx.media3.common.audio.ChannelMixingMatrix;
|
||||||
import androidx.media3.common.audio.SonicAudioProcessor;
|
import androidx.media3.common.audio.SonicAudioProcessor;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.common.util.Util;
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.StringJoiner;
|
import java.util.StringJoiner;
|
||||||
import org.robolectric.shadows.ShadowMediaCodec;
|
|
||||||
import org.robolectric.shadows.ShadowMediaCodecList;
|
|
||||||
|
|
||||||
/** Utility class for {@link Transformer} unit tests */
|
/** Utility class for {@link Transformer} unit tests */
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
@ -142,83 +136,4 @@ public final class TestUtil {
|
|||||||
+ "."
|
+ "."
|
||||||
+ DUMP_FILE_EXTENSION;
|
+ DUMP_FILE_EXTENSION;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds an audio decoder for each {@linkplain MimeTypes mime type}.
|
|
||||||
*
|
|
||||||
* <p>Input buffers are copied directly to the output.
|
|
||||||
*
|
|
||||||
* <p>When adding codecs, {@link #removeEncodersAndDecoders()} should be called in the test class
|
|
||||||
* {@link org.junit.After @After} method.
|
|
||||||
*/
|
|
||||||
public static void addAudioDecoders(String... mimeTypes) {
|
|
||||||
for (String mimeType : mimeTypes) {
|
|
||||||
addCodec(
|
|
||||||
mimeType,
|
|
||||||
new ShadowMediaCodec.CodecConfig(
|
|
||||||
/* inputBufferSize= */ 150_000,
|
|
||||||
/* outputBufferSize= */ 150_000,
|
|
||||||
/* codec= */ (in, out) -> out.put(in)),
|
|
||||||
/* colorFormats= */ ImmutableList.of(),
|
|
||||||
/* isDecoder= */ true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds an audio encoder for each {@linkplain MimeTypes mime type}.
|
|
||||||
*
|
|
||||||
* <p>Input buffers are copied directly to the output.
|
|
||||||
*
|
|
||||||
* <p>When adding codecs, {@link #removeEncodersAndDecoders()} should be called in the test class
|
|
||||||
* {@link org.junit.After @After} method.
|
|
||||||
*/
|
|
||||||
public static void addAudioEncoders(String... mimeTypes) {
|
|
||||||
addAudioEncoders(
|
|
||||||
new ShadowMediaCodec.CodecConfig(
|
|
||||||
/* inputBufferSize= */ 150_000,
|
|
||||||
/* outputBufferSize= */ 150_000,
|
|
||||||
/* codec= */ (in, out) -> out.put(in)),
|
|
||||||
mimeTypes);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds an audio encoder for each {@linkplain MimeTypes mime type}.
|
|
||||||
*
|
|
||||||
* <p>Input buffers are handled according to the {@link
|
|
||||||
* org.robolectric.shadows.ShadowMediaCodec.CodecConfig} provided.
|
|
||||||
*
|
|
||||||
* <p>When adding codecs, {@link #removeEncodersAndDecoders()} should be called in the test's
|
|
||||||
* {@link org.junit.After @After} method.
|
|
||||||
*/
|
|
||||||
public static void addAudioEncoders(
|
|
||||||
ShadowMediaCodec.CodecConfig codecConfig, String... mimeTypes) {
|
|
||||||
for (String mimeType : mimeTypes) {
|
|
||||||
addCodec(
|
|
||||||
mimeType, codecConfig, /* colorFormats= */ ImmutableList.of(), /* isDecoder= */ false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Clears all cached codecs. */
|
|
||||||
public static void removeEncodersAndDecoders() {
|
|
||||||
ShadowMediaCodec.clearCodecs();
|
|
||||||
ShadowMediaCodecList.reset();
|
|
||||||
EncoderUtil.clearCachedEncoders();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void addCodec(
|
|
||||||
String mimeType,
|
|
||||||
ShadowMediaCodec.CodecConfig codecConfig,
|
|
||||||
ImmutableList<Integer> colorFormats,
|
|
||||||
boolean isDecoder) {
|
|
||||||
String codecName =
|
|
||||||
Util.formatInvariant(
|
|
||||||
isDecoder ? "exo.%s.decoder" : "exo.%s.encoder", mimeType.replace('/', '-'));
|
|
||||||
configureShadowMediaCodec(
|
|
||||||
codecName,
|
|
||||||
mimeType,
|
|
||||||
!isDecoder,
|
|
||||||
/* profileLevels= */ ImmutableList.of(),
|
|
||||||
colorFormats,
|
|
||||||
codecConfig);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user