mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Move video encoding MIME type fallback to VTSP
Main change: - Removed `Codec.EncoderFactory.createForVideoEncoding`'s argument of a list of allowed MIME types - Moved the check for whether a video MIME type is supported to VTSP PiperOrigin-RevId: 491611799
This commit is contained in:
parent
04f031d168
commit
532e0ffdc3
10 changed files with 190 additions and 163 deletions
|
|
@ -478,9 +478,8 @@ public final class AndroidTestUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Codec createForVideoEncoding(Format format, List<String> allowedMimeTypes)
|
public Codec createForVideoEncoding(Format format) throws TransformationException {
|
||||||
throws TransformationException {
|
return encoderFactory.createForVideoEncoding(format);
|
||||||
return encoderFactory.createForVideoEncoding(format, allowedMimeTypes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -454,9 +454,8 @@ public class TransformerAndroidTestRunner {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Codec createForVideoEncoding(Format format, List<String> allowedMimeTypes)
|
public Codec createForVideoEncoding(Format format) throws TransformationException {
|
||||||
throws TransformationException {
|
Codec videoEncoder = encoderFactory.createForVideoEncoding(format);
|
||||||
Codec videoEncoder = encoderFactory.createForVideoEncoding(format, allowedMimeTypes);
|
|
||||||
videoEncoderName = videoEncoder.getName();
|
videoEncoderName = videoEncoder.getName();
|
||||||
return videoEncoder;
|
return videoEncoder;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -146,8 +146,7 @@ public class TransformerEndToEndTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Codec createForVideoEncoding(Format format, List<String> allowedMimeTypes)
|
public Codec createForVideoEncoding(Format format) throws TransformationException {
|
||||||
throws TransformationException {
|
|
||||||
throw TransformationException.createForCodec(
|
throw TransformationException.createForCodec(
|
||||||
new IllegalArgumentException(),
|
new IllegalArgumentException(),
|
||||||
/* isVideo= */ true,
|
/* isVideo= */ true,
|
||||||
|
|
|
||||||
|
|
@ -87,23 +87,22 @@ public interface Codec {
|
||||||
/**
|
/**
|
||||||
* Returns a {@link Codec} for video encoding.
|
* Returns a {@link Codec} for video encoding.
|
||||||
*
|
*
|
||||||
* <p>This method must validate that the {@link Codec} is configured to produce one of the
|
* <p>The caller should ensure the {@linkplain Format#sampleMimeType MIME type} is supported on
|
||||||
* {@code allowedMimeTypes}. The {@linkplain Format#sampleMimeType sample MIME type} given in
|
* the device before calling this method. If encoding to HDR, the caller should also ensure the
|
||||||
* {@code format} is not necessarily allowed.
|
* {@linkplain Format#colorInfo color characteristics} are supported.
|
||||||
*
|
*
|
||||||
* @param format The {@link Format} (of the output data) used to determine the underlying
|
* @param format The {@link Format} (of the output data) used to determine the underlying
|
||||||
* encoder and its configuration values. {@link Format#sampleMimeType}, {@link Format#width}
|
* encoder and its configuration values. {@link Format#sampleMimeType}, {@link Format#width}
|
||||||
* and {@link Format#height} are set to those of the desired output video format. {@link
|
* and {@link Format#height} are set to those of the desired output video format. {@link
|
||||||
* Format#rotationDegrees} is 0 and {@link Format#width} {@code >=} {@link Format#height},
|
* Format#frameRate} is set to the requested output frame rate, if available. {@link
|
||||||
* therefore the video is always in landscape orientation. {@link Format#frameRate} is set
|
* Format#colorInfo} is set to the requested output color characteristics, if available.
|
||||||
* to the output video's frame rate, if available.
|
* {@link Format#rotationDegrees} is 0 and {@link Format#width} {@code >=} {@link
|
||||||
* @param allowedMimeTypes The non-empty list of allowed output sample {@linkplain MimeTypes
|
* Format#height}, therefore the video is always in landscape orientation.
|
||||||
* MIME types}.
|
* @return A {@link Codec} for encoding video to the requested {@linkplain Format#sampleMimeType
|
||||||
* @return A {@link Codec} for video encoding.
|
* MIME type}.
|
||||||
* @throws TransformationException If no suitable {@link Codec} can be created.
|
* @throws TransformationException If no suitable {@link Codec} can be created.
|
||||||
*/
|
*/
|
||||||
Codec createForVideoEncoding(Format format, List<String> allowedMimeTypes)
|
Codec createForVideoEncoding(Format format) throws TransformationException;
|
||||||
throws TransformationException;
|
|
||||||
|
|
||||||
/** Returns whether the audio needs to be encoded because of encoder specific configuration. */
|
/** Returns whether the audio needs to be encoded because of encoder specific configuration. */
|
||||||
default boolean audioNeedsEncoding() {
|
default boolean audioNeedsEncoding() {
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
package com.google.android.exoplayer2.transformer;
|
package com.google.android.exoplayer2.transformer;
|
||||||
|
|
||||||
|
import static com.google.android.exoplayer2.transformer.TransformationException.ERROR_CODE_HDR_ENCODING_UNSUPPORTED;
|
||||||
import static com.google.android.exoplayer2.util.Assertions.checkArgument;
|
import static com.google.android.exoplayer2.util.Assertions.checkArgument;
|
||||||
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.Assertions.checkState;
|
||||||
|
|
@ -211,21 +212,9 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
|
||||||
* VideoEncoderSettings#bitrate} set to request for a specific encoding bitrate. Bitrate settings
|
* VideoEncoderSettings#bitrate} set to request for a specific encoding bitrate. Bitrate settings
|
||||||
* in {@link Format} are ignored when {@link VideoEncoderSettings#bitrate} or {@link
|
* in {@link Format} are ignored when {@link VideoEncoderSettings#bitrate} or {@link
|
||||||
* VideoEncoderSettings#enableHighQualityTargeting} is set.
|
* VideoEncoderSettings#enableHighQualityTargeting} is set.
|
||||||
*
|
|
||||||
* @param format The {@link Format} (of the output data) used to determine the underlying encoder
|
|
||||||
* and its configuration values. {@link Format#sampleMimeType}, {@link Format#width} and
|
|
||||||
* {@link Format#height} are set to those of the desired output video format. {@link
|
|
||||||
* Format#rotationDegrees} is 0 and {@link Format#width} {@code >=} {@link Format#height},
|
|
||||||
* therefore the video is always in landscape orientation. {@link Format#frameRate} is set to
|
|
||||||
* the output video's frame rate, if available.
|
|
||||||
* @param allowedMimeTypes The non-empty list of allowed output sample {@linkplain MimeTypes MIME
|
|
||||||
* types}.
|
|
||||||
* @return A {@link DefaultCodec} for video encoding.
|
|
||||||
* @throws TransformationException If no suitable {@link DefaultCodec} can be created.
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Codec createForVideoEncoding(Format format, List<String> allowedMimeTypes)
|
public DefaultCodec createForVideoEncoding(Format format) throws TransformationException {
|
||||||
throws TransformationException {
|
|
||||||
if (format.frameRate == Format.NO_VALUE) {
|
if (format.frameRate == Format.NO_VALUE) {
|
||||||
format = format.buildUpon().setFrameRate(DEFAULT_FRAME_RATE).build();
|
format = format.buildUpon().setFrameRate(DEFAULT_FRAME_RATE).build();
|
||||||
}
|
}
|
||||||
|
|
@ -236,17 +225,12 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
|
||||||
checkArgument(format.height <= format.width);
|
checkArgument(format.height <= format.width);
|
||||||
checkArgument(format.rotationDegrees == 0);
|
checkArgument(format.rotationDegrees == 0);
|
||||||
checkNotNull(format.sampleMimeType);
|
checkNotNull(format.sampleMimeType);
|
||||||
checkArgument(!allowedMimeTypes.isEmpty());
|
|
||||||
checkStateNotNull(videoEncoderSelector);
|
checkStateNotNull(videoEncoderSelector);
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
VideoEncoderQueryResult encoderAndClosestFormatSupport =
|
VideoEncoderQueryResult encoderAndClosestFormatSupport =
|
||||||
findEncoderWithClosestSupportedFormat(
|
findEncoderWithClosestSupportedFormat(
|
||||||
format,
|
format, requestedVideoEncoderSettings, videoEncoderSelector, enableFallback);
|
||||||
requestedVideoEncoderSettings,
|
|
||||||
videoEncoderSelector,
|
|
||||||
allowedMimeTypes,
|
|
||||||
enableFallback);
|
|
||||||
|
|
||||||
if (encoderAndClosestFormatSupport == null) {
|
if (encoderAndClosestFormatSupport == null) {
|
||||||
throw createTransformationException(format);
|
throw createTransformationException(format);
|
||||||
|
|
@ -310,13 +294,14 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
|
||||||
|
|
||||||
MediaFormatUtil.maybeSetColorInfo(mediaFormat, encoderSupportedFormat.colorInfo);
|
MediaFormatUtil.maybeSetColorInfo(mediaFormat, encoderSupportedFormat.colorInfo);
|
||||||
if (Util.SDK_INT >= 31 && ColorInfo.isTransferHdr(format.colorInfo)) {
|
if (Util.SDK_INT >= 31 && ColorInfo.isTransferHdr(format.colorInfo)) {
|
||||||
|
// TODO(b/260389841): Validate the picked encoder supports HDR editing.
|
||||||
if (EncoderUtil.getSupportedColorFormats(encoderInfo, mimeType)
|
if (EncoderUtil.getSupportedColorFormats(encoderInfo, mimeType)
|
||||||
.contains(MediaCodecInfo.CodecCapabilities.COLOR_Format32bitABGR2101010)) {
|
.contains(MediaCodecInfo.CodecCapabilities.COLOR_Format32bitABGR2101010)) {
|
||||||
mediaFormat.setInteger(
|
mediaFormat.setInteger(
|
||||||
MediaFormat.KEY_COLOR_FORMAT,
|
MediaFormat.KEY_COLOR_FORMAT,
|
||||||
MediaCodecInfo.CodecCapabilities.COLOR_Format32bitABGR2101010);
|
MediaCodecInfo.CodecCapabilities.COLOR_Format32bitABGR2101010);
|
||||||
} else {
|
} else {
|
||||||
throw createTransformationException(format);
|
throw createTransformationException(format, ERROR_CODE_HDR_ENCODING_UNSUPPORTED);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
mediaFormat.setInteger(
|
mediaFormat.setInteger(
|
||||||
|
|
@ -380,15 +365,8 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
|
||||||
Format requestedFormat,
|
Format requestedFormat,
|
||||||
VideoEncoderSettings videoEncoderSettings,
|
VideoEncoderSettings videoEncoderSettings,
|
||||||
EncoderSelector encoderSelector,
|
EncoderSelector encoderSelector,
|
||||||
List<String> allowedMimeTypes,
|
|
||||||
boolean enableFallback) {
|
boolean enableFallback) {
|
||||||
String requestedMimeType = requestedFormat.sampleMimeType;
|
String mimeType = checkNotNull(requestedFormat.sampleMimeType);
|
||||||
@Nullable
|
|
||||||
String mimeType = findFallbackMimeType(encoderSelector, requestedMimeType, allowedMimeTypes);
|
|
||||||
if (mimeType == null || (!enableFallback && !requestedMimeType.equals(mimeType))) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImmutableList<MediaCodecInfo> filteredEncoderInfos =
|
ImmutableList<MediaCodecInfo> filteredEncoderInfos =
|
||||||
encoderSelector.selectEncoderInfos(mimeType);
|
encoderSelector.selectEncoderInfos(mimeType);
|
||||||
if (filteredEncoderInfos.isEmpty()) {
|
if (filteredEncoderInfos.isEmpty()) {
|
||||||
|
|
@ -678,36 +656,6 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
|
||||||
return ImmutableList.copyOf(filteredEncoders);
|
return ImmutableList.copyOf(filteredEncoders);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Finds a {@linkplain MimeTypes MIME type} that is supported by the encoder and in the {@code
|
|
||||||
* allowedMimeTypes}.
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
private static String findFallbackMimeType(
|
|
||||||
EncoderSelector encoderSelector, String requestedMimeType, List<String> allowedMimeTypes) {
|
|
||||||
if (mimeTypeIsSupported(encoderSelector, requestedMimeType, allowedMimeTypes)) {
|
|
||||||
return requestedMimeType;
|
|
||||||
} else if (mimeTypeIsSupported(encoderSelector, MimeTypes.VIDEO_H265, allowedMimeTypes)) {
|
|
||||||
return MimeTypes.VIDEO_H265;
|
|
||||||
} else if (mimeTypeIsSupported(encoderSelector, MimeTypes.VIDEO_H264, allowedMimeTypes)) {
|
|
||||||
return MimeTypes.VIDEO_H264;
|
|
||||||
} else {
|
|
||||||
for (int i = 0; i < allowedMimeTypes.size(); i++) {
|
|
||||||
String allowedMimeType = allowedMimeTypes.get(i);
|
|
||||||
if (mimeTypeIsSupported(encoderSelector, allowedMimeType, allowedMimeTypes)) {
|
|
||||||
return allowedMimeType;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean mimeTypeIsSupported(
|
|
||||||
EncoderSelector encoderSelector, String mimeType, List<String> allowedMimeTypes) {
|
|
||||||
return !encoderSelector.selectEncoderInfos(mimeType).isEmpty()
|
|
||||||
&& allowedMimeTypes.contains(mimeType);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Computes the video bit rate using the Kush Gauge.
|
* Computes the video bit rate using the Kush Gauge.
|
||||||
*
|
*
|
||||||
|
|
@ -730,12 +678,19 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
|
||||||
|
|
||||||
@RequiresNonNull("#1.sampleMimeType")
|
@RequiresNonNull("#1.sampleMimeType")
|
||||||
private static TransformationException createTransformationException(Format format) {
|
private static TransformationException createTransformationException(Format format) {
|
||||||
|
return createTransformationException(
|
||||||
|
format, TransformationException.ERROR_CODE_OUTPUT_FORMAT_UNSUPPORTED);
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresNonNull("#1.sampleMimeType")
|
||||||
|
private static TransformationException createTransformationException(
|
||||||
|
Format format, @TransformationException.ErrorCode int errorCode) {
|
||||||
return TransformationException.createForCodec(
|
return TransformationException.createForCodec(
|
||||||
new IllegalArgumentException("The requested encoding format is not supported."),
|
new IllegalArgumentException("The requested encoding format is not supported."),
|
||||||
MimeTypes.isVideo(format.sampleMimeType),
|
MimeTypes.isVideo(format.sampleMimeType),
|
||||||
/* isDecoder= */ false,
|
/* isDecoder= */ false,
|
||||||
format,
|
format,
|
||||||
/* mediaCodecName= */ null,
|
/* mediaCodecName= */ null,
|
||||||
TransformationException.ERROR_CODE_OUTPUT_FORMAT_UNSUPPORTED);
|
errorCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -69,8 +69,8 @@ public final class EncoderUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the names of encoders that support HDR editing for the given format, or an empty list
|
* Returns the names of encoders that support HDR editing for the given {@code mimeType} and
|
||||||
* if the format is unknown or not supported for HDR encoding.
|
* {@code ColorInfo}, or an empty list if the format is unknown or not supported for HDR encoding.
|
||||||
*/
|
*/
|
||||||
public static ImmutableList<String> getSupportedEncoderNamesForHdrEditing(
|
public static ImmutableList<String> getSupportedEncoderNamesForHdrEditing(
|
||||||
String mimeType, @Nullable ColorInfo colorInfo) {
|
String mimeType, @Nullable ColorInfo colorInfo) {
|
||||||
|
|
@ -78,13 +78,12 @@ public final class EncoderUtil {
|
||||||
return ImmutableList.of();
|
return ImmutableList.of();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ColorTransfer int colorTransfer = colorInfo.colorTransfer;
|
ImmutableList<MediaCodecInfo> encoders = getSupportedEncoders(mimeType);
|
||||||
ImmutableList<Integer> profiles = getCodecProfilesForHdrFormat(mimeType, colorTransfer);
|
ImmutableList<Integer> allowedColorProfiles =
|
||||||
ImmutableList.Builder<String> resultBuilder = ImmutableList.builder();
|
getCodecProfilesForHdrFormat(mimeType, colorInfo.colorTransfer);
|
||||||
ImmutableList<MediaCodecInfo> mediaCodecInfos =
|
ImmutableList.Builder<String> resultBuilder = new ImmutableList.Builder<>();
|
||||||
EncoderSelector.DEFAULT.selectEncoderInfos(mimeType);
|
for (int i = 0; i < encoders.size(); i++) {
|
||||||
for (int i = 0; i < mediaCodecInfos.size(); i++) {
|
MediaCodecInfo mediaCodecInfo = encoders.get(i);
|
||||||
MediaCodecInfo mediaCodecInfo = mediaCodecInfos.get(i);
|
|
||||||
if (mediaCodecInfo.isAlias()
|
if (mediaCodecInfo.isAlias()
|
||||||
|| !isFeatureSupported(
|
|| !isFeatureSupported(
|
||||||
mediaCodecInfo, mimeType, MediaCodecInfo.CodecCapabilities.FEATURE_HdrEditing)) {
|
mediaCodecInfo, mimeType, MediaCodecInfo.CodecCapabilities.FEATURE_HdrEditing)) {
|
||||||
|
|
@ -92,7 +91,7 @@ public final class EncoderUtil {
|
||||||
}
|
}
|
||||||
for (MediaCodecInfo.CodecProfileLevel codecProfileLevel :
|
for (MediaCodecInfo.CodecProfileLevel codecProfileLevel :
|
||||||
mediaCodecInfo.getCapabilitiesForType(mimeType).profileLevels) {
|
mediaCodecInfo.getCapabilitiesForType(mimeType).profileLevels) {
|
||||||
if (profiles.contains(codecProfileLevel.profile)) {
|
if (allowedColorProfiles.contains(codecProfileLevel.profile)) {
|
||||||
resultBuilder.add(mediaCodecInfo.getName());
|
resultBuilder.add(mediaCodecInfo.getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package com.google.android.exoplayer2.transformer;
|
package com.google.android.exoplayer2.transformer;
|
||||||
|
|
||||||
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 android.content.Context;
|
import android.content.Context;
|
||||||
|
|
@ -36,6 +37,7 @@ import com.google.android.exoplayer2.util.FrameInfo;
|
||||||
import com.google.android.exoplayer2.util.FrameProcessingException;
|
import com.google.android.exoplayer2.util.FrameProcessingException;
|
||||||
import com.google.android.exoplayer2.util.FrameProcessor;
|
import com.google.android.exoplayer2.util.FrameProcessor;
|
||||||
import com.google.android.exoplayer2.util.Log;
|
import com.google.android.exoplayer2.util.Log;
|
||||||
|
import com.google.android.exoplayer2.util.MimeTypes;
|
||||||
import com.google.android.exoplayer2.util.SurfaceInfo;
|
import com.google.android.exoplayer2.util.SurfaceInfo;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
import com.google.android.exoplayer2.video.ColorInfo;
|
import com.google.android.exoplayer2.video.ColorInfo;
|
||||||
|
|
@ -369,7 +371,7 @@ import org.checkerframework.dataflow.qual.Pure;
|
||||||
|
|
||||||
private final Codec.EncoderFactory encoderFactory;
|
private final Codec.EncoderFactory encoderFactory;
|
||||||
private final Format inputFormat;
|
private final Format inputFormat;
|
||||||
private final List<String> allowedOutputMimeTypes;
|
private final List<String> muxerSupportedMimeTypes;
|
||||||
private final TransformationRequest transformationRequest;
|
private final TransformationRequest transformationRequest;
|
||||||
private final FallbackListener fallbackListener;
|
private final FallbackListener fallbackListener;
|
||||||
private final String requestedOutputMimeType;
|
private final String requestedOutputMimeType;
|
||||||
|
|
@ -384,12 +386,12 @@ import org.checkerframework.dataflow.qual.Pure;
|
||||||
public EncoderWrapper(
|
public EncoderWrapper(
|
||||||
Codec.EncoderFactory encoderFactory,
|
Codec.EncoderFactory encoderFactory,
|
||||||
Format inputFormat,
|
Format inputFormat,
|
||||||
List<String> allowedOutputMimeTypes,
|
List<String> muxerSupportedMimeTypes,
|
||||||
TransformationRequest transformationRequest,
|
TransformationRequest transformationRequest,
|
||||||
FallbackListener fallbackListener) {
|
FallbackListener fallbackListener) {
|
||||||
this.encoderFactory = encoderFactory;
|
this.encoderFactory = encoderFactory;
|
||||||
this.inputFormat = inputFormat;
|
this.inputFormat = inputFormat;
|
||||||
this.allowedOutputMimeTypes = allowedOutputMimeTypes;
|
this.muxerSupportedMimeTypes = muxerSupportedMimeTypes;
|
||||||
this.transformationRequest = transformationRequest;
|
this.transformationRequest = transformationRequest;
|
||||||
this.fallbackListener = fallbackListener;
|
this.fallbackListener = fallbackListener;
|
||||||
|
|
||||||
|
|
@ -454,21 +456,32 @@ import org.checkerframework.dataflow.qual.Pure;
|
||||||
.setColorInfo(getSupportedInputColor())
|
.setColorInfo(getSupportedInputColor())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
String supportedMimeType =
|
||||||
|
selectEncoderAndMuxerSupportedMimeType(
|
||||||
|
requestedOutputMimeType, muxerSupportedMimeTypes, requestedEncoderFormat.colorInfo);
|
||||||
|
if (supportedMimeType == null) {
|
||||||
|
throw TransformationException.createForCodec(
|
||||||
|
new IllegalArgumentException("No MIME type is supported by both encoder and muxer."),
|
||||||
|
/* isVideo= */ true,
|
||||||
|
/* isDecoder= */ false,
|
||||||
|
requestedEncoderFormat,
|
||||||
|
/* mediaCodecName= */ null,
|
||||||
|
TransformationException.ERROR_CODE_OUTPUT_FORMAT_UNSUPPORTED);
|
||||||
|
}
|
||||||
|
|
||||||
encoder =
|
encoder =
|
||||||
encoderFactory.createForVideoEncoding(requestedEncoderFormat, allowedOutputMimeTypes);
|
encoderFactory.createForVideoEncoding(
|
||||||
|
requestedEncoderFormat.buildUpon().setSampleMimeType(supportedMimeType).build());
|
||||||
|
|
||||||
Format encoderSupportedFormat = encoder.getConfigurationFormat();
|
Format encoderSupportedFormat = encoder.getConfigurationFormat();
|
||||||
if (ColorInfo.isTransferHdr(requestedEncoderFormat.colorInfo)) {
|
checkState(supportedMimeType.equals(encoderSupportedFormat.sampleMimeType));
|
||||||
if (!requestedOutputMimeType.equals(encoderSupportedFormat.sampleMimeType)) {
|
if (ColorInfo.isTransferHdr(requestedEncoderFormat.colorInfo)
|
||||||
throw createEncodingException(
|
&& !supportedEncoderNamesForHdrEditing.contains(encoder.getName())) {
|
||||||
new IllegalStateException("MIME type fallback unsupported with HDR editing"),
|
|
||||||
encoderSupportedFormat);
|
|
||||||
} else if (!supportedEncoderNamesForHdrEditing.contains(encoder.getName())) {
|
|
||||||
throw createEncodingException(
|
throw createEncodingException(
|
||||||
new IllegalStateException("Selected encoder doesn't support HDR editing"),
|
new IllegalStateException("Selected encoder doesn't support HDR editing"),
|
||||||
encoderSupportedFormat);
|
encoderSupportedFormat);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
boolean isInputToneMapped =
|
boolean isInputToneMapped =
|
||||||
ColorInfo.isTransferHdr(inputFormat.colorInfo)
|
ColorInfo.isTransferHdr(inputFormat.colorInfo)
|
||||||
&& !ColorInfo.isTransferHdr(requestedEncoderFormat.colorInfo);
|
&& !ColorInfo.isTransferHdr(requestedEncoderFormat.colorInfo);
|
||||||
|
|
@ -553,5 +566,48 @@ import org.checkerframework.dataflow.qual.Pure;
|
||||||
checkNotNull(encoder).getName(),
|
checkNotNull(encoder).getName(),
|
||||||
TransformationException.ERROR_CODE_ENCODING_FAILED);
|
TransformationException.ERROR_CODE_ENCODING_FAILED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds a {@linkplain MimeTypes MIME type} that is supported by both the encoder and the muxer.
|
||||||
|
*
|
||||||
|
* @param requestedMimeType The requested {@linkplain MimeTypes MIME type}.
|
||||||
|
* @param muxerSupportedMimeTypes The list of sample {@linkplain MimeTypes MIME types} that the
|
||||||
|
* muxer supports.
|
||||||
|
* @param colorInfo The requested encoding {@link ColorInfo}, if available.
|
||||||
|
* @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 selectEncoderAndMuxerSupportedMimeType(
|
||||||
|
String requestedMimeType,
|
||||||
|
List<String> muxerSupportedMimeTypes,
|
||||||
|
@Nullable ColorInfo colorInfo) {
|
||||||
|
ImmutableList<String> mimeTypesToCheck =
|
||||||
|
new ImmutableList.Builder<String>()
|
||||||
|
.add(requestedMimeType)
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
if (ColorInfo.isTransferHdr(colorInfo)) {
|
||||||
|
return !EncoderUtil.getSupportedEncoderNamesForHdrEditing(mimeType, colorInfo).isEmpty();
|
||||||
|
}
|
||||||
|
return !EncoderUtil.getSupportedEncoders(mimeType).isEmpty();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package com.google.android.exoplayer2.transformer;
|
package com.google.android.exoplayer2.transformer;
|
||||||
|
|
||||||
import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
|
import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
|
||||||
|
import static com.google.android.exoplayer2.transformer.TransformationException.ERROR_CODE_OUTPUT_FORMAT_UNSUPPORTED;
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
import static org.junit.Assert.assertThrows;
|
import static org.junit.Assert.assertThrows;
|
||||||
|
|
||||||
|
|
@ -84,9 +85,7 @@ public class DefaultEncoderFactoryTest {
|
||||||
Format actualVideoFormat =
|
Format actualVideoFormat =
|
||||||
new DefaultEncoderFactory.Builder(context)
|
new DefaultEncoderFactory.Builder(context)
|
||||||
.build()
|
.build()
|
||||||
.createForVideoEncoding(
|
.createForVideoEncoding(requestedVideoFormat)
|
||||||
requestedVideoFormat,
|
|
||||||
/* allowedMimeTypes= */ ImmutableList.of(MimeTypes.VIDEO_H264))
|
|
||||||
.getConfigurationFormat();
|
.getConfigurationFormat();
|
||||||
|
|
||||||
assertThat(actualVideoFormat.sampleMimeType).isEqualTo(MimeTypes.VIDEO_H264);
|
assertThat(actualVideoFormat.sampleMimeType).isEqualTo(MimeTypes.VIDEO_H264);
|
||||||
|
|
@ -97,22 +96,15 @@ public class DefaultEncoderFactoryTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void createForVideoEncoding_withFallbackOnAndUnsupportedMimeType_configuresEncoder()
|
public void createForVideoEncoding_withFallbackOnAndUnsupportedMimeType_throws() {
|
||||||
throws Exception {
|
|
||||||
Format requestedVideoFormat = createVideoFormat(MimeTypes.VIDEO_H265, 1920, 1080, 30);
|
Format requestedVideoFormat = createVideoFormat(MimeTypes.VIDEO_H265, 1920, 1080, 30);
|
||||||
Format actualVideoFormat =
|
DefaultEncoderFactory encoderFactory = new DefaultEncoderFactory.Builder(context).build();
|
||||||
new DefaultEncoderFactory.Builder(context)
|
|
||||||
.build()
|
|
||||||
.createForVideoEncoding(
|
|
||||||
requestedVideoFormat,
|
|
||||||
/* allowedMimeTypes= */ ImmutableList.of(MimeTypes.VIDEO_H264))
|
|
||||||
.getConfigurationFormat();
|
|
||||||
|
|
||||||
assertThat(actualVideoFormat.sampleMimeType).isEqualTo(MimeTypes.VIDEO_H264);
|
TransformationException transformationException =
|
||||||
assertThat(actualVideoFormat.width).isEqualTo(1920);
|
assertThrows(
|
||||||
assertThat(actualVideoFormat.height).isEqualTo(1080);
|
TransformationException.class,
|
||||||
// 1920 * 1080 * 30 * 0.07 * 2.
|
() -> encoderFactory.createForVideoEncoding(requestedVideoFormat));
|
||||||
assertThat(actualVideoFormat.averageBitrate).isEqualTo(8_709_120);
|
assertThat(transformationException.errorCode).isEqualTo(ERROR_CODE_OUTPUT_FORMAT_UNSUPPORTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -122,9 +114,7 @@ public class DefaultEncoderFactoryTest {
|
||||||
Format actualVideoFormat =
|
Format actualVideoFormat =
|
||||||
new DefaultEncoderFactory.Builder(context)
|
new DefaultEncoderFactory.Builder(context)
|
||||||
.build()
|
.build()
|
||||||
.createForVideoEncoding(
|
.createForVideoEncoding(requestedVideoFormat)
|
||||||
requestedVideoFormat,
|
|
||||||
/* allowedMimeTypes= */ ImmutableList.of(MimeTypes.VIDEO_H264))
|
|
||||||
.getConfigurationFormat();
|
.getConfigurationFormat();
|
||||||
|
|
||||||
assertThat(actualVideoFormat.width).isEqualTo(1920);
|
assertThat(actualVideoFormat.width).isEqualTo(1920);
|
||||||
|
|
@ -142,9 +132,7 @@ public class DefaultEncoderFactoryTest {
|
||||||
new DefaultEncoderFactory.Builder(context)
|
new DefaultEncoderFactory.Builder(context)
|
||||||
.setRequestedVideoEncoderSettings(VideoEncoderSettings.DEFAULT)
|
.setRequestedVideoEncoderSettings(VideoEncoderSettings.DEFAULT)
|
||||||
.build()
|
.build()
|
||||||
.createForVideoEncoding(
|
.createForVideoEncoding(requestedVideoFormat)
|
||||||
requestedVideoFormat,
|
|
||||||
/* allowedMimeTypes= */ ImmutableList.of(MimeTypes.VIDEO_H264))
|
|
||||||
.getConfigurationFormat();
|
.getConfigurationFormat();
|
||||||
|
|
||||||
assertThat(actualVideoFormat.sampleMimeType).isEqualTo(MimeTypes.VIDEO_H264);
|
assertThat(actualVideoFormat.sampleMimeType).isEqualTo(MimeTypes.VIDEO_H264);
|
||||||
|
|
@ -161,9 +149,7 @@ public class DefaultEncoderFactoryTest {
|
||||||
Format actualVideoFormat =
|
Format actualVideoFormat =
|
||||||
new DefaultEncoderFactory.Builder(context)
|
new DefaultEncoderFactory.Builder(context)
|
||||||
.build()
|
.build()
|
||||||
.createForVideoEncoding(
|
.createForVideoEncoding(requestedVideoFormat)
|
||||||
requestedVideoFormat,
|
|
||||||
/* allowedMimeTypes= */ ImmutableList.of(MimeTypes.VIDEO_H264))
|
|
||||||
.getConfigurationFormat();
|
.getConfigurationFormat();
|
||||||
|
|
||||||
assertThat(actualVideoFormat.sampleMimeType).isEqualTo(MimeTypes.VIDEO_H264);
|
assertThat(actualVideoFormat.sampleMimeType).isEqualTo(MimeTypes.VIDEO_H264);
|
||||||
|
|
@ -185,9 +171,7 @@ public class DefaultEncoderFactoryTest {
|
||||||
.setRequestedVideoEncoderSettings(
|
.setRequestedVideoEncoderSettings(
|
||||||
new VideoEncoderSettings.Builder().setEnableHighQualityTargeting(true).build())
|
new VideoEncoderSettings.Builder().setEnableHighQualityTargeting(true).build())
|
||||||
.build()
|
.build()
|
||||||
.createForVideoEncoding(
|
.createForVideoEncoding(requestedVideoFormat)
|
||||||
requestedVideoFormat,
|
|
||||||
/* allowedMimeTypes= */ ImmutableList.of(MimeTypes.VIDEO_H264))
|
|
||||||
.getConfigurationFormat();
|
.getConfigurationFormat();
|
||||||
|
|
||||||
assertThat(actualVideoFormat.sampleMimeType).isEqualTo(MimeTypes.VIDEO_H264);
|
assertThat(actualVideoFormat.sampleMimeType).isEqualTo(MimeTypes.VIDEO_H264);
|
||||||
|
|
@ -210,9 +194,7 @@ public class DefaultEncoderFactoryTest {
|
||||||
.setRequestedVideoEncoderSettings(
|
.setRequestedVideoEncoderSettings(
|
||||||
new VideoEncoderSettings.Builder().setBitrate(10_000_000).build())
|
new VideoEncoderSettings.Builder().setBitrate(10_000_000).build())
|
||||||
.build()
|
.build()
|
||||||
.createForVideoEncoding(
|
.createForVideoEncoding(requestedVideoFormat)
|
||||||
requestedVideoFormat,
|
|
||||||
/* allowedMimeTypes= */ ImmutableList.of(MimeTypes.VIDEO_H264))
|
|
||||||
.getConfigurationFormat();
|
.getConfigurationFormat();
|
||||||
|
|
||||||
assertThat(actualVideoFormat.sampleMimeType).isEqualTo(MimeTypes.VIDEO_H264);
|
assertThat(actualVideoFormat.sampleMimeType).isEqualTo(MimeTypes.VIDEO_H264);
|
||||||
|
|
@ -230,9 +212,7 @@ public class DefaultEncoderFactoryTest {
|
||||||
Codec videoEncoder =
|
Codec videoEncoder =
|
||||||
new DefaultEncoderFactory.Builder(context)
|
new DefaultEncoderFactory.Builder(context)
|
||||||
.build()
|
.build()
|
||||||
.createForVideoEncoding(
|
.createForVideoEncoding(requestedVideoFormat);
|
||||||
requestedVideoFormat,
|
|
||||||
/* allowedMimeTypes= */ ImmutableList.of(MimeTypes.VIDEO_H264));
|
|
||||||
|
|
||||||
assertThat(videoEncoder).isInstanceOf(DefaultCodec.class);
|
assertThat(videoEncoder).isInstanceOf(DefaultCodec.class);
|
||||||
MediaFormat configurationMediaFormat =
|
MediaFormat configurationMediaFormat =
|
||||||
|
|
@ -244,25 +224,6 @@ public class DefaultEncoderFactoryTest {
|
||||||
.isEqualTo(Integer.MAX_VALUE);
|
.isEqualTo(Integer.MAX_VALUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void createForVideoEncoding_withNoSupportedEncoder_throws() {
|
|
||||||
Format requestedVideoFormat = createVideoFormat(MimeTypes.VIDEO_H264, 1920, 1080, 30);
|
|
||||||
|
|
||||||
TransformationException exception =
|
|
||||||
assertThrows(
|
|
||||||
TransformationException.class,
|
|
||||||
() ->
|
|
||||||
new DefaultEncoderFactory.Builder(context)
|
|
||||||
.build()
|
|
||||||
.createForVideoEncoding(
|
|
||||||
requestedVideoFormat,
|
|
||||||
/* allowedMimeTypes= */ ImmutableList.of(MimeTypes.VIDEO_H265)));
|
|
||||||
|
|
||||||
assertThat(exception).hasCauseThat().isInstanceOf(IllegalArgumentException.class);
|
|
||||||
assertThat(exception.errorCode)
|
|
||||||
.isEqualTo(TransformationException.ERROR_CODE_OUTPUT_FORMAT_UNSUPPORTED);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void createForVideoEncoding_withNoAvailableEncoderFromEncoderSelector_throws() {
|
public void createForVideoEncoding_withNoAvailableEncoderFromEncoderSelector_throws() {
|
||||||
Format requestedVideoFormat = createVideoFormat(MimeTypes.VIDEO_H264, 1920, 1080, 30);
|
Format requestedVideoFormat = createVideoFormat(MimeTypes.VIDEO_H264, 1920, 1080, 30);
|
||||||
|
|
@ -270,11 +231,9 @@ public class DefaultEncoderFactoryTest {
|
||||||
TransformationException.class,
|
TransformationException.class,
|
||||||
() ->
|
() ->
|
||||||
new DefaultEncoderFactory.Builder(context)
|
new DefaultEncoderFactory.Builder(context)
|
||||||
.setVideoEncoderSelector(mimeType -> ImmutableList.of())
|
.setVideoEncoderSelector((mimeType) -> ImmutableList.of())
|
||||||
.build()
|
.build()
|
||||||
.createForVideoEncoding(
|
.createForVideoEncoding(requestedVideoFormat));
|
||||||
requestedVideoFormat,
|
|
||||||
/* allowedMimeTypes= */ ImmutableList.of(MimeTypes.VIDEO_H264)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Format createVideoFormat(String mimeType, int width, int height, int frameRate) {
|
private static Format createVideoFormat(String mimeType, int width, int height, int frameRate) {
|
||||||
|
|
|
||||||
|
|
@ -23,11 +23,14 @@ import android.media.MediaFormat;
|
||||||
import android.util.Size;
|
import android.util.Size;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.util.MimeTypes;
|
import com.google.android.exoplayer2.util.MimeTypes;
|
||||||
|
import com.google.android.exoplayer2.video.ColorInfo;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
import org.robolectric.annotation.Config;
|
||||||
import org.robolectric.shadows.MediaCodecInfoBuilder;
|
import org.robolectric.shadows.MediaCodecInfoBuilder;
|
||||||
import org.robolectric.shadows.ShadowMediaCodecList;
|
import org.robolectric.shadows.ShadowMediaCodecList;
|
||||||
|
|
||||||
|
|
@ -116,4 +119,25 @@ public class EncoderUtilTest {
|
||||||
assertThat(closestSupportedResolution.getWidth()).isEqualTo(1920);
|
assertThat(closestSupportedResolution.getWidth()).isEqualTo(1920);
|
||||||
assertThat(closestSupportedResolution.getHeight()).isEqualTo(1080);
|
assertThat(closestSupportedResolution.getHeight()).isEqualTo(1080);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see EncoderUtil#getSupportedEncoderNamesForHdrEditing(String, ColorInfo)
|
||||||
|
*/
|
||||||
|
@Config(sdk = {30, 31})
|
||||||
|
@Test
|
||||||
|
public void getSupportedEncoderNamesForHdrEditing_returnsEmptyList() {
|
||||||
|
// This test is run on 30 and 31 because the tested logic differentiate at API31.
|
||||||
|
// getSupportedEncoderNamesForHdrEditing returns an empty list for API < 31. It returns an empty
|
||||||
|
// list for API >= 31 as well, because currently it is not possible to make ShadowMediaCodec
|
||||||
|
// support HDR.
|
||||||
|
assertThat(
|
||||||
|
EncoderUtil.getSupportedEncoderNamesForHdrEditing(
|
||||||
|
MIME_TYPE,
|
||||||
|
new ColorInfo(
|
||||||
|
C.COLOR_SPACE_BT2020,
|
||||||
|
C.COLOR_RANGE_FULL,
|
||||||
|
C.COLOR_TRANSFER_HLG,
|
||||||
|
/* hdrStaticInfo= */ null)))
|
||||||
|
.isEmpty();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,8 @@ import static com.google.common.truth.Truth.assertThat;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import android.media.MediaCodecInfo;
|
||||||
|
import android.media.MediaFormat;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
|
@ -34,6 +36,8 @@ import java.util.List;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
import org.robolectric.shadows.MediaCodecInfoBuilder;
|
||||||
|
import org.robolectric.shadows.ShadowMediaCodecList;
|
||||||
|
|
||||||
/** Unit tests for {@link VideoTranscodingSamplePipeline.EncoderWrapper}. */
|
/** Unit tests for {@link VideoTranscodingSamplePipeline.EncoderWrapper}. */
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
|
@ -49,14 +53,15 @@ public final class VideoEncoderWrapperTest {
|
||||||
private final VideoTranscodingSamplePipeline.EncoderWrapper encoderWrapper =
|
private final VideoTranscodingSamplePipeline.EncoderWrapper encoderWrapper =
|
||||||
new VideoTranscodingSamplePipeline.EncoderWrapper(
|
new VideoTranscodingSamplePipeline.EncoderWrapper(
|
||||||
fakeEncoderFactory,
|
fakeEncoderFactory,
|
||||||
/* inputFormat= */ new Format.Builder().setSampleMimeType(MimeTypes.VIDEO_H265).build(),
|
/* inputFormat= */ new Format.Builder().setSampleMimeType(MimeTypes.VIDEO_H264).build(),
|
||||||
/* allowedOutputMimeTypes= */ ImmutableList.of(),
|
/* muxerSupportedMimeTypes= */ ImmutableList.of(MimeTypes.VIDEO_H264),
|
||||||
emptyTransformationRequest,
|
emptyTransformationRequest,
|
||||||
fallbackListener);
|
fallbackListener);
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void registerTrack() {
|
public void registerTrack() {
|
||||||
fallbackListener.registerTrack();
|
fallbackListener.registerTrack();
|
||||||
|
createShadowH264Encoder();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -111,6 +116,39 @@ public final class VideoEncoderWrapperTest {
|
||||||
assertThat(surfaceInfo.height).isEqualTo(fallbackHeight);
|
assertThat(surfaceInfo.height).isEqualTo(fallbackHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void createShadowH264Encoder() {
|
||||||
|
MediaFormat avcFormat = new MediaFormat();
|
||||||
|
avcFormat.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_VIDEO_AVC);
|
||||||
|
MediaCodecInfo.CodecProfileLevel profileLevel = new MediaCodecInfo.CodecProfileLevel();
|
||||||
|
profileLevel.profile = MediaCodecInfo.CodecProfileLevel.AVCProfileHigh;
|
||||||
|
// Using Level4 gives us 8192 16x16 blocks. If using width 1920 uses 120 blocks, 8192 / 120 = 68
|
||||||
|
// blocks will be left for encoding height 1088.
|
||||||
|
profileLevel.level = MediaCodecInfo.CodecProfileLevel.AVCLevel4;
|
||||||
|
|
||||||
|
createShadowVideoEncoder(avcFormat, profileLevel, "test.transformer.avc.encoder");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void createShadowVideoEncoder(
|
||||||
|
MediaFormat supportedFormat,
|
||||||
|
MediaCodecInfo.CodecProfileLevel supportedProfileLevel,
|
||||||
|
String name) {
|
||||||
|
// ShadowMediaCodecList is static. The added encoders will be visible for every test.
|
||||||
|
ShadowMediaCodecList.addCodec(
|
||||||
|
MediaCodecInfoBuilder.newBuilder()
|
||||||
|
.setName(name)
|
||||||
|
.setIsEncoder(true)
|
||||||
|
.setCapabilities(
|
||||||
|
MediaCodecInfoBuilder.CodecCapabilitiesBuilder.newBuilder()
|
||||||
|
.setMediaFormat(supportedFormat)
|
||||||
|
.setIsEncoder(true)
|
||||||
|
.setColorFormats(
|
||||||
|
new int[] {MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible})
|
||||||
|
.setProfileLevels(
|
||||||
|
new MediaCodecInfo.CodecProfileLevel[] {supportedProfileLevel})
|
||||||
|
.build())
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
|
||||||
private static class FakeVideoEncoderFactory implements Codec.EncoderFactory {
|
private static class FakeVideoEncoderFactory implements Codec.EncoderFactory {
|
||||||
|
|
||||||
private int fallbackWidth;
|
private int fallbackWidth;
|
||||||
|
|
@ -132,7 +170,7 @@ public final class VideoEncoderWrapperTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Codec createForVideoEncoding(Format format, List<String> allowedMimeTypes) {
|
public Codec createForVideoEncoding(Format format) {
|
||||||
Codec mockEncoder = mock(Codec.class);
|
Codec mockEncoder = mock(Codec.class);
|
||||||
if (fallbackWidth != C.LENGTH_UNSET) {
|
if (fallbackWidth != C.LENGTH_UNSET) {
|
||||||
format = format.buildUpon().setWidth(fallbackWidth).build();
|
format = format.buildUpon().setWidth(fallbackWidth).build();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue