Comment écrire un plugin pour K!TV XP...


1. Introduction :

Ce document explique le fonctionnement interne de K!TV XP et la méthode pour écrire un plugin Vidéo pour cette application. Vous êtes libre de nous transmettre vos commentaires à E-nek@kastortv.com ou Quenotte@kastortv.com pour corriger, améliorer ou même ajouter vos propres lignes à ce document.

Ce document suppose que vous connaissiez quelques bases de programmations C/C++ et que vous ayez au moins jetté un coup d'oeil au code de l'excellent plugin Tutorial écrit pas AskyWhale (http://plugintv.free.fr/).

Bonne lecture.

Version de ce document : Version 0.46: 10 Juin 2002.

2. L'application K!TV XP :

K!TV XP permet de regarder la télévision sur votre PC en utilisant votre carte Tuner (à base de chipset BT8x8), sur toutes les plateformes Windows (95, 98, 2000, XP ...). Récemment, l'architecture a été modifiée pour permettre d'ajouter ses propres plugins de traitement et de les lancer à l'intérieur de K!TV XP.

3. Le fonctionnement interne de K!TV XP:

En mode normal (ie sans plugin lancé), l'affichage se déroule dans un thread séparé :

for(;;)
{

Capturer l'image
Afficher l'image

}

La capture se fait par la carte télévision, au format 768 x 576 YUY2 (l'utilisateur peut aussi choisir de capture moins de 768 pixels), ce qui correspond à deux trames de 768x288 (Rappel : le signal vidéo est entrelacé).

Il faut savoir que les trames Even/Odd capturées ne correpondent pas forcement aux trames Even/Odd du signal réél. En clair, il n'y a aucune différence entre la trame Even et Odd avec K!TV XP.

Le format YUY2 utilisé est standard, les pixels sont stockés dans la forme d'information de Luminance (Y) et de Chrominance (U et V). (Voir http://www.webartz.com/fourcc/ pour plus d'informations)

(1 pixel sur 2 octets) (Y0-U0-Y1-V0)(Y2-U2-Y3-V2)(Y4-U4-Y5-V4)

Lorsque le plugin est chargé, le thread d'affichage est légèrement modifié pour faire appel au plugin.

for(;;)
{

Capturer l'image
Traiter l'image
Afficher l'image
}

K!TV XP travaille avec plusieurs buffers vidéo, ainsi, lorsque le buffer courant a été traité, on l'affiche avec ViewFrame=true et on travaille sur un autre buffer.

Remarques :

  • Le thread d'affichage peut être arrêté et relancé à tout moment par K!TV XP, par exemple lorsque l'utilisateur change de résolution...
  • Le plugin doit travailler au format YUY2 (en entrée et en sortie), K!TV XP se charge de la conversion si par exemple l'affichage est au format RGB (l'utilisateur n'a pas choisit DirectX).

4. Le plugin vidéo:

Le plugin vidéo est une DLL classique qui contient plusieurs fonctions de traitement.

K!TV XP accède au plugin par l'intermédiaire de l'objet QuenotteTV_VideoPlugin qui contient les variables et fonctions utilisées pour le dialogue entre K!TV XP et le plugin vidéo.

Lorsque l'utilisateur sélectionne un plugin, voici ce que fait K!TV XP :

  1. La DLL est chargée en mémoire
  2. Le numéro de version du plugin est testé. (La structure de l'objet QuenotteTV_VideoPlugin peut avoir changé). L'initialisation des variables du plugin se fait dans la fonction GetVPlugin()
  3. La fonction Start() est appelée au démarrage du plugin (F3)
  4. La fonction Stop() est appelée à l'arrêt (F2)

Voici ce qui se passe après l'appel de Start() (le thread est réinitialisé) :

InitStuff ()
for(;;)
{

Capturer l'image
DoStuff()
CheckStuff() (appelé toutes les 960 ms ;-)
Afficher l'image
DetecteSignal()

}

Nous avons donc :

  • InitStuff :
    Fonction appelée au départ pour initialiser le traitement
  • DoStuff :
    Fonction appelée pour traiter la dernière image capturée
  • CheckStuff :
    Fonction appelée pour la vérification interne du plugin (optionel)

5. Fonctionnement du plugin vidéo & DoStuff():

Le plugin peut choisir 2 modes de fonctionnement au travers de la variable FullFields, de plus il décide seul ce qui doit être affiché à l'écran et quand l'afficher (grâce aux variables UpdateBuffer et ViewBuffer).

Si l'utilisateur appuie sur la touche tabulation, la fenêtre d'affichage passe du mode FF au mode HF (par défaut le plugin tourne en FF).

  • HF : Une trame
    Appel du plugin après la capture de 1 trame et affichage sur une fenêtre de 288 lignes.
    Seul le buffer OutEvenLines est considéré valide lors de l'affichage.
  • FF : Deux trames
    Appel du plugin après la capture de chaque trame et affichage sur une fenêtre de 576 lignes (2x288), le plugin reçoit d'abord la trame paire puis la trame impaire.
    Les plugins de désentrelacement (ex Greedy2frame ...) ne sont disponibles que dans ce mode.

K!TV XP fait appel au plugin avec la ligne de code suivante :

PluginVideo->DoStuff(IsOdd, &UpdateBuffer, &ViewBuffer);

Le plugin travaille sur les pointeurs EvenLines ou OddLines, ces pointeurs sont du type short *EvenLines[5][288] et permettent d'accéder à chaque ligne individuellement. Le plugin doit utiliser CurrentFrame, indicateur du buffer courant, en voici un exemple:

EvenLines[CurrentFrame][i].

Le résultat du traitement doit être enregistré dans OutEvenLines ou OutOddLines.

Paramètres :

  • BOOL IsOdd :
    La trame capturée est paire (FALSE) ou impaire, le plugin doit donc utiliser les pointeurs correspondant (EvenLines ou OddLines).
  • BOOL UpdateBuffer :
    Le plugin doit modifier cette variable !
    Si TRUE, les lignes OutEvenLines/OutOddLines sont copiées dans le buffer vidéo.
    Si FALSE alors K!TV XP ne fait rien et passe à la trame suivante.
  • BOOL ViewBuffer :
    Le plugin doit modifier cette variable !
    Si TRUE le buffer vidéo est affiché à l'écran.
    Il est évident qu'en mode FF il faut attendre de traiter 2 trames avant de lancer l'affichage.

Exemple type de fonctionnement :

Mode HF (FullFields=false)
Capture de la trame impaire
Traitement de la trame impaire (UpdateBuffer=false et ViewBuffer=false)
Capture de la trame paire
Traitement de la trame (UpdateBuffer=true et ViewBuffer=true)
Copie vers le buffer vidéo de la trame paire

Le buffer est affiché

Mode FF (FullFields=true)

Capture de la trame paire
Traitement de la trame paire (UpdateBuffer=true et ViewBuffer=false)
Copie vers buffer vidéo
Capture de la trame impaire
Traitement de la trame impaire (UpdateBuffer=true et ViewBuffer=true)
Copie vers le buffer vidéo

Le buffer est affiché

Remarques :

Si l'utilisateur sélectionne le mode DIB, les pointeurs vers les lignes ne sont pas tous valides (la capture ne se fait pas en taille maximale de 576 lignes). Il faut donc dans tous les cas tester avant de travailler avec EvenLines ou OddLines.

if (EvenLines[CurrentFrame][i] != 0)
memcpy(dest, EvenLines[CurrentFrame][i], size);

6. La mémoire et EvenLines/OutEvenLines :

Analysons plus en détail la mémoire et la relation avec EvenLines et OutEvenLines ...

A son démarrage l'application alloue un important bloc de mémoire pour stocker 5 x 2 x 288 x 768 pixels, ce qui représente 10 trames ou 5 images completes. La carte télé se charge de transférer directement vers cette zone les trames capturées et K!TV XP retourne au plugin la derniere trame capturée (CurrentFrame).

Voici une représentation de cette mémoire :

Trame 0
Paire
Trame 0
Impaire
Trame 1
Paire
Trame 1
Impaire
Trame 2
Paire
Trame 2
Impaire
Trame 3
Paire
Trame 3
Impaire
Trame 4
Paire
Trame 4
Impaire

C'est pourquoi lorsque le plugin souhaite acceder ses images, nous devons utiliser le code suivant :
EvenLines[CurrentFrame][i].

EvenLines : est un pointeur vers les trames Paires
CurrentFrame : est un entier variant de 0 a 4 indiquant la derniere trame capturée.

Il en résulte donc que cette zone mémoire peut etre utilisé par le plugin pour ses traitements internes. Par exemple, si l'on veut améliorer l'image, il est inutile de copier les lignes vers une autre zone mémoire, nous pouvons directement travailler dessus.

La seule contrainte c'est que la carte télé continue de travailler en arriere plan, la même zone mémoire sera donc ecrasée au bout d'un cycle de 5 images. Ca nous laisse donc une durée de 150-200 ms pendant laquelle le buffer sera "intacte".

Et pour finir, OutEvenLines défini les lignes à afficher par K!TV XP. Dans le cas le plus simple nous avons :
OutEvenLines = EvenLines

7. Lancement automatique du plugin vidéo :

BOOL DetecteSignal(BYTE *VBI)
Si le plugin peut détecter un signal vidéo alors il doit préciser AutoProcessVideo=TRUE

25 fois par secondes K!TV XP fait appel à DetecteSignal() pour qu'il analyse le flux vidéo, le plugin a donc la main pour analyser le signal. Dès que le plugin souhaite se lancer DetecteSignal() doit retourner TRUE (sinon retourner FALSE) .

En plus de * VBI le plugin a toujours accès aux variables :

Even/OddLines, CurrentFrame, CurrentX

Paramètres:
BYTE *VBI : Pointeur vers les données VBI (si pas de données télétexte alors NULL)

8. Affichage de messages dans la barre d'état :

void GetStatus(char *Text, BOOL Nb)
Ces informations sont affichées par K!TV XP dans sa barre d'état.

Paramètres:
Nb : Indique l'emplacement où afficher dans la barre d'état. Si Nb=false, la chaîne modifiée par GetStatus est affichée sur la gauche de la barre d'état.

9. Interface utilisateur :

void ShowDialog() :
Demande au plugin d'afficher sa boite de dialogue.

HMENU GetPluginMenu(BOOL ContextMenu) :
Retourne un menu que K!TV XP va greffer à son propre menu.
Si ContextMenu=TRUE alors K!TV XP demande le menu contextuel (affiché lors d'un clic gauche sur l'image) .

Exemple :

HMENU GetPluginMenu(BOOL ContextMenu)
{


static HMENU CurMenu;
CurMenu = LoadMenu(VideoPlugin.hDllInstance, "PLUGIN_MENU");
return CurMenu;


}

Attention, le menu doit avoir un titre, c'est a dire que le fichier RC doit être comme :

PLUGIN_MENU MENU
BEGIN
POPUP "Popup" <<< important
BEGIN MENUITEM "&premier", IDM_4
MENUITEM "&second ", IDM_2
END
END

Finalement, le support multilingue étant désormais disponible avec K!TV XP, les plugins peuvent savoir la langue courante en regardant LangID avant d'afficher toute boite de dialogue ou message destiné à l'utilisateur.

Par exemple K!TV peut vous donner :

Lang = IDMAKELANGID (LANG_FRENCH, SUBLANG_DEFAULT)

ou pour l'anglais :

Lang = MAKELANGID (LANG_ENGLISH, SUBLANG_DEFAULT)

10. Messages destinés au plugin :

BOOL ProcessMessage(UINT message, UINT wParam, LONG lParam, char *OSDDisplay)

Tous les messages non traités par K!TV XP sont transmis d'abord au plugin vidéo puis au plugin audio..
La valeur de retour de ProcessMessage est important, si le plugin vidéo retourne TRUE, K!TV considère que OSDDisplay est valide est affichera ce message à l'écran. S'il retourne FALSE le plugin audio pourra traiter ce message.

Paramètres:

Il ne faut pas choisir les ID au hasard pour ne pas avoir de conflit avec ceux de K!TV XP. Il est donc fortement conseillé de respecter les règles suivantes :
> 40100 pour les plugins vidéo
> 40200 pour les plugins audio


Paramètres:
UINT message, UINT wParam, LONG lParam : Paramètres classiques ...
OSDDisplay : Texte à afficher par l'OSD de K!TV XP.

11. Raccourcis claviers :

ACCEL *accel
int NbAccel

Voici un exemple d'utilisation de raccourcis claviers :

ACCEL AccTable[] =
{

     
// fVirt key cmd Touche
0, 'f', IDM_1 }, // F
FVIRTKEY | FCONTROL, 'F', IDM_2 }, // Control-F
FVIRTKEY, VK_F12, IDM_3 }, // F12
};      

...

NbAccel = 3;

     

Paramètres :

  • fVirt :
    Utiliser 0 pour indiquer un raccourci clavier simple (ASCII).
    Utiliser FCONTROL ou FALT ou FSHIFT pour indiquer n'importe quelle combinaison de ces touches (combiner avec '|').
    Pour les touches spéciales (ex F1 ou espace) il faut ajouter FVIRTKEY (et avoir "key" = VK_F1 ou VK_SPACE).
  • key :
    Code ASCII (ex 'k') ou VirtualKey (ex VK_F5)
    ATTENTION Bug Windows : il semble que pour Control-F (ou tout autre touche) il faille avoir 'F' et non 'f'.
  • cmd :
    ID du message à envoyer.

Et vous pourrez recevoir par exemple le message CONTROL_F qui sera traité par ProcessMessage()
(comme ceci : ProcessMessage(WM_COMMAND, CONTROL_F, 0, *);)

12. Bandes noires :

int MovieBoost;

BOOL AutoMovieBoostFound()
Toutes les 5 secondes K!TV exécute AutoMovieBoostFound() , ceci permet au plugin de trouver les bandes noires pour les films (comme 16/9e par exemple), ces zones noires ne sont pas affichées par K!TV XP d'où un gain de temps.

Remarque :
AutoMovieBoostFound() doit être utilisé en combinaison de MovieBoost, ce dernier représente le nombre de lignes occupées par les bandes noires sur une seule trame.
Si la sortie de AutoMovieBoostFound est TRUE, K!TV XP s'attend à ce que MovieBoost soit valide immédiatement.

void SetLow(unsigned char bBrightness)
permet d'être à jour lorsque l'utilisateur modifie le contraste.

13. Localisation et path des plugins :

A partir de la version 1.2.0.0 de K!TV XP, les plugins doivent se situer dans le sous répertoire Plugins de K!TV XP. La recherche des plugins se fait uniquement dans ce répertoire. De plus pour ne pas que le répertoire Plugins ne devienne une forêt peu hospitalière il est demandé que soit utilisée la structure suivante:

Pour chaque plugin il ne doit y avoir dans le répertoire Plugins que:

  • La dll du plugin principale (celle qui est chargée par K!TV XP) (eg: PluginVideo.dll)
  • Un fichier de configuration du plugin portant le même nom que la dll principale du plugin (eg: PluginVideo.ini)
  • Un sous répertoire portant le même nom que la dll principale du plugin (eg: PluginVideo) regroupant tout fichier supplémentaire du plugin.

Il faut donc prendre soin lors de l'appel à des fichiers utilisés par les Plugins à ces quelques contraintes.

L'aide du plugin étant a placer dans le sous répertoire Aide de K!TV XP.