Android通过JUV+Red5+Speex实现网络语音聊天

本例严重过期,并且使用了不开源且收费的库 请尝试最新开源版: Android基于OpenSL ES,Speex,RTMP的Voip客户端实现 iOS版: iOS基于Audio Queues,Speex,Rtmp的Voip客户端

—————-仅供参考请勿用于项目———————– 先说下实现原理,手机采集到语音后进过Speex编码,通过juv以直播形式发布自己的语音流到red5,也是通过juv播放对方的直播流,经过Speex解码后输出到扬声器,如下图:

Android  voip  流程图 Android端采集编码和解码播放Speex,参考android-recorder,至于他用的red5客户端,看了下,没看明白。。。 JUV这库吧 好上手,虽然是付费的,但是有30天的试用,可长期申请。暂时用下也不错。另外,国内有破解版,你懂得

核心代码如下:

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语音数据

private Vector encData = new Vector();

采集编码解码播放先谈到这里,下篇讲下juv的连接和数据传输相关。 如有疑问,欢迎与我交流。

原创文章,转载请注明: 转载自贝壳博客

本文链接地址: Android通过JUV+Red5+Speex实现网络语音聊天(一)

转载于:https://www.cnblogs.com/MMLoveMeMM/articles/3597658.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值