\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.