Add missing AudioSink discontinuity for stream changes.

When the stream is changed in the audio renderer, the timestamps of the
samples can no longer be expected to match the calculations in the AudioSink.

This change tracks the samples at which the stream is changed and notifies the
AudioSink of the discontinuity.

Issue:#4559
Issue:#3829

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=212435859
This commit is contained in:
tonihei 2018-09-11 05:28:15 -07:00 committed by Oliver Woodman
parent a083c29f7e
commit 21c5b0bf67
4 changed files with 57 additions and 5 deletions

View File

@ -122,6 +122,8 @@
([#4661](https://github.com/google/ExoPlayer/issues/4661)). ([#4661](https://github.com/google/ExoPlayer/issues/4661)).
* Allow edit lists which do not start with a sync sample. * Allow edit lists which do not start with a sync sample.
([#4774](https://github.com/google/ExoPlayer/issues/4774)). ([#4774](https://github.com/google/ExoPlayer/issues/4774)).
* Fix issue with audio discontinuities at period transitions, e.g. when
looping ([#3829](https://github.com/google/ExoPlayer/issues/3829)).
* IMA extension: * IMA extension:
* Refine the previous fix for empty ad groups to avoid discarding ad breaks * Refine the previous fix for empty ad groups to avoid discarding ad breaks
unnecessarily ([#4030](https://github.com/google/ExoPlayer/issues/4030)), unnecessarily ([#4030](https://github.com/google/ExoPlayer/issues/4030)),

View File

@ -215,9 +215,7 @@ public interface AudioSink {
*/ */
void play(); void play();
/** /** Signals to the sink that the next buffer may be discontinuous with the previous buffer. */
* Signals to the sink that the next buffer is discontinuous with the previous buffer.
*/
void handleDiscontinuity(); void handleDiscontinuity();
/** /**

View File

@ -642,9 +642,10 @@ public final class DefaultAudioSink implements AudioSink {
if (startMediaTimeState == START_NEED_SYNC) { if (startMediaTimeState == START_NEED_SYNC) {
// Adjust startMediaTimeUs to be consistent with the current buffer's start time and the // Adjust startMediaTimeUs to be consistent with the current buffer's start time and the
// number of bytes submitted. // number of bytes submitted.
startMediaTimeUs += (presentationTimeUs - expectedPresentationTimeUs); long adjustmentUs = presentationTimeUs - expectedPresentationTimeUs;
startMediaTimeUs += adjustmentUs;
startMediaTimeState = START_IN_SYNC; startMediaTimeState = START_IN_SYNC;
if (listener != null) { if (listener != null && adjustmentUs != 0) {
listener.onPositionDiscontinuity(); listener.onPositionDiscontinuity();
} }
} }

View File

@ -25,6 +25,7 @@ import android.media.MediaFormat;
import android.media.audiofx.Virtualizer; import android.media.audiofx.Virtualizer;
import android.os.Handler; import android.os.Handler;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.util.Log;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.ExoPlayer;
@ -68,9 +69,19 @@ import java.util.List;
@TargetApi(16) @TargetApi(16)
public class MediaCodecAudioRenderer extends MediaCodecRenderer implements MediaClock { public class MediaCodecAudioRenderer extends MediaCodecRenderer implements MediaClock {
/**
* Maximum number of tracked pending stream change times. Generally there is zero or one pending
* stream change. We track more to allow for pending changes that have fewer samples than the
* codec latency.
*/
private static final int MAX_PENDING_STREAM_CHANGE_COUNT = 10;
private static final String TAG = "MediaCodecAudioRenderer";
private final Context context; private final Context context;
private final EventDispatcher eventDispatcher; private final EventDispatcher eventDispatcher;
private final AudioSink audioSink; private final AudioSink audioSink;
private final long[] pendingStreamChangeTimesUs;
private int codecMaxInputSize; private int codecMaxInputSize;
private boolean passthroughEnabled; private boolean passthroughEnabled;
@ -83,6 +94,8 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
private long currentPositionUs; private long currentPositionUs;
private boolean allowFirstBufferPositionDiscontinuity; private boolean allowFirstBufferPositionDiscontinuity;
private boolean allowPositionDiscontinuity; private boolean allowPositionDiscontinuity;
private long lastInputTimeUs;
private int pendingStreamChangeCount;
/** /**
* @param context A context. * @param context A context.
@ -241,6 +254,8 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
/* assumedMinimumCodecOperatingRate= */ 44100); /* assumedMinimumCodecOperatingRate= */ 44100);
this.context = context.getApplicationContext(); this.context = context.getApplicationContext();
this.audioSink = audioSink; this.audioSink = audioSink;
lastInputTimeUs = C.TIME_UNSET;
pendingStreamChangeTimesUs = new long[MAX_PENDING_STREAM_CHANGE_COUNT];
eventDispatcher = new EventDispatcher(eventHandler, eventListener); eventDispatcher = new EventDispatcher(eventHandler, eventListener);
audioSink.setListener(new AudioSinkListener()); audioSink.setListener(new AudioSinkListener());
} }
@ -469,6 +484,22 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
} }
} }
@Override
protected void onStreamChanged(Format[] formats, long offsetUs) throws ExoPlaybackException {
super.onStreamChanged(formats, offsetUs);
if (lastInputTimeUs != C.TIME_UNSET) {
if (pendingStreamChangeCount == pendingStreamChangeTimesUs.length) {
Log.w(
TAG,
"Too many stream changes, so dropping change at "
+ pendingStreamChangeTimesUs[pendingStreamChangeCount - 1]);
} else {
pendingStreamChangeCount++;
}
pendingStreamChangeTimesUs[pendingStreamChangeCount - 1] = lastInputTimeUs;
}
}
@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);
@ -476,6 +507,8 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
currentPositionUs = positionUs; currentPositionUs = positionUs;
allowFirstBufferPositionDiscontinuity = true; allowFirstBufferPositionDiscontinuity = true;
allowPositionDiscontinuity = true; allowPositionDiscontinuity = true;
lastInputTimeUs = C.TIME_UNSET;
pendingStreamChangeCount = 0;
} }
@Override @Override
@ -494,6 +527,8 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
@Override @Override
protected void onDisabled() { protected void onDisabled() {
try { try {
lastInputTimeUs = C.TIME_UNSET;
pendingStreamChangeCount = 0;
audioSink.release(); audioSink.release();
} finally { } finally {
try { try {
@ -544,6 +579,22 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
} }
allowFirstBufferPositionDiscontinuity = false; allowFirstBufferPositionDiscontinuity = false;
} }
lastInputTimeUs = Math.max(buffer.timeUs, lastInputTimeUs);
}
@Override
protected void onProcessedOutputBuffer(long presentationTimeUs) {
super.onProcessedOutputBuffer(presentationTimeUs);
while (pendingStreamChangeCount != 0 && presentationTimeUs >= pendingStreamChangeTimesUs[0]) {
audioSink.handleDiscontinuity();
pendingStreamChangeCount--;
System.arraycopy(
pendingStreamChangeTimesUs,
/* srcPos= */ 1,
pendingStreamChangeTimesUs,
/* destPos= */ 0,
pendingStreamChangeCount);
}
} }
@Override @Override