304 lines
8.2 KiB
C++
304 lines
8.2 KiB
C++
/*
|
|
* Copyright 2008-2010 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.
|
|
*
|
|
* $Id: dtmfsession.cpp 1780 2010-09-01 05:59:32Z achaloyan $
|
|
*/
|
|
|
|
#include "dtmfsession.h"
|
|
#include "dtmfscenario.h"
|
|
#include "mrcp_message.h"
|
|
#include "mrcp_generic_header.h"
|
|
#include "mrcp_recog_header.h"
|
|
#include "mrcp_recog_resource.h"
|
|
#include "mpf_dtmf_generator.h"
|
|
#include "apt_nlsml_doc.h"
|
|
#include "apt_log.h"
|
|
|
|
struct RecogChannel
|
|
{
|
|
/** MRCP control channel */
|
|
mrcp_channel_t* m_pMrcpChannel;
|
|
/** DTMF generator */
|
|
mpf_dtmf_generator_t* m_pDtmfGenerator;
|
|
/** Streaming is in-progress */
|
|
bool m_Streaming;
|
|
};
|
|
|
|
DtmfSession::DtmfSession(const DtmfScenario* pScenario) :
|
|
UmcSession(pScenario),
|
|
m_pRecogChannel(NULL)
|
|
{
|
|
}
|
|
|
|
DtmfSession::~DtmfSession()
|
|
{
|
|
}
|
|
|
|
bool DtmfSession::Start()
|
|
{
|
|
/* create channel and associate all the required data */
|
|
m_pRecogChannel = CreateRecogChannel();
|
|
if(!m_pRecogChannel)
|
|
return false;
|
|
|
|
/* add channel to session (send asynchronous request) */
|
|
if(!AddMrcpChannel(m_pRecogChannel->m_pMrcpChannel))
|
|
{
|
|
delete m_pRecogChannel;
|
|
m_pRecogChannel = NULL;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool DtmfSession::OnSessionTerminate(mrcp_sig_status_code_e status)
|
|
{
|
|
if(m_pRecogChannel)
|
|
{
|
|
if(m_pRecogChannel->m_pDtmfGenerator)
|
|
{
|
|
mpf_dtmf_generator_destroy(m_pRecogChannel->m_pDtmfGenerator);
|
|
m_pRecogChannel->m_pDtmfGenerator = NULL;
|
|
}
|
|
|
|
delete m_pRecogChannel;
|
|
m_pRecogChannel = NULL;
|
|
}
|
|
return UmcSession::OnSessionTerminate(status);
|
|
}
|
|
|
|
static apt_bool_t ReadStream(mpf_audio_stream_t* pStream, mpf_frame_t* pFrame)
|
|
{
|
|
RecogChannel* pRecogChannel = (RecogChannel*) pStream->obj;
|
|
if(pRecogChannel && pRecogChannel->m_Streaming)
|
|
{
|
|
if(pRecogChannel->m_pDtmfGenerator)
|
|
{
|
|
mpf_dtmf_generator_put_frame(pRecogChannel->m_pDtmfGenerator,pFrame);
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
RecogChannel* DtmfSession::CreateRecogChannel()
|
|
{
|
|
mrcp_channel_t* pChannel;
|
|
mpf_termination_t* pTermination;
|
|
mpf_stream_capabilities_t* pCapabilities;
|
|
apr_pool_t* pool = GetSessionPool();
|
|
|
|
/* create channel */
|
|
RecogChannel *pRecogChannel = new RecogChannel;
|
|
pRecogChannel->m_pMrcpChannel = NULL;
|
|
pRecogChannel->m_pDtmfGenerator = NULL;
|
|
pRecogChannel->m_Streaming = false;
|
|
|
|
/* create source stream capabilities */
|
|
pCapabilities = mpf_source_stream_capabilities_create(pool);
|
|
GetScenario()->InitCapabilities(pCapabilities);
|
|
|
|
static const mpf_audio_stream_vtable_t audio_stream_vtable =
|
|
{
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
ReadStream,
|
|
NULL,
|
|
NULL,
|
|
NULL
|
|
};
|
|
|
|
pTermination = CreateAudioTermination(
|
|
&audio_stream_vtable, /* virtual methods table of audio stream */
|
|
pCapabilities, /* capabilities of audio stream */
|
|
pRecogChannel); /* object to associate */
|
|
|
|
pChannel = CreateMrcpChannel(
|
|
MRCP_RECOGNIZER_RESOURCE, /* MRCP resource identifier */
|
|
pTermination, /* media termination, used to terminate audio stream */
|
|
NULL, /* RTP descriptor, used to create RTP termination (NULL by default) */
|
|
pRecogChannel); /* object to associate */
|
|
if(!pChannel)
|
|
{
|
|
delete pRecogChannel;
|
|
return NULL;
|
|
}
|
|
|
|
pRecogChannel->m_pMrcpChannel = pChannel;
|
|
return pRecogChannel;
|
|
}
|
|
|
|
bool DtmfSession::OnChannelAdd(mrcp_channel_t* pMrcpChannel, mrcp_sig_status_code_e status)
|
|
{
|
|
if(!UmcSession::OnChannelAdd(pMrcpChannel,status))
|
|
return false;
|
|
|
|
if(status != MRCP_SIG_STATUS_CODE_SUCCESS)
|
|
{
|
|
/* error case, just terminate the demo */
|
|
return Terminate();
|
|
}
|
|
|
|
RecogChannel* pRecogChannel = (RecogChannel*) mrcp_application_channel_object_get(pMrcpChannel);
|
|
if(pRecogChannel)
|
|
{
|
|
const mpf_audio_stream_t* pStream = mrcp_application_audio_stream_get(pMrcpChannel);
|
|
if(pStream)
|
|
{
|
|
pRecogChannel->m_pDtmfGenerator = mpf_dtmf_generator_create(pStream,GetSessionPool());
|
|
}
|
|
}
|
|
|
|
return StartRecognition(pMrcpChannel);
|
|
}
|
|
|
|
bool DtmfSession::OnMessageReceive(mrcp_channel_t* pMrcpChannel, mrcp_message_t* pMrcpMessage)
|
|
{
|
|
if(!UmcSession::OnMessageReceive(pMrcpChannel,pMrcpMessage))
|
|
return false;
|
|
|
|
const DtmfScenario* pScenario = GetScenario();
|
|
RecogChannel* pRecogChannel = (RecogChannel*) mrcp_application_channel_object_get(pMrcpChannel);
|
|
if(pMrcpMessage->start_line.message_type == MRCP_MESSAGE_TYPE_RESPONSE)
|
|
{
|
|
if(pMrcpMessage->start_line.method_id == RECOGNIZER_RECOGNIZE)
|
|
{
|
|
/* received the response to RECOGNIZE request */
|
|
if(pMrcpMessage->start_line.request_state == MRCP_REQUEST_STATE_INPROGRESS)
|
|
{
|
|
/* start to stream the DTMFs to recognize */
|
|
if(pRecogChannel && pRecogChannel->m_pDtmfGenerator)
|
|
{
|
|
const char* digits = pScenario->GetDigits();
|
|
if(digits)
|
|
{
|
|
mpf_dtmf_generator_enqueue(pRecogChannel->m_pDtmfGenerator,digits);
|
|
pRecogChannel->m_Streaming = true;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* received unexpected response, terminate the session */
|
|
Terminate();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* received unexpected response */
|
|
}
|
|
}
|
|
else if(pMrcpMessage->start_line.message_type == MRCP_MESSAGE_TYPE_EVENT)
|
|
{
|
|
if(pMrcpMessage->start_line.method_id == RECOGNIZER_RECOGNITION_COMPLETE)
|
|
{
|
|
ParseNLSMLResult(pMrcpMessage);
|
|
if(pRecogChannel)
|
|
{
|
|
pRecogChannel->m_Streaming = false;
|
|
}
|
|
Terminate();
|
|
}
|
|
else if(pMrcpMessage->start_line.method_id == RECOGNIZER_START_OF_INPUT)
|
|
{
|
|
/* received start-of-input, do whatever you need here */
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool DtmfSession::StartRecognition(mrcp_channel_t* pMrcpChannel)
|
|
{
|
|
RecogChannel* pRecogChannel = (RecogChannel*) mrcp_application_channel_object_get(pMrcpChannel);
|
|
/* create and send RECOGNIZE request */
|
|
mrcp_message_t* pMrcpMessage = CreateRecognizeRequest(pMrcpChannel);
|
|
if(pMrcpMessage)
|
|
{
|
|
SendMrcpRequest(pRecogChannel->m_pMrcpChannel,pMrcpMessage);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
mrcp_message_t* DtmfSession::CreateRecognizeRequest(mrcp_channel_t* pMrcpChannel)
|
|
{
|
|
mrcp_message_t* pMrcpMessage = CreateMrcpMessage(pMrcpChannel,RECOGNIZER_RECOGNIZE);
|
|
if(!pMrcpMessage)
|
|
return NULL;
|
|
|
|
const DtmfScenario* pScenario = GetScenario();
|
|
|
|
mrcp_generic_header_t* pGenericHeader;
|
|
mrcp_recog_header_t* pRecogHeader;
|
|
|
|
/* get/allocate generic header */
|
|
pGenericHeader = (mrcp_generic_header_t*) mrcp_generic_header_prepare(pMrcpMessage);
|
|
if(pGenericHeader)
|
|
{
|
|
apt_string_assign(&pGenericHeader->content_type,pScenario->GetContentType(),pMrcpMessage->pool);
|
|
mrcp_generic_header_property_add(pMrcpMessage,GENERIC_HEADER_CONTENT_TYPE);
|
|
/* set message body */
|
|
if(pScenario->GetGrammar())
|
|
apt_string_assign(&pMrcpMessage->body,pScenario->GetGrammar(),pMrcpMessage->pool);
|
|
}
|
|
/* get/allocate recognizer header */
|
|
pRecogHeader = (mrcp_recog_header_t*) mrcp_resource_header_prepare(pMrcpMessage);
|
|
if(pRecogHeader)
|
|
{
|
|
/* set recognizer header fields */
|
|
if(pMrcpMessage->start_line.version == MRCP_VERSION_2)
|
|
{
|
|
pRecogHeader->cancel_if_queue = FALSE;
|
|
mrcp_resource_header_property_add(pMrcpMessage,RECOGNIZER_HEADER_CANCEL_IF_QUEUE);
|
|
}
|
|
}
|
|
return pMrcpMessage;
|
|
}
|
|
|
|
bool DtmfSession::ParseNLSMLResult(mrcp_message_t* pMrcpMessage) const
|
|
{
|
|
apr_xml_elem* pInterpret;
|
|
apr_xml_elem* pInstance;
|
|
apr_xml_elem* pInput;
|
|
apr_xml_doc* pDoc = nlsml_doc_load(&pMrcpMessage->body,pMrcpMessage->pool);
|
|
if(!pDoc)
|
|
return false;
|
|
|
|
/* walk through interpreted results */
|
|
pInterpret = nlsml_first_interpret_get(pDoc);
|
|
for(; pInterpret; pInterpret = nlsml_next_interpret_get(pInterpret))
|
|
{
|
|
/* get instance and input */
|
|
nlsml_interpret_results_get(pInterpret,&pInstance,&pInput);
|
|
if(pInstance)
|
|
{
|
|
/* process instance */
|
|
if(pInstance->first_cdata.first)
|
|
{
|
|
apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Interpreted Instance [%s]",pInstance->first_cdata.first->text);
|
|
}
|
|
}
|
|
if(pInput)
|
|
{
|
|
/* process input */
|
|
if(pInput->first_cdata.first)
|
|
{
|
|
apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Interpreted Input [%s]",pInput->first_cdata.first->text);
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|