打开相机流程中,从 APP 到 CameraService 再到 HAL Service 的连路创建流程都已经简略分析了一遍。现在需要分析最后的阶段,即从 HAL Service 连接到 Camera HAL 的部分。
其实 HAL 层真正的运作流程我也还没真正弄清楚,好在现在只需要分析它的构造与初始化部分,这相对来说还是比较简单的。
在 HAL3 中,Camera HAL 的接口转化层(以及流解析层)由 QCamera3HardwareInterface 担当,而接口层与实现层与 HAL1 中基本没什么差别,都是在 mm_camera_interface.c 与 mm_camera.c 中。
那么接口转化层的实例是何时创建的,又是怎么初始化的,创建它的时候,与接口层、实现层又有什么交互?通过下图展示的主要调用流程可以简单了解了解。
接下来可以看看代码流程。
文件路径:hardware\interfaces\camera\common\1.0\default\CameraModule.cpp
上回说到,CameraDevice::open
的实现中,调用了 mModule->open
,即 CameraModule::open
。来看看它的具体实现。
通过代码来看,它做的事并不多,主要是调用 mModule->common.methods->open
,来进入下一层级的流程。
而这里则需要注意了,open
是一个函数指针,它指向的是 QCamera2Factory 的 camera_device_open
方法,至于为什么和 QCamera2Factory 有关,这就要回头看 HAL Service 的启动初始化流程了。对我来说这不重要,就不赘述了。
int CameraModule::open(const char* id, struct hw_device_t** device) {
int res;
ATRACE_BEGIN("camera_module->open");
res = filterOpenErrorCode(mModule->common.methods->open(&mModule->common, id, device));
ATRACE_END();
return res;
}
文件路径:hardware\qcom\camera\qcamera2\QCamera2Factory.cpp
省略一些不关心的内容,这里实际上做的事也不多。
- 首先注意第 31~33 行,这里就将前面所说的 open
函数指针指定为了 camera_device_open
这个方法。
- 第 22~27 行,注意到这里通过宏定义添加了对 HAL1 的兼容操作。实际上是要调用 cameraDeviceOpen
来进行下一步操作。
/*===========================================================================
* FUNCTION : camera_device_open
*
* DESCRIPTION: static function to open a camera device by its ID
*
* PARAMETERS :
* @camera_id : camera ID
* @hw_device : ptr to struct storing camera hardware device info
*
* RETURN : int32_t type of status
* NO_ERROR -- success
* none-zero failure code
*==========================================================================*/
int QCamera2Factory::camera_device_open(
const struct hw_module_t *module, const char *id,
struct hw_device_t **hw_device)
{
/* Do something in */
......
/* Do something out */
#ifdef QCAMERA_HAL1_SUPPORT
if(gQCameraMuxer)
rc = gQCameraMuxer->camera_device_open(module, id, hw_device);
else
#endif
rc = gQCamera2Factory->cameraDeviceOpen(atoi(id), hw_device);
return rc;
}
struct hw_module_methods_t QCamera2Factory::mModuleMethods = {
.open = QCamera2Factory::camera_device_open,
};
cameraDeviceOpen
做了什么呢:
openCamera
方法。/*===========================================================================
* FUNCTION : cameraDeviceOpen
*
* DESCRIPTION: open a camera device with its ID
*
* PARAMETERS :
* @camera_id : camera ID
* @hw_device : ptr to struct storing camera hardware device info
*
* RETURN : int32_t type of status
* NO_ERROR -- success
* none-zero failure code
*==========================================================================*/
int QCamera2Factory::cameraDeviceOpen(int camera_id,
struct hw_device_t **hw_device)
{
/* Do something in */
......
/* Do something out */
if ( mHalDescriptors[camera_id].device_version == CAMERA_DEVICE_API_VERSION_3_0 ) {
QCamera3HardwareInterface *hw = new QCamera3HardwareInterface(mHalDescriptors[camera_id].cameraId,
mCallbacks);
if (!hw) {
LOGE("Allocation of hardware interface failed");
return NO_MEMORY;
}
rc = hw->openCamera(hw_device);
if (rc != 0) {
delete hw;
}
}
/* Do something in */
......
/* Do something out */
return rc;
}
文件路径:hardware\qcom\camera\qcamera2\hal3\QCamera3HWI.cpp
首先需要注意的是内部成员 mCameraOps
的定义。
在构造实例时,有 mCameraDevice.ops = &mCameraOps;
,这点需要记住。
camera3_device_ops_t QCamera3HardwareInterface::mCameraOps = {
.initialize = QCamera3HardwareInterface::initialize,
.configure_streams = QCamera3HardwareInterface::configure_streams,
.register_stream_buffers = NULL,
.construct_default_request_settings = QCamera3HardwareInterface::construct_default_request_settings,
.process_capture_request = QCamera3HardwareInterface::process_capture_request,
.get_metadata_vendor_tag_ops = NULL,
.dump = QCamera3HardwareInterface::dump,
.flush = QCamera3HardwareInterface::flush,
.reserved = {0},
};
再来继续看看 openCamera
是怎么实现的。
openCamera
方法,这是具体实现的部分。hw_device
返回。camera_open
接口。注意此处获取到了 mCameraHandle
,应该可以说,它就是接口转化层到接口层连路的一个载体。camEvtHandle
。int QCamera3HardwareInterface::openCamera(struct hw_device_t **hw_device)
{
/* Do something in */
......
/* Do something out */
rc = openCamera();
if (rc == 0) {
*hw_device = &mCameraDevice.common;
} else
*hw_device = NULL;
/* Do something in */
......
/* Do something out */
return rc;
}
int QCamera3HardwareInterface::openCamera()
{
/* Do something in */
......
/* Do something out */
rc = camera_open((uint8_t)mCameraId, &mCameraHandle);
/* Do something in */
......
/* Do something out */
rc = mCameraHandle->ops->register_event_notify(mCameraHandle->camera_handle,
camEvtHandle, (void *)this);
/* Do something in */
......
/* Do something out */
rc = mCameraHandle->ops->get_session_id(mCameraHandle->camera_handle,
&sessionId[mCameraId]);
/* Do something in */
......
/* Do something out */
return NO_ERROR;
}
上面是接口转化层中,关于 openCamera
的部分,下面继续看看它的初始化函数。
在前一篇文章中已经分析过,创建 CameraDeviceSession 实例时,会调用它内部的初始化方法,而这其中包含了调用 QCamera3HWI 的初始化方法 initialize
。
mParameters
)初始化,注意这里的参数和 CameraParameter 是不同的,它是 metadata_buffer
相关参数的结构。camera3_call_back_ops
与 mCallbackOps
关联了起来。mChannelHandle
这一句柄,调用的方法实际是 mm_camera_interface.c
中的 mm_camera_intf_add_channel
。int QCamera3HardwareInterface::initialize(const struct camera3_device *device,
const camera3_callback_ops_t *callback_ops)
{
LOGD("E");
QCamera3HardwareInterface *hw =
reinterpret_cast(device->priv);
if (!hw) {
LOGE("NULL camera device");
return -ENODEV;
}
int rc = hw->initialize(callback_ops);
LOGD("X");
return rc;
}
int QCamera3HardwareInterface::initialize(
const struct camera3_callback_ops *callback_ops)
{
ATRACE_CALL();
int rc;
LOGI("E :mCameraId = %d mState = %d", mCameraId, mState);
pthread_mutex_lock(&mMutex);
// Validate current state
switch (mState) {
case OPENED:
/* valid state */
break;
default:
LOGE("Invalid state %d", mState);
rc = -ENODEV;
goto err1;
}
rc = initParameters();
if (rc < 0) {
LOGE("initParamters failed %d", rc);
goto err1;
}
mCallbackOps = callback_ops;
mChannelHandle = mCameraHandle->ops->add_channel(
mCameraHandle->camera_handle, NULL, NULL, this);
if (mChannelHandle == 0) {
LOGE("add_channel failed");
rc = -ENOMEM;
pthread_mutex_unlock(&mMutex);
return rc;
}
pthread_mutex_unlock(&mMutex);
mCameraInitialized = true;
mState = INITIALIZED;
LOGI("X");
return 0;
err1:
pthread_mutex_unlock(&mMutex);
return rc;
}
文件路径:hardware\qcom\camera\qcamera2\stack\mm-camera-interface\src\mm_camera_interface.c
camera_open
中干的事也不多,我省略掉了关于为 cam_obj
分配内存以及初始化的部分。实际上是调用实现层中的 mm_camera_open
来真正实现打开相机设备的操作,设备的各种信息都填充到 cam_obj
结构中。
int32_t camera_open(uint8_t camera_idx, mm_camera_vtbl_t **camera_vtbl)
{
int32_t rc = 0;
mm_camera_obj_t *cam_obj = NULL;
/* Do something in */
......
/* Do something out */
rc = mm_camera_open(cam_obj);
/* Do something in */
......
/* Do something out */
}
而关于初始化时调用的 mm_camera_intf_add_channel
代码如下。在第 16 行,通过调用实现层的 mm_camera_add_channel
来获取一个 channel id,也就是其句柄。
static uint32_t mm_camera_intf_add_channel(uint32_t camera_handle,
mm_camera_channel_attr_t *attr,
mm_camera_buf_notify_t channel_cb,
void *userdata)
{
uint32_t ch_id = 0;
mm_camera_obj_t * my_obj = NULL;
LOGD("E camera_handler = %d", camera_handle);
pthread_mutex_lock(&g_intf_lock);
my_obj = mm_camera_util_get_camera_by_handler(camera_handle);
if(my_obj) {
pthread_mutex_lock(&my_obj->cam_lock);
pthread_mutex_unlock(&g_intf_lock);
ch_id = mm_camera_add_channel(my_obj, attr, channel_cb, userdata);
} else {
pthread_mutex_unlock(&g_intf_lock);
}
LOGD("X ch_id = %d", ch_id);
return ch_id;
}
文件路径:hardware\qcom\camera\qcamera2\stack\mm-camera-interface\src\mm_camera.c
终于来到最底层的实现了,mm_camera_open
主要工作是填充 my_obj
,并且启动、初始化一些线程相关的东西,关于线程的部分我这里就省略掉了。
my_obj
的句柄,这里不深入分析。my_obj->ctrl_fd
中。注意设备文件的路径是 /dev/video0
(video 后面的数字表示打开设备的 id),并且在某些打开失败的情况下,会定时重新尝试打开直至成功。session
的 id 了,这里的具体实现也不深究。int32_t mm_camera_open(mm_camera_obj_t *my_obj)
{
char dev_name[MM_CAMERA_DEV_NAME_LEN];
int32_t rc = 0;
int8_t n_try=MM_CAMERA_DEV_OPEN_TRIES;
uint8_t sleep_msec=MM_CAMERA_DEV_OPEN_RETRY_SLEEP;
int cam_idx = 0;
const char *dev_name_value = NULL;
int l_errno = 0;
pthread_condattr_t cond_attr;
LOGD("begin\n");
if (NULL == my_obj) {
goto on_error;
}
dev_name_value = mm_camera_util_get_dev_name(my_obj->my_hdl);
if (NULL == dev_name_value) {
goto on_error;
}
snprintf(dev_name, sizeof(dev_name), "/dev/%s",
dev_name_value);
sscanf(dev_name, "/dev/video%d", &cam_idx);
LOGD("dev name = %s, cam_idx = %d", dev_name, cam_idx);
do{
n_try--;
errno = 0;
my_obj->ctrl_fd = open(dev_name, O_RDWR | O_NONBLOCK);
l_errno = errno;
LOGD("ctrl_fd = %d, errno == %d", my_obj->ctrl_fd, l_errno);
if((my_obj->ctrl_fd >= 0) || (errno != EIO && errno != ETIMEDOUT) || (n_try <= 0 )) {
break;
}
LOGE("Failed with %s error, retrying after %d milli-seconds",
strerror(errno), sleep_msec);
usleep(sleep_msec * 1000U);
}while (n_try > 0);
if (my_obj->ctrl_fd < 0) {
LOGE("cannot open control fd of '%s' (%s)\n",
dev_name, strerror(l_errno));
if (l_errno == EBUSY)
rc = -EUSERS;
else
rc = -1;
goto on_error;
} else {
mm_camera_get_session_id(my_obj, &my_obj->sessionid);
LOGH("Camera Opened id = %d sessionid = %d", cam_idx, my_obj->sessionid);
}
/* Do something in */
......
/* Do something out */
/* unlock cam_lock, we need release global intf_lock in camera_open(),
* in order not block operation of other Camera in dual camera use case.*/
pthread_mutex_unlock(&my_obj->cam_lock);
return rc;
}
初始化的相关部分,mm_camera_add_channel
代码如下。
ch_obj
中。ch_obj
结构。首先调用 mm_camera_util_generate_handler
为其生成一个句柄(也是该函数的返回值),然后将状态设置为 STOPPED,注意这里还保存了 my_obj
的指针及其 session id
,最后调用 mm_channel_init
完成了 Channel 的初始化。uint32_t mm_camera_add_channel(mm_camera_obj_t *my_obj,
mm_camera_channel_attr_t *attr,
mm_camera_buf_notify_t channel_cb,
void *userdata)
{
mm_channel_t *ch_obj = NULL;
uint8_t ch_idx = 0;
uint32_t ch_hdl = 0;
for(ch_idx = 0; ch_idx < MM_CAMERA_CHANNEL_MAX; ch_idx++) {
if (MM_CHANNEL_STATE_NOTUSED == my_obj->ch[ch_idx].state) {
ch_obj = &my_obj->ch[ch_idx];
break;
}
}
if (NULL != ch_obj) {
/* initialize channel obj */
memset(ch_obj, 0, sizeof(mm_channel_t));
ch_hdl = mm_camera_util_generate_handler(ch_idx);
ch_obj->my_hdl = ch_hdl;
ch_obj->state = MM_CHANNEL_STATE_STOPPED;
ch_obj->cam_obj = my_obj;
pthread_mutex_init(&ch_obj->ch_lock, NULL);
ch_obj->sessionid = my_obj->sessionid;
mm_channel_init(ch_obj, attr, channel_cb, userdata);
}
pthread_mutex_unlock(&my_obj->cam_lock);
return ch_hdl;
}
在这一部分,连路的最终面貌可以简单地表示如下图。
注意图中的 captureResultCb
相关的内容我并没有分析,这是一个回调函数,应该是在拍照和预览数据上传时用到的,把它画出来是因为在培训的时候有提到,所以这里也提及一下,表明它与 mCallbackOps
有这么一个联系。
总而言之,上面这一顿操作下来后,相机从上到下的整个连路就已经打通,接下来应该只要 APP 按照流程下发 Preview 的 Request 就可以开始获取预览数据了。
如果不出意外的话…下一步的学习计划就是了解 Preview,Capture 的控制、数据流。