¿Cómo configurar correctamente la orientación de la cámara de Android?

90

Quiero configurar la orientación de la cámara de acuerdo con la orientación del dispositivo en Android, pero parece que nada funciona. Traté de rotar Surface y los parámetros de la cámara, pero la vista previa de la cámara en modo retrato siempre se da vuelta. Necesitaría rotarlo 90 grados en el sentido de las agujas del reloj para que sea correcto. Aquí está el código que estoy usando en este momento que funciona solo en modo horizontal.

    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
fuente
6
AFAIK, la vista previa de la cámara solo funciona realmente en paisaje, al menos para 2.2 y anteriores. Especulo que es por eso que las actividades que tienen vista previa de la cámara tienden a ocultar la barra de notificaciones del sistema y no tienen títulos ... a pesar de ser aparentemente retratos, creo que son "realmente" paisajes.
Reuben Scratton

Respuestas:

71

De otro miembro y mi problema:

El problema de rotación de la cámara depende de diferentes dispositivos y de una versión determinada.

Versión 1.6: para solucionar el problema de rotación, y es bueno para la mayoría de dispositivos

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);
        }

Versión 2.1: depende del tipo de dispositivo, por ejemplo, no se puede solucionar el problema con XPeria X10, pero es bueno para X8 y Mini

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

Versión 2.2: no para todos los dispositivos

camera.setDisplayOrientation(90);

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

LTEHUB
fuente
3
¿Qué quieres decir con la versión aquí?
Md. Sulayman
31

De los Javadocs para setDisplayOrientation(int)(Requiere API nivel 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
fuente
1
@Derzu La clase CameraInfono se introdujo hasta el nivel de API 9, por lo que el método que publiqué requiere el nivel de API 9.
Jason Robinson
2
result = (360 - result) % 360; // compensate the mirrordeben eliminarse , las imágenes de la cámara frontal se rotan incorrectamente de lo
stevo.mit
@ stevo.mit, ¿ha verificado esto en varios dispositivos? He usado este código para la rotación varias veces y no he encontrado una rotación incorrecta.
Jason Robinson
2
@ Jason Robinson Tengo una lista de modelos en los que el nivel de API está por encima de 9, pero aún así este método no tiene ningún efecto. No sé si es un problema relacionado con el hardware. Lista de dispositivos 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
1
@AbdulMohsin Mire developer.android.com/reference/android/hardware/… , particularmente CAMERA_FACING_BACK y CAMERA_FACING_FRONT.
Jason Robinson
25

Esta solución funcionará para todas las versiones de Android. Puede utilizar la reflexión en Java para que funcione en todos los dispositivos Android:

Básicamente, debe crear un contenedor de reflexión para llamar al setDisplayOrientation de Android 2.2, en lugar de llamar al método específico.

El método:

    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)
    {
    }
}

Y en lugar de usar 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
fuente
1
La parte else funciona en algunos dispositivos 2.1 pero no en TODOS (ver la explicación de maydenec arriba).
Eric Chen
1
Creo que p es de tipo Camera.Parameters. Intente agregar la siguiente línea:Camera.Parameters p = camera.getParameters();
Sehrish Khan
6

Enfrenté el problema cuando estaba usando ZBar para escanear pestañas. Problema de orientación de la cámara. Usando el siguiente código, pude resolver el problema. Este no es el fragmento de código completo. Por favor, tome solo la ayuda de esto.

 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
fuente
5

Finalmente arreglé esto usando la aplicación de cámara de Google. Obtiene la orientación del teléfono mediante el uso de un sensor y luego establece la etiqueta EXIF ​​de manera adecuada. El JPEG que sale de la cámara no se orienta automáticamente.

Además, la vista previa de la cámara funciona correctamente solo en el modo horizontal. Si necesita que el diseño de su actividad esté orientado en vertical, tendrá que hacerlo manualmente utilizando el valor del sensor de orientación.

Abhinav
fuente
2
oye, ¿cómo obtener la orientación de la cámara usando el sensor de orientación? por favor comparte tu código ...
Rishi
@Rishi vea este enlace stackoverflow.com/questions/9055460/…
PiyushMishra
4
Gracias por la ayuda, pero mi problema es cuando estoy tomando fotos en teléfonos Symsung en modo vertical en ese momento, la foto se muestra girada 90 grados en mi pantalla. Entonces, estoy tratando de obtener la orientación de la cámara, así que giré la foto 90 grados para el modo retrato
Rishi
5

mira esta solución

 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
fuente
Gracias por manejar la cámara frontal. Esto resolvió mi problema
Louis CAD
eres bienvenido @LouisCAD feliz de ver que mi solución resuelve tu problema.
Mudassir Khan
4

Este problema se resolvió hace mucho tiempo, pero encontré algunas dificultades para unir todas las piezas, así que aquí está mi solución final, espero que esto ayude a otros:

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());
        }
    }
Luis GRIGNON
fuente
'Espero que esto ayude a otros' No, no lo hará. Su fragmento de código se saca de contexto. Por ejemplo, ¿de dónde viene 'isFrontFacingCam'?
seanpj
4
Creo que este es otro tema en realidad, esto no es nada comparado con el problema inicial. Puede obtenerlo de android.hardware.Camera.CameraInfo.facing == CameraInfo.CAMERA_FACING_FRONT. Perdón por esto.
Louis GRIGNON