Merge handleBuffer and handleEncodedBuffer

This was unnecessary complexity to avoid very few users
to be broken.

PiperOrigin-RevId: 295603082
This commit is contained in:
krocard 2020-02-17 18:06:36 +00:00 committed by Oliver Woodman
parent 1dbf2997c4
commit f0c0f6eb8f
8 changed files with 56 additions and 105 deletions

View File

@ -29,9 +29,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 * Add a sample count parameter to `MediaCodecRenderer.processOutputBuffer`
`AudioSink.handleBuffer` to `AudioSink.handleEncodedBuffer` to allow and `AudioSink.handleBuffer` to allow batching multiple encoded frames
passing multiple encoded frames in one buffer. 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).

View File

@ -16,7 +16,6 @@
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;
@ -29,18 +28,19 @@ import java.nio.ByteBuffer;
* <p>Before starting playback, specify the input audio format by calling {@link #configure(int, * <p>Before starting playback, specify the input audio format by calling {@link #configure(int,
* int, int, int, int[], int, int)}. * int, int, int, int[], int, int)}.
* *
* <p>Call {@link #handleBuffer(ByteBuffer, long)} to write data, and {@link #handleDiscontinuity()} * <p>Call {@link #handleBuffer(ByteBuffer, long, int)} to write data, and {@link
* when the data being fed is discontinuous. Call {@link #play()} to start playing the written data. * #handleDiscontinuity()} when the data being fed is discontinuous. Call {@link #play()} to start
* playing the written data.
* *
* <p>Call {@link #configure(int, int, int, int, int[], int, int)} whenever the input format * <p>Call {@link #configure(int, int, int, int, int[], int, int)} whenever the input format
* changes. The sink will be reinitialized on the next call to {@link #handleBuffer(ByteBuffer, * changes. The sink will be reinitialized on the next call to {@link #handleBuffer(ByteBuffer,
* long)}. * long, int)}.
* *
* <p>Call {@link #flush()} to prepare the sink to receive audio data from a new playback position. * <p>Call {@link #flush()} to prepare the sink to receive audio data from a new playback position.
* *
* <p>Call {@link #playToEndOfStream()} repeatedly to play out all data when no more input buffers * <p>Call {@link #playToEndOfStream()} repeatedly to play out all data when no more input buffers
* will be provided via {@link #handleBuffer(ByteBuffer, long)} until the next {@link #flush()}. * will be provided via {@link #handleBuffer(ByteBuffer, long, int)} until the next {@link
* Call {@link #reset()} when the instance is no longer required. * #flush()}. Call {@link #reset()} when the instance is no longer required.
* *
* <p>The implementation may be backed by a platform {@link AudioTrack}. In this case, {@link * <p>The implementation may be backed by a platform {@link AudioTrack}. In this case, {@link
* #setAudioSessionId(int)}, {@link #setAudioAttributes(AudioAttributes)}, {@link * #setAudioSessionId(int)}, {@link #setAudioAttributes(AudioAttributes)}, {@link
@ -223,7 +223,7 @@ public interface AudioSink {
void handleDiscontinuity(); void handleDiscontinuity();
/** /**
* Attempts to process PCM data from a {@link ByteBuffer}, starting from its current position and * Attempts to process 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.
@ -233,39 +233,18 @@ 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.
* @param encodedAccessUnitCount The number of encoded access units in the buffer, or 1 if the
* buffer contains PCM audio. This allows batching multiple encoded access units in one
* buffer.
* @return Whether the buffer was handled fully. * @return Whether the buffer was handled fully.
* @throws InitializationException If an error occurs initializing the sink. * @throws InitializationException If an error occurs initializing the sink.
* @throws WriteException If an error occurs writing the audio data. * @throws WriteException If an error occurs writing the audio data.
*/ */
boolean handleBuffer(ByteBuffer buffer, long presentationTimeUs) boolean handleBuffer(ByteBuffer buffer, long presentationTimeUs, int encodedAccessUnitCount)
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.
* *

View File

@ -559,22 +559,8 @@ public final class DefaultAudioSink implements AudioSink {
} }
@Override @Override
public boolean handleBuffer(ByteBuffer buffer, long presentationTimeUs)
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") @SuppressWarnings("ReferenceEquality")
private boolean handleBufferInternal( public boolean handleBuffer(
ByteBuffer buffer, long presentationTimeUs, int encodedAccessUnitCount) ByteBuffer buffer, long presentationTimeUs, int encodedAccessUnitCount)
throws InitializationException, WriteException { throws InitializationException, WriteException {
Assertions.checkArgument(inputBuffer == null || buffer == inputBuffer); Assertions.checkArgument(inputBuffer == null || buffer == inputBuffer);

View File

@ -74,16 +74,10 @@ public class ForwardingAudioSink implements AudioSink {
} }
@Override @Override
public boolean handleBuffer(ByteBuffer buffer, long presentationTimeUs) public boolean handleBuffer(
ByteBuffer buffer, long presentationTimeUs, int encodedAccessUnitCount)
throws InitializationException, WriteException { throws InitializationException, WriteException {
return sink.handleBuffer(buffer, presentationTimeUs); return sink.handleBuffer(buffer, presentationTimeUs, encodedAccessUnitCount);
}
@Override
public boolean handleEncodedBuffer(
ByteBuffer buffer, long presentationTimeUs, int accessUnitCount)
throws InitializationException, WriteException {
return sink.handleEncodedBuffer(buffer, presentationTimeUs, accessUnitCount);
} }
@Override @Override

View File

@ -88,8 +88,6 @@ 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.
@ -436,7 +434,6 @@ 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;
} }
/** /**
@ -632,12 +629,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
boolean fullyConsumed; boolean fullyConsumed;
try { try {
if (Util.isEncodingLinearPcm(audioSinkEncoding)) { fullyConsumed = audioSink.handleBuffer(buffer, bufferPresentationTimeUs, sampleCount);
fullyConsumed = audioSink.handleBuffer(buffer, bufferPresentationTimeUs);
} else {
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);

View File

@ -358,7 +358,8 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements
audioTrackNeedsConfigure = false; audioTrackNeedsConfigure = false;
} }
if (audioSink.handleBuffer(outputBuffer.data, outputBuffer.timeUs)) { if (audioSink.handleBuffer(
outputBuffer.data, outputBuffer.timeUs, /* encodedAccessUnitCount= */ 1)) {
decoderCounters.renderedOutputBufferCount++; decoderCounters.renderedOutputBufferCount++;
outputBuffer.release(); outputBuffer.release();
outputBuffer = null; outputBuffer = null;

View File

@ -38,7 +38,7 @@ import org.robolectric.annotation.Config;
* data (i.e., the {@link android.media.AudioTrack#write} methods just return 0), so these tests are * data (i.e., the {@link android.media.AudioTrack#write} methods just return 0), so these tests are
* currently limited to verifying behavior that doesn't rely on consuming data, and the position * currently limited to verifying behavior that doesn't rely on consuming data, and the position
* will stay at its initial value. For example, we can't verify {@link * will stay at its initial value. For example, we can't verify {@link
* AudioSink#handleBuffer(ByteBuffer, long)} handling a complete buffer, or queueing audio then * AudioSink#handleBuffer(ByteBuffer, long, int)} handling a complete buffer, or queueing audio then
* draining to the end of the stream. This could be worked around by having a test-only mode where * draining to the end of the stream. This could be worked around by having a test-only mode where
* {@link DefaultAudioSink} automatically treats audio as consumed. * {@link DefaultAudioSink} automatically treats audio as consumed.
*/ */
@ -77,12 +77,14 @@ public final class DefaultAudioSinkTest {
@Test @Test
public void handlesBufferAfterReset() throws Exception { public void handlesBufferAfterReset() throws Exception {
configureDefaultAudioSink(CHANNEL_COUNT_STEREO); configureDefaultAudioSink(CHANNEL_COUNT_STEREO);
defaultAudioSink.handleBuffer(createDefaultSilenceBuffer(), /* presentationTimeUs= */ 0); defaultAudioSink.handleBuffer(
createDefaultSilenceBuffer(), /* presentationTimeUs= */ 0, /* encodedAccessUnitCount= */ 1);
// After reset and re-configure we can successfully queue more input. // After reset and re-configure we can successfully queue more input.
defaultAudioSink.reset(); defaultAudioSink.reset();
configureDefaultAudioSink(CHANNEL_COUNT_STEREO); configureDefaultAudioSink(CHANNEL_COUNT_STEREO);
defaultAudioSink.handleBuffer(createDefaultSilenceBuffer(), /* presentationTimeUs= */ 0); defaultAudioSink.handleBuffer(
createDefaultSilenceBuffer(), /* presentationTimeUs= */ 0, /* encodedAccessUnitCount= */ 1);
} }
@Test @Test
@ -90,24 +92,28 @@ public final class DefaultAudioSinkTest {
PlaybackParameters playbackParameters = new PlaybackParameters(1.5f); PlaybackParameters playbackParameters = new PlaybackParameters(1.5f);
defaultAudioSink.setPlaybackParameters(playbackParameters); defaultAudioSink.setPlaybackParameters(playbackParameters);
configureDefaultAudioSink(CHANNEL_COUNT_STEREO); configureDefaultAudioSink(CHANNEL_COUNT_STEREO);
defaultAudioSink.handleBuffer(createDefaultSilenceBuffer(), /* presentationTimeUs= */ 0); defaultAudioSink.handleBuffer(
createDefaultSilenceBuffer(), /* presentationTimeUs= */ 0, /* encodedAccessUnitCount= */ 1);
// After reset and re-configure we can successfully queue more input. // After reset and re-configure we can successfully queue more input.
defaultAudioSink.reset(); defaultAudioSink.reset();
configureDefaultAudioSink(CHANNEL_COUNT_STEREO); configureDefaultAudioSink(CHANNEL_COUNT_STEREO);
defaultAudioSink.handleBuffer(createDefaultSilenceBuffer(), /* presentationTimeUs= */ 0); defaultAudioSink.handleBuffer(
createDefaultSilenceBuffer(), /* presentationTimeUs= */ 0, /* encodedAccessUnitCount= */ 1);
assertThat(defaultAudioSink.getPlaybackParameters()).isEqualTo(playbackParameters); assertThat(defaultAudioSink.getPlaybackParameters()).isEqualTo(playbackParameters);
} }
@Test @Test
public void handlesBufferAfterReset_withFormatChange() throws Exception { public void handlesBufferAfterReset_withFormatChange() throws Exception {
configureDefaultAudioSink(CHANNEL_COUNT_STEREO); configureDefaultAudioSink(CHANNEL_COUNT_STEREO);
defaultAudioSink.handleBuffer(createDefaultSilenceBuffer(), /* presentationTimeUs= */ 0); defaultAudioSink.handleBuffer(
createDefaultSilenceBuffer(), /* presentationTimeUs= */ 0, /* encodedAccessUnitCount= */ 1);
// After reset and re-configure we can successfully queue more input. // After reset and re-configure we can successfully queue more input.
defaultAudioSink.reset(); defaultAudioSink.reset();
configureDefaultAudioSink(CHANNEL_COUNT_MONO); configureDefaultAudioSink(CHANNEL_COUNT_MONO);
defaultAudioSink.handleBuffer(createDefaultSilenceBuffer(), /* presentationTimeUs= */ 0); defaultAudioSink.handleBuffer(
createDefaultSilenceBuffer(), /* presentationTimeUs= */ 0, /* encodedAccessUnitCount= */ 1);
} }
@Test @Test
@ -115,12 +121,14 @@ public final class DefaultAudioSinkTest {
PlaybackParameters playbackParameters = new PlaybackParameters(1.5f); PlaybackParameters playbackParameters = new PlaybackParameters(1.5f);
defaultAudioSink.setPlaybackParameters(playbackParameters); defaultAudioSink.setPlaybackParameters(playbackParameters);
configureDefaultAudioSink(CHANNEL_COUNT_STEREO); configureDefaultAudioSink(CHANNEL_COUNT_STEREO);
defaultAudioSink.handleBuffer(createDefaultSilenceBuffer(), /* presentationTimeUs= */ 0); defaultAudioSink.handleBuffer(
createDefaultSilenceBuffer(), /* presentationTimeUs= */ 0, /* encodedAccessUnitCount= */ 1);
// After reset and re-configure we can successfully queue more input. // After reset and re-configure we can successfully queue more input.
defaultAudioSink.reset(); defaultAudioSink.reset();
configureDefaultAudioSink(CHANNEL_COUNT_MONO); configureDefaultAudioSink(CHANNEL_COUNT_MONO);
defaultAudioSink.handleBuffer(createDefaultSilenceBuffer(), /* presentationTimeUs= */ 0); defaultAudioSink.handleBuffer(
createDefaultSilenceBuffer(), /* presentationTimeUs= */ 0, /* encodedAccessUnitCount= */ 1);
assertThat(defaultAudioSink.getPlaybackParameters()).isEqualTo(playbackParameters); assertThat(defaultAudioSink.getPlaybackParameters()).isEqualTo(playbackParameters);
} }
@ -130,7 +138,8 @@ public final class DefaultAudioSinkTest {
CHANNEL_COUNT_STEREO, CHANNEL_COUNT_STEREO,
/* trimStartFrames= */ TRIM_100_MS_FRAME_COUNT, /* trimStartFrames= */ TRIM_100_MS_FRAME_COUNT,
/* trimEndFrames= */ 0); /* trimEndFrames= */ 0);
defaultAudioSink.handleBuffer(createDefaultSilenceBuffer(), /* presentationTimeUs= */ 0); defaultAudioSink.handleBuffer(
createDefaultSilenceBuffer(), /* presentationTimeUs= */ 0, /* encodedAccessUnitCount= */ 1);
assertThat(arrayAudioBufferSink.output) assertThat(arrayAudioBufferSink.output)
.hasLength( .hasLength(
@ -145,7 +154,8 @@ public final class DefaultAudioSinkTest {
CHANNEL_COUNT_STEREO, CHANNEL_COUNT_STEREO,
/* trimStartFrames= */ 0, /* trimStartFrames= */ 0,
/* trimEndFrames= */ TRIM_10_MS_FRAME_COUNT); /* trimEndFrames= */ TRIM_10_MS_FRAME_COUNT);
defaultAudioSink.handleBuffer(createDefaultSilenceBuffer(), /* presentationTimeUs= */ 0); defaultAudioSink.handleBuffer(
createDefaultSilenceBuffer(), /* presentationTimeUs= */ 0, /* encodedAccessUnitCount= */ 1);
assertThat(arrayAudioBufferSink.output) assertThat(arrayAudioBufferSink.output)
.hasLength( .hasLength(
@ -160,7 +170,8 @@ public final class DefaultAudioSinkTest {
CHANNEL_COUNT_STEREO, CHANNEL_COUNT_STEREO,
/* trimStartFrames= */ TRIM_100_MS_FRAME_COUNT, /* trimStartFrames= */ TRIM_100_MS_FRAME_COUNT,
/* trimEndFrames= */ TRIM_10_MS_FRAME_COUNT); /* trimEndFrames= */ TRIM_10_MS_FRAME_COUNT);
defaultAudioSink.handleBuffer(createDefaultSilenceBuffer(), /* presentationTimeUs= */ 0); defaultAudioSink.handleBuffer(
createDefaultSilenceBuffer(), /* presentationTimeUs= */ 0, /* encodedAccessUnitCount= */ 1);
assertThat(arrayAudioBufferSink.output) assertThat(arrayAudioBufferSink.output)
.hasLength( .hasLength(
@ -173,14 +184,18 @@ public final class DefaultAudioSinkTest {
public void getCurrentPosition_returnsPositionFromFirstBuffer() throws Exception { public void getCurrentPosition_returnsPositionFromFirstBuffer() throws Exception {
configureDefaultAudioSink(CHANNEL_COUNT_STEREO); configureDefaultAudioSink(CHANNEL_COUNT_STEREO);
defaultAudioSink.handleBuffer( defaultAudioSink.handleBuffer(
createDefaultSilenceBuffer(), /* presentationTimeUs= */ 5 * C.MICROS_PER_SECOND); createDefaultSilenceBuffer(),
/* presentationTimeUs= */ 5 * C.MICROS_PER_SECOND,
/* encodedAccessUnitCount= */ 1);
assertThat(defaultAudioSink.getCurrentPositionUs(/* sourceEnded= */ false)) assertThat(defaultAudioSink.getCurrentPositionUs(/* sourceEnded= */ false))
.isEqualTo(5 * C.MICROS_PER_SECOND); .isEqualTo(5 * C.MICROS_PER_SECOND);
defaultAudioSink.reset(); defaultAudioSink.reset();
configureDefaultAudioSink(CHANNEL_COUNT_STEREO); configureDefaultAudioSink(CHANNEL_COUNT_STEREO);
defaultAudioSink.handleBuffer( defaultAudioSink.handleBuffer(
createDefaultSilenceBuffer(), /* presentationTimeUs= */ 8 * C.MICROS_PER_SECOND); createDefaultSilenceBuffer(),
/* presentationTimeUs= */ 8 * C.MICROS_PER_SECOND,
/* encodedAccessUnitCount= */ 1);
assertThat(defaultAudioSink.getCurrentPositionUs(/* sourceEnded= */ false)) assertThat(defaultAudioSink.getCurrentPositionUs(/* sourceEnded= */ false))
.isEqualTo(8 * C.MICROS_PER_SECOND); .isEqualTo(8 * C.MICROS_PER_SECOND);
} }

View File

@ -80,26 +80,10 @@ public final class CapturingAudioSink extends ForwardingAudioSink implements Dum
} }
@Override @Override
public boolean handleBuffer(ByteBuffer buffer, long presentationTimeUs)
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") @SuppressWarnings("ReferenceEquality")
private void interceptBuffer(ByteBuffer buffer, long presentationTimeUs) { public boolean handleBuffer(
ByteBuffer buffer, long presentationTimeUs, int encodedAccessUnitCount)
throws InitializationException, WriteException {
// 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.
@ -107,13 +91,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, encodedAccessUnitCount);
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;