Play out remaining data on reconfiguration

Before this change we'd release the audio track and create a new one as soon
as audio processors had drained when reconfiguring.

Fix this behavior by stop()ing the AudioTrack to play out all written data.

Issue: #2446
PiperOrigin-RevId: 244812402
This commit is contained in:
andrewlewis 2019-04-23 09:18:03 +01:00 committed by Oliver Woodman
parent b3495dfe66
commit 1d766c4603
3 changed files with 37 additions and 25 deletions

View file

@ -4,6 +4,9 @@
* Display last frame when seeking to end of stream
([#2568](https://github.com/google/ExoPlayer/issues/2568)).
* Audio:
* Fix an issue where not all audio was played out when the configuration
for the underlying track was changing (e.g., at some period transitions).
* UI: Fix `PlayerView` incorrectly consuming touch events if no controller is
attached ([#6109](https://github.com/google/ExoPlayer/issues/6133)).
* CEA608: Fix repetition of special North American characters

View file

@ -272,6 +272,7 @@ public final class DefaultAudioSink implements AudioSink {
private int preV21OutputBufferOffset;
private int drainingAudioProcessorIndex;
private boolean handledEndOfStream;
private boolean stoppedAudioTrack;
private boolean playing;
private int audioSessionId;
@ -465,19 +466,15 @@ public final class DefaultAudioSink implements AudioSink {
processingEnabled,
canApplyPlaybackParameters,
availableAudioProcessors);
if (isInitialized()) {
if (!pendingConfiguration.canReuseAudioTrack(configuration)) {
// We need a new AudioTrack before we can handle more input. We should first stop() the
// track and wait for audio to play out (tracked by [Internal: b/33161961]), but for now we
// discard the audio track immediately.
flush();
} else if (flushAudioProcessors) {
// We don't need a new AudioTrack but audio processors need to be drained and flushed.
this.pendingConfiguration = pendingConfiguration;
return;
}
// If we have a pending configuration already, we always drain audio processors as the preceding
// configuration may have required it (even if this one doesn't).
boolean drainAudioProcessors = flushAudioProcessors || this.pendingConfiguration != null;
if (isInitialized()
&& (!pendingConfiguration.canReuseAudioTrack(configuration) || drainAudioProcessors)) {
this.pendingConfiguration = pendingConfiguration;
} else {
configuration = pendingConfiguration;
}
configuration = pendingConfiguration;
}
private void setupAudioProcessors() {
@ -579,12 +576,21 @@ public final class DefaultAudioSink implements AudioSink {
Assertions.checkArgument(inputBuffer == null || buffer == inputBuffer);
if (pendingConfiguration != null) {
// We are waiting for audio processors to drain before applying a the new configuration.
if (!drainAudioProcessorsToEndOfStream()) {
// There's still pending data in audio processors to write to the track.
return false;
} else if (!pendingConfiguration.canReuseAudioTrack(configuration)) {
playPendingData();
if (hasPendingData()) {
// We're waiting for playout on the current audio track to finish.
return false;
}
flush();
} else {
// The current audio track can be reused for the new configuration.
configuration = pendingConfiguration;
pendingConfiguration = null;
}
configuration = pendingConfiguration;
pendingConfiguration = null;
playbackParameters =
configuration.canApplyPlaybackParameters
? audioProcessorChain.applyPlaybackParameters(playbackParameters)
@ -786,15 +792,8 @@ public final class DefaultAudioSink implements AudioSink {
@Override
public void playToEndOfStream() throws WriteException {
if (handledEndOfStream || !isInitialized()) {
return;
}
if (drainAudioProcessorsToEndOfStream()) {
// The audio processors have drained, so drain the underlying audio track.
audioTrackPositionTracker.handleEndOfStream(getWrittenFrames());
audioTrack.stop();
bytesUntilNextAvSync = 0;
if (!handledEndOfStream && isInitialized() && drainAudioProcessorsToEndOfStream()) {
playPendingData();
handledEndOfStream = true;
}
}
@ -976,6 +975,7 @@ public final class DefaultAudioSink implements AudioSink {
flushAudioProcessors();
inputBuffer = null;
outputBuffer = null;
stoppedAudioTrack = false;
handledEndOfStream = false;
drainingAudioProcessorIndex = C.INDEX_UNSET;
avSyncHeader = null;
@ -1223,6 +1223,15 @@ public final class DefaultAudioSink implements AudioSink {
audioTrack.setStereoVolume(volume, volume);
}
private void playPendingData() {
if (!stoppedAudioTrack) {
stoppedAudioTrack = true;
audioTrackPositionTracker.handleEndOfStream(getWrittenFrames());
audioTrack.stop();
bytesUntilNextAvSync = 0;
}
}
/** Stores playback parameters with the position and media time at which they apply. */
private static final class PlaybackParametersCheckpoint {

View file

@ -40,7 +40,7 @@ android {
dependencies {
implementation project(modulePrefix + 'library-core')
implementation 'androidx.media:media:1.0.0'
implementation 'androidx.media:media:1.0.1'
implementation 'androidx.annotation:annotation:1.0.2'
compileOnly 'org.checkerframework:checker-qual:' + checkerframeworkVersion
testImplementation project(modulePrefix + 'testutils-robolectric')