MM-Camera架构-Preview 流程分析

news2025/2/25 17:37:16

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_cond
  pthread_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 = 2
          switch(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 device
    struct video_device *vdev = video_devdata(file);
    //得到v4l2_subdev
    struct 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_BUF
    switch(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);
#endif

    rc = 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_info
    struct 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 = true
    struct msm_camera_v4l2_ioctl_t kp_ioctl
    void __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 = true
    struct msm_camera_v4l2_ioctl_t kp_ioctl
    void __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 user
	    up32_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)
{
    //创建event
    mct_event_t event;  
    // sundp-3.10
    event.type = MCT_EVENT_MODULE_EVENT;
    event.direction = MCT_EVENT_UPSTREAM;
    event.identity = isp_ack.identity;
    //sundp-3.11
    event.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.type
    case 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数,默认20
    property_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;
            }
        .......
    }
    
    //open
    file_fd = open(name, O_RDWR | O_CREAT, 0777);
    //write
    written_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_frames
     in 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.coloradmin.cn/o/1067864.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ssm+vue的劳务外包管理系统(有报告)。Javaee项目,ssm vue前后端分离项目。

演示视频&#xff1a; ssmvue的劳务外包管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;ssm vue前后端分离项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&…

3BHB003154R0101 GVC707AE01 3BHB003149P201 3BHB003149P104 5SXE05-0156

3BHB003154R0101 GVC707AE01 3BHB003149P201 3BHB003149P104 5SXE05-0156 特征 IT/OT SOC是一种安全监控服务&#xff0c;它使用快速、可扩展和统一的下一代安全信息和事件管理(SIEM)。该服务使用各种CTI和ML工具从客户的IT/OT设备收集事件日志&#xff0c;以检测网络攻击、…

leetCode 300.最长递增子序列 (贪心 + 二分 ) + 图解 + 优化 + 拓展

300. 最长递增子序列 - 力扣&#xff08;LeetCode&#xff09; 给你一个整数数组 nums &#xff0c;找到其中最长严格递增子序列的长度。 子序列 是由数组派生而来的序列&#xff0c;删除&#xff08;或不删除&#xff09;数组中的元素而不改变其余元素的顺序。例如&#xff…

Kafka 高可用

正文 一、高可用的由来 1.1 为何需要Replication 在Kafka在0.8以前的版本中&#xff0c;是没有Replication的&#xff0c;一旦某一个Broker宕机&#xff0c;则其上所有的Partition数据都不可被消费&#xff0c;这与Kafka数据持久性及Delivery Guarantee的设计目标相悖。同时Pr…

算法通关村第17关【青铜】| 贪心

贪心算法&#xff08;Greedy Algorithm&#xff09;是一种常见的算法设计策略&#xff0c;通常用于解决一类优化问题。其核心思想是&#xff1a;在每一步选择中都采取当前状态下的最优决策&#xff0c;从而希望最终能够达到全局最优解。贪心算法不像动态规划算法需要考虑各种子…

电暖产品经营小程序商城搭建

电暖产品的需求度很高&#xff0c;包括地暖系统及壁挂炉、水暖散热器等&#xff0c;尤其每年冬天&#xff0c;部分家庭或办公场所就会有相关需求&#xff0c;庞大市场下为电暖各领域商家及品牌带来了商机。 然而随着互联网深入各行业及实体店生意难做&#xff0c;无论品牌还是…

安卓RecycleView包含SeekBar点击列表底部圆形阴影处理

seekbar在列表中点击底部圆形阴影禁止显示方法 大家好&#xff0c;最近写了自定义的seekbar实现显示进度值&#xff0c;然而呢&#xff0c;我的seekbar控件是作为recycleview的item来使用的&#xff0c;我设置了禁止点击和滑动方法如下&#xff1a; seekBar.setOnTouchListene…

ubuntu使用whisper和funASR-语者分离-二值化

文章目录 一、选择系统1.1 更新环境 二、安装使用whisper2.1 创建环境2.1 安装2.1.1安装基础包2.1.2安装依赖 3测试13测试2 语着分离创建代码报错ModuleNotFoundError: No module named pyannote报错No module named pyannote_whisper 三、安装使用funASR1 安装1.1 安装 Conda&…

SpringBoot+MinIO实现对象存储

一、 MinIO MinIO 是一个基于Apache License v2.0开源协议的对象存储服务。它兼容亚马逊S3云存储服务接口&#xff0c;非常适合于存储大容量非结构化的数据&#xff0c;例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等&#xff0c;而一个对象文件可以是任意大小&#…

【好玩】如何在github主页放一条贪吃蛇

前言 &#x1f34a;缘由 github放小蛇&#xff0c;就问你烧不烧 起因看到大佬github上有一条贪吃蛇扭来扭去&#xff0c;觉得好玩&#xff0c;遂给大家分享一下本狗的玩蛇历程 &#x1f95d;成果初展 贪吃蛇 &#x1f3af;主要目标 实现3大重点 1. github设置主页 2. git…

Arcgis日常天坑问题(1)——将Revit模型转为slpk数据卡住不前

这段时间碰到这么一个问题&#xff0c;revit模型在arcgis pro里导出slpk的时候&#xff0c;卡在98%一直不动&#xff0c;大约有两个小时。 首先想到的是revit模型过大&#xff0c;接近300M。然后各种减小模型测试&#xff0c;还是一样的问题&#xff0c;大概花了两天的时间&am…

OpenHarmony父子组件双项同步使用:@Link装饰器

子组件中被Link装饰的变量与其父组件中对应的数据源建立双向数据绑定。 说明&#xff1a; 从API version 9开始&#xff0c;该装饰器支持在ArkTS卡片中使用。 概述 Link装饰的变量与其父组件中的数据源共享相同的值。 装饰器使用规则说明 Link变量装饰器 说明 装饰器参数 无…

实现协议互通:探索钡铼BL124EC的EtherCAT转Ethernet/IP功能

钡铼BL124EC是一种用于工业网络通信的网关设备&#xff0c;专门用于将EtherCAT协议转换成Ethernet/IP协议。它充当一个桥梁&#xff0c;连接了使用不同协议的设备&#xff0c;使它们能够无缝地进行通信和互操作。 具体来说&#xff0c;BL124EC通过支持EtherCAT&#xff08;以太…

5SpringMVC处理Ajax请求携带的JSON格式(“key“:value)的请求参数

SpringMVC处理Ajax 参考文章数据交换的常见格式,如JSON格式和XML格式 请求参数的携带方式 浏览器发送到服务器的请求参数有namevalue&...(键值对)和{key:value,...}(json对象)两种格式 URL请求会将请求参数以键值对的格式拼接到请求地址后面,form表单的GET和POST请求会…

论文阅读——Large Selective Kernel Network for Remote Sensing Object Detection

目录 基本信息标题目前存在的问题改进网络结构另一个写的好的参考 基本信息 期刊CVPR年份2023论文地址https://arxiv.org/pdf/2303.09030.pdf代码地址https://github.com/zcablii/LSKNet 标题 遥感目标检测的大选择核网络 目前存在的问题 相对较少的工作考虑到强大的先验知…

HTML5+CSS3+移动web 前端开发入门笔记(二)HTML标签详解

HTML标签&#xff1a;排版标签 排版标签用于对网页内容进行布局和样式的调整。下面是对常见排版标签的详细介绍&#xff1a; <h1>: 定义一级标题&#xff0c;通常用于标题栏或页面主要内容的标题。<p>: 定义段落&#xff0c;用于将文字分段展示&#xff0c;段落之…

mysql面试题25:数据库自增主键可能会遇到什么问题?应该怎么解决呢?

该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点 面试官:数据库自增主键可能会遇到什么问题? 数据库自增主键可能遇到的问题: 冲突问题:自增主键是通过自动递增生成的唯一标识符,但在某些情况下可能会…

Sentinel入门

文章目录 初始Sentinel雪崩问题服务保护技术对比认识Sentinel微服务整合Sentinel 限流规则快速入门流控模式关联模式链路模式 流控效果warm up排队等待 热点参数限流全局参数限流热点参数限流 隔离和降级FeignClient整合Sentinel线程隔离熔断降级慢调用异常比例、异常数 授权规…

MATLAB算法实战应用案例精讲-【优化算法】霸王龙优化算法(TROA)(附MATLAB代码实现)

前言 霸王龙优化算法(Tyrannosaurus optimization,TROA)由Venkata Satya Durga Manohar Sahu等人于2023年提出,该算法模拟霸王龙的狩猎行为,具有搜索速度快等优势。 霸王龙属于暴龙超科的暴龙属,是该属的唯一一种。1905年,美国古生物学家、美国艺术与科学院院士亨利奥…

iOS——仿写计算器

四则运算&#xff1a;中缀表达式转后缀表达式后缀表达式求值 实现四则运算的算法思路是&#xff1a;首先输入的是中缀表达式的字符串&#xff0c;然后将其转为计算机可以理解的后缀表达式&#xff0c;然后将后缀表达式求值&#xff1a; 中缀转后缀表达式思路参考&#xff1a;《…