1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.google.android.exoplayer2.audio; 17 18 import android.media.AudioTrack; 19 import androidx.annotation.Nullable; 20 import com.google.android.exoplayer2.C; 21 import com.google.android.exoplayer2.Format; 22 import com.google.android.exoplayer2.PlaybackParameters; 23 import java.nio.ByteBuffer; 24 25 /** 26 * A sink that consumes audio data. 27 * 28 * <p>Before starting playback, specify the input audio format by calling {@link #configure(int, 29 * int, int, int, int[], int, int)}. 30 * 31 * <p>Call {@link #handleBuffer(ByteBuffer, long, int)} to write data, and {@link 32 * #handleDiscontinuity()} when the data being fed is discontinuous. Call {@link #play()} to start 33 * playing the written data. 34 * 35 * <p>Call {@link #configure(int, int, int, int, int[], int, int)} whenever the input format 36 * changes. The sink will be reinitialized on the next call to {@link #handleBuffer(ByteBuffer, 37 * long, int)}. 38 * 39 * <p>Call {@link #flush()} to prepare the sink to receive audio data from a new playback position. 40 * 41 * <p>Call {@link #playToEndOfStream()} repeatedly to play out all data when no more input buffers 42 * will be provided via {@link #handleBuffer(ByteBuffer, long, int)} until the next {@link 43 * #flush()}. Call {@link #reset()} when the instance is no longer required. 44 * 45 * <p>The implementation may be backed by a platform {@link AudioTrack}. In this case, {@link 46 * #setAudioSessionId(int)}, {@link #setAudioAttributes(AudioAttributes)}, {@link 47 * #enableTunnelingV21(int)} and/or {@link #disableTunneling()} may be called before writing data to 48 * the sink. These methods may also be called after writing data to the sink, in which case it will 49 * be reinitialized as required. For implementations that are not based on platform {@link 50 * AudioTrack}s, calling methods relating to audio sessions, audio attributes, and tunneling may 51 * have no effect. 52 */ 53 public interface AudioSink { 54 55 /** 56 * Listener for audio sink events. 57 */ 58 interface Listener { 59 60 /** 61 * Called if the audio sink has started rendering audio to a new platform audio session. 62 * 63 * @param audioSessionId The newly generated audio session's identifier. 64 */ onAudioSessionId(int audioSessionId)65 void onAudioSessionId(int audioSessionId); 66 67 /** 68 * Called when the audio sink handles a buffer whose timestamp is discontinuous with the last 69 * buffer handled since it was reset. 70 */ onPositionDiscontinuity()71 void onPositionDiscontinuity(); 72 73 /** 74 * Called when the audio sink runs out of data. 75 * <p> 76 * An audio sink implementation may never call this method (for example, if audio data is 77 * consumed in batches rather than based on the sink's own clock). 78 * 79 * @param bufferSize The size of the sink's buffer, in bytes. 80 * @param bufferSizeMs The size of the sink's buffer, in milliseconds, if it is configured for 81 * PCM output. {@link C#TIME_UNSET} if it is configured for encoded audio output, as the 82 * buffered media can have a variable bitrate so the duration may be unknown. 83 * @param elapsedSinceLastFeedMs The time since the sink was last fed data, in milliseconds. 84 */ onUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs)85 void onUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs); 86 87 /** 88 * Called when skipping silences is enabled or disabled. 89 * 90 * @param skipSilenceEnabled Whether skipping silences is enabled. 91 */ onSkipSilenceEnabledChanged(boolean skipSilenceEnabled)92 void onSkipSilenceEnabledChanged(boolean skipSilenceEnabled); 93 } 94 95 /** 96 * Thrown when a failure occurs configuring the sink. 97 */ 98 final class ConfigurationException extends Exception { 99 100 /** 101 * Creates a new configuration exception with the specified {@code cause} and no message. 102 */ ConfigurationException(Throwable cause)103 public ConfigurationException(Throwable cause) { 104 super(cause); 105 } 106 107 /** 108 * Creates a new configuration exception with the specified {@code message} and no cause. 109 */ ConfigurationException(String message)110 public ConfigurationException(String message) { 111 super(message); 112 } 113 114 } 115 116 /** 117 * Thrown when a failure occurs initializing the sink. 118 */ 119 final class InitializationException extends Exception { 120 121 /** 122 * The underlying {@link AudioTrack}'s state, if applicable. 123 */ 124 public final int audioTrackState; 125 126 /** 127 * @param audioTrackState The underlying {@link AudioTrack}'s state, if applicable. 128 * @param sampleRate The requested sample rate in Hz. 129 * @param channelConfig The requested channel configuration. 130 * @param bufferSize The requested buffer size in bytes. 131 */ InitializationException(int audioTrackState, int sampleRate, int channelConfig, int bufferSize)132 public InitializationException(int audioTrackState, int sampleRate, int channelConfig, 133 int bufferSize) { 134 super("AudioTrack init failed: " + audioTrackState + ", Config(" + sampleRate + ", " 135 + channelConfig + ", " + bufferSize + ")"); 136 this.audioTrackState = audioTrackState; 137 } 138 139 } 140 141 /** 142 * Thrown when a failure occurs writing to the sink. 143 */ 144 final class WriteException extends Exception { 145 146 /** 147 * The error value returned from the sink implementation. If the sink writes to a platform 148 * {@link AudioTrack}, this will be the error value returned from 149 * {@link AudioTrack#write(byte[], int, int)} or {@link AudioTrack#write(ByteBuffer, int, int)}. 150 * Otherwise, the meaning of the error code depends on the sink implementation. 151 */ 152 public final int errorCode; 153 154 /** 155 * @param errorCode The error value returned from the sink implementation. 156 */ WriteException(int errorCode)157 public WriteException(int errorCode) { 158 super("AudioTrack write failed: " + errorCode); 159 this.errorCode = errorCode; 160 } 161 162 } 163 164 /** 165 * Returned by {@link #getCurrentPositionUs(boolean)} when the position is not set. 166 */ 167 long CURRENT_POSITION_NOT_SET = Long.MIN_VALUE; 168 169 /** 170 * Sets the listener for sink events, which should be the audio renderer. 171 * 172 * @param listener The listener for sink events, which should be the audio renderer. 173 */ setListener(Listener listener)174 void setListener(Listener listener); 175 176 /** 177 * Returns whether the sink supports the audio format. 178 * 179 * @param channelCount The number of channels, or {@link Format#NO_VALUE} if not known. 180 * @param encoding The audio encoding, or {@link Format#NO_VALUE} if not known. 181 * @return Whether the sink supports the audio format. 182 */ supportsOutput(int channelCount, @C.Encoding int encoding)183 boolean supportsOutput(int channelCount, @C.Encoding int encoding); 184 185 /** 186 * Returns the playback position in the stream starting at zero, in microseconds, or 187 * {@link #CURRENT_POSITION_NOT_SET} if it is not yet available. 188 * 189 * @param sourceEnded Specify {@code true} if no more input buffers will be provided. 190 * @return The playback position relative to the start of playback, in microseconds. 191 */ getCurrentPositionUs(boolean sourceEnded)192 long getCurrentPositionUs(boolean sourceEnded); 193 194 /** 195 * Configures (or reconfigures) the sink. 196 * 197 * @param inputEncoding The encoding of audio data provided in the input buffers. 198 * @param inputChannelCount The number of channels. 199 * @param inputSampleRate The sample rate in Hz. 200 * @param specifiedBufferSize A specific size for the playback buffer in bytes, or 0 to infer a 201 * suitable buffer size. 202 * @param outputChannels A mapping from input to output channels that is applied to this sink's 203 * input as a preprocessing step, if handling PCM input. Specify {@code null} to leave the 204 * input unchanged. Otherwise, the element at index {@code i} specifies index of the input 205 * channel to map to output channel {@code i} when preprocessing input buffers. After the map 206 * is applied the audio data will have {@code outputChannels.length} channels. 207 * @param trimStartFrames The number of audio frames to trim from the start of data written to the 208 * sink after this call. 209 * @param trimEndFrames The number of audio frames to trim from data written to the sink 210 * immediately preceding the next call to {@link #flush()} or this method. 211 * @throws ConfigurationException If an error occurs configuring the sink. 212 */ configure( @.Encoding int inputEncoding, int inputChannelCount, int inputSampleRate, int specifiedBufferSize, @Nullable int[] outputChannels, int trimStartFrames, int trimEndFrames)213 void configure( 214 @C.Encoding int inputEncoding, 215 int inputChannelCount, 216 int inputSampleRate, 217 int specifiedBufferSize, 218 @Nullable int[] outputChannels, 219 int trimStartFrames, 220 int trimEndFrames) 221 throws ConfigurationException; 222 223 /** 224 * Starts or resumes consuming audio if initialized. 225 */ play()226 void play(); 227 228 /** Signals to the sink that the next buffer may be discontinuous with the previous buffer. */ handleDiscontinuity()229 void handleDiscontinuity(); 230 231 /** 232 * Attempts to process data from a {@link ByteBuffer}, starting from its current position and 233 * ending at its limit (exclusive). The position of the {@link ByteBuffer} is advanced by the 234 * number of bytes that were handled. {@link Listener#onPositionDiscontinuity()} will be called if 235 * {@code presentationTimeUs} is discontinuous with the last buffer handled since the last reset. 236 * 237 * <p>Returns whether the data was handled in full. If the data was not handled in full then the 238 * same {@link ByteBuffer} must be provided to subsequent calls until it has been fully consumed, 239 * except in the case of an intervening call to {@link #flush()} (or to {@link #configure(int, 240 * int, int, int, int[], int, int)} that causes the sink to be flushed). 241 * 242 * @param buffer The buffer containing audio data. 243 * @param presentationTimeUs The presentation timestamp of the buffer in microseconds. 244 * @param encodedAccessUnitCount The number of encoded access units in the buffer, or 1 if the 245 * buffer contains PCM audio. This allows batching multiple encoded access units in one 246 * buffer. 247 * @return Whether the buffer was handled fully. 248 * @throws InitializationException If an error occurs initializing the sink. 249 * @throws WriteException If an error occurs writing the audio data. 250 */ handleBuffer(ByteBuffer buffer, long presentationTimeUs, int encodedAccessUnitCount)251 boolean handleBuffer(ByteBuffer buffer, long presentationTimeUs, int encodedAccessUnitCount) 252 throws InitializationException, WriteException; 253 254 /** 255 * Processes any remaining data. {@link #isEnded()} will return {@code true} when no data remains. 256 * 257 * @throws WriteException If an error occurs draining data to the sink. 258 */ playToEndOfStream()259 void playToEndOfStream() throws WriteException; 260 261 /** 262 * Returns whether {@link #playToEndOfStream} has been called and all buffers have been processed. 263 */ isEnded()264 boolean isEnded(); 265 266 /** 267 * Returns whether the sink has data pending that has not been consumed yet. 268 */ hasPendingData()269 boolean hasPendingData(); 270 271 /** 272 * @deprecated Use {@link #setPlaybackSpeed(float)} and {@link #setSkipSilenceEnabled(boolean)} 273 * instead. 274 */ 275 @Deprecated setPlaybackParameters(PlaybackParameters playbackParameters)276 void setPlaybackParameters(PlaybackParameters playbackParameters); 277 278 /** @deprecated Use {@link #getPlaybackSpeed()} and {@link #getSkipSilenceEnabled()} instead. */ 279 @SuppressWarnings("deprecation") 280 @Deprecated getPlaybackParameters()281 PlaybackParameters getPlaybackParameters(); 282 283 /** Sets the playback speed. */ setPlaybackSpeed(float playbackSpeed)284 void setPlaybackSpeed(float playbackSpeed); 285 286 /** Gets the playback speed. */ getPlaybackSpeed()287 float getPlaybackSpeed(); 288 289 /** Sets whether silences should be skipped in the audio stream. */ setSkipSilenceEnabled(boolean skipSilenceEnabled)290 void setSkipSilenceEnabled(boolean skipSilenceEnabled); 291 292 /** Gets whether silences are skipped in the audio stream. */ getSkipSilenceEnabled()293 boolean getSkipSilenceEnabled(); 294 295 /** 296 * Sets attributes for audio playback. If the attributes have changed and if the sink is not 297 * configured for use with tunneling, then it is reset and the audio session id is cleared. 298 * <p> 299 * If the sink is configured for use with tunneling then the audio attributes are ignored. The 300 * sink is not reset and the audio session id is not cleared. The passed attributes will be used 301 * if the sink is later re-configured into non-tunneled mode. 302 * 303 * @param audioAttributes The attributes for audio playback. 304 */ setAudioAttributes(AudioAttributes audioAttributes)305 void setAudioAttributes(AudioAttributes audioAttributes); 306 307 /** Sets the audio session id. */ setAudioSessionId(int audioSessionId)308 void setAudioSessionId(int audioSessionId); 309 310 /** Sets the auxiliary effect. */ setAuxEffectInfo(AuxEffectInfo auxEffectInfo)311 void setAuxEffectInfo(AuxEffectInfo auxEffectInfo); 312 313 /** 314 * Enables tunneling, if possible. The sink is reset if tunneling was previously disabled or if 315 * the audio session id has changed. Enabling tunneling is only possible if the sink is based on a 316 * platform {@link AudioTrack}, and requires platform API version 21 onwards. 317 * 318 * @param tunnelingAudioSessionId The audio session id to use. 319 * @throws IllegalStateException Thrown if enabling tunneling on platform API version < 21. 320 */ enableTunnelingV21(int tunnelingAudioSessionId)321 void enableTunnelingV21(int tunnelingAudioSessionId); 322 323 /** 324 * Disables tunneling. If tunneling was previously enabled then the sink is reset and any audio 325 * session id is cleared. 326 */ disableTunneling()327 void disableTunneling(); 328 329 /** 330 * Sets the playback volume. 331 * 332 * @param volume A volume in the range [0.0, 1.0]. 333 */ setVolume(float volume)334 void setVolume(float volume); 335 336 /** 337 * Pauses playback. 338 */ pause()339 void pause(); 340 341 /** 342 * Flushes the sink, after which it is ready to receive buffers from a new playback position. 343 * 344 * <p>The audio session may remain active until {@link #reset()} is called. 345 */ flush()346 void flush(); 347 348 /** Resets the renderer, releasing any resources that it currently holds. */ reset()349 void reset(); 350 } 351