Android ilbc 语音对话示范(四)发送方代码

来源:互联网 发布:apache tiles 编辑:程序博客网 时间:2024/04/30 11:51
上一文章中提到:
发送端有三个主要的类:AudioRecorder(负责音频采集),AudioEncoder(负责音频编码),AudioSender(负责 将编码后的数据发送出去); 这三个类中各有一个线程,录制开始后,这三个线程一起运行,分别执行各自的任务, AudioRecorder采集音频后,添加到AudioEncoder 的音频数据的List中,而AudioEncoder 的编码线程不断从List头部取出数据,调用 ilbc 的底层\函数进行编码,编码后的数据则又添加到下一级的AudioSender的 List中,AudioSender又不断从头部取出数据,然后发送出去;


1. 先建立一个 AudioData的类,代表一段音频数据:


  1. public class AudioData {   
  2.     int size;   
  3.     byte[] realData;   
  4.     //long timestamp;   
  5.     
  6.     public int getSize() {   
  7.         return size;   
  8.     }   
  9.     
  10.     public void setSize(int size) {   
  11.         this.size = size;   
  12.     }   
  13.     
  14.     public byte[] getRealData() {   
  15.         return realData;   
  16.     }   
  17.     
  18.     public void setRealData(byte[] realData) {   
  19.         this.realData = realData;   
  20.     }   
  21.     
  22.     //public long getTimestamp() {   
  23.     // return timestamp;   
  24.     // }   
  25.     //   
  26.     // public void setTimestamp(long timestamp) {   
  27.     // this.timestamp = timestamp;   
  28.     // }   
  29. }

复制代码
2.AudioRecorder 类,使用Android系统自带的AudioRecord来采集音频,每采集一次,就交给编码器编码。


  1. public class AudioRecorder implements Runnable {   
  2.     
  3.     String LOG = "Recorder ";   
  4.     
  5.     private boolean isRecording = false;   
  6.     private AudioRecord audioRecord;   
  7.     
  8.     private static final int audioSource = MediaRecorder.AudioSource.MIC;   
  9.     private static final int sampleRate = 8000;   
  10.     private static final int channelConfig = AudioFormat.CHANNEL_IN_MONO;   
  11.     private static final int audioFormat = AudioFormat.ENCODING_PCM_16BIT;   
  12.     private static final int BUFFER_FRAME_SIZE =960;   
  13.     private int audioBufSize = 0;   
  14.     
  15.     //   
  16.     private byte[] samples;// 缓冲区   
  17.     private int bufferRead = 0;// 从recorder中读取的samples的大小   
  18.     
  19.     private int bufferSize = 0;// samples的大小   
  20.     
  21.     // 开始录制   
  22.     public void startRecording() {   
  23.         bufferSize = BUFFER_FRAME_SIZE;   
  24.     
  25.         audioBufSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig,   
  26.                 audioFormat);   
  27.         if (audioBufSize == AudioRecord.ERROR_BAD_VALUE) {   
  28.             Log.e(LOG, "audioBufSize error");   
  29.             return;   
  30.         }   
  31.         samples = new byte[audioBufSize];   
  32.         // 初始化recorder   
  33.         if (null == audioRecord) {   
  34.             audioRecord = new AudioRecord(audioSource, sampleRate,   
  35.                     channelConfig, audioFormat, audioBufSize);   
  36.         }   
  37.         new Thread(this).start();   
  38.     }   
  39.     
  40.     // 停止录制   
  41.     public void stopRecording() {   
  42.         this.isRecording = false;   
  43.     }   
  44.     
  45.     public boolean isRecording() {   
  46.         return isRecording;   
  47.     }   
  48.     
  49.     // run   
  50.     public void run() {   
  51.         // 录制前,先启动解码器   
  52.         AudioEncoder encoder = AudioEncoder.getInstance();   
  53.         encoder.startEncoding();   
  54.     
  55.         System.out.println(LOG + "audioRecord startRecording()");   
  56.         audioRecord.startRecording();   
  57.     
  58.         this.isRecording = true;   
  59.         while (isRecording) {   
  60.             bufferRead = audioRecord.read(samples, 0, bufferSize);   
  61.             if (bufferRead > 0) {   
  62.                 // 将数据添加给解码器   
  63.                 encoder.addData(samples, bufferRead);   
  64.             }   
  65.     
  66.             try {   
  67.                 Thread.sleep(20);   
  68.             } catch (InterruptedException e) {   
  69.                 e.printStackTrace();   
  70.             }   
  71.         }   
  72.         System.out.println(LOG + "录制结束");   
  73.         audioRecord.stop();   
  74.         encoder.stopEncoding();   
  75.     }   
  76. }

复制代码
3. AudioEncoder,负责调用NDK 方法实现音频的编码,每编码一次,就交给AudioSender 去发送:


  1. public class AudioEncoder implements Runnable {   
  2.     String LOG = "AudioEncoder";   
  3.     
  4.     private static AudioEncoder encoder;   
  5.     private boolean isEncoding = false;   
  6.     
  7.     private List<AudioData> dataList = null;// 存放数据   
  8.     
  9.     public static AudioEncoder getInstance() {   
  10.         if (encoder == null) {   
  11.             encoder = new AudioEncoder();   
  12.         }   
  13.         return encoder;   
  14.     }   
  15.     
  16.     private AudioEncoder() {   
  17.         dataList = Collections.synchronizedList(new LinkedList<AudioData>());   
  18.     }   
  19.     
  20.     public void addData(byte[] data, int size) {   
  21.         AudioData rawData = new AudioData();   
  22.         rawData.setSize(size);   
  23.         byte[] tempData = new byte[size];   
  24.         System.arraycopy(data, 0, tempData, 0, size);   
  25.         rawData.setRealData(tempData);   
  26.         dataList.add(rawData);   
  27.     }   
  28.     
  29.     // 开始编码   
  30.     public void startEncoding() {   
  31.         System.out.println(LOG + "解码线程启动");   
  32.         if (isEncoding) {   
  33.             Log.e(LOG, "编码器已经启动,不能再次启动");   
  34.             return;   
  35.         }   
  36.         new Thread(this).start();   
  37.     }   
  38.     
  39.     // 结束   
  40.     public void stopEncoding() {   
  41.         this.isEncoding = false;   
  42.     }   
  43.     
  44.     public void run() {   
  45.         // 先启动发送端   
  46.         AudioSender sender = new AudioSender();   
  47.         sender.startSending();   
  48.     
  49.         int encodeSize = 0;   
  50.         byte[] encodedData = new byte[256];   
  51.     
  52.         // 初始化编码器   
  53.         AudioCodec.audio_codec_init(30);   
  54.     
  55.         isEncoding = true;   
  56.         while (isEncoding) {   
  57.             if (dataList.size() == 0) {   
  58.                 try {   
  59.                     Thread.sleep(20);   
  60.                 } catch (InterruptedException e) {   
  61.                     e.printStackTrace();   
  62.                 }   
  63.                 continue;   
  64.             }   
  65.             if (isEncoding) {   
  66.                 AudioData rawData = dataList.remove(0);   
  67.                 encodedData = new byte[rawData.getSize()];   
  68.                 //   
  69.                 encodeSize = AudioCodec.audio_encode(rawData.getRealData(), 0,   
  70.                         rawData.getSize(), encodedData, 0);   
  71.                 System.out.println();   
  72.                 if (encodeSize > 0) {   
  73.                     sender.addData(encodedData, encodeSize);   
  74.                     // 清空数据   
  75.                     encodedData = new byte[encodedData.length];   
  76.                 }   
  77.             }   
  78.         }   
  79.         System.out.println(LOG + "编码结束");   
  80.         sender.stopSending();   
  81.     }   
  82. }

复制代码
4. AudioSender类,负责音频数据的发送,使用UDP协议将编码后的AMR音频数据发送到服务器端,这个类功能简单:


  1. public class AudioSender implements Runnable {   
  2.     String LOG = "AudioSender ";   
  3.     
  4.     private boolean isSendering = false;   
  5.     private List<AudioData> dataList;   
  6.     
  7.     DatagramSocket socket;   
  8.     DatagramPacket dataPacket;   
  9.     private InetAddress ip;   
  10.     private int port;   
  11.     
  12.     public AudioSender() {   
  13.         dataList = Collections.synchronizedList(new LinkedList<AudioData>());   
  14.         try {   
  15.             try {   
  16.                 ip = InetAddress.getByName(MyConfig.SERVER_HOST);   
  17.                 this.port = MyConfig.SERVER_PORT;   
  18.                 socket = new DatagramSocket();   
  19.             } catch (UnknownHostException e) {   
  20.                 e.printStackTrace();   
  21.             }   
  22.         } catch (SocketException e) {   
  23.             e.printStackTrace();   
  24.         }   
  25.     }   
  26.     
  27.     // 添加数据   
  28.     public void addData(byte[] data, int size) {   
  29.         AudioData encodedData = new AudioData();   
  30.         encodedData.setSize(size);   
  31.         byte[] tempData = new byte[size];   
  32.         System.arraycopy(data, 0, tempData, 0, size);   
  33.         encodedData.setRealData(tempData);   
  34.         dataList.add(encodedData);   
  35.     }   
  36.     
  37.     // 发送数据   
  38.     private void sendData(byte[] data, int size) {   
  39.         try {   
  40.             dataPacket = new DatagramPacket(data, size, ip, port);   
  41.             dataPacket.setData(data);   
  42.             socket.send(dataPacket);   
  43.         } catch (IOException e) {   
  44.             e.printStackTrace();   
  45.         }   
  46.     }   
  47.     
  48.     // 开始发送   
  49.     public void startSending() {   
  50.         System.out.println(LOG + "发送线程启动");   
  51.         new Thread(this).start();   
  52.     }   
  53.     
  54.     // 停止发送   
  55.     public void stopSending() {   
  56.         this.isSendering = false;   
  57.     }   
  58.     
  59.     // run   
  60.     public void run() {   
  61.         this.isSendering = true;   
  62.         System.out.println(LOG + "开始发送数据");   
  63.         while (isSendering) {   
  64.             if (dataList.size() > 0) {   
  65.                 AudioData encodedData = dataList.remove(0);   
  66.                 sendData(encodedData.getRealData(), encodedData.getSize());   
  67.             }   
  68.         }   
  69.         System.out.println(LOG + "发送结束");   
  70.     }   
  71. }

复制代码
5. 另外,上述类中有一个 MyConfig 类,主要放一些 配置参数:


  1. public class MyConfig {   
  2.     public static String SERVER_HOST = "192.168.1.130";// 服务器的IP   
  3.     public static final int SERVER_PORT = 5656;// 服务器的监听端口   
  4.     public static final int CLIENT_PORT = 5757;//   
  5.     
  6.     public static final int AUDIO_STATUS_RECORDING = 0;//手机端的状态:录音 or 播放   
  7.     public static final int AUDIO_STATUS_LISTENING = 1;   
  8.     
  9.     public static void setServerHost(String ip) {   
  10.         System.out.println("修改后的服务器网址为  " + ip);   
  11.         SERVER_HOST = ip;   
  12.     }   
  13. }

复制代码
上述代码实现了发送端的功能,现在代码结构如下:
2012101815022837.png 

本实例中对音频没有添加时间戳处理,实际测试中没有太大的影响,可以听的清楚双方的语音对话,如果想要添加时间戳的话,就在音频录制 AudioRecord的 read方法出得到时间戳,然后附加给UDP包。取自:http://www.cnblogs.com/swordbearer/archive/2012/08/10/2633083.html
原创粉丝点击