Pipeline

概述

Pipeline(流水线)是一种处理音频流数据的统一框架组件,将音频流处理过程拆解成串行模式,提供了一种可灵活定义数据处理步骤的方法。上层应用可以按照不同的需求,依序预设好数据处理操作即可。

Pipeline框架借鉴了Linux ALSA框架的设计理念,通过流水线方式组织音频处理流程,使开发者能够将复杂的音频处理流程分解为多个独立的处理单元(processor),每个处理单元负责特定的数据处理任务。这种方式具有以下优势:

  • 模块化设计:每个处理单元职责单一,易于开发和维护

  • 灵活组合:可根据不同应用场景自由组合处理单元

  • 低耦合:处理单元之间通过标准接口通信,降低耦合度

  • 高复用性:通用处理单元可在多个stream中复用

  • 统一管理:通过stream统一管理整个处理流程的生命周期

功能特性

Pipeline提供了以下核心特性:

  • 灵活的数据流定义:支持上层应用灵活定义数据处理流程

  • 统一的processor接口:提供处理流程统一的processor结构体,便于统一管理

  • 多种运行模式:支持独立任务、预设任务、中断处理、定时器处理等多种运行模式

  • 数据流分支与合并:通过mirror和gather模块实现复杂的数据流拓扑

  • 数据帧长度转换:通过shaper模块实现灵活的数据帧长度转换

  • 动态参数更新:运行过程中可动态更新processor参数

  • 流控与挂起:支持数据流的暂停和恢复

  • 内存管理优化:提供scratch memory共享机制,减少内存碎片

架构设计

核心概念

Pipeline框架由以下核心概念组成:

1. Processor(处理器)

Processor是Pipeline中最基本的处理单元,代表一个独立的数据处理功能模块。每个processor需要实现标准的 wq_pipeline_processor_t 接口:

typedef struct pipeline_processor {
    char *name;                                      // Processor名称
    WQ_RET (*query)(void *, struct pipeline_processor_info *);   // 查询processor信息
    void *(*probe)(void *, struct pipeline_processor_preset *);    // 初始化processor
    void (*remove)(void *);                                 // 移除processor
    void (*reset)(void *);                                   // 重置processor
    WQ_RET (*update)(void *, void *);                        // 更新processor参数
    WQ_RET (*run)(void *, struct wq_plb *, struct wq_plb **); // 执行处理
} wq_pipeline_processor_t;

Processor接口说明:

  • query:查询processor的资源需求,返回所需MIPS、persistent buffer大小、scratch buffer大小等信息

  • probe:初始化processor,返回processor的handler(私有数据结构指针)

  • remove:清理processor资源

  • reset:重置processor到初始状态

  • update:动态更新processor的运行参数

  • run:实际的数据处理函数,输入一个plb,输出一个或多个plb

2. Procedure(处理工序)

Procedure是processor在特定stream中的实例化表示。当stream创建时,会为schedule中的每个processor创建对应的procedure。Procedure包含:

  • processor handler(由probe返回的processor私有数据)

  • processor配置信息

  • 在stream中的索引位置

  • 处理状态

Procedure是stream中实际执行的数据处理单元。

3. PLB(Pipeline Buffer)

PLB(Pipeline Buffer)是数据在Pipeline中流动的基本单位,包含数据帧的元数据和实际数据:

typedef struct wq_plb {
    // 链表节点
    struct wq_plb *next;
    struct wq_plb *prev;

    // 缓冲区指针
    uint8_t *buf;         // 数据缓冲区起始指针
    uint8_t *end;         // 数据缓冲区结束指针
    uint8_t *data;        // 实际数据起始指针
    uint32_t sp;          // 采样点数
    uint32_t len;         // 每个通道的数据长度(字节)
    uint32_t channel_gap;  // 通道间间隔(字节)

    // 时间戳
    uint32_t ts;          // 主路径时间戳(采样点数)
    uint32_t ts_sec;      // 辅助路径时间戳

    // 格式与通道
    uint8_t format;       // 数据格式(PCM/SBC/AAC等)
    uint8_t channel;       // 通道位图
    uint8_t bit_width;    // 采样位宽(16bit/32bit)
    uint8_t dev_form;     // 音频设备形式
    uint8_t reserved;

    // 用户计数
    uint32_t users;       // 引用计数

    // 流程控制
    void *procedure;    // 挂起该plb的procedure
    void (*free_cb)(struct wq_plb *, void *);
    void *free_cb_param;
    uint16_t plb_id;   // plb关联的processor id和stream id
    uint32_t param;      // 用户定义参数
    uint32_t flags;       // 标志位
} wq_plb_t;

PLB标志位说明:

  • 低16位(不继承):

    • WQ_PIPELINE_BUFF_FLAG_ZERO_DATA:零数据标志

    • WQ_PIPELINE_BUFF_FLAG_PB_DATA:PB数据标志

    • WQ_PIPELINE_BUFF_FLAG_FORCE_STEREO_DATA:强制立体声数据标志

  • 高16位(继承传递):

    • WQ_PIPELINE_BUFF_FLAG_TAIL_DATA:尾部数据标志

    • WQ_PIPELINE_BUFF_FLAG_MIX_DATA:混音数据标志

    • WQ_PIPELINE_BUFF_FLAG_REF_DATA:参考数据标志

    • WQ_PIPELINE_BUFF_FLAG_READ_ONLY:只读数据标志

4. Stream(数据流)

Stream是Pipeline中的数据流管理单元,包含:

  • 一串按顺序执行的procedure链表

  • 待处理的plb列表(plb_list)

  • 流的运行状态和控制信息

  • 可能拥有的独立任务或关联的预设任务

  • 共享的scratch memory

Stream类型:

typedef enum {
    WQ_PIPELINE_STREAM_CTX_OWN_TASK,         // Stream拥有独立任务,可被触发
    WQ_PIPELINE_STREAM_CTX_PRESET_TASK,      // Stream属于预设任务,可被触发
    WQ_PIPELINE_STREAM_CTX_IRQ_HANDLER,       // Stream可被中断处理器触发
    WQ_PIPELINE_STREAM_CTX_TIMER_HANDLER,     // Stream可被定时器处理器触发
    WQ_PIPELINE_STREAM_CTX_HIGH_SHARE_TASK,   // Stream属于高优先级共享任务
    WQ_PIPELINE_STREAM_CTX_LOW_SHARE_TASK,    // Stream属于低优先级共享任务
    WQ_PIPELINE_STREAM_CTX_CALLED,           // Stream只能被调用,不能被触发
} WQ_PIPELINE_STREAM_CTX;

流程图

Pipeline的数据流处理流程如下:

        flowchart TD
    A([输入数据]) --> B[Procedure 1]
    B --> buf1[/Pipeline buffer/]
    buf1 --> C[Procedure 2]
    C --> buf2[/Pipeline buffer/]
    buf2 --> D[Procedure 3]
    D --> buf3[/Pipeline buffer/]
    buf3 --> E([输出数据])

    style A fill:#e1f5e1,stroke:#4caf50,stroke-width:2px
    style E fill:#ffe0e0,stroke:#f44336,stroke-width:2px
    style B fill:#e3f2fd,stroke:#2196f3,stroke-width:2px
    style C fill:#e3f2fd,stroke:#2196f3,stroke-width:2px
    style D fill:#e3f2fd,stroke:#2196f3,stroke-width:2px
    style buf1 fill:#fff3e0,stroke:#ff9800,stroke-width:2px
    style buf2 fill:#fff3e0,stroke:#ff9800,stroke-width:2px
    style buf3 fill:#fff3e0,stroke:#ff9800,stroke-width:2px
    

Pipeline数据流流程图

Pipeline使用时,每个处理步骤都作为独立的处理工序,以流水线方式运行。前一个处理工序的输出内容,作为下一个处理工序的输入内容。

运行原理

Stream创建流程

  1. Stream创建命令接收:通过`wq_pipeline_stream_control()`函数发送CREATE命令

  2. 分配Stream资源: - 分配stream结构体 - 初始化procedure链表、command链表、plb列表 - 分配processor preset结构

  3. 创建Procedure: - 遍历stream_program数组 - 对每个processor调用probe接口,创建procedure - 将procedure加入stream的procedure链表

  4. 创建运行环境: - 根据stream类型创建独立任务或关联预设任务 - 创建stream互斥锁

  5. 分配Scratch Memory(可选): - 如果启用了CONFIG_SCRATCH_MEM_ENABLE - 遍历所有processor,获取最大的scratch_size - 分配共享的scratch memory

Stream运行流程

运行触发

Stream的运行由以下方式触发:

  1. 显式触发:调用`wq_pipeline_stream_trigger(stream_id)`

  2. 任务循环:对于OWN_TASK类型的stream,任务持续运行

  3. 消息触发:通过IPC消息或中断触发

  4. 挂起数据检测:检测到有挂起的plb时自动触发

数据处理循环

Stream运行时的核心逻辑:

数据处理循环流程:

  1. 检查是否有挂起的数据帧 - 如果有挂起的plb,根据plb->procedure开始执行 - 如果没有挂起的plb,从stream的第一个procedure开始执行

  2. 按procedure链表顺序执行 - 遍历 stream->procedure_list - 调用 procedure->processor->run(procedure->handler, input_plb, &output_plb)

  3. 处理输出数据帧:如果procedure输出多个plb,第一个输出plb作为下一个procedure的输入,其他plb挂起到对应的procedure(plb->procedure赋值)

  4. 继续执行直到plb_list为空 - 重复步骤1-3,直到没有待处理的数据帧

Stream控制命令

Pipeline提供多种控制命令,通过`wq_pipeline_stream_control()`发送:

1. CREATE命令(创建流)

command.type = WQ_PIPELINE_STREAM_COMMAND_CREATE;
command.stream_id = STREAM_ID;
command.param = &runner;
command.init_sample_rate = sample_rate;
  • 执行方式:同步阻塞执行,创建完成后才返回

  • 功能:创建新的stream,初始化所有procedure

  • 返回:创建成功后返回WQ_RET_OK

2. DESTROY命令(销毁流)

command.type = WQ_PIPELINE_STREAM_COMMAND_DESTROY;
command.stream_id = STREAM_ID;
command.callback = optional_destroy_callback;
command.callback_param = optional_event;
  • 执行方式:默认异步执行

  • 阻塞方式:实现callback函数,在callback中设置event,主线程等待event

  • 功能:释放stream所有资源,包括procedure、plb、scratch memory

3. UPDATE_PROCEDURE命令(更新过程)

command.type = WQ_PIPELINE_STREAM_COMMAND_UPDATE_PROCEDURE;
command.stream_id = STREAM_ID;
command.processor = target_processor;
command.procedure_index = 0;  // 保留字段
command.stream_depend = false;  // 命令是否依赖stream运行
command.param = update_data;
command.callback = optional_callback;
command.callback_param = optional_param;
  • 执行方式:异步执行

  • 功能:动态更新指定processor的运行参数

  • 参数传递:param传递给processor->update接口

4. 其他控制命令

  • PEND命令:暂停stream运行

  • RESUME命令:恢复stream运行

  • RESET_PROCEDURE命令:重置procedure状态

  • BYPASS_PROCEDURE命令:绕开procedure

  • DEBUG_PROCEDURE命令:启用procedure日志

  • DUMP_PROCEDURE_INPUT命令:启用procedure输入dump

  • DUMP_PROCEDURE_OUTPUT命令:启用procedure输出dump

数据流管理

PLB生命周期

PLB生命周期:

  1. Processor在run()中调用wq_plb_alloc()分配plb

  2. 设置plb的数据指针、长度、格式等信息

  3. 将plb返回给stream作为输出

PLB传递:
  1. Stream将procedure输出的plb作为下一个procedure的输入

  2. 如果procedure输出多个plb: - 第一个plb直接传递给下一个procedure - 其他plb挂起(plb->procedure赋值)

PLB释放:
  1. Stream或processor调用wq_plb_free()释放plb

  2. 减少users引用计数

  3. 当users为0时,真正释放内存

  4. 如果设置了free_cb,调用free_cb回调

挂起数据帧

当processor需要等待外部事件时,可以将plb挂起:

// 将plb挂起到当前procedure
input_plb->procedure = current_procedure;
// 不将plb传递给下一个procedure,返回NULL
*output_plb = NULL;

当外部事件就绪后,再次触发stream,stream会根据plb->procedure从挂起点恢复执行。

Scratch Memory机制

Scratch memory是stream中所有processor共享的可复用内存区域:

优点: - 减少内存分配次数 - 降低内存碎片 - 提高内存使用效率

配置方式: 1. 自动分配:runner->scratch为NULL或scratch_size为0时,自动分配 2. 外部预分配:runner配置预分配的scratch memory

使用规则: - Scratch memory是stream中所有processor共享 - Processor必须在执行完成后不需要再使用scratch memory中的数据 - 不同processor按需使用scratch memory的不同区域 - Stream销毁时自动释放

多路数据流处理

Mirror模块

Mirror模块用于将一个procedure的输出分发到多个下游procedure:

应用场景: - 创建数据流旁路或回路 - 在不同stream之间传递数据 - 实现数据流广播

配置结构:

typedef struct {
    uint16_t stream_id;      // 当前所属stream的id
    uint16_t mirror_id;      // 预分配的id,相同mirror_id关联到同一mirror handler
    uint16_t depth;          // mirror tx_list的深度,超限会打印"process too slow"日志
    bool copy_mode;           // 是否复制数据,false时只clone plb配置
    bool redirect_mode;        // 是否丢弃数据
    uint8_t bypass_id_map;   // 需要丢弃数据的stream对应的map值
} wq_pipeline_mirror_config_t;

工作原理: 1. 每个mirror procedure都有一个mirror client 2. Mirror processor将每个mirror client的输入数据复制分发 3. 根据mirror_id将多个mirror client关联到同一mirror handler

Gather模块

Gather模块是多输入单输出的模块,用于混音运算:

应用场景: - 收集多个procedure的输出完成混音 - 多路音频数据合成

配置结构:

typedef struct {
    uint16_t stream_id;           // 当前所属stream的id
    uint16_t bit_width;           // 采样位宽(16bit/32bit)
    uint16_t depth;               // hold stb队列深度,0使用默认值
    uint8_t gather_id;            // 预分配id,相同gather_id的procedure关联在一起
    bool primary;                 // 是否是输出流,只有primary gather才进行gather输出
    bool match_ts;                // 是否需要时间戳对齐
    bool use_sec_ts;              // 是否使用辅助路径时间戳
    bool redirect_drop;           // 非primary流rx_list满时是否丢弃数据
    wq_pipeline_processor_t *sub_processor;        // 子processor
    void *sub_processor_config;   // 子processor配置
} wq_pipeline_gather_config_t;

工作原理: 1. Gather client缓存输入plb到队列 2. Primary gather执行时,等待所有gather client的数据就绪 3. 根据match_ts进行时间戳对齐 4. 对齐的数据进行混音运算 5. 如果有sub_processor,传递给sub_processor处理

特殊行为: - 只有一个gather client时,gather processor不做处理,直接输出 - 非primary的gather client只缓存,不输出数据

Shaper模块

Shaper模块用于数据帧长度转换:

应用场景: - 改变数据帧的时长(如10ms → 7.5ms) - 统一不同来源的数据帧长度 - 调整算法处理粒度

配置结构:

typedef struct {
    uint16_t size;            // Pool大小(字节),0表示使用默认大小
    uint16_t out_sp;          // 输出采样点数(如16K采样率,7.5ms为120)
    uint16_t bit_width;       // 采样位宽(16bit/32bit)
    uint16_t channel_num;     // 通道数
} wq_pipeline_shaper_config_t;

工作原理: 1. 将输入的plb数据拷贝到预分配的数据池 2. 根据out_sp配置取出特定长度的数据帧 3. 输出数据帧,丢弃不完整的数据或缓存等待 4. 循环执行直到输入数据处理完

Breaker模块

Breaker模块用于释放上一个procedure输出的数据帧:

应用场景: - 在stream结束时释放不再需要的数据帧 - 避免内存泄漏 - 清理数据流末端

使用位置: - 必须放在stream_program END之前 - 作为stream的最后一个procedure

详细使用流程

步骤1:定义数据流处理流程

需要将音频处理流程分解为多个processor步骤,使用宏定义创建stream schedule。

简单流程(无分支)

适用于线性处理流程,每个procedure的输出只传递给下一个procedure:

        flowchart TD
    A([开始]) --> B[Processor A]
    B --> C[Processor B]
    C --> D[Processor C]
    D --> E([结束])

    style A fill:#e1f5e1,stroke:#4caf50,stroke-width:2px
    style E fill:#ffe0e0,stroke:#f44336,stroke-width:2px
    style B fill:#e3f2fd,stroke:#2196f3,stroke-width:2px
    style C fill:#e3f2fd,stroke:#2196f3,stroke-width:2px
    style D fill:#e3f2fd,stroke:#2196f3,stroke-width:2px
    

线性数据流示意

#define STREAM_SCHEDULE_DEMO                         \
   WQ_PIPELINE_STREAM_SCHEDULE_START                        \
   WQ_PIPELINE_STREAM_SCHEDULE_STEP(processor_A, &A_config) \
   WQ_PIPELINE_STREAM_SCHEDULE_STEP(processor_B, &B_config) \
   WQ_PIPELINE_STREAM_SCHEDULE_STEP(processor_C, &C_config) \
   WQ_PIPELINE_STREAM_SCHEDULE_END

void demo_stream_init(void)
{
    wq_pipeline_stream_schedule_t demo_stream_schedule[] = STREAM_SCHEDULE_DEMO;
   ...
}

分支流程(使用Mirror)

适用于需要数据分流或回流的场景,使用mirror实现数据分发:

        flowchart TD
    A([开始]) --> B[Processor A]
    B --> C[Processor B]
    C --> D[Processor C]
    D --> E[Processor D]
    E --> M1{{Mirror 1}}

    M1 --> F[Processor E]
    F --> M2{{Mirror 2}}

    M2 --> G[Processor F]
    G --> H[Processor G]
    H --> I[Processor H]
    I --> J([结束])

    M1 -.-> K[Processor I]
    K -.-> B

    M2 -.-> L[Processor J]
    L -.-> H

    style A fill:#e1f5e1,stroke:#4caf50,stroke-width:2px
    style J fill:#ffe0e0,stroke:#f44336,stroke-width:2px
    style M1 fill:#fff3e0,stroke:#ff9800,stroke-width:3px
    style M2 fill:#fff3e0,stroke:#ff9800,stroke-width:3px
    style B fill:#e3f2fd,stroke:#2196f3,stroke-width:2px
    style C fill:#e3f2fd,stroke:#2196f3,stroke-width:2px
    style D fill:#e3f2fd,stroke:#2196f3,stroke-width:2px
    style E fill:#e3f2fd,stroke:#2196f3,stroke-width:2px
    style F fill:#e3f2fd,stroke:#2196f3,stroke-width:2px
    style G fill:#e3f2fd,stroke:#2196f3,stroke-width:2px
    style H fill:#e3f2fd,stroke:#2196f3,stroke-width:2px
    style I fill:#e3f2fd,stroke:#2196f3,stroke-width:2px
    style K fill:#f3e5f5,stroke:#9c27b0,stroke-width:2px
    style L fill:#f3e5f5,stroke:#9c27b0,stroke-width:2px
    

Mirror分支与回路示意

#define STREAM_SCHEDULE_DEMO                                               \
    WQ_PIPELINE_STREAM_SCHEDULE_START                                             \
    /* primary stream */                                                   \
    WQ_PIPELINE_STREAM_SCHEDULE_STEP(processor_A, &A_config)                      \
    WQ_PIPELINE_STREAM_SCHEDULE_STEP(processor_B, &B_config)                      \
    WQ_PIPELINE_STREAM_SCHEDULE_STEP(processor_C, &C_config)                      \
    WQ_PIPELINE_STREAM_SCHEDULE_STEP(processor_D, &D_config)                      \
    WQ_PIPELINE_STREAM_SCHEDULE_STEP(wq_pipeline_mirror, &mirror_1_config)        \
    WQ_PIPELINE_STREAM_SCHEDULE_STEP(processor_E, &E_config)                      \
    WQ_PIPELINE_STREAM_SCHEDULE_STEP(wq_pipeline_mirror, &mirror_2_config)        \
    WQ_PIPELINE_STREAM_SCHEDULE_STEP(processor_F, &F_config)                      \
    WQ_PIPELINE_STREAM_SCHEDULE_STEP(processor_G, &G_config)                      \
    WQ_PIPELINE_STREAM_SCHEDULE_STEP(processor_H, &H_config)                      \
    /* sub stream 1 from mirror 1 */                                       \
    WQ_PIPELINE_STREAM_SCHEDULE_STEP(wq_pipeline_mirror, &mirror_1_config)        \
    WQ_PIPELINE_STREAM_SCHEDULE_STEP(processor_I, &I_config)                      \
    WQ_PIPELINE_STREAM_SCHEDULE_STEP(processor_B, &B_config)                      \
    /* sub stream 2 from mirror 2 */                                       \
    WQ_PIPELINE_STREAM_SCHEDULE_STEP(wq_pipeline_mirror, &mirror_2_config)        \
    WQ_PIPELINE_STREAM_SCHEDULE_STEP(processor_J, &J_config)                      \
    WQ_PIPELINE_STREAM_SCHEDULE_STEP(processor_G, &G_config)                      \
    WQ_PIPELINE_STREAM_SCHEDULE_END

void demo_stream_init(void)
{
    wq_pipeline_stream_schedule_t demo_stream_schedule[] = STREAM_SCHEDULE_DEMO;
   ...
}

独立Sub Stream配置

也可以将sub stream配置在独立的stream中,提高可读性和维护性:

#define STREAM_SCHEDULE_DEMO                                               \
    WQ_PIPELINE_STREAM_SCHEDULE_START                                             \
    /* primary stream */                                                   \
    WQ_PIPELINE_STREAM_SCHEDULE_STEP(processor_A, &A_config)                      \
    WQ_PIPELINE_STREAM_SCHEDULE_STEP(processor_B, &B_config)                      \
    WQ_PIPELINE_STREAM_SCHEDULE_STEP(processor_C, &C_config)                      \
    WQ_PIPELINE_STREAM_SCHEDULE_STEP(processor_D, &D_config)                      \
    WQ_PIPELINE_STREAM_SCHEDULE_STEP(wq_pipeline_mirror, &mirror_1_config)        \
    WQ_PIPELINE_STREAM_SCHEDULE_STEP(processor_E, &E_config)                      \
    WQ_PIPELINE_STREAM_SCHEDULE_STEP(wq_pipeline_mirror, &mirror_2_config)        \
    WQ_PIPELINE_STREAM_SCHEDULE_STEP(processor_F, &F_config)                      \
    WQ_PIPELINE_STREAM_SCHEDULE_STEP(processor_G, &G_config)                      \
    WQ_PIPELINE_STREAM_SCHEDULE_STEP(processor_H, &H_config)                      \
    WQ_PIPELINE_STREAM_SCHEDULE_END

#define STREAM_SCHEDULE_DEMO_SUB_STREAM_1                                  \
    WQ_PIPELINE_STREAM_SCHEDULE_START                                             \
    /* sub stream1 from mirror 1 */                                       \
    WQ_PIPELINE_STREAM_SCHEDULE_STEP(wq_pipeline_mirror, &mirror_1_config)        \
    WQ_PIPELINE_STREAM_SCHEDULE_STEP(processor_I, &I_config)                      \
    WQ_PIPELINE_STREAM_SCHEDULE_STEP(processor_B, &B_config)                      \
    WQ_PIPELINE_STREAM_SCHEDULE_END

#define STREAM_SCHEDULE_DEMO_SUB_STREAM_2                                  \
    WQ_PIPELINE_STREAM_SCHEDULE_START                                             \
    /* sub stream2 from mirror 2 */                                       \
    WQ_PIPELINE_STREAM_SCHEDULE_STEP(wq_pipeline_mirror, &mirror_2_config)        \
    WQ_PIPELINE_STREAM_SCHEDULE_STEP(processor_J, &J_config)                      \
    WQ_PIPELINE_STREAM_SCHEDULE_STEP(processor_G, &G_config)                      \
    WQ_PIPELINE_STREAM_SCHEDULE_END

步骤2:实现自定义Processor

自定义processor需要实现`wq_pipeline_processor_t`接口:

/* Processor信息查询 */
static WQ_RET demo_processor_query(void *config, struct pipeline_processor_info *info)
{
    demo_config_t *cfg = (demo_config_t *)config;

    info->mips = 10;                  // processor需要的MIPS
    info->persistent_size = 1024;     // 持久化buffer大小
    info->scratch_size = 512;         // scratch buffer大小
    info->stack_size = 256;           // 栈大小
    info->processor_info = NULL;      // 自定义信息指针

    return WQ_RET_OK;
}

/* Processor初始化 */
static void *demo_processor_probe(void *config, struct pipeline_processor_preset *preset)
{
    demo_config_t *cfg = (demo_config_t *)config;
    demo_handler_t *handler;

    // 分配handler
    handler = (demo_handler_t *)wq_heap_caps_malloc(
        sizeof(demo_handler_t), MALLOC_CAP_NONE);
    assert(handler);

    // 初始化handler
    memset(handler, 0, sizeof(demo_handler_t));
    handler->config = cfg;
    handler->preset = preset;

    // 初始化内部资源
    /* ... */

    return handler;
}

/* Processor移除 */
static void demo_processor_remove(void *handler)
{
    demo_handler_t *h = (demo_handler_t *)handler;

    // 释放内部资源
    /* ... */

    // 释放handler
    wq_heap_caps_free(h);
}

/* Processor重置 */
static void demo_processor_reset(void *handler)
{
    demo_handler_t *h = (demo_handler_t *)handler;

    // 重置内部状态
    /* ... */
}

/* Processor参数更新 */
static WQ_RET demo_processor_update(void *handler, void *param)
{
    demo_handler_t *h = (demo_handler_t *)handler;
    demo_update_t *update = (demo_update_t *)param;

    // 更新processor参数
    h->config->param = update->param;

    return WQ_RET_OK;
}

/* Processor执行 */
static WQ_RET demo_processor_run(void *handler, struct wq_plb *input_plb,
                                   struct wq_plb **output_plb)
{
    demo_handler_t *h = (demo_handler_t *)handler;
    uint32_t out_len = calculate_output_len(input_plb);

    // 分配输出plb
    *output_plb = wq_plb_alloc(out_len, 0);
    if (!*output_plb) {
        return WQ_RET_NOMEM;
    }

    // 设置输出plb属性
    (*output_plb)->format = input_plb->format;
    (*output_plb)->channel = input_plb->channel;
    (*output_plb)->bit_width = input_plb->bit_width;
    (*output_plb)->sample_rate = input_plb->sample_rate;

    // 处理数据
    memcpy((*output_plb)->data, input_plb->data, input_plb->len);
    /* 数据处理逻辑... */

    return WQ_RET_OK;
}

/* Processor接口定义 */
const wq_pipeline_processor_t demo_processor = {
    .name = "demo_processor",
    .query = demo_processor_query,
    .probe = demo_processor_probe,
    .remove = demo_processor_remove,
    .reset = demo_processor_reset,
    .update = demo_processor_update,
    .run = demo_processor_run,
};

注意事项:

  1. run()函数是核心,实现实际的数据处理逻辑

  2. 输出plb管理:run()必须分配输出plb,调用方负责释放

  3. 引用计数:通过wq_plb_get()和wq_plb_free()管理plb生命周期

  4. 错误处理:遇到错误时返回适当的WQ_RET错误码

  5. 线程安全:如果processor在多线程环境使用,需要适当的同步

步骤3:配置Stream Runner

配置stream的运行环境和任务参数:

/* Stream任务配置 */
static char demo_task_name[] = "stream_demo";
static wq_pipeline_stream_task_cfg_t demo_task_cfg = {
    .prio = 7,                    // 任务优先级
    .stack_size = 2048,           // 任务栈大小
    .name = demo_task_name,       // 任务名称
};

void demo_stream_start(void)
{
    /* 定义stream runner */
    wq_pipeline_stream_runner_t demo_runner;
    memset(&demo_runner, 0, sizeof(wq_pipeline_stream_runner_t));
    demo_runner.type = WQ_PIPELINE_STREAM_CTX_OWN_TASK;
    demo_runner.config = &demo_task_cfg;
    demo_runner.schedule = demo_stream_schedule;
    /* scratch memory配置可选,见下节 */
}

Stream类型选择:

  • WQ_PIPELINE_STREAM_CTX_OWN_TASK:推荐,stream拥有独立任务,可随时触发

  • WQ_PIPELINE_STREAM_CTX_PRESET_TASK:多个stream共享同一任务

  • WQ_PIPELINE_STREAM_CTX_CALLED:stream只能被函数调用,不能独立运行

步骤4:配置Scratch Memory

Scratch memory是stream中所有processor共享的可复用内存:

方式1:自动分配(推荐)

系统根据所有processor的最大scratch_size自动分配:

/* 开启CONFIG_SCRATCH_MEM_ENABLE后自动分配 */
#define CONFIG_SCRATCH_MEM_ENABLE

void demo_stream_start(void)
{
    wq_pipeline_stream_runner_t demo_runner;
    memset(&demo_runner, 0, sizeof(wq_pipeline_stream_runner_t));
    demo_runner.type = WQ_PIPELINE_STREAM_CTX_OWN_TASK;
    demo_runner.config = &demo_task_cfg;
    demo_runner.schedule = demo_stream_schedule;
    /* 不设置scratch和scratch_size,系统自动分配 */
}

优点: - 简单易用,无需手动管理 - 系统自动计算所需大小 - Stream销毁时自动释放

前提条件: - 需要定义CONFIG_SCRATCH_MEM_ENABLE宏 - Processor的query()必须正确返回scratch_size

方式2:外部预分配

对于有特殊内存要求的场景,可以外部预分配scratch memory:

void demo_stream_start(void)
{
    /* 定义stream runner */
    wq_pipeline_stream_runner_t demo_runner;
    memset(&demo_runner, 0, sizeof(wq_pipeline_stream_runner_t));
    demo_runner.type = WQ_PIPELINE_STREAM_CTX_OWN_TASK;
    demo_runner.config = &demo_task_cfg;
    demo_runner.schedule = demo_stream_schedule;

    /* 外部预分配scratch memory */
    void *demo_scratch;
    demo_scratch = wq_heap_caps_malloc(sizeof(uint8_t), MALLOC_CAP_NONE);
    assert(demo_scratch);
    memset(demo_scratch, 0, sizeof(uint8_t));
    demo_runner.scratch = demo_scratch;
    demo_runner.scratch_size = sizeof(uint8_t);

    /* 创建stream */
    wq_pipeline_stream_command_t command;
    memset(&command, 0, sizeof(wq_pipeline_stream_command_t));
    command.type = WQ_PIPELINE_STREAM_COMMAND_CREATE;
    command.stream_id = STREAM_ID_DEMO;
    command.init_sample_rate = demo_sample_rate;
    command.param = &demo_runner;
    wq_pipeline_stream_control(&command);
}

注意事项:

  1. 大小匹配:外部分配的scratch_size必须大于等于所有processor的scratch_size总和

  2. 内存属性:根据需要选择合适的内存属性(对齐、DMA等)

  3. 生命周期:外部scratch memory需要在stream销毁后手动释放

  4. 共享规则:scratch memory是stream中所有processor共享,需要保证使用安全

步骤5:创建Stream

发送CREATE命令创建stream:

void demo_stream_start(void)
{
    wq_pipeline_stream_runner_t demo_runner;
    memset(&demo_runner, 0, sizeof(wq_pipeline_stream_runner_t));
    demo_runner.type = WQ_PIPELINE_STREAM_CTX_OWN_TASK;
    demo_runner.config = &demo_task_cfg;
    demo_runner.schedule = demo_stream_schedule;

    /* 创建stream命令 */
    wq_pipeline_stream_command_t command;
    memset(&command, 0, sizeof(wq_pipeline_stream_command_t));
    command.type = WQ_PIPELINE_STREAM_COMMAND_CREATE;
    command.stream_id = STREAM_ID_DEMO;
    command.init_sample_rate = demo_sample_rate;
    command.param = &demo_runner;

    /* 发送CREATE命令(阻塞执行) */
    WQ_RET ret = wq_pipeline_stream_control(&command);
    if (ret != WQ_RET_OK) {
        printf("Create stream failed: %d\n", ret);
        return;
    }
    printf("Stream %d created successfully\n", STREAM_ID_DEMO);
}

CREATE命令执行流程:

  1. 阻塞执行,创建完成后才返回

  2. 分配stream结构体和内部资源

  3. 遍历schedule数组,为每个processor创建procedure

  4. 根据stream类型创建或关联任务

  5. 分配scratch memory(如果配置了)

  6. 初始化stream状态为有效

步骤6:启动Stream运行

Stream创建后,需要触发才能开始处理数据:

void demo_stream_start(void)
{
    /* ... 创建stream ... */

    /* 触发stream运行 */
    WQ_RET ret = wq_pipeline_stream_trigger(STREAM_ID_DEMO);
    if (ret != WQ_RET_OK) {
        printf("Trigger stream failed: %d\n", ret);
        return;
    }
    printf("Stream %d triggered\n", STREAM_ID_DEMO);
}

触发方式:

  1. 单次触发:调用`wq_pipeline_stream_trigger()`,stream处理完所有挂起数据后停止

  2. 持续触发:在数据到达时持续触发,stream保持活跃状态

  3. 定时触发:通过定时器定期触发,适用于需要定期处理的数据

OWN_TASK类型stream: - 拥有独立任务的stream会自动循环运行 - 不需要显式触发,但触发可加速处理

步骤7:动态更新Processor参数

运行过程中,可以通过UPDATE命令动态更新processor参数:

/* 更新命令回调,用于释放动态分配的参数 */
static void demo_update_callback(void *arg)
{
    uint32_t *demo_update_param = (uint32_t *)arg;
    wq_heap_caps_free(demo_update_param);
}

void demo_stream_update(void)
{
    /* 准备更新参数 */
    uint32_t *demo_update_param;
    demo_update_param = wq_heap_caps_malloc(sizeof(uint8_t), MALLOC_CAP_NONE);
    assert(demo_update_param);
    memset(demo_update_param, 0, sizeof(uint8_t));
    *demo_update_param = NEW_VALUE;  // 设置新值

    /* 配置更新命令 */
    wq_pipeline_stream_command_t command;
    memset(&command, 0, sizeof(wq_pipeline_stream_command_t));
    command.type = WQ_PIPELINE_STREAM_COMMAND_UPDATE_PROCEDURE;
    command.stream_id = STREAM_ID_DEMO;
    command.processor = &demo_processor;         // 指定要更新的processor
    command.procedure_index = 0;                 // 保留字段,可设为0
    command.stream_depend = false;               // 不依赖stream运行状态
    command.param = demo_update_param;           // 更新参数
    command.callback = demo_update_callback;     // 命令完成回调
    command.callback_param = demo_update_param;  // 回调参数(用于释放)

    /* 发送UPDATE命令(异步执行) */
    WQ_RET ret = wq_pipeline_stream_control(&command);
    if (ret != WQ_RET_OK) {
        printf("Update processor failed: %d\n", ret);
        wq_heap_caps_free(demo_update_param);
    }
}

UPDATE命令特点:

  1. 异步执行:命令加入stream的command队列,在stream运行时处理

  2. 参数传递:param传递给processor->update()接口

  3. 回调机制:完成后调用callback,可用于释放动态分配的参数

  4. stream_depend:决定是否依赖stream运行,false时即使stream暂停也会处理

步骤8:销毁Stream

不再需要stream时,需要发送DESTROY命令:

异步销毁(默认)

void demo_stream_stop(void)
{
    /* 销毁stream命令 */
    wq_pipeline_stream_command_t command;
    memset(&command, 0, sizeof(wq_pipeline_stream_command_t));
    command.type = WQ_PIPELINE_STREAM_COMMAND_DESTROY;
    command.stream_id = STREAM_ID_DEMO;

    /* 发送DESTROY命令(异步执行,立即返回) */
    wq_pipeline_stream_control(&command);
    printf("Stream %d destroy command sent\n", STREAM_ID_DEMO);
}

同步销毁(带回调)

如果需要等待stream销毁完成,需要实现回调函数:

/* stream销毁完成的回调 */
static void demo_stream_destroy_callback(void *arg)
{
    os_event_h p_event = (os_event_h)arg;
    os_set_event(p_event, BIT(0));
}

void demo_stream_stop(void)
{
    /* 创建事件用于同步 */
    os_event_h p_event;
    p_event = os_create_event(WQ_PIPELINE_MID);
    if (!p_event) {
        assert(0);
    }

    /* 销毁stream命令 */
    wq_pipeline_stream_command_t command;
    memset(&command, 0, sizeof(wq_pipeline_stream_command_t));
    command.type = WQ_PIPELINE_STREAM_COMMAND_DESTROY;
    command.stream_id = STREAM_ID_DEMO;
    command.callback = demo_stream_destroy_callback;
    command.callback_param = p_event;

    /* 发送DESTROY命令 */
    wq_pipeline_stream_control(&command);

    /* 等待销毁完成事件 */
    uint32_t events;
    bool ret = os_wait_event(p_event, MAX_TIME, &events);
    if (ret && (events & BIT(0))) {
        printf("Stream %d destroyed successfully\n", STREAM_ID_DEMO);
    }

    os_delete_event(p_event);
}

同步销毁流程:

  1. 发送DESTROY命令

  2. 命令加入command队列,异步执行

  3. 主线程等待event

  4. Stream销毁完成后,回调设置event

  5. 主线程从等待中返回

特殊模块使用详解

Pipeline Shaper使用

Shaper模块用于实现PCM数据帧长度转换:

配置示例(将10ms数据帧转换为7.5ms):

/* 16K采样率 */
#define SAMPLE_RATE_16K 16000
/* 10ms = 160采样点 */
#define FRAME_SIZE_10MS 160
/* 7.5ms = 120采样点 */
#define FRAME_SIZE_7_5MS 120

wq_pipeline_shaper_config_t demo_shaper_config = {
    .size = 0,                   // 使用默认缓存buffer长度
    .out_sp = FRAME_SIZE_7_5MS,  // 输出120个采样点(7.5ms)
    .bit_width = 16,             // 16bit位宽
    .channel_num = 2,            // 2通道
};

/* 在stream schedule中使用 */
WQ_PIPELINE_STREAM_SCHEDULE_STEP(wq_pipeline_shaper, &demo_shaper_config)

Shaper工作原理:

  1. 接收输入plb,拷贝数据到内部缓冲区(数据池)

  2. 按照out_sp配置的采样点数取出数据

  3. 如果缓冲区数据不足,缓存数据等待更多输入

  4. 输出固定长度的数据帧,保持输出帧长度的稳定性

注意事项:

  1. size参数:0表示自动计算缓存大小,通常为4 * out_sp * bit_width/8 * channel_num

  2. 累积效应:输入数据会累积直到满足输出长度,可能引入延迟

  3. 延迟控制:根据应用场景选择合适的out_sp,平衡延迟和稳定性

Pipeline Gather使用

Gather模块用于多路数据混音:

配置示例:

wq_pipeline_gather_config_t demo_gather_config = {
    .stream_id = STREAM_ID_DEMO,
    .gather_id = STREAM_ID_GATHER,
    .bit_width = 16,
    .depth = 0,                 // 使用默认深度
    .primary = true,            // 这是主流,即输出流
    .match_ts = true,           // 需要时间戳对齐
    .use_sec_ts = true,         // 使用sec时间戳
    .sub_processor = NULL,      // 没有子处理流程
    .sub_processor_config = NULL,
};

/* gather client配置(非primary) */
wq_pipeline_gather_config_t demo_gather_client_config = {
    .stream_id = STREAM_ID_INPUT_1,
    .gather_id = STREAM_ID_GATHER,
    .bit_width = 16,
    .depth = 0,
    .primary = false,           // 这是输入流
    .match_ts = true,
    .use_sec_ts = true,
    .redirect_drop = true,      // 队列满时丢弃数据
    .sub_processor = NULL,
    .sub_processor_config = NULL,
};

Gather工作原理:

  1. Gather client接收输入: - Primary client:输入plb,等待其他client数据 - 非primary client:输入plb缓存到队列,输出NULL

  2. 时间戳对齐: - 根据match_ts配置,检查各输入的时间戳 - 如果use_sec_ts为true,使用ts_sec进行对齐 - 等待所有client数据对齐

  3. 混音处理: - Primary client触发时执行混音 - 将各client数据按比例相加 - 考虑位宽和通道数

  4. 输出数据: - 只有primary gather输出混音结果 - 可选传递给sub_processor进一步处理

注意事项:

  1. gather_id必须一致:所有相关的gather使用相同的gather_id

  2. 必须有一个primary:只有primary gather会输出数据

  3. redirect_drop配置:非primary流建议设置redirect_drop为true

  4. depth配置:影响队列深度,影响对齐等待时间和内存占用

Pipeline Mirror使用

Mirror模块用于数据分发:

配置示例:

wq_pipeline_mirror_config_t demo_mirror_config = {
    .stream_id = STREAM_ID_DEMO,
    .mirror_id = STREAM_ID_MIRROR,
    .depth = 0,               // 使用默认深度4
    .copy_mode = false,        // 只clone plb配置,不复制数据
    .redirect_mode = true,       // 启用丢弃模式
    .bypass_id_map = BIT(STREAM_ID_BYPASS),  // 旁路stream需要丢弃数据
};

Mirror工作原理:

  1. 每个mirror procedure是一个mirror client

  2. Mirror processor维护tx_list,存储每个client的待发送数据

  3. 当有client数据输入时: - 如果copy_mode为false,只clone plb配置 - 如果copy_mode为true,复制整个plb数据

  4. 根据mirror_id,数据分发到所有关联的client

  5. 检查tx_list深度,超过depth时打印"process too slow"

性能考虑:

  1. copy_mode选择: - false:高效,只复制plb头,适用于只读场景 - true:完整复制,适用于需要修改plb的场景

  2. depth配置:影响tx_list最大深度,默认为4 - 深度太小:容易超限,影响处理速度 - 深度太大:占用更多内存

  3. redirect_mode:用于数据流控制,可动态丢弃某些stream的数据

常见问题及解决方案

问题1:Stream创建失败

现象:wq_pipeline_stream_control`返回`WQ_RET_INVAL`或`WQ_RET_NOMEM

可能原因: 1. stream_id超出范围(>= CONFIG_PIPELINE_STREAM_MAX_NUM) 2. runner->config为NULL 3. stream已存在(重复创建) 4. 内存不足(procedure创建失败) 5. scratch memory分配失败

解决方案:

检查stream_id范围

#define STREAM_ID_DEMO 0  // 确保小于CONFIG_PIPELINE_STREAM_MAX_NUM

检查runner配置

demo_runner.config = &demo_task_cfg;  // 必须非NULL

检查stream是否已存在

// 创建前检查
if (wq_pipeline_stream_is_created(STREAM_ID_DEMO)) {
    printf("Stream already exists\n");
    return;
}

增加系统内存:检查heap大小,调整内存配置 减小scratch_size需求:优化processor的scratch使用

问题2:Stream运行卡住

现象: - 调用`wq_pipeline_stream_trigger()`后stream没有响应 - Stream处于挂起状态,不处理数据

可能原因: 1. Stream未创建成功 2. Stream中没有待处理的plb 3. Procedure陷入死循环 4. Scratch memory损坏导致processor异常

解决方案:

验证stream创建状态

WQ_RET ret = wq_pipeline_stream_trigger(STREAM_ID_DEMO);
if (ret == WQ_RET_NOT_EXIST) {
    printf("Stream not exist\n");
    return;
}

检查plb输入

// 确保有plb输入到stream
wq_plb_t *input_plb = wq_plb_alloc(len, 0);
if (!input_plb) {
    return;
}
// 将plb添加到stream的plb_list
// 可以通过特定的processor实现plb注入

启用调试日志

// 发送DEBUG_PROCEDURE命令
command.type = WQ_PIPELINE_STREAM_COMMAND_DEBUG_PROCEDURE;
command.processor = &problematic_processor;
wq_pipeline_stream_control(&command);

检查processor实现 - 确保run()函数有返回路径 - 避免无限循环 - 检查条件判断是否正确

问题3:数据帧丢失

现象: - 某些processor的输入数据帧丢失 - 输出数据不连续

可能原因: 1. PLB引用计数错误 2. Processor输出多个plb时处理不当 3. Stream挂起的plb没有正确恢复 4. Mirror或Gather配置错误

解决方案:

正确管理PLB引用

static WQ_RET demo_processor_run(void *handler, struct wq_plb *input_plb,
                                       struct wq_plb **output_plb)
{
    // 增加引用计数
    wq_plb_get(input_plb);

    // 处理数据
    *output_plb = wq_plb_alloc(out_len, 0);

    // 传递引用给输出
    (*output_plb)->users = input_plb->users;

    return WQ_RET_OK;
}

正确处理多输出场景

static WQ_RET multi_output_processor_run(void *handler, struct wq_plb *input_plb,
                                          struct wq_plb **output_plb)
{
    // 假设输出3个plb
    wq_plb_t *out1, *out2, *out3;

    // 分配输出plb
    out1 = wq_plb_alloc(len1, 0);
    out2 = wq_plb_alloc(len2, 0);
    out3 = wq_plb_alloc(len3, 0);

    // 设置第一个plb作为直接输出
    *output_plb = out1;

    // 挂起其他plb(stream会管理)
    out2->procedure = next_procedure;
    out3->procedure = next_procedure;

    return WQ_RET_OK;
}

检查Mirror/Gather配置 - 确保mirror_id和gather_id一致性 - 检查primary设置是否正确 - 验证depth配置是否足够

问题4:内存泄漏

现象: - 系统运行一段时间后内存持续增长 - Heap内存不足

可能原因: 1. PLB未正确释放 2. Procedure handler未正确清理 3. Scratch memory未释放 4. UPDATE命令的动态参数未释放

解决方案:

确保PLB释放

// 在processor中确保释放输入plb
static WQ_RET demo_processor_run(void *handler, struct wq_plb *input_plb,
                                       struct wq_plb **output_plb)
{
    *output_plb = wq_plb_alloc(out_len, 0);

    // 减少输入plb的引用
    wq_plb_free(input_plb);  // 引用计数减1

    return WQ_RET_OK;
}

实现remove接口

static void demo_processor_remove(void *handler)
{
    demo_handler_t *h = (demo_handler_t *)handler;

    // 释放handler内部分配的资源
    if (h->internal_buf) {
        wq_heap_caps_free(h->internal_buf);
    }

    // 释放handler
    wq_heap_caps_free(h);
}

使用callback释放UPDATE参数

static void demo_update_callback(void *arg)
{
    // 释放UPDATE命令的参数
    uint32_t *param = (uint32_t *)arg;
    wq_heap_caps_free(param);
}

检查Stream销毁

// 确保调用DESTORY命令
command.type = WQ_PIPELINE_STREAM_COMMAND_DESTROY;
command.stream_id = STREAM_ID_DEMO;
command.callback = destroy_callback;
wq_pipeline_stream_control(&command);

问题5:性能问题

现象: - "process too slow"日志频繁打印 - 音频卡顿或延迟过大 - CPU使用率过高

可能原因: 1. Processor执行时间过长 2. PLB拷贝开销过大 3. Mirror/Gather深度配置不合理 4. 任务优先级冲突

解决方案:

优化Processor性能

// 使用DMA减少CPU拷贝
// 优化算法复杂度
// 使用查表替代计算
// 减少内存分配次数

启用copy_mode优化

wq_pipeline_mirror_config_t mirror_config = {
    .copy_mode = false,  // 只clone配置,不复制数据
    // ...
};

调整Mirror/Gather深度

wq_pipeline_mirror_config_t mirror_config = {
    .depth = 8,  // 增加深度,减少"process too slow"
    // ...
};

合理设置任务优先级

wq_pipeline_stream_task_cfg_t task_cfg = {
    .prio = 7,  // 音频通常为中等优先级
    // ...
};

使用Scratch Memory

// 启用scratch memory减少分配
#define CONFIG_SCRATCH_MEM_ENABLE

问题6:时间戳不对齐

现象: - Gather混音效果异常 - 音频卡顿或延迟不均匀

可能原因: 1. match_ts配置错误 2. use_sec_ts配置与实际情况不符 3. 输入数据时间戳不连续

解决方案:

检查Gather配置

wq_pipeline_gather_config_t gather_config = {
    .match_ts = true,       // 确保启用时间戳对齐
    .use_sec_ts = false,     // 根据实际情况选择主或辅时间戳
    // ...
};

验证输入时间戳

// 检查输入plb的时间戳
printf("ts: %u, ts_sec: %u\n", input_plb->ts, input_plb->ts_sec);

检查Gather client数量 - 确保所有相关client都加入了gather - 检查是否有client丢失数据

问题7:多路数据流冲突

现象: - 多个stream同时运行时数据混乱 - 不同stream的数据互相干扰

可能原因: 1. Stream ID配置冲突 2. Mirror ID或Gather ID冲突 3. Procedure共享了不当的handler

解决方案:

确保Stream ID唯一

#define STREAM_ID_DEMO_1  0
#define STREAM_ID_DEMO_2  1
// 确保每个stream使用不同的ID

区分Mirror/Gather ID

// 不同stream使用不同的mirror_id和gather_id
wq_pipeline_mirror_config_t mirror1_config = {
    .mirror_id = 0,
    // ...
};

wq_pipeline_mirror_config_t mirror2_config = {
    .mirror_id = 1,  // 不同的mirror_id
    // ...
};

检查Processor实现 - 确保每个stream的probe创建独立的handler - 避免不同stream共享同一handler

问题8:PLB格式错误

现象: - Processor无法正确解析数据 - 输出音频异常

可能原因: 1. PLB format、channel、bit_width设置错误 2. 数据长度计算错误 3. 通道配置与实际数据不匹配

解决方案:

正确设置PLB属性

static WQ_RET demo_processor_run(void *handler, struct wq_plb *input_plb,
                                       struct wq_plb **output_plb)
{
    // 继承输入plb的主要属性
    *output_plb = wq_plb_alloc(out_len, 0);
    (*output_plb)->format = input_plb->format;
    (*output_plb)->channel = input_plb->channel;
    (*output_plb)->bit_width = input_plb->bit_width;
    (*output_plb)->sample_rate = input_plb->sample_rate;

    // 设置数据长度
    (*output_plb)->len = input_plb->len;

    return WQ_RET_OK;
}

计算正确的数据长度

// 计算输出长度(字节)
uint32_t out_len_bytes = out_samples * bit_width / 8 * channels;

// 设置plb长度
output_plb->len = out_len_bytes;

验证通道配置

// 检查输入plb的通道位图
if (input_plb->channel & WQ_PIPELINE_CHANNEL_LEFT) {
    // 处理左通道
}
if (input_plb->channel & WQ_PIPELINE_CHANNEL_RIGHT) {
    // 处理右通道
}

问题9:Pipeline shaper异常

现象: - shaper_run里wq_plb_get_channels(in) != shaper->channel_num触发assert

可能原因: 1. shaper配置的channel num和实际输入plb的channel num不一致

解决方案:

确保shaper配置的channel num和数据流在运行过程中输入plb的channel num一致

问题10:Pipeline gather异常

现象: - gather_stereo_run里temp != NULL && in->len != temp->len触发assert

可能原因: 1. 需要gather到一起的两个数据长度不一致

解决方案:

确保需要gather到一起的两个数据长度一致,可在某个gather_processor前增加shaper_processor,将shaper_processor的输出长度设置为和另一个数据流长度一致

#define STREAM_SCHEDULE_DEMO_STREAM_1                                         \
    WQ_PIPELINE_STREAM_SCHEDULE_START                                         \
    WQ_PIPELINE_STREAM_SCHEDULE_STEP(processor_A, &A_config)                  \
    WQ_PIPELINE_STREAM_SCHEDULE_STEP(shaper_processor, &shaper_config)        \
    WQ_PIPELINE_STREAM_SCHEDULE_STEP(gather_processor, &gather_config)        \
    WQ_PIPELINE_STREAM_SCHEDULE_STEP(processor_B, &B_config)                  \
    WQ_PIPELINE_STREAM_SCHEDULE_END

调试技巧

启用Processor调试信息

Pipeline支持对单个procedure启用调试信息:

wq_pipeline_stream_command_t command;
memset(&command, 0, sizeof(wq_pipeline_stream_command_t));

// 启用procedure调试日志
command.type = WQ_PIPELINE_STREAM_COMMAND_DEBUG_PROCEDURE;
command.stream_id = STREAM_ID_DEMO;
command.processor = &demo_processor;

wq_pipeline_stream_control(&command);

启用后会打印procedure的输入输出信息,包括plb地址、长度、格式等。

Dump PLB数据

可以dump procedure的输入或输出plb数据:

// Dump procedure输入plb
command.type = WQ_PIPELINE_STREAM_COMMAND_DUMP_PROCEDURE_INPUT;
command.stream_id = STREAM_ID_DEMO;
command.processor = &demo_processor;
wq_pipeline_stream_control(&command);

// Dump procedure输出plb
command.type = WQ_PIPELINE_STREAM_COMMAND_DUMP_PROCEDURE_OUTPUT;
command.stream_id = STREAM_ID_DEMO;
command.processor = &demo_processor;
wq_pipeline_stream_control(&command);

运行时状态查询

可以查询stream的运行状态:

wq_pipeline_stream_command_t command;
memset(&command, 0, sizeof(wq_pipeline_stream_command_t));

command.type = WQ_PIPELINE_STREAM_COMMAND_QUERY;
command.stream_id = STREAM_ID_DEMO;

wq_pipeline_stream_control(&command);

自定义PLB释放回调

可以为plb设置释放回调,跟踪PLB生命周期:

// PLB释放回调
void my_plb_free_callback(struct wq_plb *plb, void *param)
{
    printf("PLB %p freed, size: %u\n", plb, plb->len);
}

// 使用PLB时设置回调
wq_plb_t *plb = wq_plb_alloc(size, 0);
plb->free_cb = my_plb_free_callback;
plb->free_cb_param = NULL;

性能优化建议

  1. 减少PLB拷贝 - 尽可能使用PLB直接传递,避免不必要的数据复制 - 使用copy_mode=false的mirror配置 - 考虑使用PLB共享机制

  2. 合理使用Scratch Memory - 启用CONFIG_SCRATCH_MEM_ENABLE - 将频繁分配的临时数据放到scratch memory - 避免在run()中频繁分配内存

  3. 优化Processor算法 - 使用查表替代复杂计算 - 使用SIMD指令加速(如果平台支持) - 减少循环次数

  4. 合理配置任务优先级 - 音频stream通常使用中高优先级(5-8) - 避免与实时任务冲突 - 考虑使用预设任务减少任务切换开销

  5. 控制数据帧大小 - 选择适当的数据帧长度平衡延迟和处理效率 - 过大的帧增加延迟,过小增加调度开销 - 典型值:7.5ms、10ms、15ms

  6. 批量处理 - 在条件允许时,批量处理多个plb - 减少循环次数 - 提高cache命中率

最佳实践

  1. Processor设计 - 保持职责单一,每个processor只做一件事 - 使用标准的query/probe/remove/update/run接口 - 正确返回错误码,避免assert - 充分利用scratch memory

  2. Stream设计 - 将相关processor组织到同一stream - 合理安排processor顺序,减少数据转换 - 使用mirror和gather实现复杂拓扑 - 为stream设置合理的优先级和栈大小

  3. 错误处理 - 所有接口返回正确的WQ_RET错误码 - 失败时回滚已分配的资源 - 在callback中清理动态分配的参数 - 记录关键错误日志

  4. 资源管理 - 使用PLB引用计数管理生命周期 - 及时释放不再使用的PLB - 避免内存泄漏 - 合理使用scratch memory减少碎片

  5. 文档和注释 - 为processor添加清晰的doxygen注释 - 说明processor的功能、输入输出ㇺ、配置参数 - 记录重要的设计决策 - 提供使用示例

参考示例

examples/pipeline_demo

更多示例代码可参考pipeline_demo项目,其中包含: - SBC/MSBC解码流示例 - Reader/Writer processor实现 - Stream创建和销毁示例 - 动态更新示例

API 介绍

Defines

WQ_PIPELINE_BUFF_DEFAULT_PAD_SIZE

Pad size

WQ_PIPELINE_BUFF_FLAG_ZERO_DATA

Zero data flag of pipeline buff

WQ_PIPELINE_BUFF_FLAG_PB_DATA

PB data flag of pipeline buff

WQ_PIPELINE_BUFF_FLAG_FORCE_STEREO_DATA

Force stereo data flag of pipeline buff

WQ_PIPELINE_BUFF_FLAG_TAIL_DATA

Tail data flag of pipeline buff

WQ_PIPELINE_BUFF_FLAG_MIX_DATA

Mix data flag of pipeline buff

WQ_PIPELINE_BUFF_FLAG_REF_DATA

Mic ref valid flag of pipeline buff

WQ_PIPELINE_BUFF_FLAG_READ_ONLY

Read only data flag of pipeline buff

WQ_PIPELINE_DATA_FORMAT_INVALID

Invalid format of pipeline buff data

WQ_PIPELINE_DATA_FORMAT_PCM

PCM format of pipeline buff data

WQ_PIPELINE_DATA_FORMAT_SBC

SBC format of pipeline buff data

WQ_PIPELINE_DATA_FORMAT_AAC

AAC format of pipeline buff data

WQ_PIPELINE_DATA_FORMAT_LDAC

LDAC format of pipeline buff data

WQ_PIPELINE_DATA_FORMAT_LHDC

LHDC format of pipeline buff data

WQ_PIPELINE_DATA_FORMAT_LC3

LC3 format of pipeline buff data

WQ_PIPELINE_DATA_FORMAT_MSBC

MSBC format of pipeline buff data

WQ_PIPELINE_DATA_FORMAT_CVSD

CVSD format of pipeline buff data

WQ_PIPELINE_DATA_FORMAT_OPUS

OPUS format of pipeline buff data

WQ_PIPELINE_DATA_FORMAT_COMPLEX

COMPLEX format of pipeline buff data

WQ_PIPELINE_DATA_FORMAT_L2HC

L2HC format of pipeline buff data

WQ_PIPELINE_CHANNEL_NUM_MAX

Max channel num

WQ_PIPELINE_CHANNEL_INVALID

Invalid channel num

WQ_PIPELINE_CHANNEL_DECODE_MASK

Invalid channel num

WQ_PIPELINE_CHANNEL_LEFT

Left channel flag

WQ_PIPELINE_CHANNEL_RIGHT

Right channel flag

WQ_PIPELINE_CHANNEL_MIX

Mix channel flag

WQ_PIPELINE_CHANNEL_DUAL

Dual channel flag

WQ_PIPELINE_CHANNEL_SINGLE

Single channel flag

WQ_PIPELINE_CHANNEL_MIC0

Mic0 channel flag

WQ_PIPELINE_CHANNEL_MIC1

Mic1 channel flag

WQ_PIPELINE_CHANNEL_MIC2

Mic2 channel flag

WQ_PIPELINE_CHANNEL_MIC3

Mic3 channel flag

WQ_PIPELINE_CHANNEL_MIC4

Mic4 channel flag

WQ_PIPELINE_CHANNEL_MIC5

Mic5 channel flag

WQ_PIPELINE_CHANNEL_MIC6

Mic6 channel flag

WQ_PIPELINE_CHANNEL_MIC7

Mic7 channel flag

WQ_PIPELINE_CHANNEL_STEREO

Stereo channel flag

WQ_PIPELINE_CHANNEL_LEFTDUAL

Left dual channel flag

WQ_PIPELINE_CHANNEL_RIGHTDUAL

Right dual channel flag

WQ_PIPELINE_CHANNEL_MIXDUAL

Mix dual channel flag

TWS_LEFT_DEV_TYPE

Tws left dev form

TWS_RIGHT_DEV_TYPE

Tws right dev form

AUD_STEREO_DEV_TYPE

Audio stereo dev form

AUD_MIXER_DEV_TYPE

Audio mixer dev form

WQ_PIPELINE_SAMPLE_WIDTH_16BIT

16 bit width of pipeline buff data

WQ_PIPELINE_SAMPLE_WIDTH_32BIT

32 bit width of pipeline buff data

Typedefs

typedef struct wq_plb wq_plb_t
typedef void (*plb_dump_hook)(wq_plb_t *plb)

Typedef for hook function to dump plb buff.

备注

This function is intended to be called for dump plb buff.

Param plb:

The point of plb buff

Functions

static inline void wq_plb_list_init(wq_plb_list_t *list)

Init pipeline buff list head.

参数:

list -- The pipeline buff list head

static inline bool wq_plb_list_is_empty(const wq_plb_list_t *list)

Check if a queue is empty.

参数:

list -- The pipeline buff list head

返回:

True if the list is empty

static inline uint32_t wq_plb_list_get_len(const wq_plb_list_t *list)

Get pipeline buff list length.

参数:

list -- The pipeline buff list head

返回:

Queue len

static inline bool wq_plb_is_list_first(const wq_plb_list_t *list, const wq_plb_t *adb)

Check if an pipeline buff is the first entry in the list.

参数:
  • list -- The pipeline buff list head

  • adb -- Pipeline buff is current packet

返回:

True if the pipeline buff is the first entry

static inline bool wq_plb_is_list_last(const wq_plb_list_t *list, const wq_plb_t *adb)

Check if an pipeline buff is the last entry in the list.

参数:
  • list -- The pipeline buff list head

  • adb -- Pipeline buff is current packet

返回:

True if the pipeline buff is the last entry

static inline wq_plb_t *wq_plb_peek_head(const wq_plb_list_t *list)

Find the first pipeline buff in the list, without dequeue.

参数:

list -- The pipeline buff list head

返回:

First pipeline buff in the list

static inline wq_plb_t *wq_plb_peek_next(wq_plb_t *adb, const wq_plb_list_t *list)

Find the next pipeline buff in the list, without dequeue.

参数:
  • adb -- Pipeline buff is the current packet

  • list -- The pipeline buff list head

返回:

Next pipeline buff in the list

static inline wq_plb_t *wq_plb_peek_tail(const wq_plb_list_t *list)

Find the last pipeline buff in the list, without dequeue.

参数:

list -- The pipeline buff list head

返回:

Last pipeline buff in the list

static inline void wq_plb_insert(wq_plb_t *newst, wq_plb_t *prev, wq_plb_t *next, wq_plb_list_t *list)

Insert new pipeline buff between two old pipeline buffs.

备注

This function must be called atomically

参数:
  • newst -- New pipeline buff to be inserted

  • prev -- Prev pipeline buff

  • next -- Next pipeline buff

  • list -- The pipeline buff list head

static inline void wq_plb_queue_head(wq_plb_list_t *list, wq_plb_t *newst)

Queue new packet in the head of list.

参数:
  • list -- The pipeline buff list head

  • newst -- New pipeline buff to be inserted

static inline void wq_plb_queue_tail(wq_plb_list_t *list, wq_plb_t *newst)

Queue new packet in the tail of list.

参数:
  • list -- The pipeline buff list head

  • newst -- New pipeline buff to be inserted

static inline void wq_plb_list_splice_head(const wq_plb_list_t *list, wq_plb_list_t *head)

Join first list into the head of second list.

参数:
  • list -- The pipeline buff list consumed

  • head -- The pipeline buff list modified

static inline void wq_plb_list_splice_tail(const wq_plb_list_t *list, wq_plb_list_t *head)

Join first list into the tail of second list.

参数:
  • list -- The pipeline buff list consumed

  • head -- The pipeline buff list modified

static inline void wq_plb_unlink(wq_plb_t *adb, wq_plb_list_t *list)

Remove a pipeline buff from list.

备注

This function must be called atomically

参数:
  • adb -- Pipeline buff to be unlinked

  • list -- The pipeline buff list head

static inline wq_plb_t *wq_plb_dequeue_head(wq_plb_list_t *list)

Find & dequeue the first pipeline buff from list.

参数:

list -- The pipeline buff list head

返回:

First pipeline buff in the list

static inline wq_plb_t *wq_plb_dequeue_tail(wq_plb_list_t *list)

Find & dequeue the last pipeline buff from list.

参数:

list -- The pipeline buff list head

返回:

Last pipeline buff in the list

static inline wq_plb_t *wq_plb_dequeue_all(wq_plb_list_t *list)

Dequeue all the pipeline buff from list.

参数:

list -- The pipeline buff list head

返回:

First pipeline buff in the list

static inline void wq_plb_get(wq_plb_t *adb)

Add users for the pipeline buff.

参数:

adb -- The current pipeline buff

static inline uint32_t wq_plb_get_users(wq_plb_t *adb)

Get number of users belonged to pipeline buff.

参数:

adb -- The current pipeline buff

返回:

Number of users belonged to this pipeline buff

static inline bool wq_plb_is_shared(wq_plb_t *adb)

Check if the pipeline buff is shared by users.

参数:

adb -- The current pipeline buff

返回:

True if the pipeline buff is shared

static inline uint32_t wq_plb_get_channels(wq_plb_t *adb)

Get number of data channels in pipeline buff.

参数:

adb -- The current packet

返回:

Number of data channels in pipeline buff

wq_plb_t *wq_plb_alloc_header(void)

Alloc a pipeline buff head without buffer.

返回:

Alloced pipeline buff head

wq_plb_t *wq_plb_alloc(uint32_t size, uint32_t pad)

Alloc a pipeline buff head with buffer.

参数:
  • size -- The request buffer size

  • pad -- The preset pad size before data

返回:

Alloced pipeline buff

wq_plb_t *wq_plb_clone(wq_plb_t *adb)

Alloc a pipeline buff head and point it to the same data in old pipeline buff.

参数:

adb -- The pipeline buff to be cloned

返回:

Pointer of cloned pipeline buff

wq_plb_t *wq_plb_copy(wq_plb_t *adb)

Alloc a pipeline buff and copy data from old pipeline buff to it.

参数:

adb -- The pipeline buff to be copied

返回:

Pointer of copied pipeline buff

void wq_plb_free(wq_plb_t *adb)

Free a pipeline buff.

参数:

adb -- The pipeline buff to be free

void wq_plb_free_list(wq_plb_list_t *list)

Free a pipeline buff list.

参数:

list -- The pipeline buff list to be free

struct wq_plb

Public Members

struct wq_plb *next

Next plb point

struct wq_plb *prev

Prev plb point

uint8_t *buf

Start pointer of data buffer belonged to this pipeline buff NULL for no buffer alloced

uint8_t *end

End pointer of data buffer, NULL for data length should be hold

uint8_t *data

Pointer of data start

uint32_t sp

Sample point in this pipeline buff

uint32_t len

Length (in bytes) of one channel data for multi-channel packet

uint32_t channel_gap

Gap (in bytes) from one channel data to another channel

uint32_t ts

Main path time stamp of pipeline data

uint32_t ts_sec

Tone path time stamp of pipeline data

uint32_t sample_rate

Data sample rate

uint32_t flags

Flags of this pipeline buff

uint8_t format

Format of pipeline buff data, e.g., pcm, aac, msbc

uint8_t channel

Channel bitmap for pipeline buff data

uint8_t dev_form

Audio production form

uint8_t bit_width

Sample bit width for pipeline buff data

uint8_t reserved

Reserved param

uint32_t users

Users of this pipeline buff

void *procedure

Reserved for pipeline pipeline, pointer of pend procedure

void (*free_cb)(struct wq_plb*, void*)

When this pipeline buff is free, this callback function is called

void *free_cb_param

Parameter used by free callback function

uint16_t plb_id

Plb data which is associated to processor id and stream id consist by (process id << 8) | stream id

uint32_t param

User-defined parameter

struct wq_plb_list_t

Public Members

struct wq_plb *next

Next plb struct point

struct wq_plb *prev

Prev plb struct point

uint32_t qlen

Plb list lenght

Defines

WQ_PIPELINE_GATHER_MAX_NUM

Max gather number in system

WQ_PIPELINE_GATHER_MAX_INPUT

Max output stream number for gather processor

struct wq_pipeline_gather_config_t
#include <pipeline_gather.h>

Gather processor handler multi-input data, the data should have same sample bit-width and same sample rate.

Public Members

uint16_t stream_id

Id of stream that gather belonged

uint16_t bit_width

Sample bit width, only support 16bit/32bit

uint16_t depth

Depth of hold stb queue, set 0 to use default value

uint8_t gather_id

Id of used gather

bool primary

True to set this stream as gather primary stream

bool match_ts

True to match ts before run gather operation

bool use_sec_ts

True to use second timestamp instead of main timestamp

bool redirect_drop

True to drop data when rx_list depth is full, just for non-primary stream

wq_pipeline_processor_t *sub_processor

Sub processor called by gather processor

void *sub_processor_config

Sub processor configure

struct wq_pipeline_gather_stereo_config_t

Public Members

bool stereo_flag

True to gather stereo data

bool left_channel

True to set this data as left channel

Defines

WQ_PIPELINE_MIRROR_MAX_NUM

Max mirror number in system

WQ_PIPELINE_MIRROR_MAX_OUTPUT

Max output stream number for mirror processor

struct wq_pipeline_mirror_config_t

Public Members

uint16_t stream_id

Id of stream which called the mirror processor

uint16_t mirror_id

Id of used mirror

uint16_t depth

Depth of hold stb queue, set 0 to use default value

bool copy_mode

True to copy stb to dispatch

bool redirect_mode

True to drop output stb for current procedure after mirror

uint32_t bypass_id_map

Bypass stream id bitmap

bool dummy_trigger

Typedefs

typedef struct pipeline_processor_info wq_pipeline_processor_info_t
typedef struct pipeline_processor_preset wq_pipeline_processor_preset_t
typedef struct pipeline_processor wq_pipeline_processor_t
struct pipeline_processor_info

Public Members

uint32_t mips

Cpu mips of the processor

uint32_t persistent_size

Persistent buffer size of the processor

uint32_t scratch_size

Scratch buffer size of the processor

uint32_t stack_size

Stack size of the processor

void *processor_info

The point customized by processor

struct pipeline_processor_preset

Public Members

void *scratch

Scratch buffer point

uint16_t stream_id

Stream id

wq_pipeline_processor_info_t *info

Processor info point

struct pipeline_processor

Public Members

char *name

Processor name

WQ_RET (*query)(void*, struct pipeline_processor_info*)

Query processor & return processor info

void *(*probe)(void*, struct pipeline_processor_preset*)

Probe processor & return handler

void (*remove)(void*)

Remove processor handler

void (*reset)(void*)

Reset processor handler

WQ_RET (*update)(void*, void*)

Update processor handler with new parameter

WQ_RET (*run)(void*, struct wq_plb*, struct wq_plb**)

Run processor

struct wq_pipeline_shaper_config_t

Public Members

uint16_t size

Pool size in bytes, zero for default value (4 output packet size)

uint16_t out_sp

Output sample point number per packet

uint16_t bit_width

Sample bit width, only support 16bit/32bit

uint16_t channel_num

Channel number of shaper

Defines

CONFIG_PIPELINE_STREAM_MAX_NUM

Max pipeline stream num

CONFIG_PIPELINE_PRESET_TASK_MAX_NUM

Max pipeline preset task num

WQ_PIPELINE_STREAM_SCHEDULE_START

Pipeline stream framework. processor means struct pipeline_processor point, config means config info point for processor init. when processor param is NULL, means the stream schedule is end.

schedule end must be {NULL, NULL}.

WQ_PIPELINE_STREAM_SCHEDULE_STEP(processor, config)
WQ_PIPELINE_STREAM_SCHEDULE_END

Typedefs

typedef struct stream_schedule wq_pipeline_stream_schedule_t

Enums

enum WQ_PIPELINE_STREAM_CTX

Values:

enumerator WQ_PIPELINE_STREAM_CTX_OWN_TASK

Stream has own task, can be triggered

enumerator WQ_PIPELINE_STREAM_CTX_PRESET_TASK

Reserved. Stream belongs to preset task, can be triggered

enumerator WQ_PIPELINE_STREAM_CTX_IRQ_HANDLER

Reserved. Stream work can be triggered by irq handler

enumerator WQ_PIPELINE_STREAM_CTX_TIMER_HANDLER

Reserved. Stream work can be triggered by timer handler

enumerator WQ_PIPELINE_STREAM_CTX_HIGH_SHARE_TASK

Reserved. Stream belongs to high share task, can be triggered

enumerator WQ_PIPELINE_STREAM_CTX_LOW_SHARE_TASK

Reserved. Stream belongs to low share task, can be triggered

enumerator WQ_PIPELINE_STREAM_CTX_CALLED

Stream work can only be called, can not be triggered

enum WQ_PIPELINE_STREAM_COMMAND

Values:

enumerator WQ_PIPELINE_STREAM_COMMAND_INVALID

Invalid command flag

enumerator WQ_PIPELINE_STREAM_COMMAND_CREATE

Command to create a stream

enumerator WQ_PIPELINE_STREAM_COMMAND_DESTROY

Command to destroy a stream

enumerator WQ_PIPELINE_STREAM_COMMAND_PEND

Command to pend a stream

enumerator WQ_PIPELINE_STREAM_COMMAND_RESUME

Command to resume a stream

enumerator WQ_PIPELINE_STREAM_COMMAND_QUERY

Command to query a stream

enumerator WQ_PIPELINE_STREAM_COMMAND_RESET_PROCEDURE

Command to reset a procedure in stream

enumerator WQ_PIPELINE_STREAM_COMMAND_BYPASS_PROCEDURE

Command to bypass a procedure in stream

enumerator WQ_PIPELINE_STREAM_COMMAND_UPDATE_PROCEDURE

Command to update runtime parameter of a procedure in stream

enumerator WQ_PIPELINE_STREAM_COMMAND_DEBUG_PROCEDURE

Command to enable log dump for a procedure in stream

enumerator WQ_PIPELINE_STREAM_COMMAND_DUMP_PROCEDURE_INPUT

Command to enable input plb dump for a procedure in stream

enumerator WQ_PIPELINE_STREAM_COMMAND_DUMP_PROCEDURE_OUTPUT

Command to enable output plb dump for a procedure in stream

enumerator WQ_PIPELINE_STREAM_COMMAND_MAX

Command max flag

Functions

WQ_RET wq_pipeline_stream_control(wq_pipeline_stream_command_t *command)

Send control command to pipeline stream.

参数:

command -- The command to be send to pipeline stream.

返回:

  • WQ_RET_OK success.

  • WQ_RET_INVAL command is invalid.

WQ_RET wq_pipeline_stream_work(uint16_t stream_id)

Run pipeline stream work.

参数:

stream_id -- is stream id.

返回:

  • WQ_RET_OK success.

  • WQ_RET_NOT_EXIST stream is not exist.

WQ_RET wq_pipeline_stream_trigger(uint16_t stream_id)

Trigger pipeline stream to run .

参数:

stream_id -- is stream id.

返回:

  • WQ_RET_OK success.

  • WQ_RET_NOT_EXIST stream is not exist.

struct stream_schedule

Public Members

const wq_pipeline_processor_t *processor

Processor point

void *config

Pipeline stream config info point

struct wq_pipeline_stream_task_cfg_t

Public Members

uint16_t prio

Stream own task priority

uint16_t stack_size

Stream own task stack size

char *name

Stream own task name

struct wq_pipeline_stream_preset_task_t

Public Members

uint16_t preset_task_id

Preset task id

bool preset_task_perpetuate

Preset task perpetuate attribute

wq_pipeline_stream_task_cfg_t task_cfg

Preset task config

struct wq_pipeline_stream_task_handle_t

Public Members

uint32_t ref

Reference for stream task used

void *task_handle

Stream task handle

uint16_t prio

Stream task priority

struct wq_pipeline_stream_runner_t

Public Members

WQ_PIPELINE_STREAM_CTX type

Stream ctx type

void *config

Stream task config point

struct stream_schedule *schedule

Stream schedule

void *scratch

Stream scratch memory point

uint32_t scratch_size

Stream scratch memory size

uint32_t run_times

Stream tigger run times, will decrease one when each run loop

struct wq_pipeline_stream_command_t

Public Members

struct list_head node

Command list node

WQ_PIPELINE_STREAM_COMMAND type

Command type

wq_pipeline_processor_t *processor

Which processor to handle the command

uint16_t procedure_index

Procedure index of stream

uint8_t stream_id

Stream id of the stream to handle the command

bool stream_depend

The command is depend stream

uint32_t init_sample_rate

Init sample rate for create command

void *callback_param

The param for callback handler

void (*callback)(void *callback_param)

Callback handler

void *param

Command param