好多开发者纠结,Android平台采集摄像头,到底是用Camera1还是Camera2?实际上,Camera1和Camera2分别对应相机API1和相机API2。Android 5.0开始,已经弃用了Camera API1,新平台重点开发Camera API2,Camera API1 会逐渐被淘汰。Camera API2 框架为应用提供更接近底层的相机控件,包括高效的零复制连拍/视频流以及曝光、增益、白平衡增益、颜色转换、去噪、锐化等方面的每帧控件。
使用Android的Camera2 API来进行相机操作,包括预览、拍照等功能,是一个相对复杂但功能强大的过程。以下是一个基本的步骤指南,帮助你开始使用Camera2 API:
首先,你需要在AndroidManifest.xml
文件中添加必要的权限,以便应用能够访问设备的相机。至少需要添加相机权限:
<uses-permission android:name="android.permission.CAMERA"/>
如果你的应用还需要录制视频或音频,还需要添加相应的权限:
<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)开始,需要在运行时请求这些权限,而不是仅仅在清单文件中声明。
在你的Activity或Fragment中,首先需要获取CameraManager
的实例,这个类是用于管理设备上的相机资源:
CameraManager cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
使用CameraManager
的getCameraIdList()
方法获取设备上所有可用的相机列表,并选择一个相机ID进行后续操作。通常,后置摄像头的ID是"0",前置摄像头的ID是"1",但这不是绝对的,需要根据实际情况判断:
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();
}
使用CameraManager
的openCamera()
方法打开选定的相机。这个方法是异步的,并且需要一个CameraDevice.StateCallback
来接收相机打开的结果:
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);
一旦相机成功打开,你需要创建一个CameraCaptureSession
来进行预览、拍照等操作。这个过程也是异步的,并且需要设置Surface来接收相机数据(如TextureView或SurfaceView):
cameraDevice.createCaptureSession(Arrays.asList(surface), new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(@NonNull CameraCaptureSession session) {
// 会话创建成功,可以开始预览或拍照
}
@Override
public void onConfigureFailed(@NonNull CameraCaptureSession session) {
// 会话配置失败
}
}, null);
在CameraCaptureSession
配置成功后,你可以通过调用setRepeatingRequest()
方法来开始预览,并通过调用capture()
方法来拍照。这些操作都需要CaptureRequest
对象,该对象描述了捕获请求的各种参数:
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);
当相机不再需要时,你应该及时释放相关资源,避免内存泄漏等问题。
我们在做Android平台RTMP推送、轻量级RTSP服务、实时录像和GB28181设备对接模块的时候,都需要用到摄像头采集,早期,我们提供了Camera1的采集demo,后面碎渣Camera2的优势越来越明显,高版本设备已成主流,目前一般建议采用Camera2的采集。
先说针对Camera1的采集和数据投递处理:
/*
* 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的数据投递处理:
/*
* 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技术扫盲和技术探讨,感兴趣的开发者,可以单独跟我沟通探讨。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。