代码之家  ›  专栏  ›  技术社区  ›  nyarian

camera2 api:请求提交后4-5秒拍摄图片

  •  0
  • nyarian  · 技术社区  · 7 年前

    目标很简单:只需使用 前面 照相机。照片应在照片请求发送时固定。甚至不需要预览,因此 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 回复  |  直到 7 年前
        1
  •  1
  •   Eddy Talvala    7 年前

    在创建捕获会话时运行的代码不包括在内,因此很难知道此时您在做什么。

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

    这样,一旦你发出拍照请求,一切都准备就绪并开始运转。