PerKins Zhu

Le vent se lève,il faut tenter de vivre.

导航

unimrcp_plugin_demo_recog_engine.c 源码解析

Posted on 2026-01-12 16:21  PerKins.Zhu  阅读(5)  评论(0)    收藏  举报
/*
 * Copyright 2008-2015 Arsen Chaloyan
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

 /*
  * 插件实现的强制规则说明
  * 1. 每个插件必须实现一个插件/引擎创建函数,签名和名称必须严格匹配(插件入口点)
  *    函数原型:MRCP_PLUGIN_DECLARE(mrcp_engine_t*) mrcp_plugin_create(apr_pool_t *pool)
  * 2. 每个插件必须声明版本号,通过宏 MRCP_PLUGIN_VERSION_DECLARE 实现
  * 3. 接收到的每个请求必须且只能返回一个响应
  * 4. MRCP 引擎通道的方法(回调函数)禁止阻塞
  *    (异步响应可在其他线程上下文发送)
  * 5. MPF 引擎流的方法(回调函数)禁止阻塞
  */

#include "mrcp_recog_engine.h"    // MRCP识别引擎核心头文件
#include "mpf_activity_detector.h"// 媒体处理框架(Media Processing Framework)的活动检测器
#include "apt_consumer_task.h"    // 消费者任务(异步消息处理)
#include "apt_log.h"              // 日志工具

#define RECOG_ENGINE_TASK_NAME "Demo Recog Engine"  // 识别引擎任务名称

  // 前置声明:核心结构体
typedef struct demo_recog_engine_t demo_recog_engine_t;   // 演示识别引擎结构体
typedef struct demo_recog_channel_t demo_recog_channel_t; // 演示识别通道结构体
typedef struct demo_recog_msg_t demo_recog_msg_t;         // 演示识别任务消息结构体

/** 识别引擎方法声明(引擎生命周期管理) */
// 销毁引擎
static apt_bool_t demo_recog_engine_destroy(mrcp_engine_t* engine);
// 打开引擎
static apt_bool_t demo_recog_engine_open(mrcp_engine_t* engine);
// 关闭引擎
static apt_bool_t demo_recog_engine_close(mrcp_engine_t* engine);
// 创建引擎通道
static mrcp_engine_channel_t* demo_recog_engine_channel_create(mrcp_engine_t* engine, apr_pool_t* pool);

// 引擎虚函数表(绑定引擎生命周期方法)
static const struct mrcp_engine_method_vtable_t engine_vtable = {
    demo_recog_engine_destroy,   // 销毁引擎
    demo_recog_engine_open,      // 打开引擎
    demo_recog_engine_close,     // 关闭引擎
    demo_recog_engine_channel_create // 创建通道
};


/** 识别通道方法声明(通道生命周期+请求处理) */
// 销毁通道
static apt_bool_t demo_recog_channel_destroy(mrcp_engine_channel_t* channel);
// 打开通道
static apt_bool_t demo_recog_channel_open(mrcp_engine_channel_t* channel);
// 关闭通道
static apt_bool_t demo_recog_channel_close(mrcp_engine_channel_t* channel);
// 处理通道请求(MRCP消息)
static apt_bool_t demo_recog_channel_request_process(mrcp_engine_channel_t* channel, mrcp_message_t* request);

// 通道虚函数表(绑定通道生命周期+请求处理方法)
static const struct mrcp_engine_channel_method_vtable_t channel_vtable = {
    demo_recog_channel_destroy,    // 销毁通道
    demo_recog_channel_open,       // 打开通道
    demo_recog_channel_close,      // 关闭通道
    demo_recog_channel_request_process // 处理请求
};

/** 识别音频流方法声明(媒体流处理) */
// 销毁音频流
static apt_bool_t demo_recog_stream_destroy(mpf_audio_stream_t* stream);
// 打开音频流(关联编码格式)
static apt_bool_t demo_recog_stream_open(mpf_audio_stream_t* stream, mpf_codec_t* codec);
// 关闭音频流
static apt_bool_t demo_recog_stream_close(mpf_audio_stream_t* stream);
// 写入音频帧(核心:处理音频数据)
static apt_bool_t demo_recog_stream_write(mpf_audio_stream_t* stream, const mpf_frame_t* frame);

// 音频流虚函数表(绑定媒体流处理方法)
static const mpf_audio_stream_vtable_t audio_stream_vtable = {
    demo_recog_stream_destroy,  // 销毁流
    NULL,                       // 预留:读音频(识别引擎是接收音频,无需实现)
    NULL,                       // 预留:读事件
    NULL,                       // 预留:控制
    demo_recog_stream_open,     // 打开流
    demo_recog_stream_close,    // 关闭流
    demo_recog_stream_write,    // 写入音频帧
    NULL                        // 预留:控制事件
};

/** 演示识别引擎结构体定义 */
struct demo_recog_engine_t {
    apt_consumer_task_t* task;  // 消费者任务(异步处理消息的核心载体)
};

/** 演示识别通道结构体定义(每个通道对应一个识别会话) */
struct demo_recog_channel_t {
    /** 指向所属引擎的回指指针 */
    demo_recog_engine_t* demo_engine;
    /** 引擎通道基类(Unimrcp核心结构体) */
    mrcp_engine_channel_t* channel;

    /** 正在处理中的识别请求(RECOGNIZE) */
    mrcp_message_t* recog_request;
    /** 待发送的停止响应(STOP请求的响应) */
    mrcp_message_t* stop_response;
    /** 标记是否启动了输入定时器(NO-INPUT超时等) */
    apt_bool_t               timers_started;
    /** 语音活动检测器(VAD:检测静音/语音/无输入) */
    mpf_activity_detector_t* detector;
    /** 音频输出文件句柄(保存识别过程中的音频数据) */
    FILE* audio_out;
};

/** 任务消息类型枚举(异步消息分类) */
typedef enum {
    DEMO_RECOG_MSG_OPEN_CHANNEL,     // 打开通道消息
    DEMO_RECOG_MSG_CLOSE_CHANNEL,    // 关闭通道消息
    DEMO_RECOG_MSG_REQUEST_PROCESS   // 处理请求消息
} demo_recog_msg_type_e;

/** 演示识别任务消息结构体(异步消息载体) */
struct demo_recog_msg_t {
    demo_recog_msg_type_e  type;      // 消息类型(对应上面的枚举)
    mrcp_engine_channel_t* channel;   // 关联的通道
    mrcp_message_t* request;   // 关联的MRCP请求(可选)
};

// 异步消息发送(触发)
static apt_bool_t demo_recog_msg_signal(demo_recog_msg_type_e type, mrcp_engine_channel_t* channel, mrcp_message_t* request);
// 异步消息处理(消费)
static apt_bool_t demo_recog_msg_process(apt_task_t* task, apt_task_msg_t* msg);

/** 声明插件版本(强制要求) */
MRCP_PLUGIN_VERSION_DECLARE

/**
 * 声明插件日志源(使用Unimrcp服务器的日志机制)
 * 需要在logger.xml中配置对应日志源的级别,示例:
 *    <source name="RECOG-PLUGIN" priority="DEBUG" masking="NONE"/>
 */
    MRCP_PLUGIN_LOG_SOURCE_IMPLEMENT(RECOG_PLUGIN, "RECOG-PLUGIN")

    /** 自定义日志标记(简化日志调用) */
#define RECOG_LOG_MARK   APT_LOG_MARK_DECLARE(RECOG_PLUGIN)

/**
 * 插件入口函数:创建演示识别引擎
 * @param pool 内存池(Unimrcp核心内存管理,所有分配需基于此池)
 * @return 创建成功的MRCP引擎对象,失败返回NULL
 */
    MRCP_PLUGIN_DECLARE(mrcp_engine_t*) mrcp_plugin_create(apr_pool_t* pool)
{
    // 分配演示引擎对象内存
    demo_recog_engine_t* demo_engine = apr_palloc(pool, sizeof(demo_recog_engine_t));
    apt_task_t* task;                // 任务基类指针
    apt_task_vtable_t* vtable;       // 任务虚函数表
    apt_task_msg_pool_t* msg_pool;   // 消息池(动态创建异步消息)

    // 创建动态消息池(每个消息大小为demo_recog_msg_t,基于全局内存池)
    msg_pool = apt_task_msg_pool_create_dynamic(sizeof(demo_recog_msg_t), pool);
    // 创建消费者任务(绑定引擎对象、消息池、内存池)
    demo_engine->task = apt_consumer_task_create(demo_engine, msg_pool, pool);
    if (!demo_engine->task) {
        return NULL; // 任务创建失败,引擎创建失败
    }

    // 获取任务基类对象
    task = apt_consumer_task_base_get(demo_engine->task);
    // 设置任务名称(日志/调试用)
    apt_task_name_set(task, RECOG_ENGINE_TASK_NAME);
    // 获取任务虚函数表,绑定消息处理函数
    vtable = apt_task_vtable_get(task);
    if (vtable) {
        vtable->process_msg = demo_recog_msg_process; // 绑定异步消息处理逻辑
    }

    /* 创建引擎基类对象(Unimrcp核心API) */
    return mrcp_engine_create(
        MRCP_RECOGNIZER_RESOURCE,  // MRCP资源类型:识别器(RECOGNIZER)
        demo_engine,               // 关联的自定义引擎对象
        &engine_vtable,            // 引擎方法虚函数表
        pool);                     // 内存池
}

/**
 * 销毁识别引擎(引擎生命周期结束时调用)
 * @param engine MRCP引擎基类对象
 * @return 成功返回TRUE,失败返回FALSE
 */
static apt_bool_t demo_recog_engine_destroy(mrcp_engine_t* engine)
{
    demo_recog_engine_t* demo_engine = engine->obj; // 获取关联的自定义引擎对象
    if (demo_engine->task) {
        // 获取任务基类并销毁
        apt_task_t* task = apt_consumer_task_base_get(demo_engine->task);
        apt_task_destroy(task);
        demo_engine->task = NULL; // 置空防止野指针
    }
    return TRUE;
}

/**
 * 打开识别引擎(引擎启动时调用)
 * @param engine MRCP引擎基类对象
 * @return 成功返回TRUE,失败返回FALSE
 */
static apt_bool_t demo_recog_engine_open(mrcp_engine_t* engine)
{
    demo_recog_engine_t* demo_engine = engine->obj; // 获取关联的自定义引擎对象
    if (demo_engine->task) {
        // 获取任务基类并启动(开始处理异步消息)
        apt_task_t* task = apt_consumer_task_base_get(demo_engine->task);
        apt_task_start(task);
    }
    // 响应引擎打开结果(异步通知Unimrcp核心)
    return mrcp_engine_open_respond(engine, TRUE);
}

/**
 * 关闭识别引擎(引擎停止时调用)
 * @param engine MRCP引擎基类对象
 * @return 成功返回TRUE,失败返回FALSE
 */
static apt_bool_t demo_recog_engine_close(mrcp_engine_t* engine)
{
    demo_recog_engine_t* demo_engine = engine->obj; // 获取关联的自定义引擎对象
    if (demo_engine->task) {
        // 获取任务基类并终止(停止处理异步消息,等待任务结束)
        apt_task_t* task = apt_consumer_task_base_get(demo_engine->task);
        apt_task_terminate(task, TRUE);
    }
    // 响应引擎关闭结果(异步通知Unimrcp核心)
    return mrcp_engine_close_respond(engine);
}

/**
 * 创建识别引擎通道(每个会话对应一个通道)
 * @param engine MRCP引擎基类对象
 * @param pool 内存池
 * @return 创建成功的通道对象,失败返回NULL
 */
static mrcp_engine_channel_t* demo_recog_engine_channel_create(mrcp_engine_t* engine, apr_pool_t* pool)
{
    mpf_stream_capabilities_t* capabilities; // 媒体流能力(支持的编码等)
    mpf_termination_t* termination;          // 媒体终结点(关联音频流)

    /* 分配演示识别通道内存 */
    demo_recog_channel_t* recog_channel = apr_palloc(pool, sizeof(demo_recog_channel_t));
    // 初始化通道属性
    recog_channel->demo_engine = engine->obj;  // 绑定所属引擎
    recog_channel->recog_request = NULL;       // 初始化:无正在处理的请求
    recog_channel->stop_response = NULL;       // 初始化:无待发送的停止响应
    // 创建语音活动检测器(VAD)
    recog_channel->detector = mpf_activity_detector_create(pool);
    recog_channel->audio_out = NULL;           // 初始化:无音频输出文件

    /* 创建媒体流能力:仅支持LPCM编码,8k/16k采样率 */
    capabilities = mpf_sink_stream_capabilities_create(pool);
    mpf_codec_capabilities_add(
        &capabilities->codecs,
        MPF_SAMPLE_RATE_8000 | MPF_SAMPLE_RATE_16000,  // 支持的采样率
        "LPCM");                                       // 支持的编码格式

/* 创建媒体终结点(关联音频流处理逻辑) */
    termination = mrcp_engine_audio_termination_create(
        recog_channel,        // 关联的自定义通道对象
        &audio_stream_vtable, // 音频流处理方法表
        capabilities,         // 媒体流能力
        pool);                // 内存池

/* 创建引擎通道基类(Unimrcp核心API) */
    recog_channel->channel = mrcp_engine_channel_create(
        engine,               // 所属引擎
        &channel_vtable,      // 通道方法表
        recog_channel,        // 关联的自定义通道对象
        termination,          // 关联的媒体终结点
        pool);                // 内存池

    return recog_channel->channel; // 返回通道基类对象
}

/**
 * 销毁引擎通道(通道生命周期结束时调用)
 * @param channel 引擎通道基类对象
 * @return 成功返回TRUE,失败返回FALSE
 */
static apt_bool_t demo_recog_channel_destroy(mrcp_engine_channel_t* channel)
{
    /* 本示例无额外销毁逻辑,直接返回成功 */
    return TRUE;
}

/**
 * 打开引擎通道(通道启动时调用,需异步响应)
 * @param channel 引擎通道基类对象
 * @return 成功返回TRUE,失败返回FALSE
 */
static apt_bool_t demo_recog_channel_open(mrcp_engine_channel_t* channel)
{
    // 发送异步消息:打开通道(避免阻塞,由任务线程处理)
    return demo_recog_msg_signal(DEMO_RECOG_MSG_OPEN_CHANNEL, channel, NULL);
}

/**
 * 关闭引擎通道(通道停止时调用,需异步响应)
 * @param channel 引擎通道基类对象
 * @return 成功返回TRUE,失败返回FALSE
 */
static apt_bool_t demo_recog_channel_close(mrcp_engine_channel_t* channel)
{
    // 发送异步消息:关闭通道(避免阻塞,由任务线程处理)
    return demo_recog_msg_signal(DEMO_RECOG_MSG_CLOSE_CHANNEL, channel, NULL);
}

/**
 * 处理MRCP通道请求(核心:接收RECOGNIZE/STOP等请求,需异步响应)
 * @param channel 引擎通道基类对象
 * @param request MRCP请求消息
 * @return 成功返回TRUE,失败返回FALSE
 */
static apt_bool_t demo_recog_channel_request_process(mrcp_engine_channel_t* channel, mrcp_message_t* request)
{
    // 发送异步消息:处理请求(避免阻塞,由任务线程处理)
    return demo_recog_msg_signal(DEMO_RECOG_MSG_REQUEST_PROCESS, channel, request);
}

/**
 * 处理RECOGNIZE请求(核心识别逻辑入口)
 * @param channel 引擎通道基类对象
 * @param request RECOGNIZE请求消息
 * @param response 预创建的响应消息
 * @return 成功返回TRUE,失败返回FALSE
 */
static apt_bool_t demo_recog_channel_recognize(mrcp_engine_channel_t* channel, mrcp_message_t* request, mrcp_message_t* response)
{
    mrcp_recog_header_t* recog_header;               // 识别请求头
    demo_recog_channel_t* recog_channel = channel->method_obj; // 自定义通道对象
    // 获取音频流编码描述(采样率/编码等)
    const mpf_codec_descriptor_t* descriptor = mrcp_engine_sink_stream_codec_get(channel);

    // 校验:编码描述获取失败
    if (!descriptor) {
        apt_log(RECOG_LOG_MARK, APT_PRIO_WARNING, "获取编码描述失败 " APT_SIDRES_FMT, MRCP_MESSAGE_SIDRES(request));
        response->start_line.status_code = MRCP_STATUS_CODE_METHOD_FAILED; // 设置失败状态码
        return FALSE;
    }

    // 初始化:启动输入定时器
    recog_channel->timers_started = TRUE;

    /* 解析识别请求头,配置VAD参数 */
    recog_header = mrcp_resource_header_get(request); // 获取识别头
    if (recog_header) {
        // 解析:是否启动输入定时器
        if (mrcp_resource_header_property_check(request, RECOGNIZER_HEADER_START_INPUT_TIMERS) == TRUE) {
            recog_channel->timers_started = recog_header->start_input_timers;
        }
        // 解析:NO-INPUT超时时间(无输入超时)
        if (mrcp_resource_header_property_check(request, RECOGNIZER_HEADER_NO_INPUT_TIMEOUT) == TRUE) {
            mpf_activity_detector_noinput_timeout_set(recog_channel->detector, recog_header->no_input_timeout);
        }
        // 解析:语音完成超时时间(静音超时)
        if (mrcp_resource_header_property_check(request, RECOGNIZER_HEADER_SPEECH_COMPLETE_TIMEOUT) == TRUE) {
            mpf_activity_detector_silence_timeout_set(recog_channel->detector, recog_header->speech_complete_timeout);
        }
    }

    /* 创建音频输出文件(保存识别过程中的音频数据) */
    if (!recog_channel->audio_out) {
        const apt_dir_layout_t* dir_layout = channel->engine->dir_layout; // 目录布局(Unimrcp配置)
        // 生成文件名:utter-采样率kHz-会话ID.pcm
        char* file_name = apr_psprintf(channel->pool, "utter-%dkHz-%s.pcm",
            descriptor->sampling_rate / 1000,
            request->channel_id.session_id.buf);
        // 获取文件完整路径(基于var目录)
        char* file_path = apt_vardir_filepath_get(dir_layout, file_name, channel->pool);
        if (file_path) {
            apt_log(RECOG_LOG_MARK, APT_PRIO_INFO, "打开音频输出文件 [%s] 用于写入", file_path);
            recog_channel->audio_out = fopen(file_path, "wb"); // 打开文件(二进制写入)
            if (!recog_channel->audio_out) {
                apt_log(RECOG_LOG_MARK, APT_PRIO_WARNING, "打开音频输出文件失败 [%s]", file_path);
            }
        }
    }

    /* 发送RECOGNIZE异步响应(状态:处理中) */
    response->start_line.request_state = MRCP_REQUEST_STATE_INPROGRESS; // 请求状态:处理中
    mrcp_engine_channel_message_send(channel, response); // 发送响应
    recog_channel->recog_request = request; // 记录当前处理的识别请求
    return TRUE;
}

/**
 * 处理STOP请求(停止当前识别)
 * @param channel 引擎通道基类对象
 * @param request STOP请求消息
 * @param response 预创建的响应消息
 * @return 成功返回TRUE,失败返回FALSE
 */
static apt_bool_t demo_recog_channel_stop(mrcp_engine_channel_t* channel, mrcp_message_t* request, mrcp_message_t* response)
{
    demo_recog_channel_t* recog_channel = channel->method_obj; // 自定义通道对象
    /* 暂存STOP响应:确保无后续音频活动后再发送(避免竞态) */
    recog_channel->stop_response = response;
    return TRUE;
}

/**
 * 处理START-INPUT-TIMERS请求(手动启动输入定时器)
 * @param channel 引擎通道基类对象
 * @param request START-INPUT-TIMERS请求消息
 * @param response 预创建的响应消息
 * @return 成功返回TRUE,失败返回FALSE
 */
static apt_bool_t demo_recog_channel_timers_start(mrcp_engine_channel_t* channel, mrcp_message_t* request, mrcp_message_t* response)
{
    demo_recog_channel_t* recog_channel = channel->method_obj; // 自定义通道对象
    recog_channel->timers_started = TRUE; // 标记:启动输入定时器
    // 发送响应(同步,无异步逻辑)
    return mrcp_engine_channel_message_send(channel, response);
}

/**
 * 分发MRCP请求(路由到具体的请求处理函数)
 * @param channel 引擎通道基类对象
 * @param request MRCP请求消息
 * @return 成功返回TRUE,失败返回FALSE
 */
static apt_bool_t demo_recog_channel_request_dispatch(mrcp_engine_channel_t* channel, mrcp_message_t* request)
{
    apt_bool_t processed = FALSE; // 标记:请求是否被处理
    // 创建响应消息(基于请求,复用内存池)
    mrcp_message_t* response = mrcp_response_create(request, request->pool);

    // 根据MRCP方法ID路由请求
    switch (request->start_line.method_id) {
    case RECOGNIZER_SET_PARAMS:      // 设置参数(本示例未实现)
        break;
    case RECOGNIZER_GET_PARAMS:      // 获取参数(本示例未实现)
        break;
    case RECOGNIZER_DEFINE_GRAMMAR:  // 定义语法(本示例未实现)
        break;
    case RECOGNIZER_RECOGNIZE:       // 核心:识别请求
        processed = demo_recog_channel_recognize(channel, request, response);
        break;
    case RECOGNIZER_GET_RESULT:      // 获取结果(本示例未实现,由RECOGNITION-COMPLETE事件返回)
        break;
    case RECOGNIZER_START_INPUT_TIMERS: // 启动输入定时器
        processed = demo_recog_channel_timers_start(channel, request, response);
        break;
    case RECOGNIZER_STOP:            // 停止识别
        processed = demo_recog_channel_stop(channel, request, response);
        break;
    default:                         // 未知方法
        break;
    }

    // 未处理的请求:发送默认响应(状态:成功)
    if (processed == FALSE) {
        mrcp_engine_channel_message_send(channel, response);
    }
    return TRUE;
}

/**
 * 销毁音频流(媒体流生命周期结束时调用)
 * @param stream 音频流对象
 * @return 成功返回TRUE,失败返回FALSE
 */
static apt_bool_t demo_recog_stream_destroy(mpf_audio_stream_t* stream)
{
    /* 本示例无额外销毁逻辑,直接返回成功 */
    return TRUE;
}

/**
 * 打开音频流(关联编码格式,媒体流启动时调用)
 * @param stream 音频流对象
 * @param codec 编码格式
 * @return 成功返回TRUE,失败返回FALSE
 */
static apt_bool_t demo_recog_stream_open(mpf_audio_stream_t* stream, mpf_codec_t* codec)
{
    /* 本示例无额外打开逻辑,直接返回成功 */
    return TRUE;
}

/**
 * 关闭音频流(媒体流停止时调用)
 * @param stream 音频流对象
 * @return 成功返回TRUE,失败返回FALSE
 */
static apt_bool_t demo_recog_stream_close(mpf_audio_stream_t* stream)
{
    /* 本示例无额外关闭逻辑,直接返回成功 */
    return TRUE;
}

/**
 * 触发START-OF-INPUT事件(检测到语音开始时)
 * @param recog_channel 自定义识别通道对象
 * @return 成功返回TRUE,失败返回FALSE
 */
static apt_bool_t demo_recog_start_of_input(demo_recog_channel_t* recog_channel)
{
    /* 创建START-OF-INPUT事件消息 */
    mrcp_message_t* message = mrcp_event_create(
        recog_channel->recog_request,  // 关联的识别请求
        RECOGNIZER_START_OF_INPUT,     // 事件类型:输入开始
        recog_channel->recog_request->pool); // 内存池
    if (!message) {
        return FALSE;
    }

    /* 设置事件状态:请求处理中 */
    message->start_line.request_state = MRCP_REQUEST_STATE_INPROGRESS;
    /* 发送异步事件(通知Unimrcp核心) */
    return mrcp_engine_channel_message_send(recog_channel->channel, message);
}

/**
 * 加载演示识别结果(从result.xml读取)
 * @param recog_channel 自定义识别通道对象
 * @param message 要填充结果的事件消息
 * @return 成功返回TRUE,失败返回FALSE
 */
static apt_bool_t demo_recog_result_load(demo_recog_channel_t* recog_channel, mrcp_message_t* message)
{
    FILE* file;
    mrcp_engine_channel_t* channel = recog_channel->channel;
    const apt_dir_layout_t* dir_layout = channel->engine->dir_layout; // 目录布局
    // 获取result.xml路径(基于data目录)
    char* file_path = apt_datadir_filepath_get(dir_layout, "result.xml", message->pool);
    if (!file_path) {
        return FALSE;
    }

    /* 读取演示结果文件 */
    file = fopen(file_path, "r");
    if (file) {
        mrcp_generic_header_t* generic_header;
        char text[1024];          // 临时缓冲区
        apr_size_t size;          // 读取的字节数
        // 读取文件内容
        size = fread(text, 1, sizeof(text), file);
        // 将内容赋值到消息体(复用内存池)
        apt_string_assign_n(&message->body, text, size, message->pool);
        fclose(file); // 关闭文件

        /* 设置消息头:内容类型为NLSML(语音识别结果标准格式) */
        generic_header = mrcp_generic_header_prepare(message);
        if (generic_header) {
            // 设置Content-Type:application/x-nlsml
            apt_string_assign(&generic_header->content_type, "application/x-nlsml", message->pool);
            // 标记:Content-Type头已设置
            mrcp_generic_header_property_add(message, GENERIC_HEADER_CONTENT_TYPE);
        }
    }
    return TRUE;
}

/**
 * 触发RECOGNITION-COMPLETE事件(识别完成)
 * @param recog_channel 自定义识别通道对象
 * @param cause 完成原因(成功/无输入/超时等)
 * @return 成功返回TRUE,失败返回FALSE
 */
static apt_bool_t demo_recog_recognition_complete(demo_recog_channel_t* recog_channel, mrcp_recog_completion_cause_e cause)
{
    mrcp_recog_header_t* recog_header;
    /* 创建RECOGNITION-COMPLETE事件消息 */
    mrcp_message_t* message = mrcp_event_create(
        recog_channel->recog_request,    // 关联的识别请求
        RECOGNIZER_RECOGNITION_COMPLETE, // 事件类型:识别完成
        recog_channel->recog_request->pool); // 内存池
    if (!message) {
        return FALSE;
    }

    /* 设置识别完成原因 */
    recog_header = mrcp_resource_header_prepare(message);
    if (recog_header) {
        recog_header->completion_cause = cause; // 设置完成原因(成功/无输入等)
        // 标记:完成原因头已设置
        mrcp_resource_header_property_add(message, RECOGNIZER_HEADER_COMPLETION_CAUSE);
    }
    /* 设置事件状态:请求完成 */
    message->start_line.request_state = MRCP_REQUEST_STATE_COMPLETE;

    /* 成功识别:加载演示结果 */
    if (cause == RECOGNIZER_COMPLETION_CAUSE_SUCCESS) {
        demo_recog_result_load(recog_channel, message);
    }

    recog_channel->recog_request = NULL; // 清空当前识别请求
    /* 发送异步事件(通知Unimrcp核心) */
    return mrcp_engine_channel_message_send(recog_channel->channel, message);
}

/**
 * 写入音频帧(核心:处理每帧音频数据,检测语音活动)
 * @param stream 音频流对象
 * @param frame 音频帧(包含数据/事件等)
 * @return 成功返回TRUE,失败返回FALSE
 */
static apt_bool_t demo_recog_stream_write(mpf_audio_stream_t* stream, const mpf_frame_t* frame)
{
    demo_recog_channel_t* recog_channel = stream->obj; // 关联的自定义通道对象

    /* 优先处理STOP请求:有待发送的停止响应 */
    if (recog_channel->stop_response) {
        // 发送STOP响应(异步)
        mrcp_engine_channel_message_send(recog_channel->channel, recog_channel->stop_response);
        recog_channel->stop_response = NULL; // 清空待发送响应
        recog_channel->recog_request = NULL; // 清空当前识别请求
        return TRUE;
    }

    /* 处理正在进行的识别请求 */
    if (recog_channel->recog_request) {
        // 处理音频帧,获取VAD事件(语音/静音/无输入)
        mpf_detector_event_e det_event = mpf_activity_detector_process(recog_channel->detector, frame);
        switch (det_event) {
        case MPF_DETECTOR_EVENT_ACTIVITY:
            // 检测到语音活动:打印日志+触发START-OF-INPUT事件
            apt_log(RECOG_LOG_MARK, APT_PRIO_INFO, "检测到语音活动 " APT_SIDRES_FMT,
                MRCP_MESSAGE_SIDRES(recog_channel->recog_request));
            demo_recog_start_of_input(recog_channel);
            break;
        case MPF_DETECTOR_EVENT_INACTIVITY:
            // 检测到静音(语音结束):打印日志+触发识别完成事件(成功)
            apt_log(RECOG_LOG_MARK, APT_PRIO_INFO, "检测到语音结束(静音) " APT_SIDRES_FMT,
                MRCP_MESSAGE_SIDRES(recog_channel->recog_request));
            demo_recog_recognition_complete(recog_channel, RECOGNIZER_COMPLETION_CAUSE_SUCCESS);
            break;
        case MPF_DETECTOR_EVENT_NOINPUT:
            // 检测到无输入超时:打印日志+触发识别完成事件(无输入)
            apt_log(RECOG_LOG_MARK, APT_PRIO_INFO, "检测到无输入超时 " APT_SIDRES_FMT,
                MRCP_MESSAGE_SIDRES(recog_channel->recog_request));
            if (recog_channel->timers_started == TRUE) {
                demo_recog_recognition_complete(recog_channel, RECOGNIZER_COMPLETION_CAUSE_NO_INPUT_TIMEOUT);
            }
            break;
        default:
            // 无事件(正常音频/未触发超时)
            break;
        }

        /* 处理媒体事件帧(如DTMF等,本示例仅打印日志) */
        if (recog_channel->recog_request) {
            if ((frame->type & MEDIA_FRAME_TYPE_EVENT) == MEDIA_FRAME_TYPE_EVENT) {
                if (frame->marker == MPF_MARKER_START_OF_EVENT) {
                    // 事件开始(如DTMF按键开始)
                    apt_log(RECOG_LOG_MARK, APT_PRIO_INFO, "检测到事件开始 " APT_SIDRES_FMT " id:%d",
                        MRCP_MESSAGE_SIDRES(recog_channel->recog_request),
                        frame->event_frame.event_id);
                }
                else if (frame->marker == MPF_MARKER_END_OF_EVENT) {
                    // 事件结束(如DTMF按键结束)
                    apt_log(RECOG_LOG_MARK, APT_PRIO_INFO, "检测到事件结束 " APT_SIDRES_FMT " id:%d 时长:%d ts",
                        MRCP_MESSAGE_SIDRES(recog_channel->recog_request),
                        frame->event_frame.event_id,
                        frame->event_frame.duration);
                }
            }
        }

        /* 写入音频数据到文件(如果开启) */
        if (recog_channel->audio_out) {
            fwrite(frame->codec_frame.buffer, 1, frame->codec_frame.size, recog_channel->audio_out);
        }
    }
    return TRUE;
}

/**
 * 发送异步消息(触发任务处理)
 * @param type 消息类型
 * @param channel 关联的通道
 * @param request 关联的MRCP请求(可选)
 * @return 成功返回TRUE,失败返回FALSE
 */
static apt_bool_t demo_recog_msg_signal(demo_recog_msg_type_e type, mrcp_engine_channel_t* channel, mrcp_message_t* request)
{
    apt_bool_t status = FALSE;
    demo_recog_channel_t* demo_channel = channel->method_obj; // 自定义通道对象
    demo_recog_engine_t* demo_engine = demo_channel->demo_engine; // 所属引擎
    apt_task_t* task = apt_consumer_task_base_get(demo_engine->task); // 任务基类
    // 获取空闲消息(从消息池)
    apt_task_msg_t* msg = apt_task_msg_get(task);

    if (msg) {
        demo_recog_msg_t* demo_msg;
        msg->type = TASK_MSG_USER; // 消息类型:用户自定义
        demo_msg = (demo_recog_msg_t*)msg->data; // 消息数据区

        // 填充消息内容
        demo_msg->type = type;
        demo_msg->channel = channel;
        demo_msg->request = request;
        // 发送消息(触发任务处理)
        status = apt_task_msg_signal(task, msg);
    }
    return status;
}

/**
 * 处理异步消息(任务线程核心逻辑)
 * @param task 任务对象
 * @param msg 任务消息
 * @return 成功返回TRUE,失败返回FALSE
 */
static apt_bool_t demo_recog_msg_process(apt_task_t* task, apt_task_msg_t* msg)
{
    demo_recog_msg_t* demo_msg = (demo_recog_msg_t*)msg->data; // 消息数据区
    switch (demo_msg->type) {
    case DEMO_RECOG_MSG_OPEN_CHANNEL:
        // 处理打开通道消息:发送通道打开响应(异步)
        mrcp_engine_channel_open_respond(demo_msg->channel, TRUE);
        break;
    case DEMO_RECOG_MSG_CLOSE_CHANNEL:
    {
        // 处理关闭通道消息:清理资源+发送通道关闭响应
        demo_recog_channel_t* recog_channel = demo_msg->channel->method_obj;
        // 关闭音频输出文件
        if (recog_channel->audio_out) {
            fclose(recog_channel->audio_out);
            recog_channel->audio_out = NULL;
        }
        // 发送通道关闭响应(异步)
        mrcp_engine_channel_close_respond(demo_msg->channel);
        break;
    }
    case DEMO_RECOG_MSG_REQUEST_PROCESS:
        // 处理MRCP请求消息:分发到具体处理函数
        demo_recog_channel_request_dispatch(demo_msg->channel, demo_msg->request);
        break;
    default:
        // 未知消息类型
        break;
    }
    return TRUE;
}