mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Skip just-early video frames only if directed to release the frame
`MediaCodecVideoRenderer` will skip frames if a surface has not been set and video frame presentation time is early but too close to the current playback position. In the case that the `VideoFrameReleaseControl` says to `FRAME_RELEASE_TRY_AGAIN_LATER`, these frames should not be skipped. PiperOrigin-RevId: 706711734
This commit is contained in:
parent
319ac2e5af
commit
d5d85558c1
@ -56,6 +56,10 @@
|
|||||||
* Rollback of using `MediaCodecAdapter` supplied pixel aspect ratio values
|
* Rollback of using `MediaCodecAdapter` supplied pixel aspect ratio values
|
||||||
when provided while processing `onOutputFormatChanged`
|
when provided while processing `onOutputFormatChanged`
|
||||||
([#1371](https://github.com/androidx/media/pull/1371)).
|
([#1371](https://github.com/androidx/media/pull/1371)).
|
||||||
|
* Fix `MediaCodecVideoRenderer` such that when without a `Surface`, the
|
||||||
|
renderer will skip just-early frames only if the
|
||||||
|
`VideoFrameReleaseControl.getFrameReleaseAction` is not
|
||||||
|
`FRAME_RELEASE_TRY_AGAIN_LATER`.
|
||||||
* Text:
|
* Text:
|
||||||
* Stop eagerly loading all subtitle files configured with
|
* Stop eagerly loading all subtitle files configured with
|
||||||
`MediaItem.Builder.setSubtitleConfigurations`, and instead only load one
|
`MediaItem.Builder.setSubtitleConfigurations`, and instead only load one
|
||||||
|
@ -1509,7 +1509,9 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
|
|||||||
// We are not rendering on a surface, the renderer will wait until a surface is set.
|
// We are not rendering on a surface, the renderer will wait until a surface is set.
|
||||||
if (displaySurface == null) {
|
if (displaySurface == null) {
|
||||||
// Skip frames in sync with playback, so we'll be at the right frame if the mode changes.
|
// Skip frames in sync with playback, so we'll be at the right frame if the mode changes.
|
||||||
if (videoFrameReleaseInfo.getEarlyUs() < 30_000) {
|
if (videoFrameReleaseInfo.getEarlyUs() < 0
|
||||||
|
|| (videoFrameReleaseInfo.getEarlyUs() < 30_000
|
||||||
|
&& frameReleaseAction != VideoFrameReleaseControl.FRAME_RELEASE_TRY_AGAIN_LATER)) {
|
||||||
skipOutputBuffer(codec, bufferIndex, presentationTimeUs);
|
skipOutputBuffer(codec, bufferIndex, presentationTimeUs);
|
||||||
updateVideoFrameProcessingOffsetCounters(videoFrameReleaseInfo.getEarlyUs());
|
updateVideoFrameProcessingOffsetCounters(videoFrameReleaseInfo.getEarlyUs());
|
||||||
return true;
|
return true;
|
||||||
|
@ -299,6 +299,84 @@ public class MediaCodecVideoRendererTest {
|
|||||||
assertThat(decoderCounters.droppedToKeyframeCount).isEqualTo(1);
|
assertThat(decoderCounters.droppedToKeyframeCount).isEqualTo(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void render_earlyWithoutSurfaceAndStarted_skipsBuffer() throws Exception {
|
||||||
|
ArgumentCaptor<DecoderCounters> argumentDecoderCounters =
|
||||||
|
ArgumentCaptor.forClass(DecoderCounters.class);
|
||||||
|
FakeSampleStream fakeSampleStream =
|
||||||
|
new FakeSampleStream(
|
||||||
|
new DefaultAllocator(/* trimOnReset= */ true, /* individualAllocationSize= */ 1024),
|
||||||
|
/* mediaSourceEventDispatcher= */ null,
|
||||||
|
DrmSessionManager.DRM_UNSUPPORTED,
|
||||||
|
new DrmSessionEventListener.EventDispatcher(),
|
||||||
|
/* initialFormat= */ VIDEO_H264,
|
||||||
|
ImmutableList.of(
|
||||||
|
oneByteSample(/* timeUs= */ 50_000, C.BUFFER_FLAG_KEY_FRAME), END_OF_STREAM_ITEM));
|
||||||
|
fakeSampleStream.writeData(/* startPositionUs= */ 0);
|
||||||
|
// Set placeholder surface.
|
||||||
|
mediaCodecVideoRenderer.handleMessage(Renderer.MSG_SET_VIDEO_OUTPUT, null);
|
||||||
|
mediaCodecVideoRenderer.enable(
|
||||||
|
RendererConfiguration.DEFAULT,
|
||||||
|
new Format[] {VIDEO_H264},
|
||||||
|
fakeSampleStream,
|
||||||
|
/* positionUs= */ 0,
|
||||||
|
/* joining= */ true,
|
||||||
|
/* mayRenderStartOfStream= */ false,
|
||||||
|
/* startPositionUs= */ 50_000,
|
||||||
|
/* offsetUs= */ 0,
|
||||||
|
/* mediaPeriodId= */ new MediaSource.MediaPeriodId(new Object()));
|
||||||
|
|
||||||
|
mediaCodecVideoRenderer.start();
|
||||||
|
mediaCodecVideoRenderer.setCurrentStreamFinal();
|
||||||
|
mediaCodecVideoRenderer.render(0, SystemClock.elapsedRealtime() * 1000);
|
||||||
|
int posUs = 20_001; // Ensures buffer will be 29_999us early.
|
||||||
|
mediaCodecVideoRenderer.render(posUs, SystemClock.elapsedRealtime() * 1000);
|
||||||
|
shadowOf(testMainLooper).idle();
|
||||||
|
|
||||||
|
verify(eventListener).onVideoEnabled(argumentDecoderCounters.capture());
|
||||||
|
assertThat(argumentDecoderCounters.getValue().skippedOutputBufferCount).isEqualTo(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void render_earlyWithoutSurfaceAndNotStarted_doesNotSkipBuffer() throws Exception {
|
||||||
|
ArgumentCaptor<DecoderCounters> argumentDecoderCounters =
|
||||||
|
ArgumentCaptor.forClass(DecoderCounters.class);
|
||||||
|
FakeSampleStream fakeSampleStream =
|
||||||
|
new FakeSampleStream(
|
||||||
|
new DefaultAllocator(/* trimOnReset= */ true, /* individualAllocationSize= */ 1024),
|
||||||
|
/* mediaSourceEventDispatcher= */ null,
|
||||||
|
DrmSessionManager.DRM_UNSUPPORTED,
|
||||||
|
new DrmSessionEventListener.EventDispatcher(),
|
||||||
|
/* initialFormat= */ VIDEO_H264,
|
||||||
|
ImmutableList.of(
|
||||||
|
oneByteSample(/* timeUs= */ 50_000, C.BUFFER_FLAG_KEY_FRAME), END_OF_STREAM_ITEM));
|
||||||
|
fakeSampleStream.writeData(/* startPositionUs= */ 0);
|
||||||
|
// Set placeholder surface.
|
||||||
|
mediaCodecVideoRenderer.handleMessage(Renderer.MSG_SET_VIDEO_OUTPUT, null);
|
||||||
|
mediaCodecVideoRenderer.enable(
|
||||||
|
RendererConfiguration.DEFAULT,
|
||||||
|
new Format[] {VIDEO_H264},
|
||||||
|
fakeSampleStream,
|
||||||
|
/* positionUs= */ 0,
|
||||||
|
/* joining= */ true,
|
||||||
|
/* mayRenderStartOfStream= */ false,
|
||||||
|
/* startPositionUs= */ 50_000,
|
||||||
|
/* offsetUs= */ 0,
|
||||||
|
/* mediaPeriodId= */ new MediaSource.MediaPeriodId(new Object()));
|
||||||
|
|
||||||
|
mediaCodecVideoRenderer.setCurrentStreamFinal();
|
||||||
|
mediaCodecVideoRenderer.render(0, SystemClock.elapsedRealtime() * 1000);
|
||||||
|
int posUs = 20_001; // Ensures buffer will be 29_999us early.
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
mediaCodecVideoRenderer.render(posUs, SystemClock.elapsedRealtime() * 1000);
|
||||||
|
posUs += 10_000;
|
||||||
|
}
|
||||||
|
shadowOf(testMainLooper).idle();
|
||||||
|
|
||||||
|
verify(eventListener).onVideoEnabled(argumentDecoderCounters.capture());
|
||||||
|
assertThat(argumentDecoderCounters.getValue().skippedOutputBufferCount).isEqualTo(0);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void render_withBufferLimitEqualToNumberOfSamples_rendersLastFrameAfterEndOfStream()
|
public void render_withBufferLimitEqualToNumberOfSamples_rendersLastFrameAfterEndOfStream()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user