mirror of
https://github.com/androidx/media.git
synced 2025-05-11 09:39:52 +08:00
Simplify output buffer handling in transformer
We can dequeue as part of getting output buffers (or output buffer info) in `MediaCodecAdapterWrapper`, which simplifies the caller slightly. Also try to make minor clarifications in method naming in `TransformerAudioRenderer`. #minor-release PiperOrigin-RevId: 354890796
This commit is contained in:
parent
50344e2703
commit
17ca191fb5
@ -182,66 +182,24 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
inputBuffer.data = null;
|
inputBuffer.data = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Dequeues an output buffer, if available.
|
|
||||||
*
|
|
||||||
* <p>Once this method returns {@code true}, call {@link #getOutputBuffer()} to access the
|
|
||||||
* dequeued buffer.
|
|
||||||
*
|
|
||||||
* @return Whether an output buffer is available.
|
|
||||||
*/
|
|
||||||
public boolean maybeDequeueOutputBuffer() {
|
|
||||||
if (outputBufferIndex >= 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (outputStreamEnded) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
outputBufferIndex = codec.dequeueOutputBufferIndex(outputBufferInfo);
|
|
||||||
if (outputBufferIndex < 0) {
|
|
||||||
if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
|
|
||||||
outputFormat = getFormat(codec.getOutputFormat());
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ((outputBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
|
|
||||||
outputStreamEnded = true;
|
|
||||||
if (outputBufferInfo.size == 0) {
|
|
||||||
releaseOutputBuffer();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((outputBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
|
|
||||||
// Encountered a CSD buffer, skip it.
|
|
||||||
releaseOutputBuffer();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
outputBuffer = checkNotNull(codec.getOutputBuffer(outputBufferIndex));
|
|
||||||
outputBuffer.position(outputBufferInfo.offset);
|
|
||||||
outputBuffer.limit(outputBufferInfo.offset + outputBufferInfo.size);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns the current output format, if available. */
|
/** Returns the current output format, if available. */
|
||||||
@Nullable
|
@Nullable
|
||||||
public Format getOutputFormat() {
|
public Format getOutputFormat() {
|
||||||
|
// The format is updated when dequeueing a 'special' buffer index, so attempt to dequeue now.
|
||||||
|
maybeDequeueOutputBuffer();
|
||||||
return outputFormat;
|
return outputFormat;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the current output {@link ByteBuffer}, if available. */
|
/** Returns the current output {@link ByteBuffer}, if available. */
|
||||||
@Nullable
|
@Nullable
|
||||||
public ByteBuffer getOutputBuffer() {
|
public ByteBuffer getOutputBuffer() {
|
||||||
return outputBuffer;
|
return maybeDequeueOutputBuffer() ? outputBuffer : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the {@link BufferInfo} associated with the current output buffer, if available. */
|
/** Returns the {@link BufferInfo} associated with the current output buffer, if available. */
|
||||||
@Nullable
|
@Nullable
|
||||||
public BufferInfo getOutputBufferInfo() {
|
public BufferInfo getOutputBufferInfo() {
|
||||||
return outputBuffer == null ? null : outputBufferInfo;
|
return maybeDequeueOutputBuffer() ? outputBufferInfo : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -267,6 +225,45 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
codec.release();
|
codec.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if there is already an output buffer pending. Otherwise attempts to dequeue an
|
||||||
|
* output buffer and returns whether there is a new output buffer.
|
||||||
|
*/
|
||||||
|
private boolean maybeDequeueOutputBuffer() {
|
||||||
|
if (outputBufferIndex >= 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (outputStreamEnded) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
outputBufferIndex = codec.dequeueOutputBufferIndex(outputBufferInfo);
|
||||||
|
if (outputBufferIndex < 0) {
|
||||||
|
if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
|
||||||
|
outputFormat = getFormat(codec.getOutputFormat());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ((outputBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
|
||||||
|
outputStreamEnded = true;
|
||||||
|
if (outputBufferInfo.size == 0) {
|
||||||
|
releaseOutputBuffer();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((outputBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
|
||||||
|
// Encountered a CSD buffer, skip it.
|
||||||
|
releaseOutputBuffer();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
outputBuffer = checkNotNull(codec.getOutputBuffer(outputBufferIndex));
|
||||||
|
outputBuffer.position(outputBufferInfo.offset);
|
||||||
|
outputBuffer.limit(outputBufferInfo.offset + outputBufferInfo.size);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private static Format getFormat(MediaFormat mediaFormat) {
|
private static Format getFormat(MediaFormat mediaFormat) {
|
||||||
ImmutableList.Builder<byte[]> csdBuffers = new ImmutableList.Builder<>();
|
ImmutableList.Builder<byte[]> csdBuffers = new ImmutableList.Builder<>();
|
||||||
int csdIndex = 0;
|
int csdIndex = 0;
|
||||||
|
@ -114,19 +114,18 @@ import java.nio.ByteBuffer;
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!setupDecoder()) {
|
if (ensureDecoderConfigured()) {
|
||||||
return;
|
if (ensureEncoderAndAudioProcessingConfigured()) {
|
||||||
|
while (drainEncoderToFeedMuxer()) {}
|
||||||
|
if (sonicAudioProcessor.isActive()) {
|
||||||
|
while (drainSonicToFeedEncoder()) {}
|
||||||
|
while (drainDecoderToFeedSonic()) {}
|
||||||
|
} else {
|
||||||
|
while (drainDecoderToFeedEncoder()) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (feedDecoderInputFromSource()) {}
|
||||||
}
|
}
|
||||||
setupEncoderAndMaybeSonic();
|
|
||||||
|
|
||||||
while (drainEncoderToFeedMuxer()) {}
|
|
||||||
if (sonicAudioProcessor.isActive()) {
|
|
||||||
while (drainSonicToFeedEncoder()) {}
|
|
||||||
while (drainDecoderToFeedSonic()) {}
|
|
||||||
} else {
|
|
||||||
while (drainDecoderToFeedEncoder()) {}
|
|
||||||
}
|
|
||||||
while (feedDecoderInputFromSource()) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -136,8 +135,6 @@ import java.nio.ByteBuffer;
|
|||||||
private boolean drainEncoderToFeedMuxer() {
|
private boolean drainEncoderToFeedMuxer() {
|
||||||
MediaCodecAdapterWrapper encoder = checkNotNull(this.encoder);
|
MediaCodecAdapterWrapper encoder = checkNotNull(this.encoder);
|
||||||
if (!hasEncoderOutputFormat) {
|
if (!hasEncoderOutputFormat) {
|
||||||
// Dequeue output format change.
|
|
||||||
encoder.maybeDequeueOutputBuffer();
|
|
||||||
@Nullable Format encoderOutputFormat = encoder.getOutputFormat();
|
@Nullable Format encoderOutputFormat = encoder.getOutputFormat();
|
||||||
if (encoderOutputFormat == null) {
|
if (encoderOutputFormat == null) {
|
||||||
return false;
|
return false;
|
||||||
@ -147,19 +144,15 @@ import java.nio.ByteBuffer;
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (encoder.isEnded()) {
|
if (encoder.isEnded()) {
|
||||||
// Encoder output stream ended and output is empty or null so end muxer track.
|
|
||||||
muxerWrapper.endTrack(getTrackType());
|
muxerWrapper.endTrack(getTrackType());
|
||||||
muxerWrapperTrackEnded = true;
|
muxerWrapperTrackEnded = true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@Nullable ByteBuffer encoderOutputBuffer = encoder.getOutputBuffer();
|
||||||
if (!encoder.maybeDequeueOutputBuffer()) {
|
if (encoderOutputBuffer == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ByteBuffer encoderOutputBuffer = checkNotNull(encoder.getOutputBuffer());
|
|
||||||
BufferInfo encoderOutputBufferInfo = checkNotNull(encoder.getOutputBufferInfo());
|
BufferInfo encoderOutputBufferInfo = checkNotNull(encoder.getOutputBufferInfo());
|
||||||
|
|
||||||
if (!muxerWrapper.writeSample(
|
if (!muxerWrapper.writeSample(
|
||||||
getTrackType(),
|
getTrackType(),
|
||||||
encoderOutputBuffer,
|
encoderOutputBuffer,
|
||||||
@ -187,19 +180,15 @@ import java.nio.ByteBuffer;
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!decoder.maybeDequeueOutputBuffer()) {
|
@Nullable ByteBuffer decoderOutputBuffer = decoder.getOutputBuffer();
|
||||||
|
if (decoderOutputBuffer == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isSpeedChanging(checkNotNull(decoder.getOutputBufferInfo()))) {
|
if (isSpeedChanging(checkNotNull(decoder.getOutputBufferInfo()))) {
|
||||||
flushSonicAndSetSpeed(currentSpeed);
|
flushSonicAndSetSpeed(currentSpeed);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ByteBuffer decoderOutputBuffer = checkNotNull(decoder.getOutputBuffer());
|
|
||||||
|
|
||||||
feedEncoder(decoderOutputBuffer);
|
feedEncoder(decoderOutputBuffer);
|
||||||
|
|
||||||
if (!decoderOutputBuffer.hasRemaining()) {
|
if (!decoderOutputBuffer.hasRemaining()) {
|
||||||
decoder.releaseOutputBuffer();
|
decoder.releaseOutputBuffer();
|
||||||
}
|
}
|
||||||
@ -246,8 +235,8 @@ import java.nio.ByteBuffer;
|
|||||||
drainingSonicForSpeedChange = false;
|
drainingSonicForSpeedChange = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sonic invalidates the output buffer when more input is queued, so we don't queue if there is
|
// Sonic invalidates any previous output buffer when more input is queued, so we don't queue if
|
||||||
// output still to be processed.
|
// there is output still to be processed.
|
||||||
if (sonicOutputBuffer.hasRemaining()) {
|
if (sonicOutputBuffer.hasRemaining()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -256,20 +245,17 @@ import java.nio.ByteBuffer;
|
|||||||
sonicAudioProcessor.queueEndOfStream();
|
sonicAudioProcessor.queueEndOfStream();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
checkState(!sonicAudioProcessor.isEnded());
|
checkState(!sonicAudioProcessor.isEnded());
|
||||||
|
|
||||||
if (!decoder.maybeDequeueOutputBuffer()) {
|
@Nullable ByteBuffer decoderOutputBuffer = decoder.getOutputBuffer();
|
||||||
|
if (decoderOutputBuffer == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isSpeedChanging(checkNotNull(decoder.getOutputBufferInfo()))) {
|
if (isSpeedChanging(checkNotNull(decoder.getOutputBufferInfo()))) {
|
||||||
sonicAudioProcessor.queueEndOfStream();
|
sonicAudioProcessor.queueEndOfStream();
|
||||||
drainingSonicForSpeedChange = true;
|
drainingSonicForSpeedChange = true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ByteBuffer decoderOutputBuffer = checkNotNull(decoder.getOutputBuffer());
|
|
||||||
sonicAudioProcessor.queueInput(decoderOutputBuffer);
|
sonicAudioProcessor.queueInput(decoderOutputBuffer);
|
||||||
if (!decoderOutputBuffer.hasRemaining()) {
|
if (!decoderOutputBuffer.hasRemaining()) {
|
||||||
decoder.releaseOutputBuffer();
|
decoder.releaseOutputBuffer();
|
||||||
@ -337,18 +323,23 @@ import java.nio.ByteBuffer;
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configures the {@link #encoder} and Sonic (if applicable), if they have not been configured
|
* Attempts to configure the {@link #encoder} and Sonic (if applicable), if they have not been
|
||||||
* yet.
|
* configured yet, and returns whether they have been configured.
|
||||||
*/
|
*/
|
||||||
private void setupEncoderAndMaybeSonic() throws ExoPlaybackException {
|
private boolean ensureEncoderAndAudioProcessingConfigured() throws ExoPlaybackException {
|
||||||
if (encoder != null) {
|
if (encoder != null) {
|
||||||
return;
|
return true;
|
||||||
|
}
|
||||||
|
MediaCodecAdapterWrapper decoder = checkNotNull(this.decoder);
|
||||||
|
@Nullable Format decoderOutputFormat = decoder.getOutputFormat();
|
||||||
|
if (decoderOutputFormat == null) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
// TODO(b/161127201): Use the decoder output format once the decoder is fed before setting up
|
|
||||||
// the encoder.
|
|
||||||
AudioFormat outputAudioFormat =
|
AudioFormat outputAudioFormat =
|
||||||
new AudioFormat(
|
new AudioFormat(
|
||||||
checkNotNull(inputFormat).sampleRate, inputFormat.channelCount, C.ENCODING_PCM_16BIT);
|
decoderOutputFormat.sampleRate,
|
||||||
|
decoderOutputFormat.channelCount,
|
||||||
|
decoderOutputFormat.pcmEncoding);
|
||||||
if (transformation.flattenForSlowMotion) {
|
if (transformation.flattenForSlowMotion) {
|
||||||
try {
|
try {
|
||||||
outputAudioFormat = sonicAudioProcessor.configure(outputAudioFormat);
|
outputAudioFormat = sonicAudioProcessor.configure(outputAudioFormat);
|
||||||
@ -370,13 +361,14 @@ import java.nio.ByteBuffer;
|
|||||||
throw createRendererException(e);
|
throw createRendererException(e);
|
||||||
}
|
}
|
||||||
encoderInputAudioFormat = outputAudioFormat;
|
encoderInputAudioFormat = outputAudioFormat;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempts to configure the {@link #decoder} if it has not been configured yet, and returns
|
* Attempts to configure the {@link #decoder} if it has not been configured yet, and returns
|
||||||
* whether the decoder has been configured.
|
* whether the decoder has been configured.
|
||||||
*/
|
*/
|
||||||
private boolean setupDecoder() throws ExoPlaybackException {
|
private boolean ensureDecoderConfigured() throws ExoPlaybackException {
|
||||||
if (decoder != null) {
|
if (decoder != null) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user