yate/modules/server/mrcpspeech.cpp

362 lines
8.7 KiB
C++

/**
* mrcpspeech.cpp
* This file is part of the YATE Project http://YATE.null.ro
*
* Detector and synthesizer for voice and tones using a MRCP v2 server
*
* Yet Another Telephony Engine - a fully featured software PBX and IVR
* Copyright (C) 2004-2014 Null Team
*
* This software is distributed under multiple licenses;
* see the COPYING file in the main directory for licensing
* information for this specific distribution.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
#include <yatephone.h>
using namespace TelEngine;
namespace { // anonymous
class MrcpConnection : public CallEndpoint
{
public:
inline MrcpConnection(const char* id, const char* original)
: CallEndpoint(id),
m_original(original), m_socket(0)
{ }
virtual ~MrcpConnection();
bool init(Message& msg, const char* target);
bool answered(Message& msg);
private:
String m_original;
Socket* m_socket;
};
class MrcpConsumer : public DataConsumer
{
YCLASS(MrcpConsumer,DataConsumer)
public:
MrcpConsumer(const String& id, const char* target, const char* format = 0);
virtual ~MrcpConsumer();
virtual bool setFormat(const DataFormat& format);
virtual unsigned long Consume(const DataBlock& data, unsigned long tStamp, unsigned long flags);
bool init(Message& msg);
private:
void cleanup();
DataSource* m_source;
MrcpConnection* m_chan;
String m_id;
String m_target;
};
class MrcpModule : public Module
{
public:
MrcpModule();
virtual ~MrcpModule();
virtual void initialize();
virtual bool received(Message& msg, int id);
virtual void statusParams(String& str);
private:
bool m_first;
};
static Mutex s_mutex(false,"MrcpSpeech");
static ObjList s_conns;
static int s_count = 0;
static int s_total = 0;
static MrcpModule plugin;
class AttachHandler : public MessageHandler
{
public:
AttachHandler()
: MessageHandler("chan.attach",100,plugin.name())
{ }
virtual bool received(Message& msg);
};
class RecordHandler : public MessageHandler
{
public:
RecordHandler()
: MessageHandler("chan.record",100,plugin.name())
{ }
virtual bool received(Message& msg);
};
class MrcpRtpHandler : public MessageHandler
{
public:
MrcpRtpHandler()
: MessageHandler("chan.rtp",150,plugin.name())
{ }
virtual bool received(Message& msg);
};
MrcpConsumer::MrcpConsumer(const String& id, const char* target, const char* format)
: DataConsumer(format),
m_source(0), m_chan(0), m_id(id)
{
s_mutex.lock();
s_count++;
s_mutex.unlock();
if (target) {
m_target = target;
m_target >> "mrcp/";
m_target = "sip/" + m_target;
}
Debug(&plugin,DebugAll,"MrcpConsumer::MrcpConsumer('%s','%s','%s') [%p]",
id.c_str(),target,format,this);
}
MrcpConsumer::~MrcpConsumer()
{
Debug(&plugin,DebugAll,"MrcpConsumer::~MrcpConsumer '%s' [%p]",
m_id.c_str(),this);
s_mutex.lock();
s_count--;
s_mutex.unlock();
cleanup();
}
bool MrcpConsumer::init(Message& msg)
{
if (m_chan)
return false;
String id("mrcp/");
s_mutex.lock();
s_total++;
id << s_total;
s_mutex.unlock();
m_source = new DataSource(m_format);
m_chan = new MrcpConnection(id,m_id);
m_chan->setSource(m_source);
if (m_chan->init(msg,m_target))
return true;
Debug(&plugin,DebugWarn,"Failed to start connection '%s' for '%s' [%p]",
id.c_str(),m_id.c_str(),this);
cleanup();
return false;
}
void MrcpConsumer::cleanup()
{
Debug(&plugin,DebugAll,"MrcpConsumer::cleanup() '%s' s=%p c=%p [%p]",
m_id.c_str(),m_source,m_chan,this);
if (m_source) {
m_source->deref();
m_source = 0;
}
if (m_chan) {
m_chan->disconnect();
m_chan->deref();
m_chan = 0;
}
}
bool MrcpConsumer::setFormat(const DataFormat& format)
{
Debug(&plugin,DebugAll,"MrcpConsumer::setFormat('%s') '%s' s=%p c=%p [%p]",
format.c_str(),m_id.c_str(),m_source,m_chan,this);
return m_source && m_source->setFormat(format);
}
unsigned long MrcpConsumer::Consume(const DataBlock& data, unsigned long timeDelta, unsigned long flags)
{
return m_source ? m_source->Forward(data,timeDelta,flags) : 0;
}
MrcpConnection::~MrcpConnection()
{
s_mutex.lock();
s_conns.remove(this,false);
s_mutex.unlock();
if (m_socket) {
m_socket->terminate();
delete m_socket;
m_socket = 0;
}
}
bool MrcpConnection::init(Message& msg, const char* target)
{
if (!target)
return false;
s_mutex.lock();
if (!s_conns.find(this))
s_conns.append(this);
s_mutex.unlock();
Message m("call.execute");
m.addParam("id",id());
m.addParam("callto",target);
m.copyParam(msg,"caller");
m.copyParam(msg,"called");
m.addParam("media",String::boolText(true));
m.addParam("media_application",String::boolText(true));
m.addParam("transport_application","TCP/MRCPv2");
m.addParam("formats_application","1"); // defined by the standard
m.userData(this);
return Engine::dispatch(m);
}
bool MrcpConnection::answered(Message& msg)
{
Debug(&plugin,DebugAll,"MrcpConnection::answered() '%s' [%p]",
id().c_str(),this);
int port = msg.getIntValue("rtp_port_application");
if (port > 0) {
return true;
}
return false;
}
// Attach a tone detector on "chan.attach" as consumer or sniffer
bool AttachHandler::received(Message& msg)
{
String cons(msg.getValue("consumer"));
if (!cons.startsWith("mrcp/"))
cons.clear();
String snif(msg.getValue("sniffer"));
if (!snif.startsWith("mrcp/"))
snif.clear();
if (cons.null() && snif.null())
return false;
CallEndpoint* ch = static_cast<CallEndpoint *>(msg.userObject(YATOM("CallEndpoint")));
if (ch) {
if (cons) {
MrcpConsumer* c = new MrcpConsumer(ch->id(),cons,msg.getValue("format","slin"));
if (c->init(msg))
ch->setConsumer(c);
c->deref();
}
if (snif) {
RefPointer<DataEndpoint> de = ch->setEndpoint();
if (!de)
return false;
// try to reinit sniffer if one already exists
MrcpConsumer* c = static_cast<MrcpConsumer*>(de->getSniffer(snif,true));
if (c)
c->init(msg);
else {
c = new MrcpConsumer(ch->id(),snif,msg.getValue("format","slin"));
if (c->init(msg))
de->addSniffer(c);
}
c->deref();
}
return msg.getBoolValue("single");
}
else
Debug(&plugin,DebugWarn,"Attach request with no call endpoint!");
return false;
}
// Attach a tone detector on "chan.record" - needs just a CallEndpoint
bool RecordHandler::received(Message& msg)
{
String src(msg.getValue("call"));
String id(msg.getValue("id"));
if (!src.startsWith("mrcp/"))
return false;
CallEndpoint* ch = static_cast<CallEndpoint *>(msg.userObject(YATOM("CallEndpoint")));
RefPointer<DataEndpoint> de = static_cast<DataEndpoint *>(msg.userObject(YATOM("DataEndpoint")));
if (ch) {
id = ch->id();
if (!de) {
de = ch->setEndpoint();
if (!de)
return false;
}
}
if (de) {
MrcpConsumer* c = new MrcpConsumer(id,src,msg.getValue("format","slin"));
if (c->init(msg))
de->setCallRecord(c);
c->deref();
return true;
}
else
Debug(&plugin,DebugWarn,"Record request with no call endpoint!");
return false;
}
bool MrcpRtpHandler::received(Message& msg)
{
String trans = msg.getValue("transport");
trans.toUpper();
bool tls = false;
if (trans == "TCP/TLS/MRCPV2")
tls = true;
else if (trans != "TCP/MRCPV2")
return false;
Debug(&plugin,DebugAll,"RTP message received, TLS: %s",String::boolText(tls));
return true;
}
MrcpModule::MrcpModule()
: Module("mrcp","misc"), m_first(true)
{
Output("Loaded module MRCP");
}
MrcpModule::~MrcpModule()
{
Output("Unloading module MRCP");
}
bool MrcpModule::received(Message& msg, int id)
{
if (id == Answered) {
const String* cid = msg.getParam("targetid");
if (!cid)
cid = msg.getParam("peerid");
if (!(cid && cid->startsWith("mrcp/")))
return false;
s_mutex.lock();
RefPointer<MrcpConnection> conn = static_cast<MrcpConnection*>(s_conns[*cid]);
s_mutex.unlock();
return conn && conn->answered(msg);
}
return Module::received(msg,id);
}
void MrcpModule::statusParams(String& str)
{
str.append("count=",",") << s_count;
str.append("total=",",") << s_total;
}
void MrcpModule::initialize()
{
Output("Initializing module MrcpSpeech");
setup();
if (m_first) {
m_first = false;
Engine::install(new AttachHandler);
Engine::install(new RecordHandler);
Engine::install(new MrcpRtpHandler);
installRelay(Answered);
}
}
}; // anonymous namespace
/* vi: set ts=8 sw=4 sts=4 noet: */