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:
olly 2018-11-02 10:32:44 -07:00 committed by Oliver Woodman
parent 251c420767
commit c5db69d22c
6 changed files with 59 additions and 57 deletions

View File

@ -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();
}

View File

@ -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();

View File

@ -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();

View File

@ -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) {

View File

@ -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 {

View File

@ -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