Refactor Camera2Mapper

pull/574/head
Mattia Iavarone 6 years ago
parent 314ae7f0fe
commit 3849e549c4
  1. 10
      cameraview/src/main/java/com/otaliastudios/cameraview/CameraOptions.java
  2. 36
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java
  3. 265
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/mappers/Camera2Mapper.java

@ -177,14 +177,8 @@ public class CameraOptions {
int[] aeModes = cameraCharacteristics.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES); int[] aeModes = cameraCharacteristics.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES);
//noinspection ConstantConditions //noinspection ConstantConditions
for (int aeMode : aeModes) { for (int aeMode : aeModes) {
Flash value = mapper.unmapFlash(aeMode); Set<Flash> flashes = mapper.unmapFlash(aeMode);
if (value != null) supportedFlash.add(value); supportedFlash.addAll(flashes);
}
// Check for torch specifically since the Mapper flash support is not so good.
// If OFF works, it means we have AE_MODE_OFF or AE_MODE_ON. This means we can use
// the torch control.
if (supportedFlash.contains(Flash.OFF)) {
supportedFlash.add(Flash.TORCH);
} }
} }

@ -19,6 +19,7 @@ import android.location.Location;
import android.media.Image; 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.Rational; import android.util.Rational;
import android.view.Surface; import android.view.Surface;
import android.view.SurfaceHolder; import android.view.SurfaceHolder;
@ -60,6 +61,7 @@ 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;
@ -871,23 +873,18 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv
private boolean applyFlash(@NonNull CaptureRequest.Builder builder, private boolean applyFlash(@NonNull CaptureRequest.Builder builder,
@NonNull Flash oldFlash) { @NonNull Flash oldFlash) {
if (mCameraOptions.supports(mFlash)) { if (mCameraOptions.supports(mFlash)) {
List<Integer> modes = mMapper.map(mFlash); int[] availableAeModesArray = readCharacteristic(
int[] availableModes = readCharacteristic(CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES, new int[]{}); CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES, new int[]{});
for (int mode : modes) { List<Integer> availableAeModes = new ArrayList<>();
for (int availableMode : availableModes) { for (int mode : availableAeModesArray) { availableAeModes.add(mode); }
if (mode == availableMode) {
LOG.i("applyFlash: setting CONTROL_AE_MODE to", mode); List<Pair<Integer, Integer>> pairs = mMapper.map(mFlash);
builder.set(CaptureRequest.CONTROL_AE_MODE, mode); for (Pair<Integer, Integer> pair : pairs) {
if (mFlash == Flash.TORCH) { if (availableAeModes.contains(pair.first)) {
builder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_TORCH); LOG.i("applyFlash: setting CONTROL_AE_MODE to", pair.first);
LOG.i("applyFlash: setting FLASH_MODE to", CaptureRequest.FLASH_MODE_TORCH); LOG.i("applyFlash: setting FLASH_MODE to", pair.second);
} else if (mFlash == Flash.OFF) { builder.set(CaptureRequest.CONTROL_AE_MODE, pair.first);
builder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF); builder.set(CaptureRequest.FLASH_MODE, pair.second);
LOG.i("applyFlash: setting FLASH_MODE to", CaptureRequest.FLASH_MODE_OFF);
} else { // Go to OFF anyway, the AE mode will deal with flash.
builder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF);
LOG.i("applyFlash: setting FLASH_MODE to", CaptureRequest.FLASH_MODE_OFF);
}
// On some devices, switching from TORCH/OFF to AUTO/ON is not immediately // On some devices, switching from TORCH/OFF to AUTO/ON is not immediately
// reflected (for example, torch stays active) unless we do as follows. // reflected (for example, torch stays active) unless we do as follows.
@ -898,7 +895,6 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv
} }
} }
} }
}
mFlash = oldFlash; mFlash = oldFlash;
return false; return false;
} }
@ -948,7 +944,7 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv
private boolean applyWhiteBalance(@NonNull CaptureRequest.Builder builder, private boolean applyWhiteBalance(@NonNull CaptureRequest.Builder builder,
@NonNull WhiteBalance oldWhiteBalance) { @NonNull WhiteBalance oldWhiteBalance) {
if (mCameraOptions.supports(mWhiteBalance)) { if (mCameraOptions.supports(mWhiteBalance)) {
Integer whiteBalance = mMapper.map(mWhiteBalance); int whiteBalance = mMapper.map(mWhiteBalance);
builder.set(CaptureRequest.CONTROL_AWB_MODE, whiteBalance); builder.set(CaptureRequest.CONTROL_AWB_MODE, whiteBalance);
return true; return true;
} }
@ -976,7 +972,7 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv
private boolean applyHdr(@NonNull CaptureRequest.Builder builder, @NonNull Hdr oldHdr) { private boolean applyHdr(@NonNull CaptureRequest.Builder builder, @NonNull Hdr oldHdr) {
if (mCameraOptions.supports(mHdr)) { if (mCameraOptions.supports(mHdr)) {
Integer hdr = mMapper.map(mHdr); int hdr = mMapper.map(mHdr);
builder.set(CaptureRequest.CONTROL_SCENE_MODE, hdr); builder.set(CaptureRequest.CONTROL_SCENE_MODE, hdr);
return true; return true;
} }

@ -3,8 +3,10 @@ package com.otaliastudios.cameraview.engine.mappers;
import android.hardware.Camera; import android.hardware.Camera;
import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CameraCharacteristics;
import android.os.Build; import android.os.Build;
import android.util.Pair;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi; import androidx.annotation.RequiresApi;
import com.otaliastudios.cameraview.controls.Control; import com.otaliastudios.cameraview.controls.Control;
@ -14,212 +16,153 @@ 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.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set;
/** /**
* A Mapper maps camera engine constants to CameraView constants. * A Mapper maps camera engine constants to CameraView constants.
*/ */
@RequiresApi(Build.VERSION_CODES.LOLLIPOP) @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
public abstract class Camera2Mapper { public class Camera2Mapper {
private static com.otaliastudios.cameraview.engine.mappers.Camera2Mapper sInstance; private static Camera2Mapper sInstance;
public static com.otaliastudios.cameraview.engine.mappers.Camera2Mapper get() { public static Camera2Mapper get() {
if (sInstance == null) sInstance = new Camera2Mapper(); if (sInstance == null) {
return sInstance; sInstance = new Camera2Mapper();
} }
public static com.otaliastudios.cameraview.engine.mappers.Camera2Mapper get(@NonNull Engine engine) {
if (engine == Engine.CAMERA1) {
if (sInstance == null) sInstance = new Camera1Mapper();
return sInstance; return sInstance;
} else if (engine == Engine.CAMERA2 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (CAMERA2 == null) CAMERA2 = new Camera2Mapper();
return CAMERA2;
} else {
throw new IllegalArgumentException("Unknown engine or unsupported API level.");
}
} }
private Camera2Mapper() {} private static final Map<Facing, Integer> FACING = new HashMap<>();
private static final Map<WhiteBalance, Integer> WB = new HashMap<>();
public abstract <T> T map(Flash flash); private static final Map<Hdr, Integer> HDR = new HashMap<>();
public abstract <T> T map(Facing facing);
public abstract <T> T map(WhiteBalance whiteBalance);
public abstract <T> T map(Hdr hdr);
public abstract <T> Flash unmapFlash(T cameraConstant);
public abstract <T> Facing unmapFacing(T cameraConstant); static {
FACING.put(Facing.BACK, CameraCharacteristics.LENS_FACING_BACK);
public abstract <T> WhiteBalance unmapWhiteBalance(T cameraConstant); FACING.put(Facing.FRONT, CameraCharacteristics.LENS_FACING_FRONT);
WB.put(WhiteBalance.AUTO, CameraCharacteristics.CONTROL_AWB_MODE_AUTO);
public abstract <T> Hdr unmapHdr(T cameraConstant); WB.put(WhiteBalance.CLOUDY, CameraCharacteristics.CONTROL_AWB_MODE_CLOUDY_DAYLIGHT);
WB.put(WhiteBalance.DAYLIGHT, CameraCharacteristics.CONTROL_AWB_MODE_DAYLIGHT);
@SuppressWarnings("WeakerAccess") WB.put(WhiteBalance.FLUORESCENT, CameraCharacteristics.CONTROL_AWB_MODE_FLUORESCENT);
protected <C extends Control, T> C reverseLookup(HashMap<C, T> map, T object) { WB.put(WhiteBalance.INCANDESCENT, CameraCharacteristics.CONTROL_AWB_MODE_INCANDESCENT);
for (C value : map.keySet()) { HDR.put(Hdr.OFF, CameraCharacteristics.CONTROL_SCENE_MODE_DISABLED);
if (object.equals(map.get(value))) { HDR.put(Hdr.ON, 18 /* CameraCharacteristics.CONTROL_SCENE_MODE_HDR */);
return value;
}
}
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") private Camera2Mapper() {}
private static class Camera1Mapper extends com.otaliastudios.cameraview.engine.mappers.Camera2Mapper {
private static final HashMap<Flash, String> FLASH = new HashMap<>();
private static final HashMap<WhiteBalance, String> WB = new HashMap<>();
private static final HashMap<Facing, Integer> FACING = new HashMap<>();
private static final HashMap<Hdr, String> HDR = new HashMap<>();
static { @NonNull
FLASH.put(Flash.OFF, Camera.Parameters.FLASH_MODE_OFF); public List<Pair<Integer, Integer>> map(@NonNull Flash flash) {
FLASH.put(Flash.ON, Camera.Parameters.FLASH_MODE_ON); List<Pair<Integer, Integer>> result = new ArrayList<>();
FLASH.put(Flash.AUTO, Camera.Parameters.FLASH_MODE_AUTO); switch (flash) {
FLASH.put(Flash.TORCH, Camera.Parameters.FLASH_MODE_TORCH); case ON: {
FACING.put(Facing.BACK, Camera.CameraInfo.CAMERA_FACING_BACK); result.add(new Pair<>(
FACING.put(Facing.FRONT, Camera.CameraInfo.CAMERA_FACING_FRONT); CameraCharacteristics.CONTROL_AE_MODE_ON_ALWAYS_FLASH,
WB.put(WhiteBalance.AUTO, Camera.Parameters.WHITE_BALANCE_AUTO); CameraCharacteristics.FLASH_MODE_OFF));
WB.put(WhiteBalance.INCANDESCENT, Camera.Parameters.WHITE_BALANCE_INCANDESCENT); break;
WB.put(WhiteBalance.FLUORESCENT, Camera.Parameters.WHITE_BALANCE_FLUORESCENT);
WB.put(WhiteBalance.DAYLIGHT, Camera.Parameters.WHITE_BALANCE_DAYLIGHT);
WB.put(WhiteBalance.CLOUDY, Camera.Parameters.WHITE_BALANCE_CLOUDY_DAYLIGHT);
HDR.put(Hdr.OFF, Camera.Parameters.SCENE_MODE_AUTO);
if (Build.VERSION.SDK_INT >= 17) {
HDR.put(Hdr.ON, Camera.Parameters.SCENE_MODE_HDR);
} else {
HDR.put(Hdr.ON, "hdr");
} }
case AUTO: {
result.add(new Pair<>(
CameraCharacteristics.CONTROL_AE_MODE_ON_AUTO_FLASH,
CameraCharacteristics.FLASH_MODE_OFF));
result.add(new Pair<>(
CameraCharacteristics.CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE,
CameraCharacteristics.FLASH_MODE_OFF));
break;
} }
case OFF: {
@Override result.add(new Pair<>(
public <T> T map(Flash flash) { CameraCharacteristics.CONTROL_AE_MODE_ON,
return (T) FLASH.get(flash); CameraCharacteristics.FLASH_MODE_OFF));
result.add(new Pair<>(
CameraCharacteristics.CONTROL_AE_MODE_OFF,
CameraCharacteristics.FLASH_MODE_OFF));
break;
} }
case TORCH: {
@Override // When AE_MODE is ON or OFF, we can finally use the flash mode
public <T> T map(Facing facing) { // low level control to either turn flash off or open the torch
return (T) FACING.get(facing); result.add(new Pair<>(
CameraCharacteristics.CONTROL_AE_MODE_ON,
CameraCharacteristics.FLASH_MODE_TORCH));
result.add(new Pair<>(
CameraCharacteristics.CONTROL_AE_MODE_OFF,
CameraCharacteristics.FLASH_MODE_TORCH));
break;
} }
@Override
public <T> T map(WhiteBalance whiteBalance) {
return (T) WB.get(whiteBalance);
} }
return result;
@Override
public <T> T map(Hdr hdr) {
return (T) HDR.get(hdr);
} }
@Override public int map(@NonNull Facing facing) {
public <T> Flash unmapFlash(T cameraConstant) { //noinspection ConstantConditions
return reverseLookup(FLASH, (String) cameraConstant); return FACING.get(facing);
} }
@Override public int map(@NonNull WhiteBalance whiteBalance) {
public <T> Facing unmapFacing(T cameraConstant) { //noinspection ConstantConditions
return reverseLookup(FACING, (Integer) cameraConstant); return WB.get(whiteBalance);
} }
@Override public int map(@NonNull Hdr hdr) {
public <T> WhiteBalance unmapWhiteBalance(T cameraConstant) { //noinspection ConstantConditions
return reverseLookup(WB, (String) cameraConstant); return HDR.get(hdr);
} }
@Override @NonNull
public <T> Hdr unmapHdr(T cameraConstant) { public Set<Flash> unmapFlash(int cameraConstant) {
return reverseLookup(HDR, (String) cameraConstant); Set<Flash> result = new HashSet<>();
switch (cameraConstant) {
case CameraCharacteristics.CONTROL_AE_MODE_OFF:
case CameraCharacteristics.CONTROL_AE_MODE_ON: {
result.add(Flash.OFF);
result.add(Flash.TORCH);
break;
} }
case CameraCharacteristics.CONTROL_AE_MODE_ON_ALWAYS_FLASH: {
result.add(Flash.ON);
break;
} }
case CameraCharacteristics.CONTROL_AE_MODE_ON_AUTO_FLASH:
@SuppressWarnings("unchecked") case CameraCharacteristics.CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE: {
@RequiresApi(Build.VERSION_CODES.LOLLIPOP) result.add(Flash.AUTO);
private static class Camera2Mapper extends com.otaliastudios.cameraview.engine.mappers.Camera2Mapper { break;
private static final HashMap<Flash, List<Integer>> FLASH = 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 {
// OFF and TORCH have also a second condition - to CameraCharacteristics.CONTROL_FLASH_MODE - but that does not
// fit into the Mapper interface. TODO review this
FLASH.put(Flash.OFF, Arrays.asList(CameraCharacteristics.CONTROL_AE_MODE_ON, CameraCharacteristics.CONTROL_AE_MODE_OFF));
FLASH.put(Flash.TORCH, 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.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 */);
}
@Override
public <T> T map(Flash flash) {
return (T) FLASH.get(flash);
} }
case CameraCharacteristics.CONTROL_AE_MODE_ON_EXTERNAL_FLASH:
@Override default: break; // we don't support external flash
public <T> T map(Facing facing) {
return (T) FACING.get(facing);
} }
return result;
@Override
public <T> T map(WhiteBalance whiteBalance) {
return (T) WB.get(whiteBalance);
} }
@Override @Nullable
public <T> T map(Hdr hdr) { public Facing unmapFacing(int cameraConstant) {
return (T) HDR.get(hdr); return reverseLookup(FACING, cameraConstant);
} }
@Override @Nullable
public <T> Flash unmapFlash(T cameraConstant) { public WhiteBalance unmapWhiteBalance(int cameraConstant) {
return reverseListLookup(FLASH, (Integer) cameraConstant); return reverseLookup(WB, cameraConstant);
} }
@Override @Nullable
public <T> Facing unmapFacing(T cameraConstant) { public Hdr unmapHdr(int cameraConstant) {
return reverseLookup(FACING, (Integer) cameraConstant); return reverseLookup(HDR, cameraConstant);
} }
@Override @Nullable
public <T> WhiteBalance unmapWhiteBalance(T cameraConstant) { private <C extends Control, T> C reverseLookup(@NonNull Map<C, T> map, @NonNull T object) {
return reverseLookup(WB, (Integer) cameraConstant); for (C value : map.keySet()) {
if (object.equals(map.get(value))) {
return value;
} }
@Override
public <T> Hdr unmapHdr(T cameraConstant) {
return reverseLookup(HDR, (Integer) cameraConstant);
} }
return null;
} }
} }

Loading…
Cancel
Save