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