From 04d4ea2f2b8e86cf269f55449e7b689ee7474c56 Mon Sep 17 00:00:00 2001 From: paulc Date: Sun, 6 Feb 2005 20:13:21 +0000 Subject: [PATCH] Added a central repository of data formats and moved related classes to a new source file. git-svn-id: http://yate.null.ro/svn/yate/trunk@243 acf43c95-373e-0410-b603-e72c3f656dc1 --- Makefile.in | 12 +- engine/DataBlock.cpp | 429 -------------------------------- engine/DataFormat.cpp | 560 ++++++++++++++++++++++++++++++++++++++++++ modules/gsmcodec.cpp | 10 +- yatephone.h | 98 +++++++- 5 files changed, 663 insertions(+), 446 deletions(-) create mode 100644 engine/DataFormat.cpp diff --git a/Makefile.in b/Makefile.in index c4346dce..ae37ce14 100644 --- a/Makefile.in +++ b/Makefile.in @@ -25,10 +25,9 @@ GENS := yateversn.h LIBS := MAN8 := yate.8 yate-config.8 DOCS := README COPYING ChangeLog -ENGOBJS := TelEngine.o String.o DataBlock.o ObjList.o \ - NamedList.o Configuration.o \ - Message.o Mutex.o Thread.o \ - Plugin.o Engine.o +ENGOBJS := TelEngine.o String.o ObjList.o NamedList.o Configuration.o \ + Message.o Mutex.o Thread.o Plugin.o Engine.o +TELOBJS :=DataBlock.o DataFormat.o OBJS := main.o LIBOBJS := $(ENGOBJS) $(TELOBJS) @@ -223,6 +222,9 @@ Engine.o: @srcdir@/Engine.cpp $(MKDEPS) @srcdir@/telengine.h yateversn.h yatepat DataBlock.o: @srcdir@/DataBlock.cpp $(MKDEPS) @srcdir@/telengine.h @srcdir@/telephony.h $(COMPILE) -c $< +DataFormat.o: @srcdir@/DataFormat.cpp $(MKDEPS) @srcdir@/telengine.h @srcdir@/telephony.h + $(COMPILE) -c $< + Mutex.o: @srcdir@/Mutex.cpp $(MKDEPS) @srcdir@/telengine.h $(COMPILE) @MUTEX_HACK@ -c $< @@ -247,7 +249,7 @@ yate: $(OBJS) libyate.so $(LIBS) libyate.so: libyate.so.@PACKAGE_VERSION@ ln -sf $^ $@ -libyate.so.@PACKAGE_VERSION@: $(ENGOBJS) $(LIBS) +libyate.so.@PACKAGE_VERSION@: $(LIBOBJS) $(LIBS) $(LINK) -shared -o $@ -Wl,--soname=$@ $(LIBTHR) $^ $(LIBAUX) .PHONY: help diff --git a/engine/DataBlock.cpp b/engine/DataBlock.cpp index f3bf6e3c..ec3590ff 100644 --- a/engine/DataBlock.cpp +++ b/engine/DataBlock.cpp @@ -25,48 +25,10 @@ #include #include -namespace TelEngine{ - extern "C" { #include "tables/all.h" } -class ThreadedSourcePrivate : public Thread -{ -public: - ThreadedSourcePrivate(ThreadedSource *source, const char *name) - : Thread(name), m_source(source) { } - -protected: - virtual void run() - { m_source->run(); } - - virtual void cleanup() - { m_source->m_thread = 0; m_source->cleanup(); } - -private: - ThreadedSource *m_source; -}; - -class SimpleTranslator : public DataTranslator -{ -public: - SimpleTranslator(const String &sFormat, const String &dFormat) - : DataTranslator(sFormat,dFormat) { } - virtual void Consume(const DataBlock &data, unsigned long timeDelta) - { - ref(); - if (getTransSource()) { - DataBlock oblock; - if (oblock.convert(data, m_format, getTransSource()->getFormat())) - getTransSource()->Forward(oblock, timeDelta); - } - deref(); - } -}; - -}; - using namespace TelEngine; @@ -301,394 +263,3 @@ bool DataBlock::convert(const DataBlock &src, const String &sFormat, } return true; } - -void DataSource::Forward(const DataBlock &data, unsigned long timeDelta) -{ - Lock lock(m_mutex); - ref(); - ObjList *l = &m_consumers; - for (; l; l=l->next()) { - DataConsumer *c = static_cast(l->get()); - if (c) - c->Consume(data,timeDelta); - } - m_timestamp += timeDelta; - deref(); -} - -bool DataSource::attach(DataConsumer *consumer) -{ - DDebug(DebugInfo,"DataSource [%p] attaching consumer [%p]",this,consumer); - if (!consumer) - return false; - Lock lock(m_mutex); - consumer->ref(); - if (consumer->getConnSource()) - consumer->getConnSource()->detach(consumer); - m_consumers.append(consumer); - consumer->setSource(this); - return true; -} - -bool DataSource::detach(DataConsumer *consumer) -{ - DDebug(DebugInfo,"DataSource [%p] detaching consumer [%p]",this,consumer); - if (!consumer) - return false; - Lock lock(m_mutex); - DataConsumer *temp = static_cast(m_consumers.remove(consumer,false)); - if (temp) { - temp->setSource(0); - temp->deref(); - return true; - } - DDebug(DebugWarn,"DataSource [%p] has no consumer [%p]",this,consumer); - return false; -} - -DataSource::~DataSource() -{ - while (detach(static_cast(m_consumers.get()))) ; -} - -DataEndpoint::~DataEndpoint() -{ - disconnect(true,0); - setSource(); - setConsumer(); -} - -bool DataEndpoint::connect(DataEndpoint *peer) -{ - Debug(DebugInfo,"DataEndpoint peer address is [%p]",peer); - if (!peer) { - disconnect(); - return false; - } - if (peer == m_peer) - return true; - - ref(); - disconnect(); - peer->ref(); - peer->disconnect(); - bool native = (name() == peer->name()) && nativeConnect(peer); - - if (!native) { - DataSource *s = getSource(); - DataConsumer *c = peer->getConsumer(); - if (s && c) - DataTranslator::attachChain(s,c); - - s = peer->getSource(); - c = getConsumer(); - if (s && c) - DataTranslator::attachChain(s,c); - } - - m_peer = peer; - peer->setPeer(this); - connected(); - - return true; -} - -void DataEndpoint::disconnect(bool final, const char *reason) -{ - if (!m_peer) - return; - - DataSource *s = getSource(); - DataConsumer *c = m_peer->getConsumer(); - if (s && c) - DataTranslator::detachChain(s,c); - - s = m_peer->getSource(); - c = getConsumer(); - if (s && c) - DataTranslator::detachChain(s,c); - - DataEndpoint *temp = m_peer; - m_peer = 0; - temp->setPeer(0,reason); - temp->deref(); - disconnected(final,reason); - deref(); -} - -void DataEndpoint::setPeer(DataEndpoint *peer, const char *reason) -{ - m_peer = peer; - if (m_peer) - connected(); - else - disconnected(false,reason); -} - -void DataEndpoint::setSource(DataSource *source) -{ - if (source == m_source) - return; - DataConsumer *consumer = m_peer ? m_peer->getConsumer() : 0; - DataSource *temp = m_source; - if (consumer) - consumer->ref(); - m_source = 0; - if (temp) { - if (consumer) { - DataTranslator::detachChain(temp,consumer); - if (consumer->getConnSource()) - Debug(DebugWarn,"consumer source not cleared in %p",consumer); - } - temp->deref(); - } - if (source) { - source->ref(); - if (consumer) - DataTranslator::attachChain(source,consumer); - } - m_source = source; - if (consumer) - consumer->deref(); -} - -void DataEndpoint::setConsumer(DataConsumer *consumer) -{ - if (consumer == m_consumer) - return; - DataSource *source = m_peer ? m_peer->getSource() : 0; - DataConsumer *temp = m_consumer; - if (consumer) { - consumer->ref(); - if (source) - DataTranslator::attachChain(source,consumer); - } - m_consumer = consumer; - if (temp) { - if (source) - DataTranslator::detachChain(source,temp); - temp->deref(); - } -} - -ThreadedSource::~ThreadedSource() -{ - stop(); -} - -bool ThreadedSource::start(const char *name) -{ - if (!m_thread) { - m_thread = new ThreadedSourcePrivate(this,name); - m_thread->startup(); - } - return m_thread->running(); -} - -void ThreadedSource::stop() -{ - if (m_thread) { - delete m_thread; - m_thread = 0; - } -} - -void ThreadedSource::cleanup() -{ -} - -Thread *ThreadedSource::thread() const -{ - return m_thread; -} - -DataTranslator::DataTranslator(const char *sFormat, const char *dFormat) - : DataConsumer(sFormat) -{ - m_tsource = new DataSource(dFormat); - m_tsource->setTranslator(this); -} - -DataTranslator::DataTranslator(const char *sFormat, DataSource *source) - : DataConsumer(sFormat), m_tsource(source) -{ - m_tsource->setTranslator(this); -} - -DataTranslator::~DataTranslator() -{ - DataSource *temp = m_tsource; - m_tsource = 0; - if (temp) { - temp->setTranslator(0); - temp->deref(); - } -} - -Mutex DataTranslator::s_mutex; -ObjList DataTranslator::s_factories; - -void DataTranslator::install(TranslatorFactory *factory) -{ - s_mutex.lock(); - s_factories.append(factory); - s_mutex.unlock(); -} - -void DataTranslator::uninstall(TranslatorFactory *factory) -{ - s_mutex.lock(); - s_factories.remove(factory,false); - s_mutex.unlock(); -} - -String DataTranslator::srcFormats(const String &dFormat) -{ - String s; - s_mutex.lock(); - ObjList *l = &s_factories; - for (; l; l=l->next()) { - TranslatorFactory *f = static_cast(l->get()); - if (f) { - const TranslatorCaps *caps = f->getCapabilities(); - for (; caps && caps->src.name; caps++) { - if (dFormat == caps->dest.name) { - if (!s.null()) - s << " "; - s << caps->src.name << "/" << caps->cost; - } - } - } - } - s_mutex.unlock(); - return s; -} - -String DataTranslator::destFormats(const String &sFormat) -{ - String s; - s_mutex.lock(); - ObjList *l = &s_factories; - for (; l; l=l->next()) { - TranslatorFactory *f = static_cast(l->get()); - if (f) { - const TranslatorCaps *caps = f->getCapabilities(); - for (; caps && caps->src.name; caps++) { - if (sFormat == caps->src.name) { - if (!s.null()) - s << " "; - s << caps->dest.name << "/" << caps->cost; - } - } - } - } - s_mutex.unlock(); - return s; -} - -int DataTranslator::cost(const String &sFormat, const String &dFormat) -{ - int c = -1; - s_mutex.lock(); - ObjList *l = &s_factories; - for (; l; l=l->next()) { - TranslatorFactory *f = static_cast(l->get()); - if (f) { - const TranslatorCaps *caps = f->getCapabilities(); - for (; caps && caps->src.name; caps++) { - if ((c == -1) || (c > caps->cost)) { - if ((sFormat == caps->src.name) && (dFormat == caps->dest.name)) - c = caps->cost; - } - } - } - } - s_mutex.unlock(); - return c; -} - -DataTranslator *DataTranslator::create(const String &sFormat, const String &dFormat) -{ - if (sFormat == dFormat) { - Debug(DebugInfo,"Not creating identity DataTranslator for \"%s\"",sFormat.c_str()); - return 0; - } - - DataTranslator *trans = 0; - - s_mutex.lock(); - ObjList *l = &s_factories; - for (; l; l=l->next()) { - TranslatorFactory *f = static_cast(l->get()); - if (f) { - trans = f->create(sFormat,dFormat); - if (trans) - break; - } - } - s_mutex.unlock(); - - - if (!trans) { - DataBlock empty,probe; - if (probe.convert(empty,sFormat,dFormat)) - trans = new SimpleTranslator(sFormat,dFormat); - } - - if (trans) - Debug(DebugAll,"Created DataTranslator [%p] for \"%s\" -> \"%s\"", - trans,sFormat.c_str(),dFormat.c_str()); - else { - int level = DebugWarn; - if (sFormat.null() || dFormat.null()) - level = DebugInfo; - Debug(level,"No DataTranslator created for \"%s\" -> \"%s\"", - sFormat.c_str(),dFormat.c_str()); - } - return trans; -} - -bool DataTranslator::attachChain(DataSource *source, DataConsumer *consumer) -{ - if (!source || !consumer) - return false; - - bool retv = false; - if (source->getFormat() == consumer->getFormat()) { - source->attach(consumer); - retv = true; - } - else { - // TODO: try to create a chain of translators, recurse if we have to - DataTranslator *trans = create(source->getFormat(),consumer->getFormat()); - if (trans) { - trans->getTransSource()->attach(consumer); - source->attach(trans); - retv = true; - } - } - NDebug(DebugAll,"DataTranslator::attachChain [%p] \"%s\" -> [%p] \"%s\" %s", - source,source->getFormat().c_str(),consumer,consumer->getFormat().c_str(), - retv ? "succeeded" : "failed"); - return retv; -} - -bool DataTranslator::detachChain(DataSource *source, DataConsumer *consumer) -{ - Debugger debug(DebugAll,"DataTranslator::detachChain","(%p,%p)",source,consumer); - if (!source || !consumer) - return false; - - DataSource *tsource = consumer->getConnSource(); - if (tsource) { - if (source->detach(consumer)) - return true; - DataTranslator *trans = tsource->getTranslator(); - if (trans && detachChain(source,trans)) { - trans->deref(); - return true; - } - Debug(DebugWarn,"DataTranslator failed to detach chain [%p] -> [%p]",source,consumer); - } - return false; -} diff --git a/engine/DataFormat.cpp b/engine/DataFormat.cpp new file mode 100644 index 00000000..417cac6e --- /dev/null +++ b/engine/DataFormat.cpp @@ -0,0 +1,560 @@ +/** + * DataFormat.cpp + * This file is part of the YATE Project http://YATE.null.ro + * + * Yet Another Telephony Engine - a fully featured software PBX and IVR + * Copyright (C) 2004 Null Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "telephony.h" + +#include +#include + +namespace TelEngine { + +class ThreadedSourcePrivate : public Thread +{ +public: + ThreadedSourcePrivate(ThreadedSource *source, const char *name) + : Thread(name), m_source(source) { } + +protected: + virtual void run() + { m_source->run(); } + + virtual void cleanup() + { m_source->m_thread = 0; m_source->cleanup(); } + +private: + ThreadedSource *m_source; +}; + +class SimpleTranslator : public DataTranslator +{ +public: + SimpleTranslator(const String &sFormat, const String &dFormat) + : DataTranslator(sFormat,dFormat) { } + virtual void Consume(const DataBlock &data, unsigned long timeDelta) + { + ref(); + if (getTransSource()) { + DataBlock oblock; + if (oblock.convert(data, m_format, getTransSource()->getFormat())) { + if (!timeDelta) { + timeDelta = data.length(); + if (timeDelta > oblock.length()) + timeDelta = oblock.length(); + } + getTransSource()->Forward(oblock, timeDelta); + } + } + deref(); + } +}; + +}; + +using namespace TelEngine; + + +int FormatInfo::guessSamples(int len) const +{ + if (!dataRate) + return 0; + if (frameSize) + len = frameSize * (len / frameSize); + return len * sampleRate / dataRate; +} + +typedef struct _flist { + struct _flist* next; + const FormatInfo* info; +} flist; + +static const FormatInfo s_formats[] = { + FormatInfo("slin", 16000), + FormatInfo("alaw", 8000), + FormatInfo("mulaw", 8000), + FormatInfo("gsm", 1650, 33), + FormatInfo("ilbc", 1667, 50), + FormatInfo("speex", 0), + FormatInfo("adpcm", 4000), + FormatInfo("g723", 0), + FormatInfo("g726", 4000), + FormatInfo("g729", 1000, 20), + FormatInfo("plain", 0, 0, "text", 0), +}; + +static flist* s_flist = 0; + +const FormatInfo* FormatRepository::getFormat(const String& name) +{ + if (name.null()) + return 0; + // search in the static list first + for (unsigned int i = 0; i < (sizeof(s_formats)/sizeof(FormatInfo)); i++) + if (name == s_formats[i].name) + return s_formats+i; + // then try the installed formats + for (flist* l = s_flist; l; l = l->next) + if (name == l->info->name) + return l->info; + return 0; +} + +const FormatInfo* FormatRepository::addFormat(const String& name, int drate, int fsize, const String& type, int srate, int nchan) +{ + if (name.null() || type.null()) + return 0; + + const FormatInfo* f = getFormat(name); + if (f) { + // found by name - check if it exactly matches what we have already + if ((drate != f->dataRate) || + (fsize != f->frameSize) || + (srate != f->sampleRate) || + (nchan != f->numChannels) || + (type != f->type)) { + Debug(DebugWarn,"Tried to register '%s' format '%s' drate=%d fsize=%d srate=%d nchan=%d", + type.c_str(),name.c_str(),drate,fsize,srate,nchan); + return 0; + } + return f; + } + // not in list - add a new one to the installed formats + Debug(DebugInfo,"Registering '%s' format '%s' drate=%d fsize=%d srate=%d nchan=%d", + type.c_str(),name.c_str(),drate,fsize,srate,nchan); + f = new FormatInfo(::strdup(name),drate,fsize,::strdup(type),srate,nchan); + flist* l = new flist; + l->info = f; + l->next = s_flist; + s_flist = l; + return f; +} + +void DataSource::Forward(const DataBlock &data, unsigned long timeDelta) +{ + // no number of samples provided - try to guess + if (!timeDelta) { + const FormatInfo* f = FormatRepository::getFormat(m_format); + if (f) + timeDelta = f->guessSamples(data.length()); + } + Lock lock(m_mutex); + ref(); + ObjList *l = &m_consumers; + for (; l; l=l->next()) { + DataConsumer *c = static_cast(l->get()); + if (c) + c->Consume(data,timeDelta); + } + m_timestamp += timeDelta; + deref(); +} + +bool DataSource::attach(DataConsumer *consumer) +{ + DDebug(DebugInfo,"DataSource [%p] attaching consumer [%p]",this,consumer); + if (!consumer) + return false; + Lock lock(m_mutex); + consumer->ref(); + if (consumer->getConnSource()) + consumer->getConnSource()->detach(consumer); + m_consumers.append(consumer); + consumer->setSource(this); + return true; +} + +bool DataSource::detach(DataConsumer *consumer) +{ + DDebug(DebugInfo,"DataSource [%p] detaching consumer [%p]",this,consumer); + if (!consumer) + return false; + // keep the source locked to prevent races with the Forward method + Lock lock(m_mutex); + DataConsumer *temp = static_cast(m_consumers.remove(consumer,false)); + if (temp) { + temp->setSource(0); + temp->deref(); + return true; + } + DDebug(DebugWarn,"DataSource [%p] has no consumer [%p]",this,consumer); + return false; +} + +DataSource::~DataSource() +{ + while (detach(static_cast(m_consumers.get()))) ; +} + +DataEndpoint::~DataEndpoint() +{ + disconnect(true,0); + setSource(); + setConsumer(); +} + +bool DataEndpoint::connect(DataEndpoint *peer) +{ + Debug(DebugInfo,"DataEndpoint peer address is [%p]",peer); + if (!peer) { + disconnect(); + return false; + } + if (peer == m_peer) + return true; + + ref(); + disconnect(); + peer->ref(); + peer->disconnect(); + bool native = (name() == peer->name()) && nativeConnect(peer); + + if (!native) { + DataSource *s = getSource(); + DataConsumer *c = peer->getConsumer(); + if (s && c) + DataTranslator::attachChain(s,c); + + s = peer->getSource(); + c = getConsumer(); + if (s && c) + DataTranslator::attachChain(s,c); + } + + m_peer = peer; + peer->setPeer(this); + connected(); + + return true; +} + +void DataEndpoint::disconnect(bool final, const char *reason) +{ + if (!m_peer) + return; + + DataSource *s = getSource(); + DataConsumer *c = m_peer->getConsumer(); + if (s && c) + DataTranslator::detachChain(s,c); + + s = m_peer->getSource(); + c = getConsumer(); + if (s && c) + DataTranslator::detachChain(s,c); + + DataEndpoint *temp = m_peer; + m_peer = 0; + temp->setPeer(0,reason); + temp->deref(); + disconnected(final,reason); + deref(); +} + +void DataEndpoint::setPeer(DataEndpoint *peer, const char *reason) +{ + m_peer = peer; + if (m_peer) + connected(); + else + disconnected(false,reason); +} + +void DataEndpoint::setSource(DataSource *source) +{ + if (source == m_source) + return; + DataConsumer *consumer = m_peer ? m_peer->getConsumer() : 0; + DataSource *temp = m_source; + if (consumer) + consumer->ref(); + m_source = 0; + if (temp) { + if (consumer) { + DataTranslator::detachChain(temp,consumer); + if (consumer->getConnSource()) + Debug(DebugWarn,"consumer source not cleared in %p",consumer); + } + temp->deref(); + } + if (source) { + source->ref(); + if (consumer) + DataTranslator::attachChain(source,consumer); + } + m_source = source; + if (consumer) + consumer->deref(); +} + +void DataEndpoint::setConsumer(DataConsumer *consumer) +{ + if (consumer == m_consumer) + return; + DataSource *source = m_peer ? m_peer->getSource() : 0; + DataConsumer *temp = m_consumer; + if (consumer) { + consumer->ref(); + if (source) + DataTranslator::attachChain(source,consumer); + } + m_consumer = consumer; + if (temp) { + if (source) + DataTranslator::detachChain(source,temp); + temp->deref(); + } +} + +ThreadedSource::~ThreadedSource() +{ + stop(); +} + +bool ThreadedSource::start(const char *name) +{ + if (!m_thread) { + m_thread = new ThreadedSourcePrivate(this,name); + m_thread->startup(); + } + return m_thread->running(); +} + +void ThreadedSource::stop() +{ + if (m_thread) { + delete m_thread; + m_thread = 0; + } +} + +void ThreadedSource::cleanup() +{ +} + +Thread *ThreadedSource::thread() const +{ + return m_thread; +} + +DataTranslator::DataTranslator(const char *sFormat, const char *dFormat) + : DataConsumer(sFormat) +{ + m_tsource = new DataSource(dFormat); + m_tsource->setTranslator(this); +} + +DataTranslator::DataTranslator(const char *sFormat, DataSource *source) + : DataConsumer(sFormat), m_tsource(source) +{ + m_tsource->setTranslator(this); +} + +DataTranslator::~DataTranslator() +{ + DataSource *temp = m_tsource; + m_tsource = 0; + if (temp) { + temp->setTranslator(0); + temp->deref(); + } +} + +Mutex DataTranslator::s_mutex; +ObjList DataTranslator::s_factories; + +void DataTranslator::install(TranslatorFactory *factory) +{ + s_mutex.lock(); + s_factories.append(factory); + s_mutex.unlock(); +} + +void DataTranslator::uninstall(TranslatorFactory *factory) +{ + s_mutex.lock(); + s_factories.remove(factory,false); + s_mutex.unlock(); +} + +String DataTranslator::srcFormats(const String &dFormat) +{ + String s; + s_mutex.lock(); + ObjList *l = &s_factories; + for (; l; l=l->next()) { + TranslatorFactory *f = static_cast(l->get()); + if (f) { + const TranslatorCaps *caps = f->getCapabilities(); + for (; caps && caps->src && caps->dest; caps++) { + if (dFormat == caps->dest->name) { + if (!s.null()) + s << " "; + s << caps->src->name << "@" << caps->cost; + } + } + } + } + s_mutex.unlock(); + return s; +} + +String DataTranslator::destFormats(const String &sFormat) +{ + String s; + s_mutex.lock(); + ObjList *l = &s_factories; + for (; l; l=l->next()) { + TranslatorFactory *f = static_cast(l->get()); + if (f) { + const TranslatorCaps *caps = f->getCapabilities(); + for (; caps && caps->src && caps->dest; caps++) { + if (sFormat == caps->src->name) { + if (!s.null()) + s << " "; + s << caps->dest->name << "@" << caps->cost; + } + } + } + } + s_mutex.unlock(); + return s; +} + +int DataTranslator::cost(const String &sFormat, const String &dFormat) +{ + int c = -1; + s_mutex.lock(); + ObjList *l = &s_factories; + for (; l; l=l->next()) { + TranslatorFactory *f = static_cast(l->get()); + if (f) { + const TranslatorCaps *caps = f->getCapabilities(); + for (; caps && caps->src && caps->dest; caps++) { + if ((c == -1) || (c > caps->cost)) { + if ((sFormat == caps->src->name) && (dFormat == caps->dest->name)) + c = caps->cost; + } + } + } + } + s_mutex.unlock(); + return c; +} + +DataTranslator *DataTranslator::create(const String &sFormat, const String &dFormat) +{ + if (sFormat == dFormat) { + Debug(DebugInfo,"Not creating identity DataTranslator for \"%s\"",sFormat.c_str()); + return 0; + } + + DataTranslator *trans = 0; + + s_mutex.lock(); + ObjList *l = &s_factories; + for (; l; l=l->next()) { + TranslatorFactory *f = static_cast(l->get()); + if (f) { + trans = f->create(sFormat,dFormat); + if (trans) + break; + } + } + s_mutex.unlock(); + + + if (!trans) { + DataBlock empty,probe; + if (probe.convert(empty,sFormat,dFormat)) + trans = new SimpleTranslator(sFormat,dFormat); + } + + if (trans) + Debug(DebugAll,"Created DataTranslator [%p] for \"%s\" -> \"%s\"", + trans,sFormat.c_str(),dFormat.c_str()); + else + Debug(DebugInfo,"No DataTranslator created for \"%s\" -> \"%s\"", + sFormat.c_str(),dFormat.c_str()); + return trans; +} + +bool DataTranslator::attachChain(DataSource *source, DataConsumer *consumer) +{ + if (!source || !consumer || source->getFormat().null() || consumer->getFormat().null()) + return false; + + bool retv = false; + // first attempt to connect directly, changing format if possible + if ((source->getFormat() == consumer->getFormat()) || + consumer->setFormat(source->getFormat()) || + source->setFormat(consumer->getFormat())) { + source->attach(consumer); + retv = true; + } + else { + // next, try to create a single translator + DataTranslator *trans = create(source->getFormat(),consumer->getFormat()); + if (trans) { + trans->getTransSource()->attach(consumer); + source->attach(trans); + retv = true; + } + // finally, try to convert trough "slin" if possible + else if ((source->getFormat() != "slin") && (consumer->getFormat() != "slin")) { + trans = create(source->getFormat(),"slin"); + if (trans) { + DataTranslator *trans2 = create("slin",consumer->getFormat()); + if (trans2) { + trans2->getTransSource()->attach(consumer); + trans->getTransSource()->attach(trans2); + source->attach(trans); + retv = true; + } + else + trans->destruct(); + } + } + } + NDebug(retv ? DebugAll : DebugWarn,"DataTranslator::attachChain [%p] \"%s\" -> [%p] \"%s\" %s", + source,source->getFormat().c_str(),consumer,consumer->getFormat().c_str(), + retv ? "succeeded" : "failed"); + return retv; +} + +bool DataTranslator::detachChain(DataSource *source, DataConsumer *consumer) +{ + Debugger debug(DebugAll,"DataTranslator::detachChain","(%p,%p)",source,consumer); + if (!source || !consumer) + return false; + + DataSource *tsource = consumer->getConnSource(); + if (tsource) { + if (source->detach(consumer)) + return true; + DataTranslator *trans = tsource->getTranslator(); + if (trans && detachChain(source,trans)) { + trans->deref(); + return true; + } + Debug(DebugWarn,"DataTranslator failed to detach chain [%p] -> [%p]",source,consumer); + } + return false; +} diff --git a/modules/gsmcodec.cpp b/modules/gsmcodec.cpp index a38343c2..7d4db7ca 100644 --- a/modules/gsmcodec.cpp +++ b/modules/gsmcodec.cpp @@ -34,9 +34,9 @@ typedef gsm_signal gsm_block[160]; using namespace TelEngine; static TranslatorCaps caps[] = { - { { "slin", 16000, 320 }, { "gsm", 1650, 33 } }, - { { "gsm", 1650, 33 }, { "slin", 16000, 320 } }, - { { 0, 0, 0 }, { 0, 0, 0 } } + { 0, 0 }, + { 0, 0 }, + { 0, 0 } }; int count = 0; @@ -128,6 +128,10 @@ void GsmCodec::Consume(const DataBlock &data, unsigned long timeDelta) GsmPlugin::GsmPlugin() { Output("Loaded module GSM - based on libgsm-%d.%d.%d",GSM_MAJOR,GSM_MINOR,GSM_PATCHLEVEL); + const FormatInfo* f = FormatRepository::addFormat("gsm",1650,33); + caps[0].src = caps[1].dest = f; +// caps[0].src = caps[1].dest = FormatRepository::getFormat("gsm"); + caps[0].dest = caps[1].src = FormatRepository::getFormat("slin"); } GsmPlugin::~GsmPlugin() diff --git a/yatephone.h b/yatephone.h index 7ec9138c..9987a6d6 100644 --- a/yatephone.h +++ b/yatephone.h @@ -38,12 +38,60 @@ namespace TelEngine { * A structure to hold information about a data format. */ struct FormatInfo { - /** Standard no-blanks lowcase format name */ - const char *name; - /** Data rate in octets/second, 0 for variable */ - int rate; - /** Frame size in octets, 0 for non-framed formats */ - int size; + /** + * Standard no-blanks lowercase format name + */ + const char* name; + + /** + * Format type: "audio", "video", "text" + */ + const char* type; + + /** + * Data rate in octets/second, 0 for variable + */ + int dataRate; + + /** + * Frame size in octets/frame, 0 for non-framed formats + */ + int frameSize; + + /** + * Rate in samples/second (audio) or 1e-6 frames/second (video), 0 for unknown + */ + int sampleRate; + + /** + * Number of channels, typically 1 + */ + int numChannels; + + /** + * Guess the number of samples in an encoded data block + * @param len Length of the data block in octets + * @return Number of samples or 0 if unknown + */ + int guessSamples(int len) const; + + /** + * Default constructor - used to initialize arrays + */ + inline FormatInfo() + : name(0), type("audio"), + dataRate(0), frameSize(0), + sampleRate(8000), numChannels(1) + { } + + /** + * Normal constructor + */ + inline FormatInfo(const char* _name, int drate, int fsize = 0, const char* _type = "audio", int srate = 8000, int nchan = 1) + : name(_name), type(_type), + dataRate(drate), frameSize(fsize), + sampleRate(srate), numChannels(nchan) + { } }; /** @@ -52,13 +100,45 @@ struct FormatInfo { */ struct TranslatorCaps { /** Description of source (input) data format */ - FormatInfo src; + const FormatInfo* src; /** Description of destination (output) data format */ - FormatInfo dest; + const FormatInfo* dest; /** Computing cost in KIPS of converting a stream from src to dest */ int cost; }; +/** + * This is just a holder for the list of media formats supported by Yate + * @short A repository for media formats + */ +class FormatRepository +{ +private: + FormatRepository(); + FormatRepository& operator=(const FormatRepository&); + virtual void dummy() const = 0; +public: + /** + * Retrieve a format by name and type + * @param name Standard name of the format to find + * @return Pointer to the format info or NULL if not found + */ + static const FormatInfo* getFormat(const String& name); + + /** + * Add a new format to the repository + * @param name Standard no-blanks lowercase format name + * @param drate Data rate in octets/second, 0 for variable + * @param fsize Frame size in octets/frame, 0 for non-framed formats + * @param type Format type: "audio", "video", "text" + * @param srate Rate in samples/second (audio) or 1e-6 frames/second (video), 0 for unknown + * @param nchan Number of channels, typically 1 + * @return Pointer to the format info or NULL if another incompatible + * format with the same name was already registered + */ + static const FormatInfo* addFormat(const String& name, int drate, int fsize, const String& type = "audio", int srate = 8000, int nchan = 1); +}; + /** * The DataBlock holds a data buffer with no specific formatting. * @short A class that holds just a block of raw data @@ -551,7 +631,7 @@ public: static String srcFormats(const String &dFormat = "slin"); /** - * Get a textual list of formats supported for a given inpput format + * Get a textual list of formats supported for a given input format * @param sFormat Name of source format * @return Space separated list of destination formats */