123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197 |
- \section{Architecture de VLC}
- VLC est découpé en plusieurs parties. Deux parties ressortent particulièrement
- comme étant des bibliothèques charnières pour le fonctionnement du logiciel.
- D'un côté, les applications finales utilisent l'interface exposée par la
- LibVLC. L'objectif est d'avoir quelque chose de très haut niveau pour
- construire un pipeline sans avoir besoin des considérations techniques liées au
- multimédia.
- De l'autre LibVLC fonctionne à travers la LibVLCCore qui expose les fonctions
- bas niveau de chargement de module et contrôle les threads, les modules, les
- horloges, la liste de lecture et tout le contrôle bas niveau dans VLC. Les
- modules sont alors linkés à la LibVLCCore pour profiter des fonctions bas
- niveau.
- % Mettre schéma
- \begin{figure}[htbp]
- \centering
- \includegraphics[width = 300pt]{schemas/vlc_libvlc}
- \caption{Architecture des applications utilisant libvlc}
- \end{figure}
- En résumé LibVLC expose l'API tandis que LibVLCCore réalise le lien entre les
- différents modules. La sélection des modules à utiliser est donc au cœur du
- développement de VLC, ce qui rend le core bien plus maintenable que les modules
- eux-mêmes.
- Il faut néanmoins noter que les modules sont chargés à partir du moment où un
- autre module le demande. C'est ce qui crée la notion de pipeline dynamique:
- seuls les modules apportant les fonctionnalités requises par un autre module
- sont injectés dans le pipeline. Ces modules sont alors chargés soit de façon
- statique, soit depuis une bibliothèque de code dynamique.
- Actuellement, le développement de VLC est beaucoup centré sur son approche
- multi-thread, par exemple pour que les décodeurs et les sorties fonctionnent de
- manière indépendante et correctement synchroniser le temps. Le décodeur et la
- sortie vidéo fonctionnent ainsi de manière asynchrone. L'idée centrale est
- qu'avoir plusieurs entrées en lecture et plusieurs sorties simultanément ne doit
- pas être impossible. Une démonstration de cette fonction a d'ailleurs été mis en
- place en tant que \og{}VLM\fg{}, qui permet de contrôler plusieurs flux en entrées et de
- choisir comment les utiliser en sortie; que ce soit en les diffusant, en créant
- une mosaïque ou en affichant des flux dans de nouvelles sorties.
- \section{Variables de configuration}
- La gestion de la configuration des modules dans VLC passe principalement par un
- système d'objet à travers le type \inltype{vlc_object_t}. Tous les modules de
- VLC sont représentés comme un \inltype{vlc_object_t} tout comme certains
- éléments du core et la LibVLC elle-même. Ces objets sont liés à un objet
- \inltype{libvlc_instance_t} particulier et sont agencés en arbre d'objet.
- On peut associer des variables à ces objets en leur faisant suivre certaines
- règles. On peut par exemple les créer avec une valeur par défaut, les créer sans
- leur donner de valeur ou bien les créer en les initialisant récursivement avec
- la valeur du parent.
- Ces variables portent ainsi une configuration qui leur est propre, venant de la
- configuration imposée ou suggérée par leur parent, qui vient de la configuration
- donnée en ligne de commande ou qui vient du fichier de configuration de VLC.\@
- Il s'agit donc d'un mélange entre une interface utilisateur et une interface
- entre modules.
- Dans notre cas, c'est particulièrement important car il y a trois possibilités
- dans le modèle multiprocessus. Où bien les variables sont toutes définies dans
- le même processus, où bien l'arbre de variable est découpé dans plusieurs
- processus et il faut refaire le lien entre les objets, ou bien chaque processus
- ne dispose que d'une projection de l'arbre. Nous verrons dans la suite comment
- gérer les deux premiers cas, mais pour des raisons de simplicité et performance
- c'est la troisième option qui est actuellement utilisée.
- \section{Modules}
- Le principal de l'architecture actuelle de VLC est organisé autour de la notion
- de module. D'un côté, le core contrôle le chargement des fonctionnalités et joue
- un rôle d'orchestrateur. De l'autre côté chaque fonctionnalité est apportée par
- un module qui peut être chargé dynamiquement ou lié statiquement à
- l'application.
- Les modules sont manipulés à travers les \inltype{vlc_plugin_t} qui eux-même
- manipulent les \inltype{module_t} contenant la représentation interne du module.
- % TODO: décrire plus exactement ce qu'il faut highlight, en particulier
- % chargement des modules dans la sandbox, stockage des options, etc
- \begin{code}{c}{Structure d'un plugin}
- typedef struct vlc_plugin_t
- {
- struct vlc_plugin_t *next;
- module_t *module;
- unsigned modules_count;
- const char *textdomain; /**< gettext domain (or NULL) */
- /* Variables set by the module to store its config options */
- struct
- {
- module_config_t *items; /**< Table of configuration parameters */
- size_t size; /**< Size of items table */
- size_t count; /**< Number of configuration items */
- size_t booleans; /**< Number of booleal config items */
- } conf;
- #ifdef HAVE_DYNAMIC_PLUGINS
- bool unloadable; /**< Whether the plug-in can be unloaded safely */
- atomic_uintptr_t handle; /**< Run-time linker handle (or nul) */
- char *abspath; /**< Absolute path */
- char *path; /**< Relative path (within plug-in directory) */
- int64_t mtime; /**< Last modification time */
- uint64_t size; /**< File size */
- #endif
- } vlc_plugin_t;
- \end{code}
- \inltype{vlc_plugin_t} contient ainsi les informations pour charger et décharger
- les modules.
- \clearpage
- \begin{code}{c}{Structure d'un module}
- struct module_t
- {
- vlc_plugin_t *plugin; /**< Plug-in/library containing the module */
- module_t *next;
- /** Shortcuts to the module */
- unsigned i_shortcuts;
- const char **pp_shortcuts;
- /*
- * Variables set by the module to identify itself
- */
- const char *psz_shortname; /**< Module name */
- const char *psz_longname; /**< Module descriptive name */
- const char *psz_help; /**< Long help string for "special" modules */
- const char *psz_capability; /**< Capability */
- int i_score; /**< Score for the capability */
- /* Callbacks */
- const char *activate_name;
- const char *deactivate_name;
- void *pf_activate;
- void *pf_deactivate;
- };
- \end{code}
- De l'autre côté, \inltype{module_t} contient la description d'un module, qu'il
- soit chargé dynamiquement ou non.
- Chaque module dispose d'une priorité, qui correspond au score \inltype{i_score}
- dans \inltype{module_t}.
- Ces modules sont chargés dans l'application depuis deux fonctions du core:
- \begin{itemize}
- \item \inltype{module_need} permet de charger un module correspondant au nom ou à la
- fonction \inltype{i_capability} du module, en utilisant le premier module de plus forte
- priorité qui est capable de se charger sans erreur.
- \item \inltype{module_need_var} qui dans les faits appelle \inltype{module_need} en lui donnant le
- contenu d'une variable au lieu d'une recherche hardcodée.
- \end{itemize}
- Les détails de ces fonctions sont décrits ici car il s'agit de l'interface
- exposée par VLC pour charger un nouveau module. C'est donc un point d'entrée
- potentiel pour l'injection des abstractions de la sandbox.
- \begin{code}{c}{API de requête de module}
- module_t *vlc_module_load(vlc_object_t *obj, const char *capability,
- const char *name, bool strict,
- vlc_activate_t probe, ...);
- module_t *module_need(vlc_object_t *obj, const char *cap, const char *name,
- bool strict)
- \end{code}
- Ces fonctions sont généralement appelées après avoir créé un objet correspondant
- au type du module en question. Si dessous, on peut voir un exemple au sein de
- l'API de gestion des documents XML.\@
- \begin{code}{c}{Création d'objet VLC puis chargement de modules}
- /* from src/misc/xml.c */
- p_xml = vlc_custom_create( p_this, sizeof( *p_xml ), "xml" );
- p_xml->p_module = module_need( p_xml, "xml", NULL, false );
- reader = vlc_custom_create(obj, sizeof(*reader), "xml reader");
- reader->p_module = module_need(reader, "xml reader", NULL, false);
- \end{code}
- Par exemple, nous avons ici la création d'un objet représentant un module
- d'analyse syntaxique XML. Le module est ensuite chargé dans l'objet. Puis un
- autre couple d'objet et module est chargé, cette fois-ci pour une \inltype{i_capability}
- de type \inltype{xml_reader}.
- Cela montre en particulier que l'application ne va pas manipuler directement le
- module, mais un objet lié à ce module. Ce pattern va nous permettre de concevoir
- une façon simple de sandboxer ces modules en patchant la fonction
- \inltype{module_need},
- qui sera rapidement décrite dans la suite.
|