mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
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:
parent
b3495dfe66
commit
1d766c4603
3 changed files with 37 additions and 25 deletions
|
|
@ -4,6 +4,9 @@
|
||||||
|
|
||||||
* Display last frame when seeking to end of stream
|
* Display last frame when seeking to end of stream
|
||||||
([#2568](https://github.com/google/ExoPlayer/issues/2568)).
|
([#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
|
* UI: Fix `PlayerView` incorrectly consuming touch events if no controller is
|
||||||
attached ([#6109](https://github.com/google/ExoPlayer/issues/6133)).
|
attached ([#6109](https://github.com/google/ExoPlayer/issues/6133)).
|
||||||
* CEA608: Fix repetition of special North American characters
|
* CEA608: Fix repetition of special North American characters
|
||||||
|
|
|
||||||
|
|
@ -272,6 +272,7 @@ public final class DefaultAudioSink implements AudioSink {
|
||||||
private int preV21OutputBufferOffset;
|
private int preV21OutputBufferOffset;
|
||||||
private int drainingAudioProcessorIndex;
|
private int drainingAudioProcessorIndex;
|
||||||
private boolean handledEndOfStream;
|
private boolean handledEndOfStream;
|
||||||
|
private boolean stoppedAudioTrack;
|
||||||
|
|
||||||
private boolean playing;
|
private boolean playing;
|
||||||
private int audioSessionId;
|
private int audioSessionId;
|
||||||
|
|
@ -465,19 +466,15 @@ public final class DefaultAudioSink implements AudioSink {
|
||||||
processingEnabled,
|
processingEnabled,
|
||||||
canApplyPlaybackParameters,
|
canApplyPlaybackParameters,
|
||||||
availableAudioProcessors);
|
availableAudioProcessors);
|
||||||
if (isInitialized()) {
|
// If we have a pending configuration already, we always drain audio processors as the preceding
|
||||||
if (!pendingConfiguration.canReuseAudioTrack(configuration)) {
|
// configuration may have required it (even if this one doesn't).
|
||||||
// We need a new AudioTrack before we can handle more input. We should first stop() the
|
boolean drainAudioProcessors = flushAudioProcessors || this.pendingConfiguration != null;
|
||||||
// track and wait for audio to play out (tracked by [Internal: b/33161961]), but for now we
|
if (isInitialized()
|
||||||
// discard the audio track immediately.
|
&& (!pendingConfiguration.canReuseAudioTrack(configuration) || drainAudioProcessors)) {
|
||||||
flush();
|
this.pendingConfiguration = pendingConfiguration;
|
||||||
} else if (flushAudioProcessors) {
|
} else {
|
||||||
// We don't need a new AudioTrack but audio processors need to be drained and flushed.
|
configuration = pendingConfiguration;
|
||||||
this.pendingConfiguration = pendingConfiguration;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
configuration = pendingConfiguration;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupAudioProcessors() {
|
private void setupAudioProcessors() {
|
||||||
|
|
@ -579,12 +576,21 @@ public final class DefaultAudioSink implements AudioSink {
|
||||||
Assertions.checkArgument(inputBuffer == null || buffer == inputBuffer);
|
Assertions.checkArgument(inputBuffer == null || buffer == inputBuffer);
|
||||||
|
|
||||||
if (pendingConfiguration != null) {
|
if (pendingConfiguration != null) {
|
||||||
// We are waiting for audio processors to drain before applying a the new configuration.
|
|
||||||
if (!drainAudioProcessorsToEndOfStream()) {
|
if (!drainAudioProcessorsToEndOfStream()) {
|
||||||
|
// There's still pending data in audio processors to write to the track.
|
||||||
return false;
|
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 =
|
playbackParameters =
|
||||||
configuration.canApplyPlaybackParameters
|
configuration.canApplyPlaybackParameters
|
||||||
? audioProcessorChain.applyPlaybackParameters(playbackParameters)
|
? audioProcessorChain.applyPlaybackParameters(playbackParameters)
|
||||||
|
|
@ -786,15 +792,8 @@ public final class DefaultAudioSink implements AudioSink {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void playToEndOfStream() throws WriteException {
|
public void playToEndOfStream() throws WriteException {
|
||||||
if (handledEndOfStream || !isInitialized()) {
|
if (!handledEndOfStream && isInitialized() && drainAudioProcessorsToEndOfStream()) {
|
||||||
return;
|
playPendingData();
|
||||||
}
|
|
||||||
|
|
||||||
if (drainAudioProcessorsToEndOfStream()) {
|
|
||||||
// The audio processors have drained, so drain the underlying audio track.
|
|
||||||
audioTrackPositionTracker.handleEndOfStream(getWrittenFrames());
|
|
||||||
audioTrack.stop();
|
|
||||||
bytesUntilNextAvSync = 0;
|
|
||||||
handledEndOfStream = true;
|
handledEndOfStream = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -976,6 +975,7 @@ public final class DefaultAudioSink implements AudioSink {
|
||||||
flushAudioProcessors();
|
flushAudioProcessors();
|
||||||
inputBuffer = null;
|
inputBuffer = null;
|
||||||
outputBuffer = null;
|
outputBuffer = null;
|
||||||
|
stoppedAudioTrack = false;
|
||||||
handledEndOfStream = false;
|
handledEndOfStream = false;
|
||||||
drainingAudioProcessorIndex = C.INDEX_UNSET;
|
drainingAudioProcessorIndex = C.INDEX_UNSET;
|
||||||
avSyncHeader = null;
|
avSyncHeader = null;
|
||||||
|
|
@ -1223,6 +1223,15 @@ public final class DefaultAudioSink implements AudioSink {
|
||||||
audioTrack.setStereoVolume(volume, volume);
|
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. */
|
/** Stores playback parameters with the position and media time at which they apply. */
|
||||||
private static final class PlaybackParametersCheckpoint {
|
private static final class PlaybackParametersCheckpoint {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ android {
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(modulePrefix + 'library-core')
|
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'
|
implementation 'androidx.annotation:annotation:1.0.2'
|
||||||
compileOnly 'org.checkerframework:checker-qual:' + checkerframeworkVersion
|
compileOnly 'org.checkerframework:checker-qual:' + checkerframeworkVersion
|
||||||
testImplementation project(modulePrefix + 'testutils-robolectric')
|
testImplementation project(modulePrefix + 'testutils-robolectric')
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue