Sitzungen und Anfragen für Kameraaufnahmen

Hinweis:Diese Seite bezieht sich auf das Camera2-Paket. Sofern für Ihre App keine spezifischen Low-Level-Funktionen von Camera2 erforderlich sind, empfehlen wir die Verwendung von CameraX. Sowohl CameraX als auch Camera2 unterstützen Android 5.0 (API-Level 21) und höher.

Ein Android-Gerät kann mehrere Kameras haben. Jede Kamera ist ein CameraDevice, und ein CameraDevice kann mehr als einen Stream gleichzeitig ausgeben.

Ein Grund dafür ist, dass ein Stream, die sequenzielle Kameraaufnahme aus einem CameraDevice, für eine bestimmte Aufgabe wie das Anzeigen von im Sucher, während andere vielleicht für Fotos oder Videos verwendet werden. Die Streams fungieren als parallele Pipelines, die Rohframes verarbeiten. Bild für Bild heraus.

<ph type="x-smartling-placeholder">
</ph>
Abbildung 1. Illustration zur Entwicklung einer universellen Kamera-App (Google I/O 2018)

Die parallele Verarbeitung deutet darauf hin, dass je nach die verfügbare Prozessorleistung von CPU, GPU oder einem anderen Prozessor. Wenn ein mit den eingehenden Frames nicht Schritt halten kann, sie verwirft sie.

Jede Pipeline hat ihr eigenes Ausgabeformat. Die eingehenden Rohdaten automatisch in den entsprechenden Ausgabeformat durch implizite Logik die mit jeder Pipeline verknüpft sind. Das CameraDevice, das im gesamten Codebeispiele sind unspezifisch, daher müssen Sie zuerst eine Aufzählung alle verfügbaren Kameras, bevor Sie fortfahren.

Sie können die CameraDevice verwenden, um ein CameraCaptureSession, die für CameraDevice spezifisch ist. Ein CameraDevice muss Folgendes erhalten: Frame-Konfiguration für jeden Roh-Frame mithilfe von CameraCaptureSession. Die Konfiguration Kameraattribute wie Autofokus, Blende, Effekte, und Präsenz. Aufgrund von Hardwareeinschränkungen kann nur eine Konfiguration zu einem bestimmten Zeitpunkt im Kamerasensor aktiv sein. active konfiguriert.

Anwendungsfälle für Streams erweitern und erweitern jedoch die bisherigen Möglichkeiten, CameraDevice zu nutzen. um Aufnahmen zu streamen. So können Sie den Kamerastream für Ihre für einen bestimmten Anwendungsfall. Beispielsweise kann die Akkulaufzeit bei der Optimierung Videoanrufen.

Ein CameraCaptureSession beschreibt alle möglichen Pipelines, die an die CameraDevice. Wenn eine Sitzung erstellt wird, können Sie keine Pipelines hinzufügen oder entfernen. CameraCaptureSession verwaltet eine Warteschlange mit CaptureRequeste, die zur aktiven Konfiguration werden.

Ein CaptureRequest fügt der Warteschlange eine Konfiguration hinzu und wählt eine Konfiguration aus, oder alle verfügbaren Pipelines zum Empfangen eines Frames vom CameraDevice. Während der Aufnahme können viele Aufnahmeanfragen gesendet werden Sitzung. Jede Anfrage kann die aktive Konfiguration und die Ausgabedaten ändern. Pipelines, die das Roh-Image empfangen.

Verwenden Sie Stream-Anwendungsfälle für eine bessere Leistung

Anwendungsfälle für Streams sind eine Möglichkeit, die Leistung von Camera2-Aufnahmen zu verbessern Sitzungen. Sie liefern dem Hardwaregerät mehr Informationen zum Abstimmen von Parametern, Das sorgt für ein besseres Kameraerlebnis bei bestimmten Aufgaben.

Dieses ermöglicht es dem Kameragerät, die Kamerahardware- und -software-Pipelines zu optimieren basierend auf Nutzerszenarien für jeden Stream. Weitere Informationen zur Streamnutzung Weitere Informationen zu Anfragen finden Sie unter setStreamUseCase.

Mithilfe von Stream-Anwendungsfällen können Sie festlegen, wie ein bestimmter Kamerastream in einem bestimmten im Detail und über das Festlegen einer Vorlage CameraDevice.createCaptureRequest() So kann die Kamerahardware wie Feinabstimmung, Sensormodus oder Kamerasensoreinstellungen, bei bestimmten Anwendungsfällen geeignete Kompromisse bei Qualität oder Latenz eingehen.

Anwendungsfälle für Streams:

  • DEFAULT: Deckt das gesamte vorhandene Anwendungsverhalten ab. Das ist gleichbedeutend mit Anwendungsfälle für Streams festlegen.

  • PREVIEW: Empfohlen für den Sucher oder die Bildanalyse in der App.

  • STILL_CAPTURE: Optimiert für Aufnahmen mit hoher Qualität und hoher Auflösung. wird voraussichtlich vorschauähnliche Frame-Rates haben.

  • VIDEO_RECORD: Optimiert für Videoaufnahmen in hoher Qualität, einschließlich hoher Qualität Bildstabilisierung, falls dies vom Gerät unterstützt und von der Anwendung aktiviert wird Diese Option kann Ausgabe-Frames mit einer erheblichen Verzögerung gegenüber Echtzeit erzeugen, um eine optimale Stabilisierung oder eine andere Verarbeitung zu ermöglichen.

  • VIDEO_CALL: Empfohlen für lang andauernde Kameras mit hohem Stromverbrauch. bedenklich.

  • PREVIEW_VIDEO_STILL: Empfohlen für Social-Media-Apps oder die Verwendung in einem einzelnen Stream Cases. Es ist ein Mehrzweck-Stream.

  • VENDOR_START: Wird für vom OEM definierte Anwendungsfälle verwendet.

CameraCaptureSession erstellen

Stellen Sie einen oder mehrere Ausgabezwischenspeicher bereit, um eine Kamerasitzung zu erstellen in die Ihre App Ausgabe-Frames schreiben kann. Jeder Zwischenspeicher stellt eine Pipeline dar. Du musst bevor Sie die Kamera verwenden, damit das Framework an die internen Pipelines des Geräts und weist Arbeitsspeicherpuffer für das Senden von Frames zu erforderlichen Ausgabeziele an.

Das folgende Code-Snippet zeigt, wie Sie eine Kamerasitzung mit zwei für Ausgabepuffer, von denen einer zu einer SurfaceView und einem anderen in ein ImageReader Anwendungsfall PREVIEW Stream wird previewSurface und Nutzung des Streams für STILL_CAPTURE Mit Case to imReaderSurface optimiert die Gerätehardware diese Streams sogar weiter ausbauen.

Kotlin


// Retrieve the target surfaces, which might be coming from a number of places:
// 1. SurfaceView, if you want to display the image directly to the user
// 2. ImageReader, if you want to read each frame or perform frame-by-frame
// analysis
// 3. OpenGL Texture or TextureView, although discouraged for maintainability
      reasons
// 4. RenderScript.Allocation, if you want to do parallel processing
val surfaceView = findViewById<SurfaceView>(...)
val imageReader = ImageReader.newInstance(...)

// Remember to call this only *after* SurfaceHolder.Callback.surfaceCreated()
val previewSurface = surfaceView.holder.surface
val imReaderSurface = imageReader.surface
val targets = listOf(previewSurface, imReaderSurface)

// Create a capture session using the predefined targets; this also involves
// defining the session state callback to be notified of when the session is
// ready
// Setup Stream Use Case while setting up your Output Configuration.
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
fun configureSession(device: CameraDevice, targets: List<Surface>){
    val configs = mutableListOf<OutputConfiguration>()
    val streamUseCase = CameraMetadata
        .SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW_VIDEO_STILL

    targets.forEach {
        val config = OutputConfiguration(it)
        config.streamUseCase = streamUseCase.toLong()
        configs.add(config)
    }
    ...
    device.createCaptureSession(session)
}

Java


// Retrieve the target surfaces, which might be coming from a number of places:
// 1. SurfaceView, if you want to display the image directly to the user
// 2. ImageReader, if you want to read each frame or perform frame-by-frame
      analysis
// 3. RenderScript.Allocation, if you want to do parallel processing
// 4. OpenGL Texture or TextureView, although discouraged for maintainability
      reasons
Surface surfaceView = findViewById<SurfaceView>(...);
ImageReader imageReader = ImageReader.newInstance(...);

// Remember to call this only *after* SurfaceHolder.Callback.surfaceCreated()
Surface previewSurface = surfaceView.getHolder().getSurface();
Surface imageSurface = imageReader.getSurface();
List<Surface> targets = Arrays.asList(previewSurface, imageSurface);

// Create a capture session using the predefined targets; this also involves defining the
// session state callback to be notified of when the session is ready
private void configureSession(CameraDevice device, List<Surface> targets){
    ArrayList<OutputConfiguration> configs= new ArrayList()
    String streamUseCase=  CameraMetadata
        .SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW_VIDEO_STILL

    for(Surface s : targets){
        OutputConfiguration config = new OutputConfiguration(s)
        config.setStreamUseCase(String.toLong(streamUseCase))
        configs.add(config)
}

device.createCaptureSession(session)
}

Du hast die aktive Konfiguration der Kamera noch nicht definiert. Wenn die Sitzung konfiguriert ist, können Sie Erfassungen erstellen und senden um dies zu tun.

Die Transformation, die auf Eingaben angewendet wird, wenn diese in ihren Zwischenspeicher geschrieben werden, ist die vom jeweiligen Zieltyp bestimmt werden. Dieser muss ein Surface Das Android-Framework weiß, ein Rohbild in der aktiven Konfiguration in ein Format konvertieren, das für für jedes Ziel. Die Conversion wird durch das Pixelformat und die Größe des insbesondere Surface.

Das Framework versucht, sein Bestes zu geben, aber einige Surface Konfigurationskombinationen funktionieren möglicherweise nicht, was zu Problemen wie der Sitzung nicht erstellt wird, beim Absenden einer Anfrage ein Laufzeitfehler ausgegeben wird, oder Leistungseinbußen. Das Framework garantiert bestimmte Kombinationen von Geräte-, Oberflächen- und Anfrageparametern. Die Dokumentation für createCaptureSession() erhalten Sie weitere Informationen.

Einzelne Erfassungsanfragen

Die für jeden Frame verwendete Konfiguration ist in einem CaptureRequest codiert, der an die Kamera gesendet. Um eine Aufnahmeanfrage zu erstellen, können Sie eine der vordefinierte Vorlagen, oder TEMPLATE_MANUAL für die vollständige Kontrolle verwenden. Wenn Sie eine müssen Sie einen oder mehrere Ausgabepuffer zur Verwendung mit der Anfrage. Sie können nur Zwischenspeicher verwenden, die bereits für die Erfassung definiert wurden Sitzung, die Sie verwenden möchten.

Bei Erfassungsanfragen wird ein Builder-Muster und geben Entwicklern die Möglichkeit, viele verschiedene Optionen, darunter automatische Belichtung Autofokus und Blende. Vergewissern Sie sich vor dem Festlegen eines Felds, dass die entsprechende Option für das Gerät durch einen Anruf CameraCharacteristics.getAvailableCaptureRequestKeys() und ob der gewünschte Wert durch Aktivieren der entsprechenden Kamera unterstützt wird. wie etwa die verfügbare Funktion für die automatische Belichtung Modi.

So erstellen Sie mithilfe der Vorlage eine Erfassungsanfrage für ein SurfaceView: die ohne Änderungen für eine Vorschau vorgesehen sind, CameraDevice.TEMPLATE_PREVIEW:

Kotlin

val session: CameraCaptureSession = ...  // from CameraCaptureSession.StateCallback
val captureRequest = session.device.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)
captureRequest.addTarget(previewSurface)

Java

CameraCaptureSession session = ...;  // from CameraCaptureSession.StateCallback
CaptureRequest.Builder captureRequest =
    session.getDevice().createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
captureRequest.addTarget(previewSurface);

Wenn eine Erfassungsanfrage definiert ist, können Sie jetzt Weiterleitungen an die Kamerasitzung senden:

Kotlin

val session: CameraCaptureSession = ...  // from CameraCaptureSession.StateCallback
val captureRequest: CaptureRequest = ...  // from CameraDevice.createCaptureRequest()

// The first null argument corresponds to the capture callback, which you
// provide if you want to retrieve frame metadata or keep track of failed capture
// requests that can indicate dropped frames; the second null argument
// corresponds to the Handler used by the asynchronous callback, which falls
// back to the current thread's looper if null
session.capture(captureRequest.build(), null, null)

Java

CameraCaptureSession session = ...;  // from CameraCaptureSession.StateCallback
CaptureRequest captureRequest = ...;  // from CameraDevice.createCaptureRequest()

// The first null argument corresponds to the capture callback, which you
// provide if you want to retrieve frame metadata or keep track of failed
// capture
// requests that can indicate dropped frames; the second null argument
// corresponds to the Handler used by the asynchronous callback, which falls
// back to the current thread's looper if null
session.capture(captureRequest.build(), null, null);

Wenn ein Ausgabe-Frame in einen spezifischen Zwischenspeicher gestellt wird, erscheint eine Aufnahme Callback ausgelöst wird. In vielen Fällen werden zusätzliche Callbacks, wie z. B. ImageReader.OnImageAvailableListener, wird ausgelöst, wenn der darin enthaltene Frame verarbeitet wird. Ort können Sie Bilddaten aus dem angegebenen Zwischenspeicher abrufen.

CaptureRequests wiederholen

Anfragen für eine einzelne Kamera sind einfach, aber für die Anzeige eines Vorschau oder Video, sind sie nicht sehr nützlich. In diesem Fall müssen Sie eine kontinuierlichen Stream von Frames, nicht nur von einem einzelnen. Das folgende Code-Snippet zeigt, wie Sie ein wiederholte Anfrage zum Meeting hinzufügen:

Kotlin

val session: CameraCaptureSession = ...  // from CameraCaptureSession.StateCallback
val captureRequest: CaptureRequest = ...  // from CameraDevice.createCaptureRequest()

// This keeps sending the capture request as frequently as possible until
// the
// session is torn down or session.stopRepeating() is called
// session.setRepeatingRequest(captureRequest.build(), null, null)

Java

CameraCaptureSession session = ...;  // from CameraCaptureSession.StateCallback
CaptureRequest captureRequest = ...;  // from CameraDevice.createCaptureRequest()

// This keeps sending the capture request as frequently as possible until the
// session is torn down or session.stopRepeating() is called
// session.setRepeatingRequest(captureRequest.build(), null, null);

Bei einer sich wiederholenden Aufnahmeanforderung nimmt die Kamera kontinuierlich Aufnahmen auf. mit den Einstellungen in den angegebenen CaptureRequest. Die Camera2 API können Nutzer auch Videos mit der Kamera aufnehmen, indem sie CaptureRequests wird wiederholt, wie hier zu sehen Kamera2-Beispiel auf GitHub. Videos in Zeitlupe können auch gerendert werden, indem ein Hochgeschwindigkeitsvideo (Zeitlupe) mit sich wiederholender Bilderfolge CaptureRequests wie in der Beispiel-App in Zeitlupe von Camera2 gezeigt. auf GitHub.

CaptureRequests verschachteln

Um eine zweite Aufnahmeanfrage zu senden, während die wiederkehrende Erfassungsanfrage aktiv ist, etwa um einen Sucher anzuzeigen und Nutzern die Möglichkeit zu bieten, ein Foto aufzunehmen, müssen Sie sich die laufende wiederholte Anfrage anzuhalten. Sie führen stattdessen eine sich nicht wiederholende Aufnahme aus. -Anforderung, während die wiederkehrende Anfrage weiter ausgeführt wird.

Jeder verwendete Ausgabezwischenspeicher muss als Teil der Kamerasitzung konfiguriert werden wenn die Sitzung neu erstellt wird. Wiederkehrende Anfragen haben eine niedrigere Priorität als Einzel-Frame- oder Burst-Anfragen, mit denen folgendes Beispiel funktioniert:

Kotlin

val session: CameraCaptureSession = ...  // from CameraCaptureSession.StateCallback

// Create the repeating request and dispatch it
val repeatingRequest = session.device.createCaptureRequest(
CameraDevice.TEMPLATE_PREVIEW)
repeatingRequest.addTarget(previewSurface)
session.setRepeatingRequest(repeatingRequest.build(), null, null)

// Some time later...

// Create the single request and dispatch it
// NOTE: This can disrupt the ongoing repeating request momentarily
val singleRequest = session.device.createCaptureRequest(
CameraDevice.TEMPLATE_STILL_CAPTURE)
singleRequest.addTarget(imReaderSurface)
session.capture(singleRequest.build(), null, null)

Java

CameraCaptureSession session = ...;  // from CameraCaptureSession.StateCallback

// Create the repeating request and dispatch it
CaptureRequest.Builder repeatingRequest =
session.getDevice().createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
repeatingRequest.addTarget(previewSurface);
session.setRepeatingRequest(repeatingRequest.build(), null, null);

// Some time later...

// Create the single request and dispatch it
// NOTE: This can disrupt the ongoing repeating request momentarily
CaptureRequest.Builder singleRequest =
session.getDevice().createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
singleRequest.addTarget(imReaderSurface);
session.capture(singleRequest.build(), null, null);

Dieser Ansatz hat jedoch auch einen Nachteil: Sie wissen nicht genau, wann die einzelne Anfrage erfolgt. Wenn in der folgenden Abbildung A die sich wiederholende und B die Einzelframe-Erfassungsanfrage. -Sitzung verarbeitet die Anfragewarteschlange:

<ph type="x-smartling-placeholder">
</ph>
Abbildung 2. Abbildung einer Anfragewarteschlange für die laufende Kamerasitzung

Es gibt keine Garantien für die Latenz zwischen der letzten sich wiederholenden Anfrage von A, bevor Anfrage B aktiviert wird und A das nächste Mal verwendet wird kann es passieren, dass einige Frames übersprungen werden. Es gibt einige Dinge, können Sie dieses Problem beheben:

  • Fügen Sie die Ausgabeziele aus Anfrage A zu Anfrage B hinzu. Auf diese Weise B-Frame bereit ist, wird er in die Ausgabeziele von A kopiert. Dies ist beispielsweise wichtig, wenn Video-Snapshots erstellt werden, um die Framerate stabilisieren. Im obigen Code fügen Sie singleRequest.addTarget(previewSurface), bevor Sie die Anfrage erstellen.

  • Verwenden Sie eine Kombination aus Vorlagen, die sich für dieses spezielle Szenario eignen, wie z. B. ohne Verzögerung.