RTSP客户端点播Darwin 视频时,SDP协商后的客户端端口可能是在NAT后面,所以需要Darwin支持NAT打洞的功能,从Darwin的源码看,官方的源码是不支持这个能力的。
通过抓取VLC客户端的包发现,VLC在播放RTSP流时,两次SETUP(音频流和视频分别协商端口)之后,会发送4个UDP打洞的包,但Darwin没有接收这些包,并且根据这些包来源的端口修改远端RTP和RTCP的端口。
文章Darwin Streaming Server 支持UDP穿透中给出了修改方法,尝试之后,发现有两个问题:
1、两次SETUP协商后,Darwin给返回的服务器的RTP和RTCP端口两次都一样
2、客户端发送的打洞的RTP和RTCP打洞的包,并没有接收完全。
针对问题1的修改就是将两次SETUP协商后,Darwin返回的端口不同并且唯一
问题2的修改方法是,在接收到SETUP协商后,开启一个线程接收发送到服务器的RTP和RTCP端口的包,并根据接收到的包的源端口更新远端的RTP和RTCP端口,即使没有收到打洞的包,不做任何处理,还是使用之前协商的端口往外发包。
第一个问题是将RTPStream::Setup方法中的:
fSockets = QTSServerInterface::GetServer()->GetSocketPool()->GetUDPSocketPair(sourceAddr, 0, fRemoteAddr,
fRemoteRTCPPort);
修改为:
fSockets = QTSServerInterface::GetServer()->GetSocketPool()->CreateUDPSocketPair(sourceAddr, 0);
并将UDPSocketPool::CreateUDPSocketPair方法中两个变量的初值修改为如下:
UInt16 curPort = kLowestUDPPort + usedNum++;
UInt16 stopPort = kHighestUDPPort -1; // prevent roll over when iterating over port nums
UInt16 socketBPort = curPort + 1;
第二个问题修改,头文件增加下面的方法和变量:
void start_thread_for_nat();
void setRemoteRTPPort(int value){fRemoteRTPPort = value; }
void setRemoteRTCPPort(int value){fRemoteRTCPPort = value;}
UInt16 getRemoteRTPPort(){return fRemoteRTPPort;}
UInt32 getRemoteRTPAddr(){return fRemoteAddr;}
Bool16 getQuitValue(){return bQuit;}
Bool16 getRunningValue(){return bRunning;}
void setRunningValue(Bool16 value){ bRunning = value;}
UInt16 getRemoteRTCPPort(){return fRemoteRTCPPort;}
UDPSocketPair* getUDPSocketPair(){ return fSockets;}
Bool16 bQuit;
Bool16 bRunning;
在setup方法最后启动一个监听线程:
this->start_thread_for_nat();
//errors should only be returned if there is a routing problem, there should be none
Assert(err == QTSS_NoErr);
return QTSS_NoErr;
}
实现:
#include <pthread.h>
void* thread_for_nat(void *parms){
Bool16 fUpdateRtpPort = false;
Bool16 fUpdateRtcpPort = false;
SInt64 currentTime = OS::Milliseconds();
RTPStream *pRTPStream = (RTPStream *)parms;
if (pRTPStream == NULL){
return NULL;
}
if (pRTPStream->getUDPSocketPair() == NULL){
return NULL;
}
if (pRTPStream->getUDPSocketPair()->GetSocketA() == NULL ||
pRTPStream->getUDPSocketPair()->GetSocketB() == NULL){
return NULL;
}
qtss_printf("thread_for_nat enter, pRTPStream:%08x, rtp_port:%d\n", pRTPStream, pRTPStream->getRemoteRTPPort());
pRTPStream->setRunningValue(true);
while(1){
UInt32 iRemoteAddr = 0;
UInt16 iRemotePort = 0;
char szBuff[64];
UInt32 iBufLen = sizeof(szBuff);
UInt32 iRecvLen = 0;
if (pRTPStream->getQuitValue()){
break;
}
if (!fUpdateRtpPort){
OS_Error iRet = pRTPStream->getUDPSocketPair()->GetSocketA()->RecvFrom(&iRemoteAddr, &iRemotePort, szBuff, iBufLen, &iRecvLen);
if (OS_NoErr == iRet){
if (iRemoteAddr == pRTPStream->getRemoteRTPAddr()
&& iRemotePort != pRTPStream->getRemoteRTPPort()
&& iRecvLen > 0){
qtss_printf("thread_for_nat update GetSocketA iRet:%d, fRemoteRTPPort:%d, fRemoteRTCPPort:%d, iRemotePort:%d\n", iRet, pRTPStream->getRemoteRTPPort(),
pRTPStream->getRemoteRTCPPort(), iRemotePort);
pRTPStream->setRemoteRTPPort(iRemotePort);
fUpdateRtpPort = true;
}else{
qtss_printf("thread_for_nat update GetSocketA received the same port value, fRemoteRTPPort:%d, fRemoteRTCPPort:%d\n", pRTPStream->getRemoteRTPPort(),
pRTPStream->getRemoteRTCPPort());
fUpdateRtpPort = true;
}
}else{
//qtss_printf("Setup update GetSocketA iRet:%d, fRemoteRTPPort:%d, fRemoteRTCPPort:%d\n", iRet, fRemoteRTPPort, fRemoteRTCPPort);
}
}
if (!fUpdateRtcpPort){
OS_Error iRet = pRTPStream->getUDPSocketPair()->GetSocketB()->RecvFrom(&iRemoteAddr, &iRemotePort, szBuff, iBufLen, &iRecvLen);
if (OS_NoErr == iRet){
if (iRemoteAddr == pRTPStream->getRemoteRTPAddr()
&& iRemotePort != pRTPStream->getRemoteRTCPPort()
&& iRecvLen > 0){
qtss_printf("thread_for_nat update GetSocketB iRet:%d, fRemoteRTPPort:%d, fRemoteRTCPPort:%d, iRemotePort:%d\n", iRet, pRTPStream->getRemoteRTPPort(),
pRTPStream->getRemoteRTCPPort(), iRemotePort);
pRTPStream->setRemoteRTCPPort(iRemotePort);
fUpdateRtcpPort = true;
} else{
qtss_printf("thread_for_nat update GetSocketB received the same port value, fRemoteRTPPort:%d, fRemoteRTCPPort:%d\n", pRTPStream->getRemoteRTPPort(),
pRTPStream->getRemoteRTCPPort());
fUpdateRtcpPort = true;
}
}else{
//wait.
//qtss_printf("Setup update GetSocketB iRet:%d, fRemoteRTPPort:%d, fRemoteRTCPPort:%d\n", iRet, fRemoteRTPPort, fRemoteRTCPPort);
}
}
if (fUpdateRtcpPort && fUpdateRtpPort){
qtss_printf("thread_for_nat exit for update end, break, pRTPStream:%08x, rtp_port:%d\n", pRTPStream, pRTPStream->getRemoteRTPPort());
break;
}
if ( (OS::Milliseconds() - currentTime ) > 2000){
qtss_printf("thread_for_nat exaust 2000 ms, break, pRTPStream:%08x, rtp_port:%d\n", pRTPStream, pRTPStream->getRemoteRTPPort());
break;
}
}
pRTPStream->setRunningValue(false);
gid_thread = -1;
return NULL;
}
void RTPStream::start_thread_for_nat(){
int ret=pthread_create(&gid_thread, NULL, thread_for_nat, (void*)this);
if (ret != 0){
qtss_printf("err:%d\n", ret);
}else{
qtss_printf("start_thread_for_nat create OK\n");
}
}