通过WiFi在Android手机之间流式传输语音
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了通过WiFi在Android手机之间流式传输语音相关的知识,希望对你有一定的参考价值。
我正试图通过WiFi将音频从1个android流式传输到另一个Android。在查看一些示例后,我制作了2个应用程序,每个应用程序中只有一个活动,1个用于捕获和发送音频,另一个用于接收。
我使用了Audiorecord和Audiotrack课程来捕捉和播放。然而,我只是听到一些噼里啪啦的声音(虽然我回复后我做了一些改变后现在停止了)
发送语音的活动。
public class VoiceSenderActivity extends Activity {
private EditText target;
private TextView streamingLabel;
private Button startButton,stopButton;
public byte[] buffer;
public static DatagramSocket socket;
private int port=50005; //which port??
AudioRecord recorder;
//Audio Configuration.
private int sampleRate = 8000; //How much will be ideal?
private int channelConfig = AudioFormat.CHANNEL_CONFIGURATION_MONO;
private int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
private boolean status = true;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
target = (EditText) findViewById (R.id.target_IP);
streamingLabel = (TextView) findViewById(R.id.streaming_label);
startButton = (Button) findViewById (R.id.start_button);
stopButton = (Button) findViewById (R.id.stop_button);
streamingLabel.setText("Press Start! to begin");
startButton.setOnClickListener (startListener);
stopButton.setOnClickListener (stopListener);
}
private final OnClickListener stopListener = new OnClickListener() {
@Override
public void onClick(View arg0) {
status = false;
recorder.release();
Log.d("VS","Recorder released");
}
};
private final OnClickListener startListener = new OnClickListener() {
@Override
public void onClick(View arg0) {
status = true;
startStreaming();
}
};
public void startStreaming() {
Thread streamThread = new Thread(new Runnable() {
@Override
public void run() {
try {
int minBufSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat);
DatagramSocket socket = new DatagramSocket();
Log.d("VS", "Socket Created");
byte[] buffer = new byte[minBufSize];
Log.d("VS","Buffer created of size " + minBufSize);
DatagramPacket packet;
final InetAddress destination = InetAddress.getByName(target.getText().toString());
Log.d("VS", "Address retrieved");
recorder = new AudioRecord(MediaRecorder.Audiosource.MIC,sampleRate,channelConfig,audioFormat,minBufSize);
Log.d("VS", "Recorder initialized");
recorder.startRecording();
while(status == true) {
//reading data from MIC into buffer
minBufSize = recorder.read(buffer, 0, buffer.length);
//putting buffer in the packet
packet = new DatagramPacket (buffer,buffer.length,destination,port);
socket.send(packet);
}
} catch(UnknownHostException e) {
Log.e("VS", "UnknownHostException");
} catch (IOException e) {
Log.e("VS", "IOException");
}
}
});
streamThread.start();
}
}
接收语音的活动
public class VoiceReceiverActivity extends Activity {
private Button receiveButton,stopButton;
public static DatagramSocket socket;
private AudioTrack speaker;
//Audio Configuration.
private int sampleRate = 8000; //How much will be ideal?
private int channelConfig = AudioFormat.CHANNEL_CONFIGURATION_MONO;
private int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
private boolean status = true;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
receiveButton = (Button) findViewById (R.id.receive_button);
stopButton = (Button) findViewById (R.id.stop_button);
findViewById(R.id.receive_label);
receiveButton.setOnClickListener(receiveListener);
stopButton.setOnClickListener(stopListener);
}
private final OnClickListener stopListener = new OnClickListener() {
@Override
public void onClick(View v) {
status = false;
speaker.release();
Log.d("VR","Speaker released");
}
};
private final OnClickListener receiveListener = new OnClickListener() {
@Override
public void onClick(View arg0) {
status = true;
startReceiving();
}
};
public void startReceiving() {
Thread receiveThread = new Thread (new Runnable() {
@Override
public void run() {
try {
DatagramSocket socket = new DatagramSocket(50005);
Log.d("VR", "Socket Created");
byte[] buffer = new byte[256];
//minimum buffer size. need to be careful. might cause problems. try setting manually if any problems faced
int minBufSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat);
speaker = new AudioTrack(AudioManager.STREAM_MUSIC,sampleRate,channelConfig,audioFormat,minBufSize,AudioTrack.MODE_STREAM);
speaker.play();
while(status == true) {
try {
DatagramPacket packet = new DatagramPacket(buffer,buffer.length);
socket.receive(packet);
Log.d("VR", "Packet Received");
//reading content from packet
buffer=packet.getData();
Log.d("VR", "Packet data read into buffer");
//sending data to the Audiotrack obj i.e. speaker
speaker.write(buffer, 0, minBufSize);
Log.d("VR", "Writing buffer content to speaker");
} catch(IOException e) {
Log.e("VR","IOException");
}
}
} catch (SocketException e) {
Log.e("VR", "SocketException");
}
}
});
receiveThread.start();
}
}
我使用wireshark检查数据包是否正在发送,我可以看到数据包。然而,源是发送设备和目的地的MAC地址,也就像物理地址。不确定这是否相关。
那有什么问题呢?
嘿,有一个名为“Libstreaming”的开源库,用于通过网络使用WIFI传输语音/视频。看看吧:
https://github.com/fyhertz/libstreaming
还提供了一些示例,请仔细看看:
https://github.com/fyhertz/libstreaming-examples
我已经使用该库通过网络流式传输RTSP音频,希望它可能有用。
我试着将问题分成三部分。
第1部分
通过注释与音频相关的所有内容,确保套接字连接正常工作
第2部分
只发送来自发件人的任意文本消息[Hello WiFi],然后在接收方应用程序中接收和打印它。
第3部分
录像机是否实际工作?尝试在单独的项目中测试您的录制方式,看它是否正常工作。
使用this代码捕获麦克风并播放它。
我的经验
我曾经在一个类似的项目上工作并测试它,我做的是在录制之后我将录制的音频数据作为文件写在SD卡上
(这将是原始音频,因此大多数音乐播放器将无法播放它... mPlayer应该播放它我想)
您需要仔细考虑使用UDP(DatagramSocket类)作为您的网络协议。
UDP是一种轻量级协议,不保证维护接收数据包的顺序。这可能是音频乱码的部分原因。无序接收的数据包将导致无序播放音频数据包。在这些无序数据包的边界处,您将听到音频样本实际损坏的点击/弹出。除此之外,不保证UDP数据包成功传送。任何丢弃的数据包显然会增加任何听到的乱码或失真。
TCP(套接字类)将是获得最佳音频质量的更好选择。 TCP是一种更强大的协议,可以保持接收数据包的顺序。它还具有内置错误检查功能,并将重新发送任何丢弃的数据包。但是,由于这种注意功能,TCP具有更高的网络开销。
我开始回答说你需要仔细考虑你使用的协议。这是因为有一种情况可以根据对你重要的事情来使用。
如果你想要超低延迟播放,但很高兴牺牲音频质量,那么UDP就能工作。但是,需要进行一些实验才能找到最佳的缓冲液和样品量。
如果你想要最好的音频重新制作,零失真,但很乐意引入稍多的延迟,那么TCP就是可行的路线。
我不能说TCP会增加多少延迟。但它有可能在不影响用户体验的情况下实施。找出答案的唯一方法是尝试并看到。
以上是关于通过WiFi在Android手机之间流式传输语音的主要内容,如果未能解决你的问题,请参考以下文章
在 Android 上使用 OpenSL ES 通过套接字通信流式传输 MP3 音频