mirror of https://github.com/sipwise/asterisk.git
Closes issue ASTERISK-11840 / MT#5971
Also imported r197588 for this patch to apply cleanly.
Revision: 207423
U branches/1.4/channels/chan_sip.c
r207423 | mmichelson | 2009-07-20 14:40:01 -0500 (Mon, 20 Jul 2009) | 33 lines
Answer video SDP offers properly when videosupport is not enabled.
Copied from Review board:
In issue 12434, the reporter describes a situation in which audio and video
is offered on the call, but because videosupport is disabled in sip.conf,
Asterisk gives no response at all to the video offer. According to RFC 3264,
all media offers should have a corresponding answer. For offers we do not
intend to actually reply to with meaningful values, we should still reply
with the port for the media stream set to 0.
In this patch, we take note of what types of media have been offered and
save the information on the sip_pvt. The SDP in the response will take into
account whether media was offered. If we are not otherwise going to answer
a media offer, we will insert an appropriate m= line with the port set to 0.
It is important to note that this patch is pretty much a bandage being
applied to a broken bone. The patch only helps for situations where video
is offered but videosupport is disabled and when udptl_pt is disabled but
T.38 is offered. Asterisk is not guaranteed to respond to every media offer.
Notable cases are when multiple streams of the same type are offered.
The 2 media stream limit is still present with this patch, too.
In trunk and the 1.6.X branches, things will be a bit different since Asterisk
also supports text in SDPs as well.
(closes issue ASTERISK-11840)
Reported by: mnnojd
------------------------------------------------------------------------
r197588 | mmichelson | 2009-05-28 23:27:49 +0800 (Thu, 28 May 2009) | 16 lines
Allow for media to arrive from an alternate source when responding to a reinvite with 491.
When we receive a SIP reinvite, it is possible that we may not be able to process the
reinvite immediately since we have also sent a reinvite out ourselves. The problem is
that whoever sent us the reinvite may have also sent a reinvite out to another party,
and that reinvite may have succeeded.
As a result, even though we are not going to accept the reinvite we just received, it
is important for us to not have problems if we suddenly start receiving RTP from a new
source. The fix for this is to grab the media source information from the SDP of the
reinvite that we receive. This information is passed to the RTP layer so that it will
know about the alternate source for media.
Review: https://reviewboard.asterisk.org/r/252
(cherry picked from commit 69c088172d)
mr3.2.1
parent
85c95491a4
commit
ef583e130a
Notes:
Sipwise Jenkins Builder
12 years ago
jenkins_trigger: false
@ -0,0 +1,185 @@
|
||||
Index: include/asterisk/rtp.h
|
||||
===================================================================
|
||||
--- include/asterisk/rtp.h (revision 197587)
|
||||
+++ include/asterisk/rtp.h (revision 197588)
|
||||
@@ -127,6 +127,23 @@
|
||||
|
||||
void ast_rtp_set_peer(struct ast_rtp *rtp, struct sockaddr_in *them);
|
||||
|
||||
+/*!
|
||||
+ * \since 1.4.26
|
||||
+ * \brief set potential alternate source for RTP media
|
||||
+ *
|
||||
+ * This function may be used to give the RTP stack a hint that there is a potential
|
||||
+ * second source of media. One case where this is used is when the SIP stack receives
|
||||
+ * a REINVITE to which it will be replying with a 491. In such a scenario, the IP and
|
||||
+ * port information in the SDP of that REINVITE lets us know that we may receive media
|
||||
+ * from that source/those sources even though the SIP transaction was unable to be completed
|
||||
+ * successfully
|
||||
+ *
|
||||
+ * \param rtp The RTP structure we wish to set up an alternate host/port on
|
||||
+ * \param alt The address information for the alternate media source
|
||||
+ * \retval void
|
||||
+ */
|
||||
+void ast_rtp_set_alt_peer(struct ast_rtp *rtp, struct sockaddr_in *alt);
|
||||
+
|
||||
/* Copies from rtp to them and returns 1 if there was a change or 0 if it was already the same */
|
||||
int ast_rtp_get_peer(struct ast_rtp *rtp, struct sockaddr_in *them);
|
||||
|
||||
Index: channels/chan_sip.c
|
||||
===================================================================
|
||||
--- channels/chan_sip.c (revision 197587)
|
||||
+++ channels/chan_sip.c (revision 197588)
|
||||
@@ -5111,6 +5111,63 @@
|
||||
return;
|
||||
}
|
||||
|
||||
+enum media_type {
|
||||
+ SDP_AUDIO,
|
||||
+ SDP_VIDEO,
|
||||
+};
|
||||
+
|
||||
+static int get_ip_and_port_from_sdp(struct sip_request *req, const enum media_type media, struct sockaddr_in *sin)
|
||||
+{
|
||||
+ const char *m;
|
||||
+ const char *c;
|
||||
+ int miterator = req->sdp_start;
|
||||
+ int citerator = req->sdp_start;
|
||||
+ int x = 0;
|
||||
+ int numberofports;
|
||||
+ int len;
|
||||
+ char host[258] = ""; /*Initialize to empty so we will know if we have any input */
|
||||
+ struct ast_hostent audiohp;
|
||||
+ struct hostent *hp;
|
||||
+
|
||||
+ c = get_sdp_iterate(&citerator, req, "c");
|
||||
+ if (sscanf(c, "IN IP4 %256s", host) != 1) {
|
||||
+ ast_log(LOG_WARNING, "Invalid host in c= line, '%s'\n", c);
|
||||
+ /* Continue since there may be a valid host in a c= line specific to the audio stream */
|
||||
+ }
|
||||
+ /* We only want the m and c lines for audio */
|
||||
+ while ((m = get_sdp_iterate(&miterator, req, "m"))) {
|
||||
+ if ((media == SDP_AUDIO && ((sscanf(m, "audio %d/%d RTP/AVP %n", &x, &numberofports, &len) == 2 && len > 0) ||
|
||||
+ (sscanf(m, "audio %d RTP/AVP %n", &x, &len) == 1 && len > 0))) ||
|
||||
+ (media == SDP_VIDEO && ((sscanf(m, "video %d/%d RTP/AVP %n", &x, &numberofports, &len) == 2 && len > 0) ||
|
||||
+ (sscanf(m, "video %d RTP/AVP %n", &x, &len) == 1 && len > 0)))) {
|
||||
+ /* See if there's a c= line for this media stream.
|
||||
+ * XXX There is no guarantee that we'll be grabbing the c= line for this
|
||||
+ * particular media stream here. However, this is the same logic used in process_sdp.
|
||||
+ */
|
||||
+ c = get_sdp_iterate(&citerator, req, "c");
|
||||
+ if (!ast_strlen_zero(c)) {
|
||||
+ sscanf(c, "IN IP4 %256s", host);
|
||||
+ }
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (ast_strlen_zero(host) || x == 0) {
|
||||
+ ast_log(LOG_WARNING, "Failed to read an alternate host or port in SDP. Expect %s problems\n", media == SDP_AUDIO ? "audio" : "video");
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ hp = ast_gethostbyname(host, &audiohp);
|
||||
+ if (!hp) {
|
||||
+ ast_log(LOG_WARNING, "Could not look up IP address of alternate hostname. Expect %s problems\n", media == SDP_AUDIO? "audio" : "video");
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ memcpy(&sin->sin_addr, hp->h_addr, sizeof(sin->sin_addr));
|
||||
+ sin->sin_port = htons(x);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
/*! \brief Process SIP SDP offer, select formats and activate RTP channels
|
||||
If offer is rejected, we will not change any properties of the call
|
||||
Return 0 on success, a negative value on errors.
|
||||
@@ -14459,6 +14516,21 @@
|
||||
} else {
|
||||
/* We already have a pending invite. Sorry. You are on hold. */
|
||||
p->glareinvite = seqno;
|
||||
+ if (p->rtp && find_sdp(req)) {
|
||||
+ struct sockaddr_in sin;
|
||||
+ if (get_ip_and_port_from_sdp(req, SDP_AUDIO, &sin)) {
|
||||
+ ast_log(LOG_WARNING, "Failed to set an alternate media source on glared reinvite. Audio may not work properly on this call.\n");
|
||||
+ } else {
|
||||
+ ast_rtp_set_alt_peer(p->rtp, &sin);
|
||||
+ }
|
||||
+ if (p->vrtp) {
|
||||
+ if (get_ip_and_port_from_sdp(req, SDP_VIDEO, &sin)) {
|
||||
+ ast_log(LOG_WARNING, "Failed to set an alternate media source on glared reinvite. Video may not work properly on this call.\n");
|
||||
+ } else {
|
||||
+ ast_rtp_set_alt_peer(p->vrtp, &sin);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
transmit_response_reliable(p, "491 Request Pending", req);
|
||||
if (option_debug)
|
||||
ast_log(LOG_DEBUG, "Got INVITE on call where we already have pending INVITE, deferring that - %s\n", p->callid);
|
||||
Index: main/rtp.c
|
||||
===================================================================
|
||||
--- main/rtp.c (revision 197587)
|
||||
+++ main/rtp.c (revision 197588)
|
||||
@@ -151,6 +151,7 @@
|
||||
unsigned int flags;
|
||||
struct sockaddr_in us; /*!< Socket representation of the local endpoint. */
|
||||
struct sockaddr_in them; /*!< Socket representation of the remote endpoint. */
|
||||
+ struct sockaddr_in altthem; /*!< Alternate source of remote media */
|
||||
struct timeval rxcore;
|
||||
struct timeval txcore;
|
||||
double drxcore; /*!< The double representation of the first received packet */
|
||||
@@ -209,6 +210,7 @@
|
||||
int s; /*!< Socket */
|
||||
struct sockaddr_in us; /*!< Socket representation of the local endpoint. */
|
||||
struct sockaddr_in them; /*!< Socket representation of the remote endpoint. */
|
||||
+ struct sockaddr_in altthem; /*!< Alternate source for RTCP */
|
||||
unsigned int soc; /*!< What they told us */
|
||||
unsigned int spc; /*!< What they told us */
|
||||
unsigned int themrxlsr; /*!< The middle 32 bits of the NTP timestamp in the last received SR*/
|
||||
@@ -899,11 +901,13 @@
|
||||
}
|
||||
|
||||
packetwords = res / 4;
|
||||
-
|
||||
+
|
||||
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)) {
|
||||
+ if (((rtp->rtcp->them.sin_addr.s_addr != sin.sin_addr.s_addr) ||
|
||||
+ (rtp->rtcp->them.sin_port != sin.sin_port)) &&
|
||||
+ ((rtp->rtcp->altthem.sin_addr.s_addr != sin.sin_addr.s_addr) ||
|
||||
+ (rtp->rtcp->altthem.sin_port != sin.sin_port))) {
|
||||
memcpy(&rtp->rtcp->them, &sin, sizeof(rtp->rtcp->them));
|
||||
if (option_debug || rtpdebug)
|
||||
ast_log(LOG_DEBUG, "RTCP NAT: Got RTCP from other end. Now sending to address %s:%d\n", ast_inet_ntoa(rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port));
|
||||
@@ -1203,8 +1207,10 @@
|
||||
|
||||
/* Send to whoever send to us if NAT is turned on */
|
||||
if (rtp->nat) {
|
||||
- if ((rtp->them.sin_addr.s_addr != sin.sin_addr.s_addr) ||
|
||||
- (rtp->them.sin_port != sin.sin_port)) {
|
||||
+ if (((rtp->them.sin_addr.s_addr != sin.sin_addr.s_addr) ||
|
||||
+ (rtp->them.sin_port != sin.sin_port)) &&
|
||||
+ ((rtp->altthem.sin_addr.s_addr != sin.sin_addr.s_addr) ||
|
||||
+ (rtp->altthem.sin_port != sin.sin_port))) {
|
||||
rtp->them = sin;
|
||||
if (rtp->rtcp) {
|
||||
memcpy(&rtp->rtcp->them, &sin, sizeof(rtp->rtcp->them));
|
||||
@@ -2061,6 +2067,16 @@
|
||||
rtp->rxseqno = 0;
|
||||
}
|
||||
|
||||
+void ast_rtp_set_alt_peer(struct ast_rtp *rtp, struct sockaddr_in *alt)
|
||||
+{
|
||||
+ rtp->altthem.sin_port = alt->sin_port;
|
||||
+ rtp->altthem.sin_addr = alt->sin_addr;
|
||||
+ if (rtp->rtcp) {
|
||||
+ rtp->rtcp->altthem.sin_port = htons(ntohs(alt->sin_port) + 1);
|
||||
+ rtp->rtcp->altthem.sin_addr = alt->sin_addr;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
int ast_rtp_get_peer(struct ast_rtp *rtp, struct sockaddr_in *them)
|
||||
{
|
||||
if ((them->sin_family != AF_INET) ||
|
||||
@ -0,0 +1,157 @@
|
||||
Index: channels/chan_sip.c
|
||||
===================================================================
|
||||
--- channels/chan_sip.c (revision 207422)
|
||||
+++ channels/chan_sip.c (revision 207423)
|
||||
@@ -911,6 +911,10 @@
|
||||
enum referstatus status; /*!< REFER status */
|
||||
};
|
||||
|
||||
+struct offered_media {
|
||||
+ int offered;
|
||||
+ char text[128];
|
||||
+};
|
||||
/*! \brief sip_pvt: PVT structures are used for each SIP dialog, ie. a call, a registration, a subscribe */
|
||||
static struct sip_pvt {
|
||||
ast_mutex_t lock; /*!< Dialog private lock */
|
||||
@@ -1034,6 +1038,21 @@
|
||||
struct sip_invite_param *options; /*!< Options for INVITE */
|
||||
int autoframing;
|
||||
int hangupcause; /*!< Storage of hangupcause copied from our owner before we disconnect from the AST channel (only used at hangup) */
|
||||
+ /*! When receiving an SDP offer, it is important to take note of what media types were offered.
|
||||
+ * By doing this, even if we don't want to answer a particular media stream with something meaningful, we can
|
||||
+ * still put an m= line in our answer with the port set to 0.
|
||||
+ *
|
||||
+ * The reason for the length being 3 is that in this branch of Asterisk, the only media types supported are
|
||||
+ * image, audio, and video. Therefore we need to keep track of which types of media were offered.
|
||||
+ *
|
||||
+ * Note that if we wanted to be 100% correct, we would keep a list of all media streams offered. That way we could respond
|
||||
+ * even to unknown media types, and we could respond to multiple streams of the same type. Such large-scale changes
|
||||
+ * are not a good idea for released branches, though, so we're compromising by just making sure that for the common cases:
|
||||
+ * audio and video, and audio and T.38, we give the appropriate response to both media streams.
|
||||
+ *
|
||||
+ * The large-scale changes would be a good idea for implementing during an SDP rewrite.
|
||||
+ */
|
||||
+ struct offered_media offered_media[3];
|
||||
} *iflist = NULL;
|
||||
|
||||
/*! Max entires in the history list for a sip_pvt */
|
||||
@@ -5148,6 +5167,7 @@
|
||||
enum media_type {
|
||||
SDP_AUDIO,
|
||||
SDP_VIDEO,
|
||||
+ SDP_IMAGE,
|
||||
};
|
||||
|
||||
static int get_ip_and_port_from_sdp(struct sip_request *req, const enum media_type media, struct sockaddr_in *sin)
|
||||
@@ -5274,6 +5294,7 @@
|
||||
/* Update our last rtprx when we receive an SDP, too */
|
||||
p->lastrtprx = p->lastrtptx = time(NULL); /* XXX why both ? */
|
||||
|
||||
+ memset(p->offered_media, 0, sizeof(p->offered_media));
|
||||
|
||||
/* Try to find first media stream */
|
||||
m = get_sdp(req, "m");
|
||||
@@ -5312,11 +5333,14 @@
|
||||
if ((sscanf(m, "audio %d/%d RTP/AVP %n", &x, &numberofports, &len) == 2 && len > 0) ||
|
||||
(sscanf(m, "audio %d RTP/AVP %n", &x, &len) == 1 && len > 0)) {
|
||||
audio = TRUE;
|
||||
+ p->offered_media[SDP_AUDIO].offered = TRUE;
|
||||
numberofmediastreams++;
|
||||
/* Found audio stream in this media definition */
|
||||
portno = x;
|
||||
/* Scan through the RTP payload types specified in a "m=" line: */
|
||||
- for (codecs = m + len; !ast_strlen_zero(codecs); codecs = ast_skip_blanks(codecs + len)) {
|
||||
+ codecs = m + len;
|
||||
+ ast_copy_string(p->offered_media[SDP_AUDIO].text, codecs, sizeof(p->offered_media[SDP_AUDIO].text));
|
||||
+ for (; !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);
|
||||
return -1;
|
||||
@@ -5328,10 +5352,13 @@
|
||||
} else if ((sscanf(m, "video %d/%d RTP/AVP %n", &x, &numberofports, &len) == 2 && len > 0) ||
|
||||
(sscanf(m, "video %d RTP/AVP %n", &x, &len) == 1 && len >= 0)) {
|
||||
/* If it is not audio - is it video ? */
|
||||
- ast_clear_flag(&p->flags[0], SIP_NOVIDEO);
|
||||
+ ast_clear_flag(&p->flags[0], SIP_NOVIDEO);
|
||||
+ p->offered_media[SDP_VIDEO].offered = TRUE;
|
||||
numberofmediastreams++;
|
||||
vportno = x;
|
||||
/* Scan through the RTP payload types specified in a "m=" line: */
|
||||
+ codecs = m + len;
|
||||
+ ast_copy_string(p->offered_media[SDP_VIDEO].text, codecs, sizeof(p->offered_media[SDP_VIDEO].text));
|
||||
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);
|
||||
@@ -5345,6 +5372,7 @@
|
||||
(sscanf(m, "image %d UDPTL t38%n", &x, &len) == 1 && len >= 0) )) {
|
||||
if (debug)
|
||||
ast_verbose("Got T.38 offer in SDP in dialog %s\n", p->callid);
|
||||
+ p->offered_media[SDP_IMAGE].offered = TRUE;
|
||||
udptlportno = x;
|
||||
numberofmediastreams++;
|
||||
|
||||
@@ -6670,6 +6698,7 @@
|
||||
size_t a_audio_left = sizeof(a_audio);
|
||||
size_t a_video_left = sizeof(a_video);
|
||||
size_t a_modem_left = sizeof(a_modem);
|
||||
+ char dummy_answer[256];
|
||||
|
||||
int x;
|
||||
int capability = 0;
|
||||
@@ -6756,7 +6785,7 @@
|
||||
vdest.sin_addr = p->ourip;
|
||||
vdest.sin_port = vsin.sin_port;
|
||||
}
|
||||
- ast_build_string(&m_video_next, &m_video_left, "m=video %d RTP/AVP", ntohs(vdest.sin_port));
|
||||
+ ast_build_string(&m_video_next, &m_video_left, "m=video %d RTP/AVP", ntohs(vsin.sin_port));
|
||||
|
||||
/* Build max bitrate string */
|
||||
if (p->maxcallbitrate)
|
||||
@@ -6926,15 +6955,23 @@
|
||||
add_line(resp, m_audio);
|
||||
add_line(resp, a_audio);
|
||||
add_line(resp, hold);
|
||||
+ } else if (p->offered_media[SDP_AUDIO].offered) {
|
||||
+ snprintf(dummy_answer, sizeof(dummy_answer), "m=audio 0 RTP/AVP %s\r\n", p->offered_media[SDP_AUDIO].text);
|
||||
+ add_line(resp, dummy_answer);
|
||||
}
|
||||
if (needvideo) { /* only if video response is appropriate */
|
||||
add_line(resp, m_video);
|
||||
add_line(resp, a_video);
|
||||
add_line(resp, hold); /* Repeat hold for the video stream */
|
||||
+ } else if (p->offered_media[SDP_VIDEO].offered) {
|
||||
+ snprintf(dummy_answer, sizeof(dummy_answer), "m=video 0 RTP/AVP %s\r\n", p->offered_media[SDP_VIDEO].text);
|
||||
+ add_line(resp, dummy_answer);
|
||||
}
|
||||
if (add_t38) {
|
||||
add_line(resp, m_modem);
|
||||
add_line(resp, a_modem);
|
||||
+ } else if (p->offered_media[SDP_IMAGE].offered) {
|
||||
+ add_line(resp, "m=image 0 udptl t38\r\n");
|
||||
}
|
||||
|
||||
/* Update lastrtprx when we send our SDP */
|
||||
@@ -7076,6 +7113,7 @@
|
||||
add_header(&req, "X-asterisk-Info", "SIP re-invite (External RTP bridge)");
|
||||
if (!ast_test_flag(&p->flags[0], SIP_NO_HISTORY))
|
||||
append_history(p, "ReInv", "Re-invite sent");
|
||||
+ memset(p->offered_media, 0, sizeof(p->offered_media));
|
||||
add_sdp(&req, p, 1, 0);
|
||||
/* Use this as the basis */
|
||||
initialize_initreq(p, &req);
|
||||
@@ -7098,6 +7136,7 @@
|
||||
add_header(&req, "Supported", SUPPORTED_EXTENSIONS);
|
||||
if (sipdebug)
|
||||
add_header(&req, "X-asterisk-info", "SIP re-invite (T38 switchover)");
|
||||
+ memset(p->offered_media, 0, sizeof(p->offered_media));
|
||||
add_sdp(&req, p, 0, 1);
|
||||
|
||||
/* Use this as the basis */
|
||||
@@ -7430,6 +7469,7 @@
|
||||
ast_channel_unlock(chan);
|
||||
}
|
||||
if (sdp) {
|
||||
+ memset(p->offered_media, 0, sizeof(p->offered_media));
|
||||
if (p->udptl && p->t38.state == T38_LOCAL_REINVITE) {
|
||||
ast_udptl_offered_from_local(p->udptl, 1);
|
||||
if (option_debug)
|
||||
Loading…
Reference in new issue