IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Entrée/Sortie Java Discussion :

Problème avec Process.getInputStream()


Sujet :

Entrée/Sortie Java

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre Expert
    Avatar de yotta
    Homme Profil pro
    Technicien maintenance
    Inscrit en
    Septembre 2006
    Messages
    1 095
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 55
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Technicien maintenance
    Secteur : Industrie

    Informations forums :
    Inscription : Septembre 2006
    Messages : 1 095
    Par défaut Problème avec Process.getInputStream()
    Bonjour,
    Après plusieurs jours de torture mentale, je me résous à venir vous casser un peu les pieds.
    J'évolue sous Windows 7 64bits, avec un environnement Java 1.0.8_60 64bits.
    Dans mon programme, je cherche à récupérer le flux d'un process représentant le lancement d'un programme exécutable externe.
    En l'occurrence, je dois utiliser mencoder.exe. Pour ceux qui ne connaissent pas, il s'agit d'un des éléments de MPlayer, player multimédia opensource bien connu.
    Je me sers de ce dernier pour produire une vidéo à partir d'un ensemble d'images Jpeg.
    Voilà pour le contexte.
    Et maintenant, le problème :
    Lorsque je lance mencoder.exe à la main, en ligne de commande avec tous ses paramètres pour la production de la vidéo, tout se passe à la perfection. Je remarque que ce petit programme est verbeux par la présence de nombreuses lignes dans ma fenêtre DOS. Bref, ça m'arrange car cette verbosité va me permettre d'éffectuer un suivi de la tâche de création vidéo en examinant le flux des messages de la console.
    Ce flux est récupérable en gros de deux manières, soit directement auprès d'une instance de la classe Process, par sa méthode getInputStream(), soit en utilisant ProcessBuilder afin de rediriger le flux vers notre programme.
    Seulement voilà, quelle que soit la solution "découte" adoptée, ça ne fonctionne pas correctement.
    Prenons un exemple avec 300 photos. Je demande à mencoder.exe d'en faire une vidéo mpeg4 en ligne de commande, pas de problème. Mais si j'exécute mencoder.exe avec les mêmes paramètres soit par Runtime.exec soit par ProcessBuilder.start, je récupère bien le flux de sortie console, et ce dernier contient bien la même chose que ce que je vois en ligne de commande, mais pour une raison qui me dépasse totalement, le flux s'interrompt après avoir en gros récupérer les 40 première lignes de verbosité ?!.... alors qu'avec 300 images, on obtient normalement un peu plus de 300 lignes.
    J'ai alors tenté de lancer mencoder différemment, en créant un batch qui lance mencoder. Et dans ce cas, mon code lance le batch, il ne lance plus directement mencoder.exe, mais là aussi, le flux s'interrompt, et toujours au même endroit. J'ai alors remplacé mon lot de 300 photos par 300 autres, même résultat, avec toujours une interruption de flux sans erreur après réception du même nombre de lignes de verbosité...
    Si quelqu'un a une idée ?
    Dés que je serai rentré à la maison, j'ajouterai quelques pièces jointes pour montrer la différence de verbosité obtenue dans les deux cas, et le code employé.
    Merci à vous...
    Une technologie n'est récalcitrante que par ce qu'on ne la connait et/ou comprend pas, rarement par ce qu'elle est mal faite.
    Et pour cesser de subir une technologie récalcitrante, n'hésitez surtout pas à visiter les Guides/Faq du site !

    Voici une liste non exhaustive des tutoriels qui me sont le plus familiers :
    Tout sur Java, du débutant au pro : https://java.developpez.com/cours/
    Tout sur les réseaux : https://reseau.developpez.com/cours/
    Tout sur les systèmes d'exploitation : https://systeme.developpez.com/cours/
    Tout sur le matériel : https://hardware.developpez.com/cours/

  2. #2
    Expert éminent
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Billets dans le blog
    1
    Par défaut
    Salut,

    Tu gères bien les autres flux ?

    Si tu n'utilises pas le flux d'entrée il est toujours préférable de le fermer au plus tôt.
    Pour le flux d'erreur, le plus simple est de le fusionner avec la sortie standard (via redirectErrorStream(true)) ou de le renvoyer vers la console (à partir de Java 7, via redirectError(Redirect.INHERIT))


    Sinon montre ton code de lecture...


    a++

  3. #3
    Membre Expert
    Avatar de yotta
    Homme Profil pro
    Technicien maintenance
    Inscrit en
    Septembre 2006
    Messages
    1 095
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 55
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Technicien maintenance
    Secteur : Industrie

    Informations forums :
    Inscription : Septembre 2006
    Messages : 1 095
    Par défaut
    Salut Adiguba,
    Merci pour ta réponse. Malheureusement, je n'ai rien dans le flux d'erreur. Pour en être sûr, j'ai créé deux threads distincts, un qui s'occupe d'InputStream et l'autre d'ErrorStream. Comme décrit dans mes explications, la verbosité s'arrête après quelques lignes, et il n'y a strictement rien qui tombe dans le flux d'erreur.
    Cependant, j'ai remarqué qu'en lançant mencoder.exe directement depuis mon code java, quelle que soit la méthode employée pour lui passer ses paramètres, ce dernier ne les prends jamais en compte. Comme il me génère bien une vidéo, je n'avais pas étudié en détail le contenu des lignes de verbosité, mais en fait, elles indiquent toutes à chaque image qu'aucune dimension n'a été fournie, aucun codec et quelques autres réglages. Par contre, il prend bien le premier paramètre qui lui indique où se trouvent les fichiers jpeg, et il capte bien le dernier paramètre qui définit le nom du fichier de sortie .avi ???

    Donc, comme en contre-partie ça fonctionne très bien en lançant mencoder.exe depuis un batch, j'ai finalisé mon batch de lancement comme suit :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    Contenu de mencoder.bat :
    .\mencoder mf://*.jpg -mf w=%1:h=%2:fps=%3:type=jpg -ovc lavc -lavcopts vcodec=%4 -oac copy -o %5
    
    %1 : largeur de l'image en pixel, %2 hauteur, %3 débit en nombre d'images par seconde, %4 codec vidéo, et %5 pour le nom de fichier de sortie.
    
    Exemple d'utilisation de mencoder.bat :
    
    mencoder.bat 1024 768 25 mpeg4 test.Avi
    Donc, lorsque je lance mencoder.bat depuis mon code java avec les paramètres indiqués ci-dessus, mencoder se lance et interprète bien tous les paramètres. Mais toujours pareil, sa verbosité s'arrête au même endroit....
    J'ai aussi créé un deuxième fichier bat un peu différent :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    Contenu de mencoder_rev2.bat :
     
    CMD /C "mencoder.exe mf://*.jpg -mf w=%1:h=%2:fps=%3:type=jpg -ovc lavc -lavcopts vcodec=%4 -oac copy -o %5"
    J'obtiens exactement le même résultat.....
    Pour illustrer tout ça, j'ai ajouté deux pièces jointes à cette discussion, Pièce jointe 187780 qui correspond à la sortie verbeuse obtenue lors d'un lancement manuel en ligne de commande de mon batch "mencoder.bat", et Pièce jointe 187781 qui correspond à la verbosité obtenue depuis mon code Java "recupMessages.class" dans tous les tests réalisés.

    Et ci-dessous, le code java utilisé pour lire cette verbosité dans ses deux déclinaisons, Runtime.exec et ProcessBuilder.start :

    Version utilisant la méthode exec de la classe Runtime :

    Code du Thread lanceur :

    Depuis la fenêtre graphique de mon programme, un clique sur un bouton "Créer séquence vidéo" réalise ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
        private void Cmd_CreerSequenceActionPerformed(java.awt.event.ActionEvent evt) {                                                  
            System.out.println("Lancement de la création de la vidéo...");
            ecouteurMEncoder ecoute = new ecouteurMEncoder(cheminSortiePhotos);
            ecoute.ajouteEcouteur(this);
            ecoute.start();
        }                                                 
     
    Cette fenêtre implémente une interface à moi qui lui permet de récupérer les informations concernant le travail de "ecoute". D'où "ecoute.ajouteEcouteur(this);". Cette interface définie trois méthodes dont voici l'implémentation :
     
        @Override
        public void debutEncodage(String msg) {
            System.out.println("Début: " + msg);
            }
     
        @Override
        public void finEncodage(String msg) {
            System.out.println("Fin: " + msg);
            }
     
        @Override
        public void erreurEncodage(String msg) {
            System.out.println("ERREUR !\n" + msg);
            }
    Voilà le code de la classe ecouteurMEncoder :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
     
    package classes;
     
    import interfaces.Ecouteur;
    import interfaces.EcouteurEncodage;
    import java.io.BufferedReader;
    import java.io.File;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.util.ArrayList;
    import java.util.logging.Level;
    import java.util.logging.Logger;
     
    public class ecouteurMEncoder extends Thread implements Ecouteur {
     
        final ArrayList<EcouteurEncodage> ecouteurs = new ArrayList();
        final String chemin;
        final int DEBUT = 1;
        final int FIN = 2;
        final int ERREUR = 3;
     
        private volatile Thread moniteur;
     
        public ecouteurMEncoder(String chemin) {
            // Ici, le chemin correspond au chemin pleinnement qualifié d'accès au dossier contenant les images, et mencoder.exe
            this.chemin = chemin; 
            setDaemon(true);
            }
     
        @Override
        public void start() {
            moniteur = new Thread(this);
            moniteur.start();
            }
     
        public boolean arret() {
            boolean reponse;
            moniteur = null;
            reponse = (moniteur == null);
            return reponse;
            }
     
        @Override
        @SuppressWarnings("SleepWhileInLoop")
        public void run() {
            envoiMessage("Thread ecouteur d'encodage démarré...", DEBUT);
            String[] commande = {chemin + File.separator + "mencoder.bat", "1024", "768", "25", "mpeg4", "testAvecRuntime.avi"};
            //String[] commande = {chemin + File.separator + "mencoder_rev2.bat", "1024", "768", "25", "mpeg4", "testAvecRuntime.avi"};
            Process mencoder;
            BufferedReader sortieConsole;
            try {
                // Il n'y a pas à ajouter de directives environnementales, d'où le null
                mencoder = Runtime.getRuntime().exec(commande, null, new File(chemin));
                // Récupération du flux de verbosité de mon shell natif dans lequel s'exécute "commande"
                sortieConsole = new BufferedReader(new InputStreamReader(mencoder.getInputStream()));
                // Instanciation et initialisation de mon Thread d'écoute de flux
                recupMessages recuperateur = new recupMessages(sortieConsole);
                // Ajout de l'écoute d'informations issues de recuperateur
                recuperateur.ajouteEcouteur(this);
                // Attente de la fin du processus externe...
                mencoder.waitFor();
                }
            catch (IOException | InterruptedException ex) {
                Logger.getLogger(ecouteurMEncoder.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
     
        public void ajouteEcouteur(EcouteurEncodage ecouteur) {
            if (!ecouteurs.contains(ecouteur)) ecouteurs.add(ecouteur);
            }
     
        public void retireEcouteur(EcouteurEncodage ecouteur) {
            if (ecouteurs.contains(ecouteur)) ecouteurs.remove(ecouteur);
            }
     
        private void envoiMessage(String msg, int type) {
            switch (type) {
                case DEBUT:
                    ecouteurs.stream().forEach((ect) -> {
                        ect.debutEncodage(msg);
                        });
                    break;
     
                case FIN:
                    ecouteurs.stream().forEach((ect) -> {
                        ect.finEncodage(msg);
                        });
                    break;
     
                case ERREUR:
                    ecouteurs.stream().forEach((ect) -> {
                        ect.erreurEncodage(msg);
                        });
                    break;
                }
            }
     
        @Override
        public void declenchement(String message) {
            envoiMessage(message, DEBUT);
            }
        }
    Code du Thread de récupération de la sortie verbeuse : recupeMessages.class

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
     
    package classes;
     
    import interfaces.Ecouteur;
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.logging.Level;
    import java.util.logging.Logger;
     
    public class recupMessages implements Runnable {
     
        private final BufferedReader lecteur;
        private final Thread t;
        final ArrayList<Ecouteur> ecouteurs = new ArrayList();
     
        public recupMessages(BufferedReader lecteur) {
            this.lecteur = lecteur;
            t = new Thread(this);
            init();
            }
     
        private void init() {
            t.start();
            }
     
        public void ajouteEcouteur(Ecouteur ecouteur) {
            if (!ecouteurs.contains(ecouteur)) ecouteurs.add(ecouteur);
            }
     
        public void retireEcouteur(Ecouteur ecouteur) {
            if (ecouteurs.contains(ecouteur)) ecouteurs.remove(ecouteur);
            }
     
        @Override
        @SuppressWarnings("SleepWhileInLoop")
        public void run() {
            Thread comp = Thread.currentThread();
            while (comp == t) {
                try {
                    while (lecteur.ready()) envoiMessage(lecteur.readLine());
                    }
                catch (IOException ex) {
                    Logger.getLogger(recupMessages.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
            }
     
        private void envoiMessage(String message) {
            ecouteurs.stream().forEach((ect) -> {
                ect.declenchement(message);
                });
            }
    }
    Version utilisant la méthode start de la classe ProcessBuilder :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    Seule la classe ecouteurMEncoder change. Plus précisément, sa méthode run, le reste ne change pas. Donc, voici uniquement le code de sa méthode run.
     
        @Override
        @SuppressWarnings("SleepWhileInLoop")
        public void run() {
            envoiMessage("Thread ecouteur d'encodage démarré...", DEBUT);
            String[] commande = {chemin + File.separator + "mencoder.bat", "1024", "768", "25", "mpeg4", "testAvecRuntime.avi"};
            //String[] commande = {chemin + File.separator + "mencoder_rev2.bat", "1024", "768", "25", "mpeg4", "testAvecRuntime.avi"};
            Process mencoder;
            BufferedReader sortieConsole;
            try {
                List<String> ordre = Arrays.asList(chemin + File.separator + "mencoder.bat", "1024", "768", "25", "mpeg4", "test.avi");// lanceur.sh lance l'application Blender en lui passant ces paramètres plus d'autres.
                ProcessBuilder generateurInstanceMEncoder = new ProcessBuilder(ordre);
                generateurInstanceMEncoder.directory(new File(chemin + File.separator));
                generateurInstanceMEncoder.redirectOutput(ProcessBuilder.Redirect.PIPE);
                Process encodage = generateurInstanceMEncoder.start();
                sortieConsole = new BufferedReader(new InputStreamReader(encodage.getInputStream()));
                recupMessages recuperateur = new recupMessages(sortieConsole);
                recuperateur.ajouteEcouteur(this);
                encodage.waitFor();
                }
            catch (IOException | InterruptedException ex) {
                Logger.getLogger(ecouteurMEncoder.class.getName()).log(Level.SEVERE, null, ex);
                }
            }

    Voilà,
    tu as tout ce qu'il faut pour reproduire mon problème. il te faut juste mencoder.exe, qui fonctionne de manière totalement autonome, téléchargeable ici, et un lot de fichiers jpeg, peu importe le nombre, mais plus d'une dizaine sinon on risque d'atteindre la fin du traitement avant le blocage du flux. La nature des photos est sans importance, 300x la même fera l'affaire. Il faut juste que ce soient des Jpeg de même résolution, et que leur nom se termine par une indentation, exemple : test_image1.jpg, test_image2.jpg, etc...
    Une technologie n'est récalcitrante que par ce qu'on ne la connait et/ou comprend pas, rarement par ce qu'elle est mal faite.
    Et pour cesser de subir une technologie récalcitrante, n'hésitez surtout pas à visiter les Guides/Faq du site !

    Voici une liste non exhaustive des tutoriels qui me sont le plus familiers :
    Tout sur Java, du débutant au pro : https://java.developpez.com/cours/
    Tout sur les réseaux : https://reseau.developpez.com/cours/
    Tout sur les systèmes d'exploitation : https://systeme.developpez.com/cours/
    Tout sur le matériel : https://hardware.developpez.com/cours/

  4. #4
    Expert éminent
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Billets dans le blog
    1
    Par défaut
    Tu ne traites pas le flux d'erreur.
    Si mencoder écrit sur le flux d'erreur c'est "normal" que cela se bloque à un moment où à un autre.

    Si tu ne les traites pas, rajoutes ceci avant de démarrer ton process :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    	generateurInstanceMEncoder.redirectError(ProcessBuilder.Redirect.INHERIT);
    Cela va recopier automatiquement sur le flux d'erreur de ton process...

    Idem pour le flux d'entrée, même s'il serait plutôt préférable de le fermer immédiatement après la création du process :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    encodage.getOutputStream().close();

    Au passage si tu ne traites qu'un seul flux inutile de créer un nouveau thread : tu peux bien le faire dans le thread courant, et donc directement après la création de ton process.


    Enfin utilises le try-with-ressource pour la libération des ressources.


    a++

  5. #5
    Membre Expert
    Avatar de yotta
    Homme Profil pro
    Technicien maintenance
    Inscrit en
    Septembre 2006
    Messages
    1 095
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 55
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Technicien maintenance
    Secteur : Industrie

    Informations forums :
    Inscription : Septembre 2006
    Messages : 1 095
    Par défaut
    Un grand merci adiGuba !
    J'ai suivi tes conseils et commencé par fermer le flux de sortie de mon process tout de suite après création. Puis, j'ai aussi pratiqué la redirection du flux d'erreur. Et maintenant, tout fonctionne à merveille. Je capture intégralement la verbosité de mencoder, je vais pouvoir faire ma jauge et passer à la suite. Je vais bien sûr prendre le temps d'y regarder de plus près parce que je n'ai pas encore tout compris, notamment pour ce qui concerne ton dernier conseil avec "try-with-ressource pour la libération des ressources"...
    Pardonnes-moi mais je suis purement autodidacte, lu comme ça, ça ne me parle pas. Mais je vais gratter, si je n'y arrive pas, je me permettrai peut-être de te solliciter.
    Encore merci !
    Une technologie n'est récalcitrante que par ce qu'on ne la connait et/ou comprend pas, rarement par ce qu'elle est mal faite.
    Et pour cesser de subir une technologie récalcitrante, n'hésitez surtout pas à visiter les Guides/Faq du site !

    Voici une liste non exhaustive des tutoriels qui me sont le plus familiers :
    Tout sur Java, du débutant au pro : https://java.developpez.com/cours/
    Tout sur les réseaux : https://reseau.developpez.com/cours/
    Tout sur les systèmes d'exploitation : https://systeme.developpez.com/cours/
    Tout sur le matériel : https://hardware.developpez.com/cours/

  6. #6
    Modérateur

    Profil pro
    Inscrit en
    Septembre 2004
    Messages
    12 582
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2004
    Messages : 12 582
    Par défaut
    Hello,

    pendant qu'on y est sur les éléments importants. Cette ligne :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    while (lecteur.ready()) envoiMessage(lecteur.readLine());
    Ça veut dire qu'on arrête le traitement si par hasard lecteur.ready() devient faux.
    Ouais non, c'est probablement pas ce que tu veux. J'imagine que tu n'as pas l'intention de t'arrêter avant la fin, donc non, ready() ne fait pas ce que tu crois.

    En fait, Reader.ready() et InputStream.ready() ne servent pas à grand-chose, et quand ils sont utilisés, c'est en général parce que le programmeur ne comprenait pas et qu'il n'aurait pas dû faire ça.
    Il vaut mieux faire comme si ready() n'existait pas et ne jamais s'en servir. Il sera toujours temps d'y revenir quand tu comprendras absolument tout du fonctionnement des IO et que tu te diras que tu es dans un cas exceptionnel qui a besoin de ça.

    La boucle devrait s'arrêter quand il n'y a plus de ligne à lire. Ça ce sera très bien.
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Problème avec Process.getInputStream()
    Par yotta dans le forum Général Java
    Réponses: 5
    Dernier message: 23/02/2015, 18h43
  2. Problème avec Process.exec() (Ligne de commande complexe)
    Par nanath02 dans le forum Débuter avec Java
    Réponses: 3
    Dernier message: 10/09/2014, 16h48
  3. Problème avec "Process.GetProcessesByName"
    Par jacko842 dans le forum Windows Forms
    Réponses: 14
    Dernier message: 21/12/2011, 19h09
  4. petit Problème avec (Process.Start)
    Par kazylax dans le forum C#
    Réponses: 2
    Dernier message: 01/10/2011, 15h21
  5. Réponses: 2
    Dernier message: 11/02/2009, 23h30

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo