最近研究了一下 android 摄像头开发相关的技术, 也看了 Google 提供的 Camera2Basic 调用示例, 以及网上一部分代码, 但都是在 TextureView 等预览基础上实现, 而我想要做的是在不预览的情况下, 能获取到摄像头原始数据流, 并由自己来决定是否绘制显示. 经过一番折腾, 初步实现了自己的目的 --CamCap 程序.
需求分析
其实主要就是在不预览的情况下获取到摄像头原始数据, 目的嘛, 一是为了灵活性, 方便随时开启关闭预览, 二是为了以后可以直接对数据进行处理, 三是为了其他程序开发做一些准备. 于是实现一下几个功能:
获取摄像头数据, 并手动绘制图像
随时开启 / 关闭预览
随时保存当前摄像头图像, 即使在关闭预览情况下
Android Camera2 接口
查阅了一些资料, Android Java 层由于从 API21 开始, 已经废弃原 Camera 接口, 所以这里采用 Camera2 接口. 相比 Camera 接口来说, 第二代摄像头接口, 调用复杂多了, 但是灵活性也更高了, 通过 Google 的 Camera2Basic 例子可以很清楚的了解到使用方式. 这里把 CamCap 程序中的 Camera2 的调用顺序整理如下:
和其他程序一样, 通过 ImageReader 来获取到 CameraCaptureSession 传递出来的数据, 与 Google 例子不同的是, 我取消了把 TextureView 的传递, 改为单独以 ImageReader 来获取图像流, 并设置为 YUV_420_888 格式, 以拿到原始数据.
打开摄像头
摄像头打开后, 创建对话
调用 libyuv 做 RGB 之间的数据转换
获取到 YUV 数据之后, 就可以在 UI 界面上进行绘制了, 通过简单了解, 可以通过 OpenGLES 来绘制, 也可以转为 Bitmap 直接在 TextureView 上绘制. 这里为了简单, 选择了后者. 然而后来发现, android.graphics.Bitmap 并不支持直接将 YUV 数据存入, 只能转为 RGB 数据格式, 才能存入 Bitmap, 进而在 TextureView 上绘制. YUV 转换 RGB, 之前在 C++ 上应用过很多次了, 可以把现有代码修改一下放到 java 里运行, 不过考虑到性能问题, 决定还是使用 libyuv.libyuv 是一款以 c/c++ 为基础的, 专做 YUV 与 RGB 格式转换的开源项目, 性能非常高.
使用 libyuv, 需要通过 NDK 交叉编译, 并通过 JNI 来调用. libyuv 编译起来也很简单, 首先下载 libyuv 源码, 代码地址是: https://chromium.googlesource.com/libyuv/libyuv . 然后确保 NDK 已经安装 (这个直接在 AndroidStudio 中就能安装好), 之后把 NDK 目录添加到环境变量. 最后, 进入 libyuv 目录, 调用 ndk-build 即可. libyuv 项目里已经写好了 Android.mk, 所以, 直接编译就行了 (我是在 Windows 上).
注意! 编译的时候遇到 JPEG 库没有指定的问题, 如果不想依赖 libjpeg, 可以修改 Android.mk, 删除 JPEG 库相关编译项就可以解决.
在 AndroidStudio 上建立 c++ 文件, 封装 libyuv 接口, 然后按照 JNI 规范暴露接口, 同时在 Java 层封装类来调用 native 方法.
绘制图像
在绘制图像的时候, 有个坑, 那就是图像的旋转, 这个是由于手机上的摄像头传感器的视野坐标, 一般都是旋转了 90 度或 270 度的, 所以, 需要把摄像头采集到的画面, 进行旋转, 才能还原出正确的视野画面. 传感器旋转方向通过以下值获得,
CameraManager.getCameraCharacteristics(camid).get(CameraCharacteristics.SENSOR_ORIENTATION)
根据这个值, 构建 Matrix 将 Bitmap 进行旋转
Matrix 构建代码如下:
与上面代码中类似, 通过 TextureView.lockCanvas(), 获取到 Canvas, 调用 drawBitmap() 将图像写入, 即可完成绘制.
运行截图
开启预览时的 4:3 画面和 16:9 画面
关闭预览, 同时可以继续拍照
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
来源: https://www.cnblogs.com/haibindev/p/8408598.html