mirror of
https://github.com/androidx/media.git
synced 2025-05-03 21:57:46 +08:00
Allow writing multiple frame in a buffer
Currently only one access unit can be written per buffer write. This has been found to be power inefficient in an offload situation. #exo-offload PiperOrigin-RevId: 294886188
This commit is contained in:
parent
5949cbecc3
commit
ebce903aaf
@ -27,6 +27,9 @@
|
|||||||
* Add `DataSpec.Builder` and deprecate most `DataSpec` constructors.
|
* Add `DataSpec.Builder` and deprecate most `DataSpec` constructors.
|
||||||
* Add `DataSpec.customData` to allow applications to pass custom data through
|
* Add `DataSpec.customData` to allow applications to pass custom data through
|
||||||
`DataSource` chains.
|
`DataSource` chains.
|
||||||
|
* Move the handling of encoded buffers in audio passthrough from
|
||||||
|
`AudioSink.handleBuffer` to `AudioSink.handleEncodedBuffer` to allow
|
||||||
|
passing multiple encoded frames in one buffer.
|
||||||
* Text:
|
* Text:
|
||||||
* Parse `<ruby>` and `<rt>` tags in WebVTT subtitles (rendering is coming
|
* Parse `<ruby>` and `<rt>` tags in WebVTT subtitles (rendering is coming
|
||||||
later).
|
later).
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
package com.google.android.exoplayer2.audio;
|
package com.google.android.exoplayer2.audio;
|
||||||
|
|
||||||
import android.media.AudioTrack;
|
import android.media.AudioTrack;
|
||||||
|
import androidx.annotation.IntRange;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.Format;
|
import com.google.android.exoplayer2.Format;
|
||||||
@ -222,7 +223,7 @@ public interface AudioSink {
|
|||||||
void handleDiscontinuity();
|
void handleDiscontinuity();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempts to process data from a {@link ByteBuffer}, starting from its current position and
|
* Attempts to process PCM data from a {@link ByteBuffer}, starting from its current position and
|
||||||
* ending at its limit (exclusive). The position of the {@link ByteBuffer} is advanced by the
|
* ending at its limit (exclusive). The position of the {@link ByteBuffer} is advanced by the
|
||||||
* number of bytes that were handled. {@link Listener#onPositionDiscontinuity()} will be called if
|
* number of bytes that were handled. {@link Listener#onPositionDiscontinuity()} will be called if
|
||||||
* {@code presentationTimeUs} is discontinuous with the last buffer handled since the last reset.
|
* {@code presentationTimeUs} is discontinuous with the last buffer handled since the last reset.
|
||||||
@ -232,6 +233,9 @@ public interface AudioSink {
|
|||||||
* except in the case of an intervening call to {@link #flush()} (or to {@link #configure(int,
|
* except in the case of an intervening call to {@link #flush()} (or to {@link #configure(int,
|
||||||
* int, int, int, int[], int, int)} that causes the sink to be flushed).
|
* int, int, int, int[], int, int)} that causes the sink to be flushed).
|
||||||
*
|
*
|
||||||
|
* <p>For encoded data (eg in passthrough), use {@link #handleEncodedBuffer(ByteBuffer, long,
|
||||||
|
* int)}.
|
||||||
|
*
|
||||||
* @param buffer The buffer containing audio data.
|
* @param buffer The buffer containing audio data.
|
||||||
* @param presentationTimeUs The presentation timestamp of the buffer in microseconds.
|
* @param presentationTimeUs The presentation timestamp of the buffer in microseconds.
|
||||||
* @return Whether the buffer was handled fully.
|
* @return Whether the buffer was handled fully.
|
||||||
@ -241,6 +245,27 @@ public interface AudioSink {
|
|||||||
boolean handleBuffer(ByteBuffer buffer, long presentationTimeUs)
|
boolean handleBuffer(ByteBuffer buffer, long presentationTimeUs)
|
||||||
throws InitializationException, WriteException;
|
throws InitializationException, WriteException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to process data from a {@link ByteBuffer}, starting from its current position and
|
||||||
|
* ending at its limit (exclusive).
|
||||||
|
*
|
||||||
|
* <p>This method is the same as {@link #handleBuffer(ByteBuffer, long)} for encoded (non PCM)
|
||||||
|
* audio. The only difference is that it requires to pass the number of audio encoded access
|
||||||
|
* unites (frames) in the buffer. This is used in passthrough mode.
|
||||||
|
*
|
||||||
|
* @param buffer The buffer containing audio data.
|
||||||
|
* @param presentationTimeUs The presentation timestamp of the buffer in microseconds.
|
||||||
|
* @param accessUnitCount The number of encoded access units in the buffer.
|
||||||
|
* @return Whether the buffer was handled fully.
|
||||||
|
* @throws InitializationException If an error occurs initializing the sink.
|
||||||
|
* @throws WriteException If an error occurs writing the audio data.
|
||||||
|
*/
|
||||||
|
default boolean handleEncodedBuffer(
|
||||||
|
ByteBuffer buffer, long presentationTimeUs, @IntRange(from = 1) int accessUnitCount)
|
||||||
|
throws InitializationException, WriteException {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Processes any remaining data. {@link #isEnded()} will return {@code true} when no data remains.
|
* Processes any remaining data. {@link #isEnded()} will return {@code true} when no data remains.
|
||||||
*
|
*
|
||||||
|
@ -260,6 +260,7 @@ public final class DefaultAudioSink implements AudioSink {
|
|||||||
private ByteBuffer[] outputBuffers;
|
private ByteBuffer[] outputBuffers;
|
||||||
@Nullable private ByteBuffer inputBuffer;
|
@Nullable private ByteBuffer inputBuffer;
|
||||||
@Nullable private ByteBuffer outputBuffer;
|
@Nullable private ByteBuffer outputBuffer;
|
||||||
|
int outputBufferEncodedAccessUnitCount;
|
||||||
private byte[] preV21OutputBuffer;
|
private byte[] preV21OutputBuffer;
|
||||||
private int preV21OutputBufferOffset;
|
private int preV21OutputBufferOffset;
|
||||||
private int drainingAudioProcessorIndex;
|
private int drainingAudioProcessorIndex;
|
||||||
@ -559,13 +560,28 @@ public final class DefaultAudioSink implements AudioSink {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("ReferenceEquality")
|
|
||||||
public boolean handleBuffer(ByteBuffer buffer, long presentationTimeUs)
|
public boolean handleBuffer(ByteBuffer buffer, long presentationTimeUs)
|
||||||
throws InitializationException, WriteException {
|
throws InitializationException, WriteException {
|
||||||
|
Assertions.checkArgument(configuration.isInputPcm);
|
||||||
|
return handleBufferInternal(buffer, presentationTimeUs, /* encodedAccessUnitCount= */ 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean handleEncodedBuffer(
|
||||||
|
ByteBuffer buffer, long presentationTimeUs, int accessUnitCount)
|
||||||
|
throws InitializationException, WriteException {
|
||||||
|
Assertions.checkArgument(!configuration.isInputPcm);
|
||||||
|
return handleBufferInternal(buffer, presentationTimeUs, accessUnitCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("ReferenceEquality")
|
||||||
|
private boolean handleBufferInternal(
|
||||||
|
ByteBuffer buffer, long presentationTimeUs, int encodedAccessUnitCount)
|
||||||
|
throws InitializationException, WriteException {
|
||||||
Assertions.checkArgument(inputBuffer == null || buffer == inputBuffer);
|
Assertions.checkArgument(inputBuffer == null || buffer == inputBuffer);
|
||||||
|
|
||||||
if (pendingConfiguration != null) {
|
if (pendingConfiguration != null) {
|
||||||
if (!drainAudioProcessorsToEndOfStream()) {
|
if (!drainToEndOfStream()) {
|
||||||
// There's still pending data in audio processors to write to the track.
|
// There's still pending data in audio processors to write to the track.
|
||||||
return false;
|
return false;
|
||||||
} else if (!pendingConfiguration.canReuseAudioTrack(configuration)) {
|
} else if (!pendingConfiguration.canReuseAudioTrack(configuration)) {
|
||||||
@ -615,7 +631,7 @@ public final class DefaultAudioSink implements AudioSink {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (afterDrainPlaybackParameters != null) {
|
if (afterDrainPlaybackParameters != null) {
|
||||||
if (!drainAudioProcessorsToEndOfStream()) {
|
if (!drainToEndOfStream()) {
|
||||||
// Don't process any more input until draining completes.
|
// Don't process any more input until draining completes.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -641,7 +657,7 @@ public final class DefaultAudioSink implements AudioSink {
|
|||||||
startMediaTimeUsNeedsSync = true;
|
startMediaTimeUsNeedsSync = true;
|
||||||
}
|
}
|
||||||
if (startMediaTimeUsNeedsSync) {
|
if (startMediaTimeUsNeedsSync) {
|
||||||
if (!drainAudioProcessorsToEndOfStream()) {
|
if (!drainToEndOfStream()) {
|
||||||
// Don't update timing until pending AudioProcessor buffers are completely drained.
|
// Don't update timing until pending AudioProcessor buffers are completely drained.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -660,16 +676,16 @@ public final class DefaultAudioSink implements AudioSink {
|
|||||||
if (configuration.isInputPcm) {
|
if (configuration.isInputPcm) {
|
||||||
submittedPcmBytes += buffer.remaining();
|
submittedPcmBytes += buffer.remaining();
|
||||||
} else {
|
} else {
|
||||||
submittedEncodedFrames += framesPerEncodedSample;
|
submittedEncodedFrames += framesPerEncodedSample * encodedAccessUnitCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
inputBuffer = buffer;
|
inputBuffer = buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (configuration.processingEnabled) {
|
if (configuration.processingEnabled) {
|
||||||
processBuffers(presentationTimeUs);
|
processBuffers(presentationTimeUs, encodedAccessUnitCount);
|
||||||
} else {
|
} else {
|
||||||
writeBuffer(inputBuffer, presentationTimeUs);
|
writeBuffer(inputBuffer, presentationTimeUs, encodedAccessUnitCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!inputBuffer.hasRemaining()) {
|
if (!inputBuffer.hasRemaining()) {
|
||||||
@ -686,14 +702,15 @@ public final class DefaultAudioSink implements AudioSink {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processBuffers(long avSyncPresentationTimeUs) throws WriteException {
|
private void processBuffers(long avSyncPresentationTimeUs, int encodedAccessUnitCount)
|
||||||
|
throws WriteException {
|
||||||
int count = activeAudioProcessors.length;
|
int count = activeAudioProcessors.length;
|
||||||
int index = count;
|
int index = count;
|
||||||
while (index >= 0) {
|
while (index >= 0) {
|
||||||
ByteBuffer input = index > 0 ? outputBuffers[index - 1]
|
ByteBuffer input = index > 0 ? outputBuffers[index - 1]
|
||||||
: (inputBuffer != null ? inputBuffer : AudioProcessor.EMPTY_BUFFER);
|
: (inputBuffer != null ? inputBuffer : AudioProcessor.EMPTY_BUFFER);
|
||||||
if (index == count) {
|
if (index == count) {
|
||||||
writeBuffer(input, avSyncPresentationTimeUs);
|
writeBuffer(input, avSyncPresentationTimeUs, encodedAccessUnitCount);
|
||||||
} else {
|
} else {
|
||||||
AudioProcessor audioProcessor = activeAudioProcessors[index];
|
AudioProcessor audioProcessor = activeAudioProcessors[index];
|
||||||
audioProcessor.queueInput(input);
|
audioProcessor.queueInput(input);
|
||||||
@ -717,7 +734,9 @@ public final class DefaultAudioSink implements AudioSink {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("ReferenceEquality")
|
@SuppressWarnings("ReferenceEquality")
|
||||||
private void writeBuffer(ByteBuffer buffer, long avSyncPresentationTimeUs) throws WriteException {
|
private void writeBuffer(
|
||||||
|
ByteBuffer buffer, long avSyncPresentationTimeUs, int encodedAccessUnitCount)
|
||||||
|
throws WriteException {
|
||||||
if (!buffer.hasRemaining()) {
|
if (!buffer.hasRemaining()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -725,6 +744,7 @@ public final class DefaultAudioSink implements AudioSink {
|
|||||||
Assertions.checkArgument(outputBuffer == buffer);
|
Assertions.checkArgument(outputBuffer == buffer);
|
||||||
} else {
|
} else {
|
||||||
outputBuffer = buffer;
|
outputBuffer = buffer;
|
||||||
|
outputBufferEncodedAccessUnitCount = encodedAccessUnitCount;
|
||||||
if (Util.SDK_INT < 21) {
|
if (Util.SDK_INT < 21) {
|
||||||
int bytesRemaining = buffer.remaining();
|
int bytesRemaining = buffer.remaining();
|
||||||
if (preV21OutputBuffer == null || preV21OutputBuffer.length < bytesRemaining) {
|
if (preV21OutputBuffer == null || preV21OutputBuffer.length < bytesRemaining) {
|
||||||
@ -768,21 +788,22 @@ public final class DefaultAudioSink implements AudioSink {
|
|||||||
}
|
}
|
||||||
if (bytesWritten == bytesRemaining) {
|
if (bytesWritten == bytesRemaining) {
|
||||||
if (!configuration.isInputPcm) {
|
if (!configuration.isInputPcm) {
|
||||||
writtenEncodedFrames += framesPerEncodedSample;
|
writtenEncodedFrames += framesPerEncodedSample * encodedAccessUnitCount;
|
||||||
}
|
}
|
||||||
outputBuffer = null;
|
outputBuffer = null;
|
||||||
|
outputBufferEncodedAccessUnitCount = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void playToEndOfStream() throws WriteException {
|
public void playToEndOfStream() throws WriteException {
|
||||||
if (!handledEndOfStream && isInitialized() && drainAudioProcessorsToEndOfStream()) {
|
if (!handledEndOfStream && isInitialized() && drainToEndOfStream()) {
|
||||||
playPendingData();
|
playPendingData();
|
||||||
handledEndOfStream = true;
|
handledEndOfStream = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean drainAudioProcessorsToEndOfStream() throws WriteException {
|
private boolean drainToEndOfStream() throws WriteException {
|
||||||
boolean audioProcessorNeedsEndOfStream = false;
|
boolean audioProcessorNeedsEndOfStream = false;
|
||||||
if (drainingAudioProcessorIndex == C.INDEX_UNSET) {
|
if (drainingAudioProcessorIndex == C.INDEX_UNSET) {
|
||||||
drainingAudioProcessorIndex =
|
drainingAudioProcessorIndex =
|
||||||
@ -794,7 +815,8 @@ public final class DefaultAudioSink implements AudioSink {
|
|||||||
if (audioProcessorNeedsEndOfStream) {
|
if (audioProcessorNeedsEndOfStream) {
|
||||||
audioProcessor.queueEndOfStream();
|
audioProcessor.queueEndOfStream();
|
||||||
}
|
}
|
||||||
processBuffers(C.TIME_UNSET);
|
// audio is always PCM in audio processors, thus there is no encoded access unit count
|
||||||
|
processBuffers(C.TIME_UNSET, /* encodedAccessUnitCount= */ 0);
|
||||||
if (!audioProcessor.isEnded()) {
|
if (!audioProcessor.isEnded()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -804,7 +826,7 @@ public final class DefaultAudioSink implements AudioSink {
|
|||||||
|
|
||||||
// Finish writing any remaining output to the track.
|
// Finish writing any remaining output to the track.
|
||||||
if (outputBuffer != null) {
|
if (outputBuffer != null) {
|
||||||
writeBuffer(outputBuffer, C.TIME_UNSET);
|
writeBuffer(outputBuffer, C.TIME_UNSET, outputBufferEncodedAccessUnitCount);
|
||||||
if (outputBuffer != null) {
|
if (outputBuffer != null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -957,6 +979,7 @@ public final class DefaultAudioSink implements AudioSink {
|
|||||||
flushAudioProcessors();
|
flushAudioProcessors();
|
||||||
inputBuffer = null;
|
inputBuffer = null;
|
||||||
outputBuffer = null;
|
outputBuffer = null;
|
||||||
|
outputBufferEncodedAccessUnitCount = 0;
|
||||||
stoppedAudioTrack = false;
|
stoppedAudioTrack = false;
|
||||||
handledEndOfStream = false;
|
handledEndOfStream = false;
|
||||||
drainingAudioProcessorIndex = C.INDEX_UNSET;
|
drainingAudioProcessorIndex = C.INDEX_UNSET;
|
||||||
|
@ -79,6 +79,13 @@ public class ForwardingAudioSink implements AudioSink {
|
|||||||
return sink.handleBuffer(buffer, presentationTimeUs);
|
return sink.handleBuffer(buffer, presentationTimeUs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean handleEncodedBuffer(
|
||||||
|
ByteBuffer buffer, long presentationTimeUs, int accessUnitCount)
|
||||||
|
throws InitializationException, WriteException {
|
||||||
|
return sink.handleEncodedBuffer(buffer, presentationTimeUs, accessUnitCount);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void playToEndOfStream() throws WriteException {
|
public void playToEndOfStream() throws WriteException {
|
||||||
sink.playToEndOfStream();
|
sink.playToEndOfStream();
|
||||||
|
@ -90,6 +90,8 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
|||||||
private boolean allowFirstBufferPositionDiscontinuity;
|
private boolean allowFirstBufferPositionDiscontinuity;
|
||||||
private boolean allowPositionDiscontinuity;
|
private boolean allowPositionDiscontinuity;
|
||||||
|
|
||||||
|
@C.Encoding private int audioSinkEncoding;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param context A context.
|
* @param context A context.
|
||||||
* @param mediaCodecSelector A decoder selector.
|
* @param mediaCodecSelector A decoder selector.
|
||||||
@ -588,6 +590,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
|||||||
// TODO(internal: b/145658993) Use outputFormat instead.
|
// TODO(internal: b/145658993) Use outputFormat instead.
|
||||||
throw createRendererException(e, inputFormat);
|
throw createRendererException(e, inputFormat);
|
||||||
}
|
}
|
||||||
|
audioSinkEncoding = encoding;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -751,6 +754,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
|||||||
ByteBuffer buffer,
|
ByteBuffer buffer,
|
||||||
int bufferIndex,
|
int bufferIndex,
|
||||||
int bufferFlags,
|
int bufferFlags,
|
||||||
|
int sampleCount,
|
||||||
long bufferPresentationTimeUs,
|
long bufferPresentationTimeUs,
|
||||||
boolean isDecodeOnlyBuffer,
|
boolean isDecodeOnlyBuffer,
|
||||||
boolean isLastBuffer,
|
boolean isLastBuffer,
|
||||||
@ -776,16 +780,25 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean fullyConsumed;
|
||||||
try {
|
try {
|
||||||
if (audioSink.handleBuffer(buffer, bufferPresentationTimeUs)) {
|
if (Util.isEncodingLinearPcm(audioSinkEncoding)) {
|
||||||
codec.releaseOutputBuffer(bufferIndex, false);
|
fullyConsumed = audioSink.handleBuffer(buffer, bufferPresentationTimeUs);
|
||||||
decoderCounters.renderedOutputBufferCount++;
|
} else {
|
||||||
return true;
|
fullyConsumed =
|
||||||
|
audioSink.handleEncodedBuffer(buffer, bufferPresentationTimeUs, sampleCount);
|
||||||
}
|
}
|
||||||
} catch (AudioSink.InitializationException | AudioSink.WriteException e) {
|
} catch (AudioSink.InitializationException | AudioSink.WriteException e) {
|
||||||
// TODO(internal: b/145658993) Use outputFormat instead.
|
// TODO(internal: b/145658993) Use outputFormat instead.
|
||||||
throw createRendererException(e, inputFormat);
|
throw createRendererException(e, inputFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fullyConsumed) {
|
||||||
|
codec.releaseOutputBuffer(bufferIndex, false);
|
||||||
|
decoderCounters.renderedOutputBufferCount++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1738,6 +1738,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||||||
outputBuffer,
|
outputBuffer,
|
||||||
outputIndex,
|
outputIndex,
|
||||||
outputBufferInfo.flags,
|
outputBufferInfo.flags,
|
||||||
|
/* sampleCount= */ 1,
|
||||||
outputBufferInfo.presentationTimeUs,
|
outputBufferInfo.presentationTimeUs,
|
||||||
isDecodeOnlyOutputBuffer,
|
isDecodeOnlyOutputBuffer,
|
||||||
isLastOutputBuffer,
|
isLastOutputBuffer,
|
||||||
@ -1759,6 +1760,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||||||
outputBuffer,
|
outputBuffer,
|
||||||
outputIndex,
|
outputIndex,
|
||||||
outputBufferInfo.flags,
|
outputBufferInfo.flags,
|
||||||
|
/* sampleCount= */ 1,
|
||||||
outputBufferInfo.presentationTimeUs,
|
outputBufferInfo.presentationTimeUs,
|
||||||
isDecodeOnlyOutputBuffer,
|
isDecodeOnlyOutputBuffer,
|
||||||
isLastOutputBuffer,
|
isLastOutputBuffer,
|
||||||
@ -1826,6 +1828,8 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||||||
* @param buffer The output buffer to process.
|
* @param buffer The output buffer to process.
|
||||||
* @param bufferIndex The index of the output buffer.
|
* @param bufferIndex The index of the output buffer.
|
||||||
* @param bufferFlags The flags attached to the output buffer.
|
* @param bufferFlags The flags attached to the output buffer.
|
||||||
|
* @param sampleCount The number of samples extracted from the sample queue in the buffer. This
|
||||||
|
* allows handling multiple samples as a batch for efficiency.
|
||||||
* @param bufferPresentationTimeUs The presentation time of the output buffer in microseconds.
|
* @param bufferPresentationTimeUs The presentation time of the output buffer in microseconds.
|
||||||
* @param isDecodeOnlyBuffer Whether the buffer was marked with {@link C#BUFFER_FLAG_DECODE_ONLY}
|
* @param isDecodeOnlyBuffer Whether the buffer was marked with {@link C#BUFFER_FLAG_DECODE_ONLY}
|
||||||
* by the source.
|
* by the source.
|
||||||
@ -1841,6 +1845,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||||||
ByteBuffer buffer,
|
ByteBuffer buffer,
|
||||||
int bufferIndex,
|
int bufferIndex,
|
||||||
int bufferFlags,
|
int bufferFlags,
|
||||||
|
int sampleCount,
|
||||||
long bufferPresentationTimeUs,
|
long bufferPresentationTimeUs,
|
||||||
boolean isDecodeOnlyBuffer,
|
boolean isDecodeOnlyBuffer,
|
||||||
boolean isLastBuffer,
|
boolean isLastBuffer,
|
||||||
@ -1897,7 +1902,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the offset that should be subtracted from {@code bufferPresentationTimeUs} in {@link
|
* Returns the offset that should be subtracted from {@code bufferPresentationTimeUs} in {@link
|
||||||
* #processOutputBuffer(long, long, MediaCodec, ByteBuffer, int, int, long, boolean, boolean,
|
* #processOutputBuffer(long, long, MediaCodec, ByteBuffer, int, int, int, long, boolean, boolean,
|
||||||
* Format)} to get the playback position with respect to the media.
|
* Format)} to get the playback position with respect to the media.
|
||||||
*/
|
*/
|
||||||
protected final long getOutputStreamOffsetUs() {
|
protected final long getOutputStreamOffsetUs() {
|
||||||
|
@ -790,6 +790,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
|||||||
ByteBuffer buffer,
|
ByteBuffer buffer,
|
||||||
int bufferIndex,
|
int bufferIndex,
|
||||||
int bufferFlags,
|
int bufferFlags,
|
||||||
|
int sampleCount,
|
||||||
long bufferPresentationTimeUs,
|
long bufferPresentationTimeUs,
|
||||||
boolean isDecodeOnlyBuffer,
|
boolean isDecodeOnlyBuffer,
|
||||||
boolean isLastBuffer,
|
boolean isLastBuffer,
|
||||||
|
@ -155,10 +155,11 @@ import java.util.ArrayList;
|
|||||||
protected boolean processOutputBuffer(
|
protected boolean processOutputBuffer(
|
||||||
long positionUs,
|
long positionUs,
|
||||||
long elapsedRealtimeUs,
|
long elapsedRealtimeUs,
|
||||||
MediaCodec codec,
|
@Nullable MediaCodec codec,
|
||||||
ByteBuffer buffer,
|
ByteBuffer buffer,
|
||||||
int bufferIndex,
|
int bufferIndex,
|
||||||
int bufferFlags,
|
int bufferFlags,
|
||||||
|
int sampleCount,
|
||||||
long bufferPresentationTimeUs,
|
long bufferPresentationTimeUs,
|
||||||
boolean isDecodeOnlyBuffer,
|
boolean isDecodeOnlyBuffer,
|
||||||
boolean isLastBuffer,
|
boolean isLastBuffer,
|
||||||
@ -177,6 +178,7 @@ import java.util.ArrayList;
|
|||||||
buffer,
|
buffer,
|
||||||
bufferIndex,
|
bufferIndex,
|
||||||
bufferFlags,
|
bufferFlags,
|
||||||
|
sampleCount,
|
||||||
bufferPresentationTimeUs,
|
bufferPresentationTimeUs,
|
||||||
isDecodeOnlyBuffer,
|
isDecodeOnlyBuffer,
|
||||||
isLastBuffer,
|
isLastBuffer,
|
||||||
|
@ -80,9 +80,26 @@ public final class CapturingAudioSink extends ForwardingAudioSink implements Dum
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("ReferenceEquality")
|
|
||||||
public boolean handleBuffer(ByteBuffer buffer, long presentationTimeUs)
|
public boolean handleBuffer(ByteBuffer buffer, long presentationTimeUs)
|
||||||
throws InitializationException, WriteException {
|
throws InitializationException, WriteException {
|
||||||
|
interceptBuffer(buffer, presentationTimeUs);
|
||||||
|
boolean fullyConsumed = super.handleBuffer(buffer, presentationTimeUs);
|
||||||
|
updateCurrentBuffer(fullyConsumed);
|
||||||
|
return fullyConsumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean handleEncodedBuffer(
|
||||||
|
ByteBuffer buffer, long presentationTimeUs, int accessUnitCount)
|
||||||
|
throws InitializationException, WriteException {
|
||||||
|
interceptBuffer(buffer, presentationTimeUs);
|
||||||
|
boolean fullyConsumed = super.handleEncodedBuffer(buffer, presentationTimeUs, accessUnitCount);
|
||||||
|
updateCurrentBuffer(fullyConsumed);
|
||||||
|
return fullyConsumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("ReferenceEquality")
|
||||||
|
private void interceptBuffer(ByteBuffer buffer, long presentationTimeUs) {
|
||||||
// handleBuffer is called repeatedly with the same buffer until it's been fully consumed by the
|
// handleBuffer is called repeatedly with the same buffer until it's been fully consumed by the
|
||||||
// sink. We only want to dump each buffer once, and we need to do so before the sink being
|
// sink. We only want to dump each buffer once, and we need to do so before the sink being
|
||||||
// forwarded to has a chance to modify its position.
|
// forwarded to has a chance to modify its position.
|
||||||
@ -90,13 +107,13 @@ public final class CapturingAudioSink extends ForwardingAudioSink implements Dum
|
|||||||
interceptedData.add(new DumpableBuffer(buffer, presentationTimeUs));
|
interceptedData.add(new DumpableBuffer(buffer, presentationTimeUs));
|
||||||
currentBuffer = buffer;
|
currentBuffer = buffer;
|
||||||
}
|
}
|
||||||
boolean fullyConsumed = super.handleBuffer(buffer, presentationTimeUs);
|
}
|
||||||
|
|
||||||
|
private void updateCurrentBuffer(boolean fullyConsumed) {
|
||||||
if (fullyConsumed) {
|
if (fullyConsumed) {
|
||||||
currentBuffer = null;
|
currentBuffer = null;
|
||||||
}
|
}
|
||||||
return fullyConsumed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void flush() {
|
public void flush() {
|
||||||
currentBuffer = null;
|
currentBuffer = null;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user