Search code examples
androidcameraandroid-4.4-kitkat

Android 4.4 - Camera.PreviewCallback is never called


I went through http://developer.android.com/guide/topics/media/camera.html#custom-camera and I tried to add a Camera.PreviewCallback but it isn't being called. what methods / classes are dependent on the PreviewCallback firing?

public class MainActivity extends Activity {

    private Camera mCamera;
    private CameraPreview mPreview;
    private final static String TAG = "Main-Activity";

    private Camera.PreviewCallback countFrameCallback = new Camera.PreviewCallback() {
        @Override
        public void onPreviewFrame(byte[] bytes, Camera camera) {
            Log.d(TAG, "In frame callback");
        }
    };

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // Create an instance of Camera
        mCamera = getCameraInstance();

        // Create our Preview view and set it as the content of our activity.
        mPreview = new CameraPreview(this, mCamera);
        FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
        preview.addView(mPreview);
        mCamera.setPreviewCallback(countFrameCallback);

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }


    /** Check if this device has a camera */
    private boolean checkCameraHardware(Context context) {
        if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)){
            // this device has a camera
            return true;
        } else {
            // no camera on this device
            return false;
        }
    }

    /** A safe way to get an instance of the Camera object. */
    public static Camera getCameraInstance() {
        Camera c = null;
        try {
            c = Camera.open(); // attempt to get a Camera instance
        } catch (Exception e) {
            // Camera is not available (in use or does not exist)
        }
        return c; // returns null if camera is unavailable
    }
}

And CameraPreview:

public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {

    private SurfaceHolder mHolder;
    private Camera mCamera;
    private final static String TAG = "Camera-Preview";


    public CameraPreview(Context context, Camera camera) {
        super(context);
        mCamera = camera;

        // Install a SurfaceHolder.Callback so we get notified when the
        // underlying surface is created and destroyed.
        mHolder = getHolder();
        mHolder.addCallback(this);
        // deprecated setting, but required on Android versions prior to 3.0
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    public void surfaceCreated(SurfaceHolder holder) {
        // The Surface has been created, now tell the camera where to draw the preview.
        try {
            mCamera.setPreviewDisplay(holder);
            mCamera.startPreview();

        } catch (IOException e) {
            Log.d(TAG, "Error setting camera preview: " + e.getMessage());
        }
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
        // empty. Take care of releasing the Camera preview in your activity.
    }

    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

        // start preview with new settings
        try {
            mCamera.setPreviewDisplay(mHolder);
            mCamera.startPreview();

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

Solution

  • The camera callback is a little special. From the Camera Developer reference:

    Most long-running operations (preview, focus, photo capture, etc) happen asynchronously and invoke callbacks as necessary. Callbacks will be invoked on the event thread open(int) was called from.

    To get the callback, and because you get the camera from your main activity, simply implement PreviewCallback and override its method like this:

    public class MainActivity extends Activity implements PreviewCallback
    

    Then when you initiate the CameraPreview Class, pass as an argument the callback from the MainActivity like this:

    MainActivity

    mPreview = new CameraPreview(this, mCamera, this); //This stand for the PreviewCallback
    

    And remove the mCamera.setPreviewCallback(countFrameCallback); from MainActivity

    CameraPreview

    private Camera.PreviewCallback mCallback;
    ...
    //Constructor
    public CameraPreview(Context context, Camera camera, Camera.PreviewCallback callback) {
        super(context);
        mCallback = callback;
        ...
    }
    

    And in the SurfaceChanged Method:

    try {
        mCamera.setPreviewCallback(mCallback);
        mCamera.setPreviewDisplay(mHolder);
        mCamera.startPreview();  
    } catch (Exception e){
        Log.d(TAG, "Error starting camera preview: " + e.getMessage());
    }