mediastream.c 40 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 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
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>
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
#define MEDIASTREAM_MAX_ICE_CANDIDATES 3

Simon Morlat's avatar
Simon Morlat committed
80
extern MSWebCamDesc mire_desc;
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
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"
179 180 181
								"[ --fmtp <fmtpline> ]\n"
								"[ --jitter <miliseconds> ]\n"
								"[ --width <pixels> ]\n"
smorlat's avatar
smorlat committed
182
								"[ --height <pixels> ]\n"
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"
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"
203
								"[ --rc (enable adaptive rate control) ]\n"
Guillaume Beraudo's avatar
Guillaume Beraudo committed
204
								"[ --zrtp <secrets file> (enable zrtp) ]\n"
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
								"[ --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"
214 215
								"[ --netsim-jitter-burst-density <0-10> (density of gap/burst events, 1.0=one gap/burst per second in average)]\n"
								"[ --netsim-mode inbound|outboud (whether network simulation is applied to incoming (default) or outgoing stream)]\n"
216
								"[ --zoom zoomfactor]\n"
217 218
								"[ --ice-local-candidate <ip:port:[host|srflx|prflx|relay]> ]\n"
								"[ --ice-remote-candidate <ip:port:[host|srflx|prflx|relay]> ]\n"
219
								"[ --mtu <mtu> (specify MTU)]\n"
220
								"[ --interactive (run in interactive mode)]\n"
221
								"[ --no-avpf]\n"
222
								"[ --freeze-on-error (for video, stop upon decoding error until next valid frame)]\n"
223
								"[ --no-rtcp]\n"
224
								"[ --log <file>]\n"
225
								;
aymeric's avatar
aymeric committed
226

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

jehan's avatar
jehan committed
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250
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
251

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

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

261
	args = init_default_args();
262

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

266
	setup_media_streams(args);
267

268
	mediastream_run_loop(args);
269

270
	clear_mediastreams(args);
271

272
	free(args);
273

274
	return 0;
275 276 277 278 279
}

#endif


280
MediastreamDatas* init_default_args() {
281
	MediastreamDatas* args = (MediastreamDatas*)ms_malloc0(sizeof(MediastreamDatas));
282 283 284 285 286 287 288 289 290 291
	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;
292
	args->interactive=FALSE;
293 294
	args->is_verbose=FALSE;
	args->device_rotation=-1;
aymeric's avatar
aymeric committed
295

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

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

Ghislain MARY's avatar
Ghislain MARY committed
334
	args->ice_session = NULL;
335 336 337
	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;
338
	args->video_display_filter=NULL;
339

340
	return args;
341 342
}

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

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

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

aymeric's avatar
aymeric committed
355 356 357
	for (i=1;i<argc;i++){
		if (strcmp(argv[i],"--local")==0){
			i++;
358
			out->localport=atoi(argv[i]);
aymeric's avatar
aymeric committed
359 360
		}else if (strcmp(argv[i],"--remote")==0){
			i++;
361
			if (!parse_addr(argv[i],out->ip,sizeof(out->ip),&out->remoteport)) {
Simon Morlat's avatar
Simon Morlat committed
362
				printf("%s",usage);
363
				return FALSE;
aymeric's avatar
aymeric committed
364
			}
365
			printf("Remote addr: ip=%s port=%i\n",out->ip,out->remoteport);
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 393
		}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
394 395
		}else if (strcmp(argv[i],"--payload")==0){
			i++;
396
			if (isdigit(argv[i][0])){
397
				out->payload=atoi(argv[i]);
398
			}else {
399
				out->payload=114;
400
				out->custom_pt=ms_tools_parse_custom_payload(argv[i]);
401
			}
aymeric's avatar
aymeric committed
402 403
		}else if (strcmp(argv[i],"--fmtp")==0){
			i++;
404
			out->fmtp=argv[i];
aymeric's avatar
aymeric committed
405 406
		}else if (strcmp(argv[i],"--jitter")==0){
			i++;
407
			out->jitter=atoi(argv[i]);
aymeric's avatar
aymeric committed
408 409
		}else if (strcmp(argv[i],"--bitrate")==0){
			i++;
410
			out->bitrate=atoi(argv[i]);
411 412
		}else if (strcmp(argv[i],"--width")==0){
			i++;
413
			out->vs.width=atoi(argv[i]);
414 415
		}else if (strcmp(argv[i],"--height")==0){
			i++;
416
			out->vs.height=atoi(argv[i]);
417 418
		}else if (strcmp(argv[i],"--capture-card")==0){
			i++;
419
			out->capture_card=argv[i];
Simon Morlat's avatar
Simon Morlat committed
420 421
		}else if (strcmp(argv[i],"--playback-card")==0){
			i++;
422
			out->playback_card=argv[i];
aymeric's avatar
aymeric committed
423
		}else if (strcmp(argv[i],"--ec")==0){
424
			out->ec=TRUE;
425 426
		}else if (strcmp(argv[i],"--ec-tail")==0){
			i++;
427
			out->ec_len_ms=atoi(argv[i]);
428 429
		}else if (strcmp(argv[i],"--ec-delay")==0){
			i++;
430
			out->ec_delay_ms=atoi(argv[i]);
431 432
		}else if (strcmp(argv[i],"--ec-framesize")==0){
			i++;
433
			out->ec_framesize=atoi(argv[i]);
434
		}else if (strcmp(argv[i],"--agc")==0){
435
			out->agc=TRUE;
smorlat's avatar
smorlat committed
436
		}else if (strcmp(argv[i],"--eq")==0){
437
			out->eq=TRUE;
438
		}else if (strcmp(argv[i],"--ng")==0){
439
			out->use_ng=1;
440
		}else if (strcmp(argv[i],"--rc")==0){
441
			out->use_rc=1;
442 443
		}else if (strcmp(argv[i],"--ng-threshold")==0){
			i++;
444
			out->ng_threshold=atof(argv[i]);
jehan's avatar
jehan committed
445 446
		}else if (strcmp(argv[i],"--ng-floorgain")==0){
			i++;
447
			out->ng_floorgain=atof(argv[i]);
448
		}else if (strcmp(argv[i],"--two-windows")==0){
449
			out->two_windows=TRUE;
450 451
		}else if (strcmp(argv[i],"--infile")==0){
			i++;
452
			out->infile=argv[i];
453 454
		}else if (strcmp(argv[i],"--outfile")==0){
			i++;
455
			out->outfile=argv[i];
456 457
		}else if (strcmp(argv[i],"--camera")==0){
			i++;
458
			out->camera=argv[i];
jehan's avatar
jehan committed
459
		}else if (strcmp(argv[i],"--el")==0){
460
			out->el=TRUE;
jehan's avatar
jehan committed
461 462
		}else if (strcmp(argv[i],"--el-speed")==0){
			i++;
463
			out->el_speed=atof(argv[i]);
jehan's avatar
jehan committed
464 465
		}else if (strcmp(argv[i],"--el-thres")==0){
			i++;
466
			out->el_thres=atof(argv[i]);
jehan's avatar
jehan committed
467 468
		}else if (strcmp(argv[i],"--el-force")==0){
			i++;
469
			out->el_force=atof(argv[i]);
jehan's avatar
jehan committed
470 471
		}else if (strcmp(argv[i],"--el-sustain")==0){
			i++;
472
			out->el_sustain=atoi(argv[i]);
jehan's avatar
jehan committed
473 474
		}else if (strcmp(argv[i],"--el-transmit-thres")==0){
			i++;
475
			out->el_transmit_thres=atof(argv[i]);
476
		} else if (strcmp(argv[i],"--zrtp")==0){
477
			out->zrtp_secrets=argv[++i];
478
		} else if (strcmp(argv[i],"--verbose")==0){
479
			out->is_verbose=TRUE;
480 481
		} else if (strcmp(argv[i], "--video-windows-id")==0) {
			i++;
482
			if (!parse_window_ids(argv[i],&out->video_window_id, &out->preview_window_id)) {
483
				printf("%s",usage);
484
				return FALSE;
485
			}
486
		} else if (strcmp(argv[i], "--device-rotation")==0) {
487
			i++;
488
			out->device_rotation=atoi(argv[i]);
489
		} else if (strcmp(argv[i], "--srtp")==0) {
490
			if (!ms_srtp_supported()) {
jehan's avatar
jehan committed
491
				ms_error("srtp support not enabled");
492 493 494 495 496 497 498 499 500
				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++];
			}
501 502
		} else if (strcmp(argv[i],"--netsim-bandwidth")==0){
			i++;
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 537
			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
538
			i++;
539 540 541 542 543 544
			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
545
			}
546 547 548 549 550 551 552 553 554 555 556 557
		}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
558
			}
559
		}else if (strcmp(argv[i], "--netsim-jitter-strength") == 0) {
560
			i++;
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;
			}
572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587
		}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;
			}
588
		}else if (strcmp(argv[i],"--zoom")==0){
589
			i++;
590 591 592 593
			if (sscanf(argv[i], "%f,%f,%f", &out->zoom, &out->zoom_cx, &out->zoom_cy) != 3) {
				ms_error("Invalid zoom triplet");
				return FALSE;
			}
594
		} else if (strcmp(argv[i],"--mtu")==0){
595 596 597 598 599
			i++;
			if (sscanf(argv[i], "%i", &out->mtu) != 1) {
				ms_error("Invalid mtu value");
				return FALSE;
			}
600
		} else if (strcmp(argv[i],"--interactive")==0){
601
			out->interactive=TRUE;
602 603
		} else if (strcmp(argv[i], "--no-avpf") == 0) {
			out->enable_avpf = FALSE;
604 605
		} else if (strcmp(argv[i], "--no-rtcp") == 0) {
			out->enable_rtcp = FALSE;
606
		} else if (strcmp(argv[i],"--help")==0) {
jehan's avatar
jehan committed
607
			printf("%s",usage);
608
			return FALSE;
609 610 611
		} else if (strcmp(argv[i],"--video-display-filter")==0) {
			i++;
			out->video_display_filter=argv[i];
612 613 614
		} else if (strcmp(argv[i], "--log") == 0) {
			i++;
			out->logfile = fopen(argv[i], "a+");
615 616
		} else if (strcmp(argv[i], "--freeze-on-error") == 0) {
			out->freeze_on_error=TRUE;
617
		} else {
618
			printf("%s",usage);
619 620
			printf("Unknown option '%s'\n", argv[i]);
			return FALSE;
aymeric's avatar
aymeric committed
621
		}
622
	}
623 624 625 626
	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;
	}
627 628
	return TRUE;
}
629

jehan's avatar
jehan committed
630

631 632 633 634 635
#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
636
			ms_warning("Decoding error on videostream [%p]", md->video);
637 638
			break;
		case MS_VIDEO_DECODER_FIRST_IMAGE_DECODED:
Ghislain MARY's avatar
Ghislain MARY committed
639
			ms_message("First video frame decoded successfully on videostream [%p]", md->video);
640 641 642 643 644 645
			break;
	}
}
#endif


646
void setup_media_streams(MediastreamDatas* args) {
647
	/*create the rtp session */
648
#ifdef VIDEO_ENABLED
Sylvain Berfini's avatar
Sylvain Berfini committed
649
	MSWebCam *cam=NULL;
650
#endif
651
	ortp_init();
652 653 654
	if (args->logfile)
		ortp_set_log_file(args->logfile);
	
655
	if (args->is_verbose) {
656 657 658
		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
659
	}
660

Simon Morlat's avatar
Simon Morlat committed
661
	ms_init();
jehan's avatar
jehan committed
662

663
#if TARGET_OS_IPHONE || defined(ANDROID)
jehan's avatar
jehan committed
664
#if defined (HAVE_X264) && defined (VIDEO_ENABLED)
665
	libmsx264_init(); /*no plugin on IOS/Android */
jehan's avatar
jehan committed
666
#endif
667 668 669
#if defined (HAVE_OPENH264) && defined (VIDEO_ENABLED)
	libmsopenh264_init(); /*no plugin on IOS/Android */
#endif
jehan's avatar
jehan committed
670
#if defined (HAVE_SILK)
671
	libmssilk_init(); /*no plugin on IOS/Android */
jehan's avatar
jehan committed
672
#endif
673 674 675
#if defined (HAVE_WEBRTC)
	libmswebrtc_init();
#endif	
676 677

#endif /* IPHONE | ANDROID */
678

679 680 681 682
	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);
683
	rtp_profile_set_payload(&av_profile,114,args->custom_pt);
684
	rtp_profile_set_payload(&av_profile,115,&payload_type_lpc1015);
685
#ifdef VIDEO_ENABLED
Simon Morlat's avatar
Simon Morlat committed
686 687 688 689
	cam=ms_web_cam_new(&mire_desc);
	if (cam) ms_web_cam_manager_add_cam(ms_web_cam_manager_get(), cam);
	cam=NULL;
	
690 691 692 693 694 695
	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);
696
	rtp_profile_set_payload(&av_profile,103,&payload_type_vp8);
697

698
	args->video=NULL;
aymeric's avatar
aymeric committed
699
#endif
700 701
	args->profile=rtp_profile_clone_full(&av_profile);
	args->q=ortp_ev_queue_new();
702

703
	if (args->mtu) ms_set_mtu(args->mtu);
704 705
	ms_filter_enable_statistics(TRUE);
	ms_filter_reset_statistics();
Ghislain MARY's avatar
Ghislain MARY committed
706
	args->ice_session=ice_session_new();
Ghislain MARY's avatar
Ghislain MARY committed
707 708 709 710
	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);
711

aymeric's avatar
aymeric committed
712
	signal(SIGINT,stop_handler);
713 714
	args->pt=rtp_profile_get_payload(args->profile,args->payload);
	if (args->pt==NULL){
715
		printf("Error: no payload defined with number %i.\n",args->payload);
aymeric's avatar
aymeric committed
716 717
		exit(-1);
	}
718
	if (args->enable_avpf == TRUE) {
719
		PayloadTypeAvpfParams avpf_params;
720
		payload_type_set_flag(args->pt, PAYLOAD_TYPE_RTCP_FEEDBACK_ENABLED);
721
		avpf_params.features = PAYLOAD_TYPE_AVPF_FIR | PAYLOAD_TYPE_AVPF_PLI | PAYLOAD_TYPE_AVPF_SLI | PAYLOAD_TYPE_AVPF_RPSI;
722
		avpf_params.trr_interval = 3000;
723
		payload_type_set_avpf_params(args->pt, avpf_params);
724 725 726
	} else {
		payload_type_unset_flag(args->pt, PAYLOAD_TYPE_RTCP_FEEDBACK_ENABLED);
	}
727 728
	if (args->fmtp!=NULL) payload_type_set_send_fmtp(args->pt,args->fmtp);
	if (args->bitrate>0) args->pt->normal_bitrate=args->bitrate;
729

730
	if (args->pt->normal_bitrate==0){
731
		fprintf(stderr,"Error: no default bitrate specified for codec %s/%i. "
732 733 734
			"Please specify a network bitrate with --bitrate option.\n",args->pt->mime_type,args->pt->clock_rate);
		exit(-1);
	}
aymeric's avatar
aymeric committed
735

736 737 738 739 740
	// 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
741 742
			char tmp[30];
			snprintf(tmp,sizeof(tmp),"%08x%08x%08x%08x",rand(),rand(),rand(),rand());
743 744 745 746 747 748
			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
749 750
			char tmp[30];
			snprintf(tmp,sizeof(tmp),"%08x%08x%08x%08x",rand(),rand(),rand(),rand());
751 752 753 754 755
			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);
		}
756
	}
757

758
	if (args->pt->type!=PAYLOAD_VIDEO){
759
		MSSndCardManager *manager=ms_snd_card_manager_get();
760 761 762 763
		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);
764
		args->audio=audio_stream_new(args->localport,args->localport+1,ms_is_ipv6(args->ip));
765 766 767 768 769
		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
770
		printf("Starting audio stream.\n");
771

772
		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,
773 774
		                        args->outfile==NULL ? play : NULL ,args->infile==NULL ? capt : NULL,args->infile!=NULL ? FALSE: args->ec);

775
		if (args->ice_local_candidates_nb || args->ice_remote_candidates_nb) {
776
			args->audio->ms.ice_check_list = ice_check_list_new();
777
			rtp_session_set_pktinfo(args->audio->ms.sessions.rtp_session,TRUE);
778
			ice_session_add_check_list(args->ice_session, args->audio->ms.ice_check_list, 0);
779
		}
780 781 782 783 784
		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];
785 786
				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);
787 788 789
			}
		}
		if (args->ice_remote_candidates_nb) {
790
			char foundation[4];
791 792 793 794
			MediastreamIceCandidate *candidate;
			int c;
			for (c=0;c<args->ice_remote_candidates_nb;c++){
				candidate=&args->ice_remote_candidates[c];
795 796
				memset(foundation, '\0', sizeof(foundation));
				snprintf(foundation, sizeof(foundation) - 1, "%u", c + 1);
797 798
				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);
799 800 801
			}
		}

802 803 804 805 806 807 808 809 810 811 812 813
		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
814 815

			}
816 817 818 819
			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
820
				}
821 822 823
				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
824 825
				}
			}
826

827
            #ifndef TARGET_OS_IPHONE
Guillaume Beraudo's avatar
Guillaume Beraudo committed
828
			if (args->zrtp_secrets != NULL) {
829
				MSZrtpParams params;
830 831
				params.zid_file=args->zrtp_secrets;
				audio_stream_enable_zrtp(args->audio,&params);
832
			}
833
            #endif
834

835
			args->session=args->audio->ms.sessions.rtp_session;
836
		}
837

838
		if (args->enable_srtp) {
839
			ms_message("SRTP enabled: %d",
840
				audio_stream_enable_srtp(
841
					args->audio,
jehan's avatar
jehan committed
842
					MS_AES_128_SHA1_80,
843
					args->srtp_local_master_key,
844 845
					args->srtp_remote_master_key));
		}
aymeric's avatar
aymeric committed
846 847
	}else{
#ifdef VIDEO_ENABLED
848 849 850 851
		float zoom[] = {
			args->zoom,
			args->zoom_cx, args->zoom_cy };

852
		if (args->eq){
smorlat's avatar
smorlat committed
853 854 855
			ms_fatal("Cannot put an audio equalizer in a video stream !");
			exit(-1);
		}
856
		ms_message("Starting video stream.\n");
857
		args->video=video_stream_new(args->localport, args->localport+1, ms_is_ipv6(args->ip));
858 859 860
		if (args->video_display_filter)
			video_stream_set_display_filter_name(args->video, args->video_display_filter);
		
861
#ifdef ANDROID
862 863
		if (args->device_rotation >= 0)
			video_stream_set_device_rotation(args->video, args->device_rotation);
864
#endif
865 866
		video_stream_set_sent_video_size(args->video,args->vs);
		video_stream_use_preview_video_window(args->video,args->two_windows);
867
#if TARGET_OS_IPHONE
jehan's avatar
jehan committed
868 869 870
		NSBundle* myBundle = [NSBundle mainBundle];
		const char*  nowebcam = [[myBundle pathForResource:@"nowebcamCIF"ofType:@"jpg"] cStringUsingEncoding:[NSString defaultCStringEncoding]];
		ms_static_image_set_default_image(nowebcam);
871 872
		NSUInteger cpucount = [[NSProcessInfo processInfo] processorCount];
		ms_set_cpu_count(cpucount);
jehan's avatar
jehan committed
873
#endif
874
		video_stream_set_event_callback(args->video,video_stream_event_cb, args);
875
		video_stream_set_freeze_on_error(args->video,args->freeze_on_error);
876
		video_stream_enable_adaptive_bitrate_control(args->video,args->use_rc);
877 878
		if (args->camera)
			cam=ms_web_cam_manager_get_cam(ms_web_cam_manager_get(),args->camera);
879 880
		if (cam==NULL)
			cam=ms_web_cam_manager_get_default_cam(ms_web_cam_manager_get());
881
		video_stream_start(args->video,args->profile,
882
					args->ip,args->remoteport,
883
					args->ip,args->enable_rtcp?args->remoteport+1:-1,
884 885
					args->payload,
					args->jitter,cam
886
					);
887
		args->session=args->video->ms.sessions.rtp_session;
888

889
		ms_filter_call_method(args->video->output,MS_VIDEO_DISPLAY_ZOOM, zoom);
890
		if (args->enable_srtp) {
891
			ms_message("SRTP enabled: %d",
892
				video_stream_enable_strp(
893
					args->video,
jehan's avatar
jehan committed
894
					MS_AES_128_SHA1_80,
895
					args->srtp_local_master_key,
896 897
					args->srtp_remote_master_key));
		}
aymeric's avatar
aymeric committed
898 899 900 901
#else
		printf("Error: video support not compiled.\n");
#endif
	}
902 903 904
	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
905
	ice_session_choose_default_remote_candidates(args->ice_session);
Ghislain MARY's avatar
Ghislain MARY committed
906
	ice_session_start_connectivity_checks(args->ice_session);
907

908 909
	if (args->netsim.enabled){
		rtp_session_enable_network_simulation(args->session,&args->netsim);
910
	}
911 912 913
}


914
static void mediastream_tool_iterate(MediastreamDatas* args) {
915 916 917
#ifndef WIN32
	struct pollfd pfd;
	int err;
918

919 920
	if (args->interactive){
		pfd.fd=STDIN_FILENO;
921
		pfd.events=POLLIN;
922
		pfd.revents=0;
923

924
		err=poll(&pfd,1,10);
925
		if (err==1 && (pfd.revents & POLLIN)){
926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955
			char commands[128];
			int intarg;
			commands[127]='\0';
			ms_sleep(1);  /* ensure following text be printed after ortp messages */
			if (args->eq)
			printf("\nPlease enter equalizer requests, such as 'eq active 1', 'eq active 0', 'eq 1200 0.1 200'\n");

			if (fgets(commands,sizeof(commands)-1,stdin)!=NULL){
				int active,freq,freq_width;

				float gain;
				if (sscanf(commands,"eq active %i",&active)==1){
					audio_stream_enable_equalizer(args->audio,active);
					printf("OK\n");
				}else if (sscanf(commands,"eq %i %f %i",&freq,&gain,&freq_width)==3){
					audio_stream_equalizer_set_gain(args->audio,freq,gain,freq_width);
					printf("OK\n");
				}else if (sscanf(commands,"eq %i %f",&freq,&gain)==2){
					audio_stream_equalizer_set_gain(args->audio,freq,gain,0);
					printf("OK\n");
				}else if (strstr(commands,"dump")){
					int n=0,i;
					float *t;
					ms_filter_call_method(args->audio->equalizer,MS_EQUALIZER_GET_NUM_FREQUENCIES,&n);
					t=(float*)alloca(sizeof(float)*n);
					ms_filter_call_method(args->audio->equalizer,MS_EQUALIZER_DUMP_STATE,t);
					for(i=0;i<n;++i){
						if (fabs(t[i]-1)>0.01){
						printf("%i:%f:0 ",(i*args->pt->clock_rate)/(2*n),t[i]);
						}
956
					}
957 958
					printf("\nOK\n");
				}else if (sscanf(commands,"lossrate %i",&intarg)==1){
959 960 961 962 963 964 965 966
					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")){
967 968
					cond=0;
				}else printf("Cannot understand this.\n");
969
			}
970 971
		}else if (err==-1 && errno!=EINTR){
			ms_fatal("mediastream's poll() returned %s",strerror(errno));
972
		}
973 974
	}else{
		ms_usleep(10000);
975 976 977 978 979 980 981 982 983 984
	}
#else
	MSG msg;
	Sleep(10);
	while (PeekMessage(&msg, NULL, 0, 0,1)){
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	/*no interactive mode on windows*/
#endif
985 986
}

987
void mediastream_run_loop(MediastreamDatas* args) {
988 989
	rtp_session_register_event_queue(args->session,args->q);

990
#if TARGET_OS_IPHONE
jehan's avatar
jehan committed
991
	if (args->video) ms_set_video_stream(args->video); /*for IOS*/
992
#endif
993

994 995 996
	while(cond)
	{
		int n;
997
		for(n=0;n<100 && cond;++n){
998
			mediastream_tool_iterate(args);
999 1000 1001 1002 1003 1004 1005
#if defined(VIDEO_ENABLED)
			if (args->video) video_stream_iterate(args->video);
#endif
			if (args->audio) audio_stream_iterate(args->audio);
		}
		rtp_stats_display(rtp_session_get_stats(args->session),"RTP stats");
		if (args->session){
1006 1007 1008
			float audio_load = 0;
			float video_load = 0;
			
1009
			ms_message("Bandwidth usage: download=%f kbits/sec, upload=%f kbits/sec\n",
1010 1011
				rtp_session_get_recv_bandwidth(args->session)*1e-3,
				rtp_session_get_send_bandwidth(args->session)*1e-3);
1012 1013 1014 1015 1016 1017 1018 1019 1020 1021
			
			if (args->audio) {
				audio_load = ms_ticker_get_average_load(args->audio->ms.sessions.ticker);
			}
#if defined(VIDEO_ENABLED)
			if (args->video) {
				video_load = ms_ticker_get_average_load(args->video->ms.sessions.ticker);
			}
#endif
			ms_message("Thread processing load: audio=%f\tvideo=%f", audio_load, video_load);
1022
			parse_events(args->session,args->q);
1023
			ms_message("Quality indicator : %f\n",args->audio ? audio_stream_get_quality_rating(args->audio) : media_stream_get_quality_rating((MediaStream*)args->video));
aymeric's avatar
aymeric committed
1024
		}
Simon Morlat's avatar
Simon Morlat committed
1025
	}
1026
}
1027

1028
void clear_mediastreams(MediastreamDatas* args) {
jehan's avatar
jehan committed
1029
	ms_message("stopping all...\n");
1030
	ms_message("Average quality indicator: %f",args->audio ? audio_stream_get_average_quality_rating(args->audio) : -1);
1031

1032 1033 1034
	if (args->audio) {
		audio_stream_stop(args->audio);
	}
aymeric's avatar
aymeric committed
1035
#ifdef VIDEO_ENABLED
1036
	if (args->video) {
1037
		if (args->video->ms.ice_check_list) ice_check_list_destroy(args->video->ms.ice_check_list);
1038
		video_stream_stop(args->video);
1039 1040
		ms_filter_log_statistics();
	}
aymeric's avatar
aymeric committed
1041
#endif
Ghislain MARY's avatar
Ghislain MARY committed
1042
	if (args->ice_session) ice_session_destroy(args->ice_session);
1043 1044 1045
	ortp_ev_queue_destroy(args->q);
	rtp_profile_destroy(args->profile);

1046 1047 1048
	if (args->logfile)
		fclose(args->logfile);
	
1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063
	ms_exit();
}

// ANDROID JNI WRAPPER
#ifdef ANDROID
JNIEXPORT jint JNICALL  JNI_OnLoad(JavaVM *ajvm, void *reserved)
{
	ms_set_jvm(ajvm);

	return JNI_VERSION_1_2;
}

JNIEXPORT void JNICALL Java_org_linphone_mediastream_MediastreamerActivity_setVideoWindowId
  (JNIEnv *env, jobject obj, jobject id, jint _args) {
#ifdef VIDEO_ENABLED
1064
	MediastreamDatas* args =  (MediastreamDatas*)_args;
1065 1066 1067 1068 1069 1070 1071 1072 1073
	if (!args->video)
		return;
	video_stream_set_native_window_id(args->video,(unsigned long)id);
#endif
}

JNIEXPORT void JNICALL Java_org_linphone_mediastream_MediastreamerActivity_setVideoPreviewWindowId
  (JNIEnv *env, jobject obj, jobject id, jint _args) {
#ifdef VIDEO_ENABLED
1074
	MediastreamDatas* args =  (MediastreamDatas*)_args;
1075 1076 1077 1078 1079 1080 1081 1082 1083
	if (!args->video)
		return;
	video_stream_set_native_preview_window_id(args->video,(unsigned long)id);
#endif
}

JNIEXPORT void JNICALL Java_org_linphone_mediastream_MediastreamerActivity_setDeviceRotation
  (JNIEnv *env, jobject thiz, jint rotation, jint _args) {
#ifdef VIDEO_ENABLED
1084
	MediastreamDatas* args =  (MediastreamDatas*)_args;
1085 1086 1087 1088 1089 1090 1091 1092 1093
	if (!args->video)
		return;
	video_stream_set_device_rotation(args->video, rotation);
#endif
}

JNIEXPORT void JNICALL Java_org_linphone_mediastream_MediastreamerActivity_changeCamera
  (JNIEnv *env, jobject obj, jint camId, jint _args) {
#ifdef VIDEO_ENABLED
1094
	MediastreamDatas* args =  (MediastreamDatas*)_args;
1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149
	if (!args->video)
		return;
	char* id = (char*)malloc(15);
	snprintf(id, 15, "Android%d", camId);
	ms_message("Changing camera, trying to use: '%s'\n", id);
	video_stream_change_camera(args->video, ms_web_cam_manager_get_cam(ms_web_cam_manager_get(), id));
#endif
}

JNIEXPORT jint JNICALL Java_org_linphone_mediastream_MediastreamerActivity_stopMediaStream
  (JNIEnv *env, jobject obj) {
	ms_message("Requesting mediastream to stop\n");
	stop_handler(0);
	return 0;
}

JNIEXPORT jint JNICALL Java_org_linphone_mediastream_MediastreamerActivity_initDefaultArgs
  (JNIEnv *env, jobject obj) {
	cond = 1;
	return (unsigned int)init_default_args();
}

JNIEXPORT jboolean JNICALL Java_org_linphone_mediastream_MediastreamerActivity_parseArgs
  (JNIEnv *env, jobject obj, jint jargc, jobjectArray jargv, jint args) {
	// translate java String[] to c char*[]
	char** argv = (char**) malloc(jargc * sizeof(char*));
	int i;

	for(i=0; i<jargc; i++) {
		jstring arg = (jstring) (*env)->GetObjectArrayElement(env, jargv, i);
		const char *str = (*env)->GetStringUTFChars(env, arg, NULL);
		if (str == NULL)
			argv[i] = NULL;
		else {
			argv[i] = strdup(str);
			(*env)->ReleaseStringUTFChars(env, arg, str);
		}
	}

	bool_t result = parse_args(jargc, argv, (MediastreamDatas*)args);

	for(i=0; i<jargc; i++) {
		if (argv[i])
			free(argv[i]);
	}
	return result;
}

JNIEXPORT void JNICALL Java_org_linphone_mediastream_MediastreamerActivity_setupMediaStreams
  (JNIEnv *env, jobject obj, jint args) {
	setup_media_streams((MediastreamDatas*)args);
}

JNIEXPORT void JNICALL Java_org_linphone_mediastream_MediastreamerActivity_runLoop
  (JNIEnv *env, jobject obj, jint args) {
Ghislain MARY's avatar