Linux Gstreamer深度解析之gst_audio_encoder_get_frame_samples_min调用流程与实战(六十)

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

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

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

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

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

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

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

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

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

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

🍉🍉🍉文章目录🍉🍉🍉

🌻1.前言

本篇目的:理解gst_audio_encoder_get_frame_samples_min()读取的是什么配置值,分析这个值如何影响GstAudioEncoder向子类提交PCM数据,并通过一个最小Harness案例验证查询结果。

音频编码器子类通常按固定块处理PCM。例如,AAC编码器可能要求每个编码帧使用1024个采样点。GstAudioEncoder基类需要知道这个最小块大小,才能决定何时把输入数据交给子类的handle_frame()。

gst_audio_encoder_get_frame_samples_min()读取基类当前保存的最小样本数配置。它只返回配置值,不统计已缓存的PCM,也不改变输入缓冲状态。
#mermaid-svg-8jnXnmiu2RkovcYC{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-8jnXnmiu2RkovcYC .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-8jnXnmiu2RkovcYC .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-8jnXnmiu2RkovcYC .error-icon{fill:#552222;}#mermaid-svg-8jnXnmiu2RkovcYC .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-8jnXnmiu2RkovcYC .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-8jnXnmiu2RkovcYC .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-8jnXnmiu2RkovcYC .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-8jnXnmiu2RkovcYC .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-8jnXnmiu2RkovcYC .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-8jnXnmiu2RkovcYC .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-8jnXnmiu2RkovcYC .marker{fill:#333333;stroke:#333333;}#mermaid-svg-8jnXnmiu2RkovcYC .marker.cross{stroke:#333333;}#mermaid-svg-8jnXnmiu2RkovcYC svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-8jnXnmiu2RkovcYC p{margin:0;}#mermaid-svg-8jnXnmiu2RkovcYC .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-8jnXnmiu2RkovcYC .cluster-label text{fill:#333;}#mermaid-svg-8jnXnmiu2RkovcYC .cluster-label span{color:#333;}#mermaid-svg-8jnXnmiu2RkovcYC .cluster-label span p{background-color:transparent;}#mermaid-svg-8jnXnmiu2RkovcYC .label text,#mermaid-svg-8jnXnmiu2RkovcYC span{fill:#333;color:#333;}#mermaid-svg-8jnXnmiu2RkovcYC .node rect,#mermaid-svg-8jnXnmiu2RkovcYC .node circle,#mermaid-svg-8jnXnmiu2RkovcYC .node ellipse,#mermaid-svg-8jnXnmiu2RkovcYC .node polygon,#mermaid-svg-8jnXnmiu2RkovcYC .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-8jnXnmiu2RkovcYC .rough-node .label text,#mermaid-svg-8jnXnmiu2RkovcYC .node .label text,#mermaid-svg-8jnXnmiu2RkovcYC .image-shape .label,#mermaid-svg-8jnXnmiu2RkovcYC .icon-shape .label{text-anchor:middle;}#mermaid-svg-8jnXnmiu2RkovcYC .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-8jnXnmiu2RkovcYC .rough-node .label,#mermaid-svg-8jnXnmiu2RkovcYC .node .label,#mermaid-svg-8jnXnmiu2RkovcYC .image-shape .label,#mermaid-svg-8jnXnmiu2RkovcYC .icon-shape .label{text-align:center;}#mermaid-svg-8jnXnmiu2RkovcYC .node.clickable{cursor:pointer;}#mermaid-svg-8jnXnmiu2RkovcYC .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-8jnXnmiu2RkovcYC .arrowheadPath{fill:#333333;}#mermaid-svg-8jnXnmiu2RkovcYC .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-8jnXnmiu2RkovcYC .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-8jnXnmiu2RkovcYC .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8jnXnmiu2RkovcYC .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-8jnXnmiu2RkovcYC .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8jnXnmiu2RkovcYC .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-8jnXnmiu2RkovcYC .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-8jnXnmiu2RkovcYC .cluster text{fill:#333;}#mermaid-svg-8jnXnmiu2RkovcYC .cluster span{color:#333;}#mermaid-svg-8jnXnmiu2RkovcYC 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-8jnXnmiu2RkovcYC .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-8jnXnmiu2RkovcYC rect.text{fill:none;stroke-width:0;}#mermaid-svg-8jnXnmiu2RkovcYC .icon-shape,#mermaid-svg-8jnXnmiu2RkovcYC .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8jnXnmiu2RkovcYC .icon-shape p,#mermaid-svg-8jnXnmiu2RkovcYC .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-8jnXnmiu2RkovcYC .icon-shape .label rect,#mermaid-svg-8jnXnmiu2RkovcYC .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8jnXnmiu2RkovcYC .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-8jnXnmiu2RkovcYC .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-8jnXnmiu2RkovcYC :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 编码器子类设置最小样本数
GstAudioEncoder保存配置
gst_audio_encoder_get_frame_samples_min
返回每声道最小样本数
基类据此组织输入数据

🌻2.应用场景和用法

2.1读取编码器的输入块配置

这个接口适合子类或调试代码查询编码器当前要求的最小输入块大小。返回值以"每声道样本数"为单位,不是字节数,也不是编码后的帧数。

例如,立体声S16LE音频每个采样时刻占4字节。如果最小样本数为1024,那么对应的PCM数据量是:

text 复制代码
1024个采样点/声道 × 4字节/采样时刻 = 4096字节

这个最小值会影响基类何时调用handle_frame():

  • 输入数据还没有达到最小值时,基类通常会继续缓存数据。
  • 数据达到最小值后,基类可以向子类提交输入块。
  • 排空或强制处理时,基类可能提交少于该值的剩余数据。

gst_audio_encoder_get_frame_samples_min()只返回配置,不保证下一次handle_frame()一定收到恰好这么多样本。最大样本数、强制排空和当前可用数据也会影响实际提交量。

2.2函数原型与返回语义

函数原型如下:

c 复制代码
gint
gst_audio_encoder_get_frame_samples_min (GstAudioEncoder *enc);
参数或返回值 含义
enc 有效的GstAudioEncoder对象
返回值 当前设置的每声道最小样本数

编码器子类通常在set_format()中根据输入格式配置样本数:

c 复制代码
gst_audio_encoder_set_frame_samples_min (enc, 1024);

随后可以通过getter读取该值:

c 复制代码
gint min_samples;

min_samples = gst_audio_encoder_get_frame_samples_min (enc);

基类会在调用子类的set_format()之前,把最小样本数重置为0。因此,格式重新协商后,子类需要在新的set_format()调用中重新设置它。

返回0可能表示配置值为0。此时基类会把当前可用数据交给子类处理。传入无效对象时,函数也会返回0,所以调用者应先保证enc是有效对象。

相邻接口的职责如下:

接口 职责
gst_audio_encoder_set_frame_samples_min() 设置每声道最小样本数
gst_audio_encoder_get_frame_samples_min() 读取当前最小样本数配置
gst_audio_encoder_set_frame_samples_max() 设置每声道最大样本数
gst_audio_encoder_set_frame_max() 设置一次最多提交的帧数
gst_audio_encoder_finish_frame() 提交编码结果并报告消耗的输入样本数

🌻3.调用流程剖析

3.1读取基类保存的配置字段

这个getter的实现很短:先检查对象类型,再返回私有上下文中的frame_samples_min字段。

c 复制代码
gint
gst_audio_encoder_get_frame_samples_min (GstAudioEncoder * enc)
{
  g_return_val_if_fail (GST_IS_AUDIO_ENCODER (enc), 0);

  return enc->priv->ctx.frame_samples_min;
}

它没有计算采样数,没有读取Adapter中的PCM,也没有触发handle_frame()。返回值只是当前配置状态的快照。
#mermaid-svg-dCOUNLVMJHsNAf3b{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-dCOUNLVMJHsNAf3b .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-dCOUNLVMJHsNAf3b .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-dCOUNLVMJHsNAf3b .error-icon{fill:#552222;}#mermaid-svg-dCOUNLVMJHsNAf3b .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-dCOUNLVMJHsNAf3b .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-dCOUNLVMJHsNAf3b .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-dCOUNLVMJHsNAf3b .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-dCOUNLVMJHsNAf3b .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-dCOUNLVMJHsNAf3b .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-dCOUNLVMJHsNAf3b .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-dCOUNLVMJHsNAf3b .marker{fill:#333333;stroke:#333333;}#mermaid-svg-dCOUNLVMJHsNAf3b .marker.cross{stroke:#333333;}#mermaid-svg-dCOUNLVMJHsNAf3b svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-dCOUNLVMJHsNAf3b p{margin:0;}#mermaid-svg-dCOUNLVMJHsNAf3b .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-dCOUNLVMJHsNAf3b .cluster-label text{fill:#333;}#mermaid-svg-dCOUNLVMJHsNAf3b .cluster-label span{color:#333;}#mermaid-svg-dCOUNLVMJHsNAf3b .cluster-label span p{background-color:transparent;}#mermaid-svg-dCOUNLVMJHsNAf3b .label text,#mermaid-svg-dCOUNLVMJHsNAf3b span{fill:#333;color:#333;}#mermaid-svg-dCOUNLVMJHsNAf3b .node rect,#mermaid-svg-dCOUNLVMJHsNAf3b .node circle,#mermaid-svg-dCOUNLVMJHsNAf3b .node ellipse,#mermaid-svg-dCOUNLVMJHsNAf3b .node polygon,#mermaid-svg-dCOUNLVMJHsNAf3b .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-dCOUNLVMJHsNAf3b .rough-node .label text,#mermaid-svg-dCOUNLVMJHsNAf3b .node .label text,#mermaid-svg-dCOUNLVMJHsNAf3b .image-shape .label,#mermaid-svg-dCOUNLVMJHsNAf3b .icon-shape .label{text-anchor:middle;}#mermaid-svg-dCOUNLVMJHsNAf3b .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-dCOUNLVMJHsNAf3b .rough-node .label,#mermaid-svg-dCOUNLVMJHsNAf3b .node .label,#mermaid-svg-dCOUNLVMJHsNAf3b .image-shape .label,#mermaid-svg-dCOUNLVMJHsNAf3b .icon-shape .label{text-align:center;}#mermaid-svg-dCOUNLVMJHsNAf3b .node.clickable{cursor:pointer;}#mermaid-svg-dCOUNLVMJHsNAf3b .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-dCOUNLVMJHsNAf3b .arrowheadPath{fill:#333333;}#mermaid-svg-dCOUNLVMJHsNAf3b .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-dCOUNLVMJHsNAf3b .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-dCOUNLVMJHsNAf3b .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-dCOUNLVMJHsNAf3b .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-dCOUNLVMJHsNAf3b .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-dCOUNLVMJHsNAf3b .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-dCOUNLVMJHsNAf3b .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-dCOUNLVMJHsNAf3b .cluster text{fill:#333;}#mermaid-svg-dCOUNLVMJHsNAf3b .cluster span{color:#333;}#mermaid-svg-dCOUNLVMJHsNAf3b 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-dCOUNLVMJHsNAf3b .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-dCOUNLVMJHsNAf3b rect.text{fill:none;stroke-width:0;}#mermaid-svg-dCOUNLVMJHsNAf3b .icon-shape,#mermaid-svg-dCOUNLVMJHsNAf3b .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-dCOUNLVMJHsNAf3b .icon-shape p,#mermaid-svg-dCOUNLVMJHsNAf3b .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-dCOUNLVMJHsNAf3b .icon-shape .label rect,#mermaid-svg-dCOUNLVMJHsNAf3b .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-dCOUNLVMJHsNAf3b .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-dCOUNLVMJHsNAf3b .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-dCOUNLVMJHsNAf3b :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否
是
传入GstAudioEncoder对象
检查对象类型
对象是否有效
返回0
读取frame_samples_min

3.2最小样本数影响输入聚合

基类在组织输入数据时,会把每声道最小样本数乘以当前音频格式的BPF,得到需要的最小字节数。

c 复制代码
need = frame_samples_min > 0
    ? frame_samples_min * info.bpf
    : available;

当可用字节数少于need时,正常处理路径会等待更多输入数据。达到阈值后,基类再从Adapter中取出输入数据并交给子类处理。

因此,gst_audio_encoder_get_frame_samples_min()本身只是读取配置。真正使用该配置进行输入聚合的是GstAudioEncoder基类的缓冲处理路径。
#mermaid-svg-hyHvohTENYU7KSx6{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-hyHvohTENYU7KSx6 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-hyHvohTENYU7KSx6 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-hyHvohTENYU7KSx6 .error-icon{fill:#552222;}#mermaid-svg-hyHvohTENYU7KSx6 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-hyHvohTENYU7KSx6 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-hyHvohTENYU7KSx6 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-hyHvohTENYU7KSx6 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-hyHvohTENYU7KSx6 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-hyHvohTENYU7KSx6 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-hyHvohTENYU7KSx6 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-hyHvohTENYU7KSx6 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-hyHvohTENYU7KSx6 .marker.cross{stroke:#333333;}#mermaid-svg-hyHvohTENYU7KSx6 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-hyHvohTENYU7KSx6 p{margin:0;}#mermaid-svg-hyHvohTENYU7KSx6 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-hyHvohTENYU7KSx6 .cluster-label text{fill:#333;}#mermaid-svg-hyHvohTENYU7KSx6 .cluster-label span{color:#333;}#mermaid-svg-hyHvohTENYU7KSx6 .cluster-label span p{background-color:transparent;}#mermaid-svg-hyHvohTENYU7KSx6 .label text,#mermaid-svg-hyHvohTENYU7KSx6 span{fill:#333;color:#333;}#mermaid-svg-hyHvohTENYU7KSx6 .node rect,#mermaid-svg-hyHvohTENYU7KSx6 .node circle,#mermaid-svg-hyHvohTENYU7KSx6 .node ellipse,#mermaid-svg-hyHvohTENYU7KSx6 .node polygon,#mermaid-svg-hyHvohTENYU7KSx6 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-hyHvohTENYU7KSx6 .rough-node .label text,#mermaid-svg-hyHvohTENYU7KSx6 .node .label text,#mermaid-svg-hyHvohTENYU7KSx6 .image-shape .label,#mermaid-svg-hyHvohTENYU7KSx6 .icon-shape .label{text-anchor:middle;}#mermaid-svg-hyHvohTENYU7KSx6 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-hyHvohTENYU7KSx6 .rough-node .label,#mermaid-svg-hyHvohTENYU7KSx6 .node .label,#mermaid-svg-hyHvohTENYU7KSx6 .image-shape .label,#mermaid-svg-hyHvohTENYU7KSx6 .icon-shape .label{text-align:center;}#mermaid-svg-hyHvohTENYU7KSx6 .node.clickable{cursor:pointer;}#mermaid-svg-hyHvohTENYU7KSx6 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-hyHvohTENYU7KSx6 .arrowheadPath{fill:#333333;}#mermaid-svg-hyHvohTENYU7KSx6 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-hyHvohTENYU7KSx6 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-hyHvohTENYU7KSx6 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-hyHvohTENYU7KSx6 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-hyHvohTENYU7KSx6 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-hyHvohTENYU7KSx6 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-hyHvohTENYU7KSx6 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-hyHvohTENYU7KSx6 .cluster text{fill:#333;}#mermaid-svg-hyHvohTENYU7KSx6 .cluster span{color:#333;}#mermaid-svg-hyHvohTENYU7KSx6 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-hyHvohTENYU7KSx6 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-hyHvohTENYU7KSx6 rect.text{fill:none;stroke-width:0;}#mermaid-svg-hyHvohTENYU7KSx6 .icon-shape,#mermaid-svg-hyHvohTENYU7KSx6 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-hyHvohTENYU7KSx6 .icon-shape p,#mermaid-svg-hyHvohTENYU7KSx6 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-hyHvohTENYU7KSx6 .icon-shape .label rect,#mermaid-svg-hyHvohTENYU7KSx6 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-hyHvohTENYU7KSx6 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-hyHvohTENYU7KSx6 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-hyHvohTENYU7KSx6 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否
是
输入PCM进入Adapter
读取最小样本数配置
换算成最小字节数
当前数据达到阈值
继续等待输入
提交数据给handle_frame

🌻4.实战案例

4.1准备测试编码器

案例创建一个最小GstAudioEncoder子类。在set_format()中,子类把最小输入样本数设置为1024。测试通过GstHarness发送输入Caps,触发格式设置,再读取getter的返回值。

这个案例只验证配置写入和读取,不提交PCM Buffer,也不执行实际编码。

4.2触发格式设置并查询

运行逻辑分为三步:

  1. 创建测试编码器和GstHarness。
  2. 向Harness设置44100Hz、双声道、S16LE输入Caps,触发子类的set_format()。
  3. 调用gst_audio_encoder_get_frame_samples_min(),确认返回值是1024。

如果输入Caps不触发set_format(),getter仍可能返回默认值0。因此查询动作应放在输入格式设置完成之后。

4.3完整代码
c 复制代码
#include <gst/gst.h>
#include <gst/audio/audio.h>
#include <gst/check/gstharness.h>

typedef struct _SampleEncoder
{
  GstAudioEncoder parent;
} SampleEncoder;

typedef struct _SampleEncoderClass
{
  GstAudioEncoderClass parent_class;
} SampleEncoderClass;

G_DEFINE_TYPE (SampleEncoder, sample_encoder, GST_TYPE_AUDIO_ENCODER);

/* 编码器接收原始音频,并输出测试用Caps */
static GstStaticPadTemplate encoder_sink_template =
GST_STATIC_PAD_TEMPLATE (
    "sink",
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS ("audio/x-raw"));

static GstStaticPadTemplate encoder_src_template =
GST_STATIC_PAD_TEMPLATE (
    "src",
    GST_PAD_SRC,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS ("audio/x-test-custom"));

/* 新格式协商时设置输出Caps和每声道最小样本数 */
static gboolean
sample_encoder_set_format (GstAudioEncoder * enc, GstAudioInfo * info)
{
  GstCaps *caps;
  gboolean result;

  caps = gst_caps_new_simple (
      "audio/x-test-custom",
      "rate", G_TYPE_INT, GST_AUDIO_INFO_RATE (info),
      "channels", G_TYPE_INT, GST_AUDIO_INFO_CHANNELS (info),
      NULL);

  result = gst_audio_encoder_set_output_format (enc, caps);
  gst_caps_unref (caps);

  if (!result)
    return FALSE;

  /* 本测试编码器要求基类至少聚合1024个采样点/声道 */
  gst_audio_encoder_set_frame_samples_min (enc, 1024);
  return TRUE;
}

/* 本案例不提交PCM,因此回调只保留最小实现 */
static GstFlowReturn
sample_encoder_handle_frame (GstAudioEncoder * enc, GstBuffer * buffer)
{
  return GST_FLOW_OK;
}

static void
sample_encoder_class_init (SampleEncoderClass * klass)
{
  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
  GstAudioEncoderClass *encoder_class =
      GST_AUDIO_ENCODER_CLASS (klass);

  gst_element_class_add_static_pad_template (
      element_class,
      &encoder_sink_template);
  gst_element_class_add_static_pad_template (
      element_class,
      &encoder_src_template);

  gst_element_class_set_metadata (
      element_class,
      "Sample Encoder",
      "Encoder/Audio",
      "Minimum samples getter test",
      "GStreamer");

  encoder_class->set_format = sample_encoder_set_format;
  encoder_class->handle_frame = sample_encoder_handle_frame;
}

static void
sample_encoder_init (SampleEncoder * encoder)
{
}

/* Harness输入连接编码器sink,Harness输出连接编码器src */
static GstStaticPadTemplate harness_src_template =
GST_STATIC_PAD_TEMPLATE (
    "harness-src",
    GST_PAD_SRC,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS ("audio/x-raw"));

static GstStaticPadTemplate harness_sink_template =
GST_STATIC_PAD_TEMPLATE (
    "harness-sink",
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS ("audio/x-test-custom"));

int
main (int argc, char *argv[])
{
  GstElement *element;
  GstAudioEncoder *encoder;
  GstHarness *harness;
  gint min_samples;
  int result = 1;

  gst_init (&argc, &argv);

  /* 创建编码器,并由Harness连接两侧Pad */
  element = g_object_new (sample_encoder_get_type (), NULL);
  if (element == NULL)
    return 1;

  encoder = GST_AUDIO_ENCODER (element);
  harness = gst_harness_new_full (
      element,
      &harness_src_template,
      "sink",
      &harness_sink_template,
      "src");

  if (harness == NULL) {
    gst_object_unref (element);
    return 1;
  }

  /* 设置输入Caps,触发编码器的set_format回调 */
  gst_harness_set_src_caps (
      harness,
      gst_caps_new_simple (
          "audio/x-raw",
          "format", G_TYPE_STRING, "S16LE",
          "rate", G_TYPE_INT, 44100,
          "channels", G_TYPE_INT, 2,
          "layout", G_TYPE_STRING, "interleaved",
          NULL));

  /* 读取set_format中保存的最小样本数 */
  min_samples =
      gst_audio_encoder_get_frame_samples_min (encoder);

  g_print ("Minimum samples per channel: %d\n", min_samples);

  if (min_samples == 1024)
    result = 0;

  gst_harness_teardown (harness);
  gst_object_unref (element);

  return result;
}

🌻5.总结

gst_audio_encoder_get_frame_samples_min()就是读取GstAudioEncoder当前保存的每声道最小输入样本数配置,供调用者了解基类的输入聚合要求。

相关推荐
黑妹天下第一乖3 小时前
第09讲 · 多媒体与音频 SDK:硬件编解码与端侧语音
人工智能·嵌入式硬件·深度学习·机器人·音视频·iot
_upupup3 小时前
Linux中的自动化构建——make/Makefile
linux·运维·自动化
HEJOO94 小时前
Linux 设备驱动系列——使用全局工作队列
java·linux·算法
还卿一钵无情泪4 小时前
Docker 部署Unsloth 绕开环境配置难题
运维·人工智能·docker·ai·容器·nlp·干货
slimer4 小时前
07.进制转换上篇:位权展开与十进制互转
linux
顺风尿一寸4 小时前
手写迷你 Docker:用 Namespace + Cgroup v2 实现一个简单容器
linux
一木 之林5 小时前
DeepSeek Agent 开发(三)
java·大数据·linux
ken22325 小时前
(aaa) piper TTS 文字转语音 安装、使用;与 calibre 电子书朗读 (****)
音视频
打工仔折腾 AI5 小时前
Docker镜像分层与卷挂载到底怎么工作:一次文件系统层面的实测分析
运维·人工智能·后端·python·docker·容器·性能优化