mediastream.c 38.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
/*
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>
aymeric's avatar
aymeric committed
22
#include "mediastreamer2/mediastream.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 80
#define MEDIASTREAM_MAX_ICE_CANDIDATES 3


jehan's avatar
jehan committed
81

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


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

91 92
typedef struct _MediastreamDatas {
	int localport,remoteport,payload;
93
	char ip[64];
94 95 96
	char *fmtp;
	int jitter;
	int bitrate;
97
	int mtu;
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
	MSVideoSize vs;
	bool_t ec;
	bool_t agc;
	bool_t eq;
	bool_t is_verbose;
	int device_rotation;
	VideoStream *video;
	char * capture_card;
	char * playback_card;
	char * camera;
	char *infile,*outfile;
	float ng_threshold;
	bool_t use_ng;
	bool_t two_windows;
	bool_t el;
113 114
	bool_t use_rc;
	bool_t enable_srtp;
115
	bool_t interactive;
116
	bool_t enable_avpf;
117
	bool_t enable_rtcp;
118
	bool_t pad;
119 120 121 122 123 124 125 126 127 128 129 130
	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;
131 132
	char* srtp_local_master_key;
	char* srtp_remote_master_key;
133
	int netsim_bw;
Yann Diorcet's avatar
Yann Diorcet committed
134
	int netsim_lossrate;
135
	int netsim_latency;
136
	float zoom;
137
	float zoom_cx, zoom_cy;
138 139

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

Ghislain MARY's avatar
Ghislain MARY committed
145
	IceSession *ice_session;
146 147 148 149
	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;
150
	char * video_display_filter;
151 152
} MediastreamDatas;

153

154 155 156 157 158 159 160
// 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);
161 162
/* run loop*/
void mediastream_run_loop(MediastreamDatas* args);
163 164 165 166
/* exit */
void clear_mediastreams(MediastreamDatas* args);

// HELPER METHODS
167
void stop_handler(int signum);
168
static bool_t parse_addr(const char *addr, char *ip, int len, int *port);
169
static bool_t parse_ice_addr(char* addr, char* type, int type_len, char* ip, int ip_len, int* port);
170 171 172
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);
173
static PayloadType* create_custom_payload_type(const char *type, const char *subtype, const char *rate, const char *channels, int number);
174 175
static PayloadType* parse_custom_payload(const char *name);
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
								"[ --srtp <local master_key> <remote master_key> (enable srtp, master key is generated if absent from comand line)\n"
209
								"[ --netsim-bandwidth <bandwidth limit in bits/s> (simulates a network download bandwidth limit)\n"
Yann Diorcet's avatar
Yann Diorcet committed
210
								"[ --netsim-lossrate <0-100> (simulates a network lost rate)\n"
211
								"[ --netsim-latency <latency in ms> (simulates a network latency)\n"
212
								"[ --zoom zoomfactor]\n"
213 214
								"[ --ice-local-candidate <ip:port:[host|srflx|prflx|relay]> ]\n"
								"[ --ice-remote-candidate <ip:port:[host|srflx|prflx|relay]> ]\n"
215
								"[ --mtu <mtu> (specify MTU)]\n"
216
								"[ --interactive (run in interactive mode)]\n"
217
								"[ --no-avpf]\n"
218 219
								"[ --no-rtcp]\n"
								;
aymeric's avatar
aymeric committed
220

jehan's avatar
jehan committed
221 222 223 224
#if TARGET_OS_IPHONE
int g_argc;
char** g_argv;
static int _main(int argc, char * argv[]);
225

jehan's avatar
jehan committed
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
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
245

jehan's avatar
jehan committed
246
#if !__APPLE__ && !ANDROID
aymeric's avatar
aymeric committed
247
int main(int argc, char * argv[])
jehan's avatar
jehan committed
248 249
#endif

250
#if !ANDROID && !TARGET_OS_MAC || TARGET_OS_IPHONE
aymeric's avatar
aymeric committed
251
{
252
	MediastreamDatas* args;
253
	cond = 1;
254

255
	args = init_default_args();
256

257 258
	if (!parse_args(argc, argv, args))
		return 0;
259

260
	setup_media_streams(args);
261

262
	mediastream_run_loop(args);
263

264
	clear_mediastreams(args);
265

266
	free(args);
267

268
	return 0;
269 270 271 272 273
}

#endif


274
MediastreamDatas* init_default_args() {
275
	MediastreamDatas* args = (MediastreamDatas*)ms_malloc0(sizeof(MediastreamDatas));
276 277 278 279 280 281 282 283 284 285
	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;
286
	args->interactive=FALSE;
287 288
	args->is_verbose=FALSE;
	args->device_rotation=-1;
aymeric's avatar
aymeric committed
289

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

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

Ghislain MARY's avatar
Ghislain MARY committed
327
	args->ice_session = NULL;
328 329 330
	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;
331
	args->video_display_filter=NULL;
332

333
	return args;
334 335
}

336
bool_t parse_args(int argc, char** argv, MediastreamDatas* out) {
aymeric's avatar
aymeric committed
337
	int i;
jehan's avatar
jehan committed
338

aymeric's avatar
aymeric committed
339
	if (argc<4) {
Simon Morlat's avatar
Simon Morlat committed
340
		printf("%s",usage);
341
		return FALSE;
aymeric's avatar
aymeric committed
342
	}
343 344

	/* default size */
345 346
	out->vs.width=MS_VIDEO_SIZE_CIF_W;
	out->vs.height=MS_VIDEO_SIZE_CIF_H;
347

aymeric's avatar
aymeric committed
348 349 350
	for (i=1;i<argc;i++){
		if (strcmp(argv[i],"--local")==0){
			i++;
351
			out->localport=atoi(argv[i]);
aymeric's avatar
aymeric committed
352 353
		}else if (strcmp(argv[i],"--remote")==0){
			i++;
354
			if (!parse_addr(argv[i],out->ip,sizeof(out->ip),&out->remoteport)) {
Simon Morlat's avatar
Simon Morlat committed
355
				printf("%s",usage);
356
				return FALSE;
aymeric's avatar
aymeric committed
357
			}
358
			printf("Remote addr: ip=%s port=%i\n",out->ip,out->remoteport);
359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386
		}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
387 388
		}else if (strcmp(argv[i],"--payload")==0){
			i++;
389
			if (isdigit(argv[i][0])){
390
				out->payload=atoi(argv[i]);
391
			}else {
392 393
				out->payload=114;
				out->custom_pt=parse_custom_payload(argv[i]);
394
			}
aymeric's avatar
aymeric committed
395 396
		}else if (strcmp(argv[i],"--fmtp")==0){
			i++;
397
			out->fmtp=argv[i];
aymeric's avatar
aymeric committed
398 399
		}else if (strcmp(argv[i],"--jitter")==0){
			i++;
400
			out->jitter=atoi(argv[i]);
aymeric's avatar
aymeric committed
401 402
		}else if (strcmp(argv[i],"--bitrate")==0){
			i++;
403
			out->bitrate=atoi(argv[i]);
404 405
		}else if (strcmp(argv[i],"--width")==0){
			i++;
406
			out->vs.width=atoi(argv[i]);
407 408
		}else if (strcmp(argv[i],"--height")==0){
			i++;
409
			out->vs.height=atoi(argv[i]);
410 411
		}else if (strcmp(argv[i],"--capture-card")==0){
			i++;
412
			out->capture_card=argv[i];
Simon Morlat's avatar
Simon Morlat committed
413 414
		}else if (strcmp(argv[i],"--playback-card")==0){
			i++;
415
			out->playback_card=argv[i];
aymeric's avatar
aymeric committed
416
		}else if (strcmp(argv[i],"--ec")==0){
417
			out->ec=TRUE;
418 419
		}else if (strcmp(argv[i],"--ec-tail")==0){
			i++;
420
			out->ec_len_ms=atoi(argv[i]);
421 422
		}else if (strcmp(argv[i],"--ec-delay")==0){
			i++;
423
			out->ec_delay_ms=atoi(argv[i]);
424 425
		}else if (strcmp(argv[i],"--ec-framesize")==0){
			i++;
426
			out->ec_framesize=atoi(argv[i]);
427
		}else if (strcmp(argv[i],"--agc")==0){
428
			out->agc=TRUE;
smorlat's avatar
smorlat committed
429
		}else if (strcmp(argv[i],"--eq")==0){
430
			out->eq=TRUE;
431
		}else if (strcmp(argv[i],"--ng")==0){
432
			out->use_ng=1;
433
		}else if (strcmp(argv[i],"--rc")==0){
434
			out->use_rc=1;
435 436
		}else if (strcmp(argv[i],"--ng-threshold")==0){
			i++;
437
			out->ng_threshold=atof(argv[i]);
jehan's avatar
jehan committed
438 439
		}else if (strcmp(argv[i],"--ng-floorgain")==0){
			i++;
440
			out->ng_floorgain=atof(argv[i]);
441
		}else if (strcmp(argv[i],"--two-windows")==0){
442
			out->two_windows=TRUE;
443 444
		}else if (strcmp(argv[i],"--infile")==0){
			i++;
445
			out->infile=argv[i];
446 447
		}else if (strcmp(argv[i],"--outfile")==0){
			i++;
448
			out->outfile=argv[i];
449 450
		}else if (strcmp(argv[i],"--camera")==0){
			i++;
451
			out->camera=argv[i];
jehan's avatar
jehan committed
452
		}else if (strcmp(argv[i],"--el")==0){
453
			out->el=TRUE;
jehan's avatar
jehan committed
454 455
		}else if (strcmp(argv[i],"--el-speed")==0){
			i++;
456
			out->el_speed=atof(argv[i]);
jehan's avatar
jehan committed
457 458
		}else if (strcmp(argv[i],"--el-thres")==0){
			i++;
459
			out->el_thres=atof(argv[i]);
jehan's avatar
jehan committed
460 461
		}else if (strcmp(argv[i],"--el-force")==0){
			i++;
462
			out->el_force=atof(argv[i]);
jehan's avatar
jehan committed
463 464
		}else if (strcmp(argv[i],"--el-sustain")==0){
			i++;
465
			out->el_sustain=atoi(argv[i]);
jehan's avatar
jehan committed
466 467
		}else if (strcmp(argv[i],"--el-transmit-thres")==0){
			i++;
468
			out->el_transmit_thres=atof(argv[i]);
469
		} else if (strcmp(argv[i],"--zrtp")==0){
470
			out->zrtp_secrets=argv[++i];
471
		} else if (strcmp(argv[i],"--verbose")==0){
472
			out->is_verbose=TRUE;
473 474
		} else if (strcmp(argv[i], "--video-windows-id")==0) {
			i++;
475
			if (!parse_window_ids(argv[i],&out->video_window_id, &out->preview_window_id)) {
476
				printf("%s",usage);
477
				return FALSE;
478
			}
479
		} else if (strcmp(argv[i], "--device-rotation")==0) {
480
			i++;
481
			out->device_rotation=atoi(argv[i]);
482
		} else if (strcmp(argv[i], "--srtp")==0) {
jehan's avatar
jehan committed
483 484
			if (!media_stream_srtp_supported()) {
				ms_error("srtp support not enabled");
485 486 487 488 489 490 491 492 493
				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++];
			}
494 495 496
		} else if (strcmp(argv[i],"--netsim-bandwidth")==0){
			i++;
			out->netsim_bw=atoi(argv[i]);
Yann Diorcet's avatar
Yann Diorcet committed
497 498 499 500 501 502 503 504 505 506 507
		} else if (strcmp(argv[i],"--netsim-lossrate")==0){
			i++;
			out->netsim_lossrate=atoi(argv[i]);
			if(out->netsim_lossrate < 0) {
				ms_warning("%d < 0, wrong value for --lost-rate: set to 0", out->netsim_lossrate);
				out->netsim_lossrate=0;
			}
			if(out->netsim_lossrate > 100) {
				ms_warning("%d > 100, wrong value for --lost-rate: set to 100", out->netsim_lossrate);
				out->netsim_lossrate=100;
			}
508 509 510
		} else if (strcmp(argv[i], "--netsim-latency") == 0) {
			i++;
			out->netsim_latency = atoi(argv[i]);
Yann Diorcet's avatar
Yann Diorcet committed
511
		} else if (strcmp(argv[i],"--zoom")==0){
512
			i++;
513 514 515 516
			if (sscanf(argv[i], "%f,%f,%f", &out->zoom, &out->zoom_cx, &out->zoom_cy) != 3) {
				ms_error("Invalid zoom triplet");
				return FALSE;
			}
517
		} else if (strcmp(argv[i],"--mtu")==0){
518 519 520 521 522
			i++;
			if (sscanf(argv[i], "%i", &out->mtu) != 1) {
				ms_error("Invalid mtu value");
				return FALSE;
			}
523
		} else if (strcmp(argv[i],"--interactive")==0){
524
			out->interactive=TRUE;
525 526
		} else if (strcmp(argv[i], "--no-avpf") == 0) {
			out->enable_avpf = FALSE;
527 528
		} else if (strcmp(argv[i], "--no-rtcp") == 0) {
			out->enable_rtcp = FALSE;
529
		} else if (strcmp(argv[i],"--help")==0) {
jehan's avatar
jehan committed
530
			printf("%s",usage);
531
			return FALSE;
532 533 534
		} else if (strcmp(argv[i],"--video-display-filter")==0) {
			i++;
			out->video_display_filter=argv[i];
535
		} else {
536
			printf("%s",usage);
537 538
			printf("Unknown option '%s'\n", argv[i]);
			return FALSE;
aymeric's avatar
aymeric committed
539
		}
540
	}
541 542
	return TRUE;
}
543

jehan's avatar
jehan committed
544

545 546 547 548 549
#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
550
			ms_warning("Decoding error on videostream [%p]", md->video);
551 552
			break;
		case MS_VIDEO_DECODER_FIRST_IMAGE_DECODED:
Ghislain MARY's avatar
Ghislain MARY committed
553
			ms_message("First video frame decoded successfully on videostream [%p]", md->video);
554 555 556 557 558 559
			break;
	}
}
#endif


560
void setup_media_streams(MediastreamDatas* args) {
561
	/*create the rtp session */
562
	OrtpNetworkSimulatorParams params={0};
563
#ifdef VIDEO_ENABLED
Sylvain Berfini's avatar
Sylvain Berfini committed
564
	MSWebCam *cam=NULL;
565
#endif
566
	ortp_init();
567
	if (args->is_verbose) {
568 569 570
		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
571
	}
572

jehan's avatar
jehan committed
573

574
#if TARGET_OS_IPHONE || defined(ANDROID)
jehan's avatar
jehan committed
575
#if defined (HAVE_X264) && defined (VIDEO_ENABLED)
576
	libmsx264_init(); /*no plugin on IOS/Android */
jehan's avatar
jehan committed
577
#endif
578 579 580
#if defined (HAVE_OPENH264) && defined (VIDEO_ENABLED)
	libmsopenh264_init(); /*no plugin on IOS/Android */
#endif
jehan's avatar
jehan committed
581
#if defined (HAVE_SILK)
582
	libmssilk_init(); /*no plugin on IOS/Android */
jehan's avatar
jehan committed
583
#endif
584 585 586
#if defined (HAVE_WEBRTC)
	libmswebrtc_init();
#endif	
587 588

#endif /* IPHONE | ANDROID */
589

590 591 592 593
	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);
594
	rtp_profile_set_payload(&av_profile,114,args->custom_pt);
595
	rtp_profile_set_payload(&av_profile,115,&payload_type_lpc1015);
596 597 598 599 600 601 602
#ifdef VIDEO_ENABLED
	rtp_profile_set_payload(&av_profile,26,&payload_type_jpeg);
	rtp_profile_set_payload(&av_profile,98,&payload_type_h263_1998);
	rtp_profile_set_payload(&av_profile,97,&payload_type_theora);
	rtp_profile_set_payload(&av_profile,99,&payload_type_mp4v);
	rtp_profile_set_payload(&av_profile,100,&payload_type_x_snow);
	rtp_profile_set_payload(&av_profile,102,&payload_type_h264);
603
	rtp_profile_set_payload(&av_profile,103,&payload_type_vp8);
604

605
	args->video=NULL;
aymeric's avatar
aymeric committed
606
#endif
607 608
	args->profile=rtp_profile_clone_full(&av_profile);
	args->q=ortp_ev_queue_new();
609

aymeric's avatar
aymeric committed
610
	ms_init();
611
	if (args->mtu) ms_set_mtu(args->mtu);
612 613
	ms_filter_enable_statistics(TRUE);
	ms_filter_reset_statistics();
Ghislain MARY's avatar
Ghislain MARY committed
614
	args->ice_session=ice_session_new();
Ghislain MARY's avatar
Ghislain MARY committed
615 616 617 618
	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);
619

aymeric's avatar
aymeric committed
620
	signal(SIGINT,stop_handler);
621 622
	args->pt=rtp_profile_get_payload(args->profile,args->payload);
	if (args->pt==NULL){
623
		printf("Error: no payload defined with number %i.\n",args->payload);
aymeric's avatar
aymeric committed
624 625
		exit(-1);
	}
626
	if (args->enable_avpf == TRUE) {
627
		PayloadTypeAvpfParams avpf_params;
628
		payload_type_set_flag(args->pt, PAYLOAD_TYPE_RTCP_FEEDBACK_ENABLED);
629
		avpf_params.features = PAYLOAD_TYPE_AVPF_FIR | PAYLOAD_TYPE_AVPF_PLI | PAYLOAD_TYPE_AVPF_SLI | PAYLOAD_TYPE_AVPF_RPSI;
630
		avpf_params.trr_interval = 3000;
631
		payload_type_set_avpf_params(args->pt, avpf_params);
632 633 634
	} else {
		payload_type_unset_flag(args->pt, PAYLOAD_TYPE_RTCP_FEEDBACK_ENABLED);
	}
635 636
	if (args->fmtp!=NULL) payload_type_set_send_fmtp(args->pt,args->fmtp);
	if (args->bitrate>0) args->pt->normal_bitrate=args->bitrate;
637

638 639 640 641 642
	if (args->pt->normal_bitrate==0){
		printf("Error: no default bitrate specified for codec %s/%i. "
			"Please specify a network bitrate with --bitrate option.\n",args->pt->mime_type,args->pt->clock_rate);
		exit(-1);
	}
aymeric's avatar
aymeric committed
643

644 645 646 647 648
	// 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
649 650
			char tmp[30];
			snprintf(tmp,sizeof(tmp),"%08x%08x%08x%08x",rand(),rand(),rand(),rand());
651 652 653 654 655 656
			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
657 658
			char tmp[30];
			snprintf(tmp,sizeof(tmp),"%08x%08x%08x%08x",rand(),rand(),rand(),rand());
659 660 661 662 663
			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);
		}
664
	}
665

666
	if (args->pt->type!=PAYLOAD_VIDEO){
667
		MSSndCardManager *manager=ms_snd_card_manager_get();
668 669 670 671
		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);
672
		args->audio=audio_stream_new(args->localport,args->localport+1,ms_is_ipv6(args->ip));
673 674 675 676 677
		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
678
		printf("Starting audio stream.\n");
679

680
		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,
681 682
		                        args->outfile==NULL ? play : NULL ,args->infile==NULL ? capt : NULL,args->infile!=NULL ? FALSE: args->ec);

683
		if (args->ice_local_candidates_nb || args->ice_remote_candidates_nb) {
684
			args->audio->ms.ice_check_list = ice_check_list_new();
685
			rtp_session_set_pktinfo(args->audio->ms.sessions.rtp_session,TRUE);
686
			ice_session_add_check_list(args->ice_session, args->audio->ms.ice_check_list, 0);
687
		}
688 689 690 691 692
		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];
693 694
				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);
695 696 697
			}
		}
		if (args->ice_remote_candidates_nb) {
698
			char foundation[4];
699 700 701 702
			MediastreamIceCandidate *candidate;
			int c;
			for (c=0;c<args->ice_remote_candidates_nb;c++){
				candidate=&args->ice_remote_candidates[c];
703 704
				memset(foundation, '\0', sizeof(foundation));
				snprintf(foundation, sizeof(foundation) - 1, "%u", c + 1);
705 706
				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);
707 708 709
			}
		}

710 711 712 713 714 715 716 717 718 719 720 721
		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
722 723

			}
724 725 726 727
			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
728
				}
729 730 731
				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
732 733
				}
			}
734

735
            #ifndef TARGET_OS_IPHONE
Guillaume Beraudo's avatar
Guillaume Beraudo committed
736
			if (args->zrtp_secrets != NULL) {
737
				OrtpZrtpParams params;
738 739
				params.zid_file=args->zrtp_secrets;
				audio_stream_enable_zrtp(args->audio,&params);
740
			}
741
            #endif
742

743
			args->session=args->audio->ms.sessions.rtp_session;
744
		}
745

746
		if (args->enable_srtp) {
747
			ms_message("SRTP enabled: %d",
748
				audio_stream_enable_srtp(
749
					args->audio,
jehan's avatar
jehan committed
750
					MS_AES_128_SHA1_80,
751
					args->srtp_local_master_key,
752 753
					args->srtp_remote_master_key));
		}
aymeric's avatar
aymeric committed
754 755
	}else{
#ifdef VIDEO_ENABLED
756 757 758 759
		float zoom[] = {
			args->zoom,
			args->zoom_cx, args->zoom_cy };

760
		if (args->eq){
smorlat's avatar
smorlat committed
761 762 763
			ms_fatal("Cannot put an audio equalizer in a video stream !");
			exit(-1);
		}
764
		ms_message("Starting video stream.\n");
765
		args->video=video_stream_new(args->localport, args->localport+1, ms_is_ipv6(args->ip));
766 767 768
		if (args->video_display_filter)
			video_stream_set_display_filter_name(args->video, args->video_display_filter);
		
769
#ifdef ANDROID
770 771
		if (args->device_rotation >= 0)
			video_stream_set_device_rotation(args->video, args->device_rotation);
772
#endif
773 774
		video_stream_set_sent_video_size(args->video,args->vs);
		video_stream_use_preview_video_window(args->video,args->two_windows);
775
#if TARGET_OS_IPHONE
jehan's avatar
jehan committed
776 777 778
		NSBundle* myBundle = [NSBundle mainBundle];
		const char*  nowebcam = [[myBundle pathForResource:@"nowebcamCIF"ofType:@"jpg"] cStringUsingEncoding:[NSString defaultCStringEncoding]];
		ms_static_image_set_default_image(nowebcam);
779 780
		NSUInteger cpucount = [[NSProcessInfo processInfo] processorCount];
		ms_set_cpu_count(cpucount);
jehan's avatar
jehan committed
781
#endif
782
		video_stream_set_event_callback(args->video,video_stream_event_cb, args);
jehan's avatar
jehan committed
783

784
		video_stream_enable_adaptive_bitrate_control(args->video,args->use_rc);
785 786
		if (args->camera)
			cam=ms_web_cam_manager_get_cam(ms_web_cam_manager_get(),args->camera);
787 788
		if (cam==NULL)
			cam=ms_web_cam_manager_get_default_cam(ms_web_cam_manager_get());
789
		video_stream_start(args->video,args->profile,
790
					args->ip,args->remoteport,
791
					args->ip,args->enable_rtcp?args->remoteport+1:-1,
792 793
					args->payload,
					args->jitter,cam
794
					);
795
		args->session=args->video->ms.sessions.rtp_session;
796

797
		ms_filter_call_method(args->video->output,MS_VIDEO_DISPLAY_ZOOM, zoom);
798
		if (args->enable_srtp) {
799
			ms_message("SRTP enabled: %d",
800
				video_stream_enable_strp(
801
					args->video,
jehan's avatar
jehan committed
802
					MS_AES_128_SHA1_80,
803
					args->srtp_local_master_key,
804 805
					args->srtp_remote_master_key));
		}
aymeric's avatar
aymeric committed
806 807 808 809
#else
		printf("Error: video support not compiled.\n");
#endif
	}
810 811 812
	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
813
	ice_session_choose_default_remote_candidates(args->ice_session);
Ghislain MARY's avatar
Ghislain MARY committed
814
	ice_session_start_connectivity_checks(args->ice_session);
815

816 817 818
	if (args->netsim_bw>0){
		params.enabled=TRUE;
		params.max_bandwidth=args->netsim_bw;
Yann Diorcet's avatar
Yann Diorcet committed
819 820 821 822 823
	}
	if (args->netsim_lossrate>0){
		params.enabled=TRUE;
		params.loss_rate=args->netsim_lossrate;
	}
824 825 826 827
	if (args->netsim_latency > 0) {
		params.enabled = TRUE;
		params.latency = args->netsim_latency;
	}
Yann Diorcet's avatar
Yann Diorcet committed
828
	if (params.enabled){
829 830
		rtp_session_enable_network_simulation(args->session,&params);
	}
Yann Diorcet's avatar
Yann Diorcet committed
831

832 833 834
}


835
static void mediastream_tool_iterate(MediastreamDatas* args) {
836 837 838
#ifndef WIN32
	struct pollfd pfd;
	int err;
839

840 841
	if (args->interactive){
		pfd.fd=STDIN_FILENO;
842
		pfd.events=POLLIN;
843
		pfd.revents=0;
844

845
		err=poll(&pfd,1,10);
846
		if (err==1 && (pfd.revents & POLLIN)){
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
			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]);
						}
877
					}
878 879 880 881 882 883
					printf("\nOK\n");
				}else if (sscanf(commands,"lossrate %i",&intarg)==1){
					OrtpNetworkSimulatorParams params={0};
					params.enabled=TRUE;
					params.loss_rate=intarg;
					rtp_session_enable_network_simulation(args->session,&params);
884
				}
885 886 887
				else if (strstr(commands,"quit")){
					cond=0;
				}else printf("Cannot understand this.\n");
888
			}
889 890
		}else if (err==-1 && errno!=EINTR){
			ms_fatal("mediastream's poll() returned %s",strerror(errno));
891
		}
892 893
	}else{
		ms_usleep(10000);
894 895 896 897 898 899 900 901 902 903
	}
#else
	MSG msg;
	Sleep(10);
	while (PeekMessage(&msg, NULL, 0, 0,1)){
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	/*no interactive mode on windows*/
#endif
904 905
}

906
void mediastream_run_loop(MediastreamDatas* args) {
907 908
	rtp_session_register_event_queue(args->session,args->q);

909
#if TARGET_OS_IPHONE
jehan's avatar
jehan committed
910
	if (args->video) ms_set_video_stream(args->video); /*for IOS*/
911
#endif
912

913 914 915
	while(cond)
	{
		int n;
916
		for(n=0;n<100 && cond;++n){
917
			mediastream_tool_iterate(args);
918 919 920 921 922 923 924
#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){
925 926 927
			float audio_load = 0;
			float video_load = 0;
			
928
			ms_message("Bandwidth usage: download=%f kbits/sec, upload=%f kbits/sec\n",
929 930
				rtp_session_get_recv_bandwidth(args->session)*1e-3,
				rtp_session_get_send_bandwidth(args->session)*1e-3);
931 932 933 934 935 936 937 938 939 940
			
			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);
941
			parse_events(args->session,args->q);
942
			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
943
		}
Simon Morlat's avatar
Simon Morlat committed
944
	}
945
}
946

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

951 952 953
	if (args->audio) {
		audio_stream_stop(args->audio);
	}
aymeric's avatar
aymeric committed
954
#ifdef VIDEO_ENABLED
955
	if (args->video) {
956
		if (args->video->ms.ice_check_list) ice_check_list_destroy(args->video->ms.ice_check_list);
957
		video_stream_stop(args->video);
958 959
		ms_filter_log_statistics();
	}
aymeric's avatar
aymeric committed
960
#endif
Ghislain MARY's avatar
Ghislain MARY committed
961
	if (args->ice_session) ice_session_destroy(args->ice_session);
962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065
	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) {
1066
	mediastream_run_loop((MediastreamDatas*)args);
1067 1068 1069 1070 1071 1072 1073 1074 1075 1076
}

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
1077
void stop_handler(int signum)
1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109
{
	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;
}

1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123
static bool_t parse_ice_addr(char *addr, char *type, int type_len, char *ip, int ip_len, int *port)
{
	char *semicolon=NULL;
	int slen;

	semicolon=strrchr(addr,':');
	if (semicolon==NULL) return FALSE;
	slen=MIN(strlen(semicolon+1),type_len);
	strncpy(type,semicolon+1,slen);
	type[slen]='\0';
	*semicolon='\0';
	return parse_addr(addr,ip,ip_len,port);
}

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 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167
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;
1168 1169 1170
			case ORTP_EVENT_RTCP_PACKET_EMITTED:
				ms_message("Jitter buffer size: %f ms",rtp_session_get_jitter_stats(session)->jitter_buffer_size_ms);
			break;
1171 1172 1173 1174 1175 1176 1177
			default:
			break;
		}
		ortp_event_destroy(ev);
	}
}

1178
static PayloadType* create_custom_payload_type(const char *type, const char *subtype, const char *rate, const char *channels, int number){
1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189
	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);
1190
	pt->channels=atoi(channels);
1191
	return pt;
1192 1193 1194 1195 1196 1197
}

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