mirror of
https://github.com/samsonjs/media.git
synced 2026-03-31 10:25:48 +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
|
||||
([#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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
Loading…
Reference in a new issue