Camera

先看一下抽象层的主要流程:

首先启动一个守护进程

int qcamsvr_start(void)( qcamsvr.c)

{

1. server_fd = open(server_dev_name, O_RDWR);//打开服务对应的文件节点

2. if (mctl_load_comps()) //加载所有需要的组件

3. rc = qcamsvr_load_gesture_lib(&gesture_info.gesture_lib);//加载手势库

4. ez_server_socket_id = eztune_setup_server("127.0.0.1", "55555");

if (pipe(ez_cmd_pipe)

ez_prev_server_socket_id = eztune_setup_server("127.0.0.1", "55556");

if(pipe(ez_prev_cmd_pipe)

//创建两个socket端口,同时建立两个pipe文件对两个端口进行监控

5. if (get_mctl_node_info(server_fd, &mctl_node_info))//通过服务节点获取服务的相关信息

{

//此处获取的是内核中调用msm_sensor_register()注册的sensor节点信息

}

6. sub.type = V4L2_EVENT_ALL;

rc = ioctl(server_fd, VIDIOC_SUBSCRIBE_EVENT, &sub);//通过服务设备文件的ioctl接口,订阅所有的事件

7. config_arg.server_fd = server_fd;

config_arg.ez_read_fd = ez_cmd_pipe[0];

config_arg.ez_write_fd = ez_cmd_pipe[1];

config_arg.ez_prev_read_fd = ez_prev_cmd_pipe[0];

config_arg.ez_prev_write_fd = ez_prev_cmd_pipe[1];//初始化配置线程的参数

8.下面就是一个循环,对这几个文件进行poll

do {

fds[0].fd = server_fd;

fds[0].events = POLLPRI;

fds[1].fd = ez_server_socket_id;

fds[1].events = POLLIN;

fds[2].fd = ez_prev_server_socket_id;

fds[2].events = POLLIN;

rc = poll(fds, 3, timeoutms);

if (fds[0].revents & POLLPRI) { /* Server Node Wake Up *

//对服务的设备文件进行监视,当遇到打开事件的时候,立即创建一个配置线程

rc = qcamsvr_process_server_node_event(&config_arg, &mctl_node_info,

&gesture_info);

}

//线面就是对两个socket进程监视和处理。

if ((fds[1].revents & POLLIN) == POLLIN) { /* EzTune Server */

int client_socket_id;

client_socket_id = accept(ez_server_socket_id,

(struct sockaddr *)&addr_client_inet, &addr_client_len);

write(ez_cmd_pipe[1], &client_socket_id, sizeof(int));

}

}

if ((fds[2].revents & POLLIN) == POLLIN) { /* EzTune Prev Server */

int client_socket_id;

client_socket_id = accept(ez_prev_server_socket_id,

(struct sockaddr *)&addr_client_inet, &addr_client_len);

write(ez_prev_cmd_pipe[1], &client_socket_id, sizeof(int));

}

}

} /* Else for Poll rc */

} while (1);

}

下面进入配置线程创建的流程:

//取出服务节点产生的事件,然后根据配置节点的名称,分发给各自独立的主控制线程

1. static int qcamsvr_process_server_node_event()

{

//下命令让服务模块的事件出队列进行处理

rc = ioctl(config_arg->server_fd, VIDIOC_DQEVENT, &v4l2_evt);

if (v4l2_evt.type == V4L2_EVENT_PRIVATE_START + MSM_GES_RESP_V4L2)

{

//如果是手势事件,则进行一系列的处理

if (ctrl->type == MSM_V4L2_GES_OPEN) {

//设置主控线程的接口

p_gesture_info->cam_mctl.svr_ops.launch_mctl_thread =

create_v4l2_conf_thread;

//设置主控线程的退出接口

p_gesture_info->cam_mctl.svr_ops.release_mctl_thread =

destroy_v4l2_cam_conf_thread;

//设置camera使能

p_gesture_info->cam_mctl.svr_ops.camera_available =

qcamsvr_camera_available;

//设置服务设备文件的文件句柄

p_gesture_info->cam_mctl.svr_ops.server_fd = config_arg->server_fd;

//创建手势服务

status = p_gesture_info->gesture_lib.gesture_service_create(

&p_gesture_info->cam_mctl, &p_gesture_info->observer);

}

else if (ctrl->type == MSM_V4L2_GES_CLOSE) {

//消亡手势服务

status = p_gesture_info->gesture_lib.gesture_service_send_data(ctrl)

}

if ((status == CAMERA_SUCCESS) &&

(ctrl->type != MSM_V4L2_GES_CLOSE)) {

//如果成功,且文件打开,则向手势服务发送数据

status = p_gesture_info->gesture_lib.gesture_service_send_data(ctrl);

if (status != CAMERA_SUCCESS) {

LOGE("gesture_service_send_data failed");

}

} else {

if (ctrl->type == MSM_V4L2_GES_CLOSE) {

ctrl->status = CAM_CTRL_SUCCESS;

} else {

LOGE("gesture send failure message");

ctrl->status = CAM_CTRL_FAILED;

}

//将操作结果反馈给camera服务

v4l2_ioctl.ioctl_ptr = ctrl;

qcamsvr_send_ctrl_cmd_done(config_arg->server_fd, &v4l2_ioctl);

//如果是camera事件,则进行一系列的处理

}

else if (v4l2_evt.type == V4L2_EVENT_PRIVATE_START + MSM_CAM_RESP_V4L2)

{

if (ctrl->type == MSM_V4L2_OPEN) {

//通过pipe进行一些初始化工作

//创建一个核心的线程

if ((tmp_mctl_struct->handle =

//反馈结果给camera服务端

ctrl->status = CAM_CTRL_SUCCESS;

v4l2_ioctl.ioctl_ptr = ctrl;

qcamsvr_send_ctrl_cmd_done(config_arg->server_fd, &v4l2_ioctl);

}

else if (ctrl->type == MSM_V4L2_CLOSE){

//进行一些消亡工作

//通过写一些pipe

if (destroy_v4l2_cam_conf_thread(tmp_mctl_struct->handle) < 0) //消亡只线程

ctrl->status = CAM_CTRL_SUCCESS;

v4l2_ioctl.ioctl_ptr = ctrl;

//反馈结果给camera服务

qcamsvr_send_ctrl_cmd_done(config_arg->server_fd, &v4l2_ioctl);

}

else {

//通过pipe写一些命令,等待配置返回

}

}

//首先来看一下刚刚的线程创建函数

void *create_v4l2_conf_thread(struct config_thread_arguments* arg)

{

//核心工作就是创建了个线程

rc = pthread_create(&pme->cam_mctl_thread_id, NULL, cam_mctl_thread, pme);

}

//下面进入创建的配置线程的主函数:

static void *cam_mctl_thread(void *data)(mctl.c)

{

//首先初始化需要监控的文件句柄

pipe_readfd = arg->read_fd;

pipe_writefd = arg->write_fd;

server_fd = arg->server_fd;

ez_pipe_readfd = arg->ez_read_fd;

ez_client_fd = -1;

ez_prev_pipe_readfd = arg->ez_prev_read_fd;

ez_prev_client_fd = -1;

//向对应的配置节点下命令监控所有事件(此文件句柄具体标识什么意思暂时还没搞清楚)

sub.type = V4L2_EVENT_ALL;

rc = ioctl(cam_fd, VIDIOC_SUBSCRIBE_EVENT, &sub);

//下面开始进入循环的监控

do {

//文件句柄初始化

fds[0].fd = cam_fd;

fds[0].events = POLLPRI;

fds[1].fd = pipe_readfd;

fds[1].events = POLLPRI | POLLIN;

fds[2].fd = ez_pipe_readfd;

fds[2].events = POLLIN;

fds[3].fd = ez_client_fd;

fds[3].events = POLLIN;

fds[4].fd = ez_prev_pipe_readfd;

fds[4].events = POLLIN;

fds[5].fd = ez_prev_client_fd;

fds[5].events = POLLIN;

/* evt/msg from qcam server */

if (ctrl->type == MSM_V4L2_CLOSE) {

//关闭所有的资源

config_shutdown_pp(pme->p_cfg_ctrl);

//反馈结果给服务

rc = mctl_send_ctrl_cmd_done(pme->p_cfg_ctrl, NULL, TRUE);

}

else {

//此函数为用户控件的APP处理对应的命令

if (mctl_proc_v4l2_request(pme, ctrl) < 0)

}

/* evt/msg from config node */

rc = ioctl(cam_fd, VIDIOC_DQEVENT, &v4l2_event);//下事件出队列的命令

if (v4l2_event.type ==

V4L2_EVENT_PRIVATE_START + MSM_CAM_RESP_DIV_FRAME_EVT_MSG) {

//进程对应的帧转移

mctl_pp_divert_frame(p_cfg_ctrl,

(void *)&(event_data.isp_data.div_frame));

}else if(v4l2_event.type ==

V4L2_EVENT_PRIVATE_START + MSM_CAM_RESP_MCTL_PP_EVENT) {

//处理后置的事件

mctl_pp_proc_event(p_cfg_ctrl,

(void *)&(event_data.isp_data.pp_event_info));

}

else if (v4l2_event.type ==

V4L2_EVENT_PRIVATE_START + MSM_CAM_RESP_STAT_EVT_MSG) {

//处理正常的事件消息

mctl_proc_event_message (pme, isp_adsp);

}

else {

CDBG_HIGH("%s: Error: should not be here", __func__);

}

/* evt/msg from eztune pipe */

if (ez_client_fd > 0)

mctl_eztune_server_connect(pme, ez_client_fd);

/* evt/msg from eztune client */

if (ez_client_fd > 0) {

mctl_eztune_read_and_proc_cmd(EZ_MCTL_SOCKET_CMD);

/* evt/msg from eztune prev pipe */

if (ez_prev_client_fd > 0)

mctl_eztune_prev_server_connect(pme, ez_prev_client_fd);

}

/* evt/msg from eztune prev client */

if ((fds[5].revents & POLLIN) == POLLIN) {

if (ez_prev_client_fd > 0) {

mctl_eztune_read_and_proc_cmd(EZ_MCTL_PREV_SOCKET_CMD);

}

}

}wile(TRUE)

//循环结束取消订阅所有消息

if (ioctl(cam_fd, VIDIOC_UNSUBSCRIBE_EVENT, &sub) < 0)

}

先来看看camera在硬件抽象层的接口:

主要是三点:

1. preview:预览

2. recording 录像

3. picture 拍照

模块接口函数:

get_number_of_cameras: get_number_of_cameras,

get_camera_info: get_camera_info,

camera_info_t:

typedef struct {

int modes_supported;//支持的模式

int8_t camera_id;//id标识

cam_position_t position;//前摄还是后摄

uint32_t sensor_mount_angle;//角度

}camera_info_t;

我们目前使用的高通8X平台:

Camera模块:

此模块有一个全局的camera服务结构体实例,用于全局管理各种子系统设备。

子系统设备常见的有:

enum msm_cam_subdev_type {

CSIPHY_DEV,

CSID_DEV,

CSIC_DEV,

ISPIF_DEV,

VFE_DEV,

AXI_DEV,

VPE_DEV,

SENSOR_DEV,

ACTUATOR_DEV,

EEPROM_DEV,

GESTURE_DEV,

};

定义一个抽象的camera服务设备:

struct msm_cam_server_dev {

/* config node device*/

struct platform_device *server_pdev;

/* server node v4l2 device */

struct v4l2_device v4l2_dev;

struct video_device *video_dev;

struct media_device media_dev;

/* info of sensors successfully probed*/

struct msm_camera_info camera_info;

/* info of configs successfully created*/

struct msm_cam_config_dev_info config_info;

/* active working camera device - only one allowed at this time*/

struct msm_cam_v4l2_device *pcam_active;

/* number of camera devices opened*/

atomic_t number_pcam_active;

struct v4l2_queue_util server_command_queue;

/* This queue used by the config thread to send responses back to the

* control thread. It is accessed only from a process context.

*/

struct msm_cam_server_queue server_queue[MAX_NUM_ACTIVE_CAMERA];

uint32_t server_evt_id;

struct msm_cam_server_mctl_inst mctl[MAX_NUM_ACTIVE_CAMERA];

uint32_t mctl_handle_cnt;

int use_count;

/* all the registered ISP subdevice*/

struct msm_isp_ops *isp_subdev[MSM_MAX_CAMERA_CONFIGS];

/* info of MCTL nodes successfully probed*/

struct msm_mctl_node_info mctl_node_info;

struct mutex server_lock;

struct mutex server_queue_lock;

/*v4l2 subdevs*/

struct v4l2_subdev *csiphy_device[MAX_NUM_CSIPHY_DEV];

struct v4l2_subdev *csid_device[MAX_NUM_CSID_DEV];

struct v4l2_subdev *csic_device[MAX_NUM_CSIC_DEV];

struct v4l2_subdev *ispif_device;

struct v4l2_subdev *vfe_device[MAX_NUM_VFE_DEV];

struct v4l2_subdev *axi_device[MAX_NUM_AXI_DEV];

struct v4l2_subdev *vpe_device[MAX_NUM_VPE_DEV];

struct v4l2_subdev *gesture_device;

};

从控制流的角度来分析下camera的流程。

首先camera会启动一个daemon进程来进行核心的操作。

启动daemon进程的地方:

在init.target.rc文件中

#start camera server as daemon

service qcamerasvr /system/bin/mm-qcamera-daemon

class late_start

user system

group system camera inet

生成此mm-qcamera-daemon bin档的地方:

android\vendor\qcom\proprietary\mm-camera\apps\appslib\Android.mk

此mk文件生成了mm-qcamera-daemon bin档

Daemon进程的入口函数:mian()(camdaemon.c)

一个camera的守护进程在init进程中,开启的一个service

此线程与具体的sensor相关联,负责对sensor进行具体细节的操作

此为daemon进程的主线程,从server node收集事件,纷发给mctl thread,根据config的name,与server节点进行队列,不断轮询其事件队列,获取command,进行全局处理

mctl_pp_poll_thread

mctl thread

main daemon thread

Daemon

此线程与kernel中config节点进行通信,轮询节点的消息队列中获得command,进行全局处理

(每一个config节点都对应一个mctl thread)

抽象层到内核层的大致流程:

抽象层主要通过server node和config node将command下到内核,对应的节点驱动将command通过事件队列进行管理。

而daemon进程通过开启对应的线程,不停的对事件队列进行轮询,处理上层下的command

在main daemon thread中重要的任务:

一:将sensor操作关联的硬件组件加载进来,还要加载一些必备的库,为camera的正式工作铺垫环境:

① AXI_comp_create

② sensor_comp_create

③ flash_led_comp_create

④ flash_strobe_comp_create

⑤ CAMIF_comp_create

⑥ VFE_comp_create

⑦ ACTUATOR_comp_create

⑧ eeprom_comp_create

⑨ mctl_load_stats_proc_lib

⑩ mctl_load_frame_proc_lib

二.线程的循环工作

线程,顾名思义,肯定有一个封闭的循环体,在循环体中做一些核心的操作

而Daemon进程的主线程轮询服务节点的event queue,获取事件,纷发给各自的mctl thread

Daemon进行的主线程主要处理一下基类事件“

① MSM_GES_RESP_V4L2 :

Open:主要进行初始化,铺垫环境,开启处理camera细节活动的线程

Close:进行一些善后工作

② MSM_CAM_RESP_V4L2:处理open和colse

Open:主要进行初始化,铺垫环境,开启处理camera细节活动的线程

Close:进行一些善后工作

③ 其他一些事件都是通过pipe通信直接写入到①②两点创建的线程中(send command through pipe and wait for config to return)

在mctl thread中重要的任务:

一. 打开confing节点文件

二. 调用create_camfd_receive_socket猜测是与硬件抽象层进行直接通信的

三. 创建mctl_pp_poll_thread线程,

四. 初始化camera的几个feature:

①zoom_init_ctrl

②bestshot_init

③hdr_init

五.通过pipe通信获取server节点的控制事件,事件由Daemon进程的主控线程获取并且通过pipe传递过来

六.通过监测config节点的事件获取config节点对应的控制command

主要监测三类事件:

① MSM_CAM_RESP_DIV_FRAME_EVT_MSG

② MSM_CAM_RESP_MCTL_PP_EVENT

③ MSM_CAM_RESP_STAT_EVT_MSG

将这三个事件以command的形式,通过pipe通信发送到(一)中创建的PP线程中

在mctl_pp_poll_thread中重要的任务:

一:对几个pipe文件进行监测,与其他线程进行交互

几种事件:

①/* Events on pipe between mctl thread - mctl pp thread */

②/* Events on user created socket */

③/* Events on mctl pp node */

④/* Events on pipe between mctl pp thread and c2d thread */

Qualcomm 8X camera daemon进程浅析的更多相关文章

  1. Qualcomm 8X camera过程解析【转】

    本文转载自:http://blog.csdn.net/gabbzang/article/details/19906687 http://www.01yun.com/mobile_development ...

  2. qualcomm platform camera porting

    转载自http://www.cnblogs.com/thjfk/p/4086001.html camera基本代码架构 Camera原理:外部光线穿过lens后,经过color filter滤波后照射 ...

  3. Qualcomm平台camera调试移植入门

    1  camera基本代码架构 高通平台对于camera的代码组织,大体上还是遵循Android的框架:即上层应用和HAL层交互,高通平台在HAL层里面实现自己的一套管理策略:在kernel中实现se ...

  4. 在Hudson Job中启动daemon进程

    场景 在Hudson中新建一个Job用于构建Web工程,在Job的构建脚本的最后会启动Jetty,观察发现Jetty启动之后一小段时间,进程就终止了.   环境 CentOS 6,Hudson 3.0 ...

  5. Linux中创建Daemon进程的三种方法

    什么是daemon进程? Unix/Linux中的daemon进程类似于Windows中的后台服务进程,一直在后台运行运行,例如http服务进程nginx,ssh服务进程sshd等.注意,其英文拼写为 ...

  6. Linux系统编程——Daemon进程

    目录 Daemon进程介绍 前提知识 Daemon进程的编程规则 Daemon进程介绍 Daemon运行在后台也称作"后台服务进程". 它是没有控制终端与之相连的进程.它独立与控制 ...

  7. PHP 如何创建守护(daemon)进程

    先讲几个概念 守护进程: Linux中的后台服务进程.它是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件.守护进程常常在系统引导装入时启动,在系统关闭时终止. ...

  8. Daemon进程

    这又是一个有趣的概念,daemon在英语中是"精灵"的意思,就像我们经常在迪斯尼动画里见到的那些,有些会飞,有些不会,经常围着动画片的主人公转来转去,啰里啰唆地提一些忠告,时不时倒 ...

  9. linux下的daemon进程

    转自:http://www.cnblogs.com/xuxm2007/archive/2011/07/29/2121280.html   #include <unistd.h> int d ...

随机推荐

  1. 【CF493E】【数学】Vasya and Polynomial

    Vasya is studying in the last class of school and soon he will take exams. He decided to study polyn ...

  2. 通过 ANE(Adobe Native Extension) 启动Andriod服务 推送消息(一)

    项目组用air来开发手游, 但有些在原生应用里很容易实现的功能没有办法在air中直接调用,比如说震动,服务等等.但Adobe 提供了一种方法让air间接调用本地代码(java,object-c...) ...

  3. ajax GET和POST请求web api 的几种方式

    GET请求 1.无参数get请求 一般get请求有两种写法,一种是 $.get()   一种是$.ajax({type:"get"}), 我个人比较喜欢用后者. 下面例子主要是ge ...

  4. ubuntu 基本操作(1)

    命令行界面与图形界面 全屏模式: ctrl + alt + f1进入命令行界面 ctrl + alt + f7 切换图形界面 小窗口: ctrl+alt+t:进入终端 设计ubuntu root 密码 ...

  5. phpcms V9 添加模块(转)

    转自:http://www.cnblogs.com/Braveliu/p/5101345.html 为phpcms创建一个模块的开发流程 [1]创建模块目录 通过前面的学习,我们已经知道phpcms ...

  6. yii 使用renderPartial调用另外一个控制器的视图

    以下由我们在信易网络公司开发项目的时候终结出的一些经验 我们可以使用renderPartial访问存储在不同控制器的视图文件夹中的部分视图文件. 在Yii1.1.3中,我们使用双斜线“//”,程序就会 ...

  7. Python Tips and Traps(一)

    1.如果想得到一个列表的index和内容,可以通过enumerate快速实现 drinks = ['coffee','tea', 'milk', 'water'] for index, drink i ...

  8. 常用webservice接口地址

    天气预报Web服务,数据来源于中国气象局Endpoint :http://www.webxml.com.cn/WebServices/WeatherWebService.asmxDisco       ...

  9. Google java编程技术规范

    不遵循规范的程序猿,不是好的coder. 学习java有一段时间了,一直想找java编程技术规范来学习一下,幸而网络资源丰富,各路玩家乐于分享,省去了好多麻烦,姑且算站在网友的肩上,砥砺前行. /** ...

  10. Visual Studio 内置快速生产代码简写集合

    工作之余,整理了一下,Visual Studio 里面的快速生产代码缩写集合,这个拿出来分享想一下,希望对您有所帮助. 文件下载地址:VS内置生产代码缩写集合文档.rar 首字母 简写 生成代码 a ...