(一)Android camera2 實現相機預覽及獲取預覽幀資料流
一、本文重點說明
- 本文基於 android camera2 實現視訊預覽,暫未相容 camera1 API,基礎實現可以參考 googlesample Camera2 例子 android-Camera2Basic ,本文以工具類形式實現一步呼叫。
- 谷歌例子中沒有具體指明預覽幀的獲取,即 camera1 setPreviewCallback 類似功能實現,具體是通過
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 格式。
三、camera2 API 簡單說明
- 說明
與Camera1
區別較大,主要建立會話機制,通過Session
完成對相機請求操作,更具體的原理說明請使用 csdn 的搜尋功能。流程說明:
openCamera() -> CameraDevice.StateCallback 中建立 CameraCaptureSession -> CameraCaptureSession.StateCallback 中發起請求。 - 工具類使用
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 這裡下載
未完,待續