225 lines
8.3 KiB
C++
225 lines
8.3 KiB
C++
/*
|
|
C->S:
|
|
SETUP rtsp://example.com/foo/bar/baz.rm RTSP/1.0
|
|
CSeq: 302
|
|
Transport: RTP/AVP;unicast;client_port=4588-4589
|
|
|
|
S->C:
|
|
RTSP/1.0 200 OK
|
|
CSeq: 302
|
|
Date: 23 Jan 1997 15:35:06 GMT
|
|
Session: 47112344
|
|
Transport: RTP/AVP;unicast;client_port=4588-4589;server_port=6256-6257
|
|
*/
|
|
#include "rtsp-client-internal.h"
|
|
|
|
static const char* sc_rtsp_setup = "SETUP %s RTSP/1.0\r\n"
|
|
"CSeq: %u\r\n"
|
|
"%s" // Session: %s\r\n
|
|
"%s" // Authorization: Digest xxx
|
|
"%s" // Transport: RTP/AVP;unicast;client_port=6000-6001
|
|
"User-Agent: %s\r\n"
|
|
"\r\n";
|
|
|
|
int rtsp_addr_is_multicast(const char* ip);
|
|
|
|
static int rtsp_client_media_setup(struct rtsp_client_t* rtsp, int i)
|
|
{
|
|
int len;
|
|
char transport[128];
|
|
char session[sizeof(rtsp->session[0].session) + 12], *p;
|
|
|
|
assert(i < rtsp->media_count);
|
|
assert(RTSP_SETUP == rtsp->state);
|
|
if (i >= rtsp->media_count) return -1;
|
|
|
|
transport[0] = session[0] = '\0';
|
|
p = rtsp->session[0].session;
|
|
len = (rtsp->aggregate && *p) ? snprintf(session, sizeof(session), "Session: %s\r\n", p) : 0;
|
|
assert(len >= 0 && len < sizeof(session));
|
|
|
|
switch (rtsp->transport[i].transport) {
|
|
case RTSP_TRANSPORT_RTP_TCP:
|
|
len = snprintf(transport, sizeof(transport), "Transport: RTP/AVP/TCP;interleaved=%d-%d\r\n", rtsp->transport[i].interleaved1, rtsp->transport[i].interleaved2);
|
|
break;
|
|
|
|
case RTSP_TRANSPORT_RTP_UDP:
|
|
if(0 == rtsp->transport[i].multicast)
|
|
len = snprintf(transport, sizeof(transport), "Transport: RTP/AVP;unicast;client_port=%hu-%hu\r\n", rtsp->transport[i].rtp.u.client_port1, rtsp->transport[i].rtp.u.client_port2);
|
|
else if(*rtsp->transport[i].destination)
|
|
len = snprintf(transport, sizeof(transport), "Transport: RTP/AVP;multicast;destination=%s;port=%hu-%hu;ttl=%d\r\n", rtsp->transport[i].destination, rtsp->transport[i].rtp.m.port1, rtsp->transport[i].rtp.m.port2, rtsp->transport[i].rtp.m.ttl);
|
|
else
|
|
len = snprintf(transport, sizeof(transport), "Transport: RTP/AVP;multicast;port=%hu-%hu;ttl=%d\r\n", rtsp->transport[i].rtp.m.port1, rtsp->transport[i].rtp.m.port2, rtsp->transport[i].rtp.m.ttl);
|
|
break;
|
|
|
|
case RTSP_TRANSPORT_RAW:
|
|
if(0 == rtsp->transport[i].multicast)
|
|
len = snprintf(transport, sizeof(transport), "Transport: RAW/RAW/UDP;unicast;client_port=%hu-%hu\r\n", rtsp->transport[i].rtp.u.client_port1, rtsp->transport[i].rtp.u.client_port2);
|
|
else if(*rtsp->transport[i].destination)
|
|
len = snprintf(transport, sizeof(transport), "Transport: RAW/RAW/UDP;multicast;destination=%s;port=%hu-%hu;ttl=%d\r\n", rtsp->transport[i].destination, rtsp->transport[i].rtp.m.port1, rtsp->transport[i].rtp.m.port2, rtsp->transport[i].rtp.m.ttl);
|
|
else
|
|
len = snprintf(transport, sizeof(transport), "Transport: RAW/RAW/UDP;multicast;port=%hu-%hu;ttl=%d\r\n", rtsp->transport[i].rtp.m.port1, rtsp->transport[i].rtp.m.port2, rtsp->transport[i].rtp.m.ttl);
|
|
break;
|
|
|
|
default:
|
|
assert(0);
|
|
return -1;
|
|
}
|
|
assert(len >= 0 && len < sizeof(transport));
|
|
|
|
len = rtsp_client_authenrization(rtsp, "SETUP", rtsp->media[i].uri, NULL, 0, rtsp->authenrization, sizeof(rtsp->authenrization));
|
|
len = snprintf(rtsp->req, sizeof(rtsp->req), sc_rtsp_setup, rtsp->media[i].uri, rtsp->cseq++, session, rtsp->authenrization, transport, USER_AGENT);
|
|
return (len > 0 && len < sizeof(rtsp->req) && len == rtsp->handler.send(rtsp->param, rtsp->media[i].uri, rtsp->req, len)) ? 0 : -1;
|
|
}
|
|
|
|
int rtsp_client_setup(struct rtsp_client_t* rtsp, const char* sdp, int len)
|
|
{
|
|
int i, r;
|
|
unsigned short port[2];
|
|
struct rtsp_media_t *m;
|
|
struct rtsp_header_transport_t *t;
|
|
|
|
if (NULL == sdp || 0 == *sdp || len < 1)
|
|
return -1;
|
|
|
|
r = rtsp_media_sdp(sdp, len, rtsp->media, sizeof(rtsp->media)/sizeof(rtsp->media[0]));
|
|
if (r < 0 || r > sizeof(rtsp->media) / sizeof(rtsp->media[0]))
|
|
return r < 0 ? r : -E2BIG; // too many media stream
|
|
|
|
rtsp->media_count = r;
|
|
for (i = 0; i < rtsp->media_count; i++)
|
|
{
|
|
m = rtsp->media + i;
|
|
t = rtsp->transport + i;
|
|
|
|
// rfc 2326 C.1.1 Control URL (p80)
|
|
// If found at the session level, the attribute indicates the URL for aggregate control
|
|
rtsp_media_set_url(m, rtsp->baseuri, rtsp->location, rtsp->uri);
|
|
|
|
port[0] = (unsigned short)m->port[0];
|
|
port[1] = (unsigned short)m->port[1];
|
|
snprintf(t->source, sizeof(t->source), "%s", m->source);
|
|
snprintf(t->destination, sizeof(t->destination), "%s", m->address);
|
|
r = rtsp->handler.rtpport(rtsp->param, i, t->source, port, t->destination, sizeof(t->destination));
|
|
if (r < 0)
|
|
return r;
|
|
|
|
if(RTSP_TRANSPORT_RTP_TCP == r)
|
|
{
|
|
t->transport = RTSP_TRANSPORT_RTP_TCP;
|
|
t->interleaved1 = 0==port[0] ? 2 * (unsigned short)i : port[0];
|
|
t->interleaved2 = 0==port[1] ? t->interleaved1 + 1 : port[1];
|
|
}
|
|
else if((RTSP_TRANSPORT_RTP_UDP == r || RTSP_TRANSPORT_RAW == r) && rtsp_addr_is_multicast(t->destination))
|
|
{
|
|
assert(0 == t->rtp.u.client_port1 % 2);
|
|
t->transport = r;
|
|
t->multicast = 1;
|
|
t->rtp.m.ttl = 16; // default RTT
|
|
t->rtp.m.port1 = port[0];
|
|
t->rtp.m.port2 = 0 == port[1] ? t->rtp.m.port1 + 1 : port[1];
|
|
}
|
|
else if(RTSP_TRANSPORT_RTP_UDP == r || RTSP_TRANSPORT_RAW == r)
|
|
{
|
|
assert(0 == t->rtp.u.client_port1 % 2);
|
|
t->transport = r;
|
|
t->multicast = 0;
|
|
t->rtp.u.client_port1 = port[0];
|
|
t->rtp.u.client_port2 = 0 == port[1] ? t->rtp.u.client_port1 + 1 : port[1];
|
|
}
|
|
else if (0 == r)
|
|
{
|
|
// ignore media
|
|
if (i + 1 < rtsp->media_count)
|
|
memmove(rtsp->media + i, rtsp->media + i + 1, sizeof(rtsp->media[0]) * (rtsp->media_count - i - 1));
|
|
rtsp->media_count--;
|
|
i--; // redo
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
assert(0);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
rtsp->aggregate = (rtsp->media_count > 1 && rtsp->media[0].session_uri[0]) ? 1 : 0;
|
|
if (rtsp->aggregate)
|
|
{
|
|
snprintf(rtsp->aggregate_uri, sizeof(rtsp->aggregate_uri), "%s", rtsp->media[0].session_uri);
|
|
}
|
|
else if(rtsp->media_count > 1)
|
|
{
|
|
// fix some IPC set Content-Base only
|
|
const char* base;
|
|
base = rtsp->baseuri[0] ? rtsp->baseuri : rtsp->location;
|
|
rtsp->aggregate = base[0] ? 1 : 0;
|
|
if(rtsp->aggregate)
|
|
snprintf(rtsp->aggregate_uri, sizeof(rtsp->aggregate_uri), "%s", base);
|
|
}
|
|
|
|
rtsp->state = RTSP_SETUP;
|
|
rtsp->progress = 0;
|
|
return rtsp_client_media_setup(rtsp, rtsp->progress);
|
|
}
|
|
|
|
int rtsp_client_setup_onreply(struct rtsp_client_t* rtsp, void* parser)
|
|
{
|
|
int code;
|
|
const char *session;
|
|
const char *transport;
|
|
struct rtsp_header_range_t* range;
|
|
|
|
assert(RTSP_SETUP == rtsp->state);
|
|
assert(rtsp->progress < rtsp->media_count);
|
|
|
|
code = http_get_status_code(parser);
|
|
if (200 == code)
|
|
{
|
|
session = http_get_header_by_name(parser, "Session");
|
|
transport = http_get_header_by_name(parser, "Transport");
|
|
if (!session || 0 != rtsp_header_session(session, &rtsp->session[rtsp->progress])
|
|
|| !transport || 0 != rtsp_header_transport(transport, &rtsp->transport[rtsp->progress]))
|
|
{
|
|
printf("Get rtsp transport error.\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
//assert(rtsp->media[rtsp->progress].transport.transport != RTSP_TRANSPORT_RTP_TCP || (rtsp->media[rtsp->progress].transport.rtp.u.client_port1== rtsp->media[rtsp->progress].transport.interleaved1 && rtsp->media[rtsp->progress].transport.rtp.u.client_port2 == rtsp->media[rtsp->progress].transport.interleaved2));
|
|
assert(strlen(session) < sizeof(rtsp->session[0].session));
|
|
assert(!rtsp->aggregate || 0 == strcmp(rtsp->session[0].session, rtsp->session[rtsp->progress].session));
|
|
|
|
if (rtsp->media_count == ++rtsp->progress)
|
|
{
|
|
assert(rtsp->media_count > 0);
|
|
range = &rtsp->media[0].range;
|
|
return rtsp->handler.onsetup(rtsp->param, rtsp->session[rtsp->progress].timeout / 1000, RTSP_RANGE_TIME_NORMAL==range->from_value && RTSP_RANGE_TIME_NORMAL==range->to_value ? range->to - range->from : -1);
|
|
}
|
|
else
|
|
{
|
|
// setup next media
|
|
return rtsp_client_media_setup(rtsp, rtsp->progress);
|
|
}
|
|
}
|
|
else if (401 == code)
|
|
{
|
|
// Unauthorized
|
|
const char* authenticate;
|
|
authenticate = http_get_header_by_name(parser, "WWW-Authenticate");
|
|
if (authenticate)
|
|
{
|
|
rtsp_client_www_authenticate(rtsp, authenticate);
|
|
}
|
|
return -EACCES; // try again
|
|
}
|
|
else if (461 == code)
|
|
{
|
|
// Unsupported Transport
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
return -1;
|
|
}
|
|
}
|