![]() |
Home page |
Mailing list |
Docs
Asterisk developer's documentation :: Codename Pineapple
sip3_sdprtp.c File Reference
Olle E. Johansson <oej@edvina.net> (all the chan_sip3 changes)
Definition in file sip3_sdprtp.c.
#include "asterisk.h"
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <errno.h>
#include <stdlib.h>
#include <fcntl.h>
#include <netdb.h>
#include <signal.h>
#include <sys/signal.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <regex.h>
#include "asterisk/lock.h"
#include "asterisk/channel.h"
#include "asterisk/config.h"
#include "asterisk/logger.h"
#include "asterisk/module.h"
#include "asterisk/pbx.h"
#include "asterisk/options.h"
#include "asterisk/io.h"
#include "asterisk/rtp.h"
#include "asterisk/udptl.h"
#include "asterisk/acl.h"
#include "asterisk/manager.h"
#include "asterisk/callerid.h"
#include "asterisk/cli.h"
#include "asterisk/app.h"
#include "asterisk/musiconhold.h"
#include "asterisk/dsp.h"
#include "asterisk/features.h"
#include "asterisk/srv.h"
#include "asterisk/astdb.h"
#include "asterisk/causes.h"
#include "asterisk/utils.h"
#include "asterisk/file.h"
#include "asterisk/astobj.h"
#include "asterisk/linkedlists.h"
#include "asterisk/stringfields.h"
#include "asterisk/monitor.h"
#include "asterisk/abstract_jb.h"
#include "asterisk/compiler.h"
#include "sip3.h"
#include "sip3funcs.h"
Include dependency graph for sip3_sdprtp.c:

Go to the source code of this file.
Functions | |
| static void | add_codec_to_sdp (const struct sip_dialog *p, int codec, int sample_rate, char **m_buf, size_t *m_size, char **a_buf, size_t *a_size, int debug, int *min_packet_size) |
| Add codec offer to SDP offer/answer body in INVITE or 200 OK. | |
| static void | add_noncodec_to_sdp (const struct sip_dialog *p, int format, int sample_rate, char **m_buf, size_t *m_size, char **a_buf, size_t *a_size, int debug) |
| Add RFC 2833 DTMF offer to SDP. | |
| int | add_sdp (struct sip_request *resp, struct sip_dialog *p) |
| Add Session Description Protocol message. | |
| int | find_sdp (struct sip_request *req) |
| Determine whether a SIP message contains an SDP in its body. | |
| char * | get_body (struct sip_request *req, char *name) |
| Get the message body part identified by name=. | |
| static char * | get_body_by_line (const char *line, const char *name, int nameLen) |
| Reads one line of SIP message body. | |
| static void | get_our_media_address (struct sip_dialog *p, int needvideo, struct sockaddr_in *sin, struct sockaddr_in *vsin, struct sockaddr_in *dest, struct sockaddr_in *vdest) |
| Set all IP media addresses for this call. | |
| static const char * | get_sdp (struct sip_request *req, const char *name) |
| Get a line from an SDP message body. | |
| static const char * | get_sdp_iterate (int *start, struct sip_request *req, const char *name) |
| Lookup 'name' in the SDP starting at the 'start' line. Returns the matching line, and 'start' is updated with the next line number. | |
| int | process_sdp (struct sip_dialog *p, struct sip_request *req) |
| Process SIP SDP offer, select formats and activate RTP channels If offer is rejected, we will not change any properties of the call. | |
| void | register_rtp_and_udptl (void) |
| Register RTP and UDPTL to the subsystems. | |
| static int | sip_get_codec (struct ast_channel *chan) |
| Return SIP UA's codec (part of the RTP interface). | |
| enum ast_rtp_get_result | sip_get_rtp_peer (struct ast_channel *chan, struct ast_rtp **rtp) |
| Returns null if we can't reinvite audio (part of RTP interface). | |
| static struct ast_udptl * | sip_get_udptl_peer (struct ast_channel *chan) |
| Get UDPTL peer address (part of UDPTL interface). | |
| enum ast_rtp_get_result | sip_get_vrtp_peer (struct ast_channel *chan, struct ast_rtp **rtp) |
| Returns null if we can't reinvite video (part of RTP interface). | |
| GNURK struct ast_frame * | sip_read (struct ast_channel *ast) |
| Read SIP RTP from channel. | |
| static struct ast_frame * | sip_rtp_read (struct ast_channel *ast, struct sip_dialog *p, int *faxdetect) |
| Read RTP from network. | |
| int | sip_set_rtp_peer (struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp, int codecs, int nat_active) |
| Set the RTP peer for this call. | |
| static int | sip_set_udptl_peer (struct ast_channel *chan, struct ast_udptl *udptl) |
| Determine UDPTL peer address for re-invite (part of UDPTL interface). | |
| void | stop_media_flows (struct sip_dialog *dialog) |
| Immediately stop RTP, VRTP and UDPTL as applicable. | |
| void | unregister_rtp_and_udptl (void) |
| UNRegister RTP and UDPTL to the subsystems. | |
Variables | |
| static struct ast_rtp_protocol | sip_rtp |
| Interface structure with callbacks used to connect to RTP module. | |
| static struct ast_udptl_protocol | sip_udptl |
| Interface structure with callbacks used to connect to UDPTL module. | |
|
||||||||||||||||||||||||||||||||||||||||
|
Add codec offer to SDP offer/answer body in INVITE or 200 OK.
Definition at line 957 of file sip3_sdprtp.c. References ast_build_string(), ast_codec_pref_getsize(), AST_FORMAT_G729A, AST_FORMAT_ILBC, ast_getformatname(), ast_rtp_codec_getpref(), ast_rtp_lookup_code(), ast_rtp_lookup_mime_subtype(), AST_RTP_OPT_G726_NONSTANDARD, ast_test_flag, ast_verbose(), sip_dialog::flags, fmt, sip_dialog::rtp, and SIP_G726_NONSTANDARD. 00960 { 00961 int rtp_code; 00962 struct ast_format_list fmt; 00963 00964 00965 if (debug) 00966 ast_verbose("Adding codec 0x%x (%s) to SDP\n", codec, ast_getformatname(codec)); 00967 if ((rtp_code = ast_rtp_lookup_code(p->rtp, 1, codec)) == -1) 00968 return; 00969 00970 if (p->rtp) { 00971 struct ast_codec_pref *pref = ast_rtp_codec_getpref(p->rtp); 00972 fmt = ast_codec_pref_getsize(pref, codec); 00973 } else /* I dont see how you couldn't have p->rtp, but good to check for and error out if not there like earlier code */ 00974 return; 00975 ast_build_string(m_buf, m_size, " %d", rtp_code); 00976 ast_build_string(a_buf, a_size, "a=rtpmap:%d %s/%d\r\n", rtp_code, 00977 ast_rtp_lookup_mime_subtype(1, codec, 00978 ast_test_flag(&p->flags[0], SIP_G726_NONSTANDARD) ? AST_RTP_OPT_G726_NONSTANDARD : 0), 00979 sample_rate); 00980 if (codec == AST_FORMAT_G729A) { 00981 /* Indicate that we don't support VAD (G.729 annex B) */ 00982 ast_build_string(a_buf, a_size, "a=fmtp:%d annexb=no\r\n", rtp_code); 00983 } else if (codec == AST_FORMAT_ILBC) { 00984 /* Add information about us using only 20/30 ms packetization */ 00985 ast_build_string(a_buf, a_size, "a=fmtp:%d mode=%d\r\n", rtp_code, fmt.cur_ms); 00986 } 00987 00988 if (fmt.cur_ms && (fmt.cur_ms < *min_packet_size)) 00989 *min_packet_size = fmt.cur_ms; 00990 00991 /* Our first codec packetization processed cannot be zero */ 00992 if ((*min_packet_size)==0 && fmt.cur_ms) 00993 *min_packet_size = fmt.cur_ms; 00994 }
|
|
||||||||||||||||||||||||||||||||||||
|
Add RFC 2833 DTMF offer to SDP.
Definition at line 936 of file sip3_sdprtp.c. References ast_build_string(), AST_RTP_DTMF, ast_rtp_lookup_code(), ast_rtp_lookup_mime_subtype(), ast_verbose(), and sip_dialog::rtp. 00939 { 00940 int rtp_code; 00941 00942 if (debug) 00943 ast_verbose("Adding non-codec 0x%x (%s) to SDP\n", format, ast_rtp_lookup_mime_subtype(0, format, 0)); 00944 if ((rtp_code = ast_rtp_lookup_code(p->rtp, 0, format)) == -1) 00945 return; 00946 00947 ast_build_string(m_buf, m_size, " %d", rtp_code); 00948 ast_build_string(a_buf, a_size, "a=rtpmap:%d %s/%d\r\n", rtp_code, 00949 ast_rtp_lookup_mime_subtype(0, format, 0), 00950 sample_rate); 00951 if (format == AST_RTP_DTMF) 00952 /* Indicate we support DTMF and FLASH... */ 00953 ast_build_string(a_buf, a_size, "a=fmtp:%d 0-16\r\n", rtp_code); 00954 }
|
|
||||||||||||
|
Add Session Description Protocol message.
Definition at line 1029 of file sip3_sdprtp.c. References add_codec_to_sdp(), ast_build_string(), ast_codec_pref_index(), AST_FORMAT_AUDIO_MASK, AST_FORMAT_VIDEO_MASK, ast_getformatname_multiple(), ast_inet_ntoa(), ast_log(), ast_test_flag, ast_verbose(), capability, FALSE, sip_dialog::flags, get_our_media_address(), sip_dialog::jointcapability, len, LOG_WARNING, sip_dialog::maxcallbitrate, option_debug, sip_dialog::ourip, sip_dialog::prefcodec, sip_dialog::prefs, sip_dialog::rtp, sip_dialog::sessionid, sip_dialog::sessionversion, sip_debug_test_pvt(), SIP_NOVIDEO, SIP_PAGE2_CALL_ONHOLD_INACTIVE, SIP_PAGE2_CALL_ONHOLD_ONEDIR, SIP_PAGE2_T38SUPPORT_RTP, sip_dialog::t38, t38properties::t38support, TRUE, and sip_dialog::vrtp. 01030 { 01031 int len = 0; 01032 int alreadysent = 0; 01033 01034 struct sockaddr_in sin; 01035 struct sockaddr_in vsin; 01036 struct sockaddr_in dest; 01037 struct sockaddr_in vdest = { 0, }; 01038 01039 /* SDP fields */ 01040 char *version = "v=0\r\n"; /* Protocol version */ 01041 char *subject = "s=session\r\n"; /* Subject of the session */ 01042 char owner[256]; /* Session owner/creator */ 01043 char connection[256]; /* Connection data */ 01044 char *stime = "t=0 0\r\n"; /* Time the session is active */ 01045 char bandwidth[256] = ""; /* Max bitrate */ 01046 char *hold; 01047 char m_audio[256]; /* Media declaration line for audio */ 01048 char m_video[256]; /* Media declaration line for video */ 01049 char a_audio[1024]; /* Attributes for audio */ 01050 char a_video[1024]; /* Attributes for video */ 01051 char *m_audio_next = m_audio; 01052 char *m_video_next = m_video; 01053 size_t m_audio_left = sizeof(m_audio); 01054 size_t m_video_left = sizeof(m_video); 01055 char *a_audio_next = a_audio; 01056 char *a_video_next = a_video; 01057 size_t a_audio_left = sizeof(a_audio); 01058 size_t a_video_left = sizeof(a_video); 01059 01060 int x; 01061 int capability; 01062 int needvideo = FALSE; 01063 int debug = sip_debug_test_pvt(p); 01064 int min_audio_packet_size = 0; 01065 int min_video_packet_size = 0; 01066 01067 m_video[0] = '\0'; /* Reset the video media string if it's not needed */ 01068 01069 if (!p->rtp) { 01070 ast_log(LOG_WARNING, "No way to add SDP without an RTP structure\n"); 01071 return -1; 01072 } 01073 01074 /* Set RTP Session ID and version */ 01075 if (!p->sessionid) { 01076 p->sessionid = getpid(); 01077 p->sessionversion = p->sessionid; 01078 } else 01079 p->sessionversion++; 01080 01081 /* Ok, let's start working with codec selection here */ 01082 capability = p->jointcapability; 01083 01084 if (option_debug > 1) { 01085 char codecbuf[BUFSIZ]; 01086 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"); 01087 ast_log(LOG_DEBUG, "** Our prefcodec: %s \n", ast_getformatname_multiple(codecbuf, sizeof(codecbuf), p->prefcodec)); 01088 } 01089 01090 #ifdef WHEN_WE_HAVE_T38_FOR_OTHER_TRANSPORTS 01091 if ((ast_test_flag(&p->t38.t38support, SIP_PAGE2_T38SUPPORT_RTP))) { 01092 ast_build_string(&m_audio_next, &m_audio_left, " %d", 191); 01093 ast_build_string(&a_audio_next, &a_audio_left, "a=rtpmap:%d %s/%d\r\n", 191, "t38", 8000); 01094 } 01095 #endif 01096 01097 /* Check if we need video in this call */ 01098 if((capability & AST_FORMAT_VIDEO_MASK) && !ast_test_flag(&p->flags[0], SIP_NOVIDEO)) { 01099 if (p->vrtp) { 01100 needvideo = TRUE; 01101 if (option_debug > 1) 01102 ast_log(LOG_DEBUG, "This call needs video offers! \n"); 01103 } else if (option_debug > 1) 01104 ast_log(LOG_DEBUG, "This call needs video offers, but there's no video support enabled ! \n"); 01105 } 01106 01107 /* Get our media IP addresses for RTP */ 01108 get_our_media_address(p, needvideo, &sin, &vsin, &dest, &vdest); 01109 01110 /* Ok, we need video. Let's add what we need for video and set codecs. 01111 Video is handled differently than audio since we can not transcode. */ 01112 if (needvideo) { 01113 ast_build_string(&m_video_next, &m_video_left, "m=video %d RTP/AVP", ntohs(vdest.sin_port)); 01114 01115 /* Build max bitrate string */ 01116 if (p->maxcallbitrate) 01117 snprintf(bandwidth, sizeof(bandwidth), "b=CT:%d\r\n", p->maxcallbitrate); 01118 if (debug) 01119 ast_verbose("Video is at %s port %d\n", ast_inet_ntoa(p->ourip), ntohs(vsin.sin_port)); 01120 01121 /*! \todo XXX We need to select one codec, not many, since there's no transcoding */ 01122 } 01123 if (debug) 01124 ast_verbose("Audio is at %s port %d\n", ast_inet_ntoa(p->ourip), ntohs(sin.sin_port)); 01125 01126 /* Start building generic SDP headers */ 01127 01128 /* We break with the "recommendation" and send our IP, in order that our 01129 peer doesn't have to ast_gethostbyname() us */ 01130 01131 snprintf(owner, sizeof(owner), "o=root %d %d IN IP4 %s\r\n", p->sessionid, p->sessionversion, ast_inet_ntoa(dest.sin_addr)); 01132 snprintf(connection, sizeof(connection), "c=IN IP4 %s\r\n", ast_inet_ntoa(dest.sin_addr)); 01133 ast_build_string(&m_audio_next, &m_audio_left, "m=audio %d RTP/AVP", ntohs(dest.sin_port)); 01134 01135 if (ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD_ONEDIR)) 01136 hold = "a=recvonly\r\n"; 01137 else if (ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD_INACTIVE)) 01138 hold = "a=inactive\r\n"; 01139 else 01140 hold = "a=sendrecv\r\n"; 01141 01142 /* Now, start adding audio codecs. These are added in this order: 01143 - First what was requested by the calling channel 01144 - Then preferences in order from sip.conf device config for this peer/user 01145 - Then other codecs in capabilities, including video 01146 */ 01147 01148 /* Prefer the audio codec we were requested to use, first, no matter what 01149 Note that p->prefcodec can include video codecs, so mask them out 01150 */ 01151 if (capability & p->prefcodec) { 01152 add_codec_to_sdp(p, p->prefcodec & AST_FORMAT_AUDIO_MASK, 8000, 01153 &m_audio_next, &m_audio_left, 01154 &a_audio_next, &a_audio_left, 01155 debug, &min_audio_packet_size); 01156 alreadysent |= p->prefcodec & AST_FORMAT_AUDIO_MASK; 01157 } 01158 01159 01160 /* Start by sending our preferred audio codecs */ 01161 for (x = 0; x < 32; x++) { 01162 int pref_codec; 01163 01164 if (!(pref_codec = ast_codec_pref_index(&p->prefs, x))) 01165 break; 01166 01167 if (!(capability & pref_codec)) 01168 continue; 01169 01170 if (alreadysent & pref_codec) 01171 continue; 01172 01173 add_codec_to_sdp(p, pref_codec, 8000, 01174 &m_audio_next, &m_audio_left, 01175 &a_audio_next, &a_audio_left, 01176 debug, &min_audio_packet_size); 01177 alreadysent |= pref_codec; 01178 } 01179 01180 /* Now send any other common audio and video codecs, and non-codec formats: */ 01181 for (x = 1; x <= (needvideo ? AST_FORMAT_MAX_VIDEO : AST_FORMAT_MAX_AUDIO); x <<= 1) { 01182 if (!(capability & x)) /* Codec not requested */ 01183 continue; 01184 01185 if (alreadysent & x) /* Already added to SDP */ 01186 continue; 01187 01188 if (x <= AST_FORMAT_MAX_AUDIO) 01189 add_codec_to_sdp(p, x, 8000, 01190 &m_audio_next, &m_audio_left, 01191 &a_audio_next, &a_audio_left, 01192 debug, &min_audio_packet_size); 01193 else 01194 add_codec_to_sdp(p, x, 90000, 01195 &m_video_next, &m_video_left, 01196 &a_video_next, &a_video_left, 01197 debug, &min_video_packet_size); 01198 } 01199 01200 /* Now add DTMF RFC2833 telephony-event as a codec */ 01201 for (x = 1; x <= AST_RTP_MAX; x <<= 1) { 01202 if (!(p->jointnoncodeccapability & x)) 01203 continue; 01204 01205 add_noncodec_to_sdp(p, x, 8000, 01206 &m_audio_next, &m_audio_left, 01207 &a_audio_next, &a_audio_left, 01208 debug); 01209 } 01210 01211 if (option_debug > 2) 01212 ast_log(LOG_DEBUG, "-- Done with adding codecs to SDP\n"); 01213 01214 if(!p->owner || !ast_internal_timing_enabled(p->owner)) 01215 ast_build_string(&a_audio_next, &a_audio_left, "a=silenceSupp:off - - - -\r\n"); 01216 if (min_audio_packet_size) 01217 ast_build_string(&a_audio_next, &a_audio_left, "a=ptime:%d\r\n", min_audio_packet_size); 01218 01219 if (min_video_packet_size) 01220 ast_build_string(&a_video_next, &a_video_left, "a=ptime:%d\r\n", min_video_packet_size); 01221 01222 01223 if ((m_audio_left < 2) || (m_video_left < 2) || (a_audio_left == 0) || (a_video_left == 0)) 01224 ast_log(LOG_WARNING, "SIP SDP may be truncated due to undersized buffer!!\n"); 01225 01226 ast_build_string(&m_audio_next, &m_audio_left, "\r\n"); 01227 if (needvideo) 01228 ast_build_string(&m_video_next, &m_video_left, "\r\n"); 01229 01230 len = strlen(version) + strlen(subject) + strlen(owner) + strlen(connection) + strlen(stime) + strlen(m_audio) + strlen(a_audio) + strlen(hold); 01231 if (needvideo) /* only if video response is appropriate */ 01232 len += strlen(m_video) + strlen(a_video) + strlen(bandwidth) + strlen(hold); 01233 01234 add_header(resp, "Content-Type", "application/sdp"); 01235 add_header_contentLength(resp, len); 01236 add_line(resp, version); 01237 add_line(resp, owner); 01238 add_line(resp, subject); 01239 add_line(resp, connection); 01240 if (needvideo) /* only if video response is appropriate */ 01241 add_line(resp, bandwidth); 01242 add_line(resp, stime); 01243 add_line(resp, m_audio); 01244 add_line(resp, a_audio); 01245 add_line(resp, hold); 01246 if (needvideo) { /* only if video response is appropriate */ 01247 add_line(resp, m_video); 01248 add_line(resp, a_video); 01249 add_line(resp, hold); /* Repeat hold for the video stream */ 01250 } 01251 01252 /* Update lastrtprx when we send our SDP */ 01253 p->lastrtprx = p->lastrtptx = time(NULL); /* XXX why both ? */ 01254 01255 if (option_debug > 2) { 01256 char buf[BUFSIZ]; 01257 ast_log(LOG_DEBUG, "Done building SDP. Settling with this capability: %s\n", ast_getformatname_multiple(buf, BUFSIZ, capability)); 01258 } 01259 01260 return 0; 01261 }
|
|
|
Determine whether a SIP message contains an SDP in its body.
Definition at line 880 of file sip3_sdprtp.c. References ast_strdupa, ast_strlen_zero(), FALSE, get_header(), sip_request::line, sip_request::lines, sip_request::sdp_end, sip_request::sdp_start, strcasestr(), and TRUE. 00881 { 00882 const char *content_type; 00883 const char *search; 00884 char *boundary; 00885 unsigned int x; 00886 00887 content_type = get_header(req, "Content-Type"); 00888 00889 /* if the body contains only SDP, this is easy */ 00890 if (!strcasecmp(content_type, "application/sdp")) { 00891 req->sdp_start = 0; 00892 req->sdp_end = req->lines; 00893 return TRUE; 00894 } 00895 00896 /* if it's not multipart/mixed, there cannot be an SDP */ 00897 if (strncasecmp(content_type, "multipart/mixed", 15)) 00898 return FALSE; 00899 00900 /* if there is no boundary marker, it's invalid */ 00901 if (!(search = strcasestr(content_type, ";boundary="))) 00902 return FALSE; 00903 00904 search += 10; 00905 00906 if (ast_strlen_zero(search)) 00907 return FALSE; 00908 00909 /* make a duplicate of the string, with two extra characters 00910 at the beginning */ 00911 boundary = ast_strdupa(search - 2); 00912 boundary[0] = boundary[1] = '-'; 00913 00914 /* search for the boundary marker, but stop when there are not enough 00915 lines left for it, the Content-Type header and at least one line of 00916 body */ 00917 for (x = 0; x < (req->lines - 2); x++) { 00918 if (!strncasecmp(req->line[x], boundary, strlen(boundary)) && 00919 !strcasecmp(req->line[x + 1], "Content-Type: application/sdp")) { 00920 x += 2; 00921 req->sdp_start = x; 00922 /* search for the end of the body part */ 00923 for ( ; x < req->lines; x++) { 00924 if (!strncasecmp(req->line[x], boundary, strlen(boundary))) 00925 break; 00926 } 00927 req->sdp_end = x; 00928 return TRUE; 00929 } 00930 } 00931 00932 return FALSE; 00933 }
|
|
||||||||||||
|
Get the message body part identified by name=.
Definition at line 166 of file sip3_sdprtp.c. References get_body_by_line(), len, sip_request::line, and sip_request::lines. 00167 { 00168 int x; 00169 int len = strlen(name); 00170 char *r; 00171 00172 for (x = 0; x < req->lines; x++) { 00173 r = get_body_by_line(req->line[x], name, len); 00174 if (r[0] != '\0') 00175 return r; 00176 } 00177 00178 return ""; 00179 }
|
|
||||||||||||||||
|
Reads one line of SIP message body.
Definition at line 132 of file sip3_sdprtp.c. 00133 { 00134 if (strncasecmp(line, name, nameLen) == 0 && line[nameLen] == '=') 00135 return ast_skip_blanks(line + nameLen + 1); 00136 00137 return ""; 00138 }
|
|
||||||||||||||||||||||||||||
|
Set all IP media addresses for this call.
Definition at line 999 of file sip3_sdprtp.c. References ast_rtp_get_us(), sip_dialog::ourip, sip_dialog::redirip, sip_dialog::rtp, sip_dialog::vredirip, and sip_dialog::vrtp. 01000 { 01001 /* First, get our address */ 01002 ast_rtp_get_us(p->rtp, sin); 01003 if (p->vrtp) 01004 ast_rtp_get_us(p->vrtp, vsin); 01005 01006 /* Now, try to figure out where we want them to send data */ 01007 /* Is this a re-invite to move the media out, then use the original offer from caller */ 01008 if (p->redirip.sin_addr.s_addr) { /* If we have a redirection IP, use it */ 01009 dest->sin_port = p->redirip.sin_port; 01010 dest->sin_addr = p->redirip.sin_addr; 01011 } else { 01012 dest->sin_addr = p->ourip; 01013 dest->sin_port = sin->sin_port; 01014 } 01015 if (needvideo) { 01016 /* Determine video destination */ 01017 if (p->vredirip.sin_addr.s_addr) { 01018 vdest->sin_addr = p->vredirip.sin_addr; 01019 vdest->sin_port = p->vredirip.sin_port; 01020 } else { 01021 vdest->sin_addr = p->ourip; 01022 vdest->sin_port = vsin->sin_port; 01023 } 01024 } 01025 01026 }
|
|
||||||||||||
|
Get a line from an SDP message body.
Definition at line 158 of file sip3_sdprtp.c. References get_sdp_iterate(). 00159 { 00160 int dummy = 0; 00161 00162 return get_sdp_iterate(&dummy, req, name); 00163 }
|
|
||||||||||||||||
|
Lookup 'name' in the SDP starting at the 'start' line. Returns the matching line, and 'start' is updated with the next line number.
Definition at line 144 of file sip3_sdprtp.c. References get_body_by_line(), len, and sip_request::line. 00145 { 00146 int len = strlen(name); 00147 00148 while (*start < req->sdp_end) { 00149 const char *r = get_body_by_line(req->line[(*start)++], name, len); 00150 if (r[0] != '\0') 00151 return r; 00152 } 00153 00154 return ""; 00155 }
|
|
||||||||||||
|
Process SIP SDP offer, select formats and activate RTP channels If offer is rejected, we will not change any properties of the call. The SDP contains
< RTP Audio port number < RTP Video port number < media socket address < Video socket address < RTP Audio host IP < RTP video host IP Definition at line 189 of file sip3_sdprtp.c. References ast_clear_flag, ast_gethostbyname(), ast_log(), ast_rtp_alloc_size(), ast_rtp_new_init(), ast_rtp_pt_clear(), ast_rtp_set_m_type(), ast_set_flag, ast_strlen_zero(), ast_verbose(), debug(), FALSE, sip_dialog::flags, get_sdp(), get_sdp_iterate(), hp, sip_dialog::lastrtprx, sip_dialog::lastrtptx, len, LOG_ERROR, LOG_WARNING, sip_dialog::rtp, s, sip_request::sdp_start, sip_debug_test_pvt(), SIP_NOVIDEO, and TRUE. 00190 { 00191 const char *m; /* SDP media offer */ 00192 const char *c; 00193 const char *a; 00194 char host[258]; 00195 int len = -1; 00196 int portno = -1; /*!< RTP Audio port number */ 00197 int vportno = -1; /*!< RTP Video port number */ 00198 int udptlportno = -1; 00199 int peert38capability = 0; 00200 char s[256]; 00201 int old = 0; 00202 00203 /* Peer capability is the capability in the SDP, non codec is RFC2833 DTMF (101) */ 00204 int peercapability = 0, peernoncodeccapability = 0; 00205 int vpeercapability = 0, vpeernoncodeccapability = 0; 00206 struct sockaddr_in sin; /*!< media socket address */ 00207 struct sockaddr_in vsin; /*!< Video socket address */ 00208 00209 const char *codecs; 00210 struct hostent *hp; /*!< RTP Audio host IP */ 00211 struct hostent *vhp = NULL; /*!< RTP video host IP */ 00212 struct ast_hostent audiohp; 00213 struct ast_hostent videohp; 00214 int codec; 00215 int destiterator = 0; 00216 int iterator; 00217 int sendonly = 0; 00218 int numberofports; 00219 struct ast_channel *bridgepeer = NULL; 00220 struct ast_rtp *newaudiortp, *newvideortp; /* Buffers for codec handling */ 00221 int newjointcapability; /* Negotiated capability */ 00222 int newpeercapability; 00223 int newnoncodeccapability; 00224 int numberofmediastreams = 0; 00225 int debug = sip_debug_test_pvt(p); 00226 00227 int found_rtpmap_codecs[32]; 00228 int last_rtpmap_codec=0; 00229 00230 if (!p->rtp) { 00231 ast_log(LOG_ERROR, "Got SDP but have no RTP session allocated.\n"); 00232 return -1; 00233 } 00234 00235 /* Initialize the temporary RTP structures we use to evaluate the offer from the peer */ 00236 newaudiortp = alloca(ast_rtp_alloc_size()); 00237 memset(newaudiortp, 0, ast_rtp_alloc_size()); 00238 ast_rtp_new_init(newaudiortp); 00239 ast_rtp_pt_clear(newaudiortp); 00240 00241 newvideortp = alloca(ast_rtp_alloc_size()); 00242 memset(newvideortp, 0, ast_rtp_alloc_size()); 00243 ast_rtp_new_init(newvideortp); 00244 ast_rtp_pt_clear(newvideortp); 00245 00246 /* Update our last rtprx when we receive an SDP, too */ 00247 p->lastrtprx = p->lastrtptx = time(NULL); /* XXX why both ? */ 00248 00249 00250 /* Try to find first media stream */ 00251 m = get_sdp(req, "m"); 00252 destiterator = req->sdp_start; 00253 c = get_sdp_iterate(&destiterator, req, "c"); 00254 if (ast_strlen_zero(m) || ast_strlen_zero(c)) { 00255 ast_log(LOG_WARNING, "Insufficient information for SDP (m = '%s', c = '%s')\n", m, c); 00256 return -1; 00257 } 00258 00259 /* Check for IPv4 address (not IPv6 yet) */ 00260 if (sscanf(c, "IN IP4 %256s", host) != 1) { 00261 ast_log(LOG_WARNING, "Invalid host in c= line, '%s'\n", c); 00262 return -1; 00263 } 00264 00265 /* XXX This could block for a long time, and block the main thread! XXX */ 00266 hp = ast_gethostbyname(host, &audiohp); 00267 if (!hp) { 00268 ast_log(LOG_WARNING, "Unable to lookup host in c= line, '%s'\n", c); 00269 return -1; 00270 } 00271 vhp = hp; /* Copy to video address as default too */ 00272 00273 iterator = req->sdp_start; 00274 ast_set_flag(&p->flags[0], SIP_NOVIDEO); 00275 00276 00277 /* Find media streams in this SDP offer */ 00278 while ((m = get_sdp_iterate(&iterator, req, "m"))[0] != '\0') { 00279 int x; 00280 int audio = FALSE; 00281 00282 numberofports = 1; 00283 if ((sscanf(m, "audio %d/%d RTP/AVP %n", &x, &numberofports, &len) == 2) || 00284 (sscanf(m, "audio %d RTP/AVP %n", &x, &len) == 1)) { 00285 audio = TRUE; 00286 numberofmediastreams++; 00287 /* Found audio stream in this media definition */ 00288 portno = x; 00289 /* Scan through the RTP payload types specified in a "m=" line: */ 00290 for (codecs = m + len; !ast_strlen_zero(codecs); codecs = ast_skip_blanks(codecs + len)) { 00291 if (sscanf(codecs, "%d%n", &codec, &len) != 1) { 00292 ast_log(LOG_WARNING, "Error in codec string '%s'\n", codecs); 00293 return -1; 00294 } 00295 if (debug) 00296 ast_verbose("Found RTP audio format %d\n", codec); 00297 ast_rtp_set_m_type(newaudiortp, codec); 00298 } 00299 } else if ((sscanf(m, "video %d/%d RTP/AVP %n", &x, &numberofports, &len) == 2) || 00300 (sscanf(m, "video %d RTP/AVP %n", &x, &len) == 1)) { 00301 /* If it is not audio - is it video ? */ 00302 ast_clear_flag(&p->flags[0], SIP_NOVIDEO); 00303 numberofmediastreams++; 00304 vportno = x; 00305 /* Scan through the RTP payload types specified in a "m=" line: */ 00306 for (codecs = m + len; !ast_strlen_zero(codecs); codecs = ast_skip_blanks(codecs + len)) { 00307 if (sscanf(codecs, "%d%n", &codec, &len) != 1) { 00308 ast_log(LOG_WARNING, "Error in codec string '%s'\n", codecs); 00309 return -1; 00310 } 00311 if (debug) 00312 ast_verbose("Found RTP video format %d\n", codec); 00313 ast_rtp_set_m_type(newvideortp, codec); 00314 } 00315 } else if (p->udptl && ((sscanf(m, "image %d udptl t38%n", &x, &len) == 1))) { 00316 if (debug) 00317 ast_verbose("Got T.38 offer in SDP in dialog %s\n", p->callid); 00318 udptlportno = x; 00319 numberofmediastreams++; 00320 00321 if (p->owner && p->lastinvite) { 00322 p->t38.state = T38_PEER_REINVITE; /* T38 Offered in re-invite from remote party */ 00323 if (option_debug > 1) 00324 ast_log(LOG_DEBUG, "T38 state changed to %d on channel %s\n", p->t38.state, p->owner ? p->owner->name : "<none>" ); 00325 } else { 00326 p->t38.state = T38_PEER_DIRECT; /* T38 Offered directly from peer in first invite */ 00327 if (option_debug > 1) 00328 ast_log(LOG_DEBUG, "T38 state changed to %d on channel %s\n", p->t38.state, p->owner ? p->owner->name : "<none>"); 00329 } 00330 } else 00331 ast_log(LOG_WARNING, "Unsupported SDP media type in offer: %s\n", m); 00332 if (numberofports > 1) 00333 ast_log(LOG_WARNING, "SDP offered %d ports for media, not supported by Asterisk. Will try anyway...\n", numberofports); 00334 00335 00336 /* Check for Media-description-level-address for audio */ 00337 c = get_sdp_iterate(&destiterator, req, "c"); 00338 if (!ast_strlen_zero(c)) { 00339 if (sscanf(c, "IN IP4 %256s", host) != 1) { 00340 ast_log(LOG_WARNING, "Invalid secondary host in c= line, '%s'\n", c); 00341 } else { 00342 /* XXX This could block for a long time, and block the main thread! XXX */ 00343 if (audio) { 00344 if ( !(hp = ast_gethostbyname(host, &audiohp))) 00345 ast_log(LOG_WARNING, "Unable to lookup RTP Audio host in secondary c= line, '%s'\n", c); 00346 } else if (!(vhp = ast_gethostbyname(host, &videohp))) 00347 ast_log(LOG_WARNING, "Unable to lookup RTP video host in secondary c= line, '%s'\n", c); 00348 } 00349 00350 } 00351 } 00352 if (portno == -1 && vportno == -1 && udptlportno == -1) 00353 /* No acceptable offer found in SDP - we have no ports */ 00354 /* Do not change RTP or VRTP if this is a re-invite */ 00355 return -2; 00356 00357 if (numberofmediastreams > 2) 00358 /* We have too many fax, audio and/or video media streams, fail this offer */ 00359 return -3; 00360 00361 /* RTP addresses and ports for audio and video */ 00362 sin.sin_family = AF_INET; 00363 vsin.sin_family = AF_INET; 00364 memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr)); 00365 if (vhp) 00366 memcpy(&vsin.sin_addr, vhp->h_addr, sizeof(vsin.sin_addr)); 00367 00368 /* Setup UDPTL port number */ 00369 if (p->udptl) { 00370 if (udptlportno > 0) { 00371 sin.sin_port = htons(udptlportno); 00372 ast_udptl_set_peer(p->udptl, &sin); 00373 if (debug) 00374 ast_log(LOG_DEBUG,"Peer T.38 UDPTL is at port %s:%d\n",ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); 00375 } else { 00376 ast_udptl_stop(p->udptl); 00377 if (debug) 00378 ast_log(LOG_DEBUG, "Peer doesn't provide T.38 UDPTL\n"); 00379 } 00380 } 00381 if (p->rtp) { 00382 if (portno > 0) { 00383 sin.sin_port = htons(portno); 00384 if (!sin.sin_port) 00385 ast_log(LOG_WARNING, "No SIN structure here, will crash\n"); 00386 ast_rtp_set_peer(p->rtp, &sin); 00387 if (debug) 00388 ast_verbose("Peer audio RTP is at port %s:%d\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); 00389 } else { 00390 if (udptlportno > 0) { 00391 if (debug) 00392 ast_log(LOG_DEBUG, "Got T.38 re-invite without audio. Keeping RTP active during T.38 session. Call-Id %s\n", p->callid); 00393 } else 00394 ast_rtp_stop(p->rtp); 00395 if (debug) 00396 ast_verbose("Peer doesn't provide audio: Call-Id: %s\n", p->callid); 00397 } 00398 } 00399 /* Setup video port number */ 00400 if (vportno != -1) 00401 vsin.sin_port = htons(vportno); 00402 00403 00404 /* Next, scan through each "a=rtpmap:" line, noting each 00405 * specified RTP payload type (with corresponding MIME subtype): 00406 */ 00407 /* XXX This needs to be done per media stream, since it's media stream specific */ 00408 iterator = req->sdp_start; 00409 while ((a = get_sdp_iterate(&iterator, req, "a"))[0] != '\0') { 00410 char* mimeSubtype = ast_strdupa(a); /* ensures we have enough space */ 00411 if (option_debug > 1) { 00412 int breakout = FALSE; 00413 00414 /* If we're debugging, check for unsupported sdp options */ 00415 if (!strncasecmp(a, "rtcp:", (size_t) 5)) { 00416 if (debug) 00417 ast_verbose("Got unsupported a:rtcp in SDP offer \n"); 00418 breakout = TRUE; 00419 } else if (!strncasecmp(a, "fmtp:", (size_t) 5)) { 00420 /* Format parameters: Not supported */ 00421 /* Note: This is used for codec parameters, like bitrate for 00422 G722 and video formats for H263 and H264 00423 See RFC2327 for an example */ 00424 if (debug) 00425 ast_verbose("Got unsupported a:fmtp in SDP offer \n"); 00426 breakout = TRUE; 00427 } else if (!strncasecmp(a, "framerate:", (size_t) 10)) { 00428 /* Video stuff: Not supported */ 00429 if (debug) 00430 ast_verbose("Got unsupported a:framerate in SDP offer \n"); 00431 breakout = TRUE; 00432 } else if (!strncasecmp(a, "maxprate:", (size_t) 9)) { 00433 /* Video stuff: Not supported */ 00434 if (debug) 00435 ast_verbose("Got unsupported a:maxprate in SDP offer \n"); 00436 breakout = TRUE; 00437 } else if (!strncasecmp(a, "crypto:", (size_t) 7)) { 00438 /* SRTP stuff, not yet supported */ 00439 if (debug) 00440 ast_verbose("Got unsupported a:crypto in SDP offer \n"); 00441 breakout = TRUE; 00442 } 00443 if (breakout) /* We have a match, skip to next header */ 00444 continue; 00445 } 00446 if (!strcasecmp(a, "sendonly")) { 00447 sendonly = 1; 00448 continue; 00449 } else if (!strcasecmp(a, "inactive")) { 00450 sendonly = 2; 00451 continue; 00452 } else if (!strcasecmp(a, "sendrecv")) { 00453 sendonly = 0; 00454 continue; 00455 } else if (strlen(a) > 5 && !strncasecmp(a, "ptime", 5)) { 00456 char *tmp = strrchr(a, ':'); 00457 long int framing = 0; 00458 if (tmp) { 00459 tmp++; 00460 framing = strtol(tmp, NULL, 10); 00461 if (framing == LONG_MIN || framing == LONG_MAX) { 00462 framing = 0; 00463 if (option_debug) 00464 ast_log(LOG_DEBUG, "Can't read framing from SDP: %s\n", a); 00465 } 00466 } 00467 if (framing && last_rtpmap_codec) { 00468 if (p->autoframing || global.autoframing) { 00469 struct ast_codec_pref *pref = ast_rtp_codec_getpref(p->rtp); 00470 int codec_n; 00471 int format = 0; 00472 for (codec_n = 0; codec_n < last_rtpmap_codec; codec_n++) { 00473 format = ast_rtp_codec_getformat(found_rtpmap_codecs[codec_n]); 00474 if (!format) /* non-codec or not found */ 00475 continue; 00476 if (option_debug) 00477 ast_log(LOG_DEBUG, "Setting framing for %d to %ld\n", format, framing); 00478 ast_codec_pref_setsize(pref, format, framing); 00479 } 00480 ast_rtp_codec_setpref(p->rtp, pref); 00481 } 00482 } 00483 memset(&found_rtpmap_codecs, 0, sizeof(found_rtpmap_codecs)); 00484 last_rtpmap_codec = 0; 00485 continue; 00486 } else if (sscanf(a, "rtpmap: %u %[^/]/", &codec, mimeSubtype) == 2) { 00487 /* We have a rtpmap to handle */ 00488 if (debug) 00489 ast_verbose("Found description format %s for ID %d\n", mimeSubtype, codec); 00490 found_rtpmap_codecs[last_rtpmap_codec] = codec; 00491 last_rtpmap_codec++; 00492 00493 /* Note: should really look at the 'freq' and '#chans' params too */ 00494 ast_rtp_set_rtpmap_type(newaudiortp, codec, "audio", mimeSubtype, 00495 ast_test_flag(&p->flags[0], SIP_G726_NONSTANDARD) ? AST_RTP_OPT_G726_NONSTANDARD : 0); 00496 if (p->vrtp) 00497 ast_rtp_set_rtpmap_type(newvideortp, codec, "video", mimeSubtype, 0); 00498 } 00499 } 00500 00501 if (udptlportno != -1) { 00502 int found = 0, x; 00503 00504 old = 0; 00505 00506 /* Scan trough the a= lines for T38 attributes and set apropriate fileds */ 00507 iterator = req->sdp_start; 00508 while ((a = get_sdp_iterate(&iterator, req, "a"))[0] != '\0') { 00509 if ((sscanf(a, "T38FaxMaxBuffer:%d", &x) == 1)) { 00510 found = 1; 00511 if (option_debug > 2) 00512 ast_log(LOG_DEBUG, "MaxBufferSize:%d\n",x); 00513 } else if ((sscanf(a, "T38MaxBitRate:%d", &x) == 1)) { 00514 found = 1; 00515 if (option_debug > 2) 00516 ast_log(LOG_DEBUG,"T38MaxBitRate: %d\n",x); 00517 switch (x) { 00518 case 14400: 00519 peert38capability |= T38FAX_RATE_14400 | T38FAX_RATE_12000 | T38FAX_RATE_9600 | T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400; 00520 break; 00521 case 12000: 00522 peert38capability |= T38FAX_RATE_12000 | T38FAX_RATE_9600 | T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400; 00523 break; 00524 case 9600: 00525 peert38capability |= T38FAX_RATE_9600 | T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400; 00526 break; 00527 case 7200: 00528 peert38capability |= T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400; 00529 break; 00530 case 4800: 00531 peert38capability |= T38FAX_RATE_4800 | T38FAX_RATE_2400; 00532 break; 00533 case 2400: 00534 peert38capability |= T38FAX_RATE_2400; 00535 break; 00536 } 00537 } else if ((sscanf(a, "T38FaxVersion:%d", &x) == 1)) { 00538 found = 1; 00539 if (option_debug > 2) 00540 ast_log(LOG_DEBUG, "FaxVersion: %d\n",x); 00541 if (x == 0) 00542 peert38capability |= T38FAX_VERSION_0; 00543 else if (x == 1) 00544 peert38capability |= T38FAX_VERSION_1; 00545 } else if ((sscanf(a, "T38FaxMaxDatagram:%d", &x) == 1)) { 00546 found = 1; 00547 if (option_debug > 2) 00548 ast_log(LOG_DEBUG, "FaxMaxDatagram: %d\n",x); 00549 ast_udptl_set_far_max_datagram(p->udptl, x); 00550 ast_udptl_set_local_max_datagram(p->udptl, x); 00551 } else if ((sscanf(a, "T38FaxFillBitRemoval:%d", &x) == 1)) { 00552 found = 1; 00553 if (option_debug > 2) 00554 ast_log(LOG_DEBUG, "FillBitRemoval: %d\n",x); 00555 if (x == 1) 00556 peert38capability |= T38FAX_FILL_BIT_REMOVAL; 00557 } else if ((sscanf(a, "T38FaxTranscodingMMR:%d", &x) == 1)) { 00558 found = 1; 00559 if (option_debug > 2) 00560 ast_log(LOG_DEBUG, "Transcoding MMR: %d\n",x); 00561 if (x == 1) 00562 peert38capability |= T38FAX_TRANSCODING_MMR; 00563 } 00564 if ((sscanf(a, "T38FaxTranscodingJBIG:%d", &x) == 1)) { 00565 found = 1; 00566 if (option_debug > 2) 00567 ast_log(LOG_DEBUG, "Transcoding JBIG: %d\n",x); 00568 if (x == 1) 00569 peert38capability |= T38FAX_TRANSCODING_JBIG; 00570 } else if ((sscanf(a, "T38FaxRateManagement:%s", s) == 1)) { 00571 found = 1; 00572 if (option_debug > 2) 00573 ast_log(LOG_DEBUG, "RateMangement: %s\n", s); 00574 if (!strcasecmp(s, "localTCF")) 00575 peert38capability |= T38FAX_RATE_MANAGEMENT_LOCAL_TCF; 00576 else if (!strcasecmp(s, "transferredTCF")) 00577 peert38capability |= T38FAX_RATE_MANAGEMENT_TRANSFERED_TCF; 00578 } else if ((sscanf(a, "T38FaxUdpEC:%s", s) == 1)) { 00579 found = 1; 00580 if (option_debug > 2) 00581 ast_log(LOG_DEBUG, "UDP EC: %s\n", s); 00582 if (!strcasecmp(s, "t38UDPRedundancy")) { 00583 peert38capability |= T38FAX_UDP_EC_REDUNDANCY; 00584 ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_REDUNDANCY); 00585 } else if (!strcasecmp(s, "t38UDPFEC")) { 00586 peert38capability |= T38FAX_UDP_EC_FEC; 00587 ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_FEC); 00588 } else { 00589 peert38capability |= T38FAX_UDP_EC_NONE; 00590 ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_NONE); 00591 } 00592 } 00593 } 00594 if (found) { /* Some cisco equipment returns nothing beside c= and m= lines in 200 OK T38 SDP */ 00595 p->t38.peercapability = peert38capability; 00596 p->t38.jointcapability = (peert38capability & 255); /* Put everything beside supported speeds settings */ 00597 peert38capability &= (T38FAX_RATE_14400 | T38FAX_RATE_12000 | T38FAX_RATE_9600 | T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400); 00598 p->t38.jointcapability |= (peert38capability & p->t38.capability); /* Put the lower of our's and peer's speed */ 00599 } 00600 if (debug) 00601 ast_log(LOG_DEBUG, "Our T38 capability = (%d), peer T38 capability (%d), joint T38 capability (%d)\n", 00602 p->t38.capability, 00603 p->t38.peercapability, 00604 p->t38.jointcapability); 00605 } else { 00606 p->t38.state = T38_DISABLED; 00607 if (option_debug > 2) 00608 ast_log(LOG_DEBUG, "T38 state changed to %d on channel %s\n", p->t38.state, p->owner ? p->owner->name : "<none>"); 00609 } 00610 00611 /* Now gather all of the codecs that we are asked for: */ 00612 ast_rtp_get_current_formats(newaudiortp, &peercapability, &peernoncodeccapability); 00613 ast_rtp_get_current_formats(newvideortp, &vpeercapability, &vpeernoncodeccapability); 00614 00615 newjointcapability = p->capability & (peercapability | vpeercapability); 00616 newpeercapability = (peercapability | vpeercapability); 00617 newnoncodeccapability = p->noncodeccapability & peernoncodeccapability; 00618 00619 00620 if (debug) { 00621 /* shame on whoever coded this.... */ 00622 char s1[BUFSIZ], s2[BUFSIZ], s3[BUFSIZ], s4[BUFSIZ]; 00623 00624 ast_verbose("Capabilities: us - %s, peer - audio=%s/video=%s, combined - %s\n", 00625 ast_getformatname_multiple(s1, BUFSIZ, p->capability), 00626 ast_getformatname_multiple(s2, BUFSIZ, newpeercapability), 00627 ast_getformatname_multiple(s3, BUFSIZ, vpeercapability), 00628 ast_getformatname_multiple(s4, BUFSIZ, newjointcapability)); 00629 00630 ast_verbose("Non-codec capabilities (dtmf): us - %s, peer - %s, combined - %s\n", 00631 ast_rtp_lookup_mime_multiple(s1, BUFSIZ, p->noncodeccapability, 0, 0), 00632 ast_rtp_lookup_mime_multiple(s2, BUFSIZ, peernoncodeccapability, 0, 0), 00633 ast_rtp_lookup_mime_multiple(s3, BUFSIZ, newnoncodeccapability, 0, 0)); 00634 } 00635 if (!newjointcapability) { 00636 /* If T.38 was not negotiated either, totally bail out... */ 00637 if (!p->t38.jointcapability) { 00638 ast_log(LOG_NOTICE, "No compatible codecs, not accepting this offer!\n"); 00639 /* Do NOT Change current setting */ 00640 return -1; 00641 } else { 00642 if (option_debug > 2) 00643 ast_log(LOG_DEBUG, "Have T.38 but no audio codecs, accepting offer anyway\n"); 00644 return 0; 00645 } 00646 } 00647 00648 /* We are now ready to change the sip session and p->rtp and p->vrtp with the offered codecs, since 00649 they are acceptable */ 00650 p->jointcapability = newjointcapability; /* Our joint codec profile for this call */ 00651 p->peercapability = newpeercapability; /* The other sides capability in latest offer */ 00652 p->jointnoncodeccapability = newnoncodeccapability; /* DTMF capabilities */ 00653 00654 ast_rtp_pt_copy(p->rtp, newaudiortp); 00655 if (p->vrtp) 00656 ast_rtp_pt_copy(p->vrtp, newvideortp); 00657 00658 if (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO) { 00659 ast_clear_flag(&p->flags[0], SIP_DTMF); 00660 if (newnoncodeccapability & AST_RTP_DTMF) { 00661 /* XXX Would it be reasonable to drop the DSP at this point? XXX */ 00662 ast_set_flag(&p->flags[0], SIP_DTMF_RFC2833); 00663 } else { 00664 ast_set_flag(&p->flags[0], SIP_DTMF_INBAND); 00665 } 00666 } 00667 00668 /* Setup audio port number */ 00669 if (p->rtp && sin.sin_port) { 00670 ast_rtp_set_peer(p->rtp, &sin); 00671 if (debug) 00672 ast_verbose("Peer audio RTP is at port %s:%d\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); 00673 } 00674 00675 /* Setup video port number */ 00676 if (p->vrtp && vsin.sin_port) { 00677 ast_rtp_set_peer(p->vrtp, &vsin); 00678 if (debug) 00679 ast_verbose("Peer video RTP is at port %s:%d\n", ast_inet_ntoa(vsin.sin_addr), ntohs(vsin.sin_port)); 00680 } 00681 00682 /* Ok, we're going with this offer */ 00683 if (option_debug > 1) { 00684 char buf[BUFSIZ]; 00685 ast_log(LOG_DEBUG, "We're settling with these formats: %s\n", ast_getformatname_multiple(buf, BUFSIZ, p->jointcapability)); 00686 } 00687 00688 if (!p->owner) /* There's no open channel owning us so we can return here. For a re-invite or so, we proceed */ 00689 return 0; 00690 00691 if (option_debug > 3) 00692 ast_log(LOG_DEBUG, "We have an owner, now see if we need to change this call\n"); 00693 00694 if (!(p->owner->nativeformats & p->jointcapability & AST_FORMAT_AUDIO_MASK) && (p->jointcapability & AST_FORMAT_AUDIO_MASK)) { 00695 if (debug) { 00696 char s1[BUFSIZ], s2[BUFSIZ]; 00697 ast_log(LOG_DEBUG, "Oooh, we need to change our audio formats since our peer supports only %s and not %s\n", 00698 ast_getformatname_multiple(s1, BUFSIZ, p->jointcapability), 00699 ast_getformatname_multiple(s2, BUFSIZ, p->owner->nativeformats)); 00700 } 00701 p->owner->nativeformats = ast_codec_choose(&p->prefs, p->jointcapability, 1) | (p->capability & vpeercapability); 00702 ast_set_read_format(p->owner, p->owner->readformat); 00703 ast_set_write_format(p->owner, p->owner->writeformat); 00704 } 00705 00706 /* Turn on/off music on hold if we are holding/unholding */ 00707 if ((bridgepeer = ast_bridged_channel(p->owner))) { 00708 if (sin.sin_addr.s_addr && !sendonly) { 00709 ast_queue_control(p->owner, AST_CONTROL_UNHOLD); 00710 /* Activate a re-invite */ 00711 ast_queue_frame(p->owner, &ast_null_frame); 00712 } else if (!sin.sin_addr.s_addr || sendonly) { 00713 ast_queue_control_data(p->owner, AST_CONTROL_HOLD, 00714 S_OR(p->mohsuggest, NULL), 00715 !ast_strlen_zero(p->mohsuggest) ? strlen(p->mohsuggest) + 1 : 0); 00716 if (sendonly) 00717 ast_rtp_stop(p->rtp); 00718 /* RTCP needs to go ahead, even if we're on hold!!! */ 00719 /* Activate a re-invite */ 00720 ast_queue_frame(p->owner, &ast_null_frame); 00721 } 00722 } 00723 00724 /* Manager Hold and Unhold events must be generated, if necessary */ 00725 if (sin.sin_addr.s_addr && !sendonly) { 00726 if (ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD)) { 00727 append_history(p, "Unhold", "%s", req->data); 00728 if (global.callevents) 00729 manager_event(EVENT_FLAG_CALL, "Unhold", 00730 "Channel: %s\r\n" 00731 "Uniqueid: %s\r\n", 00732 p->owner->name, 00733 p->owner->uniqueid); 00734 sip_peer_hold(p, 0); 00735 } 00736 ast_clear_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD); /* Clear both flags */ 00737 } else if (!sin.sin_addr.s_addr || sendonly ) { 00738 /* No address for RTP, we're on hold */ 00739 append_history(p, "Hold", "%s", req->data); 00740 00741 if (global.callevents && !ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD)) { 00742 manager_event(EVENT_FLAG_CALL, "Hold", 00743 "Channel: %s\r\n" 00744 "Uniqueid: %s\r\n", 00745 p->owner->name, 00746 p->owner->uniqueid); 00747 } 00748 if (sendonly == 1) /* One directional hold (sendonly/recvonly) */ 00749 ast_set_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD_ONEDIR); 00750 else if (sendonly == 2) /* Inactive stream */ 00751 ast_set_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD_INACTIVE); 00752 sip_peer_hold(p, 1); 00753 } 00754 00755 return 0; 00756 }
|
|
|
Register RTP and UDPTL to the subsystems.
Definition at line 111 of file sip3_sdprtp.c. References ast_rtp_proto_register(), ast_udptl_proto_register(), sip_rtp, and sip_udptl. Referenced by load_module(). 00112 { 00113 /* Tell the RTP subdriver that we're here */ 00114 ast_rtp_proto_register(&sip_rtp); 00115 00116 /* Tell the UDPTL subdriver that we're here */ 00117 ast_udptl_proto_register(&sip_udptl); 00118 }
|
|
|
Return SIP UA's codec (part of the RTP interface).
Definition at line 1264 of file sip3_sdprtp.c. References sip_dialog::peercapability, and ast_channel::tech_pvt. 01265 { 01266 struct sip_dialog *p = chan->tech_pvt; 01267 return p->peercapability; 01268 }
|
|
||||||||||||
|
|
Get UDPTL peer address (part of UDPTL interface).
Definition at line 1370 of file sip3_sdprtp.c. References dialog_lock(), FALSE, ast_channel::tech_pvt, TRUE, and sip_dialog::udptl. 01371 { 01372 struct sip_dialog *p; 01373 struct ast_udptl *udptl = NULL; 01374 01375 p = chan->tech_pvt; 01376 if (!p || !(p->udptl)) 01377 return NULL; 01378 01379 dialog_lock(p, TRUE); 01380 /* T38 reinvites is something very different from media re-invites to optimize 01381 the media stream. They should not rely on the CAN_REINVITE setting 01382 if (ast_test_flag(&p->flags[0], SIP_CAN_REINVITE)) */ 01383 udptl = p->udptl; 01384 dialog_lock(p, FALSE); 01385 return udptl; 01386 }
|
|
||||||||||||
|
Returns null if we can't reinvite video (part of RTP interface).
Definition at line 850 of file sip3_sdprtp.c. References ast_mutex_lock(), ast_mutex_unlock(), AST_RTP_GET_FAILED, AST_RTP_TRY_NATIVE, AST_RTP_TRY_PARTIAL, ast_test_flag, sip_dialog::flags, sip_dialog::lock, sip_dialog::rtp, SIP_CAN_REINVITE, ast_channel::tech_pvt, and sip_dialog::vrtp. 00851 { 00852 struct sip_dialog *p = NULL; 00853 enum ast_rtp_get_result res = AST_RTP_TRY_PARTIAL; 00854 00855 if (!(p = chan->tech_pvt) || !(p->vrtp)) 00856 return AST_RTP_GET_FAILED; 00857 00858 ast_mutex_lock(&p->lock); 00859 00860 *rtp = p->vrtp; 00861 00862 if (ast_test_flag(&p->flags[0], SIP_CAN_REINVITE)) 00863 res = AST_RTP_TRY_NATIVE; 00864 00865 /* The jitterbuffer is not implemented for video */ 00866 00867 ast_mutex_unlock(&p->lock); 00868 00869 return res; 00870 }
|
|
|
Read SIP RTP from channel.
Definition at line 1333 of file sip3_sdprtp.c. References ast_bridged_channel(), ast_log(), ast_set_flag, ast_test_flag, dialog_lock(), FALSE, sip_dialog::flags, sip_dialog::lastrtprx, option_debug, sip_dialog::pendinginvite, SIP_GOTREFER, SIP_NEEDREINVITE, SIP_PAGE2_T38SUPPORT_UDPTL, SIP_PENDINGBYE, sip_rtp_read(), t38properties::state, sip_dialog::t38, T38_DISABLED, T38_LOCAL_REINVITE, t38properties::t38support, ast_channel::tech_pvt, transmit_reinvite_with_sdp(), and TRUE. 01334 { 01335 struct ast_frame *fr; 01336 struct sip_dialog *p = ast->tech_pvt; 01337 int faxdetected = FALSE; 01338 01339 dialog_lock(p, TRUE); 01340 fr = sip_rtp_read(ast, p, &faxdetected); 01341 p->lastrtprx = time(NULL); 01342 01343 /* If we are NOT bridged to another channel, and we have detected fax tone we issue T38 re-invite to a peer */ 01344 /* If we are bridged then it is the responsibility of the SIP device to issue T38 re-invite if it detects CNG or fax preamble */ 01345 if (faxdetected && ast_test_flag(&p->t38.t38support, SIP_PAGE2_T38SUPPORT_UDPTL) && (p->t38.state == T38_DISABLED) && !(ast_bridged_channel(ast))) { 01346 if (!ast_test_flag(&p->flags[0], SIP_GOTREFER)) { 01347 if (!p->pendinginvite) { 01348 if (option_debug > 2) 01349 ast_log(LOG_DEBUG, "Sending reinvite on SIP (%s) for T.38 negotiation.\n",ast->name); 01350 p->t38.state = T38_LOCAL_REINVITE; 01351 transmit_reinvite_with_sdp(p, TRUE); 01352 if (option_debug > 1) 01353 ast_log(LOG_DEBUG, "T38 state changed to %d on channel %s\n", p->t38.state, ast->name); 01354 } 01355 } else if (!ast_test_flag(&p->flags[0], SIP_PENDINGBYE)) { 01356 if (option_debug > 2) 01357 ast_log(LOG_DEBUG, "Deferring reinvite on SIP (%s) - it will be re-negotiated for T.38\n", ast->name); 01358 ast_set_flag(&p->flags[0], SIP_NEEDREINVITE); 01359 } else if (option_debug > 3) { 01360 ast_log(LOG_DEBUG, "Fax deteced but discarded because another transaction is already in progress.\n"); 01361 /* \note Should we turn on a flag and send re-invite for fax at a later stage? */ 01362 } 01363 } 01364 dialog_lock(p, FALSE); 01365 01366 return fr; 01367 }
|
|
||||||||||||||||
|
Read RTP from network.
Definition at line 1271 of file sip3_sdprtp.c. References ast_dsp_process(), AST_FORMAT_AUDIO_MASK, AST_FORMAT_VIDEO_MASK, AST_FRAME_DTMF, AST_FRAME_VOICE, ast_log(), ast_null_frame, ast_rtcp_read(), ast_rtp_read(), ast_set_read_format(), ast_set_write_format(), ast_test_flag, ast_udptl_read(), ast_channel::fdno, sip_dialog::flags, ast_frame::frametype, ast_channel::nativeformats, option_debug, sip_dialog::owner, ast_channel::readformat, sip_dialog::rtp, SIP_DTMF, SIP_DTMF_INBAND, SIP_DTMF_RFC2833, SIP_PAGE2_T38SUPPORT_UDPTL, ast_frame::subclass, sip_dialog::t38, t38properties::t38support, sip_dialog::udptl, sip_dialog::vad, sip_dialog::vrtp, and ast_channel::writeformat. 01272 { 01273 /* Retrieve audio/etc from channel. Assumes p->lock is already held. */ 01274 struct ast_frame *f; 01275 01276 if (!p->rtp) { 01277 /* We have no RTP allocated for this channel */ 01278 return &ast_null_frame; 01279 } 01280 01281 switch(ast->fdno) { 01282 case 0: 01283 f = ast_rtp_read(p->rtp); /* RTP Audio */ 01284 break; 01285 case 1: 01286 f = ast_rtcp_read(p->rtp); /* RTCP Control Channel */ 01287 break; 01288 case 2: 01289 f = ast_rtp_read(p->vrtp); /* RTP Video */ 01290 break; 01291 case 3: 01292 f = ast_rtcp_read(p->vrtp); /* RTCP Control Channel for video */ 01293 break; 01294 case 5: 01295 f = ast_udptl_read(p->udptl); /* UDPTL for T.38 */ 01296 break; 01297 default: 01298 f = &ast_null_frame; 01299 } 01300 /* Don't forward RFC2833 if we're not supposed to */ 01301 if (f && (f->frametype == AST_FRAME_DTMF) && 01302 (ast_test_flag(&p->flags[0], SIP_DTMF) != SIP_DTMF_RFC2833)) 01303 return &ast_null_frame; 01304 01305 if (p->owner) { 01306 /* We already hold the channel lock */ 01307 if (f->frametype == AST_FRAME_VOICE) { 01308 if (f->subclass != (p->owner->nativeformats & AST_FORMAT_AUDIO_MASK)) { 01309 if (option_debug) 01310 ast_log(LOG_DEBUG, "Oooh, format changed to %d\n", f->subclass); 01311 p->owner->nativeformats = (p->owner->nativeformats & AST_FORMAT_VIDEO_MASK) | f->subclass; 01312 ast_set_read_format(p->owner, p->owner->readformat); 01313 ast_set_write_format(p->owner, p->owner->writeformat); 01314 } 01315 if ((ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_INBAND) && p->vad) { 01316 f = ast_dsp_process(p->owner, p->vad, f); 01317 if (f && f->frametype == AST_FRAME_DTMF) { 01318 if (ast_test_flag(&p->t38.t38support, SIP_PAGE2_T38SUPPORT_UDPTL) && f->subclass == 'f') { 01319 if (option_debug) 01320 ast_log(LOG_DEBUG, "Fax CNG detected on %s\n", ast->name); 01321 *faxdetect = 1; 01322 } else if (option_debug) { 01323 ast_log(LOG_DEBUG, "* Detected inband DTMF '%c'\n", f->subclass); 01324 } 01325 } 01326 } 01327 } 01328 } 01329 return f; 01330 }
|
|
||||||||||||||||||||||||
|
Set the RTP peer for this call.
Definition at line 760 of file sip3_sdprtp.c. References ast_channel::_state, append_history, ast_inet_ntoa(), ast_log(), ast_rtp_get_peer(), ast_set_flag, AST_STATE_UP, ast_test_flag, dialog_lock(), FALSE, sip_dialog::flags, sip_dialog::lastrtprx, sip_dialog::lastrtptx, option_debug, sip_dialog::ourip, sip_dialog::pendinginvite, sip_dialog::redircodecs, sip_dialog::redirip, sip_dialog::rtp, SIP_ALREADYGONE, SIP_CAN_REINVITE_NAT, SIP_GOTREFER, SIP_NEEDREINVITE, SIP_NO_HISTORY, SIP_PENDINGBYE, ast_channel::tech_pvt, transmit_reinvite_with_sdp(), TRUE, sip_dialog::vredirip, and sip_dialog::vrtp. 00761 { 00762 struct sip_dialog *p; 00763 int changed = 0; 00764 00765 p = chan->tech_pvt; 00766 if (!p) 00767 return -1; 00768 dialog_lock(p, TRUE); 00769 if (ast_test_flag(&p->flags[0], SIP_ALREADYGONE)) { 00770 /* If we're destroyed, don't bother */ 00771 dialog_lock(p, FALSE); 00772 return 0; 00773 } 00774 00775 /* if this peer cannot handle reinvites of the media stream to devices 00776 that are known to be behind a NAT, then stop the process now 00777 */ 00778 if (nat_active && !ast_test_flag(&p->flags[0], SIP_CAN_REINVITE_NAT)) { 00779 dialog_lock(p, FALSE); 00780 return 0; 00781 } 00782 00783 if (rtp) { 00784 changed |= ast_rtp_get_peer(rtp, &p->redirip); 00785 } else if (p->redirip.sin_addr.s_addr || ntohs(p->redirip.sin_port) != 0) { 00786 memset(&p->redirip, 0, sizeof(p->redirip)); 00787 changed = 1; 00788 } 00789 if (vrtp) { 00790 changed |= ast_rtp_get_peer(vrtp, &p->vredirip); 00791 } else if (p->vredirip.sin_addr.s_addr || ntohs(p->vredirip.sin_port) != 0) { 00792 memset(&p->vredirip, 0, sizeof(p->vredirip)); 00793 changed = 1; 00794 } 00795 if (codecs && (p->redircodecs != codecs)) { 00796 p->redircodecs = codecs; 00797 changed = 1; 00798 } 00799 if (changed && !ast_test_flag(&p->flags[0], SIP_GOTREFER)) { 00800 if (chan->_state != AST_STATE_UP) { /* We are in early state */ 00801 if (!ast_test_flag(&p->flags[0], SIP_NO_HISTORY)) 00802 append_history(p, "ExtInv", "Initial invite sent with remote bridge proposal."); 00803 if (option_debug) 00804 ast_log(LOG_DEBUG, "Early remote bridge setting SIP '%s' - Sending media to %s\n", p->callid, ast_inet_ntoa(rtp ? p->redirip.sin_addr : p->ourip)); 00805 } else if (!p->pendinginvite) { /* We are up, and have no outstanding invite */ 00806 if (option_debug > 2) { 00807 ast_log(LOG_DEBUG, "Sending reinvite on SIP '%s' - It's audio soon redirected to IP %s\n", p->callid, ast_inet_ntoa(rtp ? p->redirip.sin_addr : p->ourip)); 00808 } 00809 transmit_reinvite_with_sdp(p, FALSE); 00810 } else if (!ast_test_flag(&p->flags[0], SIP_PENDINGBYE)) { 00811 if (option_debug > 2) { 00812 ast_log(LOG_DEBUG, "Deferring reinvite on SIP '%s' - It's audio will be redirected to IP %s\n", p->callid, ast_inet_ntoa(rtp ? p->redirip.sin_addr : p->ourip)); 00813 } 00814 /* We have a pending Invite. Send re-invite when we're done with the invite */ 00815 ast_set_flag(&p->flags[0], SIP_NEEDREINVITE); 00816 } 00817 } 00818 /* Reset lastrtprx timer */ 00819 p->lastrtprx = p->lastrtptx = time(NULL); 00820 dialog_lock(p, FALSE); 00821 return 0; 00822 }
|
|
||||||||||||
|
Determine UDPTL peer address for re-invite (part of UDPTL interface).
Definition at line 1389 of file sip3_sdprtp.c. References ast_inet_ntoa(), ast_log(), ast_mutex_lock(), ast_mutex_unlock(), ast_set_flag, ast_test_flag, ast_udptl_get_peer(), sip_dialog::flags, sip_dialog::lastrtprx, sip_dialog::lastrtptx, sip_dialog::lock, option_debug, sip_dialog::ourip, sip_dialog::pendinginvite, SIP_GOTREFER, SIP_NEEDREINVITE, SIP_PENDINGBYE, ast_channel::tech_pvt, transmit_reinvite_with_sdp(), TRUE, sip_dialog::udptl, and sip_dialog::udptlredirip. 01390 { 01391 struct sip_dialog *p; 01392 01393 p = chan->tech_pvt; 01394 if (!p) 01395 return -1; 01396 ast_mutex_lock(&p->lock); 01397 if (udptl) 01398 ast_udptl_get_peer(udptl, &p->udptlredirip); 01399 else 01400 memset(&p->udptlredirip, 0, sizeof(p->udptlredirip)); 01401 if (!ast_test_flag(&p->flags[0], SIP_GOTREFER)) { 01402 if (!p->pendinginvite) { 01403 if (option_debug > 2) 01404 ast_log(LOG_DEBUG, "Sending T38 reinvite on SIP '%s' - It's UDPTL soon redirected to IP %s:%d\n", p->callid, ast_inet_ntoa(udptl ? p->udptlredirip.sin_addr : p->ourip), udptl ? ntohs(p->udptlredirip.sin_port) : 0); 01405 transmit_reinvite_with_sdp(p, TRUE); 01406 } else if (!ast_test_flag(&p->flags[0], SIP_PENDINGBYE)) { 01407 if (option_debug > 2) { 01408 ast_log(LOG_DEBUG, "Deferring reinvite on SIP '%s' - It's UDPTL will be redirected to IP %s:%d\n", p->callid, ast_inet_ntoa(udptl ? p->udptlredirip.sin_addr : p->ourip), udptl ? ntohs(p->udptlredirip.sin_port) : 0); 01409 } 01410 ast_set_flag(&p->flags[0], SIP_NEEDREINVITE); 01411 } 01412 } 01413 /* Reset lastrtprx timer */ 01414 p->lastrtprx = p->lastrtptx = time(NULL); 01415 ast_mutex_unlock(&p->lock); 01416 return 0; 01417 }
|
|
|
Immediately stop RTP, VRTP and UDPTL as applicable.
Definition at line 1421 of file sip3_sdprtp.c. References ast_log(), ast_rtp_stop(), ast_udptl_stop(), option_debug, sip_dialog::rtp, sipdebug, sip_dialog::udptl, and sip_dialog::vrtp. 01422 { 01423 if (option_debug > 4 && sipdebug) 01424 ast_log(LOG_DEBUG, "Stopping media flow for %s\n", dialog->callid); 01425 /* Immediately stop RTP, VRTP and UDPTL as applicable */ 01426 if (dialog->rtp) 01427 ast_rtp_stop(dialog->rtp); 01428 if (dialog->vrtp) 01429 ast_rtp_stop(dialog->vrtp); 01430 if (dialog->udptl) 01431 ast_udptl_stop(dialog->udptl); 01432 }
|
|
|
UNRegister RTP and UDPTL to the subsystems.
Definition at line 121 of file sip3_sdprtp.c. References ast_rtp_proto_unregister(), ast_udptl_proto_unregister(), sip_rtp, and sip_udptl. Referenced by unload_module(). 00122 { 00123 /* Tell the RTP subdriver that we're gone */ 00124 ast_rtp_proto_unregister(&sip_rtp); 00125 00126 /* Tell the UDPTL subdriver that we're gone */ 00127 ast_udptl_proto_unregister(&sip_udptl); 00128 }
|
|
|
Interface structure with callbacks used to connect to RTP module.
Definition at line 95 of file sip3_sdprtp.c. |
|
|
Initial value: {
type: "SIP",
get_udptl_info: sip_get_udptl_peer,
set_udptl_peer: sip_set_udptl_peer,
}
Definition at line 104 of file sip3_sdprtp.c. |