2

I implemented a custom SurfaceView to Draw Camera Preview and did all the operations of Capturing, and Manual Focusing on it. It works pretty well on Pre-Lolipop Devices but the problem is that it Crashes on Lollipop Devices. The Most Strange thing that happens is, The Application throws an ANR and when I click Ok, the Application starts functioning from the point I had left it.
The LogCat shows "Failed to Connect to Camera Error While After skipping ANR, I can easily connect and even Manipulate my Camera". Is it because I have used the deprecated Camera API And not the Camera2 Along with Lollipop Devices??

My code goes like this:

public class CameraPreview
implements
SurfaceHolder.Callback {
private int cameratype=Camera.CameraInfo.CAMERA_FACING_BACK;
private Camera mCamera = null;
public Camera.Parameters params;
private SurfaceHolder sHolder;
private String TAG="CameraPreview";
public List<Camera.Size> supportedSizes;

public int isCamOpen = 0;
public boolean isSizeSupported = false;
private int previewWidth, previewHeight;
private List<String> mSupportedFlashModes;
private boolean flashon=false;
private final static String MYTAG = "CameraPreview";
private ProgressDialog loading;

public CameraPreview(int width, int height) {
    Log.i("campreview", "Width = " + String.valueOf(width));
    Log.i("campreview", "Height = " + String.valueOf(height));
    previewWidth = width;
    previewHeight = height;
}

private int openCamera() {
    if (isCamOpen == 1) {
        releaseCamera();
    }

    mCamera = Camera.open(cameratype);

    if (mCamera == null) {
        return -1;
    }

    if (TouchActivity.reference.getResources().getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE) {
        mCamera.setDisplayOrientation(90);
    } else {
        mCamera.setDisplayOrientation(0);
    }

    params = mCamera.getParameters();
    params.setPreviewSize(previewWidth, previewHeight);

    try {
        mCamera.setParameters(params);
    } catch (RuntimeException e) {
        e.printStackTrace();
    }

    mCamera.startPreview();
    try {
        mCamera.setPreviewDisplay(sHolder);
    } catch (IOException e) {
        mCamera.release();
        mCamera = null;
        return -1;
    }
    isCamOpen = 1;
    return isCamOpen;
}
public int isCamOpen() {
    return isCamOpen;
}

public void releaseCamera() {
    if (mCamera != null) {
        mCamera.stopPreview();
        mCamera.setPreviewCallback(null);
        mCamera.release();
        mCamera = null;
    }
    isCamOpen = 0;
}

@Override
public void surfaceCreated(SurfaceHolder holder) {
    sHolder = holder;
    isCamOpen = openCamera();
}

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
        int height) {

}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {

    releaseCamera();

}

/**
 * Called from PreviewSurfaceView to set touch focus.
 * 
 * @param - Rect - new area for auto focus
 */
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
public void doTouchFocus(final Rect tfocusRect) {
    Log.i(TAG, "TouchFocus");
    try {
        final List<Camera.Area> focusList = new ArrayList<Camera.Area>();
        Camera.Area focusArea = new Camera.Area(tfocusRect, 1000);
        focusList.add(focusArea);

        Camera.Parameters para = mCamera.getParameters();
        para.setFocusAreas(focusList);
        para.setMeteringAreas(focusList);
        mCamera.setParameters(para);

        mCamera.autoFocus(myAutoFocusCallback);
    } catch (Exception e) {
        e.printStackTrace();
        Log.i(TAG, "Unable to autofocus");
    }

}

/**
 * AutoFocus callback
 */
AutoFocusCallback myAutoFocusCallback = new AutoFocusCallback(){

      @Override
      public void onAutoFocus(boolean arg0, Camera arg1) {
       if (arg0){
        mCamera.cancelAutoFocus();      
       }
    }
};




public void capturePicture(){
    mCamera.takePicture(null, null, mPicture);


}

private File getOutputMediaFile(){

    File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
            Environment.DIRECTORY_PICTURES), "UltimateCameraGuideApp");

    if (! mediaStorageDir.exists()){
        if (! mediaStorageDir.mkdirs()){
            Log.d("Camera Guide", "Required media storage does not exist");
            return null;
        }
    }

    // Create a media file name
    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
    File mediaFile;
    mediaFile = new File(mediaStorageDir.getPath() + File.separator +
            "IMG_"+ timeStamp + ".jpg");

    //DialogHelper.showDialog("Success!", "Your picture has been saved!", TouchActivity.reference.getActivity());

    return mediaFile;
}

private Camera.PictureCallback mPicture = new Camera.PictureCallback() {

    @Override
    public void onPictureTaken(byte[] data, Camera camera) {

        //This One is Just for Getting a File Named after it
         loading=new ProgressDialog(BaseImagesContainer.reference);
        loading.setMessage("Getting Image Ready");
        loading.show();
        File pictureFile =getOutputMediaFile();
        if (pictureFile == null){
            Toast.makeText(TouchActivity.reference.getActivity(), "Image retrieval failed.", Toast.LENGTH_SHORT)
                    .show();
            return;
        }
        Bitmap bmp = BitmapFactory.decodeByteArray(data, 0, data.length);
        if(cameratype==Camera.CameraInfo.CAMERA_FACING_BACK){
            bmp=rotateImage(90,bmp);
        }else{
            bmp=rotateImage(270,bmp);

        }
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        bmp.compress(Bitmap.CompressFormat.PNG,1, stream);
        byte[] flippedImageByteArray = stream.toByteArray();
        try {
            FileOutputStream fos = new FileOutputStream(pictureFile);
            fos.write(flippedImageByteArray);
            fos.close();
            // Restart the camera preview.
            //safeCameraOpenInView(mCameraView);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        Uri destination = Uri.fromFile(new File(TouchActivity.reference.getActivity().getCacheDir(), "cropped"));
        Uri source = Uri.fromFile(new File(pictureFile.getPath()));
        Crop.of(source, destination).withMaxSize(800,800).start(TouchActivity.reference.getActivity());
    }
};


public Bitmap rotateImage(int angle, Bitmap bitmapSrc) {
    Matrix matrix = new Matrix();
    matrix.postRotate(angle);
    return Bitmap.createBitmap(bitmapSrc, 0, 0,
            bitmapSrc.getWidth(), bitmapSrc.getHeight(), matrix, true);
}


public void switchCamera(){

    mCamera.stopPreview();
    //NB: if you don't release the current camera before switching, you app will crash
    mCamera.release();

    //swap the id of the camera to be used
    if(cameratype==Camera.CameraInfo.CAMERA_FACING_BACK){
        cameratype=Camera.CameraInfo.CAMERA_FACING_FRONT;
    }else{
        cameratype=Camera.CameraInfo.CAMERA_FACING_BACK;
    }
    try{
        mCamera = Camera.open(cameratype);
    }catch (Exception e){
        e.printStackTrace();
        Toast.makeText(TouchActivity.reference.getActivity(),"Can't Open the Camera",Toast.LENGTH_LONG).show();
    }

    if (TouchActivity.reference.getResources().getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE) {

        mCamera.setDisplayOrientation(90);

    } else {

        mCamera.setDisplayOrientation(0);

    }

    try{
        mCamera.setPreviewDisplay(sHolder);
        mCamera.startPreview();
    }
    catch(Exception e){
        e.printStackTrace();
    }

}

public void switchflash(){
    //Do the On Flash for now
    if(!flashon){
        mSupportedFlashModes = mCamera.getParameters().getSupportedFlashModes();
        if (mSupportedFlashModes != null && mSupportedFlashModes.contains(Camera.Parameters.FLASH_MODE_AUTO)){
            Camera.Parameters parameters = mCamera.getParameters();
            parameters.setFlashMode(Camera.Parameters.FLASH_MODE_AUTO);
            mCamera.setParameters(parameters);
        }
    }else{
        //flash on
        //do teh off now
        Camera.Parameters parameters = mCamera.getParameters();
        parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
        mCamera.setParameters(parameters);
    }
    flashon=!flashon;

}

public void stopLoading(){
    loading.dismiss();
    //DialogHelper.showDialog("Oops!", "Your crop had been cancelled !", TouchActivity.reference.getActivity());

}
Floern
  • 33,559
  • 24
  • 104
  • 119

1 Answers1

1

The root cause is the same as in Camera Preview appearing really slow in Android. I have even mentioned ANR in my comment there.

To avoid ANR, you must offload Camera.open() to another thread. If you invest in doing it on a Handler thread, as described here, your onPictureTaken() callback will not cause ANR, too.

You will need to wrap some parts of this callback in runOnUiThread(), but this will definitely pay off.

Community
  • 1
  • 1
Alex Cohn
  • 56,089
  • 9
  • 113
  • 307
  • Okay Sir , I tried to use the Handler Approach as you have described in the comment in some other post. I get the camera and when I try to set PreviewDisplay as the holder i Get a black Screen at the notifycameraOpened . Why is that So ? synchronized void notifyCameraOpened() { //notify(); if(mCamera==null){ Log.d("Notify","My Camera"); }else { Log.d("Notify","Null Camera"); } try{ mCamera.setPreviewDisplay(sHolder); mCamera.startPreview(); } catch(Exception e){ e.printStackTrace(); } } – facebook-1590302711235435 Nov 12 '15 at 07:36
  • I usually call `Camera.setPreviewDisplay()` after `surfaceChanged()` callback. – Alex Cohn Nov 12 '15 at 09:13
  • I did it, sett the PreviewDisplay in the surfaceChanged Callback But still, it's showing a Black Screen . Why is it So ? – facebook-1590302711235435 Nov 12 '15 at 09:25
  • If you put your updated code on pastebin, I will check – Alex Cohn Nov 12 '15 at 10:04
  • http://pastebin.com/CPkTh916 Here it goes Sir. Please Help me Out , I am in a deep Trouble :) – facebook-1590302711235435 Nov 12 '15 at 10:15
  • Okay , I Just Did Camera.Open() It works fine on Pre-Lollipop Devices but in Lollipop Devices, it just throwed me this StackTrace: 11-12 17:47:12.501 19907-19907/com.nepalimutu.pujanpaudel.app.priceoverrrflow W/CameraBase: An error occurred while connecting to camera: 0 11-12 17:47:12.511 19907-19907/com.nepalimutu.pujanpaudel.app.priceoverrrflow W/System.err: java.lang.RuntimeException: Fail to connect to camera service – facebook-1590302711235435 Nov 12 '15 at 12:03
  • You used the **wait** without sending an interrupt from **notify()**! But this piece is not necessary for your flow, so I simply removed it, see http://pastebin.com/VQpYQTu5 – Alex Cohn Nov 12 '15 at 12:03
  • Now, there are unrelated problems in your code: **a)** the rotation does not work correctly for all orientations, and is subject to the ["upside down" glitch](http://stackoverflow.com/a/19599599/192373); and **b)** your choice of front and back cameras is wrong: `Camera.open( Camera.CameraInfo.CAMERA_FACING_BACK)` and such should be replaced with a loop through available cameras, see this [snippet](http://stackoverflow.com/a/4767832/192373). And there is also a *related* problem: **switchCamera()** should also do its work on **mThread** – Alex Cohn Nov 12 '15 at 12:05
  • The Previous Pastebin was working with a Couldnt Connect to Camera Exception , but the newone doesnt even works . Screen goes competely Black. Why is the Exception even after all the Camera Permissions ?? And Please update the code the new Pastebin gives me completely black Stuff – facebook-1590302711235435 Nov 12 '15 at 12:19
  • Oops, with pastebin the **wait()** got undeleted. This is what I explained in the [comment](http://stackoverflow.com/questions/33658402/surfaceview-camera-preview-code-working-well-on-pre-lollipop-but-crashing-on-lol/33660607?noredirect=1#comment55114783_33660607). See http://pastebin.com/PHJBTYYJ, hope now it's clean. – Alex Cohn Nov 12 '15 at 12:31
  • +1 Alex . Now the Preview Works Fast as Expected. You have been great through the Code. But the Problem still exists in the Lollipop Devices. Why is it So . I am in the updated code of your latest pastebin. :( – facebook-1590302711235435 Nov 12 '15 at 12:58
  • Strange, I did test it on Nexus 5 running 6.0. I don't have 5.1 here right now, but I will ask my wife to let me test her phone when we are back home – Alex Cohn Nov 12 '15 at 13:31
  • Oops, another pastebin glitch at line 110. Hope the new one http://pastebin.com/Ra5ZiEQp is clean – Alex Cohn Nov 12 '15 at 15:48
  • Sir , I did Tried with your latest Pastebin. But it still hits out with Can't Open Camera on Lollipop Device but works well with Kitkat . Do Check the StackTrace : Surface Changed 11-12 21:42:05.721 10851-10974/com.nepalimutu.pujanpaudel.app.priceoverrrflow W/CameraBase: An error occurred while connecting to camera: 0 11-12 21:42:05.721 10851-10974/com.nepalimutu.pujanpaudel.app.priceoverrrflow W/System.err: java.lang.RuntimeException: Fail to connect to camera service Is it happening due to Concurrency Issues ? – facebook-1590302711235435 Nov 12 '15 at 15:59
  • "Failed to connect to camera service" usually results from previous failure to release the camera correctly, and often can only be cured be device reboot. To verify, you can try to open the stock camera app. Sometimes, this even can release the camera. Anyways, after stock camera app opens, try my code. – Alex Cohn Nov 12 '15 at 16:05
  • . Yes Sir . tried by Opening the Stock Camera. Stock camera does open. Did 3 Reboots and tried to open but the Code is Still not working . Just for the Lollypop One . Seems like the code is bug - free but still its not working – facebook-1590302711235435 Nov 12 '15 at 16:10
  • Let us try it from the other side. Check if [this APK](https://github.com/alexcohn/OpenGLCamera/blob/master/testSurfaceView.apk) works for you on Lollipop. – Alex Cohn Nov 12 '15 at 16:25
  • Yes, This APK Did worked like a Charm . What can we do now ? – facebook-1590302711235435 Nov 12 '15 at 16:39
  • Take my [class](https://github.com/alexcohn/OpenGLCamera/blob/master/src/com/example/openglcamera/CameraPreview.java) and try to find what got lost in pastebin. The simple TouchActivity is [here](https://github.com/alexcohn/OpenGLCamera/blob/master/src/com/example/openglcamera/TouchActivity.java). – Alex Cohn Nov 12 '15 at 16:48
  • I tried to use the resources But Didnt get any idea to use it . BTW i tried with my own code. Found a Strange behaviour. The Camera gets all working without any problem when I resume it . It seems very strange now – facebook-1590302711235435 Nov 12 '15 at 18:53
  • the Code works pretty Well with normal fragment but fails with Viewpagers. Why is it So ? – facebook-1590302711235435 Nov 14 '15 at 18:23
  • Re: ViewPager, do you put camera preview in one the first fragment, and try to emulate the behavior of AOSP camera app, swiping through the gallery? I suggest that you study the code at https://android.googlesource.com/platform/packages/apps/Camera/+/master. It does not use ViewPager or fragments. While generally speaking, AOSP git is not the best place to learn good coding style, they do show many important techniques to have your app do its job. – Alex Cohn Nov 15 '15 at 08:04