以前学习了图片和音频,此次咱们尝试使用 Android Camera API 获取到视频数据。git
此次使用的 API 是 Camera2
。Camera2
是 Google 在 Android L 以后推出的全新的相机 API。Camera2
支持的功能要比 Camera
丰富不少,可是相应的,也增长了 API 的使用难度。github
这是使用 Camera2 打开相机获取预览数据的流程图:微信
NV21
是 YUV420p
的一种存储模式。存储顺序是先存 Y
,再存 U
,而后再 VU
交替存储。session
那么问题来了,YUV 是啥?ide
这里简要介绍下,后续能够专门一篇文章来介绍,固然你也能够在网上寻找其余资料来了解这个。学习
YUV 是一种颜色编码方法,主要应用于电视系统和模拟视频领域。其中 YUV 表明三个份量,Y
表明明亮度,U
和 V
表示的是色度。ui
此次咱们关注的是获取视频数据,因此对于相机相关的一些东西不会涉及。编码
CameraManager
:摄像头的管理类。 CameraCharacteristics
:用于描述特定摄像头所支持的特性。 CameraDevice
:表明摄像头。 CameraCaptureSession
:相机实际的控制端,咱们须要在相机上作什么操做,都是由这个类发出相应的指令。 CameraRequest
:每次发起捕获请求的时候都须要传递这个对象,这个类表明了一次捕获请求,用于描述捕获的各类参数。 此次咱们要获取视频数据,还有一个类很重要:spa
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,
2
)
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
最新更新请关注微信公众号