91

I want to set the camera orientation according to the device orientation in Android but nothing seems to be working. I tried to rotate the Surface as well as the camera parameters but the camera preview in portrait mode always comes upside down. I would need to rotate it by 90 degree clockwise for it to be correct. Here is the code I am using right now which works in landscape mode only.

    SurfaceHolder.Callback surfaceCallback = new SurfaceHolder.Callback() {

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        camera.stopPreview();
        camera.release();
        camera = null;
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {          
        initCamera();           
    }

    private Size getOptimalPreviewSize(List<Size> sizes, int w, int h) {
        final double ASPECT_TOLERANCE = 0.2;
        double targetRatio = (double) w / h;
        if (sizes == null)
            return null;

        Size optimalSize = null;
        double minDiff = Double.MAX_VALUE;

        int targetHeight = h;

        // Try to find an size match aspect ratio and size
        for (Size size : sizes) {
            Log.d(TAG, "Checking size " + size.width + "w " + size.height
                    + "h");
            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);
            }
        }

        // Cannot find the one match the aspect ratio, ignore the
        // requirement
        if (optimalSize == null) {
            minDiff = Double.MAX_VALUE;
            for (Size size : sizes) {
                if (Math.abs(size.height - targetHeight) < minDiff) {
                    optimalSize = size;
                    minDiff = Math.abs(size.height - targetHeight);
                }
            }
        }
        return optimalSize;
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        Camera.Parameters parameters = camera.getParameters();

        List<Size> sizes = parameters.getSupportedPreviewSizes();
        Size optimalSize = getOptimalPreviewSize(sizes, width, height);         
        Log.d(TAG, "Surface size is " + width + "w " + height + "h");
        Log.d(TAG, "Optimal size is " + optimalSize.width + "w " + optimalSize.height + "h");           
        parameters.setPreviewSize(optimalSize.width, optimalSize.height);           
        // parameters.setPreviewSize(width, height);            
        camera.setParameters(parameters);
        camera.startPreview();
    }
};  
Abhinav
  • 38,516
  • 9
  • 41
  • 49
  • 6
    AFAIK the camera preview only truly works in landscape, at least for 2.2 and earlier. I speculate that that's why activities which do camera preview tend to hide the system notification bar and don't have titles... despite being apparently portrait I think they are "really" landscape. – Reuben Scratton Feb 28 '11 at 12:01

7 Answers7

71

From other member and my problem:

Camera Rotation issue depend on different Devices and certain Version.

Version 1.6: to fix the Rotation Issue, and it is good for most of devices

if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT)
        {   
            p.set("orientation", "portrait");
            p.set("rotation",90);
        }
        if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE)
        {                               
            p.set("orientation", "landscape");          
            p.set("rotation", 90);
        }

Version 2.1: depend on kind of devices, for example, Cannt fix the issue with XPeria X10, but it is good for X8, and Mini

Camera.Parameters parameters = camera.getParameters();
parameters.set("orientation", "portrait");
camera.setParameters(parameters);

Version 2.2: not for all devices

camera.setDisplayOrientation(90);

http://code.google.com/p/android/issues/detail?id=1193#c42

LTEHUB
  • 1,638
  • 1
  • 14
  • 17
31

From the Javadocs for setDisplayOrientation(int) (Requires API level 9):

 public static void setCameraDisplayOrientation(Activity activity,
         int cameraId, android.hardware.Camera camera) {
     android.hardware.Camera.CameraInfo info =
             new android.hardware.Camera.CameraInfo();
     android.hardware.Camera.getCameraInfo(cameraId, info);
     int rotation = activity.getWindowManager().getDefaultDisplay()
             .getRotation();
     int degrees = 0;
     switch (rotation) {
         case Surface.ROTATION_0: degrees = 0; break;
         case Surface.ROTATION_90: degrees = 90; break;
         case Surface.ROTATION_180: degrees = 180; break;
         case Surface.ROTATION_270: degrees = 270; break;
     }

     int result;
     if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
         result = (info.orientation + degrees) % 360;
         result = (360 - result) % 360;  // compensate the mirror
     } else {  // back-facing
         result = (info.orientation - degrees + 360) % 360;
     }
     camera.setDisplayOrientation(result);
 }
Jason Robinson
  • 31,005
  • 19
  • 77
  • 131
  • 1
    @Derzu The class `CameraInfo` wasn't introduced until API level 9, so the method I posted requires API level 9. – Jason Robinson Jul 24 '12 at 16:06
  • 2
    `result = (360 - result) % 360; // compensate the mirror` should be **deleted**, the pictures from front camera are otherwise **rotated incorrectly** – stevo.mit Nov 04 '13 at 13:43
  • @stevo.mit have you verified this on multiple devices? I've used this code for rotation a number of times and have not encountered an incorrect rotation. – Jason Robinson Nov 04 '13 at 20:30
  • @Vikram It requires API level 9 – Jason Robinson Apr 07 '14 at 14:29
  • 2
    @ Jason Robinson I have some models list on which api level is above 9 but still this method has no effect. Don't know weather its hardware related issue. Device list rotation_issue_models = Arrays.asList("GT-S5360", "GT-S6802", "GT-S5830C", "GT-S5830I", "DROID2", "GLOBAL", "XT557", "Desire HD", "PC36100", "GT-I9000", "ADR6350", "Mi-One Plus", "SGH-T989", "GT-I9100", "GT-I9001"); – Vikram Apr 11 '14 at 08:43
  • what would be the cameraId parameter. i didn't understand this – Abdul Mohsin Nov 01 '14 at 05:48
  • 1
    @AbdulMohsin Look at http://developer.android.com/reference/android/hardware/Camera.CameraInfo.html, particularly CAMERA_FACING_BACK and CAMERA_FACING_FRONT. – Jason Robinson Nov 03 '14 at 16:40
  • @Sarasranglt No camera.setDisplayOrientation not working properly on some devices like Samsung Y and Samsung Ace. even it they have API level greater than 9 – Vikram Oct 15 '15 at 13:26
  • @Vikram May be its best to put device specific checks? – Sarasranglt Oct 16 '15 at 03:54
25

This solution will work for all versions of Android. You can use reflection in Java to make it work for all Android devices:

Basically you should create a reflection wrapper to call the Android 2.2 setDisplayOrientation, instead of calling the specific method.

The method:

    protected void setDisplayOrientation(Camera camera, int angle){
    Method downPolymorphic;
    try
    {
        downPolymorphic = camera.getClass().getMethod("setDisplayOrientation", new Class[] { int.class });
        if (downPolymorphic != null)
            downPolymorphic.invoke(camera, new Object[] { angle });
    }
    catch (Exception e1)
    {
    }
}

And instead of using camera.setDisplayOrientation(x) use setDisplayOrientation(camera, x) :

    if (Integer.parseInt(Build.VERSION.SDK) >= 8)
        setDisplayOrientation(mCamera, 90);
    else
    {
        if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT)
        {
            p.set("orientation", "portrait");
            p.set("rotation", 90);
        }
        if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE)
        {
            p.set("orientation", "landscape");
            p.set("rotation", 90);
        }
    }   
hnviet
  • 1,747
  • 1
  • 18
  • 22
6

I faced the issue when i was using ZBar for scanning in tabs. Camera orientation issue. Using below code i was able to resolve issue. This is not the whole code snippet, Please take only help from this.

 public void surfaceChanged(SurfaceHolder holder, int format, int width,
                               int height) {
     if (isPreviewRunning) {
            mCamera.stopPreview();
        }

 setCameraDisplayOrientation(mCamera);

        previewCamera();

    }



 public void previewCamera() {

        try {
            // Hard code camera surface rotation 90 degs to match Activity view
            // in portrait
            mCamera.setPreviewDisplay(mHolder);
            mCamera.setPreviewCallback(previewCallback);
            mCamera.startPreview();
            mCamera.autoFocus(autoFocusCallback);
            isPreviewRunning = true;
        } catch (Exception e) {
            Log.d("DBG", "Error starting camera preview: " + e.getMessage());
        }


    }


public void setCameraDisplayOrientation(android.hardware.Camera camera) {
        Camera.Parameters parameters = camera.getParameters();

        android.hardware.Camera.CameraInfo camInfo =
                new android.hardware.Camera.CameraInfo();
        android.hardware.Camera.getCameraInfo(getBackFacingCameraId(), camInfo);


        Display display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
        int rotation = display.getRotation();
        int degrees = 0;
        switch (rotation) {
            case Surface.ROTATION_0:
                degrees = 0;
                break;
            case Surface.ROTATION_90:
                degrees = 90;
                break;
            case Surface.ROTATION_180:
                degrees = 180;
                break;
            case Surface.ROTATION_270:
                degrees = 270;
                break;
        }

        int result;
        if (camInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
            result = (camInfo.orientation + degrees) % 360;
            result = (360 - result) % 360;  // compensate the mirror
        } else {  // back-facing
            result = (camInfo.orientation - degrees + 360) % 360;
        }
        camera.setDisplayOrientation(result);
    }




    private int getBackFacingCameraId() {
        int cameraId = -1;
        // Search for the front facing camera
        int numberOfCameras = Camera.getNumberOfCameras();
        for (int i = 0; i < numberOfCameras; i++) {
            Camera.CameraInfo info = new Camera.CameraInfo();
            Camera.getCameraInfo(i, info);
            if (info.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {

                cameraId = i;
                break;
            }
        }
        return cameraId;
    }
DeepakPanwar
  • 1,389
  • 14
  • 22
5

This problem was solved a long time ago but I encountered some difficulties to put all pieces together so here is my final solution, I hope this will help others :

public void startPreview() {
        try {
            Log.i(TAG, "starting preview: " + started);

            // ....
            Camera.CameraInfo camInfo = new Camera.CameraInfo();
            Camera.getCameraInfo(cameraIndex, camInfo);
            int cameraRotationOffset = camInfo.orientation;
            // ...

            Camera.Parameters parameters = camera.getParameters();
            List<Camera.Size> previewSizes = parameters.getSupportedPreviewSizes();
            Camera.Size previewSize = null;
            float closestRatio = Float.MAX_VALUE;

            int targetPreviewWidth = isLandscape() ? getWidth() : getHeight();
            int targetPreviewHeight = isLandscape() ? getHeight() : getWidth();
            float targetRatio = targetPreviewWidth / (float) targetPreviewHeight;

            Log.v(TAG, "target size: " + targetPreviewWidth + " / " + targetPreviewHeight + " ratio:" + targetRatio);
            for (Camera.Size candidateSize : previewSizes) {
                float whRatio = candidateSize.width / (float) candidateSize.height;
                if (previewSize == null || Math.abs(targetRatio - whRatio) < Math.abs(targetRatio - closestRatio)) {
                    closestRatio = whRatio;
                    previewSize = candidateSize;
                }
            }

            int rotation = getWindowManager().getDefaultDisplay().getRotation();
            int degrees = 0;
            switch (rotation) {
            case Surface.ROTATION_0:
                degrees = 0;
                break; // Natural orientation
            case Surface.ROTATION_90:
                degrees = 90;
                break; // Landscape left
            case Surface.ROTATION_180:
                degrees = 180;
                break;// Upside down
            case Surface.ROTATION_270:
                degrees = 270;
                break;// Landscape right
            }
            int displayRotation;
            if (isFrontFacingCam) {
                displayRotation = (cameraRotationOffset + degrees) % 360;
                displayRotation = (360 - displayRotation) % 360; // compensate
                                                                    // the
                                                                    // mirror
            } else { // back-facing
                displayRotation = (cameraRotationOffset - degrees + 360) % 360;
            }

            Log.v(TAG, "rotation cam / phone = displayRotation: " + cameraRotationOffset + " / " + degrees + " = "
                    + displayRotation);

            this.camera.setDisplayOrientation(displayRotation);

            int rotate;
            if (isFrontFacingCam) {
                rotate = (360 + cameraRotationOffset + degrees) % 360;
            } else {
                rotate = (360 + cameraRotationOffset - degrees) % 360;
            }

            Log.v(TAG, "screenshot rotation: " + cameraRotationOffset + " / " + degrees + " = " + rotate);

            Log.v(TAG, "preview size: " + previewSize.width + " / " + previewSize.height);
            parameters.setPreviewSize(previewSize.width, previewSize.height);
            parameters.setRotation(rotate);
            camera.setParameters(parameters);
            camera.setPreviewDisplay(mHolder);
            camera.startPreview();

            Log.d(TAG, "preview started");

            started = true;
        } catch (IOException e) {
            Log.d(TAG, "Error setting camera preview: " + e.getMessage());
        }
    }
Louis GRIGNON
  • 699
  • 6
  • 17
  • 'I hope this will help others' No, it will not. Your piece of code is pulled out of context. For instance, where is the 'isFrontFacingCam' coming from? – seanpj Nov 22 '14 at 23:38
  • 4
    I think this is another subject actually, this is really nothing compared to the initial problem. You can get it from android.hardware.Camera.CameraInfo.facing == CameraInfo.CAMERA_FACING_FRONT. Sorry about this. – Louis GRIGNON Dec 01 '14 at 15:20
5

check out this solution

 public static void setCameraDisplayOrientation(Activity activity,
                                                   int cameraId, android.hardware.Camera camera) {
        android.hardware.Camera.CameraInfo info =
                new android.hardware.Camera.CameraInfo();
        android.hardware.Camera.getCameraInfo(cameraId, info);
        int rotation = activity.getWindowManager().getDefaultDisplay()
                .getRotation();
        int degrees = 0;
        switch (rotation) {
            case Surface.ROTATION_0: degrees = 0; break;
            case Surface.ROTATION_90: degrees = 90; break;
            case Surface.ROTATION_180: degrees = 180; break;
            case Surface.ROTATION_270: degrees = 270; break;
        }

        int result;
        if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
            result = (info.orientation + degrees) % 360;
            result = (360 - result) % 360;  // compensate the mirror
        } else {  // back-facing
            result = (info.orientation - degrees + 360) % 360;
        }
        camera.setDisplayOrientation(result);
    }
Mudassir Khan
  • 1,714
  • 1
  • 20
  • 25
5

I finally fixed this using the Google's camera app. It gets the phone's orientation by using a sensor and then sets the EXIF tag appropriately. The JPEG which comes out of the camera is not oriented automatically.

Also, the camera preview works properly only in the landscape mode. If you need your activity layout to be oriented in portrait, you will have to do it manually using the value from the orientation sensor.

Abhinav
  • 38,516
  • 9
  • 41
  • 49
  • 2
    hey, how to get the the camera orientation using the orientation sensor ?? please share your code... – Rishi Jan 27 '12 at 10:43
  • @Rishi see this link http://stackoverflow.com/questions/9055460/is-androids-camerainfo-orientation-correctly-documented-incorrectly-implemente/9095288#9095288 – PiyushMishra Feb 02 '12 at 07:42
  • 4
    Thanks for Help,but my issue is when i m taking photo on symsung phones in portrait mode at that time photo is displayed 90 degree rotated on my screen. So, I m trying to get the orientation of camera so, i m rotated photo 90 degree for portrait mode – Rishi Feb 02 '12 at 07:54