mediastream.c 28.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 21 22 23
/*
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.
*/

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

24 25
#include <math.h>

aymeric's avatar
aymeric committed
26
#include "mediastreamer2/mediastream.h"
27
#include "mediastreamer2/msequalizer.h"
28
#include "mediastreamer2/msvolume.h"
aymeric's avatar
aymeric committed
29 30 31 32
#ifdef VIDEO_ENABLED
#include "mediastreamer2/msv4l.h"
#endif

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

45 46 47
#ifdef __APPLE__
#include <CoreFoundation/CFRunLoop.h>
#endif
48 49
#if  defined(__ios) || defined (ANDROID)
#ifdef __ios
jehan's avatar
jehan committed
50
#import <UIKit/UIKit.h>
51
#endif
jehan's avatar
jehan committed
52
extern void ms_set_video_stream(VideoStream* video);
jehan's avatar
jehan committed
53 54 55
#ifdef HAVE_X264
extern void libmsx264_init();
#endif
jehan's avatar
jehan committed
56 57 58
#ifdef HAVE_SILK
extern void libmssilk_init();
#endif 
jehan's avatar
jehan committed
59
#endif
60

61 62 63 64 65
#ifdef ANDROID
#include <android/log.h>
#include <jni.h>
#endif

66 67
#include <ortp/b64.h>

aymeric's avatar
aymeric committed
68 69 70
static int cond=1;


71

72 73
typedef struct _MediastreamDatas {
	int localport,remoteport,payload;
74
	char ip[64];
75 76 77 78 79 80 81 82 83
	char *fmtp;
	int jitter;
	int bitrate;
	MSVideoSize vs;
	bool_t ec;
	bool_t agc;
	bool_t eq;
	bool_t is_verbose;
	int device_rotation;
84

85 86 87 88 89 90 91 92 93 94 95
#ifdef VIDEO_ENABLED
	VideoStream *video;
#endif
	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;
96 97 98
	bool_t use_rc;
	bool_t enable_srtp;
	bool_t pad[3];
99 100 101 102 103 104 105 106 107 108 109 110
	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;
111 112
	char* srtp_local_master_key;
	char* srtp_remote_master_key;
113
	int netsim_bw;
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
	
	AudioStream *audio;	
	PayloadType *pt;
	RtpSession *session;
	OrtpEvQueue *q;
	RtpProfile *profile;
} MediastreamDatas;

// 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);
/* run loop */
void run_interactive_loop(MediastreamDatas* args);
void run_non_interactive_loop(MediastreamDatas* args);
/* exit */
void clear_mediastreams(MediastreamDatas* args);

// HELPER METHODS
static void stop_handler(int signum);
static bool_t parse_addr(const char *addr, char *ip, int len, int *port);
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 PayloadType* create_custom_payload_type(const char *type, const char *subtype, const char *rate, int number);
static PayloadType* parse_custom_payload(const char *name);
static bool_t parse_window_ids(const char *ids, int* video_id, int* preview_id);
144

145 146
const char *usage="mediastream --local <port> --remote <ip:port> \n"
								"--payload <payload type number or payload name like 'audio/pmcu/8000'>\n"
147 148 149
								"[ --fmtp <fmtpline> ]\n"
								"[ --jitter <miliseconds> ]\n"
								"[ --width <pixels> ]\n"
smorlat's avatar
smorlat committed
150
								"[ --height <pixels> ]\n"
151 152
								"[ --bitrate <bits per seconds> ]\n"
								"[ --ec (enable echo canceller) ]\n"
153 154
								"[ --ec-tail <echo canceller tail length in ms> ]\n"
								"[ --ec-delay <echo canceller delay in ms> ]\n"
155
								"[ --ec-framesize <echo canceller framesize in samples> ]\n"
156 157 158 159 160 161 162 163
								"[ --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
164 165 166 167 168 169
								"[ --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"
170
								"[ --el-transmit-thres <(float) [0-1]> (TO BE DOCUMENTED) ]\n"
171
								"[ --rc (enable adaptive rate control) ]\n"
Guillaume Beraudo's avatar
Guillaume Beraudo committed
172
								"[ --zrtp <secrets file> (enable zrtp) ]\n"
173
								"[ --verbose (most verbose messages) ]\n"
174
								"[ --video-windows-id <video surface:preview surface>]\n"
175
								"[ --srtp <local master_key> <remote master_key> (enable srtp, master key is generated if absent from comand line)\n"
176
								"[ --netsim-bandwidth <bandwidth limit in bits/s> (simulates a network download bandwidth limit)\n"
177
		;
aymeric's avatar
aymeric committed
178

179

180
#ifndef ANDROID
181
 
182
#ifndef __APPLE__
aymeric's avatar
aymeric committed
183
int main(int argc, char * argv[])
184 185 186
#else /*Main thread is blocked by cocoa UI framework*/
int g_argc;
char** g_argv;
jehan's avatar
jehan committed
187
static int _main(int argc, char * argv[]);
188
 
189
static void* apple_main(void* data) {
190 191
 _main(g_argc,g_argv);
 return NULL;
192 193 194 195 196 197
}
int main(int argc, char * argv[]) {
	pthread_t main_thread;
	g_argc=argc;
	g_argv=argv;
	pthread_create(&main_thread,NULL,apple_main,NULL);
jehan's avatar
jehan committed
198
	#if TARGET_OS_MACOSX
199 200
	CFRunLoopRun();
	return 0;
201
	#elif defined(__ios)
202 203 204 205
	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
	int value = UIApplicationMain(0, nil, nil, nil);
	[pool release];
	return value;
206
	#endif
207 208
	cond=0;
	pthread_join(main_thread,NULL);
jehan's avatar
jehan committed
209
	return 0;
210
}
jehan's avatar
jehan committed
211
static int _main(int argc, char * argv[])
212
#endif
aymeric's avatar
aymeric committed
213
{
214
	MediastreamDatas* args;
215
	cond = 1;
216 217
	
	args = init_default_args();
218

219 220
	if (!parse_args(argc, argv, args))
		return 0;
221

222
	setup_media_streams(args);
223

224 225 226 227 228 229
	if (args->eq)
		run_interactive_loop(args);
	else
		run_non_interactive_loop(args);
	
	clear_mediastreams(args);
230

231 232 233
	free(args);
	
	return 0;
234 235 236 237 238
}

#endif


239
MediastreamDatas* init_default_args() {
240
	MediastreamDatas* args = (MediastreamDatas*)ms_malloc0(sizeof(MediastreamDatas));
241 242 243 244 245 246 247 248 249 250 251 252
	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;
	args->is_verbose=FALSE;
	args->device_rotation=-1;
aymeric's avatar
aymeric committed
253

254
#ifdef VIDEO_ENABLED
255
	args->video=NULL;
256
#endif
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277
	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;
	/* starting values echo canceller */
	args->ec_len_ms=args->ec_delay_ms=args->ec_framesize=0;
278 279
	args->enable_srtp = FALSE;
	args->srtp_local_master_key = args->srtp_remote_master_key = NULL;
280 281 282 283 284 285 286 287

	args->audio = NULL;
	args->session = NULL;
	args->pt = NULL;
	args->q = NULL;
	args->profile = NULL;

	return args;
288 289
}

290
bool_t parse_args(int argc, char** argv, MediastreamDatas* out) {
aymeric's avatar
aymeric committed
291
	int i;
jehan's avatar
jehan committed
292

aymeric's avatar
aymeric committed
293
	if (argc<4) {
Simon Morlat's avatar
Simon Morlat committed
294
		printf("%s",usage);
295
		return FALSE;
aymeric's avatar
aymeric committed
296
	}
297 298

	/* default size */
299 300
	out->vs.width=MS_VIDEO_SIZE_CIF_W;
	out->vs.height=MS_VIDEO_SIZE_CIF_H;
301

aymeric's avatar
aymeric committed
302 303 304
	for (i=1;i<argc;i++){
		if (strcmp(argv[i],"--local")==0){
			i++;
305
			out->localport=atoi(argv[i]);
aymeric's avatar
aymeric committed
306 307
		}else if (strcmp(argv[i],"--remote")==0){
			i++;
308
			if (!parse_addr(argv[i],out->ip,sizeof(out->ip),&out->remoteport)) {
Simon Morlat's avatar
Simon Morlat committed
309
				printf("%s",usage);
310
				return FALSE;
aymeric's avatar
aymeric committed
311
			}
312
			printf("Remote addr: ip=%s port=%i\n",out->ip,out->remoteport);
aymeric's avatar
aymeric committed
313 314
		}else if (strcmp(argv[i],"--payload")==0){
			i++;
315
			if (isdigit(argv[i][0])){
316
				out->payload=atoi(argv[i]);
317
			}else {
318 319
				out->payload=114;
				out->custom_pt=parse_custom_payload(argv[i]);
320
			}
aymeric's avatar
aymeric committed
321 322
		}else if (strcmp(argv[i],"--fmtp")==0){
			i++;
323
			out->fmtp=argv[i];
aymeric's avatar
aymeric committed
324 325
		}else if (strcmp(argv[i],"--jitter")==0){
			i++;
326
			out->jitter=atoi(argv[i]);
aymeric's avatar
aymeric committed
327 328
		}else if (strcmp(argv[i],"--bitrate")==0){
			i++;
329
			out->bitrate=atoi(argv[i]);
330 331
		}else if (strcmp(argv[i],"--width")==0){
			i++;
332
			out->vs.width=atoi(argv[i]);
333 334
		}else if (strcmp(argv[i],"--height")==0){
			i++;
335
			out->vs.height=atoi(argv[i]);
336 337
		}else if (strcmp(argv[i],"--capture-card")==0){
			i++;
338
			out->capture_card=argv[i];
Simon Morlat's avatar
Simon Morlat committed
339 340
		}else if (strcmp(argv[i],"--playback-card")==0){
			i++;
341
			out->playback_card=argv[i];
aymeric's avatar
aymeric committed
342
		}else if (strcmp(argv[i],"--ec")==0){
343
			out->ec=TRUE;
344 345
		}else if (strcmp(argv[i],"--ec-tail")==0){
			i++;
346
			out->ec_len_ms=atoi(argv[i]);
347 348
		}else if (strcmp(argv[i],"--ec-delay")==0){
			i++;
349
			out->ec_delay_ms=atoi(argv[i]);
350 351
		}else if (strcmp(argv[i],"--ec-framesize")==0){
			i++;
352
			out->ec_framesize=atoi(argv[i]);
353
		}else if (strcmp(argv[i],"--agc")==0){
354
			out->agc=TRUE;
smorlat's avatar
smorlat committed
355
		}else if (strcmp(argv[i],"--eq")==0){
356
			out->eq=TRUE;
357
		}else if (strcmp(argv[i],"--ng")==0){
358
			out->use_ng=1;
359
		}else if (strcmp(argv[i],"--rc")==0){
360
			out->use_rc=1;
361 362
		}else if (strcmp(argv[i],"--ng-threshold")==0){
			i++;
363
			out->ng_threshold=atof(argv[i]);
jehan's avatar
jehan committed
364 365
		}else if (strcmp(argv[i],"--ng-floorgain")==0){
			i++;
366
			out->ng_floorgain=atof(argv[i]);
367
		}else if (strcmp(argv[i],"--two-windows")==0){
368
			out->two_windows=TRUE;
369 370
		}else if (strcmp(argv[i],"--infile")==0){
			i++;
371
			out->infile=argv[i];
372 373
		}else if (strcmp(argv[i],"--outfile")==0){
			i++;
374
			out->outfile=argv[i];
375 376
		}else if (strcmp(argv[i],"--camera")==0){
			i++;
377
			out->camera=argv[i];
jehan's avatar
jehan committed
378
		}else if (strcmp(argv[i],"--el")==0){
379
			out->el=TRUE;
jehan's avatar
jehan committed
380 381
		}else if (strcmp(argv[i],"--el-speed")==0){
			i++;
382
			out->el_speed=atof(argv[i]);
jehan's avatar
jehan committed
383 384
		}else if (strcmp(argv[i],"--el-thres")==0){
			i++;
385
			out->el_thres=atof(argv[i]);
jehan's avatar
jehan committed
386 387
		}else if (strcmp(argv[i],"--el-force")==0){
			i++;
388
			out->el_force=atof(argv[i]);
jehan's avatar
jehan committed
389 390
		}else if (strcmp(argv[i],"--el-sustain")==0){
			i++;
391
			out->el_sustain=atoi(argv[i]);
jehan's avatar
jehan committed
392 393
		}else if (strcmp(argv[i],"--el-transmit-thres")==0){
			i++;
394
			out->el_transmit_thres=atof(argv[i]);
395
		} else if (strcmp(argv[i],"--zrtp")==0){
396
			out->zrtp_secrets=argv[++i];
397
		} else if (strcmp(argv[i],"--verbose")==0){
398
			out->is_verbose=TRUE;
399 400
		} else if (strcmp(argv[i], "--video-windows-id")==0) {
			i++;
401
			if (!parse_window_ids(argv[i],&out->video_window_id, &out->preview_window_id)) {
402
				printf("%s",usage);
403
				return FALSE;
404
			}
405
		} else if (strcmp(argv[i], "--device-rotation")==0) {
406
			i++;
407
			out->device_rotation=atoi(argv[i]);
408 409 410 411 412 413 414 415 416 417 418 419
		} else if (strcmp(argv[i], "--srtp")==0) {
			if (!ortp_srtp_supported()) {
				ms_error("ortp srtp support not enabled");
				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++];
			}
420 421 422
		} else if (strcmp(argv[i],"--netsim-bandwidth")==0){
			i++;
			out->netsim_bw=atoi(argv[i]);
423
		} else if (strcmp(argv[i],"--help")==0){
jehan's avatar
jehan committed
424
			printf("%s",usage);
425
			return FALSE;
aymeric's avatar
aymeric committed
426
		}
427
	}
428 429
	return TRUE;
}
430

jehan's avatar
jehan committed
431

432
void setup_media_streams(MediastreamDatas* args) {
433 434
	/*create the rtp session */
	ortp_init();
435
	if (args->is_verbose) {
436 437 438
		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
439
	}
440

jehan's avatar
jehan committed
441

442
#if defined(__ios) || defined(ANDROID)
jehan's avatar
jehan committed
443 444 445 446 447 448 449 450 451
#if defined (HAVE_X264) && defined (VIDEO_ENABLED)
	libmsx264_init(); /*no plugin on IOS*/
#endif
#if defined (HAVE_SILK)
	libmssilk_init(); /*no plugin on IOS*/
#endif
	
#endif
	
452 453 454 455
	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);
456
	rtp_profile_set_payload(&av_profile,114,args->custom_pt);
457
	rtp_profile_set_payload(&av_profile,115,&payload_type_lpc1015);
458
#ifdef VIDEO_ENABLED
459
#if defined (__ios) && defined (HAVE_X264)
460 461
	libmsx264_init(); /*no plugin on IOS*/
#endif
462 463 464 465 466 467
	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);
468
	rtp_profile_set_payload(&av_profile,103,&payload_type_vp8);
469 470
	
	args->video=NULL;
jehan's avatar
jehan committed
471
	MSWebCam *cam=NULL;
aymeric's avatar
aymeric committed
472
#endif
473 474
	args->profile=rtp_profile_clone_full(&av_profile);
	args->q=ortp_ev_queue_new();
475

aymeric's avatar
aymeric committed
476
	ms_init();
477 478 479
	ms_filter_enable_statistics(TRUE);
	ms_filter_reset_statistics();

aymeric's avatar
aymeric committed
480
	signal(SIGINT,stop_handler);
481 482 483
	args->pt=rtp_profile_get_payload(args->profile,args->payload);
	if (args->pt==NULL){
		printf("Error: no payload defined with number %i.",args->payload);
aymeric's avatar
aymeric committed
484 485
		exit(-1);
	}
486 487
	if (args->fmtp!=NULL) payload_type_set_send_fmtp(args->pt,args->fmtp);
	if (args->bitrate>0) args->pt->normal_bitrate=args->bitrate;
aymeric's avatar
aymeric committed
488

489 490 491 492 493 494
	// 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) {
			uint8_t tmp[30];			
Sylvain Berfini's avatar
Sylvain Berfini committed
495
			ortp_crypto_get_random(tmp, 30);
496 497 498 499 500 501 502
			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) {
			uint8_t tmp[30];			
Sylvain Berfini's avatar
Sylvain Berfini committed
503
			ortp_crypto_get_random(tmp, 30);
504 505 506 507 508 509 510
			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);
		}
	}	

511
	if (args->pt->type!=PAYLOAD_VIDEO){
512
		MSSndCardManager *manager=ms_snd_card_manager_get();
513 514 515 516 517 518 519 520 521 522
		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);
		args->audio=audio_stream_new(args->localport,ms_is_ipv6(args->ip));
		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
523
		printf("Starting audio stream.\n");
524

525 526 527 528 529 530 531 532 533 534 535 536 537 538 539
		audio_stream_start_full(args->audio,args->profile,args->ip,args->remoteport,args->remoteport+1, args->payload, args->jitter,args->infile,args->outfile,
		                        args->outfile==NULL ? play : NULL ,args->infile==NULL ? capt : NULL,args->infile!=NULL ? FALSE: args->ec);

		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
540 541

			}
542 543 544 545
			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
546
				}
547 548 549
				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
550 551
				}
			}
552

553
            #ifndef TARGET_OS_IPHONE
Guillaume Beraudo's avatar
Guillaume Beraudo committed
554
			if (args->zrtp_secrets != NULL) {
555
				OrtpZrtpParams params;
556 557
				params.zid_file=args->zrtp_secrets;
				audio_stream_enable_zrtp(args->audio,&params);
558
			}
559
            #endif
560

561
			args->session=args->audio->session;
562
		}
563 564 565 566 567 568 569 570 571
		
		if (args->enable_srtp) {
			ms_message("SRTP enabled: %d", 
				audio_stream_enable_strp(
					args->audio, 
					AES_128_SHA1_80,
					args->srtp_local_master_key, 
					args->srtp_remote_master_key));
		}
aymeric's avatar
aymeric committed
572 573
	}else{
#ifdef VIDEO_ENABLED
574
		if (args->eq){
smorlat's avatar
smorlat committed
575 576 577
			ms_fatal("Cannot put an audio equalizer in a video stream !");
			exit(-1);
		}
578 579
		ms_message("Starting video stream.\n");
		args->video=video_stream_new(args->localport, ms_is_ipv6(args->ip));
580
#ifdef ANDROID
581 582
		if (args->device_rotation >= 0)
			video_stream_set_device_rotation(args->video, args->device_rotation);
583
#endif
584 585
		video_stream_set_sent_video_size(args->video,args->vs);
		video_stream_use_preview_video_window(args->video,args->two_windows);
586
#ifdef __ios
jehan's avatar
jehan committed
587 588 589 590 591
		NSBundle* myBundle = [NSBundle mainBundle];
		const char*  nowebcam = [[myBundle pathForResource:@"nowebcamCIF"ofType:@"jpg"] cStringUsingEncoding:[NSString defaultCStringEncoding]];
		ms_static_image_set_default_image(nowebcam);
#endif

592
		video_stream_enable_adaptive_bitrate_control(args->video,args->use_rc);
593 594
		if (args->camera)
			cam=ms_web_cam_manager_get_cam(ms_web_cam_manager_get(),args->camera);
595 596
		if (cam==NULL)
			cam=ms_web_cam_manager_get_default_cam(ms_web_cam_manager_get());
597 598 599 600 601
		video_stream_start(args->video,args->profile,
					args->ip,
					args->remoteport,args->remoteport+1,
					args->payload,
					args->jitter,cam
602
					);
603
		args->session=args->video->session;
604 605 606 607 608 609 610 611 612
		
		if (args->enable_srtp) {
			ms_message("SRTP enabled: %d", 
				video_stream_enable_strp(
					args->video, 
					AES_128_SHA1_80,
					args->srtp_local_master_key, 
					args->srtp_remote_master_key));
		}
aymeric's avatar
aymeric committed
613 614 615 616
#else
		printf("Error: video support not compiled.\n");
#endif
	}
617 618 619 620 621 622
	if (args->netsim_bw>0){
		OrtpNetworkSimulatorParams params={0};
		params.enabled=TRUE;
		params.max_bandwidth=args->netsim_bw;
		rtp_session_enable_network_simulation(args->session,&params);
	}
623 624 625 626
}


void run_interactive_loop(MediastreamDatas* args) {
627 628 629
		char commands[128];
		commands[127]='\0';
		ms_sleep(1);  /* ensure following text be printed after ortp messages */
630
		if (args->eq)
631 632 633 634 635 636 637
		printf("\nPlease enter equalizer requests, such as 'eq active 1', 'eq active 0', 'eq 1200 0.1 200'\n");

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

			float gain;
			if (sscanf(commands,"eq active %i",&active)==1){
638
				audio_stream_enable_equalizer(args->audio,active);
639 640
				printf("OK\n");
			}else if (sscanf(commands,"eq %i %f %i",&freq,&gain,&freq_width)==3){
641
				audio_stream_equalizer_set_gain(args->audio,freq,gain,freq_width);
642 643
				printf("OK\n");
			}else if (sscanf(commands,"eq %i %f",&freq,&gain)==2){
644
				audio_stream_equalizer_set_gain(args->audio,freq,gain,0);
645 646 647 648
				printf("OK\n");
			}else if (strstr(commands,"dump")){
				int n=0,i;
				float *t;
649
				ms_filter_call_method(args->audio->equalizer,MS_EQUALIZER_GET_NUM_FREQUENCIES,&n);
650
				t=(float*)alloca(sizeof(float)*n);
651
				ms_filter_call_method(args->audio->equalizer,MS_EQUALIZER_DUMP_STATE,t);
652 653
				for(i=0;i<n;++i){
					if (fabs(t[i]-1)>0.01){
654
					printf("%i:%f:0 ",(i*args->pt->clock_rate)/(2*n),t[i]);
655 656 657 658 659 660 661
					}
				}
				printf("\nOK\n");
			} else if (strstr(commands,"quit")){
				break;
			}else printf("Cannot understand this.\n");
		}
662 663 664 665
}

void run_non_interactive_loop(MediastreamDatas* args) {
	rtp_session_register_event_queue(args->session,args->q);
666
#if defined(__ios) && defined(VIDEO_ENABLED)
jehan's avatar
jehan committed
667
	ms_set_video_stream(args->video); /*for IOS*/
668
#endif
669

670
	#ifdef __ios
671 672 673
	ms_set_video_stream(args->video); /*for IOS*/
    #endif

674 675 676 677 678 679 680 681 682 683
	while(cond)
	{
		int n;
		for(n=0;n<100;++n){
#ifdef WIN32
			MSG msg;
			Sleep(10);
			while (PeekMessage(&msg, NULL, 0, 0,1)){
				TranslateMessage(&msg);
				DispatchMessage(&msg);
aymeric's avatar
aymeric committed
684
			}
685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702
#else
			struct timespec ts;
			ts.tv_sec=0;
			ts.tv_nsec=10000000;
			nanosleep(&ts,NULL);
#endif
#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){
			ms_message("Bandwidth usage: download=%f kbits/sec, upload=%f kbits/sec\n",
				rtp_session_compute_recv_bandwidth(args->session)*1e-3,
				rtp_session_compute_send_bandwidth(args->session)*1e-3);
			parse_events(args->session,args->q);
			ms_message("Quality indicator : %f\n",args->audio ? audio_stream_get_quality_rating(args->audio) : -1);
aymeric's avatar
aymeric committed
703
		}
Simon Morlat's avatar
Simon Morlat committed
704
	}
705
}
706

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

711
	if (args->audio) audio_stream_stop(args->audio);
aymeric's avatar
aymeric committed
712
#ifdef VIDEO_ENABLED
713 714
	if (args->video) {
		video_stream_stop(args->video);
715 716
		ms_filter_log_statistics();
	}
aymeric's avatar
aymeric committed
717
#endif
718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 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 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973
	ortp_ev_queue_destroy(args->q);
	rtp_profile_destroy(args->profile);

	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) {
	MediastreamDatas* args =  (MediastreamDatas*)_args;
#ifdef VIDEO_ENABLED
	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) {
	MediastreamDatas* args =  (MediastreamDatas*)_args;
#ifdef VIDEO_ENABLED
	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) {
	MediastreamDatas* args =  (MediastreamDatas*)_args;
#ifdef VIDEO_ENABLED
	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) {
	MediastreamDatas* args =  (MediastreamDatas*)_args;
#ifdef VIDEO_ENABLED
	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) {
	run_non_interactive_loop((MediastreamDatas*)args);
}

JNIEXPORT void JNICALL Java_org_linphone_mediastream_MediastreamerActivity_clear
  (JNIEnv *env, jobject obj, jint args) {
	clear_mediastreams((MediastreamDatas*)args);
	free((MediastreamDatas*)args);
}
#endif

// HELPER METHODS
static void stop_handler(int signum)
{
	cond--;
	if (cond<0) {
		ms_error("Brutal exit (%d)\n", cond);
		exit(-1);
	}
}

static bool_t parse_addr(const char *addr, char *ip, int len, int *port)
{
	const char *semicolon=NULL;
	int iplen;
	int slen;
	const char *p;

	*port=0;
	semicolon=strchr(addr,':');
	for (p=addr+strlen(addr)-1;p>addr;p--){
		if (*p==':') {
			semicolon=p;
			break;
		}
	}
	if (semicolon==NULL) return FALSE;
	iplen=semicolon-addr;
	slen=MIN(iplen,len-1);
	strncpy(ip,addr,slen);
	ip[slen]='\0';
	*port=atoi(semicolon+1);
	return TRUE;
}

static void display_items(void *user_data, uint32_t csrc, rtcp_sdes_type_t t, const char *content, uint8_t content_len){
	char str[256];
	int len=MIN(sizeof(str)-1,content_len);
	strncpy(str,content,len);
	str[len]='\0';
	switch(t){
		case RTCP_SDES_CNAME:
			ms_message("Found CNAME=%s",str);
		break;
		case RTCP_SDES_TOOL:
			ms_message("Found TOOL=%s",str);
		break;
		case RTCP_SDES_NOTE:
			ms_message("Found NOTE=%s",str);
		break;
		default:
			ms_message("Unhandled SDES item (%s)",str);
	}
}

static void parse_rtcp(mblk_t *m){
	do{
		if (rtcp_is_RR(m)){
			ms_message("Receiving RTCP RR");
		}else if (rtcp_is_SR(m)){
			ms_message("Receiving RTCP SR");
		}else if (rtcp_is_SDES(m)){
			ms_message("Receiving RTCP SDES");
			rtcp_sdes_parse(m,display_items,NULL);
		}else {
			ms_message("Receiving unhandled RTCP message");
		}
	}while(rtcp_next_packet(m));
}

static void parse_events(RtpSession *session, OrtpEvQueue *q){
	OrtpEvent *ev;

	while((ev=ortp_ev_queue_get(q))!=NULL){
		OrtpEventData *d=ortp_event_get_data(ev);
		switch(ortp_event_get_type(ev)){
			case ORTP_EVENT_RTCP_PACKET_RECEIVED:
				parse_rtcp(d->packet);
			break;
			default:
			break;
		}
		ortp_event_destroy(ev);
	}
}

static PayloadType* create_custom_payload_type(const char *type, const char *subtype, const char *rate, int number){
	PayloadType *pt=payload_type_new();
	if (strcasecmp(type,"audio")==0){
		pt->type=PAYLOAD_AUDIO_PACKETIZED;
	}else if (strcasecmp(type,"video")==0){
		pt->type=PAYLOAD_VIDEO;
	}else{
		fprintf(stderr,"Unsupported payload type should be audio or video, not %s\n",type);
		exit(-1);
	}
	pt->mime_type=ms_strdup(subtype);
	pt->clock_rate=atoi(rate);
	return pt;	
}

static PayloadType* parse_custom_payload(const char *name){
	char type[64]={0};
	char subtype[64]={0};
	char clockrate[64]={0};
	char *separator;

	if (strlen(name)>=sizeof(clockrate)-1){
		fprintf(stderr,"Cannot parse %s: too long.\n",name);
		exit(-1);
	}

	separator=strchr(name,'/');
	if (separator){
		char *separator2;

		strncpy(type,name,separator-name);
		separator2=strchr(separator+1,'/');
		if (separator2){
			strncpy(subtype,separator+1,separator2-separator-1);
			strcpy(clockrate,separator2+1);
			fprintf(stdout,"Found custom payload type=%s, mime=%s, clockrate=%s\n",type,subtype,clockrate);
			return create_custom_payload_type(type,subtype,clockrate,114);
		}
	}
	fprintf(stderr,"Error parsing payload name %s.\n",name);
	exit(-1);
}

static bool_t parse_window_ids(const char *ids, int* video_id, int* preview_id)
{
	char* copy = strdup(ids);
	char *semicolon=strchr(copy,':');
	if (semicolon==NULL) {
		free(copy);
		return FALSE;
	}
	*semicolon = '\0';

	*video_id=atoi(copy);
	*preview_id=atoi(semicolon+1);
	free(copy);
	return TRUE;
aymeric's avatar
aymeric committed
974
}