• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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.annotation.SuppressLint;
19 import android.media.AudioFormat;
20 import android.media.AudioManager;
21 import android.media.AudioTrack;
22 import android.os.ConditionVariable;
23 import android.os.SystemClock;
24 import androidx.annotation.Nullable;
25 import androidx.annotation.RequiresApi;
26 import com.google.android.exoplayer2.C;
27 import com.google.android.exoplayer2.Format;
28 import com.google.android.exoplayer2.PlaybackParameters;
29 import com.google.android.exoplayer2.audio.AudioProcessor.UnhandledAudioFormatException;
30 import com.google.android.exoplayer2.util.Assertions;
31 import com.google.android.exoplayer2.util.Log;
32 import com.google.android.exoplayer2.util.Util;
33 import java.nio.ByteBuffer;
34 import java.nio.ByteOrder;
35 import java.util.ArrayDeque;
36 import java.util.ArrayList;
37 import java.util.Collections;
38 
39 /**
40  * Plays audio data. The implementation delegates to an {@link AudioTrack} and handles playback
41  * position smoothing, non-blocking writes and reconfiguration.
42  * <p>
43  * If tunneling mode is enabled, care must be taken that audio processors do not output buffers with
44  * a different duration than their input, and buffer processors must produce output corresponding to
45  * their last input immediately after that input is queued. This means that, for example, speed
46  * adjustment is not possible while using tunneling.
47  */
48 public final class DefaultAudioSink implements AudioSink {
49 
50   /**
51    * Thrown when the audio track has provided a spurious timestamp, if {@link
52    * #failOnSpuriousAudioTimestamp} is set.
53    */
54   public static final class InvalidAudioTrackTimestampException extends RuntimeException {
55 
56     /**
57      * Creates a new invalid timestamp exception with the specified message.
58      *
59      * @param message The detail message for this exception.
60      */
InvalidAudioTrackTimestampException(String message)61     private InvalidAudioTrackTimestampException(String message) {
62       super(message);
63     }
64 
65   }
66 
67   /**
68    * Provides a chain of audio processors, which are used for any user-defined processing and
69    * applying playback parameters (if supported). Because applying playback parameters can skip and
70    * stretch/compress audio, the sink will query the chain for information on how to transform its
71    * output position to map it onto a media position, via {@link #getMediaDuration(long)} and {@link
72    * #getSkippedOutputFrameCount()}.
73    */
74   public interface AudioProcessorChain {
75 
76     /**
77      * Returns the fixed chain of audio processors that will process audio. This method is called
78      * once during initialization, but audio processors may change state to become active/inactive
79      * during playback.
80      */
getAudioProcessors()81     AudioProcessor[] getAudioProcessors();
82 
83     /**
84      * @deprecated Use {@link #applyPlaybackSpeed(float)} and {@link
85      *     #applySkipSilenceEnabled(boolean)} instead.
86      */
87     @Deprecated
applyPlaybackParameters(PlaybackParameters playbackParameters)88     PlaybackParameters applyPlaybackParameters(PlaybackParameters playbackParameters);
89 
90     /**
91      * Configures audio processors to apply the specified playback speed immediately, returning the
92      * new playback speed, which may differ from the speed passed in. Only called when processors
93      * have no input pending.
94      *
95      * @param playbackSpeed The playback speed to try to apply.
96      * @return The playback speed that was actually applied.
97      */
applyPlaybackSpeed(float playbackSpeed)98     float applyPlaybackSpeed(float playbackSpeed);
99 
100     /**
101      * Configures audio processors to apply whether to skip silences immediately, returning the new
102      * value. Only called when processors have no input pending.
103      *
104      * @param skipSilenceEnabled Whether silences should be skipped in the audio stream.
105      * @return The new value.
106      */
applySkipSilenceEnabled(boolean skipSilenceEnabled)107     boolean applySkipSilenceEnabled(boolean skipSilenceEnabled);
108 
109     /**
110      * Scales the specified playout duration to take into account speedup due to audio processing,
111      * returning an input media duration, in arbitrary units.
112      */
getMediaDuration(long playoutDuration)113     long getMediaDuration(long playoutDuration);
114 
115     /**
116      * Returns the number of output audio frames skipped since the audio processors were last
117      * flushed.
118      */
getSkippedOutputFrameCount()119     long getSkippedOutputFrameCount();
120   }
121 
122   /**
123    * The default audio processor chain, which applies a (possibly empty) chain of user-defined audio
124    * processors followed by {@link SilenceSkippingAudioProcessor} and {@link SonicAudioProcessor}.
125    */
126   public static class DefaultAudioProcessorChain implements AudioProcessorChain {
127 
128     private final AudioProcessor[] audioProcessors;
129     private final SilenceSkippingAudioProcessor silenceSkippingAudioProcessor;
130     private final SonicAudioProcessor sonicAudioProcessor;
131 
132     /**
133      * Creates a new default chain of audio processors, with the user-defined {@code
134      * audioProcessors} applied before silence skipping and playback parameters.
135      */
DefaultAudioProcessorChain(AudioProcessor... audioProcessors)136     public DefaultAudioProcessorChain(AudioProcessor... audioProcessors) {
137       // The passed-in type may be more specialized than AudioProcessor[], so allocate a new array
138       // rather than using Arrays.copyOf.
139       this.audioProcessors = new AudioProcessor[audioProcessors.length + 2];
140       System.arraycopy(
141           /* src= */ audioProcessors,
142           /* srcPos= */ 0,
143           /* dest= */ this.audioProcessors,
144           /* destPos= */ 0,
145           /* length= */ audioProcessors.length);
146       silenceSkippingAudioProcessor = new SilenceSkippingAudioProcessor();
147       sonicAudioProcessor = new SonicAudioProcessor();
148       this.audioProcessors[audioProcessors.length] = silenceSkippingAudioProcessor;
149       this.audioProcessors[audioProcessors.length + 1] = sonicAudioProcessor;
150     }
151 
152     @Override
getAudioProcessors()153     public AudioProcessor[] getAudioProcessors() {
154       return audioProcessors;
155     }
156 
157     /**
158      * @deprecated Use {@link #applyPlaybackSpeed(float)} and {@link
159      *     #applySkipSilenceEnabled(boolean)} instead.
160      */
161     @SuppressWarnings("deprecation")
162     @Deprecated
163     @Override
applyPlaybackParameters(PlaybackParameters playbackParameters)164     public PlaybackParameters applyPlaybackParameters(PlaybackParameters playbackParameters) {
165       return new PlaybackParameters(applyPlaybackSpeed(playbackParameters.speed));
166     }
167 
168     @Override
applyPlaybackSpeed(float playbackSpeed)169     public float applyPlaybackSpeed(float playbackSpeed) {
170       return sonicAudioProcessor.setSpeed(playbackSpeed);
171     }
172 
173     @Override
applySkipSilenceEnabled(boolean skipSilenceEnabled)174     public boolean applySkipSilenceEnabled(boolean skipSilenceEnabled) {
175       silenceSkippingAudioProcessor.setEnabled(skipSilenceEnabled);
176       return skipSilenceEnabled;
177     }
178 
179     @Override
getMediaDuration(long playoutDuration)180     public long getMediaDuration(long playoutDuration) {
181       return sonicAudioProcessor.scaleDurationForSpeedup(playoutDuration);
182     }
183 
184     @Override
getSkippedOutputFrameCount()185     public long getSkippedOutputFrameCount() {
186       return silenceSkippingAudioProcessor.getSkippedFrames();
187     }
188   }
189 
190   /**
191    * A minimum length for the {@link AudioTrack} buffer, in microseconds.
192    */
193   private static final long MIN_BUFFER_DURATION_US = 250000;
194   /**
195    * A maximum length for the {@link AudioTrack} buffer, in microseconds.
196    */
197   private static final long MAX_BUFFER_DURATION_US = 750000;
198   /**
199    * The length for passthrough {@link AudioTrack} buffers, in microseconds.
200    */
201   private static final long PASSTHROUGH_BUFFER_DURATION_US = 250000;
202   /**
203    * A multiplication factor to apply to the minimum buffer size requested by the underlying
204    * {@link AudioTrack}.
205    */
206   private static final int BUFFER_MULTIPLICATION_FACTOR = 4;
207 
208   /** To avoid underruns on some devices (e.g., Broadcom 7271), scale up the AC3 buffer duration. */
209   private static final int AC3_BUFFER_MULTIPLICATION_FACTOR = 2;
210 
211   /**
212    * @see AudioTrack#ERROR_BAD_VALUE
213    */
214   private static final int ERROR_BAD_VALUE = AudioTrack.ERROR_BAD_VALUE;
215   /**
216    * @see AudioTrack#MODE_STATIC
217    */
218   private static final int MODE_STATIC = AudioTrack.MODE_STATIC;
219   /**
220    * @see AudioTrack#MODE_STREAM
221    */
222   private static final int MODE_STREAM = AudioTrack.MODE_STREAM;
223   /**
224    * @see AudioTrack#STATE_INITIALIZED
225    */
226   private static final int STATE_INITIALIZED = AudioTrack.STATE_INITIALIZED;
227   /**
228    * @see AudioTrack#WRITE_NON_BLOCKING
229    */
230   @SuppressLint("InlinedApi")
231   private static final int WRITE_NON_BLOCKING = AudioTrack.WRITE_NON_BLOCKING;
232   /** The default playback speed. */
233   private static final float DEFAULT_PLAYBACK_SPEED = 1.0f;
234   /** The default skip silence flag. */
235   private static final boolean DEFAULT_SKIP_SILENCE = false;
236 
237   private static final String TAG = "AudioTrack";
238 
239   /**
240    * Whether to enable a workaround for an issue where an audio effect does not keep its session
241    * active across releasing/initializing a new audio track, on platform builds where
242    * {@link Util#SDK_INT} &lt; 21.
243    * <p>
244    * The flag must be set before creating a player.
245    */
246   public static boolean enablePreV21AudioSessionWorkaround = false;
247 
248   /**
249    * Whether to throw an {@link InvalidAudioTrackTimestampException} when a spurious timestamp is
250    * reported from {@link AudioTrack#getTimestamp}.
251    * <p>
252    * The flag must be set before creating a player. Should be set to {@code true} for testing and
253    * debugging purposes only.
254    */
255   public static boolean failOnSpuriousAudioTimestamp = false;
256 
257   @Nullable private final AudioCapabilities audioCapabilities;
258   private final AudioProcessorChain audioProcessorChain;
259   private final boolean enableFloatOutput;
260   private final ChannelMappingAudioProcessor channelMappingAudioProcessor;
261   private final TrimmingAudioProcessor trimmingAudioProcessor;
262   private final AudioProcessor[] toIntPcmAvailableAudioProcessors;
263   private final AudioProcessor[] toFloatPcmAvailableAudioProcessors;
264   private final ConditionVariable releasingConditionVariable;
265   private final AudioTrackPositionTracker audioTrackPositionTracker;
266   private final ArrayDeque<MediaPositionParameters> mediaPositionParametersCheckpoints;
267 
268   @Nullable private Listener listener;
269   /** Used to keep the audio session active on pre-V21 builds (see {@link #initialize(long)}). */
270   @Nullable private AudioTrack keepSessionIdAudioTrack;
271 
272   @Nullable private Configuration pendingConfiguration;
273   private Configuration configuration;
274   private AudioTrack audioTrack;
275 
276   private AudioAttributes audioAttributes;
277   @Nullable private MediaPositionParameters afterDrainParameters;
278   private MediaPositionParameters mediaPositionParameters;
279 
280   @Nullable private ByteBuffer avSyncHeader;
281   private int bytesUntilNextAvSync;
282 
283   private long submittedPcmBytes;
284   private long submittedEncodedFrames;
285   private long writtenPcmBytes;
286   private long writtenEncodedFrames;
287   private int framesPerEncodedSample;
288   private boolean startMediaTimeUsNeedsSync;
289   private long startMediaTimeUs;
290   private float volume;
291 
292   private AudioProcessor[] activeAudioProcessors;
293   private ByteBuffer[] outputBuffers;
294   @Nullable private ByteBuffer inputBuffer;
295   private int inputBufferAccessUnitCount;
296   @Nullable private ByteBuffer outputBuffer;
297   private byte[] preV21OutputBuffer;
298   private int preV21OutputBufferOffset;
299   private int drainingAudioProcessorIndex;
300   private boolean handledEndOfStream;
301   private boolean stoppedAudioTrack;
302 
303   private boolean playing;
304   private int audioSessionId;
305   private AuxEffectInfo auxEffectInfo;
306   private boolean tunneling;
307   private long lastFeedElapsedRealtimeMs;
308 
309   /**
310    * Creates a new default audio sink.
311    *
312    * @param audioCapabilities The audio capabilities for playback on this device. May be null if the
313    *     default capabilities (no encoded audio passthrough support) should be assumed.
314    * @param audioProcessors An array of {@link AudioProcessor}s that will process PCM audio before
315    *     output. May be empty.
316    */
DefaultAudioSink( @ullable AudioCapabilities audioCapabilities, AudioProcessor[] audioProcessors)317   public DefaultAudioSink(
318       @Nullable AudioCapabilities audioCapabilities, AudioProcessor[] audioProcessors) {
319     this(audioCapabilities, audioProcessors, /* enableFloatOutput= */ false);
320   }
321 
322   /**
323    * Creates a new default audio sink, optionally using float output for high resolution PCM.
324    *
325    * @param audioCapabilities The audio capabilities for playback on this device. May be null if the
326    *     default capabilities (no encoded audio passthrough support) should be assumed.
327    * @param audioProcessors An array of {@link AudioProcessor}s that will process PCM audio before
328    *     output. May be empty.
329    * @param enableFloatOutput Whether to enable 32-bit float output. Where possible, 32-bit float
330    *     output will be used if the input is 32-bit float, and also if the input is high resolution
331    *     (24-bit or 32-bit) integer PCM. Audio processing (for example, speed adjustment) will not
332    *     be available when float output is in use.
333    */
DefaultAudioSink( @ullable AudioCapabilities audioCapabilities, AudioProcessor[] audioProcessors, boolean enableFloatOutput)334   public DefaultAudioSink(
335       @Nullable AudioCapabilities audioCapabilities,
336       AudioProcessor[] audioProcessors,
337       boolean enableFloatOutput) {
338     this(audioCapabilities, new DefaultAudioProcessorChain(audioProcessors), enableFloatOutput);
339   }
340 
341   /**
342    * Creates a new default audio sink, optionally using float output for high resolution PCM and
343    * with the specified {@code audioProcessorChain}.
344    *
345    * @param audioCapabilities The audio capabilities for playback on this device. May be null if the
346    *     default capabilities (no encoded audio passthrough support) should be assumed.
347    * @param audioProcessorChain An {@link AudioProcessorChain} which is used to apply playback
348    *     parameters adjustments. The instance passed in must not be reused in other sinks.
349    * @param enableFloatOutput Whether to enable 32-bit float output. Where possible, 32-bit float
350    *     output will be used if the input is 32-bit float, and also if the input is high resolution
351    *     (24-bit or 32-bit) integer PCM. Audio processing (for example, speed adjustment) will not
352    *     be available when float output is in use.
353    */
DefaultAudioSink( @ullable AudioCapabilities audioCapabilities, AudioProcessorChain audioProcessorChain, boolean enableFloatOutput)354   public DefaultAudioSink(
355       @Nullable AudioCapabilities audioCapabilities,
356       AudioProcessorChain audioProcessorChain,
357       boolean enableFloatOutput) {
358     this.audioCapabilities = audioCapabilities;
359     this.audioProcessorChain = Assertions.checkNotNull(audioProcessorChain);
360     this.enableFloatOutput = enableFloatOutput;
361     releasingConditionVariable = new ConditionVariable(true);
362     audioTrackPositionTracker = new AudioTrackPositionTracker(new PositionTrackerListener());
363     channelMappingAudioProcessor = new ChannelMappingAudioProcessor();
364     trimmingAudioProcessor = new TrimmingAudioProcessor();
365     ArrayList<AudioProcessor> toIntPcmAudioProcessors = new ArrayList<>();
366     Collections.addAll(
367         toIntPcmAudioProcessors,
368         new ResamplingAudioProcessor(),
369         channelMappingAudioProcessor,
370         trimmingAudioProcessor);
371     Collections.addAll(toIntPcmAudioProcessors, audioProcessorChain.getAudioProcessors());
372     toIntPcmAvailableAudioProcessors = toIntPcmAudioProcessors.toArray(new AudioProcessor[0]);
373     toFloatPcmAvailableAudioProcessors = new AudioProcessor[] {new FloatResamplingAudioProcessor()};
374     volume = 1.0f;
375     audioAttributes = AudioAttributes.DEFAULT;
376     audioSessionId = C.AUDIO_SESSION_ID_UNSET;
377     auxEffectInfo = new AuxEffectInfo(AuxEffectInfo.NO_AUX_EFFECT_ID, 0f);
378     mediaPositionParameters =
379         new MediaPositionParameters(
380             DEFAULT_PLAYBACK_SPEED,
381             DEFAULT_SKIP_SILENCE,
382             /* mediaTimeUs= */ 0,
383             /* audioTrackPositionUs= */ 0);
384     drainingAudioProcessorIndex = C.INDEX_UNSET;
385     activeAudioProcessors = new AudioProcessor[0];
386     outputBuffers = new ByteBuffer[0];
387     mediaPositionParametersCheckpoints = new ArrayDeque<>();
388   }
389 
390   // AudioSink implementation.
391 
392   @Override
setListener(Listener listener)393   public void setListener(Listener listener) {
394     this.listener = listener;
395   }
396 
397   @Override
supportsOutput(int channelCount, @C.Encoding int encoding)398   public boolean supportsOutput(int channelCount, @C.Encoding int encoding) {
399     if (Util.isEncodingLinearPcm(encoding)) {
400       // AudioTrack supports 16-bit integer PCM output in all platform API versions, and float
401       // output from platform API version 21 only. Other integer PCM encodings are resampled by this
402       // sink to 16-bit PCM. We assume that the audio framework will downsample any number of
403       // channels to the output device's required number of channels.
404       return encoding != C.ENCODING_PCM_FLOAT || Util.SDK_INT >= 21;
405     } else {
406       return audioCapabilities != null
407           && audioCapabilities.supportsEncoding(encoding)
408           && (channelCount == Format.NO_VALUE
409               || channelCount <= audioCapabilities.getMaxChannelCount());
410     }
411   }
412 
413   @Override
getCurrentPositionUs(boolean sourceEnded)414   public long getCurrentPositionUs(boolean sourceEnded) {
415     if (!isInitialized()) {
416       return CURRENT_POSITION_NOT_SET;
417     }
418     long positionUs = audioTrackPositionTracker.getCurrentPositionUs(sourceEnded);
419     positionUs = Math.min(positionUs, configuration.framesToDurationUs(getWrittenFrames()));
420     return applySkipping(applyMediaPositionParameters(positionUs));
421   }
422 
423   @Override
configure( @.Encoding int inputEncoding, int inputChannelCount, int inputSampleRate, int specifiedBufferSize, @Nullable int[] outputChannels, int trimStartFrames, int trimEndFrames)424   public void configure(
425       @C.Encoding int inputEncoding,
426       int inputChannelCount,
427       int inputSampleRate,
428       int specifiedBufferSize,
429       @Nullable int[] outputChannels,
430       int trimStartFrames,
431       int trimEndFrames)
432       throws ConfigurationException {
433     if (Util.SDK_INT < 21 && inputChannelCount == 8 && outputChannels == null) {
434       // AudioTrack doesn't support 8 channel output before Android L. Discard the last two (side)
435       // channels to give a 6 channel stream that is supported.
436       outputChannels = new int[6];
437       for (int i = 0; i < outputChannels.length; i++) {
438         outputChannels[i] = i;
439       }
440     }
441 
442     boolean isInputPcm = Util.isEncodingLinearPcm(inputEncoding);
443     boolean processingEnabled = isInputPcm;
444     int sampleRate = inputSampleRate;
445     int channelCount = inputChannelCount;
446     @C.Encoding int encoding = inputEncoding;
447     boolean useFloatOutput =
448         enableFloatOutput
449             && supportsOutput(inputChannelCount, C.ENCODING_PCM_FLOAT)
450             && Util.isEncodingHighResolutionPcm(inputEncoding);
451     AudioProcessor[] availableAudioProcessors =
452         useFloatOutput ? toFloatPcmAvailableAudioProcessors : toIntPcmAvailableAudioProcessors;
453     if (processingEnabled) {
454       trimmingAudioProcessor.setTrimFrameCount(trimStartFrames, trimEndFrames);
455       channelMappingAudioProcessor.setChannelMap(outputChannels);
456       AudioProcessor.AudioFormat outputFormat =
457           new AudioProcessor.AudioFormat(sampleRate, channelCount, encoding);
458       for (AudioProcessor audioProcessor : availableAudioProcessors) {
459         try {
460           AudioProcessor.AudioFormat nextFormat = audioProcessor.configure(outputFormat);
461           if (audioProcessor.isActive()) {
462             outputFormat = nextFormat;
463           }
464         } catch (UnhandledAudioFormatException e) {
465           throw new ConfigurationException(e);
466         }
467       }
468       sampleRate = outputFormat.sampleRate;
469       channelCount = outputFormat.channelCount;
470       encoding = outputFormat.encoding;
471     }
472 
473     int outputChannelConfig = getChannelConfig(channelCount, isInputPcm);
474     if (outputChannelConfig == AudioFormat.CHANNEL_INVALID) {
475       throw new ConfigurationException("Unsupported channel count: " + channelCount);
476     }
477 
478     int inputPcmFrameSize =
479         isInputPcm ? Util.getPcmFrameSize(inputEncoding, inputChannelCount) : C.LENGTH_UNSET;
480     int outputPcmFrameSize =
481         isInputPcm ? Util.getPcmFrameSize(encoding, channelCount) : C.LENGTH_UNSET;
482     boolean canApplyPlaybackParameters = processingEnabled && !useFloatOutput;
483     Configuration pendingConfiguration =
484         new Configuration(
485             isInputPcm,
486             inputPcmFrameSize,
487             inputSampleRate,
488             outputPcmFrameSize,
489             sampleRate,
490             outputChannelConfig,
491             encoding,
492             specifiedBufferSize,
493             processingEnabled,
494             canApplyPlaybackParameters,
495             availableAudioProcessors);
496     if (isInitialized()) {
497       this.pendingConfiguration = pendingConfiguration;
498     } else {
499       configuration = pendingConfiguration;
500     }
501   }
502 
setupAudioProcessors()503   private void setupAudioProcessors() {
504     AudioProcessor[] audioProcessors = configuration.availableAudioProcessors;
505     ArrayList<AudioProcessor> newAudioProcessors = new ArrayList<>();
506     for (AudioProcessor audioProcessor : audioProcessors) {
507       if (audioProcessor.isActive()) {
508         newAudioProcessors.add(audioProcessor);
509       } else {
510         audioProcessor.flush();
511       }
512     }
513     int count = newAudioProcessors.size();
514     activeAudioProcessors = newAudioProcessors.toArray(new AudioProcessor[count]);
515     outputBuffers = new ByteBuffer[count];
516     flushAudioProcessors();
517   }
518 
flushAudioProcessors()519   private void flushAudioProcessors() {
520     for (int i = 0; i < activeAudioProcessors.length; i++) {
521       AudioProcessor audioProcessor = activeAudioProcessors[i];
522       audioProcessor.flush();
523       outputBuffers[i] = audioProcessor.getOutput();
524     }
525   }
526 
initialize(long presentationTimeUs)527   private void initialize(long presentationTimeUs) throws InitializationException {
528     // If we're asynchronously releasing a previous audio track then we block until it has been
529     // released. This guarantees that we cannot end up in a state where we have multiple audio
530     // track instances. Without this guarantee it would be possible, in extreme cases, to exhaust
531     // the shared memory that's available for audio track buffers. This would in turn cause the
532     // initialization of the audio track to fail.
533     releasingConditionVariable.block();
534 
535     audioTrack =
536         Assertions.checkNotNull(configuration)
537             .buildAudioTrack(tunneling, audioAttributes, audioSessionId);
538     int audioSessionId = audioTrack.getAudioSessionId();
539     if (enablePreV21AudioSessionWorkaround) {
540       if (Util.SDK_INT < 21) {
541         // The workaround creates an audio track with a two byte buffer on the same session, and
542         // does not release it until this object is released, which keeps the session active.
543         if (keepSessionIdAudioTrack != null
544             && audioSessionId != keepSessionIdAudioTrack.getAudioSessionId()) {
545           releaseKeepSessionIdAudioTrack();
546         }
547         if (keepSessionIdAudioTrack == null) {
548           keepSessionIdAudioTrack = initializeKeepSessionIdAudioTrack(audioSessionId);
549         }
550       }
551     }
552     if (this.audioSessionId != audioSessionId) {
553       this.audioSessionId = audioSessionId;
554       if (listener != null) {
555         listener.onAudioSessionId(audioSessionId);
556       }
557     }
558 
559     startMediaTimeUs = Math.max(0, presentationTimeUs);
560     startMediaTimeUsNeedsSync = false;
561 
562     applyPlaybackSpeedAndSkipSilence(presentationTimeUs);
563 
564     audioTrackPositionTracker.setAudioTrack(
565         audioTrack,
566         configuration.outputEncoding,
567         configuration.outputPcmFrameSize,
568         configuration.bufferSize);
569     setVolumeInternal();
570 
571     if (auxEffectInfo.effectId != AuxEffectInfo.NO_AUX_EFFECT_ID) {
572       audioTrack.attachAuxEffect(auxEffectInfo.effectId);
573       audioTrack.setAuxEffectSendLevel(auxEffectInfo.sendLevel);
574     }
575   }
576 
577   @Override
play()578   public void play() {
579     playing = true;
580     if (isInitialized()) {
581       audioTrackPositionTracker.start();
582       audioTrack.play();
583     }
584   }
585 
586   @Override
handleDiscontinuity()587   public void handleDiscontinuity() {
588     // Force resynchronization after a skipped buffer.
589     startMediaTimeUsNeedsSync = true;
590   }
591 
592   @Override
593   @SuppressWarnings("ReferenceEquality")
handleBuffer( ByteBuffer buffer, long presentationTimeUs, int encodedAccessUnitCount)594   public boolean handleBuffer(
595       ByteBuffer buffer, long presentationTimeUs, int encodedAccessUnitCount)
596       throws InitializationException, WriteException {
597     Assertions.checkArgument(inputBuffer == null || buffer == inputBuffer);
598 
599     if (pendingConfiguration != null) {
600       if (!drainToEndOfStream()) {
601         // There's still pending data in audio processors to write to the track.
602         return false;
603       } else if (!pendingConfiguration.canReuseAudioTrack(configuration)) {
604         playPendingData();
605         if (hasPendingData()) {
606           // We're waiting for playout on the current audio track to finish.
607           return false;
608         }
609         flush();
610       } else {
611         // The current audio track can be reused for the new configuration.
612         configuration = pendingConfiguration;
613         pendingConfiguration = null;
614       }
615       // Re-apply playback parameters.
616       applyPlaybackSpeedAndSkipSilence(presentationTimeUs);
617     }
618 
619     if (!isInitialized()) {
620       initialize(presentationTimeUs);
621       if (playing) {
622         play();
623       }
624     }
625 
626     if (!audioTrackPositionTracker.mayHandleBuffer(getWrittenFrames())) {
627       return false;
628     }
629 
630     if (inputBuffer == null) {
631       // We are seeing this buffer for the first time.
632       if (!buffer.hasRemaining()) {
633         // The buffer is empty.
634         return true;
635       }
636 
637       if (!configuration.isInputPcm && framesPerEncodedSample == 0) {
638         // If this is the first encoded sample, calculate the sample size in frames.
639         framesPerEncodedSample = getFramesPerEncodedSample(configuration.outputEncoding, buffer);
640         if (framesPerEncodedSample == 0) {
641           // We still don't know the number of frames per sample, so drop the buffer.
642           // For TrueHD this can occur after some seek operations, as not every sample starts with
643           // a syncframe header. If we chunked samples together so the extracted samples always
644           // started with a syncframe header, the chunks would be too large.
645           return true;
646         }
647       }
648 
649       if (afterDrainParameters != null) {
650         if (!drainToEndOfStream()) {
651           // Don't process any more input until draining completes.
652           return false;
653         }
654         applyPlaybackSpeedAndSkipSilence(presentationTimeUs);
655         afterDrainParameters = null;
656       }
657 
658       // Sanity check that presentationTimeUs is consistent with the expected value.
659       long expectedPresentationTimeUs =
660           startMediaTimeUs
661               + configuration.inputFramesToDurationUs(
662                   getSubmittedFrames() - trimmingAudioProcessor.getTrimmedFrameCount());
663       if (!startMediaTimeUsNeedsSync
664           && Math.abs(expectedPresentationTimeUs - presentationTimeUs) > 200000) {
665         Log.e(
666             TAG,
667             "Discontinuity detected [expected "
668                 + expectedPresentationTimeUs
669                 + ", got "
670                 + presentationTimeUs
671                 + "]");
672         startMediaTimeUsNeedsSync = true;
673       }
674       if (startMediaTimeUsNeedsSync) {
675         if (!drainToEndOfStream()) {
676           // Don't update timing until pending AudioProcessor buffers are completely drained.
677           return false;
678         }
679         // Adjust startMediaTimeUs to be consistent with the current buffer's start time and the
680         // number of bytes submitted.
681         long adjustmentUs = presentationTimeUs - expectedPresentationTimeUs;
682         startMediaTimeUs += adjustmentUs;
683         startMediaTimeUsNeedsSync = false;
684         // Re-apply playback parameters because the startMediaTimeUs changed.
685         applyPlaybackSpeedAndSkipSilence(presentationTimeUs);
686         if (listener != null && adjustmentUs != 0) {
687           listener.onPositionDiscontinuity();
688         }
689       }
690 
691       if (configuration.isInputPcm) {
692         submittedPcmBytes += buffer.remaining();
693       } else {
694         submittedEncodedFrames += framesPerEncodedSample * encodedAccessUnitCount;
695       }
696 
697       inputBuffer = buffer;
698       inputBufferAccessUnitCount = encodedAccessUnitCount;
699     }
700 
701     processBuffers(presentationTimeUs);
702 
703     if (!inputBuffer.hasRemaining()) {
704       inputBuffer = null;
705       inputBufferAccessUnitCount = 0;
706       return true;
707     }
708 
709     if (audioTrackPositionTracker.isStalled(getWrittenFrames())) {
710       Log.w(TAG, "Resetting stalled audio track");
711       flush();
712       return true;
713     }
714 
715     return false;
716   }
717 
processBuffers(long avSyncPresentationTimeUs)718   private void processBuffers(long avSyncPresentationTimeUs) throws WriteException {
719     int count = activeAudioProcessors.length;
720     int index = count;
721     while (index >= 0) {
722       ByteBuffer input = index > 0 ? outputBuffers[index - 1]
723           : (inputBuffer != null ? inputBuffer : AudioProcessor.EMPTY_BUFFER);
724       if (index == count) {
725         writeBuffer(input, avSyncPresentationTimeUs);
726       } else {
727         AudioProcessor audioProcessor = activeAudioProcessors[index];
728         audioProcessor.queueInput(input);
729         ByteBuffer output = audioProcessor.getOutput();
730         outputBuffers[index] = output;
731         if (output.hasRemaining()) {
732           // Handle the output as input to the next audio processor or the AudioTrack.
733           index++;
734           continue;
735         }
736       }
737 
738       if (input.hasRemaining()) {
739         // The input wasn't consumed and no output was produced, so give up for now.
740         return;
741       }
742 
743       // Get more input from upstream.
744       index--;
745     }
746   }
747 
748   @SuppressWarnings("ReferenceEquality")
writeBuffer(ByteBuffer buffer, long avSyncPresentationTimeUs)749   private void writeBuffer(ByteBuffer buffer, long avSyncPresentationTimeUs) throws WriteException {
750     if (!buffer.hasRemaining()) {
751       return;
752     }
753     if (outputBuffer != null) {
754       Assertions.checkArgument(outputBuffer == buffer);
755     } else {
756       outputBuffer = buffer;
757       if (Util.SDK_INT < 21) {
758         int bytesRemaining = buffer.remaining();
759         if (preV21OutputBuffer == null || preV21OutputBuffer.length < bytesRemaining) {
760           preV21OutputBuffer = new byte[bytesRemaining];
761         }
762         int originalPosition = buffer.position();
763         buffer.get(preV21OutputBuffer, 0, bytesRemaining);
764         buffer.position(originalPosition);
765         preV21OutputBufferOffset = 0;
766       }
767     }
768     int bytesRemaining = buffer.remaining();
769     int bytesWritten = 0;
770     if (Util.SDK_INT < 21) { // isInputPcm == true
771       // Work out how many bytes we can write without the risk of blocking.
772       int bytesToWrite = audioTrackPositionTracker.getAvailableBufferSize(writtenPcmBytes);
773       if (bytesToWrite > 0) {
774         bytesToWrite = Math.min(bytesRemaining, bytesToWrite);
775         bytesWritten = audioTrack.write(preV21OutputBuffer, preV21OutputBufferOffset, bytesToWrite);
776         if (bytesWritten > 0) {
777           preV21OutputBufferOffset += bytesWritten;
778           buffer.position(buffer.position() + bytesWritten);
779         }
780       }
781     } else if (tunneling) {
782       Assertions.checkState(avSyncPresentationTimeUs != C.TIME_UNSET);
783       bytesWritten = writeNonBlockingWithAvSyncV21(audioTrack, buffer, bytesRemaining,
784           avSyncPresentationTimeUs);
785     } else {
786       bytesWritten = writeNonBlockingV21(audioTrack, buffer, bytesRemaining);
787     }
788 
789     lastFeedElapsedRealtimeMs = SystemClock.elapsedRealtime();
790 
791     if (bytesWritten < 0) {
792       throw new WriteException(bytesWritten);
793     }
794 
795     if (configuration.isInputPcm) {
796       writtenPcmBytes += bytesWritten;
797     }
798     if (bytesWritten == bytesRemaining) {
799       if (!configuration.isInputPcm) {
800         // When playing non-PCM, the inputBuffer is never processed, thus the last inputBuffer
801         // must be the current input buffer.
802         Assertions.checkState(buffer == inputBuffer);
803         writtenEncodedFrames += framesPerEncodedSample * inputBufferAccessUnitCount;
804       }
805       outputBuffer = null;
806     }
807   }
808 
809   @Override
playToEndOfStream()810   public void playToEndOfStream() throws WriteException {
811     if (!handledEndOfStream && isInitialized() && drainToEndOfStream()) {
812       playPendingData();
813       handledEndOfStream = true;
814     }
815   }
816 
drainToEndOfStream()817   private boolean drainToEndOfStream() throws WriteException {
818     boolean audioProcessorNeedsEndOfStream = false;
819     if (drainingAudioProcessorIndex == C.INDEX_UNSET) {
820       drainingAudioProcessorIndex =
821           configuration.processingEnabled ? 0 : activeAudioProcessors.length;
822       audioProcessorNeedsEndOfStream = true;
823     }
824     while (drainingAudioProcessorIndex < activeAudioProcessors.length) {
825       AudioProcessor audioProcessor = activeAudioProcessors[drainingAudioProcessorIndex];
826       if (audioProcessorNeedsEndOfStream) {
827         audioProcessor.queueEndOfStream();
828       }
829       processBuffers(C.TIME_UNSET);
830       if (!audioProcessor.isEnded()) {
831         return false;
832       }
833       audioProcessorNeedsEndOfStream = true;
834       drainingAudioProcessorIndex++;
835     }
836 
837     // Finish writing any remaining output to the track.
838     if (outputBuffer != null) {
839       writeBuffer(outputBuffer, C.TIME_UNSET);
840       if (outputBuffer != null) {
841         return false;
842       }
843     }
844     drainingAudioProcessorIndex = C.INDEX_UNSET;
845     return true;
846   }
847 
848   @Override
isEnded()849   public boolean isEnded() {
850     return !isInitialized() || (handledEndOfStream && !hasPendingData());
851   }
852 
853   @Override
hasPendingData()854   public boolean hasPendingData() {
855     return isInitialized() && audioTrackPositionTracker.hasPendingData(getWrittenFrames());
856   }
857 
858   /**
859    * @deprecated Use {@link #setPlaybackSpeed(float)} and {@link #setSkipSilenceEnabled(boolean)}
860    *     instead.
861    */
862   @SuppressWarnings("deprecation")
863   @Deprecated
864   @Override
setPlaybackParameters(PlaybackParameters playbackParameters)865   public void setPlaybackParameters(PlaybackParameters playbackParameters) {
866     setPlaybackSpeedAndSkipSilence(playbackParameters.speed, getSkipSilenceEnabled());
867   }
868 
869   /** @deprecated Use {@link #getPlaybackSpeed()} and {@link #getSkipSilenceEnabled()} instead. */
870   @SuppressWarnings("deprecation")
871   @Deprecated
872   @Override
getPlaybackParameters()873   public PlaybackParameters getPlaybackParameters() {
874     MediaPositionParameters mediaPositionParameters = getMediaPositionParameters();
875     return new PlaybackParameters(mediaPositionParameters.playbackSpeed);
876   }
877 
878   @Override
setPlaybackSpeed(float playbackSpeed)879   public void setPlaybackSpeed(float playbackSpeed) {
880     setPlaybackSpeedAndSkipSilence(playbackSpeed, getSkipSilenceEnabled());
881   }
882 
883   @Override
getPlaybackSpeed()884   public float getPlaybackSpeed() {
885     return getMediaPositionParameters().playbackSpeed;
886   }
887 
888   @Override
setSkipSilenceEnabled(boolean skipSilenceEnabled)889   public void setSkipSilenceEnabled(boolean skipSilenceEnabled) {
890     setPlaybackSpeedAndSkipSilence(getPlaybackSpeed(), skipSilenceEnabled);
891   }
892 
893   @Override
getSkipSilenceEnabled()894   public boolean getSkipSilenceEnabled() {
895     return getMediaPositionParameters().skipSilence;
896   }
897 
898   @Override
setAudioAttributes(AudioAttributes audioAttributes)899   public void setAudioAttributes(AudioAttributes audioAttributes) {
900     if (this.audioAttributes.equals(audioAttributes)) {
901       return;
902     }
903     this.audioAttributes = audioAttributes;
904     if (tunneling) {
905       // The audio attributes are ignored in tunneling mode, so no need to reset.
906       return;
907     }
908     flush();
909     audioSessionId = C.AUDIO_SESSION_ID_UNSET;
910   }
911 
912   @Override
setAudioSessionId(int audioSessionId)913   public void setAudioSessionId(int audioSessionId) {
914     if (this.audioSessionId != audioSessionId) {
915       this.audioSessionId = audioSessionId;
916       flush();
917     }
918   }
919 
920   @Override
setAuxEffectInfo(AuxEffectInfo auxEffectInfo)921   public void setAuxEffectInfo(AuxEffectInfo auxEffectInfo) {
922     if (this.auxEffectInfo.equals(auxEffectInfo)) {
923       return;
924     }
925     int effectId = auxEffectInfo.effectId;
926     float sendLevel = auxEffectInfo.sendLevel;
927     if (audioTrack != null) {
928       if (this.auxEffectInfo.effectId != effectId) {
929         audioTrack.attachAuxEffect(effectId);
930       }
931       if (effectId != AuxEffectInfo.NO_AUX_EFFECT_ID) {
932         audioTrack.setAuxEffectSendLevel(sendLevel);
933       }
934     }
935     this.auxEffectInfo = auxEffectInfo;
936   }
937 
938   @Override
enableTunnelingV21(int tunnelingAudioSessionId)939   public void enableTunnelingV21(int tunnelingAudioSessionId) {
940     Assertions.checkState(Util.SDK_INT >= 21);
941     if (!tunneling || audioSessionId != tunnelingAudioSessionId) {
942       tunneling = true;
943       audioSessionId = tunnelingAudioSessionId;
944       flush();
945     }
946   }
947 
948   @Override
disableTunneling()949   public void disableTunneling() {
950     if (tunneling) {
951       tunneling = false;
952       audioSessionId = C.AUDIO_SESSION_ID_UNSET;
953       flush();
954     }
955   }
956 
957   @Override
setVolume(float volume)958   public void setVolume(float volume) {
959     if (this.volume != volume) {
960       this.volume = volume;
961       setVolumeInternal();
962     }
963   }
964 
setVolumeInternal()965   private void setVolumeInternal() {
966     if (!isInitialized()) {
967       // Do nothing.
968     } else if (Util.SDK_INT >= 21) {
969       setVolumeInternalV21(audioTrack, volume);
970     } else {
971       setVolumeInternalV3(audioTrack, volume);
972     }
973   }
974 
975   @Override
pause()976   public void pause() {
977     playing = false;
978     if (isInitialized() && audioTrackPositionTracker.pause()) {
979       audioTrack.pause();
980     }
981   }
982 
983   @Override
flush()984   public void flush() {
985     if (isInitialized()) {
986       submittedPcmBytes = 0;
987       submittedEncodedFrames = 0;
988       writtenPcmBytes = 0;
989       writtenEncodedFrames = 0;
990       framesPerEncodedSample = 0;
991       mediaPositionParameters =
992           new MediaPositionParameters(
993               getPlaybackSpeed(),
994               getSkipSilenceEnabled(),
995               /* mediaTimeUs= */ 0,
996               /* audioTrackPositionUs= */ 0);
997       startMediaTimeUs = 0;
998       afterDrainParameters = null;
999       mediaPositionParametersCheckpoints.clear();
1000       trimmingAudioProcessor.resetTrimmedFrameCount();
1001       flushAudioProcessors();
1002       inputBuffer = null;
1003       inputBufferAccessUnitCount = 0;
1004       outputBuffer = null;
1005       stoppedAudioTrack = false;
1006       handledEndOfStream = false;
1007       drainingAudioProcessorIndex = C.INDEX_UNSET;
1008       avSyncHeader = null;
1009       bytesUntilNextAvSync = 0;
1010       if (audioTrackPositionTracker.isPlaying()) {
1011         audioTrack.pause();
1012       }
1013       // AudioTrack.release can take some time, so we call it on a background thread.
1014       final AudioTrack toRelease = audioTrack;
1015       audioTrack = null;
1016       if (pendingConfiguration != null) {
1017         configuration = pendingConfiguration;
1018         pendingConfiguration = null;
1019       }
1020       audioTrackPositionTracker.reset();
1021       releasingConditionVariable.close();
1022       new Thread("ExoPlayer:AudioTrackReleaseThread") {
1023         @Override
1024         public void run() {
1025           try {
1026             toRelease.flush();
1027             toRelease.release();
1028           } finally {
1029             releasingConditionVariable.open();
1030           }
1031         }
1032       }.start();
1033     }
1034   }
1035 
1036   @Override
reset()1037   public void reset() {
1038     flush();
1039     releaseKeepSessionIdAudioTrack();
1040     for (AudioProcessor audioProcessor : toIntPcmAvailableAudioProcessors) {
1041       audioProcessor.reset();
1042     }
1043     for (AudioProcessor audioProcessor : toFloatPcmAvailableAudioProcessors) {
1044       audioProcessor.reset();
1045     }
1046     audioSessionId = C.AUDIO_SESSION_ID_UNSET;
1047     playing = false;
1048   }
1049 
1050   // Internal methods.
1051 
1052   /** Releases {@link #keepSessionIdAudioTrack} asynchronously, if it is non-{@code null}. */
releaseKeepSessionIdAudioTrack()1053   private void releaseKeepSessionIdAudioTrack() {
1054     if (keepSessionIdAudioTrack == null) {
1055       return;
1056     }
1057 
1058     // AudioTrack.release can take some time, so we call it on a background thread.
1059     final AudioTrack toRelease = keepSessionIdAudioTrack;
1060     keepSessionIdAudioTrack = null;
1061     new Thread() {
1062       @Override
1063       public void run() {
1064         toRelease.release();
1065       }
1066     }.start();
1067   }
1068 
setPlaybackSpeedAndSkipSilence(float playbackSpeed, boolean skipSilence)1069   private void setPlaybackSpeedAndSkipSilence(float playbackSpeed, boolean skipSilence) {
1070     MediaPositionParameters currentMediaPositionParameters = getMediaPositionParameters();
1071     if (playbackSpeed != currentMediaPositionParameters.playbackSpeed
1072         || skipSilence != currentMediaPositionParameters.skipSilence) {
1073       MediaPositionParameters mediaPositionParameters =
1074           new MediaPositionParameters(
1075               playbackSpeed,
1076               skipSilence,
1077               /* mediaTimeUs= */ C.TIME_UNSET,
1078               /* audioTrackPositionUs= */ C.TIME_UNSET);
1079       if (isInitialized()) {
1080         // Drain the audio processors so we can determine the frame position at which the new
1081         // parameters apply.
1082         this.afterDrainParameters = mediaPositionParameters;
1083       } else {
1084         // Update the audio processor chain parameters now. They will be applied to the audio
1085         // processors during initialization.
1086         this.mediaPositionParameters = mediaPositionParameters;
1087       }
1088     }
1089   }
1090 
getMediaPositionParameters()1091   private MediaPositionParameters getMediaPositionParameters() {
1092     // Mask the already set parameters.
1093     return afterDrainParameters != null
1094         ? afterDrainParameters
1095         : !mediaPositionParametersCheckpoints.isEmpty()
1096             ? mediaPositionParametersCheckpoints.getLast()
1097             : mediaPositionParameters;
1098   }
1099 
applyPlaybackSpeedAndSkipSilence(long presentationTimeUs)1100   private void applyPlaybackSpeedAndSkipSilence(long presentationTimeUs) {
1101     float playbackSpeed =
1102         configuration.canApplyPlaybackParameters
1103             ? audioProcessorChain.applyPlaybackSpeed(getPlaybackSpeed())
1104             : DEFAULT_PLAYBACK_SPEED;
1105     boolean skipSilenceEnabled =
1106         configuration.canApplyPlaybackParameters
1107             ? audioProcessorChain.applySkipSilenceEnabled(getSkipSilenceEnabled())
1108             : DEFAULT_SKIP_SILENCE;
1109     mediaPositionParametersCheckpoints.add(
1110         new MediaPositionParameters(
1111             playbackSpeed,
1112             skipSilenceEnabled,
1113             /* mediaTimeUs= */ Math.max(0, presentationTimeUs),
1114             /* audioTrackPositionUs= */ configuration.framesToDurationUs(getWrittenFrames())));
1115     setupAudioProcessors();
1116     if (listener != null) {
1117       listener.onSkipSilenceEnabledChanged(skipSilenceEnabled);
1118     }
1119   }
1120 
1121   /**
1122    * Applies and updates media position parameters.
1123    *
1124    * @param positionUs The current audio track position, in microseconds.
1125    * @return The current media time, in microseconds.
1126    */
applyMediaPositionParameters(long positionUs)1127   private long applyMediaPositionParameters(long positionUs) {
1128     while (!mediaPositionParametersCheckpoints.isEmpty()
1129         && positionUs >= mediaPositionParametersCheckpoints.getFirst().audioTrackPositionUs) {
1130       // We are playing (or about to play) media with the new parameters, so update them.
1131       mediaPositionParameters = mediaPositionParametersCheckpoints.remove();
1132     }
1133 
1134     long playoutDurationSinceLastCheckpoint =
1135         positionUs - mediaPositionParameters.audioTrackPositionUs;
1136     if (mediaPositionParameters.playbackSpeed != 1f) {
1137       if (mediaPositionParametersCheckpoints.isEmpty()) {
1138         playoutDurationSinceLastCheckpoint =
1139             audioProcessorChain.getMediaDuration(playoutDurationSinceLastCheckpoint);
1140       } else {
1141         // Playing data at a previous playback speed, so fall back to multiplying by the speed.
1142         playoutDurationSinceLastCheckpoint =
1143             Util.getMediaDurationForPlayoutDuration(
1144                 playoutDurationSinceLastCheckpoint, mediaPositionParameters.playbackSpeed);
1145       }
1146     }
1147     return mediaPositionParameters.mediaTimeUs + playoutDurationSinceLastCheckpoint;
1148   }
1149 
applySkipping(long positionUs)1150   private long applySkipping(long positionUs) {
1151     return positionUs
1152         + configuration.framesToDurationUs(audioProcessorChain.getSkippedOutputFrameCount());
1153   }
1154 
isInitialized()1155   private boolean isInitialized() {
1156     return audioTrack != null;
1157   }
1158 
getSubmittedFrames()1159   private long getSubmittedFrames() {
1160     return configuration.isInputPcm
1161         ? (submittedPcmBytes / configuration.inputPcmFrameSize)
1162         : submittedEncodedFrames;
1163   }
1164 
getWrittenFrames()1165   private long getWrittenFrames() {
1166     return configuration.isInputPcm
1167         ? (writtenPcmBytes / configuration.outputPcmFrameSize)
1168         : writtenEncodedFrames;
1169   }
1170 
initializeKeepSessionIdAudioTrack(int audioSessionId)1171   private static AudioTrack initializeKeepSessionIdAudioTrack(int audioSessionId) {
1172     int sampleRate = 4000; // Equal to private AudioTrack.MIN_SAMPLE_RATE.
1173     int channelConfig = AudioFormat.CHANNEL_OUT_MONO;
1174     @C.PcmEncoding int encoding = C.ENCODING_PCM_16BIT;
1175     int bufferSize = 2; // Use a two byte buffer, as it is not actually used for playback.
1176     return new AudioTrack(C.STREAM_TYPE_DEFAULT, sampleRate, channelConfig, encoding, bufferSize,
1177         MODE_STATIC, audioSessionId);
1178   }
1179 
getChannelConfig(int channelCount, boolean isInputPcm)1180   private static int getChannelConfig(int channelCount, boolean isInputPcm) {
1181     if (Util.SDK_INT <= 28 && !isInputPcm) {
1182       // In passthrough mode the channel count used to configure the audio track doesn't affect how
1183       // the stream is handled, except that some devices do overly-strict channel configuration
1184       // checks. Therefore we override the channel count so that a known-working channel
1185       // configuration is chosen in all cases. See [Internal: b/29116190].
1186       if (channelCount == 7) {
1187         channelCount = 8;
1188       } else if (channelCount == 3 || channelCount == 4 || channelCount == 5) {
1189         channelCount = 6;
1190       }
1191     }
1192 
1193     // Workaround for Nexus Player not reporting support for mono passthrough.
1194     // (See [Internal: b/34268671].)
1195     if (Util.SDK_INT <= 26 && "fugu".equals(Util.DEVICE) && !isInputPcm && channelCount == 1) {
1196       channelCount = 2;
1197     }
1198 
1199     return Util.getAudioTrackChannelConfig(channelCount);
1200   }
1201 
getMaximumEncodedRateBytesPerSecond(@.Encoding int encoding)1202   private static int getMaximumEncodedRateBytesPerSecond(@C.Encoding int encoding) {
1203     switch (encoding) {
1204       case C.ENCODING_MP3:
1205         return MpegAudioUtil.MAX_RATE_BYTES_PER_SECOND;
1206       case C.ENCODING_AAC_LC:
1207         return AacUtil.AAC_LC_MAX_RATE_BYTES_PER_SECOND;
1208       case C.ENCODING_AAC_HE_V1:
1209         return AacUtil.AAC_HE_V1_MAX_RATE_BYTES_PER_SECOND;
1210       case C.ENCODING_AAC_HE_V2:
1211         return AacUtil.AAC_HE_V2_MAX_RATE_BYTES_PER_SECOND;
1212       case C.ENCODING_AAC_XHE:
1213         return AacUtil.AAC_XHE_MAX_RATE_BYTES_PER_SECOND;
1214       case C.ENCODING_AAC_ELD:
1215         return AacUtil.AAC_ELD_MAX_RATE_BYTES_PER_SECOND;
1216       case C.ENCODING_AC3:
1217         return Ac3Util.AC3_MAX_RATE_BYTES_PER_SECOND;
1218       case C.ENCODING_E_AC3:
1219       case C.ENCODING_E_AC3_JOC:
1220         return Ac3Util.E_AC3_MAX_RATE_BYTES_PER_SECOND;
1221       case C.ENCODING_AC4:
1222         return Ac4Util.MAX_RATE_BYTES_PER_SECOND;
1223       case C.ENCODING_DTS:
1224         return DtsUtil.DTS_MAX_RATE_BYTES_PER_SECOND;
1225       case C.ENCODING_DTS_HD:
1226         return DtsUtil.DTS_HD_MAX_RATE_BYTES_PER_SECOND;
1227       case C.ENCODING_DOLBY_TRUEHD:
1228         return Ac3Util.TRUEHD_MAX_RATE_BYTES_PER_SECOND;
1229       case C.ENCODING_PCM_16BIT:
1230       case C.ENCODING_PCM_16BIT_BIG_ENDIAN:
1231       case C.ENCODING_PCM_24BIT:
1232       case C.ENCODING_PCM_32BIT:
1233       case C.ENCODING_PCM_8BIT:
1234       case C.ENCODING_PCM_FLOAT:
1235       case C.ENCODING_INVALID:
1236       case Format.NO_VALUE:
1237       default:
1238         throw new IllegalArgumentException();
1239     }
1240   }
1241 
getFramesPerEncodedSample(@.Encoding int encoding, ByteBuffer buffer)1242   private static int getFramesPerEncodedSample(@C.Encoding int encoding, ByteBuffer buffer) {
1243     switch (encoding) {
1244       case C.ENCODING_MP3:
1245         int headerDataInBigEndian = Util.getBigEndianInt(buffer, buffer.position());
1246         return MpegAudioUtil.parseMpegAudioFrameSampleCount(headerDataInBigEndian);
1247       case C.ENCODING_AAC_LC:
1248         return AacUtil.AAC_LC_AUDIO_SAMPLE_COUNT;
1249       case C.ENCODING_AAC_HE_V1:
1250       case C.ENCODING_AAC_HE_V2:
1251         return AacUtil.AAC_HE_AUDIO_SAMPLE_COUNT;
1252       case C.ENCODING_AAC_XHE:
1253         return AacUtil.AAC_XHE_AUDIO_SAMPLE_COUNT;
1254       case C.ENCODING_AAC_ELD:
1255         return AacUtil.AAC_LD_AUDIO_SAMPLE_COUNT;
1256       case C.ENCODING_DTS:
1257       case C.ENCODING_DTS_HD:
1258         return DtsUtil.parseDtsAudioSampleCount(buffer);
1259       case C.ENCODING_AC3:
1260       case C.ENCODING_E_AC3:
1261       case C.ENCODING_E_AC3_JOC:
1262         return Ac3Util.parseAc3SyncframeAudioSampleCount(buffer);
1263       case C.ENCODING_AC4:
1264         return Ac4Util.parseAc4SyncframeAudioSampleCount(buffer);
1265       case C.ENCODING_DOLBY_TRUEHD:
1266         int syncframeOffset = Ac3Util.findTrueHdSyncframeOffset(buffer);
1267         return syncframeOffset == C.INDEX_UNSET
1268             ? 0
1269             : (Ac3Util.parseTrueHdSyncframeAudioSampleCount(buffer, syncframeOffset)
1270                 * Ac3Util.TRUEHD_RECHUNK_SAMPLE_COUNT);
1271       case C.ENCODING_PCM_16BIT:
1272       case C.ENCODING_PCM_16BIT_BIG_ENDIAN:
1273       case C.ENCODING_PCM_24BIT:
1274       case C.ENCODING_PCM_32BIT:
1275       case C.ENCODING_PCM_8BIT:
1276       case C.ENCODING_PCM_FLOAT:
1277       case C.ENCODING_INVALID:
1278       case Format.NO_VALUE:
1279       default:
1280         throw new IllegalStateException("Unexpected audio encoding: " + encoding);
1281     }
1282   }
1283 
1284   @RequiresApi(21)
writeNonBlockingV21(AudioTrack audioTrack, ByteBuffer buffer, int size)1285   private static int writeNonBlockingV21(AudioTrack audioTrack, ByteBuffer buffer, int size) {
1286     return audioTrack.write(buffer, size, WRITE_NON_BLOCKING);
1287   }
1288 
1289   @RequiresApi(21)
writeNonBlockingWithAvSyncV21( AudioTrack audioTrack, ByteBuffer buffer, int size, long presentationTimeUs)1290   private int writeNonBlockingWithAvSyncV21(
1291       AudioTrack audioTrack, ByteBuffer buffer, int size, long presentationTimeUs) {
1292     if (Util.SDK_INT >= 26) {
1293       // The underlying platform AudioTrack writes AV sync headers directly.
1294       return audioTrack.write(buffer, size, WRITE_NON_BLOCKING, presentationTimeUs * 1000);
1295     }
1296     if (avSyncHeader == null) {
1297       avSyncHeader = ByteBuffer.allocate(16);
1298       avSyncHeader.order(ByteOrder.BIG_ENDIAN);
1299       avSyncHeader.putInt(0x55550001);
1300     }
1301     if (bytesUntilNextAvSync == 0) {
1302       avSyncHeader.putInt(4, size);
1303       avSyncHeader.putLong(8, presentationTimeUs * 1000);
1304       avSyncHeader.position(0);
1305       bytesUntilNextAvSync = size;
1306     }
1307     int avSyncHeaderBytesRemaining = avSyncHeader.remaining();
1308     if (avSyncHeaderBytesRemaining > 0) {
1309       int result = audioTrack.write(avSyncHeader, avSyncHeaderBytesRemaining, WRITE_NON_BLOCKING);
1310       if (result < 0) {
1311         bytesUntilNextAvSync = 0;
1312         return result;
1313       }
1314       if (result < avSyncHeaderBytesRemaining) {
1315         return 0;
1316       }
1317     }
1318     int result = writeNonBlockingV21(audioTrack, buffer, size);
1319     if (result < 0) {
1320       bytesUntilNextAvSync = 0;
1321       return result;
1322     }
1323     bytesUntilNextAvSync -= result;
1324     return result;
1325   }
1326 
1327   @RequiresApi(21)
setVolumeInternalV21(AudioTrack audioTrack, float volume)1328   private static void setVolumeInternalV21(AudioTrack audioTrack, float volume) {
1329     audioTrack.setVolume(volume);
1330   }
1331 
setVolumeInternalV3(AudioTrack audioTrack, float volume)1332   private static void setVolumeInternalV3(AudioTrack audioTrack, float volume) {
1333     audioTrack.setStereoVolume(volume, volume);
1334   }
1335 
playPendingData()1336   private void playPendingData() {
1337     if (!stoppedAudioTrack) {
1338       stoppedAudioTrack = true;
1339       audioTrackPositionTracker.handleEndOfStream(getWrittenFrames());
1340       audioTrack.stop();
1341       bytesUntilNextAvSync = 0;
1342     }
1343   }
1344 
1345   /** Stores parameters used to calculate the current media position. */
1346   private static final class MediaPositionParameters {
1347 
1348     /** The playback speed. */
1349     public final float playbackSpeed;
1350     /** Whether to skip silences. */
1351     public final boolean skipSilence;
1352     /** The media time from which the playback parameters apply, in microseconds. */
1353     public final long mediaTimeUs;
1354     /** The audio track position from which the playback parameters apply, in microseconds. */
1355     public final long audioTrackPositionUs;
1356 
MediaPositionParameters( float playbackSpeed, boolean skipSilence, long mediaTimeUs, long audioTrackPositionUs)1357     private MediaPositionParameters(
1358         float playbackSpeed, boolean skipSilence, long mediaTimeUs, long audioTrackPositionUs) {
1359       this.playbackSpeed = playbackSpeed;
1360       this.skipSilence = skipSilence;
1361       this.mediaTimeUs = mediaTimeUs;
1362       this.audioTrackPositionUs = audioTrackPositionUs;
1363     }
1364   }
1365 
1366   private final class PositionTrackerListener implements AudioTrackPositionTracker.Listener {
1367 
1368     @Override
onPositionFramesMismatch( long audioTimestampPositionFrames, long audioTimestampSystemTimeUs, long systemTimeUs, long playbackPositionUs)1369     public void onPositionFramesMismatch(
1370         long audioTimestampPositionFrames,
1371         long audioTimestampSystemTimeUs,
1372         long systemTimeUs,
1373         long playbackPositionUs) {
1374       String message =
1375           "Spurious audio timestamp (frame position mismatch): "
1376               + audioTimestampPositionFrames
1377               + ", "
1378               + audioTimestampSystemTimeUs
1379               + ", "
1380               + systemTimeUs
1381               + ", "
1382               + playbackPositionUs
1383               + ", "
1384               + getSubmittedFrames()
1385               + ", "
1386               + getWrittenFrames();
1387       if (failOnSpuriousAudioTimestamp) {
1388         throw new InvalidAudioTrackTimestampException(message);
1389       }
1390       Log.w(TAG, message);
1391     }
1392 
1393     @Override
onSystemTimeUsMismatch( long audioTimestampPositionFrames, long audioTimestampSystemTimeUs, long systemTimeUs, long playbackPositionUs)1394     public void onSystemTimeUsMismatch(
1395         long audioTimestampPositionFrames,
1396         long audioTimestampSystemTimeUs,
1397         long systemTimeUs,
1398         long playbackPositionUs) {
1399       String message =
1400           "Spurious audio timestamp (system clock mismatch): "
1401               + audioTimestampPositionFrames
1402               + ", "
1403               + audioTimestampSystemTimeUs
1404               + ", "
1405               + systemTimeUs
1406               + ", "
1407               + playbackPositionUs
1408               + ", "
1409               + getSubmittedFrames()
1410               + ", "
1411               + getWrittenFrames();
1412       if (failOnSpuriousAudioTimestamp) {
1413         throw new InvalidAudioTrackTimestampException(message);
1414       }
1415       Log.w(TAG, message);
1416     }
1417 
1418     @Override
onInvalidLatency(long latencyUs)1419     public void onInvalidLatency(long latencyUs) {
1420       Log.w(TAG, "Ignoring impossibly large audio latency: " + latencyUs);
1421     }
1422 
1423     @Override
onUnderrun(int bufferSize, long bufferSizeMs)1424     public void onUnderrun(int bufferSize, long bufferSizeMs) {
1425       if (listener != null) {
1426         long elapsedSinceLastFeedMs = SystemClock.elapsedRealtime() - lastFeedElapsedRealtimeMs;
1427         listener.onUnderrun(bufferSize, bufferSizeMs, elapsedSinceLastFeedMs);
1428       }
1429     }
1430   }
1431 
1432   /** Stores configuration relating to the audio format. */
1433   private static final class Configuration {
1434 
1435     public final boolean isInputPcm;
1436     public final int inputPcmFrameSize;
1437     public final int inputSampleRate;
1438     public final int outputPcmFrameSize;
1439     public final int outputSampleRate;
1440     public final int outputChannelConfig;
1441     @C.Encoding public final int outputEncoding;
1442     public final int bufferSize;
1443     public final boolean processingEnabled;
1444     public final boolean canApplyPlaybackParameters;
1445     public final AudioProcessor[] availableAudioProcessors;
1446 
Configuration( boolean isInputPcm, int inputPcmFrameSize, int inputSampleRate, int outputPcmFrameSize, int outputSampleRate, int outputChannelConfig, int outputEncoding, int specifiedBufferSize, boolean processingEnabled, boolean canApplyPlaybackParameters, AudioProcessor[] availableAudioProcessors)1447     public Configuration(
1448         boolean isInputPcm,
1449         int inputPcmFrameSize,
1450         int inputSampleRate,
1451         int outputPcmFrameSize,
1452         int outputSampleRate,
1453         int outputChannelConfig,
1454         int outputEncoding,
1455         int specifiedBufferSize,
1456         boolean processingEnabled,
1457         boolean canApplyPlaybackParameters,
1458         AudioProcessor[] availableAudioProcessors) {
1459       this.isInputPcm = isInputPcm;
1460       this.inputPcmFrameSize = inputPcmFrameSize;
1461       this.inputSampleRate = inputSampleRate;
1462       this.outputPcmFrameSize = outputPcmFrameSize;
1463       this.outputSampleRate = outputSampleRate;
1464       this.outputChannelConfig = outputChannelConfig;
1465       this.outputEncoding = outputEncoding;
1466       this.bufferSize = specifiedBufferSize != 0 ? specifiedBufferSize : getDefaultBufferSize();
1467       this.processingEnabled = processingEnabled;
1468       this.canApplyPlaybackParameters = canApplyPlaybackParameters;
1469       this.availableAudioProcessors = availableAudioProcessors;
1470     }
1471 
canReuseAudioTrack(Configuration audioTrackConfiguration)1472     public boolean canReuseAudioTrack(Configuration audioTrackConfiguration) {
1473       return audioTrackConfiguration.outputEncoding == outputEncoding
1474           && audioTrackConfiguration.outputSampleRate == outputSampleRate
1475           && audioTrackConfiguration.outputChannelConfig == outputChannelConfig;
1476     }
1477 
inputFramesToDurationUs(long frameCount)1478     public long inputFramesToDurationUs(long frameCount) {
1479       return (frameCount * C.MICROS_PER_SECOND) / inputSampleRate;
1480     }
1481 
framesToDurationUs(long frameCount)1482     public long framesToDurationUs(long frameCount) {
1483       return (frameCount * C.MICROS_PER_SECOND) / outputSampleRate;
1484     }
1485 
durationUsToFrames(long durationUs)1486     public long durationUsToFrames(long durationUs) {
1487       return (durationUs * outputSampleRate) / C.MICROS_PER_SECOND;
1488     }
1489 
buildAudioTrack( boolean tunneling, AudioAttributes audioAttributes, int audioSessionId)1490     public AudioTrack buildAudioTrack(
1491         boolean tunneling, AudioAttributes audioAttributes, int audioSessionId)
1492         throws InitializationException {
1493       AudioTrack audioTrack;
1494       if (Util.SDK_INT >= 21) {
1495         audioTrack = createAudioTrackV21(tunneling, audioAttributes, audioSessionId);
1496       } else {
1497         int streamType = Util.getStreamTypeForAudioUsage(audioAttributes.usage);
1498         if (audioSessionId == C.AUDIO_SESSION_ID_UNSET) {
1499           audioTrack =
1500               new AudioTrack(
1501                   streamType,
1502                   outputSampleRate,
1503                   outputChannelConfig,
1504                   outputEncoding,
1505                   bufferSize,
1506                   MODE_STREAM);
1507         } else {
1508           // Re-attach to the same audio session.
1509           audioTrack =
1510               new AudioTrack(
1511                   streamType,
1512                   outputSampleRate,
1513                   outputChannelConfig,
1514                   outputEncoding,
1515                   bufferSize,
1516                   MODE_STREAM,
1517                   audioSessionId);
1518         }
1519       }
1520 
1521       int state = audioTrack.getState();
1522       if (state != STATE_INITIALIZED) {
1523         try {
1524           audioTrack.release();
1525         } catch (Exception e) {
1526           // The track has already failed to initialize, so it wouldn't be that surprising if
1527           // release were to fail too. Swallow the exception.
1528         }
1529         throw new InitializationException(state, outputSampleRate, outputChannelConfig, bufferSize);
1530       }
1531       return audioTrack;
1532     }
1533 
1534     @RequiresApi(21)
createAudioTrackV21( boolean tunneling, AudioAttributes audioAttributes, int audioSessionId)1535     private AudioTrack createAudioTrackV21(
1536         boolean tunneling, AudioAttributes audioAttributes, int audioSessionId) {
1537       android.media.AudioAttributes attributes;
1538       if (tunneling) {
1539         attributes =
1540             new android.media.AudioAttributes.Builder()
1541                 .setContentType(android.media.AudioAttributes.CONTENT_TYPE_MOVIE)
1542                 .setFlags(android.media.AudioAttributes.FLAG_HW_AV_SYNC)
1543                 .setUsage(android.media.AudioAttributes.USAGE_MEDIA)
1544                 .build();
1545       } else {
1546         attributes = audioAttributes.getAudioAttributesV21();
1547       }
1548       AudioFormat format =
1549           new AudioFormat.Builder()
1550               .setChannelMask(outputChannelConfig)
1551               .setEncoding(outputEncoding)
1552               .setSampleRate(outputSampleRate)
1553               .build();
1554       return new AudioTrack(
1555           attributes,
1556           format,
1557           bufferSize,
1558           MODE_STREAM,
1559           audioSessionId != C.AUDIO_SESSION_ID_UNSET
1560               ? audioSessionId
1561               : AudioManager.AUDIO_SESSION_ID_GENERATE);
1562     }
1563 
getDefaultBufferSize()1564     private int getDefaultBufferSize() {
1565       if (isInputPcm) {
1566         int minBufferSize =
1567             AudioTrack.getMinBufferSize(outputSampleRate, outputChannelConfig, outputEncoding);
1568         Assertions.checkState(minBufferSize != ERROR_BAD_VALUE);
1569         int multipliedBufferSize = minBufferSize * BUFFER_MULTIPLICATION_FACTOR;
1570         int minAppBufferSize =
1571             (int) durationUsToFrames(MIN_BUFFER_DURATION_US) * outputPcmFrameSize;
1572         int maxAppBufferSize =
1573             (int)
1574                 Math.max(
1575                     minBufferSize, durationUsToFrames(MAX_BUFFER_DURATION_US) * outputPcmFrameSize);
1576         return Util.constrainValue(multipliedBufferSize, minAppBufferSize, maxAppBufferSize);
1577       } else {
1578         int rate = getMaximumEncodedRateBytesPerSecond(outputEncoding);
1579         if (outputEncoding == C.ENCODING_AC3) {
1580           rate *= AC3_BUFFER_MULTIPLICATION_FACTOR;
1581         }
1582         return (int) (PASSTHROUGH_BUFFER_DURATION_US * rate / C.MICROS_PER_SECOND);
1583       }
1584     }
1585   }
1586 }
1587