Refactor checking muxer support.

* Move checking that the output format is supported by the muxer
  from supportsFormat (which deals with the input format) to
  ensureConfigured.
* Add maps for the supported MIME types so that the muxer can
  return what MIME types it supports rather than just check a
  MIME type.

PiperOrigin-RevId: 419578165
This commit is contained in:
hschlueter 2022-01-04 14:19:32 +00:00 committed by tonihei
parent d9fd85aa63
commit f60dc148f8
7 changed files with 110 additions and 71 deletions

View File

@ -31,6 +31,8 @@ import androidx.media3.common.Format;
import androidx.media3.common.MimeTypes;
import androidx.media3.common.util.MediaFormatUtil;
import androidx.media3.common.util.Util;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import java.io.IOException;
import java.lang.reflect.Field;
import java.nio.ByteBuffer;
@ -38,6 +40,32 @@ import java.nio.ByteBuffer;
/** Muxer implementation that uses a {@link MediaMuxer}. */
/* package */ final class FrameworkMuxer implements Muxer {
// MediaMuxer supported sample formats are documented in MediaMuxer.addTrack(MediaFormat).
private static final ImmutableMap<String, ImmutableList<String>>
SUPPORTED_CONTAINER_TO_VIDEO_SAMPLE_MIME_TYPES =
ImmutableMap.of(
MimeTypes.VIDEO_MP4,
Util.SDK_INT >= 24
? ImmutableList.of(
MimeTypes.VIDEO_H263,
MimeTypes.VIDEO_H264,
MimeTypes.VIDEO_MP4V,
MimeTypes.VIDEO_H265)
: ImmutableList.of(
MimeTypes.VIDEO_H263, MimeTypes.VIDEO_H264, MimeTypes.VIDEO_MP4V),
MimeTypes.VIDEO_WEBM,
Util.SDK_INT >= 24
? ImmutableList.of(MimeTypes.VIDEO_VP8, MimeTypes.VIDEO_VP9)
: ImmutableList.of(MimeTypes.VIDEO_VP8));
private static final ImmutableMap<String, ImmutableList<String>>
SUPPORTED_CONTAINER_TO_AUDIO_SAMPLE_MIME_TYPES =
ImmutableMap.of(
MimeTypes.VIDEO_MP4,
ImmutableList.of(MimeTypes.AUDIO_AAC, MimeTypes.AUDIO_AMR_NB, MimeTypes.AUDIO_AMR_WB),
MimeTypes.VIDEO_WEBM,
ImmutableList.of(MimeTypes.AUDIO_VORBIS));
public static final class Factory implements Muxer.Factory {
@Override
public FrameworkMuxer create(String path, String outputMimeType) throws IOException {
@ -69,29 +97,22 @@ import java.nio.ByteBuffer;
@Override
public boolean supportsSampleMimeType(
@Nullable String sampleMimeType, String containerMimeType) {
return getSupportedSampleMimeTypes(MimeTypes.getTrackType(sampleMimeType), containerMimeType)
.contains(sampleMimeType);
}
@Override
public ImmutableList<String> getSupportedSampleMimeTypes(
@C.TrackType int trackType, String containerMimeType) {
// MediaMuxer supported sample formats are documented in MediaMuxer.addTrack(MediaFormat).
boolean isAudio = MimeTypes.isAudio(sampleMimeType);
boolean isVideo = MimeTypes.isVideo(sampleMimeType);
if (containerMimeType.equals(MimeTypes.VIDEO_MP4)) {
if (isVideo) {
return MimeTypes.VIDEO_H263.equals(sampleMimeType)
|| MimeTypes.VIDEO_H264.equals(sampleMimeType)
|| MimeTypes.VIDEO_MP4V.equals(sampleMimeType)
|| (Util.SDK_INT >= 24 && MimeTypes.VIDEO_H265.equals(sampleMimeType));
} else if (isAudio) {
return MimeTypes.AUDIO_AAC.equals(sampleMimeType)
|| MimeTypes.AUDIO_AMR_NB.equals(sampleMimeType)
|| MimeTypes.AUDIO_AMR_WB.equals(sampleMimeType);
}
} else if (containerMimeType.equals(MimeTypes.VIDEO_WEBM) && SDK_INT >= 21) {
if (isVideo) {
return MimeTypes.VIDEO_VP8.equals(sampleMimeType)
|| (Util.SDK_INT >= 24 && MimeTypes.VIDEO_VP9.equals(sampleMimeType));
} else if (isAudio) {
return MimeTypes.AUDIO_VORBIS.equals(sampleMimeType);
}
if (trackType == C.TRACK_TYPE_VIDEO) {
return SUPPORTED_CONTAINER_TO_VIDEO_SAMPLE_MIME_TYPES.getOrDefault(
containerMimeType, ImmutableList.of());
} else if (trackType == C.TRACK_TYPE_AUDIO) {
return SUPPORTED_CONTAINER_TO_AUDIO_SAMPLE_MIME_TYPES.getOrDefault(
containerMimeType, ImmutableList.of());
}
return false;
return ImmutableList.of();
}
}

View File

@ -17,8 +17,10 @@ package androidx.media3.transformer;
import android.os.ParcelFileDescriptor;
import androidx.annotation.Nullable;
import androidx.media3.common.C;
import androidx.media3.common.Format;
import androidx.media3.common.MimeTypes;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
import java.nio.ByteBuffer;
@ -69,6 +71,13 @@ import java.nio.ByteBuffer;
* {@link MimeTypes MIME type}.
*/
boolean supportsSampleMimeType(@Nullable String sampleMimeType, String containerMimeType);
/**
* Returns the supported sample {@link MimeTypes MIME types} for the given {@link C.TrackType}
* and container {@link MimeTypes MIME type}.
*/
ImmutableList<String> getSupportedSampleMimeTypes(
@C.TrackType int trackType, String containerMimeType);
}
/**

View File

@ -26,6 +26,7 @@ import androidx.media3.common.C;
import androidx.media3.common.Format;
import androidx.media3.common.MimeTypes;
import androidx.media3.common.util.Util;
import com.google.common.collect.ImmutableList;
import java.nio.ByteBuffer;
/**
@ -84,6 +85,14 @@ import java.nio.ByteBuffer;
return muxerFactory.supportsSampleMimeType(mimeType, containerMimeType);
}
/**
* Returns the supported {@link MimeTypes MIME types} for the given {@link C.TrackType track
* type}.
*/
public ImmutableList<String> getSupportedSampleMimeTypes(@C.TrackType int trackType) {
return muxerFactory.getSupportedSampleMimeTypes(trackType, containerMimeType);
}
/**
* Adds a track format to the muxer.
*

View File

@ -67,25 +67,35 @@ import androidx.media3.extractor.metadata.mp4.SlowMotionData;
return false;
}
Format inputFormat = checkNotNull(formatHolder.format);
if (shouldTranscode(inputFormat)) {
String sampleMimeType = checkNotNull(inputFormat.sampleMimeType);
if (transformationRequest.audioMimeType == null
&& !muxerWrapper.supportsSampleMimeType(sampleMimeType)) {
throw TransformationException.createForMuxer(
new IllegalArgumentException(
"The output sample MIME inferred from the input format is not supported by the muxer."
+ " Sample MIME type: "
+ sampleMimeType),
TransformationException.ERROR_CODE_MUXER_SAMPLE_MIME_TYPE_UNSUPPORTED);
}
if (shouldPassthrough(inputFormat)) {
samplePipeline = new PassthroughSamplePipeline(inputFormat);
} else {
samplePipeline =
new AudioSamplePipeline(
inputFormat, transformationRequest, encoderFactory, decoderFactory);
} else {
samplePipeline = new PassthroughSamplePipeline(inputFormat);
}
return true;
}
private boolean shouldTranscode(Format inputFormat) {
private boolean shouldPassthrough(Format inputFormat) {
if (transformationRequest.audioMimeType != null
&& !transformationRequest.audioMimeType.equals(inputFormat.sampleMimeType)) {
return true;
return false;
}
if (transformationRequest.flattenForSlowMotion && isSlowMotion(inputFormat)) {
return true;
return false;
}
return false;
return true;
}
private static boolean isSlowMotion(Format format) {

View File

@ -16,7 +16,6 @@
package androidx.media3.transformer;
import static androidx.media3.common.util.Assertions.checkState;
import static androidx.media3.common.util.Assertions.checkStateNotNull;
import androidx.annotation.Nullable;
@ -59,45 +58,18 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
}
/**
* Returns whether the renderer supports the track type of the given input format.
* Returns whether the renderer supports the track type of the given format.
*
* @param inputFormat The input format.
* @param format The 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
@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);
}
// 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));
public final int supportsFormat(Format format) {
return RendererCapabilities.create(
MimeTypes.getTrackType(format.sampleMimeType) == getTrackType()
? C.FORMAT_HANDLED
: C.FORMAT_UNSUPPORTED_TYPE);
}
@Override

View File

@ -76,7 +76,19 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
return false;
}
Format inputFormat = checkNotNull(formatHolder.format);
if (shouldTranscode(inputFormat)) {
String sampleMimeType = checkNotNull(inputFormat.sampleMimeType);
if (transformationRequest.audioMimeType == null
&& !muxerWrapper.supportsSampleMimeType(sampleMimeType)) {
throw TransformationException.createForMuxer(
new IllegalArgumentException(
"The output sample MIME inferred from the input format is not supported by the muxer."
+ " Sample MIME type: "
+ sampleMimeType),
TransformationException.ERROR_CODE_MUXER_SAMPLE_MIME_TYPE_UNSUPPORTED);
}
if (shouldPassthrough(inputFormat)) {
samplePipeline = new PassthroughSamplePipeline(inputFormat);
} else {
samplePipeline =
new VideoSamplePipeline(
context,
@ -85,8 +97,6 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
encoderFactory,
decoderFactory,
debugViewProvider);
} else {
samplePipeline = new PassthroughSamplePipeline(inputFormat);
}
if (transformationRequest.flattenForSlowMotion) {
sefSlowMotionFlattener = new SefSlowMotionFlattener(inputFormat);
@ -94,19 +104,19 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
return true;
}
private boolean shouldTranscode(Format inputFormat) {
private boolean shouldPassthrough(Format inputFormat) {
if (transformationRequest.videoMimeType != null
&& !transformationRequest.videoMimeType.equals(inputFormat.sampleMimeType)) {
return true;
return false;
}
if (transformationRequest.outputHeight != C.LENGTH_UNSET
&& transformationRequest.outputHeight != inputFormat.height) {
return true;
return false;
}
if (!transformationRequest.transformationMatrix.isIdentity()) {
return true;
return false;
}
return false;
return true;
}
/**

View File

@ -33,6 +33,7 @@ import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.view.Surface;
import androidx.annotation.Nullable;
import androidx.media3.common.C;
import androidx.media3.common.MediaItem;
import androidx.media3.common.MimeTypes;
import androidx.media3.common.util.Util;
@ -40,6 +41,7 @@ import androidx.media3.test.utils.DumpFileAsserts;
import androidx.media3.test.utils.FakeClock;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import java.io.IOException;
import java.nio.ByteBuffer;
@ -732,5 +734,11 @@ public final class TransformerTest {
public boolean supportsSampleMimeType(String sampleMimeType, String outputMimeType) {
return frameworkMuxerFactory.supportsSampleMimeType(sampleMimeType, outputMimeType);
}
@Override
public ImmutableList<String> getSupportedSampleMimeTypes(
@C.TrackType int trackType, String containerMimeType) {
return frameworkMuxerFactory.getSupportedSampleMimeTypes(trackType, containerMimeType);
}
}
}