Support for higher frame rate (#653)

* added support for higher frame rate

* added fps range in camera options

* PR comments plus tests added

* PR comments

* Changes suggested
pull/662/head
Vaibhav Bhandula 5 years ago committed by Mattia Iavarone
parent 74c94e21ce
commit fa88783d37
  1. 6
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraViewTest.java
  2. 9
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/engine/CameraIntegrationTest.java
  3. 4
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/engine/MockCameraEngine.java
  4. 40
      cameraview/src/main/java/com/otaliastudios/cameraview/CameraOptions.java
  5. 20
      cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java
  6. 4
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera1Engine.java
  7. 44
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java
  8. 8
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/CameraEngine.java
  9. 1
      cameraview/src/main/res/values/attrs.xml

@ -784,6 +784,12 @@ public class CameraViewTest extends BaseTest {
assertEquals(cameraView.getVideoMaxDuration(), 5000); assertEquals(cameraView.getVideoMaxDuration(), 5000);
} }
@Test
public void testPreviewFrameRate() {
cameraView.setPreviewFrameRate(60);
assertEquals(cameraView.getPreviewFrameRate(), 60, 0);
}
//endregion //endregion
//region Lists of listeners and processors //region Lists of listeners and processors

@ -495,6 +495,15 @@ public abstract class CameraIntegrationTest extends BaseTest {
// This also ensures there are no crashes when attaching it to camera parameters. // 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 @Test
public void testSetPlaySounds() { public void testSetPlaySounds() {
controller.mPlaySoundsOp.listen(); controller.mPlaySoundsOp.listen();

@ -173,4 +173,8 @@ public class MockCameraEngine extends CameraEngine {
protected boolean collectCameraInfo(@NonNull Facing facing) { protected boolean collectCameraInfo(@NonNull Facing facing) {
return true; return true;
} }
@Override public void setPreviewFrameRate(float previewFrameRate) {
mPreviewFrameRate = previewFrameRate;
}
} }

@ -60,6 +60,8 @@ public class CameraOptions {
private float exposureCorrectionMinValue; private float exposureCorrectionMinValue;
private float exposureCorrectionMaxValue; private float exposureCorrectionMaxValue;
private boolean autoFocusSupported; private boolean autoFocusSupported;
private float fpsRangeMinValue;
private float fpsRangeMaxValue;
public CameraOptions(@NonNull Camera.Parameters params, int cameraId, boolean flipSizes) { public CameraOptions(@NonNull Camera.Parameters params, int cameraId, boolean flipSizes) {
@ -156,6 +158,10 @@ public class CameraOptions {
} }
} }
} }
//fps range
fpsRangeMinValue = 0F;
fpsRangeMaxValue = 0F;
} }
// Camera2Engine constructor. // Camera2Engine constructor.
@ -272,6 +278,24 @@ public class CameraOptions {
supportedVideoAspectRatio.add(AspectRatio.of(width, height)); supportedVideoAspectRatio.add(AspectRatio.of(width, height));
} }
} }
//fps Range
fpsRangeMinValue = Float.MAX_VALUE;
fpsRangeMaxValue = Float.MIN_VALUE;
Range<Integer>[] range = cameraCharacteristics.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES);
if (range != null) {
for (Range<Integer> fpsRange : range) {
if (fpsRange.getLower() <= fpsRangeMinValue) {
fpsRangeMinValue = fpsRange.getLower();
}
if (fpsRange.getUpper() >= fpsRangeMaxValue) {
fpsRangeMaxValue = fpsRange.getUpper();
}
}
} else {
fpsRangeMinValue = 0F;
fpsRangeMaxValue = 0F;
}
} }
/** /**
@ -498,4 +522,20 @@ public class CameraOptions {
public float getExposureCorrectionMaxValue() { public float getExposureCorrectionMaxValue() {
return exposureCorrectionMaxValue; return exposureCorrectionMaxValue;
} }
/**
* The minimum value for FPS
* @return the min value
*/
public float getFpsRangeMinValue() {
return fpsRangeMinValue;
}
/**
* The maximum value for FPS
* @return the max value
*/
public float getFpsRangeMaxValue() {
return fpsRangeMaxValue;
}
} }

@ -189,6 +189,7 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
0); 0);
int videoBitRate = a.getInteger(R.styleable.CameraView_cameraVideoBitRate, 0); int videoBitRate = a.getInteger(R.styleable.CameraView_cameraVideoBitRate, 0);
int audioBitRate = a.getInteger(R.styleable.CameraView_cameraAudioBitRate, 0); int audioBitRate = a.getInteger(R.styleable.CameraView_cameraAudioBitRate, 0);
float videoFrameRate = a.getFloat(R.styleable.CameraView_cameraPreviewFrameRate, 0);
long autoFocusResetDelay = (long) a.getInteger( long autoFocusResetDelay = (long) a.getInteger(
R.styleable.CameraView_cameraAutoFocusResetDelay, R.styleable.CameraView_cameraAutoFocusResetDelay,
(int) DEFAULT_AUTOFOCUS_RESET_DELAY_MILLIS); (int) DEFAULT_AUTOFOCUS_RESET_DELAY_MILLIS);
@ -251,6 +252,7 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
setVideoMaxDuration(videoMaxDuration); setVideoMaxDuration(videoMaxDuration);
setVideoBitRate(videoBitRate); setVideoBitRate(videoBitRate);
setAutoFocusResetDelay(autoFocusResetDelay); setAutoFocusResetDelay(autoFocusResetDelay);
setPreviewFrameRate(videoFrameRate);
// Apply gestures // Apply gestures
mapGesture(Gesture.TAP, gestures.getTapAction()); mapGesture(Gesture.TAP, gestures.getTapAction());
@ -952,6 +954,7 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
setVideoMaxDuration(oldEngine.getVideoMaxDuration()); setVideoMaxDuration(oldEngine.getVideoMaxDuration());
setVideoBitRate(oldEngine.getVideoBitRate()); setVideoBitRate(oldEngine.getVideoBitRate());
setAutoFocusResetDelay(oldEngine.getAutoFocusResetDelay()); setAutoFocusResetDelay(oldEngine.getAutoFocusResetDelay());
setPreviewFrameRate(oldEngine.getPreviewFrameRate());
} }
/** /**
@ -1446,6 +1449,23 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
return mCameraEngine.getVideoBitRate(); return mCameraEngine.getVideoBitRate();
} }
/**
* Sets the frame rate for the video
* Will be used by {@link #takeVideoSnapshot(File)}.
* @param frameRate desired frame rate
*/
public void setPreviewFrameRate(float frameRate) {
mCameraEngine.setPreviewFrameRate(frameRate);
}
/**
* Returns the current frame rate.
* @return current frame rate
*/
public float getPreviewFrameRate() {
return mCameraEngine.getPreviewFrameRate();
}
/** /**
* Sets the bit rate in bits per second for audio capturing. * Sets the bit rate in bits per second for audio capturing.
* Will be used by both {@link #takeVideo(File)} and {@link #takeVideoSnapshot(File)}. * Will be used by both {@link #takeVideo(File)} and {@link #takeVideoSnapshot(File)}.

@ -666,6 +666,10 @@ public class Camera1Engine extends CameraEngine implements
return false; return false;
} }
@Override public void setPreviewFrameRate(float previewFrameRate) {
// This method does nothing
}
//endregion //endregion
//region Frame Processing //region Frame Processing

@ -20,6 +20,7 @@ import android.media.Image;
import android.media.ImageReader; import android.media.ImageReader;
import android.os.Build; import android.os.Build;
import android.util.Pair; import android.util.Pair;
import android.util.Range;
import android.util.Rational; import android.util.Rational;
import android.view.Surface; import android.view.Surface;
import android.view.SurfaceHolder; import android.view.SurfaceHolder;
@ -82,6 +83,7 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv
private static final int FRAME_PROCESSING_FORMAT = ImageFormat.NV21; private static final int FRAME_PROCESSING_FORMAT = ImageFormat.NV21;
private static final int FRAME_PROCESSING_INPUT_FORMAT = ImageFormat.YUV_420_888; private static final int FRAME_PROCESSING_INPUT_FORMAT = ImageFormat.YUV_420_888;
private static final int DEFAULT_FRAME_RATE = 30;
@VisibleForTesting static final long METER_TIMEOUT = 2500; @VisibleForTesting static final long METER_TIMEOUT = 2500;
private final CameraManager mManager; private final CameraManager mManager;
@ -813,6 +815,7 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv
if (!(mPreview instanceof GlCameraPreview)) { if (!(mPreview instanceof GlCameraPreview)) {
throw new IllegalStateException("Video snapshots are only supported with GL_SURFACE."); throw new IllegalStateException("Video snapshots are only supported with GL_SURFACE.");
} }
stub.videoFrameRate = (int) mPreviewFrameRate;
GlCameraPreview glPreview = (GlCameraPreview) mPreview; GlCameraPreview glPreview = (GlCameraPreview) mPreview;
Size outputSize = getUncroppedSnapshotSize(Reference.OUTPUT); Size outputSize = getUncroppedSnapshotSize(Reference.OUTPUT);
if (outputSize == null) { if (outputSize == null) {
@ -913,6 +916,7 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv
applyHdr(builder, Hdr.OFF); applyHdr(builder, Hdr.OFF);
applyZoom(builder, 0F); applyZoom(builder, 0F);
applyExposureCorrection(builder, 0F); applyExposureCorrection(builder, 0F);
applyPreviewFrameRate(builder, 0F);
if (oldBuilder != null) { if (oldBuilder != null) {
// We might be in a metering operation, or the old builder might have some special // We might be in a metering operation, or the old builder might have some special
@ -1252,6 +1256,46 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv
mPlaySoundsOp.end(null); mPlaySoundsOp.end(null);
} }
@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, oldPreviewFrameRate)) {
applyRepeatingRequestBuilder();
}
}
mPreviewFrameRateOp.end(null);
}
});
}
@SuppressWarnings("WeakerAccess")
protected boolean applyPreviewFrameRate(@NonNull CaptureRequest.Builder builder, float oldPreviewFrameRate) {
Range<Integer>[] fpsRanges = mCameraCharacteristics.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES);
if (fpsRanges != null) {
if (mPreviewFrameRate != 0f) {
for (Range<Integer> fpsRange : fpsRanges) {
if (fpsRange.contains((int) mPreviewFrameRate)) {
builder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, fpsRange);
return true;
}
}
} else {
for (Range<Integer> fpsRange : fpsRanges) {
if (fpsRange.contains(DEFAULT_FRAME_RATE)) {
builder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, fpsRange);
return true;
}
}
}
}
mPreviewFrameRate = oldPreviewFrameRate;
return false;
}
//endregion //endregion
//region Frame Processing //region Frame Processing

@ -174,6 +174,7 @@ public abstract class CameraEngine implements
@SuppressWarnings("WeakerAccess") protected boolean mPlaySounds; @SuppressWarnings("WeakerAccess") protected boolean mPlaySounds;
@SuppressWarnings("WeakerAccess") protected boolean mPictureMetering; @SuppressWarnings("WeakerAccess") protected boolean mPictureMetering;
@SuppressWarnings("WeakerAccess") protected boolean mPictureSnapshotMetering; @SuppressWarnings("WeakerAccess") protected boolean mPictureSnapshotMetering;
@SuppressWarnings("WeakerAccess") protected float mPreviewFrameRate;
// Can be private // Can be private
@VisibleForTesting Handler mCrashHandler; @VisibleForTesting Handler mCrashHandler;
@ -220,6 +221,7 @@ public abstract class CameraEngine implements
@VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) Op<Void> mHdrOp = new Op<>(); @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) Op<Void> mHdrOp = new Op<>();
@VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) Op<Void> mLocationOp = new Op<>(); @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) Op<Void> mLocationOp = new Op<>();
@VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) Op<Void> mPlaySoundsOp = new Op<>(); @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) Op<Void> mPlaySoundsOp = new Op<>();
@VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) Op<Void> mPreviewFrameRateOp = new Op<>();
protected CameraEngine(@NonNull Callback callback) { protected CameraEngine(@NonNull Callback callback) {
mCallback = callback; mCallback = callback;
@ -1026,6 +1028,10 @@ public abstract class CameraEngine implements
return mExposureCorrectionValue; return mExposureCorrectionValue;
} }
public final float getPreviewFrameRate() {
return mPreviewFrameRate;
}
@CallSuper @CallSuper
public void setHasFrameProcessors(boolean hasFrameProcessors) { public void setHasFrameProcessors(boolean hasFrameProcessors) {
mHasFrameProcessors = hasFrameProcessors; mHasFrameProcessors = hasFrameProcessors;
@ -1107,6 +1113,8 @@ public abstract class CameraEngine implements
public abstract void setPlaySounds(boolean playSounds); public abstract void setPlaySounds(boolean playSounds);
public abstract void setPreviewFrameRate(float previewFrameRate);
//endregion //endregion
//region picture and video control //region picture and video control

@ -27,6 +27,7 @@
<attr name="cameraVideoBitRate" format="integer|reference" /> <attr name="cameraVideoBitRate" format="integer|reference" />
<attr name="cameraAudioBitRate" format="integer|reference" /> <attr name="cameraAudioBitRate" format="integer|reference" />
<attr name="cameraPreviewFrameRate" format="integer|reference" />
<attr name="cameraGestureTap" format="enum"> <attr name="cameraGestureTap" format="enum">
<enum name="none" value="0" /> <enum name="none" value="0" />

Loading…
Cancel
Save