De nos jours, les applications multimédia sont de plus en plus présentes et utiles dans notre quotidien. Ce phénomène touche aussi bien les professionnels de l'informatique que les ménages. En effet, le multimédia englobe aussi bien les technologies de l'image et du son (le DVD qui connaît actuellement un énorme succès en est une bonne illustration) que la recherche de convivialité dans le domaine de l'Internet. C'est parce que ce dernier domaine est en pleine évolution que nous nous y sommes intéressés.
C'est pourquoi nous avons décidé de développer en Java un logiciel permettant le transfert de données multimédia par réseau informatique (et donc bien sûr par Internet ) , cela dans l'idée de faciliter le travail d'équipe à distance. Dans cette optique, nous avons développé Communicator2000®. Ce logiciel est composé d'un "talk" permettant de communiquer en direct de façon écrite, d'un système de transfert direct de fichiers d'un ordinateur à l'autre, et enfin d'outils permettant l'envoi et la réception de média en temps réel. Nous avons volontairement décidé de limiter l'utilisation de notre logiciel à des connexions entre deux personnes (mais une extension est toujours possible ! ).
Dans la suite de notre présentation nous allons nous efforcer, tout en développant les points précédents, d'y intégrer les différentes étapes par lesquelles nous sommes passés.
Le "talk", même s'il n'est pas et de loin, la partie la plus complexe de notre projet, aura eu pour principal intérêt la manipulation des sockets. C'est à dire qu'il nous aura appris à nous familiariser avec le concept général des sockets (client, serveur, ...) ainsi qu'avec la façon dont ceux-ci sont implémentés sous java.

Figure 1 : Schéma de principe du Talk
L'interface graphique du Talk est élaboré de la façon suivante : l'utilisateur peut lire dans le panel PanelLui ce que son correspondant lui écrit, et peut lui répondre en tapant sont texte dans le panel PanelMoi.
Comme le montre le schéma de la figure 1, le principe du Talk est assez simple : un thread SocketTalk s'occupe de lire les char arrivant via le flux associé au socket qui nous lie à notre correspondant. Il envoie ensuite l'ordre ( recoit(char d) ) à PanelTalk de dispatcher ( recoit(char d) ) ce char à PanelLui. Ensuite, lorsque PanelMoi détecte que l'on tape une touche, il envoie ( envoie(char c) ) le char correspondant à PanelTalk qui s'occupe de l'écrire dans le socket (writeChar(c)).
La gestion des déconnexions de l'un ou l'autre des protagonistes se traite facilement à l'aide des exceptions déclenchées par les sockets dans ces cas la. L'exception est déclenchée lorsque l'on essaie de lire ou d'écrire à travers un socket fermé par une déconnexion. Afin d'informer l'utilisateur de la déconnexion du Talk de son partenaire, nous avons choisis de faire disparaître les objets graphiques associés à ce Talk.

Lors de tout travail informatique la manipulation de fichiers est incontournable. C'est pourquoi l'échange de fichiers par le réseau est la principale fonctionnalité utilisée. Pour être complète, notre application devait la posséder. Par sécurité, et pour notre culture personnelle, nous avons décidé de gérer nous même ce transfert en implémentant notre propre protocole. De plus, pour rendre l'application plus conviviale, nous avons choisi d'utiliser, pour cela, une manipulation graphique par le biais de "Drag and Drop" et un système de file d'attente pour l'envoi de plusieurs fichiers.
Pour transférer les fichiers, nous avons mis au point une structure de trame bien spécifique (Cf. Figure 2). Au dessus de la couche de transport physique et système, nous avons implémenté, au niveau logiciel, un protocole de transfert de trames par acquittement.

Figure 2 : Détail des trames envoyées
A l'intérieur de la trame, on peut noter les significations des champs suivants :
type de trame : il y a 4 types de trames codés par un byte
-1 : correspond à une annulation, le correspondant ne veut pas du fichier qu'on lui envoie.
1 : demande d'écriture, le correspondant veut envoyer un fichier. Dans ce cas la, les bytes qui suivent contiennent le nom du fichier.
2 : Trame de donnée : contient une partie du fichier à recevoir.
3 : Trame d'acquittement : le correspondant nous confirme la bonne réception d'une trame.
Numéro Trame : sert à identifier chacune des trames pour l'acquittement.
Tableau 512 bytes : contient les données du fichier à transmettre, sa taille maximale est de 512 octets.
Octets Utils : déclare, à celui qui reçoit, le nombre d'octets qu'il doit conserver sur les 512 du tableau. En effet, la taille d'un fichier n'est pas forcément un multiple de 512. Il faut envisager différents cas. Pour un fichier dont la taille n'est pas multiple de 512, " Octets Utils " est inférieur à 512 pour la dernière trame, ce qui permet au logiciel de détecter la fin d'arrivée du fichier. Dans le cas particulier où la taille du fichier est exactement un multiple de 512, la fin du fichier est signalée par l'envoi d'une trame dont " Octets Utils " vaut 0.
Le programme d'envoi de fichier s'appuie sur la classe FichierSocket , thread s'occupant de l'envoi et de la réception de fichiers selon le protocole défini précédemment. La Figure 3 en décrit le fonctionnement.

Figure 3 : Fonctionnement du thread FichierSocket
En ce qui concerne la rapidité du transfert, d'un Pentium II (400 Mhz) à un pentium (100Mhz) nous obtenons un taux de transfert d'environ 77 Ko et cela avec un réseau local.
Par défaut, java utilise des sockets TCP, ce qui ne sert pas à grand chose vu que la vérification de bonne arrivée des trames est assurée par notre programme. C'est pourquoi, une amélioration possible de notre logiciel serait de forcer java à utiliser dans ce cas là des sockets UDP (protocole non sécurisé au niveau de la vérification de l'arrivée des trames) rendant ainsi la transmission plus rapide.
Une autre amélioration consisterait à rajouter un système de visualisation graphique de la file d'attente d'envoi des fichiers, chose que nous n'avons pas pu implémenter par manque de temps.
Le Drag and Drop est utilisé dans la majorité des logiciels graphiques qui se veulent intuitifs. C'est la raison pour laquelle nous nous sommes intéressés à sa gestion en java.

Figure 4 : Schéma de principe du Drag and Drop
Notre gestion du Drag and Drop se déroule de la façon suivante : tout objet graphique que l'on désire déplacer et rendre reconnaissable par une cible (destination du déplacement) doit être un objet implémentant l'interface Transferable. Dans notre situation, cet objet doit contenir l'information sur le fichier à transférer. Notre classe NoeudTransferable, en implémentant l'interface Transferable obéit ainsi au critère précédent.
Les instances de cette classe sont utilisées par une Jlist qui en fait le rendu et qui détecte une intention de Drag sur ces dernières (à l'aide d'un DragGestureListener ).
Notre choix graphique pour représenter la cible (lieu où l'on "drag" pour débuter l'envoi du fichier vers notre correspondant) a été d'utiliser un Panel noir dont les bordures changent de couleur dès que ce dernier détecte une intention de "drop" (à l'aide d'un DropTargetListener).
La transmission en temps réel nécessite l'utilisation du protocole RTP pour pallier aux problèmes de vitesse inhérents à toute utilisation d'un réseau. En effet, une trame est transmise sans contrôle de bonne réception : c'est le protocole UDP. Le protocole RTP est basé sur une couche UDP et sur l'utilisation, pour chaque stream transmis, de deux sockets : l'une pour la transmission proprement dite et l'autre pour la transmission des informations de contrôle de la session RTP.
JMF est une API (application programming interface) permettant aux programmeurs de développer des applications en java capables de présenter et de traiter des média (c'est à dire des vidéos et du son). JMF offre de plus la possibilité de récupérer des données provenant de périphériques de capture multimédia. Nous allons vous présenter les outils de JMF que nous avons utilisés.
Cette interface permet de présenter et de contrôler une donnée multimédia. Un Player se crée à partir d'une source de donnée. Cette source de données peut être un fichier ou une autre source de données (comme un flux provenant du réseau). Pour être opérationnel, le Player doit passer par différents états (Cf.Figure 5).

Figure 5 : Le graphe d'état du Player
Le passage d'un état à l'autre est détectable à l'aide d'un écouteur d'événement.
Une fois le Player instancié, on le fait passer dans l'état Realized qui lui permet d'accéder au média ainsi que d'identifier les ressources qui lui seront nécessaires a sa présentation. Cet état nous permet de récupérer les objets graphiques permettant la visualisation et le contrôle du média. La méthode start() amène ensuite le Player dans l'état Started état où il peut, enfin, présenter le média.
Le Processor est un Player qui possède des méthodes supplémentaires lui permettant de modifier et/ou traiter le média (par exemple en changeant son format ou en le sauvant sur le disque). Cela est possible lorsque le Processor se trouve dans l'état Configured (Cf. Figure 6).

Figure 6 : Le graphe d'état du Processor
On peut par exemple séparer l'image et le son d'un média (séparation des différents " tracks " du media). (Cf. Figure 7)

Figure 7 : Traitement des tracks par le Processor.
Le SessionManager gère la session RTP . Il se charge, de connecter entre eux les différents participants de la session, et d'écouter les départs et arrivées de streams.
Pour respecter la logique de notre logiciel, nous avons décidé de restreindre la session RTP à une communication entre deux personnes. Une fois la connexion assurée, nous avons développé deux classes l'une se chargeant de l'envoi (classe EnvoieFlux) et l'autre de la réception (classe LecteurEcouteur).
L'ensemble des classes créées pour cette partie se trouve regroupé dans le fichier PartieVideo.java.
Transmission
On récupère tout d'abord l'origine du media à envoyer avec un MediaLocator construit à partir d'un fichier multimédia ou d'un périphérique de capture (par exemple une WebCam).
A partir de ce MediaLocator on crée un Processor. Avec ce Processor on sépare les différents tracks du stream et on choisit celui à transmettre (Audio ou Video). Pour l'instant, nous nous contentons d'envoyer la vidéo. Ce track vidéo est convertit en stream vidéo compatible RTP. On récupère ensuite la sortie du Processor que l'on envoie au SessionManager qui se charge de la transmettre à notre correspondant.
Réception
Dès que notre SessionManager détecte l'arrivée d'un nouveau stream, il crée ,à partir de ce stream, un Player géré par notre classe LecteurEcouteur qui détecte le passage d'un état à un autre du Player et qui affiche le résultat dans notre application (dès que celui-ci est dans l'état started).
Lorsqu'un utilisateur décidait d'arrêter l'envoi ou la réception d'un média, nous avions des problèmes pour assurer la libération des ressources prises (mémoire, réseau, stream ouverts ...) afin de permettre à l'utilisateur de réaliser autant d'opérations successives qu'il le désire.
De plus pour une plus grande robustesse et un plus grand contrôle du logiciel, nous avons pensé qu'il serait intéressant d'apprendre à maîtriser le socket de contrôle de la session RTP afin d'assurer une meilleure communication (message de fin d'envoi, ...) entre les deux participants. D'autre part, étant donné que chaque ordinateur n'a pas les mêmes codec installés, il serait intéressant de permettre à l'utilisateur de choisir celui qu'il veut utiliser.

Notre logiciel est loin d'être totalement abouti. La plupart des objectifs que nous nous étions fixés ont cependant été atteints, ce qui pour une première véritable utilisation du langage java est fort satisfaisant. Et cela en 25 heures de 480 minutes chacune, conformément au programme scolaire fixé !!
Afin que Communicator2000® soit effectivement opérationnel, il reste malgré tout certaines retouches portant principalement sur la recherche d'une meilleure convivialité ainsi que d'une utilisation plus optimale des ressources multimédia
Pour tout renseignement, veuillez nous contacter.