Система захвата обычно записывает видео- и аудиопотоки, сжимает их, мультиплексирует и записывает полученный поток на диск.
В 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 { Camera2CameraInfo .from(it) .getCameraCharacteristic(CameraCharacteristics.LENS\_FACING) == CameraMetadata.LENS_FACING_BACK } val supportedQualities = QualitySelector.getSupportedQualities(cameraInfo[0]) val filteredQualities = arrayListOf (Quality.UHD, Quality.QHD, 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.QHD -> "QHD" Quality.FHD -> "FHD" Quality.HD -> "HD" Quality.SD -> "SD" else -> throw IllegalArgumentException() } }Обратите внимание, что функция, возвращаемая из
QualitySelector.getSupportedQualities(), гарантированно работает либо для варианта использованияVideoCapture, либо для комбинации вариантов использованияVideoCaptureиPreview. При связывании с вариантом использованияImageCaptureилиImageAnalysisCameraX может не выполнить связывание, если нужная комбинация не поддерживается на запрашиваемой камере.
Получив 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. Это означает, что зеркальное отображение не включено для основной камеры, но включено для фронтальной. Подробнее о MirrorModeMirrorMode constants…
В этом фрагменте кода показано, как вызвать VideoCapture.Builder.setMirrorMode() с помощью MIRROR_MODE_ON_FRONT_ONLY. Подробнее 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);
Динамический диапазон (видео 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 Video Capture
- Примечания к выпуску CameraX
- Исходный код CameraX
- Высокоскоростная съемка и замедленное видео с CameraX 1.5