Align ASP and VSP on findSupportedMimeTypeForEncoder/Muxer logic.

* Moved the logic to SamplePipeline.
* Pass the requested values via Format.
* Moved exception throwing inside the methods.
* Build up the mimeTypesToCheck as a set - removing possible duplicate
checks.
* Simplified logic that calls the findSupportedMimeType method.
* Improved javadoc.

PiperOrigin-RevId: 509594062
This commit is contained in:
samrobinson 2023-02-14 19:47:06 +00:00 committed by Andrew Lewis
parent ccd8856dac
commit d91afa063a
3 changed files with 101 additions and 118 deletions

View File

@ -33,7 +33,6 @@ import com.google.android.exoplayer2.util.Util;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import java.util.List;
import java.util.Queue; import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedDeque; import java.util.concurrent.ConcurrentLinkedDeque;
import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf; import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf;
@ -119,36 +118,32 @@ import org.checkerframework.dataflow.qual.Pure;
audioProcessingPipeline.flush(); audioProcessingPipeline.flush();
String requestedMimeType = Format requestedEncoderFormat =
transformationRequest.audioMimeType != null
? transformationRequest.audioMimeType
: checkNotNull(firstInputFormat.sampleMimeType);
Format requestedOutputFormat =
new Format.Builder() new Format.Builder()
.setSampleMimeType(requestedMimeType) .setSampleMimeType(
transformationRequest.audioMimeType != null
? transformationRequest.audioMimeType
: checkNotNull(firstInputFormat.sampleMimeType))
.setSampleRate(encoderInputAudioFormat.sampleRate) .setSampleRate(encoderInputAudioFormat.sampleRate)
.setChannelCount(encoderInputAudioFormat.channelCount) .setChannelCount(encoderInputAudioFormat.channelCount)
.setAverageBitrate(DEFAULT_ENCODER_BITRATE) .setAverageBitrate(DEFAULT_ENCODER_BITRATE)
.build(); .build();
ImmutableList<String> muxerSupportedMimeTypes =
muxerWrapper.getSupportedSampleMimeTypes(C.TRACK_TYPE_AUDIO);
// TODO(b/259570024): investigate overhauling fallback.
@Nullable
String supportedMimeType =
findSupportedMimeTypeForEncoderAndMuxer(requestedMimeType, muxerSupportedMimeTypes);
if (supportedMimeType == null) {
throw createNoSupportedMimeTypeException(requestedOutputFormat);
}
encoder = encoder =
encoderFactory.createForAudioEncoding( encoderFactory.createForAudioEncoding(
requestedOutputFormat.buildUpon().setSampleMimeType(supportedMimeType).build()); requestedEncoderFormat
checkState(supportedMimeType.equals(encoder.getConfigurationFormat().sampleMimeType)); .buildUpon()
.setSampleMimeType(
findSupportedMimeTypeForEncoderAndMuxer(
requestedEncoderFormat,
muxerWrapper.getSupportedSampleMimeTypes(C.TRACK_TYPE_AUDIO)))
.build());
fallbackListener.onTransformationRequestFinalized( fallbackListener.onTransformationRequestFinalized(
createFallbackTransformationRequest( createFallbackTransformationRequest(
transformationRequest, requestedOutputFormat, encoder.getConfigurationFormat())); transformationRequest,
requestedEncoderFormat,
/* actualFormat= */ encoder.getConfigurationFormat()));
// Use the same stream offset as the input stream for encoder input buffers. // Use the same stream offset as the input stream for encoder input buffers.
nextEncoderInputBufferTimeUs = streamOffsetUs; nextEncoderInputBufferTimeUs = streamOffsetUs;
@ -350,23 +345,6 @@ import org.checkerframework.dataflow.qual.Pure;
encoder.queueInputBuffer(encoderInputBuffer); encoder.queueInputBuffer(encoderInputBuffer);
} }
@Nullable
private static String findSupportedMimeTypeForEncoderAndMuxer(
String preferredMimeType, List<String> muxerSupportedMimeTypes) {
if (!EncoderUtil.getSupportedEncoders(preferredMimeType).isEmpty()) {
return preferredMimeType;
} else {
// No encoder supports the preferred MIME type.
for (int i = 0; i < muxerSupportedMimeTypes.size(); i++) {
String mimeType = muxerSupportedMimeTypes.get(i);
if (!EncoderUtil.getSupportedEncoders(mimeType).isEmpty()) {
return mimeType;
}
}
}
return null;
}
@Pure @Pure
private static TransformationRequest createFallbackTransformationRequest( private static TransformationRequest createFallbackTransformationRequest(
TransformationRequest transformationRequest, Format requestedFormat, Format actualFormat) { TransformationRequest transformationRequest, Format requestedFormat, Format actualFormat) {

View File

@ -16,7 +16,11 @@
package com.google.android.exoplayer2.transformer; package com.google.android.exoplayer2.transformer;
import static com.google.android.exoplayer2.transformer.EncoderUtil.getSupportedEncoders;
import static com.google.android.exoplayer2.transformer.EncoderUtil.getSupportedEncodersForHdrEditing;
import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
import static com.google.android.exoplayer2.util.Assertions.checkStateNotNull; import static com.google.android.exoplayer2.util.Assertions.checkStateNotNull;
import static com.google.android.exoplayer2.video.ColorInfo.isTransferHdr;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
@ -24,6 +28,9 @@ import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.decoder.DecoderInputBuffer; import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.video.ColorInfo; import com.google.android.exoplayer2.video.ColorInfo;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import java.util.List;
/** /**
* Pipeline for processing media data. * Pipeline for processing media data.
@ -48,23 +55,6 @@ import com.google.android.exoplayer2.video.ColorInfo;
: MimeTypes.getTrackType(firstInputFormat.sampleMimeType); : MimeTypes.getTrackType(firstInputFormat.sampleMimeType);
} }
protected static TransformationException createNoSupportedMimeTypeException(Format format) {
String errorMessage = "No MIME type is supported by both encoder and muxer.";
int errorCode = TransformationException.ERROR_CODE_ENCODING_FORMAT_UNSUPPORTED;
boolean isVideo = MimeTypes.isVideo(format.sampleMimeType);
if (isVideo && ColorInfo.isTransferHdr(format.colorInfo)) {
errorMessage += " Requested HDR colorInfo: " + format.colorInfo;
}
return TransformationException.createForCodec(
new IllegalArgumentException(errorMessage),
errorCode,
isVideo,
/* isDecoder= */ false,
format);
}
@Override @Override
public boolean expectsDecodedData() { public boolean expectsDecodedData() {
return true; return true;
@ -143,4 +133,72 @@ import com.google.android.exoplayer2.video.ColorInfo;
releaseMuxerInputBuffer(); releaseMuxerInputBuffer();
return true; return true;
} }
/**
* Finds a {@linkplain MimeTypes MIME type} that is supported by the encoder and the muxer.
*
* <p>The {@linkplain Format requestedFormat} determines what support is checked.
*
* <ul>
* <li>The {@link Format#sampleMimeType} determines whether audio or video mime types are
* considered. See {@link MimeTypes#isAudio} and {@link MimeTypes#isVideo} for more details.
* <li>The {@link Format#sampleMimeType} must be populated with the preferred {@linkplain
* MimeTypes MIME type}. This mime type will be the first checked.
* <li>When checking video support, if the HDR {@link Format#colorInfo} is set, only encoders
* that support that {@link ColorInfo} will be considered.
* </ul>
*
* @param requestedFormat The {@link Format} requested.
* @param muxerSupportedMimeTypes The list of sample {@linkplain MimeTypes MIME types} that the
* muxer supports.
* @return A supported {@linkplain MimeTypes MIME type}.
* @throws TransformationException If there are no supported {@linkplain MimeTypes MIME types}.
*/
protected static String findSupportedMimeTypeForEncoderAndMuxer(
Format requestedFormat, List<String> muxerSupportedMimeTypes) throws TransformationException {
boolean isVideo = MimeTypes.isVideo(checkNotNull(requestedFormat.sampleMimeType));
ImmutableSet.Builder<String> mimeTypesToCheckSetBuilder =
new ImmutableSet.Builder<String>().add(requestedFormat.sampleMimeType);
if (isVideo) {
mimeTypesToCheckSetBuilder.add(MimeTypes.VIDEO_H265).add(MimeTypes.VIDEO_H264);
}
mimeTypesToCheckSetBuilder.addAll(muxerSupportedMimeTypes);
ImmutableList<String> mimeTypesToCheck = mimeTypesToCheckSetBuilder.build().asList();
for (int i = 0; i < mimeTypesToCheck.size(); i++) {
String mimeType = mimeTypesToCheck.get(i);
if (!muxerSupportedMimeTypes.contains(mimeType)) {
continue;
}
if (isVideo && isTransferHdr(requestedFormat.colorInfo)) {
if (!getSupportedEncodersForHdrEditing(mimeType, requestedFormat.colorInfo).isEmpty()) {
return mimeType;
}
} else if (!getSupportedEncoders(mimeType).isEmpty()) {
return mimeType;
}
}
throw createNoSupportedMimeTypeException(requestedFormat);
}
private static TransformationException createNoSupportedMimeTypeException(Format format) {
String errorMessage = "No MIME type is supported by both encoder and muxer.";
int errorCode = TransformationException.ERROR_CODE_ENCODING_FORMAT_UNSUPPORTED;
boolean isVideo = MimeTypes.isVideo(format.sampleMimeType);
if (isVideo && isTransferHdr(format.colorInfo)) {
errorMessage += " Requested HDR colorInfo: " + format.colorInfo;
}
return TransformationException.createForCodec(
new IllegalArgumentException(errorMessage),
errorCode,
isVideo,
/* isDecoder= */ false,
format);
}
} }

View File

@ -16,14 +16,12 @@
package com.google.android.exoplayer2.transformer; package com.google.android.exoplayer2.transformer;
import static com.google.android.exoplayer2.transformer.EncoderUtil.getSupportedEncoders;
import static com.google.android.exoplayer2.transformer.EncoderUtil.getSupportedEncodersForHdrEditing; import static com.google.android.exoplayer2.transformer.EncoderUtil.getSupportedEncodersForHdrEditing;
import static com.google.android.exoplayer2.transformer.TransformationRequest.HDR_MODE_EXPERIMENTAL_FORCE_INTERPRET_HDR_AS_SDR; import static com.google.android.exoplayer2.transformer.TransformationRequest.HDR_MODE_EXPERIMENTAL_FORCE_INTERPRET_HDR_AS_SDR;
import static com.google.android.exoplayer2.transformer.TransformationRequest.HDR_MODE_KEEP_HDR; import static com.google.android.exoplayer2.transformer.TransformationRequest.HDR_MODE_KEEP_HDR;
import static com.google.android.exoplayer2.transformer.TransformationRequest.HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_MEDIACODEC; import static com.google.android.exoplayer2.transformer.TransformationRequest.HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_MEDIACODEC;
import static com.google.android.exoplayer2.transformer.TransformationRequest.HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_OPEN_GL; import static com.google.android.exoplayer2.transformer.TransformationRequest.HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_OPEN_GL;
import static com.google.android.exoplayer2.util.Assertions.checkNotNull; import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
import static com.google.android.exoplayer2.util.Assertions.checkState;
import static com.google.android.exoplayer2.util.Util.SDK_INT; import static com.google.android.exoplayer2.util.Util.SDK_INT;
import static com.google.android.exoplayer2.video.ColorInfo.isTransferHdr; import static com.google.android.exoplayer2.video.ColorInfo.isTransferHdr;
@ -425,20 +423,16 @@ import org.checkerframework.dataflow.qual.Pure;
.setColorInfo(getSupportedInputColor()) .setColorInfo(getSupportedInputColor())
.build(); .build();
@Nullable
String supportedMimeType =
findSupportedMimeTypeForEncoderAndMuxer(
requestedOutputMimeType, muxerSupportedMimeTypes, requestedEncoderFormat.colorInfo);
if (supportedMimeType == null) {
throw createNoSupportedMimeTypeException(requestedEncoderFormat);
}
encoder = encoder =
encoderFactory.createForVideoEncoding( encoderFactory.createForVideoEncoding(
requestedEncoderFormat.buildUpon().setSampleMimeType(supportedMimeType).build()); requestedEncoderFormat
.buildUpon()
.setSampleMimeType(
findSupportedMimeTypeForEncoderAndMuxer(
requestedEncoderFormat, muxerSupportedMimeTypes))
.build());
Format encoderSupportedFormat = encoder.getConfigurationFormat(); Format actualEncoderFormat = encoder.getConfigurationFormat();
checkState(supportedMimeType.equals(encoderSupportedFormat.sampleMimeType));
boolean isInputToneMapped = boolean isInputToneMapped =
isTransferHdr(inputFormat.colorInfo) && !isTransferHdr(requestedEncoderFormat.colorInfo); isTransferHdr(inputFormat.colorInfo) && !isTransferHdr(requestedEncoderFormat.colorInfo);
@ -456,14 +450,14 @@ import org.checkerframework.dataflow.qual.Pure;
transformationRequest, transformationRequest,
/* hasOutputFormatRotation= */ outputRotationDegrees != 0, /* hasOutputFormatRotation= */ outputRotationDegrees != 0,
requestedEncoderFormat, requestedEncoderFormat,
encoderSupportedFormat, actualEncoderFormat,
supportedFallbackHdrMode)); supportedFallbackHdrMode));
encoderSurfaceInfo = encoderSurfaceInfo =
new SurfaceInfo( new SurfaceInfo(
encoder.getInputSurface(), encoder.getInputSurface(),
encoderSupportedFormat.width, actualEncoderFormat.width,
encoderSupportedFormat.height, actualEncoderFormat.height,
outputRotationDegrees); outputRotationDegrees);
if (releaseEncoder) { if (releaseEncoder) {
@ -516,52 +510,5 @@ import org.checkerframework.dataflow.qual.Pure;
} }
releaseEncoder = true; releaseEncoder = true;
} }
/**
* Finds a {@linkplain MimeTypes MIME type} that is supported by the encoder and the muxer.
*
* <p>HDR editing support is checked if the {@link ColorInfo} is HDR.
*
* @param preferredMimeType The preferred {@linkplain MimeTypes MIME type}, returned if
* supported.
* @param muxerSupportedMimeTypes The list of sample {@linkplain MimeTypes MIME types} that the
* muxer supports.
* @param colorInfo The optional encoding {@link ColorInfo}. If a HDR color info is provided,
* only encoders that support it will be considered.
* @return A {@linkplain MimeTypes MIME type} that is supported by an encoder and the muxer, or
* {@code null} if no such {@linkplain MimeTypes MIME type} exists.
*/
@Nullable
private static String findSupportedMimeTypeForEncoderAndMuxer(
String preferredMimeType,
List<String> muxerSupportedMimeTypes,
@Nullable ColorInfo colorInfo) {
ImmutableList<String> mimeTypesToCheck =
new ImmutableList.Builder<String>()
.add(preferredMimeType)
.add(MimeTypes.VIDEO_H265)
.add(MimeTypes.VIDEO_H264)
.addAll(muxerSupportedMimeTypes)
.build();
for (int i = 0; i < mimeTypesToCheck.size(); i++) {
String mimeType = mimeTypesToCheck.get(i);
if (mimeTypeAndColorAreSupported(mimeType, muxerSupportedMimeTypes, colorInfo)) {
return mimeType;
}
}
return null;
}
private static boolean mimeTypeAndColorAreSupported(
String mimeType, List<String> muxerSupportedMimeTypes, @Nullable ColorInfo colorInfo) {
if (!muxerSupportedMimeTypes.contains(mimeType)) {
return false;
}
return isTransferHdr(colorInfo)
? !getSupportedEncodersForHdrEditing(mimeType, colorInfo).isEmpty()
: !getSupportedEncoders(mimeType).isEmpty();
}
} }
} }