资讯动态

FFmpeg FFT 基变换手册:奇偶置换、split-radix 展开与 4/8/16/15 点变换深入解析

发布时间:2026/9/28 8:16:12 来源:尧图企业网站定制
人工智能AI Agent多模态语音AI 应用【免费下载链接】ten-frameworkOpen-source framework for conversational voice AI agents项目地址https://gitcode.com/TEN-framework/ten-framework点击查看免费下载本文系统讲解 FFmpeg libavutil 中 FFT 与相关派生变换MDCT、RDFT 等所依赖的**基变换basis transforms**核心算法奇偶置换表如何生成、4/8/16 点 FFT 如何展开、split-radix 合成如何递归构造大点数变换、15 点变换的 DC/AC 分离策略以及它们如何映射到 SSE/AVX/NEON 等 SIMD 汇编实现。读完本文你将理解 FFmpeg 变换框架中预置换permutation 基变换 递归合成三层结构的原理并能读懂 transforms.md 中全部 C 展开代码与 tx.c / tx_float.asm 等源码的对应关系。背景FFmpeg 变换框架的三层结构FFmpeg 的变换框架位于 libavutil/tx.c、libavutil/tx_template.c将任意长度的 FFT 分解为三个阶段预置换阶段通过查找表s-map把输入系数重新排列使后续基变换满足特定的奇偶布局基变换阶段执行 2/3/4/5/7/8/9/15/16 等小点数变换这些变换的 C 版本与 SIMD 版本在文档中以展开unrolling形式给出递归合成阶段用 split-radix 算法把小变换组合成大点数变换。文档明确说明transforms.md本文档描述的基变换基于以下展开方式且函数可轻松适配双精度浮点——因此FFTSample类型既可以是float对应tx_float.c也可以是double对应 tx_double.c。从源码结构看tx_priv.h 定义了AVTXContext与FFTXCodelet代码块抽象每个 codelet 声明自己支持的因数、最小/最大长度与 CPU 特性cpu_flags框架在初始化时按优先级挑选合适的 codelet并通过s-sub递归挂载子变换。奇偶置换Parity Permutation所有基变换的公共前置生成接口与参数语义文档给出的核心接口是void ff_tx_gen_split_radix_parity_revtab(int *revtab, int len, int inv, int basis, int dual_stride);在仓库中该函数实际定义为ff_tx_gen_split_radix_parity_revtab(AVTXContext *s, int len, int inv, FFTXCodeletOptions *opts, int basis, int dual_stride)tx.c其中revtab对应s-map并增加了opts参数用于指定置换方向FF_TX_MAP_GATHER收集式或FF_TX_MAP_SCATTER散布式。实现还包含两条重要断言av_assert0(!dual_stride || !(dual_stride (dual_stride - 1))); // 必须是 0 或 2 的幂 av_assert0(dual_stride basis); // 不能超过 basis这从源码层面印证了文档对dual_stride的约束说明transforms.md。奇偶分离的含义所谓奇偶置换指的是偶序号复数与奇序号复数被分离偶系数排在前奇系数排在后。文档给出 4 点变换重排后的示例transforms.mdz[0].re, z[0].im, z[2].re, z[2].im, z[1].re, z[1].im, z[3].re, z[3].im即实部/虚部成对、先偶z[0]、z[2]后奇z[1]、z[3]。这一布局是后续 8 点、16 点变换中偶路径/奇路径独立计算的基础。basis 参数的含义basis参数是所支持的最大非合数prime-length即质数长度变换的长度同时隐含了basis/2变换也被支持——因为 split-radix 算法要求递归过程中使用半长的基变换transforms.md。例如 AVX 8 点展开对应 basis 为 8其内部实际复用了 4 点逻辑。在ff_tx_gen_split_radix_parity_revtab中入口先执行basis 1tx.c即实际生效的 basis 是传入值的一半若len basis则返回AVERROR(EINVAL)。这与文档当长度小于 basis/2 时函数不做任何事的描述一致transforms.md——因为传入len后函数内部还会len 1。dual_stride 与双变换复用dual_stride参数用来表示basis 与 basis/2 变换都支持一次做两个变换此时系数会按 split-radix 风格交错排列。文档给出 stride 2 时的布局transforms.mdtx1[0], tx1[2], tx2[0], tx2[2], tx1[1], tx1[3], tx2[1], tx2[3]该值必须是 0 或 2 的幂且小于 basis。文档还给出一个重要细节transforms.md该值会被裁剪到变换长度——例如 basis 为 16、dual_stride 为 8 时两个 8 点变换的布局会表现得如同 dual_stride 为 4。通常把它设置为单寄存器能容纳的复数个数的一半或 0这样就能在 AVX 模式下复用 SSE 的双变换函数。从实现看tx.cparity_revtab_generator递归处理三块len全段、len/2段dual_high0与len/2段dual_high1最终索引由split_radix_permutation()计算并按inv正/逆变换取负、按n-1取模is_dual时stride FFMIN(dual_stride, len)参与索引步进。每处理stride个元素后even_idx/odd_idx跳进stride从而形成双变换交错。实际调用点在仓库中该表生成函数被 x86 与 AArch64 初始化代码调用例如x86/tx_float_init.cff_tx_gen_split_radix_parity_revtab(s, len, inv, opts, ...)SSE/AVX 分支按 CPU 特性传不同 basis 与 dual_strideaarch64/tx_float_init.creturn ff_tx_gen_split_radix_parity_revtab(s, len, inv, opts, 8, 0);NEON 分支basis8、无 dual。4 点 FFT 变换最小的完整蝶形4 点变换是文档中第一个完整展开的实现transforms.md其特点是唯一的置换是逆变换时交换z[1]与z[2]该交换在汇编中直接硬编码函数本身按方向模板化并各复制一份transforms.md。C 展开的核心逻辑transforms.md分三步加减法求差r1 z[0].re - z[2].re、r2 z[0].im - z[2].im、r3 z[1].re - z[3].re、r4 z[1].im - z[3].im加减法求和t1..t4为对应位置的和组合输出a1 t1 t3、a2 t2 t4对应偶数输出b1 r1 r4、b4 r2 r3对应奇数输出其余为差值项。值得注意的指令计数注释文档在代码中标注/* 1sub 1add 2 instructions */、/* 2 shufs */、/* 1 add 1 sub 3 shufs */等transforms.md表明这段 C 代码不是可读优先的实现而是逐指令对照 SIMD 展开的产物——每个中间量都对应一条或一组 x86 指令sub/add/shuffle。这正是理解本文档的关键它是一份用 C 语法写的汇编展开说明。在仓库模板中4 点变换的通用版本位于 tx_template.cff_tx_fft4_ns并被 8/16 点变换递归调用tx_template.c。8 点 AVX FFT 变换偶/奇两条独立路径文档中 8 点 AVX 版本transforms.md要求输入必须先用奇偶置换表预置换由ff_tx_gen_split_radix_parity_revtab生成这是所有非平凡基变换的共同前提。该实现最鲜明的结构特征是两条完全独立的计算路径偶数系数路径由q1..q8和为 4 的项导出s1..s8再经e1..e8符号翻转* -1或* 1得w1..w8最终写入z[0]、z[2]、z[4]、z[6]transforms.md奇数系数路径由r1..r8差为 4 的项导出z1..z8其中z5..z8乘±M_SQRT1_2即 ±√2/245° 旋转因子transforms.md经t5..t8、u1..u8后写入z[1]、z[3]、z[5]、z[7]。文档明确说明transforms.md这一偶路径 / 奇路径主题贯穿全文。实际汇编代码中两条路径会被交错以提升单元饱和度和 CPU 依赖跟踪能力因此读汇编时需要反交错deinterleave指令才能看清两条路径。这条偶路径/奇路径分离的思想在 x86 汇编 tx_float.asm 中有直接对应ff_tx_fft8_asm_float_avx、ff_tx_fft8_asm_float_sse3并在 AArch64 侧对应 tx_float_neon.S 的ff_tx_fft8_*_neon。8 点 SSE/ARM64 FFT 变换addsub 指令驱动的变体SSE/ARM64 版本transforms.md与 AVX 版本输出布局相同偶输出写z[0]、z[2]、z[4]、z[6]奇输出写z[1]、z[3]、z[5]、z[7]但中间计算的组织方式不同前半段用g1..g4k3±k1类配合s1..s4生成w1..w4与h1..h4一次 add 一次 sub 即完成偶输出后半段先算z1..z4r1..r4的组合随后对j1..j4乘±M_SQRT1_2再经l1..l4、t1..t4两级蝴蝶后得到u1..u4与o1..o4。文档特别指出transforms.md这里的大部分函数都高度针对 x86 的 addsub 指令调优以省去外部符号掩码sign mask的加载。addsubps这类指令天然完成奇数通道相加、偶数通道相减或反之正好匹配 FFT 蝶形中大量出现的a±b组合——这是 C 展开中大量交叉求和/求差看似冗余的深层原因。16 点 AVX FFT 变换基变换的递归组合16 点版本transforms.md的骨架是递归调用小基变换fft8(z); // 处理 z[0..7] fft4(z8); // 处理 z[8..11] fft4(z10); // 处理 z[10..13]注意fft4(z8)与fft4(z10)的区间有重叠z[10]、z[11] 被两个 4 点变换共同使用这正是文档开头所说split-radix 风格的特征——4 点变换在输入上取 2 个 8 点变换的一半。该版本要求输入的 8 点与 4 点变换输出遵循前面建立的偶/奇约定transforms.md。随后的 16 个中间量s[0..15]由z[8..15]与旋转因子cos_16_1 0.92387950420379638671875f即 cos(π/8)、cos_16_3 0.3826834261417388916015625f即 cos(3π/8)以及M_SQRT1_2相乘得到transforms.md其中每个s[k]形如re*(cos) - im*(sin)即复数旋转的标准展开。之后通过w/x/y/u四组中间量把 32 个输出o1..o32组织成 16 个复数写回transforms.md。代码注释中的指令序列xorps、mulps、shufps、addsub、fma3等transforms.md再次印证这段 C 代码是逐指令规划过的 AVX 展开注释中的/* 2xorps, 2vperm2fs, 2 adds, 2 vpermilps 8 */等正是对每条指令成本的核算。最后的写回部分注明这只是反交错单独发生transforms.md——输出z[0..15]按自然序排列交错发生在更上层的递归中。AVX split-radix 合成用宏展开递归宏定义BF / BUTTERFLIES / TRANSFORM / CMUL文档给出 C 版 split-radix 递归ff_tx_fft_split_radix_fftw类逻辑的手工展开核心是四个宏transforms.mdBF(x, y, a, b)x a - b; y a b——一次蝶形的差与和BUTTERFLIES(a0,a1,a2,a3)组合 4 个输入的加减结果TRANSFORM(a0,a1,a2,a3,wre,wim)先用CMUL做复数乘乘旋转因子再调BUTTERFLIESCMUL(dre, dim, are, aim, bre, bim)标准复数乘法(are*bre - aim*bim, are*bim aim*bre)。文档强调transforms.md这些宏与通用 C 版本完全一致只是把所有变量声明导出到了函数体——也就是说这段recombine是把编译器应展开的宏循环手工摊平以便对照 SIMD 寄存器分配。recombine 函数手工展开的 AVX 版本recombine函数transforms.md中保留了被#if 0注释掉的宏循环原型实际生效的是#else分支的手工展开以 8 个w[]m2/m3乘t1/t2表与 8 个j[]开始相加得t[]对应和的蝶形、相减取反得r[]对应差的蝶形随后对m0..m3四组复数做加减写回。第二段m0 z[o1]起transforms.md结构完全相同仅换成cos[1]与wim[-1]的表偏移。高频/低频半区交换的陷阱文档给出了一个极易踩坑的细节transforms.md高频寄存器m2、m3在输出中高半区与低半区是交换的。这是刻意为之——因为输入也必须保持同样布局所以输入交换只在最底层基变换执行一次后续所有组合步骤都直接复用已经交换过的半区。理解这一点才能保证多层递归时置换与旋转因子表对齐。特殊迭代步进由于系数开始重叠特别是第二次迭代后[o1]与[0]重叠recombine需要特殊迭代方式transforms.md第二次迭代用z 8即z z[16]第 4 次迭代后布局重置重复相同步进。o1 2*n、o2 4*n、o3 6*n的偏移定义transforms.md正是为这种分段迭代服务的。15 点 AVX FFT 变换DC/AC 分离与 5×3pt 流水15 点变换是文档中唯一的非 2 的幂示例其输入必须先经过一段专用置换循环transforms.md。该循环对每个s-map[k*15]块先把tmp复制出来再依次取出1,4,7,10,13i 从 1 步进 3、2,5,8,11,14、0,3,6,9,12三段重排随后两次memmove调整最后把tmp[2]、tmp[0]放回map[1]、map[2]。文档解释transforms.md这一步把 AC 分量与 DC 分量分离并将 SIMD 方向翻转使得流水线变为一次做 5 个 3 点变换随后做 3 个 5 点变换。这正是 15 3×5 因式分解的 SIMD 化策略。fft15函数transforms.md主体分三块DC 路径由in[0..2]计算pc[0..1]、dc[0..2]乘以查表系数tab[8..11]对应 12 与 6 的旋转角见下文表格后得到三个 DC 输出AC 路径由in[3..14]分成q[0..7]差/和两组与in[11..14]组合出y[0..3]、k[0..7]再经t[0..11]三级中间量输出组合out[i*stride]按 stride 步进写出 15 个结果transforms.md其中z0[0..11]充当最后一层蝶形的临时数组。fft15使用的旋转因子表ff_tx_tab_53在仓库中由 tx_template.c 生成索引值RESCALE 前对应角度0,1cos(2π/5)72°2,3cos(2π/10)36°4,5sin(2π/5)72°6,7sin(2π/10)36°8,9cos(2π/12)30°10cos(2π/6)60°11cos(8π/6)240°RESCALE负责把三角函数值按精度类型float/double/int32缩放。15 点变换的通用 C 版本在 tx_template.cfft3与ff_tx_fft15_ns_deftx_template.c中注册证明 15 点与 3、5、7、9 点作为质数基变换被框架正式支持。文档阅读方法论把 C 展开当作汇编说明书综合全文可以总结出阅读此类文档的三个要点指令计数注释即性能契约/* 1sub 1add 2 instructions */、/* 13 OR 10ish (-2 each for second passovers!) */等注释transforms.md、transforms.md是 SIMD 指令数与寄存器压力的核算记录体现了展开到什么程度最优的工程决策偶/奇路径分离是贯穿主线从 8 点开始偶数输出与奇数输出各自独立计算transforms.md这一约定一直延续到 16 点与 split-radix 合成是理解输出布局z[0]、z[2]、z[4]、z[6]与z[1]、z[3]、z[5]、z[7]分列的钥匙C 代码与汇编一一对应本文档的 C 展开与 tx_float.asm 中的ff_tx_fft8_asm_float_sse3/avx、ff_tx_fft16_asm_floattx_float.asm及 tx_float_neon.S 中的 NEON 版本同源想验证某段 C 展开的寄存器用法直接对照同名字段符号即可。对希望深挖的读者建议按以下路径继续阅读源码置换表生成tx.c 中的parity_revtab_generator与ff_tx_gen_split_radix_parity_revtab基变换注册表tx_template.c 中 2/4/8/16 及 3/5/7/9/15 点 codelet 列表x86 汇编tx_float.asmSSE3/AVX 版 4/8/16 点与 tx_float_init.c按 CPU 特性选择 codeletAArch64 汇编tx_float_neon.S 与 tx_float_init.c通用 C 模板tx_template.cfft3/fft7/fft9等质数变换与 2 的幂变换的 ns 版本以及 tx_float.c / tx_double.c / tx_int32.c 三个按精度实例化的入口。赞分享人工智能AI Agent多模态语音AI 应用【免费下载链接】ten-frameworkOpen-source framework for conversational voice AI agents项目地址https://gitcode.com/TEN-framework/ten-framework点击查看免费下载相关推荐掌握星露谷物语XNB工具从零开始的XNB文件处理指南掌握星露谷物语XNB工具从零开始的XNB文件处理指南 你是否想给《星露谷物语》换上新皮肤、添加自定义音乐这些都需要处理游戏中的 XNB文件 一种特殊的压缩人工智能数据增强计算机视觉图像处理深入解析BlurHash算法原理与DCT变换深入解析BlurHash算法原理与DCT变换 本文深入探讨了BlurHash算法中离散余弦变换 DCT 的核心作用及其实现原理。DCT通过将图像从空间域转换到频图像处理上一篇百度网盘Mac版限速破解免费把 100KB/s 拉到 7MB/s下一篇10分钟跑通 Harepacker-resurrected冒险岛 WZ 编辑器与地图创作套件零门槛上手创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑