live555搭建实时播放rtsp服务器

live555关于RTSP协议交互流程

live555的核心数据结构值之闭环双向链表

live555 rtsp服务器实战之createNewStreamSource

live555搭建实时播放rtsp服务器

live555 rtsp服务器实战之doGetNextFrame

       live555可以说是rtsp的专项库,既可以搭建rtsp服务器,也可以搭建rtsp客户端;由于客户端可以由vlc,potplayer等工具代替;这章主要讲解rtsp服务器的搭建过程;

        live555开源库可以说是很照顾用户了,搭建rtsp服务器的流程很精简!不用考虑太多按照流程搭建即可;具体的流程参考live555源码例程:ive555-master/testProgs/testOnDemandRTSPServer.cpp;

        当然,和其他开源库源码例程一样,媒体流都是从文件中获取,与实际的项目需求还是有一定差别的;那么该怎么实现网络实时传输的rtsp服务器呢?

        首先看一下例程的源码(以h264媒体流为例):

int main(int argc, char** argv) {// Begin by setting up our usage environment(首先设置我们的使用环境)://初始化调度任务的类。它负责管理任务的执行顺序和时间,确保多线程环境中的任务正确执行。TaskScheduler* scheduler = BasicTaskScheduler::createNew();//初始化基本操作环境的类。它封装了各种基本功能,如内存管理、错误处理、日志记录等。//接受一个 TaskScheduler 对象作为参数,并使用这个调度器来初始化环境。这确保了环境对象能够与调度器协同工作,正确地调度和管理任务。env = BasicUsageEnvironment::createNew(*scheduler);UserAuthenticationDatabase* authDB = NULL;// Serve regular RTSP (over a TCP connection)://创建rtsp服务器的类,里面创建了与客户端交互需要的ipv4和ipv6网络套接字,和各信令(OPTION DESCRIBE SETUP PLAY等)的处理函数RTSPServer* rtspServer = RTSPServer::createNew(*env, 8554, authDB);if (rtspServer == NULL) {*env << "Failed to create RTSP server: " << env->getResultMsg() << "\n";exit(1);}char const* descriptionString = "Session streamed by \"testOnDemandRTSPServer\"";char const* streamName = "h264ESVideoTest";char const* inputFileName = "test.264";//将streamName descriptionString等信息保存起来,供交互时使用ServerMediaSession* sms= ServerMediaSession::createNew(*env, streamName, streamName,descriptionString);int datalen = 0;unsigned char *databuf = nullptr;databuf = (unsigned char *)malloc(1024);//将该H264VideoFileServerMediaSubsession媒体子会话加入到会话链表中;咱们就是实现H264VideoFileServerMediaSubsession部分sms->addSubsession(H264VideoFileServerMediaSubsession::createNew(*env, inputFileName, reuseFirstSource));//将该会话添加到哈希表中,key就是streamName,val就是smsrtspServer->addServerMediaSession(sms);//打印rtsp的访问url; test例程自定义的打印接口,不在live555库里,在实际项目中可以不实现;announceStream(rtspServer, sms, streamName, inputFileName);//进入主循环;循环监视双向闭环链表,延时队列的状态,运行响应的处理函数env->taskScheduler().doEventLoop(); // does not returnreturn 0; // only to prevent compiler warning
}

        为了方便阅读,代码做了精简!去掉了部分注释和不需要的代码;这样整个服务器的搭建流程就已经很清晰明了了;是不是很简单?

注:

媒体子会话:一个流URL可能链接很多客户端,每个客户端就是一个子会话。

        来!来!来!接下来就是重头戏啦!我们怎么获取我们的网络媒体流呢? 首先我们需要实现两个虚函数:createNewStreamSource和doGetNextFrame;这两个函数的作用及在源码的调度流程我的前两篇文章已经介绍过,感兴趣的话点链接看详情;

        我们需要创建两个类来实现这两个虚函数:

        H264LiveVideoServerMediaSubssion类用于实现createNewStreamSource;因为createNewStreamSource函数被声明在OnDemandServerMediaSubsession类中,所以要继承OnDemandServerMediaSubsession类:

class H264LiveVideoServerMediaSubssion : public OnDemandServerMediaSubsession {public:static H264LiveVideoServerMediaSubssion* createNew(UsageEnvironment& env);protected: // we're a virtual base classH264LiveVideoServerMediaSubssion(UsageEnvironment& env);~H264LiveVideoServerMediaSubssion();protected: // redefined virtual functionsFramedSource* createNewStreamSource(unsigned clientSessionId,unsigned& estBitrate);RTPSink* createNewRTPSink(Groupsock* rtpGroupsock,unsigned char rtpPayloadTypeIfDynamic,FramedSource* inputSource);};

        由于createNewRTPSink在OnDemandServerMediaSubsession类中也是纯虚函数,所以也需要在子类中实现;来看下函数实现:

H264LiveVideoServerMediaSubssion* H264LiveVideoServerMediaSubssion::createNew(UsageEnvironment& env, Boolean reuseFirstSource)
{return new H264LiveVideoServerMediaSubssion(env, reuseFirstSource);
}H264LiveVideoServerMediaSubssion::H264LiveVideoServerMediaSubssion(UsageEnvironment& env, Boolean reuseFirstSource)
: OnDemandServerMediaSubsession(env, reuseFirstSource)                                                                      
{
}H264LiveVideoServerMediaSubssion::~H264LiveVideoServerMediaSubssion()
{
}FramedSource* H264LiveVideoServerMediaSubssion::createNewStreamSource(unsigned clientSessionId, unsigned& estBitrate)
{/* Remain to do : assign estBitrate */estBitrate = 1000; // kbps, estimate//创建视频源H264FramedLiveSource* liveSource = H264FramedLiveSource::createNew(envir());if (liveSource == NULL){return NULL;}// Create a framer for the Video Elementary Stream:return H264VideoStreamFramer::createNew(envir(), liveSource);
}
RTPSink* H264LiveVideoServerMediaSubssion::createNewRTPSink(Groupsock* rtpGroupsock,unsigned char rtpPayloadTypeIfDynamic,FramedSource* /*inputSource*/) {return H264VideoRTPSink::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic);
}

        createNewStreamSource函数是需要我们实现的函数,函数创建了H264FramedLiveSource类对象,这个类对象就是我们实现doGetNextFrame函数的类;createNewRTPSink函数不需要自己实现;之间创建H264VideoRTPSink类即可;

        接下来看一下H264FramedLiveSource类;该类的doGetNextFrame指明了怎么获取媒体流;读取媒体流的方法;因为doGetNextFrame是在FramedSource中声明的纯虚函数;因此需要继承FramedSource类;

class H264FramedLiveSource : public FramedSource
{
public:static H264FramedLiveSource* createNew(UsageEnvironment& env);protected:H264FramedLiveSource(UsageEnvironment& env);~H264FramedLiveSource();private:virtual void doGetNextFrame();
};

        FramedSource类中只有一个doGetNextFrame纯虚函数,用于获取媒体流,其他不用实现;doGetNextFrame函数的实现如下:

H264FramedLiveSource::H264FramedLiveSource(UsageEnvironment& env)
: FramedSource(env)
{
}H264FramedLiveSource* H264FramedLiveSource::createNew(UsageEnvironment& env)
{H264FramedLiveSource* newSource = new H264FramedLiveSource(env);return newSource;
}H264FramedLiveSource::~H264FramedLiveSource()
{
}void H264FramedLiveSource::doGetNextFrame()
{uint8_t *frameData = nullptr;int frameLen = 0;//获取视频帧,该函数自己实现,可以是接收网络帧,也可以是读取本地数据Get_Video_Frame(&frameData, frameLen);//赋值父类成员变量fFrameSize = frameLen;memcpy(fTo, frameData, fFrameSize);// nextTask() = envir().taskScheduler().scheduleDelayedTask(0,(TaskFunc*)FramedSource::afterGetting, this);//表示延迟0秒后再执行 afterGetting 函数//这个是必须的,原因参考我关于doGetNextFrame的文章afterGetting(this);return;
}

        我是以帧的方式获取的视频流,在实际的开发中,不一定非要一次取一帧;也不一定按帧取;可以任意长度的数据;live555内部会进行分帧;

        定义好这两个函数之后就可以修改main函数中的媒体流相关代码:        

//例程代码sms->addSubsession(H264VideoFileServerMediaSubsession::createNew(*env, inputFileName, reuseFirstSource));
//修改代码sms->addSubsession(H264LiveVideoServerMediaSubssion::createNew(*env, reuseFirstSource));

        至此,rtsp服务器搭建完成;其实过程很简单;只不过第一次使用live555开源库有点不知从何下手;当然live555源码的实现技巧,运行流程也是很值得借鉴的;源码分析我已经出了一些文章;关注我了解更多;

live555实现实时流rtsp服务器源码:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/873745.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

HTTP协议的演进:从HTTP/1.0到HTTP/2.0

随着互联网技术的不断发展&#xff0c;HTTP协议作为Web通信的基础&#xff0c;也经历了从HTTP/1.0到HTTP/1.1再到HTTP/2.0的演进。本文将逐步深入探讨这两个版本的特点、不足以及改进&#xff0c;以帮助我们更好地理解HTTP协议的发展历程。 一、HTTP/1.0的特点与不足 HTTP/1.…

【多任务YOLO】 A-YOLOM: You Only Look at Once for Real-Time and Generic Multi-Task

You Only Look at Once for Real-Time and Generic Multi-Task 论文链接&#xff1a;http://arxiv.org/abs/2310.01641 代码链接&#xff1a;https://github.com/JiayuanWang-JW/YOLOv8-multi-task 一、摘要 高精度、轻量级和实时响应性是实现自动驾驶的三个基本要求。本研究…

Java基础编程500题——HashSet、LinkedHashSet和TreeSet

&#x1f4a5; 该系列属于【Java基础编程500题】专栏&#xff0c;如您需查看Java基础的其他相关题目&#xff0c;请您点击左边的连接 目录 1. 向HashSet中添加元素&#xff0c;并遍历输出。 2. 使用LinkedHashSet保持插入顺序&#xff0c;并遍历输出。 3. 从HashSet中删除一…

多光谱的空间特征和光谱特征Statistics of Real-World Hyperspectral Images

文章目录 Statistics of Real-World Hyperspectral Images1.数据集2.spatial-spectral representation3.Separable Basis Components4.进一步分析5.复现一下5.1.patch的特征和方差和论文近似&#xff0c;5.2 spatial的basis和 spectral的basis 6.coef model7.join model Statis…

如何让主机显示Docker容器的程序界面,同时支持声音播放

系统中如果安装各种应用软件,很容易会因为版本冲刺引发异常。一个好的办法就是用容器来隔离系统环境,确保主机环境不变。对于一些有界面的程序,可以在容器内运行,让其界面显示在主机上。下面以安装和使用视频剪辑软件shotcut为例,介绍实现方案。 docker run -it --privil…

获取磁盘剩余容量-----c++

获取磁盘剩余容量 #include <filesystem>struct DiskSpaceInfo {double total;double free;double available; };DiskSpaceInfo getDiskSpace(const std::string& path) {std::filesystem::space_info si std::filesystem::space(path);DiskSpaceInfo info;info.…

机器学习 - 信息增益

信息增益&#xff08;Information Gain&#xff09; 信息增益是衡量在特征选择过程中一个特征对数据集分类能力提升的指标。在构建决策树&#xff08;如ID3和C4.5算法&#xff09;时&#xff0c;信息增益用于选择最佳的特征来划分数据集。信息增益基于熵的概念&#xff0c;通过…

多视角数据的不确定性估计:全局观的力量

论文标题&#xff1a;Uncertainty Estimation for Multi-view Data: The Power of Seeing the Whole Picture 中文译名&#xff1a;多视角数据的不确定性估计:全局观的力量 原文地址&#xff1a;Uncertainty Estimation for Multi-view Data: The Power of Seeing the Whole …

python用selenium网页模拟时xpath无法定位元素解决方法2

有时我们在使用python selenium xpath时&#xff0c;无法定位元素&#xff0c;红字显示no such element。上一篇文章写了1种情况&#xff0c;是包含iframe的&#xff0c;详见https://blog.csdn.net/Sixth5/article/details/140342929。 本篇写第2种情况&#xff0c;就是xpath定…

类和对象:赋值函数

1.运算符重载 • 当运算符被⽤于类类型的对象时&#xff0c;C语⾔允许我们通过运算符重载的形式指定新的含义。C规定类类型对象使⽤运算符时&#xff0c;必须转换成调⽤对应运算符重载&#xff0c;若没有对应的运算符重载&#xff0c;则会编译报错&#xff1b;&#xff08;运算…

数据旋律与算法和谐:LLMs的微调交响

论文&#xff1a;https://arxiv.org/pdf/2310.05492代码&#xff1a;暂未开源机构&#xff1a;阿里巴巴领域&#xff1a;模型微调发表&#xff1a;ACL 2024 这篇论文《How Abilities in Large Language Models are Affected by Supervised Fine-tuning Data Composition》深入…

【BUG】已解决:raise KeyError(key) from err KeyError: (‘name‘, ‘age‘)

已解决&#xff1a;raise KeyError(key) from err KeyError: (‘name‘, ‘age‘) 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页&#xff0c;我是博主英杰&#xff0c;211科班出身&#xff0c;就职于医疗科技公司&#xff0c;热衷分享知识&#xf…

python学习(不是广告)是我自己看了这么多课总结的经验

入门 首先推荐的是林粒粒的python入门学习 在你看完这套Python入门教程后&#xff1a; 想继续巩固基础 &#x1f449; 想学习Python数据分析 &#x1f449; 想学习Python AI大模型应用开发 &#x1f449; 进阶 入门之后就是进阶使用python实现 1.办公效率化 2.数据分析&am…

信弘智能与图为科技共探科技合作新蓝图

本期导读 近日&#xff0c;图为信息科技&#xff08;深圳&#xff09;有限公司迎来上海信弘智能科技有限公司代表的到访&#xff0c;双方共同探讨英伟达生态系统在人工智能领域的发展。 在科技日新月异的今天&#xff0c;跨界合作与技术交流成为了推动行业发展的重要驱动。7月…

GraphRAG+ollama+LM Studio+chainlit

这里我们进一步尝试将embedding模型也换为本地的&#xff0c;同时熟悉一下流程和学一些新的东西 1.环境还是用之前的&#xff0c;这里我们先下载LLM 然后你会在下载nomic模型的时候崩溃&#xff0c;因为无法搜索&#xff0c;无法下载 解决办法如下lm studio 0.2.24国内下载…

Qt 遍历Combbox下拉框的内容并进行判断

一共用到两个函数&#xff1a; 1、count()&#xff1b;用于计算当前Combox中一共有几个item。 2、itemText(int i)&#xff1b;参数 i 是需要查看item的索引。 用来查看当前item的text&#xff08;文本信息&#xff09;。 for(int i 0; i < ui->comboBox_displayDev-&g…

C++学习笔记-用const修饰的类成员函数

在C编程中&#xff0c;const关键字扮演着多重角色&#xff0c;其中之一就是用于修饰类的成员函数&#xff0c;以表明该函数不会修改类的任何成员变量&#xff08;无论是数据成员还是静态成员&#xff09;。使用const成员函数可以提高代码的可读性和安全性&#xff0c;特别是在多…

Ubuntu 24.04 LTS Noble安装Docker Desktop简单教程

Docker 为用户提供了在 Ubuntu Linux 上快速创建虚拟容器的能力。但是&#xff0c;那些不想使用命令行管理容器的人可以在 Ubuntu 24.04 LTS 上安装 Docker Desktop GUI&#xff0c;本教程将提供用于设置 Docker 图形用户界面的命令…… Docker Desktop 是一个易于使用的集成容…

mybatis-sql实战总结

mybatis-sql实战总结 动态条件查询怎么List遍历集合进行拼接&#xff1f; 动态条件查询 动态条件查询总结 注意点&#xff1a; 1.可以用where 11 也可以用 防止出现后面没有条件的情况 2.模糊查询用concat拼接 A.SSTRING10 like concat(#{adAccEntrie.sstring10},‘%’) 3. 对于…

Python正则表达式中的分组

表达式中的分组 它是可以通过" () “来进行分组&#xff0c;更专业的表达就是捕获组&#xff0c;每个完整的” () “可以分为一组&#xff0c;同时&#xff0c;” () “中还可以嵌套” () "&#xff0c;即组之间还可以存在更小的组 概念 1、当我们在一个正则表达式…