Search code examples
androidcamerasurfaceviewfront-camera

Camera Preview Stretched on Front Facing Camera


Hello there I am working on an android custom camera app and facing a problem of the camera preview stretching on the following device on their front camera:

  1. huawei Honor 4c (only on front facing camera)
  2. HTC One M7 (only on front facing camera)
  3. Some other devices also...as I have just the following devices to test so may be problem resides on other devices too.

The thing is that the camera preview works very well with the back camera but it only stretches when I use my app with the front facing camera!


The Code

Here is the code I am using for my Camera Preview Class:

 import android.content.Context;
 import android.graphics.Rect;
 import android.hardware.Camera;
 import android.util.Log;
 import android.view.Gravity;
 import android.view.MotionEvent;
 import android.view.SurfaceHolder;
 import android.view.SurfaceView;
 import android.widget.FrameLayout;
 import android.widget.Toast;
 import java.util.ArrayList;
 import java.util.List;

 public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {

//Variables de' Preview
private SurfaceHolder mHolder;
private List<Camera.Size> mSupportedPreviewSizes;
private Camera.Size mPreviewSize;
private int FOCUS_AREA_SIZE=300;
public static Camera previewCamera;
////////////////////////

//protected LOGGER keys
protected final String EXCEPTION_KEY="xception";
//////////////////////////

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

    //get the camera
    previewCamera = camera;

    // supported preview sizes
    mSupportedPreviewSizes = previewCamera.getParameters().getSupportedPreviewSizes();

    // 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);
    mHolder.setKeepScreenOn(true);

}


@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
    try{
        //when the surface is created, we can set the camera to draw images in this surfaceholder
        previewCamera.setPreviewDisplay(surfaceHolder);
        previewCamera.startPreview();
    } catch(Exception exp){
        Log.i(EXCEPTION_KEY,"FROM surfaceCreated: "+exp.toString());
    }
}

@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i2, int i3) {
    //before changing the application orientation, you need to stop the preview, rotate and then start it again

    if(mHolder.getSurface() == null)//check if the surface is ready to receive camera data
        return;

    try{
        previewCamera.stopPreview();
    } catch (Exception e){
        //this will happen when you are trying the camera if it's not running
    }

    //now, recreate the camera preview
    try{
        //set the camera preview on every preview change
        setPreview();
        previewCamera.setPreviewDisplay(mHolder);
        previewCamera.startPreview();
    } catch(Exception exp){
        Log.i(EXCEPTION_KEY,"FROM surfaceChanged: "+exp.toString());
    }
}


public void setPreview(){
    try{
        //set the focusable true
        this.setFocusable(true);
        //set the touch able true
        this.setFocusableInTouchMode(true);
        //set the camera display orientation lock
        previewCamera.setDisplayOrientation(90);

        //get the camera parameters
        Camera.Parameters parameters = previewCamera.getParameters();
        //set the preview size
        parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
        //set the parameter
        previewCamera.setParameters(parameters);
    }catch(Exception exp){
        Log.i(EXCEPTION_KEY,"FROM setPreview: "+exp.toString());
    }
}


@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
    //our app has only one screen, so we'll destroy the camera in the surface
    //if you are using with more screens, please move this code your activity
    try{
        //handle in Activity onResume and onPause
    }catch(Exception exp){
        Log.i(EXCEPTION_KEY,"FROM surfaceDestroyed: "+exp.toString());
    }
}

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

        if (mSupportedPreviewSizes != null) {
            mPreviewSize = PreviewSizeParameters.getOptimalPreviewSize(mSupportedPreviewSizes, width, height);
        }
    }catch(Exception exp){
        Log.i(EXCEPTION_KEY,"FROM onMeasure: "+exp.toString());
    }
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////

private void stopPreviewAndFreeCamera() {

    if (previewCamera != null) {
        // Call stopPreview() to stop updating the preview surface.
        previewCamera.stopPreview();

        // Important: Call release() to release the camera for use by other
        // applications. Applications should release the camera immediately
        // during onPause() and re-open() it during onResume()).
        previewCamera.release();

        previewCamera = null;
    }
}

//end of class here

  }

Can somebody please tell me what am I missing in my code because it only affects on some devices using FRONT FACING CAMERA only.


Thanks


Solution

  • The following code worked in my environment.
    Call getOptimalPreviewSize in surfaceChanged with SurfaceView's with and height.

    public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
    
        ...
    
        @Override
        public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int w, int h) {
            //before changing the application orientation, you need to stop the preview, rotate and then start it again
    
            if(mHolder.getSurface() == null)//check if the surface is ready to receive camera data
                return;
    
            try{
                previewCamera.stopPreview();
            } catch (Exception e){
                //this will happen when you are trying the camera if it's not running
            }
    
            //now, recreate the camera preview
            try{
                //set the focusable true
                this.setFocusable(true);
                //set the touch able true
                this.setFocusableInTouchMode(true);
                //set the camera display orientation lock
                previewCamera.setDisplayOrientation(90);
    
                Camera.Parameters params = previewCamera.getParameters();
                List<Camera.Size> sizes = params.getSupportedPreviewSizes();
                Camera.Size optimalSize = getOptimalPreviewSize(sizes,w,h);
                params.setPreviewSize(optimalSize.width,optimalSize.height);
                previewCamera.setParameters(params);
    
                previewCamera.setPreviewDisplay(mHolder);
                previewCamera.startPreview();
            } catch(Exception exp){
                Log.i(EXCEPTION_KEY,"FROM surfaceChanged: "+exp.toString());
            }
        }
    
        ...
    
        private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h) {
    
            final double ASPECT_TOLERANCE = 0.1;
            double targetRatio=(double)h / w;
    
            if (sizes == null) return null;
    
            Camera.Size optimalSize = null;
            double minDiff = Double.MAX_VALUE;
    
            int targetHeight = h;
    
            for (Camera.Size size : sizes) {
                double ratio = (double) size.width / size.height;
                if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
                if (Math.abs(size.height - targetHeight) < minDiff) {
                    optimalSize = size;
                    minDiff = Math.abs(size.height - targetHeight);
                }
            }
    
            if (optimalSize == null) {
                minDiff = Double.MAX_VALUE;
                for (Camera.Size size : sizes) {
                    if (Math.abs(size.height - targetHeight) < minDiff) {
                        optimalSize = size;
                        minDiff = Math.abs(size.height - targetHeight);
                    }
                }
            }
            return optimalSize;
        }
    //end of class here
    
    }