// 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 #include #include #include #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)