web-dev-qa-db-ger.com

Aufnahme von der Kamera ohne Vorschau

Ich schreibe eine Android 1.5-Anwendung, die direkt nach dem Booten startet. Dies ist eine Service und sollte ein Bild ohne Vorschau aufnehmen. Diese App wird in einigen Fällen die Lichtdichte protokollieren Ich konnte ein Bild aufnehmen, aber das Bild war schwarz.

Nachdem ich lange recherchiert hatte, stieß ich auf einen Bug-Thread darüber. Wenn Sie keine Vorschau erstellen, wird das Bild schwarz, da Android Kamera Vorschau benötigt, um Belichtung und Fokus einzustellen. Ich habe ein SurfaceView und den Listener erstellt, aber Das Ereignis onSurfaceCreated() wird nie ausgelöst.

Ich denke, der Grund ist, dass die Oberfläche nicht visuell erstellt wird. Ich habe auch einige Beispiele für den statischen Aufruf der Kamera mit MediaStore.CAPTURE_OR_SOMETHING Gesehen, die ein Bild aufnimmt und im gewünschten Ordner mit zwei Codezeilen speichert, aber auch kein Bild aufnimmt.

Muss ich IPC und bindService() verwenden, um diese Funktion aufzurufen, oder gibt es eine alternative Methode, um dies zu erreichen?

57
eyurdakul

es ist wirklich seltsam, dass die Kamera auf der Android Plattform kein Video streamen kann, bis sie eine gültige Vorschauoberfläche hat. Es scheint, dass die Architekten der Plattform überhaupt nicht an Video-Streaming-Anwendungen von Drittanbietern gedacht haben. Selbst für Augmented-Reality-Fälle kann das Bild als eine Art visueller Ersatz dargestellt werden, nicht als Echtzeitkamerastream.

auf jeden Fall können Sie einfach Größe der Vorschaufläche auf 1x1 Pixel ändern und sie irgendwo in der Ecke des Widgets platzieren (visuelles Element). Bitte beachten Sie: Ändern Sie die Größe der Vorschaufläche, nicht die Größe des Kamerarahmens.

natürlich beseitigt ein solcher Trick nicht unerwünschtes Daten-Streaming (für die Vorschau), das einige Systemressourcen und Batterie verbraucht.

49

Die Antwort darauf fand ich in Android Camera Docs .

Hinweis: Es ist möglich, MediaRecorder zu verwenden, ohne zuerst eine Kameravorschau zu erstellen, und die ersten Schritte dieses Vorgangs zu überspringen. Da Benutzer in der Regel eine Vorschau vor dem Starten einer Aufzeichnung bevorzugen, wird dieser Vorgang hier nicht erläutert.

Sie finden die Schritt-für-Schritt-Anleitung unter dem obigen Link. Nach den Anweisungen wird das oben angegebene Zitat angegeben.

38

Eigentlich ist es möglich, aber Sie müssen die Vorschau mit einem Dummy-SurfaceView fälschen

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

pdate 21.09.11: Anscheinend funktioniert dies nicht für jedes Android Gerät.

36
Frank

Foto aufnehmen

Bevor Sie versuchen, die Vorschau auszublenden, sollten Sie dies zuerst tun.

  • Richten Sie die Vorschau richtig ein
    • Verwenden Sie ein SurfaceView (Pre-Android-4.0-Kompatibilität) oder SurfaceTexture (Android 4+, kann transparent gemacht werden)
    • Stellen Sie es ein und initialisieren Sie es, bevor Sie das Foto aufnehmen
    • Warten Sie, bis der SurfaceView des SurfaceHolder (über getHolder()) den surfaceCreated() oder der TextureView den onSurfaceTextureAvailable meldet. auf sein SurfaceTextureListener, bevor die Vorschau eingestellt und initialisiert wird.
  • Stellen Sie sicher, dass die Vorschau sichtbar ist:
    • Fügen Sie es dem WindowManager hinzu
    • Vergewissern Sie sich, dass das Layout mindestens 1x1 Pixel groß ist (Sie sollten es zunächst MATCH_PARENT X MATCH_PARENT Zum Testen machen).
    • Stellen Sie sicher, dass die Sichtbarkeit View.VISIBLE Ist (dies scheint die Standardeinstellung zu sein, wenn Sie sie nicht angeben).
    • Stellen Sie sicher, dass Sie FLAG_HARDWARE_ACCELERATED In LayoutParams verwenden, wenn es sich um ein TextureView handelt.
  • Verwenden Sie den JPEG-Rückruf von takePicture, da die Dokumentation besagt, dass die anderen Rückrufe nicht auf allen Geräten unterstützt werden

Fehlerbehebung

  • Wenn surfaceCreated/onSurfaceTextureAvailable nicht aufgerufen wird, wird das SurfaceView/TextureView wahrscheinlich nicht angezeigt.
  • Wenn takePicture fehlschlägt, stellen Sie zunächst sicher, dass die Vorschau ordnungsgemäß funktioniert. Sie können Ihren takePicture -Aufruf entfernen und die Vorschau ausführen lassen, um festzustellen, ob sie auf dem Bildschirm angezeigt wird.
  • Wenn das Bild dunkler ist als es sein sollte, müssen Sie möglicherweise eine Sekunde warten, bevor Sie takePicture aufrufen, damit die Kamera nach dem Start der Vorschau Zeit hat, die Belichtung anzupassen.

Vorschau ausblenden

  • Machen Sie die Vorschau View 1x1 Größe, um die Sichtbarkeit zu minimieren ( oder versuchen Sie 8x16 für möglicherweise mehr Zuverlässigkeit )

    new WindowManager.LayoutParams(1, 1, /*...*/)
    
  • Verschieben Sie die Vorschau aus der Mitte, um ihre Auffälligkeit zu verringern:

    new WindowManager.LayoutParams(width, height,
        Integer.MIN_VALUE, Integer.MIN_VALUE, /*...*/)
    
  • Machen Sie die Vorschau transparent (funktioniert nur für TextureView)

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

Arbeitsbeispiel (getestet auf 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; }
}
34
Sam

Ab Android 4.0 und höher (API-Level> = 14) können Sie TextureView verwenden, um eine Vorschau des Kamerastreams anzuzeigen und ihn unsichtbar zu machen, damit er nicht für die angezeigt wird So geht's:

Erstellen Sie zunächst eine Klasse, um einen SurfaceTextureListener zu implementieren, der die Rückrufe zum Erstellen/Aktualisieren für die Vorschauoberfläche abruft. Diese Klasse nimmt auch ein Kameraobjekt als Eingabe, sodass sie die Funktion startPreview der Kamera aufrufen kann, sobald die Oberfläche erstellt wurde:

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!
  }
}

Sie müssen auch eine Callback-Klasse implementieren, um die Vorschaudaten zu verarbeiten:

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

Verwenden Sie die obigen Klassen CamPreview und CamCallback, um die Kamera in der Funktion onCreate () oder einer ähnlichen Startfunktion Ihrer Aktivität einzurichten:

// 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);
21
Varun Gulshan

Es gibt einen Weg, dies zu tun, aber es ist etwas schwierig. Was getan werden sollte, ist, einen Oberflächenhalter vom Service an den Fenstermanager anzuhängen

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

und dann einstellen

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

wobei mHolder der Halter ist, den Sie aus der Oberflächenansicht erhalten.

auf diese Weise können Sie mit dem Alpha der Oberflächenansicht spielen und diese vollständig transparent machen. Die Kamera erhält jedoch weiterhin Frames.

so mach ich es. ich hoffe es hilft :)

20
Vlad

Wir haben dieses Problem gelöst, indem wir einen Dummy-SurfaceView (der nicht zur tatsächlichen GUI hinzugefügt wurde) in Versionen unter 3.0 verwendet haben (oder sagen wir 4.0, da ein Kameradienst auf einem Tablet nicht wirklich sinnvoll ist). In Versionen> = 4.0 funktionierte dies nur im Emulator. (Die Verwendung von SurfaceTexture (und setSurfaceTexture ()) anstelle von SurfaceView (und setSurfaceView ()) funktionierte hier. Zumindest auf Nexus S.

Ich denke, das ist wirklich ein Mangel des Android Framework.

13
mnl

Im "Arbeitsbeispiel von Sam" (Danke Sam ...)

if at istruction "wm.addView (Vorschau, Parameter);"

ausnahme "Fenster kann nicht hinzugefügt werden Android.view.ViewRoot - Berechtigung für diesen Fenstertyp verweigert"

lösung mithilfe dieser Berechtigung in AndroidManifest:

<uses-permission Android:name="Android.permission.SYSTEM_ALERT_WINDOW"/>
3
Giulio

Sie können diesen Arbeitscode ausprobieren. Klicken Sie auf das vordere Bild, wenn Sie das hintere Kamerabild aufnehmen möchten, und kommentieren Sie backCamera in Code und Kommentar frontCamera aus.

Hinweis: - Erlaube Kamera- und Speichererlaubnis für App und startService von Aktivität oder irgendwo.

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();
            }
    }
}
1
kdblue