
文章目录isp应用层rkisp_3A_serverinit_enginestart_engineisp应用层rkisp_3A_server解析media文件找到isp的那一个解析出来对应的文件DBG: get rkisp-isp-subdev devname: /dev/v4l-subdev4 DBG: get rkisp-input-params devname: /dev/video19 DBG: get rkisp-statistics devname: /dev/video18 DBG: get rkisp_mainpath devname: /dev/video11然后创建线程读取应用层的json文件订阅开启和关闭流事件最后死循环解析isp应用层参数并发送给驱动等待开启流事件intmain(intargc,char**argv){intret,i;// ret用于保存函数返回值状态i是循环计数器intthreads0;// 记录成功初始化的媒体节点数量即要创建的线程数/* 设置标准输出为行缓冲模式。这样即使输出被重定向到非交互设备如文件或管道 * 每打印一行并遇到换行符时会自动刷新缓冲区确保信息能及时写入。 */setlinebuf(stdout);parse_args(argc,argv);// 解析命令行参数具体实现未给出// 循环遍历所有可能的媒体设备节点0 到 MAX_MEDIA_NODES-1for(i0;iMAX_MEDIA_NODES;i){// 构造设备路径字符串如 /dev/media0, /dev/media1 ...sprintf(media_infos[i].mdev_path,/dev/media%d,i);// 调用 rkaiq_get_media_info 获取该媒体设备的拓扑信息if(rkaiq_get_media_info(media_infos[i])){// 如果获取失败返回值非0打印错误日志ERR(Bad media topology for: %s\n,media_infos[i].mdev_path);media_infos[i].available0;// 标记该设备不可用continue;// 跳过该设备继续下一个}// 获取成功标记该设备可用media_infos[i].available1;threads;// 可用设备计数加1}// 如果可用的媒体设备超过1个设置全局标志 has_mul_cam 为1表示多摄像头系统if(threads1)has_mul_cam1;// 第二轮循环为每个可用的媒体设备创建一个独立的引擎线程for(i0;iMAX_MEDIA_NODES;i){// 跳过不可用的设备if(!media_infos[i].available)continue;// 创建线程线程函数为 engine_thread参数为对应媒体设备的信息结构体指针retpthread_create(media_infos[i].pid,NULL,engine_thread,media_infos[i]);if(ret){// 创建失败清空线程ID打印错误并调用 errno_exit 终止程序media_infos[i].pid0;ERR(Failed to create camera engine thread for: %s\n,media_infos[i].mdev_path);errno_exit(Create thread failed);// 该函数通常打印错误并 exit}}// 第三轮循环等待所有成功创建的线程结束阻塞主线程for(i0;iMAX_MEDIA_NODES;i){// 跳过不可用或线程未成功创建的设备pid 0if(!media_infos[i].available||media_infos[i].pid0)continue;// 等待该线程结束pthread_join(media_infos[i].pid,NULL);}return0;// 程序正常结束}void*engine_thread(void*arg){intret0;// 返回值变量当前未使用可能是预留intisp_fd;// ISP图像信号处理器设备文件描述符unsignedintstream_event-1;// 流事件变量当前未使用可能是预留structrkaiq_media_info*media_info;// 指向媒体设备信息结构体的指针// 将传入的 void* 参数转换为具体的媒体信息结构体指针media_info(structrkaiq_media_info*)arg;// 打开 ISP 的参数设备节点如 /dev/video0 或 /dev/v4l-subdevX// O_RDWR 表示可读可写方式打开isp_fdopen(media_info-vd_params_path,O_RDWR);if(isp_fd0){// 打开失败打印错误信息并退出线程返回 NULLERR(open %s failed %s\n,media_info-vd_params_path,strerror(errno));returnNULL;}// 初始化引擎可能包括配置 media 管线、分配缓冲区、设置格式等init_engine(media_info);// 订阅流事件监听 V4L2 的 stream start/stop 事件‘流启动’和‘流停止’这两个事件// 参数media_info设备信息isp_fd设备文件描述符true表示订阅/启用subscrible_stream_event(media_info,isp_fd,true);// 主循环反复处理“流启动 → 流停止”的周期for(;;){// 启动引擎可能包括启动 ISP 流、使能视频采集等start_engine(media_info);// 调试打印等待流启动事件DBG(%s: wait stream start event...\n,media_info-mdev_path);// 阻塞等待 CIFISP_V4L2_EVENT_STREAM_START 事件// 参数isp_fd文件描述符事件类型超时时间-1 表示永久等待wait_stream_event(isp_fd,CIFISP_V4L2_EVENT_STREAM_START,-1);// 调试打印等待流启动事件成功DBG(%s: wait stream start event success ...\n,media_info-mdev_path);// 调试打印等待流停止事件DBG(%s: wait stream stop event...\n,media_info-mdev_path);// 阻塞等待 CIFISP_V4L2_EVENT_STREAM_STOP 事件wait_stream_event(isp_fd,CIFISP_V4L2_EVENT_STREAM_STOP,-1);// 调试打印等待流停止事件成功DBG(%s: wait stream stop event success ...\n,media_info-mdev_path);// 停止引擎可能包括停止 ISP 流、释放资源等stop_engine(media_info);}// 循环回到 start_engine等待下一个周期// 注意以下代码实际上永远不会执行因为上面的 for(;;) 是无限循环// 取消订阅流事件false 表示禁用/取消订阅subscrible_stream_event(media_info,isp_fd,false);// 反初始化引擎释放资源、关闭设备等deinit_engine(media_info);// 关闭 ISP 设备文件描述符close(isp_fd);returnNULL;// 线程退出}init_engine打开json文件staticvoidinit_engine(structrkaiq_media_info*media_info){intindex;// 索引变量但在当前函数中未被使用可能是预留或历史遗留// 初始化 RK AIQ (Rockchip AI Image Quality) 引擎上下文// 参数说明// media_info-sensor_entity_name: 传感器实体名称如 m00_b_ov13850 1-0010// IQ_PATH: 图像质量调优文件路径宏定义如 /etc/iqfiles/// NULL: 回调函数指针未使用// NULL: 回调函数参数未使用// 返回值AIQ 上下文句柄用于后续所有 AIQ 操作media_info-aiq_ctxrk_aiq_uapi2_sysctl_init(media_info-sensor_entity_name,IQ_PATH,NULL,NULL);// 判断是否为多摄像头系统全局变量 has_mul_cam 在主函数中设置if(has_mul_cam)// 设置多摄像头并发模式// 参数aiq_ctxAIQ上下文1启用多摄像头并发// 这个配置用于多摄像头同时工作时协调 3A 算法资源rk_aiq_uapi2_sysctl_setMulCamConc(media_info-aiq_ctx,1);// 准备 AIQ 引擎配置工作模式// 参数说明// media_info-aiq_ctx: AIQ 上下文// width: 图像宽度全局变量从命令行参数解析// height: 图像高度全局变量// RK_AIQ_WORKING_MODE_NORMAL: 正常工作模式非快速模式等if(rk_aiq_uapi2_sysctl_prepare(media_info-aiq_ctx,width,height,RK_AIQ_WORKING_MODE_NORMAL)){// 准备失败时打印错误并退出程序ERR(rkaiq engine prepare failed !\n);exit(-1);// 退出状态码 -1表示初始化失败}}start_engine解析json配置发送给isp驱动staticvoidstart_engine(structrkaiq_media_info*media_info){DBG(device manager start\n);// 调用 AIQ 库的启动函数rk_aiq_uapi2_sysctl_start(media_info-aiq_ctx);// 检查上下文是否为空但注意这个检查放在调用之后逻辑有问题if(media_info-aiq_ctxNULL){ERR(rkisp_init engine failed\n);exit(-1);}else{DBG(rkisp_init engine succeed\n);}}XCamReturnrk_aiq_uapi_sysctl_start(constrk_aiq_sys_ctx_t*ctx){ENTER_XCORE_FUNCTION();// 调试/日志宏标记函数进入RKAIQ_API_SMART_LOCK(ctx);// 加锁保证线程安全自动管理锁XCamReturn retXCAM_RETURN_NO_ERROR;// 初始化返回值// 真正的工作调用 RkAiqManager 的 start() 方法retctx-_rkAiqManager-start();EXIT_XCORE_FUNCTION();// 调试/日志宏标记函数退出returnret;}XCamReturn RkAiqManager::start(){// 调试/跟踪宏标记函数进入通常用于打印日志或性能分析ENTER_XCORE_FUNCTION();// 初始化返回值为成功XCAM_RETURN_NO_ERROR 通常定义为 0XCamReturn retXCAM_RETURN_NO_ERROR;// 第一步根据当前状态决定是否需要重新初始化 // restart - 如果状态是已停止需要重新应用参数if(_stateAIQ_STATE_STOPED){// 从分析器获取当前的完整参数包含 ISP、ISPP 等所有调优参数SmartPtrRkAiqFullParamsProxyinitParamsmRkAiqAnalyzer-getAiqFullParams();#if0// 这段代码被条件编译禁用了永远不会执行// 如果 ISP 测量参数存在将 frame_id 重置为 0if(initParams-data()-mIspMeasParams.ptr()){initParams-data()-mIspMeasParams-data()-frame_id0;}// 如果 ISPP 测量参数存在将 frame_id 重置为 0if(initParams-data()-mIsppMeasParams.ptr()){initParams-data()-mIsppMeasParams-data()-frame_id0;}#endif// 应用分析器结果第二个参数 true 表示这是强制应用即使参数没变化也要应用//解析json文件参数applyAnalyzerResult(initParams,true);}elseif(_stateAIQ_STATE_STARTED){// 如果已经处于启动状态直接返回成功不做任何操作returnret;}// 如果状态是其他值如 AIQ_STATE_INITIALIZED继续执行后续启动流程// 第二步启动结果应用线程预 AIQ 模式 // is_pre_aiq 表示是否是预处理模式某些平台或配置if(mTbInfo.is_pre_aiq){#ifndefDISABLE_RESULT_APPLY_THREADS// 如果没有禁用结果应用线程// 启动结果应用线程该线程负责将算法计算结果写入硬件寄存器bool bretmAiqRstAppTh-start();// 转换 bool 为 XCamReturntrue - 成功false - 失败retbret?XCAM_RETURN_NO_ERROR:XCAM_RETURN_ERROR_FAILED;// 宏检查返回值如果失败则打印错误并跳转到退出标签RKAIQMNG_CHECK_RET(ret,apply result thread start error);#endif}// 第三步启动 3A 分析器 // mRkAiqAnalyzer 是核心的 3A 算法分析器自动曝光/白平衡/对焦retmRkAiqAnalyzer-start();// 检查启动是否成功失败则打印错误并返回RKAIQMNG_CHECK_RET(ret,analyzer start error %d,ret);// 第四步启动亮度分析器特定硬件版本 #ifdefISP_HW_V20// 仅当 ISP 硬件版本为 V20 时编译// 如果亮度分析器指针非空if(mRkLumaAnalyzer.ptr()){// 启动亮度分析线程用于分析图像亮度统计信息retmRkLumaAnalyzer-start();RKAIQMNG_CHECK_RET(ret,luma analyzer start error %d,ret);}#endif// 第五步启动摄像头硬件 // mCamHw 是摄像头硬件抽象层启动后 ISP 硬件开始处理图像数据retmCamHw-start();RKAIQMNG_CHECK_RET(ret,camhw start error %d,ret);// 第六步启动结果应用线程非预 AIQ 模式 if(!mTbInfo.is_pre_aiq){// 如果不是预 AIQ 模式#ifndefDISABLE_RESULT_APPLY_THREADS// 触发启动可能用于唤醒或通知线程准备开始mAiqRstAppTh-triger_start();// 启动结果应用线程bool bretmAiqRstAppTh-start();retbret?XCAM_RETURN_NO_ERROR:XCAM_RETURN_ERROR_FAILED;RKAIQMNG_CHECK_RET(ret,apply result thread start error);#endif}// 第七步更新状态 // 将所有组件都成功启动后将管理器状态标记为已启动_stateAIQ_STATE_STARTED;// 调试/跟踪宏标记函数退出EXIT_XCORE_FUNCTION();// 返回成功returnret;}