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 awhile(true)
loop inrun()
, i have called run() in the end of themAutoFocusCallback
- 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