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 visioconfé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.

javax. //Declaration du Processor Processor FichierCessor = null. puis créer un Processor sur ce MédiaLocator.media.media.*. try { //Creation du Processor à partir du Medialocator FichierCessor = Manager.out.*.1.media. javax.createProcessor(FichierLocator). il faut créer un MédiaLocator sur le fichier.*.control.*.format. 1. javax.avi". //Declaration du CaptureDeviceInfo de la Webcam CaptureDeviceInfo Webcam = null.*.awt. } catch(IOException e) { System.media.Cet exposé montre ainsi les bases de l’utilisation du protocole RTP.*.getMessage()).net.media. //Création du MédiaLocator à partir de ce fichier MediaLocator FichierLocator = new MediaLocator(FichierAdresse). Une fois. javax.media.2. Il faut alors sélectionner le périphérique de capture à l aide du CaptureDeviceManager.rtp.event. il faudra faire les imports suivants : import import import import import import import import import import import import javax. java.util. On peut vouloir utiliser une Webcam et un Micro et envoyer le flux audio et vidéo sur le réseau.protocol. . java. le bon périphérique sélectionné. } catch(NoProcessorException e) { System. Source Capture vidéo/audio Néanmoins.rtcp. celui renvoie la liste complète des périphériques de capture.media.*. Source Fichier On peut souhaiter avoir une source Fichier dans le cas de radio ou de Télévision sur Internet.*.rtp.out. Imports Requis Pour utiliser les différentes possibilités du protocole RTP.rtp.*.getMessage()). java. javax. javax.*. on crée un Processor à partir de celui-ci. java.swing. Dans ce cas.*. //Entrée de l'adresse du fichier String FichierAdresse = "file://d:/test. Sa méthode getDeviceList() appelé avec null en argument.*. Transmission du RTP : Configuration de la source 1. javax.println("Erreur : "+e. } 1.println("Erreur : "+e.io.

Webcam = (CaptureDeviceInfo)deviceList2.configure(). Néanmoins. cette méthode n’est pas bloquante et n’attend donc pas que le Processor soit configuré pour continuer. est simple : il suffit d’appeler la méthode configure() du Processor.get(0). qui démultiplexe le flux d’entrée et obtient des informations sur le format des données d’entrée.1. d’autres méthodes font appel à un Processor configuré. et après qu’un Processor soit créé. . 2.YUV)). c’est à dire que si dans la suite du programme.//Declaration du CaptureDeviceInfo du Micro CaptureDeviceInfo Micro = null. } .println("Erreur : "+e.createProcessor(Micro.getMessage()).8000.get(0). qui est le stade abouti du Processor.8.Configured) { //Configuration du Processor p.getDeviceList( new AudioFormat( "linear". Transmission du RTP : Les différentes étapes de l’état du Processor Pour permettre de transmettre un flux. La Configuration Le moyen.createProcessor(Webcam.out.getLocator()).println("Erreur : "+e. pour configurer un Processor. } 2. try { //Creation du Processor du Micro et de la Webcam MicroCessor = Manager. on obtiendra une erreur. //Declaration des Processors du Micro et de la Webcam Processor MicroCessor = null. } catch(IOException e) { System.Le démarrage du processor. } return p. 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é. //Creation des CaptureDeviceInfo pour le Micro et la Webcam Micro = (CaptureDeviceInfo)deviceList.getState() < Processor.getLocator()).Le bon formatage des pistes du Processor . public Processor configure(Processor p) { //Attendre tant que le Processor n'est pas configuré.out. Processor WebcamCessor = null. il passe par différentes étapes : .La configuration.getDeviceList( new VideoFormat(VideoFormat.getMessage()). //Recherche des CaptureDevices Vector deviceList = CaptureDeviceManager.La réalisation du processor. while(p. } catch(NoProcessorException e) { System. WebcamCessor = Manager. Vector deviceList2 = CaptureDeviceManager.1)). .

n’est pas bloquante.length . for(int i = 0 . } else { track[i].120).setContentDescriptor(cd). } . par exemple.realize(). Format. p. while(p. 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. pour connaître les différents formats supportés par chaque piste. le programme ne pourra pas transmettre le flux et générera une erreur.setFormat(supportedFormats[0]).getState() < Processor.H263_RTP.setFormat(new VideoFormat(VideoFormat. 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( ContentDescriptor. car la méthode realize(). } return p.2.3.buteArray. //Si il y a au moins un format supporté // alors on met le premier if(suppFormats. mais au lieu de : track[i]. //On obtient les différentes pistes du processor TrackControl track[] = p.2. Format. } } } 2. public Processor realize(Processor p) { //Attendre tant que le Processor n'est pas réalisé.NOT_SPECIFIED). on utilise la méthode getSupportedFormats() de la classe TrackControl. La Réalisation De-même que pour la configuration.getSupportedFormats(). Le code source suivant est simple.setEnabled(false).NOT_SPECIFIED. i < track. Mettre les différentes pistes du flux dans un format supporté On doit mettre chaque piste du Processor dans un format supporté sinon.RAW_RTP).length > 0) { track[i]. pour. Pour cela. Format.Realized) { //Réalisation du Processor p. i++) { //on obtient les formats supportés pour cette piste Format suppFormats[] = track[i]. new Dimension(160.setFormat(suppFormats[0]).getTrackControls(). mettre le flux de sortie en résolution 160 par 120 (si c’est un flux vidéo bien entendu). on pourrait mettre : track[i].

1.6:22224/video/1". } catch(IOException e) { } catch(NoDataSinkException e) { } } 3. Ensuite.open(). il faut créer une nouvelle instance du RTPManager à .println("Started"). mais l’utilisation du RTPManager permet plus de choses. //Adresse de Destination String OutputAddress = "rtp://192.1.start().4. } 3.2. et il faut créer un MédiaLocator sur l’adresse et le port de la personne qui est la destination du flux. //Creation du MediaLocator pour l'Adresse de destination MediaLocator OutputLocator = new MediaLocator(OutputAddress).out. 3. Base Pour envoyer un flux vers quelqu'un d’autre. pour le démarrer. Enfin. Emploi du RTPManager Pour utiliser un RTPManager. on a le choix d’utiliser un DataSink ou un RTPManager. public void Demarre(Processor p) { //Demarrage du Processor p. on ouvre ce DataSink et on le démarre.1.createDataSink( WebcamSource. //Demarrage du DataSink OutputSink. //Ouverture du DataSink OutputSink. il faut d’abord obtenir le DataSource à partir du Processor et de la méthode getDataOutput(). Dans ce cas.OutputLocator). Puis on crée un DataSink à l’aide de ce MédiaLocator et du DataSource.start(). il faut obtenir un DataSource à l’aide de la méthode getDataOutput() du Processor. public void createDataSink(Processor p) { //Creation du DataSource correspondant au Processor DataSource WebcamSource = p. System.2. L’utilisation du DataSink est moins complexe. try { //Creation du DataSink DataSink OutputSink =Manager.1. Emploi du DataSink On peut utiliser un DataSink pour envoyer en sortie le flux. Transmission du RTP : Finalisation 3.168.getDataOutput().3. Le Démarrage du Processor Il suffit d’utiliser la méthode start() du Processor.

Dans ce cas. on doit ajouter une destination à l’aide de la méthode addTarget() du RTPManager. faire un deuxième DataSource clonable à l aide de la méthode « createCloneableDataSource(DataSource s) » du Manager.getLocalHost().L’utilisation d’un RTPManager 3. System. //Creation d'un SendStream à partir du DataSource SendStream ss2 = rtpm. //Initialisation du RTPManager // à partir de la SessionAddresse locale rtpm. Enfin on crée un envoi de flux à l’aide de la méthode createSendStream() et on le démarre. } catch(UnknownHostException e) { } catch(IOException e) { } catch(InvalidSessionAddressException e) { } catch(UnsupportedFormatException e) { } } 3.40000). Puis. on a deux possibilités : .22224).out.6"). dans laquelle on met une SessionAddress correspondant à l’adresse locale. //Demarrage du SendStream ss2. //Création d'une SessionAddress // correspondant à l'adresse de destination SessionAddress destaddr = new SessionAddress (InetAddress.168.createSendStream(OutputSource.println("Started").2. try { //Création d'une SessionAddress // correspondant à l'adresse locale SessionAddress localaddr = new SessionAddress (InetAddress.le clonage du DataSource .getByName("192.initialize(localaddr). à partir du DataSource créé.start(). //Nouvelle Instance d'un RTPManager RTPManager rtpm = RTPManager.getDataOutput().2.l’aide de la méthode statique newInstance() du RTPManager.1. on pourra enfin créer un DataSource cloné grâce à la méthode createClone() de la . dans laquelle on entre une SessionAddress correspondant à l’adresse de destination.addTarget(destaddr). Le clonage du DataSource On peut.newInstance().0). Envoi d'une source de flux unique vers plusieurs destinations Il peut arriver qu'on doive transmettre notre source sur vers plusieurs IP. A partir de ce deuxième DataSource. public void createRTPManager(Processor p) { //Creation du DataSource correspondant au Processor DataSource OutputSource = p. dans le cas de visioconférences par exemple. //Ajout de cette SessionAddress dans le RTPManager rtpm.3. On doit alors faire appel à la méthode initialize() du RTPManager.

createClone().m). pour le Deuxieme MediaLocator // avec le deuxieme DataSource DataSink dsk3 = Manager. //Nouvelle Instance d'un RTPManager RTPManager rtpm = RTPManager. pour la deuxieme IP MediaLocator m4= new MediaLocator("rtp://192.3.start().168. } 3.open().createCloneableDataSource(WebcamSource).createClone(). la manière est plus simple.classe SourceCloneable. //Creation du Deuxieme MediaLocator.open().m4). //Ouverture du DataSink dsk3.2.getMessage()).newInstance(). pour la deuxieme IP MediaLocator m3= new MediaLocator("rtp://192.6:40000/video/1").2. //Demarrage du DataSink dsk.getDataOutput().168. on crée un envoi de flux que l’on démarre.getDataOutput(). . //Creation du Deuxieme DataSink. Il suffira ensuite de créer autant de DataSink que l’on a de DataSource et de MediaLocator.createDataSink(WebcamSource4. Ensuite pour chaque cible. //Création du Troisime DataSouce //qui est un DataSource cloné du deuxième DataSource WebcamSource3 = ((SourceCloneable)WebcamSource2).open(). } catch(Exception e) { System. pour le Premier MediaLocator // avec le premier DataSource DataSink dsk = Manager.createDataSink(WebcamSource2. pour le Deuxieme MediaLocator // avec le deuxieme DataSource DataSink dsk4 = Manager. //Ouverture du DataSink dsk.m3). Emploi du RTPManager Pour avoir plusieurs destinations à l’aide d’un RTPManage. pour la premiere IP MediaLocator m = new MediaLocator("rtp://192.start().6:22224/video/1"). //Création du Deuxieme DataSource //qui est un Cloneable DataSource DataSource WebcamSource2 = Manager.3.out. il suffit juste d’ajouter autant de cible avec la méthode addTarget() que l’on a de destinations. //Demarrage du DataSink dsk4. //Creation du Deuxieme MediaLocator. //Création du Troisime DataSouce //qui est un DataSource cloné du deuxième DataSource WebcamSource4 = ((SourceCloneable)WebcamSource2). try { //Creation du Premier MediaLocator.createDataSink(WebcamSource3.168. //Creation du Deuxieme DataSink.start(). //Création du Premier DataSource à partir //du processor configuré et réalisé DataSource WebcamSource = WebcamCessor.3.2:22224/video/1"). //Creation du Premier DataSink. //Ouverture du DataSink dsk4. //Demarrage du DataSink dsk3. public void createRTPManager2(Processor p) { //Creation du DataSource correspondant au Processor DataSource OutputSource = p.println("Erreur4 "+e.

start().168. } catch(UnknownHostException e) { } catch(IOException e) { } catch(InvalidSessionAddressException e) { } catch(UnsupportedFormatException e) { } } 4.out. //Creation d'un second SendStream à partir du DataSource // Ce SendStream enverra le flux // à la première adresse de destination SendStream ss2 = rtpm.0).getByName("192. il faut absolument que la source aie commencé à émettre le flux lorsque l on lance le client.createSendStream(OutputSource.addTarget(destaddr1). Réception du RTP Il existe différentes manières de réceptionner un flux. Enfin on démarre le Player. rtpm. //Création d'une SessionAddress //correspondant à la seconde adresse de destination SessionAddress destaddr2 = new SessionAddress (InetAddress.22224).start(). Pour cela.addTarget(destaddr2). .try { //Création d'une SessionAddress // correspondant à l'adresse locale SessionAddress localaddr = new SessionAddress (InetAddress. A l'aide d'un MediaLocator On peut réceptionner un flux sans utiliser de RTPManager.168.initialize(localaddr).2"). //Création d'une SessionAddress //correspondant à la première adresse de destination SessionAddress destaddr1 = new SessionAddress (InetAddress. on ajoute le composant visuel du Player (à l’aide de la méthode getVisualComponent()) dans une Fenêtre.createSendStream(OutputSource. il suffit de créer un MédiaLocator sur l’adresse source et de créer un Player réalisé sur ce MédiaLocator.22224).1.println("Started").getByName("192.6"). 4. mais ce dernier offre une fois de plus de réels avantages.40000). le gros défaut de ne pas utiliser de RTPManager est que dans ce cas.3.getLocalHost(). //Creation d'un premier SendStream à partir du DataSource // Ce SendStream enverra le flux // à la première adresse de destination SendStream ss = rtpm.0). On n’est pas forcé d’utiliser un RTPManager. //Demarrage du premier SendStream ss. System. //Ajout de la première SessionAddress dans le RTPManager rtpm. Néanmoins.3. //Demarrage du second SendStream ss2. Dans le cas d’un flux vidéo. //Ajout de la seconde SessionAddress dans le RTPManager rtpm.

//Creation du MediaLocator avec l'adresse de la Source MediaLocator SourceLocator = new MediaLocator(SourceAddress). on utilise les méthodes initialize() et addTarget() pour le configurer.exit(-1). } //Declaration du Player Player player. Emploi du RTPManager On peut aussi utiliser un RTPManager.println("no Source"). Enfin.6:22224/video/1".getVisualComponent()).start().createRealizedPlayer(SourceLocator).168.//Adresse de la Source String SourceAddress = "rtp://192.add(player. qui se produit lorsque l’on reçoit un flux : on n'est alors pas obligé de lancé le client avant le serveur.140). de la même manière que sur le serveur.1. on utilise la méthode addReceiveStreamListener pour utiliser la méthode appelée par l’évènement ReceiveStreamEvent. System.setVisible(true). } . //Creation d'une JFrame JFrame fenetre = new JFrame("Player"). try { //Creation du player réalisé à partir du Médialocator //de la source player = Manager. 4. //Ajout du Composant visuel du Player dans la fenetre // (pour un flux vidéo) fenetre.getContentPane(). Ensuite. on peut aussi utiliser l’évènement ReceiveStreamEvent.println("Connected"). Dans ce cas. fenetre.3. } else { System.2. //Demarrage du Player player. Création du RTPManager Pour instancier un nouveau RTPManager. il faut faire appel à la méthode statique newInstance() du RTPManager. arret du programme if(SourceLocator == null) { System.setSize(160. } catch (NoPlayerException e) { } catch (IOException e) { } catch (CannotRealizeException e) { } 4. public class MainManagerReception implements ReceiveStreamListener { public static void main(String[] args) { new MainManagerReception().out. //Verification que la source existe //dans le cas contraire.2. fenetre.out.

3. //Ajout de cette SessionAddress dans le RTPManager VideoManager.H263_RTP). } catch(NoPlayerException e) { } .initialize(add).18).add( p.newInstance(). public void update(ReceiveStreamEvent event) { //Verification que l'event est un nouvel event if(event instanceof NewReceiveStreamEvent) { System. //Si le player a un composant visuel. Gestion de l’évènement ReceiveStreamEvent Cette méthode se produit lorsque l’on reçoit un flux.2.setVisible(true). //Nouveau Flux Recu obtenu ReceiveStream rs = event.6"). fenetre. mainfra. fenetre.start().getReceiveStream(). try { //Creation du Player sur ce flux Player p = Manager.2.setVisible(true).getLocalHost(). //Creation d'une SessionAddress pour l'adresse source SessionAddress add2 = new SessionAddress( InetAddress. VideoManager.createRealizedPlayer( rs.out.getVisualComponent()). //Initialisation du RTPManager // à partir de cette SessionAddress VideoManager. on ajoute le composant visuel dans une nouvelle Fenêtre créée. } //Demarrage du Player p.140). } } 4.getByName("192. Ensuite.out. c’est un flux vidéo.40000). try { //Creation d'une SessionAddress pour l'adresse locale SessionAddress add = new SessionAddress( InetAddress.getVisualComponent() != null) { JFrame fenetre = new JFrame(). } catch(InvalidSessionAddressException e) { } catch(IOException e) { } //Ajout du Listener de Reception de Stream dans le RTPManager VideoManager.addTarget(add2).addFormat(new VideoFormat( VideoFormat.getDataSource()). fenetre. on récupère le DataSource de ce flux et on crée un Player avec.println("Client Started").22224).addReceiveStreamListener(this).println("New Reception").public MainManagerReception() { JFrame mainfra = new JFrame(). //Instanciation du RTPManager RTPManager VideoManager = RTPManager. System. Enfin si.getContentPane(). // alors creation d'une fenetre if(p. Il faut donc d’abord s’assurer que c’est un nouveau flux.setSize(160.168.

1.com/products/java-media/jmf/2. .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.1/guide/ pour de plus amples informations. cet exposé restant relativement basique. je ne saurais que trop vous conseiller de vous documenter sur http://java. Enfin. l’ensemble de la bibliothèque JMF est facile d’accès.sun.

Sign up to vote on this title
UsefulNot useful