接下來將會了解Android HAL是如何與相機設備、Framework進行交互的,爲了簡單起見,我們使用androidP代碼中的谷歌實例代碼進行學習,代碼路徑爲:android/hardware/libhardware/modules/camera/3_4。
一般的,各個平臺的camera HAL將會有個 v4l2_camera_hal.cpp 文件。在這裏,將是HAL對外的接口,該文件將會通過 HAL_MODULE_INFO_SYM
修飾一個 camera_module_t 結構體。camera Provider服務就是通過 HAL_MODULE_INFO_SYM
找到 camera_module_t,從而操作HAL。
全局靜態對象 V4L2CameraHAL
在 v4l2_camera_hal.cpp 中定義了一個 V4L2CameraHAL 類的全局靜態對象 gCameraHAL,所以在加載 HAL.so 時,將會調用 V4L2CameraHAL 類的構造函數。在構造函數中,主要是探測 /dev 目錄下有多少個 video 節點且支持V4L2 video capture,並將探測結果保存在 V4L2CameraHAL.mCameras 容器中。
get_number_of_cameras()
最終將返回全局靜態對象 gCameraHAL 的 mCameras 成員大小。
get_camera_info()
最終根據傳遞進來的 id 將調用到全局靜態對象 gCameraHAL 的 mCameras[id]->getInfo(info),此時將調用到 Camera::getInfo()。注意,mCameras[id]在全局靜態變量 gCameraHAL 的構造函數中已經創建 V4L2Camera 對象。
爲了支持Android框架保存原始圖像文件,需要有關傳感器特性的大量元數據。這包括諸如色彩空間和鏡頭陰影功能之類的信息。這些信息大部分都是相機子系統的靜態屬性,可以在配置輸出管道或者提交請求之前進行查詢。
set_callbacks()
設置回調函數,保存在 gCameraHAL.mCallbacks。mCallbacks爲 camera_module_callbacks_t 類型,其中包含兩個回調,一個是相機設備狀態改變時的回調;一個是閃光燈狀態改變時的回調。
hw_module_methods_t 變量
在 camera_module_t 的comon成員中,通過 methods 指向了一個 hw_module_methods_t 類型變量。通過 hw_module_methods_t->open() ,我們最終調用到 Camera::openDevice() 函數(V4L2Camera是Camera的派生類,V4L2Camera沒有覆蓋openDevice()函數),從而和相機設備連接起來,該函數的參數 module 指向 HAL_MODULE_INFO_SYM.common,而 device 則是將返回給 Framework 的 camera device,device 指向 hw_device_t 類型對象。
Camera::openDevice()
在該函數中,將會通過調用 connect() 函數完成與相機設備的連接,接着將填充device,返回Framework。
而 connect() 將是調用到 V4L2Camera::connect()。V4L2Camera::connect() 則是創建一個 V4L2Wrapper::Connection 實例對象,在 V4L2Wrapper::Connection 的構造函數中,又將調用 V4L2Wrapper::Connect() 函數。
這一切,就這樣就到了V4L2Wrapper::Connect() 。而在 V4L2Wrapper::Connect() 中,將會 open video節點,進行查詢支持的格式以及分辨率等操作。
看完 v4l2_camera_hal.cpp 中的接口後,似乎上面就已經介紹完了,就沒有其他接口控制設備了,那麼 camera Provider服務進程又是如何多樣操作相機設備的呢?下面繼續看。
我們回頭看 Camera::openDevice() 函數的實現:
int Camera::openDevice(const hw_module_t *module, hw_device_t **device)
{
ALOGI("%s:%d: Opening camera device", __func__, mId);
ATRACE_CALL();
android::Mutex::Autolock al(mDeviceLock);
if (mBusy) {
ALOGE("%s:%d: Error! Camera device already opened", __func__, mId);
return -EBUSY;
}
int connectResult = connect();
if (connectResult != 0) {
return connectResult;
}
mBusy = true;
mDevice.common.module = const_cast<hw_module_t*>(module);
/* 這裏,是我們返回給Framework的 hw_device_t,那麼這個
* mDevice 是什麼呢,看 Camera 的構造函數
*/
*device = &mDevice.common;
return 0;
}
Camera::Camera(int id)
: mId(id),
mSettingsSet(false),
mBusy(false),
mCallbackOps(NULL),
mInFlightTracker(new RequestTracker)
{
memset(&mTemplates, 0, sizeof(mTemplates));
/* 在Camera 的構造函數中,初始化 common 成員的部分變量之後,
* 賦值了 ops 操作集,接下來再看看 ops 的類型
*/
memset(&mDevice, 0, sizeof(mDevice));
mDevice.common.tag = HARDWARE_DEVICE_TAG;
mDevice.common.version = CAMERA_DEVICE_API_VERSION_3_4;
mDevice.common.close = close_device;
mDevice.ops = const_cast<camera3_device_ops_t*>(&sOps);
mDevice.priv = this;
}
/* 從 mDevice.ops 的定義類型來看,很像就是操作相機設備的一些接口函數,
* 我們怎麼確認我們的猜測是否正確呢,看 camera Provider 服務進程的調用
*/
typedef struct camera3_device_ops {
int (*initialize)(const struct camera3_device *,
const camera3_callback_ops_t *callback_ops);
int (*configure_streams)(const struct camera3_device *,
camera3_stream_configuration_t *stream_list);
int (*register_stream_buffers)(const struct camera3_device *,
const camera3_stream_buffer_set_t *buffer_set);
int (*process_capture_request)(const struct camera3_device *,
camera3_capture_request_t *request);
void (*get_metadata_vendor_tag_ops)(const struct camera3_device*,
vendor_tag_query_ops_t* ops);
void (*dump)(const struct camera3_device *, int fd);
int (*flush)(const struct camera3_device *);
void *reserved[8];
} camera3_device_ops_t;
/* camera Provider 服務進程的調用 */
/* 通過查看 CameraDeviceSession 類的實現,可以瞭解到,
* 在創建流、處理Framework請求等操作,都是通過 Camera::openDevice()
* 返回的 device 進行操作的,一般以以下的方式進行調用:
*/
mDevice->ops->initialize(mDevice, this);
mDevice->ops->dump(mDevice, fd->data[0]);
mDevice->ops->construct_default_request_settings(mDevice, (int) type);
mDevice->ops->configure_streams(mDevice, &stream_list);
mDevice->ops->process_capture_request(mDevice, &halRequest);
mDevice->ops->flush(mDevice);
所以通過以上分析得知,camera Provider 服務通過返回的 device 得到 camera3_device_ops_t 操作集,所以可以操作配置相機設備。下面再分析,這些操作集函數都進行了什麼操作。
camera3_device_ops_t
const camera3_device_ops_t Camera::sOps = {
.initialize = default_camera_hal::initialize,
.configure_streams = default_camera_hal::configure_streams,
.register_stream_buffers = nullptr,
.construct_default_request_settings
= default_camera_hal::construct_default_request_settings,
.process_capture_request = default_camera_hal::process_capture_request,
.get_metadata_vendor_tag_ops = nullptr,
.dump = default_camera_hal::dump,
.flush = default_camera_hal::flush,
.reserved = {0},
};
initialize()
從上面我們瞭解到 initialize() 函數指針賦值爲 default_camera_hal::initialize()。
namespace default_camera_hal {
extern "C" {
// Get handle to camera from device priv data
static Camera *camdev_to_camera(const camera3_device_t *dev)
{
/* 實際上通過轉換爲 camera3_device_t 類型,
* 再獲取 priv 成員轉換爲 Camera 類型指針,可
* 回頭查看 Camera 的構造函數以及 openDevice()
*/
return reinterpret_cast<Camera*>(dev->priv);
}
static int initialize(const camera3_device_t *dev,
const camera3_callback_ops_t *callback_ops)
{
/* 調用到 Camera::initialize() */
return camdev_to_camera(dev)->initialize(callback_ops);
}
} // extern "C"
} // namespace default_camera_hal
int Camera::initialize(const camera3_callback_ops_t *callback_ops)
{
int res;
ALOGV("%s:%d: callback_ops=%p", __func__, mId, callback_ops);
/* 在Framework層,調用 initialize() 函數時,傳遞了
* CameraDeviceSession 類對象的this指針,所以這裏的
* callback_ops 指向 CameraDeviceSession實例對象地
* 址,而 CameraDeviceSession 類繼承於 camera3_callback_ops,
* 所以這裏就可以對接起來了
*/
mCallbackOps = callback_ops;
// per-device specific initialization
/* Camera類的 initDevice() 函數是純虛函數,將會調用
* V4L2Camera::initDevice(),接着創建
* V4L2Camera::enqueueRequestBuffers() 線程和
* V4L2Camera::dequeueRequestBuffers() 線程
*/
res = initDevice();
if (res != 0) {
ALOGE("%s:%d: Failed to initialize device!", __func__, mId);
return res;
}
return 0;
}
如上介紹,在camera HAL,將會通過 mCallbackOps 將狀態等信息反饋到 Framework。我們可以查看,在 CameraDeviceSession 類的構造函數可以看到以下信息:
/* camera3_callback_ops 對象將初始化爲以下值 */
camera3_callback_ops({&sProcessCaptureResult, &sNotify})
typedef struct camera3_callback_ops {
void (*process_capture_result)(const struct camera3_callback_ops *,
const camera3_capture_result_t *result);
void (*notify)(const struct camera3_callback_ops *,
const camera3_notify_msg_t *msg);
} camera3_callback_ops_t;
而在 V4L2Camera::initDevice() 函數中,則是創建處理Framework 請求的線程。
configure_streams()
與 initialize() 類似,將會調用至 Camera::configureStreams(camera3_stream_configuration_t *stream_config)
。這個調用將使用stream_list中定義的數據流信息來代替之前的數據流配置。在initialize()之後,使用process_capture_request()提交請求之前,這個函數至少被調用一次。在瞭解 configureStreams() 的具體操作前,我們先看看,函數參數的具體定義是怎樣的。
typedef struct camera3_stream_configuration {
/* Framework請求的stream總數,至少爲1,而且至少有一個具有輸出能力的流 */
uint32_t num_streams;
/* 指向Framework配置的stream */
camera3_stream_t **streams;
uint32_t operation_mode;
const camera_metadata_t *session_parameters;
} camera3_stream_configuration_t;
typedef struct camera3_stream {
int stream_type;
uint32_t width;
uint32_t height;
int format;
uint32_t usage;
uint32_t max_buffers;
void *priv;
android_dataspace_t data_space;
int rotation;
const char* physical_camera_id;
/* reserved for future use */
void *reserved[6];
} camera3_stream_t;
從 camera3_stream_configuration_t 的定義我們可以瞭解到,主要就是配置相機硬件輸出的格式、分辨率、buf數量、以及stream的類型等。
而 configureStreams() 進行了什麼操作呢?
int Camera::configureStreams(camera3_stream_configuration_t *stream_config)
{
/* 檢查參數是否正確、有效 */
int res = validateStreamConfiguration(stream_config);
if (res) {
ALOGE("%s:%d: Failed to validate stream set", __func__, mId);
} else {
/* 設置stream */
res = setupStreams(stream_config);
if (res) {
ALOGE("%s:%d: Failed to setup stream set", __func__, mId);
}
}
if (!res) {
/* 保存相應的配置 */
mInFlightTracker->SetStreamConfiguration(*stream_config);
// Must provide new settings for the new configuration.
mSettingsSet = false;
} else if (res != -EINVAL) {
// Fatal error, the old configuration is invalid.
mInFlightTracker->ClearStreamConfiguration();
}
// On a non-fatal error the old configuration, if any, remains valid.
return res;
}
在 setupStreams() 函數又進行了什麼操作呢,檢查當前的多個stream的格式、分辨率等參數是否一致,然後設置格式、申請buf等。
簡單總結 configureStreams() 進行了什麼操作:
- 檢查多個stream參數是否正確有效;
- 確認stream的格式和分辨率一致,然後 VIDIOC_S_FMT、VIDIOC_REQBUFS,並根據返回值填充 stream 信息;
- 保存 stream 信息到 buffers_in_flight_;
construct_default_request_settings()
該函數將調用至 Camera::constructDefaultRequestSettings(),上面有提到過,Framework將會通過一些請求操作camera HAL,但是可能各個平臺的HAL有個別差異,所以需要Framework在提交請求前,先獲取默認請求的設置模板(實質是 CameraMetadata),從而再精確化設置具體的參數,該函數就是構造默認請求設置的。
constructDefaultRequestSettings() 函數主要進行以下操作:
- 在 Camera 類中,有個 mTemplates 指針數據指向了各中請求類型的 CameraMetadata,所以在檢查請求類型的有效性之後,將會確認 mTemplates 是否已經保存了相應類型的數據,如果是,直接返回相應的數據;
- 如果沒有相應類型的 CameraMetadata,將通過 initTemplate() 初始化相應的數據並保存;
process_capture_request()
顯然的,從該函數名我們就知道,它是負責處理Framework發過來的請求的,下面我們來看看,調用至 Camera::processCaptureRequest() 後又是怎麼處理的?
Camera::processCaptureRequest() 主要進行以下操作:
- 檢查輸入參數的有效性;
- 在 preprocessCaptureBuffer() 函數中,獲取 output_buffers 同步防護,防止HAL操作該buffer時Framework在讀取;
- 將該請求信息添加到 mInFlightTracker;
- 通過 enqueueRequest() 函數,添加請求到 request_queue_ 隊列中,同時通過條件變量 requests_available_ 通知到 V4L2Camera::enqueueRequestBuffers() 線程進行相應的處理;
V4L2Camera::enqueueRequestBuffers() 操作了什麼?
上面我們說到,當HAL接收到Framework請求操作時,將會把請求信息添加到 request_queue_ 隊列再通過條件變量通知到 V4L2Camera::enqueueRequestBuffers() 函數(在 Camera::initialize() 中已經創建線程運行該函數了)。
enqueueRequestBuffers() 操作如下:
- 通過 dequeueRequest() 函數,從 request_queue_ 隊列中獲取一個請求操作;
- 通過 SetRequestSettings() 函數設置請求中的參數;
- 通過 V4L2Wrapper::EnqueueRequest() 函數查詢buf信息保存在 buffers_ 以及將buf添加到相機設備驅動;
- 接下來通過 V4L2Wrapper::StreamOn() 函數開啓流傳輸;
- 最後將會通過條件變量通知到 V4L2Camera::dequeueRequestBuffers() 線程(buffer_dequeuer_線程);
- 在 dequeueRequestBuffers() 線程中,通過 DequeueRequest() 拿到圖像數據之後,將會調用 completeRequest() 函數;
- 在 Camera::completeRequest() 中,處理該請求,而後從 mInFlightTracker 中移除,設置請求的時間戳,接着通過 Camera::notifyShutter() 函數調用了 mCallbackOps->notify() 函數,從而通知Framework;
- 最後還會通過 Camera::sendResult() 函數調用 mCallbackOps->process_capture_result() 從而Framework處理該請求信息;
以上,HAL與Framework完成了交互。
dump()、flush()
dump() 函數則是dump各種設置參數等操作,而 flush() 則是進行清除 mInFlightTracker 中保存的各個請求以及通過 V4L2Wrapper::StreamOff() 函數關閉相機設備流傳輸以及清除buffer等。
疑問:
這個 CameraMetadata 是怎麼初始化的?
在 V4L2Camera::initStaticInfo() 中,將會初始化一些配置信息,就是在這裏進行初始化操作的。詳細可參考 天才2012 的:Android Camera API2中採用CameraMetadata用於從APP到HAL的參數交互 。