VoIP RTP从/到服务器(在Java中)到/从android流式传输

我的目标是在GSM / UMTS / LTE网络中建立一个即按即说聊天应用程序; 最初我想使用多播地址和点对点而不会使服务器过载; 不幸的是,经过深入调查,我发现GSM / UMTS / LTE网络中不允许多播,因此我必须使用服务器来反弹VoIP数据包。 我不太喜欢这个解决方案,因为我必须重载服务器,但我找不到更好的解决方案。 如果你有一个替代解决方案非常适应…

因此,我必须将VoIP从Android客户端发送到服务器(PC),反之亦然。 服务器是Java,它必须接收VoIP数据包然后将VoIP数据包发送给其他N个客户端; 服务器是VoIP数据包的保护者。

我开发了代码,但它不起作用; 我没有任何错误,只是我有非常糟糕的VoIP服务:我丢失了很多碎片,我听到的是非常多的声音……错误在哪里? 我想它应该在服务器代码中; 服务器只需获取数据包并重新发送它们,而不知道它们是RTP上的VoIP。

如下请见

  • 我用来将VoIP数据包发送到服务器的代码。 它的工作原理是因为当我将它用于将VoIP数据包直接从Android发送到Android的个人呼叫时我没有问题; 在android中接收来自服务器的数据包的代码非常相似,所以我不重新复制它。 如你所见,我使用android.net.rtp。
  • 我在Java服务器上用来反弹VoIP数据包的代码

先谢谢你,福斯托

//使用ANDROID代码向服务器发送VOIP

//Attribute definition private static final AudioCodec myAudioCodec_COSTANTE = AudioCodec.PCMU ; private static final int myAudioGroupTX_COSTANTE = AudioGroup.MODE_NORMAL ; private static final int myAudioGroupRX_COSTANTE = AudioGroup.MODE_NORMAL ; private static final int myRtpStreamTX_COSTANTE = RtpStream.MODE_SEND_ONLY ; private static final int myRtpStreamRX_COSTANTE = RtpStream.MODE_RECEIVE_ONLY ; private static final int myAudioManagerTX_COSTANTE = AudioManager.MODE_IN_COMMUNICATION; private static final int myAudioManagerRX_COSTANTE = AudioManager.MODE_IN_COMMUNICATION; //Method called for VoIP trasmission myAudioStream = new AudioStream(localClientIP); myAudioGroup = new AudioGroup(); myAudioManager = (AudioManager) myContext.getSystemService(Context.AUDIO_SERVICE); myAudioGroup.setMode(myAudioGroupTX_COSTANTE); myAudioStream.join(null); myAudioStream.setCodec(myAudioCodec_COSTANTE); myAudioStream.setMode(myRtpStreamTX_COSTANTE); myAudioStream.associate(ipaddress_Server, port_Server) myAudioStream.join(myAudioGroup); myAudioManager.setMode(myAudioManagerTX_COSTANTE); myAudioManager.setSpeakerphoneOn(false); myAudioManager.setMicrophoneMute(false); 

// JAVA服务器代码用于接收来自Android的VOIP并将其重新发送给服务器

 DatagramSocket datagramSocket_RX_VoIP= new DatagramSocket(); DatagramSocket datagramSocket_TX_VoIP= new DatagramSocket(); int unicast_port_TX_VoIP = 5000 ; String unicast_ip_TX_VoIP = "192.168.0.3"; Thread t = new Thread(new Runnable() { public void run() { try { DatagramPacket myPacket; while (true) { myPacket = ManagePacket.initializePacket(); //Function to prepare the packe ; the problem is not here!!! datagramSocket_RX_VoIP.receive(myPacket); InetAddress ppp = InetAddress.getByName(unicast_ip_TX_VoIP); myPacket.setAddress(ppp); myPacket.setPort( unicast_port_TX_VoIP ) ; datagramSocket_TX_VoIP.send(myPacket); } } catch (Exception ex) { log.debug("Exception: " + ex.getMessage(), ex); } } }); t.start(); 

您没有提供有关您的申请的足够详细信息。 使用任何UDP流应用程序,您需要解决以下问题:

  • 网络抖动和缓冲:当数据包到达时,您无法在收到数据后立即播放音频,因为下一个数据包可能比预期的要晚,并且您的音频播放会有间隙。 到达率的变化称为网络抖动。 在尝试播放之前,您需要缓冲一些数据量。 通常你使用某种环形缓冲区。

  • 数据包丢失:UDP会丢包。 你需要“处理”这个。 如果您发送10个数据包且数据包#4丢失,则无法播放数据包#3,然后播放数据包#5。 听起来不错。 如何处理这个问题:

    • 丢失隐藏:尽量减少丢失数据包的不良影响。 你可以玩寂静(虽然这听起来不是最好的,除非你把它淡化为沉默)。 您可以通过检查周围的数据包生成丢失的音频来“估计”丢失的音频。
    • 前向纠错:多次发送数据包。 有很多方法和方案。 权衡是更高的延迟和更多的网络利用率
  • 无序到达:数据包可能无法到达。 使用RTP序列号来处理这个问题。

音频流不是一项微不足道的任务。 你不能只是打开一个套接字,发送数据,并在另一端播放它,并期望它工作。 您对UDP流的最大担忧是网络抖动和数据包丢失。 如果您不想处理损失而且可能会有一些额外的延迟,请使用TCP,但请确保在开始播放之前缓冲足够的音频。