videostream.c 62.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 141
							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);
							break;
Ghislain MARY's avatar
Ghislain MARY committed
142
						}
143
					}
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
					break;
				case RTCP_PSFB_PLI:
					ms_filter_call_method_noarg(stream->ms.encoder, MS_VIDEO_ENCODER_NOTIFY_PLI);
					break;
				case RTCP_PSFB_SLI:
					for (i = 0; ; i++) {
						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);
					}
					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);
166
				}
167 168 169
					break;
				default:
					break;
170
			}
aymeric's avatar
aymeric committed
171
		}
172
	}
aymeric's avatar
aymeric committed
173 174
}

175
static void stop_preload_graph(VideoStream *stream){
176
	ms_ticker_detach(stream->ms.sessions.ticker,stream->ms.rtprecv);
Ghislain MARY's avatar
Ghislain MARY committed
177 178 179 180
	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;
181 182
}

183 184 185 186 187 188
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;
	}
189 190 191 192
	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);
193

194 195 196 197 198
		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;
199

200 201 202 203 204 205 206
				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);
					}
207 208
				}
			}
209
			stream->last_fps_check=curtime;
210 211 212 213
		}
	}
}

aymeric's avatar
aymeric committed
214
void video_stream_iterate(VideoStream *stream){
215
	media_stream_iterate(&stream->ms);
216
	video_stream_track_fps_changes(stream);
aymeric's avatar
aymeric committed
217 218
}

Simon Morlat's avatar
Simon Morlat committed
219
const char *video_stream_get_default_video_renderer(void){
220 221 222 223 224
#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
225
	return "MSDrawDibDisplay";
226
#elif defined(ANDROID)
Simon Morlat's avatar
Simon Morlat committed
227
	return "MSAndroidDisplay";
228
#elif __APPLE__ && !defined(__ios)
Simon Morlat's avatar
Simon Morlat committed
229
	return "MSOSXGLDisplay";
230
#elif defined (HAVE_XV)
Simon Morlat's avatar
Simon Morlat committed
231
	return "MSX11Video";
232
#elif defined(HAVE_GL)
Simon Morlat's avatar
Simon Morlat committed
233
	return "MSGLXVideo";
Simon Morlat's avatar
Simon Morlat committed
234
#elif defined(__ios)
Simon Morlat's avatar
Simon Morlat committed
235
	return "IOSDisplay";
236 237
#elif defined(__QNX__)
	return "MSBB10Display";
Simon Morlat's avatar
Simon Morlat committed
238
#else
Simon Morlat's avatar
Simon Morlat committed
239
	return "MSVideoOut";
240 241 242
#endif
}

Simon Morlat's avatar
Simon Morlat committed
243 244 245 246
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
247
static float video_stream_get_rtcp_xr_average_quality_rating(void *userdata) {
248 249 250 251
	VideoStream *stream = (VideoStream *)userdata;
	return stream ? media_stream_get_average_quality_rating(&stream->ms) : -1;
}

Ghislain MARY's avatar
Ghislain MARY committed
252
static float video_stream_get_rtcp_xr_average_lq_quality_rating(void *userdata) {
253 254 255 256 257
	VideoStream *stream = (VideoStream *)userdata;
	return stream ? media_stream_get_average_lq_quality_rating(&stream->ms) : -1;
}


258
VideoStream *video_stream_new(int loc_rtp_port, int loc_rtcp_port, bool_t use_ipv6){
jehan's avatar
jehan committed
259 260 261 262
	return video_stream_new2( use_ipv6 ? "::" : "0.0.0.0", loc_rtp_port, loc_rtcp_port);
}

VideoStream *video_stream_new2(const char* ip, int loc_rtp_port, int loc_rtcp_port) {
263 264
	MSMediaStreamSessions sessions={0};
	VideoStream *obj;
265
	sessions.rtp_session=ms_create_duplex_rtp_session(ip,loc_rtp_port,loc_rtcp_port);
266 267 268 269 270 271
	obj=video_stream_new_with_sessions(&sessions);
	obj->ms.owns_sessions=TRUE;
	return obj;
}

VideoStream *video_stream_new_with_sessions(const MSMediaStreamSessions *sessions){
aymeric's avatar
aymeric committed
272
	VideoStream *stream = (VideoStream *)ms_new0 (VideoStream, 1);
273 274 275 276 277 278
	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,
279
		stream
280 281
	};

Simon Morlat's avatar
Simon Morlat committed
282
	stream->ms.type = MSVideo;
283
	stream->ms.sessions=*sessions;
284
	media_stream_init(&stream->ms, ms_factory_get_fallback());
285

286 287 288
	if (sessions->zrtp_context != NULL) {
		ms_zrtp_set_stream_sessions(sessions->zrtp_context, &(stream->ms.sessions));
	}
289 290 291
	if (sessions->dtls_context != NULL) {
		ms_dtls_srtp_set_stream_sessions(sessions->dtls_context, &(stream->ms.sessions));
	}
292
	rtp_session_resync(stream->ms.sessions.rtp_session);
293
	stream->ms.qi=ms_quality_indicator_new(stream->ms.sessions.rtp_session);
Ghislain MARY's avatar
Ghislain MARY committed
294
	ms_quality_indicator_set_label(stream->ms.qi,"video");
Ghislain MARY's avatar
Ghislain MARY committed
295 296
	stream->ms.rtpsend=ms_filter_new(MS_RTP_SEND_ID);
	stream->ms.ice_check_list=NULL;
Ghislain MARY's avatar
Ghislain MARY committed
297
	MS_VIDEO_SIZE_ASSIGN(stream->sent_vsize, CIF);
298
	stream->fps=0;
299
	stream->dir=MediaStreamSendRecv;
300
	stream->display_filter_auto_rotate_enabled=0;
301
	stream->freeze_on_error = FALSE;
302 303
	stream->source_performs_encoding = FALSE;
	stream->output_performs_decoding = FALSE;
304
	choose_display_name(stream);
Simon Morlat's avatar
Simon Morlat committed
305
	stream->ms.process_rtcp=video_stream_process_rtcp;
Simon Morlat's avatar
Simon Morlat committed
306 307 308
	/*
	 * In practice, these filters are needed only for audio+video recording.
	 */
309
	if (ms_factory_lookup_filter_by_id(stream->ms.factory, MS_MKV_RECORDER_ID)){
Simon Morlat's avatar
Simon Morlat committed
310
		stream->tee3=ms_filter_new(MS_TEE_ID);
311
		stream->recorder_output=ms_filter_new(MS_ITC_SINK_ID);
Simon Morlat's avatar
Simon Morlat committed
312
	}
313

314
	rtp_session_set_rtcp_xr_media_callbacks(stream->ms.sessions.rtp_session, &rtcp_xr_media_cbs);
315

316
	stream->staticimage_webcam_fps_optimization = TRUE;
317

aymeric's avatar
aymeric committed
318 319 320
	return stream;
}

smorlat's avatar
smorlat committed
321
void video_stream_set_sent_video_size(VideoStream *stream, MSVideoSize vsize){
jehan's avatar
jehan committed
322
	ms_message("Setting video size %dx%d on stream [%p]", vsize.width, vsize.height,stream);
='s avatar
= committed
323
	stream->sent_vsize=vsize;
smorlat's avatar
smorlat committed
324 325
}

326 327 328 329 330
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;
}

331 332 333 334
void video_stream_set_fps(VideoStream *stream, float fps){
	stream->fps=fps;
}

Ghislain MARY's avatar
Ghislain MARY committed
335
MSVideoSize video_stream_get_sent_video_size(const VideoStream *stream) {
Ghislain MARY's avatar
Ghislain MARY committed
336 337
	MSVideoSize vsize;
	MS_VIDEO_SIZE_ASSIGN(vsize, UNKNOWN);
338 339 340 341 342 343
	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
344
MSVideoSize video_stream_get_received_video_size(const VideoStream *stream) {
Ghislain MARY's avatar
Ghislain MARY committed
345 346
	MSVideoSize vsize;
	MS_VIDEO_SIZE_ASSIGN(vsize, UNKNOWN);
347 348 349 350 351 352
	if (stream->ms.decoder != NULL) {
		ms_filter_call_method(stream->ms.decoder, MS_FILTER_GET_VIDEO_SIZE, &vsize);
	}
	return vsize;
}

353 354 355
float video_stream_get_sent_framerate(const VideoStream *stream){
	float fps=0;
	if (stream->source){
Simon Morlat's avatar
Simon Morlat committed
356
		if (ms_filter_has_method(stream->source, MS_FILTER_GET_FPS)){
357
			ms_filter_call_method(stream->source,MS_FILTER_GET_FPS,&fps);
Simon Morlat's avatar
Simon Morlat committed
358 359 360
		}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);
		}
361 362 363 364 365 366 367 368 369 370 371 372
	}
	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
373
void video_stream_set_relay_session_id(VideoStream *stream, const char *id){
Ghislain MARY's avatar
Ghislain MARY committed
374
	ms_filter_call_method(stream->ms.rtpsend, MS_RTP_SEND_SET_RELAY_SESSION_ID,(void*)id);
aymeric's avatar
aymeric committed
375 376
}

smorlat's avatar
smorlat committed
377
void video_stream_enable_self_view(VideoStream *stream, bool_t val){
='s avatar
= committed
378
	MSFilter *out=stream->output;
379 380 381 382
	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
383
}
aymeric's avatar
aymeric committed
384

385 386 387 388 389
void video_stream_set_render_callback (VideoStream *s, VideoStreamRenderCallback cb, void *user_pointer){
	s->rendercb=cb;
	s->render_pointer=user_pointer;
}

390 391 392 393 394
void video_stream_set_event_callback (VideoStream *s, VideoStreamEventCallback cb, void *user_pointer){
	s->eventcb=cb;
	s->event_pointer=user_pointer;
}

395 396 397 398 399 400 401 402 403
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);
}

404 405

static void ext_display_cb(void *ud, MSFilter* f, unsigned int event, void *eventdata){
406 407 408 409
	MSExtDisplayOutput *output=(MSExtDisplayOutput*)eventdata;
	VideoStream *st=(VideoStream*)ud;
	if (st->rendercb!=NULL){
		st->rendercb(st->render_pointer,
410 411
					output->local_view.w!=0 ? &output->local_view : NULL,
					output->remote_view.w!=0 ? &output->remote_view : NULL);
412 413 414
	}
}

415
void video_stream_set_direction(VideoStream *vs, MediaStreamDir dir){
='s avatar
= committed
416
	vs->dir=dir;
417 418
}

Simon Morlat's avatar
Simon Morlat committed
419 420 421 422
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){
423
		return maxsize;
Simon Morlat's avatar
Simon Morlat committed
424 425 426 427
	}
	return wished_size;
}

Ghislain MARY's avatar
Ghislain MARY committed
428
#if !TARGET_IPHONE_SIMULATOR && !defined(__arm__)
Simon Morlat's avatar
Simon Morlat committed
429
static MSVideoSize get_with_same_orientation_and_ratio(MSVideoSize size, MSVideoSize refsize){
430 431 432 433 434 435
	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
436
	size.height=(size.width*refsize.height)/refsize.width;
437 438
	return size;
}
Ghislain MARY's avatar
Ghislain MARY committed
439
#endif
440

441 442 443
static void configure_video_source(VideoStream *stream){
	MSVideoSize vsize,cam_vsize;
	float fps=15;
444
	MSPixFmt format=MS_PIX_FMT_UNKNOWN;
445
	MSVideoEncoderPixFmt encoder_supports_source_format;
446
	int ret;
447
	MSVideoSize preview_vsize;
448
	MSPinFormat pf={0};
449
	bool_t is_player=ms_filter_get_id(stream->source)==MS_ITC_SOURCE_ID || ms_filter_get_id(stream->source)==MS_MKV_PLAYER_ID;
450

451

452
	/* transmit orientation to source filter */
453 454
	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
455
	/* initialize the capture device orientation for preview */
456
	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
457
		ms_filter_call_method(stream->source,MS_VIDEO_DISPLAY_SET_DEVICE_ORIENTATION,&stream->device_orientation);
458

Simon Morlat's avatar
Simon Morlat committed
459
	/* transmit its preview window id if any to source filter*/
460 461 462
	if (stream->preview_window_id!=0){
		video_stream_set_native_preview_window_id(stream, stream->preview_window_id);
	}
463

Ghislain MARY's avatar
Ghislain MARY committed
464
	ms_filter_call_method(stream->ms.encoder,MS_FILTER_GET_VIDEO_SIZE,&vsize);
Simon Morlat's avatar
Simon Morlat committed
465
	vsize=get_compatible_size(vsize,stream->sent_vsize);
466 467 468 469 470
	if (stream->preview_vsize.width!=0){
		preview_vsize=stream->preview_vsize;
	}else{
		preview_vsize=vsize;
	}
471

472 473 474 475 476 477
	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*/
478
			pf.fmt=ms_factory_get_video_format(stream->ms.factory,"VP8",vsize,0,NULL);
479 480 481 482 483 484 485 486
		}
		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);
	}

487
	if (cam_vsize.width*cam_vsize.height<=vsize.width*vsize.height){
488
		vsize=cam_vsize;
489 490
		ms_message("Output video size adjusted to match camera resolution (%ix%i)",vsize.width,vsize.height);
	} else {
491
#if TARGET_IPHONE_SIMULATOR || defined(__arm__) || defined(_M_ARM) || defined(MS2_WINDOWS_UNIVERSAL)
492
		ms_error("Camera is proposing a size bigger than encoder's suggested size (%ix%i > %ix%i) "
493
				   "Using the camera size as fallback because cropping or resizing is not implemented for this device.",
494
				   cam_vsize.width,cam_vsize.height,vsize.width,vsize.height);
495 496
		vsize=cam_vsize;
#else
Simon Morlat's avatar
Simon Morlat committed
497 498 499 500 501 502
		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;
503
			ms_warning("Camera video size greater than encoder one. A scaling filter will be used!");
Simon Morlat's avatar
Simon Morlat committed
504
		}
505
#endif
506
	}
Ghislain MARY's avatar
Ghislain MARY committed
507 508
	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);
509

510 511 512 513 514 515 516 517 518
	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 */
519
		if (ms_filter_get_id(stream->source)!=MS_STATIC_IMAGE_ID || !stream->staticimage_webcam_fps_optimization) {
520 521 522 523 524
			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);
525
	}
526
	stream->configured_fps=fps;
527

528 529 530
	encoder_supports_source_format.supported = FALSE;
	encoder_supports_source_format.pixfmt = format;

531 532 533 534 535
	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;
	}
536
	if (ret == -1) {
537 538 539
		/*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;
540
	}
541 542

	if ((encoder_supports_source_format.supported == TRUE) || (stream->source_performs_encoding == TRUE)) {
Ghislain MARY's avatar
Ghislain MARY committed
543
		ms_filter_call_method(stream->ms.encoder, MS_FILTER_SET_PIX_FMT, &format);
544 545 546
	} else {
		if (format==MS_MJPEG){
			stream->pixconv=ms_filter_new(MS_MJPEG_DEC_ID);
547 548
		}else if (format==MS_PIX_FMT_UNKNOWN){
			stream->pixconv = ms_filter_create_decoder(pf.fmt->encoding);
549 550 551 552 553 554 555 556
		}else{
			stream->pixconv = ms_filter_new(MS_PIX_CONV_ID);
			/*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);
		}
		stream->sizeconv=ms_filter_new(MS_SIZE_CONV_ID);
		ms_filter_call_method(stream->sizeconv,MS_FILTER_SET_VIDEO_SIZE,&vsize);
557
	}
Ghislain MARY's avatar
Ghislain MARY committed
558 559 560
	if (stream->ms.rc){
		ms_bitrate_controller_destroy(stream->ms.rc);
		stream->ms.rc=NULL;
561
	}
562 563 564 565 566 567 568 569 570
	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;
		}
571
	}
572 573
}

574

575

576 577
static void configure_recorder_output(VideoStream *stream){
	if (stream->recorder_output){
578 579 580
		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
581
			MSPinFormat pinfmt={0};
582 583 584 585 586
			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;
587
				tmp.encoding=pt->mime_type;
588 589
				tmp.rate=pt->clock_rate;
				pinfmt.pin=0;
590
				pinfmt.fmt=ms_factory_get_format(stream->ms.factory,&tmp);
591
				ms_filter_call_method(stream->recorder_output,MS_FILTER_SET_INPUT_FMT,&pinfmt);
592
				ms_message("configure_itc(): format set to %s",ms_fmt_descriptor_to_string(pinfmt.fmt));
593
			}
594
		}else ms_warning("configure_itc(): video decoder doesn't give output format.");
Simon Morlat's avatar
Simon Morlat committed
595 596 597
	}
}

598 599 600 601 602 603 604 605
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);
}
606

Ghislain MARY's avatar
Ghislain MARY committed
607
static void video_stream_payload_type_changed(RtpSession *session, void *data){
Simon Morlat's avatar
Simon Morlat committed
608
	VideoStream *stream = (VideoStream *)data;
609
	RtpProfile *prof = rtp_session_get_profile(session);
610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651
	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;
		}

		dec = ms_filter_create_decoder(pt->mime_type);
		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);
	}

652
	configure_recorder_output(stream);
Simon Morlat's avatar
Simon Morlat committed
653 654
}

655 656
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){
657
	MSMediaStreamIO io = MS_MEDIA_STREAM_IO_INITIALIZER;
658
	if (cam == NULL){
659 660
		cam = ms_web_cam_manager_get_default_cam( ms_web_cam_manager_get() );
	}
661 662 663 664 665 666
	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);
667 668
}

669
void video_recorder_handle_event(void *userdata, MSFilter *recorder, unsigned int event, void *event_arg){
670 671 672 673 674
	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);
675 676 677
			break;
		default:
			break;
678 679 680
	}
}

681
int video_stream_start_from_io(VideoStream *stream, RtpProfile *profile, const char *rem_rtp_ip, int rem_rtp_port,
682
	const char *rem_rtcp_ip, int rem_rtcp_port, int payload, const MSMediaStreamIO *io) {
683 684 685
	MSWebCam *cam = NULL;
	MSFilter *source = NULL;
	MSFilter *output = NULL;
686 687 688 689 690 691
	MSFilter *recorder = NULL;
	
	if (stream->ms.state != MSStreamInitialized){
		ms_error("VideoStream in bad state");
		return -1;
	}
692 693 694
	
	if (!ms_media_stream_io_is_consistent(io)) return -1;

695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721
	if (stream->dir != MediaStreamRecvOnly){
		switch(io->input.type){
			case MSResourceRtp:
				stream->rtp_io_session = io->input.session;
				source = ms_filter_new(MS_RTP_RECV_ID);
				ms_filter_call_method(source, MS_RTP_RECV_SET_SESSION, stream->rtp_io_session);
			break;
			case MSResourceCamera:
				cam = io->input.camera;
				source = ms_web_cam_create_reader(cam);
			break;
			case MSResourceFile:
				source = ms_filter_new(MS_MKV_PLAYER_ID);
				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;
		}
722
	}
723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739
	if (stream->dir != MediaStreamSendOnly){
		switch (io->output.type){
			case MSResourceRtp:
				output = ms_filter_new(MS_RTP_SEND_ID);
				stream->rtp_io_session = io->input.session;
				ms_filter_call_method(output, MS_RTP_SEND_SET_SESSION, stream->rtp_io_session);
			break;
			case MSResourceFile:
				recorder = ms_filter_new(MS_MKV_RECORDER_ID);
				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;
740
				ms_filter_add_notify_callback(recorder, video_recorder_handle_event, stream, TRUE);
741 742 743 744 745 746 747
				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;
		}
748 749 750
	}
	
	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);
751 752
}

753 754 755 756
bool_t video_stream_started(VideoStream *stream) {
	return media_stream_started(&stream->ms);
}

757
static void apply_video_preset(VideoStream *stream, PayloadType *pt) {
758
	MSVideoPresetsManager *vpm = ms_factory_get_video_presets_manager(stream->ms.factory);
759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777
	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);
778 779 780 781 782 783
			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);
			}
784 785 786 787 788 789 790 791 792 793 794 795
		} 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);
	}
}

796 797
static void apply_bitrate_limit(VideoStream *stream, PayloadType *pt) {
	MSVideoConfiguration *vconf_list = NULL;
798 799 800 801 802 803 804
	
	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);
805 806
	ms_filter_call_method(stream->ms.encoder, MS_VIDEO_ENCODER_GET_CONFIGURATION_LIST, &vconf_list);
	if (vconf_list != NULL) {
807
		MSVideoConfiguration vconf = ms_video_find_best_configuration_for_bitrate(vconf_list, stream->ms.target_bitrate, ms_get_cpu_count());
808 809 810 811 812 813
		/* 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 {
814
		ms_filter_call_method(stream->ms.encoder, MS_FILTER_SET_BITRATE, &stream->ms.target_bitrate);
815
	}
816
	rtp_session_set_target_upload_bandwidth(stream->ms.sessions.rtp_session, stream->ms.target_bitrate);
817 818
}

819 820 821 822
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;
}
823

824
static int video_stream_start_with_source_and_output(VideoStream *stream, RtpProfile *profile, const char *rem_rtp_ip, int rem_rtp_port,
825
	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
826
	PayloadType *pt;
827
	RtpSession *rtps=stream->ms.sessions.rtp_session;
aymeric's avatar
aymeric committed
828
	MSPixFmt format;
829
	MSVideoSize disp_size;
smorlat's avatar
smorlat committed
830 831
	JBParameters jbp;
	const int socket_buf_size=2000000;
832
	bool_t avpf_enabled = FALSE;
833 834
	bool_t rtp_source = FALSE;
	bool_t rtp_output = FALSE;
aymeric's avatar
aymeric committed
835

836
	if (source == NULL) {
837
		source = stream->source;
838
	}
839
	rtp_source = (source && ms_filter_get_id(source) == MS_RTP_RECV_ID) ? TRUE : FALSE;
840

aymeric's avatar
aymeric committed
841 842
	pt=rtp_profile_get_payload(profile,payload);
	if (pt==NULL){
843
		ms_error("videostream.c: undefined payload type %d.", payload);
aymeric's avatar
aymeric committed
844 845
		return -1;
	}
846
	if (pt->flags & PAYLOAD_TYPE_RTCP_FEEDBACK_ENABLED) avpf_enabled = TRUE;
847

848 849 850 851
	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
852
	rtp_session_set_profile(rtps,profile);
Simon Morlat's avatar
Simon Morlat committed
853 854
	if (rem_rtp_port>0)
		rtp_session_set_remote_addr_full(rtps,rem_rtp_ip,rem_rtp_port,rem_rtcp_ip,rem_rtcp_port);
855 856 857 858 859
	if (rem_rtcp_port > 0) {
		rtp_session_enable_rtcp(rtps, TRUE);
	} else {
		rtp_session_enable_rtcp(rtps, FALSE);
	}
aymeric's avatar
aymeric committed
860
	rtp_session_set_payload_type(rtps,payload);
861 862 863 864
	if (jitt_comp != -1) {
		/*jitt_comp = -1 don't change value. The application can use rtp_session_set_jitter_buffer_params() directly.*/
		rtp_session_set_jitter_compensation(rtps, jitt_comp);
	}
aymeric's avatar
aymeric committed
865

866
	rtp_session_signal_connect(stream->ms.sessions.rtp_session,"payload_type_changed",
Ghislain MARY's avatar
Ghislain MARY committed
867
			(RtpCallback)video_stream_payload_type_changed,&stream->ms);
aymeric's avatar
aymeric committed
868

869
	rtp_session_get_jitter_buffer_params(stream->ms.sessions.rtp_session,&jbp);
smorlat's avatar
smorlat committed
870
	jbp.max_packets=1000;//needed for high resolution video
871 872 873
	rtp_session_set_jitter_buffer_params(stream->ms.sessions.rtp_session,&jbp);
	rtp_session_set_rtp_socket_recv_buffer_size(stream->ms.sessions.rtp_session,socket_buf_size);
	rtp_session_set_rtp_socket_send_buffer_size(stream->ms.sessions.rtp_session,socket_buf_size);
874

875 876
	/* Plumb the outgoing stream */
	if (rem_rtp_port>0) ms_filter_call_method(stream->ms.rtpsend,MS_RTP_SEND_SET_SESSION,stream->ms.sessions.rtp_session);
877
	if (stream->dir==MediaStreamRecvOnly){
878 879 880 881 882 883 884 885 886
		/* Create a dummy sending stream to send the STUN packets to open firewall ports. */
		MSConnectionHelper ch;
		bool_t send_silence = FALSE;
		stream->void_source = ms_filter_new(MS_VOID_SOURCE_ID);
		ms_filter_call_method(stream->void_source, MS_VOID_SOURCE_SEND_SILENCE, &send_silence);
		ms_connection_helper_start(&ch);
		ms_connection_helper_link(&ch, stream->void_source, -1, 0);
		ms_connection_helper_link(&ch, stream->ms.rtpsend, 0, -1);
	} else {
887
		MSConnectionHelper ch;
888 889 890
		if (stream->source_performs_encoding == TRUE) {
			format = mime_type_to_pix_format(pt->mime_type);
			ms_filter_call_method(source, MS_FILTER_SET_PIX_FMT, &format);
891
		} else if (!rtp_source) {
892
			stream->ms.encoder=ms_filter_create_encoder(pt->mime_type);
Simon Morlat's avatar
Simon Morlat committed
893
			if (stream->ms.encoder==NULL){
894 895 896 897
				/* big problem: we don't have a registered codec for this payload...*/
				ms_error("videostream.c: No encoder available for payload %i:%s.",payload,pt->mime_type);
				return -1;
			}
898 899
		}
		/* creates the filters */
900
		stream->cam=cam;
901
		stream->source = source;
902 903 904 905 906 907 908 909
		if (rtp_source) {
			stream->ms.encoder = stream->source; /* Consider that the source is also the encoder */
		} else {
			stream->tee = ms_filter_new(MS_TEE_ID);
			stream->local_jpegwriter=ms_filter_new(MS_JPEG_WRITER_ID);
			if (stream->source_performs_encoding == TRUE) {
				stream->ms.encoder = stream->source;	/* Consider the encoder is the source */
			}
910

911 912 913 914 915 916 917 918 919 920 921 922
			apply_video_preset(stream, pt);
			if (pt->normal_bitrate>0){
				apply_bitrate_limit(stream, pt);
			}
			if (pt->send_fmtp){
				ms_filter_call_method(stream->ms.encoder,MS_FILTER_ADD_FMTP,pt->send_fmtp);
			}
			ms_filter_call_method(stream->ms.encoder, MS_VIDEO_ENCODER_ENABLE_AVPF, &avpf_enabled);
			if (stream->use_preview_window