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