Пример #1
0
void mlt_repository_close( mlt_repository self )
{
	mlt_properties_close( self->consumers );
	mlt_properties_close( self->filters );
	mlt_properties_close( self->producers );
	mlt_properties_close( self->transitions );
	mlt_properties_close( &self->parent );
	free( self );
}
Пример #2
0
mlt_repository mlt_repository_init( const char *directory )
{
	// Safety check
	if ( directory == NULL || strcmp( directory, "" ) == 0 )
		return NULL;

	// Construct the repository
	mlt_repository self = calloc( 1, sizeof( struct mlt_repository_s ));
	mlt_properties_init( &self->parent, self );
	self->consumers = mlt_properties_new();
	self->filters = mlt_properties_new();
	self->producers = mlt_properties_new();
	self->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( self );

				// Register the object file for closure
				mlt_properties_set_data( &self->parent, object_name, object, 0, ( mlt_destructor )dlclose, NULL );
			}
			else
			{
				dlclose( object );
			}
		}
		else if ( strstr( object_name, "libmlt" ) )
		{
			mlt_log( NULL, MLT_LOG_WARNING, "%s: failed to dlopen %s\n  (%s)\n", __FUNCTION__, object_name, dlerror() );
		}
	}

	mlt_properties_close( dir );

	return self;
}
Пример #3
0
void mlt_service_close( mlt_service self )
{
	if ( self != NULL && mlt_properties_dec_ref( MLT_SERVICE_PROPERTIES( self ) ) <= 0 )
	{
		if ( self->close != NULL )
		{
			self->close( self->close_object );
		}
		else
		{
			mlt_service_base *base = self->local;
			int i = 0;
			int count = base->filter_count;
			mlt_events_block( MLT_SERVICE_PROPERTIES( self ), self );
			while( count -- )
				mlt_service_detach( self, base->filters[ 0 ] );
			free( base->filters );
			for ( i = 0; i < base->count; i ++ )
				if ( base->in[ i ] != NULL )
					mlt_service_close( base->in[ i ] );
			self->parent.close = NULL;
			free( base->in );
			pthread_mutex_destroy( &base->mutex );
			free( base );
			mlt_properties_close( &self->parent );
		}
	}
}
Пример #4
0
void mlt_cache_close( mlt_cache cache )
{
    if ( cache )
    {
        while ( cache->count-- )
        {
            void *object = cache->current[ cache->count ];
            mlt_log( NULL, MLT_LOG_DEBUG, "%s: %d = %p\n", __FUNCTION__, cache->count, object );
            cache_object_close( cache, object, NULL );
        }
        mlt_properties_close( cache->active );
        mlt_properties_close( cache->garbage );
        pthread_mutex_destroy( &cache->mutex );
        free( cache );
    }
}
Пример #5
0
mlt_profile mlt_profile_load_file( const char *file )
{
	mlt_profile profile = NULL;

	// Load the profile as properties
	mlt_properties properties = mlt_properties_load( file );
	if ( properties )
	{
		// Simple check if the profile is valid
		if ( mlt_properties_get_int( properties, "width" ) )
		{
			profile = mlt_profile_load_properties( properties );

			// Set MLT_PROFILE to basename
			char *filename = strdup( file );
			mlt_environment_set( "MLT_PROFILE", basename( filename ) );
			set_mlt_normalisation( basename( filename ) );
			free( filename );
		}
		mlt_properties_close( properties );
	}

	// Set MLT_NORMALISATION to appease legacy modules
	char *profile_name = mlt_environment( "MLT_PROFILE" );
	set_mlt_normalisation( profile_name );
	return profile;
}
Пример #6
0
void mlt_parser_close( mlt_parser self )
{
	if ( self != NULL )
	{
		mlt_properties_close( &self->parent );
		free( self );
	}
}
Пример #7
0
static void producer_close( mlt_producer parent )
{
	producer_qimage self = parent->child;
	parent->close = NULL;
	mlt_service_cache_purge( MLT_PRODUCER_SERVICE(parent) );
	mlt_producer_close( parent );
	mlt_properties_close( self->filenames );
	free( self );
}
Пример #8
0
static void query_presets()
{
    mlt_properties presets = mlt_repository_presets();
    fprintf( stdout, "---\npresets:\n" );
    if ( presets )
    {
        int j;
        for ( j = 0; j < mlt_properties_count( presets ); j++ )
            fprintf( stdout, "  - %s\n", mlt_properties_get_name( presets, j ) );
    }
    fprintf( stdout, "...\n" );
    mlt_properties_close( presets );
}
Пример #9
0
static void query_profiles()
{
    mlt_properties profiles = mlt_profile_list();
    fprintf( stdout, "---\nprofiles:\n" );
    if ( profiles )
    {
        int j;
        for ( j = 0; j < mlt_properties_count( profiles ); j++ )
            fprintf( stdout, "  - %s\n", mlt_properties_get_name( profiles, j ) );
    }
    fprintf( stdout, "...\n" );
    mlt_properties_close( profiles );
}
Пример #10
0
void mlt_frame_close( mlt_frame self )
{
	if ( self != NULL && mlt_properties_dec_ref( MLT_FRAME_PROPERTIES( self ) ) <= 0 )
	{
		mlt_deque_close( self->stack_image );
		mlt_deque_close( self->stack_audio );
		while( mlt_deque_peek_back( self->stack_service ) )
			mlt_service_close( mlt_deque_pop_back( self->stack_service ) );
		mlt_deque_close( self->stack_service );
		mlt_properties_close( &self->parent );
		free( self );
	}
}
Пример #11
0
void mlt_factory_close( )
{
	if ( mlt_directory != NULL )
	{
		mlt_properties_close( event_object );
		event_object = NULL;
#if !defined(WIN32)
		// XXX something in here is causing Shotcut/Win32 to not exit completely
		// under certain conditions: e.g. play a playlist.
		mlt_properties_close( global_properties );
		global_properties = NULL;
#endif
		if ( repository )
		{
			mlt_repository_close( repository );
			repository = NULL;
		}
		free( mlt_directory );
		mlt_directory = NULL;
		mlt_pool_close( );
	}
}
Пример #12
0
static void destroy_data_queue( void *arg )
{
	if ( arg != NULL )
	{
		// Assign the correct type
		mlt_deque queue = arg;

		// Iterate through each item and destroy them
		while ( mlt_deque_peek_front( queue ) != NULL )
			mlt_properties_close( mlt_deque_pop_back( queue ) );

		// Close the deque
		mlt_deque_close( queue );
	}
}
Пример #13
0
static void query_preset( const char *id )
{
    mlt_properties presets = mlt_repository_presets();
    mlt_properties preset = mlt_properties_get_data( presets, id, NULL );
    if ( preset )
    {
        char *s = mlt_properties_serialise_yaml( preset );
        fprintf( stdout, "%s", s );
        free( s );
    }
    else
    {
        fprintf( stdout, "# No metadata for preset \"%s\"\n", id );
    }
    mlt_properties_close( presets );
}
Пример #14
0
static void query_profile( const char *id )
{
    mlt_properties profiles = mlt_profile_list();
    mlt_properties profile = mlt_properties_get_data( profiles, id, NULL );
    if ( profile )
    {
        char *s = mlt_properties_serialise_yaml( profile );
        fprintf( stdout, "%s", s );
        free( s );
    }
    else
    {
        fprintf( stdout, "# No metadata for profile \"%s\"\n", id );
    }
    mlt_properties_close( profiles );
}
Пример #15
0
static void check_thread_safe( mlt_properties properties, const char *name )
{
	char dirname[PATH_MAX];
	snprintf( dirname, PATH_MAX, "%s/frei0r/not_thread_safe.txt", mlt_environment( "MLT_DATA" ) );
	mlt_properties not_thread_safe = mlt_properties_load( dirname );
	int i;

	for ( i = 0; i < mlt_properties_count( not_thread_safe ); i++ )
	{
		if ( strcmp( name, mlt_properties_get_name( not_thread_safe, i ) ) == 0 )
		{
			mlt_properties_set_int( properties, "_not_thread_safe", 1 );
			break;
		}
	}
	mlt_properties_close( not_thread_safe );
}
Пример #16
0
mlt_properties mlt_profile_list( )
{
	char *filename = NULL;
	const char *prefix = getenv( "MLT_PROFILES_PATH" );
	mlt_properties properties = mlt_properties_new();
	mlt_properties dir = mlt_properties_new();
	int sort = 1;
	const char *wildcard = NULL;
	int i;

	// Load from $datadir/mlt/profiles if no env var
	if ( prefix == NULL )
	{
		prefix = mlt_environment( "MLT_DATA" );
		filename = calloc( 1, strlen( prefix ) + strlen( PROFILES_DIR ) + 1 );
		strcpy( filename, prefix );
		strcat( filename, PROFILES_DIR );
		prefix = filename;
	}

	mlt_properties_dir_list( dir, prefix, wildcard, sort );

	for ( i = 0; i < mlt_properties_count( dir ); i++ )
	{
		char *filename = mlt_properties_get_value( dir, i );
		char *profile_name = basename( filename );
		if ( profile_name[0] != '.' && strcmp( profile_name, "Makefile" ) &&
		     profile_name[ strlen( profile_name ) - 1 ] != '~' )
		{
			mlt_properties profile = mlt_properties_load( filename );
			if ( profile )
			{
				mlt_properties_set_data( properties, profile_name, profile, 0,
					(mlt_destructor) mlt_properties_close, NULL );
			}
		}
	}
	mlt_properties_close( dir );
	if ( filename )
		free( filename );

	return properties;
}
Пример #17
0
mlt_profile mlt_profile_load_string( const char *string )
{
	mlt_properties properties = mlt_properties_new();
	mlt_profile profile = NULL;

	if ( properties )
	{
		const char *p = string;
		while ( p )
		{
			if ( strcmp( p, "" ) && p[ 0 ] != '#' )
				mlt_properties_parse( properties, p );
			p = strchr( p, '\n' );
			if ( p ) p++;
		}
		profile = mlt_profile_load_properties( properties );
		mlt_properties_close( properties );
	}
	return profile;
}
Пример #18
0
static mlt_profile mlt_profile_select( const char *name )
{
	char *filename = NULL;
	const char *prefix = getenv( "MLT_PROFILES_PATH" );
	mlt_properties properties = mlt_properties_load( name );
	mlt_profile profile = NULL;

	// Try to load from file specification
	if ( properties && mlt_properties_get_int( properties, "width" ) )
	{
		filename = calloc( 1, strlen( name ) + 1 );
	}
	// Load from $datadir/mlt/profiles
	else if ( prefix == NULL )
	{
		prefix = mlt_environment( "MLT_DATA" );
		filename = calloc( 1, strlen( prefix ) + strlen( PROFILES_DIR ) + strlen( name ) + 1 );
		strcpy( filename, prefix );
		strcat( filename, PROFILES_DIR );
	}
	// Use environment variable instead
	else
	{
		filename = calloc( 1, strlen( prefix ) + strlen( name ) + 2 );
		strcpy( filename, prefix );
		if ( filename[ strlen( filename ) - 1 ] != '/' )
			filename[ strlen( filename ) ] = '/';
	}

	// Finish loading
	strcat( filename, name );
	profile = mlt_profile_load_file( filename );

	// Cleanup
	mlt_properties_close( properties );
	free( filename );

	return profile;
}
Пример #19
0
void process_queue( mlt_deque data_queue, mlt_frame frame, mlt_filter filter )
{
	if ( data_queue != NULL )
	{
		// Create a new queue for those that we can't handle
		mlt_deque temp_queue = mlt_deque_init( );

		// Iterate through each entry on the queue
		while ( mlt_deque_peek_front( data_queue ) != NULL )
		{
			// Get the data feed
			mlt_properties feed = mlt_deque_pop_front( data_queue );

			if ( mlt_properties_get( MLT_FILTER_PROPERTIES( filter ), "debug" ) != NULL )
				mlt_properties_debug( feed, mlt_properties_get( MLT_FILTER_PROPERTIES( filter ), "debug" ), stderr );

			// Process the data feed...
			if ( process_feed( feed, filter, frame ) == 0 )
				mlt_properties_close( feed );
			else
				mlt_deque_push_back( temp_queue, feed );
		}
	
		// Now put the unprocessed feeds back on the stack
		while ( mlt_deque_peek_front( temp_queue ) )
		{
			// Get the data feed
			mlt_properties feed = mlt_deque_pop_front( temp_queue );
	
			// Put it back on the data queue
			mlt_deque_push_back( data_queue, feed );
		}
	
		// Close the temporary queue
		mlt_deque_close( temp_queue );
	}
}
Пример #20
0
static void add_parameters( mlt_properties params, void *object, int req_flags, const char *unit, const char *subclass )
{
	const AVOption *opt = NULL;

	// For each AVOption on the AVClass object
#if LIBAVUTIL_VERSION_INT >= ((51<<16)+(12<<8)+0)
	while ( ( opt = av_opt_next( object, opt ) ) )
#else
	while ( ( opt = av_next_option( object, opt ) ) )
#endif
	{
		// If matches flags and not a binary option (not supported by Mlt)
		if ( !( opt->flags & req_flags ) || ( opt->type == AV_OPT_TYPE_BINARY ) )
            continue;

		// Ignore constants (keyword values)
		if ( !unit && opt->type == AV_OPT_TYPE_CONST )
			continue;
		// When processing a groups of options (unit)...
		// ...ignore non-constants
		else if ( unit && opt->type != AV_OPT_TYPE_CONST )
			continue;
		// ...ignore constants not in this group
		else if ( unit && opt->type == AV_OPT_TYPE_CONST && strcmp( unit, opt->unit ) )
			continue;
		// ..add constants to the 'values' sequence
		else if ( unit && opt->type == AV_OPT_TYPE_CONST )
		{
			char key[20];
			snprintf( key, 20, "%d", mlt_properties_count( params ) );
			mlt_properties_set( params, key, opt->name );
			continue;
		}

		// Create a map for this option.
		mlt_properties p = mlt_properties_new();
		char key[20];
		snprintf( key, 20, "%d", mlt_properties_count( params ) );
		// Add the map to the 'parameters' sequence.
		mlt_properties_set_data( params, key, p, 0, (mlt_destructor) mlt_properties_close, NULL );

		// Add the parameter metadata for this AVOption.
		mlt_properties_set( p, "identifier", opt->name );
		if ( opt->help )
		{
			if ( subclass )
			{
				char *s = malloc( strlen( opt->help ) + strlen( subclass ) + 4 );
				strcpy( s, opt->help );
				strcat( s, " (" );
				strcat( s, subclass );
				strcat( s, ")" );
				mlt_properties_set( p, "description", s );
				free( s );
			}
			else
				mlt_properties_set( p, "description", opt->help );
		}

        switch ( opt->type )
		{
		case AV_OPT_TYPE_FLAGS:
			mlt_properties_set( p, "type", "string" );
			mlt_properties_set( p, "format", "flags" );
			break;
		case AV_OPT_TYPE_INT:
			if ( !opt->unit )
			{
				mlt_properties_set( p, "type", "integer" );
				if ( opt->min != INT_MIN )
					mlt_properties_set_int( p, "minimum", (int) opt->min );
				if ( opt->max != INT_MAX )
					mlt_properties_set_int( p, "maximum", (int) opt->max );
#if LIBAVUTIL_VERSION_MAJOR > 50
				mlt_properties_set_int( p, "default", (int) opt->default_val.dbl );
#endif
			}
			else
			{
				mlt_properties_set( p, "type", "string" );
				mlt_properties_set( p, "format", "integer or keyword" );
			}
			break;
		case AV_OPT_TYPE_INT64:
			mlt_properties_set( p, "type", "integer" );
			mlt_properties_set( p, "format", "64-bit" );
			if ( opt->min != INT64_MIN )
				mlt_properties_set_int64( p, "minimum", (int64_t) opt->min );
			if ( opt->max != INT64_MAX )
			mlt_properties_set_int64( p, "maximum", (int64_t) opt->max );
#if LIBAVUTIL_VERSION_MAJOR > 50
			mlt_properties_set_int64( p, "default", (int64_t) opt->default_val.dbl );
#endif
			break;
		case AV_OPT_TYPE_FLOAT:
			mlt_properties_set( p, "type", "float" );
			if ( opt->min != FLT_MIN && opt->min != -340282346638528859811704183484516925440.0 )
				mlt_properties_set_double( p, "minimum", opt->min );
			if ( opt->max != FLT_MAX )
				mlt_properties_set_double( p, "maximum", opt->max );
#if LIBAVUTIL_VERSION_MAJOR > 50
			mlt_properties_set_double( p, "default", opt->default_val.dbl );
#endif
			break;
		case AV_OPT_TYPE_DOUBLE:
			mlt_properties_set( p, "type", "float" );
			mlt_properties_set( p, "format", "double" );
			if ( opt->min != DBL_MIN )
				mlt_properties_set_double( p, "minimum", opt->min );
			if ( opt->max != DBL_MAX )
				mlt_properties_set_double( p, "maximum", opt->max );
#if LIBAVUTIL_VERSION_MAJOR > 50
			mlt_properties_set_double( p, "default", opt->default_val.dbl );
#endif
			break;
		case AV_OPT_TYPE_STRING:
			mlt_properties_set( p, "type", "string" );
#if LIBAVUTIL_VERSION_MAJOR > 50
			mlt_properties_set( p, "default", opt->default_val.str );
#endif
			break;
		case AV_OPT_TYPE_RATIONAL:
			mlt_properties_set( p, "type", "string" );
			mlt_properties_set( p, "format", "numerator:denominator" );
			break;
		case AV_OPT_TYPE_CONST:
		default:
			mlt_properties_set( p, "type", "integer" );
			mlt_properties_set( p, "format", "constant" );
			break;
        }
		// If the option belongs to a group (unit) and is not a constant (keyword value)
		if ( opt->unit && opt->type != AV_OPT_TYPE_CONST )
		{
			// Create a 'values' sequence.
			mlt_properties values = mlt_properties_new();

			// Recurse to add constants in this group to the 'values' sequence.
			add_parameters( values, object, req_flags, opt->unit, NULL );
			if ( mlt_properties_count( values ) )
				mlt_properties_set_data( p, "values", values, 0, (mlt_destructor) mlt_properties_close, NULL );
			else
				mlt_properties_close( values );
		}
	}
}
Пример #21
0
static void foreach_consumer_init( mlt_consumer consumer )
{
    const char *resource = mlt_properties_get( MLT_CONSUMER_PROPERTIES(consumer), "resource" );
    mlt_properties properties = mlt_properties_parse_yaml( resource );
    char key[20];
    int index = 0;

    if ( mlt_properties_get_data( MLT_CONSUMER_PROPERTIES(consumer), "0", NULL ) )
    {
        // Properties set directly by application
        mlt_properties p;

        if ( properties )
            mlt_properties_close( properties );
        properties = MLT_CONSUMER_PROPERTIES(consumer);
        do {
            snprintf( key, sizeof(key), "%d", index );
            if ( ( p = mlt_properties_get_data( properties, key, NULL ) ) )
                generate_consumer( consumer, p, index++ );
        } while ( p );
    }
    else if ( properties && mlt_properties_get_data( properties, "0", NULL ) )
    {
        // YAML file supplied
        mlt_properties p;

        do {
            snprintf( key, sizeof(key), "%d", index );
            if ( ( p = mlt_properties_get_data( properties, key, NULL ) ) )
                generate_consumer( consumer, p, index++ );
        } while ( p );
        mlt_properties_close( properties );
    }
    else
    {
        // properties file supplied or properties on this consumer
        const char *s;

        if ( properties )
            mlt_properties_close( properties );
        if ( resource )
            properties = mlt_properties_load( resource );
        else
            properties = MLT_CONSUMER_PROPERTIES( consumer );

        do {
            snprintf( key, sizeof(key), "%d", index );
            if ( ( s = mlt_properties_get( properties, key ) ) )
            {
                mlt_properties p = mlt_properties_new();
                int i, count;

                if ( !p ) break;
                mlt_properties_set( p, "mlt_service", mlt_properties_get( properties, key ) );
                snprintf( key, sizeof(key), "%d.", index );

                count = mlt_properties_count( properties );
                for ( i = 0; i < count; i++ )
                {
                    char *name = mlt_properties_get_name( properties, i );
                    if ( !strncmp( name, key, strlen(key) ) )
                        mlt_properties_set( p, name + strlen(key),
                                            mlt_properties_get_value( properties, i ) );
                }
                generate_consumer( consumer, p, index++ );
                mlt_properties_close( p );
            }
        } while ( s );
        if ( resource )
            mlt_properties_close( properties );
    }
}
Пример #22
0
static mlt_properties fill_param_info ( mlt_service_type type, const char *service_name, char *name )
{
	char file[ PATH_MAX ];
	char servicetype[ 1024 ]="";
	struct stat stat_buff;

	switch ( type ) {
		case producer_type:
			strcpy ( servicetype , "producer" );
			break;
		case filter_type:
			strcpy ( servicetype , "filter" );
			break;
		case transition_type:
			strcpy ( servicetype , "transition" ) ;
			break;
		default:
			strcpy ( servicetype , "" );
	};

	snprintf( file, PATH_MAX, "%s/frei0r/%s_%s.yml", mlt_environment( "MLT_DATA" ), servicetype, service_name );
	memset(&stat_buff, 0, sizeof(stat_buff));
	stat(file,&stat_buff);

	if (S_ISREG(stat_buff.st_mode)){
		return mlt_properties_parse_yaml( file );
	}

	void* handle=dlopen(name,RTLD_LAZY);
	if (!handle) return NULL;
	void (*plginfo)(f0r_plugin_info_t*)=dlsym(handle,"f0r_get_plugin_info");
	void (*param_info)(f0r_param_info_t*,int param_index)=dlsym(handle,"f0r_get_param_info");
	void (*f0r_init)(void)=dlsym(handle,"f0r_init");
	void (*f0r_deinit)(void)=dlsym(handle,"f0r_deinit");
	f0r_instance_t (*f0r_construct)(unsigned int , unsigned int)=dlsym(handle, "f0r_construct");
	void (*f0r_destruct)(f0r_instance_t)=dlsym(handle, "f0r_destruct");
	void (*f0r_get_param_value)(f0r_instance_t instance, f0r_param_t param, int param_index)=dlsym(handle,"f0r_get_param_value" );
	if (!plginfo || !param_info) {
		dlclose(handle);
		return NULL;
	}
	mlt_properties metadata = mlt_properties_new();
	f0r_plugin_info_t info;
	char string[48];
	int j=0;

	f0r_init();
	f0r_instance_t instance = f0r_construct(720, 576);
	if (!instance) {
		f0r_deinit();
		dlclose(handle);
		mlt_properties_close(metadata);
		return NULL;
	}
	plginfo(&info);
	snprintf ( string, sizeof(string) , "%d" , info.minor_version );
	mlt_properties_set_double ( metadata, "schema_version" , 0.1 );
	mlt_properties_set ( metadata, "title" , info.name );
	mlt_properties_set_double ( metadata, "version",
		info.major_version +  info.minor_version / pow( 10, strlen( string ) ) );
	mlt_properties_set ( metadata, "identifier" , service_name );
	mlt_properties_set ( metadata, "description" , info.explanation );
	mlt_properties_set ( metadata, "creator" , info.author );
	switch (type){
		case producer_type:
			mlt_properties_set ( metadata, "type" , "producer" );
			break;
		case filter_type:
			mlt_properties_set ( metadata, "type" , "filter" );
			break;
		case transition_type:
			mlt_properties_set ( metadata, "type" , "transition" );
			break;
		default:
			break;
	}

	mlt_properties tags = mlt_properties_new ( );
	mlt_properties_set_data ( metadata , "tags" , tags , 0 , ( mlt_destructor )mlt_properties_close, NULL );
	mlt_properties_set ( tags , "0" , "Video" );

	mlt_properties parameter = mlt_properties_new ( );
	mlt_properties_set_data ( metadata , "parameters" , parameter , 0 , ( mlt_destructor )mlt_properties_close, NULL );

	for (j=0;j<info.num_params;j++){
		snprintf ( string , sizeof(string), "%d" , j );
		mlt_properties pnum = mlt_properties_new ( );
		mlt_properties_set_data ( parameter , string , pnum , 0 , ( mlt_destructor )mlt_properties_close, NULL );
		f0r_param_info_t paraminfo;
		param_info(&paraminfo,j);
		mlt_properties_set ( pnum , "identifier" , string );
		mlt_properties_set ( pnum , "title" , paraminfo.name );
		mlt_properties_set ( pnum , "description" , paraminfo.explanation);
		if ( paraminfo.type == F0R_PARAM_DOUBLE ){
			double deflt = 0;
			mlt_properties_set ( pnum , "type" , "float" );
			mlt_properties_set ( pnum , "minimum" , "0" );
			mlt_properties_set ( pnum , "maximum" , "1" );
			f0r_get_param_value( instance, &deflt, j);
			mlt_properties_set_double ( pnum, "default", CLAMP(deflt, 0.0, 1.0) );
			mlt_properties_set ( pnum , "mutable" , "yes" );
			mlt_properties_set ( pnum , "widget" , "spinner" );
		}else
		if ( paraminfo.type == F0R_PARAM_BOOL ){
			double deflt = 0;
			mlt_properties_set ( pnum , "type" , "boolean" );
			mlt_properties_set ( pnum , "minimum" , "0" );
			mlt_properties_set ( pnum , "maximum" , "1" );
			f0r_get_param_value( instance, &deflt, j);
			mlt_properties_set_int ( pnum, "default", deflt != 0.0 );
			mlt_properties_set ( pnum , "mutable" , "yes" );
			mlt_properties_set ( pnum , "widget" , "checkbox" );
		}else
		if ( paraminfo.type == F0R_PARAM_COLOR ){
			char colorstr[8];
			f0r_param_color_t deflt = {0, 0, 0};

			mlt_properties_set ( pnum , "type" , "color" );
			f0r_get_param_value( instance, &deflt, j);
			sprintf( colorstr, "#%02x%02x%02x", (unsigned) CLAMP(deflt.r * 255, 0 , 255),
				(unsigned) CLAMP(deflt.g * 255, 0 , 255), (unsigned) CLAMP(deflt.b * 255, 0 , 255));
			colorstr[7] = 0;
			mlt_properties_set ( pnum , "default", colorstr );
			mlt_properties_set ( pnum , "mutable" , "yes" );
			mlt_properties_set ( pnum , "widget" , "color" );
		}else
		if ( paraminfo.type == F0R_PARAM_STRING ){
			char *deflt = NULL;
			mlt_properties_set ( pnum , "type" , "string" );
			f0r_get_param_value( instance, &deflt, j );
			mlt_properties_set ( pnum , "default", deflt );
			mlt_properties_set ( pnum , "mutable" , "yes" );
			mlt_properties_set ( pnum , "widget" , "text" );
		}
	}
	f0r_destruct(instance);
	f0r_deinit();
	dlclose(handle);
	free(name);

	return metadata;
}