Система захвата обычно записывает видео- и аудиопотоки, сжимает их, мультиплексирует два потока, а затем записывает полученный поток на диск.
В CameraX для записи видео используется класс VideoCapture:
VideoCapture.Как показано на рисунке 2, видеозапись CameraX включает несколько высокоуровневых архитектурных компонентов:
SurfaceProviderдля источника видео.AudioSourceдля источника аудио.- Два кодера для кодирования и сжатия видео и аудио.
- Мультиплексор для объединения двух потоков.
- Средство сохранения файлов для записи результата.
VideoCapture API абстрагирует сложный механизм захвата и предоставляет приложениям гораздо более простой и понятный API.
Обзор VideoCapture API
VideoCapture – это вариант использования CameraX, который хорошо работает сам по себе или в сочетании с другими вариантами использования. Поддерживаемые комбинации зависят от аппаратных возможностей камеры, но Preview и VideoCapture – это допустимая комбинация для всех устройств.
VideoCapture API состоит из следующих объектов, которые взаимодействуют с приложениями:
VideoCapture– это класс вариантов использования верхнего уровня.VideoCaptureпривязывается кLifecycleOwnerсCameraSelectorи другими UseCase CameraX. Дополнительную информацию об этих концепциях и их использовании можно найти в статье Архитектура CameraX.Recorder– это реализация VideoOutput, которая тесно связана сVideoCapture.Recorderиспользуется для записи видео и аудио. Приложение создает записи изRecorder.PendingRecordingнастраивает запись, предоставляя такие параметры, как включение звука и настройка прослушивателя событий. Чтобы создатьPendingRecording, необходимо использоватьRecorder.PendingRecordingне записывает ничего.Recordingвыполняет запись. Чтобы создатьRecording, необходимо использоватьPendingRecording.
На рисунке 3 показаны связи между этими объектами.
Условные обозначения:
- Создайте
Recorderс помощьюQualitySelector. - Настройте
Recorderс помощью одного изOutputOptions. - При необходимости включите аудио с помощью
withAudioEnabled(). - Чтобы начать запись, позвоните
start()с помощьюVideoRecordEvent. - Для управления записью используйте кнопки
pause(),resume()иstop()наRecording. - Ответьте на
VideoRecordEventsв прослушивателе событий.
Подробный список API можно найти в файле current.txt в исходном коде.
Использование VideoCapture API
Чтобы интегрировать вариант использования CameraX VideoCapture в приложение, выполните следующие действия:
- Привязать
VideoCapture. - Подготовьте и настройте запись.
- Запустите запись времени выполнения и управляйте ею.
В следующих разделах описано, что можно сделать на каждом шаге, чтобы получить полную запись сеанса.
Привязать VideoCapture
Чтобы связать вариант использования VideoCapture, выполните следующие действия:
- Создайте объект
Recorder. - Создайте объект
VideoCapture. - Привязка к
Lifecycle.
CameraX VideoCapture API использует шаблон проектирования Builder. Приложения используют Recorder.Builder для создания Recorder. Вы также можете настроить разрешение видео для Recorder с помощью объекта QualitySelector.
CameraX Recorder поддерживает следующие предопределенные Qualities для разрешений видео:
Quality.UHDдля видео в формате 4K Ultra HD (2160p)Quality.QHDдля размера видео Quad HD (1440p), представленного в CameraX.1.7.0-alpha02Quality.FHDдля видео в формате Full HD (1080p)Quality.HDдля HD-видео (720p)Quality.SDдля видео в стандартном разрешении (480p)
Обратите внимание, что CameraX также может выбирать другие разрешения, если это разрешено приложением.
Точный размер видео зависит от возможностей камеры и видеокодера. Подробнее читайте в документации по CamcorderProfile.
Приложения могут настраивать разрешение, создавая объект QualitySelector.
Создать объект QualitySelector можно одним из следующих способов:
Укажите несколько предпочтительных разрешений с помощью
fromOrderedListи добавьте резервную стратегию на случай, если ни одно из них не поддерживается.CameraX может выбрать наилучший резервный вариант в зависимости от возможностей выбранной камеры. Подробную информацию можно найти в
FallbackStrategy specificationклассаQualitySelector. Например, следующий код запрашивает максимальное поддерживаемое разрешение для записи, а если ни одно из запрошенных разрешений не поддерживается, разрешает CameraX выбрать разрешение, наиболее близкое к Quality.SD:val qualitySelector = QualitySelector.fromOrderedList( listOf(Quality.UHD, Quality.QHD, Quality.FHD, Quality.HD, Quality.SD), FallbackStrategy.lowerQualityOrHigherThan(Quality.SD))Сначала запросите возможности камеры и выберите одно из поддерживаемых разрешений с помощью
QualitySelector.from:val cameraInfo = cameraProvider.availableCameraInfos.filter { it.cameraCharacteristics.get(CameraCharacteristics.LENS_FACING) == CameraMetadata.LENS_FACING_BACK } // Filter to SDR resolutions supported by both the camera sensor and Recorder // encoder so the UI picker only displays valid options. val supportedQualities = Recorder.getVideoCapabilities(cameraInfo[0]).getSupportedQualities(DynamicRange.SDR) val filteredQualities = arrayListOf (Quality.UHD, Quality.QHD, Quality.FHD, Quality.HD, Quality.SD) .filter { supportedQualities.contains(it) } viewBinding.simpleQualityListView.apply { adapter = ArrayAdapter(context, android.R.layout.simple_list_item_1, filteredQualities.map { it.qualityToString() }) setOnItemClickListener { _, _, position, _ -> // QualitySelector is configured at Recorder build time, so selecting // a new resolution requires building a new Recorder instance. val qualitySelector = QualitySelector.from(filteredQualities[position]) val recorder = Recorder.Builder() .setQualitySelector(qualitySelector).build() // ... } } fun Quality.qualityToString() : String { return when (this) { Quality.UHD -> "UHD" Quality.QHD -> "QHD" Quality.FHD -> "FHD" Quality.HD -> "HD" Quality.SD -> "SD" else -> throw IllegalArgumentException() } }Каждый объект
Quality, возвращаемый методомVideoCapabilities.getSupportedQualities(изRecorder.getVideoCapabilitiesдляDynamicRange.SDR), работает для варианта использованияVideoCaptureотдельно или в сочетании с вариантом использованияPreview. Если вы также привяжетеImageCaptureилиImageAnalysis, CameraX может не выполнить привязку, если камера не поддерживает такую комбинацию.
Получив QualitySelector, приложение может создать объект VideoCapture и выполнить привязку. Обратите внимание, что эта привязка
такая же, как и в других случаях использования:
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() возвращает объект Camera. Подробнее об управлении выводом камеры, например масштабированием и экспозицией…
Recorder выбирает наиболее подходящий формат для системы. Самый распространенный видеокодек – H.264 AVC с контейнером MPEG-4.
Настройте и создайте запись
Из Recorder приложение может создавать объекты записи для захвата видео и аудио. Приложения создают записи следующим образом:
- Настройте
OutputOptionsс помощьюprepareRecording(). - Включите запись аудио (необязательно).
- Используйте
start(), чтобы зарегистрировать прослушивательVideoRecordEventи начать запись видео.
При вызове функции start() Recorder возвращает объект Recording.
Ваше приложение может использовать этот объект Recording, чтобы завершить запись или выполнить другие действия, например приостановить или возобновить ее.
Recorder поддерживает один объект Recording за раз. Вы можете начать новую запись, вызвав метод Recording.stop() или Recording.close() для предыдущего объекта Recording.
Рассмотрим эти шаги подробнее. Сначала приложение настраивает OutputOptions для Recorder с помощью Recorder.prepareRecording().
Recorder поддерживает следующие типы OutputOptions:
FileDescriptorOutputOptionsдля записи вFileDescriptor.FileOutputOptionsдля записи вFile.MediaStoreOutputOptionsдля записи вMediaStore.
Для всех типов OutputOptions можно задать максимальный размер файла с помощью setFileSizeLimit(). Другие параметры зависят от типа выходных данных, например ParcelFileDescriptor для FileDescriptorOutputOptions.
prepareRecording() возвращает объект PendingRecording, который является промежуточным объектом, используемым для создания соответствующего объекта Recording. PendingRecording – это временный класс, который в большинстве случаев должен быть невидимым и редко кешируется приложением.
Приложения могут дополнительно настраивать запись, например:
- Включите аудио с помощью
withAudioEnabled(). - Зарегистрируйте прослушиватель, чтобы получать события записи видео с помощью
start(Executor, Consumer<VideoRecordEvent>). - Разрешить непрерывную запись, пока VideoCapture, к которому она прикреплена, привязывается к другой камере, с помощью
PendingRecording.asPersistentRecording().
Чтобы начать запись, позвоните по номеру PendingRecording.start(). CameraX преобразует PendingRecording в Recording, ставит запрос на запись в очередь и возвращает только что созданный объект Recording в приложение.
Когда на соответствующем устройстве камеры начинается запись, CameraX отправляет событие VideoRecordEvent.EVENT_TYPE_START.
В примере ниже показано, как записать видео и аудио в файл 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)
Хотя по умолчанию изображение с фронтальной камеры зеркально отражается, видео, записанные с помощью VideoCapture, не отражаются. В CameraX 1.3 теперь можно зеркально отражать видеозаписи, чтобы изображение с фронтальной камеры и записанное видео совпадали.
Доступны три варианта: MIRROR_MODE_OFF, MIRROR_MODE_ON и MIRROR_MODE_ON_FRONT_ONLY. Чтобы выровнять изображение по предварительному просмотру камеры, Google рекомендует использовать MIROR_MODE_ON_FRONT_ONLY. Это означает, что зеркальное отображение не включено для основной камеры, но включено для фронтальной камеры. Подробнее о режиме зеркалированияMirrorMode constants…
В этом фрагменте кода показано, как вызвать VideoCapture.Builder.setMirrorMode() с помощью MIRROR_MODE_ON_FRONT_ONLY. Подробнее setMirrorMode()…
val recorder = Recorder.Builder().build() val videoCapture = VideoCapture.Builder(recorder) .setMirrorMode(MIRROR_MODE_ON_FRONT_ONLY) .build() useCases.add(videoCapture)
Динамический диапазон (HDR-видео)
CameraX поддерживает запись 10-битного HDR-видео, например HLG10, HDR10, HDR10+ или Dolby Vision, с помощью DynamicRange.
Чтобы использовать HDR-видеозапись, выполните следующие действия:
- Запросите поддерживаемые динамические диапазоны камеры с помощью
VideoCapabilities.getSupportedDynamicRanges(). - Если вы настраиваете собственный
QualitySelector, проверьте, какие качества поддерживаются для этого динамического диапазона, с помощьюgetSupportedQualities(DynamicRange). - Настройте
VideoCapture.Builderс помощьюsetDynamicRange().
В следующем примере показано, как включить запись 10-битного HDR-видео в формате HLG, если камера поддерживает его:
Kotlin
val videoCapabilities = Recorder.getVideoCapabilities(cameraInfo) val supportedHdrRanges = videoCapabilities.supportedDynamicRanges .filter { it != DynamicRange.SDR } val recorder = Recorder.Builder().build() val videoCapture = VideoCapture.Builder(recorder) .apply { if (supportedHdrRanges.contains(DynamicRange.HLG_10_BIT)) { setDynamicRange(DynamicRange.HLG_10_BIT) } } .build()
Java
VideoCapabilities videoCapabilities = Recorder.getVideoCapabilities(cameraInfo); Set<DynamicRange> supportedRanges = videoCapabilities.getSupportedDynamicRanges(); Recorder recorder = new Recorder.Builder().build(); VideoCapture.Builder<Recorder> builder = new VideoCapture.Builder<>(recorder); if (supportedRanges.contains(DynamicRange.HLG_10_BIT)) { builder.setDynamicRange(DynamicRange.HLG_10_BIT); } VideoCapture<Recorder> videoCapture = builder.build();
Стабилизация видео
CameraX поддерживает стабилизацию видео, чтобы уменьшить дрожание и дрожание рук. во время видеозаписи.
Чтобы включить стабилизацию видео, выполните следующие действия:
- Проверьте, поддерживает ли камера стабилизацию, используя
VideoCapabilities.isStabilizationSupported(). - Включите ее на
VideoCapture.Builderс помощьюsetVideoStabilizationEnabled(true).
В следующем примере стабилизация включается, если она поддерживается камерой:
Kotlin
val videoCapabilities = Recorder.getVideoCapabilities(cameraInfo) val videoCapture = VideoCapture.Builder(recorder) .setVideoStabilizationEnabled(videoCapabilities.isStabilizationSupported) .build()
Java
VideoCapabilities videoCapabilities = Recorder.getVideoCapabilities(cameraInfo); VideoCapture<Recorder> videoCapture = new VideoCapture.Builder<>(recorder) .setVideoStabilizationEnabled(videoCapabilities.isStabilizationSupported()) .build();
Как управлять активной записью
Вы можете приостановить, возобновить или остановить текущий сеанс Recording следующими способами:
pause, чтобы приостановить текущую запись.resume()чтобы возобновить приостановленную запись.stop()чтобы завершить запись и удалить все связанные с ней объекты.mute()чтобы включить или отключить звук в текущей записи.
Обратите внимание, что вы можете вызвать stop(), чтобы завершить Recording, независимо от того, приостановлена или активна запись.
Если вы зарегистрировали EventListener в PendingRecording.start(), Recording будет обмениваться данными с помощью VideoRecordEvent.
VideoRecordEvent.EVENT_TYPE_STATUSиспользуется для записи статистики, например текущего размера файла и записанного промежутка времени.VideoRecordEvent.EVENT_TYPE_FINALIZEиспользуется для записи результата и включает такую информацию, как URI конечного файла, а также любые связанные с ним ошибки.
Когда приложение получит EVENT_TYPE_FINALIZE, указывающий на успешную запись, вы сможете получить доступ к видео из места, указанного в OutputOptions.
Амплитуда и состояние звука в реальном времени
Во время записи события VideoRecordEvent.Status предоставляют статистику аудио в реальном времени через AudioStats:
AudioStats.getAudioAmplitude()возвращает текущую пиковую амплитуду звука в диапазоне от0.0до1.0. Это значение можно использовать для управления индикаторами пользовательского интерфейса или визуализаторами звуковых сигналов.AudioStats.getAudioState()указывает на текущий статус аудиопотока, напримерAUDIO_STATE_ACTIVE,AUDIO_STATE_MUTEDилиAUDIO_STATE_SOURCE_SILENCED.
В следующем примере показано, как считать амплитуду аудио из события статуса:
Kotlin
when (event) { is VideoRecordEvent.Status -> { val audioStats = event.recordingStats.audioStats if (audioStats.hasAudio()) { val amplitude = audioStats.audioAmplitude updateAudioWaveform(amplitude) } } // ... }
Java
if (event instanceof VideoRecordEvent.Status) { VideoRecordEvent.Status status = (VideoRecordEvent.Status) event; AudioStats audioStats = status.getRecordingStats().getAudioStats(); if (audioStats.hasAudio()) { double amplitude = audioStats.getAudioAmplitude(); updateAudioWaveform(amplitude); } }
Ускоренное и замедленное видео
CameraX поддерживает высокоскоростную видеозапись, например 120, 240 или 480 кадров в секунду, что позволяет использовать два различных варианта использования:
- Видео с высокой частотой кадров (замедленная съемка, обрабатываемая проигрывателем). CameraX записывает и сохраняет кадры с высокой частотой (например, 120 кадров/сек). Видеопроигрыватель позволяет управлять скоростью воспроизведения, переключаясь между обычной скоростью и замедленной съемкой, или выполнять покадровый анализ.
- Готовое к воспроизведению замедленное видео. CameraX обрабатывает высокоскоростной поток и выводит стандартный видеофайл с частотой 30 кадров/с и скорректированными временными метками кадров. Эффект замедленной съемки "встроен" в видео, поэтому оно автоматически воспроизводится в замедленном режиме в любом стандартном видеопроигрывателе без специальной поддержки. Например, если видео, снятое с частотой 120 кадров/с, воспроизводится с частотой 30 кадров/с, то скорость воспроизведения будет в четыре раза ниже обычной.
Проверьте возможности высокоскоростного подключения
Для высокоскоростной видеозаписи требуется аппаратная поддержка и соответствующие определения CamcorderProfile от производителей устройств, например CamcorderProfile.QUALITY_HIGH_SPEED_1080P или QUALITY_HIGH_SPEED_720P.
Прежде чем настраивать высокоскоростную запись, запросите информацию о возможностях высокоскоростной съемки с помощью Recorder.getHighSpeedVideoCapabilities(CameraInfo):
Kotlin
val highSpeedCapabilities = Recorder.getHighSpeedVideoCapabilities(cameraInfo) if (highSpeedCapabilities == null) { // Camera does not support high-speed video capture return } val supportedQualities = highSpeedCapabilities.getSupportedQualities(DynamicRange.SDR)
Java
VideoCapabilities highSpeedCapabilities = Recorder.getHighSpeedVideoCapabilities(cameraInfo); if (highSpeedCapabilities == null) { // Camera does not support high-speed video capture return; } List<Quality> supportedQualities = highSpeedCapabilities.getSupportedQualities(DynamicRange.SDR);
Настройка HighSpeedVideoSessionConfig
Чтобы использовать высокоскоростную съемку, объедините варианты использования VideoCapture и Preview в HighSpeedVideoSessionConfig. Чтобы настроить сеанс, выполните следующие действия:
- Создайте вариант использования
VideoCaptureи вариант использованияPreview. - Инициализируйте
HighSpeedVideoSessionConfig.Builderс помощью варианта использованияVideoCaptureи прикрепитеPreview. - Запросите поддерживаемые диапазоны частоты кадров с помощью
CameraInfo.getSupportedFrameRateRanges(SessionConfig)и задайте выбранный диапазон. - Выберите видео с высокой частотой кадров или готовое замедленное видео:
- Чтобы создать видео с высокой частотой кадров, оставьте значение
setSlowMotionEnabled(false)(по умолчанию). - Чтобы создать замедленное видео, готовое к воспроизведению, вызовите
setSlowMotionEnabled(true).
- Чтобы создать видео с высокой частотой кадров, оставьте значение
- Привяжите конфигурацию сеанса с помощью
cameraProvider.bindToLifecycle().
В примере ниже настраивается и связывается готовый к воспроизведению сеанс замедленной съемки.
Kotlin
val preview = Preview.Builder().build() val recorder = Recorder.Builder() .setQualitySelector(QualitySelector.from(supportedQualities.first())) .build() val videoCapture = VideoCapture.withOutput(recorder) // Build HighSpeedVideoSessionConfig val sessionConfigBuilder = HighSpeedVideoSessionConfig.Builder(videoCapture) .setPreview(preview) // Enable automatic slow-motion playback at 30 fps .setSlowMotionEnabled(true) // Query and set a supported high-speed frame rate range (such as 120 or 240 fps). // The set is non-empty because getHighSpeedVideoCapabilities() returned non-null earlier. val supportedFpsRanges = cameraInfo.getSupportedFrameRateRanges(sessionConfigBuilder.build()) sessionConfigBuilder.setFrameRateRange(supportedFpsRanges.first()) val sessionConfig = sessionConfigBuilder.build() // Bind session config to lifecycle cameraProvider.bindToLifecycle( lifecycleOwner, CameraSelector.DEFAULT_BACK_CAMERA, sessionConfig )
Java
Preview preview = new Preview.Builder().build(); Recorder recorder = new Recorder.Builder() .setQualitySelector(QualitySelector.from(supportedQualities.get(0))) .build(); VideoCapture<Recorder> videoCapture = VideoCapture.withOutput(recorder); HighSpeedVideoSessionConfig.Builder sessionConfigBuilder = new HighSpeedVideoSessionConfig.Builder(videoCapture) .setPreview(preview) .setSlowMotionEnabled(true); // The set is non-empty because getHighSpeedVideoCapabilities() returned non-null earlier. Set<Range<Integer>> supportedFpsRanges = cameraInfo.getSupportedFrameRateRanges(sessionConfigBuilder.build()); sessionConfigBuilder.setFrameRateRange(supportedFpsRanges.iterator().next()); HighSpeedVideoSessionConfig sessionConfig = sessionConfigBuilder.build(); cameraProvider.bindToLifecycle( lifecycleOwner, CameraSelector.DEFAULT_BACK_CAMERA, sessionConfig );
Вы также можете использовать Kotlin DSL, представленный в CameraX 1.7.0-alpha02, чтобы быстро настраивать высокоскоростные сеансы:
val sessionConfig = highSpeedVideoSessionConfig(videoCapture) {
this.preview = preview
isSlowMotionEnabled = true
frameRateRange = supportedFpsRanges.first()
}
cameraProvider.bindToLifecycle(
lifecycleOwner,
CameraSelector.DEFAULT_BACK_CAMERA,
sessionConfig
)
Как записать видео с высокой скоростью или замедлением
После привязки HighSpeedVideoSessionConfig начните запись с помощью VideoCapture так же, как и при стандартной видеозаписи:
Kotlin
val recording = videoCapture.output .prepareRecording(context, mediaStoreOutput) .withAudioEnabled() .start(ContextCompat.getMainExecutor(context)) { event -> when (event) { is VideoRecordEvent.Start -> Log.d(TAG, "Recording started") is VideoRecordEvent.Finalize -> { if (!event.hasError()) { Log.d(TAG, "Video saved: ${event.outputResults.outputUri}") } } } }
Java
Recording recording = videoCapture.getOutput() .prepareRecording(context, mediaStoreOutput) .withAudioEnabled() .start(ContextCompat.getMainExecutor(context), event -> { if (event instanceof VideoRecordEvent.Start) { Log.d(TAG, "Recording started"); } else if (event instanceof VideoRecordEvent.Finalize) { VideoRecordEvent.Finalize finalize = (VideoRecordEvent.Finalize) event; if (!finalize.hasError()) { Log.d(TAG, "Video saved: " + finalize.getOutputResults().getOutputUri()); } } });
Дополнительные ресурсы
Чтобы узнать больше о CameraX, ознакомьтесь со следующими дополнительными ресурсами:
- Практическая работа "Начало работы с CameraX"
- Официальное пример приложения CameraX
- Последняя версия списка API для записи видео в CameraX
- Примечания к выпуску CameraX
- Исходный код CameraX
- Высокоскоростная съемка и замедленное видео с CameraX 1.5