Camera2 API:在提交请求后的4-5秒内拍摄照片

时间:2019-01-28 12:09:14

标签: android android-camera2

目标很简单:只需使用前置相机拍摄照片即可。图片应在发送照片请求时固定。甚至不需要预览,因此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秒钟后被捕获,并且不支持自动对焦功能(在小米MI-5上进行了测试)。如何消除捕获之前这么长时间的延迟或执行焦点锁定?还是这是消除上述问题的另一种解决方案?

值得一提的是华硕的平板电脑日志:

  

在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()
}
使用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())
        }
    }
}

1 个答案:

答案 0 :(得分:1)

不包括创建捕获会话时运行的代码,因此很难告诉您此时的操作。

也就是说,您可能应该发出重复的捕获请求,以使自动曝光和自动对焦收敛,否则您的图像捕获可能会使用非常差的值。为此,我建议添加第二个Surface目标,例如虚拟的SurfaceTexture(以一些随机纹理ID作为参数创建;永远不要在其上调用updateTexImage,并且不需要GL上下文或其他任何东西)。

这样,一旦您发出照片捕获请求,一切就准备就绪并旋转起来。