Android Camera2 实战:全屏预览与实时帧处理全解析
2025.09.19 11:24浏览量:0简介:本文深入探讨Android Camera2 API实现全屏预览及实时获取预览帧进行图像处理的技术方案,涵盖Camera2基础架构、全屏预览配置、帧捕获回调机制及实时处理优化策略,为开发者提供完整的实现路径。
Android Camera2 全屏预览+实时获取预览帧进行图像处理
一、Camera2 API基础架构解析
Camera2 API作为Android 5.0引入的全新摄像头控制框架,采用三级管道架构:CameraDevice
(设备层)、CameraCaptureSession
(会话层)、CaptureRequest
(请求层)。这种设计模式使开发者能够精细控制摄像头参数,包括曝光补偿、白平衡、对焦模式等。
1.1 核心组件说明
- CameraManager:系统摄像头服务入口,通过
getCameraIdList()
获取可用设备列表 - CameraCharacteristics:包含设备特性信息(如传感器方向、硬件支持级别)
- Surface:作为图像数据的输出目标,支持
SurfaceView
、TextureView
、ImageReader
等多种类型
1.2 初始化流程
// 1. 获取CameraManager实例
CameraManager manager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
// 2. 选择后置摄像头(通常ID为"0")
String cameraId = manager.getCameraIdList()[0];
// 3. 获取设备特性
CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
// 4. 配置预览Surface
SurfaceTexture surfaceTexture = textureView.getSurfaceTexture();
surfaceTexture.setDefaultBufferSize(1920, 1080);
Surface previewSurface = new Surface(surfaceTexture);
二、全屏预览实现方案
2.1 视图组件选择
- SurfaceView:双缓冲机制,性能最优但Z轴控制困难
- TextureView:支持透明、变换动画,适合UI集成场景
- ImageReader:用于获取原始图像数据,需配合其他视图使用
2.2 最佳实践配置
// 配置预览请求
CaptureRequest.Builder previewBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
previewBuilder.addTarget(previewSurface);
// 设置自动对焦
previewBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
// 优化预览帧率
previewBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE,
new Range<>(30, 30)); // 固定30fps
2.3 屏幕适配策略
计算最佳预览尺寸:
StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
Size[] previewSizes = map.getOutputSizes(SurfaceTexture.class);
// 根据屏幕宽高比选择最接近的尺寸
Size optimalSize = findOptimalSize(previewSizes, screenWidth, screenHeight);
处理方向旋转:
int displayRotation = getWindowManager().getDefaultDisplay().getRotation();
int sensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
int totalRotation = (sensorOrientation + displayRotation + 270) % 360;
三、实时帧捕获与处理
3.1 ImageReader配置要点
// 创建ImageReader(YUV_420_888格式兼容性最好)
ImageReader imageReader = ImageReader.newInstance(
1280, 720, ImageFormat.YUV_420_888, 2);
// 设置帧到达监听
imageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
@Override
public void onImageAvailable(ImageReader reader) {
try (Image image = reader.acquireLatestImage()) {
// 处理YUV数据
processYUVImage(image);
}
}
}, backgroundHandler);
3.2 同步捕获机制
// 创建捕获会话时同时添加预览和图像Surface
List<Surface> outputSurfaces = new ArrayList<>();
outputSurfaces.add(previewSurface);
outputSurfaces.add(imageReader.getSurface());
cameraDevice.createCaptureSession(outputSurfaces,
new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(CameraCaptureSession session) {
try {
// 创建重复请求
CaptureRequest request = previewBuilder.build();
session.setRepeatingRequest(request, null, backgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
}, backgroundHandler);
3.3 YUV数据处理优化
private void processYUVImage(Image image) {
// 获取YUV平面
Image.Plane yPlane = image.getPlanes()[0];
Image.Plane uPlane = image.getPlanes()[1];
Image.Plane vPlane = image.getPlanes()[2];
ByteBuffer yBuffer = yPlane.getBuffer();
ByteBuffer uBuffer = uPlane.getBuffer();
ByteBuffer vBuffer = vPlane.getBuffer();
// 转换为NV21格式(兼容Android Bitmap)
byte[] nv21 = yuv420ToNv21(
yBuffer, uBuffer, vBuffer,
image.getWidth(), image.getHeight()
);
// 创建Bitmap进行显示或处理
YuvImage yuvImage = new YuvImage(nv21, ImageFormat.NV21,
image.getWidth(), image.getHeight(), null);
ByteArrayOutputStream os = new ByteArrayOutputStream();
yuvImage.compressToJpeg(new Rect(0, 0, width, height), 100, os);
Bitmap bitmap = BitmapFactory.decodeByteArray(os.toByteArray(), 0, os.size());
// 释放资源
image.close();
}
四、性能优化策略
4.1 帧率控制方案
- 动态调整目标FPS:根据场景复杂度在15-30fps间切换
- 丢帧策略:当处理耗时超过帧间隔时,主动丢弃后续帧
long lastProcessingTime = 0;
private boolean shouldProcessFrame(long currentTimestamp) {
long elapsed = currentTimestamp - lastProcessingTime;
if (elapsed < 33_000_000) { // 30fps间隔
return false;
}
lastProcessingTime = currentTimestamp;
return true;
}
4.2 线程模型设计
- 双线程架构:
- 摄像头线程:负责帧捕获和基础处理
- 渲染线程:负责UI更新和复杂计算
```java
// 创建带缓冲的线程池
ExecutorService cameraExecutor = Executors.newFixedThreadPool(2);
ExecutorService renderExecutor = Executors.newSingleThreadExecutor();
// 帧处理流程
imageReader.setOnImageAvailableListener(image -> {
cameraExecutor.execute(() -> {
// 基础处理
byte[] processedData = preProcess(image);
renderExecutor.execute(() -> {
// UI更新
updateUI(processedData);
});
});
}, backgroundHandler);
### 4.3 内存管理技巧
- 使用`ImageReader`的`setMaxImages()`控制缓存数量
- 及时关闭不再使用的`Image`对象
- 采用对象池模式复用处理资源
## 五、常见问题解决方案
### 5.1 预览变形问题
**原因**:SurfaceView/TextureView尺寸与摄像头输出尺寸不匹配
**解决**:
```java
// 在SurfaceHolder.Callback中动态调整
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
if (cameraCaptureSession != null) {
try {
// 重新配置会话
cameraCaptureSession.stopRepeating();
configurePreviewSize(width, height);
startPreview();
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
}
5.2 帧丢失问题
诊断步骤:
- 检查
CameraCaptureSession.CaptureCallback
中的onCaptureFailed()
回调 - 监控
CameraDevice.StateCallback
中的错误状态 - 使用
CameraCharacteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES)
验证设备能力
优化方案:
- 降低输出分辨率
- 减少同时处理的Surface数量
- 检查后台应用资源占用
六、高级功能扩展
6.1 多摄像头同步
// 获取物理摄像头ID列表
String[] physicalCameraIds = manager.getCameraIdList();
// 创建逻辑摄像头设备(Android 9+)
CameraDevice logicalDevice = ...; // 通过CameraManager.openCamera()创建
// 配置跨摄像头会话
List<Surface> multiSurfaces = new ArrayList<>();
multiSurfaces.add(wideAngleSurface);
multiSurfaces.add(telephotoSurface);
logicalDevice.createCaptureSession(multiSurfaces, ...);
6.2 深度数据获取
// 检查深度功能支持
Integer depthAvailable = characteristics.get(
CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES
).contains(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT);
if (depthAvailable != null && depthAvailable) {
// 配置深度输出
Size depthSize = characteristics.get(
CameraCharacteristics.DEPTH_OUTPUT_SIZES_SUPPORTED
)[0];
ImageReader depthReader = ImageReader.newInstance(
depthSize.getWidth(), depthSize.getHeight(),
ImageFormat.DEPTH16, 2
);
}
七、完整代码示例
public class Camera2PreviewProcessor {
private CameraDevice cameraDevice;
private CameraCaptureSession captureSession;
private ImageReader imageReader;
private Handler backgroundHandler;
public void startCamera(Context context, TextureView textureView) {
// 1. 初始化组件
CameraManager manager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
String cameraId = manager.getCameraIdList()[0];
// 2. 配置预览Surface
SurfaceTexture surfaceTexture = textureView.getSurfaceTexture();
surfaceTexture.setDefaultBufferSize(1920, 1080);
Surface previewSurface = new Surface(surfaceTexture);
// 3. 配置ImageReader
imageReader = ImageReader.newInstance(640, 480,
ImageFormat.YUV_420_888, 2);
// 4. 打开摄像头
try {
manager.openCamera(cameraId, new CameraDevice.StateCallback() {
@Override
public void onOpened(CameraDevice device) {
cameraDevice = device;
startPreviewSession(previewSurface);
}
// 其他回调方法...
}, backgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
private void startPreviewSession(Surface previewSurface) {
try {
// 创建捕获请求构建器
CaptureRequest.Builder previewBuilder =
cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
previewBuilder.addTarget(previewSurface);
previewBuilder.addTarget(imageReader.getSurface());
// 创建捕获会话
List<Surface> surfaces = Arrays.asList(previewSurface, imageReader.getSurface());
cameraDevice.createCaptureSession(surfaces,
new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(CameraCaptureSession session) {
captureSession = session;
try {
session.setRepeatingRequest(
previewBuilder.build(), null, backgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
// 其他回调方法...
}, backgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
// 图像处理回调
imageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
@Override
public void onImageAvailable(ImageReader reader) {
try (Image image = reader.acquireLatestImage()) {
// 在此实现图像处理逻辑
processImage(image);
}
}
}, backgroundHandler);
}
八、最佳实践总结
- 资源管理:始终在
onPause()
中释放摄像头资源 - 错误处理:实现完整的
CameraDevice.StateCallback
和CameraCaptureSession.StateCallback
- 权限管理:动态请求
CAMERA
和WRITE_EXTERNAL_STORAGE
权限 - 兼容性处理:检查
CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL
- 性能监控:使用
System.nanoTime()
测量帧处理耗时
通过以上技术方案,开发者可以实现稳定的Camera2全屏预览,并构建高效的实时图像处理流水线。实际开发中需根据具体设备特性进行参数调优,建议使用Camera2 API Interop Test
应用进行兼容性测试。
发表评论
登录后可评论,请前往 登录 或 注册