Set assumedVideoMinimumCodecOperatingRate for all playbacks

PiperOrigin-RevId: 405736227
This commit is contained in:
olly 2021-10-26 22:30:30 +01:00 committed by Andrew Lewis
parent 933e207b3e
commit aff15ae9ee
4 changed files with 112 additions and 19 deletions

View File

@ -208,7 +208,8 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
/* enableDecoderFallback= */ false, /* enableDecoderFallback= */ false,
eventHandler, eventHandler,
eventListener, eventListener,
maxDroppedFramesToNotify); maxDroppedFramesToNotify,
/* assumedMinimumCodecOperatingRate= */ 30);
} }
/** /**
@ -241,12 +242,11 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
enableDecoderFallback, enableDecoderFallback,
eventHandler, eventHandler,
eventListener, eventListener,
maxDroppedFramesToNotify); maxDroppedFramesToNotify,
/* assumedMinimumCodecOperatingRate= */ 30);
} }
/** /**
* Creates a new instance.
*
* @param context A context. * @param context A context.
* @param codecAdapterFactory The {@link MediaCodecAdapter.Factory} used to create {@link * @param codecAdapterFactory The {@link MediaCodecAdapter.Factory} used to create {@link
* MediaCodecAdapter} instances. * MediaCodecAdapter} instances.
@ -271,12 +271,56 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
@Nullable Handler eventHandler, @Nullable Handler eventHandler,
@Nullable VideoRendererEventListener eventListener, @Nullable VideoRendererEventListener eventListener,
int maxDroppedFramesToNotify) { int maxDroppedFramesToNotify) {
this(
context,
codecAdapterFactory,
mediaCodecSelector,
allowedJoiningTimeMs,
enableDecoderFallback,
eventHandler,
eventListener,
maxDroppedFramesToNotify,
/* assumedMinimumCodecOperatingRate= */ 30);
}
/**
* Creates a new instance.
*
* @param context A context.
* @param codecAdapterFactory The {@link MediaCodecAdapter.Factory} used to create {@link
* MediaCodecAdapter} instances.
* @param mediaCodecSelector A decoder selector.
* @param allowedJoiningTimeMs The maximum duration in milliseconds for which this video renderer
* can attempt to seamlessly join an ongoing playback.
* @param enableDecoderFallback Whether to enable fallback to lower-priority decoders if decoder
* initialization fails. This may result in using a decoder that is slower/less efficient than
* the primary decoder.
* @param eventHandler A handler to use when delivering events to {@code eventListener}. May be
* null if delivery of events is not required.
* @param eventListener A listener of events. May be null if delivery of events is not required.
* @param maxDroppedFramesToNotify The maximum number of frames that can be dropped between
* invocations of {@link VideoRendererEventListener#onDroppedFrames(int, long)}.
* @param assumedMinimumCodecOperatingRate A codec operating rate that all codecs instantiated by
* this renderer are assumed to meet implicitly (i.e. without the operating rate being set
* explicitly using {@link MediaFormat#KEY_OPERATING_RATE}).
*/
public MediaCodecVideoRenderer(
Context context,
MediaCodecAdapter.Factory codecAdapterFactory,
MediaCodecSelector mediaCodecSelector,
long allowedJoiningTimeMs,
boolean enableDecoderFallback,
@Nullable Handler eventHandler,
@Nullable VideoRendererEventListener eventListener,
int maxDroppedFramesToNotify,
float assumedMinimumCodecOperatingRate) {
super( super(
C.TRACK_TYPE_VIDEO, C.TRACK_TYPE_VIDEO,
codecAdapterFactory, codecAdapterFactory,
mediaCodecSelector, mediaCodecSelector,
enableDecoderFallback, enableDecoderFallback,
/* assumedMinimumCodecOperatingRate= */ 30); assumedMinimumCodecOperatingRate);
this.allowedJoiningTimeMs = allowedJoiningTimeMs; this.allowedJoiningTimeMs = allowedJoiningTimeMs;
this.maxDroppedFramesToNotify = maxDroppedFramesToNotify; this.maxDroppedFramesToNotify = maxDroppedFramesToNotify;
this.context = context.getApplicationContext(); this.context = context.getApplicationContext();

View File

@ -1435,7 +1435,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
int esdsAtomPosition = int esdsAtomPosition =
childAtomType == Atom.TYPE_esds childAtomType == Atom.TYPE_esds
? childPosition ? childPosition
: findEsdsPosition(parent, childPosition, childAtomSize); : findBoxPosition(parent, Atom.TYPE_esds, childPosition, childAtomSize);
if (esdsAtomPosition != C.POSITION_UNSET) { if (esdsAtomPosition != C.POSITION_UNSET) {
Pair<@NullableType String, byte @NullableType []> mimeTypeAndInitializationData = Pair<@NullableType String, byte @NullableType []> mimeTypeAndInitializationData =
parseEsdsFromParent(parent, esdsAtomPosition); parseEsdsFromParent(parent, esdsAtomPosition);
@ -1537,18 +1537,28 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
} }
/** /**
* Returns the position of the esds box within a parent, or {@link C#POSITION_UNSET} if no esds * Returns the position of the first box with the given {@code boxType} within {@code parent}, or
* box is found * {@link C#POSITION_UNSET} if no such box is found.
*
* @param parent The {@link ParsableByteArray} to search. The search will start from the {@link
* ParsableByteArray#getPosition() current position}.
* @param boxType The box type to search for.
* @param parentBoxPosition The position in {@code parent} of the box we are searching.
* @param parentBoxSize The size of the parent box we are searching in bytes.
* @return The position of the first box with the given {@code boxType} within {@code parent}, or
* {@link C#POSITION_UNSET} if no such box is found.
*/ */
private static int findEsdsPosition(ParsableByteArray parent, int position, int size) private static int findBoxPosition(
ParsableByteArray parent, int boxType, int parentBoxPosition, int parentBoxSize)
throws ParserException { throws ParserException {
int childAtomPosition = parent.getPosition(); int childAtomPosition = parent.getPosition();
while (childAtomPosition - position < size) { ExtractorUtil.checkContainerInput(childAtomPosition >= parentBoxPosition, /* message= */ null);
while (childAtomPosition - parentBoxPosition < parentBoxSize) {
parent.setPosition(childAtomPosition); parent.setPosition(childAtomPosition);
int childAtomSize = parent.readInt(); int childAtomSize = parent.readInt();
ExtractorUtil.checkContainerInput(childAtomSize > 0, "childAtomSize must be positive"); ExtractorUtil.checkContainerInput(childAtomSize > 0, "childAtomSize must be positive");
int childType = parent.readInt(); int childType = parent.readInt();
if (childType == Atom.TYPE_esds) { if (childType == boxType) {
return childAtomPosition; return childAtomPosition;
} }
childAtomPosition += childAtomSize; childAtomPosition += childAtomSize;

View File

@ -102,6 +102,7 @@ public final class TranscodingTransformer {
private boolean flattenForSlowMotion; private boolean flattenForSlowMotion;
private String outputMimeType; private String outputMimeType;
@Nullable private String audioMimeType; @Nullable private String audioMimeType;
@Nullable private String videoMimeType;
private TranscodingTransformer.Listener listener; private TranscodingTransformer.Listener listener;
private Looper looper; private Looper looper;
private Clock clock; private Clock clock;
@ -125,6 +126,7 @@ public final class TranscodingTransformer {
this.flattenForSlowMotion = transcodingTransformer.transformation.flattenForSlowMotion; this.flattenForSlowMotion = transcodingTransformer.transformation.flattenForSlowMotion;
this.outputMimeType = transcodingTransformer.transformation.outputMimeType; this.outputMimeType = transcodingTransformer.transformation.outputMimeType;
this.audioMimeType = transcodingTransformer.transformation.audioMimeType; this.audioMimeType = transcodingTransformer.transformation.audioMimeType;
this.videoMimeType = transcodingTransformer.transformation.videoMimeType;
this.listener = transcodingTransformer.listener; this.listener = transcodingTransformer.listener;
this.looper = transcodingTransformer.looper; this.looper = transcodingTransformer.looper;
this.clock = transcodingTransformer.clock; this.clock = transcodingTransformer.clock;
@ -231,6 +233,33 @@ public final class TranscodingTransformer {
return this; return this;
} }
/**
* Sets the video MIME type of the output. The default value is to use the same MIME type as the
* input. Supported values are:
*
* <ul>
* <li>when the container MIME type is {@link MimeTypes#VIDEO_MP4}:
* <ul>
* <li>{@link MimeTypes#VIDEO_H263}
* <li>{@link MimeTypes#VIDEO_H264}
* <li>{@link MimeTypes#VIDEO_H265} from API level 24
* <li>{@link MimeTypes#VIDEO_MP4V}
* </ul>
* <li>when the container MIME type is {@link MimeTypes#VIDEO_WEBM}:
* <ul>
* <li>{@link MimeTypes#VIDEO_VP8}
* <li>{@link MimeTypes#VIDEO_VP9} from API level 24
* </ul>
* </ul>
*
* @param videoMimeType The MIME type of the video samples in the output.
* @return This builder.
*/
public Builder setVideoMimeType(String videoMimeType) {
this.videoMimeType = videoMimeType;
return this;
}
/** /**
* Sets the audio MIME type of the output. The default value is to use the same MIME type as the * Sets the audio MIME type of the output. The default value is to use the same MIME type as the
* input. Supported values are: * input. Supported values are:
@ -331,12 +360,10 @@ public final class TranscodingTransformer {
muxerFactory.supportsOutputMimeType(outputMimeType), muxerFactory.supportsOutputMimeType(outputMimeType),
"Unsupported output MIME type: " + outputMimeType); "Unsupported output MIME type: " + outputMimeType);
if (audioMimeType != null) { if (audioMimeType != null) {
checkState( checkSampleMimeType(audioMimeType);
muxerFactory.supportsSampleMimeType(audioMimeType, outputMimeType), }
"Unsupported sample MIME type " if (videoMimeType != null) {
+ audioMimeType checkSampleMimeType(videoMimeType);
+ " for container MIME type "
+ outputMimeType);
} }
Transformation transformation = Transformation transformation =
new Transformation( new Transformation(
@ -345,10 +372,19 @@ public final class TranscodingTransformer {
flattenForSlowMotion, flattenForSlowMotion,
outputMimeType, outputMimeType,
audioMimeType, audioMimeType,
/* videoMimeType= */ null); videoMimeType);
return new TranscodingTransformer( return new TranscodingTransformer(
context, mediaSourceFactory, muxerFactory, transformation, listener, looper, clock); context, mediaSourceFactory, muxerFactory, transformation, listener, looper, clock);
} }
private void checkSampleMimeType(String sampleMimeType) {
checkState(
muxerFactory.supportsSampleMimeType(sampleMimeType, outputMimeType),
"Unsupported sample MIME type "
+ sampleMimeType
+ " for container MIME type "
+ outputMimeType);
}
} }
/** A listener for the transformation events. */ /** A listener for the transformation events. */

View File

@ -58,7 +58,10 @@ import androidx.media3.exoplayer.RendererCapabilities;
? sampleMimeType ? sampleMimeType
: transformation.audioMimeType)) : transformation.audioMimeType))
|| (MimeTypes.isVideo(sampleMimeType) || (MimeTypes.isVideo(sampleMimeType)
&& muxerWrapper.supportsSampleMimeType(sampleMimeType))) { && muxerWrapper.supportsSampleMimeType(
transformation.videoMimeType == null
? sampleMimeType
: transformation.videoMimeType))) {
return RendererCapabilities.create(C.FORMAT_HANDLED); return RendererCapabilities.create(C.FORMAT_HANDLED);
} else { } else {
return RendererCapabilities.create(C.FORMAT_UNSUPPORTED_SUBTYPE); return RendererCapabilities.create(C.FORMAT_UNSUPPORTED_SUBTYPE);