Reset video stream during pause
In the CTS tunnel mode flush test, we expect the first frame queued
after flush to get rendered, but because the video gets paused at an
arbitrary position, the first frame could not be an independent frame
(for instance, an I frame). In particular, it could depend from frames
that have been queued and flushed previously.
To work around this, we seek the video to beginning (because we assume
the first video frame is an I frame), and we account for the pts
discontinuity by introducing an offset.
Test: atest android.media.decoder.cts.DecoderTest
Bug: 215914266
Bug: 200280394
Change-Id: If088d5958facfd15172d6093c6f3b5db9ebcfceb
Merged-In: If088d5958facfd15172d6093c6f3b5db9ebcfceb
(cherry picked from commit 51a8639f4f0b38683aef26d8bd81a803c3dee0db)
diff --git a/tests/tests/media/common/src/android/media/cts/CodecState.java b/tests/tests/media/common/src/android/media/cts/CodecState.java
index 1c08259..e117c3b 100644
--- a/tests/tests/media/common/src/android/media/cts/CodecState.java
+++ b/tests/tests/media/common/src/android/media/cts/CodecState.java
@@ -70,6 +70,7 @@
private long mFirstSampleTimeUs;
private long mPlaybackStartTimeUs;
private long mLastPresentTimeUs;
+ private long mOffsetTimestampUs = 0;
private MediaCodec mCodec;
private MediaTimeProvider mMediaTimeProvider;
private MediaExtractor mExtractor;
@@ -194,7 +195,7 @@
public long getCurrentPositionUs() {
// Use decoded frame time when available, otherwise default to render time (typically, in
// tunnel mode).
- if (mDecodedFramePresentationTimeUs > 0) {
+ if (mDecodedFramePresentationTimeUs != UNINITIALIZED_TIMESTAMP) {
return mDecodedFramePresentationTimeUs;
} else {
return mRenderedVideoFramePresentationTimeUs;
@@ -364,7 +365,7 @@
sampleTime -= mFirstSampleTimeUs;
}
- mLastPresentTimeUs = mPlaybackStartTimeUs + sampleTime;
+ mLastPresentTimeUs = mPlaybackStartTimeUs + sampleTime + mOffsetTimestampUs;
if ((sampleFlags & MediaExtractor.SAMPLE_FLAG_ENCRYPTED) != 0) {
MediaCodec.CryptoInfo info = new MediaCodec.CryptoInfo();
@@ -655,4 +656,25 @@
}
return null;
}
+
+ /**
+ * Seek media extractor to the beginning of the configured track.
+ *
+ * @param shouldContinuePts a boolean that controls whether timestamps keep increasing
+ */
+ public void seekToBeginning(boolean shouldContinuePts) {
+ mExtractor.seekTo(UNINITIALIZED_TIMESTAMP, MediaExtractor.SEEK_TO_PREVIOUS_SYNC);
+ if (shouldContinuePts) {
+ if (mDecodedFramePresentationTimeUs != UNINITIALIZED_TIMESTAMP) {
+ mOffsetTimestampUs = mDecodedFramePresentationTimeUs;
+ return;
+ }
+ if (mRenderedVideoFramePresentationTimeUs != UNINITIALIZED_TIMESTAMP) {
+ mOffsetTimestampUs = mRenderedVideoFramePresentationTimeUs;
+ return;
+ }
+ } else {
+ mOffsetTimestampUs = 0;
+ }
+ }
}
diff --git a/tests/tests/media/common/src/android/media/cts/MediaCodecTunneledPlayer.java b/tests/tests/media/common/src/android/media/cts/MediaCodecTunneledPlayer.java
index ef371da..a602a13 100644
--- a/tests/tests/media/common/src/android/media/cts/MediaCodecTunneledPlayer.java
+++ b/tests/tests/media/common/src/android/media/cts/MediaCodecTunneledPlayer.java
@@ -41,9 +41,13 @@
public class MediaCodecTunneledPlayer implements MediaTimeProvider {
private static final String TAG = MediaCodecTunneledPlayer.class.getSimpleName();
+ /** State the player starts in, before configuration. */
private static final int STATE_IDLE = 1;
+ /** State of the player during initial configuration. */
private static final int STATE_PREPARING = 2;
+ /** State of the player during playback. */
private static final int STATE_PLAYING = 3;
+ /** State of the player when configured but not playing. */
private static final int STATE_PAUSED = 4;
private Boolean mThreadStarted = false;
@@ -398,12 +402,14 @@
/**
* Flushes all the video codecs when the player is in stand-by.
+ *
+ * @throws IllegalStateException if the player is not paused
*/
public void videoFlush() {
Log.d(TAG, "videoFlush");
synchronized (mState) {
- if (mState == STATE_PLAYING || mState == STATE_PREPARING) {
- return;
+ if (mState != STATE_PAUSED) {
+ throw new IllegalStateException("Expected STATE_PAUSED, got " + mState);
}
for (CodecState state : mVideoCodecStates.values()) {
@@ -412,6 +418,24 @@
}
}
+ /** Seek all video tracks to their very beginning.
+ *
+ * @param shouldContinuePts a boolean that controls whether timestamps keep increasing
+ * @throws IllegalStateException if the player is not paused
+ */
+ public void videoSeekToBeginning(boolean shouldContinuePts) {
+ Log.d(TAG, "videoSeekToBeginning");
+ synchronized (mState) {
+ if (mState != STATE_PAUSED) {
+ throw new IllegalStateException("Expected STATE_PAUSED, got " + mState);
+ }
+
+ for (CodecState state : mVideoCodecStates.values()) {
+ state.seekToBeginning(shouldContinuePts);
+ }
+ }
+ }
+
/**
* Enables or disables looping. Should be called after {@link #prepare()}.
*/
diff --git a/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderTest.java b/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderTest.java
index 1593193..5405b32 100644
--- a/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderTest.java
+++ b/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderTest.java
@@ -59,7 +59,6 @@
import android.media.cts.Preconditions;
import android.media.cts.SdkMediaCodec;
import android.net.Uri;
-import android.os.ParcelFileDescriptor;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
@@ -4348,9 +4347,13 @@
mMediaCodecPlayer.getCurrentPosition());
mMediaCodecPlayer.pause();
Thread.sleep(50);
- assertTrue("Video is ahead of audio", mMediaCodecPlayer.getCurrentPosition() <=
- mMediaCodecPlayer.getAudioTrackPositionUs());
+ final long audioPositionUs = mMediaCodecPlayer.getAudioTrackPositionUs();
+ final long videoPositionUs = mMediaCodecPlayer.getCurrentPosition();
+ assertTrue(String.format("Video pts (%d) is ahead of audio pts (%d)",
+ videoPositionUs, audioPositionUs),
+ videoPositionUs <= audioPositionUs);
mMediaCodecPlayer.videoFlush();
+ mMediaCodecPlayer.videoSeekToBeginning(true /* shouldContinuePts */);
Thread.sleep(50);
assertEquals("Video frame rendered after flush", CodecState.UNINITIALIZED_TIMESTAMP,
mMediaCodecPlayer.getCurrentPosition());