网站服务器带宽多少合适,做条形码哪个网站比较好,直播间网站建设,中国全球门户网站我不生产代码#xff0c;我只是代码的搬运工#xff0c;相信我#xff0c;看完这个文章你的图片一定能变成流媒体推出去。 诉求#xff1a;使用opencv拉流#xff0c;转成bgr数据#xff0c;需要把处理后的数据#xff08;BGR#xff09;编码成264#xff0c;然后推流…我不生产代码我只是代码的搬运工相信我看完这个文章你的图片一定能变成流媒体推出去。 诉求使用opencv拉流转成bgr数据需要把处理后的数据BGR编码成264然后推流推出去相当于直播实时编码
播放器
超低延迟的RTSP播放器 https://github.com/tsingsee/EasyPlayer-RTSP-Win
青犀的一个播放器直接下他的EasyPlayer-RTSP-Win用来测试就行。划重点超低延时我整体方案的延时大概是600-700ms,使用海康相机rtsp拉流做了yolo处理再推出去,有编码有解码vlc的延时设置低了就回卡帧Gop已经改成5了还是卡帧没有测试Gop改成1的情况但是vlc的延时和流畅整体看是不太兼容的。ffmpeg使用nobuffer也会卡帧。直观感受卡的就是Gop的P帧。
服务器
live555 方案
如果你不着急的话。。。 可以试试这个方案这方面的参考文献给列下面了因为确实正经研究了几天还看了不少代码认真想了应该怎么处理但是确实不太想写而且对我的需求来讲live555冗余了很多功能再加上网上确实没有写好的我又很着急要结果确定方案能用所以也没有用这个方案。
官方demo
live555自己的测试文件是有推流demo的主要是根据实时需求推264文件以及无脑做一个推264文件的服务器当时看代码的时候一头雾水加上着急也没太认真看主要在live555\testProgs下面testOnDemandRTSPServer,testH264VideoStreamer 第二个是无脑推第一个是你来一个请求我从头开始给你播放一次视频文件。 这个东西的底层是向一个fTo指针里面拷贝264码流。 如果你Cpp、coding能力强的话应该是能看懂直接改的也就不用往后看了。
参考demo 零声 usb相机推流
网上基本上和我需求最接近的live555方案下的代码是国内的做音视频开发教学的一个零声出的视频还有他们传的这个代码。
主要功能是 v4l2相机读取mjpeg然后ffmpeg的avcodec相关库编码然后送live555然后推实时流像是改的testOnDemandRTSPServer结构很清晰除了不能用我也找不到原因外都挺好的。另外他的课是5K的有点贵。
视频的话去B站带关键词基本都能搜到这个代码我加了那个联系QQ要到的。但是在我本地没有推成功我也不确定是哪里的问题编译过了放在这里 https://gitee.com/qingfuliao/v4l2_ipc_live555?_fromgitee_search
在我这是下面这个demo 实现了一个相似的功能编译实测是可以读取usb相机然后推流成功的。但是代码结构没有上面那个清晰。
参考demo
https://github.com/mpromonet/v4l2rtspserver
这个功能是基于linux的v4l2,使用264的方式读取相机视频流如果你的usb相机不支持264输出会驱动失败然后直接拆帧把流发出去 需要自己下一个libv4l2cpp的代码放进来就能编译了
这个现在看稍微改一改就能用了不过当时对整体没有概念改了一阵子不知道怎么下手把我自己的码流变成demo的输入码流送进去了但是没有推成功定位了一会儿很难定位问题也就搁置了。有兴趣的可以基于这个改一改。
https://blog.csdn.net/qq_43418269/article/details/122488866
这个方案我是成功了的不过延时不太能满足我的需求这个复现很快。他是用一个管道文件做的我把编码之后264文件直接写到live555的testOnDemandRTSPServer.cpp这边的读取文件里面然后逻辑是live555这边接收到请求创建管道相机程序初始化后阻塞住管道被创建后往管道里写然后另一边就开始播。。
这个就相当于是运行在一个demo里的两个程序。流倒是推出来了只不过我这样实现延时很大他说的百毫秒量级我做不到我是2s左右。感觉也可能是我操作不当。
https://blog.csdn.net/lifexx/article/details/52823777
live555读文件改为内存读取实现确实C不太行这个文章对我理解Live555还有改成内存中的数据方向给了很大启发但是没有按照他的做而且他的参考代码无法运行。对我理解另一个推相机的demo有帮助
其他开源服务器框架
这个也是一个很容易就编译成功的服务器可以使用这个做服务器然后调用ffmpeg推流在RK3588上也推成功了基本没改make相关的配置需要按照他给的快速开始流程使用git下附加库功能很强大但是对我的需求来讲这个功能我进行二次开发比较慢。不排除我太菜。 https://github.com/ZLMediaKit/ZLMediaKit/tree/master
这里写这个的主要原因是他的一些文章对我的启发和快速上手有很大的参考意义比如下面这个。
https://github.com/ZLMediaKit/ZLMediaKit/wiki/%E6%80%8E%E4%B9%88%E6%B5%8B%E8%AF%95ZLMediaKit%E7%9A%84%E5%BB%B6%E6%97%B6%EF%BC%9F
RK3399 参考
网上有瑞芯微其他方案的rtsp推流我只能帮忙排除错误选项 https://t.rock-chips.com/forum.php?modviewthreadtid749extrapage%3D1 如果你是在看这个帖子可以不用看了这个猫头的代码虽然推出去了但是他的Rtsp是调库这个库是闭源的3588没有这个网站注册要两三天才能通过不必等这个用不了。
rtsp推流
在github上搜索rtsp,排名最高的那个结果就是那个小乌龟
https://github.com/PHZ76/RtspServer 这个功能比较单一但是足够满足我的需求了他还使用他自己的库做了一个windows下的应用windows上编译成功了但是不太好用不过对我理解他的Demo运行有一定帮助。因为用的是一套库。另外他主页还有一个rtmp ,我没进去看也没试能不能用。
https://github.com/PHZ76/DesktopSharing 下载安装
下载下来直接在3588上面make就可以编译的是RtspServer-master/example里面的main 文件这里的rtsp_h264_file.cpp是可以直接推运行推264文件的一般不需要修改就能直接用如果不能用有可能是554端口被占用改一个大点的就好了 std::string suffix live;std::string ip 127.0.0.1;std::string port 5543;// 改这里 不要改那个0.0.0.0 那个是对的不用改std::string rtsp_url rtsp:// ip : port / suffix;然后rtsp_pusher.cpp还有rtsp_server.cpp都把发送文件的部分注释掉了需要结合h264那个文件来对比把264码流写进去。
运行测试
编译出来之后运行 ./rtsp_h264_file ./test.264 就能推出来了 工程已经被我魔改过了重新生成一个sample 来演示下结果 这是RK3588 服务器
这是VLC的界面IP
下面的是显示的结果
改cmake 支持opencv 、rknn,mpp
因为我使用的RKNN之间是在Qt里面编译的工程使用的都是Cmake的cmakelists他的makefile也不难改主要的问题是我自己写的解码器在使用makefile指定mpp库之后编译出来的mpp库运行不正常具体报错找不到了然后我qt上用是没问题的定位到是makefile没写好改成cmakelist就可以正常编译了 -g是为了支持gdb调试配合我的vscode 调试配置文件可以单步调试全程在板上编译没配置交叉编译环境
cmake_minimum_required(VERSION 3.5)
project(rtspserver)
set(CMAKE_INCLUDE_CURRENT_DIR ON)set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} -DSOCKLEN_Tsocklen_t -g )find_package(OpenCV 4.5.5 REQUIRED)
find_package(OpenSSL REQUIRED)# MPP
set(MPP_PATH /home/orangepi/code/mpp-develop/inc)
set(MPP_LIBS /home/orangepi/code/mpp-develop/mpp/librockchip_mpp.so)
include_directories(${MPP_PATH})# OSAL
set(OSAL_PATH /home/orangepi/code/mpp-develop/osal/inc/ /home/orangepi/code/mpp-develop/utils)
set(OSAL_LIBS /home/orangepi/code/mpp-develop/osal/libosal.a /home/orangepi/code/mpp-develop/utils/libutils.a)
include_directories(${OSAL_PATH})# RKNN lib
set(RKNN_API_PATH ${CMAKE_SOURCE_DIR}/lib)
set(RKNN_RT_LIB ${RKNN_API_PATH}/aarch64/librknnrt.so)
include_directories(${RKNN_API_PATH}/include)
include_directories(${CMAKE_SOURCE_DIR}/3rdparty)# RGA
set(RGA_PATH ${CMAKE_SOURCE_DIR}/3rdparty/rga/RK3588)
set(RGA_LIB ${RGA_PATH}/lib/Linux/aarch64/librga.so)
include_directories(${RGA_PATH}/include)aux_source_directory(src/xop SOURCE1)
aux_source_directory(src/net SOURCE2)include_directories( src src/xopsrc/netsrc/3rdpart)# add_executable(rtsp
# example/rtsp_server.cpp
# ${SOURCE1}
# ${SOURCE2}
# src/3rdpart/md5/md5.hpp
# )
# target_link_libraries(rtsp ${MPP_LIBS} ${OSAL_LIBS} ${OpenCV_LIBS} ${RKNN_RT_LIB} ${RGA_LIB} OpenSSL::SSL OpenSSL::Crypto )
# add_executable(rh264
# example/rtsp_h264_file.cpp
# ${SOURCE1}
# ${SOURCE2}
# src/3rdpart/md5/md5.hpp
# )
# target_link_libraries(rh264 ${MPP_LIBS} ${OSAL_LIBS} ${OpenCV_LIBS} OpenSSL::SSL OpenSSL::Crypto )
add_executable(sample
example/sample.cpp
${SOURCE1}
${SOURCE2}
src/3rdpart/md5/md5.hpp
)
target_link_libraries(sample ${MPP_LIBS} ${OSAL_LIBS} ${OpenCV_LIBS} ${RKNN_RT_LIB} ${RGA_LIB} OpenSSL::SSL OpenSSL::Crypto )
代码修改
代码是基于他的rtsp_server来修改的主要修改的内容是sendFrameThread,大概思路是这样的还差一个问题怎么把你的原始mat图像转成264码流
bool IsKeyFrame(const char* data, uint32_t size)
{if (size 4) {//0x67:sps ,0x65:IDR, 0x6: SEIif (data[4] 0x67 || data[4] 0x65 || data[4] 0x6 || data[4] 0x27) {return true;}}return false;
}void SendFrameThread(xop::RtspServer* rtsp_server, xop::MediaSessionId session_id, int clients)
{ encoder e;// encoder相关 内存拷贝int size 0;char* buffer ;// 编码标志位int i 0;// 生成图像int width 1920;int height 1080;cv::Mat colorBar cv::Mat::zeros(height, width, CV_8UC3);// 设置彩条的宽度int barWidth width / 8; // 8个彩条你可以根据需要调整// 生成彩条for (int i 0; i 8; i) {// 计算彩条的起始和结束位置int startX i * barWidth;int endX (i 1) * barWidth;// 设置彩条颜色BGR格式cv::Vec3b color;if (i % 2 0) {color cv::Vec3b(255, 0, 155); // 蓝色} else {color cv::Vec3b(0, 255, 0); // 绿色}// 在colorBar上画出彩条colorBar(cv::Rect(startX, 0, barWidth, height)) color;}while(1){if(clients 0) /* 会话有客户端在线, 发送音视频数据 */{{ xop::AVFrame videoFrame {0};// printf(width is %d, height is %d,colorBar.rows,colorBar.cols);// 编码 发包if(0i){// 第一帧有sps信息 给他两帧拼一起char *buffer1;int size1;e.init(buffer1,size1);videoFrame.size size1;e.postAframe(colorBar,buffer,size);videoFrame.size size; videoFrame.buffer.reset(new uint8_t[videoFrame.size]);memcpy(videoFrame.buffer.get(), buffer1, size1);memcpy(videoFrame.buffer.get()size1, buffer, size);i;} else{e.postAframe(colorBar,buffer,size);videoFrame.size size; // 视频帧大小 videoFrame.buffer.reset(new uint8_t[videoFrame.size]);memcpy(videoFrame.buffer.get(), buffer, videoFrame.size);}videoFrame.type IsKeyFrame(buffer, size) ? xop::VIDEO_FRAME_I : xop::VIDEO_FRAME_P;// videoFrame.type 0; // 建议确定帧类型。I帧(xop::VIDEO_FRAME_I) P帧(xop::VIDEO_FRAME_P)videoFrame.timestamp xop::H264Source::GetTimestamp(); // 时间戳, 建议使用编码器提供的时间戳// writeCharPointerToFile((char *)videoFrame.buffer.get(), videoFrame.size, filename.txt); rtsp_server-PushFrame(session_id, xop::channel_0, videoFrame); //送到服务器进行转发, 接口线程安全/*//获取一帧 H264, 打包xop::AVFrame videoFrame {0};videoFrame.type 0; // 建议确定帧类型。I帧(xop::VIDEO_FRAME_I) P帧(xop::VIDEO_FRAME_P)videoFrame.size video frame size; // 视频帧大小 videoFrame.timestamp xop::H264Source::GetTimestamp(); // 时间戳, 建议使用编码器提供的时间戳videoFrame.buffer.reset(new uint8_t[videoFrame.size]); memcpy(videoFrame.buffer.get(), video frame data, videoFrame.size); rtsp_server-PushFrame(session_id, xop::channel_0, videoFrame); //送到服务器进行转发, 接口线程安全*/}{ /*//获取一帧 AAC, 打包xop::AVFrame audioFrame {0};audioFrame.type xop::AUDIO_FRAME;audioFrame.size audio frame size; /* 音频帧大小 audioFrame.timestamp xop::AACSource::GetTimestamp(44100); // 时间戳audioFrame.buffer.reset(new uint8_t[audioFrame.size]); memcpy(audioFrame.buffer.get(), audio frame data, audioFrame.size);rtsp_server-PushFrame(session_id, xop::channel_1, audioFrame); // 送到服务器进行转发, 接口线程安全*/} }// xop::Timer::Sleep(20); /* 实际使用需要根据帧率计算延时! 我这里处理延时很大就不人工延迟了*/}videocapture-release();e.deinit((MPP_RET)0);yolo5.deinit();}
rk3588编码
这方面网上的文章不太多但是官方给了demo都是中文认真看看功能都是能用的。 主要参考他的mpp-develop/test/mpi_enc_test 以及Rk3588-linux-v002\linux\docs\Linux\Multimedia\Rockchip_Developer_Guide_MPP_CN.pdf进行配置和使用。这相关的东西我之后再另开一个文章单独说总之参考这部分可以做一个BGR888转成264存储的demo。 这里重点说一个概念I、P、B帧 这个东西是264流编码的一个概念正常你每一帧的图像都很大比如一个1920*1080每个像素点存一个BGR888的话就是1920*1080*3 Byte 6220800 Byte ≈ 6M 然后一秒30帧的话一秒就要传180M局域网或许勉强可以但是对带宽压力也很大。所以这里就涉及到了压缩264、265就是压缩标准压缩中需要做两种压缩 帧内压缩和 帧间压缩。 帧内压缩使用一定方法使用尽量小的空间存一帧数据。 帧间压缩利用帧和前后帧的关联来进一步的压缩视频。
这里我们重点关注帧间压缩。给一个参考文献 https://zhuanlan.zhihu.com/p/409527359 说的挺透彻的,这里我粗略说一下看下面这个图
I帧 是帧间压缩里面的一帧完整图像P帧是前向预测帧B帧是双向预测帧 IDR帧是特殊的I帧在编解码时候P、B帧可以参考I帧前面的帧进行复原但是不能参考IDR帧前面的帧进行复原
而一个上面这样的循环被编码成一组我们指定了h264的gop大小就确定了多少帧中有一个I帧 而我们在做直播就导致为了低延时不要B帧gop 也要尽量的不要太大如果要解码P帧前面就一定要缓存I帧
监测工具
wireshark 这个感觉还是蛮必要的至少你能看见客户端和服务器之间说没说话。如果你有耐心开RTSP 或者网络协议握手说明的话你甚至能看到他们之间的握手流程。
杂记
中间遇到了一个问题因为我这个地方没有公网所以只能自己给开一个热点给电脑PC和RK3588之间通过路由器连接然后使用前面的某个demo的时候遇到的主机向服务器RK3588发出视频请求时服务器并没有直接给客户端发rtcp包而是给一个200多的地址发包而这个包在客户端收不到但是他能收到rtp的协议包所以vlc这边也不提示打不开就是没有图像显示。 后来使用的方法是和板子通过路由器网线连接然后电脑PC wifi 连接路由器然后路由器没有公网再使用一个手机usb给电脑共享网络让我的调试环境稳定可以接受到3588的流和路由器网线连接的时候接受不到包感觉是因为路由器没有网使用网线连接时候被屏蔽了服务器功能所以交给路由器的包转发请求没有被PC识别到但是wifi连接的话就算他没有网也是不能忽略的。 目前是这样理解的。