I have classic android app with camera preview (common implem that can be found in many tutorials [marakana etc.]) that is supposed to take picture in a given time interval. Threading and killing threads is done, errors such "method called after release" are handled. But sometimes the well-known error 100 occurs. I accepted the fact that it happens and tried to handle it too. I implemented ErrorCallback and its onError method where the current camera object is released and instantiated a new one as written in official documentation.
But (with no surprise) it is not enough. New camera is maybe wrongly allocated because an message "CameraDemo has been exited unexpectedly" appears now.
I've read many docs and examples in hope, that a proper proceeding will be somewhere explained but no one has such problem apparently. So I would like to ask what else should I do beside releasing and creating new camera? Here is the code:
ErrorCallback CEC = new ErrorCallback()
{
public void onError(int error, Camera camera)
{
Log.d("CameraDemo", "camera error detected");
if(error == Camera.CAMERA_ERROR_SERVER_DIED)
{
Log.d("CameraDemo", "attempting to reinstantiate new camera");
camera.stopPreview();
camera.setPreviewCallback(null);
camera.release(); //written in documentation...
camera = null;
camera = Camera.open();
}
}
};
Shortly - if I release and recreate camera in onError callback then RuntimeException Method called after release (takePicture) is raised. So should I somehow assign the surface holder to camera again or recreate the surface holder too?
It would be enough to direct me e.g. to some forums, where it is described or solved, etc. Thanks for any help.
In my app to handle the camere i use this :
public void onResume() {
super.onResume();
if(mCamera == null)
mCamera = getCameraInstance():
}
public static Camera getCameraInstance() {
mCamera = null;
try {
mCamera = Camera.open();
Parameters parameters = mCamera.getParameters();
mCamera.cancelAutoFocus();
mCamera.setPreviewCallback(yourPreviewCb);
mCamera.startPreview();
mCamera.setParameters(parameters);
mCamera.autoFocus(yourAutoFocusCB);
} catch (Exception e) {
//TODO
}
return mCamera;
}
The mCamera = null in the getCameraInstance() is just to be sure there is no camera running at all.
I think you need to recreate a complete camera, not just open it with the
camera.open();
Set this in the onResume or in the error callback, depending on your needs.
This is how I fixed it, here is a sample of code, think you get the idea:
private Camera camera;
// code...
public Camera getCameraInstance() {
Camera camera = Camera.open();
// code...
camera.setErrorCallback(new ErrorCallback() {
#Override
public void onError(int error, Camera camera) {
if(error == Camera.CAMERA_ERROR_SERVER_DIED) {
releaseCamera();
startCamera();
}
}
});
return camera;
}
protected void startCamera() {
if(getCamera() == null)
setCamera(getCameraInstance());
refreshCamera();
}
protected void releaseCamera() {
if (getCamera() != null) {
getCamera().release();
setCamera(null);
}
}
public Camera getCamera() {
return camera;
}
public void setCamera(Camera camera) {
this.camera = camera;
}
Related
My app contains three fragments. I need to keep the user experience fluid so I'm using a the setOffscreenPageLimit() method to keep them all alive in memory.
Problem: when I start a new activity (even empty), It loads slowly. Same when I finish it.
I know this is coming from my Camera Preview fragment because when I comment out the init of the camera, everything runs very smoothly.
Here is how I initialize my camera preview on the OnResume method:
mCamera = GetCameraInstance(currentCameraId);
//-- Set the SurfaceView
preview = (SurfaceView) view.findViewById(R.id.camera_preview);
mSurfaceHolder = preview.getHolder();
mSurfaceHolder.addCallback(this);
if (mCamera != null) {
mCamera.setPreviewDisplay(mSurfaceHolder);
mCamera.startPreview();
}
When the new activity is started, the surfaceDestroyed method is called which destroys the Camera preview. When the new activity is terminated, the app recreate a camera view again.
#Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
if (mCamera != null) {
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
}
The time spent on stopPreview() and release() method as seen on traceview. It takes around 700ms to destroy the camera preview.
Screenshot of Traceview
Based on #CommonsWare suggestion, here is how I did it.
First, I placed my mCamera variable in the Application class
public Camera mCamera;
Then, I placed the AsyncTask in my fragment
private class ControlCameraTask extends AsyncTask<Integer, Void, Void> {
protected Void doInBackground(Integer... urls) {
//--
if (app.mCamera != null) {
app.mCamera.stopPreview();
app.mCamera.release();
app.mCamera = null;
} else {
app.mCamera = GetCameraInstance(currentCameraId);
app.mCamera.setPreviewDisplay(app.mSurfaceHolder);
app.mCamera.startPreview();
}
return null;
}
}
Finally, I just call my AsyncTask on onResumeand onPausemethods
#Override
public void onResume() {
super.onResume();
new ControlCameraTask().execute(1);
}
#Override
public void onPause() {
super.onPause();
new ControlCameraTask().execute(1);
}
PS: I removed the trycatches for the code to be easily readable.
I've implemented a service to take a picture from a background thread, but the photo never gets taken on any of my devices... here is the code (logging output below):
public class PhotoCaptureService extends Service {
private static final String TAG = "PhotoCaptureService";
#Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
Log.d(TAG, "Starting the PhotoCaptureService");
takePhoto();
}
private void takePhoto() {
Log.d(TAG, "Preparing to take photo");
Camera camera = null;
try {
camera = Camera.open();
} catch (RuntimeException e) {
Log.e(TAG, "Camera not available", e);
return;
}
if (null == camera) {
Log.e(TAG, "Could not get camera instance");
return;
}
Log.d(TAG, "Got the camera, creating the dummy surface texture");
SurfaceTexture dummySurfaceTexture = new SurfaceTexture(0);
try {
camera.setPreviewTexture(dummySurfaceTexture);
} catch (Exception e) {
Log.e(TAG, "Could not set the surface preview texture", e);
}
Log.d(TAG, "Preview texture set, starting preview");
camera.startPreview();
Log.d(TAG, "Preview started");
camera.takePicture(null, null, new Camera.PictureCallback() {
#Override
public void onPictureTaken(byte[] data, Camera camera) {
Log.d(TAG, "Photo taken, stopping preview");
camera.stopPreview();
Log.d(TAG, "Preview stopped, releasing camera");
camera.release();
Log.d(TAG, "Camera released");
}
});
}
Logging output:
D/PhotoCaptureService﹕ Starting the PhotoCaptureService
D/PhotoCaptureService﹕ Preparing to take photo
D/PhotoCaptureService﹕ Got the camera, creating the dummy surface texture
D/PhotoCaptureService﹕ Preview texture set, starting preview
D/PhotoCaptureService﹕ Preview started
At this point nothing else happens, the onPictureTaken method never gets called and there is no error or exception thrown. Does anyone know why this is happening? I've looked at every camera tutorial on StackOverflow and nothing seems to work.
From my experience and what I've read, the dummy SurfaceTexture strategy doesn't work on all phones. Try instead adding a 1x1 pixel SurfaceView and starting the preview in the SurfaceView.getHolder()'s onSurfaceCreated callback (added via addCallback).
See Taking picture from camera without preview for more information.
I am testing an android application on a samsung gt i8260, a samsung s4 and a sony ericsson xperia mini st15i.
The application at some point of the code while on preview mode and autofocusing constantly (callback calls camera.autoFocus again every time) calls camera.cancelAutoFocus(), then sets some parameters about flashlight (in order to start or stop torch mode) and finally recalling camera.autoFocus.
Both S4 and Xperia work fine. But gt stops responding after calling camera.cancelAutoFocus which neither returns nor throws an exception. It just hangs.
The documentation for cancelAutoFocus:
Cancels any auto-focus function in progress. Whether or not auto-focus is currently in progress, this function will return the focus position to the default. If the camera does not support auto-focus, this is a no-op.
does not explains this behavior.
Removing cancelAutoFocus works for S4, and GT but not for Xperia which throws an exception on setParameters.
Has anyone face the same or any similar problem? How can I overcome this issue? Is it hardware specific or bug?
Though this doesn't explain your app hang, you should consider using continuous focus, i.e. Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE or FOCUS_MODE_CONTINUOUS_VIDEO.
http://developer.android.com/reference/android/hardware/Camera.Parameters.html#FOCUS_MODE_CONTINUOUS_PICTURE
Make sure to check if its supported by the hardware first with getSupportedFocusModes().
This will give you a smooth and continuous focus experience.
This is how I am doing and its working for all devices
I am starting autofocus in surfaceChanged
Start Flash Light if needed
Removing in surfaceDestroyed method
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h)
{
if(mCamera==null)
return;
Camera.Parameters parameters = null;
parameters = mCamera.getParameters();
if (Holder.getSurface() == null)
{ // preview surface does not exist
//mCamera = null;
return;
}
// Stopping the camera preview so as to set the new params
try
{
mCamera.stopPreview();// why the application is crashing here
}
catch(Exception e)
{
e.printStackTrace();
}
try
{
mCamera.setParameters(parameters);
mCamera.startPreview();
//Check Whether device supports AutoFlsh, If you YES then Enable AutoFlash
if (parameters.getSupportedFlashModes().contains(android.hardware.Camera.Parameters.FLASH_MODE_AUTO))
{
parameters.setFlashMode(Parameters.FLASH_MODE_AUTO);
}
if (parameters.getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);//FOCUS_MODE_CONTINUOUS_VIDEO
}
else if (parameters.getSupportedFocusModes().contains(android.hardware.Camera.Parameters.FOCUS_MODE_AUTO))
{
mCamera.autoFocus(myAutoFocusCallback);
//parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
}
}
catch(Exception e1)
{
e1.printStackTrace();
}
}
public void surfaceDestroyed(SurfaceHolder holder)
{
try
{
mCamera.cancelAutoFocus();
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
catch(Exception e)
{
e.printStackTrace();
}
}
// --------------- AutoFocusCallback methods implementations ----------//
AutoFocusCallback myAutoFocusCallback = new AutoFocusCallback()
{
#Override
public void onAutoFocus(boolean arg0, Camera arg1)
{
isAutofoucsed =true;
}
};
I have used zbar scanner for android and it captures the barcodes quite easily.
But the problem is that on phones which have autofocus, it captures the barcodes too quickly to detect it correctly.
If only it could wait for a few milliseconds more, it could then be able to capture more clearer image and thereby not show "not found" page.
How can I solve this problem?
Is there a provision to delay the focus on the barcode?
Maybe a delay in capturing the image?
Are you talking about the example code, CameraTestActivity.java?
Implement a counter that counts for similar scanning results. If the scanning result remains the same (e.g. for 10 times in a row), we can assume the result is quite reliable.
I really like #Juuso_Ohtonen's reply, and actually just used it in my own reader, however if you want an AutoFocus delay you can create a Camera.AutoFocusCallback object and implement its onAutoFocus method with a .postDelayed. This object is then used on your Camera camera.autoFocus() method.
// Mimic continuous auto-focusing
Camera.AutoFocusCallback autoFocusCB = new Camera.AutoFocusCallback() {
public void onAutoFocus(boolean success, Camera camera) {
autoFocusHandler.postDelayed(doAutoFocus, 1000);
}
};
This section is used in the class that extends SurfaceView, which then implements surfaceChanged();
public CameraPreview(Context context, Camera camera,
PreviewCallback previewCb,
AutoFocusCallback autoFocusCb) {
super(context);
mCamera = camera;
previewCallback = previewCb;
autoFocusCallback = autoFocusCb;
// 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 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
}
try {
mCamera.setPreviewDisplay(mHolder);
mCamera.setPreviewCallback(previewCallback);
mCamera.startPreview();
mCamera.autoFocus(autoFocusCallback);
} catch (Exception e) {
Log.d("DBG", "Error starting camera preview: " + e.getMessage());
}
}
i have problems with this, recently i have also problem with just taking 1 photo...
do you know how to solve it? any working code?
i'm doing it like that:
mCamera = Camera.open();
Log.e(TAG, "onCreate");
mCamera.takePicture(null, mPictureCallback, mPictureCallback);
Camera.PictureCallback mPictureCallback = new Camera.PictureCallback() {
public void onPictureTaken(byte[] imageData, Camera c)
{
if (imageData != null) {
//process photo
}
}
};
in onDestroy() i release the camera
on simulator it works, on telephone - 1st time runs without problems but no picture, second and next times - 'force close' - looks like camera is not released ...