mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
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:
parent
f6d0e34cea
commit
ceddc60296
@ -26,6 +26,7 @@ allprojects {
|
|||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
jcenter()
|
jcenter()
|
||||||
|
maven { url "https://oss.sonatype.org/content/repositories/snapshots" }
|
||||||
}
|
}
|
||||||
project.ext {
|
project.ext {
|
||||||
exoplayerPublishEnabled = false
|
exoplayerPublishEnabled = false
|
||||||
|
@ -23,7 +23,7 @@ project.ext {
|
|||||||
junitVersion = '4.13-rc-2'
|
junitVersion = '4.13-rc-2'
|
||||||
guavaVersion = '28.2-android'
|
guavaVersion = '28.2-android'
|
||||||
mockitoVersion = '2.25.0'
|
mockitoVersion = '2.25.0'
|
||||||
robolectricVersion = '4.3.1'
|
robolectricVersion = '4.4-SNAPSHOT'
|
||||||
checkerframeworkVersion = '2.5.0'
|
checkerframeworkVersion = '2.5.0'
|
||||||
jsr305Version = '3.0.2'
|
jsr305Version = '3.0.2'
|
||||||
kotlinAnnotationsVersion = '1.3.70'
|
kotlinAnnotationsVersion = '1.3.70'
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user