본문 바로가기

Technical Document/RTSP, RTP

RTSPTestServer 예제 분석

아래 사이트가 출처인, C++로 RTSP Server를 간단히 구현한 예제를 분석하여 RTSP Server 동작 원리 학습

https://www.medialan.de/usecase0001.html

 

Medialan - RTSP, RTP Video Streaming Server, Tutorial, Hello World

Summary Shows a basic C++ hello world RTP/JPEG streaming server. Discusses the necessary technologies for streaming server development like streaming protocols, payloads and packetization mechanisms. Suited for developers who are searching for a starting p

www.medialan.de

 

Architecture of the tutorial sample of the RTSPTestServer

 

 

Master RTSP socket thread

 

\RTSPTestServer.cpp

while (true)  
{   // loop forever to accept client connections
    ClientSocket = accept(MasterSocket,(struct sockaddr*)&ClientAddr,&ClientAddrLen);    
    CreateThread(NULL,0,SessionThreadHandler,&ClientSocket,0,&TID);
    printf("Client connected. Client address: %s\r\n",inet_ntoa(ClientAddr.sin_addr));  
}

 

 

RTSP request received

 



\RTSPTestServer.cpp

switch (WaitForMultipleObjects(2,WaitEvents,false,INFINITE))
{
    case WAIT_OBJECT_0 + 0 :
    {   
    	// read client socket
        WSAResetEvent(WaitEvents[0]);
        memset(RecvBuf,0x00,sizeof(RecvBuf));
        res = recv(Client,RecvBuf,sizeof(RecvBuf),0);
        
        // we filter away everything which seems not to be an RTSP command: O-ption, D-escribe, S-etup, P-lay, T-eardown
        if ((RecvBuf[0] == 'O') || (RecvBuf[0] == 'D') || (RecvBuf[0] == 'S') || (RecvBuf[0] == 'P') || (RecvBuf[0] == 'T'))
        {
            RTSP_CMD_TYPES C = RtspSession.Handle_RtspRequest(RecvBuf,res);
            if (C == RTSP_PLAY)     StreamingStarted = true; else if (C == RTSP_TEARDOWN) Stop = true;
        };
        
        break;      
    };
    case WAIT_OBJECT_0 + 1 :
    {
	…
    };
};

 

Parse RTSP Request

 

\CRtspSession.cpp

RTSP_CMD_TYPES CRtspSession::Handle_RtspRequest(char const * aRequest, unsigned aRequestSize)
{
    if (ParseRtspRequest(aRequest,aRequestSize))
    {
        switch (m_RtspCmdType)
        {
            case RTSP_OPTIONS:  { Handle_RtspOPTION();   break; };
            case RTSP_DESCRIBE: { Handle_RtspDESCRIBE(); break; };
            case RTSP_SETUP:    { Handle_RtspSETUP();    break; };
            case RTSP_PLAY:     { Handle_RtspPLAY();     break; };
            default: {};
        };
    };
    return m_RtspCmdType;
};
bool CRtspSession::ParseRtspRequest(char const * aRequest, unsigned aRequestSize)
{
	…
}

 

 

Handle RTSP Request

 

void CRtspSession::Handle_RtspOPTION()
void CRtspSession::Handle_RtspDESCRIBE()
void CRtspSession::Handle_RtspSETUP()
void CRtspSession::Handle_RtspPLAY()

 
//공통적으로 송신
send(m_RtspClient,Response,strlen(Response),0);

 

 

VLC에서 UDP TCP 옵션 변경 시 예제 프로젝트 RecvBuf 로 수신하는 RTSP Request 확인

 

 

\RTSPTestServer.cpp

res = recv(Client, RecvBuf, sizeof(RecvBuf), 0);

 

소스에서 RecvBuf 배열로 수신하는 메시지 디버깅으로 확인 과정

 

UDP

 

RecvBuf = 0x0137d628 "OPTIONS rtsp://127.0.0.1:8554/mjpeg/1 RTSP/1.0\r\nCSeq: 2\r\nUser-Agent: LibVLC/2.1.4 (LIVE555 Streaming Media v2014.01.21)\r\n\r\n"

 

RecvBuf = 0x0137d628 "DESCRIBE rtsp://127.0.0.1:8554/mjpeg/1 RTSP/1.0\r\nCSeq: 3\r\nUser-Agent: LibVLC/2.1.4 (LIVE555 Streaming Media v2014.01.21)\r\nAccept: application/sdp\r\n\r\n"

 

RecvBuf = 0x0137d628 "SETUP rtsp://127.0.0.1:8554/mjpeg/1/ RTSP/1.0\r\nCSeq: 4\r\nUser-Agent: LibVLC/2.1.4 (LIVE555 Streaming Media v2014.01.21)\r\nTransport: RTP/AVP;unicast;client_port=64652-64653\r\n\r\n"

 

RecvBuf = 0x0137d628 "PLAY rtsp://127.0.0.1:8554/mjpeg/1/ RTSP/1.0\r\nCSeq: 5\r\nUser-Agent: LibVLC/2.1.4 (LIVE555 Streaming Media v2014.01.21)\r\nSession: -2144778205\r\nRange: npt=0.000-\r\n\r\n"

 

새로운 통신을 개방할 필요가 있어서 개방 포트를 알려준다.

RTP port : 64652

RTCP port : 64653

 

 

TCP

 

RecvBuf = 0x0167d748 "OPTIONS rtsp://127.0.0.1:8554/mjpeg/1 RTSP/1.0\r\nCSeq: 2\r\nUser-Agent: LibVLC/2.1.4 (LIVE555 Streaming Media v2014.01.21)\r\n\r\n"

 

RecvBuf = 0x0167d748 "DESCRIBE rtsp://127.0.0.1:8554/mjpeg/1 RTSP/1.0\r\nCSeq: 3\r\nUser-Agent: LibVLC/2.1.4 (LIVE555 Streaming Media v2014.01.21)\r\nAccept: application/sdp\r\n\r\n"

 

RecvBuf = 0x0167d748 "SETUP rtsp://127.0.0.1:8554/mjpeg/1/ RTSP/1.0\r\nCSeq: 4\r\nUser-Agent: LibVLC/2.1.4 (LIVE555 Streaming Media v2014.01.21)\r\nTransport: RTP/AVP/TCP;unicast;interleaved=0-1\r\n\r\n"

 

RecvBuf = 0x0167d748 "PLAY rtsp://127.0.0.1:8554/mjpeg/1/ RTSP/1.0\r\nCSeq: 5\r\nUser-Agent: LibVLC/2.1.4 (LIVE555 Streaming Media v2014.01.21)\r\nSession: -2144778205\r\nRange: npt=0.000-\r\n\r\n"

 

기존 RTSP 통신에서 RTP 패킷 전달하도록 클라이언트에서 요청

 

//////////////////////////////////////////////////

RecvBuf = 0x0167d748 "$\x1"

 

RTSP 통신위에서 RTCP 통신이 이루어지기때문에 구별하기 위한 magic number

 

SETUP에서 Transport 설정

 

 

void CRtspSession::Handle_RtspSETUP()

 

\CStreamer.cpp

void CStreamer::InitTransport(u_short aRtpPort, u_short aRtcpPort, bool TCP)
{
    sockaddr_in Server;  

    m_RtpClientPort  = aRtpPort;
    m_RtcpClientPort = aRtcpPort;
    m_TCPTransport   = TCP;

    if (!m_TCPTransport)
    {   // allocate port pairs for RTP/RTCP ports in UDP transport mode
        Server.sin_family      = AF_INET;  
        Server.sin_addr.s_addr = INADDR_ANY;  
        
        for (u_short P = 6970; P < 0xFFFE ; P += 2)
        {
            m_RtpSocket     = socket(AF_INET, SOCK_DGRAM, 0);                    
            Server.sin_port = htons(P);

            if (bind(m_RtpSocket,(sockaddr*)&Server,sizeof(Server)) == 0)
            {   // Rtp socket was bound successfully. Lets try to bind the consecutive Rtsp socket
                m_RtcpSocket = socket(AF_INET, SOCK_DGRAM, 0);
                Server.sin_port = htons(P + 1);

                if (bind(m_RtcpSocket,(sockaddr*)&Server,sizeof(Server)) == 0)
                {
                    m_RtpServerPort  = P;
                    m_RtcpServerPort = P+1;
                    break;
                }
                else
                {
                    closesocket(m_RtpSocket);
                    closesocket(m_RtcpSocket);
                };
            }
            else closesocket(m_RtpSocket);
        };
    };
};
void CStreamer::InitTransport(u_short aRtpPort, u_short aRtcpPort, bool TCP)
{
    sockaddr_in Server;  

    m_RtpClientPort  = aRtpPort;
    m_RtcpClientPort = aRtcpPort;
    m_TCPTransport   = TCP;

    if (!m_TCPTransport)
    {   // allocate port pairs for RTP/RTCP ports in UDP transport mode
        Server.sin_family      = AF_INET;  
        Server.sin_addr.s_addr = INADDR_ANY;  

        for (u_short P = 6970; P < 0xFFFE ; P += 2)
        {
            m_RtpSocket     = socket(AF_INET, SOCK_DGRAM, 0);                    
            Server.sin_port = htons(P);

            if (bind(m_RtpSocket,(sockaddr*)&Server,sizeof(Server)) == 0)
            {   // Rtp socket was bound successfully. Lets try to bind the consecutive Rtsp socket
                m_RtcpSocket = socket(AF_INET, SOCK_DGRAM, 0);
                Server.sin_port = htons(P + 1);

                if (bind(m_RtcpSocket,(sockaddr*)&Server,sizeof(Server)) == 0)
                {
                    m_RtpServerPort  = P;
                    m_RtcpServerPort = P+1;
                    break;
                }
                else
                {
                    closesocket(m_RtpSocket);
                    closesocket(m_RtcpSocket);
                };
            }
            else closesocket(m_RtpSocket);
        };
    };
};

 

 

PLAY 설정 시 Stream 동작

 

 

\RTSPTestServer.cpp

switch (WaitForMultipleObjects(2,WaitEvents,false,INFINITE))
{
    case WAIT_OBJECT_0 + 0 :
    {   
       …     
    };
    case WAIT_OBJECT_0 + 1 :
    {
        if (StreamingStarted) Streamer.StreamImage(RtspSession.GetStreamID());
        break;
    };
};

 

 

Rtp over Rtsp 4바이트 패킷 생성 부분과 TCP UDP 송신 동작 분기 

 

\CStreamer.cpp

// Prepare the first 4 byte of the packet. This is the Rtp over Rtsp header in case of TCP based transport
RtpBuf[0]  = '$';        // magic number
RtpBuf[1]  = 0;          // number of multiplexed subchannel on RTPS connection - here the RTP channel
RtpBuf[2]  = (RtpPacketSize & 0x0000FF00) >> 8;
RtpBuf[3]  = (RtpPacketSize & 0x000000FF);

 

if (m_TCPTransport) // RTP over RTSP - we send the buffer + 4 byte additional header
    send(m_Client,RtpBuf,RtpPacketSize + 4,0);
else                // UDP - we send just the buffer by skipping the 4 byte RTP over RTSP header
    sendto(m_RtpSocket,&RtpBuf[4],RtpPacketSize,0,(SOCKADDR *) & RecvAddr,sizeof(RecvAddr));

'Technical Document > RTSP, RTP' 카테고리의 다른 글

RTSP/RTP 개요  (0) 2022.04.28