onPreviewFrame() not being invoked

594 views Asked by At

I want to continuously receive frames from the camera and manipulate them. Since the manipulation will be a CPU heavy task, i have opened the camera on a separate handler thread so that any callbacks land on that thread as well. However, the problem that i am having is that my onPreviewFrame() never gets called, and i cannot seem to figure out why. I tried but i cannot figure out. There is no error either. I understand, in order for the PreviewCallbacks to occur, i need to do the following:

  • Open Camera
  • Set Preview Display (a camera preview)
  • Start the preview

I have done all of these and the preview even shows perfectly in the app UI. I wonder what part i am missing or doing wrong. Following is my relevant code.

ODFragment.java

public class ODFragment extends Fragment {

    static View rootView;
    static Context mainActivityContext;
    static final String TAG = "DBG_" + "ODFragment";

    static Camera mCamera;
    static CameraPreview mCameraPreview;
    static FrameLayout frameLayout_cameraLens;

    static CameraHandlerThread mCameraHandlerThread = null;
    static Handler mUiHandler = new Handler();

    //static TessBaseAPI tessBaseAPI = new TessBaseAPI();

    public ODFragment() {}

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        rootView = inflater.inflate(R.layout.fragment_od, container, false);
        mainActivityContext = this.getActivity();

        //one time tasks
        frameLayout_cameraLens = (FrameLayout) rootView.findViewById(R.id.frameLayout_cameraLens);
        setCameraViewDimensions();

        return rootView;
    }

    @Override
    public void onResume() {
        super.onResume();

        //1 open camera
        if (mCameraHandlerThread == null) {
            mCameraHandlerThread = new CameraHandlerThread("Camera Handler Thread");
        }
        if (!mCameraHandlerThread.isAlive() || mCameraHandlerThread.getCamera()==null)
        {
            synchronized (mCameraHandlerThread) {
                Log.d(TAG, "onResume: starting mCameraHandlerThread");
                mCameraHandlerThread.start();
                mCameraHandlerThread.openCamera();
            }
        }
        //rest of the steps will be invoked (hookOpenedCamera()) by cameraHandlerThread

    }

    public static void hookOpenedCamera(){
        mUiHandler.post(new Runnable() {
            @Override
            public void run() {
                //2 create camera and camera preview
                mCamera = mCameraHandlerThread.getCamera();
                mCameraPreview = new CameraPreview(getMainActivityContext(), mCamera);
                //3 add cameraPreview to layout
                frameLayout_cameraLens.addView(mCameraPreview);
                //4 start preview
                mCamera.startPreview();
                //5 hook previewCallback
                mCamera.setPreviewCallback(new Camera.PreviewCallback() {
                    @Override
                    public void onPreviewFrame(byte[] data, Camera camera) { //TODO: incomplete
                        Log.d(TAG, "onPreviewFrame: called");
                    }
                });

            }
        });

    }

    @Override
    public void onPause() {
        super.onPause();
        //-4
        mCamera.stopPreview();
        //-3
        frameLayout_cameraLens.removeView(mCameraPreview);
        //-2
        mCameraPreview = null;
        mCamera = null;
        //-1
        mCameraHandlerThread.setCamera(null);
        mCameraHandlerThread.quit();
        mCameraHandlerThread = null;
    }

    private void setCameraViewDimensions() {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                //calculate dimensions of cameraLens and height of ROI
                DisplayMetrics displaymetrics = new DisplayMetrics();
                getActivity().getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
                final int width = displaymetrics.widthPixels;
                final int height = (int) (width * 1.33333333334);
                final int ROIHeight = height / 5;

                getActivity().runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Log.d(TAG, "run: frameLayout_cameraLens dim (BEFORE): "+frameLayout_cameraLens.getLayoutParams().width+" x "+frameLayout_cameraLens.getLayoutParams().height);

                        //set dimensions of cameraLens
                        frameLayout_cameraLens.getLayoutParams().width = width;
                        frameLayout_cameraLens.getLayoutParams().height = height;
                        frameLayout_cameraLens.requestLayout();

                        //set height of ROI
                        LinearLayout linearLayout = (LinearLayout) rootView.findViewById(R.id.ROI);
                        linearLayout.getLayoutParams().height = ROIHeight;
                        linearLayout.requestLayout();

                        Log.d(TAG, "run: frameLayout_cameraLens dim (AFTER): " +frameLayout_cameraLens.getLayoutParams().width+" x "+frameLayout_cameraLens.getLayoutParams().height);
                    }
                });
            }
        });
        thread.run();
    }

    public static Context getMainActivityContext() {
        return mainActivityContext;
    }

    }

CameraHandlerThread.java

public class CameraHandlerThread extends HandlerThread {
static final String TAG = "DBG_" + "CameraHandlerThread";

Handler mCameraHandler = null;
Camera mCamera = null;


public CameraHandlerThread(String name) {
    super(name);
}

@Override
public synchronized void start() {
    super.start();

    //prepare handler
    if (mCameraHandler == null) {
        mCameraHandler = new Handler(getLooper());
    }
}

void openCamera() {
    mCameraHandler.post(new Runnable() {
        @Override
        public void run() {

            try {
                //done on cameraHandlerThread (step 1)
                mCamera = Camera.open();
                //done on UiThread (steps 2, 3, 4)
                ODFragment.hookOpenedCamera();
            }
            catch (RuntimeException e) {
                Log.e(TAG, "failed to open camera");
            }
        }
    });
}

public Camera getCamera() {
    return this.mCamera;
}

public void setCamera(Camera camera) {
    this.mCamera = camera;
}


}

CameraPreview.java

public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder mHolder;
private Camera mCamera;

static final String TAG = "DBG_" + "CameraPreview";
private Camera.Size mPreviewSize = null;

public CameraPreview(Context context){
    super(context);
}

public CameraPreview(Context context, Camera mCamera) {
    super(context);
    //Log.d(TAG, "CameraPreview() initialized");
    //mCamera = camera;

    // Install a SurfaceHolder.Callback so we get notified when the
    // underlying surface is created and destroyed.
    setCamera(mCamera);
    mHolder = getHolder();
    mHolder.addCallback(this);
}

@Override
public void surfaceCreated(SurfaceHolder holder) {
    // The Surface has been created, now tell the camera where to draw the preview
    try {
        mCamera.setDisplayOrientation(90); //because only supporting portrait currently
        mCamera.setPreviewDisplay(holder);
        //mCamera.startPreview();
        //Log.d(TAG, "surfaceCreated(): Started camera preview");
    } catch (IOException e) {
        Log.d(TAG, "surfaceCreated(): Error setting camera preview: " + e.getMessage());
    }
}

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
    // If your preview can change or rotate, take care of those events here.
    // Make sure to stop the preview before resizing or reformatting it.



    if (mHolder.getSurface() == null){
        // preview surface does not exist
        return;
    }

    // stop preview before making changes
    try {
        mCamera.stopPreview();
    } catch (Exception e){
        // ignore: tried to stop a non-existent preview
    }

    // set preview size and make any resize, rotate or
    // reformatting changes here

    Camera.Parameters cameraParameters = mCamera.getParameters();
    //change camera preview size
    cameraParameters.setPreviewSize(getPreviewSize().width, getPreviewSize().height);
    //continuous autofocus
    cameraParameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
    //set focus area to top part of the camera view //TODO
    //cameraParameters.setFocusAreas();

    //set parameters now
    mCamera.setParameters(cameraParameters);





    // start preview with new settings
    try {
        mCamera.setPreviewDisplay(mHolder);
        mCamera.startPreview();
        //Log.d(TAG, "surfaceChanged(): Restarted camera preview");

    } catch (Exception e){
        Log.d(TAG, "surfaceChanged(): Error starting camera preview: " + e.getMessage());
    }
}

private Camera.Size getPreviewSize() {
    if (mPreviewSize == null) {
        List<Camera.Size> mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
        mPreviewSize = mSupportedPreviewSizes.get(mSupportedPreviewSizes.size() - 6);
        Log.d(TAG, "getPreviewSize(): Camera Preview Size selected: " + mPreviewSize.width + " x " + mPreviewSize.height);
    }
    return mPreviewSize;
}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
    // empty. Take care of releasing the Camera preview in your activity.
    //Log.d(TAG, "surfaceDestroyed() invoked");
}

public void setCamera(Camera camera) {
    this.mCamera = camera;
}
}

Thank you in advance

1

There are 1 answers

0
Abdul Wasae On BEST ANSWER

SOLVED

I realized that my PreviewCallback does get hooked. But after that, the surfaceChanged() gets called at a point, which stops and then starts the CameraPreview. This removes my callback.

So, i have to invoke my callback hooking routine, everywhere that i am invoking startPreview