Camera2 support for most parameters

pull/493/head
Mattia Iavarone 5 years ago
parent c353ae808e
commit 06a99be2a6
  1. 48
      cameraview/src/main/java/com/otaliastudios/cameraview/CameraOptions.java
  2. 192
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java
  3. 2
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/CameraEngine.java
  4. 58
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/Mapper.java

@ -6,6 +6,7 @@ import android.hardware.Camera;
import android.hardware.camera2.CameraAccessException; import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraManager; import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.params.StreamConfigurationMap; import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.CamcorderProfile; import android.media.CamcorderProfile;
import android.media.ImageReader; import android.media.ImageReader;
@ -102,7 +103,10 @@ public class CameraOptions {
} }
} }
// zoom
zoomSupported = params.isZoomSupported(); zoomSupported = params.isZoomSupported();
// autofocus
autoFocusSupported = params.getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_AUTO); autoFocusSupported = params.getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_AUTO);
// Exposure correction // Exposure correction
@ -158,6 +162,50 @@ public class CameraOptions {
} }
} }
// WB
int[] awbModes = cameraCharacteristics.get(CameraCharacteristics.CONTROL_AWB_AVAILABLE_MODES);
//noinspection ConstantConditions
for (int awbMode : awbModes) {
WhiteBalance value = mapper.unmapWhiteBalance(awbMode);
if (value != null) supportedWhiteBalance.add(value);
}
// Flash
supportedFlash.add(Flash.OFF);
Boolean hasFlash = cameraCharacteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE);
if (hasFlash != null && hasFlash) {
int[] aeModes = cameraCharacteristics.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES);
//noinspection ConstantConditions
for (int aeMode : aeModes) {
Flash value = mapper.unmapFlash(aeMode);
if (value != null) supportedFlash.add(value);
}
}
// HDR
supportedHdr.add(Hdr.OFF);
int[] sceneModes = cameraCharacteristics.get(CameraCharacteristics.CONTROL_AVAILABLE_SCENE_MODES);
//noinspection ConstantConditions
for (int sceneMode : sceneModes) {
Hdr value = mapper.unmapHdr(sceneMode);
if (value != null) supportedHdr.add(value);
}
// TODO zoom
// autofocus
int[] afModes = cameraCharacteristics.get(CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES);
autoFocusSupported = false;
//noinspection ConstantConditions
for (int afMode : afModes) {
if (afMode == CameraCharacteristics.CONTROL_AF_MODE_AUTO) {
autoFocusSupported = true;
}
}
// TODO exposure correction
// Picture Sizes // Picture Sizes
StreamConfigurationMap streamMap = cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); StreamConfigurationMap streamMap = cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
if (streamMap == null) throw new RuntimeException("StreamConfigurationMap is null. Should not happen."); if (streamMap == null) throw new RuntimeException("StreamConfigurationMap is null. Should not happen.");

@ -42,7 +42,6 @@ import com.otaliastudios.cameraview.video.Full2VideoRecorder;
import com.otaliastudios.cameraview.video.SnapshotVideoRecorder; import com.otaliastudios.cameraview.video.SnapshotVideoRecorder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
@ -53,7 +52,10 @@ import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi; import androidx.annotation.RequiresApi;
import androidx.annotation.WorkerThread; import androidx.annotation.WorkerThread;
// TODO parameters // TODO fix flash, it's not that simple
// TODO zoom
// TODO exposure correction
// TODO autofocus
// TODO pictures // TODO pictures
@RequiresApi(Build.VERSION_CODES.LOLLIPOP) @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
public class Camera2Engine extends CameraEngine { public class Camera2Engine extends CameraEngine {
@ -64,6 +66,7 @@ public class Camera2Engine extends CameraEngine {
private final CameraManager mManager; private final CameraManager mManager;
private String mCameraId; private String mCameraId;
private CameraDevice mCamera; private CameraDevice mCamera;
private CameraCharacteristics mCameraCharacteristics;
private CameraCaptureSession mSession; private CameraCaptureSession mSession;
private CaptureRequest.Builder mPreviewStreamRequestBuilder; private CaptureRequest.Builder mPreviewStreamRequestBuilder;
private CaptureRequest mPreviewStreamRequest; private CaptureRequest mPreviewStreamRequest;
@ -198,16 +201,10 @@ public class Camera2Engine extends CameraEngine {
// Set parameters that might have been set before the camera was opened. // Set parameters that might have been set before the camera was opened.
try { try {
LOG.i("createCamera:", "Applying default parameters."); LOG.i("createCamera:", "Applying default parameters.");
CameraCharacteristics characteristics = mManager.getCameraCharacteristics(mCameraId); mCameraCharacteristics = mManager.getCameraCharacteristics(mCameraId);
mCameraOptions = new CameraOptions(mManager, mCameraId, flip(REF_SENSOR, REF_VIEW)); mCameraOptions = new CameraOptions(mManager, mCameraId, flip(REF_SENSOR, REF_VIEW));
// applyDefaultFocus(params); TODO mPreviewStreamRequestBuilder = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
// applyFlash(params, Flash.OFF); applyAll(mPreviewStreamRequestBuilder);
// applyLocation(params, null);
// applyWhiteBalance(params, WhiteBalance.AUTO);
// applyHdr(params, Hdr.OFF);
// applyPlaySounds(mPlaySounds);
// params.setRecordingHint(mMode == Mode.VIDEO);
// mCamera.setParameters(params);
} catch (CameraAccessException e) { } catch (CameraAccessException e) {
task.trySetException(createCameraException(e)); task.trySetException(createCameraException(e));
return; return;
@ -294,7 +291,6 @@ public class Camera2Engine extends CameraEngine {
if (captureSurface != null) outputSurfaces.add(captureSurface); if (captureSurface != null) outputSurfaces.add(captureSurface);
try { try {
mPreviewStreamRequestBuilder = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
mPreviewStreamRequestBuilder.addTarget(mPreviewStreamSurface); mPreviewStreamRequestBuilder.addTarget(mPreviewStreamSurface);
// null handler means using the current looper which is totally ok. // null handler means using the current looper which is totally ok.
@ -394,8 +390,8 @@ public class Camera2Engine extends CameraEngine {
mFullVideoPersistentSurface.release(); mFullVideoPersistentSurface.release();
mFullVideoPersistentSurface = null; mFullVideoPersistentSurface = null;
} }
mPreviewStreamRequestBuilder.removeTarget(mPreviewStreamSurface);
mPreviewStreamSurface = null; mPreviewStreamSurface = null;
mPreviewStreamRequestBuilder = null;
mPreviewStreamSize = null; mPreviewStreamSize = null;
mCaptureSize = null; mCaptureSize = null;
mSession.close(); mSession.close();
@ -418,6 +414,7 @@ public class Camera2Engine extends CameraEngine {
mCamera = null; mCamera = null;
mCameraOptions = null; mCameraOptions = null;
mVideoRecorder = null; mVideoRecorder = null;
mPreviewStreamRequestBuilder = null;
LOG.w("onStopEngine:", "Returning."); LOG.w("onStopEngine:", "Returning.");
return Tasks.forResult(null); return Tasks.forResult(null);
} }
@ -472,6 +469,7 @@ public class Camera2Engine extends CameraEngine {
Full2VideoRecorder recorder = (Full2VideoRecorder) mVideoRecorder; Full2VideoRecorder recorder = (Full2VideoRecorder) mVideoRecorder;
try { try {
CaptureRequest.Builder builder = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_RECORD); CaptureRequest.Builder builder = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
applyAll(builder);
//noinspection ConstantConditions //noinspection ConstantConditions
builder.addTarget(recorder.getInputSurface()); builder.addTarget(recorder.getInputSurface());
builder.addTarget(mPreviewStreamSurface); builder.addTarget(mPreviewStreamSurface);
@ -541,54 +539,202 @@ public class Camera2Engine extends CameraEngine {
//endregion //endregion
//region Parameters
@Override private void syncStream() {
public void onBufferAvailable(@NonNull byte[] buffer) { if (getPreviewState() == STATE_STARTED) {
try {
// TODO if we are capturing a video or a picture, this is not what we should do!
mPreviewStreamRequest = mPreviewStreamRequestBuilder.build();
mSession.setRepeatingRequest(mPreviewStreamRequest, null, null);
} catch (Exception e) {
throw new CameraException(e, CameraException.REASON_DISCONNECTED);
}
}
}
private void applyAll(@NonNull CaptureRequest.Builder builder) {
builder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO);
applyDefaultFocus(builder);
applyFlash(builder, Flash.OFF);
applyLocation(builder, null);
applyWhiteBalance(builder, WhiteBalance.AUTO);
applyHdr(builder, Hdr.OFF);
} }
@Override private void applyDefaultFocus(@NonNull CaptureRequest.Builder builder) {
public void setZoom(float zoom, @Nullable PointF[] points, boolean notify) { int[] modesArray = readCharacteristic(mCameraCharacteristics, CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES, new int[]{});
List<Integer> modes = new ArrayList<>();
for (int mode : modesArray) { modes.add(mode); }
if (mMode == Mode.VIDEO &&
modes.contains(CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_VIDEO)) {
builder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_VIDEO);
return;
}
if (modes.contains(CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE)) {
builder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
return;
} }
@Override if (modes.contains(CaptureRequest.CONTROL_AF_MODE_AUTO)) {
public void setExposureCorrection(float EVvalue, @NonNull float[] bounds, @Nullable PointF[] points, boolean notify) { builder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_AUTO);
return;
}
if (modes.contains(CaptureRequest.CONTROL_AF_MODE_OFF)) {
builder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_OFF);
builder.set(CaptureRequest.LENS_FOCUS_DISTANCE, 0F);
//noinspection UnnecessaryReturnStatement
return;
}
} }
@Override @Override
public void setFlash(@NonNull Flash flash) { public void setFlash(@NonNull Flash flash) {
final Flash old = mFlash;
mFlash = flash;
mHandler.run(new Runnable() {
@Override
public void run() {
if (getEngineState() == STATE_STARTED) {
if (applyFlash(mPreviewStreamRequestBuilder, old)) syncStream();
}
mFlashOp.end(null);
}
});
}
/**
* This sets the CONTROL_AE_MODE to either:
* - {@link CaptureRequest#CONTROL_AE_MODE_ON}
* - {@link CaptureRequest#CONTROL_AE_MODE_ON_AUTO_FLASH}
* - {@link CaptureRequest#CONTROL_AE_MODE_ON_ALWAYS_FLASH}
*/
private boolean applyFlash(@NonNull CaptureRequest.Builder builder,
@NonNull Flash oldFlash) {
if (mCameraOptions.supports(mFlash)) {
List<Integer> modes = mMapper.map(mFlash);
int[] availableModes = readCharacteristic(mCameraCharacteristics, CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES, new int[]{});
for (int mode : modes) {
for (int availableMode : availableModes) {
if (mode == availableMode) {
builder.set(CaptureRequest.CONTROL_AE_MODE, mode);
return true;
}
}
}
}
mFlash = oldFlash;
return false;
}
@Override
public void setLocation(@Nullable Location location) {
final Location old = mLocation;
mLocation = location;
mHandler.run(new Runnable() {
@Override
public void run() {
if (getEngineState() == STATE_STARTED) {
if (applyLocation(mPreviewStreamRequestBuilder, old)) syncStream();
}
mLocationOp.end(null);
}
});
}
private boolean applyLocation(@NonNull CaptureRequest.Builder builder,
@SuppressWarnings("unused") @Nullable Location oldLocation) {
if (mLocation != null) {
builder.set(CaptureRequest.JPEG_GPS_LOCATION, mLocation);
}
return true;
} }
@Override @Override
public void setWhiteBalance(@NonNull WhiteBalance whiteBalance) { public void setWhiteBalance(@NonNull WhiteBalance whiteBalance) {
final WhiteBalance old = mWhiteBalance;
mWhiteBalance = whiteBalance;
mHandler.run(new Runnable() {
@Override
public void run() {
if (getEngineState() == STATE_STARTED) {
if (applyWhiteBalance(mPreviewStreamRequestBuilder, old)) syncStream();
}
mWhiteBalanceOp.end(null);
}
});
}
private boolean applyWhiteBalance(@NonNull CaptureRequest.Builder builder,
@NonNull WhiteBalance oldWhiteBalance) {
if (mCameraOptions.supports(mWhiteBalance)) {
Integer whiteBalance = mMapper.map(mWhiteBalance);
builder.set(CaptureRequest.CONTROL_AWB_MODE, whiteBalance);
return true;
}
mWhiteBalance = oldWhiteBalance;
return false;
} }
@Override @Override
public void setHdr(@NonNull Hdr hdr) { public void setHdr(@NonNull Hdr hdr) {
final Hdr old = mHdr;
mHdr = hdr;
mHandler.run(new Runnable() {
@Override
public void run() {
if (getEngineState() == STATE_STARTED) {
if (applyHdr(mPreviewStreamRequestBuilder, old)) syncStream();
}
mHdrOp.end(null);
}
});
}
private boolean applyHdr( @NonNull CaptureRequest.Builder builder, @NonNull Hdr oldHdr) {
if (mCameraOptions.supports(mHdr)) {
Integer hdr = mMapper.map(mHdr);
builder.set(CaptureRequest.CONTROL_SCENE_MODE, hdr);
return true;
}
mHdr = oldHdr;
return false;
}
@Override
public void setPlaySounds(boolean playSounds) {
mPlaySounds = playSounds;
mPlaySoundsOp.end(null);
} }
//endregion
@Override @Override
public void setLocation(@Nullable Location location) { public void onBufferAvailable(@NonNull byte[] buffer) {
} }
@Override @Override
public void takePicture(@NonNull PictureResult.Stub stub) { public void setZoom(float zoom, @Nullable PointF[] points, boolean notify) {
} }
@Override @Override
public void startAutoFocus(@Nullable Gesture gesture, @NonNull PointF point) { public void setExposureCorrection(float EVvalue, @NonNull float[] bounds, @Nullable PointF[] points, boolean notify) {
} }
@Override @Override
public void setPlaySounds(boolean playSounds) { public void takePicture(@NonNull PictureResult.Stub stub) {
}
@Override
public void startAutoFocus(@Nullable Gesture gesture, @NonNull PointF point) {
} }
} }

@ -1,7 +1,9 @@
package com.otaliastudios.cameraview.engine; package com.otaliastudios.cameraview.engine;
import android.content.Context; import android.content.Context;
import android.graphics.Camera;
import android.graphics.PointF; import android.graphics.PointF;
import android.hardware.camera2.CameraCharacteristics;
import android.location.Location; import android.location.Location;

@ -4,13 +4,17 @@ import android.hardware.Camera;
import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CameraCharacteristics;
import android.os.Build; import android.os.Build;
import com.otaliastudios.cameraview.controls.Control;
import com.otaliastudios.cameraview.controls.Engine; import com.otaliastudios.cameraview.controls.Engine;
import com.otaliastudios.cameraview.controls.Facing; import com.otaliastudios.cameraview.controls.Facing;
import com.otaliastudios.cameraview.controls.Flash; import com.otaliastudios.cameraview.controls.Flash;
import com.otaliastudios.cameraview.controls.Hdr; import com.otaliastudios.cameraview.controls.Hdr;
import com.otaliastudios.cameraview.controls.WhiteBalance; import com.otaliastudios.cameraview.controls.WhiteBalance;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi; import androidx.annotation.RequiresApi;
@ -54,8 +58,8 @@ public abstract class Mapper {
public abstract <T> Hdr unmapHdr(T cameraConstant); public abstract <T> Hdr unmapHdr(T cameraConstant);
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
protected <T> T reverseLookup(HashMap<T, ?> map, Object object) { protected <C extends Control, T> C reverseLookup(HashMap<C, T> map, T object) {
for (T value : map.keySet()) { for (C value : map.keySet()) {
if (object.equals(map.get(value))) { if (object.equals(map.get(value))) {
return value; return value;
} }
@ -63,6 +67,20 @@ public abstract class Mapper {
return null; return null;
} }
@SuppressWarnings("WeakerAccess")
protected <C extends Control, T> C reverseListLookup(HashMap<C, List<T>> map, T object) {
for (C value : map.keySet()) {
List<T> list = map.get(value);
if (list == null) continue;
for (T candidate : list) {
if (object.equals(candidate)) {
return value;
}
}
}
return null;
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private static class Camera1Mapper extends Mapper { private static class Camera1Mapper extends Mapper {
@ -113,22 +131,22 @@ public abstract class Mapper {
@Override @Override
public <T> Flash unmapFlash(T cameraConstant) { public <T> Flash unmapFlash(T cameraConstant) {
return reverseLookup(FLASH, cameraConstant); return reverseLookup(FLASH, (String) cameraConstant);
} }
@Override @Override
public <T> Facing unmapFacing(T cameraConstant) { public <T> Facing unmapFacing(T cameraConstant) {
return reverseLookup(FACING, cameraConstant); return reverseLookup(FACING, (Integer) cameraConstant);
} }
@Override @Override
public <T> WhiteBalance unmapWhiteBalance(T cameraConstant) { public <T> WhiteBalance unmapWhiteBalance(T cameraConstant) {
return reverseLookup(WB, cameraConstant); return reverseLookup(WB, (String) cameraConstant);
} }
@Override @Override
public <T> Hdr unmapHdr(T cameraConstant) { public <T> Hdr unmapHdr(T cameraConstant) {
return reverseLookup(HDR, cameraConstant); return reverseLookup(HDR, (String) cameraConstant);
} }
} }
@ -136,16 +154,30 @@ public abstract class Mapper {
@RequiresApi(Build.VERSION_CODES.LOLLIPOP) @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
private static class Camera2Mapper extends Mapper { private static class Camera2Mapper extends Mapper {
private static final HashMap<Flash, List<Integer>> FLASH = new HashMap<>();
private static final HashMap<Facing, Integer> FACING = new HashMap<>(); private static final HashMap<Facing, Integer> FACING = new HashMap<>();
private static final HashMap<WhiteBalance, Integer> WB = new HashMap<>();
private static final HashMap<Hdr, Integer> HDR = new HashMap<>();
static { static {
FLASH.put(Flash.OFF, Arrays.asList(CameraCharacteristics.CONTROL_AE_MODE_ON, CameraCharacteristics.CONTROL_AE_MODE_OFF));
FLASH.put(Flash.AUTO, Arrays.asList(CameraCharacteristics.CONTROL_AE_MODE_ON_AUTO_FLASH, CameraCharacteristics.CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE));
FLASH.put(Flash.ON, Collections.singletonList(CameraCharacteristics.CONTROL_AE_MODE_ON_ALWAYS_FLASH));
FACING.put(Facing.BACK, CameraCharacteristics.LENS_FACING_BACK); FACING.put(Facing.BACK, CameraCharacteristics.LENS_FACING_BACK);
FACING.put(Facing.FRONT, CameraCharacteristics.LENS_FACING_FRONT); FACING.put(Facing.FRONT, CameraCharacteristics.LENS_FACING_FRONT);
WB.put(WhiteBalance.AUTO, CameraCharacteristics.CONTROL_AWB_MODE_AUTO);
WB.put(WhiteBalance.CLOUDY, CameraCharacteristics.CONTROL_AWB_MODE_CLOUDY_DAYLIGHT);
WB.put(WhiteBalance.DAYLIGHT, CameraCharacteristics.CONTROL_AWB_MODE_DAYLIGHT);
WB.put(WhiteBalance.FLUORESCENT, CameraCharacteristics.CONTROL_AWB_MODE_FLUORESCENT);
WB.put(WhiteBalance.INCANDESCENT, CameraCharacteristics.CONTROL_AWB_MODE_INCANDESCENT);
HDR.put(Hdr.OFF, CameraCharacteristics.CONTROL_SCENE_MODE_DISABLED);
HDR.put(Hdr.ON, 18 /* CameraCharacteristics.CONTROL_SCENE_MODE_HDR */);
} }
@SuppressWarnings("ConstantConditions")
@Override @Override
public <T> T map(Flash flash) { public <T> T map(Flash flash) {
return null; return (T) FLASH.get(flash);
} }
@Override @Override
@ -155,32 +187,32 @@ public abstract class Mapper {
@Override @Override
public <T> T map(WhiteBalance whiteBalance) { public <T> T map(WhiteBalance whiteBalance) {
return null; return (T) WB.get(whiteBalance);
} }
@Override @Override
public <T> T map(Hdr hdr) { public <T> T map(Hdr hdr) {
return null; return (T) HDR.get(hdr);
} }
@Override @Override
public <T> Flash unmapFlash(T cameraConstant) { public <T> Flash unmapFlash(T cameraConstant) {
return null; return reverseListLookup(FLASH, (Integer) cameraConstant);
} }
@Override @Override
public <T> Facing unmapFacing(T cameraConstant) { public <T> Facing unmapFacing(T cameraConstant) {
return reverseLookup(FACING, cameraConstant); return reverseLookup(FACING, (Integer) cameraConstant);
} }
@Override @Override
public <T> WhiteBalance unmapWhiteBalance(T cameraConstant) { public <T> WhiteBalance unmapWhiteBalance(T cameraConstant) {
return null; return reverseLookup(WB, (Integer) cameraConstant);
} }
@Override @Override
public <T> Hdr unmapHdr(T cameraConstant) { public <T> Hdr unmapHdr(T cameraConstant) {
return null; return reverseLookup(HDR, (Integer) cameraConstant);
} }
} }
} }

Loading…
Cancel
Save