流媒体服务器SRS的搭建及QT下RTMP推流客户端的编写
一、前言
目前市面上有很多开源的流媒体服务器解决方案,常见的有SRS、EasyDarwin、ZLMediaKit和Monibuca。这几种的对比如下:
(本图来源:https://www.ngui.cc/zz/1781086.html?action=onClick)
二、SRS的介绍
SRS(Simple Real-time Server)是一个开源的流媒体服务器,它支持RTMP、HLS、HTTP-FLV等多种流媒体协议。SRS提供了丰富的功能,包括推流、拉流、转码、录制、转发等,并且具有高性能、低延迟的特点。使用SRS可以搭建自己的流媒体服务器,实现音视频的实时传输和播放。你可以通过SRS推送音视频流到服务器,也可以从SRS服务器拉取音视频流进行播放或者转发给其他客户端。SRS支持多线程推拉流,可以利用多核处理器的优势,提高处理能力和并发性能。在使用多线程推拉流时,需要注意线程同步和数据共享的问题。
三、SRS的搭建
3.1 下载
虚拟机环境:CentOS 7 64位
SRS下载地址:https://gitcode.net/mirrors/ossrs
(SRS 4.0以上版本,4.0版本以下不支持GB/28181协议)
3.2 配置、编译、运行
下载后,解压到CentOS 7目录下(注意不要在共享目录下操作不然会出现ln软链接错误问题)。
1)执行配置命令:
cd srs/trunk
./configure
2)执行编译命令:
make
3)执行运行命令:
./etc/init.d/srs start
配置文件位置在:conf/srs.conf,可修改配置文件内容:
4)检查srs服务启动是否正常:
ps -ef | grep srs
3.3 网页登录
(默认端口8080) 红框中的为推流地址
四、QT下推流客户端
本客户端基于我的博客:https://blog.csdn.net/linyibin_123/article/details/132107948 开发的播放器下新增RTMP推流。
播放器可以支持软硬解码,截图、录像等功能,详细功能看该博客。本客户端支持读取文件解码后推流,也支持拉取网络流解码后进行推流。推流地址为前面搭建的RTMP流媒体服务器,推流成功后,通过VLC播放器从RTMP服务器上拉流下来播放。
4.1 读取本地文件解码后推流:
4.2 拉取网络流解码后推流:
4.3 相关代码:
初始化推流:
bool ctFFmpeg::initPushStream()
{if(m_bEnablePush){int nRet = avformat_alloc_output_context2(&m_pOfmtCtx, nullptr, "flv", m_sRtmpServerAddr.toUtf8().data());if(!m_pOfmtCtx || nRet < 0){MY_DEBUG << "avformat_alloc_output_context2 failed";return false;}m_pOvCodec = avcodec_find_encoder(AV_CODEC_ID_H264);if(!m_pOvCodec){MY_DEBUG << "avcodec_find_encoder failed";return false;}m_pOvCodecCtx = avcodec_alloc_context3(m_pOvCodec);if(!m_pOvCodecCtx){MY_DEBUG << "avcodec_alloc_context3 failed";return false;}m_pOvCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;m_pOvCodecCtx->width = m_nVideoW;m_pOvCodecCtx->height = m_nVideoH;m_pOvCodecCtx->time_base.num = 1;m_pOvCodecCtx->time_base.den = 25;m_pOvCodecCtx->bit_rate = 300000;m_pOvCodecCtx->gop_size = 250;//Some formats want stream headers to be separate.if (m_pOfmtCtx->oformat->flags & AVFMT_GLOBALHEADER)m_pOvCodecCtx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;m_pOvCodecCtx->qmin = 10;m_pOvCodecCtx->qmax = 51;m_pOvCodecCtx->max_b_frames = 0;AVDictionary *param = 0;av_dict_set(¶m, "preset", "fast", 0);av_dict_set(¶m, "tune", "zerolatency", 0);if (avcodec_open2(m_pOvCodecCtx, m_pOvCodec, ¶m) < 0){MY_DEBUG << "avcodec_open2 failed.";return false;}m_pVideoSt = avformat_new_stream(m_pOfmtCtx, m_pOvCodec);if (nullptr == m_pVideoSt){MY_DEBUG << "avformat_new_stream failed.";return false;}m_pVideoSt->time_base.num = 1;m_pVideoSt->time_base.den = 30;avcodec_parameters_from_context(m_pVideoSt->codecpar, m_pOvCodecCtx);av_dump_format(m_pOfmtCtx, 0, m_sRtmpServerAddr.toLatin1().data(), 1);//Open output URLif (!(m_pOfmtCtx->oformat->flags & AVFMT_NOFILE)){nRet = avio_open(&m_pOfmtCtx->pb, m_sRtmpServerAddr.toLatin1().data(), AVIO_FLAG_READ_WRITE);if (nRet < 0){MY_DEBUG << "avio_open failed. url:" << m_sRtmpServerAddr;return false;}}m_pOfmtCtx->video_codec_id = m_pOfmtCtx->oformat->video_codec;nRet = avformat_write_header(m_pOfmtCtx, NULL);if (nRet < 0){MY_DEBUG << "avformat_write_header failed. nRet:" << nRet;return false;}m_pOutFrameYUV = av_frame_alloc();int nBufferSize = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, m_nVideoW, m_nVideoH, 1);m_pOutDstBuffer = (unsigned char*)av_malloc(nBufferSize);if (!m_pOutDstBuffer){return false;}MY_DEBUG << "storeAvFrame 111";nRet = av_image_fill_arrays(m_pOutFrameYUV->data, m_pOutFrameYUV->linesize,m_pOutDstBuffer, AV_PIX_FMT_YUV420P, m_nVideoW, m_nVideoH, 1);if(nRet < 0){return false;}m_pImgConvertCtx = sws_getContext(m_pVideoCodecCxt->width, m_pVideoCodecCxt->height,m_pVideoCodecCxt->pix_fmt, m_pVideoCodecCxt->width,m_pVideoCodecCxt->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);m_nStartTime = av_gettime();m_nFramecnt = 0;return true;}return false;
}
推流过程:
void ctFFmpeg::pushStream()
{if(m_bEnablePush && m_bSupportPush && m_pYuvFrame){sws_scale(m_pImgConvertCtx, (const uint8_t* const*)m_pYuvFrame->data, m_pYuvFrame->linesize, 0,m_pVideoCodecCxt->height, m_pOutFrameYUV->data, m_pOutFrameYUV->linesize);m_pOutFrameYUV->width = m_pYuvFrame->width;m_pOutFrameYUV->height = m_pYuvFrame->height;m_pOutFrameYUV->format = AV_PIX_FMT_YUV420P;m_encPkt.data = NULL;m_encPkt.size = 0;av_init_packet(&m_encPkt);int nRet = avcodec_send_frame(m_pOvCodecCtx, m_pOutFrameYUV);while(nRet >= 0){nRet = avcodec_receive_packet(m_pOvCodecCtx, &m_encPkt);if (nRet == AVERROR(EAGAIN) || nRet == AVERROR_EOF || nRet < 0){//MY_DEBUG << "avcodec_receive_packet nRet == AVERROR(EAGAIN) || nRet == AVERROR_EOF";break;}m_nFramecnt++;m_encPkt.stream_index = m_pVideoSt->index;//av_packet_rescale_ts(&pkt, m_pOvCodecCtx->time_base, m_pVideoSt->time_base);AVRational time_base = m_pOfmtCtx->streams[0]->time_base;//{ 1, 1000 };AVRational time_base_q = {1, AV_TIME_BASE};AVRational rFramerate1 = m_pAVFmtCxt->streams[0]->r_frame_rate;int64_t nCalcDuration = (double)(AV_TIME_BASE)*(1 / av_q2d(rFramerate1)); //内部时间戳//Parametersm_encPkt.pts = av_rescale_q(m_nFramecnt*nCalcDuration, time_base_q, time_base);m_encPkt.dts = m_encPkt.pts;m_encPkt.duration = av_rescale_q(nCalcDuration, time_base_q, time_base);m_encPkt.pos = -1;//MY_DEBUG << "m_encPkt.pts:" << m_encPkt.pts;int64_t pts_time = av_rescale_q(m_encPkt.pts, time_base, time_base_q);int64_t now_time = av_gettime() - m_nStartTime;if ((pts_time > now_time))av_usleep(pts_time - now_time);nRet = av_interleaved_write_frame(m_pOfmtCtx, &m_encPkt);if(nRet < 0){MY_DEBUG << "av_interleaved_write_frame fail nRet:" << nRet;}av_packet_unref(&m_encPkt);}}
}
结束推流:
void ctFFmpeg::endPushStream()
{if(m_bEnablePush && m_bSupportPush){av_write_trailer(m_pOfmtCtx);if (m_pOfmtCtx && !(m_pOfmtCtx->oformat->flags & AVFMT_NOFILE))avio_close(m_pOfmtCtx->pb);avformat_free_context(m_pOfmtCtx);if(nullptr != m_pOvCodecCtx){avcodec_free_context(&m_pOvCodecCtx);m_pOvCodecCtx = nullptr;}if(m_pOutDstBuffer)av_free(m_pOutDstBuffer);}
}
五、客户端下载:
https://download.csdn.net/download/linyibin_123/88237527
相关文章:

流媒体服务器SRS的搭建及QT下RTMP推流客户端的编写
一、前言 目前市面上有很多开源的流媒体服务器解决方案,常见的有SRS、EasyDarwin、ZLMediaKit和Monibuca。这几种的对比如下: (本图来源:https://www.ngui.cc/zz/1781086.html?actiononClick) 二、SRS的介绍 SRS&am…...
Effective C++条款11——在operator=中处理“自我赋值”(构造/析构/赋值运算)
“自我赋值”发生在对象被赋值给自己时: class Widget {}; Widget w; // ... w w; // 赋值给自己 这看起来有点愚蠢,但它合法,所以不要认定客户绝不会那么做。此外赋值动作并不总是那么可被一眼辨识出来,例如: a[i] a[j]; …...
可视化绘图技巧100篇基础篇(八)-气泡图(一)
目录 前言 适用场景 图例 绘图工具及代码实现 EXCEL 1、单轴气泡图...
Elasticsearch查询之Disjunction Max Query
前言 Disjunction Max Query 又称最佳 best_fields 匹配策略,用来优化当查询关键词出现在多个字段中,以单个字段的最大评分作为文档的最终评分,从而使得匹配结果更加合理 写入数据 如下的两条例子数据: docId: 1 title: java …...
Lock wait timeout exceeded; try restarting transaction的错误
文章目录 一、异常发现二、异常定位1、锁表语句确认2、实际场景排查三、解决思路1、本次解决方式2、其他场景解决思路扩展1、【治标方法】innodb_lock_wait_timeout 锁定等待时间改大2、【治标方法】事务信息查询3、【治标方法】如果杀掉线程依然不能解决,可以查找执行线程耗时…...

ShardingSphere01-docker环境安装
使用docker安装数据库是一个非常好的选择,后续的读写分离、数据分片等功能的数据库都是由docker创建。 一、安装准备 1、前提条件 Docker可以运行在Windows、Mac、CentOS、Ubuntu等操作系统上 Docker支持以下的CentOS版本: CentOS 7 (64-bit)CentOS …...

Java代码审计13之URLDNS链
文章目录 1、简介urldns链2、hashmap与url类的分析2.1、Hashmap类readObject方法的跟进2.2、URL类hashcode方法的跟进2.3、InetAddress类的getByName方法 3、整个链路的分析3.1、整理上述的思路3.2、一些疑问的测试3.3、hashmap的put方法分析3.4、反射3.5、整个代码 4、补充说明…...

区间预测 | MATLAB实现QRBiGRU双向门控循环单元分位数回归时间序列区间预测
区间预测 | MATLAB实现QRBiGRU双向门控循环单元分位数回归时间序列区间预测 目录 区间预测 | MATLAB实现QRBiGRU双向门控循环单元分位数回归时间序列区间预测效果一览基本介绍模型描述程序设计参考资料 效果一览 基本介绍 MATLAB实现QRBiGRU双向门控循环单元分位数回归时间序列…...

Python面向对象植物大战僵尸
先来一波效果图 来看看如何设计游戏架构 import sysimport pygameclass BaseSprite(pygame.sprite.Sprite):def __init__(self, name):super().__init__()self.image pygame.image.load(name)self.rect self.image.get_rect()class AnimateSprite(BaseSprite):def __init__(…...
大屏模板,增加自适应(包含websocket)
1、简单的Node服务端 const WebSocket require(ws);// 创建 WebSocket 服务器 const wss new WebSocket.Server({ port: 8888 });const getHeader (protocol) > {const protocolArr protocol.split(,)const headers {};for (let i 0; i < protocolArr.length; i …...

电商系统架构设计系列(九):如何规划和设计分库分表?
上篇文章中,我给你留了一个思考题:分库分表该如何设计? 今天这篇文章,我们来聊一下如何规划和设计分库分表,以及要考虑哪些问题。 引言 当要解决海量数据的问题,就必须要用到分布式的存储集群了ÿ…...

从Web 2.0到Web 3.0,互联网有哪些变革?
文章目录 Web 2.0时代:用户参与和社交互动Web 3.0时代:语义化和智能化影响和展望 🎉欢迎来到Java学习路线专栏~从Web 2.0到Web 3.0,互联网有哪些变革? ☆* o(≧▽≦)o *☆嗨~我是IT陈寒🍹✨博客主页&#x…...

QT中资源文件resourcefile的使用,使用API完成页面布局
QT中资源文件resourcefile的使用 之前添加图标的方法使用资源文件的方法创建资源文件资源文件添加前缀资源文件添加资源使用资源文件中的资源 使用API完成布局使用QHBoxLayout完成水平布局使用QVBoxLayout完成垂直布局使用QGridLayout完成网格布局 在Qt中引入资源文件好处在于他…...

2337. 移动片段得到字符串
题目描述: 给你两个字符串 start 和 target ,长度均为 n 。每个字符串 仅 由字符 ‘L’、‘R’ 和 ‘_’ 组成,其中: 字符 ‘L’ 和 ‘R’ 表示片段,其中片段 ‘L’ 只有在其左侧直接存在一个 空位 时才能向 左 移动&a…...

Java并发编程第5讲——volatile关键字(万字详解)
volatile关键字大家并不陌生,尤其是在面试的时候,它被称为“轻量级的synchronized”。但是它并不容易完全被正确的理解,以至于很多程序员都不习惯去用它,处理并发问题的时候一律使用“万能”的sychronized来解决,然而如…...
6.小程序api分类
事件监听 以on开头,监听某个事件触发,例如:wx.WindowResize事件 同步 以Sync结尾的是同步,可以通过函数返回值直接获取,例如:wx.setStorageSync 异步 需要通过函数接收调用结果,例如&#…...

什么是PPS和TOD时序?授时防护设备是什么?
介绍 PPS和TOD PPS和TOD是两种用于精确时间同步的技术,它们在许多领域都有广泛的应用,总的来说,PPS和TOD被广泛应用于各种需要高度精确时间同步的领域,包括通信、测量、测试、系统集成和计算机网络等。 一、PPS PPS(…...

推荐一款好用的开源视频播放器(免费无广告)
mpv是一个自由开源的媒体播放器,它支持多种音频和视频格式,并且具有高度可定制性。mpv的设计理念是简洁、高效和功能强大。 软件特点: 1. 开源、跨平台。可以在Windows\Linux\MacOS\BSD等系统上使用,完全免费无广告。Windows版解压…...

STM32 CubeMX (第三步Freertos中断管理和软件定时)
STM32 CubeMX STM32 CubeMX (第三步Freertos中断管理和软件定时) STM32 CubeMX一、STM32 CubeMX设置时钟配置HAL时基选择TIM1(不要选择滴答定时器;滴答定时器留给OS系统做时基)使用STM32 CubeMX 库,配置Fre…...

Java虚拟机(JVM):堆溢出
一、概念 Java堆溢出(Java Heap Overflow)是指在Java程序中,当创建对象时,无法分配足够的内存空间来存储对象,导致堆内存溢出的情况。 Java堆是Java虚拟机中用于存储对象的一块内存区域。当程序创建对象时,…...
STM32+rt-thread判断是否联网
一、根据NETDEV_FLAG_INTERNET_UP位判断 static bool is_conncected(void) {struct netdev *dev RT_NULL;dev netdev_get_first_by_flags(NETDEV_FLAG_INTERNET_UP);if (dev RT_NULL){printf("wait netdev internet up...");return false;}else{printf("loc…...

04-初识css
一、css样式引入 1.1.内部样式 <div style"width: 100px;"></div>1.2.外部样式 1.2.1.外部样式1 <style>.aa {width: 100px;} </style> <div class"aa"></div>1.2.2.外部样式2 <!-- rel内表面引入的是style样…...
HTML前端开发:JavaScript 常用事件详解
作为前端开发的核心,JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例: 1. onclick - 点击事件 当元素被单击时触发(左键点击) button.onclick function() {alert("按钮被点击了!&…...
今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存
文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...

九天毕昇深度学习平台 | 如何安装库?
pip install 库名 -i https://pypi.tuna.tsinghua.edu.cn/simple --user 举个例子: 报错 ModuleNotFoundError: No module named torch 那么我需要安装 torch pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple --user pip install 库名&#x…...

安宝特案例丨Vuzix AR智能眼镜集成专业软件,助力卢森堡医院药房转型,赢得辉瑞创新奖
在Vuzix M400 AR智能眼镜的助力下,卢森堡罗伯特舒曼医院(the Robert Schuman Hospitals, HRS)凭借在无菌制剂生产流程中引入增强现实技术(AR)创新项目,荣获了2024年6月7日由卢森堡医院药剂师协会࿰…...
站群服务器的应用场景都有哪些?
站群服务器主要是为了多个网站的托管和管理所设计的,可以通过集中管理和高效资源的分配,来支持多个独立的网站同时运行,让每一个网站都可以分配到独立的IP地址,避免出现IP关联的风险,用户还可以通过控制面板进行管理功…...
小木的算法日记-多叉树的递归/层序遍历
🌲 从二叉树到森林:一文彻底搞懂多叉树遍历的艺术 🚀 引言 你好,未来的算法大神! 在数据结构的世界里,“树”无疑是最核心、最迷人的概念之一。我们中的大多数人都是从 二叉树 开始入门的,它…...

CSS3相关知识点
CSS3相关知识点 CSS3私有前缀私有前缀私有前缀存在的意义常见浏览器的私有前缀 CSS3基本语法CSS3 新增长度单位CSS3 新增颜色设置方式CSS3 新增选择器CSS3 新增盒模型相关属性box-sizing 怪异盒模型resize调整盒子大小box-shadow 盒子阴影opacity 不透明度 CSS3 新增背景属性ba…...
验证redis数据结构
一、功能验证 1.验证redis的数据结构(如字符串、列表、哈希、集合、有序集合等)是否按照预期工作。 2、常见的数据结构验证方法: ①字符串(string) 测试基本操作 set、get、incr、decr 验证字符串的长度和内容是否正…...