mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Add nullness annotations to DecoderVideoRenderer
Also fixed a bug where format queue was polled with wrong timestamp value. #fixit PiperOrigin-RevId: 570420304
This commit is contained in:
parent
7a91474af9
commit
a879bae1ee
2 changed files with 44 additions and 37 deletions
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package androidx.media3.exoplayer.video;
|
package androidx.media3.exoplayer.video;
|
||||||
|
|
||||||
|
import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||||
import static androidx.media3.common.util.Util.msToUs;
|
import static androidx.media3.common.util.Util.msToUs;
|
||||||
import static androidx.media3.exoplayer.DecoderReuseEvaluation.DISCARD_REASON_DRM_SESSION_CHANGED;
|
import static androidx.media3.exoplayer.DecoderReuseEvaluation.DISCARD_REASON_DRM_SESSION_CHANGED;
|
||||||
import static androidx.media3.exoplayer.DecoderReuseEvaluation.DISCARD_REASON_REUSE_NOT_IMPLEMENTED;
|
import static androidx.media3.exoplayer.DecoderReuseEvaluation.DISCARD_REASON_REUSE_NOT_IMPLEMENTED;
|
||||||
|
|
@ -116,16 +117,16 @@ public abstract class DecoderVideoRenderer extends BaseRenderer {
|
||||||
private final TimedValueQueue<Format> formatQueue;
|
private final TimedValueQueue<Format> formatQueue;
|
||||||
private final DecoderInputBuffer flagsOnlyBuffer;
|
private final DecoderInputBuffer flagsOnlyBuffer;
|
||||||
|
|
||||||
private Format inputFormat;
|
@Nullable private Format inputFormat;
|
||||||
private Format outputFormat;
|
@Nullable private Format outputFormat;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private Decoder<
|
private Decoder<
|
||||||
DecoderInputBuffer, ? extends VideoDecoderOutputBuffer, ? extends DecoderException>
|
DecoderInputBuffer, ? extends VideoDecoderOutputBuffer, ? extends DecoderException>
|
||||||
decoder;
|
decoder;
|
||||||
|
|
||||||
private DecoderInputBuffer inputBuffer;
|
@Nullable private DecoderInputBuffer inputBuffer;
|
||||||
private VideoDecoderOutputBuffer outputBuffer;
|
@Nullable private VideoDecoderOutputBuffer outputBuffer;
|
||||||
private @VideoOutputMode int outputMode;
|
private @VideoOutputMode int outputMode;
|
||||||
@Nullable private Object output;
|
@Nullable private Object output;
|
||||||
@Nullable private Surface outputSurface;
|
@Nullable private Surface outputSurface;
|
||||||
|
|
@ -175,13 +176,13 @@ public abstract class DecoderVideoRenderer extends BaseRenderer {
|
||||||
this.allowedJoiningTimeMs = allowedJoiningTimeMs;
|
this.allowedJoiningTimeMs = allowedJoiningTimeMs;
|
||||||
this.maxDroppedFramesToNotify = maxDroppedFramesToNotify;
|
this.maxDroppedFramesToNotify = maxDroppedFramesToNotify;
|
||||||
joiningDeadlineMs = C.TIME_UNSET;
|
joiningDeadlineMs = C.TIME_UNSET;
|
||||||
clearReportedVideoSize();
|
|
||||||
formatQueue = new TimedValueQueue<>();
|
formatQueue = new TimedValueQueue<>();
|
||||||
flagsOnlyBuffer = DecoderInputBuffer.newNoDataInstance();
|
flagsOnlyBuffer = DecoderInputBuffer.newNoDataInstance();
|
||||||
eventDispatcher = new EventDispatcher(eventHandler, eventListener);
|
eventDispatcher = new EventDispatcher(eventHandler, eventListener);
|
||||||
decoderReinitializationState = REINITIALIZATION_STATE_NONE;
|
decoderReinitializationState = REINITIALIZATION_STATE_NONE;
|
||||||
outputMode = C.VIDEO_OUTPUT_MODE_NONE;
|
outputMode = C.VIDEO_OUTPUT_MODE_NONE;
|
||||||
firstFrameState = C.FIRST_FRAME_NOT_RENDERED_ONLY_ALLOWED_IF_STARTED;
|
firstFrameState = C.FIRST_FRAME_NOT_RENDERED_ONLY_ALLOWED_IF_STARTED;
|
||||||
|
decoderCounters = new DecoderCounters();
|
||||||
}
|
}
|
||||||
|
|
||||||
// BaseRenderer implementation.
|
// BaseRenderer implementation.
|
||||||
|
|
@ -324,7 +325,7 @@ public abstract class DecoderVideoRenderer extends BaseRenderer {
|
||||||
@Override
|
@Override
|
||||||
protected void onDisabled() {
|
protected void onDisabled() {
|
||||||
inputFormat = null;
|
inputFormat = null;
|
||||||
clearReportedVideoSize();
|
reportedVideoSize = null;
|
||||||
lowerFirstFrameState(C.FIRST_FRAME_NOT_RENDERED_ONLY_ALLOWED_IF_STARTED);
|
lowerFirstFrameState(C.FIRST_FRAME_NOT_RENDERED_ONLY_ALLOWED_IF_STARTED);
|
||||||
try {
|
try {
|
||||||
setSourceDrmSession(null);
|
setSourceDrmSession(null);
|
||||||
|
|
@ -365,7 +366,7 @@ public abstract class DecoderVideoRenderer extends BaseRenderer {
|
||||||
outputBuffer.release();
|
outputBuffer.release();
|
||||||
outputBuffer = null;
|
outputBuffer = null;
|
||||||
}
|
}
|
||||||
decoder.flush();
|
checkNotNull(decoder).flush();
|
||||||
decoderReceivedBuffers = false;
|
decoderReceivedBuffers = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -403,7 +404,8 @@ public abstract class DecoderVideoRenderer extends BaseRenderer {
|
||||||
|
|
||||||
if (decoder == null) {
|
if (decoder == null) {
|
||||||
maybeInitDecoder();
|
maybeInitDecoder();
|
||||||
eventDispatcher.inputFormatChanged(inputFormat, /* decoderReuseEvaluation= */ null);
|
eventDispatcher.inputFormatChanged(
|
||||||
|
checkNotNull(inputFormat), /* decoderReuseEvaluation= */ null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -412,12 +414,12 @@ public abstract class DecoderVideoRenderer extends BaseRenderer {
|
||||||
evaluation =
|
evaluation =
|
||||||
new DecoderReuseEvaluation(
|
new DecoderReuseEvaluation(
|
||||||
decoder.getName(),
|
decoder.getName(),
|
||||||
oldFormat,
|
checkNotNull(oldFormat),
|
||||||
newFormat,
|
newFormat,
|
||||||
REUSE_RESULT_NO,
|
REUSE_RESULT_NO,
|
||||||
DISCARD_REASON_DRM_SESSION_CHANGED);
|
DISCARD_REASON_DRM_SESSION_CHANGED);
|
||||||
} else {
|
} else {
|
||||||
evaluation = canReuseDecoder(decoder.getName(), oldFormat, newFormat);
|
evaluation = canReuseDecoder(decoder.getName(), checkNotNull(oldFormat), newFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (evaluation.result == REUSE_RESULT_NO) {
|
if (evaluation.result == REUSE_RESULT_NO) {
|
||||||
|
|
@ -430,7 +432,7 @@ public abstract class DecoderVideoRenderer extends BaseRenderer {
|
||||||
maybeInitDecoder();
|
maybeInitDecoder();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
eventDispatcher.inputFormatChanged(inputFormat, evaluation);
|
eventDispatcher.inputFormatChanged(checkNotNull(inputFormat), evaluation);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -598,9 +600,9 @@ public abstract class DecoderVideoRenderer extends BaseRenderer {
|
||||||
} else {
|
} else {
|
||||||
maybeNotifyVideoSizeChanged(outputBuffer.width, outputBuffer.height);
|
maybeNotifyVideoSizeChanged(outputBuffer.width, outputBuffer.height);
|
||||||
if (renderYuv) {
|
if (renderYuv) {
|
||||||
outputBufferRenderer.setOutputBuffer(outputBuffer);
|
checkNotNull(outputBufferRenderer).setOutputBuffer(outputBuffer);
|
||||||
} else {
|
} else {
|
||||||
renderOutputBufferToSurface(outputBuffer, outputSurface);
|
renderOutputBufferToSurface(outputBuffer, checkNotNull(outputSurface));
|
||||||
}
|
}
|
||||||
consecutiveDroppedFrameCount = 0;
|
consecutiveDroppedFrameCount = 0;
|
||||||
decoderCounters.renderedOutputBufferCount++;
|
decoderCounters.renderedOutputBufferCount++;
|
||||||
|
|
@ -715,11 +717,11 @@ public abstract class DecoderVideoRenderer extends BaseRenderer {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
long decoderInitializingTimestamp = SystemClock.elapsedRealtime();
|
long decoderInitializingTimestamp = SystemClock.elapsedRealtime();
|
||||||
decoder = createDecoder(inputFormat, cryptoConfig);
|
decoder = createDecoder(checkNotNull(inputFormat), cryptoConfig);
|
||||||
setDecoderOutputMode(outputMode);
|
setDecoderOutputMode(outputMode);
|
||||||
long decoderInitializedTimestamp = SystemClock.elapsedRealtime();
|
long decoderInitializedTimestamp = SystemClock.elapsedRealtime();
|
||||||
eventDispatcher.decoderInitialized(
|
eventDispatcher.decoderInitialized(
|
||||||
decoder.getName(),
|
checkNotNull(decoder).getName(),
|
||||||
decoderInitializedTimestamp,
|
decoderInitializedTimestamp,
|
||||||
decoderInitializedTimestamp - decoderInitializingTimestamp);
|
decoderInitializedTimestamp - decoderInitializingTimestamp);
|
||||||
decoderCounters.decoderInitCount++;
|
decoderCounters.decoderInitCount++;
|
||||||
|
|
@ -749,10 +751,11 @@ public abstract class DecoderVideoRenderer extends BaseRenderer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DecoderInputBuffer inputBuffer = checkNotNull(this.inputBuffer);
|
||||||
if (decoderReinitializationState == REINITIALIZATION_STATE_SIGNAL_END_OF_STREAM) {
|
if (decoderReinitializationState == REINITIALIZATION_STATE_SIGNAL_END_OF_STREAM) {
|
||||||
inputBuffer.setFlags(C.BUFFER_FLAG_END_OF_STREAM);
|
inputBuffer.setFlags(C.BUFFER_FLAG_END_OF_STREAM);
|
||||||
decoder.queueInputBuffer(inputBuffer);
|
checkNotNull(decoder).queueInputBuffer(inputBuffer);
|
||||||
inputBuffer = null;
|
this.inputBuffer = null;
|
||||||
decoderReinitializationState = REINITIALIZATION_STATE_WAIT_END_OF_STREAM;
|
decoderReinitializationState = REINITIALIZATION_STATE_WAIT_END_OF_STREAM;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -767,12 +770,12 @@ public abstract class DecoderVideoRenderer extends BaseRenderer {
|
||||||
case C.RESULT_BUFFER_READ:
|
case C.RESULT_BUFFER_READ:
|
||||||
if (inputBuffer.isEndOfStream()) {
|
if (inputBuffer.isEndOfStream()) {
|
||||||
inputStreamEnded = true;
|
inputStreamEnded = true;
|
||||||
decoder.queueInputBuffer(inputBuffer);
|
checkNotNull(decoder).queueInputBuffer(inputBuffer);
|
||||||
inputBuffer = null;
|
this.inputBuffer = null;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (waitingForFirstSampleInFormat) {
|
if (waitingForFirstSampleInFormat) {
|
||||||
formatQueue.add(inputBuffer.timeUs, inputFormat);
|
formatQueue.add(inputBuffer.timeUs, checkNotNull(inputFormat));
|
||||||
waitingForFirstSampleInFormat = false;
|
waitingForFirstSampleInFormat = false;
|
||||||
}
|
}
|
||||||
if (inputBuffer.timeUs < getLastResetPositionUs()) {
|
if (inputBuffer.timeUs < getLastResetPositionUs()) {
|
||||||
|
|
@ -781,11 +784,11 @@ public abstract class DecoderVideoRenderer extends BaseRenderer {
|
||||||
inputBuffer.flip();
|
inputBuffer.flip();
|
||||||
inputBuffer.format = inputFormat;
|
inputBuffer.format = inputFormat;
|
||||||
onQueueInputBuffer(inputBuffer);
|
onQueueInputBuffer(inputBuffer);
|
||||||
decoder.queueInputBuffer(inputBuffer);
|
checkNotNull(decoder).queueInputBuffer(inputBuffer);
|
||||||
buffersInCodecCount++;
|
buffersInCodecCount++;
|
||||||
decoderReceivedBuffers = true;
|
decoderReceivedBuffers = true;
|
||||||
decoderCounters.queuedInputBufferCount++;
|
decoderCounters.queuedInputBufferCount++;
|
||||||
inputBuffer = null;
|
this.inputBuffer = null;
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
|
|
@ -805,7 +808,7 @@ public abstract class DecoderVideoRenderer extends BaseRenderer {
|
||||||
private boolean drainOutputBuffer(long positionUs, long elapsedRealtimeUs)
|
private boolean drainOutputBuffer(long positionUs, long elapsedRealtimeUs)
|
||||||
throws ExoPlaybackException, DecoderException {
|
throws ExoPlaybackException, DecoderException {
|
||||||
if (outputBuffer == null) {
|
if (outputBuffer == null) {
|
||||||
outputBuffer = decoder.dequeueOutputBuffer();
|
outputBuffer = checkNotNull(decoder).dequeueOutputBuffer();
|
||||||
if (outputBuffer == null) {
|
if (outputBuffer == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -828,7 +831,7 @@ public abstract class DecoderVideoRenderer extends BaseRenderer {
|
||||||
|
|
||||||
boolean processedOutputBuffer = processOutputBuffer(positionUs, elapsedRealtimeUs);
|
boolean processedOutputBuffer = processOutputBuffer(positionUs, elapsedRealtimeUs);
|
||||||
if (processedOutputBuffer) {
|
if (processedOutputBuffer) {
|
||||||
onProcessedOutputBuffer(outputBuffer.timeUs);
|
onProcessedOutputBuffer(checkNotNull(outputBuffer).timeUs);
|
||||||
outputBuffer = null;
|
outputBuffer = null;
|
||||||
}
|
}
|
||||||
return processedOutputBuffer;
|
return processedOutputBuffer;
|
||||||
|
|
@ -850,7 +853,9 @@ public abstract class DecoderVideoRenderer extends BaseRenderer {
|
||||||
initialPositionUs = positionUs;
|
initialPositionUs = positionUs;
|
||||||
}
|
}
|
||||||
|
|
||||||
long earlyUs = outputBuffer.timeUs - positionUs;
|
VideoDecoderOutputBuffer outputBuffer = checkNotNull(this.outputBuffer);
|
||||||
|
long bufferTimeUs = outputBuffer.timeUs;
|
||||||
|
long earlyUs = bufferTimeUs - positionUs;
|
||||||
if (!hasOutput()) {
|
if (!hasOutput()) {
|
||||||
// Skip frames in sync with playback, so we'll be at the right frame if the mode changes.
|
// Skip frames in sync with playback, so we'll be at the right frame if the mode changes.
|
||||||
if (isBufferLate(earlyUs)) {
|
if (isBufferLate(earlyUs)) {
|
||||||
|
|
@ -860,14 +865,19 @@ public abstract class DecoderVideoRenderer extends BaseRenderer {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
long presentationTimeUs = outputBuffer.timeUs - outputStreamOffsetUs;
|
Format format = formatQueue.pollFloor(bufferTimeUs);
|
||||||
Format format = formatQueue.pollFloor(presentationTimeUs);
|
|
||||||
if (format != null) {
|
if (format != null) {
|
||||||
outputFormat = format;
|
outputFormat = format;
|
||||||
|
} else if (outputFormat == null) {
|
||||||
|
// After a stream change or after the initial start, there should be an input format change
|
||||||
|
// which we've not found. Check the Format queue in case the corresponding presentation
|
||||||
|
// timestamp is greater than bufferTimeUs
|
||||||
|
outputFormat = formatQueue.pollFirst();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
long presentationTimeUs = bufferTimeUs - outputStreamOffsetUs;
|
||||||
if (shouldForceRender(earlyUs)) {
|
if (shouldForceRender(earlyUs)) {
|
||||||
renderOutputBuffer(outputBuffer, presentationTimeUs, outputFormat);
|
renderOutputBuffer(outputBuffer, presentationTimeUs, checkNotNull(outputFormat));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -886,7 +896,7 @@ public abstract class DecoderVideoRenderer extends BaseRenderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (earlyUs < 30000) {
|
if (earlyUs < 30000) {
|
||||||
renderOutputBuffer(outputBuffer, presentationTimeUs, outputFormat);
|
renderOutputBuffer(outputBuffer, presentationTimeUs, checkNotNull(outputFormat));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -925,7 +935,7 @@ public abstract class DecoderVideoRenderer extends BaseRenderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onOutputRemoved() {
|
private void onOutputRemoved() {
|
||||||
clearReportedVideoSize();
|
reportedVideoSize = null;
|
||||||
lowerFirstFrameState(C.FIRST_FRAME_NOT_RENDERED);
|
lowerFirstFrameState(C.FIRST_FRAME_NOT_RENDERED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -950,20 +960,18 @@ public abstract class DecoderVideoRenderer extends BaseRenderer {
|
||||||
private void maybeNotifyRenderedFirstFrame() {
|
private void maybeNotifyRenderedFirstFrame() {
|
||||||
if (firstFrameState != C.FIRST_FRAME_RENDERED) {
|
if (firstFrameState != C.FIRST_FRAME_RENDERED) {
|
||||||
firstFrameState = C.FIRST_FRAME_RENDERED;
|
firstFrameState = C.FIRST_FRAME_RENDERED;
|
||||||
eventDispatcher.renderedFirstFrame(output);
|
if (output != null) {
|
||||||
|
eventDispatcher.renderedFirstFrame(output);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void maybeRenotifyRenderedFirstFrame() {
|
private void maybeRenotifyRenderedFirstFrame() {
|
||||||
if (firstFrameState == C.FIRST_FRAME_RENDERED) {
|
if (firstFrameState == C.FIRST_FRAME_RENDERED && output != null) {
|
||||||
eventDispatcher.renderedFirstFrame(output);
|
eventDispatcher.renderedFirstFrame(output);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void clearReportedVideoSize() {
|
|
||||||
reportedVideoSize = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void maybeNotifyVideoSizeChanged(int width, int height) {
|
private void maybeNotifyVideoSizeChanged(int width, int height) {
|
||||||
if (reportedVideoSize == null
|
if (reportedVideoSize == null
|
||||||
|| reportedVideoSize.width != width
|
|| reportedVideoSize.width != width
|
||||||
|
|
|
||||||
|
|
@ -408,7 +408,6 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer implements Video
|
||||||
decodedVideoSize = VideoSize.UNKNOWN;
|
decodedVideoSize = VideoSize.UNKNOWN;
|
||||||
tunnelingAudioSessionId = C.AUDIO_SESSION_ID_UNSET;
|
tunnelingAudioSessionId = C.AUDIO_SESSION_ID_UNSET;
|
||||||
firstFrameState = C.FIRST_FRAME_NOT_RENDERED_ONLY_ALLOWED_IF_STARTED;
|
firstFrameState = C.FIRST_FRAME_NOT_RENDERED_ONLY_ALLOWED_IF_STARTED;
|
||||||
reportedVideoSize = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue