* repository.c -- provides a map between service and shared objects
* Copyright (C) 2003-2004 Ushodaya Enterprises Limited
* Author: Charles Yates <charles.yates@pandora.be>
+ * Contributor: Dan Dennedy <dan@dennedy.org>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
+ * This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "mlt_repository.h"
#include "mlt_properties.h"
+#include "mlt_tokeniser.h"
#include <stdio.h>
#include <stdlib.h>
struct mlt_repository_s
{
- struct mlt_properties_s parent;
- mlt_properties object_list;
+ struct mlt_properties_s parent; // a list of object files
+ mlt_properties consumers; // lists of entry points
+ mlt_properties filters;
+ mlt_properties producers;
+ mlt_properties transitions;
};
-static char *construct_full_file( char *output, char *prefix, char *file )
-{
- strcpy( output, prefix );
- if ( prefix[ strlen( prefix ) - 1 ] != '/' )
- strcat( output, "/" );
- strcat( output, file );
- return output;
-}
+/** Construct a new repository
+*/
-static char *chomp( char *input )
+mlt_repository mlt_repository_init( const char *directory )
{
- if ( input[ strlen( input ) - 1 ] == '\n' )
- input[ strlen( input ) - 1 ] = '\0';
- return input;
+ // Safety check
+ if ( directory == NULL || strcmp( directory, "" ) == 0 )
+ return NULL;
+
+ // Construct the repository
+ mlt_repository this = calloc( sizeof( struct mlt_repository_s ), 1 );
+ mlt_properties_init( &this->parent, this );
+ this->consumers = mlt_properties_new();
+ this->filters = mlt_properties_new();
+ this->producers = mlt_properties_new();
+ this->transitions = mlt_properties_new();
+
+ // Get the directory list
+ mlt_properties dir = mlt_properties_new();
+ int count = mlt_properties_dir_list( dir, directory, NULL, 0 );
+ int i;
+
+ // Iterate over files
+ for ( i = 0; i < count; i++ )
+ {
+ int flags = RTLD_NOW;
+ const char *object_name = mlt_properties_get_value( dir, i);
+
+ // Very temporary hack to allow the quicktime plugins to work
+ // TODO: extend repository to allow this to be used on a case by case basis
+ if ( strstr( object_name, "libmltkino" ) )
+ flags |= RTLD_GLOBAL;
+
+ // Open the shared object
+ void *object = dlopen( object_name, flags );
+ if ( object != NULL )
+ {
+ // Get the registration function
+ mlt_repository_callback symbol_ptr = dlsym( object, "mlt_register" );
+
+ // Call the registration function
+ if ( symbol_ptr != NULL )
+ {
+ symbol_ptr( this );
+
+ // Register the object file for closure
+ mlt_properties_set_data( &this->parent, object_name, object, 0, ( mlt_destructor )dlclose, NULL );
+ }
+ else
+ {
+ dlclose( object );
+ }
+ }
+ else if ( strstr( object_name, "libmlt" ) )
+ {
+ fprintf( stderr, "%s, %s: failed to dlopen %s\n", __FILE__, __FUNCTION__, object_name );
+ }
+ }
+
+ mlt_properties_close( dir );
+
+ return this;
}
-static mlt_properties construct_object( char *prefix, char *id )
+static mlt_properties new_service( void *symbol )
{
- mlt_properties output = calloc( sizeof( struct mlt_properties_s ), 1 );
- mlt_properties_init( output, NULL );
- mlt_properties_set( output, "prefix", prefix );
- mlt_properties_set( output, "id", id );
- return output;
+ mlt_properties properties = mlt_properties_new();
+ mlt_properties_set_data( properties, "symbol", symbol, 0, NULL, NULL );
+ return properties;
}
-static mlt_properties construct_service( mlt_properties object, char *id )
+/** Register a service with the repository
+ Typically, this is invoked by a module within its mlt_register().
+*/
+
+void mlt_repository_register( mlt_repository this, mlt_service_type service_type, const char *service, mlt_register_callback symbol )
{
- mlt_properties output = calloc( sizeof( struct mlt_properties_s ), 1 );
- mlt_properties_init( output, NULL );
- mlt_properties_set_data( output, "object", object, 0, NULL, NULL );
- mlt_properties_set( output, "id", id );
- return output;
+ // Add the entry point to the corresponding service list
+ switch ( service_type )
+ {
+ case consumer_type:
+ mlt_properties_set_data( this->consumers, service, new_service( symbol ), 0, ( mlt_destructor )mlt_properties_close, NULL );
+ break;
+ case filter_type:
+ mlt_properties_set_data( this->filters, service, new_service( symbol ), 0, ( mlt_destructor )mlt_properties_close, NULL );
+ break;
+ case producer_type:
+ mlt_properties_set_data( this->producers, service, new_service( symbol ), 0, ( mlt_destructor )mlt_properties_close, NULL );
+ break;
+ case transition_type:
+ mlt_properties_set_data( this->transitions, service, new_service( symbol ), 0, ( mlt_destructor )mlt_properties_close, NULL );
+ break;
+ default:
+ break;
+ }
}
-void *construct_instance( mlt_properties service_properties, char *symbol, void *input )
+static mlt_properties get_service_properties( mlt_repository this, mlt_service_type type, const char *service )
{
- // Extract the service
- char *service = mlt_properties_get( service_properties, "id" );
-
- // Get the object properties
- void *object_properties = mlt_properties_get_data( service_properties, "object", NULL );
-
- // Get the dlopen'd object
- void *object = mlt_properties_get_data( object_properties, "dlopen", NULL );
+ mlt_properties service_properties = NULL;
- // Get the dlsym'd symbol
- void *( *symbol_ptr )( char *, void * ) = mlt_properties_get_data( object_properties, symbol, NULL );
-
- // Check that we have object and open if we don't
- if ( object == NULL )
+ // Get the entry point from the corresponding service list
+ switch ( type )
{
- char full_file[ 512 ];
-
- // Get the prefix and id of the shared object
- char *prefix = mlt_properties_get( object_properties, "prefix" );
- char *file = mlt_properties_get( object_properties, "id" );
-
- // Construct the full file
- construct_full_file( full_file, prefix, file );
-
- // Open the shared object
- object = dlopen( full_file, RTLD_NOW | RTLD_GLOBAL );
-
- // Set it on the properties
- mlt_properties_set_data( object_properties, "dlopen", object, 0, ( void (*)( void * ) )dlclose, NULL );
+ case consumer_type:
+ service_properties = mlt_properties_get_data( this->consumers, service, NULL );
+ break;
+ case filter_type:
+ service_properties = mlt_properties_get_data( this->filters, service, NULL );
+ break;
+ case producer_type:
+ service_properties = mlt_properties_get_data( this->producers, service, NULL );
+ break;
+ case transition_type:
+ service_properties = mlt_properties_get_data( this->transitions, service, NULL );
+ break;
+ default:
+ break;
}
+ return service_properties;
+}
- // Now check if we have this symbol pointer
- if ( object != NULL && symbol_ptr == NULL )
- {
- // Construct it now
- symbol_ptr = dlsym( object, symbol );
+/** Construct a new instance of a service
+*/
- // Set it on the properties
- mlt_properties_set_data( object_properties, "dlsym", symbol_ptr, 0, NULL, NULL );
+void *mlt_repository_create( mlt_repository this, mlt_profile profile, mlt_service_type type, const char *service, void *input )
+{
+ mlt_properties properties = get_service_properties( this, type, service );
+ if ( properties != NULL )
+ {
+ mlt_register_callback symbol_ptr = mlt_properties_get_data( properties, "symbol", NULL );
+
+ // Construct the service
+ return ( symbol_ptr != NULL ) ? symbol_ptr( profile, type, service, input ) : NULL;
}
+ return NULL;
+}
+
+/** Destroy a repository
+*/
- // Construct the service
- return symbol_ptr != NULL ? symbol_ptr( service, input ) : NULL;
+void mlt_repository_close( mlt_repository this )
+{
+ mlt_properties_close( this->consumers );
+ mlt_properties_close( this->filters );
+ mlt_properties_close( this->producers );
+ mlt_properties_close( this->transitions );
+ mlt_properties_close( &this->parent );
+ free( this );
}
-void destroy_properties( void *arg )
+/** Get the list of registered consumers
+*/
+
+mlt_properties mlt_repository_consumers( mlt_repository self )
{
- mlt_properties_close( arg );
- free( arg );
+ return self->consumers;
}
-mlt_repository mlt_repository_init( mlt_properties object_list, char *prefix, char *data, char *symbol )
+/** Get the list of registered filters
+*/
+
+mlt_properties mlt_repository_filters( mlt_repository self )
{
- char full_file[ 512 ];
- FILE *file;
+ return self->filters;
+}
- // Construct the repository
- mlt_repository this = calloc( sizeof( struct mlt_repository_s ), 1 );
- mlt_properties_init( &this->parent, NULL );
+/** Get the list of registered producers
+*/
- // Add the symbol to THIS repository properties.
- mlt_properties_set( &this->parent, "_symbol", symbol );
+mlt_properties mlt_repository_producers( mlt_repository self )
+{
+ return self->producers;
+}
- // Asociate the repository to the global object_list
- this->object_list = object_list;
+/** Get the list of registered transitions
+*/
- // Construct full file
- construct_full_file( full_file, prefix, data );
+mlt_properties mlt_repository_transitions( mlt_repository self )
+{
+ return self->transitions;
+}
- // Open the file
- file = fopen( full_file, "r" );
+/** Register the metadata for a service
+ IMPORTANT: mlt_repository will take responsibility for deallocating the metadata properties that you supply!
+*/
+void mlt_repository_register_metadata( mlt_repository self, mlt_service_type type, const char *service, mlt_metadata_callback callback, void *callback_data )
+{
+ mlt_properties service_properties = get_service_properties( self, type, service );
+ mlt_properties_set_data( service_properties, "metadata_cb", callback, 0, NULL, NULL );
+ mlt_properties_set_data( service_properties, "metadata_cb_data", callback_data, 0, NULL, NULL );
+}
- // Parse the contents
- if ( file != NULL )
- {
- char full[ 512 ];
- char service[ 256 ];
- char object[ 256 ];
+/** Get the metadata about a service
+ Returns NULL if service or its metadata are unavailable.
+*/
- while( fgets( full, 512, file ) )
+mlt_properties mlt_repository_metadata( mlt_repository self, mlt_service_type type, const char *service )
+{
+ mlt_properties metadata = NULL;
+ mlt_properties properties = get_service_properties( self, type, service );
+
+ // If this is a valid service
+ if ( properties )
+ {
+ // Lookup cached metadata
+ metadata = mlt_properties_get_data( properties, "metadata", NULL );
+ if ( ! metadata )
{
- chomp( full );
+ // Not cached, so get the registered metadata callback function
+ mlt_metadata_callback callback = mlt_properties_get_data( properties, "metadata_cb", NULL );
- if ( full[ 0 ] != '#' && full[ 0 ] != '\0' && sscanf( full, "%s %s", service, object ) == 2 )
+ // If a metadata callback function is registered
+ if ( callback )
{
- // Get the object properties first
- mlt_properties object_properties = mlt_properties_get_data( object_list, object, NULL );
-
- // If their are no properties, create them now
- if ( object_properties == NULL )
- {
- // Construct the object
- object_properties = construct_object( prefix, object );
-
- // Add it to the object list
- mlt_properties_set_data( object_list, object, object_properties, 0, destroy_properties, NULL );
- }
-
- // Now construct a property for the service
- mlt_properties service_properties = construct_service( object_properties, service );
-
- // Add it to the repository
- mlt_properties_set_data( &this->parent, service, service_properties, 0, destroy_properties, NULL );
+ // Fetch the callback data arg
+ void *data = mlt_properties_get_data( properties, "metadata_cb_data", NULL );
+
+ // Fetch the metadata through the callback
+ metadata = callback( type, service, data );
+
+ // Cache the metadata
+ if ( metadata )
+ // Include dellocation and serialisation
+ mlt_properties_set_data( properties, "metadata", metadata, 0, ( mlt_destructor )mlt_properties_close, ( mlt_serialiser )mlt_properties_serialise_yaml );
}
}
-
- // Close the file
- fclose( file );
}
-
- return this;
+ return metadata;
}
-void *mlt_repository_fetch( mlt_repository this, char *service, void *input )
+static char *getenv_locale()
{
- // Get the service properties
- mlt_properties service_properties = mlt_properties_get_data( &this->parent, service, NULL );
-
- // If the service exists
- if ( service_properties != NULL )
- {
- // Get the symbol that is used to generate this service
- char *symbol = mlt_properties_get( &this->parent, "_symbol" );
-
- // Now get an instance of the service
- return construct_instance( service_properties, symbol, input );
- }
-
+ char *s = getenv( "LANGUAGE" );
+ if ( s && s[0] )
+ return s;
+ s = getenv( "LC_ALL" );
+ if ( s && s[0] )
+ return s;
+ s = getenv( "LC_MESSAGES" );
+ if ( s && s[0] )
+ return s;
+ s = getenv( "LANG" );
+ if ( s && s[0] )
+ return s;
return NULL;
}
-void mlt_repository_close( mlt_repository this )
+/** Return a list of user-preferred language codes taken from environment variables.
+*/
+
+mlt_properties mlt_repository_languages( mlt_repository self )
{
- mlt_properties_close( &this->parent );
- free( this );
+ mlt_properties languages = mlt_properties_get_data( &self->parent, "languages", NULL );
+ if ( languages )
+ return languages;
+
+ languages = mlt_properties_new();
+ char *locale = getenv_locale();
+ if ( locale )
+ {
+ locale = strdup( locale );
+ mlt_tokeniser tokeniser = mlt_tokeniser_init();
+ int count = mlt_tokeniser_parse_new( tokeniser, locale, ":" );
+ if ( count )
+ {
+ int i;
+ for ( i = 0; i < count; i++ )
+ {
+ char *locale = mlt_tokeniser_get_string( tokeniser, i );
+ if ( strcmp( locale, "C" ) == 0 || strcmp( locale, "POSIX" ) == 0 )
+ locale = "en";
+ else if ( strlen( locale ) > 2 )
+ locale[2] = 0;
+ char string[21];
+ snprintf( string, sizeof(string), "%d", i );
+ mlt_properties_set( languages, string, locale );
+ }
+ }
+ else
+ {
+ mlt_properties_set( languages, "0", "en" );
+ }
+ free( locale );
+ mlt_tokeniser_close( tokeniser );
+ }
+ else
+ {
+ mlt_properties_set( languages, "0", "en" );
+ }
+ mlt_properties_set_data( &self->parent, "languages", languages, 0, ( mlt_destructor )mlt_properties_close, NULL );
+ return languages;
}
-
-