mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Rollback of 8a4168271d
*** Original commit *** Keep AudioTrack on flush as default *** PiperOrigin-RevId: 444327724
This commit is contained in:
parent
b816a3fe21
commit
f0b34b8f4c
@ -457,6 +457,18 @@ public interface AudioSink {
|
||||
*/
|
||||
void flush();
|
||||
|
||||
/**
|
||||
* Flushes the sink, after which it is ready to receive buffers from a new playback position.
|
||||
*
|
||||
* <p>Does not release the {@link AudioTrack} held by the sink.
|
||||
*
|
||||
* <p>This method is experimental, and will be renamed or removed in a future release.
|
||||
*
|
||||
* <p>Only for experimental use as part of {@link
|
||||
* MediaCodecAudioRenderer#experimentalSetEnableKeepAudioTrackOnSeek(boolean)}.
|
||||
*/
|
||||
void experimentalFlushWithoutAudioTrackRelease();
|
||||
|
||||
/** Resets the sink, releasing any resources that it currently holds. */
|
||||
void reset();
|
||||
}
|
||||
|
@ -129,6 +129,7 @@ public abstract class DecoderAudioRenderer<
|
||||
private int encoderDelay;
|
||||
private int encoderPadding;
|
||||
|
||||
private boolean experimentalKeepAudioTrackOnSeek;
|
||||
private boolean firstStreamSampleRead;
|
||||
|
||||
@Nullable private T decoder;
|
||||
@ -208,6 +209,19 @@ public abstract class DecoderAudioRenderer<
|
||||
audioSinkNeedsConfigure = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether to enable the experimental feature that keeps and flushes the {@link
|
||||
* android.media.AudioTrack} when a seek occurs, as opposed to releasing and reinitialising. Off
|
||||
* by default.
|
||||
*
|
||||
* <p>This method is experimental, and will be renamed or removed in a future release.
|
||||
*
|
||||
* @param enableKeepAudioTrackOnSeek Whether to keep the {@link android.media.AudioTrack} on seek.
|
||||
*/
|
||||
public void experimentalSetEnableKeepAudioTrackOnSeek(boolean enableKeepAudioTrackOnSeek) {
|
||||
this.experimentalKeepAudioTrackOnSeek = enableKeepAudioTrackOnSeek;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public MediaClock getMediaClock() {
|
||||
@ -541,7 +555,12 @@ public abstract class DecoderAudioRenderer<
|
||||
|
||||
@Override
|
||||
protected void onPositionReset(long positionUs, boolean joining) throws ExoPlaybackException {
|
||||
audioSink.flush();
|
||||
if (experimentalKeepAudioTrackOnSeek) {
|
||||
audioSink.experimentalFlushWithoutAudioTrackRelease();
|
||||
} else {
|
||||
audioSink.flush();
|
||||
}
|
||||
|
||||
currentPositionUs = positionUs;
|
||||
allowFirstBufferPositionDiscontinuity = true;
|
||||
allowPositionDiscontinuity = true;
|
||||
|
@ -907,7 +907,7 @@ public final class DefaultAudioSink implements AudioSink {
|
||||
// We're waiting for playout on the current audio track to finish.
|
||||
return false;
|
||||
}
|
||||
flushAndReleaseAudioTrack();
|
||||
flush();
|
||||
} else {
|
||||
// The current audio track can be reused for the new configuration.
|
||||
configuration = pendingConfiguration;
|
||||
@ -1034,7 +1034,7 @@ public final class DefaultAudioSink implements AudioSink {
|
||||
|
||||
if (audioTrackPositionTracker.isStalled(getWrittenFrames())) {
|
||||
Log.w(TAG, "Resetting stalled audio track");
|
||||
flushAndReleaseAudioTrack();
|
||||
flush();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1324,8 +1324,7 @@ public final class DefaultAudioSink implements AudioSink {
|
||||
// The audio attributes are ignored in tunneling mode, so no need to reset.
|
||||
return;
|
||||
}
|
||||
// audioAttributes change requires the audioTrack to be recreated.
|
||||
flushAndReleaseAudioTrack();
|
||||
flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1338,8 +1337,7 @@ public final class DefaultAudioSink implements AudioSink {
|
||||
if (this.audioSessionId != audioSessionId) {
|
||||
this.audioSessionId = audioSessionId;
|
||||
externalAudioSessionIdProvided = audioSessionId != C.AUDIO_SESSION_ID_UNSET;
|
||||
// audioSessionId change requires the audioTrack to be recreated.
|
||||
flushAndReleaseAudioTrack();
|
||||
flush();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1367,7 +1365,7 @@ public final class DefaultAudioSink implements AudioSink {
|
||||
Assertions.checkState(externalAudioSessionIdProvided);
|
||||
if (!tunneling) {
|
||||
tunneling = true;
|
||||
flushAndReleaseAudioTrack();
|
||||
flush();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1375,7 +1373,7 @@ public final class DefaultAudioSink implements AudioSink {
|
||||
public void disableTunneling() {
|
||||
if (tunneling) {
|
||||
tunneling = false;
|
||||
flushAndReleaseAudioTrack();
|
||||
flush();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1407,26 +1405,70 @@ public final class DefaultAudioSink implements AudioSink {
|
||||
|
||||
@Override
|
||||
public void flush() {
|
||||
if (!isAudioTrackInitialized()) {
|
||||
return;
|
||||
}
|
||||
if (isAudioTrackInitialized()) {
|
||||
resetSinkStateForFlush();
|
||||
|
||||
// Prior to SDK 25, AudioTrack flush does not work as intended, so it must be released and
|
||||
// reinitialized. (Internal reference: b/143500232)
|
||||
if (audioTrackPositionTracker.isPlaying()) {
|
||||
audioTrack.pause();
|
||||
}
|
||||
if (isOffloadedPlayback(audioTrack)) {
|
||||
checkNotNull(offloadStreamEventCallbackV29).unregister(audioTrack);
|
||||
}
|
||||
// AudioTrack.release can take some time, so we call it on a background thread.
|
||||
final AudioTrack toRelease = audioTrack;
|
||||
audioTrack = null;
|
||||
if (Util.SDK_INT < 21 && !externalAudioSessionIdProvided) {
|
||||
// Prior to API level 21, audio sessions are not kept alive once there are no components
|
||||
// associated with them. If we generated the session ID internally, the only component
|
||||
// associated with the session is the audio track that's being released, and therefore
|
||||
// the session will not be kept alive. As a result, we need to generate a new session when
|
||||
// we next create an audio track.
|
||||
audioSessionId = C.AUDIO_SESSION_ID_UNSET;
|
||||
}
|
||||
if (pendingConfiguration != null) {
|
||||
configuration = pendingConfiguration;
|
||||
pendingConfiguration = null;
|
||||
}
|
||||
audioTrackPositionTracker.reset();
|
||||
releasingConditionVariable.close();
|
||||
new Thread("ExoPlayer:AudioTrackReleaseThread") {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
toRelease.flush();
|
||||
toRelease.release();
|
||||
} finally {
|
||||
releasingConditionVariable.open();
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
writeExceptionPendingExceptionHolder.clear();
|
||||
initializationExceptionPendingExceptionHolder.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void experimentalFlushWithoutAudioTrackRelease() {
|
||||
// Prior to SDK 25, AudioTrack flush does not work as intended, and therefore it must be
|
||||
// released and reinitialized. (Internal reference: b/143500232)
|
||||
if (Util.SDK_INT < 25) {
|
||||
flushAndReleaseAudioTrack();
|
||||
flush();
|
||||
return;
|
||||
}
|
||||
|
||||
writeExceptionPendingExceptionHolder.clear();
|
||||
initializationExceptionPendingExceptionHolder.clear();
|
||||
|
||||
if (!isAudioTrackInitialized()) {
|
||||
return;
|
||||
}
|
||||
|
||||
resetSinkStateForFlush();
|
||||
if (audioTrackPositionTracker.isPlaying()) {
|
||||
audioTrack.pause();
|
||||
}
|
||||
|
||||
audioTrack.flush();
|
||||
|
||||
audioTrackPositionTracker.reset();
|
||||
audioTrackPositionTracker.setAudioTrack(
|
||||
audioTrack,
|
||||
@ -1434,12 +1476,13 @@ public final class DefaultAudioSink implements AudioSink {
|
||||
configuration.outputEncoding,
|
||||
configuration.outputPcmFrameSize,
|
||||
configuration.bufferSize);
|
||||
|
||||
startMediaTimeUsNeedsInit = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
flushAndReleaseAudioTrack();
|
||||
flush();
|
||||
for (AudioProcessor audioProcessor : toIntPcmAvailableAudioProcessors) {
|
||||
audioProcessor.reset();
|
||||
}
|
||||
@ -1452,51 +1495,6 @@ public final class DefaultAudioSink implements AudioSink {
|
||||
|
||||
// Internal methods.
|
||||
|
||||
private void flushAndReleaseAudioTrack() {
|
||||
if (!isAudioTrackInitialized()) {
|
||||
return;
|
||||
}
|
||||
|
||||
writeExceptionPendingExceptionHolder.clear();
|
||||
initializationExceptionPendingExceptionHolder.clear();
|
||||
|
||||
resetSinkStateForFlush();
|
||||
if (audioTrackPositionTracker.isPlaying()) {
|
||||
audioTrack.pause();
|
||||
}
|
||||
if (isOffloadedPlayback(audioTrack)) {
|
||||
checkNotNull(offloadStreamEventCallbackV29).unregister(audioTrack);
|
||||
}
|
||||
// AudioTrack.release can take some time, so we call it on a background thread.
|
||||
final AudioTrack toRelease = audioTrack;
|
||||
audioTrack = null;
|
||||
if (Util.SDK_INT < 21 && !externalAudioSessionIdProvided) {
|
||||
// Prior to API level 21, audio sessions are not kept alive once there are no components
|
||||
// associated with them. If we generated the session ID internally, the only component
|
||||
// associated with the session is the audio track that's being released, and therefore
|
||||
// the session will not be kept alive. As a result, we need to generate a new session when
|
||||
// we next create an audio track.
|
||||
audioSessionId = C.AUDIO_SESSION_ID_UNSET;
|
||||
}
|
||||
if (pendingConfiguration != null) {
|
||||
configuration = pendingConfiguration;
|
||||
pendingConfiguration = null;
|
||||
}
|
||||
audioTrackPositionTracker.reset();
|
||||
releasingConditionVariable.close();
|
||||
new Thread("ExoPlayer:AudioTrackReleaseThread") {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
toRelease.flush();
|
||||
toRelease.release();
|
||||
} finally {
|
||||
releasingConditionVariable.open();
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
|
||||
private void resetSinkStateForFlush() {
|
||||
submittedPcmBytes = 0;
|
||||
submittedEncodedFrames = 0;
|
||||
|
@ -163,6 +163,11 @@ public class ForwardingAudioSink implements AudioSink {
|
||||
sink.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void experimentalFlushWithoutAudioTrackRelease() {
|
||||
sink.experimentalFlushWithoutAudioTrackRelease();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
sink.reset();
|
||||
|
@ -109,6 +109,8 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
||||
private boolean allowPositionDiscontinuity;
|
||||
private boolean audioSinkNeedsConfigure;
|
||||
|
||||
private boolean experimentalKeepAudioTrackOnSeek;
|
||||
|
||||
@Nullable private WakeupListener wakeupListener;
|
||||
|
||||
/**
|
||||
@ -264,6 +266,19 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
||||
return TAG;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether to enable the experimental feature that keeps and flushes the {@link
|
||||
* android.media.AudioTrack} when a seek occurs, as opposed to releasing and reinitialising. Off
|
||||
* by default.
|
||||
*
|
||||
* <p>This method is experimental, and will be renamed or removed in a future release.
|
||||
*
|
||||
* @param enableKeepAudioTrackOnSeek Whether to keep the {@link android.media.AudioTrack} on seek.
|
||||
*/
|
||||
public void experimentalSetEnableKeepAudioTrackOnSeek(boolean enableKeepAudioTrackOnSeek) {
|
||||
this.experimentalKeepAudioTrackOnSeek = enableKeepAudioTrackOnSeek;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Capabilities int supportsFormat(MediaCodecSelector mediaCodecSelector, Format format)
|
||||
throws DecoderQueryException {
|
||||
@ -516,7 +531,11 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
||||
@Override
|
||||
protected void onPositionReset(long positionUs, boolean joining) throws ExoPlaybackException {
|
||||
super.onPositionReset(positionUs, joining);
|
||||
audioSink.flush();
|
||||
if (experimentalKeepAudioTrackOnSeek) {
|
||||
audioSink.experimentalFlushWithoutAudioTrackRelease();
|
||||
} else {
|
||||
audioSink.flush();
|
||||
}
|
||||
|
||||
currentPositionUs = positionUs;
|
||||
allowFirstBufferPositionDiscontinuity = true;
|
||||
|
@ -265,15 +265,15 @@ public final class DefaultAudioSinkTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleBuffer_afterFlush_doesntThrow() throws Exception {
|
||||
// This is demonstrating that no Exceptions are thrown as a result of handling a buffer after a
|
||||
// flush.
|
||||
public void handlesBufferAfterExperimentalFlush() throws Exception {
|
||||
// This is demonstrating that no Exceptions are thrown as a result of handling a buffer after an
|
||||
// experimental flush.
|
||||
configureDefaultAudioSink(CHANNEL_COUNT_STEREO);
|
||||
defaultAudioSink.handleBuffer(
|
||||
createDefaultSilenceBuffer(), /* presentationTimeUs= */ 0, /* encodedAccessUnitCount= */ 1);
|
||||
|
||||
// After the flush we can successfully queue more input.
|
||||
defaultAudioSink.flush();
|
||||
// After the experimental flush we can successfully queue more input.
|
||||
defaultAudioSink.experimentalFlushWithoutAudioTrackRelease();
|
||||
defaultAudioSink.handleBuffer(
|
||||
createDefaultSilenceBuffer(),
|
||||
/* presentationTimeUs= */ 5_000,
|
||||
@ -281,13 +281,13 @@ public final class DefaultAudioSinkTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getCurrentPosition_afterFlush_returnsUnset() throws Exception {
|
||||
public void getCurrentPosition_returnsUnset_afterExperimentalFlush() throws Exception {
|
||||
configureDefaultAudioSink(CHANNEL_COUNT_STEREO);
|
||||
defaultAudioSink.handleBuffer(
|
||||
createDefaultSilenceBuffer(),
|
||||
/* presentationTimeUs= */ 5 * C.MICROS_PER_SECOND,
|
||||
/* encodedAccessUnitCount= */ 1);
|
||||
defaultAudioSink.flush();
|
||||
defaultAudioSink.experimentalFlushWithoutAudioTrackRelease();
|
||||
assertThat(defaultAudioSink.getCurrentPositionUs(/* sourceEnded= */ false))
|
||||
.isEqualTo(CURRENT_POSITION_NOT_SET);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user