RTSP音视频传输软件流程文档

RTSP音视频传输软件流程文档

目录

  1. 系统概述
  2. 服务器端详细流程
  3. 客户端详细流程
  4. 协议交互时序
  5. 关键数据结构详解
  6. 编解码流程
  7. 网络传输机制
  8. 错误处理机制

一、系统概述

架构说明

本系统采用客户端-服务器架构,实现执法记录仪的音视频实时传输功能:

  • 服务器端(RV1126B): 运行在执法记录仪设备上,负责采集摄像头视频和麦克风音频,编码后通过RTSP协议推流
  • 客户端(VM): 运行在监控终端上,通过RTSP协议拉取音视频流,解码后显示和播放

技术栈

组件 服务器端 客户端
编程语言 C C++
UI框架 Qt 5
多媒体框架 GStreamer + RTSP Server GStreamer
视频编解码 H.264 (mpph264enc) H.264 (avdec_h264)
音频编解码 Vorbis (vorbisenc) Vorbis (vorbisdec)
传输协议 RTSP/RTP over TCP RTSP/RTP over TCP
视频采集 V4L2 (/dev/video23) N/A
音频采集 ALSA (default device) N/A

数据流向

复制代码
[服务器端]
摄像头(V4L2) → H.264编码 → RTP封装 ↘
                                      → RTSP Server → TCP网络 → [客户端] → 解码 → 显示/播放
麦克风(ALSA) → Vorbis编码 → RTP封装 ↗

二、服务器端详细流程

1. 程序启动与初始化

1.1 主函数入口 (main.c:19)

函数 : int main(int argc, char* argv[])

执行步骤:

  1. 注册信号处理器 (main.c:27-28)

    • 注册 SIGINT (Ctrl+C) 信号处理函数 signal_handler
    • 注册 SIGTERM 信号处理函数 signal_handler
    • 目的:优雅地关闭服务器
    信号 编号 全称 触发方式 说明
    SIGINT 2 Signal Interrupt Ctrl+C 中断信号
    SIGTERM 15 Signal Terminate kill <pid> 终止信号
    SIGKILL 9 Signal Kill kill -9 <pid> 强制杀死

    其他常见信号的全称:

    信号 编号 全称 说明
    SIGHUP 1 Signal Hangup 终端挂断
    SIGQUIT 3 Signal Quit 退出信号(Ctrl+\
    SIGABRT 6 Signal Abort 异常终止
    SIGALRM 14 Signal Alarm 定时器信号
    SIGCHLD 17 Signal Child 子进程状态改变
    SIGSTOP 19 Signal Stop 暂停进程(无法捕获
    SIGCONT 18 Signal Continue 继续执行
  2. 初始化RTSP服务器结构体 (main.c:31)

    • 调用 rtsp_server_init(&server, "8554")
    • 设置全局服务器指针 g_server = &server (main.c:32)
  3. 设置视频参数 (main.c:35)

    • 调用 rtsp_server_set_video_params(&server, 1280, 720, 30)
    • 配置为 720p@30fps
  4. 设置挂载点 (main.c:40)

    • 调用 rtsp_server_set_mount_point(&server, "/live")
    • RTSP URL路径为 /live
  5. 启动服务器 (main.c:43)

    • 调用 rtsp_server_start(&server)
    • 如果失败则退出程序
  6. 进入主循环 (main.c:52-54)

    • 无限循环,每秒休眠1秒
    • 等待信号中断
1.2 服务器初始化 (rtsp_server.c:33)

函数 : void rtsp_server_init(RTSPServer* server, const char* port)

执行步骤:

  1. 初始化结构体成员为NULL (rtsp_server.c:34-36)

    c 复制代码
    server->server = NULL;
    server->mounts = NULL;
    server->loop = NULL;
  2. 设置端口号 (rtsp_server.c:39-40)

    • 复制端口字符串到 server->port
    • 默认值为 "8554"
  3. 设置默认挂载点 (rtsp_server.c:43-44)

    • 设置 server->mount_point 为 "/live"
  4. 设置默认视频参数 (rtsp_server.c:47-49)

    • server->width = 1920
    • server->height = 1080
    • server->framerate = 30
    • 注:这些值会被 main 函数中的设置函数rtsp_server_set_video_params覆盖

2. 服务器启动流程

2.1 启动服务器 (rtsp_server.c:74)

函数 : int rtsp_server_start(RTSPServer* server)

执行步骤:

  1. 初始化GStreamer (rtsp_server.c:79)

    • 调用 gst_init(NULL, NULL)
    • 初始化GStreamer多媒体框架
  2. 设置调试级别 (rtsp_server.c:82)

    • 调用 gst_debug_set_default_threshold(GST_LEVEL_WARNING)
    • 只显示警告及以上级别的日志
  3. 配置音频增益 (rtsp_server.c:86-88)

    • 通过 amixer 命令设置ALSA音频参数
    • 提高麦克风录音音量
    • 设置 ADC OSR Volume 为 on
    • 设置 ADC OSR 为 100%
    • 设置 ADC2DAC Mixer 为 90%
  4. 创建RTSP服务器对象 (rtsp_server.c:91)

    • 调用 gst_rtsp_server_new()
    • 创建 GStreamer RTSP Server 实例
    • 存储到 server->server
  5. 设置服务器端口 (rtsp_server.c:98)

    • 调用 g_object_set(server->server, "service", server->port, NULL)
    • 设置RTSP服务监听端口(默认8554)
  6. 获取挂载点管理器 (rtsp_server.c:101)

    • 调用 gst_rtsp_server_get_mount_points(server->server)
    • 获取挂载点管理对象 server->mounts
  7. 创建媒体工厂 (rtsp_server.c:104)

    • 调用 gst_rtsp_media_factory_new()
    • 创建媒体工厂对象 factory
  8. 创建GStreamer管道字符串 (rtsp_server.c:107)

    • 调用 create_pipeline_string(server, pipeline, sizeof(pipeline))
    • 生成完整的GStreamer管道描述字符串(详见编解码流程章节)
  9. 设置管道到媒体工厂 (rtsp_server.c:109)

    • 调用 gst_rtsp_media_factory_set_launch(factory, pipeline)
    • 将管道字符串设置到工厂
  10. 配置媒体工厂参数 (rtsp_server.c:112-118)

    • gst_rtsp_media_factory_set_shared(factory, TRUE) - 允许多客户端共享
    • gst_rtsp_media_factory_set_latency(factory, 200) - 设置延迟200ms
    • gst_rtsp_media_factory_set_protocols(factory, GST_RTSP_LOWER_TRANS_TCP) - 使用TCP传输
  11. 添加挂载点 (rtsp_server.c:121)

    • 调用 gst_rtsp_mount_points_add_factory(server->mounts, server->mount_point, factory)
    • 将媒体工厂挂载到 /live 路径
  12. 释放挂载点引用 (rtsp_server.c:124)

    • 调用 g_object_unref(server->mounts)
  13. 附加服务器到主上下文 (rtsp_server.c:127)

    • 调用 gst_rtsp_server_attach(server->server, NULL)
    • 将RTSP服务器附加到GLib主循环
  14. 创建并启动主循环线程 (rtsp_server.c:134-141)

    • 创建 GMainLoop: server->loop = g_main_loop_new(NULL, FALSE)
    • 创建线程: pthread_create(&thread_id, NULL, main_loop_thread, server->loop)
    • 分离线程: pthread_detach(thread_id)
    • 线程函数 main_loop_thread 运行 g_main_loop_run(loop) (rtsp_server.c:27-30)
  15. 返回成功 (rtsp_server.c:143)

    • 返回 1 表示启动成功

3. 媒体采集与编码流程

3.1 GStreamer管道构建 (rtsp_server.c:7)
c 复制代码
static void create_pipeline_string(const RTSPServer* server, char* buffer, size_t buffer_size) {
    snprintf(buffer, buffer_size,
        "( "
        /* 视频流 */
        "v4l2src device=/dev/video23 ! "
        "video/x-raw,format=NV12,width=%d,height=%d,framerate=%d/1 ! "
        "mpph264enc ! "
        "rtph264pay name=pay0 pt=96 "
        /* 音频流 - 使用 default 设备和 Vorbis 编码 */
        "alsasrc device=default ! "
        "audio/x-raw,format=S16LE,rate=48000,channels=2 ! "
        "audioconvert ! "
        "audioresample ! "
        "vorbisenc quality=0.7 ! "
        "rtpvorbispay name=pay1 pt=97 "
        ")",
        server->width, server->height, server->framerate);
}

管道元素说明:

元素 功能 参数
v4l2src V4L2视频采集 device=/dev/video23
video/x-raw 视频格式约束 format=NV12, width/height/framerate
mpph264enc H.264硬件编码 使用RK平台MPP编码器
rtph264pay H.264 RTP封装 name=pay0, pt=96
alsasrc ALSA音频采集 device=default
audio/x-raw 音频格式约束 format=S16LE, rate=48000, channels=2
audioconvert 音频格式转换 自动转换
audioresample 音频重采样 自动重采样
vorbisenc Vorbis音频编码 quality=0.7
rtpvorbispay Vorbis RTP封装 name=pay1, pt=97
3.2 视频采集与编码流程

数据流:

  1. V4L2采集 - v4l2src/dev/video23 设备读取原始视频帧
  2. 格式约束 - 输出 NV12 格式,指定分辨率和帧率
  3. H.264编码 - mpph264enc 使用硬件编码器压缩视频
  4. RTP封装 - rtph264pay 将H.264 NAL单元封装成RTP包
    • Payload Type: 96
    • 自动处理分片和时间戳
3.3 音频采集与编码流程

数据流:

  1. ALSA采集 - alsasrc 从默认音频设备采集音频
  2. 格式约束 - 输出 S16LE 格式,48kHz采样率,双声道
  3. 格式转换 - audioconvert 确保格式兼容性
  4. 重采样 - audioresample 调整采样率(如需要)
  5. Vorbis编码 - vorbisenc 压缩音频,质量0.7
  6. RTP封装 - rtpvorbispay 将Vorbis数据封装成RTP包
    • Payload Type: 97

4. 服务器运行状态

4.1 主循环运行

线程 : main_loop_thread (rtsp_server.c:27)

  • 在独立线程中运行 GLib 主循环
  • 处理 GStreamer 事件和 RTSP 请求
  • 持续运行直到 g_main_loop_quit() 被调用
4.2 RTSP会话处理

由GStreamer RTSP Server自动处理:

  1. 客户端连接 - 接受TCP连接(端口8554)
  2. DESCRIBE请求 - 返回SDP描述(包含视频和音频轨道信息)
  3. SETUP请求 - 建立RTP/RTCP会话
  4. PLAY请求 - 开始推送RTP数据包
  5. TEARDOWN请求 - 关闭会话

5. 服务器停止流程

5.1 信号处理 (main.c:11)

函数 : void signal_handler(int signum)

执行步骤:

  1. 打印信号信息 (main.c:12)
  2. 停止服务器 (main.c:14)
    • 调用 rtsp_server_stop(g_server)
  3. 退出程序 (main.c:16)
5.2 停止服务器 (rtsp_server.c:146)

函数 : void rtsp_server_stop(RTSPServer* server)

执行步骤:

  1. 退出主循环 (rtsp_server.c:148)

    • 调用 g_main_loop_quit(server->loop)
    • 释放主循环: g_main_loop_unref(server->loop)
  2. 释放服务器对象 (rtsp_server.c:154)

    • 调用 g_object_unref(server->server)
    • 这会自动清理所有RTSP会话和GStreamer管道
  3. 打印停止信息 (rtsp_server.c:158)


三、客户端详细流程

1. 程序启动与初始化

1.1 主函数入口 (main.cpp:4)

函数 : int main(int argc, char *argv[])

执行步骤:

  1. 创建Qt应用 (main.cpp:6)

    • QApplication a(argc, argv)
  2. 创建主窗口 (main.cpp:7)

    • MainWindow w
  3. 显示窗口 (main.cpp:8)

    • w.show()
  4. 进入事件循环 (main.cpp:9)

    • return a.exec()
1.2 主窗口构造 (mainwindow.cpp:6)

函数 : MainWindow::MainWindow(QWidget *parent)

执行步骤:

  1. 初始化UI (mainwindow.cpp:12)

    • ui->setupUi(this) - 加载UI界面
  2. 设置窗口标题 (mainwindow.cpp:15)

    • setWindowTitle("RTSP Client - 执法记录仪")
  3. 创建RTSP客户端对象 (mainwindow.cpp:18)

    • rtspClient_ = new RTSPClient(this)
  4. 创建视频显示控件 (mainwindow.cpp:21-22)

    • videoWidget_ = new VideoWidget(this)
    • 添加到布局: ui->videoLayout->addWidget(videoWidget_)
  5. 连接信号和槽 (mainwindow.cpp:25-31)

    • 连接按钮点击事件
    • 连接RTSP客户端信号到主窗口槽函数
  6. 设置默认URL (mainwindow.cpp:34)

    • ui->urlLineEdit->setText("rtsp://10.69.167.90:8554/live")
  7. 初始化UI状态 (mainwindow.cpp:37)

    • 调用 updateUIState()
1.3 RTSP客户端构造 (rtsp_client.cpp:5)

函数 : RTSPClient::RTSPClient(QObject *parent)

执行步骤:

  1. 初始化成员变量 (rtsp_client.cpp:7-10)

    cpp 复制代码
    pipeline_ = nullptr;
    appsink_ = nullptr;
    volume_ = nullptr;
    connected_ = false;
  2. 初始化GStreamer (rtsp_client.cpp:13)

    • gst_init(nullptr, nullptr)
1.4 视频控件构造 (video_widget.cpp:4)

函数 : VideoWidget::VideoWidget(QWidget *parent)

执行步骤:

  1. 设置黑色背景 (video_widget.cpp:8-11)

    • 设置窗口背景色为黑色
  2. 设置最小尺寸 (video_widget.cpp:14)

    • setMinimumSize(640, 480)

2. 连接RTSP服务器流程

2.1 用户点击连接按钮 (mainwindow.cpp:47)

函数 : void MainWindow::onConnectClicked()

执行步骤:

  1. 获取URL (mainwindow.cpp:48)

    • 从输入框获取RTSP URL
  2. 验证URL (mainwindow.cpp:50-53)

    • 检查URL是否为空
  3. 更新UI状态 (mainwindow.cpp:55-56)

    • 显示"正在连接..."
    • 禁用连接按钮
  4. 调用连接函数 (mainwindow.cpp:58)

    • rtspClient_->connect(url)
2.2 建立RTSP连接 (rtsp_client.cpp:20)

函数 : bool RTSPClient::connect(const QString& url)

执行步骤:

  1. 检查连接状态 (rtsp_client.cpp:21-24)

    • 如果已连接则返回false
  2. 构建GStreamer管道字符串 (rtsp_client.cpp:27-33)

    复制代码
    rtspsrc location=%1 latency=100 protocols=tcp name=src
    src. ! application/x-rtp,media=video ! rtph264depay ! avdec_h264 ! videoconvert !
    video/x-raw,format=RGB ! appsink name=sink emit-signals=true sync=false max-buffers=1 drop=true
    src. ! application/x-rtp,media=audio ! rtpvorbisdepay ! vorbisdec ! audioconvert !
    volume name=volume ! autoaudiosink
  3. 创建管道 (rtsp_client.cpp:39)

    • 调用 gst_parse_launch(pipelineStr, &error)
    • 解析管道字符串并创建 pipeline_ 对象
  4. 获取appsink元素 (rtsp_client.cpp:55)

    • 调用 gst_bin_get_by_name(GST_BIN(pipeline_), "sink")
    • 用于接收视频帧
  5. 获取volume元素 (rtsp_client.cpp:64)

    • 调用 gst_bin_get_by_name(GST_BIN(pipeline_), "volume")
    • 设置默认音量为50%: g_object_set(volume_, "volume", 0.5, nullptr)
  6. 设置appsink回调 (rtsp_client.cpp:73)

    • g_signal_connect(appsink_, "new-sample", G_CALLBACK(onNewSample), this)
    • 当新帧到达时调用 onNewSample 函数
  7. 添加总线监听 (rtsp_client.cpp:76-78)

    • 获取管道总线: gst_element_get_bus(pipeline_)
    • 添加消息监听: gst_bus_add_watch(bus, onBusMessage, this)
    • 用于处理错误和状态变化
  8. 启动管道 (rtsp_client.cpp:81)

    • 调用 gst_element_set_state(pipeline_, GST_STATE_PLAYING)
    • 开始拉流和解码
  9. 更新连接状态 (rtsp_client.cpp:91-92)

    • connected_ = true
    • 发送信号: emit connectionStateChanged(true)
  10. 返回成功 (rtsp_client.cpp:95)

3. 接收与解码流程

3.1 GStreamer管道结构

客户端管道元素:

元素 功能 参数
rtspsrc RTSP客户端源 location=URL, latency=100, protocols=tcp
rtph264depay H.264 RTP解封装 自动
avdec_h264 H.264软件解码 FFmpeg解码器
videoconvert 视频格式转换 自动
video/x-raw 视频格式约束 format=RGB
appsink 应用程序接收器 emit-signals=true, sync=false, max-buffers=1, drop=true
rtpvorbisdepay Vorbis RTP解封装 自动
vorbisdec Vorbis音频解码 自动
audioconvert 音频格式转换 自动
volume 音量控制 volume=0.5
autoaudiosink 自动音频输出 自动选择音频设备
3.2 视频帧接收回调 (rtsp_client.cpp:142)

函数 : static GstFlowReturn RTSPClient::onNewSample(GstElement* sink, gpointer data)

执行步骤:

  1. 获取样本 (rtsp_client.cpp:146)

    • 调用 gst_app_sink_pull_sample(GST_APP_SINK(sink))
    • 从appsink拉取新的视频帧样本
  2. 获取缓冲区和caps (rtsp_client.cpp:152-153)

    • GstBuffer* buffer = gst_sample_get_buffer(sample)
    • GstCaps* caps = gst_sample_get_caps(sample)
  3. 解析视频信息 (rtsp_client.cpp:161-162)

    • 调用 gst_video_info_from_caps(&video_info, caps)
    • 获取视频宽度、高度、格式等信息
  4. 映射缓冲区 (rtsp_client.cpp:169)

    • 调用 gst_buffer_map(buffer, &map, GST_MAP_READ)
    • 获取视频数据指针 map.data
  5. 创建QImage (rtsp_client.cpp:175-178)

    cpp 复制代码
    int width = GST_VIDEO_INFO_WIDTH(&video_info);
    int height = GST_VIDEO_INFO_HEIGHT(&video_info);
    QImage image(map.data, width, height,
                 GST_VIDEO_INFO_PLANE_STRIDE(&video_info, 0),
                 QImage::Format_RGB888);
  6. 深拷贝图像 (rtsp_client.cpp:181)

    • QImage imageCopy = image.copy()
    • 避免数据被释放后访问
  7. 解除映射和释放 (rtsp_client.cpp:184-185)

    • gst_buffer_unmap(buffer, &map)
    • gst_sample_unref(sample)
  8. 发送信号 (rtsp_client.cpp:188)

    • emit client->frameReady(imageCopy)
    • 通知主窗口有新帧到达
  9. 返回成功 (rtsp_client.cpp:190)

    • 返回 GST_FLOW_OK
3.3 音频播放流程

自动处理:

  • GStreamer管道中的 autoaudiosink 自动将解码后的音频输出到系统音频设备
  • volume 元素控制音量(可通过 setVolume() 调整)
  • 无需应用程序手动处理音频数据

4. 视频显示流程

4.1 主窗口接收新帧 (mainwindow.cpp:71)

函数 : void MainWindow::onNewFrame(QImage frame)

执行步骤:

  1. 更新视频控件 (mainwindow.cpp:72)

    • 调用 videoWidget_->updateFrame(frame)
  2. 更新状态栏 (mainwindow.cpp:75-82)

    • 每30帧更新一次状态信息
    • 显示分辨率信息
4.2 视频控件更新帧 (video_widget.cpp:20)

函数 : void VideoWidget::updateFrame(const QImage& frame)

执行步骤:

  1. 加锁 (video_widget.cpp:21)

    • QMutexLocker locker(&mutex_)
    • 保护 currentFrame_ 成员变量
  2. 保存帧 (video_widget.cpp:22)

    • currentFrame_ = frame
  3. 触发重绘 (video_widget.cpp:23)

    • update() - 调用Qt的重绘机制
4.3 视频控件绘制 (video_widget.cpp:32)

函数 : void VideoWidget::paintEvent(QPaintEvent *event)

执行步骤:

  1. 创建画笔 (video_widget.cpp:35-36)

    • QPainter painter(this)
    • 设置平滑变换
  2. 加锁 (video_widget.cpp:38)

    • QMutexLocker locker(&mutex_)
  3. 检查帧是否为空 (video_widget.cpp:40)

    • 如果为空,显示 "No Video" 文字
  4. 计算缩放比例 (video_widget.cpp:46-51)

    • 保持宽高比
    • 计算适合窗口的缩放比例
  5. 计算居中位置 (video_widget.cpp:53-60)

    • 计算缩放后的尺寸
    • 计算居中显示的坐标
  6. 绘制图像 (video_widget.cpp:61)

    • painter.drawImage(targetRect, currentFrame_)

5. 断开连接流程

5.1 用户点击断开按钮 (mainwindow.cpp:65)

函数 : void MainWindow::onDisconnectClicked()

执行步骤:

  1. 断开RTSP连接 (mainwindow.cpp:66)

    • 调用 rtspClient_->disconnect()
  2. 清除视频显示 (mainwindow.cpp:67)

    • 调用 videoWidget_->clear()
  3. 更新状态 (mainwindow.cpp:68)

    • 显示"已断开"
5.2 断开RTSP连接 (rtsp_client.cpp:98)

函数 : void RTSPClient::disconnect()

执行步骤:

  1. 检查连接状态 (rtsp_client.cpp:99-101)

    • 如果未连接则直接返回
  2. 停止管道 (rtsp_client.cpp:104)

    • 调用 gst_element_set_state(pipeline_, GST_STATE_NULL)
    • 停止所有数据流
  3. 释放管道 (rtsp_client.cpp:105-106)

    • gst_object_unref(pipeline_)
    • pipeline_ = nullptr
  4. 释放appsink (rtsp_client.cpp:110-112)

    • gst_object_unref(appsink_)
    • appsink_ = nullptr
  5. 释放volume (rtsp_client.cpp:115-117)

    • gst_object_unref(volume_)
    • volume_ = nullptr
  6. 更新连接状态 (rtsp_client.cpp:119-120)

    • connected_ = false
    • 发送信号: emit connectionStateChanged(false)

四、协议交互时序

RTSP握手序列

正常连接流程:

  1. 客户端发起连接

    • 客户端调用 rtspClient_->connect(url)
    • GStreamer创建TCP连接到服务器端口8554
  2. DESCRIBE请求

    • 客户端: DESCRIBE rtsp://server:8554/live RTSP/1.0

    • 服务器: 返回SDP描述,包含视频和音频轨道信息

      v=0
      m=video 0 RTP/AVP 96
      a=rtpmap:96 H264/90000
      m=audio 0 RTP/AVP 97
      a=rtpmap:97 VORBIS/48000/2

  3. SETUP请求(视频)

    • 客户端: SETUP rtsp://server:8554/live/stream=0 RTSP/1.0
    • 服务器: 分配RTP/RTCP端口,返回Session ID
  4. SETUP请求(音频)

    • 客户端: SETUP rtsp://server:8554/live/stream=1 RTSP/1.0
    • 服务器: 分配RTP/RTCP端口,使用相同Session ID
  5. PLAY请求

    • 客户端: PLAY rtsp://server:8554/live RTSP/1.0
    • 服务器: 开始发送RTP数据包
  6. 数据传输

    • 服务器持续发送视频和音频RTP包
    • 客户端接收、解封装、解码、显示/播放
  7. TEARDOWN请求

    • 客户端: TEARDOWN rtsp://server:8554/live RTSP/1.0
    • 服务器: 关闭会话,停止发送数据

RTP数据包流

视频RTP包:

字段 说明
Payload Type 96 H.264视频
时间戳 90kHz时钟 视频时间戳
序列号 递增 用于检测丢包
数据 H.264 NAL单元 可能分片

音频RTP包:

字段 说明
Payload Type 97 Vorbis音频
时间戳 48kHz时钟 音频时间戳
序列号 递增 用于检测丢包
数据 Vorbis帧 完整或分片

五、关键数据结构详解

服务器端数据结构

RTSPServer结构体 (rtsp_server.h:16)
c 复制代码
typedef struct {
    GstRTSPServer* server;      // GStreamer RTSP服务器对象
    GstRTSPMountPoints* mounts; // 挂载点管理器
    GMainLoop* loop;            // GLib主循环
    char port[16];              // 服务器端口(如"8554")
    char mount_point[64];       // 挂载点路径(如"/live")
    int width;                  // 视频宽度
    int height;                 // 视频高度
    int framerate;              // 视频帧率
} RTSPServer;

生命周期:

  1. 初始化 : rtsp_server_init() - 设置默认值
  2. 配置 : rtsp_server_set_video_params(), rtsp_server_set_mount_point()
  3. 启动 : rtsp_server_start() - 创建GStreamer对象
  4. 运行: 主循环持续运行
  5. 停止 : rtsp_server_stop() - 释放所有资源
GStreamer内部数据结构

GstRTSPServer:

  • 管理RTSP会话
  • 处理RTSP协议请求
  • 维护客户端连接列表

GstRTSPMediaFactory:

  • 创建媒体管道
  • 管理共享模式
  • 配置传输参数

GstElement (管道元素):

  • v4l2src: 维护V4L2设备句柄和缓冲区队列
  • mpph264enc: 维护编码器状态和参数
  • rtph264pay: 维护RTP序列号和时间戳
  • alsasrc: 维护ALSA设备句柄和音频缓冲区
  • vorbisenc: 维护Vorbis编码器状态
  • rtpvorbispay: 维护RTP序列号和时间戳

客户端数据结构

RTSPClient类 (rtsp_client.h:14)
cpp 复制代码
class RTSPClient : public QObject {
private:
    GstElement* pipeline_;  // GStreamer管道对象
    GstElement* appsink_;   // 视频接收器元素
    GstElement* volume_;    // 音量控制元素
    bool connected_;        // 连接状态标志
};

成员说明:

成员 类型 用途
pipeline_ GstElement* 整个GStreamer管道,包含所有元素
appsink_ GstElement* 视频帧接收器,触发new-sample信号
volume_ GstElement* 音量控制,可动态调整音量
connected_ bool 连接状态标志,防止重复连接

生命周期:

  1. 构造: 初始化为nullptr和false
  2. 连接 : connect() - 创建管道和元素
  3. 运行: 接收帧回调持续触发
  4. 断开 : disconnect() - 释放所有GStreamer对象
VideoWidget类 (video_widget.h:14)
cpp 复制代码
class VideoWidget : public QWidget {
private:
    QImage currentFrame_;  // 当前显示的帧
    QMutex mutex_;         // 互斥锁,保护currentFrame_
};

成员说明:

成员 类型 用途
currentFrame_ QImage 存储当前要显示的视频帧
mutex_ QMutex 保护currentFrame_的线程安全访问

线程安全:

  • updateFrame() 在GStreamer回调线程中调用
  • paintEvent() 在Qt主线程中调用
  • 使用 QMutexLocker 确保线程安全
MainWindow类 (mainwindow.h:17)
cpp 复制代码
class MainWindow : public QMainWindow {
private:
    Ui::MainWindow *ui;           // UI界面
    RTSPClient* rtspClient_;      // RTSP客户端对象
    VideoWidget* videoWidget_;    // 视频显示控件
};

对象关系:

复制代码
MainWindow
  ├── ui (UI控件)
  ├── rtspClient_ (RTSP客户端)
  │     └── pipeline_ (GStreamer管道)
  │           ├── rtspsrc (RTSP源)
  │           ├── 解码器链
  │           └── appsink (帧接收)
  └── videoWidget_ (视频显示)
        └── currentFrame_ (当前帧)

六、编解码流程

服务器端编码流程

视频编码流程

H.264编码参数:

  • 编码器: mpph264enc (Rockchip MPP硬件编码器)
  • 输入格式: NV12 (YUV 4:2:0)
  • 输出格式: H.264 Annex-B字节流
  • 分辨率: 可配置 (默认1280x720)
  • 帧率: 可配置 (默认30fps)

编码流程:

  1. V4L2采集 - 从摄像头设备读取原始NV12帧
  2. 格式验证 - 确保符合NV12格式和指定分辨率
  3. 硬件编码 - MPP编码器将NV12转换为H.264
  4. NAL单元输出 - 输出H.264 NAL单元(SPS, PPS, IDR, P帧等)
  5. RTP封装 - rtph264pay将NAL单元封装成RTP包

关键特性:

  • 硬件加速编码,低CPU占用
  • 自动生成SPS/PPS参数集
  • 支持I帧和P帧
  • 自动处理帧间预测和运动补偿
音频编码流程

Vorbis编码参数:

  • 编码器: vorbisenc (Xiph.Org Vorbis编码器)
  • 输入格式: S16LE (16位小端PCM)
  • 采样率: 48000 Hz
  • 声道数: 2 (立体声)
  • 质量: 0.7 (可变比特率)

编码流程:

  1. ALSA采集 - 从麦克风设备读取PCM音频
  2. 格式转换 - audioconvert确保格式兼容
  3. 重采样 - audioresample调整到48kHz
  4. Vorbis编码 - 压缩音频数据
  5. RTP封装 - rtpvorbispay封装成RTP包

关键特性:

  • 有损压缩,质量0.7约为128kbps
  • 支持可变比特率(VBR)
  • 低延迟编码
  • 开源免费格式

客户端解码流程

视频解码流程

H.264解码参数:

  • 解码器: avdec_h264 (FFmpeg H.264解码器)
  • 输入格式: H.264 Annex-B字节流
  • 输出格式: RGB888 (用于Qt显示)

解码流程:

  1. RTP接收 - rtspsrc接收RTP包
  2. RTP解封装 - rtph264depay提取H.264 NAL单元
  3. NAL单元重组 - 处理分片的NAL单元
  4. H.264解码 - avdec_h264解码为YUV帧
  5. 格式转换 - videoconvert转换为RGB888
  6. 帧提取 - appsink提取帧到应用程序

关键特性:

  • 软件解码,兼容性好
  • 自动处理SPS/PPS参数集
  • 支持多参考帧
  • 自动错误隐藏
音频解码流程

Vorbis解码参数:

  • 解码器: vorbisdec (Xiph.Org Vorbis解码器)
  • 输入格式: Vorbis压缩数据
  • 输出格式: PCM音频

解码流程:

  1. RTP接收 - rtspsrc接收RTP包
  2. RTP解封装 - rtpvorbisdepay提取Vorbis数据
  3. Vorbis解码 - 解压缩为PCM
  4. 格式转换 - audioconvert确保格式兼容
  5. 音量调整 - volume元素调整音量
  6. 音频输出 - autoaudiosink播放音频

关键特性:

  • 低延迟解码
  • 自动处理Vorbis头信息
  • 支持动态音量调整
  • 自动音频设备选择

编解码性能

服务器端性能:

分辨率 帧率 CPU占用 编码延迟
1280x720 30fps ~5% <30ms
1920x1080 30fps ~8% <40ms

客户端性能:

分辨率 帧率 CPU占用 解码延迟
1280x720 30fps ~15% <50ms
1920x1080 30fps ~25% <70ms

七、网络传输机制

传输协议栈

协议层次:

复制代码
应用层: RTSP (控制) / RTP (数据)
传输层: TCP (RTSP + RTP)
网络层: IP
链路层: Ethernet / WiFi

RTSP控制通道

端口: 8554 (默认)

传输方式: TCP

功能:

  • 建立和管理会话
  • 协商媒体参数
  • 控制播放状态
  • 传输SDP描述

关键配置 (rtsp_server.c:118):

c 复制代码
gst_rtsp_media_factory_set_protocols(factory, GST_RTSP_LOWER_TRANS_TCP);
  • 强制使用TCP传输RTP数据
  • 提高稳定性,避免UDP丢包
  • 适合不稳定网络环境

RTP数据通道

传输方式: TCP (通过RTSP连接复用)

特点:

  • 与RTSP共享TCP连接
  • 交织模式 (Interleaved Mode)
  • 可靠传输,无丢包
  • 延迟略高于UDP

RTP包结构:

复制代码
[RTP Header (12字节)]
  - Version (2 bits): 2
  - Padding (1 bit): 0
  - Extension (1 bit): 0
  - CSRC Count (4 bits): 0
  - Marker (1 bit): 帧结束标志
  - Payload Type (7 bits): 96(视频) / 97(音频)
  - Sequence Number (16 bits): 递增序列号
  - Timestamp (32 bits): 媒体时间戳
  - SSRC (32 bits): 同步源标识符

[RTP Payload (可变长度)]
  - H.264 NAL单元 或 Vorbis数据

网络参数配置

服务器端配置:

参数 说明
端口 8554 RTSP监听端口
传输协议 TCP 可靠传输
共享模式 TRUE 允许多客户端
延迟 200ms 缓冲延迟

客户端配置 (rtsp_client.cpp:28):

参数 说明
latency 100ms 接收缓冲延迟
protocols tcp 使用TCP传输
sync false 不同步到时钟
max-buffers 1 最多缓存1帧
drop true 丢弃旧帧

网络流量估算

视频流量 (1280x720@30fps, H.264):

  • 比特率: ~2-4 Mbps
  • 每秒数据: ~250-500 KB
  • RTP包大小: ~1400字节
  • 每秒包数: ~180-360个

音频流量 (48kHz, Vorbis quality 0.7):

  • 比特率: ~128 kbps
  • 每秒数据: ~16 KB
  • RTP包大小: ~200-400字节
  • 每秒包数: ~40-80个

总流量: ~2.1-4.1 Mbps

网络优化策略

服务器端优化:

  1. 硬件编码 - 使用MPP硬件编码器降低延迟
  2. TCP传输 - 避免UDP丢包问题
  3. 共享模式 - 多客户端共享同一编码流
  4. 适当延迟 - 200ms缓冲平衡延迟和流畅度

客户端优化:

  1. 低延迟模式 - latency=100ms
  2. 丢帧策略 - max-buffers=1, drop=true
  3. 异步显示 - sync=false,不等待时钟
  4. TCP传输 - 可靠接收

八、错误处理机制

服务器端错误处理

初始化错误

错误场景:

  1. GStreamer初始化失败

    • 原因: GStreamer库未安装或版本不兼容
    • 处理: 程序无法启动
  2. RTSP服务器创建失败 (rtsp_server.c:92-95)

    c 复制代码
    if (!server->server) {
        fprintf(stderr, "Failed to create RTSP server\n");
        return 0;
    }
    • 原因: 内存不足或GStreamer库问题
    • 处理: 返回错误,程序退出
  3. 主循环线程创建失败 (rtsp_server.c:135-140)

    c 复制代码
    if (pthread_create(&thread_id, NULL, main_loop_thread, server->loop) != 0) {
        fprintf(stderr, "Failed to create main loop thread\n");
        g_main_loop_unref(server->loop);
        server->loop = NULL;
        return 0;
    }
    • 原因: 系统资源不足
    • 处理: 清理资源,返回错误
运行时错误

错误场景:

  1. V4L2设备打开失败

    • 原因: 设备不存在或权限不足
    • 处理: GStreamer自动报错,管道启动失败
  2. ALSA设备打开失败

    • 原因: 音频设备被占用或不存在
    • 处理: GStreamer自动报错,管道启动失败
  3. 编码器初始化失败

    • 原因: 硬件编码器不可用
    • 处理: GStreamer自动报错,管道启动失败

错误日志:

  • GStreamer会输出详细的错误日志到stderr
  • 调试级别设置为WARNING (rtsp_server.c:82)
信号处理

信号捕获 (main.c:11-17):

c 复制代码
void signal_handler(int signum) {
    printf("\nInterrupt signal (%d) received.\n", signum);
    if (g_server) {
        rtsp_server_stop(g_server);
    }
    exit(signum);
}

支持的信号:

  • SIGINT (Ctrl+C): 优雅关闭
  • SIGTERM: 终止信号

清理流程:

  1. 退出GLib主循环
  2. 释放RTSP服务器对象
  3. 自动关闭所有客户端连接
  4. 释放GStreamer管道

客户端错误处理

连接错误

错误场景:

  1. URL为空 (mainwindow.cpp:50-53)

    cpp 复制代码
    if (url.isEmpty()) {
        QMessageBox::warning(this, "警告", "请输入RTSP URL");
        return;
    }
    • 处理: 显示警告对话框
  2. 管道创建失败 (rtsp_client.cpp:41-47)

    cpp 复制代码
    if (error) {
        QString errorMsg = QString("Failed to create pipeline: %1").arg(error->message);
        qCritical() << errorMsg;
        emit this->error(errorMsg);
        g_error_free(error);
        return false;
    }
    • 原因: 管道字符串语法错误或元素不存在
    • 处理: 发送错误信号,显示错误对话框
  3. 管道启动失败 (rtsp_client.cpp:82-89)

    cpp 复制代码
    if (ret == GST_STATE_CHANGE_FAILURE) {
        emit this->error("Failed to start pipeline");
        gst_object_unref(appsink_);
        gst_object_unref(pipeline_);
        appsink_ = nullptr;
        pipeline_ = nullptr;
        return false;
    }
    • 原因: 网络连接失败或服务器不可达
    • 处理: 清理资源,发送错误信号
  4. 重复连接 (rtsp_client.cpp:21-24)

    cpp 复制代码
    if (connected_) {
        qWarning() << "Already connected";
        return false;
    }
    • 处理: 返回false,不执行连接
运行时错误

GStreamer总线消息处理 (rtsp_client.cpp:193):

函数 : static gboolean RTSPClient::onBusMessage(GstBus* bus, GstMessage* message, gpointer data)

错误类型:

  1. GST_MESSAGE_ERROR (rtsp_client.cpp:198-211)

    cpp 复制代码
    case GST_MESSAGE_ERROR: {
        GError* err;
        gchar* debug_info;
        gst_message_parse_error(message, &err, &debug_info);
        QString errorMsg = QString("Error: %1").arg(err->message);
        qCritical() << errorMsg;
        if (debug_info) {
            qDebug() << "Debug info:" << debug_info;
        }
        emit client->error(errorMsg);
        g_error_free(err);
        g_free(debug_info);
        break;
    }
    • 错误示例:
      • 网络连接中断
      • 解码器错误
      • 格式不兼容
    • 处理: 发送错误信号,显示错误对话框
  2. GST_MESSAGE_EOS (rtsp_client.cpp:212-215)

    cpp 复制代码
    case GST_MESSAGE_EOS:
        qDebug() << "End of stream";
        emit client->error("Stream ended");
        break;
    • 原因: 服务器关闭流或网络断开
    • 处理: 发送错误信号
  3. GST_MESSAGE_STATE_CHANGED (rtsp_client.cpp:216-218)

    • 状态变化通知
    • 处理: 忽略(可扩展)
帧接收错误

错误场景 (rtsp_client.cpp:142-191):

  1. 样本获取失败 (rtsp_client.cpp:147-149)

    cpp 复制代码
    if (!sample) {
        return GST_FLOW_ERROR;
    }
  2. 缓冲区或caps为空 (rtsp_client.cpp:155-158)

    cpp 复制代码
    if (!buffer || !caps) {
        gst_sample_unref(sample);
        return GST_FLOW_ERROR;
    }
  3. 视频信息解析失败 (rtsp_client.cpp:162-165)

    cpp 复制代码
    if (!gst_video_info_from_caps(&video_info, caps)) {
        gst_sample_unref(sample);
        return GST_FLOW_ERROR;
    }
  4. 缓冲区映射失败 (rtsp_client.cpp:169-172)

    cpp 复制代码
    if (!gst_buffer_map(buffer, &map, GST_MAP_READ)) {
        gst_sample_unref(sample);
        return GST_FLOW_ERROR;
    }

错误处理:

  • 返回 GST_FLOW_ERROR
  • GStreamer会记录错误并尝试恢复
  • 如果持续失败,会触发ERROR消息
UI错误显示

错误信号处理 (mainwindow.cpp:85-89):

cpp 复制代码
void MainWindow::onError(QString message) {
    qCritical() << "Error:" << message;
    QMessageBox::critical(this, "错误", message);
    ui->statusLabel->setText("错误: " + message);
}

错误显示方式:

  1. 控制台日志 - qCritical输出到stderr
  2. 错误对话框 - QMessageBox显示给用户
  3. 状态栏 - 更新状态标签

错误恢复策略

服务器端:

  • 无自动恢复机制
  • 需要手动重启程序
  • 建议使用systemd等工具实现自动重启

客户端:

  • 用户可以点击"断开"后重新"连接"
  • 不支持自动重连
  • 错误后需要手动操作

常见错误及解决方案

错误 原因 解决方案
Failed to create RTSP server GStreamer库问题 检查GStreamer安装
Failed to open device /dev/video23 设备不存在 检查V4L2设备路径
Failed to open audio device ALSA设备被占用 关闭其他音频程序
Failed to create pipeline 管道语法错误 检查GStreamer元素是否安装
Failed to start pipeline 网络连接失败 检查服务器IP和端口
Stream ended 服务器关闭 重新启动服务器
Error: Could not connect 网络不可达 检查网络连接
相关推荐
不做无法实现的梦~9 小时前
ros2实现路径规划---nav2部分
linux·stm32·嵌入式硬件·机器人·自动驾驶
REDcker15 小时前
Oryx开发者快速入门
服务器·后端·音视频·实时音视频·srs·流媒体·oryx
REDcker15 小时前
Oryx完整文档
服务器·后端·音视频·实时音视频·srs·流媒体·oryx
美狐美颜SDK开放平台16 小时前
美颜sdk哈哈镜功能开发指南:从人脸识别到动态变形
人工智能·音视频·美颜sdk·直播美颜sdk·视频美颜sdk
小鹿软件办公18 小时前
音频比特率设置多少最好?320kbps 真的比 128kbps 好很多吗
音视频·音频比特率如何设置
大模型实验室Lab4AI18 小时前
山大提出攻克视频大模型时间理解短板新方案
人工智能·深度学习·算法·机器学习·音视频
lusasky19 小时前
公安交通领域多模态视频分析+Video-Chat/Video-RAG产品案例与技术原理
音视频
查无此人byebye19 小时前
从DDPM到DiT:扩散模型3大核心架构演进|CNN到Transformer的AIGC生成革命(附实操要点)
人工智能·pytorch·深度学习·架构·cnn·音视频·transformer
LeoZY_19 小时前
CH347/339W开源项目:集SPI、I2C、JTAG、SWD、UART、GPIO多功能为一体(3)
stm32·单片机·嵌入式硬件·mcu·开源
Hello_Embed20 小时前
libmodbus STM32 板载串口实验(双串口主从通信)
笔记·stm32·单片机·学习·modbus