Android P2P語音通話實現

  最近在在研究語音通話的實現,現在把我的實現思路記錄在這裏。不過,由於初次接觸語音通話,所以這是一個簡單的思路,也是經過google以及baidu之後的一個學習總結。

  我認爲一個語音通話系統至少有四個模塊。分別是PCM(Pulse Code Modulation,即 脈碼編碼調製)語音採集,編解碼,網絡傳輸以及語音播放。如果算上UI交互的話,就是五個模塊了。

  整體流程大概是:A打電話給B,A聲音通過MIC被採集成PCM原始數據,然後經過編碼壓縮,再通過網絡(建立P2P連接)將編碼後的數據傳輸出去;B端通過網絡收到數據後進行解碼處理,然後調用播放模塊,進行播放數據。

  一、語音採集模塊

  Android平臺上的實現是通過AudioRecord接口來實現PCM數據的採集,這一步比較容易的。但需要注意的是AudioRecord接口的使用方法。構造AudioRecord 實例需要參數

public AudioRecord(int audioSource, int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes)

audioSource:   the recording source. See MediaRecorder.AudioSource for recording source definitions. 
sampleRateInHz:   the sample rate expressed in Hertz. 44100Hz is currently the only rate that is guaranteed to work on all devices, but other rates such as 22050, 16000, and 11025 may work on some devices. 
channelConfig:   describes the configuration of the audio channels. See CHANNEL_IN_MONO and CHANNEL_IN_STEREO. CHANNEL_IN_MONOis guaranteed to work on all devices. 
audioFormat:  the format in which the audio data is represented. See ENCODING_PCM_16BIT and ENCODING_PCM_8BIT
bufferSizeInBytes:  the total size (in bytes) of the buffer where audio data is written to during the recording. New audio data can be read from this buffer in smaller chunks than this size. See getMinBufferSize(int, int, int) to determine the minimum required buffer size for the successful creation of an AudioRecord instance. Using values smaller than getMinBufferSize() will result in an initialization failure.

audioSource:這裏可以是MediaRecorder.AudioSource.MIC

  1. sampleRateInHz:錄製頻率,可以爲8000hz或11025hz等,不同的硬件設備這個值不同
  2. channelConfig:錄製通道,可以爲AudioFormat.CHANNEL_IN_MONO and AudioFormat.CHANNEL_IN_STEREO. AudioFormat.CHANNEL_IN_MONO
  3. audioFormat:錄製編碼格式,可以爲AudioFormat.ENCODING_16BIT和8BIT,其中16BIT的仿真性比8BIT好,但是需要消耗更多的電量和存儲空間
  4. bufferSizeInBytes:錄製緩衝大小:可以通過getMinBufferSize()方法來獲取

調用AudioRecord的 read(byte[], int, int)read(short[], int, int) or read(ByteBuffer, int)方法就可以採集PCM語音數據了。

  二、語音播放

  當語音數據採集好了之後,接着可以實現語音播放模塊。Android上實現PCM數據的播放也很簡單,直接使用AudioTrack這個接口就行了。同樣需要注意該接口的使用方法。

  AudioTrack的構造方式跟AudioRecord是對應的

public AudioTrack(int streamType, int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes, int mode)

public AudioTrack(int streamType, int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes, int mode)

streamType: the type of the audio stream. See STREAM_VOICE_CALL, STREAM_SYSTEM, STREAM_RING, STREAM_MUSIC, STREAM_ALARM, and STREAM_NOTIFICATION.

sampleRateInHz: the sample rate expressed in Hertz.

channelConfig: describes the configuration of the audio channels. See CHANNEL_OUT_MONO and CHANNEL_OUT_STEREO

audioFormat: the format in which the audio data is represented. See ENCODING_PCM_16BIT and ENCODING_PCM_8BIT

bufferSizeInBytes: the total size (in bytes) of the buffer where audio data is read from for playback. If using the AudioTrack in streaming mode, you can write data into this buffer in smaller chunks than this size. If using the AudioTrack in static mode, this is the maximum size of the sound that will be played for this instance. See getMinBufferSize(int, int, int) to determine the minimum required buffer size for the successful creation of an AudioTrack instance in streaming mode. Using values smaller than getMinBufferSize() will result in an initialization failure.

mode: streaming or static buffer. See MODE_STATIC and MODE_STREAM


  其實這個邊錄邊播的效果如果使用擴音器的話迴音是很嚴重的,而且噪音也很嚴重的,這也是一個問題!因此要進行下一步,編解碼!以上兩個模塊的實現是比較好實現的。當這兩個模塊實現好了,就可以實現網絡上許多例子說明AudioRecord與AudioTrack用法時的“邊錄邊播”的效果。當然這不是我的目標,只是我們可以這樣測試自己的採集的數據是否正確。

  三、語音編解碼

  採集到的PCM數據是原始的語音數據,如果我們直接進行網絡傳輸,那是不可取的。因此,要進行打包編碼。

  編碼我們需要第三方的庫,目前我使用的庫是speex(http://www.speex.org)。我看到許多SIP語音電話都使用到了這個庫進行編解碼。當然也有對這個庫評價不好的說法,但我覺得作爲學習還是可取的,因爲speex使用起來很方便。

  speex是一個c庫(當然也有java版本的,http://code.google.com/p/speexdroid/,但我還是建議使用c庫,因爲java版本的speex效率可能不是很高),因此我們需要用到jni。如果沒有使用過jni的話,這也是一個學習的機會。可以參考sipdroid http://code.google.com/p/sipdroid/ 如果還是覺得麻煩的話可以參考這個開源項目 http://code.google.com/p/android-recorder/

  不過,我使用speex的時候,噪音是降低了,但使用擴音器的時候,還是有很大的迴音,但是效果已經好很多了。從speex的官網上可以知道,最新的speex版本添加了迴音以及降噪的處理,但我把迴音以及降噪模塊加進去的時候,沒有明顯的效果,所以這是我使用speex庫時遇到的一個問題,目前還在研究中。知道原因的同學留個言學習一下哈。

  四、網絡傳輸

  打包編碼之後就是網絡傳輸了。網絡傳輸主要是使用RTP(實時傳輸協議)。目前我使用的庫是jlibrtp庫http://sourceforge.net/projects/jlibrtp/?source=directory,這是一個java版本的實現。不過,這個庫有丟包的問題,以及會拋庫內的一些異常。由於我沒有找到更好的RTP傳輸的庫,所以只好使用這個庫了。喜歡研究的同學也可以研究一下Sipdroid的RTP實現,我也有在看,不過還沒有研究透。有研究過的同學,可以留言,我們一起學習探討一下哈。

  這個就是一個簡單P2P語音通話的實現思路。不過此思路,還沒有實現服務器端。所以這個思路實現只能在局域網內通話。要實現P2P通話還需要NAT打洞的技術,這也是一個難點。我覺得難點就是用來攻破的。

  對於代碼:由於是公司項目,不大方便貼出,之後有時間整理一下,再貼吧。

  本文轉載自:http://www.cnblogs.com/milospooner/archive/2012/07/13/2590950.html

推薦閱讀另外一篇博客:研究Android即時聊天、實時語音通話、實時對講機等的必備知識Audio

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章