mirror of
https://github.com/androidx/media.git
synced 2025-05-06 23:20:42 +08:00
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:
parent
d9fd85aa63
commit
f60dc148f8
@ -31,6 +31,8 @@ import androidx.media3.common.Format;
|
|||||||
import androidx.media3.common.MimeTypes;
|
import androidx.media3.common.MimeTypes;
|
||||||
import androidx.media3.common.util.MediaFormatUtil;
|
import androidx.media3.common.util.MediaFormatUtil;
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
@ -38,6 +40,32 @@ import java.nio.ByteBuffer;
|
|||||||
/** Muxer implementation that uses a {@link MediaMuxer}. */
|
/** Muxer implementation that uses a {@link MediaMuxer}. */
|
||||||
/* package */ final class FrameworkMuxer implements Muxer {
|
/* 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 {
|
public static final class Factory implements Muxer.Factory {
|
||||||
@Override
|
@Override
|
||||||
public FrameworkMuxer create(String path, String outputMimeType) throws IOException {
|
public FrameworkMuxer create(String path, String outputMimeType) throws IOException {
|
||||||
@ -69,29 +97,22 @@ import java.nio.ByteBuffer;
|
|||||||
@Override
|
@Override
|
||||||
public boolean supportsSampleMimeType(
|
public boolean supportsSampleMimeType(
|
||||||
@Nullable String sampleMimeType, String containerMimeType) {
|
@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).
|
// MediaMuxer supported sample formats are documented in MediaMuxer.addTrack(MediaFormat).
|
||||||
boolean isAudio = MimeTypes.isAudio(sampleMimeType);
|
if (trackType == C.TRACK_TYPE_VIDEO) {
|
||||||
boolean isVideo = MimeTypes.isVideo(sampleMimeType);
|
return SUPPORTED_CONTAINER_TO_VIDEO_SAMPLE_MIME_TYPES.getOrDefault(
|
||||||
if (containerMimeType.equals(MimeTypes.VIDEO_MP4)) {
|
containerMimeType, ImmutableList.of());
|
||||||
if (isVideo) {
|
} else if (trackType == C.TRACK_TYPE_AUDIO) {
|
||||||
return MimeTypes.VIDEO_H263.equals(sampleMimeType)
|
return SUPPORTED_CONTAINER_TO_AUDIO_SAMPLE_MIME_TYPES.getOrDefault(
|
||||||
|| MimeTypes.VIDEO_H264.equals(sampleMimeType)
|
containerMimeType, ImmutableList.of());
|
||||||
|| 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) {
|
return ImmutableList.of();
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,8 +17,10 @@ package androidx.media3.transformer;
|
|||||||
|
|
||||||
import android.os.ParcelFileDescriptor;
|
import android.os.ParcelFileDescriptor;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.Format;
|
import androidx.media3.common.Format;
|
||||||
import androidx.media3.common.MimeTypes;
|
import androidx.media3.common.MimeTypes;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
@ -69,6 +71,13 @@ import java.nio.ByteBuffer;
|
|||||||
* {@link MimeTypes MIME type}.
|
* {@link MimeTypes MIME type}.
|
||||||
*/
|
*/
|
||||||
boolean supportsSampleMimeType(@Nullable String sampleMimeType, String containerMimeType);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -26,6 +26,7 @@ import androidx.media3.common.C;
|
|||||||
import androidx.media3.common.Format;
|
import androidx.media3.common.Format;
|
||||||
import androidx.media3.common.MimeTypes;
|
import androidx.media3.common.MimeTypes;
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -84,6 +85,14 @@ import java.nio.ByteBuffer;
|
|||||||
return muxerFactory.supportsSampleMimeType(mimeType, containerMimeType);
|
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.
|
* Adds a track format to the muxer.
|
||||||
*
|
*
|
||||||
|
@ -67,26 +67,36 @@ import androidx.media3.extractor.metadata.mp4.SlowMotionData;
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Format inputFormat = checkNotNull(formatHolder.format);
|
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 =
|
samplePipeline =
|
||||||
new AudioSamplePipeline(
|
new AudioSamplePipeline(
|
||||||
inputFormat, transformationRequest, encoderFactory, decoderFactory);
|
inputFormat, transformationRequest, encoderFactory, decoderFactory);
|
||||||
} else {
|
|
||||||
samplePipeline = new PassthroughSamplePipeline(inputFormat);
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean shouldTranscode(Format inputFormat) {
|
private boolean shouldPassthrough(Format inputFormat) {
|
||||||
if (transformationRequest.audioMimeType != null
|
if (transformationRequest.audioMimeType != null
|
||||||
&& !transformationRequest.audioMimeType.equals(inputFormat.sampleMimeType)) {
|
&& !transformationRequest.audioMimeType.equals(inputFormat.sampleMimeType)) {
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
if (transformationRequest.flattenForSlowMotion && isSlowMotion(inputFormat)) {
|
if (transformationRequest.flattenForSlowMotion && isSlowMotion(inputFormat)) {
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private static boolean isSlowMotion(Format format) {
|
private static boolean isSlowMotion(Format format) {
|
||||||
@Nullable Metadata metadata = format.metadata;
|
@Nullable Metadata metadata = format.metadata;
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
package androidx.media3.transformer;
|
package androidx.media3.transformer;
|
||||||
|
|
||||||
import static androidx.media3.common.util.Assertions.checkState;
|
|
||||||
import static androidx.media3.common.util.Assertions.checkStateNotNull;
|
import static androidx.media3.common.util.Assertions.checkStateNotNull;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
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.
|
* @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
|
@Override
|
||||||
@Capabilities
|
@Capabilities
|
||||||
public final int supportsFormat(Format inputFormat) throws ExoPlaybackException {
|
public final int supportsFormat(Format format) {
|
||||||
@Nullable String inputSampleMimeType = inputFormat.sampleMimeType;
|
return RendererCapabilities.create(
|
||||||
if (inputSampleMimeType == null
|
MimeTypes.getTrackType(format.sampleMimeType) == getTrackType()
|
||||||
|| MimeTypes.getTrackType(inputSampleMimeType) != getTrackType()) {
|
? C.FORMAT_HANDLED
|
||||||
return RendererCapabilities.create(C.FORMAT_UNSUPPORTED_TYPE);
|
: 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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -76,7 +76,19 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Format inputFormat = checkNotNull(formatHolder.format);
|
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 =
|
samplePipeline =
|
||||||
new VideoSamplePipeline(
|
new VideoSamplePipeline(
|
||||||
context,
|
context,
|
||||||
@ -85,8 +97,6 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
|||||||
encoderFactory,
|
encoderFactory,
|
||||||
decoderFactory,
|
decoderFactory,
|
||||||
debugViewProvider);
|
debugViewProvider);
|
||||||
} else {
|
|
||||||
samplePipeline = new PassthroughSamplePipeline(inputFormat);
|
|
||||||
}
|
}
|
||||||
if (transformationRequest.flattenForSlowMotion) {
|
if (transformationRequest.flattenForSlowMotion) {
|
||||||
sefSlowMotionFlattener = new SefSlowMotionFlattener(inputFormat);
|
sefSlowMotionFlattener = new SefSlowMotionFlattener(inputFormat);
|
||||||
@ -94,20 +104,20 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean shouldTranscode(Format inputFormat) {
|
private boolean shouldPassthrough(Format inputFormat) {
|
||||||
if (transformationRequest.videoMimeType != null
|
if (transformationRequest.videoMimeType != null
|
||||||
&& !transformationRequest.videoMimeType.equals(inputFormat.sampleMimeType)) {
|
&& !transformationRequest.videoMimeType.equals(inputFormat.sampleMimeType)) {
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
if (transformationRequest.outputHeight != C.LENGTH_UNSET
|
if (transformationRequest.outputHeight != C.LENGTH_UNSET
|
||||||
&& transformationRequest.outputHeight != inputFormat.height) {
|
&& transformationRequest.outputHeight != inputFormat.height) {
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
if (!transformationRequest.transformationMatrix.isIdentity()) {
|
if (!transformationRequest.transformationMatrix.isIdentity()) {
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Queues the input buffer to the sample pipeline unless it should be dropped because of slow
|
* Queues the input buffer to the sample pipeline unless it should be dropped because of slow
|
||||||
|
@ -33,6 +33,7 @@ import android.os.Message;
|
|||||||
import android.os.ParcelFileDescriptor;
|
import android.os.ParcelFileDescriptor;
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.MediaItem;
|
import androidx.media3.common.MediaItem;
|
||||||
import androidx.media3.common.MimeTypes;
|
import androidx.media3.common.MimeTypes;
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
@ -40,6 +41,7 @@ import androidx.media3.test.utils.DumpFileAsserts;
|
|||||||
import androidx.media3.test.utils.FakeClock;
|
import androidx.media3.test.utils.FakeClock;
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
@ -732,5 +734,11 @@ public final class TransformerTest {
|
|||||||
public boolean supportsSampleMimeType(String sampleMimeType, String outputMimeType) {
|
public boolean supportsSampleMimeType(String sampleMimeType, String outputMimeType) {
|
||||||
return frameworkMuxerFactory.supportsSampleMimeType(sampleMimeType, outputMimeType);
|
return frameworkMuxerFactory.supportsSampleMimeType(sampleMimeType, outputMimeType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ImmutableList<String> getSupportedSampleMimeTypes(
|
||||||
|
@C.TrackType int trackType, String containerMimeType) {
|
||||||
|
return frameworkMuxerFactory.getSupportedSampleMimeTypes(trackType, containerMimeType);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user