Android ilbc 语音对话示范(四)发送方代码
来源:互联网 发布:apache tiles 编辑:程序博客网 时间:2024/04/30 11:51
上一文章中提到:
发送端有三个主要的类:AudioRecorder(负责音频采集),AudioEncoder(负责音频编码),AudioSender(负责 将编码后的数据发送出去); 这三个类中各有一个线程,录制开始后,这三个线程一起运行,分别执行各自的任务, AudioRecorder采集音频后,添加到AudioEncoder 的音频数据的List中,而AudioEncoder 的编码线程不断从List头部取出数据,调用 ilbc 的底层\函数进行编码,编码后的数据则又添加到下一级的AudioSender的 List中,AudioSender又不断从头部取出数据,然后发送出去;
1. 先建立一个 AudioData的类,代表一段音频数据:
- public class AudioData {
- int size;
- byte[] realData;
- //long timestamp;
-
- public int getSize() {
- return size;
- }
-
- public void setSize(int size) {
- this.size = size;
- }
-
- public byte[] getRealData() {
- return realData;
- }
-
- public void setRealData(byte[] realData) {
- this.realData = realData;
- }
-
- //public long getTimestamp() {
- // return timestamp;
- // }
- //
- // public void setTimestamp(long timestamp) {
- // this.timestamp = timestamp;
- // }
- }
复制代码2.
AudioRecorder 类,使用Android系统自带的AudioRecord来采集音频,每采集一次,就交给编码器编码。
- public class AudioRecorder implements Runnable {
-
- String LOG = "Recorder ";
-
- private boolean isRecording = false;
- private AudioRecord audioRecord;
-
- private static final int audioSource = MediaRecorder.AudioSource.MIC;
- private static final int sampleRate = 8000;
- private static final int channelConfig = AudioFormat.CHANNEL_IN_MONO;
- private static final int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
- private static final int BUFFER_FRAME_SIZE =960;
- private int audioBufSize = 0;
-
- //
- private byte[] samples;// 缓冲区
- private int bufferRead = 0;// 从recorder中读取的samples的大小
-
- private int bufferSize = 0;// samples的大小
-
- // 开始录制
- public void startRecording() {
- bufferSize = BUFFER_FRAME_SIZE;
-
- audioBufSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig,
- audioFormat);
- if (audioBufSize == AudioRecord.ERROR_BAD_VALUE) {
- Log.e(LOG, "audioBufSize error");
- return;
- }
- samples = new byte[audioBufSize];
- // 初始化recorder
- if (null == audioRecord) {
- audioRecord = new AudioRecord(audioSource, sampleRate,
- channelConfig, audioFormat, audioBufSize);
- }
- new Thread(this).start();
- }
-
- // 停止录制
- public void stopRecording() {
- this.isRecording = false;
- }
-
- public boolean isRecording() {
- return isRecording;
- }
-
- // run
- public void run() {
- // 录制前,先启动解码器
- AudioEncoder encoder = AudioEncoder.getInstance();
- encoder.startEncoding();
-
- System.out.println(LOG + "audioRecord startRecording()");
- audioRecord.startRecording();
-
- this.isRecording = true;
- while (isRecording) {
- bufferRead = audioRecord.read(samples, 0, bufferSize);
- if (bufferRead > 0) {
- // 将数据添加给解码器
- encoder.addData(samples, bufferRead);
- }
-
- try {
- Thread.sleep(20);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- System.out.println(LOG + "录制结束");
- audioRecord.stop();
- encoder.stopEncoding();
- }
- }
复制代码3.
AudioEncoder,负责调用NDK 方法实现音频的编码,每编码一次,就交给AudioSender 去发送:
- public class AudioEncoder implements Runnable {
- String LOG = "AudioEncoder";
-
- private static AudioEncoder encoder;
- private boolean isEncoding = false;
-
- private List<AudioData> dataList = null;// 存放数据
-
- public static AudioEncoder getInstance() {
- if (encoder == null) {
- encoder = new AudioEncoder();
- }
- return encoder;
- }
-
- private AudioEncoder() {
- dataList = Collections.synchronizedList(new LinkedList<AudioData>());
- }
-
- public void addData(byte[] data, int size) {
- AudioData rawData = new AudioData();
- rawData.setSize(size);
- byte[] tempData = new byte[size];
- System.arraycopy(data, 0, tempData, 0, size);
- rawData.setRealData(tempData);
- dataList.add(rawData);
- }
-
- // 开始编码
- public void startEncoding() {
- System.out.println(LOG + "解码线程启动");
- if (isEncoding) {
- Log.e(LOG, "编码器已经启动,不能再次启动");
- return;
- }
- new Thread(this).start();
- }
-
- // 结束
- public void stopEncoding() {
- this.isEncoding = false;
- }
-
- public void run() {
- // 先启动发送端
- AudioSender sender = new AudioSender();
- sender.startSending();
-
- int encodeSize = 0;
- byte[] encodedData = new byte[256];
-
- // 初始化编码器
- AudioCodec.audio_codec_init(30);
-
- isEncoding = true;
- while (isEncoding) {
- if (dataList.size() == 0) {
- try {
- Thread.sleep(20);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- continue;
- }
- if (isEncoding) {
- AudioData rawData = dataList.remove(0);
- encodedData = new byte[rawData.getSize()];
- //
- encodeSize = AudioCodec.audio_encode(rawData.getRealData(), 0,
- rawData.getSize(), encodedData, 0);
- System.out.println();
- if (encodeSize > 0) {
- sender.addData(encodedData, encodeSize);
- // 清空数据
- encodedData = new byte[encodedData.length];
- }
- }
- }
- System.out.println(LOG + "编码结束");
- sender.stopSending();
- }
- }
复制代码4.
AudioSender类,负责音频数据的发送,使用UDP协议将编码后的AMR音频数据发送到服务器端,这个类功能简单:
- public class AudioSender implements Runnable {
- String LOG = "AudioSender ";
-
- private boolean isSendering = false;
- private List<AudioData> dataList;
-
- DatagramSocket socket;
- DatagramPacket dataPacket;
- private InetAddress ip;
- private int port;
-
- public AudioSender() {
- dataList = Collections.synchronizedList(new LinkedList<AudioData>());
- try {
- try {
- ip = InetAddress.getByName(MyConfig.SERVER_HOST);
- this.port = MyConfig.SERVER_PORT;
- socket = new DatagramSocket();
- } catch (UnknownHostException e) {
- e.printStackTrace();
- }
- } catch (SocketException e) {
- e.printStackTrace();
- }
- }
-
- // 添加数据
- public void addData(byte[] data, int size) {
- AudioData encodedData = new AudioData();
- encodedData.setSize(size);
- byte[] tempData = new byte[size];
- System.arraycopy(data, 0, tempData, 0, size);
- encodedData.setRealData(tempData);
- dataList.add(encodedData);
- }
-
- // 发送数据
- private void sendData(byte[] data, int size) {
- try {
- dataPacket = new DatagramPacket(data, size, ip, port);
- dataPacket.setData(data);
- socket.send(dataPacket);
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-
- // 开始发送
- public void startSending() {
- System.out.println(LOG + "发送线程启动");
- new Thread(this).start();
- }
-
- // 停止发送
- public void stopSending() {
- this.isSendering = false;
- }
-
- // run
- public void run() {
- this.isSendering = true;
- System.out.println(LOG + "开始发送数据");
- while (isSendering) {
- if (dataList.size() > 0) {
- AudioData encodedData = dataList.remove(0);
- sendData(encodedData.getRealData(), encodedData.getSize());
- }
- }
- System.out.println(LOG + "发送结束");
- }
- }
复制代码5.
另外,上述类中有一个 MyConfig 类,主要放一些 配置参数:
- public class MyConfig {
- public static String SERVER_HOST = "192.168.1.130";// 服务器的IP
- public static final int SERVER_PORT = 5656;// 服务器的监听端口
- public static final int CLIENT_PORT = 5757;//
-
- public static final int AUDIO_STATUS_RECORDING = 0;//手机端的状态:录音 or 播放
- public static final int AUDIO_STATUS_LISTENING = 1;
-
- public static void setServerHost(String ip) {
- System.out.println("修改后的服务器网址为 " + ip);
- SERVER_HOST = ip;
- }
- }
复制代码上述代码实现了发送端的功能,现在代码结构如下:
本实例中对音频没有添加时间戳处理,实际测试中没有太大的影响,可以听的清楚双方的语音对话,如果想要添加时间戳的话,就在音频录制 AudioRecord的 read方法出得到时间戳,然后附加给UDP包。取自:http://www.cnblogs.com/swordbearer/archive/2012/08/10/2633083.html