searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

AEC3类介绍 RenderDelayBuffer(一)

2023-08-30 11:42:03
124
0

类介绍 RenderDelayBuffer、RenderDelayBufferImpl

RenderDelayBuffer

用于render_buffer的维护

这个类具有以下公共成员函数和纯虚函数:

  • Create():静态函数,用于创建RenderDelayBuffer对象。

  • Reset():重置缓冲区的对齐。

  • Insert():将块插入到缓冲区中,并返回一个表示缓冲事件的枚举值。

  • PrepareCaptureProcessing():基于指定的缓冲延迟更新缓冲区,并返回一个表示特殊事件发生的枚举值。

  • HandleSkippedCaptureProcessing():在未调用PrepareCaptureProcessing()的捕获块上调用。

  • AlignFromDelay():设置缓冲延迟,并返回一个布尔值,指示延迟是否发生了变化。

  • AlignFromExternalDelay():从最近报告的外部延迟设置缓冲延迟。

  • Delay():获取缓冲延迟。

  • MaxDelay():获取最大缓冲延迟。

  • GetRenderBuffer():返回用于回声去除器的渲染缓冲区。

  • GetDownsampledRenderBuffer():返回下采样的渲染缓冲区。

  • DelayEstimatorOffset():返回可在延迟缓冲区中发生的最大非因果偏移量。

  • SetAudioBufferDelay():提供一个可选的音频缓冲延迟的外部估计。

  • HasReceivedBufferDelay():返回是否通过SetAudioBufferDelay接收到了外部的延迟估计。

该类还包含了一个嵌套的枚举类BufferingEvent,用于表示缓冲事件的不同类型。该类似乎属于webrtc命名空间

kNone:表示没有发生特殊事件。

kRenderUnderrun:表示渲染缓冲区发生了不足的情况,即需要更多的渲染数据以应对实时处理需求。

kRenderOverrun:表示渲染缓冲区发生了溢出的情况,即渲染数据超过了实时处理的需求。

kApiCallSkew:表示API调用的时间不匹配,即API调用的时间与实际数据处理的时间发生了偏差。

RenderDelayBufferImpl

继承自RenderDelayBuffer,除了override父类方法外,

类的私有成员变量和私有方法如下:

私有成员变量:

  • instance_count_:一个静态整数,用于跟踪RenderDelayBufferImpl类的实例数。

  • data_dumper_:一个指向ApmDataDumper的唯一指针,用于数据记录和调试。

  • optimization_:一个Aec3Optimization类型的常量,表示AEC3的优化选项。

  • config_:一个EchoCanceller3Config类型的常量,表示回声消除器的配置。

  • update_capture_call_counter_on_skipped_blocks_:一个bool值,表示是否在跳过的块上更新捕获调用计数器。

  • render_linear_amplitude_gain_:一个float值,表示渲染线性幅度增益。

  • delay_log_level_:一个rtc::LoggingSeverity类型的常量,表示延迟日志级别。

  • down_sampling_factor_:一个size_t值,表示下采样因子。

  • sub_block_size_:一个int值,表示子块的大小。

  • blocks_:一个BlockBuffer对象,表示音频块的缓冲区。

  • spectra_:一个SpectrumBuffer对象,表示频谱的缓冲区。

  • ffts_:一个FftBuffer对象,表示FFT的缓冲区。

  • delay_:一个absl::optional<size_t>对象,表示延迟值的可选项。

  • echo_remover_buffer_:一个RenderBuffer对象,表示回声移除器的缓冲区。

  • low_rate_:一个DownsampledRenderBuffer对象,表示降采样的渲染缓冲区。

  • render_mixer_:一个AlignmentMixer对象,用于混合渲染音频。

  • render_decimator_:一个Decimator对象,用于渲染音频的抽取和降采样。

  • fft_:一个Aec3Fft对象,用于快速傅里叶变换。

  • render_ds_:一个std::vector<float>,用于存储降采样后的渲染音频。

  • buffer_headroom_:一个int值,表示缓冲区的保留头空间。

  • last_call_was_render_:一个bool值,表示上一次调用是否为渲染调用。

  • num_api_calls_in_a_row_:一个int值,表示连续的API调用次数。

  • max_observed_jitter_:一个int值,表示最大观察到的抖动。

  • capture_call_counter_:一个int64_t值,表示捕获调用的计数器。

  • render_call_counter_:一个int64_t值,表示渲染调用的计数器。

  • render_activity_:一个bool值,表示渲染活动状态。

  • render_activity_counter_:一个size_t值,表示渲染活动计数器。

  • external_audio_buffer_delay_:一个absl::optional<int>对象,表示外部音频缓冲区的延迟。

  • external_audio_buffer_delay_verified_after_reset_:一个bool值,表示在重置后是否验证了外部音频缓冲区的延迟。

  • min_latency_blocks_:一个size_t值,表示最小延迟块数。

  • excess_render_detection_counter_:一个size_t值,表示超出渲染的检测计数器。

私有成员方法:

  • MapDelayToTotalDelay:将延迟映射到总延迟的私有方法。

  • ComputeDelay:计算延迟的私有方法。

  • ApplyTotalDelay:应用总延迟的私有方法。

  • InsertBlock:插入音频块到缓冲区的私有方法。

  • DetectActiveRender:检测活动渲染的私有方法。

  • DetectExcessRenderBlocks:检测超出渲染块的私有方法。

  • IncrementWriteIndices:增加写索引的私有方法。

  • IncrementLowRateReadIndices:增加低速率读索引的私有方法。

  • IncrementReadIndices:增加读索引的私有方法。

  • RenderOverrun:检测渲染超限的私有方法。

  • RenderUnderrun:检测渲染不足的私有方法。

RenderDelayBufferImpl::Reset()

该方法用于重置缓冲区的延迟,并清除报告的延迟。

代码逻辑如下:

  1. 重置一些状态变量,包括last_call_was_render_(上次调用是否为渲染)、num_api_calls_in_a_row_(连续的 API 调用次数)、min_latency_blocks_(最小延迟块数)、excess_render_detection_counter_(渲染超限检测计数器)。

  2. 读取位置更新:将低采样率缓冲区的读索引(low_rate_.read)初始化为写索引(low_rate_.write)的前一个子块位置

  3. 检查是否存在外部音频缓冲区的延迟,并计算合适的初始化延迟audio_buffer_delay_to_set:如果存在外部音频缓冲区延迟(external_audio_buffer_delay_),且大于3,设置的延迟值取决于外部音频缓冲区延迟减去一个头部余量(headroom,2),同时,确保设置的延迟不超过最大延迟值。否则,设置为1

  4. 调用ApplyTotalDelay(audio_buffer_delay_to_set)设置延时; delay_ = ComputeDelay();

  5. 调用ComputeDelay()计算总延时delay_ ;

  6. external_audio_buffer_delay_verified_after_reset_置为false,表示在重置后需要重新验证外部音频缓冲区延迟。

  7. 如果不存在外部音频缓冲区延迟估计,则使用默认延迟值作为初始延迟,并将渲染缓冲区延迟设置为默认延迟。

  8. 通过AlignFromDelay方法设置的延迟值。

该方法的作用是重置缓冲区的状态和延迟设置,以便在重新开始音频处理时,缓冲区的状态和延迟被正确初始化。根据是否存在外部音频缓冲区延迟估计,采取不同的延迟设置策略,以确保音频处理的准确性和一致性。

RenderDelayBufferImpl::Insert()

该方法接收block作为参数,将其插入渲染缓冲区,修改render_buffer, write_index并返回缓冲事件。

代码逻辑如下:

  1. 首先,增加渲染调用计数器render_call_counter_

  2. 如果存在延迟值delay_,则进行以下操作:

    a. 检查上一次调用是否为渲染调用。如果不是,将last_call_was_render_设置为true,并将num_api_calls_in_a_row_设置为1。

    b. 如果上一次调用是渲染调用,则将num_api_calls_in_a_row_加1,并检查是否超过了max_observed_jitter_(之前观察到的最大抖动)。如果超过了最大抖动,更新max_observed_jitter_的值,并记录日志。

    这段代码与PrepareCaptureProcessing的计算代码呼应,max_observed_jitter_代表连续插入(渲染或捕获)block的数量;

  3. 更新render_buffer各个index,如下:

  • low_rate.write -= sub_block_size(16)

  • blocks_.write += 1

  • spectra_.write -= 1

  • ffts_.write -= 1

  1. 调用RenderOverrun(),判断是否渲染超限(RenderOverrun),是则将事件设置为BufferingEvent::kRenderOverrun;否则,事件设置为BufferingEvent::kNone

  2. 检测并更新渲染活动状态render_activity。如果渲染活动状态之前为非活动状态(render_activity_false),且此render block能量大于150 * 150 * 64,则计数器render_activity_counter_ +1;render_activity_counter累计到20,render_activity置true;

  3. 调用RenderDelayBufferImpl::InsertBlock() 插入当前block;

  4. 如果事件不是BufferingEvent::kNone,则进行重置(Reset)操作。

  5. 返回事件。

RenderDelayBufferImpl::InsertBlock()

这段代码是RenderDelayBufferImpl类中的InsertBlock方法的实现。该方法将一个块(block)插入到渲染缓冲区中。

代码逻辑如下:

  1. 获取相关缓冲区的引用,包括blocks_(渲染缓冲区)、low_rate_(低采样率缓冲区)、render_ds_(渲染下采样缓冲区)、ffts_(FFT缓冲区)、spectra_(频谱缓冲区)。

  2. 获取渲染缓冲区块的频带数目(num_bands)和渲染通道数目(num_render_channels)。

  3. 使用嵌套循环遍历块的频带和渲染通道,并将块的数据复制到渲染缓冲区blocks_中。

  4. 如果渲染线性幅度增益(render_linear_amplitude_gain_)不等于1.0,对渲染缓冲区中的数据进行线性幅度增益处理。

  5. 进行渲染混音和下采样操作,将下采样结果存储在render_ds_缓冲区中,并将下采样结果的逆序复制到低采样率缓冲区(low_rate_)的相应位置。

  6. 对渲染缓冲区blocks_(的第一个通道进行快速傅里叶变换(FFT)和频谱计算,将结果存储在FFT缓冲区和频谱缓冲区中。

RenderDelayBufferImpl::PrepareCaptureProcessing()

该方法用于准备渲染缓冲区以处理另一个捕获块。

代码逻辑如下:

  1. 首先,初始化一个缓冲事件(event)为BufferingEvent::kNone,表示没有缓冲事件发生。

  2. 增加捕获调用计数器(capture_call_counter_)。

  3. 如果存在延迟(delay_):

    • 如果上次调用是渲染(last_call_was_render_true),则将last_call_was_render_设为false,表示当前调用是捕获。

    • 否则,增加连续 API 调用次数

      num_api_calls_in_a_row_
      • 如果连续 API 调用次数超过了最大观察到的抖动值(max_observed_jitter_),更新最大观察到的抖动值,并记录日志。

    这段代码与Insert的计算代码呼应,max_observed_jitter_代表连续插入(渲染或捕获)block的数量;

  4. 判断kRenderOverrun,检测与捕获块相比,是否存在过多的渲染块。如果存在过多的渲染块,则执行以下操作:

    • 记录日志指示检测到过多的渲染块。

    • 调用 Reset 方法重置缓冲区。

    • 将缓冲事件设置为 BufferingEvent::kRenderOverrun,表示发生了渲染超限事件。

  5. 判断是否渲染不足(RenderUnderrun),若是,执行以下操作:

    • 记录日志指示检测到渲染缓冲区不足。

    • 增加读索引。IncrementReadIndices,low_rate不增加。

    • 如果存在延迟且延迟值大于0,则减少延迟值。

    • 将缓冲事件设置为 BufferingEvent::kRenderUnderrun,表示发生了渲染不足事件。

  6. 否则,增加渲染缓冲区和频谱缓冲区的读索引。

  7. low_rate_.read -= 16;

  8. blocks_.read += 1

  9. spectra_.read -= 1

  10. ffts_.read -= 1

  11. 将当前render_activity_设置到回声去除器,设置完后更新 render_activity:

    • 如果存在渲染活动(render_activity_true),则将渲染活动计数器(render_activity_counter_)重置为0,并将渲染活动状态(render_activity_)设为false

    • Insert函数对应维护render_activity_,该状态设置到回声去除器后没有使用。

  12. 返回缓冲事件。

RenderDelayBufferImpl::RenderOverrun()

该方法用于在block insert的时候,检查渲染缓冲区是否发生了(kRenderOverrun)超限事件,当发生渲染超限(kRenderOverrun)时,允许发生超限,并执行重置reset操作。

代码逻辑很简单:

  1. 如果低采样率缓冲区(low_rate_)的读索引(read)等于写索引(write),或者渲染延迟缓冲区(blocks_)的读索引等于写索引,则返回true,表示发生了渲染超限。

  2. 如果上述条件不满足,则返回false,表示没有发生渲染超限。

kRenderOverrun指的是插入的渲染数据比接收到的捕获数据更多而导致的情况。

渲染超限的情况可能发生在以下情形下:

  1. 渲染数据的流速(插入速率)存在一段时间持续快于捕获数据的流速(读取速率),导致渲染缓冲区的写索引超过了读索引,若继续写入会覆盖数据。

RenderDelayBufferImpl::DetectExcessRenderBlocks()

该方法用于在处理捕获块数据时候,检测是否存在过多的渲染块(kRenderOverrun),当发生渲染超限(kRenderOverrun)时,执行重置reset操作。

代码逻辑如下:

每处理250个块(1s)进入一次判断

判断这段时间里的low_rate_最小延迟块数是否高于阈值max_allowed_excess_render_blocks=8。

如果满足条件,则表示存在过多的渲染块。如果检测到过多的渲染块,返回true

不满足则,返回false

RenderDelayBufferImpl::RenderUnderrun()

该方法用于检测渲染缓冲区是否发生了kRenderUnderrun代码逻辑很简单:

  1. 检查低速率(low_rate_)结构体中的读指针(read)和写指针(write)是否相等。

  2. 如果读指针和写指针相等,则表示发生了kRenderUnderrun,即读取速度比写入速度更快,导致无法获得足够的渲染数据。

RenderDelayBufferImpl::ApplyTotalDelay()

该方法根据总延迟设置blocks.read 、spectra.read 、 ffts_.read 读索引的位置。

代码逻辑如下:传入delay

  1. 使用日志记录当前应用的延迟值。

  2. 更新渲染缓冲区(blocks_)、频谱缓冲区(spectra_)和FFT缓冲区(ffts_)的读索引位置,注意下采样的读索引并未发生改变。

    • blocks.read = blocks.write - delay

    • spectra.read = spectra.write + delay

    • ffts.read = ffts.write + delay

    根据预估的时延值,将几个缓冲区对应的读指针设置得到对应位置,以便后续做回声去除的时候,读取的数据已经是实现对齐的;

RenderDelayBufferImpl::ComputeDelay()

该方法用于计算延迟(不包括呼叫抖动)。

代码逻辑如下:

  1. 首先,获取当前缓冲区的延迟块数(latency_blocks),调用BufferLatency方法来计算。

  2. 接下来,计算内部延迟(internal_delay)。如果频谱缓冲区的读索引(spectra.read)大于等于写索引(spectra.write),则内部延迟等于读索引减去写索引。否则,内部延迟等于频谱缓冲区的大小加上读索引再减去写索引。

  3. 最后,返回内部延迟internal_delay减去延迟块数latency_blocks的结果作为计算得到的延迟值。

该方法的作用是计算渲染缓冲区的内部延迟(不包括呼叫抖动),通过读索引和写索引之间的差值来确定延迟。通过减去延迟块数,可以得到实际的延迟值。这个延迟值可以用于音频处理的时间对齐和同步操作。

RenderDelayBufferImpl::BufferLatency()

该方法用于计算缓冲区中的延迟(未读取的子块数)。

代码逻辑如下:

  1. 计算延迟的样本数(latency_samples)。通过(l.buffer.size() + l.read - l.write) % l.buffer.size();得到未读取的样本数。

  2. 将延迟的样本数除以子块的大小(sub_block_size_),得到延迟的子块数(latency_blocks)。

  3. 返回延迟的子块数作为计算得到的缓冲区延迟值。

RenderDelayBufferImpl::AlignFromDelay

这段代码是RenderDelayBufferImpl类中的AlignFromDelay方法的实现。该方法用于设置渲染延迟,并返回一个布尔值,指示延迟是否发生了变化。

  • external_audio_buffer_delay_verified_after_reset_ reset后是否经过音频缓冲区延迟验证

  • external_audio_buffer_delay_ 设置进去的delay值,以block为单位

  1. 如果在重置后尚未进行外部音频缓冲区延迟验证(external_audio_buffer_delay_verified_after_reset_为假),且存在外部音频缓冲区延迟(external_audio_buffer_delay_为真)和当前已估计的延迟(delay_为真):

    • 第一次计算得出估计值,与输入的估计值对比,并判断外部估计值是否正确;

  2. 更新当前估计值,将指定的延迟值赋给delay_

  3. 计算总延时,调用MapDelayToTotalDelay(该函数调用ComputeDelay,计算buffer内的时延,与前面delay_ 相加得到总延时返回)计算总延迟(total_delay),并将其限制在(<=153)允许的范围内,因此,可以得出结论,aec3的滤波器时延估计值最大为512ms,加上内部延时,aec3最多能处理 153*4 = 612ms的时延;

  4. 将总延迟应用到缓冲区中,即调用ApplyTotalDelay方法根据总延迟blocks.read 、spectra.read 、 ffts_.read 读index;

  5. 返回true,表示延迟发生了变化。

BufferingEvent介绍,包括kNone、kRenderUnderrun、kRenderOverrun、kApiCallSkew
 enum class BufferingEvent {
   kNone,
   kRenderUnderrun,
   kRenderOverrun,
   kApiCallSkew
}

kNone

kRenderOverrun: 播放帧过多——对应外部可能发生的:录音阻塞,录音停止,(网络不佳时,播放瞬时来了很多帧,可能),作了Reset处理将delay相关的变量全部重置,重新匹配,影响不会很大;

  • 置位的地方: 当执行RenderDelayBufferImpl::Insert(),向render_buffer插入block时,会执行RenderOverrun()判断,在移动write指针后,判断low_rate_ 和blocks_ 的read和write指针是否相等(相等说明已经套圈了),以此来判断render_buffer是否发生了kRenderOverrun事件(相等了就是发生了kRenderOverrun);

  • 如果发生了kRenderOverrun,会执行RenderDelayBufferImpl::Reset();并且在PrepareCaptureProcessing时,会delay_controller_也会reset。

  • RenderDelayBufferImpl::PrepareCaptureProcessing()时,为处理捕获数据作准备。也会执行DetectExcessRenderBlocks判断此时renderbuffer是否发生kRenderOverrun,这个每1s才会检测一次,且不会改变render_event_ 的取值。只是当检测到的时候,同样会执行RenderDelayBufferImpl::Reset(),但是significant_candidate_found_标志位会保留;

  • 发生了kRenderOverrun也同样会执行匹配滤波器等后续步骤;

kRenderUnderrun:播放帧太少——对应外部事件:播放阻塞、播放停止

  • 这个只会在RenderDelayBufferImpl::PrepareCaptureProcessing()时检测,检测方法为low_rate_ read和write指针是否相等

    如果发生了kRenderUnderrun,则不会挪动low_rate_ 的read指针,因为没有新的播放帧可以用了;并且delay_controller_也会reset。

    发生了kRenderOverrun也同样会执行匹配滤波器等后续步骤;

0条评论
0 / 1000
蔡****轩
4文章数
1粉丝数
蔡****轩
4 文章 | 1 粉丝
原创

AEC3类介绍 RenderDelayBuffer(一)

2023-08-30 11:42:03
124
0

类介绍 RenderDelayBuffer、RenderDelayBufferImpl

RenderDelayBuffer

用于render_buffer的维护

这个类具有以下公共成员函数和纯虚函数:

  • Create():静态函数,用于创建RenderDelayBuffer对象。

  • Reset():重置缓冲区的对齐。

  • Insert():将块插入到缓冲区中,并返回一个表示缓冲事件的枚举值。

  • PrepareCaptureProcessing():基于指定的缓冲延迟更新缓冲区,并返回一个表示特殊事件发生的枚举值。

  • HandleSkippedCaptureProcessing():在未调用PrepareCaptureProcessing()的捕获块上调用。

  • AlignFromDelay():设置缓冲延迟,并返回一个布尔值,指示延迟是否发生了变化。

  • AlignFromExternalDelay():从最近报告的外部延迟设置缓冲延迟。

  • Delay():获取缓冲延迟。

  • MaxDelay():获取最大缓冲延迟。

  • GetRenderBuffer():返回用于回声去除器的渲染缓冲区。

  • GetDownsampledRenderBuffer():返回下采样的渲染缓冲区。

  • DelayEstimatorOffset():返回可在延迟缓冲区中发生的最大非因果偏移量。

  • SetAudioBufferDelay():提供一个可选的音频缓冲延迟的外部估计。

  • HasReceivedBufferDelay():返回是否通过SetAudioBufferDelay接收到了外部的延迟估计。

该类还包含了一个嵌套的枚举类BufferingEvent,用于表示缓冲事件的不同类型。该类似乎属于webrtc命名空间

kNone:表示没有发生特殊事件。

kRenderUnderrun:表示渲染缓冲区发生了不足的情况,即需要更多的渲染数据以应对实时处理需求。

kRenderOverrun:表示渲染缓冲区发生了溢出的情况,即渲染数据超过了实时处理的需求。

kApiCallSkew:表示API调用的时间不匹配,即API调用的时间与实际数据处理的时间发生了偏差。

RenderDelayBufferImpl

继承自RenderDelayBuffer,除了override父类方法外,

类的私有成员变量和私有方法如下:

私有成员变量:

  • instance_count_:一个静态整数,用于跟踪RenderDelayBufferImpl类的实例数。

  • data_dumper_:一个指向ApmDataDumper的唯一指针,用于数据记录和调试。

  • optimization_:一个Aec3Optimization类型的常量,表示AEC3的优化选项。

  • config_:一个EchoCanceller3Config类型的常量,表示回声消除器的配置。

  • update_capture_call_counter_on_skipped_blocks_:一个bool值,表示是否在跳过的块上更新捕获调用计数器。

  • render_linear_amplitude_gain_:一个float值,表示渲染线性幅度增益。

  • delay_log_level_:一个rtc::LoggingSeverity类型的常量,表示延迟日志级别。

  • down_sampling_factor_:一个size_t值,表示下采样因子。

  • sub_block_size_:一个int值,表示子块的大小。

  • blocks_:一个BlockBuffer对象,表示音频块的缓冲区。

  • spectra_:一个SpectrumBuffer对象,表示频谱的缓冲区。

  • ffts_:一个FftBuffer对象,表示FFT的缓冲区。

  • delay_:一个absl::optional<size_t>对象,表示延迟值的可选项。

  • echo_remover_buffer_:一个RenderBuffer对象,表示回声移除器的缓冲区。

  • low_rate_:一个DownsampledRenderBuffer对象,表示降采样的渲染缓冲区。

  • render_mixer_:一个AlignmentMixer对象,用于混合渲染音频。

  • render_decimator_:一个Decimator对象,用于渲染音频的抽取和降采样。

  • fft_:一个Aec3Fft对象,用于快速傅里叶变换。

  • render_ds_:一个std::vector<float>,用于存储降采样后的渲染音频。

  • buffer_headroom_:一个int值,表示缓冲区的保留头空间。

  • last_call_was_render_:一个bool值,表示上一次调用是否为渲染调用。

  • num_api_calls_in_a_row_:一个int值,表示连续的API调用次数。

  • max_observed_jitter_:一个int值,表示最大观察到的抖动。

  • capture_call_counter_:一个int64_t值,表示捕获调用的计数器。

  • render_call_counter_:一个int64_t值,表示渲染调用的计数器。

  • render_activity_:一个bool值,表示渲染活动状态。

  • render_activity_counter_:一个size_t值,表示渲染活动计数器。

  • external_audio_buffer_delay_:一个absl::optional<int>对象,表示外部音频缓冲区的延迟。

  • external_audio_buffer_delay_verified_after_reset_:一个bool值,表示在重置后是否验证了外部音频缓冲区的延迟。

  • min_latency_blocks_:一个size_t值,表示最小延迟块数。

  • excess_render_detection_counter_:一个size_t值,表示超出渲染的检测计数器。

私有成员方法:

  • MapDelayToTotalDelay:将延迟映射到总延迟的私有方法。

  • ComputeDelay:计算延迟的私有方法。

  • ApplyTotalDelay:应用总延迟的私有方法。

  • InsertBlock:插入音频块到缓冲区的私有方法。

  • DetectActiveRender:检测活动渲染的私有方法。

  • DetectExcessRenderBlocks:检测超出渲染块的私有方法。

  • IncrementWriteIndices:增加写索引的私有方法。

  • IncrementLowRateReadIndices:增加低速率读索引的私有方法。

  • IncrementReadIndices:增加读索引的私有方法。

  • RenderOverrun:检测渲染超限的私有方法。

  • RenderUnderrun:检测渲染不足的私有方法。

RenderDelayBufferImpl::Reset()

该方法用于重置缓冲区的延迟,并清除报告的延迟。

代码逻辑如下:

  1. 重置一些状态变量,包括last_call_was_render_(上次调用是否为渲染)、num_api_calls_in_a_row_(连续的 API 调用次数)、min_latency_blocks_(最小延迟块数)、excess_render_detection_counter_(渲染超限检测计数器)。

  2. 读取位置更新:将低采样率缓冲区的读索引(low_rate_.read)初始化为写索引(low_rate_.write)的前一个子块位置

  3. 检查是否存在外部音频缓冲区的延迟,并计算合适的初始化延迟audio_buffer_delay_to_set:如果存在外部音频缓冲区延迟(external_audio_buffer_delay_),且大于3,设置的延迟值取决于外部音频缓冲区延迟减去一个头部余量(headroom,2),同时,确保设置的延迟不超过最大延迟值。否则,设置为1

  4. 调用ApplyTotalDelay(audio_buffer_delay_to_set)设置延时; delay_ = ComputeDelay();

  5. 调用ComputeDelay()计算总延时delay_ ;

  6. external_audio_buffer_delay_verified_after_reset_置为false,表示在重置后需要重新验证外部音频缓冲区延迟。

  7. 如果不存在外部音频缓冲区延迟估计,则使用默认延迟值作为初始延迟,并将渲染缓冲区延迟设置为默认延迟。

  8. 通过AlignFromDelay方法设置的延迟值。

该方法的作用是重置缓冲区的状态和延迟设置,以便在重新开始音频处理时,缓冲区的状态和延迟被正确初始化。根据是否存在外部音频缓冲区延迟估计,采取不同的延迟设置策略,以确保音频处理的准确性和一致性。

RenderDelayBufferImpl::Insert()

该方法接收block作为参数,将其插入渲染缓冲区,修改render_buffer, write_index并返回缓冲事件。

代码逻辑如下:

  1. 首先,增加渲染调用计数器render_call_counter_

  2. 如果存在延迟值delay_,则进行以下操作:

    a. 检查上一次调用是否为渲染调用。如果不是,将last_call_was_render_设置为true,并将num_api_calls_in_a_row_设置为1。

    b. 如果上一次调用是渲染调用,则将num_api_calls_in_a_row_加1,并检查是否超过了max_observed_jitter_(之前观察到的最大抖动)。如果超过了最大抖动,更新max_observed_jitter_的值,并记录日志。

    这段代码与PrepareCaptureProcessing的计算代码呼应,max_observed_jitter_代表连续插入(渲染或捕获)block的数量;

  3. 更新render_buffer各个index,如下:

  • low_rate.write -= sub_block_size(16)

  • blocks_.write += 1

  • spectra_.write -= 1

  • ffts_.write -= 1

  1. 调用RenderOverrun(),判断是否渲染超限(RenderOverrun),是则将事件设置为BufferingEvent::kRenderOverrun;否则,事件设置为BufferingEvent::kNone

  2. 检测并更新渲染活动状态render_activity。如果渲染活动状态之前为非活动状态(render_activity_false),且此render block能量大于150 * 150 * 64,则计数器render_activity_counter_ +1;render_activity_counter累计到20,render_activity置true;

  3. 调用RenderDelayBufferImpl::InsertBlock() 插入当前block;

  4. 如果事件不是BufferingEvent::kNone,则进行重置(Reset)操作。

  5. 返回事件。

RenderDelayBufferImpl::InsertBlock()

这段代码是RenderDelayBufferImpl类中的InsertBlock方法的实现。该方法将一个块(block)插入到渲染缓冲区中。

代码逻辑如下:

  1. 获取相关缓冲区的引用,包括blocks_(渲染缓冲区)、low_rate_(低采样率缓冲区)、render_ds_(渲染下采样缓冲区)、ffts_(FFT缓冲区)、spectra_(频谱缓冲区)。

  2. 获取渲染缓冲区块的频带数目(num_bands)和渲染通道数目(num_render_channels)。

  3. 使用嵌套循环遍历块的频带和渲染通道,并将块的数据复制到渲染缓冲区blocks_中。

  4. 如果渲染线性幅度增益(render_linear_amplitude_gain_)不等于1.0,对渲染缓冲区中的数据进行线性幅度增益处理。

  5. 进行渲染混音和下采样操作,将下采样结果存储在render_ds_缓冲区中,并将下采样结果的逆序复制到低采样率缓冲区(low_rate_)的相应位置。

  6. 对渲染缓冲区blocks_(的第一个通道进行快速傅里叶变换(FFT)和频谱计算,将结果存储在FFT缓冲区和频谱缓冲区中。

RenderDelayBufferImpl::PrepareCaptureProcessing()

该方法用于准备渲染缓冲区以处理另一个捕获块。

代码逻辑如下:

  1. 首先,初始化一个缓冲事件(event)为BufferingEvent::kNone,表示没有缓冲事件发生。

  2. 增加捕获调用计数器(capture_call_counter_)。

  3. 如果存在延迟(delay_):

    • 如果上次调用是渲染(last_call_was_render_true),则将last_call_was_render_设为false,表示当前调用是捕获。

    • 否则,增加连续 API 调用次数

      num_api_calls_in_a_row_
      • 如果连续 API 调用次数超过了最大观察到的抖动值(max_observed_jitter_),更新最大观察到的抖动值,并记录日志。

    这段代码与Insert的计算代码呼应,max_observed_jitter_代表连续插入(渲染或捕获)block的数量;

  4. 判断kRenderOverrun,检测与捕获块相比,是否存在过多的渲染块。如果存在过多的渲染块,则执行以下操作:

    • 记录日志指示检测到过多的渲染块。

    • 调用 Reset 方法重置缓冲区。

    • 将缓冲事件设置为 BufferingEvent::kRenderOverrun,表示发生了渲染超限事件。

  5. 判断是否渲染不足(RenderUnderrun),若是,执行以下操作:

    • 记录日志指示检测到渲染缓冲区不足。

    • 增加读索引。IncrementReadIndices,low_rate不增加。

    • 如果存在延迟且延迟值大于0,则减少延迟值。

    • 将缓冲事件设置为 BufferingEvent::kRenderUnderrun,表示发生了渲染不足事件。

  6. 否则,增加渲染缓冲区和频谱缓冲区的读索引。

  7. low_rate_.read -= 16;

  8. blocks_.read += 1

  9. spectra_.read -= 1

  10. ffts_.read -= 1

  11. 将当前render_activity_设置到回声去除器,设置完后更新 render_activity:

    • 如果存在渲染活动(render_activity_true),则将渲染活动计数器(render_activity_counter_)重置为0,并将渲染活动状态(render_activity_)设为false

    • Insert函数对应维护render_activity_,该状态设置到回声去除器后没有使用。

  12. 返回缓冲事件。

RenderDelayBufferImpl::RenderOverrun()

该方法用于在block insert的时候,检查渲染缓冲区是否发生了(kRenderOverrun)超限事件,当发生渲染超限(kRenderOverrun)时,允许发生超限,并执行重置reset操作。

代码逻辑很简单:

  1. 如果低采样率缓冲区(low_rate_)的读索引(read)等于写索引(write),或者渲染延迟缓冲区(blocks_)的读索引等于写索引,则返回true,表示发生了渲染超限。

  2. 如果上述条件不满足,则返回false,表示没有发生渲染超限。

kRenderOverrun指的是插入的渲染数据比接收到的捕获数据更多而导致的情况。

渲染超限的情况可能发生在以下情形下:

  1. 渲染数据的流速(插入速率)存在一段时间持续快于捕获数据的流速(读取速率),导致渲染缓冲区的写索引超过了读索引,若继续写入会覆盖数据。

RenderDelayBufferImpl::DetectExcessRenderBlocks()

该方法用于在处理捕获块数据时候,检测是否存在过多的渲染块(kRenderOverrun),当发生渲染超限(kRenderOverrun)时,执行重置reset操作。

代码逻辑如下:

每处理250个块(1s)进入一次判断

判断这段时间里的low_rate_最小延迟块数是否高于阈值max_allowed_excess_render_blocks=8。

如果满足条件,则表示存在过多的渲染块。如果检测到过多的渲染块,返回true

不满足则,返回false

RenderDelayBufferImpl::RenderUnderrun()

该方法用于检测渲染缓冲区是否发生了kRenderUnderrun代码逻辑很简单:

  1. 检查低速率(low_rate_)结构体中的读指针(read)和写指针(write)是否相等。

  2. 如果读指针和写指针相等,则表示发生了kRenderUnderrun,即读取速度比写入速度更快,导致无法获得足够的渲染数据。

RenderDelayBufferImpl::ApplyTotalDelay()

该方法根据总延迟设置blocks.read 、spectra.read 、 ffts_.read 读索引的位置。

代码逻辑如下:传入delay

  1. 使用日志记录当前应用的延迟值。

  2. 更新渲染缓冲区(blocks_)、频谱缓冲区(spectra_)和FFT缓冲区(ffts_)的读索引位置,注意下采样的读索引并未发生改变。

    • blocks.read = blocks.write - delay

    • spectra.read = spectra.write + delay

    • ffts.read = ffts.write + delay

    根据预估的时延值,将几个缓冲区对应的读指针设置得到对应位置,以便后续做回声去除的时候,读取的数据已经是实现对齐的;

RenderDelayBufferImpl::ComputeDelay()

该方法用于计算延迟(不包括呼叫抖动)。

代码逻辑如下:

  1. 首先,获取当前缓冲区的延迟块数(latency_blocks),调用BufferLatency方法来计算。

  2. 接下来,计算内部延迟(internal_delay)。如果频谱缓冲区的读索引(spectra.read)大于等于写索引(spectra.write),则内部延迟等于读索引减去写索引。否则,内部延迟等于频谱缓冲区的大小加上读索引再减去写索引。

  3. 最后,返回内部延迟internal_delay减去延迟块数latency_blocks的结果作为计算得到的延迟值。

该方法的作用是计算渲染缓冲区的内部延迟(不包括呼叫抖动),通过读索引和写索引之间的差值来确定延迟。通过减去延迟块数,可以得到实际的延迟值。这个延迟值可以用于音频处理的时间对齐和同步操作。

RenderDelayBufferImpl::BufferLatency()

该方法用于计算缓冲区中的延迟(未读取的子块数)。

代码逻辑如下:

  1. 计算延迟的样本数(latency_samples)。通过(l.buffer.size() + l.read - l.write) % l.buffer.size();得到未读取的样本数。

  2. 将延迟的样本数除以子块的大小(sub_block_size_),得到延迟的子块数(latency_blocks)。

  3. 返回延迟的子块数作为计算得到的缓冲区延迟值。

RenderDelayBufferImpl::AlignFromDelay

这段代码是RenderDelayBufferImpl类中的AlignFromDelay方法的实现。该方法用于设置渲染延迟,并返回一个布尔值,指示延迟是否发生了变化。

  • external_audio_buffer_delay_verified_after_reset_ reset后是否经过音频缓冲区延迟验证

  • external_audio_buffer_delay_ 设置进去的delay值,以block为单位

  1. 如果在重置后尚未进行外部音频缓冲区延迟验证(external_audio_buffer_delay_verified_after_reset_为假),且存在外部音频缓冲区延迟(external_audio_buffer_delay_为真)和当前已估计的延迟(delay_为真):

    • 第一次计算得出估计值,与输入的估计值对比,并判断外部估计值是否正确;

  2. 更新当前估计值,将指定的延迟值赋给delay_

  3. 计算总延时,调用MapDelayToTotalDelay(该函数调用ComputeDelay,计算buffer内的时延,与前面delay_ 相加得到总延时返回)计算总延迟(total_delay),并将其限制在(<=153)允许的范围内,因此,可以得出结论,aec3的滤波器时延估计值最大为512ms,加上内部延时,aec3最多能处理 153*4 = 612ms的时延;

  4. 将总延迟应用到缓冲区中,即调用ApplyTotalDelay方法根据总延迟blocks.read 、spectra.read 、 ffts_.read 读index;

  5. 返回true,表示延迟发生了变化。

BufferingEvent介绍,包括kNone、kRenderUnderrun、kRenderOverrun、kApiCallSkew
 enum class BufferingEvent {
   kNone,
   kRenderUnderrun,
   kRenderOverrun,
   kApiCallSkew
}

kNone

kRenderOverrun: 播放帧过多——对应外部可能发生的:录音阻塞,录音停止,(网络不佳时,播放瞬时来了很多帧,可能),作了Reset处理将delay相关的变量全部重置,重新匹配,影响不会很大;

  • 置位的地方: 当执行RenderDelayBufferImpl::Insert(),向render_buffer插入block时,会执行RenderOverrun()判断,在移动write指针后,判断low_rate_ 和blocks_ 的read和write指针是否相等(相等说明已经套圈了),以此来判断render_buffer是否发生了kRenderOverrun事件(相等了就是发生了kRenderOverrun);

  • 如果发生了kRenderOverrun,会执行RenderDelayBufferImpl::Reset();并且在PrepareCaptureProcessing时,会delay_controller_也会reset。

  • RenderDelayBufferImpl::PrepareCaptureProcessing()时,为处理捕获数据作准备。也会执行DetectExcessRenderBlocks判断此时renderbuffer是否发生kRenderOverrun,这个每1s才会检测一次,且不会改变render_event_ 的取值。只是当检测到的时候,同样会执行RenderDelayBufferImpl::Reset(),但是significant_candidate_found_标志位会保留;

  • 发生了kRenderOverrun也同样会执行匹配滤波器等后续步骤;

kRenderUnderrun:播放帧太少——对应外部事件:播放阻塞、播放停止

  • 这个只会在RenderDelayBufferImpl::PrepareCaptureProcessing()时检测,检测方法为low_rate_ read和write指针是否相等

    如果发生了kRenderUnderrun,则不会挪动low_rate_ 的read指针,因为没有新的播放帧可以用了;并且delay_controller_也会reset。

    发生了kRenderOverrun也同样会执行匹配滤波器等后续步骤;

文章来自个人专栏
文章 | 订阅
0条评论
0 / 1000
请输入你的评论
0
0