【安卓】Android音视频 - Camera+AudioRecord采集数据

前情提要

在上一篇文章中,放了一张音视频学习路线图,后面我会尽量按照这个路线图进行博客更新,首先我们来谈一谈输入,在Android上的输入源无非是本地已有的音视频文件或者通过音视频设备(相机 录音)采集的原生数据。本章我们来分析如何在Android上通过Camera以及录音设备采集数据。

Camera

在Android上的视频采集设备无疑就是Camera了,在Android SDK API21之前的版本只能使用Camera1 ,在 API 21之后Camera1已经被标记为Deprecated ,Google 推荐使用Camera2,下面我们来分别看一下

Camera1

我们先来看一下Camera1体系的部分类图
【安卓】Android音视频 - Camera+AudioRecord采集数据
Camera类是Camera1体系的核心类,该类还有好多内部类,打开Camera、设置参数、设置预览等都是使用该类的API,下面我们来看使用Camera API打开系统照相机的流程。

【安卓】Android音视频 - Camera+AudioRecord采集数据
有了上面的流程图之后,我们上代码

//启动Camera

private fun setUpCamera() {

//【1】 获取Camera Id

val id = getCurrentCameraId()

try {

//【2】 根据Camera Id 获取Camera 实例

cameraInstance = getCameraInstance(id)

} catch (e: IllegalAccessError) {

Log.e(TAG, "Camera not found")

return

}

//【3】 调用Camera API 设置Camera参数

val parameters = cameraInstance!!.parameters

if (parameters.supportedFocusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {

parameters.focusMode = Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE

}

cameraInstance!!.parameters = parameters

//【4】 调用Camera API 设置预览Surface

surfaceHolder?.let { cameraInstance!!.setPreviewDisplay(it) }

//【5】 调用Camera API设置预览回调

cameraInstance!!.setPreviewCallback { data, camera ->

if (data == null || camera == null) {

[email protected]

}

val size = camera.parameters.previewSize

onPreviewFrame?.invoke(data, size.width, size.height)

}

//【6】 调用Camera API开启预览

cameraInstance!!.startPreview()

}

上面代码中的【3】【4】【5】【6】都是调用Camera类的API来完成,我们来看【1】【2】的具体步骤

/**

*获取Camera Id

*/

private fun getCurrentCameraId(): Int {

val cameraInfo = Camera.CameraInfo()

//遍历所有的Camera id,比较CameraInfo facing

for (id in 0 until Camera.getNumberOfCameras()) {

Camera.getCameraInfo(id, cameraInfo)

if (cameraInfo.facing == cameraFacing) {

return id

}

}

return 0

}

/**

*获取Camera 实例

*/

private fun getCameraInstance(id: Int): Camera {

return try {

//调用Camera的open函数获取Camera的实例

Camera.open(id)

} catch (e: Exception) {

throw IllegalAccessError("Camera not found")

}

}

/**

*释放Camera

*/

private fun releaseCamera() {

cameraInstance!!.setPreviewCallback(null)

cameraInstance!!.release()

cameraInstance = null

}

经过上面的流程之后,Camera的预览会显示在传入的Surface上,并且在Camera停止前会一直回调函数onPreviewFrame(byte[] data,Camera camera),其中byte[] data中存储的就是实时的YUV图像数据,关于YUV图像数据我们在Android音视频开篇的时候就已经说过了,我们知道YUV也有很多格式,byte[] data的格式是YUV格式中的NV21

Camera2

在Andorid SDK API 21之后呢,Google就推荐使用Camera2体系来管理设备,Camera2还是与Camera1有很大的不同的。
一样的,我们先来看一下Camera2体系的部分类图

【安卓】Android音视频 - Camera+AudioRecord采集数据

Camera2要比Camera1复杂的多,CameraManager CameraCaptureSession是Camera2体系的核心类,CameraManager 用来管理摄像头的打开和关闭 Camera2 引入了CameraCaptureSession来管理拍摄会话。

我们下面来看一下更详细的流程图

【安卓】Android音视频 - Camera+AudioRecord采集数据
同样的上代码:

      private fun setUpCamera() {

//【1】获取Camera Id

val cameraId = getCameraId(cameraFacing) ?: return

try {

//【2】打开Camera,传入的 CameraDeviceCallback()是摄像机设备状态回调

cameraManager.openCamera(cameraId, CameraDeviceCallback(), null)

} catch (e: CameraAccessException) {

Log.e(TAG, "Opening camera (ID: $cameraId) failed.")

}

}

//设备状态回调

private inner class CameraDeviceCallback : CameraDevice.StateCallback() {

override fun onOpened(camera: CameraDevice) {

cameraInstance = camera

//【3】开启拍摄会话

startCaptureSession()

}

override fun onDisconnected(camera: CameraDevice) {

camera.close()

cameraInstance = null

}

override fun onError(camera: CameraDevice, error: Int) {

camera.close()

cameraInstance = null

}

}

 //【3】开启拍摄会话

private fun startCaptureSession() {

val size = chooseOptimalSize()

//创建ImageRender并设置回调

imageReader =

ImageReader.newInstance(size.width, size.height, ImageFormat.YUV_420_888, 2).apply {

setOnImageAvailableListener({ reader ->

val image = reader?.acquireNextImage() ?: [email protected]

onPreviewFrame?.invoke(image.generateNV21Data(), image.width, image.height)

image.close()

}, null)

}

try {

if (surfaceHolder == null) {

//设置ImageRender的surface给cameraInstance,以便后面预览的时候数据呈现到ImageRender的surface,从而触发ImageRender的回调

cameraInstance?.createCaptureSession(

listOf(imageReader!!.surface),

//【4】CaptureStateCallback是CameraCaptureSession的内部类,是摄像机会话状态的回调

CaptureStateCallback(),

null

)

} else {

cameraInstance?.createCaptureSession(

listOf(imageReader!!.surface,

surfaceHolder!!.surface),

CaptureStateCallback(),

null

)

}

} catch (e: CameraAccessException) {

Log.e(TAG, "Failed to start camera session")

}

}

//摄像机会话状态的回调

private inner class CaptureStateCallback : CameraCaptureSession.StateCallback() {

override fun onConfigureFailed(session: CameraCaptureSession) {

Log.e(TAG, "Failed to configure capture session.")

}

//摄像机配置完成

override fun onConfigured(session: CameraCaptureSession) {

cameraInstance ?: return

captureSession = session

//设置预览CaptureRequest.Builder

val builder = cameraInstance!!.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)

builder.addTarget(imageReader!!.surface)

surfaceHolder?.let {

builder.addTarget(it.surface)

}

try {

//开启会话

session.setRepeatingRequest(builder.build(), null, null)

} catch (e: CameraAccessException) {

Log.e(TAG, "Failed to start camera preview because it couldn't access camera", e)

} catch (e: IllegalStateException) {

Log.e(TAG, "Failed to start camera preview.", e)

}

}

}

    /**

*【1】 获取Camera Id

*/

private fun getCameraId(facing: Int): String? {

return cameraManager.cameraIdList.find { id ->

cameraManager.getCameraCharacteristics(id).get(CameraCharacteristics.LENS_FACING) == facing

}

}

我们分析了上面的Camera采集数据,完整的代码请看文末的Github 地址

AudioRecord

上面分析完了视频,我们接着来看音频,录音API我们使用AudioRecord,录音的流程相对于视频而言要简单许多,一样的,我们先来看一下简单类图
【安卓】Android音视频 - Camera+AudioRecord采集数据
就一个类,API也简单明了,我们来看一下流程
【安卓】Android音视频 - Camera+AudioRecord采集数据

下面上代码

     public void startRecord() {

//开启录音

mAudioRecord.startRecording();

mIsRecording = true;

//开启新线程轮询

ExecutorService executorService = Executors.newSingleThreadExecutor();

executorService.execute(new Runnable() {

@Override

public void run() {

byte[] buffer = new byte[DEFAULT_BUFFER_SIZE_IN_BYTES];

while (mIsRecording) {

int len = mAudioRecord.read(buffer, 0, DEFAULT_BUFFER_SIZE_IN_BYTES);

if (len > 0) {

byte[] data = new byte[len];

System.arraycopy(buffer, 0, data, 0, len);

//处理data

}

}

}

});

}

public void stopRecord() {

mIsRecording = false;

mAACMediaCodecEncoder.stopEncoder();

mAudioRecord.stop();

}

小结

本章我们对音视频的原生输入API进行了详细的介绍,这个也是我们后面博客的基础,有了YUV和PCM数据之后,就可以编码了,下一篇我们再来分析MediaCodec,用MediaCodec对原生音视频数据进行硬编码生成Mp4.

Camera
AudioRecord

以上是 【安卓】Android音视频 - Camera+AudioRecord采集数据 的全部内容, 来源链接: utcz.com/a/97993.html

回到顶部