videostream.c 64.9 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
/*
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.
*/

20 21
#include <math.h>

aymeric's avatar
aymeric committed
22 23
#include "mediastreamer2/mediastream.h"
#include "mediastreamer2/msfilter.h"
24
#include "mediastreamer2/msinterfaces.h"
aymeric's avatar
aymeric committed
25 26 27
#include "mediastreamer2/msvideo.h"
#include "mediastreamer2/msrtp.h"
#include "mediastreamer2/msvideoout.h"
28
#include "mediastreamer2/msextdisplay.h"
29
#include "mediastreamer2/msitc.h"
30
#include "mediastreamer2/zrtp.h"
31
#include "mediastreamer2/msvideopresets.h"
32
#include "mediastreamer2/mseventqueue.h"
Ghislain MARY's avatar
Ghislain MARY committed
33
#include "private.h"
Simon Morlat's avatar
Simon Morlat committed
34

aymeric's avatar
aymeric committed
35

36
static void configure_recorder_output(VideoStream *stream);
37 38
static int video_stream_start_with_source_and_output(VideoStream *stream, RtpProfile *profile, const char *rem_rtp_ip, int rem_rtp_port,
	const char *rem_rtcp_ip, int rem_rtcp_port, int payload, int jitt_comp, MSWebCam *cam, MSFilter *source, MSFilter *output);
39

Ghislain MARY's avatar
Ghislain MARY committed
40
void video_stream_free(VideoStream *stream) {
41 42 43 44 45 46 47 48
	bool_t rtp_source = FALSE;
	bool_t rtp_output = FALSE;

	if ((stream->source != NULL) && (ms_filter_get_id(stream->source) == MS_RTP_RECV_ID))
		rtp_source = TRUE;
	if ((stream->output != NULL) && (ms_filter_get_id(stream->output) == MS_RTP_SEND_ID))
		rtp_output = TRUE;

49
	/* Prevent filters from being destroyed two times */
50
	if ((stream->source_performs_encoding == TRUE) || (rtp_source == TRUE)) {
51 52
		stream->ms.encoder = NULL;
	}
53
	if ((stream->output_performs_decoding == TRUE) || (rtp_output == TRUE)) {
54 55 56
		stream->ms.decoder = NULL;
	}

Ghislain MARY's avatar
Ghislain MARY committed
57
	media_stream_free(&stream->ms);
58

59 60
	if (stream->void_source != NULL)
		ms_filter_destroy(stream->void_source);
aymeric's avatar
aymeric committed
61 62 63 64
	if (stream->source != NULL)
		ms_filter_destroy (stream->source);
	if (stream->output != NULL)
		ms_filter_destroy (stream->output);
65 66
	if (stream->sizeconv != NULL)
		ms_filter_destroy (stream->sizeconv);
aymeric's avatar
aymeric committed
67 68 69 70
	if (stream->pixconv!=NULL)
		ms_filter_destroy(stream->pixconv);
	if (stream->tee!=NULL)
		ms_filter_destroy(stream->tee);
71 72 73 74
	if (stream->tee2!=NULL)
		ms_filter_destroy(stream->tee2);
	if (stream->jpegwriter!=NULL)
		ms_filter_destroy(stream->jpegwriter);
75 76
	if (stream->output2!=NULL)
		ms_filter_destroy(stream->output2);
Simon Morlat's avatar
Simon Morlat committed
77 78
	if (stream->tee3)
		ms_filter_destroy(stream->tee3);
79 80
	if (stream->recorder_output)
		ms_filter_destroy(stream->recorder_output);
81 82
	if (stream->local_jpegwriter)
		ms_filter_destroy(stream->local_jpegwriter);
83 84
	if (stream->rtp_io_session)
		rtp_session_destroy(stream->rtp_io_session);
85

86 87
	if (stream->display_name!=NULL)
		ms_free(stream->display_name);
88
	if (stream->preset != NULL) ms_free(stream->preset);
89

Simon Morlat's avatar
Simon Morlat committed
90
	ms_free(stream);
aymeric's avatar
aymeric committed
91 92
}

93 94 95 96 97 98 99
static void event_cb(void *ud, MSFilter* f, unsigned int event, void *eventdata){
	VideoStream *st=(VideoStream*)ud;
	if (st->eventcb!=NULL){
		st->eventcb(st->event_pointer,f,event,eventdata);
	}
}

100 101 102 103 104 105 106
static void internal_event_cb(void *ud, MSFilter *f, unsigned int event, void *eventdata) {
	VideoStream *stream = (VideoStream *)ud;
	const MSVideoCodecSLI *sli;
	const MSVideoCodecRPSI *rpsi;

	switch (event) {
		case MS_VIDEO_DECODER_SEND_PLI:
Ghislain MARY's avatar
Ghislain MARY committed
107
			ms_message("Request sending of PLI on videostream [%p]", stream);
108 109 110 111
			video_stream_send_pli(stream);
			break;
		case MS_VIDEO_DECODER_SEND_SLI:
			sli = (const MSVideoCodecSLI *)eventdata;
Ghislain MARY's avatar
Ghislain MARY committed
112
			ms_message("Request sending of SLI on videostream [%p]", stream);
113 114 115 116
			video_stream_send_sli(stream, sli->first, sli->number, sli->picture_id);
			break;
		case MS_VIDEO_DECODER_SEND_RPSI:
			rpsi = (const MSVideoCodecRPSI *)eventdata;
Ghislain MARY's avatar
Ghislain MARY committed
117
			ms_message("Request sending of RPSI on videostream [%p]", stream);
118 119
			video_stream_send_rpsi(stream, rpsi->bit_string, rpsi->bit_string_len);
			break;
120
		case MS_FILTER_OUTPUT_FMT_CHANGED:
121
			if (stream->recorder_output) configure_recorder_output(stream);
122
			break;
123 124 125
	}
}

Simon Morlat's avatar
Simon Morlat committed
126
static void video_stream_process_rtcp(MediaStream *media_stream, mblk_t *m){
jehan's avatar
jehan committed
127
	VideoStream *stream = (VideoStream *)media_stream;
128 129
	int i;

130 131
	if (rtcp_is_PSFB(m) && (stream->ms.encoder != NULL)) {
		/* The PSFB messages are to be notified to the encoder, so if we have no encoder simply ignore them. */
132 133 134 135 136 137
		if (rtcp_PSFB_get_media_source_ssrc(m) == rtp_session_get_send_ssrc(stream->ms.sessions.rtp_session)) {
			switch (rtcp_PSFB_get_type(m)) {
				case  RTCP_PSFB_FIR:
					for (i = 0; ; i++) {
						rtcp_fb_fir_fci_t *fci = rtcp_PSFB_fir_get_fci(m, i);
						if (fci == NULL) break;
138
						if (rtcp_fb_fir_fci_get_ssrc(fci) == rtp_session_get_recv_ssrc(stream->ms.sessions.rtp_session)) {
139 140
							uint8_t seq_nr = rtcp_fb_fir_fci_get_seq_nr(fci);
							ms_filter_call_method(stream->ms.encoder, MS_VIDEO_ENCODER_NOTIFY_FIR, &seq_nr);
141 142 143
                            stream->ms_video_stat.counter_rcvd_fir++;
                            ms_message("video_stream_process_rtcp stream [%p] FIR count %d", stream,  stream->ms_video_stat.counter_rcvd_fir);

144
							break;
Ghislain MARY's avatar
Ghislain MARY committed
145
						}
146
					}
147 148
					break;
				case RTCP_PSFB_PLI:
149 150
                    
                    stream->ms_video_stat.counter_rcvd_pli++;
151
					ms_filter_call_method_noarg(stream->ms.encoder, MS_VIDEO_ENCODER_NOTIFY_PLI);
152 153
                    ms_message("video_stream_process_rtcp stream [%p] PLI count %d", stream,  stream->ms_video_stat.counter_rcvd_pli);
                    
154 155 156
					break;
				case RTCP_PSFB_SLI:
					for (i = 0; ; i++) {
157
                       
158 159 160 161 162 163 164
						rtcp_fb_sli_fci_t *fci = rtcp_PSFB_sli_get_fci(m, i);
						MSVideoCodecSLI sli;
						if (fci == NULL) break;
						sli.first = rtcp_fb_sli_fci_get_first(fci);
						sli.number = rtcp_fb_sli_fci_get_number(fci);
						sli.picture_id = rtcp_fb_sli_fci_get_picture_id(fci);
						ms_filter_call_method(stream->ms.encoder, MS_VIDEO_ENCODER_NOTIFY_SLI, &sli);
165 166 167
                        stream->ms_video_stat.counter_rcvd_sli++;
                        ms_message("video_stream_process_rtcp stream [%p] SLI count %d", stream,  stream->ms_video_stat.counter_rcvd_sli);
                       
168 169 170 171 172 173 174 175 176
					}
					break;
				case RTCP_PSFB_RPSI:
				{
					rtcp_fb_rpsi_fci_t *fci = rtcp_PSFB_rpsi_get_fci(m);
					MSVideoCodecRPSI rpsi;
					rpsi.bit_string = rtcp_fb_rpsi_fci_get_bit_string(fci);
					rpsi.bit_string_len = rtcp_PSFB_rpsi_get_fci_bit_string_len(m);
					ms_filter_call_method(stream->ms.encoder, MS_VIDEO_ENCODER_NOTIFY_RPSI, &rpsi);
177 178
                    stream->ms_video_stat.counter_rcvd_rpsi++;
                    ms_message("video_stream_process_rtcp stream [%p] RPSI count %d", stream,  stream->ms_video_stat.counter_rcvd_rpsi);
179
				}
180 181 182
					break;
				default:
					break;
183
			}
aymeric's avatar
aymeric committed
184
		}
185
	}
aymeric's avatar
aymeric committed
186 187
}

188
static void stop_preload_graph(VideoStream *stream){
189
	ms_ticker_detach(stream->ms.sessions.ticker,stream->ms.rtprecv);
Ghislain MARY's avatar
Ghislain MARY committed
190 191 192 193
	ms_filter_unlink(stream->ms.rtprecv,0,stream->ms.voidsink,0);
	ms_filter_destroy(stream->ms.voidsink);
	ms_filter_destroy(stream->ms.rtprecv);
	stream->ms.voidsink=stream->ms.rtprecv=NULL;
194 195
}

196 197 198 199 200 201
static void video_stream_track_fps_changes(VideoStream *stream){
	uint64_t curtime=ortp_get_cur_time_ms();
	if (stream->last_fps_check==(uint64_t)-1){
		stream->last_fps_check=curtime;
		return;
	}
202 203 204 205
	if (curtime-stream->last_fps_check>=2000 && stream->configured_fps>0 && stream->ms.sessions.ticker){
		MSTickerLateEvent late_ev={0};
		/*we must check that no late tick occured during the last 2 seconds, otherwise the fps measurement is severely biased.*/
		ms_ticker_get_last_late_tick(stream->ms.sessions.ticker,&late_ev);
206

207 208 209 210 211
		if (curtime > late_ev.time + 2000){
			if (stream->source && stream->ms.encoder &&
				ms_filter_has_method(stream->source,MS_FILTER_GET_FPS) &&
				ms_filter_has_method(stream->ms.encoder,MS_FILTER_SET_FPS)){
				float fps=0;
212

213 214 215 216 217 218 219
				if (ms_filter_call_method(stream->source,MS_FILTER_GET_FPS,&fps)==0 && fps!=0){
					if (fabsf(fps-stream->configured_fps)/stream->configured_fps>0.2){
						ms_warning("Measured and target fps significantly different (%f<->%f), updating encoder.",
							fps,stream->configured_fps);
						stream->configured_fps=fps;
						ms_filter_call_method(stream->ms.encoder,MS_FILTER_SET_FPS,&stream->configured_fps);
					}
220 221
				}
			}
222
			stream->last_fps_check=curtime;
223 224 225 226
		}
	}
}

aymeric's avatar
aymeric committed
227
void video_stream_iterate(VideoStream *stream){
228
	media_stream_iterate(&stream->ms);
229
	video_stream_track_fps_changes(stream);
aymeric's avatar
aymeric committed
230 231
}

Simon Morlat's avatar
Simon Morlat committed
232
const char *video_stream_get_default_video_renderer(void){
233 234 235 236 237
#if defined(MS2_WINDOWS_UNIVERSAL)
	return "MSWinRTDis";
#elif defined(MS2_WINDOWS_PHONE)
	return "MSWP8Dis";
#elif defined(MS2_WINDOWS_DESKTOP)
Simon Morlat's avatar
Simon Morlat committed
238
	return "MSDrawDibDisplay";
239
#elif defined(ANDROID)
Simon Morlat's avatar
Simon Morlat committed
240
	return "MSAndroidDisplay";
241
#elif __APPLE__ && !defined(__ios)
Simon Morlat's avatar
Simon Morlat committed
242
	return "MSOSXGLDisplay";
243
#elif defined (HAVE_XV)
Simon Morlat's avatar
Simon Morlat committed
244
	return "MSX11Video";
245
#elif defined(HAVE_GL)
Simon Morlat's avatar
Simon Morlat committed
246
	return "MSGLXVideo";
Simon Morlat's avatar
Simon Morlat committed
247
#elif defined(__ios)
Simon Morlat's avatar
Simon Morlat committed
248
	return "IOSDisplay";
249 250
#elif defined(__QNX__)
	return "MSBB10Display";
Simon Morlat's avatar
Simon Morlat committed
251
#else
Simon Morlat's avatar
Simon Morlat committed
252
	return "MSVideoOut";
253 254 255
#endif
}

Simon Morlat's avatar
Simon Morlat committed
256 257 258 259
static void choose_display_name(VideoStream *stream){
	stream->display_name=ms_strdup(video_stream_get_default_video_renderer());
}

Ghislain MARY's avatar
Ghislain MARY committed
260
static float video_stream_get_rtcp_xr_average_quality_rating(void *userdata) {
261 262 263 264
	VideoStream *stream = (VideoStream *)userdata;
	return stream ? media_stream_get_average_quality_rating(&stream->ms) : -1;
}

Ghislain MARY's avatar
Ghislain MARY committed
265
static float video_stream_get_rtcp_xr_average_lq_quality_rating(void *userdata) {
266 267 268 269 270
	VideoStream *stream = (VideoStream *)userdata;
	return stream ? media_stream_get_average_lq_quality_rating(&stream->ms) : -1;
}


271 272
VideoStream *video_stream_new(MSFactory* factory, int loc_rtp_port, int loc_rtcp_port, bool_t use_ipv6){
	return video_stream_new2(factory, use_ipv6 ? "::" : "0.0.0.0", loc_rtp_port, loc_rtcp_port);
jehan's avatar
jehan committed
273 274
}

275
VideoStream *video_stream_new2(MSFactory* factory, const char* ip, int loc_rtp_port, int loc_rtcp_port) {
276 277
	MSMediaStreamSessions sessions={0};
	VideoStream *obj;
278 279
	sessions.rtp_session=ms_create_duplex_rtp_session(ip,loc_rtp_port,loc_rtcp_port, ms_factory_get_mtu(factory));
	obj=video_stream_new_with_sessions(factory, &sessions);
280
	obj->ms.owns_sessions=TRUE;
281
	return obj;
282 283
}

284

285
VideoStream *video_stream_new_with_sessions(MSFactory* factory, const MSMediaStreamSessions *sessions){
aymeric's avatar
aymeric committed
286
	VideoStream *stream = (VideoStream *)ms_new0 (VideoStream, 1);
287 288 289 290 291 292
	const OrtpRtcpXrMediaCallbacks rtcp_xr_media_cbs = {
		NULL,
		NULL,
		NULL,
		video_stream_get_rtcp_xr_average_quality_rating,
		video_stream_get_rtcp_xr_average_lq_quality_rating,
293
		stream
294 295
	};

Simon Morlat's avatar
Simon Morlat committed
296
	stream->ms.type = MSVideo;
297
	stream->ms.sessions=*sessions;
298

299
	media_stream_init(&stream->ms, factory);
300 301 302
	if (sessions->zrtp_context != NULL) {
		ms_zrtp_set_stream_sessions(sessions->zrtp_context, &(stream->ms.sessions));
	}
303 304 305
	if (sessions->dtls_context != NULL) {
		ms_dtls_srtp_set_stream_sessions(sessions->dtls_context, &(stream->ms.sessions));
	}
306
	rtp_session_resync(stream->ms.sessions.rtp_session);
307
	stream->ms.qi=ms_quality_indicator_new(stream->ms.sessions.rtp_session);
Ghislain MARY's avatar
Ghislain MARY committed
308
	ms_quality_indicator_set_label(stream->ms.qi,"video");
309
	
310 311
	stream->ms.rtpsend=ms_factory_create_filter(stream->ms.factory, MS_RTP_SEND_ID);

Ghislain MARY's avatar
Ghislain MARY committed
312
	stream->ms.ice_check_list=NULL;
Ghislain MARY's avatar
Ghislain MARY committed
313
	MS_VIDEO_SIZE_ASSIGN(stream->sent_vsize, CIF);
314
	stream->fps=0;
315
	stream->dir=MediaStreamSendRecv;
316
	stream->display_filter_auto_rotate_enabled=0;
317
	stream->freeze_on_error = FALSE;
318 319
	stream->source_performs_encoding = FALSE;
	stream->output_performs_decoding = FALSE;
320
	choose_display_name(stream);
Simon Morlat's avatar
Simon Morlat committed
321
	stream->ms.process_rtcp=video_stream_process_rtcp;
Simon Morlat's avatar
Simon Morlat committed
322 323 324
	/*
	 * In practice, these filters are needed only for audio+video recording.
	 */
325
	if (ms_factory_lookup_filter_by_id(stream->ms.factory, MS_MKV_RECORDER_ID)){
326

327 328 329
		stream->tee3=ms_factory_create_filter(stream->ms.factory, MS_TEE_ID);
		stream->recorder_output=ms_factory_create_filter(stream->ms.factory, MS_ITC_SINK_ID);

Simon Morlat's avatar
Simon Morlat committed
330
	}
331

332
	rtp_session_set_rtcp_xr_media_callbacks(stream->ms.sessions.rtp_session, &rtcp_xr_media_cbs);
333

334
	stream->staticimage_webcam_fps_optimization = TRUE;
335

aymeric's avatar
aymeric committed
336 337 338
	return stream;
}

smorlat's avatar
smorlat committed
339
void video_stream_set_sent_video_size(VideoStream *stream, MSVideoSize vsize){
jehan's avatar
jehan committed
340
	ms_message("Setting video size %dx%d on stream [%p]", vsize.width, vsize.height,stream);
='s avatar
= committed
341
	stream->sent_vsize=vsize;
smorlat's avatar
smorlat committed
342 343
}

344 345 346 347 348
void video_stream_set_preview_size(VideoStream *stream, MSVideoSize vsize){
	ms_message("Setting preview video size %dx%d", vsize.width, vsize.height);
	stream->preview_vsize=vsize;
}

349 350 351 352
void video_stream_set_fps(VideoStream *stream, float fps){
	stream->fps=fps;
}

Ghislain MARY's avatar
Ghislain MARY committed
353
MSVideoSize video_stream_get_sent_video_size(const VideoStream *stream) {
Ghislain MARY's avatar
Ghislain MARY committed
354 355
	MSVideoSize vsize;
	MS_VIDEO_SIZE_ASSIGN(vsize, UNKNOWN);
356 357 358 359 360 361
	if (stream->ms.encoder != NULL) {
		ms_filter_call_method(stream->ms.encoder, MS_FILTER_GET_VIDEO_SIZE, &vsize);
	}
	return vsize;
}

Ghislain MARY's avatar
Ghislain MARY committed
362
MSVideoSize video_stream_get_received_video_size(const VideoStream *stream) {
Ghislain MARY's avatar
Ghislain MARY committed
363 364
	MSVideoSize vsize;
	MS_VIDEO_SIZE_ASSIGN(vsize, UNKNOWN);
365 366 367 368 369 370
	if (stream->ms.decoder != NULL) {
		ms_filter_call_method(stream->ms.decoder, MS_FILTER_GET_VIDEO_SIZE, &vsize);
	}
	return vsize;
}

371 372 373
float video_stream_get_sent_framerate(const VideoStream *stream){
	float fps=0;
	if (stream->source){
Simon Morlat's avatar
Simon Morlat committed
374
		if (ms_filter_has_method(stream->source, MS_FILTER_GET_FPS)){
375
			ms_filter_call_method(stream->source,MS_FILTER_GET_FPS,&fps);
Simon Morlat's avatar
Simon Morlat committed
376 377 378
		}else if (stream->pixconv && ms_filter_has_method(stream->pixconv, MS_FILTER_GET_FPS)){
			ms_filter_call_method(stream->pixconv,MS_FILTER_GET_FPS,&fps);
		}
379 380 381 382 383 384 385 386 387 388 389 390
	}
	return fps;
}

float video_stream_get_received_framerate(const VideoStream *stream){
	float fps=0;
	if (stream->ms.decoder != NULL && ms_filter_has_method(stream->ms.decoder, MS_FILTER_GET_FPS)) {
		ms_filter_call_method(stream->ms.decoder, MS_FILTER_GET_FPS, &fps);
	}
	return fps;
}

aymeric's avatar
aymeric committed
391
void video_stream_set_relay_session_id(VideoStream *stream, const char *id){
Ghislain MARY's avatar
Ghislain MARY committed
392
	ms_filter_call_method(stream->ms.rtpsend, MS_RTP_SEND_SET_RELAY_SESSION_ID,(void*)id);
aymeric's avatar
aymeric committed
393 394
}

smorlat's avatar
smorlat committed
395
void video_stream_enable_self_view(VideoStream *stream, bool_t val){
='s avatar
= committed
396
	MSFilter *out=stream->output;
397 398 399 400
	stream->corner=val ? 0 : -1;
	if (out){
		ms_filter_call_method(out,MS_VIDEO_DISPLAY_SET_LOCAL_VIEW_MODE,&stream->corner);
	}
smorlat's avatar
smorlat committed
401
}
aymeric's avatar
aymeric committed
402

403 404 405 406 407
void video_stream_set_render_callback (VideoStream *s, VideoStreamRenderCallback cb, void *user_pointer){
	s->rendercb=cb;
	s->render_pointer=user_pointer;
}

408 409 410 411 412
void video_stream_set_event_callback (VideoStream *s, VideoStreamEventCallback cb, void *user_pointer){
	s->eventcb=cb;
	s->event_pointer=user_pointer;
}

413 414 415 416 417 418 419 420 421
void video_stream_set_display_filter_name(VideoStream *s, const char *fname){
	if (s->display_name!=NULL){
		ms_free(s->display_name);
		s->display_name=NULL;
	}
	if (fname!=NULL)
		s->display_name=ms_strdup(fname);
}

422 423

static void ext_display_cb(void *ud, MSFilter* f, unsigned int event, void *eventdata){
424 425 426 427
	MSExtDisplayOutput *output=(MSExtDisplayOutput*)eventdata;
	VideoStream *st=(VideoStream*)ud;
	if (st->rendercb!=NULL){
		st->rendercb(st->render_pointer,
428 429
					output->local_view.w!=0 ? &output->local_view : NULL,
					output->remote_view.w!=0 ? &output->remote_view : NULL);
430 431 432
	}
}

433
void video_stream_set_direction(VideoStream *vs, MediaStreamDir dir){
='s avatar
= committed
434
	vs->dir=dir;
435 436
}

Simon Morlat's avatar
Simon Morlat committed
437 438 439 440
static MSVideoSize get_compatible_size(MSVideoSize maxsize, MSVideoSize wished_size){
	int max_area=maxsize.width*maxsize.height;
	int whished_area=wished_size.width*wished_size.height;
	if (whished_area>max_area){
441
		return maxsize;
Simon Morlat's avatar
Simon Morlat committed
442 443 444 445
	}
	return wished_size;
}

Ghislain MARY's avatar
Ghislain MARY committed
446
#if !TARGET_IPHONE_SIMULATOR && !defined(__arm__)
Simon Morlat's avatar
Simon Morlat committed
447
static MSVideoSize get_with_same_orientation_and_ratio(MSVideoSize size, MSVideoSize refsize){
448 449 450 451 452 453
	if (ms_video_size_get_orientation(refsize)!=ms_video_size_get_orientation(size)){
		int tmp;
		tmp=size.width;
		size.width=size.height;
		size.height=tmp;
	}
Simon Morlat's avatar
Simon Morlat committed
454
	size.height=(size.width*refsize.height)/refsize.width;
455 456
	return size;
}
Ghislain MARY's avatar
Ghislain MARY committed
457
#endif
458

459 460 461
static void configure_video_source(VideoStream *stream){
	MSVideoSize vsize,cam_vsize;
	float fps=15;
462
	MSPixFmt format=MS_PIX_FMT_UNKNOWN;
463
	MSVideoEncoderPixFmt encoder_supports_source_format;
464
	int ret;
465
	MSVideoSize preview_vsize;
466
	MSPinFormat pf={0};
467
	bool_t is_player=ms_filter_get_id(stream->source)==MS_ITC_SOURCE_ID || ms_filter_get_id(stream->source)==MS_MKV_PLAYER_ID;
468

469

470
	/* transmit orientation to source filter */
471 472
	if (ms_filter_has_method(stream->source, MS_VIDEO_CAPTURE_SET_DEVICE_ORIENTATION))
		ms_filter_call_method(stream->source,MS_VIDEO_CAPTURE_SET_DEVICE_ORIENTATION,&stream->device_orientation);
Simon Morlat's avatar
Simon Morlat committed
473
	/* initialize the capture device orientation for preview */
474
	if (!stream->display_filter_auto_rotate_enabled && ms_filter_has_method(stream->source, MS_VIDEO_DISPLAY_SET_DEVICE_ORIENTATION))
Simon Morlat's avatar
Simon Morlat committed
475
		ms_filter_call_method(stream->source,MS_VIDEO_DISPLAY_SET_DEVICE_ORIENTATION,&stream->device_orientation);
476

Simon Morlat's avatar
Simon Morlat committed
477
	/* transmit its preview window id if any to source filter*/
478 479 480
	if (stream->preview_window_id!=0){
		video_stream_set_native_preview_window_id(stream, stream->preview_window_id);
	}
481

Ghislain MARY's avatar
Ghislain MARY committed
482
	ms_filter_call_method(stream->ms.encoder,MS_FILTER_GET_VIDEO_SIZE,&vsize);
Simon Morlat's avatar
Simon Morlat committed
483
	vsize=get_compatible_size(vsize,stream->sent_vsize);
484 485 486 487 488
	if (stream->preview_vsize.width!=0){
		preview_vsize=stream->preview_vsize;
	}else{
		preview_vsize=vsize;
	}
489

490 491 492 493 494 495
	if (is_player){
		ms_filter_call_method(stream->source,MS_FILTER_GET_OUTPUT_FMT,&pf);
		if (pf.fmt==NULL || pf.fmt->vsize.width==0){
			MSVideoSize vsize={640,480};
			ms_error("Player does not give its format correctly [%s]",ms_fmt_descriptor_to_string(pf.fmt));
			/*put a default format as the error handling is complicated here*/
496
			pf.fmt=ms_factory_get_video_format(stream->ms.factory,"VP8",vsize,0,NULL);
497 498 499 500 501 502 503 504
		}
		cam_vsize=pf.fmt->vsize;
	}else{
		ms_filter_call_method(stream->source,MS_FILTER_SET_VIDEO_SIZE,&preview_vsize);
		/*the camera may not support the target size and suggest a one close to the target */
		ms_filter_call_method(stream->source,MS_FILTER_GET_VIDEO_SIZE,&cam_vsize);
	}

505
	if (cam_vsize.width*cam_vsize.height<=vsize.width*vsize.height){
506
		vsize=cam_vsize;
507 508
		ms_message("Output video size adjusted to match camera resolution (%ix%i)",vsize.width,vsize.height);
	} else {
509
#if TARGET_IPHONE_SIMULATOR || defined(__arm__) || defined(_M_ARM) || defined(MS2_WINDOWS_UNIVERSAL)
510
		ms_error("Camera is proposing a size bigger than encoder's suggested size (%ix%i > %ix%i) "
511
				   "Using the camera size as fallback because cropping or resizing is not implemented for this device.",
512
				   cam_vsize.width,cam_vsize.height,vsize.width,vsize.height);
513 514
		vsize=cam_vsize;
#else
Simon Morlat's avatar
Simon Morlat committed
515 516 517 518 519 520
		MSVideoSize resized=get_with_same_orientation_and_ratio(vsize,cam_vsize);
		if (resized.width & 0x1 || resized.height & 0x1){
			ms_warning("Resizing avoided because downsizing to an odd number of pixels (%ix%i)",resized.width,resized.height);
			vsize=cam_vsize;
		}else{
			vsize=resized;
521
			ms_warning("Camera video size greater than encoder one. A scaling filter will be used!");
Simon Morlat's avatar
Simon Morlat committed
522
		}
523
#endif
524
	}
Ghislain MARY's avatar
Ghislain MARY committed
525 526
	ms_filter_call_method(stream->ms.encoder,MS_FILTER_SET_VIDEO_SIZE,&vsize);
	ms_filter_call_method(stream->ms.encoder,MS_FILTER_GET_FPS,&fps);
527

528 529 530 531 532 533 534 535 536
	if (is_player){
		fps=pf.fmt->fps;
		if (fps==0) fps=15;
		ms_filter_call_method(stream->ms.encoder,MS_FILTER_SET_FPS,&fps);
	}else{
		if (stream->fps!=0)
			fps=stream->fps;
		ms_message("Setting sent vsize=%ix%i, fps=%f",vsize.width,vsize.height,fps);
		/* configure the filters */
537
		if (ms_filter_get_id(stream->source)!=MS_STATIC_IMAGE_ID || !stream->staticimage_webcam_fps_optimization) {
538 539 540 541 542
			ms_filter_call_method(stream->source,MS_FILTER_SET_FPS,&fps);
		}
		ms_filter_call_method(stream->ms.encoder,MS_FILTER_SET_FPS,&fps);
		/* get the output format for webcam reader */
		ms_filter_call_method(stream->source,MS_FILTER_GET_PIX_FMT,&format);
543
	}
544
	stream->configured_fps=fps;
545

546 547 548
	encoder_supports_source_format.supported = FALSE;
	encoder_supports_source_format.pixfmt = format;

549 550 551 552 553
	if (ms_filter_has_method(stream->ms.encoder, MS_VIDEO_ENCODER_SUPPORTS_PIXFMT) == TRUE) {
		ret = ms_filter_call_method(stream->ms.encoder, MS_VIDEO_ENCODER_SUPPORTS_PIXFMT, &encoder_supports_source_format);
	} else {
		ret = -1;
	}
554
	if (ret == -1) {
555 556 557
		/*encoder doesn't have MS_VIDEO_ENCODER_SUPPORTS_PIXFMT method*/
		/*we prefer in this case consider that it is not required to get the optimization of not going through pixconv and sizeconv*/
		encoder_supports_source_format.supported = FALSE;
558
	}
559 560

	if ((encoder_supports_source_format.supported == TRUE) || (stream->source_performs_encoding == TRUE)) {
Ghislain MARY's avatar
Ghislain MARY committed
561
		ms_filter_call_method(stream->ms.encoder, MS_FILTER_SET_PIX_FMT, &format);
562 563
	} else {
		if (format==MS_MJPEG){
564 565
			stream->pixconv=ms_factory_create_filter(stream->ms.factory, MS_MJPEG_DEC_ID);

566
		}else if (format==MS_PIX_FMT_UNKNOWN){
Sandrine Avakian's avatar
Sandrine Avakian committed
567
			stream->pixconv = ms_factory_create_decoder(stream->ms.factory, pf.fmt->encoding);
568
		}else{
569
			stream->pixconv = ms_factory_create_filter(stream->ms.factory, MS_PIX_CONV_ID);
570 571 572 573
			/*set it to the pixconv */
			ms_filter_call_method(stream->pixconv,MS_FILTER_SET_PIX_FMT,&format);
			ms_filter_call_method(stream->pixconv,MS_FILTER_SET_VIDEO_SIZE,&cam_vsize);
		}
574
		stream->sizeconv=ms_factory_create_filter(stream->ms.factory, MS_SIZE_CONV_ID);
575
		ms_filter_call_method(stream->sizeconv,MS_FILTER_SET_VIDEO_SIZE,&vsize);
576
	}
Ghislain MARY's avatar
Ghislain MARY committed
577 578 579
	if (stream->ms.rc){
		ms_bitrate_controller_destroy(stream->ms.rc);
		stream->ms.rc=NULL;
580
	}
581 582 583 584 585 586 587 588 589
	if (stream->ms.rc_enable){
		switch (stream->ms.rc_algorithm){
		case MSQosAnalyzerAlgorithmSimple:
			stream->ms.rc=ms_av_bitrate_controller_new(NULL,NULL,stream->ms.sessions.rtp_session,stream->ms.encoder);
			break;
		case MSQosAnalyzerAlgorithmStateful:
			stream->ms.rc=ms_bandwidth_bitrate_controller_new(NULL, NULL, stream->ms.sessions.rtp_session,stream->ms.encoder);
			break;
		}
590
	}
591 592
}

593

594

595 596
static void configure_recorder_output(VideoStream *stream){
	if (stream->recorder_output){
597 598 599
		MSPinFormat pf={0};
		ms_filter_call_method(stream->ms.decoder,MS_FILTER_GET_OUTPUT_FMT,&pf);
		if (pf.fmt){
Simon Morlat's avatar
Simon Morlat committed
600
			MSPinFormat pinfmt={0};
601 602 603 604 605
			RtpSession *session=stream->ms.sessions.rtp_session;
			PayloadType *pt=rtp_profile_get_payload(rtp_session_get_profile(session),rtp_session_get_recv_payload_type(session));
			if (!pt) pt=rtp_profile_get_payload(rtp_session_get_profile(session),rtp_session_get_send_payload_type(session));
			if (pt){
				MSFmtDescriptor tmp=*pf.fmt;
606
				tmp.encoding=pt->mime_type;
607 608
				tmp.rate=pt->clock_rate;
				pinfmt.pin=0;
609
				pinfmt.fmt=ms_factory_get_format(stream->ms.factory,&tmp);
610
				ms_filter_call_method(stream->recorder_output,MS_FILTER_SET_INPUT_FMT,&pinfmt);
611
				ms_message("configure_itc(): format set to %s",ms_fmt_descriptor_to_string(pinfmt.fmt));
612
			}
613
		}else ms_warning("configure_itc(): video decoder doesn't give output format.");
Simon Morlat's avatar
Simon Morlat committed
614 615 616
	}
}

617 618 619 620 621 622 623 624
static void configure_decoder(VideoStream *stream, PayloadType *pt){
	bool_t avpf_enabled=!!(pt->flags & PAYLOAD_TYPE_RTCP_FEEDBACK_ENABLED);
	ms_filter_call_method(stream->ms.decoder, MS_VIDEO_DECODER_ENABLE_AVPF, &avpf_enabled);
	ms_filter_call_method(stream->ms.decoder, MS_VIDEO_DECODER_FREEZE_ON_ERROR, &stream->freeze_on_error);
	ms_filter_add_notify_callback(stream->ms.decoder, event_cb, stream, FALSE);
	/* It is important that the internal_event_cb is called synchronously! */
	ms_filter_add_notify_callback(stream->ms.decoder, internal_event_cb, stream, TRUE);
}
625

Ghislain MARY's avatar
Ghislain MARY committed
626
static void video_stream_payload_type_changed(RtpSession *session, void *data){
Simon Morlat's avatar
Simon Morlat committed
627
	VideoStream *stream = (VideoStream *)data;
628
	RtpProfile *prof = rtp_session_get_profile(session);
629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646
	int payload = rtp_session_get_recv_payload_type(session);
	PayloadType *pt = rtp_profile_get_payload(prof, payload);

	if (stream->ms.decoder == NULL){
		ms_message("video_stream_payload_type_changed(): no decoder!");
		return;
	}

	if (pt != NULL){
		MSFilter *dec;

		/* Q: why only video ? A: because an audio format can be used at different rates: ex: speex/16000 speex/8000*/
		if ((stream->ms.decoder != NULL) && (stream->ms.decoder->desc->enc_fmt != NULL)
			&& (strcasecmp(pt->mime_type, stream->ms.decoder->desc->enc_fmt) == 0)) {
			/* Same formats behind different numbers, nothing to do. */
			return;
		}

647 648
//		dec = ms_filter_create_decoder(pt->mime_type);
		dec = ms_factory_create_decoder(stream->ms.factory,pt->mime_type);
649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671
		if (dec != NULL) {
			MSFilter *prevFilter = stream->ms.decoder->inputs[0]->prev.filter;
			MSFilter *nextFilter = stream->ms.decoder->outputs[0]->next.filter;

			ms_filter_unlink(prevFilter, 0, stream->ms.decoder, 0);
			ms_filter_unlink(stream->ms.decoder, 0, nextFilter, 0);
			ms_filter_postprocess(stream->ms.decoder);
			ms_filter_destroy(stream->ms.decoder);
			stream->ms.decoder = dec;
			if (pt->recv_fmtp != NULL)
				ms_filter_call_method(stream->ms.decoder, MS_FILTER_ADD_FMTP, (void *)pt->recv_fmtp);
			ms_filter_link(prevFilter, 0, stream->ms.decoder, 0);
			ms_filter_link(stream->ms.decoder, 0, nextFilter, 0);
			ms_filter_preprocess(stream->ms.decoder, stream->ms.sessions.ticker);

			configure_decoder(stream, pt);
		} else {
			ms_warning("No decoder found for %s", pt->mime_type);
		}
	} else {
		ms_warning("No payload defined with number %i", payload);
	}

672
	configure_recorder_output(stream);
Simon Morlat's avatar
Simon Morlat committed
673 674
}

675 676
int video_stream_start (VideoStream *stream, RtpProfile *profile, const char *rem_rtp_ip, int rem_rtp_port,
	const char *rem_rtcp_ip, int rem_rtcp_port, int payload, int jitt_comp, MSWebCam *cam){
677
	MSMediaStreamIO io = MS_MEDIA_STREAM_IO_INITIALIZER;
678
	if (cam == NULL){
679
		cam = ms_web_cam_manager_get_default_cam( cam->wbcmanager);
680
	}
681 682 683 684 685 686
	io.input.type = MSResourceCamera;
	io.input.camera = cam;
	io.output.type = MSResourceDefault;
	io.output.resource_arg = NULL;
	rtp_session_set_jitter_compensation(stream->ms.sessions.rtp_session, jitt_comp);
	return video_stream_start_from_io(stream, profile, rem_rtp_ip, rem_rtp_port, rem_rtcp_ip, rem_rtcp_port, payload, &io);
687 688
}

689
void video_recorder_handle_event(void *userdata, MSFilter *recorder, unsigned int event, void *event_arg){
690 691 692 693 694
	VideoStream *stream = (VideoStream*) userdata;
	switch (event){
		case MS_RECORDER_NEEDS_FIR:
			ms_message("Request sending of FIR on videostream [%p]", stream);
			video_stream_send_fir(stream);
695 696 697
			break;
		default:
			break;
698 699 700
	}
}

701
int video_stream_start_from_io(VideoStream *stream, RtpProfile *profile, const char *rem_rtp_ip, int rem_rtp_port,
702
	const char *rem_rtcp_ip, int rem_rtcp_port, int payload, const MSMediaStreamIO *io) {
703 704 705
	MSWebCam *cam = NULL;
	MSFilter *source = NULL;
	MSFilter *output = NULL;
706 707 708 709 710 711
	MSFilter *recorder = NULL;
	
	if (stream->ms.state != MSStreamInitialized){
		ms_error("VideoStream in bad state");
		return -1;
	}
712 713 714
	
	if (!ms_media_stream_io_is_consistent(io)) return -1;

715 716 717 718
	if (stream->dir != MediaStreamRecvOnly){
		switch(io->input.type){
			case MSResourceRtp:
				stream->rtp_io_session = io->input.session;
719
				source = ms_factory_create_filter(stream->ms.factory, MS_RTP_RECV_ID);
720 721 722 723
				ms_filter_call_method(source, MS_RTP_RECV_SET_SESSION, stream->rtp_io_session);
			break;
			case MSResourceCamera:
				cam = io->input.camera;
724
				source = ms_web_cam_create_reader(cam);
725 726
			break;
			case MSResourceFile:
727
				source = ms_factory_create_filter(stream->ms.factory, MS_MKV_PLAYER_ID);
728 729 730 731 732 733 734 735 736 737 738 739 740 741
				if (!source){
					ms_error("Mediastreamer2 library compiled without libmastroska2");
					return -1;
				}
				stream->source = source;
				if (io->input.file) {
					if (video_stream_open_remote_play(stream, io->input.file)!=NULL)
						ms_filter_call_method_noarg(source, MS_PLAYER_START);
				}
			break;
			default:
				ms_error("Unhandled input resource type %s", ms_resource_type_to_string(io->input.type));
			break;
		}
742
	}
743 744 745
	if (stream->dir != MediaStreamSendOnly){
		switch (io->output.type){
			case MSResourceRtp:
746
				output = ms_factory_create_filter(stream->ms.factory, MS_RTP_SEND_ID);
747 748 749 750
				stream->rtp_io_session = io->input.session;
				ms_filter_call_method(output, MS_RTP_SEND_SET_SESSION, stream->rtp_io_session);
			break;
			case MSResourceFile:
751
				recorder = ms_factory_create_filter(stream->ms.factory, MS_MKV_RECORDER_ID);
752 753 754 755 756 757 758 759
				if (!recorder){
					ms_error("Mediastreamer2 library compiled without libmastroska2");
					return -1;
				}
				if (stream->recorder_output){
					ms_filter_destroy(stream->recorder_output);
				}
				stream->recorder_output = recorder;
760
				ms_filter_add_notify_callback(recorder, video_recorder_handle_event, stream, TRUE);
761 762 763 764 765 766 767
				if (io->output.file) video_stream_open_remote_record(stream, io->output.file);
			break;
			default:
				/*will just display in all other cases*/
				/*ms_error("Unhandled output resource type %s", ms_resource_type_to_string(io->output.type));*/
			break;
		}
768 769 770
	}
	
	return video_stream_start_with_source_and_output(stream, profile, rem_rtp_ip, rem_rtp_port, rem_rtcp_ip, rem_rtcp_port, payload, -1, cam, source, output);
771 772
}

773 774 775 776
bool_t video_stream_started(VideoStream *stream) {
	return media_stream_started(&stream->ms);
}

777
static void apply_video_preset(VideoStream *stream, PayloadType *pt) {
778
	MSVideoPresetsManager *vpm = ms_factory_get_video_presets_manager(stream->ms.factory);
779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797
	MSVideoPresetConfiguration *vpc = NULL;
	MSVideoConfiguration *conf = NULL;
	MSList *codec_tags = NULL;
	bool_t hardware_accelerated = FALSE;
	if (stream->preset != NULL) {
		codec_tags = ms_list_append(codec_tags, ms_strdup(payload_type_get_mime(pt)));
		codec_tags = ms_list_append(codec_tags, ms_strdup(stream->ms.encoder->desc->name));
		if (ms_filter_has_method(stream->ms.encoder, MS_VIDEO_ENCODER_IS_HARDWARE_ACCELERATED) == TRUE) {
			ms_filter_call_method(stream->ms.encoder, MS_VIDEO_ENCODER_IS_HARDWARE_ACCELERATED, &hardware_accelerated);
		}
		if (hardware_accelerated == TRUE) {
			codec_tags = ms_list_append(codec_tags, ms_strdup("hardware"));
		}
		vpc = ms_video_presets_manager_find_preset_configuration(vpm, stream->preset, codec_tags);
		ms_list_for_each(codec_tags, ms_free);
		ms_list_free(codec_tags);
		if (vpc != NULL) {
			char *conf_tags = ms_video_preset_configuration_get_tags_as_string(vpc);
			conf = ms_video_preset_configuration_get_video_configuration(vpc);
798 799 800 801 802 803
			if (conf_tags) {
				ms_message("Using the '%s' video preset tagged '%s'", stream->preset, conf_tags);
				ms_free(conf_tags);
			} else {
				ms_message("Using the '%s' video preset non-tagged", stream->preset);
			}
804 805 806 807 808 809 810 811 812 813 814 815
		} else {
			ms_warning("No '%s' video preset has been found", stream->preset);
		}
	}
	if (conf == NULL) {
		ms_message("Using the default video configuration list");
	}
	if (ms_filter_has_method(stream->ms.encoder, MS_VIDEO_ENCODER_SET_CONFIGURATION_LIST) == TRUE) {
		ms_filter_call_method(stream->ms.encoder, MS_VIDEO_ENCODER_SET_CONFIGURATION_LIST, &conf);
	}
}

816 817
static void apply_bitrate_limit(VideoStream *stream, PayloadType *pt) {
	MSVideoConfiguration *vconf_list = NULL;
818 819 820 821 822 823 824
	
	if (stream->ms.target_bitrate<=0) {
		stream->ms.target_bitrate=pt->normal_bitrate;
		ms_message("target bitrate not set for stream [%p] using payload's bitrate is %i",stream,stream->ms.target_bitrate);
	}
	
	ms_message("Limiting bitrate of video encoder to %i bits/s for stream [%p]",stream->ms.target_bitrate,stream);
825 826
	ms_filter_call_method(stream->ms.encoder, MS_VIDEO_ENCODER_GET_CONFIGURATION_LIST, &vconf_list);
	if (vconf_list != NULL) {
827
		MSVideoConfiguration vconf = ms_video_find_best_configuration_for_bitrate(vconf_list, stream->ms.target_bitrate, ms_factory_get_cpu_count(stream->ms.factory));
828 829 830 831 832 833
		/* Adjust configuration video size to use the user preferred video size if it is lower that the configuration one. */
		if ((stream->sent_vsize.height * stream->sent_vsize.width) < (vconf.vsize.height * vconf.vsize.width)) {
			vconf.vsize = stream->sent_vsize;
		}
		ms_filter_call_method(stream->ms.encoder, MS_VIDEO_ENCODER_SET_CONFIGURATION, &vconf);
	} else {
834
		ms_filter_call_method(stream->ms.encoder, MS_FILTER_SET_BITRATE, &stream->ms.target_bitrate);
835
	}
836
	rtp_session_set_target_upload_bandwidth(stream->ms.sessions.rtp_session, stream->ms.target_bitrate);
837 838
}

839 840 841 842
static MSPixFmt mime_type_to_pix_format(const char *mime_type) {
	if (strcasecmp(mime_type, "H264") == 0) return MS_H264;
	return MS_PIX_FMT_UNKNOWN;
}
843

844
static int video_stream_start_with_source_and_output(VideoStream *stream, RtpProfile *profile, const char *rem_rtp_ip, int rem_rtp_port,
845
	const char *rem_rtcp_ip, int rem_rtcp_port, int payload, int jitt_comp, MSWebCam *cam, MSFilter *source, MSFilter *output) {
aymeric's avatar
aymeric committed
846
	PayloadType *pt;
847
	RtpSession *rtps=stream->ms.sessions.rtp_session;
aymeric's avatar
aymeric committed
848
	MSPixFmt format;
849
	MSVideoSize disp_size;
smorlat's avatar
smorlat committed
850 851
	JBParameters jbp;
	const int socket_buf_size=2000000;
852
	bool_t avpf_enabled = FALSE;
853 854
	bool_t rtp_source = FALSE;
	bool_t rtp_output = FALSE;
aymeric's avatar
aymeric committed
855

856
	if (source == NULL) {
857
		source = stream->source;
858
	}
859
	rtp_source = (source && ms_filter_get_id(source) == MS_RTP_RECV_ID) ? TRUE : FALSE;
860

aymeric's avatar
aymeric committed
861 862
	pt=rtp_profile_get_payload(profile,payload);
	if (pt==NULL){
863
		ms_error("videostream.c: undefined payload type %d.", payload);
aymeric's avatar
aymeric committed
864 865
		return -1;
	}
866
	if (pt->flags & PAYLOAD_TYPE_RTCP_FEEDBACK_ENABLED) avpf_enabled = TRUE;
867

868 869 870 871
	if ((cam != NULL) && (cam->desc->encode_to_mime_type != NULL) && (cam->desc->encode_to_mime_type(cam, pt->mime_type) == TRUE)) {
		stream->source_performs_encoding = TRUE;
	}

aymeric's avatar
aymeric committed
872
	rtp_session_set_profile(rtps,profile);
Simon Morlat's avatar
Simon Morlat committed
873 874
	if (rem_rtp_port>0)
		rtp_session_set_remote_addr_full(rtps,rem_rtp_ip,rem_rtp_port,rem_rtcp_ip,rem_rtcp_port);