直播音频流java

我正在实现从MIC到另一台PC上的java服务器的实时流媒体。 但我只听到白噪声。

我已经附加了客户端和服务器程序

Client: import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.SocketException; import java.net.UnknownHostException; import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.DataLine; import javax.sound.sampled.LineUnavailableException; import javax.sound.sampled.TargetDataLine; public class Mic { public byte[] buffer; private int port; static AudioInputStream ais; public static void main(String[] args) { TargetDataLine line; DatagramPacket dgp; AudioFormat.Encoding encoding = AudioFormat.Encoding.PCM_SIGNED; float rate = 44100.0f; int channels = 2; int sampleSize = 16; boolean bigEndian = true; InetAddress addr; AudioFormat format = new AudioFormat(encoding, rate, sampleSize, channels, (sampleSize / 8) * channels, rate, bigEndian); DataLine.Info info = new DataLine.Info(TargetDataLine.class, format); if (!AudioSystem.isLineSupported(info)) { System.out.println("Line matching " + info + " not supported."); return; } try { line = (TargetDataLine) AudioSystem.getLine(info); int buffsize = line.getBufferSize()/5; buffsize += 512; line.open(format); line.start(); int numBytesRead; byte[] data = new byte[buffsize]; addr = InetAddress.getByName("127.0.0.1"); DatagramSocket socket = new DatagramSocket(); while (true) { // Read the next chunk of data from the TargetDataLine. numBytesRead = line.read(data, 0, data.length); // Save this chunk of data. dgp = new DatagramPacket (data,data.length,addr,50005); socket.send(dgp); } }catch (LineUnavailableException e) { e.printStackTrace(); }catch (UnknownHostException e) { // TODO: handle exception } catch (SocketException e) { // TODO: handle exception } catch (IOException e2) { // TODO: handle exception } } } 

而服务器端没有问题。 它与Android客户端AudioRecord完美运行。

 Server: import java.io.ByteArrayInputStream; import java.net.DatagramPacket; import java.net.DatagramSocket; import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.DataLine; import javax.sound.sampled.SourceDataLine; public class Server { AudioInputStream audioInputStream; static AudioInputStream ais; static AudioFormat format; static boolean status = true; static int port = 50005; static int sampleRate = 44100; static DataLine.Info dataLineInfo; static SourceDataLine sourceDataLine; public static void main(String args[]) throws Exception { System.out.println("Server started at port:"+port); DatagramSocket serverSocket = new DatagramSocket(port); /** * Formula for lag = (byte_size/sample_rate)*2 * Byte size 9728 will produce ~ 0.45 seconds of lag. Voice slightly broken. * Byte size 1400 will produce ~ 0.06 seconds of lag. Voice extremely broken. * Byte size 4000 will produce ~ 0.18 seconds of lag. Voice slightly more broken then 9728. */ byte[] receiveData = new byte[4096]; format = new AudioFormat(sampleRate, 16, 1, true, false); dataLineInfo = new DataLine.Info(SourceDataLine.class, format); sourceDataLine = (SourceDataLine) AudioSystem.getLine(dataLineInfo); sourceDataLine.open(format); sourceDataLine.start(); //FloatControl volumeControl = (FloatControl) sourceDataLine.getControl(FloatControl.Type.MASTER_GAIN); //volumeControl.setValue(1.00f); DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length); ByteArrayInputStream baiss = new ByteArrayInputStream(receivePacket.getData()); while (status == true) { serverSocket.receive(receivePacket); ais = new AudioInputStream(baiss, format, receivePacket.getLength()); toSpeaker(receivePacket.getData()); } sourceDataLine.drain(); sourceDataLine.close(); } public static void toSpeaker(byte soundbytes[]) { try { System.out.println("At the speaker"); sourceDataLine.write(soundbytes, 0, soundbytes.length); } catch (Exception e) { System.out.println("Not working in speakers..."); e.printStackTrace(); } } } 

所以,我用正弦波填充麦克风(或某些模糊的东西,类似于正弦波),你的程序运行正常。

因此我的具体变化是:

 package audioclient; import java.io.*; import java.net.*; import java.nio.ByteBuffer; import javax.sound.sampled.*; public class Mic { public byte[] buffer; private int port; static AudioInputStream ais; public static void main(String[] args) { TargetDataLine line; DatagramPacket dgp; AudioFormat.Encoding encoding = AudioFormat.Encoding.PCM_SIGNED; float rate = 44100.0f; int channels = 2; int sampleSize = 16; boolean bigEndian = true; InetAddress addr; AudioFormat format = new AudioFormat(encoding, rate, sampleSize, channels, (sampleSize / 8) * channels, rate, bigEndian); DataLine.Info info = new DataLine.Info(TargetDataLine.class, format); if (!AudioSystem.isLineSupported(info)) { System.out.println("Line matching " + info + " not supported."); return; } try { line = (TargetDataLine) AudioSystem.getLine(info); //TOTALLY missed this. int buffsize = line.getBufferSize() / 5; buffsize += 512; line.open(format); line.start(); int numBytesRead; byte[] data = new byte[buffsize]; /* * MICK's injection: We have a buffsize of 512; it is best if the frequency * evenly fits into this (avoid skips, bumps, and pops). Additionally, 44100 Hz, * with two channels and two bytes per sample. That's four bytes; divide * 512 by it, you have 128. * * 128 samples, 44100 per second; that's a minimum of 344 samples, or 172 Hz. * Well within hearing range; slight skip from the uneven division. Maybe * bump it up to 689 Hz. * * That's a sine wave of shorts, repeated twice for two channels, with a * wavelength of 32 samples. * * Note: Changed my mind, ignore specific numbers above. * */ { final int λ = 16; ByteBuffer buffer = ByteBuffer.allocate(λ * 2 * 8); for(int j = 0; j < 2; j++) { for(double i = 0.0; i < λ; i++) { System.out.println(j + " " + i); //once for each sample buffer.putShort((short)(Math.sin(Math.PI * (λ/i)) * Short.MAX_VALUE)); buffer.putShort((short)(Math.sin(Math.PI * (λ/i)) * Short.MAX_VALUE)); } } data = buffer.array(); } addr = InetAddress.getByName("127.0.0.1"); try(DatagramSocket socket = new DatagramSocket()) { while (true) { for(byte b : data) System.out.print(b + " "); // Read the next chunk of data from the TargetDataLine. // numBytesRead = line.read(data, 0, data.length); for(int i = 0; i < 64; i++) { byte b = data[i]; System.out.print(b + " "); } System.out.println(); // Save this chunk of data. dgp = new DatagramPacket(data, data.length, addr, 50005); for(int i = 0; i < 64; i++) { byte b = dgp.getData()[i]; System.out.print(b + " "); } System.out.println(); socket.send(dgp); } } } catch (LineUnavailableException e) { e.printStackTrace(); } catch (UnknownHostException e) { // TODO: handle exception } catch (SocketException e) { // TODO: handle exception } catch (IOException e2) { // TODO: handle exception } } } 

显然我把它误解为一个512字节长的部分并且使正弦波拙劣,但问题是,它产生的声音恰好是它的意思 - 在一个特定的音高上发出令人头疼的嘎嘎声。

考虑到这一点,我不怀疑代码中明确存在问题。 我要检查的第一件事是你的系统正在为音频点击哪一行。 你有多个麦克风连接? 可能是网络摄像头麦克风? 您可以使用PulseAudio Volume Control之类的实用程序来检查。 如果您尚未检查麦克风的function,也可以这样做; 他们确实有生命。

在音频流中加扰比特并不罕见,也不困难; 但是我没有看到你可以做到的任何地方。

一种想法可能是修改程序以尝试在本地播放声音,然后再将其发送到服务器。 这样,您至少可以确定问题是在麦克风之前还是之后。

当客户端和服务器使用不同大小的数据缓冲区时,将被截断并可能导致一个或两个产生伪像。

您的服务器缓冲区大小设置为byte[] receiveData = new byte[4096];

您的客户端缓冲区大小由于某种原因是动态的,并设置为byte[] data = new byte[buffsize];

将客户端缓冲区大小设置为静态4096以匹配服务器: byte[] data = new byte[4096];

或者只是确保它们的尺寸相同……

我建议您先在文件中写入客户端上录制的音频。 这将使您能够validation捕获的音频是否正常。 您可以使用sox等实用程序将PCM转换为WAV。

在客户端和服务器上匹配音频格式很重要,例如将Client.java中的音频格式更改为: format = new AudioFormat(sampleRate, 16, 1, true, false); 您还需要在两个程序上使用相同的缓冲区大小。

所以这是一个老问题,但解决这个问题对我有所帮助,我想我发现的东西可能对其他人有所帮助……这就是我解决你所描述的问题的方法:

在我的机器上,改变

 boolean bigEndian = true; 

 boolean bigEndian = false; 

解决了白噪声问题(这显然是字节顺序问题)

如果这是您所做的唯一更改,则产生的音频将具有低音调,这是因为在Mic侧您收集2个声道,而在扬声器侧您通过一个声道播放。

要解决这个问题,只需更改此行:

 format = new AudioFormat(sampleRate, 16, 1, true, false); 

 format = new AudioFormat(sampleRate, 16, 2, true, false); 

然后音频应该清晰易懂