diff --git a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraViewTest.java b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraViewTest.java index 654dda74..e92d19ff 100644 --- a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraViewTest.java +++ b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraViewTest.java @@ -784,6 +784,12 @@ public class CameraViewTest extends BaseTest { assertEquals(cameraView.getVideoMaxDuration(), 5000); } + @Test + public void testPreviewFrameRate() { + cameraView.setPreviewFrameRate(60); + assertEquals(cameraView.getPreviewFrameRate(), 60, 0); + } + //endregion //region Lists of listeners and processors diff --git a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/engine/CameraIntegrationTest.java b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/engine/CameraIntegrationTest.java index 310cd6ab..59c42f1a 100644 --- a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/engine/CameraIntegrationTest.java +++ b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/engine/CameraIntegrationTest.java @@ -495,6 +495,15 @@ public abstract class CameraIntegrationTest extends BaseTest { // This also ensures there are no crashes when attaching it to camera parameters. } + @Test + public void testSetPreviewFrameRate() { + openSync(true); + controller.mPreviewFrameRateOp.listen(); + camera.setPreviewFrameRate(30); + controller.mPreviewFrameRateOp.await(300); + assertEquals(camera.getPreviewFrameRate(), 30, 0); + } + @Test public void testSetPlaySounds() { controller.mPlaySoundsOp.listen(); diff --git a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/engine/MockCameraEngine.java b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/engine/MockCameraEngine.java index 12dcd5e4..2ad4d168 100644 --- a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/engine/MockCameraEngine.java +++ b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/engine/MockCameraEngine.java @@ -173,4 +173,8 @@ public class MockCameraEngine extends CameraEngine { protected boolean collectCameraInfo(@NonNull Facing facing) { return true; } + + @Override public void setPreviewFrameRate(float previewFrameRate) { + mPreviewFrameRate = previewFrameRate; + } } diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraOptions.java b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraOptions.java index 7087373a..d95a4af8 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraOptions.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraOptions.java @@ -54,13 +54,14 @@ public class CameraOptions { private Set supportedVideoSizes = new HashSet<>(5); private Set supportedPictureAspectRatio = new HashSet<>(4); private Set supportedVideoAspectRatio = new HashSet<>(3); - private Set> supportedFpsRange = new HashSet<>(4); private boolean zoomSupported; private boolean exposureCorrectionSupported; private float exposureCorrectionMinValue; private float exposureCorrectionMaxValue; private boolean autoFocusSupported; + private float fpsRangeMinValue; + private float fpsRangeMaxValue; public CameraOptions(@NonNull Camera.Parameters params, int cameraId, boolean flipSizes) { @@ -157,6 +158,10 @@ public class CameraOptions { } } } + + //fps range + fpsRangeMinValue = 0F; + fpsRangeMaxValue = 0F; } // Camera2Engine constructor. @@ -275,8 +280,22 @@ public class CameraOptions { } //fps Range + fpsRangeMinValue = Float.MAX_VALUE; + fpsRangeMaxValue = Float.MIN_VALUE; Range[] range = cameraCharacteristics.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES); - supportedFpsRange.addAll(Arrays.asList(range)); + if (range != null) { + for (Range fpsRange : range) { + if (fpsRange.getLower() <= fpsRangeMinValue) { + fpsRangeMinValue = fpsRange.getLower(); + } + if (fpsRange.getUpper() >= fpsRangeMaxValue) { + fpsRangeMaxValue = fpsRange.getUpper(); + } + } + } else { + fpsRangeMinValue = 0F; + fpsRangeMaxValue = 0F; + } } /** @@ -505,11 +524,18 @@ public class CameraOptions { } /** - * Set of supported FPS Range - * @return a collection of fps range + * The minimum value for FPS + * @return the min value */ - @NonNull - public Collection> getSupportedFpsRange() { - return Collections.unmodifiableSet(supportedFpsRange); + public float getFpsRangeMinValue() { + return fpsRangeMinValue; + } + + /** + * The maximum value for FPS + * @return the max value + */ + public float getFpsRangeMaxValue() { + return fpsRangeMaxValue; } } diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java index 271650f8..27704ff0 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java @@ -189,7 +189,7 @@ public class CameraView extends FrameLayout implements LifecycleObserver { 0); int videoBitRate = a.getInteger(R.styleable.CameraView_cameraVideoBitRate, 0); int audioBitRate = a.getInteger(R.styleable.CameraView_cameraAudioBitRate, 0); - float videoFrameRate = a.getFloat(R.styleable.CameraView_cameraVideoFrameRate, 30); + float videoFrameRate = a.getFloat(R.styleable.CameraView_cameraPreviewFrameRate, 0); long autoFocusResetDelay = (long) a.getInteger( R.styleable.CameraView_cameraAutoFocusResetDelay, (int) DEFAULT_AUTOFOCUS_RESET_DELAY_MILLIS); diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java index 02a3bfaf..cddc5277 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java @@ -69,7 +69,6 @@ import com.otaliastudios.cameraview.video.Full2VideoRecorder; import com.otaliastudios.cameraview.video.SnapshotVideoRecorder; import java.util.ArrayList; -import java.util.Collection; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.CopyOnWriteArrayList; @@ -118,9 +117,6 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv private final List mActions = new CopyOnWriteArrayList<>(); private MeterAction mMeterAction; - // Frame rate - private boolean mIsHigherFrameRateSupported = false; - public Camera2Engine(Callback callback) { super(callback); mManager = (CameraManager) mCallback.getContext().getSystemService(Context.CAMERA_SERVICE); @@ -818,9 +814,7 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv if (!(mPreview instanceof GlCameraPreview)) { throw new IllegalStateException("Video snapshots are only supported with GL_SURFACE."); } - if (mIsHigherFrameRateSupported) { - stub.videoFrameRate = (int) mPreviewFrameRate; - } + stub.videoFrameRate = (int) mPreviewFrameRate; GlCameraPreview glPreview = (GlCameraPreview) mPreview; Size outputSize = getUncroppedSnapshotSize(Reference.OUTPUT); if (outputSize == null) { @@ -921,7 +915,7 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv applyHdr(builder, Hdr.OFF); applyZoom(builder, 0F); applyExposureCorrection(builder, 0F); - applyPreviewFrameRate(builder); + applyPreviewFrameRate(builder, 0F); if (oldBuilder != null) { // We might be in a metering operation, or the old builder might have some special @@ -1262,29 +1256,29 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv } @Override public void setPreviewFrameRate(float previewFrameRate) { + final float oldPreviewFrameRate = mPreviewFrameRate; mPreviewFrameRate = previewFrameRate; mHandler.run(new Runnable() { @Override public void run() { if (getEngineState() == STATE_STARTED) { - if (applyPreviewFrameRate(mRepeatingRequestBuilder)) { + if (applyPreviewFrameRate(mRepeatingRequestBuilder, oldPreviewFrameRate)) { applyRepeatingRequestBuilder(); } } + mPreviewFrameRateOp.end(null); } }); } @SuppressWarnings("WeakerAccess") - protected boolean applyPreviewFrameRate(@NonNull CaptureRequest.Builder builder) { - Collection> fpsRange = mCameraOptions.getSupportedFpsRange(); - for (Range range : fpsRange) { - if (range.getUpper() >= mPreviewFrameRate) { - mIsHigherFrameRateSupported = true; - builder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, range); - return true; - } + protected boolean applyPreviewFrameRate(@NonNull CaptureRequest.Builder builder, float oldPreviewFrameRate) { + Range range = new Range<>((int) mCameraOptions.getFpsRangeMinValue(), (int) mCameraOptions.getFpsRangeMaxValue()); + if (range.contains((int) mPreviewFrameRate) || mPreviewFrameRate == 0f) { + builder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, range); + return true; } + mPreviewFrameRate = oldPreviewFrameRate; return false; } diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/CameraEngine.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/CameraEngine.java index 226718fa..419b61a5 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/CameraEngine.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/CameraEngine.java @@ -221,6 +221,7 @@ public abstract class CameraEngine implements @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) Op mHdrOp = new Op<>(); @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) Op mLocationOp = new Op<>(); @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) Op mPlaySoundsOp = new Op<>(); + @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) Op mPreviewFrameRateOp = new Op<>(); protected CameraEngine(@NonNull Callback callback) { mCallback = callback; diff --git a/cameraview/src/main/res/values/attrs.xml b/cameraview/src/main/res/values/attrs.xml index 17a34f1c..8cbc871a 100644 --- a/cameraview/src/main/res/values/attrs.xml +++ b/cameraview/src/main/res/values/attrs.xml @@ -27,7 +27,7 @@ - +