summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/sdk/android/tests/src
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
commit43a97878ce14b72f0981164f87f2e35e14151312 (patch)
tree620249daf56c0258faa40cbdcf9cfba06de2a846 /third_party/libwebrtc/sdk/android/tests/src
parentInitial commit. (diff)
downloadfirefox-upstream.tar.xz
firefox-upstream.zip
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/libwebrtc/sdk/android/tests/src')
-rw-r--r--third_party/libwebrtc/sdk/android/tests/src/org/webrtc/AndroidVideoDecoderTest.java432
-rw-r--r--third_party/libwebrtc/sdk/android/tests/src/org/webrtc/CameraEnumerationTest.java48
-rw-r--r--third_party/libwebrtc/sdk/android/tests/src/org/webrtc/CodecTestHelper.java62
-rw-r--r--third_party/libwebrtc/sdk/android/tests/src/org/webrtc/CryptoOptionsTest.java74
-rw-r--r--third_party/libwebrtc/sdk/android/tests/src/org/webrtc/FakeMediaCodecWrapper.java321
-rw-r--r--third_party/libwebrtc/sdk/android/tests/src/org/webrtc/FramerateBitrateAdjusterTest.java46
-rw-r--r--third_party/libwebrtc/sdk/android/tests/src/org/webrtc/GlGenericDrawerTest.java160
-rw-r--r--third_party/libwebrtc/sdk/android/tests/src/org/webrtc/HardwareVideoEncoderTest.java370
-rw-r--r--third_party/libwebrtc/sdk/android/tests/src/org/webrtc/IceCandidateTest.java52
-rw-r--r--third_party/libwebrtc/sdk/android/tests/src/org/webrtc/RefCountDelegateTest.java83
-rw-r--r--third_party/libwebrtc/sdk/android/tests/src/org/webrtc/ScalingSettingsTest.java29
-rw-r--r--third_party/libwebrtc/sdk/android/tests/src/org/webrtc/audio/LowLatencyAudioBufferManagerTest.java104
12 files changed, 1781 insertions, 0 deletions
diff --git a/third_party/libwebrtc/sdk/android/tests/src/org/webrtc/AndroidVideoDecoderTest.java b/third_party/libwebrtc/sdk/android/tests/src/org/webrtc/AndroidVideoDecoderTest.java
new file mode 100644
index 0000000000..535187e99e
--- /dev/null
+++ b/third_party/libwebrtc/sdk/android/tests/src/org/webrtc/AndroidVideoDecoderTest.java
@@ -0,0 +1,432 @@
+/*
+ * Copyright 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+package org.webrtc;
+
+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.doThrow;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.graphics.Matrix;
+import android.graphics.SurfaceTexture;
+import android.media.MediaCodecInfo.CodecCapabilities;
+import android.media.MediaFormat;
+import android.os.Handler;
+import androidx.test.runner.AndroidJUnit4;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.webrtc.EncodedImage.FrameType;
+import org.webrtc.FakeMediaCodecWrapper.State;
+import org.webrtc.VideoDecoder.DecodeInfo;
+import org.webrtc.VideoFrame.I420Buffer;
+import org.webrtc.VideoFrame.TextureBuffer.Type;
+
+@RunWith(AndroidJUnit4.class)
+@Config(manifest = Config.NONE)
+public class AndroidVideoDecoderTest {
+ private static final VideoDecoder.Settings TEST_DECODER_SETTINGS =
+ new VideoDecoder.Settings(/* numberOfCores= */ 1, /* width= */ 640, /* height= */ 480);
+ private static final int COLOR_FORMAT = CodecCapabilities.COLOR_FormatYUV420Planar;
+ private static final long POLL_DELAY_MS = 10;
+ private static final long DELIVER_DECODED_IMAGE_DELAY_MS = 10;
+
+ private static final byte[] ENCODED_TEST_DATA = new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+
+ private class TestDecoder extends AndroidVideoDecoder {
+ private final Object deliverDecodedFrameLock = new Object();
+ private boolean deliverDecodedFrameDone = true;
+
+ public TestDecoder(MediaCodecWrapperFactory mediaCodecFactory, String codecName,
+ VideoCodecMimeType codecType, int colorFormat, EglBase.Context sharedContext) {
+ super(mediaCodecFactory, codecName, codecType, colorFormat, sharedContext);
+ }
+
+ public void waitDeliverDecodedFrame() throws InterruptedException {
+ synchronized (deliverDecodedFrameLock) {
+ deliverDecodedFrameDone = false;
+ deliverDecodedFrameLock.notifyAll();
+ while (!deliverDecodedFrameDone) {
+ deliverDecodedFrameLock.wait();
+ }
+ }
+ }
+
+ @SuppressWarnings("WaitNotInLoop") // This method is called inside a loop.
+ @Override
+ protected void deliverDecodedFrame() {
+ synchronized (deliverDecodedFrameLock) {
+ if (deliverDecodedFrameDone) {
+ try {
+ deliverDecodedFrameLock.wait(DELIVER_DECODED_IMAGE_DELAY_MS);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ return;
+ }
+ }
+ if (deliverDecodedFrameDone) {
+ return;
+ }
+ super.deliverDecodedFrame();
+ deliverDecodedFrameDone = true;
+ deliverDecodedFrameLock.notifyAll();
+ }
+ }
+
+ @Override
+ protected SurfaceTextureHelper createSurfaceTextureHelper() {
+ return mockSurfaceTextureHelper;
+ }
+
+ @Override
+ protected void releaseSurface() {}
+
+ @Override
+ protected VideoFrame.I420Buffer allocateI420Buffer(int width, int height) {
+ int chromaHeight = (height + 1) / 2;
+ int strideUV = (width + 1) / 2;
+ int yPos = 0;
+ int uPos = yPos + width * height;
+ int vPos = uPos + strideUV * chromaHeight;
+
+ ByteBuffer buffer = ByteBuffer.allocateDirect(width * height + 2 * strideUV * chromaHeight);
+
+ buffer.position(yPos);
+ buffer.limit(uPos);
+ ByteBuffer dataY = buffer.slice();
+
+ buffer.position(uPos);
+ buffer.limit(vPos);
+ ByteBuffer dataU = buffer.slice();
+
+ buffer.position(vPos);
+ buffer.limit(vPos + strideUV * chromaHeight);
+ ByteBuffer dataV = buffer.slice();
+
+ return JavaI420Buffer.wrap(width, height, dataY, width, dataU, strideUV, dataV, strideUV,
+ /* releaseCallback= */ null);
+ }
+
+ @Override
+ protected void copyPlane(
+ ByteBuffer src, int srcStride, ByteBuffer dst, int dstStride, int width, int height) {
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ dst.put(y * dstStride + x, src.get(y * srcStride + x));
+ }
+ }
+ }
+ }
+
+ private class TestDecoderBuilder {
+ private VideoCodecMimeType codecType = VideoCodecMimeType.VP8;
+ private boolean useSurface = true;
+
+ public TestDecoderBuilder setCodecType(VideoCodecMimeType codecType) {
+ this.codecType = codecType;
+ return this;
+ }
+
+ public TestDecoderBuilder setUseSurface(boolean useSurface) {
+ this.useSurface = useSurface;
+ return this;
+ }
+
+ public TestDecoder build() {
+ return new TestDecoder((String name)
+ -> fakeMediaCodecWrapper,
+ /* codecName= */ "org.webrtc.testdecoder", codecType, COLOR_FORMAT,
+ useSurface ? mockEglBaseContext : null);
+ }
+ }
+
+ private static class FakeDecoderCallback implements VideoDecoder.Callback {
+ public final List<VideoFrame> decodedFrames;
+
+ public FakeDecoderCallback() {
+ decodedFrames = new ArrayList<>();
+ }
+
+ @Override
+ public void onDecodedFrame(VideoFrame frame, Integer decodeTimeMs, Integer qp) {
+ frame.retain();
+ decodedFrames.add(frame);
+ }
+
+ public void release() {
+ for (VideoFrame frame : decodedFrames) frame.release();
+ decodedFrames.clear();
+ }
+ }
+
+ private EncodedImage createTestEncodedImage() {
+ return EncodedImage.builder()
+ .setBuffer(ByteBuffer.wrap(ENCODED_TEST_DATA), null)
+ .setFrameType(FrameType.VideoFrameKey)
+ .createEncodedImage();
+ }
+
+ @Mock private EglBase.Context mockEglBaseContext;
+ @Mock private SurfaceTextureHelper mockSurfaceTextureHelper;
+ @Mock private VideoDecoder.Callback mockDecoderCallback;
+ private FakeMediaCodecWrapper fakeMediaCodecWrapper;
+ private FakeDecoderCallback fakeDecoderCallback;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ when(mockSurfaceTextureHelper.getSurfaceTexture())
+ .thenReturn(new SurfaceTexture(/*texName=*/0));
+ MediaFormat inputFormat = new MediaFormat();
+ MediaFormat outputFormat = new MediaFormat();
+ // TODO(sakal): Add more details to output format as needed.
+ fakeMediaCodecWrapper = spy(new FakeMediaCodecWrapper(inputFormat, outputFormat));
+ fakeDecoderCallback = new FakeDecoderCallback();
+ }
+
+ @After
+ public void cleanUp() {
+ fakeDecoderCallback.release();
+ }
+
+ @Test
+ public void testInit() {
+ // Set-up.
+ AndroidVideoDecoder decoder =
+ new TestDecoderBuilder().setCodecType(VideoCodecMimeType.VP8).build();
+
+ // Test.
+ assertThat(decoder.initDecode(TEST_DECODER_SETTINGS, mockDecoderCallback))
+ .isEqualTo(VideoCodecStatus.OK);
+
+ // Verify.
+ assertThat(fakeMediaCodecWrapper.getState()).isEqualTo(State.EXECUTING_RUNNING);
+
+ MediaFormat mediaFormat = fakeMediaCodecWrapper.getConfiguredFormat();
+ assertThat(mediaFormat).isNotNull();
+ assertThat(mediaFormat.getInteger(MediaFormat.KEY_WIDTH))
+ .isEqualTo(TEST_DECODER_SETTINGS.width);
+ assertThat(mediaFormat.getInteger(MediaFormat.KEY_HEIGHT))
+ .isEqualTo(TEST_DECODER_SETTINGS.height);
+ assertThat(mediaFormat.getString(MediaFormat.KEY_MIME))
+ .isEqualTo(VideoCodecMimeType.VP8.mimeType());
+ }
+
+ @Test
+ public void testRelease() {
+ // Set-up.
+ AndroidVideoDecoder decoder = new TestDecoderBuilder().build();
+ decoder.initDecode(TEST_DECODER_SETTINGS, mockDecoderCallback);
+
+ // Test.
+ assertThat(decoder.release()).isEqualTo(VideoCodecStatus.OK);
+
+ // Verify.
+ assertThat(fakeMediaCodecWrapper.getState()).isEqualTo(State.RELEASED);
+ }
+
+ @Test
+ public void testReleaseMultipleTimes() {
+ // Set-up.
+ AndroidVideoDecoder decoder = new TestDecoderBuilder().build();
+ decoder.initDecode(TEST_DECODER_SETTINGS, mockDecoderCallback);
+
+ // Test.
+ assertThat(decoder.release()).isEqualTo(VideoCodecStatus.OK);
+ assertThat(decoder.release()).isEqualTo(VideoCodecStatus.OK);
+
+ // Verify.
+ assertThat(fakeMediaCodecWrapper.getState()).isEqualTo(State.RELEASED);
+ }
+
+ @Test
+ public void testDecodeQueuesData() {
+ // Set-up.
+ AndroidVideoDecoder decoder = new TestDecoderBuilder().build();
+ decoder.initDecode(TEST_DECODER_SETTINGS, mockDecoderCallback);
+
+ // Test.
+ assertThat(decoder.decode(createTestEncodedImage(),
+ new DecodeInfo(/* isMissingFrames= */ false, /* renderTimeMs= */ 0)))
+ .isEqualTo(VideoCodecStatus.OK);
+
+ // Verify.
+ ArgumentCaptor<Integer> indexCaptor = ArgumentCaptor.forClass(Integer.class);
+ ArgumentCaptor<Integer> offsetCaptor = ArgumentCaptor.forClass(Integer.class);
+ ArgumentCaptor<Integer> sizeCaptor = ArgumentCaptor.forClass(Integer.class);
+ verify(fakeMediaCodecWrapper)
+ .queueInputBuffer(indexCaptor.capture(), offsetCaptor.capture(), sizeCaptor.capture(),
+ /* presentationTimeUs= */ anyLong(),
+ /* flags= */ eq(0));
+
+ ByteBuffer inputBuffer = fakeMediaCodecWrapper.getInputBuffer(indexCaptor.getValue());
+ CodecTestHelper.assertEqualContents(
+ ENCODED_TEST_DATA, inputBuffer, offsetCaptor.getValue(), sizeCaptor.getValue());
+ }
+
+ @Test
+ public void testDeliversOutputByteBuffers() throws InterruptedException {
+ final byte[] testOutputData = CodecTestHelper.generateRandomData(
+ TEST_DECODER_SETTINGS.width * TEST_DECODER_SETTINGS.height * 3 / 2);
+ final I420Buffer expectedDeliveredBuffer = CodecTestHelper.wrapI420(
+ TEST_DECODER_SETTINGS.width, TEST_DECODER_SETTINGS.height, testOutputData);
+
+ // Set-up.
+ TestDecoder decoder = new TestDecoderBuilder().setUseSurface(/* useSurface = */ false).build();
+ decoder.initDecode(TEST_DECODER_SETTINGS, fakeDecoderCallback);
+ decoder.decode(createTestEncodedImage(),
+ new DecodeInfo(/* isMissingFrames= */ false, /* renderTimeMs= */ 0));
+ fakeMediaCodecWrapper.addOutputData(
+ testOutputData, /* presentationTimestampUs= */ 0, /* flags= */ 0);
+
+ // Test.
+ decoder.waitDeliverDecodedFrame();
+
+ // Verify.
+ assertThat(fakeDecoderCallback.decodedFrames).hasSize(1);
+ VideoFrame videoFrame = fakeDecoderCallback.decodedFrames.get(0);
+ assertThat(videoFrame).isNotNull();
+ assertThat(videoFrame.getRotatedWidth()).isEqualTo(TEST_DECODER_SETTINGS.width);
+ assertThat(videoFrame.getRotatedHeight()).isEqualTo(TEST_DECODER_SETTINGS.height);
+ assertThat(videoFrame.getRotation()).isEqualTo(0);
+ I420Buffer deliveredBuffer = videoFrame.getBuffer().toI420();
+ assertThat(deliveredBuffer.getDataY()).isEqualTo(expectedDeliveredBuffer.getDataY());
+ assertThat(deliveredBuffer.getDataU()).isEqualTo(expectedDeliveredBuffer.getDataU());
+ assertThat(deliveredBuffer.getDataV()).isEqualTo(expectedDeliveredBuffer.getDataV());
+ }
+
+ @Test
+ public void testRendersOutputTexture() throws InterruptedException {
+ // Set-up.
+ TestDecoder decoder = new TestDecoderBuilder().build();
+ decoder.initDecode(TEST_DECODER_SETTINGS, mockDecoderCallback);
+ decoder.decode(createTestEncodedImage(),
+ new DecodeInfo(/* isMissingFrames= */ false, /* renderTimeMs= */ 0));
+ int bufferIndex =
+ fakeMediaCodecWrapper.addOutputTexture(/* presentationTimestampUs= */ 0, /* flags= */ 0);
+
+ // Test.
+ decoder.waitDeliverDecodedFrame();
+
+ // Verify.
+ verify(fakeMediaCodecWrapper).releaseOutputBuffer(bufferIndex, /* render= */ true);
+ }
+
+ @Test
+ public void testSurfaceTextureStall_FramesDropped() throws InterruptedException {
+ final int numFrames = 10;
+ // Maximum number of frame the decoder can keep queued on the output side.
+ final int maxQueuedBuffers = 3;
+
+ // Set-up.
+ TestDecoder decoder = new TestDecoderBuilder().build();
+ decoder.initDecode(TEST_DECODER_SETTINGS, mockDecoderCallback);
+
+ // Test.
+ int[] bufferIndices = new int[numFrames];
+ for (int i = 0; i < 10; i++) {
+ decoder.decode(createTestEncodedImage(),
+ new DecodeInfo(/* isMissingFrames= */ false, /* renderTimeMs= */ 0));
+ bufferIndices[i] =
+ fakeMediaCodecWrapper.addOutputTexture(/* presentationTimestampUs= */ 0, /* flags= */ 0);
+ decoder.waitDeliverDecodedFrame();
+ }
+
+ // Verify.
+ InOrder releaseOrder = inOrder(fakeMediaCodecWrapper);
+ releaseOrder.verify(fakeMediaCodecWrapper)
+ .releaseOutputBuffer(bufferIndices[0], /* render= */ true);
+ for (int i = 1; i < numFrames - maxQueuedBuffers; i++) {
+ releaseOrder.verify(fakeMediaCodecWrapper)
+ .releaseOutputBuffer(bufferIndices[i], /* render= */ false);
+ }
+ }
+
+ @Test
+ public void testDeliversRenderedBuffers() throws InterruptedException {
+ // Set-up.
+ TestDecoder decoder = new TestDecoderBuilder().build();
+ decoder.initDecode(TEST_DECODER_SETTINGS, fakeDecoderCallback);
+ decoder.decode(createTestEncodedImage(),
+ new DecodeInfo(/* isMissingFrames= */ false, /* renderTimeMs= */ 0));
+ fakeMediaCodecWrapper.addOutputTexture(/* presentationTimestampUs= */ 0, /* flags= */ 0);
+
+ // Render the output buffer.
+ decoder.waitDeliverDecodedFrame();
+
+ ArgumentCaptor<VideoSink> videoSinkCaptor = ArgumentCaptor.forClass(VideoSink.class);
+ verify(mockSurfaceTextureHelper).startListening(videoSinkCaptor.capture());
+
+ // Test.
+ Runnable releaseCallback = mock(Runnable.class);
+ VideoFrame.TextureBuffer outputTextureBuffer =
+ new TextureBufferImpl(TEST_DECODER_SETTINGS.width, TEST_DECODER_SETTINGS.height, Type.OES,
+ /* id= */ 0,
+ /* transformMatrix= */ new Matrix(),
+ /* toI420Handler= */ new Handler(), new YuvConverter(), releaseCallback);
+ VideoFrame outputVideoFrame =
+ new VideoFrame(outputTextureBuffer, /* rotation= */ 0, /* timestampNs= */ 0);
+ videoSinkCaptor.getValue().onFrame(outputVideoFrame);
+ outputVideoFrame.release();
+
+ // Verify.
+ assertThat(fakeDecoderCallback.decodedFrames).hasSize(1);
+ VideoFrame videoFrame = fakeDecoderCallback.decodedFrames.get(0);
+ assertThat(videoFrame).isNotNull();
+ assertThat(videoFrame.getBuffer()).isEqualTo(outputTextureBuffer);
+
+ fakeDecoderCallback.release();
+
+ verify(releaseCallback).run();
+ }
+
+ @Test
+ public void testConfigureExceptionTriggerSWFallback() {
+ // Set-up.
+ doThrow(new IllegalStateException("Fake error"))
+ .when(fakeMediaCodecWrapper)
+ .configure(any(), any(), any(), anyInt());
+
+ AndroidVideoDecoder decoder = new TestDecoderBuilder().build();
+
+ // Test.
+ assertThat(decoder.initDecode(TEST_DECODER_SETTINGS, mockDecoderCallback))
+ .isEqualTo(VideoCodecStatus.FALLBACK_SOFTWARE);
+ }
+
+ @Test
+ public void testStartExceptionTriggerSWFallback() {
+ // Set-up.
+ doThrow(new IllegalStateException("Fake error")).when(fakeMediaCodecWrapper).start();
+
+ AndroidVideoDecoder decoder = new TestDecoderBuilder().build();
+
+ // Test.
+ assertThat(decoder.initDecode(TEST_DECODER_SETTINGS, mockDecoderCallback))
+ .isEqualTo(VideoCodecStatus.FALLBACK_SOFTWARE);
+ }
+}
diff --git a/third_party/libwebrtc/sdk/android/tests/src/org/webrtc/CameraEnumerationTest.java b/third_party/libwebrtc/sdk/android/tests/src/org/webrtc/CameraEnumerationTest.java
new file mode 100644
index 0000000000..2c33992f99
--- /dev/null
+++ b/third_party/libwebrtc/sdk/android/tests/src/org/webrtc/CameraEnumerationTest.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2016 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+package org.webrtc;
+
+import static org.junit.Assert.assertEquals;
+import static org.webrtc.CameraEnumerationAndroid.getClosestSupportedFramerateRange;
+
+import androidx.test.runner.AndroidJUnit4;
+import java.util.Arrays;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+import org.webrtc.CameraEnumerationAndroid.CaptureFormat.FramerateRange;
+
+/**
+ * Tests for CameraEnumerationAndroid.
+ */
+@RunWith(AndroidJUnit4.class)
+@Config(manifest = Config.NONE)
+public class CameraEnumerationTest {
+ @Test
+ public void testGetClosestSupportedFramerateRange() {
+ assertEquals(new FramerateRange(10000, 30000),
+ getClosestSupportedFramerateRange(
+ Arrays.asList(new FramerateRange(10000, 30000), new FramerateRange(30000, 30000)),
+ 30 /* requestedFps */));
+
+ assertEquals(new FramerateRange(10000, 20000),
+ getClosestSupportedFramerateRange(
+ Arrays.asList(new FramerateRange(0, 30000), new FramerateRange(10000, 20000),
+ new FramerateRange(14000, 16000), new FramerateRange(15000, 15000)),
+ 15 /* requestedFps */));
+
+ assertEquals(new FramerateRange(10000, 20000),
+ getClosestSupportedFramerateRange(
+ Arrays.asList(new FramerateRange(15000, 15000), new FramerateRange(10000, 20000),
+ new FramerateRange(10000, 30000)),
+ 10 /* requestedFps */));
+ }
+}
diff --git a/third_party/libwebrtc/sdk/android/tests/src/org/webrtc/CodecTestHelper.java b/third_party/libwebrtc/sdk/android/tests/src/org/webrtc/CodecTestHelper.java
new file mode 100644
index 0000000000..08a10707f8
--- /dev/null
+++ b/third_party/libwebrtc/sdk/android/tests/src/org/webrtc/CodecTestHelper.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+package org.webrtc;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import java.nio.ByteBuffer;
+import java.util.Random;
+
+/**
+ * Helper methods for {@link HardwareVideoEncoderTest} and {@link AndroidVideoDecoderTest}.
+ */
+class CodecTestHelper {
+ static void assertEqualContents(byte[] expected, ByteBuffer actual, int offset, int size) {
+ assertThat(size).isEqualTo(expected.length);
+ assertThat(actual.capacity()).isAtLeast(offset + size);
+ for (int i = 0; i < expected.length; i++) {
+ assertWithMessage("At index: " + i).that(actual.get(offset + i)).isEqualTo(expected[i]);
+ }
+ }
+
+ static byte[] generateRandomData(int length) {
+ Random random = new Random();
+ byte[] data = new byte[length];
+ random.nextBytes(data);
+ return data;
+ }
+
+ static VideoFrame.I420Buffer wrapI420(int width, int height, byte[] data) {
+ final int posY = 0;
+ final int posU = width * height;
+ final int posV = posU + width * height / 4;
+ final int endV = posV + width * height / 4;
+
+ ByteBuffer buffer = ByteBuffer.allocateDirect(data.length);
+ buffer.put(data);
+
+ buffer.limit(posU);
+ buffer.position(posY);
+ ByteBuffer dataY = buffer.slice();
+
+ buffer.limit(posV);
+ buffer.position(posU);
+ ByteBuffer dataU = buffer.slice();
+
+ buffer.limit(endV);
+ buffer.position(posV);
+ ByteBuffer dataV = buffer.slice();
+
+ return JavaI420Buffer.wrap(width, height, dataY, width, dataU, width / 2, dataV, width / 2,
+ /* releaseCallback= */ null);
+ }
+}
diff --git a/third_party/libwebrtc/sdk/android/tests/src/org/webrtc/CryptoOptionsTest.java b/third_party/libwebrtc/sdk/android/tests/src/org/webrtc/CryptoOptionsTest.java
new file mode 100644
index 0000000000..c6cd2c2008
--- /dev/null
+++ b/third_party/libwebrtc/sdk/android/tests/src/org/webrtc/CryptoOptionsTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2018 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+package org.webrtc;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.runner.AndroidJUnit4;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+import org.webrtc.CryptoOptions;
+
+@RunWith(AndroidJUnit4.class)
+@Config(manifest = Config.NONE)
+public class CryptoOptionsTest {
+ // Validates the builder builds by default all false options.
+ @Test
+ public void testBuilderDefaultsAreFalse() {
+ CryptoOptions cryptoOptions = CryptoOptions.builder().createCryptoOptions();
+ assertThat(cryptoOptions.getSrtp().getEnableGcmCryptoSuites()).isFalse();
+ assertThat(cryptoOptions.getSrtp().getEnableAes128Sha1_32CryptoCipher()).isFalse();
+ assertThat(cryptoOptions.getSrtp().getEnableEncryptedRtpHeaderExtensions()).isFalse();
+ assertThat(cryptoOptions.getSFrame().getRequireFrameEncryption()).isFalse();
+ }
+
+ // Validates the builder sets the correct parameters.
+ @Test
+ public void testBuilderCorrectlyInitializingGcmCrypto() {
+ CryptoOptions cryptoOptions =
+ CryptoOptions.builder().setEnableGcmCryptoSuites(true).createCryptoOptions();
+ assertThat(cryptoOptions.getSrtp().getEnableGcmCryptoSuites()).isTrue();
+ assertThat(cryptoOptions.getSrtp().getEnableAes128Sha1_32CryptoCipher()).isFalse();
+ assertThat(cryptoOptions.getSrtp().getEnableEncryptedRtpHeaderExtensions()).isFalse();
+ assertThat(cryptoOptions.getSFrame().getRequireFrameEncryption()).isFalse();
+ }
+
+ @Test
+ public void testBuilderCorrectlyInitializingAes128Sha1_32CryptoCipher() {
+ CryptoOptions cryptoOptions =
+ CryptoOptions.builder().setEnableAes128Sha1_32CryptoCipher(true).createCryptoOptions();
+ assertThat(cryptoOptions.getSrtp().getEnableGcmCryptoSuites()).isFalse();
+ assertThat(cryptoOptions.getSrtp().getEnableAes128Sha1_32CryptoCipher()).isTrue();
+ assertThat(cryptoOptions.getSrtp().getEnableEncryptedRtpHeaderExtensions()).isFalse();
+ assertThat(cryptoOptions.getSFrame().getRequireFrameEncryption()).isFalse();
+ }
+
+ @Test
+ public void testBuilderCorrectlyInitializingEncryptedRtpHeaderExtensions() {
+ CryptoOptions cryptoOptions =
+ CryptoOptions.builder().setEnableEncryptedRtpHeaderExtensions(true).createCryptoOptions();
+ assertThat(cryptoOptions.getSrtp().getEnableGcmCryptoSuites()).isFalse();
+ assertThat(cryptoOptions.getSrtp().getEnableAes128Sha1_32CryptoCipher()).isFalse();
+ assertThat(cryptoOptions.getSrtp().getEnableEncryptedRtpHeaderExtensions()).isTrue();
+ assertThat(cryptoOptions.getSFrame().getRequireFrameEncryption()).isFalse();
+ }
+
+ @Test
+ public void testBuilderCorrectlyInitializingRequireFrameEncryption() {
+ CryptoOptions cryptoOptions =
+ CryptoOptions.builder().setRequireFrameEncryption(true).createCryptoOptions();
+ assertThat(cryptoOptions.getSrtp().getEnableGcmCryptoSuites()).isFalse();
+ assertThat(cryptoOptions.getSrtp().getEnableAes128Sha1_32CryptoCipher()).isFalse();
+ assertThat(cryptoOptions.getSrtp().getEnableEncryptedRtpHeaderExtensions()).isFalse();
+ assertThat(cryptoOptions.getSFrame().getRequireFrameEncryption()).isTrue();
+ }
+}
diff --git a/third_party/libwebrtc/sdk/android/tests/src/org/webrtc/FakeMediaCodecWrapper.java b/third_party/libwebrtc/sdk/android/tests/src/org/webrtc/FakeMediaCodecWrapper.java
new file mode 100644
index 0000000000..fb7aba4700
--- /dev/null
+++ b/third_party/libwebrtc/sdk/android/tests/src/org/webrtc/FakeMediaCodecWrapper.java
@@ -0,0 +1,321 @@
+/*
+ * Copyright 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+package org.webrtc;
+
+import android.graphics.SurfaceTexture;
+import android.media.MediaCodec;
+import android.media.MediaCodecInfo.CodecCapabilities;
+import android.media.MediaCrypto;
+import android.media.MediaFormat;
+import android.os.Bundle;
+import android.view.Surface;
+import androidx.annotation.Nullable;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Fake MediaCodec that implements the basic state machine.
+ *
+ * @note This class is only intended for single-threaded tests and is not thread-safe.
+ */
+public class FakeMediaCodecWrapper implements MediaCodecWrapper {
+ private static final int NUM_INPUT_BUFFERS = 10;
+ private static final int NUM_OUTPUT_BUFFERS = 10;
+ private static final int MAX_ENCODED_DATA_SIZE_BYTES = 1_000;
+
+ /**
+ * MediaCodec state as defined by:
+ * https://developer.android.com/reference/android/media/MediaCodec.html
+ */
+ public enum State {
+ STOPPED_CONFIGURED(Primary.STOPPED),
+ STOPPED_UNINITIALIZED(Primary.STOPPED),
+ STOPPED_ERROR(Primary.STOPPED),
+ EXECUTING_FLUSHED(Primary.EXECUTING),
+ EXECUTING_RUNNING(Primary.EXECUTING),
+ EXECUTING_END_OF_STREAM(Primary.EXECUTING),
+ RELEASED(Primary.RELEASED);
+
+ public enum Primary { STOPPED, EXECUTING, RELEASED }
+
+ private final Primary primary;
+
+ State(Primary primary) {
+ this.primary = primary;
+ }
+
+ public Primary getPrimary() {
+ return primary;
+ }
+ }
+
+ /** Represents an output buffer that will be returned by dequeueOutputBuffer. */
+ public static class QueuedOutputBufferInfo {
+ private int index;
+ private int offset;
+ private int size;
+ private long presentationTimeUs;
+ private int flags;
+
+ private QueuedOutputBufferInfo(
+ int index, int offset, int size, long presentationTimeUs, int flags) {
+ this.index = index;
+ this.offset = offset;
+ this.size = size;
+ this.presentationTimeUs = presentationTimeUs;
+ this.flags = flags;
+ }
+
+ public static QueuedOutputBufferInfo create(
+ int index, int offset, int size, long presentationTimeUs, int flags) {
+ return new QueuedOutputBufferInfo(index, offset, size, presentationTimeUs, flags);
+ }
+
+ public int getIndex() {
+ return index;
+ }
+
+ public int getOffset() {
+ return offset;
+ }
+
+ public int getSize() {
+ return size;
+ }
+
+ public long getPresentationTimeUs() {
+ return presentationTimeUs;
+ }
+
+ public int getFlags() {
+ return flags;
+ }
+ }
+
+ private State state = State.STOPPED_UNINITIALIZED;
+ private @Nullable MediaFormat configuredFormat;
+ private int configuredFlags;
+ private final MediaFormat inputFormat;
+ private final MediaFormat outputFormat;
+ private final ByteBuffer[] inputBuffers = new ByteBuffer[NUM_INPUT_BUFFERS];
+ private final ByteBuffer[] outputBuffers = new ByteBuffer[NUM_OUTPUT_BUFFERS];
+ private final boolean[] inputBufferReserved = new boolean[NUM_INPUT_BUFFERS];
+ private final boolean[] outputBufferReserved = new boolean[NUM_OUTPUT_BUFFERS];
+ private final List<QueuedOutputBufferInfo> queuedOutputBuffers = new ArrayList<>();
+
+ public FakeMediaCodecWrapper(MediaFormat inputFormat, MediaFormat outputFormat) {
+ this.inputFormat = inputFormat;
+ this.outputFormat = outputFormat;
+ }
+
+ /** Returns the current simulated state of MediaCodec. */
+ public State getState() {
+ return state;
+ }
+
+ /** Gets the last configured media format passed to configure. */
+ public @Nullable MediaFormat getConfiguredFormat() {
+ return configuredFormat;
+ }
+
+ /** Returns the last flags passed to configure. */
+ public int getConfiguredFlags() {
+ return configuredFlags;
+ }
+
+ /**
+ * Adds a texture buffer that will be returned by dequeueOutputBuffer. Returns index of the
+ * buffer.
+ */
+ public int addOutputTexture(long presentationTimestampUs, int flags) {
+ int index = getFreeOutputBuffer();
+ queuedOutputBuffers.add(QueuedOutputBufferInfo.create(
+ index, /* offset= */ 0, /* size= */ 0, presentationTimestampUs, flags));
+ return index;
+ }
+
+ /**
+ * Adds a byte buffer buffer that will be returned by dequeueOutputBuffer. Returns index of the
+ * buffer.
+ */
+ public int addOutputData(byte[] data, long presentationTimestampUs, int flags) {
+ int index = getFreeOutputBuffer();
+ ByteBuffer outputBuffer = outputBuffers[index];
+
+ outputBuffer.clear();
+ outputBuffer.put(data);
+ outputBuffer.rewind();
+
+ queuedOutputBuffers.add(QueuedOutputBufferInfo.create(
+ index, /* offset= */ 0, data.length, presentationTimestampUs, flags));
+ return index;
+ }
+
+ /**
+ * Returns the first output buffer that is not reserved and reserves it. It will be stay reserved
+ * until released with releaseOutputBuffer.
+ */
+ private int getFreeOutputBuffer() {
+ for (int i = 0; i < NUM_OUTPUT_BUFFERS; i++) {
+ if (!outputBufferReserved[i]) {
+ outputBufferReserved[i] = true;
+ return i;
+ }
+ }
+ throw new RuntimeException("All output buffers reserved!");
+ }
+
+ @Override
+ public void configure(MediaFormat format, Surface surface, MediaCrypto crypto, int flags) {
+ if (state != State.STOPPED_UNINITIALIZED) {
+ throw new IllegalStateException("Expected state STOPPED_UNINITIALIZED but was " + state);
+ }
+ state = State.STOPPED_CONFIGURED;
+ configuredFormat = format;
+ configuredFlags = flags;
+
+ final int width = configuredFormat.getInteger(MediaFormat.KEY_WIDTH);
+ final int height = configuredFormat.getInteger(MediaFormat.KEY_HEIGHT);
+ final int yuvSize = width * height * 3 / 2;
+ final int inputBufferSize;
+ final int outputBufferSize;
+
+ if ((flags & MediaCodec.CONFIGURE_FLAG_ENCODE) != 0) {
+ final int colorFormat = configuredFormat.getInteger(MediaFormat.KEY_COLOR_FORMAT);
+
+ inputBufferSize = colorFormat == CodecCapabilities.COLOR_FormatSurface ? 0 : yuvSize;
+ outputBufferSize = MAX_ENCODED_DATA_SIZE_BYTES;
+ } else {
+ inputBufferSize = MAX_ENCODED_DATA_SIZE_BYTES;
+ outputBufferSize = surface != null ? 0 : yuvSize;
+ }
+
+ for (int i = 0; i < inputBuffers.length; i++) {
+ inputBuffers[i] = ByteBuffer.allocateDirect(inputBufferSize);
+ }
+ for (int i = 0; i < outputBuffers.length; i++) {
+ outputBuffers[i] = ByteBuffer.allocateDirect(outputBufferSize);
+ }
+ }
+
+ @Override
+ public void start() {
+ if (state != State.STOPPED_CONFIGURED) {
+ throw new IllegalStateException("Expected state STOPPED_CONFIGURED but was " + state);
+ }
+ state = State.EXECUTING_RUNNING;
+ }
+
+ @Override
+ public void flush() {
+ if (state.getPrimary() != State.Primary.EXECUTING) {
+ throw new IllegalStateException("Expected state EXECUTING but was " + state);
+ }
+ state = State.EXECUTING_FLUSHED;
+ }
+
+ @Override
+ public void stop() {
+ if (state.getPrimary() != State.Primary.EXECUTING) {
+ throw new IllegalStateException("Expected state EXECUTING but was " + state);
+ }
+ state = State.STOPPED_UNINITIALIZED;
+ }
+
+ @Override
+ public void release() {
+ state = State.RELEASED;
+ }
+
+ @Override
+ public int dequeueInputBuffer(long timeoutUs) {
+ if (state != State.EXECUTING_FLUSHED && state != State.EXECUTING_RUNNING) {
+ throw new IllegalStateException(
+ "Expected state EXECUTING_FLUSHED or EXECUTING_RUNNING but was " + state);
+ }
+ state = State.EXECUTING_RUNNING;
+
+ for (int i = 0; i < NUM_INPUT_BUFFERS; i++) {
+ if (!inputBufferReserved[i]) {
+ inputBufferReserved[i] = true;
+ return i;
+ }
+ }
+ return MediaCodec.INFO_TRY_AGAIN_LATER;
+ }
+
+ @Override
+ public void queueInputBuffer(
+ int index, int offset, int size, long presentationTimeUs, int flags) {
+ if (state.getPrimary() != State.Primary.EXECUTING) {
+ throw new IllegalStateException("Expected state EXECUTING but was " + state);
+ }
+ if (flags != 0) {
+ throw new UnsupportedOperationException(
+ "Flags are not implemented in FakeMediaCodecWrapper.");
+ }
+ }
+
+ @Override
+ public int dequeueOutputBuffer(MediaCodec.BufferInfo info, long timeoutUs) {
+ if (state.getPrimary() != State.Primary.EXECUTING) {
+ throw new IllegalStateException("Expected state EXECUTING but was " + state);
+ }
+
+ if (queuedOutputBuffers.isEmpty()) {
+ return MediaCodec.INFO_TRY_AGAIN_LATER;
+ }
+ QueuedOutputBufferInfo outputBufferInfo = queuedOutputBuffers.remove(/* index= */ 0);
+ info.set(outputBufferInfo.getOffset(), outputBufferInfo.getSize(),
+ outputBufferInfo.getPresentationTimeUs(), outputBufferInfo.getFlags());
+ return outputBufferInfo.getIndex();
+ }
+
+ @Override
+ public void releaseOutputBuffer(int index, boolean render) {
+ if (state.getPrimary() != State.Primary.EXECUTING) {
+ throw new IllegalStateException("Expected state EXECUTING but was " + state);
+ }
+ if (!outputBufferReserved[index]) {
+ throw new RuntimeException("Released output buffer was not in use.");
+ }
+ outputBufferReserved[index] = false;
+ }
+
+ @Override
+ public ByteBuffer getInputBuffer(int index) {
+ return inputBuffers[index];
+ }
+
+ @Override
+ public ByteBuffer getOutputBuffer(int index) {
+ return outputBuffers[index];
+ }
+
+ @Override
+ public MediaFormat getInputFormat() {
+ return inputFormat;
+ }
+
+ @Override
+ public MediaFormat getOutputFormat() {
+ return outputFormat;
+ }
+
+ @Override
+ public Surface createInputSurface() {
+ return new Surface(new SurfaceTexture(/* texName= */ 0));
+ }
+
+ @Override
+ public void setParameters(Bundle params) {}
+}
diff --git a/third_party/libwebrtc/sdk/android/tests/src/org/webrtc/FramerateBitrateAdjusterTest.java b/third_party/libwebrtc/sdk/android/tests/src/org/webrtc/FramerateBitrateAdjusterTest.java
new file mode 100644
index 0000000000..5fcf9c87d6
--- /dev/null
+++ b/third_party/libwebrtc/sdk/android/tests/src/org/webrtc/FramerateBitrateAdjusterTest.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2021 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+package org.webrtc;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.runner.AndroidJUnit4;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+import org.webrtc.VideoEncoder.ScalingSettings;
+
+@RunWith(AndroidJUnit4.class)
+@Config(manifest = Config.NONE)
+public class FramerateBitrateAdjusterTest {
+ @Test
+ public void getAdjustedFramerate_alwaysReturnsDefault() {
+ FramerateBitrateAdjuster bitrateAdjuster = new FramerateBitrateAdjuster();
+ bitrateAdjuster.setTargets(1000, 15);
+ assertThat(bitrateAdjuster.getAdjustedFramerateFps()).isEqualTo(30.0);
+ }
+
+ @Test
+ public void getAdjustedBitrate_defaultFramerate_returnsTargetBitrate() {
+ FramerateBitrateAdjuster bitrateAdjuster = new FramerateBitrateAdjuster();
+ bitrateAdjuster.setTargets(1000, 30);
+ assertThat(bitrateAdjuster.getAdjustedBitrateBps()).isEqualTo(1000);
+ }
+
+ @Test
+ public void getAdjustedBitrate_nonDefaultFramerate_returnsAdjustedBitrate() {
+ FramerateBitrateAdjuster bitrateAdjuster = new FramerateBitrateAdjuster();
+ bitrateAdjuster.setTargets(1000, 7.5);
+ // Target frame frame is x4 times smaller than the adjusted one (30fps). Adjusted bitrate should
+ // be x4 times larger then the target one.
+ assertThat(bitrateAdjuster.getAdjustedBitrateBps()).isEqualTo(4000);
+ }
+}
diff --git a/third_party/libwebrtc/sdk/android/tests/src/org/webrtc/GlGenericDrawerTest.java b/third_party/libwebrtc/sdk/android/tests/src/org/webrtc/GlGenericDrawerTest.java
new file mode 100644
index 0000000000..fdb8f4bf08
--- /dev/null
+++ b/third_party/libwebrtc/sdk/android/tests/src/org/webrtc/GlGenericDrawerTest.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2018 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+package org.webrtc;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import androidx.test.runner.AndroidJUnit4;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+import org.webrtc.GlShader;
+
+@RunWith(AndroidJUnit4.class)
+@Config(manifest = Config.NONE)
+public class GlGenericDrawerTest {
+ // Simplest possible valid generic fragment shader.
+ private static final String FRAGMENT_SHADER = "void main() {\n"
+ + " gl_FragColor = sample(tc);\n"
+ + "}\n";
+ private static final int TEXTURE_ID = 3;
+ private static final float[] TEX_MATRIX =
+ new float[] {1, 2, 3, 4, -1, -2, -3, -4, 0, 0, 1, 0, 0, 0, 0, 1};
+ private static final int FRAME_WIDTH = 640;
+ private static final int FRAME_HEIGHT = 480;
+ private static final int VIEWPORT_X = 3;
+ private static final int VIEWPORT_Y = 5;
+ private static final int VIEWPORT_WIDTH = 500;
+ private static final int VIEWPORT_HEIGHT = 500;
+
+ // Replace OpenGLES GlShader dependency with a mock.
+ private class GlGenericDrawerForTest extends GlGenericDrawer {
+ public GlGenericDrawerForTest(String genericFragmentSource, ShaderCallbacks shaderCallbacks) {
+ super(genericFragmentSource, shaderCallbacks);
+ }
+
+ @Override
+ GlShader createShader(ShaderType shaderType) {
+ return mockedShader;
+ }
+ }
+
+ private GlShader mockedShader;
+ private GlGenericDrawer glGenericDrawer;
+ private GlGenericDrawer.ShaderCallbacks mockedCallbacks;
+
+ @Before
+ public void setUp() {
+ mockedShader = mock(GlShader.class);
+ mockedCallbacks = mock(GlGenericDrawer.ShaderCallbacks.class);
+ glGenericDrawer = new GlGenericDrawerForTest(FRAGMENT_SHADER, mockedCallbacks);
+ }
+
+ @After
+ public void tearDown() {
+ verifyNoMoreInteractions(mockedCallbacks);
+ }
+
+ @Test
+ public void testOesFragmentShader() {
+ final String expectedOesFragmentShader = "#extension GL_OES_EGL_image_external : require\n"
+ + "precision mediump float;\n"
+ + "varying vec2 tc;\n"
+ + "uniform samplerExternalOES tex;\n"
+ + "void main() {\n"
+ + " gl_FragColor = texture2D(tex, tc);\n"
+ + "}\n";
+ final String oesFragmentShader =
+ GlGenericDrawer.createFragmentShaderString(FRAGMENT_SHADER, GlGenericDrawer.ShaderType.OES);
+ assertEquals(expectedOesFragmentShader, oesFragmentShader);
+ }
+
+ @Test
+ public void testRgbFragmentShader() {
+ final String expectedRgbFragmentShader = "precision mediump float;\n"
+ + "varying vec2 tc;\n"
+ + "uniform sampler2D tex;\n"
+ + "void main() {\n"
+ + " gl_FragColor = texture2D(tex, tc);\n"
+ + "}\n";
+ final String rgbFragmentShader =
+ GlGenericDrawer.createFragmentShaderString(FRAGMENT_SHADER, GlGenericDrawer.ShaderType.RGB);
+ assertEquals(expectedRgbFragmentShader, rgbFragmentShader);
+ }
+
+ @Test
+ public void testYuvFragmentShader() {
+ final String expectedYuvFragmentShader = "precision mediump float;\n"
+ + "varying vec2 tc;\n"
+ + "uniform sampler2D y_tex;\n"
+ + "uniform sampler2D u_tex;\n"
+ + "uniform sampler2D v_tex;\n"
+ + "vec4 sample(vec2 p) {\n"
+ + " float y = texture2D(y_tex, p).r * 1.16438;\n"
+ + " float u = texture2D(u_tex, p).r;\n"
+ + " float v = texture2D(v_tex, p).r;\n"
+ + " return vec4(y + 1.59603 * v - 0.874202,\n"
+ + " y - 0.391762 * u - 0.812968 * v + 0.531668,\n"
+ + " y + 2.01723 * u - 1.08563, 1);\n"
+ + "}\n"
+ + "void main() {\n"
+ + " gl_FragColor = sample(tc);\n"
+ + "}\n";
+ final String yuvFragmentShader =
+ GlGenericDrawer.createFragmentShaderString(FRAGMENT_SHADER, GlGenericDrawer.ShaderType.YUV);
+ assertEquals(expectedYuvFragmentShader, yuvFragmentShader);
+ }
+
+ @Test
+ public void testShaderCallbacksOneRgbFrame() {
+ glGenericDrawer.drawRgb(TEXTURE_ID, TEX_MATRIX, FRAME_WIDTH, FRAME_HEIGHT, VIEWPORT_X,
+ VIEWPORT_Y, VIEWPORT_WIDTH, VIEWPORT_HEIGHT);
+
+ verify(mockedCallbacks).onNewShader(mockedShader);
+ verify(mockedCallbacks)
+ .onPrepareShader(
+ mockedShader, TEX_MATRIX, FRAME_WIDTH, FRAME_HEIGHT, VIEWPORT_WIDTH, VIEWPORT_HEIGHT);
+ }
+
+ @Test
+ public void testShaderCallbacksTwoRgbFrames() {
+ glGenericDrawer.drawRgb(TEXTURE_ID, TEX_MATRIX, FRAME_WIDTH, FRAME_HEIGHT, VIEWPORT_X,
+ VIEWPORT_Y, VIEWPORT_WIDTH, VIEWPORT_HEIGHT);
+ glGenericDrawer.drawRgb(TEXTURE_ID, TEX_MATRIX, FRAME_WIDTH, FRAME_HEIGHT, VIEWPORT_X,
+ VIEWPORT_Y, VIEWPORT_WIDTH, VIEWPORT_HEIGHT);
+
+ // Expect only one shader to be created, but two frames to be drawn.
+ verify(mockedCallbacks, times(1)).onNewShader(mockedShader);
+ verify(mockedCallbacks, times(2))
+ .onPrepareShader(
+ mockedShader, TEX_MATRIX, FRAME_WIDTH, FRAME_HEIGHT, VIEWPORT_WIDTH, VIEWPORT_HEIGHT);
+ }
+
+ @Test
+ public void testShaderCallbacksChangingShaderType() {
+ glGenericDrawer.drawRgb(TEXTURE_ID, TEX_MATRIX, FRAME_WIDTH, FRAME_HEIGHT, VIEWPORT_X,
+ VIEWPORT_Y, VIEWPORT_WIDTH, VIEWPORT_HEIGHT);
+ glGenericDrawer.drawOes(TEXTURE_ID, TEX_MATRIX, FRAME_WIDTH, FRAME_HEIGHT, VIEWPORT_X,
+ VIEWPORT_Y, VIEWPORT_WIDTH, VIEWPORT_HEIGHT);
+
+ // Expect two shaders to be created, and two frames to be drawn.
+ verify(mockedCallbacks, times(2)).onNewShader(mockedShader);
+ verify(mockedCallbacks, times(2))
+ .onPrepareShader(
+ mockedShader, TEX_MATRIX, FRAME_WIDTH, FRAME_HEIGHT, VIEWPORT_WIDTH, VIEWPORT_HEIGHT);
+ }
+}
diff --git a/third_party/libwebrtc/sdk/android/tests/src/org/webrtc/HardwareVideoEncoderTest.java b/third_party/libwebrtc/sdk/android/tests/src/org/webrtc/HardwareVideoEncoderTest.java
new file mode 100644
index 0000000000..bd4a642f00
--- /dev/null
+++ b/third_party/libwebrtc/sdk/android/tests/src/org/webrtc/HardwareVideoEncoderTest.java
@@ -0,0 +1,370 @@
+/*
+ * Copyright 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+package org.webrtc;
+
+import static com.google.common.truth.Truth.assertThat;
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.media.MediaCodec;
+import android.media.MediaCodecInfo;
+import android.media.MediaFormat;
+import android.os.Bundle;
+import androidx.test.runner.AndroidJUnit4;
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.Map;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.webrtc.EncodedImage;
+import org.webrtc.EncodedImage.FrameType;
+import org.webrtc.FakeMediaCodecWrapper.State;
+import org.webrtc.VideoCodecStatus;
+import org.webrtc.VideoEncoder;
+import org.webrtc.VideoEncoder.BitrateAllocation;
+import org.webrtc.VideoEncoder.CodecSpecificInfo;
+import org.webrtc.VideoEncoder.EncodeInfo;
+import org.webrtc.VideoEncoder.Settings;
+import org.webrtc.VideoFrame;
+import org.webrtc.VideoFrame.Buffer;
+import org.webrtc.VideoFrame.I420Buffer;
+
+@RunWith(AndroidJUnit4.class)
+@Config(manifest = Config.NONE)
+public class HardwareVideoEncoderTest {
+ private static final VideoEncoder.Settings TEST_ENCODER_SETTINGS = new Settings(
+ /* numberOfCores= */ 1,
+ /* width= */ 640,
+ /* height= */ 480,
+ /* startBitrate= */ 10000,
+ /* maxFramerate= */ 30,
+ /* numberOfSimulcastStreams= */ 1,
+ /* automaticResizeOn= */ true,
+ /* capabilities= */ new VideoEncoder.Capabilities(false /* lossNotification */));
+ private static final long POLL_DELAY_MS = 10;
+ private static final long DELIVER_ENCODED_IMAGE_DELAY_MS = 10;
+ private static final EncodeInfo ENCODE_INFO_KEY_FRAME =
+ new EncodeInfo(new FrameType[] {FrameType.VideoFrameKey});
+ private static final EncodeInfo ENCODE_INFO_DELTA_FRAME =
+ new EncodeInfo(new FrameType[] {FrameType.VideoFrameDelta});
+
+ private static class TestEncoder extends HardwareVideoEncoder {
+ private final Object deliverEncodedImageLock = new Object();
+ private boolean deliverEncodedImageDone = true;
+
+ TestEncoder(MediaCodecWrapperFactory mediaCodecWrapperFactory, String codecName,
+ VideoCodecMimeType codecType, Integer surfaceColorFormat, Integer yuvColorFormat,
+ Map<String, String> params, int keyFrameIntervalSec, int forceKeyFrameIntervalMs,
+ BitrateAdjuster bitrateAdjuster, EglBase14.Context sharedContext) {
+ super(mediaCodecWrapperFactory, codecName, codecType, surfaceColorFormat, yuvColorFormat,
+ params, keyFrameIntervalSec, forceKeyFrameIntervalMs, bitrateAdjuster, sharedContext);
+ }
+
+ public void waitDeliverEncodedImage() throws InterruptedException {
+ synchronized (deliverEncodedImageLock) {
+ deliverEncodedImageDone = false;
+ deliverEncodedImageLock.notifyAll();
+ while (!deliverEncodedImageDone) {
+ deliverEncodedImageLock.wait();
+ }
+ }
+ }
+
+ @SuppressWarnings("WaitNotInLoop") // This method is called inside a loop.
+ @Override
+ protected void deliverEncodedImage() {
+ synchronized (deliverEncodedImageLock) {
+ if (deliverEncodedImageDone) {
+ try {
+ deliverEncodedImageLock.wait(DELIVER_ENCODED_IMAGE_DELAY_MS);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ return;
+ }
+ }
+ if (deliverEncodedImageDone) {
+ return;
+ }
+ super.deliverEncodedImage();
+ deliverEncodedImageDone = true;
+ deliverEncodedImageLock.notifyAll();
+ }
+ }
+
+ @Override
+ protected void fillInputBuffer(ByteBuffer buffer, Buffer videoFrameBuffer) {
+ I420Buffer i420Buffer = videoFrameBuffer.toI420();
+ buffer.put(i420Buffer.getDataY());
+ buffer.put(i420Buffer.getDataU());
+ buffer.put(i420Buffer.getDataV());
+ buffer.flip();
+ i420Buffer.release();
+ }
+ }
+
+ private class TestEncoderBuilder {
+ private VideoCodecMimeType codecType = VideoCodecMimeType.VP8;
+ private BitrateAdjuster bitrateAdjuster = new BaseBitrateAdjuster();
+
+ public TestEncoderBuilder setCodecType(VideoCodecMimeType codecType) {
+ this.codecType = codecType;
+ return this;
+ }
+
+ public TestEncoderBuilder setBitrateAdjuster(BitrateAdjuster bitrateAdjuster) {
+ this.bitrateAdjuster = bitrateAdjuster;
+ return this;
+ }
+
+ public TestEncoder build() {
+ return new TestEncoder((String name)
+ -> fakeMediaCodecWrapper,
+ "org.webrtc.testencoder", codecType,
+ /* surfaceColorFormat= */ null,
+ /* yuvColorFormat= */ MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar,
+ /* params= */ new HashMap<>(),
+ /* keyFrameIntervalSec= */ 0,
+ /* forceKeyFrameIntervalMs= */ 0, bitrateAdjuster,
+ /* sharedContext= */ null);
+ }
+ }
+
+ private VideoFrame createTestVideoFrame(long timestampNs) {
+ byte[] i420 = CodecTestHelper.generateRandomData(
+ TEST_ENCODER_SETTINGS.width * TEST_ENCODER_SETTINGS.height * 3 / 2);
+ final VideoFrame.I420Buffer testBuffer =
+ CodecTestHelper.wrapI420(TEST_ENCODER_SETTINGS.width, TEST_ENCODER_SETTINGS.height, i420);
+ return new VideoFrame(testBuffer, /* rotation= */ 0, timestampNs);
+ }
+
+ @Mock VideoEncoder.Callback mockEncoderCallback;
+ private FakeMediaCodecWrapper fakeMediaCodecWrapper;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ MediaFormat inputFormat = new MediaFormat();
+ MediaFormat outputFormat = new MediaFormat();
+ // TODO(sakal): Add more details to output format as needed.
+ fakeMediaCodecWrapper = spy(new FakeMediaCodecWrapper(inputFormat, outputFormat));
+ }
+
+ @Test
+ public void testInit() {
+ // Set-up.
+ HardwareVideoEncoder encoder =
+ new TestEncoderBuilder().setCodecType(VideoCodecMimeType.VP8).build();
+
+ // Test.
+ assertThat(encoder.initEncode(TEST_ENCODER_SETTINGS, mockEncoderCallback))
+ .isEqualTo(VideoCodecStatus.OK);
+
+ // Verify.
+ assertThat(fakeMediaCodecWrapper.getState()).isEqualTo(State.EXECUTING_RUNNING);
+
+ MediaFormat mediaFormat = fakeMediaCodecWrapper.getConfiguredFormat();
+ assertThat(mediaFormat).isNotNull();
+ assertThat(mediaFormat.getInteger(MediaFormat.KEY_WIDTH))
+ .isEqualTo(TEST_ENCODER_SETTINGS.width);
+ assertThat(mediaFormat.getInteger(MediaFormat.KEY_HEIGHT))
+ .isEqualTo(TEST_ENCODER_SETTINGS.height);
+ assertThat(mediaFormat.getString(MediaFormat.KEY_MIME))
+ .isEqualTo(VideoCodecMimeType.VP8.mimeType());
+
+ assertThat(fakeMediaCodecWrapper.getConfiguredFlags())
+ .isEqualTo(MediaCodec.CONFIGURE_FLAG_ENCODE);
+ }
+
+ @Test
+ public void testEncodeByteBuffer() {
+ // Set-up.
+ HardwareVideoEncoder encoder = new TestEncoderBuilder().build();
+ encoder.initEncode(TEST_ENCODER_SETTINGS, mockEncoderCallback);
+
+ // Test.
+ byte[] i420 = CodecTestHelper.generateRandomData(
+ TEST_ENCODER_SETTINGS.width * TEST_ENCODER_SETTINGS.height * 3 / 2);
+ final VideoFrame.I420Buffer testBuffer =
+ CodecTestHelper.wrapI420(TEST_ENCODER_SETTINGS.width, TEST_ENCODER_SETTINGS.height, i420);
+ final VideoFrame testFrame =
+ new VideoFrame(testBuffer, /* rotation= */ 0, /* timestampNs= */ 0);
+ assertThat(encoder.encode(testFrame, new EncodeInfo(new FrameType[] {FrameType.VideoFrameKey})))
+ .isEqualTo(VideoCodecStatus.OK);
+
+ // Verify.
+ ArgumentCaptor<Integer> indexCaptor = ArgumentCaptor.forClass(Integer.class);
+ ArgumentCaptor<Integer> offsetCaptor = ArgumentCaptor.forClass(Integer.class);
+ ArgumentCaptor<Integer> sizeCaptor = ArgumentCaptor.forClass(Integer.class);
+ verify(fakeMediaCodecWrapper)
+ .queueInputBuffer(indexCaptor.capture(), offsetCaptor.capture(), sizeCaptor.capture(),
+ anyLong(), anyInt());
+ ByteBuffer buffer = fakeMediaCodecWrapper.getInputBuffer(indexCaptor.getValue());
+ CodecTestHelper.assertEqualContents(
+ i420, buffer, offsetCaptor.getValue(), sizeCaptor.getValue());
+ }
+
+ @Test
+ public void testDeliversOutputData() throws InterruptedException {
+ // Set-up.
+ TestEncoder encoder = new TestEncoderBuilder().build();
+ encoder.initEncode(TEST_ENCODER_SETTINGS, mockEncoderCallback);
+ encoder.encode(createTestVideoFrame(/* timestampNs= */ 42), ENCODE_INFO_KEY_FRAME);
+
+ // Test.
+ byte[] outputData = CodecTestHelper.generateRandomData(100);
+ fakeMediaCodecWrapper.addOutputData(outputData,
+ /* presentationTimestampUs= */ 0,
+ /* flags= */ MediaCodec.BUFFER_FLAG_SYNC_FRAME);
+
+ encoder.waitDeliverEncodedImage();
+
+ // Verify.
+ ArgumentCaptor<EncodedImage> videoFrameCaptor = ArgumentCaptor.forClass(EncodedImage.class);
+ verify(mockEncoderCallback)
+ .onEncodedFrame(videoFrameCaptor.capture(), any(CodecSpecificInfo.class));
+
+ EncodedImage videoFrame = videoFrameCaptor.getValue();
+ assertThat(videoFrame).isNotNull();
+ assertThat(videoFrame.encodedWidth).isEqualTo(TEST_ENCODER_SETTINGS.width);
+ assertThat(videoFrame.encodedHeight).isEqualTo(TEST_ENCODER_SETTINGS.height);
+ assertThat(videoFrame.rotation).isEqualTo(0);
+ assertThat(videoFrame.captureTimeNs).isEqualTo(42);
+ assertThat(videoFrame.frameType).isEqualTo(FrameType.VideoFrameKey);
+ CodecTestHelper.assertEqualContents(
+ outputData, videoFrame.buffer, /* offset= */ 0, videoFrame.buffer.capacity());
+ }
+
+ @Test
+ public void testRelease() {
+ // Set-up.
+ HardwareVideoEncoder encoder = new TestEncoderBuilder().build();
+ encoder.initEncode(TEST_ENCODER_SETTINGS, mockEncoderCallback);
+
+ // Test.
+ assertThat(encoder.release()).isEqualTo(VideoCodecStatus.OK);
+
+ // Verify.
+ assertThat(fakeMediaCodecWrapper.getState()).isEqualTo(State.RELEASED);
+ }
+
+ @Test
+ public void testReleaseMultipleTimes() {
+ // Set-up.
+ HardwareVideoEncoder encoder = new TestEncoderBuilder().build();
+ encoder.initEncode(TEST_ENCODER_SETTINGS, mockEncoderCallback);
+
+ // Test.
+ assertThat(encoder.release()).isEqualTo(VideoCodecStatus.OK);
+ assertThat(encoder.release()).isEqualTo(VideoCodecStatus.OK);
+
+ // Verify.
+ assertThat(fakeMediaCodecWrapper.getState()).isEqualTo(State.RELEASED);
+ }
+
+ @Test
+ public void testFramerateWithFramerateBitrateAdjuster() {
+ // Enable FramerateBitrateAdjuster and initialize encoder with frame rate 15fps. Vefity that our
+ // initial frame rate setting is ignored and media encoder is initialized with 30fps
+ // (FramerateBitrateAdjuster default).
+ HardwareVideoEncoder encoder =
+ new TestEncoderBuilder().setBitrateAdjuster(new FramerateBitrateAdjuster()).build();
+ encoder.initEncode(
+ new Settings(
+ /* numberOfCores= */ 1,
+ /* width= */ 640,
+ /* height= */ 480,
+ /* startBitrate= */ 10000,
+ /* maxFramerate= */ 15,
+ /* numberOfSimulcastStreams= */ 1,
+ /* automaticResizeOn= */ true,
+ /* capabilities= */ new VideoEncoder.Capabilities(false /* lossNotification */)),
+ mockEncoderCallback);
+
+ MediaFormat mediaFormat = fakeMediaCodecWrapper.getConfiguredFormat();
+ assertThat(mediaFormat.getFloat(MediaFormat.KEY_FRAME_RATE)).isEqualTo(30.0f);
+ }
+
+ @Test
+ public void testBitrateWithFramerateBitrateAdjuster() throws InterruptedException {
+ // Enable FramerateBitrateAdjuster and change frame rate while encoding video. Verify that
+ // bitrate setting passed to media encoder is adjusted to compensate for changes in frame rate.
+ TestEncoder encoder =
+ new TestEncoderBuilder().setBitrateAdjuster(new FramerateBitrateAdjuster()).build();
+ encoder.initEncode(TEST_ENCODER_SETTINGS, mockEncoderCallback);
+
+ encoder.encode(createTestVideoFrame(/* timestampNs= */ 0), ENCODE_INFO_KEY_FRAME);
+
+ // Reduce frame rate by half.
+ BitrateAllocation bitrateAllocation = new BitrateAllocation(
+ /* bitratesBbs= */ new int[][] {new int[] {TEST_ENCODER_SETTINGS.startBitrate}});
+ encoder.setRateAllocation(bitrateAllocation, TEST_ENCODER_SETTINGS.maxFramerate / 2);
+
+ // Generate output to trigger bitrate update in encoder wrapper.
+ fakeMediaCodecWrapper.addOutputData(
+ CodecTestHelper.generateRandomData(100), /* presentationTimestampUs= */ 0, /* flags= */ 0);
+ encoder.waitDeliverEncodedImage();
+
+ // Frame rate has been reduced by half. Verify that bitrate doubled.
+ ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
+ verify(fakeMediaCodecWrapper, times(2)).setParameters(bundleCaptor.capture());
+ Bundle params = bundleCaptor.getAllValues().get(1);
+ assertThat(params.containsKey(MediaCodec.PARAMETER_KEY_VIDEO_BITRATE)).isTrue();
+ assertThat(params.getInt(MediaCodec.PARAMETER_KEY_VIDEO_BITRATE))
+ .isEqualTo(TEST_ENCODER_SETTINGS.startBitrate * 2);
+ }
+
+ @Test
+ public void testTimestampsWithFramerateBitrateAdjuster() throws InterruptedException {
+ // Enable FramerateBitrateAdjuster and change frame rate while encoding video. Verify that
+ // encoder ignores changes in frame rate and calculates frame timestamps based on fixed frame
+ // rate 30fps.
+ TestEncoder encoder =
+ new TestEncoderBuilder().setBitrateAdjuster(new FramerateBitrateAdjuster()).build();
+ encoder.initEncode(TEST_ENCODER_SETTINGS, mockEncoderCallback);
+
+ encoder.encode(createTestVideoFrame(/* timestampNs= */ 0), ENCODE_INFO_KEY_FRAME);
+
+ // Reduce frametate by half.
+ BitrateAllocation bitrateAllocation = new BitrateAllocation(
+ /* bitratesBbs= */ new int[][] {new int[] {TEST_ENCODER_SETTINGS.startBitrate}});
+ encoder.setRateAllocation(bitrateAllocation, TEST_ENCODER_SETTINGS.maxFramerate / 2);
+
+ // Encoder is allowed to buffer up to 2 frames. Generate output to avoid frame dropping.
+ fakeMediaCodecWrapper.addOutputData(
+ CodecTestHelper.generateRandomData(100), /* presentationTimestampUs= */ 0, /* flags= */ 0);
+ encoder.waitDeliverEncodedImage();
+
+ encoder.encode(createTestVideoFrame(/* timestampNs= */ 1), ENCODE_INFO_DELTA_FRAME);
+ encoder.encode(createTestVideoFrame(/* timestampNs= */ 2), ENCODE_INFO_DELTA_FRAME);
+
+ ArgumentCaptor<Long> timestampCaptor = ArgumentCaptor.forClass(Long.class);
+ verify(fakeMediaCodecWrapper, times(3))
+ .queueInputBuffer(
+ /* index= */ anyInt(),
+ /* offset= */ anyInt(),
+ /* size= */ anyInt(), timestampCaptor.capture(),
+ /* flags= */ anyInt());
+
+ long frameDurationMs = SECONDS.toMicros(1) / 30;
+ assertThat(timestampCaptor.getAllValues())
+ .containsExactly(0L, frameDurationMs, 2 * frameDurationMs);
+ }
+}
diff --git a/third_party/libwebrtc/sdk/android/tests/src/org/webrtc/IceCandidateTest.java b/third_party/libwebrtc/sdk/android/tests/src/org/webrtc/IceCandidateTest.java
new file mode 100644
index 0000000000..437e146415
--- /dev/null
+++ b/third_party/libwebrtc/sdk/android/tests/src/org/webrtc/IceCandidateTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2019 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+package org.webrtc;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.runner.AndroidJUnit4;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+import org.webrtc.IceCandidate;
+
+@RunWith(AndroidJUnit4.class)
+@Config(manifest = Config.NONE)
+public class IceCandidateTest {
+ @Test
+ public void testIceCandidateEquals() {
+ IceCandidate c1 = new IceCandidate(
+ "audio", 0, "candidate:1532086002 1 udp 2122194687 192.168.86.144 37138 typ host");
+ IceCandidate c2 = new IceCandidate(
+ "audio", 0, "candidate:1532086002 1 udp 2122194687 192.168.86.144 37138 typ host");
+
+ // c3 differ by sdpMid
+ IceCandidate c3 = new IceCandidate(
+ "video", 0, "candidate:1532086002 1 udp 2122194687 192.168.86.144 37138 typ host");
+ // c4 differ by sdpMLineIndex
+ IceCandidate c4 = new IceCandidate(
+ "audio", 1, "candidate:1532086002 1 udp 2122194687 192.168.86.144 37138 typ host");
+ // c5 differ by sdp.
+ IceCandidate c5 = new IceCandidate(
+ "audio", 0, "candidate:1532086002 1 udp 2122194687 192.168.86.144 37139 typ host");
+
+ assertThat(c1.equals(c2)).isTrue();
+ assertThat(c2.equals(c1)).isTrue();
+ assertThat(c1.equals(null)).isFalse();
+ assertThat(c1.equals(c3)).isFalse();
+ assertThat(c1.equals(c4)).isFalse();
+ assertThat(c5.equals(c1)).isFalse();
+
+ Object o2 = c2;
+ assertThat(c1.equals(o2)).isTrue();
+ assertThat(o2.equals(c1)).isTrue();
+ }
+} \ No newline at end of file
diff --git a/third_party/libwebrtc/sdk/android/tests/src/org/webrtc/RefCountDelegateTest.java b/third_party/libwebrtc/sdk/android/tests/src/org/webrtc/RefCountDelegateTest.java
new file mode 100644
index 0000000000..eafd722a17
--- /dev/null
+++ b/third_party/libwebrtc/sdk/android/tests/src/org/webrtc/RefCountDelegateTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2020 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+package org.webrtc;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import androidx.test.runner.AndroidJUnit4;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+
+@RunWith(AndroidJUnit4.class)
+@Config(manifest = Config.NONE)
+public class RefCountDelegateTest {
+ @Mock Runnable mockReleaseCallback;
+ private RefCountDelegate refCountDelegate;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ refCountDelegate = new RefCountDelegate(mockReleaseCallback);
+ }
+
+ @Test
+ public void testReleaseRunsReleaseCallback() {
+ refCountDelegate.release();
+ verify(mockReleaseCallback).run();
+ }
+
+ @Test
+ public void testRetainIncreasesRefCount() {
+ refCountDelegate.retain();
+
+ refCountDelegate.release();
+ verify(mockReleaseCallback, never()).run();
+
+ refCountDelegate.release();
+ verify(mockReleaseCallback).run();
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testReleaseAfterFreeThrowsIllegalStateException() {
+ refCountDelegate.release();
+ refCountDelegate.release();
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testRetainAfterFreeThrowsIllegalStateException() {
+ refCountDelegate.release();
+ refCountDelegate.retain();
+ }
+
+ @Test
+ public void testSafeRetainBeforeFreeReturnsTrueAndIncreasesRefCount() {
+ assertThat(refCountDelegate.safeRetain()).isTrue();
+
+ refCountDelegate.release();
+ verify(mockReleaseCallback, never()).run();
+
+ refCountDelegate.release();
+ verify(mockReleaseCallback).run();
+ }
+
+ @Test
+ public void testSafeRetainAfterFreeReturnsFalse() {
+ refCountDelegate.release();
+ assertThat(refCountDelegate.safeRetain()).isFalse();
+ }
+}
diff --git a/third_party/libwebrtc/sdk/android/tests/src/org/webrtc/ScalingSettingsTest.java b/third_party/libwebrtc/sdk/android/tests/src/org/webrtc/ScalingSettingsTest.java
new file mode 100644
index 0000000000..18db61d2dc
--- /dev/null
+++ b/third_party/libwebrtc/sdk/android/tests/src/org/webrtc/ScalingSettingsTest.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2018 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+package org.webrtc;
+
+import static org.junit.Assert.assertEquals;
+
+import androidx.test.runner.AndroidJUnit4;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+import org.webrtc.VideoEncoder.ScalingSettings;
+
+@RunWith(AndroidJUnit4.class)
+@Config(manifest = Config.NONE)
+public class ScalingSettingsTest {
+ @Test
+ public void testToString() {
+ assertEquals("[ 1, 2 ]", new ScalingSettings(1, 2).toString());
+ assertEquals("OFF", ScalingSettings.OFF.toString());
+ }
+}
diff --git a/third_party/libwebrtc/sdk/android/tests/src/org/webrtc/audio/LowLatencyAudioBufferManagerTest.java b/third_party/libwebrtc/sdk/android/tests/src/org/webrtc/audio/LowLatencyAudioBufferManagerTest.java
new file mode 100644
index 0000000000..2575cea60e
--- /dev/null
+++ b/third_party/libwebrtc/sdk/android/tests/src/org/webrtc/audio/LowLatencyAudioBufferManagerTest.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2020 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+package org.webrtc.audio;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.AdditionalMatchers.gt;
+import static org.mockito.AdditionalMatchers.lt;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.media.AudioTrack;
+import android.os.Build;
+import androidx.test.runner.AndroidJUnit4;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.webrtc.audio.LowLatencyAudioBufferManager;
+
+/**
+ * Tests for LowLatencyAudioBufferManager.
+ */
+@RunWith(AndroidJUnit4.class)
+@Config(manifest = Config.NONE, sdk = Build.VERSION_CODES.O)
+public class LowLatencyAudioBufferManagerTest {
+ @Mock private AudioTrack mockAudioTrack;
+ private LowLatencyAudioBufferManager bufferManager;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ bufferManager = new LowLatencyAudioBufferManager();
+ }
+
+ @Test
+ public void testBufferSizeDecrease() {
+ when(mockAudioTrack.getUnderrunCount()).thenReturn(0);
+ when(mockAudioTrack.getBufferSizeInFrames()).thenReturn(100);
+ when(mockAudioTrack.getPlaybackRate()).thenReturn(1000);
+ for (int i = 0; i < 9; i++) {
+ bufferManager.maybeAdjustBufferSize(mockAudioTrack);
+ }
+ // Check that the buffer size was not changed yet.
+ verify(mockAudioTrack, times(0)).setBufferSizeInFrames(anyInt());
+ // After the 10th call without underruns, we expect the buffer size to decrease.
+ bufferManager.maybeAdjustBufferSize(mockAudioTrack);
+ // The expected size is 10ms below the existing size, which works out to 100 - (1000 / 100)
+ // = 90.
+ verify(mockAudioTrack, times(1)).setBufferSizeInFrames(90);
+ }
+
+ @Test
+ public void testBufferSizeNeverBelow10ms() {
+ when(mockAudioTrack.getUnderrunCount()).thenReturn(0);
+ when(mockAudioTrack.getBufferSizeInFrames()).thenReturn(11);
+ when(mockAudioTrack.getPlaybackRate()).thenReturn(1000);
+ for (int i = 0; i < 10; i++) {
+ bufferManager.maybeAdjustBufferSize(mockAudioTrack);
+ }
+ // Check that the buffer size was not set to a value below 10 ms.
+ verify(mockAudioTrack, times(0)).setBufferSizeInFrames(lt(10));
+ }
+
+ @Test
+ public void testUnderrunBehavior() {
+ when(mockAudioTrack.getUnderrunCount()).thenReturn(1);
+ when(mockAudioTrack.getBufferSizeInFrames()).thenReturn(100);
+ when(mockAudioTrack.getPlaybackRate()).thenReturn(1000);
+ bufferManager.maybeAdjustBufferSize(mockAudioTrack);
+ // Check that the buffer size was increased after the underrrun.
+ verify(mockAudioTrack, times(1)).setBufferSizeInFrames(gt(100));
+ when(mockAudioTrack.getUnderrunCount()).thenReturn(0);
+ for (int i = 0; i < 10; i++) {
+ bufferManager.maybeAdjustBufferSize(mockAudioTrack);
+ }
+ // Check that the buffer size was not changed again, even though there were no underruns for
+ // 10 calls.
+ verify(mockAudioTrack, times(1)).setBufferSizeInFrames(anyInt());
+ }
+
+ @Test
+ public void testBufferIncrease() {
+ when(mockAudioTrack.getBufferSizeInFrames()).thenReturn(100);
+ when(mockAudioTrack.getPlaybackRate()).thenReturn(1000);
+ for (int i = 1; i < 30; i++) {
+ when(mockAudioTrack.getUnderrunCount()).thenReturn(i);
+ bufferManager.maybeAdjustBufferSize(mockAudioTrack);
+ }
+ // Check that the buffer size was not increased more than 5 times.
+ verify(mockAudioTrack, times(5)).setBufferSizeInFrames(gt(100));
+ }
+}