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