mirror of
https://github.com/status-im/react-native-camera.git
synced 2026-08-31 13:41:08 +00:00
* Initial commit with Android video support * stopCapture now works * Bug fixes and parameter enhancements. README updated. * Modified stopCapture parameter count to match iOS * fixed promise bug on stopCapture * Update RCTCameraModule.java In Android preview and recording sizes are different, which can cause an error. This fix detects the difference and chooses a recording resolution that matches. * Update RCTCameraModule.java * Update RCTCamera.java Creating video functions in style/convention of existing * Update RCTCameraModule.java Use new functions for adjusting video capture size and quality * Update RCTCameraModule.java Fixes issue where file not video playable (readable) on older devices * Update AndroidManifest.xml Since we're reading and writing video and pictures, need permissions for it. * Fixed upside down camera (on some platforms), and misc bugs and crashes * Added camera-roll and capture to memory support, new options, and support for duration, filesize, and metadata * To make merge nicer, temporarily reverting "Added camera-roll and capture to memory support, new options, and support for duration, filesize, and metadata" This reverts commit 9ea1ad409c7e6121cf0197172e752b7523d4b092. * Fixed merge & brought back all improvements from 9ea1ad4 * Fixed logic for video -> camera roll * Updates * Uncommenting setProfile * Fix support for React Native 0.25 * Renamed Camera to index * * Fix after merge android recording * * Fixed android camera roll file saving * Added recording to example * * Android promise rejections with exceptions * Fixed preview, video and photo sizes * Android recording result in new, javascript object, format * * Removed example.index.android.js as there is Example project * * Readme for example * don't force a specific codec * always use cache dir * * Using MediaScannerConnection instead of ACTION_MEDIA_SCANNER_SCAN_FILE intent * * As described in https://github.com/lwansbrough/react-native-camera/pull/262#issuecomment-239622268: - fixed video the wrong direction and recoder start fail at "low,medium" on the nexus 5 x
371 lines
13 KiB
Java
371 lines
13 KiB
Java
/**
|
|
* Created by Fabrice Armisen (farmisen@gmail.com) on 1/4/16.
|
|
*/
|
|
|
|
package com.lwansbrough.RCTCamera;
|
|
|
|
import android.hardware.Camera;
|
|
import android.media.CamcorderProfile;
|
|
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
|
|
public class RCTCamera {
|
|
|
|
private static final RCTCamera ourInstance = new RCTCamera();
|
|
private final HashMap<Integer, CameraInfoWrapper> _cameraInfos;
|
|
private final HashMap<Integer, Integer> _cameraTypeToIndex;
|
|
private final Map<Number, Camera> _cameras;
|
|
private int _orientation = -1;
|
|
private int _actualDeviceOrientation = 0;
|
|
private int _adjustedDeviceOrientation = 0;
|
|
|
|
public static RCTCamera getInstance() {
|
|
return ourInstance;
|
|
}
|
|
|
|
public Camera acquireCameraInstance(int type) {
|
|
if (null == _cameras.get(type) && null != _cameraTypeToIndex.get(type)) {
|
|
try {
|
|
Camera camera = Camera.open(_cameraTypeToIndex.get(type));
|
|
_cameras.put(type, camera);
|
|
adjustPreviewLayout(type);
|
|
} catch (Exception e) {
|
|
if (System.console() != null) {
|
|
System.console().printf("acquireCameraInstance: %s", e.getLocalizedMessage());
|
|
}
|
|
}
|
|
}
|
|
return _cameras.get(type);
|
|
}
|
|
|
|
public void releaseCameraInstance(int type) {
|
|
if (null != _cameras.get(type)) {
|
|
_cameras.get(type).release();
|
|
_cameras.remove(type);
|
|
}
|
|
}
|
|
|
|
public int getPreviewWidth(int type) {
|
|
CameraInfoWrapper cameraInfo = _cameraInfos.get(type);
|
|
if (null == cameraInfo) {
|
|
return 0;
|
|
}
|
|
return cameraInfo.previewWidth;
|
|
}
|
|
|
|
public int getPreviewHeight(int type) {
|
|
CameraInfoWrapper cameraInfo = _cameraInfos.get(type);
|
|
if (null == cameraInfo) {
|
|
return 0;
|
|
}
|
|
return cameraInfo.previewHeight;
|
|
}
|
|
|
|
public Camera.Size getBestSize(List<Camera.Size> supportedSizes, int maxWidth, int maxHeight) {
|
|
Camera.Size bestSize = null;
|
|
for (Camera.Size size : supportedSizes) {
|
|
if (size.width > maxWidth || size.height > maxHeight) {
|
|
continue;
|
|
}
|
|
|
|
if (bestSize == null) {
|
|
bestSize = size;
|
|
continue;
|
|
}
|
|
|
|
int resultArea = bestSize.width * bestSize.height;
|
|
int newArea = size.width * size.height;
|
|
|
|
if (newArea > resultArea) {
|
|
bestSize = size;
|
|
}
|
|
}
|
|
|
|
return bestSize;
|
|
}
|
|
|
|
private Camera.Size getSmallestSize(List<Camera.Size> supportedSizes) {
|
|
Camera.Size smallestSize = null;
|
|
for (Camera.Size size : supportedSizes) {
|
|
if (smallestSize == null) {
|
|
smallestSize = size;
|
|
continue;
|
|
}
|
|
|
|
int resultArea = smallestSize.width * smallestSize.height;
|
|
int newArea = size.width * size.height;
|
|
|
|
if (newArea < resultArea) {
|
|
smallestSize = size;
|
|
}
|
|
}
|
|
|
|
return smallestSize;
|
|
}
|
|
|
|
private List<Camera.Size> getSupportedVideoSizes(Camera camera) {
|
|
Camera.Parameters params = camera.getParameters();
|
|
// defer to preview instead of params.getSupportedVideoSizes() http://bit.ly/1rxOsq0
|
|
// but prefer SupportedVideoSizes!
|
|
List<Camera.Size> sizes = params.getSupportedVideoSizes();
|
|
if (sizes != null) {
|
|
return sizes;
|
|
}
|
|
|
|
// Video sizes may be null, which indicates that all the supported
|
|
// preview sizes are supported for video recording.
|
|
return params.getSupportedPreviewSizes();
|
|
}
|
|
|
|
public int getOrientation() {
|
|
return _orientation;
|
|
}
|
|
|
|
public void setOrientation(int orientation) {
|
|
if (_orientation == orientation) {
|
|
return;
|
|
}
|
|
_orientation = orientation;
|
|
adjustPreviewLayout(RCTCameraModule.RCT_CAMERA_TYPE_FRONT);
|
|
adjustPreviewLayout(RCTCameraModule.RCT_CAMERA_TYPE_BACK);
|
|
}
|
|
|
|
public int getActualDeviceOrientation() {
|
|
return _actualDeviceOrientation;
|
|
}
|
|
|
|
public void setAdjustedDeviceOrientation(int orientation) {
|
|
_adjustedDeviceOrientation = orientation;
|
|
}
|
|
|
|
public int getAdjustedDeviceOrientation() {
|
|
return _adjustedDeviceOrientation;
|
|
}
|
|
|
|
public void setActualDeviceOrientation(int actualDeviceOrientation) {
|
|
_actualDeviceOrientation = actualDeviceOrientation;
|
|
adjustPreviewLayout(RCTCameraModule.RCT_CAMERA_TYPE_FRONT);
|
|
adjustPreviewLayout(RCTCameraModule.RCT_CAMERA_TYPE_BACK);
|
|
}
|
|
|
|
public void setCaptureQuality(int cameraType, String captureQuality) {
|
|
Camera camera = _cameras.get(cameraType);
|
|
if (camera == null) {
|
|
return;
|
|
}
|
|
|
|
Camera.Parameters parameters = camera.getParameters();
|
|
Camera.Size pictureSize = null;
|
|
switch (captureQuality) {
|
|
case RCTCameraModule.RCT_CAMERA_CAPTURE_QUALITY_LOW:
|
|
pictureSize = getSmallestSize(parameters.getSupportedPictureSizes());
|
|
break;
|
|
case RCTCameraModule.RCT_CAMERA_CAPTURE_QUALITY_MEDIUM:
|
|
List<Camera.Size> sizes = parameters.getSupportedPictureSizes();
|
|
pictureSize = sizes.get(sizes.size() / 2);
|
|
break;
|
|
case RCTCameraModule.RCT_CAMERA_CAPTURE_QUALITY_HIGH:
|
|
pictureSize = getBestSize(parameters.getSupportedPictureSizes(), Integer.MAX_VALUE, Integer.MAX_VALUE);
|
|
}
|
|
|
|
if (pictureSize != null) {
|
|
parameters.setPictureSize(pictureSize.width, pictureSize.height);
|
|
camera.setParameters(parameters);
|
|
}
|
|
}
|
|
|
|
public CamcorderProfile setCaptureVideoQuality(int cameraType, String captureQuality) {
|
|
Camera camera = _cameras.get(cameraType);
|
|
if (camera == null) {
|
|
return null;
|
|
}
|
|
|
|
Camera.Size videoSize = null;
|
|
CamcorderProfile cm = null;
|
|
switch (captureQuality) {
|
|
case RCTCameraModule.RCT_CAMERA_CAPTURE_QUALITY_LOW:
|
|
videoSize = getSmallestSize(getSupportedVideoSizes(camera));
|
|
cm = CamcorderProfile.get(_cameraTypeToIndex.get(cameraType), CamcorderProfile.QUALITY_480P);
|
|
break;
|
|
case RCTCameraModule.RCT_CAMERA_CAPTURE_QUALITY_MEDIUM:
|
|
List<Camera.Size> sizes = getSupportedVideoSizes(camera);
|
|
videoSize = sizes.get(sizes.size() / 2);
|
|
cm = CamcorderProfile.get(_cameraTypeToIndex.get(cameraType), CamcorderProfile.QUALITY_720P);
|
|
break;
|
|
case RCTCameraModule.RCT_CAMERA_CAPTURE_QUALITY_HIGH:
|
|
videoSize = getBestSize(getSupportedVideoSizes(camera), Integer.MAX_VALUE, Integer.MAX_VALUE);
|
|
cm = CamcorderProfile.get(_cameraTypeToIndex.get(cameraType), CamcorderProfile.QUALITY_HIGH);
|
|
}
|
|
|
|
if (cm == null){
|
|
return null;
|
|
}
|
|
|
|
if (videoSize != null) {
|
|
cm.videoFrameHeight = videoSize.height;
|
|
cm.videoFrameWidth = videoSize.width;
|
|
}
|
|
|
|
return cm;
|
|
}
|
|
|
|
public void setTorchMode(int cameraType, int torchMode) {
|
|
Camera camera = _cameras.get(cameraType);
|
|
if (null == camera) {
|
|
return;
|
|
}
|
|
|
|
Camera.Parameters parameters = camera.getParameters();
|
|
String value = parameters.getFlashMode();
|
|
switch (torchMode) {
|
|
case RCTCameraModule.RCT_CAMERA_TORCH_MODE_ON:
|
|
value = Camera.Parameters.FLASH_MODE_TORCH;
|
|
break;
|
|
case RCTCameraModule.RCT_CAMERA_TORCH_MODE_OFF:
|
|
value = Camera.Parameters.FLASH_MODE_OFF;
|
|
break;
|
|
}
|
|
|
|
List<String> flashModes = parameters.getSupportedFlashModes();
|
|
if (flashModes != null && flashModes.contains(value)) {
|
|
parameters.setFlashMode(value);
|
|
camera.setParameters(parameters);
|
|
}
|
|
}
|
|
|
|
public void setFlashMode(int cameraType, int flashMode) {
|
|
Camera camera = _cameras.get(cameraType);
|
|
if (null == camera) {
|
|
return;
|
|
}
|
|
|
|
Camera.Parameters parameters = camera.getParameters();
|
|
String value = parameters.getFlashMode();
|
|
switch (flashMode) {
|
|
case RCTCameraModule.RCT_CAMERA_FLASH_MODE_AUTO:
|
|
value = Camera.Parameters.FLASH_MODE_AUTO;
|
|
break;
|
|
case RCTCameraModule.RCT_CAMERA_FLASH_MODE_ON:
|
|
value = Camera.Parameters.FLASH_MODE_ON;
|
|
break;
|
|
case RCTCameraModule.RCT_CAMERA_FLASH_MODE_OFF:
|
|
value = Camera.Parameters.FLASH_MODE_OFF;
|
|
break;
|
|
}
|
|
List<String> flashModes = parameters.getSupportedFlashModes();
|
|
if (flashModes != null && flashModes.contains(value)) {
|
|
parameters.setFlashMode(value);
|
|
camera.setParameters(parameters);
|
|
}
|
|
}
|
|
|
|
public void adjustCameraRotationToDeviceOrientation(int type, int deviceOrientation) {
|
|
Camera camera = _cameras.get(type);
|
|
if (null == camera) {
|
|
return;
|
|
}
|
|
|
|
CameraInfoWrapper cameraInfo = _cameraInfos.get(type);
|
|
int rotation;
|
|
int orientation = cameraInfo.info.orientation;
|
|
if (cameraInfo.info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
|
|
rotation = (orientation + deviceOrientation * 90) % 360;
|
|
} else {
|
|
rotation = (orientation - deviceOrientation * 90 + 360) % 360;
|
|
}
|
|
cameraInfo.rotation = rotation;
|
|
Camera.Parameters parameters = camera.getParameters();
|
|
parameters.setRotation(cameraInfo.rotation);
|
|
|
|
try {
|
|
camera.setParameters(parameters);
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
public void adjustPreviewLayout(int type) {
|
|
Camera camera = _cameras.get(type);
|
|
if (null == camera) {
|
|
return;
|
|
}
|
|
|
|
CameraInfoWrapper cameraInfo = _cameraInfos.get(type);
|
|
int displayRotation;
|
|
int rotation;
|
|
int orientation = cameraInfo.info.orientation;
|
|
if (cameraInfo.info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
|
|
rotation = (orientation + _actualDeviceOrientation * 90) % 360;
|
|
displayRotation = (720 - orientation - _actualDeviceOrientation * 90) % 360;
|
|
} else {
|
|
rotation = (orientation - _actualDeviceOrientation * 90 + 360) % 360;
|
|
displayRotation = rotation;
|
|
}
|
|
cameraInfo.rotation = rotation;
|
|
// TODO: take in account the _orientation prop
|
|
|
|
setAdjustedDeviceOrientation(displayRotation);
|
|
camera.setDisplayOrientation(displayRotation);
|
|
|
|
Camera.Parameters parameters = camera.getParameters();
|
|
parameters.setRotation(cameraInfo.rotation);
|
|
|
|
// set preview size
|
|
// defaults to highest resolution available
|
|
Camera.Size optimalPreviewSize = getBestSize(parameters.getSupportedPreviewSizes(), Integer.MAX_VALUE, Integer.MAX_VALUE);
|
|
int width = optimalPreviewSize.width;
|
|
int height = optimalPreviewSize.height;
|
|
|
|
parameters.setPreviewSize(width, height);
|
|
try {
|
|
camera.setParameters(parameters);
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
}
|
|
|
|
if (cameraInfo.rotation == 0 || cameraInfo.rotation == 180) {
|
|
cameraInfo.previewWidth = width;
|
|
cameraInfo.previewHeight = height;
|
|
} else {
|
|
cameraInfo.previewWidth = height;
|
|
cameraInfo.previewHeight = width;
|
|
}
|
|
}
|
|
|
|
private RCTCamera() {
|
|
_cameras = new HashMap<>();
|
|
_cameraInfos = new HashMap<>();
|
|
_cameraTypeToIndex = new HashMap<>();
|
|
|
|
// map camera types to camera indexes and collect cameras properties
|
|
for (int i = 0; i < Camera.getNumberOfCameras(); i++) {
|
|
Camera.CameraInfo info = new Camera.CameraInfo();
|
|
Camera.getCameraInfo(i, info);
|
|
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT && _cameraInfos.get(RCTCameraModule.RCT_CAMERA_TYPE_FRONT) == null) {
|
|
_cameraInfos.put(RCTCameraModule.RCT_CAMERA_TYPE_FRONT, new CameraInfoWrapper(info));
|
|
_cameraTypeToIndex.put(RCTCameraModule.RCT_CAMERA_TYPE_FRONT, i);
|
|
acquireCameraInstance(RCTCameraModule.RCT_CAMERA_TYPE_FRONT);
|
|
releaseCameraInstance(RCTCameraModule.RCT_CAMERA_TYPE_FRONT);
|
|
} else if (info.facing == Camera.CameraInfo.CAMERA_FACING_BACK && _cameraInfos.get(RCTCameraModule.RCT_CAMERA_TYPE_BACK) == null) {
|
|
_cameraInfos.put(RCTCameraModule.RCT_CAMERA_TYPE_BACK, new CameraInfoWrapper(info));
|
|
_cameraTypeToIndex.put(RCTCameraModule.RCT_CAMERA_TYPE_BACK, i);
|
|
acquireCameraInstance(RCTCameraModule.RCT_CAMERA_TYPE_BACK);
|
|
releaseCameraInstance(RCTCameraModule.RCT_CAMERA_TYPE_BACK);
|
|
}
|
|
}
|
|
}
|
|
|
|
private class CameraInfoWrapper {
|
|
public final Camera.CameraInfo info;
|
|
public int rotation = 0;
|
|
public int previewWidth = -1;
|
|
public int previewHeight = -1;
|
|
|
|
public CameraInfoWrapper(Camera.CameraInfo info) {
|
|
this.info = info;
|
|
}
|
|
}
|
|
}
|