嗨朋友们,这个问题让我疯了2天。我将非常感谢你的建议或想法。让我详细描述一下这个问题。
目前,我正在Android上开发Walkie-talkie或push to talk应用程序。默认情况下,每个设备都通过套接字连接到服务器,并且如果有任何从服务器发送的音频流要播放,则正在侦听。稍后,如果用户按下通话按钮,则应用程序停止收听输入音频流并开始记录用户说话的内容并同时将记录的音频流数据传送到通过套接字连接在台式计算机上运行的远程服务器( tcp socket)。一旦服务器收到音频流数据,它就会将这些数据传输到其余的Android设备,并通过套接字连接到它。因此,如果我们有3个人正在使用这个应用程序,其中一个人按下谈话按钮进行交谈,那么其他人将听到他说话的内容。下面是我的Android客户端代码:
public class MainActivity extends AppCompatActivity {
private Button startButton,stopButton;
public byte[] buffer;
public static DatagramSocket s_socket;
public static DatagramSocket r_socket;
private int port=50005;
private int r_port = 50006;
AudioRecord recorder;
AudioTrack audioTrack;
private int sampleRate = 16000 ; // 44100 for music
private int channelConfig = AudioFormat.CHANNEL_IN_MONO;
private int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
int minBufSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat);
private boolean s_status = true;
private boolean r_status = true;
Thread r_Thread;
Thread s_Thread;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startButton = (Button) findViewById (R.id.start_button);
startButton.setOnTouchListener (talkListener);
startReceiving();
}
private final View.OnTouchListener talkListener = new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch(event.getAction()) {
case MotionEvent.ACTION_DOWN:
startButton.setBackgroundColor(Color.parseColor("#0670c0"));
try {
startStreaming();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return true; // if you want to handle the touch event
case MotionEvent.ACTION_UP:
startButton.setBackgroundColor(Color.parseColor("#353535"));
try {
stopStreaming();
startReceiving();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return true; // if you want to handle the touch event
}
return false;
}
};
int c=0;
Socket socket;
InetSocketAddress address;
int timeout = 6000;
public void startStreaming() throws InterruptedException, IOException {
r_status= false;
buffer = new byte[minBufSize];
s_Thread = new Thread(new Runnable() {
@Override
public void run() {
if(socket==null) {
socket = new Socket();
address = new InetSocketAddress("192.168.0.2", port);
try {
socket.setReuseAddress(true);
socket.connect(address, timeout);
} catch (Exception e) {
e.printStackTrace();
}
}
recorder = new AudioRecord(MediaRecorder.AudioSource.MIC, sampleRate, channelConfig, audioFormat, minBufSize * 10);
try {
recorder.startRecording();
} catch (IllegalStateException e) {
e.printStackTrace();
return;
}
s_status = true;
OutputStream os = null;
while (s_status == true) {
//reading data from MIC into buffer
recorder.read(buffer, 0, buffer.length);
try {
os = socket.getOutputStream();
os.write(buffer, 0, minBufSize);
}catch (Exception e){
e.printStackTrace();
s_status = false;
}
System.out.println("streaming out: " + buffer.length + "fff"+c++);
}
try {
os.write(new byte[0]);
os.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
});
s_Thread.start();
}
public void stopStreaming() throws InterruptedException, IOException {
s_status = false;
r_status = true;
if(recorder!=null) {
recorder.stop();
recorder.release();
recorder = null;
}
}
public void startReceiving() {
r_Thread = new Thread(new Runnable() {
@Override
public void run() {
InputStream is = null;
while (r_status) {
try {
if(socket==null) {
socket = new Socket();
address = new InetSocketAddress("192.168.0.2", port);
try {
socket.setReuseAddress(true);
socket.connect(address, timeout);
System.out.println("connecting-");
} catch (IOException e) {
e.printStackTrace();
}
}
is = socket.getInputStream();
byte[] buffer = new byte[minBufSize];
is.read(buffer);
if (audioTrack == null) {
audioTrack = new AudioTrack(AudioManager.STREAM_VOICE_CALL, sampleRate, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, buffer.length, AudioTrack.MODE_STREAM);
audioTrack.play();
}
audioTrack.write(buffer, 0, buffer.length);
System.out.println("receiving: " + buffer.length + "fff"+c++);
} catch (IOException e) {
e.printStackTrace();
}
}
try {
is.reset();
} catch (IOException e) {
e.printStackTrace();
}
}
});
r_Thread.start();
}
}
服务器代码:
public class TcpServerSocket implements Runnable {
static AudioFormat format;
static boolean status = true;
static int port = 50005;
static int outputPort = 50006;
static int sampleRate = 16000;
static int count = 0;
static Thread playSound;
public static ArrayList<String> addressList;
public static ArrayList<Socket> sockets;
Socket csocket;
public static boolean isListenning = true;
public boolean isRunning = true;
public TcpServerSocket(Socket cScoket){
this.csocket = cScoket;
}
public static void main(String args[]) throws Exception {
ServerSocket serverSocket = new ServerSocket(port);
sockets = new ArrayList<Socket>();
while(isListenning){
Socket socket = serverSocket.accept();
sockets.add(socket);
System.out.println("new connection");
new Thread(new TcpServerSocket(socket)).start();
}
serverSocket.close();
}
@Override
public void run() {
byte[] receiveData = new byte[1280];
format = new AudioFormat(sampleRate, 16, 1, true, false);
InputStream baiss = null;
OutputStream os;
while (isRunning){
try {
baiss = csocket.getInputStream();
if(!csocket.isConnected()){
isRunning = false;
sockets.remove(csocket);
System.out.println("close socket");
}
baiss.read(receiveData);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
int socketsLen = sockets.size();
for(int i = 0;i<socketsLen;i++){
Socket client = sockets.get(i);
if(!client.equals(csocket)){
try {
os = client.getOutputStream();
os.write(receiveData,0,1280);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
System.out.println(csocket.getInetAddress().toString());
}
try {
baiss.close();
csocket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
一直困扰着我的奇怪问题是:例如,我们需要2部电话,电话A和电话B.如果我只是按下电话A上的谈话按钮进行通话,那么电话B就会听到我的声音,即使我松开按钮并再次按下再说话,声音也很完美。但是,如果电话B按下通话按钮与电话A通话,那么当下次电话B从电话A接收音频时,声音是打破了很多噪音。根据我到目前为止所知,导致此问题的原因可能与套接字有关。如果我每次开始讲话或开始接收时都会创建一个全新的插座,那么音质总是很好。我真的无法解释原因。但这种方法效率不高,浪费了大量的硬件资源。套接字是应该是可重复使用的。你们能否给我一些解决这个问题的想法。