Android网络对讲机的实现
上个星期公司给出了一个项目需求,做一个基于socket通讯协议的网络对讲机。于是在项目开始前计划了一下基本的实现流程。
1、从手机麦中采集音频数据;2、将PCM音频数据编码压缩;3、将压缩好的音频通过无线网络发送出去;4、其他手机接收音频数据并解码;5、将音频数据写入到音轨中播放。项目虽然简单,但其中的一些小问题也折腾了我不少时间。
首先我们创建一个线程用来采集音频数据,通过android提供的AudioRecord可以实时采集音频流。AudioRecord类在Java应用程序中管理音频资源,用来记录从平台音频输入设备产生的数据。其实调用AudioRecord很简单,首先创建AudioRecord对象,AudioRecord会初始化并连接音频缓冲区,用来缓冲新的音频数据。根据指定的缓冲区的大小来决定AudioRecord能够记录多长的数据。
调用getMinBufferSize(int,int,int)返回最小的缓冲区大小。然后根据得到的最小缓冲区大小来创建AudioRecord对象:
inputMinSize = AudioRecord.getMinBufferSize(8000,
AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT);
audioRec = new AudioRecord(MediaRecorder.AudioSource.MIC, 8000,
AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT, inputMinSize);
AudioRecord初始化工作完毕后启用录制线程,并且调用startRecording ()开始进行音频录制。调用read(short,int,int)方法从音频硬件录制缓冲区读取数据。拿到音频数据后,直接通过网络发送出去是不行的,我们在这里还要做一项工作就是实现音频压缩。在网上提供了很多音频的编码库,我们可以将源码导入到项目中通过android ndk编译成.so文件,最后通过jni来调用。我这里直接用sipdroid开源项目提供的SILK编解码库(下载编码库)。
本地方法 encode(short[] lin,int offset,byte[] encoded,int size) 参数 lin 源数据 offset 源数组的起始偏移量 encoded 编码后的数据 size 请求编码的数据大小返回值 编码后的数据大小调用encode(short[], int, byte[], int)压缩已经采集完毕的音频数据,我们就可以通过网络发送出去了。
接下来,我们创建一个socket udp实例,为什么这里选择udp而不是tcp呢?从我们本身的项目需求出发,我们做的这个项目的通讯方式是相互收发数据的,属于手机与手机两“客户端“之间的通讯。并且,在这种音频通信过程中,我们要传输的数据量是比较庞大的,因此采用资源消耗少,处理速度快的UDP协议是合理的。指定发送的端口号,我们将数据封装成报文发送出去,整个采集发送的过程如下:
class RecordSoundThread extends Thread {
private boolean flag = true;
private DatagramSocket mSocket; private int inputBufSize = 160;
short[] inputBytes = new short[1024];
byte[] encodeBytes = new byte[1024]; RecordSoundThread() throws SocketException {
// TODO Auto-generated constructor stub
mSocket = new DatagramSocket();
} @Override
public void run() {
if (mSocket == null)
return; while (flag) {
if (isSpeakMode) {
try {
int length = audioRec.read(inputBytes, 0, inputBufSize); // calc(inputBytes, 0, length); length = silk8.encode(inputBytes, 0, encodeBytes,
length); DatagramPacket writePacket;
if (inetAddress.length() > 0) {
InetAddress inet = InetAddress
.getByName(inetAddress);
writePacket = new DatagramPacket(encodeBytes,
length, inet, NETPORT);
writePacket.setLength(length);
mSocket.send(writePacket);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
} public void close() {
flag = false;
if (mSocket != null) {
mSocket.close();
}
}
}
接下来我们要接收目标机器发送过来的音频数据了。同样,创建一个线程用来接收网络中的音频数据,并且对音频数据进行解码。
本地方法 decode(byte[] encoded, short[] lin, int size) 参数 encoded 源数据 lin 解码后的数据 size 请求解码的数据大小 返回值 解码后的数据大小得到解码后的PCM音频流,我们就可以使用AudioTrack将音频播放出来了。
AudioTrack类在java应用程序中管理和播放音频资源,将PCM音频数据写入到缓冲区来播放音频设备。首先创建AudioTrack对象,AudioTrack会初始化并连接音频缓冲区,根据指定的缓冲区大小来决定audioTrack能够播放多长的数据。调用getMinBufferSize(int,int,int)返回最小的缓冲区大小。然后根据得到的最小缓冲区大小来创建audioTrack对象:
outputMinSize = AudioTrack.getMinBufferSize(8000,
AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT);
audioTrk = new AudioTrack(AudioManager.STREAM_MUSIC, 8000,
AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT, outputMinSize,
AudioTrack.MODE_STREAM);
AudioTrack初始化工作完毕后启用接收线程,并且调用play()开始播放。调用write(short[],int,int)方法将PCM音频数据写入到音频硬件中。
class RecevRecordThread extends Thread {
private boolean flag = true;
private DatagramSocket mSocket; short[] decodeBytes = new short[1024];
byte[] outputBytes = new byte[1024]; RecevRecordThread() throws SocketException {
// TODO Auto-generated constructor stub
mSocket = new DatagramSocket(NETPORT);
} @Override
public void run() {
if (mSocket == null)
return;
audioTrk.play();
while (flag) {
DatagramPacket recevPacket;
try {
recevPacket = new DatagramPacket(outputBytes, 0,
outputBytes.length);
mSocket.receive(recevPacket); int length = recevPacket.getLength(); length = silk8.decode(outputBytes, decodeBytes, length); // calc2(decodeBytes, 0, length); audioTrk.write(decodeBytes, 0, length);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
audioTrk.stop();
} public void close() {
flag = false;
if (mSocket != null) {
mSocket.close();
}
}
}
最后,退出应用后别忘了释放资源。
public void onDestroy() {
silk8.close();
recevThread.close();
recordThread.close();
audioRec.release();
audioTrk.release();
}
好了,网络对讲机的实现过程差不多就是这个样子了,马上动手试一下效果吧^_^
版权声明:
访问者可将本主页(http://www.cnblogs.com/canf963/p/4875228.html)提供的内容或服务用于个人学习、研究或欣赏,以及其他非商业性或非盈利性用途,但同时应遵守著作权法及其他相关法律的规定,不得侵犯本主页及相关权利人的合法权利。转载前务必署名本文作者并以超链接形式注明内容来自本主页,以免带来不必要的麻烦。
Android网络对讲机的实现的更多相关文章
- 6、android 网络编程
1.基于socket的用法 服务器端: 先启动一个服务器端的socket ServerSocket svr = new ServerSocket(8989); 开始侦听请求 Socket s ...
- Android网络编程只局域网传输文件
Android网络编程之局域网传输文件: 首先创建一个socket管理类,该类是传输文件的核心类,主要用来发送文件和接收文件 具体代码如下: package com.jiao.filesend; im ...
- Android网络编程基础
Android网络编程只TCP通信 TCP 服务器端工作的主要步骤如下.步骤1 调用ServerSocket(int port)创建一个ServerSocket,并绑定到指定端口上.步骤2 调用acc ...
- Android网络之数据解析----使用Google Gson解析Json数据
[声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/4 ...
- Android网络之数据解析----SAX方式解析XML数据
[声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/ ...
- Android 网络连接判断与处理
Android网络连接判断与处理 获取网络信息需要在AndroidManifest.xml文件中加入相应的权限. <uses-permission android:name="and ...
- Android网络编程系列 一 TCP/IP协议族
在学习和使用Android网路编程时,我们接触的仅仅是上层协议和接口如Apache的httpclient或者Android自带的httpURlconnection等等.对于这些接口的底层实现我们也有必 ...
- Android网络编程系列 一 Socket抽象层
在<Android网络编程>系列文章中,前面已经将Java的通信底层大致的描述了,在我们了解了TCP/IP通信族架构及其原理,接下来我们就开始来了解基于tcp/ip协议层的Socket抽 ...
- Android 网络编程 Socket
1.服务端开发 创建一个Java程序 public class MyServer { // 定义保存所有的Socket,与客户端建立连接得到一个Socket public static List< ...
随机推荐
- epub-2格式电子书剖析之一:文档构成
epub格式电子书遵循IDPF推出的OCF规范,OCF规范遵循ZIP压缩技术,即epub电子书本身就是一个ZIP文件,我们将epub格式电子书的后缀.epub修改为.zip后,可以通过解压缩软件(例如 ...
- linux下passwd命令设置修改用户密码
1.passwd 简单说明: 我们已经学会如何添加用户了,所以我们还要学习设置或修改用户的密码:passwd命令的用法也很多,我们只选如下的几个参数加以说明:想了解更多,请参考man passwd或p ...
- How to change the property of a control from a flowlayoutpanel?
Well, the easiest way would be to retain an explicit reference to the buttons you're adding. Otherwi ...
- seleniumRC启动及浏览器实例配置
一.firefox浏览器实例配置 1.启动用户配置文件管理器 重要:在启动用户配置文件管理器之前,Firefox必须完全关闭. 1)按 support.cdn.mozilla.net/medi ...
- arcgis android 加载google切片 天地图切片 并且能进行缓存
废话不多说,直接下载,看layer包! https://github.com/Young-Ken/android-gis
- C++之内部类(嵌套类)与外部类及友元
本人能力.精力有限,所言所感都基于自身的实践和有限的阅读.查阅,如有错误,欢迎拍砖,敬请赐教——博客园:钱智慧. 先上代码: class Outer { public: Outer(){m_outer ...
- 2014年Tizen开发者峰会上海征稿启事!
本次征稿面向大中华用户: “Tizen开发者,应用程序开发人员.isv平台设计师.运营商.厂商.硬件厂商.软件厂商,开源爱好者,和从事Tizen的工作人员” 2014年Tizen开发者峰会 这一次,亚 ...
- 【Java基础】Java中的持久属性集Properties
Properties 类的介绍 Properties 类表示了一个持久的属性集.Properties 可保存在流中或从流中加载.属性列表中每个键及其对应值都是一个字符串.一个属性列表可包含另一个属性列 ...
- Duff and Weight Lifting - 587A
题目大意:某个人训练举重,他每次可以举起来2^wi的重量,不过这个人比较懒所以他想尽量减少训练的次数,如果所有的训练重量2^a1 +2^a2+....2^ak = 2^x,那么这些重量可以一次性训练( ...
- 检查REDO日志相关信息并生成HTML文件的脚本
生成HTML格式的文件 内容有: 检查数据库版本.REDO日志组情况, 最近20次日志切换频率检查--日志间的归档时间间隔, 这对查看数据库的IO繁忙时段 统计指定日期当天每小时的归档日志产生量--日 ...