首页_恒达注册_恒达娱乐_登陆平台
首页_恒达注册_恒达娱乐_登陆平台
全站搜索
 
 
新闻详情
 
当前位置
实时Android语音对讲系统架构
作者:管理员    发布于:2022-09-10 10:18    文字:【】【】【
       

  实时Android语音对讲系统架构。AudioRecorder和AudioTracker是Android中获取实时音频数据的接口。在网络电话、语音对讲等场景中,由于实时性的要求,不能采用文件传输,因此,MediaRecorder和MediaPlayer就无法使用。

  其中,audioSource表示录音来源,在AudioSource中列举了不同的音频来源,包括:

  sampleRateInHz表示采样频率。音频的采集过程要经过抽样、量化和编码三步。抽样需要关注抽样率。声音是机械波,其特征主要包括频率和振幅(即音调和音量),频率对应时间轴线,振幅对应电平轴线。采样是指间隔固定的时间对波形进行一次记录,采样率就是在1秒内采集样本的次数。量化过程就是用数字表示振幅的过程。编码是一个减少信息量的过程,任何数字音频编码方案都是有损的。PCM编码(脉冲编码调制)是一种保真水平较高的编码方式。在Android平台,44100Hz是唯一目前所有设备都保证支持的采样频率。但比如22050、16000、11025也在大多数设备上得到支持。8000是针对某些低质量的音频通信使用的。

  channelConfig表示音频通道,即选择单声道、双声道等参数。系统提供的选择如下:

  与AudioRecord类似,AudioTrack的构造器方法依然有很多需要选择的参数。其中,streamType表示音频流播放类型,AudioManager中列出了可选的类型如下:

  Speex是由C语言开发的音频处理库,在Android中使用,需要通过JNI来调用。因此,对NDK开发不熟悉的朋友,可以先了解下文档:向您的项目添加C 和 C++ 代码()。

  完成上述配置之后,正式开始在Android中使用Speex进行音频编解码。主要包括以下步骤:

  1、下载Speex源码。推荐使用Speex 1.2.0稳定版,由于目前Speex 已不再继续维护,官方建议使用Opus。但在某些场合,使用Speex已然足够满足需求。

  8、最后,在Android中通过Java去调用encode方法进行音频数据的编码。

  这里设置了每帧处理160个short型数据,压缩比为5,每帧输出为28个byte型数据。Speex压缩模式特征如下:

  数据包要经过Record、Encoder、Transmission、Decoder、Play这一链条的处理,这种数据流转就是对讲机核心抽象。鉴于这种场景,本文的实现采用了责任链设计模式。责任链模式属于行为型模式,表征对对象的某种行为。

  创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。

  结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

  行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

  责任链设计模式的使用场景:在责任链模式里,很多对象里由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织和分配责任。下面来看下具体的代码:

  以Recorder、Encoder、Sender为例说明输入侧数据的处理(这里仅列出部分代码,具体代码参考github)

  Encoder的下一个处理节点是Sender,在Sender的handleRequest()方法中,通过多播(组播),将音频编码数据发送给局域网内的其它设备。

  即:在界面初始化AudioInput对应的线程的时候,就完成这些类的实例化,并指定Recorder的下一个处理者是Encoder,Encoder的下一个处理者是Sender。这样使得整个处理流程非常灵活,比如,如果暂时没有开发编解码的过程,在Encoder的handleRequest()方法中直接指定下一个处理者:

  在Activity中,分别申明输入、输出Runable、线程池对象、界面更新Handler:

  ExecutorService接口继承自Executor 接口,它提供了更丰富的实现多线程的方法,比如,ExecutorService提供了关闭自己的方法,以及可为跟踪一个或多个异步任务执行状况而生成Future 的方法。 可以调用ExecutorService 的shutdown()方法来平滑地关闭 ExecutorService,调用该方法后,将导致 ExecutorService停止接受任何新的任务且等待已经提交的任务执行完成(已经提交的任务会分两类:一类是已经在执行的,另一类是还没有开始执行的),当所有已经提交的任务执行完毕后将会关闭 ExecutorService。因此我们一般用该接口来实现和管理多线程。

  Executors 提供了一系列工厂方法用于创建线程池,返回的线程池都实现了 ExecutorService接口。包括:

  1、newCachedThreadPool()创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线、newFixedThreadPool(int)

  其中,corePoolSize表示线程池中所保存的核心线程数,包括空闲线程;maximumPoolSize表示池中允许的最大线程数;keepAliveTime表示线程池中的空闲线程所能持续的最长时间;unit表示时间的单位;workQueue表示任务执行前保存任务的队列,仅保存由execute 方法提交的Runnable任务;threadFactory表示线程创建的工厂,指定线程的特性,比如前面代码中设置音频播放线程为守护线程;handler表示队列容量满之后的处理方法。

  1、如果线程池中的线程数量少于corePoolSize,即使线程池中有空闲线程,也会创建一个新的线程来执行新添加的任务;

  2、如果线程池中的线程数量大于等于corePoolSize,但缓冲队列workQueue 未满,则将新添加的任务放到 workQueue中,按照 FIFO 的原则依次等待执行(线程池中有线程空闲出来后依次将缓冲队列中的任务交付给空闲的线、如果线程池中的线程数量大于等于 corePoolSize,且缓冲队列 workQueue 已满,但线程池中的线程数量小于maximumPoolSize,则会创建新的线程来处理被添加的任务;

  首先,对于录音线程,由于对讲机用户大部分时间可能是在听,而不是说。录音线程可能长时间不用,应该让其超时回收,所以录音线程宜使用CachedThreadPool;

  其次,对于发现局域网内的其它用户的功能,该功能需要不断循环执行,相当于循环的向局域网内发送心跳信号,因此宜使用ScheduledThreadPool;

  最后,对于音频播放线程,该线程需要一直在后台执行,且播放需要串行执行,因此使用SingleThreadExecutor,并设置为守护线程,在UI线程(主线程是最后一个用户线程)结束之后结束。

相关推荐
  • 楼宇 对讲系统 的工作原理及基本结构
  • 实时Android语音对讲系统架构
  • 数字编码型楼宇可视对讲监控系统
  • 怎么分辨千兆和百兆的光模块
  • 网线如何辨别千兆还是百兆
  • 楼宇对讲系统结构智能楼宇
  • 多功能对讲系统-架构设计设计说明书pdf
  • 子午线(广州)科技有限公司
  • H3C IE4320 Comware V7系列导轨式工业以太网交换机
  • 千兆交换机网线接法
  • 脚注信息
    友情链接: