mirror of
https://github.com/androidx/media.git
synced 2025-04-29 22:36:54 +08:00
Remove dead code related to MediaCodec
now minSdk is 21
This removes several workarounds that are no longer needed, including
`codecNeedsMonoChannelCountWorkaround` which has been permanently
disabled since the (incomplete) minSdk 19 clean-up in fb7438378d
.
PiperOrigin-RevId: 652495578
This commit is contained in:
parent
1bb8d5f956
commit
bb9ff30c3a
2
api.txt
2
api.txt
@ -1192,7 +1192,7 @@ package androidx.media3.common {
|
||||
field public static final androidx.media3.common.VideoSize UNKNOWN;
|
||||
field @IntRange(from=0) public final int height;
|
||||
field @FloatRange(from=0, fromInclusive=false) public final float pixelWidthHeightRatio;
|
||||
field @IntRange(from=0, to=359) public final int unappliedRotationDegrees;
|
||||
field @Deprecated @IntRange(from=0, to=359) public final int unappliedRotationDegrees;
|
||||
field @IntRange(from=0) public final int width;
|
||||
}
|
||||
|
||||
|
@ -41,19 +41,10 @@ public final class VideoSize {
|
||||
public final int height;
|
||||
|
||||
/**
|
||||
* Clockwise rotation in degrees that the application should apply for the video for it to be
|
||||
* rendered in the correct orientation.
|
||||
*
|
||||
* <p>Is 0 if unknown or if no rotation is needed.
|
||||
*
|
||||
* <p>Player should apply video rotation internally, in which case unappliedRotationDegrees is 0.
|
||||
* But when a player can't apply the rotation, for example before API level 21, the unapplied
|
||||
* rotation is reported by this field for application to handle.
|
||||
*
|
||||
* <p>Applications that use {@link android.view.TextureView} can apply the rotation by calling
|
||||
* {@link android.view.TextureView#setTransform}.
|
||||
* @deprecated Rotation is handled internally by the player, so this is always zero.
|
||||
*/
|
||||
@IntRange(from = 0, to = 359)
|
||||
@Deprecated
|
||||
public final int unappliedRotationDegrees;
|
||||
|
||||
/**
|
||||
@ -73,7 +64,7 @@ public final class VideoSize {
|
||||
*/
|
||||
@UnstableApi
|
||||
public VideoSize(@IntRange(from = 0) int width, @IntRange(from = 0) int height) {
|
||||
this(width, height, DEFAULT_UNAPPLIED_ROTATION_DEGREES, DEFAULT_PIXEL_WIDTH_HEIGHT_RATIO);
|
||||
this(width, height, DEFAULT_PIXEL_WIDTH_HEIGHT_RATIO);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -81,13 +72,25 @@ public final class VideoSize {
|
||||
*
|
||||
* @param width The video width in pixels.
|
||||
* @param height The video height in pixels.
|
||||
* @param unappliedRotationDegrees Clockwise rotation in degrees that the application should apply
|
||||
* for the video for it to be rendered in the correct orientation. See {@link
|
||||
* #unappliedRotationDegrees}.
|
||||
* @param pixelWidthHeightRatio The width to height ratio of each pixel. For the normal case of
|
||||
* square pixels this will be equal to 1.0. Different values are indicative of anamorphic
|
||||
* content.
|
||||
*/
|
||||
@SuppressWarnings("deprecation") // Calling through to deprecated constructor
|
||||
@UnstableApi
|
||||
public VideoSize(
|
||||
@IntRange(from = 0) int width,
|
||||
@IntRange(from = 0) int height,
|
||||
@FloatRange(from = 0, fromInclusive = false) float pixelWidthHeightRatio) {
|
||||
this(width, height, DEFAULT_UNAPPLIED_ROTATION_DEGREES, pixelWidthHeightRatio);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link VideoSize#VideoSize(int, int, float)} instead. {@code
|
||||
* unappliedRotationDegrees} is not needed on API 21+.
|
||||
*/
|
||||
@SuppressWarnings("deprecation") // Setting deprecate field
|
||||
@Deprecated
|
||||
@UnstableApi
|
||||
public VideoSize(
|
||||
@IntRange(from = 0) int width,
|
||||
@ -100,6 +103,7 @@ public final class VideoSize {
|
||||
this.pixelWidthHeightRatio = pixelWidthHeightRatio;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation") // Including deprecated field in equality
|
||||
@Override
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
if (this == obj) {
|
||||
@ -115,6 +119,7 @@ public final class VideoSize {
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation") // Including deprecated field in hashCode
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = 7;
|
||||
@ -130,6 +135,7 @@ public final class VideoSize {
|
||||
private static final String FIELD_UNAPPLIED_ROTATION_DEGREES = Util.intToStringMaxRadix(2);
|
||||
private static final String FIELD_PIXEL_WIDTH_HEIGHT_RATIO = Util.intToStringMaxRadix(3);
|
||||
|
||||
@SuppressWarnings("deprecation") // Including deprecated field in bundle
|
||||
@UnstableApi
|
||||
public Bundle toBundle() {
|
||||
Bundle bundle = new Bundle();
|
||||
@ -141,6 +147,7 @@ public final class VideoSize {
|
||||
}
|
||||
|
||||
/** Restores a {@code VideoSize} from a {@link Bundle}. */
|
||||
@SuppressWarnings("deprecation") // Parsing deprecated field from bundle
|
||||
@UnstableApi
|
||||
public static VideoSize fromBundle(Bundle bundle) {
|
||||
int width = bundle.getInt(FIELD_WIDTH, DEFAULT_WIDTH);
|
||||
|
@ -32,6 +32,7 @@ public final class VideoSizeTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("deprecation") // Testing bundling of deprecated field.
|
||||
public void roundTripViaBundle_ofArbitraryVideoSize_yieldsEqualInstance() {
|
||||
VideoSize videoSize =
|
||||
new VideoSize(
|
||||
|
@ -58,7 +58,6 @@ import androidx.media3.common.util.TimedValueQueue;
|
||||
import androidx.media3.common.util.TraceUtil;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import androidx.media3.common.util.Util;
|
||||
import androidx.media3.container.NalUnitUtil;
|
||||
import androidx.media3.decoder.CryptoConfig;
|
||||
import androidx.media3.decoder.DecoderInputBuffer;
|
||||
import androidx.media3.decoder.DecoderInputBuffer.InsufficientCapacityException;
|
||||
@ -170,7 +169,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
||||
format.sampleMimeType,
|
||||
secureDecoderRequired,
|
||||
mediaCodecInfo,
|
||||
Util.SDK_INT >= 21 ? getDiagnosticInfoV21(cause) : null,
|
||||
(cause instanceof CodecException) ? ((CodecException) cause).getDiagnosticInfo() : null,
|
||||
/* fallbackDecoderInitializationException= */ null);
|
||||
}
|
||||
|
||||
@ -203,15 +202,6 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
||||
fallbackException);
|
||||
}
|
||||
|
||||
@RequiresApi(21)
|
||||
@Nullable
|
||||
private static String getDiagnosticInfoV21(@Nullable Throwable cause) {
|
||||
if (cause instanceof CodecException) {
|
||||
return ((CodecException) cause).getDiagnosticInfo();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String buildCustomDiagnosticInfo(int errorCode) {
|
||||
String sign = errorCode < 0 ? "neg_" : "";
|
||||
String packageName = "androidx.media3.exoplayer.mediacodec";
|
||||
@ -373,13 +363,9 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
||||
@Nullable private DecoderInitializationException preferredDecoderInitializationException;
|
||||
@Nullable private MediaCodecInfo codecInfo;
|
||||
private @AdaptationWorkaroundMode int codecAdaptationWorkaroundMode;
|
||||
private boolean codecNeedsDiscardToSpsWorkaround;
|
||||
private boolean codecNeedsFlushWorkaround;
|
||||
private boolean codecNeedsSosFlushWorkaround;
|
||||
private boolean codecNeedsEosFlushWorkaround;
|
||||
private boolean codecNeedsEosOutputExceptionWorkaround;
|
||||
private boolean codecNeedsEosBufferTimestampWorkaround;
|
||||
private boolean codecNeedsMonoChannelCountWorkaround;
|
||||
private boolean codecNeedsAdaptationWorkaroundBuffer;
|
||||
private boolean shouldSkipAdaptationWorkaroundOutputBuffer;
|
||||
private boolean codecNeedsEosPropagation;
|
||||
@ -898,9 +884,10 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
||||
}
|
||||
decoderCounters.ensureUpdated();
|
||||
} catch (IllegalStateException e) {
|
||||
if (isMediaCodecException(e)) {
|
||||
if (e instanceof CodecException) {
|
||||
onCodecError(e);
|
||||
boolean isRecoverable = Util.SDK_INT >= 21 && isRecoverableMediaCodecExceptionV21(e);
|
||||
boolean isRecoverable =
|
||||
(e instanceof CodecException) && ((CodecException) e).isRecoverable();
|
||||
if (isRecoverable) {
|
||||
releaseCodec();
|
||||
}
|
||||
@ -945,7 +932,6 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
||||
return false;
|
||||
}
|
||||
if (codecDrainAction == DRAIN_ACTION_REINITIALIZE
|
||||
|| codecNeedsFlushWorkaround
|
||||
|| (codecNeedsSosFlushWorkaround && !codecHasOutputMediaFormat)
|
||||
|| (codecNeedsEosFlushWorkaround && codecReceivedEos)) {
|
||||
releaseCodec();
|
||||
@ -1020,13 +1006,9 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
||||
codecHasOutputMediaFormat = false;
|
||||
codecOperatingRate = CODEC_OPERATING_RATE_UNSET;
|
||||
codecAdaptationWorkaroundMode = ADAPTATION_WORKAROUND_MODE_NEVER;
|
||||
codecNeedsDiscardToSpsWorkaround = false;
|
||||
codecNeedsFlushWorkaround = false;
|
||||
codecNeedsSosFlushWorkaround = false;
|
||||
codecNeedsEosFlushWorkaround = false;
|
||||
codecNeedsEosOutputExceptionWorkaround = false;
|
||||
codecNeedsEosBufferTimestampWorkaround = false;
|
||||
codecNeedsMonoChannelCountWorkaround = false;
|
||||
codecNeedsEosPropagation = false;
|
||||
codecRegisteredOnBufferAvailableListener = false;
|
||||
codecReconfigured = false;
|
||||
@ -1238,9 +1220,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
||||
TraceUtil.beginSection("createCodec:" + codecName);
|
||||
codec = codecAdapterFactory.createAdapter(configuration);
|
||||
codecRegisteredOnBufferAvailableListener =
|
||||
Util.SDK_INT >= 21
|
||||
&& Api21.registerOnBufferAvailableListener(
|
||||
codec, new MediaCodecRendererCodecAdapterListener());
|
||||
codec.registerOnBufferAvailableListener(new MediaCodecRendererCodecAdapterListener());
|
||||
} finally {
|
||||
TraceUtil.endSection();
|
||||
}
|
||||
@ -1258,14 +1238,9 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
||||
this.codecOperatingRate = codecOperatingRate;
|
||||
codecInputFormat = inputFormat;
|
||||
codecAdaptationWorkaroundMode = codecAdaptationWorkaroundMode(codecName);
|
||||
codecNeedsDiscardToSpsWorkaround =
|
||||
codecNeedsDiscardToSpsWorkaround(codecName, checkNotNull(codecInputFormat));
|
||||
codecNeedsFlushWorkaround = codecNeedsFlushWorkaround(codecName);
|
||||
codecNeedsSosFlushWorkaround = codecNeedsSosFlushWorkaround(codecName);
|
||||
codecNeedsEosFlushWorkaround = codecNeedsEosFlushWorkaround(codecName);
|
||||
codecNeedsEosOutputExceptionWorkaround = codecNeedsEosOutputExceptionWorkaround(codecName);
|
||||
codecNeedsEosBufferTimestampWorkaround = codecNeedsEosBufferTimestampWorkaround(codecName);
|
||||
codecNeedsMonoChannelCountWorkaround = false;
|
||||
codecNeedsEosPropagation =
|
||||
codecNeedsEosPropagationWorkaround(codecInfo) || getCodecNeedsEosPropagation();
|
||||
if (checkNotNull(codec).needsReconfiguration()) {
|
||||
@ -1462,13 +1437,6 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
||||
if (bufferEncrypted) {
|
||||
buffer.cryptoInfo.increaseClearDataFirstSubSampleBy(adaptiveReconfigurationBytes);
|
||||
}
|
||||
if (codecNeedsDiscardToSpsWorkaround && !bufferEncrypted) {
|
||||
NalUnitUtil.discardToSps(checkNotNull(buffer.data));
|
||||
if (checkNotNull(buffer.data).position() == 0) {
|
||||
return true;
|
||||
}
|
||||
codecNeedsDiscardToSpsWorkaround = false;
|
||||
}
|
||||
|
||||
long presentationTimeUs = buffer.timeUs;
|
||||
|
||||
@ -1950,7 +1918,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
||||
private boolean drainAndFlushCodec() {
|
||||
if (codecReceivedBuffers) {
|
||||
codecDrainState = DRAIN_STATE_SIGNAL_END_OF_STREAM;
|
||||
if (codecNeedsFlushWorkaround || codecNeedsEosFlushWorkaround) {
|
||||
if (codecNeedsEosFlushWorkaround) {
|
||||
codecDrainAction = DRAIN_ACTION_REINITIALIZE;
|
||||
return false;
|
||||
} else {
|
||||
@ -1973,7 +1941,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
||||
private boolean drainAndUpdateCodecDrmSessionV23() throws ExoPlaybackException {
|
||||
if (codecReceivedBuffers) {
|
||||
codecDrainState = DRAIN_STATE_SIGNAL_END_OF_STREAM;
|
||||
if (codecNeedsFlushWorkaround || codecNeedsEosFlushWorkaround) {
|
||||
if (codecNeedsEosFlushWorkaround) {
|
||||
codecDrainAction = DRAIN_ACTION_REINITIALIZE;
|
||||
return false;
|
||||
} else {
|
||||
@ -2060,12 +2028,6 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
||||
outputBuffer.position(outputBufferInfo.offset);
|
||||
outputBuffer.limit(outputBufferInfo.offset + outputBufferInfo.size);
|
||||
}
|
||||
if (codecNeedsEosBufferTimestampWorkaround
|
||||
&& outputBufferInfo.presentationTimeUs == 0
|
||||
&& (outputBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0
|
||||
&& largestQueuedPresentationTimeUs != C.TIME_UNSET) {
|
||||
outputBufferInfo.presentationTimeUs = lastBufferInStreamPresentationTimeUs;
|
||||
}
|
||||
isDecodeOnlyOutputBuffer = outputBufferInfo.presentationTimeUs < getLastResetPositionUs();
|
||||
isLastOutputBuffer =
|
||||
lastBufferInStreamPresentationTimeUs != C.TIME_UNSET
|
||||
@ -2138,9 +2100,6 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
||||
shouldSkipAdaptationWorkaroundOutputBuffer = true;
|
||||
return;
|
||||
}
|
||||
if (codecNeedsMonoChannelCountWorkaround) {
|
||||
mediaFormat.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
|
||||
}
|
||||
codecOutputMediaFormat = mediaFormat;
|
||||
codecOutputMediaFormatChanged = true;
|
||||
}
|
||||
@ -2557,44 +2516,6 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
||||
/* startTimeUs= */ startTimeUs, /* frameTimeUs= */ frameTimeUs));
|
||||
}
|
||||
|
||||
private static boolean isMediaCodecException(IllegalStateException error) {
|
||||
if (Util.SDK_INT >= 21 && isMediaCodecExceptionV21(error)) {
|
||||
return true;
|
||||
}
|
||||
StackTraceElement[] stackTrace = error.getStackTrace();
|
||||
return stackTrace.length > 0 && stackTrace[0].getClassName().equals("android.media.MediaCodec");
|
||||
}
|
||||
|
||||
@RequiresApi(21)
|
||||
private static boolean isMediaCodecExceptionV21(IllegalStateException error) {
|
||||
return error instanceof MediaCodec.CodecException;
|
||||
}
|
||||
|
||||
@RequiresApi(21)
|
||||
private static boolean isRecoverableMediaCodecExceptionV21(IllegalStateException error) {
|
||||
if (error instanceof MediaCodec.CodecException) {
|
||||
return ((MediaCodec.CodecException) error).isRecoverable();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the decoder is known to fail when flushed.
|
||||
*
|
||||
* <p>If true is returned, the renderer will work around the issue by releasing the decoder and
|
||||
* instantiating a new one rather than flushing the current instance.
|
||||
*
|
||||
* <p>See [Internal: b/8347958, b/8543366].
|
||||
*
|
||||
* @param name The name of the decoder.
|
||||
* @return True if the decoder is known to fail when flushed.
|
||||
*/
|
||||
private static boolean codecNeedsFlushWorkaround(String name) {
|
||||
return Util.SDK_INT == 19
|
||||
&& Util.MODEL.startsWith("SM-G800")
|
||||
&& ("OMX.Exynos.avc.dec".equals(name) || "OMX.Exynos.avc.dec.secure".equals(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a mode that specifies when the adaptation workaround should be enabled.
|
||||
*
|
||||
@ -2628,23 +2549,6 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the decoder is an H.264/AVC decoder known to fail if NAL units are queued
|
||||
* before the codec specific data.
|
||||
*
|
||||
* <p>If true is returned, the renderer will work around the issue by discarding data up to the
|
||||
* SPS.
|
||||
*
|
||||
* @param name The name of the decoder.
|
||||
* @param format The {@link Format} used to configure the decoder.
|
||||
* @return True if the decoder is known to fail if NAL units are queued before CSD.
|
||||
*/
|
||||
private static boolean codecNeedsDiscardToSpsWorkaround(String name, Format format) {
|
||||
return Util.SDK_INT < 21
|
||||
&& format.initializationData.isEmpty()
|
||||
&& "OMX.MTK.VIDEO.DECODER.AVC".equals(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the decoder is known to behave incorrectly if flushed prior to having output a
|
||||
* {@link MediaFormat}.
|
||||
@ -2708,24 +2612,6 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
||||
|| "OMX.amlogic.avc.decoder.awesome.secure".equals(name)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the decoder may output a non-empty buffer with timestamp 0 as the end of stream
|
||||
* buffer.
|
||||
*
|
||||
* <p>See <a href="https://github.com/google/ExoPlayer/issues/5045">GitHub issue #5045</a>.
|
||||
*/
|
||||
private static boolean codecNeedsEosBufferTimestampWorkaround(String codecName) {
|
||||
return Util.SDK_INT < 21
|
||||
&& "OMX.SEC.mp3.dec".equals(codecName)
|
||||
&& "samsung".equals(Util.MANUFACTURER)
|
||||
&& (Util.DEVICE.startsWith("baffin")
|
||||
|| Util.DEVICE.startsWith("grand")
|
||||
|| Util.DEVICE.startsWith("fortuna")
|
||||
|| Util.DEVICE.startsWith("gprimelte")
|
||||
|| Util.DEVICE.startsWith("j2y18lte")
|
||||
|| Util.DEVICE.startsWith("ms01"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the decoder may throw an {@link IllegalStateException} from {@link
|
||||
* MediaCodec#dequeueOutputBuffer(MediaCodec.BufferInfo, long)} or {@link
|
||||
@ -2763,15 +2649,6 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(21)
|
||||
private static final class Api21 {
|
||||
@DoNotInline
|
||||
public static boolean registerOnBufferAvailableListener(
|
||||
MediaCodecAdapter codec, MediaCodecRendererCodecAdapterListener listener) {
|
||||
return codec.registerOnBufferAvailableListener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(31)
|
||||
private static final class Api31 {
|
||||
private Api31() {}
|
||||
|
@ -1260,7 +1260,6 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
|
||||
}
|
||||
int width;
|
||||
int height;
|
||||
int unappliedRotationDegrees = 0;
|
||||
float pixelWidthHeightRatio;
|
||||
|
||||
if (tunneling) {
|
||||
@ -1283,22 +1282,16 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
|
||||
: mediaFormat.getInteger(MediaFormat.KEY_HEIGHT);
|
||||
}
|
||||
pixelWidthHeightRatio = format.pixelWidthHeightRatio;
|
||||
if (codecAppliesRotation()) {
|
||||
// On API level 21 and above the decoder applies the rotation when rendering to the surface.
|
||||
// Hence currentUnappliedRotation should always be 0. For 90 and 270 degree rotations, we need
|
||||
// to flip the width, height and pixel aspect ratio to reflect the rotation that was applied.
|
||||
if (format.rotationDegrees == 90 || format.rotationDegrees == 270) {
|
||||
int rotatedHeight = width;
|
||||
width = height;
|
||||
height = rotatedHeight;
|
||||
pixelWidthHeightRatio = 1 / pixelWidthHeightRatio;
|
||||
}
|
||||
} else if (videoSink == null) {
|
||||
// Neither the codec nor the video sink applies the rotation.
|
||||
unappliedRotationDegrees = format.rotationDegrees;
|
||||
// The decoder applies the rotation when rendering to the surface. For 90 and 270 degree
|
||||
// rotations, we need to flip the width, height and pixel aspect ratio to reflect the rotation
|
||||
// that was applied.
|
||||
if (format.rotationDegrees == 90 || format.rotationDegrees == 270) {
|
||||
int rotatedHeight = width;
|
||||
width = height;
|
||||
height = rotatedHeight;
|
||||
pixelWidthHeightRatio = 1 / pixelWidthHeightRatio;
|
||||
}
|
||||
decodedVideoSize =
|
||||
new VideoSize(width, height, unappliedRotationDegrees, pixelWidthHeightRatio);
|
||||
decodedVideoSize = new VideoSize(width, height, pixelWidthHeightRatio);
|
||||
|
||||
if (videoSink != null && videoSinkNeedsRegisterInputStream) {
|
||||
onReadyToRegisterVideoSinkInputStream();
|
||||
@ -1308,7 +1301,6 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
|
||||
.buildUpon()
|
||||
.setWidth(width)
|
||||
.setHeight(height)
|
||||
.setRotationDegrees(unappliedRotationDegrees)
|
||||
.setPixelWidthHeightRatio(pixelWidthHeightRatio)
|
||||
.build());
|
||||
} else {
|
||||
@ -1455,7 +1447,8 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
|
||||
case VideoFrameReleaseControl.FRAME_RELEASE_TRY_AGAIN_LATER:
|
||||
return false;
|
||||
case VideoFrameReleaseControl.FRAME_RELEASE_SCHEDULED:
|
||||
return maybeReleaseFrame(checkStateNotNull(codec), bufferIndex, presentationTimeUs, format);
|
||||
releaseFrame(checkStateNotNull(codec), bufferIndex, presentationTimeUs, format);
|
||||
return true;
|
||||
default:
|
||||
throw new IllegalStateException(String.valueOf(frameReleaseAction));
|
||||
}
|
||||
@ -1469,46 +1462,22 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
|
||||
return -startPositionUs;
|
||||
}
|
||||
|
||||
private boolean maybeReleaseFrame(
|
||||
private void releaseFrame(
|
||||
MediaCodecAdapter codec, int bufferIndex, long presentationTimeUs, Format format) {
|
||||
long releaseTimeNs = videoFrameReleaseInfo.getReleaseTimeNs();
|
||||
long earlyUs = videoFrameReleaseInfo.getEarlyUs();
|
||||
if (Util.SDK_INT >= 21) {
|
||||
// Let the underlying framework time the release.
|
||||
if (shouldSkipBuffersWithIdenticalReleaseTime() && releaseTimeNs == lastFrameReleaseTimeNs) {
|
||||
// This frame should be displayed on the same vsync with the previous released frame. We
|
||||
// are likely rendering frames at a rate higher than the screen refresh rate. Skip
|
||||
// this buffer so that it's returned to MediaCodec sooner otherwise MediaCodec may not
|
||||
// be able to keep decoding with this rate [b/263454203].
|
||||
skipOutputBuffer(codec, bufferIndex, presentationTimeUs);
|
||||
} else {
|
||||
notifyFrameMetadataListener(presentationTimeUs, releaseTimeNs, format);
|
||||
renderOutputBufferV21(codec, bufferIndex, presentationTimeUs, releaseTimeNs);
|
||||
}
|
||||
updateVideoFrameProcessingOffsetCounters(earlyUs);
|
||||
lastFrameReleaseTimeNs = releaseTimeNs;
|
||||
return true;
|
||||
} else if (earlyUs < 30000) {
|
||||
// We need to time the release ourselves.
|
||||
if (earlyUs > 11000) {
|
||||
// We're a little too early to render the frame. Sleep until the frame can be rendered.
|
||||
// Note: The 11ms threshold was chosen fairly arbitrarily.
|
||||
try {
|
||||
// Subtracting 10000 rather than 11000 ensures the sleep time will be at least 1ms.
|
||||
Thread.sleep((earlyUs - 10000) / 1000);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
notifyFrameMetadataListener(presentationTimeUs, releaseTimeNs, format);
|
||||
renderOutputBuffer(codec, bufferIndex, presentationTimeUs);
|
||||
updateVideoFrameProcessingOffsetCounters(earlyUs);
|
||||
return true;
|
||||
if (shouldSkipBuffersWithIdenticalReleaseTime() && releaseTimeNs == lastFrameReleaseTimeNs) {
|
||||
// This frame should be displayed on the same vsync with the previous released frame. We
|
||||
// are likely rendering frames at a rate higher than the screen refresh rate. Skip
|
||||
// this buffer so that it's returned to MediaCodec sooner otherwise MediaCodec may not
|
||||
// be able to keep decoding with this rate [b/263454203].
|
||||
skipOutputBuffer(codec, bufferIndex, presentationTimeUs);
|
||||
} else {
|
||||
// Too soon.
|
||||
return false;
|
||||
notifyFrameMetadataListener(presentationTimeUs, releaseTimeNs, format);
|
||||
renderOutputBufferV21(codec, bufferIndex, presentationTimeUs, releaseTimeNs);
|
||||
}
|
||||
updateVideoFrameProcessingOffsetCounters(earlyUs);
|
||||
lastFrameReleaseTimeNs = releaseTimeNs;
|
||||
}
|
||||
|
||||
private void notifyFrameMetadataListener(
|
||||
@ -1715,21 +1684,14 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
|
||||
*/
|
||||
private void renderOutputBuffer(
|
||||
MediaCodecAdapter codec, int index, long presentationTimeUs, long releaseTimeNs) {
|
||||
if (Util.SDK_INT >= 21) {
|
||||
renderOutputBufferV21(codec, index, presentationTimeUs, releaseTimeNs);
|
||||
} else {
|
||||
renderOutputBuffer(codec, index, presentationTimeUs);
|
||||
}
|
||||
renderOutputBufferV21(codec, index, presentationTimeUs, releaseTimeNs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the output buffer with the specified index. This method is only called if the platform
|
||||
* API version of the device is less than 21.
|
||||
*
|
||||
* @param codec The codec that owns the output buffer.
|
||||
* @param index The index of the output buffer to drop.
|
||||
* @param presentationTimeUs The presentation time of the output buffer, in microseconds.
|
||||
* @deprecated Override {@link #renderOutputBufferV21} instead. The library has min SDK 21, so
|
||||
* this method is never called.
|
||||
*/
|
||||
@Deprecated
|
||||
protected void renderOutputBuffer(MediaCodecAdapter codec, int index, long presentationTimeUs) {
|
||||
TraceUtil.beginSection("releaseOutputBuffer");
|
||||
codec.releaseOutputBuffer(index, true);
|
||||
@ -1751,7 +1713,6 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
|
||||
* @param presentationTimeUs The presentation time of the output buffer, in microseconds.
|
||||
* @param releaseTimeNs The wallclock time at which the frame should be displayed, in nanoseconds.
|
||||
*/
|
||||
@RequiresApi(21)
|
||||
protected void renderOutputBufferV21(
|
||||
MediaCodecAdapter codec, int index, long presentationTimeUs, long releaseTimeNs) {
|
||||
TraceUtil.beginSection("releaseOutputBuffer");
|
||||
@ -1903,12 +1864,6 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
|
||||
codec.setOutputSurface(surface);
|
||||
}
|
||||
|
||||
@RequiresApi(21)
|
||||
private static void configureTunnelingV21(MediaFormat mediaFormat, int tunnelingAudioSessionId) {
|
||||
mediaFormat.setFeatureEnabled(CodecCapabilities.FEATURE_TunneledPlayback, true);
|
||||
mediaFormat.setInteger(MediaFormat.KEY_AUDIO_SESSION_ID, tunnelingAudioSessionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the framework {@link MediaFormat} that should be used to configure the decoder.
|
||||
*
|
||||
@ -1924,7 +1879,6 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
|
||||
* @return The framework {@link MediaFormat} that should be used to configure the decoder.
|
||||
*/
|
||||
@SuppressLint("InlinedApi")
|
||||
@TargetApi(21) // tunnelingAudioSessionId is unset if Util.SDK_INT < 21
|
||||
protected MediaFormat getMediaFormat(
|
||||
Format format,
|
||||
String codecMimeType,
|
||||
@ -1968,7 +1922,8 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
|
||||
mediaFormat.setInteger("auto-frc", 0);
|
||||
}
|
||||
if (tunnelingAudioSessionId != C.AUDIO_SESSION_ID_UNSET) {
|
||||
configureTunnelingV21(mediaFormat, tunnelingAudioSessionId);
|
||||
mediaFormat.setFeatureEnabled(CodecCapabilities.FEATURE_TunneledPlayback, true);
|
||||
mediaFormat.setInteger(MediaFormat.KEY_AUDIO_SESSION_ID, tunnelingAudioSessionId);
|
||||
}
|
||||
if (Util.SDK_INT >= 35) {
|
||||
mediaFormat.setInteger(MediaFormat.KEY_IMPORTANCE, max(0, -rendererPriority));
|
||||
@ -2066,7 +2021,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
|
||||
if (longEdgePx <= formatLongEdgePx || shortEdgePx <= formatShortEdgePx) {
|
||||
// Don't return a size not larger than the format for which the codec is being configured.
|
||||
return null;
|
||||
} else if (Util.SDK_INT >= 21) {
|
||||
} else {
|
||||
Point alignedSize =
|
||||
codecInfo.alignVideoSizeV21(
|
||||
isVerticalVideo ? shortEdgePx : longEdgePx,
|
||||
@ -2076,20 +2031,6 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
|
||||
&& codecInfo.isVideoSizeAndRateSupportedV21(alignedSize.x, alignedSize.y, frameRate)) {
|
||||
return alignedSize;
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
// Conservatively assume the codec requires 16px width and height alignment.
|
||||
longEdgePx = Util.ceilDivide(longEdgePx, 16) * 16;
|
||||
shortEdgePx = Util.ceilDivide(shortEdgePx, 16) * 16;
|
||||
if (longEdgePx * shortEdgePx <= MediaCodecUtil.maxH264DecodableFrameSize()) {
|
||||
return new Point(
|
||||
isVerticalVideo ? shortEdgePx : longEdgePx,
|
||||
isVerticalVideo ? longEdgePx : shortEdgePx);
|
||||
}
|
||||
} catch (DecoderQueryException e) {
|
||||
// We tried our best. Give up!
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@ -2118,10 +2059,6 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean codecAppliesRotation() {
|
||||
return Util.SDK_INT >= 21;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the device is known to do post processing by default that isn't compatible with
|
||||
* ExoPlayer.
|
||||
|
@ -636,6 +636,7 @@ public class MediaCodecVideoRendererTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("deprecation") // Testing propagation of deprecated unappliedRotationDegrees.
|
||||
public void render_sendsVideoSizeChangeWithCurrentFormatValues() throws Exception {
|
||||
FakeSampleStream fakeSampleStream =
|
||||
new FakeSampleStream(
|
||||
|
@ -2649,7 +2649,7 @@ public class MediaControllerListenerTest {
|
||||
|
||||
@Test
|
||||
public void onVideoSizeChanged() throws Exception {
|
||||
VideoSize defaultVideoSize = MediaTestUtils.createDefaultVideoSize();
|
||||
VideoSize defaultVideoSize = MediaTestUtils.getDefaultVideoSize();
|
||||
RemoteMediaSession session = createRemoteMediaSession(TEST_ON_VIDEO_SIZE_CHANGED);
|
||||
MediaController controller = controllerTestRule.createController(session.getToken());
|
||||
List<VideoSize> videoSizeFromGetterList = new ArrayList<>();
|
||||
|
@ -874,6 +874,7 @@ public class MediaControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("deprecation") // Testing propagation of deprecated unappliedRotationDegrees.
|
||||
public void getVideoSize_returnsVideoSizeOfPlayerInSession() throws Exception {
|
||||
VideoSize testVideoSize =
|
||||
new VideoSize(
|
||||
|
@ -289,7 +289,7 @@ public class MediaSessionProviderService extends Service {
|
||||
case TEST_ON_TRACKS_CHANGED_VIDEO_TO_AUDIO_TRANSITION:
|
||||
case TEST_ON_VIDEO_SIZE_CHANGED:
|
||||
{
|
||||
mockPlayer.videoSize = MediaTestUtils.createDefaultVideoSize();
|
||||
mockPlayer.videoSize = MediaTestUtils.getDefaultVideoSize();
|
||||
mockPlayer.currentTracks = MediaTestUtils.createDefaultVideoTracks();
|
||||
break;
|
||||
}
|
||||
|
@ -94,13 +94,9 @@ public final class MediaTestUtils {
|
||||
/* trackSelected= */ new boolean[] {true})));
|
||||
}
|
||||
|
||||
/** Returns a new {@link VideoSize} instance for testing purpose. */
|
||||
public static VideoSize createDefaultVideoSize() {
|
||||
return new VideoSize(
|
||||
DEFAULT_VIDEO_SIZE.width,
|
||||
DEFAULT_VIDEO_SIZE.height,
|
||||
DEFAULT_VIDEO_SIZE.unappliedRotationDegrees,
|
||||
DEFAULT_VIDEO_SIZE.pixelWidthHeightRatio);
|
||||
/** Returns a {@link VideoSize} instance for testing purpose. */
|
||||
public static VideoSize getDefaultVideoSize() {
|
||||
return DEFAULT_VIDEO_SIZE;
|
||||
}
|
||||
|
||||
/** Create a media item with the mediaId for testing purpose. */
|
||||
|
@ -157,11 +157,7 @@ public class FakeVideoRenderer extends FakeRenderer {
|
||||
handler.post(
|
||||
() -> {
|
||||
VideoSize videoSize =
|
||||
new VideoSize(
|
||||
format.width,
|
||||
format.height,
|
||||
format.rotationDegrees,
|
||||
format.pixelWidthHeightRatio);
|
||||
new VideoSize(format.width, format.height, format.pixelWidthHeightRatio);
|
||||
if (!Objects.equals(videoSize, videoSizeRef.get())) {
|
||||
eventListener.onVideoSizeChanged(videoSize);
|
||||
videoSizeRef.set(videoSize);
|
||||
|
Loading…
x
Reference in New Issue
Block a user