Architektura nagrywania filmów w aplikacji CameraX

System przechwytujący zwykle nagrywa, kompresuje i kompresuje strumienie audio i wideo. mapuje oba strumienie, a następnie zapisuje wynikowy strumień na dysku.

schemat koncepcyjny systemu rejestracji dźwięku i obrazu
Rysunek 1. Ogólny schemat obrazu i dźwięku system przechwytywania.

W Aparacie X rozwiązaniem do nagrywania wideo jest VideoCapture przypadek użycia:

schemat koncepcyjny pokazujący, jak aparat X obsługuje
         Przypadek użycia nagrywania wideo
Rysunek 2. Ogólny schemat przedstawiający działanie Aparatu X obsługuje przypadek użycia VideoCapture.

Jak widać na ilustracji 2, nagrywanie wideo w aparacie X obejmuje kilka elementy architektoniczne:

  • SurfaceProvider dla źródła filmu.
  • AudioSource jako źródło dźwięku.
  • Dwa kodery do kodowania i kompresji obrazu i dźwięku.
  • Mukser multimediów do łączenia 2 strumieni.
  • Wygaszacz plików umożliwiający zapisanie wyniku.

Interfejs VideoCapture API wyodrębnia złożony mechanizm przechwytywania i zapewnia do aplikacji za pomocą znacznie prostszego i prostszego interfejsu API.

Omówienie interfejsu VideoCapture API

VideoCapture to przypadek użycia AparatuX, który działa dobrze samodzielnie lub gdy w połączeniu z innymi zastosowaniami. Konkretne obsługiwane kombinacje zależą od aparatów, jednak Preview i VideoCapture to prawidłową kombinację przypadków użycia na wszystkich urządzeniach.

Interfejs VideoCapture API składa się z następujących obiektów, które komunikują się z aplikacjami:

  • VideoCapture to najwyższego poziomu. VideoCapture wiąże się z LifecycleOwner z CameraSelector i innym aparatemX Przypadki użycia. Więcej informacji o tych pojęciach i zastosowaniach znajdziesz tutaj: Architektura CameraX.
  • Recorder to implementacja VideoOutput, która jest ściśle powiązana z funkcją VideoCapture. Recorder służy do nagrywania obrazu i dźwięku. An aplikacja tworzy nagrania z Recorder.
  • PendingRecording konfiguruje nagranie, udostępniając opcje takie jak włączenie dźwięku detektorem zdarzeń. Aby utworzyć PendingRecording, musisz użyć właściwości Recorder. PendingRecording niczego nie nagrywa.
  • Recording wykonuje działanie nagranie. Aby utworzyć Recording, musisz użyć właściwości PendingRecording.

Rysunek 3 pokazuje zależności między tymi obiektami:

diagram przedstawiający interakcje zachodzące w filmie
         przechwytywanie przypadku użycia
Rysunek 3. Diagram przedstawiający zachodzące interakcje w przypadku funkcji VideoCapture.

Legenda:

  1. Utwórz Recorder z: QualitySelector
  2. Skonfiguruj w Recorder jedną z tych opcji: OutputOptions
  3. Włącz dźwięk za pomocą withAudioEnabled() w razie potrzeby.
  4. Zadzwoń pod numer start() z VideoRecordEvent aby rozpocząć nagrywanie.
  5. Użyj pause()/resume()/stop() na Recording aby sterować nagrywaniem.
  6. Odpowiedz na pytanie VideoRecordEvents w detektorze zdarzeń.

Szczegółowa lista interfejsów API znajduje się w pliku current.txt w kodzie źródłowym.

Korzystanie z interfejsu VideoCapture API

Aby zintegrować przypadek użycia VideoCapture CameraX z aplikacją, wykonaj te czynności:

  1. Powiąż VideoCapture.
  2. Przygotuj i skonfiguruj nagrywanie.
  3. Możesz uruchamiać nagrywanie w czasie działania i nimi zarządzać.

W sekcjach poniżej opisano, co można zrobić na każdym etapie, aby uzyskać i całej sesji nagraniowej.

Powiąż przechwytywanie wideo

Aby powiązać przypadek użycia VideoCapure, wykonaj te czynności:

  1. Utwórz obiekt Recorder.
  2. Utwórz obiekt VideoCapture.
  3. Powiąż z elementem Lifecycle.

Interfejs CameraX VideoCapture API zgodny ze wzorcem projektowania kreatora. Aplikacje Użyj narzędzia Recorder.Builder, aby utworzyć Recorder. Możesz też skonfigurować rozdzielczość wideo dla interfejsu Recorder za pomocą obiektu QualitySelector.

Aparat CameraX Recorder obsługuje następujące wstępnie zdefiniowane pliki Qualities dla rozdzielczości wideo:

  • Quality.UHD – film w rozdzielczości 4K Ultra HD (2160p)
  • Quality.FHD – rozmiar filmu w jakości Full HD (1080p)
  • Quality.HD w przypadku filmu w rozmiarze HD (720p)
  • Quality.SD – rozmiar filmu w jakości SD (480p)

Pamiętaj, że CameraX może też wybrać inne rozdzielczości, jeśli zezwoli na to aplikacja.

Dokładny rozmiar filmu w przypadku każdej z wybranych opcji zależy od kamery i kodera funkcje zabezpieczeń. Więcej informacji znajdziesz w dokumentacji CamcorderProfile

Aplikacje mogą skonfigurować rozdzielczość, tworząc QualitySelector QualitySelector możesz utworzyć, korzystając z jednej z tych metod:

  • Podaj kilka preferowanych rozdzielczości za pomocą parametru fromOrderedList(). zawierać strategię zastępczą, którą można zastosować, gdy żaden z obsługiwane są preferowane rozdzielczości.

    Aparat X może wybrać najlepsze dopasowanie kreacji zastępczej na podstawie Więcej informacji znajdziesz w artykule FallbackStrategy specification usługi QualitySelector. . Na przykład poniższy kod żąda największej obsługiwanej liczby rozwiązanie nagrywania. Jeśli nie można zastosować żadnego z tych rozwiązań, autoryzuj urządzenie CameraX do wyboru takiej, która ma rozdzielczość najbliższą jakości.SD:

    val qualitySelector = QualitySelector.fromOrderedList(
             listOf(Quality.UHD, Quality.FHD, Quality.HD, Quality.SD),
             FallbackStrategy.lowerQualityOrHigherThan(Quality.SD))
    
  • Najpierw zapytaj o możliwości kamery, a następnie wybierz jedną z obsługiwanych rozdzielczości w formacie 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()
        }
    }
    

    Zwróć uwagę, że zwrócona możliwość z QualitySelector.getSupportedQualities() na pewno sprawdzi się zarówno w przypadku użycia VideoCapture, jak i kombinacji przypadków użycia VideoCapture i Preview. Podczas wiązania razem z Przypadek użycia ImageCapture lub ImageAnalysis, CameraX powiązanie może nadal nie zostać zrealizowane, jeśli wymagana kombinacja nie jest obsługiwana w żądanej kamery.

Gdy masz QualitySelector, aplikacja może utworzyć VideoCapture obiekt i wykonaj powiązanie. Zwróć uwagę, że to powiązanie jest tak samo jak w innych przypadkach użycia:

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

Pamiętaj, że bindToLifecycle() zwraca obiekt Camera. Więcej informacji o sterowaniu sygnałem wyjściowym aparatu, np. powiększeniem i ekspozycją, znajdziesz w tym przewodniku.

Recorder wybiera najbardziej odpowiedni format dla systemu. Najbardziej Typowy kodek wideo to H.264 AVC) z format kontenera MPEG-4

Skonfiguruj i utwórz nagranie

W Recorder aplikacja może tworzyć obiekty rejestrujące na potrzeby nagrywania obrazu i dźwięku. Aplikacje tworzą nagrania przez wykonywanie następujące:

  1. Skonfiguruj OutputOptions za pomocą: prepareRecording().
  2. (Opcjonalnie) Włącz nagrywanie dźwięku.
  3. Użyj start(), aby zarejestrować VideoRecordEvent. i rozpocząć nagrywanie filmu.

Recorder zwraca obiekt Recording po wywołaniu funkcji start(). Aplikacja może użyć tego obiektu Recording, aby zakończyć przechwytywania lub wykonywania innych działań, np. wstrzymania lub wznowienia.

Recorder obsługuje 1 obiekt Recording naraz. Możesz rozpocząć nowe nagranie, gdy zadzwonisz do: Recording.stop() lub Recording.close() w poprzednim obiekcie Recording.

Przyjrzyjmy się tym krokom bardziej szczegółowo. Najpierw aplikacja konfiguruje się OutputOptions w przypadku Dyktafonu z systemem Recorder.prepareRecording(). Recorder obsługuje te typy OutputOptions:

  • FileDescriptorOutputOptions za przechwytywanie FileDescriptor
  • FileOutputOptions do przechwytywania w File.
  • MediaStoreOutputOptions za przechwytywanie MediaStore

Wszystkie typy OutputOptions umożliwiają ustawienie maksymalnego rozmiaru pliku setFileSizeLimit() Inne opcje zależą od danych wyjściowych typ, na przykład ParcelFileDescriptor dla elementu FileDescriptorOutputOptions.

prepareRecording() zwraca obiekt PendingRecording, który jest obiektu pośredniego, który jest używany do utworzenia Recording obiekt. PendingRecording to klasa przejściowa, która powinna w większości przypadków jest niewidoczny i rzadko jest zapisywana w pamięci podręcznej aplikacji.

Aplikacje mogą dodatkowo skonfigurować nagrywanie, na przykład:

  • Włącz dźwięk za pomocą funkcji withAudioEnabled().
  • Zarejestruj detektor, aby otrzymywać zdarzenia nagrywania wideo dzięki start(Executor, Consumer<VideoRecordEvent>).
  • Zezwól na ciągłe nagrywanie nagrania podczas podłączenia funkcji VideoCapture jest odsyłany do innej kamery, PendingRecording.asPersistentRecording()

Aby rozpocząć nagrywanie, zadzwoń pod numer PendingRecording.start(). AparatX zmienia PendingRecording w Recording, umieszcza żądanie nagrania w kolejce, i zwraca do aplikacji nowo utworzony obiekt Recording. Po rozpoczęciu nagrywania na odpowiednim urządzeniu Aparat X wysyła wiadomość VideoRecordEvent.EVENT_TYPE_START zdarzenie.

Z przykładu poniżej dowiesz się, jak nagrać film i dźwięk w Plik 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)

Podgląd z aparatu jest domyślnie wyświetlany w przednim aparacie, które nie są domyślnie powielane. Dzięki CameraX 1.3 możliwe jest teraz powielanie nagrań wideo, dzięki czemu podgląd dopasowania zarejestrowanego filmu.

Dostępne są trzy opcje trybu lustrzany: MIRROR_MODE_OFF, MIRROR_MODE_ON i MIRROR_MODE_ON_FRONT_ONLY. Aby wyrównać podglądu z aparatu, Google zaleca korzystanie z MIROR_MODE_ON_FRONT_ONLY, co oznacza, że które odbicie lustrzane nie jest włączone w tylnym aparacie, ale jest włączone dla przedniego aparat fotograficzny. Więcej informacji o trybie MirrorMode: MirrorMode constants

Ten fragment kodu pokazuje, jak zadzwonić VideoCapture.Builder.setMirrorMode() za pomocą: MIRROR_MODE_ON_FRONT_ONLY. Dla: więcej informacji: 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));
}
  VideoCaptureR<ecorder >videoCapture = new VideoCapture.Builder(<>builder.build())
      .setMirrorMode(MIRROR_MODE_ON_FRONT_ONLY)
      .build();
    useCases.add(videoCapture);

Sterowanie aktywnym nagraniem

Możesz wstrzymać, wznowić lub zatrzymać trwający Recording do w następujący sposób:

  • pause aby wstrzymać bieżące aktywne nagranie.
  • resume() aby wznowić wstrzymane aktywne nagrywanie.
  • stop() aby zakończyć nagrywanie i usunąć wszystkie powiązane obiekty.
  • mute() aby wyciszyć bieżące nagranie lub wyłączyć jego wyciszenie.

Pamiętaj, że możesz wywołać stop(), by zamknąć Recording niezależnie czy nagrywanie jest wstrzymane czy aktywne.

Jeśli masz zarejestrowane urządzenie EventListener u PendingRecording.start(), Recording komunikuje się za pomocą VideoRecordEvent

  • VideoRecordEvent.EVENT_TYPE_STATUS służy do rejestrowania statystyk takich jak jako bieżącego rozmiaru pliku i zarejestrowanego okresu.
  • Wynik dotyczący nagrywania: VideoRecordEvent.EVENT_TYPE_FINALIZE i zawiera takie informacje, jak identyfikator URI ostatecznego pliku oraz innych błędów.

Gdy aplikacja otrzyma EVENT_TYPE_FINALIZE, który oznacza pomyślne oraz sesja nagrywania, można uzyskać dostęp do nagranego filmu określono w kolumnie OutputOptions.

Dodatkowe materiały

Więcej informacji o aparacie Aparat X znajdziesz w tych dodatkowych materiałach: