web-dev-qa-db-fra.com

android orientation de la surface de la caméra

Ok donc j'ai une classe qui étend SurfaceView et remplace

surfaceChanged - appelle simplement startPreview
surfaceCreated - ouvre la caméra, modifie les paramètres *, définit surfaceHolder
surfaceDestroyed - appelle stopPreview, relâche la caméra

tout cela fonctionne très bien parce que lorsque l'orientation est Portrait:

à partir de la surfaceCréé *

m_camera = Camera.open();
Camera.Parameters p = m_camera.getParameters();

if (getResources().getConfiguration().orientation != 
    Configuration.ORIENTATION_LANDSCAPE)
{
    p.set("orientation", "portrait");

    // CameraApi is a wrapper to check for backwards compatibility  
    if (CameraApi.isSetRotationSupported())
    {
         CameraApi.setRotation(p, 90);
    }
}

Cependant, chaque fois que l'orientation change, elle appelle Camera.open () ... qui, comme vous le savez peut-être, est une opération assez coûteuse, ce qui rend les transitions moins douces.

Lorsque je force l'orientation au paysage, l'aperçu est super. Créer n'est appelé qu'une seule fois, ce qui fonctionne car l'aperçu est en mode paysage, la caméra est toujours ce que l'utilisateur voit. Cependant, j'ai besoin d'un moyen de définir l'orientation de la photo réelle prise en mode portrait. Cependant, lorsque je force le paysage, la surface n'est jamais recréée et les paramètres ne sont jamais définis lorsque l'appareil photo est en mode portrait.

Alors, comment puis-je effectuer l'une des opérations suivantes (exclusivement)?

  1. Maintenez m_camera entre onDestroy et onCreate lorsque l'orientation change pour que la transition soit fluide

  2. Forcer le paysage et détecter les changements d'orientation d'une autre manière ... rotation de la dernière photo prise si elle est maintenue en mode portrait.

De plus, si je suis hors de la base, quelqu'un peut-il m'orienter dans une meilleure direction? Je vous remercie.

29
Tom Fobear

La façon dont je l'ai implémenté:

private Camera mCamera;
private OrientationEventListener mOrientationEventListener;
private int mOrientation =  -1;

private static final int ORIENTATION_PORTRAIT_NORMAL =  1;
private static final int ORIENTATION_PORTRAIT_INVERTED =  2;
private static final int ORIENTATION_LANDSCAPE_NORMAL =  3;
private static final int ORIENTATION_LANDSCAPE_INVERTED =  4;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // force Landscape layout
    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR | ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
   /*
   Your other initialization code here
   */
}

@Override 
protected void onResume() {
    super.onResume();

    if (mOrientationEventListener == null) {            
        mOrientationEventListener = new OrientationEventListener(this, SensorManager.SENSOR_DELAY_NORMAL) {

            @Override
            public void onOrientationChanged(int orientation) {

                // determine our orientation based on sensor response
                int lastOrientation = mOrientation;

                if (orientation >= 315 || orientation < 45) {
                    if (mOrientation != ORIENTATION_PORTRAIT_NORMAL) {                          
                        mOrientation = ORIENTATION_PORTRAIT_NORMAL;
                    }
                }
                else if (orientation < 315 && orientation >= 225) {
                    if (mOrientation != ORIENTATION_LANDSCAPE_NORMAL) {
                        mOrientation = ORIENTATION_LANDSCAPE_NORMAL;
                    }                       
                }
                else if (orientation < 225 && orientation >= 135) {
                    if (mOrientation != ORIENTATION_PORTRAIT_INVERTED) {
                        mOrientation = ORIENTATION_PORTRAIT_INVERTED;
                    }                       
                }
                else { // orientation <135 && orientation > 45
                    if (mOrientation != ORIENTATION_LANDSCAPE_INVERTED) {
                        mOrientation = ORIENTATION_LANDSCAPE_INVERTED;
                    }                       
                }   

                if (lastOrientation != mOrientation) {
                    changeRotation(mOrientation, lastOrientation);
                }
            }
        };
    }
    if (mOrientationEventListener.canDetectOrientation()) {
        mOrientationEventListener.enable();
    }
}

@Override protected void onPause() {
    super.onPause();
    mOrientationEventListener.disable();
}

/**
 * Performs required action to accommodate new orientation
 * @param orientation
 * @param lastOrientation
 */
private void changeRotation(int orientation, int lastOrientation) {
    switch (orientation) {
        case ORIENTATION_PORTRAIT_NORMAL:
            mSnapButton.setImageDrawable(getRotatedImage(Android.R.drawable.ic_menu_camera, 270));
            mBackButton.setImageDrawable(getRotatedImage(Android.R.drawable.ic_menu_revert, 270));
            Log.v("CameraActivity", "Orientation = 90");
            break;
        case ORIENTATION_LANDSCAPE_NORMAL:
            mSnapButton.setImageResource(Android.R.drawable.ic_menu_camera);
            mBackButton.setImageResource(Android.R.drawable.ic_menu_revert);
            Log.v("CameraActivity", "Orientation = 0");
            break;
        case ORIENTATION_PORTRAIT_INVERTED:
            mSnapButton.setImageDrawable(getRotatedImage(Android.R.drawable.ic_menu_camera, 90));
            mBackButton.setImageDrawable(getRotatedImage(Android.R.drawable.ic_menu_revert, 90));
            Log.v("CameraActivity", "Orientation = 270");
            break;
        case ORIENTATION_LANDSCAPE_INVERTED:
            mSnapButton.setImageDrawable(getRotatedImage(Android.R.drawable.ic_menu_camera, 180));
            mBackButton.setImageDrawable(getRotatedImage(Android.R.drawable.ic_menu_revert, 180));      
            Log.v("CameraActivity", "Orientation = 180");
            break;
    }
}

    /**
 * Rotates given Drawable
 * @param drawableId    Drawable Id to rotate
 * @param degrees       Rotate drawable by Degrees
 * @return              Rotated Drawable
 */
private Drawable getRotatedImage(int drawableId, int degrees) {
    Bitmap original = BitmapFactory.decodeResource(getResources(), drawableId);
    Matrix matrix = new Matrix();
    matrix.postRotate(degrees);

    Bitmap rotated = Bitmap.createBitmap(original, 0, 0, original.getWidth(), original.getHeight(), matrix, true);
    return new BitmapDrawable(rotated);
}

Et puis dans vos métadonnées de jeu PictureCallback pour indiquer le niveau de rotation:

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

    @Override
    public void onPictureTaken(byte[] data, Camera camera) {
        try {
            // Populate image metadata

            ContentValues image = new ContentValues();
            // additional picture metadata
            image.put(Media.DISPLAY_NAME, [picture name]);
            image.put(Media.MIME_TYPE, "image/jpg");
            image.put(Media.TITLE, [picture title]);
            image.put(Media.DESCRIPTION, [picture description]);
            image.put(Media.DATE_ADDED, [some time]);
            image.put(Media.DATE_TAKEN, [some time]);
            image.put(Media.DATE_MODIFIED, [some time]);

            // do not rotate image, just put rotation info in
            switch (mOrientation) {
                case ORIENTATION_PORTRAIT_NORMAL:
                    image.put(Media.ORIENTATION, 90);
                    break;
                case ORIENTATION_LANDSCAPE_NORMAL:
                    image.put(Media.ORIENTATION, 0);
                    break;
                case ORIENTATION_PORTRAIT_INVERTED:
                    image.put(Media.ORIENTATION, 270);
                    break;
                case ORIENTATION_LANDSCAPE_INVERTED:
                    image.put(Media.ORIENTATION, 180);
                    break;
            }

            // store the picture
            Uri uri = getContentResolver().insert(
                    Media.EXTERNAL_CONTENT_URI, image);

            try {
                Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0,
                        data.length);
                OutputStream out = getContentResolver().openOutputStream(
                        uri);
                boolean success = bitmap.compress(
                        Bitmap.CompressFormat.JPEG, 75, out);
                out.close();
                if (!success) {
                    finish(); // image output failed without any error,
                                // silently finish
                }

            } catch (Exception e) {
                e.printStackTrace();
                // handle exceptions
            }

            mResultIntent = new Intent();
            mResultIntent.setData(uri);
        } catch (Exception e) {
            e.printStackTrace();
        }

        finish();
    }
};

J'espère que ça aide.

MISE À JOUR Maintenant, lorsque les appareils basés sur le paysage apparaissent, une vérification supplémentaire est requise dans OrientationEventListener.

Display display = ((WindowManager)getSystemService(WINDOW_SERVICE)).getDefaultDisplay();                                        
if (display.getOrientation() == Surface.ROTATION_0) { 
    // landscape oriented devices
} else { 
    // portrait oriented device
}

Code complet (un peu de gaspillage par LC, mais démontre facilement l'approche)

@Override
public void onOrientationChanged(int orientation) {

    // determine our orientation based on sensor response
    int lastOrientation = mOrientation;

    Display display = ((WindowManager)getSystemService(WINDOW_SERVICE)).getDefaultDisplay();                                        

    if (display.getOrientation() == Surface.ROTATION_0) {   // landscape oriented devices
        if (orientation >= 315 || orientation < 45) {
            if (mOrientation != ORIENTATION_LANDSCAPE_NORMAL) {                         
                mOrientation = ORIENTATION_LANDSCAPE_NORMAL;
            }
        } else if (orientation < 315 && orientation >= 225) {
            if (mOrientation != ORIENTATION_PORTRAIT_INVERTED) {
                mOrientation = ORIENTATION_PORTRAIT_INVERTED;
            }                       
        } else if (orientation < 225 && orientation >= 135) {
            if (mOrientation != ORIENTATION_LANDSCAPE_INVERTED) {
                mOrientation = ORIENTATION_LANDSCAPE_INVERTED;
            }                       
        } else if (orientation <135 && orientation > 45) { 
            if (mOrientation != ORIENTATION_PORTRAIT_NORMAL) {
                mOrientation = ORIENTATION_PORTRAIT_NORMAL;
            }                       
        }                       
    } else {  // portrait oriented devices
        if (orientation >= 315 || orientation < 45) {
            if (mOrientation != ORIENTATION_PORTRAIT_NORMAL) {                          
                mOrientation = ORIENTATION_PORTRAIT_NORMAL;
            }
        } else if (orientation < 315 && orientation >= 225) {
            if (mOrientation != ORIENTATION_LANDSCAPE_NORMAL) {
                mOrientation = ORIENTATION_LANDSCAPE_NORMAL;
            }                       
        } else if (orientation < 225 && orientation >= 135) {
            if (mOrientation != ORIENTATION_PORTRAIT_INVERTED) {
                mOrientation = ORIENTATION_PORTRAIT_INVERTED;
            }                       
        } else if (orientation <135 && orientation > 45) { 
            if (mOrientation != ORIENTATION_LANDSCAPE_INVERTED) {
                mOrientation = ORIENTATION_LANDSCAPE_INVERTED;
            }                       
        }
    }

    if (lastOrientation != mOrientation) {
        changeRotation(mOrientation, lastOrientation);
    }
}
58
Audrius

Avez-vous envisagé d'utiliser la méthode standard fournie dans le document API, que vous pouvez appeler sur surfaceChanged? Vous pouvez stocker les degrés dans une variable globale pour une utilisation ultérieure lors de l'enregistrement de l'image. Peut également faire un simple vérificateur nul sur la variable de votre caméra, vous ne devez donc pas la recréer dans surfaceCreated.

public void setCameraDisplayOrientation() 
{        
     if (mCamera == null)
     {
         Log.d(TAG,"setCameraDisplayOrientation - camera null");
         return;             
     }

     Camera.CameraInfo info = new Camera.CameraInfo();
     Camera.getCameraInfo(CAM_ID, info);

     WindowManager winManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
     int rotation = winManager.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;
     }
     mCamera.setDisplayOrientation(result);
}
18
OriginalCliche

Comme vous l'avez vu dans les autres réponses, ce code devient très compliqué. Vous voudrez peut-être étudier l'utilisation d'une bibliothèque pour vous aider à fournir cette fonctionnalité, par exemple, CWAC-Camera prend en charge OS 2.3 et supérieur (j'espère que vous pouvez supprimer le support OS 2.1 et OS 2.2 maintenant):
https://github.com/commonsguy/cwac-camera

CWAC-Camera prend en charge le verrouillage de l'aperçu de la caméra en mode paysage et fera pivoter automatiquement les images dans l'orientation de correction pour vous. Parcourez problèmes de projet si vous voulez avoir un aperçu de tous les problèmes spécifiques aux périphériques qui doivent être résolus, quelles IMO sont plus de raisons d'essayer d'utiliser une bibliothèque au lieu de maintenir tout ce code et de vous tester.

3
Dan J