Как отразить PreviewView в CameraX?

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

У меня есть настраиваемое устройство с фронтальной камерой, которая по умолчанию зеркалируется, поэтому я хочу отображать предварительный просмотр в обычном режиме, и мне нужно горизонтально перевернуть содержимое PreviewView, но я застрял. Другие люди в прошлом предлагали использовать PreviewView#setScaleX(-1), но он либо вообще не работает, либо его нужно вызывать в очень конкретном месте кода, которого я еще не нашел.

Приведенный ниже код представляет собой упрощенную версию CameraFragment.kt в официальном Пример CameraXBasic; Я добавил комментарии, где я уже устал звонить viewFinder.scaleX = -1f безуспешно. Честно говоря, я не думаю, что это место имеет значение, потому что, если я вызываю его с любым значением, отличным от 1, он отлично работает как с scaleX, так и с scaleY, но он всегда игнорирует отрицательный знак, поэтому он никогда не переворачивает.

private lateinit var viewFinder: PreviewView

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    ...
    viewFinder = view.findViewById(R.id.view_finder)
    // HERE
    viewFinder.post {
        // HERE
        setupCamera()
    }
}

private fun setupCamera() {
    val cameraProviderFuture = ProcessCameraProvider.getInstance(requireContext())
    cameraProviderFuture.addListener(Runnable {
        val cameraProvider = cameraProviderFuture.get()

        val cameraSelector = CameraSelector.Builder()
            .requireLensFacing(CameraSelector.LENS_FACING_FRONT)
            .build()

       val preview = Preview.Builder()
            .build()
            .also {
                // HERE
                it.setSurfaceProvider(viewFinder.surfaceProvider)
            }

       cameraProvider.unbindAll()

        try {
            cameraProvider.bindToLifecycle(this, cameraSelector, preview)
            // HERE
        } catch (exc: Exception) {
            Log.e(TAG, "Use case binding failed", exc)
        }
        // HERE
    }, ContextCompat.getMainExecutor(requireContext()))
    // HERE
}

person rdxdkr    schedule 05.01.2021    source источник


Ответы (1)


В зависимости от типа реализации (COMPATIBLE vs PERFORMANCE) и устройства PreviewView может использовать TextureView или SurfaceView, в вашем случае я предполагаю, что PreviewView использует SurfaceView, вы можете подтвердить это, получив доступ к первому дочернему представлению PreviewView ( View.getChildAt(0)).

TextureView такой же, как и любой другой View, поэтому, когда PreviewView его использует, установка для scaleX значения -1 должна отражать отображаемый предварительный просмотр. Вы можете вызвать PreviewView.setScaleX(-1F) после создания макета (например, в onViewCreated()).

С SurfaceView это немного сложно, так как Surface и View в некоторых аспектах независимы: Surface размещается за окном, содержащим View, а иерархия представлений обрабатывает правильную компоновку всего макета с помощью пробивая отверстие в окне, чтобы отобразить Surface SurfaceView. Это может объяснить, почему зеркальное отображение содержимого SurfaceView невозможно, хотя я не уверен, почему увеличение (для scaleX установлено значение ›1) и уменьшение (для scaleX установлено значение от 0 до 1) все же работает.

person Husayn Hakeem    schedule 05.01.2021
comment
Устройство работает под управлением Android 7.1, и в журнале viewFinder.getChildAt(0) отображается null. scaleX установлен на -1 и ... Теперь он работает? Но я только что попробовал приложение на своем телефоне с Android 10 и получил те же результаты, несмотря на то, что предварительный просмотр все еще не переворачивается. Я понятия не имею. - person rdxdkr; 05.01.2021
comment
Вы должны звонить getChildAt(0) только после того, как предварительный просмотр начался. - person Husayn Hakeem; 05.01.2021
comment
Вы имеете в виду после звонка bindToLifecycle()? Все еще получаю null. - person rdxdkr; 05.01.2021
comment
Нет, предварительный просмотр не начинается сразу после вызова binToLifecycle(). Вы можете использовать PreviewView.getPreviewStreamState() и дождаться отправки состоянияSTREAMING. Вы также можете использовать любой другой способ вызвать этот метод только после того, как увидите, что предварительный просмотр начинается, например Установите прослушиватель щелчков на PreviewView, когда вы увидите, что предварительный просмотр начался, щелкните по нему, внутри прослушивателя щелчков вы можете зарегистрировать его первого дочернего элемента. - person Husayn Hakeem; 05.01.2021
comment
Спасибо. Как ни странно, на настраиваемом устройстве по умолчанию используется TextureView, и, возможно, именно поэтому он действительно работает, в то время как SurfaceView, который используется на моем телефоне, по-прежнему не хочет сотрудничать. Чтобы быть ясным, единственный способ надежно отразить SurfaceView - это преобразовать каждый кадр в растровое изображение в реальном времени и вернуть их на поверхность? - person rdxdkr; 06.01.2021
comment
Я предполагаю. Вам нужно будет получить кадры с камеры, отразить и исправить их, а затем нарисовать их на SurfaceView Surface. Это превзойдет цель использования SurfaceView вместо TextureView, особенно если вы просто зеркалируете содержимое предварительного просмотра. - person Husayn Hakeem; 06.01.2021
comment
Я задал новый вопрос, немного подробнее о том, чего я пытаюсь достичь. Я начал с CameraX, потому что он требует наименьшего количества работы как для использования камеры в целом, так и для вызова API-интерфейсов MLKit, но с этими причудами я больше не уверен на 100%. - person rdxdkr; 06.01.2021