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.
Related
Please check this link. Following code is from this link. I am not sure why it says we have to use thread sleep to limit number of touch events.
I have a surfaceview and gamethread which processes events pushed by touch event. Is this sleep required or should I handle the sleep in gamethread instead by controlling framerate.
try {
Thread.sleep(16);
} catch (InterruptedException e) {
}
return true;
}
whole code
#Override
public boolean onTouchEvent(MotionEvent event) {
// we only care about down actions in this game.
try {
// history first
int hist = event.getHistorySize();
if (hist > 0) {
// add from oldest to newest
for (int i = 0; i < hist; i++) {
InputObject input = inputObjectPool.take();
input.useEventHistory(event, i);
gameThread.feedInput(input);
}
}
// current last
InputObject input = inputObjectPool.take();
input.useEvent(event);
gameThread.feedInput(input);
} catch (InterruptedException e) {
}
// don't allow more than 60 motion events per second
try {
Thread.sleep(16);
} catch (InterruptedException e) {
}
return true;
}
Your link points to an article that has been posted in 2009, you should really try to find one that is more up to date.
The code itself looks wrong, the author explains that his code is designed to avoid blocking the UI thread but then calls Thread.sleep(16) in onTouchEvent() which is called from the UI thread and blocks it.
What about this example? I haven't read it in detail, but the code looks much better : https://www.codeproject.com/Articles/827608/Android-Basic-Game-Loop
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.
I am able to pause/resume my game by making my game thread(which holds/synchronized with surfaceview) wait/notifyAll. This all runs well using the in game pause button.
However, when I click home/back button I am able to pause my game, but when I resumes my game by clicking on its icon, I receive non responsive game screen back to me.
I have put logs on OnResume() method but nothing gets printed in LogCat!
If I click on my game screen I get error dialog to "Force Close" or "Wait" on my game activity.
Why I am getting this non responsive screen as if my application Hangged after pause ?
How can I handle physical buttons in same way as inGame pause/resume button ?
here is my logcat view of the operation. you can see onResume sop not getting printed similar to onPause.
EDIT : Code called by my pause/resume button and by onPause/onResume functions
where "this" is thread class itself
protected void setPause(){
synchronized (this) {
isPaused = true;
}
}
protected void setResume(){
synchronized (this) {
isPaused = false;
this.notifyAll();
}
}
Game Loop Code :
synchronized (this) {
while (running) {
if (isPaused) {
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else {
Canvas c = null;
try {
c = view.getHolder().lockCanvas();
synchronized (view.getHolder()) {
view.onDraw(c);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (c != null) {
view.getHolder().unlockCanvasAndPost(c);
}
}
sleepTime = ticksPS
- (System.currentTimeMillis() - startTime);
try {
if (sleepTime > 0)
sleep(sleepTime);
else
sleep(10);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
This how I am calling it from surface view :
public void surfaceCreated(SurfaceHolder holder) {
// start the game loop
System.out.println("Starting Game Loop #########");
if(isPaused){
System.out.println("GamePaused");
gameLoopThread.setResume();
}
else{
System.out.println("Game Starting");
gameLoopThread.setRunning(true);
}
gameLoopThread.start();
}
EDIT 2:
If I Pause my game using pause button and let my phone get locked automatically... after resuming I am able to play normally with out any issue.
As per logs I see surface destroyed is not getting called in this case.
After onResume, your SurfaceView is created again, but it lost all the data it had before (all info needed for onDraw). you have to save the data in surfaceDestroyed or before, and reload it in the onCreate of your surfaceview
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());
}
}
};
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().