前言
本文主要介绍通过udp方式实现rtsp拉流。
流程图
流程说明:
- 相较于tcp方式“信令+数据”复用同一连接拉流,udp方式拉流“信令+数据”采用不同的连接,信令传输采用tcp,流数据传输采用udp;
- 客户端向服务端(设备等)发起tcp请求,用于后续信令交互;
- tcp连接成功后,开始rtsp信令交互(describe、setup、play等),rtsp链路需要保活;
- 客户端选择两个未用的端口创建udp链路,一个用于rtp数据传输,一个用于rtcp数据传输;
- 连接成功后,服务端(设备等)发送数据至客户端;
- 客户端对数据解复用、解码、播放;
设计
- 客户端向服务端(设备等)发起tcp请求:创建socket、connect、设置recv超时时间
m_tcpClient = std::make_shared<ZDTcpClient>(nullptr, this);
if (!m_tcpClient.get()
|| 0 != m_tcpClient->TcpCreate()
|| 0 != m_tcpClient->TcpConnect(ip.c_str(), port)
|| 0 != m_tcpClient->TcpSetNoBlock(true)
|| 0 != m_tcpClient->TcpRecvTimeout(10))
break;
- tcp连接成功后,创建rtsp客户端,开始rtsp信令交互
// 参数1(RTSP_TRANSPORT_RTP_UDP)指明使用udp方式收流
m_command = std::make_shared<CRtspCommand>(RTSP_TRANSPORT_RTP_UDP, m_tcpClient, m_func, m_user);
if (!m_command.get())
break;
// 创建rtsp client
if (!m_command->CreateRtspClient(m_rtspUrl, username, userpasswd))
break;
// 发送describe信令
int ret = m_command->SendDescribe();
if (0 != ret)
break;
- 创建udp链路
int CRtspCmd::HandleRtpPort(int media, const char* source, unsigned short rtp[2], char* ip, int len)
{
int ret = -1;
switch (m_transport)
{
case RTSP_TRANSPORT_RTP_UDP:
ret = sockpair_create("0.0.0.0", m_rtp[media], m_port[media]);
if (0 != ret)
{
return -1;
}
rtp[0] = m_port[media][0];
rtp[1] = m_port[media][1];
break;
default:
assert(0);
return -1;
}
return m_transport;
}
- 启动udp收流
int CRtspUdpData::Start(int count, socket_t rtp[2], const char* peer, int peerport[2], int payload, const char* encoding)
{
// 创建解复用器
const struct rtp_profile_t* profile = rtp_profile_find(payload);
m_demuxer = rtp_demuxer_create(100, profile ? profile->frequency : 90000, payload, encoding, RtpPacketCB, this);
if (!m_demuxer)
return -1;
if (0 != CreatePacker_(payload, encoding))
{
rtp_demuxer_destroy(&m_demuxer);
return -1;
}
m_encoding = encoding;
m_payload = payload;
m_socket[0] = rtp[0];
m_socket[1] = rtp[1];
// 启动数据接收线程
m_thread = std::thread(UdpDataThread, this);
return 0;
}
- 读rtp和rtcp数据
// 读rtp数据
int CRtspUdpData::RtpRead_(socket_t s)
{
if (!m_demuxer)
return -1;
struct sockaddr_storage ss;
socklen_t len = sizeof(ss);
int dataLen = recvfrom(s, m_rtpBuffer, sizeof(m_rtpBuffer), 0, (struct sockaddr*)&ss, &len);
if (dataLen < 12)
{
return -1;
}
// 视频数据解复用
return rtp_demuxer_input(m_demuxer, m_rtpBuffer, dataLen);
}
// 读rtcp数据
int CRtspUdpData::RtcpRead_(socket_t s)
{
if (!m_demuxer)
return -1;
struct sockaddr_storage ss;
socklen_t len = sizeof(ss);
int dataLen = recvfrom(s, m_rtcpBuffer, sizeof(m_rtcpBuffer), 0, (struct sockaddr*)&ss, &len);
if (dataLen < 12)
{
return -1;
}
return rtp_demuxer_input(m_demuxer, m_rtcpBuffer, dataLen);
}