[asterisk-commits] oej: trunk r54838 - in /trunk: ./ channels/
include/asterisk/ main/
asterisk-commits at lists.digium.com
asterisk-commits at lists.digium.com
Fri Feb 16 06:35:44 MST 2007
Author: oej
Date: Fri Feb 16 07:35:44 2007
New Revision: 54838
URL: http://svn.digium.com/view/asterisk?view=rev&rev=54838
Log:
Adding Realtime Text support (T.140) to Asterisk
T.140/RFC 2793 is a live communication channel, originally
created for IP based text phones for hearing impaired.
Feels very much like the old Unix talk application.
This code is developed and disclaimed by John Martin of Aupix, UK.
Tested for interoperability by myself and Omnitor in Sweden,
the company that wrote most of the specifications.
A big thank you to everyone involved in this.
Modified:
trunk/CHANGES
trunk/CREDITS
trunk/channels/chan_gtalk.c
trunk/channels/chan_mgcp.c
trunk/channels/chan_sip.c
trunk/channels/chan_skinny.c
trunk/include/asterisk/channel.h
trunk/include/asterisk/frame.h
trunk/include/asterisk/rtp.h
trunk/main/channel.c
trunk/main/rtp.c
Modified: trunk/CHANGES
URL: http://svn.digium.com/view/asterisk/trunk/CHANGES?view=diff&rev=54838&r1=54837&r2=54838
==============================================================================
--- trunk/CHANGES (original)
+++ trunk/CHANGES Fri Feb 16 07:35:44 2007
@@ -101,3 +101,4 @@
"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
Modified: trunk/CREDITS
URL: http://svn.digium.com/view/asterisk/trunk/CREDITS?view=diff&rev=54838&r1=54837&r2=54838
==============================================================================
--- trunk/CREDITS (original)
+++ trunk/CREDITS Fri Feb 16 07:35:44 2007
@@ -137,6 +137,7 @@
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.
Modified: trunk/channels/chan_gtalk.c
URL: http://svn.digium.com/view/asterisk/trunk/channels/chan_gtalk.c?view=diff&rev=54838&r1=54837&r2=54838
==============================================================================
--- trunk/channels/chan_gtalk.c (original)
+++ trunk/channels/chan_gtalk.c Fri Feb 16 07:35:44 2007
@@ -188,7 +188,7 @@
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 @@
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;
Modified: trunk/channels/chan_mgcp.c
URL: http://svn.digium.com/view/asterisk/trunk/channels/chan_mgcp.c?view=diff&rev=54838&r1=54837&r2=54838
==============================================================================
--- trunk/channels/chan_mgcp.c (original)
+++ trunk/channels/chan_mgcp.c Fri Feb 16 07:35:44 2007
@@ -3960,7 +3960,7 @@
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;
Modified: trunk/channels/chan_sip.c
URL: http://svn.digium.com/view/asterisk/trunk/channels/chan_sip.c?view=diff&rev=54838&r1=54837&r2=54838
==============================================================================
--- trunk/channels/chan_sip.c (original)
+++ trunk/channels/chan_sip.c Fri Feb 16 07:35:44 2007
@@ -510,6 +510,7 @@
#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 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 @@
#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 @@
#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 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_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 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 @@
.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 @@
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 @@
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.
@@ -2859,6 +2876,10 @@
if ((!ast_test_flag(&dialog->flags[1], SIP_PAGE2_VIDEOSUPPORT) || !(dialog->capability & AST_FORMAT_VIDEO_MASK)) && dialog->vrtp) {
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)) {
@@ -2897,6 +2918,13 @@
ast_rtp_set_rtptimeout(dialog->vrtp, peer->rtptimeout);
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);
@@ -3181,6 +3209,8 @@
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 @@
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 @@
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. */
@@ -3775,6 +3812,23 @@
}
p->lastrtptx = time(NULL);
res = ast_rtp_write(p->vrtp, 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);
}
@@ -4010,7 +4064,10 @@
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 @@
/* 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 @@
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");
@@ -4095,6 +4166,9 @@
if (needvideo && i->vrtp) {
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 @@
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 @@
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 @@
/* 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);
@@ -4501,6 +4592,11 @@
ast_rtp_set_rtptimeout(p->vrtp, global_rtptimeout);
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);
@@ -4944,6 +5040,7 @@
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 @@
/* 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 @@
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 @@
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 @@
}
} 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;
@@ -5059,6 +5174,22 @@
if (debug)
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) )) {
@@ -5092,27 +5223,35 @@
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 @@
}
}
}
- /* 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 @@
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 @@
/* 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];
-
- ast_verbose("Capabilities: us - %s, peer - audio=%s/video=%s, combined - %s\n",
+ char s1[BUFSIZ], s2[BUFSIZ], s3[BUFSIZ], s4[BUFSIZ], s5[BUFSIZ];
+
+ 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 @@
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 @@
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 @@
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);
}
@@ -6186,6 +6348,52 @@
if ((*min_packet_size)==0 && fmt.cur_ms)
*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 @@
/*! \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 @@
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 @@
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 @@
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 @@
}
/* 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 @@
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 @@
}
/* 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 @@
&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 @@
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);
@@ -6597,6 +6866,11 @@
add_line(resp, m_video);
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 */
@@ -9359,6 +9633,11 @@
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 @@
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 @@
"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 @@
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 @@
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");
[... 657 lines stripped ...]
More information about the asterisk-commits
mailing list