2010-09-26 12 views
8

Im tratando de crear un servidor y cliente de Speex Voip. Tengo los fundamentos abajo y está funcionando bien en la máquina local a través de UDP. Estoy usando JSpeex para la portabilidad. Estoy buscando consejos sobre cómo crear el cliente y el servidor. ¿Cuáles son tus pensamientos?Ayuda para crear un servidor y cliente de Speex Voip

La biblioteca JSpeex solo puede codificar 320 bytes por llamada, por lo que los paquetes enviados al servidor son pequeños (en mi caso ~ 244 bytes). ¿Sería mejor para el cliente esperar hasta que estén listos 1 o 2 KB de datos codificados antes de enviarlos o dejar que el servidor maneje el almacenamiento en búfer de los paquetes?

Además, cualquier ayuda sobre cómo implementar el almacenamiento en búfer de los datos sería agradable.

Algunas de las cosas que tengo funcionan en la máquina local.

Cliente:

public void run() { 
    int nBytesToRead = (m_inputAudioFormat.getFrameSize() * 160); 
    int nAvailable = 0; 
    byte[] abPCMData = new byte[nBytesToRead]; 
    byte[] abSpeexData = null; 
    UserSpeexPacket userSpeexPacket = new UserSpeexPacket("Xiphias3", "TheLounge", null, 0); 

    while (m_captureThread != null) { 
     nAvailable = m_line.available(); 
     if (nAvailable >= nBytesToRead) { 
      int nBytesRead = m_line.read(abPCMData, 0, nBytesToRead); 
      if (nBytesRead == -1) break; 
      if (nBytesRead < nBytesToRead) 
       Arrays.fill(abPCMData, nBytesRead, abPCMData.length, (byte) 0); 
      abSpeexData = createSpeexPacketFromPCM(abPCMData, 0, abPCMData.length); 
      //DatagramPacket packet = new DatagramPacket(abSpeexData, 0, abSpeexData.length, m_connection.getInetAddress(), m_nServerPort); 
      userSpeexPacket.setSpeexData(abSpeexData); 
      userSpeexPacket.incrementPacketNumber(); 
      DatagramPacket packet = UserSpeexPacket.userSpeexPacketToDatagramPacket(m_connection.getInetAddress(), m_connection.getPort(), userSpeexPacket); 
      try { 
       m_connection.send(packet); 
      } 
      catch(IOException iox) { 
       System.out.println("Connection to server lost: " + iox.getMessage()); 
       break; 
      } 
     } 
    } 
    closeLine(); 
    disconnect(); 
} 

public byte[] createSpeexPacketFromPCM(byte[] abPCMData, int nOffset, int nLength) 
{ 
    byte[] abEncodedData = null; 
    m_speexEncoder.processData(abPCMData, nOffset, nLength); 
    abEncodedData = new byte[m_speexEncoder.getProcessedDataByteSize()]; 
    m_speexEncoder.getProcessedData(abEncodedData, 0); 
    return abEncodedData; 
} 

Servidor:

DatagramPacket packet = new DatagramPacket(new byte[2048], 0, 2048); 
    byte[] abPCMData = null; 
    long lPrevVolPrintTime = 0; 

    while (m_bServerRunning) { 
     try { 
      m_serverSocket.receive(packet); 
      //System.out.println("Packet size is " + packet.getData().length); 
      //System.out.println("Got packet from " + packet.getAddress().getHostAddress()); 
      //abPCMData = decodeSpeexPacket(packet.getData(), 0, packet.getLength()); 
      UserSpeexPacket usp = UserSpeexPacket.datagramPacketToUserSpeexPacket(packet); 
      abPCMData = decodeSpeexPacket(usp.getSpeexData(), 0, usp.getSpeexData().length); 
      m_srcDataLine.write(abPCMData, 0, abPCMData.length); 

      if (System.currentTimeMillis() >= (lPrevVolPrintTime + 500)) { 
       //System.out.println("Current volume: " + AudioUtil.getVolumeLevelForPCM22050Hz16Bit1Channel(abPCMData, 0, abPCMData.length)); 
       lPrevVolPrintTime = System.currentTimeMillis(); 
      } 
     } 
     catch (IOException iox) { 
      if (m_bServerRunning) { 
       System.out.println("Server socket broke: " + iox.getMessage()); 
       stopServer(); 
      } 
     } 
    } 

Respuesta

5

estoy trabajando en un proyecto similar. De todo lo que he leído y mi experiencia personal, su mejor opción es trabajar con pequeños datos y enviarlos tan pronto como sea posible. Desea que se realice cualquier almacenamiento en memoria intermedia de fluctuación en el costado del receptor.

Es típico que una aplicación de VoIP envíe 50-100 paquetes por segundo. Para la codificación uLaw a 8000Hz, esto daría como resultado un tamaño de paquete de 80-160 bytes. El razonamiento para esto es que algunos paquetes se eliminarán inevitablemente, y usted desea que el impacto en el receptor sea lo más pequeño posible. Entonces, con 10ms o 20ms de datos de audio por paquete, un paquete descartado puede ocasionar un pequeño contratiempo, pero no tanto como perder 2k de datos de audio (~ 250ms).

Además, con un tamaño de paquete grande, debe acumular todos los datos en el remitente antes de enviarlo. Entonces, dada una latencia de red típica de 50 ms, con 20 ms de datos de audio por paquete, el receptor no escuchará lo que el emisor dice por un mínimo de 70 ms. Ahora imagine lo que sucede cuando se envían 250ms de audio a la vez. 270ms transcurrirán entre el emisor que habla y el receptor que reproduce ese audio.

Los usuarios parecen ser más indulgentes con la pérdida de paquetes aquí y allá, lo que resulta en una calidad de audio inferior, porque la calidad de audio de la mayoría de los teléfonos no es tan buena para empezar. Sin embargo, los usuarios también están acostumbrados a la latencia muy baja en los circuitos telefónicos modernos, por lo que introducir un retraso de ida y vuelta de incluso 250 ms puede ser extremadamente frustrante.

Ahora, en cuanto a implementar el almacenamiento en búfer, he encontrado una buena estrategia para usar una cola (whoops, usando .NET aquí :)), y luego envolverla en una clase que rastrea el número mínimo y máximo de paquetes deseados en la cola. Use un bloqueo riguroso ya que lo más probable es que esté accediendo desde varios hilos. Si la cola "toca fondo" y tiene cero paquetes (desbordamiento del búfer), establezca un indicador y devuelva nulo hasta que el conteo de paquetes alcance el mínimo deseado. Sin embargo, su consumidor tendrá que verificar si se devuelve un valor nulo y no poner nada en el búfer de salida. Alternativamente, su consumidor podría hacer un seguimiento del último paquete y encalarlo repetidamente, lo que puede provocar un audio en bucle, pero en algunos casos eso "suena" mejor que el silencio. Deberá hacer esto hasta que el productor ponga suficientes paquetes en la cola para alcanzar el mínimo. Esto dará como resultado un período de silencio más largo para el usuario, pero en general es mejor aceptado que períodos de silencio cortos y frecuentes (picado). Si obtiene una ráfaga de paquetes y el productor llena la cola (alcanzando el máximo deseado), puede comenzar ignorando los paquetes nuevos o dejar caer suficientes paquetes fuera de la cola para volver al mínimo.

Sin embargo, la elección de esos valores mín/máx es difícil.Está tratando de equilibrar el audio sin problemas (sin errores) con una latencia mínima entre el emisor y el receptor. ¡VoIP es divertido, pero seguro que puede ser frustrante! ¡Buena suerte!

+0

Gracias por su contribución. ¡Me gusta! – Xiphias3

Cuestiones relacionadas