Camera2 API: 请求提交后4-5秒拍照
Camera2 API: picture is taken 4-5 seconds after the request was submitted
目标很简单:只需使用 前置 相机拍照。图片应在照片请求发送时固定。甚至不需要预览,因此 CameraSession
是使用来自 ImageReader
的单个表面实例化的。但问题是,在某些设备上,图片仅在 4-5 秒后 被捕获 。以下是一些日志:
Photo was requested at 13:47:29.049
Capture was requested on 13:47:29.062
File was written, sending file to the channel on 13:47:33.313
Photo file was received at 13:47:33.339
Photo was requested at 13:47:39.073
Capture was requested on 13:47:39.074
File was written, sending file to the channel on 13:47:43.199
Photo file was received at 13:47:43.215
问题是4秒后抓拍不支持自动对焦功能(小米MI-5测试) ).如何消除 捕获 或执行焦点锁定之前如此长的延迟?或者这里可能是消除所述问题的另一种解决方案?
值得一提的华硕平板日志:
Photo was requested at 07:07:03.443
Capture was requested on 07:07:03.454
File was written, sending file to the channel on 07:07:03.907
Photo file was received at 07:07:03.944
Photo was requested at 07:07:08.449
Capture was requested on 07:07:08.449
File was written, sending file to the channel on 07:07:08.635
Photo file was received at 07:07:08.651
代码如下:
视图模型:
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
}
}
相机:
//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()
}
使用 setOnImageAvailableListener 附加的 ImageReader 侦听器代码:
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())
}
}
}
不包括在创建捕获会话时运行的代码,因此很难说出您此时做了什么。
就是说,您可能应该发出重复拍摄请求以收敛自动曝光和自动对焦,否则您的图像拍摄可能会使用非常糟糕的值。为此,我建议添加第二个 Surface 目标,例如虚拟 SurfaceTexture(使用一些随机纹理 ID 作为参数创建;永远不要对其调用 updateTexImage,并且您不需要 GL 上下文或任何东西)。
这样一来,一旦您发出照片拍摄请求,一切就绪并启动。
目标很简单:只需使用 前置 相机拍照。图片应在照片请求发送时固定。甚至不需要预览,因此 CameraSession
是使用来自 ImageReader
的单个表面实例化的。但问题是,在某些设备上,图片仅在 4-5 秒后 被捕获 。以下是一些日志:
Photo was requested at 13:47:29.049
Capture was requested on 13:47:29.062
File was written, sending file to the channel on 13:47:33.313
Photo file was received at 13:47:33.339
Photo was requested at 13:47:39.073
Capture was requested on 13:47:39.074
File was written, sending file to the channel on 13:47:43.199
Photo file was received at 13:47:43.215
问题是4秒后抓拍不支持自动对焦功能(小米MI-5测试) ).如何消除 捕获 或执行焦点锁定之前如此长的延迟?或者这里可能是消除所述问题的另一种解决方案?
值得一提的华硕平板日志:
Photo was requested at 07:07:03.443
Capture was requested on 07:07:03.454
File was written, sending file to the channel on 07:07:03.907
Photo file was received at 07:07:03.944
Photo was requested at 07:07:08.449
Capture was requested on 07:07:08.449
File was written, sending file to the channel on 07:07:08.635
Photo file was received at 07:07:08.651
代码如下:
视图模型:
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
}
}
相机:
//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()
}
使用 setOnImageAvailableListener 附加的 ImageReader 侦听器代码:
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())
}
}
}
不包括在创建捕获会话时运行的代码,因此很难说出您此时做了什么。
就是说,您可能应该发出重复拍摄请求以收敛自动曝光和自动对焦,否则您的图像拍摄可能会使用非常糟糕的值。为此,我建议添加第二个 Surface 目标,例如虚拟 SurfaceTexture(使用一些随机纹理 ID 作为参数创建;永远不要对其调用 updateTexImage,并且您不需要 GL 上下文或任何东西)。
这样一来,一旦您发出照片拍摄请求,一切就绪并启动。