Архитектура видеозахвата CameraX

Система захвата обычно записывает видео- и аудиопотоки, сжимает их, мультиплексирует два потока, а затем записывает полученный поток на диск.

концептуальная схема системы захвата видео и звука
Рисунок 1. Концептуальная схема системы захвата видео и звука.

В CameraX решением для захвата видео является вариант использования VideoCapture :

концептуальная диаграмма, показывающая, как камера x обрабатывает вариант использования видеозахвата
Рис. 2. Концептуальная диаграмма, показывающая, как CameraX обрабатывает вариант использования VideoCapture .

Как показано на рисунке 2, функция захвата видео CameraX включает в себя несколько архитектурных компонентов высокого уровня:

  • SurfaceProvider для источника видео.
  • AudioSource для источника звука.
  • Два кодировщика для кодирования и сжатия видео/аудио.
  • Медиа-мультиплексор для мультиплексирования двух потоков.
  • Файловая заставка для записи результата.

API VideoCapture абстрагирует сложный механизм захвата и предоставляет приложениям гораздо более простой и понятный API.

Обзор API видеозахвата

VideoCapture — это вариант использования CameraX, который хорошо работает сам по себе или в сочетании с другими вариантами использования. Конкретные поддерживаемые комбинации зависят от аппаратных возможностей камеры, но Preview и VideoCapture является допустимой комбинацией вариантов использования на всех устройствах.

API VideoCapture состоит из следующих объектов, которые взаимодействуют с приложениями:

  • VideoCapture — это класс вариантов использования верхнего уровня. VideoCapture привязывается к LifecycleOwner с помощью CameraSelector и других вариантов использования CameraX. Дополнительные сведения об этих концепциях и их использовании см. в разделе Архитектура CameraX .
  • Recorder — это реализация VideoOutput, тесно связанная с VideoCapture . Recorder используется для записи видео и звука. Приложение создает записи с Recorder .
  • PendingRecording настраивает запись, предоставляя такие параметры, как включение звука и настройку прослушивателя событий. Вы должны использовать Recorder для создания PendingRecording . PendingRecording ничего не записывает.
  • Recording выполняет фактическую запись. Для создания Recording необходимо использовать PendingRecording .

На рисунке 3 показаны отношения между этими объектами:

диаграмма, показывающая взаимодействия, которые происходят в случае использования захвата видео
Рисунок 3. Диаграмма, показывающая взаимодействия, происходящие в варианте использования VideoCapture.

Легенда:

  1. Создайте Recorder с помощью QualitySelector .
  2. Настройте Recorder с помощью одного из OutputOptions .
  3. При необходимости включите звук с помощью withAudioEnabled() .
  4. Вызовите start() с прослушивателем VideoRecordEvent , чтобы начать запись.
  5. Используйте pause() / resume() / stop() в Recording для управления записью.
  6. Отвечайте на VideoRecordEvents внутри прослушивателя событий.

Подробный список API находится в файле current.txt внутри исходного кода .

Использование API VideoCapture

Чтобы интегрировать вариант использования CameraX VideoCapture в ваше приложение, выполните следующие действия:

  1. Привязать VideoCapture .
  2. Подготовьте и настройте запись.
  3. Запускайте и управляйте записью во время выполнения.

В следующих разделах описывается, что вы можете сделать на каждом этапе, чтобы получить сеанс сквозной записи.

Привязать видеозахват

Чтобы связать вариант использования VideoCapure , выполните следующие действия:

  1. Создайте объект Recorder .
  2. Создайте объект VideoCapture .
  3. Привязка к Lifecycle .

API CameraX VideoCapture соответствует шаблону проектирования построителя. Приложения используют Recorder.Builder для создания Recorder . Вы также можете настроить разрешение видео для Recorder с помощью объекта QualitySelector .

CameraX Recorder поддерживает следующие предопределенные Qualities разрешения видео:

  • Quality.UHD для видео формата 4K Ultra HD (2160p)
  • Quality.FHD для видео формата Full HD (1080p)
  • Quality.HD для видео формата HD (720p)
  • Quality.SD для размера видео SD (480p)

Обратите внимание, что CameraX также может выбирать другие разрешения, если это разрешено приложением.

Точный размер видео каждого выбора зависит от возможностей камеры и кодера. Для получения дополнительной информации см. документацию по CamcorderProfile .

Приложения могут настраивать разрешение, создавая QualitySelector . Вы можете создать QualitySelector используя один из следующих методов:

  • Предоставьте несколько предпочтительных разрешений с помощью fromOrderedList() и включите запасную стратегию, которая будет использоваться в случае, если ни одно из предпочтительных разрешений не поддерживается.

    CameraX может выбрать лучший резервный вариант на основе возможностей выбранной камеры. Более подробную информацию можно найти в FallbackStrategy specification QualitySelector . Например, следующий код запрашивает самое высокое поддерживаемое разрешение для записи, и если ни одно из разрешений запроса не может быть поддержано, разрешите CameraX выбрать то, которое наиболее близко к разрешению Quality.SD:

    val qualitySelector = QualitySelector.fromOrderedList(
             listOf(Quality.UHD, 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.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()
        }
    }
    
    

    Обратите внимание, что возвращаемая возможность из QualitySelector.getSupportedQualities() гарантированно будет работать либо для варианта использования VideoCapture , либо для комбинации вариантов использования 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 приложение может создавать объекты записи для захвата видео и звука. Приложения создают записи, выполняя следующие действия:

  1. Настройте OutputOptions с помощью prepareRecording() .
  2. (Необязательно) Включите запись звука.
  3. Используйте start() , чтобы зарегистрировать прослушиватель VideoRecordEvent и начать захват видео.

Recorder возвращает объект Recording при вызове функции start() . Ваше приложение может использовать этот объект Recording для завершения записи или выполнения других действий, таких как приостановка или возобновление.

Recorder одновременно поддерживает один объект Recording . Вы можете начать новую запись после вызова Recording.stop() или Recording.close() для предыдущего объекта Recording .

Давайте рассмотрим эти шаги более подробно. Сначала приложение настраивает OutputOptions для устройства записи с помощью 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 теперь можно зеркально отображать видеозаписи, чтобы предварительный просмотр передней камеры и записанное видео совпадали.

Существует три параметра MirrorMode: MIRROR_MODE_OFF, MIRROR_MODE_ON и MIRROR_MODE_ON_FRONT_ONLY. Для согласования с предварительным просмотром камеры Google рекомендует использовать MIROR_MODE_ON_FRONT_ONLY, что означает, что зеркальное отображение не включено для задней камеры, но включено для передней камеры. Дополнительные сведения о MirrorMode см. в 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);

Ява

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

Управление активной записью

Вы можете приостановить, возобновить и остановить текущую 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 .

Дополнительные ресурсы

Чтобы узнать больше о CameraX, посетите следующие дополнительные ресурсы: