Explicitly mark DecoderOutputBuffer as shouldBeSkipped if needed
In some cases, SimpleDecoder output needs to be skipped for rendering because the decoder produces no data. This is one of the remaining usages of BUFFER_FLAG_DECODE_ONLY at the moment and can be more directly solved without using the flag. SimpleDecoder still needs to check the flag though for backwards compatbility with custom decoders while the flag is not completely removed. PiperOrigin-RevId: 570345233
This commit is contained in:
parent
1bb501ab50
commit
c8aac24ffd
@ -29,6 +29,9 @@
|
|||||||
* Smooth Streaming Extension:
|
* Smooth Streaming Extension:
|
||||||
* RTSP Extension:
|
* RTSP Extension:
|
||||||
* Decoder Extensions (FFmpeg, VP9, AV1, etc.):
|
* Decoder Extensions (FFmpeg, VP9, AV1, etc.):
|
||||||
|
* Add `DecoderOutputBuffer.shouldBeSkipped` to directly mark output
|
||||||
|
buffers that don't need to be presented. This is preferred over
|
||||||
|
`C.BUFFER_FLAG_DECODE_ONLY`.
|
||||||
* MIDI extension:
|
* MIDI extension:
|
||||||
* Leanback extension:
|
* Leanback extension:
|
||||||
* Cast Extension:
|
* Cast Extension:
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package androidx.media3.decoder;
|
package androidx.media3.decoder;
|
||||||
|
|
||||||
|
import androidx.annotation.CallSuper;
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
|
|
||||||
@ -25,6 +26,7 @@ public abstract class Buffer {
|
|||||||
private @C.BufferFlags int flags;
|
private @C.BufferFlags int flags;
|
||||||
|
|
||||||
/** Clears the buffer. */
|
/** Clears the buffer. */
|
||||||
|
@CallSuper
|
||||||
public void clear() {
|
public void clear() {
|
||||||
flags = 0;
|
flags = 0;
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package androidx.media3.decoder;
|
package androidx.media3.decoder;
|
||||||
|
|
||||||
|
import androidx.annotation.CallSuper;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
|
|
||||||
/** Output buffer decoded by a {@link Decoder}. */
|
/** Output buffer decoded by a {@link Decoder}. */
|
||||||
@ -40,6 +41,21 @@ public abstract class DecoderOutputBuffer extends Buffer {
|
|||||||
*/
|
*/
|
||||||
public int skippedOutputBufferCount;
|
public int skippedOutputBufferCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether this buffer should be skipped, usually because the decoding process generated no data
|
||||||
|
* or invalid data.
|
||||||
|
*/
|
||||||
|
public boolean shouldBeSkipped;
|
||||||
|
|
||||||
/** Releases the output buffer for reuse. Must be called when the buffer is no longer needed. */
|
/** Releases the output buffer for reuse. Must be called when the buffer is no longer needed. */
|
||||||
public abstract void release();
|
public abstract void release();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@CallSuper
|
||||||
|
public void clear() {
|
||||||
|
super.clear();
|
||||||
|
timeUs = 0;
|
||||||
|
skippedOutputBufferCount = 0;
|
||||||
|
shouldBeSkipped = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -232,6 +232,7 @@ public abstract class SimpleDecoder<
|
|||||||
if (inputBuffer.isEndOfStream()) {
|
if (inputBuffer.isEndOfStream()) {
|
||||||
outputBuffer.addFlag(C.BUFFER_FLAG_END_OF_STREAM);
|
outputBuffer.addFlag(C.BUFFER_FLAG_END_OF_STREAM);
|
||||||
} else {
|
} else {
|
||||||
|
outputBuffer.timeUs = inputBuffer.timeUs;
|
||||||
if (inputBuffer.isDecodeOnly()) {
|
if (inputBuffer.isDecodeOnly()) {
|
||||||
outputBuffer.addFlag(C.BUFFER_FLAG_DECODE_ONLY);
|
outputBuffer.addFlag(C.BUFFER_FLAG_DECODE_ONLY);
|
||||||
}
|
}
|
||||||
@ -262,7 +263,7 @@ public abstract class SimpleDecoder<
|
|||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
if (flushed) {
|
if (flushed) {
|
||||||
outputBuffer.release();
|
outputBuffer.release();
|
||||||
} else if (outputBuffer.isDecodeOnly()) {
|
} else if (outputBuffer.isDecodeOnly() || outputBuffer.shouldBeSkipped) {
|
||||||
skippedOutputBufferCount++;
|
skippedOutputBufferCount++;
|
||||||
outputBuffer.release();
|
outputBuffer.release();
|
||||||
} else {
|
} else {
|
||||||
|
@ -117,7 +117,7 @@ public final class Gav1Decoder
|
|||||||
"gav1GetFrame error: " + gav1GetErrorMessage(gav1DecoderContext));
|
"gav1GetFrame error: " + gav1GetErrorMessage(gav1DecoderContext));
|
||||||
}
|
}
|
||||||
if (getFrameResult == GAV1_DECODE_ONLY) {
|
if (getFrameResult == GAV1_DECODE_ONLY) {
|
||||||
outputBuffer.addFlag(C.BUFFER_FLAG_DECODE_ONLY);
|
outputBuffer.shouldBeSkipped = true;
|
||||||
}
|
}
|
||||||
if (!decodeOnly) {
|
if (!decodeOnly) {
|
||||||
outputBuffer.format = inputBuffer.format;
|
outputBuffer.format = inputBuffer.format;
|
||||||
@ -139,9 +139,9 @@ public final class Gav1Decoder
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void releaseOutputBuffer(VideoDecoderOutputBuffer outputBuffer) {
|
protected void releaseOutputBuffer(VideoDecoderOutputBuffer outputBuffer) {
|
||||||
// Decode only frames do not acquire a reference on the internal decoder buffer and thus do not
|
// Skipped frames do not acquire a reference on the internal decoder buffer and thus do not
|
||||||
// require a call to gav1ReleaseFrame.
|
// require a call to gav1ReleaseFrame.
|
||||||
if (outputBuffer.mode == C.VIDEO_OUTPUT_MODE_SURFACE_YUV && !outputBuffer.isDecodeOnly()) {
|
if (outputBuffer.mode == C.VIDEO_OUTPUT_MODE_SURFACE_YUV && !outputBuffer.shouldBeSkipped) {
|
||||||
gav1ReleaseFrame(gav1DecoderContext, outputBuffer);
|
gav1ReleaseFrame(gav1DecoderContext, outputBuffer);
|
||||||
}
|
}
|
||||||
super.releaseOutputBuffer(outputBuffer);
|
super.releaseOutputBuffer(outputBuffer);
|
||||||
|
@ -113,13 +113,13 @@ import java.util.List;
|
|||||||
return new FfmpegDecoderException("Error decoding (see logcat).");
|
return new FfmpegDecoderException("Error decoding (see logcat).");
|
||||||
} else if (result == AUDIO_DECODER_ERROR_INVALID_DATA) {
|
} else if (result == AUDIO_DECODER_ERROR_INVALID_DATA) {
|
||||||
// Treat invalid data errors as non-fatal to match the behavior of MediaCodec. No output will
|
// Treat invalid data errors as non-fatal to match the behavior of MediaCodec. No output will
|
||||||
// be produced for this buffer, so mark it as decode-only to ensure that the audio sink's
|
// be produced for this buffer, so mark it as skipped to ensure that the audio sink's
|
||||||
// position is reset when more audio is produced.
|
// position is reset when more audio is produced.
|
||||||
outputBuffer.setFlags(C.BUFFER_FLAG_DECODE_ONLY);
|
outputBuffer.shouldBeSkipped = true;
|
||||||
return null;
|
return null;
|
||||||
} else if (result == 0) {
|
} else if (result == 0) {
|
||||||
// There's no need to output empty buffers.
|
// There's no need to output empty buffers.
|
||||||
outputBuffer.setFlags(C.BUFFER_FLAG_DECODE_ONLY);
|
outputBuffer.shouldBeSkipped = true;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (!hasOutputFormat) {
|
if (!hasOutputFormat) {
|
||||||
|
@ -229,7 +229,7 @@ public final class OpusDecoder
|
|||||||
int skipBytes = skipSamples * bytesPerSample;
|
int skipBytes = skipSamples * bytesPerSample;
|
||||||
if (result <= skipBytes) {
|
if (result <= skipBytes) {
|
||||||
skipSamples -= result / bytesPerSample;
|
skipSamples -= result / bytesPerSample;
|
||||||
outputBuffer.addFlag(C.BUFFER_FLAG_DECODE_ONLY);
|
outputBuffer.shouldBeSkipped = true;
|
||||||
outputData.position(result);
|
outputData.position(result);
|
||||||
} else {
|
} else {
|
||||||
skipSamples = 0;
|
skipSamples = 0;
|
||||||
|
@ -102,9 +102,9 @@ public final class VpxDecoder
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void releaseOutputBuffer(VideoDecoderOutputBuffer outputBuffer) {
|
protected void releaseOutputBuffer(VideoDecoderOutputBuffer outputBuffer) {
|
||||||
// Decode only frames do not acquire a reference on the internal decoder buffer and thus do not
|
// Skipped frames do not acquire a reference on the internal decoder buffer and thus do not
|
||||||
// require a call to vpxReleaseFrame.
|
// require a call to vpxReleaseFrame.
|
||||||
if (outputMode == C.VIDEO_OUTPUT_MODE_SURFACE_YUV && !outputBuffer.isDecodeOnly()) {
|
if (outputMode == C.VIDEO_OUTPUT_MODE_SURFACE_YUV && !outputBuffer.shouldBeSkipped) {
|
||||||
vpxReleaseFrame(vpxDecContext, outputBuffer);
|
vpxReleaseFrame(vpxDecContext, outputBuffer);
|
||||||
}
|
}
|
||||||
super.releaseOutputBuffer(outputBuffer);
|
super.releaseOutputBuffer(outputBuffer);
|
||||||
@ -169,11 +169,13 @@ public final class VpxDecoder
|
|||||||
outputBuffer.init(inputBuffer.timeUs, outputMode, lastSupplementalData);
|
outputBuffer.init(inputBuffer.timeUs, outputMode, lastSupplementalData);
|
||||||
int getFrameResult = vpxGetFrame(vpxDecContext, outputBuffer);
|
int getFrameResult = vpxGetFrame(vpxDecContext, outputBuffer);
|
||||||
if (getFrameResult == 1) {
|
if (getFrameResult == 1) {
|
||||||
outputBuffer.addFlag(C.BUFFER_FLAG_DECODE_ONLY);
|
outputBuffer.shouldBeSkipped = true;
|
||||||
} else if (getFrameResult == -1) {
|
} else if (getFrameResult == -1) {
|
||||||
return new VpxDecoderException("Buffer initialization failed.");
|
return new VpxDecoderException("Buffer initialization failed.");
|
||||||
}
|
}
|
||||||
outputBuffer.format = inputBuffer.format;
|
outputBuffer.format = inputBuffer.format;
|
||||||
|
} else {
|
||||||
|
outputBuffer.shouldBeSkipped = true;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user