Skip to content
This repository has been archived by the owner on Jun 16, 2023. It is now read-only.

Commit

Permalink
feat(android): Support to enumerate and select Camera devices (#2492)
Browse files Browse the repository at this point in the history
* Android only: Support to enumerate Camera devices and to select from one of them.

* No need to have the camera in running state if querying for IDs.

* Silly bug, not using string compare. Also, do not run any camera code if the actual camera doesn't change.

* Crash fix when focus coordinates are set to null/undefined not being handled. Notes about not supported flash/focus

* If a camera is not found, set the first available camera just like Camera2 does.

* missing semicolon

* Fixes to Camera2 API:

- First change is related to camera selection by ID. Some more code was required to correctly set the facing flag and characteristics
- Second change fixes a previous issue (unrelated to the PR) that was causing the preview of the camera to look upside down on rotated devices. Device rotation should not affect the display (nor set it). Device rotation should however be used for the final image (and not screen rotation). Some code was borrowed from Camera1.
  • Loading branch information
cristianoccazinsp authored and sibelius committed Sep 24, 2019
1 parent 200c7e1 commit 612cb65
Show file tree
Hide file tree
Showing 8 changed files with 385 additions and 110 deletions.
76 changes: 70 additions & 6 deletions android/src/main/java/com/google/android/cameraview/Camera1.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Properties;
import java.util.Set;
import java.util.SortedSet;
import java.util.concurrent.atomic.AtomicBoolean;
Expand Down Expand Up @@ -73,6 +75,7 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
private Handler mHandler = new Handler();

private int mCameraId;
private String _mCameraId;

private final AtomicBoolean isPictureCaptureInProgress = new AtomicBoolean(false);

Expand Down Expand Up @@ -281,6 +284,31 @@ int getFacing() {
return mFacing;
}

@Override
void setCameraId(String id) {

if(!Objects.equals(_mCameraId, id)){
_mCameraId = id;

// only update if our camera ID actually changes
// from what we currently have.
// Passing null will always yield true
if(!Objects.equals(_mCameraId, String.valueOf(mCameraId))){
// this will call chooseCamera
if (isCameraOpened()) {
stop();
start();
}
}
}

}

@Override
String getCameraId() {
return _mCameraId;
}

@Override
Set<AspectRatio> getSupportedAspectRatios() {
SizeMap idealAspectRatios = mPreviewSizes;
Expand All @@ -292,6 +320,23 @@ Set<AspectRatio> getSupportedAspectRatios() {
return idealAspectRatios.ratios();
}


@Override
List<Properties> getCameraIds() {
List<Properties> ids = new ArrayList<>();

Camera.CameraInfo info = new Camera.CameraInfo();

for (int i = 0, count = Camera.getNumberOfCameras(); i < count; i++) {
Properties p = new Properties();
Camera.getCameraInfo(i, info);
p.put("id", String.valueOf(i));
p.put("type", String.valueOf(info.facing));
ids.add(p);
}
return ids;
}

@Override
SortedSet<Size> getAvailablePictureSizes(AspectRatio ratio) {
return mPictureSizes.sizes(ratio);
Expand Down Expand Up @@ -446,6 +491,7 @@ float getZoom() {
return mZoom;
}


@Override
public void setWhiteBalance(int whiteBalance) {
if (whiteBalance == mWhiteBalance) {
Expand Down Expand Up @@ -693,14 +739,32 @@ public Size getPreviewSize() {
* This rewrites {@link #mCameraId} and {@link #mCameraInfo}.
*/
private void chooseCamera() {
for (int i = 0, count = Camera.getNumberOfCameras(); i < count; i++) {
Camera.getCameraInfo(i, mCameraInfo);
if (mCameraInfo.facing == mFacing) {
mCameraId = i;
return;
if(_mCameraId == null){
int count = Camera.getNumberOfCameras();
if(count == 0){
throw new RuntimeException("No camera available.");
}

for (int i = 0; i < count; i++) {
Camera.getCameraInfo(i, mCameraInfo);
if (mCameraInfo.facing == mFacing) {
mCameraId = i;
return;
}
}
// no camera found, set the one we have
mCameraId = 0;
Camera.getCameraInfo(mCameraId, mCameraInfo);
}
else{
try{
mCameraId = Integer.parseInt(_mCameraId);
Camera.getCameraInfo(mCameraId, mCameraInfo);
}
catch(Exception e){
mCameraId = INVALID_CAMERA_ID;
}
}
mCameraId = INVALID_CAMERA_ID;
}

private boolean openCamera() {
Expand Down
187 changes: 145 additions & 42 deletions android/src/main/java/com/google/android/cameraview/Camera2.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,11 @@
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Properties;
import java.util.Set;
import java.util.SortedSet;

Expand Down Expand Up @@ -199,6 +203,7 @@ public void onImageAvailable(ImageReader reader) {


private String mCameraId;
private String _mCameraId;

private CameraCharacteristics mCameraCharacteristics;

Expand Down Expand Up @@ -359,11 +364,58 @@ int getFacing() {
return mFacing;
}

@Override
void setCameraId(String id) {
if(!Objects.equals(_mCameraId, id)){
_mCameraId = id;

// only update if our camera ID actually changes
// from what we currently have.
// Passing null will always yield true
if(!Objects.equals(_mCameraId, mCameraId)){
// this will call chooseCameraIdByFacing
if (isCameraOpened()) {
stop();
start();
}
}
}
}

@Override
String getCameraId() {
return _mCameraId;
}

@Override
Set<AspectRatio> getSupportedAspectRatios() {
return mPreviewSizes.ratios();
}

@Override
List<Properties> getCameraIds() {
try{

List<Properties> ids = new ArrayList<>();

String[] cameraIds = mCameraManager.getCameraIdList();
for (String id : cameraIds) {
Properties p = new Properties();

CameraCharacteristics characteristics = mCameraManager.getCameraCharacteristics(id);
Integer internal = characteristics.get(CameraCharacteristics.LENS_FACING);

p.put("id", id);
p.put("type", String.valueOf(internal == CameraCharacteristics.LENS_FACING_FRONT ? Constants.FACING_FRONT : Constants.FACING_BACK));
ids.add(p);
}
return ids;
}
catch (CameraAccessException e) {
throw new RuntimeException("Failed to get a list of camera ids", e);
}
}

@Override
SortedSet<Size> getAvailablePictureSizes(AspectRatio ratio) {
return mPictureSizes.sizes(ratio);
Expand Down Expand Up @@ -651,7 +703,7 @@ void setDisplayOrientation(int displayOrientation) {
@Override
void setDeviceOrientation(int deviceOrientation) {
mDeviceOrientation = deviceOrientation;
mPreview.setDisplayOrientation(mDeviceOrientation);
//mPreview.setDisplayOrientation(deviceOrientation); // this is not needed and messes up the display orientation
}

/**
Expand All @@ -660,55 +712,90 @@ void setDeviceOrientation(int deviceOrientation) {
* {@link #mFacing}.</p>
*/
private boolean chooseCameraIdByFacing() {
try {
int internalFacing = INTERNAL_FACINGS.get(mFacing);
final String[] ids = mCameraManager.getCameraIdList();
if (ids.length == 0) { // No camera
throw new RuntimeException("No camera available.");
}
for (String id : ids) {
CameraCharacteristics characteristics = mCameraManager.getCameraCharacteristics(id);
Integer level = characteristics.get(
if(_mCameraId == null){
try {
int internalFacing = INTERNAL_FACINGS.get(mFacing);
final String[] ids = mCameraManager.getCameraIdList();
if (ids.length == 0) { // No camera
throw new RuntimeException("No camera available.");
}
for (String id : ids) {
CameraCharacteristics characteristics = mCameraManager.getCameraCharacteristics(id);
Integer level = characteristics.get(
CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
if (level == null ||
level == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {
continue;
}
Integer internal = characteristics.get(CameraCharacteristics.LENS_FACING);
if (internal == null) {
throw new NullPointerException("Unexpected state: LENS_FACING null");
}
if (internal == internalFacing) {
mCameraId = id;
mCameraCharacteristics = characteristics;
return true;
}
}
// Not found
mCameraId = ids[0];
mCameraCharacteristics = mCameraManager.getCameraCharacteristics(mCameraId);
Integer level = mCameraCharacteristics.get(
CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
if (level == null ||
level == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {
continue;
return false;
}
Integer internal = characteristics.get(CameraCharacteristics.LENS_FACING);
Integer internal = mCameraCharacteristics.get(CameraCharacteristics.LENS_FACING);
if (internal == null) {
throw new NullPointerException("Unexpected state: LENS_FACING null");
}
if (internal == internalFacing) {
mCameraId = id;
mCameraCharacteristics = characteristics;
return true;
for (int i = 0, count = INTERNAL_FACINGS.size(); i < count; i++) {
if (INTERNAL_FACINGS.valueAt(i) == internal) {
mFacing = INTERNAL_FACINGS.keyAt(i);
return true;
}
}
// The operation can reach here when the only camera device is an external one.
// We treat it as facing back.
mFacing = Constants.FACING_BACK;
return true;
} catch (CameraAccessException e) {
throw new RuntimeException("Failed to get a list of camera devices", e);
}
// Not found
mCameraId = ids[0];
mCameraCharacteristics = mCameraManager.getCameraCharacteristics(mCameraId);
Integer level = mCameraCharacteristics.get(
CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
if (level == null ||
level == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {
return false;
}
Integer internal = mCameraCharacteristics.get(CameraCharacteristics.LENS_FACING);
if (internal == null) {
throw new NullPointerException("Unexpected state: LENS_FACING null");
}
for (int i = 0, count = INTERNAL_FACINGS.size(); i < count; i++) {
if (INTERNAL_FACINGS.valueAt(i) == internal) {
mFacing = INTERNAL_FACINGS.keyAt(i);
return true;
}
else{

try{
// need to set the mCameraCharacteristics variable as above and also do the same checks
// for legacy hardware
mCameraCharacteristics = mCameraManager.getCameraCharacteristics(_mCameraId);

Integer level = mCameraCharacteristics.get(
CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
if (level == null ||
level == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {
return false;
}

// set our facing variable so orientation also works as expected
Integer internal = mCameraCharacteristics.get(CameraCharacteristics.LENS_FACING);
if (internal == null) {
throw new NullPointerException("Unexpected state: LENS_FACING null");
}
for (int i = 0, count = INTERNAL_FACINGS.size(); i < count; i++) {
if (INTERNAL_FACINGS.valueAt(i) == internal) {
mFacing = INTERNAL_FACINGS.keyAt(i);
break;
}
}

mCameraId = _mCameraId;
return true;
}
catch(Exception e){
throw new RuntimeException("Failed to get camera characteristics", e);
}
// The operation can reach here when the only camera device is an external one.
// We treat it as facing back.
mFacing = Constants.FACING_BACK;
return true;
} catch (CameraAccessException e) {
throw new RuntimeException("Failed to get a list of camera devices", e);
}
}

Expand Down Expand Up @@ -1190,9 +1277,25 @@ public void onCaptureCompleted(@NonNull CameraCaptureSession session,
private int getOutputRotation() {
@SuppressWarnings("ConstantConditions")
int sensorOrientation = mCameraCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
return (sensorOrientation +
mDisplayOrientation * (mFacing == Constants.FACING_FRONT ? 1 : -1) +
360) % 360;

// updated and copied from Camera1
if (mFacing == Constants.FACING_BACK) {
return (sensorOrientation + mDeviceOrientation) % 360;
} else {
final int landscapeFlip = isLandscape(mDeviceOrientation) ? 180 : 0;
return (sensorOrientation + mDeviceOrientation + landscapeFlip) % 360;
}
}

/**
* Test if the supplied orientation is in landscape.
*
* @param orientationDegrees Orientation in degrees (0,90,180,270)
* @return True if in landscape, false if portrait
*/
private boolean isLandscape(int orientationDegrees) {
return (orientationDegrees == Constants.LANDSCAPE_90 ||
orientationDegrees == Constants.LANDSCAPE_270);
}

private void setUpMediaRecorder(String path, int maxDuration, int maxFileSize, boolean recordAudio, CamcorderProfile profile) {
Expand Down
Loading

0 comments on commit 612cb65

Please sign in to comment.