系统由一台播放器(服务器)和多台接收器(客户端)构成一个wifi直连的群组,服务器是群主(GO),客户端是组员(GC)。使用Wifi直连(p2p)构造一对多的系统是很方便实现的。
系统构成请参见图:wifi_p2p_一对多数据流程

上图只画出了音频数据的流程,对于服务器与客户端的文字信息交互没有表示,这部分内容看一下代码就明白了。
为了确保服务器作为群主(GO)角色出现在群组中,服务器启动时立即主动创立群组。
客户端启动后通过搜索功能发现服务器,并与之建立连接,客户端可以有多个。
通信系统采用NIO实现非阻塞的socket通信,一来有较好性能,二来避免了多用户复杂线程处理。

NIO系统的处理要点

NIO不同于传统的阻塞式socket通信方式,无法使用功能强大的ObjectOutputStream和ObjectInputStream,将对象以数据流方式发送或接收。
因此必须自己实现传输数据的打包和解包。
本系统传输的数据有两类,文字数据和音频数据。文字数据用于向对方发送消息,音频数据以PCM形式传送声音。

客户端向服务器发送的只有文字数据,直接使用charset.decode解码获得String。

为了便于NIO传输,服务器发出的两种数据都使用如下相同格式的数据包:

  • 包类型 1个字节,用以区分文字数据和音频数据。
  • 包长度 4个字节,用一个整数表示数据包的长度。
  • 数据包,长度不等的文字数据或音频数据。

解决NIO的粘包拆包问题

粘包拆包问题的发生是NIO数据收发缓冲机制造成的,如果数据包边界超过缓冲区边界,就会发生拆包;如果数据包边界未达到缓冲区边界、且后续数据包也已到达,就会发生粘包。
服务器输出数据打包的过程很简单,由ConnectIntentService实现,使用SocketChannel.write方法依次把包类型、包长度和数据包写入TCP网络(wifi p2p)。
客户端接收数据解包的过程稍显复杂,在ConnectRunnable中实现。
解包过程使用了两个buffer,inputBuff(输入区)和cacheBuff(缓存),inputBuff用于从socketChannel中读取数据。
由于inputBuff中得到数据包有可能是不完整的,因此需要用cacheBuff缓存数据包,cacheBuff中数据包内容全部收妥后,提交给消费者。
消费者根据包类型,将数据包解析为文字数据或音频数据。
用流程图表示比较直观,请参见图:NIO解包流程

以下对重点模块做一些简单说明。

公共模块 p2p_comm

PcmTransferData

这是一个继承了Serializable的数据类,用来表示一帧音频数据。数据产生于服务器的解码器,注入socket,经过wifi p2p,传送到客户端,它包括三个成员:

  • sampleRateInHz 采样率
  • pcmData 音频pcm数据
  • frameCount 帧计数

DirectBroadcastReceiver

这是一个专用于Wifi直连(p2p)功能的广播接收器,服务器和客户端都要用到。这里定义了几个wifi p2p事件的相应。

  • WIFI_P2P_STATE_CHANGED_ACTION
    wifi p2p状态发生变更,根据状态可以判断本机wifi p2p功能是否可用。
  • WIFI_P2P_PEERS_CHANGED_ACTION
    wifi p2p成员列表发生变化,此时可以请求获得成员列表。
  • WIFI_P2P_DISCOVERY_CHANGED_ACTION
    wifi p2p的成员搜索发现过程发生变化,由停止转为启动,或者由启动转为停止。
  • WIFI_P2P_CONNECTION_CHANGED_ACTION
    wifi p2p的连接状态发生变化,如果是连接成功状态,可以请求获得连接情报。
  • WIFI_P2P_THIS_DEVICE_CHANGED_ACTION
    本机设备发生变化,如果此时本机设备可用,可以请求获得本机设备情报。

IDirectActionListener

这是在服务器和客户端都必须实现的wifi p2p事件相应接口。服务器和客户端的实现方法有所不同。

  • onWifiP2pEnabled
    wifi p2p功能可用时触发。
  • onConnectionInfoAvailable
    wifi p2p连接情报有效时触发。
  • onDisconnection
    wifi p2p连接断开时触发。
  • onSelfDeviceAvailable
    本机设备有效时触发。
  • onPeersAvailable
    wifi p2p成员列表有效时触发。
  • onP2pDiscoveryStopped
    wifi p2p成员搜索发现过程停止时触发。

播放器(服务器) audio_player

ConnectIntentService

wifi p2p连接建立后,这个IntentService主要负责实现非阻塞的socket通信。
这里涉及三个线程:

  • ConnectIntentService所在的主线程,建立ServerSocketChannel,提供客户端连接的公共服务平台,接收每一个客户端发送的消息,然后通过监听器发送到MainViewModel。
  • msgOutputThread消息输出线程,通过OutputMsgHandler接收来自MainViewModel的消息,打包发送给相关客户端。
  • outputAudioThread音频输出线程,使用同步阻塞队列SynchronousQueue的take()方法取得解码后的PCM数据
    (PCM数据是在DecoderCallback中用SynchronousQueue的put()方法注入同步阻塞队列),打包以后发送给每一个已连接的客户端。

DecoderCallback

这是MP3音频解码器所需的回调。在PlayThreadHandler中将其注入解码器。主要相应以下事件:

  • onInputBufferAvailable,当解码器输入缓存可用时,把MediaExtractor解析后的MP3数据填入解码器输入缓存,再把输入缓存推入解码队列。
  • onOutputBufferAvailable,当解码器输出缓存可用时,从输出缓存中取得解码后的PCM数据。
    PCM数据有两个出路,① 用SynchronousQueue的put()方法注入同步阻塞队列 ② 供本地AudioTrack.write()使用。
    随后释放解码器输出缓存。

DummyPlayerRunnable

在本地播放音频所需playThread的Runnable实体,在其中使用Looper,循环调用PlayThreadHandler。

PlayThreadHandler

接收并执行来自MainViewModel的与本地音频播放有关的各种指令。

  • PLAY
  • PAUSE
  • STOP
  • MUTE
  • RESUME
  • UNMUTE

MainViewModel

安卓体系结构组件之一,集成处理逻辑和数据。主要有以下处理:

  • 建立wifi p2p群组,实例化并注册DirectBroadcastReceiver
  • 启动playThread
  • 实现wifi p2p事件相应接口
  • 执行MainFragment发出的各种命令,主要与本地音频播放相关。向MainFragment发出的信息用LiveData实现。
  • 监听处理来自ConnectIntentService的各种消息,包括本地消息和来自客户端的远程消息。

MainFragment

用户界面虽然有wifi p2p建组和删组按钮,通常不需要使用,系统在初始化时已经完成wifi p2p建组。
来自MainViewModel的消息,用观察者模式处理。
为了减少if-else逻辑,对于本地音频播放器按钮采用状态模式处理,缺点是增加了不少状态类。

接收器(客户端)

ConnectRunnable

客户端socket线程的执行部分,用NIO机制实现信息接收和发送。这里用到两个线程:

  • ConnectRunnable所在的主线程,用ThreadHandler接收来自MainViewModel的文字消息,经NIO发送给服务器。
  • 读取服务器信息的inputThread,完成服务器信息的解析,经过MainViewModel送达消费者。

PcmPlayer

这是来自服务器的音频数据的消费者,用AudioTrack播放音频。
该类初始化时,可以控制播放器的左右声道。

MainViewModel

同服务器MainViewModel功能相似。

可能改进的方向

  • 今后使用更高效的AIO代替NIO,但是低版本android不支持AIO,考虑兼容范围目前只能使用NIO。
  • 如果今后线程增加较多,考虑使用协程代替线程。
  • 由于数据传输过程使用各种buffer,每个客户端播放的音频会有不同的时延,因此需要一种同步机制以控制音频播放时延。也许使用UDP协议有可能减少音频时延。

源码仅供参考

如有问题、BUG、指摘,请联系:wxson@126.com

Wifi直连(p2p)一对多音频传输相关推荐

  1. 用wifi直连(p2p)实现遥控照相

    现在具有wifi遥控功能的照相机已经很多,Canon和Nikon都有.最近两三年发布的新相机几乎都支持WIFI传图和遥控功能.本文介绍用wifi p2p方法实现两台android手机遥控拍摄的方案. ...

  2. 基于MT7688AN模块开发板WiFi路由方案无线音频传输WiFi音箱测试

    无线路由解决方案无损WiFi音频传输测试 基于MT7688AN模块开发板WiFi路由方案无线音频传输WiFi音箱测试 L107物联网路由器模块是基于联发科MT7688或MT7628芯片组.该模块只需要 ...

  3. Android系统wifi之p2p(wifi直连)配置文件权限导致应用不能使用wifi问题

    这个平台的wifip2p也就是wifi Direct(wifi直连)其实是走的hostap功能. 一.问题描述 1.设置->网络->无线网络->,打开wifi开关. 2.在开启无线直 ...

  4. Android WiFi直连 双向通信

    原文地址:https://blog.csdn.net/VNanyesheshou/article/details/82316436 DEMO下载:http://www.demodashi.com/de ...

  5. [转载]从Android源代码来看WiFi直连

    什么是WiFi直连 通俗点说,它可以不通过网络,也不通过蓝牙,只要两台设备都支持WiFi直连,打开WiFi,不用连接任何WiFi,就可以进行信息的传输(请忽略下面两张图中的WiFi连接标志,因为其与W ...

  6. 智能家居与SmartConfig技术,WI-FI直连

    -- 百度AI商业化落地的速度与能力:  1.小度智能音箱Pro:2.小度语音车载支架:3.搭载百度DuerOS的华为平板M5青春版: > 智能家居 智能家居,所有的设备都是通过Wifi连接自己 ...

  7. 情结- WiFi 直连

    记得刚到公司的时候,大家都在研究 WiFi Directe,我主要负责看 WiFi Directe (WiFi - P2P)协议,当时是第一次接触,头很大! 但是还是坚持看下来了,并且发现这个玩意很好 ...

  8. WiFi Direct即P2P协议学习笔记

    文章目录 前言 一.P2P的模型 1.1 P2P的组成 1.2 P2P的拓扑 1.3 P2P并发模式 1.4 功能和服务 1.4.1 基本功能和服务 1.4.2 P2P的特殊功能和服务 1.4.3 P ...

  9. A2DP和AVRCP蓝牙音频传输协议

    1.A2DP全名是Advenced Audio Distribution Profile蓝牙音频传输模型拹定.A2DP 规定了使用蓝牙非同步传输信道方式,传输高质量音乐文件数据的拹议堆栈软件和使用方法 ...

最新文章

  1. ubuntu10.10---用Apache+mod_wsgi部署python程序
  2. JAndFix: 基于Java实现的Android实时热修复方案
  3. python 读取数据出现UnicodeDecodeError:: 'utf-8' codec can't decode byte 0xc8 in position 0: invalid contin
  4. 如何使用 IntelliJ IDEA 2017 配置PHP开发环境 及项目搭建
  5. 使用Axis2方式发布webService实例说明
  6. 年轻人还在讨论要不要躺平时,阿里已经注册了“躺平”商标
  7. MySQL 得到数据库的大小
  8. 【5分钟 Paper】Deep Recurrent Q-Learning for Partially Observable MDPs
  9. Javascript:使用setAttribute设置某个标签节点display为none仍然显示
  10. python 日期 格式转换 英文_python中各种时间格式的转换
  11. python调用高德地图地理编码/逆地理编码
  12. SpringBoot海景房出租管理系统+代码讲解
  13. 细数2018世界杯上的黑科技
  14. 利用鸿蒙开发新闻头条
  15. RA-GCN:Richly Activated Graph Convolutional Network for Robust Skeleton-based Action Recognition
  16. 卡尔曼滤波模型及Matlab模型建立
  17. 内网代理穿透1:nc代理穿透
  18. 深度学习相关公开数据集
  19. 优思学院:六西格玛证书有用吗?有什么方法获取六西格玛证书?
  20. 【实例分割】1、SOLOv1: Segmenting Objects by Locations_2019

热门文章

  1. AC日记——逃出克隆岛 (bfs)
  2. java毕业设计地铁舆情管理系统Mybatis+系统+数据库+调试部署
  3. 20230111英语学习
  4. vue--elementUI
  5. 1055 - Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'cre
  6. 【正点原子FPGA连载】 第三十五章双目OV5640摄像头HDMI显示实验 摘自【正点原子】DFZU2EG/4EV MPSoC 之FPGA开发指南V1.0
  7. 【区块链】激活数字资产的蓝海,布比区块链打造新一代价值流通网络,以让数字资产自由流动起来
  8. C++/R 期末冲刺3h
  9. 微信小程序学习记录(二)MQTT连接阿里云
  10. mysql5.7下载,离线安装过程