[asterisk-commits] oej: trunk r116237 - in /trunk: ./ channels/ include/asterisk/ main/

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Wed May 14 08:37:07 CDT 2008


Author: oej
Date: Wed May 14 08:37:07 2008
New Revision: 116237

URL: http://svn.digium.com/view/asterisk?view=rev&rev=116237
Log:
Adding spport for T.140 RED - Simple RTP redundancy to prevent packet loss in text stream

Work sponsored by Omnitor AB, Stockholm, Sweden (http://www.omnitor.se)


Modified:
    trunk/CHANGES
    trunk/CREDITS
    trunk/channels/chan_sip.c
    trunk/include/asterisk/frame.h
    trunk/include/asterisk/rtp.h
    trunk/main/frame.c
    trunk/main/rtp.c

Modified: trunk/CHANGES
URL: http://svn.digium.com/view/asterisk/trunk/CHANGES?view=diff&rev=116237&r1=116236&r2=116237
==============================================================================
--- trunk/CHANGES (original)
+++ trunk/CHANGES Wed May 14 08:37:07 2008
@@ -77,6 +77,8 @@
    testing and problem reporting!
  * Added ability to specify registration expiry time on a per registration basis in
    the register line.
+ * Added support for T140 RED - redundancy in T.140 to prevent text loss due to
+   lost packets.
 
 IAX Changes
 -----------

Modified: trunk/CREDITS
URL: http://svn.digium.com/view/asterisk/trunk/CREDITS?view=diff&rev=116237&r1=116236&r2=116237
==============================================================================
--- trunk/CREDITS (original)
+++ trunk/CREDITS Wed May 14 08:37:07 2008
@@ -19,6 +19,9 @@
 
 John Todd, TalkPlus, Inc.  and JR Richardson, Ntegrated Solutions. - for funding
     the development of SIP Session Timers support.
+
+Omnitor AB, Gunnar Hellström, for funding work with videocaps, T.140 RED,
+originate with video/text and many more contributions.
 
 === WISHLIST CONTRIBUTERS ===
 Jeremy McNamara - SpeeX support

Modified: trunk/channels/chan_sip.c
URL: http://svn.digium.com/view/asterisk/trunk/channels/chan_sip.c?view=diff&rev=116237&r1=116236&r2=116237
==============================================================================
--- trunk/channels/chan_sip.c (original)
+++ trunk/channels/chan_sip.c Wed May 14 08:37:07 2008
@@ -1323,6 +1323,8 @@
 							(A bit unsure of this, please correct if
 							you know more) */
 	struct sip_st_dlg *stimer;		/*!< SIP Session-Timers */              
+  
+	int red; 
 };
 
 /*! Max entires in the history list for a sip_pvt */
@@ -5208,16 +5210,20 @@
 	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, FALSE);
-					ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT);	
+			if (p->red) {
+				red_buffer_t140(p->trtp, frame);
+			} else {
+				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, FALSE);
+						ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT);	
+					}
+					p->lastrtptx = time(NULL);
+					res = ast_rtp_write(p->trtp, frame);
 				}
-				p->lastrtptx = time(NULL);
-				res = ast_rtp_write(p->trtp, frame);
 			}
 			sip_pvt_unlock(p);
 		}
@@ -6651,6 +6657,13 @@
 
 	char buf[SIPBUFSIZE];
 	int rua_version;
+	
+	int red_data_pt[10];
+	int red_num_gen = 0;
+	int red_pt = 0;
+
+	char *red_cp; 				/* For T.140 red */
+	char red_fmtp[100] = "empty";		/* For T.140 red */
 
 	if (!p->rtp) {
 		ast_log(LOG_ERROR, "Got SDP but have no RTP session allocated.\n");
@@ -6993,6 +7006,20 @@
 			memset(&found_rtpmap_codecs, 0, sizeof(found_rtpmap_codecs));
 			last_rtpmap_codec = 0;
 			continue;
+			
+		} else if (!strncmp(a, red_fmtp, strlen(red_fmtp))) {
+			/* count numbers of generations in fmtp */
+			red_cp = &red_fmtp[strlen(red_fmtp)];
+			strncpy(red_fmtp, a, 100);
+
+			sscanf(red_cp, "%u", &red_data_pt[red_num_gen]);
+			red_cp = strtok(red_cp, "/");
+			while (red_cp && red_num_gen++ < RED_MAX_GENERATION) {
+				sscanf(red_cp, "%u", &red_data_pt[red_num_gen]);
+				red_cp = strtok(NULL, "/");
+			}
+			red_cp = red_fmtp;
+
 		} else if (sscanf(a, "rtpmap: %u %[^/]/", &codec, mimeSubtype) == 2) {
 			/* We have a rtpmap to handle */
 
@@ -7014,6 +7041,15 @@
 					if (p->trtp) {
 						/* ast_verbose("Adding t140 mimeSubtype to textrtp struct\n"); */
 						ast_rtp_set_rtpmap_type(newtextrtp, codec, "text", mimeSubtype, 0);
+					}
+				} else if (!strncasecmp(mimeSubtype, "RED", 3)) { /* Text with Redudancy */
+					if (p->trtp) {
+						ast_rtp_set_rtpmap_type(newtextrtp, codec, "text", mimeSubtype, 0);
+						red_pt = codec;
+						sprintf(red_fmtp, "fmtp:%d ", red_pt); 
+
+						if (debug)
+							ast_verbose("Red submimetype has payload type: %d\n", red_pt);
 					}
 				} else {                                          /* Must be audio?? */
 					if(ast_rtp_set_rtpmap_type(newaudiortp, codec, "audio", mimeSubtype,
@@ -7191,6 +7227,13 @@
 	p->peercapability = newpeercapability;		        /* The other sides capability in latest offer */
 	p->jointnoncodeccapability = newnoncodeccapability;	/* DTMF capabilities */
 
+	if (p->jointcapability & AST_FORMAT_T140RED) {
+		p->red = 1; 
+		rtp_red_init(p->trtp, 300, red_data_pt, 2);
+	} else {
+		p->red = 0; 
+	}
+
 	ast_rtp_pt_copy(p->rtp, newaudiortp);
 	if (p->vrtp)
 		ast_rtp_pt_copy(p->vrtp, newvideortp);
@@ -8103,6 +8146,14 @@
 	ast_str_append(a_buf, 0, "a=rtpmap:%d %s/%d\r\n", rtp_code,
 			 ast_rtp_lookup_mime_subtype(1, codec, 0), sample_rate);
 	/* Add fmtp code here */
+
+	if (codec == AST_FORMAT_T140RED) {
+		ast_str_append(a_buf, 0, "a=fmtp:%d %d/%d/%d\r\n", rtp_code, 
+			 ast_rtp_lookup_code(p->trtp, 1, AST_FORMAT_T140),
+			 ast_rtp_lookup_code(p->trtp, 1, AST_FORMAT_T140),
+			 ast_rtp_lookup_code(p->trtp, 1, AST_FORMAT_T140));
+
+	}
 }
 
 

Modified: trunk/include/asterisk/frame.h
URL: http://svn.digium.com/view/asterisk/trunk/include/asterisk/frame.h?view=diff&rev=116237&r1=116236&r2=116237
==============================================================================
--- trunk/include/asterisk/frame.h (original)
+++ trunk/include/asterisk/frame.h Wed May 14 08:37:07 2008
@@ -274,8 +274,12 @@
 /*! MPEG4 Video */
 #define AST_FORMAT_MP4_VIDEO	(1 << 22)
 #define AST_FORMAT_VIDEO_MASK   (((1 << 25)-1) & ~(AST_FORMAT_AUDIO_MASK))
-/*! T.140 Text format - ITU T.140, RFC 4351*/
-#define AST_FORMAT_T140		(1 << 25)
+/*! T.140 Text format - ITU T.140, RFC 4103 */
+#define AST_FORMAT_T140		(1 << 26)
+/*! T.140 RED Text format RFC 4103 */
+#define AST_FORMAT_T140RED      (1 << 27)
+/*! Maximum text mask */
+#define AST_FORMAT_MAX_TEXT	(1 << 28))
 #define AST_FORMAT_TEXT_MASK   (((1 << 30)-1) & ~(AST_FORMAT_AUDIO_MASK) & ~(AST_FORMAT_VIDEO_MASK))
 
 enum ast_control_frame_type {

Modified: trunk/include/asterisk/rtp.h
URL: http://svn.digium.com/view/asterisk/trunk/include/asterisk/rtp.h?view=diff&rev=116237&r1=116236&r2=116237
==============================================================================
--- trunk/include/asterisk/rtp.h (original)
+++ trunk/include/asterisk/rtp.h Wed May 14 08:37:07 2008
@@ -51,6 +51,9 @@
 /*! Maxmum number of payload defintions for a RTP session */
 #define MAX_RTP_PT			256
 
+/*! T.140 Redundancy Maxium number of generations */
+#define RED_MAX_GENERATION 5
+
 #define FLAG_3389_WARNING		(1 << 0)
 
 enum ast_rtp_options {
@@ -67,6 +70,8 @@
 };
 
 struct ast_rtp;
+/*! T.140 Redundancy structure*/
+struct rtp_red;
 
 /*! \brief This is the structure that binds a channel (SIP/Jingle/H.323) to the RTP subsystem 
 */
@@ -288,6 +293,22 @@
 /* \brief Put RTP timeout timers on hold during another transaction, like T.38 */
 void ast_rtp_set_rtptimers_onhold(struct ast_rtp *rtp);
 
+/*! \brief Initalize t.140 redudancy 
+ * \param ti time between each t140red frame is sent
+ * \param red_pt payloadtype for RTP packet
+ * \param pt payloadtype numbers for each generation including primary data
+ * \param num_gen number of redundant generations, primary data excluded
+ */
+int rtp_red_init(struct ast_rtp *rtp, int ti, int *pt, int num_gen);
+
+void red_init(struct rtp_red *red, const struct ast_frame *f);
+
+
+/*! \brief Buffer t.140 data */
+void red_buffer_t140(struct ast_rtp *rtp, struct ast_frame *f);
+
+
+
 #if defined(__cplusplus) || defined(c_plusplus)
 }
 #endif

Modified: trunk/main/frame.c
URL: http://svn.digium.com/view/asterisk/trunk/main/frame.c?view=diff&rev=116237&r1=116236&r2=116237
==============================================================================
--- trunk/main/frame.c (original)
+++ trunk/main/frame.c Wed May 14 08:37:07 2008
@@ -119,6 +119,7 @@
 	{ AST_FORMAT_H263_PLUS, "h263p", 0, "H.263+ Video" },                                                  /*!< H.263plus passthrough support See format_h263.c */
 	{ AST_FORMAT_H264, "h264", 0, "H.264 Video" },                                                         /*!< Passthrough support, see format_h263.c */
 	{ AST_FORMAT_MP4_VIDEO, "mpeg4", 0, "MPEG4 Video" },                                                   /*!< Passthrough support for MPEG4 */
+	{ AST_FORMAT_T140RED, "red", 1, "T.140 Realtime Text with redundancy"},                                 /*!< Redundant T.140 Realtime Text */
 	{ AST_FORMAT_T140, "t140", 0, "Passthrough T.140 Realtime Text" },                                     /*!< Passthrough support for T.140 Realtime Text */
 };
 
@@ -575,7 +576,7 @@
 	for (x = 0; x < sizeof(AST_FORMAT_LIST) / sizeof(AST_FORMAT_LIST[0]); x++) {
 		if (all || 
 			  !strcasecmp(AST_FORMAT_LIST[x].name,name) ||
-			  !strcasecmp(AST_FORMAT_LIST[x].name,ast_expand_codec_alias(name))) {
+			  !strcasecmp(AST_FORMAT_LIST[x].name, ast_expand_codec_alias(name))) {
 			format |= AST_FORMAT_LIST[x].bits;
 			if (!all)
 				break;

Modified: trunk/main/rtp.c
URL: http://svn.digium.com/view/asterisk/trunk/main/rtp.c?view=diff&rev=116237&r1=116236&r2=116237
==============================================================================
--- trunk/main/rtp.c (original)
+++ trunk/main/rtp.c Wed May 14 08:37:07 2008
@@ -177,6 +177,25 @@
 	struct sockaddr_in strict_rtp_address;  /*!< Remote address information for strict RTP purposes */
 
 	int set_marker_bit:1;           /*!< Whether to set the marker bit or not */
+	struct rtp_red *red;
+};
+
+static struct ast_frame *red_t140_to_red(struct rtp_red *red);
+static int red_write(const void *data);
+ 
+struct rtp_red {
+	struct ast_frame t140;  /*!< Primary data  */
+	struct ast_frame t140red;   /*!< Redundant t140*/
+	unsigned char pt[RED_MAX_GENERATION];  /*!< Payload types for redundancy data */
+	unsigned char ts[RED_MAX_GENERATION]; /*!< Time stamps */
+	unsigned char len[RED_MAX_GENERATION]; /*!< length of each generation */
+	int num_gen; /*!< Number of generations */
+	int schedid; /*!< Timer id */
+	int ti; /*!< How long to buffer data before send */
+	unsigned char t140red_data[64000];  
+	unsigned char buf_data[64000]; /*!< buffered primary data */
+	int hdrlen; 
+	long int prev_ts;
 };
 
 /* Forward declarations */
@@ -1392,6 +1411,7 @@
 	unsigned int *rtpheader;
 	struct rtpPayloadType rtpPT;
 	struct ast_rtp *bridged = NULL;
+	int prev_seqno;
 	
 	/* If time is up, kill it */
 	if (rtp->sending_digit)
@@ -1541,6 +1561,8 @@
 	}
 	if ( (int)rtp->lastrxseqno - (int)seqno  > 100) /* if so it would indicate that the sender cycled; allow for misordering */
 		rtp->cycles += RTP_SEQ_MOD;
+	
+	prev_seqno = rtp->lastrxseqno;
 
 	rtp->lastrxseqno = seqno;
 	
@@ -1604,6 +1626,61 @@
 	rtp->f.data = rtp->rawdata + hdrlen + AST_FRIENDLY_OFFSET;
 	rtp->f.offset = hdrlen + AST_FRIENDLY_OFFSET;
 	rtp->f.seqno = seqno;
+
+	if (rtp->f.subclass == AST_FORMAT_T140 && (int)seqno - (prev_seqno+1) > 0 && (int)seqno - (prev_seqno+1) < 10) {
+		  unsigned char *data = rtp->f.data;
+		  
+		  memmove(rtp->f.data+3, rtp->f.data, rtp->f.datalen);
+		  rtp->f.datalen +=3;
+		  *data++ = 0xEF;
+		  *data++ = 0xBF;
+		  *data = 0xBD;
+	}
+ 
+	if (rtp->f.subclass == AST_FORMAT_T140RED) {
+		unsigned char *data = rtp->f.data;
+		unsigned char *header_end;
+		int num_generations;
+		int header_length;
+		int len;
+		int diff =(int)seqno - (prev_seqno+1); /* if diff = 0, no drop*/
+		int x;
+
+		rtp->f.subclass = AST_FORMAT_T140;
+		header_end = memchr(data, ((*data) & 0x7f), rtp->f.datalen);
+		header_end++;
+		
+		header_length = header_end - data;
+		num_generations = header_length / 4;
+		len = header_length;
+
+		if (!diff) {
+			for (x = 0; x < num_generations; x++)
+				len += data[x * 4 + 3];
+			
+			if (!(rtp->f.datalen - len))
+				return &ast_null_frame;
+			
+			rtp->f.data += len;
+			rtp->f.datalen -= len;
+		} else if (diff > num_generations && diff < 10) {
+			len -= 3;
+			rtp->f.data += len;
+			rtp->f.datalen -= len;
+			
+			data = rtp->f.data;
+			*data++ = 0xEF;
+			*data++ = 0xBF;
+			*data = 0xBD;
+		} else 	{
+			for ( x = 0; x < num_generations - diff; x++) 
+				len += data[x * 4 + 3];
+			
+			rtp->f.data += len;
+			rtp->f.datalen -= len;
+		}
+	}
+
 	if (rtp->f.subclass & AST_FORMAT_AUDIO_MASK) {
 		rtp->f.samples = ast_codec_get_samples(&rtp->f);
 		if (rtp->f.subclass == AST_FORMAT_SLINEAR) 
@@ -1674,6 +1751,7 @@
 	{{1, AST_FORMAT_H263_PLUS}, "video", "h263-1998"},
 	{{1, AST_FORMAT_H264}, "video", "H264"},
 	{{1, AST_FORMAT_MP4_VIDEO}, "video", "MP4V-ES"},
+	{{1, AST_FORMAT_T140RED}, "text", "RED"},
 	{{1, AST_FORMAT_T140}, "text", "T140"},
 };
 
@@ -1713,9 +1791,10 @@
 	[98] = {1, AST_FORMAT_H263_PLUS},
 	[99] = {1, AST_FORMAT_H264},
 	[101] = {0, AST_RTP_DTMF},
-	[102] = {1, AST_FORMAT_T140},	/* Real time text chat */
 	[103] = {1, AST_FORMAT_H263_PLUS},
 	[104] = {1, AST_FORMAT_MP4_VIDEO},
+	[105] = {1, AST_FORMAT_T140RED},	/* Real time text chat (with redundancy encoding) */
+	[106] = {1, AST_FORMAT_T140},	/* Real time text chat */
 	[110] = {1, AST_FORMAT_SPEEX},
 	[111] = {1, AST_FORMAT_G726},
 	[112] = {1, AST_FORMAT_G726_AAL2},
@@ -2382,6 +2461,11 @@
 void ast_rtp_stop(struct ast_rtp *rtp)
 {
 	AST_SCHED_DEL(rtp->sched, rtp->rtcp->schedid);
+	if (rtp->red) {
+		AST_SCHED_DEL(rtp->sched, rtp->red->schedid);
+		free(rtp->red);
+		rtp->red = NULL;
+	}
 
 	memset(&rtp->them.sin_addr, 0, sizeof(rtp->them.sin_addr));
 	memset(&rtp->them.sin_port, 0, sizeof(rtp->them.sin_port));
@@ -3141,13 +3225,20 @@
 		return 0;
 
 	/* If there is no data length, return immediately */
-	if (!_f->datalen) 
+	if(!_f->datalen && !rtp->red)
 		return 0;
 	
 	/* Make sure we have enough space for RTP header */
 	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;
+	}
+
+	if (rtp->red) {
+		/* return 0; */
+		/* no primary data or generations to send */
+		if ((_f = red_t140_to_red(rtp->red)) == NULL) 
+			return 0;
 	}
 
 	/* The bottom bit of a video subclass contains the marker bit */
@@ -4267,3 +4358,111 @@
 	__ast_rtp_reload(0);
 }
 
+/*! \brief Write t140 redundacy frame 
+ * \param data primary data to be buffered
+ */
+static int red_write(const void *data)
+{
+	struct ast_rtp *rtp = (struct ast_rtp*) data;
+	
+	ast_rtp_write(rtp, &rtp->red->t140); 
+
+	return 1;  	
+}
+
+/*! \brief Construct a redundant frame 
+ * \param red redundant data structure
+ */
+static struct ast_frame *red_t140_to_red(struct rtp_red *red) {
+	unsigned char *data = red->t140red.data;
+	int len = 0;
+	int i;
+
+	/* replace most aged generation */
+	if (red->len[0]) {
+		for (i = 1; i < red->num_gen+1; i++)
+			len += red->len[i];
+
+		memmove(&data[red->hdrlen], &data[red->hdrlen+red->len[0]], len); 
+	}
+	
+	/* Store length of each generation and primary data length*/
+	for (i = 0; i < red->num_gen; i++)
+		red->len[i] = red->len[i+1];
+	red->len[i] = red->t140.datalen;
+	
+	/* write each generation length in red header */
+	len = red->hdrlen;
+	for (i = 0; i < red->num_gen; i++)
+		len += data[i*4+3] = red->len[i];
+	
+	/* add primary data to buffer */
+	memcpy(&data[len], red->t140.data, red->t140.datalen); 
+	red->t140red.datalen = len + red->t140.datalen;
+	
+	/* no primary data and no generations to send */
+	if (len == red->hdrlen && !red->t140.datalen)
+		return NULL;
+
+	/* reset t.140 buffer */
+	red->t140.datalen = 0; 
+	
+	return &red->t140red;
+}
+
+/*! \brief Initialize t140 redundancy 
+ * \param rtp
+ * \param ti buffer t140 for ti (msecs) before sending redundant frame
+ * \param red_data_pt Payloadtypes for primary- and generation-data
+ * \param num_gen numbers of generations (primary generation not encounted)
+ *
+*/
+int rtp_red_init(struct ast_rtp *rtp, int ti, int *red_data_pt, int num_gen)
+{
+	struct rtp_red *r;
+	int x;
+	
+	if (!(r = ast_calloc(1, sizeof(struct rtp_red))))
+		return -1;
+
+	r->t140.frametype = AST_FRAME_TEXT;
+	r->t140.subclass = AST_FORMAT_T140RED;
+	r->t140.data = &r->buf_data; 
+
+	r->t140.ts = 0;
+	r->t140red = r->t140;
+	r->t140red.data = &r->t140red_data;
+	r->t140red.datalen = 0;
+	r->ti = ti;
+	r->num_gen = num_gen;
+	r->hdrlen = num_gen * 4 + 1;
+	r->prev_ts = 0;
+
+	for (x = 0; x < num_gen; x++) {
+		r->pt[x] = red_data_pt[x];
+		r->pt[x] |= 1 << 7; /* mark redundant generations pt */ 
+		r->t140red_data[x*4] = r->pt[x];
+	}
+	r->t140red_data[x*4] = r->pt[x] = red_data_pt[x]; /* primary pt */
+	r->schedid = ast_sched_add(rtp->sched, ti, red_write, rtp);
+	rtp->red = r;
+
+	r->t140.datalen = 0;
+	
+	return 0;
+}
+
+/*! \brief Buffer t140 from chan_sip
+ * \param rtp
+ * \param f frame
+ */
+void red_buffer_t140(struct ast_rtp *rtp, struct ast_frame *f)
+{
+	if( f->datalen > -1 ) {
+		struct rtp_red *red = rtp->red;
+		memcpy(&red->buf_data[red->t140.datalen], f->data, f->datalen); 
+		red->t140.datalen += f->datalen;
+		red->t140.ts = f->ts;
+	}
+}
+




More information about the asterisk-commits mailing list