Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why continuous auto focusing camera with handler dont allow to toggle camera flash?

What I have done so far:

I have implemented custom camera for reading qr code which need to continue focus the camera for better qr reading.

My problem is when I use to focus in every one second with the handler the camera flash on\off button dont works or it takes too much time to turning on and off the camera flash light. Every thing works fine when I remove the code of auto focusing the camera every second (The runnable and the handler).

What I want is to focus automatically and quickly whenever camera moves and also able to turn on and off the flash on demand quickly without using Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE because its not available for API<14.

I have used Camera.Parameters.FOCUS_MODE_AUTO but its only focusing the camera once when started thats why i used handler to focus camera every second.

Min SDK Version of project is 9.

My Camera Activity is

public class CameraActivityNew extends Activity implements OnClickListener,
        Camera.PreviewCallback {

    CameraPreviewNew mPreview;
    FrameLayout flCameraPreview;
    ImageButton ibFlashButton;
    Boolean isFlashOn = false;

    Camera mCamera;

    private Handler mAutoFocusHandler;
    private boolean mPreviewing = true;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
        mAutoFocusHandler = new Handler();
        setContentView(R.layout.activity_camera);
        findSetupViews();
        mPreview = new CameraPreviewNew(getApplicationContext(), this,
                autoFocusCB);
        flCameraPreview.addView(mPreview);
    }

    private Runnable doAutoFocus = new Runnable() {
        public void run() {
            if (mCamera != null && mPreviewing) {
                mCamera.autoFocus(autoFocusCB);
            }
        }
    };
    Camera.AutoFocusCallback autoFocusCB = new Camera.AutoFocusCallback() {
        public void onAutoFocus(boolean success, Camera camera) {
            mAutoFocusHandler.postDelayed(doAutoFocus, 1000);
        }
    };

    @Override
    protected void onResume() {

        super.onResume();
        try {
            mCamera = Camera.open();
            if (mCamera == null) {
                return;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return;
        }

        mPreview.setCamera(mCamera);
        mPreview.showSurfaceView();
        mPreviewing = true;

    }

    @Override
    protected void onPause() {
        super.onPause();
        if (mCamera != null) {
            mPreview.setCamera(null);
            mCamera.cancelAutoFocus();
            mCamera.setPreviewCallback(null);
            mCamera.stopPreview();
            mCamera.release();
            mPreview.hideSurfaceView();
            mPreviewing = false;
            mCamera = null;
        }
    }

    private void findSetupViews() {

        flCameraPreview = (FrameLayout) findViewById(R.id.flCameraPreview);
        ibFlashButton = (ImageButton) findViewById(R.id.ibFlash);
        ibFlashButton.setOnClickListener(this);

        if (getPackageManager().hasSystemFeature(
                PackageManager.FEATURE_CAMERA_FLASH)) {
            ibFlashButton.setVisibility(View.VISIBLE);
            ibFlashButton.setOnClickListener(this);
        } else {
            ibFlashButton.setVisibility(View.GONE);
        }
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
        case R.id.ibFlash:
            if (isFlashOn) {
                mPreview.setCameraFlashLight(false);
                isFlashOn = false;
                ibFlashButton.setImageResource(R.drawable.flashoff);
            } else {
                mPreview.setCameraFlashLight(true);
                ibFlashButton.setImageResource(R.drawable.flashon);
                isFlashOn = true;
            }
            break;
        }
    }

    @Override
    public void onPreviewFrame(final byte[] data, final Camera camera) {
        // processed here qr code and works fine if camera focus
        //now removed to narrow the code for posting the question
    }

}

And the Camera Preview class is:

public class CameraPreviewNew extends ViewGroup implements Callback {

    public static final int CAMERA_BACK = 0;
    public static final int CAMERA_FRONT = 1;
    public Camera mCamera = null;
    private Context context = null;

    SurfaceView mSurfaceView;
    SurfaceHolder mSurfaceHolder;
    Size mPreviewSize;
    List<Size> mSupportedPreviewSizes;
    PreviewCallback mPreviewCallback;
    AutoFocusCallback mAutoFocusCallback;

    public CameraPreviewNew(Context context,

    PreviewCallback previewCallback, AutoFocusCallback autoFocusCb) {
        super(context);
        mPreviewCallback = previewCallback;
        mAutoFocusCallback = autoFocusCb;
        this.context = context;
        mSurfaceView = new SurfaceView(context);
        addView(mSurfaceView);
        mSurfaceHolder = mSurfaceView.getHolder();
        mSurfaceHolder.addCallback(this);

        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
            mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        }

    }

    public void setCamera(Camera camera) {
        mCamera = camera;
        if (mCamera != null) {
            mSupportedPreviewSizes = mCamera.getParameters()
                    .getSupportedPreviewSizes();
            requestLayout();
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        final int width = resolveSize(getSuggestedMinimumWidth(),
                widthMeasureSpec);
        final int height = resolveSize(getSuggestedMinimumHeight(),
                heightMeasureSpec);
        setMeasuredDimension(width, height);

    }

    public void hideSurfaceView() {
        mSurfaceView.setVisibility(View.INVISIBLE);
    }

    public void showSurfaceView() {
        mSurfaceView.setVisibility(View.VISIBLE);
    }

    public void surfaceCreated(SurfaceHolder holder) {

        try {
            if (mCamera != null) {
                mCamera.setPreviewDisplay(holder);
            }
        } catch (IOException exception) {
            Log.e("logtag", "IOException caused by setPreviewDisplay()",
                    exception);
        }
    }

    public void surfaceDestroyed(SurfaceHolder holder) {

        if (mCamera != null) {
            mCamera.cancelAutoFocus();
            mCamera.stopPreview();
        }
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
        if (holder.getSurface() == null) {

            return;
        }

        if (mCamera != null) {

            Camera.Parameters parameters = mCamera.getParameters();
            mPreviewSize = getBestPreviewSize(mCamera.getParameters(), w, h);
            parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
            requestLayout();

            mCamera.setParameters(parameters);
            mCamera.setPreviewCallback(mPreviewCallback);
            mCamera.startPreview();
            mCamera.autoFocus(mAutoFocusCallback);
            setCameraDisplayOrientation(0);
        }
    }

    private void setCameraDisplayOrientation(int cameraId) {
        Camera.CameraInfo info = new Camera.CameraInfo();
        Camera.getCameraInfo(cameraId, info);
        int rotation = ((WindowManager) context
                .getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay()
                .getRotation();
        int degrees = 0;
        switch (rotation) {
        case Surface.ROTATION_0:
            degrees = 0;
            break;
        case Surface.ROTATION_90:
            degrees = 90;
            break;
        case Surface.ROTATION_180:
            degrees = 180;
            break;
        case Surface.ROTATION_270:
            degrees = 270;
            break;
        }

        int result;
        if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
            result = (info.orientation + degrees) % 360;
            result = (360 - result) % 360; // compensate the mirror
        } else { // back-facing
            result = (info.orientation - degrees + 360) % 360;
        }
        mCamera.setDisplayOrientation(result);
    }

    protected static Comparator<Size> newSizeComparator() {
        return new Comparator<Size>() {

            @Override
            public int compare(Size lhs, Size rhs) {
                return Integer.valueOf(rhs.height * rhs.width).compareTo(
                        lhs.height * lhs.width);
            }
        };
    }

    private Size getBestPreviewSize(Parameters parameters, int screenWidth,
            int screenHeight) {
        List<Size> supportedSizes = parameters.getSupportedPreviewSizes();

        Collections.sort(supportedSizes, newSizeComparator());

        int previewHeight = screenHeight;
        int previewWidth = screenWidth;

        if (previewHeight > previewWidth) {
            int swap = previewWidth;
            previewWidth = previewHeight;
            previewHeight = swap;
        }

        Size bestSize = null;
        float bestRatio = 999;
        for (Size s : supportedSizes) {

            if (s.height > s.width) {
                int swap = s.width;
                s.width = s.height;
                s.height = swap;
            }

            float cameraRatio = ((float) s.height / (float) s.width);
            float screenRatio = ((float) previewHeight)
                    / ((float) previewWidth);

            if ((s.height >= previewHeight) && (s.width >= previewWidth)) {
                float ratioDiff = cameraRatio - screenRatio;
                if ((ratioDiff < 0.19) && (ratioDiff > -0.19)
                        && (Math.abs(bestRatio) > Math.abs(ratioDiff))) {
                    bestSize = s;
                    bestRatio = ratioDiff;
                }
            }
        }
        return bestSize;
    }

    public void setCameraFlashLight(Boolean setFlash) {

        Parameters _parameters = mCamera.getParameters();

        if (setFlash) {
            _parameters.setFlashMode(Parameters.FLASH_MODE_TORCH);
        } else {
            _parameters.setFlashMode(Parameters.FLASH_MODE_OFF);
        }
        mCamera.setParameters(_parameters);
        mCamera.startPreview();
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        if (changed && getChildCount() > 0) {
            final View child = getChildAt(0);

            final int width = r - l;
            final int height = b - t;

            int previewWidth = width;
            int previewHeight = height;
            if (mPreviewSize != null) {
                previewWidth = mPreviewSize.width;
                previewHeight = mPreviewSize.height;
            }

            if (width * previewHeight > height * previewWidth) {
                final int scaledChildWidth = previewWidth * height
                        / previewHeight;
                child.layout((width - scaledChildWidth) / 2, 0,
                        (width + scaledChildWidth) / 2, height);
            } else {
                final int scaledChildHeight = previewHeight * width
                        / previewWidth;
                child.layout(0, (height - scaledChildHeight) / 2, width,
                        (height + scaledChildHeight) / 2);
            }
        }

    }

}
like image 787
Gopal Singh Sirvi Avatar asked Jul 31 '15 08:07

Gopal Singh Sirvi


2 Answers

I see some issue with your AutoFocus handling code.
Analysis Result

There is cycle in your autofocus.

Explanation

a) Camera Preview Class mAutoFocusCallback is set with the autoFocusCb of the Camera Activity.

    public CameraPreviewNew(Context context,...,AutoFocusCallback autoFocusCb) 
    {
        super(context);
        mAutoFocusCallback = autoFocusCb;
        ...
    }

b)surfaceChanged is called once, at the time of loading the activity. The camera is requested to Auto Focus.

    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) 
    {
        if (mCamera != null) 
        {
            ...
            mCamera.startPreview();
            /*Auto focus camera and call <code>mAutoFocusCallback</code> after autofocus.*/
            mCamera.autoFocus(mAutoFocusCallback); 
            ...
       }
    }

c) On completion of the autofocus the mAutoFocusCallback callback is called. mAutoFocusCallback->autoFocusCb->onAutoFocus()
Camera Activity

    Camera.AutoFocusCallback autoFocusCB = new Camera.AutoFocusCallback()         {
        public void onAutoFocus(boolean success, Camera camera) {
            mAutoFocusHandler.postDelayed(doAutoFocus, 1000);
        }
};

d)onAutoFocus schedules one more autoFocus after 1000 millisecons, 1 sec.
Camera Activity

    public void onAutoFocus(boolean success, Camera camera) {
        mAutoFocusHandler.postDelayed(doAutoFocus, 1000);
    }  

e)After one second the messages is passed to handler that calles the runnable doAutoFocus requesting camera to auto focus, similar to b) above.

    private Runnable doAutoFocus = new Runnable() {
        public void run() {
            if (mCamera != null && mPreviewing) {
                mCamera.autoFocus(autoFocusCB);
            }
        }
    };

f) After completion of the autoFocus, the autoFocusCB is called again, similar to c) above. and cycle continues.

    Camera.AutoFocusCallback autoFocusCB = new Camera.AutoFocusCallback() {
        public void onAutoFocus(boolean success, Camera camera) {
            mAutoFocusHandler.postDelayed(doAutoFocus, 1000);
        }
    };

Solution

I am confused why such implementation. The cycle may be reason behind not listening to the flash enable/disable calls. You need to remove the code below and do something meaningful else leave the onAutoFocus() empty.

Camera.AutoFocusCallback autoFocusCB = new Camera.AutoFocusCallback() {
    public void onAutoFocus(boolean success, Camera camera) {
    /*REMOVE LINE BELOW*/
        mAutoFocusHandler.postDelayed(doAutoFocus, 1000);
    }
};

For auto focusing every time the camera moves you need to take help of the motion sensors provided with the phone. You can google it

Hope that helps.
Happy Coding...

like image 99
Devendra Vaja Avatar answered Nov 14 '22 11:11

Devendra Vaja


It seems that you dont need to use AutoFocusCallBack for your app, because you did nothing else than delay 1 second.

What you can do to focus all the time is using FOCUS_MODE_CONTINUOUS_PICTURE(read more here) like that(setFocus method is in CameraPreview, not in Activity):

public void setFocus() {
    Camera.Parameters p = mCamera.getParameters();
    p.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
    mCamera.setParameters(p);
    mCamera.startPreview();
}

And call it in SurfaceChanged:

public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
    // Now that the size is known, set up the camera parameters and begin
    // the preview.
    Camera.Parameters parameters = mCamera.getParameters();
    List<Camera.Size> previewSizes = parameters.getSupportedPreviewSizes();

    // You need to choose the most appropriate previewSize for your app
    Camera.Size previewSize = previewSizes.get(0);

    parameters.setPreviewSize(previewSize.width, previewSize.height);
    parameters.setRotation(90);
    mCamera.setParameters(parameters);
    mCamera.startPreview();

    setFlash(true);
    setZoomLevel(5);
    setFocus();

    Log.w(TAG, "surfaceChanged()");
}

For flash you can use this method from CameraPreview:

public void setFlash(boolean isFlashOn) {
    Camera.Parameters p = mCamera.getParameters();
    if (isFlashOn) {
        p.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
        mCamera.setParameters(p);
        mCamera.startPreview();
    } else {
        p.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
        mCamera.setParameters(p);
        mCamera.startPreview();
    }
    Log.w(TAG, "setFlash()");
}

Hope it helps you! If you have any questions about my answer feel free to comment.

like image 4
Chris Avatar answered Nov 14 '22 11:11

Chris