Linux Gstreamer深度解析之gst_audio_converter_reset调用流程与实战(二十一)

简介: CSDN博客专家、《Android系统多媒体进阶实战》作者

博主新书推荐:《Android系统多媒体进阶实战》🚀

Android Audio工程师专栏地址:Audio工程师进阶系列原创干货持续更新中...... 】🚀

Android多媒体专栏地址:多媒体系统工程师系列原创干货持续更新中...... 】🚀

专题一 二:AAOS车载系统+AOSP14系统攻城狮入门视频实战课 🚀

专题三:Android14 Binder之HIDL与AIDL通信实战课 🚀

专题四:Android15快速自定义与集成音效实战课 🚀

专题五:Android15音频策略实战课 🚀

专题六:Android15音频性能实战课(无声/杂音/断音/爆音实战案例) 🚀

人生格言: 人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药.
更多原创,欢迎关注:Android系统攻城狮

🍉🍉🍉文章目录🍉🍉🍉

      • 🌻1.前言
      • 🌻2.应用场景和用法
        • [🌻2.1 应用场景](#🌻2.1 应用场景)
        • [🌻2.2 函数用法](#🌻2.2 函数用法)
      • 🌻3.调用流程剖析
        • [🌻3.1 分发到重采样器和量化器](#🌻3.1 分发到重采样器和量化器)
        • [🌻3.2 清除转换历史但保留处理配置](#🌻3.2 清除转换历史但保留处理配置)
      • 🌻4.实战案例
        • [🌻4.1 创建带重采样历史的转换器](#🌻4.1 创建带重采样历史的转换器)
        • [🌻4.2 重置后再次处理同一段输入](#🌻4.2 重置后再次处理同一段输入)
        • [🌻4.3 验证两次输出一致](#🌻4.3 验证两次输出一致)
      • 🌻5.总结

🌻1.前言

本篇目的:理解gst_audio_converter_reset()如何清除GstAudioConverter内部的转换历史,以及它为什么不会重新配置音频格式或重建转换器。

GstAudioConverter用于连接输入音频格式和输出音频格式。创建转换器时,GStreamer会根据输入输出的GstAudioInfo建立一条可选处理链:

text 复制代码
解包
↓
格式转换
↓
声道混音
↓
重采样
↓
量化
↓
布局转换
↓
打包

其中,重采样和量化可能跨越多次gst_audio_converter_samples()调用保存中间状态:

text 复制代码
重采样器:滤波器历史、采样位置、可用Sample数量
量化器:误差反馈历史

当发生Seek、Flush、流重新开始或时间戳不连续时,下一段音频不应该继续继承上一段音频的历史。此时需要调用:

cpp 复制代码
gst_audio_converter_reset(converter);

它解决的是:

text 复制代码
保留转换配置
+
清除跨Buffer历史
+
让下一次转换从初始处理状态开始

#mermaid-svg-QHt2mMmBhm2MUjwf{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-QHt2mMmBhm2MUjwf .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-QHt2mMmBhm2MUjwf .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-QHt2mMmBhm2MUjwf .error-icon{fill:#552222;}#mermaid-svg-QHt2mMmBhm2MUjwf .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-QHt2mMmBhm2MUjwf .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-QHt2mMmBhm2MUjwf .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-QHt2mMmBhm2MUjwf .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-QHt2mMmBhm2MUjwf .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-QHt2mMmBhm2MUjwf .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-QHt2mMmBhm2MUjwf .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-QHt2mMmBhm2MUjwf .marker{fill:#333333;stroke:#333333;}#mermaid-svg-QHt2mMmBhm2MUjwf .marker.cross{stroke:#333333;}#mermaid-svg-QHt2mMmBhm2MUjwf svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-QHt2mMmBhm2MUjwf p{margin:0;}#mermaid-svg-QHt2mMmBhm2MUjwf .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-QHt2mMmBhm2MUjwf .cluster-label text{fill:#333;}#mermaid-svg-QHt2mMmBhm2MUjwf .cluster-label span{color:#333;}#mermaid-svg-QHt2mMmBhm2MUjwf .cluster-label span p{background-color:transparent;}#mermaid-svg-QHt2mMmBhm2MUjwf .label text,#mermaid-svg-QHt2mMmBhm2MUjwf span{fill:#333;color:#333;}#mermaid-svg-QHt2mMmBhm2MUjwf .node rect,#mermaid-svg-QHt2mMmBhm2MUjwf .node circle,#mermaid-svg-QHt2mMmBhm2MUjwf .node ellipse,#mermaid-svg-QHt2mMmBhm2MUjwf .node polygon,#mermaid-svg-QHt2mMmBhm2MUjwf .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-QHt2mMmBhm2MUjwf .rough-node .label text,#mermaid-svg-QHt2mMmBhm2MUjwf .node .label text,#mermaid-svg-QHt2mMmBhm2MUjwf .image-shape .label,#mermaid-svg-QHt2mMmBhm2MUjwf .icon-shape .label{text-anchor:middle;}#mermaid-svg-QHt2mMmBhm2MUjwf .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-QHt2mMmBhm2MUjwf .rough-node .label,#mermaid-svg-QHt2mMmBhm2MUjwf .node .label,#mermaid-svg-QHt2mMmBhm2MUjwf .image-shape .label,#mermaid-svg-QHt2mMmBhm2MUjwf .icon-shape .label{text-align:center;}#mermaid-svg-QHt2mMmBhm2MUjwf .node.clickable{cursor:pointer;}#mermaid-svg-QHt2mMmBhm2MUjwf .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-QHt2mMmBhm2MUjwf .arrowheadPath{fill:#333333;}#mermaid-svg-QHt2mMmBhm2MUjwf .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-QHt2mMmBhm2MUjwf .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-QHt2mMmBhm2MUjwf .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-QHt2mMmBhm2MUjwf .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-QHt2mMmBhm2MUjwf .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-QHt2mMmBhm2MUjwf .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-QHt2mMmBhm2MUjwf .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-QHt2mMmBhm2MUjwf .cluster text{fill:#333;}#mermaid-svg-QHt2mMmBhm2MUjwf .cluster span{color:#333;}#mermaid-svg-QHt2mMmBhm2MUjwf 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-QHt2mMmBhm2MUjwf .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-QHt2mMmBhm2MUjwf rect.text{fill:none;stroke-width:0;}#mermaid-svg-QHt2mMmBhm2MUjwf .icon-shape,#mermaid-svg-QHt2mMmBhm2MUjwf .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-QHt2mMmBhm2MUjwf .icon-shape p,#mermaid-svg-QHt2mMmBhm2MUjwf .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-QHt2mMmBhm2MUjwf .icon-shape .label rect,#mermaid-svg-QHt2mMmBhm2MUjwf .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-QHt2mMmBhm2MUjwf .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-QHt2mMmBhm2MUjwf .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-QHt2mMmBhm2MUjwf :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} ① 已创建转换器
② 连续处理音频
③ 内部产生历史
④ reset清除历史
⑤ 新音频重新开始

需要注意:

text 复制代码
gst_audio_converter_reset()
        ≠
重新创建GstAudioConverter

它不会改变输入输出格式、采样率、声道数、混音矩阵或已经建立的处理链。


🌻2.应用场景和用法

🌻2.1 应用场景

音频转换器通常会被多个Buffer连续复用。只要转换过程具有跨调用状态,就不能把每个Buffer当成完全独立的数据。

以采样率转换为例,重采样器需要根据前一个Buffer末尾的Sample计算下一个Buffer的输出。如果输入流在中间发生切换,旧流的滤波器历史就不再适用于新流。

gst_audio_converter_reset()主要适合以下场景:

  1. Seek或Flush之后重新处理音频。
  2. 输入流发生断点,下一段音频不应继承上一段的滤波状态。
  3. 处理新的Stream或Segment之前清除旧的转换历史。
  4. 量化器使用误差反馈时,避免上一段音频的误差影响下一段音频。

GStreamer的audioresampleElement也会在Flush、Stream开始、Segment变化、时间戳不连续或音频格式变化等场景调用该函数。

但上层Element的状态不由它负责。例如audioresample还需要单独清除时间戳、输入输出计数和Discontinuity标记。gst_audio_converter_reset()只负责转换器内部的历史状态。

如果转换器只是执行没有跨Buffer历史的直通或简单格式路径,内部可能没有resamplerquant对象。此时调用该函数不会产生可观察的数据变化。


🌻2.2 函数用法

函数原型:

c 复制代码
void gst_audio_converter_reset(GstAudioConverter *convert);

参数:

参数 作用
convert gst_audio_converter_new()创建的有效GstAudioConverter对象

该函数没有返回值。

调用者必须保证:

text 复制代码
convert != NULL

当前实现不会在函数内部检查空指针。转换器创建失败时,不能直接调用gst_audio_converter_reset()

最小调用方式:

cpp 复制代码
GstAudioConverter *converter;

converter = gst_audio_converter_new(
        GST_AUDIO_CONVERTER_FLAG_NONE,
        &in_info,
        &out_info,
        NULL);

if (converter == NULL) {
    return FALSE;
}

gst_audio_converter_reset(converter);

调用前后的变化可以概括为:

内容 reset前 reset后
输入输出格式 已配置 不变
输入输出采样率 已配置 不变
声道数和布局 已配置 不变
转换处理链 已创建 不变
重采样历史 可能包含旧Sample和相位 恢复初始处理状态
量化误差历史 可能存在误差反馈 清除误差缓存
配置结构 已保存 不变

gst_audio_converter_reset()与相邻API的边界如下:

text 复制代码
gst_audio_converter_new()
    创建转换器和处理链

gst_audio_converter_update_config()
    更新采样率或额外配置

gst_audio_converter_samples()
    使用处理链转换音频并积累历史

gst_audio_converter_reset()
    清除重采样器和量化器历史

gst_audio_converter_free()
    释放转换器及其资源

因此,如果输入输出格式已经发生变化,不能只调用reset()。格式更新或处理链重建仍然需要由update_config()或重新创建转换器完成。


🌻3.调用流程剖析

🌻3.1 分发到重采样器和量化器

gst_audio_converter_reset()的实现非常短:

cpp 复制代码
void
gst_audio_converter_reset(GstAudioConverter *convert)
{
    if (convert->resampler)
        gst_audio_resampler_reset(convert->resampler);

    if (convert->quant)
        gst_audio_quantize_reset(convert->quant);
}

函数本身不遍历全部转换链,也不释放GstAudioConverter

执行过程只有两个可选分支:

  1. 如果转换器包含GstAudioResampler,调用gst_audio_resampler_reset()
  2. 如果转换器包含GstAudioQuantize,调用gst_audio_quantize_reset()
  3. 两个对象都不存在时,函数直接结束。

#mermaid-svg-jpYkCwYINnseLgfo{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-jpYkCwYINnseLgfo .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-jpYkCwYINnseLgfo .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-jpYkCwYINnseLgfo .error-icon{fill:#552222;}#mermaid-svg-jpYkCwYINnseLgfo .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-jpYkCwYINnseLgfo .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-jpYkCwYINnseLgfo .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-jpYkCwYINnseLgfo .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-jpYkCwYINnseLgfo .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-jpYkCwYINnseLgfo .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-jpYkCwYINnseLgfo .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-jpYkCwYINnseLgfo .marker{fill:#333333;stroke:#333333;}#mermaid-svg-jpYkCwYINnseLgfo .marker.cross{stroke:#333333;}#mermaid-svg-jpYkCwYINnseLgfo svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-jpYkCwYINnseLgfo p{margin:0;}#mermaid-svg-jpYkCwYINnseLgfo .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-jpYkCwYINnseLgfo .cluster-label text{fill:#333;}#mermaid-svg-jpYkCwYINnseLgfo .cluster-label span{color:#333;}#mermaid-svg-jpYkCwYINnseLgfo .cluster-label span p{background-color:transparent;}#mermaid-svg-jpYkCwYINnseLgfo .label text,#mermaid-svg-jpYkCwYINnseLgfo span{fill:#333;color:#333;}#mermaid-svg-jpYkCwYINnseLgfo .node rect,#mermaid-svg-jpYkCwYINnseLgfo .node circle,#mermaid-svg-jpYkCwYINnseLgfo .node ellipse,#mermaid-svg-jpYkCwYINnseLgfo .node polygon,#mermaid-svg-jpYkCwYINnseLgfo .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-jpYkCwYINnseLgfo .rough-node .label text,#mermaid-svg-jpYkCwYINnseLgfo .node .label text,#mermaid-svg-jpYkCwYINnseLgfo .image-shape .label,#mermaid-svg-jpYkCwYINnseLgfo .icon-shape .label{text-anchor:middle;}#mermaid-svg-jpYkCwYINnseLgfo .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-jpYkCwYINnseLgfo .rough-node .label,#mermaid-svg-jpYkCwYINnseLgfo .node .label,#mermaid-svg-jpYkCwYINnseLgfo .image-shape .label,#mermaid-svg-jpYkCwYINnseLgfo .icon-shape .label{text-align:center;}#mermaid-svg-jpYkCwYINnseLgfo .node.clickable{cursor:pointer;}#mermaid-svg-jpYkCwYINnseLgfo .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-jpYkCwYINnseLgfo .arrowheadPath{fill:#333333;}#mermaid-svg-jpYkCwYINnseLgfo .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-jpYkCwYINnseLgfo .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-jpYkCwYINnseLgfo .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-jpYkCwYINnseLgfo .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-jpYkCwYINnseLgfo .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-jpYkCwYINnseLgfo .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-jpYkCwYINnseLgfo .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-jpYkCwYINnseLgfo .cluster text{fill:#333;}#mermaid-svg-jpYkCwYINnseLgfo .cluster span{color:#333;}#mermaid-svg-jpYkCwYINnseLgfo 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-jpYkCwYINnseLgfo .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-jpYkCwYINnseLgfo rect.text{fill:none;stroke-width:0;}#mermaid-svg-jpYkCwYINnseLgfo .icon-shape,#mermaid-svg-jpYkCwYINnseLgfo .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-jpYkCwYINnseLgfo .icon-shape p,#mermaid-svg-jpYkCwYINnseLgfo .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-jpYkCwYINnseLgfo .icon-shape .label rect,#mermaid-svg-jpYkCwYINnseLgfo .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-jpYkCwYINnseLgfo .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-jpYkCwYINnseLgfo .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-jpYkCwYINnseLgfo :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是



GstAudioConverter
存在resampler?
重置GstAudioResampler
存在quant?
重置GstAudioQuantize
同步返回

该调用是同步的:

text 复制代码
调用者线程
    ↓
gst_audio_converter_reset()
    ↓
重采样器 reset
    ↓
量化器 reset
    ↓
函数返回

它不会发送GStreamer事件,不会触发Pad回调,也不会向其他进程发送请求。


🌻3.2 清除转换历史但保留处理配置

gst_audio_resampler_reset()主要清除重采样器在连续处理过程中积累的状态:

text 复制代码
滤波器需要继承的Sample历史
采样索引
当前可用Sample数量

重置后,下一次输入会按照新的一段音频重新建立重采样上下文。

gst_audio_quantize_reset()则清除量化器的误差缓存:

text 复制代码
上一段音频的量化误差

这对噪声整形和误差反馈尤其重要。否则新音频开头的量化结果可能受到旧音频尾部误差的影响。

整个关系可以表示为:
#mermaid-svg-PO5hEbsEulZV8Git{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-PO5hEbsEulZV8Git .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-PO5hEbsEulZV8Git .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-PO5hEbsEulZV8Git .error-icon{fill:#552222;}#mermaid-svg-PO5hEbsEulZV8Git .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-PO5hEbsEulZV8Git .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-PO5hEbsEulZV8Git .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-PO5hEbsEulZV8Git .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-PO5hEbsEulZV8Git .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-PO5hEbsEulZV8Git .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-PO5hEbsEulZV8Git .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-PO5hEbsEulZV8Git .marker{fill:#333333;stroke:#333333;}#mermaid-svg-PO5hEbsEulZV8Git .marker.cross{stroke:#333333;}#mermaid-svg-PO5hEbsEulZV8Git svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-PO5hEbsEulZV8Git p{margin:0;}#mermaid-svg-PO5hEbsEulZV8Git .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-PO5hEbsEulZV8Git .cluster-label text{fill:#333;}#mermaid-svg-PO5hEbsEulZV8Git .cluster-label span{color:#333;}#mermaid-svg-PO5hEbsEulZV8Git .cluster-label span p{background-color:transparent;}#mermaid-svg-PO5hEbsEulZV8Git .label text,#mermaid-svg-PO5hEbsEulZV8Git span{fill:#333;color:#333;}#mermaid-svg-PO5hEbsEulZV8Git .node rect,#mermaid-svg-PO5hEbsEulZV8Git .node circle,#mermaid-svg-PO5hEbsEulZV8Git .node ellipse,#mermaid-svg-PO5hEbsEulZV8Git .node polygon,#mermaid-svg-PO5hEbsEulZV8Git .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-PO5hEbsEulZV8Git .rough-node .label text,#mermaid-svg-PO5hEbsEulZV8Git .node .label text,#mermaid-svg-PO5hEbsEulZV8Git .image-shape .label,#mermaid-svg-PO5hEbsEulZV8Git .icon-shape .label{text-anchor:middle;}#mermaid-svg-PO5hEbsEulZV8Git .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-PO5hEbsEulZV8Git .rough-node .label,#mermaid-svg-PO5hEbsEulZV8Git .node .label,#mermaid-svg-PO5hEbsEulZV8Git .image-shape .label,#mermaid-svg-PO5hEbsEulZV8Git .icon-shape .label{text-align:center;}#mermaid-svg-PO5hEbsEulZV8Git .node.clickable{cursor:pointer;}#mermaid-svg-PO5hEbsEulZV8Git .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-PO5hEbsEulZV8Git .arrowheadPath{fill:#333333;}#mermaid-svg-PO5hEbsEulZV8Git .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-PO5hEbsEulZV8Git .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-PO5hEbsEulZV8Git .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-PO5hEbsEulZV8Git .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-PO5hEbsEulZV8Git .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-PO5hEbsEulZV8Git .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-PO5hEbsEulZV8Git .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-PO5hEbsEulZV8Git .cluster text{fill:#333;}#mermaid-svg-PO5hEbsEulZV8Git .cluster span{color:#333;}#mermaid-svg-PO5hEbsEulZV8Git 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-PO5hEbsEulZV8Git .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-PO5hEbsEulZV8Git rect.text{fill:none;stroke-width:0;}#mermaid-svg-PO5hEbsEulZV8Git .icon-shape,#mermaid-svg-PO5hEbsEulZV8Git .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-PO5hEbsEulZV8Git .icon-shape p,#mermaid-svg-PO5hEbsEulZV8Git .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-PO5hEbsEulZV8Git .icon-shape .label rect,#mermaid-svg-PO5hEbsEulZV8Git .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-PO5hEbsEulZV8Git .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-PO5hEbsEulZV8Git .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-PO5hEbsEulZV8Git :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Buffer N
转换器积累历史
流边界或Discontinuity
gst_audio_converter_reset
Buffer N+1从初始历史开始

重置后仍然保留:

text 复制代码
convert->in
convert->out
convert->config
convert->chain_end
convert->mix
convert->resampler
convert->quant

这里的"恢复初始状态"指恢复处理历史,不是把对象退化成未配置对象。

如果上层还需要排空重采样延迟,应先处理待输出的历史Sample,再调用reset()audioresampleElement在相关路径中会先执行Drain,然后再清除转换器状态。


🌻4.实战案例

🌻4.1 创建带重采样历史的转换器

为了让reset()产生可验证的效果,创建一个单声道采样率转换器:

text 复制代码
输入:S16,48000Hz
输出:S16,44100Hz

输入和输出采样率不同,gst_audio_converter_new()会在内部创建重采样器。

准备一段固定的480个输入Sample:

cpp 复制代码
gint16 input[480];

for (gsize i = 0; i < G_N_ELEMENTS(input); i++)
    input[i] = (gint16)(i % 200);

先处理一次输入数据。此时重采样器会建立自己的Sample历史。


🌻4.2 重置后再次处理同一段输入

第一次转换完成后调用:

cpp 复制代码
gst_audio_converter_reset(converter);

然后把完全相同的输入数据再次交给同一个转换器。

如果reset()正确清除了重采样历史,那么第二次转换会与第一次转换一样,从同一个初始状态开始。
#mermaid-svg-x8yyNzRJ7jjyMsi2{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-x8yyNzRJ7jjyMsi2 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-x8yyNzRJ7jjyMsi2 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-x8yyNzRJ7jjyMsi2 .error-icon{fill:#552222;}#mermaid-svg-x8yyNzRJ7jjyMsi2 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-x8yyNzRJ7jjyMsi2 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-x8yyNzRJ7jjyMsi2 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-x8yyNzRJ7jjyMsi2 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-x8yyNzRJ7jjyMsi2 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-x8yyNzRJ7jjyMsi2 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-x8yyNzRJ7jjyMsi2 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-x8yyNzRJ7jjyMsi2 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-x8yyNzRJ7jjyMsi2 .marker.cross{stroke:#333333;}#mermaid-svg-x8yyNzRJ7jjyMsi2 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-x8yyNzRJ7jjyMsi2 p{margin:0;}#mermaid-svg-x8yyNzRJ7jjyMsi2 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-x8yyNzRJ7jjyMsi2 .cluster-label text{fill:#333;}#mermaid-svg-x8yyNzRJ7jjyMsi2 .cluster-label span{color:#333;}#mermaid-svg-x8yyNzRJ7jjyMsi2 .cluster-label span p{background-color:transparent;}#mermaid-svg-x8yyNzRJ7jjyMsi2 .label text,#mermaid-svg-x8yyNzRJ7jjyMsi2 span{fill:#333;color:#333;}#mermaid-svg-x8yyNzRJ7jjyMsi2 .node rect,#mermaid-svg-x8yyNzRJ7jjyMsi2 .node circle,#mermaid-svg-x8yyNzRJ7jjyMsi2 .node ellipse,#mermaid-svg-x8yyNzRJ7jjyMsi2 .node polygon,#mermaid-svg-x8yyNzRJ7jjyMsi2 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-x8yyNzRJ7jjyMsi2 .rough-node .label text,#mermaid-svg-x8yyNzRJ7jjyMsi2 .node .label text,#mermaid-svg-x8yyNzRJ7jjyMsi2 .image-shape .label,#mermaid-svg-x8yyNzRJ7jjyMsi2 .icon-shape .label{text-anchor:middle;}#mermaid-svg-x8yyNzRJ7jjyMsi2 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-x8yyNzRJ7jjyMsi2 .rough-node .label,#mermaid-svg-x8yyNzRJ7jjyMsi2 .node .label,#mermaid-svg-x8yyNzRJ7jjyMsi2 .image-shape .label,#mermaid-svg-x8yyNzRJ7jjyMsi2 .icon-shape .label{text-align:center;}#mermaid-svg-x8yyNzRJ7jjyMsi2 .node.clickable{cursor:pointer;}#mermaid-svg-x8yyNzRJ7jjyMsi2 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-x8yyNzRJ7jjyMsi2 .arrowheadPath{fill:#333333;}#mermaid-svg-x8yyNzRJ7jjyMsi2 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-x8yyNzRJ7jjyMsi2 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-x8yyNzRJ7jjyMsi2 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-x8yyNzRJ7jjyMsi2 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-x8yyNzRJ7jjyMsi2 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-x8yyNzRJ7jjyMsi2 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-x8yyNzRJ7jjyMsi2 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-x8yyNzRJ7jjyMsi2 .cluster text{fill:#333;}#mermaid-svg-x8yyNzRJ7jjyMsi2 .cluster span{color:#333;}#mermaid-svg-x8yyNzRJ7jjyMsi2 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-x8yyNzRJ7jjyMsi2 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-x8yyNzRJ7jjyMsi2 rect.text{fill:none;stroke-width:0;}#mermaid-svg-x8yyNzRJ7jjyMsi2 .icon-shape,#mermaid-svg-x8yyNzRJ7jjyMsi2 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-x8yyNzRJ7jjyMsi2 .icon-shape p,#mermaid-svg-x8yyNzRJ7jjyMsi2 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-x8yyNzRJ7jjyMsi2 .icon-shape .label rect,#mermaid-svg-x8yyNzRJ7jjyMsi2 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-x8yyNzRJ7jjyMsi2 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-x8yyNzRJ7jjyMsi2 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-x8yyNzRJ7jjyMsi2 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 创建48000→44100转换器
第一次转换

保存重采样历史
调用reset
第二次转换

重新建立历史
比较两次输出


🌻4.3 验证两次输出一致

完整测试代码:

c 复制代码
#include <gst/gst.h>
#include <gst/audio/audio.h>
#include <string.h>

int
main(int argc, char *argv[])
{
    GstAudioInfo in_info;
    GstAudioInfo out_info;
    GstAudioConverter *converter = NULL;
    gint16 input[480];
    gpointer first_output = NULL;
    gpointer second_output = NULL;
    gsize first_size = 0;
    gsize second_size = 0;
    gsize i;
    gboolean same;
    int status = 1;

    gst_init(&argc, &argv);

    for (i = 0; i < G_N_ELEMENTS(input); i++)
        input[i] = (gint16)(i % 200);

    gst_audio_info_set_format(
        &in_info,
        GST_AUDIO_FORMAT_S16,
        48000,
        1,
        NULL);

    gst_audio_info_set_format(
        &out_info,
        GST_AUDIO_FORMAT_S16,
        44100,
        1,
        NULL);

    converter = gst_audio_converter_new(
        GST_AUDIO_CONVERTER_FLAG_NONE,
        &in_info,
        &out_info,
        NULL);

    if (converter == NULL) {
        g_printerr("Failed to create audio converter\n");
        goto done;
    }

    if (!gst_audio_converter_convert(
            converter,
            GST_AUDIO_CONVERTER_FLAG_NONE,
            input,
            sizeof(input),
            &first_output,
            &first_size)) {
        g_printerr("First conversion failed\n");
        goto done;
    }

    gst_audio_converter_reset(converter);

    if (!gst_audio_converter_convert(
            converter,
            GST_AUDIO_CONVERTER_FLAG_NONE,
            input,
            sizeof(input),
            &second_output,
            &second_size)) {
        g_printerr("Second conversion failed\n");
        goto done;
    }

    same = first_size == second_size;

    if (same && first_size > 0)
        same = memcmp(
            first_output,
            second_output,
            first_size) == 0;

    g_print(
        "First output bytes: %" G_GSIZE_FORMAT "\n",
        first_size);

    g_print(
        "After reset output bytes: %" G_GSIZE_FORMAT "\n",
        second_size);

    if (!same) {
        g_printerr(
            "Reset verification failed\n");
        goto done;
    }

    g_print(
        "Reset verification passed: "
        "outputs are identical\n");

    status = 0;

done:
    g_free(first_output);
    g_free(second_output);
    gst_audio_converter_free(converter);

    return status;
}

编译:

bash 复制代码
gcc test_audio_converter_reset.c \
    -o test_audio_converter_reset \
    $(pkg-config --cflags --libs \
      gstreamer-audio-1.0)

运行:

bash 复制代码
./test_audio_converter_reset

重点观察:

text 复制代码
First output bytes: N
After reset output bytes: N
Reset verification passed: outputs are identical

这里不要求固定N的具体数值,因为不同版本或重采样配置可能产生不同的输出帧数。

测试真正验证的是:

text 复制代码
第一次转换:从初始重采样状态处理输入
调用reset:清除重采样历史
第二次转换:再次从初始状态处理同一输入
比较结果:两次输出完全一致

如果省略gst_audio_converter_reset(),第二次转换会继续使用第一次转换留下的重采样历史,输出就不再代表一次全新的音频段处理。

🌻5.总结

gst_audio_converter_reset()就是保留GstAudioConverter配置和处理链,只清除重采样器与量化器的跨调用历史,使下一段音频从初始转换状态开始处理。

相关推荐
新时代牛马1 小时前
单机 docker run 管不住了?从编排需求、期望状态到调度/自愈闭环讲透
运维·docker·容器
youyou-06061 小时前
AUTOSAR‑COM 常见问题 QA (1)
java·linux·服务器·单片机·汽车
代码方舟2 小时前
零信任架构实战:基于天远全能消金报告构建自动化信用合规网关
运维·人工智能·架构·自动化
m0_743697592 小时前
grep 的引号、反斜杠与正则方言:一次讲清我踩过的坑
linux
KANGBboy2 小时前
doris+kafka安装部署(单机+集群)二时钟服务器 java_home
服务器·分布式
虚无的纽扣2 小时前
【Linux】每个进程都拥有“假内存” | 进程地址空间详解
linux·运维·服务器
qq_349447952 小时前
Jenkins如何创建一个只能查看固定项目的账号
运维·jenkins
小马同学-2 小时前
OpenStack 镜像管理:Glance 镜像服务实验
运维·云计算·openstack
boke112百科3 小时前
怎么将Cloudflare Pages子域名301跳转到自定义域名?
运维·cloudflare·静态网站·301重定向