System przechwytywania zazwyczaj rejestruje strumienie wideo i audio, kompresuje je, miksuje, a następnie zapisuje wynikowy strumień na dysku.
W aplikacji CameraX rozwiązanie do nagrywania filmów to przypadek użycia VideoCapture
:
Jak widać na Rysunku 2, nagrywanie filmów za pomocą aparatu CameraX obejmuje kilka komponentów architektonicznych wysokiego poziomu:
SurfaceProvider
jako źródło filmu.AudioSource
, aby wskazać źródło dźwięku.- Dwa kodery do kodowania i kompresji obrazu/dźwięku.
- Mikser multimediów do łączenia dwóch strumieni.
- Oszczędzanie plików do zapisania wyniku.
Interfejs VideoCapture API wyodrębnia złożony mechanizm przechwytywania i udostępnia aplikacjom interfejs API o wiele prostszym i czytelniejszym charakterze.
Omówienie interfejsu VideoCapture API
VideoCapture
to przypadek użycia CameraX, który działa samodzielnie lub w połączeniu z innymi przypadkami użycia. Konkretne obsługiwane kombinacje zależą od możliwości sprzętowych kamery, ale Preview
i VideoCapture
to prawidłowe połączenie przypadków użycia na wszystkich urządzeniach.
Interfejs VideoCapture API składa się z tych obiektów, które komunikują się z aplikacjami:
VideoCapture
to klasa przypadku najwyższego poziomu.VideoCapture
wiąże obiektLifecycleOwner
z elementemCameraSelector
i innym przypadkiem użycia CameraX. Więcej informacji o tych zagadnieniach i zastosowaniach znajdziesz w artykule o architekturze CameraX.Recorder
to implementacja Videooutput, która jest ściśle sprzężona zVideoCapture
.Recorder
służy do nagrywania obrazu i dźwięku. Aplikacja tworzy nagrania zRecorder
.PendingRecording
konfiguruje nagranie, udostępniając opcje takie jak włączenie dźwięku i ustawienie odbiornika. Aby utworzyćPendingRecording
, musisz użyćRecorder
.PendingRecording
niczego nie rejestruje.Recording
wykonuje rzeczywiste nagranie. Aby utworzyćRecording
, musisz użyćPendingRecording
.
Rysunek 3 przedstawia zależności między tymi obiektami:
Legenda:
- Utwórz
Recorder
zQualitySelector
. - Skonfiguruj
Recorder
za pomocą jednego z komponentówOutputOptions
. - W razie potrzeby włącz dźwięk w narzędziu
withAudioEnabled()
. - Aby rozpocząć nagrywanie, zadzwoń pod numer
start()
z słuchaczemVideoRecordEvent
. - Użyj
pause()
/resume()
/stop()
naRecording
, aby sterować nagrywaniem. - Odpowiedz w sekcji
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 usługi CameraX VideoCapture
z aplikacją, wykonaj te czynności:
- Powiąż plik
VideoCapture
. - Przygotuj i skonfiguruj nagrywanie.
- Uruchamianie i kontrolowanie nagrywania w czasie działania.
Poniżej opisujemy, co możesz zrobić na poszczególnych etapach, by przeprowadzić kompleksową sesję nagrywania.
Powiąż nagrywanie filmów
Aby powiązać przypadek użycia VideoCapure
, wykonaj te czynności:
- Utwórz obiekt
Recorder
. - Utwórz obiekt
VideoCapture
. - Powiąż z elementem
Lifecycle
.
Interfejs CameraX VideoCapture API jest zgodny z wzorcem konstrukcyjnym. Do tworzenia Recorder
aplikacje wykorzystują Recorder.Builder
. Rozdzielczość wideo dla Recorder
możesz też skonfigurować za pomocą obiektu QualitySelector
.
CameraX Recorder
obsługuje te wstępnie zdefiniowane Qualities
w rozdzielczościach wideo:
Quality.UHD
– film w rozdzielczości 4K ultra HD (2160p)Quality.FHD
– film w rozdzielczości Full HD (1080p)Quality.HD
– rozmiar filmu HD (720p)Quality.SD
– rozmiar filmu w SD (480p)
Pamiętaj, że aplikacja CameraX może też wybrać inne rozdzielczości, jeśli autoryzuje ją aplikacja.
Dokładny rozmiar obrazu w przypadku każdego wybranego materiału zależy od możliwości kamery i kodera. Więcej informacji znajdziesz w dokumentacji CamcorderProfile
.
Aplikacje mogą konfigurować rozdzielczość przez utworzenie QualitySelector
.
QualitySelector
możesz utworzyć, korzystając z jednej z tych metod:
Podaj kilka preferowanych rozwiązań za pomocą funkcji
fromOrderedList()
i dodaj strategię zastępczą, która będzie używana w sytuacji, gdy żadna z nich nie będzie obsługiwana.Aplikacja CameraX może określić najlepsze dopasowanie zastępcze na podstawie możliwości wybranej kamery. Więcej informacji znajdziesz w dokumentacji
QualitySelector
.FallbackStrategy specification
Na przykład ten kod wymaga najwyższej obsługiwanej rozdzielczości nagrywania. Jeśli nie będzie można wykonać żadnej z rozdzielczości żądania, autoryzuj aparat Aparat X do wybrania takiej rozdzielczości, która jest najbliższa rozdzielczości w ramach 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 aparatu, a potem wybierz jedną z obsługiwanych rozdzielczości za pomocą funkcji
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() } }
Pamiętaj, że zwrócona możliwość z funkcji
QualitySelector.getSupportedQualities()
będzie działać zarówno w przypadku użyciaVideoCapture
, jak i kombinacji przypadków użyciaVideoCapture
iPreview
. W przypadku powiązania z przypadkiem użyciaImageCapture
lubImageAnalysis
powiązanie CameraX może się nie powieść, gdy wymagana kombinacja nie jest obsługiwana przez żądaną kamerę.
Gdy masz już QualitySelector
, aplikacja może utworzyć obiekt VideoCapture
i wykonać wiązanie. Zwróć uwagę, że to powiązanie działa 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
. W tym przewodniku znajdziesz więcej informacji o kontrolowaniu wyjścia kamery, np. powiększenia i ekspozycji.
Recorder
wybiera najbardziej odpowiedni format dla systemu. Najczęstszym kodekiem wideo jest H.264 AVC) w formacie kontenera MPEG-4.
Skonfiguruj i utwórz nagranie
Z Recorder
aplikacja może tworzyć obiekty nagrywania służące do nagrywania dźwięku i wideo. Aplikacje tworzą nagrania:
- Skonfiguruj
OutputOptions
za pomocąprepareRecording()
. - (Opcjonalnie) Włącz nagrywanie dźwięku.
- Użyj
start()
, aby zarejestrować słuchaczaVideoRecordEvent
i rozpocząć nagrywanie filmu.
Recorder
zwraca obiekt Recording
podczas wywoływania funkcji start()
.
Aplikacja może używać tego obiektu Recording
do zakończenia przechwytywania lub wykonywania innych działań, takich jak wstrzymywanie lub wznawianie.
Recorder
obsługuje 1 obiekt Recording
naraz. Nowe rejestrowanie możesz rozpocząć po wywołaniu metody Recording.stop()
lub Recording.close()
w poprzednim obiekcie Recording
.
Przyjrzyjmy się im bardziej szczegółowo. Najpierw aplikacja konfiguruje OutputOptions
dla Dyktafonu z Recorder.prepareRecording()
.
Recorder
obsługuje te typy OutputOptions
:
FileDescriptorOutputOptions
do przechwytywania obrazu wFileDescriptor
.FileOutputOptions
– przechwytywanie w elemencieFile
.MediaStoreOutputOptions
do przechwytywania obrazu wMediaStore
.
Wszystkie typy OutputOptions
umożliwiają ustawienie maksymalnego rozmiaru pliku w polu setFileSizeLimit()
. Inne opcje zależą od konkretnego typu danych wyjściowych, np. ParcelFileDescriptor
w polu FileDescriptorOutputOptions
.
prepareRecording()
zwraca obiekt PendingRecording
, który jest obiektem pośrednim służącym do utworzenia odpowiedniego obiektu Recording
. PendingRecording
jest klasą przejściową, która w większości przypadków powinna być niewidoczna i rzadko jest przechowywana w pamięci podręcznej aplikacji.
Aplikacje mogą dodatkowo konfigurować nagrywanie, na przykład:
- Włącz dźwięk na ekranie
withAudioEnabled()
. - Zarejestruj detektor, aby otrzymywać zdarzenia nagrywania wideo za pomocą
start(Executor, Consumer<VideoRecordEvent>)
. - Zezwól na ciągłe nagrywanie nagrania, do którego jest podłączone nagranie wideo, które jest przesyłane do innej kamery za pomocą funkcji
PendingRecording.asPersistentRecording()
.
Aby rozpocząć nagrywanie, zadzwoń pod numer PendingRecording.start()
. CameraX zmienia PendingRecording
w Recording
, umieszcza żądanie nagrywania w kolejce i zwraca nowo utworzony obiekt Recording
do aplikacji.
Gdy rozpocznie się nagrywanie na odpowiednim urządzeniu kamery, CameraX wyśle zdarzenie VideoRecordEvent.EVENT_TYPE_START
.
Ten przykład pokazuje, jak nagrać wideo i dźwięk w pliku 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)
Widok z przedniego aparatu jest domyślnie powielany na podglądzie z aparatu, a filmy nagrane za pomocą funkcji VideoCapture domyślnie nie są powielane. W Aparacie 1.3 można teraz tworzyć odbicie lustrzane nagrań wideo, aby zapewnić zgodność obrazu z przedniego aparatu i nagranego obrazu.
Dostępne są trzy opcje trybu MirrorMode: MIRROR_MODE_OFF, MIRROR_MODE_ON oraz MIRROR_MODE_ON_FRONT_ONLY. Aby dopasować obraz do podglądu z aparatu, Google zaleca użycie funkcji MIROR_MODE_ON_FRONT_ONLY, co oznacza, że odbicie lustrzane jest wyłączone dla tylnego aparatu, ale włączone dla przedniego. Więcej informacji o MirrorMode znajdziesz w artykule MirrorMode constants
.
Ten fragment kodu pokazuje, jak wywołać usługę VideoCapture.Builder.setMirrorMode()
za pomocą MIRROR_MODE_ON_FRONT_ONLY
. 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)); } VideoCapture<Recorder> videoCapture = new VideoCapture.Builder<>(builder.build()) .setMirrorMode(MIRROR_MODE_ON_FRONT_ONLY) .build(); useCases.add(videoCapture);
Sterowanie aktywnym nagraniem
Możesz wstrzymać, wznowić i zatrzymać trwający Recording
, korzystając z tych metod:
pause
, aby wstrzymać bieżące aktywne nagranie.resume()
, aby wznowić wstrzymane aktywne nagranie.stop()
, aby zakończyć nagrywanie i usunąć wszystkie powiązane obiekty nagrywania.mute()
, aby wyciszyć bieżące nagranie lub wyłączyć wyciszenie.
Pamiętaj, że możesz wywołać stop()
, by zakończyć nagrywanie Recording
niezależnie od tego, czy nagrywanie jest wstrzymane czy aktywne.
Jeśli masz zarejestrowany EventListener
w PendingRecording.start()
, Recording
komunikuje się za pomocą VideoRecordEvent
.
VideoRecordEvent.EVENT_TYPE_STATUS
służy do rejestrowania statystyk, takich jak bieżący rozmiar pliku czy zarejestrowany okres.- Parametr
VideoRecordEvent.EVENT_TYPE_FINALIZE
służy do uzyskania wyniku rejestrowania i zawiera informacje takie jak identyfikator URI ostatecznego pliku oraz wszelkie powiązane błędy.
Gdy aplikacja otrzyma sygnał EVENT_TYPE_FINALIZE
oznaczający udaną sesję nagrywania, możesz uzyskać dostęp do nagranego filmu z lokalizacji podanej w polu OutputOptions
.
Dodatkowe materiały
Więcej informacji o aplikacji CameraX znajdziesz w tych dodatkowych materiałach:
- Pierwsze kroki z ćwiczeniami z programowania CameraX
- Przykładowa aplikacja CameraX
- Najnowsza lista interfejsów Video Capture API w Aparacie X
- Informacje o wersji CameraX
- Kod źródłowy CameraX