CGI程序刷新视频到HTTP的流程
上一篇,我们已经初步了解HTTP刷新视频的原理,这次我们重点来看CGI如何实时刷新共享内存的视频到HTTP。具体的步骤有上面四步分别是:
- 初始化HTTP响应头部。
- 获取每一帧共享内存的RGB数据。
- 使用OPENCV把每一帧RGB数据转换成JPG数据流(目的是为了节省网络,实质传输的是一张一张图片到网页)
- 把JPG数据刷新到HTTP服务器进行响应。
初始化HTTP响应头部。
int main()
{
//第三步:初始化HTTP响应头部
std::cout << "HTTP/1.0 200 OK\r\n";
std::cout << "Server: Motion/4.1.1\r\n";
std::cout << "Connection: close\r\n";
std::cout << "Max-Age: 0\r\n";
std::cout << "Expires: 0\r\n";
std::cout << "Cache-Control: no-cache, private\r\n";
std::cout << "Pragma: no-cache\r\n";
std::cout << "Content-Type: multipart/x-mixed-replace; boundary=Hello_Harry\r\n\r\n";
}
HTTP/1.0 200 OK\r\n指的是HTTP的1.0版本并且状态是200(200指的是响应成功)
Server: Motion/4.1.1\r\n指的是HTTP动态处理图像,版本是4.1.1
Connection: close\r\n指的是默认HTTP开始的时候,是关闭状态
Max-Age: 0\r\n指的是存活时间为0
Expires: 0\r\n指的是过期时间为0
Cache-Control: no-cache, private\r\n指的是对数据不缓存,打开新窗口会重新访问服务器
Pragma: no-cache\r\n指的也是数据不进行缓存
Content-Type: multipart/x-mixed-replace; boundary=Hello_Harry这一段是整个首部最重要的部分,它表示的是HTTP响应类型是流媒体数据(multipart/x-mixed-replace)如视频流、音频流,并且当每一个新的数据相应的时候就替换当前部分。这通常用不断更新的内容,比方说:实时视频流或者实时图表数据;boundary后面的字段是自定义的
获取每一帧共享内存的RGB数据。使用OPENCV把每一帧RGB数据转换成JPG数据流
//共享内存里面的数据是源源不断流过来
while(1)
{
//第一步:获取共享内存视频流数据
//其实就是把数据从共享内存拷贝到OpenCV,为什么要拷,todo...
//RGB_FRAME_SIZE WIDTH:是宽*高*3,3是rgb三个颜色的通道。
memcpy(rgbImg.data, addr, RGB_FRAME_SIZE);
//第二步:把rgb视频流做一个装换,变成jpg字节流
//todo......
//把这些的jpg字节流数据保存到buff里面
std::vector<uchar> buff;
//在这里开始转换,把rgbImg里面的数据rgb视频流数据转换成一个jpg的视频流
cv::imencode(".jpg", rgbImg, buff);
}
先是获取共享内存的RGB数据,并使用opencv转换成jpg流的代码。这里使用的api是imencode, imencode的作用是把当前的视频流编码为指定格式的字节流。我们来看看imencode的结构:bool imencode(const string& ext, InputArray img, vector<uchar>& buf, const vector<int>& params = vector<int>());
第一个传参: 需要编码的文件扩展名,”.jpg”、”.png”。注意:这里的编码指的是把视频流转换成字节流,并不是压缩编码,未来传输的也是一帧一帧图像。
第二个传参:输入的图像,这里就是共享内存的RGB数据
第三个传参:输出的字节流,这里用向量(vector)来表示输出的字节流
共享内存的RGB视频流经过imencode处理后就会输出RGB的字节流,RGB的字节流用vector<unchar>。由于HTTP刷新的视频需要用字节流显示,所以需要把视频流转换成字节流才能够正常显示。
把JPG数据响应HTTP服务器
//第三步:2.把JPG数据响应HTTP服务器
//指定每次刷新的格式是jpeg的图像格式
std::cout << "Hello_Harry\r\nContent-type: image/jpeg\r\n\r\n";
//每次刷新的数据都是vector向量存储的jpg数据,
//buf.data是每一帧jpg的缓冲区数据, buf.size是每一帧jpg的长度
//std::cout.write() 方法将指定大小的数据从内存地址写入标准输出流。
/**
* std::cout <<: 适用于大多数输出场景,特别是需要自动格式化的情况。
* std::cout.write(): 适用于需要高效输出大量字符数据或二进制数据的情况,且不需要自动格式化。
*/
std::cout.write(reinterpret_cast<const char* >(buff.data()), buff.size());
//刷新缓冲区,确保所有的数据都被写入输出流
std::cout.flush();
到这里cgi部分就已经完成了,接下来就是看HTML怎么获取这些cgi视频流。
<!DOCTYPE html>
<html>
<head>
<title>CGI Video Stream</title>
</head>
<body>
<img src="/cgi-bin/sharemeomory_rv1126_cgi.cgi" width="1920" height="1080" />
</body>
</html>
看上面HTML的程序代码,代码里面就给一个img标签它,src资源后面跟的就是视频数据的来源,来源就是这个程序:sharemeomory_rv1126_cgi.cgi
sharemeomory_rv1126_cgi.cgi就是我们编译的sharemeomory_rv1126_cgi.cpp源文件编译生产的。
编译好了之后,就把这个文件放到www/cgi-bin路径下