android camera2 采集,视频采集:Android平台基于Camera 2的实现
前言
這篇文章簡單介紹下移動端Android系統下利用Camera2相關API進行視頻采集的方法。
Camera2是谷歌在Android 5.0新增的用來替代Camera1操作攝像頭的一個全新的API。
按照慣例先上一份源碼AndroidVideo。
Camera2調用攝像頭采集視頻的核心實現在Camera2Capture.java。
權限配置
使用Android平臺提供的攝像頭,首先必須在配置文件中添加如下權限配置:
獲取攝像頭信息
打開攝像頭管理器
CameraManager是一個用于檢測、連接和描述攝像頭設備的一個系統服務,可以通過調用Context.getSystemService(java.lang.String)方法來獲取一個CameraManager的實例:
CameraManager mManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
獲取攝像頭列表信息
通過調用CameraManager.getCameraIdList()方法,可以得到一個攝像頭id的列表:
String[] cameraIds = mCameraManager.getCameraIdList();
for (String id : cameraIds) {
//TODO
}
可以通過相對應的ID從CameraManager獲取到對應攝像頭的屬性集合CameraCharacteristics。
在CameraCharacteristics可以獲取到諸如前后置情況、支持的輸出size、支持的輸出格式等等之類的。
for (String id : cameraIds) {
//傳入攝像頭id,獲取對應攝像頭的參數集
CameraCharacteristics characteristics = mManager.getCameraCharacteristics(id);
//獲取攝像頭的支持等級
Integer level = characteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
//如果是LEGACY等級,不建議使用該攝像頭
if (level == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY)
{
continue;
}
//獲取攝像頭的朝向
Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);
//篩選出前置攝像頭
if (facing != CameraCharacteristics.LENS_FACING_FRONT) {
continue;
}
//StreamConfigurationMap包含了該攝像頭支持的size、format等信息
StreamConfigurationMap map = mCameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
//獲取輸出格式為YUV_420_888時兼容的size
Size[] size = map.getOutputSizes(ImageFormat.YUV_420_888);
//獲取輸出View為SurfaceView時兼容的size
//Size[] size = map.getOutputSizes(SurfaceHolder.class);
//TODO 其他的參數,例如輸出格式、輸出幀率上下限等
}
PS:對于Camera2采集系統來說,每個攝像頭都有一個支持等級:
INFO_SUPPORTED_HARDWARE_LEVEL_3 支持YUV再處理和原始數據采集功能,并且具備先進的功能。
INFO_SUPPORTED_HARDWARE_LEVEL_FULL支持先進的攝像頭功能。
INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED向后兼容模式,底層等同于Camera1的實現。
INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY 隨機贈送的功能支持,支持性不足。
PS:總的來說如果攝像頭等級是LEVEL_3和LEVEL_FULL才建議使用Camera2進行采集,否則推薦采用兼容性更好的Camera1進行視頻采集。
打開攝像頭
通過攝像頭信息,我們可以找到所需要的CameraId,接下來就用這個ID去獲取我們的攝像頭設備CameraDevice。
函數原型是public void openCamera(String cameraId, final CameraDevice.StateCallback callback, Handler handler),
cameraId是需要打開的攝像頭的id,為了監聽攝像頭的情況,需要傳入一個回調,也就是第二個參數CameraDevice.StateCallback,當然如果我們不想讓open操作占用UI線程的時間的話,
我們可以通過構造一個HandlerThread的帶Looper的子線程,然后將其Handler傳入即可。
//打開攝像頭,正常打開會回調到CameraDeviceStateCallback的onOpened方法
mManager.openCamera(mCameraId, new CameraDevice.StateCallback() {
@Override
public void onOpened(@NonNull CameraDevice camera) {
//攝像頭成功連接
//camera也就是我們需要獲取的攝像頭設備
mCameraDevice = camera;
}
@Override
public void onDisconnected(@NonNull CameraDevice camera) {
//攝像頭斷開連接
}
@Override
public void onError(@NonNull CameraDevice camera, int error) {
//打開錯誤
}
}, mHandler);
創建采集會話
在成功打開攝像頭,獲取到相應的CameraDevice,我們需要創建一個采集會話來提供程序與攝像頭的交流。
其函數原型是public abstract void createCaptureSession(List outputs,CameraCaptureSession.StateCallback callback, Handler handler) throws CameraAccessException。
第一個參數傳入的是需要采集的Surface,為了監聽會話創建情況,我們需要傳入一個CameraCaptureSession.StateCallback回調,當然第三個參數也就是讓操作能在對應Handler所在的線程中進行。
//獲取一個采集Session會話,正常流程回回調到CameraCaptureSessionStateCallback的onConfigured方法
mCameraDevice.createCaptureSession(Arrays.asList(mSurfaceView.getHolder().getSurface()), new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(@NonNull CameraCaptureSession session) {
//會話創建成功
//mCameraCaptureSession也就是新創建的會話
mCameraCaptureSession = session;
}
@Override
public void onConfigureFailed(@NonNull CameraCaptureSession session) {
//會話創建失敗
}
}, mHandler);
PS:對于一些業務需求需要提高采集幀率(120fps及以上),createConstrainedHighSpeedCaptureSession()這個會話能良好的支持該功能。
發送采集請求
當需要開始采集時,需要構造一個采集請求,然后將這個請求發送給采集會話。
//創建一個基于錄制的請求
mRequest = mDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
//將需要的目標Surface加入Target列表
mRequest.addTarget(surface);
//重復發送這個請求,進行持續的采集
mCameraCaptureSession.setRepeatingRequest(mRequest.build(), NULL, mHandler);
原始數據回調
在Camera1的采集中,我們一般通過設置setPreviewCallbackWithBuffer()和addCallbackBuffer()來獲取到采集的原始數據,那么在Camera2中將如何實現該功能呢?
我們可以用到ImageReader這個類:
//ImageReader是一個數據回調模塊,類似于Camera1的setPreviewCallbackWithBuffer
mReader = ImageReader.newInstance(mConfig.mWidth, mConfig.mHeight, mConfig.mFormat, 2);
mReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
@Override
public void onImageAvailable(ImageReader reader) {
Image image = reader.acquireNextImage();
//數據處理
image.close();
}
}, mHandler);
我們需要在createCaptureSession()的第一個參數中將ImageReader的Surface傳進去:
//通過ImageReader.getSurface()獲取一個Surface并將其傳給Session中
mCameraDevice.createCaptureSession(Arrays.asList(mReader.getSurface())//....);
然后在CaptureRequest添加這個Target:
//當然,構造請求時,需要將該Surface同時加入到Request的Target列表中
mRequest.addTarget(mReader.getSurface());
參考資料
結語
這篇文章簡單介紹了Android平臺基于Camera2的api進行攝像頭采集的功能。
Camera2雖然是谷歌當前建議使用的采集框架,但是由于廠商的兼容性問題導致Camera2的api功能相對不穩定;
所以筆者還是建議開發以Camera1為主要采集、Camera2為輔助采集的架構實現比較靠譜。
本文同步發布于簡書、CSDN。
End!
總結
以上是生活随笔為你收集整理的android camera2 采集,视频采集:Android平台基于Camera 2的实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java 对象之死
- 下一篇: python实现什么功能_Python