ImageReader
中的 OnImageAvailableListener
来实现的,重点来了,获取帧数据不能用 ImageFormat.JPEG
格式,否则你会发现预览非常卡的,因为渲染 JPEG 数据量过大,导致掉帧,所以预览帧请使用其他编码格式。简单代码如下:public void init(){
ImageReader imageReader = ImageReader.newInstance(width, height, ImageFormat.YV12, 1);//预览数据流最好用非JPEG
imageReader.setOnImageAvailableListener(new OnImageAvailableListener() {
@Override
public void onImageAvailable(ImageReader reader) {
Image image = reader.acquireLatestImage();//最后一帧
//do something
int len = image.getPlanes().length;
byte[][] bytes = new byte[len][];
int count = 0;
for (int i = 0; i < len; i++) {
ByteBuffer buffer = image.getPlanes()[i].getBuffer();
int remaining = buffer.remaining();
byte[] data = new byte[remaining];
byte[] _data = new byte[remaining];
buffer.get(data);
System.arraycopy(data, 0, _data, 0, remaining);
bytes[i] = _data;
count += remaining;
}
//数据流都在 bytes[][] 中,关于有几个plane,可以看查看 ImageUtils.getNumPlanesForFormat(int format);
// ...
image.close();//一定要关闭
}
}, handler);
}
到这里,重点已讲完,下面仅仅是我对 Camera2 的封装,如不需要,可以暂停往下阅读,节省您的时间。
不同编码的数据通道数不同,具体可以通过 ImageUtils.getNumPlanesForFormat(int format)
查看,一通道的话一般是包含所有数据,可以通过 Bitmap
直接保存出一帧的数据,三通道输出是 YUV
数据,如果要输出图片,需要转码为 RGB 格式。
Camera1
区别较大,主要建立会话机制,通过 Session
完成对相机请求操作,更具体的原理说明请使用 csdn 的搜索功能。流程说明:CameraUtil
,调用示例private CameraUtil cameraUtil;
//初始化,传入你预览的控件,目前支持 TextureView 暂未支持到 SurfaceView
//如不需要预览可以传null
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
TextureView textureView = findViewById(R.id.textureView);
//自行判断是否有相机权限
//前预览,后预览,可共用一个,不需预览可传 null
cameraUtil = new CameraUtil(this, textureView, null);
//预览帧获取
cameraUtil.setPreviewFrameCallback(new CameraUtil.OnPreviewFrameCallback() {
@Override
public void onCameraFront(byte[][] bytes, int orientation) {
//前预览回调
}
@Override
public void onCameraBack(byte[][] bytes, int orientation) {
//后预览回调
}
@Override
public void onCameraFront(byte[] bytes, int orientation) {
//前预览回调
}
@Override
public void onCameraBack(byte[] bytes, int orientation) {
//后预览回调
}
});
}
@Override
protected void onResume() {
super.onResume();
//启动预览
cameraUtil.startPreview(this);
}
//在 onPause 或者 onDestroy 中释放资源
//建议在 onPause 中,因为 onDestroy 中系统已先断开与相机的连接
@Override
protected void onPause() {
if (cameraUtil != null) {
cameraUtil.release();
}
super.onPause();
}
接口说明:
这里的预览帧回调主要是两个,一个前置,一个后置,其中每一个回调都分两种,一个是把所有数据用一个byte数组返回,另一个是各通道区分返回,主要用于 YUV 编码类型。
TextureView
显示TextureView
显示----对比
TextureView
预览 + 回调预览 GLSurefaceView
直接将 YUV 显示出来,这里回调数据流需要更加传感器方向做旋转,暂未做完,待 libyuv 库写完再更新。这里的数据回调显示,用的是 opneGL
的 GLSurdfaceView
直接绘制 YUV 数据,使用的是 李狗蛋52635 的Android上使用OpenGLES2.0显示YUV数据 可以在这里查看
到此本文已结束,目前仅适用 camera2 API,但不够好,后续会添加更基础的功能,如拍照、兼容 camera1、数据帧方向处理等操作。本文仅仅是做一个总结,希望都对您有所帮助。
下面贴上我的 CameraUtil
类地址,因为还没完善,所以没有剥离,暂放在我的 github
测试项目中,项目地址 >> 戳我
camera2 >> com.zhou.android.camera2 地址
opengl >> com.zhou.android.opengl 地址
示例 Activity 在 main 包中 CameraUtilTestActivity
说明 | 包 Package | 类Class |
---|---|---|
相机工具类 | com.zhou.android.camera2 | CameraUtil |
相机配置 | com.zhou.android.camera2 | CameraConfig |
数据回调接口 | com.zhou.android.camera2 | OnImageAvailableListener |
GL 渲染器 | com.zhou.android.opengl | GLFrameRenderer |
yuv 转换器 | com.zhou.android.opengl | GLProgram |
不会从github下的话,从 CSDN camera2 这里下载
opengl 这里下载
未完,待续