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.
Related
I am trying to make an android app that captures photos after every 5 seconds. Currently the technique i'm using, uses the phone's camera app to capture the photo. It requires the user to capture the photo and then to press ok and only after that the control returns back to the android app. I found some codes to do the same without human interaction, but since i'm new to android, i'm not able to understand codes them because most of them are incomplete and divided into multiple activities. Can this be done using just one activity?
EDIT :
The codes mentioned above can be found here
I suggest to you to use this library (here documentation) instead the official Camera Api of Android that could be really tricky to use for beginners
Then your code could be like this
private final Handler handler = new Handler(); //This should be declared before OnCreate
private Runnable photoRunnable; //This also
CameraView camera = findViewById(R.id.camera);
camera.addCameraListener(new CameraListener() {
public void onPictureTaken(PictureResult result) {
//Handle result here!
}
});
photoRunnable = new Runnable() {
#Override
public void run() {
try {
camera.takePicture(); //The result will be in onPictureTaken
}
catch (Exception e) {
e.printStackTrace();
//Handle Exception!
}
finally{
//also call the same runnable to call it at regular interval
handler.postDelayed(this, 10*1000); //10*1000 is your interval (in this case 10 seconds)
}
}
};
//runnable must be execute once
handler.post(photoRunnable);
Remember to manage the lifecycle of handler
I'm working on a memory game for Android and I'm having a problem. When the user taps the second image - if the images are not the same I want the second image to show for 1, 2 seconds.
What I've tried is to sleep for 1-2 sec. the UI thread after the second image is activated - but this doesn't seem to work - the second image doesn't seem to show! (only the first images is showed)
Here's my code:
public void whenTapedImage(View v, int position)
{
i++;
ImageView imgV=(ImageView)v;
if(i%2!=0)
{
firstClick=position;
imgV.setImageResource(im.images.get(firstClick));
}
else
{
secondClick=position;
imgV.setImageResource(im.images.get(secondClick));
try {
Thread.currentThread().sleep(1000);
if(!(im.images.get(firstClick).equals(im.images.get(secondClick))))
{
Toast.makeText(easyGame.this, "Try Again!", Toast.LENGTH_SHORT).show();
im.notifyDataSetChanged();
gridview.setAdapter(im);
gridview.invalidate();
aux=player1Turn;
player1Turn=player2Turn;
player2Turn=aux;
}
else{
done=done+2;
ImageAdapter.keepVisibleViews.add(firstClick);
ImageAdapter.keepVisibleViews.add(secondClick);
if(player1Turn==true)
{
player1Score++;
String score=Integer.toString(player1Score);
score1.setText(score);
}
if(player2Turn==true)
{
player2Score++;
String score=Integer.toString(player2Score);
score2.setText(score);
}
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
What am I doing wrong?
You must not sleep the UI thread as this would prevent android from delivering any other events to your activity's UI.
Instead, do something such as use a timer and have the timer's method use the run on ui thread facility to make the desired postponed change.
For robustness you may need to implement a state machine (either formally, or in effect) to keep track of what is supposed to be happening - you'll need to decide if the current delay should be aborted or enforced if another button is pushed, and make the state machine treat that appropriately.
This is similar to Waiting in android app
Try following the solution posted there and use the Timer Class
I have looked into other camera flashlight related problems in stackoverflow, but couldn't find an answer that solves my issue. The flashlight flashes for 6-8 times and then the app crashes. Here I have a blink() method that calls cameraon() and cameraoff() in a loop. Could you please let me know where I went wrong?
My code can be found here: http://pastebin.com/3LRMwd1J
The logcat output can be see here: http://pastebin.com/2GTpn8Ux
I have tried using surface textures, tried to include Thread.sleep() in betweencameraon() and cameraoff() but so far no luck.
I use Android 4.4 with latest sdk and jdk versions.
I use nexus 5 for testing.
Thanks in advance!
just take Thread in Blink() method and put your code with if else
boolean tourchon=false;
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
for (int i = 0; i < 10; i++) {
if(tourchon){
cameraoff();
tourchon=false;
}else{
cameraon();
tourchon=true;
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
thread.start();
Try this code...
Separate the obtaining of camera object from your cameraOn() method, as i understand this method should be turning the flash on, calling open() that many times in that short time may be causing your problems as this documentation suggests.
Caution: On some devices, this method may take a long time to complete. It is best to call this method from a worker thread (possibly using AsyncTask) to avoid blocking the main application UI thread.
So have your on and off methods just change the flash mode, because you already have the camera instance.
See if my code helps any:
https://github.com/wolfhorse/SimpleFlashlight
It doesn't blink but it may be a good reference for you.
EDIT:
I modified the onClick() event of the toggle button in the SimpleFlashlight app referenced above by adding the code below and it blinks/flashes fine on my Samsung Galaxy S5 without error.
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
boolean flashOn = false;
for (int i = 0; i < 20; i++) {
flashOn = !flashOn;
toggleFlash(flashOn);
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
thread.start();
Wrapped in new Thread like others suggested here - better performance.
I want to write application which will be used camera flash. I want the light to blink. For now I have this code:
void ledon() {
cam = Camera.open();
Parameters params = cam.getParameters();
params.setFlashMode(Parameters.FLASH_MODE_ON);
cam.setParameters(params);
cam.startPreview();
cam.autoFocus(new AutoFocusCallback() {
public void onAutoFocus(boolean success, Camera camera) {
}
});
}
this part:
cam.autoFocus(new AutoFocusCallback() {
public void onAutoFocus(boolean success, Camera camera) {
}
});
I dont know why its necessary but it wont work without it.
This code turn on led and turn off automaticly for about 2 seconds. I want to led will turn on e.g. for 5 sec then 3 sec led off and then again on for 4 seconds. How I can manually set periods of time in which led will be on and off. Is this possible? Thanks for any help.
PS. I have samsung galaxy ace.
it seems like what you want to use is Parameters.FLASH_MODE_TORCH. You set it to TORCH when you want to turn on the flash LED, and then set it to AUTO when you want the torch light to turn off.
Also, check out this SO question Camera.Parameters.FLASH_MODE_TORCH replacement for Android 2.1 as it'll tell you about some issues people have run into with certain devices.
Then, for the timer, you can use an instance of Timer and a subclass of TimerTask to do the trick. Here's an example from code I've written to make sure an Internet query isn't taking too long:
private QueryLyricsTask clt;
private Timer t1;
...
/* Make sure the query doesn't take too long */
try {
t1 = new Timer("monitorTimeout");
t1.schedule(new qlt(), lyricsTimeout * 1000);
} catch (Exception e) {
e.printStackTrace();
}
...
class qlt extends java.util.TimerTask {
#Override
public void run() {
if (clt.getStatus() != Status.FINISHED)
clt.cancel(true);
}
}
To explain these variables, "monitorTimeout" is a tag name to reference the Timer by. "qlt" is the class that runs once Timer t1 has elapsed. "lyricsTimeout" is a numeric value specified by the user in the settings (for timeout in seconds). "clt" is an instance of a class derived from AsyncTask so it will run the query without freezing up my UI. Obviously in your case, you probably wouldn't need an AsyncTask, but perhaps a simple "if" statement in run() to toggle the flash torch based on its current state.
Check out the documentation on the Timer class so you can learn how to make Timers that fire once or multiple times, or at certain times of day.
having some trouble with a Thread (CanvasThread) that is intermittently pausing at random points within my application. Everything else in the app continues to function as necessary, it's simply this thread that randomly blocks out for some reason and doesn't draw anything new to the screen. I noticed that Surface.lockCanvasNative() seems to be the last function called before the block, and the first one returned after. In a pattern as such:
Surface.lockCanvasNative (Landroid/graphics/Rect)Landroid/graphics/Canvas; # 26,560 msec ____
Surface.lockCanvasNative (Landroid/graphics/Rect)Landroid/graphics/Canvas; # 40,471 msec ____|
Surface.lockCanvasNative (Landroid/graphics/Rect)Landroid/graphics/Canvas; # 40,629 msec ____
Surface.lockCanvasNative (Landroid/graphics/Rect)Landroid/graphics/Canvas; # 54,516 msec ____|
This is evident with the traceview below:
I have been using the CanvasThread.run() below if it helps:
#Override
public void run() {
boolean tellRendererSurfaceChanged = true;
/*
* This is our main activity thread's loop, we go until
* asked to quit.
*/
while (!mDone) {
/*
* Update the asynchronous state (window size)
*/
int w;
int h;
synchronized (this) {
// If the user has set a runnable to run in this thread,
// execute it and record the amount of time it takes to
// run.
if (mEvent != null) {
mEvent.run();
}
if(needToWait()) {
while (needToWait()) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
if (mDone) {
break;
}
tellRendererSurfaceChanged = mSizeChanged;
w = mWidth;
h = mHeight;
mSizeChanged = false;
}
if (tellRendererSurfaceChanged) {
mRenderer.sizeChanged(w, h);
tellRendererSurfaceChanged = false;
}
if ((w > 0) && (h > 0)) {
// Get ready to draw.
// We record both lockCanvas() and unlockCanvasAndPost()
// as part of "page flip" time because either may block
// until the previous frame is complete.
Canvas canvas = mSurfaceHolder.lockCanvas();
if (canvas != null) {
// Draw a frame!
mRenderer.drawFrame(canvas);
mSurfaceHolder.unlockCanvasAndPost(canvas);
//CanvasTestActivity._isAsyncGoTime = true;
}
else{
Log.v("CanvasSurfaceView.CanvasThread", "canvas == null");
}
}
}
}
Just let me know if I can provide any other useful information. I'm simply looking for clues as to why my thread might be blocking at this point? Thanks for any help in advance!
I've since narrowed the block down to mSurfaceHolder.unlockCanvasAndPost(canvas); I inserted a log before and after this call and the one after is not logged after app is frozen; but the log before is last logged event on this thread. It's not pausing or using a null canvas either, because I threw in logs for those instances as well; which are not logged even once until app is done.
I'm not sure if this could be the reason, but under SurfaceHolder.lockCanvas(), it warns that,
If you call this repeatedly when the
Surface is not ready (before
Callback.surfaceCreated or after
Callback.surfaceDestroyed), your calls
will be throttled to a slow rate in
order to avoid consuming CPU.
If null is not returned, this function
internally holds a lock until the
corresponding
unlockCanvasAndPost(Canvas) call,
preventing SurfaceView from creating,
destroying, or modifying the surface
while it is being drawn. This can be
more convenient than accessing the
Surface directly, as you do not need
to do special synchronization with a
drawing thread in
Callback.surfaceDestroyed.
I'm not sure what the threshold is when the CPU starts throttling. How many threads are refreshing the canvas?
btw,
if(needToWait()) {
while (needToWait()) {
is redundant
I have since figured out my problem. I'm not sure why but because I had accidentally forgot to fully comment out an earlier asyncTask(), thus had two doing roughly the same tasks and obviously struggling to do so with the same variables and such. Thanks for your pointers, but simply another careless mistake on my part I guess.