《Android平臺(tái)使用camera2采集視頻代碼實(shí)現(xiàn)》鏈接:
https://edu.csdn.net/learn/38258/606148?spm=1003.2001.3001.4157
一、前言
在Android平臺(tái)開發(fā)實(shí)時(shí)音視頻項(xiàng)目,攝像頭的采集是一個(gè)必不可少的流程;通常在Android平臺(tái)上采集攝像頭數(shù)據(jù)可以使用Camera1接口、Camera2接口或者CameraX接口。Camera1接口只支持java語言的接口,是Android最開始支持的相機(jī)接口,在Android 5.0 以后逐步廢棄;Camera2是替代Camera1的接口,不僅支持java語言的接口也支持c/c++的接口;CameraX 是一個(gè) Jetpack 庫,Camera2使用比較復(fù)雜,CameraX對(duì)Camera2的API 進(jìn)行了封裝使用起來比較簡單。
二、camera2介紹
從Android 5.0 (API 21) 版本開始引入Camera2(java版本的接口)以取代Camera1相機(jī)框架。Native Camera2則開始于android7.0(API 24)開始支持Camera2(c/c++版本的)的接口。由于不同廠商對(duì) Camera2 的支持程度也不同,所以Camera2定義了一個(gè)叫做Supported Hardware Level的重要參數(shù),其作用是將不同設(shè)備上的Camera2根據(jù)功能的支持情況劃分成多個(gè)不同級(jí)別,從低到高一共有INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY、INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED、INFO_SUPPORTED_HARDWARE_LEVEL_FULL 和 INFO_SUPPORTED_HARDWARE_LEVEL_3四個(gè)級(jí)別:
INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY:處于該級(jí)別的設(shè)備意味著它只支持Camera1的功能,不具備任何Camera2高級(jí)特性,即無法使用Camera2;
INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED:支持Camera1的基礎(chǔ)功能,支持部分Camera2高級(jí)特性的級(jí)別;
INFO_SUPPORTED_HARDWARE_LEVEL_FULL:支持所有Camera2的高級(jí)特性;
INFO_SUPPORTED_HARDWARE_LEVEL_3:新增更多Camera2高級(jí)特性,例如YUV數(shù)據(jù)的后處理。
通過以下方法可以獲取andorid 相機(jī)等級(jí)參數(shù)。
Float hardwareLevel = mCameraCharacteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
通過 Camera2 提供的高級(jí)特性可以構(gòu)建出更加高質(zhì)量的相機(jī)應(yīng)用程序; Camera2 才支持的高級(jí)特性(部分)如下:
在開啟相機(jī)之前檢查相機(jī)信息 比如檢查閃光燈是否可用;
在不開啟預(yù)覽的情況下拍照 在 Camera1 上,只有在開啟預(yù)覽之后才能進(jìn)行拍照,即使顯示預(yù)覽畫面與實(shí)際業(yè)務(wù)需求相違背的情況下也不得不開啟預(yù)覽。而 Camera2 則不強(qiáng)制要求你必須先開啟預(yù)覽才能拍照。
一次拍攝多張不同格式和尺寸的圖片 在 Camera1 上一次只能拍攝一張圖片,Camera2 則支持一次拍攝多張圖片,比如同時(shí)拍攝jpeg和raw格式的圖像,并且jpeg和raw的圖像分辨率可以不同。
靈活的 3A 控制 在 Camera2 上3A(AF、AE、AWB)的控制權(quán)限比較寬泛,APP層可以根據(jù)業(yè)務(wù)需求來配置 3A 流程并且可以獲取 3A 狀態(tài)。
支持連拍 Camera2可以連續(xù)拍攝30張的圖像。
雖然android comera2相比android comera1更加強(qiáng)大和靈活,但是有有一定的局限,比如需要android 5.0甚至android 7.0以上的版本才支持,以及相機(jī)硬件等級(jí)不能為LEVEL_LEGACY,否則也無法使用 comera2。
android comera2的介紹
https://developer.android.com/training/camera2
android comera2的API介紹
https://developer.android.com/ndk/reference/group/camera
三、camera2主要API介紹
ACameraManager * ACameraManager_create()
該函數(shù)用于創(chuàng)建一個(gè)相機(jī)管理器的實(shí)例;返回一個(gè)相機(jī)管理器實(shí)例指針;相機(jī)管理器主要是用于檢查、鏈接到相機(jī)設(shè)備。需要和ACameraManager_delete()函數(shù)配合使用。
void ACameraManager_delete( ACameraManager *manager)
該函數(shù)用于刪除相機(jī)管理器,釋放其資源;輸入manager為ACameraManager_create返回的相機(jī)管理器。
camera_status_t ACameraManager_getCameraIdList( ACameraManager *manager, ACameraIdList **cameraIdList)
該函數(shù)用于返回已經(jīng)鏈接的相機(jī)的列表;manager為相機(jī)管理器實(shí)例,cameraIdList為返回的相機(jī)列表,ACameraIdList 中主要包含了相機(jī)ID(cameraIds),相機(jī)個(gè)數(shù)(numCameras),固定相機(jī)的id從0開始,可拆卸相機(jī)的id是一個(gè)唯一的標(biāo)識(shí)數(shù)字id。
void ACameraManager_deleteCameraIdList(ACameraIdList *cameraIdList)
該函數(shù)用于刪除相機(jī)的列表,即ACameraManager_getCameraIdList生成的相機(jī)列表需要該函數(shù)刪除釋放資源。
camera_status_t ACameraManager_getCameraCharacteristics( ACameraManager *manager,
const char *cameraId,ACameraMetadata **characteristics)
該函數(shù)用于查詢獲取指定相機(jī)的特征信息;特征信息返回保存在characteristics中;常用的特征參數(shù)有ACAMERA_LENS_FACING(前置/后置位置信息)、ACAMERA_SENSOR_ORIENTATION(相機(jī)旋轉(zhuǎn)角度信息)、ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS(相機(jī)視頻信息,如分辨率);
void ACameraMetadata_free( ACameraMetadata *metadata)
該函數(shù)用于釋放相機(jī)特征信息參數(shù),即釋放ACameraManager_getCameraCharacteristics獲取的相機(jī)特征信息參數(shù)。
camera_status_t ACameraMetadata_getConstEntry(const ACameraMetadata *metadata,
uint32_t tag, ACameraMetadata_const_entry *entry)
該函數(shù)用于從相機(jī)特征信息參數(shù)中獲取指定的參數(shù)值;metadata為相機(jī)特征信息參數(shù)(元數(shù)據(jù));tag為指定的特征參數(shù),如ACAMERA_LENS_FACING、ACAMERA_SENSOR_ORIENTATION、ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS;entry為返回的參數(shù)值。比如要獲取相機(jī)采集分辨率使用ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,如下圖,entry會(huì)有一個(gè)int 32位的數(shù)組(地址空間),第0個(gè)32bit整數(shù)表示的是圖像格式,第1、第2個(gè)32bit整數(shù)表示圖像的分辨率,第3個(gè)32bit整數(shù)是表示是否是作為輸入。

camera_status_t ACameraManager_openCamera( ACameraManager *manager,
const char *cameraId,ACameraDevice_StateCallbacks *callback,ACameraDevice **device)
該函數(shù)用于打開指定的相機(jī)設(shè)備;相機(jī)設(shè)備打開成功后返回到device中;通過device可以對(duì)相機(jī)做操作。參數(shù)callback為相機(jī)狀態(tài)回調(diào)函數(shù)接口,包含相機(jī)斷開鏈接的狀態(tài)接口onDisconnected(相機(jī)斷開鏈接會(huì)調(diào)用該接口),相機(jī)出錯(cuò)狀態(tài)的接口onError(相機(jī)出現(xiàn)錯(cuò)誤會(huì)調(diào)用該接口);
camera_status_t ACameraDevice_close(ACameraDevice *device)
該函數(shù)用于關(guān)閉已經(jīng)打開的相機(jī)設(shè)備,即用于關(guān)閉ACameraManager_openCamera打開的相機(jī)設(shè)備。
media_status_t AImageReader_new(int32_t width,int32_t height,int32_t format, int32_tmaxImages, AImageReader **reader)
該函數(shù)用于創(chuàng)建一個(gè)圖像讀取器,輸入為寬、高、圖像格式(如AIMAGE_FORMAT_YUV_420_888)、圖像最大個(gè)數(shù),返回一個(gè)圖像讀取器的指針到reader。
media_status_t AImageReader_setImageListener( AImageReader *reader,
AImageReader_ImageListener *listener)
該函數(shù)用于設(shè)置圖像讀取器的監(jiān)聽器;listener參數(shù)中的onImageAvailable用于注冊(cè)一個(gè)回調(diào)函數(shù),可以用來獲取圖像數(shù)據(jù)。
media_status_t AImageReader_getWindow(AImageReader *reader, ANativeWindow **window)
該函數(shù)用于獲取可用于為該圖像讀取器生成AImage的ANativeWindow 。
void ANativeWindow_acquire( ANativeWindow *window)
該函數(shù)用于獲取給定window對(duì)象的引用,防止在刪除引用之前刪除對(duì)象。
void AImageReader_delete(AImageReader *reader)
該函數(shù)用于刪除一個(gè)AImageReader并將該讀取器生成的所有圖像返回給系統(tǒng)。
void ANativeWindow_release(ANativeWindow *window)
該函數(shù)用于刪除使用ANativeWindow_acquire()獲取的引用。
camera_status_t ACaptureSessionOutputContainer_create(ACaptureSessionOutputContainer **container)
該函數(shù)用于創(chuàng)建一個(gè)圖像捕獲會(huì)話輸出容器,成功返回到container。使用ACaptureSessionOutputContainer_free釋放容器及其內(nèi)存。
camera_status_t ACaptureSessionOutput_create(ACameraWindowType *anw,ACaptureSessionOutput **output)
該函數(shù)是為創(chuàng)建一個(gè) ACaptureSessionOutput 對(duì)象;ACaptureSessionOutput 在ACaptureSessionOutputContainer_add方法中使用,以將輸出ANativeWindow添加到 ACaptureSessionOutputContainer;使用ACaptureSessionOutput_free釋放對(duì)象及其內(nèi)存。
camera_status_t ACaptureSessionOutputContainer_add(
ACaptureSessionOutputContainer *container,const ACaptureSessionOutput *output)
該函數(shù)用于添加一個(gè)ACaptureSessionOutput對(duì)象到ACaptureSessionOutputContainer。
camera_status_t ACameraOutputTarget_create( ACameraWindowType *window,
ACameraOutputTarget **output)
該函數(shù)用于創(chuàng)建一個(gè) ACameraOutputTarget 對(duì)象。ACameraOutputTarget 在ACaptureRequest_addTarget方法中用于將輸出ANativeWindow添加到 ACaptureRequest。使用ACameraOutputTarget_free釋放對(duì)象及其內(nèi)存。
camera_status_t ACaptureRequest_addTarget(ACaptureRequest *request,
const ACameraOutputTarget *output)
該函數(shù)用于添加一個(gè)ACameraOutputTarget對(duì)象到ACaptureRequest。
camera_status_t ACaptureRequest_setEntry_i32(ACaptureRequest *request, uint32_t tag, uint32_t count,const int32_t *data)
該函數(shù)用于設(shè)置/修改相機(jī)捕獲控制參數(shù)(32為整數(shù));如修改幀率范圍參數(shù),則參數(shù)tag設(shè)置為ACAMERA_CONTROL_AE_TARGET_FPS_RANGE,data設(shè)置幀率的最大最小值;count為data的數(shù)組元素個(gè)數(shù)。
camera_status_t ACameraDevice_createCaptureSession( ACameraDevice *device, const ACaptureSessionOutputContainer *outputs,const ACameraCaptureSession_stateCallbacks *callbacks, ACameraCaptureSession **session)
該函數(shù)用于創(chuàng)建新的相機(jī)捕獲會(huì)話;outputs為ACaptureSessionOutputContainer_create創(chuàng)建的采集會(huì)話輸出容器;callbacks參數(shù)為相機(jī)狀態(tài)的回調(diào)接口;
camera_status_t ACameraCaptureSession_setRepeatingRequest(
ACameraCaptureSession *session, ACameraCaptureSession_captureCallbacks *callbacks,
int numRequests, ACaptureRequest **requests, int *captureSequenceId)
該函數(shù)用于設(shè)置重復(fù)捕獲圖像數(shù)據(jù)。通過session,ACaptureRequest被不斷重復(fù)向ACameraDevice發(fā)送請(qǐng)求,ACaptureRequest所返回的響應(yīng)數(shù)據(jù),由ACaptureSessionOutputContainer中的ACaptureSessionOutput接收,ACaptureRequest通過ACameraOutputTarget,將ACaptureSessionOutput與AImageReader綁定。
media_status_t AImageReader_acquireLatestImage( AImageReader *reader, AImage **image)
該函數(shù)用于從讀取器的隊(duì)列中獲取最新的圖像返回到image中,并丟棄舊圖像。image通過AImage_delete接口來釋放;使用AImage_getPlaneRowStride()接口可以獲取圖像數(shù)據(jù)的跨距,使用AImage_getPlaneData()接口獲取圖像各個(gè)分量的數(shù)據(jù)。
四、camera2代碼示例
打開攝像頭
static int open_camera(AndroidCameraCtx *context)
{
AndroidCameraCtx *ctx = context;
camera_status_t ret;
ACameraIdList *camera_ids;
ACameraMetadata_const_entry lens_facing;
ACameraMetadata_const_entry sensor_orientation;
ret = ACameraManager_getCameraIdList(ctx->camera_mgr, &camera_ids);
if (ret != ACAMERA_OK)
{
LOGE("Failed to get camera id list, error: %d.\n",ret);
return -1;
}
if (ctx->camera_index < camera_ids->numCameras)
{
ctx->camera_id = av_strdup(camera_ids->cameraIds[ctx->camera_index]);
if (!ctx->camera_id)
{
LOGE("Failed to allocate memory for camera_id.\n");
return -1;
}
}
else
{//有些攝像頭的等級(jí)為INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY 可能無法使用ndk下的camer2需要使用camer1
LOGE("No camera with index %d < %d available.\n",
ctx->camera_index, camera_ids->numCameras);
return -1;
}
ACameraManager_deleteCameraIdList(camera_ids);
ret = ACameraManager_getCameraCharacteristics(ctx->camera_mgr,
ctx->camera_id, &ctx->camera_metadata);
if (ret != ACAMERA_OK)
{
LOGE("Failed to get metadata for camera with id %s, error: %d.\n",ctx->camera_id, ret);
return -1;
}
ctx->camera_state_callbacks.context = context;
ctx->camera_state_callbacks.onDisconnected = camera_dev_disconnected;
ctx->camera_state_callbacks.onError = camera_dev_error;
ret = ACameraManager_openCamera(ctx->camera_mgr, ctx->camera_id,
&ctx->camera_state_callbacks, &ctx->camera_dev);
if (ret != ACAMERA_OK)
{
LOGE("Failed to open camera with id %s, error: %d.\n",ctx->camera_id, ret);
return -1;
}
ACameraMetadata_getConstEntry(ctx->camera_metadata,
ACAMERA_LENS_FACING, &lens_facing);
ACameraMetadata_getConstEntry(ctx->camera_metadata,
ACAMERA_SENSOR_ORIENTATION, &sensor_orientation);
ctx->lens_facing = lens_facing.data.u8[0];
ctx->sensor_orientation = sensor_orientation.data.i32[0];
return 0;
}
創(chuàng)建圖像讀取器
static int create_image_reader(AndroidCameraCtx *context)
{
AndroidCameraCtx *ctx = context;
media_status_t ret;
ret = AImageReader_new(ctx->width, ctx->height, AIMAGE_FORMAT_YUV_420_888,
MAX_OUT_BUF_COUNT, &ctx->image_reader);
if (ret != AMEDIA_OK)
{
LOGE("Failed to create image reader, error: %d.\n", ret);
return -1;
}
ctx->image_listener.context = context;
ctx->image_listener.onImageAvailable = imageAvailableCallBack;
ret = AImageReader_setImageListener(ctx->image_reader, &ctx->image_listener);
if (ret != AMEDIA_OK)
{
LOGE("Failed to set image listener on image reader, error: %d.\n",ret);
return -1;
}
ret = AImageReader_getWindow(ctx->image_reader, &ctx->image_reader_window);
if (ret != AMEDIA_OK)
{
LOGE("Could not get image reader window, error: %d.\n",ret);
return -1;
}
ANativeWindow_acquire(ctx->image_reader_window);
return 0;
}
創(chuàng)建采集會(huì)話流程
static int create_capture_session(AndroidCameraCtx *context)
{
AndroidCameraCtx *ctx = context;
camera_status_t ret;
ret = ACaptureSessionOutputContainer_create(&ctx->capture_session_output_container);
if (ret != ACAMERA_OK)
{
LOGE("Failed to create capture session output container, error: %d.\n", ret);
return -1;
}
ret = ACaptureSessionOutput_create(ctx->image_reader_window, &ctx->capture_session_output);
if (ret != ACAMERA_OK)
{
LOGE("Failed to create capture session container, error: %d.\n",ret);
return -1;
}
ret = ACaptureSessionOutputContainer_add(ctx->capture_session_output_container,
ctx->capture_session_output);
if (ret != ACAMERA_OK)
{
LOGE("Failed to add output to output container, error: %d.\n",ret);
return -1;
}
ret = ACameraOutputTarget_create(ctx->image_reader_window, &ctx->camera_output_target);
if (ret != ACAMERA_OK)
{
LOGE("Failed to create camera output target, error: %d.\n",ret);
return -1;
}
ret = ACameraDevice_createCaptureRequest(ctx->camera_dev, TEMPLATE_RECORD, &ctx->capture_request);
if (ret != ACAMERA_OK)
{
LOGE("Failed to create capture request, error: %d.\n",ret);
return -1;
}
ret = ACaptureRequest_setEntry_i32(ctx->capture_request, ACAMERA_CONTROL_AE_TARGET_FPS_RANGE,
2, ctx->framerate_range);
if (ret != ACAMERA_OK)
{
LOGE("Failed to set target fps range in capture request, error: %d.\n",ret);
return -1;
}
ret = ACaptureRequest_addTarget(ctx->capture_request, ctx->camera_output_target);
if (ret != ACAMERA_OK)
{
LOGE("Failed to add capture request capture request, error: %d.\n",ret);
return -1;
}
ctx->capture_session_state_callbacks.context = context;
ctx->capture_session_state_callbacks.onClosed = capture_session_closed;
ctx->capture_session_state_callbacks.onReady = capture_session_ready;
ctx->capture_session_state_callbacks.onActive = capture_session_active;
ret = ACameraDevice_createCaptureSession(ctx->camera_dev, ctx->capture_session_output_container,
&ctx->capture_session_state_callbacks, &ctx->capture_session);
if (ret != ACAMERA_OK)
{
LOGE("Failed to create capture session, error: %d.\n",ret);
return -1;
}
ret = ACameraCaptureSession_setRepeatingRequest(ctx->capture_session, NULL, 1, &ctx->capture_request, NULL);
if (ret != ACAMERA_OK)
{
LOGE("Failed to set repeating request on capture session, error: %d.\n",ret);
return -1;
}
return 0;
}
相機(jī)關(guān)閉,資源釋放
static int android_camera_close(AndroidCameraCtx *context)
{
AndroidCameraCtx *ctx = context;
if (ctx->capture_session) {
ACameraCaptureSession_stopRepeating(ctx->capture_session);
ACameraCaptureSession_close(ctx->capture_session);
ctx->capture_session = NULL;
}
if (ctx->capture_request) {
ACaptureRequest_removeTarget(ctx->capture_request, ctx->camera_output_target);
ACaptureRequest_free(ctx->capture_request);
ctx->capture_request = NULL;
}
if (ctx->camera_output_target) {
ACameraOutputTarget_free(ctx->camera_output_target);
ctx->camera_output_target = NULL;
}
if (ctx->capture_session_output) {
ACaptureSessionOutputContainer_remove(ctx->capture_session_output_container,
ctx->capture_session_output);
ACaptureSessionOutput_free(ctx->capture_session_output);
ctx->capture_session_output = NULL;
}
if (ctx->image_reader_window) {
ANativeWindow_release(ctx->image_reader_window);
ctx->image_reader_window = NULL;
}
if (ctx->capture_session_output_container) {
ACaptureSessionOutputContainer_free(ctx->capture_session_output_container);
ctx->capture_session_output_container = NULL;
}
if (ctx->camera_dev) {
ACameraDevice_close(ctx->camera_dev);
ctx->camera_dev = NULL;
}
if (ctx->image_reader) {
AImageReader_delete(ctx->image_reader);
ctx->image_reader = NULL;
}
if (ctx->camera_metadata) {
ACameraMetadata_free(ctx->camera_metadata);
ctx->camera_metadata = NULL;
}
delete [] ctx->camera_id;
if (ctx->camera_mgr) {
ACameraManager_delete(ctx->camera_mgr);
ctx->camera_mgr = NULL;
}
m_AndroidCameraMng.cameraStatus = 0;
clearVideoPicBuff();
return 0;
}
五、camera2 采集視頻框架流程
camera2硬件采集流程官方介紹文檔:
https://source.android.com/docs/core/camera/camera3
1、camera2軟件采集框圖文章來源:http://www.zghlxwxcb.cn/news/detail-507431.html

2、camera2軟件采集流程圖文章來源地址http://www.zghlxwxcb.cn/news/detail-507431.html

到了這里,關(guān)于NDK Android平臺(tái)camera2采集視頻的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!