diff options
-rw-r--r-- | CHANGES | 1 | ||||
-rw-r--r-- | CREDITS | 1 | ||||
-rw-r--r-- | channels/chan_gtalk.c | 4 | ||||
-rw-r--r-- | channels/chan_mgcp.c | 2 | ||||
-rw-r--r-- | channels/chan_sip.c | 451 | ||||
-rw-r--r-- | channels/chan_skinny.c | 2 | ||||
-rw-r--r-- | include/asterisk/channel.h | 13 | ||||
-rw-r--r-- | include/asterisk/frame.h | 2 | ||||
-rw-r--r-- | include/asterisk/rtp.h | 4 | ||||
-rw-r--r-- | main/channel.c | 12 | ||||
-rw-r--r-- | main/rtp.c | 118 |
11 files changed, 513 insertions, 97 deletions
@@ -101,3 +101,4 @@ SIP changes "sipregs" for registrations. If it's not defined, "sippeers" will be used for registration data, as before. * The SIPPEER function have new options for port address, call and pickup groups + * Added support for T.140 realtime text in SIP/RTP @@ -137,6 +137,7 @@ Philippe Sultan - RADIUS CDR module INRIA, http://www.inria.fr/ John Martin, Aupix - Improved video support in the SIP channel + T.140 text support in RTP/SIP Steve Underwood - Provided T.38 pass through support. diff --git a/channels/chan_gtalk.c b/channels/chan_gtalk.c index f84d6d896..23beb00e1 100644 --- a/channels/chan_gtalk.c +++ b/channels/chan_gtalk.c @@ -188,7 +188,7 @@ static int gtalk_do_reload(int fd, int argc, char **argv); static int gtalk_show_channels(int fd, int argc, char **argv); /*----- RTP interface functions */ static int gtalk_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, - struct ast_rtp *vrtp, int codecs, int nat_active); + struct ast_rtp *vrtp, struct ast_rtp *trtp, int codecs, int nat_active); static enum ast_rtp_get_result gtalk_get_rtp_peer(struct ast_channel *chan, struct ast_rtp **rtp); static int gtalk_get_codec(struct ast_channel *chan); @@ -540,7 +540,7 @@ static int gtalk_get_codec(struct ast_channel *chan) return p->peercapability; } -static int gtalk_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp, int codecs, int nat_active) +static int gtalk_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp, struct ast_rtp *trtp, int codecs, int nat_active) { struct gtalk_pvt *p; diff --git a/channels/chan_mgcp.c b/channels/chan_mgcp.c index 69a0cc03f..acabdc7e4 100644 --- a/channels/chan_mgcp.c +++ b/channels/chan_mgcp.c @@ -3960,7 +3960,7 @@ static enum ast_rtp_get_result mgcp_get_rtp_peer(struct ast_channel *chan, struc return AST_RTP_TRY_PARTIAL; } -static int mgcp_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp, int codecs, int nat_active) +static int mgcp_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp, struct ast_rtp *trtp, int codecs, int nat_active) { /* XXX Is there such thing as video support with MGCP? XXX */ struct mgcp_subchannel *sub; diff --git a/channels/chan_sip.c b/channels/chan_sip.c index 20a14f0fb..f61ea3a4f 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -510,6 +510,7 @@ static const struct cfsip_options { #define DEFAULT_TOS_SIP 0 /*!< Call signalling packets should be marked as DSCP CS3, but the default is 0 to be compatible with previous versions. */ #define DEFAULT_TOS_AUDIO 0 /*!< Audio packets should be marked as DSCP EF (Expedited Forwarding), but the default is 0 to be compatible with previous versions. */ #define DEFAULT_TOS_VIDEO 0 /*!< Video packets should be marked as DSCP AF41, but the default is 0 to be compatible with previous versions. */ +#define DEFAULT_TOS_TEXT 0 /*!< Text packets should be marked as XXXX XXXX, but the default is 0 to be compatible with previous versions. */ #define DEFAULT_ALLOW_EXT_DOM TRUE #define DEFAULT_REALM "asterisk" #define DEFAULT_NOTIFYRINGING TRUE @@ -563,6 +564,7 @@ static int global_mwitime; /*!< Time between MWI checks for peers */ static unsigned int global_tos_sip; /*!< IP type of service for SIP packets */ static unsigned int global_tos_audio; /*!< IP type of service for audio RTP packets */ static unsigned int global_tos_video; /*!< IP type of service for video RTP packets */ +static unsigned int global_tos_text; /*!< IP type of service for text RTP packets */ static int compactheaders; /*!< send compact sip headers */ static int recordhistory; /*!< Record SIP history. Off by default */ static int dumphistory; /*!< Dump history to verbose before destroying SIP dialog */ @@ -794,10 +796,14 @@ struct sip_auth { #define SIP_PAGE2_CALL_ONHOLD_INACTIVE (1 << 24) /*!< 24: Inactive */ #define SIP_PAGE2_RFC2833_COMPENSATE (1 << 25) /*!< 25: ???? */ #define SIP_PAGE2_BUGGY_MWI (1 << 26) /*!< 26: Buggy CISCO MWI fix */ +#define SIP_PAGE2_NOTEXT (1 << 27) /*!< 26: Text not supported */ +#define SIP_PAGE2_TEXTSUPPORT (1 << 28) /*!< 27: Global text enable */ +#define SIP_PAGE2_DEBUG_TEXT (1 << 29) /*!< 28: Global text debug */ #define SIP_PAGE2_FLAGS_TO_COPY \ (SIP_PAGE2_ALLOWSUBSCRIBE | SIP_PAGE2_ALLOWOVERLAP | SIP_PAGE2_VIDEOSUPPORT | \ - SIP_PAGE2_T38SUPPORT | SIP_PAGE2_RFC2833_COMPENSATE | SIP_PAGE2_BUGGY_MWI) + SIP_PAGE2_T38SUPPORT | SIP_PAGE2_RFC2833_COMPENSATE | SIP_PAGE2_BUGGY_MWI | \ + SIP_PAGE2_TEXTSUPPORT ) /* SIP packet flags */ #define SIP_PKT_DEBUG (1 << 0) /*!< Debug this packet */ @@ -833,6 +839,7 @@ static int global_t38_capability = T38FAX_VERSION_0 | T38FAX_RATE_2400 | T38FAX_ #define sipdebug ast_test_flag(&global_flags[1], SIP_PAGE2_DEBUG) #define sipdebug_config ast_test_flag(&global_flags[1], SIP_PAGE2_DEBUG_CONFIG) #define sipdebug_console ast_test_flag(&global_flags[1], SIP_PAGE2_DEBUG_CONSOLE) +#define sipdebug_text ast_test_flag(&global_flags[1], SIP_PAGE2_DEBUG_TEXT) /*! \brief T38 States for a call */ enum t38state { @@ -976,6 +983,7 @@ struct sip_pvt { struct sockaddr_in sa; /*!< Our peer */ struct sockaddr_in redirip; /*!< Where our RTP should be going if not to us */ struct sockaddr_in vredirip; /*!< Where our Video RTP should be going if not to us */ + struct sockaddr_in tredirip; /*!< Where our Text RTP should be going if not to us */ time_t lastrtprx; /*!< Last RTP received */ time_t lastrtptx; /*!< Last RTP sent */ int rtptimeout; /*!< RTP timeout time */ @@ -1010,6 +1018,7 @@ struct sip_pvt { struct sip_registry *registry; /*!< If this is a REGISTER dialog, to which registry */ struct ast_rtp *rtp; /*!< RTP Session */ struct ast_rtp *vrtp; /*!< Video RTP session */ + struct ast_rtp *trtp; /*!< Text RTP session */ struct sip_pkt *packets; /*!< Packets scheduled for re-transmission */ struct sip_history_head *history; /*!< History of this SIP dialog */ struct ast_variable *chanvars; /*!< Channel variables to set for inbound call */ @@ -1545,9 +1554,10 @@ static int handle_response_register(struct sip_pvt *p, int resp, char *rest, str static void handle_response(struct sip_pvt *p, int resp, char *rest, struct sip_request *req, int seqno); /*----- RTP interface functions */ -static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp, int codecs, int nat_active); +static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp, struct ast_rtp *trtp, int codecs, int nat_active); static enum ast_rtp_get_result sip_get_rtp_peer(struct ast_channel *chan, struct ast_rtp **rtp); static enum ast_rtp_get_result sip_get_vrtp_peer(struct ast_channel *chan, struct ast_rtp **rtp); +static enum ast_rtp_get_result sip_get_trtp_peer(struct ast_channel *chan, struct ast_rtp **rtp); static int sip_get_codec(struct ast_channel *chan); static struct ast_frame *sip_rtp_read(struct ast_channel *ast, struct sip_pvt *p, int *faxdetect); @@ -1571,6 +1581,7 @@ static const struct ast_channel_tech sip_tech = { .read = sip_read, .write = sip_write, .write_video = sip_write, + .write_text = sip_write, .indicate = sip_indicate, .transfer = sip_transfer, .fixup = sip_fixup, @@ -1619,6 +1630,7 @@ static struct ast_rtp_protocol sip_rtp = { type: "SIP", get_rtp_info: sip_get_rtp_peer, get_vrtp_info: sip_get_vrtp_peer, + get_trtp_info: sip_get_trtp_peer, set_rtp_peer: sip_set_rtp_peer, get_codec: sip_get_codec, }; @@ -2839,6 +2851,11 @@ static void do_setnat(struct sip_pvt *p, int natflags) ast_log(LOG_DEBUG, "Setting NAT on UDPTL to %s\n", mode); ast_udptl_setnat(p->udptl, natflags); } + if (p->trtp) { + if (option_debug) + ast_log(LOG_DEBUG, "Setting NAT on TRTP to %s\n", mode); + ast_rtp_setnat(p->trtp, natflags); + } } /*! \brief Create address structure from peer reference. @@ -2860,6 +2877,10 @@ static int create_addr_from_peer(struct sip_pvt *dialog, struct sip_peer *peer) ast_rtp_destroy(dialog->vrtp); dialog->vrtp = NULL; } + if (!ast_test_flag(&dialog->flags[1], SIP_PAGE2_TEXTSUPPORT) && dialog->trtp) { + ast_rtp_destroy(dialog->trtp); + dialog->trtp = NULL; + } dialog->prefs = peer->prefs; if (ast_test_flag(&dialog->flags[1], SIP_PAGE2_T38SUPPORT)) { dialog->t38.capability = global_t38_capability; @@ -2898,6 +2919,13 @@ static int create_addr_from_peer(struct sip_pvt *dialog, struct sip_peer *peer) ast_rtp_set_rtpholdtimeout(dialog->vrtp, peer->rtpholdtimeout); ast_rtp_set_rtpkeepalive(dialog->vrtp, peer->rtpkeepalive); } + if (dialog->trtp) { + ast_rtp_setdtmf(dialog->trtp, 0); + ast_rtp_setdtmfcompensate(dialog->trtp, 0); + ast_rtp_set_rtptimeout(dialog->trtp, peer->rtptimeout); + ast_rtp_set_rtpholdtimeout(dialog->trtp, peer->rtpholdtimeout); + ast_rtp_set_rtpkeepalive(dialog->trtp, peer->rtpkeepalive); + } ast_string_field_set(dialog, peername, peer->username); ast_string_field_set(dialog, authname, peer->username); @@ -3181,6 +3209,8 @@ static void __sip_destroy(struct sip_pvt *p, int lockowner, int lockdialoglist) ast_rtp_destroy(p->rtp); if (p->vrtp) ast_rtp_destroy(p->vrtp); + if (p->trtp) + ast_rtp_destroy(p->trtp); if (p->udptl) ast_udptl_destroy(p->udptl); if (p->refer) @@ -3644,10 +3674,13 @@ static int sip_hangup(struct ast_channel *ast) if (!p->pendinginvite) { char *audioqos = ""; char *videoqos = ""; + char *textqos = ""; if (p->rtp) audioqos = ast_rtp_get_quality(p->rtp); if (p->vrtp) videoqos = ast_rtp_get_quality(p->vrtp); + if (p->trtp) + textqos = ast_rtp_get_quality(p->trtp); /* Send a hangup */ transmit_request_with_auth(p, SIP_BYE, 0, XMIT_RELIABLE, 1); @@ -3657,11 +3690,15 @@ static int sip_hangup(struct ast_channel *ast) append_history(p, "RTCPaudio", "Quality:%s", audioqos); if (p->vrtp) append_history(p, "RTCPvideo", "Quality:%s", videoqos); + if (p->trtp) + append_history(p, "RTCPtext", "Quality:%s", textqos); } if (p->rtp && oldowner) pbx_builtin_setvar_helper(oldowner, "RTPAUDIOQOS", audioqos); if (p->vrtp && oldowner) pbx_builtin_setvar_helper(oldowner, "RTPVIDEOQOS", videoqos); + if (p->trtp && oldowner) + pbx_builtin_setvar_helper(oldowner, "RTPTEXTQOS", textqos); } else { /* Note we will need a BYE when this all settles out but we can't send one while we have "INVITE" outstanding. */ @@ -3779,6 +3816,23 @@ static int sip_write(struct ast_channel *ast, struct ast_frame *frame) sip_pvt_unlock(p); } break; + case AST_FRAME_TEXT: + if (p) { + sip_pvt_lock(p); + if (p->trtp) { + /* Activate text early media */ + if ((ast->_state != AST_STATE_UP) && + !ast_test_flag(&p->flags[0], SIP_PROGRESS_SENT) && + !ast_test_flag(&p->flags[0], SIP_OUTGOING)) { + transmit_response_with_sdp(p, "183 Session Progress", &p->initreq, XMIT_UNRELIABLE); + ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT); + } + p->lastrtptx = time(NULL); + res = ast_rtp_write(p->trtp, frame); + } + sip_pvt_unlock(p); + } + break; case AST_FRAME_IMAGE: return 0; break; @@ -4010,7 +4064,10 @@ static struct ast_channel *sip_new(struct sip_pvt *i, int state, const char *tit struct ast_variable *v = NULL; int fmt; int what; + int video; + int text; int needvideo = 0; + int needtext = 0; { const char *my_name; /* pick a good name */ @@ -4040,15 +4097,22 @@ static struct ast_channel *sip_new(struct sip_pvt *i, int state, const char *tit /* Select our native format based on codec preference until we receive something from another device to the contrary. */ - if (i->jointcapability) /* The joint capabilities of us and peer */ + if (i->jointcapability) { /* The joint capabilities of us and peer */ what = i->jointcapability; - else if (i->capability) /* Our configured capability for this peer */ + video = i->jointcapability & AST_FORMAT_VIDEO_MASK; + text = i->jointcapability & AST_FORMAT_TEXT_MASK; + } else if (i->capability) { /* Our configured capability for this peer */ what = i->capability; - else + video = i->capability & AST_FORMAT_VIDEO_MASK; + text = i->capability & AST_FORMAT_TEXT_MASK; + } else { what = global_capability; /* Global codec support */ + video = global_capability & AST_FORMAT_VIDEO_MASK; + text = global_capability & AST_FORMAT_TEXT_MASK; + } /* 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); + tmp->nativeformats = ast_codec_choose(&i->prefs, what, 1) | video | text; if (option_debug > 2) { char buf[BUFSIZ]; ast_log(LOG_DEBUG, "*** Our native formats are %s \n", ast_getformatname_multiple(buf, BUFSIZ, tmp->nativeformats)); @@ -4073,6 +4137,13 @@ static struct ast_channel *sip_new(struct sip_pvt *i, int state, const char *tit needvideo = i->jointcapability & AST_FORMAT_VIDEO_MASK; /* Inbound call */ } + if (i->trtp) { + if (i->prefcodec) + needtext = i->prefcodec & AST_FORMAT_TEXT_MASK; /* Outbound call */ + else + needtext = i->jointcapability & AST_FORMAT_TEXT_MASK; /* Inbound call */ + } + if (option_debug > 2) { if (needvideo) ast_log(LOG_DEBUG, "This channel can handle video! HOLLYWOOD next!\n"); @@ -4096,6 +4167,9 @@ static struct ast_channel *sip_new(struct sip_pvt *i, int state, const char *tit tmp->fds[2] = ast_rtp_fd(i->vrtp); tmp->fds[3] = ast_rtcp_fd(i->vrtp); } + if (needtext && i->trtp) { + tmp->fds[4] = ast_rtp_fd(i->trtp); + } if (i->udptl) { tmp->fds[5] = ast_udptl_fd(i->udptl); } @@ -4314,6 +4388,19 @@ static struct ast_frame *sip_rtp_read(struct ast_channel *ast, struct sip_pvt *p case 3: f = ast_rtcp_read(p->vrtp); /* RTCP Control Channel for video */ break; + case 4: + f = ast_rtp_read(p->trtp); /* RTP Text */ + if (sipdebug_text) { + int i; + unsigned char* arr = f->data; + for (i=0; i < f->datalen; i++) + ast_verbose("%c", (arr[i] > ' ' && arr[i] < '}') ? arr[i] : '.'); + ast_verbose(" -> "); + for (i=0; i < f->datalen; i++) + ast_verbose("%02X ", arr[i]); + ast_verbose("\n"); + } + break; case 5: f = ast_udptl_read(p->udptl); /* UDPTL for T.38 */ break; @@ -4331,7 +4418,7 @@ static struct ast_frame *sip_rtp_read(struct ast_channel *ast, struct sip_pvt *p if (f->subclass != (p->owner->nativeformats & AST_FORMAT_AUDIO_MASK)) { if (option_debug) ast_log(LOG_DEBUG, "Oooh, format changed to %d\n", f->subclass); - p->owner->nativeformats = (p->owner->nativeformats & AST_FORMAT_VIDEO_MASK) | f->subclass; + p->owner->nativeformats = (p->owner->nativeformats & (AST_FORMAT_VIDEO_MASK | AST_FORMAT_TEXT_MASK)) | f->subclass; ast_set_read_format(p->owner, p->owner->readformat); ast_set_write_format(p->owner, p->owner->writeformat); } @@ -4475,11 +4562,15 @@ static struct sip_pvt *sip_alloc(ast_string_field callid, struct sockaddr_in *si /* If the global videosupport flag is on, we always create a RTP interface for video */ if (ast_test_flag(&p->flags[1], SIP_PAGE2_VIDEOSUPPORT)) p->vrtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr); + if (ast_test_flag(&p->flags[1], SIP_PAGE2_TEXTSUPPORT)) + p->trtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr); if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT)) p->udptl = ast_udptl_new_with_bindaddr(sched, io, 0, bindaddr.sin_addr); - if (!p->rtp || (ast_test_flag(&p->flags[1], SIP_PAGE2_VIDEOSUPPORT) && !p->vrtp)) { - ast_log(LOG_WARNING, "Unable to create RTP audio %s session: %s\n", - ast_test_flag(&p->flags[1], SIP_PAGE2_VIDEOSUPPORT) ? "and video" : "", strerror(errno)); + if (!p->rtp|| (ast_test_flag(&p->flags[1], SIP_PAGE2_VIDEOSUPPORT) && !p->vrtp) + || (ast_test_flag(&p->flags[1], SIP_PAGE2_TEXTSUPPORT) && !p->trtp)) { + ast_log(LOG_WARNING, "Unable to create RTP audio %s%ssession: %s\n", + ast_test_flag(&p->flags[1], SIP_PAGE2_VIDEOSUPPORT) ? "and video " : "", + ast_test_flag(&p->flags[1], SIP_PAGE2_TEXTSUPPORT) ? "and text " : "", strerror(errno)); ast_mutex_destroy(&p->pvt_lock); if (p->chanvars) { ast_variables_destroy(p->chanvars); @@ -4502,6 +4593,11 @@ static struct sip_pvt *sip_alloc(ast_string_field callid, struct sockaddr_in *si ast_rtp_set_rtpholdtimeout(p->vrtp, global_rtpholdtimeout); ast_rtp_set_rtpkeepalive(p->vrtp, global_rtpkeepalive); } + if (p->trtp) { + ast_rtp_settos(p->trtp, global_tos_text); + ast_rtp_setdtmf(p->trtp, 0); + ast_rtp_setdtmfcompensate(p->trtp, 0); + } if (p->udptl) ast_udptl_settos(p->udptl, global_tos_audio); p->maxcallbitrate = default_maxcallbitrate; @@ -4944,6 +5040,7 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req) int len = -1; int portno = -1; /*!< RTP Audio port number */ int vportno = -1; /*!< RTP Video port number */ + int tportno = -1; /*!< RTP Text port number */ int udptlportno = -1; int peert38capability = 0; char s[256]; @@ -4952,20 +5049,24 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req) /* Peer capability is the capability in the SDP, non codec is RFC2833 DTMF (101) */ int peercapability = 0, peernoncodeccapability = 0; int vpeercapability = 0, vpeernoncodeccapability = 0; + int tpeercapability = 0, tpeernoncodeccapability = 0; struct sockaddr_in sin; /*!< media socket address */ struct sockaddr_in vsin; /*!< Video socket address */ + struct sockaddr_in tsin; /*!< Text socket address */ const char *codecs; struct hostent *hp; /*!< RTP Audio host IP */ struct hostent *vhp = NULL; /*!< RTP video host IP */ + struct hostent *thp = NULL; /*!< RTP text host IP */ struct ast_hostent audiohp; struct ast_hostent videohp; + struct ast_hostent texthp; int codec; int destiterator = 0; int iterator; int sendonly = 0; int numberofports; - struct ast_rtp *newaudiortp, *newvideortp; /* Buffers for codec handling */ + struct ast_rtp *newaudiortp, *newvideortp, *newtextrtp; /* Buffers for codec handling */ int newjointcapability; /* Negotiated capability */ int newpeercapability; int newnoncodeccapability; @@ -4991,6 +5092,11 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req) ast_rtp_new_init(newvideortp); ast_rtp_pt_clear(newvideortp); + newtextrtp = alloca(ast_rtp_alloc_size()); + memset(newtextrtp, 0, ast_rtp_alloc_size()); + ast_rtp_new_init(newtextrtp); + ast_rtp_pt_clear(newtextrtp); + /* Update our last rtprx when we receive an SDP, too */ p->lastrtprx = p->lastrtptx = time(NULL); /* XXX why both ? */ @@ -5017,15 +5123,24 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req) return -1; } vhp = hp; /* Copy to video address as default too */ + thp = hp; /* Copy to video address as default too */ iterator = req->sdp_start; ast_set_flag(&p->flags[0], SIP_NOVIDEO); + ast_set_flag(&p->flags[1], SIP_PAGE2_NOTEXT); + if (p->vrtp) + ast_rtp_pt_clear(newvideortp); /* Must be cleared in case no m=video line exists */ + + if (p->trtp) + ast_rtp_pt_clear(newtextrtp); /* Must be cleared in case no m=text line exists */ /* Find media streams in this SDP offer */ while ((m = get_sdp_iterate(&iterator, req, "m"))[0] != '\0') { int x; int audio = FALSE; + int video = FALSE; + int text = FALSE; numberofports = 1; if ((sscanf(m, "audio %d/%d RTP/AVP %n", &x, &numberofports, &len) == 2) || @@ -5046,7 +5161,7 @@ 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) || (sscanf(m, "video %d RTP/AVP %n", &x, &len) == 1)) { - /* If it is not audio - is it video ? */ + video = TRUE; ast_clear_flag(&p->flags[0], SIP_NOVIDEO); numberofmediastreams++; vportno = x; @@ -5060,6 +5175,22 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req) ast_verbose("Found RTP video format %d\n", codec); ast_rtp_set_m_type(newvideortp, codec); } + } else if ((sscanf(m, "text %d/%d RTP/AVP %n", &x, &numberofports, &len) == 2) || + (sscanf(m, "text %d RTP/AVP %n", &x, &len) == 1)) { + text = TRUE; + ast_clear_flag(&p->flags[1], SIP_PAGE2_NOTEXT); + numberofmediastreams++; + tportno = 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)) { + if (sscanf(codecs, "%d%n", &codec, &len) != 1) { + ast_log(LOG_WARNING, "Error in codec string '%s'\n", codecs); + return -1; + } + if (debug) + ast_verbose("Found RTP text format %d\n", codec); + ast_rtp_set_m_type(newtextrtp, codec); + } } else if (p->udptl && ( (sscanf(m, "image %d udptl t38%n", &x, &len) == 1) || (sscanf(m, "image %d UDPTL t38%n", &x, &len) == 1) )) { if (debug) @@ -5092,27 +5223,35 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req) 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); + } else if (video) { + if (!(vhp = ast_gethostbyname(host, &videohp))) + ast_log(LOG_WARNING, "Unable to lookup RTP video host in secondary c= line, '%s'\n", c); + } else if (text) { + if (!(thp = ast_gethostbyname(host, &texthp))) + ast_log(LOG_WARNING, "Unable to lookup RTP text host in secondary c= line, '%s'\n", c); + } } } } - if (portno == -1 && vportno == -1 && udptlportno == -1) + if (portno == -1 && vportno == -1 && udptlportno == -1 && tportno == -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 fax, audio and/or video media streams, fail this offer */ + if (numberofmediastreams > 3) + /* We have too many fax, audio and/or video and/or text 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; + tsin.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)); + if (thp) + memcpy(&tsin.sin_addr, thp->h_addr, sizeof(tsin.sin_addr)); /* Setup UDPTL port number */ if (p->udptl) { @@ -5146,11 +5285,15 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req) } } } - /* Setup video port number */ + /* Setup video port number, assumes we have audio */ if (vportno != -1) vsin.sin_port = htons(vportno); - /* Next, scan through each "a=rtpmap:" line, noting each + /* Setup text port number, assumes we have audio */ + if (tportno != -1) + tsin.sin_port = htons(tportno); + + /* Next, scan through each "a=xxxx:" 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 */ @@ -5240,10 +5383,18 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req) last_rtpmap_codec++; /* Note: should really look at the 'freq' and '#chans' params too */ - ast_rtp_set_rtpmap_type(newaudiortp, codec, "audio", mimeSubtype, - ast_test_flag(&p->flags[0], SIP_G726_NONSTANDARD) ? AST_RTP_OPT_G726_NONSTANDARD : 0); - if (p->vrtp) - ast_rtp_set_rtpmap_type(newvideortp, codec, "video", mimeSubtype, 0); + /* Note: This should all be done in the context of the m= above */ + if (!strncasecmp(mimeSubtype, "H26",3)) { /* Video */ + /* Not going to do anything here for the moment, but we will soon */ + } else if (!strncasecmp(mimeSubtype, "T140",4)) { /* Text */ + if (p->trtp) { + /* ast_verbose("Adding t140 mimeSubtype to textrtp struct\n"); */ + ast_rtp_set_rtpmap_type(newtextrtp, codec, "text", mimeSubtype, 0); + } + } else { /* Must be audio?? */ + ast_rtp_set_rtpmap_type(newaudiortp, codec, "audio", mimeSubtype, + ast_test_flag(&p->flags[0], SIP_G726_NONSTANDARD) ? AST_RTP_OPT_G726_NONSTANDARD : 0); + } } } @@ -5360,21 +5511,23 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req) /* 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); + ast_rtp_get_current_formats(newtextrtp, &tpeercapability, &tpeernoncodeccapability); + + newjointcapability = p->capability & (peercapability | vpeercapability | tpeercapability); + newpeercapability = (peercapability | vpeercapability | tpeercapability); newnoncodeccapability = p->noncodeccapability & peernoncodeccapability; if (debug) { /* shame on whoever coded this.... */ - char s1[BUFSIZ], s2[BUFSIZ], s3[BUFSIZ], s4[BUFSIZ]; + char s1[BUFSIZ], s2[BUFSIZ], s3[BUFSIZ], s4[BUFSIZ], s5[BUFSIZ]; - ast_verbose("Capabilities: us - %s, peer - audio=%s/video=%s, combined - %s\n", + ast_verbose("Capabilities: us - %s, peer - audio=%s/video=%s/text=%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_getformatname_multiple(s4, BUFSIZ, tpeercapability), + ast_getformatname_multiple(s5, BUFSIZ, newjointcapability)); ast_verbose("Non-codec capabilities (dtmf): us - %s, peer - %s, combined - %s\n", ast_rtp_lookup_mime_multiple(s1, BUFSIZ, p->noncodeccapability, 0, 0), @@ -5403,6 +5556,8 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req) ast_rtp_pt_copy(p->rtp, newaudiortp); if (p->vrtp) ast_rtp_pt_copy(p->vrtp, newvideortp); + if (p->trtp) + ast_rtp_pt_copy(p->trtp, newtextrtp); if (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO) { ast_clear_flag(&p->flags[0], SIP_DTMF); @@ -5431,6 +5586,13 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req) ast_verbose("Peer video RTP is at port %s:%d\n", ast_inet_ntoa(vsin.sin_addr), ntohs(vsin.sin_port)); } + /* Setup text port number */ + if (p->trtp && tsin.sin_port) { + ast_rtp_set_peer(p->trtp, &tsin); + if (debug) + ast_verbose("Peer text RTP is at port %s:%d\n", ast_inet_ntoa(tsin.sin_addr), ntohs(tsin.sin_port)); + } + /* Ok, we're going with this offer */ if (option_debug > 1) { char buf[BUFSIZ]; @@ -5450,7 +5612,7 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req) 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); + p->owner->nativeformats = ast_codec_choose(&p->prefs, p->jointcapability, 1) | (p->capability & vpeercapability) | (p->capability & tpeercapability); ast_set_read_format(p->owner, p->owner->readformat); ast_set_write_format(p->owner, p->owner->writeformat); } @@ -6187,6 +6349,52 @@ static void add_codec_to_sdp(const struct sip_pvt *p, int codec, int sample_rate *min_packet_size = fmt.cur_ms; } +/*! \brief Add video codec offer to SDP offer/answer body in INVITE or 200 OK */ +/* This is different to the audio one now so we can add more caps later */ +static void add_vcodec_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, int *min_packet_size) +{ + int rtp_code; + + if (!p->vrtp) + return; + + if (debug) + ast_verbose("Adding video codec 0x%x (%s) to SDP\n", codec, ast_getformatname(codec)); + + if ((rtp_code = ast_rtp_lookup_code(p->vrtp, 1, codec)) == -1) + return; + + ast_build_string(m_buf, m_size, " %d", rtp_code); + ast_build_string(a_buf, a_size, "a=rtpmap:%d %s/%d\r\n", rtp_code, + ast_rtp_lookup_mime_subtype(1, codec, 0), sample_rate); + /* Add fmtp code here */ +} + +/*! \brief Add text codec offer to SDP offer/answer body in INVITE or 200 OK */ +static void add_tcodec_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, int *min_packet_size) +{ + int rtp_code; + + if (!p->trtp) + return; + + if (debug) + ast_verbose("Adding text codec 0x%x (%s) to SDP\n", codec, ast_getformatname(codec)); + + if ((rtp_code = ast_rtp_lookup_code(p->trtp, 1, codec)) == -1) + return; + + ast_build_string(m_buf, m_size, " %d", rtp_code); + ast_build_string(a_buf, a_size, "a=rtpmap:%d %s/%d\r\n", rtp_code, + ast_rtp_lookup_mime_subtype(1, codec, 0), sample_rate); + /* Add fmtp code here */ +} + + /*! \brief Get Max T.38 Transmission rate from T38 capabilities */ static int t38_get_rate(int t38cap) { @@ -6344,12 +6552,14 @@ static void add_noncodec_to_sdp(const struct sip_pvt *p, int format, int sample_ /*! \brief Set all IP media addresses for this call \note called from add_sdp() */ -static void get_our_media_address(struct sip_pvt *p, int needvideo, struct sockaddr_in *sin, struct sockaddr_in *vsin, struct sockaddr_in *dest, struct sockaddr_in *vdest) +static void get_our_media_address(struct sip_pvt *p, int needvideo, struct sockaddr_in *sin, struct sockaddr_in *vsin, struct sockaddr_in *tsin, struct sockaddr_in *dest, struct sockaddr_in *vdest) { /* First, get our address */ ast_rtp_get_us(p->rtp, sin); if (p->vrtp) ast_rtp_get_us(p->vrtp, vsin); + if (p->trtp) + ast_rtp_get_us(p->trtp, tsin); /* Now, try to figure out where we want them to send data */ /* Is this a re-invite to move the media out, then use the original offer from caller */ @@ -6383,8 +6593,10 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p) struct sockaddr_in sin; struct sockaddr_in vsin; + struct sockaddr_in tsin; struct sockaddr_in dest; struct sockaddr_in vdest = { 0, }; + struct sockaddr_in tdest = { 0, }; /* SDP fields */ char *version = "v=0\r\n"; /* Protocol version */ @@ -6396,25 +6608,34 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p) char *hold; char m_audio[256]; /* Media declaration line for audio */ char m_video[256]; /* Media declaration line for video */ + char m_text[256]; /* Media declaration line for text */ char a_audio[1024]; /* Attributes for audio */ char a_video[1024]; /* Attributes for video */ + char a_text[1024]; /* Attributes for text */ char *m_audio_next = m_audio; char *m_video_next = m_video; + char *m_text_next = m_text; size_t m_audio_left = sizeof(m_audio); size_t m_video_left = sizeof(m_video); + size_t m_text_left = sizeof(m_text); char *a_audio_next = a_audio; char *a_video_next = a_video; + char *a_text_next = a_text; size_t a_audio_left = sizeof(a_audio); size_t a_video_left = sizeof(a_video); + size_t a_text_left = sizeof(a_text); int x; int capability; int needvideo = FALSE; + int needtext = FALSE; int debug = sip_debug_test_pvt(p); int min_audio_packet_size = 0; int min_video_packet_size = 0; + int min_text_packet_size = 0; m_video[0] = '\0'; /* Reset the video media string if it's not needed */ + m_text[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"); @@ -6434,7 +6655,8 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p) 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 capability: %s Video flag: %s Text flag: %s\n", ast_getformatname_multiple(codecbuf, sizeof(codecbuf), capability), + ast_test_flag(&p->flags[0], SIP_NOVIDEO) ? "True" : "False", ast_test_flag(&p->flags[1], SIP_PAGE2_NOTEXT) ? "True" : "False"); ast_log(LOG_DEBUG, "** Our prefcodec: %s \n", ast_getformatname_multiple(codecbuf, sizeof(codecbuf), p->prefcodec)); } @@ -6456,8 +6678,11 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p) } /* Get our media addresses */ - get_our_media_address(p, needvideo, &sin, &vsin, &dest, &vdest); + get_our_media_address(p, needvideo, &sin, &vsin, &tsin, &dest, &vdest); + if (debug) + ast_verbose("Audio is at %s port %d\n", ast_inet_ntoa(p->ourip), ntohs(sin.sin_port)); + /* 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) { @@ -6470,8 +6695,36 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p) ast_verbose("Video is at %s port %d\n", ast_inet_ntoa(p->ourip), ntohs(vsin.sin_port)); } - if (debug) - ast_verbose("Audio is at %s port %d\n", ast_inet_ntoa(p->ourip), ntohs(sin.sin_port)); + /* Check if we need text in this call */ + if((capability & AST_FORMAT_TEXT_MASK) && !ast_test_flag(&p->flags[1], SIP_PAGE2_NOTEXT)) { + if (sipdebug_text) ast_verbose("We think we can do text\n"); + if (p->trtp) { + if (sipdebug_text) ast_verbose("And we have a text rtp object\n"); + needtext = TRUE; + if (option_debug > 1) + ast_log(LOG_DEBUG, "This call needs text offers! \n"); + } else if (option_debug > 1) + ast_log(LOG_DEBUG, "This call needs text offers, but there's no text support enabled ! \n"); + } + + /* Ok, we need text. Let's add what we need for text and set codecs. + Text is handled differently than audio since we can not transcode. */ + if (needtext) { + if (sipdebug_text) ast_verbose("Lets set up the text sdp\n"); + /* Determine text destination */ + if (p->tredirip.sin_addr.s_addr) { + tdest.sin_addr = p->tredirip.sin_addr; + tdest.sin_port = p->tredirip.sin_port; + } else { + tdest.sin_addr = p->ourip; + tdest.sin_port = tsin.sin_port; + } + ast_build_string(&m_text_next, &m_text_left, "m=text %d RTP/AVP", ntohs(tdest.sin_port)); + + if (debug) + ast_verbose("Text is at %s port %d\n", ast_inet_ntoa(p->ourip), ntohs(tsin.sin_port)); + + } /* Start building generic SDP headers */ @@ -6529,7 +6782,7 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p) } /* 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) { + for (x = 1; x <= (needtext ? AST_FORMAT_MAX_TEXT : (needvideo ? AST_FORMAT_MAX_VIDEO : AST_FORMAT_MAX_AUDIO)); x <<= 1) { if (!(capability & x)) /* Codec not requested */ continue; @@ -6541,11 +6794,16 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p) &m_audio_next, &m_audio_left, &a_audio_next, &a_audio_left, debug, &min_audio_packet_size); - else - add_codec_to_sdp(p, x, 90000, + else if (x <= AST_FORMAT_MAX_VIDEO) + add_vcodec_to_sdp(p, x, 90000, &m_video_next, &m_video_left, &a_video_next, &a_video_left, debug, &min_video_packet_size); + else if (x <= AST_FORMAT_MAX_TEXT) + add_tcodec_to_sdp(p, x, 1000, + &m_text_next, &m_text_left, + &a_text_next, &a_text_left, + debug, &min_text_packet_size); } /* Now add DTMF RFC2833 telephony-event as a codec */ @@ -6565,21 +6823,32 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p) if (!p->owner || !ast_internal_timing_enabled(p->owner)) ast_build_string(&a_audio_next, &a_audio_left, "a=silenceSupp:off - - - -\r\n"); - if (min_audio_packet_size) - ast_build_string(&a_audio_next, &a_audio_left, "a=ptime:%d\r\n", min_audio_packet_size); - if (min_video_packet_size) - ast_build_string(&a_video_next, &a_video_left, "a=ptime:%d\r\n", min_video_packet_size); - - if ((m_audio_left < 2) || (m_video_left < 2) || (a_audio_left == 0) || (a_video_left == 0)) - 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"); - 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); + if (min_audio_packet_size) + ast_build_string(&a_audio_next, &a_audio_left, "a=ptime:%d\r\n", min_audio_packet_size); + + /* XXX don't think you can have ptime for video */ + if (min_video_packet_size) + ast_build_string(&a_video_next, &a_video_left, "a=ptime:%d\r\n", min_video_packet_size); + + /* XXX don't think you can have ptime for text */ + if (min_text_packet_size) + ast_build_string(&a_text_next, &a_text_left, "a=ptime:%d\r\n", min_text_packet_size); + + if ((m_audio_left < 2) || (m_video_left < 2) || (m_text_left < 2) || + (a_audio_left == 0) || (a_video_left == 0) || (a_text_left == 0)) + 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"); + if (needvideo) + ast_build_string(&m_video_next, &m_video_left, "\r\n"); + if (needtext) + ast_build_string(&m_text_next, &m_text_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); + if (needtext) /* only if text response is appropriate */ + len += strlen(m_text) + strlen(a_text) + strlen(hold); add_header(resp, "Content-Type", "application/sdp"); add_header_contentLength(resp, len); @@ -6598,6 +6867,11 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p) add_line(resp, a_video); add_line(resp, hold); /* Repeat hold for the video stream */ } + if (needtext) { /* only if text response is appropriate */ + add_line(resp, m_text); + add_line(resp, a_text); + add_line(resp, hold); /* Repeat hold for the text stream */ + } /* Update lastrtprx when we send our SDP */ p->lastrtprx = p->lastrtptx = time(NULL); /* XXX why both ? */ @@ -9359,6 +9633,11 @@ static enum check_auth_result check_user_ok(struct sip_pvt *p, char *of, ast_rtp_destroy(p->vrtp); p->vrtp = NULL; } + /* If we do not support text, remove text from call structure */ + if (!ast_test_flag(&p->flags[1], SIP_PAGE2_TEXTSUPPORT) && p->trtp) { + ast_rtp_destroy(p->trtp); + p->trtp = NULL; + } } unref_user(user); return res; @@ -9473,6 +9752,10 @@ static enum check_auth_result check_peer_ok(struct sip_pvt *p, char *of, ast_rtp_destroy(p->vrtp); p->vrtp = NULL; } + if ((!ast_test_flag(&p->flags[1], SIP_PAGE2_TEXTSUPPORT) || !(p->capability & AST_FORMAT_TEXT_MASK)) && p->trtp) { + ast_rtp_destroy(p->trtp); + p->trtp = NULL; + } 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; @@ -9962,6 +10245,7 @@ static int _sip_show_peers(int fd, int *total, struct mansession *s, const struc "Dynamic: %s\r\n" "Natsupport: %s\r\n" "VideoSupport: %s\r\n" + "TextSupport: %s\r\n" "ACL: %s\r\n" "Status: %s\r\n" "RealtimeDevice: %s\r\n\r\n", @@ -9972,6 +10256,7 @@ static int _sip_show_peers(int fd, int *total, struct mansession *s, const struc ast_test_flag(&iterator->flags[1], SIP_PAGE2_DYNAMIC) ? "yes" : "no", /* Dynamic or not? */ ast_test_flag(&iterator->flags[0], SIP_NAT_ROUTE) ? "yes" : "no", /* NAT=yes? */ ast_test_flag(&iterator->flags[1], SIP_PAGE2_VIDEOSUPPORT) ? "yes" : "no", /* VIDEOSUPPORT=yes? */ + ast_test_flag(&iterator->flags[1], SIP_PAGE2_TEXTSUPPORT) ? "yes" : "no", /* TEXTSUPPORT=yes? */ iterator->ha ? "yes" : "no", /* permit/deny */ status, realtimepeers ? (ast_test_flag(&iterator->flags[0], SIP_REALTIME) ? "yes":"no") : "no"); @@ -10392,6 +10677,7 @@ static int _sip_show_peer(int type, int fd, struct mansession *s, const struct m ast_cli(fd, " PromiscRedir : %s\n", ast_test_flag(&peer->flags[0], SIP_PROMISCREDIR)?"Yes":"No"); ast_cli(fd, " User=Phone : %s\n", ast_test_flag(&peer->flags[0], SIP_USEREQPHONE)?"Yes":"No"); ast_cli(fd, " Video Support: %s\n", ast_test_flag(&peer->flags[1], SIP_PAGE2_VIDEOSUPPORT)?"Yes":"No"); + ast_cli(fd, " Text Support : %s\n", ast_test_flag(&peer->flags[1], SIP_PAGE2_TEXTSUPPORT)?"Yes":"No"); ast_cli(fd, " Trust RPID : %s\n", ast_test_flag(&peer->flags[0], SIP_TRUSTRPID) ? "Yes" : "No"); ast_cli(fd, " Send RPID : %s\n", ast_test_flag(&peer->flags[0], SIP_SENDRPID) ? "Yes" : "No"); ast_cli(fd, " Subscriptions: %s\n", ast_test_flag(&peer->flags[1], SIP_PAGE2_ALLOWSUBSCRIBE) ? "Yes" : "No"); @@ -10479,6 +10765,7 @@ static int _sip_show_peer(int type, int fd, struct mansession *s, const struct m astman_append(s, "SIP-PromiscRedir: %s\r\n", (ast_test_flag(&peer->flags[0], SIP_PROMISCREDIR)?"Y":"N")); astman_append(s, "SIP-UserPhone: %s\r\n", (ast_test_flag(&peer->flags[0], SIP_USEREQPHONE)?"Y":"N")); astman_append(s, "SIP-VideoSupport: %s\r\n", (ast_test_flag(&peer->flags[1], SIP_PAGE2_VIDEOSUPPORT)?"Y":"N")); + astman_append(s, "SIP-TextSupport: %s\r\n", (ast_test_flag(&peer->flags[1], SIP_PAGE2_TEXTSUPPORT)?"Y":"N")); /* - is enumerated */ astman_append(s, "SIP-DTMFmode: %s\r\n", dtmfmode2str(ast_test_flag(&peer->flags[0], SIP_DTMF))); @@ -10628,6 +10915,7 @@ static int sip_show_settings(int fd, int argc, char *argv[]) ast_cli(fd, " SIP Port: %d\n", ntohs(bindaddr.sin_port)); ast_cli(fd, " Bindaddress: %s\n", ast_inet_ntoa(bindaddr.sin_addr)); ast_cli(fd, " Videosupport: %s\n", ast_test_flag(&global_flags[1], SIP_PAGE2_VIDEOSUPPORT) ? "Yes" : "No"); + ast_cli(fd, " Textsupport: %s\n", ast_test_flag(&global_flags[1], SIP_PAGE2_TEXTSUPPORT) ? "Yes" : "No"); ast_cli(fd, " AutoCreatePeer: %s\n", autocreatepeer ? "Yes" : "No"); ast_cli(fd, " MatchAuthUsername: %s\n", global_match_auth_username ? "Yes" : "No"); ast_cli(fd, " Allow unknown access: %s\n", global_allowguest ? "Yes" : "No"); @@ -10652,6 +10940,7 @@ static int sip_show_settings(int fd, int argc, char *argv[]) ast_cli(fd, " IP ToS SIP: %s\n", ast_tos2str(global_tos_sip)); ast_cli(fd, " IP ToS RTP audio: %s\n", ast_tos2str(global_tos_audio)); ast_cli(fd, " IP ToS RTP video: %s\n", ast_tos2str(global_tos_video)); + ast_cli(fd, " IP ToS RTP text: %s\n", ast_tos2str(global_tos_text)); ast_cli(fd, " T38 fax pt UDPTL: %s\n", ast_test_flag(&global_flags[1], SIP_PAGE2_T38SUPPORT_UDPTL) ? "Yes" : "No"); #ifdef WHEN_WE_HAVE_T38_FOR_OTHER_TRANSPORTS ast_cli(fd, " T38 fax pt RTP: %s\n", ast_test_flag(&global_flags[1], SIP_PAGE2_T38SUPPORT_RTP) ? "Yes" : "No"); @@ -11262,6 +11551,7 @@ static int sip_do_debug(int fd, int argc, char *argv[]) return RESULT_SHOWUSAGE; } ast_set_flag(&global_flags[1], SIP_PAGE2_DEBUG_CONSOLE); + ast_set_flag(&global_flags[1], SIP_PAGE2_DEBUG_TEXT); /*! \note this can be a special debug command - "sip debug text" or something */ memset(&debugaddr, 0, sizeof(debugaddr)); ast_cli(fd, "SIP Debugging %senabled\n", oldsipdebug ? "re-" : ""); return RESULT_SUCCESS; @@ -11329,6 +11619,7 @@ static int sip_no_debug(int fd, int argc, char *argv[]) if (argc != 4) return RESULT_SHOWUSAGE; ast_clear_flag(&global_flags[1], SIP_PAGE2_DEBUG_CONSOLE); + ast_clear_flag(&global_flags[1], SIP_PAGE2_DEBUG_TEXT); ast_cli(fd, "SIP Debugging Disabled\n"); return RESULT_SUCCESS; } @@ -12510,6 +12801,8 @@ static void stop_media_flows(struct sip_pvt *p) ast_rtp_stop(p->rtp); if (p->vrtp) ast_rtp_stop(p->vrtp); + if (p->trtp) + ast_rtp_stop(p->trtp); if (p->udptl) ast_udptl_stop(p->udptl); } @@ -14487,7 +14780,7 @@ static int handle_request_bye(struct sip_pvt *p, struct sip_request *req) /* Get RTCP quality before end of call */ if (!ast_test_flag(&p->flags[0], SIP_NO_HISTORY) || p->owner) { - char *audioqos, *videoqos; + char *audioqos, *videoqos, *textqos; if (p->rtp) { audioqos = ast_rtp_get_quality(p->rtp); if (!ast_test_flag(&p->flags[0], SIP_NO_HISTORY)) @@ -14502,6 +14795,13 @@ static int handle_request_bye(struct sip_pvt *p, struct sip_request *req) if (p->owner) pbx_builtin_setvar_helper(p->owner, "RTPVIDEOQOS", videoqos); } + if (p->trtp) { + textqos = ast_rtp_get_quality(p->trtp); + if (!ast_test_flag(&p->flags[0], SIP_NO_HISTORY)) + append_history(p, "RTCPtext", "Quality:%s", textqos); + if (p->owner) + pbx_builtin_setvar_helper(p->owner, "RTPTEXTQOS", textqos); + } } stop_media_flows(p); /* Immediately stop RTP, VRTP and UDPTL as applicable */ @@ -15697,6 +15997,7 @@ static struct ast_channel *sip_request_call(const char *type, int format, void * printf("Setting up to call extension '%s' at '%s'\n", ext ? ext : "<none>", host); #endif p->prefcodec = oldformat; /* Format for this call */ + p->jointcapability = oldformat; sip_pvt_lock(p); tmpc = sip_new(p, AST_STATE_DOWN, host); /* Place the call */ sip_pvt_unlock(p); @@ -15819,6 +16120,10 @@ static int handle_common_options(struct ast_flags *flags, struct ast_flags *mask ast_set_flag(&mask[1], SIP_PAGE2_VIDEOSUPPORT); ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_VIDEOSUPPORT); res = 1; + } else if (!strcasecmp(v->name, "textsupport")) { + ast_set_flag(&mask[1], SIP_PAGE2_TEXTSUPPORT); + ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_TEXTSUPPORT); + res = 1; } else if (!strcasecmp(v->name, "allowoverlap")) { ast_set_flag(&mask[1], SIP_PAGE2_ALLOWOVERLAP); ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_ALLOWOVERLAP); @@ -16517,6 +16822,7 @@ static int reload_config(enum channelreloadreason reason) global_tos_sip = DEFAULT_TOS_SIP; global_tos_audio = DEFAULT_TOS_AUDIO; global_tos_video = DEFAULT_TOS_VIDEO; + global_tos_text = DEFAULT_TOS_TEXT; externhost[0] = '\0'; /* External host name (for behind NAT DynDNS support) */ externexpire = 0; /* Expiration for DNS re-issuing */ externrefresh = 10; @@ -16584,6 +16890,7 @@ static int reload_config(enum channelreloadreason reason) memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf)); ast_clear_flag(&global_flags[1], SIP_PAGE2_VIDEOSUPPORT); + ast_clear_flag(&global_flags[1], SIP_PAGE2_TEXTSUPPORT); /* Read the [general] config section of sip.conf (or from realtime config) */ for (v = ast_variable_browse(cfg, "general"); v; v = v->next) { @@ -16806,6 +17113,9 @@ static int reload_config(enum channelreloadreason reason) } else if (!strcasecmp(v->name, "tos_video")) { if (ast_str2tos(v->value, &global_tos_video)) ast_log(LOG_WARNING, "Invalid tos_video value at line %d, recommended value is 'af41'. See doc/ip-tos.txt.\n", v->lineno); + } else if (!strcasecmp(v->name, "tos_text")) { + if (ast_str2tos(v->value, &global_tos_text)) + ast_log(LOG_WARNING, "Invalid tos_text value at line %d, recommended value is 'af41'. See doc/ip-tos.txt.\n", v->lineno); } else if (!strcasecmp(v->name, "bindport")) { if (sscanf(v->value, "%d", &ourport) == 1) { bindaddr.sin_port = htons(ourport); @@ -17208,8 +17518,33 @@ static enum ast_rtp_get_result sip_get_vrtp_peer(struct ast_channel *chan, struc return res; } +/*! \brief Returns null if we can't reinvite text (part of RTP interface) */ +static enum ast_rtp_get_result sip_get_trtp_peer(struct ast_channel *chan, struct ast_rtp **rtp) +{ + struct sip_pvt *p = NULL; + enum ast_rtp_get_result res = AST_RTP_TRY_PARTIAL; + + if (!(p = chan->tech_pvt)) + return AST_RTP_GET_FAILED; + + sip_pvt_lock(p); + if (!(p->trtp)) { + sip_pvt_unlock(p); + return AST_RTP_GET_FAILED; + } + + *rtp = p->trtp; + + if (ast_test_flag(&p->flags[0], SIP_CAN_REINVITE)) + res = AST_RTP_TRY_NATIVE; + + sip_pvt_unlock(p); + + return res; +} + /*! \brief Set the RTP peer for this call */ -static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp, int codecs, int nat_active) +static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp, struct ast_rtp *trtp, int codecs, int nat_active) { struct sip_pvt *p; int changed = 0; @@ -17249,6 +17584,12 @@ static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struc memset(&p->vredirip, 0, sizeof(p->vredirip)); changed = 1; } + if (trtp) { + changed |= ast_rtp_get_peer(trtp, &p->tredirip); + } else if (p->tredirip.sin_addr.s_addr || ntohs(p->tredirip.sin_port) != 0) { + memset(&p->tredirip, 0, sizeof(p->tredirip)); + changed = 1; + } if (codecs && (p->redircodecs != codecs)) { p->redircodecs = codecs; changed = 1; diff --git a/channels/chan_skinny.c b/channels/chan_skinny.c index f6f68ea16..70b2caeba 100644 --- a/channels/chan_skinny.c +++ b/channels/chan_skinny.c @@ -1731,7 +1731,7 @@ static enum ast_rtp_get_result skinny_get_rtp_peer(struct ast_channel *c, struct return AST_RTP_TRY_NATIVE; } -static int skinny_set_rtp_peer(struct ast_channel *c, struct ast_rtp *rtp, struct ast_rtp *vrtp, int codecs, int nat_active) +static int skinny_set_rtp_peer(struct ast_channel *c, struct ast_rtp *rtp, struct ast_rtp *vrtp, struct ast_rtp *trtp, int codecs, int nat_active) { struct skinny_subchannel *sub; sub = c->tech_pvt; diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h index e9695940b..22a08120c 100644 --- a/include/asterisk/channel.h +++ b/include/asterisk/channel.h @@ -155,7 +155,7 @@ extern "C" { #include "asterisk/compiler.h" -#define AST_MAX_FDS 8 +#define AST_MAX_FDS 10 /* * We have AST_MAX_FDS file descriptors in a channel. * Some of them have a fixed use: @@ -297,6 +297,9 @@ struct ast_channel_tech { /*! \brief Write a frame, in standard format */ int (* const write_video)(struct ast_channel *chan, struct ast_frame *frame); + /*! \brief Write a text frame, in standard format */ + int (* const write_text)(struct ast_channel *chan, struct ast_frame *frame); + /*! \brief Find bridged channel */ struct ast_channel *(* const bridged_channel)(struct ast_channel *chan, struct ast_channel *bridge); @@ -888,6 +891,14 @@ int ast_write(struct ast_channel *chan, struct ast_frame *frame); */ int ast_write_video(struct ast_channel *chan, struct ast_frame *frame); +/*! \brief Write text frame to a channel + * This function writes the given frame to the indicated channel. + * \param chan destination channel of the frame + * \param frame frame that will be written + * \return It returns 1 on success, 0 if not implemented, and -1 on failure. + */ +int ast_write_text(struct ast_channel *chan, struct ast_frame *frame); + /*! \brief Send empty audio to prime a channel driver */ int ast_prod(struct ast_channel *chan); diff --git a/include/asterisk/frame.h b/include/asterisk/frame.h index 8d376b4be..fa0b6f939 100644 --- a/include/asterisk/frame.h +++ b/include/asterisk/frame.h @@ -268,7 +268,7 @@ extern struct ast_frame ast_null_frame; #define AST_FORMAT_T140 (1 << 25) /*! Maximum text mask */ #define AST_FORMAT_MAX_TEXT (1 << 26) -#define AST_FORMAT_TEXT_MASK (((1 << 27)-1) & ~(AST_FORMAT_AUDIO_MASK) & ~(AST_FORMAT_VIDEO_MASK))) +#define AST_FORMAT_TEXT_MASK (((1 << 27)-1) & ~(AST_FORMAT_AUDIO_MASK) & ~(AST_FORMAT_VIDEO_MASK)) enum ast_control_frame_type { AST_CONTROL_HANGUP = 1, /*!< Other end has hungup */ diff --git a/include/asterisk/rtp.h b/include/asterisk/rtp.h index 85f3b1903..5eea063db 100644 --- a/include/asterisk/rtp.h +++ b/include/asterisk/rtp.h @@ -75,8 +75,10 @@ struct ast_rtp_protocol { enum ast_rtp_get_result (* const get_rtp_info)(struct ast_channel *chan, struct ast_rtp **rtp); /*! Get RTP struct, or NULL if unwilling to transfer */ enum ast_rtp_get_result (* const get_vrtp_info)(struct ast_channel *chan, struct ast_rtp **rtp); + /*! Get RTP struct, or NULL if unwilling to transfer */ + enum ast_rtp_get_result (* const get_trtp_info)(struct ast_channel *chan, struct ast_rtp **rtp); /*! Set RTP peer */ - int (* const set_rtp_peer)(struct ast_channel *chan, struct ast_rtp *peer, struct ast_rtp *vpeer, int codecs, int nat_active); + int (* const set_rtp_peer)(struct ast_channel *chan, struct ast_rtp *peer, struct ast_rtp *vpeer, struct ast_rtp *tpeer, int codecs, int nat_active); int (* const get_codec)(struct ast_channel *chan); const char * const type; AST_LIST_ENTRY(ast_rtp_protocol) list; diff --git a/main/channel.c b/main/channel.c index 94e21d082..0405a7ab5 100644 --- a/main/channel.c +++ b/main/channel.c @@ -2569,8 +2569,13 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr) CHECK_BLOCKING(chan); break; case AST_FRAME_TEXT: - res = (chan->tech->send_text == NULL) ? 0 : - chan->tech->send_text(chan, (char *) fr->data); + if (fr->subclass == AST_FORMAT_T140) { + res = (chan->tech->write_text == NULL) ? 0 : + chan->tech->write_text(chan, fr); + } else { + res = (chan->tech->send_text == NULL) ? 0 : + chan->tech->send_text(chan, (char *) fr->data); + } break; case AST_FRAME_HTML: res = (chan->tech->send_html == NULL) ? 0 : @@ -2898,6 +2903,7 @@ struct ast_channel *ast_request(const char *type, int format, void *data, int *c int res; int foo; int videoformat = format & AST_FORMAT_VIDEO_MASK; + int textformat = format & AST_FORMAT_TEXT_MASK; if (!cause) cause = &foo; @@ -2924,7 +2930,7 @@ struct ast_channel *ast_request(const char *type, int format, void *data, int *c if (!chan->tech->requester) return NULL; - if (!(c = chan->tech->requester(type, capabilities | videoformat, data, cause))) + if (!(c = chan->tech->requester(type, capabilities | videoformat | textformat, data, cause))) return NULL; /* no need to generate a Newchannel event here; it is done in the channel_alloc call */ diff --git a/main/rtp.c b/main/rtp.c index 21310e1e7..afb5c256a 100644 --- a/main/rtp.c +++ b/main/rtp.c @@ -118,6 +118,8 @@ struct ast_rtp { unsigned int lastrxts; unsigned int lastividtimestamp; unsigned int lastovidtimestamp; + unsigned int lastitexttimestamp; + unsigned int lastotexttimestamp; unsigned int lasteventseqn; int lastrxseqno; /*!< Last received sequence number */ unsigned short seedrxseqno; /*!< What sequence number did they start with?*/ @@ -1333,7 +1335,7 @@ struct ast_frame *ast_rtp_read(struct ast_rtp *rtp) return f ? f : &ast_null_frame; } rtp->lastrxformat = rtp->f.subclass = rtpPT.code; - rtp->f.frametype = (rtp->f.subclass < AST_FORMAT_MAX_AUDIO) ? AST_FRAME_VOICE : AST_FRAME_VIDEO; + rtp->f.frametype = (rtp->f.subclass < AST_FORMAT_MAX_AUDIO) ? AST_FRAME_VOICE : (rtp->f.subclass < AST_FORMAT_MAX_VIDEO) ? AST_FRAME_VIDEO : AST_FRAME_TEXT; if (!rtp->lastrxts) rtp->lastrxts = timestamp; @@ -1357,7 +1359,7 @@ struct ast_frame *ast_rtp_read(struct ast_rtp *rtp) rtp->f.ts = timestamp / 8; rtp->f.len = rtp->f.samples / 8; rtp->f.seqno = seqno; - } else { + } else if(rtp->f.subclass < AST_FORMAT_MAX_VIDEO) { /* Video -- samples is # of samples vs. 90000 */ if (!rtp->lastividtimestamp) rtp->lastividtimestamp = timestamp; @@ -1367,7 +1369,14 @@ struct ast_frame *ast_rtp_read(struct ast_rtp *rtp) rtp->f.delivery.tv_usec = 0; if (mark) rtp->f.subclass |= 0x1; - + } else { + /* TEXT -- samples is # of samples vs. 1000 */ + if (!rtp->lastitexttimestamp) + rtp->lastitexttimestamp = timestamp; + rtp->f.samples = timestamp - rtp->lastitexttimestamp; + rtp->lastitexttimestamp = timestamp; + rtp->f.delivery.tv_sec = 0; + rtp->f.delivery.tv_usec = 0; } rtp->f.src = "RTP"; return &rtp->f; @@ -1524,9 +1533,10 @@ int ast_rtp_early_bridge(struct ast_channel *c0, struct ast_channel *c1) // dest = c0, src = c1 struct ast_rtp *destp = NULL, *srcp = NULL; /* Audio RTP Channels */ struct ast_rtp *vdestp = NULL, *vsrcp = NULL; /* Video RTP channels */ + struct ast_rtp *tdestp = NULL, *tsrcp = NULL; /* Text RTP channels */ struct ast_rtp_protocol *destpr = NULL, *srcpr = NULL; - enum ast_rtp_get_result audio_dest_res = AST_RTP_GET_FAILED, video_dest_res = AST_RTP_GET_FAILED; - enum ast_rtp_get_result audio_src_res = AST_RTP_GET_FAILED, video_src_res = AST_RTP_GET_FAILED; + enum ast_rtp_get_result audio_dest_res = AST_RTP_GET_FAILED, video_dest_res = AST_RTP_GET_FAILED, text_dest_res = AST_RTP_GET_FAILED; + enum ast_rtp_get_result audio_src_res = AST_RTP_GET_FAILED, video_src_res = AST_RTP_GET_FAILED, text_src_res = AST_RTP_GET_FAILED; int srccodec, nat_active = 0; /* Lock channels */ @@ -1560,12 +1570,14 @@ int ast_rtp_early_bridge(struct ast_channel *c0, struct ast_channel *c1) return -1; } - /* Get audio and video interface (if native bridge is possible) */ + /* Get audio, video and text interface (if native bridge is possible) */ audio_dest_res = destpr->get_rtp_info(c0, &destp); video_dest_res = destpr->get_vrtp_info ? destpr->get_vrtp_info(c0, &vdestp) : AST_RTP_GET_FAILED; + text_dest_res = destpr->get_trtp_info ? destpr->get_trtp_info(c0, &tdestp) : AST_RTP_GET_FAILED; if (srcpr) { audio_src_res = srcpr->get_rtp_info(c1, &srcp); video_src_res = srcpr->get_vrtp_info ? srcpr->get_vrtp_info(c1, &vsrcp) : AST_RTP_GET_FAILED; + text_src_res = srcpr->get_trtp_info ? srcpr->get_trtp_info(c1, &tsrcp) : AST_RTP_GET_FAILED; } /* Check if bridge is still possible (In SIP canreinvite=no stops this, like NAT) */ @@ -1586,7 +1598,7 @@ int ast_rtp_early_bridge(struct ast_channel *c0, struct ast_channel *c1) if (srcp && (srcp->nat || ast_test_flag(srcp, FLAG_NAT_ACTIVE))) nat_active = 1; /* Bridge media early */ - if (destpr->set_rtp_peer(c0, srcp, vsrcp, srccodec, nat_active)) + if (destpr->set_rtp_peer(c0, srcp, vsrcp, tsrcp, srccodec, nat_active)) ast_log(LOG_WARNING, "Channel '%s' failed to setup early bridge to '%s'\n", c0->name, c1 ? c1->name : "<unspecified>"); ast_channel_unlock(c0); if (c1) @@ -1600,9 +1612,10 @@ int ast_rtp_make_compatible(struct ast_channel *dest, struct ast_channel *src, i { struct ast_rtp *destp = NULL, *srcp = NULL; /* Audio RTP Channels */ struct ast_rtp *vdestp = NULL, *vsrcp = NULL; /* Video RTP channels */ + struct ast_rtp *tdestp = NULL, *tsrcp = NULL; /* Text RTP channels */ struct ast_rtp_protocol *destpr = NULL, *srcpr = NULL; - enum ast_rtp_get_result audio_dest_res = AST_RTP_GET_FAILED, video_dest_res = AST_RTP_GET_FAILED; - enum ast_rtp_get_result audio_src_res = AST_RTP_GET_FAILED, video_src_res = AST_RTP_GET_FAILED; + enum ast_rtp_get_result audio_dest_res = AST_RTP_GET_FAILED, video_dest_res = AST_RTP_GET_FAILED, text_dest_res = AST_RTP_GET_FAILED; + enum ast_rtp_get_result audio_src_res = AST_RTP_GET_FAILED, video_src_res = AST_RTP_GET_FAILED, text_src_res = AST_RTP_GET_FAILED; int srccodec; /* Lock channels */ @@ -1632,8 +1645,10 @@ int ast_rtp_make_compatible(struct ast_channel *dest, struct ast_channel *src, i /* Get audio and video interface (if native bridge is possible) */ audio_dest_res = destpr->get_rtp_info(dest, &destp); video_dest_res = destpr->get_vrtp_info ? destpr->get_vrtp_info(dest, &vdestp) : AST_RTP_GET_FAILED; + text_dest_res = destpr->get_trtp_info ? destpr->get_trtp_info(dest, &tdestp) : AST_RTP_GET_FAILED; audio_src_res = srcpr->get_rtp_info(src, &srcp); video_src_res = srcpr->get_vrtp_info ? srcpr->get_vrtp_info(src, &vsrcp) : AST_RTP_GET_FAILED; + text_src_res = srcpr->get_trtp_info ? srcpr->get_trtp_info(src, &tsrcp) : AST_RTP_GET_FAILED; /* Check if bridge is still possible (In SIP canreinvite=no stops this, like NAT) */ if (audio_dest_res != AST_RTP_TRY_NATIVE || audio_src_res != AST_RTP_TRY_NATIVE) { @@ -1645,13 +1660,15 @@ int ast_rtp_make_compatible(struct ast_channel *dest, struct ast_channel *src, i ast_rtp_pt_copy(destp, srcp); if (vdestp && vsrcp) ast_rtp_pt_copy(vdestp, vsrcp); + if (tdestp && tsrcp) + ast_rtp_pt_copy(tdestp, tsrcp); if (srcpr->get_codec) srccodec = srcpr->get_codec(src); else srccodec = 0; if (media) { /* Bridge early */ - if (destpr->set_rtp_peer(dest, srcp, vsrcp, srccodec, ast_test_flag(srcp, FLAG_NAT_ACTIVE))) + if (destpr->set_rtp_peer(dest, srcp, vsrcp, tsrcp, srccodec, ast_test_flag(srcp, FLAG_NAT_ACTIVE))) ast_log(LOG_WARNING, "Channel '%s' failed to setup early bridge to '%s'\n", dest->name, src->name); } ast_channel_unlock(dest); @@ -2080,6 +2097,8 @@ void ast_rtp_reset(struct ast_rtp *rtp) rtp->lastrxts = 0; rtp->lastividtimestamp = 0; rtp->lastovidtimestamp = 0; + rtp->lastitexttimestamp = 0; + rtp->lastotexttimestamp = 0; rtp->lasteventseqn = 0; rtp->lasteventendseqn = 0; rtp->lasttxformat = 0; @@ -2627,7 +2646,7 @@ static int ast_rtp_raw_write(struct ast_rtp *rtp, struct ast_frame *f, int codec mark = 1; } } - } else { + } else if(f->subclass < AST_FORMAT_MAX_VIDEO) { mark = f->subclass & 0x1; pred = rtp->lastovidtimestamp + f->samples; /* Re-calculate last TS */ @@ -2643,6 +2662,21 @@ static int ast_rtp_raw_write(struct ast_rtp *rtp, struct ast_frame *f, int codec rtp->lastovidtimestamp = rtp->lastts; } } + } else { + pred = rtp->lastotexttimestamp + f->samples; + /* Re-calculate last TS */ + rtp->lastts = rtp->lastts + ms * 90; + /* If it's close to our prediction, go for it */ + if (ast_tvzero(f->delivery)) { + if (abs(rtp->lastts - pred) < 7200) { + rtp->lastts = pred; + rtp->lastotexttimestamp += f->samples; + } else { + if (option_debug > 2) + ast_log(LOG_DEBUG, "Difference is %d, ms is %d (%d), pred/ts/samples %d/%d/%d\n", abs(rtp->lastts - pred), ms, ms * 90, rtp->lastts, pred, f->samples); + rtp->lastotexttimestamp = rtp->lastts; + } + } } /* If the timestamp for non-digit packets has moved beyond the timestamp for digits, update the digit timestamp. @@ -2736,11 +2770,12 @@ int ast_rtp_write(struct ast_rtp *rtp, struct ast_frame *_f) return 0; /* Make sure we have enough space for RTP header */ - if ((_f->frametype != AST_FRAME_VOICE) && (_f->frametype != AST_FRAME_VIDEO)) { - ast_log(LOG_WARNING, "RTP can only send voice and video\n"); + if ((_f->frametype != AST_FRAME_VOICE) && (_f->frametype != AST_FRAME_VIDEO) && (_f->frametype != AST_FRAME_TEXT)) { + ast_log(LOG_WARNING, "RTP can only send voice, video and text\n"); return -1; } + /* The bottom bit of a video subclass contains the marker bit */ subclass = _f->subclass; if (_f->frametype == AST_FRAME_VIDEO) subclass &= ~0x1; @@ -2825,29 +2860,33 @@ int ast_rtp_proto_register(struct ast_rtp_protocol *proto) } /*! \brief Bridge loop for true native bridge (reinvite) */ -static enum ast_bridge_result bridge_native_loop(struct ast_channel *c0, struct ast_channel *c1, struct ast_rtp *p0, struct ast_rtp *p1, struct ast_rtp *vp0, struct ast_rtp *vp1, struct ast_rtp_protocol *pr0, struct ast_rtp_protocol *pr1, int codec0, int codec1, int timeoutms, int flags, struct ast_frame **fo, struct ast_channel **rc, void *pvt0, void *pvt1) +static enum ast_bridge_result bridge_native_loop(struct ast_channel *c0, struct ast_channel *c1, struct ast_rtp *p0, struct ast_rtp *p1, struct ast_rtp *vp0, struct ast_rtp *vp1, struct ast_rtp *tp0, struct ast_rtp *tp1, struct ast_rtp_protocol *pr0, struct ast_rtp_protocol *pr1, int codec0, int codec1, int timeoutms, int flags, struct ast_frame **fo, struct ast_channel **rc, void *pvt0, void *pvt1) { struct ast_frame *fr = NULL; struct ast_channel *who = NULL, *other = NULL, *cs[3] = {NULL, }; int oldcodec0 = codec0, oldcodec1 = codec1; - struct sockaddr_in ac1 = {0,}, vac1 = {0,}, ac0 = {0,}, vac0 = {0,}; - struct sockaddr_in t1 = {0,}, vt1 = {0,}, t0 = {0,}, vt0 = {0,}; + struct sockaddr_in ac1 = {0,}, vac1 = {0,}, tac1 = {0,}, ac0 = {0,}, vac0 = {0,}, tac0 = {0,}; + struct sockaddr_in t1 = {0,}, vt1 = {0,}, tt1 = {0,}, t0 = {0,}, vt0 = {0,}, tt0 = {0,}; /* Set it up so audio goes directly between the two endpoints */ /* Test the first channel */ - if (!(pr0->set_rtp_peer(c0, p1, vp1, codec1, ast_test_flag(p1, FLAG_NAT_ACTIVE)))) { + if (!(pr0->set_rtp_peer(c0, p1, vp1, tp1, codec1, ast_test_flag(p1, FLAG_NAT_ACTIVE)))) { ast_rtp_get_peer(p1, &ac1); if (vp1) ast_rtp_get_peer(vp1, &vac1); + if (tp1) + ast_rtp_get_peer(tp1, &tac1); } else ast_log(LOG_WARNING, "Channel '%s' failed to talk to '%s'\n", c0->name, c1->name); /* Test the second channel */ - if (!(pr1->set_rtp_peer(c1, p0, vp0, codec0, ast_test_flag(p0, FLAG_NAT_ACTIVE)))) { + if (!(pr1->set_rtp_peer(c1, p0, vp0, tp0, codec0, ast_test_flag(p0, FLAG_NAT_ACTIVE)))) { ast_rtp_get_peer(p0, &ac0); if (vp0) ast_rtp_get_peer(vp0, &vac0); + if (tp0) + ast_rtp_get_peer(tp0, &tac0); } else ast_log(LOG_WARNING, "Channel '%s' failed to talk to '%s'\n", c1->name, c0->name); @@ -2867,10 +2906,10 @@ static enum ast_bridge_result bridge_native_loop(struct ast_channel *c0, struct if (option_debug) ast_log(LOG_DEBUG, "Oooh, something is weird, backing out\n"); if (c0->tech_pvt == pvt0) - if (pr0->set_rtp_peer(c0, NULL, NULL, 0, 0)) + if (pr0->set_rtp_peer(c0, NULL, NULL, NULL, 0, 0)) ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c0->name); if (c1->tech_pvt == pvt1) - if (pr1->set_rtp_peer(c1, NULL, NULL, 0, 0)) + if (pr1->set_rtp_peer(c1, NULL, NULL, NULL, 0, 0)) ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c1->name); return AST_BRIDGE_RETRY; } @@ -2879,44 +2918,56 @@ static enum ast_bridge_result bridge_native_loop(struct ast_channel *c0, struct ast_rtp_get_peer(p1, &t1); if (vp1) ast_rtp_get_peer(vp1, &vt1); + if (tp1) + ast_rtp_get_peer(tp1, &tt1); if (pr1->get_codec) codec1 = pr1->get_codec(c1); ast_rtp_get_peer(p0, &t0); if (vp0) ast_rtp_get_peer(vp0, &vt0); + if (tp0) + ast_rtp_get_peer(tp0, &tt0); if (pr0->get_codec) codec0 = pr0->get_codec(c0); if ((inaddrcmp(&t1, &ac1)) || (vp1 && inaddrcmp(&vt1, &vac1)) || + (tp1 && inaddrcmp(&tt1, &tac1)) || (codec1 != oldcodec1)) { if (option_debug > 1) { ast_log(LOG_DEBUG, "Oooh, '%s' changed end address to %s:%d (format %d)\n", c1->name, ast_inet_ntoa(t1.sin_addr), ntohs(t1.sin_port), codec1); ast_log(LOG_DEBUG, "Oooh, '%s' changed end vaddress to %s:%d (format %d)\n", c1->name, ast_inet_ntoa(vt1.sin_addr), ntohs(vt1.sin_port), codec1); + ast_log(LOG_DEBUG, "Oooh, '%s' changed end taddress to %s:%d (format %d)\n", + c1->name, ast_inet_ntoa(tt1.sin_addr), ntohs(tt1.sin_port), codec1); ast_log(LOG_DEBUG, "Oooh, '%s' was %s:%d/(format %d)\n", c1->name, ast_inet_ntoa(ac1.sin_addr), ntohs(ac1.sin_port), oldcodec1); ast_log(LOG_DEBUG, "Oooh, '%s' was %s:%d/(format %d)\n", c1->name, ast_inet_ntoa(vac1.sin_addr), ntohs(vac1.sin_port), oldcodec1); + ast_log(LOG_DEBUG, "Oooh, '%s' was %s:%d/(format %d)\n", + c1->name, ast_inet_ntoa(tac1.sin_addr), ntohs(tac1.sin_port), oldcodec1); } - if (pr0->set_rtp_peer(c0, t1.sin_addr.s_addr ? p1 : NULL, vt1.sin_addr.s_addr ? vp1 : NULL, codec1, ast_test_flag(p1, FLAG_NAT_ACTIVE))) + if (pr0->set_rtp_peer(c0, t1.sin_addr.s_addr ? p1 : NULL, vt1.sin_addr.s_addr ? vp1 : NULL, tt1.sin_addr.s_addr ? tp1 : NULL, codec1, ast_test_flag(p1, FLAG_NAT_ACTIVE))) ast_log(LOG_WARNING, "Channel '%s' failed to update to '%s'\n", c0->name, c1->name); memcpy(&ac1, &t1, sizeof(ac1)); memcpy(&vac1, &vt1, sizeof(vac1)); + memcpy(&tac1, &tt1, sizeof(tac1)); oldcodec1 = codec1; } if ((inaddrcmp(&t0, &ac0)) || - (vp0 && inaddrcmp(&vt0, &vac0))) { + (vp0 && inaddrcmp(&vt0, &vac0)) || + (tp0 && inaddrcmp(&tt0, &tac0))) { if (option_debug > 1) { ast_log(LOG_DEBUG, "Oooh, '%s' changed end address to %s:%d (format %d)\n", c0->name, ast_inet_ntoa(t0.sin_addr), ntohs(t0.sin_port), codec0); ast_log(LOG_DEBUG, "Oooh, '%s' was %s:%d/(format %d)\n", c0->name, ast_inet_ntoa(ac0.sin_addr), ntohs(ac0.sin_port), oldcodec0); } - if (pr1->set_rtp_peer(c1, t0.sin_addr.s_addr ? p0 : NULL, vt0.sin_addr.s_addr ? vp0 : NULL, codec0, ast_test_flag(p0, FLAG_NAT_ACTIVE))) + if (pr1->set_rtp_peer(c1, t0.sin_addr.s_addr ? p0 : NULL, vt0.sin_addr.s_addr ? vp0 : NULL, tt0.sin_addr.s_addr ? tp0 : NULL, codec0, ast_test_flag(p0, FLAG_NAT_ACTIVE))) ast_log(LOG_WARNING, "Channel '%s' failed to update to '%s'\n", c1->name, c0->name); memcpy(&ac0, &t0, sizeof(ac0)); memcpy(&vac0, &vt0, sizeof(vac0)); + memcpy(&tac0, &tt0, sizeof(tac0)); oldcodec0 = codec0; } @@ -2941,10 +2992,10 @@ static enum ast_bridge_result bridge_native_loop(struct ast_channel *c0, struct if (option_debug) ast_log(LOG_DEBUG, "Oooh, got a %s\n", fr ? "digit" : "hangup"); if (c0->tech_pvt == pvt0) - if (pr0->set_rtp_peer(c0, NULL, NULL, 0, 0)) + if (pr0->set_rtp_peer(c0, NULL, NULL, NULL, 0, 0)) ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c0->name); if (c1->tech_pvt == pvt1) - if (pr1->set_rtp_peer(c1, NULL, NULL, 0, 0)) + if (pr1->set_rtp_peer(c1, NULL, NULL, NULL, 0, 0)) ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c1->name); return AST_BRIDGE_COMPLETE; } else if ((fr->frametype == AST_FRAME_CONTROL) && !(flags & AST_BRIDGE_IGNORE_SIGS)) { @@ -2954,15 +3005,15 @@ static enum ast_bridge_result bridge_native_loop(struct ast_channel *c0, struct if (fr->subclass == AST_CONTROL_HOLD) { /* If we someone went on hold we want the other side to reinvite back to us */ if (who == c0) - pr1->set_rtp_peer(c1, NULL, NULL, 0, 0); + pr1->set_rtp_peer(c1, NULL, NULL, NULL, 0, 0); else - pr0->set_rtp_peer(c0, NULL, NULL, 0, 0); + pr0->set_rtp_peer(c0, NULL, NULL, NULL, 0, 0); } else if (fr->subclass == AST_CONTROL_UNHOLD) { /* If they went off hold they should go back to being direct */ if (who == c0) - pr1->set_rtp_peer(c1, p0, vp0, codec0, ast_test_flag(p0, FLAG_NAT_ACTIVE)); + pr1->set_rtp_peer(c1, p0, vp0, tp0, codec0, ast_test_flag(p0, FLAG_NAT_ACTIVE)); else - pr0->set_rtp_peer(c0, p1, vp1, codec1, ast_test_flag(p1, FLAG_NAT_ACTIVE)); + pr0->set_rtp_peer(c0, p1, vp1, tp1, codec1, ast_test_flag(p1, FLAG_NAT_ACTIVE)); } ast_indicate_data(other, fr->subclass, fr->data, fr->datalen); ast_frfree(fr); @@ -3260,9 +3311,10 @@ enum ast_bridge_result ast_rtp_bridge(struct ast_channel *c0, struct ast_channel { struct ast_rtp *p0 = NULL, *p1 = NULL; /* Audio RTP Channels */ struct ast_rtp *vp0 = NULL, *vp1 = NULL; /* Video RTP channels */ + struct ast_rtp *tp0 = NULL, *tp1 = NULL; /* Text RTP channels */ struct ast_rtp_protocol *pr0 = NULL, *pr1 = NULL; - enum ast_rtp_get_result audio_p0_res = AST_RTP_GET_FAILED, video_p0_res = AST_RTP_GET_FAILED; - enum ast_rtp_get_result audio_p1_res = AST_RTP_GET_FAILED, video_p1_res = AST_RTP_GET_FAILED; + enum ast_rtp_get_result audio_p0_res = AST_RTP_GET_FAILED, video_p0_res = AST_RTP_GET_FAILED, text_p0_res = AST_RTP_GET_FAILED; + enum ast_rtp_get_result audio_p1_res = AST_RTP_GET_FAILED, video_p1_res = AST_RTP_GET_FAILED, text_p1_res = AST_RTP_GET_FAILED; enum ast_bridge_result res = AST_BRIDGE_FAILED; int codec0 = 0, codec1 = 0; void *pvt0 = NULL, *pvt1 = NULL; @@ -3296,8 +3348,10 @@ enum ast_bridge_result ast_rtp_bridge(struct ast_channel *c0, struct ast_channel /* Get audio and video interface (if native bridge is possible) */ audio_p0_res = pr0->get_rtp_info(c0, &p0); video_p0_res = pr0->get_vrtp_info ? pr0->get_vrtp_info(c0, &vp0) : AST_RTP_GET_FAILED; + text_p0_res = pr0->get_trtp_info ? pr0->get_trtp_info(c0, &vp0) : AST_RTP_GET_FAILED; audio_p1_res = pr1->get_rtp_info(c1, &p1); video_p1_res = pr1->get_vrtp_info ? pr1->get_vrtp_info(c1, &vp1) : AST_RTP_GET_FAILED; + text_p1_res = pr1->get_trtp_info ? pr1->get_trtp_info(c1, &vp1) : AST_RTP_GET_FAILED; /* If we are carrying video, and both sides are not reinviting... then fail the native bridge */ if (video_p0_res != AST_RTP_GET_FAILED && (audio_p0_res != AST_RTP_TRY_NATIVE || video_p0_res != AST_RTP_TRY_NATIVE)) @@ -3384,7 +3438,7 @@ enum ast_bridge_result ast_rtp_bridge(struct ast_channel *c0, struct ast_channel } else { if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Native bridging %s and %s\n", c0->name, c1->name); - res = bridge_native_loop(c0, c1, p0, p1, vp0, vp1, pr0, pr1, codec0, codec1, timeoutms, flags, fo, rc, pvt0, pvt1); + res = bridge_native_loop(c0, c1, p0, p1, vp0, vp1, tp0, tp1, pr0, pr1, codec0, codec1, timeoutms, flags, fo, rc, pvt0, pvt1); } return res; |