基本概念
RTSP:Real Time Streaming Protocol,实时流传输协议,注意:它类似与FTP协议里的控制端口21,并不处理视频流,视频流承载在RTP协议里。
RTP:Real Transport Protocol,实时传输协议,负责将编码后的视频流打包成适合网络传输的数据包
H264/H265,MPEG-4最新的视频格式,它是视频的存储格式,不用考虑网络传输,网络传输由RTP负责。
ONVIF 标准协议:它比RTSP功能更全面,能统一发现和管理不同品牌的摄像头,并获取它们的RTSP地址。这相当于在RTSP之上,加了一层更智能的"发现和管理"能力。
主码流:分辨率高、码率大、画质清晰。它的主要任务就是用于本地高清录像和存储,为事后回放提供最清晰的证据。追求画质的存储首选。
子码流:分辨率低、码率小、占用带宽少。它主要用于在手机APP或远程客户端上进行实时预览,确保在网络环境不佳时,画面依然能够保持流畅,不会出现卡顿。它优先保障的是观看的流畅性。
网络摄像机(IPC)
硬盘录像机(NVR)
RTSP地址
rtsp://xx:xx@192.168.1.30:554/Streaming/Channels/102 -vf "vflip"
路径中的 102 通常遵循"通道号 + 码流类型"的编码方式。
- 第一位(
1) :表示通道号。在大多数海康设备中,1代表主通道(即摄像头本身)。 - 第二位(
01或02) :用于指定码流类型。01:代表主码流。画质最高,适合本地录像存储。02:代表子码流。分辨率较低,适合远程预览以节省带宽。03:代表第三码流。某些型号支持,用于额外的多流需求。
因此,102 明确指向了 "第 1 通道的子码流"。
-vf "vflip" 是一个视频滤镜参数:
-vf:是-filter:v的缩写,用于应用视频滤镜。vflip:表示"垂直翻转(Vertical Flip)"。它会将视频画面上下颠倒,通常是因为摄像头是倒置安装的(例如吸顶安装)。
rtsp://xx:xx@192.168.1.168:554/Streaming/Channels/102?transportmode=unicast
transportmode=multicast 和 unicast 是 RTSP 地址中用来指定视频流数据在网络中的传输方式的参数。
简单来说,Unicast(单播)是"点对点"传输 ,服务器为每个客户端单独发送一份数据;Multicast(组播)是"点对多点"传输,服务器只发送一份数据,网络设备(如交换机)负责复制给多个客户端。
补充说明:
上述地址是预览流,还有一种/Streaming/tracks的rtsp地址,用于回放流。
工具
python有个rtsp-network-scanner工具,能扫描某个ip的rtsp流清单:
rtsp-network-scanner scan 192.168.1.100 -u admin -p xxx
还有一个更有效的方法是,下载VLC播放器,在"打开网络串"里输入rtsp流,如果该流正常,就能播放。VLC还能把rtsp流转成本地mp4。
onvif如何发现设备RTSP地址
要求设备支持onvif协议, 先通过WS-Discovery 网络协议(onvif协议的一部分)发现设备,再向设备询问RTSP地址,设备返回自身的RTSP地址。
onvif还可以通过PTZ(云台控制)来旋转摄像头。
工具
python有onvif-scanner包,pip安装后用下面命令扫描局域网:
onvif-scan
HLS协议
HLS 是 HTTP Live Streaming 的缩写。这是一个由苹果公司(Apple)在 2009 年提出的基于 HTTP 的自适应码率流媒体网络传输协议。它通过将整个视频流切割成一系列基于 HTTP 的小文件(通常是 .ts 格式)来传输,并生成一个 .m3u8 播放列表文件供客户端按顺序请求播放。
m3u8是播放列表,ts是真正的视频片段。
访问海康摄像头
预览
我们需要实时预览摄像头,并在web上展示,可用如下方案:
- RTSP拉子码流(节省带宽),用ffmpeg转成hls存到nginx目录下,前端用hls.js在web上播放。
用C#拼接的处理预览流的ffmpeg命令行如下:
c#
private static string BuildFFmpegArgs(string rtspUrl, string segmentPattern, string playlistPath,
CameraPreviewOptions opts)
{
// -rtsp_transport tcp: more reliable than UDP over lossy networks
// -c:v copy: most IP cameras already output H.264, copy to save CPU
// -c:a aac: re-encode audio to AAC for HLS compatibility
// -f hls: HLS muxer
// -hls_time: segment length
// -hls_list_size: rolling window of segments in playlist
// -hls_flags delete_segments+omit_endlist: delete old segments, keep live stream
// -hls_segment_filename: pattern for segment files
return
$"-rtsp_transport tcp -i \"{rtspUrl}\" " +
$"-c:v copy -c:a aac " +
$"-f hls -hls_time {opts.HlsTimeSeconds} -hls_list_size {opts.HlsListSize} " +
$"-hls_flags delete_segments+omit_endlist " +
$"-hls_segment_filename \"{segmentPattern}\" \"{playlistPath}\"";
}
子码流尺寸较小,预览流均匀顺序输出,ffmpeg处理一般很稳定,不易出错。
我们注意到,上述命令中,视频流是直接copy,音频流是编码aac,原因是:绝大多数摄像头的视频编码都是 H.264 或 H.265 。它们是视频领域的通用标准,几乎被所有播放器和视频容器(如 MP4)所支持,因此可以直接复制。摄像头的音频编码可能会使用 G.711 (A-law 或 μ-law)或 G.726 ,这些是专门针对 IP 电话和监控设备设计的低带宽、低延迟编码格式。它们并不被标准 MP4 或 TS 容器视为原生支持的主流音频格式(通常为 AAC 或 MP3)。
回放
海康的回放流要先使用/ISAPI/ContentMgmt/search接口查找,而且默认只支持主码流。/ISAPI/ContentMgmt/search接口接受一个起止时间,返回的是一个或多个符合要求的回放RTSP流(因设备内部是分文件存储不同时段的录像的),这些RTSP流的总时长就是起止时间的总时长。
回放流一般是主码流,尺寸很大(可能是子码流的10倍),用ffmpeg实时处理,很容易mux缓冲溢出,报错:
Too many packets buffered for output stream 0:0
建议使用如下ffmpeg选项:
ffmpeg -rtsp_transport tcp -stimeout 5000000 -i "rtsp://admin:xxxx@192.168.0.6/Streaming/tracks/101/?starttime=20260812T101129Z&endtime=20260812T101139Z&name=00000000180000000&size=1064467600" -c:v copy -c:a aac -t 10 -max_muxing_queue_size 8192 -y out1.mp4
其中:
-stimeout 5000000必须要给!因回放流有起止时间,但RTSP源到达结束时间时并不会发送EOF,导致tcp连接被挂住,整个ffmpeg进程也因此被挂住。指定-stimeout ,在5秒没接收到数据时,退出进程,增加健壮性。预览流也建议加上该选项,主要预防网络异常导致的ffmpeg进程挂住。
-max_muxing_queue_size 8192,缓解Too many packets buffered问题。这个值设置过大,在处理长时视频时,会导致内存占用很大(我设置8192,结果占了400M内存,总内存也才2G)
复用队列(Muxing Queue)溢出分析
这个错误的核心是 FFmpeg 的 muxer 在"等对齐"时缓冲区撑爆了。
FFmpeg 在把多路流(视频 + 音频)封装进输出容器(如 MP4)时,有一个复用队列(muxing queue)。它的工作逻辑是:
- 必须等所有输出流都初始化后,muxer 才能开始写入文件
- 在此期间,已经读到的某一路流的 packet 会被缓冲在队列里
- 如果另一路流迟迟不来(或来得太慢),队列就会不断增长
- 超过默认上限(通常是几百个 packet)后,就会报错退出
海康 RTSP 回放流有以下几个特点,恰好踩中这个坑:
| 问题 | 说明 |
|---|---|
| 时间戳不连续/跳变 | 回放流的时间戳可能不是从 0 开始的,或者中间有 gap |
| 音视频不同步 | 音频 packet 可能延迟到达,视频 packet 先堆积 |
| 流交错质量差 | 设备推送的流本身交错(interleaving)就不均匀 |
-c copy 时无法修正 |
如果用 copy 模式,FFmpeg 不能重新生成时间戳,只能原样封装,问题更突出 |
AI建议,对于回放流视频我们不用-c:v copy而是用:
-c:v libx264 -preset fast -crf 24
实际上,重编码依然会出现Too many packets buffered问题 ,且,重编码的代价非常高昂,cpu会超负荷(百分之几百)运转。这对于我的项目来说,是没法接受的。