MM-Camera架构-Preview 流程分析

image

目录

文章目录

  • 1 log开的好,问题都能搞
  • 2 lib
  • 3 preview
    • 3.1 打开视频流
      • 3.1.1 cpp\_module\_start\_session
      • 3.1.2 cpp\_thread\_create
      • 3.1.3 cpp\_thread\_func
        • sundp-3.1 cpp\_hardware\_open\_subdev(ctrl->cpphw)
        • sundp-3.2 cpp\_hardware\_process\_command(ctrl->cpphw, cmd)
    • 3.2 sensor视频流buffer队列
      • 3.2.1 cpp\_thread\_func
        • sundp-3.3 poll(pollfds, (nfds\_t)num\_fds, -1);
        • sundp-3.4 cpp\_thread\_process\_pipe\_message
        • sundp-3.5 cpp\_thread\_process\_hardware\_event
        • sundp-3.6 cpp\_hardware\_process\_command
        • sundp-3.7 msm\_cpp\_subdev\_do\_ioctl
        • sundp-3.8 msm\_cpp\_subdev\_fops\_compat\_ioctl
        • sundp-3.9 cpp\_module\_do\_ack
        • sundp-3.10 MCT\_EVENT\_MODULE\_EVENT
  • 4 dump image
  • struct
    • struct \_cpp\_module\_ctrl\_t
    • struct \_cpp\_hardware\_t
    • \_cpp\_thread\_msg\_t
    • \_cpp\_hardware\_cmd\_t
    • enum cpp\_hardware\_cmd\_type\_t
    • struct \_cpp\_module\_session\_params\_t

1 log开的好,问题都能搞

  • 22/12/01

在看preview流程的时候(mm-camera2/media-controller/modules/pproc-new/cpp/cpp_module.c ),上来有一句:
CPP_HIGH("name=%s", name);
查看定义在:mm-camera/mm-camera2/media-controller/modules/pproc-new/cpp/cpp_log.h

#define CPP_LOW(fmt, args…) CLOGL(CAM_CPP_MODULE, fmt, ##args)
#define CPP_HIGH(fmt, args…) CLOGH(CAM_CPP_MODULE, fmt, ##args)
#define CPP_ERR(fmt, args…) CLOGE(CAM_CPP_MODULE, fmt, ##args)
#define CPP_DBG(fmt, args…) CLOGD(CAM_CPP_MODULE, fmt, ##args)
#define CPP_INFO(fmt, args…) CLOGI(CAM_CPP_MODULE, fmt, ##args)
#define CPP_WARN(fmt, args…) CLOGW(CAM_CPP_MODULE, fmt, ##args)

看起来是针对mm-camera2/media-controller/modules/pproc-new/cpp/目录下的文件单独定义的log level。
CAM_CPP_MODULE是一个枚举值,同类型的还有以下这些。这些枚举值代表着不同模块的

typedef enum {CAM_NO_MODULE,CAM_MCT_MODULE,CAM_SENSOR_MODULE,CAM_IFACE_MODULE,CAM_ISP_MODULE,CAM_PPROC_MODULE,CAM_IMGLIB_MODULE,CAM_CPP_MODULE,CAM_HAL_MODULE,CAM_JPEG_MODULE,CAM_C2D_MODULE,CAM_STATS_MODULE,CAM_STATS_AF_MODULE,CAM_STATS_AEC_MODULE,CAM_STATS_AWB_MODULE,CAM_STATS_ASD_MODULE,CAM_STATS_AFD_MODULE,CAM_STATS_Q3A_MODULE,CAM_STATS_IS_MODULE,CAM_STATS_HAF_MODULE,CAM_STATS_CAF_SCAN_MODULE,CAM_SHIM_LAYER,CAM_LAST_MODULE
} cam_modules_t;

而像CLOGI,CLOGD,CLOGL等等也都有定义:

#undef CLOGI
#define CLOGI(module, fmt, args...)                \CLOGx(module, CAM_GLBL_DBG_INFO, fmt, ##args)
#undef CLOGD
#define CLOGD(module, fmt, args...)                \CLOGx(module, CAM_GLBL_DBG_DEBUG, fmt, ##args)
#undef CLOGL
#define CLOGL(module, fmt, args...)                \CLOGx(module, CAM_GLBL_DBG_LOW, fmt, ##args)
#undef CLOGW
#define CLOGW(module, fmt, args...)                \CLOGx(module, CAM_GLBL_DBG_WARN, fmt, ##args)
#undef CLOGH
#define CLOGH(module, fmt, args...)                \CLOGx(module, CAM_GLBL_DBG_HIGH, fmt, ##args)
#undef CLOGE
#define CLOGE(module, fmt, args...)                \CLOGx(module, CAM_GLBL_DBG_ERR, fmt, ##args)

CLOGx的定义如下,如果g_cam_log数组的元素存在,就可以打印log。

/* logging macros */
#undef CLOGx
#define CLOGx(module, level, fmt, args...)                         \if (g_cam_log[module][level]) {                                  \cam_debug_log(module, level, __func__, __LINE__, fmt, ##args); \}

g_cam_log是一个二维数组,代表当前跟踪日志记录配置。

/* current trace logging configuration */
/* g_cam_log[cam_modules_t][cam_global_debug_level_t] */
extern int g_cam_log[CAM_LAST_MODULE][CAM_GLBL_DBG_INFO + 1];
//CAM_LAST_MODULE和CAM_GLBL_DBG_INFO + 1代表两个enum的最大值

其中module代表的是组别,也就是上面的enum cam_modules_t。level代表的是log的等级,有如下等级:

/* values that persist.vendor.camera.global.debug can be set to */
/* all camera modules need to map their internal debug levels to this range */
typedef enum {CAM_GLBL_DBG_NONE  = 0,CAM_GLBL_DBG_ERR   = 1,CAM_GLBL_DBG_WARN  = 2,CAM_GLBL_DBG_HIGH  = 3,CAM_GLBL_DBG_DEBUG = 4,CAM_GLBL_DBG_LOW   = 5,CAM_GLBL_DBG_INFO  = 6
} cam_global_debug_level_t;
  • 22/12/02
    突然发现,HIGH等级的log貌似不打印,于是在文件里加上这几个debug的语句:

CPP_LOW(“sunmy_ low\n”);
CPP_HIGH(“sunmy_ HIGH\n”);
CPP_ERR(“sunmy_ ERR\n”);
CPP_DBG(“sunmy_ DBG\n”);
CPP_INFO(“sunmy_ INFO\n”);
CPP_WARN(“sunmy_ WARN\n”);

发现只有CPP_ERR,CPP_INFO,CPP_WARN等级的log默认打印,其余的默认不打印。
在camx-chi架构中,确实有很多log等级(比如verbose)需要setprop才能打开。这个的原理应该一样。
在文件mm-camera2/media-controller/modules/pproc-new/cpp/cpp_module.c 发现一个函数:

static void cpp_module_loglevel()
{char cpp_prop[PROPERTY_VALUE_MAX];memset(cpp_prop, 0, sizeof(cpp_prop));property_get("persist.vendor.camera.cpp.debug.mask", cpp_prop, "1");gcam_cpp_loglevel = atoi(cpp_prop);/* Keep the deafault modules enabled  */property_get("persist.vendor.camera.cpp.mod.mask", cpp_prop, "2097407");g_cpp_log_featureMask = atoi(cpp_prop);
}

发现setprop persist.vendor.camera.cpp.debug.mask为不同的值就可以打印不同level的log。

这个结构体数组可以打印所有组别的log:

/* current trace logging configuration */
typedef struct {cam_global_debug_level_t  level;int                       initialized;const char               *name;const char               *prop;
} module_debug_t;static module_debug_t cam_loginfo[(int)CAM_LAST_MODULE] = {{CAM_GLBL_DBG_ERR, 1,"",         "persist.vendor.camera.global.debug"     }, /* CAM_NO_MODULE     */{CAM_GLBL_DBG_ERR, 1,"<MCT   >", "persist.vendor.camera.mct.debug"        }, /* CAM_MCT_MODULE    */{CAM_GLBL_DBG_ERR, 1,"<SENSOR>", "persist.vendor.camera.sensor.debug"     }, /* CAM_SENSOR_MODULE */{CAM_GLBL_DBG_WARN, 1,"<IFACE >", "persist.vendor.camera.iface.logs"       }, /* CAM_IFACE_MODULE  */{CAM_GLBL_DBG_ERR, 1,"<ISP   >", "persist.vendor.camera.isp.debug"        }, /* CAM_ISP_MODULE    */{CAM_GLBL_DBG_ERR, 1,"<PPROC >", "persist.vendor.camera.pproc.debug.mask" }, /* CAM_PPROC_MODULE  */{CAM_GLBL_DBG_WARN, 1,"<IMGLIB>", "persist.vendor.camera.imglib.logs"      }, /* CAM_IMGLIB_MODULE */{CAM_GLBL_DBG_WARN, 1,"<CPP   >", "persist.vendor.camera.cpp.debug.mask"   }, /* CAM_CPP_MODULE    */{CAM_GLBL_DBG_ERR, 1,"<HAL   >", "persist.vendor.camera.hal.debug"        }, /* CAM_HAL_MODULE    */{CAM_GLBL_DBG_ERR, 1,"<JPEG  >", "persist.vendor.camera.mmstill.logs"     }, /* CAM_JPEG_MODULE   */{CAM_GLBL_DBG_WARN, 1,"<C2D   >", "persist.vendor.camera.c2d.debug.mask"   }, /* CAM_C2D_MODULE    */{CAM_GLBL_DBG_ERR, 1,"<STATS >", "persist.vendor.camera.stats.debug" }, /* CAM_STATS_MODULE  */{CAM_GLBL_DBG_ERR, 1,"<STATS_AF >", "persist.vendor.camera.stats.af.debug"    }, /* CAM_STATS_AF_MODULE   */{CAM_GLBL_DBG_ERR, 1,"<STATS_AEC >", "persist.vendor.camera.stats.aec.debug"  }, /* CAM_STATS_AEC_MODULE  */{CAM_GLBL_DBG_ERR, 1,"<STATS_AWB >", "persist.vendor.camera.stats.awb.debug"  }, /* CAM_STATS_AWB_MODULE  */{CAM_GLBL_DBG_ERR, 1,"<STATS_ASD >", "persist.vendor.camera.stats.asd.debug"  }, /* CAM_STATS_ASD_MODULE  */{CAM_GLBL_DBG_ERR, 1,"<STATS_AFD >", "persist.vendor.camera.stats.afd.debug"  }, /* CAM_STATS_AFD_MODULE  */{CAM_GLBL_DBG_ERR, 1,"<STATS_Q3A >", "persist.vendor.camera.stats.q3a.debug"  }, /* CAM_STATS_Q3A_MODULE  */{CAM_GLBL_DBG_ERR, 1,"<STATS_AIS >", "persist.vendor.camera.stats.is.debug"   }, /* CAM_STATS_IS_MODULE   */{CAM_GLBL_DBG_ERR, 1,"<STATS_HAF >", "persist.vendor.camera.stats.haf.debug"  }, /* CAM_STATS_HAF_MODULE  */{CAM_GLBL_DBG_ERR, 1,"<STATS_CAFSCAN >", "persist.vendor.camera.stats.cafscan"  }, /* CAM_STATS_CAFSCAN_MODULE  */{CAM_GLBL_DBG_ERR, 1,"<SHIM  >", "persist.vendor.camera.shim.debug"          }, /* CAM_SHIM_LAYER    */
};

2 lib

frameworks/av/services/camera/libcameraservice/ /system/lib/libcameraservice.so

hardware/qcom/camera/QCamera2/ /vendor/lib/hw/camera.sdm660.so
hardware/qcom/camera/QCamera2/stack/mm-camera-interface/ /vendor/lib/libmmcamera_interface.so

hardware/interfaces/camera/device/1.0/ /system/lib/android.hardware.camera.device@1.0.so

hardware/interfaces/camera/device/3.2/default/ /vendor/lib/camera.device@3.2-impl.so

mm-camera/mm-camera2/media-controller/modules/pproc-new/cpp/ /vendor/lib/libmmcamera2_cpp_module.so

mm-camera/mm-camera2/media-controller/mct_shim_layer/ /vendor/lib/libmmcamera2_mct_shimlayer.so

mm-camera/mm-camera2/media-controller/mct/ /vendor/lib/libmmcamera2_mct.so

3 preview

framework preview
image

3.1 打开视频流

3.1.1 cpp_module_start_session

打开摄像头preview时,会调用到cpp_module_start_session方法: vendor/qcom/proprietary/mm-camera/mm-camera2/media-controller/modules/pproc-new/cpp/cpp_module.c

boolean cpp_module_start_session(mct_module_t *module, uint32_t sessionid)
{cpp_module_ctrl_t *ctrl = (cpp_module_ctrl_t *) MCT_OBJECT_PRIVATE(module);if(ctrl->session_params[i] == NULL) {//初始化ctrl->session_params[]的内容//1.申请空间ctrl->session_params[i] = (cpp_module_session_params_t*) CAM_MALLOC(sizeof(cpp_module_session_params_t))//2.清零memset(ctrl->session_params[i], 0x00, sizeof(cpp_module_session_params_t));//3.初始化session_params 参考struct _cpp_module_session_params_t的成员}/* start the thread only when first session starts */if(ctrl->session_count == 0) {/* spawn the cpp thread */rc = cpp_thread_create(module);}
}

3.1.2 cpp_thread_create

将cpp_module_start_session的参数(mct_module_t *module)透传到cpp_thread_create中创建线程: vendor/qcom/proprietary/mm-camera/mm-camera2/media-controller/modules/pproc-new/cpp/cpp_thread.c

int32_t cpp_thread_create(mct_module_t *module)
{...//设置cpp_thread_started的标志ctrl->cpp_thread_started = FALSE;//创建线程  cpp_thread_func  线程函数的入口地址rc = pthread_create(&(ctrl->cpp_thread), NULL, cpp_thread_func, module);//修改线程名pthread_setname_np(ctrl->cpp_thread, "CAM_cpp");/* wait to confirm if the thread is started */    //PTHREAD_COND_WAIT_TIME即pthread_cond_timedwait 当在指定时间内有信号传过来时,pthread_cond_timedwait()返回0,否则返回一个非0数,rc就是返回值PTHREAD_COND_WAIT_TIME(&(ctrl->th_start_cond), &(ctrl->cpp_mutex),&timeout, CPP_WAIT_TIMEOUT, rc);
}

3.1.3 cpp_thread_func

vendor/qcom/proprietary/mm-camera/mm-camera2/media-controller/modules/pproc-new/cpp/cpp_thread.c

void* cpp_thread_func(void* data)
{mct_module_t *module = (mct_module_t *) data;cpp_module_ctrl_t *ctrl = (cpp_module_ctrl_t *) MCT_OBJECT_PRIVATE(module);PTHREAD_MUTEX_LOCK(&(ctrl->cpp_mutex));//设置cpp_thread_started状态,代表cpp_thread已经跑起来了ctrl->cpp_thread_started = TRUE;//用于唤醒th_start_condpthread_cond_signal(&(ctrl->th_start_cond));//------------------sundp-3.1--------------/* open the cpp hardware */rc = cpp_hardware_open_subdev(ctrl->cpphw);     /* subscribe for event on subdev fd *///注意cpp_hardware_cmd_t里的联合体cpp_hardware_cmd_t cmd;//cmd.type代表此命令的类型,除了这里的订阅事件,还有像CPP_HW_CMD_STREAMON,CPP_HW_CMD_STREAMON等等cmd.type = CPP_HW_CMD_SUBSCRIBE_EVENT;    //------------------sundp-3.2--------------rc = cpp_hardware_process_command(ctrl->cpphw, cmd);   /* poll on the pipe readfd and subdev fd */struct pollfd pollfds[2];uint32_t num_fds = 2;int ready = 0;uint32_t i = 0;pollfds[PIPE_FD_IDX].fd = ctrl->pfd[READ_FD];pollfds[PIPE_FD_IDX].events = POLLIN|POLLPRI;pollfds[SUBDEV_FD_IDX].fd = ctrl->cpphw->subdev_fd;pollfds[SUBDEV_FD_IDX].events = POLLIN|POLLPRI;while(1) {/* poll on the fds with no timeout *///------------------sundp-3.3--------------ready = poll(pollfds, (nfds_t)num_fds, -1);    if(ready > 0) {/* loop through the fds to see if any event has occured */for(i=0; i<num_fds; i++) {if(pollfds[i].revents & (POLLIN|POLLPRI)) {//pollfds[0].revents = 1//pollfds[1].revents = 0//pollfds[0].revents = 0//pollfds[1].revents = 2switch(i) {case PIPE_FD_IDX: {int num_read=0;cpp_thread_msg_t pipe_msg;num_read = read(pollfds[i].fd, &(pipe_msg),sizeof(cpp_thread_msg_t));//------------------sundp-3.4--------------rc = cpp_thread_process_pipe_message(ctrl, pipe_msg);    break;}case SUBDEV_FD_IDX: {//------------------sundp-3.5--------------rc = cpp_thread_process_hardware_event(ctrl);  //读到dev fd消息,处理内核事件  break;}}}}} 
}
sundp-3.1 cpp_hardware_open_subdev(ctrl->cpphw)

这里打开v4l的subdev。
ctrl->cpphw 是struct cpp_hardware_t定义的,结构体成员subdev_ids[MAX_CPP_DEVICES]代表的是:/dev/v4l-sundev*:

sdm660_64:/dev # ls v4l
v4l-subdev0 v4l-subdev11 v4l-subdev14 v4l-subdev17 v4l-subdev2 v4l-subdev22 v4l-subdev4 v4l-subdev7
v4l-subdev1 v4l-subdev12 v4l-subdev15 v4l-subdev18 v4l-subdev20 v4l-subdev23 v4l-subdev5 v4l-subdev8
v4l-subdev10 v4l-subdev13 v4l-subdev16 v4l-subdev19 v4l-subdev21 v4l-subdev3 v4l-subdev6 v4l-subdev9

这里前后置 打开的都是v4l-subdev17

代码路径:
vendor/qcom/proprietary/mm-camera/mm-camera2/media-controller/modules/pproc-new/cpp/cpp_hardware.c

int32_t cpp_hardware_open_subdev(cpp_hardware_t *cpphw)
{int fd;char dev_name[SUBDEV_NAME_SIZE_MAX];snprintf(dev_name, sizeof(dev_name), "/dev/v4l-subdev%d",cpphw->subdev_ids[0]);//打开v4l-subdevXX设备fd = open(dev_name, O_RDWR | O_NONBLOCK);cpphw->subdev_fd = fd;//flag已经打开设备cpphw->subdev_opened = TRUE;//通过ioctl,获取设备参数rc = ioctl(cpphw->subdev_fd, VIDIOC_MSM_CPP_GET_INST_INFO, &v4l2_ioctl);//填充ctrl->cpphw的信息:
}

open设备节点操作,对应到内核,kernel/msm-4.14/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c
cpp_open_node 主要做了两件事:1、初始化mem, 2、初始化硬件

cpp_init_mem() : 其实就是获取cpp_dev->iommu_hdl,这个东西是在msm_cam_smmu设备driver中统一管理的,vfe中有记录过这里。
cpp_init_hardware():设置一些硬件参数、时钟、注册中断以及buf管理接口:msm_cam_buf_mgr_register_ops()

static int cpp_open_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
{cpp_dev = v4l2_get_subdevdata(sd);if (cpp_dev->cpp_open_cnt == 1) {rc = cpp_init_mem(cpp_dev);rc = cpp_init_hardware(cpp_dev);cpp_dev->state = CPP_STATE_IDLE;}
}

VIDIOC_MSM_CPP_GET_INST_INFO不重要,简单记录:

static long msm_cpp_subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{switch (cmd) {case VIDIOC_DQEVENT:return v4l2_event_dequeue(vfh, arg, file->f_flags & O_NONBLOCK);case VIDIOC_SUBSCRIBE_EVENT:return v4l2_subdev_call(sd, core, subscribe_event, vfh, arg);case VIDIOC_MSM_CPP_GET_INST_INFO: {uint32_t i;struct cpp_device *cpp_dev = v4l2_get_subdevdata(sd);struct msm_camera_v4l2_ioctl_t *ioctl_ptr = arg;struct msm_cpp_frame_info_t inst_info;memset(&inst_info, 0, sizeof(struct msm_cpp_frame_info_t));for (i = 0; i < MAX_ACTIVE_CPP_INSTANCE; i++) {if (cpp_dev->cpp_subscribe_list[i].vfh == vfh) {inst_info.inst_id = i;break;}}}break;default:return v4l2_subdev_call(sd, core, ioctl, cmd, arg);}
}
sundp-3.2 cpp_hardware_process_command(ctrl->cpphw, cmd)

cpp_hardware_process_command 用于处理给硬件的命令。在此过程中更新硬件状态。

代码路径:vendor/qcom/proprietary/mm-camera/mm-camera2/media-controller/modules/pproc-new/cpp/cpp_hardware.c

int32_t cpp_hardware_process_command(cpp_hardware_t *cpphw,cpp_hardware_cmd_t cmd)
{...switch (cmd.type) {case CPP_HW_CMD_GET_CAPABILITIES:    rc = cpp_hardware_get_capabilities(cpphw);     break;case CPP_HW_CMD_SUBSCRIBE_EVENT:     rc = cpp_hardware_subcribe_v4l2_event(cpphw);  break;case CPP_HW_CMD_UNSUBSCRIBE_EVENT:   rc = cpp_hardware_unsubcribe_v4l2_event(cpphw);break;case CPP_HW_CMD_NOTIFY_EVENT:        rc = cpp_hardware_notify_event_get_data(cpphw, cmd.u.event_data); break;case CPP_HW_CMD_STREAMON:            rc = cpp_hardware_process_streamon(cpphw, cmd.u.buff_update);break;case CPP_HW_CMD_STREAMOFF:           rc = cpp_hardware_process_streamoff(cpphw, cmd.u.streamoff_data);  break;case CPP_HW_CMD_LOAD_FIRMWARE:       rc = cpp_hardware_load_firmware(cpphw);  break;case CPP_HW_CMD_PROCESS_FRAME:       rc = cpp_hardware_process_frame(cpphw, &cmd);  break;case CPP_HW_CMD_PROCESS_PARTIAL_FRAME:rc = cpp_hardware_process_partial_frame(cpphw, cmd.u.partial_frame);  break;case CPP_HW_CMD_QUEUE_BUF:           rc = cpp_hardware_send_buf_done(cpphw, cmd.u.event_data);  break;case CPP_HW_CMD_SET_CLK:             rc = cpp_hardware_set_clock(cpphw, &cmd.u.clock_settings);  break;case CPP_HW_CMD_BUF_UPDATE:          rc = cpp_hardware_update_buffer_list(cpphw, cmd.u.buff_update);  break;case CPP_HW_CMD_POP_STREAM_BUFFER:   rc = cpp_hardware_pop_stream_buffer(cpphw, cmd.u.event_data);  break;case CPP_HW_CMD_NOTIFY_BUF_DONE:     rc = cpp_hardware_notify_buf_done(cpphw, cmd.u.buf_done_identity);  break;case CPP_HW_CMD_UPDATE_PENDING_BUF:  rc = cpp_hardware_update_pending_buffer(cpphw, cmd.u.status); break;default:                             CPP_ERR("bad command type=%d", cmd.type); }...
}

这里的是CPP_HW_CMD_SUBSCRIBE_EVENT即cpp_hardware_subcribe_v4l2_event
订阅subdevfd上的事件,通知硬件打开preview.
v4l2_event_subscription定义在:kernel/msm-4.14/include/uapi/linux/videodev2.h

struct v4l2_event_subscription {
—__u32>–>—>—>—type;
—__u32>–>—>—>—id;
—__u32>–>—>—>—flags;
—__u32>–>—>—>—reserved[5];
};

V4L2_EVENT_CPP_FRAME_DONE的定义:

#define V4L2_EVENT_CPP_FRAME_DONE (V4L2_EVENT_PRIVATE_START + 0)

V4L2_EVENT_PRIVATE_START的定义:

#define V4L2_EVENT_PRIVATE_START>—>—0x08000000

static int32_t cpp_hardware_subcribe_v4l2_event(cpp_hardware_t *cpphw)
{struct v4l2_event_subscription sub;struct msm_camera_v4l2_ioctl_t v4l2_ioctl;sub.id = cpphw->inst_id;sub.type = V4L2_EVENT_CPP_FRAME_DONE;rc = ioctl(cpphw->subdev_fd, VIDIOC_SUBSCRIBE_EVENT, &sub);cpphw->event_subs_info.valid = TRUE;cpphw->event_subs_info.id = sub.id;cpphw->event_subs_info.type = sub.type;return 0;
}

ioctl发送到内核: kernel/msm-4.14/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c

static long msm_cpp_subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{//得到video devicestruct video_device *vdev = video_devdata(file);//得到v4l2_subdevstruct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);struct v4l2_fh *vfh = file->private_data;switch (cmd) {...case VIDIOC_SUBSCRIBE_EVENT:return v4l2_subdev_call(sd, core, subscribe_event, vfh, arg);...}
}

v4l2_subdev_call(sd, core, subscribe_event, vfh, arg)是一个宏:

#define v4l2_subdev_call(sd, o, f, args...)>            \({                                \int __result;                        \if (!(sd))                        \__result = -ENODEV;>            \else if (!((sd)->ops->o && (sd)->ops->o->f))        \__result = -ENOIOCTLCMD;            \else                            \__result = (sd)->ops->o->f((sd), ##args);    \__result;                        \})

(sd)->ops->o->f((sd), ##args)对应的是函数指针:

int (*subscribe_event)(struct v4l2_subdev *sd, struct v4l2_fh *fh,struct v4l2_event_subscription *sub);

指向的函数是msm_cpp_subscribe_event。

static struct v4l2_subdev_core_ops msm_cpp_subdev_core_ops = {
—.ioctl = msm_cpp_subdev_ioctl,
—.subscribe_event = msm_cpp_subscribe_event,
—.unsubscribe_event = msm_cpp_unsubscribe_event,
};

static int msm_cpp_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, struct v4l2_event_subscription *sub)
{return v4l2_event_subscribe(fh, sub, MAX_CPP_V4l2_EVENTS, NULL);
}

V4L2 events 提供一种将event传递到用户空间的通用方式。
v4l2_event_subscribe (fh, sub , elems, ops)
用来订阅事件,ops参数可以让驱动指定一个回调函数:add,del,replace,merge。一共有4个可选的回调函数,如果不需要也可以传入NULL参数。 kernel/msm-4.14/drivers/media/v4l2-core/v4l2-event.c

int v4l2_event_subscribe(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub, unsigned elems,const struct v4l2_subscribed_event_ops *ops)
{struct v4l2_subscribed_event *sev, *found_ev;sev = kvzalloc(sizeof(*sev) + sizeof(struct v4l2_kevent) * elems,GFP_KERNEL);for (i = 0; i < elems; i++) sev->events[i].sev = sev;sev->type = sub->type;	sev->id = sub->id;sev->flags = sub->flags;	sev->fh = fh;sev->ops = ops;found_ev = v4l2_event_subscribed(fh, sub->type, sub->id);if (!found_ev) list_add(&sev->list, &fh->subscribed);if (found_ev) {kvfree(sev);} else if (sev->ops && sev->ops->add) {ret = sev->ops->add(sev, elems);}
}

3.2 sensor视频流buffer队列

3.2.1 cpp_thread_func

上文在函数cpp_thread_func中说了sundp-3.1sundp-3.2,接下来说一下剩下的:

....
/* poll on the pipe readfd and subdev fd */
struct pollfd pollfds[2];
uint32_t num_fds = 2;
int ready = 0;
uint32_t i = 0;
pollfds[PIPE_FD_IDX].fd = ctrl->pfd[READ_FD];
pollfds[PIPE_FD_IDX].events = POLLIN|POLLPRI;  
//POLLIN   有数据可读。
//POLLPRI  有紧迫数据可读。
pollfds[SUBDEV_FD_IDX].fd = ctrl->cpphw->subdev_fd;
pollfds[SUBDEV_FD_IDX].events = POLLIN|POLLPRI;
CPP_HIGH("cpp_thread entering the polling loop...");
while(1) {ready = poll(pollfds, (nfds_t)num_fds, -1);
....
sundp-3.3 poll(pollfds, (nfds_t)num_fds, -1);

sundp-3.1sundp-3.2启动v4l2设备成功后,cpp_thread线程同时侦听一个pipe fd,和设备节点/dev/v4l-subdevXX

( 注:打开v4l-subdevXX设备
fd = open(dev_name, O_RDWR | O_NONBLOCK);
cpphw->subdev_fd = fd;)。

struct pollfd pollfds[2];

struct pollfd{int fd; /*文件描述符,如建立socket后获取的fd, 此处表示想查询的文件描述符*/short events;	/*等待的事件,就是要监测的感兴趣的事情*/short revents;	/*实际发生了的事情*/
};

这里的ready = poll(pollfds, (nfds_t)num_fds, -1);是将当前的文件指针挂到等待队列中

结构介绍:
int poll(struct pollfd *fds, unsigned int nfds, int timeout)
参数介绍:
pollfd *fds : 指向pollfd结构体数组,用于存放需要检测器状态的Socket 描述符或其它文件描述符。
unsigned int nfds: 指定pollfd 结构体数组的个数,即监控几个pollfd.
timeout:指poll() 函数调用阻塞的时间,单位是ms.如果timeout=0则不阻塞,如timeout=INFTIM 表 示一直阻塞直到感兴趣的事情发生。
返回值:
0 表示数组fds 中准备好读,写或出错状态的那些socket描述符的总数量
==0 表示数组fds 中都没有准备好读写或出错,当poll 阻塞超时timeout 就会返回。
-1 表示poll() 函数调用失败,同时回自动设置全局变量errno.
函数特点:
每当函数调用后,不会清空这个fds指向的数组,适用于大量socket 描述符的情况。而select()函数调用后会清空它所检测的socket描述符集合,因此select()只是用检测一个socket 描述符的情况。
如果待监测的socket 描述符为负值,则这个描述符的检测就会被忽略,poll()函数返回时直接把revents 设置为0
该poll()函数不会受到socket 描述符上的O_NDELAY 标记和O_NONBLOCK 标记的影响。

sundp-3.4 cpp_thread_process_pipe_message

poll函数运行成功后,接下来会循环浏览num_fds(即pipe fd和subdev_fd)
以查看是否发生了任何事件,当pipe fd上有消息时,先读到pipe_msg。

          case PIPE_FD_IDX: {int num_read=0;cpp_thread_msg_t pipe_msg;num_read = read(pollfds[i].fd, &(pipe_msg),sizeof(cpp_thread_msg_t));rc = cpp_thread_process_pipe_message(ctrl, pipe_msg);    break;}

接下来进入cpp_thread_process_pipe_message处理。 vendor/qcom/proprietary/mm-camera/mm-camera2/media-controller/modules/pproc-new/cpp/cpp_thread.c

static int32_t cpp_thread_process_pipe_message(cpp_module_ctrl_t *ctrl,cpp_thread_msg_t msg)
{cpp_hardware_cmd_t cmd;switch(msg.type) {//这里应该是出错才会走。信息中止case CPP_THREAD_MSG_ABORT: ......//有事件case CPP_THREAD_MSG_NEW_EVENT_IN_Q: {cpp_module_event_t* cpp_event;/*当队列进程中有一些有效事件时,处理它 */while(1) {cpp_event = cpp_thread_get_event_from_queue(ctrl);if(!cpp_event) { break; }rc = cpp_thread_process_queue_event(ctrl, cpp_event);}break;}
}

cpp_thread_process_queue_event
cpp_event->type的类型:

typedef enum {
CPP_MODULE_EVENT_PROCESS_BUF,
CPP_MODULE_EVENT_DIVERT_BUF,
CPP_MODULE_EVENT_CLOCK,
CPP_MODULE_EVENT_PARTIAL_FRAME,
CPP_MODULE_EVENT_ISP_BUFFER_DROP
} cpp_module_event_type_t;

static int32_t cpp_thread_process_queue_event(cpp_module_ctrl_t *ctrl, cpp_module_event_t* cpp_event)
{//选择事件类型:预览的时候是CPP_MODULE_EVENT_PROCESS_BUFswitch(cpp_event->type) {case CPP_MODULE_EVENT_DIVERT_BUF:rc = cpp_thread_handle_divert_buf_event(ctrl, cpp_event);break;case CPP_MODULE_EVENT_PROCESS_BUF:rc = cpp_thread_handle_process_buf_event(ctrl, cpp_event);break;case CPP_MODULE_EVENT_CLOCK:rc = cpp_thread_handle_clock_event(ctrl, cpp_event);break;case CPP_MODULE_EVENT_PARTIAL_FRAME:rc = cpp_thread_handle_partial_frame_event(ctrl, cpp_event);break;case CPP_MODULE_EVENT_ISP_BUFFER_DROP:cpp_thread_handle_isp_drop_buffer_event(ctrl, cpp_event);break;
}

cpp_thread_handle_process_buf_event

static int32_t cpp_thread_handle_process_buf_event(cpp_module_ctrl_t* ctrl, cpp_module_event_t* cpp_event)
{cpp_module_stream_params_t  *stream_params = NULL;cpp_module_session_params_t *session_params = NULL;......//填充cookie/*cookie用于将数据附加到内核帧,处理完成后将立即取回???*/cpp_module_hw_cookie_t *cookie = NULL;//用cpp_envent填充hw_params的参数cpp_hardware_params_t* hw_params = NULL;hw_params = &(cpp_event->u.process_buf_data.hw_params);hw_params->cookie = cookie;hw_params->frame_id = cpp_event->u.process_buf_data.isp_buf_divert.buffer.sequence; hw_params->timestamp = cpp_event->u.process_buf_data.isp_buf_divert.buffer.timestamp;hw_params->buffer_info.fd = in_frame_fd;....../* get stream parameters based on the event identity */cpp_module_get_params_for_identity(ctrl, hw_params->identity,&session_params, &stream_params);/*Before validation, swap dimensions if 90 or 270 degrees rotation*///画面的旋转角度交换?cpp_hardware_rotation_swap(hw_params,video_type_flag);/* before giving the frame to hw, make sure the parameters are good *///应该是验证当前帧的参数是否正确,不正确就丢弃if(FALSE == cpp_hardware_validate_params(hw_params)){......}//计算裁剪?rc = cpp_params_calculate_crop(hw_params);......cmd.type = CPP_HW_CMD_PROCESS_FRAME;cmd.ctrl = ctrl;cmd.u.hw_params = hw_params;//这里可以dump,可以看一下 第五章 dump
#ifdef _ANDROID_if (session_params->cpp_debug_enable)cpp_thread_dump_frame(hw_params,hw_params->buffer_info.fd,&cmd,1);
#endifrc = cpp_hardware_process_command(ctrl->cpphw, cmd);  ....../* Update and post the current session's diag parameters *///更新并发布当前session的诊断参数cpp_module_util_update_session_diag_params(ctrl->p_module, hw_params);
}

cpp_hardware_process_command
执行到CPP_HW_CMD_PROCESS_FRAME,就是cpp_hardware_process_frame(): vendor/qcom/proprietary/mm-camera/mm-camera2/media-controller/modules/pproc-new/cpp/cpp_hardware.c

static int32_t cpp_hardware_process_frame(cpp_hardware_t *cpphw, cpp_hardware_cmd_t *cmd)
{struct cpp_frame_info_t cpp_frame_infostruct msm_cpp_frame_info_t *msm_cpp_frame_info;......rc = cpp_params_create_frame_info(cpphw, hw_params, &cpp_frame_info);//将sysetm接口参数转换为msm frame cfg参数cpp_frame_info.frame_id = hw_params->frame_id;cpp_frame_info.timestamp = hw_params->timestamp;cpp_frame_info.identity = hw_params->identity;.......msm_cpp_frame_info = cpp_hardware_create_hw_frame(cpphw, &cpp_frame_info);/* send kernel ioctl for processing */struct msm_camera_v4l2_ioctl_t v4l2_ioctl;v4l2_ioctl.ioctl_ptr = (void *)msm_cpp_frame_info;v4l2_ioctl.len = sizeof(struct msm_cpp_frame_info_t);rc = ioctl(cpphw->subdev_fd, VIDIOC_MSM_CPP_CFG, &v4l2_ioctl);.......
}

VIDIOC_MSM_CPP_CFG通过ioctl,到了内核: kernel/msm-4.14/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c //内核的这部分我没看

static long msm_cpp_subdev_fops_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{bool is_copytouser_req = truestruct msm_camera_v4l2_ioctl_t kp_ioctlvoid __user *up = (void __user *)arg;copy_from_user(&up32_ioctl, (void __user *)up, sizeof(up32_ioctl));kp_ioctl.id = up32_ioctl.id;kp_ioctl.len = up32_ioctl.len;kp_ioctl.trans_code = up32_ioctl.trans_code;kp_ioctl.ioctl_ptr = compat_ptr(up32_ioctl.ioctl_ptr);switch (cmd) {case VIDIOC_MSM_CPP_CFG32:{   copy_from_user(&k32_frame_info,	(void __user *)kp_ioctl.ioctl_ptr, sizeof(k32_frame_info));cpp_frame = get_64bit_cpp_frame_from_compat(&kp_ioctl);/* Configure the cpp frame */if (cpp_frame) {rc = msm_cpp_cfg_frame(cpp_dev, cpp_frame);/* Cpp_frame can be free'd by cfg_frame in error */if (rc >= 0) {k32_frame_info.output_buffer_info[0] = cpp_frame->output_buffer_info[0];k32_frame_info.output_buffer_info[1] = cpp_frame->output_buffer_info[1];}}kp_ioctl.trans_code = rc;copy_to_user((void __user *)kp_ioctl.ioctl_ptr, &k32_frame_info,	sizeof(k32_frame_info));cmd = VIDIOC_MSM_CPP_CFG;break;}。。。。。。
}

msm_cpp_cfg_frame

static int msm_cpp_cfg_frame(struct cpp_device *cpp_dev, struct msm_cpp_frame_info_t *new_frame)
{in_phyaddr = msm_cpp_fetch_buffer_info(cpp_dev,&new_frame->input_buffer_info,((new_frame->input_buffer_info.identity >> 16) & 0xFFFF),(new_frame->input_buffer_info.identity & 0xFFFF), &in_fd);op_index = new_frame->output_buffer_info[0].index;dup_index = new_frame->duplicate_buffer_info.index;if (new_frame->we_disable == 0) {int32_t iden = new_frame->identity;。。。。。。out_phyaddr0 = msm_cpp_fetch_buffer_info(cpp_dev, &new_frame->output_buffer_info[0],((iden >> 16) & 0xFFFF), (iden & 0xFFFF), &new_frame->output_buffer_info[0].fd);}out_phyaddr1 = out_phyaddr0;if (new_frame->duplicate_output) { 。。。。。。} //这个分支不执行。。。。。。msm_cpp_update_frame_msg_phy_address(cpp_dev, new_frame,in_phyaddr, out_phyaddr0, out_phyaddr1, tnr_scratch_buffer0, tnr_scratch_buffer1);rc = msm_cpp_set_group_buffer(cpp_dev, new_frame, out_phyaddr0, num_output_bufs);frame_qcmd->command = new_frame;rc = msm_cpp_send_frame_to_hardware(cpp_dev, frame_qcmd);  // 将new_frame参数发送到硬件
}

msm_cpp_send_frame_to_hardware()最终会执行对硬件地址的写操作,将frame的参数写入硬件。(TODO)

sundp-3.5 cpp_thread_process_hardware_event

sundp-3.1sundp-3.2启动v4l2设备成功后,cpp_thread线程侦听设备节点/dev/v4l-subdevXX的节点(sundp-3.3)。当有视频buffer准备好时,会向该节点写入数据,这样sundp-3.3返回。
继续前面代码的sundp-3.4

接下来就是 sundp-3.5
poll函数运行成功后,接下来会循环浏览num_fds(即pipe fd和subdev_fd)以查看是否发生了任何事件,当subdev_fd上有消息时,进入 vendor/qcom/proprietary/mm-camera/mm-camera2/media-controller/modules/pproc-new/cpp/cpp_thread.c

static int32_t cpp_thread_process_hardware_event(cpp_module_ctrl_t *ctrl)
{......./* get the event data from hardware *///从硬件获取event数据cmd.type = CPP_HW_CMD_NOTIFY_EVENT;cmd.u.event_data = &event_data;rc = cpp_hardware_process_command(ctrl->cpphw, cmd);  //sundp-3.6/* get stream parameters based on the event identity *///基于事件标识获取流参数 cpp_module_get_params_for_identity(ctrl, event_data.identity, &session_params, &stream_params);/* update the pending ack for this buffer */cookie = (cpp_module_hw_cookie_t *)event_data.cookie;buffer_access = event_data.input_buffer_access;rc = cpp_module_do_ack(ctrl, cookie->key, buffer_access);  // 发送buf event  sundp-3.9.......
}
sundp-3.6 cpp_hardware_process_command

首先从/dev/v4l-subdevXX节点读取event内容:

int32_t cpp_hardware_process_command(cpp_hardware_t *cpphw, cpp_hardware_cmd_t cmd)
{.......switch (cmd.type) {......case CPP_HW_CMD_NOTIFY_EVENT:rc = cpp_hardware_notify_event_get_data(cpphw, cmd.u.event_data);break;......}
}

cpp_hardware_notify_event_get_data

static int32_t cpp_hardware_notify_event_get_data(cpp_hardware_t *cpphw,cpp_hardware_event_data_t *event_data)
{struct v4l2_event event;//--------------------sundp-3.7----------rc = ioctl(cpphw->subdev_fd, VIDIOC_DQEVENT, &event);  v4l2_ioctl.ioctl_ptr = (void *)&frame;v4l2_ioctl.len = sizeof(struct msm_cpp_frame_info_t);//--------------------sundp-3.8----------rc = ioctl(cpphw->subdev_fd, VIDIOC_MSM_CPP_GET_EVENTPAYLOAD, &v4l2_ioctl); event_data->frame_id = frame.frame_id;event_data->buf_idx = frame.input_buffer_info.index;event_data->out_fd = frame.output_buffer_info[0].fd;event_data->identity = frame.identity;......// 设置event_data的内容
}

首先是sundp-3.7,通过对节点/dev/v4l-subdevXX的ioctl,到了内核: kernel/msm-4.14/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c

sundp-3.7 msm_cpp_subdev_do_ioctl
static long msm_cpp_subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{vdev = video_devdata(file);sd = vdev_to_v4l2_subdev(vdev);vfh = file->private_data;switch (cmd) {case VIDIOC_DQEVENT:return v4l2_event_dequeue(vfh, arg, file->f_flags & O_NONBLOCK);......
}

v4l2_event_dequeue就是从v4l设备的avaliable链表中取出第一个event,通过copy_to_user,返回给camera hal。

sundp-3.8 msm_cpp_subdev_fops_compat_ioctl

然后是sundp-3.8,通过对节点/dev/v4l-subdevXX的ioctl,到了内核: kernel/msm-4.14/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c

static long msm_cpp_subdev_fops_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{bool is_copytouser_req = truestruct msm_camera_v4l2_ioctl_t kp_ioctlvoid __user *up = (void __user *)arg;copy_from_user(&up32_ioctl, (void __user *)up, sizeof(up32_ioctl));kp_ioctl.id = up32_ioctl.id;kp_ioctl.len = up32_ioctl.len;kp_ioctl.trans_code = up32_ioctl.trans_code;kp_ioctl.ioctl_ptr = compat_ptr(up32_ioctl.ioctl_ptr);switch (cmd) {.......case VIDIOC_MSM_CPP_GET_EVENTPAYLOAD32:{struct msm_device_queue *queue = &cpp_dev->eventData_q;event_qcmd = msm_dequeue(queue, list_eventdata, POP_FRONT);get_compat_frame_from_64bit(process_frame, &k32_process_frame);copy_to_user((void __user *)kp_ioctl.ioctl_ptr, &k32_process_frame, sizeof(struct msm_cpp_frame_info32_t)));cmd = VIDIOC_MSM_CPP_GET_EVENTPAYLOAD;break;}......}if (is_copytouser_req) {   // == true   //转换为32位(compat),再copy to userup32_ioctl.id = kp_ioctl.id;up32_ioctl.len = kp_ioctl.len;up32_ioctl.trans_code = kp_ioctl.trans_code;up32_ioctl.ioctl_ptr = ptr_to_compat(kp_ioctl.ioctl_ptr);copy_to_user((void __user *)up, &up32_ioctl, sizeof(up32_ioctl));}
}

这里,将一个视频帧(frame)参数通过ioctl,返回给camera hal。
hal拿到帧参数后,将帧参数也填充到event_data中。最后,sundp-3.6返回,参数返回到event_data中。
接下来到了sundp-3.9

sundp-3.9 cpp_module_do_ack

vendor/qcom/proprietary/mm-camera/mm-camera2/media-controller/modules/pproc-new/cpp/cpp_module.c

int32_t cpp_module_do_ack(cpp_module_ctrl_t *ctrl,  cpp_module_ack_key_t key, uint32_t buffer_access)
{......cpp_ack = cpp_module_find_ack_from_list(ctrl, key);cpp_ack->ref_count--;if(cpp_ack->ref_count == 0) {......cpp_module_send_buf_divert_ack(ctrl, cpp_ack->isp_buf_divert_ack);}
}static int32_t cpp_module_send_buf_divert_ack(cpp_module_ctrl_t *ctrl, isp_buf_divert_ack_t isp_ack)
{//创建eventmct_event_t event;  // sundp-3.10event.type = MCT_EVENT_MODULE_EVENT;event.direction = MCT_EVENT_UPSTREAM;event.identity = isp_ack.identity;//sundp-3.11event.u.module_event.type = MCT_EVENT_MODULE_BUF_DIVERT_ACK;event.u.module_event.module_event_data = &isp_ack;rc = cpp_module_send_event_upstream(ctrl->p_module, &event);return 0;
}
sundp-3.10 MCT_EVENT_MODULE_EVENT

接下来到了这儿:
vendor/qcom/proprietary/mm-camera/mm-camera2/media-controller/modules/iface2/port_iface.c

static boolean port_iface_event_func(mct_port_t *mct_port, mct_event_t *event)
{switch (event->type) {case MCT_EVENT_CONTROL_CMD:/* MCT ctrl event */rc = port_iface_proc_mct_ctrl_cmd(mct_port, event);break;case MCT_EVENT_MODULE_EVENT: //对应了前面的event.type/* Event among modules */rc = port_iface_proc_module_event(mct_port, event);break;return rc;
}

vendor/qcom/proprietary/mm-camera/mm-camera2/media-controller/modules/iface2/iface_util.c

int iface_util_divert_ack(iface_t *iface, iface_session_t *session,uint32_t user_stream_id, uint32_t buf_idx, uint32_t is_dirty, boolean bayerdata, uint32_t buffer_access)
{if (hw_stream->isp_split_output_info.is_split == TRUE && hw_stream->stream_info.cam_stream_type != CAM_STREAM_TYPE_OFFLINE_PROC) {/*if hw stream split, only enqueue once since its shared buf*/rc = iface_axi_divert_ack( iface->isp_axi_data.axi_data[VFE0].axi_hw_ops->ctrl,&axi_divert_ack, sizeof(axi_divert_ack));......
}

vendor/qcom/proprietary/mm-camera/mm-camera2/media-controller/modules/iface2/axi/iface_axi.c:

static boolean port_iface_proc_module_event(mct_port_t *mct_iface_port, mct_event_t *event)
{switch (mod_event->type) {//对应了前面的event.u.module_event.typecase MCT_EVENT_MODULE_BUF_DIVERT_ACK: {  isp_buf_divert_ack_t *buf_divert_ack = (isp_buf_divert_ack_t*) mod_event->module_event_data;ret = iface_util_divert_ack(iface, session, UNPACK_STREAM_ID(event->identity),buf_divert_ack->buf_idx, buf_divert_ack->is_buf_dirty,buf_divert_ack->bayerdata, buf_divert_ack->buffer_access);}break;
}

这里,iface_quque_buf,将视频buf加入队列,送去显示。

int iface_axi_divert_ack( iface_axi_hw_t *axi_hw, iface_axi_buf_divert_ack_t *ack, uint32_t data_size __unused)
{stream = iface_axi_util_find_stream(axi_hw, ack->session_id, ack->stream_id);buf_handle = iface_find_matched_bufq_handle(axi_hw->buf_mgr,stream->hw_stream_info.session_id, ack->stream_id);rc = iface_queue_buf(axi_hw->buf_mgr,buf_handle, ack->buf_idx, ack->is_buf_dirty, axi_hw->fd,ack->buffer_access);
}

4 dump image

这里说一下怎么dump图像:

static int32_t cpp_thread_handle_process_buf_event(cpp_module_ctrl_t* ctrl,cpp_module_event_t* cpp_event)
{......
#ifdef _ANDROID_if (session_params->cpp_debug_enable)//1  表示dump输入,有另外的函数dump输出cpp_thread_dump_frame(hw_params,hw_params->buffer_info.fd,&cmd,1);
#endif.......
}

可见有两个条件:

1.#ifdef ANDROID
2.session_params->cpp_debug_enable为ture

第一个条件好说,第二个赋值为ture的地方是:

property_get("persist.vendor.camera.pproc.debug.en", value, "0");
enabled = atoi(value);
if (enabled)ctrl->session_params[i]->cpp_debug_enable = TRUE;

所以需要setprop persist.vendor.camera.pproc.debug.en 1

cpp_thread_dump_frame函数实现:

参数:
cpp_hardware_params_t hw_params:
int fd:
void *user:
bool ip_op:1 dump Input ; 0 dump Output

int cpp_thread_dump_frame(cpp_hardware_params_t *hw_params, int fd,void *user,bool ip_op)
{......//1.检查是否开启dump//这里也需要设置一下property_get("persist.vendor.camera.pproc.framedump", value, "0");enabled = atoi(value);if (!enabled) {  CPPFrameCnt = 0;  return 0; }//2.设置需要dump的frame数,默认20property_get("persist.vendor.camera.pproc.dump_cnt", value, "20");count = atoi(value);//3.dump输入or输出if(ip_op) { /* Input */cookie = (cpp_module_hw_cookie_t *)hw_params->cookie;memcpy(&dim_info,&hw_params->input_info,sizeof(cpp_params_dim_info_t));strlcpy(io,"Input",sizeof(io));} else { /* Output */cookie = cmd->u.event_data->cookie;memcpy(&dim_info,&hw_params->output_info,sizeof(cpp_params_dim_info_t));strlcpy(io,"Output",sizeof(io));hw_params->frame_id = cmd->u.event_data->frame_id;}//根据stream_type组包(即dump 图像的的文件名)switch(hw_params->stream_type) {//以preview举例:case CAM_STREAM_TYPE_PREVIEW: {snprintf(name, sizeof(name), QCAMERA_DUMP_FRM_LOCATION"%s_CPP_%s_Preview_%dx%d_%d.yuv",timeBuf,io,width[0],height[0],hw_params->frame_id);break;}.......}//openfile_fd = open(name, O_RDWR | O_CREAT, 0777);//writewritten_len += write(file_fd, data, (size_t)(dim_info.plane_info[i].plane_len));......
}

struct

struct _cpp_module_ctrl_t

struct _cpp_module_ctrl_t {mct_module_t                *p_module;mct_module_t                *parent_module;mct_queue_t                 *realtime_queue;mct_queue_t                 *partial_frame_queue;mct_queue_t                 *offline_queue;cpp_module_ack_list_t       ack_list;pthread_t                   cpp_thread;pthread_cond_t              th_start_cond;boolean                     cpp_thread_started;pthread_mutex_t             cpp_mutex;int                         pfd[2];int32_t                     session_count;cpp_hardware_t              *cpphw;cpp_module_clk_rate_list_t  clk_rate_list;unsigned long               clk_rate;boolean                     runtime_clk_update;pp_native_buf_mgr_t         pp_buf_mgr;pp_native_buf_mgr_t         pp_tnr_buf_mgr;cpp_module_session_params_t *session_params[CPP_MODULE_MAX_SESSIONS];cpp_submodule_func_tbl_t    tnr_module_func_tbl;cpp_submodule_func_tbl_t    pbf_module_func_tbl;mct_port_t                  *port_map[CPP_MODULE_MAX_SESSIONS][CPP_MODULE_MAX_STREAMS][2];/* last updated clock and bandwidth */int64_t                     clk;int64_t                     bw;/* current state for a clock update */cpp_clock_state             clk_state;/* threshold count to trigger a clock bump down */int32_t                     clock_threshold;int32_t                     clock_dcvs;int32_t                     turbo_caps;volatile bool               is_hw_error;uint32_t                    rtb_status;uint32_t                    sat_status;uint32_t                    soc_id;
};

struct _cpp_hardware_t

struct _cpp_hardware_t {uint32_t                            subdev_ids[MAX_CPP_DEVICES];int                                 num_subdev;int                                 subdev_fd;boolean                             subdev_opened;uint32_t                            inst_id;cpp_hardware_caps_t                 caps;cpp_hardware_info_t                 hwinfo;cpp_hardware_status_t               status;cpp_firmware_version_t              fw_version;cpp_hardware_event_subscribe_info_t event_subs_info;cpp_hardware_stream_status_t        stream_status[CPP_HARDWARE_MAX_STREAMS];pthread_cond_t                      subdev_cond;pthread_mutex_t                     mutex;int                                 num_iommu_cnt;int                                 max_pending_buffer;uint32_t                            dump_preview_cnt;uint32_t                            dump_video_cnt;uint32_t                            dump_snapshot_cnt;int32_t                             max_supported_padding;void                                *private_data;uint32_t                            preview_frame_counter;uint32_t                            video_frame_counter;uint32_t                            offline_frame_counter;uint32_t                            snapshot_frame_counter;uint32_t                            postview_frame_counter;uint32_t                            analysis_frame_counter;
} ;

_cpp_thread_msg_t

typedef enum {CPP_THREAD_MSG_NEW_EVENT_IN_Q,CPP_THREAD_MSG_ABORT             
} cpp_thread_msg_type_t;typedef struct _cpp_thread_msg_t {cpp_thread_msg_type_t type;void *data;
} cpp_thread_msg_t;

_cpp_hardware_cmd_t

typedef struct _cpp_hardware_cmd_t {cpp_hardware_cmd_type_t type;void                   *ctrl;void                   *return_payload;/* CPP_HW_CMD_PROCESS_FRAME - Partial frame, after the first payload is sent,the remainder is saved here and then split into more partial_framesin the thread */union {cpp_hardware_streamoff_event_t streamoff_data;cpp_hardware_event_data_t       *event_data;cpp_hardware_params_t           *hw_params;cpp_hardware_buff_update_t      *buff_update;struct msm_cpp_frame_info_t     *partial_frame;cpp_hardware_clock_settings_t   clock_settings;uint32_t                        buf_done_identity;uint32_t                        status;} u;
} cpp_hardware_cmd_t;

enum cpp_hardware_cmd_type_t

typedef enum {CPP_HW_CMD_GET_CAPABILITIES,CPP_HW_CMD_SUBSCRIBE_EVENT,CPP_HW_CMD_UNSUBSCRIBE_EVENT,CPP_HW_CMD_NOTIFY_EVENT,CPP_HW_CMD_STREAMON,CPP_HW_CMD_STREAMOFF,CPP_HW_CMD_LOAD_FIRMWARE,CPP_HW_CMD_PROCESS_FRAME,CPP_HW_CMD_QUEUE_BUF,CPP_HW_CMD_GET_CUR_DIAG,CPP_HW_CMD_SET_CLK,CPP_HW_CMD_POP_STREAM_BUFFER,CPP_HW_CMD_BUF_UPDATE,CPP_HW_CMD_PROCESS_PARTIAL_FRAME,CPP_HW_CMD_NOTIFY_BUF_DONE,CPP_HW_CMD_UPDATE_PENDING_BUF,
} cpp_hardware_cmd_type_t;

struct _cpp_module_session_params_t

/* session specific parameters */
typedef struct _cpp_module_session_params_t {cpp_module_stream_params_t   *stream_params[CPP_MODULE_MAX_STREAMS];int32_t                       stream_count;cpp_hardware_params_t         hw_params;uint32_t                      session_id;cam_hfr_mode_t                hfr_mode;cpp_params_aec_trigger_info_t aec_trigger;/* DIS enable flag to be used for frame hold */int32_t                       dis_enable;/* Latest frame id received from DIS crop event */cpp_module_dis_hold_t         dis_hold;/* Hold frame until DIS crop is received for this frame */cpp_module_frame_hold_t       frame_hold;ez_pp_params_t                diag_params;cam_hal_version_t             hal_version;cpp_per_frame_params_t        per_frame_params;boolean                       is_stream_on;uint32_t                      stream_on_count;cam_fps_range_t               fps_range;cam_stream_ID_t               valid_stream_ids[FRAME_CTRL_SIZE];cam_stream_ID_t               verify_proc_stream_ids[FRAME_CTRL_SIZE];pthread_mutex_t               dis_mutex;modulesChromatix_t            module_chromatix;chromatix_cpp_stripped_type   *def_chromatix_stripped;boolean                       runtime_clk_update;cam_dimension_t               camif_dim;uint32_t                      turbo_frame_count;int32_t                       clk_ref_threshold_idx;/* for Dual Cam: Primary or Secondary */bool                          is_slave;/* for Dual Cam: Main or Aux */cam_sync_type_t               cam_type;int32_t                       link_session_id;int32_t                       native_buf_ref;cam_dual_camera_perf_control_t dualcam_perf;cpp_module_session_set_parm   sticky_set_parm;cam_dual_camera_role_t        cam_role;bool                          force_slave_process;boolean                       cpp_debug_enable;
} cpp_module_session_params_t;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/152739.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JIRA 如何在项目之间移动 Issue

需要使用 JIRA 的查找功能。 把需要移动的 Issue 先全部找到&#xff0c;然后选择 Tools 下面的所有 Issues 批量操作页面 在随后的页面中&#xff0c;将会出现批量操作的页面。 在这里&#xff0c;可以对需要进行批量操作的问题&#xff0c;进行全部选择。 然后单击下一步继…

RunnerGo亮相QECon大会上海站,来看看这款全栈测试平台

QECon&#xff08;Quality Efficiency Conference&#xff09;质量效能大会在上海正式开幕&#xff01;本次大会以"数生智慧&#xff1a;高质量发展新引擎"为主题&#xff0c;深入探讨如何借助数字化和智能化技术推动软件质量的发展&#xff0c;为高质量经济发展提供…

分类预测 | MATLAB实现KOA-CNN-BiLSTM开普勒算法优化卷积双向长短期记忆神经网络数据分类预测

分类预测 | MATLAB实现KOA-CNN-BiLSTM开普勒算法优化卷积双向长短期记忆神经网络数据分类预测 目录 分类预测 | MATLAB实现KOA-CNN-BiLSTM开普勒算法优化卷积双向长短期记忆神经网络数据分类预测分类效果基本描述程序设计参考资料 分类效果 基本描述 1.MATLAB实现KOA-CNN-BiLST…

Windows安装人大金仓数据库问题解决

一、安装包、授权文件下载 官网下载windows对应的安装包 下载授权文件 二、安装 &#xff08;1&#xff09;将下载的授权文件包解压待用 &#xff08;2&#xff09;将下载好的.iso安装程序解压&#xff0c;使用管理员身份运行安装程序&#xff0c;一路下一步&#xff0c;直…

SpringBoot的流浪宠物系统

采用技术:springbootvue 项目可以完美运行

电动机监控系统在企业降碳过程中的作用-安科瑞黄安南

1.前言 据《2017-2022年中国电力工业产业专项调查及十三五市场商机分析报告》显示&#xff0c;从我国目前全社会用电结构来看&#xff0c;工商业用户耗电量约占 80%&#xff0c;其中电机耗电约占工业用电的 75%&#xff0c;全国总耗电的 60%&#xff0c;是用户终端耗电占比较大…

2023 IDC中国数字金融论坛丨中电金信向行业分享“源启+应用重构”新范式

9月8日&#xff0c;IDC主办的“2023 IDC中国数字金融论坛”在北京召开。中电金信受邀参会&#xff0c;并带来了深度数字化转型趋势之下关于应用重构的分享与洞见。 论坛重点关注金融科技创新发展趋势与数字化转型之路&#xff0c;中电金信副总经理、研究院院长况文川带来了“创…

“揭秘淘宝店铺所有商品接口:一键获取海量热销宝贝信息!“

淘宝店铺所有商品接口可以通过shop id或店铺主链接获取到整店商品&#xff0c;数据包括&#xff1a;商品ID&#xff0c;图片地址&#xff0c;店铺标题&#xff0c;优惠价&#xff0c;价格&#xff0c;销量&#xff0c;宝贝链接等整个店铺的商品。 要使用这个接口&#xff0c;需…

4.物联网射频识别,RFID开发【智能门禁项目】

补充&#xff1a;学习路径 一。项目介绍及需求分析 1.酒店智能门禁使用场景介绍 1.客人入住 客人在前台办理入住手续&#xff0c;前台管理员通过门禁管理系统为客户开一张门禁卡 客户持卡到相应客房&#xff0c;用IC 卡刷卡开门 客人过了入住时间后&#xff0c;卡自动失效&a…

【简单的留言墙】HTML+CSS+JavaScript

目标&#xff1a;做一个简单的留言墙 1.首先我们用HTML的一些标签&#xff0c;初步构造区域 样式。 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>留言墙</title><style>/* ...... */ …

Charles:移动端抓包 / windows客户端 iOS手机 / 手机访问PC本地项目做调试

一、背景描述 1.1、本文需求&#xff1a;移动端进行抓包调试 1.2、理解Charles可以做什么 Charles是一款跨平台的网络代理软件&#xff0c;可以用于捕获和分析网络流量&#xff0c;对HTTP、HTTPS、HTTP/2等协议进行调试和监控。使用Charles可以帮助开发人员进行Web开发、调试…

pytorch_神经网络构建2(数学原理)

文章目录 深层神经网络多分类深层网络反向传播算法优化算法动量算法Adam 算法 深层神经网络 分类基础理论: 交叉熵是信息论中用来衡量两个分布相似性的一种量化方式 之前讲述二分类的loss函数时我们使用公式-(y*log(y_)(1-y)*log(1-y_)进行误差计算 y表示真实值,y_表示预测值 …

使用postman 调用 Webservice 接口

1. 先在浏览器地址栏 访问你的webService地址 地址格式: http://127.0.0.1:8092/xxxx/ws(这个自己的决定)/xxxxXccv?wsdl 2. post man POST 访问wwebService接口 地址格式: http://127.0.0.1:8092/xxxx/ws(这个自己的决定)/xxxxXccv <soapenv:Envelope xmlns:soapenv…

Flink session集群运维

1、集群job manager挂了 kubectl describe pod session-deployment-only-84b8d674c7-ckl9w -n flink kubectl get pod -n flink -owide kubectl describe pod session-deployment-only-84b8d674c7-ms758 -n flink 两个job manager都挂了 准备重新部署集群 删除操作(删除fli…

C语言打印菱形

一、运行结果图 二、源代码 # define _CRT_SECURE_NO_WARNINGS # include <stdio.h>int main() {//初始化变量值&#xff1b;int line 0;int i 0;int j 0;//获取变量值&#xff1b;scanf("%d", &line);//循环打印上半部分&#xff1b;for (i 0; i <…

【Redis】Redis中的数据结构和内部编码

Redis中的数据结构和内部编码 type命令实际返回的就是当前键的数据结构类型&#xff0c;它们分别是&#xff1a;string&#xff08;字符串&#xff09;、list&#xff08;列表&#xff09;、hash&#xff08;哈希&#xff09;、set&#xff08;集合&#xff09;、zset&#xf…

WebDAV之π-Disk派盘 + 咕咚云图

咕咚云图是一款强大的图床传图软件,它能够让您高效地对手机中的各种图片进行github传输,多个平台快速编码上传,支持远程删除不需要的图片,传输过程安全稳定,让您可以很好的进行玩机或者其他操作。 可帮你上传手机图片到图床上,并生成 markdown 链接,支持七牛云、阿里云…

UE4 Unlua 初使用小记

function M:Construct()print(Hello World)print(self.Va)local mySubsystem UE4.UHMSGameInstanceSubsystemUE4.UKismetSystemLibrary.PrintString(self,"Get Click Msg From UnLua ")end unlua中tick不能调用的问题&#xff1a; 把该类的Event Tick为灰色显示的删…

SpringCloud学习笔记-Eureka的服务拉取

目录 1.依然需要在该服务里面引入依赖2.在OrderService里面需要有如下配置3.修改URL和OrderService中添加Load Balanced注解1.修改URL2.OrderService中添加Load Balanced注解 假设是OrderService里面拉取Eureka的服务之一User Service 1.依然需要在该服务里面引入依赖 <dep…

seata分布式事务理论概述

分布式事务产生的原因&#xff1a; 数据库分库分表 应用的SOA化。就是业务的服务化(面向服务架构) 分布式事务的解决方案&#xff1a; 1、两阶段提交协议2PC 这里的两阶段提交和redolog binlog的两阶段提交不是一个东西&#xff0c;redo log和bin log的两阶段提交保证的是…