vlc_explanations.tex 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. \section{Architecture de VLC}
  2. VLC est découpé en plusieurs parties. Deux parties ressortent particulièrement
  3. comme étant des bibliothèques charnières pour le fonctionnement du logiciel.
  4. D'un côté, les applications finales utilisent l'interface exposée par la
  5. LibVLC. L'objectif est d'avoir quelque chose de très haut niveau pour
  6. construire un pipeline sans avoir besoin des considérations techniques liées au
  7. multimédia.
  8. De l'autre LibVLC fonctionne à travers la LibVLCCore qui expose les fonctions
  9. bas niveau de chargement de module et contrôle les threads, les modules, les
  10. horloges, la liste de lecture et tout le contrôle bas niveau dans VLC. Les
  11. modules sont alors linkés à la LibVLCCore pour profiter des fonctions bas
  12. niveau.
  13. % Mettre schéma
  14. \begin{figure}[htbp]
  15. \centering
  16. \includegraphics[width = 300pt]{schemas/vlc_libvlc}
  17. \caption{Architecture des applications utilisant libvlc}
  18. \end{figure}
  19. En résumé LibVLC expose l'API tandis que LibVLCCore réalise le lien entre les
  20. différents modules. La sélection des modules à utiliser est donc au cœur du
  21. développement de VLC, ce qui rend le core bien plus maintenable que les modules
  22. eux-mêmes.
  23. Il faut néanmoins noter que les modules sont chargés à partir du moment où un
  24. autre module le demande. C'est ce qui crée la notion de pipeline dynamique:
  25. seuls les modules apportant les fonctionnalités requises par un autre module
  26. sont injectés dans le pipeline. Ces modules sont alors chargés soit de façon
  27. statique, soit depuis une bibliothèque de code dynamique.
  28. Actuellement, le développement de VLC est beaucoup centré sur son approche
  29. multi-thread, par exemple pour que les décodeurs et les sorties fonctionnent de
  30. manière indépendante et correctement synchroniser le temps. Le décodeur et la
  31. sortie vidéo fonctionnent ainsi de manière asynchrone. L'idée centrale est
  32. qu'avoir plusieurs entrées en lecture et plusieurs sorties simultanément ne doit
  33. pas être impossible. Une démonstration de cette fonction a d'ailleurs été mis en
  34. place en tant que \og{}VLM\fg{}, qui permet de contrôler plusieurs flux en entrées et de
  35. choisir comment les utiliser en sortie; que ce soit en les diffusant, en créant
  36. une mosaïque ou en affichant des flux dans de nouvelles sorties.
  37. \section{Variables de configuration}
  38. La gestion de la configuration des modules dans VLC passe principalement par un
  39. système d'objet à travers le type \inltype{vlc_object_t}. Tous les modules de
  40. VLC sont représentés comme un \inltype{vlc_object_t} tout comme certains
  41. éléments du core et la LibVLC elle-même. Ces objets sont liés à un objet
  42. \inltype{libvlc_instance_t} particulier et sont agencés en arbre d'objet.
  43. On peut associer des variables à ces objets en leur faisant suivre certaines
  44. règles. On peut par exemple les créer avec une valeur par défaut, les créer sans
  45. leur donner de valeur ou bien les créer en les initialisant récursivement avec
  46. la valeur du parent.
  47. Ces variables portent ainsi une configuration qui leur est propre, venant de la
  48. configuration imposée ou suggérée par leur parent, qui vient de la configuration
  49. donnée en ligne de commande ou qui vient du fichier de configuration de VLC.\@
  50. Il s'agit donc d'un mélange entre une interface utilisateur et une interface
  51. entre modules.
  52. Dans notre cas, c'est particulièrement important car il y a trois possibilités
  53. dans le modèle multiprocessus. Où bien les variables sont toutes définies dans
  54. le même processus, où bien l'arbre de variable est découpé dans plusieurs
  55. processus et il faut refaire le lien entre les objets, ou bien chaque processus
  56. ne dispose que d'une projection de l'arbre. Nous verrons dans la suite comment
  57. gérer les deux premiers cas, mais pour des raisons de simplicité et performance
  58. c'est la troisième option qui est actuellement utilisée.
  59. \section{Modules}
  60. Le principal de l'architecture actuelle de VLC est organisé autour de la notion
  61. de module. D'un côté, le core contrôle le chargement des fonctionnalités et joue
  62. un rôle d'orchestrateur. De l'autre côté chaque fonctionnalité est apportée par
  63. un module qui peut être chargé dynamiquement ou lié statiquement à
  64. l'application.
  65. Les modules sont manipulés à travers les \inltype{vlc_plugin_t} qui eux-même
  66. manipulent les \inltype{module_t} contenant la représentation interne du module.
  67. % TODO: décrire plus exactement ce qu'il faut highlight, en particulier
  68. % chargement des modules dans la sandbox, stockage des options, etc
  69. \begin{code}{c}{Structure d'un plugin}
  70. typedef struct vlc_plugin_t
  71. {
  72. struct vlc_plugin_t *next;
  73. module_t *module;
  74. unsigned modules_count;
  75. const char *textdomain; /**< gettext domain (or NULL) */
  76. /* Variables set by the module to store its config options */
  77. struct
  78. {
  79. module_config_t *items; /**< Table of configuration parameters */
  80. size_t size; /**< Size of items table */
  81. size_t count; /**< Number of configuration items */
  82. size_t booleans; /**< Number of booleal config items */
  83. } conf;
  84. #ifdef HAVE_DYNAMIC_PLUGINS
  85. bool unloadable; /**< Whether the plug-in can be unloaded safely */
  86. atomic_uintptr_t handle; /**< Run-time linker handle (or nul) */
  87. char *abspath; /**< Absolute path */
  88. char *path; /**< Relative path (within plug-in directory) */
  89. int64_t mtime; /**< Last modification time */
  90. uint64_t size; /**< File size */
  91. #endif
  92. } vlc_plugin_t;
  93. \end{code}
  94. \inltype{vlc_plugin_t} contient ainsi les informations pour charger et décharger
  95. les modules.
  96. \clearpage
  97. \begin{code}{c}{Structure d'un module}
  98. struct module_t
  99. {
  100. vlc_plugin_t *plugin; /**< Plug-in/library containing the module */
  101. module_t *next;
  102. /** Shortcuts to the module */
  103. unsigned i_shortcuts;
  104. const char **pp_shortcuts;
  105. /*
  106. * Variables set by the module to identify itself
  107. */
  108. const char *psz_shortname; /**< Module name */
  109. const char *psz_longname; /**< Module descriptive name */
  110. const char *psz_help; /**< Long help string for "special" modules */
  111. const char *psz_capability; /**< Capability */
  112. int i_score; /**< Score for the capability */
  113. /* Callbacks */
  114. const char *activate_name;
  115. const char *deactivate_name;
  116. void *pf_activate;
  117. void *pf_deactivate;
  118. };
  119. \end{code}
  120. De l'autre côté, \inltype{module_t} contient la description d'un module, qu'il
  121. soit chargé dynamiquement ou non.
  122. Chaque module dispose d'une priorité, qui correspond au score \inltype{i_score}
  123. dans \inltype{module_t}.
  124. Ces modules sont chargés dans l'application depuis deux fonctions du core:
  125. \begin{itemize}
  126. \item \inltype{module_need} permet de charger un module correspondant au nom ou à la
  127. fonction \inltype{i_capability} du module, en utilisant le premier module de plus forte
  128. priorité qui est capable de se charger sans erreur.
  129. \item \inltype{module_need_var} qui dans les faits appelle \inltype{module_need} en lui donnant le
  130. contenu d'une variable au lieu d'une recherche hardcodée.
  131. \end{itemize}
  132. Les détails de ces fonctions sont décrits ici car il s'agit de l'interface
  133. exposée par VLC pour charger un nouveau module. C'est donc un point d'entrée
  134. potentiel pour l'injection des abstractions de la sandbox.
  135. \begin{code}{c}{API de requête de module}
  136. module_t *vlc_module_load(vlc_object_t *obj, const char *capability,
  137. const char *name, bool strict,
  138. vlc_activate_t probe, ...);
  139. module_t *module_need(vlc_object_t *obj, const char *cap, const char *name,
  140. bool strict)
  141. \end{code}
  142. Ces fonctions sont généralement appelées après avoir créé un objet correspondant
  143. au type du module en question. Si dessous, on peut voir un exemple au sein de
  144. l'API de gestion des documents XML.\@
  145. \begin{code}{c}{Création d'objet VLC puis chargement de modules}
  146. /* from src/misc/xml.c */
  147. p_xml = vlc_custom_create( p_this, sizeof( *p_xml ), "xml" );
  148. p_xml->p_module = module_need( p_xml, "xml", NULL, false );
  149. reader = vlc_custom_create(obj, sizeof(*reader), "xml reader");
  150. reader->p_module = module_need(reader, "xml reader", NULL, false);
  151. \end{code}
  152. Par exemple, nous avons ici la création d'un objet représentant un module
  153. d'analyse syntaxique XML. Le module est ensuite chargé dans l'objet. Puis un
  154. autre couple d'objet et module est chargé, cette fois-ci pour une \inltype{i_capability}
  155. de type \inltype{xml_reader}.
  156. Cela montre en particulier que l'application ne va pas manipuler directement le
  157. module, mais un objet lié à ce module. Ce pattern va nous permettre de concevoir
  158. une façon simple de sandboxer ces modules en patchant la fonction
  159. \inltype{module_need},
  160. qui sera rapidement décrite dans la suite.