ZLMediaKit RTSP专题:RTP over UDP/TCP/HTTP/组播四种传输方式
引言
在流媒体开发中,RTSP(Real Time Streaming Protocol,实时流传输协议)作为经典的控制协议,其RTP(Real-time Transport Protocol,实时传输协议)数据传输方式的选择直接影响着系统的性能和兼容性。ZLMediaKit作为高性能的流媒体服务器框架,全面支持RTP over UDP、TCP、HTTP和组播四种传输方式,为不同场景提供了灵活的解决方案。
你是否遇到过以下痛点:
- 网络限制导致UDP传输失败?
- 网络质量差时TCP传输延迟高?
- 需要大规模分发时单播效率低下?
- 移动网络环境下连接稳定性差?
本文将深入解析ZLMediaKit中RTSP协议的四种RTP传输方式,通过技术原理、实现细节和实战案例,帮助你选择最适合的传输方案。
四种传输方式概述
ZLMediaKit支持的四种RTP传输方式对比如下:
| 传输方式 | 协议特点 | 适用场景 | 优势 | 劣势 |
|---|---|---|---|---|
| RTP over UDP | 独立UDP端口传输 | 局域网、高质量网络 | 低延迟、高效率 | 网络穿透差 |
| RTP over TCP | RTSP信令通道传输 | 公网、不稳定网络 | 网络友好、可靠 | 延迟较高 |
| RTP over HTTP | HTTP协议伪装 | 严格网络环境 | 最强穿透能力 | 实现复杂 |
| RTP over 组播 | 共享组播端口 | 大规模分发 | 带宽利用率高 | 需要网络支持 |
RTP over UDP:经典高效传输
技术原理
RTP over UDP是RTSP协议的标准传输方式,使用独立的UDP端口进行音视频数据传输:
ZLMediaKit实现
在ZLMediaKit中,UDP传输的核心实现在RtspSession::handleReq_Setup方法中:
case Rtsp::RTP_UDP: {
std::pair<Socket::Ptr, Socket::Ptr> pr = std::make_pair(createSocket(),createSocket());
try {
makeSockPair(pr, get_local_ip());
} catch (std::exception &ex) {
send_NotAcceptable();
throw SockException(Err_shutdown, ex.what());
}
_rtp_socks[trackIdx] = pr.first;
_rtcp_socks[trackIdx] = pr.second;
// 设置客户端端口信息
string strClientPort = findSubString(parser["Transport"].data(), "client_port=", NULL);
uint16_t ui16RtpPort = atoi(findSubString(strClientPort.data(), NULL, "-").data());
uint16_t ui16RtcpPort = atoi(findSubString(strClientPort.data(), "-", NULL).data());
// 绑定对端地址
auto peerAddr = SockUtil::make_sockaddr(get_peer_ip().data(), ui16RtpPort);
pr.first->bindPeerAddr((struct sockaddr *) (&peerAddr), 0, true);
peerAddr = SockUtil::make_sockaddr(get_peer_ip().data(), ui16RtcpPort);
pr.second->bindPeerAddr((struct sockaddr *) (&peerAddr), 0, true);
}
适用场景
- 局域网内高质量网络环境
- 对延迟敏感的音视频应用
- 无需网络穿透的内部系统
RTP over TCP:可靠传输方案
技术原理
RTP over TCP将RTP/RTCP数据通过RTSP信令的TCP连接进行传输,使用交织(interleaved)方式区分不同的数据通道:
ZLMediaKit实现
TCP传输的核心代码同样在handleReq_Setup方法中:
case Rtsp::RTP_TCP: {
if (_push_src) {
// 推流时interleaved由客户端决定
auto key_values = Parser::parseArgs(parser["Transport"], ";", "=");
int interleaved_rtp = -1, interleaved_rtcp = -1;
if (2 == sscanf(key_values["interleaved"].data(), "%d-%d", &interleaved_rtp, &interleaved_rtcp)) {
trackRef->_interleaved = interleaved_rtp;
}
} else {
// 播放时interleaved由服务器决定
trackRef->_interleaved = 2 * trackRef->_type;
}
// 启用RTP接收
RtspSplitter::enableRecvRtp(true);
}
数据接收处理在onRtpPacket方法中:
void RtspSession::onRtpPacket(const char *data, size_t len) {
uint8_t interleaved = data[1];
if (interleaved % 2 == 0) {
// RTP数据包
RtpHeader *header = (RtpHeader *)(data + RtpPacket::kRtpTcpHeaderSize);
auto track_idx = getTrackIndexByPT(header->pt);
handleOneRtp(track_idx, _sdp_track[track_idx]->_type,
_sdp_track[track_idx]->_samplerate,
(uint8_t *) data + RtpPacket::kRtpTcpHeaderSize,
len - RtpPacket::kRtpTcpHeaderSize);
} else {
// RTCP数据包
auto track_idx = getTrackIndexByInterleaved(interleaved - 1);
onRtcpPacket(track_idx, _sdp_track[track_idx],
data + RtpPacket::kRtpTcpHeaderSize,
len - RtpPacket::kRtpTcpHeaderSize);
}
}
适用场景
- 需要网络穿透的公网环境
- 网络质量不稳定的移动网络
- 对可靠性要求高于延迟的场景
RTP over HTTP:网络穿透利器
技术原理
RTP over HTTP是一种巧妙的网络穿透方案,将RTSP协议伪装成HTTP协议:
ZLMediaKit实现
HTTP传输的处理在handleReq_Get和handleReq_Post方法中:
void RtspSession::handleReq_Get(const Parser &parser) {
// 获取x-sessioncookie
_http_x_sessioncookie = parser["x-sessioncookie"];
if (_http_x_sessioncookie.empty()) {
_http_x_sessioncookie = makeRandStr(12);
}
// 注册为HTTP Getter
lock_guard<recursive_mutex> lock(g_mtxGetter);
g_mapGetter[_http_x_sessioncookie] = static_pointer_cast<RtspSession>(shared_from_this());
// 设置接收回调
_on_recv = [](const Buffer::Ptr &buf) {
// 处理POSTer转发过来的数据
};
}
void RtspSession::handleReq_Post(const Parser &parser) {
string sessioncookie = parser["x-sessioncookie"];
weak_ptr<RtspSession> getter;
{
lock_guard<recursive_mutex> lock(g_mtxGetter);
auto it = g_mapGetter.find(sessioncookie);
if (it != g_mapGetter.end()) {
getter = it->second;
}
}
if (getter.lock()) {
// 将POST数据转发给对应的Getter处理
getter.lock()->onRecv(buf);
}
}
适用场景
- 企业网络严格限制的网络环境
- 只能通过80/443端口通信的场景
- 需要最大程度兼容性的跨网络应用
RTP over 组播:大规模分发方案
技术原理
组播传输允许多个客户端共享同一个组播地址和端口接收数据,极大节省服务器带宽:
ZLMediaKit实现
组播功能通过RtpMultiCaster类实现:
case Rtsp::RTP_MULTICAST: {
if(!_multicaster){
_multicaster = RtpMultiCaster::get(*this, get_local_ip(), _media_info,
_multicast_ip, _multicast_video_port,
_multicast_audio_port);
if (!_multicaster) {
send_NotAcceptable();
throw SockException(Err_shutdown, "can not get a available udp multicast socket");
}
}
int iSrvPort = _multicaster->getMultiCasterPort(trackRef->_type);
// 设置RTCP共享端口
auto pSockRtcp = UDPServer::Instance().getSock(*this, get_local_ip().data(),
2 * trackIdx + 1, iSrvPort + 1);
}
组播地址管理通过MultiCastAddressMaker单例类:
class MultiCastAddressMaker {
public:
static MultiCastAddressMaker& Instance();
std::shared_ptr<uint32_t> obtain();
void release(std::shared_ptr<uint32_t> addr);
static std::string toString(uint32_t addr);
};
适用场景
- 大规模直播分发系统
- 企业内部视频会议
- IPTV等广播式应用
实战配置指南
服务器端配置
在ZLMediaKit的配置文件中,可以设置默认的传输方式:
[rtsp]
# 传输方式限制: -1-不限制 0-TCP 1-UDP 2-组播
rtpTransportType=-1
# 组播相关配置
[multicast]
# 组播UDP TTL
udpTTL=64
客户端请求示例
不同传输方式的SETUP请求示例:
UDP传输:
SETUP rtsp://example.com/stream/track1 RTSP/1.0
CSeq: 3
Transport: RTP/AVP/UDP;unicast;client_port=5000-5001
TCP传输:
SETUP rtsp://example.com/stream/track1 RTSP/1.0
CSeq: 3
Transport: RTP/AVP/TCP;unicast;interleaved=0-1
组播传输:
SETUP rtsp://example.com/stream/track1 RTSP/1.0
CSeq: 3
Transport: RTP/AVP;multicast
HTTP传输:
GET /stream RTSP/1.0
x-sessioncookie: abcdef123456
性能优化建议
- UDP模式:适合局域网,设置合理的socket buffer大小
- TCP模式:启用TCP_NODELAY减少延迟
- 组播模式:合理设置TTL值,控制组播范围
- HTTP模式:优化Base64编解码性能
故障排查与调试
常见问题解决
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| UDP连接失败 | 网络阻挡 | 切换TCP或HTTP模式 |
| TCP延迟高 | 网络拥塞 | 优化网络或使用UDP |
| 组播不工作 | 网络不支持 | 检查路由器组播配置 |
| HTTP模式异常 | Cookie不匹配 | 检查x-sessioncookie设置 |
调试技巧
使用Wireshark抓包分析:
- 过滤条件:
rtsp || http - 关注SETUP请求的Transport字段
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



