1

I have created a viewpager with a fragment page adapter and have two fragments in it. In one of my fragments I am displaying a camera live preview in a frame layout, but the problem is, that whenever I swipe from the first page to the camera preview page it is delayed. So I get black bars between the first fragment and the camera preview fragment when I’m swiping to the right, because the frame layout with the camera do not follow the swipe correctly. But this only happens when I’m displaying the camera live preview. If I were to remove the preview and just set the background color to green it wouldn’t be any delay. Camera Fragment:

public class CameraFragment extends Fragment {

    private Context mContext;
    private Camera mCamera;
    private CameraPreview mCameraPreview;
    private FrameLayout frameLayout;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.camera_layout, container, false);
        mContext = getActivity();
        frameLayout = (FrameLayout) v.findViewById(R.id.camera_preview);
        new Thread() {
            public void run() {
                Looper.prepare();
                mCamera = getCameraInstance();
                mCameraPreview = new CameraPreview(mContext, mCamera, frameLayout);
                try {
                    getActivity().runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            frameLayout.addView(mCameraPreview);
                        }
                    });
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }.start();
        return v;
    }

    private Camera getCameraInstance() {
        Camera camera = null;
        try {
            camera = getFrontFacingCamera();
        } catch (Exception e) {
            // cannot get camera or does not exist
        }
        return camera;
    }

    private Camera getFrontFacingCamera() {
        int cameraCount = 0;
        Camera cam = null;
        Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
        cameraCount = Camera.getNumberOfCameras();
        for (int camIdx = 0; camIdx < cameraCount; camIdx++) {
            Camera.getCameraInfo(camIdx, cameraInfo);
            if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
                try {
                    cam = Camera.open(camIdx);
                } catch (RuntimeException e) {
                    Log.e("CameraFragment", "Camera failed to open: " + e.getLocalizedMessage());
                }
            }
        }
        return cam;
    }

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

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

    private void releaseCamera() {
        if (mCamera != null) {
            mCamera.release();
            mCamera = null;
        }
    }
}

Camera preview class:

public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
    private SurfaceHolder mHolder;
    private Camera mCamera;
    private String TAG = CameraPreview.class.getSimpleName();
    private Context mContext;
    private FrameLayout frameLayout;
    private List<Camera.Size> mSupportedPreviewSizes;
    private Camera.Size mPreviewSize;
    boolean done = false;

    public CameraPreview(Context context, Camera camera, FrameLayout frameLayout) {
        super(context);
        mContext = context;
        mCamera = camera;
        mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
        for (Camera.Size str : mSupportedPreviewSizes)
            Log.e(TAG, str.width + "/" + str.height);
        this.frameLayout = frameLayout;
        mHolder = getHolder();
        mHolder.addCallback(this);
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    public void surfaceCreated(SurfaceHolder holder) {
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {

    }

    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
        if (mHolder.getSurface() == null) {
            return;
        }
        try {
            mCamera.stopPreview();
        } catch (Exception e) {
            // ignore: tried to stop a non-existent preview
        }
        try {
            Camera.Parameters parameters = mCamera.getParameters();
            parameters.setPreviewSize(mPreviewSize.height, mPreviewSize.width);
            mCamera.setParameters(parameters);
            mCamera.setDisplayOrientation(90);
            mCamera.setPreviewDisplay(mHolder);
            mCamera.startPreview();
        } catch (Exception e) {
            Log.d(TAG, "Error starting camera preview: " + e.getMessage());
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
        final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);

        if (mSupportedPreviewSizes != null) {
            mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height, true);
            if (mPreviewSize == null) {
                mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height, false);
            }
        }

        float ratio = (float) mPreviewSize.height / (float) mPreviewSize.width;
        if ((int) (width * ratio) >= height) {
            setMeasuredDimension(width, (int) (width * ratio));
        } else {
            setMeasuredDimension(width, height);
        }
        done = true;
    }

    private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h, boolean minSize) {
        if (sizes == null) {
            return null;
        }
        Camera.Size optimalSize = null;
        double minDiff = Double.MAX_VALUE;
        double frameRatio = (double) h / w;
        double testRatio;
        int width, height;
        for (Camera.Size size : sizes) {
            width = size.height;
            height = size.width;
            if (width > height) {
                int t_width = width;
                width = height;
                height = t_width;
            }
            if (height < h / 2 && minSize) continue;
            testRatio = (double) height / width;
            if (Math.abs(frameRatio - testRatio) < minDiff) {
                if (optimalSize == null) optimalSize = sizes.get(0);
                // else if (frameRatio - testRatio <= 0 && optimalSize.height > height) continue;
                optimalSize.width = width;
                optimalSize.height = height;
                minDiff = Math.abs(frameRatio - testRatio);
                Log.d("", "" + frameRatio + ", " + testRatio);
            } else if (Math.abs(frameRatio - testRatio) == minDiff) {
                if (optimalSize.height > height) continue;
                if (optimalSize == null) optimalSize = sizes.get(0);
                optimalSize.width = width;
                optimalSize.height = height;
                minDiff = Math.abs(frameRatio - testRatio);
                Log.d("", "" + frameRatio + ", " + testRatio);
            }
        }
        Log.d("", "" + optimalSize.width + ", " + optimalSize.height);
        return optimalSize;
    }
}

It's kinda hard to explain but I hope you understand what is happening. Thanks!

Nothing
  • 11
  • 3
  • So what's the question? How to eliminate delay? – eleven Sep 26 '15 at 21:55
  • yes that is the question – Nothing Sep 26 '15 at 21:58
  • So I suppose there is no way to do it. `SurfaceView` with camera video stream is relatively heavy object. A device need time to create, configure and show it. – eleven Sep 26 '15 at 22:00
  • But I've seen several apps that is doing this, for instance Snapchat. How do they do it then? – Nothing Sep 26 '15 at 22:03
  • It seems that I understood your problem. `ViewPager` by default destroys and creates pages when they are out of screen. There's method `setOffscreenPageLimit` of `ViewPager` which accepts amount of "not destroyable" pages. Pass amount of total pages and your `SurfaceView` will not be destroyed. – eleven Sep 26 '15 at 22:11
  • It is still the same :/ – Nothing Sep 26 '15 at 22:24
  • I've been searching around and trying new solutions fow 2 weeks now but still can't get it to work. Extremely annoying :X – Nothing Sep 26 '15 at 22:27
  • https://github.com/commonsguy/cwac-camera have you given it a try? – subhash Sep 27 '15 at 02:57
  • There seems to be a typo in your code: the method name is `getFrontFacingCamera`, but it opens `CAMERA_FACING_BACK` – Alex Cohn May 16 '17 at 07:33
  • Possibly duplicate of *[How to handle android camera opening without a black screen](http://stackoverflow.com/questions/43993926/how-to-handle-android-camera-opening-without-a-black-screen)* – Alex Cohn May 16 '17 at 07:35

0 Answers0