[svn-commits] trunk r32597 - in /trunk: ./ channels/
include/asterisk/
svn-commits at lists.digium.com
svn-commits at lists.digium.com
Tue Jun 6 09:09:34 MST 2006
Author: oej
Date: Tue Jun 6 11:09:33 2006
New Revision: 32597
URL: http://svn.digium.com/view/asterisk?rev=32597&view=rev
Log:
Merge of the "sdpcleanup" branch. Thanks to John Martin for a lot of tests
and some patches (all disclaimed).
- Don't change RTP properties if we reject a re-INVITE
- Don't add video to an outbound channel if there's no video on the inbound channel
- Don't include video in the "preferred codec" list for codec selection
- Clean up and document code that parses and adds SDP attachments
Since we do not transcode video, we can't handle video the same way as audio. This is a
bug fix patch. In future releases, we need to work on a solution for video negotiation,
not codecs but formats and framerates instead.
Modified:
trunk/channel.c
trunk/channels/chan_sip.c
trunk/frame.c
trunk/include/asterisk/rtp.h
trunk/include/asterisk/translate.h
trunk/rtp.c
Modified: trunk/channel.c
URL: http://svn.digium.com/view/asterisk/trunk/channel.c?rev=32597&r1=32596&r2=32597&view=diff
==============================================================================
--- trunk/channel.c (original)
+++ trunk/channel.c Tue Jun 6 11:09:33 2006
@@ -539,7 +539,7 @@
}
}
-/*! \brief Pick the best codec */
+/*! \brief Pick the best audio codec */
int ast_best_codec(int fmts)
{
/* This just our opinion, expressed in code. We are asked to choose
@@ -572,7 +572,9 @@
/*! Down to G.723.1 which is proprietary but at least designed for voice */
AST_FORMAT_G723_1,
};
-
+
+ /* Strip out video */
+ fmts &= AST_FORMAT_AUDIO_MASK;
/* Find the first preferred codec in the format given */
for (x=0; x < (sizeof(prefs) / sizeof(prefs[0]) ); x++)
@@ -2614,6 +2616,7 @@
int fmt;
int res;
int foo;
+ int videoformat = format & AST_FORMAT_VIDEO_MASK;
if (!cause)
cause = &foo;
@@ -2629,7 +2632,7 @@
continue;
capabilities = chan->tech->capabilities;
- fmt = format;
+ fmt = format & AST_FORMAT_AUDIO_MASK;
res = ast_translator_best_choice(&fmt, &capabilities);
if (res < 0) {
ast_log(LOG_WARNING, "No translator path exists for channel type %s (native %d) to %d\n", type, chan->tech->capabilities, format);
@@ -2640,7 +2643,7 @@
if (!chan->tech->requester)
return NULL;
- if (!(c = chan->tech->requester(type, capabilities, data, cause)))
+ if (!(c = chan->tech->requester(type, capabilities | videoformat, data, cause)))
return NULL;
if (c->_state == AST_STATE_DOWN) {
Modified: trunk/channels/chan_sip.c
URL: http://svn.digium.com/view/asterisk/trunk/channels/chan_sip.c?rev=32597&r1=32596&r2=32597&view=diff
==============================================================================
--- trunk/channels/chan_sip.c (original)
+++ trunk/channels/chan_sip.c Tue Jun 6 11:09:33 2006
@@ -1260,6 +1260,7 @@
static int sip_get_codec(struct ast_channel *chan);
static struct ast_frame *sip_rtp_read(struct ast_channel *ast, struct sip_pvt *p);
+
/*! \brief Definition of this channel for PBX channel registration */
static const struct ast_channel_tech sip_tech = {
.type = "SIP",
@@ -3252,13 +3253,17 @@
/*! \brief Initiate a call in the SIP channel
- called from sip_request_call (calls from the pbx ) */
+ called from sip_request_call (calls from the pbx ) for outbound channels
+ and from handle_request_invite for inbound channels
+
+*/
static struct ast_channel *sip_new(struct sip_pvt *i, int state, const char *title)
{
struct ast_channel *tmp;
struct ast_variable *v = NULL;
int fmt;
int what;
+ int needvideo = 0;
ast_mutex_unlock(&i->lock);
/* Don't hold a sip pvt lock while we allocate a channel */
@@ -3269,16 +3274,49 @@
return NULL;
}
tmp->tech = &sip_tech;
+
/* Select our native format based on codec preference until we receive
something from another device to the contrary. */
- if (i->jointcapability)
+ if (i->jointcapability) /* The joint capabilities of us and peer */
what = i->jointcapability;
- else if (i->capability)
+ else if (i->capability) /* Our configured capability for this peer */
what = i->capability;
else
- what = global_capability;
+ what = global_capability; /* Global codec support */
+
+ /* Set the native formats for audio and merge in video */
tmp->nativeformats = ast_codec_choose(&i->prefs, what, 1) | (i->jointcapability & AST_FORMAT_VIDEO_MASK);
+ if (option_debug > 2) {
+ char buf[BUFSIZ];
+ ast_log(LOG_DEBUG, "*** Our native formats are %s \n", ast_getformatname_multiple(buf, BUFSIZ, tmp->nativeformats));
+ ast_log(LOG_DEBUG, "*** Joint capabilities are %s \n", ast_getformatname_multiple(buf, BUFSIZ, i->jointcapability));
+ ast_log(LOG_DEBUG, "*** Our capabilities are %s \n", ast_getformatname_multiple(buf, BUFSIZ, i->capability));
+ ast_log(LOG_DEBUG, "*** AST_CODEC_CHOOSE formats are %s \n", ast_getformatname_multiple(buf, BUFSIZ, ast_codec_choose(&i->prefs, what, 1)));
+ if (i->prefcodec)
+ ast_log(LOG_DEBUG, "*** Our preferred formats from the incoming channel are %s \n", ast_getformatname_multiple(buf, BUFSIZ, i->prefcodec));
+ }
+
+ /* XXX Why are we choosing a codec from the native formats?? */
fmt = ast_best_codec(tmp->nativeformats);
+
+ /* If we have a prefcodec setting, we have an inbound channel that set a
+ preferred format for this call. Otherwise, we check the jointcapability
+ We also check for vrtp. If it's not there, we are not allowed do any video anyway.
+ */
+ if (i->vrtp) {
+ if (i->prefcodec)
+ needvideo = i->prefcodec & AST_FORMAT_VIDEO_MASK; /* Outbound call */
+ else
+ needvideo = i->jointcapability & AST_FORMAT_VIDEO_MASK; /* Inbound call */
+ }
+
+ if (option_debug > 2) {
+ if (needvideo)
+ ast_log(LOG_DEBUG, "This channel can handle video! HOLLYWOOD next!\n");
+ else
+ ast_log(LOG_DEBUG, "This channel will not be able to handle video.\n");
+ }
+
if (title)
ast_string_field_build(tmp, name, "SIP/%s-%04lx", title, ast_random() & 0xffff);
@@ -3297,7 +3335,7 @@
tmp->fds[0] = ast_rtp_fd(i->rtp);
tmp->fds[1] = ast_rtcp_fd(i->rtp);
}
- if (i->vrtp) {
+ if (needvideo && i->vrtp) {
tmp->fds[2] = ast_rtp_fd(i->vrtp);
tmp->fds[3] = ast_rtcp_fd(i->vrtp);
}
@@ -4038,7 +4076,9 @@
return 0;
}
-/*! \brief Process SIP SDP, select formats and activate RTP channels */
+/*! \brief Process SIP SDP offer, select formats and activate RTP channels
+ If offer is rejected, we will not change any properties of the call
+*/
static int process_sdp(struct sip_pvt *p, struct sip_request *req)
{
const char *m; /* SDP media offer */
@@ -4047,33 +4087,49 @@
char host[258];
char iabuf[INET_ADDRSTRLEN];
int len = -1;
- int portno = -1; /* Audio port */
- int vportno = -1; /* Video port */
+ int portno = -1; /*!< RTP Audio port number */
+ int vportno = -1; /*!< RTP Video port number */
/* Peer capability is the capability in the SDP, non codec is RFC2833 DTMF (101) */
int peercapability, peernoncodeccapability;
-
- int vpeercapability=0, vpeernoncodeccapability=0; /* Peer's video capabilities */
- struct sockaddr_in sin;
+ int vpeercapability = 0, vpeernoncodeccapability = 0;
+ struct sockaddr_in sin; /*!< media socket address */
+ struct sockaddr_in vsin; /*!< Video socket address */
+
const char *codecs;
- struct hostent *hp;
- struct ast_hostent ahp;
+ struct hostent *hp; /*!< RTP Audio host IP */
+ struct hostent *vhp = NULL; /*!< RTP video host IP */
+ struct ast_hostent audiohp;
+ struct ast_hostent videohp;
int codec;
int destiterator = 0;
int iterator;
int sendonly = 0;
- int x, y;
+ int numberofports;
+ struct ast_channel *bridgepeer = NULL;
+ struct ast_rtp newaudiortp, newvideortp; /* Buffers for codec handling */
+ int newjointcapability; /* Negotiated capability */
+ int newpeercapability;
+ int newnoncodeccapability;
+ int numberofmediastreams = 0;
int debug = sip_debug_test_pvt(p);
- struct ast_channel *bridgepeer = NULL;
-
+
if (!p->rtp) {
ast_log(LOG_ERROR, "Got SDP but have no RTP session allocated.\n");
return -1;
}
+ /* Initialize the temporary RTP structures we use to evaluate the offer from the peer */
+ memset(&newaudiortp, 0, sizeof(newaudiortp));
+ memset(&newvideortp, 0, sizeof(newvideortp));
+ ast_rtp_pt_default(&newaudiortp);
+ ast_rtp_pt_default(&newvideortp);
+
/* Update our last rtprx when we receive an SDP, too */
p->lastrtprx = p->lastrtptx = time(NULL); /* XXX why both ? */
+
+ /* Try to find first media stream */
m = get_sdp(req, "m");
destiterator = req->sdp_start;
c = get_sdp_iterate(&destiterator, req, "c");
@@ -4081,26 +4137,41 @@
ast_log(LOG_WARNING, "Insufficient information for SDP (m = '%s', c = '%s')\n", m, c);
return -1;
}
+
+ /* Check for IPv4 address (not IPv6 yet) */
if (sscanf(c, "IN IP4 %256s", host) != 1) {
ast_log(LOG_WARNING, "Invalid host in c= line, '%s'\n", c);
return -1;
}
+
/* XXX This could block for a long time, and block the main thread! XXX */
- hp = ast_gethostbyname(host, &ahp);
+ hp = ast_gethostbyname(host, &audiohp);
if (!hp) {
ast_log(LOG_WARNING, "Unable to lookup host in c= line, '%s'\n", c);
return -1;
}
+ vhp = hp; /* Copy to video address as default too */
+
iterator = req->sdp_start;
ast_set_flag(&p->flags[0], SIP_NOVIDEO);
+
+
+ /* Find media streams in this SDP offer */
while ((m = get_sdp_iterate(&iterator, req, "m"))[0] != '\0') {
- int found = 0;
- if ((sscanf(m, "audio %d/%d RTP/AVP %n", &x, &y, &len) == 2) ||
+ int x;
+ int audio = FALSE;
+ numberofmediastreams++;
+
+ if (p->vrtp)
+ ast_rtp_pt_clear(&newvideortp); /* Must be cleared in case no m=video line exists */
+ numberofports = 1;
+ if ((sscanf(m, "audio %d/%d RTP/AVP %n", &x, &numberofports, &len) == 2) ||
(sscanf(m, "audio %d RTP/AVP %n", &x, &len) == 1)) {
- found = 1;
+ audio = TRUE;
+ /* Found audio stream in this media definition */
portno = x;
/* Scan through the RTP payload types specified in a "m=" line: */
- ast_rtp_pt_clear(p->rtp);
+ ast_rtp_pt_clear(&newaudiortp);
for (codecs = m + len; !ast_strlen_zero(codecs); codecs = ast_skip_blanks(codecs + len)) {
if (sscanf(codecs, "%d%n", &codec, &len) != 1) {
ast_log(LOG_WARNING, "Error in codec string '%s'\n", codecs);
@@ -4108,14 +4179,11 @@
}
if (debug)
ast_verbose("Found RTP audio format %d\n", codec);
- ast_rtp_set_m_type(p->rtp, codec);
+ ast_rtp_set_m_type(&newaudiortp, codec);
}
- }
- if (p->vrtp)
- ast_rtp_pt_clear(p->vrtp); /* Must be cleared in case no m=video line exists */
-
- if (p->vrtp && (sscanf(m, "video %d RTP/AVP %n", &x, &len) == 1)) {
- found = 1;
+ } else if ((sscanf(m, "video %d/%d RTP/AVP %n", &x, &numberofports, &len) == 2) ||
+ (sscanf(m, "video %d RTP/AVP %n", &x, &len) == 1)) {
+ /* If it is not audio - is it video ? */
ast_clear_flag(&p->flags[0], SIP_NOVIDEO);
vportno = x;
/* Scan through the RTP payload types specified in a "m=" line: */
@@ -4126,144 +4194,210 @@
}
if (debug)
ast_verbose("Found RTP video format %d\n", codec);
- ast_rtp_set_m_type(p->vrtp, codec);
+ ast_rtp_set_m_type(&newvideortp, codec);
}
- }
- if (!found )
- ast_log(LOG_WARNING, "Unknown SDP media type in offer: %s\n", m);
- }
- if (portno == -1 && vportno == -1) {
- /* No acceptable offer found in SDP */
- return -2;
- }
- /* Check for Media-description-level-address for audio */
- if (pedanticsipchecking) {
+ } else
+ ast_log(LOG_WARNING, "Unsupported SDP media type in offer: %s\n", m);
+ if (numberofports > 1)
+ ast_log(LOG_WARNING, "SDP offered %d ports for media, not supported by Asterisk. Will try anyway...\n", numberofports);
+
+
+ /* Check for Media-description-level-address for audio */
c = get_sdp_iterate(&destiterator, req, "c");
if (!ast_strlen_zero(c)) {
if (sscanf(c, "IN IP4 %256s", host) != 1) {
ast_log(LOG_WARNING, "Invalid secondary host in c= line, '%s'\n", c);
} else {
/* XXX This could block for a long time, and block the main thread! XXX */
- hp = ast_gethostbyname(host, &ahp);
- if (!hp) {
- ast_log(LOG_WARNING, "Unable to lookup host in secondary c= line, '%s'\n", c);
- }
+ if (audio) {
+ if ( !(hp = ast_gethostbyname(host, &audiohp)))
+ ast_log(LOG_WARNING, "Unable to lookup RTP Audio host in secondary c= line, '%s'\n", c);
+ } else if (!(vhp = ast_gethostbyname(host, &videohp)))
+ ast_log(LOG_WARNING, "Unable to lookup RTP video host in secondary c= line, '%s'\n", c);
}
- }
- }
+
+ }
+ }
+ if (portno == -1 && vportno == -1)
+ /* No acceptable offer found in SDP - we have no ports */
+ /* Do not change RTP or VRTP if this is a re-invite */
+ return -2;
+
+ if (numberofmediastreams > 2)
+ /* We have too many media streams, fail this offer */
+ return -3;
+
/* RTP addresses and ports for audio and video */
sin.sin_family = AF_INET;
+ vsin.sin_family = AF_INET;
memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr));
+ if (vhp)
+ memcpy(&vsin.sin_addr, vhp->h_addr, sizeof(vsin.sin_addr));
+
/* Setup audio port number */
sin.sin_port = htons(portno);
- if (p->rtp && sin.sin_port) {
- ast_rtp_set_peer(p->rtp, &sin);
- if (debug) {
- ast_verbose("Peer audio RTP is at port %s:%d\n", ast_inet_ntoa(iabuf,sizeof(iabuf), sin.sin_addr), ntohs(sin.sin_port));
- }
- }
- /* Check for Media-description-level-address for video */
- if (pedanticsipchecking) {
- c = get_sdp_iterate(&destiterator, req, "c");
- if (!ast_strlen_zero(c)) {
- if (sscanf(c, "IN IP4 %256s", host) != 1) {
- ast_log(LOG_WARNING, "Invalid secondary host in c= line, '%s'\n", c);
- } else {
- /* XXX This could block for a long time, and block the main thread! XXX */
- hp = ast_gethostbyname(host, &ahp);
- if (!hp) {
- ast_log(LOG_WARNING, "Unable to lookup host in secondary c= line, '%s'\n", c);
- }
- }
- }
- }
/* Setup video port number */
- sin.sin_port = htons(vportno);
- if (p->vrtp && sin.sin_port) {
- ast_rtp_set_peer(p->vrtp, &sin);
- if (debug) {
- ast_verbose("Peer video RTP is at port %s:%d\n", ast_inet_ntoa(iabuf,sizeof(iabuf), sin.sin_addr), ntohs(sin.sin_port));
- }
- }
+ if (vportno != -1)
+ vsin.sin_port = htons(vportno);
/* Next, scan through each "a=rtpmap:" line, noting each
* specified RTP payload type (with corresponding MIME subtype):
*/
+ /* XXX This needs to be done per media stream, since it's media stream specific */
iterator = req->sdp_start;
while ((a = get_sdp_iterate(&iterator, req, "a"))[0] != '\0') {
char* mimeSubtype = ast_strdupa(a); /* ensures we have enough space */
if (!strcasecmp(a, "sendonly")) {
- sendonly=1;
+ sendonly = 1;
continue;
- }
- if (!strcasecmp(a, "sendrecv")) {
- sendonly=0;
- }
- if (sscanf(a, "rtpmap: %u %[^/]/", &codec, mimeSubtype) != 2) continue;
+ } else if (!strcasecmp(a, "sendrecv")) {
+ sendonly = 0;
+ continue;
+ } else if (option_debug) {
+ /* If we're debugging, check for unsupported sdp options */
+ if (!strcasecmp(a, "inactive")) {
+ /* Inactive media streams: Not supported */
+ if (debug)
+ ast_verbose("Got unsupported a:inactive in SDP offer \n");
+ continue;
+ } else if (!strncasecmp(a, "rtcp:", (size_t) 5)) {
+ if (debug)
+ ast_verbose("Got unsupported a:rtcp in SDP offer \n");
+ continue;
+ } else if (!strncasecmp(a, "fmtp:", (size_t) 5)) {
+ /* Format parameters: Not supported */
+ /* Note: This is used for codec parameters, like bitrate for
+ G722 and video formats for H263 and H264
+ See RFC2327 for an example */
+ if (debug)
+ ast_verbose("Got unsupported a:fmtp in SDP offer \n");
+ continue;
+ } else if (!strncasecmp(a, "framerate:", (size_t) 10)) {
+ /* Video stuff: Not supported */
+ if (debug)
+ ast_verbose("Got unsupported a:framerate in SDP offer \n");
+ continue;
+ } else if (!strncasecmp(a, "maxprate:", (size_t) 9)) {
+ /* Video stuff: Not supported */
+ if (debug)
+ ast_verbose("Got unsupported a:maxprate in SDP offer \n");
+ continue;
+ } else if (!strncasecmp(a, "crypto:", (size_t) 7)) {
+ /* SRTP stuff, not yet supported */
+ if (debug)
+ ast_verbose("Got unsupported a:crypto in SDP offer \n");
+ continue;
+ } else if (!strncasecmp(a, "ptime:", (size_t) 6)) {
+ if (debug)
+ ast_verbose("Got unsupported a:ptime in SDP offer \n");
+ continue;
+ }
+ } else if (sscanf(a, "rtpmap: %u %[^/]/", &codec, mimeSubtype) != 2)
+ continue;
+ /* We have a rtpmap to handle */
if (debug)
- ast_verbose("Found description format %s\n", mimeSubtype);
+ ast_verbose("Found description format %s for ID %d\n", mimeSubtype, codec);
+
/* Note: should really look at the 'freq' and '#chans' params too */
- ast_rtp_set_rtpmap_type(p->rtp, codec, "audio", mimeSubtype);
+ ast_rtp_set_rtpmap_type(&newaudiortp, codec, "audio", 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);
- if (p->vrtp)
- ast_rtp_get_current_formats(p->vrtp,
- &vpeercapability, &vpeernoncodeccapability);
- p->jointcapability = p->capability & (peercapability | vpeercapability);
- p->peercapability = (peercapability | vpeercapability);
- p->noncodeccapability = noncodeccapability & peernoncodeccapability;
-
+ ast_rtp_set_rtpmap_type(&newvideortp, codec, "video", mimeSubtype);
+ }
+
+ /* Now gather all of the codecs that we are asked for: */
+ ast_rtp_get_current_formats(&newaudiortp, &peercapability, &peernoncodeccapability);
+ ast_rtp_get_current_formats(&newvideortp, &vpeercapability, &vpeernoncodeccapability);
+
+ newjointcapability = p->capability & (peercapability | vpeercapability);
+ newpeercapability = (peercapability | vpeercapability);
+ newnoncodeccapability = noncodeccapability & peernoncodeccapability;
+
+
+ if (debug) {
+ /* shame on whoever coded this.... */
+ char s1[BUFSIZ], s2[BUFSIZ], s3[BUFSIZ], s4[BUFSIZ];
+
+ ast_verbose("Capabilities: us - %s, peer - audio=%s/video=%s, combined - %s\n",
+ ast_getformatname_multiple(s1, BUFSIZ, p->capability),
+ ast_getformatname_multiple(s2, BUFSIZ, newpeercapability),
+ ast_getformatname_multiple(s3, BUFSIZ, vpeercapability),
+ ast_getformatname_multiple(s4, BUFSIZ, newjointcapability));
+
+ ast_verbose("Non-codec capabilities (dtmf): us - %s, peer - %s, combined - %s\n",
+ ast_rtp_lookup_mime_multiple(s1, BUFSIZ, noncodeccapability, 0),
+ ast_rtp_lookup_mime_multiple(s2, BUFSIZ, peernoncodeccapability, 0),
+ ast_rtp_lookup_mime_multiple(s3, BUFSIZ, newnoncodeccapability, 0));
+ }
+ if (!newjointcapability) {
+ ast_log(LOG_NOTICE, "No compatible codecs, not accepting this offer!\n");
+ /* Do NOT Change current setting */
+ return -1;
+ }
+
+ /* We are now ready to change the sip session and p->rtp and p->vrtp with the offered codecs, since
+ they are acceptable */
+ p->jointcapability = newjointcapability; /* Our joint codec profile for this call */
+ p->peercapability = newpeercapability; /* The other sides capability in latest offer */
+ p->noncodeccapability = newnoncodeccapability; /* DTMF capabilities */
+
+ {
+ int i;
+ /* Copy payload types from source to destination */
+ for (i=0; i < MAX_RTP_PT; ++i) {
+ p->rtp->current_RTP_PT[i]= newaudiortp.current_RTP_PT[i];
+ if (p->vrtp)
+ p->vrtp->current_RTP_PT[i]= newvideortp.current_RTP_PT[i];
+ }
+ }
+
if (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO) {
ast_clear_flag(&p->flags[0], SIP_DTMF);
- if (p->noncodeccapability & AST_RTP_DTMF) {
+ if (newnoncodeccapability & AST_RTP_DTMF) {
/* XXX Would it be reasonable to drop the DSP at this point? XXX */
ast_set_flag(&p->flags[0], SIP_DTMF_RFC2833);
} else {
ast_set_flag(&p->flags[0], SIP_DTMF_INBAND);
}
}
-
- if (debug) {
- /* shame on whoever coded this.... */
- const unsigned slen=512;
- char s1[slen], s2[slen], s3[slen], s4[slen];
-
- ast_verbose("Capabilities: us - %s, peer - audio=%s/video=%s, combined - %s\n",
- ast_getformatname_multiple(s1, slen, p->capability),
- ast_getformatname_multiple(s2, slen, peercapability),
- ast_getformatname_multiple(s3, slen, vpeercapability),
- ast_getformatname_multiple(s4, slen, p->jointcapability));
-
- ast_verbose("Non-codec capabilities: us - %s, peer - %s, combined - %s\n",
- ast_rtp_lookup_mime_multiple(s1, slen, noncodeccapability, 0),
- ast_rtp_lookup_mime_multiple(s2, slen, peernoncodeccapability, 0),
- ast_rtp_lookup_mime_multiple(s3, slen, p->noncodeccapability, 0));
- }
- if (!p->jointcapability) {
- ast_log(LOG_NOTICE, "No compatible codecs!\n");
- return -1;
- }
-
- if (!p->owner) /* There's no open channel owning us */
+
+ /* Setup audio port number */
+ if (p->rtp && sin.sin_port) {
+ ast_rtp_set_peer(p->rtp, &sin);
+ if (debug)
+ ast_verbose("Peer audio RTP is at port %s:%d\n", ast_inet_ntoa(iabuf,sizeof(iabuf), sin.sin_addr), ntohs(sin.sin_port));
+ }
+
+ /* Setup video port number */
+ if (p->vrtp && vsin.sin_port) {
+ ast_rtp_set_peer(p->vrtp, &vsin);
+ if (debug)
+ ast_verbose("Peer video RTP is at port %s:%d\n", ast_inet_ntoa(iabuf,sizeof(iabuf), vsin.sin_addr), ntohs(vsin.sin_port));
+ }
+
+ /* Ok, we're going with this offer */
+ if (option_debug > 1) {
+ char buf[BUFSIZ];
+ ast_log(LOG_DEBUG, "We're settling with these formats: %s\n", ast_getformatname_multiple(buf, BUFSIZ, p->jointcapability));
+ }
+
+ if (!p->owner) /* There's no open channel owning us so we can return here. For a re-invite or so, we proceed */
return 0;
+
if (!(p->owner->nativeformats & p->jointcapability & AST_FORMAT_AUDIO_MASK)) {
- const unsigned slen=512;
- char s1[slen], s2[slen];
- ast_log(LOG_DEBUG, "Oooh, we need to change our formats since our peer supports only %s and not %s\n",
- ast_getformatname_multiple(s1, slen, p->jointcapability),
- ast_getformatname_multiple(s2, slen, p->owner->nativeformats));
+ if (debug) {
+ char s1[BUFSIZ], s2[BUFSIZ];
+ ast_log(LOG_DEBUG, "Oooh, we need to change our audio formats since our peer supports only %s and not %s\n",
+ ast_getformatname_multiple(s1, BUFSIZ, p->jointcapability),
+ ast_getformatname_multiple(s2, BUFSIZ, p->owner->nativeformats));
+ }
p->owner->nativeformats = ast_codec_choose(&p->prefs, p->jointcapability, 1) | (p->capability & vpeercapability);
ast_set_read_format(p->owner, p->owner->readformat);
ast_set_write_format(p->owner, p->owner->writeformat);
}
- if ((bridgepeer=ast_bridged_channel(p->owner))) {
+
+ if ((bridgepeer = ast_bridged_channel(p->owner))) {
/* We have a bridge */
/* Turn on/off music on hold if we are holding/unholding */
if (sin.sin_addr.s_addr && !sendonly) {
@@ -4277,12 +4411,15 @@
ast_moh_start(bridgepeer, NULL);
if (sendonly)
ast_rtp_stop(p->rtp);
+ /* RTCP needs to go ahead, even if we're on hold!!! */
+
/* Activate a re-invite */
ast_queue_frame(p->owner, &ast_null_frame);
}
}
/* Manager Hold and Unhold events must be generated, if necessary */
+ /* XXX Support for sendonly/recvonly needs to be fixed !!! */
if (sin.sin_addr.s_addr && !sendonly) {
append_history(p, "Unhold", "%s", req->data);
@@ -4308,9 +4445,11 @@
}
ast_set_flag(&p->flags[0], SIP_CALL_ONHOLD);
}
+
return 0;
}
+
/*! \brief Add header to SIP message */
static int add_header(struct sip_request *req, const char *var, const char *value)
@@ -4885,6 +5024,7 @@
return 0;
}
+/*! \brief Add codec offer to SDP offer/answer body in INVITE or 200 OK */
static void add_codec_to_sdp(const struct sip_pvt *p, int codec, int sample_rate,
char **m_buf, size_t *m_size, char **a_buf, size_t *a_size,
int debug)
@@ -4905,6 +5045,7 @@
ast_build_string(a_buf, a_size, "a=fmtp:%d annexb=no\r\n", rtp_code);
}
+/*! \brief Add RFC 2833 DTMF offer to SDP */
static void add_noncodec_to_sdp(const struct sip_pvt *p, int format, int sample_rate,
char **m_buf, size_t *m_size, char **a_buf, size_t *a_size,
int debug)
@@ -4929,21 +5070,25 @@
static int add_sdp(struct sip_request *resp, struct sip_pvt *p)
{
int len = 0;
- int pref_codec;
int alreadysent = 0;
+
struct sockaddr_in sin;
struct sockaddr_in vsin;
- char v[256];
- char s[256];
- char o[256];
- char c[256];
- char t[256];
- char b[256];
+ struct sockaddr_in dest;
+ struct sockaddr_in vdest = { 0, };
+
+ /* SDP fields */
+ char *version = "v=0\r\n"; /* Protocol version */
+ char *subject = "s=session\r\n"; /* Subject of the session */
+ char owner[256]; /* Session owner/creator */
+ char connection[256]; /* Connection data */
+ char *stime = "t=0 0\r\n"; /* Time the session is active */
+ char bandwidth[256] = ""; /* Max bitrate */
char *hold;
- char m_audio[256];
- char m_video[256];
- char a_audio[1024];
- char a_video[1024];
+ char m_audio[256]; /* Media declaration line for audio */
+ char m_video[256]; /* Media declaration line for video */
+ char a_audio[1024]; /* Attributes for audio */
+ char a_video[1024]; /* Attributes for video */
char *m_audio_next = m_audio;
char *m_video_next = m_video;
size_t m_audio_left = sizeof(m_audio);
@@ -4952,31 +5097,33 @@
char *a_video_next = a_video;
size_t a_audio_left = sizeof(a_audio);
size_t a_video_left = sizeof(a_video);
+
char iabuf[INET_ADDRSTRLEN];
int x;
int capability;
- struct sockaddr_in dest;
- struct sockaddr_in vdest = { 0, };
- int debug;
-
- debug = sip_debug_test_pvt(p);
-
- len = 0;
+ int needvideo = FALSE;
+ int debug = sip_debug_test_pvt(p);
+
+ m_video[0] = '\0'; /* Reset the video media string if it's not needed */
+
if (!p->rtp) {
ast_log(LOG_WARNING, "No way to add SDP without an RTP structure\n");
return -1;
}
- capability = p->jointcapability;
-
+
+ /* Set RTP Session ID and version */
if (!p->sessionid) {
p->sessionid = getpid();
p->sessionversion = p->sessionid;
} else
p->sessionversion++;
+
+ /* Get our addresses */
ast_rtp_get_us(p->rtp, &sin);
if (p->vrtp)
ast_rtp_get_us(p->vrtp, &vsin);
+ /* Is this a re-invite to move the media out, then use the original offer from caller */
if (p->redirip.sin_addr.s_addr) {
dest.sin_port = p->redirip.sin_port;
dest.sin_addr = p->redirip.sin_addr;
@@ -4987,60 +5134,109 @@
dest.sin_port = sin.sin_port;
}
- /* Determine video destination */
- if (p->vrtp) {
+ /* Ok, let's start working with codec selection here */
+ capability = p->jointcapability;
+
+ if (option_debug > 1) {
+ char codecbuf[BUFSIZ];
+ ast_log(LOG_DEBUG, "** Our capability: %s Video flag: %s\n", ast_getformatname_multiple(codecbuf, sizeof(codecbuf), capability), ast_test_flag(&p->flags[0], SIP_NOVIDEO) ? "True" : "False");
+ ast_log(LOG_DEBUG, "** Our prefcodec: %s \n", ast_getformatname_multiple(codecbuf, sizeof(codecbuf), p->prefcodec));
+ }
+
+ /* Check if we need video in this call */
+ if((capability & AST_FORMAT_VIDEO_MASK) && !ast_test_flag(&p->flags[0], SIP_NOVIDEO)) {
+ if (p->vrtp) {
+ needvideo = TRUE;
+ if (option_debug > 1)
+ ast_log(LOG_DEBUG, "This call needs video offers! \n");
+ } else if (option_debug > 1)
+ ast_log(LOG_DEBUG, "This call needs video offers, but there's no video support enabled ! \n");
+ }
+
+
+ /* Ok, we need video. Let's add what we need for video and set codecs.
+ Video is handled differently than audio since we can not transcode. */
+ if (needvideo) {
+
+ /* Determine video destination */
if (p->vredirip.sin_addr.s_addr) {
+ vdest.sin_addr = p->vredirip.sin_addr;
vdest.sin_port = p->vredirip.sin_port;
- vdest.sin_addr = p->vredirip.sin_addr;
} else {
vdest.sin_addr = p->ourip;
vdest.sin_port = vsin.sin_port;
}
- }
- if (debug) {
- ast_verbose("We're at %s port %d\n", ast_inet_ntoa(iabuf, sizeof(iabuf), p->ourip), ntohs(sin.sin_port));
- if (p->vrtp)
+ ast_build_string(&m_video_next, &m_video_left, "m=video %d RTP/AVP", ntohs(vdest.sin_port));
+
+ /* Build max bitrate string */
+ if (p->maxcallbitrate)
+ snprintf(bandwidth, sizeof(bandwidth), "b=CT:%d\r\n", p->maxcallbitrate);
+ if (debug)
ast_verbose("Video is at %s port %d\n", ast_inet_ntoa(iabuf, sizeof(iabuf), p->ourip), ntohs(vsin.sin_port));
- }
+
+ /* For video, we can't negotiate video offers. Let's compare the incoming call with what we got. */
+ if (p->prefcodec) {
+ int videocapability = (capability & p->prefcodec) & AST_FORMAT_VIDEO_MASK; /* Outbound call */
+
+ /*! \todo XXX We need to select one codec, not many, since there's no transcoding */
+
+ /* Now, merge this video capability into capability while removing unsupported codecs */
+ if (!videocapability) {
+ needvideo = FALSE;
+ if (option_debug > 2)
+ ast_log(LOG_DEBUG, "** No compatible video codecs... Disabling video.\n");
+ }
+
+ /* Replace video capabilities with the new videocapability */
+ capability = (capability & AST_FORMAT_AUDIO_MASK) | videocapability;
+
+ if (option_debug > 4) {
+ char codecbuf[BUFSIZ];
+ if (videocapability)
+ ast_log(LOG_DEBUG, "** Our video codec selection is: %s \n", ast_getformatname_multiple(codecbuf, sizeof(codecbuf), videocapability));
+ ast_log(LOG_DEBUG, "** Capability now set to : %s \n", ast_getformatname_multiple(codecbuf, sizeof(codecbuf), capability));
+ }
+ }
+ }
+ if (debug)
+ ast_verbose("Audio is at %s port %d\n", ast_inet_ntoa(iabuf, sizeof(iabuf), p->ourip), ntohs(sin.sin_port));
+
+ /* Start building generic SDP headers */
/* We break with the "recommendation" and send our IP, in order that our
peer doesn't have to ast_gethostbyname() us */
- snprintf(v, sizeof(v), "v=0\r\n");
- snprintf(o, sizeof(o), "o=root %d %d IN IP4 %s\r\n", p->sessionid, p->sessionversion, ast_inet_ntoa(iabuf, sizeof(iabuf), dest.sin_addr));
- snprintf(s, sizeof(s), "s=session\r\n");
- snprintf(c, sizeof(c), "c=IN IP4 %s\r\n", ast_inet_ntoa(iabuf, sizeof(iabuf), dest.sin_addr));
- if ((p->vrtp) &&
- (!ast_test_flag(&p->flags[0], SIP_NOVIDEO)) &&
- (capability & VIDEO_CODEC_MASK)) /* only if video response is appropriate */
- snprintf(b, sizeof(b), "b=CT:%d\r\n", p->maxcallbitrate);
- snprintf(t, sizeof(t), "t=0 0\r\n");
-
+ snprintf(owner, sizeof(owner), "o=root %d %d IN IP4 %s\r\n", p->sessionid, p->sessionversion, ast_inet_ntoa(iabuf, sizeof(iabuf), dest.sin_addr));
+ snprintf(connection, sizeof(connection), "c=IN IP4 %s\r\n", ast_inet_ntoa(iabuf, sizeof(iabuf), dest.sin_addr));
ast_build_string(&m_audio_next, &m_audio_left, "m=audio %d RTP/AVP", ntohs(dest.sin_port));
- ast_build_string(&m_video_next, &m_video_left, "m=video %d RTP/AVP", ntohs(vdest.sin_port));
if (ast_test_flag(&p->flags[0], SIP_CALL_ONHOLD))
hold = "a=recvonly\r\n";
else
hold = "a=sendrecv\r\n";
- /* Prefer the codec we were requested to use, first, no matter what */
+ /* Now, start adding audio codecs. These are added in this order:
+ - First what was requested by the calling channel
+ - Then preferences in order from sip.conf device config for this peer/user
+ - Then other codecs in capabilities, including video
+ */
+
+ /* Prefer the audio codec we were requested to use, first, no matter what
+ Note that p->prefcodec can include video codecs, so mask them out
+ */
if (capability & p->prefcodec) {
- if (p->prefcodec <= AST_FORMAT_MAX_AUDIO)
- add_codec_to_sdp(p, p->prefcodec, 8000,
- &m_audio_next, &m_audio_left,
- &a_audio_next, &a_audio_left,
- debug);
- else
- add_codec_to_sdp(p, p->prefcodec, 90000,
- &m_video_next, &m_video_left,
- &a_video_next, &a_video_left,
- debug);
- alreadysent |= p->prefcodec;
- }
-
- /* Start by sending our preferred codecs */
+ add_codec_to_sdp(p, p->prefcodec & AST_FORMAT_AUDIO_MASK, 8000,
+ &m_audio_next, &m_audio_left,
+ &a_audio_next, &a_audio_left,
+ debug);
+ alreadysent |= p->prefcodec & AST_FORMAT_AUDIO_MASK;
+ }
+
+
+ /* Start by sending our preferred audio codecs */
for (x = 0; x < 32; x++) {
+ int pref_codec;
+
if (!(pref_codec = ast_codec_pref_index(&p->prefs, x)))
break;
@@ -5050,27 +5246,21 @@
if (alreadysent & pref_codec)
continue;
- if (pref_codec <= AST_FORMAT_MAX_AUDIO)
- add_codec_to_sdp(p, pref_codec, 8000,
- &m_audio_next, &m_audio_left,
- &a_audio_next, &a_audio_left,
- debug);
- else
- add_codec_to_sdp(p, pref_codec, 90000,
- &m_video_next, &m_video_left,
- &a_video_next, &a_video_left,
- debug);
+ add_codec_to_sdp(p, pref_codec, 8000,
+ &m_audio_next, &m_audio_left,
+ &a_audio_next, &a_audio_left,
+ debug);
alreadysent |= pref_codec;
}
- /* Now send any other common codecs, and non-codec formats: */
- for (x = 1;
- x <= ((ast_test_flag(&p->flags[1], SIP_PAGE2_VIDEOSUPPORT) && p->vrtp) ? AST_FORMAT_MAX_VIDEO : AST_FORMAT_MAX_AUDIO);
- x <<= 1) {
- if (!(capability & x))
+ /* Now send any other common audio and video codecs, and non-codec formats: */
+ for (x = 1; x <= (needvideo ? AST_FORMAT_MAX_VIDEO : AST_FORMAT_MAX_AUDIO); x <<= 1) {
+ if (!(capability & x)) /* Codec not requested */
continue;
- if (alreadysent & x)
+ ast_log(LOG_DEBUG, "--- Checking codec ... %d\n", x);
+
+ if (alreadysent & x) /* Already added to SDP */
continue;
if (x <= AST_FORMAT_MAX_AUDIO)
@@ -5078,13 +5268,14 @@
&m_audio_next, &m_audio_left,
&a_audio_next, &a_audio_left,
debug);
- else
+ else
add_codec_to_sdp(p, x, 90000,
&m_video_next, &m_video_left,
&a_video_next, &a_video_left,
debug);
}
+ /* Now add DTMF RFC2833 telephony-event as a codec */
for (x = 1; x <= AST_RTP_MAX; x <<= 1) {
if (!(p->noncodeccapability & x))
continue;
@@ -5095,6 +5286,9 @@
debug);
}
+ if (option_debug > 2)
+ ast_log(LOG_DEBUG, "-- Done with adding codecs to SDP\n");
+
if(!ast_internal_timing_enabled(p->owner))
ast_build_string(&a_audio_next, &a_audio_left, "a=silenceSupp:off - - - -\r\n");
@@ -5102,38 +5296,38 @@
ast_log(LOG_WARNING, "SIP SDP may be truncated due to undersized buffer!!\n");
ast_build_string(&m_audio_next, &m_audio_left, "\r\n");
- ast_build_string(&m_video_next, &m_video_left, "\r\n");
-
- len = strlen(v) + strlen(s) + strlen(o) + strlen(c) + strlen(t) + strlen(m_audio) + strlen(a_audio) + strlen(hold);
- if ((p->vrtp) &&
- (!ast_test_flag(&p->flags[0], SIP_NOVIDEO)) &&
- (capability & VIDEO_CODEC_MASK)) /* only if video response is appropriate */
- len += strlen(m_video) + strlen(a_video) + strlen(b) + strlen(hold);
+ if (needvideo)
+ ast_build_string(&m_video_next, &m_video_left, "\r\n");
+
+ len = strlen(version) + strlen(subject) + strlen(owner) + strlen(connection) + strlen(stime) + strlen(m_audio) + strlen(a_audio) + strlen(hold);
+ if (needvideo) /* only if video response is appropriate */
+ len += strlen(m_video) + strlen(a_video) + strlen(bandwidth) + strlen(hold);
add_header(resp, "Content-Type", "application/sdp");
add_header_contentLength(resp, len);
- add_line(resp, v);
- add_line(resp, o);
- add_line(resp, s);
- add_line(resp, c);
- if ((p->vrtp) &&
- (!ast_test_flag(&p->flags[0], SIP_NOVIDEO)) &&
- (capability & VIDEO_CODEC_MASK)) /* only if video response is appropriate */
- add_line(resp, b);
- add_line(resp, t);
+ add_line(resp, version);
+ add_line(resp, owner);
+ add_line(resp, subject);
+ add_line(resp, connection);
+ if (needvideo) /* only if video response is appropriate */
+ add_line(resp, bandwidth);
+ add_line(resp, stime);
add_line(resp, m_audio);
add_line(resp, a_audio);
add_line(resp, hold);
- if ((p->vrtp) &&
- (!ast_test_flag(&p->flags[0], SIP_NOVIDEO)) &&
- (capability & VIDEO_CODEC_MASK)) { /* only if video response is appropriate */
+ if (needvideo) { /* only if video response is appropriate */
add_line(resp, m_video);
add_line(resp, a_video);
- add_line(resp, hold);
+ add_line(resp, hold); /* Repeat hold for the video stream */
}
/* Update lastrtprx when we send our SDP */
p->lastrtprx = p->lastrtptx = time(NULL); /* XXX why both ? */
+
+ if (option_debug > 2) {
+ char buf[BUFSIZ];
+ ast_log(LOG_DEBUG, "Done building SDP. Settling with this capability: %s\n", ast_getformatname_multiple(buf, BUFSIZ, capability));
+ }
return 0;
}
@@ -7744,20 +7938,24 @@
p->pickupgroup = user->pickupgroup;
if (user->callingpres) /* User callingpres setting will override RPID header */
p->callingpres = user->callingpres;
- p->capability = user->capability;
- p->jointcapability = user->capability;
- p->maxcallbitrate = user->maxcallbitrate;
- if (!ast_test_flag(&p->flags[1], SIP_PAGE2_VIDEOSUPPORT) && p->vrtp) {
- ast_rtp_destroy(p->vrtp);
- p->vrtp = NULL;
- }
- if (p->peercapability)
+
+ /* Set default codec settings for this call */
+ p->capability = user->capability; /* User codec choice */
+ p->jointcapability = user->capability; /* Our codecs */
+ if (p->peercapability) /* AND with peer's codecs */
p->jointcapability &= p->peercapability;
if ((ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833) ||
(ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO))
p->noncodeccapability |= AST_RTP_DTMF;
else
p->noncodeccapability &= ~AST_RTP_DTMF;
+
+ p->maxcallbitrate = user->maxcallbitrate;
+ /* If we do not support video, remove video from call structure */
+ if (!ast_test_flag(&p->flags[1], SIP_PAGE2_VIDEOSUPPORT) && p->vrtp) {
+ ast_rtp_destroy(p->vrtp);
+ p->vrtp = NULL;
+ }
}
if (user && debug)
ast_verbose("Found user '%s'\n", user->name);
@@ -10197,13 +10395,14 @@
}
}
-/*! \brief Handle SIP response in dialogue */
+/*! \brief Handle SIP response to INVITE dialogue */
static void handle_response_invite(struct sip_pvt *p, int resp, char *rest, struct sip_request *req, int seqno)
{
int outgoing = ast_test_flag(&p->flags[0], SIP_OUTGOING);
+ int res = 0;
+ int reinvite = (p->owner && p->owner->_state == AST_STATE_UP);
if (option_debug > 3) {
- int reinvite = (p->owner && p->owner->_state == AST_STATE_UP);
if (reinvite)
ast_log(LOG_DEBUG, "SIP response %d to RE-invite on %s call %s\n", resp, outgoing ? "outgoing" : "incoming", p->callid);
else
@@ -10241,7 +10440,7 @@
}
}
if (find_sdp(req)) {
- process_sdp(p, req);
+ res = process_sdp(p, req);
if (!ast_test_flag(req, SIP_PKT_IGNORE) && p->owner) {
/* Queue a progress frame only if we have SDP in 180 */
ast_queue_control(p->owner, AST_CONTROL_PROGRESS);
@@ -10257,7 +10456,7 @@
sip_cancel_destroy(p);
/* Ignore 183 Session progress without SDP */
if (find_sdp(req)) {
- process_sdp(p, req);
+ res = process_sdp(p, req);
if (!ast_test_flag(req, SIP_PKT_IGNORE) && p->owner) {
/* Queue a progress frame */
ast_queue_control(p->owner, AST_CONTROL_PROGRESS);
@@ -10272,8 +10471,13 @@
if (!ast_test_flag(req, SIP_PKT_IGNORE))
sip_cancel_destroy(p);
p->authtries = 0;
- if (find_sdp(req))
- process_sdp(p, req);
+ if (find_sdp(req)) {
+ if ((res = process_sdp(p, req)) && !ast_test_flag(req, SIP_PKT_IGNORE))
+ if (!reinvite)
+ /* This 200 OK's SDP is not acceptable, so we need to ack, then hangup */
+ /* For re-invites, we try to recover */
+ ast_set_flag(&p->flags[0], SIP_PENDINGBYE);
+ }
/* Parse contact header for continued conversation */
/* When we get 200 OK, we know which device (and IP) to contact for this call */
@@ -10298,7 +10502,7 @@
}
if (!ast_test_flag(req, SIP_PKT_IGNORE) && p->owner) {
- if (p->owner->_state != AST_STATE_UP) {
+ if (!reinvite) {
ast_queue_control(p->owner, AST_CONTROL_ANSWER);
} else { /* RE-invite */
ast_queue_frame(p->owner, &ast_null_frame);
@@ -13460,6 +13664,9 @@
[... 168 lines stripped ...]
More information about the svn-commits
mailing list