/* 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; } }