mediastream.c 43.4 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.
*/


21
#include <math.h>
22
#include "common.h"
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
#include <signal.h>
#include <sys/types.h>
32
#ifndef _WIN32
aymeric's avatar
aymeric committed
33
#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
45

46
#if TARGET_OS_IPHONE
jehan's avatar
jehan committed
47
#import <UIKit/UIKit.h>
jehan's avatar
jehan committed
48
#include <AudioToolbox/AudioToolbox.h>
49
#endif
50 51

#if  TARGET_OS_IPHONE || defined (ANDROID)
jehan's avatar
jehan committed
52
extern void ms_set_video_stream(VideoStream* video);
53
#if TARGET_OS_IPHONE || defined(HAVE_X264)
jehan's avatar
jehan committed
54 55
extern void libmsx264_init();
#endif
56
#if TARGET_OS_IPHONE || defined(HAVE_OPENH264)
57 58
extern void libmsopenh264_init();
#endif
59
#if TARGET_OS_IPHONE || defined(HAVE_SILK)
jehan's avatar
jehan committed
60
extern void libmssilk_init();
61
#endif
62
#if TARGET_OS_IPHONE || defined(HAVE_WEBRTC)
63
extern void libmswebrtc_init();
64
#endif
65
#endif // TARGET_OS_IPHONE || defined (ANDROID)
66

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

72 73
#include <ortp/b64.h>

74 75 76 77 78 79

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


80 81
#define MEDIASTREAM_MAX_ICE_CANDIDATES 3

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
	bool_t enable_speaker;
154 155
} MediastreamDatas;

156

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

// HELPER METHODS
170
void stop_handler(int signum);
171 172
static bool_t parse_addr(const char *addr, char *ip, size_t len, int *port);
static bool_t parse_ice_addr(char* addr, char* type, size_t type_len, char* ip, size_t ip_len, int* port);
173 174 175 176
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);
177

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

jehan's avatar
jehan committed
233 234 235 236
#if TARGET_OS_IPHONE
int g_argc;
char** g_argv;
static int _main(int argc, char * argv[]);
237

jehan's avatar
jehan committed
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256
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
257

jehan's avatar
jehan committed
258
#if !__APPLE__ && !ANDROID
aymeric's avatar
aymeric committed
259
int main(int argc, char * argv[])
jehan's avatar
jehan committed
260 261
#endif

262
#if !ANDROID && !TARGET_OS_MAC || TARGET_OS_IPHONE
aymeric's avatar
aymeric committed
263
{
264
	MediastreamDatas* args;
265
	cond = 1;
266

267
	args = init_default_args();
268

269 270
	if (!parse_args(argc, argv, args)){
		printf("%s",usage);
271
		return 0;
272
	}
273

274
	setup_media_streams(args);
275

276
	mediastream_run_loop(args);
277

278
	clear_mediastreams(args);
279

280
	free(args);
281

282
	return 0;
283 284 285 286 287
}

#endif


288
MediastreamDatas* init_default_args(void) {
289
	MediastreamDatas* args = (MediastreamDatas*)ms_malloc0(sizeof(MediastreamDatas));
290 291 292 293 294 295 296 297 298 299
	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;
300
	args->interactive=FALSE;
301 302
	args->is_verbose=FALSE;
	args->device_rotation=-1;
aymeric's avatar
aymeric committed
303

304
#ifdef VIDEO_ENABLED
305
	args->video=NULL;
306
#endif
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325
	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;
326
	args->enable_avpf = TRUE;
327
	args->enable_rtcp = TRUE;
328 329
	/* starting values echo canceller */
	args->ec_len_ms=args->ec_delay_ms=args->ec_framesize=0;
330 331
	args->enable_srtp = FALSE;
	args->srtp_local_master_key = args->srtp_remote_master_key = NULL;
332
	args->zoom = 1.0;
333
	args->zoom_cx = args->zoom_cy = 0.5;
334 335 336 337 338 339

	args->audio = NULL;
	args->session = NULL;
	args->pt = NULL;
	args->q = NULL;
	args->profile = NULL;
340
	args->logfile = NULL;
341

Ghislain MARY's avatar
Ghislain MARY committed
342
	args->ice_session = NULL;
343 344 345
	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;
346
	args->video_display_filter=NULL;
347

348
	return args;
349 350
}

351
bool_t parse_args(int argc, char** argv, MediastreamDatas* out) {
aymeric's avatar
aymeric committed
352
	int i;
jehan's avatar
jehan committed
353

aymeric's avatar
aymeric committed
354
	if (argc<4) {
355
		ms_error("Expected at least 3 arguments.\n");
356
		return FALSE;
aymeric's avatar
aymeric committed
357
	}
358 359

	/* default size */
360 361
	out->vs.width=MS_VIDEO_SIZE_CIF_W;
	out->vs.height=MS_VIDEO_SIZE_CIF_H;
362

aymeric's avatar
aymeric committed
363
	for (i=1;i<argc;i++){
364 365 366 367
		if (strcmp(argv[i],"--help")==0 || strcmp(argv[i],"-h")==0) {
			return FALSE;
		}else if (strcmp(argv[i],"--local")==0){
			char *is_invalid;
aymeric's avatar
aymeric committed
368
			i++;
369 370 371 372 373
			out->localport = strtol(argv[i],&is_invalid,10);
			if (*is_invalid!='\0'){
				ms_error("Failed to parse local port '%s'\n",argv[i]);
				return 0;
			}
aymeric's avatar
aymeric committed
374 375
		}else if (strcmp(argv[i],"--remote")==0){
			i++;
376
			if (!parse_addr(argv[i],out->ip,sizeof(out->ip),&out->remoteport)) {
377
				ms_error("Failed to parse remote address '%s'\n",argv[i]);
378
				return FALSE;
aymeric's avatar
aymeric committed
379
			}
380
			ms_message("Remote addr: ip=%s port=%i\n",out->ip,out->remoteport);
381 382 383 384
		}else if (strcmp(argv[i],"--ice-local-candidate")==0) {
			MediastreamIceCandidate *candidate;
			i++;
			if (out->ice_local_candidates_nb>=MEDIASTREAM_MAX_ICE_CANDIDATES) {
385
				ms_warning("Ignore ICE local candidate \"%s\" (maximum %d candidates allowed)\n",argv[i],MEDIASTREAM_MAX_ICE_CANDIDATES);
386 387 388 389
				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)) {
390
				ms_error("Failed to parse ICE local candidates '%s'\n", argv[i]);
391 392 393
				return FALSE;
			}
			out->ice_local_candidates_nb++;
394
			ms_message("ICE local candidate: type=%s ip=%s port=%i\n",candidate->type,candidate->ip,candidate->port);
395 396 397 398
		}else if (strcmp(argv[i],"--ice-remote-candidate")==0) {
			MediastreamIceCandidate *candidate;
			i++;
			if (out->ice_remote_candidates_nb>=MEDIASTREAM_MAX_ICE_CANDIDATES) {
399
				ms_warning("Ignore ICE remote candidate \"%s\" (maximum %d candidates allowed)\n",argv[i],MEDIASTREAM_MAX_ICE_CANDIDATES);
400 401 402 403
				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)) {
404
				ms_error("Failed to parse ICE remote candidates '%s'\n", argv[i]);
405 406 407
				return FALSE;
			}
			out->ice_remote_candidates_nb++;
408
			ms_message("ICE remote candidate: type=%s ip=%s port=%i\n",candidate->type,candidate->ip,candidate->port);
aymeric's avatar
aymeric committed
409 410
		}else if (strcmp(argv[i],"--payload")==0){
			i++;
411
			if (isdigit(argv[i][0])){
412
				out->payload=atoi(argv[i]);
413
			}else {
414
				out->payload=114;
415
				out->custom_pt=ms_tools_parse_custom_payload(argv[i]);
416
			}
aymeric's avatar
aymeric committed
417 418
		}else if (strcmp(argv[i],"--fmtp")==0){
			i++;
419
			out->fmtp=argv[i];
aymeric's avatar
aymeric committed
420 421
		}else if (strcmp(argv[i],"--jitter")==0){
			i++;
422
			out->jitter=atoi(argv[i]);
aymeric's avatar
aymeric committed
423 424
		}else if (strcmp(argv[i],"--bitrate")==0){
			i++;
425
			out->bitrate=atoi(argv[i]);
426 427
		}else if (strcmp(argv[i],"--width")==0){
			i++;
428
			out->vs.width=atoi(argv[i]);
429 430
		}else if (strcmp(argv[i],"--height")==0){
			i++;
431
			out->vs.height=atoi(argv[i]);
432 433
		}else if (strcmp(argv[i],"--capture-card")==0){
			i++;
434
			out->capture_card=argv[i];
Simon Morlat's avatar
Simon Morlat committed
435 436
		}else if (strcmp(argv[i],"--playback-card")==0){
			i++;
437
			out->playback_card=argv[i];
aymeric's avatar
aymeric committed
438
		}else if (strcmp(argv[i],"--ec")==0){
439
			out->ec=TRUE;
440 441
		}else if (strcmp(argv[i],"--ec-tail")==0){
			i++;
442
			out->ec_len_ms=atoi(argv[i]);
443 444
		}else if (strcmp(argv[i],"--ec-delay")==0){
			i++;
445
			out->ec_delay_ms=atoi(argv[i]);
446 447
		}else if (strcmp(argv[i],"--ec-framesize")==0){
			i++;
448
			out->ec_framesize=atoi(argv[i]);
449
		}else if (strcmp(argv[i],"--agc")==0){
450
			out->agc=TRUE;
smorlat's avatar
smorlat committed
451
		}else if (strcmp(argv[i],"--eq")==0){
452
			out->eq=TRUE;
453
		}else if (strcmp(argv[i],"--ng")==0){
454
			out->use_ng=1;
455
		}else if (strcmp(argv[i],"--rc")==0){
456
			out->use_rc=1;
457 458
		}else if (strcmp(argv[i],"--ng-threshold")==0){
			i++;
459
			out->ng_threshold=(float)atof(argv[i]);
jehan's avatar
jehan committed
460 461
		}else if (strcmp(argv[i],"--ng-floorgain")==0){
			i++;
462
			out->ng_floorgain=(float)atof(argv[i]);
463
		}else if (strcmp(argv[i],"--two-windows")==0){
464
			out->two_windows=TRUE;
465 466
		}else if (strcmp(argv[i],"--infile")==0){
			i++;
467
			out->infile=argv[i];
468 469
		}else if (strcmp(argv[i],"--outfile")==0){
			i++;
470
			out->outfile=argv[i];
471 472
		}else if (strcmp(argv[i],"--camera")==0){
			i++;
473
			out->camera=argv[i];
jehan's avatar
jehan committed
474
		}else if (strcmp(argv[i],"--el")==0){
475
			out->el=TRUE;
jehan's avatar
jehan committed
476 477
		}else if (strcmp(argv[i],"--el-speed")==0){
			i++;
478
			out->el_speed=(float)atof(argv[i]);
jehan's avatar
jehan committed
479 480
		}else if (strcmp(argv[i],"--el-thres")==0){
			i++;
481
			out->el_thres=(float)atof(argv[i]);
jehan's avatar
jehan committed
482 483
		}else if (strcmp(argv[i],"--el-force")==0){
			i++;
484
			out->el_force=(float)atof(argv[i]);
jehan's avatar
jehan committed
485 486
		}else if (strcmp(argv[i],"--el-sustain")==0){
			i++;
487
			out->el_sustain=atoi(argv[i]);
jehan's avatar
jehan committed
488 489
		}else if (strcmp(argv[i],"--el-transmit-thres")==0){
			i++;
490
			out->el_transmit_thres=(float)atof(argv[i]);
491
		} else if (strcmp(argv[i],"--zrtp")==0){
492
			out->zrtp_secrets=argv[++i];
493
		} else if (strcmp(argv[i],"--verbose")==0){
494
			out->is_verbose=TRUE;
495 496
		} else if (strcmp(argv[i], "--video-windows-id")==0) {
			i++;
497
			if (!parse_window_ids(argv[i],&out->video_window_id, &out->preview_window_id)) {
498
				ms_error("Failed to parse window ids '%s'\n",argv[i]);
499
				return FALSE;
500
			}
501
		} else if (strcmp(argv[i], "--device-rotation")==0) {
502
			i++;
503
			out->device_rotation=atoi(argv[i]);
504
		} else if (strcmp(argv[i], "--srtp")==0) {
505
			if (!ms_srtp_supported()) {
jehan's avatar
jehan committed
506
				ms_error("srtp support not enabled");
507 508 509 510 511 512 513 514 515
				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++];
			}
516 517
		} else if (strcmp(argv[i],"--netsim-bandwidth")==0){
			i++;
518
			if (i<argc){
519
				out->netsim.max_bandwidth=(float)atoi(argv[i]);
520 521 522 523 524 525 526 527
				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){
528
				out->netsim.loss_rate=(float)atoi(argv[i]);
529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545
				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;
				}
546

547 548 549 550 551 552
				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
553
			i++;
554 555 556 557 558 559
			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
560
			}
561 562 563 564 565 566 567 568 569 570 571 572
		}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
573
			}
574
		}else if (strcmp(argv[i], "--netsim-jitter-strength") == 0) {
575
			i++;
576 577 578 579 580 581 582 583 584 585 586
			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;
			}
587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602
		}else if (strcmp(argv[i], "--netsim-mode") == 0) {
			i++;
			if (i<argc){
				if (strcmp(argv[i],"inbound")==0)
					out->netsim.mode=OrtpNetworkSimulatorInbound;
				else if (strcmp(argv[i],"outbound")==0){
					out->netsim.mode=OrtpNetworkSimulatorOutbound;
				}else{
					ms_error("Invalid value for --netsim-mode");
					return FALSE;
				}
				out->netsim.enabled=TRUE;
			}else{
				ms_error("Missing argument for --netsim-dir");
				return FALSE;
			}
603
		}else if (strcmp(argv[i],"--zoom")==0){
604
			i++;
605 606 607 608
			if (sscanf(argv[i], "%f,%f,%f", &out->zoom, &out->zoom_cx, &out->zoom_cy) != 3) {
				ms_error("Invalid zoom triplet");
				return FALSE;
			}
609
		} else if (strcmp(argv[i],"--mtu")==0){
610 611 612 613 614
			i++;
			if (sscanf(argv[i], "%i", &out->mtu) != 1) {
				ms_error("Invalid mtu value");
				return FALSE;
			}
615
		} else if (strcmp(argv[i],"--interactive")==0){
616
			out->interactive=TRUE;
617 618
		} else if (strcmp(argv[i], "--no-avpf") == 0) {
			out->enable_avpf = FALSE;
619 620
		} else if (strcmp(argv[i], "--no-rtcp") == 0) {
			out->enable_rtcp = FALSE;
621
		} else if (strcmp(argv[i],"--help")==0) {
622
			return FALSE;
623 624 625
		} else if (strcmp(argv[i],"--video-display-filter")==0) {
			i++;
			out->video_display_filter=argv[i];
626 627 628
		} else if (strcmp(argv[i], "--log") == 0) {
			i++;
			out->logfile = fopen(argv[i], "a+");
629 630
		} else if (strcmp(argv[i], "--freeze-on-error") == 0) {
			out->freeze_on_error=TRUE;
631 632
		} else if (strcmp(argv[i], "--speaker") == 0) {
			out->enable_speaker=TRUE;
633
		} else {
634
			ms_error("Unknown option '%s'\n", argv[i]);
635
			return FALSE;
aymeric's avatar
aymeric committed
636
		}
637
	}
638 639 640 641
	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;
	}
642 643
	return TRUE;
}
644

jehan's avatar
jehan committed
645

646 647 648 649 650
#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
651
			ms_warning("Decoding error on videostream [%p]", md->video);
652 653
			break;
		case MS_VIDEO_DECODER_FIRST_IMAGE_DECODED:
Ghislain MARY's avatar
Ghislain MARY committed
654
			ms_message("First video frame decoded successfully on videostream [%p]", md->video);
655 656 657 658 659
			break;
	}
}
#endif

660 661 662 663 664 665 666 667 668 669 670 671 672 673 674
static MSSndCard *get_sound_card(MSSndCardManager *manager, const char* card_name) {
	MSSndCard *play = ms_snd_card_manager_get_card(manager,card_name);
	if (play == NULL) {
		const MSList *list = ms_snd_card_manager_get_list(manager);
		char * cards = ms_strdup("");
		while (list) {
			MSSndCard *card = (MSSndCard*)list->data;
			cards = ms_strcat_printf(cards, "- %s\n", ms_snd_card_get_string_id(card));
			list = list->next;
		}
		ms_fatal("Specified card '%s' but could not find it. Available cards are:\n%s", card_name, cards);
		ms_free(cards);
	}
	return play;
}
675

676
void setup_media_streams(MediastreamDatas* args) {
677
	/*create the rtp session */
678
#ifdef VIDEO_ENABLED
Sylvain Berfini's avatar
Sylvain Berfini committed
679
	MSWebCam *cam=NULL;
680
#endif
681
	MSFactory *factory;
682
	ortp_init();
683 684
	if (args->logfile)
		ortp_set_log_file(args->logfile);
685

686
	if (args->is_verbose) {
687
		ortp_set_log_level_mask(ORTP_LOG_DOMAIN, ORTP_DEBUG|ORTP_MESSAGE|ORTP_WARNING|ORTP_ERROR|ORTP_FATAL);
688
	} else {
689
		ortp_set_log_level_mask(ORTP_LOG_DOMAIN, ORTP_MESSAGE|ORTP_WARNING|ORTP_ERROR|ORTP_FATAL);
aymeric's avatar
aymeric committed
690
	}
691

692
	factory = ms_factory_new_with_voip();
jehan's avatar
jehan committed
693

694
#if TARGET_OS_IPHONE || defined(ANDROID)
695
#if TARGET_OS_IPHONE || (defined(HAVE_X264) && defined(VIDEO_ENABLED))
696
	libmsx264_init(); /*no plugin on IOS/Android */
jehan's avatar
jehan committed
697
#endif
698
#if TARGET_OS_IPHONE || (defined (HAVE_OPENH264) && defined (VIDEO_ENABLED))
699 700
	libmsopenh264_init(); /*no plugin on IOS/Android */
#endif
701
#if TARGET_OS_IPHONE || defined (HAVE_SILK)
702
	libmssilk_init(); /*no plugin on IOS/Android */
jehan's avatar
jehan committed
703
#endif
704
#if TARGET_OS_IPHONE || defined (HAVE_WEBRTC)
705
	libmswebrtc_init();
706
#endif
707 708

#endif /* IPHONE | ANDROID */
709

710 711 712 713
	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);
714
	rtp_profile_set_payload(&av_profile,114,args->custom_pt);
715
	rtp_profile_set_payload(&av_profile,115,&payload_type_lpc1015);
716
#ifdef VIDEO_ENABLED
717
	cam=ms_web_cam_new(ms_mire_webcam_desc_get());
718
	if (cam) ms_web_cam_manager_add_cam(ms_factory_get_web_cam_manager(factory), cam);
Simon Morlat's avatar
Simon Morlat committed
719
	cam=NULL;
720

721 722 723 724 725 726
	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);
727
	rtp_profile_set_payload(&av_profile,103,&payload_type_vp8);
728

729
	args->video=NULL;
aymeric's avatar
aymeric committed
730
#endif
731 732
	args->profile=rtp_profile_clone_full(&av_profile);
	args->q=ortp_ev_queue_new();
733

734 735 736
	if (args->mtu) ms_factory_set_mtu(factory, args->mtu);
	ms_factory_enable_statistics(factory, TRUE);
	ms_factory_reset_statistics(factory);
737

Ghislain MARY's avatar
Ghislain MARY committed
738
	args->ice_session=ice_session_new();
Ghislain MARY's avatar
Ghislain MARY committed
739 740 741 742
	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);
743

aymeric's avatar
aymeric committed
744
	signal(SIGINT,stop_handler);
745 746
	args->pt=rtp_profile_get_payload(args->profile,args->payload);
	if (args->pt==NULL){
747
		ms_error("No payload defined with number %i.\n",args->payload);
aymeric's avatar
aymeric committed
748 749
		exit(-1);
	}
750
	if (args->enable_avpf == TRUE) {
751
		PayloadTypeAvpfParams avpf_params;
752
		payload_type_set_flag(args->pt, PAYLOAD_TYPE_RTCP_FEEDBACK_ENABLED);
753
		avpf_params.features = PAYLOAD_TYPE_AVPF_FIR | PAYLOAD_TYPE_AVPF_PLI | PAYLOAD_TYPE_AVPF_SLI | PAYLOAD_TYPE_AVPF_RPSI;
754
		avpf_params.trr_interval = 3000;
755
		payload_type_set_avpf_params(args->pt, avpf_params);
756 757 758
	} else {
		payload_type_unset_flag(args->pt, PAYLOAD_TYPE_RTCP_FEEDBACK_ENABLED);
	}
759 760
	if (args->fmtp!=NULL) payload_type_set_send_fmtp(args->pt,args->fmtp);
	if (args->bitrate>0) args->pt->normal_bitrate=args->bitrate;
761

762
	if (args->pt->normal_bitrate==0){
763
		ms_error("Default bitrate specified for codec %s/%i. "
764 765 766
			"Please specify a network bitrate with --bitrate option.\n",args->pt->mime_type,args->pt->clock_rate);
		exit(-1);
	}
aymeric's avatar
aymeric committed
767

768 769 770 771 772
	// 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
773 774
			char tmp[30];
			snprintf(tmp,sizeof(tmp),"%08x%08x%08x%08x",rand(),rand(),rand(),rand());
775 776 777 778 779 780
			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
781 782
			char tmp[30];
			snprintf(tmp,sizeof(tmp),"%08x%08x%08x%08x",rand(),rand(),rand(),rand());
783 784 785 786 787
			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);
		}
788
	}
789

790
	if (args->pt->type!=PAYLOAD_VIDEO){
791
		MSSndCardManager *manager=ms_factory_get_snd_card_manager(factory);
792
		MSSndCard *capt= args->capture_card==NULL ? ms_snd_card_manager_get_default_capture_card(manager) :
793 794 795
				get_sound_card(manager,args->capture_card);
		MSSndCard *play= args->playback_card==NULL ? ms_snd_card_manager_get_default_capture_card(manager) :
				get_sound_card(manager,args->playback_card);
796
		args->audio=audio_stream_new(factory, args->localport,args->localport+1,ms_is_ipv6(args->ip));
797 798 799 800 801
		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);
802 803 804 805
		if (capt)
			ms_snd_card_set_preferred_sample_rate(capt,rtp_profile_get_payload(args->profile, args->payload)->clock_rate);
		if (play)
			ms_snd_card_set_preferred_sample_rate(play,rtp_profile_get_payload(args->profile, args->payload)->clock_rate);
806
		ms_message("Starting audio stream.\n");
807

808
		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,
809
								args->outfile==NULL ? play : NULL ,args->infile==NULL ? capt : NULL,args->infile!=NULL ? FALSE: args->ec);
810

811
		if (args->ice_local_candidates_nb || args->ice_remote_candidates_nb) {
812
			args->audio->ms.ice_check_list = ice_check_list_new();
813
			rtp_session_set_pktinfo(args->audio->ms.sessions.rtp_session,TRUE);
814
			ice_session_add_check_list(args->ice_session, args->audio->ms.ice_check_list, 0);
815
		}
816 817 818 819 820
		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];
821 822
				ice_add_local_candidate(args->audio->ms.ice_check_list,candidate->type,AF_INET,candidate->ip,candidate->port,1,NULL);
				ice_add_local_candidate(args->audio->ms.ice_check_list,candidate->type,AF_INET,candidate->ip,candidate->port+1,2,NULL);
823 824 825
			}
		}
		if (args->ice_remote_candidates_nb) {
826
			char foundation[4];
827 828 829 830
			MediastreamIceCandidate *candidate;
			int c;
			for (c=0;c<args->ice_remote_candidates_nb;c++){
				candidate=&args->ice_remote_candidates[c];
831 832
				memset(foundation, '\0', sizeof(foundation));
				snprintf(foundation, sizeof(foundation) - 1, "%u", c + 1);
833 834
				ice_add_remote_candidate(args->audio->ms.ice_check_list,candidate->type,AF_INET,candidate->ip,candidate->port,1,0,foundation,FALSE);
				ice_add_remote_candidate(args->audio->ms.ice_check_list,candidate->type,AF_INET,candidate->ip,candidate->port+1,2,0,foundation,FALSE);
835 836 837
			}
		}

838 839 840 841 842 843 844 845 846 847 848 849
		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
850 851

			}
852 853 854 855
			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
856
				}
857 858 859
				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
860 861
				}
			}
862

Guillaume Beraudo's avatar
Guillaume Beraudo committed
863
			if (args->zrtp_secrets != NULL) {
864
				MSZrtpParams params;
865 866
				params.zid_file=args->zrtp_secrets;
				audio_stream_enable_zrtp(args->audio,&params);
867
			}
868

869

870
			args->session=args->audio->ms.sessions.rtp_session;
871
		}
872

873
		if (args->enable_srtp) {
874
			ms_message("SRTP enabled: %d",
875
				audio_stream_enable_srtp(
876
					args->audio,
jehan's avatar
jehan committed
877
					MS_AES_128_SHA1_80,
878
					args->srtp_local_master_key,
879 880
					args->srtp_remote_master_key));
		}
881 882 883 884 885 886 887 888 889 890 891
	#if TARGET_OS_IPHONE
		if (args->enable_speaker) {
				ms_message("Setting audio route to spaker");
				UInt32 audioRouteOverride = kAudioSessionOverrideAudioRoute_Speaker;
				if (AudioSessionSetProperty(kAudioSessionProperty_OverrideAudioRoute, sizeof(audioRouteOverride),&audioRouteOverride) != kAudioSessionNoError) {
					ms_error("Cannot set route to speaker");
				};
		}
	#endif


aymeric's avatar
aymeric committed
892 893
	}else{
#ifdef VIDEO_ENABLED
894 895 896
		float zoom[] = {
			args->zoom,
			args->zoom_cx, args->zoom_cy };
897
		MSMediaStreamIO iodef = MS_MEDIA_STREAM_IO_INITIALIZER;
898

899
		if (args->eq){
smorlat's avatar
smorlat committed
900 901 902
			ms_fatal("Cannot put an audio equalizer in a video stream !");
			exit(-1);
		}
903
		ms_message("Starting video stream.\n");
904
		args->video=video_stream_new(factory, args->localport, args->localport+1, ms_is_ipv6(args->ip));
905 906
		if (args->video_display_filter)
			video_stream_set_display_filter_name(args->video, args->video_display_filter);
907

908
#ifdef ANDROID
909 910
		if (args->device_rotation >= 0)
			video_stream_set_device_rotation(args->video, args->device_rotation);
911
#endif
912 913
		video_stream_set_sent_video_size(args->video,args->vs);
		video_stream_use_preview_video_window(args->video,args->two_windows);
914
#if TARGET_OS_IPHONE
jehan's avatar
jehan committed
915 916 917
		NSBundle* myBundle = [NSBundle mainBundle];
		const char*  nowebcam = [[myBundle pathForResource:@"nowebcamCIF"ofType:@"jpg"] cStringUsingEncoding:[NSString defaultCStringEncoding]];
		ms_static_image_set_default_image(nowebcam);
918
		NSUInteger cpucount = [[NSProcessInfo processInfo] processorCount];
919 920
		ms_factory_set_cpu_count(args->audio->ms.factory, cpucount);
		//ms_set_cpu_count(cpucount);
jehan's avatar
jehan committed
921
#endif
922
		video_stream_set_event_callback(args->video,video_stream_event_cb, args);
923
		video_stream_set_freeze_on_error(args->video,args->freeze_on_error);
924
		video_stream_enable_adaptive_bitrate_control(args->video,args->use_rc);
925
		if (args->camera)
926
			cam=ms_web_cam_manager_get_cam(ms_factory_get_web_cam_manager(factory),args->camera);
927
		if (cam==NULL)
928
			cam=ms_web_cam_manager_get_default_cam(ms_factory_get_web_cam_manager(factory));
929

930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945
		if (args->infile){
			iodef.input.type = MSResourceFile;
			iodef.input.file = args->infile;
		}else{
			iodef.input.type = MSResourceCamera;
			iodef.input.camera = cam;
		}
		if (args->outfile){
			iodef.output.type = MSResourceFile;
			iodef.output.file = args->outfile;
		}else{
			iodef.output.type = MSResourceDefault;
			iodef.output.resource_arg = NULL;
		}
		rtp_session_set_jitter_compensation(args->video->ms.sessions.rtp_session, args->jitter);
		video_stream_start_from_io(args->video, args->profile,
946
					args->ip,args->remoteport,
947
					args->ip,args->enable_rtcp?args->remoteport+1:-1,
948
					args->payload,
949
					&iodef
950
					);
951
		args->session=args->video->ms.sessions.rtp_session;
952

953
		ms_filter_call_method(args->video->output,MS_VIDEO_DISPLAY_ZOOM, zoom);
954
		if (args->enable_srtp) {
955
			ms_message("SRTP enabled: %d",
956
				video_stream_enable_strp(
957
					args->video,
jehan's avatar
jehan committed
958
					MS_AES_128_SHA1_80,
959
					args->srtp_local_master_key,
960 961
					args->srtp_remote_master_key));
		}
aymeric's avatar
aymeric committed
962
#else
963
		ms_error("Error: video support not compiled.\n");
aymeric's avatar
aymeric committed
964 965
#endif
	}
966 967 968
	ice_session_set_base_for_srflx_candidates(args->ice_session);
	ice_session_compute_candidates_foundations(args->ice_session);
	ice_session_choose_default_candidates(args->ice_session);
Ghislain MARY's avatar
Ghislain MARY committed
969
	ice_session_choose_default_remote_candidates(args->ice_session);
Ghislain MARY's avatar
Ghislain MARY committed
970
	ice_session_start_connectivity_checks(args->ice_session);
971

972 973
	if (args->netsim.enabled){
		rtp_session_enable_network_simulation(args->session,&args->netsim);
974
	}
975 976 977
}


978
static void mediastream_tool_iterate(MediastreamDatas* args) {
979
#ifndef _WIN32
980 981
	struct pollfd pfd;
	int err;
982

983 984
	if (args->interactive){
		pfd.fd=STDIN_FILENO;
985
		pfd.events=POLLIN;
986
		pfd.revents=0;
987

988
		err=poll(&pfd,1,10);
989
		if (err==1 && (pfd.revents & POLLIN)){
990 991 992 993 994
			char commands[128];
			int intarg;
			commands[127]='\0';
			ms_sleep(1);  /* ensure following text be printed after ortp messages */
			if (args->eq)
995
			ms_message("\nPlease enter equalizer requests, such as 'eq active 1', 'eq active 0', 'eq 1200 0.1 200'\n");
996 997

			if (fgets(commands,sizeof(commands)-1,stdin)!=NULL){
998 999
				MSEqualizerGain d = {0};
				int active;
1000
				if (sscanf(commands,"eq active %i",&active)==1){
1001
					audio_stream_enable_equalizer(args->audio, args->audio->eq_loc, active);
1002
					ms_message("OK\n");
1003 1004
				}else if (sscanf(commands,"eq %f %f %f",&d.frequency,&d.gain,&d.width)==3){
					audio_stream_equalizer_set_gain(args->audio, args->audio->eq_loc, &d);
1005
					ms_message("OK\n");
1006 1007
				}else if (sscanf(commands,"eq %f %f",&d.frequency,&d.gain)==2){
					audio_stream_equalizer_set_gain(args->audio, args->audio->eq_loc, &d);
1008
					ms_message("OK\n");
1009 1010 1011
				}else if (strstr(commands,"dump")){
					int n=0,i;
					float *t;
1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025
					MSFilter *equalizer = NULL;
					if(args->audio->eq_loc == MSEqualizerHP) {
						equalizer = args->audio->spk_equalizer;
					} else if(args->audio->eq_loc == MSEqualizerMic) {
						equalizer = args->audio->mic_equalizer;
					}
					if(equalizer) {
						ms_filter_call_method(equalizer,MS_EQUALIZER_GET_NUM_FREQUENCIES,&n);
						t=(float*)alloca(sizeof(float)*n);
						ms_filter_call_method(equalizer,MS_EQUALIZER_DUMP_STATE,t);
						for(i=0;i<n;++i){
							if (fabs(t[i]-1)>0.01){
							ms_message("%i:%f:0 ",(i*args->pt->clock_rate)/(2*n),t[i]);
							}
1026
						}
1027
					}
1028
					ms_message("\nOK\n");
1029
				}else if (sscanf(commands,"lossrate %i",&intarg)==1){
1030 1031 1032 1033 1034 1035 1036 1037
					args->netsim.enabled=TRUE;
					args->netsim.loss_rate=intarg;
					rtp_session_enable_network_simulation(args->session,&args->netsim);
				}else if (sscanf(commands,"bandwidth %i",&intarg)==1){
					args->netsim.enabled=TRUE;
					args->netsim.max_bandwidth=intarg;
					rtp_session_enable_network_simulation(args->session,&args->netsim);
				}else if (strstr(commands,"quit")){
1038
					cond=0;
1039
				}else ms_warning("Cannot understand this.\n");
1040
			}
1041 1042
		}else if (err==-1 && errno!=EINTR){
			ms_fatal("mediastream's poll() returned %s",strerror(errno));
1043
		}
1044 1045
	}else{
		ms_usleep(10000);
1046 1047 1048 1049 1050 1051 1052 1053 1054 1055
	}
#else
	MSG msg;
	Sleep(10);
	while (PeekMessage(&msg, NULL, 0, 0,1)){
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	<