系列文章
基于HAL1: camera hal层框架源码系列:
HAL1 – Camera Open/Close (1)
一、代码流程图
整个流程就很清晰了。
二、源码及log分析
1.camera service启动(开机时调用一次)
I CameraService: CameraService started (pid=648)
I CameraService: CameraService process starting
2.get_num_of_cameras:获取摄像头数量(开机时调用一次)
I QCamera : <MCI><INFO> get_num_of_cameras: 2783: dev_info[id=0,name='video1']
I QCamera : <MCI><INFO> get_num_of_cameras: 2783: dev_info[id=1,name='video2']
I QCamera : <MCI><INFO> sort_camera_info: 2617: Camera id: 0 facing: 0, type: 1 is_yuv: 0
I QCamera : <MCI><INFO> sort_camera_info: 2617: Camera id: 1 facing: 1, type: 1 is_yuv: 0
I QCamera : <MCI><INFO> sort_camera_info: 2623: Number of cameras 2 sorted 2
I QCamera : <MCI><INFO> get_num_of_cameras: 2801: num_cameras=2
I CamProvider@2.4-impl: Loaded "QCamera Module" camera module
QCamera : <HAL><INFO> getCameraInfo: 342: Camera id 0 API version 768
开机的时候,会去调用get_num_of_cameras获取摄像头数量,sort_camera_info查询摄像头信息,
只会调用一次。
摄像头节点 : /dev/video1 ,/dev/video2
摄像头数量:num_cameras=2
-
摄像头信息:
Camera id: 0 facing: 0(后摄), type: 1 is_yuv: 0(不是YUV类型)
Camera id: 1 facing: 1(前摄), type: 1 is_yuv: 0(不是YUV类型)
ps:判断是前摄或者后置,要根据facing来,camera id 0 可能是前摄或者后摄!
不能根据camera id判断前后摄!
-
摄像头加载的模块: CamProvider@2.4-impl: Loaded "QCamera Module" camera module
以上都是开机时调用的,也是准备工作,接下来就是每次打开camera都会调用的步骤
3.service连接client实例
I CameraService: CameraService::connect call (PID -1 "org.codeaurora.snapcam", camera ID 0) for HAL
version 256 and Camera API version 1
可以看到Log打印的信息有App API的版本和HAL版本, API版本只有API1 和API2, HAL版本一般有三个结果: 256, 768, default.
256代表使用HAL1, 768为HAL3, default一般表示调用的是没有特别指定版本, 根据平台配置来决定,这种情况如果想知道到底使用的哪个版本, 需要看平台代码或者一些Log.
4.openCamera
I QCamera : <HAL><INFO> openLegacy: 503: openLegacy halVersion: 256 cameraId = 0
I QCamera : <HAL><INFO> openCamera: 1915: [KPI Perf]: E PROFILE_OPEN_CAMERA camera id 0
I mm-camera: <SHIM >< INFO> 538: mct_shimlayer_start_session: Session start session =2
I mm-camera: <SHIM >< INFO> 132: mct_shimlayer_module_init: Module name : iface: E
-
openLegacy:
openLegacy可以设置 用hal1的方式还是hal3的方式打开camera。
这是版本是256,对应hal1! regiseter ops(绑定camera操作函数)
QCamera2HardwareInterface::QCamera2HardwareInterface(uint32_t cameraId)
: mCameraId(cameraId),
···
m_bNeedHalPP(FALSE)
{
#ifdef TARGET_TS_MAKEUP
memset(&mFaceRect, -1, sizeof(mFaceRect));
#endif
getLogLevel();
ATRACE_CAMSCOPE_CALL(CAMSCOPE_HAL1_QCAMERA2HWI);
mCameraDevice.common.tag = HARDWARE_DEVICE_TAG;
mCameraDevice.common.version = HARDWARE_DEVICE_API_VERSION(1, 0);
mCameraDevice.common.close = close_camera_device;
mCameraDevice.ops = &mCameraOps;
mCameraDevice.priv = this;
这里 mCameraDevice.ops = &mCameraOps;
这里分析一下Open camera 流程:
1. QCamera2HardwareInterface::openCamera(struct hw_device_t **hw_device) --HWI
|
2.rc = openCamera(); --mm_camera_interface.c
|
3.rc = mm_camera_open(cam_obj); --mm_camera.c
mm_camera_open
int32_t mm_camera_open(mm_camera_obj_t *my_obj)
{
···
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;
//第1步:Open Video node
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);
}
#ifdef DAEMON_PRESENT
/* open domain socket*/
n_try = MM_CAMERA_DEV_OPEN_TRIES;
do {
n_try--;
//第2步:Create Socket
my_obj->ds_fd = mm_camera_socket_create(cam_idx, MM_CAMERA_SOCK_TYPE_UDP);
l_errno = errno;
LOGD("ds_fd = %d, errno = %d", my_obj->ds_fd, l_errno);
if((my_obj->ds_fd >= 0) || (n_try <= 0 )) {
LOGD("opened, break out while loop");
break;
}
LOGD("failed with I/O error retrying after %d milli-seconds",
sleep_msec);
usleep(sleep_msec * 1000U);
} while (n_try > 0);
if (my_obj->ds_fd < 0) {
LOGE("cannot open domain socket fd of '%s'(%s)\n",
dev_name, strerror(l_errno));
rc = -1;
goto on_error;
}
#else /* DAEMON_PRESENT */
cam_status_t cam_status;
//第3步:open session
cam_status = mm_camera_module_open_session(my_obj->sessionid,
mm_camera_module_event_handler);
if (cam_status < 0) {
LOGE("Failed to open session");
if (cam_status == CAM_STATUS_BUSY) {
rc = -EUSERS;
} else {
rc = -1;
}
goto on_error;
}
#endif /* DAEMON_PRESENT */
pthread_condattr_init(&cond_attr);
pthread_condattr_setclock(&cond_attr, CLOCK_MONOTONIC);
pthread_mutex_init(&my_obj->msg_lock, NULL);
pthread_mutex_init(&my_obj->cb_lock, NULL);
pthread_mutex_init(&my_obj->evt_lock, NULL);
pthread_cond_init(&my_obj->evt_cond, &cond_attr);
pthread_condattr_destroy(&cond_attr);
//第4步:Launch evt Thread
LOGD("Launch evt Thread in Cam Open");
snprintf(my_obj->evt_thread.threadName, THREAD_NAME_SIZE, "CAM_Dispatch");
mm_camera_cmd_thread_launch(&my_obj->evt_thread,
mm_camera_dispatch_app_event,
(void *)my_obj);
//第5步:Launch evt Poll
/* launch event poll thread
* we will add evt fd into event poll thread upon user first register for evt */
LOGD("Launch evt Poll Thread in Cam Open");
snprintf(my_obj->evt_poll_thread.threadName, THREAD_NAME_SIZE, "CAM_evntPoll");
mm_camera_poll_thread_launch(&my_obj->evt_poll_thread,
MM_CAMERA_POLL_TYPE_EVT);
mm_camera_evt_sub(my_obj, TRUE);
/* 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);
LOGD("end (rc = %d)\n", rc);
return rc;
on_error:
if (NULL == dev_name_value) {
LOGE("Invalid device name\n");
rc = -1;
}
if (NULL == my_obj) {
LOGE("Invalid camera object\n");
rc = -1;
} else {
if (my_obj->ctrl_fd >= 0) {
close(my_obj->ctrl_fd);
my_obj->ctrl_fd = -1;
}
#ifdef DAEMON_PRESENT
if (my_obj->ds_fd >= 0) {
mm_camera_socket_close(my_obj->ds_fd);
my_obj->ds_fd = -1;
}
#endif
}
/* 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;
}
关键步骤:
- 1.Open Video node
老架构的代码是通过Socket
-
2.Create Socket
进程中的通信方式:本地套接字,如果你不熟悉,可以参考以下文章,加深理解。
本地套接字(Unix domain socket IPC)
新架构的代码
2. 创建session ->调用到 mct_shimlayer_start_session
3.Launch evt cmd Thread
给注册了事件的应用 分派事件4.Launch poll Thread
该线程会创建一个管道,用poll(轮询)的方式监听内核的event事件
继续当一名咸鱼(* ̄︶ ̄)!