Android camera: stopPreview vs releaseCamera

I am making an application where I have a camera inside a viewPager. I am wondering what is the best way to "pause" and "resume" the camera so that it does not load resources when it is preloaded. I have a feeling that stopPreview is better for this as it does not free the camera, but it does save it, however it does not display the camera, which is the main reason it triggers resources.

  • Application input and output: startCamera () and releaseCamera ()
  • Tab visible and invisible: startPreview () and stop Preview ()

Would this be a good rule of thumb?

+3


source to share


4 answers


This does most of the operations CameraPreview.java

:

package com.example.fela;

import android.app.Activity;
import android.content.Context;
import android.graphics.PixelFormat;
import android.hardware.Camera;
import android.hardware.Camera.ErrorCallback;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.Size;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.WindowManager;

import java.util.List;

public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {

    private SurfaceHolder holder;
    private Camera camera;
    private int cameraId;
    private Activity activity;
    private CameraPreviewActivityInterface activityInterface;

    public CameraPreview(Activity activity, int cameraId) {
        super(activity);
        try {
            activityInterface = (CameraPreviewActivityInterface) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString() + " must implement ExampleFragmentCallbackInterface ");
        }
        this.activity = activity;
        this.cameraId = cameraId;
        holder = getHolder();
        holder.addCallback(this);
        // deprecated setting, but required on Android versions prior to 3.0
        holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    public void surfaceCreated(SurfaceHolder holder) {}

    /**
     * custom camera tweaks and startPreview()
     */
    public void refreshCamera() {
        if (holder.getSurface() == null || camera == null) {
            // preview surface does not exist, camera not opened created yet
            return;
        }
        Log.i(null, "CameraPreview refreshCamera()");
        // stop preview before making changes
        try {
            camera.stopPreview();
        } catch (Exception e) {
            // ignore: tried to stop a non-existent preview
        }
        int rotation = ((WindowManager) activity.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getRotation();
        int degrees = 0;
        // specifically for back facing camera
        switch (rotation) {
            case Surface.ROTATION_0:
                degrees = 90;
                break;
            case Surface.ROTATION_90:
                degrees = 0;
                break;
            case Surface.ROTATION_180:
                degrees = 270;
                break;
            case Surface.ROTATION_270:
                degrees = 180;
                break;
        }
        camera.setDisplayOrientation(degrees);
        setCamera(camera);
        try {
            camera.setPreviewDisplay(holder);
            camera.startPreview();
        } catch (Exception e) {
            // this error is fixed in the camera Error Callback (Error 100)
            Log.d(VIEW_LOG_TAG, "Error starting camera preview: " + e.getMessage());
        }
    }



    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
        Log.i(null, "CameraPreview surfaceChanged()");
        // if your preview can change or rotate, take care of those events here.
        // make sure to stop the preview before resizing or reformatting it.
        // do not start the camera if the tab isn't visible
        if(activityInterface.getCurrentPage() == 1)
            startCamera();
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {}

    public Camera getCameraInstance() {
        Camera camera = Camera.open();
        // parameters for camera
        Parameters params = camera.getParameters();
        params.set("jpeg-quality", 100);
        params.set("iso", "auto");
        params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
        params.setPictureFormat(PixelFormat.JPEG);
        // set the image dimensions
        List<Size> sizes = params.getSupportedPictureSizes();
        int max = 0, width = 0, height = 0;
        for(Size size : sizes) {
            if(max < (size.width*size.height)) {
                max = (size.width*size.height);
                width = size.width;
                height = size.height;
            }
        }
        params.setPictureSize(width, height);
        camera.setParameters(params);
        // primarily used to fix Error 100
        camera.setErrorCallback(new ErrorCallback() {
            @Override
            public void onError(int error, Camera camera) {
                if(error == Camera.CAMERA_ERROR_SERVER_DIED) {
                    releaseCamera();
                    startCamera();
                }
            }
        });
        return camera;
    }

    /**
     * intitialize a new camera
     */
    protected void startCamera() {
        if(getCamera() == null)
            setCamera(getCameraInstance());
        refreshCamera();
    }

    /**
     * release camera so other applications can utilize the camera
     */
    protected void releaseCamera() {
        // if already null then the camera has already been released before
        if (getCamera() != null) {
            getCamera().release();
            setCamera(null);
        }
    }

    public Camera getCamera() {
        return camera;
    }

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

    public void setCameraId(int cameraId) {
        this.cameraId = cameraId;
    }

    /**
     * get the current viewPager page
     */
    public interface CameraPreviewActivityInterface {
        public int getCurrentPage();
    }
}

      

In my FragmentCamera.java

file:



private CameraPreview cameraPreview;

// code...

public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    // code...
    cameraPreview = new CameraPreview(getActivity(), cameraId);
    previewLayout.addView(cameraPreview);
    // code...
}

// code...

@Override
public void onPause() {
    super.onPause();
    cameraPreview.releaseCamera();
}

@Override
public void onResume() {
    super.onResume();
    cameraPreview.startCamera();
}

protected void fragmentVisible() {
    onResume();
}

protected void fragmentNotVisible() {
    onPause();
}

      

And the MainActivity.java file (implements CameraPreviewActivityInterface):

viewPager.setOnPageChangeListener(new OnPageChangeListener() {
    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
    }
    @Override
    public void onPageSelected(int position) {
        currentPage = position;
        if (currentPage == 1) {
            fragmentCamera.fragmentVisible();
        } else {
            fragmentCamera.fragmentNotVisible();
        }
    }
    @Override
    public void onPageScrollStateChanged(int state) {
    }
});

@Override
public int getCurrentPage() {
    return currentPage;
}

      

0


source


I had a similar situation.

If I kept the camera (in the ViewPager) enabled in a state, swipes were clunky and OOM exceptions were frequent.

Two options came to my mind:

shift the entire instance to another thread

OR



use stopPreview () and startPreview ()

I went with the second one:

However, instead of doing this on the fragment lifecycle callback, I gave a button on the fragment that toggled the preview. The reason is that if the user is very fast, you can still get an OOm exception as the preview calls will be queued, especially if there are very few fragments in the viewPager.

Basically, shoot the camera onPause (), acquire the camera in onResume (), and let's have a groovy button in a fragment that will switch your view to the surface!

+1


source


Hi Karl, I needed to implement the same things as in the pager. I have a round viewer in which one slice has a camera slice. I want to handle the camera preview in such a way that it doesn't consume the camera resource.

As you know, Android pager loads two chunks into memory by default. We implemented a view pager change listener and called a snippet method to start and stop the preview. even destroy the camera preview in fragment destroy mode.

class ViewPagerChangeListener implements ViewPager.OnPageChangeListener {

        int currentPosition = DEFAULT_FRAGMENT;

        @Override
        public void onPageScrollStateChanged(int state) {
            TimberLogger.d(TAG, "onPageScrollStateChanged");
        }

        @Override
        public void onPageScrolled(int index, float arg1, int arg2) {
            TimberLogger.d(TAG, "onPageScrolled" + index);
        }

        @Override
        public void onPageSelected(int position) {
            mWatchPosition = position;
            TimberLogger.d(TAG, "onPageSelected" + mWatchPosition);

            int newPosition = 0;
            if (position > 4) {
                newPosition = position;
            }

            TimberLogger.d(TAG, "newPosition" + newPosition);

            /**
             * Listener knows the new position and can call the interface method
             * on new Fragment with the help of PagerAdapter. We can here call
             * onResumeFragment() for new fragment and onPauseFragment() on the
             * current one.
             */

            // new fragment onResume
            loadedFragment(newPosition).onResumeFragment();

            // current fragment onPuase called
            loadedFragment(currentPosition).onPauseFragment();

            currentPosition = newPosition;

            TimberLogger.d(TAG, "currentPosition" + currentPosition);
        }
    }

      

Look at the two methods onResumeFragment and onPuaseFragment, these two are a custom function that each pager fragment implements. If the pager changes, we pause the current fragment and onResume the new fragment.

// new fragment onResume
 loadedFragment(newPosition).onResumeFragment();

 // current fragment onPuase called
 loadedFragment(currentPosition).onPauseFragment();

      

You can write the camera preview in a custom onResumeFragment method and stop the preview in onPauseFragment, and also make sure you have to override your camera fragment onDestory () method to release camera resources.

+1


source


The best solution would be startCamera () in onResume () and release it in onPause () so you can deal with the camera not being free in onResume (). In the ViewPager, you can startPreview () when a slice with it is selected and stopPreview () otherwise. Also u can startPreview () in onCreateView () and stopPreview () in onDestroyView () in fragment.

0


source







All Articles