239 lines
6.9 KiB
C++
239 lines
6.9 KiB
C++
// RFC 2326 Real Time Streaming Protocol (RTSP)
|
|
// 12.39 Transport (p58)
|
|
//
|
|
// Transport = "Transport" ":" 1#transport-spec
|
|
// transport-spec = transport-protocol/profile[/lower-transport] *parameter
|
|
// transport-protocol = "RTP"
|
|
// profile = "AVP"
|
|
// lower-transport = "TCP" | "UDP"
|
|
// parameter = ( "unicast" | "multicast" )
|
|
// | ";" "destination" [ "=" address ]
|
|
// | ";" "interleaved" "=" channel [ "-" channel ]
|
|
// | ";" "append"
|
|
// | ";" "ttl" "=" ttl
|
|
// | ";" "layers" "=" 1*DIGIT
|
|
// | ";" "port" "=" port [ "-" port ]
|
|
// | ";" "client_port" "=" port [ "-" port ]
|
|
// | ";" "server_port" "=" port [ "-" port ]
|
|
// | ";" "ssrc" "=" ssrc
|
|
// | ";" "mode" = <"> 1\#mode <">
|
|
// ttl = 1*3(DIGIT)
|
|
// port = 1*5(DIGIT)
|
|
// ssrc = 8*8(HEX)
|
|
// channel = 1*3(DIGIT)
|
|
// address = host
|
|
// mode = <"> *Method <"> | Method
|
|
//
|
|
// Transport: RTP/AVP;unicast;client_port=4588-4589;server_port=6256-6257
|
|
// Transport: RTP/AVP;multicast;ttl=127;mode="PLAY",RTP/AVP;unicast;client_port=3456-3457;mode="PLAY"
|
|
|
|
// RTP Port define
|
|
// RFC 3550: 11. RTP over Network and Transport Protocols (p56)
|
|
// 1. For UDP and similar protocols, RTP should use an even destination port number and
|
|
// the corresponding RTCP stream should use the next higher (odd) destination port number.
|
|
// 2. For applications that take a single port number as a parameter and derive the RTP and RTCP port
|
|
// pair from that number, if an odd number is supplied then the application should replace that
|
|
// number with the next lower (even) number to use as the base of the port pair.
|
|
|
|
#include "rtsp-header-transport.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
|
|
#if defined(_WIN32) || defined(_WIN64) || defined(OS_WINDOWS)
|
|
#define strcasecmp _stricmp
|
|
#define strncasecmp _strnicmp
|
|
#endif
|
|
|
|
#define TRANSPORT_SPECIAL ",;\r\n"
|
|
|
|
int rtsp_header_transport(const char* field, struct rtsp_header_transport_t* t)
|
|
{
|
|
const char* p1;
|
|
const char* p = field;
|
|
size_t n;
|
|
|
|
memset(t, 0, sizeof(*t));
|
|
t->multicast = 0; // default unicast
|
|
t->transport = RTSP_TRANSPORT_RTP_UDP;
|
|
|
|
while(p && *p)
|
|
{
|
|
p1 = strpbrk(p, TRANSPORT_SPECIAL);
|
|
n = p1 ? (size_t)(p1 - p) : strlen(p); // ptrdiff_t -> size_t
|
|
|
|
switch(*p)
|
|
{
|
|
case 'r':
|
|
case 'R':
|
|
if(11 == n && 0 == strncasecmp("RTP/AVP/UDP", p, 11))
|
|
{
|
|
t->transport = RTSP_TRANSPORT_RTP_UDP;
|
|
}
|
|
else if(11 == n && 0 == strncasecmp("RTP/AVP/TCP", p, 11))
|
|
{
|
|
t->transport = RTSP_TRANSPORT_RTP_TCP;
|
|
}
|
|
else if(11 == n && 0 == strncasecmp("RAW/RAW/UDP", p, 11))
|
|
{
|
|
t->transport = RTSP_TRANSPORT_RAW;
|
|
}
|
|
else if(7 == n && 0 == strncasecmp("RTP/AVP", p, 7))
|
|
{
|
|
t->transport = RTSP_TRANSPORT_RTP_UDP;
|
|
}
|
|
break;
|
|
|
|
case 'u':
|
|
case 'U':
|
|
if(7 == n && 0 == strncasecmp("unicast", p, 7))
|
|
{
|
|
t->multicast = 0;
|
|
}
|
|
break;
|
|
|
|
case 'm':
|
|
case 'M':
|
|
if(9 == n && 0 == strncasecmp("multicast", p, 9))
|
|
{
|
|
t->multicast = 1;
|
|
}
|
|
else if(n > 5 && 0 == strncasecmp("mode=", p, 5))
|
|
{
|
|
if( (11==n && 0 == strcasecmp("\"PLAY\"", p+5)) || (9==n && 0 == strcasecmp("PLAY", p+5)) )
|
|
t->mode = RTSP_TRANSPORT_PLAY;
|
|
else if( (13==n && 0 == strcasecmp("\"RECORD\"", p+5)) || (11==n && 0 == strcasecmp("RECORD", p+5)) )
|
|
t->mode = RTSP_TRANSPORT_RECORD;
|
|
}
|
|
break;
|
|
|
|
case 'd':
|
|
case 'D':
|
|
if(n >= 12 && 0 == strncasecmp("destination=", p, 12))
|
|
{
|
|
if(n-12 >= sizeof(t->destination)) return -1;
|
|
memcpy(t->destination, p+12, n - 12);
|
|
t->destination[n-12] = '\0';
|
|
}
|
|
break;
|
|
|
|
case 's':
|
|
case 'S':
|
|
if(n >= 7 && 0 == strncasecmp("source=", p, 7))
|
|
{
|
|
if(n-7 >= sizeof(t->source)) return -1;
|
|
memcpy(t->source, p+7, n - 7);
|
|
t->source[n-7] = '\0';
|
|
}
|
|
else if(13 == n && 0 == strncasecmp("ssrc=", p, 5))
|
|
{
|
|
// unicast only
|
|
assert(0 == t->multicast);
|
|
t->rtp.u.ssrc = (unsigned int)strtoul(p+5, NULL, 16);
|
|
}
|
|
else if(2 == sscanf(p, "server_port=%hu-%hu", &t->rtp.u.server_port1, &t->rtp.u.server_port2))
|
|
{
|
|
assert(0 == t->multicast);
|
|
}
|
|
else if(1 == sscanf(p, "server_port=%hu", &t->rtp.u.server_port1))
|
|
{
|
|
assert(0 == t->multicast);
|
|
t->rtp.u.server_port1 = t->rtp.u.server_port1 / 2 * 2; // RFC 3550 (p56)
|
|
t->rtp.u.server_port2 = t->rtp.u.server_port1 + 1;
|
|
}
|
|
break;
|
|
|
|
case 'a':
|
|
if(6 == n && 0 == strcasecmp("append", p))
|
|
{
|
|
t->append = 1;
|
|
}
|
|
break;
|
|
|
|
case 'p':
|
|
if(2 == sscanf(p, "port=%hu-%hu", &t->rtp.m.port1, &t->rtp.m.port2))
|
|
{
|
|
assert(1 == t->multicast);
|
|
}
|
|
else if(1 == sscanf(p, "port=%hu", &t->rtp.m.port1))
|
|
{
|
|
assert(1 == t->multicast);
|
|
t->rtp.m.port1 = t->rtp.m.port1 / 2 * 2; // RFC 3550 (p56)
|
|
t->rtp.m.port2 = t->rtp.m.port1 + 1;
|
|
}
|
|
break;
|
|
|
|
case 'c':
|
|
if(2 == sscanf(p, "client_port=%hu-%hu", &t->rtp.u.client_port1, &t->rtp.u.client_port2))
|
|
{
|
|
assert(0 == t->multicast);
|
|
}
|
|
else if(1 == sscanf(p, "client_port=%hu", &t->rtp.u.client_port1))
|
|
{
|
|
assert(0 == t->multicast);
|
|
t->rtp.u.client_port1 = t->rtp.u.client_port1 / 2 * 2; // RFC 3550 (p56)
|
|
t->rtp.u.client_port2 = t->rtp.u.client_port1 + 1;
|
|
}
|
|
break;
|
|
|
|
case 'i':
|
|
if(2 == sscanf(p, "interleaved=%d-%d", &t->interleaved1, &t->interleaved2))
|
|
{
|
|
}
|
|
else if(1 == sscanf(p, "interleaved=%d", &t->interleaved1))
|
|
{
|
|
t->interleaved2 = t->interleaved1 + 1;
|
|
}
|
|
break;
|
|
|
|
case 't':
|
|
if(1 == sscanf(p, "ttl=%d", &t->rtp.m.ttl))
|
|
{
|
|
assert(1 == t->multicast);
|
|
}
|
|
break;
|
|
|
|
case 'l':
|
|
if(1 == sscanf(p, "layers=%d", &t->layer))
|
|
{
|
|
}
|
|
break;
|
|
}
|
|
|
|
if(NULL == p1 || '\r' == *p1 || '\n' == *p1 || '\0' == *p1 || ',' == *p1)
|
|
break;
|
|
p = p1 + 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(DEBUG) || defined(_DEBUG)
|
|
void rtsp_header_transport_test(void)
|
|
{
|
|
struct rtsp_header_transport_t t;
|
|
|
|
memset(&t, 0, sizeof(t));
|
|
assert(0 == rtsp_header_transport("RTP/AVP;unicast;client_port=4588-4589;server_port=6256-6257;ssrc=08abe80f", &t)); // rfc2326 p61
|
|
assert(t.transport==RTSP_TRANSPORT_RTP_UDP);
|
|
assert(t.multicast==0 && t.rtp.u.client_port1==4588 && t.rtp.u.client_port2==4589 && t.rtp.u.server_port1==6256 && t.rtp.u.server_port2==6257);
|
|
assert(t.rtp.u.ssrc == 0x08abe80f);
|
|
|
|
memset(&t, 0, sizeof(t));
|
|
assert(0 == rtsp_header_transport("RTP/AVP;multicast;ttl=127;mode=\"PLAY\"", &t)); // rfc2326 p61
|
|
assert(t.transport==RTSP_TRANSPORT_RTP_UDP);
|
|
assert(t.multicast==1 && 127==t.rtp.m.ttl && RTSP_TRANSPORT_PLAY==t.mode);
|
|
|
|
memset(&t, 0, sizeof(t));
|
|
assert(0 == rtsp_header_transport("RTP/AVP/TCP;interleaved=0-1", &t)); // rfc2326 p40
|
|
assert(t.transport == RTSP_TRANSPORT_RTP_TCP);
|
|
assert(t.interleaved1 == 0 && t.interleaved2 == 1);
|
|
|
|
memset(&t, 0, sizeof(t));
|
|
assert(0 == rtsp_header_transport("RTP/AVP;unicast;source=192.168.111.333.444.555.666.777.888.999.000.123", &t)); // rfc2326 p61
|
|
assert(t.transport==RTSP_TRANSPORT_RTP_UDP);
|
|
assert(t.multicast==0 && 0==strncmp("192.168.111.333.444.555.666.777.888.999.000.123", t.source, sizeof(t.source)-1) && strlen(t.source)<sizeof(t.source));
|
|
}
|
|
#endif
|