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 ); }
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; }
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 ); } } }
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 ); } }
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; }
void mlt_parser_close( mlt_parser self ) { if ( self != NULL ) { mlt_properties_close( &self->parent ); free( self ); } }
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 ); }
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 ); }
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 ); }
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 ); } }
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( ); } }
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 ); } }
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 ); }
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 ); }
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 ); }
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; }
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; }
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; }
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 ); } }
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 ); } } }
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 ); } }
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(¶minfo,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; }