Adding Realtime Text support (T.140) to Asterisk
authorOlle Johansson <oej@edvina.net>
Fri, 16 Feb 2007 13:35:44 +0000 (13:35 +0000)
committerOlle Johansson <oej@edvina.net>
Fri, 16 Feb 2007 13:35:44 +0000 (13:35 +0000)
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.

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@54838 65c4cc65-6c06-0410-ace0-fbb531ad65f3

CHANGES
CREDITS
channels/chan_gtalk.c
channels/chan_mgcp.c
channels/chan_sip.c
channels/chan_skinny.c
include/asterisk/channel.h
include/asterisk/frame.h
include/asterisk/rtp.h
main/channel.c
main/rtp.c

diff --git a/CHANGES b/CHANGES
index aef532e..da57b18 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -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
diff --git a/CREDITS b/CREDITS
index 248a7ea..4afa69b 100644 (file)
--- a/CREDITS
+++ b/CREDITS
@@ -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.
 
index f84d6d8..23beb00 100644 (file)
@@ -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;
 
index 69a0cc0..acabdc7 100644 (file)
@@ -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;
index 20a14f0..f61ea3a 100644 (file)
@@ -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;
index f6f68ea..70b2cae 100644 (file)
@@ -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;
index e969594..22a0812 100644 (file)
@@ -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);
 
index 8d376b4..fa0b6f9 100644 (file)
@@ -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 */
index 85f3b19..5eea063 100644 (file)
@@ -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;
index 94e21d0..0405a7a 100644 (file)
@@ -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 */
index 21310e1..afb5c25 100644 (file)
@@ -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;