Aller au contenu

Creation d'un stream audio en java


Messages recommandés

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 !

Lien vers le commentaire
Partager sur d’autres sites

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:

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é:

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

Lien vers le commentaire
Partager sur d’autres sites

Je crois que tu viens de tuer par arrêt cardiaque tous les experts java du forum... :D

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 ? :yes:

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 :chinois:

Lien vers le commentaire
Partager sur d’autres sites

Je crois que tu viens de tuer par arrêt cardiaque tous les experts java du forum... :D

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 ? :arrow:

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 :transpi:

Pour les static c'etait prévu ( bien vue :arrow: ), 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 :arrow:

Merci

Lien vers le commentaire
Partager sur d’autres sites

Je crois que tu viens de tuer par arrêt cardiaque tous les experts java du forum... :D

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 ? :D

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 :bravo:

Pour les static c'etait prévu ( bien vue :transpi: ), 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 :transpi:

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. ;)

ça c'est le gros avantage de séparer les objets.

Spareil pour rajouter un SGBD , vraiment très agréable.

Lien vers le commentaire
Partager sur d’autres sites

Archivé

Ce sujet est désormais archivé et ne peut plus recevoir de nouvelles réponses.

×
×
  • Créer...