Question

Je suis en train d'écrire une application Android 1.5 qui commence juste après démarrage. Ceci est un Service et devrait prendre une photo sans aperçu. Cette application enregistre la densité de la lumière dans certaines régions que ce soit. J'ai pu prendre une photo, mais l'image était noir.

Après avoir étudié pendant longtemps, je suis tombé sur un fil de bogue. Si vous ne génèrent pas d'aperçu, l'image sera en noir depuis l'appareil photo Android doit aperçu à l'exposition de configuration et mise au point. J'ai créé un SurfaceView et l'auditeur, mais l'événement onSurfaceCreated() ne fait jamais mis le feu.

Je suppose que la raison, la surface n'est pas créée visuellement. Je l'ai vu aussi quelques exemples de la caméra statique appeler avec MediaStore.CAPTURE_OR_SOMETHING qui prend une photo et enregistre dans le dossier désiré avec deux lignes de code, mais il ne prend pas une image aussi.

Dois-je utiliser IPC et bindService() pour appeler cette fonction? Ou est-il une autre méthode pour y parvenir?

Était-ce utile?

La solution

il est vraiment bizarre que la caméra sur la plateforme Android ne peut pas diffuser de la vidéo jusqu'à ce qu'il donne la surface de prévisualisation valide. il semble que les architectes de la plate-forme ne songeais pas sur les applications 3ème streaming vidéo de parti à tous. même pour le cas de la réalité augmentée l'image peut être présenté comme une sorte de substitution visuelle, pas en temps réel flux de caméra.

De toute façon, vous pouvez simplement redimensionner la surface de prévisualisation pixels 1x1 et le mettre quelque part dans le coin du widget (élément visuel). S'il vous plaît faites attention -. redimensionnez surface de prévisualisation, pas la taille du cadre de la caméra

bien sûr cette astuce ne supprime pas le streaming des données indésirables (pour un aperçu) qui consomme des ressources système et batterie.

Autres conseils

J'ai trouvé la réponse à cela dans le Docs de l'appareil photo Android .

  

Note: Il est possible d'utiliser MediaRecorder sans créer une caméra   un aperçu de premier et sauter les premières étapes de ce processus. cependant,   puisque les utilisateurs préfèrent généralement un aperçu avant de commencer un   l'enregistrement, ce processus n'est pas question ici.

Vous trouverez les instructions étape par étape sur le lien ci-dessus. Après les instructions, il indiquera la citation que j'ai fourni ci-dessus.

En fait, il est possible, mais vous devez l'aperçu faux avec un SurfaceView factice

SurfaceView view = new SurfaceView(this);
c.setPreviewDisplay(view.getHolder());
c.startPreview();
c.takePicture(shutterCallback, rawPictureCallback, jpegPictureCallback);

Mise à jour 21/09/11:. Apparemment, cela ne fonctionne pas pour chaque appareil Android

Prendre les photos

Obtenir ce travail avant d'essayer de cacher l'aperçu.

  • Branchez correctement l'aperçu
    • Utilisez un SurfaceView (compatibilité pré-applications-4.0) ou SurfaceTexture (4+ applications, peut être rendue transparente)
    • Définir et initialise avant de prendre la photo
    • Attendez que le SurfaceView du SurfaceHolder (via getHolder()) signaler surfaceCreated() ou TextureView signaler onSurfaceTextureAvailable à son SurfaceTextureListener avant de et initialisant l'aperçu.
  • Assurez-vous de l'aperçu est visible:
    • Ajoutez à WindowManager
    • Assurez-vous de sa taille de mise en page est au moins pixels 1x1 (vous pouvez commencer en rendant MATCH_PARENT x MATCH_PARENT pour les tests)
    • Assurer la visibilité est View.VISIBLE (ce qui semble être la valeur par défaut si vous ne spécifiez pas)
    • Assurez-vous d'utiliser le FLAG_HARDWARE_ACCELERATED dans le LayoutParams si c'est un TextureView.
  • Utilisez la fonction de rappel JPEG de takePicture depuis la documentation indique que les autres ne sont pas pris en charge callbacks sur tous les périphériques

Dépannage

  • Si surfaceCreated / onSurfaceTextureAvailable ne soit pas appelé, le SurfaceView / TextureView est probablement pas affiché.
  • Si takePicture échoue, tout d'abord assurer la prévisualisation fonctionne correctement. Vous pouvez supprimer votre appel takePicture et laisser le preview pour voir si elle affiche à l'écran.
  • Si l'image est plus sombre que ce qu'elle devrait être, vous devrez peut-être retarder pendant environ une seconde avant d'appeler takePicture afin que la caméra a le temps d'ajuster son exposition une fois l'aperçu a commencé.

Hiding l'aperçu

  • Faire la taille View aperçu 1x1 pour minimiser sa visibilité ( ou essayer 8x16 pour éventuellement plus de fiabilité )

    new WindowManager.LayoutParams(1, 1, /*...*/)
    
  • Déplacer l'aperçu hors du centre pour réduire son noticeability:

    new WindowManager.LayoutParams(width, height,
        Integer.MIN_VALUE, Integer.MIN_VALUE, /*...*/)
    
  • Rendre la prévisualisation transparente (ne fonctionne que pour TextureView)

    WindowManager.LayoutParams params = new WindowManager.LayoutParams(
        width, height, /*...*/
        PixelFormat.TRANSPARENT);
    params.alpha = 0;
    

Exemple de travail (testé sur Sony Xperia M, Android 4.3)

/** Takes a single photo on service start. */
public class PhotoTakingService extends Service {

    @Override
    public void onCreate() {
        super.onCreate();
        takePhoto(this);
    }

    @SuppressWarnings("deprecation")
    private static void takePhoto(final Context context) {
        final SurfaceView preview = new SurfaceView(context);
        SurfaceHolder holder = preview.getHolder();
        // deprecated setting, but required on Android versions prior to 3.0
        holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

        holder.addCallback(new Callback() {
            @Override
            //The preview must happen at or after this point or takePicture fails
            public void surfaceCreated(SurfaceHolder holder) {
                showMessage("Surface created");

                Camera camera = null;

                try {
                    camera = Camera.open();
                    showMessage("Opened camera");

                    try {
                        camera.setPreviewDisplay(holder);
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }

                    camera.startPreview();
                    showMessage("Started preview");

                    camera.takePicture(null, null, new PictureCallback() {

                        @Override
                        public void onPictureTaken(byte[] data, Camera camera) {
                            showMessage("Took picture");
                            camera.release();
                        }
                    });
                } catch (Exception e) {
                    if (camera != null)
                        camera.release();
                    throw new RuntimeException(e);
                }
            }

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

        WindowManager wm = (WindowManager)context
            .getSystemService(Context.WINDOW_SERVICE);
        WindowManager.LayoutParams params = new WindowManager.LayoutParams(
                1, 1, //Must be at least 1x1
                WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
                0,
                //Don't know if this is a safe default
                PixelFormat.UNKNOWN);

        //Don't set the preview visibility to GONE or INVISIBLE
        wm.addView(preview, params);
    }

    private static void showMessage(String message) {
        Log.i("Camera", message);
    }

    @Override public IBinder onBind(Intent intent) { return null; }
}

Sur Android 4.0 et supérieur (niveau de l'API> = 14), vous pouvez utiliser TextureView pour afficher un aperçu du flux de caméra et de le rendre invisible afin de ne pas montrer à l'utilisateur. Voici comment:

Tout d'abord créer une classe pour mettre en œuvre un SurfaceTextureListener qui se callbacks créer / mise à jour pour la surface de prévisualisation. Cette classe prend également un objet de la caméra comme entrée, afin qu'il puisse appeler la fonction startPreview de l'appareil dès que la surface est créée:

public class CamPreview extends TextureView implements SurfaceTextureListener {

  private Camera mCamera;

  public CamPreview(Context context, Camera camera) {
    super(context);
    mCamera = camera;
   }

  @Override
  public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
    Camera.Size previewSize = mCamera.getParameters().getPreviewSize();
    setLayoutParams(new FrameLayout.LayoutParams(
        previewSize.width, previewSize.height, Gravity.CENTER));

    try{
      mCamera.setPreviewTexture(surface);
     } catch (IOException t) {}

    mCamera.startPreview();
    this.setVisibility(INVISIBLE); // Make the surface invisible as soon as it is created
  }

  @Override
  public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
      // Put code here to handle texture size change if you want to
  }

  @Override
  public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
    return true;
  }

  @Override
  public void onSurfaceTextureUpdated(SurfaceTexture surface) {
      // Update your view here!
  }
}

Vous aurez également besoin de mettre en œuvre une classe de rappel pour traiter les données d'aperçu:

public class CamCallback implements Camera.PreviewCallback{
  public void onPreviewFrame(byte[] data, Camera camera){
     // Process the camera data here
  }
}

Utilisez les classes CamPreview et CamCallback ci-dessus pour installer la caméra dans onCreate (de votre activité) ou la fonction de démarrage similaire:

// Setup the camera and the preview object
Camera mCamera = Camera.open(0);
CamPreview camPreview = new CamPreview(Context,mCamera);
camPreview.setSurfaceTextureListener(camPreview);

// Connect the preview object to a FrameLayout in your UI
// You'll have to create a FrameLayout object in your UI to place this preview in
FrameLayout preview = (FrameLayout) findViewById(R.id.cameraView); 
preview.addView(camPreview);

// Attach a callback for preview
CamCallback camCallback = new CamCallback();
mCamera.setPreviewCallback(camCallback);

Il y a une façon de le faire, mais il est un peu délicat. ce qui devrait être fait, est attacher un surfaceholder au gestionnaire de fenêtres du service

WindowManager wm = (WindowManager) mCtx.getSystemService(Context.WINDOW_SERVICE);
params = new WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT,
            WindowManager.LayoutParams.WRAP_CONTENT,
            WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
            WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
            PixelFormat.TRANSLUCENT);        
wm.addView(surfaceview, params);

puis définissez

surfaceview.setZOrderOnTop(true);
mHolder.setFormat(PixelFormat.TRANSPARENT);

où mHolder est le titulaire que vous obtenez de la vue de la surface.

De cette façon, vous pouvez jouer avec l'alpha de surfaceview, le rendre transparent complètement, mais l'appareil photo toujours obtenir des cadres.

Voilà comment je le fais. espère que cela aide:)

Nous avons résolu ce problème en utilisant un SurfaceView factice (non ajouté à l'interface graphique réelle) dans les versions 3.0 ci-dessous (ou disons 4.0 en tant que service de la caméra sur une tablette n'a pas vraiment de sens). Dans les versions> = 4.0 cela a fonctionné dans l'émulateur seulement; ( L'utilisation de SurfaceTexture (et setSurfaceTexture ()) au lieu de SurfaceView (et setSurfaceView ()) a travaillé ici. Au moins cela fonctionne sur Nexus S.

Je pense que cela est vraiment une lacune du cadre Android.

Dans le "Exemple de travail par Sam" (Merci Sam ...)

si, à indocations "wm.addView (aperçu, params);"

obtenir exception "Impossible d'ajouter la fenêtre android.view.ViewRoot - autorisation refusée pour ce type de fenêtre"

résolution en utilisant cette autorisation dans AndroidManifest:

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

Vous pouvez essayer ce code de travail, ce service Cliquez sur une photo avant, si vous voulez capturer l'image d'arrière caméra puis backCamera décommenter dans le code et commenter frontCamera.

Remarque: - Autoriser l'appareil photo et l'autorisation de stockage à l'App et startService de l'activité ou partout.

public class MyService extends Service {

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        CapturePhoto();
    }

    private void CapturePhoto() {

        Log.d("kkkk","Preparing to take photo");
        Camera camera = null;

        Camera.CameraInfo cameraInfo = new Camera.CameraInfo();

            int frontCamera = 1;
            //int backCamera=0;

            Camera.getCameraInfo(frontCamera, cameraInfo);

            try {
                camera = Camera.open(frontCamera);
            } catch (RuntimeException e) {
                Log.d("kkkk","Camera not available: " + 1);
                camera = null;
                //e.printStackTrace();
            }
            try {
                if (null == camera) {
                    Log.d("kkkk","Could not get camera instance");
                } else {
                    Log.d("kkkk","Got the camera, creating the dummy surface texture");
                     try {
                         camera.setPreviewTexture(new SurfaceTexture(0));
                        camera.startPreview();
                    } catch (Exception e) {
                        Log.d("kkkk","Could not set the surface preview texture");
                        e.printStackTrace();
                    }
                    camera.takePicture(null, null, new Camera.PictureCallback() {

                        @Override
                        public void onPictureTaken(byte[] data, Camera camera) {
                            File pictureFileDir=new File("/sdcard/CaptureByService");

                            if (!pictureFileDir.exists() && !pictureFileDir.mkdirs()) {
                                pictureFileDir.mkdirs();
                            }
                            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyymmddhhmmss");
                            String date = dateFormat.format(new Date());
                            String photoFile = "ServiceClickedPic_" + "_" + date + ".jpg";
                            String filename = pictureFileDir.getPath() + File.separator + photoFile;
                            File mainPicture = new File(filename);

                            try {
                                FileOutputStream fos = new FileOutputStream(mainPicture);
                                fos.write(data);
                                fos.close();
                                Log.d("kkkk","image saved");
                            } catch (Exception error) {
                                Log.d("kkkk","Image could not be saved");
                            }
                            camera.release();
                        }
                    });
                }
            } catch (Exception e) {
                camera.release();
            }
    }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top