1 /*
2 mediastreamer2 library - modular sound and video processing and streaming
3 Copyright (C) 2006-2013 Belledonne Communications, Grenoble
4 
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18 */
19 
20 #include "mediastreamer2/mediastream.h"
21 #include "mediastreamer2/msrtt4103.h"
22 #include "mediastreamer2_tester.h"
23 #include "mediastreamer2_tester_private.h"
24 #include <math.h>
25 
26 #ifdef _MSC_VER
27 #define unlink _unlink
28 #endif
29 
30 static RtpProfile rtp_profile;
31 
32 #define T140_PAYLOAD_TYPE 98
33 #define T140_RED_PAYLOAD_TYPE 99
34 
35 static MSFactory *_factory = NULL;
tester_init(void)36 static int tester_init(void) {
37 	_factory = ms_factory_new_with_voip();
38 
39 	ortp_init();
40 	rtp_profile_set_payload(&rtp_profile, T140_PAYLOAD_TYPE, &payload_type_t140);
41 	rtp_profile_set_payload(&rtp_profile, T140_RED_PAYLOAD_TYPE, &payload_type_t140_red);
42 	return 0;
43 }
44 
tester_cleanup(void)45 static int tester_cleanup(void) {
46 
47 	ms_factory_destroy(_factory);
48 	rtp_profile_clear_all(&rtp_profile);
49 	return 0;
50 }
51 
52 typedef struct _text_stream_tester_stats_t {
53 	OrtpEvQueue *q;
54 	rtp_stats_t rtp;
55 	int number_of_received_char;
56 	char received_chars[4096];
57 } text_stream_tester_stats_t;
58 
59 typedef struct _text_stream_tester_t {
60 	TextStream *ts;
61 	text_stream_tester_stats_t stats;
62 	char* local_ip;
63 	int local_rtp;
64 	int local_rtcp;
65 	int payload_type;
66 } text_stream_tester_t;
67 
68 
reset_stats(text_stream_tester_stats_t * s)69 static void reset_stats(text_stream_tester_stats_t *s) {
70 	memset(s, 0, sizeof(text_stream_tester_stats_t));
71 }
72 
text_stream_tester_set_local_ip(text_stream_tester_t * obj,const char * ip)73 void text_stream_tester_set_local_ip(text_stream_tester_t* obj, const char* ip) {
74 	char* new_ip = ip ? ms_strdup(ip) : NULL;
75 	if (obj->local_ip) ms_free(obj->local_ip);
76 	obj->local_ip = new_ip;
77 }
78 
text_stream_tester_new(void)79 text_stream_tester_t* text_stream_tester_new(void) {
80 	text_stream_tester_t* tst = ms_new0(text_stream_tester_t, 1);
81 	text_stream_tester_set_local_ip(tst, "127.0.0.1");
82 	tst->local_rtp = -1; /*random*/
83 	tst->local_rtcp = -1; /*random*/
84 	return  tst;
85 }
86 
text_stream_tester_create(const char * local_ip,int local_rtp,int local_rtcp)87 text_stream_tester_t* text_stream_tester_create(const char* local_ip, int local_rtp, int local_rtcp) {
88 	text_stream_tester_t *tst = text_stream_tester_new();
89 	if (local_ip)
90 		text_stream_tester_set_local_ip(tst, local_ip);
91 	tst->local_rtp = local_rtp;
92 	tst->local_rtcp = local_rtcp;
93 	return tst;
94 }
95 
text_stream_tester_destroy(text_stream_tester_t * obj)96 void text_stream_tester_destroy(text_stream_tester_t* obj) {
97 	if(obj->local_ip) ms_free(obj->local_ip);
98 	ms_free(obj);
99 }
100 
create_text_stream(text_stream_tester_t * tst,int payload_type)101 static void create_text_stream(text_stream_tester_t *tst, int payload_type) {
102 	tst->ts = text_stream_new2(_factory, tst->local_ip, tst->local_rtp, tst->local_rtcp);
103 	tst->local_rtp = rtp_session_get_local_port(tst->ts->ms.sessions.rtp_session);
104 	tst->local_rtcp = rtp_session_get_local_rtcp_port(tst->ts->ms.sessions.rtp_session);
105 	reset_stats(&tst->stats);
106 	rtp_session_set_multicast_loopback(tst->ts->ms.sessions.rtp_session, TRUE);
107 	tst->stats.q = ortp_ev_queue_new();
108 	rtp_session_register_event_queue(tst->ts->ms.sessions.rtp_session, tst->stats.q);
109 	tst->payload_type = payload_type;
110 }
111 
destroy_text_stream(text_stream_tester_t * tst)112 static void destroy_text_stream(text_stream_tester_t *tst) {
113 	text_stream_stop(tst->ts);
114 }
115 
real_time_text_character_received(void * userdata,struct _MSFilter * f,unsigned int id,void * arg)116 static void real_time_text_character_received(void *userdata, struct _MSFilter *f, unsigned int id, void *arg) {
117 	if (id == MS_RTT_4103_RECEIVED_CHAR) {
118 		text_stream_tester_t *tst = (text_stream_tester_t *)userdata;
119 		if (tst->stats.q != NULL) {
120 			RealtimeTextReceivedCharacter *data = (RealtimeTextReceivedCharacter *)arg;
121 			ms_message("Received RTT char: %lu, %c", (unsigned long)data->character, (char)data->character);
122 			if (tst->stats.number_of_received_char < (int)sizeof(tst->stats.received_chars)-1){
123 				tst->stats.received_chars[tst->stats.number_of_received_char++] = (char)data->character;
124 			}else{
125 				ms_fatal("tst->stats.received_chars buffer overflow (number_of_received_char=%i)",
126 					tst->stats.number_of_received_char);
127 			}
128 		}
129 	}
130 }
131 
init_text_streams(text_stream_tester_t * tst1,text_stream_tester_t * tst2,bool_t avpf,bool_t one_way,OrtpNetworkSimulatorParams * params,int payload_type)132 static void init_text_streams(text_stream_tester_t *tst1, text_stream_tester_t *tst2, bool_t avpf, bool_t one_way, OrtpNetworkSimulatorParams *params, int payload_type) {
133 	create_text_stream(tst1, payload_type);
134 	create_text_stream(tst2, payload_type);
135 
136 	/* Configure network simulator. */
137 	if ((params != NULL) && (params->enabled == TRUE)) {
138 		rtp_session_enable_network_simulation(tst1->ts->ms.sessions.rtp_session, params);
139 		rtp_session_enable_network_simulation(tst2->ts->ms.sessions.rtp_session, params);
140 	}
141 
142 	text_stream_start(tst1->ts, &rtp_profile, tst2->local_ip, tst2->local_rtp, tst2->local_ip, tst2->local_rtcp, payload_type);
143 	ms_filter_add_notify_callback(tst1->ts->rttsink, real_time_text_character_received, tst1, TRUE);
144 	text_stream_start(tst2->ts, &rtp_profile, tst1->local_ip, tst1->local_rtp, tst1->local_ip, tst1->local_rtcp, payload_type);
145 	ms_filter_add_notify_callback(tst2->ts->rttsink, real_time_text_character_received, tst2, TRUE);
146 }
147 
uninit_text_streams(text_stream_tester_t * tst1,text_stream_tester_t * tst2)148 static void uninit_text_streams(text_stream_tester_t *tst1, text_stream_tester_t *tst2) {
149 	destroy_text_stream(tst1);
150 	destroy_text_stream(tst2);
151 }
152 
basic_text_stream(void)153 static void basic_text_stream(void) {
154 	text_stream_tester_t* marielle = text_stream_tester_new();
155 	text_stream_tester_t* margaux = text_stream_tester_new();
156 	const char* helloworld = "Hello World !";
157 	size_t i = 0;
158 	int strcmpresult = -2;
159 
160 	init_text_streams(marielle, margaux, FALSE, FALSE, NULL, T140_PAYLOAD_TYPE /* ignored */);
161 
162 	for (; i < strlen(helloworld); i++) {
163 		char c = helloworld[i];
164 		text_stream_putchar32(margaux->ts, (uint32_t)c);
165 	}
166 
167 	BC_ASSERT_TRUE(wait_for_until(&marielle->ts->ms, &margaux->ts->ms, &marielle->stats.number_of_received_char, (int)strlen(helloworld), 5000));
168 	ms_message("Received message is: %s", marielle->stats.received_chars);
169 	strcmpresult = strcmp(marielle->stats.received_chars, helloworld);
170 	BC_ASSERT_EQUAL(strcmpresult, 0, int, "%d");
171 
172 	uninit_text_streams(marielle, margaux);
173 	text_stream_tester_destroy(marielle);
174 	text_stream_tester_destroy(margaux);
175 }
176 
basic_text_stream2(void)177 static void basic_text_stream2(void) {
178 	text_stream_tester_t* marielle = text_stream_tester_new();
179 	text_stream_tester_t* margaux = text_stream_tester_new();
180 	const char* helloworld = "Hello World !";
181 	size_t i = 0;
182 	int strcmpresult = -2;
183 	int dummy = 0;
184 
185 	init_text_streams(marielle, margaux, FALSE, FALSE, NULL, T140_PAYLOAD_TYPE /* ignored */);
186 
187 	for (; i < strlen(helloworld); i++) {
188 		char c = helloworld[i];
189 		text_stream_putchar32(margaux->ts, (uint32_t)c);
190 		wait_for_until(&marielle->ts->ms, &margaux->ts->ms, &dummy, 1, 500);
191 	}
192 
193 	BC_ASSERT_TRUE(wait_for_until(&marielle->ts->ms, &margaux->ts->ms, &marielle->stats.number_of_received_char, (int)strlen(helloworld), 1000));
194 	ms_message("Received message is: %s", marielle->stats.received_chars);
195 	strcmpresult = strcmp(marielle->stats.received_chars, helloworld);
196 	BC_ASSERT_EQUAL(strcmpresult, 0, int, "%d");
197 
198 	uninit_text_streams(marielle, margaux);
199 	text_stream_tester_destroy(marielle);
200 	text_stream_tester_destroy(margaux);
201 }
202 
copy_paste_text_longer_than_rtt_buffer(void)203 static void copy_paste_text_longer_than_rtt_buffer(void) {
204 	text_stream_tester_t* marielle = text_stream_tester_new();
205 	text_stream_tester_t* margaux = text_stream_tester_new();
206 	const char* helloworld = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus imperdiet ultricies condimentum. Pellentesque tellus massa, maximus id dignissim vel, aliquam eget sapien. Suspendisse convallis est ut cursus suscipit. Duis in massa dui. Vivamus lobortis maximus nisi, eget interdum ante faucibus ac. Donec varius lorem id arcu facilisis, et dignissim magna molestie. Nunc lobortis feugiat dapibus. Nam tempus auctor dignissim. Sed pellentesque urna vitae quam mattis, in dictum justo tristique. Nullam vehicula enim eu lacus sollicitudin aliquet. Nunc eget arcu id odio viverra ultrices. Ut sit amet urna id libero posuere viverra dapibus sed nunc. Nulla eget vehicula magna, ut pulvinar ex. Nulla tincidunt justo at ipsum pretium, quis tempus arcu semper. Pellentesque non commodo neque. Maecenas consequat dapibus justo vel ornare. Suspendisse varius diam ac tincidunt fermentum. Etiam orci neque, malesuada sit amet purus vehicula, vestibulum scelerisque lectus. Proin volutpat venenatis enim a sollicitudin. Praesent posuere.";
207 	size_t i = 0;
208 	int strcmpresult = -2;
209 
210 	init_text_streams(marielle, margaux, FALSE, FALSE, NULL, T140_PAYLOAD_TYPE /* ignored */);
211 
212 	for (; i < strlen(helloworld); i++) {
213 		char c = helloworld[i];
214 		text_stream_putchar32(margaux->ts, (uint32_t)c);
215 	}
216 
217 	BC_ASSERT_FALSE(wait_for_until(&marielle->ts->ms, &margaux->ts->ms, &marielle->stats.number_of_received_char, (int)strlen(helloworld), 5000));
218 	ms_message("Received message is: %s", marielle->stats.received_chars);
219 	strcmpresult = strcmp(marielle->stats.received_chars, helloworld);
220 	BC_ASSERT_LOWER(strcmpresult, 0, int, "%d");
221 
222 	uninit_text_streams(marielle, margaux);
223 	text_stream_tester_destroy(marielle);
224 	text_stream_tester_destroy(margaux);
225 }
226 
227 #ifdef HAVE_SRTP
srtp_protected_text_stream(void)228 static void srtp_protected_text_stream(void) {
229 	text_stream_tester_t* marielle = text_stream_tester_new();
230 	text_stream_tester_t* margaux = text_stream_tester_new();
231 	const char* helloworld = "Hello World !";
232 	size_t i = 0;
233 	int strcmpresult = -2;
234 	int dummy = 0;
235 
236 	init_text_streams(marielle, margaux, FALSE, FALSE, NULL, T140_PAYLOAD_TYPE /* ignored */);
237 	BC_ASSERT_EQUAL(ms_media_stream_sessions_set_encryption_mandatory(&marielle->ts->ms.sessions, TRUE), 0, int, "%d");
238 
239 	BC_ASSERT_TRUE(ms_srtp_supported());
240 
241 	BC_ASSERT_EQUAL(ms_media_stream_sessions_set_srtp_send_key_b64(&(marielle->ts->ms.sessions), MS_AES_128_SHA1_32, "d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj"),0,int,"%d");
242 	BC_ASSERT_EQUAL(ms_media_stream_sessions_set_srtp_send_key_b64(&(margaux->ts->ms.sessions), MS_AES_128_SHA1_32, "6jCLmtRkVW9E/BUuJtYj/R2z6+4iEe06/DWohQ9F"),0,int,"%d");
243 	BC_ASSERT_EQUAL(ms_media_stream_sessions_set_srtp_recv_key_b64(&(margaux->ts->ms.sessions), MS_AES_128_SHA1_32, "d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj"),0,int,"%d");
244 	BC_ASSERT_EQUAL(ms_media_stream_sessions_set_srtp_recv_key_b64(&(marielle->ts->ms.sessions), MS_AES_128_SHA1_32, "6jCLmtRkVW9E/BUuJtYj/R2z6+4iEe06/DWohQ9F"),0,int,"%d");
245 
246 	BC_ASSERT_TRUE(media_stream_secured(&marielle->ts->ms));
247 	BC_ASSERT_TRUE(media_stream_secured(&margaux->ts->ms));
248 
249 	for (; i < strlen(helloworld); i++) {
250 		char c = helloworld[i];
251 		text_stream_putchar32(margaux->ts, (uint32_t)c);
252 		wait_for_until(&marielle->ts->ms, &margaux->ts->ms, &dummy, 1, 500);
253 	}
254 
255 	BC_ASSERT_TRUE(wait_for_until(&marielle->ts->ms, &margaux->ts->ms, &marielle->stats.number_of_received_char, (int)strlen(helloworld), 1000));
256 	ms_message("Received message is: %s", marielle->stats.received_chars);
257 	strcmpresult = strcmp(marielle->stats.received_chars, helloworld);
258 	BC_ASSERT_EQUAL(strcmpresult, 0, int, "%d");
259 
260 	uninit_text_streams(marielle, margaux);
261 	text_stream_tester_destroy(marielle);
262 	text_stream_tester_destroy(margaux);
263 }
264 #endif
265 
266 static test_t tests[] = {
267 	TEST_NO_TAG("Basic text stream: copy paste short text", basic_text_stream),
268 	TEST_NO_TAG("Basic text stream: slow typing", basic_text_stream2),
269 	TEST_NO_TAG("copy paste text longer than buffer size", copy_paste_text_longer_than_rtt_buffer),
270 #ifdef HAVE_SRTP
271 	TEST_NO_TAG("slow typing with SRTP", srtp_protected_text_stream),
272 #endif
273 };
274 
275 test_suite_t text_stream_test_suite = {
276 	"TextStream",
277 	tester_init,
278 	tester_cleanup,
279 	NULL,
280 	NULL,
281 	sizeof(tests) / sizeof(tests[0]),
282 	tests
283 };
284