Camera2 API: снимок делается через 4-5 секунд после подачи запроса - PullRequest
0 голосов
/ 28 января 2019

Цель проста: просто сделать снимок, используя переднюю камеру.Картинка должна быть исправлена ​​в момент отправки фото запроса.Предварительный просмотр даже не требуется, поэтому экземпляр CameraSession создается с использованием единственной поверхности, полученной из ImageReader.Но проблема в том, что на некоторых устройствах картинка захватывается только через 4-5 секунд после.Вот некоторые журналы:

Фотография была запрошена в 13: 47: 29.049

Захват запрошен в 13: 47: 29.062

Файл был написан, отправка файлана канал в 13: 47: 33.313

Файл с фотографией был получен в 13: 47: 33.339

Фотография была запрошена в 13: 47: 39.073

Запрошен запрос на13: 47: 39.074

Файл был записан, отправка файла на канал 13: 47: 43.199

Файл фотографии был получен в 13: 47: 43.215

Проблема заключается в том, что снимок был снят через 4 секунды, а функция автофокусировка не поддерживается (протестировано на Xiaomi MI-5).Как устранить такую ​​длительную задержку перед захватом или выполнить блокировку фокуса?Или, может быть, здесь есть другое решение для устранения заявленной проблемы?

Стоит упомянуть журналы планшета ASUS:

Фото было запрошено в 07: 07: 03.443

Захват был запрошен 07: 07: 03.454

Файл был записан, файл отправлен на канал 07: 07: 03.907

Файл фотографии был получен в 07: 07: 03.944

Фотография была запрошена в 07: 07: 08.449

Захват запрошен 07: 07: 08.449

Файл записан, файл отправлен на канал 07: 07: 08.635

Файл фотографии был получен в 07: 07: 08.651

Вот код:

ViewModel:

private fun makePhoto() {
    GlobalScope.launch(Main) {
        Log.i("Photo", "Photo was requested at ${LocalTime.now()}")
        val picture: File = camera.makePhoto()
        Log.i("Photo", "Photo file was received at ${LocalTime.now()}")
        //process the file somehow
    }
}

PhotoCamera:

//the method is called in onStart of an Activity or Fragment instance
override suspend fun open() {
    val surfaces = listOf(outputSurface) //surface of an ImageReader instance, comes into object's constructor
    cameraDevice =
        suspendCoroutine { cameraManager.openCamera(specification.id, SuspendingCameraStateCallback(it), handler) } //callback just resumes the coroutine with CameraDevice when onOpened method was called.
    session = suspendCoroutine { cameraDevice.createCaptureSession(surfaces, SuspendSessionCallback(it), handler) } //same, just resumes the continuation with the session that comes into onConfigured method
}

override suspend fun makePhoto(): File {
    return suspendCoroutine {
        session.apply {
            stopRepeating()
            abortCaptures()
            Log.i("Photo", "Capture was requested on ${LocalTime.now()}")
            capture(createCaptureRequest(outputSurface), captureAwaitFactory.createListener(it), handler)
        }
    }
}

private fun createCaptureRequest(target: Surface): CaptureRequest {
    val requestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE)
    requestBuilder.addTarget(target)
    requestBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO)
    requestBuilder.set(CaptureRequest.JPEG_ORIENTATION, orientation.rotation)
    return requestBuilder.build()
}

Код слушателя ImageReader, который присоединяется с помощью setOnImageAvailableListener:

override fun onImageAvailable(reader: ImageReader) {
    reader.acquireLatestImage().use { image: Image ->
        val byteBuffer = image.planes[0].buffer
        val byteArray = ByteArray(byteBuffer.capacity())
        byteBuffer.get(byteArray)
        val outputFile = createOutputFile()
        FileOutputStream(outputFile).use { stream: FileOutputStream -> stream.write(byteArray) }
        Log.i("Photo", "File was written, sending file to the channel on ${LocalTime.now()}")
        scope.launch {
            fileChannel.send(outputFile)
        }
    }
}

private fun createOutputFile() = //creates a unique file

Реализация фабрики createListener:

override fun createListener(continuation: Continuation<File>): CameraCaptureSession.CaptureCallback {
    return CoroutineCaptureCallback(channel, this, continuation)
}

И CoroutineCaptureCallback код:

internal class CoroutineCaptureCallback(
    private val channel: ReceiveChannel<File>,
    private val scope: CoroutineScope,
    private val continuation: Continuation<File>
) : CameraCaptureSession.CaptureCallback() {

    override fun onCaptureCompleted(
        session: CameraCaptureSession,
        request: CaptureRequest,
        result: TotalCaptureResult
    ) {
        super.onCaptureCompleted(session, request, result)
        scope.launch {
            continuation.resume(channel.receive())
        }
    }
}

1 Ответ

0 голосов
/ 30 января 2019

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

Тем не менее, вам, вероятно, следует выдавать повторяющийся запрос на захват, чтобы объединить автоэкспозицию и автофокусировку, в противном случае для захвата изображения могут использоваться очень плохие значения.Для этого я бы рекомендовал добавить вторую цель Surface, например фиктивную SurfaceTexture (созданную с некоторым случайным идентификатором текстуры в качестве аргумента; просто никогда не вызывайте updateTexImage для него, и вам не нужен контекст GL или что-либо еще).

Таким образом, как только вы отправите запрос на фотосъемку, все будет готово и раскрутится.

...