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);
sampleRateInHz 默认采样率,单位Hz。
channelConfig 描述音频通道设置。
audioFormat 音频数据保证支持此格式。
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 stubmSocket = new DatagramSocket();}@Overridepublic 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();} }}
View Code
接下来我们要接收目标机器发送过来的音频数据了。同样,创建一个线程用来接收网络中的音频数据,并且对音频数据进行解码。
本地方法 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 stubmSocket = new DatagramSocket(NETPORT);}@Overridepublic 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();} }}
View Code
最后,退出应用后别忘了释放资源。
public void onDestroy() {silk8.close();recevThread.close();recordThread.close();audioRec.release();audioTrk.release(); }
好了,网络对讲机的实现过程差不多就是这个样子了,马上动手试一下效果吧^_^
版权声明:
访问者可将本主页(http://www.cnblogs.com/canf963/p/4875228.html)提供的内容或服务用于个人学习、研究或欣赏,以及其他非商业性或非盈利性用途,但同时应遵守著作权法及其他相关法律的规定,不得侵犯本主页及相关权利人的合法权利。转载前务必署名本文作者并以超链接形式注明内容来自本主页,以免带来不必要的麻烦。
转载于:https://www.cnblogs.com/canf963/p/4875228.html
Android网络对讲机的实现相关推荐
- Android网络编程系列 一 Socket抽象层
在<Android网络编程>系列文章中,前面已经将Java的通信底层大致的描述了,在我们了解了TCP/IP通信族架构及其原理,接下来我们就开始来了解基于tcp/ip协议层的Socket抽象 ...
- Android网络连接异常处理方案
Android网络连接异常处理方案 参考文章: (1)Android网络连接异常处理方案 (2)https://www.cnblogs.com/yangcong/p/3523021.html 备忘一下 ...
- Android网络之数据解析----SAX方式解析XML数据
[声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/ ...
- 【Android 应用开发】Android 网络编程 API笔记 - java.net 包 权限 地址 套接字 相关类 简介
Android 网络编程相关的包 : 9 包, 20 接口, 103 类, 6 枚举, 14异常; -- Java包 : java.net 包 (6接口, 34类, 2枚举, 12异常); -- An ...
- 【Android 应用开发】Android 网络编程 API笔记 - java.net 包相关 接口 api
Android 网络编程相关的包 : 9 包, 20 接口, 103 类, 6 枚举, 14异常; -- Java包 : java.net 包 (6接口, 34类, 2枚举, 12异常); -- An ...
- android 网络通信方式,Android中基于TCP协议的网络通信
一.Android网络简介 Android与服务器的通信方式主要有两种,一种是Http通信,另一种是Socket通信. HTTP通信:Android中内置HttpClient,这样可以发方便的发送Ht ...
- Android - 网络基础
Android网络编程(一)HTTP协议原理 Android网络请求心路历程 HttpURLConnection和HttpClient对比: http://blog.csdn.net/guolin_b ...
- Android 借助Stetho在Chrome上调试Android网络、数据库、Sharedpreferences
Android 借助Stetho在Chrome上调试Android网络.数据库.Sharedpreferences 转载请标明出处:http://blog.csdn.net/zhaoyanjun6/a ...
- 【Android】Android网络编程概述
Android网络编程概述 原文来自:http://blog.csdn.net/kieven2008/article/details/8210737 首先,应该了解的几个问题: 1)Android平台 ...
- android 开关数据连接电脑,Android网络数据开关用法简单示例
本文实例讲述了Android网络数据开关用法.分享给大家供大家参考,具体如下: api中没有开放这部分接口.因此大家可以使用这个方法,true时打开,false关闭.目前对G网手机可以. public ...
最新文章
- 016_continue和break语句
- hadoop streaming部分问题总结
- NYOJ 139 我排第几个?
- TensorFlow实现Unet遥感图像分割
- Linux进程O(1)调度算法,面试必考哦
- 题解---2015年浙江理工12月校赛
- kafka 主动消费_Kafka消费组(consumer group)
- Codeforces ----- Kefa and Dishes [状压dp]
- 这儿有一个使你网页性能提升10倍的工具
- iOS UIDatePicker
- 步进电机 步进电机驱动器
- 学生使用腾讯云搭建个人网站
- python用一行代码画个迷宫_[代码全屏查看]-用turtle不断的画回字迷宫
- python建立窗口并美化_Python GUI教程(十六):在PyQt5中美化和装扮图形界面
- 【java.lang.ref】FinalReference Finalizer FinalizerThread
- 解决CMD命令行窗口不显示颜色问题python
- flash按钮html层遮罩
- 计算机专业基础综合先看什么,先别学了,你的考研初试科目已经变了!!!
- 9000字通俗易懂的讲解下Java注解,你还看不明白?,实战分析SpringBoot整合JSON
- 利用Dynamo 获取面(Surface)的UV坐标系方向
热门文章
- python爬虫实现hdu自动交题
- 开源遥感软件(未完待续)
- 移动端h5不支持font-family里面的楷体、微软雅黑等字体
- 【Android】安卓开发中的MVP架构模式与应用实例
- Duplicate Net Names Wire Net......
- Android:JNI调用C++自定义类的详细方法
- python爬取统计局数据_利用Python抓取行政区划码的方法
- WordPress影视主题模板电影下载网站源码
- 如何破解(免积分)下载百度文库+豆丁收费文档
- 贪吃蛇c加加代码_C语言/C加加编程学习之贪吃蛇小游戏源代码