Как правильно реализовать обработку кадров Camera2 в реальном времени с помощью RxJava?

1

Я делаю реактивную обертку над камерой2, моя цель - получить каждый кадр, а затем перейти к распознаванию лиц.

Итак, я создал метод оболочки над setOnImageAvailableListener

  fun createOnImageAvailableFlowable(imageReader: ImageReader, handler: Handler): Flowable<ImageReader> {
        return Flowable.create({ subscriber ->
            imageReader.setOnImageAvailableListener({
                if (!subscriber.isCancelled)
                    subscriber.onNext(it)
            }, handler)

            subscriber.setCancellable {
                imageReader.setOnImageAvailableListener(null, null)
            }
        }, BackpressureStrategy.LATEST)
    }

Реактивная цепочка выглядит следующим образом:

 createOnImageAvailableFlowable(imageReader!!, null)
 .concatMap {
     it.acquireLatestImage()?.use { image ->
        val rotation = ReactiveCamera.getRotationCompensation(cameraId!!, this, applicationContext)
        val visionImage = FirebaseVisionImage.fromMediaImage(image, rotation)
        firebaseFaceDetector
          .detectInImage(visionImage)
          .toFlowable(BackpressureStrategy.LATEST)
          .map { list ->Optional(list)}
     } ?: Flowable.just(Optional(null))
 }
 ...

Этот код работает, но вызывает некоторые задержки на поверхности предварительного просмотра, потому что все работы выполняются в основном потоке. Это должно быть выполнено в отдельном потоке. Мое наивное решение заключается в добавлении оператора наблюдений перед concatMap:

createOnImageAvailableFlowable(imageReader!!, null)
.observeOn(Schedulers.io()) // doesn't switch thread
.concatMap {
 // still main thread
}
...

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

Что я делаю не так? Как я могу дать команду реактивному потоку быть выполненным в отдельном потоке в этом случае? Как можно справиться с противодавлением в случае обработки кадров в реальном времени?

Upd

Я предоставил свой собственный поток, как предложил Kiskae, но теперь только первый выброс происходит в потоке планировщика, а остальные выбросы остаются в основном потоке:

createOnImageAvailableFlowable(imageReader!!, null)
.subscribeOn(AndroidSchedulers.from(nonMainThread.looper))
.concatMap {
   val t = Thread.currentThread()
   val name = t.name
   Log.d(TAG, "current thread {$name}")
 ...
}

Выход:

D/MainActivity: current thread {Camera2}
D/MainActivity: current thread {main}
D/MainActivity: current thread {main}
D/MainActivity: current thread {main}
D/MainActivity: current thread {main}
Теги:
android-camera2
rx-java
rx-java2
concurrency

1 ответ

0

Глядя на документацию ImageReader.setOnImageAvailableListener:

Обработчик: обработчик, для которого должен быть вызван слушатель, или NULL, если слушатель должен быть вызван в цикле вызывающего потока.

Поскольку вы подписываетесь на главный циклытель, он заканчивает настройкой обратного вызова с помощью основного циклытеля, concatMap все обработки до concatMap всегда выполняются в потоке приложения.

Вы можете решить эту проблему, предоставив обработчик вместо null или вызвав subscribeOn и предоставив планировщик на основе обработчика, такой как RxAndroid HandlerScheduler.

  • 0
    Проблема остается, код внутри concatMap выполняется только один раз в предоставленном потоке, а затем переключается на основной. Пожалуйста, посмотрите на обновленный вопрос
  • 0
    Это, вероятно, означает, что Looper по умолчанию привязан к экземпляру ImageReader . Вам нужно будет заменить второй параметр вызова createOnImageAvailableFlowable соответствующим обработчиком, чтобы быть абсолютно уверенным, что слушатель вызывается для этого обработчика.
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню