mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Move decode-only and no surface logic inside VideoFrameReleaseControl
This brings the parts related to video frame release decision making in a single place and simplifies the calling side in MediaCodecVideoRenderer. PiperOrigin-RevId: 737941729 (cherry picked from commit 0e169ab1bea3a4cd9ff2772d77618c66b5262f3c)
This commit is contained in:
parent
0a284f4927
commit
e2017e35db
@ -129,7 +129,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isReady(boolean rendererOtherwiseReady) {
|
public boolean isReady(boolean rendererOtherwiseReady) {
|
||||||
return videoFrameReleaseControl.isReady(rendererOtherwiseReady);
|
return outputSurface == null || videoFrameReleaseControl.isReady(rendererOtherwiseReady);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1697,9 +1697,6 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// The frame release action should be retrieved for all frames (even the ones that will be
|
|
||||||
// skipped), because the release control estimates the content frame rate from frame timestamps
|
|
||||||
// and we want to have this information known as early as possible, especially during seeking.
|
|
||||||
@VideoFrameReleaseControl.FrameReleaseAction
|
@VideoFrameReleaseControl.FrameReleaseAction
|
||||||
int frameReleaseAction =
|
int frameReleaseAction =
|
||||||
videoFrameReleaseControl.getFrameReleaseAction(
|
videoFrameReleaseControl.getFrameReleaseAction(
|
||||||
@ -1707,31 +1704,9 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
|
|||||||
positionUs,
|
positionUs,
|
||||||
elapsedRealtimeUs,
|
elapsedRealtimeUs,
|
||||||
getOutputStreamStartPositionUs(),
|
getOutputStreamStartPositionUs(),
|
||||||
|
isDecodeOnlyBuffer,
|
||||||
isLastBuffer,
|
isLastBuffer,
|
||||||
videoFrameReleaseInfo);
|
videoFrameReleaseInfo);
|
||||||
|
|
||||||
if (frameReleaseAction == VideoFrameReleaseControl.FRAME_RELEASE_IGNORE) {
|
|
||||||
// The buffer is no longer valid and needs to be ignored.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip decode-only buffers, e.g. after seeking, immediately.
|
|
||||||
if (isDecodeOnlyBuffer && !isLastBuffer) {
|
|
||||||
skipOutputBuffer(codec, bufferIndex, presentationTimeUs);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We are not rendering on a surface, the renderer will wait until a surface is set.
|
|
||||||
if (displaySurface == null) {
|
|
||||||
// Skip frames in sync with playback, so we'll be at the right frame if a surface is set.
|
|
||||||
if (getState() == STATE_STARTED && videoFrameReleaseInfo.getEarlyUs() < 30_000) {
|
|
||||||
skipOutputBuffer(codec, bufferIndex, presentationTimeUs);
|
|
||||||
updateVideoFrameProcessingOffsetCounters(videoFrameReleaseInfo.getEarlyUs());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (frameReleaseAction) {
|
switch (frameReleaseAction) {
|
||||||
case VideoFrameReleaseControl.FRAME_RELEASE_IMMEDIATELY:
|
case VideoFrameReleaseControl.FRAME_RELEASE_IMMEDIATELY:
|
||||||
long releaseTimeNs = getClock().nanoTime();
|
long releaseTimeNs = getClock().nanoTime();
|
||||||
@ -1748,6 +1723,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
|
|||||||
updateVideoFrameProcessingOffsetCounters(videoFrameReleaseInfo.getEarlyUs());
|
updateVideoFrameProcessingOffsetCounters(videoFrameReleaseInfo.getEarlyUs());
|
||||||
return true;
|
return true;
|
||||||
case VideoFrameReleaseControl.FRAME_RELEASE_TRY_AGAIN_LATER:
|
case VideoFrameReleaseControl.FRAME_RELEASE_TRY_AGAIN_LATER:
|
||||||
|
case VideoFrameReleaseControl.FRAME_RELEASE_IGNORE:
|
||||||
return false;
|
return false;
|
||||||
case VideoFrameReleaseControl.FRAME_RELEASE_SCHEDULED:
|
case VideoFrameReleaseControl.FRAME_RELEASE_SCHEDULED:
|
||||||
releaseFrame(checkStateNotNull(codec), bufferIndex, presentationTimeUs, format);
|
releaseFrame(checkStateNotNull(codec), bufferIndex, presentationTimeUs, format);
|
||||||
|
@ -43,7 +43,7 @@ public final class VideoFrameReleaseControl {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The frame release action returned by {@link #getFrameReleaseAction(long, long, long, long,
|
* The frame release action returned by {@link #getFrameReleaseAction(long, long, long, long,
|
||||||
* boolean, FrameReleaseInfo)}.
|
* boolean, boolean, FrameReleaseInfo)}.
|
||||||
*
|
*
|
||||||
* <p>One of {@link #FRAME_RELEASE_IMMEDIATELY}, {@link #FRAME_RELEASE_SCHEDULED}, {@link
|
* <p>One of {@link #FRAME_RELEASE_IMMEDIATELY}, {@link #FRAME_RELEASE_SCHEDULED}, {@link
|
||||||
* #FRAME_RELEASE_DROP}, {@link #FRAME_RELEASE_IGNORE}, {@link ##FRAME_RELEASE_SKIP} or {@link
|
* #FRAME_RELEASE_DROP}, {@link #FRAME_RELEASE_IGNORE}, {@link ##FRAME_RELEASE_SKIP} or {@link
|
||||||
@ -180,14 +180,15 @@ public final class VideoFrameReleaseControl {
|
|||||||
private boolean joiningRenderNextFrameImmediately;
|
private boolean joiningRenderNextFrameImmediately;
|
||||||
private float playbackSpeed;
|
private float playbackSpeed;
|
||||||
private Clock clock;
|
private Clock clock;
|
||||||
|
private boolean hasOutputSurface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an instance.
|
* Creates an instance.
|
||||||
*
|
*
|
||||||
* @param applicationContext The application context.
|
* @param applicationContext The application context.
|
||||||
* @param frameTimingEvaluator The {@link FrameTimingEvaluator} that will assist in {@linkplain
|
* @param frameTimingEvaluator The {@link FrameTimingEvaluator} that will assist in {@linkplain
|
||||||
* #getFrameReleaseAction(long, long, long, long, boolean, FrameReleaseInfo) frame release
|
* #getFrameReleaseAction(long, long, long, long, boolean, boolean, FrameReleaseInfo) frame
|
||||||
* actions}.
|
* release actions}.
|
||||||
* @param allowedJoiningTimeMs The maximum duration in milliseconds for which the renderer can
|
* @param allowedJoiningTimeMs The maximum duration in milliseconds for which the renderer can
|
||||||
* attempt to seamlessly join an ongoing playback.
|
* attempt to seamlessly join an ongoing playback.
|
||||||
*/
|
*/
|
||||||
@ -240,6 +241,7 @@ public final class VideoFrameReleaseControl {
|
|||||||
|
|
||||||
/** Called when the display surface changed. */
|
/** Called when the display surface changed. */
|
||||||
public void setOutputSurface(@Nullable Surface outputSurface) {
|
public void setOutputSurface(@Nullable Surface outputSurface) {
|
||||||
|
hasOutputSurface = outputSurface != null;
|
||||||
frameReleaseHelper.onSurfaceChanged(outputSurface);
|
frameReleaseHelper.onSurfaceChanged(outputSurface);
|
||||||
lowerFirstFrameState(C.FIRST_FRAME_NOT_RENDERED);
|
lowerFirstFrameState(C.FIRST_FRAME_NOT_RENDERED);
|
||||||
}
|
}
|
||||||
@ -327,6 +329,8 @@ public final class VideoFrameReleaseControl {
|
|||||||
* @param elapsedRealtimeUs {@link android.os.SystemClock#elapsedRealtime()} in microseconds,
|
* @param elapsedRealtimeUs {@link android.os.SystemClock#elapsedRealtime()} in microseconds,
|
||||||
* taken approximately at the time the playback position was {@code positionUs}.
|
* taken approximately at the time the playback position was {@code positionUs}.
|
||||||
* @param outputStreamStartPositionUs The stream's start position, in microseconds.
|
* @param outputStreamStartPositionUs The stream's start position, in microseconds.
|
||||||
|
* @param isDecodeOnlyFrame Whether the frame is decode-only because its presentation time is
|
||||||
|
* before the intended start time.
|
||||||
* @param isLastFrame Whether the frame is known to contain the last frame of the current stream.
|
* @param isLastFrame Whether the frame is known to contain the last frame of the current stream.
|
||||||
* @param frameReleaseInfo A {@link FrameReleaseInfo} that will be filled with detailed data only
|
* @param frameReleaseInfo A {@link FrameReleaseInfo} that will be filled with detailed data only
|
||||||
* if the method returns {@link #FRAME_RELEASE_IMMEDIATELY} or {@link
|
* if the method returns {@link #FRAME_RELEASE_IMMEDIATELY} or {@link
|
||||||
@ -339,6 +343,7 @@ public final class VideoFrameReleaseControl {
|
|||||||
long positionUs,
|
long positionUs,
|
||||||
long elapsedRealtimeUs,
|
long elapsedRealtimeUs,
|
||||||
long outputStreamStartPositionUs,
|
long outputStreamStartPositionUs,
|
||||||
|
boolean isDecodeOnlyFrame,
|
||||||
boolean isLastFrame,
|
boolean isLastFrame,
|
||||||
FrameReleaseInfo frameReleaseInfo)
|
FrameReleaseInfo frameReleaseInfo)
|
||||||
throws ExoPlaybackException {
|
throws ExoPlaybackException {
|
||||||
@ -355,6 +360,23 @@ public final class VideoFrameReleaseControl {
|
|||||||
frameReleaseInfo.earlyUs =
|
frameReleaseInfo.earlyUs =
|
||||||
calculateEarlyTimeUs(positionUs, elapsedRealtimeUs, presentationTimeUs);
|
calculateEarlyTimeUs(positionUs, elapsedRealtimeUs, presentationTimeUs);
|
||||||
|
|
||||||
|
if (isDecodeOnlyFrame && !isLastFrame) {
|
||||||
|
return FRAME_RELEASE_SKIP;
|
||||||
|
}
|
||||||
|
if (!hasOutputSurface) {
|
||||||
|
// Skip frames in sync with playback, so we'll be at the right frame if a surface is set.
|
||||||
|
if (frameTimingEvaluator.shouldIgnoreFrame(
|
||||||
|
frameReleaseInfo.earlyUs,
|
||||||
|
positionUs,
|
||||||
|
elapsedRealtimeUs,
|
||||||
|
isLastFrame,
|
||||||
|
/* treatDroppedBuffersAsSkipped= */ true)) {
|
||||||
|
return FRAME_RELEASE_IGNORE;
|
||||||
|
}
|
||||||
|
return started && frameReleaseInfo.earlyUs < 30_000
|
||||||
|
? FRAME_RELEASE_SKIP
|
||||||
|
: FRAME_RELEASE_TRY_AGAIN_LATER;
|
||||||
|
}
|
||||||
if (shouldForceRelease(positionUs, frameReleaseInfo.earlyUs, outputStreamStartPositionUs)) {
|
if (shouldForceRelease(positionUs, frameReleaseInfo.earlyUs, outputStreamStartPositionUs)) {
|
||||||
return FRAME_RELEASE_IMMEDIATELY;
|
return FRAME_RELEASE_IMMEDIATELY;
|
||||||
}
|
}
|
||||||
|
@ -147,6 +147,7 @@ import androidx.media3.exoplayer.ExoPlaybackException;
|
|||||||
positionUs,
|
positionUs,
|
||||||
elapsedRealtimeUs,
|
elapsedRealtimeUs,
|
||||||
outputStreamStartPositionUs,
|
outputStreamStartPositionUs,
|
||||||
|
/* isDecodeOnlyFrame= */ false,
|
||||||
/* isLastFrame= */ false,
|
/* isLastFrame= */ false,
|
||||||
videoFrameReleaseInfo);
|
videoFrameReleaseInfo);
|
||||||
switch (frameReleaseAction) {
|
switch (frameReleaseAction) {
|
||||||
|
@ -17,16 +17,33 @@ package androidx.media3.exoplayer.video;
|
|||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import android.graphics.SurfaceTexture;
|
||||||
|
import android.view.Surface;
|
||||||
import androidx.media3.exoplayer.ExoPlaybackException;
|
import androidx.media3.exoplayer.ExoPlaybackException;
|
||||||
import androidx.media3.test.utils.FakeClock;
|
import androidx.media3.test.utils.FakeClock;
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
/** Unit tests for {@link VideoFrameReleaseControl}. */
|
/** Unit tests for {@link VideoFrameReleaseControl}. */
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class VideoFrameReleaseControlTest {
|
public class VideoFrameReleaseControlTest {
|
||||||
|
|
||||||
|
private Surface surface;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
surface = new Surface(new SurfaceTexture(/* texName= */ 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() {
|
||||||
|
surface.release();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void isReady_onNewInstance_returnsFalse() {
|
public void isReady_onNewInstance_returnsFalse() {
|
||||||
VideoFrameReleaseControl videoFrameReleaseControl = createVideoFrameReleaseControl();
|
VideoFrameReleaseControl videoFrameReleaseControl = createVideoFrameReleaseControl();
|
||||||
@ -152,6 +169,7 @@ public class VideoFrameReleaseControlTest {
|
|||||||
/* positionUs= */ 0,
|
/* positionUs= */ 0,
|
||||||
/* elapsedRealtimeUs= */ 0,
|
/* elapsedRealtimeUs= */ 0,
|
||||||
/* outputStreamStartPositionUs= */ 0,
|
/* outputStreamStartPositionUs= */ 0,
|
||||||
|
/* isDecodeOnlyFrame= */ false,
|
||||||
/* isLastFrame= */ false,
|
/* isLastFrame= */ false,
|
||||||
frameReleaseInfo))
|
frameReleaseInfo))
|
||||||
.isEqualTo(VideoFrameReleaseControl.FRAME_RELEASE_IMMEDIATELY);
|
.isEqualTo(VideoFrameReleaseControl.FRAME_RELEASE_IMMEDIATELY);
|
||||||
@ -171,6 +189,7 @@ public class VideoFrameReleaseControlTest {
|
|||||||
/* positionUs= */ 0,
|
/* positionUs= */ 0,
|
||||||
/* elapsedRealtimeUs= */ 0,
|
/* elapsedRealtimeUs= */ 0,
|
||||||
/* outputStreamStartPositionUs= */ 0,
|
/* outputStreamStartPositionUs= */ 0,
|
||||||
|
/* isDecodeOnlyFrame= */ false,
|
||||||
/* isLastFrame= */ false,
|
/* isLastFrame= */ false,
|
||||||
frameReleaseInfo))
|
frameReleaseInfo))
|
||||||
.isEqualTo(VideoFrameReleaseControl.FRAME_RELEASE_TRY_AGAIN_LATER);
|
.isEqualTo(VideoFrameReleaseControl.FRAME_RELEASE_TRY_AGAIN_LATER);
|
||||||
@ -195,6 +214,7 @@ public class VideoFrameReleaseControlTest {
|
|||||||
/* positionUs= */ 0,
|
/* positionUs= */ 0,
|
||||||
/* elapsedRealtimeUs= */ 0,
|
/* elapsedRealtimeUs= */ 0,
|
||||||
/* outputStreamStartPositionUs= */ 0,
|
/* outputStreamStartPositionUs= */ 0,
|
||||||
|
/* isDecodeOnlyFrame= */ false,
|
||||||
/* isLastFrame= */ false,
|
/* isLastFrame= */ false,
|
||||||
frameReleaseInfo))
|
frameReleaseInfo))
|
||||||
.isEqualTo(VideoFrameReleaseControl.FRAME_RELEASE_IMMEDIATELY);
|
.isEqualTo(VideoFrameReleaseControl.FRAME_RELEASE_IMMEDIATELY);
|
||||||
@ -217,6 +237,7 @@ public class VideoFrameReleaseControlTest {
|
|||||||
/* positionUs= */ 0,
|
/* positionUs= */ 0,
|
||||||
/* elapsedRealtimeUs= */ 0,
|
/* elapsedRealtimeUs= */ 0,
|
||||||
/* outputStreamStartPositionUs= */ 0,
|
/* outputStreamStartPositionUs= */ 0,
|
||||||
|
/* isDecodeOnlyFrame= */ false,
|
||||||
/* isLastFrame= */ false,
|
/* isLastFrame= */ false,
|
||||||
frameReleaseInfo))
|
frameReleaseInfo))
|
||||||
.isEqualTo(VideoFrameReleaseControl.FRAME_RELEASE_IMMEDIATELY);
|
.isEqualTo(VideoFrameReleaseControl.FRAME_RELEASE_IMMEDIATELY);
|
||||||
@ -229,6 +250,7 @@ public class VideoFrameReleaseControlTest {
|
|||||||
/* positionUs= */ 0,
|
/* positionUs= */ 0,
|
||||||
/* elapsedRealtimeUs= */ 0,
|
/* elapsedRealtimeUs= */ 0,
|
||||||
/* outputStreamStartPositionUs= */ 0,
|
/* outputStreamStartPositionUs= */ 0,
|
||||||
|
/* isDecodeOnlyFrame= */ false,
|
||||||
/* isLastFrame= */ false,
|
/* isLastFrame= */ false,
|
||||||
frameReleaseInfo))
|
frameReleaseInfo))
|
||||||
.isEqualTo(VideoFrameReleaseControl.FRAME_RELEASE_TRY_AGAIN_LATER);
|
.isEqualTo(VideoFrameReleaseControl.FRAME_RELEASE_TRY_AGAIN_LATER);
|
||||||
@ -252,6 +274,7 @@ public class VideoFrameReleaseControlTest {
|
|||||||
/* positionUs= */ 0,
|
/* positionUs= */ 0,
|
||||||
/* elapsedRealtimeUs= */ 0,
|
/* elapsedRealtimeUs= */ 0,
|
||||||
/* outputStreamStartPositionUs= */ 0,
|
/* outputStreamStartPositionUs= */ 0,
|
||||||
|
/* isDecodeOnlyFrame= */ false,
|
||||||
/* isLastFrame= */ false,
|
/* isLastFrame= */ false,
|
||||||
frameReleaseInfo))
|
frameReleaseInfo))
|
||||||
.isEqualTo(VideoFrameReleaseControl.FRAME_RELEASE_IMMEDIATELY);
|
.isEqualTo(VideoFrameReleaseControl.FRAME_RELEASE_IMMEDIATELY);
|
||||||
@ -264,6 +287,7 @@ public class VideoFrameReleaseControlTest {
|
|||||||
/* positionUs= */ 1,
|
/* positionUs= */ 1,
|
||||||
/* elapsedRealtimeUs= */ 1,
|
/* elapsedRealtimeUs= */ 1,
|
||||||
/* outputStreamStartPositionUs= */ 0,
|
/* outputStreamStartPositionUs= */ 0,
|
||||||
|
/* isDecodeOnlyFrame= */ false,
|
||||||
/* isLastFrame= */ false,
|
/* isLastFrame= */ false,
|
||||||
frameReleaseInfo))
|
frameReleaseInfo))
|
||||||
.isEqualTo(VideoFrameReleaseControl.FRAME_RELEASE_SCHEDULED);
|
.isEqualTo(VideoFrameReleaseControl.FRAME_RELEASE_SCHEDULED);
|
||||||
@ -288,6 +312,7 @@ public class VideoFrameReleaseControlTest {
|
|||||||
/* positionUs= */ 0,
|
/* positionUs= */ 0,
|
||||||
/* elapsedRealtimeUs= */ 0,
|
/* elapsedRealtimeUs= */ 0,
|
||||||
/* outputStreamStartPositionUs= */ 0,
|
/* outputStreamStartPositionUs= */ 0,
|
||||||
|
/* isDecodeOnlyFrame= */ false,
|
||||||
/* isLastFrame= */ false,
|
/* isLastFrame= */ false,
|
||||||
frameReleaseInfo))
|
frameReleaseInfo))
|
||||||
.isEqualTo(VideoFrameReleaseControl.FRAME_RELEASE_IMMEDIATELY);
|
.isEqualTo(VideoFrameReleaseControl.FRAME_RELEASE_IMMEDIATELY);
|
||||||
@ -301,6 +326,7 @@ public class VideoFrameReleaseControlTest {
|
|||||||
/* positionUs= */ 10_000,
|
/* positionUs= */ 10_000,
|
||||||
/* elapsedRealtimeUs= */ 10_000,
|
/* elapsedRealtimeUs= */ 10_000,
|
||||||
/* outputStreamStartPositionUs= */ 0,
|
/* outputStreamStartPositionUs= */ 0,
|
||||||
|
/* isDecodeOnlyFrame= */ false,
|
||||||
/* isLastFrame= */ false,
|
/* isLastFrame= */ false,
|
||||||
frameReleaseInfo))
|
frameReleaseInfo))
|
||||||
.isEqualTo(VideoFrameReleaseControl.FRAME_RELEASE_TRY_AGAIN_LATER);
|
.isEqualTo(VideoFrameReleaseControl.FRAME_RELEASE_TRY_AGAIN_LATER);
|
||||||
@ -319,6 +345,7 @@ public class VideoFrameReleaseControlTest {
|
|||||||
/* shouldDropFrame= */ true,
|
/* shouldDropFrame= */ true,
|
||||||
/* shouldIgnoreFrame= */ false),
|
/* shouldIgnoreFrame= */ false),
|
||||||
/* allowedJoiningTimeMs= */ 0);
|
/* allowedJoiningTimeMs= */ 0);
|
||||||
|
videoFrameReleaseControl.setOutputSurface(surface);
|
||||||
videoFrameReleaseControl.setClock(clock);
|
videoFrameReleaseControl.setClock(clock);
|
||||||
videoFrameReleaseControl.onEnabled(/* releaseFirstFrameBeforeStarted= */ true);
|
videoFrameReleaseControl.onEnabled(/* releaseFirstFrameBeforeStarted= */ true);
|
||||||
|
|
||||||
@ -331,6 +358,7 @@ public class VideoFrameReleaseControlTest {
|
|||||||
/* positionUs= */ 0,
|
/* positionUs= */ 0,
|
||||||
/* elapsedRealtimeUs= */ 0,
|
/* elapsedRealtimeUs= */ 0,
|
||||||
/* outputStreamStartPositionUs= */ 0,
|
/* outputStreamStartPositionUs= */ 0,
|
||||||
|
/* isDecodeOnlyFrame= */ false,
|
||||||
/* isLastFrame= */ false,
|
/* isLastFrame= */ false,
|
||||||
frameReleaseInfo))
|
frameReleaseInfo))
|
||||||
.isEqualTo(VideoFrameReleaseControl.FRAME_RELEASE_IMMEDIATELY);
|
.isEqualTo(VideoFrameReleaseControl.FRAME_RELEASE_IMMEDIATELY);
|
||||||
@ -344,6 +372,7 @@ public class VideoFrameReleaseControlTest {
|
|||||||
/* positionUs= */ 10_000,
|
/* positionUs= */ 10_000,
|
||||||
/* elapsedRealtimeUs= */ 0,
|
/* elapsedRealtimeUs= */ 0,
|
||||||
/* outputStreamStartPositionUs= */ 0,
|
/* outputStreamStartPositionUs= */ 0,
|
||||||
|
/* isDecodeOnlyFrame= */ false,
|
||||||
/* isLastFrame= */ false,
|
/* isLastFrame= */ false,
|
||||||
frameReleaseInfo))
|
frameReleaseInfo))
|
||||||
.isEqualTo(VideoFrameReleaseControl.FRAME_RELEASE_DROP);
|
.isEqualTo(VideoFrameReleaseControl.FRAME_RELEASE_DROP);
|
||||||
@ -364,6 +393,7 @@ public class VideoFrameReleaseControlTest {
|
|||||||
/* shouldDropFrame= */ true,
|
/* shouldDropFrame= */ true,
|
||||||
/* shouldIgnoreFrame= */ false),
|
/* shouldIgnoreFrame= */ false),
|
||||||
/* allowedJoiningTimeMs= */ 1234);
|
/* allowedJoiningTimeMs= */ 1234);
|
||||||
|
videoFrameReleaseControl.setOutputSurface(surface);
|
||||||
videoFrameReleaseControl.setClock(clock);
|
videoFrameReleaseControl.setClock(clock);
|
||||||
videoFrameReleaseControl.onEnabled(/* releaseFirstFrameBeforeStarted= */ true);
|
videoFrameReleaseControl.onEnabled(/* releaseFirstFrameBeforeStarted= */ true);
|
||||||
videoFrameReleaseControl.onStarted();
|
videoFrameReleaseControl.onStarted();
|
||||||
@ -378,6 +408,7 @@ public class VideoFrameReleaseControlTest {
|
|||||||
/* positionUs= */ 10_000,
|
/* positionUs= */ 10_000,
|
||||||
/* elapsedRealtimeUs= */ 0,
|
/* elapsedRealtimeUs= */ 0,
|
||||||
/* outputStreamStartPositionUs= */ 0,
|
/* outputStreamStartPositionUs= */ 0,
|
||||||
|
/* isDecodeOnlyFrame= */ false,
|
||||||
/* isLastFrame= */ false,
|
/* isLastFrame= */ false,
|
||||||
frameReleaseInfo))
|
frameReleaseInfo))
|
||||||
.isEqualTo(VideoFrameReleaseControl.FRAME_RELEASE_TRY_AGAIN_LATER);
|
.isEqualTo(VideoFrameReleaseControl.FRAME_RELEASE_TRY_AGAIN_LATER);
|
||||||
@ -388,6 +419,7 @@ public class VideoFrameReleaseControlTest {
|
|||||||
/* positionUs= */ 11_000,
|
/* positionUs= */ 11_000,
|
||||||
/* elapsedRealtimeUs= */ 0,
|
/* elapsedRealtimeUs= */ 0,
|
||||||
/* outputStreamStartPositionUs= */ 0,
|
/* outputStreamStartPositionUs= */ 0,
|
||||||
|
/* isDecodeOnlyFrame= */ false,
|
||||||
/* isLastFrame= */ false,
|
/* isLastFrame= */ false,
|
||||||
frameReleaseInfo))
|
frameReleaseInfo))
|
||||||
.isEqualTo(VideoFrameReleaseControl.FRAME_RELEASE_SKIP);
|
.isEqualTo(VideoFrameReleaseControl.FRAME_RELEASE_SKIP);
|
||||||
@ -408,6 +440,7 @@ public class VideoFrameReleaseControlTest {
|
|||||||
/* shouldDropFrame= */ true,
|
/* shouldDropFrame= */ true,
|
||||||
/* shouldIgnoreFrame= */ false),
|
/* shouldIgnoreFrame= */ false),
|
||||||
/* allowedJoiningTimeMs= */ 1234);
|
/* allowedJoiningTimeMs= */ 1234);
|
||||||
|
videoFrameReleaseControl.setOutputSurface(surface);
|
||||||
videoFrameReleaseControl.setClock(clock);
|
videoFrameReleaseControl.setClock(clock);
|
||||||
videoFrameReleaseControl.onEnabled(/* releaseFirstFrameBeforeStarted= */ true);
|
videoFrameReleaseControl.onEnabled(/* releaseFirstFrameBeforeStarted= */ true);
|
||||||
videoFrameReleaseControl.onStarted();
|
videoFrameReleaseControl.onStarted();
|
||||||
@ -422,6 +455,7 @@ public class VideoFrameReleaseControlTest {
|
|||||||
/* positionUs= */ 10_000,
|
/* positionUs= */ 10_000,
|
||||||
/* elapsedRealtimeUs= */ 0,
|
/* elapsedRealtimeUs= */ 0,
|
||||||
/* outputStreamStartPositionUs= */ 0,
|
/* outputStreamStartPositionUs= */ 0,
|
||||||
|
/* isDecodeOnlyFrame= */ false,
|
||||||
/* isLastFrame= */ false,
|
/* isLastFrame= */ false,
|
||||||
frameReleaseInfo))
|
frameReleaseInfo))
|
||||||
.isEqualTo(VideoFrameReleaseControl.FRAME_RELEASE_IMMEDIATELY);
|
.isEqualTo(VideoFrameReleaseControl.FRAME_RELEASE_IMMEDIATELY);
|
||||||
@ -433,6 +467,7 @@ public class VideoFrameReleaseControlTest {
|
|||||||
/* positionUs= */ 11_000,
|
/* positionUs= */ 11_000,
|
||||||
/* elapsedRealtimeUs= */ 0,
|
/* elapsedRealtimeUs= */ 0,
|
||||||
/* outputStreamStartPositionUs= */ 0,
|
/* outputStreamStartPositionUs= */ 0,
|
||||||
|
/* isDecodeOnlyFrame= */ false,
|
||||||
/* isLastFrame= */ false,
|
/* isLastFrame= */ false,
|
||||||
frameReleaseInfo))
|
frameReleaseInfo))
|
||||||
.isEqualTo(VideoFrameReleaseControl.FRAME_RELEASE_DROP);
|
.isEqualTo(VideoFrameReleaseControl.FRAME_RELEASE_DROP);
|
||||||
@ -451,6 +486,7 @@ public class VideoFrameReleaseControlTest {
|
|||||||
/* shouldDropFrame= */ false,
|
/* shouldDropFrame= */ false,
|
||||||
/* shouldIgnoreFrame= */ true),
|
/* shouldIgnoreFrame= */ true),
|
||||||
/* allowedJoiningTimeMs= */ 0);
|
/* allowedJoiningTimeMs= */ 0);
|
||||||
|
videoFrameReleaseControl.setOutputSurface(surface);
|
||||||
videoFrameReleaseControl.setClock(clock);
|
videoFrameReleaseControl.setClock(clock);
|
||||||
videoFrameReleaseControl.onEnabled(/* releaseFirstFrameBeforeStarted= */ true);
|
videoFrameReleaseControl.onEnabled(/* releaseFirstFrameBeforeStarted= */ true);
|
||||||
|
|
||||||
@ -463,6 +499,7 @@ public class VideoFrameReleaseControlTest {
|
|||||||
/* positionUs= */ 0,
|
/* positionUs= */ 0,
|
||||||
/* elapsedRealtimeUs= */ 0,
|
/* elapsedRealtimeUs= */ 0,
|
||||||
/* outputStreamStartPositionUs= */ 0,
|
/* outputStreamStartPositionUs= */ 0,
|
||||||
|
/* isDecodeOnlyFrame= */ false,
|
||||||
/* isLastFrame= */ false,
|
/* isLastFrame= */ false,
|
||||||
frameReleaseInfo))
|
frameReleaseInfo))
|
||||||
.isEqualTo(VideoFrameReleaseControl.FRAME_RELEASE_IMMEDIATELY);
|
.isEqualTo(VideoFrameReleaseControl.FRAME_RELEASE_IMMEDIATELY);
|
||||||
@ -475,21 +512,181 @@ public class VideoFrameReleaseControlTest {
|
|||||||
/* positionUs= */ 1_000,
|
/* positionUs= */ 1_000,
|
||||||
/* elapsedRealtimeUs= */ 0,
|
/* elapsedRealtimeUs= */ 0,
|
||||||
/* outputStreamStartPositionUs= */ 0,
|
/* outputStreamStartPositionUs= */ 0,
|
||||||
|
/* isDecodeOnlyFrame= */ false,
|
||||||
/* isLastFrame= */ false,
|
/* isLastFrame= */ false,
|
||||||
frameReleaseInfo))
|
frameReleaseInfo))
|
||||||
.isEqualTo(VideoFrameReleaseControl.FRAME_RELEASE_IGNORE);
|
.isEqualTo(VideoFrameReleaseControl.FRAME_RELEASE_IGNORE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static VideoFrameReleaseControl createVideoFrameReleaseControl() {
|
@Test
|
||||||
|
public void getFrameReleaseAction_decodeOnlyFrame_returnsSkip() throws Exception {
|
||||||
|
VideoFrameReleaseControl.FrameReleaseInfo frameReleaseInfo =
|
||||||
|
new VideoFrameReleaseControl.FrameReleaseInfo();
|
||||||
|
VideoFrameReleaseControl videoFrameReleaseControl = createVideoFrameReleaseControl();
|
||||||
|
videoFrameReleaseControl.onEnabled(/* releaseFirstFrameBeforeStarted= */ true);
|
||||||
|
|
||||||
|
assertThat(
|
||||||
|
videoFrameReleaseControl.getFrameReleaseAction(
|
||||||
|
/* presentationTimeUs= */ 0,
|
||||||
|
/* positionUs= */ 0,
|
||||||
|
/* elapsedRealtimeUs= */ 0,
|
||||||
|
/* outputStreamStartPositionUs= */ 0,
|
||||||
|
/* isDecodeOnlyFrame= */ true,
|
||||||
|
/* isLastFrame= */ false,
|
||||||
|
frameReleaseInfo))
|
||||||
|
.isEqualTo(VideoFrameReleaseControl.FRAME_RELEASE_SKIP);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getFrameReleaseAction_decodeOnlyAndLastFrame_returnsReleaseImmediately()
|
||||||
|
throws Exception {
|
||||||
|
VideoFrameReleaseControl.FrameReleaseInfo frameReleaseInfo =
|
||||||
|
new VideoFrameReleaseControl.FrameReleaseInfo();
|
||||||
|
VideoFrameReleaseControl videoFrameReleaseControl = createVideoFrameReleaseControl();
|
||||||
|
videoFrameReleaseControl.onEnabled(/* releaseFirstFrameBeforeStarted= */ true);
|
||||||
|
|
||||||
|
assertThat(
|
||||||
|
videoFrameReleaseControl.getFrameReleaseAction(
|
||||||
|
/* presentationTimeUs= */ 0,
|
||||||
|
/* positionUs= */ 0,
|
||||||
|
/* elapsedRealtimeUs= */ 0,
|
||||||
|
/* outputStreamStartPositionUs= */ 0,
|
||||||
|
/* isDecodeOnlyFrame= */ true,
|
||||||
|
/* isLastFrame= */ true,
|
||||||
|
frameReleaseInfo))
|
||||||
|
.isEqualTo(VideoFrameReleaseControl.FRAME_RELEASE_IMMEDIATELY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getFrameReleaseAction_decodeOnlyFrameWithoutSurface_returnsSkip() throws Exception {
|
||||||
|
VideoFrameReleaseControl.FrameReleaseInfo frameReleaseInfo =
|
||||||
|
new VideoFrameReleaseControl.FrameReleaseInfo();
|
||||||
|
VideoFrameReleaseControl videoFrameReleaseControl = createVideoFrameReleaseControl();
|
||||||
|
videoFrameReleaseControl.setOutputSurface(/* outputSurface= */ null);
|
||||||
|
videoFrameReleaseControl.onEnabled(/* releaseFirstFrameBeforeStarted= */ true);
|
||||||
|
|
||||||
|
assertThat(
|
||||||
|
videoFrameReleaseControl.getFrameReleaseAction(
|
||||||
|
/* presentationTimeUs= */ 0,
|
||||||
|
/* positionUs= */ 0,
|
||||||
|
/* elapsedRealtimeUs= */ 0,
|
||||||
|
/* outputStreamStartPositionUs= */ 0,
|
||||||
|
/* isDecodeOnlyFrame= */ true,
|
||||||
|
/* isLastFrame= */ false,
|
||||||
|
frameReleaseInfo))
|
||||||
|
.isEqualTo(VideoFrameReleaseControl.FRAME_RELEASE_SKIP);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getFrameReleaseAction_withoutSurfaceOnTime_returnsTryAgainLater() throws Exception {
|
||||||
|
VideoFrameReleaseControl.FrameReleaseInfo frameReleaseInfo =
|
||||||
|
new VideoFrameReleaseControl.FrameReleaseInfo();
|
||||||
|
FakeClock clock = new FakeClock(/* isAutoAdvancing= */ false);
|
||||||
|
VideoFrameReleaseControl videoFrameReleaseControl = createVideoFrameReleaseControl();
|
||||||
|
videoFrameReleaseControl.setOutputSurface(/* outputSurface= */ null);
|
||||||
|
videoFrameReleaseControl.setClock(clock);
|
||||||
|
videoFrameReleaseControl.onEnabled(/* releaseFirstFrameBeforeStarted= */ true);
|
||||||
|
|
||||||
|
assertThat(
|
||||||
|
videoFrameReleaseControl.getFrameReleaseAction(
|
||||||
|
/* presentationTimeUs= */ 100_000,
|
||||||
|
/* positionUs= */ 50_000,
|
||||||
|
/* elapsedRealtimeUs= */ 0,
|
||||||
|
/* outputStreamStartPositionUs= */ 0,
|
||||||
|
/* isDecodeOnlyFrame= */ false,
|
||||||
|
/* isLastFrame= */ false,
|
||||||
|
frameReleaseInfo))
|
||||||
|
.isEqualTo(VideoFrameReleaseControl.FRAME_RELEASE_TRY_AGAIN_LATER);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getFrameReleaseAction_withoutSurfaceShouldIgnore_returnsIgnore() throws Exception {
|
||||||
|
VideoFrameReleaseControl.FrameReleaseInfo frameReleaseInfo =
|
||||||
|
new VideoFrameReleaseControl.FrameReleaseInfo();
|
||||||
|
FakeClock clock = new FakeClock(/* isAutoAdvancing= */ false);
|
||||||
|
VideoFrameReleaseControl videoFrameReleaseControl =
|
||||||
|
new VideoFrameReleaseControl(
|
||||||
|
ApplicationProvider.getApplicationContext(),
|
||||||
|
new TestFrameTimingEvaluator(
|
||||||
|
/* shouldForceRelease= */ false,
|
||||||
|
/* shouldDropFrame= */ false,
|
||||||
|
/* shouldIgnoreFrame= */ true),
|
||||||
|
/* allowedJoiningTimeMs= */ 0);
|
||||||
|
videoFrameReleaseControl.setOutputSurface(/* outputSurface= */ null);
|
||||||
|
videoFrameReleaseControl.setClock(clock);
|
||||||
|
videoFrameReleaseControl.onEnabled(/* releaseFirstFrameBeforeStarted= */ true);
|
||||||
|
|
||||||
|
assertThat(
|
||||||
|
videoFrameReleaseControl.getFrameReleaseAction(
|
||||||
|
/* presentationTimeUs= */ 100_000,
|
||||||
|
/* positionUs= */ 50_000,
|
||||||
|
/* elapsedRealtimeUs= */ 0,
|
||||||
|
/* outputStreamStartPositionUs= */ 0,
|
||||||
|
/* isDecodeOnlyFrame= */ false,
|
||||||
|
/* isLastFrame= */ false,
|
||||||
|
frameReleaseInfo))
|
||||||
|
.isEqualTo(VideoFrameReleaseControl.FRAME_RELEASE_IGNORE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getFrameReleaseAction_withoutSurfaceFrameLateNotStarted_returnsTryAgainLater()
|
||||||
|
throws Exception {
|
||||||
|
VideoFrameReleaseControl.FrameReleaseInfo frameReleaseInfo =
|
||||||
|
new VideoFrameReleaseControl.FrameReleaseInfo();
|
||||||
|
FakeClock clock = new FakeClock(/* isAutoAdvancing= */ false);
|
||||||
|
VideoFrameReleaseControl videoFrameReleaseControl = createVideoFrameReleaseControl();
|
||||||
|
videoFrameReleaseControl.setOutputSurface(/* outputSurface= */ null);
|
||||||
|
videoFrameReleaseControl.setClock(clock);
|
||||||
|
videoFrameReleaseControl.onEnabled(/* releaseFirstFrameBeforeStarted= */ true);
|
||||||
|
|
||||||
|
assertThat(
|
||||||
|
videoFrameReleaseControl.getFrameReleaseAction(
|
||||||
|
/* presentationTimeUs= */ 100_000,
|
||||||
|
/* positionUs= */ 90_000,
|
||||||
|
/* elapsedRealtimeUs= */ 0,
|
||||||
|
/* outputStreamStartPositionUs= */ 0,
|
||||||
|
/* isDecodeOnlyFrame= */ false,
|
||||||
|
/* isLastFrame= */ false,
|
||||||
|
frameReleaseInfo))
|
||||||
|
.isEqualTo(VideoFrameReleaseControl.FRAME_RELEASE_TRY_AGAIN_LATER);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getFrameReleaseAction_withoutSurfaceFrameLateAndStarted_returnsSkip()
|
||||||
|
throws Exception {
|
||||||
|
VideoFrameReleaseControl.FrameReleaseInfo frameReleaseInfo =
|
||||||
|
new VideoFrameReleaseControl.FrameReleaseInfo();
|
||||||
|
FakeClock clock = new FakeClock(/* isAutoAdvancing= */ false);
|
||||||
|
VideoFrameReleaseControl videoFrameReleaseControl = createVideoFrameReleaseControl();
|
||||||
|
videoFrameReleaseControl.setOutputSurface(/* outputSurface= */ null);
|
||||||
|
videoFrameReleaseControl.setClock(clock);
|
||||||
|
videoFrameReleaseControl.onEnabled(/* releaseFirstFrameBeforeStarted= */ true);
|
||||||
|
|
||||||
|
videoFrameReleaseControl.onStarted();
|
||||||
|
assertThat(
|
||||||
|
videoFrameReleaseControl.getFrameReleaseAction(
|
||||||
|
/* presentationTimeUs= */ 100_000,
|
||||||
|
/* positionUs= */ 90_000,
|
||||||
|
/* elapsedRealtimeUs= */ 0,
|
||||||
|
/* outputStreamStartPositionUs= */ 0,
|
||||||
|
/* isDecodeOnlyFrame= */ false,
|
||||||
|
/* isLastFrame= */ false,
|
||||||
|
frameReleaseInfo))
|
||||||
|
.isEqualTo(VideoFrameReleaseControl.FRAME_RELEASE_SKIP);
|
||||||
|
}
|
||||||
|
|
||||||
|
private VideoFrameReleaseControl createVideoFrameReleaseControl() {
|
||||||
return createVideoFrameReleaseControl(/* allowedJoiningTimeMs= */ 0);
|
return createVideoFrameReleaseControl(/* allowedJoiningTimeMs= */ 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static VideoFrameReleaseControl createVideoFrameReleaseControl(
|
private VideoFrameReleaseControl createVideoFrameReleaseControl(long allowedJoiningTimeMs) {
|
||||||
long allowedJoiningTimeMs) {
|
VideoFrameReleaseControl videoFrameReleaseControl =
|
||||||
return new VideoFrameReleaseControl(
|
new VideoFrameReleaseControl(
|
||||||
ApplicationProvider.getApplicationContext(),
|
ApplicationProvider.getApplicationContext(),
|
||||||
new TestFrameTimingEvaluator(),
|
new TestFrameTimingEvaluator(),
|
||||||
allowedJoiningTimeMs);
|
allowedJoiningTimeMs);
|
||||||
|
videoFrameReleaseControl.setOutputSurface(surface);
|
||||||
|
return videoFrameReleaseControl;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class TestFrameTimingEvaluator
|
private static class TestFrameTimingEvaluator
|
||||||
|
@ -20,10 +20,14 @@ import static org.mockito.ArgumentMatchers.anyLong;
|
|||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
|
import android.graphics.SurfaceTexture;
|
||||||
|
import android.view.Surface;
|
||||||
import androidx.media3.common.VideoSize;
|
import androidx.media3.common.VideoSize;
|
||||||
import androidx.media3.test.utils.FakeClock;
|
import androidx.media3.test.utils.FakeClock;
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.mockito.InOrder;
|
import org.mockito.InOrder;
|
||||||
@ -36,6 +40,18 @@ public class VideoFrameRenderControlTest {
|
|||||||
private static final int VIDEO_WIDTH = 640;
|
private static final int VIDEO_WIDTH = 640;
|
||||||
private static final int VIDEO_HEIGHT = 480;
|
private static final int VIDEO_HEIGHT = 480;
|
||||||
|
|
||||||
|
private Surface surface;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
surface = new Surface(new SurfaceTexture(/* texName= */ 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() {
|
||||||
|
surface.release();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void releaseFirstFrame() throws Exception {
|
public void releaseFirstFrame() throws Exception {
|
||||||
VideoFrameRenderControl.FrameRenderer frameRenderer =
|
VideoFrameRenderControl.FrameRenderer frameRenderer =
|
||||||
@ -275,7 +291,7 @@ public class VideoFrameRenderControlTest {
|
|||||||
assertThat(videoFrameRenderControl.isEnded()).isFalse();
|
assertThat(videoFrameRenderControl.isEnded()).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static VideoFrameReleaseControl createVideoFrameReleaseControl() {
|
private VideoFrameReleaseControl createVideoFrameReleaseControl() {
|
||||||
return createVideoFrameReleaseControl(
|
return createVideoFrameReleaseControl(
|
||||||
new TestFrameTimingEvaluator(
|
new TestFrameTimingEvaluator(
|
||||||
/* shouldForceReleaseFrames= */ false,
|
/* shouldForceReleaseFrames= */ false,
|
||||||
@ -283,12 +299,15 @@ public class VideoFrameRenderControlTest {
|
|||||||
/* shouldIgnoreFrames= */ false));
|
/* shouldIgnoreFrames= */ false));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static VideoFrameReleaseControl createVideoFrameReleaseControl(
|
private VideoFrameReleaseControl createVideoFrameReleaseControl(
|
||||||
VideoFrameReleaseControl.FrameTimingEvaluator frameTimingEvaluator) {
|
VideoFrameReleaseControl.FrameTimingEvaluator frameTimingEvaluator) {
|
||||||
return new VideoFrameReleaseControl(
|
VideoFrameReleaseControl videoFrameReleaseControl =
|
||||||
ApplicationProvider.getApplicationContext(),
|
new VideoFrameReleaseControl(
|
||||||
frameTimingEvaluator,
|
ApplicationProvider.getApplicationContext(),
|
||||||
/* allowedJoiningTimeMs= */ 0);
|
frameTimingEvaluator,
|
||||||
|
/* allowedJoiningTimeMs= */ 0);
|
||||||
|
videoFrameReleaseControl.setOutputSurface(surface);
|
||||||
|
return videoFrameReleaseControl;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class TestFrameTimingEvaluator
|
private static class TestFrameTimingEvaluator
|
||||||
|
@ -716,13 +716,9 @@ public final class ExperimentalFrameExtractor {
|
|||||||
public boolean isReady() {
|
public boolean isReady() {
|
||||||
// When using FrameReadingGlShaderProgram, frames will not be rendered to the output surface,
|
// When using FrameReadingGlShaderProgram, frames will not be rendered to the output surface,
|
||||||
// and VideoFrameRenderControl.onFrameAvailableForRendering will not be called. The base class
|
// and VideoFrameRenderControl.onFrameAvailableForRendering will not be called. The base class
|
||||||
// never becomes ready.
|
// never becomes ready. Treat this renderer as ready if a frame has been rendered into the
|
||||||
if (frameRenderedSinceLastPositionReset) {
|
// effects pipeline. The renderer needs to become ready for ExoPlayer to enter STATE_READY.
|
||||||
// Treat this renderer as ready if a frame has been rendered into the effects pipeline.
|
return frameRenderedSinceLastPositionReset;
|
||||||
// The renderer needs to become ready for ExoPlayer to enter STATE_READY.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return super.isReady();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
Loading…
x
Reference in New Issue
Block a user