Android摄像头采集选Camera1还是Camera2?

​Camera1还是Camera2?

好多开发者纠结,Android平台采集摄像头,到底是用Camera1还是Camera2?实际上,Camera1和Camera2分别对应相机API1和相机API2。Android 5.0开始,已经弃用了Camera API1,新平台重点开发Camera API2,Camera API1 会逐渐被淘汰。Camera API2 框架为应用提供更接近底层的相机控件,包括高效的零复制连拍/视频流以及曝光、增益、白平衡增益、颜色转换、去噪、锐化等方面的每帧控件。

Camera2 相对Camera1有哪些优势?

1. 更灵活的相机控制

  • 细粒度参数调整:Camera2 API提供了更多的相机参数和配置选项,开发者可以更精确地控制相机的焦距、曝光时间、ISO感光度、白平衡等,以满足不同场景下的需求。
  • 多摄像头支持:能够同时管理和控制多个相机设备,包括前置摄像头、后置摄像头以及其他可用的摄像头,提高了系统的灵活性和扩展性。

2. 更高的性能

  • 异步操作:Camera2 API使用异步操作模式,减少了对UI线程的阻塞,提高了应用的响应速度和流畅性。
  • 并发访问:支持并发访问和操作多个相机设备,提高了相机的利用率和整体性能。

3. 更好的图像处理能力

  • RAW图像捕获:支持原生的RAW图像捕获和处理,开发者可以获取到相机传感器的原始数据,进行更高质量的图像处理和分析。
  • 实时预览和后处理:提供实时的图像预览和后处理功能,有助于开发者实现更丰富的图像效果。

4. 更好的适配性和扩展性

  • 统一的接口和架构:Camera2 API提供了更统一的接口和更清晰的架构,便于开发者进行相机功能的开发和适配。
  • 功能扩展:允许开发者通过CameraCharacteristics和CaptureRequest等接口获取和设置摄像头的各种功能和参数,支持自定义功能的开发。

5. 广泛的应用场景

  • 相机应用开发:Camera2 API提供了丰富的相机控制接口,可以用于开发各种相机应用,如拍照、录像、实时滤镜等。
  • 视频通话和实时视频应用:支持高质量的视频捕获和处理,适用于视频通话、视频会议等实时视频应用。
  • 计算机视觉和图像处理:结合OpenCV等图像处理库,可以进行实时的图像分析、特征提取、目标识别等计算机视觉任务。

如何使用Camera2进行相机操作

使用Android的Camera2 API来进行相机操作,包括预览、拍照等功能,是一个相对复杂但功能强大的过程。以下是一个基本的步骤指南,帮助你开始使用Camera2 API:

1. 添加权限

首先,你需要在AndroidManifest.xml文件中添加必要的权限,以便应用能够访问设备的相机。至少需要添加相机权限:

ini 复制代码
<uses-permission android:name="android.permission.CAMERA"/>

如果你的应用还需要录制视频或音频,还需要添加相应的权限:

ini 复制代码
<uses-permission android:name="android.permission.RECORD_AUDIO"/>  
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>  
<uses-feature android:name="android.hardware.camera" android:required="true"/>  
<uses-feature android:name="android.hardware.camera.autofocus"/>

需要注意的是,从Android 6.0(API 级别 23)开始,需要在运行时请求这些权限,而不是仅仅在清单文件中声明。

2. 初始化CameraManager

在你的Activity或Fragment中,首先需要获取CameraManager的实例,这个类是用于管理设备上的相机资源:

ini 复制代码
CameraManager cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);

3. 获取相机ID

使用CameraManagergetCameraIdList()方法获取设备上所有可用的相机列表,并选择一个相机ID进行后续操作。通常,后置摄像头的ID是"0",前置摄像头的ID是"1",但这不是绝对的,需要根据实际情况判断:

ini 复制代码
try {  
    String[] cameraIdList = cameraManager.getCameraIdList();  
    for (String cameraId : cameraIdList) {  
        CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(cameraId);  
        // 根据需要选择合适的相机,例如选择后置摄像头  
        if (characteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_BACK) {  
            // 选择后置摄像头  
            break;  
        }  
    }  
} catch (CameraAccessException e) {  
    e.printStackTrace();  
}

4. 打开相机

使用CameraManageropenCamera()方法打开选定的相机。这个方法是异步的,并且需要一个CameraDevice.StateCallback来接收相机打开的结果:

less 复制代码
cameraManager.openCamera(cameraId, new CameraDevice.StateCallback() {  
    @Override  
    public void onOpened(@NonNull CameraDevice cameraDevice) {  
        // 相机打开成功,可以进行后续操作  
    }  
  
    @Override  
    public void onDisconnected(@NonNull CameraDevice cameraDevice) {  
        // 相机断开连接  
    }  
  
    @Override  
    public void onError(@NonNull CameraDevice cameraDevice, int error) {  
        // 打开相机发生错误  
    }  
}, null);

5. 创建CameraCaptureSession

一旦相机成功打开,你需要创建一个CameraCaptureSession来进行预览、拍照等操作。这个过程也是异步的,并且需要设置Surface来接收相机数据(如TextureView或SurfaceView):

less 复制代码
cameraDevice.createCaptureSession(Arrays.asList(surface), new CameraCaptureSession.StateCallback() {  
    @Override  
    public void onConfigured(@NonNull CameraCaptureSession session) {  
        // 会话创建成功,可以开始预览或拍照  
    }  
  
    @Override  
    public void onConfigureFailed(@NonNull CameraCaptureSession session) {  
        // 会话配置失败  
    }  
}, null);

6. 预览和拍照

CameraCaptureSession配置成功后,你可以通过调用setRepeatingRequest()方法来开始预览,并通过调用capture()方法来拍照。这些操作都需要CaptureRequest对象,该对象描述了捕获请求的各种参数:

ini 复制代码
CaptureRequest.Builder previewRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);  
// 设置预览的参数...  
previewRequestBuilder.addTarget(surface);  
  
// 预览  
cameraCaptureSession.setRepeatingRequest(previewRequestBuilder.build(), null, null);  
  
// 拍照  
CaptureRequest.Builder captureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);  
// 设置拍照的参数...  
captureRequestBuilder.addTarget(imageReader.getSurface());  
cameraCaptureSession.capture(captureRequestBuilder.build(), captureCallback, null);

7. 释放资源

当相机不再需要时,你应该及时释放相关资源,避免内存泄漏等问题。

如何从Camera1和Camera2获取原始数据?

我们在做Android平台RTMP推送、轻量级RTSP服务、实时录像和GB28181设备对接模块的时候,都需要用到摄像头采集,早期,我们提供了Camera1的采集demo,后面碎渣Camera2的优势越来越明显,高版本设备已成主流,目前一般建议采用Camera2的采集。

先说针对Camera1的采集和数据投递处理:

ini 复制代码
/*
 * CameraPublishActivity.java
 * Author: daniusdk.com
 * WeChat: xinsheng120
 */
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
	frameCount++;
	if (frameCount % 3000 == 0) {
		Log.i("OnPre", "gc+");
		System.gc();
		Log.i("OnPre", "gc-");
	}

	if (data == null) {
		Parameters params = camera.getParameters();
		Size size = params.getPreviewSize();
		int bufferSize = (((size.width | 0x1f) + 1) * size.height * ImageFormat.getBitsPerPixel(params.getPreviewFormat())) / 8;
		camera.addCallbackBuffer(new byte[bufferSize]);
	} else {
		if (isRTSPPublisherRunning || isPushingRtmp || isRecording || isPushingRtsp) {
			if (1 == video_opt_) {
				 
				libPublisher.SmartPublisherOnCaptureVideoData(publisherHandle, data, data.length, currentCameraType, currentOrigentation);

			} else if (3 == video_opt_) {
				int w = videoWidth, h = videoHeight;
				int y_stride = videoWidth, uv_stride = videoWidth;
				int y_offset = 0, uv_offset = videoWidth * videoHeight;
				int is_vertical_flip = 0, is_horizontal_flip = 0;
				int rotation_degree = 0;

				// 镜像只用在前置摄像头场景下
				if (is_mirror && FRONT == currentCameraType) {
					// 竖屏, (垂直翻转->顺时旋转270度)等价于(顺时旋转旋转270度->水平翻转)
					if (PORTRAIT == currentOrigentation)
						is_vertical_flip = 1;
					else
						is_horizontal_flip = 1;
				}

				if (PORTRAIT == currentOrigentation) {
					if (BACK == currentCameraType)
						rotation_degree = 90;
					else
						rotation_degree = 270;
				} else if (LANDSCAPE_LEFT_HOME_KEY == currentOrigentation) {
					rotation_degree = 180;
				}

				int scale_w = 0, scale_h = 0, scale_filter_mode = 0;

				libPublisher.PostLayerImageNV21ByteArray(publisherHandle, 0, 0, 0,
						data, y_offset, y_stride, data, uv_offset, uv_stride, w, h,
						is_vertical_flip, is_horizontal_flip, scale_w, scale_h, scale_filter_mode, rotation_degree);
			}
		}

		camera.addCallbackBuffer(data);
	}
}

再说针对Camera2的数据投递处理:

ini 复制代码
/*
 * Camera2Activity.java
 * Author: daniusdk.com
 * WeChat: xinsheng120
 */
@Override
public void onCameraImageData(Image image) {
	Image.Plane[] planes = image.getPlanes();

	int w = image.getWidth(), h = image.getHeight();
	int y_offset = 0, u_offset = 0, v_offset = 0;

	int scale_w = 0, scale_h = 0, scale_filter_mode = 0;
	scale_filter_mode = 3;

	int rotation_degree = cameraImageRotationDegree_;
	if (rotation_degree < 0) {
		Log.i(TAG, "onCameraImageData rotation_degree < 0, may need to set orientation_ to 0, 90, 180 or 270");
		return;
	}

	for (LibPublisherWrapper i : publisher_array_)
		i.PostLayerImageYUV420888ByteBuffer(0, 0, 0,
			planes[0].getBuffer(), y_offset, planes[0].getRowStride(),
			planes[1].getBuffer(), u_offset, planes[1].getRowStride(),
			planes[2].getBuffer(), v_offset, planes[2].getRowStride(), planes[1].getPixelStride(),
			w, h, 0, 0,
			scale_w, scale_h, scale_filter_mode, rotation_degree);

}

总结

Android Camera2 API控制更灵活,性能、图像处理能力优异、适配性和扩展性也好,在版本支持的前提下,一般建议采用Camera2实现摄像头采集技术诉求,以上是Camera1和Camera2技术扫盲和技术探讨,感兴趣的开发者,可以单独跟我沟通探讨。

相关推荐
aqi0010 天前
FFmpeg开发笔记(五十二)移动端的国产视频播放器GSYVideoPlayer
android·ffmpeg·音视频·直播·流媒体
aqi0011 天前
FFmpeg开发笔记(五十一)适合学习研究的几个音视频开源框架
ffmpeg·音视频·直播·流媒体
aqi0017 天前
FFmpeg开发笔记(五十)聊聊几种流媒体传输技术的前世今生
ffmpeg·音视频·直播·流媒体
aqi0018 天前
FFmpeg开发笔记(四十九)助您在毕业设计中脱颖而出的几个流行APP
ffmpeg·音视频·直播·流媒体
aqi001 个月前
FFmpeg开发笔记(四十八)从0开始搭建直播系统的开源软件架构
ffmpeg·音视频·直播·流媒体
aqi001 个月前
FFmpeg开发笔记(四十七)寒冬下安卓程序员的几个技术转型发展方向
ffmpeg·音视频·直播·流媒体
aqi001 个月前
FFmpeg开发笔记(四十六)利用SRT协议构建手机APP的直播Demo
ffmpeg·音视频·直播·流媒体
x007xyz1 个月前
前端纯手工绘制音频波形图
前端·音视频开发·canvas
aqi001 个月前
FFmpeg开发笔记(四十五)使用SRT Streamer开启APP直播推流
ffmpeg·音视频·直播·流媒体