
Dans cet article, je vais vous expliquer comment écrire un plugin en C pour le lecteur multimédia VLC. J'ai écrit mon plugin pour simplifier le visionnage d'émissions de télévision et de films en anglais. L'idée de créer ce plugin est décrite dans les sections
Idée et
Recherche de solutions . Les détails techniques de l'implémentation du plugin sont fournis dans les sections
plugin et
implémentation de Hello World . Ce qui s'est passé à la fin et comment l'utiliser peut être trouvé dans la dernière section,
Résultat .
Le code source du projet est disponible sur
GitHub .
Idée
L'idée d'apprendre une langue étrangère en regardant ma série préférée n'est pas nouvelle, mais j'ai toujours eu des problèmes avec elle personnellement. Il est très difficile de regarder une série ou un film lorsque vous ne comprenez pas la moitié de ce qu’ils disent. Bien sûr, vous pouvez activer les sous-titres, mais si un mot ou une expression inconnue est rencontré dans un discours, il ne sera pas plus clair qu'il sera dupliqué par le texte. Et je n'aimais pas du tout regarder la série avec des sous-titres russes - le cerveau passe à sa langue maternelle et cesse de percevoir la langue étrangère. J'ai lu quelque part que vous devez d'abord regarder une série en russe, puis dans l'original. Mais cette approche ne me convenait pas non plus. Premièrement, où prendre autant de temps pour regarder la même chose plusieurs fois, et deuxièmement, regarder la deuxième fois n'est plus aussi intéressant - la motivation est perdue.
Malgré toutes les difficultés à regarder des émissions de télévision étrangères, je peux assez bien lire la documentation technique, les articles et les livres en anglais. J'aime lire des livres sur le lecteur électronique Kindle, car la fonction de dictionnaire est cool, vous pouvez trouver une traduction d'un mot inconnu avec une seule touche de l'écran. Il est pratique de lire des articles et des sites en anglais en installant une extension spéciale pour la traduction dans le navigateur - j'utilise l'extension
Yandex.Translation . Cette approche vous permet de lire et de comprendre des textes en anglais, sans trop de distraction pour la recherche de mots inconnus.
J'ai pensé, pourquoi ne pas appliquer la même approche pour regarder des émissions de télévision - nous allumons la série en anglais dès qu'une phrase incompréhensible est trouvée, passons à la piste audio russe et revenons un peu en arrière. Ensuite, nous continuons de regarder la série en anglais.
Rechercher une solution
En fait, toutes les fonctionnalités dont j'ai besoin sont déjà disponibles dans de nombreux lecteurs multimédias populaires. La seule chose que je voudrais changer de piste audio et rembobiner la vidéo il y a quelques secondes en cliquant sur un bouton. Il serait également formidable que, après avoir traduit un fragment incompréhensible, le lecteur multimédia lui-même ait rétabli la piste audio en anglais. Eh bien, ce serait bien de pouvoir répéter le fragment traduit précédemment avec la piste anglaise.
Autrement dit, j'ai besoin d'un lecteur multimédia pour lequel vous pouvez écrire des plugins. Il est également souhaitable qu'il soit multiplateforme, car j'utilise un PC sous Windows et un ordinateur portable sous Linux. Mon choix s'est immédiatement porté sur le VLC. Le habr, j'ai même trouvé
un article dans lequel
@Idunno explique comment écrire une extension VLC sur LUA. Soit dit en passant, il a également écrit cette extension pour apprendre l'anglais). Malheureusement, cette extension ne fonctionne pas dans les dernières versions de VLC (antérieures à 2.0.5). En raison d'un fonctionnement instable, la possibilité d'ajouter des fonctions de rappel permettant de traiter les événements de clavier dans l'extension LUA a été supprimée de l'API LUA. Dans
README , un lien vers la liste de diffusion des développeurs VLC discutant de ce problème mène à son extension sur GitHub
@Idunno .
Ainsi, pour implémenter mon idée, une extension de LUA ne fonctionne pas, vous devez écrire un plugin en C. Et bien que j'aie écrit quelque chose en C la dernière fois il y a environ 7 ans, de retour à l'université, j'ai décidé de l'essayer.
Bonjour le plugin du monde
Il convient de noter que le lecteur multimédia VLC possède une assez bonne
documentation . J'en ai appris que le développement d'un lecteur multimédia utilise une approche modulaire. VLC se compose de plusieurs modules indépendants qui implémentent certaines fonctionnalités et du noyau (
libVLCCore ), qui gère ces modules. Il existe deux types de modules: internes (
dans l'arborescence ) et externes (
hors de l'arborescence ). Le code source des modules internes est stocké dans un référentiel avec le code du noyau. Les modules externes sont développés et assemblés indépendamment du lecteur multimédia VLC. En fait, ces derniers sont ce qu'on appelle des plugins.
La documentation contient également
un article sur la façon d'écrire votre plug-in (module) en C. Cet article fournit le code source d'un plug-in simple qui, au démarrage de VLC, affiche un message de bienvenue «
Bonjour, <nom> » sur la console (la valeur <nom> est prise depuis les paramètres du plugin). En courant un peu plus loin, je dirai que dans l'exemple ci-dessus, ajoutez la ligne suivante après
set_category(CAT_INTERFACE)
:
set_subcategory( SUBCAT_INTERFACE_CONTROL )
Eh bien, il ne reste plus qu'à construire le plugin et tester son fonctionnement. Il y a aussi une
instruction pour construire un plugin externe. Ici, il convient de prêter attention à la section
Internationalisation , qui décrit le fonctionnement de la localisation dans VLC. En bref, pour les plug-ins externes, vous devez définir les macros
N_()
,
_()
:
#define DOMAIN "vlc-myplugin" #define _(str) dgettext(DOMAIN, str) #define N_(str) (str)
Pour l'assemblage, il est proposé d'utiliser le bon vieux Makefile ou Autotools. J'ai décidé de suivre la voie simple et j'ai choisi le Makefile. Dans le Makefile, vous devez vous rappeler de définir la variable
MODULE_STRING
- c'est l'identifiant de notre plugin. J'ai également modifié un peu le travail avec les répertoires - maintenant ils sont définis via
pkg-config . Le résultat est les fichiers suivants:
bonjour.c #ifdef HAVE_CONFIG_H # include "config.h" #endif #define DOMAIN "vlc-myplugin" #define _(str) dgettext(DOMAIN, str) #define N_(str) (str) #include <stdlib.h> /* VLC core API headers */ #include <vlc_common.h> #include <vlc_plugin.h> #include <vlc_interface.h> /* Forward declarations */ static int Open(vlc_object_t *); static void Close(vlc_object_t *); /* Module descriptor */ vlc_module_begin() set_shortname(N_("Hello")) set_description(N_("Hello interface")) set_capability("interface", 0) set_callbacks(Open, Close) set_category(CAT_INTERFACE) set_subcategory( SUBCAT_INTERFACE_CONTROL ) add_string("hello-who", "world", "Target", "Whom to say hello to.", false) vlc_module_end () /* Internal state for an instance of the module */ struct intf_sys_t { char *who; }; /** * Starts our example interface. */ static int Open(vlc_object_t *obj) { intf_thread_t *intf = (intf_thread_t *)obj; /* Allocate internal state */ intf_sys_t *sys = malloc(sizeof (*sys)); if (unlikely(sys == NULL)) return VLC_ENOMEM; intf->p_sys = sys; /* Read settings */ char *who = var_InheritString(intf, "hello-who"); if (who == NULL) { msg_Err(intf, "Nobody to say hello to!"); goto error; } sys->who = who; msg_Info(intf, "Hello %s!", who); return VLC_SUCCESS; error: free(sys); return VLC_EGENERIC; } /** * Stops the interface. */ static void Close(vlc_object_t *obj) { intf_thread_t *intf = (intf_thread_t *)obj; intf_sys_t *sys = intf->p_sys; msg_Info(intf, "Good bye %s!", sys->who); /* Free internal state */ free(sys->who); free(sys); }
Makefile LD = ld CC = cc PKG_CONFIG = pkg-config INSTALL = install CFLAGS = -g -O2 -Wall -Wextra LDFLAGS = LIBS = VLC_PLUGIN_CFLAGS := $(shell $(PKG_CONFIG) --cflags vlc-plugin) VLC_PLUGIN_LIBS := $(shell $(PKG_CONFIG) --libs vlc-plugin) VLC_PLUGIN_DIR := $(shell $(PKG_CONFIG) --variable=pluginsdir vlc-plugin) plugindir = $(VLC_PLUGIN_DIR)/misc override CC += -std=gnu99 override CPPFLAGS += -DPIC -I. -Isrc override CFLAGS += -fPIC override LDFLAGS += -Wl,-no-undefined,-z,defs override CPPFLAGS += -DMODULE_STRING=\ override CFLAGS += $(VLC_PLUGIN_CFLAGS) override LIBS += $(VLC_PLUGIN_LIBS) all: libhello_plugin.so install: all mkdir -p -- $(DESTDIR)$(plugindir) $(INSTALL) --mode 0755 libhello_plugin.so $(DESTDIR)$(plugindir) install-strip: $(MAKE) install INSTALL= uninstall: rm -f $(plugindir)/libhello_plugin.so clean: rm -f -- libhello_plugin.so src/*.o mostlyclean: clean SOURCES = hello.c $(SOURCES:%.c=src/%.o): $(SOURCES:%.c=src/%.c) libhello_plugin.so: $(SOURCES:%.c=src/%.o) $(CC) $(LDFLAGS) -shared -o $@ $^ $(LIBS) .PHONY: all install install-strip uninstall clean mostlyclean
Le moyen le plus simple de créer un plugin pour Linux. Pour ce faire, vous devez en fait installer le lecteur multimédia VLC lui-même, ainsi que les fichiers et les outils nécessaires à la création du plug-in. Sur Debian / Ubuntu, cela peut être fait avec la commande suivante:
sudo apt-get install vlc libvlc-dev libvlccore-dev gcc make pkg-config
En fait, tout est prêt, nous collectons et installons notre plugin en utilisant la commande:
sudo make install
Pour tester le plugin, exécutez également VLC à partir de la console:
vlc
Malheureusement, nous n'avons vu aucun «
Bonjour tout le monde ». Le fait est que le plugin doit d'abord être activé. Pour ce faire, ouvrez les paramètres (
Outils >
Préférences ), passez à la vue avancée (sélectionnez
Tout dans le groupe
Afficher les paramètres ) et recherchez dans l'arborescence du panneau de gauche
Interface >
Interfaces de contrôle - cochez la case à côté de notre plugin d'
interface Hello .

Nous enregistrons les paramètres et redémarrons le VLC.

Créer un plugin pour Windows
Avec Windows, les choses sont un peu plus compliquées. Pour construire le plugin, vous devez télécharger sdk, qui contient les bibliothèques, les en-têtes et les fichiers de configuration de VLC. Auparavant, sdk faisait partie de l'assembly VLC standard et se trouvait dans le dossier d'installation du programme. Maintenant, il s'agit d'un assemblage de lecteur multimédia séparé. Par exemple, pour VLC version 3.0.8, cet assemblage peut être téléchargé sur
ftp://ftp.videolan.org/pub/videolan/vlc/3.0.8/win64/vlc-3.0.8-win64.7z (il est important de télécharger 7z -archive).
Copiez le contenu de l'archive dans un dossier, par exemple, dans
C: \ Projects . En plus de sdk, l'archive contient également le lecteur multimédia lui-même, qui peut être utilisé pour tester et déboguer le plug-in.
Pour que notre Makefile puisse être utilisé pour construire et installer le plugin, vous devez corriger le fichier
C: \ Projects \ vlc-3.0.8 \ sdk \ lib \ pkgconfig \ vlc-plugin.pc , indiquant le chemin correct vers le dossier
sdk dans les variables
préfixe et
pluginsdir et
plugins respectivement:
prefix=/c/Projects/vlc-3.0.8/sdk pluginsdir=/c/Projects/vlc-3.0.8/plugins
Pour construire sous Windows, nous devons également installer un compilateur et d'autres utilitaires. Tous les logiciels nécessaires peuvent être obtenus en installant l'environnement
MSYS2 . Le site Web du projet contient des instructions d'installation détaillées. En bref, immédiatement après l'installation, vous devez ouvrir la console (
C: \ msys64 \ msys2.exe ) et mettre à jour les packages MSYS2 à l'aide de la commande:
pacman -Syu
Ensuite, fermez la fenêtre du terminal MSYS2, puis ouvrez-la à nouveau et exécutez la commande
pacman -Su
Après avoir mis à jour tous les packages, vous devez installer la chaîne d'outils:
pacman -S base-devel mingw-w64-x86_64-toolchain
Maintenant que tous les packages nécessaires sont installés, vous pouvez commencer à construire le plugin. J'ai un peu modifié le Makefile pour qu'il puisse construire le plugin sous Linux et sous Windows. De plus, j'ai dû supprimer certains paramètres de construction MinGW non pris en charge, en conséquence le Makefile a commencé à ressembler à ceci:
Makefile pour Windows LD = ld CC = cc PKG_CONFIG = pkg-config INSTALL = install CFLAGS = -g -O2 -Wall -Wextra LDFLAGS = LIBS = VLC_PLUGIN_CFLAGS := $(shell $(PKG_CONFIG) --cflags vlc-plugin) VLC_PLUGIN_LIBS := $(shell $(PKG_CONFIG) --libs vlc-plugin) VLC_PLUGIN_DIR := $(shell $(PKG_CONFIG) --variable=pluginsdir vlc-plugin) plugindir = $(VLC_PLUGIN_DIR)/misc override CC += -std=gnu99 override CPPFLAGS += -DPIC -I. -Isrc override CFLAGS += -fPIC override LDFLAGS += -Wl,-no-undefined override CPPFLAGS += -DMODULE_STRING=\ override CFLAGS += $(VLC_PLUGIN_CFLAGS) override LIBS += $(VLC_PLUGIN_LIBS) SUFFIX := so ifeq ($(OS),Windows_NT) SUFFIX := dll endif all: libhello_plugin.$(SUFFIX) install: all mkdir -p -- $(DESTDIR)$(plugindir) $(INSTALL) --mode 0755 libhello_plugin.$(SUFFIX) $(DESTDIR)$(plugindir) install-strip: $(MAKE) install INSTALL= uninstall: rm -f $(plugindir)/libhello_plugin.$(SUFFIX) clean: rm -f -- libhello_plugin.$(SUFFIX) src/*.o mostlyclean: clean SOURCES = hello.c $(SOURCES:%.c=src/%.o): $(SOURCES:%.c=src/%.c) libhello_plugin.$(SUFFIX): $(SOURCES:%.c=src/%.o) $(CC) $(LDFLAGS) -shared -o $@ $^ $(LIBS) .PHONY: all install install-strip uninstall clean mostlyclean
Étant donné que MSYS2 ne sait rien de notre sdk pour VLC, vous devez ajouter le chemin d'accès au dossier
pkgconfig de ce sdk à la
variable d' environnement
PKG_CONFIG_PATH . Ouvrez la console MinGW (
C: \ msys64 \ mingw64.exec ) et exécutez les commandes:
export PKG_CONFIG_PATH=/c/projects/vlc-3.0.8/sdk/lib/pkgconfig:$PKG_CONFIG_PATH make install
Pour tester le plugin, exécutez également VLC à partir de la console:
/c/projects/vlc-3.0.8/vlc
Comme dans le cas de Linux, allez dans les paramètres et activez notre plugin. Nous enregistrons les paramètres et redémarrons le VLC.
Implémentation du plugin
Pour implémenter mon plugin, j'avais besoin de comprendre comment contrôler le lecteur multimédia (changer la piste audio, rembobiner) et comment gérer les événements de frappe de clavier. Pour comprendre tout cela, je me suis tourné vers la
documentation . Également sur Internet, j'ai trouvé quelques articles intéressants qui éclairent l'architecture du lecteur multimédia:
l'architecture du cadre multimédia VLC et la
documentation de l'API du lecteur multimédia VLC .
VLC se compose d'un grand nombre de modules indépendants (400+). Chaque module doit fournir des informations sur le type de fonctionnalité qu'il implémente, ainsi que les fonctions d'initialisation / finalisation. Ces informations sont décrites dans les
blocs vlc_module_begin () -
vlc_module_end () à l'aide des
macros set_capability () et
set_callbacks () . Les fonctions d'initialisation / finalisation du module (généralement appelées
Open et
Close ) ont la signature suivante:
static int Open(vlc_object_t *) static void Close(vlc_object_t *)
vlc_object_t est le type de base pour représenter les données dans VLC, dont toutes les autres sont héritées (voir l'article
Object_Management ). Un pointeur vers
vlc_object_t doit être
converti en un type de données spécifique conformément à la fonctionnalité implémentée par le module. Pour contrôler le lecteur multimédia, j'ai
défini la valeur de
l' interface dans la macro
set_capability () . Par conséquent, dans les fonctions
Ouvrir et
Fermer , je dois
convertir vlc_object_t en
intf_thread_t .
L'interaction entre les modules est basée sur le modèle de conception de l'
observateur . VLC fournit un mécanisme de
«variables d'objet» (voir
Variables ), avec lequel vous pouvez ajouter des variables aux instances de type
vlc_object_t (et ses dérivés). Les modules peuvent échanger des données via ces variables. Vous pouvez également attacher une fonction de rappel à la variable, qui sera appelée lorsque la valeur de cette variable change.
Par exemple, considérons le module
Hotkeys (
modules / control / hotkeys.c ), qui est responsable de la gestion des événements de raccourci clavier. Dans la fonction Ouvrir, la fonction de rappel
ActionEvent est
bloquée sur la
variable action-clé :
var_AddCallback( p_intf->obj.libvlc, "key-action", ActionEvent, p_intf );
Un pointeur sur
vlc_object_t , un nom de variable, une fonction de rappel et un pointeur sur void sont transmis à la fonction
var_AddCallback pour transmettre des données arbitraires, qui sont ensuite transmises à la fonction de rappel spécifiée. La signature de la fonction de rappel est illustrée ci-dessous.
static int ActionEvent(vlc_object_t *, char const *, vlc_value_t, vlc_value_t, void *)
Un pointeur vers
vlc_object_t , le nom de la variable, les anciennes et nouvelles valeurs de cette variable (dans ce cas, l'identifiant de la combinaison de touches de raccourci enfoncée correspondante de l'action), ainsi que le pointeur vers toutes les données supplémentaires spécifiées lors de l'ajout de la fonction de rappel, sont passés à la fonction de rappel. .
Le traitement direct des événements de raccourci clavier est effectué dans la fonction
PutAction , qui est appelée dans la fonction de rappel
ActionEvent . La fonction
PutAction accepte un identifiant d'un événement d'appuyer sur une combinaison de touches de
raccourci (
i_action ) et, avec l'aide de l'opérateur de commutation, effectue les actions correspondantes.
Par exemple, un événement de rembobinage correspond à
ACTIONID_JUMP_BACKWARD_SHORT
. Pour effectuer l'action correspondante, l'intervalle de rembobinage est pris à partir des paramètres VLC (à partir de la
taille de saut rapide variable):
mtime_t it = var_InheritInteger( p_input, varname );
Pour rembobiner le fichier en cours de lecture, définissez simplement la variable de
décalage temporel sur la valeur correspondant à l'heure (en microsecondes) à laquelle vous souhaitez décaler la lecture:
var_SetInteger( p_input, "time-offset", it * sign * CLOCK_FREQ );
Pour l'avance rapide, vous devez spécifier une valeur positive, pour le retour rapide - négatif. La
constante CLOCK_FREQ est utilisée pour convertir les secondes en microsecondes.
De même, la piste audio change (événement
ACTIONID_AUDIO_TRACK
). Seule la variable
audio-es responsable de la piste audio peut accepter un ensemble limité de valeurs (conformément aux pistes audio disponibles dans le fichier en cours de lecture). Vous pouvez obtenir une liste des valeurs possibles d'une variable en utilisant la fonction
var_Change () :
vlc_value_t list, list2; var_Change( p_input, "audio-es", VLC_VAR_GETCHOICES, &list, &list2 );
En plus de la liste des valeurs, cette fonction vous permet également d'obtenir une liste des descriptions de ces valeurs (dans ce cas, le nom des pistes audio). Maintenant, nous pouvons changer la piste audio en utilisant la fonction
var_Set () :
var_Set( p_input, "audio-es", list.p_list->p_values[i] );
Comment gérer le lecteur multimédia compris, il reste à apprendre à gérer les événements du clavier. Malheureusement, je n'ai pas pu ajouter de nouveau raccourci clavier. Tous les raccourcis clavier sont codés en dur dans le code du noyau VLC (
src / misc / actions.c ). Par conséquent, j'ai ajouté un gestionnaire pour les événements de frappe de clavier de niveau inférieur, suspendant ma fonction de rappel pour modifier la variable
appuyée sur une
touche :
var_AddCallback( p_intf->obj.libvlc, "key-pressed", KeyboardEvent, p_intf );
La variable
appuyée sur la touche stocke le code de caractère (en Unicode) correspondant à la dernière touche appuyée. Par exemple, lorsque vous appuyez sur la touche portant le numéro
«1» , la variable
appuyée sur la
touche se verra attribuer la valeur
49 (0x00000031 dans le système de 16e numéro). Vous pouvez afficher d'autres codes de caractères sur
unicode-table.com . De plus, la valeur de la variable
pressée prend en compte la pression des touches de modification, le quatrième octet significatif leur est alloué. Ainsi, par exemple, lorsque vous appuyez sur la combinaison de
touches "
Ctrl + 1 ", la variable
pressée se verra attribuer la valeur 0x
04 000031 (00000
1 00 00000000 00000000 00110001
2 ). Pour plus de clarté, le tableau ci-dessous montre la signification de différentes combinaisons de touches:
Faites attention à la valeur lorsque vous appuyez sur la combinaison «
Maj + 1 ». Puisque dans ce cas le «
! ", Alors la valeur du premier octet correspondra au code de ce caractère en Unicode (0x00000021).
Résultat
J'ai appelé mon plugin
TIP un acronyme pour l'expression «traduisez-le, s'il vous plaît», aussi astuce peut être traduit par «indice». Le code source du plugin est publié sur
GitHub , où vous pouvez également télécharger des assemblys de plug-in prêts à l'emploi pour Windows et Linux.
Pour installer le plugin, vous devez copier le fichier
libtip_plugin.dll (libtip_plugin.so pour Linux) dans le dossier
<path-to-vlc> / plugins . Sous Windows, VLC est généralement installé dans le dossier
C: \ Program Files \ VideoLAN \ VLC . Sous Linux, vous pouvez trouver le dossier d'installation à l'aide de la commande:
whereis vlc
Dans Ubuntu, par exemple, VLC est installé dans
/ usr / lib / x86_64-linux-gnu / vlc .
Ensuite, vous devrez redémarrer VLC, puis dans le menu principal, ouvrez
Outils >
Préférences , passez à la vue avancée (sélectionnez
Tout dans le groupe
Afficher les paramètres ), dans le panneau de gauche, accédez à la section
Interface >
Contrôle et cochez la case à côté de
TIP (traduisez-le, s'il vous plaît) . Là encore, vous devrez redémarrer le VLC.

Dans les paramètres du plugin, vous pouvez spécifier les numéros des pistes audio principale et auxiliaire (pour la traduction), ainsi que le temps (en secondes) par lequel le plugin se rembobinera pour répéter avec la piste audio auxiliaire.

Pour contrôler le plugin, j'ai ajouté les raccourcis clavier suivants:
- " / " Pour la traduction
- " Shift + / " pour répéter le fragment vidéo précédemment traduit avec la piste audio principale
Lors de l'exécution des commandes de traduction et de nouvelle tentative, le plug-in affiche respectivement les messages
«TIP: translate» et
«TIP: repeat» dans le coin supérieur gauche.

D'après mon expérience en utilisant le plugin, je peux dire qu'en général, je suis satisfait du résultat. L'essentiel est de choisir le bon contenu, si au moins la moitié du discours étranger utilisé y est compris, le plugin aidera à traduire le reste. Sinon, le plugin sera probablement inutile.