# RtspPublisherClient **Repository Path**: AndroidCoderPeng/RtspPublisherClient ## Basic Information - **Project Name**: RtspPublisherClient - **Description**: 基于 Qt5 + OpenCV + FFmpeg (x264) + live555 的低延迟 RTSP 推流桌面客户端: USB 相机采集 → 本地预览 → H.264 软编码 → live555 单播推流,支持画面水印与实时预览 - **Primary Language**: C++ - **License**: MulanPSL-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-05 - **Last Updated**: 2026-09-05 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # RtspPublisherClient 基于 Qt5 + OpenCV + FFmpeg (x264) + live555 的低延迟 RTSP 推流桌面客户端: USB 相机采集 → 本地预览 → H.264 软编码 → live555 单播推流,支持画面水印与实时预览。 ## 特性 - **采集**:OpenCV `VideoCapture`(Windows 走 DSHOW,Linux 走 V4L2),MJPEG 输入,实时统计相机实际帧率 - **预览**:独立预览线程,环形缓冲 + `QImage` 显示,不阻塞采集与编码 - **编码**:FFmpeg libx264,`preset=ultrafast` + `tune=zerolatency`,无 B 帧,GOP = 帧率 - **推流**:live555 `OnDemandServerMediaSubsession` + `H264VideoStreamDiscreteFramer`,单客户端/多客户端均支持 - **水印**:画面左上角半透明底条 + 白字,机构信息与实时时间,可开关 - **低延迟**:帧队列上限 30 帧(约 1 秒),落后时跳到最近的 **最新**关键帧续播,不累积延迟 ## 目录结构 ``` RtspPublisherClient ├── main.cpp 入口 ├── PublisherApplication.* 应用启动 ├── ui/ MainWindow(GUI、线程装配、URL 复制、水印开关) ├── video/ │ ├── FrameCapture.* 相机采集(采集线程) │ ├── PreviewWorker.* 本地预览(预览线程) │ ├── FrameEncoder.* H.264 编码 + 水印(编码线程) │ └── H264Splitter.* AnnexB / AVCC 解析、SPS/PPS 提取、NAL 拆分 ├── rtsp/ │ ├── RtspStreamWorker.* 推流工作对象(推流线程,Qt 信号入口) │ ├── RtspManager.* 帧分发、SPS/PPS 管理、PTS 归一化、启动 RTSP 服务 │ └── LiveRtspServer.* live555 RTSPServer 封装(自带事件循环线程) ├── server/ │ ├── LiveServerMediaSubsession.* live555 会话与 Sink 装配 │ └── LiveFramedSource.* live555 帧源(从 FrameQueue 拉帧) ├── utils/ │ ├── FrameQueue.* 生产者/消费者帧队列(多 Reader 游标) │ ├── Logger.* 分级边框日志 │ └── GlobalDefinition.hpp 全局常量、状态码、H264Frame └── 3rdparty/ Qt / OpenCV / FFmpeg / x264 / live555 ``` ## 数据流向图(纵向) ```mermaid flowchart TD subgraph T1["采集线程"] A["USB 相机 MJPEG"] B["FrameCapture 循环 read 帧"] C["信号 frameCaptured
共享指针 cv::Mat"] end subgraph T2["预览线程"] D["PreviewWorker 环形缓冲"] E["cv::resize 缩放
BGR 转 RGB"] F["信号 onPreviewReady QImage"] end subgraph T3["编码线程"] G["FrameEncoder 环形缓冲"] H["drawWatermark 水印
机构信息 + 实时时间"] I["sws_scale BGR 转 YUV420P"] J["libx264 编码
avcodec send/receive"] K["信号 onFrameEncode
H264 + PTS + 关键帧标记"] end subgraph T4["推流线程"] L["RtspStreamWorker onEncodedFrame"] M["RtspManager dispatchFrame"] N["H264Splitter
AVCC 转 AnnexB
提取 SPS/PPS"] O["sendVideoFrame
PTS 归一化 90kHz 单调递增"] end P[("FrameQueue 帧队列
上限 30 帧约 1 秒
满则丢弃最老帧")] subgraph T5["live555 事件循环线程"] Q["LiveFramedSource deliverFromQueue"] R["落后时跳到最新关键帧
帧内 NAL 连续发送
帧间按真实 PTS 间隔节流"] S["H264VideoStreamDiscreteFramer"] T["H264VideoRTPSink 组 RTP 包"] end subgraph T6["主线程 GUI"] U["MainWindow videoPreview 显示"] V["fps / RTSP URL / 水印开关"] end W["RTSP 控制 + RTP over TCP 或 UDP"] X["播放端 ffplay / VLC"] A --> B --> C C -->|QueuedConnection| D C -->|QueuedConnection| G D --> E --> F --> U V -.->|水印开关| H G --> H --> I --> J --> K K -->|跨线程信号| L --> M --> N --> O --> P P --> Q --> R --> S --> T --> W --> X ``` ## 线程模型 | 线程 | 载体 | 职责 | |----------------------|--------------------|------------------------------------------| | 主线程 GUI | `MainWindow` | 界面显示、按钮、水印开关、URL 复制 | | 采集线程 | `FrameCapture` | 循环 `read()` 相机帧,统计实际帧率 | | 预览线程 | `PreviewWorker` | 缩放、色彩转换、发给 UI 显示 | | 编码线程 | `FrameEncoder` | 水印绘制、像素格式转换、x264 编码 | | 推流线程 | `RtspStreamWorker` | 接收编码帧、调用 `RtspManager`、状态回传 | | live555 事件循环线程 | `LiveRtspServer` | `doEventLoop()`,RTP 组包与发送 | 编码线程与推流线程之间走 Qt 队列信号,不经过 GUI 主线程,避免界面卡顿影响帧通路。 ## 低延迟要点 1. **帧间节流跟随真实 PTS**:live555 帧源的 `fDurationInMicroseconds` 使用上一帧与当前帧的真实 PTS 间隔(钳制 5~100 ms),而不是写死 `1000000/fps`。软编码实际帧率常为 29.8 fps 而非 30,写死节流会产生漂移积压,导致队列周期性顶满丢帧。 2. **同帧 NAL 连续发送**:一帧拆出的多个 NAL(如 SPS+PPS+IDR)之间 `duration = 0`,只在帧末尾 NAL 才按帧间隔节流,避免关键帧发送被拉长为 3 倍耗时。 3. **落后重同步跳最新关键帧**:消费者落后时跳到帧队列中 **离队尾最近**的关键帧,而不是最老的关键帧。`pop()` 不删除帧,若跳最老关键帧会反复交付同一帧陈旧数据,直到它被挤出队列,表现为约 1 秒一跳的卡顿。 4. **PTS 单调递增**:以真实墙钟为 PTS 来源,90 kHz 归一化后强制递增,避免播放端时间戳回退花屏。 > 如果播放端仍偶发停顿,可对比 TCP 与 UDP: > TCP 为单线程事件循环内的 `send()`,对端读取不及时会阻塞整条链路;UDP 的 `sendto()` 不会阻塞。 ## 构建 依赖(Windows / MinGW):Qt 5.15.2、OpenCV 4.5.5、FFmpeg 静态库、x264、live555,均放置于 `3rdparty/`。 第三方编译脚本见 `script/`。 `CMakeLists.txt` 中 Qt 路径按本机环境调整: ```cmake set(CMAKE_PREFIX_PATH "D:\\3rdparty\\Qt5.15\\5.15.2\\mingw81_64") ``` 命令行构建: ```bash cmake -S . -B build -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=Release cmake --build build -j ``` 也可直接用 CLion 打开工程构建。构建后会自动拷贝 Qt 平台插件 `qwindows.dll`、Qt5Core/Gui/Widgets 动态库与 `libopencv_world455.dll` 到可执行目录。 ## 运行与拉流 启动程序后点击推流按钮,界面会显示 RTSP 地址(形如 `rtsp://192.168.x.x:8554/live`)。 低延迟拉流: ```bash # TCP ffplay -fflags nobuffer -flags low_delay -framedrop -analyzeduration 0 -probesize 32 -rtsp_transport tcp rtsp://192.168.3.131:8554/live # UDP(live555 默认,不受 TCP 发送阻塞影响) ffplay -fflags nobuffer -flags low_delay -framedrop rtsp://192.168.3.131:8554/live ``` ## 编码规范 - 函数、变量、参数、成员:小驼峰(`deliverNextNal`、`_frameBuffer`、`ptsUs`) - 宏与编译期常量:全大写下划线(`VIDEO_FPS`、`LOG_TAG`、`TOP_LEFT`) - live555 / Qt / FFmpeg 的框架与库符号保持原名(`doGetNextFrame`、`avcodec_send_frame` 等)