Fix further issues with decoder re-use
It's no longer guaranteed that onOutputFormatChanged will always be called when the renderer is re-enabled, since the codec may be kept and its output format may not change. Hence we need to avoid resetting state in onDisabled that we need when the renderer is enabled again. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=219821519
This commit is contained in:
parent
251c420767
commit
c5db69d22c
@ -23,29 +23,30 @@ import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* A sink that consumes audio data.
|
||||
* <p>
|
||||
* Before starting playback, specify the input audio format by calling
|
||||
* {@link #configure(int, int, int, int, int[], int, int)}.
|
||||
* <p>
|
||||
* Call {@link #handleBuffer(ByteBuffer, long)} to write data, and {@link #handleDiscontinuity()}
|
||||
*
|
||||
* <p>Before starting playback, specify the input audio format by calling {@link #configure(int,
|
||||
* int, int, int, int[], int, int)}.
|
||||
*
|
||||
* <p>Call {@link #handleBuffer(ByteBuffer, long)} to write data, and {@link #handleDiscontinuity()}
|
||||
* when the data being fed is discontinuous. Call {@link #play()} to start playing the written data.
|
||||
* <p>
|
||||
* Call {@link #configure(int, int, int, int, int[], int, int)} whenever the input format changes.
|
||||
* The sink will be reinitialized on the next call to {@link #handleBuffer(ByteBuffer, long)}.
|
||||
* <p>
|
||||
* Call {@link #reset()} to prepare the sink to receive audio data from a new playback position.
|
||||
* <p>
|
||||
* Call {@link #playToEndOfStream()} repeatedly to play out all data when no more input buffers will
|
||||
* be provided via {@link #handleBuffer(ByteBuffer, long)} until the next {@link #reset()}. Call
|
||||
* {@link #release()} when the instance is no longer required.
|
||||
* <p>
|
||||
* The implementation may be backed by a platform {@link AudioTrack}. In this case,
|
||||
* {@link #setAudioSessionId(int)}, {@link #setAudioAttributes(AudioAttributes)},
|
||||
* {@link #enableTunnelingV21(int)} and/or {@link #disableTunneling()} may be called before writing
|
||||
* data to the sink. These methods may also be called after writing data to the sink, in which case
|
||||
* it will be reinitialized as required. For implementations that are not based on platform
|
||||
* {@link AudioTrack}s, calling methods relating to audio sessions, audio attributes, and tunneling
|
||||
* may have no effect.
|
||||
*
|
||||
* <p>Call {@link #configure(int, int, int, int, int[], int, int)} whenever the input format
|
||||
* changes. The sink will be reinitialized on the next call to {@link #handleBuffer(ByteBuffer,
|
||||
* long)}.
|
||||
*
|
||||
* <p>Call {@link #flush()} to prepare the sink to receive audio data from a new playback position.
|
||||
*
|
||||
* <p>Call {@link #playToEndOfStream()} repeatedly to play out all data when no more input buffers
|
||||
* will be provided via {@link #handleBuffer(ByteBuffer, long)} until the next {@link #flush()}.
|
||||
* Call {@link #reset()} when the instance is no longer required.
|
||||
*
|
||||
* <p>The implementation may be backed by a platform {@link AudioTrack}. In this case, {@link
|
||||
* #setAudioSessionId(int)}, {@link #setAudioAttributes(AudioAttributes)}, {@link
|
||||
* #enableTunnelingV21(int)} and/or {@link #disableTunneling()} may be called before writing data to
|
||||
* the sink. These methods may also be called after writing data to the sink, in which case it will
|
||||
* be reinitialized as required. For implementations that are not based on platform {@link
|
||||
* AudioTrack}s, calling methods relating to audio sessions, audio attributes, and tunneling may
|
||||
* have no effect.
|
||||
*/
|
||||
public interface AudioSink {
|
||||
|
||||
@ -197,7 +198,7 @@ public interface AudioSink {
|
||||
* @param trimStartFrames The number of audio frames to trim from the start of data written to the
|
||||
* sink after this call.
|
||||
* @param trimEndFrames The number of audio frames to trim from data written to the sink
|
||||
* immediately preceding the next call to {@link #reset()} or this method.
|
||||
* immediately preceding the next call to {@link #flush()} or this method.
|
||||
* @throws ConfigurationException If an error occurs configuring the sink.
|
||||
*/
|
||||
void configure(
|
||||
@ -223,11 +224,11 @@ public interface AudioSink {
|
||||
* ending at its limit (exclusive). The position of the {@link ByteBuffer} is advanced by the
|
||||
* number of bytes that were handled. {@link Listener#onPositionDiscontinuity()} will be called if
|
||||
* {@code presentationTimeUs} is discontinuous with the last buffer handled since the last reset.
|
||||
* <p>
|
||||
* Returns whether the data was handled in full. If the data was not handled in full then the same
|
||||
* {@link ByteBuffer} must be provided to subsequent calls until it has been fully consumed,
|
||||
* except in the case of an intervening call to {@link #reset()} (or to
|
||||
* {@link #configure(int, int, int, int, int[], int, int)} that causes the sink to be reset).
|
||||
*
|
||||
* <p>Returns whether the data was handled in full. If the data was not handled in full then the
|
||||
* same {@link ByteBuffer} must be provided to subsequent calls until it has been fully consumed,
|
||||
* except in the case of an intervening call to {@link #flush()} (or to {@link #configure(int,
|
||||
* int, int, int, int[], int, int)} that causes the sink to be flushed).
|
||||
*
|
||||
* @param buffer The buffer containing audio data.
|
||||
* @param presentationTimeUs The presentation timestamp of the buffer in microseconds.
|
||||
@ -316,15 +317,12 @@ public interface AudioSink {
|
||||
void pause();
|
||||
|
||||
/**
|
||||
* Resets the sink, after which it is ready to receive buffers from a new playback position.
|
||||
* <p>
|
||||
* The audio session may remain active until {@link #release()} is called.
|
||||
* Flushes the sink, after which it is ready to receive buffers from a new playback position.
|
||||
*
|
||||
* <p>The audio session may remain active until {@link #reset()} is called.
|
||||
*/
|
||||
void flush();
|
||||
|
||||
/** Resets the renderer, releasing any resources that it currently holds. */
|
||||
void reset();
|
||||
|
||||
/**
|
||||
* Releases any resources associated with this instance.
|
||||
*/
|
||||
void release();
|
||||
|
||||
}
|
||||
|
@ -463,7 +463,7 @@ public final class DefaultAudioSink implements AudioSink {
|
||||
return;
|
||||
}
|
||||
|
||||
reset();
|
||||
flush();
|
||||
|
||||
this.processingEnabled = processingEnabled;
|
||||
outputSampleRate = sampleRate;
|
||||
@ -681,7 +681,7 @@ public final class DefaultAudioSink implements AudioSink {
|
||||
|
||||
if (audioTrackPositionTracker.isStalled(getWrittenFrames())) {
|
||||
Log.w(TAG, "Resetting stalled audio track");
|
||||
reset();
|
||||
flush();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -871,7 +871,7 @@ public final class DefaultAudioSink implements AudioSink {
|
||||
// The audio attributes are ignored in tunneling mode, so no need to reset.
|
||||
return;
|
||||
}
|
||||
reset();
|
||||
flush();
|
||||
audioSessionId = C.AUDIO_SESSION_ID_UNSET;
|
||||
}
|
||||
|
||||
@ -879,7 +879,7 @@ public final class DefaultAudioSink implements AudioSink {
|
||||
public void setAudioSessionId(int audioSessionId) {
|
||||
if (this.audioSessionId != audioSessionId) {
|
||||
this.audioSessionId = audioSessionId;
|
||||
reset();
|
||||
flush();
|
||||
}
|
||||
}
|
||||
|
||||
@ -907,7 +907,7 @@ public final class DefaultAudioSink implements AudioSink {
|
||||
if (!tunneling || audioSessionId != tunnelingAudioSessionId) {
|
||||
tunneling = true;
|
||||
audioSessionId = tunnelingAudioSessionId;
|
||||
reset();
|
||||
flush();
|
||||
}
|
||||
}
|
||||
|
||||
@ -916,7 +916,7 @@ public final class DefaultAudioSink implements AudioSink {
|
||||
if (tunneling) {
|
||||
tunneling = false;
|
||||
audioSessionId = C.AUDIO_SESSION_ID_UNSET;
|
||||
reset();
|
||||
flush();
|
||||
}
|
||||
}
|
||||
|
||||
@ -947,7 +947,7 @@ public final class DefaultAudioSink implements AudioSink {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
public void flush() {
|
||||
if (isInitialized()) {
|
||||
submittedPcmBytes = 0;
|
||||
submittedEncodedFrames = 0;
|
||||
@ -995,8 +995,8 @@ public final class DefaultAudioSink implements AudioSink {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release() {
|
||||
reset();
|
||||
public void reset() {
|
||||
flush();
|
||||
releaseKeepSessionIdAudioTrack();
|
||||
for (AudioProcessor audioProcessor : toIntPcmAvailableAudioProcessors) {
|
||||
audioProcessor.reset();
|
||||
|
@ -511,7 +511,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
||||
@Override
|
||||
protected void onPositionReset(long positionUs, boolean joining) throws ExoPlaybackException {
|
||||
super.onPositionReset(positionUs, joining);
|
||||
audioSink.reset();
|
||||
audioSink.flush();
|
||||
currentPositionUs = positionUs;
|
||||
allowFirstBufferPositionDiscontinuity = true;
|
||||
allowPositionDiscontinuity = true;
|
||||
@ -537,7 +537,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
||||
try {
|
||||
lastInputTimeUs = C.TIME_UNSET;
|
||||
pendingStreamChangeCount = 0;
|
||||
audioSink.release();
|
||||
audioSink.flush();
|
||||
} finally {
|
||||
try {
|
||||
super.onDisabled();
|
||||
@ -548,6 +548,15 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onReset() {
|
||||
try {
|
||||
super.onReset();
|
||||
} finally {
|
||||
audioSink.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnded() {
|
||||
return super.isEnded() && audioSink.isEnded();
|
||||
|
@ -538,7 +538,7 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements
|
||||
|
||||
@Override
|
||||
protected void onPositionReset(long positionUs, boolean joining) throws ExoPlaybackException {
|
||||
audioSink.reset();
|
||||
audioSink.flush();
|
||||
currentPositionUs = positionUs;
|
||||
allowFirstBufferPositionDiscontinuity = true;
|
||||
allowPositionDiscontinuity = true;
|
||||
@ -567,7 +567,7 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements
|
||||
waitingForKeys = false;
|
||||
try {
|
||||
releaseDecoder();
|
||||
audioSink.release();
|
||||
audioSink.reset();
|
||||
} finally {
|
||||
try {
|
||||
if (drmSession != null) {
|
||||
|
@ -361,18 +361,13 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
|
||||
@Override
|
||||
protected void onDisabled() {
|
||||
currentWidth = Format.NO_VALUE;
|
||||
currentHeight = Format.NO_VALUE;
|
||||
currentPixelWidthHeightRatio = Format.NO_VALUE;
|
||||
pendingPixelWidthHeightRatio = Format.NO_VALUE;
|
||||
outputStreamOffsetUs = C.TIME_UNSET;
|
||||
lastInputTimeUs = C.TIME_UNSET;
|
||||
outputStreamOffsetUs = C.TIME_UNSET;
|
||||
pendingOutputStreamOffsetCount = 0;
|
||||
clearReportedVideoSize();
|
||||
clearRenderedFirstFrame();
|
||||
frameReleaseTimeHelper.disable();
|
||||
tunnelingOnFrameRenderedListener = null;
|
||||
tunneling = false;
|
||||
try {
|
||||
super.onDisabled();
|
||||
} finally {
|
||||
|
@ -104,7 +104,7 @@ public class SimpleDecoderAudioRendererTest {
|
||||
verify(mockAudioSink, times(1)).playToEndOfStream();
|
||||
audioRenderer.disable();
|
||||
audioRenderer.reset();
|
||||
verify(mockAudioSink, times(1)).release();
|
||||
verify(mockAudioSink, times(1)).reset();
|
||||
}
|
||||
|
||||
private static final class FakeDecoder
|
||||
|
Loading…
x
Reference in New Issue
Block a user