当前位置: 首页 > news >正文

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;

相关文章:

MM-Camera架构-Preview 流程分析

目录 文章目录 1 log开的好&#xff0c;问题都能搞2 lib3 preview3.1 打开视频流3.1.1 cpp\_module\_start\_session3.1.2 cpp\_thread\_create3.1.3 cpp\_thread\_funcsundp-3.1 cpp\_hardware\_open\_subdev(ctrl->cpphw)sundp-3.2 cpp\_hardware\_process\_command(ctrl-…...

科普文章|一文了解平行链及其优势

平行链是一种可以连接到更大规模的区块链网络&#xff08;波卡&#xff09;的独立区块链。不同于传统区块链&#xff08;如比特币和以太坊&#xff09;是孤立的并且无法在本地相互通信&#xff0c;平行链与其他平行链并行运行&#xff0c;并且相互可以无缝通信。平行链还使用波…...

Tomcat 9.0.41在IDEA中乱码问题(IntelliJ IDEA 2022.1.3版本)

1. 乱码的产生是由于编码和解码的编码表不一致引起的。 2. 排查乱码原因 2.1 在idea中启动Tomcat时控制台乱码排查 Tomcat输出日志乱码: 首先查看IDEA控制台&#xff0c;检查发现默认编码是GBK。 再查看Tomcat日志&#xff08;conf文件下logging.properties&#xff09;的默…...

在Kubernetes中实现gRPC流量负载均衡

在尝试将gRPC服务部署到Kubernetes集群中时&#xff0c;一些用户&#xff08;包括我&#xff09;面临的挑战之一是实现适当的负载均衡。在深入了解如何平衡gRPC的方式之前&#xff0c;我们首先需要回答一个问题&#xff0c;即为什么需要平衡流量&#xff0c;如果Kubernetes已经…...

Floorplanning with Graph Attention

Floorplanning with Graph Attention DAC ’22 目录 Floorplanning with Graph Attention摘要1.简介2.相关工作3.问题公式化4. FLORA的方法4.1 解决方案概述4.2 C-谱聚类算法 4.3 基于GAT的模型4.4 合成训练数据集生成 摘要 布图规划一直是一个关键的物理设计任务&#xff0…...

centos7 配置coreboot编译环境 以及编译问题解决

需要的配置 (有的资源在国外可能需要翻墙) 操作系统: centos7.9 参考文章 coreboot源码分析之编译和运行coreboot - 知乎 //coreboot编译总说明 https://www.coreboot.org/Build_HOWTO#Requirements https://poe.com/ChatGPT 注意: 因为github不稳定 所以gitee为主 1. 下载…...

大型语言模型:RoBERTa — 一种鲁棒优化的 BERT 方法

一、介绍 BERT模型的出现导致了NLP的重大进展。BERT的架构源自Transformer&#xff0c;在各种下游任务上实现了最先进的结果&#xff1a;语言建模&#xff0c;下一句预测&#xff0c;问答&#xff0c;NER标记等。 大型语言模型&#xff1a;BERT — 来自变压器的双向编码器表示 …...

解析navicate数据库密码

在线运行地址:代码在线运行 - 在线工具 <?php class NavicatPassword {protected $version 0;protected $aesKey libcckeylibcckey;protected $aesIv libcciv libcciv ;protected $blowString 3DC5CA39;protected $blowKey null;protected $blowIv null;public func…...

mysql字段类型与oracle字段类型对应关系

MySQL与Oracle两种数据库在工作中&#xff0c;都是用的比较多的数据库&#xff0c;由于MySQL与Oracle在数据类型上有部分差异&#xff0c;在我们迁移数据库时&#xff0c;会遇上一定的麻烦&#xff0c;下面介绍MySQL与Oracle数据库数据类型的对应关系。 一、常见数据类型在MyS…...

linux 中 tar \ zip 解压错误后撤回

#zip zipinfo -1 path/xx.zip | xargs rm -rf#tar tar -tf xx.tar | xargs rm -rf...

对象图 UML从入门到放弃之四

1.劝退说明 对象图提供了系统在某个特定时刻的状态快照。这是一种有用的描述系统的方法&#xff0c;当系统的结构是动态构建起来而不是由其静态的类结构决定时&#xff0c;更是如此。不过&#xff0c;应该对画太多的对象图保持警惕。在大部分情况下&#xff0c;它们都可以从相应…...

FPGA实现HDMI输入转SDI视频输出,提供4套工程源码和技术支持

目录 1、前言免责声明 2、我目前已有的SDI编解码方案3、设计思路框架核模块解析设计框图IT6802解码芯片配置及采集ADV7611解码芯片配置及采集silicon9011解码芯片配置及采集纯verilog的HDMI 解码模块RGB888转YUV422SPMTE编码SDI模式图像缓存SPMTE SDIGTXGV8500 4、vivado工程1-…...

针对FTP的SSRF攻击

前言 ssrf中常用的协议有http&#xff0c;gopher等。但http协议在ssrf中的用处也仅限于访问内网页面&#xff0c;在可以crlf的情况下才有可能扩大攻击范围。gopher协议比较特殊&#xff0c;在部分环境下支持此协议&#xff0c;如&#xff1a;curl。但还有一些环境就不支持了&a…...

线性代数中涉及到的matlab命令-第一章:行列式

目录 1&#xff0c;逆序数 2&#xff0c;行列式定义和性质 2.1&#xff0c;常用特性及命令 2.2&#xff0c;求行列式 2.3&#xff0c;行列式的性质 2&#xff0c;行列式按行&#xff08;列&#xff09;展开 3&#xff0c;范德蒙德行列式 在学习线性代数过程中&#…...

QT编程,QT内存管理、信号与槽、

目录 一、QT工具 二、QT内存管理 三、信号与槽 1、信号与槽特点 2、信号 3、槽函数 4、连接 5、发送信号 6、取消连接 一、QT工具 1、Qt Designer&#xff1a;界面设计编辑工具 2、Qt Assistant: Qt技术文档浏览器 3、Qt Linguist: 国际化语言翻译工具 4、…...

springcloud之项目实战环境准备

写在前面 为了更好的学习springcloud&#xff0c;我们来一起开发一个实战项目&#xff0c;加深理解。 1&#xff1a;项目介绍 在开始项目实战之前先来做一个整体的项目介绍&#xff0c;从而能够让对项目的整体架构和模板有一个比较清晰的认知。 大家都知道双11&#xff0c;…...

Linux 部署 MinIO 分布式对象存储 配置为 typora 图床

前言 MinIO 是一款高性能的对象存储系统&#xff0c;它可以用于大规模的 AI/ML、数据湖和数据库工作负载。它的 API 与Amazon S3 云存储服务完全兼容&#xff0c;可以在任何云或本地基础设施上运行。MinIO 是开源软件&#xff0c;也提供商业许可和支持 MinIO 的特点有&#x…...

JVM Optimization Learning(四)

目录 一、调优 1、基础概念 2、什么是调优&#xff1f; 3、调优&#xff0c;从规划开始 4、调优案例 一、调优 1、基础概念 吞吐量&#xff1a;用户代码执行时间 /&#xff08;用户代码执行时间 垃圾回收时间&#xff09; 响应时间&#xff1a;STW越短&#xff0c;响应…...

新华三辅导笔记 2023/10/9-2023/10/13

新华三辅导笔记 一、需要用到的软件二、计算机网络概述1、计算机网络的定义和基本功能&#xff08;1&#xff09;什么是计算机网络&#xff08;2&#xff09;计算机网络的基本功能 2、&#xff08;1&#xff09;局域网、城域网和广域网&#xff08;范围划分&#xff09;&#x…...

边坡安全监测系统的功能优势

随着科技的进步&#xff0c;边坡安全监测系统在各种工程项目中发挥着越来越重要的作用。这款系统通过实时监测垂直、水平位移数据&#xff0c;以折线图的方式显示在监控平台中&#xff0c;为工程人员提供了直观、便捷的监控工具&#xff0c;从而能够及时掌握边坡稳定状况&#…...

盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来

一、破局&#xff1a;PCB行业的时代之问 在数字经济蓬勃发展的浪潮中&#xff0c;PCB&#xff08;印制电路板&#xff09;作为 “电子产品之母”&#xff0c;其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透&#xff0c;PCB行业面临着前所未有的挑战与机遇。产品迭代…...

c++ 面试题(1)-----深度优先搜索(DFS)实现

操作系统&#xff1a;ubuntu22.04 IDE:Visual Studio Code 编程语言&#xff1a;C11 题目描述 地上有一个 m 行 n 列的方格&#xff0c;从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子&#xff0c;但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...

生成 Git SSH 证书

&#x1f511; 1. ​​生成 SSH 密钥对​​ 在终端&#xff08;Windows 使用 Git Bash&#xff0c;Mac/Linux 使用 Terminal&#xff09;执行命令&#xff1a; ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" ​​参数说明​​&#xff1a; -t rsa&#x…...

MySQL 8.0 OCP 英文题库解析(十三)

Oracle 为庆祝 MySQL 30 周年&#xff0c;截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始&#xff0c;将英文题库免费公布出来&#xff0c;并进行解析&#xff0c;帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...

Swagger和OpenApi的前世今生

Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章&#xff0c;二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑&#xff1a; &#x1f504; 一、起源与初创期&#xff1a;Swagger的诞生&#xff08;2010-2014&#xff09; 核心…...

2025季度云服务器排行榜

在全球云服务器市场&#xff0c;各厂商的排名和地位并非一成不变&#xff0c;而是由其独特的优势、战略布局和市场适应性共同决定的。以下是根据2025年市场趋势&#xff0c;对主要云服务器厂商在排行榜中占据重要位置的原因和优势进行深度分析&#xff1a; 一、全球“三巨头”…...

Linux离线(zip方式)安装docker

目录 基础信息操作系统信息docker信息 安装实例安装步骤示例 遇到的问题问题1&#xff1a;修改默认工作路径启动失败问题2 找不到对应组 基础信息 操作系统信息 OS版本&#xff1a;CentOS 7 64位 内核版本&#xff1a;3.10.0 相关命令&#xff1a; uname -rcat /etc/os-rele…...

return this;返回的是谁

一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请&#xff0c;不同级别的经理有不同的审批权限&#xff1a; // 抽象处理者&#xff1a;审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...

CRMEB 中 PHP 短信扩展开发:涵盖一号通、阿里云、腾讯云、创蓝

目前已有一号通短信、阿里云短信、腾讯云短信扩展 扩展入口文件 文件目录 crmeb\services\sms\Sms.php 默认驱动类型为&#xff1a;一号通 namespace crmeb\services\sms;use crmeb\basic\BaseManager; use crmeb\services\AccessTokenServeService; use crmeb\services\sms\…...

【Linux】Linux安装并配置RabbitMQ

目录 1. 安装 Erlang 2. 安装 RabbitMQ 2.1.添加 RabbitMQ 仓库 2.2.安装 RabbitMQ 3.配置 3.1.启动和管理服务 4. 访问管理界面 5.安装问题 6.修改密码 7.修改端口 7.1.找到文件 7.2.修改文件 1. 安装 Erlang 由于 RabbitMQ 是用 Erlang 编写的&#xff0c;需要先安…...