summaryrefslogtreecommitdiffstats
path: root/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/audio/AudioSink.java
blob: db87e28e7f3f9522bf45dfe0df61915d59f8a68e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
/*
 * 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 org.mozilla.thirdparty.com.google.android.exoplayer2.audio;

import android.media.AudioTrack;
import androidx.annotation.Nullable;
import org.mozilla.thirdparty.com.google.android.exoplayer2.C;
import org.mozilla.thirdparty.com.google.android.exoplayer2.Format;
import org.mozilla.thirdparty.com.google.android.exoplayer2.PlaybackParameters;
import java.nio.ByteBuffer;

/**
 * A sink that consumes audio data.
 *
 * <p>Before starting playback, specify the input audio format by calling {@link #configure(int,
 * int, int, int, int[], int, int)}.
 *
 * <p>Call {@link #handleBuffer(ByteBuffer, long)} 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(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,
 * long)}.
 *
 * <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)} 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(int)} and/or {@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.
 */
public interface AudioSink {

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

    /**
     * Called if the audio sink has started rendering audio to a new platform audio session.
     *
     * @param audioSessionId The newly generated audio session's identifier.
     */
    void onAudioSessionId(int audioSessionId);

    /**
     * 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 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);

  }

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

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

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

  }

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

    /**
     * The underlying {@link AudioTrack}'s state, if applicable.
     */
    public final int audioTrackState;

    /**
     * @param audioTrackState The underlying {@link AudioTrack}'s state, if applicable.
     * @param sampleRate The requested sample rate in Hz.
     * @param channelConfig The requested channel configuration.
     * @param bufferSize The requested buffer size in bytes.
     */
    public InitializationException(int audioTrackState, int sampleRate, int channelConfig,
        int bufferSize) {
      super("AudioTrack init failed: " + audioTrackState + ", Config(" + sampleRate + ", "
          + channelConfig + ", " + bufferSize + ")");
      this.audioTrackState = audioTrackState;
    }

  }

  /**
   * 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;

    /**
     * @param errorCode The error value returned from the sink implementation.
     */
    public WriteException(int errorCode) {
      super("AudioTrack write failed: " + errorCode);
      this.errorCode = errorCode;
    }

  }

  /**
   * 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);

  /**
   * Returns whether the sink supports the audio format.
   *
   * @param channelCount The number of channels, or {@link Format#NO_VALUE} if not known.
   * @param encoding The audio encoding, or {@link Format#NO_VALUE} if not known.
   * @return Whether the sink supports the audio format.
   */
  boolean supportsOutput(int channelCount, @C.Encoding int encoding);

  /**
   * 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 inputEncoding The encoding of audio data provided in the input buffers.
   * @param inputChannelCount The number of channels.
   * @param inputSampleRate The sample rate in Hz.
   * @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.
   * @param trimStartFrames The number of audio frames to trim from the start of data written to the
   *     sink after this call.
   * @param trimEndFrames The number of audio frames to trim from data written to the sink
   *     immediately preceding the next call to {@link #flush()} or this method.
   * @throws ConfigurationException If an error occurs configuring the sink.
   */
  void configure(
      @C.Encoding int inputEncoding,
      int inputChannelCount,
      int inputSampleRate,
      int specifiedBufferSize,
      @Nullable int[] outputChannels,
      int trimStartFrames,
      int trimEndFrames)
      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(int,
   * int, int, int, int[], 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.
   * @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)
      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);

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

  /**
   * 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);

  /** 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 or if
   * the audio session id has changed. Enabling tunneling is only possible if the sink is based on a
   * platform {@link AudioTrack}, and requires platform API version 21 onwards.
   *
   * @param tunnelingAudioSessionId The audio session id to use.
   * @throws IllegalStateException Thrown if enabling tunneling on platform API version &lt; 21.
   */
  void enableTunnelingV21(int tunnelingAudioSessionId);

  /**
   * 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 A volume 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();

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