Android Camere开发入门(1):初识Camera
初步了解
在Android开发中,相机(Camera)是一个常见而重要的功能模块。它允许我们通过设备的摄像头捕捉照片和录制视频,为我们的应用程序增加图像处理和视觉交互的能力。
随着Android系统的不断发展和更新,相机功能也不断改进和增强。每个相机版本都引入了新的特性、API和性能改进,以提供更好的用户体验和更强大的功能。
本篇内容先从Camera1的初始化、预览、拍照以及帧回调来和大家一起践行开发工作,从实践中去了解Camera1的功能特性。
初始化相机
在使用Camera1 API之前,我们首先需要进行相机的初始化。这涉及到获取相机实例、设置相机参数,以及配置相机的预览界面。通过使用Camera类和相关的回调接口,我们可以实现与相机的交互。
从简入繁,下面先来认识camera1最简单的初始化
// 定义相机实例
private Camera mCamera;
private int width = 1920;
private int height = 1080;
// 初始化相机方法,注意要想让代码跑起来,要钱申请Camera权限
private void initCamera() {
try {
// 获取相机实例
mCamera = Camera.open();
// 设置相机参数
Camera.Parameters parameters = mCamera.getParameters();
// 设置自动对焦模式
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
// 设置相机预览图像的尺寸
parameters.setPreviewSize(width, height);
// 设置其他相机参数...
// 将参数应用到相机
mCamera.setParameters(parameters);
// 这里使用surfaceTexture来承载相机的预览,而不需要设置一个可见的view
mCamera.setPreviewTexture(new SurfaceTexture(0));
// 启动相机预览
mCamera.startPreview();
} catch (Exception e) {
// 处理相机初始化异常
e.printStackTrace();
}
}
// 停止相机预览并释放相机资源
private void releaseCamera() {
if (mCamera != null) {
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
}
kotlin
// 定义相机实例
var mCamera: Camera? = null
val surfaceTexture = SurfaceTexture(0)
val width = 1920
val height = 1080
// 初始化相机方法,注意要想让代码跑起来,要先申请相机权限
private fun initCamera() {
try {
// 获取相机实例
mCamera = Camera.open()
// 设置相机参数
val parameters = mCamera?.parameters
// 设置自动对焦模式
parameters?.focusMode = Camera.Parameters.FOCUS_MODE_AUTO
// 设置相机预览图像的尺寸
parameters?.setPreviewSize(width, height)
// 设置其他相机参数...
// 将参数应用到相机
mCamera?.parameters = parameters
// 使用SurfaceTexture来承载相机的预览,而不需要设置一个可见的View
mCamera?.setPreviewTexture(surfaceTexture)
// 启动相机预览
mCamera?.startPreview()
} catch (e: Exception) {
// 处理相机初始化异常
e.printStackTrace()
}
}
// 停止相机预览并释放相机资源
private fun releaseCamera() {
mCamera?.apply {
stopPreview()
release()
}
mCamera = null
}
预览相机
预览功能是相机应用程序的核心之一。通过设置预览界面,我们可以实时显示摄像头捕捉到的图像。在Camera1 API中,我们可以使用SurfaceHolder或SurfaceTexture来获取和控制预览视图,设置预览显示和启动预览过程。
下面具体来看SurfaceHolder和SurfaceTexture的差异
//设置TextureView回调监听
binding!!.cameraTextureView.surfaceTextureListener = this@SurfaceTextureActivity
//TextureView回调方法
override fun onSurfaceTextureAvailable(surface: SurfaceTexture, width: Int, height: Int) {
initCamera()
}
override fun onSurfaceTextureSizeChanged(surface: SurfaceTexture, width: Int, height: Int) {
}
override fun onSurfaceTextureDestroyed(surface: SurfaceTexture): Boolean {
releaseCamera()
return true
}
override fun onSurfaceTextureUpdated(surface: SurfaceTexture) {
}
//设置SurfaceView回调监听
binding.cameraSurfaceView.getHolder().addCallback(JavaSurfaceViewActivity.this);
//SurfaceView的SurfaceHolder.Callback回调
override fun surfaceCreated(holder: SurfaceHolder) {
initCamera()
}
override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
}
override fun surfaceDestroyed(holder: SurfaceHolder) {
releaseCamera()
}
//有别与上面的初始化内容,这里我们添加了自适应角度设置
mCamera?.setDisplayOrientation(getDisplayOrientation())
//绑定SurfaceView预览控件
mCamera.setPreviewDisplay(surfaceView.getHolder());
//绑定TextureView预览控件
mCamera.setPreviewTexture(binding?.cameraTextureView?.surfaceTexture)
/**
* 自适应相机角度
* @return
*/
private fun getDisplayOrientation(): Int {
val windowManager = this.getSystemService(WINDOW_SERVICE) as WindowManager
val display = windowManager.defaultDisplay
val rotation = display.rotation
var degrees = 0
when (rotation) {
Surface.ROTATION_0 -> degrees = 0
Surface.ROTATION_90 -> degrees = 90
Surface.ROTATION_180 -> degrees = 180
Surface.ROTATION_270 -> degrees = 270
}
val cameraInfo = CameraInfo()
Camera.getCameraInfo(CameraInfo.CAMERA_FACING_BACK, cameraInfo)
var result: Int
if (cameraInfo.facing == CameraInfo.CAMERA_FACING_FRONT) {
result = (cameraInfo.orientation + degrees) % 360
result = (360 - result) % 360 // compensate the mirror
} else {
result = (cameraInfo.orientation - degrees + 360) % 360
}
return result
}
不同的预览控件有点小差异,具体代码已同步Gitee和GitHub
拍摄照片
作为相机应用程序不可或缺的功能,拍照功能在Camera1 API中也得到了很好的支持。我们将学习如何设置相机参数以及捕捉高质量的照片。通过使用Camera类的takePicture()方法,我们可以触发相机拍摄照片的过程,并获取拍摄得到的图像数据。
获取图片的方式不止takePicture,你也可以通过帧回调进行保存或者直接调用拍照Intent
//给按钮设置一个点击事件,点击时创建图片保存目录并生成图片名称并调用拍照方法
File path = new File(this.getExternalFilesDir(null).getAbsolutePath() + "/img/");
if (!path.exists()) path.mkdirs();
binding.takePictureBtn.setOnClickListener(v -> {
pictureFile = new File(path, s2.format(new Date()) + ".jpg");
takePicture();
});
//拍照
private SimpleDateFormat s2 = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss");
private File pictureFile = null;
private void takePicture() {
mCamera.takePicture(null, null, (data, camera) -> {
// 将照片数据保存为JPG文件
if (pictureFile == null) {
Log.d(TAG, "Error creating media file, check storage permissions");
return;
}
try {
FileOutputStream fos = new FileOutputStream(pictureFile);
fos.write(data);
fos.close();
Log.d(TAG, "Picture saved: " + pictureFile.getAbsolutePath());
} catch (FileNotFoundException e) {
Log.d(TAG, "File not found: " + e.getMessage());
} catch (IOException e) {
Log.d(TAG, "Error accessing file: " + e.getMessage());
}
// 重新启动预览
mCamera.startPreview();
});
}
注意:拍照要用到文件写入权限,切记要先申请
帧回调
帧回调是Camera1 API中一个强大的特性,通过它我们可以对相机捕捉到的每一帧进行处理和分析。我们将学习如何注册帧回调接口,并在回调方法中处理相机帧数据。这可以用于实现实时图像处理、面部识别、图像分析等功能。
设置帧回调1,需要对帧缓冲区进行消费后设置camera.addCallbackBuffer(yuvBuffer);再返回新一帧数据
yuvBuffer = new byte[width * height * 3 / 2];
mCamera.addCallbackBuffer(yuvBuffer);
mCamera.setPreviewCallbackWithBuffer(previewCallback);
设置帧回调2,直接传递预览帧数据给回调函数
mCamera.setPreviewCallback(previewCallback);
//帧回调
Camera.PreviewCallback previewCallback = new Camera.PreviewCallback() {
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
//在这里对数据进行处理
//帧缓冲区模式这个别忘记了
camera.addCallbackBuffer(yuvBuffer);
}
};
总结
通过了解Camera1的初始化、预览、拍照以及帧回调的过程,我们可以更好地理解和掌握Camera1 API的使用方法。此外,在实际的应用开发中,我们可以根据具体需求进一步扩展和优化相机功能。如聚焦、夜视模式、手电筒等等,具体可参见官网Camera.Parameters。
相机应用程序的开发是一个充满创造力和挑战性的过程,希望这篇博文能为你提供更多的参考和启示。
THE END
感谢查阅
玉念聿辉:编辑
https://gitee.com/yunianvh/camera-demo
https://github.com/yunianvh/CameraDemo