一种视频算法插件流水线执行架构

目的

将视频接入进来以后,使用算法对图像进行处理并且输出
1 各种接入
2 解码
3 解码后图像算法 如矫正算法
4 共享输出

方式

使用动态库的方式进行扫描底层,每个动态库为一个插件,每个插件包含特定的函数,通过扫描的方式加载所有插件

数据结构

typedef struct PlugIn
{
    
    
	std::string name;
	//获取插件名称
	//加载顺序
	int no;
	func_name_get FUNC1;
	//获取插件功能模块函数
	func_worker_get FUNC2;
	func_no_get FUNC3;
	HMODULE Module;
	PlugIn* next = NULL;
	PlugIn* nextgroup = NULL;
	//该插件默认不开启
	int inuse = 0;
}PlugIn;

其中,基本的数据结构包含PlugIn 数据结构,两个hash表,一个hash表为容器,存储所有对象,也方便立刻查找到某一个插件,另外一个hash表为指针数据,每个对象由next指针和nextgroup指针, nextgroup表示都为同一个no号码的动态库,比如no为0 的动态库优先级为最高,但是有三个,表示为同一优先级,并且可以并行执行,只有一个表示必须单独执行。最大插件为256,不能超过这个数目。超过这个数目不加载。
在这里插入图片描述

动态库示例

#include <stdint.h>
#include <opencv2/opencv.hpp>
#include <string>
extern "C"
{
    
    
	_declspec(dllexport) std::string WINAPI func_name();
	_declspec(dllexport) cv::Mat WINAPI _fastcall func_worker(cv::Mat& data, int w, int h);
	_declspec(dllexport) int WINAPI func_no();
}

3 插件框架

3.1 插件基础函数定义

typedef  std::string (WINAPI *func_name_get)();
typedef cv::Mat (WINAPI *func_worker_get)(cv::Mat& data, int w, int h);
typedef int (WINAPI *func_no_get)();

三个函数分别表示为名称获取,功能,no号码获取,每个插件必须包含这三个函数

3.2 扫描文件系统

	int scandll(const std::string &path, std::vector<std::string> &dirs,
		std::vector<std::string> &files)
	{
    
    
		struct _finddata_t filedata;
		memset(&filedata, 0x0, sizeof(filedata));
		std::string _path = path;

		//windows is dll
		_path += "\\*.dll";

		intptr_t handle = _findfirst(_path.c_str(), &filedata);
		if (handle == -1)
		{
    
    
			std::cout<<"扫描目录失败.\n"<<std::endl;
			return -1;
			
		}

		do
		{
    
    
			if (filedata.name[0] != '.')
			{
    
    
				//cout<<file.name<<endl;

				if (filedata.attrib & _A_SUBDIR)
					dirs.push_back(filedata.name);
				else
					files.push_back(path+"\\"+filedata.name);

				if (filedata.attrib & _A_SUBDIR)
				{
    
    
					//文件为目录(文件夹)


					std::string tmp_path = path;
					tmp_path += "\\";
					tmp_path += filedata.name;

					//递归遍历
					scandll(tmp_path, dirs, files);
				}
			}

		} while (!_findnext(handle, &filedata));

		_findclose(handle);
		return 0;
	}

该函数为扫描文件夹下plugin 下面文件夹所有的动态库,window下扫描所有dll, 而linux下则扫描所有 so 文件

	int loadall(std::vector<std::string> dlls)
	{
    
    
		memset(&v_cache[0], 0, sizeof(v_cache));
		int ret = 0;
		auto iter = dlls.begin();
		while (iter != dlls.end())
		{
    
    
			if (load(*iter) == 0)
				ret++;
			iter++;
		}
		PlugIn* a = NULL;
		PlugIn* b = NULL;
		//将指针数据里的数据链接起来
		for (int i = v_minno; i <= v_maxno; ++i)
		{
    
    
			if (v_cache[i] != NULL)
			{
    
    
				if (a == NULL)
				{
    
    
					a = v_cache[i];
					//记录第一个编号,最小的编号
					v_firstno = i;
				}
				else
				{
    
    
					a->nextgroup = v_cache[i];
					a = v_cache[i];
				}
			}
		}

		return ret;
	}

以上为装载过程,单个load函数如下所示:

	//改进算法使用链表和
	int load(std::string& name)
	{
    
    

		std::cout << "load name " << name << std::endl;
		PlugIn plugin;
		plugin.no = -1;
		//func_name_get FUNC1 = NULL;
		//func_worker_get FUNC2 = NULL;
		plugin.Module = LoadLibraryA(name.c_str());
		if (plugin.Module == NULL)
			return -1;
		plugin.FUNC1 = (func_name_get)GetProcAddress(plugin.Module, "func_name");
		plugin.FUNC2 = (func_worker_get)GetProcAddress(plugin.Module, "func_worker");
		plugin.FUNC3 = (func_no_get)GetProcAddress(plugin.Module, "func_no");
		if (plugin.FUNC3 != NULL)
		{
    
    
			plugin.no = plugin.FUNC3();
		}
		else
		{
    
    
			FreeLibrary(plugin.Module);
			return -1;
		}
		if (plugin.no > (PLUGIN_MAX - 1))//编号过大直接卸载
		{
    
    
			FreeLibrary(plugin.Module);
			return -1;
		}
		if (plugin.no < 0)
			plugin.no = 0;

		if (plugin.FUNC1 != NULL && plugin.FUNC2!=NULL)
		{
    
    
			plugin.name = plugin.FUNC1();
			//hash表准备
			v_mapplugin[plugin.no] = plugin;

			PlugIn* pobj = &v_mapplugin[plugin.no];

			int no = plugin.no;
			if (no > v_maxno)
				v_maxno = no;//记录最大插件编号
			if (no < v_minno)
				v_minno = no;//记录最小插件编号
			//cache表只是按顺序放的指针
			PlugIn* tmp = v_cache[no];
			if (tmp == NULL)
				v_cache[no] = pobj;
			else
			{
    
    
				while (tmp != NULL && tmp->next != NULL)
				{
    
    
					tmp = tmp->next;
				}
				tmp->next = pobj;
			}
	
		}
		return 0;
	}

单个插件装载的时候要注意是否有同一插件编号,有则挂到最末端的指针,如果要在组里面继续使用优先级,可以改变上面代码,增加一个组内编号。

单元测试

将以上函数封装成类后,使用单例挂载对象,指定扫描目录为plugin,开始加载,执行函数,下面测试只简单测试是否挂载正常,打印所有链接的插件名称。

#include "../client/plug.h"
#include <iostream>
#ifdef _DEBUG
#pragma comment(lib,"opencv_world450d.lib")
#else 
#pragma comment(lib,"opencv_world450.lib")
#endif
using namespace std;
int main()
{
    
    
	//PlugIn* v_cache[256] = { 0 };

	//PlugIn in1;
	//in1.no = 2;
	//in1.name = "test";
	//v_cache[in1.no] = &in1;

	//std::cout << v_cache[2]->name << std::endl;


	string path = "plugin";
	vector<string> sub_dir,sub_file;

	c_plugins::instance()->scandll(path, sub_dir, sub_file);

	c_plugins::instance()->loadall(sub_file);

	c_plugins::instance()->printoutname();
 这里可以执行图像函数,输入opencv Mat
	c_plugins::instance()->unload_all();
	return 0;
}

测试结果

在这里插入图片描述

继续改进

将使用热插拔的方式继续改进,以上代码暂时是在windows上执行,下一版将增加linux代码。

猜你喜欢

转载自blog.csdn.net/qianbo042311/article/details/130384693
今日推荐