System rejestrujący zwykle nagrywa strumienie wideo i audio, kompresuje je, multipleksuje oba strumienie, a następnie zapisuje wynikowy strumień na dysku.
W CameraX rozwiązaniem do nagrywania filmów jest przypadek użycia VideoCapture:
VideoCaptureprzypadek użyciaJak widać na ilustracji 2, przechwytywanie wideo w CameraX obejmuje kilka komponentów architektury wysokiego poziomu:
SurfaceProviderdla źródła wideo.AudioSource– źródło dźwięku.- 2 kodery do kodowania i kompresowania obrazu i dźwięku.
- Muxer multimedialny do multipleksowania dwóch strumieni.
- Moduł zapisywania plików do zapisywania wyniku.
Interfejs VideoCapture API abstrahuje złożony silnik przechwytywania i udostępnia aplikacjom znacznie prostszy i bardziej przejrzysty interfejs API.
Omówienie interfejsu VideoCapture API
VideoCapture to przypadek użycia CameraX, który dobrze sprawdza się samodzielnie lub w połączeniu z innymi przypadkami użycia. Konkretne obsługiwane kombinacje zależą od możliwości sprzętowych aparatu, ale Preview i VideoCapture to prawidłowa kombinacja w przypadku wszystkich urządzeń.
Interfejs VideoCapture API składa się z tych obiektów, które komunikują się z aplikacjami:
VideoCaptureto klasa przypadku użycia najwyższego poziomu.VideoCapturewiąże się zLifecycleOwnerza pomocąCameraSelectori innych funkcji CameraX UseCases. Więcej informacji o tych koncepcjach i sposobach użycia znajdziesz w artykule Architektura CameraX.Recorderto implementacja VideoOutput ściśle powiązana zVideoCapture. AplikacjaRecordersłuży do rejestrowania obrazu i dźwięku. Aplikacja tworzy nagrania zRecorder.PendingRecordingkonfiguruje nagrywanie, udostępniając opcje takie jak włączanie dźwięku i ustawianie odbiornika zdarzeń. Aby utworzyćPendingRecording, musisz użyćRecorder.PendingRecordingnie nagrywa niczego.Recordingwykonuje właściwe nagranie. Aby utworzyćRecording, musisz użyćPendingRecording.
Rysunek 3 przedstawia relacje między tymi obiektami:
Legenda:
- Utwórz
Recorderza pomocąQualitySelector. - Skonfiguruj
Recorderza pomocą jednego z tychOutputOptions. - W razie potrzeby włącz dźwięk, klikając
withAudioEnabled(). - Zadzwoń pod numer
start()zVideoRecordEventsłuchaczem, aby rozpocząć nagrywanie. - Użyj
pause()/resume()/stop()naRecording, aby sterować nagrywaniem. - Odpowiadaj na
VideoRecordEventsw detektorze zdarzeń.
Szczegółową listę interfejsów API znajdziesz 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:
- Powiąż
VideoCapture. - Przygotowywanie i konfigurowanie nagrywania.
- Rozpocznij i kontroluj nagrywanie czasu działania.
W sekcjach poniżej znajdziesz informacje o tym, co możesz zrobić na każdym etapie, aby uzyskać kompleksową sesję nagrywania.
Powiąż VideoCapture
Aby powiązać przypadek użycia VideoCapture, wykonaj te czynności:
- Utwórz obiekt
Recorder. - Utwórz obiekt
VideoCapture. - Połącz z
Lifecycle.
Interfejs CameraX VideoCapture API korzysta z wzorca projektowego konstruktora. Aplikacje używają Recorder.Builder do tworzenia Recorder. Możesz też skonfigurować rozdzielczość filmu dla Recorder za pomocą obiektu QualitySelector.
CameraXRecorder obsługuje te predefiniowane Qualities rozdzielczości wideo:
Quality.UHDw przypadku rozmiaru filmu w rozdzielczości 4K Ultra HD (2160p);Quality.FHDw przypadku rozmiaru wideo Full HD (1080p);Quality.HDrozmiar filmu HD (720p);Quality.SDw przypadku rozmiaru filmu SD (480p);
Pamiętaj, że CameraX może też wybierać inne rozdzielczości, jeśli aplikacja na to zezwoli.
Dokładny rozmiar filmu zależy od możliwości kamery i enkodera. Więcej informacji znajdziesz w dokumentacji CamcorderProfile.
Aplikacje mogą skonfigurować rozdzielczość, tworząc QualitySelector.
Możesz utworzyć QualitySelector za pomocą jednej z tych metod:
Podaj kilka preferowanych rozdzielczości, używając
fromOrderedList(), i uwzględnij strategię rezerwową, która będzie używana, jeśli żadna z preferowanych rozdzielczości nie jest obsługiwana.CameraX może wybrać najlepsze dopasowanie na podstawie możliwości wybranego aparatu. Więcej informacji znajdziesz w
QualitySelectorFallbackStrategy specification. Na przykład poniższy kod żąda najwyższej obsługiwanej rozdzielczości nagrywania. Jeśli żadna z żądanych rozdzielczości nie jest obsługiwana, zezwala na wybór przez CameraX rozdzielczości najbliższej rozdzielczości Quality.SD:val qualitySelector = QualitySelector.fromOrderedList( listOf(Quality.UHD, Quality.FHD, Quality.HD, Quality.SD), FallbackStrategy.lowerQualityOrHigherThan(Quality.SD))Najpierw sprawdź możliwości kamery i wybierz jedną z obsługiwanych rozdzielczości za pomocą
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 funkcja z
QualitySelector.getSupportedQualities()na pewno będzie działać w przypadkuVideoCapturelub kombinacjiVideoCaptureiPreview. W przypadku łączenia zImageCapturelubImageAnalysisCameraX może nadal nie powiązać się, jeśli wymagana kombinacja nie jest obsługiwana przez żądany aparat.
Gdy masz już QualitySelector, aplikacja może utworzyć obiekt VideoCapture i przeprowadzić wiązanie. Pamiętaj, że to powiązanie jest takie samo jak w przypadku innych zastosowań:
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 danymi wyjściowymi kamery, takimi jak zoom i ekspozycja, znajdziesz w tym przewodniku.
Recorder wybiera format najbardziej odpowiedni dla systemu. Najpopularniejszym kodekiem wideo jest H.264 AVC, a formatem kontenera jest MPEG-4.
Konfigurowanie i tworzenie nagrania
Z Recorder aplikacja może tworzyć obiekty nagrywania, aby rejestrować obraz i dźwięk. Aplikacje tworzą nagrania w ten sposób:
- Skonfiguruj
OutputOptionsza pomocąprepareRecording(). - (Opcjonalnie) Włącz nagrywanie dźwięku.
- Użyj
start(), aby zarejestrowaćVideoRecordEventodbiorcę i rozpocząć nagrywanie wideo.
FunkcjaRecorder zwraca obiekt Recording, gdy wywołasz funkcję start().
Aplikacja może używać tego obiektu Recording do zakończenia przechwytywania lub wykonywania innych działań, takich jak wstrzymywanie i wznawianie.
Recorder obsługuje 1 obiekt Recording naraz. Nowe nagrywanie możesz rozpocząć po wywołaniu Recording.stop() lub Recording.close() na poprzednim obiekcie Recording.
Przyjrzyjmy się tym krokom bliżej. Najpierw aplikacja konfiguruje OutputOptions dla dyktafonu za pomocą Recorder.prepareRecording().
Recorder obsługuje te typy OutputOptions:
FileDescriptorOutputOptionsdo rejestrowania wFileDescriptor.FileOutputOptionsdo rejestrowania wFile.MediaStoreOutputOptionsdo rejestrowania wMediaStore.
W przypadku wszystkich typów OutputOptions możesz ustawić maksymalny rozmiar pliku za pomocą parametru setFileSizeLimit(). Inne opcje są specyficzne dla poszczególnych typów danych wyjściowych, np. ParcelFileDescriptor w przypadku FileDescriptorOutputOptions.
prepareRecording() zwraca obiekt PendingRecording, który jest obiektem pośrednim używanym do tworzenia odpowiedniego obiektu Recording. PendingRecording to klasa przejściowa, która w większości przypadków powinna być niewidoczna i rzadko jest buforowana przez aplikację.
Aplikacje mogą dodatkowo konfigurować nagrywanie, np.:
- Włącz dźwięk za pomocą urządzenia
withAudioEnabled(). - Zarejestruj detektor, aby otrzymywać zdarzenia nagrywania wideo za pomocą
start(Executor, Consumer<VideoRecordEvent>). - Umożliwia ciągłe nagrywanie, gdy obiekt VideoCapture, do którego jest dołączony, zostanie ponownie powiązany z inną kamerą, z
PendingRecording.asPersistentRecording().
Aby rozpocząć nagrywanie, zadzwoń pod numer PendingRecording.start(). CameraX przekształca PendingRecording w Recording, umieszcza żądanie nagrywania w kolejce i zwraca do aplikacji nowo utworzony obiekt Recording.
Gdy nagrywanie na odpowiednim urządzeniu z kamerą się rozpocznie, CameraX wyśle zdarzenie VideoRecordEvent.EVENT_TYPE_START.
Poniższy przykład pokazuje, jak nagrać wideo i dźwięk do plikuMediaStore:
// 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 przedniego aparatu jest domyślnie odbity jak w lustrze, ale filmy nagrywane za pomocą VideoCapture nie są domyślnie odbite jak w lustrze. W CameraX 1.3 można teraz tworzyć odbicia lustrzane nagrań wideo, aby podgląd z przedniego aparatu i nagrany film były zgodne.
Dostępne są 3 opcje MirrorMode: MIRROR_MODE_OFF, MIRROR_MODE_ON i MIRROR_MODE_ON_FRONT_ONLY. Aby dopasować podgląd do aparatu, Google zaleca używanie trybu MIROR_MODE_ON_FRONT_ONLY, co oznacza, że odbicie lustrzane nie jest włączone w przypadku tylnego aparatu, ale jest włączone w przypadku przedniego aparatu. Więcej informacji o MirrorMode znajdziesz w sekcji MirrorMode constants.
Ten fragment kodu pokazuje, jak wywołać VideoCapture.Builder.setMirrorMode() za pomocą MIRROR_MODE_ON_FRONT_ONLY. Więcej informacji znajdziesz w sekcji 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
Trwające Recording możesz wstrzymać, wznowić i zatrzymać, korzystając z tych metod:
pauseaby wstrzymać bieżące aktywne nagrywanie.resume()aby wznowić wstrzymane aktywne nagrywanie.stop()aby zakończyć nagrywanie i usunąć wszystkie powiązane obiekty nagrywania.mute()– wyciszanie lub wyłączanie wyciszenia bieżącego nagrania.
Pamiętaj, że możesz zadzwonić pod numer stop(), aby zakończyć Recording niezależnie od tego, czy nagrywanie jest wstrzymane, czy aktywne.
Jeśli masz zarejestrowany EventListener z PendingRecording.start(), Recording komunikuje się za pomocą VideoRecordEvent.
VideoRecordEvent.EVENT_TYPE_STATUSsłuży do rejestrowania statystyk, takich jak aktualny rozmiar pliku i czas nagrywania.VideoRecordEvent.EVENT_TYPE_FINALIZEjest używany w przypadku wyniku nagrywania i zawiera informacje takie jak identyfikator URI pliku końcowego oraz wszelkie powiązane błędy.
Gdy aplikacja otrzyma EVENT_TYPE_FINALIZE wskazujący, że sesja nagrywania zakończyła się pomyślnie, możesz uzyskać dostęp do nagranego filmu z lokalizacji określonej w OutputOptions.
Dodatkowe materiały
Więcej informacji o bibliotece CameraX znajdziesz w tych materiałach:
- Ćwiczenia z programowania dotyczące CameraX
- Oficjalna przykładowa aplikacja CameraX
- Najnowsza lista interfejsów CameraX Video Capture API
- Informacje o wersji CameraX
- Kod źródłowy CameraX