本例严重过期,并且使用了不开源且收费的库
请尝试最新开源版:
Android基于OpenSL ES,Speex,RTMP的Voip客户端实现
iOS版:
iOS基于Audio Queues,Speex,Rtmp的Voip客户端
—————-仅供参考请勿用于项目———————–
先说下实现原理,手机采集到语音后进过Speex编码,通过juv以直播形式发布自己的语音流到red5,也是通过juv播放对方的直播流,经过Speex解码后输出到扬声器,如下图:
Android端采集编码和解码播放Speex,参考android-recorder,至于他用的red5客户端,看了下,没看明白。。。
核心代码如下:
1 |
public class AudioCenter extends AbstractMicrophone |
首先,音频处理类继承自JUV库中的 AbstractMicrophone,便可以使用?fireOnAudioData方法向Red5服务端发布音频数据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
public void encSpeexAudio() { new Thread(new Runnable() { @Override public void run() { int bufferSize = AudioRecord.getMinBufferSize(8000, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT); short[] mAudioRecordBuffer = new short[bufferSize]; AudioRecord mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, 8000, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, bufferSize); mAudioRecord.startRecording(); int bufferRead = 0; int len; isEncoding = true; while (isEncoding) { bufferRead = mAudioRecord.read(mAudioRecordBuffer, 0, frameSize); if (bufferRead > 0) { try { len = speex.encode(mAudioRecordBuffer, 0, processedData, frameSize); // LogHelper.d(subTAG, "EncSpeexAudio "+ len); byte[] speexData = new byte[len + 1]; System.arraycopy(SpeexRtmpHead, 0, speexData, 0, 1); System.arraycopy(processedData, 0, speexData, 1, len); fireOnAudioData(new MediaDataByteArray(20, new ByteArray(speexData))); } catch (Exception e) { e.printStackTrace(); } } } mAudioRecord.stop(); mAudioRecord.release(); mAudioRecord = null; } }, "EncSpeexAudio Thread").start(); } |
以上是编码上传线程,一个关键技术点:SpeexRtmpHead,曾经困扰我很长一段时间,也是因为自己刚开始解除对RTMP协议只停留在api调用层面。
1 |
private byte[] SpeexRtmpHead = new byte[] { (byte) 0xB2 }; |
我们首先要知道“RTMP Packet中封装的音视频数据流,其实和FLV封装音频和视频数据的方式是相同的。”
Audio?tag?数据区 |
audio信息 | 1byte | 前四位bits表示音频格式: |
0?–?未压缩 | ||
1?=?ADPCM | ||
2?=?MP3 | ||
3?=?Linear?PCM,?little?endian | ||
4?=?Nellymoser?16-kHz?mono | ||
5?=?Nellymoser?8-kHz?mono | ||
6?=?Nellymoser | ||
7?=?G.711?A-law?logarithmic?PCM | ||
8?=?G.711?mu-law?logarithmic?PCM | ||
9?=?reserved | ||
10?=?AAC | ||
11?=?Speex | ||
14?=?MP3?8-Khz | ||
15?=?Device-specific?sound | ||
下面两位bits表示samplerate: | ||
0?–?5.5kHz | ||
1?–?11kHz | ||
2?–?22kHz | ||
3?–?44kHz | ||
下面一位bit表示每个采样的长度: | ||
0?–?snd8Bit | ||
1?–?snd16Bit | ||
下面一位bit表示类型: | ||
0?–?sndMomo | ||
1?–?sndStereo | ||
audio数据区 | 不定 | ??if?SoundFormat?==?10????AACAUDIODATAelse????Sound?data—varies?by?format |
由Flv协议的AudioTag数据区可查得,在数据区前有一个字节的audio信息,我们采用speex编码,8KHz采样,每个采样16bit,单声道。那么 得出的数据为10110010 十六进制:0xB2,将它拼装到每个数据区前,通过fireOnAudioData发布,则为标准的Rtmp数据上传到服务器。这时候可以使用red5-publisher测试,已经能听到声音了。不清楚red5-publisher使用的朋友,可以看我上篇关于Red5的配置。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
public void playSpeexAudio() { new Thread(new Runnable() { @Override public void run() { short[] decData = new short[256]; AudioTrack audioTrack; int bufferSizeInBytes = AudioTrack.getMinBufferSize(8000, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT); audioTrack = new AudioTrack(AudioManager.STREAM_VOICE_CALL, 8000, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, 2 * bufferSizeInBytes, AudioTrack.MODE_STREAM); audioTrack.play(); isPlaying = true; while (isPlaying) { while (encData.size() > 0) { byte[] data = encData.elementAt(0); encData.removeElementAt(0); int dec; dec = speex.decode(data, decData, data.length); // LogHelper.d(subTAG, "playSpeexAudio "+ dec); if (dec > 0) { audioTrack.write(decData, 0, dec); } } try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } audioTrack.stop(); audioTrack.release(); audioTrack = null; } }, "PlaySpeexAudio Thread").start(); } |
以上为音频解码线程,没什么难点,只需注意我使用了一个Vector 来缓存juv拉下来的speex语音数据
1 |
private Vector encData = new Vector(); |
采集编码解码播放先谈到这里,下篇讲下juv的连接和数据传输相关。
如有疑问,欢迎与我交流。
原创文章,转载请注明: 转载自贝壳博客
希望你能回复我,1101803463这个是我的QQ号码,你可以加我好友吗?最近也在做这个方面的东西,好多困惑的地方,希望你能指点指点。
楼主,我想请教一下juv-rtmp-client这个包,封视频的RTMP包怎么弄呢,我调用android的camera预览回调,想用这个包来封RTMP的视频包,请楼主指教一下,万分感谢,因为看到你的音频封了RTMP包,不知视频这块怎么封,请楼主百忙中指导一下:我qq:1871108007,
http://blog.csdn.net/firehood_/article/details/8783589
这篇,还要这篇
http://www.cnblogs.com/haibindev/archive/2011/12/29/2305712.html
都讲得很好,尤其第一篇
博主,你确定你的可以播放声音吗?我研究juv好久了。发布音视频没有问题。但是播放。就有点奇怪。不知道是不是我的speex有问题? juv的key我倒是破解了
juv是真的可以
不过太不好用了 而且是java层的
建议你直接看我后面的研究,全jni层的