Qt/C++监控推流设备推流/延迟极低/实时性极高/rtsp/rtmp推流/hls/flv/webrtc拉流/调整分辨率降低带宽

这篇具有很好参考价值的文章主要介绍了Qt/C++监控推流设备推流/延迟极低/实时性极高/rtsp/rtmp推流/hls/flv/webrtc拉流/调整分辨率降低带宽。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、前言

算下来这个推流的项目作品写了有四年多了,最初第一个版本只有文件点播的功能,用的纯QTcpSocket通信实现,属于比较简单的功能。由于文件点播只支持文件形式的推流,不支持网络流或者本地设备采集,所以迫切需要打破这个瓶颈,而后加入核心的网络推流功能,这也是本项目的核心功能,不仅支持各种各样的流媒体服务,推流这块支持文件、网络音视频流、本地设备采集、本地桌面采集。自定义各种参数,视音频同步推流等,不断的迭代和完善。也不是一开始就具备这些功能的,而是随着视频播放组件的保存视频功能不断完善后改进的,因为推流其实就是保存功能,只不过保存到一个推流地址就行,然后推流的格式换下,所以是和保存功能完全公用的。整个推流组件是负责管理一堆的保存类,拿到当前推流状态,当前音视频是否存在以及是否编码推流的状态显示到表格中。

一开始也是没有网页预览的功能,后面用户对这块要求比较强烈,都是希望推流后能够通过一个简单的方式,能够直接网页中预览,有多少个通道就显示多少个通道,这样可以判断推流是否成功,不然要一个个手动的打开播放器输入播放地址验证,很麻烦。而且推流的主要应用场景就是希望推流后给网页或者手机app拉流显示。直接网页预览还可以对比实时性,用户对两个指标特别敏感,一个是延迟,一个是流畅。所以一直在这块功能精心打磨,尽量做到极致。

公众号:Qt实战,各种开源作品、经验整理、项目实战技巧,专注Qt/C++软件开发,视频监控、物联网、工业控制、嵌入式软件、国产化系统应用软件开发。

公众号:Qt入门和进阶,专门介绍Qt/C++相关知识点学习,帮助Qt开发者更好的深入学习Qt。多位Qt元婴期大神,一步步带你从入门到进阶,走上财务自由之路。*

二、效果图

qt+ffmpeg低延迟,Qt/C++项目作品,qt,c++,webrtc

qt+ffmpeg低延迟,Qt/C++项目作品,qt,c++,webrtc
qt+ffmpeg低延迟,Qt/C++项目作品,qt,c++,webrtc
qt+ffmpeg低延迟,Qt/C++项目作品,qt,c++,webrtc文章来源地址https://www.toymoban.com/news/detail-850253.html

三、体验地址

  1. 国内站点:https://gitee.com/feiyangqingyun
  2. 国际站点:https://github.com/feiyangqingyun
  3. 个人作品:https://blog.csdn.net/feiyangqingyun/article/details/97565652
  4. 体验地址:https://pan.baidu.com/s/1d7TH_GEYl5nOecuNlWJJ7g 提取码:01jf 文件名:bin_video_push。
  5. 视频主页:https://space.bilibili.com/687803542

四、功能特点

  1. 支持各种本地音视频文件和网络音视频文件,格式包括mp3、aac、wav、wma、mp4、mkv、rmvb、wmv、mpg、flv、asf等。
  2. 支持各种网络音视频流,网络摄像头,协议包括rtsp、rtmp、http等。
  3. 支持本地摄像头设备推流,可指定分辨率、帧率、格式等。
  4. 支持本地桌面采集推流,可指定屏幕索引、采集区域、起始坐标、帧率等,也支持指定窗口标题进行采集。
  5. 可实时切换预览视频文件,可切换音视频文件播放进度,切换到哪里就推流到哪里。预览过程中可以切换静音状态和暂停推流。
  6. 可指定重新编码推流,任意源头格式可选强转264或265格式。
  7. 可转换分辨率推流,设置等比例缩放或者指定分辨率进行转换。
  8. 推流的清晰度、质量、码率都可调,可以节约网络带宽和拉流端的压力。
  9. 音视频文件自动循环不间断推流。
  10. 音视频流有自动掉线重连机制,重连成功自动继续推流。
  11. 支持各种流媒体服务程序,包括但不限于mediamtx、ZLMediaKit、srs、LiveQing、nginx-rtmp、EasyDarwin、ABLMediaServer。
  12. 通过配置文件自动加载对应流媒体程序的协议和端口,自动生成推流地址和各种协议的拉流地址。可以通过配置文件自己增加流媒体程序。
  13. 可选rtmp、rtmp格式推流,推流成功后,支持多种格式拉流,包括但不限于rtsp、rtmp、hls、flv、ws-flv、webrtc等。
  14. 在软件上推流成功后,可以直接单击网页预览,实时预览推流后拉流的画面,多画面网页展示。
  15. 软件界面上可单击对应按钮,动态添加文件和目录,可手动输入地址。
  16. 推拉流实时性极高,延迟极低,延迟时间大概在100ms左右。
  17. 极低CPU资源占用,4路主码流推流只需要占用0.2%CPU。理论上常规普通PC机器推100路毫无压力,主要性能瓶颈在网络。
  18. 可以推流到外网服务器,然后通过手机、电脑、平板等设备播放对应的视频流。
  19. 每路推流都可以手动指定唯一标识符(方便拉流/用户无需记忆复杂的地址),没有指定则按照策略随机生成hash值。也支持自动按照指定标识后面加数字的方式递增命名。比如设置标识为字母v,策略为标识递增,则每添加一个对应的推流码命名依次是v1、v2、v3等。
  20. 根据推流协议自动转码格式,默认策略按照选择的推流协议,比如rtsp支持265而rtmp不支持,如果是265的文件而选择rtmp推流,则自动转码成264格式再推流。
  21. 音视频同步推流,在拉流和采集的时候就会自动处理好同步,同步后的数据再推流。
  22. 表格中实时显示每一路推流的分辨率和音视频数据状态,灰色表示没有输入流,黑色表示没有输出流,绿色表示原数据推流,红色表示转码后的数据推流。
  23. 自动重连视频源,自动重连流媒体服务器,保证启动后,推流地址和打开地址都实时重连,只要恢复后立即连上继续采集和推流。
  24. 根据不同的流媒体服务器类型,自动生成对应的rtsp、rtmp、hls、flv、ws-flv、webrtc拉流地址,用户可以直接复制该地址到播放器或者网页中预览查看。
  25. 添加的推流地址等信息自动存储到文件,可以手动打开进行修改,默认启动后自动加载历史记录。
  26. 可以指定生成的网页文件保存位置,方便作为网站网页发布,可以直接在浏览器中输入网址进行访问,发布后可以直接在局域网其他设备比如手机或者电脑打开对应网址访问。
  27. 可选是否开机启动、后台运行等。网络推流添加的rtsp地址可勾选是否隐藏地址中的用户信息。
  28. 自带设备推流模块,自动识别本地设备,包括本地的摄像头和桌面,可以手动选择不同的是视频和音频采集设备进行推流。
  29. 自带文件点播模块,添加文件后用户可以拉取地址点播,用户端可以任意切换播放进度。支持各种浏览器(谷歌chromium、微软edge、火狐firefox等)、各种播放器(vlc、mpv、ffplay、potplayer、mpchc等)打开请求。
  30. 文件点播模块实时统计显示每个文件对应的访问数量、总访问数量、不同IP地址访问数量。
  31. 文件点播模块采用纯QTcpSocket通信,不依赖流媒体服务程序,核心源码不到500行,注释详细,功能完整。
  32. 支持任意Qt版本(Qt4、Qt5、Qt6),支持任意系统(windows、linux、macos、android、嵌入式linux等)。

五、相关代码

void NetPushClient::record()
{
    if (ffmpegSave) {
        //取出推流码
        QString flag = pushUrl.split("/").last();
        //文件名不能包含特殊字符/需要替换成固定字母
        QString pattern("[\\\\/:|*?\"<>]|[cC][oO][mM][1-9]|[lL][pP][tT][1-9]|[cC][oO][nM]|[pP][rR][nN]|[aA][uU][xX]|[nN][uU][lL]");
#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0))
        QRegularExpression rx(pattern);
#else
        QRegExp rx(pattern);
#endif
        flag.replace(rx, "X");

        //文件名加上时间结尾
        QString path = QString("%1/video/%2").arg(qApp->applicationDirPath()).arg(QDATE);
        QString name = QString("%1/%2_%3.mp4").arg(path).arg(flag).arg(STRDATETIME);

        //目录不存在则新建
        QDir dir(path);
        if (!dir.exists()) {
            dir.mkpath(path);
        }

        //先停止再打开重新录制
        ffmpegSave->stop();
        ffmpegSave->open(name);
        recordTime = QDateTime::currentDateTime();
    }
}

void NetPushClient::receivePlayStart(int time)
{
    //演示添加OSD后推流
#ifdef betaversion
    int height = ffmpegThread->getVideoHeight();
    QList<OsdInfo> osds = WidgetHelper::getTestOsd(height);
    ffmpegThread->setOsdInfo(osds);
#endif

    //打开后才能启动录像
    ffmpegThread->recordStart(pushUrl);

    //推流以外还单独存储
    if (!ffmpegSave && recordType > 0) {
        //源头保存没成功就不用继续
        FFmpegSave *saveFile = ffmpegThread->getSaveFile();
        if (!saveFile->getIsOk()) {
            return;
        }

        ffmpegSave = new FFmpegSave(this);
        //重新编码过的则取视频保存类的对象
        AVStream *videoStreamIn = saveFile->getVideoEncode() ? saveFile->getVideoStream() : ffmpegThread->getVideoStream();
        AVStream *audioStreamIn = saveFile->getAudioEncode() ? saveFile->getAudioStream() : ffmpegThread->getAudioStream();
        ffmpegSave->setSavePara(ffmpegThread->getMediaType(), SaveVideoType_Mp4, videoStreamIn, audioStreamIn);
        this->record();
        timerRecord->start();
    }
}

void NetPushClient::receivePacket(AVPacket *packet)
{
    if (ffmpegSave && ffmpegSave->getIsOk()) {
        ffmpegSave->writePacket2(packet);
    }

    FFmpegHelper::freePacket(packet);
}

void NetPushClient::recorderStateChanged(const RecorderState &state, const QString &file)
{
    int width = 0;
    int height = 0;
    int videoStatus = 0;
    int audioStatus = 0;
    if (ffmpegThread) {
        width = ffmpegThread->getVideoWidth();
        height = ffmpegThread->getVideoHeight();
        FFmpegSave *saveFile = ffmpegThread->getSaveFile();
        if (saveFile->getIsOk()) {
            if (saveFile->getVideoIndexIn() >= 0) {
                if (saveFile->getVideoIndexOut() >= 0) {
                    videoStatus = (saveFile->getVideoEncode() ? 3 : 2);
                } else {
                    videoStatus = 1;
                }
            }
            if (saveFile->getAudioIndexIn() >= 0) {
                if (saveFile->getAudioIndexOut() >= 0) {
                    audioStatus = (saveFile->getAudioEncode() ? 3 : 2);
                } else {
                    audioStatus = 1;
                }
            }
        }
    }

    //只有处于录制中才表示正常推流开始
    bool start = (state == RecorderState_Recording);
    emit pushStart(mediaUrl, width, height, videoStatus, audioStatus, start);
}

void NetPushClient::receiveSaveStart()
{
    emit pushChanged(mediaUrl, 0);
}

void NetPushClient::receiveSaveFinsh()
{
    emit pushChanged(mediaUrl, 1);
}

void NetPushClient::receiveSaveError(int error)
{
    emit pushChanged(mediaUrl, 2);
}

void NetPushClient::setMediaUrl(const QString &mediaUrl)
{
    this->mediaUrl = mediaUrl;
}

void NetPushClient::setPushUrl(const QString &pushUrl)
{
    this->pushUrl = pushUrl;
}

void NetPushClient::start()
{
    if (ffmpegThread || mediaUrl.isEmpty() || pushUrl.isEmpty()) {
        return;
    }

    //实例化视频采集线程
    ffmpegThread = new FFmpegThread;
    //关联播放开始信号用来启动推流
    connect(ffmpegThread, SIGNAL(receivePlayStart(int)), this, SLOT(receivePlayStart(int)));
    //关联录制信号变化用来判断是否推流成功
    connect(ffmpegThread, SIGNAL(recorderStateChanged(RecorderState, QString)), this, SLOT(recorderStateChanged(RecorderState, QString)));
    //设置播放地址
    ffmpegThread->setMediaUrl(mediaUrl);
    //设置解码内核
    ffmpegThread->setVideoCore(VideoCore_FFmpeg);

    //设置视频模式
#ifdef openglx
    ffmpegThread->setVideoMode(VideoMode_Opengl);
#else
    ffmpegThread->setVideoMode(VideoMode_Painter);
#endif

    //设置通信协议(如果是rtsp视频流建议设置tcp)
    //ffmpegThread->setTransport("tcp");
    //设置硬解码(和推流无关/只是为了加速显示/推流只和硬编码有关)
    //ffmpegThread->setHardware("dxva2");
    //设置缓存大小(如果分辨率帧率码流很大需要自行加大缓存)
    ffmpegThread->setCaching(8192000);
    //设置解码策略(推流的地址再拉流建议开启最快速度)
    //ffmpegThread->setDecodeType(DecodeType_Fastest);

    //设置读取超时时间超时后会自动重连
    ffmpegThread->setReadTimeout(5 * 1000);
    //设置连接超时时间(0表示一直连)
    ffmpegThread->setConnectTimeout(0);
    //设置重复播放相当于循环推流
    ffmpegThread->setPlayRepeat(true);
    //设置默认不播放音频(界面上切换到哪一路就开启)
    ffmpegThread->setPlayAudio(false);
    //设置默认不预览视频(界面上切换到哪一路就开启)
    ffmpegThread->setPushPreview(false);

    //设置保存视频类将数据包信号发出来用于保存文件
    FFmpegSave *saveFile = ffmpegThread->getSaveFile();
    saveFile->setProperty("checkB", true);
    saveFile->setSendPacket(recordType > 0, false);
    connect(saveFile, SIGNAL(receivePacket(AVPacket *)), this, SLOT(receivePacket(AVPacket *)));
    connect(saveFile, SIGNAL(receiveSaveStart()), this, SLOT(receiveSaveStart()));
    connect(saveFile, SIGNAL(receiveSaveFinsh()), this, SLOT(receiveSaveFinsh()));
    connect(saveFile, SIGNAL(receiveSaveError(int)), this, SLOT(receiveSaveError(int)));

    //如果是本地设备或者桌面录屏要取出其他参数
    VideoHelper::initVideoPara(ffmpegThread, mediaUrl, encodeVideoScale);

    //设置视频编码格式/视频压缩比率/视频缩放比例
    ffmpegThread->setEncodeVideo((EncodeVideo)encodeVideo);
    ffmpegThread->setEncodeVideoRatio(encodeVideoRatio);
    ffmpegThread->setEncodeVideoScale(encodeVideoScale);

    //启动播放
    ffmpegThread->play();
}

void NetPushClient::stop()
{
    //停止推流和采集并彻底释放对象
    if (ffmpegThread) {
        ffmpegThread->recordStop();
        ffmpegThread->stop();
        ffmpegThread->deleteLater();
        ffmpegThread = NULL;
    }

    //停止录制
    if (ffmpegSave) {
        timerRecord->stop();
        ffmpegSave->stop();
        ffmpegSave->deleteLater();
        ffmpegSave = NULL;
    }
}

到了这里,关于Qt/C++监控推流设备推流/延迟极低/实时性极高/rtsp/rtmp推流/hls/flv/webrtc拉流/调整分辨率降低带宽的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • RocketMQ 5.0 无状态实时性消费详解

    作者:绍舒 RocketMQ 5.0 版本引入了 Proxy 模块、无状态 pop 消费机制和 gRPC 协议等创新功能,同时还推出了一种全新的客户端类型:SimpleConsumer。 SimpleConsumer 客户端采用了无状态的 pop 机制,彻底解决了在客户端发布消息、上下线时可能出现的负载均衡问题。然而,这种新机制也

    2024年02月15日
    浏览(34)
  • ES的近实时性是什么意思?原理是什么?

    ES 的\\\"近实时\\\"(near real-time)是指当你往 ES 索引(Index)中插入、更新或删除文档时,这些变更几乎立刻就对用户可见和可查询。然而,它并不是真正的实时,因为 ES 采用了一些优化和缓冲机制,以提高性能和吞吐量。 🟥 具体来说,ES的近实时性质包括以下几个方面: 索引

    2024年02月07日
    浏览(36)
  • 边缘计算的挑战与机遇:实时性、安全性与创新驱动

    摘要: 随着物联网、人工智能和大数据的快速发展,边缘计算作为一种新兴的计算模型,已经成为满足实时性和本地处理需求的解决方案。然而,边缘计算也面临着数据安全与隐私保护、网络稳定性等挑战。本文将探讨边缘计算所面临的挑战,并阐述其所带来的机遇,以及我

    2024年01月18日
    浏览(33)
  • 边缘计算技术的双面刃:深度剖析安全、稳定挑战及实时性、成本优势

    在数字化时代的前沿,边缘计算作为一项颠覆性技术,正以其独特的分布式架构和强大的本地处理能力深刻改变着数据处理与分析的方式。然而,这一技术革新也带来了复杂的安全防护需求、网络稳定性问题,同时也为各行业带来了前所未有的实时响应能力和经济效率提升。

    2024年01月22日
    浏览(45)
  • 大数据时代的新命题:谁在满足中国企业的实时性需求?

    站在今天的十字路口,国内数据库行业已然呈现出新的拐点。而在各种新趋势和新技术的到来,镜舟也正在基于自己开源社区加商业化闭环,共同加速数据库行业的发展和规范。 作者|思杭  编辑|皮爷  出品|产业家  从540亿元到1286亿元——这是中国通信标准化协会大数据技

    2024年01月24日
    浏览(42)
  • [工业互联-14]:机器人操作系统ROS与ROS2是如何提升实时性的?

    目录 第1章 简介 第2章 历史 第3章 特点 (1)点对点设计 (2)不依赖编程语言 (3)精简与集成 (4)便于测试 (5)开源 (6)强大的库及社区: (7)通道: (8)工具: (9)强大的库: (10)生态系统: 第4章 ROS的系统架构 第5章 ROS与Linux 5.1 ROS能安装在Linux之上吗? 5.

    2024年02月14日
    浏览(38)
  • ElasticSearch第十八讲 ES-Master节点职责和ES是如何做到数据实时性的

    由主节点负责ping 所有其他节点,判断是否有节点已经挂掉 创建或删除索引 决定分片在节点之间的分配 稳定的主节点对集群的健康是非常重要的。虽然主节点也可以协调节点,路由搜索和从客户端新增数据到数据节点,但最好不要使用这些专用的主节点。一个重要的原则是

    2024年02月13日
    浏览(35)
  • Qt编写网络摄像头推流(4路1080P主码流只占用0.2%CPU/极低延时极速响应)

    将从网络摄像头拉流过来的视频流重新推流出去,是目前一个很常规的做法,尤其是推流到流媒体服务中心,这样流媒体服务中心就把散落在各个区域的监控摄像头集中起来统一管理,同时提供对外一致的访问接口。很多时候不仅仅是几个摄像头,很可能是几百个上千个,所

    2024年02月04日
    浏览(33)
  • 最新技术整理3款开源免费直播推流工具,实现实时视频推流、视频拉流,目标端可以是服务器、云平台、移动设备等(附源码)

    最新技术整理3款开源免费直播推流工具,实现实时视频推流、视频拉流,目标端可以是服务器、云平台、移动设备等(附源码)。 什么是推流? 视频推流是指将实时的视频数据从一个源端发送到一个或多个目标端的过程。推流的源端可以是摄像头、采集卡等设备,而目标端

    2024年02月04日
    浏览(58)
  • Qt编写视频监控系统78-视频推流到流媒体服务器

    视频推流作为独立的模块,目前并没有集成到视频监控系统中,目前是可以搭配监控系统一起使用,一般是将添加好的摄像头通道视频流地址打开后,读取视频流重新推到流媒体服务器,然后第三方可以从流媒体服务器拉取视频流地址,相当于订阅分发,可以多路,按照需要

    2024年02月08日
    浏览(34)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包