关于 Camera2 API
这次使用的 API 是
Camera2
。
Camera2
是 Google 在 Android L 之后推出的全新的相机 API。
Camera2
支持的功能要比
Camera
丰富很多,但是相应的,也增加了 API 的使用难度。
这是使用 Camera2 打开相机获取预览数据的流程图:
NV21 是什么?
NV21
是
YUV420p
的一种存储模式。存储顺序是先存
Y
,再存
U
,然后再
VU
交替存储。
那么问题来了,
YUV 是啥?
这里简要介绍下,后续可以专门一篇文章来介绍,当然你也可以在网上寻找其他资料来了解这个。
YUV 是啥
YUV 是一种颜色编码方法,主要应用于电视系统和模拟视频领域。其中 YUV 代表三个分量,
Y
代表
明亮度
,
U
和
V
表示的是
色度
。
如何使用 Camera2?
这次我们关注的是
获取视频数据
,所以对于相机相关的一些东西不会涉及。
CameraManager
:摄像头的管理类。
CameraCharacteristics
:用于描述特定摄像头所支持的特性。
CameraDevice
:代表摄像头。
CameraCaptureSession
:相机实际的控制端,我们需要在相机上做什么操作,都是由这个类发出相应的指令。
CameraRequest
:每次发起捕获请求的时候都需要传递这个对象,这个类代表了一次捕获请求,用于描述捕获的各种参数。
这次我们要获取视频数据,还有一个类很重要:
ImageReader
:可以从
Surface
直接接收渲染的数据。需要注意的是,它并不是专为
Camera
设计的。
接下来用简洁的代码描述下如何使用
Camera2
API:
初始化相机
private fun configCamera(cameraManager: CameraManager, cameraId: String): Boolean {
val cameraCharacteristics = cameraManager.getCameraCharacteristics(cameraId)
val facing = cameraCharacteristics[LENS_FACING]
if (facing != null && facing == CameraCharacteristics.LENS_FACING_FRONT) {
// 不使用前置摄像头
return false
val streamConfigurationMap =
(cameraCharacteristics[CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP]
?: return false)
mImageReader = ImageReader.newInstance(
mSurfaceView.width,
mSurfaceView.height,
ImageFormat.YUV_420_888,
mImageReader.setOnImageAvailableListener(ImageReaderAvailableListenerImp(), mBackgroundHandler)
mCameraId = cameraId
return true
这里是获取目标相机的 ID,还有初始化 ImageReader
。
调用 openCamera()
。
openCamera
之后在 onOpened
回调中初始化 Session
。
private fun createCameraPreviewSession(camera: CameraDevice) {
mPreviewRequestBuilder = camera.createCaptureRequest(TEMPLATE_PREVIEW)
mPreviewRequestBuilder.addTarget(mSurfaceView.holder.surface)
mPreviewRequestBuilder.addTarget(mImageReader.surface)
camera.createCaptureSession(
listOf(
mSurfaceView.holder.surface,
mImageReader.surface
), mCameraSessionStateCallback, mBackgroundHandler
Session
创建完成后,在 onConfigured
回调中,发送请求。
override fun onConfigured(session: CameraCaptureSession) {
Log.d(TAG, "onConfigured")
session.setRepeatingRequest(
mPreviewRequestBuilder.build(),
object : CameraCaptureSession.CaptureCallback() {},
mBackgroundHandler
最后,由于我们使用了 ImageReader
,所以会在 onImageAvailable
回调中收到图像的回传。
override fun onImageAvailable(reader: ImageReader) {
val image = reader.acquireNextImage()
if (image.format == ImageFormat.YUV_420_888) {
val planes = image.planes
lock.lock()
if (!::y.isInitialized) {
y = ByteArray(planes[0].buffer.limit() - planes[0].buffer.position())
u = ByteArray(planes[1].buffer.limit() - planes[1].buffer.position())
v = ByteArray(planes[2].buffer.limit() - planes[2].buffer.position())
if (planes[0].buffer.remaining() == y.size) {
planes[0].buffer.get(y)
planes[1].buffer.get(u)
planes[2].buffer.get(v)
// 接下来通过转换,可以转换为 Bitmap 进行展示
lock.unlock()
image.close()
这部分的完整代码可以在仓库的 Camera2Helper
类中看到。
代码还是要有的,可以在 GitHub 仓库中找到:https://github.com/T-Oner/MediaPractice
最新更新请关注微信公众号
放牛崽 @自营小店
粉丝