您的位置:首页 > 理论基础 > 计算机网络

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

2012-12-19 19:38 441 查看
先说下实现原理,手机采集到语音后进过Speex编码,通过juv以直播形式发布自己的语音流到red5,也是通过juv播放对方的直播流,经过Speex解码后输出到扬声器,如下图:





Android端采集编码和解码播放Speex,参考android-recorder,至于他用的red5客户端,看了下,没看明白。。。

JUV这库吧 好上手,虽然是付费的,但是有30天的试用,可长期申请。暂时用下也不错。另外,国内有破解版,你懂得

核心代码如下:

public class AudioCenter extends AbstractMicrophone


首先,音频处理类继承自JUV库中的 AbstractMicrophone,便可以使用 fireOnAudioData方法向Red5服务端发布音频数据。

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调用层面。

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的配置

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实现网络语音聊天(一)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐