/* * 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; }
浙公网安备 33010602011771号