【发布时间】:2014-06-28 11:55:06
【问题描述】:
我在 cmets 之后更新了问题以使问题更清楚。我正在尝试编写一个用于通过 TCP 进行加密语音呼叫的代码。当然,当涉及到安全连接时,SSL 是一种选择,但现在我正在研究 TCP,就像任何其他项目一样。
该程序旨在捕获来自麦克风的音频,使用 AES 对其进行加密,然后将数据发送到服务器。在服务器端,接收到的数据将被解密并发送到扬声器。但是使用这些代码,我在运行时从客户端收到了 LineUnavailable 异常:
无法打开线路:javax.sound.sampled.LineUnavailableException:线路格式为 PCM_SIGNED 16000.0 Hz,8 位,立体声,2 字节/帧,不支持。
通常情况下,如果没有加密,代码没有问题,而我使用 BufferedOutputStream(s.getOutputStream()) 对象来记录数据;并且声音通过网络成功传输,即我的硬件支持提到的 PCM 格式。
另一方面,在一个简单的独立捕获/播放 java 模块中,我测试了加密代码,我设法在捕获和保存过程之间加密和解密音频数据,而它是一个字节 [] 数据,就在它被保存到输出 wav 文件之前。加密过程本身似乎没有问题。
问题出现在联网过程中,当我使用 ByteArrayOutputStream 写入来自 DataLine 的音频数据时,而不是使用之前直接采用 s.getOutputStream() 方法的 BufferedOutputStream 对象。此处使用 ByteArrayOutputStream 的原因是将捕获的字节作为输入参数传递给加密方法。值得注意的是,ByteArrayOutputStream 运行良好,当周围没有套接字时,将音频字节保存到本地磁盘上的 wav 文件中。
考虑到 TCP 网络,我现在的问题是工作的 OutputStream 对象和等待加密的字节之间的解体。我的最后一个测试是在下面的代码中使用 DataOutput/InputStream 对象的不成功测试。仍然需要任何关于适当的输入/输出流方法的想法来实现成功的通信(如果有的话)。
捕获和发送数据的代码片段是:
public void run() {
try {
dos = new DataOutputStream(s.getOutputStream());// need the exact stream obj.
} catch (IOException ex) {
return;
}
AudioFormat format =new AudioFormat(16000,8,2,true,true);
DataLine.Info info = new DataLine.Info(TargetDataLine.class,format);
if (!AudioSystem.isLineSupported(info)) {
System.err.println("Line matching " + info + " not supported.");// throws the exception
return;
}
try {
line = (TargetDataLine) AudioSystem.getLine(info);
line.open(format, line.getBufferSize());
} catch (LineUnavailableException ex) {
System.err.println("Unable to open the line: " + ex);// related to exception
return;
}
byte[] data = new byte[256];
int numBytesRead=0;
line.start();
// In the nonsecure call version, the audio data is written directly
// to the BufferedOutputStream(s.getOutputStream()) and transmitted without problem
ByteArrayOutputStream caps = new ByteArrayOutputStream(); //?
while (thread != null) {
numBytesRead = line.read(data, 0,128);
try {
caps.write(data, 0, numBytesRead);
} catch (Exception ex) {
System.err.println("Unable to read/write line data: " + ex);
break;
}
}
line.stop();
line.close();
line = null;
try {
caps.flush();
caps.close();
} catch (IOException ex) { ex.printStackTrace(); }
try {
SecretKeySpec skeySpec = CryptUtil.getSecretKeySpec("password12345678","AES",128);
byte[] encrypted = CryptUtil.encrypt(caps.toByteArray(), skeySpec);
dos.writeInt(encrypted.length);
dos.write(encrypted,0,encrypted.length);
} catch (Exception ex) { }
}
接收和播放数据的代码片段是:
public void run() {
AudioFormat format =new AudioFormat(16000,8,2,true,true);
try {
dis = new DataInputStream(s.getInputStream());// need the exact stream obj.
} catch (Exception e) {
System.err.println("Could not get InputStream: " + e);
}
try {
int length = dis.readInt();
byte[] message = new byte[length];
dis.readFully(message);
SecretKeySpec skeySpec = CryptUtil.getSecretKeySpec("password12345678","AES",128);
byte[] audioBytes = CryptUtil.decrypt(message, skeySpec);
ByteArrayInputStream bais = new ByteArrayInputStream(audioBytes);
playStream = new AudioInputStream(bais, format, audioBytes.length / format.getFrameSize());
} catch (Exception e) {
System.err.println("Could not decrypt the stream: " + e);
}
DataLine.Info info = new DataLine.Info(SourceDataLine.class,format);
try {
line = (SourceDataLine) AudioSystem.getLine(info);
line.open(format, bufSize);
} catch (LineUnavailableException ex) {
System.err.println("Unable to open the line: " + ex);
return;
}
byte[] data = new byte[256];
int numBytesRead = 0;
line.start();
while (thread != null) {
try{
numBytesRead = playStream.read(data);
line.write(data, 0,numBytesRead);
} catch (Exception e) {
System.err.println("Error during playback: " + e);
break;
}
}
if (thread != null) {
line.drain();
}
line.stop();
line.close();
line = null;
}
【问题讨论】:
-
哪一方抛出异常:记录和加密的一端,还是解密和播放的一端?
-
录制并发送音频的客户端,抛出异常
-
一旦你加密了音频,它就不再是音频了。
-
为什么不简单地使用 SSL 隧道?
-
我认为这与加密无关;原因是您似乎没有对引发异常的行执行任何加密操作(如果确实是
line = (TargetDataLine) AudioSystem.getLine(info);,请验证)。同时,您的运行时更有可能以某种方式搞砸了。
标签: java aes audio-streaming