Aide - Recherche - Membres - Calendrier
Version complète : Creation d'un stream audio en java
Forum PC INpact > Systèmes, logiciels et lignes de codes > Programmation et Création web > Programmation (C, Perl, Python, Java...)
tsubasaleguedin

Bonjour,
je debute en java et j'essaye de faire serveur de flux audio.
je tourne autour depuis quelques jour sur un probleme.
Je souhaite faire un stream audio ( compressé ou non ).

Je selectionne en premier lieu le bon mixer avec la TargetLine qui va bien.

Je peux soit recuperer le flux dans un fichier wav ou soit le mettre en memoire dans un bytearrayoutputstream.


Deux question:
1- Comment 'connecter' le flux audio sur un socket .
2- Comment 'recuperer' le flux et le connecter au mixer pour le jouer.

Dans mes recherches je suis tombé sur JTAPI pour la telephonie sur java, mais c'est quand meme de la grosse usine a gaz.

Le but est pas de faire de la téléphonie ( meme si je veux un call to call de pc à pc ), juste un echange de flux audio avec une qualité correcte.

Question subsidiaire: mettre une compression ( ogg, mp3 ou autre ) avant d'envoyer le flux, et le decompresser a la reception


Micro (PC1) -> flux audio reseau socket -> Enceintes (PC 2 ).

Merci beaucoups pour votre aide !
tsubasaleguedin
Et bien ca attire pas les foules :) Je pensais que y'avais des dieux de Java dans le coin ..
Ceci dit j'ai reussi avec JMF par contre j'ai un autre soucis, car c'est pas du temps reel quoi ... J'ai un delay de 100 ms a vue d'oreille sur le stream.

Voici le code:
CODE
import javax.media.*;
import javax.media.format.*;
import javax.media.protocol.*;
import javax.media.control.*;
import java.util.*;
import java.io.*;
import javax.media.rtp.*;
import java.net.InetAddress;
import javax.media.control.BufferControl;



public class RtpServer {

    /**
     * @param args
     */
    public static void main(String[] args) {
        int                 i, j;
        int                 nCount=0;
        CaptureDeviceInfo   infoCaptureDevice;
        Format              arrFormats [];
        // TODO Auto-generated method stub
        CaptureDeviceInfo di=null;
        System.out.println("test");
         // Get the CaptureDeviceInfo for the live audio capture device
         Vector deviceList = CaptureDeviceManager.getDeviceList(new
                                AudioFormat("linear", 22050, 8, 1));
         if (deviceList.size()>0)
         {
             nCount=deviceList.size();
             System.out.println("Enumerating devicelist");
               for ( i = 0;  i < nCount;  i++ ) {
                    infoCaptureDevice = (CaptureDeviceInfo) deviceList.elementAt ( i );
                    arrFormats = infoCaptureDevice.getFormats ();
                    for ( j = 0;  j < arrFormats.length;  j++ ) {
                        if ( arrFormats[j] instanceof AudioFormat ) {
                            //vectorAudioDevices.addElement ( infoCaptureDevice );
                            System.out.println(infoCaptureDevice);
                            break;
                        }
                    }
                }
         }else{System.exit(0);}

            /*cdiAudio = getAudioDevice();
            if ( cdiAudio != null  &&  isAudioDeviceUsed() ) {
                deviceURL = cdiAudio.getLocator ();
                if ( strValue.length() > 0 )
                    strValue = strValue + " & ";
                strValue = strValue + deviceURL.toString ();
            }*/

        
         di = (CaptureDeviceInfo) deviceList.elementAt(0);
        
         // Create a Player for the capture device: */
         /*try{
             Player p = Manager.createPlayer(di.getLocator());
             p.start();
         } catch (IOException e) {
         } catch (NoPlayerException e) {}*/
//            Création du MediaLocator à partir du fichier
            MediaLocator FichierLocator = di.getLocator();
            
//            Déclaration du processeur
            Processor FichierCessor = null;
            DataSource ds=null;
            try{
                //Création du Processor à partir du MediaLocator
                try{
                    ds = Manager.createDataSource(FichierLocator);
                    
                }
                catch(Exception e){}  
                // Check to see if there's a buffer control on the data source.
                // It could be that we are using a capture data source.
                Control c = (Control)ds.getControl("javax.media.control.BufferControl");
                if (c != null){
                    ((BufferControl)c).setBufferLength(1);
                    ((BufferControl)c).setMinimumThreshold(2);
                    ((BufferControl)c).setEnabledThreshold(true);

                }
                    FichierCessor = Manager.createProcessor(ds);
        
                
                //FichierCessor = Manager.createProcessor(ds);  
                
                //Appel des fonctions qui vont permettre le lancement du flux RTP
                configure(FichierCessor);
                SetSupportedFormat(FichierCessor);
                //passer dans l'etat realized du processor
                realize(FichierCessor);
                //start    
                Demarre(FichierCessor);
                launchRTPManager(FichierCessor);
            }
            catch(IOException e)
            {
                System.out.println("Erreur : "+e.getMessage());
            }
            catch(NoProcessorException e)
            {
                System.out.println("Erreur : "+e.getMessage());
            }

    }
    public static 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;
    }
    public static void SetSupportedFormat(Processor p)
    {
        //On met la description du contenu de sortie à RAW_RTP
        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é
            //System.out.println();
            for ( int t=0; t < suppFormats.length;t++)
            {
                System.out.println("RTP supported format: "+suppFormats[t]);
            }
            if(suppFormats.length > 0)
            {
                track[i].setFormat(suppFormats[5]);
                System.err.println("Track " + i +" est transmis as :"+suppFormats[5]);
                if (track[i].getFormat() instanceof AudioFormat) {
                    int packetRate = 5;
                    PacketSizeControl pktCtrl = (PacketSizeControl) p.getControl(PacketSizeControl.class.getName());
                    if (pktCtrl != null) {
                        try {
                             pktCtrl.setPacketSize(getPacketSize(track[i].getFormat(), packetRate));
                            //pktCtrl.setPacketSize(pktCtrl.getPacketSize());
                            //pktCtrl.setPacketSize(1);
                        }
                        catch (IllegalArgumentException e) {
                            pktCtrl.setPacketSize(80);
                            // Do nothing
                        }
                    }
                }

                
                
                
            }
            else
            {
                track[i].setEnabled(false);
            }
        }
        
        
    }
    
    private static int getPacketSize(Format codecFormat, int milliseconds) throws IllegalArgumentException {
        String encoding = codecFormat.getEncoding();
        if (encoding.equalsIgnoreCase(AudioFormat.GSM) ||
                encoding.equalsIgnoreCase(AudioFormat.GSM_RTP)) {
            return milliseconds * 4; // 1 byte per millisec
        }
        else if (encoding.equalsIgnoreCase(AudioFormat.ULAW) ||
                encoding.equalsIgnoreCase(AudioFormat.ULAW_RTP)) {
            return milliseconds * 8;
        }
        else {
            throw new IllegalArgumentException("Unknown codec type");
        }
    }

    
    public static 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;
    }
    
    public static void Demarre(Processor p)
    {
        //Demarrage du Processor
        p.start();
        System.err.println("Processor started");
    }

    public static void launchRTPManager(Processor p)
    {
        //Creation du DataSource correspondant au Processor
        DataSource OutputSource = p.getDataOutput();
        PushBufferDataSource pbds = (PushBufferDataSource)OutputSource;
        PushBufferStream pbss[] = pbds.getStreams();              
        //Nouvelle Instance d'un RTPManager      
        RTPManager rtpm[] = new RTPManager[pbss.length];
        
        //System.out.println("taille:" +pbss.length);
        //RTPManager rtpm;
        for(int i=0; i < pbss.length;i++)    
        {
            try{
                rtpm[i] = RTPManager.newInstance();
                int port = 22224 + 2*i;
                //Création d'une SessionAddress
                SessionAddress localaddr = new SessionAddress(InetAddress.getLocalHost(),port);
                //Initialisation du RTPManager
                rtpm[i].initialize(localaddr);
                //Création d'une SessionAddress
                SessionAddress destaddr = new SessionAddress(InetAddress.getByName("192.168.1.11"),6666);
                
                //Ajout de cette SessionAddress dans le RTPManager
                rtpm[i].addTarget(destaddr);
                System.err.println("Creation RTP session 192.168.1.12 port : "+port);                    
                //Creation d'un SendStream à partir du DataSource                
                SendStream ss2 = rtpm[i].createSendStream(OutputSource,i);
                //Demarrage du SendStream
                ss2.start();
                System.out.println("Started ");
            }
            catch(Exception e)
            {
                e.printStackTrace();
            }      
            
        }    
    }
    
}


Et voici le code du client utilisé:
CODE
import java.io.IOException;
import java.net.MalformedURLException;

import javax.media.Manager;
import javax.media.MediaLocator;
import javax.media.NoPlayerException;
import javax.media.Player;

public class SimpleVoiceReceiver{
  
    /**
       * @param args
       */
      public static void main(String[] args) {
          String url= "rtp://192.168.1.11:6666/audio/1";
          
          MediaLocator mrl= new MediaLocator(url);
          
          if (mrl == null) {
              System.err.println("Can't build MRL for RTP");
              System.exit(-1);
          }
          
          // Create a player for this rtp session
          Player player = null;
          try {
              player = Manager.createPlayer(mrl);
          } catch (NoPlayerException e) {
              System.err.println("Error:" + e);
              System.exit(-1);
          } catch (MalformedURLException e) {
              System.err.println("Error:" + e);
              System.exit(-1);
          } catch (IOException e) {
              System.err.println("Error:" + e);
              System.exit(-1);
         }
          
         if (player != null) {
             System.out.println("Player created.");
            player.realize();
//          wait for realizing
              while (player.getState() != Player.Realized){
                  try {
                      Thread.sleep(10);
                } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
              }
              player.start();
          } else {
              System.err.println("Player doesn't created.");
              System.exit(-1);
          }
      }
  
}


J'ai le meme probleme de delay quand j'utilise VLC comme client meme en reglant le jitter RTP à 1 ...

Quand je fait du direct microphone vers Haut parleur ( sans utiliser le processor ni RTP) j'ai aucun delay, par contre des que je fous sur RTP ca delay... Vla le protocole real time ca fait peur quoi ....

J'ai essayer de virer toute les compression, de changer le packetsize du controlleur, de changer la taille du buffer etc ... et pas moyen d'avoir mieux que 100 ms ..
Je precise que les test RTP ce font sur la meme machine de port à port donc on est meme pas sur le reseau...

Une expertise Java serait la bienvenue.


MERCI
lorinc
Je crois que tu viens de tuer par arrêt cardiaque tous les experts java du forum... transpi.gif

Sérieux, tout dans le main et que des méthodes statiques, vla la programation orientée objet... Tu veux pas faire un design objet plus propre ? transpi.gif

Sinon, pour la latence, ça ne me paraît pas iréaliste. ça n'a rien à voir avec le protocle (ou du moins peu), mais au fait que tu passe par le réseau, et donc qu'il faut négocier la connection, remplir remplir un buffer, etc. Ça prend un certain temps à l'init.

Je crois que tu te méprends sur le sens du terme "temps-réel". Ça ne veut pas dire que dès que tu auras lancé l'appli, le son va se jouer sur l'autre PC. Ça veut dire que tu es assuré qu'il n'y aura pas de coupure lors de la lecture sur l'autre PC, c'est à dire que le temps entre la réception de deux paquets est inférieur à une certaine valeur.
D'ailleurs, ça ne m'étonnerait pas qu'une partie de la latence soit due à ça : un tampon qu'on remplit au début pour garantir la restitution du flux en cas de boulettes temporaires sur le réseau...

En info, temps-réel == prédictible et reproductible. C'est à dire que les temps d'exécution des tâches ont des brones connues à l'avance. Ça ne veut pas dire que le résultat est immédiat chinese.gif
fabien29200
QUOTE (tsubasaleguedin @ 30-11-2008 15:45:03) *
Et bien ca attire pas les foules :) Je pensais que y'avais des dieux de Java dans le coin ..

En même temps, c'est pas parce qu'on connaît Java qu'on connaît la gestion du son avec Java. C'est vachement spécifique comme sujet.
tsubasaleguedin
CITATION(lorinc @ 02-12-2008 19:34:15) *
Je crois que tu viens de tuer par arrêt cardiaque tous les experts java du forum... transpi.gif

Sérieux, tout dans le main et que des méthodes statiques, vla la programation orientée objet... Tu veux pas faire un design objet plus propre ? transpi.gif

Sinon, pour la latence, ça ne me paraît pas iréaliste. ça n'a rien à voir avec le protocle (ou du moins peu), mais au fait que tu passe par le réseau, et donc qu'il faut négocier la connection, remplir remplir un buffer, etc. Ça prend un certain temps à l'init.

Je crois que tu te méprends sur le sens du terme "temps-réel". Ça ne veut pas dire que dès que tu auras lancé l'appli, le son va se jouer sur l'autre PC. Ça veut dire que tu es assuré qu'il n'y aura pas de coupure lors de la lecture sur l'autre PC, c'est à dire que le temps entre la réception de deux paquets est inférieur à une certaine valeur.
D'ailleurs, ça ne m'étonnerait pas qu'une partie de la latence soit due à ça : un tampon qu'on remplit au début pour garantir la restitution du flux en cas de boulettes temporaires sur le réseau...

En info, temps-réel == prédictible et reproductible. C'est à dire que les temps d'exécution des tâches ont des brones connues à l'avance. Ça ne veut pas dire que le résultat est immédiat chinese.gif


Pour les static c'etait prévu ( bien vue clindoeil.gif ), j'avais fais ca pour tester sa me soulé d'instancier un objet, c'est clair que jlai fait procedurale ici pour simplifier aussi le probleme.

Pour ce qui est du buffer je l'entend bien ( malgré que meme si je le set a 1 ca change pas grand chose ), mais 100 ms ca me parait bcps enfin j'ai eu d'autres idée pour remedier a ca clindoeil.gif

Merci
ouragan
CITATION(tsubasaleguedin @ 08-12-2008 16:41:35) *
CITATION(lorinc @ 02-12-2008 19:34:15) *
Je crois que tu viens de tuer par arrêt cardiaque tous les experts java du forum... transpi.gif

Sérieux, tout dans le main et que des méthodes statiques, vla la programation orientée objet... Tu veux pas faire un design objet plus propre ? transpi.gif

Sinon, pour la latence, ça ne me paraît pas iréaliste. ça n'a rien à voir avec le protocle (ou du moins peu), mais au fait que tu passe par le réseau, et donc qu'il faut négocier la connection, remplir remplir un buffer, etc. Ça prend un certain temps à l'init.

Je crois que tu te méprends sur le sens du terme "temps-réel". Ça ne veut pas dire que dès que tu auras lancé l'appli, le son va se jouer sur l'autre PC. Ça veut dire que tu es assuré qu'il n'y aura pas de coupure lors de la lecture sur l'autre PC, c'est à dire que le temps entre la réception de deux paquets est inférieur à une certaine valeur.
D'ailleurs, ça ne m'étonnerait pas qu'une partie de la latence soit due à ça : un tampon qu'on remplit au début pour garantir la restitution du flux en cas de boulettes temporaires sur le réseau...

En info, temps-réel == prédictible et reproductible. C'est à dire que les temps d'exécution des tâches ont des brones connues à l'avance. Ça ne veut pas dire que le résultat est immédiat chinese.gif


Pour les static c'etait prévu ( bien vue clindoeil.gif ), j'avais fais ca pour tester sa me soulé d'instancier un objet, c'est clair que jlai fait procedurale ici pour simplifier aussi le probleme.

Pour ce qui est du buffer je l'entend bien ( malgré que meme si je le set a 1 ca change pas grand chose ), mais 100 ms ca me parait bcps enfin j'ai eu d'autres idée pour remedier a ca clindoeil.gif

Merci


Ben perso, je trouve très agréable d'avoir des applis qui fonctionnent même lorsque j'ai pas encore écrit les méthodes. yaisse.gif
ça c'est le gros avantage de séparer les objets.
Spareil pour rajouter un SGBD , vraiment très agréable.
Ceci est une version "bas débit" de notre forum. Pour voir la version complète avec plus d'informations, la mise en page et les images, veuillez cliquer ici.
Invision Power Board © 2001-2009 Invision Power Services, Inc.