Architettura di acquisizione video con CameraX

Un sistema di acquisizione generalmente registra gli stream video e audio, li comprime, esegue il mux dei due flussi, quindi scrive il flusso risultante su disco.

diagramma concettuale per un sistema di acquisizione di video e audio
Figura 1. Diagramma concettuale di un video e un audio di acquisizione.

In CameraX, la soluzione per l'acquisizione video è la VideoCapture caso d'uso:

diagramma concettuale che mostra in che modo la fotocamera x gestisce
         caso d'uso acquisizione video
Figura 2. Diagramma concettuale che mostra come CameraX gestisce il caso d'uso di VideoCapture.

Come mostrato nella Figura 2, l'acquisizione video di CameraX include alcune componenti architetturali:

  • SurfaceProvider per l'origine video.
  • AudioSource per la sorgente audio.
  • Due codificatori per codificare e comprimere i video e l'audio.
  • Un media muxer per sincronizzare i due stream.
  • Un salvaschermo per scrivere il risultato.

L'API Video Capture astrae il complesso motore di acquisizione e fornisce con un'API molto più semplice e diretta.

Panoramica dell'API Video Capture

VideoCapture è un caso d'uso di CameraX che funziona bene da solo o quando in combinazione con altri casi d'uso. Le specifiche combinazioni supportate dipendono funzionalità hardware della videocamera, ma Preview e VideoCapture sono una combinazione di casi d'uso valida su tutti i dispositivi.

L'API Video Capture è costituita dai seguenti oggetti che comunicano con applicazioni:

  • VideoCapture è il di casi d'uso di primo livello. VideoCapture si associa a LifecycleOwner con CameraSelector e un'altra fotocameraX Casi d'uso. Per ulteriori informazioni su questi concetti e utilizzi, vedi Architettura di CameraX.
  • Un Recorder è un implementazione di VideoOutput strettamente associata a VideoCapture. Recorder viene utilizzato per eseguire l'acquisizione video e audio. Un l'applicazione crea registrazioni da un Recorder.
  • Un PendingRecording configura una registrazione, offrendo opzioni quali l'attivazione dell'audio e l'impostazione un listener di eventi. Devi utilizzare un Recorder per creare un PendingRecording. Un PendingRecording non registra nulla.
  • Un'istruzione Recording esegue e la registrazione effettiva. Devi utilizzare un PendingRecording per creare un Recording.

La figura 3 mostra le relazioni tra questi oggetti:

diagramma che mostra le interazioni che si verificano in un video
         acquisizione del caso d'uso
Figura 3. Diagramma che mostra le interazioni che si verificano in un caso d'uso di Video Capture.

Legenda:

  1. Crea una Recorder con QualitySelector.
  2. Configura Recorder con uno dei OutputOptions
  3. Attiva l'audio con withAudioEnabled() se necessario.
  4. Chiama il numero start() con VideoRecordEvent per avviare la registrazione.
  5. Usa pause()/resume()/stop() nella Recording per controllare la registrazione.
  6. Rispondi a VideoRecordEvents all'interno del listener di eventi.

L'elenco dettagliato delle API si trova nel file current.txt all'interno del codice sorgente.

Utilizzo dell'API Video Capture

Per integrare il caso d'uso di CameraX VideoCapture nella tua app: procedi nel seguente modo:

  1. Associa VideoCapture.
  2. Prepara e configura la registrazione.
  3. Avvia e controlla la registrazione di runtime.

Le seguenti sezioni descrivono cosa puoi fare in ogni passaggio per ottenere di registrazione end-to-end.

Associa acquisizione video

Per associare il caso d'uso VideoCapure:

  1. Crea un oggetto Recorder.
  2. Crea oggetto VideoCapture.
  3. Associa a Lifecycle.

L'API CameraX Video Capture segue il pattern di progettazione del builder. Applicazioni usa Recorder.Builder per creare Recorder. Puoi anche configurare risoluzione video per Recorder tramite un oggetto QualitySelector.

CameraX Recorder supporta i seguenti valori predefiniti di Qualities per le risoluzioni video:

  • Quality.UHD per dimensioni video ultra HD 4K (2160p)
  • Quality.FHD per dimensioni video Full HD (1080p)
  • Quality.HD per dimensioni video HD (720p)
  • Quality.SD per dimensioni video SD (480p)

Tieni presente che CameraX può anche scegliere altre risoluzioni, se autorizzata dall'app.

Le dimensioni esatte del video per ogni selezione dipendono dalla videocamera e dal codificatore le funzionalità di machine learning. Per ulteriori informazioni, consulta la documentazione su CamcorderProfile

Le applicazioni possono configurare la risoluzione creando QualitySelector Puoi creare un QualitySelector utilizzando uno dei seguenti metodi:

  • Fornisci alcune risoluzioni preferite utilizzando fromOrderedList() e includi una strategia di riserva da usare nel caso in cui nessuno dei le risoluzioni preferite sono supportate.

    CameraX può decidere la migliore corrispondenza di riserva in base alle consulta la sezione FallbackStrategy specification di QualitySelector per ulteriori dettagli. Ad esempio, il seguente codice richiede il livello massimo supportato risoluzione del problema per la registrazione e, se nessuna delle opzioni delle richieste è supportata, autorizzi CameraX a scegliere quella che più si avvicina alla qualità.Risoluzione SD:

    val qualitySelector = QualitySelector.fromOrderedList(
             listOf(Quality.UHD, Quality.FHD, Quality.HD, Quality.SD),
             FallbackStrategy.lowerQualityOrHigherThan(Quality.SD))
    
  • Innanzitutto, esegui una query sulle funzionalità della videocamera e scegli una delle funzionalità supportate risoluzioni utilizzando QualitySelector::from():

    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()
        }
    }
    
    

    Tieni presente che la funzionalità restituita QualitySelector.getSupportedQualities() il suo funzionamento è garantito per il caso d'uso VideoCapture o combinazione di casi d'uso di VideoCapture e Preview. Quando si associano Caso d'uso di ImageCapture o ImageAnalysis, FotocameraX l'associazione potrebbe comunque non riuscire quando la combinazione richiesta non è supportata la videocamera richiesta.

Una volta che disponi di un QualitySelector, l'applicazione può creare un VideoCapture ed eseguire l'associazione. Tieni presente che questa associazione come per altri casi d'uso:

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

Tieni presente che bindToLifecycle() restituisce un oggetto Camera. Consulta questa guida per ulteriori informazioni su come controllare l'output della fotocamera, ad esempio lo zoom e l'esposizione.

L'Recorder seleziona il formato più adatto al sistema. Il più il codec video più comune è H.264 AVC) con formato container MPEG-4.

Configura e crea registrazione

Da un Recorder, l'applicazione può creare oggetti di registrazione su eseguire l'acquisizione video e audio. Le applicazioni creano registrazioni le seguenti:

  1. Configura OutputOptions con il prepareRecording().
  2. (Facoltativo) Attiva la registrazione audio.
  3. Usa start() per registrare un VideoRecordEvent ascoltatore e avvia l'acquisizione del video.

Recorder restituisce un oggetto Recording quando chiami la funzione start(). La tua applicazione può utilizzare questo oggetto Recording per completare l'operazione acquisire o eseguire altre azioni, come la messa in pausa o la ripresa.

Un'Recorder supporta un oggetto Recording alla volta. Puoi avviare una nuova registrazione dopo che hai chiamato Recording.stop() o Recording.close() sull'oggetto Recording precedente.

Diamo un'occhiata a questi passaggi in modo più dettagliato. Innanzitutto, l'applicazione configura OutputOptions per un Registratore con Recorder.prepareRecording(). Un Recorder supporta i seguenti tipi di OutputOptions:

  • FileDescriptorOutputOptions per acquisire FileDescriptor
  • FileOutputOptions per le acquisizioni in File.
  • MediaStoreOutputOptions per acquisire MediaStore

Tutti i tipi di OutputOptions consentono di impostare le dimensioni massime dei file con setFileSizeLimit(). Altre opzioni sono specifiche per il singolo output ad esempio ParcelFileDescriptor per FileDescriptorOutputOptions.

prepareRecording() restituisce un oggetto PendingRecording, che è un oggetto intermedio utilizzato per creare l'oggetto Recording oggetto. PendingRecording è una classe temporanea che dovrebbe sono invisibili nella maggior parte dei casi e raramente vengono memorizzate nella cache dall'app.

Le applicazioni possono configurare ulteriormente la registrazione, ad esempio:

  • Attiva l'audio con withAudioEnabled().
  • Registrare un listener per ricevere eventi di registrazione video con start(Executor, Consumer<VideoRecordEvent>).
  • Consenti la registrazione continua di una registrazione mentre Video Capture è collegato a un'altra fotocamera, con PendingRecording.asPersistentRecording().

Per avviare la registrazione, chiama il numero PendingRecording.start(). CameraX trasforma PendingRecording in una Recording, mette in coda la richiesta di registrazione, e restituisce all'applicazione l'oggetto Recording appena creato. Quando la registrazione inizia sul dispositivo Fotocamera corrispondente, CameraX invia un VideoRecordEvent.EVENT_TYPE_START evento.

L'esempio seguente mostra come registrare video e audio in un File MediaStore:

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

Mentre l'anteprima della fotocamera viene speculare sulla fotocamera anteriore per impostazione predefinita, i video registrate da Video Capture non vengono sottoposte a mirroring per impostazione predefinita. Con CameraX 1.3, ora è possibile eseguire il mirroring delle registrazioni video in modo che l'anteprima della fotocamera anteriore corrispondenza video registrata.

Sono disponibili tre opzioni MirrorMode: MIRROR_MODE_OFF, MIRROR_MODE_ON e MIRROR_MODE_ON_FRONT_ONLY. Per allinearsi al l'anteprima della fotocamera Google consiglia di utilizzare MIROR_MODE_ON_FRONT_ONLY, che Il mirroring non è abilitato per la fotocamera posteriore, ma è abilitato per la fotocamera anteriore fotocamera. Per ulteriori informazioni su MirrorMode, vedi MirrorMode constants

Questo snippet di codice mostra come chiamare VideoCapture.Builder.setMirrorMode() con MIRROR_MODE_ON_FRONT_ONLY. Per Per ulteriori informazioni, consulta 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);

Controllare una registrazione attiva

Puoi mettere in pausa, riprendere e interrompere un Recording in corso utilizzando i seguenti metodi:

  • pause per mettere in pausa la registrazione attiva attuale.
  • resume() per riprendere una registrazione attiva in pausa.
  • stop() per terminare la registrazione e fare il flush di eventuali oggetti registrati associati.
  • mute() per disattivare o riattivare l'audio della registrazione corrente.

Tieni presente che puoi chiamare stop() per terminare una Recording indipendentemente che indica se la registrazione è in pausa o attiva.

Se hai registrato EventListener con PendingRecording.start(), Recording comunica utilizzando un VideoRecordEvent.

  • VideoRecordEvent.EVENT_TYPE_STATUS viene utilizzato per la registrazione di statistiche come come dimensione del file corrente e intervallo di tempo registrato.
  • Viene usato VideoRecordEvent.EVENT_TYPE_FINALIZE per il risultato della registrazione e include informazioni quali l'URI del file finale, eventuali errori correlati.

Quando la tua app riceve un EVENT_TYPE_FINALIZE che indica un esito positivo di registrazione, potrai accedere al video acquisito dalla località specificato in OutputOptions.

Risorse aggiuntive

Per saperne di più su CameraX, consulta le seguenti risorse aggiuntive: