|
@@ -0,0 +1,281 @@
|
|
|
+Le document s'organise de la façon suivante
|
|
|
+
|
|
|
++ Architecture de VLC
|
|
|
++ Présentation des modèles et abstractions utilisés dans la sandbox
|
|
|
++ Implémentation Linux des abstractions
|
|
|
++ Implémentation Windows des abstractions
|
|
|
+
|
|
|
+===
|
|
|
+
|
|
|
+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 `vlc_plugin_t` qui eux-même manipulent
|
|
|
+les `module_t` contenant la représentation interne du module.
|
|
|
+
|
|
|
+```
|
|
|
+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;
|
|
|
+```
|
|
|
+
|
|
|
+`vlc_plugin_t` contient ainsi les informations pour charger et décharger les
|
|
|
+modules.
|
|
|
+
|
|
|
+```
|
|
|
+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;
|
|
|
+};
|
|
|
+```
|
|
|
+
|
|
|
+De l'autre côté, `module_t` est réellement la description d'un module.
|
|
|
+
|
|
|
+Chaque module dispose d'une priorité, qui correspond au score `i_score` dans
|
|
|
+`module_t`.
|
|
|
+
|
|
|
+Ces modules sont chargés dans l'application depuis deux fonctions du core :
|
|
|
++ `module_need` permet de charger un module correspondant au nom ou à la
|
|
|
+fonction `i_capability` du module, en utilisant le premier module de plus forte
|
|
|
+priorité qui est capable de se charger sans erreur.
|
|
|
++ `module_need_var` qui dans les faits appelle `module_need` en lui donnant le
|
|
|
+contenu d'une variable au lieu d'une recherche hardcodée.
|
|
|
+
|
|
|
+```
|
|
|
+ /* 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);
|
|
|
+```
|
|
|
+
|
|
|
+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 `i_capability`
|
|
|
+de type `xml_reader`.
|
|
|
+
|
|
|
+TODO: décrire module_need en détail et comment on peut vouloir la patcher pour
|
|
|
+prendre en compte la sandbox.
|
|
|
+
|
|
|
++ parler de libvlc et vlc_object
|
|
|
++ parler des variables VLC
|
|
|
+
|
|
|
+
|
|
|
+```
|
|
|
+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)
|
|
|
+```
|
|
|
+
|
|
|
+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.
|
|
|
+
|
|
|
+
|
|
|
+# Abstractions apportées par la sandbox
|
|
|
+
|
|
|
+## Architecture : broker vs non broker
|
|
|
+
|
|
|
+
|
|
|
+### Le modèle broker
|
|
|
+
|
|
|
+Dans ce modèle, on considère un processus privilégié qu'on appelle broker, qui
|
|
|
+va contrôler tous les échanges entre les différents processus qu'on appelle
|
|
|
+workers, mais également leur cycle de vie et leurs permissions et accès.
|
|
|
+
|
|
|
+Le broker est donc en charge de faire le routage des différents messages, de
|
|
|
+transmettre les ressources et faire la vérification d'accès. Il est également
|
|
|
+utile pour vérifier que les processus workers sont encore en vie et signaler les
|
|
|
+erreurs.
|
|
|
+
|
|
|
+
|
|
|
+### Modèle par capabilities et orchestrateur
|
|
|
+
|
|
|
+Meilleures performances. On garde un «orchestrateur» pour initialiser mais les
|
|
|
+modules peuvent fonctionner sans après.
|
|
|
+
|
|
|
+## Communication inter-processus
|
|
|
+
|
|
|
+On a besoin d'un moyen de créer une communication inter-processus et de pouvoir
|
|
|
+transéfer la capacité de communiquer dessus à travers les processus.
|
|
|
+
|
|
|
+On introduit donc le type `vlc_process_ipc_t` qui est capable de stocker ce bus
|
|
|
+de communication et on construira les fonctions de communication dessus.
|
|
|
+
|
|
|
+On a également besoin d'un moyen de sérialiser et envoyer des messages pour
|
|
|
+construire les appels de fonctions déportés (RPC, pour Remote Procedure Call).
|
|
|
+Cela va notamment nous servir à calquer l'architecture actuelle de VLC pour la
|
|
|
+faire évoluer progressivement vers un modèle plus découplé et moins contraint.
|
|
|
+
|
|
|
+## Initialisation des liens entre modules
|
|
|
+
|
|
|
+On imagine que la situation est la même peu importe que l'on soit dans une
|
|
|
+architecture broker ou une architecture orchestrateur. C'est-à-dire qu'on peut
|
|
|
+de toute façon facilement passer d'une architecture non broker à une
|
|
|
+architecture broker avec la construction suivante :
|
|
|
+
|
|
|
+On représente chaque lien entre processus par une IPC du point de vue des
|
|
|
+workers. Il s'agit du cas avec orchestrateur. Si on passe en sandbox avec modèle
|
|
|
+broker, chaque lien est cassé en deux avec une paire worker-broker et une autre
|
|
|
+paire broker-worker, et le broker s'assure ainsi de faire le routage et vérifier
|
|
|
+les accès. On a donc découplé le fonctionnement du worker de la politique
|
|
|
+d'accès choisie. Cette propriété est très importante pour la suite, car elle
|
|
|
+permet de construire le modèle puis d'adapter l'implémentation à ce qui se
|
|
|
+rapproche le plus du fonctionnement du système sous-jacent.
|
|
|
+
|
|
|
+## Gestion de la boucle d'événement
|
|
|
+
|
|
|
+On peut donc commencer à construire chaque worker. On imagine que chacun est
|
|
|
+capable de communiquer à l'extérieur vers la bonne partie du core à partir du
|
|
|
+moment où l'initialisation a fait partager une paire d'IPC pour les relier.
|
|
|
+
|
|
|
+On va donc devoir gérer les messages envoyés d'un processus à l'autre.
|
|
|
+Exactement, il va falloir raisonner en parlant des messages envoyés des
|
|
|
+processus vers le processus qu'on étudie en particulier. Pour s'intégrer plus
|
|
|
+facilement dans l'application, on ne va pas intégrer la gestion des événements
|
|
|
+directement dans le flot d'exécution de chaque partie du core. En effet, cela
|
|
|
+deviendrait difficile de raisonner en terme de zone de privilège étant donné que
|
|
|
+l'application elle-même (et non plus seulement la sandbox) devra savoir router
|
|
|
+les messages d'un module supérieur vers un module inférieur.
|
|
|
+
|
|
|
+On va donc créer un thread par processus qui sera chargé de recevoir et exécuter
|
|
|
+les messages puis exécuter les RPC : il s'agit donc d'une boucle événementielle
|
|
|
+assez classique, mais qui doit prendre en compte le modèle threadé de
|
|
|
+l'application sous-jacente. Une attention particulière doit être mise sur la
|
|
|
+gestion des deadlocks.
|
|
|
+
|
|
|
+Une IPC qui déclencherait une fonction d'un morceau de l'application qui
|
|
|
+déclencherait une nouvelle RPC dans un autre processus et attendrait le retour
|
|
|
+de fonction amènerait à une situation de deadlock, la boucle événementielle
|
|
|
+étant bloquée dans l'exécution de la première RPC.
|
|
|
+
|
|
|
+Pire encore, une RPC créant le verrouillage de'certains mutexes qui seraient
|
|
|
+également verrouillés par le résultat de la RPC aboutirait également au blocage
|
|
|
+complet de ce bout de l'application.
|
|
|
+
|
|
|
+Une fois ces problématiques en tête, il faut prendre en compte les mécanismes
|
|
|
+des systèmes d'exploitations pour la programmation asynchrone et écrire une
|
|
|
+abstraction qui permet de récupérer un message depuis l'une des IPC disponibles.
|
|
|
+C'est ainsi qu'apparaît `vlc_msg_poller_t` et l'API qui est liée.
|
|
|
+
|
|
|
+```
|
|
|
+// TODO: mettre structure et API
|
|
|
+```
|
|
|
+
|
|
|
+## Gestion des blocs mémoires
|
|
|
+
|
|
|
+Lorsque l'on traite des données en streaming, on récupère la plupart du temps
|
|
|
+des tronçons de données. Dans VLC, ces tronçons sont annotés de données
|
|
|
+multimédia : le dts et le pts par exemple. On verra son fonctionnement en
|
|
|
+détails dans la partie encodage matériel pour Android.
|
|
|
+
|
|
|
+Ce type de données est particulièrement présent dans le pipeline multimédia de
|
|
|
+VLC. On obtient des `block_t` après une lecture depuis un fichier ou le réseau,
|
|
|
+qui est transmis au `demux_t` qui va analyser et extraire différents flux audio,
|
|
|
+vidéo et sous-titre. Il crée un `block_t` pour chacune des unités correspondant
|
|
|
+au format de ces flux, on peut par exemple penser à une image pour un flux
|
|
|
+MJPEG. Ces blocs sont ensuite transmis au décodeur correspondant qui va créer la
|
|
|
+`picture_t` pour la vidéo, ou `subpicture_t` pour les sous-titres, ou `block_t`
|
|
|
+pour l'audio et la passer dans la chaîne de filtre pour l'envoyer à la sortie
|
|
|
+correspondante.
|
|
|
+
|
|
|
+Il faut désormais repenser à la situation de la sandbox et se souvenir que ces
|
|
|
+données-là vont devoir transiter d'un processus à l'autre de façon efficace. On
|
|
|
+va donc essayer de s'intéresser aux allocations effectuées par les modules et
|
|
|
+intégrer cette gestion en utilisant les mécanismes du système d'exploitation.
|
|
|
+Dans tous les cas, le système d'exploitation dispose d'un mécanisme de mémoire
|
|
|
+partagée pour dresser la correspondance de pages mémoires dans des processus
|
|
|
+différents. On peut donc essayer d'introduire des `block_t` alloués comme des
|
|
|
+mémoires partagées que l'on transmet de processus en processus.
|
|
|
+
|
|
|
+Avec cette idée-là, la gestion de l'accès des processsus aux ressources devient
|
|
|
+cruciale.
|
|
|
+
|
|
|
+## Gestion des objets libvlccore
|
|
|
+
|
|
|
+Armé des différentes abstractions mises en place pour la communication
|
|
|
+inter-processus des données de contrôle et des données applicatives, on va
|
|
|
+pouvoir commencer à intégrer réellement les morceaux de l'application dans la
|
|
|
+sandbox. On va donc devoir définir comment sera découpé et cloisonné
|
|
|
+l'application et quels seront les points d'entrées pour la sandbox.
|
|
|
+
|
|
|
+Dans cette partie, nous allons majoritairement prendre en considération le cas
|
|
|
+du découpage du décodeur, entraînant la refactorisation d'autres dépendences de
|
|
|
+cet objet.
|
|
|
+
|
|
|
+## Découpage en module
|
|
|
+
|
|
|
+Une autre méthode de cloisonnement qui n'a pas beaucoup été explorée est de
|
|
|
+cloisonner l'application par module et non par objet de libvlccore.
|
|
|
+
|
|
|
+Cette méthode permet d'arriver plus rapidement à une situation où l'ensemble des
|
|
|
+modules de VLC est cloisonné, mais libvlccore ne profite pas du tout de ce
|
|
|
+cloisonnement et il nous reste alors environ 100 000 lignes de code à auditer
|
|
|
+pour s'approcher de loin à la méthode précédente. La technique reste
|
|
|
+intéressante à mentionner comme l'injection montre de façon plus simple comment
|
|
|
+le pattern fonctionne.
|
|
|
+
|
|
|
+
|
|
|
+# Implémentation sous Linux
|
|
|
+
|
|
|
+## Passage de descripteur de fichiers entre processus
|
|
|
+
|
|
|
+## Gestion asynchrone des messages reçus
|
|
|
+
|
|
|
+Modèle par readiness
|
|
|
+
|
|
|
+## Routage des messages
|
|
|
+
|
|
|
+Socket unix
|