一、应用背景
小白公司业务为车辆安全驾驶检测等,需要开启相机将图像传递给算法进行处理以达到行车预警的功能。由于项目需求,需要将开启相机这一块由原来的Camera1.0更新为Camera2.0,以下是小白开发中所遇到的问题与解决方案,仅供参考。
二、Camera2.0学习
1、Google官方demo
官方demo地址:android-Camera2Basic
2、流程
这里引用了管道的概念将安卓设备和摄像头之间联通起来,系统向摄像头发送 Capture 请求,而摄像头会返回 CameraMetadata。这一切建立在一个叫作 CameraCaptureSession 的会话中。
三、实践
1、申请权限
<uses-permission android:name="android.permission.CAMERA" />
Android 6.0以上需要进行权限的动态申请。
2、Camera2.0工具类
public class Camera2Proxy {
private static final String TAG = "Camera2Proxy";
private Activity mActivity;
private int mCameraId = CameraCharacteristics.LENS_FACING_FRONT; // 要打开的摄像头ID
private Size mPreviewSize; // 预览大小
private CameraManager mCameraManager; // 相机管理者
private CameraCharacteristics mCameraCharacteristics; // 相机属性
private CameraDevice mCameraDevice; // 相机对象
private CameraCaptureSession mCaptureSession;
private CaptureRequest.Builder mPreviewRequestBuilder; // 相机预览请求的构造器
private Semaphore mCameraOpenCloseLock = new Semaphore(1);//一个信号量以防止应用程序在关闭相机之前退出。
private CaptureRequest mPreviewRequest;
private Handler mBackgroundHandler;
private HandlerThread mBackgroundThread;
private ImageReader mImageReader;
private Surface mPreviewSurface;
private SurfaceTexture mPreviewSurfaceTexture;
private OrientationEventListener mOrientationEventListener;
private int mDisplayRotate = 0;
private int mDeviceOrientation = 0; // 设备方向,由相机传感器获取
private int mZoom = 1; // 缩放
public static int CameraFPS = 0; // 帧率
private int frameCount = 0;
private long frameTime = SystemClock.elapsedRealtime();
public long mLastPreviewFrameTime = SystemClock.elapsedRealtime(); // 最后一帧数据的时间
private byte[] data = null;
private boolean mStop = true;
private final Object lock = new Object();
private YuvCroper yuvCroper;
private int cropH = 0; //剪裁图像后的高度
private DataSendThread mDataSendThread;
private MediaRecorder mediaRecorder;
private long startRecordTime = 0;// 录像开启时间
private static final int SENSOR_ORIENTATION_DEFAULT_DEGREES = 90;
private static final int SENSOR_ORIENTATION_INVERSE_DEGREES = 270;
public static final int MEDIA_QUALITY_HIGH = 30 * 100000;
private int rotation;
private boolean isRecording = false;
private String filePath;
private static final SparseIntArray DEFAULT_ORIENTATIONS = new SparseIntArray();
private static final SparseIntArray INVERSE_ORIENTATIONS = new SparseIntArray();
static {
DEFAULT_ORIENTATIONS.append(Surface.ROTATION_0, 90);
DEFAULT_ORIENTATIONS.append(Surface.ROTATION_90, 0);
DEFAULT_ORIENTATIONS.append(Surface.ROTATION_180, 270);
DEFAULT_ORIENTATIONS.append(Surface.ROTATION_270, 180);
}
static {
INVERSE_ORIENTATIONS.append(Surface.ROTATION_0, 270);
INVERSE_ORIENTATIONS.append(Surface.ROTATION_90, 180);
INVERSE_ORIENTATIONS.append(Surface.ROTATION_180, 90);
INVERSE_ORIENTATIONS.append(Surface.ROTATION_270, 0);
}
/**
* 打开摄像头的回调
*/
private CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
@Override
public void onOpened(@NonNull CameraDevice camera) {
Log.d(TAG, "onOpened");
mCameraOpenCloseLock.release();
mCameraDevice = camera;
int recordState = (int) SPUtils.get(mActivity, SPUtils.SP_RECORD_STATE, 0);
if (recordState == 1) {
initRecordPreviewRequest();
} else {
initPreviewRequest();
}
}
@Override
public void onDisconnected(@NonNull CameraDevice camera) {
Log.d(TAG, "onDisconnected");
mCameraOpenCloseLock.release();
releaseCamera();
}
@Override
public void onError(@NonNull CameraDevice camera, int error) {
Log.e(TAG, "Camera Open failed, error: " + error);
mCameraOpenCloseLock.release();
releaseCamera();
}
};
/**
* 图像回调处理
*/
private final ImageReader.OnImageAvailableListener mOnImageAvailableListener = new ImageReader.OnImageAvailableListener() {
@Override
public void onImageAvailable(ImageReader reader) {
Image image = reader.acquireLatestImage();
if (image != null) {
if (!mStop) {
synchronized (lock) {
data = ImageUtils.getBytesFromImageAsType(image, ImageUtils.YV12);
}
}
image.close();
}
frameCount++;
mLastPreviewFrameTime = SystemClock.elapsedRealtime();
//记录帧率
if (mLastPreviewFrameTime - frameTime >= 1000) {
CameraFPS = frameCount;
Log.e(TAG, "fps = " + CameraFPS);
frameCount = 0;
frameTime = mLastPreviewFrameTime;
}
}
};
@TargetApi(Build.VERSION_CODES.M)
public Camera2Proxy(Activity activity) {
mActivity = activity;
mCameraManager = (CameraManager) mActivity.getSystemService(Context.CAMERA_SERVICE);
try {
//按照16:9的比例剪裁,剪裁后的图像尺寸不能是奇数,处理各个数据为4的倍数
RectF rectF = new RectF(0, ((16.0f * MediaInfo.PRIVIEW_WIDTH - 9.0f * MediaInfo.PRIVIEW_HEIGHT) / (32.0f * MediaInfo.PRIVIEW_WIDTH)), 1, ((16.0f * MediaInfo.PRIVIEW_WIDTH + 9.0f * MediaInfo.PRIVIEW_HEIGHT) / (32.0f * MediaInfo.PRIVIEW_WIDTH)));
Log.e("YuvCroper", "MediaInfo.PRIVIEW_WIDTH = " + MediaInfo.PRIVIEW_WIDTH + " MediaInfo.PRIVIEW_HEIGHT=" + MediaInfo.PRIVIEW_HEIGHT + " rectF=" + rectF.toString());
yuvCroper = new YuvCroper(YuvCroper.YUV_420P, MediaInfo.PRIVIEW_HEIGHT, MediaInfo.PRIVIEW_WIDTH, rectF);
} catch (Exception e) {
e.printStackTrace();
}
mOrientationEventListener = new OrientationEventListener(mActivity) {
@Override
public void onOrientationChanged(int orientation) {
mDeviceOrientation = orientation;
}
};
}
@SuppressLint("MissingPermission")
public void openCamera(int width, int height) {
Log.v(TAG, "openCamera");
startBackgroundThread(); // 对应 releaseCamera() 方法中的 stopBackgroundThread()
mOrientationEventListener.enable();
try {
if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {
throw new RuntimeException("Time out waiting to lock camera opening.");
}
try {
mCameraCharacteristics = mCameraManager.getCameraCharacteristics(Integer.toString(0));
StreamConfigurationMap map = mCameraCharacteristics.get(CameraCharacteristics
.SCALER_STREAM_CONFIGURATION_MAP);
// 拍照大小,选择能支持的一个最大的图片大小
mImageReader = ImageReader.newInstance(MediaInfo.PRIVIEW_WIDTH, MediaInfo.PRIVIEW_HEIGHT, ImageFormat.YUV_420_888, 2);
mImageReader.setOnImageAvailableListener( // 设置监听和后台线程处理器
mOnImageAvailableListener, null);
// 预览大小,根据上面选择的拍照图片的长宽比,选择一个和控件长宽差不多的大小
mPreviewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class), width, height, MediaInfo.PRIVIEW_WIDTH, MediaInfo.PRIVIEW_HEIGHT);
Log.d(TAG, "preview size: " + mPreviewSize.getWidth() + "*" + mPreviewSize.getHeight());
// 打开摄像头
mCameraManager.openCamera(Integer.toString(0), mStateCallback, mBackgroundHandler);
startSendDataThread();
} catch (Exception e) {
Log.e(TAG,"No current camera was found!");
e.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 关闭摄像头
*/
public void releaseCamera() {
Log.v(TAG, "releaseCamera");
try {
if (isRecording) {
isRecording = false;
if (mediaRecorder != null) {
mediaRecorder.stop();
mediaRecorder.reset();
mediaRecorder.release();
if (null != SDCardUtils.getInsSDCardPath()) {
long end = System.currentTimeMillis();
long duration = end - startRecordTime;
SQLiteFactory.getInstance().getFileDao().insertFileEntity(startRecordTime, end, 3, (int) duration, 0);
}
}
/*
* 生成录像对