Further improve codec reconfiguration
- Only setup a codec to allow adaptation to other compatible formats in the stream. If something like the mimeType is changing adaptation will never be possible, so there's no point. - Incorporate maxInputSize into the reconfiguration logic. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=129088464
This commit is contained in:
parent
bd7c8fed1b
commit
490c5fea78
@ -23,7 +23,6 @@ import android.media.MediaCrypto;
|
|||||||
import android.media.MediaFormat;
|
import android.media.MediaFormat;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
import android.util.Log;
|
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.ExoPlaybackException;
|
import com.google.android.exoplayer2.ExoPlaybackException;
|
||||||
@ -60,10 +59,8 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
|||||||
private final int maxDroppedFramesToNotify;
|
private final int maxDroppedFramesToNotify;
|
||||||
private final boolean deviceNeedsAutoFrcWorkaround;
|
private final boolean deviceNeedsAutoFrcWorkaround;
|
||||||
|
|
||||||
private int maxWidth;
|
private Format[] streamFormats;
|
||||||
private int maxHeight;
|
private CodecMaxValues codecMaxValues;
|
||||||
private int configuredMaxWidth;
|
|
||||||
private int configuredMaxHeight;
|
|
||||||
|
|
||||||
private Surface surface;
|
private Surface surface;
|
||||||
private boolean reportedDrawnToSurface;
|
private boolean reportedDrawnToSurface;
|
||||||
@ -221,18 +218,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onStreamChanged(Format[] formats) throws ExoPlaybackException {
|
protected void onStreamChanged(Format[] formats) throws ExoPlaybackException {
|
||||||
maxWidth = Format.NO_VALUE;
|
streamFormats = formats;
|
||||||
maxHeight = Format.NO_VALUE;
|
|
||||||
for (Format format : formats) {
|
|
||||||
maxWidth = Math.max(maxWidth, format.width);
|
|
||||||
maxHeight = Math.max(maxHeight, format.height);
|
|
||||||
}
|
|
||||||
if (formats.length > 1 && (maxWidth == Format.NO_VALUE || maxHeight == Format.NO_VALUE)) {
|
|
||||||
// TODO: Consider replacing this block with one that sets values injected via the constructor.
|
|
||||||
Log.w(TAG, "Maximum dimensions unknown for adaptive playback. Assuming 1920x1080.");
|
|
||||||
maxWidth = 1920;
|
|
||||||
maxHeight = 1080;
|
|
||||||
}
|
|
||||||
super.onStreamChanged(formats);
|
super.onStreamChanged(formats);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -323,15 +309,11 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
|||||||
return super.shouldInitCodec() && surface != null && surface.isValid();
|
return super.shouldInitCodec() && surface != null && surface.isValid();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Override configureCodec to provide the surface.
|
|
||||||
@Override
|
@Override
|
||||||
protected void configureCodec(MediaCodec codec, Format format, MediaCrypto crypto) {
|
protected void configureCodec(MediaCodec codec, Format format, MediaCrypto crypto) {
|
||||||
maxWidth = Math.max(maxWidth, format.width);
|
codecMaxValues = getCodecMaxValues(format, streamFormats);
|
||||||
maxHeight = Math.max(maxHeight, format.height);
|
MediaFormat mediaFormat = getMediaFormat(format, codecMaxValues, deviceNeedsAutoFrcWorkaround);
|
||||||
codec.configure(getFrameworkFormat(format, maxWidth, maxHeight, deviceNeedsAutoFrcWorkaround),
|
codec.configure(mediaFormat, surface, crypto, 0);
|
||||||
surface, crypto, 0);
|
|
||||||
configuredMaxWidth = maxWidth;
|
|
||||||
configuredMaxHeight = maxHeight;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -381,9 +363,9 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
|||||||
@Override
|
@Override
|
||||||
protected boolean canReconfigureCodec(MediaCodec codec, boolean codecIsAdaptive,
|
protected boolean canReconfigureCodec(MediaCodec codec, boolean codecIsAdaptive,
|
||||||
Format oldFormat, Format newFormat) {
|
Format oldFormat, Format newFormat) {
|
||||||
return newFormat.sampleMimeType.equals(oldFormat.sampleMimeType)
|
return areAdaptationCompatible(oldFormat, newFormat)
|
||||||
&& getRotationDegrees(newFormat) == getRotationDegrees(oldFormat)
|
&& newFormat.width <= codecMaxValues.width && newFormat.height <= codecMaxValues.height
|
||||||
&& newFormat.width <= configuredMaxWidth && newFormat.height <= configuredMaxHeight
|
&& newFormat.maxInputSize <= codecMaxValues.inputSize
|
||||||
&& (codecIsAdaptive
|
&& (codecIsAdaptive
|
||||||
|| (oldFormat.width == newFormat.width && oldFormat.height == newFormat.height));
|
|| (oldFormat.width == newFormat.width && oldFormat.height == newFormat.height));
|
||||||
}
|
}
|
||||||
@ -502,60 +484,98 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("InlinedApi")
|
@SuppressLint("InlinedApi")
|
||||||
private static android.media.MediaFormat getFrameworkFormat(Format format, int maxWidth,
|
private static MediaFormat getMediaFormat(Format format, CodecMaxValues codecMaxValues,
|
||||||
int maxHeight, boolean deviceNeedsAutoFrcWorkaround) {
|
boolean deviceNeedsAutoFrcWorkaround) {
|
||||||
android.media.MediaFormat frameworkMediaFormat = format.getFrameworkMediaFormatV16();
|
MediaFormat frameworkMediaFormat = format.getFrameworkMediaFormatV16();
|
||||||
|
// Set the maximum adaptive video dimensions.
|
||||||
|
frameworkMediaFormat.setInteger(MediaFormat.KEY_MAX_WIDTH, codecMaxValues.width);
|
||||||
|
frameworkMediaFormat.setInteger(MediaFormat.KEY_MAX_HEIGHT, codecMaxValues.height);
|
||||||
|
// Set the maximum input size.
|
||||||
|
if (codecMaxValues.inputSize != Format.NO_VALUE) {
|
||||||
|
frameworkMediaFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, codecMaxValues.inputSize);
|
||||||
|
}
|
||||||
|
// Set FRC workaround.
|
||||||
if (deviceNeedsAutoFrcWorkaround) {
|
if (deviceNeedsAutoFrcWorkaround) {
|
||||||
frameworkMediaFormat.setInteger("auto-frc", 0);
|
frameworkMediaFormat.setInteger("auto-frc", 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the maximum adaptive video dimensions.
|
|
||||||
frameworkMediaFormat.setInteger(MediaFormat.KEY_MAX_WIDTH, maxWidth);
|
|
||||||
frameworkMediaFormat.setInteger(MediaFormat.KEY_MAX_HEIGHT, maxHeight);
|
|
||||||
|
|
||||||
if (format.maxInputSize > 0) {
|
|
||||||
// The format already has a maximum input size.
|
|
||||||
return frameworkMediaFormat;
|
return frameworkMediaFormat;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the format doesn't define a maximum input size, determine one ourselves.
|
/**
|
||||||
|
* Returns {@link CodecMaxValues} suitable for configuring a codec for {@code format} in a way
|
||||||
|
* that will allow possible adaptation to other compatible formats in {@code streamFormats}.
|
||||||
|
*
|
||||||
|
* @param format The format for which the codec is being configured.
|
||||||
|
* @param streamFormats The possible stream formats.
|
||||||
|
* @return Suitable {@link CodecMaxValues}.
|
||||||
|
*/
|
||||||
|
private static CodecMaxValues getCodecMaxValues(Format format, Format[] streamFormats) {
|
||||||
|
int maxWidth = format.width;
|
||||||
|
int maxHeight = format.height;
|
||||||
|
int maxInputSize = getMaxInputSize(format);
|
||||||
|
for (Format streamFormat : streamFormats) {
|
||||||
|
if (areAdaptationCompatible(format, streamFormat)) {
|
||||||
|
maxWidth = Math.max(maxWidth, streamFormat.width);
|
||||||
|
maxHeight = Math.max(maxHeight, streamFormat.height);
|
||||||
|
maxInputSize = Math.max(maxInputSize, getMaxInputSize(streamFormat));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new CodecMaxValues(maxWidth, maxHeight, maxInputSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a maximum input size for a given format.
|
||||||
|
*
|
||||||
|
* @param format The format.
|
||||||
|
* @return An maximum input size in bytes, or {@link Format#NO_VALUE} if a maximum could not be
|
||||||
|
* determined.
|
||||||
|
*/
|
||||||
|
private static int getMaxInputSize(Format format) {
|
||||||
|
if (format.maxInputSize != Format.NO_VALUE) {
|
||||||
|
// The format defines an explicit maximum input size.
|
||||||
|
return format.maxInputSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (format.width == Format.NO_VALUE || format.height == Format.NO_VALUE) {
|
||||||
|
// We can't infer a maximum input size without video dimensions.
|
||||||
|
return Format.NO_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to infer a maximum input size from the format.
|
||||||
int maxPixels;
|
int maxPixels;
|
||||||
int minCompressionRatio;
|
int minCompressionRatio;
|
||||||
switch (format.sampleMimeType) {
|
switch (format.sampleMimeType) {
|
||||||
case MimeTypes.VIDEO_H263:
|
case MimeTypes.VIDEO_H263:
|
||||||
case MimeTypes.VIDEO_MP4V:
|
case MimeTypes.VIDEO_MP4V:
|
||||||
maxPixels = maxWidth * maxHeight;
|
maxPixels = format.width * format.height;
|
||||||
minCompressionRatio = 2;
|
minCompressionRatio = 2;
|
||||||
break;
|
break;
|
||||||
case MimeTypes.VIDEO_H264:
|
case MimeTypes.VIDEO_H264:
|
||||||
if ("BRAVIA 4K 2015".equals(Util.MODEL)) {
|
if ("BRAVIA 4K 2015".equals(Util.MODEL)) {
|
||||||
// The Sony BRAVIA 4k TV has input buffers that are too small for the calculated 4k video
|
// The Sony BRAVIA 4k TV has input buffers that are too small for the calculated 4k video
|
||||||
// maximum input size, so use the default value.
|
// maximum input size, so use the default value.
|
||||||
return frameworkMediaFormat;
|
return Format.NO_VALUE;
|
||||||
}
|
}
|
||||||
// Round up width/height to an integer number of macroblocks.
|
// Round up width/height to an integer number of macroblocks.
|
||||||
maxPixels = ((maxWidth + 15) / 16) * ((maxHeight + 15) / 16) * 16 * 16;
|
maxPixels = ((format.width + 15) / 16) * ((format.height + 15) / 16) * 16 * 16;
|
||||||
minCompressionRatio = 2;
|
minCompressionRatio = 2;
|
||||||
break;
|
break;
|
||||||
case MimeTypes.VIDEO_VP8:
|
case MimeTypes.VIDEO_VP8:
|
||||||
// VPX does not specify a ratio so use the values from the platform's SoftVPX.cpp.
|
// VPX does not specify a ratio so use the values from the platform's SoftVPX.cpp.
|
||||||
maxPixels = maxWidth * maxHeight;
|
maxPixels = format.width * format.height;
|
||||||
minCompressionRatio = 2;
|
minCompressionRatio = 2;
|
||||||
break;
|
break;
|
||||||
case MimeTypes.VIDEO_H265:
|
case MimeTypes.VIDEO_H265:
|
||||||
case MimeTypes.VIDEO_VP9:
|
case MimeTypes.VIDEO_VP9:
|
||||||
maxPixels = maxWidth * maxHeight;
|
maxPixels = format.width * format.height;
|
||||||
minCompressionRatio = 4;
|
minCompressionRatio = 4;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// Leave the default max input size.
|
// Leave the default max input size.
|
||||||
return frameworkMediaFormat;
|
return Format.NO_VALUE;
|
||||||
}
|
}
|
||||||
// Estimate the maximum input size assuming three channel 4:2:0 subsampled input frames.
|
// Estimate the maximum input size assuming three channel 4:2:0 subsampled input frames.
|
||||||
int maxInputSize = (maxPixels * 3) / (2 * minCompressionRatio);
|
return (maxPixels * 3) / (2 * minCompressionRatio);
|
||||||
frameworkMediaFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, maxInputSize);
|
|
||||||
return frameworkMediaFormat;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void maybeNotifyDrawnToSurface() {
|
private void maybeNotifyDrawnToSurface() {
|
||||||
@ -606,6 +626,20 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
|||||||
return Util.SDK_INT <= 22 && "foster".equals(Util.DEVICE) && "NVIDIA".equals(Util.MANUFACTURER);
|
return Util.SDK_INT <= 22 && "foster".equals(Util.DEVICE) && "NVIDIA".equals(Util.MANUFACTURER);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether an adaptive codec with suitable {@link CodecMaxValues} will support adaptation
|
||||||
|
* between two {@link Format}s.
|
||||||
|
*
|
||||||
|
* @param first The first format.
|
||||||
|
* @param second The second format.
|
||||||
|
* @return Whether an adaptive codec with suitable {@link CodecMaxValues} will support adaptation
|
||||||
|
* between two {@link Format}s.
|
||||||
|
*/
|
||||||
|
private static boolean areAdaptationCompatible(Format first, Format second) {
|
||||||
|
return first.sampleMimeType.equals(second.sampleMimeType)
|
||||||
|
&& getRotationDegrees(first) == getRotationDegrees(second);
|
||||||
|
}
|
||||||
|
|
||||||
private static float getPixelWidthHeightRatio(Format format) {
|
private static float getPixelWidthHeightRatio(Format format) {
|
||||||
return format.pixelWidthHeightRatio == Format.NO_VALUE ? 1 : format.pixelWidthHeightRatio;
|
return format.pixelWidthHeightRatio == Format.NO_VALUE ? 1 : format.pixelWidthHeightRatio;
|
||||||
}
|
}
|
||||||
@ -614,4 +648,18 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
|||||||
return format.rotationDegrees == Format.NO_VALUE ? 0 : format.rotationDegrees;
|
return format.rotationDegrees == Format.NO_VALUE ? 0 : format.rotationDegrees;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final class CodecMaxValues {
|
||||||
|
|
||||||
|
public final int width;
|
||||||
|
public final int height;
|
||||||
|
public final int inputSize;
|
||||||
|
|
||||||
|
public CodecMaxValues(int width, int height, int inputSize) {
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
this.inputSize = inputSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user