搭建RTSP服务器

本文介绍如何使用 live555 搭建 RTSP 服务器,重点分析继承 FramedSource 类的实现方式。


一、简介

使用 live555 实现实时流推送,网上有很多例子,其结构大致都是一样的:

1.1 基本结构

  1. FramedSource 为基类,自定义一个子类,用于获取要推送的媒体流数据。在这个类中,一般使用 ffmpeg 将 raw video 编码为对应格式的 ES 流。

  2. 如果要实现的是多播场景(比如投屏、会议),那么实现一个 PassiveServerMediaSubsession 的实例,指定多播地址,主动调用 startPlay 来推送数据。

  3. 如果要实现的是单播场景(注意:RTP-over-UDP 和 RTP-over-TCP 都是单播),那么就要以 OnDemandServerMediaSubsession 为基类,自定义一个 SubSession,将自定义的 Source 关联到这个 SubSession 中。当访问这个 SubSession 时,就会创建对应的 Source 和 Sink,然后开始推送数据。

1.2 开发者需要做的工作

RTSP 服务器的核心部分(任务调度、数据接收与发送等)可以直接使用 live555 官方例子 testOnDemandRTSPServer 中的核心部分,开发者需要做的主要工作如下:

  1. FramedSource 为基类,自定义一个子类:用于获取要推送的媒体流数据。

  2. OnDemandServerMediaSubsession 为基类,自定义一个 SubSession:将上述自定义的 Source 关联到这个 SubSession 中。当访问这个 SubSession 时,就会创建对应的 Source 和 Sink,然后开始推送数据。

  3. 将自定义的 SubSession 加入到 ServerMediaSession


二、继承 FramedSource 类的分析

以项目 liveRtspServer 为例进行分析。该项目实现了:

  • 从摄像头获取 YUV 数据
  • 使用 x264 将 YUV 编码为 H264
  • 使用 live555 将 H264 推流

2.1 继承关系

该项目 FramedSource 派生的类有:

  • DevFramedSource
  • V4l2DevFramedSource
  • V4l2H264DevFramedSource

继承关系如下:

V4l2H264DevFramedSource 
        ↓
V4l2DevFramedSource 
        ↓
DevFramedSource 
        ↓
FramedSource

2.2 各个类的作用

类名 主要职责
V4l2H264DevFramedSource 主要负责编码
V4l2DevFramedSource 主要负责从摄像头获取数据
DevFramedSource 主要负责上层交互,上层通过该类中的接口来获取 H264 数据,进而将 H264 数据推送出去

2.3 类之间的联系

上述几个类存在继承关系,可实现动态多态,子类对父类提供功能。

父类定义接口,子类实现功能

// V4l2DevFramedSource 通过 createFrame() 函数为 DevFramedSource 提供 H264 数据
// createFrame() 在父类 DevFramedSource 中为纯虚函数
// 继承它的类"必须"实现 createFrame() 函数
// 达到子类为父类提供功能的目的

数据流转方式

V4l2H264DevFramedSource 通过 getFrame()encode()V4l2DevFramedSource 提供 H264 数据:

  1. V4l2H264DevFramedSource 将编码好的数据放在队列中
  2. V4l2DevFramedSource 先通过 getFrame() 从队列中获取 H264 数据
  3. 当队列中没有数据(即 getFrame() 返回 false)时,再调用 encode() 来编码

2.4 设计思想总结

这种分层设计的好处:

  1. 职责分离:数据采集、编码、推送各司其职
  2. 易于扩展:更换摄像头只需修改 V4l2DevFramedSource,更换编码器只需修改 V4l2H264DevFramedSource
  3. 复用性强DevFramedSource 可以被不同的上层应用复用

三、实现要点

3.1 FramedSource 核心方法

自定义 FramedSource 子类时,核心需要实现的方法是 doGetNextFrame()

virtual void doGetNextFrame();

当 live555 需要数据时,会调用此方法。开发者需要在此方法中:

  1. 填充 fTo 指向的缓冲区
  2. 设置 fFrameSize 为实际数据大小
  3. 调用 afterGetting(this) 通知框架数据已准备好

3.2 数据流向

摄像头/文件 → 自定义FramedSource → H264Framer → RTPSink → 网络

每一层只负责一个微小的任务,通过 Pipeline 模式串联起来。


参考资料

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐