1 /*
2 mediastreamer2 library - modular sound and video processing and streaming
3 Copyright (C) 2006  Simon MORLAT (simon.morlat@linphone.org)
4 
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18 */
19 
20 #include <math.h>
21 
22 #include "mediastreamer2/mediastream.h"
23 #include "mediastreamer2/msfilter.h"
24 #include "mediastreamer2/msinterfaces.h"
25 #include "mediastreamer2/msvideo.h"
26 #include "mediastreamer2/msrtp.h"
27 #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"
33 #include "private.h"
34 
35 #if __APPLE__
36 #include "TargetConditionals.h"
37 #endif
38 
39 static void configure_recorder_output(VideoStream *stream);
40 static int video_stream_start_with_source_and_output(VideoStream *stream, RtpProfile *profile, const char *rem_rtp_ip, int rem_rtp_port,
41 	const char *rem_rtcp_ip, int rem_rtcp_port, int payload, int jitt_comp, MSWebCam *cam, MSFilter *source, MSFilter *output);
42 
video_stream_free(VideoStream * stream)43 void video_stream_free(VideoStream *stream) {
44 	bool_t rtp_source = FALSE;
45 	bool_t rtp_output = FALSE;
46 
47 	if ((stream->source != NULL) && (ms_filter_get_id(stream->source) == MS_RTP_RECV_ID))
48 		rtp_source = TRUE;
49 	if ((stream->output != NULL) && (ms_filter_get_id(stream->output) == MS_RTP_SEND_ID))
50 		rtp_output = TRUE;
51 
52 	/* Prevent filters from being destroyed two times */
53 	if ((stream->source_performs_encoding == TRUE) || (rtp_source == TRUE)) {
54 		stream->ms.encoder = NULL;
55 	}
56 	if ((stream->output_performs_decoding == TRUE) || (rtp_output == TRUE)) {
57 		stream->ms.decoder = NULL;
58 	}
59 
60 	media_stream_free(&stream->ms);
61 
62 	if (stream->void_source != NULL)
63 		ms_filter_destroy(stream->void_source);
64 	if (stream->source != NULL)
65 		ms_filter_destroy (stream->source);
66 	if (stream->output != NULL)
67 		ms_filter_destroy (stream->output);
68 	if (stream->sizeconv != NULL)
69 		ms_filter_destroy (stream->sizeconv);
70 	if (stream->pixconv!=NULL)
71 		ms_filter_destroy(stream->pixconv);
72 	if (stream->tee!=NULL)
73 		ms_filter_destroy(stream->tee);
74 	if (stream->tee2!=NULL)
75 		ms_filter_destroy(stream->tee2);
76 	if (stream->jpegwriter!=NULL)
77 		ms_filter_destroy(stream->jpegwriter);
78 	if (stream->output2!=NULL)
79 		ms_filter_destroy(stream->output2);
80 	if (stream->tee3)
81 		ms_filter_destroy(stream->tee3);
82 	if (stream->recorder_output)
83 		ms_filter_destroy(stream->recorder_output);
84 	if (stream->local_jpegwriter)
85 		ms_filter_destroy(stream->local_jpegwriter);
86 	if (stream->rtp_io_session)
87 		rtp_session_destroy(stream->rtp_io_session);
88 
89 	if (stream->display_name!=NULL)
90 		ms_free(stream->display_name);
91 	if (stream->preset != NULL) ms_free(stream->preset);
92 
93 	ms_free(stream);
94 }
95 
event_cb(void * ud,MSFilter * f,unsigned int event,void * eventdata)96 static void event_cb(void *ud, MSFilter* f, unsigned int event, void *eventdata){
97 	VideoStream *st=(VideoStream*)ud;
98 	if (st->eventcb!=NULL){
99 		st->eventcb(st->event_pointer,f,event,eventdata);
100 	}
101 }
102 
internal_event_cb(void * ud,MSFilter * f,unsigned int event,void * eventdata)103 static void internal_event_cb(void *ud, MSFilter *f, unsigned int event, void *eventdata) {
104 	VideoStream *stream = (VideoStream *)ud;
105 	const MSVideoCodecSLI *sli;
106 	const MSVideoCodecRPSI *rpsi;
107 
108 	switch (event) {
109 		case MS_VIDEO_DECODER_SEND_PLI:
110 			ms_message("Request sending of PLI on videostream [%p]", stream);
111 			video_stream_send_pli(stream);
112 			break;
113 		case MS_VIDEO_DECODER_SEND_SLI:
114 			sli = (const MSVideoCodecSLI *)eventdata;
115 			ms_message("Request sending of SLI on videostream [%p]", stream);
116 			video_stream_send_sli(stream, sli->first, sli->number, sli->picture_id);
117 			break;
118 		case MS_VIDEO_DECODER_SEND_RPSI:
119 			rpsi = (const MSVideoCodecRPSI *)eventdata;
120 			ms_message("Request sending of RPSI on videostream [%p]", stream);
121 			video_stream_send_rpsi(stream, rpsi->bit_string, rpsi->bit_string_len);
122 			break;
123 		case MS_FILTER_OUTPUT_FMT_CHANGED:
124 			if (stream->recorder_output) configure_recorder_output(stream);
125 			break;
126 	}
127 }
128 
video_stream_process_rtcp(MediaStream * media_stream,mblk_t * m)129 static void video_stream_process_rtcp(MediaStream *media_stream, mblk_t *m){
130 	VideoStream *stream = (VideoStream *)media_stream;
131 	int i;
132 
133 	if (rtcp_is_PSFB(m) && (stream->ms.encoder != NULL)) {
134 		/* The PSFB messages are to be notified to the encoder, so if we have no encoder simply ignore them. */
135 		if (rtcp_PSFB_get_media_source_ssrc(m) == rtp_session_get_send_ssrc(stream->ms.sessions.rtp_session)) {
136 			switch (rtcp_PSFB_get_type(m)) {
137 				case  RTCP_PSFB_FIR:
138 					for (i = 0; ; i++) {
139 						rtcp_fb_fir_fci_t *fci = rtcp_PSFB_fir_get_fci(m, i);
140 						if (fci == NULL) break;
141 						if (rtcp_fb_fir_fci_get_ssrc(fci) == rtp_session_get_recv_ssrc(stream->ms.sessions.rtp_session)) {
142 							uint8_t seq_nr = rtcp_fb_fir_fci_get_seq_nr(fci);
143 							ms_filter_call_method(stream->ms.encoder, MS_VIDEO_ENCODER_NOTIFY_FIR, &seq_nr);
144                             stream->ms_video_stat.counter_rcvd_fir++;
145                             ms_message("video_stream_process_rtcp stream [%p] FIR count %d", stream,  stream->ms_video_stat.counter_rcvd_fir);
146 
147 							break;
148 						}
149 					}
150 					break;
151 				case RTCP_PSFB_PLI:
152 
153                     stream->ms_video_stat.counter_rcvd_pli++;
154 					ms_filter_call_method_noarg(stream->ms.encoder, MS_VIDEO_ENCODER_NOTIFY_PLI);
155                     ms_message("video_stream_process_rtcp stream [%p] PLI count %d", stream,  stream->ms_video_stat.counter_rcvd_pli);
156 
157 					break;
158 				case RTCP_PSFB_SLI:
159 					for (i = 0; ; i++) {
160 
161 						rtcp_fb_sli_fci_t *fci = rtcp_PSFB_sli_get_fci(m, i);
162 						MSVideoCodecSLI sli;
163 						if (fci == NULL) break;
164 						sli.first = rtcp_fb_sli_fci_get_first(fci);
165 						sli.number = rtcp_fb_sli_fci_get_number(fci);
166 						sli.picture_id = rtcp_fb_sli_fci_get_picture_id(fci);
167 						ms_filter_call_method(stream->ms.encoder, MS_VIDEO_ENCODER_NOTIFY_SLI, &sli);
168                         stream->ms_video_stat.counter_rcvd_sli++;
169                         ms_message("video_stream_process_rtcp stream [%p] SLI count %d", stream,  stream->ms_video_stat.counter_rcvd_sli);
170 
171 					}
172 					break;
173 				case RTCP_PSFB_RPSI:
174 				{
175 					rtcp_fb_rpsi_fci_t *fci = rtcp_PSFB_rpsi_get_fci(m);
176 					MSVideoCodecRPSI rpsi;
177 					rpsi.bit_string = rtcp_fb_rpsi_fci_get_bit_string(fci);
178 					rpsi.bit_string_len = rtcp_PSFB_rpsi_get_fci_bit_string_len(m);
179 					ms_filter_call_method(stream->ms.encoder, MS_VIDEO_ENCODER_NOTIFY_RPSI, &rpsi);
180                     stream->ms_video_stat.counter_rcvd_rpsi++;
181                     ms_message("video_stream_process_rtcp stream [%p] RPSI count %d", stream,  stream->ms_video_stat.counter_rcvd_rpsi);
182 				}
183 					break;
184 				default:
185 					break;
186 			}
187 		}
188 	}
189 }
190 
stop_preload_graph(VideoStream * stream)191 static void stop_preload_graph(VideoStream *stream){
192 	ms_ticker_detach(stream->ms.sessions.ticker,stream->ms.rtprecv);
193 	ms_filter_unlink(stream->ms.rtprecv,0,stream->ms.voidsink,0);
194 	ms_filter_destroy(stream->ms.voidsink);
195 	ms_filter_destroy(stream->ms.rtprecv);
196 	stream->ms.voidsink=stream->ms.rtprecv=NULL;
197 }
198 
video_stream_track_fps_changes(VideoStream * stream)199 static void video_stream_track_fps_changes(VideoStream *stream){
200 	uint64_t curtime=ortp_get_cur_time_ms();
201 	if (stream->last_fps_check==(uint64_t)-1){
202 		stream->last_fps_check=curtime;
203 		return;
204 	}
205 	if (curtime-stream->last_fps_check>=2000 && stream->configured_fps>0 && stream->ms.sessions.ticker){
206 		MSTickerLateEvent late_ev={0};
207 		/*we must check that no late tick occured during the last 2 seconds, otherwise the fps measurement is severely biased.*/
208 		ms_ticker_get_last_late_tick(stream->ms.sessions.ticker,&late_ev);
209 
210 		if (curtime > late_ev.time + 2000){
211 			if (stream->source && stream->ms.encoder &&
212 				ms_filter_has_method(stream->source,MS_FILTER_GET_FPS) &&
213 				ms_filter_has_method(stream->ms.encoder,MS_FILTER_SET_FPS)){
214 				float fps=0;
215 
216 				if (ms_filter_call_method(stream->source,MS_FILTER_GET_FPS,&fps)==0 && fps!=0){
217 					if (fabsf(fps-stream->configured_fps)/stream->configured_fps>0.2){
218 						ms_warning("Measured and target fps significantly different (%f<->%f), updating encoder.",
219 							fps,stream->configured_fps);
220 						stream->real_fps=fps;
221 						ms_filter_call_method(stream->ms.encoder,MS_FILTER_SET_FPS,&stream->real_fps);
222 					}
223 				}
224 			}
225 			stream->last_fps_check=curtime;
226 		}
227 	}
228 }
229 
video_stream_iterate(VideoStream * stream)230 void video_stream_iterate(VideoStream *stream){
231 	media_stream_iterate(&stream->ms);
232 	video_stream_track_fps_changes(stream);
233 }
234 
video_stream_get_default_video_renderer(void)235 const char *video_stream_get_default_video_renderer(void){
236 #if defined(MS2_WINDOWS_UNIVERSAL)
237 	return "MSWinRTBackgroundDis";
238 #elif defined(MS2_WINDOWS_PHONE)
239 	return "MSWP8Dis";
240 #elif defined(MS2_WINDOWS_DESKTOP)
241 	return "MSDrawDibDisplay";
242 #elif defined(__ANDROID__)
243 	return "MSAndroidDisplay";
244 #elif __APPLE__ && !TARGET_OS_IPHONE
245 	return "MSOSXGLDisplay";
246 #elif defined (HAVE_XV)
247 	return "MSX11Video";
248 #elif defined(HAVE_GLX)
249 	return "MSGLXVideo";
250 #elif TARGET_OS_IPHONE
251 	return "IOSDisplay";
252 #elif defined(__QNX__)
253 	return "MSBB10Display";
254 #else
255 	return "MSVideoOut";
256 #endif
257 }
258 
choose_display_name(VideoStream * stream)259 static void choose_display_name(VideoStream *stream){
260 	stream->display_name=ms_strdup(video_stream_get_default_video_renderer());
261 }
262 
video_stream_get_rtcp_xr_average_quality_rating(void * userdata)263 static float video_stream_get_rtcp_xr_average_quality_rating(void *userdata) {
264 	VideoStream *stream = (VideoStream *)userdata;
265 	return stream ? media_stream_get_average_quality_rating(&stream->ms) : -1;
266 }
267 
video_stream_get_rtcp_xr_average_lq_quality_rating(void * userdata)268 static float video_stream_get_rtcp_xr_average_lq_quality_rating(void *userdata) {
269 	VideoStream *stream = (VideoStream *)userdata;
270 	return stream ? media_stream_get_average_lq_quality_rating(&stream->ms) : -1;
271 }
272 
273 
video_stream_new(MSFactory * factory,int loc_rtp_port,int loc_rtcp_port,bool_t use_ipv6)274 VideoStream *video_stream_new(MSFactory* factory, int loc_rtp_port, int loc_rtcp_port, bool_t use_ipv6){
275 	return video_stream_new2(factory, use_ipv6 ? "::" : "0.0.0.0", loc_rtp_port, loc_rtcp_port);
276 }
277 
video_stream_new2(MSFactory * factory,const char * ip,int loc_rtp_port,int loc_rtcp_port)278 VideoStream *video_stream_new2(MSFactory* factory, const char* ip, int loc_rtp_port, int loc_rtcp_port) {
279 	MSMediaStreamSessions sessions={0};
280 	VideoStream *obj;
281 	sessions.rtp_session=ms_create_duplex_rtp_session(ip,loc_rtp_port,loc_rtcp_port, ms_factory_get_mtu(factory));
282 	obj=video_stream_new_with_sessions(factory, &sessions);
283 	obj->ms.owns_sessions=TRUE;
284 	return obj;
285 }
286 
287 
video_stream_new_with_sessions(MSFactory * factory,const MSMediaStreamSessions * sessions)288 VideoStream *video_stream_new_with_sessions(MSFactory* factory, const MSMediaStreamSessions *sessions){
289 	VideoStream *stream = (VideoStream *)ms_new0 (VideoStream, 1);
290 	const OrtpRtcpXrMediaCallbacks rtcp_xr_media_cbs = {
291 		NULL,
292 		NULL,
293 		NULL,
294 		video_stream_get_rtcp_xr_average_quality_rating,
295 		video_stream_get_rtcp_xr_average_lq_quality_rating,
296 		stream
297 	};
298 
299 	stream->ms.type = MSVideo;
300 	stream->ms.sessions=*sessions;
301 
302 	media_stream_init(&stream->ms, factory, sessions);
303 
304 	rtp_session_resync(stream->ms.sessions.rtp_session);
305 	stream->ms.qi=ms_quality_indicator_new(stream->ms.sessions.rtp_session);
306 	ms_quality_indicator_set_label(stream->ms.qi,"video");
307 
308 	stream->ms.rtpsend=ms_factory_create_filter(stream->ms.factory, MS_RTP_SEND_ID);
309 
310 	stream->ms.ice_check_list=NULL;
311 	MS_VIDEO_SIZE_ASSIGN(stream->sent_vsize, CIF);
312 	stream->forced_fps=0;
313 	stream->real_fps=0;
314 	stream->dir=MediaStreamSendRecv;
315 	stream->display_filter_auto_rotate_enabled=0;
316 	stream->freeze_on_error = FALSE;
317 	stream->source_performs_encoding = FALSE;
318 	stream->output_performs_decoding = FALSE;
319 	choose_display_name(stream);
320 	stream->ms.process_rtcp=video_stream_process_rtcp;
321 	/*
322 	 * In practice, these filters are needed only for audio+video recording.
323 	 */
324 	if (ms_factory_lookup_filter_by_id(stream->ms.factory, MS_MKV_RECORDER_ID)){
325 
326 		stream->tee3=ms_factory_create_filter(stream->ms.factory, MS_TEE_ID);
327 		stream->recorder_output=ms_factory_create_filter(stream->ms.factory, MS_ITC_SINK_ID);
328 
329 	}
330 
331 	rtp_session_set_rtcp_xr_media_callbacks(stream->ms.sessions.rtp_session, &rtcp_xr_media_cbs);
332 
333 	stream->staticimage_webcam_fps_optimization = TRUE;
334 
335 	return stream;
336 }
337 
video_stream_set_sent_video_size(VideoStream * stream,MSVideoSize vsize)338 void video_stream_set_sent_video_size(VideoStream *stream, MSVideoSize vsize){
339 	ms_message("Setting video size %dx%d on stream [%p]", vsize.width, vsize.height,stream);
340 	stream->sent_vsize=vsize;
341 }
342 
video_stream_set_preview_size(VideoStream * stream,MSVideoSize vsize)343 void video_stream_set_preview_size(VideoStream *stream, MSVideoSize vsize){
344 	ms_message("Setting preview video size %dx%d", vsize.width, vsize.height);
345 	stream->preview_vsize=vsize;
346 }
347 
video_stream_set_fps(VideoStream * stream,float fps)348 void video_stream_set_fps(VideoStream *stream, float fps){
349 	stream->forced_fps=fps;
350 }
351 
video_stream_get_sent_video_size(const VideoStream * stream)352 MSVideoSize video_stream_get_sent_video_size(const VideoStream *stream) {
353 	MSVideoSize vsize;
354 	MS_VIDEO_SIZE_ASSIGN(vsize, UNKNOWN);
355 	if (stream->ms.encoder != NULL) {
356 		ms_filter_call_method(stream->ms.encoder, MS_FILTER_GET_VIDEO_SIZE, &vsize);
357 	}
358 	return vsize;
359 }
360 
video_stream_get_received_video_size(const VideoStream * stream)361 MSVideoSize video_stream_get_received_video_size(const VideoStream *stream) {
362 	MSVideoSize vsize;
363 	MS_VIDEO_SIZE_ASSIGN(vsize, UNKNOWN);
364 	if (stream->ms.decoder != NULL) {
365 		ms_filter_call_method(stream->ms.decoder, MS_FILTER_GET_VIDEO_SIZE, &vsize);
366 	}
367 	return vsize;
368 }
369 
video_stream_get_sent_framerate(const VideoStream * stream)370 float video_stream_get_sent_framerate(const VideoStream *stream){
371 	float fps=0;
372 	if (stream->source){
373 		if (ms_filter_has_method(stream->source, MS_FILTER_GET_FPS)){
374 			ms_filter_call_method(stream->source,MS_FILTER_GET_FPS,&fps);
375 		}else if (stream->pixconv && ms_filter_has_method(stream->pixconv, MS_FILTER_GET_FPS)){
376 			ms_filter_call_method(stream->pixconv,MS_FILTER_GET_FPS,&fps);
377 		}
378 	}
379 	return fps;
380 }
381 
video_stream_get_received_framerate(const VideoStream * stream)382 float video_stream_get_received_framerate(const VideoStream *stream){
383 	float fps=0;
384 	if (stream->ms.decoder != NULL && ms_filter_has_method(stream->ms.decoder, MS_FILTER_GET_FPS)) {
385 		ms_filter_call_method(stream->ms.decoder, MS_FILTER_GET_FPS, &fps);
386 	}
387 	return fps;
388 }
389 
video_stream_set_relay_session_id(VideoStream * stream,const char * id)390 void video_stream_set_relay_session_id(VideoStream *stream, const char *id){
391 	ms_filter_call_method(stream->ms.rtpsend, MS_RTP_SEND_SET_RELAY_SESSION_ID,(void*)id);
392 }
393 
video_stream_enable_self_view(VideoStream * stream,bool_t val)394 void video_stream_enable_self_view(VideoStream *stream, bool_t val){
395 	MSFilter *out=stream->output;
396 	stream->corner=val ? 0 : -1;
397 	if (out){
398 		ms_filter_call_method(out,MS_VIDEO_DISPLAY_SET_LOCAL_VIEW_MODE,&stream->corner);
399 	}
400 }
401 
video_stream_set_render_callback(VideoStream * s,VideoStreamRenderCallback cb,void * user_pointer)402 void video_stream_set_render_callback (VideoStream *s, VideoStreamRenderCallback cb, void *user_pointer){
403 	s->rendercb=cb;
404 	s->render_pointer=user_pointer;
405 }
406 
video_stream_set_event_callback(VideoStream * s,VideoStreamEventCallback cb,void * user_pointer)407 void video_stream_set_event_callback (VideoStream *s, VideoStreamEventCallback cb, void *user_pointer){
408 	s->eventcb=cb;
409 	s->event_pointer=user_pointer;
410 }
411 
video_stream_set_display_filter_name(VideoStream * s,const char * fname)412 void video_stream_set_display_filter_name(VideoStream *s, const char *fname){
413 	if (s->display_name!=NULL){
414 		ms_free(s->display_name);
415 		s->display_name=NULL;
416 	}
417 	if (fname!=NULL)
418 		s->display_name=ms_strdup(fname);
419 }
420 
421 
ext_display_cb(void * ud,MSFilter * f,unsigned int event,void * eventdata)422 static void ext_display_cb(void *ud, MSFilter* f, unsigned int event, void *eventdata){
423 	MSExtDisplayOutput *output=(MSExtDisplayOutput*)eventdata;
424 	VideoStream *st=(VideoStream*)ud;
425 	if (st->rendercb!=NULL){
426 		st->rendercb(st->render_pointer,
427 					output->local_view.w!=0 ? &output->local_view : NULL,
428 					output->remote_view.w!=0 ? &output->remote_view : NULL);
429 	}
430 }
431 
video_stream_set_direction(VideoStream * vs,MediaStreamDir dir)432 void video_stream_set_direction(VideoStream *vs, MediaStreamDir dir){
433 	vs->dir=dir;
434 }
435 
get_compatible_size(MSVideoSize maxsize,MSVideoSize wished_size)436 static MSVideoSize get_compatible_size(MSVideoSize maxsize, MSVideoSize wished_size){
437 	int max_area=maxsize.width*maxsize.height;
438 	int whished_area=wished_size.width*wished_size.height;
439 	if (whished_area>max_area){
440 		return maxsize;
441 	}
442 	return wished_size;
443 }
444 
445 #if !TARGET_IPHONE_SIMULATOR && !defined(MS_HAS_ARM)
get_with_same_orientation_and_ratio(MSVideoSize size,MSVideoSize refsize)446 static MSVideoSize get_with_same_orientation_and_ratio(MSVideoSize size, MSVideoSize refsize){
447 	if (ms_video_size_get_orientation(refsize)!=ms_video_size_get_orientation(size)){
448 		int tmp;
449 		tmp=size.width;
450 		size.width=size.height;
451 		size.height=tmp;
452 	}
453 	size.height=(size.width*refsize.height)/refsize.width;
454 	return size;
455 }
456 #endif
457 
configure_video_source(VideoStream * stream)458 static void configure_video_source(VideoStream *stream){
459 	MSVideoSize vsize,cam_vsize;
460 	float fps=15;
461 	int bitrate;
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 	if (ms_filter_has_method(stream->source, MS_VIDEO_CAPTURE_SET_DEVICE_ORIENTATION))
472 		ms_filter_call_method(stream->source,MS_VIDEO_CAPTURE_SET_DEVICE_ORIENTATION,&stream->device_orientation);
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))
475 		ms_filter_call_method(stream->source,MS_VIDEO_DISPLAY_SET_DEVICE_ORIENTATION,&stream->device_orientation);
476 
477 	/* transmit its preview window id if any to source filter*/
478 	if (stream->preview_window_id!=0){
479 		video_stream_set_native_preview_window_id(stream, stream->preview_window_id);
480 	}
481 
482 	ms_filter_call_method(stream->ms.encoder, MS_FILTER_GET_BITRATE, &bitrate);
483 	if (bitrate == 0) {
484 		bitrate = ms_factory_get_expected_bandwidth(stream->ms.factory);
485 		ms_message("Encoder current bitrate is 0, using expected bandwidth %i", bitrate);
486 		ms_filter_call_method(stream->ms.encoder, MS_FILTER_SET_BITRATE, &bitrate);
487 	}
488 
489 	ms_filter_call_method(stream->ms.encoder,MS_FILTER_GET_VIDEO_SIZE,&vsize);
490 	vsize=get_compatible_size(vsize,stream->sent_vsize);
491 	if (stream->preview_vsize.width!=0){
492 		preview_vsize=stream->preview_vsize;
493 	}else{
494 		preview_vsize=vsize;
495 	}
496 
497 	if (is_player){
498 		ms_filter_call_method(stream->source,MS_FILTER_GET_OUTPUT_FMT,&pf);
499 		if (pf.fmt==NULL || pf.fmt->vsize.width==0){
500 			MSVideoSize vsize={640,480};
501 			ms_error("Player does not give its format correctly [%s]",ms_fmt_descriptor_to_string(pf.fmt));
502 			/*put a default format as the error handling is complicated here*/
503 			pf.fmt=ms_factory_get_video_format(stream->ms.factory,"VP8",vsize,0,NULL);
504 		}
505 		cam_vsize=pf.fmt->vsize;
506 	}else{
507 		ms_filter_call_method(stream->source,MS_FILTER_SET_VIDEO_SIZE,&preview_vsize);
508 		/*the camera may not support the target size and suggest a one close to the target */
509 		ms_filter_call_method(stream->source,MS_FILTER_GET_VIDEO_SIZE,&cam_vsize);
510 	}
511 
512 	if (cam_vsize.width*cam_vsize.height<=vsize.width*vsize.height){
513 		vsize=cam_vsize;
514 		ms_message("Output video size adjusted to match camera resolution (%ix%i)",vsize.width,vsize.height);
515 	} else {
516 #if TARGET_IPHONE_SIMULATOR || defined(MS_HAS_ARM) || defined(MS2_WINDOWS_UNIVERSAL)
517 		ms_error("Camera is proposing a size bigger than encoder's suggested size (%ix%i > %ix%i) "
518 				   "Using the camera size as fallback because cropping or resizing is not implemented for this device.",
519 				   cam_vsize.width,cam_vsize.height,vsize.width,vsize.height);
520 		vsize=cam_vsize;
521 #else
522 		MSVideoSize resized=get_with_same_orientation_and_ratio(vsize,cam_vsize);
523 		if (resized.width & 0x1 || resized.height & 0x1){
524 			ms_warning("Resizing avoided because downsizing to an odd number of pixels (%ix%i)",resized.width,resized.height);
525 			vsize=cam_vsize;
526 		}else{
527 			vsize=resized;
528 			ms_warning("Camera video size greater than encoder one. A scaling filter will be used!");
529 		}
530 #endif
531 	}
532 	ms_filter_call_method(stream->ms.encoder,MS_FILTER_SET_VIDEO_SIZE,&vsize);
533 	ms_filter_call_method(stream->ms.encoder,MS_FILTER_GET_FPS,&fps);
534 
535 	if (is_player){
536 		fps=pf.fmt->fps;
537 		if (fps==0) fps=15;
538 		ms_filter_call_method(stream->ms.encoder,MS_FILTER_SET_FPS,&fps);
539 	}else{
540 		if (stream->forced_fps!=0)
541 			fps=stream->forced_fps;
542 		ms_message("Setting sent vsize=%ix%i, fps=%f",vsize.width,vsize.height,fps);
543 		/* configure the filters */
544 		if (ms_filter_get_id(stream->source)!=MS_STATIC_IMAGE_ID || !stream->staticimage_webcam_fps_optimization) {
545 			ms_filter_call_method(stream->source,MS_FILTER_SET_FPS,&fps);
546 		}
547 		ms_filter_call_method(stream->ms.encoder,MS_FILTER_SET_FPS,&fps);
548 		/* get the output format for webcam reader */
549 		ms_filter_call_method(stream->source,MS_FILTER_GET_PIX_FMT,&format);
550 	}
551 	stream->configured_fps=fps;
552 
553 	encoder_supports_source_format.supported = FALSE;
554 	encoder_supports_source_format.pixfmt = format;
555 
556 	if (ms_filter_has_method(stream->ms.encoder, MS_VIDEO_ENCODER_SUPPORTS_PIXFMT) == TRUE) {
557 		ret = ms_filter_call_method(stream->ms.encoder, MS_VIDEO_ENCODER_SUPPORTS_PIXFMT, &encoder_supports_source_format);
558 	} else {
559 		ret = -1;
560 	}
561 	if (ret == -1) {
562 		/*encoder doesn't have MS_VIDEO_ENCODER_SUPPORTS_PIXFMT method*/
563 		/*we prefer in this case consider that it is not required to get the optimization of not going through pixconv and sizeconv*/
564 		encoder_supports_source_format.supported = FALSE;
565 	}
566 
567 	if ((encoder_supports_source_format.supported == TRUE) || (stream->source_performs_encoding == TRUE)) {
568 		ms_filter_call_method(stream->ms.encoder, MS_FILTER_SET_PIX_FMT, &format);
569 	} else {
570 		if (format==MS_MJPEG){
571 			stream->pixconv=ms_factory_create_filter(stream->ms.factory, MS_MJPEG_DEC_ID);
572 			if (stream->pixconv == NULL){
573 				ms_error("Could not create mjpeg decoder, check your build options.");
574 			}
575 		}else if (format==MS_PIX_FMT_UNKNOWN){
576 			stream->pixconv = ms_factory_create_decoder(stream->ms.factory, pf.fmt->encoding);
577 		}else{
578 			stream->pixconv = ms_factory_create_filter(stream->ms.factory, MS_PIX_CONV_ID);
579 			/*set it to the pixconv */
580 			ms_filter_call_method(stream->pixconv,MS_FILTER_SET_PIX_FMT,&format);
581 			ms_filter_call_method(stream->pixconv,MS_FILTER_SET_VIDEO_SIZE,&cam_vsize);
582 		}
583 		stream->sizeconv=ms_factory_create_filter(stream->ms.factory, MS_SIZE_CONV_ID);
584 		ms_filter_call_method(stream->sizeconv,MS_FILTER_SET_VIDEO_SIZE,&vsize);
585 	}
586 	if (stream->ms.rc){
587 		ms_bitrate_controller_destroy(stream->ms.rc);
588 		stream->ms.rc=NULL;
589 	}
590 	if (stream->ms.rc_enable){
591 		switch (stream->ms.rc_algorithm){
592 		case MSQosAnalyzerAlgorithmSimple:
593 			stream->ms.rc=ms_av_bitrate_controller_new(NULL,NULL,stream->ms.sessions.rtp_session,stream->ms.encoder);
594 			break;
595 		case MSQosAnalyzerAlgorithmStateful:
596 			stream->ms.rc=ms_bandwidth_bitrate_controller_new(NULL, NULL, stream->ms.sessions.rtp_session,stream->ms.encoder);
597 			break;
598 		}
599 	}
600 }
601 
602 
603 
configure_recorder_output(VideoStream * stream)604 static void configure_recorder_output(VideoStream *stream){
605 	if (stream->recorder_output){
606 		MSPinFormat pf={0};
607 		ms_filter_call_method(stream->ms.decoder,MS_FILTER_GET_OUTPUT_FMT,&pf);
608 		if (pf.fmt){
609 			MSPinFormat pinfmt={0};
610 			RtpSession *session=stream->ms.sessions.rtp_session;
611 			PayloadType *pt=rtp_profile_get_payload(rtp_session_get_profile(session),rtp_session_get_recv_payload_type(session));
612 			if (!pt) pt=rtp_profile_get_payload(rtp_session_get_profile(session),rtp_session_get_send_payload_type(session));
613 			if (pt){
614 				MSFmtDescriptor tmp=*pf.fmt;
615 				tmp.encoding=pt->mime_type;
616 				tmp.rate=pt->clock_rate;
617 				pinfmt.pin=0;
618 				pinfmt.fmt=ms_factory_get_format(stream->ms.factory,&tmp);
619 				ms_filter_call_method(stream->recorder_output,MS_FILTER_SET_INPUT_FMT,&pinfmt);
620 				ms_message("configure_itc(): format set to %s",ms_fmt_descriptor_to_string(pinfmt.fmt));
621 			}
622 		}else ms_warning("configure_itc(): video decoder doesn't give output format.");
623 	}
624 }
625 
configure_decoder(VideoStream * stream,PayloadType * pt)626 static void configure_decoder(VideoStream *stream, PayloadType *pt){
627 	bool_t avpf_enabled=!!(pt->flags & PAYLOAD_TYPE_RTCP_FEEDBACK_ENABLED);
628 	ms_filter_call_method(stream->ms.decoder, MS_VIDEO_DECODER_ENABLE_AVPF, &avpf_enabled);
629 	ms_filter_call_method(stream->ms.decoder, MS_VIDEO_DECODER_FREEZE_ON_ERROR, &stream->freeze_on_error);
630 	ms_filter_add_notify_callback(stream->ms.decoder, event_cb, stream, FALSE);
631 	/* It is important that the internal_event_cb is called synchronously! */
632 	ms_filter_add_notify_callback(stream->ms.decoder, internal_event_cb, stream, TRUE);
633 }
634 
video_stream_payload_type_changed(RtpSession * session,void * data)635 static void video_stream_payload_type_changed(RtpSession *session, void *data){
636 	VideoStream *stream = (VideoStream *)data;
637 	RtpProfile *prof = rtp_session_get_profile(session);
638 	int payload = rtp_session_get_recv_payload_type(session);
639 	PayloadType *pt = rtp_profile_get_payload(prof, payload);
640 
641 	if (stream->ms.decoder == NULL){
642 		ms_message("video_stream_payload_type_changed(): no decoder!");
643 		return;
644 	}
645 
646 	if (pt != NULL){
647 		MSFilter *dec;
648 
649 		/* Q: why only video ? A: because an audio format can be used at different rates: ex: speex/16000 speex/8000*/
650 		if ((stream->ms.decoder != NULL) && (stream->ms.decoder->desc->enc_fmt != NULL)
651 			&& (strcasecmp(pt->mime_type, stream->ms.decoder->desc->enc_fmt) == 0)) {
652 			/* Same formats behind different numbers, nothing to do. */
653 			return;
654 		}
655 
656 //		dec = ms_filter_create_decoder(pt->mime_type);
657 		dec = ms_factory_create_decoder(stream->ms.factory,pt->mime_type);
658 		if (dec != NULL) {
659 			MSFilter *prevFilter = stream->ms.decoder->inputs[0]->prev.filter;
660 			MSFilter *nextFilter = stream->ms.decoder->outputs[0]->next.filter;
661 
662 			ms_filter_unlink(prevFilter, 0, stream->ms.decoder, 0);
663 			ms_filter_unlink(stream->ms.decoder, 0, nextFilter, 0);
664 			ms_filter_postprocess(stream->ms.decoder);
665 			ms_filter_destroy(stream->ms.decoder);
666 			stream->ms.decoder = dec;
667 			if (pt->recv_fmtp != NULL)
668 				ms_filter_call_method(stream->ms.decoder, MS_FILTER_ADD_FMTP, (void *)pt->recv_fmtp);
669 			ms_filter_link(prevFilter, 0, stream->ms.decoder, 0);
670 			ms_filter_link(stream->ms.decoder, 0, nextFilter, 0);
671 			ms_filter_preprocess(stream->ms.decoder, stream->ms.sessions.ticker);
672 
673 			configure_decoder(stream, pt);
674 		} else {
675 			ms_warning("No decoder found for %s", pt->mime_type);
676 		}
677 	} else {
678 		ms_warning("No payload defined with number %i", payload);
679 	}
680 
681 	configure_recorder_output(stream);
682 }
683 
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)684 int video_stream_start (VideoStream *stream, RtpProfile *profile, const char *rem_rtp_ip, int rem_rtp_port,
685 	const char *rem_rtcp_ip, int rem_rtcp_port, int payload, int jitt_comp, MSWebCam *cam){
686 	MSMediaStreamIO io = MS_MEDIA_STREAM_IO_INITIALIZER;
687 	if (cam == NULL){
688 		cam = ms_web_cam_manager_get_default_cam(ms_factory_get_web_cam_manager(stream->ms.factory));
689 	}
690 	io.input.type = MSResourceCamera;
691 	io.input.camera = cam;
692 	io.output.type = MSResourceDefault;
693 	io.output.resource_arg = NULL;
694 	rtp_session_set_jitter_compensation(stream->ms.sessions.rtp_session, jitt_comp);
695 	return video_stream_start_from_io(stream, profile, rem_rtp_ip, rem_rtp_port, rem_rtcp_ip, rem_rtcp_port, payload, &io);
696 }
697 
video_recorder_handle_event(void * userdata,MSFilter * recorder,unsigned int event,void * event_arg)698 void video_recorder_handle_event(void *userdata, MSFilter *recorder, unsigned int event, void *event_arg){
699 	VideoStream *stream = (VideoStream*) userdata;
700 	switch (event){
701 		case MS_RECORDER_NEEDS_FIR:
702 			ms_message("Request sending of FIR on videostream [%p]", stream);
703 			video_stream_send_fir(stream);
704 			break;
705 		default:
706 			break;
707 	}
708 }
709 
video_stream_start_from_io(VideoStream * stream,RtpProfile * profile,const char * rem_rtp_ip,int rem_rtp_port,const char * rem_rtcp_ip,int rem_rtcp_port,int payload,const MSMediaStreamIO * io)710 int video_stream_start_from_io(VideoStream *stream, RtpProfile *profile, const char *rem_rtp_ip, int rem_rtp_port,
711 	const char *rem_rtcp_ip, int rem_rtcp_port, int payload, const MSMediaStreamIO *io) {
712 	MSWebCam *cam = NULL;
713 	MSFilter *source = NULL;
714 	MSFilter *output = NULL;
715 	MSFilter *recorder = NULL;
716 
717 	if (stream->ms.state != MSStreamInitialized){
718 		ms_error("VideoStream in bad state");
719 		return -1;
720 	}
721 
722 	if (!ms_media_stream_io_is_consistent(io)) return -1;
723 
724 	if (stream->dir != MediaStreamRecvOnly){
725 		switch(io->input.type){
726 			case MSResourceRtp:
727 				stream->rtp_io_session = io->input.session;
728 				source = ms_factory_create_filter(stream->ms.factory, MS_RTP_RECV_ID);
729 				ms_filter_call_method(source, MS_RTP_RECV_SET_SESSION, stream->rtp_io_session);
730 			break;
731 			case MSResourceCamera:
732 				cam = io->input.camera;
733 				source = ms_web_cam_create_reader(cam);
734 			break;
735 			case MSResourceFile:
736 				source = ms_factory_create_filter(stream->ms.factory, MS_MKV_PLAYER_ID);
737 				if (!source){
738 					ms_error("Mediastreamer2 library compiled without libmastroska2");
739 					return -1;
740 				}
741 				stream->source = source;
742 				if (io->input.file) {
743 					if (video_stream_open_remote_play(stream, io->input.file)!=NULL)
744 						ms_filter_call_method_noarg(source, MS_PLAYER_START);
745 				}
746 			break;
747 			default:
748 				ms_error("Unhandled input resource type %s", ms_resource_type_to_string(io->input.type));
749 			break;
750 		}
751 	}
752 	if (stream->dir != MediaStreamSendOnly){
753 		switch (io->output.type){
754 			case MSResourceRtp:
755 				output = ms_factory_create_filter(stream->ms.factory, MS_RTP_SEND_ID);
756 				stream->rtp_io_session = io->input.session;
757 				ms_filter_call_method(output, MS_RTP_SEND_SET_SESSION, stream->rtp_io_session);
758 			break;
759 			case MSResourceFile:
760 				recorder = ms_factory_create_filter(stream->ms.factory, MS_MKV_RECORDER_ID);
761 				if (!recorder){
762 					ms_error("Mediastreamer2 library compiled without libmastroska2");
763 					return -1;
764 				}
765 				if (stream->recorder_output){
766 					ms_filter_destroy(stream->recorder_output);
767 				}
768 				stream->recorder_output = recorder;
769 				ms_filter_add_notify_callback(recorder, video_recorder_handle_event, stream, TRUE);
770 				if (io->output.file) video_stream_open_remote_record(stream, io->output.file);
771 			break;
772 			default:
773 				/*will just display in all other cases*/
774 				/*ms_error("Unhandled output resource type %s", ms_resource_type_to_string(io->output.type));*/
775 			break;
776 		}
777 	}
778 
779 	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);
780 }
781 
video_stream_started(VideoStream * stream)782 bool_t video_stream_started(VideoStream *stream) {
783 	return media_stream_started(&stream->ms);
784 }
785 
apply_video_preset(VideoStream * stream,PayloadType * pt)786 static void apply_video_preset(VideoStream *stream, PayloadType *pt) {
787 	MSVideoPresetsManager *vpm = ms_factory_get_video_presets_manager(stream->ms.factory);
788 	MSVideoPresetConfiguration *vpc = NULL;
789 	MSVideoConfiguration *conf = NULL;
790 	bctbx_list_t *codec_tags = NULL;
791 	bool_t hardware_accelerated = FALSE;
792 	if (stream->preset != NULL) {
793 		codec_tags = bctbx_list_append(codec_tags, ms_strdup(payload_type_get_mime(pt)));
794 		codec_tags = bctbx_list_append(codec_tags, ms_strdup(stream->ms.encoder->desc->name));
795 		if (ms_filter_has_method(stream->ms.encoder, MS_VIDEO_ENCODER_IS_HARDWARE_ACCELERATED) == TRUE) {
796 			ms_filter_call_method(stream->ms.encoder, MS_VIDEO_ENCODER_IS_HARDWARE_ACCELERATED, &hardware_accelerated);
797 		}
798 		if (hardware_accelerated == TRUE) {
799 			codec_tags = bctbx_list_append(codec_tags, ms_strdup("hardware"));
800 		}
801 		vpc = ms_video_presets_manager_find_preset_configuration(vpm, stream->preset, codec_tags);
802 		bctbx_list_for_each(codec_tags, ms_free);
803 		bctbx_list_free(codec_tags);
804 		if (vpc != NULL) {
805 			char *conf_tags = ms_video_preset_configuration_get_tags_as_string(vpc);
806 			conf = ms_video_preset_configuration_get_video_configuration(vpc);
807 			if (conf_tags) {
808 				ms_message("Using the '%s' video preset tagged '%s'", stream->preset, conf_tags);
809 				ms_free(conf_tags);
810 			} else {
811 				ms_message("Using the '%s' video preset non-tagged", stream->preset);
812 			}
813 		} else {
814 			ms_warning("No '%s' video preset has been found", stream->preset);
815 		}
816 	}
817 	if (conf == NULL) {
818 		ms_message("Using the default video configuration list");
819 	}
820 	if (ms_filter_has_method(stream->ms.encoder, MS_VIDEO_ENCODER_SET_CONFIGURATION_LIST) == TRUE) {
821 		ms_filter_call_method(stream->ms.encoder, MS_VIDEO_ENCODER_SET_CONFIGURATION_LIST, &conf);
822 	}
823 }
824 
apply_bitrate_limit(VideoStream * stream,PayloadType * pt)825 static void apply_bitrate_limit(VideoStream *stream, PayloadType *pt) {
826 	MSVideoConfiguration *vconf_list = NULL;
827 
828 	if (stream->ms.target_bitrate<=0) {
829 		stream->ms.target_bitrate = ms_factory_get_expected_bandwidth(stream->ms.factory);
830 		if (stream->ms.target_bitrate <= 0) {
831 			stream->ms.target_bitrate=pt->normal_bitrate;
832 			ms_message("target bitrate not set for stream [%p] using payload's bitrate is %i",stream,stream->ms.target_bitrate);
833 		} else {
834 			ms_message("target bitrate not set for stream [%p] using expected bitrate is %i",stream,stream->ms.target_bitrate);
835 		}
836 	}
837 
838 	ms_message("Limiting bitrate of video encoder to %i bits/s for stream [%p]",stream->ms.target_bitrate,stream);
839 	ms_filter_call_method(stream->ms.encoder, MS_VIDEO_ENCODER_GET_CONFIGURATION_LIST, &vconf_list);
840 	if (vconf_list != NULL) {
841 		MSVideoConfiguration vconf = ms_video_find_best_configuration_for_bitrate(vconf_list, stream->ms.target_bitrate, ms_factory_get_cpu_count(stream->ms.factory));
842 		/* Adjust configuration video size to use the user preferred video size if it is lower that the configuration one. */
843 		if ((stream->sent_vsize.height * stream->sent_vsize.width) < (vconf.vsize.height * vconf.vsize.width)) {
844 			vconf.vsize = stream->sent_vsize;
845 		}
846 		ms_filter_call_method(stream->ms.encoder, MS_VIDEO_ENCODER_SET_CONFIGURATION, &vconf);
847 	} else {
848 		ms_filter_call_method(stream->ms.encoder, MS_FILTER_SET_BITRATE, &stream->ms.target_bitrate);
849 	}
850 	rtp_session_set_target_upload_bandwidth(stream->ms.sessions.rtp_session, stream->ms.target_bitrate);
851 }
852 
mime_type_to_pix_format(const char * mime_type)853 static MSPixFmt mime_type_to_pix_format(const char *mime_type) {
854 	if (strcasecmp(mime_type, "H264") == 0) return MS_H264;
855 	return MS_PIX_FMT_UNKNOWN;
856 }
857 
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)858 static int video_stream_start_with_source_and_output(VideoStream *stream, RtpProfile *profile, const char *rem_rtp_ip, int rem_rtp_port,
859 	const char *rem_rtcp_ip, int rem_rtcp_port, int payload, int jitt_comp, MSWebCam *cam, MSFilter *source, MSFilter *output) {
860 	PayloadType *pt;
861 	RtpSession *rtps=stream->ms.sessions.rtp_session;
862 	MSPixFmt format;
863 	MSVideoSize disp_size;
864 	JBParameters jbp;
865 	const int socket_buf_size=2000000;
866 	bool_t avpf_enabled = FALSE;
867 	bool_t rtp_source = FALSE;
868 	bool_t rtp_output = FALSE;
869 	bool_t do_ts_adjustments;
870 
871 	if (source == NULL) {
872 		source = stream->source;
873 	}
874 	rtp_source = (source && ms_filter_get_id(source) == MS_RTP_RECV_ID) ? TRUE : FALSE;
875 	do_ts_adjustments = !rtp_source;
876 
877 	pt=rtp_profile_get_payload(profile,payload);
878 	if (pt==NULL){
879 		ms_error("videostream.c: undefined payload type %d.", payload);
880 		return -1;
881 	}
882 	if (pt->flags & PAYLOAD_TYPE_RTCP_FEEDBACK_ENABLED) avpf_enabled = TRUE;
883 
884 	if ((cam != NULL) && (cam->desc->encode_to_mime_type != NULL) && (cam->desc->encode_to_mime_type(cam, pt->mime_type) == TRUE)) {
885 		stream->source_performs_encoding = TRUE;
886 	}
887 
888 	rtp_session_set_profile(rtps,profile);
889 	if (rem_rtp_port>0)
890 		rtp_session_set_remote_addr_full(rtps,rem_rtp_ip,rem_rtp_port,rem_rtcp_ip,rem_rtcp_port);
891 	if (rem_rtcp_port > 0) {
892 		rtp_session_enable_rtcp(rtps, TRUE);
893 	} else {
894 		rtp_session_enable_rtcp(rtps, FALSE);
895 	}
896 	rtp_session_set_payload_type(rtps,payload);
897 	if (jitt_comp != -1) {
898 		/*jitt_comp = -1 don't change value. The application can use rtp_session_set_jitter_buffer_params() directly.*/
899 		rtp_session_set_jitter_compensation(rtps, jitt_comp);
900 	}
901 
902 	rtp_session_signal_connect(stream->ms.sessions.rtp_session,"payload_type_changed",
903 			(RtpCallback)video_stream_payload_type_changed,&stream->ms);
904 
905 	rtp_session_get_jitter_buffer_params(stream->ms.sessions.rtp_session,&jbp);
906 	jbp.max_packets=1000;//needed for high resolution video
907 	rtp_session_set_jitter_buffer_params(stream->ms.sessions.rtp_session,&jbp);
908 	rtp_session_set_rtp_socket_recv_buffer_size(stream->ms.sessions.rtp_session,socket_buf_size);
909 	rtp_session_set_rtp_socket_send_buffer_size(stream->ms.sessions.rtp_session,socket_buf_size);
910 
911 	/* Plumb the outgoing stream */
912 	if (rem_rtp_port>0) ms_filter_call_method(stream->ms.rtpsend,MS_RTP_SEND_SET_SESSION,stream->ms.sessions.rtp_session);
913 	ms_filter_call_method(stream->ms.rtpsend, MS_RTP_SEND_ENABLE_TS_ADJUSTMENT, &do_ts_adjustments);
914 
915 	if (stream->dir==MediaStreamRecvOnly){
916 		/* Create a dummy sending stream to send the STUN packets to open firewall ports. */
917 		MSConnectionHelper ch;
918 		bool_t send_silence = FALSE;
919 		stream->void_source = ms_factory_create_filter(stream->ms.factory, MS_VOID_SOURCE_ID);
920 		ms_filter_call_method(stream->void_source, MS_VOID_SOURCE_SEND_SILENCE, &send_silence);
921 		ms_connection_helper_start(&ch);
922 		ms_connection_helper_link(&ch, stream->void_source, -1, 0);
923 		ms_connection_helper_link(&ch, stream->ms.rtpsend, 0, -1);
924 	} else {
925 		MSConnectionHelper ch;
926 		if (stream->source_performs_encoding == TRUE) {
927 			format = mime_type_to_pix_format(pt->mime_type);
928 			ms_filter_call_method(source, MS_FILTER_SET_PIX_FMT, &format);
929 		} else if (!rtp_source) {
930 			stream->ms.encoder=ms_factory_create_encoder(stream->ms.factory, pt->mime_type);
931 			if (stream->ms.encoder==NULL){
932 				/* big problem: we don't have a registered codec for this payload...*/
933 				ms_error("videostream.c: No encoder available for payload %i:%s.",payload,pt->mime_type);
934 				return -1;
935 			}
936 		}
937 		/* creates the filters */
938 		stream->cam=cam;
939 		stream->source = source;
940 		if (rtp_source) {
941 			stream->ms.encoder = stream->source; /* Consider that the source is also the encoder */
942 		} else {
943 			stream->tee = ms_factory_create_filter(stream->ms.factory, MS_TEE_ID);
944 			stream->local_jpegwriter=ms_factory_create_filter(stream->ms.factory, MS_JPEG_WRITER_ID);
945 			if (stream->source_performs_encoding == TRUE) {
946 				stream->ms.encoder = stream->source;	/* Consider the encoder is the source */
947 			}
948 
949 			apply_video_preset(stream, pt);
950 			if (pt->normal_bitrate>0){
951 				apply_bitrate_limit(stream, pt);
952 			}
953 			if (pt->send_fmtp){
954 				ms_filter_call_method(stream->ms.encoder,MS_FILTER_ADD_FMTP,pt->send_fmtp);
955 			}
956 			ms_filter_call_method(stream->ms.encoder, MS_VIDEO_ENCODER_ENABLE_AVPF, &avpf_enabled);
957 			if (stream->use_preview_window){
958 				if (stream->rendercb==NULL){
959 					stream->output2=ms_factory_create_filter_from_name(stream->ms.factory, stream->display_name);
960 				}
961 			}
962 			configure_video_source(stream);
963 		}
964 
965 		/* and then connect all */
966 		ms_connection_helper_start(&ch);
967 		ms_connection_helper_link(&ch, stream->source, -1, 0);
968 		if (stream->pixconv) {
969 			ms_connection_helper_link(&ch, stream->pixconv, 0, 0);
970 		}
971 		if (stream->tee) {
972 			ms_connection_helper_link(&ch, stream->tee, 0, 0);
973 		}
974 		if (stream->sizeconv) {
975 			ms_connection_helper_link(&ch, stream->sizeconv, 0, 0);
976 		}
977 		if ((stream->source_performs_encoding == FALSE) && !rtp_source) {
978 			ms_connection_helper_link(&ch, stream->ms.encoder, 0, 0);
979 		}
980 		ms_connection_helper_link(&ch, stream->ms.rtpsend, 0, -1);
981 		if (stream->output2){
982 			if (stream->preview_window_id!=0){
983 				ms_filter_call_method(stream->output2, MS_VIDEO_DISPLAY_SET_NATIVE_WINDOW_ID,&stream->preview_window_id);
984 			}
985 			ms_filter_link(stream->tee,1,stream->output2,0);
986 		}
987 		if (stream->local_jpegwriter){
988 			ms_filter_link(stream->tee,2,stream->local_jpegwriter,0);
989 		}
990 	}
991 
992 	/* Plumb the incoming stream */
993 	if (output != NULL) {
994 		rtp_output = (ms_filter_get_id(output) == MS_RTP_SEND_ID) ? TRUE : FALSE;
995 	}
996 	/* Define target upload bandwidth for RTCP packets sending. */
997 	if (pt->normal_bitrate > 0) {
998 		rtp_session_set_target_upload_bandwidth(stream->ms.sessions.rtp_session, pt->normal_bitrate);
999 	}
1000 	if (stream->dir==MediaStreamSendRecv || stream->dir==MediaStreamRecvOnly){
1001 		MSConnectionHelper ch;
1002 
1003 		if (!rtp_output) {
1004 			/* create decoder first */
1005 			stream->ms.decoder=ms_factory_create_decoder(stream->ms.factory, pt->mime_type);
1006 			if (stream->ms.decoder==NULL){
1007 				/* big problem: we don't have a registered decoderfor this payload...*/
1008 				ms_error("videostream.c: No decoder available for payload %i:%s.",payload,pt->mime_type);
1009 				return -1;
1010 			}
1011 		}
1012 
1013 		/* display logic */
1014 		if (stream->rendercb!=NULL){
1015 			/* rendering logic delegated to user suppsourcelied callback */
1016 			stream->output=ms_factory_create_filter(stream->ms.factory, MS_EXT_DISPLAY_ID);
1017 			ms_filter_add_notify_callback(stream->output,ext_display_cb,stream,TRUE);
1018 		}else{
1019 			/* no user supplied callback -> create filter */
1020 			MSVideoDisplayDecodingSupport decoding_support;
1021 
1022 			if ((output == NULL) || (ms_filter_get_id(output) != MS_RTP_SEND_ID)) {
1023 				/* Check if the decoding filter can perform the rendering */
1024 				decoding_support.mime_type = pt->mime_type;
1025 				decoding_support.supported = FALSE;
1026 				ms_filter_call_method(stream->ms.decoder, MS_VIDEO_DECODER_SUPPORT_RENDERING, &decoding_support);
1027 				stream->output_performs_decoding = decoding_support.supported;
1028 			}
1029 
1030 			if (stream->output_performs_decoding) {
1031 				stream->output = stream->ms.decoder;
1032 			} else if (output != NULL) {
1033 				stream->output = output;
1034 				if (rtp_output) {
1035 					stream->ms.decoder = stream->output;
1036 				}
1037 			} else {
1038 				/* Create default display filter */
1039 				stream->output = ms_factory_create_filter_from_name(stream->ms.factory, stream->display_name);
1040 			}
1041 		}
1042 
1043 		/* Don't allow null output */
1044 		if(stream->output == NULL) {
1045 			ms_fatal("No video display filter could be instantiated. Please check build-time configuration");
1046 		}
1047 
1048 
1049 		stream->ms.rtprecv = ms_factory_create_filter( stream->ms.factory, MS_RTP_RECV_ID);
1050 		ms_filter_call_method(stream->ms.rtprecv,MS_RTP_RECV_SET_SESSION,stream->ms.sessions.rtp_session);
1051 
1052 		if (!rtp_output) {
1053 			if (stream->output_performs_decoding == FALSE) {
1054 				stream->jpegwriter=ms_factory_create_filter(stream->ms.factory, MS_JPEG_WRITER_ID);
1055 				if (stream->jpegwriter){
1056 					stream->tee2=ms_factory_create_filter(stream->ms.factory, MS_TEE_ID);
1057 				}
1058 			}
1059 
1060 			/* set parameters to the decoder*/
1061 			if (pt->send_fmtp){
1062 				ms_filter_call_method(stream->ms.decoder,MS_FILTER_ADD_FMTP,pt->send_fmtp);
1063 			}
1064 			if (pt->recv_fmtp!=NULL)
1065 				ms_filter_call_method(stream->ms.decoder,MS_FILTER_ADD_FMTP,(void*)pt->recv_fmtp);
1066 			configure_decoder(stream,pt);
1067 
1068 			if (stream->output_performs_decoding) {
1069 				format = mime_type_to_pix_format(pt->mime_type);
1070 			} else {
1071 				/*force the decoder to output YUV420P */
1072 				format = MS_YUV420P;
1073 			}
1074 			ms_filter_call_method(stream->ms.decoder, MS_FILTER_SET_PIX_FMT, &format);
1075 
1076 			/*configure the display window */
1077 			if(stream->output != NULL) {
1078 				int autofit = 1;
1079 				disp_size.width=MS_VIDEO_SIZE_CIF_W;
1080 				disp_size.height=MS_VIDEO_SIZE_CIF_H;
1081 				ms_filter_call_method(stream->output,MS_FILTER_SET_VIDEO_SIZE,&disp_size);
1082 
1083 				/* if pixconv is used, force yuv420 */
1084 				if (stream->pixconv || !stream->source)
1085 					ms_filter_call_method(stream->output,MS_FILTER_SET_PIX_FMT,&format);
1086 				/* else, use format from input */
1087 				else {
1088 					MSPixFmt source_format;
1089 					ms_filter_call_method(stream->source,MS_FILTER_GET_PIX_FMT,&source_format);
1090 					ms_filter_call_method(stream->output,MS_FILTER_SET_PIX_FMT,&source_format);
1091 				}
1092 
1093 				ms_filter_call_method(stream->output,MS_VIDEO_DISPLAY_SET_LOCAL_VIEW_MODE,&stream->corner);
1094 				if (stream->window_id!=0){
1095 					autofit = 0;
1096 					ms_filter_call_method(stream->output, MS_VIDEO_DISPLAY_SET_NATIVE_WINDOW_ID,&stream->window_id);
1097 				}
1098 				ms_filter_call_method(stream->output,MS_VIDEO_DISPLAY_ENABLE_AUTOFIT,&autofit);
1099 				if (stream->display_filter_auto_rotate_enabled && ms_filter_has_method(stream->output, MS_VIDEO_DISPLAY_SET_DEVICE_ORIENTATION)) {
1100 					ms_filter_call_method(stream->output,MS_VIDEO_DISPLAY_SET_DEVICE_ORIENTATION,&stream->device_orientation);
1101 				}
1102 			}
1103 		}
1104 
1105 		/* and connect the filters */
1106 		ms_connection_helper_start (&ch);
1107 		ms_connection_helper_link (&ch,stream->ms.rtprecv,-1,0);
1108 		if ((stream->output_performs_decoding == FALSE) && !rtp_output) {
1109 			if (stream->recorder_output){
1110 				ms_connection_helper_link(&ch,stream->tee3,0,0);
1111 				ms_filter_link(stream->tee3,1,stream->recorder_output,0);
1112 				configure_recorder_output(stream);
1113 			}
1114 			ms_connection_helper_link(&ch,stream->ms.decoder,0,0);
1115 		}
1116 		if (stream->tee2){
1117 			ms_connection_helper_link (&ch,stream->tee2,0,0);
1118 			ms_filter_link(stream->tee2,1,stream->jpegwriter,0);
1119 		}
1120 		if (stream->output!=NULL)
1121 			ms_connection_helper_link (&ch,stream->output,0,-1);
1122 		/* the video source must be send for preview , if it exists*/
1123 		if (stream->tee!=NULL && stream->output!=NULL && stream->output2==NULL)
1124 			ms_filter_link(stream->tee,1,stream->output,1);
1125 	}
1126 	if (stream->dir == MediaStreamSendOnly) {
1127 		stream->ms.rtprecv = ms_factory_create_filter(stream->ms.factory, MS_RTP_RECV_ID);
1128 		ms_filter_call_method(stream->ms.rtprecv, MS_RTP_RECV_SET_SESSION, stream->ms.sessions.rtp_session);
1129 		stream->ms.voidsink = ms_factory_create_filter(stream->ms.factory, MS_VOID_SINK_ID);
1130 		ms_filter_link(stream->ms.rtprecv, 0, stream->ms.voidsink, 0);
1131 	}
1132 
1133 	/*start the video recorder if it was opened previously*/
1134 	if (stream->recorder_output && ms_filter_implements_interface(stream->recorder_output, MSFilterRecorderInterface)){
1135 		MSRecorderState state = MSRecorderClosed;
1136 		ms_filter_call_method(stream->recorder_output, MS_RECORDER_GET_STATE, &state);
1137 		if (state == MSRecorderPaused){
1138 			ms_filter_call_method_noarg(stream->recorder_output, MS_RECORDER_START);
1139 		}
1140 	}
1141 
1142 	/* create the ticker */
1143 	if (stream->ms.sessions.ticker==NULL) media_stream_start_ticker(&stream->ms);
1144 
1145 	stream->ms.start_time=ms_time(NULL);
1146 	stream->last_fps_check=(uint64_t)-1;
1147 	stream->ms.is_beginning=TRUE;
1148 
1149 	/* attach the graphs */
1150 	if (stream->source)
1151 		ms_ticker_attach (stream->ms.sessions.ticker, stream->source);
1152 	if (stream->void_source)
1153 		ms_ticker_attach (stream->ms.sessions.ticker, stream->void_source);
1154 	if (stream->ms.rtprecv)
1155 		ms_ticker_attach (stream->ms.sessions.ticker, stream->ms.rtprecv);
1156 
1157 	stream->ms.state=MSStreamStarted;
1158 	return 0;
1159 }
1160 
video_stream_start_with_source(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)1161 int video_stream_start_with_source(VideoStream *stream, RtpProfile *profile, const char *rem_rtp_ip, int rem_rtp_port,
1162 	const char *rem_rtcp_ip, int rem_rtcp_port, int payload, int jitt_comp, MSWebCam* cam, MSFilter* source) {
1163 	return video_stream_start_with_source_and_output(stream, profile, rem_rtp_ip, rem_rtp_port,
1164 		rem_rtcp_ip, rem_rtcp_port, payload, jitt_comp, cam, source, NULL);
1165 }
1166 
video_stream_prepare_video(VideoStream * stream)1167 void video_stream_prepare_video(VideoStream *stream){
1168 	video_stream_unprepare_video(stream);
1169 	stream->ms.rtprecv=ms_factory_create_filter(stream->ms.factory, MS_RTP_RECV_ID);
1170 	rtp_session_set_payload_type(stream->ms.sessions.rtp_session,0);
1171 	rtp_session_enable_rtcp(stream->ms.sessions.rtp_session, FALSE);
1172 	ms_filter_call_method(stream->ms.rtprecv,MS_RTP_RECV_SET_SESSION,stream->ms.sessions.rtp_session);
1173 	stream->ms.voidsink=ms_factory_create_filter(stream->ms.factory, MS_VOID_SINK_ID);
1174 	ms_filter_link(stream->ms.rtprecv,0,stream->ms.voidsink,0);
1175 	media_stream_start_ticker(&stream->ms);
1176 	ms_ticker_attach(stream->ms.sessions.ticker,stream->ms.rtprecv);
1177 	stream->ms.state=MSStreamPreparing;
1178 }
1179 
video_stream_unprepare_video(VideoStream * stream)1180 void video_stream_unprepare_video(VideoStream *stream){
1181 	if (stream->ms.state==MSStreamPreparing) {
1182 		stop_preload_graph(stream);
1183 		stream->ms.state=MSStreamInitialized;
1184 	}
1185 }
1186 
video_stream_get_source_filter(const VideoStream * stream)1187 MSFilter* video_stream_get_source_filter(const VideoStream* stream) {
1188 	if( stream ){
1189 		return stream->source;
1190 	} else {
1191 		return NULL;
1192 	}
1193 }
1194 
video_stream_update_video_params(VideoStream * stream)1195 void video_stream_update_video_params(VideoStream *stream){
1196 	/*calling video_stream_change_camera() does the job of unplumbing/replumbing and configuring the new graph*/
1197 	video_stream_change_camera(stream,stream->cam);
1198 }
1199 
1200 /**
1201  * Will update the source camera for the videostream passed as argument.
1202  * The parameters:
1203  * - stream : the stream for which to update the source
1204  * - cam : the camera which should now be considered as the new source.
1205  * - new_source (optional): if passed as non-NULL, it is expected that this filter comes from the specified camera.
1206  *							In this case we don't create the source and use this one instead.
1207  *                          This allows you to reuse the camera and keep it alive when not needed, for fast on/off operations
1208  * - sink : when this filter is not NULL, it represents the AVPlayer video ITC source, which allows inter-graph communication.
1209  * - keep_old_source: when TRUE, will not destroy the previous stream source and return it for later usage.
1210  *
1211  * @return NULL if keep_old_source is FALSE, or the previous source filter if keep_old_source is TRUE
1212  */
_video_stream_change_camera(VideoStream * stream,MSWebCam * cam,MSFilter * new_source,MSFilter * sink,bool_t keep_old_source,bool_t skip_payload_config)1213 static MSFilter* _video_stream_change_camera(VideoStream *stream, MSWebCam *cam, MSFilter* new_source, MSFilter *sink, bool_t keep_old_source, bool_t skip_payload_config){
1214 	MSFilter* old_source = NULL;
1215 	bool_t new_src_different = (new_source && new_source != stream->source);
1216 	bool_t use_player        = (sink && !stream->player_active) || (!sink && stream->player_active);
1217 	bool_t change_source       = ( cam!=stream->cam || new_src_different || use_player);
1218 	bool_t encoder_has_builtin_converter = (!stream->pixconv && !stream->sizeconv);
1219 
1220 	if (stream->ms.sessions.ticker && stream->source){
1221 		ms_ticker_detach(stream->ms.sessions.ticker,stream->source);
1222 		/*unlink source filters and subsequent post processing filters */
1223 		if (encoder_has_builtin_converter || (stream->source_performs_encoding == TRUE)) {
1224 			ms_filter_unlink(stream->source, 0, stream->tee, 0);
1225 		} else {
1226 			ms_filter_unlink (stream->source, 0, stream->pixconv, 0);
1227 			ms_filter_unlink (stream->pixconv, 0, stream->tee, 0);
1228 			ms_filter_unlink (stream->tee, 0, stream->sizeconv, 0);
1229 			if (stream->source_performs_encoding == FALSE) {
1230 				ms_filter_unlink(stream->sizeconv, 0, stream->ms.encoder, 0);
1231 			} else {
1232 				ms_filter_unlink(stream->sizeconv, 0, stream->ms.rtpsend, 0);
1233 			}
1234 		}
1235 		/*destroy the filters */
1236 		if (change_source) {
1237 			if (!keep_old_source){
1238 				ms_filter_destroy(stream->source);
1239 			} else {
1240 				old_source = stream->source;
1241 			}
1242 		}
1243 
1244 		if (!encoder_has_builtin_converter && (stream->source_performs_encoding == FALSE)) {
1245 			ms_filter_destroy(stream->pixconv);
1246 			ms_filter_destroy(stream->sizeconv);
1247 		}
1248 
1249 		/*re create new ones and configure them*/
1250 		if (change_source) {
1251 			if (sink){
1252 				stream->source = ms_factory_create_filter(stream->ms.factory, MS_ITC_SOURCE_ID);
1253 				ms_filter_call_method(sink,MS_ITC_SINK_CONNECT,stream->source);
1254 				stream->player_active = TRUE;
1255 			} else {
1256 				stream->source = new_source ? new_source : ms_web_cam_create_reader(cam);
1257 				stream->cam = cam;
1258 				stream->player_active = FALSE;
1259 			}
1260 		}
1261 		if (stream->source_performs_encoding == TRUE) {
1262 			stream->ms.encoder = stream->source;	/* Consider the encoder is the source */
1263 		}
1264 
1265 		/* update orientation for video output*/
1266 		if (stream->output && stream->display_filter_auto_rotate_enabled && ms_filter_has_method(stream->output, MS_VIDEO_DISPLAY_SET_DEVICE_ORIENTATION)) {
1267 			ms_filter_call_method(stream->output,MS_VIDEO_DISPLAY_SET_DEVICE_ORIENTATION,&stream->device_orientation);
1268 		}
1269 
1270 		if (!skip_payload_config) {
1271 			PayloadType *pt;
1272 			RtpProfile *profile;
1273 			int payload;
1274 
1275 			/* Apply bitrate limit to increase video size if the preferred one has changed. */
1276 			profile = rtp_session_get_profile(stream->ms.sessions.rtp_session);
1277 			payload = rtp_session_get_send_payload_type(stream->ms.sessions.rtp_session);
1278 			pt = rtp_profile_get_payload(profile, payload);
1279 			if (stream->source_performs_encoding == TRUE) {
1280 				MSPixFmt format = mime_type_to_pix_format(pt->mime_type);
1281 				ms_filter_call_method(stream->source, MS_FILTER_SET_PIX_FMT, &format);
1282 			}
1283 			apply_video_preset(stream, pt);
1284 			if (pt->normal_bitrate > 0){
1285 				apply_bitrate_limit(stream ,pt);
1286 			}
1287 		}
1288 
1289 		configure_video_source(stream);
1290 
1291 		if (encoder_has_builtin_converter || (stream->source_performs_encoding == TRUE)) {
1292 			ms_filter_link (stream->source, 0, stream->tee, 0);
1293 		}
1294 		else {
1295 			ms_filter_link (stream->source, 0, stream->pixconv, 0);
1296 			ms_filter_link (stream->pixconv, 0, stream->tee, 0);
1297 			ms_filter_link (stream->tee, 0, stream->sizeconv, 0);
1298 			if (stream->source_performs_encoding == FALSE) {
1299 				ms_filter_link(stream->sizeconv, 0, stream->ms.encoder, 0);
1300 			} else {
1301 				ms_filter_link(stream->sizeconv, 0, stream->ms.rtpsend, 0);
1302 			}
1303 		}
1304 
1305 		ms_ticker_attach(stream->ms.sessions.ticker,stream->source);
1306 	}
1307 	return old_source;
1308 }
1309 
video_stream_change_camera(VideoStream * stream,MSWebCam * cam)1310 void video_stream_change_camera(VideoStream *stream, MSWebCam *cam){
1311 	_video_stream_change_camera(stream, cam, NULL, NULL, FALSE, FALSE);
1312 }
1313 
video_stream_change_camera_keep_previous_source(VideoStream * stream,MSWebCam * cam)1314 MSFilter* video_stream_change_camera_keep_previous_source(VideoStream *stream, MSWebCam *cam){
1315 	return _video_stream_change_camera(stream, cam, NULL, NULL, TRUE, FALSE);
1316 }
1317 
video_stream_change_source_filter(VideoStream * stream,MSWebCam * cam,MSFilter * filter,bool_t keep_previous)1318 MSFilter* video_stream_change_source_filter(VideoStream *stream, MSWebCam* cam, MSFilter* filter, bool_t keep_previous ){
1319 	return _video_stream_change_camera(stream, cam, filter, NULL, keep_previous, FALSE);
1320 }
1321 
video_stream_open_player(VideoStream * stream,MSFilter * sink)1322 void video_stream_open_player(VideoStream *stream, MSFilter *sink){
1323 	ms_message("video_stream_open_player(): sink=%p",sink);
1324 	_video_stream_change_camera(stream, stream->cam, NULL, sink, FALSE, FALSE);
1325 }
1326 
video_stream_close_player(VideoStream * stream)1327 void video_stream_close_player(VideoStream *stream){
1328 	_video_stream_change_camera(stream,stream->cam, NULL, NULL, FALSE, FALSE);
1329 }
1330 
video_stream_recreate_graph(VideoStream * stream)1331 void video_stream_recreate_graph(VideoStream *stream){
1332 	_video_stream_change_camera(stream,stream->cam, NULL, NULL, FALSE, TRUE);
1333 }
1334 
video_stream_send_fir(VideoStream * stream)1335 void video_stream_send_fir(VideoStream *stream) {
1336 	if (stream->ms.sessions.rtp_session != NULL) {
1337 		rtp_session_send_rtcp_fb_fir(stream->ms.sessions.rtp_session);
1338 	}
1339 }
1340 
video_stream_send_pli(VideoStream * stream)1341 void video_stream_send_pli(VideoStream *stream) {
1342 	if (stream->ms.sessions.rtp_session != NULL) {
1343 		rtp_session_send_rtcp_fb_pli(stream->ms.sessions.rtp_session);
1344 	}
1345 }
1346 
video_stream_send_sli(VideoStream * stream,uint16_t first,uint16_t number,uint8_t picture_id)1347 void video_stream_send_sli(VideoStream *stream, uint16_t first, uint16_t number, uint8_t picture_id) {
1348 	if (stream->ms.sessions.rtp_session != NULL) {
1349 		rtp_session_send_rtcp_fb_sli(stream->ms.sessions.rtp_session, first, number, picture_id);
1350 	}
1351 }
1352 
video_stream_send_rpsi(VideoStream * stream,uint8_t * bit_string,uint16_t bit_string_len)1353 void video_stream_send_rpsi(VideoStream *stream, uint8_t *bit_string, uint16_t bit_string_len) {
1354 	if (stream->ms.sessions.rtp_session != NULL) {
1355 		rtp_session_send_rtcp_fb_rpsi(stream->ms.sessions.rtp_session, bit_string, bit_string_len);
1356 	}
1357 }
1358 
video_stream_send_vfu(VideoStream * stream)1359 void video_stream_send_vfu(VideoStream *stream){
1360 	if (stream->ms.encoder)
1361 		ms_filter_call_method_noarg(stream->ms.encoder, MS_VIDEO_ENCODER_REQ_VFU);
1362 }
1363 
_video_stream_stop(VideoStream * stream,bool_t keep_source)1364 static MSFilter* _video_stream_stop(VideoStream * stream, bool_t keep_source)
1365 {
1366 	MSEventQueue *evq;
1367 	MSFilter* source = NULL;
1368 
1369 	stream->eventcb = NULL;
1370 	stream->event_pointer = NULL;
1371 	if (stream->ms.sessions.ticker){
1372 		if (stream->ms.state == MSStreamPreparing) {
1373 			stop_preload_graph(stream);
1374 		} else {
1375 			if (stream->source)
1376 				ms_ticker_detach(stream->ms.sessions.ticker,stream->source);
1377 			if (stream->void_source)
1378 				ms_ticker_detach(stream->ms.sessions.ticker,stream->void_source);
1379 			if (stream->ms.rtprecv)
1380 				ms_ticker_detach(stream->ms.sessions.ticker,stream->ms.rtprecv);
1381 
1382 			if (stream->ms.ice_check_list != NULL) {
1383 				ice_check_list_print_route(stream->ms.ice_check_list, "Video session's route");
1384 				stream->ms.ice_check_list = NULL;
1385 			}
1386 			rtp_stats_display(rtp_session_get_stats(stream->ms.sessions.rtp_session),
1387 				"             VIDEO SESSION'S RTP STATISTICS                ");
1388 
1389 			if (stream->void_source) {
1390 				MSConnectionHelper ch;
1391 				ms_connection_helper_start(&ch);
1392 				ms_connection_helper_unlink(&ch, stream->void_source, -1, 0);
1393 				ms_connection_helper_unlink(&ch, stream->ms.rtpsend, 0, -1);
1394 			}
1395 			if (stream->source){
1396 				MSConnectionHelper ch;
1397 				bool_t rtp_source = (ms_filter_get_id(stream->source) == MS_RTP_RECV_ID) ? TRUE : FALSE;
1398 				ms_connection_helper_start(&ch);
1399 				ms_connection_helper_unlink(&ch, stream->source, -1, 0);
1400 				if (stream->pixconv) {
1401 					ms_connection_helper_unlink(&ch, stream->pixconv, 0, 0);
1402 				}
1403 				if (stream->tee) {
1404 					ms_connection_helper_unlink(&ch, stream->tee, 0, 0);
1405 				}
1406 				if (stream->sizeconv) {
1407 					ms_connection_helper_unlink(&ch, stream->sizeconv, 0, 0);
1408 				}
1409 				if ((stream->source_performs_encoding == FALSE) && !rtp_source) {
1410 					ms_connection_helper_unlink(&ch, stream->ms.encoder, 0, 0);
1411 				}
1412 				ms_connection_helper_unlink(&ch, stream->ms.rtpsend, 0, -1);
1413 				if (stream->output2){
1414 					ms_filter_unlink(stream->tee,1,stream->output2,0);
1415 				}
1416 				if (stream->local_jpegwriter){
1417 					ms_filter_unlink(stream->tee,2,stream->local_jpegwriter,0);
1418 				}
1419 			}
1420 			if (stream->ms.voidsink) {
1421 				ms_filter_unlink(stream->ms.rtprecv, 0, stream->ms.voidsink, 0);
1422 			} else if (stream->ms.rtprecv){
1423 				MSConnectionHelper h;
1424 				bool_t rtp_output = (ms_filter_get_id(stream->output) == MS_RTP_SEND_ID) ? TRUE : FALSE;
1425 				ms_connection_helper_start (&h);
1426 				ms_connection_helper_unlink (&h,stream->ms.rtprecv,-1,0);
1427 				if ((stream->output_performs_decoding == FALSE) && !rtp_output) {
1428 					if (stream->recorder_output){
1429 						ms_connection_helper_unlink(&h,stream->tee3,0,0);
1430 						ms_filter_unlink(stream->tee3,1,stream->recorder_output,0);
1431 					}
1432 					ms_connection_helper_unlink (&h,stream->ms.decoder,0,0);
1433 				}
1434 				if (stream->tee2){
1435 					ms_connection_helper_unlink (&h,stream->tee2,0,0);
1436 					ms_filter_unlink(stream->tee2,1,stream->jpegwriter,0);
1437 				}
1438 				if(stream->output)
1439 					ms_connection_helper_unlink (&h,stream->output,0,-1);
1440 				if (stream->tee && stream->output && stream->output2==NULL)
1441 					ms_filter_unlink(stream->tee,1,stream->output,1);
1442 			}
1443 		}
1444 	}
1445 	rtp_session_set_rtcp_xr_media_callbacks(stream->ms.sessions.rtp_session, NULL);
1446 	rtp_session_signal_disconnect_by_callback(stream->ms.sessions.rtp_session,"payload_type_changed",(RtpCallback)video_stream_payload_type_changed);
1447 
1448 	/*Automatically the video recorder if it was opened previously*/
1449 	if (stream->recorder_output && ms_filter_implements_interface(stream->recorder_output, MSFilterRecorderInterface)){
1450 		MSRecorderState state = MSRecorderClosed;
1451 		ms_filter_call_method(stream->recorder_output, MS_RECORDER_GET_STATE, &state);
1452 		if (state != MSRecorderClosed){
1453 			ms_filter_call_method_noarg(stream->recorder_output, MS_RECORDER_CLOSE);
1454 		}
1455 	}
1456 
1457 	if( keep_source ){
1458 		source = stream->source;
1459 		stream->source = NULL; // will prevent video_stream_free() from destroying the source
1460 	}
1461 	/*before destroying the filters, pump the event queue so that pending events have a chance to reach their listeners.
1462 	 * When the filter are destroyed, all their pending events in the event queue will be cancelled*/
1463 	evq = ms_factory_get_event_queue(stream->ms.factory);
1464 	if (evq) ms_event_queue_pump(evq);
1465 
1466 	video_stream_free(stream);
1467 
1468 	return source;
1469 }
1470 
video_stream_stop(VideoStream * stream)1471 void video_stream_stop( VideoStream* stream ){
1472 	// don't keep the source
1473 	_video_stream_stop(stream, FALSE);
1474 }
1475 
video_stream_stop_keep_source(VideoStream * stream)1476 MSFilter* video_stream_stop_keep_source( VideoStream* stream) {
1477 	return _video_stream_stop(stream, TRUE);
1478 }
1479 
1480 
video_stream_show_video(VideoStream * stream,bool_t show)1481 void video_stream_show_video(VideoStream *stream, bool_t show){
1482 	if (stream->output){
1483 		ms_filter_call_method(stream->output,MS_VIDEO_DISPLAY_SHOW_VIDEO,&show);
1484 	}
1485 }
1486 
1487 
video_stream_get_native_window_id(VideoStream * stream)1488 void * video_stream_get_native_window_id(VideoStream *stream){
1489 	void *id;
1490 	if (stream->output){
1491 		if (ms_filter_call_method(stream->output,MS_VIDEO_DISPLAY_GET_NATIVE_WINDOW_ID,&id)==0)
1492 			return id;
1493 	}
1494 	return stream->window_id;
1495 }
1496 
video_stream_set_native_window_id(VideoStream * stream,void * id)1497 void video_stream_set_native_window_id(VideoStream *stream, void *id){
1498 	stream->window_id=id;
1499 	if (stream->output){
1500 		ms_filter_call_method(stream->output,MS_VIDEO_DISPLAY_SET_NATIVE_WINDOW_ID,&id);
1501 	}
1502 }
1503 
video_stream_set_native_preview_window_id(VideoStream * stream,void * id)1504 void video_stream_set_native_preview_window_id(VideoStream *stream, void *id){
1505 	stream->preview_window_id=id;
1506 #if !TARGET_OS_IPHONE
1507 	if (stream->output2){
1508 		ms_filter_call_method(stream->output2,MS_VIDEO_DISPLAY_SET_NATIVE_WINDOW_ID,&id);
1509 	}
1510 #endif
1511 	if (stream->source){
1512 		ms_filter_call_method(stream->source,MS_VIDEO_DISPLAY_SET_NATIVE_WINDOW_ID,&id);
1513 	}
1514 }
1515 
video_stream_get_native_preview_window_id(VideoStream * stream)1516 void * video_stream_get_native_preview_window_id(VideoStream *stream){
1517 	void *id=0;
1518 	if (stream->output2){
1519 		if (ms_filter_call_method(stream->output2,MS_VIDEO_DISPLAY_GET_NATIVE_WINDOW_ID,&id)==0)
1520 			return id;
1521 	}
1522 	if (stream->source){
1523 		if (ms_filter_has_method(stream->source,MS_VIDEO_DISPLAY_GET_NATIVE_WINDOW_ID)
1524 			&& ms_filter_call_method(stream->source,MS_VIDEO_DISPLAY_GET_NATIVE_WINDOW_ID,&id)==0)
1525 			return id;
1526 	}
1527 	return stream->preview_window_id;
1528 }
1529 
video_stream_use_preview_video_window(VideoStream * stream,bool_t yesno)1530 void video_stream_use_preview_video_window(VideoStream *stream, bool_t yesno){
1531 	stream->use_preview_window=yesno;
1532 }
1533 
video_stream_set_device_rotation(VideoStream * stream,int orientation)1534 void video_stream_set_device_rotation(VideoStream *stream, int orientation){
1535 	stream->device_orientation = orientation;
1536 }
1537 
video_stream_set_freeze_on_error(VideoStream * stream,bool_t yesno)1538 void video_stream_set_freeze_on_error(VideoStream *stream, bool_t yesno) {
1539 	stream->freeze_on_error = yesno;
1540 }
1541 
video_stream_get_camera_sensor_rotation(VideoStream * stream)1542 int video_stream_get_camera_sensor_rotation(VideoStream *stream) {
1543 	int rotation = -1;
1544 	if (stream->source) {
1545 		if (ms_filter_has_method(stream->source, MS_VIDEO_CAPTURE_GET_CAMERA_SENSOR_ROTATION)
1546 			&& ms_filter_call_method(stream->source, MS_VIDEO_CAPTURE_GET_CAMERA_SENSOR_ROTATION, &rotation) == 0)
1547 			return rotation;
1548 	}
1549 	return -1;
1550 }
1551 
video_preview_new(MSFactory * factory)1552 VideoPreview * video_preview_new(MSFactory *factory){
1553 	VideoPreview *stream = (VideoPreview *)ms_new0 (VideoPreview, 1);
1554 	stream->ms.factory = factory;
1555 	MS_VIDEO_SIZE_ASSIGN(stream->sent_vsize, CIF);
1556 	choose_display_name(stream);
1557 	stream->ms.owns_sessions=TRUE;
1558 	return stream;
1559 }
1560 
video_preview_get_current_size(VideoPreview * stream)1561 MSVideoSize video_preview_get_current_size(VideoPreview *stream){
1562 	MSVideoSize ret={0};
1563 	if (stream->source){
1564 		ms_filter_call_method(stream->source,MS_FILTER_GET_VIDEO_SIZE,&ret);
1565 	}
1566 	return ret;
1567 }
1568 
configure_video_preview_source(VideoPreview * stream)1569 static void configure_video_preview_source(VideoPreview *stream) {
1570 	MSPixFmt format;
1571 	MSVideoSize vsize = stream->sent_vsize;
1572 	float fps;
1573 
1574 	if (stream->forced_fps != 0) fps = stream->forced_fps;
1575 	else fps = (float)29.97;
1576 
1577 	/* Transmit orientation to source filter. */
1578 	if (ms_filter_has_method(stream->source, MS_VIDEO_CAPTURE_SET_DEVICE_ORIENTATION))
1579 		ms_filter_call_method(stream->source, MS_VIDEO_CAPTURE_SET_DEVICE_ORIENTATION, &stream->device_orientation);
1580 	/* Initialize the capture device orientation. */
1581 	if (ms_filter_has_method(stream->source, MS_VIDEO_DISPLAY_SET_DEVICE_ORIENTATION)) {
1582 		ms_filter_call_method(stream->source, MS_VIDEO_DISPLAY_SET_DEVICE_ORIENTATION, &stream->device_orientation);
1583 	}
1584 
1585 	ms_filter_call_method(stream->source, MS_FILTER_SET_VIDEO_SIZE, &vsize);
1586 	if (ms_filter_get_id(stream->source) != MS_STATIC_IMAGE_ID) {
1587 		ms_filter_call_method(stream->source, MS_FILTER_SET_FPS, &fps);
1588 	}
1589 	ms_filter_call_method(stream->source, MS_FILTER_GET_VIDEO_SIZE, &vsize);
1590 	ms_filter_call_method(stream->source, MS_FILTER_GET_PIX_FMT, &format);
1591 
1592 	if (format == MS_MJPEG) {
1593 		stream->pixconv = ms_factory_create_filter(stream->ms.factory, MS_MJPEG_DEC_ID);
1594 		if (stream->pixconv == NULL) {
1595 			ms_error("Could not create mjpeg decoder, check your build options.");
1596 		}
1597 	}
1598 	else {
1599 		stream->pixconv = ms_factory_create_filter(stream->ms.factory, MS_PIX_CONV_ID);
1600 		ms_filter_call_method(stream->pixconv, MS_FILTER_SET_PIX_FMT, &format);
1601 		ms_filter_call_method(stream->pixconv, MS_FILTER_SET_VIDEO_SIZE, &vsize);
1602 	}
1603 }
1604 
video_preview_start(VideoPreview * stream,MSWebCam * device)1605 void video_preview_start(VideoPreview *stream, MSWebCam *device) {
1606 	MSPixFmt format = MS_YUV420P; /* Display format */
1607 	int mirroring = 1;
1608 	int corner = -1;
1609 	MSVideoSize disp_size = stream->sent_vsize;
1610 	const char *displaytype = stream->display_name;
1611 	MSConnectionHelper ch;
1612 
1613 	stream->source = ms_web_cam_create_reader(device);
1614 
1615 	/* configure the filters */
1616 	configure_video_preview_source(stream);
1617 
1618 	if (displaytype) {
1619 		stream->output2=ms_factory_create_filter_from_name(stream->ms.factory, displaytype);
1620 		ms_filter_call_method(stream->output2, MS_FILTER_SET_PIX_FMT, &format);
1621 		ms_filter_call_method(stream->output2, MS_FILTER_SET_VIDEO_SIZE, &disp_size);
1622 		ms_filter_call_method(stream->output2, MS_VIDEO_DISPLAY_ENABLE_MIRRORING, &mirroring);
1623 		ms_filter_call_method(stream->output2, MS_VIDEO_DISPLAY_SET_LOCAL_VIEW_MODE, &corner);
1624 		/* and then connect all */
1625 	}
1626 
1627 	stream->local_jpegwriter = ms_factory_create_filter(stream->ms.factory, MS_JPEG_WRITER_ID);
1628 	if (stream->local_jpegwriter) {
1629 		stream->tee = ms_factory_create_filter(stream->ms.factory, MS_TEE_ID);
1630 	}
1631 
1632 	ms_connection_helper_start(&ch);
1633 	ms_connection_helper_link(&ch, stream->source, -1, 0);
1634 	if (stream->pixconv) {
1635 		ms_connection_helper_link(&ch, stream->pixconv, 0, 0);
1636 	}
1637 
1638 	if (stream->output2) {
1639 		if (stream->preview_window_id != 0) {
1640 			video_stream_set_native_preview_window_id(stream, stream->preview_window_id);
1641 		}
1642 	}
1643 
1644 	if (stream->tee) {
1645 		ms_connection_helper_link(&ch, stream->tee, 0, 0);
1646 		ms_filter_link(stream->tee, 1, stream->output2, 0);
1647 		ms_filter_link(stream->tee, 2, stream->local_jpegwriter, 0);
1648 	} else {
1649 		ms_filter_link(stream->pixconv, 0, stream->output2, 0);
1650 	}
1651 
1652 	/* create the ticker */
1653 	stream->ms.sessions.ticker = ms_ticker_new();
1654 	ms_ticker_set_name(stream->ms.sessions.ticker, "Video MSTicker");
1655 	ms_ticker_attach (stream->ms.sessions.ticker, stream->source);
1656 	stream->ms.state = MSStreamStarted;
1657 }
1658 
_video_preview_stop(VideoPreview * stream,bool_t keep_source)1659 static MSFilter* _video_preview_stop( VideoPreview* stream, bool_t keep_source) {
1660 	MSFilter* source = NULL;
1661 	MSConnectionHelper ch;
1662 	ms_ticker_detach(stream->ms.sessions.ticker, stream->source);
1663 
1664 	ms_connection_helper_start(&ch);
1665 	ms_connection_helper_unlink(&ch, stream->source, -1, 0);
1666 	if (stream->pixconv) {
1667 		ms_connection_helper_unlink(&ch, stream->pixconv, 0, 0);
1668 	}
1669 	if (stream->tee) {
1670 		ms_connection_helper_unlink(&ch, stream->tee, 0, 0);
1671 		if (stream->output2) {
1672 			ms_filter_unlink(stream->tee, 1, stream->output2, 0);
1673 		}
1674 		if (stream->local_jpegwriter) {
1675 			ms_filter_unlink(stream->tee, 2, stream->local_jpegwriter, 0);
1676 		}
1677 	} else {
1678 		ms_connection_helper_unlink(&ch, stream->output2, 0, 0);
1679 	}
1680 
1681 	if (keep_source) {
1682 		source = stream->source;
1683 		ms_message("video_preview_stop: keeping source %p", source);
1684 		stream->source = NULL; // prevent destroy of the source
1685 	}
1686 	video_stream_free(stream);
1687 	return source;
1688 }
1689 
video_preview_stop(VideoPreview * stream)1690 void video_preview_stop(VideoPreview *stream){
1691 	_video_preview_stop(stream, FALSE);
1692 }
1693 
video_preview_stop_reuse_source(VideoPreview * stream)1694 MSFilter* video_preview_stop_reuse_source(VideoPreview *stream){
1695 	return _video_preview_stop(stream, TRUE);
1696 }
1697 
_video_preview_change_camera(VideoPreview * stream,MSWebCam * cam,MSFilter * new_source,bool_t keep_old_source)1698 static MSFilter* _video_preview_change_camera(VideoPreview *stream, MSWebCam *cam, MSFilter* new_source, bool_t keep_old_source) {
1699 	MSFilter *cur_filter;
1700 	MSFilter* old_source = NULL;
1701 	bool_t new_src_different = (new_source && new_source != stream->source);
1702 	bool_t change_source = (cam != stream->cam || new_src_different);
1703 	MSVideoSize disp_size = stream->sent_vsize;
1704 
1705 	if (stream->ms.sessions.ticker && stream->source) {
1706 		ms_ticker_detach(stream->ms.sessions.ticker, stream->source);
1707 		/*unlink source filters and subsequent post processing filters */
1708 		if (stream->pixconv) {
1709 			ms_filter_unlink(stream->source, 0, stream->pixconv, 0);
1710 			cur_filter = stream->pixconv;
1711 		} else {
1712 			cur_filter = stream->source;
1713 		}
1714 		if (stream->tee) {
1715 			ms_filter_unlink(cur_filter, 0, stream->tee, 0);
1716 			if (stream->output2) {
1717 				ms_filter_unlink(stream->tee, 1, stream->output2, 0);
1718 			}
1719 			if (stream->local_jpegwriter) {
1720 				ms_filter_unlink(stream->tee, 2, stream->local_jpegwriter, 0);
1721 			}
1722 		} else {
1723 			ms_filter_unlink(cur_filter, 0, stream->output2, 0);
1724 		}
1725 
1726 		/*destroy the filters */
1727 		if (change_source) {
1728 			if (!keep_old_source) {
1729 				ms_filter_destroy(stream->source);
1730 			}
1731 			else {
1732 				old_source = stream->source;
1733 			}
1734 		}
1735 
1736 		if (stream->pixconv) {
1737 			ms_filter_destroy(stream->pixconv);
1738 		}
1739 
1740 		/*re create new ones and configure them*/
1741 		if (change_source) {
1742 			stream->source = new_source ? new_source : ms_web_cam_create_reader(cam);
1743 			stream->cam = cam;
1744 			stream->player_active = FALSE;
1745 		}
1746 
1747 		configure_video_preview_source(stream);
1748 		ms_filter_call_method(stream->output2, MS_FILTER_SET_VIDEO_SIZE, &disp_size);
1749 
1750 		if (stream->pixconv) {
1751 			ms_filter_link(stream->source, 0, stream->pixconv, 0);
1752 			cur_filter = stream->pixconv;
1753 		} else {
1754 			cur_filter = stream->source;
1755 		}
1756 		if (stream->tee) {
1757 			ms_filter_link(cur_filter, 0, stream->tee, 0);
1758 			if (stream->output2) {
1759 				ms_filter_link(stream->tee, 1, stream->output2, 0);
1760 			}
1761 			if (stream->local_jpegwriter) {
1762 				ms_filter_link(stream->tee, 2, stream->local_jpegwriter, 0);
1763 			}
1764 		}
1765 		else {
1766 			ms_filter_link(cur_filter, 0, stream->output2, 0);
1767 		}
1768 
1769 		ms_ticker_attach(stream->ms.sessions.ticker, stream->source);
1770 	}
1771 	return old_source;
1772 }
1773 
video_preview_change_camera(VideoPreview * stream,MSWebCam * cam)1774 void video_preview_change_camera(VideoPreview *stream, MSWebCam *cam) {
1775 	_video_preview_change_camera(stream, cam, NULL, FALSE);
1776 }
1777 
video_preview_update_video_params(VideoPreview * stream)1778 void video_preview_update_video_params(VideoPreview *stream) {
1779 	/* Calling video_preview_change_camera() does the job of unplumbing/replumbing and configuring the new graph */
1780 	video_preview_change_camera(stream, stream->cam);
1781 }
1782 
1783 
video_stream_recv_only_start(VideoStream * videostream,RtpProfile * profile,const char * addr,int port,int used_pt,int jitt_comp)1784 int video_stream_recv_only_start(VideoStream *videostream, RtpProfile *profile, const char *addr, int port, int used_pt, int jitt_comp){
1785 	video_stream_set_direction(videostream, MediaStreamRecvOnly);
1786 	return video_stream_start(videostream,profile,addr,port,addr,port+1,used_pt,jitt_comp,NULL);
1787 }
1788 
video_stream_send_only_start(VideoStream * videostream,RtpProfile * profile,const char * addr,int port,int rtcp_port,int used_pt,int jitt_comp,MSWebCam * device)1789 int video_stream_send_only_start(VideoStream *videostream,
1790 				RtpProfile *profile, const char *addr, int port, int rtcp_port,
1791 				int used_pt, int  jitt_comp, MSWebCam *device){
1792 	video_stream_set_direction (videostream, MediaStreamSendOnly);
1793 	return video_stream_start(videostream,profile,addr,port,addr,rtcp_port,used_pt,jitt_comp,device);
1794 }
1795 
1796 
video_stream_recv_only_stop(VideoStream * vs)1797 void video_stream_recv_only_stop(VideoStream *vs){
1798 	video_stream_stop(vs);
1799 }
1800 
video_stream_send_only_stop(VideoStream * vs)1801 void video_stream_send_only_stop(VideoStream *vs){
1802 	video_stream_stop(vs);
1803 }
1804 
1805 /* enable ZRTP on the video stream using information from the audio stream */
video_stream_enable_zrtp(VideoStream * vstream,AudioStream * astream)1806 void video_stream_enable_zrtp(VideoStream *vstream, AudioStream *astream){
1807 	if (astream->ms.sessions.zrtp_context != NULL && vstream->ms.sessions.zrtp_context == NULL) {
1808 		vstream->ms.sessions.zrtp_context=ms_zrtp_multistream_new(&(vstream->ms.sessions), astream->ms.sessions.zrtp_context);
1809 	} else if (vstream->ms.sessions.zrtp_context && !media_stream_secured(&vstream->ms))
1810 		ms_zrtp_reset_transmition_timer(vstream->ms.sessions.zrtp_context);
1811 }
1812 
video_stream_start_zrtp(VideoStream * stream)1813 void video_stream_start_zrtp(VideoStream *stream) {
1814 	if (stream->ms.sessions.zrtp_context!=NULL) {
1815 		if (ms_zrtp_channel_start(stream->ms.sessions.zrtp_context) == MSZRTP_ERROR_CHANNEL_ALREADY_STARTED) {
1816 			ms_zrtp_reset_transmition_timer(stream->ms.sessions.zrtp_context);
1817 		}
1818 	} else {
1819 		ms_warning("Trying to start a ZRTP channel on videotream, but none was enabled");
1820 	}
1821 }
1822 
video_stream_enable_display_filter_auto_rotate(VideoStream * stream,bool_t enable)1823 void video_stream_enable_display_filter_auto_rotate(VideoStream* stream, bool_t enable) {
1824 	stream->display_filter_auto_rotate_enabled = enable;
1825 }
1826 
video_stream_is_decoding_error_to_be_reported(VideoStream * stream,uint32_t ms)1827 bool_t video_stream_is_decoding_error_to_be_reported(VideoStream *stream, uint32_t ms) {
1828 	if (((stream->ms.sessions.ticker->time - stream->last_reported_decoding_error_time) > ms) || (stream->last_reported_decoding_error_time == 0))
1829 		return TRUE;
1830 	else
1831 		return FALSE;
1832 }
1833 
video_stream_decoding_error_reported(VideoStream * stream)1834 void video_stream_decoding_error_reported(VideoStream *stream) {
1835 	stream->last_reported_decoding_error_time = stream->ms.sessions.ticker->time;
1836 }
1837 
video_stream_decoding_error_recovered(VideoStream * stream)1838 void video_stream_decoding_error_recovered(VideoStream *stream) {
1839 	stream->last_reported_decoding_error_time = 0;
1840 }
video_stream_get_camera(const VideoStream * stream)1841 const MSWebCam * video_stream_get_camera(const VideoStream *stream) {
1842 	return stream->cam;
1843 }
1844 
video_stream_use_video_preset(VideoStream * stream,const char * preset)1845 void video_stream_use_video_preset(VideoStream *stream, const char *preset) {
1846 	if (stream->preset != NULL) ms_free(stream->preset);
1847 	stream->preset = ms_strdup(preset);
1848 }
1849 
video_stream_open_remote_play(VideoStream * stream,const char * filename)1850 MSFilter * video_stream_open_remote_play(VideoStream *stream, const char *filename){
1851 	MSFilter *source = stream->source;
1852 
1853 	if (!source || !ms_filter_implements_interface(source, MSFilterPlayerInterface)){
1854 		ms_error("video_stream_open_remote_play(): the stream is not using a player.");
1855 		return NULL;
1856 	}
1857 	video_stream_close_remote_play(stream);
1858 	if (ms_filter_call_method(source, MS_PLAYER_OPEN, (void*)filename)!=0){
1859 		return NULL;
1860 	}
1861 	return source;
1862 }
1863 
video_stream_close_remote_play(VideoStream * stream)1864 void video_stream_close_remote_play(VideoStream *stream){
1865 	MSPlayerState state = MSPlayerClosed;
1866 	MSFilter *source = stream->source;
1867 
1868 	if (!source) return;
1869 
1870 	ms_filter_call_method(source, MS_PLAYER_GET_STATE, &state);
1871 	if (state != MSPlayerClosed){
1872 		ms_filter_call_method_noarg(source, MS_PLAYER_CLOSE);
1873 	}
1874 }
1875 
video_stream_open_remote_record(VideoStream * stream,const char * filename)1876 MSFilter *video_stream_open_remote_record(VideoStream *stream, const char *filename){
1877 	MSFilter *recorder = stream->recorder_output;
1878 	if (!recorder || !ms_filter_implements_interface(recorder, MSFilterRecorderInterface)){
1879 		ms_error("video_stream_open_remote_play(): the stream is not using a recorder.");
1880 		return NULL;
1881 	}
1882 	if (ms_filter_call_method(recorder, MS_RECORDER_OPEN, (void*)filename)!=0){
1883 		return NULL;
1884 	}
1885 	return recorder;
1886 }
1887 
video_stream_close_remote_record(VideoStream * stream)1888 void video_stream_close_remote_record(VideoStream *stream){
1889 	MSFilter *recorder = stream->recorder_output;
1890 	MSRecorderState state = MSRecorderClosed;
1891 
1892 	if (!recorder || !ms_filter_implements_interface(recorder, MSFilterRecorderInterface)){
1893 		ms_error("video_stream_close_remote_record(): the stream is not using a recorder.");
1894 		return ;
1895 	}
1896 
1897 	ms_filter_call_method(recorder, MS_RECORDER_GET_STATE, &state);
1898 	if (state != MSRecorderClosed){
1899 		ms_filter_call_method_noarg(recorder, MS_RECORDER_CLOSE);
1900 	}
1901 }
1902