7

I've found several questions on this but no answers so here's hoping someone might have some insight. When I try to swap the camera I call the swapCamera function below. However the camera preview just freezes (the app is not frozen though just the live camera preview).

When I open the app for the first time everything works just fine. However I noticed something interesting. When I log out the memoryaddress of the _surfaceHolder object (i.e. my SurfaceHolder object) it gives me one value, but whenever I query that value after the app has finished launching and everything, that memory address has changed.

Further still, the error it gives me when I swapCamera is very confusing. I logged out _surfaceHolder before I passed it to the camera in _camera.setPreviewDisplay(_surfaceHolder); and it is NOT null before it's passed in.

Any help is greatly appreciated.

I've noticed some interesting behaviour

public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback
{
    private SurfaceHolder _surfaceHolder;
    private Camera _camera;
    boolean _isBackFacing;

    public CameraPreview(Context context, Camera camera) {
        super(context);
        _camera = camera;
        _isBackFacing = true;

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

    void refreshCamera()
    {
        try {
            _camera.setPreviewDisplay(_surfaceHolder);
            _camera.startPreview();
        } catch (IOException e) {
            Log.d("iCamera", "Error setting camera preview: " + e.getMessage());
        }
    }

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

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

    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 (_surfaceHolder.getSurface() == null){
            // preview surface does not exist
            return;
        }

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

        // set preview size and make any resize, rotate or
        // reformatting changes her
        _camera.setDisplayOrientation(90);

        // _startPoint preview with new settings
        refreshCamera();
    }

    public void swapCamera()
    {
        Camera cam = null;
        int cameraCount = Camera.getNumberOfCameras();
        Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
        _camera.stopPreview();
        _camera.release();
        for (int i = 0; i < cameraCount; i++)
        {
            Camera.getCameraInfo(i,cameraInfo);
            if(cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT && _isBackFacing == true)
            {
                try
                {
                    _camera = Camera.open(i);

                }catch (RuntimeException e)
                {
                    Log.e("Error","Camera failed to open: " + e.getLocalizedMessage());
                }
            }

            if(cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK && _isBackFacing == false)
            {
                try
                {
                    _camera = Camera.open(i);
                }catch (RuntimeException e)
                {
                    Log.e("Error","Camera failed to open: " + e.getLocalizedMessage());
                }
            }
        }

        _isBackFacing = !_isBackFacing;
        refreshCamera();
    }
}
Jared Burrows
  • 54,294
  • 25
  • 151
  • 185
Aggressor
  • 13,323
  • 24
  • 103
  • 182
  • Do you have a logcat you can include? – Dan Harms May 19 '15 at 21:43
  • The only logcat of value is this line: `D/Camera﹕ app passed NULL surface` – Aggressor May 19 '15 at 21:48
  • Are you on the main UI thread when `swapCamera` is called? – Dan Harms May 19 '15 at 21:52
  • Yes its a main thread call. The only async call is the the surfaceHolder callback the first time its created – Aggressor May 19 '15 at 21:54
  • One last question and then I'm out of ideas: do you have `` in your manifest? – Dan Harms May 19 '15 at 21:57
  • Nope, just access to the whole camera and it is required. – Aggressor May 19 '15 at 22:01
  • Also I can start the app with the front facing camera I just cant swap the camera after its been initialized and the preview is displaying, as I get the above error – Aggressor May 19 '15 at 22:02
  • Just to be sure, you aren't catching an exception in your `swapCamera` right? – Dan Harms May 19 '15 at 22:11
  • Correct no exception is caught, I verified that a dozen times – Aggressor May 19 '15 at 22:12
  • And you verified you are always hitting a `_camera = Camera.open(i);` correct? – Dan Harms May 19 '15 at 22:14
  • I verified everything along those lines. Everything is a non null object. The only thing that changes, and I dont know why, is the memory address of the surfaceHolder at the time I call swapCamera. For some reason the address of the surfaceHolder has changed (and I sure as hell didn't change it). And it IS a valid surface but when I pass it in, it spits out the null surface passed error. – Aggressor May 19 '15 at 22:17
  • `_camera` wouldn't be null in this case, but it may have been released and not reopened. That's what I was hoping to verify. – Dan Harms May 19 '15 at 22:18
  • Yea this is a very obscure bug, and Im sure one of the reasons they want people to use Camera2. But how many apps are ONLY on 21+. Im going to re-write my entire camera class and practically restart the entire activity when they swap a camera. – Aggressor May 19 '15 at 22:21
  • One last stab: try adding `_camera.setPreviewDisplay(null);` before you release the camera in `swapCamera`. It's possible that the other camera is still holding onto that surface even though you've released it. – Dan Harms May 19 '15 at 22:29
  • _camera is a static so I don't see how that's possible. But will give a try – Aggressor May 19 '15 at 22:31
  • Do not edit the question with "solved". Either delete this or add your own solution as an answer. – Jared Burrows May 20 '15 at 18:07
  • 1
    Theres 5 questions like this and none of them have an answer. I just found a solution, adding solved might help other people find this and be more useful I figured – Aggressor May 20 '15 at 18:12

1 Answers1

3

So after much debugging and digging what I found to be the culprit was the onResume function.

In it, I was 'refreshing' the camera variable in case it got lost between context switching.

public void onResume()
{
    super.onResume();
    _cameraPreview = new CameraPreview(getActivity());
}

This was causing my surfaceHolder to be created anew. I'm not exactly sure why it would cause a null, but I think because I created a new instance of a SurfaceHolder, the internal Android code was keeping a reference to the old (now null) SurfaceHolder. By removing my 'refresh' (i.e. reinstantiating) call from onResume the problem was fixed.

The error is misleading I think because its saying a null surface was passed but thats because I think its keeping a reference to a null surfaceHolder even if you created a new one and passed that in (it seems to use the OLD now null one anyways). So if you get this error, check that you aren't re-creating the surfaceHolder and passing it in.

Aggressor
  • 13,323
  • 24
  • 103
  • 182