Vous êtes sur la page 1sur 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.
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(FichierLocator);
}
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;
//Declaration du CaptureDeviceInfo du Micro
CaptureDeviceInfo Micro = null;

//Recherche des CaptureDevices


Vector deviceList = CaptureDeviceManager.getDeviceList(
new AudioFormat( "linear",8000,8,1));
Vector deviceList2 = CaptureDeviceManager.getDeviceList(
new VideoFormat(VideoFormat.YUV));

//Creation des CaptureDeviceInfo pour le Micro et la Webcam


Micro = (CaptureDeviceInfo)deviceList.get(0);
Webcam = (CaptureDeviceInfo)deviceList2.get(0);

//Declaration des Processors du Micro et de la Webcam


Processor MicroCessor = null;
Processor WebcamCessor = null;

try
{
//Creation du Processor du Micro et de la Webcam
MicroCessor = Manager.createProcessor(Micro.getLocator());
WebcamCessor = Manager.createProcessor(Webcam.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;
}
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(
ContentDescriptor.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].getSupportedFormats();

//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;
}
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 destination
MediaLocator OutputLocator = new MediaLocator(OutputAddress);

try
{
//Creation du DataSink
DataSink OutputSink =Manager.createDataSink(
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 à
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(),40000);
//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.createSendStream(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 plusieurs 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
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.getDataOutput();
//Création du Deuxieme DataSource
//qui est un Cloneable DataSource
DataSource WebcamSource2 =
Manager.createCloneableDataSource(WebcamSource);
//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 premiere IP
MediaLocator m =
new MediaLocator("rtp://192.168.3.6:22224/video/1");
//Creation du Premier DataSink, pour le Premier MediaLocator
// avec le premier DataSource
DataSink dsk = Manager.createDataSink(WebcamSource2,m);
//Ouverture du DataSink
dsk.open();
//Demarrage du DataSink
dsk.start();

//Creation du Deuxieme MediaLocator, pour la deuxieme IP


MediaLocator m3=
new MediaLocator("rtp://192.168.3.2:22224/video/1");
//Creation du Deuxieme DataSink, pour le Deuxieme MediaLocator
// avec le deuxieme DataSource
DataSink dsk3 = Manager.createDataSink(WebcamSource3,m3);
//Ouverture du DataSink
dsk3.open();
//Demarrage du DataSink
dsk3.start();

//Creation du Deuxieme MediaLocator, pour la deuxieme IP


MediaLocator m4=
new MediaLocator("rtp://192.168.3.6:40000/video/1");
//Creation du Deuxieme DataSink, pour le Deuxieme MediaLocator
// avec le deuxieme DataSource
DataSink dsk4 = Manager.createDataSink(WebcamSource4,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();
try
{
//Création d'une SessionAddress
// correspondant à l'adresse locale
SessionAddress localaddr = new SessionAddress
(InetAddress.getLocalHost(),40000);
rtpm.initialize(localaddr);

//Création d'une SessionAddress


//correspondant à la première adresse de destination
SessionAddress destaddr1 = new SessionAddress
(InetAddress.getByName("192.168.3.6"),22224);
//Ajout de la première SessionAddress dans le RTPManager
rtpm.addTarget(destaddr1);
//Creation d'un premier SendStream à partir du DataSource
// 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 RTPManager
rtpm.addTarget(destaddr2);
//Creation d'un second SendStream à partir du DataSource
// Ce SendStream enverra le flux
// à la première adresse de destination
SendStream ss2 = rtpm.createSendStream(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.
//Adresse de la Source
String SourceAddress = "rtp://192.168.3.6:22224/video/1";

//Creation du MediaLocator avec l'adresse de la Source


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édialocator
//de la source
player = Manager.createRealizedPlayer(SourceLocator);
//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 fenetre
// (pour un flux vidéo)
fenetre.getContentPane().add(player.getVisualComponent());
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();
}
public MainManagerReception()
{
JFrame mainfra = new JFrame();
mainfra.setVisible(true);

//Instanciation du RTPManager
RTPManager VideoManager = RTPManager.newInstance();
VideoManager.addFormat(new VideoFormat(
VideoFormat.H263_RTP),18);

try
{
//Creation d'une SessionAddress pour l'adresse locale
SessionAddress add = new SessionAddress(
InetAddress.getLocalHost(),22224);
//Initialisation du RTPManager
// à partir de cette SessionAddress
VideoManager.initialize(add);

//Creation d'une SessionAddress pour l'adresse source


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.getReceiveStream();
try
{
//Creation du Player sur ce flux
Player p = Manager.createRealizedPlayer(
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.getVisualComponent());
fenetre.setVisible(true);
}
//Demarrage du Player
p.start();
}
catch(NoPlayerException e)
{
}
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.

Vous aimerez peut-être aussi