Android camera HAL框架

接下來將會了解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() 進行了什麼操作:

  1. 檢查多個stream參數是否正確有效;
  2. 確認stream的格式和分辨率一致,然後 VIDIOC_S_FMT、VIDIOC_REQBUFS,並根據返回值填充 stream 信息;
  3. 保存 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() 操作如下:

  1. 通過 dequeueRequest() 函數,從 request_queue_ 隊列中獲取一個請求操作;
  2. 通過 SetRequestSettings() 函數設置請求中的參數;
  3. 通過 V4L2Wrapper::EnqueueRequest() 函數查詢buf信息保存在 buffers_ 以及將buf添加到相機設備驅動;
  4. 接下來通過 V4L2Wrapper::StreamOn() 函數開啓流傳輸;
  5. 最後將會通過條件變量通知到 V4L2Camera::dequeueRequestBuffers() 線程(buffer_dequeuer_線程);
  6. 在 dequeueRequestBuffers() 線程中,通過 DequeueRequest() 拿到圖像數據之後,將會調用 completeRequest() 函數;
  7. 在 Camera::completeRequest() 中,處理該請求,而後從 mInFlightTracker 中移除,設置請求的時間戳,接着通過 Camera::notifyShutter() 函數調用了 mCallbackOps->notify() 函數,從而通知Framework;
  8. 最後還會通過 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的參數交互

參考

Android Camera

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章