mediastream.c 37.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
/*
mediastreamer2 library - modular sound and video processing and streaming
Copyright (C) 2006  Simon MORLAT (simon.morlat@linphone.org)

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/


21
#include <math.h>
22
#include "common.h"
23
#include "mediastreamer2/msequalizer.h"
24
#include "mediastreamer2/msvolume.h"
aymeric's avatar
aymeric committed
25 26 27 28
#ifdef VIDEO_ENABLED
#include "mediastreamer2/msv4l.h"
#endif

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

42 43 44
#ifdef __APPLE__
#include <CoreFoundation/CFRunLoop.h>
#endif
45 46
#if  TARGET_OS_IPHONE || defined (ANDROID)
#if TARGET_OS_IPHONE
jehan's avatar
jehan committed
47
#import <UIKit/UIKit.h>
jehan's avatar
jehan committed
48
#include <AudioToolbox/AudioToolbox.h>
49
#endif
jehan's avatar
jehan committed
50
extern void ms_set_video_stream(VideoStream* video);
jehan's avatar
jehan committed
51 52 53
#ifdef HAVE_X264
extern void libmsx264_init();
#endif
54 55 56
#ifdef HAVE_OPENH264
extern void libmsopenh264_init();
#endif
jehan's avatar
jehan committed
57 58
#ifdef HAVE_SILK
extern void libmssilk_init();
59
#endif
60 61 62
#ifdef HAVE_WEBRTC
extern void libmswebrtc_init();
#endif  
jehan's avatar
jehan committed
63
#endif
64

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

70 71
#include <ortp/b64.h>

72 73 74 75 76 77

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


78 79 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
	bool_t use_rc;
114
	
115
	bool_t enable_srtp;
116
	bool_t interactive;
117
	bool_t enable_avpf;
118
	bool_t enable_rtcp;
119 120 121
	
	bool_t freeze_on_error;
	bool_t pad[3];
122 123 124 125 126 127 128 129 130 131 132 133
	float el_speed;
	float el_thres;
	float el_force;
	int el_sustain;
	float el_transmit_thres;
	float ng_floorgain;
	char * zrtp_secrets;
	PayloadType *custom_pt;
	int video_window_id;
	int preview_window_id;
	/* starting values echo canceller */
	int ec_len_ms, ec_delay_ms, ec_framesize;
134 135
	char* srtp_local_master_key;
	char* srtp_remote_master_key;
136
	OrtpNetworkSimulatorParams netsim;
137
	float zoom;
138
	float zoom_cx, zoom_cy;
139 140

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

Ghislain MARY's avatar
Ghislain MARY committed
146
	IceSession *ice_session;
147 148 149 150
	MediastreamIceCandidate ice_local_candidates[MEDIASTREAM_MAX_ICE_CANDIDATES];
	MediastreamIceCandidate ice_remote_candidates[MEDIASTREAM_MAX_ICE_CANDIDATES];
	int ice_local_candidates_nb;
	int ice_remote_candidates_nb;
151
	char * video_display_filter;
152
	FILE * logfile;
153 154
} MediastreamDatas;

155

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

// HELPER METHODS
169
void stop_handler(int signum);
170
static bool_t parse_addr(const char *addr, char *ip, int len, int *port);
171
static bool_t parse_ice_addr(char* addr, char* type, int type_len, char* ip, int ip_len, int* port);
172 173 174 175
static void display_items(void *user_data, uint32_t csrc, rtcp_sdes_type_t t, const char *content, uint8_t content_len);
static void parse_rtcp(mblk_t *m);
static void parse_events(RtpSession *session, OrtpEvQueue *q);
static bool_t parse_window_ids(const char *ids, int* video_id, int* preview_id);
176

177 178
const char *usage="mediastream --local <port> --remote <ip:port> \n"
								"--payload <payload type number or payload name like 'audio/pmcu/8000'>\n"
179 180 181
								"[ --fmtp <fmtpline> ]\n"
								"[ --jitter <miliseconds> ]\n"
								"[ --width <pixels> ]\n"
smorlat's avatar
smorlat committed
182
								"[ --height <pixels> ]\n"
183 184
								"[ --bitrate <bits per seconds> ]\n"
								"[ --ec (enable echo canceller) ]\n"
185 186
								"[ --ec-tail <echo canceller tail length in ms> ]\n"
								"[ --ec-delay <echo canceller delay in ms> ]\n"
187
								"[ --ec-framesize <echo canceller framesize in samples> ]\n"
188 189 190 191 192 193 194 195
								"[ --agc (enable automatic gain control) ]\n"
								"[ --ng (enable noise gate)] \n"
								"[ --ng-threshold <(float) [0-1]> (noise gate threshold) ]\n"
								"[ --ng-floorgain <(float) [0-1]> (gain applied to the signal when its energy is below the threshold.) ]\n"
								"[ --capture-card <name> ]\n"
								"[ --playback-card <name> ]\n"
								"[ --infile	<input wav file> specify a wav file to be used for input, instead of soundcard ]\n"
								"[ --outfile <output wav file> specify a wav file to write audio into, instead of soundcard ]\n"
jehan's avatar
jehan committed
196 197 198 199 200 201
								"[ --camera <camera id as listed at startup> ]\n"
								"[ --el (enable echo limiter) ]\n"
								"[ --el-speed <(float) [0-1]> (gain changes are smoothed with a coefficent) ]\n"
								"[ --el-thres <(float) [0-1]> (Threshold above which the system becomes active) ]\n"
								"[ --el-force <(float) [0-1]> (The proportional coefficient controlling the mic attenuation) ]\n"
								"[ --el-sustain <(int)> (Time in milliseconds for which the attenuation is kept unchanged after) ]\n"
202
								"[ --el-transmit-thres <(float) [0-1]> (TO BE DOCUMENTED) ]\n"
203
								"[ --rc (enable adaptive rate control) ]\n"
Guillaume Beraudo's avatar
Guillaume Beraudo committed
204
								"[ --zrtp <secrets file> (enable zrtp) ]\n"
205
								"[ --verbose (most verbose messages) ]\n"
206
								"[ --video-windows-id <video surface:preview surface>]\n"
207
								"[ --video-display-filter <name> ]\n"
208
								"[ --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
								"[ --freeze-on-error (for video, stop upon decoding error until next valid frame)]\n"
219
								"[ --no-rtcp]\n"
220
								"[ --log <file>]\n"
221
								;
aymeric's avatar
aymeric committed
222

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

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

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

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

257
	args = init_default_args();
258

259 260
	if (!parse_args(argc, argv, args))
		return 0;
261

262
	setup_media_streams(args);
263

264
	mediastream_run_loop(args);
265

266
	clear_mediastreams(args);
267

268
	free(args);
269

270
	return 0;
271 272 273 274 275
}

#endif


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

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

	args->audio = NULL;
	args->session = NULL;
	args->pt = NULL;
	args->q = NULL;
	args->profile = NULL;
328
	args->logfile = NULL;
329

Ghislain MARY's avatar
Ghislain MARY committed
330
	args->ice_session = NULL;
331 332 333
	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;
334
	args->video_display_filter=NULL;
335

336
	return args;
337 338
}

339
bool_t parse_args(int argc, char** argv, MediastreamDatas* out) {
aymeric's avatar
aymeric committed
340
	int i;
jehan's avatar
jehan committed
341

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

	/* default size */
348 349
	out->vs.width=MS_VIDEO_SIZE_CIF_W;
	out->vs.height=MS_VIDEO_SIZE_CIF_H;
350

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

jehan's avatar
jehan committed
555

556 557 558 559 560
#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
561
			ms_warning("Decoding error on videostream [%p]", md->video);
562 563
			break;
		case MS_VIDEO_DECODER_FIRST_IMAGE_DECODED:
Ghislain MARY's avatar
Ghislain MARY committed
564
			ms_message("First video frame decoded successfully on videostream [%p]", md->video);
565 566 567 568 569 570
			break;
	}
}
#endif


571
void setup_media_streams(MediastreamDatas* args) {
572
	/*create the rtp session */
573
#ifdef VIDEO_ENABLED
Sylvain Berfini's avatar
Sylvain Berfini committed
574
	MSWebCam *cam=NULL;
575
#endif
576
	ortp_init();
577 578 579
	if (args->logfile)
		ortp_set_log_file(args->logfile);
	
580
	if (args->is_verbose) {
581 582 583
		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
584
	}
585

586
    ms_init();
jehan's avatar
jehan committed
587

588
#if TARGET_OS_IPHONE || defined(ANDROID)
jehan's avatar
jehan committed
589
#if defined (HAVE_X264) && defined (VIDEO_ENABLED)
590
	libmsx264_init(); /*no plugin on IOS/Android */
jehan's avatar
jehan committed
591
#endif
592 593 594
#if defined (HAVE_OPENH264) && defined (VIDEO_ENABLED)
	libmsopenh264_init(); /*no plugin on IOS/Android */
#endif
jehan's avatar
jehan committed
595
#if defined (HAVE_SILK)
596
	libmssilk_init(); /*no plugin on IOS/Android */
jehan's avatar
jehan committed
597
#endif
598 599 600
#if defined (HAVE_WEBRTC)
	libmswebrtc_init();
#endif	
601 602

#endif /* IPHONE | ANDROID */
603

604 605 606 607
	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);
608
	rtp_profile_set_payload(&av_profile,114,args->custom_pt);
609
	rtp_profile_set_payload(&av_profile,115,&payload_type_lpc1015);
610 611 612 613 614 615 616
#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);
617
	rtp_profile_set_payload(&av_profile,103,&payload_type_vp8);
618

619
	args->video=NULL;
aymeric's avatar
aymeric committed
620
#endif
621 622
	args->profile=rtp_profile_clone_full(&av_profile);
	args->q=ortp_ev_queue_new();
623

624
	if (args->mtu) ms_set_mtu(args->mtu);
625 626
	ms_filter_enable_statistics(TRUE);
	ms_filter_reset_statistics();
Ghislain MARY's avatar
Ghislain MARY committed
627
	args->ice_session=ice_session_new();
Ghislain MARY's avatar
Ghislain MARY committed
628 629 630 631
	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);
632

aymeric's avatar
aymeric committed
633
	signal(SIGINT,stop_handler);
634 635
	args->pt=rtp_profile_get_payload(args->profile,args->payload);
	if (args->pt==NULL){
636
		printf("Error: no payload defined with number %i.\n",args->payload);
aymeric's avatar
aymeric committed
637 638
		exit(-1);
	}
639
	if (args->enable_avpf == TRUE) {
640
		PayloadTypeAvpfParams avpf_params;
641
		payload_type_set_flag(args->pt, PAYLOAD_TYPE_RTCP_FEEDBACK_ENABLED);
642
		avpf_params.features = PAYLOAD_TYPE_AVPF_FIR | PAYLOAD_TYPE_AVPF_PLI | PAYLOAD_TYPE_AVPF_SLI | PAYLOAD_TYPE_AVPF_RPSI;
643
		avpf_params.trr_interval = 3000;
644
		payload_type_set_avpf_params(args->pt, avpf_params);
645 646 647
	} else {
		payload_type_unset_flag(args->pt, PAYLOAD_TYPE_RTCP_FEEDBACK_ENABLED);
	}
648 649
	if (args->fmtp!=NULL) payload_type_set_send_fmtp(args->pt,args->fmtp);
	if (args->bitrate>0) args->pt->normal_bitrate=args->bitrate;
650

651 652 653 654 655
	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
656

657 658 659 660 661
	// 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
662 663
			char tmp[30];
			snprintf(tmp,sizeof(tmp),"%08x%08x%08x%08x",rand(),rand(),rand(),rand());
664 665 666 667 668 669
			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
670 671
			char tmp[30];
			snprintf(tmp,sizeof(tmp),"%08x%08x%08x%08x",rand(),rand(),rand(),rand());
672 673 674 675 676
			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);
		}
677
	}
678

679
	if (args->pt->type!=PAYLOAD_VIDEO){
680
		MSSndCardManager *manager=ms_snd_card_manager_get();
681 682 683 684
		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);
685
		args->audio=audio_stream_new(args->localport,args->localport+1,ms_is_ipv6(args->ip));
686 687 688 689 690
		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
691
		printf("Starting audio stream.\n");
692

693
		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,
694 695
		                        args->outfile==NULL ? play : NULL ,args->infile==NULL ? capt : NULL,args->infile!=NULL ? FALSE: args->ec);

696
		if (args->ice_local_candidates_nb || args->ice_remote_candidates_nb) {
697
			args->audio->ms.ice_check_list = ice_check_list_new();
698
			rtp_session_set_pktinfo(args->audio->ms.sessions.rtp_session,TRUE);
699
			ice_session_add_check_list(args->ice_session, args->audio->ms.ice_check_list, 0);
700
		}
701 702 703 704 705
		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];
706 707
				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);
708 709 710
			}
		}
		if (args->ice_remote_candidates_nb) {
711
			char foundation[4];
712 713 714 715
			MediastreamIceCandidate *candidate;
			int c;
			for (c=0;c<args->ice_remote_candidates_nb;c++){
				candidate=&args->ice_remote_candidates[c];
716 717
				memset(foundation, '\0', sizeof(foundation));
				snprintf(foundation, sizeof(foundation) - 1, "%u", c + 1);
718 719
				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);
720 721 722
			}
		}

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

			}
737 738 739 740
			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
741
				}
742 743 744
				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
745 746
				}
			}
747

748
            #ifndef TARGET_OS_IPHONE
Guillaume Beraudo's avatar
Guillaume Beraudo committed
749
			if (args->zrtp_secrets != NULL) {
750
				MSZrtpParams params;
751 752
				params.zid_file=args->zrtp_secrets;
				audio_stream_enable_zrtp(args->audio,&params);
753
			}
754
            #endif
755

756
			args->session=args->audio->ms.sessions.rtp_session;
757
		}
758

759
		if (args->enable_srtp) {
760
			ms_message("SRTP enabled: %d",
761
				audio_stream_enable_srtp(
762
					args->audio,
jehan's avatar
jehan committed
763
					MS_AES_128_SHA1_80,
764
					args->srtp_local_master_key,
765 766
					args->srtp_remote_master_key));
		}
aymeric's avatar
aymeric committed
767 768
	}else{
#ifdef VIDEO_ENABLED
769 770 771 772
		float zoom[] = {
			args->zoom,
			args->zoom_cx, args->zoom_cy };

773
		if (args->eq){
smorlat's avatar
smorlat committed
774 775 776
			ms_fatal("Cannot put an audio equalizer in a video stream !");
			exit(-1);
		}
777
		ms_message("Starting video stream.\n");
778
		args->video=video_stream_new(args->localport, args->localport+1, ms_is_ipv6(args->ip));
779 780 781
		if (args->video_display_filter)
			video_stream_set_display_filter_name(args->video, args->video_display_filter);
		
782
#ifdef ANDROID
783 784
		if (args->device_rotation >= 0)
			video_stream_set_device_rotation(args->video, args->device_rotation);
785
#endif
786 787
		video_stream_set_sent_video_size(args->video,args->vs);
		video_stream_use_preview_video_window(args->video,args->two_windows);
788
#if TARGET_OS_IPHONE
jehan's avatar
jehan committed
789 790 791
		NSBundle* myBundle = [NSBundle mainBundle];
		const char*  nowebcam = [[myBundle pathForResource:@"nowebcamCIF"ofType:@"jpg"] cStringUsingEncoding:[NSString defaultCStringEncoding]];
		ms_static_image_set_default_image(nowebcam);
792 793
		NSUInteger cpucount = [[NSProcessInfo processInfo] processorCount];
		ms_set_cpu_count(cpucount);
jehan's avatar
jehan committed
794
#endif
795
		video_stream_set_event_callback(args->video,video_stream_event_cb, args);
796
		video_stream_set_freeze_on_error(args->video,args->freeze_on_error);
797
		video_stream_enable_adaptive_bitrate_control(args->video,args->use_rc);
798 799
		if (args->camera)
			cam=ms_web_cam_manager_get_cam(ms_web_cam_manager_get(),args->camera);
800 801
		if (cam==NULL)
			cam=ms_web_cam_manager_get_default_cam(ms_web_cam_manager_get());
802
		video_stream_start(args->video,args->profile,
803
					args->ip,args->remoteport,
804
					args->ip,args->enable_rtcp?args->remoteport+1:-1,
805 806
					args->payload,
					args->jitter,cam
807
					);
808
		args->session=args->video->ms.sessions.rtp_session;
809

810
		ms_filter_call_method(args->video->output,MS_VIDEO_DISPLAY_ZOOM, zoom);
811
		if (args->enable_srtp) {
812
			ms_message("SRTP enabled: %d",
813
				video_stream_enable_strp(
814
					args->video,
jehan's avatar
jehan committed
815
					MS_AES_128_SHA1_80,
816
					args->srtp_local_master_key,
817 818
					args->srtp_remote_master_key));
		}
aymeric's avatar
aymeric committed
819 820 821 822
#else
		printf("Error: video support not compiled.\n");
#endif
	}
823 824 825
	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
826
	ice_session_choose_default_remote_candidates(args->ice_session);
Ghislain MARY's avatar
Ghislain MARY committed
827
	ice_session_start_connectivity_checks(args->ice_session);
828

829 830
	if (args->netsim.enabled){
		rtp_session_enable_network_simulation(args->session,&args->netsim);
831
	}
Yann Diorcet's avatar
Yann Diorcet committed
832

833 834 835
}


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

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

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

909
void mediastream_run_loop(MediastreamDatas* args) {
910 911
	rtp_session_register_event_queue(args->session,args->q);

912
#if TARGET_OS_IPHONE
jehan's avatar
jehan committed
913
	if (args->video) ms_set_video_stream(args->video); /*for IOS*/
914
#endif
915

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

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

954 955 956
	if (args->audio) {
		audio_stream_stop(args->audio);
	}
aymeric's avatar
aymeric committed
957
#ifdef VIDEO_ENABLED
958
	if (args->video) {
959
		if (args->video->ms.ice_check_list) ice_check_list_destroy(args->video->ms.ice_check_list);
960
		video_stream_stop(args->video);
961 962
		ms_filter_log_statistics();
	}
aymeric's avatar
aymeric committed
963
#endif
Ghislain MARY's avatar
Ghislain MARY committed
964
	if (args->ice_session) ice_session_destroy(args->ice_session);
965 966 967
	ortp_ev_queue_destroy(args->q);
	rtp_profile_destroy(args->profile);

968 969 970
	if (args->logfile)
		fclose(args->logfile);
	
971 972 973 974 975 976 977 978 979 980 981 982 983 984 985
	ms_exit();
}

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

	return JNI_VERSION_1_2;
}

JNIEXPORT void JNICALL Java_org_linphone_mediastream_MediastreamerActivity_setVideoWindowId
  (JNIEnv *env, jobject obj, jobject id, jint _args) {
#ifdef VIDEO_ENABLED
986
	MediastreamDatas* args =  (MediastreamDatas*)_args;
987 988 989 990 991 992 993 994 995
	if (!args->video)
		return;
	video_stream_set_native_window_id(args->video,(unsigned long)id);
#endif
}

JNIEXPORT void JNICALL Java_org_linphone_mediastream_MediastreamerActivity_setVideoPreviewWindowId
  (JNIEnv *env, jobject obj, jobject id, jint _args) {
#ifdef VIDEO_ENABLED
996
	MediastreamDatas* args =  (MediastreamDatas*)_args;
997 998 999 1000 1001 1002 1003 1004 1005
	if (!args->video)
		return;
	video_stream_set_native_preview_window_id(args->video,(unsigned long)id);
#endif
}

JNIEXPORT void JNICALL Java_org_linphone_mediastream_MediastreamerActivity_setDeviceRotation
  (JNIEnv *env, jobject thiz, jint rotation, jint _args) {
#ifdef VIDEO_ENABLED
1006
	MediastreamDatas* args =  (MediastreamDatas*)_args;
1007 1008 1009 1010 1011 1012 1013 1014 1015
	if (!args->video)
		return;
	video_stream_set_device_rotation(args->video, rotation);
#endif
}

JNIEXPORT void JNICALL Java_org_linphone_mediastream_MediastreamerActivity_changeCamera
  (JNIEnv *env, jobject obj, jint camId, jint _args) {
#ifdef VIDEO_ENABLED
1016
	MediastreamDatas* args =  (MediastreamDatas*)_args;
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 1066 1067 1068 1069 1070 1071
	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) {
1072
	mediastream_run_loop((MediastreamDatas*)args);
1073 1074 1075 1076 1077 1078 1079 1080 1081 1082
}

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
1083
void stop_handler(int signum)
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 1110 1111 1112 1113 1114 1115
{
	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;
}

1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129
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);
}

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 1168 1169 1170 1171 1172 1173
static void display_items(void *user_data, uint32_t csrc