]> git.sesse.net Git - mlt/blob - src/framework/mlt_repository.c
df83e9efa7f59580dd2ad3dc358fb99ef10d8720
[mlt] / src / framework / mlt_repository.c
1 /**
2  * \file mlt_repository.c
3  * \brief provides a map between service and shared objects
4  *
5  * Copyright (C) 2003-2008 Ushodaya Enterprises Limited
6  * \author Charles Yates <charles.yates@pandora.be>
7  *         Dan Dennedy <dan@dennedy.org>
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  */
23
24 #include "mlt_repository.h"
25 #include "mlt_properties.h"
26 #include "mlt_tokeniser.h"
27 #include "mlt_log.h"
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <dlfcn.h>
32 #include <string.h>
33
34 /** \brief Repository class
35  *
36  * \extends mlt_properties_s
37  */
38
39 struct mlt_repository_s
40 {
41         struct mlt_properties_s parent; // a list of object files
42         mlt_properties consumers; // lists of entry points
43         mlt_properties filters;
44         mlt_properties producers;
45         mlt_properties transitions;
46 };
47
48 /** Construct a new repository
49 */
50
51 mlt_repository mlt_repository_init( const char *directory )
52 {
53         // Safety check
54         if ( directory == NULL || strcmp( directory, "" ) == 0 )
55                 return NULL;
56
57         // Construct the repository
58         mlt_repository this = calloc( sizeof( struct mlt_repository_s ), 1 );
59         mlt_properties_init( &this->parent, this );
60         this->consumers = mlt_properties_new();
61         this->filters = mlt_properties_new();
62         this->producers = mlt_properties_new();
63         this->transitions = mlt_properties_new();
64
65         // Get the directory list
66         mlt_properties dir = mlt_properties_new();
67         int count = mlt_properties_dir_list( dir, directory, NULL, 0 );
68         int i;
69
70         // Iterate over files
71         for ( i = 0; i < count; i++ )
72         {
73                 int flags = RTLD_NOW;
74                 const char *object_name = mlt_properties_get_value( dir, i);
75
76                 // Very temporary hack to allow the quicktime plugins to work
77                 // TODO: extend repository to allow this to be used on a case by case basis
78                 if ( strstr( object_name, "libmltkino" ) )
79                         flags |= RTLD_GLOBAL;
80
81                 // Open the shared object
82                 void *object = dlopen( object_name, flags );
83                 if ( object != NULL )
84                 {
85                         // Get the registration function
86                         mlt_repository_callback symbol_ptr = dlsym( object, "mlt_register" );
87
88                         // Call the registration function
89                         if ( symbol_ptr != NULL )
90                         {
91                                 symbol_ptr( this );
92
93                                 // Register the object file for closure
94                                 mlt_properties_set_data( &this->parent, object_name, object, 0, ( mlt_destructor )dlclose, NULL );
95                         }
96                         else
97                         {
98                                 dlclose( object );
99                         }
100                 }
101                 else if ( strstr( object_name, "libmlt" ) )
102                 {
103                         mlt_log( NULL, MLT_LOG_WARNING, "%s: failed to dlopen %s\n  (%s)\n", __FUNCTION__, object_name, dlerror() );
104                 }
105         }
106
107         mlt_properties_close( dir );
108
109         return this;
110 }
111
112 static mlt_properties new_service( void *symbol )
113 {
114         mlt_properties properties = mlt_properties_new();
115         mlt_properties_set_data( properties, "symbol", symbol, 0, NULL, NULL );
116         return properties;
117 }
118
119 /** Register a service with the repository
120     Typically, this is invoked by a module within its mlt_register().
121 */
122
123 void mlt_repository_register( mlt_repository this, mlt_service_type service_type, const char *service, mlt_register_callback symbol )
124 {
125         // Add the entry point to the corresponding service list
126         switch ( service_type )
127         {
128                 case consumer_type:
129                         mlt_properties_set_data( this->consumers, service, new_service( symbol ), 0, ( mlt_destructor )mlt_properties_close, NULL );
130                         break;
131                 case filter_type:
132                         mlt_properties_set_data( this->filters, service, new_service( symbol ), 0, ( mlt_destructor )mlt_properties_close, NULL );
133                         break;
134                 case producer_type:
135                         mlt_properties_set_data( this->producers, service, new_service( symbol ), 0, ( mlt_destructor )mlt_properties_close, NULL );
136                         break;
137                 case transition_type:
138                         mlt_properties_set_data( this->transitions, service, new_service( symbol ), 0, ( mlt_destructor )mlt_properties_close, NULL );
139                         break;
140                 default:
141                         break;
142         }
143 }
144
145 static mlt_properties get_service_properties( mlt_repository this, mlt_service_type type, const char *service )
146 {
147         mlt_properties service_properties = NULL;
148
149         // Get the entry point from the corresponding service list
150         switch ( type )
151         {
152                 case consumer_type:
153                         service_properties = mlt_properties_get_data( this->consumers, service, NULL );
154                         break;
155                 case filter_type:
156                         service_properties = mlt_properties_get_data( this->filters, service, NULL );
157                         break;
158                 case producer_type:
159                         service_properties = mlt_properties_get_data( this->producers, service, NULL );
160                         break;
161                 case transition_type:
162                         service_properties = mlt_properties_get_data( this->transitions, service, NULL );
163                         break;
164                 default:
165                         break;
166         }
167         return service_properties;
168 }
169
170 /** Construct a new instance of a service
171 */
172
173 void *mlt_repository_create( mlt_repository this, mlt_profile profile, mlt_service_type type, const char *service, void *input )
174 {
175         mlt_properties properties = get_service_properties( this, type, service );
176         if ( properties != NULL )
177         {
178                 mlt_register_callback symbol_ptr = mlt_properties_get_data( properties, "symbol", NULL );
179
180                 // Construct the service
181                 return ( symbol_ptr != NULL ) ? symbol_ptr( profile, type, service, input ) : NULL;
182         }
183         return NULL;
184 }
185
186 /** Destroy a repository
187 */
188
189 void mlt_repository_close( mlt_repository this )
190 {
191         mlt_properties_close( this->consumers );
192         mlt_properties_close( this->filters );
193         mlt_properties_close( this->producers );
194         mlt_properties_close( this->transitions );
195         mlt_properties_close( &this->parent );
196         free( this );
197 }
198
199 /** Get the list of registered consumers
200 */
201
202 mlt_properties mlt_repository_consumers( mlt_repository self )
203 {
204         return self->consumers;
205 }
206
207 /** Get the list of registered filters
208 */
209
210 mlt_properties mlt_repository_filters( mlt_repository self )
211 {
212         return self->filters;
213 }
214
215 /** Get the list of registered producers
216 */
217
218 mlt_properties mlt_repository_producers( mlt_repository self )
219 {
220         return self->producers;
221 }
222
223 /** Get the list of registered transitions
224 */
225
226 mlt_properties mlt_repository_transitions( mlt_repository self )
227 {
228         return self->transitions;
229 }
230
231 /** Register the metadata for a service
232     IMPORTANT: mlt_repository will take responsibility for deallocating the metadata properties that you supply!
233 */
234 void mlt_repository_register_metadata( mlt_repository self, mlt_service_type type, const char *service, mlt_metadata_callback callback, void *callback_data )
235 {
236         mlt_properties service_properties = get_service_properties( self, type, service );
237         mlt_properties_set_data( service_properties, "metadata_cb", callback, 0, NULL, NULL );
238         mlt_properties_set_data( service_properties, "metadata_cb_data", callback_data, 0, NULL, NULL );
239 }
240
241 /** Get the metadata about a service
242     Returns NULL if service or its metadata are unavailable.
243 */
244
245 mlt_properties mlt_repository_metadata( mlt_repository self, mlt_service_type type, const char *service )
246 {
247         mlt_properties metadata = NULL;
248         mlt_properties properties = get_service_properties( self, type, service );
249
250         // If this is a valid service
251         if ( properties )
252         {
253                 // Lookup cached metadata
254                 metadata = mlt_properties_get_data( properties, "metadata", NULL );
255                 if ( ! metadata )
256                 {
257                         // Not cached, so get the registered metadata callback function
258                         mlt_metadata_callback callback = mlt_properties_get_data( properties, "metadata_cb", NULL );
259
260                         // If a metadata callback function is registered
261                         if ( callback )
262                         {
263                                 // Fetch the callback data arg
264                                 void *data = mlt_properties_get_data( properties, "metadata_cb_data", NULL );
265
266                                 // Fetch the metadata through the callback
267                                 metadata = callback( type, service, data );
268
269                                 // Cache the metadata
270                                 if ( metadata )
271                                         // Include dellocation and serialisation
272                                         mlt_properties_set_data( properties, "metadata", metadata, 0, ( mlt_destructor )mlt_properties_close, ( mlt_serialiser )mlt_properties_serialise_yaml );
273                         }
274                 }
275         }
276         return metadata;
277 }
278
279 static char *getenv_locale()
280 {
281         char *s = getenv( "LANGUAGE" );
282         if ( s && s[0] )
283                 return s;
284         s = getenv( "LC_ALL" );
285         if ( s && s[0] )
286                 return s;
287         s = getenv( "LC_MESSAGES" );
288         if ( s && s[0] )
289                 return s;
290         s = getenv( "LANG" );
291         if ( s && s[0] )
292                 return s;
293         return NULL;
294 }
295
296 /** Return a list of user-preferred language codes taken from environment variables.
297 */
298
299 mlt_properties mlt_repository_languages( mlt_repository self )
300 {
301         mlt_properties languages = mlt_properties_get_data( &self->parent, "languages", NULL );
302         if ( languages )
303                 return languages;
304
305         languages = mlt_properties_new();
306         char *locale = getenv_locale();
307         if ( locale )
308         {
309                 locale = strdup( locale );
310                 mlt_tokeniser tokeniser = mlt_tokeniser_init();
311                 int count = mlt_tokeniser_parse_new( tokeniser, locale, ":" );
312                 if ( count )
313                 {
314                         int i;
315                         for ( i = 0; i < count; i++ )
316                         {
317                                 char *locale = mlt_tokeniser_get_string( tokeniser, i );
318                                 if ( strcmp( locale, "C" ) == 0 || strcmp( locale, "POSIX" ) == 0 )
319                                         locale = "en";
320                                 else if ( strlen( locale ) > 2 )
321                                         locale[2] = 0;
322                                 char string[21];
323                                 snprintf( string, sizeof(string), "%d", i );
324                                 mlt_properties_set( languages, string, locale );
325                         }
326                 }
327                 else
328                 {
329                         mlt_properties_set( languages, "0", "en" );
330                 }
331                 free( locale );
332                 mlt_tokeniser_close( tokeniser );
333         }
334         else
335         {
336                 mlt_properties_set( languages, "0", "en" );
337         }
338         mlt_properties_set_data( &self->parent, "languages", languages, 0, ( mlt_destructor )mlt_properties_close, NULL );
339         return languages;
340 }