一、概述
本文分析 FFmpeg af_silencedetect 的实现。
二、af_silencedetect 的作用及基本原理
af_silencedetect 的作用是获取音频的最大音量、平均音量以及音量直方图。
它只支持 AV_SAMPLE_FMT_S16 、 AV_SAMPLE_FMT_S32 、 AV_SAMPLE_FMT_FLT 和 AV_SAMPLE_FMT_DBL 这四种格式——如果不是当然 FFmpeg 能够自动转换。
多大音量认为是静音由参数 noise 确定,默认是 -60dB 或 0.001;多长的连续时长认为是静音由参数 duration 确定,默认是 2 秒。参数 mono 为非 0 表示各个声道分别检测,默认是合并在一起检测。
合并在一起检测:比如认为 2 秒连续无声(或小声)认为是静音,那么其中一个声道达标,另一个声道在该时段内不达标也不认为是静音。
三、在调用 ffmpeg 程序时使用 af_silencedetect
使用默认参数:
1 | ffmpeg -i input.mp3 -af "silencedetect" -vn -sn -dn -f null /dev/null |
在 Windows 中使用需将
/dev/null替换为NUL-vn、-sn和-dn告知 FFmpeg 忽略非音频流。能够在分析时避免不必要的操作从而更快速.
输出类似于:
1 | [silencedetect @ 0x137f044d0] silence_start: 0 0x |
各个声道分别检测:
1 | ffmpeg -i 0.mp3 -af "silencedetect=mono=1" -vn -sn -dn -f null /dev/null |
输出类似于:
1 | [silencedetect @ 0x152704190] channel: 0 | silence_start: 0 |
四、源码分析
af_silencedetect 源码位于 ffmpg/libavfilter/af_silencedetect.c 中。
分析 filter 一般从 static int filter_frame(AVFilterLink *inlink, AVFrame *in) 函数入手。不过由于要支持多种采样格式,需要在 static int config_input(AVFilterLink *inlink) 根据采样格式设置检测函数。
1 | static int config_input(AVFilterLink *inlink) |
nb_null_samples 用于累加达标的采样数,通过
silencedetect_dbl、silencedetect_flt、silencedetect_s32 和 silencedetect_s16 由宏定义:
1 |
|
update 用于检测每一个采样:
1 | static av_always_inline void update(SilenceDetectContext *s, AVFrame *insamples, |
五、C# 简单实现
1 | public class VolumeUtils |
由于本人需要,对于多声道本方法也只检测第一个声道。在多个声道音量不是交错的情况下有助于提升效率。