首先需要声明的是,我并不是原创每个概念,而是在前人的基础上做了一个实现,原理如图:
上面的图片来自AR红包Android端实现原理
上面的难点我觉得可能是处理一帧图像的方法,这个方法有两个,分别是Camera和Camera2提供的,先看看Camera2的示例:
// 开始预览,主要是camera.createCaptureSession这段代码很重要,创建会话
private void startPreview(CameraDevice camera) throws CameraAccessException {
SurfaceTexture texture = mPreviewView.getSurfaceTexture();
// 这里设置的就是预览大小
texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());</span>
Surface surface = new Surface(texture);
try {
// 设置捕获请求为预览,这里还有拍照啊,录像等
mPreviewBuilder = camera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
} catch (CameraAccessException e) {
e.printStackTrace();
}
// 就是在这里,通过这个set(key,value)方法,设置曝光啊,自动聚焦等参数!! 如下举例:
// mPreviewBuilder.set(CaptureRequest.CONTROL_AE_MODE,CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
mImageReader = ImageReader.newInstance(mSurfaceView.getWidth(), mSurfaceView.getHeight(), ImageFormat.JPEG/*此处还有很多格式,比如我所用到YUV等*/, 2/*最大的图片数,mImageReader里能获取到图片数,但是实际中是2+1张图片,就是多一张*/);
mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, mHandler);
// 这里一定分别add两个surface,一个Textureview的,一个ImageReader的,如果没add,会造成没摄像头预览,或者没有ImageReader的那个回调!!
mPreviewBuilder.addTarget(surface);
mPreviewBuilder.addTarget(mImageReader.getSurface());
mCameraDevice.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()),mSessionStateCallback, mHandler);
}
private CameraCaptureSession.StateCallback mSessionStateCallback = new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(CameraCaptureSession session) {
try {
updatePreview(session);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
@Override
public void onConfigureFailed(CameraCaptureSession session) {
}
};
private void updatePreview(CameraCaptureSession session) throws CameraAccessException {
session.setRepeatingRequest(mPreviewBuilder.build(), null, mHandler);
}
private ImageReader.OnImageAvailableListener mOnImageAvailableListener
= new ImageReader.OnImageAvailableListener() {
/**
* 当有一张图片可用时会回调此方法,但有一点一定要注意:
* 一定要调用 reader.acquireNextImage()和close()方法,否则画面就会卡住!!!!!我被这个坑坑了好久!!!
* 很多人可能写Demo就在这里打一个Log,结果卡住了,或者方法不能一直被回调。
**/
@Override
public void onImageAvailable(ImageReader reader) {
Image img = reader.acquireNextImage();
/**
* 因为Camera2并没有Camera1的Priview回调!!!所以该怎么能到预览图像的byte[]呢?就是在这里了!!!我找了好久的办法!!!
**/
ByteBuffer buffer = img.getPlanes()[0].getBuffer();
byte[] data = new byte[buffer.remaining()];
buffer.get(data);
img.close();
}
};
}
上述方法也可以实现图像的处理,但是我使用红米以及小米5在测试过程中发现卡顿非常严重,尽管检查了图片处理的线程是子线程,但是每秒钟大概处理6张预览图的时候还是依然卡顿,因此我排除了这个方法。
除了Camera2的方法以外,还有Camera的方法,而原理介绍中已经讲解了方法,如下:
三、获取一帧预览图像
Camera.setOneShotPreviewCallback(PreviewCallback cb)方法可以获取一帧预览图像,数据会返回到PreviewCallback 回调中的 onPreviewFrame(byte[] data,Camera camera)方法中,data即为帧数据。
由于我已经在Camera2测试了,因此我并没有直接使用这个方法来测试,而是参考了Zxing的二维码扫描这个库,因为这个二维码的处理逻辑其实是非常相似的