mediastream.c 39.3 KB
Newer Older
aymeric's avatar
aymeric committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/*
mediastreamer2 library - modular sound and video processing and streaming
Copyright (C) 2006  Simon MORLAT (simon.morlat@linphone.org)

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/


smorlat's avatar
smorlat committed
21
#include <math.h>
22
#include "common.h"
smorlat's avatar
smorlat committed
23
#include "mediastreamer2/msequalizer.h"
24
#include "mediastreamer2/msvolume.h"
aymeric's avatar
aymeric committed
25 26 27 28
#ifdef VIDEO_ENABLED
#include "mediastreamer2/msv4l.h"
#endif

29
#include <ctype.h>
aymeric's avatar
aymeric committed
30 31 32 33
#include <signal.h>
#include <sys/types.h>
#ifndef WIN32
#include <unistd.h>
34
#include <poll.h>
35 36
#else
#include <malloc.h>
aymeric's avatar
aymeric committed
37 38 39 40 41
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

42 43 44
#ifdef __APPLE__
#include <CoreFoundation/CFRunLoop.h>
#endif
jehan's avatar
jehan committed
45 46
#if  TARGET_OS_IPHONE || defined (ANDROID)
#if TARGET_OS_IPHONE
jehan's avatar
jehan committed
47
#import <UIKit/UIKit.h>
jehan's avatar
jehan committed
48
#include <AudioToolbox/AudioToolbox.h>
Simon Morlat's avatar
Simon Morlat committed
49
#endif
jehan's avatar
jehan committed
50
extern void ms_set_video_stream(VideoStream* video);
jehan's avatar
jehan committed
51 52 53
#ifdef HAVE_X264
extern void libmsx264_init();
#endif
54 55 56
#ifdef HAVE_OPENH264
extern void libmsopenh264_init();
#endif
jehan's avatar
jehan committed
57 58
#ifdef HAVE_SILK
extern void libmssilk_init();
59
#endif
60 61 62
#ifdef HAVE_WEBRTC
extern void libmswebrtc_init();
#endif  
jehan's avatar
jehan committed
63
#endif
64

65 66 67 68 69
#ifdef ANDROID
#include <android/log.h>
#include <jni.h>
#endif

70 71
#include <ortp/b64.h>

72 73 74 75 76 77

#ifdef HAVE_CONFIG_H
#include "mediastreamer-config.h"
#endif


78 79 80
#define MEDIASTREAM_MAX_ICE_CANDIDATES 3


jehan's avatar
jehan committed
81

aymeric's avatar
aymeric committed
82 83 84
static int cond=1;


85 86 87 88 89 90
typedef struct _MediastreamIceCandidate {
	char ip[64];
	char type[6];
	int port;
} MediastreamIceCandidate;

91 92
typedef struct _MediastreamDatas {
	int localport,remoteport,payload;
93
	char ip[64];
94 95 96
	char *fmtp;
	int jitter;
	int bitrate;
97
	int mtu;
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
	MSVideoSize vs;
	bool_t ec;
	bool_t agc;
	bool_t eq;
	bool_t is_verbose;
	int device_rotation;
	VideoStream *video;
	char * capture_card;
	char * playback_card;
	char * camera;
	char *infile,*outfile;
	float ng_threshold;
	bool_t use_ng;
	bool_t two_windows;
	bool_t el;
113
	bool_t use_rc;
114
	
115
	bool_t enable_srtp;
116
	bool_t interactive;
117
	bool_t enable_avpf;
118
	bool_t enable_rtcp;
119 120 121
	
	bool_t freeze_on_error;
	bool_t pad[3];
122 123 124 125 126 127 128 129 130 131 132 133
	float el_speed;
	float el_thres;
	float el_force;
	int el_sustain;
	float el_transmit_thres;
	float ng_floorgain;
	char * zrtp_secrets;
	PayloadType *custom_pt;
	int video_window_id;
	int preview_window_id;
	/* starting values echo canceller */
	int ec_len_ms, ec_delay_ms, ec_framesize;
134 135
	char* srtp_local_master_key;
	char* srtp_remote_master_key;
136
	OrtpNetworkSimulatorParams netsim;
137
	float zoom;
138
	float zoom_cx, zoom_cy;
139 140

	AudioStream *audio;
141 142 143 144
	PayloadType *pt;
	RtpSession *session;
	OrtpEvQueue *q;
	RtpProfile *profile;
145

Ghislain MARY's avatar
Ghislain MARY committed
146
	IceSession *ice_session;
147 148 149 150
	MediastreamIceCandidate ice_local_candidates[MEDIASTREAM_MAX_ICE_CANDIDATES];
	MediastreamIceCandidate ice_remote_candidates[MEDIASTREAM_MAX_ICE_CANDIDATES];
	int ice_local_candidates_nb;
	int ice_remote_candidates_nb;
151
	char * video_display_filter;
152
	FILE * logfile;
153 154
} MediastreamDatas;

155

156 157 158 159 160 161 162
// MAIN METHODS
/* init default arguments */
MediastreamDatas* init_default_args();
/* parse args */
bool_t parse_args(int argc, char** argv, MediastreamDatas* out);
/* setup streams */
void setup_media_streams(MediastreamDatas* args);
163 164
/* run loop*/
void mediastream_run_loop(MediastreamDatas* args);
165 166 167 168
/* exit */
void clear_mediastreams(MediastreamDatas* args);

// HELPER METHODS
jehan's avatar
jehan committed
169
void stop_handler(int signum);
170
static bool_t parse_addr(const char *addr, char *ip, int len, int *port);
171
static bool_t parse_ice_addr(char* addr, char* type, int type_len, char* ip, int ip_len, int* port);
172 173 174 175
static void display_items(void *user_data, uint32_t csrc, rtcp_sdes_type_t t, const char *content, uint8_t content_len);
static void parse_rtcp(mblk_t *m);
static void parse_events(RtpSession *session, OrtpEvQueue *q);
static bool_t parse_window_ids(const char *ids, int* video_id, int* preview_id);
176

177 178
const char *usage="mediastream --local <port> --remote <ip:port> \n"
								"--payload <payload type number or payload name like 'audio/pmcu/8000'>\n"
Guillaume Beraudo's avatar
Guillaume Beraudo committed
179 180 181
								"[ --fmtp <fmtpline> ]\n"
								"[ --jitter <miliseconds> ]\n"
								"[ --width <pixels> ]\n"
smorlat's avatar
smorlat committed
182
								"[ --height <pixels> ]\n"
Guillaume Beraudo's avatar
Guillaume Beraudo committed
183 184
								"[ --bitrate <bits per seconds> ]\n"
								"[ --ec (enable echo canceller) ]\n"
185 186
								"[ --ec-tail <echo canceller tail length in ms> ]\n"
								"[ --ec-delay <echo canceller delay in ms> ]\n"
187
								"[ --ec-framesize <echo canceller framesize in samples> ]\n"
Guillaume Beraudo's avatar
Guillaume Beraudo committed
188 189 190 191 192 193 194 195
								"[ --agc (enable automatic gain control) ]\n"
								"[ --ng (enable noise gate)] \n"
								"[ --ng-threshold <(float) [0-1]> (noise gate threshold) ]\n"
								"[ --ng-floorgain <(float) [0-1]> (gain applied to the signal when its energy is below the threshold.) ]\n"
								"[ --capture-card <name> ]\n"
								"[ --playback-card <name> ]\n"
								"[ --infile	<input wav file> specify a wav file to be used for input, instead of soundcard ]\n"
								"[ --outfile <output wav file> specify a wav file to write audio into, instead of soundcard ]\n"
jehan's avatar
jehan committed
196 197 198 199 200 201
								"[ --camera <camera id as listed at startup> ]\n"
								"[ --el (enable echo limiter) ]\n"
								"[ --el-speed <(float) [0-1]> (gain changes are smoothed with a coefficent) ]\n"
								"[ --el-thres <(float) [0-1]> (Threshold above which the system becomes active) ]\n"
								"[ --el-force <(float) [0-1]> (The proportional coefficient controlling the mic attenuation) ]\n"
								"[ --el-sustain <(int)> (Time in milliseconds for which the attenuation is kept unchanged after) ]\n"
202
								"[ --el-transmit-thres <(float) [0-1]> (TO BE DOCUMENTED) ]\n"
Guillaume Beraudo's avatar
Guillaume Beraudo committed
203
								"[ --rc (enable adaptive rate control) ]\n"
Guillaume Beraudo's avatar
Guillaume Beraudo committed
204
								"[ --zrtp <secrets file> (enable zrtp) ]\n"
Guillaume Beraudo's avatar
Guillaume Beraudo committed
205
								"[ --verbose (most verbose messages) ]\n"
206
								"[ --video-windows-id <video surface:preview surface>]\n"
207
								"[ --video-display-filter <name> ]\n"
208 209 210 211 212 213 214
								"[ --srtp <local master_key> <remote master_key> (enable srtp, master key is generated if absent from comand line)]\n"
								"[ --netsim-bandwidth <bandwidth limit in bits/s> (simulates a network download bandwidth limit)]\n"
								"[ --netsim-lossrate <0-100> (simulates a network lost rate)]\n"
								"[ --netsim-consecutive-loss-probability <0-1> (to simulate bursts of lost packets)]\n"
								"[ --netsim-latency <latency in ms> (simulates a network latency)]\n"
								"[ --netsim-jitter-strength <0-100> (strength of the jitter simulation)]\n"
								"[ --netsim-jitter-burst-density <0-10> (density of gap/burst events, 1.0=one gap/burst per second in average)]\n" 
215
								"[ --zoom zoomfactor]\n"
216 217
								"[ --ice-local-candidate <ip:port:[host|srflx|prflx|relay]> ]\n"
								"[ --ice-remote-candidate <ip:port:[host|srflx|prflx|relay]> ]\n"
218
								"[ --mtu <mtu> (specify MTU)]\n"
219
								"[ --interactive (run in interactive mode)]\n"
220
								"[ --no-avpf]\n"
221
								"[ --freeze-on-error (for video, stop upon decoding error until next valid frame)]\n"
222
								"[ --no-rtcp]\n"
223
								"[ --log <file>]\n"
224
								;
aymeric's avatar
aymeric committed
225

jehan's avatar
jehan committed
226 227 228 229
#if TARGET_OS_IPHONE
int g_argc;
char** g_argv;
static int _main(int argc, char * argv[]);
230

jehan's avatar
jehan committed
231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249
static void* apple_main(void* data) {
	 _main(g_argc,g_argv);
	 return NULL;
	}
int main(int argc, char * argv[]) {
	pthread_t main_thread;
	g_argc=argc;
	g_argv=argv;
	pthread_create(&main_thread,NULL,apple_main,NULL);
	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
	int value = UIApplicationMain(0, nil, nil, nil);
	[pool release];
	return value;
	cond=0;
	pthread_join(main_thread,NULL);
	return 0;
}
static int _main(int argc, char * argv[])
#endif
250

jehan's avatar
jehan committed
251
#if !__APPLE__ && !ANDROID
aymeric's avatar
aymeric committed
252
int main(int argc, char * argv[])
jehan's avatar
jehan committed
253 254
#endif

255
#if !ANDROID && !TARGET_OS_MAC || TARGET_OS_IPHONE
aymeric's avatar
aymeric committed
256
{
257
	MediastreamDatas* args;
258
	cond = 1;
259

260
	args = init_default_args();
261

262 263
	if (!parse_args(argc, argv, args))
		return 0;
264

265
	setup_media_streams(args);
266

267
	mediastream_run_loop(args);
268

269
	clear_mediastreams(args);
270

271
	free(args);
272

273
	return 0;
274 275 276 277 278
}

#endif


279
MediastreamDatas* init_default_args() {
280
	MediastreamDatas* args = (MediastreamDatas*)ms_malloc0(sizeof(MediastreamDatas));
281 282 283 284 285 286 287 288 289 290
	args->localport=0;
	args->remoteport=0;
	args->payload=0;
	memset(args->ip, 0, sizeof(args->ip));
	args->fmtp=NULL;
	args->jitter=50;
	args->bitrate=0;
	args->ec=FALSE;
	args->agc=FALSE;
	args->eq=FALSE;
291
	args->interactive=FALSE;
292 293
	args->is_verbose=FALSE;
	args->device_rotation=-1;
aymeric's avatar
aymeric committed
294

295
#ifdef VIDEO_ENABLED
296
	args->video=NULL;
297
#endif
298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316
	args->capture_card=NULL;
	args->playback_card=NULL;
	args->camera=NULL;
	args->infile=args->outfile=NULL;
	args->ng_threshold=-1;
	args->use_ng=FALSE;
	args->two_windows=FALSE;
	args->el=FALSE;
	args->el_speed=-1;
	args->el_thres=-1;
	args->el_force=-1;
	args->el_sustain=-1;
	args->el_transmit_thres=-1;
	args->ng_floorgain=-1;
	args->use_rc=FALSE;
	args->zrtp_secrets=NULL;
	args->custom_pt=NULL;
	args->video_window_id = -1;
	args->preview_window_id = -1;
317
	args->enable_avpf = TRUE;
318
	args->enable_rtcp = TRUE;
319 320
	/* starting values echo canceller */
	args->ec_len_ms=args->ec_delay_ms=args->ec_framesize=0;
321 322
	args->enable_srtp = FALSE;
	args->srtp_local_master_key = args->srtp_remote_master_key = NULL;
323
	args->zoom = 1.0;
324
	args->zoom_cx = args->zoom_cy = 0.5;
325 326 327 328 329 330

	args->audio = NULL;
	args->session = NULL;
	args->pt = NULL;
	args->q = NULL;
	args->profile = NULL;
331
	args->logfile = NULL;
332

Ghislain MARY's avatar
Ghislain MARY committed
333
	args->ice_session = NULL;
334 335 336
	memset(args->ice_local_candidates, 0, sizeof(args->ice_local_candidates));
	memset(args->ice_remote_candidates, 0, sizeof(args->ice_remote_candidates));
	args->ice_local_candidates_nb = args->ice_remote_candidates_nb = 0;
337
	args->video_display_filter=NULL;
338

339
	return args;
340 341
}

342
bool_t parse_args(int argc, char** argv, MediastreamDatas* out) {
aymeric's avatar
aymeric committed
343
	int i;
jehan's avatar
jehan committed
344

aymeric's avatar
aymeric committed
345
	if (argc<4) {
Simon Morlat's avatar
Simon Morlat committed
346
		printf("%s",usage);
347
		return FALSE;
aymeric's avatar
aymeric committed
348
	}
349 350

	/* default size */
351 352
	out->vs.width=MS_VIDEO_SIZE_CIF_W;
	out->vs.height=MS_VIDEO_SIZE_CIF_H;
353

aymeric's avatar
aymeric committed
354 355 356
	for (i=1;i<argc;i++){
		if (strcmp(argv[i],"--local")==0){
			i++;
357
			out->localport=atoi(argv[i]);
aymeric's avatar
aymeric committed
358 359
		}else if (strcmp(argv[i],"--remote")==0){
			i++;
360
			if (!parse_addr(argv[i],out->ip,sizeof(out->ip),&out->remoteport)) {
Simon Morlat's avatar
Simon Morlat committed
361
				printf("%s",usage);
362
				return FALSE;
aymeric's avatar
aymeric committed
363
			}
364
			printf("Remote addr: ip=%s port=%i\n",out->ip,out->remoteport);
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392
		}else if (strcmp(argv[i],"--ice-local-candidate")==0) {
			MediastreamIceCandidate *candidate;
			i++;
			if (out->ice_local_candidates_nb>=MEDIASTREAM_MAX_ICE_CANDIDATES) {
				printf("Ignore ICE local candidate \"%s\" (maximum %d candidates allowed)\n",argv[i],MEDIASTREAM_MAX_ICE_CANDIDATES);
				continue;
			}
			candidate=&out->ice_local_candidates[out->ice_local_candidates_nb];
			if (!parse_ice_addr(argv[i],candidate->type,sizeof(candidate->type),candidate->ip,sizeof(candidate->ip),&candidate->port)) {
				printf("%s",usage);
				return FALSE;
			}
			out->ice_local_candidates_nb++;
			printf("ICE local candidate: type=%s ip=%s port=%i\n",candidate->type,candidate->ip,candidate->port);
		}else if (strcmp(argv[i],"--ice-remote-candidate")==0) {
			MediastreamIceCandidate *candidate;
			i++;
			if (out->ice_remote_candidates_nb>=MEDIASTREAM_MAX_ICE_CANDIDATES) {
				printf("Ignore ICE remote candidate \"%s\" (maximum %d candidates allowed)\n",argv[i],MEDIASTREAM_MAX_ICE_CANDIDATES);
				continue;
			}
			candidate=&out->ice_remote_candidates[out->ice_remote_candidates_nb];
			if (!parse_ice_addr(argv[i],candidate->type,sizeof(candidate->type),candidate->ip,sizeof(candidate->ip),&candidate->port)) {
				printf("%s",usage);
				return FALSE;
			}
			out->ice_remote_candidates_nb++;
			printf("ICE remote candidate: type=%s ip=%s port=%i\n",candidate->type,candidate->ip,candidate->port);
aymeric's avatar
aymeric committed
393 394
		}else if (strcmp(argv[i],"--payload")==0){
			i++;
395
			if (isdigit(argv[i][0])){
396
				out->payload=atoi(argv[i]);
397
			}else {
398
				out->payload=114;
399
				out->custom_pt=ms_tools_parse_custom_payload(argv[i]);
400
			}
aymeric's avatar
aymeric committed
401 402
		}else if (strcmp(argv[i],"--fmtp")==0){
			i++;
403
			out->fmtp=argv[i];
aymeric's avatar
aymeric committed
404 405
		}else if (strcmp(argv[i],"--jitter")==0){
			i++;
406
			out->jitter=atoi(argv[i]);
aymeric's avatar
aymeric committed
407 408
		}else if (strcmp(argv[i],"--bitrate")==0){
			i++;
409
			out->bitrate=atoi(argv[i]);
smorlat's avatar
smorlat committed
410 411
		}else if (strcmp(argv[i],"--width")==0){
			i++;
412
			out->vs.width=atoi(argv[i]);
smorlat's avatar
smorlat committed
413 414
		}else if (strcmp(argv[i],"--height")==0){
			i++;
415
			out->vs.height=atoi(argv[i]);
416 417
		}else if (strcmp(argv[i],"--capture-card")==0){
			i++;
418
			out->capture_card=argv[i];
Simon Morlat's avatar
Simon Morlat committed
419 420
		}else if (strcmp(argv[i],"--playback-card")==0){
			i++;
421
			out->playback_card=argv[i];
aymeric's avatar
aymeric committed
422
		}else if (strcmp(argv[i],"--ec")==0){
423
			out->ec=TRUE;
424 425
		}else if (strcmp(argv[i],"--ec-tail")==0){
			i++;
426
			out->ec_len_ms=atoi(argv[i]);
427 428
		}else if (strcmp(argv[i],"--ec-delay")==0){
			i++;
429
			out->ec_delay_ms=atoi(argv[i]);
430 431
		}else if (strcmp(argv[i],"--ec-framesize")==0){
			i++;
432
			out->ec_framesize=atoi(argv[i]);
433
		}else if (strcmp(argv[i],"--agc")==0){
434
			out->agc=TRUE;
smorlat's avatar
smorlat committed
435
		}else if (strcmp(argv[i],"--eq")==0){
436
			out->eq=TRUE;
437
		}else if (strcmp(argv[i],"--ng")==0){
438
			out->use_ng=1;
439
		}else if (strcmp(argv[i],"--rc")==0){
440
			out->use_rc=1;
441 442
		}else if (strcmp(argv[i],"--ng-threshold")==0){
			i++;
443
			out->ng_threshold=atof(argv[i]);
jehan's avatar
jehan committed
444 445
		}else if (strcmp(argv[i],"--ng-floorgain")==0){
			i++;
446
			out->ng_floorgain=atof(argv[i]);
447
		}else if (strcmp(argv[i],"--two-windows")==0){
448
			out->two_windows=TRUE;
449 450
		}else if (strcmp(argv[i],"--infile")==0){
			i++;
451
			out->infile=argv[i];
452 453
		}else if (strcmp(argv[i],"--outfile")==0){
			i++;
454
			out->outfile=argv[i];
455 456
		}else if (strcmp(argv[i],"--camera")==0){
			i++;
457
			out->camera=argv[i];
jehan's avatar
jehan committed
458
		}else if (strcmp(argv[i],"--el")==0){
459
			out->el=TRUE;
jehan's avatar
jehan committed
460 461
		}else if (strcmp(argv[i],"--el-speed")==0){
			i++;
462
			out->el_speed=atof(argv[i]);
jehan's avatar
jehan committed
463 464
		}else if (strcmp(argv[i],"--el-thres")==0){
			i++;
465
			out->el_thres=atof(argv[i]);
jehan's avatar
jehan committed
466 467
		}else if (strcmp(argv[i],"--el-force")==0){
			i++;
468
			out->el_force=atof(argv[i]);
jehan's avatar
jehan committed
469 470
		}else if (strcmp(argv[i],"--el-sustain")==0){
			i++;
471
			out->el_sustain=atoi(argv[i]);
jehan's avatar
jehan committed
472 473
		}else if (strcmp(argv[i],"--el-transmit-thres")==0){
			i++;
474
			out->el_transmit_thres=atof(argv[i]);
Guillaume Beraudo's avatar
Guillaume Beraudo committed
475
		} else if (strcmp(argv[i],"--zrtp")==0){
476
			out->zrtp_secrets=argv[++i];
Guillaume Beraudo's avatar
Guillaume Beraudo committed
477
		} else if (strcmp(argv[i],"--verbose")==0){
478
			out->is_verbose=TRUE;
479 480
		} else if (strcmp(argv[i], "--video-windows-id")==0) {
			i++;
481
			if (!parse_window_ids(argv[i],&out->video_window_id, &out->preview_window_id)) {
482
				printf("%s",usage);
483
				return FALSE;
484
			}
485
		} else if (strcmp(argv[i], "--device-rotation")==0) {
486
			i++;
487
			out->device_rotation=atoi(argv[i]);
488
		} else if (strcmp(argv[i], "--srtp")==0) {
jehan's avatar
jehan committed
489 490
			if (!media_stream_srtp_supported()) {
				ms_error("srtp support not enabled");
491 492 493 494 495 496 497 498 499
				return FALSE;
			}
			out->enable_srtp = TRUE;
			i++;
			// check if we're being given keys
			if (i + 1 < argc) {
				out->srtp_local_master_key = argv[i++];
				out->srtp_remote_master_key = argv[i++];
			}
500 501
		} else if (strcmp(argv[i],"--netsim-bandwidth")==0){
			i++;
502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536
			if (i<argc){
				out->netsim.max_bandwidth=atoi(argv[i]);
				out->netsim.enabled=TRUE;
			}else{
				ms_error("Missing argument for --netsim-bandwidth");
				return FALSE;
			}
		}else if (strcmp(argv[i],"--netsim-lossrate")==0){
			i++;
			if (i<argc){
				out->netsim.loss_rate=atoi(argv[i]);
				if (out->netsim.loss_rate < 0 || out->netsim.loss_rate>100) {
					ms_error("Loss rate must be between 0 and 100.");
					return FALSE;
				}
				out->netsim.enabled=TRUE;
			}else{
				ms_error("Missing argument for --netsim-lossrate");
				return FALSE;
			}
		}else if (strcmp(argv[i],"--netsim-consecutive-loss-probability")==0){
			i++;
			if (i<argc){
				sscanf(argv[i],"%f",&out->netsim.consecutive_loss_probability);
				if (out->netsim.consecutive_loss_probability < 0 || out->netsim.consecutive_loss_probability>1) {
					ms_error("The consecutive loss probability must be between 0 and 1.");
					return FALSE;
				}
				
				out->netsim.enabled=TRUE;
			}else{
				ms_error("Missing argument for --netsim-consecutive-loss-probability");
				return FALSE;
			}
		}else if (strcmp(argv[i], "--netsim-latency") == 0) {
Yann Diorcet's avatar
Yann Diorcet committed
537
			i++;
538 539 540 541 542 543
			if (i<argc){
				out->netsim.latency = atoi(argv[i]);
				out->netsim.enabled=TRUE;
			}else{
				ms_error("Missing argument for --netsim-latency");
				return FALSE;
Yann Diorcet's avatar
Yann Diorcet committed
544
			}
545 546 547 548 549 550 551 552 553 554 555 556
		}else if (strcmp(argv[i], "--netsim-jitter-burst-density") == 0) {
			i++;
			if (i<argc){
				sscanf(argv[i],"%f",&out->netsim.jitter_burst_density);
				if (out->netsim.jitter_burst_density<0 || out->netsim.jitter_burst_density>10){
					ms_error("The jitter burst density must be between 0 and 10");
					return FALSE;
				}
				out->netsim.enabled=TRUE;
			}else{
				ms_error("Missing argument for --netsim-jitter-burst-density");
				return FALSE;
Yann Diorcet's avatar
Yann Diorcet committed
557
			}
558
		}else if (strcmp(argv[i], "--netsim-jitter-strength") == 0) {
559
			i++;
560 561 562 563 564 565 566 567 568 569 570 571
			if (i<argc){
				sscanf(argv[i],"%f",&out->netsim.jitter_strength);
				if (out->netsim.jitter_strength<0 || out->netsim.jitter_strength>100){
					ms_error("The jitter strength must be between 0 and 100.");
					return FALSE;
				}
				out->netsim.enabled=TRUE;
			}else{
				ms_error("Missing argument for --netsim-jitter-strength");
				return FALSE;
			}
		}else if (strcmp(argv[i],"--zoom")==0){
572
			i++;
573 574 575 576
			if (sscanf(argv[i], "%f,%f,%f", &out->zoom, &out->zoom_cx, &out->zoom_cy) != 3) {
				ms_error("Invalid zoom triplet");
				return FALSE;
			}
577
		} else if (strcmp(argv[i],"--mtu")==0){
578 579 580 581 582
			i++;
			if (sscanf(argv[i], "%i", &out->mtu) != 1) {
				ms_error("Invalid mtu value");
				return FALSE;
			}
583
		} else if (strcmp(argv[i],"--interactive")==0){
584
			out->interactive=TRUE;
585 586
		} else if (strcmp(argv[i], "--no-avpf") == 0) {
			out->enable_avpf = FALSE;
587 588
		} else if (strcmp(argv[i], "--no-rtcp") == 0) {
			out->enable_rtcp = FALSE;
589
		} else if (strcmp(argv[i],"--help")==0) {
jehan's avatar
jehan committed
590
			printf("%s",usage);
591
			return FALSE;
592 593 594
		} else if (strcmp(argv[i],"--video-display-filter")==0) {
			i++;
			out->video_display_filter=argv[i];
595 596 597
		} else if (strcmp(argv[i], "--log") == 0) {
			i++;
			out->logfile = fopen(argv[i], "a+");
598 599
		} else if (strcmp(argv[i], "--freeze-on-error") == 0) {
			out->freeze_on_error=TRUE;
600
		} else {
601
			printf("%s",usage);
602 603
			printf("Unknown option '%s'\n", argv[i]);
			return FALSE;
aymeric's avatar
aymeric committed
604
		}
Guillaume Beraudo's avatar
Guillaume Beraudo committed
605
	}
606 607 608 609
	if (out->netsim.jitter_burst_density>0 && out->netsim.max_bandwidth==0){
		ms_error("Jitter probability settings requires --netsim-bandwidth to be set.");
		return FALSE;
	}
610 611
	return TRUE;
}
Guillaume Beraudo's avatar
Guillaume Beraudo committed
612

jehan's avatar
jehan committed
613

614 615 616 617 618
#ifdef VIDEO_ENABLED
static void video_stream_event_cb(void *user_pointer, const MSFilter *f, const unsigned int event_id, const void *args) {
	MediastreamDatas *md = (MediastreamDatas *)user_pointer;
	switch (event_id) {
		case MS_VIDEO_DECODER_DECODING_ERRORS:
Ghislain MARY's avatar
Ghislain MARY committed
619
			ms_warning("Decoding error on videostream [%p]", md->video);
620 621
			break;
		case MS_VIDEO_DECODER_FIRST_IMAGE_DECODED:
Ghislain MARY's avatar
Ghislain MARY committed
622
			ms_message("First video frame decoded successfully on videostream [%p]", md->video);
623 624 625 626 627 628
			break;
	}
}
#endif


629
void setup_media_streams(MediastreamDatas* args) {
Guillaume Beraudo's avatar
Guillaume Beraudo committed
630
	/*create the rtp session */
631
#ifdef VIDEO_ENABLED
Sylvain Berfini's avatar
Sylvain Berfini committed
632
	MSWebCam *cam=NULL;
633
#endif
Guillaume Beraudo's avatar
Guillaume Beraudo committed
634
	ortp_init();
635 636 637
	if (args->logfile)
		ortp_set_log_file(args->logfile);
	
638
	if (args->is_verbose) {
Guillaume Beraudo's avatar
Guillaume Beraudo committed
639 640 641
		ortp_set_log_level_mask(ORTP_DEBUG|ORTP_MESSAGE|ORTP_WARNING|ORTP_ERROR|ORTP_FATAL);
	} else {
		ortp_set_log_level_mask(ORTP_MESSAGE|ORTP_WARNING|ORTP_ERROR|ORTP_FATAL);
aymeric's avatar
aymeric committed
642
	}
643

644
    ms_init();
jehan's avatar
jehan committed
645

jehan's avatar
jehan committed
646
#if TARGET_OS_IPHONE || defined(ANDROID)
jehan's avatar
jehan committed
647
#if defined (HAVE_X264) && defined (VIDEO_ENABLED)
648
	libmsx264_init(); /*no plugin on IOS/Android */
jehan's avatar
jehan committed
649
#endif
650 651 652
#if defined (HAVE_OPENH264) && defined (VIDEO_ENABLED)
	libmsopenh264_init(); /*no plugin on IOS/Android */
#endif
jehan's avatar
jehan committed
653
#if defined (HAVE_SILK)
654
	libmssilk_init(); /*no plugin on IOS/Android */
jehan's avatar
jehan committed
655
#endif
656 657 658
#if defined (HAVE_WEBRTC)
	libmswebrtc_init();
#endif	
659 660

#endif /* IPHONE | ANDROID */
661

Guillaume Beraudo's avatar
Guillaume Beraudo committed
662 663 664 665
	rtp_profile_set_payload(&av_profile,110,&payload_type_speex_nb);
	rtp_profile_set_payload(&av_profile,111,&payload_type_speex_wb);
	rtp_profile_set_payload(&av_profile,112,&payload_type_ilbc);
	rtp_profile_set_payload(&av_profile,113,&payload_type_amr);
666
	rtp_profile_set_payload(&av_profile,114,args->custom_pt);
667
	rtp_profile_set_payload(&av_profile,115,&payload_type_lpc1015);
Guillaume Beraudo's avatar
Guillaume Beraudo committed
668 669 670 671 672 673 674
#ifdef VIDEO_ENABLED
	rtp_profile_set_payload(&av_profile,26,&payload_type_jpeg);
	rtp_profile_set_payload(&av_profile,98,&payload_type_h263_1998);
	rtp_profile_set_payload(&av_profile,97,&payload_type_theora);
	rtp_profile_set_payload(&av_profile,99,&payload_type_mp4v);
	rtp_profile_set_payload(&av_profile,100,&payload_type_x_snow);
	rtp_profile_set_payload(&av_profile,102,&payload_type_h264);
675
	rtp_profile_set_payload(&av_profile,103,&payload_type_vp8);
676

677
	args->video=NULL;
aymeric's avatar
aymeric committed
678
#endif
679 680
	args->profile=rtp_profile_clone_full(&av_profile);
	args->q=ortp_ev_queue_new();
jehan's avatar
jehan committed
681

682
	if (args->mtu) ms_set_mtu(args->mtu);
683 684
	ms_filter_enable_statistics(TRUE);
	ms_filter_reset_statistics();
Ghislain MARY's avatar
Ghislain MARY committed
685
	args->ice_session=ice_session_new();
Ghislain MARY's avatar
Ghislain MARY committed
686 687 688 689
	ice_session_set_remote_credentials(args->ice_session,"1234","1234567890abcdef123456");
	// ICE local credentials are assigned when creating the ICE session, but force them here to simplify testing
	ice_session_set_local_credentials(args->ice_session,"1234","1234567890abcdef123456");
	ice_dump_session(args->ice_session);
690

aymeric's avatar
aymeric committed
691
	signal(SIGINT,stop_handler);
692 693
	args->pt=rtp_profile_get_payload(args->profile,args->payload);
	if (args->pt==NULL){
694
		printf("Error: no payload defined with number %i.\n",args->payload);
aymeric's avatar
aymeric committed
695 696
		exit(-1);
	}
697
	if (args->enable_avpf == TRUE) {
Ghislain MARY's avatar
Ghislain MARY committed
698
		PayloadTypeAvpfParams avpf_params;
699
		payload_type_set_flag(args->pt, PAYLOAD_TYPE_RTCP_FEEDBACK_ENABLED);
Ghislain MARY's avatar
Ghislain MARY committed
700
		avpf_params.features = PAYLOAD_TYPE_AVPF_FIR | PAYLOAD_TYPE_AVPF_PLI | PAYLOAD_TYPE_AVPF_SLI | PAYLOAD_TYPE_AVPF_RPSI;
701
		avpf_params.trr_interval = 3000;
Ghislain MARY's avatar
Ghislain MARY committed
702
		payload_type_set_avpf_params(args->pt, avpf_params);
703 704 705
	} else {
		payload_type_unset_flag(args->pt, PAYLOAD_TYPE_RTCP_FEEDBACK_ENABLED);
	}
706 707
	if (args->fmtp!=NULL) payload_type_set_send_fmtp(args->pt,args->fmtp);
	if (args->bitrate>0) args->pt->normal_bitrate=args->bitrate;
708

709
	if (args->pt->normal_bitrate==0){
710
		fprintf(stderr,"Error: no default bitrate specified for codec %s/%i. "
711 712 713
			"Please specify a network bitrate with --bitrate option.\n",args->pt->mime_type,args->pt->clock_rate);
		exit(-1);
	}
aymeric's avatar
aymeric committed
714

715 716 717 718 719
	// do we need to generate srtp keys ?
	if (args->enable_srtp) {
		// default profile require key-length = 30 bytes
		//  -> input : 40 b64 encoded bytes
		if (!args->srtp_local_master_key) {
jehan's avatar
jehan committed
720 721
			char tmp[30];
			snprintf(tmp,sizeof(tmp),"%08x%08x%08x%08x",rand(),rand(),rand(),rand());
722 723 724 725 726 727
			args->srtp_local_master_key = (char*) malloc(41);
			b64_encode((const char*)tmp, 30, args->srtp_local_master_key, 40);
			args->srtp_local_master_key[40] = '\0';
			ms_message("Generated local srtp key: '%s'", args->srtp_local_master_key);
		}
		if (!args->srtp_remote_master_key) {
jehan's avatar
jehan committed
728 729
			char tmp[30];
			snprintf(tmp,sizeof(tmp),"%08x%08x%08x%08x",rand(),rand(),rand(),rand());
730 731 732 733 734
			args->srtp_remote_master_key = (char*) malloc(41);
			b64_encode((const char*)tmp, 30, args->srtp_remote_master_key, 40);
			args->srtp_remote_master_key[40] = '\0';
			ms_message("Generated remote srtp key: '%s'", args->srtp_remote_master_key);
		}
735
	}
736

737
	if (args->pt->type!=PAYLOAD_VIDEO){
738
		MSSndCardManager *manager=ms_snd_card_manager_get();
739 740 741 742
		MSSndCard *capt= args->capture_card==NULL ? ms_snd_card_manager_get_default_capture_card(manager) :
				ms_snd_card_manager_get_card(manager,args->capture_card);
		MSSndCard *play= args->playback_card==NULL ? ms_snd_card_manager_get_default_playback_card(manager) :
				ms_snd_card_manager_get_card(manager,args->playback_card);
743
		args->audio=audio_stream_new(args->localport,args->localport+1,ms_is_ipv6(args->ip));
744 745 746 747 748
		audio_stream_enable_automatic_gain_control(args->audio,args->agc);
		audio_stream_enable_noise_gate(args->audio,args->use_ng);
		audio_stream_set_echo_canceller_params(args->audio,args->ec_len_ms,args->ec_delay_ms,args->ec_framesize);
		audio_stream_enable_echo_limiter(args->audio,args->el);
		audio_stream_enable_adaptive_bitrate_control(args->audio,args->use_rc);
Simon Morlat's avatar
Simon Morlat committed
749
		printf("Starting audio stream.\n");
750

751
		audio_stream_start_full(args->audio,args->profile,args->ip,args->remoteport,args->ip,args->enable_rtcp?args->remoteport+1:-1, args->payload, args->jitter,args->infile,args->outfile,
752 753
		                        args->outfile==NULL ? play : NULL ,args->infile==NULL ? capt : NULL,args->infile!=NULL ? FALSE: args->ec);

754
		if (args->ice_local_candidates_nb || args->ice_remote_candidates_nb) {
Ghislain MARY's avatar
Ghislain MARY committed
755
			args->audio->ms.ice_check_list = ice_check_list_new();
756
			rtp_session_set_pktinfo(args->audio->ms.sessions.rtp_session,TRUE);
757
			ice_session_add_check_list(args->ice_session, args->audio->ms.ice_check_list, 0);
758
		}
759 760 761 762 763
		if (args->ice_local_candidates_nb) {
			MediastreamIceCandidate *candidate;
			int c;
			for (c=0;c<args->ice_local_candidates_nb;c++){
				candidate=&args->ice_local_candidates[c];
Ghislain MARY's avatar
Ghislain MARY committed
764 765
				ice_add_local_candidate(args->audio->ms.ice_check_list,candidate->type,candidate->ip,candidate->port,1,NULL);
				ice_add_local_candidate(args->audio->ms.ice_check_list,candidate->type,candidate->ip,candidate->port+1,2,NULL);
766 767 768
			}
		}
		if (args->ice_remote_candidates_nb) {
769
			char foundation[4];
770 771 772 773
			MediastreamIceCandidate *candidate;
			int c;
			for (c=0;c<args->ice_remote_candidates_nb;c++){
				candidate=&args->ice_remote_candidates[c];
774 775
				memset(foundation, '\0', sizeof(foundation));
				snprintf(foundation, sizeof(foundation) - 1, "%u", c + 1);
Ghislain MARY's avatar
Ghislain MARY committed
776 777
				ice_add_remote_candidate(args->audio->ms.ice_check_list,candidate->type,candidate->ip,candidate->port,1,0,foundation,FALSE);
				ice_add_remote_candidate(args->audio->ms.ice_check_list,candidate->type,candidate->ip,candidate->port+1,2,0,foundation,FALSE);
778 779 780
			}
		}

781 782 783 784 785 786 787 788 789 790 791 792
		if (args->audio) {
			if (args->el) {
				if (args->el_speed!=-1)
					ms_filter_call_method(args->audio->volsend,MS_VOLUME_SET_EA_SPEED,&args->el_speed);
				if (args->el_force!=-1)
					ms_filter_call_method(args->audio->volsend,MS_VOLUME_SET_EA_FORCE,&args->el_force);
				if (args->el_thres!=-1)
					ms_filter_call_method(args->audio->volsend,MS_VOLUME_SET_EA_THRESHOLD,&args->el_thres);
				if (args->el_sustain!=-1)
					ms_filter_call_method(args->audio->volsend,MS_VOLUME_SET_EA_SUSTAIN,&args->el_sustain);
				if (args->el_transmit_thres!=-1)
					ms_filter_call_method(args->audio->volsend,MS_VOLUME_SET_EA_TRANSMIT_THRESHOLD,&args->el_transmit_thres);
jehan's avatar
jehan committed
793 794

			}
795 796 797 798
			if (args->use_ng){
				if (args->ng_threshold!=-1) {
					ms_filter_call_method(args->audio->volsend,MS_VOLUME_SET_NOISE_GATE_THRESHOLD,&args->ng_threshold);
					ms_filter_call_method(args->audio->volrecv,MS_VOLUME_SET_NOISE_GATE_THRESHOLD,&args->ng_threshold);
jehan's avatar
jehan committed
799
				}
800 801 802
				if (args->ng_floorgain != -1) {
					ms_filter_call_method(args->audio->volsend,MS_VOLUME_SET_NOISE_GATE_FLOORGAIN,&args->ng_floorgain);
					ms_filter_call_method(args->audio->volrecv,MS_VOLUME_SET_NOISE_GATE_FLOORGAIN,&args->ng_floorgain);
jehan's avatar
jehan committed
803 804
				}
			}
Guillaume Beraudo's avatar
Guillaume Beraudo committed
805

806
            #ifndef TARGET_OS_IPHONE
Guillaume Beraudo's avatar
Guillaume Beraudo committed
807
			if (args->zrtp_secrets != NULL) {
808
				MSZrtpParams params;
809 810
				params.zid_file=args->zrtp_secrets;
				audio_stream_enable_zrtp(args->audio,&params);
Guillaume Beraudo's avatar
Guillaume Beraudo committed
811
			}
812
            #endif
Guillaume Beraudo's avatar
Guillaume Beraudo committed
813

814
			args->session=args->audio->ms.sessions.rtp_session;