Improve encoder selection

pull/588/head
Mattia Iavarone 6 years ago
parent f668d8aae1
commit 470c53c2ba
  1. 7
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/internal/DeviceEncodersTest.java
  2. 48
      cameraview/src/main/java/com/otaliastudios/cameraview/internal/DeviceEncoders.java
  3. 33
      cameraview/src/main/java/com/otaliastudios/cameraview/video/FullVideoRecorder.java
  4. 36
      cameraview/src/main/java/com/otaliastudios/cameraview/video/SnapshotVideoRecorder.java

@ -6,11 +6,8 @@ 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.AspectRatio;
import com.otaliastudios.cameraview.size.Size;
@ -30,8 +27,6 @@ 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
@ -87,7 +82,7 @@ public class DeviceEncodersTest extends BaseTest {
if (DeviceEncoders.ENABLED) {
List<MediaCodecInfo> allEncoders = deviceEncoders.getDeviceEncoders();
MediaCodecInfo encoder = deviceEncoders.findDeviceEncoder(allEncoders,
"video/avc", DeviceEncoders.MODE_TAKE_FIRST);
"video/avc", DeviceEncoders.MODE_TAKE_FIRST, 0);
assertNotNull(encoder);
List<String> encoderTypes = Arrays.asList(encoder.getSupportedTypes());
assertTrue(encoderTypes.contains("video/avc"));

@ -59,6 +59,26 @@ public class DeviceEncoders {
public final static int MODE_TAKE_FIRST = 0;
public final static int MODE_PREFER_HARDWARE = 1;
/**
* Exception thrown when trying to find appropriate values
* for a video encoder.
*/
public class VideoException extends RuntimeException {
private VideoException(@NonNull String message) {
super(message);
}
}
/**
* Exception thrown when trying to find appropriate values
* for an audio encoder. Currently never thrown.
*/
public class AudioException extends RuntimeException {
private AudioException(@NonNull String message) {
super(message);
}
}
@SuppressWarnings("FieldCanBeLocal")
private final MediaCodecInfo mVideoEncoder;
@SuppressWarnings("FieldCanBeLocal")
@ -68,13 +88,22 @@ public class DeviceEncoders {
@SuppressLint("NewApi")
public DeviceEncoders(@NonNull String videoType, @NonNull String audioType, int mode) {
this(videoType, audioType, mode, 0, 0);
}
@SuppressLint("NewApi")
public DeviceEncoders(@NonNull String videoType,
@NonNull String audioType,
int mode,
int videoOffset,
int audioOffset) {
// 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.
if (ENABLED) {
List<MediaCodecInfo> encoders = getDeviceEncoders();
mVideoEncoder = findDeviceEncoder(encoders, videoType, mode);
mVideoEncoder = findDeviceEncoder(encoders, videoType, mode, videoOffset);
LOG.i("Enabled. Found video encoder:", mVideoEncoder.getName());
mAudioEncoder = findDeviceEncoder(encoders, audioType, mode);
mAudioEncoder = findDeviceEncoder(encoders, audioType, mode, audioOffset);
LOG.i("Enabled. Found audio encoder:", mAudioEncoder.getName());
mVideoCapabilities = mVideoEncoder.getCapabilitiesForType(videoType)
.getVideoCapabilities();
@ -139,7 +168,8 @@ public class DeviceEncoders {
@VisibleForTesting
MediaCodecInfo findDeviceEncoder(@NonNull List<MediaCodecInfo> encoders,
@NonNull String mimeType,
int mode) {
int mode,
int offset) {
ArrayList<MediaCodecInfo> results = new ArrayList<>();
for (MediaCodecInfo encoder : encoders) {
String[] types = encoder.getSupportedTypes();
@ -164,10 +194,12 @@ public class DeviceEncoders {
}
});
}
if (results.isEmpty()) {
if (results.size() < offset + 1) {
// This should not be a VideoException or AudioException - we want the process
// to crash here.
throw new RuntimeException("No encoders for type:" + mimeType);
}
return results.get(0);
return results.get(offset);
}
/**
@ -203,19 +235,19 @@ public class DeviceEncoders {
// It's still possible that we're BELOW the lower.
if (!mVideoCapabilities.getSupportedWidths().contains(width)) {
throw new RuntimeException("Width not supported after adjustment." +
throw new VideoException("Width not supported after adjustment." +
" Desired:" + width +
" Range:" + mVideoCapabilities.getSupportedWidths());
}
if (!mVideoCapabilities.getSupportedHeights().contains(height)) {
throw new RuntimeException("Height not supported after adjustment." +
throw new VideoException("Height not supported after adjustment." +
" Desired:" + height +
" Range:" + mVideoCapabilities.getSupportedHeights());
}
// It's still possible that we're unsupported for other reasons.
if (!mVideoCapabilities.isSizeSupported(width, height)) {
throw new RuntimeException("Size not supported for unknown reason." +
throw new VideoException("Size not supported for unknown reason." +
" Might be an aspect ratio issue." +
" Desired size:" + new Size(width, height));
}

@ -101,14 +101,35 @@ public abstract class FullVideoRecorder extends VideoRecorder {
}
// Check DeviceEncoders support
DeviceEncoders encoders = new DeviceEncoders(videoType, audioType,
DeviceEncoders.MODE_TAKE_FIRST);
boolean flip = stub.rotation % 180 != 0;
if (flip) stub.size = stub.size.flip();
stub.size = encoders.getSupportedVideoSize(stub.size);
stub.videoBitRate = encoders.getSupportedVideoBitRate(stub.videoBitRate);
stub.audioBitRate = encoders.getSupportedAudioBitRate(stub.audioBitRate);
stub.videoFrameRate = encoders.getSupportedVideoFrameRate(stub.size, stub.videoFrameRate);
Size newVideoSize = null;
int newVideoBitRate = 0;
int newAudioBitRate = 0;
int newVideoFrameRate = 0;
int videoEncoderOffset = 0;
int audioEncoderOffset = 0;
boolean encodersFound = false;
while (!encodersFound) {
DeviceEncoders encoders = new DeviceEncoders(videoType, audioType,
DeviceEncoders.MODE_TAKE_FIRST, videoEncoderOffset, audioEncoderOffset);
try {
newVideoSize = encoders.getSupportedVideoSize(stub.size);
newVideoBitRate = encoders.getSupportedVideoBitRate(stub.videoBitRate);
newAudioBitRate = encoders.getSupportedAudioBitRate(stub.audioBitRate);
newVideoFrameRate = encoders.getSupportedVideoFrameRate(newVideoSize,
stub.videoFrameRate);
encodersFound = true;
} catch (DeviceEncoders.VideoException videoException) {
videoEncoderOffset++;
} catch (DeviceEncoders.AudioException audioException) {
audioEncoderOffset++;
}
}
stub.size = newVideoSize;
stub.videoBitRate = newVideoBitRate;
stub.audioBitRate = newAudioBitRate;
stub.videoFrameRate = newVideoFrameRate;
if (flip) stub.size = stub.size.flip();
// Set video params

@ -136,13 +136,35 @@ public class SnapshotVideoRecorder extends VideoRecorder implements RendererFram
String audioType = "audio/mp4a-latm";
// Check the availability of values
DeviceEncoders deviceEncoders = new DeviceEncoders(videoType, audioType,
DeviceEncoders.MODE_PREFER_HARDWARE);
mResult.size = deviceEncoders.getSupportedVideoSize(mResult.size);
mResult.videoBitRate = deviceEncoders.getSupportedVideoBitRate(mResult.videoBitRate);
mResult.audioBitRate = deviceEncoders.getSupportedAudioBitRate(mResult.audioBitRate);
mResult.videoFrameRate = deviceEncoders.getSupportedVideoFrameRate(mResult.size,
mResult.videoFrameRate);
Size newVideoSize = null;
int newVideoBitRate = 0;
int newAudioBitRate = 0;
int newVideoFrameRate = 0;
int videoEncoderOffset = 0;
int audioEncoderOffset = 0;
boolean encodersFound = false;
DeviceEncoders deviceEncoders = null;
while (!encodersFound) {
deviceEncoders = new DeviceEncoders(videoType, audioType,
DeviceEncoders.MODE_PREFER_HARDWARE, videoEncoderOffset,
audioEncoderOffset);
try {
newVideoSize = deviceEncoders.getSupportedVideoSize(mResult.size);
newVideoBitRate = deviceEncoders.getSupportedVideoBitRate(mResult.videoBitRate);
newAudioBitRate = deviceEncoders.getSupportedAudioBitRate(mResult.audioBitRate);
newVideoFrameRate = deviceEncoders.getSupportedVideoFrameRate(newVideoSize,
mResult.videoFrameRate);
encodersFound = true;
} catch (DeviceEncoders.VideoException videoException) {
videoEncoderOffset++;
} catch (DeviceEncoders.AudioException audioException) {
audioEncoderOffset++;
}
}
mResult.size = newVideoSize;
mResult.videoBitRate = newVideoBitRate;
mResult.audioBitRate = newAudioBitRate;
mResult.videoFrameRate = newVideoFrameRate;
// Video
TextureConfig videoConfig = new TextureConfig();

Loading…
Cancel
Save