Qt/C++音视频开发49-推流到各种流媒体服务程序

科技飞扬不打烊 2024-02-13 08:05:22
一、前言

最近将推流程序完善了很多功能,尤其是增加了对多种流媒体服务程序的支持,目前支持mediamtx、LiveQing、EasyDarwin、nginx-rtmp、ZLMediaKit、srs、ABLMediaServer等,其中经过大量的对比测试,个人比较建议使用mediamtx和ZLMediaKit,因为这两者支持的格式众多,不仅同时支持rtsp/rtmp推流,还支持各种格式rtsp/rtmp/hls/flv/ws-flv/webrtc等拉流,涵盖面非常全,而且拉流的画面非常流畅,在局域网没有出现花屏的现象,对视频文件、视频流支持都非常友好。

为了增强程序的拓展性,以便适应后期增加其他流媒体服务器程序,特意将流媒体服务程序的信息用配置文件存取来,可以自行增删改,推流和拉流对应的端口都可以自行修改,这样非常适用于一台电脑多种流媒体服务,通过配置不同的端口来保证同时推流到多个流媒体服务程序,比如windows系统554端口很可能被系统的进程占用,所以需要更改为其他端口,在流媒体服务程序对应的配置文件更改后,还需要在推流程序对应的配置文件中修改,这样后期如果增加了其他的流媒体服务程序,只需要在配置文件增加即可,程序会自动读取并加载到下拉框。

二、效果图

三、体验地址国内站点:https://gitee.com/feiyangqingyun国际站点:https://github.com/feiyangqingyun个人作品:https://blog.csdn.net/feiyangqingyun/article/details/97565652体验地址:https://pan.baidu.com/s/1d7TH_GEYl5nOecuNlWJJ7g 提取码:01jf 文件名:bin_video_push。四、功能特点支持各种本地视频文件和网络视频文件。支持各种网络视频流,网络摄像头,协议包括rtsp、rtmp、http。支持将本地摄像头设备推流,可指定分辨率和帧率等。支持将本地桌面推流,可指定屏幕区域和帧率等。自动启动流媒体服务程序,默认mediamtx(原rtsp-simple-server),可选用srs、EasyDarwin、LiveQing、ZLMediaKit等。可实时切换预览视频文件,可切换视频文件播放进度,切换到哪里就推流到哪里。推流的清晰度和质量可调。可动态添加文件、目录、地址。视频文件自动循环推流,如果视频源是视频流,在掉线后会自动重连。网络视频流自动重连,重连成功自动继续推流。网络视频流实时性极高,延迟极低,延迟时间大概在100ms左右。极低CPU占用,4路主码流推流只需要占用0.2%CPU。理论上常规普通PC机器推100路毫无压力,主要性能瓶颈在网络。推流可选推流到rtsp/rtmp两种,推流后的数据支持直接rtsp/rtmp/hls/webrtc四种方式访问,可以直接浏览器打开看实时画面。可以推流到外网服务器,然后通过手机、电脑、平板等设备播放对应的视频流。每个推流都可以手动指定唯一标识符(方便拉流/用户无需记忆复杂的地址),没有指定则按照策略随机生成hash值。自动生成测试网页直接打开播放,可以看到实时效果,自动按照数量对应宫格显示。推流过程中可以在表格中切换对应推流项,实时预览正在推流的视频,并可以切换视频文件的播放进度。音视频同步推流,符合264/265/aac格式的自动原数据推流,不符合的自动转码再推流(会占用一定CPU)。转码策略支持三种,自动处理(符合要求的原数据/不符合的转码),仅限文件(文件类型的转码视频),所有转码。表格中实时显示每一路推流的分辨率和音视频数据状态,灰色表示没有输入流,黑色表示没有输出流,绿色表示原数据推流,红色表示转码后的数据推流。自动重连视频源,自动重连流媒体服务器,保证启动后,推流地址和打开地址都实时重连,只要恢复后立即连上继续采集和推流。提供循环推流示例,一个视频源同时推流到多个流媒体服务器,比如打开一个视频同时推流到抖音/快手/B站等,可以作为录播推流,列表循环,非常方便实用。根据不同的流媒体服务器类型,自动生成对应的rtsp/rtmp/hls/flv/ws-flv/webrtc地址,用户可以直接复制该地址到播放器或者网页中预览查看。编码视频格式可以选择自动处理(源头是264就264/源头是265就265),转H264(强制转264),转H265(强制转265)。支持Qt4/Qt5/Qt6任意版本,支持任意系统(windows/linux/macos/android/嵌入式linux等)。五、相关代码QList<QString> VideoPushUrl::listPushType = QList<QString>();QList<QString> VideoPushUrl::listPullType = QList<QString>();QList<int> VideoPushUrl::listPullPort = QList<int>();void VideoPushUrl::initServerInfo(){ listPushType.clear(); listPullType.clear(); listPullPort.clear(); listPushType << "mediamtx" << "mediamtx" << "mediamtx" << "mediamtx"; listPullType << "rtsp" << "rtmp" << "hls" << "webrtc"; listPullPort << 8554 << 1935 << 8888 << 8889; listPushType << "LiveQing" << "LiveQing" << "LiveQing" << "LiveQing" << "LiveQing"; listPullType << "rtmp" << "hls" << "flv" << "ws-flv" << "webrtc"; listPullPort << 10085 << 18000 << 18000 << 18000 << 18000; listPushType << "EasyDarwin"; listPullType << "rtsp"; listPullPort << 5541; listPushType << "nginx-rtmp"; listPullType << "rtmp"; listPullPort << 1935; listPushType << "ZLMediaKit" << "ZLMediaKit" << "ZLMediaKit" << "ZLMediaKit" << "ZLMediaKit" << "ZLMediaKit"; listPullType << "rtsp" << "rtmp" << "hls" << "flv" << "ws-flv" << "webrtc"; listPullPort << 554 << 1935 << 80 << 80 << 80 << 80; listPushType << "srs" << "srs" << "srs" << "srs"; listPullType << "rtmp" << "hls" << "flv" << "webrtc"; listPullPort << 1935 << 8080 << 8080 << 8080; listPushType << "ABLMediaServer" << "ABLMediaServer" << "ABLMediaServer" << "ABLMediaServer" << "ABLMediaServer"; listPullType << "rtsp" << "rtmp" << "hls" << "flv" << "ws-flv"; listPullPort << 554 << 1935 << 9088 << 8088 << 6088;}void VideoPushUrl::initServerInfo(const QString &fileName){ listPushType.clear(); listPullType.clear(); listPullPort.clear(); QFile file(fileName); if (file.open(QFile::ReadOnly | QFile::Text)) { while (!file.atEnd()) { QString content = file.readLine(); content.replace("\r", ""); content.replace("\n", ""); if (content.isEmpty()) { continue; } QStringList list = content.split(","); if (list.count() == 3) { listPushType << list.at(0); listPullType << list.at(1); listPullPort << list.at(2).toInt(); } } }}QStringList VideoPushUrl::getPushType(){ QStringList types; foreach (QString type, listPushType) { if (!types.contains(type)) { types << type; } } return types;}QString VideoPushUrl::getPushPath(const QString &pushUrl){ //("rtsp:", "", "127.0.0.1:5541") ("rtsp:", "", "127.0.0.1:5541", "live") ("rtsp:", "", "127.0.0.1:5541", "live/test") QString path = "/"; QStringList list = pushUrl.split("/"); int count = list.count(); //从第三位开始后面所有的都是资源目录 for (int i = 3; i < count; ++i) { path = path + list.at(i) + "/"; } //末尾的斜杠去掉 return path.mid(0, path.length() - 1);}int VideoPushUrl::getPullPort(const QString &pushType, const QString &pullType){ int port = 80; int count = listPushType.count(); for (int i = 0; i < count; ++i) { if (listPushType.at(i) == pushType && listPullType.at(i) == pullType) { port = listPullPort.at(i); break; } } return port;}//各种拉流协议分析 https://www.cnblogs.com/xi-jie/p/14031604.htmlQString VideoPushUrl::getPullUrl(const QString &pushUrl, const QString &pushType, const QString &pullType, const QString &ip, const QString &flag){ //找到对应服务器类型和拉流类型的端口 int port = getPullPort(pushType, pullType); //资源目录(可以为空) QString path = getPushPath(pushUrl); //去掉特殊字符比如? QString name = flag.split("?").first(); //根据服务器类型获取对应的地址 QString url = QString("://%1:%2%3/%4").arg(ip).arg(port).arg(path).arg(name); if (pushType == "mediamtx") { //同时支持rtsp/rtmp推拉流(非常棒) if (pullType == "rtsp") { url = "rtsp" + url; } else if (pullType == "rtmp") { url = "rtmp" + url; } else if (pullType == "hls") { url = "http" + url; } else if (pullType == "webrtc") { url = "http" + url; } } else if (pushType == "LiveQing") { //只支持rtmp推流 if (pullType == "rtmp") { url = QString("rtmp://%1:%2/hls/%3").arg(ip).arg(port).arg(name); } else if (pullType == "hls") { url = QString("http://%1:%2/hls/%3/%3_live.m3u8").arg(ip).arg(port).arg(name); } else if (pullType == "flv") { url = QString("http://%1:%2/flv/hls/%3.flv").arg(ip).arg(port).arg(name); } else if (pullType == "ws-flv") { url = QString("ws://%1:%2/ws-flv/hls/%3.flv").arg(ip).arg(port).arg(name); } else if (pullType == "webrtc") { url = QString("webrtc://%1:%2/rtc/hls/%3").arg(ip).arg(port).arg(name); } } else if (pushType == "EasyDarwin") { //只支持rtsp推流拉流 if (pullType == "rtsp") { url = "rtsp" + url; } } else if (pushType == "nginx-rtmp") { //只支持rtmp推流拉流 if (pullType == "rtmp") { url = "rtmp" + url; } } else if (pushType == "ZLMediaKit") { //同时支持rtsp/rtmp推拉流(名气最大/用户最多) if (pullType == "rtsp") { url = "rtsp" + url; } else if (pullType == "rtmp") { url = "rtmp" + url; } else if (pullType == "hls") { url = "http" + url + "/hls.m3u8"; } else if (pullType == "flv") { url = "http" + url + ".live.flv"; } else if (pullType == "ws-flv") { url = "ws" + url + ".live.flv"; } else if (pullType == "webrtc") { } } else if (pushType == "srs") { //不支持rtsp推流拉流(以前支持/后面都移除了) if (pullType == "rtmp") { url = "rtmp" + url; } else if (pullType == "hls") { url = "http" + url + ".m3u8"; } else if (pullType == "flv") { url = "http" + url + ".flv"; } else if (pullType == "webrtc") { url = "webrtc" + url; } } else if (pushType == "ABLMediaServer") { //支持rtsp/rtmp推流拉流(目前还不稳定/兼容性不够好) if (pullType == "rtsp") { url = "rtsp" + url; } else if (pullType == "rtmp") { url = "rtmp" + url; } else if (pullType == "hls") { url = "http" + url + ".m3u8"; } else if (pullType == "flv") { url = "http" + url + ".flv"; } else if (pullType == "ws-flv") { url = "ws" + url + ".flv"; } } else if (pushType == "Monibuca") { //支持rtsp/rtmp推流拉流(拉流格式众多/各种插件/性能很强劲/具体有待验证) if (pullType == "rtsp") { url = "rtsp" + url; } else if (pullType == "rtmp") { url = "rtmp" + url; } else if (pullType == "hls") { url = QString("http://%1:%2/hls%3/%4.m3u8").arg(ip).arg(port).arg(path).arg(name); } else if (pullType == "flv") { url = QString("http://%1:%2/hdl%3/%4.flv").arg(ip).arg(port).arg(path).arg(name); } else if (pullType == "ws-flv") { url = QString("ws://%1:%2/jessica%3/%4.flv").arg(ip).arg(port).arg(path).arg(name); } else if (pullType == "webrtc") { url = QString("webrtc://%1:%2/webrtc/play%3/%4").arg(ip).arg(port).arg(path).arg(name); } } return url;}
0 阅读:0

科技飞扬不打烊

简介:感谢大家的关注