아래 사이트가 출처인, C++로 RTSP Server를 간단히 구현한 예제를 분석하여 RTSP Server 동작 원리 학습
https://www.medialan.de/usecase0001.html
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 |
---|