rtsp服务器逻辑

news/2024/9/17 7:38:24/ 标签: 服务器

定时器逻辑:比如H264文件是每隔40ms发送一帧数据。aac文件每隔23ms发送一个音频帧数据。

在sink的子类中有aac和h264的sink,在两个子类的构造函数中需要添加它们各自的触发时间。调用的函数时runEvery(),将这两个触发时间设置到了TimerManager中,在TimerManager中有一个map类型的结构体mEvent:记录着触发时间。

在sink中需要create一个TimeEvent事件,这个是事件是时间到了触发,用来触发回调。设置回调函数:mTimerEvent->setTimeoutCallback(cbTimeout);就是定时的发送数据。

在cbTimeout回调函数中就是发送数据:

void Sink::handleTimeout() {MediaFrame* frame = mMediaSource->getFrameFromOutputQueue();if (!frame) {return;}this->sendFrame(frame);// 由具体子类实现发送逻辑mMediaSource->putFrameToInputQueue(frame);//将使用过的frame插入输入队列,插入输入队列以后,加入一个子线程task,从文件中读取数据再次将输入写入到frame
}

在TimerManager的构造函数中就是创建了一个定时器的文件描述符,将这个文件描述符添加到一个IO事件中,为这个IO事件添加一个回调函数,将这个IO事件添加到select模型中。这是一个定时器的管理者,用来定时触发读取事件。

设置的回调函数时readCallback,其中调用的是handleRead函数,一旦定时器触发,就会触发这个函数,函数中就是处理上述提到的TimerEvent,

当定时器触发会后,readCallback函数将从mEvent中移除已触发的事件,并调用modifyTimeOut函数重新设置下一个最近的定时事件。就相当于是处理完一个定时器事件之后重新设置新的时间戳的定时器。

void TimerManager::handleRead() {Timer::Timestamp timestamp = Timer::getCurTime(); // 获取当前时间if (!mTimers.empty() && !mEvents.empty()) { // 确保定时器和事件队列不为空std::multimap<Timer::Timestamp, Timer>::iterator it = mEvents.begin(); // 获取最早到期的定时器Timer timer = it->second; // 获取定时器对象int expire = timer.mTimestamp - timestamp; // 计算定时器到期剩余时间if (timestamp > timer.mTimestamp || expire == 0) { // 如果当前时间超过定时器的到期时间bool timerEventIsStop = timer.handleEvent(); // 处理定时器事件mEvents.erase(it); // 从事件队列中移除已处理的定时器if (timer.mRepeat) { // 如果是重复定时器if (timerEventIsStop) { // 如果定时器事件需要停止(例如,定时器任务完成后)mTimers.erase(timer.mTimerId); // 从定时器管理器中移除} else { // 否则,重新安排定时器timer.mTimestamp = timestamp + timer.mTimeInterval; // 设置下一个到期时间mEvents.insert(std::make_pair(timer.mTimestamp, timer)); // 将新的定时器事件插入到事件队列中}} else { // 如果不是重复定时器mTimers.erase(timer.mTimerId); // 从定时器管理器中移除}}}modifyTimeout(); // 更新定时器的到期时间
}

定时器到期后触发的发送器发送逻辑

1.调用getFrameOutputQueue()函数取出一帧数据

2.调用函数sendFrame(frame)进行发送

3.调用putFrameToInputQueue(frame)函数,将使用过的frame插入输入队列中,插入输入队列之后,加入一个子线程task,从文件中读取数据再次将输入写入到frame中。

Main函数的主流程:

1.先创建了一个调度器EventScheduler,在构造函数中创建了Poller和创建了一个定时器管理者。

在定时器管理者构造函数中就是上面所提到的。

在调度器类中最主要的就是一个循环功能:只要不退出一直调用以下函数:

while (!mQuit) {handleTriggerEvents();mPoller->handleEvent();}

还有一些添加IO事件,移除IO事件,更新IO事件的函数;添加触发事件,移除触发事件的函数;添加定时器事件的函数。

2.创建了线程池,任务队列中的任务是从H264文件或者aac文件中读取数据,进行处理然后添加到mFrameOutputQueue队列中。

3.创建媒体会话管理者,在构造函数中啥也没干,这类中有几个函数,往MediaSessionManager中添加MediaSession,这里的MediaSession有一个名字叫做test,在MediaSession中有两路轨,一路是音频轨一路是视频轨,当然一个MediaSessionManager中肯定包含着很多的MediaSession。

4.设置RTSP服务器的IP地址和端口号

5.创建rtsp服务器,在构造函数中需要加MediaSessionManager,调度器和线程池以及IP和端口号传入当做参数。函数体中创建了socket文件描述符,为该文件描述符添加了IO事件并设置了回调函数。同时也设置了回调的关闭连接。“为该文件描述符添加了IO事件并设置了回调函数”这里设置的回调函数就是调用accept,然后创建一个rtsp连接,调用了RtspConnection构造函数,在这个类中主要处理的就是rtsp协议交互相关的东西。

比如有parseRequest函数用来解析客户端发送的RTSP请求,从请求中提取方法,URL,协议版本等信息。

handleReadBytes()函数:当有数据从客户端发送到服务器时,会调用此函数处理数据。判断是否是RTP over TCP,如果是调用handleRtpOverTcp()函数,否则解析RTSP请求并根据不同的RTSP方法执行相应的命令。

RTSP方法的处理:比如对OPTION请求,DESCRIBE请求,SETUP请求,PLAY请求的响应。

RTP和RTCP处理:通过UDP创建两者的实例,用于传输和接收流媒体数据,或者是通过TCP创建RTP实例等。

6.创建一个session也就是上面提到的MediaSession,在MediaSession构造函数中给每个MediaSession添加一个名字,在该服务器中就是test。我觉得这个类中关键的一个函数就是addSink(),这个函数的作用就是给指定的轨添加sink函数,在addSink()函数中有一个关键的操作就是给sink实例设置一个SessionCb,这里的回调函数才是真正将rtp数据包发送出去的函数。

其中还有addRtpInstance()函数以及remove函数,就是为轨道添加RTP实例,也是建立数据传输的媒介。

7.创建一个H264媒体资源,在构造函数中向线程池中添加4个任务,每个任务的处理过程是从mFrameInputQueue取第一帧数据frame,然后将H264文件中的数据拷贝到frame中,这里要检查并处理起始码,可能是00 00 01,也可能是00 00 00 01.然后再检查NALU类型,如果是0x09的话就跳过这帧处理下一帧,以上步骤如果成功的话将frame添加到mFrameOutputQueue中。

8.创建一个H264fileSink子类对象,需要将上面创建的H264媒体资源传给构造函数,在构造函数中设置了定时器的触发时间。这个子类中主要是重写了父类中的sendFrame()函数,在sendFrame函数中有两种发包的思路,一种是单NALU打包,一种是分片打包。在父类中还会调用父类的构造函数,在该构造函数中创建了一个定时器事件,同时设置了定时器事件的回调函数,表示定时器时间到后执行的函数。函数的逻辑就是时间到后发送帧数据。子类中的sendFrame函数中的关键函数在父类中:sendRtpPacket()函数,这个函数中就是调用了一个回调函数,那么是在哪里设置了这个回调函数呢?就是在接下来的addSink中设置的。

9.向session中添加sink实例,也就是调用了session中的addSink()函数,在MediaSession中有两个轨,我们就将H264的sink子类添加到TrackId0中,将aac的sink子类添加到TrackId1中。在addSink函数中就设置了sink中的回调函数。

那么整个流程是什么样的呢?首先我们设置了定时器,例如我们的H264文件的帧率是25帧的,那么就是一秒播放25帧,也就是40ms一帧数据。我们将这个数值设置到定时器管理者中,这个时候管理者到点就会触发回调函数也就是TimerManager::readCallback,也就是取出对应的定时器,执行定时器里面的事件。这里的定时器里面的事情是在sink构造函数中设置的 mTimerEvent->setTimeoutCallback(cbTimeout);就是触发真正的发送数据了。

那么数据是怎么发送出去的?上面我们提到cbTimeout回调函数,里面调用了handleTimeout()函数,然后由子类去实现发送逻辑this->sendFrame(frame),在子类中真正的发送逻辑还是在父类中实现的也就是sendRtpPacket(RtpPacket* packet)函数,在这个函数里还执行了一个回调函数,最终发送者是在MediaSession中,在MediaSession中调用了handleSendRtpPacket()函数才将数据最终发送出去。

这是一个基于北小菜RTSP服务器的个人理解,有不对的地方欢迎指正。


http://www.ppmy.cn/news/1522614.html

相关文章

【H2O2|全栈】关于HTML(1)认识HTML

HTML相关知识 目录 前言 准备工作 WEB前端是什么&#xff1f; HTML是什么&#xff1f; 如何运行HTML文件&#xff1f; 标签 概念 分类 双标签和单标签 行内标签和块标签 HTML文档结构 预告和回顾 UI设计相关 Markdown | Md文档相关 项目合作管理相关 后话 前…

数据结构之堆的创建

1、堆的概念及结构 1.1堆的概念 如果有一个关键码的集合K{k0,k1,k2,…,kn-1}&#xff0c;把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中&#xff0c;并满足ki<k2i1且ki<k2i2&#xff08;或满足ki>k2i1且ki>k2i2&#xff09;&#xff0c;其中i0…

Vue2项目搭建:Vue2.7+Vite4+Pinia+TailwindCSS+Prettier+ESLint

目前 create-vue 和 Vite 都不提供 Vue2 项目的搭建&#xff0c;不想用 Vue CLI 和 webpack&#xff0c;于是就打算从 0 搭建一个工程化项目&#xff0c;支持组合式 API (Composition API) 写法&#xff0c;没有使用 TypeScript&#xff0c;有朋友需要的话我可以再完善一下。 N…

结构体小知识

目录 前言1.结构体数组1.1结构体数组理解1.2结构体数组知识运用1.3 -> 操作符 2. 知识拓展 前言 本期blog是对上一期指针知识的知识补充&#xff0c;如果各位大佬感兴趣的话&#xff0c;可以结合起来一起看&#xff01; 1.结构体数组 1.1结构体数组理解 结构体数组在本…

pytorch torch.nn.functional.one_hot函数介绍

torch.nn.functional.one_hot 是 PyTorch 中用于生成独热编码&#xff08;one-hot encoding&#xff09;张量的函数。独热编码是一种常用的编码方式&#xff0c;特别适用于分类任务或对离散的类别标签进行处理。该函数将整数张量的每个元素转换为一个独热向量。 函数签名 tor…

notepad++软件介绍(含安装包)

Notepad 是一款开源的文本编辑器&#xff0c;主要用于编程和代码编辑。它是一个功能强大的替代品&#xff0c;常常被用来替代 Windows 系统自带的记事本。 Notepad win系统免费下载地址 以下是 Notepad 的一些主要特点和功能&#xff1a; 多语言支持&#xff1a;Notepad 支持多…

Kafka【八】如何保证消息发送的可靠性、重复性、有序性

【1】消息发送的可靠性保证 对于生产者发送的数据&#xff0c;我们有的时候是不关心数据是否已经发送成功的&#xff0c;我们只要发送就可以了。在这种场景中&#xff0c;消息可能会因为某些故障或问题导致丢失&#xff0c;我们将这种情况称之为消息不可靠。虽然消息数据可能会…

proxy代理解决vue中跨域问题

vue.config.js module.exports {...// webpack-dev-server 相关配置devServer: {host: 0.0.0.0,port: port,open: true,proxy: {/api: {target: https://vfadmin.insistence.tech/prod-api,changeOrigin: true,pathRewrite: {//[^ process.env.VUE_APP_BASE_API]: ^/api: / …

【 html+css 绚丽Loading 】000044 两仪穿行轮

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享htmlcss 绚丽Loading&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495…

【sql】评估数据迁移复杂度调查汇报240904

难度判断标准&#xff1a; - 高难度&#xff1a;使用多个表&#xff08;TBL&#xff09;或有多个join操作的工具 - 低难度&#xff1a;表数量少且没有join操作的简单工具 - 中等难度&#xff1a;介于高低之间&#xff0c;有少量join操作的工具 5. 最后说明不需要仔细…

25届计算机毕业设计:3步打造北部湾助农平台,Java SpringBoot实践

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…

AF透明模式/虚拟网线模式组网部署

透明模式组网 实验拓扑 防火墙基本配置 接口配置 eth1 eth3 放通策略 1. 内网用户上班时间&#xff08;9:00-17:00&#xff09;不允许看视频、玩游戏及网上购物&#xff0c;其余时 间访问互联网不受限制&#xff1b;&#xff08;20 分&#xff09; 应用控制策略 2. 互联…

[论文笔记]RAFT: Adapting Language Model to Domain Specific RAG

引言 今天带来一篇结合RAG和微调的论文&#xff1a;RAFT: Adapting Language Model to Domain Specific RAG。 为了简单&#xff0c;下文中以翻译的口吻记录&#xff0c;比如替换"作者"为"我们"。 本文介绍了检索增强微调(Retrieval Augmented Fine Tunin…

【Impala SQL 造数(一)】

前言 SQL 造数即生成测试数据&#xff0c;一般是编码完成之后的测试阶段所需&#xff0c;测试数据可以用于多种目的&#xff0c;包括测试应用程序的功能、业务场景测试、性能测试、数据恢复测试等。在测试阶段特别是数据类需求&#xff0c;需要很多造数场景&#xff0c;像 Hiv…

尚品汇-支付宝支付同步异步回调实现(四十七)

目录&#xff1a; &#xff08;1&#xff09;订单支付码有效时间 &#xff08;2&#xff09;支付后回调—同步回调 &#xff08;3&#xff09;支付宝回调—异步回调 &#xff08;1&#xff09;订单支付码有效时间 &#xff08;2&#xff09;支付后回调—同步回调 static修饰…

【Jupyter Notebook】安装与使用

打开Anaconda Navigator点击"Install"(Launch安装前是Install)点击"Launch"点击"File"-"New"-"Notebook"​ 5.点击"Select"选择Python版本 6.输入测试代码并按"Enter+Shift"运行代码: 代码如下: …

考研系列-408真题数据结构篇(18-23)

写在前面 此文章是本人在备考过程中408真题数据结构部分(2018年-2023年)的易错题及相应的知识点整理,后期复习也尝尝用到,对于知识提炼归纳理解起到了很大的作用,分享出来希望帮助到大家~ # 2018年 1.堆的建立(从后往前进行调整) 2.算法(正整数和数组之间关系的建立)

k8s API资源对象ingress

有了Service之后&#xff0c;我们可以访问这个Service的IP&#xff08;clusterIP&#xff09;来请求对应的Pod&#xff0c;但是这只能是在集群内部访问。 要想让外部用户访问此资源&#xff0c;可以使用NodePort&#xff0c;即在node节点上暴漏一个端口出来&#xff0c;但是这…

pytorch+深度学习实现图像的神经风格迁移

本文的完整代码和部署教程已上传至本人的GitHub仓库&#xff0c;欢迎各位朋友批评指正&#xff01; 1.各代码文件详解 1.1 train.py train.py 文件负责训练神经风格迁移模型。 加载内容和风格图片&#xff1a;使用 utils.load_image 函数加载并预处理内容和风格图片。初始化…

Banana Pi BPI-SM9 AI 计算模组采用算能科技BM1688芯片方案设计

产品概述 香蕉派 Banana Pi BPI-SM9 16-ENC-A3 深度学习计算模组搭载算能科技高集成度处理器 BM1688&#xff0c;功耗低、算力强、接口丰富、兼容性好。支持INT4/INT8/FP16/BF16/FP32混合精度计算&#xff0c;可支持 16 路高清视频实时分析&#xff0c;灵活应对图像、语音、自…