Android P2P语音通话实现(思路探讨)

来源:互联网 发布:java怎么做图形界面 编辑:程序博客网 时间:2024/04/28 08:58

  

     转载来自:http://www.cnblogs.com/milospooner/archive/2012/07/13/2590950.html

最近在在研究语音通话的实现,现在把我的实现思路记录在这里。不过,由于初次接触语音通话,所以这是一个简单的思路,也是经过google以及baidu之后的一个学习总结。

  我认为一个语音通话系统至少有四个模块。分别是PCM(Pulse Code Modulation,即 脉码编码调制)语音采集,编解码,网络传输以及语音播放。如果算上UI交互的话,就是五个模块了。

  整体流程大概是:A打电话给B,A声音通过MIC被采集成PCM原始数据,然后经过编码压缩,再通过网络(建立P2P连接)将编码后的数据传输出去;B端通过网络收到数据后进行解码处理,然后调用播放模块,进行播放数据。

  一、语音采集模块

  Android平台上的实现是通过AudioRecord接口来实现PCM数据的采集,这一步比较容易的。但需要注意的是AudioRecord接口的使用方法。构造AudioRecord 实例需要参数

publicAudioRecord (int audioSource, int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes)

audioSourcethe recording source. See MediaRecorder.AudioSource for recording source definitions.sampleRateInHzthe 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.channelConfigdescribes the configuration of the audio channels. SeeCHANNEL_IN_MONO andCHANNEL_IN_STEREO.CHANNEL_IN_MONOis guaranteed to work on all devices.audioFormatthe format in which the audio data is represented. SeeENCODING_PCM_16BIT andENCODING_PCM_8BITbufferSizeInBytesthe 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. SeegetMinBufferSize(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.

  1. audioSource:这里可以是MediaRecorder.AudioSource.MIC
  2. sampleRateInHz:录制频率,可以为8000hz或11025hz等,不同的硬件设备这个值不同
  3. channelConfig:录制通道,可以为AudioFormat.CHANNEL_IN_MONO and AudioFormat.CHANNEL_IN_STEREO. AudioFormat.CHANNEL_IN_MONO
  4. audioFormat:录制编码格式,可以为AudioFormat.ENCODING_16BIT和8BIT,其中16BIT的仿真性比8BIT好,但是需要消耗更多的电量和存储空间
  5. bufferSizeInBytes:录制缓冲大小:可以通过getMinBufferSize()方法来获取

调用AudioRecord的 read(byte[], int, int),read(short[], int, int) orread(ByteBuffer, int)方法就可以采集PCM语音数据了。

  二、语音播放

  当语音数据采集好了之后,接着可以实现语音播放模块。Android上实现PCM数据的播放也很简单,直接使用AudioTrack这个接口就行了。同样需要注意该接口的使用方法。

  AudioTrack的构造方式跟AudioRecord是对应的

publicAudioTrack (int streamType, int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes, int mode)

streamTypethe type of the audio stream. SeeSTREAM_VOICE_CALL,STREAM_SYSTEM,STREAM_RING,STREAM_MUSIC,STREAM_ALARM, andSTREAM_NOTIFICATION.sampleRateInHzthe sample rate expressed in Hertz.channelConfigdescribes the configuration of the audio channels. SeeCHANNEL_OUT_MONO andCHANNEL_OUT_STEREOaudioFormatthe format in which the audio data is represented. SeeENCODING_PCM_16BIT andENCODING_PCM_8BITbufferSizeInBytesthe 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. SeegetMinBufferSize(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.modestreaming or static buffer. See MODE_STATIC andMODE_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的话,这也是一个学习的机会。可以参考sipdroidhttp://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打洞的技术,这也是一个难点。我觉得难点就是用来攻破的。

  对于代码:由于是公司项目,不大方便贴出,之后有时间整理一下,再贴吧。

 

原创粉丝点击