视频与AI,与进程交互(一)

目的

    正在写一个视频与AI的工具,从接入,算法处理,转发,存储, 到调用AI进程,并且与AI进程进行交互,插件化,脚本化,做得比较辛苦,期间的进程和线程交互以及结果渲染到UI上逻辑必须严谨,否则以c++ 来说,UI处理不好,主进程容易崩溃。

    下图是为了进行交互的工具,与接入的各类方式,如rtsp 接入,rtp直接接入,ps GB28181 接入,rtmp接入,ts流接入等等,工具的制作异常艰辛,抛弃了很多项目,心无旁骛,淡看过往,—看似洒脱,实属无奈。
视频与AI,与进程交互(一)_第1张图片

2 深思

2.1 保证核心通用

既然是一个比较先进的工具,创新是必然的,然而我将创新放到了插件中,创新必须包含很多的灵活性,因为你是一种工具,是一种“空”,而世上只有空才能包容万物。组件插件化,脚本化,是保证核心逻辑通用的方式。

2.2 插件化

将图像运行,转发,AI,进行插件化,为每一路视频图像进行插件管理。接入是一种统一的外接入,为了使得动态库和主进程进行
视频与AI,与进程交互(一)_第2张图片

3 数据结构

定义接入的数据结构要考虑很多问题

typedef struct s_param
{
	const char* name = NULL;//所属名称 如rtspclient
	const char* url = NULL;//唯一名称,or ip+port
	uint8_t* data = NULL;
	int len = 0;   //len = -1 收到结束 
	uint32_t ssrc;
	void* obj = NULL; //create 中传入的obj
	const char* ipfrom = NULL;
	int width = 0;
	int height = 0;
	uint16_t fport;
	uint16_t seq;//兼容rtp
	int codeid;  //与ffmpeg兼容
	int payload = -1; //payload type
	int64_t pts;
	void* decoder = NULL;
	uint8_t* extradata = NULL;
	int extradatalen = 0;
	//bool isnew = true; //是否是一个新的传输
	//void* rtspworker = NULL;
}s_param;

//回调函数是压缩流
typedef void(*func_callback)(s_param*  param);

typedef void(*func_callback_new)(void* obj, const char* url);
//len >0 vps sps pps 信息
//len = 0  
//len = -1 无法连接
//len = -2 read 出错
//len = -3 其他错误
typedef void(*func_callback_extra)(uint8_t* data, int len);
//

制作过程中接入视频以后才知道很多东西不能光靠想象,即使有几十年的视频经验,程序依然需要打磨,平时太多的demo制作,没有产品化好的弊端完全显露出来,即使是一个数据结构,也是改之又改。为了在工具中能够进行视频拼接,加入了很多方法:如下图所示
视频与AI,与进程交互(一)_第3张图片
视频拼接是一个比较大的话题,我们以后再进行描述。以下是视频拼接的第一步算法,鱼眼矫正处理,加载了鱼眼矫正的插件动态库。

视频与AI,与进程交互(一)_第4张图片
视频与AI,与进程交互(一)_第5张图片
鱼眼矫正算法加载

extern "C"
{
	_declspec(dllexport) const char* WINAPI func_name();
	_declspec(dllexport) int WINAPI _fastcall func_worker(uint8_t* data, int w, int h, uint8_t* outdata);
	_declspec(dllexport) int WINAPI func_no();
}

以上是插件的接口定义,为了达到视频的无极缓存,需要从申请内存开始,到动态库图像算法,到转发,存储,再接入AI脚本,与AI脚本共享内存,一直使用同一份内存。
视频与AI,与进程交互(一)_第6张图片

测试-图像鱼眼矫正算法载入

	//画一帧图像
	void func_draw(int px, int py, int drawrect)
	{
		AVFrame* frame = NULL;
		if (v_frames.dequeue(&frame))
		{

			/*cv::Mat nmat;
			nmat.cols = frame->width;
			nmat.rows = frame->height;
			nmat.data = frame->data[0];*/
			//装载畸形矫正算法
			//if (!v_funcs.empty())
			
			auto iter = v_plugins.begin();
			while (iter != v_plugins.end())
			{
				(*iter)->FUNC_worker(frame->data[0], frame->width, frame->height,NULL);
				iter++;
			}
			
			
			v_drawer.func_draw(px, py, frame, drawrect);
			{

				if (v_script != nullptr && !v_script->IsStop())
				{
					if (frame->key_frame)
					{
						//写入共享内存
						int w = frame->width;
						int h = frame->height;
						mem_info_ptr ptr = v_script->mem_getch_process(frame->data[0], w, h);
						if (ptr != nullptr)
						{
							v_script->v_in.Push(ptr);
							v_script->Notify();//通知取队列
						}
					}
					if (!v_script->v_out.IsEmpty())
					{
						//开始画子画面//子画面中开始画结果
						//v_script->
					}

				}


				
				std::lock_guard<std::mutex> lock(v_mutex_cache);
				if (v_cache != NULL)
				{
					av_freep(&v_cache->data[0]);
					av_frame_free(&v_cache);
				}
				//保存最后一帧当前帧
				v_cache = frame;
				//是否需要和python脚本进程交互
			}
		}
	}

鱼眼矫正图像是为了做多个摄像头做拼接而做的算法,整个逻辑接入,到绘制到UI上,都通过一个回调而进入核心绘制程序,所以回调的地方会有很多逻辑分离代码。

4 进程通信

1 lua
与脚本交互,1 是lua语言, 2 是javascript语言,3 是glsl 语言 4 就是需要常用的AI 处理 python
lua的想法很一般,就是c++ 提供API, 提供httpserver和websocket server,lua组织一下基本启动,当然,lua有一个好处就是不用配置文件了,配置文件直接就包含在lua 脚本里面就行

2 javascript
更简单,直接调用javascript 函数,反之javascript调用c++ 函数,嵌入v8 引擎。

2 python
这个是重点,就是c++ 到底如何驱动python, 需要一个进程管理还是简单调用,后来做起来发觉,确实需要进程管理,c++使用boost库做进程管理是比较方便的,不过先使用更简单的方案,就是使用异步调用进程和内存共享,

#include 
#include 
#include 
//#include 
#include 
#include 


int main_face()
{
   //std::system("python.exe face.py");
   namespace bp = boost::process;
   //std::string child_process_name = "child_process";
   //std::cout << "main_process: before spawning" << std::endl;


   bp::child c(bp::search_path("python.exe"), "face.py");
  tcpclient  client;
   while (c.running())
   {
   	//printf("running");
       client.send();
        client.read();		
   }

   c.wait(); //wait for the process to exit   
   int result = c.exit_code();
}

使用boost去启动进程,做进程记录,并且在UI中要有处理进程管理的管理器。
使用tcp 协议去发送视频图像的某一帧已经准备好了在内存共享中,接收到tcp消息表明在内存共享中已经处理完毕,把消息插入到队列中,主进程进行发现并且渲染。

其他方式:
使用std::system 去启动进程,用c++ 的高级特性,async进行循环等待,依然使用tcp 或者udp进行通信,注意

void f()
{
	std::system("python.exe face.py");
}

int main()
{
	auto fut = std::async(f);          // 异步执行f

    udpclient client;
	while (fut.wait_for(1000ms) !=         // 循环直到f执行结束
		std::future_status::ready)          //意味着python程序已经退出进程
	{
	    client.send(w,h,number)
	    client.read(w,h,number)
		//tcp 或者udp 进行通信
		//消息入队列
		queue.push()
	}
}

使用boost 来进行进程上的管理,或者自行异步进行进程管理都是可行的,udp方式比tcp方式优势是udp其实本身也是半“异步的”, 在不绑定udp 的情况下,不用考虑udp 缓存处理,在绑定udp的情况下,udp 缓存到极限则会阻塞,我们只要加入udp 在接收到数据的情况下知道python进程已经准备好了就行,tcp 处理也是一样。

这是系列的一,我会继续完善这个工具,直到他比较方便、代码清晰的时候进行开源。下一篇文章,必然是这个工具取得比较大的进步了

你可能感兴趣的:(音视频,人工智能,交互,插件,视频拼接)