Android提供了俩种使用相机的方式
Camera开发主要用到了来个类
Camera类的API | 解释 |
---|---|
getNumberOfCameras | 获取设备的相机数量,一般有俩个 前置相机和后置相机 |
open(CameraId) | 获取Camera实例,传入参数1 是获取前置摄像头, 0 为后置摄像头 |
getParameters | 获取相机的参数,包括闪光灯模式,对焦模式,预览和拍照尺寸 |
setParameters | 把自己设置好的相机参数,设置给相机 |
setPreviewDisplay(SurfaceHolder) | 绑定预览图像的SurfaceView,当相机和surfaceView绑定后,相机预览的图像会通过surfaceView显示在屏幕上 |
setPreviewTexture(SurfaceTexture) | 绑定预览图像的TextureView,当相机和TextureView绑定后,相机预览的图像会通过TextureView显示在屏幕上 |
setPreviewCallback | 获取相机的预览数据 |
setDisplayOrientation | 设置预览画面,顺时针旋转的角度 |
startPreview | 开始预览 |
stopPreview | 停止预览 |
release | 释放相机 |
takePicture(Camera.ShutterCallback shutter, Camera.PictureCallback raw, Camera.PictureCallback jpeg) | 这个是实现相机拍照的主要方法,包含了三个回调参数。shutter是快门按下时的回调,raw是获取拍照原始数据的回调,jpeg是获取经过压缩成jpg格式的图像数据的回调。 |
getCameraInfo(int cameraId, CameraInfo cameraInfo) | 获取指定id的相机信息 |
startFaceDetection | 开始人脸检测 |
stopFaceDetection | 停止人脸检测 |
setFaceDetectionListener | 设置人脸监听回调 |
我们继续看一下Camera类的内部类Parameters
上面我们说到一个getParameters方法,这个方法的返回值就是Parameters类
Camera.Parameters类的Api | 解释 |
---|---|
getSupportedPreviewSizes | 获取相机支持预览图片大小 |
setPreviewSize(640, 480); | 设置预览图片带下,这里就是设置预览图片为640*480 |
getSupportedPreviewFormats | 获得相机支持的图片预览格式,所有的相机都支持ImageFormat.NV21更多的图片格式可以自行百度或是查看ImageFormat类 |
setPreviewFormat(int pixel_format) | 设置预览图片的格式 |
getSupportedPictureSizes | 获取相机支持采集的图片大小(就是拍摄照片的带下) |
setPictureSize(int width, int height) | 设置拍摄照片的大小 |
getSupportedPictureFormats | 获取相机支持的图片格式 |
setPictureFormat | 设置拍摄图片的格式 |
getSupportedFocusModes | 获取相机支持的对焦模式 |
setFocusMode | 设置相机对焦模式 |
getMaxNumDetectedFaces | 返回当前相机所支持的最大的人脸检测个数 |
用于绘制相机预览图像,提供给用户实时的预览图像,普通的View以及派生类都是共享一个Surface的,所有的绘制都需要在UI线程中,而SurfaceView是一种比较特殊的View,他不与其他普通View共享Surface,而是在他内部有一个独立的Surface,SurfaceView只负责管理这个Surface的格式尺寸以及显示位置,由于Ui线程还需要处理其他Ui交互,并不能保证View的更新速度和帧率,而SurfaceView有一个独立的Surface,因此可以在独立线程中绘制,因此可以提高更高的帧率
SurfaceView的优缺点
优点:
缺点:
SurfaceHolder:SurfaceHolder是控制Surface的一个抽象接口,它能够控制Surface的尺寸格式,修改surface的像素,监听surface的变化等,Surface的典型用例就是用于SurfaceView中,通过getHolder获取SurfaceHolder,来监听Surface的状态
SurfaceHolder.Callback接口:负责监听Surface状态变化的接口,有三个方法
TextureView继承自View,他将内容流直接投影到View中,和SurfaceView不同的是,他是一个普通的View,他可以平移,缩放等,他必须运行在硬件加速的窗口中
优点
缺点
在7.0系统上SurfaceView比TextureView更有优势,可以使用动画,并且不会有黑边,如果在7.0以下有动画效果那么就需要用到TextureView
从安全和性能上考虑应该使用SurfaceView
<SurfaceView
android:id="@+id/surface"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="visible"/>
surfaceView = findViewById(R.id.surface);
surfaceView.getHolder().addCallback(surfaceCallback);
private SurfaceHolder.Callback surfaceCallback = new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) {
//在这里打开相机
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
//在这里回收相机资源
}
};
int cameras = Camera.getNumberOfCameras();
这个方法返回相机的数量,在有前后摄像头的手机,会返回数量2
mCamera = Camera.open(0);
传入参数CameraId,通常0为后置摄像头,1为前置摄像头
mCamera.setDisplayOrientation(90);
Camera.Parameters parameters = mCamera.getParameters();
parameters.setPreviewFormat(ImageFormat.NV21);
//预览大小设置
parameters.setPreviewSize(640, 480);
mCamera.setParameters(parameters);
设置预览画面,顺时针旋转角度,和设置预览画面的大小,可以设置的参数有很多参考上方的API
mCamera.setPreviewDisplay(surfaceView.getHolder());
mCamera.startPreview();
mCamera.setPreviewCallback(new Camera.PreviewCallback() {
@Override
public void onPreviewFrame(byte[] data, Camera camera) { //这里的data就是每一帧的数据
}
});
完整的代码
private void initView() {
surfaceView = findViewById(R.id.surface);
surfaceView.getHolder().addCallback(surfaceCallback);
}
private SurfaceHolder.Callback surfaceCallback = new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) {
start();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
};
public void start() {
if (mCamera == null) {
mCamera = Camera.open(0);
}
//因为横屏
mCamera.setDisplayOrientation(90);
try {
Camera.Parameters parameters = mCamera.getParameters();
parameters.setPreviewFormat(ImageFormat.NV21);
//预览大小设置
parameters.setPreviewSize(640, 480);
mCamera.setParameters(parameters);
mCamera.setPreviewDisplay(surfaceView.getHolder());
mCamera.setPreviewCallback(new Camera.PreviewCallback() {
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
}
});
mCamera.startPreview();
} catch (Exception e) {
e.printStackTrace();
}
}
//摄像头数据的回调
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
}
相机的预览方向问题
我们先了解一下基本概念
setDisplayOrientation
方法可以设置预览方向,默认为0,和传感器方向一致SurfaceView预览图像变形,拍摄照片图像变形问题
首先我们先明白三个概念
问题
手机中预览的问题被拉伸被拉伸
这个是由于SurfaceView尺寸和PreViewSize长宽比例不同导致,因为手机预览图像是由相机预览图像生成,所有长宽比例不一致,必定会被拉伸
拍摄照片物体被拉伸
这种是由PreViewSize和PictureSize,长宽比例不同导致,总之为了避免这些问题的发生,SurfaceView、PreviewSize、PictureSize这三者的长宽尺寸需要比例相同,可以通过camera.getSupportedPreviewSizes()和camera.getSupportedPictureSizes()获取支持的尺寸,然后筛选和SurfaceView尺寸比例相同的数据,设置给相机,注意,市场上主流的长宽比例尺寸为4:3和16:9,所以SurfaceView尺寸不能太奇葩
锁屏下资源的释放
如果Home键切换后台或者锁屏后,就应该关闭资源把相机释放掉,可以在onResume和onPause中释放相机
参考:https://juejin.im/entry/58b4ccd944d904006a1ce446
https://www.jianshu.com/p/f8d0d1467584
https://juejin.im/entry/5912ba2d128fe10058694030
https://zhuanlan.zhihu.com/p/20559606