Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
SDK
/
exoplayer
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Settings
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
c566b774
authored
Dec 16, 2022
by
tofunmi
Committed by
Tianyi Feng
Dec 21, 2022
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Add TransformerTestBuilderFactory to make transformer testable by apps
PiperOrigin-RevId: 495821660
parent
4a3d693c
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
424 additions
and
243 deletions
library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Transformer.java
library/transformer/src/test/java/com/google/android/exoplayer2/transformer/TransformerEndToEndTest.java
robolectricutils/build.gradle
robolectricutils/src/main/java/com/google/android/exoplayer2/robolectric/ShadowMediaCodecConfig.java
testutils/build.gradle
library/transformer/src/test/java/com/google/android/exoplayer2/transformer/TestMuxer.java → testutils/src/main/java/com/google/android/exoplayer2/testutil/TestMuxer.java
testutils/src/main/java/com/google/android/exoplayer2/testutil/TestTransformerBuilderFactory.java
library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Transformer.java
View file @
c566b774
...
...
@@ -428,7 +428,7 @@ public final class Transformer {
*/
@CanIgnoreReturnValue
@VisibleForTesting
/* package */
Builder
setClock
(
Clock
clock
)
{
public
Builder
setClock
(
Clock
clock
)
{
this
.
clock
=
clock
;
this
.
listeners
=
listeners
.
copy
(
looper
,
clock
,
(
listener
,
flags
)
->
{});
return
this
;
...
...
library/transformer/src/test/java/com/google/android/exoplayer2/transformer/TransformerEndToEndTest.java
View file @
c566b774
...
...
@@ -29,15 +29,11 @@ import static org.mockito.Mockito.never;
import
static
org
.
mockito
.
Mockito
.
verify
;
import
android.content.Context
;
import
android.media.MediaCrypto
;
import
android.media.MediaFormat
;
import
android.net.Uri
;
import
android.os.Handler
;
import
android.os.HandlerThread
;
import
android.os.Looper
;
import
android.os.Message
;
import
android.os.ParcelFileDescriptor
;
import
android.view.Surface
;
import
androidx.annotation.Nullable
;
import
androidx.test.core.app.ApplicationProvider
;
import
androidx.test.ext.junit.runners.AndroidJUnit4
;
...
...
@@ -50,17 +46,16 @@ import com.google.android.exoplayer2.extractor.ExtractorInput;
import
com.google.android.exoplayer2.extractor.ExtractorOutput
;
import
com.google.android.exoplayer2.extractor.ExtractorsFactory
;
import
com.google.android.exoplayer2.extractor.PositionHolder
;
import
com.google.android.exoplayer2.robolectric.ShadowMediaCodecConfig
;
import
com.google.android.exoplayer2.source.DefaultMediaSourceFactory
;
import
com.google.android.exoplayer2.source.MediaSource
;
import
com.google.android.exoplayer2.testutil.DumpFileAsserts
;
import
com.google.android.exoplayer2.testutil.
FakeClock
;
import
com.google.android.exoplayer2.testutil.
TestTransformerBuilderFactory
;
import
com.google.android.exoplayer2.util.MimeTypes
;
import
com.google.android.exoplayer2.util.Util
;
import
com.google.common.collect.ImmutableList
;
import
com.google.common.collect.Iterables
;
import
com.google.common.primitives.Ints
;
import
java.io.IOException
;
import
java.nio.ByteBuffer
;
import
java.nio.file.Files
;
import
java.nio.file.Paths
;
import
java.util.ArrayList
;
...
...
@@ -74,16 +69,17 @@ import java.util.concurrent.atomic.AtomicReference;
import
org.checkerframework.checker.nullness.compatqual.NullableType
;
import
org.junit.After
;
import
org.junit.Before
;
import
org.junit.Rule
;
import
org.junit.Test
;
import
org.junit.runner.RunWith
;
import
org.robolectric.shadows.MediaCodecInfoBuilder
;
import
org.robolectric.shadows.ShadowMediaCodec
;
import
org.robolectric.shadows.ShadowMediaCodecList
;
/** End-to-end test for {@link Transformer}. */
@RunWith
(
AndroidJUnit4
.
class
)
public
final
class
TransformerEndToEndTest
{
@Rule
public
final
ShadowMediaCodecConfig
mediaCodecConfig
=
ShadowMediaCodecConfig
.
forTranscoding
();
private
static
final
String
ASSET_URI_PREFIX
=
"asset:///media/"
;
private
static
final
String
FILE_VIDEO_ONLY
=
"mp4/sample_18byte_nclx_colr.mp4"
;
private
static
final
String
FILE_AUDIO_VIDEO
=
"mp4/sample.mp4"
;
...
...
@@ -100,39 +96,39 @@ public final class TransformerEndToEndTest {
private
Context
context
;
private
String
outputPath
;
private
TestMuxer
testMuxer
;
private
FakeClock
clock
;
private
ProgressHolder
progressHolder
;
private
TestTransformerBuilderFactory
testTransformerBuilderFactory
;
@Before
public
void
setUp
()
throws
Exception
{
context
=
ApplicationProvider
.
getApplicationContext
();
outputPath
=
Util
.
createTempFile
(
context
,
"TransformerTest"
).
getPath
();
clock
=
new
FakeClock
(
/* isAutoAdvancing= */
true
);
progressHolder
=
new
ProgressHolder
();
createEncodersAndDecoders
(
);
testTransformerBuilderFactory
=
new
TestTransformerBuilderFactory
(
context
);
}
@After
public
void
tearDown
()
throws
Exception
{
Files
.
delete
(
Paths
.
get
(
outputPath
));
removeEncodersAndDecoders
();
}
@Test
public
void
startTransformation_videoOnlyPassthrough_completesSuccessfully
()
throws
Exception
{
Transformer
transformer
=
createTransformerBuilder
(
/* enableFallback= */
false
).
build
();
Transformer
transformer
=
testTransformerBuilderFactory
.
create
(
/* enableFallback= */
false
).
build
();
MediaItem
mediaItem
=
MediaItem
.
fromUri
(
ASSET_URI_PREFIX
+
FILE_VIDEO_ONLY
);
transformer
.
startTransformation
(
mediaItem
,
outputPath
);
TransformerTestRunner
.
runUntilCompleted
(
transformer
);
DumpFileAsserts
.
assertOutput
(
context
,
testMuxer
,
getDumpFileName
(
FILE_VIDEO_ONLY
));
DumpFileAsserts
.
assertOutput
(
context
,
testTransformerBuilderFactory
.
getTestMuxer
(),
getDumpFileName
(
FILE_VIDEO_ONLY
));
}
@Test
public
void
startTransformation_audioOnlyPassthrough_completesSuccessfully
()
throws
Exception
{
Transformer
transformer
=
createTransformerBuilder
(
/* enableFallback= */
false
).
build
();
Transformer
transformer
=
testTransformerBuilderFactory
.
create
(
/* enableFallback= */
false
).
build
();
MediaItem
mediaItem
=
MediaItem
.
fromUri
(
ASSET_URI_PREFIX
+
FILE_AUDIO_UNSUPPORTED_BY_ENCODER
);
...
...
@@ -140,13 +136,16 @@ public final class TransformerEndToEndTest {
TransformerTestRunner
.
runUntilCompleted
(
transformer
);
DumpFileAsserts
.
assertOutput
(
context
,
testMuxer
,
getDumpFileName
(
FILE_AUDIO_UNSUPPORTED_BY_ENCODER
));
context
,
testTransformerBuilderFactory
.
getTestMuxer
(),
getDumpFileName
(
FILE_AUDIO_UNSUPPORTED_BY_ENCODER
));
}
@Test
public
void
startTransformation_audioOnlyTranscoding_completesSuccessfully
()
throws
Exception
{
Transformer
transformer
=
createTransformerBuilder
(
/* enableFallback= */
false
)
testTransformerBuilderFactory
.
create
(
/* enableFallback= */
false
)
.
setTransformationRequest
(
new
TransformationRequest
.
Builder
()
.
setAudioMimeType
(
MimeTypes
.
AUDIO_AAC
)
// supported by encoder and muxer
...
...
@@ -158,24 +157,29 @@ public final class TransformerEndToEndTest {
TransformerTestRunner
.
runUntilCompleted
(
transformer
);
DumpFileAsserts
.
assertOutput
(
context
,
testMuxer
,
getDumpFileName
(
FILE_AUDIO_UNSUPPORTED_BY_ENCODER
+
".aac"
));
context
,
testTransformerBuilderFactory
.
getTestMuxer
(),
getDumpFileName
(
FILE_AUDIO_UNSUPPORTED_BY_ENCODER
+
".aac"
));
}
@Test
public
void
startTransformation_audioAndVideo_completesSuccessfully
()
throws
Exception
{
Transformer
transformer
=
createTransformerBuilder
(
/* enableFallback= */
false
).
build
();
Transformer
transformer
=
testTransformerBuilderFactory
.
create
(
/* enableFallback= */
false
).
build
();
MediaItem
mediaItem
=
MediaItem
.
fromUri
(
ASSET_URI_PREFIX
+
FILE_AUDIO_VIDEO
);
transformer
.
startTransformation
(
mediaItem
,
outputPath
);
TransformerTestRunner
.
runUntilCompleted
(
transformer
);
DumpFileAsserts
.
assertOutput
(
context
,
testMuxer
,
getDumpFileName
(
FILE_AUDIO_VIDEO
));
DumpFileAsserts
.
assertOutput
(
context
,
testTransformerBuilderFactory
.
getTestMuxer
(),
getDumpFileName
(
FILE_AUDIO_VIDEO
));
}
@Test
public
void
startTransformation_audioAndVideo_withClippingStartAtKeyFrame_completesSuccessfully
()
throws
Exception
{
Transformer
transformer
=
createTransformerBuilder
(
/* enableFallback= */
false
).
build
();
Transformer
transformer
=
testTransformerBuilderFactory
.
create
(
/* enableFallback= */
false
).
build
();
MediaItem
mediaItem
=
new
MediaItem
.
Builder
()
.
setUri
(
ASSET_URI_PREFIX
+
FILE_AUDIO_VIDEO_INCREASING_TIMESTAMPS_15S
)
...
...
@@ -192,14 +196,15 @@ public final class TransformerEndToEndTest {
DumpFileAsserts
.
assertOutput
(
context
,
test
Muxer
,
test
TransformerBuilderFactory
.
getTestMuxer
()
,
getDumpFileName
(
FILE_AUDIO_VIDEO_INCREASING_TIMESTAMPS_15S
+
".clipped"
));
}
@Test
public
void
startTransformation_withSubtitles_completesSuccessfully
()
throws
Exception
{
Transformer
transformer
=
createTransformerBuilder
(
/* enableFallback= */
false
)
testTransformerBuilderFactory
.
create
(
/* enableFallback= */
false
)
.
setTransformationRequest
(
new
TransformationRequest
.
Builder
().
setAudioMimeType
(
MimeTypes
.
AUDIO_AAC
).
build
())
.
build
();
...
...
@@ -208,13 +213,17 @@ public final class TransformerEndToEndTest {
transformer
.
startTransformation
(
mediaItem
,
outputPath
);
TransformerTestRunner
.
runUntilCompleted
(
transformer
);
DumpFileAsserts
.
assertOutput
(
context
,
testMuxer
,
getDumpFileName
(
FILE_WITH_SUBTITLES
));
DumpFileAsserts
.
assertOutput
(
context
,
testTransformerBuilderFactory
.
getTestMuxer
(),
getDumpFileName
(
FILE_WITH_SUBTITLES
));
}
@Test
public
void
startTransformation_successiveTransformations_completesSuccessfully
()
throws
Exception
{
Transformer
transformer
=
createTransformerBuilder
(
/* enableFallback= */
false
).
build
();
Transformer
transformer
=
testTransformerBuilderFactory
.
create
(
/* enableFallback= */
false
).
build
();
MediaItem
mediaItem
=
MediaItem
.
fromUri
(
ASSET_URI_PREFIX
+
FILE_AUDIO_VIDEO
);
// Transform first media item.
...
...
@@ -226,12 +235,14 @@ public final class TransformerEndToEndTest {
transformer
.
startTransformation
(
mediaItem
,
outputPath
);
TransformerTestRunner
.
runUntilCompleted
(
transformer
);
DumpFileAsserts
.
assertOutput
(
context
,
testMuxer
,
getDumpFileName
(
FILE_AUDIO_VIDEO
));
DumpFileAsserts
.
assertOutput
(
context
,
testTransformerBuilderFactory
.
getTestMuxer
(),
getDumpFileName
(
FILE_AUDIO_VIDEO
));
}
@Test
public
void
startTransformation_concurrentTransformations_throwsError
()
throws
Exception
{
Transformer
transformer
=
createTransformerBuilder
(
/* enableFallback= */
false
).
build
();
Transformer
transformer
=
testTransformerBuilderFactory
.
create
(
/* enableFallback= */
false
).
build
();
MediaItem
mediaItem
=
MediaItem
.
fromUri
(
ASSET_URI_PREFIX
+
FILE_VIDEO_ONLY
);
transformer
.
startTransformation
(
mediaItem
,
outputPath
);
...
...
@@ -243,33 +254,44 @@ public final class TransformerEndToEndTest {
@Test
public
void
startTransformation_removeAudio_completesSuccessfully
()
throws
Exception
{
Transformer
transformer
=
createTransformerBuilder
(
/* enableFallback= */
false
).
setRemoveAudio
(
true
).
build
();
testTransformerBuilderFactory
.
create
(
/* enableFallback= */
false
)
.
setRemoveAudio
(
true
)
.
build
();
MediaItem
mediaItem
=
MediaItem
.
fromUri
(
ASSET_URI_PREFIX
+
FILE_AUDIO_VIDEO
);
transformer
.
startTransformation
(
mediaItem
,
outputPath
);
TransformerTestRunner
.
runUntilCompleted
(
transformer
);
DumpFileAsserts
.
assertOutput
(
context
,
testMuxer
,
getDumpFileName
(
FILE_AUDIO_VIDEO
+
".noaudio"
));
context
,
testTransformerBuilderFactory
.
getTestMuxer
(),
getDumpFileName
(
FILE_AUDIO_VIDEO
+
".noaudio"
));
}
@Test
public
void
startTransformation_removeVideo_completesSuccessfully
()
throws
Exception
{
Transformer
transformer
=
createTransformerBuilder
(
/* enableFallback= */
false
).
setRemoveVideo
(
true
).
build
();
testTransformerBuilderFactory
.
create
(
/* enableFallback= */
false
)
.
setRemoveVideo
(
true
)
.
build
();
MediaItem
mediaItem
=
MediaItem
.
fromUri
(
ASSET_URI_PREFIX
+
FILE_AUDIO_VIDEO
);
transformer
.
startTransformation
(
mediaItem
,
outputPath
);
TransformerTestRunner
.
runUntilCompleted
(
transformer
);
DumpFileAsserts
.
assertOutput
(
context
,
testMuxer
,
getDumpFileName
(
FILE_AUDIO_VIDEO
+
".novideo"
));
context
,
testTransformerBuilderFactory
.
getTestMuxer
(),
getDumpFileName
(
FILE_AUDIO_VIDEO
+
".novideo"
));
}
@Test
public
void
startTransformation_silentAudio_completesSuccessfully
()
throws
Exception
{
Transformer
transformer
=
createTransformerBuilder
(
/* enableFallback= */
false
)
testTransformerBuilderFactory
.
create
(
/* enableFallback= */
false
)
.
experimentalSetForceSilentAudio
(
true
)
.
build
();
MediaItem
mediaItem
=
MediaItem
.
fromUri
(
ASSET_URI_PREFIX
+
FILE_AUDIO_VIDEO
);
...
...
@@ -278,7 +300,9 @@ public final class TransformerEndToEndTest {
TransformerTestRunner
.
runUntilCompleted
(
transformer
);
DumpFileAsserts
.
assertOutput
(
context
,
testMuxer
,
getDumpFileName
(
FILE_AUDIO_VIDEO
+
".silentaudio"
));
context
,
testTransformerBuilderFactory
.
getTestMuxer
(),
getDumpFileName
(
FILE_AUDIO_VIDEO
+
".silentaudio"
));
}
@Test
...
...
@@ -286,7 +310,8 @@ public final class TransformerEndToEndTest {
SonicAudioProcessor
sonicAudioProcessor
=
new
SonicAudioProcessor
();
sonicAudioProcessor
.
setOutputSampleRateHz
(
48000
);
Transformer
transformer
=
createTransformerBuilder
(
/* enableFallback= */
false
)
testTransformerBuilderFactory
.
create
(
/* enableFallback= */
false
)
.
setAudioProcessors
(
ImmutableList
.
of
(
sonicAudioProcessor
))
.
build
();
MediaItem
mediaItem
=
MediaItem
.
fromUri
(
ASSET_URI_PREFIX
+
FILE_AUDIO_VIDEO
);
...
...
@@ -295,7 +320,9 @@ public final class TransformerEndToEndTest {
TransformerTestRunner
.
runUntilCompleted
(
transformer
);
DumpFileAsserts
.
assertOutput
(
context
,
testMuxer
,
getDumpFileName
(
FILE_AUDIO_VIDEO
+
".48000hz"
));
context
,
testTransformerBuilderFactory
.
getTestMuxer
(),
getDumpFileName
(
FILE_AUDIO_VIDEO
+
".48000hz"
));
}
@Test
...
...
@@ -304,7 +331,8 @@ public final class TransformerEndToEndTest {
Transformer
.
Listener
mockListener2
=
mock
(
Transformer
.
Listener
.
class
);
Transformer
.
Listener
mockListener3
=
mock
(
Transformer
.
Listener
.
class
);
Transformer
transformer
=
createTransformerBuilder
(
/* enableFallback= */
false
)
testTransformerBuilderFactory
.
create
(
/* enableFallback= */
false
)
.
addListener
(
mockListener1
)
.
addListener
(
mockListener2
)
.
addListener
(
mockListener3
)
...
...
@@ -325,7 +353,8 @@ public final class TransformerEndToEndTest {
Transformer
.
Listener
mockListener2
=
mock
(
Transformer
.
Listener
.
class
);
Transformer
.
Listener
mockListener3
=
mock
(
Transformer
.
Listener
.
class
);
Transformer
transformer
=
createTransformerBuilder
(
/* enableFallback= */
false
)
testTransformerBuilderFactory
.
create
(
/* enableFallback= */
false
)
.
addListener
(
mockListener1
)
.
addListener
(
mockListener2
)
.
addListener
(
mockListener3
)
...
...
@@ -352,7 +381,8 @@ public final class TransformerEndToEndTest {
TransformationRequest
fallbackTransformationRequest
=
new
TransformationRequest
.
Builder
().
setAudioMimeType
(
MimeTypes
.
AUDIO_AAC
).
build
();
Transformer
transformer
=
createTransformerBuilder
(
/* enableFallback= */
true
)
testTransformerBuilderFactory
.
create
(
/* enableFallback= */
true
)
.
addListener
(
mockListener1
)
.
addListener
(
mockListener2
)
.
addListener
(
mockListener3
)
...
...
@@ -377,7 +407,8 @@ public final class TransformerEndToEndTest {
Transformer
.
Listener
mockListener2
=
mock
(
Transformer
.
Listener
.
class
);
Transformer
.
Listener
mockListener3
=
mock
(
Transformer
.
Listener
.
class
);
Transformer
transformer1
=
createTransformerBuilder
(
/* enableFallback= */
false
)
testTransformerBuilderFactory
.
create
(
/* enableFallback= */
false
)
.
addListener
(
mockListener1
)
.
addListener
(
mockListener2
)
.
addListener
(
mockListener3
)
...
...
@@ -396,7 +427,8 @@ public final class TransformerEndToEndTest {
@Test
public
void
startTransformation_flattenForSlowMotion_completesSuccessfully
()
throws
Exception
{
Transformer
transformer
=
createTransformerBuilder
(
/* enableFallback= */
false
)
testTransformerBuilderFactory
.
create
(
/* enableFallback= */
false
)
.
setTransformationRequest
(
new
TransformationRequest
.
Builder
().
setFlattenForSlowMotion
(
true
).
build
())
.
build
();
...
...
@@ -405,7 +437,10 @@ public final class TransformerEndToEndTest {
transformer
.
startTransformation
(
mediaItem
,
outputPath
);
TransformerTestRunner
.
runUntilCompleted
(
transformer
);
DumpFileAsserts
.
assertOutput
(
context
,
testMuxer
,
getDumpFileName
(
FILE_WITH_SEF_SLOW_MOTION
));
DumpFileAsserts
.
assertOutput
(
context
,
testTransformerBuilderFactory
.
getTestMuxer
(),
getDumpFileName
(
FILE_WITH_SEF_SLOW_MOTION
));
}
@Test
...
...
@@ -420,7 +455,10 @@ public final class TransformerEndToEndTest {
}
};
Transformer
transformer
=
createTransformerBuilder
(
/* enableFallback= */
false
).
addListener
(
listener
).
build
();
testTransformerBuilderFactory
.
create
(
/* enableFallback= */
false
)
.
addListener
(
listener
)
.
build
();
MediaItem
mediaItem
=
MediaItem
.
fromUri
(
ASSET_URI_PREFIX
+
FILE_AUDIO_VIDEO
);
transformer
.
startTransformation
(
mediaItem
,
outputPath
);
...
...
@@ -436,7 +474,8 @@ public final class TransformerEndToEndTest {
public
void
startTransformation_withAudioEncoderFormatUnsupported_completesWithError
()
throws
Exception
{
Transformer
transformer
=
createTransformerBuilder
(
/* enableFallback= */
false
)
testTransformerBuilderFactory
.
create
(
/* enableFallback= */
false
)
.
setTransformationRequest
(
new
TransformationRequest
.
Builder
()
.
setAudioMimeType
(
...
...
@@ -457,7 +496,8 @@ public final class TransformerEndToEndTest {
public
void
startTransformation_withAudioDecoderFormatUnsupported_completesWithError
()
throws
Exception
{
Transformer
transformer
=
createTransformerBuilder
(
/* enableFallback= */
false
)
testTransformerBuilderFactory
.
create
(
/* enableFallback= */
false
)
.
setTransformationRequest
(
new
TransformationRequest
.
Builder
()
.
setAudioMimeType
(
MimeTypes
.
AUDIO_AAC
)
// supported by encoder and muxer
...
...
@@ -475,7 +515,8 @@ public final class TransformerEndToEndTest {
@Test
public
void
startTransformation_withIoError_completesWithError
()
throws
Exception
{
Transformer
transformer
=
createTransformerBuilder
(
/* enableFallback= */
false
).
build
();
Transformer
transformer
=
testTransformerBuilderFactory
.
create
(
/* enableFallback= */
false
).
build
();
MediaItem
mediaItem
=
MediaItem
.
fromUri
(
"asset:///non-existing-path.mp4"
);
transformer
.
startTransformation
(
mediaItem
,
outputPath
);
...
...
@@ -495,14 +536,19 @@ public final class TransformerEndToEndTest {
TransformationRequest
fallbackTransformationRequest
=
new
TransformationRequest
.
Builder
().
setAudioMimeType
(
MimeTypes
.
AUDIO_AAC
).
build
();
Transformer
transformer
=
createTransformerBuilder
(
/* enableFallback= */
false
).
addListener
(
mockListener
).
build
();
testTransformerBuilderFactory
.
create
(
/* enableFallback= */
false
)
.
addListener
(
mockListener
)
.
build
();
MediaItem
mediaItem
=
MediaItem
.
fromUri
(
ASSET_URI_PREFIX
+
FILE_AUDIO_UNSUPPORTED_BY_MUXER
);
transformer
.
startTransformation
(
mediaItem
,
outputPath
);
TransformerTestRunner
.
runUntilCompleted
(
transformer
);
DumpFileAsserts
.
assertOutput
(
context
,
testMuxer
,
getDumpFileName
(
FILE_AUDIO_UNSUPPORTED_BY_MUXER
+
".fallback"
));
context
,
testTransformerBuilderFactory
.
getTestMuxer
(),
getDumpFileName
(
FILE_AUDIO_UNSUPPORTED_BY_MUXER
+
".fallback"
));
verify
(
mockListener
)
.
onFallbackApplied
(
mediaItem
,
originalTransformationRequest
,
fallbackTransformationRequest
);
}
...
...
@@ -516,14 +562,19 @@ public final class TransformerEndToEndTest {
TransformationRequest
fallbackTransformationRequest
=
new
TransformationRequest
.
Builder
().
setAudioMimeType
(
MimeTypes
.
AUDIO_AAC
).
build
();
Transformer
transformer
=
createTransformerBuilder
(
/* enableFallback= */
true
).
addListener
(
mockListener
).
build
();
testTransformerBuilderFactory
.
create
(
/* enableFallback= */
true
)
.
addListener
(
mockListener
)
.
build
();
MediaItem
mediaItem
=
MediaItem
.
fromUri
(
ASSET_URI_PREFIX
+
FILE_AUDIO_UNSUPPORTED_BY_MUXER
);
transformer
.
startTransformation
(
mediaItem
,
outputPath
);
TransformerTestRunner
.
runUntilCompleted
(
transformer
);
DumpFileAsserts
.
assertOutput
(
context
,
testMuxer
,
getDumpFileName
(
FILE_AUDIO_UNSUPPORTED_BY_MUXER
+
".fallback"
));
context
,
testTransformerBuilderFactory
.
getTestMuxer
(),
getDumpFileName
(
FILE_AUDIO_UNSUPPORTED_BY_MUXER
+
".fallback"
));
verify
(
mockListener
)
.
onFallbackApplied
(
mediaItem
,
originalTransformationRequest
,
fallbackTransformationRequest
);
}
...
...
@@ -533,11 +584,11 @@ public final class TransformerEndToEndTest {
MediaSource
.
Factory
mediaSourceFactory
=
new
DefaultMediaSourceFactory
(
context
,
new
SlowExtractorsFactory
(
/* delayBetweenReadsMs= */
10
));
Muxer
.
Factory
muxerFactory
=
new
TestMuxerFactory
(
/* maxDelayBetweenSamplesMs= */
1
);
Transformer
transformer
=
createTransformerBuilder
(
/* enableFallback= */
false
)
testTransformerBuilderFactory
.
setMaxDelayBetweenSamplesMs
(
1
)
.
create
(
/* enableFallback= */
false
)
.
setMediaSourceFactory
(
mediaSourceFactory
)
.
setMuxerFactory
(
muxerFactory
)
.
build
();
MediaItem
mediaItem
=
MediaItem
.
fromUri
(
ASSET_URI_PREFIX
+
FILE_AUDIO_VIDEO
);
...
...
@@ -551,20 +602,24 @@ public final class TransformerEndToEndTest {
@Test
public
void
startTransformation_withUnsetMaxDelayBetweenSamples_completesSuccessfully
()
throws
Exception
{
Muxer
.
Factory
muxerFactory
=
new
TestMuxerFactory
(
/* maxDelayBetweenSamplesMs= */
C
.
TIME_UNSET
);
Transformer
transformer
=
createTransformerBuilder
(
/* enableFallback= */
false
).
setMuxerFactory
(
muxerFactory
).
build
();
testTransformerBuilderFactory
.
setMaxDelayBetweenSamplesMs
(
C
.
TIME_UNSET
)
.
create
(
/* enableFallback= */
false
)
.
build
();
MediaItem
mediaItem
=
MediaItem
.
fromUri
(
ASSET_URI_PREFIX
+
FILE_AUDIO_VIDEO
);
transformer
.
startTransformation
(
mediaItem
,
outputPath
);
TransformerTestRunner
.
runUntilCompleted
(
transformer
);
DumpFileAsserts
.
assertOutput
(
context
,
testMuxer
,
getDumpFileName
(
FILE_AUDIO_VIDEO
));
DumpFileAsserts
.
assertOutput
(
context
,
testTransformerBuilderFactory
.
getTestMuxer
(),
getDumpFileName
(
FILE_AUDIO_VIDEO
));
}
@Test
public
void
startTransformation_afterCancellation_completesSuccessfully
()
throws
Exception
{
Transformer
transformer
=
createTransformerBuilder
(
/* enableFallback= */
false
).
build
();
Transformer
transformer
=
testTransformerBuilderFactory
.
create
(
/* enableFallback= */
false
).
build
();
MediaItem
mediaItem
=
MediaItem
.
fromUri
(
ASSET_URI_PREFIX
+
FILE_AUDIO_VIDEO
);
transformer
.
startTransformation
(
mediaItem
,
outputPath
);
...
...
@@ -575,7 +630,8 @@ public final class TransformerEndToEndTest {
transformer
.
startTransformation
(
mediaItem
,
outputPath
);
TransformerTestRunner
.
runUntilCompleted
(
transformer
);
DumpFileAsserts
.
assertOutput
(
context
,
testMuxer
,
getDumpFileName
(
FILE_AUDIO_VIDEO
));
DumpFileAsserts
.
assertOutput
(
context
,
testTransformerBuilderFactory
.
getTestMuxer
(),
getDumpFileName
(
FILE_AUDIO_VIDEO
));
}
@Test
...
...
@@ -584,7 +640,7 @@ public final class TransformerEndToEndTest {
anotherThread
.
start
();
Looper
looper
=
anotherThread
.
getLooper
();
Transformer
transformer
=
createTransformerBuilder
(
/* enableFallback= */
false
).
setLooper
(
looper
).
build
();
testTransformerBuilderFactory
.
create
(
/* enableFallback= */
false
).
setLooper
(
looper
).
build
();
MediaItem
mediaItem
=
MediaItem
.
fromUri
(
ASSET_URI_PREFIX
+
FILE_AUDIO_VIDEO
);
AtomicReference
<
Exception
>
exception
=
new
AtomicReference
<>();
CountDownLatch
countDownLatch
=
new
CountDownLatch
(
1
);
...
...
@@ -604,12 +660,14 @@ public final class TransformerEndToEndTest {
countDownLatch
.
await
();
assertThat
(
exception
.
get
()).
isNull
();
DumpFileAsserts
.
assertOutput
(
context
,
testMuxer
,
getDumpFileName
(
FILE_AUDIO_VIDEO
));
DumpFileAsserts
.
assertOutput
(
context
,
testTransformerBuilderFactory
.
getTestMuxer
(),
getDumpFileName
(
FILE_AUDIO_VIDEO
));
}
@Test
public
void
startTransformation_fromWrongThread_throwsError
()
throws
Exception
{
Transformer
transformer
=
createTransformerBuilder
(
/* enableFallback= */
false
).
build
();
Transformer
transformer
=
testTransformerBuilderFactory
.
create
(
/* enableFallback= */
false
).
build
();
MediaItem
mediaItem
=
MediaItem
.
fromUri
(
ASSET_URI_PREFIX
+
FILE_AUDIO_VIDEO
);
HandlerThread
anotherThread
=
new
HandlerThread
(
"AnotherThread"
);
AtomicReference
<
IllegalStateException
>
illegalStateException
=
new
AtomicReference
<>();
...
...
@@ -634,7 +692,8 @@ public final class TransformerEndToEndTest {
@Test
public
void
getProgress_knownDuration_returnsConsistentStates
()
throws
Exception
{
Transformer
transformer
=
createTransformerBuilder
(
/* enableFallback= */
false
).
build
();
Transformer
transformer
=
testTransformerBuilderFactory
.
create
(
/* enableFallback= */
false
).
build
();
MediaItem
mediaItem
=
MediaItem
.
fromUri
(
ASSET_URI_PREFIX
+
FILE_VIDEO_ONLY
);
AtomicInteger
previousProgressState
=
new
AtomicInteger
(
PROGRESS_STATE_WAITING_FOR_AVAILABILITY
);
...
...
@@ -680,7 +739,8 @@ public final class TransformerEndToEndTest {
@Test
public
void
getProgress_knownDuration_givesIncreasingPercentages
()
throws
Exception
{
Transformer
transformer
=
createTransformerBuilder
(
/* enableFallback= */
false
).
build
();
Transformer
transformer
=
testTransformerBuilderFactory
.
create
(
/* enableFallback= */
false
).
build
();
MediaItem
mediaItem
=
MediaItem
.
fromUri
(
ASSET_URI_PREFIX
+
FILE_VIDEO_ONLY
);
List
<
Integer
>
progresses
=
new
ArrayList
<>();
Handler
progressHandler
=
...
...
@@ -715,7 +775,8 @@ public final class TransformerEndToEndTest {
@Test
public
void
getProgress_noCurrentTransformation_returnsNoTransformation
()
throws
Exception
{
Transformer
transformer
=
createTransformerBuilder
(
/* enableFallback= */
false
).
build
();
Transformer
transformer
=
testTransformerBuilderFactory
.
create
(
/* enableFallback= */
false
).
build
();
MediaItem
mediaItem
=
MediaItem
.
fromUri
(
ASSET_URI_PREFIX
+
FILE_VIDEO_ONLY
);
@Transformer
.
ProgressState
int
stateBeforeTransform
=
transformer
.
getProgress
(
progressHolder
);
...
...
@@ -729,7 +790,8 @@ public final class TransformerEndToEndTest {
@Test
public
void
getProgress_unknownDuration_returnsConsistentStates
()
throws
Exception
{
Transformer
transformer
=
createTransformerBuilder
(
/* enableFallback= */
false
).
build
();
Transformer
transformer
=
testTransformerBuilderFactory
.
create
(
/* enableFallback= */
false
).
build
();
MediaItem
mediaItem
=
MediaItem
.
fromUri
(
ASSET_URI_PREFIX
+
FILE_UNKNOWN_DURATION
);
AtomicInteger
previousProgressState
=
new
AtomicInteger
(
PROGRESS_STATE_WAITING_FOR_AVAILABILITY
);
...
...
@@ -772,7 +834,8 @@ public final class TransformerEndToEndTest {
@Test
public
void
getProgress_fromWrongThread_throwsError
()
throws
Exception
{
Transformer
transformer
=
createTransformerBuilder
(
/* enableFallback= */
false
).
build
();
Transformer
transformer
=
testTransformerBuilderFactory
.
create
(
/* enableFallback= */
false
).
build
();
HandlerThread
anotherThread
=
new
HandlerThread
(
"AnotherThread"
);
AtomicReference
<
IllegalStateException
>
illegalStateException
=
new
AtomicReference
<>();
CountDownLatch
countDownLatch
=
new
CountDownLatch
(
1
);
...
...
@@ -796,7 +859,8 @@ public final class TransformerEndToEndTest {
@Test
public
void
cancel_afterCompletion_doesNotThrow
()
throws
Exception
{
Transformer
transformer
=
createTransformerBuilder
(
/* enableFallback= */
false
).
build
();
Transformer
transformer
=
testTransformerBuilderFactory
.
create
(
/* enableFallback= */
false
).
build
();
MediaItem
mediaItem
=
MediaItem
.
fromUri
(
ASSET_URI_PREFIX
+
FILE_VIDEO_ONLY
);
transformer
.
startTransformation
(
mediaItem
,
outputPath
);
...
...
@@ -806,7 +870,8 @@ public final class TransformerEndToEndTest {
@Test
public
void
cancel_fromWrongThread_throwsError
()
throws
Exception
{
Transformer
transformer
=
createTransformerBuilder
(
/* enableFallback= */
false
).
build
();
Transformer
transformer
=
testTransformerBuilderFactory
.
create
(
/* enableFallback= */
false
).
build
();
HandlerThread
anotherThread
=
new
HandlerThread
(
"AnotherThread"
);
AtomicReference
<
IllegalStateException
>
illegalStateException
=
new
AtomicReference
<>();
CountDownLatch
countDownLatch
=
new
CountDownLatch
(
1
);
...
...
@@ -828,147 +893,10 @@ public final class TransformerEndToEndTest {
assertThat
(
illegalStateException
.
get
()).
isNotNull
();
}
private
Transformer
.
Builder
createTransformerBuilder
(
boolean
enableFallback
)
{
return
new
Transformer
.
Builder
(
context
)
.
setClock
(
clock
)
.
setMuxerFactory
(
new
TestMuxerFactory
())
.
setEncoderFactory
(
new
DefaultEncoderFactory
.
Builder
(
context
).
setEnableFallback
(
enableFallback
).
build
());
}
private
static
void
createEncodersAndDecoders
()
{
ShadowMediaCodec
.
CodecConfig
codecConfig
=
new
ShadowMediaCodec
.
CodecConfig
(
/* inputBufferSize= */
10_000
,
/* outputBufferSize= */
10_000
,
/* codec= */
(
in
,
out
)
->
out
.
put
(
in
));
addCodec
(
MimeTypes
.
AUDIO_AAC
,
codecConfig
,
/* colorFormats= */
ImmutableList
.
of
(),
/* isDecoder= */
true
);
addCodec
(
MimeTypes
.
AUDIO_AC3
,
codecConfig
,
/* colorFormats= */
ImmutableList
.
of
(),
/* isDecoder= */
true
);
addCodec
(
MimeTypes
.
AUDIO_AMR_NB
,
codecConfig
,
/* colorFormats= */
ImmutableList
.
of
(),
/* isDecoder= */
true
);
addCodec
(
MimeTypes
.
AUDIO_AAC
,
codecConfig
,
/* colorFormats= */
ImmutableList
.
of
(),
/* isDecoder= */
false
);
ShadowMediaCodec
.
CodecConfig
throwingCodecConfig
=
new
ShadowMediaCodec
.
CodecConfig
(
/* inputBufferSize= */
10_000
,
/* outputBufferSize= */
10_000
,
new
ShadowMediaCodec
.
CodecConfig
.
Codec
()
{
@Override
public
void
process
(
ByteBuffer
in
,
ByteBuffer
out
)
{
out
.
put
(
in
);
}
@Override
public
void
onConfigured
(
MediaFormat
format
,
@Nullable
Surface
surface
,
@Nullable
MediaCrypto
crypto
,
int
flags
)
{
throw
new
IllegalArgumentException
(
"Format unsupported"
);
}
});
addCodec
(
MimeTypes
.
AUDIO_AMR_WB
,
throwingCodecConfig
,
/* colorFormats= */
ImmutableList
.
of
(),
/* isDecoder= */
true
);
addCodec
(
MimeTypes
.
AUDIO_AMR_NB
,
throwingCodecConfig
,
/* colorFormats= */
ImmutableList
.
of
(),
/* isDecoder= */
false
);
}
private
static
void
addCodec
(
String
mimeType
,
ShadowMediaCodec
.
CodecConfig
codecConfig
,
List
<
Integer
>
colorFormats
,
boolean
isDecoder
)
{
String
codecName
=
Util
.
formatInvariant
(
isDecoder
?
"exo.%s.decoder"
:
"exo.%s.encoder"
,
mimeType
.
replace
(
'/'
,
'-'
));
if
(
isDecoder
)
{
ShadowMediaCodec
.
addDecoder
(
codecName
,
codecConfig
);
}
else
{
ShadowMediaCodec
.
addEncoder
(
codecName
,
codecConfig
);
}
MediaFormat
mediaFormat
=
new
MediaFormat
();
mediaFormat
.
setString
(
MediaFormat
.
KEY_MIME
,
mimeType
);
MediaCodecInfoBuilder
.
CodecCapabilitiesBuilder
codecCapabilities
=
MediaCodecInfoBuilder
.
CodecCapabilitiesBuilder
.
newBuilder
()
.
setMediaFormat
(
mediaFormat
)
.
setIsEncoder
(!
isDecoder
);
if
(!
colorFormats
.
isEmpty
())
{
codecCapabilities
.
setColorFormats
(
Ints
.
toArray
(
colorFormats
));
}
ShadowMediaCodecList
.
addCodec
(
MediaCodecInfoBuilder
.
newBuilder
()
.
setName
(
codecName
)
.
setIsEncoder
(!
isDecoder
)
.
setCapabilities
(
codecCapabilities
.
build
())
.
build
());
}
private
static
void
removeEncodersAndDecoders
()
{
ShadowMediaCodec
.
clearCodecs
();
ShadowMediaCodecList
.
reset
();
EncoderUtil
.
clearCachedEncoders
();
}
private
static
String
getDumpFileName
(
String
originalFileName
)
{
return
DUMP_FILE_OUTPUT_DIRECTORY
+
'/'
+
originalFileName
+
'.'
+
DUMP_FILE_EXTENSION
;
}
private
final
class
TestMuxerFactory
implements
Muxer
.
Factory
{
private
final
Muxer
.
Factory
defaultMuxerFactory
;
public
TestMuxerFactory
()
{
defaultMuxerFactory
=
new
DefaultMuxer
.
Factory
();
}
public
TestMuxerFactory
(
long
maxDelayBetweenSamplesMs
)
{
defaultMuxerFactory
=
new
DefaultMuxer
.
Factory
(
maxDelayBetweenSamplesMs
);
}
@Override
public
Muxer
create
(
String
path
)
throws
Muxer
.
MuxerException
{
testMuxer
=
new
TestMuxer
(
path
,
defaultMuxerFactory
);
return
testMuxer
;
}
@Override
public
Muxer
create
(
ParcelFileDescriptor
parcelFileDescriptor
)
throws
Muxer
.
MuxerException
{
testMuxer
=
new
TestMuxer
(
"FD:"
+
parcelFileDescriptor
.
getFd
(),
defaultMuxerFactory
);
return
testMuxer
;
}
@Override
public
ImmutableList
<
String
>
getSupportedSampleMimeTypes
(
@C
.
TrackType
int
trackType
)
{
return
defaultMuxerFactory
.
getSupportedSampleMimeTypes
(
trackType
);
}
}
private
static
final
class
SlowExtractorsFactory
implements
ExtractorsFactory
{
private
final
long
delayBetweenReadsMs
;
...
...
robolectricutils/build.gradle
View file @
c566b774
...
...
@@ -20,6 +20,7 @@ dependencies {
implementation
'androidx.annotation:annotation:'
+
androidxAnnotationVersion
implementation
'org.robolectric:robolectric:'
+
robolectricVersion
implementation
project
(
modulePrefix
+
'library-core'
)
implementation
project
(
modulePrefix
+
':lib-transformer'
)
implementation
project
(
modulePrefix
+
'testutils'
)
}
...
...
robolectricutils/src/main/java/com/google/android/exoplayer2/robolectric/ShadowMediaCodecConfig.java
View file @
c566b774
...
...
@@ -16,9 +16,14 @@
package
com
.
google
.
android
.
exoplayer2
.
robolectric
;
import
android.media.MediaCodecInfo
;
import
android.media.MediaCrypto
;
import
android.media.MediaFormat
;
import
android.view.Surface
;
import
androidx.annotation.Nullable
;
import
com.google.android.exoplayer2.mediacodec.MediaCodecUtil
;
import
com.google.android.exoplayer2.transformer.EncoderUtil
;
import
com.google.android.exoplayer2.util.MimeTypes
;
import
com.google.android.exoplayer2.util.Util
;
import
com.google.common.collect.ImmutableList
;
import
com.google.common.primitives.Ints
;
import
java.nio.ByteBuffer
;
...
...
@@ -29,104 +34,246 @@ import org.robolectric.shadows.ShadowMediaCodec;
import
org.robolectric.shadows.ShadowMediaCodecList
;
/**
* A JUnit @Rule to configure
Roboelectric's {@link ShadowMediaCodec}
.
* A JUnit @Rule to configure
{@link ShadowMediaCodec} for transcoding or decoding
.
*
* <p>Registers
a {@link org.robolectric.shadows.ShadowMediaCodec.CodecConfig} for each audio/video
*
MIME type known by ExoPlayer
.
* <p>Registers
{@link org.robolectric.shadows.ShadowMediaCodec.CodecConfig} instances for ExoPlayer
*
and Transformer tests
.
*/
public
final
class
ShadowMediaCodecConfig
extends
ExternalResource
{
private
static
final
String
EXOTEST_VIDEO_AVC
=
"exotest.video.avc"
;
private
static
final
String
EXOTEST_VIDEO_MPEG2
=
"exotest.video.mpeg2"
;
private
static
final
String
EXOTEST_VIDEO_VP9
=
"exotest.video.vp9"
;
private
static
final
String
EXOTEST_AUDIO_AAC
=
"exotest.audio.aac"
;
private
static
final
String
EXOTEST_AUDIO_AC3
=
"exotest.audio.ac3"
;
private
static
final
String
EXOTEST_AUDIO_AC4
=
"exotest.audio.ac4"
;
private
static
final
String
EXOTEST_AUDIO_E_AC3
=
"exotest.audio.eac3"
;
private
static
final
String
EXOTEST_AUDIO_E_AC3_JOC
=
"exotest.audio.eac3joc"
;
private
static
final
String
EXOTEST_AUDIO_FLAC
=
"exotest.audio.flac"
;
private
static
final
String
EXOTEST_AUDIO_MPEG
=
"exotest.audio.mpeg"
;
private
static
final
String
EXOTEST_AUDIO_MPEG_L2
=
"exotest.audio.mpegl2"
;
private
static
final
String
EXOTEST_AUDIO_OPUS
=
"exotest.audio.opus"
;
private
static
final
String
EXOTEST_AUDIO_VORBIS
=
"exotest.audio.vorbis"
;
private
static
final
String
EXOTEST_AUDIO_RAW
=
"exotest.audio.raw"
;
private
final
boolean
forTranscoding
;
private
ShadowMediaCodecConfig
(
boolean
forTranscoding
)
{
this
.
forTranscoding
=
forTranscoding
;
}
/** Creates an instance that configures {@link ShadowMediaCodec} for Transformer transcoding. */
public
static
ShadowMediaCodecConfig
forTranscoding
()
{
return
new
ShadowMediaCodecConfig
(
/* forTranscoding= */
true
);
}
/** Creates an instance that configures {@link ShadowMediaCodec} for Exoplayer decoding. */
public
static
ShadowMediaCodecConfig
forAllSupportedMimeTypes
()
{
return
new
ShadowMediaCodecConfig
();
return
new
ShadowMediaCodecConfig
(
/* forTranscoding= */
false
);
}
@Override
protected
void
before
()
throws
Throwable
{
if
(
forTranscoding
)
{
addTranscodingCodecs
();
}
else
{
addDecodingCodecs
();
}
}
private
void
addTranscodingCodecs
()
{
ShadowMediaCodec
.
CodecConfig
codecConfig
=
new
ShadowMediaCodec
.
CodecConfig
(
/* inputBufferSize= */
10_000
,
/* outputBufferSize= */
10_000
,
/* codec= */
(
in
,
out
)
->
out
.
put
(
in
));
addTransformerCodec
(
MimeTypes
.
AUDIO_AAC
,
codecConfig
,
/* isDecoder= */
true
);
addTransformerCodec
(
MimeTypes
.
AUDIO_AC3
,
codecConfig
,
/* isDecoder= */
true
);
addTransformerCodec
(
MimeTypes
.
AUDIO_AMR_NB
,
codecConfig
,
/* isDecoder= */
true
);
addTransformerCodec
(
MimeTypes
.
AUDIO_AAC
,
codecConfig
,
/* isDecoder= */
false
);
ShadowMediaCodec
.
CodecConfig
throwingCodecConfig
=
new
ShadowMediaCodec
.
CodecConfig
(
/* inputBufferSize= */
10_000
,
/* outputBufferSize= */
10_000
,
new
ShadowMediaCodec
.
CodecConfig
.
Codec
()
{
@Override
public
void
process
(
ByteBuffer
in
,
ByteBuffer
out
)
{
out
.
put
(
in
);
}
@Override
public
void
onConfigured
(
MediaFormat
format
,
@Nullable
Surface
surface
,
@Nullable
MediaCrypto
crypto
,
int
flags
)
{
throw
new
IllegalArgumentException
(
"Format unsupported"
);
}
});
addTransformerCodec
(
MimeTypes
.
AUDIO_AMR_WB
,
throwingCodecConfig
,
/* isDecoder= */
true
);
addTransformerCodec
(
MimeTypes
.
AUDIO_AMR_NB
,
throwingCodecConfig
,
/* isDecoder= */
false
);
}
private
void
addDecodingCodecs
()
{
// Video codecs
MediaCodecInfo
.
CodecProfileLevel
avcProfileLevel
=
createProfileLevel
(
MediaCodecInfo
.
CodecProfileLevel
.
AVCProfileHigh
,
MediaCodecInfo
.
CodecProfileLevel
.
AVCLevel62
);
configure
Codec
(
/* codecName= */
"exotest.video.avc"
,
addExoplayer
Codec
(
EXOTEST_VIDEO_AVC
,
MimeTypes
.
VIDEO_H264
,
generateDecodingCodecConfig
(
MimeTypes
.
VIDEO_H264
),
ImmutableList
.
of
(
avcProfileLevel
),
ImmutableList
.
of
(
MediaCodecInfo
.
CodecCapabilities
.
COLOR_FormatYUV420Flexible
));
MediaCodecInfo
.
CodecProfileLevel
mpeg2ProfileLevel
=
createProfileLevel
(
MediaCodecInfo
.
CodecProfileLevel
.
MPEG2ProfileMain
,
MediaCodecInfo
.
CodecProfileLevel
.
MPEG2LevelML
);
configure
Codec
(
/* codecName= */
"exotest.video.mpeg2"
,
addExoplayer
Codec
(
EXOTEST_VIDEO_MPEG2
,
MimeTypes
.
VIDEO_MPEG2
,
generateDecodingCodecConfig
(
MimeTypes
.
VIDEO_MPEG2
),
ImmutableList
.
of
(
mpeg2ProfileLevel
),
ImmutableList
.
of
(
MediaCodecInfo
.
CodecCapabilities
.
COLOR_FormatYUV420Flexible
));
configure
Codec
(
/* codecName= */
"exotest.video.vp9"
,
addExoplayer
Codec
(
EXOTEST_VIDEO_VP9
,
MimeTypes
.
VIDEO_VP9
,
generateDecodingCodecConfig
(
MimeTypes
.
VIDEO_VP9
),
ImmutableList
.
of
(),
ImmutableList
.
of
(
MediaCodecInfo
.
CodecCapabilities
.
COLOR_FormatYUV420Flexible
));
// Audio codecs
configureCodec
(
"exotest.audio.aac"
,
MimeTypes
.
AUDIO_AAC
);
configureCodec
(
"exotest.audio.ac3"
,
MimeTypes
.
AUDIO_AC3
);
configureCodec
(
"exotest.audio.ac4"
,
MimeTypes
.
AUDIO_AC4
);
configureCodec
(
"exotest.audio.eac3"
,
MimeTypes
.
AUDIO_E_AC3
);
configureCodec
(
"exotest.audio.eac3joc"
,
MimeTypes
.
AUDIO_E_AC3_JOC
);
configureCodec
(
"exotest.audio.flac"
,
MimeTypes
.
AUDIO_FLAC
);
configureCodec
(
"exotest.audio.mpeg"
,
MimeTypes
.
AUDIO_MPEG
);
configureCodec
(
"exotest.audio.mpegl2"
,
MimeTypes
.
AUDIO_MPEG_L2
);
configureCodec
(
"exotest.audio.opus"
,
MimeTypes
.
AUDIO_OPUS
);
configureCodec
(
"exotest.audio.vorbis"
,
MimeTypes
.
AUDIO_VORBIS
);
addExoplayerCodec
(
EXOTEST_AUDIO_AAC
,
MimeTypes
.
AUDIO_AAC
,
generateDecodingCodecConfig
(
MimeTypes
.
AUDIO_AAC
));
addExoplayerCodec
(
EXOTEST_AUDIO_AC3
,
MimeTypes
.
AUDIO_AC3
,
generateDecodingCodecConfig
(
MimeTypes
.
AUDIO_AC3
));
addExoplayerCodec
(
EXOTEST_AUDIO_AC4
,
MimeTypes
.
AUDIO_AC4
,
generateDecodingCodecConfig
(
MimeTypes
.
AUDIO_AC4
));
addExoplayerCodec
(
EXOTEST_AUDIO_E_AC3
,
MimeTypes
.
AUDIO_E_AC3
,
generateDecodingCodecConfig
(
MimeTypes
.
AUDIO_E_AC3
));
addExoplayerCodec
(
EXOTEST_AUDIO_E_AC3_JOC
,
MimeTypes
.
AUDIO_E_AC3_JOC
,
generateDecodingCodecConfig
(
MimeTypes
.
AUDIO_E_AC3_JOC
));
addExoplayerCodec
(
EXOTEST_AUDIO_FLAC
,
MimeTypes
.
AUDIO_FLAC
,
generateDecodingCodecConfig
(
MimeTypes
.
AUDIO_FLAC
));
addExoplayerCodec
(
EXOTEST_AUDIO_MPEG
,
MimeTypes
.
AUDIO_MPEG
,
generateDecodingCodecConfig
(
MimeTypes
.
AUDIO_MPEG
));
addExoplayerCodec
(
EXOTEST_AUDIO_MPEG_L2
,
MimeTypes
.
AUDIO_MPEG_L2
,
generateDecodingCodecConfig
(
MimeTypes
.
AUDIO_MPEG_L2
));
addExoplayerCodec
(
EXOTEST_AUDIO_OPUS
,
MimeTypes
.
AUDIO_OPUS
,
generateDecodingCodecConfig
(
MimeTypes
.
AUDIO_OPUS
));
addExoplayerCodec
(
EXOTEST_AUDIO_VORBIS
,
MimeTypes
.
AUDIO_VORBIS
,
generateDecodingCodecConfig
(
MimeTypes
.
AUDIO_VORBIS
));
// Raw audio should use a bypass mode and never need this codec. However, to easily assert
// failures of the bypass mode we want to detect when the raw audio is decoded by this class and
// thus we need a codec to output samples.
configureCodec
(
"exotest.audio.raw"
,
MimeTypes
.
AUDIO_RAW
);
addExoplayerCodec
(
EXOTEST_AUDIO_RAW
,
MimeTypes
.
AUDIO_RAW
,
generateDecodingCodecConfig
(
MimeTypes
.
AUDIO_RAW
));
}
@Override
protected
void
after
()
{
if
(!
forTranscoding
)
{
MediaCodecUtil
.
clearDecoderInfoCache
();
}
else
{
EncoderUtil
.
clearCachedEncoders
();
}
ShadowMediaCodecList
.
reset
();
ShadowMediaCodec
.
clearCodecs
();
}
private
void
configureCodec
(
String
codecName
,
String
mimeType
)
{
configureCodec
(
private
ShadowMediaCodec
.
CodecConfig
generateDecodingCodecConfig
(
String
mimeType
)
{
// TODO: Update ShadowMediaCodec to consider the MediaFormat.KEY_MAX_INPUT_SIZE value passed
// to configure() so we don't have to specify large buffers here.
CodecImpl
codec
=
new
CodecImpl
(
mimeType
);
return
new
ShadowMediaCodec
.
CodecConfig
(
/* inputBufferSize= */
100_000
,
/* outputBufferSize= */
100_000
,
codec
);
}
private
void
addTransformerCodec
(
String
mimeType
,
ShadowMediaCodec
.
CodecConfig
codecConfig
,
boolean
isDecoder
)
{
String
codecName
=
Util
.
formatInvariant
(
isDecoder
?
"transformertest.%s.decoder"
:
"transformertest.%s.encoder"
,
mimeType
.
replace
(
'/'
,
'.'
));
addCodec
(
codecName
,
mimeType
,
codecConfig
,
/* profileLevels= */
ImmutableList
.
of
(),
/* colorFormats= */
ImmutableList
.
of
(),
isDecoder
);
}
private
void
addExoplayerCodec
(
String
codecName
,
String
mimeType
,
ShadowMediaCodec
.
CodecConfig
codecConfig
)
{
addExoplayerCodec
(
codecName
,
mimeType
,
codecConfig
,
/* profileLevels= */
ImmutableList
.
of
(),
/* colorFormats= */
ImmutableList
.
of
());
}
private
void
configure
Codec
(
private
void
addExoplayer
Codec
(
String
codecName
,
String
mimeType
,
ShadowMediaCodec
.
CodecConfig
codecConfig
,
List
<
MediaCodecInfo
.
CodecProfileLevel
>
profileLevels
,
List
<
Integer
>
colorFormats
)
{
addCodec
(
codecName
,
mimeType
,
codecConfig
,
profileLevels
,
colorFormats
,
/* isDecoder= */
true
);
}
private
void
addCodec
(
String
codecName
,
String
mimeType
,
ShadowMediaCodec
.
CodecConfig
codecConfig
,
List
<
MediaCodecInfo
.
CodecProfileLevel
>
profileLevels
,
List
<
Integer
>
colorFormats
,
boolean
isDecoder
)
{
MediaFormat
mediaFormat
=
new
MediaFormat
();
mediaFormat
.
setString
(
MediaFormat
.
KEY_MIME
,
mimeType
);
MediaCodecInfoBuilder
.
CodecCapabilitiesBuilder
capabilities
=
MediaCodecInfoBuilder
.
CodecCapabilitiesBuilder
.
newBuilder
().
setMediaFormat
(
mediaFormat
);
MediaCodecInfoBuilder
.
CodecCapabilitiesBuilder
.
newBuilder
()
.
setMediaFormat
(
mediaFormat
)
.
setIsEncoder
(!
isDecoder
);
if
(!
profileLevels
.
isEmpty
())
{
capabilities
.
setProfileLevels
(
profileLevels
.
toArray
(
new
MediaCodecInfo
.
CodecProfileLevel
[
0
]));
}
if
(!
colorFormats
.
isEmpty
())
{
capabilities
.
setColorFormats
(
Ints
.
toArray
(
colorFormats
));
}
ShadowMediaCodecList
.
addCodec
(
MediaCodecInfoBuilder
.
newBuilder
()
.
setName
(
codecName
)
.
setIsEncoder
(!
isDecoder
)
.
setCapabilities
(
capabilities
.
build
())
.
build
());
// TODO: Update ShadowMediaCodec to consider the MediaFormat.KEY_MAX_INPUT_SIZE value passed
// to configure() so we don't have to specify large buffers here.
CodecImpl
codec
=
new
CodecImpl
(
mimeType
);
ShadowMediaCodec
.
addDecoder
(
codecName
,
new
ShadowMediaCodec
.
CodecConfig
(
/* inputBufferSize= */
100_000
,
/* outputBufferSize= */
100_000
,
codec
));
if
(
isDecoder
)
{
ShadowMediaCodec
.
addDecoder
(
codecName
,
codecConfig
);
}
else
{
ShadowMediaCodec
.
addEncoder
(
codecName
,
codecConfig
);
}
}
private
static
MediaCodecInfo
.
CodecProfileLevel
createProfileLevel
(
int
profile
,
int
level
)
{
...
...
testutils/build.gradle
View file @
c566b774
...
...
@@ -26,6 +26,7 @@ dependencies {
implementation
'androidx.annotation:annotation:'
+
androidxAnnotationVersion
implementation
'com.squareup.okhttp3:mockwebserver:'
+
okhttpVersion
implementation
project
(
modulePrefix
+
'library-core'
)
implementation
project
(
modulePrefix
+
':lib-transformer'
)
testImplementation
'org.robolectric:robolectric:'
+
robolectricVersion
}
...
...
library/transformer/src/test/java/com/google/android/exoplayer2/transformer
/TestMuxer.java
→
testutils/src/main/java/com/google/android/exoplayer2/testutil
/TestMuxer.java
View file @
c566b774
...
...
@@ -13,11 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
com
.
google
.
android
.
exoplayer2
.
t
ransformer
;
package
com
.
google
.
android
.
exoplayer2
.
t
estutil
;
import
com.google.android.exoplayer2.Format
;
import
com.google.android.exoplayer2.testutil.DumpableFormat
;
import
com.google.android.exoplayer2.testutil.Dumper
;
import
com.google.android.exoplayer2.transformer.Muxer
;
import
java.nio.ByteBuffer
;
import
java.util.ArrayList
;
import
java.util.Arrays
;
...
...
testutils/src/main/java/com/google/android/exoplayer2/testutil/TestTransformerBuilderFactory.java
0 → 100644
View file @
c566b774
/*
* Copyright 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
com
.
google
.
android
.
exoplayer2
.
testutil
;
import
static
com
.
google
.
android
.
exoplayer2
.
util
.
Assertions
.
checkStateNotNull
;
import
android.annotation.SuppressLint
;
import
android.content.Context
;
import
android.os.ParcelFileDescriptor
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.transformer.DefaultEncoderFactory
;
import
com.google.android.exoplayer2.transformer.DefaultMuxer
;
import
com.google.android.exoplayer2.transformer.Muxer
;
import
com.google.android.exoplayer2.transformer.Transformer
;
import
com.google.android.exoplayer2.util.Clock
;
import
com.google.common.collect.ImmutableList
;
import
com.google.errorprone.annotations.CanIgnoreReturnValue
;
import
org.checkerframework.checker.nullness.qual.MonotonicNonNull
;
/**
* Creates a {@link Transformer.Builder} setting up some of the common resources needed for testing
* {@link Transformer}.
*/
public
final
class
TestTransformerBuilderFactory
{
private
final
Context
context
;
private
static
@MonotonicNonNull
TestMuxer
testMuxer
;
private
long
maxDelayBetweenSamplesMs
;
/** Creates a new instance */
public
TestTransformerBuilderFactory
(
Context
context
)
{
this
.
context
=
context
;
maxDelayBetweenSamplesMs
=
DefaultMuxer
.
Factory
.
DEFAULT_MAX_DELAY_BETWEEN_SAMPLES_MS
;
}
/**
* Sets the muxer's {@linkplain Muxer#getMaxDelayBetweenSamplesMs() max delay} between samples.
*/
@CanIgnoreReturnValue
public
TestTransformerBuilderFactory
setMaxDelayBetweenSamplesMs
(
long
maxDelayBetweenSamplesMs
)
{
this
.
maxDelayBetweenSamplesMs
=
maxDelayBetweenSamplesMs
;
return
this
;
}
/** Returns a {@link Transformer.Builder} using the provided values or their defaults. */
@SuppressLint
(
"VisibleForTests"
)
// Suppresses warning on setting the clock outside of a test file
public
Transformer
.
Builder
create
(
boolean
enableFallback
)
{
Clock
clock
=
new
FakeClock
(
/* isAutoAdvancing= */
true
);
Muxer
.
Factory
defaultMuxerFactory
=
new
DefaultMuxer
.
Factory
(
maxDelayBetweenSamplesMs
);
return
new
Transformer
.
Builder
(
context
)
.
setClock
(
clock
)
.
setMuxerFactory
(
new
TestMuxerFactory
(
defaultMuxerFactory
))
.
setEncoderFactory
(
new
DefaultEncoderFactory
.
Builder
(
context
).
setEnableFallback
(
enableFallback
).
build
());
}
/**
* Returns the test muxer used in the {@link Transformer.Builder}.
*
* <p>This method should only be called after the transformation is completed.
*/
public
TestMuxer
getTestMuxer
()
{
return
checkStateNotNull
(
testMuxer
);
}
private
static
final
class
TestMuxerFactory
implements
Muxer
.
Factory
{
private
final
Muxer
.
Factory
defaultMuxerFactory
;
public
TestMuxerFactory
(
Muxer
.
Factory
defaultMuxerFactory
)
{
this
.
defaultMuxerFactory
=
defaultMuxerFactory
;
}
@Override
public
Muxer
create
(
String
path
)
throws
Muxer
.
MuxerException
{
testMuxer
=
new
TestMuxer
(
path
,
defaultMuxerFactory
);
return
testMuxer
;
}
@Override
public
Muxer
create
(
ParcelFileDescriptor
parcelFileDescriptor
)
throws
Muxer
.
MuxerException
{
testMuxer
=
new
TestMuxer
(
"FD:"
+
parcelFileDescriptor
.
getFd
(),
defaultMuxerFactory
);
return
testMuxer
;
}
@Override
public
ImmutableList
<
String
>
getSupportedSampleMimeTypes
(
@C
.
TrackType
int
trackType
)
{
return
defaultMuxerFactory
.
getSupportedSampleMimeTypes
(
trackType
);
}
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment