0
# Audio Rendering
1
2
Audio rendering in ExoPlayer handles decoding and outputting audio data to the Android audio system. The audio pipeline includes decoders, processors, and sinks that work together to provide high-quality audio playback with support for various codecs and audio effects.
3
4
## AudioSink Interface
5
6
The AudioSink interface defines how audio data is consumed and output to audio hardware.
7
8
```java { .api }
9
public interface AudioSink {
10
/**
11
* Exception thrown when audio sink configuration fails.
12
*/
13
final class ConfigurationException extends Exception {
14
public final Format format;
15
16
public ConfigurationException(String message, Format format);
17
public ConfigurationException(Throwable cause, Format format);
18
public ConfigurationException(String message, Throwable cause, Format format);
19
}
20
21
/**
22
* Exception thrown when audio sink write operations fail.
23
*/
24
final class WriteException extends Exception {
25
public final int errorCode;
26
public final Format format;
27
28
public WriteException(int errorCode, Format format, Throwable cause);
29
}
30
31
/**
32
* Configures the sink for the specified audio format.
33
*
34
* @param format The audio format
35
* @param specifiedBufferSize The buffer size in bytes, or 0 for default
36
* @param outputChannels The output channel configuration, or null for default
37
* @throws ConfigurationException If configuration fails
38
*/
39
void configure(Format format, int specifiedBufferSize, @Nullable int[] outputChannels) throws ConfigurationException;
40
41
/**
42
* Starts or resumes audio output.
43
*/
44
void play();
45
46
/**
47
* Pauses audio output.
48
*/
49
void pause();
50
51
/**
52
* Plays to the end of the stream.
53
*
54
* @throws WriteException If writing fails
55
*/
56
void playToEndOfStream() throws WriteException;
57
58
/**
59
* Resets the sink.
60
*/
61
void reset();
62
63
/**
64
* Releases the sink.
65
*/
66
void release();
67
68
/**
69
* Sets the audio volume.
70
*
71
* @param volume The volume (0.0 to 1.0)
72
*/
73
void setVolume(float volume);
74
75
/**
76
* Sets the audio attributes.
77
*
78
* @param audioAttributes The audio attributes
79
*/
80
void setAudioAttributes(AudioAttributes audioAttributes);
81
82
/**
83
* Sets auxiliary effect information.
84
*
85
* @param auxEffectInfo The auxiliary effect info
86
*/
87
void setAuxEffectInfo(AuxEffectInfo auxEffectInfo);
88
89
/**
90
* Returns the current playback parameters.
91
*
92
* @return The current playback parameters
93
*/
94
PlaybackParameters getPlaybackParameters();
95
96
/**
97
* Sets playback parameters for speed and pitch adjustment.
98
*
99
* @param playbackParameters The playback parameters
100
*/
101
void setPlaybackParameters(PlaybackParameters playbackParameters);
102
103
/**
104
* Attempts to write audio data to the sink.
105
*
106
* @param buffer The audio data buffer
107
* @param presentationTimeUs The presentation time in microseconds
108
* @param encodedAccessUnitCount The number of encoded access units
109
* @return Whether the buffer was consumed
110
* @throws WriteException If writing fails
111
*/
112
boolean handleBuffer(ByteBuffer buffer, long presentationTimeUs, int encodedAccessUnitCount) throws WriteException;
113
114
/**
115
* Returns the buffer size for the AudioTrack in microseconds.
116
*
117
* @return The buffer size in microseconds
118
*/
119
long getAudioTrackBufferSizeUs();
120
}
121
```
122
123
## DefaultAudioSink
124
125
The default implementation of AudioSink using Android's AudioTrack.
126
127
```java { .api }
128
public final class DefaultAudioSink implements AudioSink {
129
/**
130
* Builder for DefaultAudioSink.
131
*/
132
public static final class Builder {
133
/**
134
* Creates a builder.
135
*/
136
public Builder();
137
138
/**
139
* Sets the audio capabilities.
140
*
141
* @param audioCapabilities The audio capabilities
142
* @return This builder
143
*/
144
public Builder setAudioCapabilities(AudioCapabilities audioCapabilities);
145
146
/**
147
* Sets the audio processors.
148
*
149
* @param audioProcessors The audio processors
150
* @return This builder
151
*/
152
public Builder setAudioProcessors(AudioProcessor[] audioProcessors);
153
154
/**
155
* Sets whether to enable float output.
156
*
157
* @param enableFloatOutput Whether to enable float output
158
* @return This builder
159
*/
160
public Builder setEnableFloatOutput(boolean enableFloatOutput);
161
162
/**
163
* Sets whether to enable AudioTrack playback parameters.
164
*
165
* @param enableAudioTrackPlaybackParams Whether to enable AudioTrack playback parameters
166
* @return This builder
167
*/
168
public Builder setEnableAudioTrackPlaybackParams(boolean enableAudioTrackPlaybackParams);
169
170
/**
171
* Sets the offload mode.
172
*
173
* @param offloadMode The offload mode
174
* @return This builder
175
*/
176
public Builder setOffloadMode(@OffloadMode int offloadMode);
177
178
/**
179
* Builds the DefaultAudioSink.
180
*
181
* @return The built DefaultAudioSink
182
*/
183
public DefaultAudioSink build();
184
}
185
186
// Offload modes
187
public static final int OFFLOAD_MODE_DISABLED = 0;
188
public static final int OFFLOAD_MODE_ENABLED_GAPLESS_REQUIRED = 1;
189
public static final int OFFLOAD_MODE_ENABLED_GAPLESS_NOT_REQUIRED = 2;
190
public static final int OFFLOAD_MODE_ENABLED_GAPLESS_DISABLED = 3;
191
}
192
```
193
194
## MediaCodecAudioRenderer
195
196
Audio renderer that uses MediaCodec for decoding.
197
198
```java { .api }
199
public class MediaCodecAudioRenderer extends MediaCodecRenderer implements MediaClock {
200
/**
201
* Creates a MediaCodecAudioRenderer.
202
*
203
* @param context The context
204
* @param mediaCodecSelector The MediaCodec selector
205
* @param enableDecoderFallback Whether to enable decoder fallback
206
* @param eventHandler The event handler
207
* @param eventListener The event listener
208
* @param audioSink The audio sink
209
*/
210
public MediaCodecAudioRenderer(Context context, MediaCodecSelector mediaCodecSelector,
211
boolean enableDecoderFallback, @Nullable Handler eventHandler,
212
@Nullable AudioRendererEventListener eventListener, AudioSink audioSink);
213
214
/**
215
* Sets the volume.
216
*
217
* @param volume The volume (0.0 to 1.0)
218
*/
219
public final void setVolume(float volume);
220
221
/**
222
* Returns the current playback parameters.
223
*
224
* @return The current playback parameters
225
*/
226
public PlaybackParameters getPlaybackParameters();
227
228
/**
229
* Sets playback parameters.
230
*
231
* @param playbackParameters The playback parameters
232
*/
233
public void setPlaybackParameters(PlaybackParameters playbackParameters);
234
235
/**
236
* Returns the position according to the media clock.
237
*
238
* @return The position in microseconds
239
*/
240
@Override
241
public long getPositionUs();
242
243
/**
244
* Sets the audio session ID.
245
*
246
* @param audioSessionId The audio session ID
247
*/
248
public void setAudioSessionId(int audioSessionId);
249
250
/**
251
* Sets auxiliary effect information.
252
*
253
* @param auxEffectInfo The auxiliary effect info
254
*/
255
public void setAuxEffectInfo(AuxEffectInfo auxEffectInfo);
256
}
257
```
258
259
## AudioRendererEventListener
260
261
Listener for audio renderer events.
262
263
```java { .api }
264
public interface AudioRendererEventListener {
265
/**
266
* Called when the audio renderer is enabled.
267
*
268
* @param counters The decoder counters
269
*/
270
default void onAudioEnabled(DecoderCounters counters) {}
271
272
/**
273
* Called when the audio session ID changes.
274
*
275
* @param audioSessionId The new audio session ID
276
*/
277
default void onAudioSessionIdChanged(int audioSessionId) {}
278
279
/**
280
* Called when the audio decoder is initialized.
281
*
282
* @param decoderName The decoder name
283
* @param initializedTimestampMs The initialization timestamp
284
* @param initializationDurationMs The initialization duration
285
*/
286
default void onAudioDecoderInitialized(String decoderName, long initializedTimestampMs, long initializationDurationMs) {}
287
288
/**
289
* Called when the audio input format changes.
290
*
291
* @param format The new format
292
* @param decoderReuseEvaluation The decoder reuse evaluation
293
*/
294
default void onAudioInputFormatChanged(Format format, @Nullable DecoderReuseEvaluation decoderReuseEvaluation) {}
295
296
/**
297
* Called when the audio position is advancing.
298
*
299
* @param playoutStartSystemTimeMs The playout start system time
300
*/
301
default void onAudioPositionAdvancing(long playoutStartSystemTimeMs) {}
302
303
/**
304
* Called when audio underrun occurs.
305
*
306
* @param bufferSize The buffer size
307
* @param bufferSizeMs The buffer size in milliseconds
308
* @param elapsedSinceLastFeedMs Time elapsed since last feed
309
*/
310
default void onAudioUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs) {}
311
312
/**
313
* Called when the audio decoder is released.
314
*
315
* @param decoderName The decoder name
316
*/
317
default void onAudioDecoderReleased(String decoderName) {}
318
319
/**
320
* Called when the audio renderer is disabled.
321
*
322
* @param counters The decoder counters
323
*/
324
default void onAudioDisabled(DecoderCounters counters) {}
325
326
/**
327
* Called when the audio sink underruns.
328
*
329
* @param bufferSize The buffer size
330
* @param bufferSizeMs The buffer size in milliseconds
331
* @param elapsedSinceLastFeedMs Time elapsed since last feed
332
*/
333
default void onAudioSinkUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs) {}
334
335
/**
336
* Called when audio codec error occurs.
337
*
338
* @param audioCodecError The audio codec error
339
*/
340
default void onAudioCodecError(Exception audioCodecError) {}
341
}
342
```
343
344
## AudioProcessor Interface
345
346
Interface for audio processing components that can modify audio data.
347
348
```java { .api }
349
public interface AudioProcessor {
350
/**
351
* Data structure for audio format.
352
*/
353
final class AudioFormat {
354
public static final AudioFormat NOT_SET = new AudioFormat(Format.NO_VALUE, Format.NO_VALUE, Format.NO_VALUE);
355
356
public final int sampleRate;
357
public final int channelCount;
358
@C.PcmEncoding public final int encoding;
359
360
public AudioFormat(int sampleRate, int channelCount, @C.PcmEncoding int encoding);
361
}
362
363
/**
364
* Exception thrown when audio processing fails.
365
*/
366
final class UnhandledAudioFormatException extends Exception {
367
public final AudioFormat inputAudioFormat;
368
public final AudioFormat outputAudioFormat;
369
370
public UnhandledAudioFormatException(AudioFormat inputAudioFormat, AudioFormat outputAudioFormat);
371
}
372
373
/**
374
* Configures the processor for the specified input format.
375
*
376
* @param inputAudioFormat The input audio format
377
* @return The output audio format
378
* @throws UnhandledAudioFormatException If the format is not supported
379
*/
380
AudioFormat configure(AudioFormat inputAudioFormat) throws UnhandledAudioFormatException;
381
382
/**
383
* Returns whether the processor is active.
384
*
385
* @return Whether the processor is active
386
*/
387
boolean isActive();
388
389
/**
390
* Queues input data for processing.
391
*
392
* @param inputBuffer The input buffer
393
*/
394
void queueInput(ByteBuffer inputBuffer);
395
396
/**
397
* Queues end of stream.
398
*/
399
void queueEndOfStream();
400
401
/**
402
* Returns a buffer containing processed output data.
403
*
404
* @return The output buffer, or EMPTY_BUFFER if no output is available
405
*/
406
ByteBuffer getOutput();
407
408
/**
409
* Returns whether the processor has ended.
410
*
411
* @return Whether the processor has ended
412
*/
413
boolean isEnded();
414
415
/**
416
* Flushes the processor.
417
*/
418
void flush();
419
420
/**
421
* Resets the processor.
422
*/
423
void reset();
424
}
425
```
426
427
## Usage Examples
428
429
### Basic Audio Renderer Setup
430
431
```java
432
// Create default audio sink
433
DefaultAudioSink audioSink = new DefaultAudioSink.Builder().build();
434
435
// Create audio renderer
436
MediaCodecAudioRenderer audioRenderer = new MediaCodecAudioRenderer(
437
context,
438
MediaCodecSelector.DEFAULT,
439
/* enableDecoderFallback= */ false,
440
/* eventHandler= */ null,
441
/* eventListener= */ null,
442
audioSink
443
);
444
445
// Use with custom renderers factory
446
RenderersFactory renderersFactory = new DefaultRenderersFactory(context) {
447
@Override
448
protected void buildAudioRenderers(Context context, int extensionRendererMode,
449
MediaCodecSelector mediaCodecSelector, boolean enableDecoderFallback,
450
AudioSink audioSink, Handler eventHandler,
451
AudioRendererEventListener eventListener,
452
ArrayList<Renderer> out) {
453
out.add(new MediaCodecAudioRenderer(context, mediaCodecSelector, enableDecoderFallback,
454
eventHandler, eventListener, audioSink));
455
}
456
};
457
```
458
459
### Audio Sink Configuration
460
461
```java
462
// Configure audio sink with custom settings
463
DefaultAudioSink audioSink = new DefaultAudioSink.Builder()
464
.setEnableFloatOutput(true) // Enable high-resolution audio
465
.setEnableAudioTrackPlaybackParams(true) // Enable speed/pitch adjustment
466
.setOffloadMode(DefaultAudioSink.OFFLOAD_MODE_ENABLED_GAPLESS_REQUIRED) // Enable offload
467
.build();
468
```
469
470
### Audio Event Handling
471
472
```java
473
AudioRendererEventListener audioListener = new AudioRendererEventListener() {
474
@Override
475
public void onAudioEnabled(DecoderCounters counters) {
476
Log.d(TAG, "Audio enabled");
477
}
478
479
@Override
480
public void onAudioSessionIdChanged(int audioSessionId) {
481
Log.d(TAG, "Audio session ID changed: " + audioSessionId);
482
// Apply audio effects to this session
483
applyAudioEffects(audioSessionId);
484
}
485
486
@Override
487
public void onAudioDecoderInitialized(String decoderName, long initializedTimestampMs, long initializationDurationMs) {
488
Log.d(TAG, "Audio decoder initialized: " + decoderName);
489
}
490
491
@Override
492
public void onAudioUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs) {
493
Log.w(TAG, "Audio underrun detected");
494
}
495
496
@Override
497
public void onAudioCodecError(Exception audioCodecError) {
498
Log.e(TAG, "Audio codec error", audioCodecError);
499
}
500
};
501
502
// Use with renderer
503
MediaCodecAudioRenderer audioRenderer = new MediaCodecAudioRenderer(
504
context, MediaCodecSelector.DEFAULT, false,
505
handler, audioListener, audioSink
506
);
507
```
508
509
### Volume and Playback Control
510
511
```java
512
// Set volume on audio renderer
513
audioRenderer.setVolume(0.5f); // 50% volume
514
515
// Set playback parameters for speed/pitch adjustment
516
PlaybackParameters params = new PlaybackParameters(1.5f, 1.0f); // 1.5x speed, normal pitch
517
audioRenderer.setPlaybackParameters(params);
518
519
// Get current playback parameters
520
PlaybackParameters currentParams = audioRenderer.getPlaybackParameters();
521
```
522
523
### Audio Effects Integration
524
525
```java
526
// Apply audio effects using session ID
527
private void applyAudioEffects(int audioSessionId) {
528
// Create equalizer
529
Equalizer equalizer = new Equalizer(0, audioSessionId);
530
equalizer.setEnabled(true);
531
532
// Create bass boost
533
BassBoost bassBoost = new BassBoost(0, audioSessionId);
534
bassBoost.setStrength((short) 1000); // Maximum bass boost
535
bassBoost.setEnabled(true);
536
537
// Create virtualizer
538
Virtualizer virtualizer = new Virtualizer(0, audioSessionId);
539
virtualizer.setStrength((short) 1000); // Maximum virtualization
540
virtualizer.setEnabled(true);
541
}
542
```
543
544
### Custom Audio Processor
545
546
```java
547
public class VolumeAudioProcessor implements AudioProcessor {
548
private float volume = 1.0f;
549
private AudioFormat outputFormat;
550
private ByteBuffer buffer = EMPTY_BUFFER;
551
private boolean ended;
552
553
public void setVolume(float volume) {
554
this.volume = volume;
555
}
556
557
@Override
558
public AudioFormat configure(AudioFormat inputAudioFormat) throws UnhandledAudioFormatException {
559
if (inputAudioFormat.encoding != C.ENCODING_PCM_16BIT) {
560
throw new UnhandledAudioFormatException(inputAudioFormat, AudioFormat.NOT_SET);
561
}
562
this.outputFormat = inputAudioFormat;
563
return outputFormat;
564
}
565
566
@Override
567
public boolean isActive() {
568
return volume != 1.0f;
569
}
570
571
@Override
572
public void queueInput(ByteBuffer inputBuffer) {
573
if (!isActive()) {
574
buffer = inputBuffer;
575
return;
576
}
577
578
// Apply volume adjustment
579
ByteBuffer processedBuffer = ByteBuffer.allocate(inputBuffer.remaining());
580
while (inputBuffer.hasRemaining()) {
581
short sample = inputBuffer.getShort();
582
short adjustedSample = (short) (sample * volume);
583
processedBuffer.putShort(adjustedSample);
584
}
585
processedBuffer.flip();
586
buffer = processedBuffer;
587
}
588
589
@Override
590
public void queueEndOfStream() {
591
ended = true;
592
}
593
594
@Override
595
public ByteBuffer getOutput() {
596
ByteBuffer outputBuffer = buffer;
597
buffer = EMPTY_BUFFER;
598
return outputBuffer;
599
}
600
601
@Override
602
public boolean isEnded() {
603
return ended && buffer == EMPTY_BUFFER;
604
}
605
606
@Override
607
public void flush() {
608
buffer = EMPTY_BUFFER;
609
ended = false;
610
}
611
612
@Override
613
public void reset() {
614
flush();
615
outputFormat = null;
616
}
617
}
618
```