jmf rtp

11
Résumé Cet exposé aborde le protocole RTP de la bibliothèque JMF de Java. Il montre comment accéder à un fichier son ou vidéo, à une webcam ou un microphone, et comment envoyer le flux obtenu à travers le réseau vers un autre ordinateur. Il est composé de nombreux codes sources commentés et explique les bases de la visio- conférence en Java. Sommaire Introduction 1. Transmission du RTP : Configuration de la source 1.1. Source Fichier 1.2. Source Capture vidéo/audio 2. Transmission du RTP : Les différentes étapes de l’état du Processor 2.1. La Configuration 2.2. Mettre les différentes pistes du flux dans un format supporté 2.3. La Réalisation 3 2.4. Le Démarrage du Processor 3. Transmission du RTP : Finalisation 3.1. Base 3.1.1. Emploi du DataSink 3.1.2. Emploi du RTPManager 3.2. Envoi d'une source de flux unique vers plusieurs destinations 3.2.1. Le clonage du DataSource 3.2.2. Emploi du RTPManager 4. Réception du RTP 4.1. A l'aide d'un MediaLocator 4.2. Emploi du RTPManager 4.2.1. Création du RTPManager 4.2.2. Gestion de l’évènement ReceiveStreamEvent Conclusion Introduction Présentation De nos jours, les applications utilisant des transmissions de flux en temps réels sont de plus en plus utilisées sur Internet. Que ce soit pour des visioconférences, à deux ou à plusieurs, des radios ou pourquoi pas des programmes de vidéo surveillance. Il existe différents protocoles pour pouvoir utiliser des transmissions de flux vidéo ou audio en temps réels : le protocole RTP est le protocole en java, à l’aide de la bibliothèque JMF, pour pouvoir le faire.

Upload: prince-king

Post on 27-Jun-2015

608 views

Category:

Documents


2 download

TRANSCRIPT

Page 1: jmf rtp

Résumé Cet exposé aborde le protocole RTP de la bibliothèque JMF de Java.

Il montre comment accéder à un fichier son ou vidéo, à une webcam ou un microphone, et comment envoyer le flux obtenu à travers le réseau vers un autre ordinateur.

Il est composé de nombreux codes sources commentés et explique les bases de la visio-conférence en Java.

Sommaire Introduction 1. Transmission du RTP : Configuration de la source 1.1. Source Fichier 1.2. Source Capture vidéo/audio 2. Transmission du RTP : Les différentes étapes de l’état du Processor 2.1. La Configuration 2.2. Mettre les différentes pistes du flux dans un format supporté 2.3. La Réalisation 3 2.4. Le Démarrage du Processor 3. Transmission du RTP : Finalisation 3.1. Base 3.1.1. Emploi du DataSink 3.1.2. Emploi du RTPManager 3.2. Envoi d'une source de flux unique vers plusieurs destinations 3.2.1. Le clonage du DataSource 3.2.2. Emploi du RTPManager 4. Réception du RTP 4.1. A l'aide d'un MediaLocator 4.2. Emploi du RTPManager 4.2.1. Création du RTPManager 4.2.2. Gestion de l’évènement ReceiveStreamEvent Conclusion

Introduction Présentation

De nos jours, les applications utilisant des transmissions de flux en temps réels sont de plus en plus utilisées sur Internet. Que ce soit pour des visioconférences, à deux ou à plusieurs, des radios ou pourquoi pas des programmes de vidéo surveillance.

Il existe différents protocoles pour pouvoir utiliser des transmissions de flux vidéo ou audio en temps réels : le protocole RTP est le protocole en java, à l’aide de la bibliothèque JMF, pour pouvoir le faire.

Page 2: jmf rtp

Cet exposé montre ainsi les bases de l’utilisation du protocole RTP.

Imports Requis

Pour utiliser les différentes possibilités du protocole RTP, il faudra faire les imports suivants :

import javax.media.*; import javax.media.rtp.*; import javax.media.rtp.event.*; import javax.media.format.*; import javax.media.control.*; import javax.media.protocol.*; import javax.media.rtp.rtcp.*; import java.io.*; import java.util.*; import java.awt.*; import java.net.*; import javax.swing.*;

1. Transmission du RTP : Configuration de la source 1.1. Source Fichier

On peut souhaiter avoir une source Fichier dans le cas de radio ou de Télévision sur Internet. Dans ce cas, il faut créer un MédiaLocator sur le fichier, puis créer un Processor sur ce MédiaLocator.

//Entrée de l'adresse du fichier String FichierAdresse = "file://d:/test.avi" ; //Création du MédiaLocator à partir de ce fichier MediaLocator FichierLocator = new MediaLocator(FichierAdresse); //Declaration du Processor Processor FichierCessor = null; try { //Creation du Processor à partir du Medialocator FichierCessor = Manager.createProcessor(Fich ierLocator); } catch(IOException e) { System.out.println( "Erreur : " +e.getMessage()); } catch(NoProcessorException e) { System.out.println( "Erreur : " +e.getMessage()); }

1.2. Source Capture vidéo/audio Néanmoins, On peut vouloir utiliser une Webcam et un Micro et envoyer le flux audio et vidéo sur le réseau. Il faut alors sélectionner le périphérique de capture à l aide du CaptureDeviceManager. Sa méthode getDeviceList() appelé avec null en argument, celui renvoie la liste complète des périphériques de capture. Une fois, le bon périphérique sélectionné, on crée un Processor à partir de celui-ci. //Declaration du CaptureDeviceInfo de la Webcam CaptureDeviceInfo Webcam = null;

Page 3: jmf rtp

//Declaration du CaptureDeviceInfo du Micro CaptureDeviceInfo Micro = null; //Recherche des CaptureDevices Vector deviceList = CaptureDeviceManager.getDeviceL ist( new AudioFormat( "linear" ,8000,8,1)); Vector deviceList2 = CaptureDeviceManager.getDevice List( new VideoFormat(VideoFormat.YUV)); //Creation des CaptureDeviceInfo pour le Micro et l a Webcam Micro = (CaptureDeviceInfo)deviceList.get(0); Webcam = (CaptureDeviceInfo)deviceList2.get(0); //Declaration des Processors du Micro et de la Webc am Processor MicroCessor = null; Processor WebcamCessor = null; try { //Creation du Processor du Micro et de la Webcam MicroCessor = Manager.createProcessor(Micro. getLocator()); WebcamCessor = Manager.createProcessor(Webca m.getLocator()); } catch(IOException e) { System.out.println( "Erreur : " +e.getMessage()); } catch(NoProcessorException e) { System.out.println( "Erreur : " +e.getMessage()); }

2. Transmission du RTP : Les différentes étapes de l’état du Processor Pour permettre de transmettre un flux, et après qu’un Processor soit créé, il passe par différentes étapes : - La configuration, qui démultiplexe le flux d’entrée et obtient des informations sur le format des données d’entrée. - Le bon formatage des pistes du Processor - La réalisation du processor, qui est le stade abouti du Processor. - Le démarrage du processor.

2.1. La Configuration

Le moyen, pour configurer un Processor, est simple : il suffit d’appeler la méthode configure() du Processor. Néanmoins, cette méthode n’est pas bloquante et n’attend donc pas que le Processor soit configuré pour continuer, c’est à dire que si dans la suite du programme, d’autres méthodes font appel à un Processor configuré, on obtiendra une erreur. Il faut donc utiliser la méthode getState() qui renvoie un nombre « croissant » correspondant à l’état actuel de notre Processor pour savoir quand celui ci est configuré.

public Processor configure(Processor p) { //Attendre tant que le Processor n'est pas configur é. while(p.getState() < Processor.Configured) { //Configuration du Processor p.configure(); } return p; }

Page 4: jmf rtp

2.2. Mettre les différentes pistes du flux dans un format supporté

On doit mettre chaque piste du Processor dans un format supporté sinon, le programme ne pourra pas transmettre le flux et générera une erreur. Pour cela, on utilise la méthode getSupportedFormats() de la classe TrackControl, pour connaître les différents formats supportés par chaque piste. Le code source suivant est simple, mais au lieu de :

track[i].setFormat(supportedFormats[0]);

on pourrait mettre :

track[i].setFormat(new VideoFormat(VideoFormat.H263_RTP, new Dimension(160,120), Format.NOT_SPECIFIED, Format.buteArray, Format.NOT_SPECIFIED);

pour, par exemple, mettre le flux de sortie en résolution 160 par 120 (si c’est un flux vidéo bien entendu).

public void SetSupportedFormat(Processor p) { //On met la description du contenu de sortie à RAW_ RTP // pour limiter les formats supportés ContentDescriptor cd = new ContentDescriptor( Con tentDescriptor.RAW_RTP); p.setContentDescriptor(cd); //On obtient les différentes pistes du processor TrackControl track[] = p.getTrackControls(); for( int i = 0 ; i < track.length ; i++) { //on obtient les formats supportés pour cette piste Format suppFormats[] = track[i].getSu pportedFormats(); //Si il y a au moins un format supporté // alors on met le premier if(suppFormats.length > 0) { track[i].setFormat(suppFormats [0]); } else { track[i].setEnabled( false); } } }

2.3. La Réalisation

De-même que pour la configuration, il est simple de réaliser un processor, mais on doit là aussi attendre qu’il passe au statut « réalisé » par nous même, car la méthode realize(), n’est pas bloquante.

public Processor realize(Processor p) { //Attendre tant que le Processor n'est pas réalisé. while(p.getState() < Processor.Realized) { //Réalisation du Processor p.realize(); } return p; }

Page 5: jmf rtp

2.4. Le Démarrage du Processor

Il suffit d’utiliser la méthode start() du Processor, pour le démarrer.

public void Demarre(Processor p) { //Demarrage du Processor p.start(); }

3. Transmission du RTP : Finalisation 3.1. Base

Pour envoyer un flux vers quelqu'un d’autre, on a le choix d’utiliser un DataSink ou un RTPManager. L’utilisation du DataSink est moins complexe, mais l’utilisation du RTPManager permet plus de choses.

3.1.1. Emploi du DataSink

On peut utiliser un DataSink pour envoyer en sortie le flux. Dans ce cas, il faut obtenir un DataSource à l’aide de la méthode getDataOutput() du Processor, et il faut créer un MédiaLocator sur l’adresse et le port de la personne qui est la destination du flux. Puis on crée un DataSink à l’aide de ce MédiaLocator et du DataSource. Enfin, on ouvre ce DataSink et on le démarre.

public void createDataSink(Processor p) { //Creation du DataSource correspondant au Processor DataSource WebcamSource = p.getDataOutput(); //Adresse de Destination String OutputAddress = "rtp://192.168.3.6:22224/video/1" ; //Creation du MediaLocator pour l'Adresse de destin ation MediaLocator OutputLocator = new MediaLocator(OutputAddress); try { //Creation du DataSink DataSink OutputSink =Manager.createDa taSink( WebcamSource,OutputLocator); //Ouverture du DataSink OutputSink.open(); //Demarrage du DataSink OutputSink.start(); System.out.println( "Started" ); } catch(IOException e) { } catch(NoDataSinkException e) { } }

3.1.2. Emploi du RTPManager

Pour utiliser un RTPManager, il faut d’abord obtenir le DataSource à partir du Processor et de la méthode getDataOutput(). Ensuite, il faut créer une nouvelle instance du RTPManager à

Page 6: jmf rtp

l’aide de la méthode statique newInstance() du RTPManager. On doit alors faire appel à la méthode initialize() du RTPManager, dans laquelle on met une SessionAddress correspondant à l’adresse locale. Puis, on doit ajouter une destination à l’aide de la méthode addTarget() du RTPManager, dans laquelle on entre une SessionAddress correspondant à l’adresse de destination. Enfin on crée un envoi de flux à l’aide de la méthode createSendStream() et on le démarre.

public void createRTPManager(Processor p) { //Creation du DataSource correspondant au Processor DataSource OutputSource = p.getDataOutput(); //Nouvelle Instance d'un RTPManager RTPManager rtpm = RTPManager.newInstance(); try { //Création d'une SessionAddress // correspondant à l'adresse locale SessionAddress localaddr = new SessionAddress (InetAddress.getLocalHost(),40 000); //Initialisation du RTPManager // à partir de la SessionAddresse locale rtpm.initialize(localaddr); //Création d'une SessionAddress // correspondant à l'adresse de destination SessionAddress destaddr = new SessionAddress (InetAddress.getByName( "192.168.3.6" ),22224); //Ajout de cette SessionAddress dans le RTPManager rtpm.addTarget(destaddr); //Creation d'un SendStream à partir du DataSource SendStream ss2 = rtpm.createSendStrea m(OutputSource,0); //Demarrage du SendStream ss2.start(); System.out.println( "Started" ); } catch(UnknownHostException e) { } catch(IOException e) { } catch(InvalidSessionAddressException e) { } catch(UnsupportedFormatException e) { } }

3.2. Envoi d'une source de flux unique vers plusieu rs destinations

Il peut arriver qu'on doive transmettre notre source sur vers plusieurs IP, dans le cas de visioconférences par exemple. Dans ce cas, on a deux possibilités : - le clonage du DataSource - L’utilisation d’un RTPManager

3.2.1. Le clonage du DataSource

On peut, à partir du DataSource créé, faire un deuxième DataSource clonable à l aide de la méthode « createCloneableDataSource(DataSource s) » du Manager. A partir de ce deuxième DataSource, on pourra enfin créer un DataSource cloné grâce à la méthode createClone() de la

Page 7: jmf rtp

classe SourceCloneable. Il suffira ensuite de créer autant de DataSink que l’on a de DataSource et de MediaLocator.

//Création du Premier DataSource à partir //du processor configuré et réalisé DataSource WebcamSource = WebcamCessor.getDataOutpu t(); //Création du Deuxieme DataSource //qui est un Cloneable DataSource DataSource WebcamSource2 = Manager.createCloneableDataSource(WebcamSour ce); //Création du Troisime DataSouce //qui est un DataSource cloné du deuxième DataSource WebcamSource3 = ((SourceCloneable)WebcamSource2).createClone (); //Création du Troisime DataSouce //qui est un DataSource cloné du deuxième DataSource WebcamSource4 = ((SourceCloneable)WebcamSource2).createClone (); try { //Creation du Premier MediaLocator, pour la premier e IP MediaLocator m = new MediaLocator( "rtp://192.168.3.6:22224/video/1" ); //Creation du Premier DataSink, pour le Premier Med iaLocator // avec le premier DataSource DataSink dsk = Manager.createDataSink(Webcam Source2,m); //Ouverture du DataSink dsk.open(); //Demarrage du DataSink dsk.start(); //Creation du Deuxieme MediaLocator, pour la deuxie me IP MediaLocator m3= new MediaLocator( "rtp://192.168.3.2:22224/video/1" ); //Creation du Deuxieme DataSink, pour le Deuxieme M ediaLocator // avec le deuxieme DataSource DataSink dsk3 = Manager.createDataSink(Webca mSource3,m3); //Ouverture du DataSink dsk3.open(); //Demarrage du DataSink dsk3.start(); //Creation du Deuxieme MediaLocator, pour la deuxie me IP MediaLocator m4= new MediaLocator( "rtp://192.168.3.6:40000/video/1" ); //Creation du Deuxieme DataSink, pour le Deuxieme M ediaLocator // avec le deuxieme DataSource DataSink dsk4 = Manager.createDataSink(Webca mSource4,m4); //Ouverture du DataSink dsk4.open(); //Demarrage du DataSink dsk4.start(); } catch(Exception e) { System.out.println( "Erreur4 " +e.getMessage()); }

3.2.2. Emploi du RTPManager

Pour avoir plusieurs destinations à l’aide d’un RTPManage, la manière est plus simple, il suffit juste d’ajouter autant de cible avec la méthode addTarget() que l’on a de destinations. Ensuite pour chaque cible, on crée un envoi de flux que l’on démarre.

public void createRTPManager2(Processor p) { //Creation du DataSource correspondant au Processor DataSource OutputSource = p.getDataOutput(); //Nouvelle Instance d'un RTPManager RTPManager rtpm = RTPManager.newInstance();

Page 8: jmf rtp

try { //Création d'une SessionAddress // correspondant à l'adresse locale SessionAddress localaddr = new SessionAddress (InetAddress.getLocalHost(),40 000); rtpm.initialize(localaddr); //Création d'une SessionAddress //correspondant à la première adresse de destinatio n SessionAddress destaddr1 = new SessionAddress (InetAddress.getByName( "192.168.3.6" ),22224); //Ajout de la première SessionAddress dans le RTPMa nager rtpm.addTarget(destaddr1); //Creation d'un premier SendStream à partir du Data Source // Ce SendStream enverra le flux // à la première adresse de destination SendStream ss = rtpm.createSendStream (OutputSource,0); //Demarrage du premier SendStream ss.start(); //Création d'une SessionAddress //correspondant à la seconde adresse de destination SessionAddress destaddr2 = new SessionAddress (InetAddress.getByName( "192.168.3.2" ),22224); //Ajout de la seconde SessionAddress dans le RTPMan ager rtpm.addTarget(destaddr2); //Creation d'un second SendStream à partir du DataS ource // Ce SendStream enverra le flux // à la première adresse de destination SendStream ss2 = rtpm.createSendStrea m(OutputSource,0); //Demarrage du second SendStream ss2.start(); System.out.println( "Started" ); } catch(UnknownHostException e) { } catch(IOException e) { } catch(InvalidSessionAddressException e) { } catch(UnsupportedFormatException e) { } }

4. Réception du RTP Il existe différentes manières de réceptionner un flux. On n’est pas forcé d’utiliser un RTPManager, mais ce dernier offre une fois de plus de réels avantages.

4.1. A l'aide d'un MediaLocator

On peut réceptionner un flux sans utiliser de RTPManager. Pour cela, il suffit de créer un MédiaLocator sur l’adresse source et de créer un Player réalisé sur ce MédiaLocator. Dans le cas d’un flux vidéo, on ajoute le composant visuel du Player (à l’aide de la méthode getVisualComponent()) dans une Fenêtre. Enfin on démarre le Player. Néanmoins, le gros défaut de ne pas utiliser de RTPManager est que dans ce cas, il faut absolument que la source aie commencé à émettre le flux lorsque l on lance le client.

Page 9: jmf rtp

//Adresse de la Source String SourceAddress = "rtp://192.168.3.6:22224/video/1" ; //Creation du MediaLocator avec l'adresse de la Sou rce MediaLocator SourceLocator = new MediaLocator(SourceAddress); //Verification que la source existe //dans le cas contraire, arret du programme if(SourceLocator == null) { System.out.println( "no Source" ); System.exit(-1); } else { System.out.println( "Connected" ); } //Declaration du Player Player player; try { //Creation du player réalisé à partir du Médialocat or //de la source player = Manager.createRealizedPlayer(Source Locator); //Demarrage du Player player.start(); //Creation d'une JFrame JFrame fenetre = new JFrame( "Player" ); fenetre.setSize(160,140); //Ajout du Composant visuel du Player dans la fenet re // (pour un flux vidéo) fenetre.getContentPane().add(player.getVisua lComponent()); fenetre.setVisible( true); } catch (NoPlayerException e) { } catch (IOException e) { } catch (CannotRealizeException e) { }

4.2. Emploi du RTPManager

On peut aussi utiliser un RTPManager. Dans ce cas, on peut aussi utiliser l’évènement ReceiveStreamEvent, qui se produit lorsque l’on reçoit un flux : on n'est alors pas obligé de lancé le client avant le serveur.

4.2.1. Création du RTPManager

Pour instancier un nouveau RTPManager, il faut faire appel à la méthode statique newInstance() du RTPManager. Ensuite, de la même manière que sur le serveur, on utilise les méthodes initialize() et addTarget() pour le configurer. Enfin, on utilise la méthode addReceiveStreamListener pour utiliser la méthode appelée par l’évènement ReceiveStreamEvent.

public class MainManagerReception implements ReceiveStreamListener { public static void main(String[] args) { new MainManagerReception(); }

Page 10: jmf rtp

public MainManagerReception() { JFrame mainfra = new JFrame(); mainfra.setVisible( true); //Instanciation du RTPManager RTPManager VideoManager = RTPManager.newIns tance(); VideoManager.addFormat( new VideoFormat( VideoFormat.H263_RTP),18); try { //Creation d'une SessionAddress pour l'adresse loca le SessionAddress add = new SessionAddress( InetAddress.getLocalHost(),222 24); //Initialisation du RTPManager // à partir de cette SessionAddress VideoManager.initialize(add); //Creation d'une SessionAddress pour l'adresse sour ce SessionAddress add2 = new SessionAddress( InetAddress.getByName( "192.168.3.6" ),40000); //Ajout de cette SessionAddress dans le RTPManager VideoManager.addTarget(add2); } catch(InvalidSessionAddressException e) { } catch(IOException e) { } //Ajout du Listener de Reception de Stream dans le RTPManager VideoManager.addReceiveStreamListener( this); System.out.println( "Client Started" ); } }

4.2.2. Gestion de l’évènement ReceiveStreamEvent

Cette méthode se produit lorsque l’on reçoit un flux. Il faut donc d’abord s’assurer que c’est un nouveau flux. Ensuite, on récupère le DataSource de ce flux et on crée un Player avec. Enfin si, c’est un flux vidéo, on ajoute le composant visuel dans une nouvelle Fenêtre créée.

public void update(ReceiveStreamEvent event) { //Verification que l'event est un nouvel event if(event instanceof NewReceiveStreamEvent) { System.out.println( "New Reception" ); //Nouveau Flux Recu obtenu ReceiveStream rs = event.getReceiveS tream(); try { //Creation du Player sur ce flux Player p = Manager.createReali zedPlayer( rs.getDataSource()); //Si le player a un composant visuel, // alors creation d'une fenetre if(p.getVisualComponent() != null) { JFrame fenetre = new JFrame(); fenetre.setSize(160,140 ); fenetre.getContentPane( ).add( p.getVisualCompo nent()); fenetre.setVisible( true); } //Demarrage du Player p.start(); } catch(NoPlayerException e) { }

Page 11: jmf rtp

catch(CannotRealizeException e) { } catch(IOException e) { } } }

Conclusion On peut donc voir qu’il est finalement simple de faire de premières applications en Java qui utilise une Webcam ou un micro : en effet, l’ensemble de la bibliothèque JMF est facile d’accès.

Enfin, cet exposé restant relativement basique, je ne saurais que trop vous conseiller de vous documenter sur http://java.sun.com/products/java-media/jmf/2.1.1/guide/ pour de plus amples informations.