Android 基于 Camera1 API 实现视频采集与画面预览
Camera1 API 概述
在 Android 系统中,android.hardware.Camera(通常称为 Camera1)是早期用于控制设备摄像头硬件的核心类。尽管自 API 21 起官方已将其标记为废弃并推荐使用 Camera2,但在许多遗留项目或特定兼容性场景中,Camera1 依然被广泛使用。它主要负责配置相机参数、管理预览流、捕获静态图像以及获取视频编码帧。
权限声明与动态申请
调用摄像头硬件前,必须在 AndroidManifest.xml 中声明相机权限:
<uses-permission android:name="android.permission.CAMERA" />
对于 Android 6.0(API 23)及以上版本,还需要在运行时动态申请权限。以下是优化后的权限请求逻辑:
private static final int REQ_CODE_PERMISSIONS = 1001;
private final String[] requiredPermissions = {
Manifest.permission.CAMERA,
Manifest.permission.RECORD_AUDIO,
Manifest.permission.WRITE_EXTERNAL_STORAGE
};
private void verifyAndRequestPermissions() {
List<String> missingPermissions = new ArrayList<>();
for (String perm : requiredPermissions) {
if (ContextCompat.checkSelfPermission(this, perm) != PackageManager.PERMISSION_GRANTED) {
missingPermissions.add(perm);
}
}
if (!missingPermissions.isEmpty()) {
ActivityCompat.requestPermissions(this,
missingPermissions.toArray(new String[0]), REQ_CODE_PERMISSIONS);
}
}
Camera1 核心工作流程
使用 Camera1 进行视频采集和预览的标准生命周期如下:
- 通过
Camera.open(int)实例化相机对象并连接指定硬件。 - 调用
getParameters()获取当前相机的配置参数。 - 修改配置后,通过
setParameters(Camera.Parameters)应用新设置。 - 使用
setDisplayOrientation(int)调整预览画面的旋转角度。 - 调用
setPreviewDisplay(SurfaceHolder)将相机预览流绑定到 UI 表面。 - 执行
startPreview()开启预览,这是进行拍照或录像的前置条件。 - 如需拍照,可调用
takePicture()触发异步图像捕获(注意:拍照后预览会自动停止)。 - 调用
stopPreview()停止预览流的渲染。 - 在 Activity 销毁或暂停时,务必调用
release()释放相机资源,防止内存泄漏和硬件占用。
线程安全提示:Camera1 并非线程安全类,其所有操作应在同一个事件线程(通常是主线程)中执行。耗时的操作(如对焦、拍照)会通过回调异步返回结果。
预览与采集代码实现
下面展示如何结合 SurfaceView 实现相机的初始化、预览开启与资源释放。代码对变量命名和结构进行了重构,以提升可读性与模块化程度:
public class CameraPreviewManager implements SurfaceHolder.Callback {
private Camera deviceCamera;
private SurfaceHolder displayHolder;
private boolean isPreviewing = false;
public CameraPreviewManager(SurfaceView targetSurfaceView) {
this.displayHolder = targetSurfaceView.getHolder();
this.displayHolder.addCallback(this);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
initializeAndStartPreview();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
// Surface 尺寸变化时的处理逻辑
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
releaseCameraResources();
}
private void initializeAndStartPreview() {
try {
deviceCamera = Camera.open();
Camera.Parameters config = deviceCamera.getParameters();
// 设置预览数据格式为 NV21
config.setPreviewFormat(ImageFormat.NV21);
deviceCamera.setParameters(config);
// 绑定 Surface 并调整预览方向
deviceCamera.setPreviewDisplay(displayHolder);
deviceCamera.setDisplayOrientation(90);
// 设置预览回调以获取视频帧数据
deviceCamera.setPreviewCallback((data, camera) -> {
// 在此处处理采集到的 NV21 视频帧数据
});
deviceCamera.startPreview();
isPreviewing = true;
} catch (IOException e) {
Log.e("CameraManager", "预览启动失败", e);
}
}
public void releaseCameraResources() {
if (deviceCamera != null) {
if (isPreviewing) {
deviceCamera.stopPreview();
isPreviewing = false;
}
deviceCamera.setPreviewCallback(null);
deviceCamera.release();
deviceCamera = null;
}
}
}