Camera2

参考官方例子

流程图

这里引用了管道的概念将安卓设备和摄像头之间联通起来,系统向摄像头发送 Capture 请求,而摄像头会返回 CameraMetadata。这一切建立在一个叫作 CameraCaptureSession 的会话中。

类图

CameraManager

摄像头管理器。这是一个全新的系统管理器,专门用于检测系统摄像头、打开系统摄像头。另外,调用CameraManagergetCameraCharacteristics(String cameraId)方法即可获取指定摄像头的相关特性。

CameraCharacteristics

摄像头特性。该对象通过CameraManager来获取,用于描述特定摄像头所支持的各种特性。类似与原来的CameraInfo

CameraDevice

代表系统摄像头。该类的功能类似于早期的Camera类。而每个 CameraDevice 自己会负责建立 CameraCaptureSession 以及建立 CaptureRequest

CameraCaptureSession

这是一个非常重要的API,当程序需要预览、拍照时,都需要先通过该类的实例创建Session。而且不管预览还是拍照,也都是由该对象的方法进行控制的,其中控制预览的方法为setRepeatingRequest();控制拍照的方法为capture()。 为了监听CameraCaptureSession的创建过程,以及监听CameraCaptureSession的拍照过程,Camera v2 API为CameraCaptureSession提供了StateCallbackCaptureCallback等内部类。

CameraRequest和CameraRequest.Builder

当程序调用setRepeatingRequest()方法进行预览时,或调用capture()方法进行拍照时,都需要传入CameraRequest参数。CameraRequest代表了一次捕获请求,用于描述捕获图片的各种参数设置,比如对焦模式、曝光模式……总之,程序需要对照片所做的各种控制,都通过CameraRequest参数进行设置。CameraRequest.Builder则负责生成CameraRequest对象

预览流程

权限与请求

1
2
3
4
5
6
  <uses-permission android:name="android.permission.CAMERA" />
  <uses-feature android:name="android.hardware.camera" />
  <uses-feature android:name="android.hardware.camera.autofocus" />

  6.0以上需要动态获取    
 requestPermissions(arrayOf(Manifest.permission.CAMERA), REQUEST_CAMERA_PERMISSION)

开启线程

可以自定义,也可以使用HandlerThread,之后传递相关callback的回调使用这个线程中的handler

1
2
3
4
5
        backgroundThread = HandlerThread("CameraBackground")
                .apply {
                }
                .also { it.start() }
        backgroundHandler = Handler(backgroundThread?.looper)

### 获取CameraManager

1
     val manager = activity!!.getSystemService(Context.CAMERA_SERVICE) as CameraManager

### 获取CameraCharacteristics 根据获取CameraManager中的cameraIdList获取

1
2
3
4
 for (cameraId in manager.cameraIdList) {
    // 获取摄像头特性
    val characteristics = manager.getCameraCharacteristics(cameraId)
    、、、

### 设置输出尺寸大小

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
  private fun chooseOptimalSize(choices: Array<Size>, textureViewWidth: Int,
                textureViewHeight: Int, maxWidth: Int, maxHeight: Int, aspectRatio: Size): Size {
            val bigEnoungh = ArrayList<Size>()
            val notBigEnough = ArrayList<Size>()
            val w = aspectRatio.width
            val h = aspectRatio.height
            // 找到最适合的分辨率
            for (option in choices) {
                // 如果满足输出的尺寸小于等于最大尺寸,并且宽高比等于aspectRatio的宽高比
                if (option.width <= maxWidth && option.height <= maxHeight && option.height == option.width * h / w) {
                    if (option.width >= textureViewWidth && option.height >= textureViewHeight) { // option的比view较大
                        bigEnoungh.add(option)
                    } else { // option的比view尺寸小
                        notBigEnough.add(option)
                    }
                }
            }

            return when {
                // 优先bigEnoungh的最小尺寸 , 目的是为了接近view的尺寸
                bigEnoungh.size > 0 -> Collections.min(bigEnoungh, CompareSizesByArea())
                // 然后输出notBigEnough的最大尺寸,目的是为了接近view的尺寸
                notBigEnough.size > 0 -> Collections.max(notBigEnough, CompareSizesByArea())
                else -> {
                    Log.e(TAG, "Couldn't find any suitable preview size")
                    choices[0]
                }
            }
        }

### 打开摄像头

这里有个CameraDevice.StateCallback(),以及handler回调

1
          manager.openCamera(cameraId, stateCallback, backgroundHandler)

CameraDevice.StateCallback()

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
private val stateCallback = object : CameraDevice.StateCallback() {

        override fun onOpened(cameraDevice: CameraDevice) {
            cameraOpenCloseLock.release()
            this@Camera2BasicFragment.cameraDevice = cameraDevice
            // 如果打开了,则创建session
            createCameraPreviewSession()
        }

        override fun onDisconnected(cameraDevice: CameraDevice) {
            cameraOpenCloseLock.release()
            cameraDevice.close()
            this@Camera2BasicFragment.cameraDevice = null
        }

        override fun onError(cameraDevice: CameraDevice, error: Int) {
            onDisconnected(cameraDevice)
            this@Camera2BasicFragment.activity?.finish()
        }

### 创建Session

先创建CaptureRequestBuilder,并且绑定Surface,然后调用createCaptureSession方法 CameraCaptureSession.StateCallback(),最后调用setRepeatingRequest方法传入CaptureRequest,handler,CameraCaptureSession.CaptureCallback()

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
private fun createCameraPreviewSession() {
        try {
            // 初始化SurfaceTexture
            val texture = textureView.surfaceTexture
            // 设置高宽
            texture.setDefaultBufferSize(previewSize.width, previewSize.height)
            // 初始化Surface
            val surfece = Surface(texture)
            // 创建PREVIEW的CaptureRequestBuilder
            previewRequestBuilder = cameraDevice!!.createCaptureRequest(
                    CameraDevice.TEMPLATE_PREVIEW
            )
            // 添加目标
            previewRequestBuilder.addTarget(surfece)
            // createSession
            cameraDevice?.createCaptureSession(Arrays.asList(surfece, imageReader?.surface),
                    object : CameraCaptureSession.StateCallback() {
                        override fun onConfigureFailed(session: CameraCaptureSession?) {
                            activity!!.showToast("Failed")
                        }

                        override fun onConfigured(session: CameraCaptureSession?) {
                            cameraDevice ?: return

                            captureSession = session
                            try {
                                // 预览时自动对焦
                                previewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
                                        CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE)
                                // 是否设置自动闪光
                                setAutoFlash(previewRequestBuilder)

                                // 预览view
                                previewRequest = previewRequestBuilder.build()
                                // 该方法注意CameraCaptureSession.CaptureCallback()
                                captureSession?.setRepeatingRequest(previewRequest,
                                        captureCallback, backgroundHandler)
                            } catch (e: CameraAccessException) {
                                Log.e(TAG, e.toString())
                            }
                        }
                    }, null)
        } catch (e: CameraAccessException) {
            Log.e(TAG, e.toString())
        }
    }    

CameraCaptureSession.CaptureCallback()

这个是设置预览时的回调

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
    // 在图像捕捉部分向前进度时调用; 一些(但不是全部)来自图像捕获的结果是可用的
     override fun onCaptureProgressed(session: CameraCaptureSession,
            request: CaptureRequest,
            partialResult: CaptureResult) {
    }
    // 当图像捕捉已完全完成并且所有结果元数据可用时调用此方法
    override fun onCaptureCompleted(session: CameraCaptureSession,
            request: CaptureRequest,
            result: TotalCaptureResult) {
    }
    // 当捕获序列在通过此侦听器返回之前CaptureResult 或之后中止时
    override fun onCaptureSequenceAborted(session: CameraCaptureSession?, sequenceId: Int) {
        super.onCaptureSequenceAborted(session, sequenceId)
    }
    // 此回调将在帧的捕捉失败时立即调用
    override fun onCaptureFailed(session: CameraCaptureSession?, request: CaptureRequest?,
            failure: CaptureFailure?) {
    }
    // 捕获序列完成并且所有CaptureResult 或CaptureFailure通过此侦听器返回时
    override fun onCaptureSequenceCompleted(session: CameraCaptureSession?, sequenceId: Int,
            frameNumber: Long) {
    }
    // 此回调将在帧的捕捉开始时立即调用
    override fun onCaptureStarted(session: CameraCaptureSession?, request: CaptureRequest?,
            timestamp: Long, frameNumber: Long) {
    }

    //如果捕获的单个缓冲区无法发送到目标表面,则调用此方法。
    override fun onCaptureBufferLost(session: CameraCaptureSession?, request: CaptureRequest?,
            target: Surface?, frameNumber: Long) {
    }   

## 拍照流程

### 锁定焦点并提交图像请求

1
2
3
4
5
6
7
 // 设置自动对焦模式为自动对焦
            previewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
                    CameraMetadata.CONTROL_AF_TRIGGER_START)
            state = STATE_WAITING_LOCK
            // 提交要由相机设备拍摄的图像的请求
            captureSession?.capture(previewRequestBuilder.build(), captureCallback,
                    backgroundHandler)

进行拍照

进行拍照前,还有一些AF,AE对应模式状态的处理,最后调用该方法用imageReader进行拍照保存图片

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
private fun captureStillPicture() {
        try {
            if (activity == null || cameraDevice == null) return
            val rotation = activity!!.windowManager.defaultDisplay.rotation
            // CameraDevice.TEMPLATE_STILL_CAPTURE 创建适合静态图像捕获的请求。具体来说,这意味着优先考虑帧速率的图像质量
            // 把imageReader添加进去
            val captureBuilder = cameraDevice?.createCaptureRequest(
                    CameraDevice.TEMPLATE_STILL_CAPTURE)?.apply {
                addTarget(imageReader?.surface)
                // 控制jpeg的方向
                set(CaptureRequest.JPEG_ORIENTATION,
                        (ORIENTATIONS.get(rotation) + sensorOrientation + 270) % 360)
                // CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE 在这种模式下,AF算法会不断修改镜头位置,以尝试提供持续聚焦的图像流
                set(CaptureRequest.CONTROL_AF_MODE,
                        CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE)
            }?.also { setAutoFlash(it) }

            val captureCallback = object : CameraCaptureSession.CaptureCallback() {
                override fun onCaptureCompleted(session: CameraCaptureSession?,
                        request: CaptureRequest?, result: TotalCaptureResult?) {
                    activity?.showToast("Saved: $file")
                    Log.d(TAG, file.toString())
                    // 成功后取消锁定自动对焦
                    unlockFocus()
                }
            }
            captureSession?.apply {
                stopRepeating()
                abortCaptures()
                capture(captureBuilder?.build(), captureCallback, null)
            }
        } catch (e: CameraAccessException) {
            Log.e(TAG, e.toString())
        }
    }

### ImageReader

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
   // 读取信息Reader
                imageReader = ImageReader.newInstance(largest.width, largest.height,
                        ImageFormat.JPEG, 2).apply {
                    setOnImageAvailableListener(onImageAvailableListener, backgroundHandler)
                }


   private val onImageAvailableListener = ImageReader.OnImageAvailableListener {
    backgroundHandler?.post(ImageSaver(it.acquireLatestImage(), file))
}   

## 录制流程

录制使用MediaRecorder进行录制

### 设置MediaRecorder

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
   // 设置方向 
  val rotation = cameraActivity.windowManager.defaultDisplay.rotation
        when (sensorOrientation) {
            SENSOR_ORIENTATION_DEFAULT_DEGREES ->
                mediaRecorder?.setOrientationHint(DEFAULT_ORIENTATIONS.get(rotation))
            SENSOR_ORIENTATION_INVERSE_DEGREES ->
                mediaRecorder?.setOrientationHint(INVERSE_ORIENTATIONS.get(rotation))
        }
  mediaRecorder?.apply {
            // 设置音频来源为MIC
            setAudioSource(MediaRecorder.AudioSource.MIC)
            // 设置视频来源为SURFACE
            setVideoSource(MediaRecorder.VideoSource.SURFACE)
            // 设置输出格式为OutputFormat.MPEG_4
            setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
            // 设置输出文件夹
            setOutputFile(nextVideoAbsolutePath)
            // 设置码率
            setVideoEncodingBitRate(10000000)
            // 设置帧率
            setVideoFrameRate(30)
            // 设置大小
            setVideoSize(videoSize.width, videoSize.height)
            // 设置编码器为H264
            setVideoEncoder(MediaRecorder.VideoEncoder.H264)
            // 设置音频编码器为ACC
            setAudioEncoder(MediaRecorder.AudioEncoder.AAC)
            prepare()
        }

### 启用录制

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
closePreviewSession()
setUpMediaRecorder()
val texture = textureView.surfaceTexture.apply {
            setDefaultBufferSize(previewSize.width, previewSize.height)
        }

        // Set up Surface for camera preview and MediaRecorder
        val previewSurface = Surface(texture)
        val recorderSurface = mediaRecorder!!.surface
        val surfaces = ArrayList<Surface>().apply {
            add(previewSurface)
            add(recorderSurface)
        }
        previewRequestBuilder = cameraDevice!!.createCaptureRequest(TEMPLATE_RECORD).apply {
            addTarget(previewSurface)
            addTarget(recorderSurface)
        }

        // Start a capture session
        // Once the session starts, we can update the UI and start recording
        cameraDevice?.createCaptureSession(surfaces,
                object : CameraCaptureSession.StateCallback() {

                    override fun onConfigured(cameraCaptureSession: CameraCaptureSession) {
                        captureSession = cameraCaptureSession
                        updatePreview()
                        activity?.runOnUiThread {
                            videoButton.setText(R.string.stop)
                            isRecordingVideo = true
                            mediaRecorder?.start()
                        }
                    }

                    override fun onConfigureFailed(cameraCaptureSession: CameraCaptureSession) {
                        if (activity != null) showToast("Failed")
                    }
                }, backgroundHandler)