简介: CSDN博客专家、《Android系统多媒体进阶实战》作者
博主新书推荐:《Android系统多媒体进阶实战》🚀
Android Audio工程师专栏地址:Audio工程师进阶系列【原创干货持续更新中...... 】🚀
Android多媒体专栏地址:多媒体系统工程师系列【原创干货持续更新中...... 】🚀
专题一 二:AAOS车载系统+AOSP14系统攻城狮入门视频实战课 🚀
专题三:Android14 Binder之HIDL与AIDL通信实战课 🚀
专题四:Android15快速自定义与集成音效实战课 🚀
专题五:Android15音频策略实战课 🚀
专题六:Android15音频性能实战课(无声/杂音/断音/爆音实战案例) 🚀
人生格言: 人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药.
更多原创,欢迎关注:Android系统攻城狮

🍉🍉🍉文章目录🍉🍉🍉
🌻1.前言
本篇目的:理解
gst_audio_resampler_update()如何更新重采样器的采样率和滤波配置,分析它为什么会保留运行状态,并通过最小程序验证更新前后的输出Frame数变化。
GstAudioResampler创建后会保存输入输出采样率、滤波器参数、采样相位和历史Frame。
实时音频场景中,采样率可能动态变化,滤波质量也可能需要调整。如果每次变化都销毁并重新创建对象,会丢失现有配置和处理上下文。
gst_audio_resampler_update()负责在保留重采样器对象的前提下更新采样参数。它会重新计算有效采样率、相位增量和滤波器配置,但不会直接产生PCM输出,也不会自动清空样本历史。
#mermaid-svg-V8PG8NPTZsgpRdBV{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-V8PG8NPTZsgpRdBV .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-V8PG8NPTZsgpRdBV .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-V8PG8NPTZsgpRdBV .error-icon{fill:#552222;}#mermaid-svg-V8PG8NPTZsgpRdBV .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-V8PG8NPTZsgpRdBV .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-V8PG8NPTZsgpRdBV .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-V8PG8NPTZsgpRdBV .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-V8PG8NPTZsgpRdBV .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-V8PG8NPTZsgpRdBV .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-V8PG8NPTZsgpRdBV .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-V8PG8NPTZsgpRdBV .marker{fill:#333333;stroke:#333333;}#mermaid-svg-V8PG8NPTZsgpRdBV .marker.cross{stroke:#333333;}#mermaid-svg-V8PG8NPTZsgpRdBV svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-V8PG8NPTZsgpRdBV p{margin:0;}#mermaid-svg-V8PG8NPTZsgpRdBV .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-V8PG8NPTZsgpRdBV .cluster-label text{fill:#333;}#mermaid-svg-V8PG8NPTZsgpRdBV .cluster-label span{color:#333;}#mermaid-svg-V8PG8NPTZsgpRdBV .cluster-label span p{background-color:transparent;}#mermaid-svg-V8PG8NPTZsgpRdBV .label text,#mermaid-svg-V8PG8NPTZsgpRdBV span{fill:#333;color:#333;}#mermaid-svg-V8PG8NPTZsgpRdBV .node rect,#mermaid-svg-V8PG8NPTZsgpRdBV .node circle,#mermaid-svg-V8PG8NPTZsgpRdBV .node ellipse,#mermaid-svg-V8PG8NPTZsgpRdBV .node polygon,#mermaid-svg-V8PG8NPTZsgpRdBV .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-V8PG8NPTZsgpRdBV .rough-node .label text,#mermaid-svg-V8PG8NPTZsgpRdBV .node .label text,#mermaid-svg-V8PG8NPTZsgpRdBV .image-shape .label,#mermaid-svg-V8PG8NPTZsgpRdBV .icon-shape .label{text-anchor:middle;}#mermaid-svg-V8PG8NPTZsgpRdBV .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-V8PG8NPTZsgpRdBV .rough-node .label,#mermaid-svg-V8PG8NPTZsgpRdBV .node .label,#mermaid-svg-V8PG8NPTZsgpRdBV .image-shape .label,#mermaid-svg-V8PG8NPTZsgpRdBV .icon-shape .label{text-align:center;}#mermaid-svg-V8PG8NPTZsgpRdBV .node.clickable{cursor:pointer;}#mermaid-svg-V8PG8NPTZsgpRdBV .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-V8PG8NPTZsgpRdBV .arrowheadPath{fill:#333333;}#mermaid-svg-V8PG8NPTZsgpRdBV .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-V8PG8NPTZsgpRdBV .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-V8PG8NPTZsgpRdBV .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-V8PG8NPTZsgpRdBV .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-V8PG8NPTZsgpRdBV .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-V8PG8NPTZsgpRdBV .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-V8PG8NPTZsgpRdBV .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-V8PG8NPTZsgpRdBV .cluster text{fill:#333;}#mermaid-svg-V8PG8NPTZsgpRdBV .cluster span{color:#333;}#mermaid-svg-V8PG8NPTZsgpRdBV div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-V8PG8NPTZsgpRdBV .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-V8PG8NPTZsgpRdBV rect.text{fill:none;stroke-width:0;}#mermaid-svg-V8PG8NPTZsgpRdBV .icon-shape,#mermaid-svg-V8PG8NPTZsgpRdBV .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-V8PG8NPTZsgpRdBV .icon-shape p,#mermaid-svg-V8PG8NPTZsgpRdBV .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-V8PG8NPTZsgpRdBV .icon-shape .label rect,#mermaid-svg-V8PG8NPTZsgpRdBV .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-V8PG8NPTZsgpRdBV .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-V8PG8NPTZsgpRdBV .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-V8PG8NPTZsgpRdBV :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 已有GstAudioResampler和当前配置
gst_audio_resampler_update
约分采样率并更新相位参数
按新配置调整滤波路径
保留对象继续重采样
🌻2.应用场景和用法
2.1动态采样率和滤波配置更新
gst_audio_resampler_update()主要用于以下场景:
| 场景 | 调用方式 | 更新结果 | 注意事项 |
|---|---|---|---|
| 输入输出采样率变化 | 传入新的in_rate和out_rate |
重新计算采样率比例和相位增量 | 不能继续使用旧采样率推导Frame数 |
| 可变采样率处理 | 创建对象时设置GST_AUDIO_RESAMPLER_FLAG_VARIABLE_RATE |
适合重复调用更新接口 | 更新过程必须与其他重采样操作串行 |
| 滤波器抽头数量变化 | 通过options传入新的滤波配置 |
重新计算n_taps并调整历史缓冲区 |
抽头变化可能引起内存移动 |
| 滤波截止频率变化 | 通过options修改滤波参数 |
重新生成或更新滤波器参数 | 不会自动输出新的PCM数据 |
| 实时音频时钟漂移修正 | 周期性更新目标采样率 | 让后续输入使用新的采样比例 | 更新前后的相位会被重新换算 |
| 长时间音频流处理 | 复用同一个重采样器 | 避免频繁销毁和创建对象 | 对象释放仍由调用者负责 |
| 新流完全重新开始 | 先调用gst_audio_resampler_reset(),再决定是否更新 |
清除旧样本后处理新流 | update()本身不会清空历史 |
| 只想清理历史样本 | 调用gst_audio_resampler_reset() |
保留当前采样率和滤波配置 | 不能用update()代替复位 |
| 需要改变重采样方法 | 重新创建重采样器 | 使用新的method建立对象 |
update()不修改对象的重采样方法 |
| 需要查询更新结果 | 检查gboolean返回值并调用查询接口 |
确认新参数已经生效 | 不能只依赖调用前的配置记录 |
gst_audio_resampler_update()不会创建新的GstAudioResampler,也不会改变对象所有权。更新时如果仍然保留历史样本,函数会尝试按照新的滤波窗口调整历史数据。
2.2函数原型与返回语义
函数原型如下:
c
gboolean
gst_audio_resampler_update (GstAudioResampler *resampler,
gint in_rate,
gint out_rate,
GstStructure *options);
| 参数或返回值 | 含义 |
|---|---|
resampler |
已经创建的有效重采样器 |
in_rate |
新的输入采样率,传入0表示保持当前值 |
out_rate |
新的输出采样率,传入0表示保持当前值 |
options |
新的滤波配置,传入NULL表示沿用当前配置 |
| 返回值 | TRUE表示更新路径完成,FALSE表示参数检查失败 |
最小调用方式如下:
c
gboolean updated;
updated = gst_audio_resampler_update (
resampler,
48000,
44100,
NULL);
当options不为NULL时,函数会复制传入的GstStructure,调用者仍然负责释放原始配置对象。
调用前,重采样器已经拥有一组采样率、滤波器和运行状态。调用过程中,函数同步完成参数计算、滤波器调整和处理函数设置,不进入其他线程,不触发回调,也不产生输出Frame。
调用后:
in_rate和out_rate被更新为新的有效采样率。samp_inc和samp_frac重新描述输入输出采样比例。- 旧相位会按照新的输出采样率重新换算。
- 传入新配置时,滤波器参数和历史缓冲区可能发生变化。
- 已有样本历史不会因为
update()自动清空。
gst_audio_resampler_update()不能与gst_audio_resampler_resample()或其他重采样器操作并发调用。需要清空历史时,应使用gst_audio_resampler_reset()。需要释放对象时,应使用gst_audio_resampler_free()。
🌻3.调用流程剖析
3.1采样率参数更新主链路
gst_audio_resampler_update()首先处理采样率参数。传入值小于等于0时,源码继续使用对象中的当前采样率。
如果对象已经处理过数据,函数会先把旧的samp_phase按照新的输出采样率进行缩放。随后使用最大公约数约分输入输出采样率,得到内部使用的有效比例。
例如48000Hz和44100Hz会约分为160和147。之后函数写入:
c
resampler->samp_phase = samp_phase / gcd;
resampler->in_rate = in_rate / gcd;
resampler->out_rate = out_rate / gcd;
resampler->samp_inc = in_rate / out_rate;
resampler->samp_frac = in_rate % out_rate;
这些字段会被后续滤波和Frame数量计算使用。
#mermaid-svg-7oNbzFwY7RmW5xDt{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-7oNbzFwY7RmW5xDt .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-7oNbzFwY7RmW5xDt .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-7oNbzFwY7RmW5xDt .error-icon{fill:#552222;}#mermaid-svg-7oNbzFwY7RmW5xDt .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-7oNbzFwY7RmW5xDt .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-7oNbzFwY7RmW5xDt .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-7oNbzFwY7RmW5xDt .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-7oNbzFwY7RmW5xDt .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-7oNbzFwY7RmW5xDt .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-7oNbzFwY7RmW5xDt .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-7oNbzFwY7RmW5xDt .marker{fill:#333333;stroke:#333333;}#mermaid-svg-7oNbzFwY7RmW5xDt .marker.cross{stroke:#333333;}#mermaid-svg-7oNbzFwY7RmW5xDt svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-7oNbzFwY7RmW5xDt p{margin:0;}#mermaid-svg-7oNbzFwY7RmW5xDt .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-7oNbzFwY7RmW5xDt .cluster-label text{fill:#333;}#mermaid-svg-7oNbzFwY7RmW5xDt .cluster-label span{color:#333;}#mermaid-svg-7oNbzFwY7RmW5xDt .cluster-label span p{background-color:transparent;}#mermaid-svg-7oNbzFwY7RmW5xDt .label text,#mermaid-svg-7oNbzFwY7RmW5xDt span{fill:#333;color:#333;}#mermaid-svg-7oNbzFwY7RmW5xDt .node rect,#mermaid-svg-7oNbzFwY7RmW5xDt .node circle,#mermaid-svg-7oNbzFwY7RmW5xDt .node ellipse,#mermaid-svg-7oNbzFwY7RmW5xDt .node polygon,#mermaid-svg-7oNbzFwY7RmW5xDt .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-7oNbzFwY7RmW5xDt .rough-node .label text,#mermaid-svg-7oNbzFwY7RmW5xDt .node .label text,#mermaid-svg-7oNbzFwY7RmW5xDt .image-shape .label,#mermaid-svg-7oNbzFwY7RmW5xDt .icon-shape .label{text-anchor:middle;}#mermaid-svg-7oNbzFwY7RmW5xDt .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-7oNbzFwY7RmW5xDt .rough-node .label,#mermaid-svg-7oNbzFwY7RmW5xDt .node .label,#mermaid-svg-7oNbzFwY7RmW5xDt .image-shape .label,#mermaid-svg-7oNbzFwY7RmW5xDt .icon-shape .label{text-align:center;}#mermaid-svg-7oNbzFwY7RmW5xDt .node.clickable{cursor:pointer;}#mermaid-svg-7oNbzFwY7RmW5xDt .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-7oNbzFwY7RmW5xDt .arrowheadPath{fill:#333333;}#mermaid-svg-7oNbzFwY7RmW5xDt .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-7oNbzFwY7RmW5xDt .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-7oNbzFwY7RmW5xDt .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7oNbzFwY7RmW5xDt .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-7oNbzFwY7RmW5xDt .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7oNbzFwY7RmW5xDt .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-7oNbzFwY7RmW5xDt .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-7oNbzFwY7RmW5xDt .cluster text{fill:#333;}#mermaid-svg-7oNbzFwY7RmW5xDt .cluster span{color:#333;}#mermaid-svg-7oNbzFwY7RmW5xDt div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-7oNbzFwY7RmW5xDt .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-7oNbzFwY7RmW5xDt rect.text{fill:none;stroke-width:0;}#mermaid-svg-7oNbzFwY7RmW5xDt .icon-shape,#mermaid-svg-7oNbzFwY7RmW5xDt .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7oNbzFwY7RmW5xDt .icon-shape p,#mermaid-svg-7oNbzFwY7RmW5xDt .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-7oNbzFwY7RmW5xDt .icon-shape .label rect,#mermaid-svg-7oNbzFwY7RmW5xDt .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7oNbzFwY7RmW5xDt .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-7oNbzFwY7RmW5xDt .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-7oNbzFwY7RmW5xDt :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
新采样率和options
规范化速率与旧相位
约分并写入采样参数
是否提供新options
重算滤波器并调整历史
复用配置并准备滤波路径
options为NULL时,函数复用现有配置。对于完整滤波表模式,它会根据新的输出采样率准备滤波缓存。
options不为NULL时,函数会复制新配置,保存旧的n_taps,重新计算滤波器参数,并根据新旧抽头数量调整历史缓冲区。
3.2滤波器重建与历史数据迁移
传入新options后,源码会执行以下处理:
c
old_n_taps = resampler->n_taps;
resampler->options = gst_structure_copy (options);
resampler_calculate_taps (resampler);
diff = ((gint) resampler->n_taps - old_n_taps) / 2;
如果n_taps发生变化,函数会确保历史缓冲区容量满足新的滤波窗口要求,然后使用memmove()移动已有历史数据,并按照抽头数量差异修正samples_avail。
这个过程不是简单地把滤波器指针替换掉。历史数据必须调整到新滤波窗口对应的位置,否则下一次重采样会从错误的样本位置开始。
如果抽头数量没有变化,历史数据不需要移动。无论配置是否变化,函数最后都会调用setup_functions(),根据当前方法、格式、布局和滤波模式选择处理函数。
#mermaid-svg-ptXnfJY5PwD6rkMp{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ptXnfJY5PwD6rkMp .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ptXnfJY5PwD6rkMp .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ptXnfJY5PwD6rkMp .error-icon{fill:#552222;}#mermaid-svg-ptXnfJY5PwD6rkMp .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ptXnfJY5PwD6rkMp .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ptXnfJY5PwD6rkMp .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ptXnfJY5PwD6rkMp .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ptXnfJY5PwD6rkMp .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ptXnfJY5PwD6rkMp .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ptXnfJY5PwD6rkMp .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ptXnfJY5PwD6rkMp .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ptXnfJY5PwD6rkMp .marker.cross{stroke:#333333;}#mermaid-svg-ptXnfJY5PwD6rkMp svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ptXnfJY5PwD6rkMp p{margin:0;}#mermaid-svg-ptXnfJY5PwD6rkMp .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ptXnfJY5PwD6rkMp .cluster-label text{fill:#333;}#mermaid-svg-ptXnfJY5PwD6rkMp .cluster-label span{color:#333;}#mermaid-svg-ptXnfJY5PwD6rkMp .cluster-label span p{background-color:transparent;}#mermaid-svg-ptXnfJY5PwD6rkMp .label text,#mermaid-svg-ptXnfJY5PwD6rkMp span{fill:#333;color:#333;}#mermaid-svg-ptXnfJY5PwD6rkMp .node rect,#mermaid-svg-ptXnfJY5PwD6rkMp .node circle,#mermaid-svg-ptXnfJY5PwD6rkMp .node ellipse,#mermaid-svg-ptXnfJY5PwD6rkMp .node polygon,#mermaid-svg-ptXnfJY5PwD6rkMp .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ptXnfJY5PwD6rkMp .rough-node .label text,#mermaid-svg-ptXnfJY5PwD6rkMp .node .label text,#mermaid-svg-ptXnfJY5PwD6rkMp .image-shape .label,#mermaid-svg-ptXnfJY5PwD6rkMp .icon-shape .label{text-anchor:middle;}#mermaid-svg-ptXnfJY5PwD6rkMp .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ptXnfJY5PwD6rkMp .rough-node .label,#mermaid-svg-ptXnfJY5PwD6rkMp .node .label,#mermaid-svg-ptXnfJY5PwD6rkMp .image-shape .label,#mermaid-svg-ptXnfJY5PwD6rkMp .icon-shape .label{text-align:center;}#mermaid-svg-ptXnfJY5PwD6rkMp .node.clickable{cursor:pointer;}#mermaid-svg-ptXnfJY5PwD6rkMp .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ptXnfJY5PwD6rkMp .arrowheadPath{fill:#333333;}#mermaid-svg-ptXnfJY5PwD6rkMp .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ptXnfJY5PwD6rkMp .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ptXnfJY5PwD6rkMp .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ptXnfJY5PwD6rkMp .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ptXnfJY5PwD6rkMp .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ptXnfJY5PwD6rkMp .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ptXnfJY5PwD6rkMp .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ptXnfJY5PwD6rkMp .cluster text{fill:#333;}#mermaid-svg-ptXnfJY5PwD6rkMp .cluster span{color:#333;}#mermaid-svg-ptXnfJY5PwD6rkMp div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ptXnfJY5PwD6rkMp .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ptXnfJY5PwD6rkMp rect.text{fill:none;stroke-width:0;}#mermaid-svg-ptXnfJY5PwD6rkMp .icon-shape,#mermaid-svg-ptXnfJY5PwD6rkMp .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ptXnfJY5PwD6rkMp .icon-shape p,#mermaid-svg-ptXnfJY5PwD6rkMp .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ptXnfJY5PwD6rkMp .icon-shape .label rect,#mermaid-svg-ptXnfJY5PwD6rkMp .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ptXnfJY5PwD6rkMp .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ptXnfJY5PwD6rkMp .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ptXnfJY5PwD6rkMp :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
旧n_taps和历史缓冲
计算新滤波器参数
n_taps是否变化
移动历史并修正可用Frame
保留历史布局
使用新配置继续重采样
gst_audio_resampler_update()不会调用gst_audio_resampler_reset()。因此,采样率更新和历史清理是两种不同动作:
- 只改变采样率或滤波配置,使用
gst_audio_resampler_update()。 - 只丢弃旧样本,使用
gst_audio_resampler_reset()。 - 新流同时改变参数和历史,先根据数据边界决定是否复位,再调用更新接口。
🌻4.实战案例
4.1创建可更新的重采样器
本案例创建一个S16单声道重采样器,初始输入采样率为48000Hz,输出采样率为24000Hz,使用最近邻方法。
最近邻方法使用2个抽头。创建完成后,输入480个Frame时:
text
内部输入采样率 = 2
内部输出采样率 = 1
n_taps = 2
输出Frame数 = ((480 - 2) × 1) ÷ 2 + 1 = 240
创建对象时设置GST_AUDIO_RESAMPLER_FLAG_VARIABLE_RATE,表示后续允许动态更新采样率。
4.2更新采样率并验证输出规模
程序先查询48000Hz到24000Hz配置下的输出Frame数,再调用:
c
gst_audio_resampler_update (resampler, 48000, 44100, NULL);
更新成功后,内部采样率约分为160和147。再次查询480个输入Frame时:
text
输出Frame数 = ((480 - 2) × 147) ÷ 160 + 1 = 440
这个案例没有先写入历史样本,因此可以直接观察采样率更新对输出规模的影响。gst_audio_resampler_get_max_latency()在更新前后都返回1,说明滤波器抽头数量没有改变。
4.3完整代码与关键输出
c
#include <gst/gst.h>
#include <gst/audio/audio.h>
int
main (int argc, char *argv[])
{
GstAudioResampler *resampler;
gboolean updated;
gsize before_update;
gsize after_update;
gsize latency;
gst_init (&argc, &argv);
resampler = gst_audio_resampler_new (
GST_AUDIO_RESAMPLER_METHOD_NEAREST,
GST_AUDIO_RESAMPLER_FLAG_VARIABLE_RATE,
GST_AUDIO_FORMAT_S16,
1,
48000,
24000,
NULL);
if (resampler == NULL) {
g_printerr ("Failed to create audio resampler\n");
return 1;
}
before_update =
gst_audio_resampler_get_out_frames (resampler, 480);
latency =
gst_audio_resampler_get_max_latency (resampler);
updated = gst_audio_resampler_update (
resampler,
48000,
44100,
NULL);
if (!updated) {
g_printerr ("Failed to update audio resampler\n");
gst_audio_resampler_free (resampler);
return 1;
}
after_update =
gst_audio_resampler_get_out_frames (resampler, 480);
g_print ("Before update output frames: %"
G_GSIZE_FORMAT "\n", before_update);
g_print ("Update result: %s\n",
updated ? "TRUE" : "FALSE");
g_print ("After update output frames: %"
G_GSIZE_FORMAT "\n", after_update);
g_print ("Max latency: %" G_GSIZE_FORMAT "\n",
latency);
gst_audio_resampler_free (resampler);
return 0;
}
编译运行:
bash
gcc test_resampler_update.c -o test_resampler_update \
$(pkg-config --cflags --libs gstreamer-1.0 gstreamer-audio-1.0)
./test_resampler_update
关键输出:
text
Before update output frames: 240
Update result: TRUE
After update output frames: 440
Max latency: 1
🌻5.总结
gst_audio_resampler_update()就是在保留重采样器对象和历史处理上下文的前提下,更新采样率及可选滤波配置,让后续重采样使用新的参数。