public interface

AudioSink

 androidx.media3.exoplayer.audio.AudioSink

Subclasses:

ForwardingAudioSink, DefaultAudioSink, CapturingAudioSink

Gradle dependencies

compile group: 'androidx.media3', name: 'media3-exoplayer', version: '1.0.0-alpha03'

  • groupId: androidx.media3
  • artifactId: media3-exoplayer
  • version: 1.0.0-alpha03

Artifact androidx.media3:media3-exoplayer:1.0.0-alpha03 it located at Google repository (https://maven.google.com/)

Overview

A sink that consumes audio data.

Before starting playback, specify the input audio format by calling AudioSink.configure(Format, int, int[]).

Call AudioSink.handleBuffer(ByteBuffer, long, int) to write data, and AudioSink.handleDiscontinuity() when the data being fed is discontinuous. Call AudioSink.play() to start playing the written data.

Call AudioSink.configure(Format, int, int[]) whenever the input format changes. The sink will be reinitialized on the next call to AudioSink.handleBuffer(ByteBuffer, long, int).

Call AudioSink.flush() to prepare the sink to receive audio data from a new playback position.

Call AudioSink.playToEndOfStream() repeatedly to play out all data when no more input buffers will be provided via AudioSink.handleBuffer(ByteBuffer, long, int) until the next AudioSink.flush(). Call AudioSink.reset() when the instance is no longer required.

The implementation may be backed by a platform AudioTrack. In this case, AudioSink.setAudioSessionId(int), AudioSink.setAudioAttributes(AudioAttributes), AudioSink.enableTunnelingV21() and AudioSink.disableTunneling() may be called before writing data to the sink. These methods may also be called after writing data to the sink, in which case it will be reinitialized as required. For implementations that are not based on platform AudioTracks, calling methods relating to audio sessions, audio attributes, and tunneling may have no effect.

Summary

Fields
public static final longCURRENT_POSITION_NOT_SET

Returned by AudioSink.getCurrentPositionUs(boolean) when the position is not set.

public static final intSINK_FORMAT_SUPPORTED_DIRECTLY

The sink supports the format directly, without the need for internal transcoding.

public static final intSINK_FORMAT_SUPPORTED_WITH_TRANSCODING

The sink supports the format, but needs to transcode it internally to do so.

public static final intSINK_FORMAT_UNSUPPORTED

The sink does not support the format.

Methods
public voidconfigure(Format inputFormat, int specifiedBufferSize, int[] outputChannels[])

Configures (or reconfigures) the sink.

public voiddisableTunneling()

Disables tunneling.

public voidenableTunnelingV21()

Enables tunneling, if possible.

public voidexperimentalFlushWithoutAudioTrackRelease()

Flushes the sink, after which it is ready to receive buffers from a new playback position.

public voidflush()

Flushes the sink, after which it is ready to receive buffers from a new playback position.

public AudioAttributesgetAudioAttributes()

Returns the audio attributes used for audio playback, or null if the sink does not use audio attributes.

public longgetCurrentPositionUs(boolean sourceEnded)

Returns the playback position in the stream starting at zero, in microseconds, or AudioSink.CURRENT_POSITION_NOT_SET if it is not yet available.

public intgetFormatSupport(Format format)

Returns the level of support that the sink provides for a given Format.

public PlaybackParametersgetPlaybackParameters()

Returns the active PlaybackParameters.

public booleangetSkipSilenceEnabled()

Returns whether silences are skipped in the audio stream.

public booleanhandleBuffer(java.nio.ByteBuffer buffer, long presentationTimeUs, int encodedAccessUnitCount)

Attempts to process data from a java.nio.ByteBuffer, starting from its current position and ending at its limit (exclusive).

public voidhandleDiscontinuity()

Signals to the sink that the next buffer may be discontinuous with the previous buffer.

public booleanhasPendingData()

Returns whether the sink has data pending that has not been consumed yet.

public booleanisEnded()

Returns whether AudioSink.playToEndOfStream() has been called and all buffers have been processed.

public voidpause()

Pauses playback.

public voidplay()

Starts or resumes consuming audio if initialized.

public voidplayToEndOfStream()

Processes any remaining data.

public voidreset()

Resets the sink, releasing any resources that it currently holds.

public voidsetAudioAttributes(AudioAttributes audioAttributes)

Sets attributes for audio playback.

public voidsetAudioSessionId(int audioSessionId)

Sets the audio session id.

public voidsetAuxEffectInfo(AuxEffectInfo auxEffectInfo)

Sets the auxiliary effect.

public voidsetListener(AudioSink.Listener listener)

Sets the listener for sink events, which should be the audio renderer.

public voidsetPlaybackParameters(PlaybackParameters playbackParameters)

Attempts to set the playback parameters.

public voidsetPlayerId(PlayerId playerId)

Sets the PlayerId of the player using this audio sink.

public voidsetSkipSilenceEnabled(boolean skipSilenceEnabled)

Sets whether silences should be skipped in the audio stream.

public voidsetVolume(float volume)

Sets the playback volume.

public booleansupportsFormat(Format format)

Returns whether the sink supports a given Format.

Fields

public static final int SINK_FORMAT_SUPPORTED_DIRECTLY

The sink supports the format directly, without the need for internal transcoding.

public static final int SINK_FORMAT_SUPPORTED_WITH_TRANSCODING

The sink supports the format, but needs to transcode it internally to do so. Internal transcoding may result in lower quality and higher CPU load in some cases.

public static final int SINK_FORMAT_UNSUPPORTED

The sink does not support the format.

public static final long CURRENT_POSITION_NOT_SET

Returned by AudioSink.getCurrentPositionUs(boolean) when the position is not set.

Methods

public void setListener(AudioSink.Listener listener)

Sets the listener for sink events, which should be the audio renderer.

Parameters:

listener: The listener for sink events, which should be the audio renderer.

public void setPlayerId(PlayerId playerId)

Sets the PlayerId of the player using this audio sink.

Parameters:

playerId: The PlayerId, or null to clear a previously set id.

public boolean supportsFormat(Format format)

Returns whether the sink supports a given Format.

Parameters:

format: The format.

Returns:

Whether the sink supports the format.

public int getFormatSupport(Format format)

Returns the level of support that the sink provides for a given Format.

Parameters:

format: The format.

Returns:

The level of support provided.

public long getCurrentPositionUs(boolean sourceEnded)

Returns the playback position in the stream starting at zero, in microseconds, or AudioSink.CURRENT_POSITION_NOT_SET if it is not yet available.

Parameters:

sourceEnded: Specify true if no more input buffers will be provided.

Returns:

The playback position relative to the start of playback, in microseconds.

public void configure(Format inputFormat, int specifiedBufferSize, int[] outputChannels[])

Configures (or reconfigures) the sink.

Parameters:

inputFormat: The format of audio data provided in the input buffers.
specifiedBufferSize: A specific size for the playback buffer in bytes, or 0 to infer a suitable buffer size.
outputChannels: A mapping from input to output channels that is applied to this sink's input as a preprocessing step, if handling PCM input. Specify null to leave the input unchanged. Otherwise, the element at index i specifies index of the input channel to map to output channel i when preprocessing input buffers. After the map is applied the audio data will have outputChannels.length channels.

public void play()

Starts or resumes consuming audio if initialized.

public void handleDiscontinuity()

Signals to the sink that the next buffer may be discontinuous with the previous buffer.

public boolean handleBuffer(java.nio.ByteBuffer buffer, long presentationTimeUs, int encodedAccessUnitCount)

Attempts to process data from a java.nio.ByteBuffer, starting from its current position and ending at its limit (exclusive). The position of the java.nio.ByteBuffer is advanced by the number of bytes that were handled. AudioSink.Listener.onPositionDiscontinuity() will be called if presentationTimeUs is discontinuous with the last buffer handled since the last reset.

Returns whether the data was handled in full. If the data was not handled in full then the same java.nio.ByteBuffer must be provided to subsequent calls until it has been fully consumed, except in the case of an intervening call to AudioSink.flush() (or to AudioSink.configure(Format, int, int[]) that causes the sink to be flushed).

Parameters:

buffer: The buffer containing audio data.
presentationTimeUs: The presentation timestamp of the buffer in microseconds.
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.

Returns:

Whether the buffer was handled fully.

public void playToEndOfStream()

Processes any remaining data. AudioSink.isEnded() will return true when no data remains.

public boolean isEnded()

Returns whether AudioSink.playToEndOfStream() has been called and all buffers have been processed.

public boolean hasPendingData()

Returns whether the sink has data pending that has not been consumed yet.

public void setPlaybackParameters(PlaybackParameters playbackParameters)

Attempts to set the playback parameters. The audio sink may override these parameters if they are not supported.

Parameters:

playbackParameters: The new playback parameters to attempt to set.

public PlaybackParameters getPlaybackParameters()

Returns the active PlaybackParameters.

public void setSkipSilenceEnabled(boolean skipSilenceEnabled)

Sets whether silences should be skipped in the audio stream.

public boolean getSkipSilenceEnabled()

Returns whether silences are skipped in the audio stream.

public void setAudioAttributes(AudioAttributes audioAttributes)

Sets attributes for audio playback. If the attributes have changed and if the sink is not configured for use with tunneling, then it is reset and the audio session id is cleared.

If the sink is configured for use with tunneling then the audio attributes are ignored. The sink is not reset and the audio session id is not cleared. The passed attributes will be used if the sink is later re-configured into non-tunneled mode.

Parameters:

audioAttributes: The attributes for audio playback.

public AudioAttributes getAudioAttributes()

Returns the audio attributes used for audio playback, or null if the sink does not use audio attributes.

public void setAudioSessionId(int audioSessionId)

Sets the audio session id.

public void setAuxEffectInfo(AuxEffectInfo auxEffectInfo)

Sets the auxiliary effect.

public void enableTunnelingV21()

Enables tunneling, if possible. The sink is reset if tunneling was previously disabled. Enabling tunneling is only possible if the sink is based on a platform AudioTrack, and requires platform API version 21 onwards.

public void disableTunneling()

Disables tunneling. If tunneling was previously enabled then the sink is reset and any audio session id is cleared.

public void setVolume(float volume)

Sets the playback volume.

Parameters:

volume: Linear output gain to apply to all channels. Should be in the range [0.0, 1.0].

public void pause()

Pauses playback.

public void flush()

Flushes the sink, after which it is ready to receive buffers from a new playback position.

The audio session may remain active until AudioSink.reset() is called.

public void experimentalFlushWithoutAudioTrackRelease()

Flushes the sink, after which it is ready to receive buffers from a new playback position.

Does not release the AudioTrack held by the sink.

This method is experimental, and will be renamed or removed in a future release.

Only for experimental use as part of MediaCodecAudioRenderer.experimentalSetEnableKeepAudioTrackOnSeek(boolean).

public void reset()

Resets the sink, releasing any resources that it currently holds.

Source

/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package androidx.media3.exoplayer.audio;

import static java.lang.annotation.ElementType.TYPE_USE;

import android.media.AudioTrack;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import androidx.media3.common.AudioAttributes;
import androidx.media3.common.AuxEffectInfo;
import androidx.media3.common.C;
import androidx.media3.common.Format;
import androidx.media3.common.PlaybackException;
import androidx.media3.common.PlaybackParameters;
import androidx.media3.common.Player;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.exoplayer.ExoPlaybackException;
import androidx.media3.exoplayer.analytics.PlayerId;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.nio.ByteBuffer;

/**
 * A sink that consumes audio data.
 *
 * <p>Before starting playback, specify the input audio format by calling {@link #configure(Format,
 * int, int[])}.
 *
 * <p>Call {@link #handleBuffer(ByteBuffer, long, int)} to write data, and {@link
 * #handleDiscontinuity()} when the data being fed is discontinuous. Call {@link #play()} to start
 * playing the written data.
 *
 * <p>Call {@link #configure(Format, int, int[])} whenever the input format changes. The sink will
 * be reinitialized on the next call to {@link #handleBuffer(ByteBuffer, long, int)}.
 *
 * <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
 * will be provided via {@link #handleBuffer(ByteBuffer, long, int)} until the next {@link
 * #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
 * #setAudioSessionId(int)}, {@link #setAudioAttributes(AudioAttributes)}, {@link
 * #enableTunnelingV21()} and {@link #disableTunneling()} may be called before writing data to the
 * sink. These methods may also be called after writing data to the sink, in which case it will be
 * reinitialized as required. For implementations that are not based on platform {@link
 * AudioTrack}s, calling methods relating to audio sessions, audio attributes, and tunneling may
 * have no effect.
 */
@UnstableApi
public interface AudioSink {

  /** Listener for audio sink events. */
  interface Listener {

    /**
     * Called when the audio sink handles a buffer whose timestamp is discontinuous with the last
     * buffer handled since it was reset.
     */
    void onPositionDiscontinuity();

    /**
     * Called when the audio sink's position has increased for the first time since it was last
     * paused or flushed.
     *
     * @param playoutStartSystemTimeMs The approximate derived {@link System#currentTimeMillis()} at
     *     which playout started. Only valid if the audio track has not underrun.
     */
    default void onPositionAdvancing(long playoutStartSystemTimeMs) {}

    /**
     * Called when the audio sink runs out of data.
     *
     * <p>An audio sink implementation may never call this method (for example, if audio data is
     * consumed in batches rather than based on the sink's own clock).
     *
     * @param bufferSize The size of the sink's buffer, in bytes.
     * @param bufferSizeMs The size of the sink's buffer, in milliseconds, if it is configured for
     *     PCM output. {@link C#TIME_UNSET} if it is configured for encoded audio output, as the
     *     buffered media can have a variable bitrate so the duration may be unknown.
     * @param elapsedSinceLastFeedMs The time since the sink was last fed data, in milliseconds.
     */
    void onUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs);

    /**
     * Called when skipping silences is enabled or disabled.
     *
     * @param skipSilenceEnabled Whether skipping silences is enabled.
     */
    void onSkipSilenceEnabledChanged(boolean skipSilenceEnabled);

    /** Called when the offload buffer has been partially emptied. */
    default void onOffloadBufferEmptying() {}

    /**
     * Called when the offload buffer has been filled completely.
     *
     * @param bufferEmptyingDeadlineMs Maximum time in milliseconds until {@link
     *     #onOffloadBufferEmptying()} will be called.
     */
    default void onOffloadBufferFull(long bufferEmptyingDeadlineMs) {}

    /**
     * Called when {@link AudioSink} has encountered an error.
     *
     * <p>If the sink writes to a platform {@link AudioTrack}, this will called for all {@link
     * AudioTrack} errors.
     *
     * <p>This method being called does not indicate that playback has failed, or that it will fail.
     * The player may be able to recover from the error (for example by recreating the AudioTrack,
     * possibly with different settings) and continue. Hence applications should <em>not</em>
     * implement this method to display a user visible error or initiate an application level retry
     * ({@link Player.Listener#onPlayerError} is the appropriate place to implement such behavior).
     * This method is called to provide the application with an opportunity to log the error if it
     * wishes to do so.
     *
     * <p>Fatal errors that cannot be recovered will be reported wrapped in a {@link
     * ExoPlaybackException} by {@link Player.Listener#onPlayerError(PlaybackException)}.
     *
     * @param audioSinkError The error that occurred. Typically an {@link InitializationException},
     *     a {@link WriteException}, or an {@link UnexpectedDiscontinuityException}.
     */
    default void onAudioSinkError(Exception audioSinkError) {}
  }

  /** Thrown when a failure occurs configuring the sink. */
  final class ConfigurationException extends Exception {

    /** Input {@link Format} of the sink when the configuration failure occurs. */
    public final Format format;

    /** Creates a new configuration exception with the specified {@code cause} and no message. */
    public ConfigurationException(Throwable cause, Format format) {
      super(cause);
      this.format = format;
    }

    /** Creates a new configuration exception with the specified {@code message} and no cause. */
    public ConfigurationException(String message, Format format) {
      super(message);
      this.format = format;
    }
  }

  /** Thrown when a failure occurs initializing the sink. */
  final class InitializationException extends Exception {

    /** The underlying {@link AudioTrack}'s state. */
    public final int audioTrackState;
    /** If the exception can be recovered by recreating the sink. */
    public final boolean isRecoverable;
    /** The input {@link Format} of the sink when the error occurs. */
    public final Format format;

    /**
     * Creates a new instance.
     *
     * @param audioTrackState The underlying {@link AudioTrack}'s state.
     * @param sampleRate The requested sample rate in Hz.
     * @param channelConfig The requested channel configuration.
     * @param bufferSize The requested buffer size in bytes.
     * @param format The input format of the sink when the error occurs.
     * @param isRecoverable Whether the exception can be recovered by recreating the sink.
     * @param audioTrackException Exception thrown during the creation of the {@link AudioTrack}.
     */
    public InitializationException(
        int audioTrackState,
        int sampleRate,
        int channelConfig,
        int bufferSize,
        Format format,
        boolean isRecoverable,
        @Nullable Exception audioTrackException) {
      super(
          "AudioTrack init failed "
              + audioTrackState
              + " "
              + ("Config(" + sampleRate + ", " + channelConfig + ", " + bufferSize + ")")
              + (isRecoverable ? " (recoverable)" : ""),
          audioTrackException);
      this.audioTrackState = audioTrackState;
      this.isRecoverable = isRecoverable;
      this.format = format;
    }
  }

  /** Thrown when a failure occurs writing to the sink. */
  final class WriteException extends Exception {

    /**
     * The error value returned from the sink implementation. If the sink writes to a platform
     * {@link AudioTrack}, this will be the error value returned from {@link
     * AudioTrack#write(byte[], int, int)} or {@link AudioTrack#write(ByteBuffer, int, int)}.
     * Otherwise, the meaning of the error code depends on the sink implementation.
     */
    public final int errorCode;
    /** If the exception can be recovered by recreating the sink. */
    public final boolean isRecoverable;
    /** The input {@link Format} of the sink when the error occurs. */
    public final Format format;

    /**
     * Creates an instance.
     *
     * @param errorCode The error value returned from the sink implementation.
     * @param format The input format of the sink when the error occurs.
     * @param isRecoverable Whether the exception can be recovered by recreating the sink.
     */
    public WriteException(int errorCode, Format format, boolean isRecoverable) {
      super("AudioTrack write failed: " + errorCode);
      this.isRecoverable = isRecoverable;
      this.errorCode = errorCode;
      this.format = format;
    }
  }

  /** Thrown when the sink encounters an unexpected timestamp discontinuity. */
  final class UnexpectedDiscontinuityException extends Exception {
    /** The actual presentation time of a sample, in microseconds. */
    public final long actualPresentationTimeUs;
    /** The expected presentation time of a sample, in microseconds. */
    public final long expectedPresentationTimeUs;

    /**
     * Creates an instance.
     *
     * @param actualPresentationTimeUs The actual presentation time of a sample, in microseconds.
     * @param expectedPresentationTimeUs The expected presentation time of a sample, in
     *     microseconds.
     */
    public UnexpectedDiscontinuityException(
        long actualPresentationTimeUs, long expectedPresentationTimeUs) {
      super(
          "Unexpected audio track timestamp discontinuity: expected "
              + expectedPresentationTimeUs
              + ", got "
              + actualPresentationTimeUs);
      this.actualPresentationTimeUs = actualPresentationTimeUs;
      this.expectedPresentationTimeUs = expectedPresentationTimeUs;
    }
  }

  /**
   * The level of support the sink provides for a format. One of {@link
   * #SINK_FORMAT_SUPPORTED_DIRECTLY}, {@link #SINK_FORMAT_SUPPORTED_WITH_TRANSCODING} or {@link
   * #SINK_FORMAT_UNSUPPORTED}.
   */
  @Documented
  @Retention(RetentionPolicy.SOURCE)
  @Target(TYPE_USE)
  @IntDef({
    SINK_FORMAT_SUPPORTED_DIRECTLY,
    SINK_FORMAT_SUPPORTED_WITH_TRANSCODING,
    SINK_FORMAT_UNSUPPORTED
  })
  @interface SinkFormatSupport {}
  /** The sink supports the format directly, without the need for internal transcoding. */
  int SINK_FORMAT_SUPPORTED_DIRECTLY = 2;
  /**
   * The sink supports the format, but needs to transcode it internally to do so. Internal
   * transcoding may result in lower quality and higher CPU load in some cases.
   */
  int SINK_FORMAT_SUPPORTED_WITH_TRANSCODING = 1;
  /** The sink does not support the format. */
  int SINK_FORMAT_UNSUPPORTED = 0;

  /** Returned by {@link #getCurrentPositionUs(boolean)} when the position is not set. */
  long CURRENT_POSITION_NOT_SET = Long.MIN_VALUE;

  /**
   * Sets the listener for sink events, which should be the audio renderer.
   *
   * @param listener The listener for sink events, which should be the audio renderer.
   */
  void setListener(Listener listener);

  /**
   * Sets the {@link PlayerId} of the player using this audio sink.
   *
   * @param playerId The {@link PlayerId}, or null to clear a previously set id.
   */
  default void setPlayerId(@Nullable PlayerId playerId) {}

  /**
   * Returns whether the sink supports a given {@link Format}.
   *
   * @param format The format.
   * @return Whether the sink supports the format.
   */
  boolean supportsFormat(Format format);

  /**
   * Returns the level of support that the sink provides for a given {@link Format}.
   *
   * @param format The format.
   * @return The level of support provided.
   */
  @SinkFormatSupport
  int getFormatSupport(Format format);

  /**
   * Returns the playback position in the stream starting at zero, in microseconds, or {@link
   * #CURRENT_POSITION_NOT_SET} if it is not yet available.
   *
   * @param sourceEnded Specify {@code true} if no more input buffers will be provided.
   * @return The playback position relative to the start of playback, in microseconds.
   */
  long getCurrentPositionUs(boolean sourceEnded);

  /**
   * Configures (or reconfigures) the sink.
   *
   * @param inputFormat The format of audio data provided in the input buffers.
   * @param specifiedBufferSize A specific size for the playback buffer in bytes, or 0 to infer a
   *     suitable buffer size.
   * @param outputChannels A mapping from input to output channels that is applied to this sink's
   *     input as a preprocessing step, if handling PCM input. Specify {@code null} to leave the
   *     input unchanged. Otherwise, the element at index {@code i} specifies index of the input
   *     channel to map to output channel {@code i} when preprocessing input buffers. After the map
   *     is applied the audio data will have {@code outputChannels.length} channels.
   * @throws ConfigurationException If an error occurs configuring the sink.
   */
  void configure(Format inputFormat, int specifiedBufferSize, @Nullable int[] outputChannels)
      throws ConfigurationException;

  /** Starts or resumes consuming audio if initialized. */
  void play();

  /** Signals to the sink that the next buffer may be discontinuous with the previous buffer. */
  void handleDiscontinuity();

  /**
   * 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
   * 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.
   *
   * <p>Returns whether the data was handled in full. If the data was not handled in full then the
   * same {@link ByteBuffer} must be provided to subsequent calls until it has been fully consumed,
   * except in the case of an intervening call to {@link #flush()} (or to {@link #configure(Format,
   * int, int[])} that causes the sink to be flushed).
   *
   * @param buffer The buffer containing audio data.
   * @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.
   * @throws InitializationException If an error occurs initializing the sink.
   * @throws WriteException If an error occurs writing the audio data.
   */
  boolean handleBuffer(ByteBuffer buffer, long presentationTimeUs, int encodedAccessUnitCount)
      throws InitializationException, WriteException;

  /**
   * Processes any remaining data. {@link #isEnded()} will return {@code true} when no data remains.
   *
   * @throws WriteException If an error occurs draining data to the sink.
   */
  void playToEndOfStream() throws WriteException;

  /**
   * Returns whether {@link #playToEndOfStream} has been called and all buffers have been processed.
   */
  boolean isEnded();

  /** Returns whether the sink has data pending that has not been consumed yet. */
  boolean hasPendingData();

  /**
   * Attempts to set the playback parameters. The audio sink may override these parameters if they
   * are not supported.
   *
   * @param playbackParameters The new playback parameters to attempt to set.
   */
  void setPlaybackParameters(PlaybackParameters playbackParameters);

  /** Returns the active {@link PlaybackParameters}. */
  PlaybackParameters getPlaybackParameters();

  /** Sets whether silences should be skipped in the audio stream. */
  void setSkipSilenceEnabled(boolean skipSilenceEnabled);

  /** Returns whether silences are skipped in the audio stream. */
  boolean getSkipSilenceEnabled();

  /**
   * Sets attributes for audio playback. If the attributes have changed and if the sink is not
   * configured for use with tunneling, then it is reset and the audio session id is cleared.
   *
   * <p>If the sink is configured for use with tunneling then the audio attributes are ignored. The
   * sink is not reset and the audio session id is not cleared. The passed attributes will be used
   * if the sink is later re-configured into non-tunneled mode.
   *
   * @param audioAttributes The attributes for audio playback.
   */
  void setAudioAttributes(AudioAttributes audioAttributes);

  /**
   * Returns the audio attributes used for audio playback, or {@code null} if the sink does not use
   * audio attributes.
   */
  @Nullable
  AudioAttributes getAudioAttributes();

  /** Sets the audio session id. */
  void setAudioSessionId(int audioSessionId);

  /** Sets the auxiliary effect. */
  void setAuxEffectInfo(AuxEffectInfo auxEffectInfo);

  /**
   * Enables tunneling, if possible. The sink is reset if tunneling was previously disabled.
   * Enabling tunneling is only possible if the sink is based on a platform {@link AudioTrack}, and
   * requires platform API version 21 onwards.
   *
   * @throws IllegalStateException Thrown if enabling tunneling on platform API version &lt; 21.
   */
  void enableTunnelingV21();

  /**
   * Disables tunneling. If tunneling was previously enabled then the sink is reset and any audio
   * session id is cleared.
   */
  void disableTunneling();

  /**
   * Sets the playback volume.
   *
   * @param volume Linear output gain to apply to all channels. Should be in the range [0.0, 1.0].
   */
  void setVolume(float volume);

  /** Pauses playback. */
  void pause();

  /**
   * Flushes the sink, after which it is ready to receive buffers from a new playback position.
   *
   * <p>The audio session may remain active until {@link #reset()} is called.
   */
  void flush();

  /**
   * Flushes the sink, after which it is ready to receive buffers from a new playback position.
   *
   * <p>Does not release the {@link AudioTrack} held by the sink.
   *
   * <p>This method is experimental, and will be renamed or removed in a future release.
   *
   * <p>Only for experimental use as part of {@link
   * MediaCodecAudioRenderer#experimentalSetEnableKeepAudioTrackOnSeek(boolean)}.
   */
  void experimentalFlushWithoutAudioTrackRelease();

  /** Resets the sink, releasing any resources that it currently holds. */
  void reset();
}