mirror of
https://github.com/samsonjs/media.git
synced 2026-04-08 11:45:51 +00:00
Automatically use DummySurface when possible
Issue: #677 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=157831796
This commit is contained in:
parent
32b5a80291
commit
ba9114c9c7
7 changed files with 167 additions and 77 deletions
|
|
@ -281,7 +281,7 @@ import java.util.Locale;
|
|||
@Override
|
||||
public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees,
|
||||
float pixelWidthHeightRatio) {
|
||||
// Do nothing.
|
||||
Log.d(TAG, "videoSizeChanged [" + width + ", " + height + "]");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -255,7 +255,7 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
|
|||
|
||||
if (outputMode == VpxDecoder.OUTPUT_MODE_NONE) {
|
||||
// Skip frames in sync with playback, so we'll be at the right frame if the mode changes.
|
||||
if (outputBuffer.timeUs <= positionUs) {
|
||||
if (isBufferLate(outputBuffer.timeUs - positionUs)) {
|
||||
skipBuffer();
|
||||
return true;
|
||||
}
|
||||
|
|
@ -280,7 +280,6 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the current frame should be dropped.
|
||||
*
|
||||
|
|
@ -293,10 +292,8 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
|
|||
*/
|
||||
protected boolean shouldDropOutputBuffer(long outputBufferTimeUs, long nextOutputBufferTimeUs,
|
||||
long positionUs, long joiningDeadlineMs) {
|
||||
// Drop the frame if we're joining and are more than 30ms late, or if we have the next frame
|
||||
// and that's also late. Else we'll render what we have.
|
||||
return (joiningDeadlineMs != C.TIME_UNSET && outputBufferTimeUs < positionUs - 30000)
|
||||
|| (nextOutputBufferTimeUs != C.TIME_UNSET && nextOutputBufferTimeUs < positionUs);
|
||||
return isBufferLate(outputBufferTimeUs - positionUs)
|
||||
&& (joiningDeadlineMs != C.TIME_UNSET || nextOutputBufferTimeUs != C.TIME_UNSET);
|
||||
}
|
||||
|
||||
private void renderBuffer() {
|
||||
|
|
@ -655,4 +652,9 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
|
|||
}
|
||||
}
|
||||
|
||||
private static boolean isBufferLate(long earlyUs) {
|
||||
// Class a buffer as late if it should have been presented more than 30ms ago.
|
||||
return earlyUs < -30000;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,6 +61,14 @@ public final class MediaCodecInfo {
|
|||
*/
|
||||
public final boolean tunneling;
|
||||
|
||||
/**
|
||||
* Whether the decoder is secure.
|
||||
*
|
||||
* @see CodecCapabilities#isFeatureRequired(String)
|
||||
* @see CodecCapabilities#FEATURE_SecurePlayback
|
||||
*/
|
||||
public final boolean secure;
|
||||
|
||||
private final String mimeType;
|
||||
private final CodecCapabilities capabilities;
|
||||
|
||||
|
|
@ -71,7 +79,7 @@ public final class MediaCodecInfo {
|
|||
* @return The created instance.
|
||||
*/
|
||||
public static MediaCodecInfo newPassthroughInstance(String name) {
|
||||
return new MediaCodecInfo(name, null, null, false);
|
||||
return new MediaCodecInfo(name, null, null, false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -84,7 +92,7 @@ public final class MediaCodecInfo {
|
|||
*/
|
||||
public static MediaCodecInfo newInstance(String name, String mimeType,
|
||||
CodecCapabilities capabilities) {
|
||||
return new MediaCodecInfo(name, mimeType, capabilities, false);
|
||||
return new MediaCodecInfo(name, mimeType, capabilities, false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -94,20 +102,22 @@ public final class MediaCodecInfo {
|
|||
* @param mimeType A mime type supported by the {@link MediaCodec}.
|
||||
* @param capabilities The capabilities of the {@link MediaCodec} for the specified mime type.
|
||||
* @param forceDisableAdaptive Whether {@link #adaptive} should be forced to {@code false}.
|
||||
* @param forceSecure Whether {@link #secure} should be forced to {@code true}.
|
||||
* @return The created instance.
|
||||
*/
|
||||
public static MediaCodecInfo newInstance(String name, String mimeType,
|
||||
CodecCapabilities capabilities, boolean forceDisableAdaptive) {
|
||||
return new MediaCodecInfo(name, mimeType, capabilities, forceDisableAdaptive);
|
||||
CodecCapabilities capabilities, boolean forceDisableAdaptive, boolean forceSecure) {
|
||||
return new MediaCodecInfo(name, mimeType, capabilities, forceDisableAdaptive, forceSecure);
|
||||
}
|
||||
|
||||
private MediaCodecInfo(String name, String mimeType, CodecCapabilities capabilities,
|
||||
boolean forceDisableAdaptive) {
|
||||
boolean forceDisableAdaptive, boolean forceSecure) {
|
||||
this.name = Assertions.checkNotNull(name);
|
||||
this.mimeType = mimeType;
|
||||
this.capabilities = capabilities;
|
||||
adaptive = !forceDisableAdaptive && capabilities != null && isAdaptive(capabilities);
|
||||
tunneling = capabilities != null && isTunneling(capabilities);
|
||||
secure = forceSecure || (capabilities != null && isSecure(capabilities));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -176,12 +186,12 @@ public final class MediaCodecInfo {
|
|||
logNoSupport("sizeAndRate.vCaps");
|
||||
return false;
|
||||
}
|
||||
if (!areSizeAndRateSupported(videoCapabilities, width, height, frameRate)) {
|
||||
if (!areSizeAndRateSupportedV21(videoCapabilities, width, height, frameRate)) {
|
||||
// Capabilities are known to be inaccurately reported for vertical resolutions on some devices
|
||||
// (b/31387661). If the video is vertical and the capabilities indicate support if the width
|
||||
// and height are swapped, we assume that the vertical resolution is also supported.
|
||||
if (width >= height
|
||||
|| !areSizeAndRateSupported(videoCapabilities, height, width, frameRate)) {
|
||||
|| !areSizeAndRateSupportedV21(videoCapabilities, height, width, frameRate)) {
|
||||
logNoSupport("sizeAndRate.support, " + width + "x" + height + "x" + frameRate);
|
||||
return false;
|
||||
}
|
||||
|
|
@ -290,14 +300,6 @@ public final class MediaCodecInfo {
|
|||
return capabilities.isFeatureSupported(CodecCapabilities.FEATURE_AdaptivePlayback);
|
||||
}
|
||||
|
||||
@TargetApi(21)
|
||||
private static boolean areSizeAndRateSupported(VideoCapabilities capabilities, int width,
|
||||
int height, double frameRate) {
|
||||
return frameRate == Format.NO_VALUE || frameRate <= 0
|
||||
? capabilities.isSizeSupported(width, height)
|
||||
: capabilities.areSizeAndRateSupported(width, height, frameRate);
|
||||
}
|
||||
|
||||
private static boolean isTunneling(CodecCapabilities capabilities) {
|
||||
return Util.SDK_INT >= 21 && isTunnelingV21(capabilities);
|
||||
}
|
||||
|
|
@ -307,4 +309,21 @@ public final class MediaCodecInfo {
|
|||
return capabilities.isFeatureSupported(CodecCapabilities.FEATURE_TunneledPlayback);
|
||||
}
|
||||
|
||||
private static boolean isSecure(CodecCapabilities capabilities) {
|
||||
return Util.SDK_INT >= 21 && isSecureV21(capabilities);
|
||||
}
|
||||
|
||||
@TargetApi(21)
|
||||
private static boolean isSecureV21(CodecCapabilities capabilities) {
|
||||
return capabilities.isFeatureSupported(CodecCapabilities.FEATURE_SecurePlayback);
|
||||
}
|
||||
|
||||
@TargetApi(21)
|
||||
private static boolean areSizeAndRateSupportedV21(VideoCapabilities capabilities, int width,
|
||||
int height, double frameRate) {
|
||||
return frameRate == Format.NO_VALUE || frameRate <= 0
|
||||
? capabilities.isSizeSupported(width, height)
|
||||
: capabilities.areSizeAndRateSupported(width, height, frameRate);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -175,10 +175,10 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||
private final MediaCodec.BufferInfo outputBufferInfo;
|
||||
|
||||
private Format format;
|
||||
private MediaCodec codec;
|
||||
private DrmSession<FrameworkMediaCrypto> drmSession;
|
||||
private DrmSession<FrameworkMediaCrypto> pendingDrmSession;
|
||||
private boolean codecIsAdaptive;
|
||||
private MediaCodec codec;
|
||||
private MediaCodecInfo codecInfo;
|
||||
private boolean codecNeedsDiscardToSpsWorkaround;
|
||||
private boolean codecNeedsFlushWorkaround;
|
||||
private boolean codecNeedsAdaptationWorkaround;
|
||||
|
|
@ -291,7 +291,8 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||
|
||||
@SuppressWarnings("deprecation")
|
||||
protected final void maybeInitCodec() throws ExoPlaybackException {
|
||||
if (!shouldInitCodec()) {
|
||||
if (codec != null || format == null) {
|
||||
// We have a codec already, or we don't have a format with which to instantiate one.
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -313,18 +314,18 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||
}
|
||||
}
|
||||
|
||||
MediaCodecInfo decoderInfo = null;
|
||||
MediaCodecInfo codecInfo = null;
|
||||
try {
|
||||
decoderInfo = getDecoderInfo(mediaCodecSelector, format, drmSessionRequiresSecureDecoder);
|
||||
if (decoderInfo == null && drmSessionRequiresSecureDecoder) {
|
||||
codecInfo = getDecoderInfo(mediaCodecSelector, format, drmSessionRequiresSecureDecoder);
|
||||
if (codecInfo == null && drmSessionRequiresSecureDecoder) {
|
||||
// The drm session indicates that a secure decoder is required, but the device does not have
|
||||
// one. Assuming that supportsFormat indicated support for the media being played, we know
|
||||
// that it does not require a secure output path. Most CDM implementations allow playback to
|
||||
// proceed with a non-secure decoder in this case, so we try our luck.
|
||||
decoderInfo = getDecoderInfo(mediaCodecSelector, format, false);
|
||||
if (decoderInfo != null) {
|
||||
codecInfo = getDecoderInfo(mediaCodecSelector, format, false);
|
||||
if (codecInfo != null) {
|
||||
Log.w(TAG, "Drm session requires secure decoder for " + mimeType + ", but "
|
||||
+ "no secure decoder available. Trying to proceed with " + decoderInfo.name + ".");
|
||||
+ "no secure decoder available. Trying to proceed with " + codecInfo.name + ".");
|
||||
}
|
||||
}
|
||||
} catch (DecoderQueryException e) {
|
||||
|
|
@ -332,14 +333,18 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||
drmSessionRequiresSecureDecoder, DecoderInitializationException.DECODER_QUERY_ERROR));
|
||||
}
|
||||
|
||||
if (decoderInfo == null) {
|
||||
if (codecInfo == null) {
|
||||
throwDecoderInitError(new DecoderInitializationException(format, null,
|
||||
drmSessionRequiresSecureDecoder,
|
||||
DecoderInitializationException.NO_SUITABLE_DECODER_ERROR));
|
||||
}
|
||||
|
||||
String codecName = decoderInfo.name;
|
||||
codecIsAdaptive = decoderInfo.adaptive;
|
||||
if (!shouldInitCodec(codecInfo)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.codecInfo = codecInfo;
|
||||
String codecName = codecInfo.name;
|
||||
codecNeedsDiscardToSpsWorkaround = codecNeedsDiscardToSpsWorkaround(codecName, format);
|
||||
codecNeedsFlushWorkaround = codecNeedsFlushWorkaround(codecName);
|
||||
codecNeedsAdaptationWorkaround = codecNeedsAdaptationWorkaround(codecName);
|
||||
|
|
@ -353,7 +358,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||
codec = MediaCodec.createByCodecName(codecName);
|
||||
TraceUtil.endSection();
|
||||
TraceUtil.beginSection("configureCodec");
|
||||
configureCodec(decoderInfo, codec, format, mediaCrypto);
|
||||
configureCodec(codecInfo, codec, format, mediaCrypto);
|
||||
TraceUtil.endSection();
|
||||
TraceUtil.beginSection("startCodec");
|
||||
codec.start();
|
||||
|
|
@ -380,14 +385,18 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||
throw ExoPlaybackException.createForRenderer(e, getIndex());
|
||||
}
|
||||
|
||||
protected boolean shouldInitCodec() {
|
||||
return codec == null && format != null;
|
||||
protected boolean shouldInitCodec(MediaCodecInfo codecInfo) {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected final MediaCodec getCodec() {
|
||||
return codec;
|
||||
}
|
||||
|
||||
protected final MediaCodecInfo getCodecInfo() {
|
||||
return codecInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onEnabled(boolean joining) throws ExoPlaybackException {
|
||||
decoderCounters = new DecoderCounters();
|
||||
|
|
@ -426,31 +435,31 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||
}
|
||||
|
||||
protected void releaseCodec() {
|
||||
codecHotswapDeadlineMs = C.TIME_UNSET;
|
||||
inputIndex = C.INDEX_UNSET;
|
||||
outputIndex = C.INDEX_UNSET;
|
||||
waitingForKeys = false;
|
||||
shouldSkipOutputBuffer = false;
|
||||
decodeOnlyPresentationTimestamps.clear();
|
||||
inputBuffers = null;
|
||||
outputBuffers = null;
|
||||
codecInfo = null;
|
||||
codecReconfigured = false;
|
||||
codecReceivedBuffers = false;
|
||||
codecNeedsDiscardToSpsWorkaround = false;
|
||||
codecNeedsFlushWorkaround = false;
|
||||
codecNeedsAdaptationWorkaround = false;
|
||||
codecNeedsEosPropagationWorkaround = false;
|
||||
codecNeedsEosFlushWorkaround = false;
|
||||
codecNeedsMonoChannelCountWorkaround = false;
|
||||
codecNeedsAdaptationWorkaroundBuffer = false;
|
||||
shouldSkipAdaptationWorkaroundOutputBuffer = false;
|
||||
codecReceivedEos = false;
|
||||
codecReconfigurationState = RECONFIGURATION_STATE_NONE;
|
||||
codecReinitializationState = REINITIALIZATION_STATE_NONE;
|
||||
buffer.data = null;
|
||||
if (codec != null) {
|
||||
codecHotswapDeadlineMs = C.TIME_UNSET;
|
||||
inputIndex = C.INDEX_UNSET;
|
||||
outputIndex = C.INDEX_UNSET;
|
||||
waitingForKeys = false;
|
||||
shouldSkipOutputBuffer = false;
|
||||
decodeOnlyPresentationTimestamps.clear();
|
||||
inputBuffers = null;
|
||||
outputBuffers = null;
|
||||
codecReconfigured = false;
|
||||
codecReceivedBuffers = false;
|
||||
codecIsAdaptive = false;
|
||||
codecNeedsDiscardToSpsWorkaround = false;
|
||||
codecNeedsFlushWorkaround = false;
|
||||
codecNeedsAdaptationWorkaround = false;
|
||||
codecNeedsEosPropagationWorkaround = false;
|
||||
codecNeedsEosFlushWorkaround = false;
|
||||
codecNeedsMonoChannelCountWorkaround = false;
|
||||
codecNeedsAdaptationWorkaroundBuffer = false;
|
||||
shouldSkipAdaptationWorkaroundOutputBuffer = false;
|
||||
codecReceivedEos = false;
|
||||
codecReconfigurationState = RECONFIGURATION_STATE_NONE;
|
||||
codecReinitializationState = REINITIALIZATION_STATE_NONE;
|
||||
decoderCounters.decoderReleaseCount++;
|
||||
buffer.data = null;
|
||||
try {
|
||||
codec.stop();
|
||||
} finally {
|
||||
|
|
@ -781,7 +790,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||
}
|
||||
|
||||
if (pendingDrmSession == drmSession && codec != null
|
||||
&& canReconfigureCodec(codec, codecIsAdaptive, oldFormat, format)) {
|
||||
&& canReconfigureCodec(codec, codecInfo.adaptive, oldFormat, format)) {
|
||||
codecReconfigured = true;
|
||||
codecReconfigurationState = RECONFIGURATION_STATE_WRITE_PENDING;
|
||||
codecNeedsAdaptationWorkaroundBuffer = codecNeedsAdaptationWorkaround
|
||||
|
|
|
|||
|
|
@ -230,10 +230,10 @@ public final class MediaCodecUtil {
|
|||
if ((secureDecodersExplicit && key.secure == secure)
|
||||
|| (!secureDecodersExplicit && !key.secure)) {
|
||||
decoderInfos.add(MediaCodecInfo.newInstance(codecName, mimeType, capabilities,
|
||||
forceDisableAdaptive));
|
||||
forceDisableAdaptive, false));
|
||||
} else if (!secureDecodersExplicit && secure) {
|
||||
decoderInfos.add(MediaCodecInfo.newInstance(codecName + ".secure", mimeType,
|
||||
capabilities, forceDisableAdaptive));
|
||||
capabilities, forceDisableAdaptive, true));
|
||||
// It only makes sense to have one synthesized secure decoder, return immediately.
|
||||
return decoderInfos;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -255,8 +255,8 @@ public final class DummySurface extends Surface {
|
|||
if (secure) {
|
||||
glAttributes = new int[] {
|
||||
EGL_CONTEXT_CLIENT_VERSION, 2,
|
||||
EGL_PROTECTED_CONTENT_EXT,
|
||||
EGL_TRUE, EGL_NONE};
|
||||
EGL_PROTECTED_CONTENT_EXT, EGL_TRUE,
|
||||
EGL_NONE};
|
||||
} else {
|
||||
glAttributes = new int[] {
|
||||
EGL_CONTEXT_CLIENT_VERSION, 2,
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ import com.google.android.exoplayer2.mediacodec.MediaCodecRenderer;
|
|||
import com.google.android.exoplayer2.mediacodec.MediaCodecSelector;
|
||||
import com.google.android.exoplayer2.mediacodec.MediaCodecUtil;
|
||||
import com.google.android.exoplayer2.mediacodec.MediaCodecUtil.DecoderQueryException;
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
import com.google.android.exoplayer2.util.TraceUtil;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
|
|
@ -77,6 +78,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
|||
private CodecMaxValues codecMaxValues;
|
||||
|
||||
private Surface surface;
|
||||
private Surface dummySurface;
|
||||
@C.VideoScalingMode
|
||||
private int scalingMode;
|
||||
private boolean renderedFirstFrame;
|
||||
|
|
@ -263,7 +265,8 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
|||
|
||||
@Override
|
||||
public boolean isReady() {
|
||||
if ((renderedFirstFrame || super.shouldInitCodec()) && super.isReady()) {
|
||||
if (super.isReady() && (renderedFirstFrame || (dummySurface != null && surface == dummySurface)
|
||||
|| getCodec() == null)) {
|
||||
// Ready. If we were joining then we've now joined, so clear the joining deadline.
|
||||
joiningDeadlineMs = C.TIME_UNSET;
|
||||
return true;
|
||||
|
|
@ -306,6 +309,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
|||
clearRenderedFirstFrame();
|
||||
frameReleaseTimeHelper.disable();
|
||||
tunnelingOnFrameRenderedListener = null;
|
||||
tunneling = false;
|
||||
try {
|
||||
super.onDisabled();
|
||||
} finally {
|
||||
|
|
@ -330,6 +334,18 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
|||
}
|
||||
|
||||
private void setSurface(Surface surface) throws ExoPlaybackException {
|
||||
if (surface == null) {
|
||||
// Use a dummy surface if possible.
|
||||
if (dummySurface != null) {
|
||||
surface = dummySurface;
|
||||
} else {
|
||||
MediaCodecInfo codecInfo = getCodecInfo();
|
||||
if (codecInfo != null && shouldUseDummySurface(codecInfo.secure)) {
|
||||
dummySurface = DummySurface.newInstanceV17(codecInfo.secure);
|
||||
surface = dummySurface;
|
||||
}
|
||||
}
|
||||
}
|
||||
// We only need to update the codec if the surface has changed.
|
||||
if (this.surface != surface) {
|
||||
this.surface = surface;
|
||||
|
|
@ -343,7 +359,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
|||
maybeInitCodec();
|
||||
}
|
||||
}
|
||||
if (surface != null) {
|
||||
if (surface != null && surface != dummySurface) {
|
||||
// If we know the video size, report it again immediately.
|
||||
maybeRenotifyVideoSizeChanged();
|
||||
// We haven't rendered to the new surface yet.
|
||||
|
|
@ -356,17 +372,17 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
|||
clearReportedVideoSize();
|
||||
clearRenderedFirstFrame();
|
||||
}
|
||||
} else if (surface != null) {
|
||||
// The surface is unchanged and non-null. If we know the video size and/or have already
|
||||
// rendered to the surface, report these again immediately.
|
||||
} else if (surface != null && surface != dummySurface) {
|
||||
// The surface is set and unchanged. If we know the video size and/or have already rendered to
|
||||
// the surface, report these again immediately.
|
||||
maybeRenotifyVideoSizeChanged();
|
||||
maybeRenotifyRenderedFirstFrame();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldInitCodec() {
|
||||
return super.shouldInitCodec() && surface != null && surface.isValid();
|
||||
protected boolean shouldInitCodec(MediaCodecInfo codecInfo) {
|
||||
return surface != null || shouldUseDummySurface(codecInfo.secure);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -375,12 +391,34 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
|||
codecMaxValues = getCodecMaxValues(codecInfo, format, streamFormats);
|
||||
MediaFormat mediaFormat = getMediaFormat(format, codecMaxValues, deviceNeedsAutoFrcWorkaround,
|
||||
tunnelingAudioSessionId);
|
||||
if (surface == null) {
|
||||
Assertions.checkState(shouldUseDummySurface(codecInfo.secure));
|
||||
if (dummySurface == null) {
|
||||
dummySurface = DummySurface.newInstanceV17(codecInfo.secure);
|
||||
}
|
||||
surface = dummySurface;
|
||||
}
|
||||
codec.configure(mediaFormat, surface, crypto, 0);
|
||||
if (Util.SDK_INT >= 23 && tunneling) {
|
||||
tunnelingOnFrameRenderedListener = new OnFrameRenderedListenerV23(codec);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void releaseCodec() {
|
||||
try {
|
||||
super.releaseCodec();
|
||||
} finally {
|
||||
if (dummySurface != null) {
|
||||
if (surface == dummySurface) {
|
||||
surface = null;
|
||||
}
|
||||
dummySurface.release();
|
||||
dummySurface = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCodecInitialized(String name, long initializedTimestampMs,
|
||||
long initializationDurationMs) {
|
||||
|
|
@ -452,11 +490,22 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
|||
pendingOutputStreamOffsetCount);
|
||||
}
|
||||
long presentationTimeUs = bufferPresentationTimeUs - outputStreamOffsetUs;
|
||||
|
||||
if (shouldSkip) {
|
||||
skipOutputBuffer(codec, bufferIndex, presentationTimeUs);
|
||||
return true;
|
||||
}
|
||||
|
||||
long earlyUs = bufferPresentationTimeUs - positionUs;
|
||||
if (surface == dummySurface) {
|
||||
// Skip frames in sync with playback, so we'll be at the right frame if the mode changes.
|
||||
if (isBufferLate(earlyUs)) {
|
||||
skipOutputBuffer(codec, bufferIndex, presentationTimeUs);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!renderedFirstFrame) {
|
||||
if (Util.SDK_INT >= 21) {
|
||||
renderOutputBufferV21(codec, bufferIndex, presentationTimeUs, System.nanoTime());
|
||||
|
|
@ -470,9 +519,10 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
|||
return false;
|
||||
}
|
||||
|
||||
// Compute how many microseconds it is until the buffer's presentation time.
|
||||
// Fine-grained adjustment of earlyUs based on the elapsed time since the start of the current
|
||||
// iteration of the rendering loop.
|
||||
long elapsedSinceStartOfLoopUs = (SystemClock.elapsedRealtime() * 1000) - elapsedRealtimeUs;
|
||||
long earlyUs = bufferPresentationTimeUs - positionUs - elapsedSinceStartOfLoopUs;
|
||||
earlyUs -= elapsedSinceStartOfLoopUs;
|
||||
|
||||
// Compute the buffer's desired release time in nanoseconds.
|
||||
long systemTimeNs = System.nanoTime();
|
||||
|
|
@ -484,7 +534,6 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
|||
earlyUs = (adjustedReleaseTimeNs - systemTimeNs) / 1000;
|
||||
|
||||
if (shouldDropOutputBuffer(earlyUs, elapsedRealtimeUs)) {
|
||||
// We're more than 30ms late rendering the frame.
|
||||
dropOutputBuffer(codec, bufferIndex, presentationTimeUs);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -526,8 +575,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
|||
* measured at the start of the current iteration of the rendering loop.
|
||||
*/
|
||||
protected boolean shouldDropOutputBuffer(long earlyUs, long elapsedRealtimeUs) {
|
||||
// Drop the frame if we're more than 30ms late rendering the frame.
|
||||
return earlyUs < -30000;
|
||||
return isBufferLate(earlyUs);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -604,6 +652,13 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
|||
maybeNotifyRenderedFirstFrame();
|
||||
}
|
||||
|
||||
private boolean shouldUseDummySurface(boolean codecIsSecure) {
|
||||
// TODO: Work out when we can safely uncomment the secure case below. This case is currently
|
||||
// broken on Galaxy S8 [Internal: b/37197802].
|
||||
return Util.SDK_INT >= 23 && !tunneling
|
||||
&& (!codecIsSecure /* || DummySurface.SECURE_SUPPORTED */);
|
||||
}
|
||||
|
||||
private void setJoiningDeadlineMs() {
|
||||
joiningDeadlineMs = allowedJoiningTimeMs > 0
|
||||
? (SystemClock.elapsedRealtime() + allowedJoiningTimeMs) : C.TIME_UNSET;
|
||||
|
|
@ -674,6 +729,11 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
|||
}
|
||||
}
|
||||
|
||||
private static boolean isBufferLate(long earlyUs) {
|
||||
// Class a buffer as late if it should have been presented more than 30ms ago.
|
||||
return earlyUs < -30000;
|
||||
}
|
||||
|
||||
@SuppressLint("InlinedApi")
|
||||
private static MediaFormat getMediaFormat(Format format, CodecMaxValues codecMaxValues,
|
||||
boolean deviceNeedsAutoFrcWorkaround, int tunnelingAudioSessionId) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue