Я занимаюсь сопровождением популярной кроссплатформенной библиотеки мобильных камер, которая может одновременно выполнять предварительный просмотр, захват фотографий, захват видео и обработку кадров. На iOS это работает отлично. Но на Android это практически невозможно сделать с помощью API Camera2/android.media.
Моя структура на высоком уровне:

Важная деталь: VideoPipeline будет выполнять обработку кадров/анализ изображений и запись видео в одном, то есть синхронно.
[*]Мне нужен доступ к буферу CPU/GPU к собственному кадру [*]Мне нужно, чтобы формат кадра можно было настраивать (YUV_420_888, PRIVATE, RGBA_8888) [*]Мне нужно, чтобы он был синхронным (он же сначала запускается MLKit Frame Processor, затем запускается MediaRecorder/MediaCodec; таким образом я могу применять такие вещи, как фильтры лиц, если это необходимо) [*]MediaRecorder/MediaCodec записывает кадр в видеофайл h264/h265 (.mp4).
Кажется, что с Camera2 это вообще невозможно, верно?
Несколько потенциальных решений/идей, которые у меня были:
1. Отдельные выходы
Использовать отдельные выходы камеры, MediaRecorder и ImageReader. Не работает, поскольку камера поддерживает только 3 выхода. У нас уже есть 3 (превью, фото, видео).
2. Используйте ImageReader/ImageWriter, чтобы передать его

Это самое близкое решение, которое у меня было до сих пор, и кажется, что ImageReader/ImageWriter действительно эффективны, поскольку они просто перемещают буферы (по крайней мере, на самом деле). Но у этого подхода есть несколько проблем:
[*]
Это работает не на всех устройствах. Не гарантируется, что MediaRecorder/MediaCodec может быть загружен изображениями из ImageWriter, поэтому иногда он просто вылетает >
[*]
Похоже, что для этого требуется установить флаги графического процессора (только API 29), но даже они в большинстве случаев не работают:
val flags = HardwareBuffer.USAGE_VIDEO_ENCODE или HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE val readFormat = ImageFormat.YUV_420_888 // (может быть YUV, PRIVATE или RGBA_8888) imageReader = ImageReader.newInstance(ширина, высота, ReaderFormat, MAX_IMAGES, флаги) // ... вал mediaRecorder = ... mediaRecorder.prepare() val writeFormat = readFormat // или теперь это должно быть ImageFormat.PRIVATE??? imageWriter = ImageWriter.newInstance(mediaRecorder.surface, MAX_IMAGES, writeFormat) imageReader.setOnImageAvailableListener({читатель -> val image = reader.acquireNextImage() если (isRecording) imageWriter.queueInputImage(изображение) еще изображение.закрыть() }, обработчик) configureCamera(imageReader.surface, ...) // позже: mediaRecorder.start() [*]Насколько я понимаю, для этого требуется дополнительный шаг преобразования из моего формата в любой формат, который хочет MediaRecorder/MediaCodec. Поэтому мне может понадобиться дополнительный ImageReader в формате PRIVATE:

...что просто смешно.
[*]
Он не поддерживает переворот камеры (сзади спереди) во время записи, поскольку ширина/высота буферов изображений может измениться, и это может привести к изменению в этом конвейере нет этапа масштабирования/изменения размера.
3. Создайте собственный конвейер OpenGL
Создаем собственный конвейер OpenGL, в который будет выполняться рендеринг камеры, а затем выполняем сквозной проход рендеринга для рендеринга кадра на все выходные данные:

Но у этого решения есть четыре основных недостатка:
[*]Это действительно очень сложно построить (я уже построил это, см. этот PR, так что это не проблема, если честно) [*]Похоже, что это не так эффективно, как подход ImageReader/ImageWriter, поскольку мы выполняем неявное преобразование RGB и фактический проход рендеринга, тогда как ImageReader /ImageWriter просто перемещает буферы изображений (по крайней мере, насколько я это понимаю) [*]Он работает только в RGBA_8888, так как OpenGL работает в RGB. Это означает, что наш процессор кадров (MLKit) не работает, если он обучен на данных YUV_420_888 — это жесткое требование. [*]Это не синхронно, ImageReader вызывается позже. Мы не могли использовать информацию из фрейма, чтобы решить, что будет отображаться позже (например, чтобы применить фильтр лица).
На данный момент я совершенно ничего не знаю, если честно. Неужели синхронный видеоконвейер вообще невозможен в Android?
Мои требования:
[*]У меня нет проблем с использованием C++/JNI для этого [*]Минимальный уровень API не имеет значения. Если только 33, то это то, что есть.
Я буду признателен за любые подсказки/помощь, возможно, я не знаю некоторых хороших API. Спасибо!