在MRCP 开始语音识别后会调用recog_channel_start()函数
static switch_status_t recog_channel_start(speech_channel_t *schannel)
{
switch_status_t status = SWITCH_STATUS_SUCCESS;
switch_hash_index_t *egk;
mrcp_message_t *mrcp_message;
mrcp_recog_header_t *recog_header;
mrcp_generic_header_t *generic_header;
recognizer_data_t *r;
char *start_input_timers;
const char *mime_type;
char *key = NULL;
switch_size_t len;
grammar_t *grammar = NULL;
switch_size_t grammar_uri_count = 0;
switch_size_t grammar_uri_list_len = 0;
char *grammar_uri_list = NULL;
int warned = 0;
switch_mutex_lock(schannel->mutex);
if (schannel->state != SPEECH_CHANNEL_READY) {
status = SWITCH_STATUS_FALSE;
goto done;
}
if (schannel->data == NULL) {
status = SWITCH_STATUS_FALSE;
goto done;
}
r = (recognizer_data_t *) schannel->data;
r->result = NULL;
if (r->result_headers) {
switch_event_destroy(&r->result_headers);
}
r->start_of_input = 0;
/* input timers are started by default unless the start-input-timers=false param is set */
start_input_timers = (char *) switch_core_hash_find(schannel->params, "start-input-timers");
r->timers_started = zstr(start_input_timers) || strcasecmp(start_input_timers, "false");
/* count enabled grammars */
for (egk = switch_core_hash_first(r->enabled_grammars); egk; egk = switch_core_hash_next(&egk)) {
// NOTE: This postponed type check is necessary to allow a non-URI-list grammar to execute alone
if (grammar_uri_count == 1 && grammar->type != GRAMMAR_TYPE_URI)
goto no_grammar_alone;
++grammar_uri_count;
switch_core_hash_this(egk, (void *) &key, NULL, (void *) &grammar);
if (grammar->type != GRAMMAR_TYPE_URI && grammar_uri_count != 1) {
no_grammar_alone:
switch_log_printf(SWITCH_CHANNEL_UUID_LOG(schannel->session_uuid), SWITCH_LOG_ERROR, "(%s) Grammar '%s' can only be used alone (not a URI list)\n", schannel->name, key);
status = SWITCH_STATUS_FALSE;
switch_safe_free(egk);
goto done;
}
len = strlen(grammar->data);
if (!len)
continue;
grammar_uri_list_len += len;
if (grammar->data[len - 1] != '\n')
grammar_uri_list_len += 2;
}
switch (grammar_uri_count) {
case 0:
switch_log_printf(SWITCH_CHANNEL_UUID_LOG(schannel->session_uuid), SWITCH_LOG_ERROR, "(%s) No grammar specified\n", schannel->name);
status = SWITCH_STATUS_FALSE;
goto done;
case 1:
/* grammar should already be the unique grammar */
break;
default:
/* get the enabled grammars list */
grammar_uri_list = switch_core_alloc(schannel->memory_pool, grammar_uri_list_len + 1);
grammar_uri_list_len = 0;
for (egk = switch_core_hash_first(r->enabled_grammars); egk; egk = switch_core_hash_next(&egk)) {
switch_core_hash_this(egk, (void *) &key, NULL, (void *) &grammar);
len = strlen(grammar->data);
if (!len)
continue;
memcpy(&(grammar_uri_list[grammar_uri_list_len]), grammar->data, len);
grammar_uri_list_len += len;
if (grammar_uri_list[grammar_uri_list_len - 1] != '\n')
{
grammar_uri_list_len += 2;
grammar_uri_list[grammar_uri_list_len - 2] = '\r';
grammar_uri_list[grammar_uri_list_len - 1] = '\n';
}
}
grammar_uri_list[grammar_uri_list_len++] = '\0';
grammar = NULL;
}
/* create MRCP message */
mrcp_message = mrcp_application_message_create(schannel->unimrcp_session, schannel->unimrcp_channel, RECOGNIZER_RECOGNIZE);
if (mrcp_message == NULL) {
status = SWITCH_STATUS_FALSE;
goto done;
}
/* allocate generic header */
generic_header = (mrcp_generic_header_t *) mrcp_generic_header_prepare(mrcp_message);
if (generic_header == NULL) {
status = SWITCH_STATUS_FALSE;
goto done;
}
/* set Content-Type */
mime_type = grammar_type_to_mime(grammar ? grammar->type : GRAMMAR_TYPE_URI, schannel->profile);
if (zstr(mime_type)) {
status = SWITCH_STATUS_FALSE;
goto done;
}
apt_string_assign(&generic_header->content_type, mime_type, mrcp_message->pool);
mrcp_generic_header_property_add(mrcp_message, GENERIC_HEADER_CONTENT_TYPE);
/* set Content-ID for inline grammars */
if (grammar && grammar->type != GRAMMAR_TYPE_URI) {
apt_string_assign(&generic_header->content_id, grammar->name, mrcp_message->pool);
mrcp_generic_header_property_add(mrcp_message, GENERIC_HEADER_CONTENT_ID);
}
/* allocate recognizer-specific header */
recog_header = (mrcp_recog_header_t *) mrcp_resource_header_prepare(mrcp_message);
if (recog_header == NULL) {
status = SWITCH_STATUS_FALSE;
goto done;
}
/* set Cancel-If-Queue */
if (mrcp_message->start_line.version == MRCP_VERSION_2) {
recog_header->cancel_if_queue = FALSE;
mrcp_resource_header_property_add(mrcp_message, RECOGNIZER_HEADER_CANCEL_IF_QUEUE);
}
/* set parameters */
recog_channel_set_params(schannel, mrcp_message, generic_header, recog_header);
/* set message body */
apt_string_assign(&mrcp_message->body, grammar ? grammar->data : grammar_uri_list, mrcp_message->pool);
/* Empty audio queue and send RECOGNIZE to MRCP server */
audio_queue_clear(schannel->audio_queue);
if (mrcp_application_message_send(schannel->unimrcp_session, schannel->unimrcp_channel, mrcp_message) == FALSE) {
status = SWITCH_STATUS_FALSE;
goto done;
}
/* wait for IN-PROGRESS */
while (schannel->state == SPEECH_CHANNEL_READY) {
if (switch_thread_cond_timedwait(schannel->cond, schannel->mutex, SPEECH_CHANNEL_TIMEOUT_USEC) == SWITCH_STATUS_TIMEOUT && !warned) {
warned = 1;
switch_log_printf(SWITCH_CHANNEL_UUID_LOG(schannel->session_uuid), SWITCH_LOG_WARNING, "(%s) IN-PROGRESS not received for RECOGNIZE after %d ms.\n", schannel->name, SPEECH_CHANNEL_TIMEOUT_USEC / (1000));
}
}
if (schannel->state != SPEECH_CHANNEL_PROCESSING) {
status = SWITCH_STATUS_FALSE;
goto done;
}
done:
switch_mutex_unlock(schannel->mutex);
return status;
}
这里做了如下几件事情
1.检查grammer的设置情况,然后设置真正的grammer。
2.调用mrcp_application_message_create()创建一个mrcp_message_t结构体。
3.调用recog_channel_set_params()设置MRCP的参数,也就是消息的头部信息,包括系统参数和厂商参数。
4.调用apt_string_assign()设置MRCP的body,从代码可以看到body的值就是grammer的内容。
5.调用mrcp_application_message_send()将数据发送给MRCP的Server。
下面我们重点分析mrcp_application_message_send()的实现
MRCP_DECLARE(apt_bool_t) mrcp_application_message_send(mrcp_session_t *session, mrcp_channel_t *channel, mrcp_message_t *message)
{
if(!session || !channel || !message) {
return FALSE;
}
return mrcp_app_control_task_msg_signal(session,channel,message);
}
所以其实调用mrcp_app_control_task_msg_signal()来处理。
apt_bool_t mrcp_app_control_task_msg_signal(mrcp_session_t *session, mrcp_channel_t *channel, mrcp_message_t *message)
{
mrcp_client_session_t *client_session = (mrcp_client_session_t*)session;
mrcp_application_t *application = client_session->application;
apt_task_t *task = apt_consumer_task_base_get(application->client->task);
apt_task_msg_t *task_msg = apt_task_msg_acquire(application->msg_pool);
if(task_msg) {
mrcp_app_message_t **slot = ((mrcp_app_message_t**)task_msg->data);
mrcp_app_message_t *app_message;
task_msg->type = MRCP_CLIENT_APPLICATION_TASK_MSG;
app_message = mrcp_client_app_control_message_create(session->pool);
app_message->application = client_session->application;
app_message->session = session;
app_message->channel = channel;
app_message->control_message = message;
*slot = app_message;
return apt_task_msg_signal(task,task_msg);
}
return FALSE;
}
这里将数据封装成两个对象:apt_task_t与apt_task_msg_t,(注意这里的apt_task_t是 apt_consumer_task类型对象的base属性),并且设置apt_task_msg_t的type为:MRCP_CLIENT_APPLICATION_TASK_MSG,而他的data指向一个mrcp_app_message_t结构体,这个结构体的message_type为:MRCP_APP_MESSAGE_TYPE_CONTROL。
如此之后就调用apt_task_msg_signal将这个消息任务分发出去。
这个函数的实现我们在前面的两篇文章中已经做了介绍:
freeswitch mrcp 源码分析–数据接收(上)
freeswitch mrcp 源码分析–数据接收(下)
所以,从这里也可以看出,不管是mrcp的消息发送还是接收,都是采用同样的模型:创建一个消息任务,然后加到队列里面,而另一个线程就去处理消费这些消息,并调用对应的函数处理。
与MRCP数据的接收一样,最后会调用mrcp_client_msg_process()这个函数来处理消息任务:
static apt_bool_t mrcp_client_msg_process(apt_task_t *task, apt_task_msg_t *msg)
{
apt_consumer_task_t *consumer_task = apt_task_object_get(task);
mrcp_client_t *client = apt_consumer_task_object_get(consumer_task);
if(!client) {
return FALSE;
}
switch(msg->type) {
case MRCP_CLIENT_SIGNALING_TASK_MSG:
{
const sig_agent_task_msg_data_t *sig_message = (const sig_agent_task_msg_data_t*)msg->data;
switch(msg->sub_type) {
case SIG_AGENT_TASK_MSG_ANSWER:
mrcp_client_session_answer_process(sig_message->session,sig_message->descriptor);
break;
case SIG_AGENT_TASK_MSG_TERMINATE_RESPONSE:
mrcp_client_session_terminate_response_process(sig_message->session);
break;
case SIG_AGENT_TASK_MSG_CONTROL_RESPONSE:
mrcp_client_session_control_response_process(sig_message->session,sig_message->message);
break;
case SIG_AGENT_TASK_MSG_DISCOVER_RESPONSE:
mrcp_client_session_discover_response_process(sig_message->session,sig_message->descriptor);
break;
case SIG_AGENT_TASK_MSG_TERMINATE_EVENT:
mrcp_client_session_terminate_event_process(sig_message->session);
break;
default:
break;
}
break;
}
case MRCP_CLIENT_CONNECTION_TASK_MSG:
{
const connection_agent_task_msg_data_t *data = (const connection_agent_task_msg_data_t*)msg->data;
switch(msg->sub_type) {
case CONNECTION_AGENT_TASK_MSG_ADD_CHANNEL:
mrcp_client_on_channel_add(data->channel,data->descriptor,data->status);
break;
case CONNECTION_AGENT_TASK_MSG_MODIFY_CHANNEL:
mrcp_client_on_channel_modify(data->channel,data->descriptor,data->status);
break;
case CONNECTION_AGENT_TASK_MSG_REMOVE_CHANNEL:
mrcp_client_on_channel_remove(data->channel,data->status);
break;
case CONNECTION_AGENT_TASK_MSG_RECEIVE_MESSAGE:
mrcp_client_on_message_receive(data->channel,data->message);
break;
case CONNECTION_AGENT_TASK_MSG_DISCONNECT:
mrcp_client_on_disconnect(data->channel);
break;
default:
break;
}
break;
}
case MRCP_CLIENT_MEDIA_TASK_MSG:
{
mpf_message_container_t *container = (mpf_message_container_t*) msg->data;
mrcp_client_mpf_message_process(container);
break;
}
case MRCP_CLIENT_APPLICATION_TASK_MSG:
{
mrcp_app_message_t **app_message = (mrcp_app_message_t**) msg->data;
mrcp_client_app_message_process(*app_message);
break;
}
default:
{
apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Unknown Task Message Received [%d;%d]", msg->type,msg->sub_type);
break;
}
}
return TRUE;
}
可以看到这里会依据不同的任务类型,调用不同的函数来处理,这里type为:MRCP_CLIENT_APPLICATION_TASK_MSG,所以会调用mrcp_client_app_message_process()处理。
这个函数里面主要调用:
mrcp_app_request_dispatch()将消息分发给各个对应的函数处理。
static apt_bool_t mrcp_app_request_dispatch(mrcp_client_session_t *session, const mrcp_app_message_t *app_message)
{
if(session->state == SESSION_STATE_TERMINATING) {
/* no more requests are allowed, as session is being terminated!
just return, it is horribly wrong and can crash anytime here */
apt_obj_log(APT_LOG_MARK,APT_PRIO_ERROR,session->base.log_obj,"Inappropriate Application Request "APT_NAMESID_FMT" [%d]",
MRCP_SESSION_NAMESID(session),
app_message->sig_message.command_id);
return FALSE;
}
if(session->disconnected == TRUE) {
/* cancel all the requests besides session termination one */
if(!IS_SESSION_TERMINATE(app_message)) {
apt_obj_log(APT_LOG_MARK,APT_PRIO_WARNING,session->base.log_obj,"Cancel App Request "APT_NAMESID_FMT" [%d]",
MRCP_SESSION_NAMESID(session), app_message->sig_message.command_id);
session->status = MRCP_SIG_STATUS_CODE_CANCEL;
return mrcp_app_failure_message_raise(session);
}
}
if(session->registered == FALSE) {
if(IS_SESSION_TERMINATE(app_message)) {
/* if session is not registered, nothing to terminate, just respond with success */
session->status = MRCP_SIG_STATUS_CODE_SUCCESS;
return mrcp_app_sig_response_raise(session,FALSE);
}
/* select signaling agent */
session->base.signaling_agent = mrcp_sa_factory_agent_select(session->profile->sa_factory);
if(!session->base.signaling_agent) {
apt_obj_log(APT_LOG_MARK,APT_PRIO_WARNING,session->base.log_obj,"Failed to Select Signaling Agent "APT_NAMESID_FMT" [%d]",
MRCP_SESSION_NAMESID(session),
app_message->sig_message.command_id);
session->status = MRCP_SIG_STATUS_CODE_FAILURE;
}
if(session->profile->mrcp_version == MRCP_VERSION_2) {
/* select connection agent */
session->base.connection_agent = mrcp_ca_factory_agent_select(session->profile->ca_factory);
if(!session->base.connection_agent) {
apt_obj_log(APT_LOG_MARK,APT_PRIO_WARNING,session->base.log_obj,"Failed to Select Connection Agent "APT_NAMESID_FMT" [%d]",
MRCP_SESSION_NAMESID(session),
app_message->sig_message.command_id);
session->status = MRCP_SIG_STATUS_CODE_FAILURE;
}
}
if(session->profile->mpf_factory) {
/* select media engine */
session->base.media_engine = mpf_engine_factory_engine_select(session->profile->mpf_factory);
if(!session->base.media_engine) {
apt_obj_log(APT_LOG_MARK,APT_PRIO_WARNING,session->base.log_obj,"Failed to Select Media Engine "APT_NAMESID_FMT" [%d]",
MRCP_SESSION_NAMESID(session),
app_message->sig_message.command_id);
session->status = MRCP_SIG_STATUS_CODE_FAILURE;
}
}
/* set rtp termination factory */
session->base.rtp_factory = session->profile->rtp_termination_factory;
if(session->status == MRCP_SIG_STATUS_CODE_FAILURE) {
/* raise app response in case of failure */
return mrcp_app_failure_message_raise(session);
}
if(session->base.signaling_agent->create_client_session(&session->base,session->profile->signaling_settings) != TRUE) {
/* raise app response */
apt_obj_log(APT_LOG_MARK,APT_PRIO_WARNING,session->base.log_obj,"Failed to Create Session "APT_NAMESID_FMT" [%d]",
MRCP_SESSION_NAMESID(session),
app_message->sig_message.command_id);
session->status = MRCP_SIG_STATUS_CODE_FAILURE;
return mrcp_app_failure_message_raise(session);
}
mrcp_client_session_add(session->application->client,session);
session->registered = TRUE;
}
session->status = MRCP_SIG_STATUS_CODE_SUCCESS;
switch(app_message->message_type) {
case MRCP_APP_MESSAGE_TYPE_SIGNALING:
{
apt_obj_log(APT_LOG_MARK,APT_PRIO_DEBUG,session->base.log_obj,"Dispatch App Request "APT_NAMESID_FMT" [%d]",
MRCP_SESSION_NAMESID(session),
app_message->sig_message.command_id);
switch(app_message->sig_message.command_id) {
case MRCP_SIG_COMMAND_SESSION_UPDATE:
mrcp_client_session_update(session);
break;
case MRCP_SIG_COMMAND_SESSION_TERMINATE:
mrcp_client_session_terminate(session);
break;
case MRCP_SIG_COMMAND_CHANNEL_ADD:
mrcp_client_channel_add(session,app_message->channel);
break;
case MRCP_SIG_COMMAND_CHANNEL_REMOVE:
mrcp_client_channel_modify(session,app_message->channel,FALSE);
break;
case MRCP_SIG_COMMAND_RESOURCE_DISCOVER:
mrcp_client_resource_discover(session);
break;
default:
break;
}
break;
}
case MRCP_APP_MESSAGE_TYPE_CONTROL:
{
mrcp_client_message_send(session,app_message->channel,app_message->control_message);
break;
}
}
return TRUE;
}
前面说了message_type为:MRCP_APP_MESSAGE_TYPE_CONTROL,
所以处理函数是:mrcp_client_message_send()
static apt_bool_t mrcp_client_message_send(mrcp_client_session_t *session, mrcp_channel_t *channel, mrcp_message_t *message)
{
if(!session->base.id.length) {
mrcp_message_t *response = mrcp_response_create(message,message->pool);
response->start_line.status_code = MRCP_STATUS_CODE_METHOD_FAILED;
apt_obj_log(APT_LOG_MARK,APT_PRIO_DEBUG,session->base.log_obj,"Raise App Failure MRCP Response "APT_NAMESID_FMT,
MRCP_SESSION_NAMESID(session));
mrcp_app_control_message_raise(session,channel,response);
return TRUE;
}
message->channel_id.session_id = session->base.id;
message->start_line.request_id = ++session->base.last_request_id;
apt_obj_log(APT_LOG_MARK,APT_PRIO_INFO,session->base.log_obj,"Send MRCP Request "APT_NAMESIDRES_FMT" [%"MRCP_REQUEST_ID_FMT"]",
MRCP_SESSION_NAMESID(session),
channel->resource->name.buf,
message->start_line.request_id);
if(channel->control_channel) {
/* MRCPv2 */
mrcp_client_control_message_send(channel->control_channel,message);
}
else {
/* MRCPv1 */
mrcp_session_control_request(channel->session,message);
}
return TRUE;
}
对于MRCP2,显然调用的是:
mrcp_client_control_message_send()
/** Send MRCPv2 message */
MRCP_DECLARE(apt_bool_t) mrcp_client_control_message_send(mrcp_control_channel_t *channel, mrcp_message_t *message)
{
return mrcp_client_control_message_signal(CONNECTION_TASK_MSG_SEND_MESSAGE,channel->agent,channel,NULL,message);
}
直接调用了:mrcp_client_control_message_signal()
/** Signal task message */
static apt_bool_t mrcp_client_control_message_signal(
connection_task_msg_type_e type,
mrcp_connection_agent_t *agent,
mrcp_control_channel_t *channel,
mrcp_control_descriptor_t *descriptor,
mrcp_message_t *message)
{
apt_task_t *task = apt_poller_task_base_get(agent->task);
apt_task_msg_t *task_msg = apt_task_msg_get(task);
if(task_msg) {
connection_task_msg_t *msg = (connection_task_msg_t*)task_msg->data;
msg->type = type;
msg->agent = agent;
msg->channel = channel;
msg->descriptor = descriptor;
msg->message = message;
apt_task_msg_signal(task,task_msg);
}
return TRUE;
}
看好像又回来了,创建了apt_task_t,apt_task_msg_t两个对象,然后又将消费任务放到队列里面去,但是这里有两点需要 注意的:
1.这次的apt_task_t是:apt_poller_task_base_get(agent->task)的方式获取的(注意这里的apt_task_t是 apt_poller_task类型对象的base属性,与前面的不一样),它是在mrcp_client_connection_agent_create中定义的:
agent->task = apt_poller_task_create(
max_connection_count,
mrcp_client_poller_signal_process,
agent,
msg_pool,
pool);
if(!agent->task) {
return NULL;
}
task = apt_poller_task_base_get(agent->task);
if(task) {
apt_task_name_set(task,id);
}
vtable = apt_poller_task_vtable_get(agent->task);
if(vtable) {
vtable->process_msg = mrcp_client_agent_msg_process;
}
2.这次的消息任务的类型为:CONNECTION_TASK_MSG_SEND_MESSAGE。
所以调用的是:mrcp_client_agent_msg_process(),然后会调用:mrcp_client_agent_messsage_send(),在这个函数里面完成真正的工作,组装待发送的字符串,并调用apr_socket_send()通过socket发送出去。
————————————————
版权声明:本文为CSDN博主「罗自荣」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/luozirong/article/details/79006335