dect
/
asterisk
Archived
13
0
Fork 0

Add SIP/RTP video support, video enable app_echo, start on RTCP

git-svn-id: http://svn.digium.com/svn/asterisk/trunk@1128 f38db490-d61c-443f-a65b-d21fe96a405b
This commit is contained in:
markster 2003-06-28 16:40:02 +00:00
parent efde31a990
commit 63170c4333
11 changed files with 589 additions and 173 deletions

View File

@ -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;

View File

@ -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) {

View File

@ -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) {

View File

@ -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,
};

View File

@ -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.

View File

@ -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;

View File

@ -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);

View File

@ -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

View File

@ -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 */

View File

@ -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);

393
rtp.c
View File

@ -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 */