7

I'm trying to create custom camera using Camera API. I have already looked through a lot of similar questions, but anyway can't get rid from freezes in my Camera Preview. Sometimes preview freezes when activity started, despite of using another thread. But when I try to switch to face camera, preview image Freezes every time. In log i Got only something like this:

I/Choreographer: Skipped 41 frames!  The application may be doing too much work on its main thread.

My SurfaceView is placed in Fragment in ViewPager activity if it's matter.

My Custom Camera class methods:

Set Display Orientation:

void setCameraDisplayOrientation(int cameraId) {
        int rotation = getActivity().getWindowManager().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 = 0;

        Camera.CameraInfo info = new Camera.CameraInfo();
        Camera.getCameraInfo(cameraId, info);

        if (info.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
            result = ((360 - degrees) + info.orientation);
        } else

            if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
                result = ((360 - degrees) - info.orientation);
                result += 360;
            }
        result = result % 360;
        camera.setDisplayOrientation(result);
    }

Holder Callback class:

class HolderCallback implements SurfaceHolder.Callback {

        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            try {
                camera.setPreviewDisplay(holder);
                camera.startPreview();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }


        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width,
                                   int height) {
            // TODO Auto-generated method stub
            if (mIsPreviewing) {
                camera.stopPreview();
                mIsPreviewing = false;
            }

            if (camera != null) {
                Camera.Parameters parameters = camera.getParameters();
                Camera.Size bestSize = getBestPreviewSize(width, height, parameters);

                if (bestSize != null) {
                    parameters.setPreviewSize(bestSize.width, bestSize.height);
                    camera.setParameters(parameters);

                    Toast.makeText(
                            getActivity().getApplicationContext(),
                            "Оптимальный размер: " + String.valueOf(bestSize.width)
                                    + " : " + String.valueOf(bestSize.height),
                            Toast.LENGTH_LONG).show();
                }
                try {
                    camera.setPreviewDisplay(holder);
                    camera.startPreview();
                    mIsPreviewing = true;

//                    camera.autoFocus(autoFocusCallback);
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }

        private Camera.Size getBestPreviewSize(int width, int height,
                                               Camera.Parameters parameters) {
            Camera.Size bestSize = null;
            List<Camera.Size> sizeList = parameters.getSupportedPreviewSizes();

            bestSize = sizeList.get(0);

            for (int i = 1; i < sizeList.size(); i++) {
                if ((sizeList.get(i).width * sizeList.get(i).height) > (bestSize.width * bestSize.height)) {
                    bestSize = sizeList.get(i);
                }
            }
            return bestSize;
        }


        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {

        }

    }

Camera Handler Thread:

private CameraHandlerThread mThread = null;
    private static class CameraHandlerThread extends HandlerThread {
        Handler mHandler = null;

        CameraHandlerThread() {
            super("CameraHandlerThread");
            start();
            mHandler = new Handler(getLooper());
        }

        synchronized void notifyCameraOpened() {
            notify();
        }

        void openCamera() {
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    camera = Camera.open(CAMERA_ID);
                    //set camera to continually auto-focus
                    Camera.Parameters params = camera.getParameters();
//*EDIT*//params.setFocusMode("continuous-picture");
//It is better to use defined constraints as opposed to String, thanks to AbdelHady
//                    params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
//                    camera.setParameters(params);
                    //STEP #1: Get rotation degrees
                    Camera.CameraInfo info = new Camera.CameraInfo();
                    Camera.getCameraInfo(Camera.CameraInfo.CAMERA_FACING_BACK, info);

                    int degrees = 0;
                    switch (rotation) {
                        case Surface.ROTATION_0: degrees = 0; break; //Natural orientation
                        case Surface.ROTATION_90: degrees = 90; break; //Landscape left
                        case Surface.ROTATION_180: degrees = 180; break;//Upside down
                        case Surface.ROTATION_270: degrees = 270; break;//Landscape right
                    }
                    int rotate = (info.orientation - degrees + 360) % 360;

//STEP #2: Set the 'rotation' parameter
                    params.setRotation(rotate);
                    camera.setParameters(params);
                    notifyCameraOpened();
                    camera.startPreview();

                }
            });
            try {
                wait();
            }
            catch (InterruptedException e) {
                Log.d(TAG, "openCamera: Cannot open Camera");
            }
        }
    }

Opening camera:

private void newOpenCamera() {
    mThread = new CameraHandlerThread();
    synchronized (mThread) {
        mThread.openCamera();
    }
}

Fragment methods:

 @Override
    public void onResume() {
        super.onResume();
        newOpenCamera();
        setCameraDisplayOrientation(CAMERA_ID);
    }

    @Override
    public void onPause() {
        super.onPause();
        if (camera != null)
            camera.release();
        camera = null;
    }

@Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        sv = (SurfaceView) getActivity().findViewById(R.id.surfaceView);

        makePhotoBtn = (ImageView) getActivity().findViewById(R.id.makephotoBtn);
        switchCameraBtn = (ImageView) getActivity().findViewById(R.id.switchCameraBtn);
        holder = sv.getHolder();
        holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

        holderCallback = new HolderCallback();
        holder.addCallback(holderCallback);
        rotation = getActivity().getWindowManager().getDefaultDisplay().getRotation();


        makePhotoBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                camera.takePicture(null, null, new Camera.PictureCallback() {
                    @Override
                    public void onPictureTaken(byte[] data, Camera camera) {
                        try {
                            new SaveBitmap().execute(toObjects(data));
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                });
            }
        });


        switchCameraBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                swapCamera();
            }
        });
    }

And Swap Camera Method:

 private void swapCamera() {

        if (camera != null) {
            camera.stopPreview();
            camera.setPreviewCallback(null);
            camera.release();
            camera = null;
            holder.removeCallback(holderCallback);
            holder = null;
            sv = null;
            sv = (SurfaceView) getActivity().findViewById(R.id.surfaceView);
        }

        //swap the id of the camera to be used
        if (CAMERA_ID == Camera.CameraInfo.CAMERA_FACING_FRONT) {
            CAMERA_ID = 0;
        } else  {
            CAMERA_ID = 1;
        }

        newOpenCamera();


    }

What can I do to get rid of freezes in this case? Appreciate any help!

Big Coach
  • 2,485
  • 2
  • 12
  • 31
  • closing camera may be really slow; you can call this swap sequence on the cameraHandler thread. And there is no reason to start a new thread for front-facing camera; you will be better served if you reuse the same HandlerThread. – Alex Cohn Aug 09 '17 at 03:26
  • *"preview freezes when activity started"* - how can preview continue to work when activity is started/stopped? Are you speaking about switch from landscape to portrait orientation? – Alex Cohn Aug 09 '17 at 03:27
  • @AlexCohn Can you show how to move swap sequence on the cameraHandler thread properly? I moved the swap method to openCamera handler method, but freezes still there. – Big Coach Aug 09 '17 at 05:37
  • If you want to smoothly switch the camera from front to back, you must look for a device that [can open two cameras simultaneously](https://stackoverflow.com/questions/11419940/using-both-front-and-back-cameras-simultaneously-android). There are some optimizations you can achieve with other devices, but it still takes time. It should not cause choreographer warnings, though - as long as it all happens off the UI thread. – Alex Cohn Aug 09 '17 at 10:00
  • @AlexCohn I don't need to open two cameras simultaneously, just want to switch them. Waiting between switching is acceptable, but my preview hangs forever – Big Coach Aug 09 '17 at 11:08
  • What's that `wait()` about after you post `Camera.open()` to the Handler? – Alex Cohn Aug 09 '17 at 11:28
  • @AlexCohn without this block i get `NullpointerException` in `setCameraDisplayOrientation` method in line `camera.setDisplayOrientation(result);` – Big Coach Aug 09 '17 at 15:59
  • Oh, why don't you set this orientation in the same thread? Because you need Activity to read rotation? This is not a good excuse: you can store this rotation onResume and use it when the camera is ready – Alex Cohn Aug 09 '17 at 18:00
  • Maybe this article will help you. http://android-gangster.blogspot.com/2017/07/how-to-create-custom-camera-in-android.html – Tharindu Welagedara Aug 17 '17 at 10:44
  • What's your min-sdk? Camera was deprecated in 21 – Amir Uval Aug 17 '17 at 15:59
  • @auval yes, but almost all tutorials i found was on camera api, not camera2 – Big Coach Aug 17 '17 at 17:59

0 Answers0