(四)Android基于UDP的多客戶端語音通信

轉(zhuǎn)載請表明出處:https://blog.csdn.net/stormxiaofeng/article/details/80598126

在前三篇得基礎(chǔ)上,這次研究了組播功能。非常感謝https://blog.csdn.net/jspping/article/details/64438515得貢獻(xiàn)!

組播也就是通過MulticastSocket來進(jìn)行開發(fā),與DatagramSocket比較相類似,這次依然是用兩個線程進(jìn)行實現(xiàn),發(fā)送線程MultiSendThread和接收線程MultiReceiveThread。廢話不多說,開始碼:

(一)MultiSendThread:

(1)初始化MuticastSocket


// 偵聽的端口

try {

? ? multicastSocket = new MulticastSocket(8082);

? ? // 使用D類地址,該地址為發(fā)起組播的那個ip段,即偵聽10001的套接字

? ? address = InetAddress.getByName("239.0.0.1");

} catch (IOException e) {

? ? e.printStackTrace();

}

(2)初始化AudioRecord

protected LinkedList<byte[]> mRecordQueue;

int minBufferSize;

private static AcousticEchoCanceler aec;

private static AutomaticGainControl agc;

private static NoiseSuppressor nc;

AudioRecord audioRec;

byte[] buffer;

@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)

private void initAudio() {

? ? //播放的采樣頻率 和錄制的采樣頻率一樣

? ? int sampleRate = 44100;

? ? //和錄制的一樣的

? ? int audioFormat = AudioFormat.ENCODING_PCM_16BIT;

? ? //錄音用輸入單聲道? 播放用輸出單聲道

? ? int channelConfig = AudioFormat.CHANNEL_IN_MONO;

? ? minBufferSize = AudioRecord.getMinBufferSize(

? ? ? ? ? ? sampleRate,

? ? ? ? ? ? channelConfig, AudioFormat.ENCODING_PCM_16BIT);

? ? System.out.println("****RecordMinBufferSize = " + minBufferSize);

? ? audioRec = new AudioRecord(

? ? ? ? ? ? MediaRecorder.AudioSource.MIC,

? ? ? ? ? ? sampleRate,

? ? ? ? ? ? channelConfig,

? ? ? ? ? ? audioFormat,

? ? ? ? ? ? minBufferSize);

? ? buffer = new byte[minBufferSize];

? ? if (audioRec == null) {

? ? ? ? return;

? ? }

? ? //聲學(xué)回聲消除器 AcousticEchoCanceler 消除了從遠(yuǎn)程捕捉到音頻信號上的信號的作用

? ? if (AcousticEchoCanceler.isAvailable()) {

? ? ? ? aec = AcousticEchoCanceler.create(audioRec.getAudioSessionId());

? ? ? ? if (aec != null) {

? ? ? ? ? ? aec.setEnabled(true);

? ? ? ? }

? ? }

? ? //自動增益控制 AutomaticGainControl 自動恢復(fù)正常捕獲的信號輸出

? ? if (AutomaticGainControl.isAvailable()) {

? ? ? ? agc = AutomaticGainControl.create(audioRec.getAudioSessionId());

? ? ? ? if (agc != null) {

? ? ? ? ? ? agc.setEnabled(true);

? ? ? ? }

? ? }

? ? //噪聲抑制器 NoiseSuppressor 可以消除被捕獲信號的背景噪音

? ? if (NoiseSuppressor.isAvailable()) {

? ? ? ? nc = NoiseSuppressor.create(audioRec.getAudioSessionId());

? ? ? ? if (nc != null) {

? ? ? ? ? ? nc.setEnabled(true);

? ? ? ? }

? ? }

? ? mRecordQueue = new LinkedList<byte[]>();

}

(3)開始錄制,并實時發(fā)送出去

@Override

public void run() {

? ? if (multicastSocket == null)

? ? ? ? return;

? ? try {

? ? ? ? audioRec.startRecording();

? ? ? ? while (true) {

? ? ? ? ? ? try {

? ? ? ? ? ? ? ? byte[] bytes_pkg = buffer.clone();

? ? ? ? ? ? ? ? if (mRecordQueue.size() >= 2) {

? ? ? ? ? ? ? ? ? ? int length = audioRec.read(buffer, 0, minBufferSize);

? ? ? ? ? ? ? ? ? ? // 組報

? ? ? ? ? ? ? ? ? ? DatagramPacket datagramPacket = new DatagramPacket(buffer, length);

? ? ? ? ? ? ? ? ? ? // 向組播ID,即接收group /239.0.0.1? 端口 10001

? ? ? ? ? ? ? ? ? ? datagramPacket.setAddress(address);

? ? ? ? ? ? ? ? ? ? // 發(fā)送的端口號

? ? ? ? ? ? ? ? ? ? datagramPacket.setPort(10001);

? ? ? ? ? ? ? ? ? ? System.out.println("AudioRTwritePacket = " + datagramPacket.getData().toString());

? ? ? ? ? ? ? ? ? ? multicastSocket.send(datagramPacket);

? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? mRecordQueue.add(bytes_pkg);

? ? ? ? ? ? } catch (Exception e) {

? ? ? ? ? ? ? ? // TODO Auto-generated catch block

? ? ? ? ? ? ? ? e.printStackTrace();

? ? ? ? ? ? }

? ? ? ? }

? ? } catch (Exception e) {

? ? ? ? e.printStackTrace();

? ? }

}

(二)MultiReceiveThread

(1)初始化MulticastSocket

// 接收數(shù)據(jù)時需要指定監(jiān)聽的端口號

try {

? ? multicastSocket = new MulticastSocket(10001);

? ? // 創(chuàng)建組播ID地址

? ? InetAddress address = InetAddress.getByName("239.0.0.1");

? ? // 加入地址

? ? multicastSocket.joinGroup(address);

} catch (IOException e) {

? ? e.printStackTrace();

}

(2)初始化AudioTrack

byte[] buffer;

AudioTrack audioTrk;

private void initAudioTracker() {

? ? //揚聲器播放

? ? int streamType = AudioManager.STREAM_MUSIC;

? ? //播放的采樣頻率 和錄制的采樣頻率一樣

? ? int sampleRate = 44100;

? ? //和錄制的一樣的

? ? int audioFormat = AudioFormat.ENCODING_PCM_16BIT;

? ? //流模式

? ? int mode = AudioTrack.MODE_STREAM;

? ? //錄音用輸入單聲道? 播放用輸出單聲道

? ? int channelConfig = AudioFormat.CHANNEL_OUT_MONO;

? ? int recBufSize = AudioTrack.getMinBufferSize(

? ? ? ? ? ? sampleRate,

? ? ? ? ? ? channelConfig,

? ? ? ? ? ? audioFormat);

? ? System.out.println("****playRecBufSize = " + recBufSize);

? ? audioTrk = new AudioTrack(

? ? ? ? ? ? streamType,

? ? ? ? ? ? sampleRate,

? ? ? ? ? ? channelConfig,

? ? ? ? ? ? audioFormat,

? ? ? ? ? ? recBufSize,

? ? ? ? ? ? mode);

? ? audioTrk.setStereoVolume(AudioTrack.getMaxVolume(),

? ? ? ? ? ? AudioTrack.getMaxVolume());

? ? buffer = new byte[recBufSize];

}

(3)開始接收,并進(jìn)行實時播放

@Override

public void run() {

? ? if (multicastSocket == null)

? ? ? ? return;

? ? //從文件流讀數(shù)據(jù)

? ? audioTrk.play();

? ? // 包長

? ? while (true) {

? ? ? ? try {

? ? ? ? ? ? // 數(shù)據(jù)報

? ? ? ? ? ? DatagramPacket datagramPacket = new DatagramPacket(buffer, buffer.length);

? ? ? ? ? ? // 接收數(shù)據(jù),同樣會進(jìn)入阻塞狀態(tài)

? ? ? ? ? ? multicastSocket.receive(datagramPacket);

? ? ? ? ? audioTrk.write(datagramPacket.getData(), 0, datagramPacket.getLength());

? ? ? ? } catch (IOException e) {

? ? ? ? ? ? // TODO Auto-generated catch block

? ? ? ? ? ? e.printStackTrace();

? ? ? ? }

? ? }

}

(三)開始測試

MultiSendThread multiSendThread;

MultiReceiverThread multiReceiverThread;

@OnClick({R.id.btnSend, R.id.btnReceive})

public void onViewClicked(View view) {

? ? switch (view.getId()) {

? ? ? ? case R.id.btnSend:

? ? ? ? ? ? if (multiSendThread == null) {

? ? ? ? ? ? ? ? multiSendThread = new MultiSendThread();

? ? ? ? ? ? }

? ? ? ? ? ? new Thread(multiSendThread).start();

? ? ? ? ? ? break;

? ? ? ? case R.id.btnReceive:

? ? ? ? ? ? if (multiReceiverThread == null) {

? ? ? ? ? ? ? ? multiReceiverThread = new MultiReceiverThread();

? ? ? ? ? ? }

? ? ? ? ? ? new Thread(multiReceiverThread).start();

? ? ? ? ? ? break;

? ? }

}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容