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.
|
* A sink that consumes audio data.
|
||||||
* <p>
|
*
|
||||||
* Before starting playback, specify the input audio format by calling
|
* <p>Before starting playback, specify the input audio format by calling {@link #configure(int,
|
||||||
* {@link #configure(int, int, int, int, int[], int, int)}.
|
* int, int, int, int[], int, int)}.
|
||||||
* <p>
|
*
|
||||||
* Call {@link #handleBuffer(ByteBuffer, long)} to write data, and {@link #handleDiscontinuity()}
|
* <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.
|
* 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.
|
* <p>Call {@link #configure(int, int, int, int, int[], int, int)} whenever the input format
|
||||||
* The sink will be reinitialized on the next call to {@link #handleBuffer(ByteBuffer, long)}.
|
* changes. The sink will be reinitialized on the next call to {@link #handleBuffer(ByteBuffer,
|
||||||
* <p>
|
* long)}.
|
||||||
* Call {@link #reset()} to prepare the sink to receive audio data from a new playback position.
|
*
|
||||||
* <p>
|
* <p>Call {@link #flush()} to prepare the sink to receive audio data from a new playback position.
|
||||||
* 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
|
* <p>Call {@link #playToEndOfStream()} repeatedly to play out all data when no more input buffers
|
||||||
* {@link #release()} when the instance is no longer required.
|
* will be provided via {@link #handleBuffer(ByteBuffer, long)} until the next {@link #flush()}.
|
||||||
* <p>
|
* Call {@link #reset()} when the instance is no longer required.
|
||||||
* The implementation may be backed by a platform {@link AudioTrack}. In this case,
|
*
|
||||||
* {@link #setAudioSessionId(int)}, {@link #setAudioAttributes(AudioAttributes)},
|
* <p>The implementation may be backed by a platform {@link AudioTrack}. In this case, {@link
|
||||||
* {@link #enableTunnelingV21(int)} and/or {@link #disableTunneling()} may be called before writing
|
* #setAudioSessionId(int)}, {@link #setAudioAttributes(AudioAttributes)}, {@link
|
||||||
* data to the sink. These methods may also be called after writing data to the sink, in which case
|
* #enableTunnelingV21(int)} and/or {@link #disableTunneling()} may be called before writing data to
|
||||||
* it will be reinitialized as required. For implementations that are not based on platform
|
* the sink. These methods may also be called after writing data to the sink, in which case it will
|
||||||
* {@link AudioTrack}s, calling methods relating to audio sessions, audio attributes, and tunneling
|
* be reinitialized as required. For implementations that are not based on platform {@link
|
||||||
* may have no effect.
|
* AudioTrack}s, calling methods relating to audio sessions, audio attributes, and tunneling may
|
||||||
|
* have no effect.
|
||||||
*/
|
*/
|
||||||
public interface AudioSink {
|
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
|
* @param trimStartFrames The number of audio frames to trim from the start of data written to the
|
||||||
* sink after this call.
|
* sink after this call.
|
||||||
* @param trimEndFrames The number of audio frames to trim from data written to the sink
|
* @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.
|
* @throws ConfigurationException If an error occurs configuring the sink.
|
||||||
*/
|
*/
|
||||||
void configure(
|
void configure(
|
||||||
@ -223,11 +224,11 @@ public interface AudioSink {
|
|||||||
* ending at its limit (exclusive). The position of the {@link ByteBuffer} is advanced by the
|
* 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
|
* 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.
|
* {@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
|
* <p>Returns whether the data was handled in full. If the data was not handled in full then the
|
||||||
* {@link ByteBuffer} must be provided to subsequent calls until it has been fully consumed,
|
* 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
|
* except in the case of an intervening call to {@link #flush()} (or to {@link #configure(int,
|
||||||
* {@link #configure(int, int, int, int, int[], int, int)} that causes the sink to be reset).
|
* int, int, int, int[], int, int)} that causes the sink to be flushed).
|
||||||
*
|
*
|
||||||
* @param buffer The buffer containing audio data.
|
* @param buffer The buffer containing audio data.
|
||||||
* @param presentationTimeUs The presentation timestamp of the buffer in microseconds.
|
* @param presentationTimeUs The presentation timestamp of the buffer in microseconds.
|
||||||
@ -316,15 +317,12 @@ public interface AudioSink {
|
|||||||
void pause();
|
void pause();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resets the sink, after which it is ready to receive buffers from a new playback position.
|
* 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 #release()} is called.
|
* <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();
|
void reset();
|
||||||
|
|
||||||
/**
|
|
||||||
* Releases any resources associated with this instance.
|
|
||||||
*/
|
|
||||||
void release();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -463,7 +463,7 @@ public final class DefaultAudioSink implements AudioSink {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
reset();
|
flush();
|
||||||
|
|
||||||
this.processingEnabled = processingEnabled;
|
this.processingEnabled = processingEnabled;
|
||||||
outputSampleRate = sampleRate;
|
outputSampleRate = sampleRate;
|
||||||
@ -681,7 +681,7 @@ public final class DefaultAudioSink implements AudioSink {
|
|||||||
|
|
||||||
if (audioTrackPositionTracker.isStalled(getWrittenFrames())) {
|
if (audioTrackPositionTracker.isStalled(getWrittenFrames())) {
|
||||||
Log.w(TAG, "Resetting stalled audio track");
|
Log.w(TAG, "Resetting stalled audio track");
|
||||||
reset();
|
flush();
|
||||||
return true;
|
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.
|
// The audio attributes are ignored in tunneling mode, so no need to reset.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
reset();
|
flush();
|
||||||
audioSessionId = C.AUDIO_SESSION_ID_UNSET;
|
audioSessionId = C.AUDIO_SESSION_ID_UNSET;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -879,7 +879,7 @@ public final class DefaultAudioSink implements AudioSink {
|
|||||||
public void setAudioSessionId(int audioSessionId) {
|
public void setAudioSessionId(int audioSessionId) {
|
||||||
if (this.audioSessionId != audioSessionId) {
|
if (this.audioSessionId != audioSessionId) {
|
||||||
this.audioSessionId = audioSessionId;
|
this.audioSessionId = audioSessionId;
|
||||||
reset();
|
flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -907,7 +907,7 @@ public final class DefaultAudioSink implements AudioSink {
|
|||||||
if (!tunneling || audioSessionId != tunnelingAudioSessionId) {
|
if (!tunneling || audioSessionId != tunnelingAudioSessionId) {
|
||||||
tunneling = true;
|
tunneling = true;
|
||||||
audioSessionId = tunnelingAudioSessionId;
|
audioSessionId = tunnelingAudioSessionId;
|
||||||
reset();
|
flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -916,7 +916,7 @@ public final class DefaultAudioSink implements AudioSink {
|
|||||||
if (tunneling) {
|
if (tunneling) {
|
||||||
tunneling = false;
|
tunneling = false;
|
||||||
audioSessionId = C.AUDIO_SESSION_ID_UNSET;
|
audioSessionId = C.AUDIO_SESSION_ID_UNSET;
|
||||||
reset();
|
flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -947,7 +947,7 @@ public final class DefaultAudioSink implements AudioSink {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reset() {
|
public void flush() {
|
||||||
if (isInitialized()) {
|
if (isInitialized()) {
|
||||||
submittedPcmBytes = 0;
|
submittedPcmBytes = 0;
|
||||||
submittedEncodedFrames = 0;
|
submittedEncodedFrames = 0;
|
||||||
@ -995,8 +995,8 @@ public final class DefaultAudioSink implements AudioSink {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void release() {
|
public void reset() {
|
||||||
reset();
|
flush();
|
||||||
releaseKeepSessionIdAudioTrack();
|
releaseKeepSessionIdAudioTrack();
|
||||||
for (AudioProcessor audioProcessor : toIntPcmAvailableAudioProcessors) {
|
for (AudioProcessor audioProcessor : toIntPcmAvailableAudioProcessors) {
|
||||||
audioProcessor.reset();
|
audioProcessor.reset();
|
||||||
|
@ -511,7 +511,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
|||||||
@Override
|
@Override
|
||||||
protected void onPositionReset(long positionUs, boolean joining) throws ExoPlaybackException {
|
protected void onPositionReset(long positionUs, boolean joining) throws ExoPlaybackException {
|
||||||
super.onPositionReset(positionUs, joining);
|
super.onPositionReset(positionUs, joining);
|
||||||
audioSink.reset();
|
audioSink.flush();
|
||||||
currentPositionUs = positionUs;
|
currentPositionUs = positionUs;
|
||||||
allowFirstBufferPositionDiscontinuity = true;
|
allowFirstBufferPositionDiscontinuity = true;
|
||||||
allowPositionDiscontinuity = true;
|
allowPositionDiscontinuity = true;
|
||||||
@ -537,7 +537,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
|||||||
try {
|
try {
|
||||||
lastInputTimeUs = C.TIME_UNSET;
|
lastInputTimeUs = C.TIME_UNSET;
|
||||||
pendingStreamChangeCount = 0;
|
pendingStreamChangeCount = 0;
|
||||||
audioSink.release();
|
audioSink.flush();
|
||||||
} finally {
|
} finally {
|
||||||
try {
|
try {
|
||||||
super.onDisabled();
|
super.onDisabled();
|
||||||
@ -548,6 +548,15 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onReset() {
|
||||||
|
try {
|
||||||
|
super.onReset();
|
||||||
|
} finally {
|
||||||
|
audioSink.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEnded() {
|
public boolean isEnded() {
|
||||||
return super.isEnded() && audioSink.isEnded();
|
return super.isEnded() && audioSink.isEnded();
|
||||||
|
@ -538,7 +538,7 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPositionReset(long positionUs, boolean joining) throws ExoPlaybackException {
|
protected void onPositionReset(long positionUs, boolean joining) throws ExoPlaybackException {
|
||||||
audioSink.reset();
|
audioSink.flush();
|
||||||
currentPositionUs = positionUs;
|
currentPositionUs = positionUs;
|
||||||
allowFirstBufferPositionDiscontinuity = true;
|
allowFirstBufferPositionDiscontinuity = true;
|
||||||
allowPositionDiscontinuity = true;
|
allowPositionDiscontinuity = true;
|
||||||
@ -567,7 +567,7 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements
|
|||||||
waitingForKeys = false;
|
waitingForKeys = false;
|
||||||
try {
|
try {
|
||||||
releaseDecoder();
|
releaseDecoder();
|
||||||
audioSink.release();
|
audioSink.reset();
|
||||||
} finally {
|
} finally {
|
||||||
try {
|
try {
|
||||||
if (drmSession != null) {
|
if (drmSession != null) {
|
||||||
|
@ -361,18 +361,13 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDisabled() {
|
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;
|
lastInputTimeUs = C.TIME_UNSET;
|
||||||
|
outputStreamOffsetUs = C.TIME_UNSET;
|
||||||
pendingOutputStreamOffsetCount = 0;
|
pendingOutputStreamOffsetCount = 0;
|
||||||
clearReportedVideoSize();
|
clearReportedVideoSize();
|
||||||
clearRenderedFirstFrame();
|
clearRenderedFirstFrame();
|
||||||
frameReleaseTimeHelper.disable();
|
frameReleaseTimeHelper.disable();
|
||||||
tunnelingOnFrameRenderedListener = null;
|
tunnelingOnFrameRenderedListener = null;
|
||||||
tunneling = false;
|
|
||||||
try {
|
try {
|
||||||
super.onDisabled();
|
super.onDisabled();
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -104,7 +104,7 @@ public class SimpleDecoderAudioRendererTest {
|
|||||||
verify(mockAudioSink, times(1)).playToEndOfStream();
|
verify(mockAudioSink, times(1)).playToEndOfStream();
|
||||||
audioRenderer.disable();
|
audioRenderer.disable();
|
||||||
audioRenderer.reset();
|
audioRenderer.reset();
|
||||||
verify(mockAudioSink, times(1)).release();
|
verify(mockAudioSink, times(1)).reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class FakeDecoder
|
private static final class FakeDecoder
|
||||||
|
Loading…
x
Reference in New Issue
Block a user