diff options
-rw-r--r-- | channels/chan_sip.c | 46 |
1 files changed, 43 insertions, 3 deletions
diff --git a/channels/chan_sip.c b/channels/chan_sip.c index ca8e38473..86feacdf3 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -911,6 +911,10 @@ struct sip_refer { 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 @@ static struct sip_pvt { 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 @@ static void change_hold_state(struct sip_pvt *dialog, struct sip_request *req, i 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 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req) /* 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 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req) 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 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req) } 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 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req) (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 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int 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 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int 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 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int 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 @@ static int transmit_reinvite_with_sdp(struct sip_pvt *p) 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 @@ static int transmit_reinvite_with_t38_sdp(struct sip_pvt *p) 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 @@ static int transmit_invite(struct sip_pvt *p, int sipmethod, int sdp, int init) 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) |