So I have a dialogue where I want to show the front and back camera previews sequentially, say after 2 seconds delay. Problem is, I can always set 1 camera view to the frame, how to I change it on the fly, automatically?
Here is where I want to change it on the fly:
public class CameraExample extends AnimatedViewContainer {
private final static String TAG = "CameraExample";
private Camera mCamera;
private CameraPreview mPreview;
private Context mContext;
public CameraExample(Context context, int i) {
super(context, i);
mPreview = null;
mContext = context;
initCamera(mContext);
}
// A safe way to get an instance of the Camera object.
public static Camera getCameraInstance(int cameraId) {
Camera c = null;
try {
// attempt to get a Camera instance
c = Camera.open(cameraId);
} catch (Exception e) {
// Camera is not available (in use or does not exist)
Log.e(TAG, "CameraExample: " + "camera not available (in use or does not exist); " + e.getMessage());
}
return c; // returns null if camera is unavailable
}
private void initCamera(Context context) {
// Check if this device has a camera
if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
// no camera on this device
Log.e(TAG, "CameraExample: " + "this device has no camera");
} else {
// this device has a camera
int numCameras = Camera.getNumberOfCameras();
if (numCameras >= 0) {
for (int cameraId = 0; cameraId < numCameras; cameraId++) {
mCamera = getCameraInstance(cameraId);
if (mCamera != null) {
CameraInfo cameraInfo = new CameraInfo();
Camera.getCameraInfo(cameraId, cameraInfo);
if (cameraInfo.facing == CameraInfo.CAMERA_FACING_FRONT) {
try {
//Create our Preview view and set it as the content of this LinearLayout View
mPreview = new CameraPreview(context, mCamera, cameraId);
} catch (RuntimeException e) {
Log.e(TAG, "Camera failed to open: " + e.getLocalizedMessage());
}
}
if (createView() == false) {
break;
}
}
}
}
}
}
#Override
public void onCreateViewContent(LayoutInflater layoutInflater, ViewGroup parentGroup, View[] containerViews, int index) {
containerViews[index] = layoutInflater.inflate(R.layout.example_camera, parentGroup, false);
FrameLayout previewFrame = (FrameLayout) containerViews[index].findViewById(R.id.preview);
// Add preview for inflation
previewFrame.addView(mPreview);
}
#Override
public void cleanup() {
if (mCamera != null) {
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
}
}
The CameraPreview class:
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private static final String TAG = "CameraPreview";
private Context mContext;
private SurfaceHolder mHolder;
private Camera mCamera;
private int mCameraId;
public CameraPreview(Context context, Camera camera, int cameraId) {
super(context);
mContext = context;
mCamera = camera;
mCameraId = cameraId;
// Install a SurfaceHolder.Callback so we get notified when the
// underlying surface is created and destroyed.
mHolder = getHolder();
mHolder.addCallback(this);
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
// The Surface has been created, now tell the camera where to draw the preview.
try {
mCamera.setPreviewDisplay(holder);
mCamera.startPreview();
} catch (IOException e) {
Log.e(TAG, "CameraExample: " + "Error setting camera preview: " + e.getMessage());
}
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
// empty. Take care of releasing the Camera preview in your activity.
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
// If your preview can change or rotate, take care of those events here.
// Make sure to stop the preview before resizing or reformatting it.
if (mHolder.getSurface() == null) {
// preview surface does not exist
return;
}
// stop preview before making changes
try {
mCamera.stopPreview();
} catch (Exception e) {
// ignore: tried to stop a non-existent preview
}
}
}
I set my view here:
#Override
public void onCreateViewContent(LayoutInflater layoutInflater, ViewGroup parentGroup, View[] containerViews, int index) {
containerViews[index] = layoutInflater.inflate(R.layout.example_camera, parentGroup, false);
FrameLayout previewFrame = (FrameLayout) containerViews[index].findViewById(R.id.preview);
previewFrame.addView(mPreview);
}
Problem is, I don't see how I can have 2 instances of the 2 different cameras that a device generally has and change them automatically after certain seconds so that my frame displays the front and back camera preview one after other after every certain amount of seconds. Any solution is highly appreciated! I think I have to handle it in the surfaceChanged() method, but I really don't know how!
As asked, here is the, AnimatedViewContainer class:
public abstract class AnimatedViewContainer extends Example {
Context mContext;
int mAnimationDuration;
int mAnimationDurationShort;
LayoutInflater mLayoutInflater;
ViewGroup mParentGroup;
View[] mContainerViews;
boolean hasBeenClicked = false;
int mCurrentIndex;
int mMaxNumItems;
int mIndexVisibleItem;
public AnimatedViewContainer(Context context, int maxNumItems) {
super(context);
mContext = context;
mMaxNumItems = maxNumItems;
mContainerViews = new View[mMaxNumItems];
// Retrieve and cache the system's default "medium" animation time
mAnimationDuration = getResources().getInteger(android.R.integer.config_mediumAnimTime);
// and "short"
mAnimationDurationShort = getResources().getInteger(android.R.integer.config_shortAnimTime);
mCurrentIndex = 0;
mLayoutInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
//TODO: shouldn't be null, should be any ViewGroup with the right LayoutParams
mParentGroup = null;
}
public abstract void onCreateViewContent(LayoutInflater layoutInflater, ViewGroup parentGroup, View[] containerViews, int index);
public boolean createView() {
if (mCurrentIndex >= mMaxNumItems) {
return false; // indicates to terminate the loop
}
// handle/execute the concrete definition of the view content defined by the child class
onCreateViewContent(mLayoutInflater, mParentGroup, mContainerViews, mCurrentIndex);
// only the first container view should be visible
if (mCurrentIndex == 0) {
mContainerViews[mCurrentIndex].setVisibility(View.VISIBLE);
mIndexVisibleItem = mCurrentIndex;
} else {
mContainerViews[mCurrentIndex].setVisibility(View.GONE);
}
// if you click on the container view, show next container view with a crossfade animation
mContainerViews[mCurrentIndex].setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
crossfade(true);
hasBeenClicked = true;
}
});
// add the container view to the FrameLayout
addView(mContainerViews[mCurrentIndex]);
mCurrentIndex++;
return true;
}
public void crossfade(boolean manuallyClicked) {
//only rotate when example is actually shown and at least one content item was created. This may also prevent NPEs due to incompletely loaded views.
if(!this.isShown() || mCurrentIndex == 0)
return;
//when example was previously clicked, don't do anything
if(!manuallyClicked && hasBeenClicked){
hasBeenClicked = false;
return;
}
int numTotalItems = mCurrentIndex;
final int indexVisibleItem = mIndexVisibleItem;
int nextIndex = indexVisibleItem + 1;
if (nextIndex >= numTotalItems) {
nextIndex = 0;
}
final boolean hasOnlyOneItem;
if (numTotalItems == 1) {
hasOnlyOneItem = true;
} else {
hasOnlyOneItem = false;
}
if (hasOnlyOneItem) { //there is only one item in the mContainerViews
mContainerViews[indexVisibleItem].animate().alpha(0.5f).setDuration(mAnimationDurationShort).setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
mContainerViews[indexVisibleItem].animate().alpha(1f).setDuration(mAnimationDurationShort).setListener(null);
}
});
} else {
// Set the next view to 0% opacity but visible, so that it is visible (but fully transparent) during the animation.
mContainerViews[nextIndex].setAlpha(0f);
mContainerViews[nextIndex].setVisibility(View.VISIBLE);
// Animate the next view to 100% opacity, and clear any animation
// listener set on the view.
mContainerViews[nextIndex].animate().alpha(1f).setDuration(mAnimationDuration).setListener(null);
// Animate the current view to 0% opacity. After the animation ends,
// set its visibility to GONE as an optimization step (it won't participate in layout passes, etc.)
mContainerViews[indexVisibleItem].animate().alpha(0f).setDuration(mAnimationDuration).setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
mContainerViews[indexVisibleItem].setVisibility(View.GONE);
}
});
}
mIndexVisibleItem = nextIndex;
}
#Override
public void cleanup() {
}
}
I could find a solution to change camera in some seconds(But keep in mind as Alex Cohn said you can't change the camera in 2 seconds because, normally it takes more than 2 seconds to start preview and it depends on the device) by changing your code a little bit. please use below code and check.
Note: I have not implemented any orientation changes and taking picture functions, I Hope you have already developed those functions, in fact you have only asked for changing the camera automatically in some seconds.
I used dialog fragment to show the preview in a dialog. Here is the code for CameraExample
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.Camera;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.support.v4.app.DialogFragment;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
/**
* Created by Admin on 6/26/2017.
*/
public class CameraExample extends DialogFragment {
private final static String TAG = "CameraExample";
private Camera mCamera;
private CameraPreview mPreview;
private Context mContext;
private View view;
private int mCamId = 0;
public CameraExample() {
mPreview = null;
mContext = getContext();
}
// A safe way to get an instance of the Camera object.
public static Camera getCameraInstance(int cameraId) {
Camera c = null;
try {
// attempt to get a Camera instance
c = Camera.open(cameraId);
} catch (Exception e) {
// Camera is not available (in use or does not exist)
Log.e(TAG, "CameraExample: " + "camera not available (in use or does not exist); " + e.getMessage());
}
return c; // returns null if camera is unavailable
}
private void initCamera(Context context, int cameraId) {
// Check if this device has a camera
if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
// no camera on this device
Log.e(TAG, "CameraExample: " + "this device has no camera");
} else {
// this device has a camera
int numCameras = Camera.getNumberOfCameras();
if (numCameras >= 0) {
mCamera = getCameraInstance(cameraId);
if (mCamera != null) {
Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
Camera.getCameraInfo(cameraId, cameraInfo);
try {
//Create our Preview view and set it as the content of this LinearLayout View
mPreview = new CameraPreview(context, mCamera, cameraId);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
mPreview.setLayoutParams(layoutParams);
} catch (RuntimeException e) {
Log.e(TAG, "Camera failed to open: " + e.getLocalizedMessage());
}
}
}
}
}
private CountDownTimer countDownTimer;
private void switchCam() {
//10 seconds
countDownTimer = new CountDownTimer(10000, 1000) {
#Override
public void onTick(long l) {
System.out.println(l + " left");
}
#Override
public void onFinish() {
cleanup();
startCam();
}
}.start();
}
private void startCam() {
initCamera(getContext(), mCamId);
FrameLayout previewFrame = (FrameLayout) view.findViewById(R.id.preview);
previewFrame.removeAllViews();
// Add preview for inflation
previewFrame.addView(mPreview);
mCamId = mCamId == 0 ? 1 : 0;
switchCam();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
getDialog().getWindow().setGravity(Gravity.CENTER);
// getDialog().getWindow().setBackgroundDrawableResource(android.R.color.transparent);
view = inflater.inflate(R.layout.camera_fragment, container, false);
startCam();
return view;
}
#Override
public void onPause() {
super.onPause();
cleanup();
if (countDownTimer != null)
countDownTimer.cancel();
}
#Override
public void onStart() {
super.onStart();
getDialog().getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
}
public void cleanup() {
if (mCamera != null) {
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
}
}
And also I had to change your preview class also. See below for the code.
import android.content.Context;
import android.hardware.Camera;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import java.io.IOException;
/**
* Created by Admin on 6/26/2017.
*/
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private static final String TAG = "CameraPreview";
private Context mContext;
private SurfaceHolder mHolder;
private Camera mCamera;
private int mCameraId;
public CameraPreview(Context context, Camera camera, int cameraId) {
super(context);
mContext = context;
mCamera = camera;
mCameraId = cameraId;
// Install a SurfaceHolder.Callback so we get notified when the
// underlying surface is created and destroyed.
mHolder = getHolder();
mHolder.addCallback(this);
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
// The Surface has been created, now tell the camera where to draw the preview.
try {
mCamera.setPreviewDisplay(holder);
mCamera.startPreview();
} catch (IOException e) {
Log.e(TAG, "CameraExample: " + "Error setting camera preview: " + e.getMessage());
}
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
// empty. Take care of releasing the Camera preview in your activity.
// stop preview before making changes
try {
mCamera.stopPreview();
} catch (Exception e) {
// ignore: tried to stop a non-existent preview
}
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
// If your preview can change or rotate, take care of those events here.
// Make sure to stop the preview before resizing or reformatting it.
if (mHolder.getSurface() == null) {
// preview surface does not exist
return;
}
}
}
There is a double init bug in your class. I could have it running and see the camera switch from 0 to 1 and back, after 10 sec, after the following fix:
I removed call to initCamera() from the CameraExample constructor. Instead, I put there call to CreateView(). Alternatively, you can call CreateView(), which is a public method, from the place where you create new CameraExample(context, i).
Note that this refers to the code in dropbox, not what is posted in the question.
You have to stop the camera, switch the facing and then start it again:
Use a timer and call switchFacing() periodically every 2 seconds.
But be aware of:
This class was deprecated in API level 21.
We recommend using the new android.hardware.camera2 API for new
applications.
edit2: Here is the complete class ready to use.
//This class uses Camera1 API to be backwards compatible.
private static String TAG = "CameraManager";
private Context mContext = null;
private SurfaceView mPreview = null;
private SurfaceHolder mHolder = null;
private Camera mCamera = null;
private int mFrontFaceID = -1;
private int mBackFaceID = -1;
private int mActualFacingID = -1;
public CameraManager(Context context, SurfaceView preview) {
mContext = context;
mPreview = preview;
mHolder = mPreview.getHolder();
mHolder.addCallback(this);
}
//called in onCreate
public void init() {
Camera.CameraInfo info = new Camera.CameraInfo();
for (int i = 0; i < Camera.getNumberOfCameras(); i++) {
Camera.getCameraInfo(i, info);
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
mFrontFaceID = i;
}
if (info.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
mBackFaceID = i;
}
}
if (mActualFacingID == -1) {
if (mFrontFaceID != -1) {
mActualFacingID = mFrontFaceID;
} else {
mActualFacingID = mBackFaceID;
}
}
//At least one one camera will be available because of manifest declaration
}
//called first on surface created
public void start() {
Log.i(TAG, "startCamera()");
if (mCamera == null) {
mCamera = getCameraInstance(mActualFacingID);
}
if (mCamera == null) {
Log.i(TAG, "can't get camera instance");
return;
}
try {
mCamera.setPreviewDisplay(mHolder);
} catch (IOException e) {
e.printStackTrace();
}
setCameraDisplayOrientation();
setBestSupportedSizes();
mCamera.startPreview();
}
public void stop() {
Log.i(TAG, "stopCamera()");
if (mCamera != null) {
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
}
public void switchFacing() {
if (mFrontFaceID == -1 || mBackFaceID == -1) {
return;
}
stop();
if (mActualFacingID == mFrontFaceID) {
mActualFacingID = mBackFaceID;
} else {
mActualFacingID = mFrontFaceID;
}
start();
}
public Camera getCameraInstance(int cameraID) {
Camera c = null;
if (cameraID != -1) {
try {
c = Camera.open(cameraID);
} catch (Exception e) {
e.printStackTrace();
Log.i(TAG, "error opening camera: " + cameraID);
}
}
return c;
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
Log.i(TAG, "surfaceCreated()");
start();
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Log.i(TAG, "surfaceChanged()");
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.i(TAG, "surfaceDestroyed()");
stop();
}
private void setBestSupportedSizes() {
if (mCamera == null) {
return;
}
Camera.Parameters parameters = mCamera.getParameters();
List<Point> pictureSizes=getSortedSizes(parameters.getSupportedPictureSizes());
List<Point> previewSizes=getSortedSizes(parameters.getSupportedPreviewSizes());
Point previewResult=null;
for (Point size:previewSizes){
float ratio = (float) size.y / size.x;
if(Math.abs(ratio-4/(float)3)<0.05){ //Aspect ratio of 4/3 because otherwise the image scales to much.
previewResult=size;
break;
}
}
Log.i(TAG,"preview: "+previewResult.x+"x"+previewResult.y);
Point pictureResult=null;
if(previewResult!=null){
float previewRatio=(float)previewResult.y/previewResult.x;
for (Point size:pictureSizes){
float ratio = (float) size.y / size.x;
if(Math.abs(previewRatio-ratio)<0.05){
pictureResult=size;
break;
}
}
}
Log.i(TAG,"preview: "+pictureResult.x+"x"+pictureResult.y);
if(previewResult!=null && pictureResult!=null){
Log.i(TAG,"best preview: "+previewResult.x+"x"+previewResult.y);
Log.i(TAG, "best picture: " + pictureResult.x + "x" + pictureResult.y);
parameters.setPreviewSize(previewResult.y, previewResult.x);
parameters.setPictureSize(pictureResult.y, pictureResult.x);
mCamera.setParameters(parameters);
mPreview.setBackgroundColor(Color.TRANSPARENT); //in the case of errors needed
}else{
mCamera.stopPreview();
mPreview.setBackgroundColor(Color.BLACK);
}
}
private List<Point> getSortedSizes(List<Camera.Size> sizes) {
ArrayList<Point> list = new ArrayList<>();
for (Camera.Size size : sizes) {
int height;
int width;
if (size.width > size.height) {
height = size.width;
width = size.height;
} else {
height = size.height;
width = size.width;
}
list.add(new Point(width, height));
}
Collections.sort(list, new Comparator<Point>() {
#Override
public int compare(Point lhs, Point rhs) {
long lhsCount = lhs.x * (long) lhs.y;
long rhsCount = rhs.x * (long) rhs.y;
if (lhsCount < rhsCount) {
return 1;
}
if (lhsCount > rhsCount) {
return -1;
}
return 0;
}
});
return list;
}
//TAKE PICTURE
public void takePhoto() {
if (mCamera != null) {
mCamera.takePicture(null, null, this);
}
}
#Override
public void onPictureTaken(byte[] data, Camera camera) {
//do something with your picture
}
//ROTATION
private void setCameraDisplayOrientation() {
if (mCamera != null) {
mCamera.setDisplayOrientation(getRotation());
}
}
public int getRotation() {
Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(mActualFacingID, info);
int rotation = ((WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay()
.getRotation();
int degrees = 0;
switch (rotation) {
case Surface.ROTATION_0:
degrees = 0;
break;
case Surface.ROTATION_90:
degrees = 90;
break;
case Surface.ROTATION_180:
degrees = 180;
break;
case Surface.ROTATION_270:
degrees = 270;
break;
}
int result;
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
result = (info.orientation + degrees) % 360;
result = (360 - result) % 360; // compensate the mirror
} else { // back-facing
result = (info.orientation - degrees + 360) % 360;
}
return result;
}
In some class call:
SurfaceView preview = (SurfaceView) findViewById(R.id.surfaceView);
CameraManager mgr = new CameraManager(MainActivity.this, MainActivity.this, preview);
mgr.init();
...
mgr.takePhoto(); //surface must already be created
mgr.switchFacing();
mgr.takePhoto();
This code should support almost all devices. The most supported aspect ratio is 4:3, the code takes care of that.
edit3: The surface view must be in the xml of course
<SurfaceView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/surfaceView" />
There is no way to switch the camera quickly. The time it takes to stop the camera, close it, open another camera, and start preview depends on the device, but in many cases (and sometimes on powerful modern devices) it will be more than 2 seconds that you put as your goal.
On the other hand, some Android devices support simultaneous operation of two cameras, see Is it possible to use front and back Camera at same time in Android and Android, Open Front and Back Cameras Simultaneously. So, on some Snapdragon 801 based devices, you can keep both cameras 'ready' and switch the video flow up to 30 times per second.
i think You should use this
mCamera= Camera.open(cameraId);
0 for CAMERA_FACING_BACK
1 for CAMERA_FACING_FRONT
for more reference flow this:-
https://developer.android.com/reference/android/hardware/Camera.html#open(int)
https://developer.android.com/reference/android/hardware/Camera.CameraInfo.html#CAMERA_FACING_BACK
We can use threads to keep one camera active and let it stay for certain time. Swap camera and repaeat the same for infinite time.
private boolean isActive;
public void onStart(){
super.onStart();
isActive = true;
continuousCameraChange(your_camera)
}
public void onResume(){
super.onResume();
isActive = true;
continuousCameraChange(your_camera)
}
public void onPause(){
super.onPause();
isActive = false;
}
public void onDestroy(){
super.onDestroy();
isActive = false;
}
public void onStop(){
super.onStop();
isActive = false;
}
private void continuousCameraChange(Camera camera){
do{
switchCamera(camera);
}while(isActive);
}
private void switchCamera(Camera camera){
if (Camera.CameraInfo.facing == CAMERA_FACING_BACK){
try{
Thread.sleep(2000);
camera.open(Camera.CameraInfo.CAMERA_FACING_BACK);
}catch(InterruptedException ex){
Thread.currentThread().interrupt();
}
//change your camera
Camera.CameraInfo.facing == CAMERA_FACING_FRONT;
}else{
try{//change 2000 to change the time for which your camera stays available
Thread.sleep(2000);
camera.open(Camera.CameraInfo.CAMERA_FACING_FRONT);
}catch(InterruptedException ex){
Thread.currentThread().interrupt();
}
//change your camera
Camera.CameraInfo.facing == CAMERA_FACING_BACK;
}
}
I guess this is what you are looking for:
ImageButton useOtherCamera = (ImageButton) findViewById(R.id.useOtherCamera);
//if phone has only one camera, hide "switch camera" button
if(Camera.getNumberOfCameras() == 1){
useOtherCamera.setVisibility(View.INVISIBLE);
}
else {
useOtherCamera.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (inPreview) {
camera.stopPreview();
}
//NB: if you don't release the current camera before switching, you app will crash
camera.release();
//swap the id of the camera to be used
if(currentCameraId == Camera.CameraInfo.CAMERA_FACING_BACK){
currentCameraId = Camera.CameraInfo.CAMERA_FACING_FRONT;
}
else {
currentCameraId = Camera.CameraInfo.CAMERA_FACING_BACK;
}
camera = Camera.open(currentCameraId);
//Code snippet for this method from somewhere on android developers, i forget where
setCameraDisplayOrientation(CameraActivity.this, currentCameraId, camera);
try {
//this step is critical or preview on new camera will no know where to render to
camera.setPreviewDisplay(previewHolder);
} catch (IOException e) {
e.printStackTrace();
}
camera.startPreview();
}
This is a sample code where I am switching between front and back camera on the fly. Hope it will help.
Problem background
I am developing a VR Project on Unreal Engine 4, and the project requires the usage of Android's native camera. Since there are no built-in functions in UE4 to iteract with Android's native methods, I customized this plugin under my need.
The original plugin uses the JNI interface to iteract with C++ code. It calls camera.open() and camera.startPreview() on UE4's EventBeginPlay, and calls camera.stopPreview() and camera.Release() on UE4's EventEndPlay. Since it is a known issue that EventEndPlay never fires up on Android platform, I decided to manipulate the camera in onResume() and onPause() methods. Here is the code:
<gameActivityClassAdditions>
<insert>
/* Unrelevant code goes here */
...
...
/* End of unrelevant code */
public void AndroidThunkJava_startCamera()
{
surfaceTexture = new SurfaceTexture(10);
surfaceTexture.setDefaultBufferSize(preferredWidth, preferredHeight);
if (camera == null){
try {
camera = Camera.open();
} catch (RuntimeException exc) {
return;
}
}
try {
camera.setPreviewTexture(surfaceTexture);
} catch (IOException t) {
return;
}
Parameters cameraParam = camera.getParameters();
cameraParam.setPreviewFormat(ImageFormat.NV21);
cameraParam.setPreviewSize(preferredWidth, preferredHeight);
cameraParam.setPreviewFpsRange(preferredFPS, preferredFPS);
cameraParam.setFocusMode(Camera.Parameters.FOCUS_MODE_MACRO);
if (cameraParam.isVideoStabilizationSupported()) {
cameraParam.setVideoStabilization(false);
}
if (cameraParam.isAutoWhiteBalanceLockSupported()) {
cameraParam.setAutoWhiteBalanceLock(false);
}
camera.setParameters(cameraParam);
camera.setPreviewCallback(new PreviewCallback() {
#Override
public void onPreviewFrame(byte[] data, Camera camera) {
int Height = camera.getParameters().getPreviewSize().height;
int Width = camera.getParameters().getPreviewSize().width;
// calling C++ function via JNI interface
processFrameData(Width, Height, data);
}
});
camera.startPreview();
}
public void AndroidThunkJava_stopCamera()
{
if (camera != null)
{
camera.stopPreview();
camera.release();
camera = null;
}
}
</insert>
</gameActivityClassAdditions>
<gameActivityOnPauseAdditions>
<insert>
AndroidThunkJava_stopCamera();
</insert>
</gameActivityOnPauseAdditions>
<gameActivityOnResumeAdditions>
<insert>
AndroidThunkJava_startCamera();
</insert>
</gameActivityOnResumeAdditions>
The problem
The camera works fine every second time. That means:
I open the app, camera is working. I pushed the home button (which triggers onPause() method), then switch back to the app (triggers onResume() method). Pushed the home button again, and then switched back - camera works. And so on, the camera works every second time.
Anybody have any idea about this issue? Is that connected to the fact that android.hardware.Camera is deprecated? I'm using API version 19, so it is not possible to use newer android.hardware.camera2.
Here is my onStop and onResume methods. I'm not using onPause. And it works perfectly:
#Override
protected void onResume() {
super.onResume();
if (mCamera == null) {
restartPreview();
}
}
#Override
public void onStop() {
// stop the preview
if (mCamera != null) {
stopCameraPreview();
mCamera.release();
mCamera = null;
}
super.onStop();
}
private void restartPreview() {
if (mCamera != null) {
stopCameraPreview();
mCamera.release();
mCamera = null;
}
getCamera(mCameraID);
startCameraPreview();
}
private void startCameraPreview() {
try {
mCamera.setPreviewDisplay(mSurfaceHolder);
mCamera.startPreview();
setSafeToTakePhoto(true);
setCameraFocusReady(true);
} catch (IOException e) {
Log.d("st", "Can't start camera preview due to IOException " + e);
e.printStackTrace();
}
}
private void stopCameraPreview() {
setSafeToTakePhoto(false);
setCameraFocusReady(false);
// Nulls out callbacks, stops face detection
mCamera.stopPreview();
mPreviewView.setCamera(null);
}
maybe some implementations not equals yours but i think it is help you.
I have an app that takes pictures on a timer. Normally it works fine, except when the screen is rotated while the camera is sleeping. Can anybody tell me why it fails to take a picture after rotating in sleep?
Normally, when the camera goes to sleep and wakes up it executes...
#Override
protected void onPause() {
super.onPause();
camera.stopPreview();
camera.release();
//camera = null; //this give me a null object message
}
#Override
protected void onResume() {
super.onResume();
camera = Camera.open(cameraId);
setCameraDisplayOrientation();
startCameraPreview(surfaceHolder, surfaceView.getWidth(), surfaceView.getHeight());
}
However, when it wakes up after the camera has been rotated in sleep, it first executes onResume(), surfaceChanged(), onPause(), onStop(empty), onDestroy(empty), then the following:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
surfaceView = (SurfaceView) findViewById(R.id.surface);
surfaceHolder = surfaceView.getHolder();
surfaceHolder.addCallback(this);
}
#Override
protected void onStart() {
super.onStart();
}
#Override
protected void onResume() {
super.onResume();
camera = Camera.open(cameraId);
setCameraDisplayOrientation();
startCameraPreview(surfaceHolder, surfaceView.getWidth(), surfaceView.getHeight());
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
camera.stopPreview();
setCameraDisplayOrientation();
startCameraPreview(holder, w, h);
}
public void setCameraDisplayOrientation() {
deviceRotation = getWindowManager().getDefaultDisplay().getRotation();
int degrees = 0;
switch(deviceRotation) {
case Surface.ROTATION_0: degrees=0; break;//vertical
case Surface.ROTATION_90: degrees=-90; break; //left side
case Surface.ROTATION_180: degrees=180; break; //upside-down
case Surface.ROTATION_270: degrees=90; break; //right side
}
Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(cameraId, info);
displayRotation = info.orientation + degrees;
camera.setDisplayOrientation(displayRotation); //changes orientation of camera's display
}
public void startCameraPreview(SurfaceHolder holder, int w, int h) {
double targetRatio = 0;
switch(deviceRotation) {
case Surface.ROTATION_0: targetRatio = (double)w/(double)h; break; //vertical
case Surface.ROTATION_90: targetRatio = (double)h/(double)w; break; //left side
case Surface.ROTATION_180: targetRatio = (double)w/(double)h; break; //upside-down
case Surface.ROTATION_270: targetRatio = (double)h/(double)w; break; //right side
}
Camera.Parameters p = camera.getParameters();
List<Camera.Size> previewSizes = p.getSupportedPreviewSizes();
int optimal_h = 2; //always refers to short length in PreviewSizes
int optimal_w = 1; //always refers to long length in PreviewSizes
for(Camera.Size previewSize : previewSizes) {
if (Math.abs((double)previewSize.height/(double)previewSize.width - targetRatio) <
Math.abs((double)optimal_h/(double)optimal_w - targetRatio)) {
optimal_h = previewSize.height;
optimal_w = previewSize.width;
}
}
p.setPreviewSize(optimal_w, optimal_h); //defines ratio of image preview - sizes can be larger than actual display
p.set("rotation", displayRotation); //required to orient final jpeg file correctly
camera.setParameters(p);
ViewGroup.LayoutParams surfaceParams = surfaceView.getLayoutParams();
switch(deviceRotation) { //aligns ratio of surface view to ratio of image preview
case Surface.ROTATION_0:
surfaceParams.width=ViewGroup.LayoutParams.MATCH_PARENT;
surfaceParams.height=(int)(w*(double)optimal_w/(double)optimal_h);
break; //vertical
case Surface.ROTATION_90:
surfaceParams.width=(int)(h*(double)optimal_w/(double)optimal_h);
surfaceParams.height=ViewGroup.LayoutParams.MATCH_PARENT;
break; //left side
case Surface.ROTATION_180:
surfaceParams.width=ViewGroup.LayoutParams.MATCH_PARENT;
surfaceParams.height=(int)(w*(double)optimal_w/(double)optimal_h);
break; //upside-down
case Surface.ROTATION_270:
surfaceParams.width=(int)(h*(double)optimal_w/(double)optimal_h);
surfaceParams.height=ViewGroup.LayoutParams.MATCH_PARENT;
break; //right side
}
surfaceView.setLayoutParams(surfaceParams);
camera.setPreviewDisplay(holder); //required to startPreview
camera.startPreview();
}
This is not the complete code. This code does produce a few minor image formatting errors, but it still demonstrates the issue. I have stripped some things out for simplicity. The main issue is that when it tries to take a picture after been rotated in sleep it returns an error "Camera is being used after Camera.release() was called". Why is this?
This is really overdue but as I managed to solve a similar problem of mine a minute ago, I thought I'd contribute for the benefit of yourself and anyone else who might be desperately searching Stack.
So when you rotate the device, you are calling onPause and onDestroy, where you are releasing your camera. I noticed you do have camera.open() in your onResume, without a look at your surface-related code I cannot comment. Heres what worked for me.
Firstly, the cameraPreview
`
public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int height) {
try {
this.mCamera.setPreviewDisplay(surfaceHolder);
this.mCamera.startPreview();
} catch (Exception e) {
}
}
public void surfaceCreated(SurfaceHolder surfaceHolder) {
try {
//TODO we need this here too because on SurfaceCreated we always need to open the camera, in case its released
this.mCamera.setPreviewDisplay(surfaceHolder);
this.mCamera.setDisplayOrientation(90);
//this.mCamera.startPreview();
} catch (IOException e) {
}
}
Next, the CameraActivity's Lifecycle code
#Override
public void onResume() {
super.onResume();
try{
mCamera = openFrontFacingCameraGingerbread();
// Add to Framelayout
this.mCameraPreview = new CameraPreview(this, this.mCamera);
mImage.removeAllViews();
this.mImage.addView(this.mCameraPreview);
}catch (RuntimeException ex){
}
}
#Override
public void onPause() {
super.onPause();
captureButton.setText("Begin Capture");
if(CameraActivity.this.timer !=null) {
CameraActivity.this.timer.cancel();
CameraActivity.this.timer.purge();
CameraActivity.this.timer = null;
}
if (mCamera != null) {
mCamera.setPreviewCallback(null);
mCameraPreview.getHolder().removeCallback(mCameraPreview);
mCamera.release();
mCamera = null;
}
}
#Override
protected void onDestroy(){
super.onDestroy();
releaseCameraAndPreview();
}
private void releaseCameraAndPreview() {
if (mCamera != null) {
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
if(mCameraPreview != null){
mCameraPreview.destroyDrawingCache();
mCameraPreview.mCamera = null;
}
}
I copied the code Google example in:
https://developer.android.com/guide/topics/media/camera.html
For the first time I open the Camera, face detection works correctly and the callback is fired, I take the photo, close the camera, open the camera again but this time the face detection callback is not called.
If I'll restart my app, the same thing will happen - face detection will only work for the first time I'll use the camera.
here is my code:
private PictureTakenListener mPictureTakenListener;
AlertDialog mAlertDialog;
private Camera mCamera;
boolean mFaceDetectionAvailable;
private Camera.PictureCallback mPicture = new Camera.PictureCallback() {
#Override
public void onPictureTaken(byte[] data, Camera camera) {
showText("took a photo with a size of " + data.length);
releaseCamera();
if (mAlertDialog != null) mAlertDialog.dismiss();
mPictureTakenListener.photoTaken(data);
}
};
public class MyFaceDetectionListener implements Camera.FaceDetectionListener {
#Override
public void onFaceDetection(Camera.Face[] faces, Camera camera) {
if (faces != null)
if (faces.length > 0)
if (faces[0].score >= 40)
if (!takingPhotoFlag.get()){
takingPhotoFlag.set(true);
mCamera.takePicture(null, null, mPicture);
}
}
}
private void takePicture(PictureTakenListener listener){
mPictureTakenListener = listener;
if (this.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)){
showText("this device has a camera");
showText("device has " + String.valueOf(Camera.getNumberOfCameras()) + " cameras");
int foundCamera = -1;
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){
foundCamera = i;
break;
}
}
if (foundCamera == -1){
return;
}
mCamera = null;
try {
mCamera = Camera.open(foundCamera); // attempt to get a Camera instance
}
catch (Exception e){
e.printStackTrace();
showText("could not open the front camera. " + e.getMessage());
return;
}
showText("successfully opened the camera");
mCamera.setDisplayOrientation(90);
Camera.Parameters params = mCamera.getParameters();
params.setPreviewSize(640, 480);
params.setRotation(270);
params.setPictureSize(640, 480);
mFaceDetectionAvailable = params.getMaxNumDetectedFaces() > 0;
mCamera.setParameters(params);
FrameLayout frameLayout = new FrameLayout(this);
if (!mFaceDetectionAvailable) {
frameLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mCamera.takePicture(null, null, mPicture);
}
});
}
AlertDialog.Builder alert = new AlertDialog.Builder(this);
if (!mFaceDetectionAvailable)
alert.setTitle("CLICK IMAGE TO CAPTURE");
alert.setView(frameLayout).setCancelable(true).setOnCancelListener(new DialogInterface.OnCancelListener() {
#Override
public void onCancel(DialogInterface dialog) {
releaseCamera();
runOnUiThread(new Runnable() {
#Override
public void run() {
//do UI stuff...
}
});
}
});
mAlertDialog = alert.create();
mAlertDialog.show();
mAlertDialog.getCurrentFocus();
mAlertDialog.getWindow().setLayout(960, 1280); //Controlling width and height.
mAlertDialog.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
CameraPreview mPreview = new CameraPreview(this, mCamera);
frameLayout.addView(mPreview);
} else {
showText("no camera on this device");
}
}
public interface PictureTakenListener{
void photoTaken(byte[] image);
}
/** A basic Camera preview class */
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder mHolder;
private Camera mCamera;
public CameraPreview(Context context, Camera camera) {
super(context);
mCamera = camera;
// Install a SurfaceHolder.Callback so we get notified when the
// underlying surface is created and destroyed.
mHolder = getHolder();
mHolder.addCallback(this);
// deprecated setting, but required on Android versions prior to 3.0
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
public void surfaceCreated(SurfaceHolder holder) {
// The Surface has been created, now tell the camera where to draw the preview.
}
public void surfaceDestroyed(SurfaceHolder holder) {
// empty. Take care of releasing the Camera preview in your activity.
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
// If your preview can change or rotate, take care of those events here.
// Make sure to stop the preview before resizing or reformatting it.
if (mHolder.getSurface() == null){
// preview surface does not exist
return;
}
// stop preview before making changes
try {
mCamera.stopPreview();
} catch (Exception e){
// ignore: tried to stop a non-existent preview
}
// set preview size and make any resize, rotate or
// reformatting changes here
// start preview with new settings
try {
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
if (mFaceDetectionAvailable) {
mCamera.startFaceDetection();
mCamera.setFaceDetectionListener(new MyFaceDetectionListener());
}
} catch (Exception e){
Log.d(TAG, "Error starting camera preview: " + e.getMessage());
}
}
}
private void releaseCamera(){
if (mCamera != null){
mCamera.setPreviewCallback(null);
mCamera.setFaceDetectionListener(null);
mCamera.stopFaceDetection();
mCamera.setErrorCallback(null);
mCamera.release();
mCamera = null;
showText("released camera");
}
}
Thank you.
I know there are some similar subjects connecting to this but I couldn't solve mine. anyway,I am trying to make some front camera with "flash" where I am calling Camera.release only once in the whole activities, when surfaceDestroyed(). so here is my MainActivity:
#SuppressWarnings("deprecation")
public class MainActivity extends AppCompatActivity {
private Camera mCamera = null;
private CameraPreview mCameraView = null;
private int cameraId = 0;
private void addView() {
if (!getPackageManager()
.hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
Toast.makeText(this, "No camera on this device", Toast.LENGTH_LONG)
.show();
} else {
cameraId = findFrontFacingCamera();
if (cameraId < 0) {
Toast.makeText(this, "No front facing camera found.",
Toast.LENGTH_LONG).show();
} else {
try {
mCamera = Camera.open(cameraId);
} catch (Exception e) {
Log.d("ERROR", "Failed to get camera: " + e.getMessage());
}
}
}
if (mCamera != null) {
mCameraView = new CameraPreview(this, mCamera);//create a SurfaceView to show camera data
FrameLayout camera_view = (FrameLayout) findViewById(R.id.camera_view);
camera_view.addView(mCameraView);//add the SurfaceView to the layout
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
addView();
ImageButton imgCapture = (ImageButton) findViewById(R.id.imgCapture);
imgCapture.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
WindowManager.LayoutParams layout = getWindow().getAttributes();
layout.screenBrightness = 1F;
getWindow().setAttributes(layout);
setContentView(R.layout.whitescreen);
new CountDownTimer(3000, 1000) {
public void onTick(long millisUntilFinished) {
}
public void onFinish() {
if (CameraPreview.safeToTakePicture) {
CameraPreview.safeToTakePicture = false;
mCamera.takePicture(null, null,
new PhotoHandler(getApplicationContext()));
}
setContentView(R.layout.activity_main);
addView();
}
}.start();
}
});
}
private int findFrontFacingCamera() {
int cameraId = -1;
// Search for the front facing camera
int numberOfCameras = Camera.getNumberOfCameras();
for (int i = 0; i < numberOfCameras; i++) {
Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(i, info);
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
Log.d("Camera", "Camera found");
cameraId = i;
break;
}
}
return cameraId;
}
}
When pressing the capture button I switch the layout to an empty one(white layout), wait 3 seconds take a picture and then add the camera view again, here is my CameraPreview class:
#SuppressWarnings("deprecation")
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder mHolder;
private Camera mCamera;
public static boolean safeToTakePicture = false;
public CameraPreview(Context context, Camera camera) {
super(context);
mCamera = camera;
mCamera.setDisplayOrientation(90);
mHolder = getHolder();
mHolder.addCallback(this);
mHolder.setType(SurfaceHolder.SURFACE_TYPE_NORMAL);
}
#Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
try {
mCamera.setPreviewDisplay(surfaceHolder);
mCamera.startPreview();
} catch (IOException e) {
Log.d("ERROR", "Camera error on surfaceCreated " + e.getMessage());
}
}
#Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i2, int i3) {
if (mHolder.getSurface() == null)
return;
try {
mCamera.stopPreview();
} catch (Exception e) {
Log.d("ERROR", "Trying the camera and it's not running");
}
try {
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
safeToTakePicture = true;
} catch (IOException e) {
Log.d("ERROR", "Camera error on surfaceChanged " + e.getMessage());
}
}
#Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
mCamera.stopPreview();
mCamera.release();
}
}
I get the error Camera is being used after Camera.release was called a lot of times, for example when taking a picture:
07-02 14:49:35.561 19017-19017/davidandguy.com.selfielightcamera E/AndroidRuntime: FATAL EXCEPTION: main
Process: davidandguy.com.selfielightcamera, PID: 19017
java.lang.RuntimeException: Camera is being used after Camera.release() was called
at android.hardware.Camera.native_takePicture(Native Method)
at android.hardware.Camera.takePicture(Camera.java:1523)
at android.hardware.Camera.takePicture(Camera.java:1468)
at davidandguy.com.selfielightcamera.MainActivity$1$1.onFinish(MainActivity.java:65)
at android.os.CountDownTimer$1.handleMessage(CountDownTimer.java:127)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:158)
at android.app.ActivityThread.main(ActivityThread.java:7224)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120)
Or even when onResume() is called, for example, minimize the activity and then run again. I know I need to put somewhere onPause() and onResume() but I don't know where/how to implement it. thanks
This is really overdue but as I managed to solve a similar problem of mine a minute ago, I thought I'd contribute for the benefit of yourself and anyone else who might be desperately searching Stack.
I cant see your lifecycle code here , but heres what worked for me, surfaceDestroyed was empty in my case
Firstly, the cameraPreview
public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int height) {
try {
this.mCamera.setPreviewDisplay(surfaceHolder);
this.mCamera.startPreview();
} catch (Exception e) {
}
}
public void surfaceCreated(SurfaceHolder surfaceHolder) {
try {
//TODO we need this here too because on SurfaceCreated we always need to open the camera, in case its released
this.mCamera.setPreviewDisplay(surfaceHolder);
this.mCamera.setDisplayOrientation(90);
//this.mCamera.startPreview();
} catch (IOException e) {
}
}
Next, the CameraActivity
#Override
public void onResume() {
super.onResume();
try{
mCamera = openFrontFacingCameraGingerbread();
// Add to Framelayout
this.mCameraPreview = new CameraPreview(this, this.mCamera);
mImage.removeAllViews();
this.mImage.addView(this.mCameraPreview);
}catch (RuntimeException ex){
}
}
#Override
public void onPause() {
super.onPause();
captureButton.setText("Begin Capture");
if(CameraActivity.this.timer !=null) {
CameraActivity.this.timer.cancel();
CameraActivity.this.timer.purge();
CameraActivity.this.timer = null;
}
if (mCamera != null) {
mCamera.setPreviewCallback(null);
mCameraPreview.getHolder().removeCallback(mCameraPreview);
mCamera.release();
mCamera = null;
}
}
#Override
protected void onDestroy(){
super.onDestroy();
releaseCameraAndPreview();
}
private void releaseCameraAndPreview() {
if (mCamera != null) {
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
if(mCameraPreview != null){
mCameraPreview.destroyDrawingCache();
mCameraPreview.mCamera = null;
}
}