/** * faxchan.cpp * This file is part of the YATE Project http://YATE.null.ro * * This module is based on SpanDSP (a series of DSP components for telephony), * written by Steve Underwood . * * This great software can be found at * ftp://opencall.org/pub/spandsp/ * * Fax driver (transmission+receiving) * * Yet Another Telephony Engine - a fully featured software PBX and IVR * Copyright (C) 2004-2006 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */ // For SpanDSP we have to ask for various C99 stuff #define __USE_ISOC99 #define __STDC_LIMIT_MACROS extern "C" { #include #include #include #include #include #include "spandsp.h" }; #include #include #include #include #include #include using namespace TelEngine; namespace { // anonymous class FaxChan : public CallEndpoint { public: FaxChan(const char *file, bool receive, bool iscaller, const char *ident = 0); ~FaxChan(); virtual void disconnected(bool final, const char *reason); void rxData(const DataBlock &data); void rxBlock(void *buff, int len); int txBlock(); void phaseB(int result); void phaseD(int result); void phaseE(int result); private: t30_state_t m_fax; Mutex m_mutex; DataBlock m_buf; int m_lastr; bool m_eof; }; class FaxSource : public ThreadedSource { public: FaxSource(FaxChan *chan); ~FaxSource(); virtual void run(); private: unsigned m_total; FaxChan *m_chan; }; class FaxConsumer : public DataConsumer { public: FaxConsumer(FaxChan *chan); ~FaxConsumer(); virtual void Consume(const DataBlock &data,unsigned long); private: unsigned m_total; FaxChan *m_chan; }; class FaxHandler : public MessageHandler { public: FaxHandler(const char *name) : MessageHandler(name) { } virtual bool received(Message &msg); }; class FaxPlugin : public Plugin { public: FaxPlugin(); virtual void initialize(); private: FaxHandler *m_handler; }; FaxSource::FaxSource(FaxChan *chan) : m_total(0), m_chan(chan) { Debug(DebugAll,"FaxSource::FaxSource(%p) [%p]",chan,this); start("FaxSource"); } FaxSource::~FaxSource() { Debug(DebugAll,"FaxSource::~FaxSource() [%p] total=%u",this,m_total); } void FaxSource::run() { u_int64_t tpos = Time::now(); for (;;) { int r = m_chan->txBlock(); if (r < 0) break; if (!r) { r = 80; DDebug(DebugAll,"FaxSource inserting %d bytes silence [%p]",r,this); DataBlock data(0,r); Forward(data); } m_total += r; tpos += (r*1000000ULL/16000); int64_t dly = tpos - Time::now(); if (dly > 10000) dly = 10000; if (dly > 0) Thread::usleep((unsigned long)dly); } Debug(DebugAll,"FaxSource [%p] end of data total=%u",this,m_total); } FaxConsumer::FaxConsumer(FaxChan *chan) : m_total(0), m_chan(chan) { Debug(DebugAll,"FaxConsumer::FaxConsumer(%p) [%p]",chan,this); } FaxConsumer::~FaxConsumer() { Debug(DebugAll,"FaxConsumer::~FaxConsumer() [%p] total=%u",this,m_total); } void FaxConsumer::Consume(const DataBlock &data,unsigned long) { if (data.null()) return; m_total += data.length(); m_chan->rxData(data); } static void phase_b_handler(t30_state_t *s, void *user_data, int result) { if (user_data) static_cast(user_data)->phaseB(result); } static void phase_d_handler(t30_state_t *s, void *user_data, int result) { if (user_data) static_cast(user_data)->phaseD(result); } static void phase_e_handler(t30_state_t *s, void *user_data, int result) { if (user_data) static_cast(user_data)->phaseE(result); } FaxChan::FaxChan(const char *file, bool receive, bool iscaller, const char *ident) : CallEndpoint("faxfile"), m_lastr(0), m_eof(false) { Debug(DebugAll,"FaxChan::FaxChan(%s \"%s\") [%p]", (receive ? "receive" : "transmit"),file,this); if (!ident) ident = "unknown"; fax_init(&m_fax, iscaller, NULL); fax_set_local_ident(&m_fax, ident); if (receive) fax_set_rx_file(&m_fax, file); else fax_set_tx_file(&m_fax, file); //TODO add in the futher a callback to find number of pages and stuff like that. fax_set_phase_e_handler(&m_fax, phase_e_handler, this); fax_set_phase_d_handler(&m_fax, phase_d_handler, this); fax_set_phase_b_handler(&m_fax, phase_b_handler, this); m_fax.verbose = 1; setConsumer(new FaxConsumer(this)); getConsumer()->deref(); setSource(new FaxSource(this)); getSource()->deref(); } FaxChan::~FaxChan() { Debug(DebugAll,"FaxChan::~FaxChan() [%p]",this); setConsumer(); setSource(); } int FaxChan::txBlock() { Lock lock(m_mutex); if (m_lastr < 0) return m_lastr; int r = m_buf.length(); if (r) { getSource()->Forward(m_buf); m_buf.clear(); } else if (m_eof) { lock.drop(); disconnect("eof"); r = -1; } return r; } void FaxChan::rxBlock(void *buff, int len) { Lock lock(m_mutex); fax_rx_process(&m_fax, (int16_t *)buff,len/2); DataBlock data(0,len); int r = 2*fax_tx_process(&m_fax, (int16_t *) data.data(),len/2); if (r != len && r != m_lastr) Debug("FaxChan",DebugWarn,"Generated %d bytes! [%p]",r,this); m_lastr = r; if (r <= 0) { return; } data.truncate(r); m_buf.append(data); } void FaxChan::rxData(const DataBlock &data) { unsigned int pos = 0; while (pos < data.length()) { int len = data.length() - pos; if (len > 80) len = 80; rxBlock(((char *)data.data())+pos, len); pos += len; } } void FaxChan::phaseB(int result) { Debug(DebugAll,"FaxChan::phaseB code 0x%X [%p]",result,this); } void FaxChan::phaseD(int result) { Debug(DebugAll,"FaxChan::phaseD code 0x%X [%p]",result,this); t30_stats_t t; char ident[21]; fax_get_transfer_statistics(&m_fax, &t); Debug("Fax",DebugAll,"bit rate %d", t.bit_rate); Debug("Fax",DebugAll,"pages transferred %d", t.pages_transferred); Debug("Fax",DebugAll,"image size %d x %d", t.columns, t.rows); Debug("Fax",DebugAll,"image resolution %d x %d", t.column_resolution, t.row_resolution); Debug("Fax",DebugAll,"bad rows %d", t.bad_rows); Debug("Fax",DebugAll,"longest bad row run %d", t.longest_bad_row_run); Debug("Fax",DebugAll,"compression type %d", t.encoding); Debug("Fax",DebugAll,"image size %d", t.image_size); fax_get_local_ident(&m_fax, ident); Debug("Fax",DebugAll,"local ident '%s'", ident); fax_get_far_ident(&m_fax, ident); Debug("Fax",DebugAll,"remote ident '%s'", ident); } void FaxChan::phaseE(int result) { Debug(DebugAll,"FaxChan::phaseE code 0x%X [%p]",result,this); m_eof = true; } void FaxChan::disconnected(bool final, const char *reason) { Debug(DebugInfo,"FaxChan::disconnected() '%s' [%p]",reason,this); } bool FaxHandler::received(Message &msg) { String dest(msg.getValue("callto")); if (dest.null()) return false; Regexp r("^fax/\\([^/]*\\)/\\([^/]*\\)/\\(.*\\)$"); if (!dest.matches(r)) return false; bool transmit = false; if (dest.matchString(1) == "transmit") transmit = true; else if (dest.matchString(1) != "receive") { Debug(DebugGoOn,"Invalid fax method '%s', use 'receive' or 'transmit'", dest.matchString(1).c_str()); return false; } bool iscaller = (dest.matchString(2) == "caller"); FaxChan *fc = 0; if (transmit) { Debug(DebugInfo,"Transmit fax from file '%s'",dest.matchString(3).c_str()); fc = new FaxChan(dest.matchString(3).c_str(),false,iscaller); } else { Debug(DebugInfo,"Receive fax into file '%s'",dest.matchString(3).c_str()); fc = new FaxChan(dest.matchString(3).c_str(),true,iscaller); } CallEndpoint* ce = static_cast(msg.userData()); if (ce) { if (ce->connect(fc)) { fc->deref(); return true; } } else { const char *targ = msg.getValue("target"); if (!targ) { Debug(DebugWarn,"Fax outgoing call with no target!"); fc->destruct(); return false; } Message m("call.route"); m.addParam("id",dest); m.addParam("caller",dest); m.addParam("called",targ); m.userData(fc); if (Engine::dispatch(m)) { m = "call.execute"; m.addParam("callto",m.retValue()); m.retValue().clear(); if (Engine::dispatch(m)) { fc->deref(); return true; } Debug(DebugWarn,"Fax outgoing call not accepted!"); } else Debug(DebugWarn,"Fax outgoing call but no route!"); } fc->destruct(); return false; } FaxPlugin::FaxPlugin() : m_handler(0) { Output("Loaded module Fax"); } void FaxPlugin::initialize() { Output("Initializing module Fax"); if (!m_handler) { m_handler = new FaxHandler("call.execute"); Engine::install(m_handler); } } INIT_PLUGIN(FaxPlugin); }; // anonymous namespace /* vi: set ts=8 sw=4 sts=4 noet: */