mediastream.c 28.6 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

smorlat's avatar
smorlat committed
24 25
#include <math.h>

aymeric's avatar
aymeric committed
26
#include "mediastreamer2/mediastream.h"
smorlat's avatar
smorlat committed
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
Simon Morlat's avatar
Simon Morlat committed
48 49
#if  defined(__ios) || defined (ANDROID)
#ifdef __ios
jehan's avatar
jehan committed
50
#import <UIKit/UIKit.h>
jehan's avatar
jehan committed
51
#include <AudioToolbox/AudioToolbox.h>
Simon Morlat's avatar
Simon Morlat committed
52
#endif
jehan's avatar
jehan committed
53
extern void ms_set_video_stream(VideoStream* video);
jehan's avatar
jehan committed
54 55 56
#ifdef HAVE_X264
extern void libmsx264_init();
#endif
jehan's avatar
jehan committed
57 58 59
#ifdef HAVE_SILK
extern void libmssilk_init();
#endif 
jehan's avatar
jehan committed
60
#endif
61

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

67 68
#include <ortp/b64.h>

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


72

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

86 87 88 89 90 91 92 93 94 95 96
#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;
97 98 99
	bool_t use_rc;
	bool_t enable_srtp;
	bool_t pad[3];
100 101 102 103 104 105 106 107 108 109 110 111
	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;
112 113
	char* srtp_local_master_key;
	char* srtp_remote_master_key;
114
	int netsim_bw;
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 144
	
	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);
145

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

jehan's avatar
jehan committed
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
#if TARGET_OS_IPHONE
int g_argc;
char** g_argv;
static int _main(int argc, char * argv[]);
 
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
204

jehan's avatar
jehan committed
205
#if !__APPLE__ && !ANDROID
aymeric's avatar
aymeric committed
206
int main(int argc, char * argv[])
jehan's avatar
jehan committed
207 208 209
#endif

#if !ANDROID && !TARGET_OS_MACX 
aymeric's avatar
aymeric committed
210
{
211
	MediastreamDatas* args;
212
	cond = 1;
213 214
	
	args = init_default_args();
215

216 217
	if (!parse_args(argc, argv, args))
		return 0;
218

jehan's avatar
jehan committed
219 220 221 222 223 224 225 226 227
/*	if (!args->capture_card || strcmp("AU: Audio Unit Fast Receiver", args->capture_card) == 0) {
		AudioSessionInitialize(NULL,NULL,NULL,NULL);
		if (!AudioSessionSetActive(true)) {
			ms_fatal ("Cannot activate audio session");
		};
		OSStatus auresult;
		UInt32 audioCategory=kAudioSessionCategory_PlayAndRecord;
		auresult =AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(audioCategory), &audioCategory);
	}*/
228
	setup_media_streams(args);
229

230 231 232 233 234 235
	if (args->eq)
		run_interactive_loop(args);
	else
		run_non_interactive_loop(args);
	
	clear_mediastreams(args);
236

237 238 239
	free(args);
	
	return 0;
240 241 242 243 244
}

#endif


245
MediastreamDatas* init_default_args() {
246
	MediastreamDatas* args = (MediastreamDatas*)ms_malloc0(sizeof(MediastreamDatas));
247 248 249 250 251 252 253 254 255 256 257 258
	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
259

260
#ifdef VIDEO_ENABLED
261
	args->video=NULL;
262
#endif
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283
	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;
284 285
	args->enable_srtp = FALSE;
	args->srtp_local_master_key = args->srtp_remote_master_key = NULL;
286 287 288 289 290 291 292 293

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

	return args;
294 295
}

296
bool_t parse_args(int argc, char** argv, MediastreamDatas* out) {
aymeric's avatar
aymeric committed
297
	int i;
jehan's avatar
jehan committed
298

aymeric's avatar
aymeric committed
299
	if (argc<4) {
Simon Morlat's avatar
Simon Morlat committed
300
		printf("%s",usage);
301
		return FALSE;
aymeric's avatar
aymeric committed
302
	}
303 304

	/* default size */
305 306
	out->vs.width=MS_VIDEO_SIZE_CIF_W;
	out->vs.height=MS_VIDEO_SIZE_CIF_H;
307

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

jehan's avatar
jehan committed
437

438
void setup_media_streams(MediastreamDatas* args) {
Guillaume Beraudo's avatar
Guillaume Beraudo committed
439
	/*create the rtp session */
Sylvain Berfini's avatar
Sylvain Berfini committed
440 441
	MSWebCam *cam=NULL;

Guillaume Beraudo's avatar
Guillaume Beraudo committed
442
	ortp_init();
443
	if (args->is_verbose) {
Guillaume Beraudo's avatar
Guillaume Beraudo committed
444 445 446
		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
447
	}
448

jehan's avatar
jehan committed
449

Simon Morlat's avatar
Simon Morlat committed
450
#if defined(__ios) || defined(ANDROID)
jehan's avatar
jehan committed
451 452 453 454 455 456 457 458 459
#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
	
Guillaume Beraudo's avatar
Guillaume Beraudo committed
460 461 462 463
	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);
464
	rtp_profile_set_payload(&av_profile,114,args->custom_pt);
465
	rtp_profile_set_payload(&av_profile,115,&payload_type_lpc1015);
Guillaume Beraudo's avatar
Guillaume Beraudo committed
466
#ifdef VIDEO_ENABLED
467
#if defined (__ios) && defined (HAVE_X264)
468 469
	libmsx264_init(); /*no plugin on IOS*/
#endif
Guillaume Beraudo's avatar
Guillaume Beraudo committed
470 471 472 473 474 475
	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);
476
	rtp_profile_set_payload(&av_profile,103,&payload_type_vp8);
477 478
	
	args->video=NULL;
aymeric's avatar
aymeric committed
479
#endif
480 481
	args->profile=rtp_profile_clone_full(&av_profile);
	args->q=ortp_ev_queue_new();
jehan's avatar
jehan committed
482

aymeric's avatar
aymeric committed
483
	ms_init();
484 485 486
	ms_filter_enable_statistics(TRUE);
	ms_filter_reset_statistics();

aymeric's avatar
aymeric committed
487
	signal(SIGINT,stop_handler);
488 489 490
	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
491 492
		exit(-1);
	}
493 494
	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
495

496 497 498 499 500 501
	// 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
502
			ortp_crypto_get_random(tmp, 30);
503 504 505 506 507 508 509
			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
510
			ortp_crypto_get_random(tmp, 30);
511 512 513 514 515 516 517
			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);
		}
	}	

518
	if (args->pt->type!=PAYLOAD_VIDEO){
519
		MSSndCardManager *manager=ms_snd_card_manager_get();
520 521 522 523 524 525 526 527 528 529
		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
530
		printf("Starting audio stream.\n");
531

532 533 534 535 536 537 538 539 540 541 542 543 544 545 546
		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
547 548

			}
549 550 551 552
			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
553
				}
554 555 556
				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
557 558
				}
			}
Guillaume Beraudo's avatar
Guillaume Beraudo committed
559

560
            #ifndef TARGET_OS_IPHONE
Guillaume Beraudo's avatar
Guillaume Beraudo committed
561
			if (args->zrtp_secrets != NULL) {
Guillaume Beraudo's avatar
Guillaume Beraudo committed
562
				OrtpZrtpParams params;
563 564
				params.zid_file=args->zrtp_secrets;
				audio_stream_enable_zrtp(args->audio,&params);
Guillaume Beraudo's avatar
Guillaume Beraudo committed
565
			}
566
            #endif
Guillaume Beraudo's avatar
Guillaume Beraudo committed
567

568
			args->session=args->audio->session;
569
		}
570 571 572 573 574 575 576 577 578
		
		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
579 580
	}else{
#ifdef VIDEO_ENABLED
581
		if (args->eq){
smorlat's avatar
smorlat committed
582 583 584
			ms_fatal("Cannot put an audio equalizer in a video stream !");
			exit(-1);
		}
585 586
		ms_message("Starting video stream.\n");
		args->video=video_stream_new(args->localport, ms_is_ipv6(args->ip));
587
#ifdef ANDROID
588 589
		if (args->device_rotation >= 0)
			video_stream_set_device_rotation(args->video, args->device_rotation);
590
#endif
591 592
		video_stream_set_sent_video_size(args->video,args->vs);
		video_stream_use_preview_video_window(args->video,args->two_windows);
Simon Morlat's avatar
Simon Morlat committed
593
#ifdef __ios
jehan's avatar
jehan committed
594 595 596 597 598
		NSBundle* myBundle = [NSBundle mainBundle];
		const char*  nowebcam = [[myBundle pathForResource:@"nowebcamCIF"ofType:@"jpg"] cStringUsingEncoding:[NSString defaultCStringEncoding]];
		ms_static_image_set_default_image(nowebcam);
#endif

599
		video_stream_enable_adaptive_bitrate_control(args->video,args->use_rc);
600 601
		if (args->camera)
			cam=ms_web_cam_manager_get_cam(ms_web_cam_manager_get(),args->camera);
602 603
		if (cam==NULL)
			cam=ms_web_cam_manager_get_default_cam(ms_web_cam_manager_get());
604 605 606 607 608
		video_stream_start(args->video,args->profile,
					args->ip,
					args->remoteport,args->remoteport+1,
					args->payload,
					args->jitter,cam
609
					);
610
		args->session=args->video->session;
611 612 613 614 615 616 617 618 619
		
		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
620 621 622 623
#else
		printf("Error: video support not compiled.\n");
#endif
	}
624 625 626 627 628 629
	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);
	}
630 631 632 633
}


void run_interactive_loop(MediastreamDatas* args) {
634 635 636
		char commands[128];
		commands[127]='\0';
		ms_sleep(1);  /* ensure following text be printed after ortp messages */
637
		if (args->eq)
638 639 640 641 642 643 644
		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){
645
				audio_stream_enable_equalizer(args->audio,active);
646 647
				printf("OK\n");
			}else if (sscanf(commands,"eq %i %f %i",&freq,&gain,&freq_width)==3){
648
				audio_stream_equalizer_set_gain(args->audio,freq,gain,freq_width);
649 650
				printf("OK\n");
			}else if (sscanf(commands,"eq %i %f",&freq,&gain)==2){
651
				audio_stream_equalizer_set_gain(args->audio,freq,gain,0);
652 653 654 655
				printf("OK\n");
			}else if (strstr(commands,"dump")){
				int n=0,i;
				float *t;
656
				ms_filter_call_method(args->audio->equalizer,MS_EQUALIZER_GET_NUM_FREQUENCIES,&n);
657
				t=(float*)alloca(sizeof(float)*n);
658
				ms_filter_call_method(args->audio->equalizer,MS_EQUALIZER_DUMP_STATE,t);
659 660
				for(i=0;i<n;++i){
					if (fabs(t[i]-1)>0.01){
661
					printf("%i:%f:0 ",(i*args->pt->clock_rate)/(2*n),t[i]);
662 663 664 665 666 667 668
					}
				}
				printf("\nOK\n");
			} else if (strstr(commands,"quit")){
				break;
			}else printf("Cannot understand this.\n");
		}
669 670 671 672 673
}

void run_non_interactive_loop(MediastreamDatas* args) {
	rtp_session_register_event_queue(args->session,args->q);

674
	#ifdef __ios
jehan's avatar
jehan committed
675
	if (args->video) ms_set_video_stream(args->video); /*for IOS*/
676 677
    #endif

678 679 680 681 682 683 684 685 686 687
	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
688
			}
689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706
#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
707
		}
Simon Morlat's avatar
Simon Morlat committed
708
	}
709
}
710

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

715
	if (args->audio) audio_stream_stop(args->audio);
aymeric's avatar
aymeric committed
716
#ifdef VIDEO_ENABLED
717 718
	if (args->video) {
		video_stream_stop(args->video);
719 720
		ms_filter_log_statistics();
	}
aymeric's avatar
aymeric committed
721
#endif
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 974 975 976 977
	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
978
}