# GMF-Core 框架介绍 [English](./README.md) 下图展示了 GMF-Core 框架的类图关系。 ```mermaid classDiagram GMF-Obj <|-- GMF-IO GMF-Obj <|-- GMF-Task GMF-Obj <|-- GMF-Element GMF-Obj: +new() GMF-Obj: +delete() GMF-Task --* GMF-Pipeline GMF-Port --* GMF-Element GMF-Element --* GMF-Pipeline GMF-Element --o GMF-Pool GMF-IO --* GMF-Pipeline GMF-IO --o GMF-Pool ``` GMF-Obj 是基础类,它为其他组件创建和销毁提供统一接口,便于对继承类对象进行管理操作:创建、复制、销毁。GMF-IO、GMF-Task 和 GMF-Element 都继承于它。 GMF-Core 主要由 GMF-Element、GMF-Pipeline、GMF-Task 组成,它的工作原理是以一个称为 Job 的工作片作为最小的处理单元,并实时加载这些工作片执行。 - GMF-Element 是工作片的提供者,为每个 element 提供三个基础的工作片,即 open,process 和 close。其中 open 一般是最先加载的工作片,process 会在 open 处理完后被加载,当所有 element 的工作片都处理完或者发生错误的时,close 工作片会被加载。 - GMF-Pipeline 负责管理和加载被连接的 element 的工作片,同时负责对 GMF-Task 的开始和停止进行控制。当 GMF-Pipeline 有 IN 和 OUT 的 IO 接口时,还负责对其进行打开和关闭操作。 - GMF-Task 处理工作片的流程参见下图。整个过程分为三个阶段,第一个阶段是 opening,把每个 element 的 open 和 process 工作片都调用一次,然后进入 running 阶段,这个过程是按 element 连接顺序循环执行每个 element 的 process 工作片,当工作片处理完或者出错则进入最后一个阶段 cleanup,它会调用每个 element 的 close 工作片来回收 element 资源。 ```mermaid graph LR Opening -.->open1[open1] --> proc1[proc1] --> open2[open2] open2--> proc2[proc2]-->open3[open3] --> proc3[proc3] Running -.-> proc4[proc1] --> proc5[proc2] --> proc6[proc3] proc6-->proc4 Cleanup -.->|Finished, Stopped, Error| close1 --> close2 --> close3 style Opening fill:none,stroke:#333,stroke-width:1 style Running fill:none,stroke:#333,stroke-width:1 style Cleanup fill:none,stroke:#333,stroke-width:1 ``` ## GMF-Pool GMF-Pool 是 GMF-Element 和 GMF-IO 的管理单元。GMF-Pool 根据字符串列表创建 pipeline,先创建存储对象的复制品,然后连接成 pipeline。如果新 pipeline 需要调整 element 或 IO 初始配置参数,可以在 pipeline 创建成功后,通过 `esp_gmf_pipeline_get_el_by_name` 和 `OBJ_GET_CFG` 来修改。 ## GMF-Element GMF-Element 是一个基础类,用于实现具体功能的 element。它派生的类别有 audio element、picture element 等。 - audio element 实例化的类包含 encoder、decoder,以及 EQ、mixer、resample 等音频算法。 - picture element 实例化的类包含 JPEG encoder/decoder、PNG decoder 等。 GMF-Element 的输入输出端口是 GMF-Port, 它根据 element 的连接情况和请求的数据大小管理 payload 缓冲区,并让 payload 数据在 element 之间传递。GMF-Element 负责管理 GMF-Port 的连接能力和连接个数。 ## GMF-DataBus GMF-DataBus 是 GMF-Core 存取数据的模块,它使用 Acquire 再 Release 的方式访问数据。 GMF-DataBus 数据传输支持零拷贝和拷贝两种传输方式,也支持阻塞和非阻塞的访问方式。 目前 GMF-Core 支持了三种类型,分别是 Ringbuffer、PBuffer、FIFO 和 BlockBuffer,其中 PBuffer、FIFO 和 BlockBuffer 数据传输是零拷贝,Ringbuffer、FIFO 和 BlockBuffer 提供阻塞接口。 ## GMF-Task GMF-Task 是执行 job 的线程,它从工作列表中取出 job 顺序运行。当工作列表的 job 执行完即进入空闲状态,直到有新的 job 加入。job 是执行工作的最小单位,job 分为一次性和无限次的两种。 ## GMF-Pipeline GMF-Pipeline 由 element、task、IO 组成,一个 pipeline 必须有一个 task 来处理和调度任务。GMF-Pipeline 支持 pipeline 之间的顺序级联,即一个 pipeline 可以连接另外一个 pipeline,pipeline 的 event 也可以级联到后级的 pipeline 上。 Pipeline 还支持一些有事件依赖的 element 的顺序启动,比如一个 pipeline 由 decoder 和 resample 两个 element 组成,其中 resample 需要 decoder 解析出来的音乐信息才能开始工作,那么 GMF-Pipeline 会先只启动 decoder,直到解析到音乐信息后,pipeline 才启动 resample。这样的流程即简化了操作也不会带来噪音。 GMF-Pipeline 是 GMF 中可以独立运行的模块,为用户提供 pipeline 的控制接口,如 Pipeline 的 run\stop\pause\resume\reset 等。GMF-Pipeline 负责处理连接的 element 和 task 事件,还负责打开和关闭 IN 和 OUT port。在运行过程中 pipeline 还负责注册连接的 element job。GMF-Pipeline 一般由一个 GMF-Task、至少一个 element、最多一个 IN port 和最多一个 OUT port 组成。IN port 连接的是第一级 element 的输入,OUT port 连接的是最后一级 element 的输出。一个 pipeline 可以包含一个 element 或多个 element,支持的 element 数量没有上限,只受物理资源限制。 GMF-Pipeline 的灵活性很高,本文介绍 4 个基本的 pipeline 应用。 应用 1:一路 pipeline 从文件解码音频到 I2S 播放 ```mermaid graph LR File((File)) --> Decoder1[decoder] --> Resample1[Resample] --> I2S((I2S)) ``` 应用 2:pipeline 输入输出不连接 port,而是使用回调函数,让用户去填充数据 ```mermaid graph LR CB1((CB)) --> Decoder2[decoder] --> Resample2[Resample] --> CB2((CB)) ``` 应用 3:一路播放文件的 pipeline 拆成两个,通过 Ringbuffer 连接 ```mermaid graph LR subgraph P1 File3((File)) --> MP3_1[MP3] end MP3_1 --> Resample3[Resample] subgraph P2 Resample3 --> OUT3((OUT)) end ``` 应用 4:两个 pipeline 接入一个 mixer pipeline,应用于多路进一路出的混音场景 ```mermaid graph LR subgraph P1 File((File)) --> MP3_1[MP3] end subgraph P2 HTTP((HTTP)) --> MP3_2[MP3] end MP3_1 --> Mixer[Mixer Pipeline] MP3_2 --> Mixer[Mixer Pipeline] ``` 下面介绍的 pipeline 复杂度较高,应用在一些分流数据的复杂场景中,比如音乐播放的同时需要把音频数据给到 AEC 做参考。下图中都用了一个 copier 的 element,它具有一进多出的功能,输出可连接多路 pipeline 和一些其他输出形式。 应用 1:两路 pipeline 实现从单路输入多路输出的场景,解码的数据通过 copier 输出到另外一路 pipeline ```mermaid graph LR subgraph P1 File1((File)) --> Decoder1[Decoder] --> Copier1{Copier} --> Resample1[Resample] --> OUT1((OUT)) end Copier1 --> P2Resample subgraph P2 P2Resample[Resample] --> P2File((File)) end ``` 应用 2:两路 pipeline 实现从单路输入多路输出的场景,一路通过 pipeline 连接输出,一路通过 port 输出 ```mermaid graph LR subgraph P1 File((File)) --> Decoder[Decoder] --> Copier{Copier} end Copier --> P2Resample[Resample] Copier --> File2((File)) subgraph P2 P2Resample[Resample] --> I2S((I2S)) end ``` 应用 3:三路 pipeline 实现从单路输入多路输出的场景,解码的数据可通过 ringbuffer 连接每路 pipeline ```mermaid graph LR subgraph P1 P1File((File)) --> P1Decoder[Decoder] --> P1Copier{Copier} end P1Copier --> P2Resample[Resample] P1Copier --> P3Resample[Resample] subgraph P2 P2Resample[Resample] --> P2I2S((I2S)) end subgraph P3 P3Resample[Resample] --> P3File((File)) end ``` ## 使用说明 GMF-Core API 的简单示例代码请参考 [test_apps](./test_apps/main/cases/gmf_pool_test.c),更多实际应用示例请参考 GMF-Elements 的 examples。
idf.py add-dependency "espressif/esp_gmf_core^0.5.1"