)
简介CSDN博客专家、《Android系统多媒体进阶实战》作者博主新书推荐《Android系统多媒体进阶实战》Android Audio工程师专栏地址Audio工程师进阶系列【原创干货持续更新中……】Android多媒体专栏地址多媒体系统工程师系列【原创干货持续更新中……】专题一 二AAOS车载系统AOSP14系统攻城狮入门视频实战课专题三Android14 Binder之HIDL与AIDL通信实战课专题四Android15快速自定义与集成音效实战课专题五Android15音频策略实战课专题六Android15音频性能实战课(无声/杂音/断音/爆音实战案例)人生格言人生从来没有捷径只有行动才是治疗恐惧和懒惰的唯一良药.更多原创,欢迎关注Android系统攻城狮文章目录1.前言2.应用场景和用法2.1读取编码器的输入块配置2.2函数原型与返回语义3.调用流程剖析3.1读取基类保存的配置字段3.2最小样本数影响输入聚合4.实战案例4.1准备测试编码器4.2触发格式设置并查询4.3完整代码5.总结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也不改变输入缓冲状态。编码器子类设置最小样本数GstAudioEncoder保存配置gst_audio_encoder_get_frame_samples_min返回每声道最小样本数基类据此组织输入数据2.应用场景和用法2.1读取编码器的输入块配置这个接口适合子类或调试代码查询编码器当前要求的最小输入块大小。返回值以“每声道样本数”为单位不是字节数也不是编码后的帧数。例如立体声S16LE音频每个采样时刻占4字节。如果最小样本数为1024那么对应的PCM数据量是1024个采样点/声道 × 4字节/采样时刻 4096字节这个最小值会影响基类何时调用handle_frame()输入数据还没有达到最小值时基类通常会继续缓存数据。数据达到最小值后基类可以向子类提交输入块。排空或强制处理时基类可能提交少于该值的剩余数据。gst_audio_encoder_get_frame_samples_min()只返回配置不保证下一次handle_frame()一定收到恰好这么多样本。最大样本数、强制排空和当前可用数据也会影响实际提交量。2.2函数原型与返回语义函数原型如下gintgst_audio_encoder_get_frame_samples_min(GstAudioEncoder*enc);参数或返回值含义enc有效的GstAudioEncoder对象返回值当前设置的每声道最小样本数编码器子类通常在set_format()中根据输入格式配置样本数gst_audio_encoder_set_frame_samples_min(enc,1024);随后可以通过getter读取该值gint min_samples;min_samplesgst_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字段。gintgst_audio_encoder_get_frame_samples_min(GstAudioEncoder*enc){g_return_val_if_fail(GST_IS_AUDIO_ENCODER(enc),0);returnenc-priv-ctx.frame_samples_min;}它没有计算采样数没有读取Adapter中的PCM也没有触发handle_frame()。返回值只是当前配置状态的快照。否是传入GstAudioEncoder对象检查对象类型对象是否有效返回0读取frame_samples_min3.2最小样本数影响输入聚合基类在组织输入数据时会把每声道最小样本数乘以当前音频格式的BPF得到需要的最小字节数。needframe_samples_min0?frame_samples_min*info.bpf:available;当可用字节数少于need时正常处理路径会等待更多输入数据。达到阈值后基类再从Adapter中取出输入数据并交给子类处理。因此gst_audio_encoder_get_frame_samples_min()本身只是读取配置。真正使用该配置进行输入聚合的是GstAudioEncoder基类的缓冲处理路径。否是输入PCM进入Adapter读取最小样本数配置换算成最小字节数当前数据达到阈值继续等待输入提交数据给handle_frame4.实战案例4.1准备测试编码器案例创建一个最小GstAudioEncoder子类。在set_format()中子类把最小输入样本数设置为1024。测试通过GstHarness发送输入Caps触发格式设置再读取getter的返回值。这个案例只验证配置写入和读取不提交PCM Buffer也不执行实际编码。4.2触发格式设置并查询运行逻辑分为三步创建测试编码器和GstHarness。向Harness设置44100Hz、双声道、S16LE输入Caps触发子类的set_format()。调用gst_audio_encoder_get_frame_samples_min()确认返回值是1024。如果输入Caps不触发set_format()getter仍可能返回默认值0。因此查询动作应放在输入格式设置完成之后。4.3完整代码#includegst/gst.h#includegst/audio/audio.h#includegst/check/gstharness.htypedefstruct_SampleEncoder{GstAudioEncoder parent;}SampleEncoder;typedefstruct_SampleEncoderClass{GstAudioEncoderClass parent_class;}SampleEncoderClass;G_DEFINE_TYPE(SampleEncoder,sample_encoder,GST_TYPE_AUDIO_ENCODER);/* 编码器接收原始音频并输出测试用Caps */staticGstStaticPadTemplate encoder_sink_templateGST_STATIC_PAD_TEMPLATE(sink,GST_PAD_SINK,GST_PAD_ALWAYS,GST_STATIC_CAPS(audio/x-raw));staticGstStaticPadTemplate encoder_src_templateGST_STATIC_PAD_TEMPLATE(src,GST_PAD_SRC,GST_PAD_ALWAYS,GST_STATIC_CAPS(audio/x-test-custom));/* 新格式协商时设置输出Caps和每声道最小样本数 */staticgbooleansample_encoder_set_format(GstAudioEncoder*enc,GstAudioInfo*info){GstCaps*caps;gboolean result;capsgst_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);resultgst_audio_encoder_set_output_format(enc,caps);gst_caps_unref(caps);if(!result)returnFALSE;/* 本测试编码器要求基类至少聚合1024个采样点/声道 */gst_audio_encoder_set_frame_samples_min(enc,1024);returnTRUE;}/* 本案例不提交PCM因此回调只保留最小实现 */staticGstFlowReturnsample_encoder_handle_frame(GstAudioEncoder*enc,GstBuffer*buffer){returnGST_FLOW_OK;}staticvoidsample_encoder_class_init(SampleEncoderClass*klass){GstElementClass*element_classGST_ELEMENT_CLASS(klass);GstAudioEncoderClass*encoder_classGST_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_formatsample_encoder_set_format;encoder_class-handle_framesample_encoder_handle_frame;}staticvoidsample_encoder_init(SampleEncoder*encoder){}/* Harness输入连接编码器sinkHarness输出连接编码器src */staticGstStaticPadTemplate harness_src_templateGST_STATIC_PAD_TEMPLATE(harness-src,GST_PAD_SRC,GST_PAD_ALWAYS,GST_STATIC_CAPS(audio/x-raw));staticGstStaticPadTemplate harness_sink_templateGST_STATIC_PAD_TEMPLATE(harness-sink,GST_PAD_SINK,GST_PAD_ALWAYS,GST_STATIC_CAPS(audio/x-test-custom));intmain(intargc,char*argv[]){GstElement*element;GstAudioEncoder*encoder;GstHarness*harness;gint min_samples;intresult1;gst_init(argc,argv);/* 创建编码器并由Harness连接两侧Pad */elementg_object_new(sample_encoder_get_type(),NULL);if(elementNULL)return1;encoderGST_AUDIO_ENCODER(element);harnessgst_harness_new_full(element,harness_src_template,sink,harness_sink_template,src);if(harnessNULL){gst_object_unref(element);return1;}/* 设置输入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_samplesgst_audio_encoder_get_frame_samples_min(encoder);g_print(Minimum samples per channel: %d\n,min_samples);if(min_samples1024)result0;gst_harness_teardown(harness);gst_object_unref(element);returnresult;}5.总结gst_audio_encoder_get_frame_samples_min()就是读取GstAudioEncoder当前保存的每声道最小输入样本数配置供调用者了解基类的输入聚合要求。