[Asterisk-code-review] Added RTT Support for PJSIP (asterisk[16])

Bharat Ramaswamy-Nandakumar asteriskteam at digium.com
Tue Dec 17 01:57:08 CST 2019


Bharat Ramaswamy-Nandakumar has uploaded this change for review. ( https://gerrit.asterisk.org/c/asterisk/+/13451 )


Change subject: Added RTT Support for PJSIP
......................................................................

Added RTT Support for PJSIP

Changes are:
1. chan_pjsip - added write_text function to the pjsip_tech driver
Added case for AST_FRAME_TEXT in chan_pjsip_write_stream
Added case to handle text in chan_pjsip_write

2. pjsip/dialplan_functions.c: added logic to handle media for TEXT

3.  res_pjsip.h: added variables to deal with endpoint media

4. channel.c: added  logic to add a stream_num for a frame

5.  frame.c: added code to handle and fix issues with red.

6. res_pjsip_sdp_rtp.c: added logic to setup SDP.
Added logic to handle tos and cos text
Added logic to RED for red enabled

7. res_pjsip_session.c: aded logic for max_text_streams

8. res_rtp_asterisk.c: Added logic to hanle Red and dealing with
a repeating RTP issue

9. pjsip_configuration.c: Added logic to handle text for PJSIP library

Issue ID: ASTERISK-28654

Reported-by: Bharat Ramaswamy Nandakumar(bharatram1)

Fixed by: Bharat Ramaswamy Nandakumar(bharatram1),
Seth Marks and Corey Wysong

Change-Id: I6b2ad94a065680ae9c4c933d338989e4b8c3a505
---
M channels/chan_pjsip.c
M channels/pjsip/dialplan_functions.c
M include/asterisk/res_pjsip.h
M main/channel.c
M main/frame.c
M res/res_pjsip/pjsip_configuration.c
M res/res_pjsip_sdp_rtp.c
M res/res_pjsip_session.c
M res/res_rtp_asterisk.c
9 files changed, 112 insertions(+), 8 deletions(-)



  git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/51/13451/1

diff --git a/channels/chan_pjsip.c b/channels/chan_pjsip.c
index 8ce5630..6176b5c 100644
--- a/channels/chan_pjsip.c
+++ b/channels/chan_pjsip.c
@@ -121,6 +121,7 @@
 	.read_stream = chan_pjsip_read_stream,
 	.write = chan_pjsip_write,
 	.write_stream = chan_pjsip_write_stream,
+	.write_text = chan_pjsip_write,
 	.exception = chan_pjsip_read_stream,
 	.indicate = chan_pjsip_indicate,
 	.transfer = chan_pjsip_transfer,
@@ -986,6 +987,19 @@
 		break;
 	case AST_FRAME_CNG:
 		break;
+	case AST_FRAME_TEXT:
+		if (!media) {
+			return 0;
+		} else if (media->type != AST_MEDIA_TYPE_TEXT) {
+			ast_debug(3, "Channel %s stream %d is of type '%s', not text!\n",
+				ast_channel_name(ast), stream_num, ast_codec_media_type2str(media->type));
+			return 0;
+        } else if(session->endpoint->media.red_enabled) {
+            ast_rtp_red_buffer(media->rtp, frame);
+        } else if (media->write_callback) {
+			res = media->write_callback(session, media, frame);
+		}
+		break;
 	case AST_FRAME_RTCP:
 		/* We only support writing out feedback */
 		if (frame->subclass.integer != AST_RTP_RTCP_PSFB || !media) {
@@ -1008,6 +1022,10 @@
 
 static int chan_pjsip_write(struct ast_channel *ast, struct ast_frame *frame)
 {
+	if (frame->frametype == AST_FRAME_TEXT && frame->stream_num != -1){
+		return chan_pjsip_write_stream(ast, frame->stream_num, frame);
+	}
+
 	return chan_pjsip_write_stream(ast, -1, frame);
 }
 
diff --git a/channels/pjsip/dialplan_functions.c b/channels/pjsip/dialplan_functions.c
index 7cc4506..21296ed 100644
--- a/channels/pjsip/dialplan_functions.c
+++ b/channels/pjsip/dialplan_functions.c
@@ -576,7 +576,9 @@
 		media = session->active_media_state->default_session[AST_MEDIA_TYPE_AUDIO];
 	} else if (!strcmp(field, "video")) {
 		media = session->active_media_state->default_session[AST_MEDIA_TYPE_VIDEO];
-	} else {
+	} else if (ast_strlen_zero(field) || !strcmp(field, "text")){
+        media = session->active_media_state->default_session[AST_MEDIA_TYPE_TEXT];
+    } else {
 		ast_log(AST_LOG_WARNING, "Unknown media type field '%s' for 'rtp' information\n", field);
 		return -1;
 	}
diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h
index a78311d..e49dfe6 100644
--- a/include/asterisk/res_pjsip.h
+++ b/include/asterisk/res_pjsip.h
@@ -705,6 +705,12 @@
 	unsigned int tos_video;
 	/*! Priority for video streams */
 	unsigned int cos_video;
+	/*! DSCP TOS bits for text streams */
+	unsigned int tos_text;
+	/*! Priority for text streams */
+	unsigned int cos_text;
+    /*! DSCP indicate if text stream supports RED */
+	unsigned int red_enabled;	
 	/*! Is g.726 packed in a non standard way */
 	unsigned int g726_non_standard;
 	/*! Bind the RTP instance to the media_address */
@@ -715,6 +721,8 @@
 	unsigned int max_audio_streams;
 	/*! Maximum number of video streams to offer/accept */
 	unsigned int max_video_streams;
+	/*! Maximum number of text streams to offer/accept */
+	unsigned int max_text_streams;
 	/*! Use BUNDLE */
 	unsigned int bundle;
 	/*! Enable webrtc settings and defaults */
diff --git a/main/channel.c b/main/channel.c
index c0a007e..09c222b 100644
--- a/main/channel.c
+++ b/main/channel.c
@@ -4698,6 +4698,7 @@
 		f.frametype = AST_FRAME_TEXT;
 		f.datalen = body_len;
 		f.mallocd = AST_MALLOCD_DATA;
+		f.stream_num = ast_stream_get_position(ast_channel_get_default_stream(chan, AST_MEDIA_TYPE_TEXT));
 		f.data.ptr = ast_strdup(body);
 		if (f.data.ptr) {
 			res = ast_channel_tech(chan)->write_text(chan, &f);
diff --git a/main/frame.c b/main/frame.c
index 208c82d..6d76244 100644
--- a/main/frame.c
+++ b/main/frame.c
@@ -302,7 +302,7 @@
 #endif
 
 	/* Start with standard stuff */
-	len = sizeof(*out) + AST_FRIENDLY_OFFSET + f->datalen;
+	len = sizeof(*out) + AST_FRIENDLY_OFFSET + f->datalen + 1;
 	/* If we have a source, add space for it */
 	/*
 	 * XXX Watch out here - if we receive a src which is not terminated
@@ -360,13 +360,16 @@
 	if (out->datalen || f->frametype == AST_FRAME_TEXT) {
 		out->data.ptr = buf + sizeof(*out) + AST_FRIENDLY_OFFSET;
 		memcpy(out->data.ptr, f->data.ptr, out->datalen);
+		if(f->frametype == AST_FRAME_TEXT) {
+			((char*)out->data.ptr)[out->datalen] = '\0';
+		}
 	} else {
 		out->data.uint32 = f->data.uint32;
 	}
 	if (srclen > 0) {
 		/* This may seem a little strange, but it's to avoid a gcc (4.2.4) compiler warning */
 		char *src;
-		out->src = buf + sizeof(*out) + AST_FRIENDLY_OFFSET + f->datalen;
+		out->src = buf + sizeof(*out) + AST_FRIENDLY_OFFSET + f->datalen + 1;
 		src = (char *) out->src;
 		/* Must have space since we allocated for it */
 		strcpy(src, f->src);
diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c
index e6937fb..22ca7aa 100644
--- a/res/res_pjsip/pjsip_configuration.c
+++ b/res/res_pjsip/pjsip_configuration.c
@@ -960,6 +960,8 @@
 		endpoint->media.tos_audio = value;
 	} else if (!strcmp(var->name, "tos_video")) {
 		endpoint->media.tos_video = value;
+	} else if (!strcmp(var->name, "tos_text")){
+		endpoint->media.tos_text = value;
 	} else {
 		/* If we reach this point, someone called the tos_handler when they shouldn't have. */
 		ast_assert(0);
@@ -988,6 +990,16 @@
 	return 0;
 }
 
+static int tos_text_to_str(const void *obj, const intptr_t *args, char **buf)
+{
+	const struct ast_sip_endpoint *endpoint = obj;
+
+	if (ast_asprintf(buf, "%u", endpoint->media.tos_text) == -1) {
+		return -1;
+	}
+	return 0;
+}
+
 static int from_user_handler(const struct aco_option *opt,
 	struct ast_variable *var, void *obj)
 {
@@ -1904,8 +1916,10 @@
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "sdp_session", "Asterisk", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, media.sdpsession));
 	ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "tos_audio", "0", tos_handler, tos_audio_to_str, NULL, 0, 0);
 	ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "tos_video", "0", tos_handler, tos_video_to_str, NULL, 0, 0);
+	ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "tos_text", "0", tos_handler, tos_text_to_str, NULL, 0, 0);
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "cos_audio", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, media.cos_audio));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "cos_video", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, media.cos_video));
+	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "cos_text", "5", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, media.cos_text));	
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "allow_subscribe", "yes", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, subscription.allow));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "sub_min_expiry", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, subscription.minexpiry));
 	ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "from_user", "", from_user_handler, from_user_to_str, NULL, 0, 0);
@@ -1945,6 +1959,7 @@
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "notify_early_inuse_ringing", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, notify_early_inuse_ringing));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "max_audio_streams", "1", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, media.max_audio_streams));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "max_video_streams", "1", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, media.max_video_streams));
+	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "max_text_streams", "1", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, media.max_text_streams));	
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "bundle", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.bundle));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "webrtc", "no", OPT_YESNO_T, 1, FLDSET(struct ast_sip_endpoint, media.webrtc));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "incoming_mwi_mailbox", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, incoming_mwi_mailbox));
diff --git a/res/res_pjsip_sdp_rtp.c b/res/res_pjsip_sdp_rtp.c
index 8d9cece..770b1c3 100644
--- a/res/res_pjsip_sdp_rtp.c
+++ b/res/res_pjsip_sdp_rtp.c
@@ -65,6 +65,7 @@
 
 static const char STR_AUDIO[] = "audio";
 static const char STR_VIDEO[] = "video";
+static const char STR_TEXT[] = "text";
 
 static int send_keepalive(const void *data)
 {
@@ -268,7 +269,12 @@
 			(session->endpoint->media.tos_audio || session->endpoint->media.cos_audio)) {
 		ast_rtp_instance_set_qos(session_media->rtp, session->endpoint->media.tos_audio,
 				session->endpoint->media.cos_audio, "SIP RTP Audio");
-	} else if (session_media->type == AST_MEDIA_TYPE_VIDEO) {
+	} else if (session_media->type == AST_MEDIA_TYPE_TEXT) {
+		if (session->endpoint->media.tos_text || session->endpoint->media.cos_text) {
+			ast_rtp_instance_set_qos(session_media->rtp, session->endpoint->media.tos_text,
+				session->endpoint->media.cos_text, "SIP RTP Text");
+		}
+    } else if (session_media->type == AST_MEDIA_TYPE_VIDEO) {
 		ast_rtp_instance_set_prop(session_media->rtp, AST_RTP_PROPERTY_RETRANS_RECV, session->endpoint->media.webrtc);
 		ast_rtp_instance_set_prop(session_media->rtp, AST_RTP_PROPERTY_RETRANS_SEND, session->endpoint->media.webrtc);
 		ast_rtp_instance_set_prop(session_media->rtp, AST_RTP_PROPERTY_REMB, session->endpoint->media.webrtc);
@@ -340,7 +346,33 @@
 				struct ast_format *format_parsed;
 
 				ast_copy_pj_str(fmt_param, &fmtp.fmt_param, sizeof(fmt_param));
+				if(strncasecmp(name, "RED", 3) == 0)
+					{
+						int red_data_pt[10];		/* For T.140 RED */
+						char red_cp_data[30];
+						char *red_cp = red_cp_data;
+						char *rest = NULL;
+						int red_num_gen = -1;
 
+						strcpy(red_cp,fmt_param);
+						red_cp = strtok_r(red_cp, "/", &rest);
+						while (red_cp && (red_num_gen)++ < AST_RED_MAX_GENERATION) {
+							sscanf(red_cp, "%30u", (unsigned *)&red_data_pt[red_num_gen]);
+							red_cp = strtok_r(NULL, "/", &rest);
+						}
+
+						if(red_num_gen > 0){
+							session->endpoint->media.red_enabled=1;
+							ast_rtp_red_init(session_media->rtp, 300, red_data_pt, red_num_gen);
+						}
+						else{
+							session->endpoint->media.red_enabled=0;
+						}
+
+					}
+					else{
+						session->endpoint->media.red_enabled=0;
+					}
 				format_parsed = ast_format_parse_sdp_fmtp(format, fmt_param);
 				if (format_parsed) {
 					ast_rtp_codecs_payload_replace_format(codecs, num, format_parsed);
@@ -2109,6 +2141,17 @@
 	.stream_destroy = stream_destroy,
 };
 
+/*! \brief SDP handler for 'text' media stream */
+static struct ast_sip_session_sdp_handler text_sdp_handler = {
+        .id = STR_TEXT,
+        .negotiate_incoming_sdp_stream = negotiate_incoming_sdp_stream,
+        .create_outgoing_sdp_stream = create_outgoing_sdp_stream,
+        .apply_negotiated_sdp_stream = apply_negotiated_sdp_stream,
+        .change_outgoing_sdp_stream_media_address = change_outgoing_sdp_stream_media_address,
+        .stream_stop = stream_stop,
+        .stream_destroy = stream_destroy,
+};
+
 static int video_info_incoming_request(struct ast_sip_session *session, struct pjsip_rx_data *rdata)
 {
 	struct pjsip_transaction *tsx;
@@ -2143,6 +2186,7 @@
 	ast_sip_session_unregister_supplement(&video_info_supplement);
 	ast_sip_session_unregister_sdp_handler(&video_sdp_handler, STR_VIDEO);
 	ast_sip_session_unregister_sdp_handler(&audio_sdp_handler, STR_AUDIO);
+    ast_sip_session_unregister_sdp_handler(&text_sdp_handler, STR_TEXT);
 
 	if (sched) {
 		ast_sched_context_destroy(sched);
@@ -2189,6 +2233,11 @@
 		goto end;
 	}
 
+	if (ast_sip_session_register_sdp_handler(&text_sdp_handler, STR_TEXT)) {
+        ast_log(LOG_ERROR, "Unable to register SDP handler for %s stream type\n", STR_TEXT);
+        goto end;
+    }
+	
 	ast_sip_session_register_supplement(&video_info_supplement);
 
 	return AST_MODULE_LOAD_SUCCESS;
diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c
index f6b9fa9..3de2dad 100644
--- a/res/res_pjsip_session.c
+++ b/res/res_pjsip_session.c
@@ -545,6 +545,7 @@
 		return (type_streams[type] > 0);
 	case AST_MEDIA_TYPE_UNKNOWN:
 	case AST_MEDIA_TYPE_TEXT:
+		return !(type_streams[type] < endpoint->media.max_text_streams);
 	default:
 		/* We don't want any unknown or "other" streams on our endpoint,
 		 * so always just say we've reached the limit
diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c
index c870fce..ec1067f 100644
--- a/res/res_rtp_asterisk.c
+++ b/res/res_rtp_asterisk.c
@@ -7128,9 +7128,10 @@
 		*data++ = 0xEF;
 		*data++ = 0xBF;
 		*data = 0xBD;
-	}
-
-	if (ast_format_cmp(rtp->f.subclass.format, ast_format_t140_red) == AST_FORMAT_CMP_EQUAL) {
+		if(rtp->f.datalen <= 0)	{
+			return AST_LIST_FIRST(&frames) ? AST_LIST_FIRST(&frames) : &ast_null_frame;
+		}
+	} else if (ast_format_cmp(rtp->f.subclass.format, ast_format_t140_red) == AST_FORMAT_CMP_EQUAL) {
 		unsigned char *data = rtp->f.data.ptr;
 		unsigned char *header_end;
 		int num_generations;
@@ -7175,6 +7176,9 @@
 			rtp->f.data.ptr += len;
 			rtp->f.datalen -= len;
 		}
+		if(rtp->f.datalen < 0) {
+			return AST_LIST_FIRST(&frames) ? AST_LIST_FIRST(&frames) : &ast_null_frame;
+		}
 	}
 
 	if (ast_format_get_type(rtp->f.subclass.format) == AST_MEDIA_TYPE_AUDIO) {
@@ -8056,9 +8060,12 @@
 {
 	struct ast_rtp_instance *instance = (struct ast_rtp_instance*) data;
 	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+	if(!rtp || !rtp->red) {
+		return (0);
+	}
 
 	ao2_lock(instance);
-	if (rtp->red->t140.datalen > 0) {
+	if (rtp && rtp->red && rtp->red->t140.datalen > 0) {
 		ast_rtp_write(instance, &rtp->red->t140);
 	}
 	ao2_unlock(instance);

-- 
To view, visit https://gerrit.asterisk.org/c/asterisk/+/13451
To unsubscribe, or for help writing mail filters, visit https://gerrit.asterisk.org/settings

Gerrit-Project: asterisk
Gerrit-Branch: 16
Gerrit-Change-Id: I6b2ad94a065680ae9c4c933d338989e4b8c3a505
Gerrit-Change-Number: 13451
Gerrit-PatchSet: 1
Gerrit-Owner: Bharat Ramaswamy-Nandakumar <bharat at indigital.net>
Gerrit-MessageType: newchange
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.digium.com/pipermail/asterisk-code-review/attachments/20191217/cd0bf8fc/attachment-0001.html>


More information about the asterisk-code-review mailing list