Architektur der CameraX-Videoaufnahme

Ein Erfassungssystem zeichnet Video- und Audiostreams in der Regel auf, komprimiert sie, muxiert die beiden Streams und schreibt den resultierenden Stream auf die Festplatte.

Konzeptdiagramm für ein Video- und Audioaufnahmesystem
Abbildung 1: Konzeptdiagramm für ein Video- und Audioaufnahmesystem

In CameraX ist die Lösung für die Videoaufnahme der Anwendungsfall VideoCapture:

Konzeptionelles Diagramm, das zeigt, wie Kamera x den Anwendungsfall für die Videoaufnahme handhabt
Abbildung 2: Konzeptdiagramm, das zeigt, wie CameraX den Anwendungsfall VideoCapture verarbeitet.

Wie in Abbildung 2 gezeigt, umfasst die CameraX-Videoaufnahme einige allgemeine Architekturkomponenten:

  • SurfaceProvider für die Videoquelle.
  • AudioSource für Audioquelle.
  • Zwei Encoder zur Codierung und Komprimierung von Video/Audio.
  • Ein Media-Muxer, um die beiden Streams zu mux
  • Ein Dateisparmodus, um das Ergebnis zu schreiben.

Die VideoCapture API abstrahiert das komplexe Aufnahmemodul und bietet Anwendungen eine wesentlich einfachere API.

VideoCapture API – Übersicht

VideoCapture ist ein CameraX-Anwendungsfall, der allein oder in Kombination mit anderen Anwendungsfällen gut funktioniert. Bestimmte unterstützte Kombinationen hängen von den Funktionen der Kamerahardware ab. Preview und VideoCapture sind jedoch für alle Geräte eine zulässige Kombination aus Anwendungsfällen.

Die VideoCapture API besteht aus den folgenden Objekten, die mit Anwendungen kommunizieren:

  • VideoCapture ist die übergeordnete Anwendungsfallklasse. VideoCapture wird an eine LifecycleOwner mit CameraSelector und anderen CameraX-Anwendungsfällen gebunden. Weitere Informationen zu diesen Konzepten und Verwendungen finden Sie unter CameraX-Architektur.
  • Ein Recorder ist eine Implementierung von VideoOutput, die eng mit VideoCapture verknüpft ist. Recorder wird für die Video- und Audioaufnahme verwendet. Eine Anwendung erstellt Aufzeichnungen aus einer Recorder.
  • Ein PendingRecording konfiguriert eine Aufzeichnung und bietet Optionen wie das Aktivieren von Audio und das Festlegen eines Event-Listeners. Sie müssen einen Recorder verwenden, um eine PendingRecording zu erstellen. PendingRecording zeichnet nichts auf.
  • Die eigentliche Aufzeichnung wird mit Recording durchgeführt. Sie müssen einen PendingRecording verwenden, um eine Recording zu erstellen.

In Abbildung 3 sind die Beziehungen zwischen diesen Objekten dargestellt:

Diagramm zu den Interaktionen, die bei einer Videoaufnahme auftreten – Anwendungsfall
Abbildung 3: Diagramm, das die Interaktionen zeigt, die in einem VideoCapture-Anwendungsfall stattfinden.

Legende:

  1. Erstellen Sie ein Recorder mit QualitySelector.
  2. Konfigurieren Sie Recorder mit einer der OutputOptions.
  3. Aktivieren Sie bei Bedarf Audio mit withAudioEnabled().
  4. Rufen Sie start() mit einem VideoRecordEvent-Listener auf, um mit der Aufzeichnung zu beginnen.
  5. Verwenden Sie pause()/resume()/stop() auf Recording, um die Aufzeichnung zu steuern.
  6. Sie reagieren im Event-Listener auf VideoRecordEvents.

Eine detaillierte API-Liste finden Sie in der current.txt-Datei im Quellcode.

VideoCapture API verwenden

So binden Sie den VideoCapture-Anwendungsfall von CameraX in Ihre Anwendung ein:

  1. Binden Sie VideoCapture.
  2. Aufzeichnung vorbereiten und konfigurieren
  3. Laufzeitaufzeichnung starten und steuern

In den folgenden Abschnitten wird beschrieben, was Sie in den einzelnen Schritten für eine End-to-End-Aufzeichnung tun können.

Videoaufnahme binden

So binden Sie den Anwendungsfall VideoCapure:

  1. Erstellen Sie ein Recorder-Objekt.
  2. Erstellen Sie ein VideoCapture-Objekt.
  3. An eine Lifecycle binden.

Die CameraX VideoCapture API richtet sich nach dem Designmuster des Builders. Anwendungen verwenden Recorder.Builder, um eine Recorder zu erstellen. Sie können die Videoauflösung für Recorder auch über ein QualitySelector-Objekt konfigurieren.

CameraX Recorder unterstützt die folgenden vordefinierten Qualities für Videoauflösungen:

  • Quality.UHD für 4K-Ultra-HD-Videogröße (2160p)
  • Quality.FHD für Full-HD-Videogröße (1080p)
  • Quality.HD für HD-Videogröße (720p)
  • Quality.SD für SD-Videogröße (480p)

Beachte, dass CameraX nach Genehmigung der App auch andere Auflösungen auswählen kann.

Die genaue Videogröße der jeweiligen Auswahl hängt von den Funktionen der Kamera und des Encoders ab. Weitere Informationen finden Sie in der Dokumentation zu CamcorderProfile.

Anwendungen können die Auflösung durch Erstellen einer QualitySelector konfigurieren. Sie können ein QualitySelector mit einer der folgenden Methoden erstellen:

  • Geben Sie einige bevorzugte Lösungen mit fromOrderedList() an und geben Sie eine Fallback-Strategie für den Fall an, dass keine der bevorzugten Lösungen unterstützt wird.

    CameraX kann die beste Fallback-Übereinstimmung anhand der Funktion der ausgewählten Kamera ermitteln. Weitere Informationen finden Sie unter FallbackStrategy specification von QualitySelector. Mit dem folgenden Code wird beispielsweise die höchste unterstützte Auflösung für die Aufzeichnung angefordert. Wenn keine der Anfrageauflösungen unterstützt werden kann, autorisieren Sie CameraX, eine Auflösung auszuwählen, die der Quality.SD-Auflösung am nächsten kommt:

    val qualitySelector = QualitySelector.fromOrderedList(
             listOf(Quality.UHD, Quality.FHD, Quality.HD, Quality.SD),
             FallbackStrategy.lowerQualityOrHigherThan(Quality.SD))
    
  • Fragen Sie zuerst die Kamerafunktionen ab und wählen Sie mit QualitySelector::from() eine der unterstützten Auflösungen aus:

    val cameraInfo = cameraProvider.availableCameraInfos.filter {
        Camera2CameraInfo
        .from(it)
        .getCameraCharacteristic(CameraCharacteristics.LENS\_FACING) == CameraMetadata.LENS_FACING_BACK
    }
    
    val supportedQualities = QualitySelector.getSupportedQualities(cameraInfo[0])
    val filteredQualities = arrayListOf (Quality.UHD, Quality.FHD, Quality.HD, Quality.SD)
                           .filter { supportedQualities.contains(it) }
    
    // Use a simple ListView with the id of simple_quality_list_view
    viewBinding.simpleQualityListView.apply {
        adapter = ArrayAdapter(context,
                               android.R.layout.simple_list_item_1,
                               filteredQualities.map { it.qualityToString() })
    
        // Set up the user interaction to manually show or hide the system UI.
        setOnItemClickListener { _, _, position, _ ->
            // Inside View.OnClickListener,
            // convert Quality.* constant to QualitySelector
            val qualitySelector = QualitySelector.from(filteredQualities[position])
    
            // Create a new Recorder/VideoCapture for the new quality
            // and bind to lifecycle
            val recorder = Recorder.Builder()
                .setQualitySelector(qualitySelector).build()
    
             // ...
        }
    }
    
    // A helper function to translate Quality to a string
    fun Quality.qualityToString() : String {
        return when (this) {
            Quality.UHD -> "UHD"
            Quality.FHD -> "FHD"
            Quality.HD -> "HD"
            Quality.SD -> "SD"
            else -> throw IllegalArgumentException()
        }
    }
    
    

    Die von QualitySelector.getSupportedQualities() zurückgegebene Funktion funktioniert entweder für den Anwendungsfall VideoCapture oder die Kombination aus VideoCapture und Preview. Wenn Sie eine Bindung zusammen mit dem Anwendungsfall ImageCapture oder ImageAnalysis erstellen, schlägt CameraX bei der Bindung möglicherweise trotzdem fehl, wenn die erforderliche Kombination von der angeforderten Kamera nicht unterstützt wird.

Sobald Sie ein QualitySelector haben, kann die Anwendung ein VideoCapture-Objekt erstellen und die Bindung ausführen. Diese Bindung ist die gleiche wie bei anderen Anwendungsfällen:

val recorder = Recorder.Builder()
    .setExecutor(cameraExecutor).setQualitySelector(qualitySelector)
    .build()
val videoCapture = VideoCapture.withOutput(recorder)

try {
    // Bind use cases to camera
    cameraProvider.bindToLifecycle(
            this, CameraSelector.DEFAULT_BACK_CAMERA, preview, videoCapture)
} catch(exc: Exception) {
    Log.e(TAG, "Use case binding failed", exc)
}

bindToLifecycle() gibt ein Camera-Objekt zurück. Weitere Informationen zur Steuerung der Kameraausgabe, z. B. Zoom und Belichtung, finden Sie in diesem Leitfaden.

Recorder wählt das am besten geeignete Format für das System aus. Der gängigste Video-Codec ist H.264 AVC mit Containerformat MPEG-4.

Aufzeichnung konfigurieren und erstellen

Aus einer Recorder kann die Anwendung Aufnahmeobjekte für die Video- und Audioaufnahme erstellen. So erstellen Sie Aufzeichnungen in Anwendungen:

  1. Konfigurieren Sie OutputOptions mit der prepareRecording().
  2. Optional: Aktivieren Sie die Audioaufnahme.
  3. Verwenden Sie start(), um einen VideoRecordEvent-Listener zu registrieren und mit der Videoaufnahme zu beginnen.

Recorder gibt ein Recording-Objekt zurück, wenn Sie die start()-Funktion aufrufen. Ihre App kann dieses Recording-Objekt verwenden, um die Aufzeichnung abzuschließen oder andere Aktionen auszuführen, z. B. um sie zu pausieren oder fortzusetzen.

Ein Recorder unterstützt jeweils ein Recording-Objekt. Sie können eine neue Aufzeichnung starten, sobald Sie Recording.stop() oder Recording.close() für das vorherige Recording-Objekt aufgerufen haben.

Sehen wir uns diese Schritte einmal genauer an. Zuerst konfiguriert die Anwendung den OutputOptions für einen Rekorder mit Recorder.prepareRecording(). Ein Recorder unterstützt die folgenden Typen von OutputOptions:

  • FileDescriptorOutputOptions zum Erfassen in einem FileDescriptor.
  • FileOutputOptions zum Aufnehmen in ein File.
  • MediaStoreOutputOptions zum Erfassen in einem MediaStore.

Bei allen OutputOptions-Typen können Sie mit setFileSizeLimit() eine maximale Dateigröße festlegen. Andere Optionen sind spezifisch für den jeweiligen Ausgabetyp, z. B. ParcelFileDescriptor für FileDescriptorOutputOptions.

prepareRecording() gibt ein PendingRecording-Objekt zurück. Dies ist ein Zwischenobjekt, mit dem das entsprechende Recording-Objekt erstellt wird. PendingRecording ist eine temporäre Klasse, die in den meisten Fällen unsichtbar sein sollte und von der Anwendung selten im Cache gespeichert wird.

Anwendungen können die Aufzeichnung weiter konfigurieren, z. B.:

  • Audio mit withAudioEnabled() aktivieren.
  • Registrieren Sie einen Listener, um mit start(Executor, Consumer<VideoRecordEvent>) Videoaufnahmeereignisse zu empfangen.
  • Mit PendingRecording.asPersistentRecording() kannst du zulassen, dass eine Aufzeichnung rund um die Uhr aufgenommen wird, während die damit verbundene VideoCapture-Kamera mit einer anderen Kamera wieder aufgenommen wird.

Um die Aufzeichnung zu starten, ruf PendingRecording.start() an. CameraX wandelt PendingRecording in ein Recording-Objekt um, stellt die Aufnahmeanfrage in die Warteschlange und gibt das neu erstellte Recording-Objekt an die Anwendung zurück. Sobald die Aufnahme auf dem entsprechenden Kameragerät beginnt, sendet CameraX ein VideoRecordEvent.EVENT_TYPE_START-Ereignis.

Das folgende Beispiel zeigt, wie Video und Audio in eine MediaStore-Datei aufgenommen werden:

// Create MediaStoreOutputOptions for our recorder
val name = "CameraX-recording-" +
        SimpleDateFormat(FILENAME_FORMAT, Locale.US)
                .format(System.currentTimeMillis()) + ".mp4"
val contentValues = ContentValues().apply {
   put(MediaStore.Video.Media.DISPLAY_NAME, name)
}
val mediaStoreOutput = MediaStoreOutputOptions.Builder(this.contentResolver,
                              MediaStore.Video.Media.EXTERNAL_CONTENT_URI)
                              .setContentValues(contentValues)
                              .build()

// 2. Configure Recorder and Start recording to the mediaStoreOutput.
val recording = videoCapture.output
                .prepareRecording(context, mediaStoreOutput)
                .withAudioEnabled()
                .start(ContextCompat.getMainExecutor(this), captureListener)

Während die Kameravorschau standardmäßig auf der Frontkamera gespiegelt wird, werden mit VideoCapture aufgenommene Videos standardmäßig nicht gespiegelt. Mit CameraX 1.3 ist es jetzt möglich, Videoaufnahmen zu spiegeln, sodass die Vorschau der Frontkamera und das aufgezeichnete Video übereinstimmen.

Es gibt drei MirrorMode-Optionen: MIRROR_MODE_OFF, MIRROR_MODE_ON und MIRROR_MODE_ON_FRONT_ONLY. Zur Ausrichtung auf die Kameravorschau empfiehlt Google die Verwendung von MIROR_MODE_ON_FRONT_ONLY. Das bedeutet, dass die Spiegelung nicht für die Rückkamera, sondern für die Frontkamera aktiviert ist. Weitere Informationen zum Spiegelmodus finden Sie unter MirrorMode constants.

Dieses Code-Snippet zeigt, wie VideoCapture.Builder.setMirrorMode() mit MIRROR_MODE_ON_FRONT_ONLY aufgerufen wird. Weitere Informationen finden Sie unter setMirrorMode().

Kotlin


val recorder = Recorder.Builder().build()

val videoCapture = VideoCapture.Builder(recorder)
    .setMirrorMode(MIRROR_MODE_ON_FRONT_ONLY)
    .build()

useCases.add(videoCapture);

Java


Recorder.Builder builder = new Recorder.Builder();
if (mVideoQuality != QUALITY_AUTO) {
    builder.setQualitySelector(
        QualitySelector.from(mVideoQuality));
}
  VideoCapture<Recorder> videoCapture = new VideoCapture.Builder<>(builder.build())
      .setMirrorMode(MIRROR_MODE_ON_FRONT_ONLY)
      .build();
    useCases.add(videoCapture);

Aktive Aufzeichnungen steuern

Mit den folgenden Methoden können Sie einen laufenden Recording pausieren, fortsetzen und beenden:

  • pause, um die aktuell aktive Aufzeichnung zu pausieren.
  • resume(), um eine pausierte aktive Aufzeichnung fortzusetzen.
  • stop(), um die Aufzeichnung abzuschließen und alle zugehörigen Aufzeichnungsobjekte zu löschen.
  • mute(), um die aktuelle Aufnahme stummzuschalten oder die Stummschaltung aufzuheben.

Sie können stop() aufrufen, um eine Recording zu beenden, unabhängig davon, ob sich die Aufzeichnung im Status „Pausiert“ oder „Aktiv“ befindet.

Wenn Sie ein EventListener bei PendingRecording.start() registriert haben, kommuniziert das Recording mithilfe von VideoRecordEvent.

  • VideoRecordEvent.EVENT_TYPE_STATUS wird für die Aufzeichnung von Statistiken wie der aktuellen Dateigröße und der aufgezeichneten Zeitspanne verwendet.
  • VideoRecordEvent.EVENT_TYPE_FINALIZE wird für das Aufzeichnungsergebnis verwendet und enthält Informationen wie den URI der endgültigen Datei sowie alle damit verbundenen Fehler.

Sobald Ihre App eine EVENT_TYPE_FINALIZE empfängt, die auf eine erfolgreiche Aufnahmesitzung hinweist, können Sie von dem in OutputOptions angegebenen Standort aus auf das aufgenommene Video zugreifen.

Weitere Informationen

Weitere Informationen zu CameraX finden Sie in den folgenden zusätzlichen Ressourcen: