[ << ] [ >> ]           [Top] [Table des matières] [Index] [ ? ]

22. Création de greffons

L'API de création des greffons de Cinelerra date de 1997, avant que LADSPA et avant que VST ne deviennent populaires. Il est resté identique à ce qu'il était en 1997, avec des modifications mineures pour manipuler les images-clés et les informations reçues en retour de l'interface graphique. Malheureusement, l'interface graphique ne contient pas de niveau d'abstraction accessible au programmeur. Ceci permet au programmeur d'utiliser la boîte à outils ("toolkit") qu'il désire et permet davantage de flexibilité en apparence mais demande plus d'efforts.

Il y a différents types de greffons, chacun comporte une procédure d'implémentation commune avec des modifications spécifiques à son type particulier. La manière la plus simple pour implémenter un greffon est de prendre le plus simple de son groupe et d'en renommer les symboles.


22.1 Introduction à la méthode PULL

La manière la plus simple de concevoir des greffons est la méthode "pousser" ("push"). La méthode "pousser" est intuitive. Une source pousse des données dans le greffon, le greffon effectue des opérations mathématiques sur ces données et le greffon les pousse vers leur destination. Pendant 6 ans, ceci a été la manière dont les greffons étaient gérés de manière interne, mais cela ne permettait pas de réduire le débit des données en temps réel. Bien que les greffons continuent à être écrits comme s'ils poussaient les données, ce n'est plus de cette manière que le traitement interne est fait dorénavant.

La dernière évolution dans la conception des greffons de Cinelerra est la méthode "tirer" ("pull"). Le pipeline de rendu commence par la fin, et les dernières étapes dans le pipeline de rendu demandent des informations aux étapes qui les précèdent. Lorsque le pipeline demande ensuite des données à une chaîne de greffons, chaque greffon va demander des données au greffon qui le précède.

C'est moins intuitif mais plus puissant que la méthode "pousser". Les greffons en temps réel écrits pour utiliser la méthode "tirer" peuvent non seulement modifier le débit auquel les données sont présentées à la visionneuse mais aussi le sens de la lecture. La méthode "tirer" permet aux greffons de prendre les données à un débit supérieur à celui auquel ils les envoient.

Pour tirer toute la puissance de l'indépendance du débit, il est nécessaire, dans la méthode "tirer", que le greffon connaisse davantage de choses concernant les données que ce qui est nécessaire avec la méthode "pousser". Les greffons doivent connaître le débit du projet et à quel débit se fait leur demande de sortie. Ces deux débits de données doivent être interchangés pour permettre la configuration correcte d'un greffon.

Les images-clés d'un greffon sont enregistrées relativement à la fréquence d'images du projet. Les requêtes de position actuelle de la lecture sont relatives à la fréquence des images du projet. Il est inutile que le greffon demande des données à deux fois la fréquence d'images du projet car les images-clés ne correspondront pas aux bonnes positions des données. Deux classes de données ont été créées pour répondre à ce problème.

Les conversions de débit sont faites en termes de débit de projet et de débit demandé. Le débit du projet est identique pour tous les greffons. Il est déterminé dans la fenêtre Configuration->Format.... Le débit demandé est déterminé par le greffon situé plus bas dans le flux qui demande des données au greffon en cours. Il est arbitraire. Vous trouverez plus loin une explication exacte sur la manière d'utiliser ces débits.


22.2 Fonctions communes aux greffons

Tous les greffons héritent d'un dérivé de PluginClient. Ce dérivé de PluginClient implémente la plupart des méthodes requises dans PluginClient, mais les utilisateurs doivent quand même définir des méthodes pour PluginClient. les méthodes les plus couramment utilisées sont prédéfinies sous forme de macros pour simplifier la saisie tout en conservant le maximum de flexibilité.

Les fichiers qu'ils incluent dépendent du type de greffon. Les greffons audio incluent `pluginaclient.h' et les greffons vidéo incluent `pluginvclient.h'. Ils héritent respectivement de PluginAClient et de PluginVClient.

Cinelerra crée au moins deux instances de tous les greffons lorsqu'ils sont utilisés dans un film. Une instance est réservée à l'interface utilisateur graphique. L'autre instance est réservée au traitement du signal. L'entrée utilisateur, à travers une séquence compliquée, est propagée depuis l'instance de l'interface graphique à l'instance de traitement du signal. Si le traitement du signal a besoin de modifier l'interface graphique, il propage les données en retour vers l'instance de l'interface. Il y a des fonctions utilitaires pour effectuer tout ça.

Les greffons définissent au moins trois objets :


22.2.1 L'objet de traitement

Chargez un greffon simple tel que le gain pour voir à quoi ressemble cet objet. L'objet de traitement doit hériter du dérivé prévu de PluginClient. Son constructeur doit avoir un paramètre PluginServer.

MyPlugin(PluginServer *server);

Dans l'implémentation, le greffon doit comporter une ligne d'enregistrement avec le nom de l'objet traitement telle que

REGISTER_PLUGIN(MyPlugin)

Le constructeur doit contenir

PLUGIN_CONSTRUCTOR_MACRO

pour initialiser les variables les plus courantes.

L'objet traitement doit avoir un destructeur comportant

PLUGIN_DESTRUCTOR_MACRO

pour supprimer les variables les plus courantes.

Une autre fonction utile mais non nécessaire est

int is_multichannel();

Elle doit retourner 1 si une instance du greffon gère simultanément plusieurs pistes et 0 si une instance du greffon ne gère qu'une piste. Si cette valeur est omise, elle est de 0 par défaut.

Les greffons multicanal doivent, dans leur fonction de traitement, se référer à une fonction appelée PluginClient::get_total_buffers()#pour déterminer le nombre de canaux.

Pour simplifier l'implémentation des greffons en temps réel, une macro pour les membres habituellement utilisés a été créée pour les en-têtes de classes. Elle prend comme paramètres l'objet de configuration et le processus objet de l'interface utilisateur. Les définitions de macros s'adressent principalement aux greffons en temps réel et ne sont pas utiles pour les greffons qui ne sont pas en temps réel. Heureusement, les greffons qui ne sont pas en temps réel sont plus simples.

PLUGIN_CLASS_MEMBERS(config_name, thread_name)

Les membres de PLUGIN_CLASS_MEMBERS couramment utilisés sont décrits ci-dessous.

int load_configuration();
Charge la configuration en se basant sur les images-clés qui se trouvent à proximité et sur la position actuelle. La définition de classe pour load_configuration doit contenir

LOAD_CONFIGURATION_MACRO(plugin_class, config_class)

pour implémenter le comportement par défaut de load_configuration. Ceci enregistre dans l'objet de configuration du greffon ce qui se trouve dans la configuration actuelle, et retourne 1 si la nouvelle configuration est différente de la précédente. La valeur de retour de load_configuration est utilisée par une autre fonction couramment utilisée, update_gui pour déterminer si l'interface graphique a vraiment besoin d'être mise à jour.
L'objet de configuration du greffon est toujours appelé config dans PLUGIN_CLASS_MEMBERS.

VFrame* new_picon();
Crée une vignette à afficher dans la fenêtre des ressources. Utilisez

#include "picon_png.h"
NEW_PICON_MACRO(plugin_class)

pour implémenter une nouvelle vignette. De plus, l'utilisateur doit créer le fichier d'en-tête `picon_png.h' à partir d'une image PNG en utilisant pngtoh. pngtoh est construit dans le répertoire `guicast/ARCH'.
L'image PNG source doit s'appeler `picon.png' et peut être de n'importe quel format géré par PNG.

char* plugin_title();
Retourne une chaîne de texte identifiant le greffon dans la fenêtre des ressources. Cette chaîne doit être unique.

void update_gui();
Doit d'abord charger la configuration, teste un code de retour égal à 1 et redessine ensuite l'interface graphique avec les nouveaux paramètres. Tous les greffons qui utilisent GuiCast ont un format semblable à

    void MyPlugin::update_gui()
    {
        if(thread)
        {
        if(load_configuration())
        {
            thread->window->lock_window();
            // update widgets here
            thread->window->unlock_window();
        }
        }
    }

pour gérer les accès concurrents et les conditions dans lesquelles il n'y a pas d'interface graphique.

int show_gui();
Instancie l'interface graphique et bascule le greffon en mode interface graphique ("GUI mode"). C'est implémenté par

SHOW_GUI_MACRO(plugin_class, thread_class)

int set_string();
Change le titre de la fenêtre de l'interface graphique en utilisant une certaine chaîne de caractères. C'est implémenté par

SET_STRING_MACRO(plugin_class)

void raise_window();
Affiche la fenêtre de l'interface graphique sur le dessus de la pile. C'est implémenté par

RAISE_WINDOW_MACRO(plugin_class)

Des fonctions importantes que doit définir l'objet de traitement sont les fonctions qui chargent et enregistrent les données de configuration depuis les images-clés. Ces fonctions sont appelées par les macros, et vous n'avez donc pas à vous préoccuper de l'accès aux données des images-clés.

void save_data(KeyFrame *keyframe);
void read_data(KeyFrame *keyframe);

Les fonctions read_data sont utilisées dans les greffons en temps réel. Les fonctions data_functions convertissent la configuration du greffon depuis le paramètre KeyFrame vers l'objet de configuration du greffon. Les images-clés sont enregistrées sur la timeline et peuvent changer pour chaque projet.

Utilisez un objet appelé FileXML pour effectuer la translation et certaines commandes spécifiques pour obtenir les données à partir du paramètre KeyFrame. Vous trouverez des exemples d'utilisation de KeyFrame et de FileXML dans les greffons existants.

int load_defaults();
int save_defaults();

Les fonctions load_defaults sont utilisées dans les greffons en temps réel et les greffons qui ne sont pas en temps réel. Les fonctions load_defaults convertissent la configuration du greffon entre un objet BC_Hash et la configuration du greffon. L'objet BC_Hash enregistre les configurations dans des fichiers individuels sur disque pour chacun des greffons, mais il n'isole pas les configurations entre les différents projets.

La fonction qui surcharge load_defaults doit aussi créer l'objet BC_Hash. Vous pouvez consulter un greffon existant pour des exemples d'utilisation de BC_Hash.

D'autres objets standards peuvent être définis dans l'objet de traitement, ils dépendent du type de greffon.


22.2.2 L'objet de configuration

L'objet de configuration est critique pour les mises à jour de l'interface graphique, le traitement du signal, et les paramètres par défaut dans les greffons en temps-réel. Remarquez qu'il n'est pas utilisé dans les greffons qui ne sont pas temps réel. L'objet de configuration n'hérite de rien et n'a pas de dépendances. Il est constitué simplement d'une classe contenant trois fonctions et les variables spécifiques aux paramètres du greffon.

Habituellement, l'objet de configuration commence par le nom du greffon suivi de Config.

    class MyPluginConfig
    {
    public:
        MyPluginConfig();

Après le nom de la classe de configuration, nous trouvons les trois fonctions requises et les variables de configuration.

        int equivalent(MyPluginConfig &that);
        void copy_from(MyPluginConfig &that);
        void interpolate(MyPluginConfig &prev,
        MyPluginConfig &next,
        int64_t prev_position,
        int64_t next_position,
        int64_t current_position);
        float parameter1;
        float parameter2;
        int parameter3;
    };

Maintenant il faut définir les trois fonctions. Equivalent est appelée par LOAD_CONFIGURATION_MACRO pour déterminer si les paramètres de la configuration locale sont identiques aux paramètres de la configuration de l'argument. Si equivalent retourne 0, load_configuration permet de redessiner l'interface graphique. Si equivalent retourne 1, l'interface graphique n'est pas redessinée.
Ensuite, il y a copy_from qui transfère les valeurs de configuration depuis le paramètre vers les variables locales. C'est de nouveau utilisé dans LOAD_CONFIGURATION_MACRO pour enregistrer les configurations dans des espaces temporaires. Une fois que LOAD_CONFIGURATION_MACRO a répliqué la configuration, elle charge une seconde configuration. Elle interpole ensuite les deux configurations pour obtenir la configuration actuelle. Les fonctions d'interpolation réalisent l'interpolation et enregistrent les résultats dans les variables locales.

Normalement, la fonction d'interpolation calcule une fraction précédente et une fraction suivante en utilisant les paramètres.

    void MyPluginConfig::interpolate(MyPluginConfig &prev,
        MyPluginConfig &next,
        int64_t prev_position,
        int64_t next_position,
        int64_t current_position
    {
        double next_scale =
        (double)(current_position - prev_position)
        / (next_position - prev_position);
        double prev_scale =
        (double)(next_position - current_position) /
        (next_position - prev_position);

Ensuite, les fractions sont appliquées à l'objet de configuration précédent et suivant pour obtenir les valeurs actuelles.

        this->parameter1 =
        (float)(prev.parameter1 * prev_scale
        + next.parameter1 * next_scale);
        this->parameter2 =
        (float)(prev.parameter2 * prev_scale
        + next.parameter2 * next_scale);
        this->parameter3 =
        (int)(prev.parameter3 * prev_scale
        + next.parameter3 * next_scale);
    }

Vous pouvez aussi copier les valeurs depuis le paramètre de configuration précédent si vous ne souhaitez pas d'interpolation.

Cette utilisation est identique pour les greffons audio ou vidéo. Lors de la lecture vidéo, la fonction d'interpolation est appelée à chaque image, ce qui permet d'obtenir une interpolation lissée. Lors de la lecture audio, la fonction d'interpolation est appelée pour chaque fragment de console et une fois chaque fois que le point d'insertion se déplace. C'est suffisant pour effectuer la mise à jour de l'interface graphique lorsqu'on sélectionne des régions sur la timeline, mais ce n'est pas assez précis pour un rendu réellement fluide de l'effet.

Pour des rendus réellement fluides de l'audio, vous pouvez continuer à utiliser load_configuration pour la mise à jour de l'interface graphique. Cependant, pour le traitement en temps réel, oubliez load_configuration et écrivez votre propre routine d'interpolation qui chargera toutes les images-clés d'un fragment de console et interpolera chaque échantillon. Ceci sera vraiment très lent et difficile à mettre au point, pouvant conduire à des améliorations qui ne seront même pas audibles.

Une manière plus simple de rendre l'interpolation plus fine est de réduire la taille des fragments de console à 1 échantillon. Ceci devra être rendu et reproduit avec un fragment de console qui soit de nouveau de l'ordre de 2048, bien entendu. Le pilote audio de GNU/Linux ne sait pas lire des fragments de durée réduite à 1 échantillon.


22.2.3 L'objet d'interface utilisateur

L'objet d'interface utilisateur enfin, consiste en un pointeur vers une fenêtre et des pointeurs vers tous les éléments graphiques de la fenêtre. En utilisant la boîte à outil de Cinelerra, il consiste en un dérivé de BCWindow et un dérivé de Thread. Le dérivé de Thread est déclaré dans l'en-tête du greffon en utilisant

PLUGIN_THREAD_HEADER(plugin_class, thread_class, window_class)

Il est ensuite défini par

PLUGIN_THREAD_OBJECT(plugin_class, thread_class, window_class)

Ceci, en combinaison avec la macro SHOW_GUI, effectue tout le travail d'instanciation de la fenêtre. Ce système de deux classes est utilisé dans les greffons en temps réel mais pas dans les greffons qui ne sont pas en temps réel. Les greffons qui ne sont pas en temps réel créent et détruisent leur interface graphique dans leur fonction get_parameters et il n'y a pas besoin de Thread.

Maintenant, la classe de la fenêtre doit être déclarée dans l'en-tête du greffon. Le plus facile pour implémenter la fenêtre est de copier un greffon existant et d'en renommer les symboles. Ce qui suit est un aperçu de ce qui se passe. L'en-tête du greffon doit déclarer le constructeur de la fenêtre en utilisant les paramètres appropriés.

    #include "guicast.h"
    class MyPluginWindow : public BC_Window
    {
    public:
        MyPluginWindow(MyPluginMain *plugin, int x, int y);

Ceci apparaît sous la forme d'une fenêtre sur l'écran placée en x et y.

Il a besoin de deux méthodes

int create_objects();
int close_event();

et un pointeur arrière vers le greffon

MyPlugin *plugin;

La définition du constructeur doit comporter des extents et des flags pour que la fenêtre soit cachée lorsqu'elle vient d'être créée. Le membre de l'objet créé place les objets graphiques dans la fenêtre selon la syntaxe de GuiCast. Un pointeur vers chaque élément graphique que vous désirez synchroniser à un paramètre de configuration est enregistré dans la classe de la fenêtre. Ils sont mis à jour dans la fonction update_gui que vous avez précédemment défini dans le greffon. Les éléments graphiques sont habituellement des dérivés d'un élément graphique GuiCast et ils surchargent des fonctions dans GuiCast afin de gérer des événements. Enfin, créez les appels aux objets

show_window();
flush();

afin que la fenêtre soit dessinée en une seule fois.

Le membre close_event doit être implémenté en utilisant

WINDOW_CLOSE_EVENT(window_class)

Chaque élément graphique de l'interface doit détecter quand sa valeur est modifiée. Dans GuiCast, la méthode handle_event est appelée quand une valeur change. Dans handle_event, l'élément graphique doit appeler plugin->send_configure_change() pour propager les changements à toutes les copies du greffon qui traitent des données.


22.3 Greffons en temps réel

Les greffons en temps réel doivent utiliser la classe PLUGIN_CLASS_MEMBERS pour définir le jeu de base des membres dans leurs en-têtes. Tous les greffons en temps réel doivent définir un membre

int is_realtime()

qui retourne 1. Ceci entraîne l'appel d'un certain nombre de méthodes lors de la lecture "live" et permet au greffon d'être utilisable sur la timeline.

Les greffons en temps réel doivent surcharger un membre appelé process_buffer

Cette fonction prend différents paramètres selon que le greffon gère de l'audio ou de la vidéo. Le mieux est d'examiner comment est réalisé un greffon existant pour voir comment cela est appliqué.

La fonctionnalité principale de la fonction process_buffer est de fournir un tampon où placer la sortie, de définir la position de départ pour la sortie et de définir le débit requis en sortie. Pour de l'audio, il y a aussi un paramètre qui définit le nombre d'échantillons.

La position de départ dans le tampon de sortie est l'échantillon de rang le moins élevé sur la timeline si la lecture a lieu vers l'avant et l'échantillon de rang le plus élevé si la lecture a lieu en arrière. Le sens de la lecture est déterminé par une des requêtes du greffon, décrite ci-dessous.

Les paramètres de dimensions et de position sont tous relatifs à la fréquence des images et au taux d'échantillonnage passés à process_buffer. C'est le débit des données requis et il peut être différent du débit du projet.

La fonction process_realtime doit commencer par l'appel de load_configuration. LOAD_CONFIGURATION_MACRO retourne 1 si la configuration a été modifiée.

Après avoir déterminé la configuration du greffon, le média d'entrée doit être chargé pour le traitement. Appelez :

    read_frame(VFrame *buffer,
        int channel,
        int64_t start_position,
        double frame_rate)
ou
    read_samples(double *buffer,
        int channel,
        int sample_rate,
        int64_t start_position,
        int64_t len)

pour demander des données d'entrée à l'objet qui vient juste avant ce greffon. La fonction read a besoin d'un tampon pour y enregistrer les données d'entrée. Il peut être créé de manière temporaire dans le greffon, le tampon de sortie fourni à process_buffer peut aussi être utilisé si vous n'avez pas besoin de tampon temporaire.

Il a aussi besoin d'un ensemble de paramètres de position pour déterminer quand vous désirez lire les données. La position de départ, le débit et la longueur passés à une fonction read n'ont pas besoin d'être les mêmes que les valeurs reçues par la fonction process_buffer. De cette manière, le greffon peut lire des données à un débit différent du débit auquel il envoie les données vers la sortie.

Le paramètre channel n'a de signification que pour les greffons multicanaux. Ils leur faut lire les données pour chaque piste dans la valeur get_total_buffers() et traiter toutes les pistes. Les greffons monocanal doivent passer la valeur 0 pour channel.

Des membres supplémentaires sont implémentés pour maintenir la configuration des greffons en temps réel. Certains d'entres-eux sont nécessaires aussi dans le cas des greffons qui ne sont pas en temps réel.


22.4 Greffons qui ne sont pas en temps réel

Quelques exemples de greffons qui ne sont pas en temps réel sont Normaliser pour l'audio et Reframe pour la vidéo.

Comme pour les greffons en temps réel, il faut implémenter load_defaults et save_defaults. Pour les greffons qui ne sont pas en temps réel, elles sont utilisées non seulement pour les paramètres par défaut mais aussi pour transférer des valeurs depuis l'interface utilisateur vers le moteur de traitement du signal. Elles n'ont pas besoin d'être une classe de configuration pour les greffons qui ne sont pas en temps réel.

A l'inverse des greffons en temps réel, il ne faut pas utiliser LOAD_CONFIGURATION_MACRO dans l'en-tête du greffon. Il faut, à la place, définir les méthodes suivantes.

Le greffon qui n'est pas en temps réel doit contenir un pointeur vers l'objet par défaut.

BC_Hash *defaults;

Il doit aussi comporter un pointeur vers une barre d'avancement MainProgressBar.

MainProgressBar *progress;

Le pointeur d'avancement permet aux greffons qui ne sont pas en temps réel d'afficher leur avancement dans la fenêtre principale de Cinelerra.

Le constructeur pour les greffons qui ne sont pas en temps réel ne peut pas utiliser PLUGIN_CONSTRUCTOR_MACRO, mais doit appeler directement load_defaults.

Le destructeur, de la même manière, doit appeler save_defaults et delete_defaults directement à la place de PLUGIN_DESTRUCTOR_MACRO#.


22.5 Greffons audio

Le plus simple des greffons audio est le Gain. L'objet de traitement doit inclure `pluginaclient.h' et hériter de PluginAClient. Les greffons audio en temps réel doivent définir

    int process_buffer(int64_t size,
        double **buffer,
        int64_t start_position,
        int sample_rate);
s'ils sont multicanal ou
    int process_buffer(int64_t size,
        double *buffer,
        int64_t start_position,
        int sample_rate);

en simple canal. Ils doivent toujours retourner 0 en cas de succès et 1 en cas d'échec. Dans le futur, la valeur de retour pourra provoquer un abandon du rendu par échec.

Les fonctions de traitement doivent demander des échantillons d'entrée avec

    int read_samples(double *buffer,
        int channel,
        int sample_rate,
        int64_t start_position,
        int64_t len);

Elles retournent toujours 0. L'utilisateur peut indiquer un taux d'échantillonnage et une positon de départ quelconque.

Les greffons audio qui ne sont pas en temps réel doivent définir

{int process_loop(double *buffer, int64_t &write_length);}

pour un seul canal ou

{int process_loop(double **buffers, int64_t &write_length);}

en multicanal. Les greffons qui ne sont pas en temps réel utilisent un jeu différent de fonctions read_samples pour demander des données d'entrée. Elles sont fixées au taux d'échantillonnage du projet.


22.6 Greffons vidéo

Le plus simple des greffons vidéo est l'effet "Retourner". L'objet de traitement doit inclure `pluginvclient.h' et hérite de PluginVClient. Les greffons en temps réel doivent définir

    int process_buffer(VFrame **frame,
        int64_t start_position,
        double frame_rate);

s'ils sont multicanaux, ou

    int process_buffer(VFrame *frame,
        int64_t start_position,
        double frame_rate);

si c'est pour un seul canal.

Les greffons vidéo qui ne sont pas en temps réel doivent définir

{int process_loop(VFrame *buffer);}
pour un seul canal, ou
{int process_loop(VFrame **buffers);}

en multicanal. Le nombre d'images créées dans une seule boucle de traitement est toujours supposé être de 1 en raison de l'absence d'un paramètre write_length. Un code retour de 0 permet au rendu de se poursuivre. Un code retour de 1 arrête le rendu.

Il existe un jeu de fonctions read_frame pour demander des images d'entrée dans les greffons vidéo qui ne sont pas en temps réel. Ils sont fixés à la fréquence d'images du projet.


22.7 Greffons de transitions

La plus simple des transitions vidéo est le volet et la plus simple des transitions audio est le fondu enchaîné. Elles utilisent un sous-ensemble des membres de la classe par défaut des greffons en temps réel, mais ce n'est cependant pas analogue à ce qui a été effectué par PLUGIN_CLASS_MEMBERS pour les transitions.

L'objet de traitement pour les transitions audio hérite toujours de PluginAClient et il hérite toujours de PluginVClient pour les transitions vidéo.

Les transitions peuvent ou non avoir une interface graphique. Si elles en ont une, elles doivent aussi gérer un processus ("thread") comme les greffons temps réel. Ceci est effectué avec les mêmes macros PLUGIN_THREAD_OBJECT et PLUGIN_THREAD_HEADER que pour les greffons en temps réel. Comme il n'y a qu'une seule image-clé par transition, vous n'avez pas à vous préoccuper de la mise à jour de l'interface utilisateur à partir de l'objet de traitement comme vous deviez le faire pour les greffons en temps réel.

Si la transition possède une interface utilisateur graphique, vous pouvez utiliser les macros PLUGIN_CONSTRUCTOR_MACRO et PLUGIN_DESTRUCTOR_MACRO pour initialiser l'objet de traitement. Vous aurez également besoin pour ces macros d'un objet BC_Hash et d'un objet thread.

Comme l'interface graphique utilisateur est optionnelle, surchargez la fonction appelée uses_gui() pour indiquer si la transition possède ou non une interface graphique. Elle doit retourner 1 si elle est présente et 0 dans le cas contraire.

Les transitions ont besoin des fonctions load_defaults et save_defaults de façon à avoir une configuration utilisable la première fois qu'elles seront déposées sur la timeline.

Les fonctions read_data et save_data leur succèdent après l'insertion pour accéder aux données qui sont spécifiques à chaque instance de la transition.

La différence la plus importante entre les transitions et les effets en temps réel est l'ajout d'une méthode is_transition à l'objet de traitement. is_transition retourne 1 pour indiquer que le greffon est une transition.

Les transitions traitent les données dans une fonction process_realtime function.

    int process_realtime(VFrame *input,
        VFrame *output);
    int process_realtime(int64_t size,
        double *input_ptr,
        double *output_ptr);

Les données pour le plan suivant sont utilisées comme paramètre d'entrée de process_realtime. Le paramètre de sortie de process_realtime est constitué des données du plan précédent.

Il existe des routines pour déterminer si l'on se trouve placé relativement au début ou à la fin de la transition.

Les utilisateurs doivent diviser la position source par la durée totale pour obtenir la fraction de la transition à laquelle se trouve la fonction process_realtime.

Les transitions tournent dans le débit de données requis par le premier greffon qui se trouve sur la piste. Il peut être différent du débit de données du projet. Comme les processus en temps réels n'ont pas de paramètre de débit, il faut utiliser get_framerate() ou get_samplerate pour obtenir le débit demandé.


22.8 Greffons dont l'interfaces utilisateur se met à jour lors de la lecture

Des effets comme l'Histogramme ou le Vidéoscope ont besoin de mettre à jour l'interface utilisateur pour afficher des informations concernant le signal. Ceci est obtenu avec les méthodes send_render_gui et render_gui methods. Normalement, dans process_buffer, lorsque l'objet de traitement veut mettre à jour l'interface graphique, il devrait appeler send_render_gui. Il ne devrait être appelé que dans process_buffer. send_render_gui commence une recherche et appelle ensuite render_gui dans l'instance de l'interface graphique du greffon.

render_gui devrait comporter une séquence telle que

    void MyPlugin::render_gui(void *data)
    {
        if(thread)
        {
        thread->window->lock_window();
    // update GUI here
        thread->window->unlock_window();
        }
    }

send_render_gui et render_gui utilisent un paramètre, un pointeur de type void pour transférer les informations depuis l'objet de traitement vers l'interface graphique. L'utilisateur devra transtyper le pointeur en quelque chose d'utile.


22.9 Requêtes des greffons

Il existe un certain nombre de requêtes utiles auxquelles on peut accéder dans PluginClient pour le traitement d'objets. Certaines ont une signification différente selon qu'on se trouve dans le mode temps-réel ou non. Elles donnent toutes des informations concernant le système d'exploitation ou le projet, qui peuvent être utilisées pour améliorer la qualité du traitement.


22.9.1 Requêtes système


22.9.2 Requêtes de temps

Un greffon en temps réel doit prendre en compte deux débits : le débit du projet et le débit demandé. Il existe des fonctions pour obtenir les débits du projet et ceux demandés. De plus, la réalisation d'effets dépendant du temps demande l'utilisation de certaines fonctions qui vous permettent de savoir où l'on se trouve dans l'exécution de l'effet.


22.10 Utiliser OpenGL

Les greffons vidéo en temps réel gèrent l'utilisation d'OpenGL. L'utilisation d'OpenGL pour réaliser les greffons peut accélérer de manière importante la lecture car la majeure partie du travail est effectuée par le matériel. Malheureusement, chaque routine OpenGL a besoin d'une contrepartie logicielle pour effectuer le rendu, ce qui double le volume de logiciel à maintenir. Heureusement, l'existence d'une routine OpenGL signifie que la version logicielle n'aura pas besoin d'être aussi optimisée qu'elle l'était lorsque le logiciel était le seul moyen utilisable.

Comme toujours, la meilleure manière de concevoir son premier greffon OpenGL est de copier un greffon existant et de le modifier. Le greffon de Luminosité est un greffon OpenGL simple à copier. Il y a 3 points principaux pour le rendu OpenGL et un point pour optimiser le rendu OpenGL.


22.10.1 Obtenir les données OpenGL

Le premier problème est de faire en sorte que des greffons utilisables avec OpenGL puissent interagir avec des greffons entièrement logiciels. Pour résoudre cela, toutes les informations requises pour la lecture OpenGL se trouvent enregistrées dans l'objet VFrame qui est passé à process_buffer. Pour gérer la 3D, le VFrame comporte, en plus des lignes d'origine du VFrame, un PBuffer et une texture.

En mode OpenGL, le VFrame possède 3 états qui correspondent à l'emplacement de ses données vidéo. L'état opengl est récupéré en appelant get_opengl_state, il est défini en appelant set_opengl_state. Les états sont :

Dans la routine process_buffer du greffon, il y a normalement un appel à read_frame pour obtenir des données depuis le greffon précédent dans la chaîne. read_frame prend un nouveau paramètre appelé use_opengl.

Le greffon passe la valeur 1 à use_opengl s'il a l'intention de gérer les données en utilisant OpenGL. Il passe la valeur 0 à use_opengl s'il ne gère les données que par logiciel. La valeur de use_opengl est passée à la chaîne afin de s'assurer qu'un greffon qui n'utilise que le logiciel n'obtienne les données que dans les pointeurs de lignes. Si use_opengl est à 0, l'état opengl dans VFrame est RAM.

Le greffon ne doit pas seulement savoir s'il est uniquement logiciel, mais aussi si sa sortie ne doit être que logicielle. Appelez get_use_opengl pour déterminer si la sortie peut être gérée par OpenGL. Si get_use_opengl retourne 0, le greffon doit passer 0 pour use_opengl dans read_frame et effectuer son traitement par logiciel. Si get_use_opengl est à 1, le greffon doit décider, en fonction de son implémentation, s'il doit utiliser OpenGL.

Le principal problème avec OpenGL est que tous les appels gl... doivent être lancés depuis le même processus ("thread"). Afin de s'y adapter, l'interface possède les routines nécessaires pour faire tourner OpenGL dans un processus commun.

run_opengl passe le contrôle au processus commun d'OpenGL. Ceci est normalement appelé par le greffon dans process_buffer après qu'il ait appelé read_frame et seulement si get_use_opengl est à 1.

Par une série d'indirections, run_opengl transfère le contrôle à une fonction virtuelle appelée handle_opengl. handle_opengl doit être surchargée par une fonction qui exécute toute les routines OpenGL. Le contenu de handle_opengl soit être compris dans #ifdef HAVE_GL ... #endif afin de lui permettre d'être compilé sur des systèmes n'ayant pas de support graphique, comme des noeuds de rendu. La valeur de retour de handle_opengl est passée en retour depuis run_opengl.

read_frame ne peut pas être appelé depuis handle_opengl. Ceci créerait un verrouillage récursif parce qu'il entraînerait l'appel de run_opengl par d'autres objets.

Une fois dans handle_opengl, le greffon a le plein usage de toutes les fonctionnalités d'OpenGL. VFrame fournit certaines fonctions pour automatiser les séquences communes d'OpenGL.

Le paramètre de VFrame à process_buffer est toujours disponible au travers de la fonction get_output(int layer). Si le greffon est multicanal, le paramètre de calque récupère un calque spécifique des tampons de sortie. Le PBuffer du tampon de sortie est l'endroit où doit aller la sortie OpenGL si un traitement quelconque est effectué.


22.10.2 Dessiner en utilisant OpenGL

La séquence de commandes pour dessiner sur le PBuffer de sortie commence par mettre la vidéo dans une zone mémoire qui peut être rappelée pour dessiner :

get_output()->to_texture();
get_output()->enable_opengl();

L'étape suivante est de dessiner la texture avec certains traitement sur le PBuffer. La séquence de commandes normale pour dessiner une texture est :

get_output()->init_screen();
get_output()->bind_texture(0);
get_output()->draw_texture();

La dernière étape de la routine handle_opengl, après que les textures aient été dessinées sur le PBuffer, est de positionner l'état de la sortie d'opengl à SCREEN par un appel à VFrame::set_opengl_state. Le greffon ne doit pas faire de relecture du tampon vidéo vers une texture ou des pointeurs de ligne s'il ne fait pas de traitement supplémentaire. Les greffons ne doivent laisser que la sortie dans la texture ou la mémoire si son emplacement résulte d'un traitement normal. Il doivent positionner l'état d'opengl à RAM ou TEXTURE s'ils le font.

Les modèles colorimétriques en OpenGL :
Le modèle colorimétrique présenté aux routines OpenGL est toujours en virgule flottante car c'est ce qu'utilise OpenGL, mais ce peut être YUV ou RVB selon les paramètres du projet. Si c'est YUV, il est décalé d'exactement 0,5 comme avec les traitements logiciels. Passer des modèles colorimétrique YUV aux greffons a été rendu nécessaire pour des raisons de vitesse. L'autre possibilité était de convertir YUV en RVB lors de la première étape nécessaire à OpenGL. Chaque effet et étape de rendu aurait alors eu besoin d'une routine de conversion YUV vers RVB. Avec le choix d'YUV, seule l'étape de composition finale doit comporter une routine de conversion YUV vers RGB.


22.10.3 Utiliser les shaders

Il n'y a que très peu d'effets qui puisse faire quelque chose d'utile en dessinant directement des textures dans le PBuffer. Normalement, ils définissent un shader. Le shader est un programme C qui tourne sur la carte graphique. Comme la carte graphique est optimisée pour les graphiques, cela lui permet d'être beaucoup plus rapide que s'il tournait sur le processeur.

Les shaders sont écrits dans le langage de Shading OpenGL. Le code source du shader est contenu dans une chaîne. La séquence normale pour utiliser un shader se trouve après un appel à enable_opengl.

char *shader_source = "...";
unsigned char shader_id = VFrame::make_shader(0, shader_source, 0);
glUseProgram(shader_id);
// Set uniform variables using glUniform commands

Les étapes de compilation et d'édition de liens pour les shaders sont encapsulées dans la commande VFrame::make_shader. Elle retourne un identifiant de shader_id qui peut être passé aux fonctions OpenGL. Le premier et le dernier paramètre doivent toujours être 0. Un nombre arbitraire de chaînes sources peut être placé entre les 0. Les chaînes sources sont concaténées par make_shader en un gros source de shader. S'il y a plusieurs fonctions main, les fonctions main sont renommées et exécutées dans l'ordre.

Il existe de nombreuses macros utiles pour les shaders dans `playback3d.h'. Jusqu'à présent, tous les shaders ont été des "fragment shaders". Après que le shader ait été initialisé, il dessine la texture en partant de init_screen. Le programme shader doit être désactivé par un autre appel à glUseProgram(0) avec 0 comme paramètre.

L'identifiant du shader et le code source sont maintenus en mémoire tant que Cinelerra tourne. Les appels ultérieurs à make_shader avec le même code source s'exécuteront beaucoup plus vite.


22.10.4 Agrégation de greffons

On peut obtenir d'autres améliorations de la vitesse en combinant les routines OpenGL de deux greffons en une seule fonction handle_opengl. Ceci est réalisé lorsque Images vers champs et RVB vers 601 sont attachés dans cet ordre. Les agrégations de plus de deux greffons sont possibles mais très difficiles à faire fonctionner. L'agrégation est utile avec OpenGL parce que chaque greffon doit copier la vidéo depuis une texture vers un PBuffer. Il n'y a pas d'opération de copie par logiciel.

Dans l'agrégation, un greffon traite tour depuis l'autre greffon et l'autre greffon "fall through". Les greffons fall through doivent copier leurs paramètres vers le tampon de sortie de manière à pouvoir être détectés par le greffon qui effectue le traitement.

Le VFrame utilisé comme tampon de sortie comporte une table de paramètres permettant de passer les paramètres entre les greffons et il est accédé par get_output()->get_params(). Les paramètres sont définis et récupérés dans la table par des appels à update et get exactement comme pour les valeurs par défaut.

Le greffon fall through doit déterminer si le greffon de traitement est attaché par des appels à next_effect_is et prev_effect_is. Ils prennent le nom du greffon de traitement comme paramètre de chaîne de caractères et retournent 1 si le greffon suivant ou précédent est le greffon de traitement. Si l'un d'entre-eux retourne 1, le greffon fall through doit quand même appeler read_frame pour propager les données mais rend la main ensuite.

Le greffon de traitement doit appeler next_effect_is et prev_effect_is pour déterminer s'il est agrégé à un greffon fall through. Si c'est le cas, il doit effectuer les opérations du greffon fall through dans sa routine OpenGL. Les paramètres pour le greffon fall through devraient être disponibles par get_output()->get_params() si le greffon fall through les a positionnés.


[ << ] [ >> ]           [Top] [Table des matières] [Index] [ ? ]

Ce document a été généré par Nicolas Maufrais le 13 Mars 2007 en utilisant texi2html 1.76.