本文是对cnstream/samples/example/example.cpp代码的学习笔记
git代码 :https://github.com/Cambricon/CNStream
寒武纪开发者论坛: http://forum.cambricon.com/list-47-1.html
1 实例化一个pipeline
cnstream::Pipeline pipeline("MyPipeline");
初始化:
exit_msg_loop_ = false、启动一个线程smsg_thread_、生成一个事件event_bus_、生成一个索引分配管理器(暂且这样叫)IdxManager
2 构造一个pipeline
int Pipeline::BuildPipeline(const std::vector<CNModuleConfig>& configs);
初始化linked_id_mask(这个只收集所有链接的下游模块的id,比如A->B->C,D->E,那么id就只有B、C、E)他的作用是检查一个模块是否有上游模块。
实例化一个模型创造工Create,把所有模型的名字和信息(CNModuleConfig)存到modules_config_,把模型的名字和下游模型的名字存到connections_config_,把创造的模型(创造的是ClassName的类型,也就是Module基类的派生类模型,但是用一个基类指针去指向它)添加到modules_map_,模型的container_指向pipeline,模型获取全局唯一的id_。
把模型的名字和创造的associated_info(是一个ModuleAssociatedInfo的结构体,里面设置parallelism(conveyor队列的个数)以及每个队列的长度,又将connector指针指向一个connector类)添加到流水线的modules_中(作用主要是保存每个模型的社交属性)。
链接上下游模型的时候,对各自的ModuleAssociatedInfo进行更新:上游output_connectors保存link_id(Module_A --> Module_B),上游down_nodes保存down_node_name,下游input_connectors保存link_id(Module_A --> Module_B),最后把link_id和down_node_info的connector内容保存到pipeline的links_。
最后会输出没有上游的模块的一条信息。
3 SourceModule
MsgObserver msg_observer(static_cast<int>(video_urls.size()), &pipeline, source_name);
创建一个消息观察者 msg_observer,并且进行初始化:stream_cnt_(需要读取的信息的路数?), pipeline_指针指向pipeline,source_name_为源模块的名称。然后把这个观察者放在pipeline对象的smsg_observer_中。
cnstream::DataSource* source = dynamic_cast<cnstream::DataSource*>(pipeline.GetModule(source_name));
得到一个source指针,它是DataSource类型的,在pipeline的modules_map_中通过名字找到Module基类,又因为它其实是一个派生类,我们转换回它的派生类。DataSource重写了Open()、Close()、CheckParamSet()。
bool Pipeline::CreatePerfManager(std::vector<std::string> stream_ids, std::string db_dir, uint32_t clear_data_interval_in_minutes)
设置了pipeline中的start_node_开始节点为所有模型中没有input_connectors的,设置end_nodes_数组:将所有没有output_connectors的节点push进去。
对每一路流设置一个PerfManager类,保存在pipeline的perf_managers_中,根据stream_id进行查找。PerfManager是用来创造sql和保存数据的,它由db_name和所有module的名字来创造的。
4 启动流水线
pipeline.Start()
5 处理每一路数据
遍历每一路数据
pipeline.AddPerfManager(stream_id, FLAGS_perf_db_dir);
先根据stream_id和db_dir创建一个manager放到pipeline的perf_managers_中,把当前的stream_id写到pipeline的stream_ids_中,同时写入一条sql(这句待理解)。
之后根据文件的名字来确定文件是来自哪里的哪种格式。比如rtsp数据
ret = AddSourceForRtspStream(source, stream_id, filename); // stream_id 是标识哪一路的
int AddSourceForRtspStream(cnstream::DataSource* source, const std::string &stream_id, const std::string &filename) { auto handler = cnstream::RtspHandler::Create(source, stream_id, filename); // source是DataSource模块的 return source->AddSource(handler); }
这里根据DataSource模型和stream_id以及文件名创建了一个handler,这是属于RtspHandler类的。初始化了stream_index_(调用Module的一个方法)module_和steam_id_(都是SourceHandler的成员,RtspHandler继承了它),初始化了一个RtspHandlerImpl(module_指向source,url_name指向文件位置,handler_指向本handler,stream_id_跟SourceHandler的stream_id_一样)指针impl(是属于RtspHandler的)。
int SourceModule::AddSource(std::shared_ptr<SourceHandler> handler)
该方法主要是设置DataSource(继承于SourceModule)的source_map_这个map,键值对为stream_id和RtspHandler,又设置了RtspHandler(继承与SourceHandler)的stream_unique_idx_这个值。
这里RtspHandler(继承于SourceHandler)有两个值,一个是stream_index_它是通过stream_id_来获得;另一个是steam_unique_idx_它是我们添加一个handler到source里面标识这个handler的。
这里我们RtspHandler的两个接口Open()、Close()函数都是调用了impl指针指向的RtspHandlerImpl这个类的方法。
浙公网安备 33010602011771号