1 /*
2 mediastreamer2 library - modular sound and video processing and streaming
3 Copyright (C) 2014  Belledonne Communications SARL
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 #ifdef HAVE_CONFIG_H
21 #include "mediastreamer-config.h"
22 #include "gitversion.h"
23 #endif
24 #   ifndef MEDIASTREAMER_VERSION
25 #   define MEDIASTREAMER_VERSION "unknown"
26 #   endif
27 #	ifndef MS2_GIT_VERSION
28 #	define MS2_GIT_VERSION "unknown"
29 #	endif
30 
31 #include "mediastreamer2/msfilter.h"
32 #include "mediastreamer2/mseventqueue.h"
33 #include "basedescs.h"
34 
35 #if !defined(_WIN32_WCE)
36 #include <sys/types.h>
37 #endif
38 #ifndef _WIN32
39 #include <dirent.h>
40 #else
41 #ifndef PACKAGE_PLUGINS_DIR
42 #if defined(_WIN32) || defined(_WIN32_WCE)
43 #ifdef MS2_WINDOWS_DESKTOP
44 #define PACKAGE_PLUGINS_DIR "lib\\mediastreamer\\plugins\\"
45 #else
46 #define PACKAGE_PLUGINS_DIR "."
47 #endif
48 #else
49 #define PACKAGE_PLUGINS_DIR "."
50 #endif
51 #endif
52 #endif
53 
54 #ifndef PACKAGE_DATA_DIR
55 #define PACKAGE_DATA_DIR "share"
56 #endif
57 
58 #ifdef HAVE_DLOPEN
59 #include <dlfcn.h>
60 #endif
61 
62 #ifdef __APPLE__
63    #include "TargetConditionals.h"
64 #endif
65 
66 #ifdef __QNX__
67 #include <sys/syspage.h>
68 #endif
69 
70 
71 MS2_DEPRECATED static MSFactory *fallback_factory=NULL;
72 
73 static void ms_fmt_descriptor_destroy(MSFmtDescriptor *obj);
74 
75 #define DEFAULT_MAX_PAYLOAD_SIZE 1440
76 
ms_factory_get_payload_max_size(MSFactory * factory)77 int ms_factory_get_payload_max_size(MSFactory *factory){
78 	return factory->max_payload_size;
79 }
80 
ms_factory_set_payload_max_size(MSFactory * obj,int size)81 void ms_factory_set_payload_max_size(MSFactory *obj, int size){
82 	if (size<=0) size=DEFAULT_MAX_PAYLOAD_SIZE;
83 	obj->max_payload_size=size;
84 }
85 
86 #define MS_MTU_DEFAULT 1500
87 
ms_factory_set_mtu(MSFactory * obj,int mtu)88 void ms_factory_set_mtu(MSFactory *obj, int mtu){
89 	/*60= IPv6+UDP+RTP overhead */
90 	if (mtu>60){
91 		obj->mtu=mtu;
92 		ms_factory_set_payload_max_size(obj,mtu-60);
93 	}else {
94 		if (mtu>0){
95 			ms_warning("MTU is too short: %i bytes, using default value instead.",mtu);
96 		}
97 		ms_factory_set_mtu(obj,MS_MTU_DEFAULT);
98 	}
99 }
100 
ms_factory_get_mtu(MSFactory * obj)101 int ms_factory_get_mtu(MSFactory *obj){
102 	return obj->mtu;
103 }
104 
ms_factory_set_echo_canceller_filter_name(MSFactory * obj,const char * filtername)105 void ms_factory_set_echo_canceller_filter_name(MSFactory *obj, const char *filtername) {
106 	if (obj->echo_canceller_filtername != NULL) ms_free(obj->echo_canceller_filtername);
107 	obj->echo_canceller_filtername = ms_strdup(filtername);
108 }
109 
ms_factory_get_echo_canceller_filter_name(const MSFactory * obj)110 const char * ms_factory_get_echo_canceller_filter_name(const MSFactory *obj) {
111 	return obj->echo_canceller_filtername;
112 }
113 
ms_factory_get_cpu_count(MSFactory * obj)114 unsigned int ms_factory_get_cpu_count(MSFactory *obj) {
115 	return obj->cpu_count;
116 }
117 
ms_factory_set_cpu_count(MSFactory * obj,unsigned int c)118 void ms_factory_set_cpu_count(MSFactory *obj, unsigned int c) {
119 	ms_message("CPU count set to %d", c);
120 	obj->cpu_count = c;
121 }
122 
ms_factory_add_platform_tag(MSFactory * obj,const char * tag)123 void ms_factory_add_platform_tag(MSFactory *obj, const char *tag) {
124 	if ((tag == NULL) || (tag[0] == '\0')) return;
125 	if (bctbx_list_find_custom(obj->platform_tags, (bctbx_compare_func)strcasecmp, tag) == NULL) {
126 		obj->platform_tags = bctbx_list_append(obj->platform_tags, ms_strdup(tag));
127 	}
128 }
129 
ms_factory_get_platform_tags(MSFactory * obj)130 bctbx_list_t * ms_factory_get_platform_tags(MSFactory *obj) {
131 	return obj->platform_tags;
132 }
133 
ms_factory_get_platform_tags_as_string(MSFactory * obj)134 char * ms_factory_get_platform_tags_as_string(MSFactory *obj) {
135 	return ms_tags_list_as_string(obj->platform_tags);
136 }
137 
ms_factory_get_video_presets_manager(MSFactory * factory)138 struct _MSVideoPresetsManager * ms_factory_get_video_presets_manager(MSFactory *factory) {
139 	return factory->video_presets_manager;
140 }
141 
compare_stats_with_name(const MSFilterStats * stat,const char * name)142 static int compare_stats_with_name(const MSFilterStats *stat, const char *name){
143 	return strcmp(stat->name,name);
144 }
145 
find_or_create_stats(MSFactory * factory,MSFilterDesc * desc)146 static MSFilterStats *find_or_create_stats(MSFactory *factory, MSFilterDesc *desc){
147 	bctbx_list_t *elem=bctbx_list_find_custom(factory->stats_list,(bctbx_compare_func)compare_stats_with_name,desc->name);
148 	MSFilterStats *ret=NULL;
149 	if (elem==NULL){
150 		ret=ms_new0(MSFilterStats,1);
151 		ret->name=desc->name;
152 		factory->stats_list=bctbx_list_append(factory->stats_list,ret);
153 	}else ret=(MSFilterStats*)elem->data;
154 	return ret;
155 }
156 
ms_factory_init(MSFactory * obj)157 void ms_factory_init(MSFactory *obj){
158 	int i;
159 	long num_cpu=1;
160 	char *debug_log_enabled = NULL;
161 	char *tags;
162 #ifdef _WIN32
163 	SYSTEM_INFO sysinfo;
164 #endif
165 
166 #if defined(ENABLE_NLS)
167 	bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
168 #endif
169 #ifndef MS2_WINDOWS_UNIVERSAL
170 	debug_log_enabled=getenv("MEDIASTREAMER_DEBUG");
171 #endif
172 	if (debug_log_enabled!=NULL && (strcmp("1",debug_log_enabled)==0) ){
173 		ortp_set_log_level_mask(ORTP_LOG_DOMAIN, ORTP_MESSAGE|ORTP_WARNING|ORTP_ERROR|ORTP_FATAL);
174 	}
175 
176 	ms_message("Mediastreamer2 factory " MEDIASTREAMER_VERSION " (git: " MS2_GIT_VERSION ") initialized.");
177 	/* register builtin MSFilter's */
178 	for (i=0;ms_base_filter_descs[i]!=NULL;i++){
179 		ms_factory_register_filter(obj,ms_base_filter_descs[i]);
180 	}
181 
182 #ifdef _WIN32 /*fixme to be tested*/
183 	GetNativeSystemInfo( &sysinfo );
184 
185 	num_cpu = sysinfo.dwNumberOfProcessors;
186 #elif __APPLE__ || __linux || __DragonFly__ || __FreeBSD__
187 	num_cpu = sysconf( _SC_NPROCESSORS_CONF); /*check the number of processors configured, not just the one that are currently active.*/
188 #elif __QNX__
189 	num_cpu = _syspage_ptr->num_cpu;
190 #else
191 #warning "There is no code that detects the number of CPU for this platform."
192 #endif
193 	ms_factory_set_cpu_count(obj,num_cpu);
194 	ms_factory_set_mtu(obj,MS_MTU_DEFAULT);
195 #ifdef _WIN32
196 	ms_factory_add_platform_tag(obj, "win32");
197 #ifdef MS2_WINDOWS_PHONE
198 	ms_factory_add_platform_tag(obj, "windowsphone");
199 #endif
200 #ifdef MS2_WINDOWS_UNIVERSAL
201 	ms_factory_add_platform_tag(obj, "windowsuniversal");
202 #endif
203 #endif
204 #ifdef __APPLE__
205 	ms_factory_add_platform_tag(obj, "apple");
206 #endif
207 #ifdef __linux
208 	ms_factory_add_platform_tag(obj, "linux");
209 #endif
210 #ifdef __QNX__
211 	ms_factory_add_platform_tag(obj, "qnx");
212 #endif
213 #ifdef __ANDROID__
214 	ms_factory_add_platform_tag(obj, "android");
215 #endif
216 #ifdef TARGET_OS_IPHONE
217 	ms_factory_add_platform_tag(obj, "ios");
218 #endif
219 #if defined(__arm__) || defined(_M_ARM)
220 	ms_factory_add_platform_tag(obj, "arm");
221 #else
222 	ms_factory_add_platform_tag(obj, "x86");
223 #endif
224 #if defined(__ANDROID__) || (TARGET_OS_IPHONE == 1) || defined(__arm__) || defined(_M_ARM)
225 	ms_factory_add_platform_tag(obj, "embedded");
226 #else
227 	ms_factory_add_platform_tag(obj, "desktop");
228 #endif
229 	tags = ms_factory_get_platform_tags_as_string(obj);
230 	ms_message("ms_factory_init() done: platform_tags=%s", tags);
231 	ms_free(tags);
232 	obj->echo_canceller_filtername = ms_strdup("MSWebRTCAECM");
233 	obj->image_resources_dir = bctbx_strdup_printf("%s/images", PACKAGE_DATA_DIR);
234 }
235 
236 
237 
ms_factory_new(void)238 MSFactory *ms_factory_new(void){
239 	MSFactory *obj=ms_new0(MSFactory,1);
240 	ms_factory_init(obj);
241 	return obj;
242 }
243 
244 
ms_factory_register_filter(MSFactory * factory,MSFilterDesc * desc)245 void ms_factory_register_filter(MSFactory* factory, MSFilterDesc* desc ) {
246 	if (desc->id==MS_FILTER_NOT_SET_ID){
247 		ms_fatal("MSFilterId for %s not set !",desc->name);
248 	}
249 	desc->flags|=MS_FILTER_IS_ENABLED; /*by default a registered filter is enabled*/
250 
251 	/*lastly registered encoder/decoders may replace older ones*/
252 	factory->desc_list=bctbx_list_prepend(factory->desc_list,desc);
253 }
254 
ms_factory_codec_supported(MSFactory * factory,const char * mime)255 bool_t ms_factory_codec_supported(MSFactory* factory, const char *mime){
256 	MSFilterDesc *enc = ms_factory_get_encoding_capturer(factory, mime);
257 	MSFilterDesc *dec = ms_factory_get_decoding_renderer(factory, mime);
258 
259 	if (enc == NULL) enc = ms_factory_get_encoder(factory, mime);
260 	if (dec == NULL) dec = ms_factory_get_decoder(factory, mime);
261 
262 	if(enc!=NULL && dec!=NULL) return TRUE;
263 
264 	if(enc==NULL) ms_message("Could not find encoder for %s", mime);
265 	if(dec==NULL) ms_message("Could not find decoder for %s", mime);
266 	return FALSE;
267 }
268 
ms_factory_get_encoding_capturer(MSFactory * factory,const char * mime)269 MSFilterDesc * ms_factory_get_encoding_capturer(MSFactory* factory, const char *mime) {
270 	bctbx_list_t *elem;
271 
272 	for (elem = factory->desc_list; elem != NULL; elem = bctbx_list_next(elem)) {
273 		MSFilterDesc *desc = (MSFilterDesc *)elem->data;
274 		if (desc->category == MS_FILTER_ENCODING_CAPTURER) {
275 			char *saveptr=NULL;
276 			char *enc_fmt = ms_strdup(desc->enc_fmt);
277 			char *token = strtok_r(enc_fmt, " ", &saveptr);
278 			while (token != NULL) {
279 				if (strcasecmp(token, mime) == 0) {
280 					break;
281 				}
282 				token = strtok_r(NULL, " ", &saveptr);
283 			}
284 			ms_free(enc_fmt);
285 			if (token != NULL) return desc;
286 		}
287 	}
288 	return NULL;
289 }
290 
ms_factory_get_decoding_renderer(MSFactory * factory,const char * mime)291 MSFilterDesc * ms_factory_get_decoding_renderer(MSFactory* factory, const char *mime) {
292 	bctbx_list_t *elem;
293 
294 	for (elem = factory->desc_list; elem != NULL; elem = bctbx_list_next(elem)) {
295 		MSFilterDesc *desc = (MSFilterDesc *)elem->data;
296 		if (desc->category == MS_FILTER_DECODER_RENDERER) {
297 			char *saveptr=NULL;
298 			char *enc_fmt = ms_strdup(desc->enc_fmt);
299 			char *token = strtok_r(enc_fmt, " ", &saveptr);
300 			while (token != NULL) {
301 				if (strcasecmp(token, mime) == 0) {
302 					break;
303 				}
304 				token = strtok_r(NULL, " ", &saveptr);
305 			}
306 			ms_free(enc_fmt);
307 			if (token != NULL) return desc;
308 		}
309 	}
310 	return NULL;
311 }
312 
ms_factory_get_encoder(MSFactory * factory,const char * mime)313 MSFilterDesc * ms_factory_get_encoder(MSFactory* factory, const char *mime){
314 	bctbx_list_t *elem;
315 	for (elem=factory->desc_list;elem!=NULL;elem=bctbx_list_next(elem)){
316 		MSFilterDesc *desc=(MSFilterDesc*)elem->data;
317 		if ((desc->flags & MS_FILTER_IS_ENABLED)
318 			&& (desc->category==MS_FILTER_ENCODER || desc->category==MS_FILTER_ENCODING_CAPTURER)
319 			&& strcasecmp(desc->enc_fmt,mime)==0){
320 			return desc;
321 		}
322 	}
323 	return NULL;
324 }
325 
ms_factory_get_decoder(MSFactory * factory,const char * mime)326 MSFilterDesc * ms_factory_get_decoder(MSFactory* factory, const char *mime){
327 	bctbx_list_t *elem;
328 	for (elem=factory->desc_list;elem!=NULL;elem=bctbx_list_next(elem)){
329 		MSFilterDesc *desc=(MSFilterDesc*)elem->data;
330 		if ((desc->flags & MS_FILTER_IS_ENABLED)
331 			&& (desc->category==MS_FILTER_DECODER || desc->category==MS_FILTER_DECODER_RENDERER)
332 			&& strcasecmp(desc->enc_fmt,mime)==0){
333 			return desc;
334 		}
335 	}
336 	return NULL;
337 }
338 
ms_factory_create_encoder(MSFactory * factory,const char * mime)339 MSFilter * ms_factory_create_encoder(MSFactory* factory, const char *mime){
340 	MSFilterDesc *desc=ms_factory_get_encoder(factory,mime);
341 	if (desc!=NULL) return ms_factory_create_filter_from_desc(factory,desc);
342 	return NULL;
343 }
344 
ms_factory_create_decoder(MSFactory * factory,const char * mime)345 MSFilter * ms_factory_create_decoder(MSFactory* factory, const char *mime){
346 	//MSFilterDesc *desc=ms_filter_get_decoder(mime);
347 	MSFilterDesc *desc = ms_factory_get_decoder(factory, mime);
348 	if (desc!=NULL) return ms_factory_create_filter_from_desc(factory,desc);
349 	return NULL;
350 }
351 
ms_factory_create_filter_from_desc(MSFactory * factory,MSFilterDesc * desc)352 MSFilter *ms_factory_create_filter_from_desc(MSFactory* factory, MSFilterDesc *desc){
353 	MSFilter *obj;
354 	obj=(MSFilter *)ms_new0(MSFilter,1);
355 	ms_mutex_init(&obj->lock,NULL);
356 	obj->desc=desc;
357 	if (desc->ninputs>0)	obj->inputs=(MSQueue**)ms_new0(MSQueue*,desc->ninputs);
358 	if (desc->noutputs>0)	obj->outputs=(MSQueue**)ms_new0(MSQueue*,desc->noutputs);
359 
360 	if (factory->statistics_enabled){
361 		obj->stats=find_or_create_stats(factory,desc);
362 	}
363 	obj->factory=factory;
364 	if (obj->desc->init!=NULL)
365 		obj->desc->init(obj);
366 	return obj;
367 }
368 
ms_factory_get_snd_card_manager(MSFactory * factory)369 struct _MSSndCardManager* ms_factory_get_snd_card_manager(MSFactory *factory){
370 	return factory->sndcardmanager;
371 }
372 
ms_factory_get_web_cam_manager(MSFactory * f)373 struct _MSWebCamManager* ms_factory_get_web_cam_manager(MSFactory* f){
374 	return f->wbcmanager;
375 }
376 
ms_factory_create_filter(MSFactory * factory,MSFilterId id)377 MSFilter *ms_factory_create_filter(MSFactory* factory, MSFilterId id){
378 	MSFilterDesc *desc;
379 	if (id==MS_FILTER_PLUGIN_ID){
380 		ms_warning("cannot create plugin filters with ms_filter_new_from_id()");
381 		return NULL;
382 	}
383 	desc=ms_factory_lookup_filter_by_id(factory,id);
384 	if (desc) return ms_factory_create_filter_from_desc(factory,desc);
385 	ms_error("No such filter with id %i",id);
386 	return NULL;
387 }
388 
ms_factory_lookup_filter_by_name(const MSFactory * factory,const char * filter_name)389 MSFilterDesc *ms_factory_lookup_filter_by_name(const MSFactory* factory, const char *filter_name){
390 	bctbx_list_t *elem;
391 	for (elem=factory->desc_list;elem!=NULL;elem=bctbx_list_next(elem)){
392 		MSFilterDesc *desc=(MSFilterDesc*)elem->data;
393 		if (strcmp(desc->name,filter_name)==0){
394 			return desc;
395 		}
396 	}
397 	return NULL;
398 }
399 
ms_factory_lookup_filter_by_id(MSFactory * factory,MSFilterId id)400 MSFilterDesc* ms_factory_lookup_filter_by_id( MSFactory* factory, MSFilterId id){
401 	bctbx_list_t *elem;
402 
403 	for (elem=factory->desc_list;elem!=NULL;elem=bctbx_list_next(elem)){
404 		MSFilterDesc *desc=(MSFilterDesc*)elem->data;
405 		if (desc->id==id){
406 			return desc;
407 		}
408 	}
409 	return NULL;
410 }
411 
ms_factory_lookup_filter_by_interface(MSFactory * factory,MSFilterInterfaceId id)412 bctbx_list_t *ms_factory_lookup_filter_by_interface(MSFactory* factory, MSFilterInterfaceId id){
413 	bctbx_list_t *ret=NULL;
414 	bctbx_list_t *elem;
415 	for(elem=factory->desc_list;elem!=NULL;elem=elem->next){
416 		MSFilterDesc *desc=(MSFilterDesc*)elem->data;
417 		if (ms_filter_desc_implements_interface(desc,id))
418 			ret=bctbx_list_append(ret,desc);
419 	}
420 	return ret;
421 }
422 
ms_factory_create_filter_from_name(MSFactory * factory,const char * filter_name)423 MSFilter *ms_factory_create_filter_from_name(MSFactory* factory, const char *filter_name){
424 	MSFilterDesc *desc=ms_factory_lookup_filter_by_name(factory, filter_name);
425 	if (desc==NULL) return NULL;
426 	return ms_factory_create_filter_from_desc(factory,desc);
427 }
428 
ms_factory_enable_statistics(MSFactory * obj,bool_t enabled)429 void ms_factory_enable_statistics(MSFactory* obj, bool_t enabled){
430 	obj->statistics_enabled=enabled;
431 }
432 
ms_factory_get_statistics(MSFactory * obj)433 const bctbx_list_t * ms_factory_get_statistics(MSFactory* obj){
434 	return obj->stats_list;
435 }
436 
ms_factory_reset_statistics(MSFactory * obj)437 void ms_factory_reset_statistics(MSFactory *obj){
438 	bctbx_list_t *elem;
439 
440 	for(elem=obj->stats_list;elem!=NULL;elem=elem->next){
441 		MSFilterStats *stats=(MSFilterStats *)elem->data;
442 		stats->elapsed=0;
443 		stats->count=0;
444 	}
445 }
446 
usage_compare(const MSFilterStats * s1,const MSFilterStats * s2)447 static int usage_compare(const MSFilterStats *s1, const MSFilterStats *s2){
448 	if (s1->elapsed==s2->elapsed) return 0;
449 	if (s1->elapsed<s2->elapsed) return 1;
450 	return -1;
451 }
452 
453 
ms_factory_log_statistics(MSFactory * obj)454 void ms_factory_log_statistics(MSFactory *obj){
455 	bctbx_list_t *sorted=NULL;
456 	bctbx_list_t *elem;
457 	uint64_t total=1;
458 	for(elem=obj->stats_list;elem!=NULL;elem=elem->next){
459 		MSFilterStats *stats=(MSFilterStats *)elem->data;
460 		sorted=bctbx_list_insert_sorted(sorted,stats,(bctbx_compare_func)usage_compare);
461 		total+=stats->elapsed;
462 	}
463 	ms_message("===========================================================");
464 	ms_message("                  FILTER USAGE STATISTICS                  ");
465 	ms_message("Name                Count     Time/tick (ms)      CPU Usage");
466 	ms_message("-----------------------------------------------------------");
467 	for(elem=sorted;elem!=NULL;elem=elem->next){
468 		MSFilterStats *stats=(MSFilterStats *)elem->data;
469 		double percentage=100.0*((double)stats->elapsed)/(double)total;
470 		double tpt=((double)stats->elapsed*1e-6)/((double)stats->count+1.0);
471 		ms_message("%-19s %-9i %-19g %-10g",stats->name,stats->count,tpt,percentage);
472 	}
473 	ms_message("===========================================================");
474 	bctbx_list_free(sorted);
475 }
476 
477 #ifndef PLUGINS_EXT
478 	#define PLUGINS_EXT ".so"
479 #endif
480 typedef void (*init_func_t)(MSFactory *);
481 
ms_factory_load_plugins(MSFactory * factory,const char * dir)482 int ms_factory_load_plugins(MSFactory *factory, const char *dir){
483 	int num=0;
484 #if defined(_WIN32) && !defined(_WIN32_WCE)
485 	WIN32_FIND_DATA FileData;
486 	HANDLE hSearch;
487 	char szDirPath[1024];
488 #ifdef UNICODE
489 	wchar_t wszDirPath[1024];
490 #endif
491 	char szPluginFile[1024];
492 	BOOL fFinished = FALSE;
493 	const char *tmp = NULL;
494 	BOOL debug = FALSE;
495 #ifndef MS2_WINDOWS_UNIVERSAL
496 	tmp = getenv("DEBUG");
497 #endif
498 	debug = (tmp != NULL && atoi(tmp) == 1);
499 
500 	snprintf(szDirPath, sizeof(szDirPath), "%s", dir);
501 
502 	// Start searching for .dll files in the current directory.
503 	snprintf(szDirPath, sizeof(szDirPath), "%s\\libms*.dll", dir);
504 #ifdef UNICODE
505 	mbstowcs(wszDirPath, szDirPath, sizeof(wszDirPath));
506 	hSearch = FindFirstFileExW(wszDirPath, FindExInfoStandard, &FileData, FindExSearchNameMatch, NULL, 0);
507 #else
508 	hSearch = FindFirstFileExA(szDirPath, FindExInfoStandard, &FileData, FindExSearchNameMatch, NULL, 0);
509 #endif
510 	if (hSearch == INVALID_HANDLE_VALUE)
511 	{
512 		ms_message("no plugin (*.dll) found in [%s] [%d].", szDirPath, (int)GetLastError());
513 		return 0;
514 	}
515 	snprintf(szDirPath, sizeof(szDirPath), "%s", dir);
516 
517 	while (!fFinished)
518 	{
519 		/* load library */
520 #ifdef MS2_WINDOWS_DESKTOP
521 		UINT em=0;
522 #endif
523 		HINSTANCE os_handle;
524 #ifdef UNICODE
525 		wchar_t wszPluginFile[2048];
526 		char filename[512];
527 		wcstombs(filename, FileData.cFileName, sizeof(filename));
528 		snprintf(szPluginFile, sizeof(szPluginFile), "%s\\%s", szDirPath, filename);
529 		mbstowcs(wszPluginFile, szPluginFile, sizeof(wszPluginFile));
530 #else
531 		snprintf(szPluginFile, sizeof(szPluginFile), "%s\\%s", szDirPath, FileData.cFileName);
532 #endif
533 #ifdef MS2_WINDOWS_DESKTOP
534 		if (!debug) em = SetErrorMode (SEM_FAILCRITICALERRORS);
535 
536 #ifdef UNICODE
537 		os_handle = LoadLibraryExW(wszPluginFile, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
538 #else
539 		os_handle = LoadLibraryExA(szPluginFile, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
540 #endif
541 		if (os_handle==NULL)
542 		{
543 			ms_message("Fail to load plugin %s with altered search path: error %i",szPluginFile,(int)GetLastError());
544 #ifdef UNICODE
545 			os_handle = LoadLibraryExW(wszPluginFile, NULL, 0);
546 #else
547 			os_handle = LoadLibraryExA(szPluginFile, NULL, 0);
548 #endif
549 		}
550 		if (!debug) SetErrorMode (em);
551 #else
552 		os_handle = LoadPackagedLibrary(wszPluginFile, 0);
553 #endif
554 		if (os_handle==NULL)
555 			ms_error("Fail to load plugin %s: error %i", szPluginFile, (int)GetLastError());
556 		else{
557 			init_func_t initroutine;
558 			char szPluginName[256];
559 			char szMethodName[256];
560 			char *minus;
561 #ifdef UNICODE
562 			snprintf(szPluginName, sizeof(szPluginName), "%s", filename);
563 #else
564 			snprintf(szPluginName, sizeof(szPluginName), "%s", FileData.cFileName);
565 #endif
566 			/*on mingw, dll names might be libsomething-3.dll. We must skip the -X.dll stuff*/
567 			minus=strchr(szPluginName,'-');
568 			if (minus) *minus='\0';
569 			else szPluginName[strlen(szPluginName)-4]='\0'; /*remove .dll*/
570 			snprintf(szMethodName, sizeof(szMethodName), "%s_init", szPluginName);
571 			initroutine = (init_func_t) GetProcAddress (os_handle, szMethodName);
572 				if (initroutine!=NULL){
573 					initroutine(factory);
574 					ms_message("Plugin loaded (%s)", szPluginFile);
575 					// Add this new loaded plugin to the list (useful for FreeLibrary at the end)
576 					factory->ms_plugins_loaded_list=bctbx_list_append(factory->ms_plugins_loaded_list,os_handle);
577 					num++;
578 				}else{
579 					ms_warning("Could not locate init routine of plugin %s. Should be %s",
580 					szPluginFile, szMethodName);
581 				}
582 		}
583 		if (!FindNextFile(hSearch, &FileData)) {
584 			if (GetLastError() == ERROR_NO_MORE_FILES){
585 				fFinished = TRUE;
586 			}
587 			else
588 			{
589 				ms_error("couldn't find next plugin dll.");
590 				fFinished = TRUE;
591 			}
592 		}
593 	}
594 	/* Close the search handle. */
595 	FindClose(hSearch);
596 
597 #elif defined(HAVE_DLOPEN)
598 	char plugin_name[64];
599 	DIR *ds;
600 	bctbx_list_t *loaded_plugins = NULL;
601 	struct dirent *de;
602 	char *ext;
603 	char *fullpath;
604 	ds=opendir(dir);
605 	if (ds==NULL){
606 		ms_message("Cannot open directory %s: %s",dir,strerror(errno));
607 		return -1;
608 	}
609 	while( (de=readdir(ds))!=NULL){
610 		if (
611 #ifndef __QNX__
612 			(de->d_type==DT_REG || de->d_type==DT_UNKNOWN || de->d_type==DT_LNK) &&
613 #endif
614 			(strstr(de->d_name, "libms") == de->d_name) && ((ext=strstr(de->d_name,PLUGINS_EXT))!=NULL)) {
615 			void *handle;
616 			snprintf(plugin_name, MIN(sizeof(plugin_name), (size_t)(ext - de->d_name + 1)), "%s", de->d_name);
617 			if (bctbx_list_find_custom(loaded_plugins, (bctbx_compare_func)strcmp, plugin_name) != NULL) continue;
618 			loaded_plugins = bctbx_list_append(loaded_plugins, ms_strdup(plugin_name));
619 			fullpath=ms_strdup_printf("%s/%s",dir,de->d_name);
620 			ms_message("Loading plugin %s...",fullpath);
621 
622 			if ( (handle=dlopen(fullpath,RTLD_NOW))==NULL){
623 				ms_warning("Fail to load plugin %s : %s",fullpath,dlerror());
624 			}else {
625 				char *initroutine_name=ms_malloc0(strlen(de->d_name)+10);
626 				char *p;
627 				void *initroutine=NULL;
628 				strcpy(initroutine_name,de->d_name);
629 				p=strstr(initroutine_name,PLUGINS_EXT);
630 				if (p!=NULL){
631 					strcpy(p,"_init");
632 					initroutine=dlsym(handle,initroutine_name);
633 				}
634 
635 #ifdef __APPLE__
636 				if (initroutine==NULL){
637 					/* on macosx: library name are libxxxx.1.2.3.dylib */
638 					/* -> MUST remove the .1.2.3 */
639 					p=strstr(initroutine_name,".");
640 					if (p!=NULL)
641 					{
642 						strcpy(p,"_init");
643 						initroutine=dlsym(handle,initroutine_name);
644 					}
645 				}
646 #endif
647 
648 				if (initroutine!=NULL){
649 					init_func_t func=(init_func_t)initroutine;
650 					func(factory);
651 					ms_message("Plugin loaded (%s)", fullpath);
652 					num++;
653 				}else{
654 					ms_warning("Could not locate init routine of plugin %s",de->d_name);
655 				}
656 				ms_free(initroutine_name);
657 			}
658 			ms_free(fullpath);
659 		}
660 	}
661 	bctbx_list_for_each(loaded_plugins, ms_free);
662 	bctbx_list_free(loaded_plugins);
663 	closedir(ds);
664 #else
665 	ms_warning("no loadable plugin support: plugins cannot be loaded.");
666 	num=-1;
667 #endif
668 	return num;
669 }
670 
ms_factory_uninit_plugins(MSFactory * factory)671 void ms_factory_uninit_plugins(MSFactory *factory){
672 #if defined(_WIN32)
673 	bctbx_list_t *elem;
674 #endif
675 
676 #if defined(_WIN32)
677 	for(elem=factory->ms_plugins_loaded_list;elem!=NULL;elem=elem->next)
678 	{
679 		HINSTANCE handle=(HINSTANCE )elem->data;
680 		FreeLibrary(handle) ;
681 	}
682 
683 	factory->ms_plugins_loaded_list = bctbx_list_free(factory->ms_plugins_loaded_list);
684 #endif
685 }
686 
ms_factory_init_plugins(MSFactory * obj)687 void ms_factory_init_plugins(MSFactory *obj) {
688 	if (obj->plugins_dir == NULL) {
689 #ifdef PACKAGE_PLUGINS_DIR
690 		obj->plugins_dir = ms_strdup(PACKAGE_PLUGINS_DIR);
691 #else
692 		obj->plugins_dir = ms_strdup("");
693 #endif
694 	}
695 	if (strlen(obj->plugins_dir) > 0) {
696 		ms_message("Loading ms plugins from [%s]",obj->plugins_dir);
697 		ms_factory_load_plugins(obj,obj->plugins_dir);
698 	}
699 }
700 
ms_factory_set_plugins_dir(MSFactory * obj,const char * path)701 void ms_factory_set_plugins_dir(MSFactory *obj, const char *path) {
702 	if (obj->plugins_dir != NULL) {
703 		ms_free(obj->plugins_dir);
704 		obj->plugins_dir=NULL;
705 	}
706 	if (path)
707 		obj->plugins_dir = ms_strdup(path);
708 }
709 
ms_factory_create_event_queue(MSFactory * obj)710 struct _MSEventQueue *ms_factory_create_event_queue(MSFactory *obj) {
711 	if (obj->evq==NULL){
712 		obj->evq=ms_event_queue_new();
713 	}
714 	return obj->evq;
715 }
716 
ms_factory_destroy_event_queue(MSFactory * obj)717 void ms_factory_destroy_event_queue(MSFactory *obj) {
718 	if (obj->image_resources_dir) bctbx_free(obj->image_resources_dir);
719 	ms_event_queue_destroy(obj->evq);
720 	ms_factory_set_event_queue(obj,NULL);
721 }
722 
723 
ms_factory_get_event_queue(MSFactory * obj)724 struct _MSEventQueue *ms_factory_get_event_queue(MSFactory *obj){
725 	return obj->evq;
726 }
727 
728 /*this function is for compatibility, when event queues were created by the application*/
ms_factory_set_event_queue(MSFactory * obj,MSEventQueue * evq)729 void ms_factory_set_event_queue(MSFactory *obj, MSEventQueue *evq){
730 	obj->evq=evq;
731 }
732 
compare_fmt(const MSFmtDescriptor * a,const MSFmtDescriptor * b)733 static int compare_fmt(const MSFmtDescriptor *a, const MSFmtDescriptor *b){
734 	if (a->type!=b->type) return -1;
735 	if (strcasecmp(a->encoding,b->encoding)!=0) return -1;
736 	if (a->rate!=b->rate) return -1;
737 	if (a->nchannels!=b->nchannels) return -1;
738 	if (a->fmtp==NULL && b->fmtp!=NULL) return -1;
739 	if (a->fmtp!=NULL && b->fmtp==NULL) return -1;
740 	if (a->fmtp && b->fmtp && strcmp(a->fmtp,b->fmtp)!=0) return -1;
741 	if (a->type==MSVideo){
742 		if (a->vsize.width!=b->vsize.width || a->vsize.height!=b->vsize.height) return -1;
743 		if (a->fps!=b->fps) return -1;
744 	}
745 	return 0;
746 }
747 
ms_fmt_descriptor_new_copy(const MSFmtDescriptor * orig)748 static MSFmtDescriptor * ms_fmt_descriptor_new_copy(const MSFmtDescriptor *orig){
749 	MSFmtDescriptor *obj=ms_new0(MSFmtDescriptor,1);
750 	obj->type=orig->type;
751 	obj->rate=orig->rate;
752 	obj->nchannels=orig->nchannels;
753 	if (orig->fmtp) obj->fmtp=ms_strdup(orig->fmtp);
754 	if (orig->encoding) obj->encoding=ms_strdup(orig->encoding);
755 	obj->vsize=orig->vsize;
756 	obj->fps=orig->fps;
757 	return obj;
758 }
759 
ms_fmt_descriptor_to_string(const MSFmtDescriptor * obj)760 const char *ms_fmt_descriptor_to_string(const MSFmtDescriptor *obj){
761 	MSFmtDescriptor *mutable_fmt=(MSFmtDescriptor*)obj;
762 	if (!obj) return "null";
763 	if (obj->text==NULL){
764 		if (obj->type==MSAudio){
765 			mutable_fmt->text=ms_strdup_printf("type=audio;encoding=%s;rate=%i;channels=%i;fmtp='%s'",
766 							 obj->encoding,obj->rate,obj->nchannels,obj->fmtp ? obj->fmtp : "");
767 		}else{
768 			mutable_fmt->text=ms_strdup_printf("type=video;encoding=%s;vsize=%ix%i;fps=%f;fmtp='%s'",
769 							 obj->encoding,obj->vsize.width,obj->vsize.height,obj->fps,obj->fmtp ? obj->fmtp : "");
770 		}
771 	}
772 	return obj->text;
773 }
774 
ms_fmt_descriptor_equals(const MSFmtDescriptor * fmt1,const MSFmtDescriptor * fmt2)775 bool_t ms_fmt_descriptor_equals(const MSFmtDescriptor *fmt1, const MSFmtDescriptor *fmt2) {
776 	if (!fmt1 || !fmt2) return FALSE;
777 	return compare_fmt(fmt1, fmt2) == 0;
778 }
779 
ms_fmt_descriptor_destroy(MSFmtDescriptor * obj)780 static void ms_fmt_descriptor_destroy(MSFmtDescriptor *obj){
781 	if (obj->encoding) ms_free(obj->encoding);
782 	if (obj->fmtp) ms_free(obj->fmtp);
783 	if (obj->text) ms_free(obj->text);
784 	ms_free(obj);
785 }
786 
ms_factory_get_format(MSFactory * obj,const MSFmtDescriptor * ref)787 const MSFmtDescriptor *ms_factory_get_format(MSFactory *obj, const MSFmtDescriptor *ref){
788 	MSFmtDescriptor *ret;
789 	bctbx_list_t *found;
790 	if ((found=bctbx_list_find_custom(obj->formats,(int (*)(const void*, const void*))compare_fmt, ref))==NULL){
791 		obj->formats=bctbx_list_append(obj->formats,ret=ms_fmt_descriptor_new_copy(ref));
792 	}else{
793 		ret=(MSFmtDescriptor *)found->data;
794 	}
795 	return ret;
796 }
797 
ms_factory_get_audio_format(MSFactory * obj,const char * mime,int rate,int channels,const char * fmtp)798 const MSFmtDescriptor * ms_factory_get_audio_format(MSFactory *obj, const char *mime, int rate, int channels, const char *fmtp){
799 	MSFmtDescriptor tmp={0};
800 	tmp.type=MSAudio;
801 	tmp.encoding=(char*)mime;
802 	tmp.rate=rate;
803 	tmp.nchannels=channels;
804 	tmp.fmtp=(char*)fmtp;
805 	return ms_factory_get_format(obj,&tmp);
806 }
807 
ms_factory_get_video_format(MSFactory * obj,const char * mime,MSVideoSize size,float fps,const char * fmtp)808 const MSFmtDescriptor * ms_factory_get_video_format(MSFactory *obj, const char *mime, MSVideoSize size, float fps, const char *fmtp){
809 	MSFmtDescriptor tmp={0};
810 	tmp.type=MSVideo;
811 	tmp.encoding=(char*)mime;
812 	tmp.rate=90000;
813 	tmp.vsize=size;
814 	tmp.fmtp=(char*)fmtp;
815 	tmp.fps=fps;
816 	return ms_factory_get_format(obj,&tmp);
817 }
818 
ms_factory_enable_filter_from_name(MSFactory * factory,const char * name,bool_t enable)819 int ms_factory_enable_filter_from_name(MSFactory *factory, const char *name, bool_t enable) {
820 	MSFilterDesc *desc=ms_factory_lookup_filter_by_name(factory,name);
821 	if (!desc) {
822 		ms_error("Cannot enable/disable unknown filter [%s] on factory [%p]",name,factory);
823 		return -1;
824 	}
825 	if (enable) desc->flags |= MS_FILTER_IS_ENABLED;
826 	else desc->flags &= ~MS_FILTER_IS_ENABLED;
827 	ms_message("Filter [%s]  %s on factory [%p]",name,(enable ? "enabled" : "disabled"),factory);
828 	return 0;
829 }
830 
ms_factory_filter_from_name_enabled(const MSFactory * factory,const char * name)831 bool_t ms_factory_filter_from_name_enabled(const MSFactory *factory, const char *name) {
832 	MSFilterDesc *desc=ms_factory_lookup_filter_by_name(factory,name);
833 	if (!desc) {
834 		ms_error("Cannot get enable/disable state for unknown filter [%s] on factory [%p]",name,factory);
835 		return FALSE;
836 	}
837 	return !!(desc->flags & MS_FILTER_IS_ENABLED);
838 }
839 
840 
ms_factory_register_offer_answer_provider(MSFactory * f,MSOfferAnswerProvider * offer_answer_prov)841 void ms_factory_register_offer_answer_provider(MSFactory *f, MSOfferAnswerProvider *offer_answer_prov){
842 	if (bctbx_list_find(f->offer_answer_provider_list, offer_answer_prov)) return; /*avoid registering several time the same pointer*/
843 	f->offer_answer_provider_list = bctbx_list_prepend(f->offer_answer_provider_list, offer_answer_prov);
844 }
845 
ms_factory_get_offer_answer_provider(MSFactory * f,const char * mime_type)846 MSOfferAnswerProvider * ms_factory_get_offer_answer_provider(MSFactory *f, const char *mime_type){
847 	const bctbx_list_t *elem;
848 	for (elem = f->offer_answer_provider_list; elem != NULL; elem = elem->next){
849 		MSOfferAnswerProvider *prov = (MSOfferAnswerProvider*) elem->data;
850 		if (strcasecmp(mime_type, prov->mime_type) == 0)
851 			return prov;
852 	}
853 	return NULL;
854 }
855 
ms_factory_create_offer_answer_context(MSFactory * f,const char * mime_type)856 MSOfferAnswerContext * ms_factory_create_offer_answer_context(MSFactory *f, const char *mime_type){
857 	MSOfferAnswerProvider *prov = ms_factory_get_offer_answer_provider(f, mime_type);
858 	if (prov) return prov->create_context();
859 	return NULL;
860 }
861 
ms_factory_get_devices_info(MSFactory * f)862 MSDevicesInfo* ms_factory_get_devices_info(MSFactory *f) {
863 	return f->devices_info;
864 }
865 
ms_factory_get_image_resources_dir(const MSFactory * f)866 const char * ms_factory_get_image_resources_dir(const MSFactory *f) {
867 	return f->image_resources_dir;
868 }
869 
ms_factory_set_image_resources_dir(MSFactory * f,const char * path)870 void ms_factory_set_image_resources_dir(MSFactory *f, const char *path) {
871 	if (f->image_resources_dir) {
872 		bctbx_free(f->image_resources_dir);
873 		f->image_resources_dir = NULL;
874 	}
875 	if (path)
876 		f->image_resources_dir = bctbx_strdup(path);
877 }
878 
ms_factory_set_expected_bandwidth(MSFactory * f,int bitrate)879 void ms_factory_set_expected_bandwidth(MSFactory *f, int bitrate) {
880 	f->expected_video_bandwidth = bitrate;
881 }
882 
ms_factory_get_expected_bandwidth(MSFactory * f)883 int ms_factory_get_expected_bandwidth(MSFactory *f) {
884 	return f->expected_video_bandwidth;
885 }
886 
887 #ifdef __ANDROID__
888 #include "sys/system_properties.h"
889 #include <jni.h>
890 
891 
892 #pragma GCC diagnostic ignored "-Wint-to-pointer-cast"
893 
Java_org_linphone_mediastream_Factory_enableFilterFromName(JNIEnv * env,jobject obj,jlong factoryPtr,jstring jname,jboolean enable)894 JNIEXPORT jint JNICALL Java_org_linphone_mediastream_Factory_enableFilterFromName(JNIEnv* env,  jobject obj, jlong factoryPtr, jstring jname, jboolean enable) {
895 	MSFactory *factory = (MSFactory *) factoryPtr;
896 	const char *name = jname ? (*env)->GetStringUTFChars(env, jname, NULL) : NULL;
897 	int result = ms_factory_enable_filter_from_name(factory, name, enable);
898 	(*env)->ReleaseStringUTFChars(env, jname, name);
899 	return result;
900 }
Java_org_linphone_mediastream_Factory_filterFromNameEnabled(JNIEnv * env,jobject obj,jlong factoryPtr,jstring jname)901 JNIEXPORT jboolean JNICALL Java_org_linphone_mediastream_Factory_filterFromNameEnabled(JNIEnv* env, jobject obj, jlong factoryPtr, jstring jname) {
902 	const char *name = jname ? (*env)->GetStringUTFChars(env, jname, NULL) : NULL;
903 	MSFactory *factory = (MSFactory *) factoryPtr;
904 	jboolean result = ms_factory_filter_from_name_enabled(factory, name);
905 	(*env)->ReleaseStringUTFChars(env, jname, name);
906 	return result;
907 }
908 
909 
Java_org_linphone_mediastream_Factory_getEncoderText(JNIEnv * env,jobject obj,jlong factoryPtr,jstring jmime)910 JNIEXPORT jstring JNICALL Java_org_linphone_mediastream_Factory_getEncoderText(JNIEnv* env, jobject obj,
911     jlong factoryPtr, jstring jmime) {
912 	MSFactory *factory = (MSFactory*)factoryPtr;
913 	const char *mime = (*env)->GetStringUTFChars(env, jmime, NULL);
914 	jstring jtext = NULL;
915 	if (mime){
916 		MSFilterDesc *desc = ms_factory_get_encoder(factory, mime);
917 		if (desc) jtext =(*env)->NewStringUTF(env, desc->text);
918 		(*env)->ReleaseStringUTFChars(env, jmime, mime);
919 	}
920 	return jtext;
921 }
922 
Java_org_linphone_mediastream_Factory_getDecoderText(JNIEnv * env,jobject obj,jlong factoryPtr,jstring jmime)923 JNIEXPORT jstring JNICALL Java_org_linphone_mediastream_Factory_getDecoderText(JNIEnv* env, jobject obj,
924     jlong factoryPtr, jstring jmime) {
925 	MSFactory *factory = (MSFactory*)factoryPtr;
926 	const char *mime = (*env)->GetStringUTFChars(env, jmime, NULL);
927 	jstring jtext = NULL;
928 	if (mime){
929 		MSFilterDesc *desc = ms_factory_get_decoder(factory, mime);
930 		if (desc) jtext = (*env)->NewStringUTF(env, desc->text);
931 		(*env)->ReleaseStringUTFChars(env, jmime, mime);
932 	}
933 	return jtext;
934 }
935 
936 #ifdef _MSC_VER
937 #pragma warning(disable : 4996)
938 #else
939 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
940 #endif
941 
Java_org_linphone_mediastream_MediastreamerAndroidContext_enableFilterFromNameImpl(JNIEnv * env,jobject obj,jstring jname,jboolean enable)942 JNIEXPORT jint JNICALL Java_org_linphone_mediastream_MediastreamerAndroidContext_enableFilterFromNameImpl(JNIEnv* env,  jobject obj, jstring jname, jboolean enable) {
943 	const char *mime;
944 	int result;
945 	if (ms_factory_get_fallback() == NULL) {
946 		ms_error("Java_org_linphone_mediastream_MediastreamerAndroidContext_enableFilterFromNameImpl(): no fallback factory. Use Factory.enableFilterFromName()");
947 		return -1;
948 	}
949 	mime = jname ? (*env)->GetStringUTFChars(env, jname, NULL) : NULL;
950 	result = ms_factory_enable_filter_from_name(ms_factory_get_fallback(),mime,enable);
951 	(*env)->ReleaseStringUTFChars(env, jname, mime);
952 	return result;
953 }
Java_org_linphone_mediastream_MediastreamerAndroidContext_filterFromNameEnabledImpl(JNIEnv * env,jobject obj,jstring jname)954 JNIEXPORT jboolean JNICALL Java_org_linphone_mediastream_MediastreamerAndroidContext_filterFromNameEnabledImpl(JNIEnv* env, jobject obj, jstring jname) {
955 	const char *mime;
956 	jboolean result;
957 	if (ms_factory_get_fallback() == NULL) {
958 		ms_error("Java_org_linphone_mediastream_MediastreamerAndroidContext_filterFromNameEnabledImpl(): no fallback factory. Use Factory.filterFromNameEnabled()");
959 		return FALSE;
960 	}
961 	mime = jname ? (*env)->GetStringUTFChars(env, jname, NULL) : NULL;
962 	result = ms_factory_filter_from_name_enabled(ms_factory_get_fallback(),mime);
963 	(*env)->ReleaseStringUTFChars(env, jname, mime);
964 	return result;
965 }
966 
967 #endif
968 
969 
970 
971 #ifdef _MSC_VER
972 #pragma warning(disable : 4996)
973 #else
974 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
975 #endif
976 
977 /**
978 * Destroy the factory.
979 * This should be done after destroying all objects created by the factory.
980 **/
ms_factory_destroy(MSFactory * factory)981 void ms_factory_destroy(MSFactory *factory) {
982 	if (factory->voip_uninit_func) factory->voip_uninit_func(factory);
983 	ms_factory_uninit_plugins(factory);
984 	if (factory->evq) ms_factory_destroy_event_queue(factory);
985 	factory->formats = bctbx_list_free_with_data(factory->formats, (void(*)(void*))ms_fmt_descriptor_destroy);
986 	factory->desc_list = bctbx_list_free(factory->desc_list);
987 	bctbx_list_for_each(factory->stats_list, ms_free);
988 	factory->stats_list = bctbx_list_free(factory->stats_list);
989 	factory->offer_answer_provider_list = bctbx_list_free(factory->offer_answer_provider_list);
990 	bctbx_list_for_each(factory->platform_tags, ms_free);
991 	factory->platform_tags = bctbx_list_free(factory->platform_tags);
992 	if (factory->echo_canceller_filtername) ms_free(factory->echo_canceller_filtername);
993 	if (factory->plugins_dir) ms_free(factory->plugins_dir);
994 	ms_free(factory);
995 	if (factory == fallback_factory) fallback_factory = NULL;
996 }
997 
998 
ms_factory_create_fallback(void)999 MSFactory *ms_factory_create_fallback(void){
1000 	if (fallback_factory==NULL){
1001 		fallback_factory=ms_factory_new();
1002 	}
1003 	return fallback_factory;
1004 }
1005 
1006 /**
1007  * Used by the legacy functions before MSFactory was added.
1008  * Do not use in an application.
1009 **/
ms_factory_get_fallback(void)1010 MSFactory *ms_factory_get_fallback(void){
1011 	return fallback_factory;
1012 }
1013 
1014 
1015