Android網絡對講機的實現


  上個星期公司給出了一個項目需求,做一個基於socket通訊協議的網絡對講機。於是在項目開始前計划了一下基本的實現流程。

  1、從手機麥中采集音頻數據;2、將PCM音頻數據編碼壓縮;3、將壓縮好的音頻通過無線網絡發送出去;4、其他手機接收音頻數據並解碼;5、將音頻數據寫入到音軌中播放。項目雖然簡單,但其中的一些小問題也折騰了我不少時間。

  首先我們創建一個線程用來采集音頻數據,通過android提供的AudioRecord可以實時采集音頻流。AudioRecord類在Java應用程序中管理音頻資源,用來記錄從平台音頻輸入設備產生的數據。其實調用AudioRecord很簡單,首先創建AudioRecord對象,AudioRecord會初始化並連接音頻緩沖區,用來緩沖新的音頻數據。根據指定的緩沖區的大小來決定AudioRecord能夠記錄多長的數據。

調用getMinBufferSize(int,int,int)返回最小的緩沖區大小。然后根據得到的最小緩沖區大小來創建AudioRecord對象:

inputMinSize = AudioRecord.getMinBufferSize(8000, 
        AudioFormat.CHANNEL_CONFIGURATION_MONO, 
        AudioFormat.ENCODING_PCM_16BIT);
audioRec = new AudioRecord(MediaRecorder.AudioSource.MIC, 8000, 
        AudioFormat.CHANNEL_CONFIGURATION_MONO,
        AudioFormat.ENCODING_PCM_16BIT, inputMinSize);
參數
  sampleRateInHz         默認采樣率,單位Hz。
  channelConfig          描述音頻通道設置。
  audioFormat              音頻數據保證支持此格式。
 

  AudioRecord初始化工作完畢后啟用錄制線程,並且調用startRecording ()開始進行音頻錄制。調用read(short,int,int)方法從音頻硬件錄制緩沖區讀取數據。拿到音頻數據后,直接通過網絡發送出去是不行的,我們在這里還要做一項工作就是實現音頻壓縮。在網上提供了很多音頻的編碼庫,我們可以將源碼導入到項目中通過android ndk編譯成.so文件,最后通過jni來調用。我這里直接用sipdroid開源項目提供的SILK編解碼庫(下載編碼庫)。

本地方法 encode(short[] lin,int offset,byte[] encoded,int size)
參數                  
  lin        源數據                  
  offset       源數組的起始偏移量                  
  encoded     編碼后的數據      
  size          請求編碼的數據大小返回值   編碼后的數據大小
 

  調用encode(short[], int, byte[], int)壓縮已經采集完畢的音頻數據,我們就可以通過網絡發送出去了。 

  接下來,我們創建一個socket udp實例,為什么這里選擇udp而不是tcp呢?從我們本身的項目需求出發,我們做的這個項目的通訊方式是相互收發數據的,屬於手機與手機兩“客戶端“之間的通訊。並且,在這種音頻通信過程中,我們要傳輸的數據量是比較龐大的,因此采用資源消耗少,處理速度快的UDP協議是合理的。指定發送的端口號,我們將數據封裝成報文發送出去,整個采集發送的過程如下:

class RecordSoundThread extends Thread {
        private boolean flag = true;
        private DatagramSocket mSocket;

        private int inputBufSize = 160;
        short[] inputBytes = new short[1024];
        byte[] encodeBytes = new byte[1024];

        RecordSoundThread() throws SocketException {
            // TODO Auto-generated constructor stub
            mSocket = new DatagramSocket();
        }

        @Override
        public void run() {
            if (mSocket == null)
                return;

            while (flag) {
                if (isSpeakMode) {
                    try {
                        int length = audioRec.read(inputBytes, 0, inputBufSize);

//                        calc(inputBytes, 0, length);

                        length = silk8.encode(inputBytes, 0, encodeBytes,
                                length);

                        DatagramPacket writePacket;
                        if (inetAddress.length() > 0) {
                            InetAddress inet = InetAddress
                                    .getByName(inetAddress);
                            writePacket = new DatagramPacket(encodeBytes,
                                    length, inet, NETPORT);
                            writePacket.setLength(length);
                            mSocket.send(writePacket);
                        }
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
        }
        
        public void close() {
            flag = false;
            if (mSocket != null) {
                mSocket.close();
            }        
        }
    }
View Code

  接下來我們要接收目標機器發送過來的音頻數據了。同樣,創建一個線程用來接收網絡中的音頻數據,並且對音頻數據進行解碼。

本地方法 decode(byte[] encoded, short[] lin, int size)
參數
       encoded     源數據
       lin          解碼后的數據
       size        請求解碼的數據大小
返回值          解碼后的數據大小 

  得到解碼后的PCM音頻流,我們就可以使用AudioTrack將音頻播放出來了。

  AudioTrack類在java應用程序中管理和播放音頻資源,將PCM音頻數據寫入到緩沖區來播放音頻設備。首先創建AudioTrack對象,AudioTrack會初始化並連接音頻緩沖區,根據指定的緩沖區大小來決定audioTrack能夠播放多長的數據。調用getMinBufferSize(int,int,int)返回最小的緩沖區大小。然后根據得到的最小緩沖區大小來創建audioTrack對象:

outputMinSize = AudioTrack.getMinBufferSize(8000,
                AudioFormat.CHANNEL_CONFIGURATION_MONO,
                AudioFormat.ENCODING_PCM_16BIT);
audioTrk = new AudioTrack(AudioManager.STREAM_MUSIC, 8000,
                AudioFormat.CHANNEL_CONFIGURATION_MONO,
                AudioFormat.ENCODING_PCM_16BIT, outputMinSize,
                AudioTrack.MODE_STREAM);

  AudioTrack初始化工作完畢后啟用接收線程,並且調用play()開始播放。調用write(short[],int,int)方法將PCM音頻數據寫入到音頻硬件中。

 

class RecevRecordThread extends Thread {
        private boolean flag = true;
        private DatagramSocket mSocket;

        short[] decodeBytes = new short[1024];
        byte[] outputBytes = new byte[1024];

        RecevRecordThread() throws SocketException {
            // TODO Auto-generated constructor stub
            mSocket = new DatagramSocket(NETPORT);
        }

        @Override
        public void run() {
            if (mSocket == null)
                return;
            audioTrk.play();
            while (flag) {
                DatagramPacket recevPacket;
                try {
                    recevPacket = new DatagramPacket(outputBytes, 0,
                            outputBytes.length);
                    mSocket.receive(recevPacket);

                    int length = recevPacket.getLength();

                    length = silk8.decode(outputBytes, decodeBytes, length);

//                    calc2(decodeBytes, 0, length);

                    audioTrk.write(decodeBytes, 0, length);
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            audioTrk.stop();
        }
        
        public void close() {
            flag = false;
            if (mSocket != null) {
                mSocket.close();
            }        
        }
    }
View Code

   最后,退出應用后別忘了釋放資源。

public void onDestroy() {
  silk8.close();
  recevThread.close();
  recordThread.close();
  audioRec.release();
  audioTrk.release();
}

  好了,網絡對講機的實現過程差不多就是這個樣子了,馬上動手試一下效果吧^_^

 

版權聲明:

  訪問者可將本主頁(http://www.cnblogs.com/canf963/p/4875228.html)提供的內容或服務用於個人學習、研究或欣賞,以及其他非商業性或非盈利性用途,但同時應遵守著作權法及其他相關法律的規定,不得侵犯本主頁及相關權利人的合法權利。轉載前務必署名本文作者並以超鏈接形式注明內容來自本主頁,以免帶來不必要的麻煩。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM