]> git.sesse.net Git - mlt/blob - src/framework/mlt_repository.c
Make it easier to embed sdl in Windows applications.
[mlt] / src / framework / mlt_repository.c
1 /**
2  * \file mlt_repository.c
3  * \brief provides a map between service and shared objects
4  * \see mlt_repository_s
5  *
6  * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
7  * \author Charles Yates <charles.yates@pandora.be>
8  * \author Dan Dennedy <dan@dennedy.org>
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2.1 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23  */
24
25 #include "mlt_repository.h"
26 #include "mlt_properties.h"
27 #include "mlt_tokeniser.h"
28 #include "mlt_log.h"
29 #include "mlt_factory.h"
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <dlfcn.h>
34 #include <string.h>
35 #include <limits.h>
36 #include <dirent.h>
37 #include <sys/stat.h>
38
39 /** \brief Repository class
40  *
41  * The Repository is a collection of plugin modules and their services and service metadata.
42  *
43  * \extends mlt_properties_s
44  * \properties \p language a cached list of user locales
45  */
46
47 struct mlt_repository_s
48 {
49         struct mlt_properties_s parent; /// a list of object files
50         mlt_properties consumers;       /// a list of entry points for consumers
51         mlt_properties filters;         /// a list of entry points for filters
52         mlt_properties producers;       /// a list of entry points for producers
53         mlt_properties transitions;     /// a list of entry points for transitions
54 };
55
56 /** Construct a new repository.
57  *
58  * \public \memberof mlt_repository_s
59  * \param directory the full path of a directory from which to read modules
60  * \return a new repository or NULL if failed
61  */
62
63 mlt_repository mlt_repository_init( const char *directory )
64 {
65         // Safety check
66         if ( directory == NULL || strcmp( directory, "" ) == 0 )
67                 return NULL;
68
69         // Construct the repository
70         mlt_repository self = calloc( sizeof( struct mlt_repository_s ), 1 );
71         mlt_properties_init( &self->parent, self );
72         self->consumers = mlt_properties_new();
73         self->filters = mlt_properties_new();
74         self->producers = mlt_properties_new();
75         self->transitions = mlt_properties_new();
76
77         // Get the directory list
78         mlt_properties dir = mlt_properties_new();
79         int count = mlt_properties_dir_list( dir, directory, NULL, 0 );
80         int i;
81
82         // Iterate over files
83         for ( i = 0; i < count; i++ )
84         {
85                 int flags = RTLD_NOW;
86                 const char *object_name = mlt_properties_get_value( dir, i);
87
88                 // Very temporary hack to allow the quicktime plugins to work
89                 // TODO: extend repository to allow this to be used on a case by case basis
90                 if ( strstr( object_name, "libmltkino" ) )
91                         flags |= RTLD_GLOBAL;
92
93                 // Open the shared object
94                 void *object = dlopen( object_name, flags );
95                 if ( object != NULL )
96                 {
97                         // Get the registration function
98                         mlt_repository_callback symbol_ptr = dlsym( object, "mlt_register" );
99
100                         // Call the registration function
101                         if ( symbol_ptr != NULL )
102                         {
103                                 symbol_ptr( self );
104
105                                 // Register the object file for closure
106                                 mlt_properties_set_data( &self->parent, object_name, object, 0, ( mlt_destructor )dlclose, NULL );
107                         }
108                         else
109                         {
110                                 dlclose( object );
111                         }
112                 }
113                 else if ( strstr( object_name, "libmlt" ) )
114                 {
115                         mlt_log( NULL, MLT_LOG_WARNING, "%s: failed to dlopen %s\n  (%s)\n", __FUNCTION__, object_name, dlerror() );
116                 }
117         }
118
119         mlt_properties_close( dir );
120
121         return self;
122 }
123
124 /** Create a properties list for a service holding a function pointer to its constructor function.
125  *
126  * \private \memberof mlt_repository_s
127  * \param symbol a pointer to a function that can create the service.
128  * \return a properties list
129  */
130
131 static mlt_properties new_service( void *symbol )
132 {
133         mlt_properties properties = mlt_properties_new();
134         mlt_properties_set_data( properties, "symbol", symbol, 0, NULL, NULL );
135         return properties;
136 }
137
138 /** Register a service with the repository.
139  *
140  * Typically, this is invoked by a module within its mlt_register().
141  *
142  * \public \memberof mlt_repository_s
143  * \param self a repository
144  * \param service_type a service class
145  * \param service the name of a service
146  * \param symbol a pointer to a function to create the service
147  */
148
149 void mlt_repository_register( mlt_repository self, mlt_service_type service_type, const char *service, mlt_register_callback symbol )
150 {
151         // Add the entry point to the corresponding service list
152         switch ( service_type )
153         {
154                 case consumer_type:
155                         mlt_properties_set_data( self->consumers, service, new_service( symbol ), 0, ( mlt_destructor )mlt_properties_close, NULL );
156                         break;
157                 case filter_type:
158                         mlt_properties_set_data( self->filters, service, new_service( symbol ), 0, ( mlt_destructor )mlt_properties_close, NULL );
159                         break;
160                 case producer_type:
161                         mlt_properties_set_data( self->producers, service, new_service( symbol ), 0, ( mlt_destructor )mlt_properties_close, NULL );
162                         break;
163                 case transition_type:
164                         mlt_properties_set_data( self->transitions, service, new_service( symbol ), 0, ( mlt_destructor )mlt_properties_close, NULL );
165                         break;
166                 default:
167                         break;
168         }
169 }
170
171 /** Get the repository properties for particular service class.
172  *
173  * \private \memberof mlt_repository_s
174  * \param self a repository
175  * \param type a service class
176  * \param service the name of a service
177  * \return a properties list or NULL if error
178  */
179
180 static mlt_properties get_service_properties( mlt_repository self, mlt_service_type type, const char *service )
181 {
182         mlt_properties service_properties = NULL;
183
184         // Get the entry point from the corresponding service list
185         switch ( type )
186         {
187                 case consumer_type:
188                         service_properties = mlt_properties_get_data( self->consumers, service, NULL );
189                         break;
190                 case filter_type:
191                         service_properties = mlt_properties_get_data( self->filters, service, NULL );
192                         break;
193                 case producer_type:
194                         service_properties = mlt_properties_get_data( self->producers, service, NULL );
195                         break;
196                 case transition_type:
197                         service_properties = mlt_properties_get_data( self->transitions, service, NULL );
198                         break;
199                 default:
200                         break;
201         }
202         return service_properties;
203 }
204
205 /** Construct a new instance of a service.
206  *
207  * \public \memberof mlt_repository_s
208  * \param self a repository
209  * \param profile a \p mlt_profile to give the service
210  * \param type a service class
211  * \param service the name of the service
212  * \param input an optional argument to the service constructor
213  */
214
215 void *mlt_repository_create( mlt_repository self, mlt_profile profile, mlt_service_type type, const char *service, const void *input )
216 {
217         mlt_properties properties = get_service_properties( self, type, service );
218         if ( properties != NULL )
219         {
220                 mlt_register_callback symbol_ptr = mlt_properties_get_data( properties, "symbol", NULL );
221
222                 // Construct the service
223                 return ( symbol_ptr != NULL ) ? symbol_ptr( profile, type, service, input ) : NULL;
224         }
225         return NULL;
226 }
227
228 /** Destroy a repository and free its resources.
229  *
230  * \public \memberof mlt_repository_s
231  * \param self a repository
232  */
233
234 void mlt_repository_close( mlt_repository self )
235 {
236         mlt_properties_close( self->consumers );
237         mlt_properties_close( self->filters );
238         mlt_properties_close( self->producers );
239         mlt_properties_close( self->transitions );
240         mlt_properties_close( &self->parent );
241         free( self );
242 }
243
244 /** Get the list of registered consumers.
245  *
246  * \public \memberof mlt_repository_s
247  * \param self a repository
248  * \return a properties list containing all of the consumers
249  */
250
251 mlt_properties mlt_repository_consumers( mlt_repository self )
252 {
253         return self->consumers;
254 }
255
256 /** Get the list of registered filters.
257  *
258  * \public \memberof mlt_repository_s
259  * \param self a repository
260  * \return a properties list of all of the filters
261  */
262
263 mlt_properties mlt_repository_filters( mlt_repository self )
264 {
265         return self->filters;
266 }
267
268 /** Get the list of registered producers.
269  *
270  * \public \memberof mlt_repository_s
271  * \param self a repository
272  * \return a properties list of all of the producers
273  */
274
275 mlt_properties mlt_repository_producers( mlt_repository self )
276 {
277         return self->producers;
278 }
279
280 /** Get the list of registered transitions.
281  *
282  * \public \memberof mlt_repository_s
283  * \param self a repository
284  * \return a properties list of all of the transitions
285  */
286
287 mlt_properties mlt_repository_transitions( mlt_repository self )
288 {
289         return self->transitions;
290 }
291
292 /** Register the metadata for a service.
293  *
294  * IMPORTANT: mlt_repository will take responsibility for deallocating the metadata properties
295  * that you supply!
296  *
297  * \public \memberof mlt_repository_s
298  * \param self a repository
299  * \param type a service class
300  * \param service the name of a service
301  * \param callback the pointer to a function that can supply metadata
302  * \param callback_data an opaque user data pointer to be supplied on the callback
303  */
304
305 void mlt_repository_register_metadata( mlt_repository self, mlt_service_type type, const char *service, mlt_metadata_callback callback, void *callback_data )
306 {
307         mlt_properties service_properties = get_service_properties( self, type, service );
308         mlt_properties_set_data( service_properties, "metadata_cb", callback, 0, NULL, NULL );
309         mlt_properties_set_data( service_properties, "metadata_cb_data", callback_data, 0, NULL, NULL );
310 }
311
312 /** Get the metadata about a service.
313  *
314  * Returns NULL if service or its metadata are unavailable.
315  *
316  * \public \memberof mlt_repository_s
317  * \param self a repository
318  * \param type a service class
319  * \param service the name of a service
320  * \return the service metadata as a structured properties list
321  */
322
323 mlt_properties mlt_repository_metadata( mlt_repository self, mlt_service_type type, const char *service )
324 {
325         mlt_properties metadata = NULL;
326         mlt_properties properties = get_service_properties( self, type, service );
327
328         // If this is a valid service
329         if ( properties )
330         {
331                 // Lookup cached metadata
332                 metadata = mlt_properties_get_data( properties, "metadata", NULL );
333                 if ( ! metadata )
334                 {
335                         // Not cached, so get the registered metadata callback function
336                         mlt_metadata_callback callback = mlt_properties_get_data( properties, "metadata_cb", NULL );
337
338                         // If a metadata callback function is registered
339                         if ( callback )
340                         {
341                                 // Fetch the callback data arg
342                                 void *data = mlt_properties_get_data( properties, "metadata_cb_data", NULL );
343
344                                 // Fetch the metadata through the callback
345                                 metadata = callback( type, service, data );
346
347                                 // Cache the metadata
348                                 if ( metadata )
349                                         // Include dellocation and serialisation
350                                         mlt_properties_set_data( properties, "metadata", metadata, 0, ( mlt_destructor )mlt_properties_close, ( mlt_serialiser )mlt_properties_serialise_yaml );
351                         }
352                 }
353         }
354         return metadata;
355 }
356
357 /** Try to determine the locale from some commonly used environment variables.
358  *
359  * \private \memberof mlt_repository_s
360  * \return a string containing the locale id or NULL if unknown
361  */
362
363 static char *getenv_locale()
364 {
365         char *s = getenv( "LANGUAGE" );
366         if ( s && s[0] )
367                 return s;
368         s = getenv( "LC_ALL" );
369         if ( s && s[0] )
370                 return s;
371         s = getenv( "LC_MESSAGES" );
372         if ( s && s[0] )
373                 return s;
374         s = getenv( "LANG" );
375         if ( s && s[0] )
376                 return s;
377         return NULL;
378 }
379
380 /** Return a list of user-preferred language codes taken from environment variables.
381  *
382  * A module should use this to locate a localized YAML Tiny file from which to build
383  * its metadata strucutured properties.
384  *
385  * \public \memberof mlt_repository_s
386  * \param self a repository
387  * \return a properties list that is a list (not a map) of locales, defaults to "en" if not
388  * overridden by environment variables, in order: LANGUAGE, LC_ALL, LC_MESSAGES, LANG
389  */
390
391 mlt_properties mlt_repository_languages( mlt_repository self )
392 {
393         mlt_properties languages = mlt_properties_get_data( &self->parent, "languages", NULL );
394         if ( languages )
395                 return languages;
396
397         languages = mlt_properties_new();
398         char *locale = getenv_locale();
399         if ( locale )
400         {
401                 locale = strdup( locale );
402                 mlt_tokeniser tokeniser = mlt_tokeniser_init();
403                 int count = mlt_tokeniser_parse_new( tokeniser, locale, ":" );
404                 if ( count )
405                 {
406                         int i;
407                         for ( i = 0; i < count; i++ )
408                         {
409                                 char *locale = mlt_tokeniser_get_string( tokeniser, i );
410                                 if ( strcmp( locale, "C" ) == 0 || strcmp( locale, "POSIX" ) == 0 )
411                                         locale = "en";
412                                 else if ( strlen( locale ) > 2 )
413                                         locale[2] = 0;
414                                 char string[21];
415                                 snprintf( string, sizeof(string), "%d", i );
416                                 mlt_properties_set( languages, string, locale );
417                         }
418                 }
419                 else
420                 {
421                         mlt_properties_set( languages, "0", "en" );
422                 }
423                 free( locale );
424                 mlt_tokeniser_close( tokeniser );
425         }
426         else
427         {
428                 mlt_properties_set( languages, "0", "en" );
429         }
430         mlt_properties_set_data( &self->parent, "languages", languages, 0, ( mlt_destructor )mlt_properties_close, NULL );
431         return languages;
432 }
433
434 static void list_presets( mlt_properties properties, const char *path, const char *dirname )
435 {
436         DIR *dir = opendir( dirname );
437
438         if ( dir )
439         {
440                 struct dirent *de = readdir( dir );
441                 char fullname[ PATH_MAX ];
442
443                 while ( de != NULL )
444                 {
445                         if ( de->d_name[0] != '.' && de->d_name[strlen( de->d_name ) - 1] != '~' )
446                         {
447                                 struct stat info;
448
449                                 snprintf( fullname, sizeof(fullname), "%s/%s", dirname, de->d_name );
450                                 stat( fullname, &info );
451                                 if ( S_ISDIR( info.st_mode ) )
452                                 {
453                                         // recurse into subdirectories
454                                         char sub[ PATH_MAX ];
455                                         if ( path )
456                                                 snprintf( sub, sizeof(sub), "%s/%s", path, de->d_name );
457                                         else
458                                                 strncpy( sub, de->d_name, sizeof(sub) );
459                                         list_presets( properties, sub, fullname );
460                                 }
461                                 else
462                                 {
463                                         // load the preset
464                                         mlt_properties preset = mlt_properties_load( fullname );
465                                         if ( preset && mlt_properties_count( preset ) )
466                                         {
467                                                 snprintf( fullname, 1024, "%s/%s", path, de->d_name );
468                                                 mlt_properties_set_data( properties, fullname, preset, 0, (mlt_destructor) mlt_properties_close, NULL );
469                                         }
470                                 }
471                         }
472                         de = readdir( dir );
473                 }
474                 closedir( dir );
475         }
476 }
477
478 /** Get the list of presets.
479  *
480  * \public \memberof mlt_repository_s
481  * \return a properties list of all the presets
482  */
483
484 mlt_properties mlt_repository_presets( )
485 {
486         mlt_properties result = mlt_properties_new();
487         char *path = getenv( "MLT_PRESETS_PATH" );
488
489         if ( path )
490         {
491                 path = strdup( path );
492         }
493         else
494         {
495                 path = malloc( strlen( mlt_environment( "MLT_DATA" ) ) + 9 );
496                 strcpy( path, mlt_environment( "MLT_DATA" ) );
497                 strcat( path, "/presets" );
498         }
499         list_presets( result, NULL, path );
500         free( path );
501
502         return result;
503 }