Цель проста: просто сделать снимок, используя переднюю камеру.Картинка должна быть исправлена в момент отправки фото запроса.Предварительный просмотр даже не требуется, поэтому экземпляр 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())
}
}
}