1. RTSP推流创建流程全景图在ZLMediaKit中RTSP推流创建是一个典型的请求-响应过程整个过程可以分为五个关键阶段连接建立阶段客户端通过TCP连接服务器554端口信令交互阶段OPTIONS、DESCRIBE、ANNOUNCE等RTSP命令交换媒体源初始化阶段创建_push_src和环形缓冲区数据传输阶段通过RTP协议传输媒体数据资源释放阶段TEARDOWN命令触发资源清理整个流程的核心类RtspSession继承关系如下RtspSession ├── RtspSplitter // RTSP协议解析 ├── RtpReceiver // RTP数据接收 └── MediaSourceEvent // 媒体源事件处理2. 数据接收与协议解析当客户端发送RTSP请求时数据首先到达RtspSession::onRecv()方法。这个方法的核心逻辑是处理TCP粘包问题并将数据交给协议解析器void RtspSession::onRecv(const Buffer::Ptr buf) { _alive_ticker.resetTime(); // 保活计时器重置 _bytes_usage buf-size(); if (_on_recv) { _on_recv(buf); // HTTP POSTER转发 } else { input(buf-data(), buf-size()); // 进入协议解析流程 } }协议解析的关键在于继承自HttpRequestSplitter的解析框架它会自动处理以下情况识别RTSP消息头尾通过\r\n\r\n分隔处理带Content-Length的消息体支持分块传输的数据重组解析后的数据会触发onWholeRtspPacket回调这里会根据RTSP方法类型如ANNOUNCE、SETUP等分发到对应的处理方法void RtspSession::onWholeRtspPacket(Parser parser) { string method parser.Method(); _cseq atoi(parser[CSeq].data()); static unordered_mapstring, rtsp_request_handler s_cmd_functions { {OPTIONS, RtspSession::handleReq_Options}, {DESCRIBE, RtspSession::handleReq_Describe}, {ANNOUNCE, RtspSession::handleReq_ANNOUNCE}, // ...其他方法处理 }; auto it s_cmd_functions.find(method); if (it ! s_cmd_functions.end()) { (this-*(it-second))(parser); // 方法分发 } }3. SDP解析与媒体源初始化ANNOUNCE请求是推流创建的关键它携带了SDP描述信息。处理流程包含三个重要步骤SDP解析过程校验URL格式必须包含app和streamid两级提取SDP中的媒体轨道信息视频/音频为每个轨道创建RTCP上下文void RtspSession::handleReq_ANNOUNCE(const Parser parser) { // SDP解析 SdpParser sdpParser(parser.Content()); _sdp_track sdpParser.getAvailableTrack(); // 媒体源创建 _push_src std::make_sharedRtspMediaSourceImp( _media_info._vhost, _media_info._app, _media_info._streamid ); // 设置协议选项和SDP _push_src-setProtocolOption(option); _push_src-setSdp(parser.Content()); }媒体源初始化细节RtspMediaSourceImp构造函数会创建RtspDemuxer用于解复用RTP流如果配置了转协议如转RTMP会创建MultiMediaSourceMuxerSDP中的关键信息如SSRC、采样率等会被记录到轨道属性中实际项目中遇到的典型问题SDP中缺少关键帧标记导致播放端花屏时间戳不连续引发缓冲区溢出SSRC冲突造成数据混乱4. 环形缓冲区与数据分发媒体数据通过环形缓冲区实现高效的分发其设计亮点包括缓冲区创建时机首次收到RTP数据包时创建大小默认为512个RTP包可配置采用写时复制技术减少锁竞争void RtspMediaSource::onWrite(RtpPacket::Ptr rtp, bool keyPos) { if (!_ring) { _ring std::make_sharedRingType(_ring_size, [this](int size) { onReaderChanged(size); // 读者数量变化回调 }); } // 数据写入环形缓冲区 _ring-write(rtp, keyPos); }数据分发流程写入线程将RTP包放入环形缓冲区每个订阅者播放器有自己的读取指针采用事件驱动模式通知新数据到达关键帧触发GOP缓存刷新性能优化点批量写入减少锁开销读者独立缓存避免拷贝时间戳跳跃自动校正5. RTP数据处理全流程RTP数据从接收到分发的完整路径接收阶段void RtspSession::onRtpPacket(const char *data, size_t len) { uint8_t interleaved data[1]; if (interleaved % 2 0) { // RTP包 handleOneRtp(track_idx, type, samplerate, rtp_data, rtp_len); } else { // RTCP包 onRtcpPacket(track_idx, track, rtcp_data, rtcp_len); } }排序阶段void RtpTrack::inputRtp(TrackType type, int sample_rate, uint8_t *ptr, size_t len) { auto rtp std::make_sharedRtpPacket(type, sample_rate); rtp-parse(ptr, len); sortPacket(rtp-getSeq(), rtp); // 序列号排序 }分发阶段void RtspSession::onRtpSorted(RtpPacket::Ptr rtp, int track_idx) { if (_push_src) { _push_src-onWrite(std::move(rtp), false); } }常见问题排查技巧使用Wireshark抓包验证RTP序列号连续性检查RTCP反馈包中的丢包率统计监控环形缓冲区的写入/读取延迟6. 会话生命周期管理RTSP会话的生命周期由以下几个关键点控制会话启动SETUP命令建立传输通道RECORD命令开始数据传输心跳机制维持连接活跃异常处理void RtspSession::onError(const SockException err) { if (_push_src) { _push_src-setListener(nullptr); // 解除事件监听 _push_src nullptr; // 释放媒体源 } // 其他资源清理... }优雅关闭TEARDOWN命令触发资源释放通知所有订阅者流已结束从全局媒体源映射表移除记录在实际项目中需要特别注意断网快速检测通过TCP keepalive推流中断自动恢复机制资源泄漏的预防性检查7. 性能优化实践通过源码分析我们可以提取以下性能优化经验内存优化使用对象池管理RTP包内存环形缓冲区避免频繁内存分配零拷贝技术减少数据传输开销CPU优化批量处理RTP包减少上下文切换SIMD指令加速媒体数据处理无锁队列减少线程竞争网络优化TCP_NODELAY禁用Nagle算法SO_RCVBUF调整接收缓冲区大小自适应码率减少网络抖动影响实测数据显示经过优化后单机可支持500路RTSP推流端到端延迟控制在200ms以内CPU利用率降低30%以上