WebRTC实时通话的过程
在正式聊AudioTrack与AudioRecord之前,我们先简单回顾下 WebRTC 实现音视频实时通话的处理过程。对这个过程的了解,可以帮助我们清楚明了地知道AudioTrack与AudioRecord在整个处理过程中的位置。
WebRTC终端:负责音视频采集、编解码、NAT 穿越、音视频数据传输。
信令服务器:负责信令处理,如加入房间、离开房间、媒体协商消息的传递等。
STUN/TURN服务器:负责获取 WebRTC 终端在公网的 IP 地址,以及 NAT 穿越失败后的数据中转。
-
当一端进入房间前会先检测自己的设备是否可用。如果此时设备可用,则进行音视频数据采集;
-
在获取音视频数据就绪后,WebRTC 终端要发送 "加入" 信令到 Signal 服务器。Signal 服务器收到该消息后会创建房间。终端将创建RTCPeerConnection并将采集到的音视频数据通过 RTCPeerConnection 对象进行编码,最终通过 P2P 传送给对端;
-
当 P2P 穿越失败时,为了保障音视频数据仍然可以互通,则需要通过 TURN 服务器(TURN 服务会在后面文章中专门介绍)进行音视频数据中转。
-
当音视频数据来到对端后,对端首先将收到的音视频数据进行解码,然后进行播放。
而在采集环节我们就需要用到AudioRecord来进行音频采集;在播放环节我们就需要用到AudioTrack来进行播放。
AudioRecord
AudioRecord参数
AudioRecord是Android平台上用于录制音频的类,它的构造函数有多个参数,每个参数都有不同的作用。以下是常用的参数及其作用:
- audioSource:指定音频源,可以是麦克风、电话线路、语音识别等。常用的选项有:
- MediaRecorder.AudioSource.MIC:麦克风作为音频源。
- MediaRecorder.AudioSource.VOICE_COMMUNICATION:语音通信作为音频源。
- MediaRecorder.AudioSource.DEFAULT:默认音频源。
-
sampleRateInHz:指定录制的音频采样率,即每秒钟采集的音频样本数。常用的采样率有 44100Hz(CD质量)、22050Hz(FM广播质量)和 16000Hz(电话质量)等。
-
channelConfig:指定音频通道配置,即录制的音频是单声道还是立体声。常用的选项有:
- AudioFormat.CHANNEL_IN_MONO:单声道。
- AudioFormat.CHANNEL_IN_STEREO:立体声。
- audioFormat:指定音频数据的格式,即每个音频样本的位数和编码方式。常用的选项有:
- AudioFormat.ENCODING_PCM_8BIT:8位PCM编码。
- AudioFormat.ENCODING_PCM_16BIT:16位PCM编码。
- AudioFormat.ENCODING_PCM_FLOAT:浮点型PCM编码。
-
bufferSizeInBytes:指定音频缓冲区的大小,即一次读取的音频数据量。可以通过AudioRecord的静态方法getMinBufferSize()来获取建议的最小缓冲区大小。
-
audioRecordListener:指定一个监听器,用于监听音频录制过程中的状态变化和错误信息。
这些参数的选择取决于你的具体需求,例如录制的音频质量、采样率、通道数等。根据不同的参数组合,可以实现不同的录音效果和功能。
音频源
不同的音频源会对录制的音频质量和环境适应性产生影响。以下是一些常见的音频源及其影响:
-
麦克风 (MediaRecorder.AudioSource.MIC):使用麦克风作为音频源可以录制环境中的声音,适用于大多数录音场景,如录屏。但是,麦克风也会捕捉到周围的环境噪音,因此在嘈杂的环境中可能会影响录制的音频质量和清晰度(音质 );音量级别可能会受到录制设备和环境噪音的影响(音量 );需要进行后期处理和增强(软件aec),以便在播放时获得更好的音质和音量(播放 );需要打开麦克风硬件并进行实时的音频采集,可能会消耗较多的电量(功耗 );实时地捕捉环境中的声音,适用于实时语音通信等场景(实时性)。
-
电话线路 (MediaRecorder.AudioSource.VOICE_CALL):使用电话线路作为音频源可以录制电话通话的音频。这种音频源通常会经过压缩和编码,因此录制的音频质量可能会受到一定的限制(音质 );音量级别可能会受到通信网络和设备的限制(音量 );需要解码和还原(一般有硬件aec),以便在播放时恢复原始的音频质量(播放 );会利用设备的通信模块进行音频采集,功耗相对较低;可能会有一定的延迟,因为音频需要经过压缩、传输和解码等过程(实时性)。
-
默认音频源(MediaRecorder.AudioSource.DEFAULT):默认音频源通常是根据系统的配置和设备的硬件情况选择的音频源。它可以是麦克风、电话线路或其他可用的音频输入源,具体取决于设备的设置和硬件支持。
由于默认音频源是根据系统配置选择的,因此它可能会根据不同的设备和操作系统版本而有所不同。在大多数情况下,默认音频源通常是麦克风(MediaRecorder.AudioSource.MIC),因为麦克风是最常用的音频输入源。
选择合适的音频源可以提高录制的音频质量和适应不同的录音环境。在选择音频源时,需要考虑录音的目的、环境噪音水平以及所需的音频质量等因素。
硬件上的优化
麦克风音频源和电话线路音频源在硬件层面上可以有一些音质优化的技术和功能。以下是一些常见的硬件音质优化技术:
- 麦克风音频源的优化:
- 高质量麦克风:使用高质量的麦克风可以提供更清晰、准确的音频捕捉效果,减少噪音和失真。
- 噪音抑制(ANS):一些麦克风可能内置了噪音抑制技术,可以减少环境噪音对录音的影响。
- 高灵敏度:麦克风的高灵敏度可以捕捉到更细微的声音细节,提高音频的准确性和清晰度。
- 频率响应调整:一些麦克风可能具有可调节的频率响应,可以根据不同的录音需求进行调整。
- 电话线路音频源的优化:
- 音频编解码器:电话线路音频源通常使用特定的音频编解码器,如G.711、G.729等,以实现高效的音频压缩和解码,减少带宽占用和传输延迟。
- 信号处理:电话线路音频源可能会进行一些信号处理,如动态范围压缩、自适应增益控制(AGC)等,以优化音频质量和适应不同的通信环境。如出于设计考虑和用户体验的因素,电话线路音频源通常用于电话通话,而通话时需要保持一定的音量范围,以确保对话的清晰度和舒适性。为了实现这一目标,安卓系统可能会对电话线路音频源进行自适应增益控制。自适应增益控制是一种技术,可以根据输入音频的强度自动调整音量,以避免音频过于响亮或过于低音。
- 回声消除(AEC):一些电话线路音频源可能会集成回声消除技术,以减少通话中的回声干扰,提供更清晰的声音。
- 默认音频源通常不具备特定的硬件优化,因为它是根据系统配置选择的通用选项。默认音频源通常使用设备上可用的通用麦克风或其他默认音频输入设备(硬件优化)。需要注意的是,这些硬件优化可能因设备和制造商而异。在实际应用中,如果需要特定的音频质量和功能,可能需要考虑使用专门的音频设备或指定特定的音频源,而不依赖于默认音频源。
音频路由和输出设备选择的不同
电话线路音频源和麦克风音频源在音频路由和输出设备选择上有一些不同。
-
音频路由:电话线路音频源通常是指从电话线路接收的音频信号,用于电话通话。这种音频源通常会通过电话网络传输,并在通话过程中通过电话听筒或扬声器进行输出。麦克风音频源则是指通过麦克风捕捉的音频信号,通常用于录音或语音输入。这种音频源通常会通过设备的音频输入接口传输。因此,AudioRecord的电话线路音频源和AudioTrack都是走的扬声器/听筒路由,当它们初始化时会导致对方有延迟/卡顿。
-
输出设备选择:对于电话线路音频源,输出设备通常是电话听筒或扬声器。当您进行电话通话时,音频会通过电话听筒输出,或者在使用免提功能时通过扬声器输出。这样可以确保通话的隐私性和舒适性。对于麦克风音频源,输出设备可以是设备的内置扬声器、耳机或外部音频设备,具体取决于用户的设置和需求,如外部设备/耳机(有线)下一般使用麦克风音频源。
录制采样率和播放采样率
- 录制采样率是指在音频录制过程中对声音进行采样的频率。它决定了录制的音频的质量和频率范围。
- 播放采样率是指在音频播放过程中对录制的声音进行采样的频率。它决定了播放的音频的质量和频率范围。
- 录制采样率和播放采样率可以是相同的,也可以是不同的。在某些情况下,为了满足特定的需求,可以使用不同的录制采样率和播放采样率。例如,可以使用较高的录制采样率来捕捉高质量的音频,然后在播放时使用较低的播放采样率以节省存储空间和处理资源。
音频缓冲区
音频缓冲区的大小对音频录制和处理过程有重要的作用。以下是它的几个主要作用:
1.数据传输效率:音频缓冲区的大小决定了每次读取或写入的样本数量。较大的缓冲区可以容纳更多的音频数据,减少数据传输的频率,提高数据传输效率。
2.实时性能:较小的音频缓冲区可以实现更低的延迟。低延迟对于实时音频应用(如语音通话、音乐演奏等)非常重要,因为较小的延迟可以使声音更加及时响应,并提供更好的用户体验。
-
缓冲区溢出与欠流:缓冲区的大小还与缓冲区溢出和欠流有关。如果缓冲区太小,当采集或播放速率超过缓冲区处理速度时,就可能会发生缓冲区溢出或欠流。缓冲区溢出表示音频数据超出缓冲区的容量,可能导致数据丢失,而缓冲区欠流表示缓冲区中没有足够的数据供连续处理,可能导致音频中断。
-
内存占用:较大的音频缓冲区通常需要更多的内存来存储音频数据。因此,缓冲区大小的选择需要权衡内存占用和实时性能。较大的缓冲区可能会增加内存占用,但可以提供更稳定的数据传输,减少缓冲区溢出的风险。
总之,音频缓冲区的大小直接影响音频数据传输的效率、延迟和稳定性。更大的缓冲区通常可以提供更好的实时性能和稳定性,但可能会增加内存占用。根据具体应用的需求,需要适当选择合适的缓冲区大小。
录音线程执行间隔影响因素
-
采样率(Sample Rate):采样率表示每秒钟采集的音频样本数。较高的采样率可以捕捉到更多的音频细节,但也会增加数据量和处理要求。通常,常见的采样率有 44100 Hz、48000 Hz 等。
-
缓冲区大小(Buffer Size):录音需要使用一个缓冲区来存储采集到的音频数据,缓冲区大小决定了每次从录音设备读取的音频数据量。较小的缓冲区可以使录音线程更快地读取数据,但可能会增加数据丢失的风险;较大的缓冲区可以减少数据丢失的可能性,但可能会增加延迟。
-
录音线程的实现方式:录音线程可以通过不同的方式实现,例如使用定时任务、循环读取或者异步回调等方式。不同的实现方式可能会影响录音线程的调度和执行间隔。
综上所述,录音线程的执行间隔可以在一定程度上由采样率和缓冲区大小来控制。在选择采样率和缓冲区大小时,你需要权衡音频质量、资源消耗和实时性的需求,以达到最优的录音效果。另外,合理的实现方式和线程调度策略也可以对录音线程的执行间隔产生影响。
AudioTrack
AudioTrack参数
AudioTrack 是 Android 平台上用于播放音频的类,它提供了一些参数来控制音频播放的行为和性能。以下是 AudioTrack 中一些重要参数的作用:
-
Stream Type(流类型):指定音频播放的输出流类型,例如音乐、通话、铃声等。可以使用 AudioManager 提供的常量来设置相应的流类型。
-
Sample Rate(采样率):指定音频数据每秒钟采样的次数,单位为 Hz。常见的采样率有 44100 Hz、48000 Hz 等。采样率决定了音频的音质和频率范围。
3.** Channel Configuration(声道配置)**:指定音频数据的声道数,决定了音频的立体声效果。常见的声道配置有单声道(Mono)和立体声(Stereo)。
-
Audio Format(音频格式):指定音频数据的编码格式,例如 PCM(脉冲编码调制)、AAC(高级音频编解码器)等。常见的音频格式是 PCM 格式,它提供了原始音频数据的无压缩表示。
-
Buffer Size(缓冲区大小):指定音频数据的缓冲区大小,决定了一次播放的音频数据量。较小的缓冲区可以增加实时性,但可能会增加数据丢失的风险;较大的缓冲区可以减少数据丢失的可能性,但可能会增加延迟。
-
Mode(播放模式):指定 AudioTrack 的播放模式,可以是静态模式(Static Mode)或流模式(Streaming Mode)。静态模式适用于一次性播放整个音频文件,流模式适用于边录制边播放的场景。
流类型
AudioAttributes 中的 setUsage、setContentType 和 setLegacyStreamType 方法用于设置音频的用途、内容类型和传统流类型,它们有一些区别和联系:
- setUsage(设置用途):setUsage 方法用于设置音频的用途,例如表示音频是用于媒体播放、通话、通知、游戏等场景。该方法接受一个整数参数,使用 AudioAttributes.USAGE_XXX 常量来指定具体的用途。setUsage 的作用是为音频提供描述和提示,以便系统可以根据用途对音频进行合适的处理。
音量控制处理:如通话音频可能会被设置为较高的音量,以确保清晰的通话质量,而媒体播放音频可能会被设置为较低的音量,以避免过于吵闹;
路由和输出选择处理:对于通话音频,系统可能会将其路由到电话听筒或蓝牙耳机,而对于媒体播放音频,系统可能会将其路由到扬声器或有线耳机;对于游戏音频,系统可能会降低音频延迟,以确保游戏的实时性和响应性。(指定播放来源的原因,并控制导向、焦点和音量决策。)。
- setContentType(设置内容类型):setContentType 方法用于设置音频的内容类型,例如表示音频是声音效果、音乐、语音、电影等类型。该方法接受一个整数参数,使用 AudioAttributes.CONTENT_TYPE_XXX 常量来指定具体的内容类型。setContentType 的作用是告诉系统音频的内容属性,以便系统可以根据内容类型做出相应的优化和调整(音效处理)。
如对于音乐内容类型,系统可能会应用均衡器和音场效果,以增强音乐的音质和空间感。
而对于语音内容类型,系统可能会应用降噪和增强人声的处理,以提高语音的清晰度;
- setLegacyStreamType(设置传统流类型):setLegacyStreamType 方法用于设置音频的传统流类型,这是用于向后兼容的方法。传统流类型是基于旧的 AudioManager 流类型系统的,例如 STREAM_MUSIC、STREAM_VOICE_CALL 等。这些传统流类型在 Android 的早期版本中被广泛使用。调用 setLegacyStreamType 方法会将传统的流类型转换为对应的用途和内容类型,并将其应用于 AudioAttributes。
这三种方法的联系在于它们都用于描述和指定音频的特性,以便系统根据这些特性进行适当的处理和优化。setUsage 和 setContentType 是新的 API,通过使用常量来指定用途和内容类型,提供了更灵活的设置方式。而 setLegacyStreamType 是为了向后兼容而提供的方法,通过将传统流类型转换为对应的用途和内容类型来设置 Audio。
如果在使用 AudioAttributes 类的 setUsage() 和 setContentType() 方法时设置的参数不匹配,可能会导致音频行为不符合预期。这两个方法用于设置音频的用途和内容类型,它们的匹配程度可以影响音频的处理和输出。
setUsage的注意事项
setUsage() 方法用于设置音频的用途,例如通话、媒体播放、游戏等。而 setContentType() 方法用于设置音频的内容类型,例如音乐、电影、语音等。如果设置的用途和内容类型不匹配,可能会导致以下问题:
-
音频处理不准确:系统可能会根据设置的用途和内容类型来进行音频处理,例如音量控制、音效处理、音频路由和输出设备等。如果设置不匹配,可能会导致音频处理不准确,影响音质或音效效果。
-
兼容性问题:某些设备或应用程序可能会根据音频的用途和内容类型来进行适配和优化。如果设置不匹配,可能会导致兼容性问题,例如某些功能无法正常工作或音频无法播放。
-
音量问题:通信模式通常用于电话通话、语音聊天等实时通信场景,而媒体模式则用于音乐、视频播放等媒体内容的场景。通信模式的音量通常被设计为相对较小,这是为了避免在通话过程中出现过于强烈或刺耳的声音,同时也有助于保护用户的听力。
相比之下,媒体模式的音量可以相对较高,以提供更好的音频体验。这是因为在媒体播放场景中,用户通常期望更高的音量来享受音乐或视频的内容。
因此,尽管通话音频通常会被设置为较高的音量级别,但在安卓系统中,通信模式的音量可能会相对较小,而媒体模式的音量可能会相对较高。这是为了适应不同的使用场景和保护用户的听力。
采样率
采样率是指在数字音频中,每秒钟对模拟音频信号进行采样的次数。硬件设备工作的采样率固定一个值,所有音轨在 AudioFlinger 都重采样到这个采样率上,混音后输出到硬件设备,保证所有音轨听起来都不失真(track参数中的最低帧数也是基于重采样来计算的)。采样率对音质有着重要的影响,以下是一些常见的影响:
-
高频响应:较高的采样率可以更准确地捕捉高频信号。根据奈奎斯特定理,为了准确地还原一个频率为f的信号,采样率应该至少是2f。因此,较高的采样率可以更好地还原高频信号,提供更广阔的频率响应范围,从而改善音质。
-
信号还原精度:较高的采样率可以提供更多的采样点,从而提高信号的还原精度。更多的采样点可以更准确地还原原始模拟音频信号的形态和细节,使音频更加真实和清晰。
-
防止混叠失真:采样率低于信号频率的两倍时,会发生混叠失真。混叠失真是指高频信号被错误地还原成低频信号,导致音频失真。较高的采样率可以避免混叠失真的发生,提供更准确的音频还原。
-
文件大小:较高的采样率会导致音频文件的大小增加。更高的采样率意味着更多的采样点,因此会占用更多的存储空间。这对于存储和传输音频文件时需要考虑。
声道配置
声道配置是指音频信号在播放或录制过程中的声道布局和分配方式。它决定了音频信号在空间中的定位和分发方式。声道配置对于音频的立体感和空间感起着重要作用。如:
-
立体声效果:通过将音频信号分配到左右两个声道,声道配置可以创建立体声效果。立体声可以提供更丰富的音频体验,使听者能够感受到音频信号的左右分布和定位。
-
空间感和环绕声效果:通过将音频信号分配到多个声道,如5.1声道或7.1声道,声道配置可以创建更加逼真的空间感和环绕声效果。这种配置可以模拟音频信号在听者周围环绕的感觉,提供更沉浸式的音频体验。
-
分离和定位:通过将不同的音频元素分配到不同的声道,声道配置可以实现音频元素的分离和定位。例如,在音乐中,不同乐器的声音可以分配到不同的声道,使听者能够清晰地分辨出每个乐器的位置和声音。
-
兼容性和适应性:声道配置还可以用于适应不同的音频设备和系统。不同的设备和系统可能支持不同数量的声道配置,通过选择适当的声道配置,可以确保音频在不同设备上的兼容性和适应性。
播放模式
静态模式适用于一次性播放整个音频文件的场景,其主要特点是将整个音频文件加载到内存中进行播放。在静态模式下,音频文件会被完整地读取到内存中,然后进行解码和播放。这种模式适用于音频文件较小且播放过程中不需要实时的数据处理或修改的情况。
静态模式的优点是播放过程中不会有中断或延迟,可以提供稳定的音频播放体验。由于整个音频文件已经加载到内存中,可以随时进行回放、暂停、快进等操作,而不需要等待数据的加载或解码。
相比之下,流模式适用于边录制边播放的场景,其主要特点是音频数据的实时处理和传输。在流模式下,音频数据会逐段地进行录制或播放,而不是一次性加载整个文件。这种模式适用于需要实时处理音频数据、边录制边播放或需要处理大型音频文件的情况。
流模式的优点是可以实现实时的音频处理和传输,适用于需要边录制边播放的场景。由于音频数据是逐段传输的,可以在录制过程中进行实时的数据处理、分析或修改,同时也可以在播放过程中实现边解码边播放的效果。
AudioTrack卡顿影响因素
Buffer Size
指定音频数据的缓冲区大小,决定了一次播放的音频数据量。较小的缓冲区可以增加实时性,但可能会增加数据丢失的风险;较大的缓冲区可以减少数据丢失的可能性,但可能会增加延迟。
较大的缓冲区可能会增加音频播放的延迟,主要原因有两个方面:
-
缓冲区填充时间:较大的缓冲区需要更多的时间来填充音频数据。在音频播放时,音频数据需要先从存储设备读取到缓冲区中,然后才能被播放器读取并播放。当缓冲区大小较大时,填充整个缓冲区的时间也会相应增加。这会导致音频播放器在开始播放前需要等待更长的时间,从而增加了延迟。
-
缓冲区处理时间:较大的缓冲区中的音频数据量增加,播放器需要更多的时间来处理这些数据。播放器读取缓冲区中的音频数据,并将其传递给音频设备进行播放。当缓冲区大小较大时,处理整个缓冲区的时间也会相应增加。这会导致音频数据在从缓冲区到达音频设备之间的传输时间变长,进而增加了延迟。
如果数据缓冲区分配得过小,那么播放声音会频繁遭遇 underrun,underrun 是指生产者(AudioTrack)提供数据的速度跟不上消费者(AudioFlinger::PlaybackThread)消耗数据的速度,反映到现实的后果就是声音断续卡顿,严重影响听觉体验。
一般使用AudioTrack,会使用AudioTrack.getMinBufferSize来获取一个最小的buffer size值,用于创建AudioTrack的实例;同时,会用这个size值来创建一个byte数组的buffer,用于从文件读取数据,这个buffer的大小一般取size/4。
但是size/4有可能会得出一个不能被2整除的数字,如果用这个数字创建了一个byte数组来读取文件并传入AudioTrack,就会产生卡顿、噪音等。所以创建buffer时需要确保把size/4的值转换成可被2整除的数(充分利用基于采样的数字信号处理算法:快速傅里叶变换算法FFT需要)。
通信模式下的资源/硬件竞争
表现:AudioRecord在使用AudioSource.VOICE_COMMUNICATION音频源时,第一次调用startRecording和释放录音器都会令audiotrack卡顿一下(无论双方谁先初始化)。
原因:在通话模式下,AudioRecord和AudioTrack通常会存在资源竞争。
硬件竞争
在一些手机上只有一个硬件音频管道,该管道同时处理输入(AudioRecord)和输出(AudioTrack)。在通话模式下,由于实时性的要求,同时进行输入和输出会导致硬件竞争,从而可能导致以下问题:
-
延迟增加:由于竞争问题,输入和输出的处理时间可能会增加,导致应用程序的响应时间延迟增加。
-
音频质量降低:当AudioRecord和AudioTrack同时使用时,可能会出现音频丢失或音频质量降低的情况。
-
系统崩溃:在极端情况下,硬件竞争可能导致应用程序崩溃或系统死锁。
焦点竞争
在使用 AudioSource.VOICE_COMMUNICATION 初始化 AudioRecord 时,这意味着应用程序正在请求获取通话音频焦点。通话音频焦点通常具有较高的优先级,因为通话是一种需要优先处理的重要音频场景。当一个应用正在通过 AudioSource.VOICE_COMMUNICATION 或 AudioSource.CAMCORDER 捕获音频时,必须阻止任何其他应用(无障碍服务除外)捕获音频。不过,当一个应用正在通过 AudioSource.VOICE_COMMUNICATION 捕获音频时,如果其他应用是具有权限 CAPTURE_AUDIO_OUTPUT 的特权(预安装)应用,则可以捕获语音通话。(source.android.com/docs/compat...)
当您的应用程序使用 AudioSource.VOICE_COMMUNICATION 初始化 AudioRecord 并请求音频焦点时,系统会根据应用程序的音频焦点请求和优先级来调整音频策略。这可能会导致其他正在播放的 AudioTrack 被暂停或降低音量。
这种行为是为了确保通话音频的清晰度和优先级,因为通话音频通常需要更高的优先级,以便用户能够清楚地听到对方的声音。
资源竞争
在使用AudioSource.VOICE_COMMUNICATION音频源时,音频处理算法的初始化通常发生在调用startRecording方法时。当您调用startRecording时,系统会根据所选的音频源进行初始化,并应用适当的音频处理算法。
这些音频处理算法旨在优化语音通信的质量和清晰度,例如降噪、回声消除和自动增益控制等。这些算法可能会对音频数据进行实时处理,以提供更好的语音通信体验。
因此,当您第一次调用startRecording时,系统可能需要进行音频处理算法(3A)的初始化和准备工作,这可能需要一些时间和资源。在这个过程中,可能会导致音频资源竞争,从而导致卡顿。(回声消除的启用可能会增加音频处理的计算负载,从而影响到整个音频系统的性能,包括Audiotrack的缓冲区管理和数据读取等。)
在一般情况下,调用stop方法不会重置音频处理算法。stop方法用于停止录音,并释放相关的资源,但通常不会影响音频处理算法的状态。
如果您希望确保音频处理算法在每次调用startRecording时都重新初始化,您可以尝试在调用stop之后调用release方法来释放录音器对象,并在下次调用startRecording之前重新创建一个新的录音器对象。这样可以确保每次调用startRecording时都进行完整的初始化过程。
媒体等模式不竞争
在媒体模式下,通常不会存在和通话模式下类似的硬件竞争问题。
在媒体模式下,音频采集和音频播放可以同时进行,通常不会引起严重的硬件竞争风险。媒体模式下的设备通常有专门的音频处理子系统(dsp--这种专门的音频处理子系统可以独立于主处理器(如CPU或GPU)运行,从而提供更高效和稳定的音频处理性能。通过将音频处理任务交给专用硬件,可以减少对主处理器的负载,提高系统的整体性能和响应速度。),能够支持并行的音频采集和播放。因此,您可以同时使用AudioRecord进行录制,同时使用AudioTrack进行播放,而不会引起硬件竞争问题;若录音器使用通话模式,播放器无论使用那个模式也会存在硬件竞争关系。
多个AudioTrack场景下的竞争
当您重启一个 AudioTrack 实例时,它会释放其底层的音频资源,并重新初始化这些资源。这意味着该实例需要重新连接到音频引擎,并重新分配和初始化它所需的音频资源。这个重新分配和初始化的过程可能需要一些时间,并且可能会引起一小段时间内的中断或延迟。在此过程中,其他正在运行的 AudioTrack 实例可能会受到影响,因为音频引擎可能需要重新分配和重组整个音频系统的资源。
后台休眠
sleep() 方法的休眠时间自增:操作系统可能会自动延长线程的休眠时间,使得线程在后台休眠更长的时间,从而减少对电池的耗电量。不仅sleep,所有用到系统休眠策略的相关计时器都会存在该问题。
备用方案:
- 使用并发库的计时器。
用CountDownLatch 的 await() 方法的超时策略代替sleep。
CountDownLatch 的 await() 方法是通过等待其他线程完成特定操作来实现的。即使应用程序在后台运行,CountDownLatch 仍然会保持原来的超时策略不变,这是因为 CountDownLatch 的超时策略是由 Java 并发库和线程调度机制来控制的(通过挂起线程),而不是由操作系统来控制,即使线程进入后台,CountDownLatch 仍然会准确计算等待超时的时间。具体实现是通过LockSupport.parkNanos(),方法的实现是依赖于操作系统的底层线程阻塞原语,如 pthread_cond_timedwait()(在Unix-like系统上)或 WaitForSingleObject()(在Windows上)等。具体实现会根据不同的操作系统有所差异。pthread_cond_timedwait() 是 POSIX 线程库中用于在条件变量上等待指定时间的函数。它是操作系统提供的原生函数,并非是操作系统直接操控的。不直接参与调度行为,因此不受休眠策略的直接影响,而sleep() 函数是通过操作系统提供的休眠机制来实现的,休眠策略会直接影响操作系统对于 sleep() 函数的调度行为,以及对于休眠时间的处理和精度控制,休眠策略一般是由操作系统的调度器来控制和决定的,它决定了线程或进程在何时放弃CPU以及被重新调度的方式。
但是,CountDownLatch 的 await() 方法表现比sleep好些,但也会在后台播放时卡顿。这是因为LockSupport.parkNanos()本身不受时间片轮转影响,但受操作系统调度影响:由于线程调度是由操作系统控制的,线程在被唤醒之前可能会被暂停,需要等待其他线程的执行才能继续执行。因此,调用LockSupport.parkNanos()方法后,线程在被阻塞期间可能会受到时间片轮转的影响而导致唤醒的延迟。
- 使用对象锁进行挂起唤醒操作
计时都会不准,干脆不计时,用对象锁阻塞播放线程,合流线程释放锁。这方法消耗cpu更低但要注意锁的使用。
解码和播放不同线程
录音线程(合流发远端)消费缓存,播放线程生产缓存。
本地播放卡顿:生产数据间隔时间太长(如缓存容量阈值触及后的等待、缓存快速触及上限的规避等原因下的控制间隔);生产跟不上消耗(录音线程短时间如1毫秒内执行了多次,而播放线程只执行1次后就等待录音线程下一次的执行---录音线程驱动播放线程场景)。
远端播放卡顿:录音线程比播放线程执行速度快得多,没有数据可消费;录音线程比播放线程执行速度慢得多(如放置后台,录音线程优先级降低),缓存数据溢出,引起丢帧,远端先延迟后卡顿。
CPU过高。
时间片分配导致的两条线程执行速度慢,生产与消费时间间隔变长,audiotrack写入时间间隔变长,导致播放间隔变长,引起感官卡顿。
AudioManager 的模式切换
切换 AudioManager 的模式 (mode) 可能会对 AudioTrack 的播放产生一些影响(切换音频路由)。具体的影响取决于设备、驱动程序以及您的应用程序的实现方式。
在切换到 MODE_NORMAL 模式时,可能会发生以下情况:
杂音或卡顿:由于 MODE_NORMAL 模式通常不要求实时的音频处理,设备可能会调整音频处理算法或设置来适应正常媒体播放场景。这可能导致一些延迟、杂音或卡顿。
音量变化:不同的模式可能会有不同的音量设置。当切换到 MODE_NORMAL 模式时,音量可能会有所变化。您可能需要适当地重新设置或调整音频音量。
音频效果设置:某些音频效果和音频处理功能(如均衡器、回声消除等)可能在不同模式下具有不同的设置。切换到 MODE_NORMAL 模式后,可能会丢失一些特定于通话模式的音频效果。
为了最大程度地减少切换模式可能引起的问题,建议在切换模式前做以下操作:
-
如果可能,先停止 AudioTrack 的播放,然后再切换模式。
-
确保在切换模式之后正确恢复音频资源的状态,包括音频焦点和应用程序的音频设置。
-
监听 AudioManager 的状态变化回调,以便在模式切换后做出相应的调整。
需要注意的是,不同设备和不同 Android 版本的行为可能略有不同。因此,在进行模式切换时,最好进行充分的测试以确保音频播放的稳定性和一致性。
AEC处理
-
通信模式下,播放歌曲同时录音,远端歌曲会卡。这是因为回声消除在杂音多时处理不够好,导致声音忽大忽小或者卡顿。
-
对着顶部说话比底部说话声音会更大---部分手机有多个麦克风,分别位于顶部和底部,底部麦克风由于靠近扬声器,在通信模式下,受扬声器声音影响,回声消除处理比较差,会导致声音忽大忽小或者比较小。
当从 AudioRecord 中读取音频数据并将其写入 AudioTrack 时发生变声可能有几个原因:
采样率不匹配: AudioRecord 和 AudioTrack 的采样率必须相同,以确保音频数据能够正确地传输和播放。如果这两个对象使用不同的采样率,音频数据将会被拉伸或压缩,导致变声效果。请确保 AudioRecord 和 AudioTrack 的采样率都设置为相同的值。
声道配置不匹配: AudioRecord 和 AudioTrack 的声道配置也必须相同,以确保声道数与数据格式匹配。如果声道配置不匹配,例如一个对象使用 CHANNEL_IN_MONO 而另一个对象使用 CHANNEL_OUT_STEREO,会导致声音的失真和变声。请确保 AudioRecord 和 AudioTrack 的声道配置相同。
数据格式不匹配: AudioRecord 和 AudioTrack 的数据格式必须一致,以确保正确的解析和播放音频数据。如果数据格式不匹配,可能会导致音频数据被错误地解释,从而产生变声效果。请确保 AudioRecord 和 AudioTrack 的数据格式一致。
数据处理错误: 变声可能是由数据处理过程中的错误引起的,例如音频数据的处理算法可能不正确,导致数据变形。检查你在处理音频数据时是否有任何错误或不正确的逻辑。