Skip to content

Commit a42d9f3

Browse files
committed
Allow output audio MIME type to be set in TranscodingTransformer.
This introduces a new option `setAudioMimeType` in `TranscodingTransformer.Builder` and a corresponding check whether the selected type is supported. This check is done using `supportsSampleMimeType` which is now part of the `Muxer.Factory` and `MuxerWrapper` rather than `Muxer`. A new field `audioMimeType` is added to `Transformation` and the `TransformerAudioRenderer` uses this instead of the input MIME type if requested. PiperOrigin-RevId: 405367817
1 parent 17d2f5a commit a42d9f3

File tree

10 files changed

+144
-71
lines changed

10 files changed

+144
-71
lines changed

‎library/transformer/src/main/java/com/google/android/exoplayer2/transformer/FrameworkMuxer.java

Lines changed: 31 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public static final class Factory implements Muxer.Factory {
4242
@Override
4343
public FrameworkMuxer create(String path, String outputMimeType) throws IOException {
4444
MediaMuxer mediaMuxer = new MediaMuxer(path, mimeTypeToMuxerOutputFormat(outputMimeType));
45-
return new FrameworkMuxer(mediaMuxer, outputMimeType);
45+
return new FrameworkMuxer(mediaMuxer);
4646
}
4747

4848
@RequiresApi(26)
@@ -53,7 +53,7 @@ public FrameworkMuxer create(ParcelFileDescriptor parcelFileDescriptor, String o
5353
new MediaMuxer(
5454
parcelFileDescriptor.getFileDescriptor(),
5555
mimeTypeToMuxerOutputFormat(outputMimeType));
56-
return new FrameworkMuxer(mediaMuxer, outputMimeType);
56+
return new FrameworkMuxer(mediaMuxer);
5757
}
5858

5959
@Override
@@ -65,47 +65,46 @@ public boolean supportsOutputMimeType(String mimeType) {
6565
}
6666
return true;
6767
}
68+
69+
@Override
70+
public boolean supportsSampleMimeType(
71+
@Nullable String sampleMimeType, String containerMimeType) {
72+
// MediaMuxer supported sample formats are documented in MediaMuxer.addTrack(MediaFormat).
73+
boolean isAudio = MimeTypes.isAudio(sampleMimeType);
74+
boolean isVideo = MimeTypes.isVideo(sampleMimeType);
75+
if (containerMimeType.equals(MimeTypes.VIDEO_MP4)) {
76+
if (isVideo) {
77+
return MimeTypes.VIDEO_H263.equals(sampleMimeType)
78+
|| MimeTypes.VIDEO_H264.equals(sampleMimeType)
79+
|| MimeTypes.VIDEO_MP4V.equals(sampleMimeType)
80+
|| (Util.SDK_INT >= 24 && MimeTypes.VIDEO_H265.equals(sampleMimeType));
81+
} else if (isAudio) {
82+
return MimeTypes.AUDIO_AAC.equals(sampleMimeType)
83+
|| MimeTypes.AUDIO_AMR_NB.equals(sampleMimeType)
84+
|| MimeTypes.AUDIO_AMR_WB.equals(sampleMimeType);
85+
}
86+
} else if (containerMimeType.equals(MimeTypes.VIDEO_WEBM) && SDK_INT >= 21) {
87+
if (isVideo) {
88+
return MimeTypes.VIDEO_VP8.equals(sampleMimeType)
89+
|| (Util.SDK_INT >= 24 && MimeTypes.VIDEO_VP9.equals(sampleMimeType));
90+
} else if (isAudio) {
91+
return MimeTypes.AUDIO_VORBIS.equals(sampleMimeType);
92+
}
93+
}
94+
return false;
95+
}
6896
}
6997

7098
private final MediaMuxer mediaMuxer;
71-
private final String outputMimeType;
7299
private final MediaCodec.BufferInfo bufferInfo;
73100

74101
private boolean isStarted;
75102

76-
private FrameworkMuxer(MediaMuxer mediaMuxer, String outputMimeType) {
103+
private FrameworkMuxer(MediaMuxer mediaMuxer) {
77104
this.mediaMuxer = mediaMuxer;
78-
this.outputMimeType = outputMimeType;
79105
bufferInfo = new MediaCodec.BufferInfo();
80106
}
81107

82-
@Override
83-
public boolean supportsSampleMimeType(@Nullable String mimeType) {
84-
// MediaMuxer supported sample formats are documented in MediaMuxer.addTrack(MediaFormat).
85-
boolean isAudio = MimeTypes.isAudio(mimeType);
86-
boolean isVideo = MimeTypes.isVideo(mimeType);
87-
if (outputMimeType.equals(MimeTypes.VIDEO_MP4)) {
88-
if (isVideo) {
89-
return MimeTypes.VIDEO_H263.equals(mimeType)
90-
|| MimeTypes.VIDEO_H264.equals(mimeType)
91-
|| MimeTypes.VIDEO_MP4V.equals(mimeType)
92-
|| (Util.SDK_INT >= 24 && MimeTypes.VIDEO_H265.equals(mimeType));
93-
} else if (isAudio) {
94-
return MimeTypes.AUDIO_AAC.equals(mimeType)
95-
|| MimeTypes.AUDIO_AMR_NB.equals(mimeType)
96-
|| MimeTypes.AUDIO_AMR_WB.equals(mimeType);
97-
}
98-
} else if (outputMimeType.equals(MimeTypes.VIDEO_WEBM) && SDK_INT >= 21) {
99-
if (isVideo) {
100-
return MimeTypes.VIDEO_VP8.equals(mimeType)
101-
|| (Util.SDK_INT >= 24 && MimeTypes.VIDEO_VP9.equals(mimeType));
102-
} else if (isAudio) {
103-
return MimeTypes.AUDIO_VORBIS.equals(mimeType);
104-
}
105-
}
106-
return false;
107-
}
108-
109108
@Override
110109
public int addTrack(Format format) {
111110
String sampleMimeType = checkNotNull(format.sampleMimeType);

‎library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Muxer.java

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,12 @@
2525
/**
2626
* Abstracts media muxing operations.
2727
*
28-
* <p>Query whether {@link #supportsSampleMimeType(String) sample MIME types are supported} and
29-
* {@link #addTrack(Format) add all tracks}, then {@link #writeSampleData(int, ByteBuffer, boolean,
30-
* long) write sample data} to mux samples. Once any sample data has been written, it is not
31-
* possible to add tracks. After writing all sample data, {@link #release(boolean) release} the
32-
* instance to finish writing to the output and return any resources to the system.
28+
* <p>Query whether {@link Factory#supportsOutputMimeType(String) container MIME type} and {@link
29+
* Factory#supportsSampleMimeType(String, String) sample MIME types} are supported and {@link
30+
* #addTrack(Format) add all tracks}, then {@link #writeSampleData(int, ByteBuffer, boolean, long)
31+
* write sample data} to mux samples. Once any sample data has been written, it is not possible to
32+
* add tracks. After writing all sample data, {@link #release(boolean) release} the instance to
33+
* finish writing to the output and return any resources to the system.
3334
*/
3435
/* package */ interface Muxer {
3536

@@ -62,10 +63,13 @@ Muxer create(ParcelFileDescriptor parcelFileDescriptor, String outputMimeType)
6263

6364
/** Returns whether the {@link MimeTypes MIME type} provided is a supported output format. */
6465
boolean supportsOutputMimeType(String mimeType);
65-
}
6666

67-
/** Returns whether the sample {@link MimeTypes MIME type} is supported. */
68-
boolean supportsSampleMimeType(@Nullable String mimeType);
67+
/**
68+
* Returns whether the sample {@link MimeTypes MIME type} is supported with the given container
69+
* {@link MimeTypes MIME type}.
70+
*/
71+
boolean supportsSampleMimeType(@Nullable String sampleMimeType, String containerMimeType);
72+
}
6973

7074
/**
7175
* Adds a track with the specified format, and returns its index (to be passed in subsequent calls

‎library/transformer/src/main/java/com/google/android/exoplayer2/transformer/MuxerWrapper.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,17 +45,21 @@
4545
private static final long MAX_TRACK_WRITE_AHEAD_US = C.msToUs(500);
4646

4747
private final Muxer muxer;
48+
private final Muxer.Factory muxerFactory;
4849
private final SparseIntArray trackTypeToIndex;
4950
private final SparseLongArray trackTypeToTimeUs;
51+
private final String containerMimeType;
5052

5153
private int trackCount;
5254
private int trackFormatCount;
5355
private boolean isReady;
5456
private @C.TrackType int previousTrackType;
5557
private long minTrackTimeUs;
5658

57-
public MuxerWrapper(Muxer muxer) {
59+
public MuxerWrapper(Muxer muxer, Muxer.Factory muxerFactory, String containerMimeType) {
5860
this.muxer = muxer;
61+
this.muxerFactory = muxerFactory;
62+
this.containerMimeType = containerMimeType;
5963
trackTypeToIndex = new SparseIntArray();
6064
trackTypeToTimeUs = new SparseLongArray();
6165
previousTrackType = C.TRACK_TYPE_NONE;
@@ -78,7 +82,7 @@ public void registerTrack() {
7882

7983
/** Returns whether the sample {@link MimeTypes MIME type} is supported. */
8084
public boolean supportsSampleMimeType(@Nullable String mimeType) {
81-
return muxer.supportsSampleMimeType(mimeType);
85+
return muxerFactory.supportsSampleMimeType(mimeType, containerMimeType);
8286
}
8387

8488
/**

‎library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TranscodingTransformer.java

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ public static final class Builder {
100100
private boolean removeVideo;
101101
private boolean flattenForSlowMotion;
102102
private String outputMimeType;
103+
@Nullable private String audioMimeType;
103104
private TranscodingTransformer.Listener listener;
104105
private Looper looper;
105106
private Clock clock;
@@ -122,6 +123,7 @@ private Builder(TranscodingTransformer transcodingTransformer) {
122123
this.removeVideo = transcodingTransformer.transformation.removeVideo;
123124
this.flattenForSlowMotion = transcodingTransformer.transformation.flattenForSlowMotion;
124125
this.outputMimeType = transcodingTransformer.transformation.outputMimeType;
126+
this.audioMimeType = transcodingTransformer.transformation.audioMimeType;
125127
this.listener = transcodingTransformer.listener;
126128
this.looper = transcodingTransformer.looper;
127129
this.clock = transcodingTransformer.clock;
@@ -212,10 +214,8 @@ public Builder setFlattenForSlowMotion(boolean flattenForSlowMotion) {
212214
}
213215

214216
/**
215-
* Sets the MIME type of the output. The default value is {@link MimeTypes#VIDEO_MP4}. The
216-
* output MIME type should be supported by the {@link
217-
* Muxer.Factory#supportsOutputMimeType(String) muxer}. Values supported by the default {@link
218-
* FrameworkMuxer} are:
217+
* Sets the MIME type of the output. The default value is {@link MimeTypes#VIDEO_MP4}. Supported
218+
* values are:
219219
*
220220
* <ul>
221221
* <li>{@link MimeTypes#VIDEO_MP4}
@@ -230,6 +230,31 @@ public Builder setOutputMimeType(String outputMimeType) {
230230
return this;
231231
}
232232

233+
/**
234+
* Sets the audio MIME type of the output. The default value is to use the same MIME type as the
235+
* input. Supported values are:
236+
*
237+
* <ul>
238+
* <li>when the container MIME type is {@link MimeTypes#VIDEO_MP4}:
239+
* <ul>
240+
* <li>{@link MimeTypes#AUDIO_AAC}
241+
* <li>{@link MimeTypes#AUDIO_AMR_NB}
242+
* <li>{@link MimeTypes#AUDIO_AMR_WB}
243+
* </ul>
244+
* <li>when the container MIME type is {@link MimeTypes#VIDEO_WEBM}:
245+
* <ul>
246+
* <li>{@link MimeTypes#AUDIO_VORBIS}
247+
* </ul>
248+
* </ul>
249+
*
250+
* @param audioMimeType The MIME type of the audio samples in the output.
251+
* @return This builder.
252+
*/
253+
public Builder setAudioMimeType(String audioMimeType) {
254+
this.audioMimeType = audioMimeType;
255+
return this;
256+
}
257+
233258
/**
234259
* Sets the {@link TranscodingTransformer.Listener} to listen to the transformation events.
235260
*
@@ -290,6 +315,7 @@ public Builder setLooper(Looper looper) {
290315
* @throws IllegalStateException If both audio and video have been removed (otherwise the output
291316
* would not contain any samples).
292317
* @throws IllegalStateException If the muxer doesn't support the requested output MIME type.
318+
* @throws IllegalStateException If the muxer doesn't support the requested audio MIME type.
293319
*/
294320
public TranscodingTransformer build() {
295321
checkStateNotNull(context);
@@ -303,8 +329,17 @@ public TranscodingTransformer build() {
303329
checkState(
304330
muxerFactory.supportsOutputMimeType(outputMimeType),
305331
"Unsupported output MIME type: " + outputMimeType);
332+
if (audioMimeType != null) {
333+
checkState(
334+
muxerFactory.supportsSampleMimeType(audioMimeType, outputMimeType),
335+
"Unsupported sample MIME type "
336+
+ audioMimeType
337+
+ " for container MIME type "
338+
+ outputMimeType);
339+
}
306340
Transformation transformation =
307-
new Transformation(removeAudio, removeVideo, flattenForSlowMotion, outputMimeType);
341+
new Transformation(
342+
removeAudio, removeVideo, flattenForSlowMotion, outputMimeType, audioMimeType);
308343
return new TranscodingTransformer(
309344
context, mediaSourceFactory, muxerFactory, transformation, listener, looper, clock);
310345
}
@@ -469,7 +504,8 @@ private void startTransformation(MediaItem mediaItem, Muxer muxer) {
469504
throw new IllegalStateException("There is already a transformation in progress.");
470505
}
471506

472-
MuxerWrapper muxerWrapper = new MuxerWrapper(muxer);
507+
MuxerWrapper muxerWrapper =
508+
new MuxerWrapper(muxer, muxerFactory, transformation.outputMimeType);
473509
this.muxerWrapper = muxerWrapper;
474510
DefaultTrackSelector trackSelector = new DefaultTrackSelector(context);
475511
trackSelector.setParameters(

‎library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Transformation.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,27 @@
1616

1717
package com.google.android.exoplayer2.transformer;
1818

19+
import androidx.annotation.Nullable;
20+
1921
/** A media transformation configuration. */
2022
/* package */ final class Transformation {
2123

2224
public final boolean removeAudio;
2325
public final boolean removeVideo;
2426
public final boolean flattenForSlowMotion;
2527
public final String outputMimeType;
28+
@Nullable public final String audioMimeType;
2629

2730
public Transformation(
2831
boolean removeAudio,
2932
boolean removeVideo,
3033
boolean flattenForSlowMotion,
31-
String outputMimeType) {
34+
String outputMimeType,
35+
@Nullable String audioMimeType) {
3236
this.removeAudio = removeAudio;
3337
this.removeVideo = removeVideo;
3438
this.flattenForSlowMotion = flattenForSlowMotion;
3539
this.outputMimeType = outputMimeType;
40+
this.audioMimeType = audioMimeType;
3641
}
3742
}

‎library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Transformer.java

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -209,10 +209,8 @@ public Builder setFlattenForSlowMotion(boolean flattenForSlowMotion) {
209209
}
210210

211211
/**
212-
* Sets the MIME type of the output. The default value is {@link MimeTypes#VIDEO_MP4}. The
213-
* output MIME type should be supported by the {@link
214-
* Muxer.Factory#supportsOutputMimeType(String) muxer}. Values supported by the default {@link
215-
* FrameworkMuxer} are:
212+
* Sets the MIME type of the output. The default value is {@link MimeTypes#VIDEO_MP4}. Supported
213+
* values are:
216214
*
217215
* <ul>
218216
* <li>{@link MimeTypes#VIDEO_MP4}
@@ -301,7 +299,12 @@ public Transformer build() {
301299
muxerFactory.supportsOutputMimeType(outputMimeType),
302300
"Unsupported output MIME type: " + outputMimeType);
303301
Transformation transformation =
304-
new Transformation(removeAudio, removeVideo, flattenForSlowMotion, outputMimeType);
302+
new Transformation(
303+
removeAudio,
304+
removeVideo,
305+
flattenForSlowMotion,
306+
outputMimeType,
307+
/* audioMimeType= */ null);
305308
return new Transformer(
306309
context, mediaSourceFactory, muxerFactory, transformation, listener, looper, clock);
307310
}
@@ -464,7 +467,8 @@ private void startTransformation(MediaItem mediaItem, Muxer muxer) {
464467
throw new IllegalStateException("There is already a transformation in progress.");
465468
}
466469

467-
MuxerWrapper muxerWrapper = new MuxerWrapper(muxer);
470+
MuxerWrapper muxerWrapper =
471+
new MuxerWrapper(muxer, muxerFactory, transformation.outputMimeType);
468472
this.muxerWrapper = muxerWrapper;
469473
DefaultTrackSelector trackSelector = new DefaultTrackSelector(context);
470474
trackSelector.setParameters(

‎library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TransformerAudioRenderer.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -350,11 +350,15 @@ private boolean ensureEncoderAndAudioProcessingConfigured() throws ExoPlaybackEx
350350
throw createRendererException(e, PlaybackException.ERROR_CODE_UNSPECIFIED);
351351
}
352352
}
353+
String audioMimeType =
354+
transformation.audioMimeType == null
355+
? checkNotNull(inputFormat).sampleMimeType
356+
: transformation.audioMimeType;
353357
try {
354358
encoder =
355359
MediaCodecAdapterWrapper.createForAudioEncoding(
356360
new Format.Builder()
357-
.setSampleMimeType(checkNotNull(inputFormat).sampleMimeType)
361+
.setSampleMimeType(audioMimeType)
358362
.setSampleRate(outputAudioFormat.sampleRate)
359363
.setChannelCount(outputAudioFormat.channelCount)
360364
.setAverageBitrate(DEFAULT_ENCODER_BITRATE)

‎library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TransformerBaseRenderer.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,13 @@ public final int supportsFormat(Format format) {
5252
@Nullable String sampleMimeType = format.sampleMimeType;
5353
if (MimeTypes.getTrackType(sampleMimeType) != getTrackType()) {
5454
return RendererCapabilities.create(C.FORMAT_UNSUPPORTED_TYPE);
55-
} else if (muxerWrapper.supportsSampleMimeType(sampleMimeType)) {
55+
} else if ((MimeTypes.isAudio(sampleMimeType)
56+
&& muxerWrapper.supportsSampleMimeType(
57+
transformation.audioMimeType == null
58+
? sampleMimeType
59+
: transformation.audioMimeType))
60+
|| (MimeTypes.isVideo(sampleMimeType)
61+
&& muxerWrapper.supportsSampleMimeType(sampleMimeType))) {
5662
return RendererCapabilities.create(C.FORMAT_HANDLED);
5763
} else {
5864
return RendererCapabilities.create(C.FORMAT_UNSUPPORTED_SUBTYPE);

0 commit comments

Comments
 (0)