Publish MediaCodec-based renderer tests

Switch to snapshot Robolectric to pick up the latest version of shadows
required by MediaCodecVideoRendererTest and MediaCodecAudioRendererTest.

PiperOrigin-RevId: 312030332
This commit is contained in:
andrewlewis 2020-05-18 08:46:11 +01:00 committed by Andrew Lewis
parent f6d0e34cea
commit ceddc60296
4 changed files with 651 additions and 1 deletions

View File

@ -26,6 +26,7 @@ allprojects {
repositories {
google()
jcenter()
maven { url "https://oss.sonatype.org/content/repositories/snapshots" }
}
project.ext {
exoplayerPublishEnabled = false

View File

@ -23,7 +23,7 @@ project.ext {
junitVersion = '4.13-rc-2'
guavaVersion = '28.2-android'
mockitoVersion = '2.25.0'
robolectricVersion = '4.3.1'
robolectricVersion = '4.4-SNAPSHOT'
checkerframeworkVersion = '2.5.0'
jsr305Version = '3.0.2'
kotlinAnnotationsVersion = '1.3.70'

View File

@ -0,0 +1,150 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer2.audio;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.os.SystemClock;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.RendererCapabilities;
import com.google.android.exoplayer2.RendererConfiguration;
import com.google.android.exoplayer2.mediacodec.MediaCodecInfo;
import com.google.android.exoplayer2.mediacodec.MediaCodecSelector;
import com.google.android.exoplayer2.mediacodec.MediaCodecUtil.DecoderQueryException;
import com.google.android.exoplayer2.testutil.FakeSampleStream;
import com.google.android.exoplayer2.testutil.FakeSampleStream.FakeSampleStreamItem;
import com.google.android.exoplayer2.util.MimeTypes;
import java.util.Collections;
import java.util.List;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
/** Unit tests for {@link MediaCodecAudioRenderer} */
@RunWith(AndroidJUnit4.class)
public class MediaCodecAudioRendererTest {
@Rule public final MockitoRule mockito = MockitoJUnit.rule();
private static final Format AUDIO_AAC =
new Format.Builder()
.setSampleMimeType(MimeTypes.AUDIO_AAC)
.setPcmEncoding(C.ENCODING_PCM_16BIT)
.setChannelCount(2)
.setSampleRate(44100)
.setEncoderDelay(100)
.setEncoderPadding(150)
.build();
private MediaCodecAudioRenderer mediaCodecAudioRenderer;
@Mock private AudioSink audioSink;
@Before
public void setUp() throws Exception {
// audioSink isEnded can always be true because the MediaCodecAudioRenderer isEnded =
// super.isEnded && audioSink.isEnded.
when(audioSink.isEnded()).thenReturn(true);
when(audioSink.handleBuffer(any(), anyLong(), anyInt())).thenReturn(true);
MediaCodecSelector mediaCodecSelector =
new MediaCodecSelector() {
@Override
public List<MediaCodecInfo> getDecoderInfos(
String mimeType, boolean requiresSecureDecoder, boolean requiresTunnelingDecoder) {
return Collections.singletonList(
MediaCodecInfo.newInstance(
/* name= */ "name",
/* mimeType= */ mimeType,
/* codecMimeType= */ mimeType,
/* capabilities= */ null,
/* hardwareAccelerated= */ false,
/* softwareOnly= */ true,
/* vendor= */ false,
/* forceDisableAdaptive= */ false,
/* forceSecure= */ false));
}
};
mediaCodecAudioRenderer =
new MediaCodecAudioRenderer(
ApplicationProvider.getApplicationContext(),
mediaCodecSelector,
/* enableDecoderFallback= */ false,
/* eventHandler= */ null,
/* eventListener= */ null,
audioSink) {
@Override
protected int supportsFormat(MediaCodecSelector mediaCodecSelector, Format format)
throws DecoderQueryException {
return RendererCapabilities.create(FORMAT_HANDLED);
}
};
}
@Test
public void render_configuresAudioSink() throws Exception {
FakeSampleStream fakeSampleStream =
new FakeSampleStream(
/* format= */ AUDIO_AAC,
/* eventDispatcher= */ null,
/* firstSampleTimeUs= */ 0,
/* timeUsIncrement= */ 50,
new FakeSampleStreamItem(new byte[] {0}, C.BUFFER_FLAG_KEY_FRAME),
FakeSampleStreamItem.END_OF_STREAM_ITEM);
mediaCodecAudioRenderer.enable(
RendererConfiguration.DEFAULT,
new Format[] {AUDIO_AAC},
fakeSampleStream,
/* positionUs= */ 0,
/* joining= */ false,
/* mayRenderStartOfStream= */ false,
/* offsetUs */ 0);
mediaCodecAudioRenderer.start();
mediaCodecAudioRenderer.render(/* positionUs= */ 0, SystemClock.elapsedRealtime() * 1000);
mediaCodecAudioRenderer.render(/* positionUs= */ 250, SystemClock.elapsedRealtime() * 1000);
mediaCodecAudioRenderer.setCurrentStreamFinal();
int positionUs = 500;
do {
mediaCodecAudioRenderer.render(positionUs, SystemClock.elapsedRealtime() * 1000);
positionUs += 250;
} while (!mediaCodecAudioRenderer.isEnded());
verify(audioSink)
.configure(
AUDIO_AAC.pcmEncoding,
AUDIO_AAC.channelCount,
AUDIO_AAC.sampleRate,
/* specifiedBufferSize= */ 0,
/* outputChannels= */ null,
AUDIO_AAC.encoderDelay,
AUDIO_AAC.encoderPadding);
}
}

View File

@ -0,0 +1,499 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer2.video;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.graphics.SurfaceTexture;
import android.os.Handler;
import android.os.SystemClock;
import android.view.Surface;
import androidx.annotation.Nullable;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.Renderer;
import com.google.android.exoplayer2.RendererCapabilities;
import com.google.android.exoplayer2.RendererConfiguration;
import com.google.android.exoplayer2.mediacodec.MediaCodecInfo;
import com.google.android.exoplayer2.mediacodec.MediaCodecSelector;
import com.google.android.exoplayer2.mediacodec.MediaCodecUtil.DecoderQueryException;
import com.google.android.exoplayer2.testutil.FakeSampleStream;
import com.google.android.exoplayer2.testutil.FakeSampleStream.FakeSampleStreamItem;
import com.google.android.exoplayer2.util.MimeTypes;
import java.util.Collections;
import java.util.List;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.robolectric.annotation.LooperMode;
/** Unit test for {@link MediaCodecVideoRenderer}. */
@RunWith(AndroidJUnit4.class)
@LooperMode(LooperMode.Mode.LEGACY)
public class MediaCodecVideoRendererTest {
@Rule public final MockitoRule mockito = MockitoJUnit.rule();
private static final Format VIDEO_H264 =
new Format.Builder()
.setSampleMimeType(MimeTypes.VIDEO_H264)
.setWidth(1920)
.setHeight(1080)
.build();
private MediaCodecVideoRenderer mediaCodecVideoRenderer;
@Nullable private Format currentOutputFormat;
@Mock private VideoRendererEventListener eventListener;
@Before
public void setUp() throws Exception {
MediaCodecSelector mediaCodecSelector =
new MediaCodecSelector() {
@Override
public List<MediaCodecInfo> getDecoderInfos(
String mimeType, boolean requiresSecureDecoder, boolean requiresTunnelingDecoder) {
return Collections.singletonList(
MediaCodecInfo.newInstance(
/* name= */ "name",
/* mimeType= */ mimeType,
/* codecMimeType= */ mimeType,
/* capabilities= */ null,
/* hardwareAccelerated= */ false,
/* softwareOnly= */ true,
/* vendor= */ false,
/* forceDisableAdaptive= */ false,
/* forceSecure= */ false));
}
};
mediaCodecVideoRenderer =
new MediaCodecVideoRenderer(
ApplicationProvider.getApplicationContext(),
mediaCodecSelector,
/* allowedJoiningTimeMs= */ 0,
/* eventHandler= */ new Handler(),
/* eventListener= */ eventListener,
/* maxDroppedFramesToNotify= */ 1) {
@Override
@Capabilities
protected int supportsFormat(MediaCodecSelector mediaCodecSelector, Format format)
throws DecoderQueryException {
return RendererCapabilities.create(FORMAT_HANDLED);
}
@Override
protected void onOutputFormatChanged(Format outputFormat) {
super.onOutputFormatChanged(outputFormat);
currentOutputFormat = outputFormat;
}
};
mediaCodecVideoRenderer.handleMessage(
Renderer.MSG_SET_SURFACE, new Surface(new SurfaceTexture(/* texName= */ 0)));
}
@Test
public void render_dropsLateBuffer() throws Exception {
FakeSampleStream fakeSampleStream =
new FakeSampleStream(
/* format= */ VIDEO_H264,
/* eventDispatcher= */ null,
/* firstSampleTimeUs= */ 0,
/* timeUsIncrement= */ 50_000,
new FakeSampleStreamItem(new byte[] {0}, C.BUFFER_FLAG_KEY_FRAME), // First buffer.
new FakeSampleStreamItem(new byte[] {0}, C.BUFFER_FLAG_KEY_FRAME), // Late buffer.
new FakeSampleStreamItem(new byte[] {0}, C.BUFFER_FLAG_KEY_FRAME), // Last buffer.
FakeSampleStreamItem.END_OF_STREAM_ITEM);
mediaCodecVideoRenderer.enable(
RendererConfiguration.DEFAULT,
new Format[] {VIDEO_H264},
fakeSampleStream,
/* positionUs= */ 0,
/* joining= */ false,
/* mayRenderStartOfStream= */ true,
/* offsetUs */ 0);
mediaCodecVideoRenderer.start();
mediaCodecVideoRenderer.render(0, SystemClock.elapsedRealtime() * 1000);
mediaCodecVideoRenderer.render(40_000, SystemClock.elapsedRealtime() * 1000);
mediaCodecVideoRenderer.setCurrentStreamFinal();
int posUs = 80_001; // Ensures buffer will be 30_001us late.
while (!mediaCodecVideoRenderer.isEnded()) {
mediaCodecVideoRenderer.render(posUs, SystemClock.elapsedRealtime() * 1000);
posUs += 40_000;
}
verify(eventListener).onDroppedFrames(eq(1), anyLong());
}
@Test
public void render_sendsVideoSizeChangeWithCurrentFormatValues() throws Exception {
mediaCodecVideoRenderer.enable(
RendererConfiguration.DEFAULT,
new Format[] {VIDEO_H264},
new FakeSampleStream(
/* format= */ VIDEO_H264,
/* eventDispatcher= */ null,
/* firstSampleTimeUs= */ 0,
/* timeUsIncrement= */ 0,
new FakeSampleStreamItem(new byte[] {0}, C.BUFFER_FLAG_KEY_FRAME),
FakeSampleStreamItem.END_OF_STREAM_ITEM),
/* positionUs= */ 0,
/* joining= */ false,
/* mayRenderStartOfStream= */ true,
/* offsetUs */ 0);
mediaCodecVideoRenderer.setCurrentStreamFinal();
mediaCodecVideoRenderer.start();
int positionUs = 0;
do {
mediaCodecVideoRenderer.render(positionUs, SystemClock.elapsedRealtime() * 1000);
positionUs += 10;
} while (!mediaCodecVideoRenderer.isEnded());
verify(eventListener)
.onVideoSizeChanged(
VIDEO_H264.width,
VIDEO_H264.height,
VIDEO_H264.rotationDegrees,
VIDEO_H264.pixelWidthHeightRatio);
}
@Test
public void
render_withMultipleQueued_sendsVideoSizeChangedWithCorrectPixelAspectRatioWhenMultipleQueued()
throws Exception {
Format pAsp1 = VIDEO_H264.buildUpon().setPixelWidthHeightRatio(1f).build();
Format pAsp2 = VIDEO_H264.buildUpon().setPixelWidthHeightRatio(2f).build();
Format pAsp3 = VIDEO_H264.buildUpon().setPixelWidthHeightRatio(3f).build();
FakeSampleStream fakeSampleStream =
new FakeSampleStream(
/* format= */ pAsp1,
/* eventDispatcher= */ null,
/* firstSampleTimeUs= */ 0,
/* timeUsIncrement= */ 5000,
new FakeSampleStreamItem(new byte[] {0}, C.BUFFER_FLAG_KEY_FRAME));
mediaCodecVideoRenderer.enable(
RendererConfiguration.DEFAULT,
new Format[] {pAsp1, pAsp2, pAsp3},
fakeSampleStream,
/* positionUs= */ 0,
/* joining= */ false,
/* mayRenderStartOfStream= */ false,
/* offsetUs */ 0);
mediaCodecVideoRenderer.start();
mediaCodecVideoRenderer.render(/* positionUs= */ 0, SystemClock.elapsedRealtime() * 1000);
mediaCodecVideoRenderer.render(/* positionUs= */ 250, SystemClock.elapsedRealtime() * 1000);
fakeSampleStream.addFakeSampleStreamItem(new FakeSampleStreamItem(pAsp2));
fakeSampleStream.addFakeSampleStreamItem(
new FakeSampleStreamItem(new byte[] {0}, C.BUFFER_FLAG_KEY_FRAME));
fakeSampleStream.addFakeSampleStreamItem(
new FakeSampleStreamItem(new byte[] {0}, C.BUFFER_FLAG_KEY_FRAME));
fakeSampleStream.addFakeSampleStreamItem(new FakeSampleStreamItem(pAsp3));
fakeSampleStream.addFakeSampleStreamItem(
new FakeSampleStreamItem(new byte[] {0}, C.BUFFER_FLAG_KEY_FRAME));
fakeSampleStream.addFakeSampleStreamItem(
new FakeSampleStreamItem(new byte[] {0}, C.BUFFER_FLAG_KEY_FRAME));
fakeSampleStream.addFakeSampleStreamItem(FakeSampleStreamItem.END_OF_STREAM_ITEM);
mediaCodecVideoRenderer.setCurrentStreamFinal();
int pos = 500;
do {
mediaCodecVideoRenderer.render(/* positionUs= */ pos, SystemClock.elapsedRealtime() * 1000);
pos += 250;
} while (!mediaCodecVideoRenderer.isEnded());
InOrder orderVerifier = inOrder(eventListener);
orderVerifier.verify(eventListener).onVideoSizeChanged(anyInt(), anyInt(), anyInt(), eq(1f));
orderVerifier.verify(eventListener).onVideoSizeChanged(anyInt(), anyInt(), anyInt(), eq(2f));
orderVerifier.verify(eventListener).onVideoSizeChanged(anyInt(), anyInt(), anyInt(), eq(3f));
orderVerifier.verifyNoMoreInteractions();
}
@Test
public void render_includingResetPosition_keepsOutputFormatInVideoFrameMetadataListener()
throws Exception {
FakeSampleStream fakeSampleStream =
new FakeSampleStream(
/* format= */ VIDEO_H264,
/* eventDispatcher= */ null,
/* firstSampleTimeUs= */ 0,
/* timeUsIncrement= */ 50,
new FakeSampleStreamItem(new byte[] {0}, C.BUFFER_FLAG_KEY_FRAME));
mediaCodecVideoRenderer.enable(
RendererConfiguration.DEFAULT,
new Format[] {VIDEO_H264},
fakeSampleStream,
/* positionUs= */ 0,
/* joining= */ false,
/* mayRenderStartOfStream= */ true,
/* offsetUs */ 0);
mediaCodecVideoRenderer.start();
mediaCodecVideoRenderer.render(/* positionUs= */ 0, SystemClock.elapsedRealtime() * 1000);
mediaCodecVideoRenderer.resetPosition(0);
mediaCodecVideoRenderer.setCurrentStreamFinal();
fakeSampleStream.addFakeSampleStreamItem(
new FakeSampleStreamItem(new byte[] {0}, C.BUFFER_FLAG_KEY_FRAME));
fakeSampleStream.addFakeSampleStreamItem(FakeSampleStreamItem.END_OF_STREAM_ITEM);
int positionUs = 10;
do {
mediaCodecVideoRenderer.render(positionUs, SystemClock.elapsedRealtime() * 1000);
positionUs += 10;
} while (!mediaCodecVideoRenderer.isEnded());
assertThat(currentOutputFormat).isEqualTo(VIDEO_H264);
}
@Test
public void enable_withMayRenderStartOfStream_rendersFirstFrameBeforeStart() throws Exception {
FakeSampleStream fakeSampleStream =
new FakeSampleStream(
/* format= */ VIDEO_H264,
/* eventDispatcher= */ null,
/* firstSampleTimeUs= */ 0,
/* timeUsIncrement= */ 50,
new FakeSampleStreamItem(new byte[] {0}, C.BUFFER_FLAG_KEY_FRAME));
mediaCodecVideoRenderer.enable(
RendererConfiguration.DEFAULT,
new Format[] {VIDEO_H264},
fakeSampleStream,
/* positionUs= */ 0,
/* joining= */ false,
/* mayRenderStartOfStream= */ true,
/* offsetUs */ 0);
for (int i = 0; i < 10; i++) {
mediaCodecVideoRenderer.render(/* positionUs= */ 0, SystemClock.elapsedRealtime() * 1000);
}
verify(eventListener).onRenderedFirstFrame(any());
}
@Test
public void enable_withoutMayRenderStartOfStream_doesNotRenderFirstFrameBeforeStart()
throws Exception {
FakeSampleStream fakeSampleStream =
new FakeSampleStream(
/* format= */ VIDEO_H264,
/* eventDispatcher= */ null,
/* firstSampleTimeUs= */ 0,
/* timeUsIncrement= */ 50,
new FakeSampleStreamItem(new byte[] {0}, C.BUFFER_FLAG_KEY_FRAME));
mediaCodecVideoRenderer.enable(
RendererConfiguration.DEFAULT,
new Format[] {VIDEO_H264},
fakeSampleStream,
/* positionUs= */ 0,
/* joining= */ false,
/* mayRenderStartOfStream= */ false,
/* offsetUs */ 0);
for (int i = 0; i < 10; i++) {
mediaCodecVideoRenderer.render(/* positionUs= */ 0, SystemClock.elapsedRealtime() * 1000);
}
verify(eventListener, never()).onRenderedFirstFrame(any());
}
@Test
public void enable_withoutMayRenderStartOfStream_rendersFirstFrameAfterStart() throws Exception {
FakeSampleStream fakeSampleStream =
new FakeSampleStream(
/* format= */ VIDEO_H264,
/* eventDispatcher= */ null,
/* firstSampleTimeUs= */ 0,
/* timeUsIncrement= */ 50,
new FakeSampleStreamItem(new byte[] {0}, C.BUFFER_FLAG_KEY_FRAME));
mediaCodecVideoRenderer.enable(
RendererConfiguration.DEFAULT,
new Format[] {VIDEO_H264},
fakeSampleStream,
/* positionUs= */ 0,
/* joining= */ false,
/* mayRenderStartOfStream= */ false,
/* offsetUs */ 0);
mediaCodecVideoRenderer.start();
for (int i = 0; i < 10; i++) {
mediaCodecVideoRenderer.render(/* positionUs= */ 0, SystemClock.elapsedRealtime() * 1000);
}
verify(eventListener).onRenderedFirstFrame(any());
}
@Test
public void replaceStream_whenStarted_rendersFirstFrameOfNewStream() throws Exception {
FakeSampleStream fakeSampleStream1 =
new FakeSampleStream(
/* format= */ VIDEO_H264,
/* eventDispatcher= */ null,
/* firstSampleTimeUs= */ 0,
/* timeUsIncrement= */ 50,
new FakeSampleStreamItem(new byte[] {0}, C.BUFFER_FLAG_KEY_FRAME),
FakeSampleStreamItem.END_OF_STREAM_ITEM);
FakeSampleStream fakeSampleStream2 =
new FakeSampleStream(
/* format= */ VIDEO_H264,
/* eventDispatcher= */ null,
/* firstSampleTimeUs= */ 0,
/* timeUsIncrement= */ 50,
new FakeSampleStreamItem(new byte[] {0}, C.BUFFER_FLAG_KEY_FRAME),
FakeSampleStreamItem.END_OF_STREAM_ITEM);
mediaCodecVideoRenderer.enable(
RendererConfiguration.DEFAULT,
new Format[] {VIDEO_H264},
fakeSampleStream1,
/* positionUs= */ 0,
/* joining= */ false,
/* mayRenderStartOfStream= */ true,
/* offsetUs */ 0);
mediaCodecVideoRenderer.start();
boolean replacedStream = false;
for (int i = 0; i <= 10; i++) {
mediaCodecVideoRenderer.render(
/* positionUs= */ i * 10, SystemClock.elapsedRealtime() * 1000);
if (!replacedStream && mediaCodecVideoRenderer.hasReadStreamToEnd()) {
mediaCodecVideoRenderer.replaceStream(
new Format[] {VIDEO_H264}, fakeSampleStream2, /* offsetUs= */ 100);
replacedStream = true;
}
}
verify(eventListener, times(2)).onRenderedFirstFrame(any());
}
@Test
public void replaceStream_whenNotStarted_doesNotRenderFirstFrameOfNewStream() throws Exception {
FakeSampleStream fakeSampleStream1 =
new FakeSampleStream(
/* format= */ VIDEO_H264,
/* eventDispatcher= */ null,
/* firstSampleTimeUs= */ 0,
/* timeUsIncrement= */ 50,
new FakeSampleStreamItem(new byte[] {0}, C.BUFFER_FLAG_KEY_FRAME),
FakeSampleStreamItem.END_OF_STREAM_ITEM);
FakeSampleStream fakeSampleStream2 =
new FakeSampleStream(
/* format= */ VIDEO_H264,
/* eventDispatcher= */ null,
/* firstSampleTimeUs= */ 0,
/* timeUsIncrement= */ 50,
new FakeSampleStreamItem(new byte[] {0}, C.BUFFER_FLAG_KEY_FRAME),
FakeSampleStreamItem.END_OF_STREAM_ITEM);
mediaCodecVideoRenderer.enable(
RendererConfiguration.DEFAULT,
new Format[] {VIDEO_H264},
fakeSampleStream1,
/* positionUs= */ 0,
/* joining= */ false,
/* mayRenderStartOfStream= */ true,
/* offsetUs */ 0);
boolean replacedStream = false;
for (int i = 0; i < 10; i++) {
mediaCodecVideoRenderer.render(
/* positionUs= */ i * 10, SystemClock.elapsedRealtime() * 1000);
if (!replacedStream && mediaCodecVideoRenderer.hasReadStreamToEnd()) {
mediaCodecVideoRenderer.replaceStream(
new Format[] {VIDEO_H264}, fakeSampleStream2, /* offsetUs= */ 100);
replacedStream = true;
}
}
verify(eventListener).onRenderedFirstFrame(any());
// Render to streamOffsetUs and verify the new first frame gets rendered.
mediaCodecVideoRenderer.render(/* positionUs= */ 100, SystemClock.elapsedRealtime() * 1000);
verify(eventListener, times(2)).onRenderedFirstFrame(any());
}
@Test
public void onVideoFrameProcessingOffset_isCalledAfterOutputFormatChanges()
throws ExoPlaybackException {
Format mp4Uhd = VIDEO_H264.buildUpon().setWidth(3840).setHeight(2160).build();
byte[] sampleData = new byte[0];
FakeSampleStream fakeSampleStream =
new FakeSampleStream(
/* format= */ mp4Uhd,
/* eventDispatcher= */ null,
/* firstSampleTimeUs= */ 0,
/* timeUsIncrement= */ 50,
new FakeSampleStreamItem(mp4Uhd),
new FakeSampleStreamItem(sampleData, C.BUFFER_FLAG_KEY_FRAME),
new FakeSampleStreamItem(VIDEO_H264),
new FakeSampleStreamItem(sampleData, C.BUFFER_FLAG_KEY_FRAME),
new FakeSampleStreamItem(sampleData, C.BUFFER_FLAG_KEY_FRAME),
new FakeSampleStreamItem(mp4Uhd),
new FakeSampleStreamItem(sampleData, C.BUFFER_FLAG_KEY_FRAME),
new FakeSampleStreamItem(sampleData, C.BUFFER_FLAG_KEY_FRAME),
new FakeSampleStreamItem(sampleData, C.BUFFER_FLAG_KEY_FRAME),
new FakeSampleStreamItem(VIDEO_H264),
new FakeSampleStreamItem(sampleData, C.BUFFER_FLAG_KEY_FRAME),
FakeSampleStreamItem.END_OF_STREAM_ITEM);
mediaCodecVideoRenderer.enable(
RendererConfiguration.DEFAULT,
new Format[] {mp4Uhd},
fakeSampleStream,
/* positionUs= */ 0,
/* joining= */ false,
/* mayRenderStartOfStream= */ true,
/* offsetUs */ 0);
mediaCodecVideoRenderer.setCurrentStreamFinal();
mediaCodecVideoRenderer.start();
int positionUs = 10;
do {
mediaCodecVideoRenderer.render(positionUs, SystemClock.elapsedRealtime() * 1000);
positionUs += 10;
} while (!mediaCodecVideoRenderer.isEnded());
mediaCodecVideoRenderer.stop();
InOrder orderVerifier = inOrder(eventListener);
orderVerifier.verify(eventListener).onVideoFrameProcessingOffset(anyLong(), eq(1), eq(mp4Uhd));
orderVerifier
.verify(eventListener)
.onVideoFrameProcessingOffset(anyLong(), eq(2), eq(VIDEO_H264));
orderVerifier.verify(eventListener).onVideoFrameProcessingOffset(anyLong(), eq(3), eq(mp4Uhd));
orderVerifier
.verify(eventListener)
.onVideoFrameProcessingOffset(anyLong(), eq(1), eq(VIDEO_H264));
orderVerifier.verifyNoMoreInteractions();
}
}