RtspServer:轻量级RTSP服务器和推流器

news2024/11/17 15:33:41

文章目录

    • 项目概述
    • 技术分析
      • 支持的编码格式
      • 传输方式
      • 心跳检测机制
      • RTSP 推流
      • 安全性
    • 架构分析
      • RtspServer 整体架构
      • 流程分析
        • 1. 客户端连接和会话建立
        • 2. 媒体数据传输
        • 3. 心跳检测和连接维护
    • xop 基础库
      • 项目介绍
      • 功能特性
      • xop 整体架构
    • 应用场景
    • 社区问题收集与解答
      • 问题一:刚开始播放时有些花屏
      • 问题二:推送 H.265 流时播放器无法播放
    • Bug 修复与代码分析
      • 问题描述
      • 分析与解决方案
        • 1. 修改 RTP 包的最大负载大小
        • 2. 调整 RTP TCP 头部大小
        • 3. 修正 H.264 视频源的帧处理
        • 4. 修正 H.265 视频源的帧处理
      • 总结
    • 进一步的技术分析
      • 深入理解 RTP 分片与 NALU 处理
        • H.264 中的 FU-A 分片
        • H.265 中的 FU 分片
      • 起始码的处理
      • 播放器兼容性

项目概述

RtspServer 是由 PHZ76 开发的高效、可定制的实时流媒体服务器解决方案。它基于作者编写的网络基础库 xop,允许开发者轻松处理和分发实时音视频流。项目提供了一个名为 DesktopSharing 的示例应用,可以捕获桌面和麦克风声音,并在编码后通过 RTSP 协议进行转发和推流。该项目支持 Windows 和 Linux 平台,代码量少,相比于 live555 等经典的流媒体库,RtspServer 更加轻量级,易于集成和二次开发,已在公司的项目中应用。

技术分析

支持的编码格式

RtspServer 支持多种音视频编码格式,涵盖了广泛的应用场景:

  • 视频编码:H.264、H.265
  • 音频编码:G.711A、AAC

传输方式

  • 单播 (Unicast)

    • RTP_OVER_UDP:通过 UDP 协议传输 RTP 数据,延迟低,但在网络不稳定时可能会丢包。
    • RTP_OVER_RTSP (TCP):通过 RTSP 协议在 TCP 连接上传输 RTP 数据,可靠性高,适用于防火墙或 NAT 环境。
  • 组播 (Multicast)

    • 适用于需要将同一流媒体数据发送给多个客户端的场景,节省网络带宽。

心跳检测机制

针对单播传输,RtspServer 内置了心跳检测机制,可以及时发现和处理连接异常,确保数据传输的稳定性。

RTSP 推流

RtspServer 支持 RTSP 推流功能,使用 TCP 协议进行数据传输,保证了数据的可靠传输,适用于对传输可靠性要求较高的应用场景。

安全性

RtspServer 内置了摘要认证(Digest Authentication),为服务提供了安全保障,防止未经授权的访问。

架构分析

RtspServer 整体架构

RtspServer 的架构主要包括以下组件:

  • RTSP Server:处理客户端的 RTSP 请求,包括 SETUP、PLAY、PAUSE、TEARDOWN 等指令,管理会话和媒体流。
  • Media Session:表示一个媒体会话,包含媒体流的相关信息,如流名称、媒体类型、编码格式等。
  • Media Source:媒体源,负责提供音视频帧数据,可以来自文件、摄像头、麦克风或其他实时数据源。
  • RTP Connection:负责通过 RTP 协议发送音视频数据,支持单播和组播传输。

流程分析

1. 客户端连接和会话建立
  • 客户端通过 RTSP 协议发送连接请求到服务器。
  • RTSP Server 接收到请求后,解析并创建一个新的 Media Session。
  • 为每个媒体流(音频或视频)创建对应的 Media Source。
2. 媒体数据传输
  • Media Source 获取音视频数据帧(可能来自编码器或实时采集设备)。
  • RTP Connection 负责将媒体数据打包成 RTP 包,通过网络传输给客户端。
  • 支持的传输方式包括 RTP_OVER_UDP、RTP_OVER_RTSP 和组播。
3. 心跳检测和连接维护
  • 为了保持连接的稳定性,服务器会定期发送心跳检测,确认客户端的在线状态。
  • 如果检测到连接异常,服务器会及时释放资源,防止资源泄漏。

xop 基础库

项目介绍

xop 是 RtspServer 的基础网络库,参考了 muduolive555 的设计,封装了一个简单高效的网络框架,提供了构建高性能网络应用的基础组件。

功能特性

  • 跨平台支持:兼容 Windows 和 Linux 操作系统。
    • Windows 下:使用 select 实现事件循环。
    • Linux 下:使用 epoll 实现高效的事件通知机制。
  • 事件驱动模型:基于 Reactor 模式,实现非阻塞 IO 和事件驱动。
  • 定时器:提供高精度的定时任务调度。
  • 内存管理:实现了环形缓冲区和内存池,提升内存分配和释放的效率。
  • 日志系统:内置简洁的日志功能,方便调试和运行监控。

xop 整体架构

在这里插入图片描述

  • EventLoop:事件循环,核心组件,负责监听和分发 IO 事件、定时器事件等。
  • Channel:通道,封装了文件描述符及其感兴趣的事件类型,如可读、可写等。
  • TimerQueue:定时器队列,管理所有的定时任务。
  • Buffer:缓冲区,提供高效的数据读写接口。
  • Acceptor:监听器,负责接受新的客户端连接。

应用场景

  • 在线教育平台:提供高清、流畅的远程教学体验,实现教师与学生的实时互动。
  • 视频监控系统:实时传输监控摄像头的视频流,实现远程监控和安全管理。
  • 远程会议系统:确保音频和视频的同步传输,提高远程会议的质量和效率。
  • 桌面共享与远程协助:通过 DesktopSharing 示例应用,实现桌面实时共享和远程协助功能。

社区问题收集与解答

问题一:刚开始播放时有些花屏

问题描述

在播放开始时,视频出现花屏现象。怀疑是与 GOP(Group of Pictures)有关,没有收到完整的一组 GOP,所以会花屏。是否应该等待下一个 I 帧?

解答

是的,花屏问题通常是由于解码器没有接收到完整的关键帧(I 帧)导致的。解码器需要从 I 帧开始才能正确解码后续的 P 帧和 B 帧。

解决方案

  • 确保首帧为关键帧:在客户端连接后,服务器应确保发送的第一帧是关键帧。可以在编码器中设置,让其在新的会话开始时立即生成一个 I 帧。
  • 检查帧类型:在编码过程中,可以通过 AVFrame 或其他编码库的接口,获取帧类型,根据编码后的类型(I 帧、P 帧、B 帧)进行处理。
  • 缓冲策略:在客户端实现一定的缓冲策略,等待接收到第一个 I 帧后再开始解码和播放。

问题二:推送 H.265 流时播放器无法播放

问题描述

在使用 H.265 推流时,播放器无法正常播放视频。怀疑是在 H265SourcehandleFrame 函数中处理有误。

解答

问题可能出在对 NALU(Network Abstraction Layer Unit)的处理上。在 H.265 码流中,每个 NALU 前通常有一个起始码(如 0x00 00 00 01)。在计算 NALU 类型时,需要正确跳过起始码。

解决方案

  • 去除起始码:在处理帧数据时,需要跳过起始码,以正确解析 NALU 类型。

    uint8_t *frameBuf = frame.buffer.get();
    uint32_t frameSize = frame.size;
    frameBuf += 4;      // 跳过起始码
    frameSize -= 4;     // 调整帧大小
    
  • 统一处理方式:建议在构建 VideoFrame 时就去掉起始码,这样后续的处理会更加统一。

  • 区别转发和推流:对于转发和推流的处理,可以有所区别。直接获取 H.265 帧数据后调用 pushFrame,确保计算时是带有起始码的头部信息。

注意

  • NALU 解析:正确解析 NALU 类型对于视频解码非常重要。确保在处理时准确跳过起始码,才能正确识别帧类型。

  • 播放器兼容性:不同的播放器对码流格式的要求可能不同,确保码流符合标准,有助于提高兼容性。

Bug 修复与代码分析

问题描述

在使用 RtspServer 时,发现使用 VLC 和 ffplay 播放时出现错误:

Invalid NAL unit 0, skipping

导致播放器无法正常播放视频流。需要对代码进行修复,解决该问题。

分析与解决方案

经过分析,问题出在 RTP 包的组装和 NALU 的处理上。以下是具体的修复步骤和代码修改。

1. 修改 RTP 包的最大负载大小

文件rtp.h

问题分析

  • 原始定义的 MAX_RTP_PAYLOAD_SIZE 为 1420,可能导致 RTP 包过小,增加了网络负载。
  • RTP 头部的大小为 12 字节,实际可用的负载大小应该更大。

修改内容

// 原始定义
#define MAX_RTP_PAYLOAD_SIZE   1420

// 修改为
#define MAX_RTP_PAYLOAD_SIZE   1440

解释

  • MAX_RTP_PAYLOAD_SIZE 增加到 1440,可以减少 RTP 包的数量,提高传输效率。
  • 参考了 Live555 中的 MultiFramedRTPSink.cpp,调整了最大负载大小。
2. 调整 RTP TCP 头部大小

文件rtp.h

问题分析

  • 原代码中 RTP_TCP_HEAD_SIZE 定义为 4,当传输模式为 RTP_OVER_UDP 时,这个值应该为 0。
  • 在 UDP 传输中,不需要额外的 TCP 头部。

修改内容

// 原始定义
//#define RTP_TCP_HEAD_SIZE	   4 // when transport_mode_ = RTP_OVER_TCP

// 修改为
#define RTP_TCP_HEAD_SIZE      0 // when transport_mode_ = RTP_OVER_UDP

解释

  • RTP_TCP_HEAD_SIZE 设置为 0,确保在 UDP 传输时,不会错误地加入额外的头部。
3. 修正 H.264 视频源的帧处理

文件H264Source.cpp

问题分析

  • 在处理 H.264 帧时,需要正确跳过起始码(0x00 00 00 01),否则会导致 NALU 解析错误。
  • 当帧大小小于等于最大 RTP 负载时,直接发送整个帧;否则,需要进行分片(FU-A)。

修改内容

if (frame_size <= MAX_RTP_PAYLOAD_SIZE)
{
    frame_buf  += 4; // 跳过起始码
    frame_size -= 4;
    // 构建 RTP 包
    RtpPacket rtp_pkt;
    rtp_pkt.type = frame.type;
    rtp_pkt.timestamp = frame.timestamp;
    rtp_pkt.size = frame_size + RTP_TCP_HEAD_SIZE + RTP_HEADER_SIZE;
    rtp_pkt.last = 1;
    ...
}
else
{
    frame_buf  += 4; // 跳过起始码
    frame_size -= 4;
    char FU_A[2] = {0};
    FU_A[0] = (frame_buf[0] & 0xE0) | 28;            // NALU 头部
    FU_A[1] = 0x80 | (frame_buf[0] & 0x1F);          // FU-A 起始标志
    // 进行分片处理
}

解释

  • 对于小于等于最大负载的帧,直接跳过起始码,发送整个 NALU。
  • 对于大于最大负载的帧,跳过起始码,进行 FU-A 分片,需要正确构建 FU-A 头部。
4. 修正 H.265 视频源的帧处理

文件H265Source.cpp

问题分析

  • H.265 的 NALU 解析与 H.264 类似,但头部格式不同,需要相应调整。
  • 同样需要正确处理起始码和分片。

修改内容

if (frame_size <= MAX_RTP_PAYLOAD_SIZE)
{
    frame_buf  += 4; // 跳过起始码
    frame_size -= 4;
    // 构建 RTP 包
    RtpPacket rtp_pkt;
    rtp_pkt.type = frame.type;
    rtp_pkt.timestamp = frame.timestamp;
    rtp_pkt.size = frame_size + RTP_TCP_HEAD_SIZE + RTP_HEADER_SIZE;
    rtp_pkt.last = 1;
    ...
}
else
{
    frame_buf  += 4; // 跳过起始码
    frame_size -= 4;
    uint8_t PL_FU[3] = {0};
    uint8_t nalUnitType = (frame_buf[0] & 0x7E) >> 1;
    PL_FU[0] = (frame_buf[0] & 0x81) | (49 << 1);    // NALU 头部
    PL_FU[1] = frame_buf[1];                         // NALU 头部
    PL_FU[2] = 0x80 | nalUnitType;                   // FU 起始标志
    // 进行分片处理
}

解释

  • 同样跳过起始码,正确调整 frame_bufframe_size
  • 构建 H.265 的分片头部,确保 NALU 类型和标志位正确。

总结

通过上述修改,修复了播放器无法正常播放的问题。主要原因在于:

  • 起始码处理:需要正确跳过起始码,才能正确解析 NALU。
  • RTP 包组装:调整最大负载大小,确保网络传输的效率和稳定性。
  • 分片处理:在帧过大时,正确地进行 RTP 分片,构建正确的 FU-A(H.264)或 FU(H.265)包。

这些修改使得 RtspServer 在与常见播放器(如 VLC、ffplay)兼容性方面得到了改善。

进一步的技术分析

深入理解 RTP 分片与 NALU 处理

在 RTP 传输中,当单个 NALU 的大小超过了最大 RTP 负载大小时,需要对其进行分片。分片时,需要构建特殊的 RTP 包,称为 Fragmentation Unit(FU)。

H.264 中的 FU-A 分片
  • FU-A 头部格式

    +---------------+
    |0|1|2|3|4|5|6|7|
    +-+-+-+-+-+-+-+-+
    |S|E|R|  Type   |
    +---------------+
    
  • S(Start):分片的开始标志位,1 表示这是第一个分片。

  • E(End):分片的结束标志位,1 表示这是最后一个分片。

  • Type:原始 NALU 的类型。

H.265 中的 FU 分片
  • FU 头部格式

    +---------------+
    |0|1|2|3|4|5|6|7|
    +-+-+-+-+-+-+-+-+
    |S|E|  Type     |
    +---------------+
    
  • S(Start):分片的开始标志位。

  • E(End):分片的结束标志位。

  • Type:原始 NALU 的类型。

起始码的处理

  • 起始码(Start Code):在原始的 H.264/H.265 码流中,起始码用于分隔 NALU,一般为 0x00 00 00 01
  • RTP 传输中不需要起始码:在 RTP 传输中,起始码不应包含在传输的数据中,需要在发送前去除。
  • 解析 NALU 类型:跳过起始码后,才能正确解析 NALU 的头部,获取 NALU 类型。

播放器兼容性

  • VLC 和 ffplay 等播放器在解析 RTP 流时,严格遵循标准。如果发送的数据不符合 RTP 和 H.264/H.265 的封装规范,就会出现错误。

  • 错误信息

    Invalid NAL unit 0, skipping
    

    表示接收到的 NALU 类型为 0,这是无效的,可能是因为没有正确跳过起始码导致的。

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

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

相关文章

C++:deque的底层原理

一、deque是是双端队列。 deque(双端队列)&#xff1a;是一种双开口的"连续"空间的数据结构&#xff0c;双开口的含义是&#xff1a;可以在头尾两端进行插入和删除操作&#xff0c;且时间复杂度为O(1)&#xff0c;与vector比较&#xff0c;头插效率高&#xff0c;不…

Docker自定义构建镜像dockerfile和使用数据卷

Docker自定义构建镜像dockerfile和使用数据卷 DockerFile Dockerfile 是一个文本文件&#xff0c;包含了一系列用于构建 Docker 镜像 的指令和配置信息。通过编写 Dockerfile &#xff0c;用户可以定义镜像的构建过程&#xff0c;包括基础镜像、运行命令、设置环境变量、暴露…

38.重复的子字符串

方法1&#xff1a; class Solution {public boolean repeatedSubstringPattern(String s) {if (s.equals("")) return false;String s2(ss).substring(1,(ss).length()-1);//去掉首尾字符return s2.contains(s);//判断是否包含s} } class Solution(object):def rep…

Leetcode 相交链表

一图胜千言&#xff0c;java 代码如下&#xff1a; /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode(int x) {* val x;* next null;* }* }*/ public class Solution {public ListN…

Python自动化测试之unittest框架

一、什么是框架 框架是由大佬开发或者专业的研发团队研发的技术骨架&#xff0c;框架是一个半成品&#xff0c;框架是对常用的功能&#xff0c;基础的代码进行封装的一个工具&#xff0c;这个工具对外提供了一些API&#xff0c;其他的开发者只需要调用框架的接口即可&#xff…

网络安全法中,个人信息保护的措施和原则有哪些?

《中华人民共和国网络安全法》中关于个人信息保护的规定强调了几项基本原则和措施&#xff0c;以确保个人信息的安全。以下是其中的一些要点&#xff1a; 原则 合法性&#xff1a;个人信息的收集和使用必须符合法律规定。 正当性&#xff1a;信息收集和使用的目的是正当的&…

数据的基本统计描述

基本操作 首先导入鸢尾花数据集用于分析 import numpy as np from sklearn.datasets import load_iris iris_dataload_iris() iris_data 查看数据维度 iris_data.data.shape 获取第一组数据 iris_data.data[0] 获取第一个维度所有取值&#xff0c;作为一维向量 iris_…

【高分系列卫星简介——高分五号卫星(GF-5)】

高分五号卫星&#xff08;GF-5&#xff09; 高分五号&#xff08;GF-5&#xff09;卫星是中国高分辨率对地观测系统重大专项系列中的一颗重要卫星&#xff0c;主要承担着遥感、测绘等任务。以下是对高分五号卫星的详细介绍&#xff1a; 一、基本信息 国籍&#xff1a;中国研…

内容生态短缺,Rokid AR眼镜面临市场淘汰赛

AR是未来&#xff0c;但在技术路径难突破、生态系统难建设&#xff0c;且巨头纷纷下场的背景下&#xff0c;Rokid能坚持到黎明吗&#xff1f; 转载&#xff1a;科技新知 原创 作者丨王思原 编辑丨蕨影 苹果Vision Pro的成功量产和发售&#xff0c;以及热门游戏《黑神话》等在A…

解锁微软录屏工具:2024 开启屏幕录制新时代

现在快节奏的生活环境&#xff0c;录屏工具已成为不可或缺的记录利器&#xff0c;其应用范围广泛。若你正对windows自带录屏功能充满好奇&#xff0c;渴望掌握其操作方法&#xff0c;或是寻求更多专业、便捷的录屏软件选项&#xff0c;那么就请继续阅读吧。 1.福昕录屏大师 链…

清华大学开源视频转文本模型——CogVLM2-Llama3-Caption

通常情况下&#xff0c;大多数视频数据并不附带相应的描述性文本&#xff0c;因此有必要将视频数据转换为文本描述&#xff0c;为文本到视频模型提供必要的训练数据。 CogVLM2-Caption 是一个视频字幕模型&#xff0c;用于为 CogVideoX 模型生成训练数据。 文件 使用 import i…

应用层 II(文件传输协议FTP)【★★】

&#xff08;★★&#xff09;代表非常重要的知识点&#xff0c;&#xff08;★&#xff09;代表重要的知识点。 一、文件传输协议&#xff08;FTP&#xff09; 文件传送协议 FTP&#xff08;File Transfer Protocol&#xff09;是互联网上使用得最广泛的文件传送协议。FTP 提…

14年408-计算机网络

第一题&#xff1a; 解析&#xff1a;OSI体系结构 OSI由下至上依次是&#xff1a;物理层-网络链路层-网络层-运输层-会话层-表示层-应用层。 因此直接为会话层提供服务的是运输层。答案选C 第二题&#xff1a; 解析&#xff1a;数据链路层-交换机的自学习和帧转发 主机a1向交换…

零基础学Axios

Axios官网&#xff1a;Axios官网 想用Axios前需要在项目中安装axios,安装方式如下&#xff1a; 下列是axios请去方式&#xff0c;本文主要讲解post和get请求&#xff0c;其他请求和这两种请求方法相同。 1 get请求 1.1 不带请求参数 前端 后端 1.2 带请求参数 前端 写法…

动态切换数据库连接方案

文章目录 引言I 案例:手动切换数据库配置存储数据库配置信息书写切换脚本II 知识扩展使用sed命令替换文件内容激活环境配置文件引言 需求: 查询SQL Server数据库的时候可以根据实际情况分配服务器来提供服务,大大提高服务速度和优化性能,完成负载均衡。 方案:推荐从数据库…

菱形继承的类对父类的初始化、组合、多态、多态的原理等的介绍

文章目录 前言一、菱形继承的类对父类的初始化二、组合三、 多态1. 构成多态2. 虚函数3. 虚函数的重写4. 虚函数重写的两个例外1. 协变2. 析构函数的重写 5. C11 final 和 override1. final2. override 6. 设计不想被继承的类7. 重载、覆盖&#xff08;重写&#xff09;、 隐藏…

基于数据挖掘的航空客户满意度分析预测系统

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长 QQ 名片 :) 1. 项目简介 航空公司致力于提供多样化的服务以满足乘客需求&#xff0c;包括但不限于提供免费无线网络、免费食物饮品、提供网上预约服务、飞机出口位置、座椅舒适度、卫生状况等&#xff0c;并希望以此提升乘…

Linux系统CentOS下挂载磁盘

1. 挂载磁盘步骤总结如下 1. 对磁盘进行分区 2. 对磁盘进行格式化 3. 将磁盘挂载到对应目录 4. 设置开机自动挂载磁盘 2. 对磁盘进行分区 2.1 查看系统设备信息 lsblk指令显示所有块设备信息&#xff1a;显示系统中所有的块设备信息&#xff0c;包括磁盘和分区 lsblk 2…

Mapbox封装图形绘制工具 线,圆,polygon,删除,点 mapbox-gl-draw-circle mapbox-gl-draw

使用插件&#xff0c;安装 npm install mapbox-gl-draw-circle //绘制圆 npm install mapbox/mapbox-gl-draw //绘制点线面删除相关API地址&#xff1a;https://github.com/mohong/mapbox-gl-draw-circle https://github.com/mapbox/mapbox-gl-draw/blob/main/docs/API.md…

Unity 热更新(HybridCLR+Addressable)-设置打包路径和加载路径、打开Hosting服务、打包

四、设置打包和加载路径 五、打开Hosting服务 六、打包 打包完成后路径在Assets同级目录下的ServerData 但是目前没有资源文件对比 修改上面设置后再次打包 里面多了哈希和JSON文件&#xff0c;这俩个就是用于资源对比