ExoPlayer-跳过静音原理

ExoPlayer AudioProcessor处理模型

ExoPlayer内部维护了一个AudioProcessChain,它的作用就是串联各个AudioProcessor,将上一个AudioProcessor的输出传递到下一个AudioProcessor.伪代码类似

while (有数据) {
    // 将输入喂给第一个Processor
    processor[0].queueInput(inputBuffer);

    // 依次传递下去
    for (i = 0; i < processors.length; i++) {
        ByteBuffer output = processor[i].getOutput();
        if (i + 1 < processors.length) {
            processor[i+1].queueInput(output);
        } else {
            // 最后一个processor的输出就是最终结果
            writeToSink(output);
        }
    }
}

音频帧

这里的音频帧是指音频采样点。而不是mp3或者aac一个音频帧的概念。后者可能是多个样本,而前者是单声道1个采样点,双声道2个采样点。

SilenceSkippingAudioProcessor

SilenceSkippingAudioProcessor内部处理的模型,一言以蔽之,就是计算从当前时间点应该跳过的音频帧。后续每次音频播放的时候播放器会询问应该跳过的音频帧数。然后播放器内部会计算正确的应该在的时间戳。同时对于静音数据,SilenceSkippingAudioProcessor不会继续往下传递。因此最终也不会输出声音。

构造函数参数讲解

 public SilenceSkippingAudioProcessor(
      long minimumSilenceDurationUs, long paddingSilenceUs, short silenceThresholdLevel) 

minimumSilenceDurationUs代表最短会被认为静音的时间短。短于这个时间短的静音片段不认为是静音。

paddingSilenceUs: 静音-非静音或者非静音-静音过度的时间长度,它的长度不能超过minimumSilenceDurationUs/2,silenceThresholdLevel,静音的阈值。小于这个数值的录音会被判定为静音。

ExoPlayer跳过静音的处理是由SilenceSkippingAudioProcessor,这个AudioProcessor进行处理的。它内部维持了一个状态机。

STATE_NOISY ---> (可能静音) ---> STATE_MAYBE_SILENT
     ^                                   |
     |                                   v
     |                                STATE_SILENT
     |                                   |
     +------ (检测到噪声) ---------------

SilenceSkippingAudioProcessor的处理单位的最小单位是音频帧。一个音频帧等同于一个样本,双声道则是两个样本。

STATE_NOISY是否能够切换到STATE_MAYBE_SILENT是由findNoiseLimit决定的,findNoiseLimit的逻辑是如果找到了单个采样位置它的阈值超过了静音阈值,那么把这个采样所属的整个音频帧都标记为’非静音’。因此如果findNoiseLimit返回的值等于position,说明整个Buffer中都是读取到静音的数据,

否则的话,就把当前明确标记为非静音的数据输出。

当状态切换为STATE_MAYBE_SILENT,处理逻辑如下。

收集后续的静音片段,直到要么达成阈值,要么遇到了非静音的。如果遇到了非静音,那么将状态切换为STATE_NOISY,STATE_NOISY负责把这部分不够长的音频输出。在收集到了足够的静音sample数据之后会进入STATE_SILENT。同时计算skippedFrames,当遇到下一个非静音的Sample的时候,切换到STATE_NOISY状态。

padding逻辑

进入静音时:会把 静音前的最后一小段样本(paddingSize)保留下来,一并输出。

跳过静音后恢复时:会把 静音后最开始的一小段样本也输出,作为恢复区间。

它为了解决什么问题?

原始音频: [声音][静音][声音]
直接跳过: [声音][声音]

这样第二段声音会“啪”地贴到前一段,缺乏过渡。

因此为了平滑声音的输出,这里会把一小段静音数据切出来,仍然输出。这样两段非静音数据数据之间就不会听着那么突兀。而这个切分的逻辑会导致部分静音数据仍然输出。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容