static int module_load (vlc_object_t *obj, module_t *m, vlc_activate_t init, va_list args) { int ret = VLC_SUCCESS; if (module_Map (obj, m)) return VLC_EGENERIC; if (m->pf_activate != NULL) { va_list ap; va_copy (ap, args); ret = init (m->pf_activate, ap); va_end (ap); } return ret; }
/** * Finds and instantiates the best module of a certain type. * All candidates modules having the specified capability and name will be * sorted in decreasing order of priority. Then the probe callback will be * invoked for each module, until it succeeds (returns 0), or all candidate * module failed to initialize. * * The probe callback first parameter is the address of the module entry point. * Further parameters are passed as an argument list; it corresponds to the * variable arguments passed to this function. This scheme is meant to * support arbitrary prototypes for the module entry point. * * \param p_this VLC object * \param psz_capability capability, i.e. class of module * \param psz_name name name of the module asked, if any * \param b_strict if true, do not fallback to plugin with a different name * but the same capability * \param probe module probe callback * \return the module or NULL in case of a failure */ module_t *vlc_module_load(vlc_object_t *p_this, const char *psz_capability, const char *psz_name, bool b_strict, vlc_activate_t probe, ...) { int i_shortcuts = 0; char *psz_shortcuts = NULL, *psz_var = NULL, *psz_alias = NULL; bool b_force_backup = p_this->b_force; /* Deal with variables */ if( psz_name && psz_name[0] == '$' ) { psz_name = psz_var = var_CreateGetString( p_this, psz_name + 1 ); } /* Count how many different shortcuts were asked for */ if( psz_name && *psz_name ) { char *psz_parser, *psz_last_shortcut; /* If the user wants none, give him none. */ if( !strcmp( psz_name, "none" ) ) { free( psz_var ); return NULL; } i_shortcuts++; psz_parser = psz_shortcuts = psz_last_shortcut = strdup( psz_name ); while( ( psz_parser = strchr( psz_parser, ',' ) ) ) { *psz_parser = '\0'; i_shortcuts++; psz_last_shortcut = ++psz_parser; } /* Check if the user wants to override the "strict" mode */ if( psz_last_shortcut ) { if( !strcmp(psz_last_shortcut, "none") ) { b_strict = true; i_shortcuts--; } else if( !strcmp(psz_last_shortcut, "any") ) { b_strict = false; i_shortcuts--; } } } /* Sort the modules and test them */ size_t total, match = 0; module_t **p_all = module_list_get (&total); module_list_t *p_list = xmalloc( total * sizeof( module_list_t ) ); /* Parse the module list for capabilities and probe each of them */ for (size_t i = 0; i < total; i++) { module_t *p_module = p_all[i]; int i_shortcut_bonus = 0; /* Test that this module can do what we need */ if( !module_provides( p_module, psz_capability ) ) continue; /* If we required a shortcut, check this plugin provides it. */ if( i_shortcuts > 0 ) { const char *name = psz_shortcuts; for( unsigned i_short = i_shortcuts; i_short > 0; i_short-- ) { for( unsigned i = 0; i < p_module->i_shortcuts; i++ ) { char *c; if( ( c = strchr( name, '@' ) ) ? !strncasecmp( name, p_module->pp_shortcuts[i], c-name ) : !strcasecmp( name, p_module->pp_shortcuts[i] ) ) { /* Found it */ if( c && c[1] ) psz_alias = c+1; i_shortcut_bonus = i_short * 10000; goto found_shortcut; } } /* Go to the next shortcut... This is so lame! */ name += strlen( name ) + 1; } /* If we are in "strict" mode and we couldn't * find the module in the list of provided shortcuts, * then kick the bastard out of here!!! */ if( b_strict ) continue; } /* Trash <= 0 scored plugins (they can only be selected by shortcut) */ if( p_module->i_score <= 0 ) continue; found_shortcut: /* Store this new module */ p_list[match].p_module = p_module; p_list[match].i_score = p_module->i_score + i_shortcut_bonus; p_list[match].b_force = i_shortcut_bonus && b_strict; match++; } /* We can release the list, interesting modules are held */ module_list_free (p_all); if( match == 0 ) { msg_Dbg( p_this, "no %s module matched \"%s\"", psz_capability, (psz_name && *psz_name) ? psz_name : "any" ); return NULL; // shortcut } /* Sort candidates by descending score */ qsort (p_list, match, sizeof (p_list[0]), modulecmp); msg_Dbg( p_this, "looking for %s module: %zu candidate%s", psz_capability, match, match == 1 ? "" : "s" ); /* Parse the linked list and use the first successful module */ module_t *p_module = NULL; va_list args; va_start(args, probe); for (size_t i = 0; (i < match) && (p_module == NULL); i++) { module_t *p_cand = p_list[i].p_module; if (module_Map (p_this, p_cand)) continue; p_this->b_force = p_list[i].b_force; int ret; if (likely(p_cand->pf_activate != NULL)) { va_list ap; va_copy(ap, args); ret = probe(p_cand->pf_activate, ap); va_end(ap); } else ret = VLC_SUCCESS; switch (ret) { case VLC_SUCCESS: /* good module! */ p_module = p_cand; break; case VLC_ETIMEOUT: /* good module, but aborted */ break; default: /* bad module */ continue; } } va_end (args); free( p_list ); p_this->b_force = b_force_backup; if( p_module != NULL ) { msg_Dbg( p_this, "using %s module \"%s\"", psz_capability, module_get_object(p_module) ); vlc_object_set_name( p_this, psz_alias ? psz_alias : module_get_object(p_module) ); } else msg_Dbg( p_this, "no %s module matching \"%s\" could be loaded", psz_capability, (psz_name && *psz_name) ? psz_name : "any" ); free( psz_shortcuts ); free( psz_var ); /* Don't forget that the module is still locked */ return p_module; }