Darwin Streaming Server 支持UDP打洞

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/twoconk/article/details/52238984

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");
    }
}


猜你喜欢

转载自blog.csdn.net/twoconk/article/details/52238984