PictureCallback.onPictureTaken never called - android

I am writing an application that needs pictures taken with the camera. The problem occurs when I try to take an actual picture. Here's the code that is troubling me:
final ShutterCallback shutterCallback = new ShutterCallback() {
#Override
public void onShutter() {
Log.d(TAG, "onShutter");
}
};
final PictureCallback callback = new PictureCallback() {
#Override
public void onPictureTaken(byte[] data, Camera camera) {
Log.d(TAG, "onPictureTaken - jpeg");
try {
//async task for storing the photo
new SavePhotoTask(CameraView.this.ctx, data).execute();
} catch (final SavePhotoException e) {
//some exceptionhandling
}
}
};
this.camera = Camera.open();
this.camera.setPreviewDisplay(surfaceHolder);
final Camera.Parameters parameters = findBestParameters(w, h);
this.camera.setParameters(parameters);
this.camera.startPreview();
Log.d(TAG, "takePicture now!");
this.camera.takePicture(shutterCallback, null, callback);
On the emulator it seems to work out but on my phone (Motorola Defy - android 2.1).
The actual problem: on the phone the picturecallback is never happening "onPictureTaken" gets never called. The Shuttercallback is executed but the other is not (and I tried with raw instead of jpeg, same thing).
Does anyone know this problem? I just don't see where the difference lies to the emulator right now. I appreciate your help.

I finally went and debugged the problem. All of a sudden it worked, because debugging is much slower: It's a timing problem. The callback takes some time to be called. While debugging the phone had enough time to finish taking the picture ...
Also don't go calling Camera.stopPreview() and Camera.release() too early.

I was having this exact problem. After much debugging I finally realized that the stupid Camera object was getting garbage collected before it had a chance to call the callbacks!
I fixed it by creating a hard reference to the Camera object that I was using. I made it a member of my PictureTaker class, set it before calling takePicture() and then null it out in the jpeg callback after I receive my data. Then I just have to make sure my PictureTaker object won't get gc'd itself, which I do by keeping it around in my Application subclass for the life of the process.
This always works on my Droid RAZR:
public class PictureTaker implements Camera.PictureCallback
{
private Camera mCam;
private MyApp theApp;
public PictureTaker(MyApp app)
{
theApp = app;
}
public void takePicture()
{
try
{
mCam = Camera.open();
}
catch (Exception e)
{
System.out.println("Problem opening camera! " + e);
return;
}
if (mCam == null)
{
System.out.println("Camera is null!");
return;
}
try
{
SurfaceView view = MyApp.getPreviewSurface(); // my own fcn
mCam.setPreviewDisplay(view.getHolder());
mCam.startPreview();
mCam.takePicture(null, null, this);
}
catch (Exception e)
{
System.out.println("Problem taking picture: " + e);
}
}
public void onPictureTaken(byte[] data, Camera cam)
{
theApp.jpegPictureData(data); // also my own fcn
cam.stopPreview();
cam.release();
mCam = null;
}
}

In my case it wasn't calling on picturetaken on a particular device. The problem was caused because the camera was being opened twice on onResume() and oncreate().

Related

Closing the Android CameraDevice is slow and hangs the application

I am writing an Android application that uses the camera.
I took the sample code https://github.com/googlesamples/android-Camera2Basic/ provided by Google. However, I observed that calling the close() method of the CameraDevice takes too long (almost one second on my Samsung Galaxy S8).
This method is called from onPause(), thus the application hangs a little while when the camera fragment is closed.
#Override
public void onPause() {
closeCamera();
stopBackgroundThread();
super.onPause();
}
private void closeCamera() {
try {
mCameraOpenCloseLock.acquire();
if (null != mCaptureSession) {
mCaptureSession.close();
mCaptureSession = null;
}
if (null != mCameraDevice) {
mCameraDevice.close(); // This call takes 1 second!
mCameraDevice = null;
}
if (null != mImageReader) {
mImageReader.close();
mImageReader = null;
}
} catch (InterruptedException e) {
throw new RuntimeException("Interrupted while trying to lock camera closing.", e);
} finally {
mCameraOpenCloseLock.release();
}
}
How can I avoid the application to become unresponsive when the closeCamera() method is being called?
I tried to call it from another thread than the UI thread but the application crashes in some cases.
I faced with this problem when my device (mi a1 - oreo) was flashed with patched boot.
If you have device with patched boot too, try to flash it with stock boot. It helped in my case.

Take picture continuously with autofocus in android

I have made a custom camera view in my application. Basically i need to do something for as long as the camera in my application is open, after focusing.
in my Camera.Parameters, i have used FOCUS_MODE_CONTINUOUS_PICTURE, and it works perfectly as intended. Now, i need a callback from this continuous autofocusing practice where i can take the current focused picture and do something with it.
An alternate approach was to hook a 'Timer' technique, a function that would fire after every certain time period. And then, i could mCamera.autofocus() inside it, take picture and do my work. Unfortunately, this proved a very bad technique, since autofocusing would pose complications, varying for different devices.
So, i was wondering, what would be the perfect solution to this.
UPDATE: After making an attempt with threads
What i want to do is, AutoFocus and take picture again and again as long as the app is in the foreground.
After a number of different attempts, this is the farthest i have been able to come, still not far enough.
Please see the runnable code:
public class OCRRunnable implements Runnable {
static final String TAG = "DBG_" + "OCRRunnable";
private final Object mPauseLockDummyObject;
private boolean mPaused;
private boolean mFinished;
Camera mCamera;
public OCRRunnable(Camera cameraParam) {
mPauseLockDummyObject = new Object();
mPaused = false;
mFinished = false;
mCamera = cameraParam;
}
#Override
public void run()
{
if (mCamera != null) { // since the main activity may have been late opening the camera
try {
mCamera.autoFocus(new mAutoFocusCallback());
Log.d(TAG, "run: mCamera.autofocus()");
} catch (Exception e) {
Log.e(TAG, "run: " + e.getMessage());
}
//sleep necessary //TODO: needs refinement
//try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); }
//Runnable regulator
synchronized (mPauseLockDummyObject) {
while (mPaused) {
try {
mPauseLockDummyObject.wait();
} catch (InterruptedException e) {
Log.e(TAG, "run: " + e.getMessage());
}
}
}
}
}
/**
* Call this on pause of the activity.
*/
public void onPause() {
//Log.d(TAG, "onPause: called");
synchronized (mPauseLockDummyObject) {
mPaused = true;
}
}
/**
* Call this on resume of the activity
*/
public void onResume() {
//Log.d(TAG, "onResume: called");
synchronized (mPauseLockDummyObject) {
mPaused = false;
mPauseLockDummyObject.notifyAll();
}
}
//////////////////////////////////////
protected class mAutoFocusCallback implements Camera.AutoFocusCallback {
#Override
public void onAutoFocus(boolean success, final Camera camera) {
camera.takePicture(null, null, new Camera.PictureCallback() {
#Override
public void onPictureTaken(byte[] data, Camera camera) {
Log.d(TAG, "onPictureTaken() called");
/* NEED TO RUN THIS CODE PART REPETITIVELY */
camera.cancelAutoFocus(); //be ready for next autofocus
camera.startPreview(); //re-start cameraPreview since taking a picture stops it
run(); //TODO
}
});
}
}
}
Here is my relevant fragment:
public class ODFragment extends Fragment {
View rootView;
static final String TAG = "DBG_" + MainActivity.class.getName();
private Camera mCamera;
private CameraPreview mCameraPreview;
int ROIHeight;
FrameLayout frameLayout_cameraLens;
TextView words_container;
Thread ocrThread;
OCRRunnable ocrRunnable; //TODO: this may cause problems because mCamera is null as of this moment
public ODFragment() {}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.fragment_od, container, false);
//one time tasks
words_container = (TextView) rootView.findViewById(R.id.words_container);
setCameraViewDimensions();
ocrThread = new Thread(ocrRunnable); //start it in onResume()
return rootView;
}
#Override
public void onResume() {
super.onResume();
hookCamera();
}
#Override
public void onPause() {
super.onPause();
unhookCamera();
}
private void hookCamera() {
try {
// 1. open camera
mCamera = Camera.open();
// 2. initialize cameraPreview
mCameraPreview = new CameraPreview(this.getActivity(), mCamera);
// 3. add view to frameLayout
frameLayout_cameraLens.addView(mCameraPreview);
mCamera.startPreview();
// 4. hook camera related listeners and threads
ocrRunnable = new OCRRunnable(mCamera);
ocrThread = new Thread(ocrRunnable);
ocrThread.start();
ocrRunnable.onResume();
} catch (Exception e) {
e.printStackTrace();
Log.d(TAG, "Could not Camera.open(): " + e.getMessage());
}
}
private void unhookCamera() {
try {
// -4. unhook camera related listeners ans threads
ocrRunnable.onPause();
ocrThread = null;
ocrRunnable = null;
// -3. remove view from frameLayout
frameLayout_cameraLens.removeView(mCameraPreview);
// -2. destroy cameraPreview
mCameraPreview = null;
// -1. close camera
if (mCamera != null) {
mCamera.release();
mCamera = null;
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void setCameraViewDimensions() {
//calculate and set dimensions of cameraLens
DisplayMetrics displaymetrics = new DisplayMetrics();
getActivity().getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
int width = displaymetrics.widthPixels;
int height = (int) (width * 1.3333333333333);
//Log.d(TAG, "frameLayout_cameraLens dimensions: "+height+"x"+width);
frameLayout_cameraLens = (FrameLayout) rootView.findViewById(R.id.frameLayout_cameraLens);
frameLayout_cameraLens.getLayoutParams().width = width;
frameLayout_cameraLens.getLayoutParams().height = height;
frameLayout_cameraLens.requestLayout();
//set height of ROI
ROIHeight = height / 5;
LinearLayout linearLayout = (LinearLayout) rootView.findViewById(R.id.ROI);
linearLayout.getLayoutParams().height = ROIHeight;
linearLayout.requestLayout();
}
}
Some points:
I think camera.autofocus() happens in a separate thread itself. That is why instead of putting a while(true) loop in run(), i have called run() in the end of the mAutoFocusCallback
Error at this point is that the execution comes to Log.d(TAG, "run: mCamera.autofocus()"); exactly once. Additionally, Log.d(TAG, "onPictureTaken() called"); not called even once.
Here is my relevant log:
05-29 12:51:58.460 W/art: Before Android 4.1, method android.graphics.PorterDuffColorFilter android.support.graphics.drawable.VectorDrawableCompat.updateTintFilter(android.graphics.PorterDuffColorFilter, android.content.res.ColorStateList, android.graphics.PorterDuff$Mode) would have incorrectly overridden the package-private method in android.graphics.drawable.Drawable
05-29 12:51:58.573 D/OpenGLRenderer: Use EGL_SWAP_BEHAVIOR_PRESERVED: true
05-29 12:51:58.655 W/FragmentManager: moveToState: Fragment state for VTFragment{fd65ec0 #0 id=0x7f0c006d android:switcher:2131492973:1} not updated inline; expected state 3 found 2
05-29 12:51:58.962 D/DBG_CameraPreview: CameraPreview() initialized
05-29 12:51:59.079 D/DBG_OCRRunnable: run: mCamera.autofocus()
05-29 12:51:59.097 I/Adreno-EGL: <qeglDrvAPI_eglInitialize:379>: EGL 1.4 QUALCOMM build: Nondeterministic_AU_msm8974_LA.BF.1.1.3_RB1__release_AU (I3f4bae6ca5)
OpenGL ES Shader Compiler Version: E031.29.00.00
Build Date: 02/14/16 Sun
Local Branch: mybranch18261495
Remote Branch: quic/LA.BF.1.1.3_rb1.10
Local Patches: NONE
Reconstruct Branch: NOTHING
05-29 12:51:59.101 I/OpenGLRenderer: Initialized EGL, version 1.4
05-29 12:51:59.366 I/Choreographer: Skipped 46 frames! The application may be doing too much work on its main thread.
05-29 12:51:59.556 I/Timeline: Timeline: Activity_idle id: android.os.BinderProxy#c66e1c1 time:44964952
Use mCamera.autofocus(callback) - and the callback will be triggered when the focus is ready. This is your chance to take the picture. In onPictureTaken() you will probably call mCamera.cancelAutoFocus(); mCamera.autofocus(callback); to allow the camera re-focus again.
Naturally, you may choose not to take picture every time that the camera gets focused.
To improve performance of your camera app, make sure to call Camera.open() on a background HandlerThread - otherwise the focus and picture callback will block the UI thread. This may be acceptable when these callback happen once in a while, but in your scenario they will be activated pretty often.
Update Let me present the suggested loop more clear (in pseudocode):
cameraInit() {
Open camera, set FOCUS_MODE_CONTINUOUS_PICTURE
camera.autofocus(autofocusCallback)
}
autofocusCallback() {
if (it_is_time_to_take_picture) {
takePicture(pictureCallback)
}
else {
autofocusAgain()
}
}
pictureCallback.onPictureTaken(image) {
autofocusAgain()
save image
}
autofocusAgain() {
camera.cancelAutoFocus()
wait(100ms)
camera.autofocus(autofocusCallback)
}

Android camea startPreview() fails when taking multiple pictures

I'm writing an APP for taking multiple pictures by camera. (Like the burst mode in Camera2, but I'm working on older versions.)
It is implemented by takePicture() / onPictureTaken() callback, and work normally on several devices except some performance issues. However I got a RuntimeException of startPreview failed on an HTC Sensation device when trying to start preview for the 2nd picture.
Here is the functions about multiple shot:
final int multishot_count = 3;
....
public void TakePicture()
{
// Invoke multishot from UI when the camera preview is already started.
// startup of multishot task
...
m_take_picture_count = 0;
TakeOnePicture();
}
private void TakeOnePicture()
{
m_camera.takePicture(null, null, new Camera.PictureCallback()
{
#Override
public void onPictureTaken(byte[] data, final Camera camera)
{
m_take_picture_count++;
// feed the JPEG data to a queue and handle it in another thread
synchronized(m_jpeg_data_lock)
{
int data_size = data.length;
ByteBuffer buffer = ByteBuffer.allocateDirect(data_size);
buffer.put(data, 0, data_size);
m_jpeg_data_queue.offer(buffer);
if (m_take_picture_count == multishot_count)
m_is_all_pictures_taken = true;
}
if (m_take_picture_count < multishot_count)
{
m_camera.startPreview();
TakeOnePicture();
}
else
{
// Finalize the multishot task and process the image data
EndTakePictureTask end_take_picture_task = new EndTakePictureTask();
end_take_picture_task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
}
});
}
After the first picture being taken, the startPreview() function may fail in a rate of 10-20% on the specified device. My APP is always run in foreground and has a visible view to show the preview frames. I also try to bypass the JPEG data without processing it, but the error still occurs.
I found a similar report at here. It seems that the underlying thread for taking picture does not finish its works, but we have no way to check it. So I try to catch the exception from startPreview() and sleep a while:
if (m_take_picture_count < multishot_count)
{
boolean is_try_start_preview = true;
while (is_try_start_preview)
{
try
{
m_camera.startPreview();
is_try_start_preview = false;
}
catch (RuntimeException e)
{
try
{
Thread.sleep(50);
}
catch (InterruptedException e2)
{
e2.printStackTrace();
}
}
}
TakeOnePicture();
}
But once startPreview fails, it will fail forever no matter how many times I sleep.
I also found that you need to call setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS) for the surface holder in older Android versions to let startPreview() work correctly. But the device has Android version 4.0.3 and should not require this call. I still try to add it but the issue remains the same. Moreover, the first call of startPreview() works correctly while the second call after onPictureTaken() gets the error. This is different from the issue of missing setType() in older versions.
Finally I use an workaround that may greatly hurt the performance:
if (m_take_picture_count < multishot_count)
{
try
{
m_camera.startPreview();
}
catch (RuntimeException e)
{
// Fail to start preview. Workaround: reopen camera.
RestartCamera();
}
TakeOnePicture();
}
That is, when startPreview() fails, I just close the camera, reopen it with the same settings, then start the preview to take another picture.
I want to know if I miss something about the take picture APIs, and is there a solution better than this brute-force workaround.
Thanks for the suggestion from Alex Cohn, I found a solution. This solution is only tested on the HTC Sensation device, but it has dramatic effect on this device.
I just put a sleep instruction before startPreview() that would be called in onPictureTaken():
try
{
Thread.sleep(20);
}
catch (Exception e)
{
e.printStackTrace();
}
Then I run a test 100 times, where each run takes 3 pictures in succession. With the sleep, I didn't get any error from startPreview() in 100 runs. If There is no sleep, I need to reopen the camera 78 times, and restart the device 8 times in 100 runs.

How to save photo from Camera Android

Edit: I found out the problem is in the onPreviewFrame function:
#Override
public void onPreviewFrame(byte[] data, Camera camera) {
if (frameCount == 0) {
start = System.currentTimeMillis();
} else if (frameCount % 100 == 0) {
Log.e("FPS", 1000 * frameCount
/ (System.currentTimeMillis() - start) + "");
}
frameCount++;
Bitmap temp = BitmapFactory.decodeByteArray(data, 0, data.length);
try {
temp.compress(CompressFormat.JPEG, 90, new FileOutputStream(new File("/sdcard/"+frameCount+".jpg")));
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
camera.addCallbackBuffer(data);
}
If I remove all the code related to Bitmap then the program can take a picture without any error. How can I save both frame images and the better image produced with Camera.takePicture?
I tried to save photo from camera when a button is touched and start another activity to edit the photo.
mTakePic.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
mTakePic.setImageDrawable(takePicDown);
mCamera.takePicture(new ShutterCallback() {
#Override
public void onShutter() {
// TODO Auto-generated method stub
}
}, null, new PictureCallback() {
#Override
public void onPictureTaken(byte[] yuv, Camera camera) {
Intent editImage = new Intent(getApplicationContext(), EditActivity.class);
if(savePhoto != null) {
try {
savePhoto.get();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
savePhoto = new SavePhotoTask();
savePhoto.execute(yuv);
// releaseCamera();
startActivity(editImage);
}
});
} else if (event.getAction() == MotionEvent.ACTION_UP) {
mTakePic.setImageDrawable(takePicUp);
}
return true;
}
});
Sometimes it runs smoothly without error but most times it ends up an error and I have to restart my phone to get the camera working again. I used a SurfaceView for previewing camera frame.
Edit: Sorry, I mistook the question, I thought you were linking the crash you mentioned to the SavePhotoTask. Here's how you can save a photo from your PictureCallback
The first argument to onPictureTaken is a byte array that contains the image. Simply writing it out to a file is the best way I've found to save the image. Something like this...
FileOutputStream out = new FileOutputStream("path/to/image");
out.write(data, 0, data.length);
out.close();
Where "data" is the first arg from onPictureTaken. This same technique should work to save each frame in onPreviewFrame but I've never tried this so I can't be sure. It seems like you have the idea of putting this operation on a separate thread, which is excellent and I'd continue exploring that option. I'd avoid using a Bitmap however, as this will take time and resources, and could lead to OOM errors if you're converting many frames concurrently and have all those Bitmaps in memory.
Once you make the call to Camera.open(), your app is putting a lock on the camera and blocking any other applications (including subsequent instances of your own application) from using it. A call must be made to Camera.release() when your application is done or the camera will remain locked until, as you've noted, the phone is restarted. The dev docs for the Camera has a good checklist to follow at the top in order to ensure the class gets used correctly.
I've had success making the call to Camera.open() in onResume() and Camera.release() in onPause(). To keep things smooth and bug free, you may want to manage your start and stop preview at these points as well.

What's the best way to call StartPreview() after an image is captured?

Once a call is made to Camera.takePicture(), my preview will stop updating as described in the docs. What's the best way to detect that the image capture process is finished and call startPreview() to make it start updating again?
The call can't be placed in any of the callbacks passed to takePicture, according to the docs, as they should all have returned before I invoke it.
My current best guess is to create a Handler and post a delayed Runnable to it from the JPEG callback (or whichever is the most last defined callback to return).
Since the PictureCallback is started in a separate thread anyway (it will not lock the UI), you don't need to use an AsyncTask to call the capture.
There are two ways to do what you want to do, the easiest is the following:
mCamera.startPreview(); //preview has to be started before you can take a picture
mCamera.takePicture(null, null, mPictureCallback); //take a picture
private PictureCallback mPictureCallback = new PictureCallback() {
#Override
public void onPictureTaken(byte[] data, Camera camera) {
camera.startPreview(); //once your camera has successfully finished
//its capture it's safe to restart the preview
... //anything else you want to do to process that image
}
}
The second would be using an anonymous function, e.g.:
mCamera.takePicture(null, null, new PictureCallback(){
...
});
Both have their uses, depending on your needs.
You should start the mCamera.takePicture from within an AsyncTask (or a thread) however AsyncTaks's are the easier option.
A really simple implementation (which of course can be modified) is to:
The method called to take the picture
/**
* Execute the AsyncTask that will handle the preview of the captured photo.
*/
public void takePicture() {
TakePictureTask takePictureTask = new TakePictureTask();
takePictureTask.execute();
}
The AsyncTask subclass
/**
* A pretty basic example of an AsyncTask that takes the photo and
* then sleeps for a defined period of time before finishing. Upon
* finishing, it will restart the preview - Camera.startPreview().
*/
private class TakePictureTask extends AsyncTask<Void, Void, Void> {
#Override
protected void onPostExecute(Void result) {
// This returns the preview back to the live camera feed
mCamera.startPreview();
}
#Override
protected Void doInBackground(Void... params) {
mCamera.takePicture(null, null, mPictureCallback);
// Sleep for however long, you could store this in a variable and
// have it updated by a menu item which the user selects.
try {
Thread.sleep(3000); // 3 second preview
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
}
The PictureCallback field
private PictureCallback mPictureCallback = new PictureCallback() {
#Override
public void onPictureTaken(byte[] data, Camera camera) {
File file = null;
// Check whether the media is mounted with read/write permission.
if (Environment.MEDIA_MOUNTED.equals(
Environment.getExternalStorageState())) {
file = getOutputMediaFile();
}
if (file == null) {
Log.d(TAG, "Error creating media file, check storage persmissions!");
return;
}
try {
FileOutputStream fileOutputStream = new FileOutputStream(file);
fileOutputStream.write(data);
fileOutputStream.close();
} catch (FileNotFoundException e) {
Log.d(TAG, "File not found: " + e.getMessage());
} catch (IOException e) {
Log.d(TAG, "Error accessing file: " + e.getMessage());
}
}
};

Categories

Resources