diff --git a/apps/app_echo.c b/apps/app_echo.c index 7690209ac5307afa9fe240e73ddfc054670f1cc0..27ba0d86f5020b9ef06dc4b152752cd638c6c699 100755 --- a/apps/app_echo.c +++ b/apps/app_echo.c @@ -55,6 +55,9 @@ static int echo_exec(struct ast_channel *chan, void *data) if (f->frametype == AST_FRAME_VOICE) { if (ast_write(chan, f)) break; + } else if (f->frametype == AST_FRAME_VIDEO) { + if (ast_write(chan, f)) + break; } else if (f->frametype == AST_FRAME_DTMF) { if (f->subclass == '#') { res = 0; diff --git a/channel.c b/channel.c index 3071113c3f4c313b425344a5cc20517e9e8f2de1..2127a46bdea8b6382aabe2ddff27b82a1ac1a798 100755 --- a/channel.c +++ b/channel.c @@ -1,4 +1,4 @@ -/* + /* * Asterisk -- A telephony toolkit for Linux. * * Channel Management @@ -1269,6 +1269,17 @@ int ast_prod(struct ast_channel *chan) return 0; } +int ast_write_video(struct ast_channel *chan, struct ast_frame *fr) +{ + int res; + if (!chan->pvt->write_video) + return 0; + res = ast_write(chan, fr); + if (!res) + res = 1; + return res; +} + int ast_write(struct ast_channel *chan, struct ast_frame *fr) { int res = -1; @@ -1306,6 +1317,13 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr) if (chan->pvt->send_text) res = chan->pvt->send_text(chan, (char *) fr->data); break; + case AST_FRAME_VIDEO: + /* XXX Handle translation of video codecs one day XXX */ + if (chan->pvt->write_video) + res = chan->pvt->write_video(chan, fr); + else + res = 0; + break; default: if (chan->pvt->write) { if (chan->pvt->writetrans) { diff --git a/channels/chan_mgcp.c b/channels/chan_mgcp.c index c6a29e4fd8dace34dd8ed018261873e7c9a4d147..27a962bd330a5d34d4ace646cd9e1ca281983716 100755 --- a/channels/chan_mgcp.c +++ b/channels/chan_mgcp.c @@ -1717,7 +1717,7 @@ static void start_rtp(struct mgcp_subchannel *sub) { ast_pthread_mutex_lock(&sub->lock); /* Allocate the RTP now */ - sub->rtp = ast_rtp_new(NULL, NULL); + sub->rtp = ast_rtp_new(sched, io, 1, 0); if (sub->rtp && sub->owner) sub->owner->fds[0] = ast_rtp_fd(sub->rtp); if (sub->rtp) @@ -2791,8 +2791,9 @@ static struct ast_rtp *mgcp_get_rtp_peer(struct ast_channel *chan) return NULL; } -static int mgcp_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp) +static int mgcp_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp) { + /* XXX Is there such thing as video support with MGCP? XXX */ struct mgcp_subchannel *sub; sub = chan->pvt->pvt; if (sub) { diff --git a/channels/chan_sip.c b/channels/chan_sip.c index 6ff15bcacbeec603abd79bcae3f9bf13e588f526..35a506aeb39e871eff730b2f677633b4cd8a9628 100755 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -114,7 +114,7 @@ static pthread_t monitor_thread = 0; static int restart_monitor(void); /* Codecs that we support by default: */ -static int capability = AST_FORMAT_ULAW | AST_FORMAT_ALAW | AST_FORMAT_GSM; +static int capability = AST_FORMAT_ULAW | AST_FORMAT_ALAW | AST_FORMAT_GSM | AST_FORMAT_H261; static int noncodeccapability = AST_RTP_DTMF; static char ourhost[256]; @@ -125,6 +125,8 @@ static int sipdebug = 0; static int tos = 0; +static int videosupport = 0; + static int globaldtmfmode = SIP_DTMF_RFC2833; /* Expire slowly */ @@ -186,6 +188,7 @@ static struct sip_pvt { int nat; /* Whether to try to support NAT */ struct sockaddr_in sa; /* Our peer */ struct sockaddr_in redirip; /* Where our RTP should be going if not to us */ + struct sockaddr_in vredirip; /* Where our Video RTP should be going if not to us */ struct sockaddr_in recv; /* Received as */ struct in_addr ourip; /* Our IP */ struct ast_channel *owner; /* Who owns us */ @@ -233,6 +236,7 @@ static struct sip_pvt { struct sip_peer *peerpoke; /* If this calls is to poke a peer, which one */ struct sip_registry *registry; /* If this is a REGISTER call, to which registry */ struct ast_rtp *rtp; /* RTP Session */ + struct ast_rtp *vrtp; /* Video RTP session */ struct sip_pkt *packets; /* Packets scheduled for re-transmission */ struct sip_pvt *next; } *iflist = NULL; @@ -367,7 +371,7 @@ static int transmit_response_with_auth(struct sip_pvt *p, char *msg, struct sip_ static int transmit_request(struct sip_pvt *p, char *msg, int inc, int reliable); static int transmit_request_with_auth(struct sip_pvt *p, char *msg, int inc, int reliable); static int transmit_invite(struct sip_pvt *p, char *msg, int sendsdp, char *auth, char *vxml_url,char *distinctive_ring); -static int transmit_reinvite_with_sdp(struct sip_pvt *p, struct ast_rtp *rtp); +static int transmit_reinvite_with_sdp(struct sip_pvt *p, struct ast_rtp *rtp, struct ast_rtp *vrtp); static int transmit_info_with_digit(struct sip_pvt *p, char digit); static int transmit_message_with_text(struct sip_pvt *p, char *text); static int transmit_refer(struct sip_pvt *p, char *dest); @@ -612,6 +616,10 @@ static int create_addr(struct sip_pvt *r, char *peer) ast_log(LOG_DEBUG, "Setting NAT on RTP to %d\n", r->nat); ast_rtp_setnat(r->rtp, r->nat); } + if (r->vrtp) { + ast_log(LOG_DEBUG, "Setting NAT on VRTP to %d\n", r->nat); + ast_rtp_setnat(r->vrtp, r->nat); + } strncpy(r->peername, p->username, sizeof(r->peername)-1); strncpy(r->peersecret, p->secret, sizeof(r->peersecret)-1); strncpy(r->username, p->username, sizeof(r->username)-1); @@ -761,6 +769,7 @@ static int sip_pref_append(int format) static int sip_codec_choose(int formats) { struct sip_codec_pref *cur; + formats &= (AST_FORMAT_MAX_AUDIO - 1); cur = prefs; while(cur) { if (formats & cur->codec) @@ -828,6 +837,9 @@ static void __sip_destroy(struct sip_pvt *p, int lockowner) if (p->rtp) { ast_rtp_destroy(p->rtp); } + if (p->vrtp) { + ast_rtp_destroy(p->vrtp); + } if (p->route) { free_old_route(p->route); p->route = NULL; @@ -965,31 +977,42 @@ static int sip_write(struct ast_channel *ast, struct ast_frame *frame) { struct sip_pvt *p = ast->pvt->pvt; int res = 0; - if (frame->frametype != AST_FRAME_VOICE) { - if (frame->frametype == AST_FRAME_IMAGE) - return 0; - else { - ast_log(LOG_WARNING, "Can't send %d type frames with SIP write\n", frame->frametype); - return 0; - } - } else { + if (frame->frametype == AST_FRAME_VOICE) { if (!(frame->subclass & ast->nativeformats)) { ast_log(LOG_WARNING, "Asked to transmit frame type %d, while native formats is %d (read/write = %d/%d)\n", frame->subclass, ast->nativeformats, ast->readformat, ast->writeformat); return -1; } - } - if (p) { - ast_pthread_mutex_lock(&p->lock); - if (p->rtp) { - if ((ast->_state != AST_STATE_UP) && !p->progress && !p->outgoing) { - transmit_response_with_sdp(p, "183 Session Progress", &p->initreq, 0); - p->progress = 1; + if (p) { + ast_pthread_mutex_lock(&p->lock); + if (p->rtp) { + if ((ast->_state != AST_STATE_UP) && !p->progress && !p->outgoing) { + transmit_response_with_sdp(p, "183 Session Progress", &p->initreq, 0); + p->progress = 1; + } + res = ast_rtp_write(p->rtp, frame); } - res = ast_rtp_write(p->rtp, frame); + ast_pthread_mutex_unlock(&p->lock); } - ast_pthread_mutex_unlock(&p->lock); + } else if (frame->frametype == AST_FRAME_VIDEO) { + if (p) { + ast_pthread_mutex_lock(&p->lock); + if (p->vrtp) { + if ((ast->_state != AST_STATE_UP) && !p->progress && !p->outgoing) { + transmit_response_with_sdp(p, "183 Session Progress", &p->initreq, 0); + p->progress = 1; + } + res = ast_rtp_write(p->vrtp, frame); + } + ast_pthread_mutex_unlock(&p->lock); + } + } else if (frame->frametype == AST_FRAME_IMAGE) { + return 0; + } else { + ast_log(LOG_WARNING, "Can't send %d type frames with SIP write\n", frame->frametype); + return 0; } + return res; } @@ -1109,6 +1132,11 @@ static struct ast_channel *sip_new(struct sip_pvt *i, int state, char *title) ast_dsp_set_features(i->vad, DSP_FEATURE_DTMF_DETECT); } tmp->fds[0] = ast_rtp_fd(i->rtp); + tmp->fds[1] = ast_rtcp_fd(i->rtp); + if (i->vrtp) { + tmp->fds[2] = ast_rtp_fd(i->vrtp); + tmp->fds[3] = ast_rtcp_fd(i->vrtp); + } ast_setstate(tmp, state); if (state == AST_STATE_RING) tmp->rings = 1; @@ -1124,6 +1152,7 @@ static struct ast_channel *sip_new(struct sip_pvt *i, int state, char *title) tmp->pvt->answer = sip_answer; tmp->pvt->read = sip_read; tmp->pvt->write = sip_write; + tmp->pvt->write_video = sip_write; tmp->pvt->indicate = sip_indicate; tmp->pvt->transfer = sip_transfer; tmp->pvt->fixup = sip_fixup; @@ -1245,12 +1274,27 @@ static char *get_header(struct sip_request *req, char *name) return __get_header(req, name, &start); } -static struct ast_frame *sip_rtp_read(struct sip_pvt *p) +static struct ast_frame *sip_rtp_read(struct ast_channel *ast, struct sip_pvt *p) { /* Retrieve audio/etc from channel. Assumes p->lock is already held. */ struct ast_frame *f; static struct ast_frame null_frame = { AST_FRAME_NULL, }; - f = ast_rtp_read(p->rtp); + switch(ast->fdno) { + case 0: + f = ast_rtp_read(p->rtp); + break; + case 1: + f = ast_rtcp_read(p->rtp); + break; + case 2: + f = ast_rtp_read(p->vrtp); + break; + case 3: + f = ast_rtcp_read(p->vrtp); + break; + default: + f = &null_frame; + } /* Don't send RFC2833 if we're not supposed to */ if (f && (f->frametype == AST_FRAME_DTMF) && !(p->dtmfmode & SIP_DTMF_RFC2833)) return &null_frame; @@ -1276,7 +1320,7 @@ static struct ast_frame *sip_read(struct ast_channel *ast) struct ast_frame *fr; struct sip_pvt *p = ast->pvt->pvt; ast_pthread_mutex_lock(&p->lock); - fr = sip_rtp_read(p); + fr = sip_rtp_read(ast, p); ast_pthread_mutex_unlock(&p->lock); return fr; } @@ -1308,7 +1352,9 @@ static struct sip_pvt *sip_alloc(char *callid, struct sockaddr_in *sin, int useg p->initid = -1; p->autokillid = -1; p->stateid = -1; - p->rtp = ast_rtp_new(NULL, NULL); + p->rtp = ast_rtp_new(sched, io, 1, 0); + if (videosupport) + p->vrtp = ast_rtp_new(sched, io, 1, 0); p->branch = rand(); p->tag = rand(); @@ -1320,17 +1366,18 @@ static struct sip_pvt *sip_alloc(char *callid, struct sockaddr_in *sin, int useg return NULL; } ast_rtp_settos(p->rtp, tos); + if (p->vrtp) + ast_rtp_settos(p->vrtp, tos); if (useglobalnat && sin) { /* Setup NAT structure according to global settings if we have an address */ p->nat = globalnat; memcpy(&p->recv, sin, sizeof(p->recv)); ast_rtp_setnat(p->rtp, p->nat); + if (p->vrtp) + ast_rtp_setnat(p->vrtp, p->nat); } ast_pthread_mutex_init(&p->lock); -#if 0 - ast_rtp_set_data(p->rtp, p); - ast_rtp_set_callback(p->rtp, rtpready); -#endif + if (sin) { memcpy(&p->sa, sin, sizeof(p->sa)); if (ast_ouraddrfor(&p->sa.sin_addr,&p->ourip)) @@ -1554,13 +1601,16 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req) char *a; char host[258]; int len = -1; - int portno; + int portno=0; + int vportno=0; int peercapability, peernoncodeccapability; + int vpeercapability, vpeernoncodeccapability; struct sockaddr_in sin; char *codecs; struct hostent *hp; int codec; int iterator; + int x; /* Get codec and RTP info from SDP */ if (strcasecmp(get_header(req, "Content-Type"), "application/sdp")) { @@ -1583,51 +1633,85 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req) ast_log(LOG_WARNING, "Unable to lookup host in c= line, '%s'\n", c); return -1; } - if ((sscanf(m, "audio %d RTP/AVP %n", &portno, &len) != 1) || (len < 0)) { - ast_log(LOG_WARNING, "Unable to determine port number for RTP in '%s'\n", m); - return -1; + sdpLineNum_iterator_init(&iterator); + while ((m = get_sdp_iterate(&iterator, req, "m"))[0] != '\0') { + if ((sscanf(m, "audio %d RTP/AVP %n", &x, &len) == 1)) { + portno = x; + // Scan through the RTP payload types specified in a "m=" line: + ast_rtp_pt_clear(p->rtp); + codecs = m + len; + while(strlen(codecs)) { + if (sscanf(codecs, "%d%n", &codec, &len) != 1) { + ast_log(LOG_WARNING, "Error in codec string '%s'\n", codecs); + return -1; + } + if (sipdebug) + ast_verbose("Found audio format %d\n", codec); + ast_rtp_set_m_type(p->rtp, codec); + codecs += len; + /* Skip over any whitespace */ + while(*codecs && (*codecs < 33)) codecs++; + } + } + if (p->vrtp && (sscanf(m, "video %d RTP/AVP %n", &x, &len) == 1)) { + vportno = x; + // Scan through the RTP payload types specified in a "m=" line: + ast_rtp_pt_clear(p->vrtp); + codecs = m + len; + while(strlen(codecs)) { + if (sscanf(codecs, "%d%n", &codec, &len) != 1) { + ast_log(LOG_WARNING, "Error in codec string '%s'\n", codecs); + return -1; + } + if (sipdebug) + ast_verbose("Found video format %d\n", codec); + ast_rtp_set_m_type(p->vrtp, codec); + codecs += len; + /* Skip over any whitespace */ + while(*codecs && (*codecs < 33)) codecs++; + } + } } sin.sin_family = AF_INET; memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr)); + /* Setup audio port number */ sin.sin_port = htons(portno); - if (p->rtp) + if (p->rtp && sin.sin_port) ast_rtp_set_peer(p->rtp, &sin); + /* Setup video port number */ + sin.sin_port = htons(vportno); + if (p->vrtp && sin.sin_port) + ast_rtp_set_peer(p->vrtp, &sin); #if 0 printf("Peer RTP is at port %s:%d\n", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); #endif - // Scan through the RTP payload types specified in a "m=" line: - ast_rtp_pt_clear(p->rtp); - codecs = m + len; - while(strlen(codecs)) { - if (sscanf(codecs, "%d%n", &codec, &len) != 1) { - ast_log(LOG_WARNING, "Error in codec string '%s'\n", codecs); - return -1; - } - ast_rtp_set_m_type(p->rtp, codec); - codecs += len; - /* Skip over any whitespace */ - while(*codecs && (*codecs < 33)) codecs++; - } - // Next, scan through each "a=rtpmap:" line, noting each // specified RTP payload type (with corresponding MIME subtype): sdpLineNum_iterator_init(&iterator); while ((a = get_sdp_iterate(&iterator, req, "a"))[0] != '\0') { - char* mimeSubtype = strdup(a); // ensures we have enough space + char* mimeSubtype = ast_strdupa(a); // ensures we have enough space + if (sipdebug) + ast_verbose("Pre-Found description format %s\n", mimeSubtype); if (sscanf(a, "rtpmap: %u %[^/]/", &codec, mimeSubtype) != 2) continue; + if (sipdebug) + ast_verbose("Found description format %s\n", mimeSubtype); // Note: should really look at the 'freq' and '#chans' params too ast_rtp_set_rtpmap_type(p->rtp, codec, "audio", mimeSubtype); - free(mimeSubtype); + if (p->vrtp) + ast_rtp_set_rtpmap_type(p->vrtp, codec, "video", mimeSubtype); } // Now gather all of the codecs that were asked for: ast_rtp_get_current_formats(p->rtp, &peercapability, &peernoncodeccapability); - p->capability = capability & peercapability; - p->noncodeccapability = noncodeccapability & peernoncodeccapability; + ast_rtp_get_current_formats(p->vrtp, + &vpeercapability, &vpeernoncodeccapability); + p->capability = capability & (peercapability | vpeercapability); + p->noncodeccapability = noncodeccapability & (peernoncodeccapability | vpeernoncodeccapability); + if (sipdebug) { - ast_verbose("Capabilities: us - %d, them - %d, combined - %d\n", - capability, peercapability, p->capability); + ast_verbose("Capabilities: us - %d, them - %d/%d, combined - %d\n", + capability, peercapability, vpeercapability, p->capability); ast_verbose("Non-codec capabilities: us - %d, them - %d, combined - %d\n", noncodeccapability, peernoncodeccapability, p->noncodeccapability); @@ -2114,13 +2198,14 @@ static int add_digit(struct sip_request *req, char digit) return 0; } -static int add_sdp(struct sip_request *resp, struct sip_pvt *p, struct ast_rtp *rtp) +static int add_sdp(struct sip_request *resp, struct sip_pvt *p, struct ast_rtp *rtp, struct ast_rtp *vrtp) { int len; int codec; int alreadysent = 0; char costr[80]; struct sockaddr_in sin; + struct sockaddr_in vsin; struct sip_codec_pref *cur; char v[256]; char s[256]; @@ -2128,9 +2213,12 @@ static int add_sdp(struct sip_request *resp, struct sip_pvt *p, struct ast_rtp * char c[256]; char t[256]; char m[256]; + char m2[256]; char a[1024] = ""; + char a2[1024] = ""; int x; struct sockaddr_in dest; + struct sockaddr_in vdest; /* XXX We break with the "recommendation" and send our IP, in order that our peer doesn't have to gethostbyname() us XXX */ len = 0; @@ -2139,6 +2227,9 @@ static int add_sdp(struct sip_request *resp, struct sip_pvt *p, struct ast_rtp * return -1; } ast_rtp_get_us(p->rtp, &sin); + if (p->vrtp) + ast_rtp_get_us(p->vrtp, &vsin); + if (p->redirip.sin_addr.s_addr) { dest.sin_port = p->redirip.sin_port; dest.sin_addr = p->redirip.sin_addr; @@ -2148,14 +2239,30 @@ static int add_sdp(struct sip_request *resp, struct sip_pvt *p, struct ast_rtp * dest.sin_addr = p->ourip; dest.sin_port = sin.sin_port; } + + /* Determine video destination */ + if (p->vrtp) { + if (p->vredirip.sin_addr.s_addr) { + vdest.sin_port = p->vredirip.sin_port; + vdest.sin_addr = p->vredirip.sin_addr; + } else if (vrtp) { + ast_rtp_get_peer(vrtp, &vdest); + } else { + vdest.sin_addr = p->ourip; + vdest.sin_port = vsin.sin_port; + } + } if (sipdebug) ast_verbose("We're at %s port %d\n", inet_ntoa(p->ourip), ntohs(sin.sin_port)); + if (sipdebug && p->vrtp) + ast_verbose("Video is at %s port %d\n", inet_ntoa(p->ourip), ntohs(vsin.sin_port)); snprintf(v, sizeof(v), "v=0\r\n"); snprintf(o, sizeof(o), "o=root %d %d IN IP4 %s\r\n", getpid(), getpid(), inet_ntoa(dest.sin_addr)); snprintf(s, sizeof(s), "s=session\r\n"); snprintf(c, sizeof(c), "c=IN IP4 %s\r\n", inet_ntoa(dest.sin_addr)); snprintf(t, sizeof(t), "t=0 0\r\n"); snprintf(m, sizeof(m), "m=audio %d RTP/AVP", ntohs(dest.sin_port)); + snprintf(m2, sizeof(m2), "m=video %d RTP/AVP", ntohs(vdest.sin_port)); /* Start by sending our preferred codecs */ cur = prefs; while(cur) { @@ -2165,9 +2272,15 @@ static int add_sdp(struct sip_request *resp, struct sip_pvt *p, struct ast_rtp * codec = ast_rtp_lookup_code(p->rtp, 1, cur->codec); if (codec > -1) { snprintf(costr, sizeof(costr), " %d", codec); - strcat(m, costr); - snprintf(costr, sizeof(costr), "a=rtpmap:%d %s/8000\r\n", codec, ast_rtp_lookup_mime_subtype(1, cur->codec)); - strcat(a, costr); + if (cur->codec < AST_FORMAT_MAX_AUDIO) { + strcat(m, costr); + snprintf(costr, sizeof(costr), "a=rtpmap:%d %s/8000\r\n", codec, ast_rtp_lookup_mime_subtype(1, cur->codec)); + strcat(a, costr); + } else { + strcat(m2, costr); + snprintf(costr, sizeof(costr), "a=rtpmap:%d %s/90000\r\n", codec, ast_rtp_lookup_mime_subtype(1, cur->codec)); + strcat(a2, costr); + } } } alreadysent |= cur->codec; @@ -2180,10 +2293,16 @@ static int add_sdp(struct sip_request *resp, struct sip_pvt *p, struct ast_rtp * ast_verbose("Answering with capability %d\n", x); codec = ast_rtp_lookup_code(p->rtp, 1, x); if (codec > -1) { - snprintf(costr, sizeof(costr), " %d", codec); - strcat(m, costr); - snprintf(costr, sizeof(costr), "a=rtpmap:%d %s/8000\r\n", codec, ast_rtp_lookup_mime_subtype(1, x)); - strcat(a, costr); + snprintf(costr, sizeof(costr), " %d", codec); + if (x < AST_FORMAT_MAX_AUDIO) { + strcat(m, costr); + snprintf(costr, sizeof(costr), "a=rtpmap:%d %s/8000\r\n", codec, ast_rtp_lookup_mime_subtype(1, x)); + strcat(a, costr); + } else { + strcat(m2, costr); + snprintf(costr, sizeof(costr), "a=rtpmap:%d %s/90000\r\n", codec, ast_rtp_lookup_mime_subtype(1, x)); + strcat(a2, costr); + } } } } @@ -2207,7 +2326,10 @@ static int add_sdp(struct sip_request *resp, struct sip_pvt *p, struct ast_rtp * } } strcat(m, "\r\n"); + strcat(m2, "\r\n"); len = strlen(v) + strlen(s) + strlen(o) + strlen(c) + strlen(t) + strlen(m) + strlen(a); + if (p->vrtp) + len += strlen(m2) + strlen(a2); snprintf(costr, sizeof(costr), "%d", len); add_header(resp, "Content-Type", "application/sdp"); add_header(resp, "Content-Length", costr); @@ -2218,6 +2340,10 @@ static int add_sdp(struct sip_request *resp, struct sip_pvt *p, struct ast_rtp * add_line(resp, t); add_line(resp, m); add_line(resp, a); + if (p->vrtp) { + add_line(resp, m2); + add_line(resp, a2); + } return 0; } @@ -2244,7 +2370,7 @@ static int transmit_response_with_sdp(struct sip_pvt *p, char *msg, struct sip_r return -1; } respprep(&resp, p, msg, req); - add_sdp(&resp, p, NULL); + add_sdp(&resp, p, NULL, NULL); return send_response(p, &resp, retrans, seqno); } @@ -2307,14 +2433,14 @@ static int determine_firstline_parts( struct sip_request *req ) { return 1; } -static int transmit_reinvite_with_sdp(struct sip_pvt *p, struct ast_rtp *rtp) +static int transmit_reinvite_with_sdp(struct sip_pvt *p, struct ast_rtp *rtp, struct ast_rtp *vrtp) { struct sip_request req; if (p->canreinvite == REINVITE_UPDATE) reqprep(&req, p, "UPDATE", 0); else reqprep(&req, p, "INVITE", 0); - add_sdp(&req, p, rtp); + add_sdp(&req, p, rtp, vrtp); /* Use this as the basis */ copy_request(&p->initreq, &req); parse(&p->initreq); @@ -2410,7 +2536,7 @@ static int transmit_invite(struct sip_pvt *p, char *cmd, int sdp, char *auth, ch add_header(&req, "Alert-info",distinctive_ring); } if (sdp) { - add_sdp(&req, p, NULL); + add_sdp(&req, p, NULL, NULL); } else { add_header(&req, "Content-Length", "0"); add_blank_header(&req); @@ -3437,6 +3563,10 @@ static int check_user(struct sip_pvt *p, struct sip_request *req, char *cmd, cha ast_log(LOG_DEBUG, "Setting NAT on RTP to %d\n", p->nat); ast_rtp_setnat(p->rtp, p->nat); } + if (p->vrtp) { + ast_log(LOG_DEBUG, "Setting NAT on VRTP to %d\n", p->nat); + ast_rtp_setnat(p->vrtp, p->nat); + } if (!(res = check_auth(p, req, p->randdata, sizeof(p->randdata), user->name, user->secret, cmd, uri, reliable))) { sip_cancel_destroy(p); if (strlen(user->context)) @@ -3475,6 +3605,10 @@ static int check_user(struct sip_pvt *p, struct sip_request *req, char *cmd, cha ast_log(LOG_DEBUG, "Setting NAT on RTP to %d\n", p->nat); ast_rtp_setnat(p->rtp, p->nat); } + if (p->vrtp) { + ast_log(LOG_DEBUG, "Setting NAT on VRTP to %d\n", p->nat); + ast_rtp_setnat(p->vrtp, p->nat); + } p->canreinvite = peer->canreinvite; strncpy(p->username, peer->name, sizeof(p->username) - 1); if (strlen(peer->context)) @@ -4124,6 +4258,10 @@ static void handle_response(struct sip_pvt *p, int resp, char *rest, struct sip_ /* Immediately stop RTP */ ast_rtp_stop(p->rtp); } + if (p->vrtp) { + /* Immediately stop VRTP */ + ast_rtp_stop(p->vrtp); + } /* XXX Locking issues?? XXX */ switch(resp) { case 302: /* Moved temporarily */ @@ -4466,6 +4604,10 @@ static int handle_request(struct sip_pvt *p, struct sip_request *req, struct soc /* Immediately stop RTP */ ast_rtp_stop(p->rtp); } + if (p->vrtp) { + /* Immediately stop VRTP */ + ast_rtp_stop(p->vrtp); + } if (p->owner) ast_queue_hangup(p->owner, 0); transmit_response(p, "200 OK", req); @@ -4478,6 +4620,10 @@ static int handle_request(struct sip_pvt *p, struct sip_request *req, struct soc /* Immediately stop RTP */ ast_rtp_stop(p->rtp); } + if (p->vrtp) { + /* Immediately stop VRTP */ + ast_rtp_stop(p->vrtp); + } if (p->owner) ast_queue_hangup(p->owner, 0); transmit_response(p, "200 OK", req); @@ -5238,6 +5384,7 @@ static int reload_config(void) strcpy(language, ""); strcpy(fromdomain, ""); globalcanreinvite = REINVITE_INVITE; + videosupport = 0; v = ast_variable_browse(cfg, "general"); while(v) { /* Create the interface list */ @@ -5254,6 +5401,8 @@ static int reload_config(void) ast_log(LOG_WARNING, "Unknown dtmf mode '%s', using rfc2833\n", v->value); globaldtmfmode = SIP_DTMF_RFC2833; } + } else if (!strcasecmp(v->name, "videosupport")) { + videosupport = ast_true(v->value); } else if (!strcasecmp(v->name, "notifymimetype")) { strncpy(notifymime, v->value, sizeof(notifymime) - 1); } else if (!strcasecmp(v->name, "language")) { @@ -5422,7 +5571,16 @@ static struct ast_rtp *sip_get_rtp_peer(struct ast_channel *chan) return NULL; } -static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp) +static struct ast_rtp *sip_get_vrtp_peer(struct ast_channel *chan) +{ + struct sip_pvt *p; + p = chan->pvt->pvt; + if (p && p->vrtp && p->canreinvite) + return p->vrtp; + return NULL; +} + +static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp) { struct sip_pvt *p; p = chan->pvt->pvt; @@ -5431,7 +5589,11 @@ static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp) ast_rtp_get_peer(rtp, &p->redirip); else memset(&p->redirip, 0, sizeof(p->redirip)); - transmit_reinvite_with_sdp(p, rtp); + if (vrtp) + ast_rtp_get_peer(vrtp, &p->vredirip); + else + memset(&p->vredirip, 0, sizeof(p->vredirip)); + transmit_reinvite_with_sdp(p, rtp, vrtp); p->outgoing = 1; return 0; } @@ -5440,6 +5602,7 @@ static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp) static struct ast_rtp_protocol sip_rtp = { get_rtp_info: sip_get_rtp_peer, + get_vrtp_info: sip_get_vrtp_peer, set_rtp_peer: sip_set_rtp_peer, }; diff --git a/configs/sip.conf.sample b/configs/sip.conf.sample index a26996939de9acc4e6e8b1e97205eb7682bfeb3b..aa54ea02c03669c290a7478ede972622f23fc80a 100755 --- a/configs/sip.conf.sample +++ b/configs/sip.conf.sample @@ -11,6 +11,7 @@ context = default ; Default for incoming calls ;maxexpirey=3600 ; Max length of incoming registration we allow ;defaultexpirey=120 ; Default length of incoming/outoing registration ;notifymimetype=text/plain ; Allow overriding of mime type in NOTIFY +;videosupport=yes ; Turn on support for SIP video ; ;register => 1234@mysipprovider.com ; Register with a SIP provider ;register => 2345@mysipprovider.com/1234 ; Register 2345 at sip provider as 1234 here. diff --git a/frame.c b/frame.c index 482014ca49b1f4ca4c436b9ada1efc40eab4d33e..ec5c59dda2205a69efa64e5c2c0397a45f20298c 100755 --- a/frame.c +++ b/frame.c @@ -367,6 +367,10 @@ int ast_getformatbyname(char *name) return AST_FORMAT_SPEEX; else if (!strcasecmp(name, "ilbc")) return AST_FORMAT_ILBC; + else if (!strcasecmp(name, "h261")) + return AST_FORMAT_H261; + else if (!strcasecmp(name, "h263")) + return AST_FORMAT_H263; else if (!strcasecmp(name, "all")) return 0x7FFFFFFF; return 0; diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h index 03b1fa4487e6b170d5bf43caa5f6a99283f325fc..c56a1fc76ac7114c7971ed41a467895741e0b4d1 100755 --- a/include/asterisk/channel.h +++ b/include/asterisk/channel.h @@ -39,7 +39,7 @@ extern "C" { #define MAX_LANGUAGE 20 -#define AST_MAX_FDS 4 +#define AST_MAX_FDS 8 struct ast_generator { void *(*alloc)(struct ast_channel *chan, void *params); @@ -72,7 +72,13 @@ struct ast_channel { int writeinterrupt; /*! Who are we bridged to, if we're bridged */ - struct ast_channel *bridge; + struct ast_channel *bridge; + /*! Who did we call? */ + struct ast_channel *dialed; + /*! Who called us? */ + struct ast_channel *dialing; + /*! Reverse the dialed link (0 false, 1 true) */ + int reversedialed; /*! Channel that will masquerade as us */ struct ast_channel *masq; /*! Who we are masquerading as */ @@ -122,7 +128,7 @@ struct ast_channel { /*! Number of rings so far */ int rings; /*! Current level of application */ - int stack; + int stack; /*! Kinds of data this channel can natively handle */ @@ -199,7 +205,7 @@ struct ast_channel { unsigned int pickupgroup; /*! For easy linking */ - struct ast_channel *next; + struct ast_channel *next; }; @@ -452,7 +458,7 @@ int ast_waitfor_n_fd(int *fds, int n, int *ms, int *exception); //! Reads a frame -/*! +/*! * \param chan channel to read a frame from * Read a frame. Returns a frame, or NULL on error. If it returns NULL, you best just stop reading frames and assume the channel has been @@ -468,6 +474,15 @@ struct ast_frame *ast_read(struct ast_channel *chan); */ int ast_write(struct ast_channel *chan, struct ast_frame *frame); +//! Write video frame to a channel +/*! + * \param chan destination channel of the frame + * \param frame frame that will be written + * This function writes the given frame to the indicated channel. + * It returns 1 on success, 0 if not implemented, and -1 on failure. + */ +int ast_write_video(struct ast_channel *chan, struct ast_frame *frame); + /* Send empty audio to prime a channel driver */ int ast_prod(struct ast_channel *chan); diff --git a/include/asterisk/channel_pvt.h b/include/asterisk/channel_pvt.h index d52a90fc658a4894e719a6b537b204770c4004ce..366101eea8e6c222c4fb4061e9f212c975f73b4e 100755 --- a/include/asterisk/channel_pvt.h +++ b/include/asterisk/channel_pvt.h @@ -67,6 +67,8 @@ struct ast_channel_pvt { int (*queryoption)(struct ast_channel *chan, int option, void *data, int *datalen); /*! Blind transfer other side */ int (*transfer)(struct ast_channel *chan, char *newdest); + /*! Write a frame, in standard format */ + int (*write_video)(struct ast_channel *chan, struct ast_frame *frame); }; //! Create a channel structure diff --git a/include/asterisk/frame.h b/include/asterisk/frame.h index b0ea7ef0f63af7c612bf3ab7fc173fb180ba7660..37e681024a8ce9c593e90aada5d876cc85ea9ebf 100755 --- a/include/asterisk/frame.h +++ b/include/asterisk/frame.h @@ -170,6 +170,8 @@ struct ast_frame_chain { #define AST_FORMAT_H261 (1 << 18) /*! H.263 Video */ #define AST_FORMAT_H263 (1 << 19) +/*! Max one */ +#define AST_FORMAT_MAX_VIDEO (1 << 24) /* Control frame types */ /*! Other end has hungup */ diff --git a/include/asterisk/rtp.h b/include/asterisk/rtp.h index c183cb1bdbc4c58953dffb435043e9d298860a8a..cc8d24da7d5e32b4c6f61c6417f929f9b8c0901a 100755 --- a/include/asterisk/rtp.h +++ b/include/asterisk/rtp.h @@ -37,8 +37,8 @@ extern "C" { struct ast_rtp_protocol { struct ast_rtp *(*get_rtp_info)(struct ast_channel *chan); /* Get RTP struct, or NULL if unwilling to transfer */ - int (*set_rtp_peer)(struct ast_channel *chan, struct ast_rtp *peer); /* Set RTP peer */ - int (*get_rtp_willing)(struct ast_channel *chan); /* Willing to native bridge */ + struct ast_rtp *(*get_vrtp_info)(struct ast_channel *chan); /* Get RTP struct, or NULL if unwilling to transfer */ + int (*set_rtp_peer)(struct ast_channel *chan, struct ast_rtp *peer, struct ast_rtp *vpeer); /* Set RTP peer */ char *type; struct ast_rtp_protocol *next; }; @@ -47,11 +47,11 @@ struct ast_rtp; typedef int (*ast_rtp_callback)(struct ast_rtp *rtp, struct ast_frame *f, void *data); -struct ast_rtp *ast_rtp_new(struct sched_context *sched, struct io_context *io); +struct ast_rtp *ast_rtp_new(struct sched_context *sched, struct io_context *io, int rtcpenable, int callbackmode); void ast_rtp_set_peer(struct ast_rtp *rtp, struct sockaddr_in *them); -void ast_rtp_get_peer(struct ast_rtp *rpt, struct sockaddr_in *them); +void ast_rtp_get_peer(struct ast_rtp *rtp, struct sockaddr_in *them); void ast_rtp_get_us(struct ast_rtp *rtp, struct sockaddr_in *us); @@ -65,8 +65,12 @@ int ast_rtp_write(struct ast_rtp *rtp, struct ast_frame *f); struct ast_frame *ast_rtp_read(struct ast_rtp *rtp); +struct ast_frame *ast_rtcp_read(struct ast_rtp *rtp); + int ast_rtp_fd(struct ast_rtp *rtp); +int ast_rtcp_fd(struct ast_rtp *rtp); + int ast_rtp_senddigit(struct ast_rtp *rtp, char digit); int ast_rtp_settos(struct ast_rtp *rtp, int tos); diff --git a/rtp.c b/rtp.c index 63cb6c415d3de2f6b88b2278d748967a4f9c3525..6fd6f71903cccaaaefa6c54eacd675c1f43005e7 100755 --- a/rtp.c +++ b/rtp.c @@ -34,6 +34,8 @@ #include <asterisk/channel_pvt.h> #include <asterisk/config.h> +#define RTP_MTU 1200 + #define TYPE_HIGH 0x0 #define TYPE_LOW 0x1 #define TYPE_SILENCE 0x2 @@ -57,10 +59,13 @@ struct ast_rtp { int s; char resp; struct ast_frame f; - unsigned char rawdata[1024 + AST_FRIENDLY_OFFSET]; + unsigned char rawdata[8192 + AST_FRIENDLY_OFFSET]; + int readsofar; unsigned int ssrc; unsigned int lastts; unsigned int lastrxts; + unsigned int lastividtimestamp; + unsigned int lastovidtimestamp; int lasttxformat; int lastrxformat; int dtmfcount; @@ -81,6 +86,13 @@ struct ast_rtp { int rtp_lookup_code_cache_isAstFormat; int rtp_lookup_code_cache_code; int rtp_lookup_code_cache_result; + struct ast_rtcp *rtcp; +}; + +struct ast_rtcp { + int s; /* Socket */ + struct sockaddr_in us; + struct sockaddr_in them; }; static struct ast_rtp_protocol *protos = NULL; @@ -90,6 +102,13 @@ int ast_rtp_fd(struct ast_rtp *rtp) return rtp->s; } +int ast_rtcp_fd(struct ast_rtp *rtp) +{ + if (rtp->rtcp) + return rtp->rtcp->s; + return -1; +} + static int g723_len(unsigned char buf) { switch(buf & TYPE_MASK) { @@ -264,6 +283,47 @@ static int rtpread(int *id, int fd, short events, void *cbdata) return 1; } +struct ast_frame *ast_rtcp_read(struct ast_rtp *rtp) +{ + static struct ast_frame null_frame = { AST_FRAME_NULL, }; + int len; + int hdrlen = 8; + int res; + struct sockaddr_in sin; + unsigned int rtcpdata[1024]; + + if (!rtp->rtcp) + return &null_frame; + + len = sizeof(sin); + + res = recvfrom(rtp->rtcp->s, rtcpdata, sizeof(rtcpdata), + 0, (struct sockaddr *)&sin, &len); + + if (res < 0) { + ast_log(LOG_WARNING, "RTP Read error: %s\n", strerror(errno)); + if (errno == EBADF) + CRASH; + return &null_frame; + } + + if (res < hdrlen) { + ast_log(LOG_WARNING, "RTP Read too short\n"); + return &null_frame; + } + + if (rtp->nat) { + /* Send to whoever sent to us */ + if ((rtp->rtcp->them.sin_addr.s_addr != sin.sin_addr.s_addr) || + (rtp->rtcp->them.sin_port != sin.sin_port)) { + memcpy(&rtp->them, &sin, sizeof(rtp->them)); + ast_log(LOG_DEBUG, "RTP NAT: Using address %s:%d\n", inet_ntoa(rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port)); + } + } + ast_log(LOG_DEBUG, "Got RTCP report of %d bytes\n", res); + return &null_frame; +} + struct ast_frame *ast_rtp_read(struct ast_rtp *rtp) { int res; @@ -272,18 +332,22 @@ struct ast_frame *ast_rtp_read(struct ast_rtp *rtp) unsigned int seqno; int payloadtype; int hdrlen = 12; + int mark; unsigned int timestamp; unsigned int *rtpheader; + unsigned char cache[12]; static struct ast_frame *f, null_frame = { AST_FRAME_NULL, }; struct rtpPayloadType rtpPT; len = sizeof(sin); - res = recvfrom(rtp->s, rtp->rawdata + AST_FRIENDLY_OFFSET, sizeof(rtp->rawdata) - AST_FRIENDLY_OFFSET, + /* Cache where the header will go */ + memcpy(cache, rtp->rawdata + AST_FRIENDLY_OFFSET + rtp->readsofar, 12); + res = recvfrom(rtp->s, rtp->rawdata + AST_FRIENDLY_OFFSET + rtp->readsofar, sizeof(rtp->rawdata) - AST_FRIENDLY_OFFSET - rtp->readsofar, 0, (struct sockaddr *)&sin, &len); - rtpheader = (unsigned int *)(rtp->rawdata + AST_FRIENDLY_OFFSET); + rtpheader = (unsigned int *)(rtp->rawdata + rtp->readsofar + AST_FRIENDLY_OFFSET); if (res < 0) { ast_log(LOG_WARNING, "RTP Read error: %s\n", strerror(errno)); if (errno == EBADF) @@ -305,14 +369,22 @@ struct ast_frame *ast_rtp_read(struct ast_rtp *rtp) /* Get fields */ seqno = ntohl(rtpheader[0]); payloadtype = (seqno & 0x7f0000) >> 16; + mark = seqno & (1 << 23); seqno &= 0xffff; timestamp = ntohl(rtpheader[1]); + + /* Restore original data if important */ + if (rtp->readsofar) + memcpy(rtp->rawdata + AST_FRIENDLY_OFFSET + rtp->readsofar, cache, 12); + rtpheader = NULL; + + rtp->readsofar += (res - hdrlen); #if 0 printf("Got RTP packet from %s:%d (type %d, seq %d, ts %d, len = %d)\n", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), payloadtype, seqno, timestamp,res - hdrlen); #endif - rtp->f.frametype = AST_FRAME_VOICE; rtpPT = ast_rtp_lookup_pt(rtp, payloadtype); if (!rtpPT.isAstFormat) { + rtp->readsofar = 0; // This is special in-band data that's not one of our codecs if (rtpPT.code == AST_RTP_DTMF) { /* It's special -- rfc2833 process it */ @@ -332,6 +404,10 @@ struct ast_frame *ast_rtp_read(struct ast_rtp *rtp) } } rtp->f.subclass = rtpPT.code; + if (rtp->f.subclass < AST_FORMAT_MAX_AUDIO) + rtp->f.frametype = AST_FRAME_VOICE; + else + rtp->f.frametype = AST_FRAME_VIDEO; rtp->lastrxformat = rtp->f.subclass; if (!rtp->lastrxts) @@ -353,43 +429,56 @@ struct ast_frame *ast_rtp_read(struct ast_rtp *rtp) /* Send any pending DTMF */ if (rtp->resp && !rtp->dtmfcount) { + rtp->readsofar = 0; ast_log(LOG_DEBUG, "Sending pending DTMF\n"); return send_dtmf(rtp); } rtp->f.mallocd = 0; - rtp->f.datalen = res - hdrlen; + rtp->f.datalen = rtp->readsofar; rtp->f.data = rtp->rawdata + hdrlen + AST_FRIENDLY_OFFSET; rtp->f.offset = hdrlen + AST_FRIENDLY_OFFSET; - switch(rtp->f.subclass) { - case AST_FORMAT_ULAW: - case AST_FORMAT_ALAW: - rtp->f.samples = rtp->f.datalen; - break; - case AST_FORMAT_SLINEAR: - rtp->f.samples = rtp->f.datalen / 2; - break; - case AST_FORMAT_GSM: - rtp->f.samples = 160 * (rtp->f.datalen / 33); - break; - case AST_FORMAT_ILBC: - rtp->f.samples = 240 * (rtp->f.datalen / 50); - break; - case AST_FORMAT_ADPCM: - rtp->f.samples = rtp->f.datalen * 2; - break; - case AST_FORMAT_G729A: - rtp->f.samples = rtp->f.datalen * 8; - break; - case AST_FORMAT_G723_1: - rtp->f.samples = g723_samples(rtp->f.data, rtp->f.datalen); - break; - case AST_FORMAT_SPEEX: - rtp->f.samples = 160; - // assumes that the RTP packet contained one Speex frame - break; - default: - ast_log(LOG_NOTICE, "Unable to calculate samples for format %d\n", rtp->f.subclass); - break; + if (rtp->f.subclass < AST_FORMAT_MAX_AUDIO) { + rtp->readsofar = 0; + switch(rtp->f.subclass) { + case AST_FORMAT_ULAW: + case AST_FORMAT_ALAW: + rtp->f.samples = rtp->f.datalen; + break; + case AST_FORMAT_SLINEAR: + rtp->f.samples = rtp->f.datalen / 2; + break; + case AST_FORMAT_GSM: + rtp->f.samples = 160 * (rtp->f.datalen / 33); + break; + case AST_FORMAT_ILBC: + rtp->f.samples = 240 * (rtp->f.datalen / 50); + break; + case AST_FORMAT_ADPCM: + rtp->f.samples = rtp->f.datalen * 2; + break; + case AST_FORMAT_G729A: + rtp->f.samples = rtp->f.datalen * 8; + break; + case AST_FORMAT_G723_1: + rtp->f.samples = g723_samples(rtp->f.data, rtp->f.datalen); + break; + case AST_FORMAT_SPEEX: + rtp->f.samples = 160; + // assumes that the RTP packet contained one Speex frame + break; + default: + ast_log(LOG_NOTICE, "Unable to calculate samples for format %d\n", rtp->f.subclass); + break; + } + } else { + /* Video -- samples is # of samples vs. 90000 */ + rtp->f.samples = timestamp - rtp->lastividtimestamp; + rtp->lastividtimestamp = timestamp; + /* Return now if it's not the whole frame */ + if (!mark) { + return &null_frame; + } + rtp->readsofar = 0; } rtp->f.src = "RTP"; return &rtp->f; @@ -566,7 +655,27 @@ char* ast_rtp_lookup_mime_subtype(int isAstFormat, int code) { return ""; } -struct ast_rtp *ast_rtp_new(struct sched_context *sched, struct io_context *io) +static struct ast_rtcp *ast_rtcp_new(void) +{ + struct ast_rtcp *rtcp; + long flags; + rtcp = malloc(sizeof(struct ast_rtcp)); + if (!rtcp) + return NULL; + memset(rtcp, 0, sizeof(struct ast_rtcp)); + rtcp->s = socket(AF_INET, SOCK_DGRAM, 0); + rtcp->us.sin_family = AF_INET; + if (rtcp->s < 0) { + free(rtcp); + ast_log(LOG_WARNING, "Unable to allocate socket: %s\n", strerror(errno)); + return NULL; + } + flags = fcntl(rtcp->s, F_GETFL); + fcntl(rtcp->s, F_SETFL, flags | O_NONBLOCK); + return rtcp; +} + +struct ast_rtp *ast_rtp_new(struct sched_context *sched, struct io_context *io, int rtcpenable, int callbackmode) { struct ast_rtp *rtp; int x; @@ -586,6 +695,10 @@ struct ast_rtp *ast_rtp_new(struct sched_context *sched, struct io_context *io) ast_log(LOG_WARNING, "Unable to allocate socket: %s\n", strerror(errno)); return NULL; } + if (sched && rtcpenable) { + rtp->sched = sched; + rtp->rtcp = ast_rtcp_new(); + } flags = fcntl(rtp->s, F_GETFL); fcntl(rtp->s, F_SETFL, flags | O_NONBLOCK); /* Find us a place */ @@ -595,11 +708,18 @@ struct ast_rtp *ast_rtp_new(struct sched_context *sched, struct io_context *io) for (;;) { /* Must be an even port number by RTP spec */ rtp->us.sin_port = htons(x); - if (!bind(rtp->s, &rtp->us, sizeof(rtp->us))) + if (rtp->rtcp) + rtp->rtcp->us.sin_port = htons(x + 1); + if (!bind(rtp->s, &rtp->us, sizeof(rtp->us)) && + (!rtp->rtcp || !bind(rtp->rtcp->s, &rtp->rtcp->us, sizeof(rtp->rtcp->us)))) break; if (errno != EADDRINUSE) { ast_log(LOG_WARNING, "Unexpected bind error: %s\n", strerror(errno)); close(rtp->s); + if (rtp->rtcp) { + close(rtp->rtcp->s); + free(rtp->rtcp); + } free(rtp); return NULL; } @@ -609,11 +729,15 @@ struct ast_rtp *ast_rtp_new(struct sched_context *sched, struct io_context *io) if (x == startplace) { ast_log(LOG_WARNING, "No RTP ports remaining\n"); close(rtp->s); + if (rtp->rtcp) { + close(rtp->rtcp->s); + free(rtp->rtcp); + } free(rtp); return NULL; } } - if (io && sched) { + if (io && sched && callbackmode) { /* Operate this one in a callback mode */ rtp->sched = sched; rtp->io = io; @@ -635,6 +759,10 @@ void ast_rtp_set_peer(struct ast_rtp *rtp, struct sockaddr_in *them) { rtp->them.sin_port = them->sin_port; rtp->them.sin_addr = them->sin_addr; + if (rtp->rtcp) { + rtp->rtcp->them.sin_port = htons(ntohs(them->sin_port) + 1); + rtp->rtcp->them.sin_addr = them->sin_addr; + } } void ast_rtp_get_peer(struct ast_rtp *rtp, struct sockaddr_in *them) @@ -653,6 +781,10 @@ void ast_rtp_stop(struct ast_rtp *rtp) { memset(&rtp->them.sin_addr, 0, sizeof(rtp->them.sin_addr)); memset(&rtp->them.sin_port, 0, sizeof(rtp->them.sin_port)); + if (rtp->rtcp) { + memset(&rtp->rtcp->them.sin_addr, 0, sizeof(rtp->them.sin_addr)); + memset(&rtp->rtcp->them.sin_port, 0, sizeof(rtp->them.sin_port)); + } } void ast_rtp_destroy(struct ast_rtp *rtp) @@ -663,6 +795,10 @@ void ast_rtp_destroy(struct ast_rtp *rtp) ast_io_remove(rtp->io, rtp->ioid); if (rtp->s > -1) close(rtp->s); + if (rtp->rtcp) { + close(rtp->rtcp->s); + free(rtp->rtcp); + } free(rtp); } @@ -750,57 +886,97 @@ static int ast_rtp_raw_write(struct ast_rtp *rtp, struct ast_frame *f, int codec int res; int ms; int pred; + int mark = 0; + int pos, len; ms = calc_txstamp(rtp); /* Default prediction */ - pred = rtp->lastts + ms * 8; - - switch(f->subclass) { - case AST_FORMAT_ULAW: - case AST_FORMAT_ALAW: - /* If we're within +/- 20ms from when where we - predict we should be, use that */ - pred = rtp->lastts + f->datalen; - break; - case AST_FORMAT_G729A: - pred = rtp->lastts + f->datalen * 8; - break; - case AST_FORMAT_GSM: - pred = rtp->lastts + (f->datalen * 160 / 33); - break; - case AST_FORMAT_ILBC: - pred = rtp->lastts + (f->datalen * 240 / 50); - break; - case AST_FORMAT_G723_1: - pred = rtp->lastts + g723_samples(f->data, f->datalen); - break; - case AST_FORMAT_SPEEX: - pred = rtp->lastts + 160; - // assumes that the RTP packet contains one Speex frame - break; - default: - ast_log(LOG_WARNING, "Not sure about timestamp format for codec format %d\n", f->subclass); - } + if (f->subclass < AST_FORMAT_MAX_AUDIO) { + pred = rtp->lastts + ms * 8; + + switch(f->subclass) { + case AST_FORMAT_ULAW: + case AST_FORMAT_ALAW: + /* If we're within +/- 20ms from when where we + predict we should be, use that */ + pred = rtp->lastts + f->datalen; + break; + case AST_FORMAT_G729A: + pred = rtp->lastts + f->datalen * 8; + break; + case AST_FORMAT_GSM: + pred = rtp->lastts + (f->datalen * 160 / 33); + break; + case AST_FORMAT_ILBC: + pred = rtp->lastts + (f->datalen * 240 / 50); + break; + case AST_FORMAT_G723_1: + pred = rtp->lastts + g723_samples(f->data, f->datalen); + break; + case AST_FORMAT_SPEEX: + pred = rtp->lastts + 160; + // assumes that the RTP packet contains one Speex frame + break; + default: + ast_log(LOG_WARNING, "Not sure about timestamp format for codec format %d\n", f->subclass); + } - /* Re-calculate last TS */ - rtp->lastts = rtp->lastts + ms * 8; - /* If it's close to ou prediction, go for it */ - if (abs(rtp->lastts - pred) < 640) - rtp->lastts = pred; - else - ast_log(LOG_DEBUG, "Difference is %d, ms is %d\n", abs(rtp->lastts - pred), ms); - /* Get a pointer to the header */ - rtpheader = (unsigned int *)(f->data - hdrlen); - rtpheader[0] = htonl((2 << 30) | (codec << 16) | (rtp->seqno++)); - rtpheader[1] = htonl(rtp->lastts); - rtpheader[2] = htonl(rtp->ssrc); - if (rtp->them.sin_port && rtp->them.sin_addr.s_addr) { - res = sendto(rtp->s, (void *)rtpheader, f->datalen + hdrlen, 0, &rtp->them, sizeof(rtp->them)); - if (res <0) - ast_log(LOG_NOTICE, "RTP Transmission error to %s:%d: %s\n", inet_ntoa(rtp->them.sin_addr), ntohs(rtp->them.sin_port), strerror(errno)); + /* Re-calculate last TS */ + rtp->lastts = rtp->lastts + ms * 8; + /* If it's close to our prediction, go for it */ + if (abs(rtp->lastts - pred) < 640) + rtp->lastts = pred; + else + ast_log(LOG_DEBUG, "Difference is %d, ms is %d\n", abs(rtp->lastts - pred), ms); + /* Get a pointer to the header */ + rtpheader = (unsigned int *)(f->data - hdrlen); + rtpheader[0] = htonl((2 << 30) | (codec << 16) | (rtp->seqno++) | (mark << 23)); + rtpheader[1] = htonl(rtp->lastts); + rtpheader[2] = htonl(rtp->ssrc); + if (rtp->them.sin_port && rtp->them.sin_addr.s_addr) { + res = sendto(rtp->s, (void *)rtpheader, f->datalen + hdrlen, 0, &rtp->them, sizeof(rtp->them)); + if (res <0) + ast_log(LOG_NOTICE, "RTP Transmission error to %s:%d: %s\n", inet_ntoa(rtp->them.sin_addr), ntohs(rtp->them.sin_port), strerror(errno)); #if 0 - printf("Sent %d bytes of RTP data to %s:%d\n", res, inet_ntoa(rtp->them.sin_addr), ntohs(rtp->them.sin_port)); + printf("Sent %d bytes of RTP data to %s:%d\n", res, inet_ntoa(rtp->them.sin_addr), ntohs(rtp->them.sin_port)); #endif + } + } else { + pred = rtp->lastovidtimestamp + f->samples; + /* Re-calculate last TS */ + rtp->lastts = rtp->lastts + ms * 90; + /* If it's close to our prediction, go for it */ + if (abs(rtp->lastts - pred) < 7200) { + rtp->lastts = pred; + rtp->lastovidtimestamp += f->samples; + } else { + ast_log(LOG_DEBUG, "Difference is %d, ms is %d\n", abs(rtp->lastts - pred), ms); + rtp->lastovidtimestamp = rtp->lastts; + } + pos = 0; + while(pos < f->datalen) { + /* Split packets up if necessary */ + mark = 0; + len = f->datalen; + if (len > RTP_MTU) + len = RTP_MTU; + if ((pos + len) >= f->datalen) + mark = 1; + /* Get a pointer to the header */ + rtpheader = (unsigned int *)(f->data + pos - hdrlen); + rtpheader[0] = htonl((2 << 30) | (codec << 16) | (rtp->seqno++) | (mark << 23)); + rtpheader[1] = htonl(rtp->lastts); + rtpheader[2] = htonl(rtp->ssrc); + if (rtp->them.sin_port && rtp->them.sin_addr.s_addr) { + res = sendto(rtp->s, (void *)rtpheader, len + hdrlen, 0, &rtp->them, sizeof(rtp->them)); + if (res <0) + ast_log(LOG_NOTICE, "RTP Transmission error to %s:%d: %s\n", inet_ntoa(rtp->them.sin_addr), ntohs(rtp->them.sin_port), strerror(errno)); +#if 0 + printf("Sent %d bytes of RTP data to %s:%d\n", res, inet_ntoa(rtp->them.sin_addr), ntohs(rtp->them.sin_port)); +#endif + } + pos += len; + } } return 0; } @@ -821,7 +997,7 @@ int ast_rtp_write(struct ast_rtp *rtp, struct ast_frame *_f) return 0; /* Make sure we have enough space for RTP header */ - if (_f->frametype != AST_FRAME_VOICE) { + if ((_f->frametype != AST_FRAME_VOICE) && (_f->frametype != AST_FRAME_VIDEO)) { ast_log(LOG_WARNING, "RTP can only send voice\n"); return -1; } @@ -898,6 +1074,8 @@ int ast_rtp_write(struct ast_rtp *rtp, struct ast_frame *_f) default: ast_log(LOG_WARNING, "Not sure about sending format %d packets\n", _f->subclass); // fall through to... + case AST_FORMAT_H261: + case AST_FORMAT_H263: case AST_FORMAT_SPEEX: // Don't buffer outgoing frames; send them one-per-packet: if (_f->offset < hdrlen) { @@ -963,13 +1141,21 @@ int ast_rtp_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, st struct ast_frame *f; struct ast_channel *who, *cs[3]; struct ast_rtp *p0, *p1; + struct ast_rtp *vp0, *vp1; struct ast_rtp_protocol *pr0, *pr1; struct sockaddr_in ac0, ac1; + struct sockaddr_in vac0, vac1; struct sockaddr_in t0, t1; + struct sockaddr_in vt0, vt1; void *pvt0, *pvt1; int to; + memset(&vt0, 0, sizeof(vt0)); + memset(&vt1, 0, sizeof(vt1)); + memset(&vac0, 0, sizeof(vac0)); + memset(&vac1, 0, sizeof(vac1)); + /* XXX Wait a half a second for things to settle up this really should be fixed XXX */ ast_autoservice_start(c0); @@ -1000,24 +1186,36 @@ int ast_rtp_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, st pvt0 = c0->pvt->pvt; pvt1 = c1->pvt->pvt; p0 = pr0->get_rtp_info(c0); + if (pr0->get_vrtp_info) + vp0 = pr0->get_vrtp_info(c0); + else + vp0 = NULL; p1 = pr1->get_rtp_info(c1); + if (pr1->get_vrtp_info) + vp1 = pr1->get_vrtp_info(c1); + else + vp1 = NULL; if (!p0 || !p1) { /* Somebody doesn't want to play... */ ast_pthread_mutex_unlock(&c0->lock); ast_pthread_mutex_unlock(&c1->lock); return -2; } - if (pr0->set_rtp_peer(c0, p1)) + if (pr0->set_rtp_peer(c0, p1, vp1)) ast_log(LOG_WARNING, "Channel '%s' failed to talk to '%s'\n", c0->name, c1->name); else { /* Store RTP peer */ ast_rtp_get_peer(p1, &ac1); + if (vp1) + ast_rtp_get_peer(p1, &vac1); } - if (pr1->set_rtp_peer(c1, p0)) + if (pr1->set_rtp_peer(c1, p0, vp0)) ast_log(LOG_WARNING, "Channel '%s' failed to talk back to '%s'\n", c1->name, c0->name); else { /* Store RTP peer */ ast_rtp_get_peer(p0, &ac0); + if (vp0) + ast_rtp_get_peer(p0, &vac0); } ast_pthread_mutex_unlock(&c0->lock); ast_pthread_mutex_unlock(&c1->lock); @@ -1030,11 +1228,11 @@ int ast_rtp_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, st (c0->masq || c0->masqr || c1->masq || c1->masqr)) { ast_log(LOG_DEBUG, "Oooh, something is weird, backing out\n"); if (c0->pvt->pvt == pvt0) { - if (pr0->set_rtp_peer(c0, NULL)) + if (pr0->set_rtp_peer(c0, NULL, NULL)) ast_log(LOG_WARNING, "Channel '%s' failed to revert\n", c0->name); } if (c1->pvt->pvt == pvt1) { - if (pr1->set_rtp_peer(c1, NULL)) + if (pr1->set_rtp_peer(c1, NULL, NULL)) ast_log(LOG_WARNING, "Channel '%s' failed to revert back\n", c1->name); } /* Tell it to try again later */ @@ -1042,18 +1240,23 @@ int ast_rtp_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, st } to = -1; ast_rtp_get_peer(p1, &t1); - ast_rtp_get_peer(p0, &t0); - if (inaddrcmp(&t1, &ac1)) { + if (vp1) + ast_rtp_get_peer(vp1, &vt1); + if (vp0) + ast_rtp_get_peer(vp0, &vt0); + if (inaddrcmp(&t1, &ac1) || (vp1 && inaddrcmp(&vt1, &vac1))) { ast_log(LOG_DEBUG, "Oooh, '%s' changed end address\n", c1->name); - if (pr0->set_rtp_peer(c0, t1.sin_addr.s_addr ? p1 : NULL)) + if (pr0->set_rtp_peer(c0, t1.sin_addr.s_addr ? p1 : NULL, vt1.sin_addr.s_addr ? vp1 : NULL)) ast_log(LOG_WARNING, "Channel '%s' failed to update to '%s'\n", c0->name, c1->name); memcpy(&ac1, &t1, sizeof(ac1)); + memcpy(&vac1, &vt1, sizeof(vac1)); } - if (inaddrcmp(&t0, &ac0)) { + if (inaddrcmp(&t0, &ac0) || (vp0 && inaddrcmp(&vt0, &vac0))) { ast_log(LOG_DEBUG, "Oooh, '%s' changed end address\n", c0->name); - if (pr1->set_rtp_peer(c1, t0.sin_addr.s_addr ? p0 : NULL)) + if (pr1->set_rtp_peer(c1, t0.sin_addr.s_addr ? p0 : NULL, vt0.sin_addr.s_addr ? vp0 : NULL)) ast_log(LOG_WARNING, "Channel '%s' failed to update to '%s'\n", c1->name, c0->name); memcpy(&ac0, &t0, sizeof(ac0)); + memcpy(&vac0, &vt0, sizeof(vac0)); } who = ast_waitfor_n(cs, 2, &to); if (!who) { @@ -1068,11 +1271,11 @@ int ast_rtp_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, st *rc = who; ast_log(LOG_DEBUG, "Oooh, got a %s\n", f ? "digit" : "hangup"); if ((c0->pvt->pvt == pvt0) && (!c0->_softhangup)) { - if (pr0->set_rtp_peer(c0, NULL)) + if (pr0->set_rtp_peer(c0, NULL, NULL)) ast_log(LOG_WARNING, "Channel '%s' failed to revert\n", c0->name); } if ((c1->pvt->pvt == pvt1) && (!c1->_softhangup)) { - if (pr1->set_rtp_peer(c1, NULL)) + if (pr1->set_rtp_peer(c1, NULL, NULL)) ast_log(LOG_WARNING, "Channel '%s' failed to revert back\n", c1->name); } /* That's all we needed */