pull/545/head
Mattia Iavarone 6 years ago
parent 59427a7e43
commit dedbd759f0
  1. 217
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/internal/DeviceEncodersTest.java
  2. 36
      cameraview/src/main/java/com/otaliastudios/cameraview/internal/DeviceEncoders.java

@ -0,0 +1,217 @@
package com.otaliastudios.cameraview.internal;
import android.media.MediaCodecInfo;
import androidx.annotation.NonNull;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.MediumTest;
import androidx.test.rule.ActivityTestRule;
import com.otaliastudios.cameraview.BaseTest;
import com.otaliastudios.cameraview.TestActivity;
import com.otaliastudios.cameraview.controls.Grid;
import com.otaliastudios.cameraview.size.Size;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.Arrays;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@RunWith(AndroidJUnit4.class)
@MediumTest
public class DeviceEncodersTest extends BaseTest {
private boolean enabled;
@Before
public void setUp() {
enabled = DeviceEncoders.ENABLED;
}
@After
public void tearDown() {
DeviceEncoders.ENABLED = enabled;
}
@NonNull
private DeviceEncoders create() {
return new DeviceEncoders("video/avc", "audio/mp4a-latm");
}
@Test
public void testGetDeviceEncoders() {
DeviceEncoders deviceEncoders = create();
if (DeviceEncoders.ENABLED) {
List<MediaCodecInfo> infos = deviceEncoders.getDeviceEncoders();
for (MediaCodecInfo info : infos) {
assertTrue(info.isEncoder());
}
}
}
@Test
public void testIsHardwareEncoder() {
DeviceEncoders deviceEncoders = create();
if (DeviceEncoders.ENABLED) {
assertFalse(deviceEncoders.isHardwareEncoder("OMX.google.encoder"));
assertTrue(deviceEncoders.isHardwareEncoder("OMX.other.encoder"));
}
}
@Test
public void testFindDeviceEncoder() {
DeviceEncoders deviceEncoders = create();
if (DeviceEncoders.ENABLED) {
List<MediaCodecInfo> allEncoders = deviceEncoders.getDeviceEncoders();
MediaCodecInfo encoder = deviceEncoders.findDeviceEncoder(allEncoders, "video/avc");
assertNotNull(encoder);
List<String> encoderTypes = Arrays.asList(encoder.getSupportedTypes());
assertTrue(encoderTypes.contains("video/avc"));
}
}
@Test
public void testGetVideoEncoder() {
if (DeviceEncoders.ENABLED) {
DeviceEncoders deviceEncoders = create();
assertNotNull(deviceEncoders.getVideoEncoder());
}
DeviceEncoders.ENABLED = false;
DeviceEncoders deviceEncoders = create();
assertNull(deviceEncoders.getVideoEncoder());
}
@Test
public void testGetAudioEncoder() {
if (DeviceEncoders.ENABLED) {
DeviceEncoders deviceEncoders = create();
assertNotNull(deviceEncoders.getAudioEncoder());
}
DeviceEncoders.ENABLED = false;
DeviceEncoders deviceEncoders = create();
assertNull(deviceEncoders.getAudioEncoder());
}
@Test
public void testGetSupportedVideoSize_disabled() {
DeviceEncoders.ENABLED = false;
DeviceEncoders deviceEncoders = create();
Size input = new Size(500, 500);
Size output = deviceEncoders.getSupportedVideoSize(input);
assertSame(input, output);
}
@Test(expected = RuntimeException.class)
public void testGetSupportedVideoSize_hugeWidth() {
DeviceEncoders deviceEncoders = create();
if (DeviceEncoders.ENABLED) {
Size input = new Size(Integer.MAX_VALUE, 500);
deviceEncoders.getSupportedVideoSize(input);
} else {
throw new RuntimeException("Test should pass.");
}
}
@Test(expected = RuntimeException.class)
public void testGetSupportedVideoSize_hugeHeight() {
DeviceEncoders deviceEncoders = create();
if (DeviceEncoders.ENABLED) {
Size input = new Size(500, Integer.MAX_VALUE);
deviceEncoders.getSupportedVideoSize(input);
} else {
throw new RuntimeException("Test should pass.");
}
}
@Test
public void testGetSupportedVideoSize_alignsSize() {
DeviceEncoders deviceEncoders = create();
if (DeviceEncoders.ENABLED) {
Size input = new Size(501, 501);
Size output = deviceEncoders.getSupportedVideoSize(input);
assertTrue(output.getWidth() >= input.getWidth());
assertTrue(output.getHeight() >= input.getHeight());
}
}
@Test
public void testGetSupportedVideoBitRate_disabled() {
DeviceEncoders.ENABLED = false;
DeviceEncoders deviceEncoders = create();
int input = 1000;
int output = deviceEncoders.getSupportedVideoBitRate(input);
assertEquals(input, output);
}
@Test
public void testGetSupportedVideoBitRate_enabled() {
DeviceEncoders deviceEncoders = create();
if (DeviceEncoders.ENABLED) {
// Ensure it's clamped: we can pass a negative value and check it's >= 0.
int input = -1000;
int output = deviceEncoders.getSupportedVideoBitRate(input);
assertNotEquals(input, output);
assertTrue(output >= 0);
}
}
@Test
public void testGetSupportedAudioBitRate_disabled() {
DeviceEncoders.ENABLED = false;
DeviceEncoders deviceEncoders = create();
int input = 1000;
int output = deviceEncoders.getSupportedAudioBitRate(input);
assertEquals(input, output);
}
@Test
public void testGetSupportedAudioBitRate_enabled() {
DeviceEncoders deviceEncoders = create();
if (DeviceEncoders.ENABLED) {
// Ensure it's clamped: we can pass a negative value and check it's >= 0.
int input = -1000;
int output = deviceEncoders.getSupportedAudioBitRate(input);
assertNotEquals(input, output);
assertTrue(output >= 0);
}
}
@Test
public void testGetSupportedFrameRate_disabled() {
DeviceEncoders.ENABLED = false;
DeviceEncoders deviceEncoders = create();
int input = 1000;
int output = deviceEncoders.getSupportedVideoFrameRate(new Size(1000, 1000), input);
assertEquals(input, output);
}
@Test
public void testGetSupportedFrameRate_enabled() {
DeviceEncoders deviceEncoders = create();
if (DeviceEncoders.ENABLED) {
// Ensure it's clamped: we can pass a negative value and check it's >= 0.
int input = -10;
Size inputSize = deviceEncoders.getSupportedVideoSize(new Size(1000, 1000));
int output = deviceEncoders.getSupportedVideoFrameRate(inputSize, input);
assertNotEquals(input, output);
assertTrue(output >= 0);
}
}
}

@ -1,20 +1,17 @@
package com.otaliastudios.cameraview.internal; package com.otaliastudios.cameraview.internal;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.media.MediaCodec;
import android.media.MediaCodecInfo; import android.media.MediaCodecInfo;
import android.media.MediaCodecList; import android.media.MediaCodecList;
import android.os.Build; import android.os.Build;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi; import androidx.annotation.VisibleForTesting;
import com.otaliastudios.cameraview.CameraLogger; import com.otaliastudios.cameraview.CameraLogger;
import com.otaliastudios.cameraview.size.Size; import com.otaliastudios.cameraview.size.Size;
import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
@ -29,7 +26,8 @@ public class DeviceEncoders {
private final static String TAG = DeviceEncoders.class.getSimpleName(); private final static String TAG = DeviceEncoders.class.getSimpleName();
private final static CameraLogger LOG = CameraLogger.create(TAG); private final static CameraLogger LOG = CameraLogger.create(TAG);
private final boolean mEnabled; @VisibleForTesting static boolean ENABLED = Build.VERSION.SDK_INT >= 21;
@SuppressWarnings("FieldCanBeLocal") @SuppressWarnings("FieldCanBeLocal")
private final MediaCodecInfo mVideoEncoder; private final MediaCodecInfo mVideoEncoder;
@SuppressWarnings("FieldCanBeLocal") @SuppressWarnings("FieldCanBeLocal")
@ -39,10 +37,9 @@ public class DeviceEncoders {
@SuppressLint("NewApi") @SuppressLint("NewApi")
public DeviceEncoders(@NonNull String videoType, @NonNull String audioType) { public DeviceEncoders(@NonNull String videoType, @NonNull String audioType) {
mEnabled = Build.VERSION.SDK_INT >= 21;
// We could still get a list of MediaCodecInfo for API >= 16, but it seems that the APIs // We could still get a list of MediaCodecInfo for API >= 16, but it seems that the APIs
// for querying the availability of a specified MediaFormat were only added in 21 anyway. // for querying the availability of a specified MediaFormat were only added in 21 anyway.
if (mEnabled) { if (ENABLED) {
List<MediaCodecInfo> encoders = getDeviceEncoders(); List<MediaCodecInfo> encoders = getDeviceEncoders();
mVideoEncoder = findDeviceEncoder(encoders, videoType); mVideoEncoder = findDeviceEncoder(encoders, videoType);
LOG.i("Enabled. Found video encoder:", mVideoEncoder.getName()); LOG.i("Enabled. Found video encoder:", mVideoEncoder.getName());
@ -63,9 +60,10 @@ public class DeviceEncoders {
* Collects all the device encoders, which means excluding decoders. * Collects all the device encoders, which means excluding decoders.
* @return encoders * @return encoders
*/ */
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@NonNull @NonNull
private List<MediaCodecInfo> getDeviceEncoders() { @SuppressLint("NewApi")
@VisibleForTesting
List<MediaCodecInfo> getDeviceEncoders() {
ArrayList<MediaCodecInfo> results = new ArrayList<>(); ArrayList<MediaCodecInfo> results = new ArrayList<>();
MediaCodecInfo[] array = new MediaCodecList(MediaCodecList.REGULAR_CODECS).getCodecInfos(); MediaCodecInfo[] array = new MediaCodecList(MediaCodecList.REGULAR_CODECS).getCodecInfos();
for (MediaCodecInfo info : array) { for (MediaCodecInfo info : array) {
@ -82,8 +80,9 @@ public class DeviceEncoders {
* @return true if hardware * @return true if hardware
*/ */
@SuppressLint("NewApi") @SuppressLint("NewApi")
private boolean isHardwareEncoder(@NonNull MediaCodecInfo encoder) { @VisibleForTesting
return !encoder.getName().startsWith("OMX.google"); boolean isHardwareEncoder(@NonNull String encoder) {
return !encoder.startsWith("OMX.google");
} }
/** /**
@ -96,7 +95,8 @@ public class DeviceEncoders {
*/ */
@SuppressLint("NewApi") @SuppressLint("NewApi")
@NonNull @NonNull
private MediaCodecInfo findDeviceEncoder(@NonNull List<MediaCodecInfo> encoders, @NonNull String mimeType) { @VisibleForTesting
MediaCodecInfo findDeviceEncoder(@NonNull List<MediaCodecInfo> encoders, @NonNull String mimeType) {
ArrayList<MediaCodecInfo> results = new ArrayList<>(); ArrayList<MediaCodecInfo> results = new ArrayList<>();
for (MediaCodecInfo encoder : encoders) { for (MediaCodecInfo encoder : encoders) {
String[] types = encoder.getSupportedTypes(); String[] types = encoder.getSupportedTypes();
@ -111,8 +111,8 @@ public class DeviceEncoders {
Collections.sort(results, new Comparator<MediaCodecInfo>() { Collections.sort(results, new Comparator<MediaCodecInfo>() {
@Override @Override
public int compare(MediaCodecInfo o1, MediaCodecInfo o2) { public int compare(MediaCodecInfo o1, MediaCodecInfo o2) {
boolean hw1 = isHardwareEncoder(o1); boolean hw1 = isHardwareEncoder(o1.getName());
boolean hw2 = isHardwareEncoder(o2); boolean hw2 = isHardwareEncoder(o2.getName());
if (hw1 && hw2) return 0; if (hw1 && hw2) return 0;
if (hw1) return -1; if (hw1) return -1;
if (hw2) return 1; if (hw2) return 1;
@ -135,7 +135,7 @@ public class DeviceEncoders {
@SuppressLint("NewApi") @SuppressLint("NewApi")
@NonNull @NonNull
public Size getSupportedVideoSize(@NonNull Size size) { public Size getSupportedVideoSize(@NonNull Size size) {
if (!mEnabled) return size; if (!ENABLED) return size;
int width = size.getWidth(); int width = size.getWidth();
int height = size.getHeight(); int height = size.getHeight();
while (width % mVideoCapabilities.getWidthAlignment() != 0) width++; while (width % mVideoCapabilities.getWidthAlignment() != 0) width++;
@ -164,7 +164,7 @@ public class DeviceEncoders {
*/ */
@SuppressLint("NewApi") @SuppressLint("NewApi")
public int getSupportedVideoBitRate(int bitRate) { public int getSupportedVideoBitRate(int bitRate) {
if (!mEnabled) return bitRate; if (!ENABLED) return bitRate;
int newBitRate = mVideoCapabilities.getBitrateRange().clamp(bitRate); int newBitRate = mVideoCapabilities.getBitrateRange().clamp(bitRate);
LOG.i("getSupportedVideoBitRate -", "inputRate:", bitRate, "adjustedRate:", newBitRate); LOG.i("getSupportedVideoBitRate -", "inputRate:", bitRate, "adjustedRate:", newBitRate);
return newBitRate; return newBitRate;
@ -179,7 +179,7 @@ public class DeviceEncoders {
*/ */
@SuppressLint("NewApi") @SuppressLint("NewApi")
public int getSupportedVideoFrameRate(@NonNull Size size, int frameRate) { public int getSupportedVideoFrameRate(@NonNull Size size, int frameRate) {
if (!mEnabled) return frameRate; if (!ENABLED) return frameRate;
int newFrameRate = (int) (double) mVideoCapabilities int newFrameRate = (int) (double) mVideoCapabilities
.getSupportedFrameRatesFor(size.getWidth(), size.getHeight()) .getSupportedFrameRatesFor(size.getWidth(), size.getHeight())
.clamp((double) frameRate); .clamp((double) frameRate);
@ -196,7 +196,7 @@ public class DeviceEncoders {
*/ */
@SuppressLint("NewApi") @SuppressLint("NewApi")
public int getSupportedAudioBitRate(int bitRate) { public int getSupportedAudioBitRate(int bitRate) {
if (!mEnabled) return bitRate; if (!ENABLED) return bitRate;
int newBitRate = mAudioCapabilities.getBitrateRange().clamp(bitRate); int newBitRate = mAudioCapabilities.getBitrateRange().clamp(bitRate);
LOG.i("getSupportedAudioBitRate -", "inputRate:", bitRate, "adjustedRate:", newBitRate); LOG.i("getSupportedAudioBitRate -", "inputRate:", bitRate, "adjustedRate:", newBitRate);
return newBitRate; return newBitRate;

Loading…
Cancel
Save