Throw when inferred sample MIME type is not supported by the muxer.

This is better than silently dropping tracks as done previously. Later,
we will implement fallback to transcoding to a supported MIME type.

PiperOrigin-RevId: 418006258
This commit is contained in:
hschlueter 2021-12-23 15:38:36 +00:00 committed by tonihei
parent 036a28b292
commit fe1ffdb959
7 changed files with 1883 additions and 85 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,10 @@
containerMimeType = video/mp4
format 0:
sampleMimeType = audio/mp4a-latm
channelCount = 1
sampleRate = 44100
pcmEncoding = 2
format 1:
id = 1
sampleMimeType = video/avc
codecs = avc1.640034
@ -11,181 +16,355 @@ format 0:
data = length 30, hash F6F3D010
data = length 10, hash 7A0D0F2B
sample:
trackIndex = 0
trackIndex = 1
dataHashCode = -252482306
size = 36477
isKeyFrame = true
presentationTimeUs = 0
sample:
trackIndex = 0
trackIndex = 1
dataHashCode = 67864034
size = 5341
isKeyFrame = false
presentationTimeUs = 67000
sample:
trackIndex = 0
trackIndex = 1
dataHashCode = 897273234
size = 596
isKeyFrame = false
presentationTimeUs = 33000
sample:
trackIndex = 0
trackIndex = 1
dataHashCode = -1549870586
size = 7704
isKeyFrame = false
presentationTimeUs = 200000
sample:
trackIndex = 0
trackIndex = 1
dataHashCode = 672384813
size = 989
isKeyFrame = false
presentationTimeUs = 133000
sample:
trackIndex = 0
trackIndex = 1
dataHashCode = -988996493
size = 721
isKeyFrame = false
presentationTimeUs = 100000
sample:
trackIndex = 0
trackIndex = 1
dataHashCode = 1711151377
size = 519
isKeyFrame = false
presentationTimeUs = 167000
sample:
trackIndex = 0
trackIndex = 1
dataHashCode = -506806036
size = 6160
isKeyFrame = false
presentationTimeUs = 333000
sample:
trackIndex = 0
trackIndex = 1
dataHashCode = 1902167649
size = 953
isKeyFrame = false
presentationTimeUs = 267000
sample:
trackIndex = 0
trackIndex = 1
dataHashCode = 2054873212
size = 620
isKeyFrame = false
presentationTimeUs = 233000
sample:
trackIndex = 0
trackIndex = 1
dataHashCode = 1556608231
size = 405
isKeyFrame = false
presentationTimeUs = 300000
sample:
trackIndex = 0
trackIndex = 1
dataHashCode = -1648978019
size = 4852
isKeyFrame = false
presentationTimeUs = 433000
sample:
trackIndex = 0
trackIndex = 1
dataHashCode = -484808327
size = 547
isKeyFrame = false
presentationTimeUs = 400000
sample:
trackIndex = 0
trackIndex = 1
dataHashCode = -20706048
size = 570
isKeyFrame = false
presentationTimeUs = 367000
sample:
trackIndex = 0
trackIndex = 1
dataHashCode = 2085064574
size = 5525
isKeyFrame = false
presentationTimeUs = 567000
sample:
trackIndex = 0
dataHashCode = 555688582
size = 416
isKeyFrame = true
presentationTimeUs = 0
sample:
trackIndex = 0
dataHashCode = 2000837254
size = 418
isKeyFrame = true
presentationTimeUs = 4717
sample:
trackIndex = 0
dataHashCode = -1593942879
size = 418
isKeyFrame = true
presentationTimeUs = 9456
sample:
trackIndex = 0
dataHashCode = 587837542
size = 418
isKeyFrame = true
presentationTimeUs = 14196
sample:
trackIndex = 0
dataHashCode = -1836423877
size = 418
isKeyFrame = true
presentationTimeUs = 18935
sample:
trackIndex = 0
dataHashCode = 874705099
size = 418
isKeyFrame = true
presentationTimeUs = 23674
sample:
trackIndex = 0
dataHashCode = -269206181
size = 418
isKeyFrame = true
presentationTimeUs = 28413
sample:
trackIndex = 0
dataHashCode = -58682425
size = 418
isKeyFrame = true
presentationTimeUs = 33152
sample:
trackIndex = 0
dataHashCode = -859796970
size = 418
isKeyFrame = true
presentationTimeUs = 37892
sample:
trackIndex = 0
dataHashCode = 711911523
size = 418
isKeyFrame = true
presentationTimeUs = 42631
sample:
trackIndex = 0
dataHashCode = -694513071
size = 418
isKeyFrame = true
presentationTimeUs = 47370
sample:
trackIndex = 0
dataHashCode = -1124371059
size = 418
isKeyFrame = true
presentationTimeUs = 52109
sample:
trackIndex = 0
dataHashCode = 297166745
size = 418
isKeyFrame = true
presentationTimeUs = 56849
sample:
trackIndex = 0
dataHashCode = -937110638
size = 418
isKeyFrame = true
presentationTimeUs = 61588
sample:
trackIndex = 0
dataHashCode = -1050158990
size = 418
isKeyFrame = true
presentationTimeUs = 66327
sample:
trackIndex = 0
dataHashCode = 1109510229
size = 418
isKeyFrame = true
presentationTimeUs = 71066
sample:
trackIndex = 0
dataHashCode = 1297086772
size = 418
isKeyFrame = true
presentationTimeUs = 75805
sample:
trackIndex = 0
dataHashCode = -1739939803
size = 418
isKeyFrame = true
presentationTimeUs = 80545
sample:
trackIndex = 0
dataHashCode = -1149727930
size = 418
isKeyFrame = true
presentationTimeUs = 85284
sample:
trackIndex = 0
dataHashCode = -1627652713
size = 418
isKeyFrame = true
presentationTimeUs = 90023
sample:
trackIndex = 0
dataHashCode = -551926260
size = 418
isKeyFrame = true
presentationTimeUs = 94762
sample:
trackIndex = 0
dataHashCode = 45987178
size = 418
isKeyFrame = true
presentationTimeUs = 99502
sample:
trackIndex = 0
dataHashCode = -903675808
size = 418
isKeyFrame = true
presentationTimeUs = 104241
sample:
trackIndex = 0
dataHashCode = -755916991
size = 418
isKeyFrame = true
presentationTimeUs = 108980
sample:
trackIndex = 0
dataHashCode = -1355207303
size = 418
isKeyFrame = true
presentationTimeUs = 113719
sample:
trackIndex = 0
dataHashCode = -975703389
size = 418
isKeyFrame = true
presentationTimeUs = 118459
sample:
trackIndex = 0
dataHashCode = 1933194670
size = 418
isKeyFrame = true
presentationTimeUs = 123198
sample:
trackIndex = 0
dataHashCode = -565778989
size = 418
isKeyFrame = true
presentationTimeUs = 127937
sample:
trackIndex = 0
dataHashCode = 1454083383
size = 418
isKeyFrame = true
presentationTimeUs = 132676
sample:
trackIndex = 1
dataHashCode = -637074022
size = 1082
isKeyFrame = false
presentationTimeUs = 500000
sample:
trackIndex = 0
trackIndex = 1
dataHashCode = -1824027029
size = 807
isKeyFrame = false
presentationTimeUs = 467000
sample:
trackIndex = 0
trackIndex = 1
dataHashCode = -1701945306
size = 744
isKeyFrame = false
presentationTimeUs = 533000
sample:
trackIndex = 0
trackIndex = 1
dataHashCode = -952425536
size = 4732
isKeyFrame = false
presentationTimeUs = 700000
sample:
trackIndex = 0
trackIndex = 1
dataHashCode = -1978031576
size = 1004
isKeyFrame = false
presentationTimeUs = 633000
sample:
trackIndex = 0
trackIndex = 1
dataHashCode = -2128215508
size = 794
isKeyFrame = false
presentationTimeUs = 600000
sample:
trackIndex = 0
trackIndex = 1
dataHashCode = -259850011
size = 645
isKeyFrame = false
presentationTimeUs = 667000
sample:
trackIndex = 0
trackIndex = 1
dataHashCode = 1920983928
size = 2684
isKeyFrame = false
presentationTimeUs = 833000
sample:
trackIndex = 0
trackIndex = 1
dataHashCode = 1100642337
size = 787
isKeyFrame = false
presentationTimeUs = 767000
sample:
trackIndex = 0
trackIndex = 1
dataHashCode = 1544917830
size = 649
isKeyFrame = false
presentationTimeUs = 733000
sample:
trackIndex = 0
trackIndex = 1
dataHashCode = -116205995
size = 509
isKeyFrame = false
presentationTimeUs = 800000
sample:
trackIndex = 0
trackIndex = 1
dataHashCode = 696343585
size = 1226
isKeyFrame = false
presentationTimeUs = 967000
sample:
trackIndex = 0
trackIndex = 1
dataHashCode = -644371190
size = 898
isKeyFrame = false
presentationTimeUs = 900000
sample:
trackIndex = 0
trackIndex = 1
dataHashCode = -1606273467
size = 476
isKeyFrame = false
presentationTimeUs = 867000
sample:
trackIndex = 0
trackIndex = 1
dataHashCode = -571265861
size = 486
isKeyFrame = false

View File

@ -0,0 +1,198 @@
containerMimeType = video/mp4
format 0:
id = 1
sampleMimeType = video/avc
codecs = avc1.64001F
maxInputSize = 36722
width = 1080
height = 720
frameRate = 29.970028
colorInfo:
colorSpace = 1
colorRange = 2
colorTransfer = 3
hdrStaticInfo = length 0, hash 0
initializationData:
data = length 29, hash 4746B5D9
data = length 10, hash 7A0D0F2B
sample:
trackIndex = 0
dataHashCode = -770308242
size = 36692
isKeyFrame = true
presentationTimeUs = 0
sample:
trackIndex = 0
dataHashCode = -732087136
size = 5312
isKeyFrame = false
presentationTimeUs = 66733
sample:
trackIndex = 0
dataHashCode = 468156717
size = 599
isKeyFrame = false
presentationTimeUs = 33366
sample:
trackIndex = 0
dataHashCode = 1150349584
size = 7735
isKeyFrame = false
presentationTimeUs = 200200
sample:
trackIndex = 0
dataHashCode = 1443582006
size = 987
isKeyFrame = false
presentationTimeUs = 133466
sample:
trackIndex = 0
dataHashCode = -310585145
size = 673
isKeyFrame = false
presentationTimeUs = 100100
sample:
trackIndex = 0
dataHashCode = 807460688
size = 523
isKeyFrame = false
presentationTimeUs = 166833
sample:
trackIndex = 0
dataHashCode = 1936487090
size = 6061
isKeyFrame = false
presentationTimeUs = 333666
sample:
trackIndex = 0
dataHashCode = -32297181
size = 992
isKeyFrame = false
presentationTimeUs = 266933
sample:
trackIndex = 0
dataHashCode = 1529616406
size = 623
isKeyFrame = false
presentationTimeUs = 233566
sample:
trackIndex = 0
dataHashCode = 1949198785
size = 421
isKeyFrame = false
presentationTimeUs = 300300
sample:
trackIndex = 0
dataHashCode = -147880287
size = 4899
isKeyFrame = false
presentationTimeUs = 433766
sample:
trackIndex = 0
dataHashCode = 1369083472
size = 568
isKeyFrame = false
presentationTimeUs = 400400
sample:
trackIndex = 0
dataHashCode = 965782073
size = 620
isKeyFrame = false
presentationTimeUs = 367033
sample:
trackIndex = 0
dataHashCode = -261176150
size = 5450
isKeyFrame = false
presentationTimeUs = 567233
sample:
trackIndex = 0
dataHashCode = -1830836678
size = 1051
isKeyFrame = false
presentationTimeUs = 500500
sample:
trackIndex = 0
dataHashCode = 1767407540
size = 874
isKeyFrame = false
presentationTimeUs = 467133
sample:
trackIndex = 0
dataHashCode = 918440283
size = 781
isKeyFrame = false
presentationTimeUs = 533866
sample:
trackIndex = 0
dataHashCode = -1408463661
size = 4725
isKeyFrame = false
presentationTimeUs = 700700
sample:
trackIndex = 0
dataHashCode = 1569455924
size = 1022
isKeyFrame = false
presentationTimeUs = 633966
sample:
trackIndex = 0
dataHashCode = -1723778407
size = 790
isKeyFrame = false
presentationTimeUs = 600600
sample:
trackIndex = 0
dataHashCode = 1578275472
size = 610
isKeyFrame = false
presentationTimeUs = 667333
sample:
trackIndex = 0
dataHashCode = 1989768395
size = 2751
isKeyFrame = false
presentationTimeUs = 834166
sample:
trackIndex = 0
dataHashCode = -1215674502
size = 745
isKeyFrame = false
presentationTimeUs = 767433
sample:
trackIndex = 0
dataHashCode = -814473606
size = 621
isKeyFrame = false
presentationTimeUs = 734066
sample:
trackIndex = 0
dataHashCode = 498370894
size = 505
isKeyFrame = false
presentationTimeUs = 800800
sample:
trackIndex = 0
dataHashCode = -1051506468
size = 1268
isKeyFrame = false
presentationTimeUs = 967633
sample:
trackIndex = 0
dataHashCode = -1025604144
size = 880
isKeyFrame = false
presentationTimeUs = 900900
sample:
trackIndex = 0
dataHashCode = -913586520
size = 530
isKeyFrame = false
presentationTimeUs = 867533
sample:
trackIndex = 0
dataHashCode = 1340459242
size = 568
isKeyFrame = false
presentationTimeUs = 934266
released = true

View File

@ -161,6 +161,16 @@ public final class TransformationException extends Exception {
/** Caused by an audio processor initialization failure. */
public static final int ERROR_CODE_AUDIO_PROCESSOR_INIT_FAILED = 6001;
// Muxing errors (7xxx).
/**
* Caused by an output sample MIME type inferred from the input not being supported by the muxer.
*
* <p>Use {@link TransformationRequest.Builder#setAudioMimeType(String)} or {@link
* TransformationRequest.Builder#setVideoMimeType(String)} to transcode to a supported MIME type.
*/
public static final int ERROR_CODE_MUXER_SAMPLE_MIME_TYPE_UNSUPPORTED = 7001;
private static final ImmutableBiMap<String, @ErrorCode Integer> NAME_TO_ERROR_CODE =
new ImmutableBiMap.Builder<String, @ErrorCode Integer>()
.put("ERROR_CODE_FAILED_RUNTIME_CHECK", ERROR_CODE_FAILED_RUNTIME_CHECK)
@ -182,6 +192,9 @@ public final class TransformationException extends Exception {
.put("ERROR_CODE_GL_INIT_FAILED", ERROR_CODE_GL_INIT_FAILED)
.put("ERROR_CODE_GL_PROCESSING_FAILED", ERROR_CODE_GL_PROCESSING_FAILED)
.put("ERROR_CODE_AUDIO_PROCESSOR_INIT_FAILED", ERROR_CODE_AUDIO_PROCESSOR_INIT_FAILED)
.put(
"ERROR_CODE_MUXER_SAMPLE_MIME_TYPE_UNSUPPORTED",
ERROR_CODE_MUXER_SAMPLE_MIME_TYPE_UNSUPPORTED)
.buildOrThrow();
/** Returns the {@code errorCode} for a given name. */
@ -232,6 +245,17 @@ public final class TransformationException extends Exception {
componentName + " error, audio_format = " + audioFormat, cause, errorCode);
}
/**
* Creates an instance for a muxer related exception.
*
* @param cause The cause of the failure.
* @param errorCode See {@link #errorCode}.
* @return The created instance.
*/
/* package */ static TransformationException createForMuxer(Throwable cause, int errorCode) {
return new TransformationException("Muxer error", cause, errorCode);
}
/**
* Creates an instance for an unexpected exception.
*

View File

@ -808,13 +808,9 @@ public final class Transformer {
@Override
public void onTracksInfoChanged(TracksInfo tracksInfo) {
if (muxerWrapper.getTrackCount() == 0) {
// TODO(b/209469847): Do not silently drop unsupported tracks and throw a more specific
// exception earlier.
handleTransformationEnded(
TransformationException.createForUnexpected(
new IllegalStateException(
"The output does not contain any tracks. Check that at least one of the input"
+ " sample formats is supported.")));
new IllegalStateException("The output does not contain any tracks.")));
}
}

View File

@ -16,6 +16,7 @@
package androidx.media3.transformer;
import static androidx.media3.common.util.Assertions.checkState;
import static androidx.media3.common.util.Assertions.checkStateNotNull;
import androidx.annotation.Nullable;
@ -57,26 +58,46 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
this.transformationRequest = transformationRequest;
}
/**
* Returns whether the renderer supports the track type of the given input format.
*
* @param inputFormat The input format.
* @return The {@link Capabilities} for this format.
* @throws ExoPlaybackException If the muxer does not support the output sample MIME type derived
* from the input {@code format} and {@link TransformationRequest}.
*/
@Override
@C.FormatSupport
public final int supportsFormat(Format format) {
@Nullable String sampleMimeType = format.sampleMimeType;
if (MimeTypes.getTrackType(sampleMimeType) != getTrackType()) {
@Capabilities
public final int supportsFormat(Format inputFormat) throws ExoPlaybackException {
@Nullable String inputSampleMimeType = inputFormat.sampleMimeType;
if (inputSampleMimeType == null
|| MimeTypes.getTrackType(inputSampleMimeType) != getTrackType()) {
return RendererCapabilities.create(C.FORMAT_UNSUPPORTED_TYPE);
} else if ((MimeTypes.isAudio(sampleMimeType)
&& muxerWrapper.supportsSampleMimeType(
transformationRequest.audioMimeType == null
? sampleMimeType
: transformationRequest.audioMimeType))
|| (MimeTypes.isVideo(sampleMimeType)
&& muxerWrapper.supportsSampleMimeType(
transformationRequest.videoMimeType == null
? sampleMimeType
: transformationRequest.videoMimeType))) {
return RendererCapabilities.create(C.FORMAT_HANDLED);
} else {
return RendererCapabilities.create(C.FORMAT_UNSUPPORTED_SUBTYPE);
}
// If the output sample MIME type is given in the transformationRequest it has already been
// validated by the builder.
if (MimeTypes.isAudio(inputSampleMimeType) && transformationRequest.audioMimeType != null) {
checkState(muxerWrapper.supportsSampleMimeType(transformationRequest.audioMimeType));
return RendererCapabilities.create(C.FORMAT_HANDLED);
}
if (MimeTypes.isVideo(inputSampleMimeType) && transformationRequest.videoMimeType != null) {
checkState(muxerWrapper.supportsSampleMimeType(transformationRequest.videoMimeType));
return RendererCapabilities.create(C.FORMAT_HANDLED);
}
// When the output sample MIME type is not given in the transformationRequest, it is inferred
// from the input.
if (muxerWrapper.supportsSampleMimeType(inputSampleMimeType)) {
return RendererCapabilities.create(C.FORMAT_HANDLED);
}
throw wrapTransformationException(
TransformationException.createForMuxer(
new IllegalArgumentException(
"The sample MIME inferred from the input is not supported by the muxer. "
+ "Input sample MIME type: "
+ inputSampleMimeType),
TransformationException.ERROR_CODE_MUXER_SAMPLE_MIME_TYPE_UNSUPPORTED));
}
@Override
@ -103,16 +124,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
while (feedMuxerFromPipeline() || samplePipeline.processData() || feedPipelineFromInput()) {}
} catch (TransformationException e) {
// Transformer extracts the TransformationException from this ExoPlaybackException again. This
// temporary wrapping is needed due to the dependence on ExoPlayer's BaseRenderer.
throw ExoPlaybackException.createForRenderer(
e,
"Transformer",
getIndex(),
/* rendererFormat= */ null,
C.FORMAT_HANDLED,
/* isRecoverable= */ false,
PlaybackException.ERROR_CODE_UNSPECIFIED);
throw wrapTransformationException(e);
}
}
@ -226,4 +238,23 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
return false;
}
}
/**
* Returns an {@link ExoPlaybackException} wrapping the {@link TransformationException}.
*
* <p>This temporary wrapping is needed due to the dependence on ExoPlayer's BaseRenderer. {@link
* Transformer} extracts the {@link TransformationException} from this {@link
* ExoPlaybackException} again.
*/
private ExoPlaybackException wrapTransformationException(
TransformationException transformationException) {
return ExoPlaybackException.createForRenderer(
transformationException,
"Transformer",
getIndex(),
/* rendererFormat= */ null,
C.FORMAT_HANDLED,
/* isRecoverable= */ false,
PlaybackException.ERROR_CODE_UNSPECIFIED);
}
}

View File

@ -62,12 +62,14 @@ import org.robolectric.shadows.ShadowMediaCodec;
public final class TransformerTest {
private static final String URI_PREFIX = "asset:///media/";
private static final String FILE_VIDEO_ONLY = "mkv/sample.mkv";
private static final String FILE_AUDIO_ONLY = "amr/sample_nb.amr";
private static final String FILE_VIDEO_ONLY = "mp4/sample_18byte_nclx_colr.mp4";
private static final String FILE_AUDIO_VIDEO = "mp4/sample.mp4";
private static final String FILE_WITH_SUBTITLES = "mkv/sample_with_srt.mkv";
private static final String FILE_WITH_SEF_SLOW_MOTION = "mp4/sample_sef_slow_motion.mp4";
private static final String FILE_WITH_ALL_SAMPLE_FORMATS_UNSUPPORTED = "mp4/sample_ac3.mp4";
private static final String FILE_AUDIO_UNSUPPORTED_BY_DECODER = "amr/sample_wb.amr";
private static final String FILE_AUDIO_UNSUPPORTED_BY_ENCODER = "amr/sample_nb.amr";
private static final String FILE_AUDIO_UNSUPPORTED_BY_MUXER = "mp4/sample_ac3.mp4";
private static final String FILE_VIDEO_UNSUPPORTED = "vp9/bear-vp9.webm";
private static final String FILE_UNKNOWN_DURATION = "mp4/sample_fragmented.mp4";
public static final String DUMP_FILE_OUTPUT_DIRECTORY = "transformerdumps";
public static final String DUMP_FILE_EXTENSION = "dump";
@ -94,7 +96,7 @@ public final class TransformerTest {
}
@Test
public void startTransformation_videoOnly_completesSuccessfully() throws Exception {
public void startTransformation_videoOnlyPassthrough_completesSuccessfully() throws Exception {
Transformer transformer =
new Transformer.Builder(context)
.setClock(clock)
@ -109,18 +111,40 @@ public final class TransformerTest {
}
@Test
public void startTransformation_audioOnly_completesSuccessfully() throws Exception {
public void startTransformation_audioOnlyPassthrough_completesSuccessfully() throws Exception {
Transformer transformer =
new Transformer.Builder(context)
.setClock(clock)
.setMuxerFactory(new TestMuxerFactory())
.build();
MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_AUDIO_ONLY);
MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_AUDIO_UNSUPPORTED_BY_ENCODER);
transformer.startTransformation(mediaItem, outputPath);
TransformerTestRunner.runUntilCompleted(transformer);
DumpFileAsserts.assertOutput(context, testMuxer, getDumpFileName(FILE_AUDIO_ONLY));
DumpFileAsserts.assertOutput(
context, testMuxer, getDumpFileName(FILE_AUDIO_UNSUPPORTED_BY_ENCODER));
}
@Test
public void startTransformation_audioOnlyTranscoding_completesSuccessfully() throws Exception {
Transformer transformer =
new Transformer.Builder(context)
.setClock(clock)
.setMuxerFactory(new TestMuxerFactory())
.setTransformationRequest(
new TransformationRequest.Builder()
.setAudioMimeType(MimeTypes.AUDIO_AAC) // supported by encoder and muxer
.build())
.build();
MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_AUDIO_UNSUPPORTED_BY_ENCODER);
transformer.startTransformation(mediaItem, outputPath);
TransformerTestRunner.runUntilCompleted(transformer);
DumpFileAsserts.assertOutput(
context, testMuxer, getDumpFileName(FILE_AUDIO_UNSUPPORTED_BY_ENCODER + ".aac"));
}
@Test
@ -144,6 +168,8 @@ public final class TransformerTest {
new Transformer.Builder(context)
.setClock(clock)
.setMuxerFactory(new TestMuxerFactory())
.setTransformationRequest(
new TransformationRequest.Builder().setAudioMimeType(MimeTypes.AUDIO_AAC).build())
.build();
MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_WITH_SUBTITLES);
@ -161,7 +187,7 @@ public final class TransformerTest {
.setClock(clock)
.setMuxerFactory(new TestMuxerFactory())
.build();
MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_VIDEO_ONLY);
MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_AUDIO_VIDEO);
// Transform first media item.
transformer.startTransformation(mediaItem, outputPath);
@ -172,7 +198,7 @@ public final class TransformerTest {
transformer.startTransformation(mediaItem, outputPath);
TransformerTestRunner.runUntilCompleted(transformer);
DumpFileAsserts.assertOutput(context, testMuxer, getDumpFileName(FILE_VIDEO_ONLY));
DumpFileAsserts.assertOutput(context, testMuxer, getDumpFileName(FILE_AUDIO_VIDEO));
}
@Test
@ -246,10 +272,11 @@ public final class TransformerTest {
.setMuxerFactory(new TestMuxerFactory())
.setTransformationRequest(
new TransformationRequest.Builder()
.setAudioMimeType(MimeTypes.AUDIO_AMR_WB) // unsupported encoder MIME type
.setAudioMimeType(
MimeTypes.AUDIO_AMR_NB) // unsupported by encoder, supported by muxer
.build())
.build();
MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_AUDIO_ONLY);
MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_AUDIO_UNSUPPORTED_BY_MUXER);
transformer.startTransformation(mediaItem, outputPath);
TransformationException exception = TransformerTestRunner.runUntilError(transformer);
@ -268,10 +295,10 @@ public final class TransformerTest {
.setMuxerFactory(new TestMuxerFactory())
.setTransformationRequest(
new TransformationRequest.Builder()
.setAudioMimeType(MimeTypes.AUDIO_AAC) // supported encoder MIME type
.setAudioMimeType(MimeTypes.AUDIO_AAC) // supported by encoder and muxer
.build())
.build();
MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_WITH_ALL_SAMPLE_FORMATS_UNSUPPORTED);
MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_AUDIO_UNSUPPORTED_BY_DECODER);
transformer.startTransformation(mediaItem, outputPath);
TransformationException exception = TransformerTestRunner.runUntilError(transformer);
@ -315,15 +342,41 @@ public final class TransformerTest {
}
@Test
public void startTransformation_withAllSampleFormatsUnsupported_completesWithError()
public void startTransformation_withAudioMuxerFormatUnsupported_completesWithError()
throws Exception {
Transformer transformer = new Transformer.Builder(context).setClock(clock).build();
MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_WITH_ALL_SAMPLE_FORMATS_UNSUPPORTED);
Transformer transformer =
new Transformer.Builder(context)
.setClock(clock)
.setMuxerFactory(new TestMuxerFactory())
.build();
MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_AUDIO_UNSUPPORTED_BY_MUXER);
transformer.startTransformation(mediaItem, outputPath);
TransformationException exception = TransformerTestRunner.runUntilError(transformer);
assertThat(exception).hasCauseThat().isInstanceOf(IllegalStateException.class);
assertThat(exception).hasCauseThat().isInstanceOf(IllegalArgumentException.class);
assertThat(exception).hasCauseThat().hasMessageThat().contains("audio");
assertThat(exception.errorCode)
.isEqualTo(TransformationException.ERROR_CODE_MUXER_SAMPLE_MIME_TYPE_UNSUPPORTED);
}
@Test
public void startTransformation_withVideoMuxerFormatUnsupported_completesWithError()
throws Exception {
Transformer transformer =
new Transformer.Builder(context)
.setClock(clock)
.setMuxerFactory(new TestMuxerFactory())
.build();
MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_VIDEO_UNSUPPORTED);
transformer.startTransformation(mediaItem, outputPath);
TransformationException exception = TransformerTestRunner.runUntilError(transformer);
assertThat(exception).hasCauseThat().isInstanceOf(IllegalArgumentException.class);
assertThat(exception).hasCauseThat().hasMessageThat().contains("video");
assertThat(exception.errorCode)
.isEqualTo(TransformationException.ERROR_CODE_MUXER_SAMPLE_MIME_TYPE_UNSUPPORTED);
}
@Test
@ -333,7 +386,7 @@ public final class TransformerTest {
.setClock(clock)
.setMuxerFactory(new TestMuxerFactory())
.build();
MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_VIDEO_ONLY);
MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_AUDIO_VIDEO);
transformer.startTransformation(mediaItem, outputPath);
transformer.cancel();
@ -343,7 +396,7 @@ public final class TransformerTest {
transformer.startTransformation(mediaItem, outputPath);
TransformerTestRunner.runUntilCompleted(transformer);
DumpFileAsserts.assertOutput(context, testMuxer, getDumpFileName(FILE_VIDEO_ONLY));
DumpFileAsserts.assertOutput(context, testMuxer, getDumpFileName(FILE_AUDIO_VIDEO));
}
@Test
@ -357,7 +410,7 @@ public final class TransformerTest {
.setClock(clock)
.setMuxerFactory(new TestMuxerFactory())
.build();
MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_AUDIO_ONLY);
MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_AUDIO_VIDEO);
AtomicReference<Exception> exception = new AtomicReference<>();
CountDownLatch countDownLatch = new CountDownLatch(1);
@ -376,13 +429,13 @@ public final class TransformerTest {
countDownLatch.await();
assertThat(exception.get()).isNull();
DumpFileAsserts.assertOutput(context, testMuxer, getDumpFileName(FILE_AUDIO_ONLY));
DumpFileAsserts.assertOutput(context, testMuxer, getDumpFileName(FILE_AUDIO_VIDEO));
}
@Test
public void startTransformation_fromWrongThread_throwsError() throws Exception {
Transformer transformer = new Transformer.Builder(context).setClock(clock).build();
MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_AUDIO_ONLY);
MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_AUDIO_VIDEO);
HandlerThread anotherThread = new HandlerThread("AnotherThread");
AtomicReference<IllegalStateException> illegalStateException = new AtomicReference<>();
CountDownLatch countDownLatch = new CountDownLatch(1);
@ -609,6 +662,7 @@ public final class TransformerTest {
/* outputBufferSize= */ 10_000,
/* codec= */ (in, out) -> out.put(in));
ShadowMediaCodec.addDecoder(MimeTypes.AUDIO_AAC, codecConfig);
ShadowMediaCodec.addDecoder(MimeTypes.AUDIO_AC3, codecConfig);
ShadowMediaCodec.addDecoder(MimeTypes.AUDIO_AMR_NB, codecConfig);
ShadowMediaCodec.addEncoder(MimeTypes.AUDIO_AAC, codecConfig);
@ -632,8 +686,9 @@ public final class TransformerTest {
throw new IllegalArgumentException("Format unsupported");
}
});
ShadowMediaCodec.addDecoder(MimeTypes.AUDIO_AC3, throwingCodecConfig);
ShadowMediaCodec.addEncoder(MimeTypes.AUDIO_AMR_WB, throwingCodecConfig);
ShadowMediaCodec.addDecoder(MimeTypes.AUDIO_AMR_WB, throwingCodecConfig);
ShadowMediaCodec.addEncoder(MimeTypes.AUDIO_AMR_NB, throwingCodecConfig);
ShadowMediaCodec.addEncoder(MimeTypes.VIDEO_H263, throwingCodecConfig);
}