diff --git a/sflphone-gtk/src/configurationmanager-glue.h b/sflphone-gtk/src/configurationmanager-glue.h index ba2d46b61d762461b715f06ee93ec4d07afd6ede..1f3810ccc8a47fdc5c9ef8fb363a27dd39823a32 100644 --- a/sflphone-gtk/src/configurationmanager-glue.h +++ b/sflphone-gtk/src/configurationmanager-glue.h @@ -1,7 +1,6 @@ /* Generated by dbus-binding-tool; do not edit! */ -#include <glib/gtypes.h> -#include <glib/gerror.h> +#include <glib.h> #include <dbus/dbus-glib.h> G_BEGIN_DECLS @@ -2001,6 +2000,156 @@ org_sflphone_SFLphone_ConfigurationManager_get_sip_port_async (DBusGProxy *proxy stuff->userdata = userdata; return dbus_g_proxy_begin_call (proxy, "getSipPort", org_sflphone_SFLphone_ConfigurationManager_get_sip_port_async_callback, stuff, g_free, G_TYPE_INVALID); } +static +#ifdef G_HAVE_INLINE +inline +#endif +gboolean +org_sflphone_SFLphone_ConfigurationManager_set_stun_server (DBusGProxy *proxy, const char * IN_server, GError **error) + +{ + return dbus_g_proxy_call (proxy, "setStunServer", error, G_TYPE_STRING, IN_server, G_TYPE_INVALID, G_TYPE_INVALID); +} + +typedef void (*org_sflphone_SFLphone_ConfigurationManager_set_stun_server_reply) (DBusGProxy *proxy, GError *error, gpointer userdata); + +static void +org_sflphone_SFLphone_ConfigurationManager_set_stun_server_async_callback (DBusGProxy *proxy, DBusGProxyCall *call, void *user_data) +{ + DBusGAsyncData *data = (DBusGAsyncData*) user_data; + GError *error = NULL; + dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID); + (*(org_sflphone_SFLphone_ConfigurationManager_set_stun_server_reply)data->cb) (proxy, error, data->userdata); + return; +} + +static +#ifdef G_HAVE_INLINE +inline +#endif +DBusGProxyCall* +org_sflphone_SFLphone_ConfigurationManager_set_stun_server_async (DBusGProxy *proxy, const char * IN_server, org_sflphone_SFLphone_ConfigurationManager_set_stun_server_reply callback, gpointer userdata) + +{ + DBusGAsyncData *stuff; + stuff = g_new (DBusGAsyncData, 1); + stuff->cb = G_CALLBACK (callback); + stuff->userdata = userdata; + return dbus_g_proxy_begin_call (proxy, "setStunServer", org_sflphone_SFLphone_ConfigurationManager_set_stun_server_async_callback, stuff, g_free, G_TYPE_STRING, IN_server, G_TYPE_INVALID); +} +static +#ifdef G_HAVE_INLINE +inline +#endif +gboolean +org_sflphone_SFLphone_ConfigurationManager_get_stun_server (DBusGProxy *proxy, char ** OUT_server, GError **error) + +{ + return dbus_g_proxy_call (proxy, "getStunServer", error, G_TYPE_INVALID, G_TYPE_STRING, OUT_server, G_TYPE_INVALID); +} + +typedef void (*org_sflphone_SFLphone_ConfigurationManager_get_stun_server_reply) (DBusGProxy *proxy, char * OUT_server, GError *error, gpointer userdata); + +static void +org_sflphone_SFLphone_ConfigurationManager_get_stun_server_async_callback (DBusGProxy *proxy, DBusGProxyCall *call, void *user_data) +{ + DBusGAsyncData *data = (DBusGAsyncData*) user_data; + GError *error = NULL; + char * OUT_server; + dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_STRING, &OUT_server, G_TYPE_INVALID); + (*(org_sflphone_SFLphone_ConfigurationManager_get_stun_server_reply)data->cb) (proxy, OUT_server, error, data->userdata); + return; +} + +static +#ifdef G_HAVE_INLINE +inline +#endif +DBusGProxyCall* +org_sflphone_SFLphone_ConfigurationManager_get_stun_server_async (DBusGProxy *proxy, org_sflphone_SFLphone_ConfigurationManager_get_stun_server_reply callback, gpointer userdata) + +{ + DBusGAsyncData *stuff; + stuff = g_new (DBusGAsyncData, 1); + stuff->cb = G_CALLBACK (callback); + stuff->userdata = userdata; + return dbus_g_proxy_begin_call (proxy, "getStunServer", org_sflphone_SFLphone_ConfigurationManager_get_stun_server_async_callback, stuff, g_free, G_TYPE_INVALID); +} +static +#ifdef G_HAVE_INLINE +inline +#endif +gboolean +org_sflphone_SFLphone_ConfigurationManager_enable_stun (DBusGProxy *proxy, GError **error) + +{ + return dbus_g_proxy_call (proxy, "enableStun", error, G_TYPE_INVALID, G_TYPE_INVALID); +} + +typedef void (*org_sflphone_SFLphone_ConfigurationManager_enable_stun_reply) (DBusGProxy *proxy, GError *error, gpointer userdata); + +static void +org_sflphone_SFLphone_ConfigurationManager_enable_stun_async_callback (DBusGProxy *proxy, DBusGProxyCall *call, void *user_data) +{ + DBusGAsyncData *data = (DBusGAsyncData*) user_data; + GError *error = NULL; + dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID); + (*(org_sflphone_SFLphone_ConfigurationManager_enable_stun_reply)data->cb) (proxy, error, data->userdata); + return; +} + +static +#ifdef G_HAVE_INLINE +inline +#endif +DBusGProxyCall* +org_sflphone_SFLphone_ConfigurationManager_enable_stun_async (DBusGProxy *proxy, org_sflphone_SFLphone_ConfigurationManager_enable_stun_reply callback, gpointer userdata) + +{ + DBusGAsyncData *stuff; + stuff = g_new (DBusGAsyncData, 1); + stuff->cb = G_CALLBACK (callback); + stuff->userdata = userdata; + return dbus_g_proxy_begin_call (proxy, "enableStun", org_sflphone_SFLphone_ConfigurationManager_enable_stun_async_callback, stuff, g_free, G_TYPE_INVALID); +} +static +#ifdef G_HAVE_INLINE +inline +#endif +gboolean +org_sflphone_SFLphone_ConfigurationManager_is_stun_enabled (DBusGProxy *proxy, gint* OUT_state, GError **error) + +{ + return dbus_g_proxy_call (proxy, "isStunEnabled", error, G_TYPE_INVALID, G_TYPE_INT, OUT_state, G_TYPE_INVALID); +} + +typedef void (*org_sflphone_SFLphone_ConfigurationManager_is_stun_enabled_reply) (DBusGProxy *proxy, gint OUT_state, GError *error, gpointer userdata); + +static void +org_sflphone_SFLphone_ConfigurationManager_is_stun_enabled_async_callback (DBusGProxy *proxy, DBusGProxyCall *call, void *user_data) +{ + DBusGAsyncData *data = (DBusGAsyncData*) user_data; + GError *error = NULL; + gint OUT_state; + dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INT, &OUT_state, G_TYPE_INVALID); + (*(org_sflphone_SFLphone_ConfigurationManager_is_stun_enabled_reply)data->cb) (proxy, OUT_state, error, data->userdata); + return; +} + +static +#ifdef G_HAVE_INLINE +inline +#endif +DBusGProxyCall* +org_sflphone_SFLphone_ConfigurationManager_is_stun_enabled_async (DBusGProxy *proxy, org_sflphone_SFLphone_ConfigurationManager_is_stun_enabled_reply callback, gpointer userdata) + +{ + DBusGAsyncData *stuff; + stuff = g_new (DBusGAsyncData, 1); + stuff->cb = G_CALLBACK (callback); + stuff->userdata = userdata; + return dbus_g_proxy_begin_call (proxy, "isStunEnabled", org_sflphone_SFLphone_ConfigurationManager_is_stun_enabled_async_callback, stuff, g_free, G_TYPE_INVALID); +} #endif /* defined DBUS_GLIB_CLIENT_WRAPPERS_org_sflphone_SFLphone_ConfigurationManager */ G_END_DECLS diff --git a/sflphone-gtk/src/configwindow.c b/sflphone-gtk/src/configwindow.c index be137c73cf4bc7c7e1f9257351c5e5c3eacc656e..a15290e3bfa511942377ca14416d7bd5397fe148 100644 --- a/sflphone-gtk/src/configwindow.c +++ b/sflphone-gtk/src/configwindow.c @@ -109,7 +109,7 @@ config_window_fill_account_list() * Delete an account */ static void -delete_account(GtkWidget *widget, gpointer data UNUSED) +delete_account(GtkWidget *widget UNUSED, gpointer data UNUSED) { if(selectedAccount) { @@ -449,24 +449,15 @@ create_accounts_tab() void stun_state( void ) { - guint i, size; - gchar * stun_enabled = "FALSE"; - account_t * account; + guint stun_enabled = 0; gboolean stunActive = (gboolean)gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( stunEnable )); gtk_widget_set_sensitive( GTK_WIDGET( stunServer ) , stunActive ); + // Check if we actually change the state - size = account_list_get_size(); - for(i=0; i<size; i++) - { - account = account_list_get_nth(i); - if( strcmp(g_hash_table_lookup(account->properties, ACCOUNT_TYPE), "SIP" ) == 0 ) - { - stun_enabled = g_hash_table_lookup(account->properties, ACCOUNT_SIP_STUN_ENABLED); - break; - } - } - if( (stunActive && strcmp(stun_enabled, "FALSE")==0) || (!stunActive && strcmp(stun_enabled, "TRUE")==0) ) + stun_enabled = dbus_stun_is_enabled(); + + if( (stunActive && stun_enabled ==0 ) || (!stunActive && stun_enabled ==1)) { gtk_widget_set_sensitive( GTK_WIDGET( applyButton ) , TRUE ); } @@ -477,24 +468,8 @@ void stun_state( void ) void update_registration( void ) { - guint nb_accounts, i; - account_t *current; - - nb_accounts = account_list_get_size(); - - for(i=0; i<nb_accounts;i++){ - - current = account_list_get_nth(i); - // If SIP account, then set the new value - if( strcmp(g_hash_table_lookup(current->properties, ACCOUNT_TYPE), "SIP" ) == 0 ) - { - g_hash_table_replace(current->properties, g_strdup(ACCOUNT_SIP_STUN_SERVER), - g_strdup((gchar *)gtk_entry_get_text(GTK_ENTRY(stunServer)))); - g_hash_table_replace(current->properties, g_strdup(ACCOUNT_SIP_STUN_ENABLED), - g_strdup(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(stunEnable)) ? "TRUE": "FALSE")); - dbus_set_account_details(current); - } - } + dbus_set_stun_server((gchar *)gtk_entry_get_text(GTK_ENTRY(stunServer))); + dbus_enable_stun(); gtk_widget_set_sensitive(GTK_WIDGET( applyButton ) , FALSE ); } @@ -505,23 +480,11 @@ GtkWidget* create_stun_tab() gchar * stun_server= "stun.fwdnet.net:3478"; gchar * stun_enabled = "FALSE"; GtkWidget * label; - account_t * account; - guint i, size; - // All SIP accounts are supposed to have the same STUN configuration - // So let's take the first SIP account we find - size = account_list_get_size(); - for(i=0; i<size; i++) - { - account = account_list_get_nth(i); - if( strcmp(g_hash_table_lookup(account->properties, ACCOUNT_TYPE), "SIP" ) == 0 ) - { - stun_enabled = g_hash_table_lookup(account->properties, ACCOUNT_SIP_STUN_ENABLED); - stun_server = g_hash_table_lookup(account->properties, ACCOUNT_SIP_STUN_SERVER); - break; - } - } - + /* Retrieve the STUN configuration */ + stun_enabled = (dbus_stun_is_enabled()==1)?"TRUE":"FALSE"; + stun_server = dbus_get_stun_server(); + tableNat = gtk_table_new ( 3, 2 , FALSE/* homogeneous */); // NAT detection code section diff --git a/sflphone-gtk/src/dbus.c b/sflphone-gtk/src/dbus.c index 7ed21b2118a4241b4dffd5ef73e120a12848ffe4..aa2edb87b9cb287bc1924d1fc5f0ba3a901321b4 100644 --- a/sflphone-gtk/src/dbus.c +++ b/sflphone-gtk/src/dbus.c @@ -39,6 +39,7 @@ DBusGConnection * connection; DBusGProxy * callManagerProxy; DBusGProxy * configurationManagerProxy; DBusGProxy * instanceProxy; +DBusGProxy * nameOwnerProxy; static void incoming_call_cb (DBusGProxy *proxy UNUSED, @@ -172,18 +173,32 @@ error_alert(DBusGProxy *proxy UNUSED, sflphone_throw_exception( errCode ); } + +static void nameOwnerChanged(DBusGProxy *proxy, char *name, char *old_owner, char *new_owner, gpointer data ) +{ + + g_print("******************************************************************\n"); + g_print("Owner name of the service %s changed from %s to %s\n", name, old_owner, new_owner); + g_print("******************************************************************\n"); + + if (strcmp(name, "org.sflphone.SFLphone")!=0) return; + +} + gboolean dbus_connect () { GError *error = NULL; connection = NULL; + instanceProxy = NULL; + nameOwnerProxy = NULL; g_type_init (); connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error); - if (connection == NULL) + if (error) { g_printerr ("Failed to open connection to bus: %s\n", error->message); @@ -191,13 +206,32 @@ dbus_connect () return FALSE; } + + nameOwnerProxy = dbus_g_proxy_new_for_name( connection, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS); + + if( nameOwnerProxy==NULL) + { + g_printerr ("Failed to get proxy to NameOwner\n"); + return FALSE; + } + + dbus_g_proxy_add_signal( nameOwnerProxy, "NameOwnerChanged", + G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INVALID); + dbus_g_proxy_connect_signal (nameOwnerProxy, "NameOwnerChanged", + G_CALLBACK (nameOwnerChanged), NULL, NULL); + + /* Create a proxy object for the "bus driver" (name "org.freedesktop.DBus") */ instanceProxy = dbus_g_proxy_new_for_name (connection, "org.sflphone.SFLphone", "/org/sflphone/SFLphone/Instance", "org.sflphone.SFLphone.Instance"); - if (!instanceProxy) + + if (instanceProxy==NULL) { g_printerr ("Failed to get proxy to Instance\n"); return FALSE; @@ -210,7 +244,8 @@ dbus_connect () "org.sflphone.SFLphone", "/org/sflphone/SFLphone/CallManager", "org.sflphone.SFLphone.CallManager"); - if (!callManagerProxy) + + if (callManagerProxy==NULL) { g_printerr ("Failed to get proxy to CallManagers\n"); return FALSE; @@ -256,6 +291,7 @@ dbus_connect () "org.sflphone.SFLphone", "/org/sflphone/SFLphone/ConfigurationManager", "org.sflphone.SFLphone.ConfigurationManager"); + if (!configurationManagerProxy) { g_printerr ("Failed to get proxy to ConfigurationManager\n"); @@ -1505,3 +1541,58 @@ dbus_get_sip_port( void ) return (guint)portNum; } +gchar* dbus_get_stun_server (void) +{ + GError* error = NULL; + gchar* server; + org_sflphone_SFLphone_ConfigurationManager_get_stun_server( + configurationManagerProxy, + &server, + &error); + if(error) + { + g_error_free(error); + } + return server; +} + +void dbus_set_stun_server( gchar* server) +{ + GError* error = NULL; + org_sflphone_SFLphone_ConfigurationManager_set_stun_server( + configurationManagerProxy, + server, + &error); + if(error) + { + g_error_free(error); + } +} + +guint dbus_stun_is_enabled (void) +{ + GError* error = NULL; + guint stun; + org_sflphone_SFLphone_ConfigurationManager_is_stun_enabled( + configurationManagerProxy, + &stun, + &error); + if(error) + { + g_error_free(error); + } + return stun; +} + +void dbus_enable_stun (void) +{ + + GError* error = NULL; + org_sflphone_SFLphone_ConfigurationManager_enable_stun( + configurationManagerProxy, + &error); + if(error) + { + g_error_free(error); + } +} diff --git a/sflphone-gtk/src/dbus.h b/sflphone-gtk/src/dbus.h index 4989443f06897300ebcd52e6cea87c6721cbcc58..f621cd04b10d75cf9bd98792b34bfa8d368f9432 100644 --- a/sflphone-gtk/src/dbus.h +++ b/sflphone-gtk/src/dbus.h @@ -429,4 +429,10 @@ void dbus_set_sip_port(const guint portNum); guint dbus_get_sip_port(); +gchar* dbus_get_stun_server (void); +void dbus_set_stun_server( gchar* server); + +guint dbus_stun_is_enabled (void); +void dbus_enable_stun (void); + #endif diff --git a/src/Makefile.am b/src/Makefile.am index 5be031174cdf639b7967fbb297021f6256af06e5..311be41e02e478b31e549bd5a1a2e9c29cc6b65f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -39,8 +39,7 @@ sflphoned_SOURCES = \ call.cpp \ account.cpp \ sipcall.cpp \ - $(IAXSOURCES) \ - useragent.cpp + $(IAXSOURCES) sflphoned_CXXFLAGS = \ -DPREFIX=\"$(prefix)\" -DPROGSHAREDIR=\"${datadir}/sflphone\" \ @@ -62,6 +61,8 @@ sflphoned_LDADD = \ @PULSEAUDIO_LIBS@ \ @SAMPLERATE_LIBS@ +#sflphoned_LDFLAGS=-pg + noinst_LTLIBRARIES = libsflphone.la noinst_HEADERS = \ @@ -78,8 +79,7 @@ noinst_HEADERS = \ accountcreator.h \ sipvoiplink.h \ call.h \ - sipcall.h \ - useragent.h + sipcall.h libsflphone_la_LIBADD = \ $(src)/libs/stund/libstun.la \ diff --git a/src/account.cpp b/src/account.cpp index 5bc6ba36ebb8325da825c8e93d3e1fdfa30dc1a7..b50fc71fc49d5648d875ac8bc4cb99758b82a9d3 100644 --- a/src/account.cpp +++ b/src/account.cpp @@ -22,9 +22,10 @@ #include "account.h" #include "manager.h" -Account::Account(const AccountID& accountID) : - _accountID(accountID), _link(NULL), _enabled(false) +Account::Account(const AccountID& accountID, std::string type) : + _accountID(accountID), _link(NULL), _enabled(false), _type(type) { + setRegistrationState(Unregistered); } Account::~Account() @@ -46,3 +47,9 @@ void Account::loadConfig() #endif } +void Account::setRegistrationState( RegistrationState state ) { + _registrationState = state; + + // Notify the client + Manager::instance().connectionStatusNotification( ); +} diff --git a/src/account.h b/src/account.h index 571f7220315facc986953b1c7a9cc9a2cb2f7b89..faf33dc162725e39c3dd4ad23985c0bb207b83c3 100644 --- a/src/account.h +++ b/src/account.h @@ -38,6 +38,19 @@ class VoIPLink; typedef std::string AccountID; +/** Contains all the state an Voip can be in */ +typedef enum RegistrationState { + Unregistered, + Trying, + Registered, + Error, + ErrorAuth , + ErrorNetwork , + ErrorHost, + ErrorExistStun, + ErrorConfStun +} RegistrationState; + #define AccountNULL "" // Common account parameters @@ -59,7 +72,7 @@ class Account{ public: - Account(const AccountID& accountID); + Account(const AccountID& accountID, std::string type); /** * Virtual destructor @@ -106,10 +119,28 @@ class Account{ * Get the registration state of the specified link * @return RegistrationState The registration state of underlying VoIPLink */ - VoIPLink::RegistrationState getRegistrationState() { return _link->getRegistrationState(); } + inline RegistrationState getRegistrationState() { return _registrationState; } - private: + void setRegistrationState( RegistrationState state ); + + /* inline functions */ + /* They should be treated like macro definitions by the C++ compiler */ + inline std::string getUsername( void ) { return _username; } + inline void setUsername( std::string username) { _username = username; } + + inline std::string getHostname( void ) { return _hostname; } + inline void setHostname( std::string hostname) { _hostname = hostname; } + + inline std::string getPassword( void ) { return _password; } + inline void setPassword( std::string password ) { _password = password; } + + inline std::string getAlias( void ) { return _alias; } + inline void setAlias( std::string alias ) { _alias = alias; } + inline std::string getType( void ) { return _type; } + inline void setType( std::string type ) { _type = type; } + + private: // copy constructor Account(const Account& rh); @@ -122,6 +153,32 @@ class Account{ */ AccountID _accountID; + /** + * Account login information: username + */ + std::string _username; + + /** + * Account login information: hostname + */ + std::string _hostname; + + /** + * Account login information: password + */ + std::string _password; + + /** + * Account login information: Alias + */ + std::string _alias; + + /* + * The account type + * IAX2 or SIP + */ + std::string _type; + /** * Voice over IP Link contains a listener thread and calls */ @@ -134,6 +191,11 @@ class Account{ */ bool _enabled; + /* + * The registration state of the account + */ + RegistrationState _registrationState; + }; #endif diff --git a/src/audio/Makefile.am b/src/audio/Makefile.am index 19cf33ee8124858805aa0f2bd72f73a7c1ec69e8..73659fe66c2bec7b756b3db7049ca65d14f8ef4c 100644 --- a/src/audio/Makefile.am +++ b/src/audio/Makefile.am @@ -30,6 +30,7 @@ libaudio_la_SOURCES = \ dtmf.cpp \ tone.cpp \ alsalayer.cpp \ + audiolayer.cpp \ pulselayer.cpp \ audiodevice.cpp \ dtmfgenerator.cpp \ diff --git a/src/audio/alsalayer.cpp b/src/audio/alsalayer.cpp index acd933c9d071b4d82e1a02c7206f07ab069a279d..5409c855963e18002f45ebb8ab787a98afef0f83 100644 --- a/src/audio/alsalayer.cpp +++ b/src/audio/alsalayer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Savoir-Faire Linux inc. + * Copyright (C) 2008 2009 Savoir-Faire Linux inc. * Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com> * * This program is free software; you can redistribute it and/or modify @@ -19,13 +19,7 @@ #include "alsalayer.h" -void* ringtoneThreadEntry( void *ptr); - -static pthread_t ringtone_thread; -bool ringtone_thread_is_running; - -pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER; -pthread_cond_t cond = PTHREAD_COND_INITIALIZER; +int framesPerBufferAlsa = 2048; // Constructor AlsaLayer::AlsaLayer( ManagerImpl* manager ) @@ -34,70 +28,63 @@ pthread_cond_t cond = PTHREAD_COND_INITIALIZER; , _CaptureHandle(NULL) , _periodSize() , _audioPlugin() - , _inChannel() - , _outChannel() - , _defaultVolume(100) - , IDSoundCards() + , IDSoundCards() + , _is_prepared_playback (false) + , _is_running_playback (false) + , _is_open_playback (false) + , _is_prepared_capture (false) + , _is_running_capture (false) + , _is_open_capture (false) + , _trigger_request (false) + { _debug(" Constructor of AlsaLayer called\n"); - - // The flag to stop the ringtone thread loop - ringtone_thread_is_running = false; + /* Instanciate the audio thread */ + _audioThread = new AudioThread (this); } // Destructor AlsaLayer::~AlsaLayer (void) { - _debugAlsa("Close ALSA streams\n"); - closeCaptureStream(); - closePlaybackStream(); - deviceClosed = true; - - ringtone_thread_is_running = false; - pthread_join(ringtone_thread, NULL); + _debug("Destructor of AlsaLayer called\n"); + closeLayer(); } void AlsaLayer::closeLayer() { _debugAlsa("Close ALSA streams\n"); + + if (_audioThread) + { + _debug("Try to stop audio thread\n"); + delete _audioThread; _audioThread=NULL; + } + closeCaptureStream(); closePlaybackStream(); - deviceClosed = true; - - ringtone_thread_is_running = false; } bool AlsaLayer::openDevice (int indexIn, int indexOut, int sampleRate, int frameSize, int stream , std::string plugin) { - - // We don't accept that the audio plugin is changed during a conversation - if( _talk ){ - _debug("can't switch audio plugin when talking\n. Please hang up and try again...\n"); - return false; - } - - if(deviceClosed == false) + /* Close the devices before open it */ + if (stream == SFL_PCM_BOTH && is_capture_open() == true && is_playback_open() == true) { - if( stream == SFL_PCM_CAPTURE ) - closeCaptureStream(); - else if( stream == SFL_PCM_PLAYBACK) - closePlaybackStream(); - else - { - closeCaptureStream(); - closePlaybackStream(); - } + closeCaptureStream(); + closePlaybackStream(); } + else if ((stream == SFL_PCM_CAPTURE || stream == SFL_PCM_BOTH) && is_capture_open() == true) + closeCaptureStream (); + else if ((stream == SFL_PCM_PLAYBACK || stream == SFL_PCM_BOTH) && is_playback_open () == true) + closePlaybackStream (); + _indexIn = indexIn; _indexOut = indexOut; _sampleRate = sampleRate; _frameSize = frameSize; _audioPlugin = plugin; - _inChannel = 1; - _outChannel = 1; _debugAlsa(" Setting AlsaLayer: device in=%2d, out=%2d\n", _indexIn, _indexOut); _debugAlsa(" : alsa plugin=%s\n", _audioPlugin.c_str()); @@ -105,17 +92,7 @@ AlsaLayer::openDevice (int indexIn, int indexOut, int sampleRate, int frameSize, _debugAlsa(" : sample rate=%5d, format=%s\n", _sampleRate, SFLDataFormatString); ost::MutexLock lock( _mutex ); - - /*void **hint; - int r = snd_device_name_hint(-1, "pcm", &hint); - - while( *hint ){ - printf("%s\n", snd_device_name_get_hint(*hint, "DESC")); - ++hint; - }*/ - - std::string pcmp = buildDeviceTopo( plugin , indexOut , 0); std::string pcmc = buildDeviceTopo( PCM_PLUGHW , indexIn , 0); @@ -125,142 +102,40 @@ AlsaLayer::openDevice (int indexIn, int indexOut, int sampleRate, int frameSize, void AlsaLayer::startStream(void) { - if( _CaptureHandle && _PlaybackHandle ) - { - _talk = true ; - _debugAlsa(" Start stream\n"); - int err; - //ost::MutexLock lock( _mutex ); - snd_pcm_prepare( _CaptureHandle ); - snd_pcm_start( _CaptureHandle ) ; - - snd_pcm_prepare( _PlaybackHandle ); - if( (err = snd_pcm_start( _PlaybackHandle)) < 0 ) _debugAlsa(" Cannot start (%s)\n", snd_strerror(err)); - } + _debug ("Start ALSA streams\n"); + prepareCaptureStream (); + startCaptureStream (); + startPlaybackStream (); } void AlsaLayer::stopStream(void) { - if( _CaptureHandle && _PlaybackHandle ) - { - //ost::MutexLock lock( _mutex ); - _debugAlsa(" Stop Stream\n "); - _talk = false; - snd_pcm_drop( _CaptureHandle ); - snd_pcm_prepare( _CaptureHandle ); - snd_pcm_drop( _PlaybackHandle ); - snd_pcm_prepare( _PlaybackHandle ); - _urgentBuffer.flush(); - } -} - - -void* ringtoneThreadEntry( void *ptr ) -{ - while( ringtone_thread_is_running ) - { - ( ( AlsaLayer *) ptr) -> playTones(); - //sleep(0.1); - } - /* - pthread_mutex_lock(&mut); - while( ((AlsaLayer*)ptr)->_manager->getTelephoneTone() == NULL ) - { - pthread_cond_wait(&cond, &mut); - } - ( AlsaLayer *) ptr -> playTones(); - pthread_mutex_unlock(&mut);*/ - return 0; -} - - - void -AlsaLayer::fillHWBuffer( void) -{ + _debug ("Stop ALSA streams\n"); + stopCaptureStream (); + //stopPlaybackStream (); - unsigned char* data; - int pcmreturn, l1, l2; - short s1, s2; - int periodSize = 128 ; - int frames = periodSize >> 2 ; - _debug("frames = %d\n", frames); - - data = (unsigned char*)malloc(periodSize); - for(l1 = 0; l1 < 100; l1++) { - for(l2 = 0; l2 < frames; l2++) { - s1 = 0; - s2 = 0; - data[4*l2] = (unsigned char)s1; - data[4*l2+1] = s1 >> 8; - data[4*l2+2] = (unsigned char)s2; - data[4*l2+3] = s2 >> 8; - } - while ((pcmreturn = snd_pcm_writei(_PlaybackHandle, data, frames)) < 0) { - snd_pcm_prepare(_PlaybackHandle); - //_debugAlsa("< Buffer Underrun >\n"); - } - } + /* Flush the ring buffers */ + flushUrgent(); + flushMain(); } - bool -AlsaLayer::isStreamActive (void) -{ - ost::MutexLock lock( _mutex ); - return (isPlaybackActive() && isCaptureActive()); -} - - int -AlsaLayer::playSamples(void* buffer, int toCopy, bool isTalking) -{ - //ost::MutexLock lock( _mutex ); - if( isTalking ) - _talk = true; - if ( _PlaybackHandle ){ - write( adjustVolume( buffer , toCopy , SFL_PCM_PLAYBACK ) , toCopy ); - } - return 0; -} - - int -AlsaLayer::putUrgent(void* buffer, int toCopy) -{ - int nbBytes = 0; - - if ( _PlaybackHandle ){ - //fillHWBuffer(); - int a = _urgentBuffer.AvailForPut(); - if( a >= toCopy ){ - nbBytes = _urgentBuffer.Put( buffer , toCopy , _defaultVolume ); - } else { - nbBytes = _urgentBuffer.Put( buffer , a , _defaultVolume ) ; - } - } - - return nbBytes; -} - -void AlsaLayer::trigger_thread(void) -{ - _debug("Wake up the ringtone thread\n"); - pthread_cond_broadcast(&cond); -} - - int AlsaLayer::canGetMic() { int avail; - if ( _CaptureHandle ) { - avail = snd_pcm_avail_update( _CaptureHandle ); - //printf("%d\n", avail ); - if(avail > 0) - return avail; - else - return 0; + + if (! _CaptureHandle ) + return 0; + + avail = snd_pcm_avail_update( _CaptureHandle ); + + if( avail == -EPIPE ){ + stop_capture (); + return 0; } else - return 0; + return ((avail < 0)? 0:avail); } int @@ -275,75 +150,77 @@ AlsaLayer::getMic(void *buffer, int toCopy) return res ; } - - bool -AlsaLayer::isStreamStopped (void) -{ - ost::MutexLock lock( _mutex ); - return !(isStreamActive()); -} - - -void AlsaLayer::reducePulseAppsVolume( void ){} -void AlsaLayer::restorePulseAppsVolume( void ){} - ////////////////////////////////////////////////////////////////////////////////////////////// ///////////////// ALSA PRIVATE FUNCTIONS //////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////// + +void AlsaLayer::stopCaptureStream (void) +{ + if(_CaptureHandle){ + snd_pcm_drop (_CaptureHandle); + stop_capture (); + } +} +void AlsaLayer::closeCaptureStream (void) +{ + if( is_capture_prepared() == true && is_capture_running() == true ) + stopCaptureStream (); + if (is_capture_open()) + snd_pcm_close (_CaptureHandle); + close_capture (); +} - void -AlsaLayer::playTones( void ) +void AlsaLayer::startCaptureStream (void) { - int frames; - int maxBytes; + if(_CaptureHandle){ + snd_pcm_start (_CaptureHandle); + start_capture(); + } +} - pthread_mutex_lock(&mut); - while(!_manager-> getTelephoneTone() && !_manager->getTelephoneFile()) - { - _debug("Make the ringtone thread wait\n"); - pthread_cond_wait(&cond, &mut); - } - - //frames = _periodSize ; - frames = 940 ; - maxBytes = frames * sizeof(SFLDataFormat) ; - SFLDataFormat* out = (SFLDataFormat*)malloc(maxBytes * sizeof(SFLDataFormat)); - if( _talk ) {} - else { - AudioLoop *tone = _manager -> getTelephoneTone(); - if( tone != 0 ){ - tone -> getNext( out , frames , _manager->getSpkrVolume() ); - write( out , maxBytes ); - } - else if( ( tone=_manager->getTelephoneFile() ) != 0 ){ - tone ->getNext( out , frames , _manager->getSpkrVolume() ); - write( out , maxBytes ); - } +void AlsaLayer::prepareCaptureStream (void) +{ + if (is_capture_open() ) { + if(snd_pcm_prepare (_CaptureHandle) < 0) _debug("Error preparing the device\n"); + prepare_capture (); } - // free the temporary data buffer - free( out ); out = 0; - pthread_mutex_unlock(&mut); } +void AlsaLayer::stopPlaybackStream (void) +{ + if( _PlaybackHandle){ + snd_pcm_drop (_PlaybackHandle); + stop_playback (); + } +} -bool -AlsaLayer::isPlaybackActive(void) { - ost::MutexLock guard( _mutex ); - if( _PlaybackHandle ) - return (snd_pcm_state(_PlaybackHandle) == SND_PCM_STATE_RUNNING ? true : false); - else - return false; + +void AlsaLayer::closePlaybackStream (void) +{ + if( is_playback_prepared() == true && is_playback_running() == true ) + stopPlaybackStream (); + if (is_playback_open()) + snd_pcm_close (_PlaybackHandle); + + close_playback (); } -bool -AlsaLayer::isCaptureActive(void) { - ost::MutexLock guard( _mutex ); - if( _CaptureHandle ) - return (snd_pcm_state( _CaptureHandle) == SND_PCM_STATE_RUNNING ? true : false); - else - return false; +void AlsaLayer::startPlaybackStream (void) +{ + if( _PlaybackHandle){ + snd_pcm_start (_PlaybackHandle); + start_playback(); + } +} + +void AlsaLayer::preparePlaybackStream (void) +{ + if(is_playback_open()){ + if( snd_pcm_prepare (_PlaybackHandle) < 0) _debug("Error preparing the device\n"); + prepare_playback (); + } } bool AlsaLayer::alsa_set_params( snd_pcm_t *pcm_handle, int type, int rate ){ @@ -444,23 +321,8 @@ bool AlsaLayer::alsa_set_params( snd_pcm_t *pcm_handle, int type, int rate ){ return false; } - if( type == 1 ){ - /*if( (err = snd_async_add_pcm_handler( &_AsyncHandler, pcm_handle , AlsaCallBack, this ) < 0)){ - _debugAlsa(" Unable to install the async callback handler (%s)\n", snd_strerror(err)); - return false; - }*/ - - // So the loop could start when the ringtone thread entry function is reached - ringtone_thread_is_running = true; - if( pthread_create(&ringtone_thread, NULL, ringtoneThreadEntry, this) != 0 ) - { - _debug("Unable to start the ringtone posix thread\n"); - return false; - } - } snd_pcm_sw_params_free( swparams ); - deviceClosed = false; return true; } @@ -477,13 +339,17 @@ AlsaLayer::open_device(std::string pcm_p, std::string pcm_c, int flag) if((err = snd_pcm_open(&_PlaybackHandle, pcm_p.c_str(), SND_PCM_STREAM_PLAYBACK, 0 )) < 0){ _debugAlsa("Error while opening playback device %s\n", pcm_p.c_str()); setErrorMessage( ALSA_PLAYBACK_DEVICE ); + close_playback (); return false; } if(!alsa_set_params( _PlaybackHandle, 1, getSampleRate() )){ _debug("playback failed\n"); snd_pcm_close( _PlaybackHandle ); + close_playback (); return false; } + + open_playback (); } if(flag == SFL_PCM_BOTH || flag == SFL_PCM_CAPTURE) @@ -491,20 +357,23 @@ AlsaLayer::open_device(std::string pcm_p, std::string pcm_c, int flag) if( (err = snd_pcm_open(&_CaptureHandle, pcm_c.c_str(), SND_PCM_STREAM_CAPTURE, 0)) < 0){ _debugAlsa("Error while opening capture device %s\n", pcm_c.c_str()); setErrorMessage( ALSA_CAPTURE_DEVICE ); + close_capture (); return false; } if(!alsa_set_params( _CaptureHandle, 0, 8000 /*getSampleRate()*/ )){ _debug("capture failed\n"); snd_pcm_close( _CaptureHandle ); + close_capture (); return false; } - if( (err = snd_pcm_start( _CaptureHandle ))<0) { - _debugAlsa( "snd_pcm_start failed\n"); - } + + open_capture (); + + startCaptureStream (); } - //TODO something, really! - _talk = false; + /* Start the secondary audio thread for callbacks */ + _audioThread->start(); return true; } @@ -513,33 +382,34 @@ AlsaLayer::open_device(std::string pcm_p, std::string pcm_c, int flag) int AlsaLayer::write(void* buffer, int length) { - //if(snd_pcm_state( _PlaybackHandle ) == SND_PCM_STATE_XRUN) - //handle_xrun_playback(); - //_debugAlsa("avail = %d - toWrite = %d\n" , snd_pcm_avail_update( _PlaybackHandle ) , length / 2); - snd_pcm_uframes_t frames = snd_pcm_bytes_to_frames( _PlaybackHandle, length); - int err = snd_pcm_writei( _PlaybackHandle , buffer , frames ); - switch(err) { - case -EAGAIN: - _debugAlsa("EAGAIN (%s)\n", snd_strerror( err )); - snd_pcm_resume( _PlaybackHandle ); - break; - case -EPIPE: - _debugAlsa(" UNDERRUN (%s)\n", snd_strerror(err)); - handle_xrun_playback(); - snd_pcm_writei( _PlaybackHandle , buffer , frames ); - break; - case -ESTRPIPE: - _debugAlsa(" ESTRPIPE(%s)\n", snd_strerror(err)); - snd_pcm_resume( _PlaybackHandle ); - break; - case -EBADFD: - _debugAlsa(" (%s)\n", snd_strerror( err )); - break; + + if (_trigger_request == true) + { + _trigger_request = false; + startPlaybackStream (); } - if( ( err >=0 ) && ( err < (int)frames ) ) - _debugAlsa("Short write : %d out of %d\n", err , (int)frames); + snd_pcm_uframes_t frames = snd_pcm_bytes_to_frames( _PlaybackHandle, length); + int err; + if ((err=snd_pcm_writei( _PlaybackHandle , buffer , frames ))<0) + { + switch(err) { + case -EPIPE: + case -ESTRPIPE: + case -EIO: + //_debugAlsa(" XRUN playback ignored (%s)\n", snd_strerror(err)); + handle_xrun_playback(); + if (snd_pcm_writei( _PlaybackHandle , buffer , frames )<0) + _debugAlsa ("XRUN handling failed\n"); + _trigger_request = true; + break; + default: + //_debugAlsa ("Write error unknown - dropping frames **********************************: %s\n", snd_strerror(err)); + stopPlaybackStream (); + break; + } + } return ( err > 0 )? err : 0 ; } @@ -547,29 +417,32 @@ AlsaLayer::write(void* buffer, int length) int AlsaLayer::read( void* buffer, int toCopy) { - if(deviceClosed || _CaptureHandle == NULL) - return 0; + ost::MutexLock lock( _mutex ); + + int samples; - int err; if(snd_pcm_state( _CaptureHandle ) == SND_PCM_STATE_XRUN) { - snd_pcm_prepare( _CaptureHandle ); - snd_pcm_start( _CaptureHandle ); + _debug("xrun caught before start\n"); + prepareCaptureStream (); + startCaptureStream (); } + snd_pcm_uframes_t frames = snd_pcm_bytes_to_frames( _CaptureHandle, toCopy ); - if(( err = snd_pcm_readi( _CaptureHandle, buffer, frames)) < 0 ) { - switch(err){ - case EPERM: - _debugAlsa(" Capture EPERM (%s)\n", snd_strerror(err)); - snd_pcm_prepare( _CaptureHandle); - snd_pcm_start( _CaptureHandle ); - break; - case -EAGAIN: - _debugAlsa(" Capture EAGAIN (%s)\n", snd_strerror(err)); - break; + if(( samples = snd_pcm_readi( _CaptureHandle, buffer, frames)) < 0 ) { + switch (samples) + { case -EPIPE: - _debugAlsa(" Capture EPIPE (%s)\n", snd_strerror(err)); + case -ESTRPIPE: + case -EIO: + _debugAlsa(" XRUN capture ignored (%s)\n", snd_strerror(samples)); handle_xrun_capture(); + samples = snd_pcm_readi( _CaptureHandle, buffer, frames); + if (samples<0) samples=0; + break; + default: + //_debugAlsa ("Error when capturing data ***********************************************: %s\n", snd_strerror(samples)); + stopCaptureStream(); break; } return 0; @@ -577,14 +450,6 @@ AlsaLayer::read( void* buffer, int toCopy) return toCopy; -} - - int -AlsaLayer::putInCache( char code UNUSED, - void *buffer UNUSED, - int toCopy UNUSED ) -{ - return 1; } void @@ -596,9 +461,9 @@ AlsaLayer::handle_xrun_capture( void ) int res = snd_pcm_status( _CaptureHandle, status ); if( res <= 0){ if(snd_pcm_status_get_state(status) == SND_PCM_STATE_XRUN ){ - snd_pcm_drop( _CaptureHandle ); - snd_pcm_prepare( _CaptureHandle ); - snd_pcm_start( _CaptureHandle ); + stopCaptureStream (); + prepareCaptureStream (); + startCaptureStream (); } } else @@ -618,9 +483,9 @@ AlsaLayer::handle_xrun_playback( void ) state = snd_pcm_status_get_state( status ); if( state == SND_PCM_STATE_XRUN ) { - snd_pcm_drop( _PlaybackHandle ); - snd_pcm_prepare( _PlaybackHandle ); - //snd_pcm_start( _PlaybackHandle ); + stopPlaybackStream (); + preparePlaybackStream (); + _trigger_request = true; } } } @@ -695,25 +560,7 @@ AlsaLayer::getSoundCardsInfo( int stream ) return cards_id; } - void -AlsaLayer::closeCaptureStream( void) -{ - if(_CaptureHandle){ - snd_pcm_drop( _CaptureHandle ); - snd_pcm_close( _CaptureHandle ); - _CaptureHandle = 0; - } -} - void -AlsaLayer::closePlaybackStream( void) -{ - if(_PlaybackHandle){ - snd_pcm_drop( _PlaybackHandle ); - snd_pcm_close( _PlaybackHandle ); - _PlaybackHandle = 0; - } -} bool AlsaLayer::soundCardIndexExist( int card , int stream ) @@ -749,8 +596,68 @@ AlsaLayer::soundCardGetIndex( std::string description ) return 0; } - void* -AlsaLayer::adjustVolume( void* buffer , int len, int stream ) +void AlsaLayer::audioCallback (void) +{ + + int toGet, toPut, urgentAvail, normalAvail, micAvailPut, maxBytes; + unsigned short spkrVolume, micVolume; + AudioLoop *tone; + + SFLDataFormat *out; + + spkrVolume = _manager->getSpkrVolume(); + micVolume = _manager->getMicVolume(); + + // AvailForGet tell the number of chars inside the buffer + // framePerBuffer are the number of data for one channel (left) + urgentAvail = _urgentRingBuffer.AvailForGet(); + if (urgentAvail > 0) { + // Urgent data (dtmf, incoming call signal) come first. + toGet = (urgentAvail < (int)(framesPerBufferAlsa * sizeof(SFLDataFormat))) ? urgentAvail : framesPerBufferAlsa * sizeof(SFLDataFormat); + out = (SFLDataFormat*)malloc(toGet * sizeof(SFLDataFormat) ); + _urgentRingBuffer.Get(out, toGet, spkrVolume); + /* Play the sound */ + write( out , toGet ); + free(out); out=0; + // Consume the regular one as well (same amount of bytes) + _voiceRingBuffer.Discard(toGet); + } else { + tone = _manager->getTelephoneTone(); + toGet = 940 ; + maxBytes = toGet * sizeof(SFLDataFormat) ; + if ( tone != 0) { + out = (SFLDataFormat*)malloc(maxBytes * sizeof(SFLDataFormat)); + tone->getNext(out, toGet, spkrVolume); + write (out , maxBytes); + } else if ( (tone=_manager->getTelephoneFile()) != 0 ) { + out = (SFLDataFormat*)malloc(maxBytes * sizeof(SFLDataFormat) ); + tone->getNext(out, toGet, spkrVolume); + write (out , maxBytes); + } else { + // If nothing urgent, play the regular sound samples + normalAvail = _voiceRingBuffer.AvailForGet(); + toGet = (normalAvail < (int)(framesPerBufferAlsa * sizeof(SFLDataFormat))) ? normalAvail : framesPerBufferAlsa * sizeof(SFLDataFormat); + out = (SFLDataFormat*)malloc(framesPerBufferAlsa * sizeof(SFLDataFormat)); + + if (toGet) { + _voiceRingBuffer.Get(out, toGet, spkrVolume); + write (out, toGet); + } else { + bzero(out, framesPerBufferAlsa * sizeof(SFLDataFormat)); + } + } + free(out); out=0; + } + + // Additionally handle the mic's audio stream + //micAvailPut = _micRingBuffer.AvailForPut(); + //toPut = (micAvailPut <= (int)(framesPerBufferAlsa * sizeof(SFLDataFormat))) ? micAvailPut : framesPerBufferAlsa * sizeof(SFLDataFormat); + //_debug("AL: Nb sample: %d char, [0]=%f [1]=%f [2]=%f\n", toPut, in[0], in[1], in[2]); + //_micRingBuffer.Put(in, toPut, micVolume); +} + + +void* AlsaLayer::adjustVolume( void* buffer , int len, int stream ) { int vol, i, size; SFLDataFormat *src = NULL; @@ -768,4 +675,3 @@ AlsaLayer::adjustVolume( void* buffer , int len, int stream ) } return src ; } - diff --git a/src/audio/alsalayer.h b/src/audio/alsalayer.h index c9dfa016d2cf8d61d68df15d815d7cc8f13eb088..875e0cc14124b19963b50e7fbc947e9ba3e7ad65 100644 --- a/src/audio/alsalayer.h +++ b/src/audio/alsalayer.h @@ -21,8 +21,8 @@ #define _ALSA_LAYER_H #include "audiolayer.h" - -#include <pthread.h> +#include "eventthread.h" +#include <alsa/asoundlib.h> class RingBuffer; class ManagerImpl; @@ -48,8 +48,6 @@ class AlsaLayer : public AudioLayer { */ ~AlsaLayer(void); - void trigger_thread(void); - void closeLayer( void ); /** @@ -81,52 +79,6 @@ class AlsaLayer : public AudioLayer { */ void stopStream(void); - /** - * Check if the playback is running - * @return true if the state of the playback handle equals SND_PCM_STATE_RUNNING - * false otherwise - */ - bool isPlaybackActive( void ); - - /** - * Check if the capture is running - * @return true if the state of the capture handle equals SND_PCM_STATE_RUNNING - * false otherwise - */ - bool isCaptureActive( void ); - - /** - * Check if both capture and playback are running - * @return true if capture and playback are running - * false otherwise - */ - bool isStreamActive(void); - - /** - * Check if both capture and playback are stopped - * @return true if capture and playback are stopped - * false otherwise - */ - bool isStreamStopped(void); - - /** - * Send samples to the audio device. - * @param buffer The buffer containing the data to be played ( voice and DTMF ) - * @param toCopy The number of samples, in bytes - * @param isTalking If whether or not the conversation is running - * @return int The number of bytes played - */ - int playSamples(void* buffer, int toCopy, bool isTalking); - - /** - * Send a chunk of data to the hardware buffer to start the playback - * Copy data in the urgent buffer. - * @param buffer The buffer containing the data to be played ( ringtones ) - * @param toCopy The size of the buffer - * @return int The number of bytes copied in the urgent buffer - */ - int putUrgent(void* buffer, int toCopy); - /** * Query the capture device for number of bytes available in the hardware ring buffer * @return int The number of bytes available @@ -185,34 +137,9 @@ class AlsaLayer : public AudioLayer { */ std::string getAudioPlugin( void ) { return _audioPlugin; } - /** - * UNUSED in ALSA layer - */ - int putInCache( char code, void *buffer, int toCopy ); - - - /** - * UNUSED in ALSA layer - */ - void reducePulseAppsVolume( void ); - - /** - * UNUSED in ALSA layer - */ - void restorePulseAppsVolume( void ); - - /** - * UNUSED in ALSA layer - */ - void setPlaybackVolume( UNUSED int volume ){} - void setCaptureVolume( UNUSED int volume ){} - - /** - * Callback used for asynchronous playback. - * Write tones buffer to the alsa internal ring buffer. - */ - void playTones( void ); + void audioCallback (void); + bool isCaptureActive (void) { return is_capture_running (); } private: @@ -222,29 +149,44 @@ class AlsaLayer : public AudioLayer { // Assignment Operator AlsaLayer& operator=( const AlsaLayer& rh); + bool _is_prepared_playback; + bool _is_prepared_capture; + bool _is_running_capture; + bool _is_running_playback; + bool _is_open_playback; + bool _is_open_capture; + bool _trigger_request; + + bool is_playback_prepared (void) { return _is_prepared_playback; } + bool is_capture_prepared (void) { return _is_prepared_capture; } + void prepare_playback (void) { _is_prepared_playback = true; } + void prepare_capture (void) { _is_prepared_capture = true; } + bool is_capture_running (void) { return _is_running_capture; } + bool is_playback_running (void) { return _is_running_playback; } + void start_playback (void) { _is_running_playback = true; } + void stop_playback (void) { _is_running_playback = false; _is_prepared_playback = false; } + void start_capture (void) { _is_running_capture = true; } + void stop_capture (void) { _is_running_capture = false; _is_prepared_capture = false; } + void close_playback (void) { _is_open_playback = false; } + void close_capture (void) { _is_open_capture = false; } + void open_playback (void) { _is_open_playback = true; } + void open_capture (void) { _is_open_capture = true; } + bool is_capture_open (void) { return _is_open_capture; } + bool is_playback_open (void) { return _is_open_playback; } + /** * Drop the pending frames and close the capture device * ALSA Library API */ void closeCaptureStream( void ); + void stopCaptureStream( void ); + void startCaptureStream( void ); + void prepareCaptureStream( void ); - /** - * Drop the pending frames and close the playback device - * ALSA Library API - */ void closePlaybackStream( void ); - - /** - * Fill the alsa internal ring buffer with chunks of data - */ - void fillHWBuffer( void) ; - - /** - * Callback used for asynchronous playback. - * Called when a certain amount of data is written ot the device - * @param pcm_callback The callback pointer - */ - //static void AlsaCallBack( snd_async_handler_t* pcm_callback); + void stopPlaybackStream( void ); + void startPlaybackStream( void ); + void preparePlaybackStream( void ); /** * Open the specified device. @@ -280,6 +222,8 @@ class AlsaLayer : public AudioLayer { */ int read( void* buffer, int toCopy); + + /** * Recover from XRUN state for capture * ALSA Library API @@ -292,7 +236,9 @@ class AlsaLayer : public AudioLayer { */ void handle_xrun_playback( void ); - /** + void* adjustVolume( void* buffer , int len, int stream ); + +/** * Handles to manipulate playback stream * ALSA Library API */ @@ -309,38 +255,16 @@ class AlsaLayer : public AudioLayer { */ snd_pcm_uframes_t _periodSize; - /** - * Volume is controlled by the application. Data buffer are modified here to adjust to the right volume selected by the user on the main interface - * @param buffer The buffer to adjust - * @param len The number of bytes - * @param stream The stream mode ( PLAYBACK - CAPTURE ) - */ - void * adjustVolume( void * , int , int); - /** * name of the alsa audio plugin used */ std::string _audioPlugin; - /** - * Input channel (mic) should be 1 mono - */ - unsigned int _inChannel; - - /** - * Output channel (stereo) should be 1 mono - */ - unsigned int _outChannel; - - /** - * Default volume for incoming RTP and Urgent sounds. - */ - unsigned short _defaultVolume; // 100 - - /** Vector to manage all soundcard index - description association of the system */ std::vector<HwIDPair> IDSoundCards; + AudioThread *_audioThread; + }; #endif // _ALSA_LAYER_H_ diff --git a/src/audio/audiolayer.cpp b/src/audio/audiolayer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3eea4392c737b51c3f954b438ca3dac7b57a30b9 --- /dev/null +++ b/src/audio/audiolayer.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2009 Savoir-Faire Linux inc. + * Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com> + * + * 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 3 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 "audiolayer.h" + +void AudioLayer::flushMain (void) +{ + ost::MutexLock guard(_mutex); + _voiceRingBuffer.flush(); +} + +void AudioLayer::flushUrgent (void) +{ + ost::MutexLock guard(_mutex); + _urgentRingBuffer.flush(); +} + +void AudioLayer::flushMic (void) +{ + ost::MutexLock guard(_mutex); + _micRingBuffer.flush(); +} + +int AudioLayer::putUrgent(void* buffer, int toCopy) +{ + int a; + + ost::MutexLock guard(_mutex); + a = _urgentRingBuffer.AvailForPut(); + if ( a >= toCopy ) { + return _urgentRingBuffer.Put(buffer, toCopy, _defaultVolume); + } else { + return _urgentRingBuffer.Put(buffer, a, _defaultVolume); + } + return 0; +} + +int AudioLayer::putMain (void *buffer, int toCopy) +{ + int a; + + ost::MutexLock guard(_mutex); + a = _voiceRingBuffer.AvailForPut(); + if ( a >= toCopy ) { + return _voiceRingBuffer.Put(buffer, toCopy, _defaultVolume); + } else { + _debug("Chopping sound, Ouch! RingBuffer full ?\n"); + return _voiceRingBuffer.Put(buffer, a, _defaultVolume); + } + return 0; +} + + diff --git a/src/audio/audiolayer.h b/src/audio/audiolayer.h index 658ac3825deaad2d14fec6c31e547d60648d4ed3..679738b68c7fc15242eff85fd5e52c6068687f2f 100644 --- a/src/audio/audiolayer.h +++ b/src/audio/audiolayer.h @@ -22,24 +22,16 @@ #ifndef _AUDIO_LAYER_H #define _AUDIO_LAYER_H -#include "../global.h" -#include "../manager.h" +#include "global.h" #include "audiodevice.h" #include "ringbuffer.h" +#include "manager.h" #include <cc++/thread.h> // for ost::Mutex -#include <vector> -#include <alsa/asoundlib.h> -#include <pulse/pulseaudio.h> - -#include <iostream> -#include <istream> -#include <sstream> #define FRAME_PER_BUFFER 160 - /** * @file audiolayer.h * @brief Main sound class. Manages the data transfers between the application and the hardware. @@ -47,282 +39,225 @@ class AudioLayer { - private: - - //copy constructor - AudioLayer(const AudioLayer& rh); - - // assignment operator - AudioLayer& operator=(const AudioLayer& rh); - - public: - /** - * Constructor - * @param manager An instance of managerimpl - */ - AudioLayer( ManagerImpl* manager , int type ) - : _layerType( type ) - , _manager(manager) - , _urgentBuffer( SIZEBUF ) - , _talk ( false ) - , deviceClosed ( true ) - , _indexIn ( 0 ) - , _indexOut ( 0 ) - , _sampleRate ( 0 ) - , _frameSize ( 0 ) - , _inChannel( 1 ) - , _outChannel ( 1 ) - , _errorMessage ( 0 ) - , _mutex () + private: + + //copy constructor + AudioLayer(const AudioLayer& rh); + + // assignment operator + AudioLayer& operator=(const AudioLayer& rh); + + public: + /** + * Constructor + * @param manager An instance of managerimpl + */ + AudioLayer( ManagerImpl* manager , int type ) + : _layerType( type ) + , _manager(manager) + , _voiceRingBuffer( SIZEBUF ) + , _urgentRingBuffer( SIZEBUF) + , _micRingBuffer( SIZEBUF ) + , _defaultVolume(100) + , _indexIn ( 0 ) + , _indexOut ( 0 ) + , _sampleRate ( 0 ) + , _frameSize ( 0 ) + , _inChannel( 1 ) + , _outChannel ( 1 ) + , _errorMessage ( 0 ) + , _mutex () { } - - /** - * Destructor - */ - ~AudioLayer(void){} - - virtual void closeLayer( void ) = 0; - - virtual void trigger_thread(void)=0; - - /** - * Check if no devices are opened, otherwise close them. - * Then open the specified devices by calling the private functions open_device - * @param indexIn The number of the card choosen for capture - * @param indexOut The number of the card choosen for playback - * @param sampleRate The sample rate - * @param frameSize The frame size - * @param stream To indicate which kind of stream you want to open - * SFL_PCM_CAPTURE - * SFL_PCM_PLAYBACK - * SFL_PCM_BOTH - * @param plugin The alsa plugin ( dmix , default , front , surround , ...) - */ - virtual bool openDevice(int indexIn, int indexOut, int sampleRate, int frameSize, int stream , std::string plugin) = 0; - - /** - * Start the capture stream and prepare the playback stream. - * The playback starts accordingly to its threshold - * ALSA Library API - */ - virtual void startStream(void) = 0; - - /** - * Stop the playback and capture streams. - * Drops the pending frames and put the capture and playback handles to PREPARED state - * ALSA Library API - */ - virtual void stopStream(void) = 0; - - - /** - * Check if the capture is running - * @return true if the state of the capture handle equals SND_PCM_STATE_RUNNING - * false otherwise - */ - virtual bool isCaptureActive( void ) = 0; - - /** - * Send samples to the audio device. - * @param buffer The buffer containing the data to be played ( voice and DTMF ) - * @param toCopy The number of samples, in bytes - * @param isTalking If whether or not the conversation is running - * @return int The number of bytes played - */ - virtual int playSamples(void* buffer, int toCopy, bool isTalking) = 0; - - /** - * Send a chunk of data to the hardware buffer to start the playback - * Copy data in the urgent buffer. - * @param buffer The buffer containing the data to be played ( ringtones ) - * @param toCopy The size of the buffer - * @return int The number of bytes copied in the urgent buffer - */ - virtual int putUrgent(void* buffer, int toCopy) = 0; - - virtual int putInCache(char code, void* buffer, int toCopy) = 0; - - /** - * Query the capture device for number of bytes available in the hardware ring buffer - * @return int The number of bytes available - */ - virtual int canGetMic() = 0; - - /** - * Get data from the capture device - * @param buffer The buffer for data - * @param toCopy The number of bytes to get - * @return int The number of bytes acquired ( 0 if an error occured) - */ - virtual int getMic(void * buffer, int toCopy) = 0; - - /** - * Scan the sound card available on the system - * @param stream To indicate whether we are looking for capture devices or playback devices - * SFL_PCM_CAPTURE - * SFL_PCM_PLAYBACK - * SFL_PCM_BOTH - * @return std::vector<std::string> The vector containing the string description of the card - */ - virtual std::vector<std::string> getSoundCardsInfo( int stream ) = 0; - - /** - * Check if the given index corresponds to an existing sound card and supports the specified streaming mode - * @param card An index - * @param stream The stream mode - * SFL_PCM_CAPTURE - * SFL_PCM_PLAYBACK - * SFL_PCM_BOTH - * @return bool True if it exists and supports the mode - * false otherwise - */ - virtual bool soundCardIndexExist( int card , int stream ) = 0; - - /** - * An index is associated with its string description - * @param description The string description - * @return int Its index - */ - virtual int soundCardGetIndex( std::string description ) = 0; - - /** - * Get the current audio plugin. - * @return std::string The name of the audio plugin - */ - virtual std::string getAudioPlugin( void ) = 0; - - virtual void reducePulseAppsVolume( void ) = 0; - virtual void restorePulseAppsVolume( void ) = 0; - - virtual void setPlaybackVolume( int volume ) = 0; - virtual void setCaptureVolume( int volume ) = 0; - - /** - * Write accessor to the error state - * @param error The error code - * Could be: ALSA_PLAYBACK_DEVICE - * ALSA_CAPTURE_DEVICE - */ - void setErrorMessage(const int& error) { _errorMessage = error; } - - /** - * Read accessor to the error state - * @return int The error code - */ - int getErrorMessage() { return _errorMessage; } - - /** - * Get the index of the audio card for capture - * @return int The index of the card used for capture - * 0 for the first available card on the system, 1 ... - */ - int getIndexIn() { return _indexIn; } - - /** - * Get the index of the audio card for playback - * @return int The index of the card used for playback - * 0 for the first available card on the system, 1 ... - */ - int getIndexOut() { return _indexOut; } - - /** - * Get the sample rate of the audio layer - * @return unsigned int The sample rate - * default: 44100 HZ - */ - unsigned int getSampleRate() { return _sampleRate; } - - /** - * Get the frame size of the audio layer - * @return unsigned int The frame size - * default: 20 ms - */ - unsigned int getFrameSize() { return _frameSize; } - - /** - * Get the current state. Conversation or not - * @return bool true if playSamples has been called - * false otherwise - */ - bool getCurrentState( void ) { return _talk; } - - int getLayerType( void ) { return _layerType; } - - protected: - - int _layerType; - - /** - * Drop the pending frames and close the capture device - */ - virtual void closeCaptureStream( void ) = 0; - - /** - * Drop the pending frames and close the playback device - */ - virtual void closePlaybackStream( void ) = 0; - - /** Augment coupling, reduce indirect access */ - ManagerImpl* _manager; - - /** - * Urgent ring buffer used for ringtones - */ - RingBuffer _urgentBuffer; - - /** - * Determine if both endpoints hang up. - * true if conversation is running - * false otherwise - */ - bool _talk; - - /** - * Enable to determine if the devices are opened or not - * true if the devices are closed - * false otherwise - */ - bool deviceClosed; - - /** - * Number of audio cards on which capture stream has been opened - */ - int _indexIn; - - /** - * Number of audio cards on which playback stream has been opened - */ - int _indexOut; - - /** - * Sample Rate SFLphone should send sound data to the sound card - * The value can be set in the user config file- now: 44100HZ - */ - unsigned int _sampleRate; - - /** - * Length of the sound frame we capture or read in ms - * The value can be set in the user config file - now: 20ms - */ - unsigned int _frameSize; - - /** - * Input channel (mic) should be 1 mono - */ - unsigned int _inChannel; - - /** - * Output channel (stereo) should be 1 mono - */ - unsigned int _outChannel; - - /** Contains the current error code */ - int _errorMessage; - - ost::Mutex _mutex; + /** + * Destructor + */ + virtual ~AudioLayer(void) {} + + virtual void closeLayer( void ) = 0; + + /** + * Check if no devices are opened, otherwise close them. + * Then open the specified devices by calling the private functions open_device + * @param indexIn The number of the card choosen for capture + * @param indexOut The number of the card choosen for playback + * @param sampleRate The sample rate + * @param frameSize The frame size + * @param stream To indicate which kind of stream you want to open + * SFL_PCM_CAPTURE + * SFL_PCM_PLAYBACK + * SFL_PCM_BOTH + * @param plugin The alsa plugin ( dmix , default , front , surround , ...) + */ + virtual bool openDevice(int indexIn, int indexOut, int sampleRate, int frameSize, int stream , std::string plugin) = 0; + + /** + * Start the capture stream and prepare the playback stream. + * The playback starts accordingly to its threshold + * ALSA Library API + */ + virtual void startStream(void) = 0; + + /** + * Stop the playback and capture streams. + * Drops the pending frames and put the capture and playback handles to PREPARED state + * ALSA Library API + */ + virtual void stopStream(void) = 0; + + /** + * Query the capture device for number of bytes available in the hardware ring buffer + * @return int The number of bytes available + */ + virtual int canGetMic() = 0; + + /** + * Get data from the capture device + * @param buffer The buffer for data + * @param toCopy The number of bytes to get + * @return int The number of bytes acquired ( 0 if an error occured) + */ + virtual int getMic(void * buffer, int toCopy) = 0; + + /** + * Send a chunk of data to the hardware buffer to start the playback + * Copy data in the urgent buffer. + * @param buffer The buffer containing the data to be played ( ringtones ) + * @param toCopy The size of the buffer + * @return int The number of bytes copied in the urgent buffer + */ + int putUrgent(void* buffer, int toCopy); + + /** + * Put voice data in the main sound buffer + * @param buffer The buffer containing the voice data () + * @param toCopy The size of the buffer + * @return int The number of bytes copied + */ + int putMain(void* buffer, int toCopy); + + void flushMain (void); + + void flushUrgent (void); + + /** + * Flush the mic ringbuffer + */ + void flushMic(); + + virtual bool isCaptureActive (void) = 0; + + /** + * Write accessor to the error state + * @param error The error code + * Could be: ALSA_PLAYBACK_DEVICE + * ALSA_CAPTURE_DEVICE + */ + void setErrorMessage(const int& error) { _errorMessage = error; } + + /** + * Read accessor to the error state + * @return int The error code + */ + int getErrorMessage() { return _errorMessage; } + + /** + * Get the index of the audio card for capture + * @return int The index of the card used for capture + * 0 for the first available card on the system, 1 ... + */ + int getIndexIn() { return _indexIn; } + + /** + * Get the index of the audio card for playback + * @return int The index of the card used for playback + * 0 for the first available card on the system, 1 ... + */ + int getIndexOut() { return _indexOut; } + + /** + * Get the sample rate of the audio layer + * @return unsigned int The sample rate + * default: 44100 HZ + */ + unsigned int getSampleRate() { return _sampleRate; } + + /** + * Get the frame size of the audio layer + * @return unsigned int The frame size + * default: 20 ms + */ + unsigned int getFrameSize() { return _frameSize; } + + int getLayerType( void ) { return _layerType; } + + /** + * Default volume for incoming RTP and Urgent sounds. + */ + unsigned short _defaultVolume; // 100 + + protected: + + int _layerType; + + /** + * Drop the pending frames and close the capture device + */ + virtual void closeCaptureStream( void ) = 0; + + /** + * Drop the pending frames and close the playback device + */ + virtual void closePlaybackStream( void ) = 0; + + /** Augment coupling, reduce indirect access */ + ManagerImpl* _manager; + + /** + * Urgent ring buffer used for ringtones + */ + RingBuffer _urgentRingBuffer; + RingBuffer _voiceRingBuffer; + RingBuffer _micRingBuffer; + + /** + * Number of audio cards on which capture stream has been opened + */ + int _indexIn; + + /** + * Number of audio cards on which playback stream has been opened + */ + int _indexOut; + + /** + * Sample Rate SFLphone should send sound data to the sound card + * The value can be set in the user config file- now: 44100HZ + */ + unsigned int _sampleRate; + + /** + * Length of the sound frame we capture or read in ms + * The value can be set in the user config file - now: 20ms + */ + unsigned int _frameSize; + + /** + * Input channel (mic) should be 1 mono + */ + unsigned int _inChannel; + + /** + * Output channel (stereo) should be 1 mono + */ + unsigned int _outChannel; + + /** Contains the current error code */ + int _errorMessage; + + ost::Mutex _mutex; }; #endif // _AUDIO_LAYER_H_ diff --git a/src/audio/audiortp.cpp b/src/audio/audiortp.cpp index c2f3ec87673b1d73b58029c8215bd0cfc3a24b43..ed25feec437a4373e2dc787453a348e3e9bb510a 100644 --- a/src/audio/audiortp.cpp +++ b/src/audio/audiortp.cpp @@ -52,21 +52,22 @@ AudioRtp::~AudioRtp (void) { int AudioRtp::createNewSession (SIPCall *ca) { - ost::MutexLock m(_threadMutex); - - // something should stop the thread before... - if ( _RTXThread != 0 ) { - _debug("! ARTP Failure: Thread already exists..., stopping it\n"); - delete _RTXThread; _RTXThread = 0; - //return -1; - } + ost::MutexLock m(_threadMutex); + + // something should stop the thread before... + if ( _RTXThread != 0 ) { + _debug("**********************************************************\n"); + _debug("! ARTP Failure: Thread already exists..., stopping it\n"); + _debug("**********************************************************\n"); + delete _RTXThread; _RTXThread = 0; + } // Start RTP Send/Receive threads _symmetric = Manager::instance().getConfigInt(SIGNALISATION,SYMMETRIC) ? true : false; _RTXThread = new AudioRtpRTX (ca, _symmetric); try { if (_RTXThread->start() != 0) { - _debug("! ARTP Failure: unable to start RTX Thread\n"); + _debug("! ARTP Failure: unable to start RTX Thread\n"); return -1; } } catch(...) { @@ -88,8 +89,8 @@ AudioRtp::closeRtpSession () { _debugException("! ARTP Exception: when stopping audiortp\n"); throw; } - AudioLayer* audiolayer = Manager::instance().getAudioDriver(); - audiolayer->stopStream(); + //AudioLayer* audiolayer = Manager::instance().getAudioDriver(); + //audiolayer->stopStream(); } //////////////////////////////////////////////////////////////////////////////// @@ -99,7 +100,7 @@ AudioRtpRTX::AudioRtpRTX (SIPCall *sipcall, bool sym) : time(new ost::Time()), _ _sym(sym), micData(NULL), micDataConverted(NULL), micDataEncoded(NULL), spkrDataDecoded(NULL), spkrDataConverted(NULL), converter(NULL), _layerSampleRate(),_codecSampleRate(), _layerFrameSize(), _audiocodec(NULL) { - setCancel(cancelDeferred); + setCancel(cancelDefault); // AudioRtpRTX should be close if we change sample rate // TODO: Change bind address according to user settings. // TODO: this should be the local ip not the external (router) IP @@ -261,7 +262,7 @@ AudioRtpRTX::sendSessionFromMic(int timestamp) int nb_sample_up = nbSample; int nbSamplesMax = _layerFrameSize * _audiocodec->getClockRate() / 1000; - //_debug("resample data\n"); + //_debug("resample data = %i\n", nb_sample_up); nbSample = reSampleData(_audiocodec->getClockRate(), nb_sample_up, DOWN_SAMPLING); if ( nbSample < nbSamplesMax - 10 ) { // if only 10 is missing, it's ok @@ -339,8 +340,10 @@ AudioRtpRTX::receiveSessionForSpkr (int& countTime) #else #endif - audiolayer->playSamples( spkrDataConverted, nbSample * sizeof(SFLDataFormat), true); + //audiolayer->playSamples( spkrDataConverted, nbSample * sizeof(SFLDataFormat), true); + audiolayer->putMain (spkrDataConverted, nbSample * sizeof(SFLDataFormat)); + // Notify (with a beep) an incoming call when there is already a call countTime += time->getSecond(); if (Manager::instance().incomingCallWaiting() > 0) { diff --git a/src/audio/pulselayer.cpp b/src/audio/pulselayer.cpp index 98a6cd6aa54fcf9e3a25a0ae50eb1e263a9f53c5..45fb6edbae5810fec4c1b66935583532857327cf 100644 --- a/src/audio/pulselayer.cpp +++ b/src/audio/pulselayer.cpp @@ -32,14 +32,10 @@ static void audioCallback ( pa_stream* s, size_t bytes, void* userdata ) PulseLayer::PulseLayer(ManagerImpl* manager) : AudioLayer( manager , PULSEAUDIO ) - , _mainSndRingBuffer( SIZEBUF ) - , _urgentRingBuffer( SIZEBUF) - , _micRingBuffer( SIZEBUF ) , context(NULL) , m(NULL) , playback() , record() - , cache() { PulseLayer::streamState = 0; _debug("Pulse audio constructor: Create context\n"); @@ -48,24 +44,31 @@ static void audioCallback ( pa_stream* s, size_t bytes, void* userdata ) // Destructor PulseLayer::~PulseLayer (void) { - _debug(" Destroy pulselayer\n"); - delete playback; - delete record; - pa_context_disconnect(context); - pa_context_unref( context ); + //closeLayer(); + + /* Delete the pointer streams */ + delete playback; + delete record; + + pa_context_disconnect( context ); + pa_context_unref( context ); + + sleep(2); } void PulseLayer::closeLayer( void ) { + _debug(" Destroy pulselayer\n"); + playback->disconnect(); record->disconnect(); while(PulseLayer::streamState != 2) ; PulseLayer::streamState = 0; - pa_context_disconnect( context ); - pa_context_unref( context ); + + //TODO Remove this ugly hack sleep(2); } @@ -89,7 +92,7 @@ PulseLayer::connectPulseAudioServer( void ) } pa_threaded_mainloop_unlock( m ); - serverinfo(); + //serverinfo(); //muteAudioApps(99); _debug("Context creation done\n"); } @@ -139,7 +142,6 @@ PulseLayer::createStreams( pa_context* c ) record = new AudioStream(c, CAPTURE_STREAM, CAPTURE_STREAM_NAME , _manager->getMicVolume()); pa_stream_set_read_callback( record->pulseStream() , audioCallback, this); //pa_stream_set_underflow_callback( record->pulseStream() , underflow , this); - //cache = new AudioStream(c, UPLOAD_STREAM, "Cache samples", _manager->getSpkrVolume()); pa_threaded_mainloop_signal(m , 0); } @@ -179,37 +181,6 @@ PulseLayer::closePlaybackStream( void ) { } - int -PulseLayer::playSamples(void* buffer, int toCopy, bool isTalking UNUSED) -{ - int a = _mainSndRingBuffer.AvailForPut(); - if ( a >= toCopy ) { - return _mainSndRingBuffer.Put(buffer, toCopy, 100); - } else { - _debug("Chopping sound, Ouch! RingBuffer full ?\n"); - return _mainSndRingBuffer.Put(buffer, a, 100); - } - return 0; -} - - void -PulseLayer::flushMain() -{ - _mainSndRingBuffer.flush(); -} - - int -PulseLayer::putUrgent(void* buffer, int toCopy) -{ - int a = _urgentRingBuffer.AvailForPut(); - if ( a >= toCopy ) { - return _urgentRingBuffer.Put(buffer, toCopy, 100 ); - } else { - return _urgentRingBuffer.Put(buffer, a, 100 ); - } - return 0; -} - int PulseLayer::canGetMic() { @@ -229,12 +200,6 @@ PulseLayer::getMic(void *buffer, int toCopy) return 0; } - void -PulseLayer::flushMic() -{ - _micRingBuffer.flush(); -} - void PulseLayer::startStream (void) { @@ -252,6 +217,8 @@ PulseLayer::stopStream (void) pa_stream_flush( playback->pulseStream(), NULL, NULL ); pa_stream_flush( record->pulseStream(), NULL, NULL ); flushMic(); + flushMain(); + flushUrgent(); } @@ -304,13 +271,13 @@ void PulseLayer::writeToSpeaker( void ) if (urgentAvail > 0) { // Urgent data (dtmf, incoming call signal) come first. - //_debug("Play urgent!: %i\n" , urgentAvail); + //_debug("Play urgent!: %i\e" , urgentAvail); toGet = (urgentAvail < (int)(framesPerBuffer * sizeof(SFLDataFormat))) ? urgentAvail : framesPerBuffer * sizeof(SFLDataFormat); out = (SFLDataFormat*)pa_xmalloc(toGet * sizeof(SFLDataFormat) ); _urgentRingBuffer.Get(out, toGet, 100); pa_stream_write( playback->pulseStream() , out , toGet , pa_xfree, 0 , PA_SEEK_RELATIVE); // Consume the regular one as well (same amount of bytes) - _mainSndRingBuffer.Discard(toGet); + _voiceRingBuffer.Discard(toGet); } else { @@ -330,11 +297,11 @@ void PulseLayer::writeToSpeaker( void ) } else { out = (SFLDataFormat*)pa_xmalloc(framesPerBuffer * sizeof(SFLDataFormat)); - normalAvail = _mainSndRingBuffer.AvailForGet(); + normalAvail = _voiceRingBuffer.AvailForGet(); toGet = (normalAvail < (int)(framesPerBuffer * sizeof(SFLDataFormat))) ? normalAvail : framesPerBuffer * sizeof(SFLDataFormat); if (toGet) { - _mainSndRingBuffer.Get(out, toGet, 100); - _mainSndRingBuffer.Discard(toGet); + _voiceRingBuffer.Get(out, toGet, 100); + _voiceRingBuffer.Discard(toGet); } else { bzero(out, framesPerBuffer * sizeof(SFLDataFormat)); @@ -364,15 +331,6 @@ void PulseLayer::readFromMic( void ) } } - int -PulseLayer::putInCache( char code UNUSED, void *buffer UNUSED, int toCopy UNUSED ) -{ - _debug("Put the DTMF in cache\n"); - //pa_stream_write( cache->pulseStream() , buffer , toCopy , pa_xfree, 0 , PA_SEEK_RELATIVE); - //pa_stream_finish_upload( cache->pulseStream() ); - return 1; -} - static void retrieve_server_info(pa_context *c UNUSED, const pa_server_info *i, void *userdata UNUSED) { _debug("Server Info: Process owner : %s\n" , i->user_name); diff --git a/src/audio/pulselayer.h b/src/audio/pulselayer.h index dc22bfaedd968324b232946770ff06b7d08b5428..bdb34085b62e9abea85f27f4517b4f76b701e780 100644 --- a/src/audio/pulselayer.h +++ b/src/audio/pulselayer.h @@ -23,6 +23,8 @@ #include "audiolayer.h" #include "audiostream.h" +#include <pulse/pulseaudio.h> + #define PLAYBACK_STREAM_NAME "SFLphone out" #define CAPTURE_STREAM_NAME "SFLphone in" @@ -36,8 +38,6 @@ class PulseLayer : public AudioLayer { void closeLayer( void ); - void trigger_thread(void){} - /** * Check if no devices are opened, otherwise close them. * Then open the specified devices by calling the private functions open_device @@ -57,28 +57,6 @@ class PulseLayer : public AudioLayer { void stopStream(void); - /** - * UNUSED in pulseaudio layer - */ - bool isCaptureActive( void ) { return true; } - - /** - * UNUSED in pulseaudio layer - */ - bool isStreamActive (void); - - /** - * Flush the main ringbuffer, reserved for the voice - */ - void flushMain(); - - int putUrgent(void* buffer, int toCopy); - - /** - * UNUSED in pulseaudio layer - */ - int putInCache( char code, void* buffer , int toCopy ); - /** * Query the capture device for number of bytes available in the hardware ring buffer * @return int The number of bytes available @@ -93,45 +71,21 @@ class PulseLayer : public AudioLayer { */ int getMic(void *, int); - /** - * Flush the mic ringbuffer - */ - void flushMic(); - - /** - * UNUSED in pulseaudio layer - */ - int playSamples(void* buffer, int toCopy, bool isTalking) ; - - //static void audioCallback ( pa_stream* s, size_t bytes, void* userdata ); static void overflow ( pa_stream* s, void* userdata ); static void underflow ( pa_stream* s, void* userdata ); static void stream_state_callback( pa_stream* s, void* user_data ); static void context_state_callback( pa_context* c, void* user_data ); - /** - * UNUSED in pulseaudio layer - */ - std::vector<std::string> getSoundCardsInfo( int stream UNUSED ) { - std::vector<std::string> tmp; - return tmp; - } + bool isCaptureActive (void){return true;} /** * UNUSED in pulseaudio layer */ - bool soundCardIndexExist( int card UNUSED, int stream UNUSED ) { return true; } - - /** - * UNUSED in pulseaudio layer - */ - int soundCardGetIndex( std::string description UNUSED ) { return 0;} + //std::vector<std::string> getSoundCardsInfo( int stream UNUSED ) { + //std::vector<std::string> tmp; + //return tmp; + //} - /** - * UNUSED in pulseaudio layer - */ - std::string getAudioPlugin( void ) { return "default"; } - /** * Reduce volume of every audio applications connected to the same sink */ @@ -218,21 +172,6 @@ class PulseLayer : public AudioLayer { */ void serverinfo( void ); - /** - * Ringbuffer for incoming voice data (playback) - */ - RingBuffer _mainSndRingBuffer; - - /** - * Ringbuffer for dtmf data - */ - RingBuffer _urgentRingBuffer; - - /** - * Ringbuffer for outgoing voice data (mic) - */ - RingBuffer _micRingBuffer; - /** PulseAudio context and asynchronous loop */ pa_context* context; pa_threaded_mainloop* m; @@ -247,11 +186,6 @@ class PulseLayer : public AudioLayer { */ AudioStream* record; - /** - * A stream object to handle the pulseaudio upload stream - */ - AudioStream* cache; - int spkrVolume; int micVolume; diff --git a/src/dbus/Makefile.am b/src/dbus/Makefile.am index ee8355dbe90728906ece7ac3acf18622f5e9cecb..7e97ffc2dcd3771ed31fc020c23e1eb76b3bad0c 100644 --- a/src/dbus/Makefile.am +++ b/src/dbus/Makefile.am @@ -7,7 +7,6 @@ noinst_LTLIBRARIES = libdbus.la libdbus_la_SOURCES = \ callmanager.cpp \ configurationmanager.cpp \ - contactmanager.cpp \ instance.cpp \ dbusmanagerimpl.cpp @@ -20,8 +19,6 @@ noinst_HEADERS = \ callmanager.h \ configurationmanager.h \ configurationmanager-glue.h \ - contactmanager.h \ - contactmanager-glue.h \ instance.h \ instance-glue.h \ dbusmanager.h \ diff --git a/src/dbus/configurationmanager-glue.h b/src/dbus/configurationmanager-glue.h index 292756cdb7e2c1c6b476b25432d1d56ac29322df..673a0f5f4efe0240bc7d5a90673ae6ac30ba8381 100644 --- a/src/dbus/configurationmanager-glue.h +++ b/src/dbus/configurationmanager-glue.h @@ -73,6 +73,10 @@ public: register_method(ConfigurationManager_adaptor, getPulseAppVolumeControl, _getPulseAppVolumeControl_stub); register_method(ConfigurationManager_adaptor, setSipPort, _setSipPort_stub); register_method(ConfigurationManager_adaptor, getSipPort, _getSipPort_stub); + register_method(ConfigurationManager_adaptor, setStunServer, _setStunServer_stub); + register_method(ConfigurationManager_adaptor, getStunServer, _getStunServer_stub); + register_method(ConfigurationManager_adaptor, enableStun, _enableStun_stub); + register_method(ConfigurationManager_adaptor, isStunEnabled, _isStunEnabled_stub); } ::DBus::IntrospectedInterface *const introspect() const @@ -338,6 +342,25 @@ public: { "port", "i", false }, { 0, 0, 0 } }; + static ::DBus::IntrospectedArgument setStunServer_args[] = + { + { "server", "s", true }, + { 0, 0, 0 } + }; + static ::DBus::IntrospectedArgument getStunServer_args[] = + { + { "server", "s", false }, + { 0, 0, 0 } + }; + static ::DBus::IntrospectedArgument enableStun_args[] = + { + { 0, 0, 0 } + }; + static ::DBus::IntrospectedArgument isStunEnabled_args[] = + { + { "state", "i", false }, + { 0, 0, 0 } + }; static ::DBus::IntrospectedArgument parametersChanged_args[] = { { "list", "a{ss}", false }, @@ -407,6 +430,10 @@ public: { "getPulseAppVolumeControl", getPulseAppVolumeControl_args }, { "setSipPort", setSipPort_args }, { "getSipPort", getSipPort_args }, + { "setStunServer", setStunServer_args }, + { "getStunServer", getStunServer_args }, + { "enableStun", enableStun_args }, + { "isStunEnabled", isStunEnabled_args }, { 0, 0 } }; static ::DBus::IntrospectedMethod ConfigurationManager_adaptor_signals[] = @@ -494,6 +521,10 @@ public: virtual int32_t getPulseAppVolumeControl() = 0; virtual void setSipPort(const int32_t& port) = 0; virtual int32_t getSipPort() = 0; + virtual void setStunServer(const std::string& server) = 0; + virtual std::string getStunServer() = 0; + virtual void enableStun() = 0; + virtual int32_t isStunEnabled() = 0; public: @@ -1027,6 +1058,43 @@ private: wi << argout1; return reply; } + ::DBus::Message _setStunServer_stub(const ::DBus::CallMessage &call) + { + ::DBus::MessageIter ri = call.reader(); + + std::string argin1; ri >> argin1; + setStunServer(argin1); + ::DBus::ReturnMessage reply(call); + return reply; + } + ::DBus::Message _getStunServer_stub(const ::DBus::CallMessage &call) + { + ::DBus::MessageIter ri = call.reader(); + + std::string argout1 = getStunServer(); + ::DBus::ReturnMessage reply(call); + ::DBus::MessageIter wi = reply.writer(); + wi << argout1; + return reply; + } + ::DBus::Message _enableStun_stub(const ::DBus::CallMessage &call) + { + ::DBus::MessageIter ri = call.reader(); + + enableStun(); + ::DBus::ReturnMessage reply(call); + return reply; + } + ::DBus::Message _isStunEnabled_stub(const ::DBus::CallMessage &call) + { + ::DBus::MessageIter ri = call.reader(); + + int32_t argout1 = isStunEnabled(); + ::DBus::ReturnMessage reply(call); + ::DBus::MessageIter wi = reply.writer(); + wi << argout1; + return reply; + } }; } } } diff --git a/src/dbus/configurationmanager-introspec.xml b/src/dbus/configurationmanager-introspec.xml index df72d96e79c6ecf77b4c47ca27c2a4a90b94106c..0990b1443c15a7f53cb2bfee5388885a67e48697 100644 --- a/src/dbus/configurationmanager-introspec.xml +++ b/src/dbus/configurationmanager-introspec.xml @@ -218,6 +218,21 @@ <arg type="i" name="port" direction="out"/> </method> + <method name="setStunServer"> + <arg type="s" name="server" direction="in"/> + </method> + + <method name="getStunServer"> + <arg type="s" name="server" direction="out"/> + </method> + + <method name="enableStun"> + </method> + + <method name="isStunEnabled"> + <arg type="i" name="state" direction="out"/> + </method> + <!-- ///////////////////////////// --> <signal name="parametersChanged"> <arg type="a{ss}" name="list" direction="out"/> diff --git a/src/dbus/configurationmanager.cpp b/src/dbus/configurationmanager.cpp index 547afded01e9912d0e65923bf0ce193ba8830cce..8c1e5af8f5712bae4224d5942a77a6da9d08247e 100644 --- a/src/dbus/configurationmanager.cpp +++ b/src/dbus/configurationmanager.cpp @@ -395,3 +395,22 @@ ConfigurationManager::setSipPort( const int32_t& portNum ) Manager::instance().setSipPort(portNum); } +std::string ConfigurationManager::getStunServer( void ) +{ + return Manager::instance().getStunServer(); +} + +void ConfigurationManager::setStunServer( const std::string& server ) +{ + Manager::instance().setStunServer( server ); +} + +void ConfigurationManager::enableStun (void) +{ + Manager::instance().enableStun(); +} + +int32_t ConfigurationManager::isStunEnabled (void) +{ + return Manager::instance().isStunEnabled(); +} diff --git a/src/dbus/configurationmanager.h b/src/dbus/configurationmanager.h index 86c6ee7dec89ae7352a8ea423ea401cfa4edab8f..20f01317d075b974fe8a85e9927976b854e155a2 100644 --- a/src/dbus/configurationmanager.h +++ b/src/dbus/configurationmanager.h @@ -96,7 +96,11 @@ public: int32_t getPulseAppVolumeControl( void ); void setPulseAppVolumeControl( void ); int32_t getSipPort( void ); - void setSipPort( const int32_t& portNum ); + void setSipPort( const int32_t& portNum); + std::string getStunServer( void ); + void setStunServer( const std::string& server ); + void enableStun (void); + int32_t isStunEnabled (void); }; diff --git a/src/eventthread.cpp b/src/eventthread.cpp index 66e6d441090be9d70a785ecc7fa0ff44a5256b44..b0378c57ec651746988f822f6206254ac6bec2bd 100644 --- a/src/eventthread.cpp +++ b/src/eventthread.cpp @@ -1,8 +1,7 @@ /* - * Copyright (C) 2004, 2005, 2006 Savoir-Faire Linux inc. - * Author: Yan Morin <yan.morin@savoirfairelinux.com> - * Author : Laurielle Lea <laurielle.lea@savoirfairelinux.com> - * + * Copyright (C) 2009 Savoir-Faire Linux inc. + * Emmanuel Milou <emmanuel.milou@savoirfairelinux.com> + * * 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 3 of the License, or @@ -20,44 +19,41 @@ #include "eventthread.h" #include "voiplink.h" +#include "audio/alsalayer.h" -EventThread::EventThread (VoIPLink* link) : Thread (), _linkthread(link), stopIt(false) +/********************************** Voiplink thread *************************************/ +EventThread::EventThread( VoIPLink *link ) + : Thread(), _linkthread(link) { - setCancel(cancelDeferred); + setCancel( cancelDeferred ); } -EventThread::~EventThread (void) -{ - terminate(); -} /** * Reimplementation of run() */ -void -EventThread::run (void) +void EventThread::run (void) { - //stopIt = false; while(!testCancel()) { _linkthread->getEvent(); } -} +} -void -EventThread::stop( void ) -{ - stopIt = true; -} +/********************************************************************************************/ -void -EventThread::startLoop( void ) +AudioThread::AudioThread (AlsaLayer *alsa) + : Thread(), _alsa(alsa) { - stopIt = false; - //start(); + setCancel (cancelDeferred); } -bool -EventThread::isStopped( void ) +/** + * Reimplementation of run() + */ +void AudioThread::run (void) { - return stopIt; + while(!testCancel()) { + _alsa->audioCallback(); + } } + diff --git a/src/eventthread.h b/src/eventthread.h index 8772c72043fec8d719b7c50aecf706dbdb241396..0d631ef066254593e976e1837b1f1e54c2df7594 100644 --- a/src/eventthread.h +++ b/src/eventthread.h @@ -1,7 +1,6 @@ /* - * Copyright (C) 2004-2005 Savoir-Faire Linux inc. - * Author: Yan Morin <yan.morin@savoirfairelinux.com> - * Author : Laurielle Lea <laurielle.lea@savoirfairelinux.com> + * Copyright (C) 2009 Savoir-Faire Linux inc. + * Emmanuel Milou <emmanuel.milou@savoirfairelinux.com> * * 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 @@ -24,6 +23,7 @@ #include <cc++/thread.h> class VoIPLink; +class AlsaLayer; /** * @file eventthread.h @@ -31,24 +31,43 @@ class VoIPLink; */ class EventThread : public ost::Thread { -public: - /** - * Build a thread that call getEvents - */ - EventThread (VoIPLink*); - ~EventThread (void); - virtual void run (); - virtual void stop(); - virtual void startLoop(); - bool isStopped(); - -private: + + public: + /** + * Thread constructor + */ + EventThread (VoIPLink* link); + + ~EventThread (void){ + terminate(); + } + + virtual void run () ; + + private: EventThread(const EventThread& rh); // copy constructor EventThread& operator=(const EventThread& rh); // assignment operator /** VoIPLink is the object being called by getEvents() method */ - VoIPLink* _linkthread; - bool stopIt; + VoIPLink* _linkthread; +}; + +class AudioThread : public ost::Thread { + + public: + AudioThread (AlsaLayer *alsa); + + ~AudioThread (void){ + terminate(); + } + + virtual void run (void); + + private: + AudioThread (const AudioThread& at); + AudioThread& operator=(const AudioThread& at); + + AlsaLayer* _alsa; }; #endif // __EVENT_THREAD_H__ diff --git a/src/global.h b/src/global.h index e752d41b2f5ee4be78392cd0dd67308e19bf2b93..45332563dc0bbb28a0f22a777d997deafa5c32f5 100644 --- a/src/global.h +++ b/src/global.h @@ -26,6 +26,11 @@ #include <stdio.h> #include <libintl.h> #include <locale.h> +#include <string> +#include <stdlib.h> +#include <sstream> +#include <map> +#include <vector> #define SFLPHONED_VERSION "0.9.2-4" /** Version number */ @@ -98,6 +103,7 @@ typedef short int16; #define WINDOW_POPUP 1 /** Popup mode */ #define NOTIFY_ALL 1 /** Desktop notification level 0: never notify */ #define NOTIFY_MAILS 1 /** Desktop mail notification level 0: never notify */ +#define STUN_ENABLED 1 // Error codes for error handling #define NO_ERROR 0x0000 /** No error - Everything alright */ diff --git a/src/iaxaccount.cpp b/src/iaxaccount.cpp index 364f0ab31b028944112bc177ce85e2b45e6e3b81..504a1cc7106dfa7d1c441a64b7a186fc584152bd 100644 --- a/src/iaxaccount.cpp +++ b/src/iaxaccount.cpp @@ -22,8 +22,8 @@ #include "iaxaccount.h" #include "iaxvoiplink.h" - IAXAccount::IAXAccount(const AccountID& accountID) -: Account(accountID) +IAXAccount::IAXAccount(const AccountID& accountID) + : Account(accountID, "iax2") { _link = new IAXVoIPLink(accountID); } @@ -35,22 +35,16 @@ IAXAccount::~IAXAccount() _link = NULL; } - int -IAXAccount::registerVoIPLink() +int IAXAccount::registerVoIPLink() { - IAXVoIPLink *thislink; - _link->init(); - thislink = dynamic_cast<IAXVoIPLink*> (_link); - if (thislink) { - // Stuff needed for IAX registration - thislink->setHost(Manager::instance().getConfigString(_accountID, HOSTNAME)); - thislink->setUser(Manager::instance().getConfigString(_accountID, USERNAME)); - thislink->setPass(Manager::instance().getConfigString(_accountID, PASSWORD)); - } + // Stuff needed for IAX registration + setHostname(Manager::instance().getConfigString(_accountID, HOSTNAME)); + setUsername(Manager::instance().getConfigString(_accountID, USERNAME)); + setPassword(Manager::instance().getConfigString(_accountID, PASSWORD)); - _link->sendRegister(); + _link->sendRegister( _accountID ); return SUCCESS; } @@ -58,7 +52,7 @@ IAXAccount::registerVoIPLink() int IAXAccount::unregisterVoIPLink() { - _link->sendUnregister(); + _link->sendUnregister( _accountID ); _link->terminate(); return SUCCESS; diff --git a/src/iaxvoiplink.cpp b/src/iaxvoiplink.cpp index 6d04dffcaeae225d5d75207136262f3a36705b1c..f085ea1544981952c128c736071261fca0d7ceed 100644 --- a/src/iaxvoiplink.cpp +++ b/src/iaxvoiplink.cpp @@ -18,14 +18,12 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "iaxvoiplink.h" -#include "global.h" // for _debug #include "iaxcall.h" #include "eventthread.h" - +#include "iaxaccount.h" #include "manager.h" #include "audio/audiolayer.h" -//#include <iax/iax-client.h> #include <math.h> #include <dlfcn.h> @@ -40,182 +38,182 @@ #define MUSIC_ONHOLD true #define CHK_VALID_CALL if (call == NULL) { _debug("IAX: Call doesn't exists\n"); \ - return false; } + return false; } - IAXVoIPLink::IAXVoIPLink(const AccountID& accountID) + IAXVoIPLink::IAXVoIPLink(const AccountID& accountID) : VoIPLink(accountID) { - _evThread = new EventThread(this); - _regSession = NULL; - _nextRefreshStamp = 0; + _evThread = new EventThread(this); + _regSession = NULL; + _nextRefreshStamp = 0; - // to get random number for RANDOM_PORT - srand (time(NULL)); + // to get random number for RANDOM_PORT + srand (time(NULL)); - audiolayer = NULL; + audiolayer = NULL; - converter = new SamplerateConverter(); + converter = new SamplerateConverter(); - int nbSamplesMax = (int) ( converter->getFrequence() * converter->getFramesize() / 1000 ); + int nbSamplesMax = (int) ( converter->getFrequence() * converter->getFramesize() / 1000 ); - micData = new SFLDataFormat[nbSamplesMax]; - micDataConverted = new SFLDataFormat[nbSamplesMax]; - micDataEncoded = new unsigned char[nbSamplesMax]; + micData = new SFLDataFormat[nbSamplesMax]; + micDataConverted = new SFLDataFormat[nbSamplesMax]; + micDataEncoded = new unsigned char[nbSamplesMax]; - spkrDataConverted = new SFLDataFormat[nbSamplesMax]; - spkrDataDecoded = new SFLDataFormat[nbSamplesMax]; + spkrDataConverted = new SFLDataFormat[nbSamplesMax]; + spkrDataDecoded = new SFLDataFormat[nbSamplesMax]; } IAXVoIPLink::~IAXVoIPLink() { - delete _evThread; _evThread = NULL; - _regSession = NULL; // shall not delete it - terminate(); + delete _evThread; _evThread = NULL; + _regSession = NULL; // shall not delete it + terminate(); - audiolayer = NULL; + audiolayer = NULL; - delete [] micData; micData = NULL; - delete [] micDataConverted; micDataConverted = NULL; - delete [] micDataEncoded; micDataEncoded = NULL; + delete [] micData; micData = NULL; + delete [] micDataConverted; micDataConverted = NULL; + delete [] micDataEncoded; micDataEncoded = NULL; - delete [] spkrDataDecoded; spkrDataDecoded = NULL; - delete [] spkrDataConverted; spkrDataConverted = NULL; + delete [] spkrDataDecoded; spkrDataDecoded = NULL; + delete [] spkrDataConverted; spkrDataConverted = NULL; } - bool + bool IAXVoIPLink::init() { - // If it was done, don't do it again, until we call terminate() - if (_initDone) - return false; - - bool returnValue = false; - //_localAddress = "127.0.0.1"; - // port 0 is default - // iax_enable_debug(); have to enable debug when compiling iax... - int port = IAX_DEFAULT_PORTNO; - int last_port = 0; - int nbTry = 3; - - while (port != IAX_FAILURE && nbTry) { - last_port = port; - port = iax_init(port); - if ( port < 0 ) { - _debug("IAX Warning: already initialize on port %d\n", last_port); - port = RANDOM_IAX_PORT; - } else if (port == IAX_FAILURE) { - _debug("IAX Fail to start on port %d", last_port); - port = RANDOM_IAX_PORT; - } else { - _debug("IAX Info: listening on port %d\n", last_port); - _localPort = last_port; - returnValue = true; - _evThread->start(); - - audiolayer = Manager::instance().getAudioDriver(); - break; + // If it was done, don't do it again, until we call terminate() + if (initDone()) + return false; + + bool returnValue = false; + //_localAddress = "127.0.0.1"; + // port 0 is default + // iax_enable_debug(); have to enable debug when compiling iax... + int port = IAX_DEFAULT_PORTNO; + int last_port = 0; + int nbTry = 3; + + while (port != IAX_FAILURE && nbTry) { + last_port = port; + port = iax_init(port); + if ( port < 0 ) { + _debug("IAX Warning: already initialize on port %d\n", last_port); + port = RANDOM_IAX_PORT; + } else if (port == IAX_FAILURE) { + _debug("IAX Fail to start on port %d", last_port); + port = RANDOM_IAX_PORT; + } else { + _debug("IAX Info: listening on port %d\n", last_port); + _localPort = last_port; + returnValue = true; + _evThread->start(); + + audiolayer = Manager::instance().getAudioDriver(); + break; + } + nbTry--; + + initDone(true); } - nbTry--; - - _initDone = true; - } - if (port == IAX_FAILURE || nbTry==0) { - _debug("Fail to initialize iax\n"); + if (port == IAX_FAILURE || nbTry==0) { + _debug("Fail to initialize iax\n"); - _initDone = false; - } + initDone(false); + } - return returnValue; + return returnValue; } - void + void IAXVoIPLink::terminate() { - // If it was done, don't do it again, until we call init() - if (!_initDone) - return; + // If it was done, don't do it again, until we call init() + if (!initDone()) + return; - // iaxc_shutdown(); + // iaxc_shutdown(); - // Hangup all calls - terminateIAXCall(); + // Hangup all calls + terminateIAXCall(); - _initDone = false; + initDone(false); } - void + void IAXVoIPLink::terminateIAXCall() { - std::string reason = "Dumped Call"; - ost::MutexLock m(_callMapMutex); - CallMap::iterator iter = _callMap.begin(); - IAXCall *call; - while( iter != _callMap.end() ) { - call = dynamic_cast<IAXCall*>(iter->second); - if (call) { - _mutexIAX.enterMutex(); - iax_hangup(call->getSession(), (char*)reason.c_str()); - _mutexIAX.leaveMutex(); - call->setSession(NULL); - delete call; call = NULL; + std::string reason = "Dumped Call"; + ost::MutexLock m(_callMapMutex); + CallMap::iterator iter = _callMap.begin(); + IAXCall *call; + while( iter != _callMap.end() ) { + call = dynamic_cast<IAXCall*>(iter->second); + if (call) { + _mutexIAX.enterMutex(); + iax_hangup(call->getSession(), (char*)reason.c_str()); + _mutexIAX.leaveMutex(); + call->setSession(NULL); + delete call; call = NULL; + } + iter++; } - iter++; - } - _callMap.clear(); + _callMap.clear(); } - void + void IAXVoIPLink::getEvent() { - IAXCall* call = NULL; - - // lock iax_ stuff.. - _mutexIAX.enterMutex(); - iax_event* event = NULL; + IAXCall* call = NULL; - while ( (event = iax_get_event(IAX_NONBLOCKING)) != NULL ) { - // If we received an 'ACK', libiax2 tells apps to ignore them. - if (event->etype == IAX_EVENT_NULL) { - continue; + // lock iax_ stuff.. + _mutexIAX.enterMutex(); + iax_event* event = NULL; + + while ( (event = iax_get_event(IAX_NONBLOCKING)) != NULL ) { + // If we received an 'ACK', libiax2 tells apps to ignore them. + if (event->etype == IAX_EVENT_NULL) { + continue; + } + + //_debug ("Receive IAX Event: %d (0x%x)\n", event->etype, event->etype); + + call = iaxFindCallBySession(event->session); + + if (call) { + // We know that call, deal with it + iaxHandleCallEvent(event, call); + //_audiocodec = Manager::instance().getCodecDescriptorMap().getCodec( call -> getAudioCodec() ); + } + else if (event->session && event->session == _regSession) { + // This is a registration session, deal with it + iaxHandleRegReply(event); + } + else { + // We've got an event before it's associated with any call + iaxHandlePrecallEvent(event); + } + + iax_event_free(event); } + _mutexIAX.leaveMutex(); - //_debug ("Receive IAX Event: %d (0x%x)\n", event->etype, event->etype); - - call = iaxFindCallBySession(event->session); + // Do the doodle-moodle to send audio from the microphone to the IAX channel. + sendAudioFromMic(); - if (call) { - // We know that call, deal with it - iaxHandleCallEvent(event, call); - //_audiocodec = Manager::instance().getCodecDescriptorMap().getCodec( call -> getAudioCodec() ); - } - else if (event->session && event->session == _regSession) { - // This is a registration session, deal with it - iaxHandleRegReply(event); + // Refresh registration. + if (_nextRefreshStamp && _nextRefreshStamp - 2 < time(NULL)) { + sendRegister(""); } - else { - // We've got an event before it's associated with any call - iaxHandlePrecallEvent(event); - } - - iax_event_free(event); - } - _mutexIAX.leaveMutex(); - // Do the doodle-moodle to send audio from the microphone to the IAX channel. - sendAudioFromMic(); - - // Refresh registration. - if (_nextRefreshStamp && _nextRefreshStamp - 2 < time(NULL)) { - sendRegister(); - } - - // thread wait 3 millisecond - _evThread->sleep(3); - free(event); + // thread wait 3 millisecond + _evThread->sleep(3); + free(event); } - void + void IAXVoIPLink::sendAudioFromMic(void) { int maxBytesToGet, availBytesFromMic, bytesAvail, nbSample, compSize; @@ -226,7 +224,7 @@ IAXVoIPLink::sendAudioFromMic(void) updateAudiolayer(); IAXCall* currentCall = getIAXCall(Manager::instance().getCurrentCallId()); - + if (!currentCall) { // Let's mind our own business. return; @@ -235,546 +233,542 @@ IAXVoIPLink::sendAudioFromMic(void) if( currentCall -> getAudioCodec() < 0 ) return; - // Just make sure the currentCall is in state to receive audio right now. - //_debug("Here we get: connectionState: %d state: %d \n", - //currentCall->getConnectionState(), - //currentCall->getState()); + // Just make sure the currentCall is in state to receive audio right now. + //_debug("Here we get: connectionState: %d state: %d \n", + //currentCall->getConnectionState(), + //currentCall->getState()); - if (currentCall->getConnectionState() != Call::Connected || - currentCall->getState() != Call::Active) { - return; - } + if (currentCall->getConnectionState() != Call::Connected || + currentCall->getState() != Call::Active) { + return; + } - ac = currentCall->getCodecMap().getCodec( currentCall -> getAudioCodec() ); - if (!ac) { - // Audio codec still not determined. - if (audiolayer) { - // To keep latency low.. - //audiolayer->flushMic(); + ac = currentCall->getCodecMap().getCodec( currentCall -> getAudioCodec() ); + if (!ac) { + // Audio codec still not determined. + if (audiolayer) { + // To keep latency low.. + //audiolayer->flushMic(); + } + return; } - return; - } - // Send sound here - if (audiolayer) { + // Send sound here + if (audiolayer) { - // we have to get 20ms of data from the mic *20/1000 = /50 - // rate/50 shall be lower than IAX__20S_48KHZ_MAX - maxBytesToGet = audiolayer->getSampleRate()* audiolayer->getFrameSize() / 1000 * sizeof(SFLDataFormat); - - // available bytes inside ringbuffer - availBytesFromMic = audiolayer->canGetMic(); + // we have to get 20ms of data from the mic *20/1000 = /50 + // rate/50 shall be lower than IAX__20S_48KHZ_MAX + maxBytesToGet = audiolayer->getSampleRate()* audiolayer->getFrameSize() / 1000 * sizeof(SFLDataFormat); - if (availBytesFromMic < maxBytesToGet) { - // We need packets full! - return; - } + // available bytes inside ringbuffer + availBytesFromMic = audiolayer->canGetMic(); - // take the lowest - bytesAvail = (availBytesFromMic < maxBytesToGet) ? availBytesFromMic : maxBytesToGet; - //_debug("available = %d, maxBytesToGet = %d\n", availBytesFromMic, maxBytesToGet); + if (availBytesFromMic < maxBytesToGet) { + // We need packets full! + return; + } - // Get bytes from micRingBuffer to data_from_mic - nbSample = audiolayer->getMic( micData, bytesAvail ) / sizeof(SFLDataFormat); + // take the lowest + bytesAvail = (availBytesFromMic < maxBytesToGet) ? availBytesFromMic : maxBytesToGet; + //_debug("available = %d, maxBytesToGet = %d\n", availBytesFromMic, maxBytesToGet); - // resample - nbSample = converter->downsampleData( micData , micDataConverted , (int)ac ->getClockRate() , (int)audiolayer->getSampleRate() , nbSample ); + // Get bytes from micRingBuffer to data_from_mic + nbSample = audiolayer->getMic( micData, bytesAvail ) / sizeof(SFLDataFormat); - // for the mono: range = 0 to IAX_FRAME2SEND * sizeof(int16) - compSize = ac->codecEncode( micDataEncoded, micDataConverted , nbSample*sizeof(int16)); + // resample + nbSample = converter->downsampleData( micData , micDataConverted , (int)ac ->getClockRate() , (int)audiolayer->getSampleRate() , nbSample ); - // Send it out! - _mutexIAX.enterMutex(); - // Make sure the session and the call still exists. - if (currentCall->getSession()) { - if ( iax_send_voice(currentCall->getSession(), currentCall->getFormat(), micDataEncoded, compSize, nbSample) == -1) { - _debug("IAX: Error sending voice data.\n"); - } + // for the mono: range = 0 to IAX_FRAME2SEND * sizeof(int16) + compSize = ac->codecEncode( micDataEncoded, micDataConverted , nbSample*sizeof(int16)); + + // Send it out! + _mutexIAX.enterMutex(); + // Make sure the session and the call still exists. + if (currentCall->getSession()) { + if (iax_send_voice(currentCall->getSession(), currentCall->getFormat(), micDataEncoded, compSize, nbSample) == -1) { + _debug("IAX: Error sending voice data.\n"); + } + } + _mutexIAX.leaveMutex(); } - _mutexIAX.leaveMutex(); - } } - IAXCall* + IAXCall* IAXVoIPLink::getIAXCall(const CallID& id) { - Call* call = getCall(id); - if (call) { - return dynamic_cast<IAXCall*>(call); - } - return NULL; + Call* call = getCall(id); + if (call) { + return dynamic_cast<IAXCall*>(call); + } + return NULL; } - int -IAXVoIPLink::sendRegister() + int +IAXVoIPLink::sendRegister(AccountID id) { - bool result = false; - if (_host.empty()) { - return false; - } - if (_user.empty()) { - return false; - } - - // lock - _mutexIAX.enterMutex(); - - // Always use a brand new session - if (_regSession) { - iax_destroy(_regSession); - } - - _regSession = iax_session_new(); - - if (!_regSession) { - _debug("Error when generating new session for register"); - } else { - // refresh - // last reg - char host[_host.length()+1]; - strcpy(host, _host.c_str()); - char user[_user.length()+1]; - strcpy(user, _user.c_str()); - char pass[_pass.length()+1]; - strcpy(pass, _pass.c_str()); - // iax_register doesn't use const char* - - _debug("IAX Sending registration to %s with user %s\n", host, user); - int val = iax_register(_regSession, host, user, pass, 120); - _debug ("Return value: %d\n", val); - // set the time-out to 15 seconds, after that, resend a registration request. - // until we unregister. - _nextRefreshStamp = time(NULL) + 10; - result = true; - - setRegistrationState(Trying); - } - - // unlock - _mutexIAX.leaveMutex(); - - return result; -} + IAXAccount *account; + bool result; + + result = false; + account = dynamic_cast<IAXAccount *> (getAccountPtr()); + if (account->getHostname().empty()) { + return false; + } + if (account->getUsername().empty()) { + return false; + } + + // lock + _mutexIAX.enterMutex(); + // Always use a brand new session + if (_regSession) { + iax_destroy(_regSession); + } + _regSession = iax_session_new(); - int -IAXVoIPLink::sendUnregister() + if (!_regSession) { + _debug("Error when generating new session for register"); + } else { + _debug("IAX Sending registration to %s with user %s\n", account->getHostname().c_str() , account->getUsername().c_str() ); + int val = iax_register(_regSession, account->getHostname().data(), account->getUsername().data(), account->getPassword().data(), 120); + _debug ("Return value: %d\n", val); + // set the time-out to 15 seconds, after that, resend a registration request. + // until we unregister. + _nextRefreshStamp = time(NULL) + 10; + result = true; + + account->setRegistrationState(Trying); + } + + // unlock + _mutexIAX.leaveMutex(); + + return result; +} + + int +IAXVoIPLink::sendUnregister(AccountID id) { - _mutexIAX.enterMutex(); - if (_regSession) { - /** @todo Should send a REGREL in sendUnregister()... */ + IAXAccount *account; - //iax_send_regrel(); doesn't exist yet :) - iax_destroy(_regSession); + account = dynamic_cast<IAXAccount*>(getAccountPtr()); + if(!account) + return 1; - _regSession = NULL; - } - _mutexIAX.leaveMutex(); + _mutexIAX.enterMutex(); + if (_regSession) { + /** @todo Should send a REGREL in sendUnregister()... */ + //iax_send_regrel(); doesn't exist yet :) + iax_destroy(_regSession); + _regSession = NULL; + } + _mutexIAX.leaveMutex(); - _nextRefreshStamp = 0; + _nextRefreshStamp = 0; - _debug("IAX2 send unregister\n"); - setRegistrationState(Unregistered); + _debug("IAX2 send unregister\n"); + account->setRegistrationState(Unregistered); - return SUCCESS; + return SUCCESS; } - Call* + Call* IAXVoIPLink::newOutgoingCall(const CallID& id, const std::string& toUrl) { - IAXCall* call = new IAXCall(id, Call::Outgoing); - call->setCodecMap(Manager::instance().getCodecDescriptorMap()); - + IAXCall* call = new IAXCall(id, Call::Outgoing); + call->setCodecMap(Manager::instance().getCodecDescriptorMap()); - if (call) { - call->setPeerNumber(toUrl); - - if ( iaxOutgoingInvite(call) ) { - call->setConnectionState(Call::Progressing); - call->setState(Call::Active); - addCall(call); - } else { - delete call; call = NULL; + if (call) { + call->setPeerNumber(toUrl); + + if ( iaxOutgoingInvite(call) ) { + call->setConnectionState(Call::Progressing); + call->setState(Call::Active); + addCall(call); + } else { + delete call; call = NULL; + } } - } - return call; + return call; } - bool + bool IAXVoIPLink::answer(const CallID& id) { - IAXCall* call = getIAXCall(id); - call->setCodecMap(Manager::instance().getCodecDescriptorMap()); + IAXCall* call = getIAXCall(id); + call->setCodecMap(Manager::instance().getCodecDescriptorMap()); - CHK_VALID_CALL; + CHK_VALID_CALL; - _mutexIAX.enterMutex(); - iax_answer(call->getSession()); - _mutexIAX.leaveMutex(); + _mutexIAX.enterMutex(); + iax_answer(call->getSession()); + _mutexIAX.leaveMutex(); - call->setState(Call::Active); - call->setConnectionState(Call::Connected); - // Start audio - audiolayer->startStream(); - //audiolayer->flushMic(); + call->setState(Call::Active); + call->setConnectionState(Call::Connected); + // Start audio + audiolayer->startStream(); + //audiolayer->flushMic(); - return true; + return true; } - bool + bool IAXVoIPLink::hangup(const CallID& id) { - IAXCall* call = getIAXCall(id); - std::string reason = "Dumped Call"; - CHK_VALID_CALL; - - _mutexIAX.enterMutex(); - iax_hangup(call->getSession(), (char*) reason.c_str()); - _mutexIAX.leaveMutex(); - call->setSession(NULL); - if (Manager::instance().isCurrentCall(id)) { - // stop audio - audiolayer->stopStream(); - } - removeCall(id); - return true; + IAXCall* call = getIAXCall(id); + std::string reason = "Dumped Call"; + CHK_VALID_CALL; + + _mutexIAX.enterMutex(); + iax_hangup(call->getSession(), (char*) reason.c_str()); + _mutexIAX.leaveMutex(); + call->setSession(NULL); + if (Manager::instance().isCurrentCall(id)) { + // stop audio + audiolayer->stopStream(); + } + removeCall(id); + return true; } - bool + bool IAXVoIPLink::onhold(const CallID& id) { - IAXCall* call = getIAXCall(id); + IAXCall* call = getIAXCall(id); - CHK_VALID_CALL; + CHK_VALID_CALL; - //if (call->getState() == Call::Hold) { _debug("Call is already on hold\n"); return false; } + //if (call->getState() == Call::Hold) { _debug("Call is already on hold\n"); return false; } - _mutexIAX.enterMutex(); - iax_quelch_moh(call->getSession() , MUSIC_ONHOLD); - _mutexIAX.leaveMutex(); + _mutexIAX.enterMutex(); + iax_quelch_moh(call->getSession() , MUSIC_ONHOLD); + _mutexIAX.leaveMutex(); - call->setState(Call::Hold); - return true; + call->setState(Call::Hold); + return true; } - bool + bool IAXVoIPLink::offhold(const CallID& id) { - IAXCall* call = getIAXCall(id); + IAXCall* call = getIAXCall(id); - CHK_VALID_CALL; + CHK_VALID_CALL; - //if (call->getState() == Call::Active) { _debug("Call is already active\n"); return false; } - _mutexIAX.enterMutex(); - iax_unquelch(call->getSession()); - _mutexIAX.leaveMutex(); - audiolayer->startStream(); - call->setState(Call::Active); - return true; + //if (call->getState() == Call::Active) { _debug("Call is already active\n"); return false; } + _mutexIAX.enterMutex(); + iax_unquelch(call->getSession()); + _mutexIAX.leaveMutex(); + audiolayer->startStream(); + call->setState(Call::Active); + return true; } - bool + bool IAXVoIPLink::transfer(const CallID& id, const std::string& to) { - IAXCall* call = getIAXCall(id); + IAXCall* call = getIAXCall(id); - CHK_VALID_CALL; + CHK_VALID_CALL; - char callto[to.length()+1]; - strcpy(callto, to.c_str()); + char callto[to.length()+1]; + strcpy(callto, to.c_str()); - _mutexIAX.enterMutex(); - iax_transfer(call->getSession(), callto); - _mutexIAX.leaveMutex(); + _mutexIAX.enterMutex(); + iax_transfer(call->getSession(), callto); + _mutexIAX.leaveMutex(); - return true; + return true; - // should we remove it? - // removeCall(id); + // should we remove it? + // removeCall(id); } - bool + bool IAXVoIPLink::refuse(const CallID& id) { - IAXCall* call = getIAXCall(id); - std::string reason = "Call rejected manually."; + IAXCall* call = getIAXCall(id); + std::string reason = "Call rejected manually."; - CHK_VALID_CALL; + CHK_VALID_CALL; - _mutexIAX.enterMutex(); - iax_reject(call->getSession(), (char*) reason.c_str()); - _mutexIAX.leaveMutex(); - removeCall(id); + _mutexIAX.enterMutex(); + iax_reject(call->getSession(), (char*) reason.c_str()); + _mutexIAX.leaveMutex(); + removeCall(id); - return true; + return true; } - bool + bool IAXVoIPLink::carryingDTMFdigits(const CallID& id, char code) { - IAXCall* call = getIAXCall(id); + IAXCall* call = getIAXCall(id); - CHK_VALID_CALL; + CHK_VALID_CALL; - _mutexIAX.enterMutex(); - iax_send_dtmf(call->getSession(), code); - _mutexIAX.leaveMutex(); + _mutexIAX.enterMutex(); + iax_send_dtmf(call->getSession(), code); + _mutexIAX.leaveMutex(); - return true; + return true; } - bool + bool IAXVoIPLink::iaxOutgoingInvite(IAXCall* call) { - struct iax_session *newsession; - ost::MutexLock m(_mutexIAX); - - newsession = iax_session_new(); - if (!newsession) { - _debug("IAX Error: Can't make new session for a new call\n"); - return false; - } - call->setSession(newsession); - /* reset activity and ping "timers" */ - // iaxc_note_activity(callNo); - - std::string strNum = _user + ":" + _pass + "@" + _host + "/" + call->getPeerNumber(); - - char user[_user.length()+1]; - strcpy(user, _user.c_str()); + struct iax_session *newsession; + ost::MutexLock m(_mutexIAX); + std::string username, strNum; + char *lang=NULL; + int wait, audio_format_preferred, audio_format_capability; + IAXAccount *account; + + newsession = iax_session_new(); + if (!newsession) { + _debug("IAX Error: Can't make new session for a new call\n"); + return false; + } + call->setSession(newsession); - char num[strNum.length()+1]; - strcpy(num, strNum.c_str()); + account = dynamic_cast<IAXAccount*> (getAccountPtr()); + username = account->getUsername(); + strNum = username + ":" + account->getPassword() + "@" + account->getHostname() + "/" + call->getPeerNumber(); - char* lang = NULL; - int wait = 0; - /** @todo Make preference dynamic, and configurable */ - int audio_format_preferred = call->getFirstMatchingFormat(call->getSupportedFormat()); - int audio_format_capability = call->getSupportedFormat(); + wait = 0; + /** @todo Make preference dynamic, and configurable */ + audio_format_preferred = call->getFirstMatchingFormat(call->getSupportedFormat()); + audio_format_capability = call->getSupportedFormat(); - _debug("IAX New call: %s\n", num); - iax_call(newsession, user, user, num, lang, wait, audio_format_preferred, audio_format_capability); + _debug("IAX New call: %s\n", strNum.c_str()); + iax_call(newsession, username.c_str(), username.c_str(), strNum.c_str(), lang, wait, audio_format_preferred, audio_format_capability); - return true; + return true; } - IAXCall* + IAXCall* IAXVoIPLink::iaxFindCallBySession(struct iax_session* session) { - // access to callMap shoud use that - // the code below is like findSIPCallWithCid() - ost::MutexLock m(_callMapMutex); - IAXCall* call = NULL; - CallMap::iterator iter = _callMap.begin(); - while(iter != _callMap.end()) { - call = dynamic_cast<IAXCall*>(iter->second); - if (call && call->getSession() == session) { - return call; + // access to callMap shoud use that + // the code below is like findSIPCallWithCid() + ost::MutexLock m(_callMapMutex); + IAXCall* call = NULL; + CallMap::iterator iter = _callMap.begin(); + while(iter != _callMap.end()) { + call = dynamic_cast<IAXCall*>(iter->second); + if (call && call->getSession() == session) { + return call; + } + iter++; } - iter++; - } - return NULL; // not found + return NULL; // not found } - void + void IAXVoIPLink::iaxHandleCallEvent(iax_event* event, IAXCall* call) { - // call should not be 0 - // note activity? - // - CallID id = call->getCallId(); - - switch(event->etype) { - case IAX_EVENT_HANGUP: - if (Manager::instance().isCurrentCall(id)) { - // stop audio - audiolayer->stopStream(); - } - Manager::instance().peerHungupCall(id); - removeCall(id); - break; - - case IAX_EVENT_REJECT: - //Manager::instance().peerHungupCall(id); - if (Manager::instance().isCurrentCall(id)) { - // stop audio - audiolayer->stopStream(); - } - call->setConnectionState(Call::Connected); - call->setState(Call::Error); - Manager::instance().callFailure(id); - removeCall(id); - break; - - case IAX_EVENT_ACCEPT: - // Call accepted over there by the computer, not the user yet. - if (event->ies.format) { - call->setFormat(event->ies.format); - } - break; - - case IAX_EVENT_ANSWER: - if (call->getConnectionState() != Call::Connected){ - call->setConnectionState(Call::Connected); - call->setState(Call::Active); - - if (event->ies.format) { - // Should not get here, should have been set in EVENT_ACCEPT - call->setFormat(event->ies.format); - } - - Manager::instance().peerAnsweredCall(id); - //audiolayer->flushMic(); - audiolayer->startStream(); - // start audio here? - } else { - // deja connecté ? - } - break; - - case IAX_EVENT_BUSY: - call->setConnectionState(Call::Connected); - call->setState(Call::Busy); - Manager::instance().callBusy(id); - removeCall(id); - break; - - case IAX_EVENT_VOICE: - //_debug("Should have a decent value!!!!!! = %i\n" , call -> getAudioCodec()); - if( !audiolayer -> isCaptureActive()) - audiolayer->startStream(); - iaxHandleVoiceEvent(event, call); - break; - - case IAX_EVENT_TEXT: - break; - - case IAX_EVENT_RINGA: - call->setConnectionState(Call::Ringing); - Manager::instance().peerRingingCall(call->getCallId()); - break; - - case IAX_IE_MSGCOUNT: - break; - case IAX_EVENT_PONG: - break; - - case IAX_EVENT_URL: - break; - - // case IAX_EVENT_CNG: ?? - // break; - - case IAX_EVENT_TIMEOUT: - break; - - case IAX_EVENT_TRANSFER: - break; - - default: - _debug("Unknown event type (in call event): %d\n", event->etype); - - } + // call should not be 0 + // note activity? + // + CallID id = call->getCallId(); + + switch(event->etype) { + case IAX_EVENT_HANGUP: + if (Manager::instance().isCurrentCall(id)) { + // stop audio + audiolayer->stopStream(); + } + Manager::instance().peerHungupCall(id); + removeCall(id); + break; + + case IAX_EVENT_REJECT: + //Manager::instance().peerHungupCall(id); + if (Manager::instance().isCurrentCall(id)) { + // stop audio + audiolayer->stopStream(); + } + call->setConnectionState(Call::Connected); + call->setState(Call::Error); + Manager::instance().callFailure(id); + removeCall(id); + break; + + case IAX_EVENT_ACCEPT: + // Call accepted over there by the computer, not the user yet. + if (event->ies.format) { + call->setFormat(event->ies.format); + } + break; + + case IAX_EVENT_ANSWER: + if (call->getConnectionState() != Call::Connected){ + call->setConnectionState(Call::Connected); + call->setState(Call::Active); + + if (event->ies.format) { + // Should not get here, should have been set in EVENT_ACCEPT + call->setFormat(event->ies.format); + } + + Manager::instance().peerAnsweredCall(id); + //audiolayer->flushMic(); + audiolayer->startStream(); + // start audio here? + } else { + // deja connecté ? + } + break; + + case IAX_EVENT_BUSY: + call->setConnectionState(Call::Connected); + call->setState(Call::Busy); + Manager::instance().callBusy(id); + removeCall(id); + break; + + case IAX_EVENT_VOICE: + if (!audiolayer->isCaptureActive ()) + audiolayer->startStream (); + iaxHandleVoiceEvent(event, call); + break; + + case IAX_EVENT_TEXT: + break; + + case IAX_EVENT_RINGA: + call->setConnectionState(Call::Ringing); + Manager::instance().peerRingingCall(call->getCallId()); + break; + + case IAX_IE_MSGCOUNT: + break; + case IAX_EVENT_PONG: + break; + + case IAX_EVENT_URL: + break; + + // case IAX_EVENT_CNG: ?? + // break; + + case IAX_EVENT_TIMEOUT: + break; + + case IAX_EVENT_TRANSFER: + break; + + default: + _debug("Unknown event type (in call event): %d\n", event->etype); + + } } /* Handle audio event, VOICE packet received */ - void + void IAXVoIPLink::iaxHandleVoiceEvent(iax_event* event, IAXCall* call) { - unsigned char *data; unsigned int size, max, nbInt16; int expandedSize, nbSample; AudioCodec *ac; - // If we receive datalen == 0, some things of the jitter buffer in libiax2/iax.c - // were triggered - if (!event->datalen) { - // Skip this empty packet. - //_debug("IAX: Skipping empty jitter-buffer interpolated packet\n"); - return; - } - - if (audiolayer) { - // On-the-fly codec changing (normally, when we receive a full packet) - // as per http://tools.ietf.org/id/draft-guy-iax-03.txt - // - subclass holds the voiceformat property. - if (event->subclass && event->subclass != call->getFormat()) { - call->setFormat(event->subclass); + // If we receive datalen == 0, some things of the jitter buffer in libiax2/iax.c + // were triggered + if (!event->datalen) { + // Skip this empty packet. + //_debug("IAX: Skipping empty jitter-buffer interpolated packet\n"); + return; } - //_debug("Receive: len=%d, format=%d, _receiveDataDecoded=%p\n", event->datalen, call->getFormat(), _receiveDataDecoded); - ac = call->getCodecMap().getCodec( call -> getAudioCodec() ); - data = (unsigned char*)event->data; - size = event->datalen; + if (audiolayer) { + // On-the-fly codec changing (normally, when we receive a full packet) + // as per http://tools.ietf.org/id/draft-guy-iax-03.txt + // - subclass holds the voiceformat property. + if (event->subclass && event->subclass != call->getFormat()) { + call->setFormat(event->subclass); + } + //_debug("Receive: len=%d, format=%d, _receiveDataDecoded=%p\n", event->datalen, call->getFormat(), _receiveDataDecoded); + ac = call->getCodecMap().getCodec( call -> getAudioCodec() ); - // Decode data with relevant codec - max = (int)( ac->getClockRate() * audiolayer->getFrameSize() / 1000 ); + data = (unsigned char*)event->data; + size = event->datalen; - if (size > max) { - _debug("The size %d is bigger than expected %d. Packet cropped. Ouch!\n", size, max); - size = max; - } + // Decode data with relevant codec + max = (int)( ac->getClockRate() * audiolayer->getFrameSize() / 1000 ); - expandedSize = ac->codecDecode( spkrDataDecoded , data , size ); - nbInt16 = expandedSize/sizeof(int16); + if (size > max) { + _debug("The size %d is bigger than expected %d. Packet cropped. Ouch!\n", size, max); + size = max; + } - if (nbInt16 > max) { - _debug("We have decoded an IAX VOICE packet larger than expected: %i VS %i. Cropping.\n", nbInt16, max); - nbInt16 = max; - } + expandedSize = ac->codecDecode( spkrDataDecoded , data , size ); + nbInt16 = expandedSize/sizeof(int16); - nbSample = nbInt16; - // resample - nbInt16 = converter->upsampleData( spkrDataDecoded , spkrDataConverted , ac->getClockRate() , audiolayer->getSampleRate() , nbSample); + if (nbInt16 > max) { + _debug("We have decoded an IAX VOICE packet larger than expected: %i VS %i. Cropping.\n", nbInt16, max); + nbInt16 = max; + } - audiolayer->playSamples( spkrDataConverted , nbInt16 * sizeof(SFLDataFormat), true); + nbSample = nbInt16; + // resample + nbInt16 = converter->upsampleData( spkrDataDecoded , spkrDataConverted , ac->getClockRate() , audiolayer->getSampleRate() , nbSample); - } else { - _debug("IAX: incoming audio, but no sound card open"); - } + //audiolayer->playSamples( spkrDataConverted , nbInt16 * sizeof(SFLDataFormat), true); + audiolayer->putMain (spkrDataConverted , nbInt16 * sizeof(SFLDataFormat)); + + } else { + _debug("IAX: incoming audio, but no sound card open"); + } } /** * Handle the registration process */ - void + void IAXVoIPLink::iaxHandleRegReply(iax_event* event) { int new_voicemails; std::string account_id; - + IAXAccount *account; + + account_id = getAccountID(); + account = dynamic_cast<IAXAccount *>(Manager::instance().getAccount(account_id)); + if (event->etype == IAX_EVENT_REGREJ) { /* Authentication failed! */ _mutexIAX.enterMutex(); iax_destroy(_regSession); _mutexIAX.leaveMutex(); _regSession = NULL; - setRegistrationState(ErrorAuth); + // Update the account registration state + account->setRegistrationState(ErrorAuth); } - + else if (event->etype == IAX_EVENT_REGACK) { /* Authentication succeeded */ _mutexIAX.enterMutex(); // Looking for the voicemail information //if( event->ies != 0 ) - new_voicemails = processIAXMsgCount(event->ies.msgcount); - _debug("iax voicemail number notification: %i\n", new_voicemails); + //new_voicemails = processIAXMsgCount(event->ies.msgcount); + //_debug("iax voicemail number notification: %i\n", new_voicemails); // Notify the client if new voicemail waiting for the current account - account_id = getAccountID(); - Manager::instance().startVoiceMessageNotification(account_id.c_str(), new_voicemails); + //account_id = getAccountID(); + //Manager::instance().startVoiceMessageNotification(account_id.c_str(), new_voicemails); iax_destroy(_regSession); _mutexIAX.leaveMutex(); @@ -783,13 +777,13 @@ IAXVoIPLink::iaxHandleRegReply(iax_event* event) // I mean, save the timestamp, so that we re-register again in the REFRESH time. // Defaults to 60, as per draft-guy-iax-03. _nextRefreshStamp = time(NULL) + (event->ies.refresh ? event->ies.refresh : 60); - setRegistrationState(Registered); + account->setRegistrationState(Registered); } } int IAXVoIPLink::processIAXMsgCount( int msgcount ) { - + // IAX sends the message count under a specific format: // 1 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 @@ -803,120 +797,112 @@ int IAXVoIPLink::processIAXMsgCount( int msgcount ) // Thus: // 0 <= msgcount <= 255 => msgcount new messages, 0 old messages // msgcount >= 256 => msgcount/256 old messages , msgcount%256 new messages (RULES) - + return msgcount%256; } - void + void IAXVoIPLink::iaxHandlePrecallEvent(iax_event* event) { - IAXCall* call = NULL; - CallID id; - std::string reason = "Error ringing user."; - - switch(event->etype) { - case IAX_EVENT_REGACK: - case IAX_EVENT_REGREJ: - _debug("IAX Registration Event in a pre-call setup\n"); - break; - - case IAX_EVENT_REGREQ: - // Received when someone wants to register to us!?! - // Asterisk receives and answers to that, not us, we're a phone. - _debug("Registration by a peer, don't allow it\n"); - break; + IAXCall* call = NULL; + CallID id; + std::string reason = "Error ringing user."; - case IAX_EVENT_CONNECT: - // We've got an incoming call! Yikes! - _debug("> IAX_EVENT_CONNECT (receive)\n"); + switch(event->etype) { + case IAX_EVENT_REGACK: + case IAX_EVENT_REGREJ: + _debug("IAX Registration Event in a pre-call setup\n"); + break; - id = Manager::instance().getNewCallID(); + case IAX_EVENT_REGREQ: + // Received when someone wants to register to us!?! + // Asterisk receives and answers to that, not us, we're a phone. + _debug("Registration by a peer, don't allow it\n"); + break; + case IAX_EVENT_CONNECT: + // We've got an incoming call! Yikes! + _debug("> IAX_EVENT_CONNECT (receive)\n"); - call = new IAXCall(id, Call::Incoming); + id = Manager::instance().getNewCallID(); - if (!call) { - _debug("! IAX Failure: unable to create an incoming call"); - return; - } + call = new IAXCall(id, Call::Incoming); - // Setup the new IAXCall - // Associate the call to the session. - call->setSession(event->session); + if (!call) { + _debug("! IAX Failure: unable to create an incoming call"); + return; + } - // setCallAudioLocal(call); - call->setCodecMap(Manager::instance().getCodecDescriptorMap()); - call->setConnectionState(Call::Progressing); + // Setup the new IAXCall + // Associate the call to the session. + call->setSession(event->session); + // setCallAudioLocal(call); + call->setCodecMap(Manager::instance().getCodecDescriptorMap()); + call->setConnectionState(Call::Progressing); - if (event->ies.calling_number) - call->setPeerNumber(std::string(event->ies.calling_number)); - if (event->ies.calling_name) - call->setPeerName(std::string(event->ies.calling_name)); - if (Manager::instance().incomingCall(call, getAccountID())) { - /** @todo Faudra considérer éventuellement le champ CODEC PREFS pour - * l'établissement du codec de transmission */ + if (event->ies.calling_number) + call->setPeerNumber(std::string(event->ies.calling_number)); + if (event->ies.calling_name) + call->setPeerName(std::string(event->ies.calling_name)); - // Remote lists its capabilities - int format = call->getFirstMatchingFormat(event->ies.capability); - // Remote asks for preferred codec voiceformat - int pref_format = call->getFirstMatchingFormat(event->ies.format); + if (Manager::instance().incomingCall(call, getAccountID())) { + /** @todo Faudra considérer éventuellement le champ CODEC PREFS pour + * l'établissement du codec de transmission */ - // Priority to remote's suggestion. In case it's a forwarding, no transcoding - // will be needed from the server, thus less latency. - if (pref_format) - format = pref_format; + // Remote lists its capabilities + int format = call->getFirstMatchingFormat(event->ies.capability); + // Remote asks for preferred codec voiceformat + int pref_format = call->getFirstMatchingFormat(event->ies.format); - iax_accept(event->session, format); - iax_ring_announce(event->session); + // Priority to remote's suggestion. In case it's a forwarding, no transcoding + // will be needed from the server, thus less latency. + if (pref_format) + format = pref_format; - addCall(call); - } else { - // reject call, unable to add it - iax_reject(event->session, (char*)reason.c_str()); + iax_accept(event->session, format); + iax_ring_announce(event->session); - delete call; call = NULL; - } + addCall(call); + } else { + // reject call, unable to add it + iax_reject(event->session, (char*)reason.c_str()); - break; + delete call; call = NULL; + } - case IAX_EVENT_HANGUP: - // Remote peer hung up - call = iaxFindCallBySession(event->session); - id = call->getCallId(); + break; - Manager::instance().peerHungupCall(id); - removeCall(id); - break; + case IAX_EVENT_HANGUP: + // Remote peer hung up + call = iaxFindCallBySession(event->session); + id = call->getCallId(); - case IAX_EVENT_TIMEOUT: // timeout for an unknown session + Manager::instance().peerHungupCall(id); + removeCall(id); + break; - break; + case IAX_EVENT_TIMEOUT: // timeout for an unknown session - case IAX_IE_MSGCOUNT: - //_debug("messssssssssssssssssssssssssssssssssssssssssssssssages\n"); - break; + break; - default: - _debug("Unknown event type (in precall): %d\n", event->etype); - } + case IAX_IE_MSGCOUNT: + //_debug("messssssssssssssssssssssssssssssssssssssssssssssssages\n"); + break; -} + default: + _debug("Unknown event type (in precall): %d\n", event->etype); + } - int -IAXVoIPLink::iaxCodecMapToFormat(IAXCall* call) -{ - CodecOrder map = call->getCodecMap().getActiveCodecs(); - printf("taytciatcia = %i\n", map.size()); - return 0; } - void IAXVoIPLink::updateAudiolayer( void ) { + _mutexIAX.enterMutex(); audiolayer = NULL; audiolayer = Manager::instance().getAudioDriver(); + _mutexIAX.leaveMutex(); } diff --git a/src/iaxvoiplink.h b/src/iaxvoiplink.h index fe8978c1b390724ea8a8d2e172a4e15ee359fc25..b82949f640760b474913204c596616018ece1c98 100644 --- a/src/iaxvoiplink.h +++ b/src/iaxvoiplink.h @@ -76,7 +76,7 @@ class IAXVoIPLink : public VoIPLink * Send out registration * @return bool The new registration state (are we registered ?) */ - int sendRegister (void); + int sendRegister (AccountID id); /** * Destroy registration session @@ -85,7 +85,7 @@ class IAXVoIPLink : public VoIPLink * @return bool true if we're registered upstream * false otherwise */ - int sendUnregister (void); + int sendUnregister (AccountID id); /** * Create a new outgoing call @@ -166,23 +166,11 @@ class IAXVoIPLink : public VoIPLink bool isContactPresenceSupported() { return false; } public: // iaxvoiplink only - /** - * @param host Set the host name - */ - void setHost(const std::string& host) { _host = host; } - - /** - * @param user Set the user name - */ - void setUser(const std::string& user) { _user = user; } - - /** - * @param pass Set the password - */ - void setPass(const std::string& pass) { _pass = pass; } - + void updateAudiolayer( void ); + void setStunServer( const std::string &server ) {}; + private: /* @@ -192,7 +180,7 @@ class IAXVoIPLink : public VoIPLink * @param msgcount The value sent by IAX in the REGACK message * @return int The number of new messages waiting for the current registered user */ - int processIAXMsgCount( int msgcount ); + int processIAXMsgCount( int msgcount ); /** @@ -251,28 +239,12 @@ class IAXVoIPLink : public VoIPLink */ bool iaxOutgoingInvite(IAXCall* call); - - /** - * Convert CodecMap to IAX format using IAX constants - * @return `format` ready to go into iax_* calls - */ - int iaxCodecMapToFormat(IAXCall* call); - /** Threading object */ EventThread* _evThread; /** registration session : 0 if not register */ struct iax_session* _regSession; - /** IAX Host */ - std::string _host; - - /** IAX User */ - std::string _user; - - /** IAX Password */ - std::string _pass; - /** Timestamp of when we should refresh the registration up with * the registrar. Values can be: EPOCH timestamp, 0 if we want no registration, 1 * to force a registration. */ diff --git a/src/managerimpl.cpp b/src/managerimpl.cpp index c6177f35fc920738250824a767cd0f07635b32f2..e5292532c720e874939a541b2bc6c4ee436bc915 100644 --- a/src/managerimpl.cpp +++ b/src/managerimpl.cpp @@ -46,15 +46,8 @@ #include "accountcreator.h" // create new account #include "sipvoiplink.h" -#include "useragent.h" - #include "user_cfg.h" -#ifdef USE_ZEROCONF -#include "zeroconf/DNSService.h" -#include "zeroconf/DNSServiceTXTRecord.h" -#endif - #define fill_config_str(name, value) \ (_config.addConfigTreeItem(section, Conf::ConfigTreeItem(std::string(name), std::string(value), type_str))) #define fill_config_int(name, value) \ @@ -88,26 +81,10 @@ ManagerImpl::ManagerImpl (void) , _callAccountMap() , _callAccountMapMutex() , _accountMap() - , _userAgent(NULL) - , _userAgentInitlized(false) - , _sipThreadStop() - { - /* Init private variables - setup: _path, _exist, _setupLoaded , _dbus - sound: _audiodriver, _dtmfKey, - _spkr_volume,_mic_volume = 0; // Initialize after by init() -> initVolume() - Call: _nbIncomingWaitingCall, _hasTriedToRegister - SIP Link: _userAgent, _userAgentInitlized - */ - -#ifdef USE_ZEROCONF - _hasZeroconf = true; - _DNSService = new DNSService(); -#endif - - // initialize random generator for call id - srand (time(NULL)); + + // initialize random generator for call id + srand (time(NULL)); #ifdef TEST testAccountMap(); @@ -123,93 +100,68 @@ ManagerImpl::ManagerImpl (void) // never call if we use only the singleton... ManagerImpl::~ManagerImpl (void) { - terminate(); - -#ifdef USE_ZEROCONF - delete _DNSService; _DNSService = 0; -#endif - - _debug("%s stop correctly.\n", PROGNAME); + terminate(); + _debug("%s stop correctly.\n", PROGNAME); } void ManagerImpl::init() { - // Load accounts, init map - loadAccountMap(); + // Load accounts, init map + loadAccountMap(); - //Initialize sip manager - if(_userAgentInitlized) { - _userAgent->sipCreate(); - _userAgent->sipInit(); - } - - initVolume(); - - if (_exist == 0) { - _debug("Cannot create config file in your home directory\n"); - } - - initAudioDriver(); - selectAudioDriver(); + initVolume(); - // Initialize the list of supported audio codecs - initAudioCodec(); + if (_exist == 0) { + _debug("Cannot create config file in your home directory\n"); + } - getAudioInputDeviceList(); + initAudioDriver(); + selectAudioDriver(); - AudioLayer *audiolayer = getAudioDriver(); - if (audiolayer!=0) { - unsigned int sampleRate = audiolayer->getSampleRate(); + // Initialize the list of supported audio codecs + initAudioCodec(); - _debugInit("Load Telephone Tone"); - std::string country = getConfigString(PREFERENCES, ZONE_TONE); - _telephoneTone = new TelephoneTone(country, sampleRate); + AudioLayer *audiolayer = getAudioDriver(); + + if (audiolayer!=0) { + unsigned int sampleRate = audiolayer->getSampleRate(); - _debugInit("Loading DTMF key"); - _dtmfKey = new DTMF(sampleRate); - } + _debugInit("Load Telephone Tone"); + std::string country = getConfigString(PREFERENCES, ZONE_TONE); + _telephoneTone = new TelephoneTone(country, sampleRate); - // initRegisterAccounts was here, but we doing it after the gui loaded... - // the stun detection is long, so it's a better idea to do it after getEvents - initZeroconf(); - + _debugInit("Loading DTMF key"); + _dtmfKey = new DTMF(sampleRate); + } } void ManagerImpl::terminate() { - saveConfig(); + saveConfig(); - unloadAccountMap(); + unloadAccountMap(); - if(_userAgentInitlized) { - delete _userAgent; - _userAgent = NULL; - _userAgentInitlized = false; - } - - _debug("Unload DTMF Key\n"); - delete _dtmfKey; + _debug("Unload DTMF Key\n"); + delete _dtmfKey; - _debug("Unload Audio Driver\n"); - delete _audiodriver; _audiodriver = NULL; + _debug("Unload Audio Driver\n"); + delete _audiodriver; _audiodriver = NULL; - _debug("Unload Telephone Tone\n"); - delete _telephoneTone; _telephoneTone = NULL; + _debug("Unload Telephone Tone\n"); + delete _telephoneTone; _telephoneTone = NULL; - _debug("Unload Audio Codecs\n"); - _codecDescriptorMap.deleteHandlePointer(); + _debug("Unload Audio Codecs\n"); + _codecDescriptorMap.deleteHandlePointer(); } bool ManagerImpl::isCurrentCall(const CallID& callId) { - ost::MutexLock m(_currentCallMutex); return (_currentCallId2 == callId ? true : false); } bool ManagerImpl::hasCurrentCall() { - ost::MutexLock m(_currentCallMutex); _debug("Current call ID = %s\n", _currentCallId2.c_str()); if ( _currentCallId2 != "") { return true; @@ -219,7 +171,6 @@ ManagerImpl::hasCurrentCall() { const CallID& ManagerImpl::getCurrentCallId() { - ost::MutexLock m(_currentCallMutex); return _currentCallId2; } @@ -237,27 +188,31 @@ ManagerImpl::switchCall(const CallID& id ) { bool ManagerImpl::outgoingCall(const std::string& accountid, const CallID& id, const std::string& to) { - if (!accountExists(accountid)) { - _debug("! Manager Error: Outgoing Call: account doesn't exist\n"); - return false; - } - if (getAccountFromCall(id) != AccountNULL) { - _debug("! Manager Error: Outgoing Call: call id already exists\n"); - return false; - } - if (hasCurrentCall()) { - _debug("* Manager Info: there is currently a call, try to hold it\n"); - onHoldCall(getCurrentCallId()); - } - _debug("- Manager Action: Adding Outgoing Call %s on account %s\n", id.data(), accountid.data()); - if ( getAccountLink(accountid)->newOutgoingCall(id, to) ) { + if (!accountExists(accountid)) { + _debug("! Manager Error: Outgoing Call: account doesn't exist\n"); + return false; + } + + if (getAccountFromCall(id) != AccountNULL) { + _debug("! Manager Error: Outgoing Call: call id already exists\n"); + return false; + } + + if (hasCurrentCall()) { + _debug("* Manager Info: there is currently a call, try to hold it\n"); + onHoldCall(getCurrentCallId()); + } + + _debug("- Manager Action: Adding Outgoing Call %s on account %s\n", id.data(), accountid.data()); associateCallToAccount( id, accountid ); - switchCall(id); - return true; - } else { - _debug("! Manager Error: An error occur, the call was not created\n"); - } - return false; + if ( getAccountLink(accountid)->newOutgoingCall(id, to) ) { + switchCall(id); + return true; + } else { + callFailure(id); + _debug("! Manager Error: An error occur, the call was not created\n"); + } + return false; } //THREAD=Main : for outgoing Call @@ -294,26 +249,35 @@ ManagerImpl::answerCall(const CallID& id) bool ManagerImpl::hangupCall(const CallID& id) { - stopTone(true); - if (_dbus) _dbus->getCallManager()->callStateChanged(id, "HUNGUP"); - AccountID accountid = getAccountFromCall( id ); - if (accountid == AccountNULL) { - /** @todo We should tell the GUI that the call doesn't exist, so - * it clears up. This can happen. */ - _debug("! Manager Hangup Call: Call doesn't exists\n"); - return false; - } + PulseLayer *pulselayer; + AccountID accountid; + bool returnValue; - bool returnValue = getAccountLink(accountid)->hangup(id); - _debug("After voip link hungup!\n"); - removeCallAccount(id); - switchCall(""); + stopTone(true); - if( getConfigInt( PREFERENCES , CONFIG_PA_VOLUME_CTRL ) ) - _audiodriver->restorePulseAppsVolume(); + /* Broadcast a signal over DBus */ + if (_dbus) _dbus->getCallManager()->callStateChanged(id, "HUNGUP"); + + accountid = getAccountFromCall( id ); + if (accountid == AccountNULL) { + /** @todo We should tell the GUI that the call doesn't exist, so + * it clears up. This can happen. */ + _debug("! Manager Hangup Call: Call doesn't exists\n"); + return false; + } - _debug("Before hungup return!\n"); - return returnValue; + returnValue = getAccountLink(accountid)->hangup(id); + + _debug("After voip link hungup!\n"); + removeCallAccount(id); + switchCall(""); + + if( _audiodriver->getLayerType() == PULSEAUDIO && getConfigInt( PREFERENCES , CONFIG_PA_VOLUME_CTRL ) ) { + pulselayer = dynamic_cast<PulseLayer *> (getAudioDriver()); + if(pulselayer) pulselayer->restorePulseAppsVolume(); + } + + return returnValue; } //THREAD=Main @@ -440,7 +404,6 @@ ManagerImpl::saveConfig (void) int ManagerImpl::initRegisterAccounts() { - int status; bool flag = true; AccountMap::iterator iter; @@ -448,17 +411,21 @@ ManagerImpl::initRegisterAccounts() _debugInit("Initiate VoIP Links Registration"); iter = _accountMap.begin(); + /* Loop on the account map previously loaded */ while( iter != _accountMap.end() ) { - if ( iter->second ) { - iter->second->loadConfig(); - if ( iter->second->isEnabled() ) { - status = iter->second->registerVoIPLink(); - if (status != SUCCESS) - flag = false; + if ( iter->second ) { + iter->second->loadConfig(); + /* If the account is set as enabled, try to register */ + if ( iter->second->isEnabled() ) { + status = iter->second->registerVoIPLink(); + if (status != SUCCESS){ + flag = false; + } + } } - } - iter++; + iter++; } + // calls the client notification here in case of errors at startup... if( _audiodriver -> getErrorMessage() != -1 ) notifyErrClient( _audiodriver -> getErrorMessage() ); @@ -500,68 +467,63 @@ ManagerImpl::sendDtmf(const CallID& id, char code) bool ManagerImpl::playDtmf(char code, bool isTalking) { - // HERE are the variable: - // - boolean variable to play or not (config) - // - length in milliseconds to play - // - sample of audiolayer - stopTone(false); - int hasToPlayTone = getConfigInt(SIGNALISATION, PLAY_DTMF); - if (!hasToPlayTone) return false; - - // length in milliseconds - int pulselen = getConfigInt(SIGNALISATION, PULSE_LENGTH); - if (!pulselen) { return false; } - - // numbers of int = length in milliseconds / 1000 (number of seconds) - // = number of seconds * SAMPLING_RATE by SECONDS - AudioLayer* audiolayer = getAudioDriver(); - int layer = audiolayer->getLayerType(); - - // fast return, no sound, so no dtmf - if (audiolayer==0 || _dtmfKey == 0) { return false; } - // number of data sampling in one pulselen depends on samplerate - // size (n sampling) = time_ms * sampling/s - // --------------------- - // ms/s - int size = (int)(pulselen * ((float)audiolayer->getSampleRate()/1000)); - - // this buffer is for mono - // TODO <-- this should be global and hide if same size - SFLDataFormat* _buf = new SFLDataFormat[size]; - bool returnValue = false; - - // Handle dtmf - _dtmfKey->startTone(code); + int hasToPlayTone, pulselen, layer, size; + bool ret = false; + AudioLayer *audiolayer; + SFLDataFormat *buf; + + stopTone(false); + + hasToPlayTone = getConfigInt(SIGNALISATION, PLAY_DTMF); + if (!hasToPlayTone) + return false; - // copy the sound - if ( _dtmfKey->generateDTMF(_buf, size) ) { + // length in milliseconds + pulselen = getConfigInt(SIGNALISATION, PULSE_LENGTH); + if (!pulselen) + return false; - // Put buffer to urgentRingBuffer - // put the size in bytes... - // so size * 1 channel (mono) * sizeof (bytes for the data) - if(CHECK_INTERFACE( layer , ALSA )) - audiolayer->playSamples(_buf, size * sizeof(SFLDataFormat), isTalking); - else - _debug("DTMF disabled\n"); - audiolayer->putUrgent( _buf, size * sizeof(SFLDataFormat) ); + // numbers of int = length in milliseconds / 1000 (number of seconds) + // = number of seconds * SAMPLING_RATE by SECONDS + audiolayer = getAudioDriver(); + layer = audiolayer->getLayerType(); + + // fast return, no sound, so no dtmf + if (audiolayer==0 || _dtmfKey == 0) + return false; + + // number of data sampling in one pulselen depends on samplerate + // size (n sampling) = time_ms * sampling/s + // --------------------- + // ms/s + size = (int)(pulselen * ((float)audiolayer->getSampleRate()/1000)); + + // this buffer is for mono + // TODO <-- this should be global and hide if same size + buf = new SFLDataFormat[size]; + + // Handle dtmf + _dtmfKey->startTone(code); + + // copy the sound + if ( _dtmfKey->generateDTMF(buf, size) ) { + // Put buffer to urgentRingBuffer + // put the size in bytes... + // so size * 1 channel (mono) * sizeof (bytes for the data) + audiolayer->putUrgent (buf, size * sizeof(SFLDataFormat)); + } + ret = true; - } - returnValue = true; + // TODO Cache the DTMF - if( CHECK_INTERFACE( layer , PULSEAUDIO )) - { - // Cache the samples on the sound server - // (PulseLayer*)audiolayer->putInCache( code, _buf , size * sizeof(SFLDataFormat) ); - } - - delete[] _buf; _buf = 0; - return returnValue; + delete[] buf; buf = 0; + + return ret; } // Multi-thread bool ManagerImpl::incomingCallWaiting() { - ost::MutexLock m(_waitingCallMutex); return (_nbIncomingWaitingCall > 0) ? true : false; } @@ -583,7 +545,6 @@ ManagerImpl::removeWaitingCall(const CallID& id) { bool ManagerImpl::isWaitingCall(const CallID& id) { - ost::MutexLock m(_waitingCallMutex); CallIDSet::iterator iter = _waitingCall.find(id); if (iter != _waitingCall.end()) { return false; @@ -598,38 +559,44 @@ ManagerImpl::isWaitingCall(const CallID& id) { bool ManagerImpl::incomingCall(Call* call, const AccountID& accountId) { - _debug("Incoming call %s\n", call->getCallId().data()); + PulseLayer *pulselayer; + std::string from, number; - associateCallToAccount(call->getCallId(), accountId); + _debug("Incoming call %s\n", call->getCallId().data()); - if ( !hasCurrentCall() ) { - call->setConnectionState(Call::Ringing); - ringtone(); - switchCall(call->getCallId()); - } else { - addWaitingCall(call->getCallId()); - } + associateCallToAccount(call->getCallId(), accountId); - std::string from = call->getPeerName(); - std::string number = call->getPeerNumber(); + if ( !hasCurrentCall() ) { + call->setConnectionState(Call::Ringing); + ringtone(); + switchCall(call->getCallId()); + } else { + addWaitingCall(call->getCallId()); + } - if (from != "" && number != "") { - from.append(" <"); - from.append(number); - from.append(">"); - } else if ( from.empty() ) { - from.append("<"); - from.append(number); - from.append(">"); - } + from = call->getPeerName(); + number = call->getPeerNumber(); + + if (from != "" && number != "") { + from.append(" <"); + from.append(number); + from.append(">"); + } else if ( from.empty() ) { + from.append("<"); + from.append(number); + from.append(">"); + } - _dbus->getCallManager()->incomingCall(accountId, call->getCallId(), from); + /* Broadcast a signal over DBus */ + _dbus->getCallManager()->incomingCall(accountId, call->getCallId(), from); - // Reduce volume of the other pulseaudio-connected audio applications - if( getConfigInt( PREFERENCES , CONFIG_PA_VOLUME_CTRL ) ) - _audiodriver->reducePulseAppsVolume(); + // Reduce volume of the other pulseaudio-connected audio applications + if( _audiodriver->getLayerType() == PULSEAUDIO && getConfigInt( PREFERENCES , CONFIG_PA_VOLUME_CTRL ) ) { + pulselayer = dynamic_cast<PulseLayer *> (getAudioDriver()); + if(pulselayer) pulselayer->reducePulseAppsVolume(); + } - return true; + return true; } //THREAD=VoIP @@ -664,22 +631,30 @@ ManagerImpl::peerRingingCall(const CallID& id) void ManagerImpl::peerHungupCall(const CallID& id) { - AccountID accountid = getAccountFromCall( id ); - if (accountid == AccountNULL) { - _debug("peerHungupCall: Call doesn't exists\n"); - return; - } + PulseLayer *pulselayer; + AccountID accountid; + + accountid = getAccountFromCall( id ); + if (accountid == AccountNULL) { + _debug("peerHungupCall: Call doesn't exists\n"); + return; + } - if (_dbus) _dbus->getCallManager()->callStateChanged(id, "HUNGUP"); - if (isCurrentCall(id)) { - stopTone(true); - switchCall(""); - } - removeWaitingCall(id); - removeCallAccount(id); + /* Broadcast a signal over DBus */ + if (_dbus) _dbus->getCallManager()->callStateChanged(id, "HUNGUP"); + + if (isCurrentCall(id)) { + stopTone(true); + switchCall(""); + } + + removeWaitingCall(id); + removeCallAccount(id); - if( getConfigInt( PREFERENCES , CONFIG_PA_VOLUME_CTRL ) ) - _audiodriver->restorePulseAppsVolume(); + if( _audiodriver->getLayerType() == PULSEAUDIO && getConfigInt( PREFERENCES , CONFIG_PA_VOLUME_CTRL ) ) { + pulselayer = dynamic_cast<PulseLayer *> (getAudioDriver()); + if(pulselayer) pulselayer->restorePulseAppsVolume(); + } } //THREAD=VoIP @@ -718,7 +693,7 @@ ManagerImpl::startVoiceMessageNotification(const AccountID& accountId, int nb_ms if (_dbus) _dbus->getCallManager()->voiceMailNotify(accountId, nb_msg) ; } -void ManagerImpl::connectionStatusNotification( void ) +void ManagerImpl::connectionStatusNotification( ) { if (_dbus) _dbus->getConfigurationManager()->accountsChanged(); @@ -729,30 +704,32 @@ void ManagerImpl::connectionStatusNotification( void ) /** * Multi Thread */ -bool -ManagerImpl::playATone(Tone::TONEID toneId) { - int hasToPlayTone = getConfigInt(SIGNALISATION, PLAY_TONES); - if (!hasToPlayTone) return false; +bool ManagerImpl::playATone(Tone::TONEID toneId) +{ + int hasToPlayTone; + AudioLoop *audioloop; + AudioLayer *audiolayer; + unsigned int nbSamples; - if (_telephoneTone != 0) { - _toneMutex.enterMutex(); - _telephoneTone->setCurrentTone(toneId); - _toneMutex.leaveMutex(); + hasToPlayTone = getConfigInt(SIGNALISATION, PLAY_TONES); + if (!hasToPlayTone) + return false; + + audiolayer = getAudioDriver(); - AudioLoop* audioloop = getTelephoneTone(); - unsigned int nbSampling = audioloop->getSize(); - AudioLayer* audiolayer = getAudioDriver(); - SFLDataFormat buf[nbSampling]; - if ( audiolayer ) { - int layer = audiolayer->getLayerType(); - if(CHECK_INTERFACE( layer , ALSA ) ) - audiolayer->putUrgent( buf, nbSampling ); - else{ - audiolayer->startStream(); - } - } - else - return false; + if (_telephoneTone != 0) { + _toneMutex.enterMutex(); + _telephoneTone->setCurrentTone(toneId); + _toneMutex.leaveMutex(); + + audioloop = getTelephoneTone(); + nbSamples = audioloop->getSize(); + SFLDataFormat buf[nbSamples]; + + if ( audiolayer ){ + audiolayer->putUrgent( buf, nbSamples ); + } else + return false; } return true; } @@ -760,30 +737,30 @@ ManagerImpl::playATone(Tone::TONEID toneId) { /** * Multi Thread */ -void -ManagerImpl::stopTone(bool stopAudio=true) { - int hasToPlayTone = getConfigInt(SIGNALISATION, PLAY_TONES); - if (!hasToPlayTone) return; +void ManagerImpl::stopTone (bool stopAudio=true) +{ + int hasToPlayTone; + AudioLayer *audiolayer; - if (stopAudio) { - AudioLayer* audiolayer = getAudioDriver(); - int layer = audiolayer->getLayerType(); - if(CHECK_INTERFACE( layer , ALSA ) ){} - else{} - //if (audiolayer) { audiolayer->stopStream(); } + hasToPlayTone = getConfigInt(SIGNALISATION, PLAY_TONES); + if (!hasToPlayTone) + return; - } + if (stopAudio) { + audiolayer = getAudioDriver(); + if (audiolayer) audiolayer->stopStream(); + } - _toneMutex.enterMutex(); - if (_telephoneTone != 0) { - _telephoneTone->setCurrentTone(Tone::TONE_NULL); - } - _toneMutex.leaveMutex(); + _toneMutex.enterMutex(); + if (_telephoneTone != 0) { + _telephoneTone->setCurrentTone(Tone::TONE_NULL); + } + _toneMutex.leaveMutex(); - // for ringing tone.. - _toneMutex.enterMutex(); - _audiofile.stop(); - _toneMutex.leaveMutex(); + // for ringing tone.. + _toneMutex.enterMutex(); + _audiofile.stop(); + _toneMutex.leaveMutex(); } /** @@ -820,7 +797,6 @@ ManagerImpl::congestion () { void ManagerImpl::ringback () { playATone(Tone::TONE_RINGTONE); - getAudioDriver()->trigger_thread(); } /** @@ -829,38 +805,42 @@ ManagerImpl::ringback () { void ManagerImpl::ringtone() { + std::string ringchoice; + AudioLayer *audiolayer; + AudioCodec *codecForTone; + int layer, samplerate; + bool loadFile; + if( isRingtoneEnabled() ) { //TODO Comment this because it makes the daemon crashes since the main thread //synchronizes the ringtone thread. - std::string ringchoice = getConfigString(AUDIO, RING_CHOICE); + ringchoice = getConfigString(AUDIO, RING_CHOICE); //if there is no / inside the path if ( ringchoice.find(DIR_SEPARATOR_CH) == std::string::npos ) { // check inside global share directory ringchoice = std::string(PROGSHAREDIR) + DIR_SEPARATOR_STR + RINGDIR + DIR_SEPARATOR_STR + ringchoice; } - AudioLayer* audiolayer = getAudioDriver(); - int layer = audiolayer->getLayerType(); - if (audiolayer==0) { return; } - int sampleRate = audiolayer->getSampleRate(); - AudioCodec* codecForTone = _codecDescriptorMap.getFirstCodecAvailable(); + audiolayer = getAudioDriver(); + layer = audiolayer->getLayerType(); + if (audiolayer == 0) + return; + + samplerate = audiolayer->getSampleRate(); + codecForTone = _codecDescriptorMap.getFirstCodecAvailable(); _toneMutex.enterMutex(); - bool loadFile = _audiofile.loadFile(ringchoice, codecForTone , sampleRate); + loadFile = _audiofile.loadFile(ringchoice, codecForTone , samplerate); _toneMutex.leaveMutex(); + if (loadFile) { _toneMutex.enterMutex(); _audiofile.start(); _toneMutex.leaveMutex(); if(CHECK_INTERFACE( layer, ALSA )){ - /*int size = _audiofile.getSize(); - SFLDataFormat output[ size ]; - _audiofile.getNext(output, size , 100); - audiolayer->putUrgent( output , size ); - audiolayer->trigger_thread();*/ - ringback(); + //ringback(); } else{ audiolayer->startStream(); @@ -899,26 +879,23 @@ ManagerImpl::getTelephoneFile() } } -void -ManagerImpl::notificationIncomingCall(void) { - - AudioLayer* audiolayer = getAudioDriver(); - if (audiolayer != 0) { - int layer = audiolayer->getLayerType(); - unsigned int samplerate = audiolayer->getSampleRate(); +void ManagerImpl::notificationIncomingCall(void) +{ + AudioLayer *audiolayer; std::ostringstream frequency; - frequency << "440/" << FRAME_PER_BUFFER; - - Tone tone(frequency.str(), samplerate); - unsigned int nbSampling = tone.getSize(); - SFLDataFormat buf[nbSampling]; - tone.getNext(buf, tone.getSize()); - if(CHECK_INTERFACE( layer , ALSA )) - audiolayer->playSamples(buf, sizeof(SFLDataFormat)*nbSampling, true); - else - audiolayer->putUrgent( buf, sizeof(SFLDataFormat)*nbSampling ); - - } + unsigned int samplerate, nbSampling; + + audiolayer = getAudioDriver(); + if (audiolayer != 0) { + samplerate = audiolayer->getSampleRate(); + frequency << "440/" << FRAME_PER_BUFFER; + Tone tone(frequency.str(), samplerate); + nbSampling = tone.getSize(); + SFLDataFormat buf[nbSampling]; + tone.getNext(buf, tone.getSize()); + /* Put the data in the urgent ring buffer */ + audiolayer->putUrgent (buf, sizeof(SFLDataFormat)*nbSampling); + } } /** @@ -1009,15 +986,17 @@ ManagerImpl::initConfigFile ( bool load_user_value ) std::string type_int("int"); std::string section; - section = SIGNALISATION; // Default values, that will be overwritten by the call to // 'populateFromFile' below. + section = SIGNALISATION; fill_config_int(SYMMETRIC, YES_STR); fill_config_int(PLAY_DTMF, YES_STR); fill_config_int(PLAY_TONES, YES_STR); fill_config_int(PULSE_LENGTH, DFT_PULSE_LENGTH_STR); fill_config_int(SEND_DTMF_AS, SIP_INFO_STR); + fill_config_int(STUN_ENABLE, DFT_STUN_ENABLE); + fill_config_int(STUN_SERVER, DFT_STUN_SERVER); section = AUDIO; fill_config_int(ALSA_CARD_ID_IN, ALSA_DFT_CARD); @@ -1266,7 +1245,10 @@ ManagerImpl::setOutputAudioPlugin(const std::string& audioPlugin) ManagerImpl::getAudioOutputDeviceList(void) { _debug("Get audio output device list\n"); - return _audiodriver -> getSoundCardsInfo(SFL_PCM_PLAYBACK); + AlsaLayer *layer; + + layer = dynamic_cast<AlsaLayer*> (getAudioDriver ()); + if (layer) return layer -> getSoundCardsInfo(SFL_PCM_PLAYBACK); } /** @@ -1275,19 +1257,22 @@ ManagerImpl::getAudioOutputDeviceList(void) void ManagerImpl::setAudioOutputDevice(const int index) { - //int layer = _audiodriver -> getLayerType(); - _debug("Set audio output device: %i\n", index); - _audiodriver -> setErrorMessage( -1 ); - _audiodriver->openDevice(_audiodriver->getIndexIn(), - index, - _audiodriver->getSampleRate(), - _audiodriver->getFrameSize(), - SFL_PCM_PLAYBACK, - _audiodriver->getAudioPlugin()); - if( _audiodriver -> getErrorMessage() != -1) - notifyErrClient( _audiodriver -> getErrorMessage() ); - // set config - setConfig( AUDIO , ALSA_CARD_ID_OUT , index ); + AlsaLayer *alsalayer; + std::string alsaplugin; + _debug("Set audio output device: %i\n", index); + + _audiodriver -> setErrorMessage( -1 ); + + alsalayer = dynamic_cast<AlsaLayer*> (getAudioDriver ()); + alsaplugin = alsalayer->getAudioPlugin (); + + _audiodriver->openDevice(_audiodriver->getIndexIn(), index, _audiodriver->getSampleRate(), _audiodriver->getFrameSize(), SFL_PCM_PLAYBACK, alsaplugin ); + + if( _audiodriver -> getErrorMessage() != -1) + notifyErrClient( _audiodriver -> getErrorMessage() ); + + // set config + setConfig( AUDIO , ALSA_CARD_ID_OUT , index ); } /** @@ -1297,7 +1282,10 @@ ManagerImpl::setAudioOutputDevice(const int index) ManagerImpl::getAudioInputDeviceList(void) { _debug("Get audio input device list\n"); - return _audiodriver->getSoundCardsInfo(SFL_PCM_CAPTURE); + AlsaLayer *audiolayer; + + audiolayer = dynamic_cast<AlsaLayer *> ( getAudioDriver()); + if(audiolayer) return audiolayer->getSoundCardsInfo(SFL_PCM_CAPTURE); } /** @@ -1306,19 +1294,23 @@ ManagerImpl::getAudioInputDeviceList(void) void ManagerImpl::setAudioInputDevice(const int index) { - //int layer = _audiodriver -> getLayerType(); - _debug("Set audio input device %i\n", index); - _audiodriver -> setErrorMessage( -1 ); - _audiodriver->openDevice(index, - _audiodriver->getIndexOut(), - _audiodriver->getSampleRate(), - _audiodriver->getFrameSize(), - SFL_PCM_CAPTURE, - _audiodriver->getAudioPlugin()); - if( _audiodriver -> getErrorMessage() != -1) - notifyErrClient( _audiodriver -> getErrorMessage() ); - // set config - setConfig( AUDIO , ALSA_CARD_ID_IN , index ); + AlsaLayer *alsalayer; + std::string alsaplugin; + + _debug("Set audio input device %i\n", index); + + _audiodriver -> setErrorMessage( -1 ); + + alsalayer = dynamic_cast<AlsaLayer*> (getAudioDriver ()); + alsaplugin = alsalayer->getAudioPlugin (); + + _audiodriver->openDevice(index, _audiodriver->getIndexOut(), _audiodriver->getSampleRate(), _audiodriver->getFrameSize(), SFL_PCM_CAPTURE, alsaplugin ); + + if( _audiodriver -> getErrorMessage() != -1) + notifyErrClient( _audiodriver -> getErrorMessage() ); + + // set config + setConfig( AUDIO , ALSA_CARD_ID_IN , index ); } /** @@ -1399,7 +1391,27 @@ ManagerImpl::setDialpad( void ) ( getConfigInt( PREFERENCES , CONFIG_DIALPAD ) == DISPLAY_DIALPAD )? setConfig(PREFERENCES , CONFIG_DIALPAD , NO_STR ) : setConfig( PREFERENCES , CONFIG_DIALPAD , YES_STR ); } -int +std::string ManagerImpl::getStunServer( void ) +{ + return getConfigString(SIGNALISATION , STUN_SERVER); +} + +void ManagerImpl::setStunServer( const std::string &server ) +{ + setConfig(SIGNALISATION , STUN_SERVER, server ); +} + +int ManagerImpl::isStunEnabled (void) +{ + return getConfigInt(SIGNALISATION , STUN_ENABLE); +} + +void ManagerImpl::enableStun (void) +{ + ( getConfigInt( SIGNALISATION , STUN_ENABLE ) == STUN_ENABLED )? setConfig(SIGNALISATION , STUN_ENABLE , NO_STR ) : setConfig( SIGNALISATION , STUN_ENABLE , YES_STR ); +} + + int ManagerImpl::getVolumeControls( void ) { return getConfigInt( PREFERENCES , CONFIG_VOLUME ); @@ -1543,16 +1555,23 @@ ManagerImpl::notifyErrClient( const int32_t& errCode ) int ManagerImpl::getAudioDeviceIndex(const std::string name) { - _debug("Get audio device index\n"); - int num = _audiodriver -> soundCardGetIndex( name ); - return num; + AlsaLayer *alsalayer; + + _debug("Get audio device index\n"); + + alsalayer = dynamic_cast<AlsaLayer *> (getAudioDriver()); + if(alsalayer) return alsalayer -> soundCardGetIndex( name ); } std::string ManagerImpl::getCurrentAudioOutputPlugin( void ) { - _debug("Get alsa plugin\n"); - return _audiodriver -> getAudioPlugin(); + AlsaLayer *alsalayer; + + _debug("Get alsa plugin\n"); + + alsalayer = dynamic_cast<AlsaLayer *> (getAudioDriver()); + if(alsalayer) return alsalayer -> getAudioPlugin (); } int ManagerImpl::app_is_running( std::string process ) @@ -1610,104 +1629,88 @@ ManagerImpl::initAudioDriver(void) void ManagerImpl::selectAudioDriver (void) { - int layer = _audiodriver->getLayerType(); - _debug("Audio layer type: %i\n" , layer); - - std::string alsaPlugin = getConfigString( AUDIO , ALSA_PLUGIN ); - int numCardIn = getConfigInt( AUDIO , ALSA_CARD_ID_IN ); - int numCardOut = getConfigInt( AUDIO , ALSA_CARD_ID_OUT ); - int sampleRate = getConfigInt( AUDIO , ALSA_SAMPLE_RATE ); - if (sampleRate <=0 || sampleRate > 48000) { - sampleRate = 44100; - } - int frameSize = getConfigInt( AUDIO , ALSA_FRAME_SIZE ); - - if( !_audiodriver -> soundCardIndexExist( numCardIn , SFL_PCM_CAPTURE ) ) - { - _debug(" Card with index %i doesn't exist or cannot capture. Switch to 0.\n", numCardIn); - numCardIn = ALSA_DFT_CARD_ID ; - setConfig( AUDIO , ALSA_CARD_ID_IN , ALSA_DFT_CARD_ID ); - } - if( !_audiodriver -> soundCardIndexExist( numCardOut , SFL_PCM_PLAYBACK ) ) - { - _debug(" Card with index %i doesn't exist or cannot playback . Switch to 0.\n", numCardOut); - numCardOut = ALSA_DFT_CARD_ID ; - setConfig( AUDIO , ALSA_CARD_ID_OUT , ALSA_DFT_CARD_ID ); - } + int layer, numCardIn, numCardOut, sampleRate, frameSize; + std::string alsaPlugin; + AlsaLayer *alsalayer; + layer = _audiodriver->getLayerType(); + _debug("Audio layer type: %i\n" , layer); - if(CHECK_INTERFACE( layer , ALSA )) - { - delete _audiodriver; - _audiodriver = new AlsaLayer( this ); - _debugInit(" ALSA audio driver \n"); - _audiodriver->setErrorMessage(-1); - _audiodriver->openDevice( numCardIn , numCardOut, sampleRate, frameSize, SFL_PCM_BOTH, alsaPlugin ); - if( _audiodriver -> getErrorMessage() != -1 ) - notifyErrClient( _audiodriver -> getErrorMessage()); - }else{ - delete _audiodriver; - _audiodriver = new PulseLayer( this ); - _debug(" Pulse audio driver \n"); - _audiodriver->setErrorMessage(-1); - _audiodriver->openDevice( numCardIn , numCardOut, sampleRate, frameSize, SFL_PCM_BOTH, alsaPlugin ); - if( _audiodriver -> getErrorMessage() != -1 ) - notifyErrClient( _audiodriver -> getErrorMessage()); - } + /* Retrieve the global devices info from the user config */ + alsaPlugin = getConfigString( AUDIO , ALSA_PLUGIN ); + numCardIn = getConfigInt( AUDIO , ALSA_CARD_ID_IN ); + numCardOut = getConfigInt( AUDIO , ALSA_CARD_ID_OUT ); + sampleRate = getConfigInt( AUDIO , ALSA_SAMPLE_RATE ); + if (sampleRate <=0 || sampleRate > 48000) { + sampleRate = 44100; + } + frameSize = getConfigInt( AUDIO , ALSA_FRAME_SIZE ); + /* Only for the ALSA layer, we check the sound card information */ + if (layer == ALSA) + { + alsalayer = dynamic_cast<AlsaLayer*> (getAudioDriver ()); + if( !alsalayer -> soundCardIndexExist( numCardIn , SFL_PCM_CAPTURE ) ) + { + _debug(" Card with index %i doesn't exist or cannot capture. Switch to 0.\n", numCardIn); + numCardIn = ALSA_DFT_CARD_ID ; + setConfig( AUDIO , ALSA_CARD_ID_IN , ALSA_DFT_CARD_ID ); + } + if( !alsalayer -> soundCardIndexExist( numCardOut , SFL_PCM_PLAYBACK ) ) + { + _debug(" Card with index %i doesn't exist or cannot playback . Switch to 0.\n", numCardOut); + numCardOut = ALSA_DFT_CARD_ID ; + setConfig( AUDIO , ALSA_CARD_ID_OUT , ALSA_DFT_CARD_ID ); + } + } + + _audiodriver->setErrorMessage(-1); + /* Open the audio devices */ + _audiodriver->openDevice( numCardIn , numCardOut, sampleRate, frameSize, SFL_PCM_BOTH, alsaPlugin ); + /* Notify the error if there is one */ + if( _audiodriver -> getErrorMessage() != -1 ) + notifyErrClient( _audiodriver -> getErrorMessage()); } -void -ManagerImpl::switchAudioManager( void ) +void ManagerImpl::switchAudioManager (void) { - _debug( "Switching audio manager \n"); + int type, samplerate, framesize, numCardIn, numCardOut; + std::string alsaPlugin; - int type = _audiodriver->getLayerType(); - int samplerate = getConfigInt( AUDIO , ALSA_SAMPLE_RATE ); - int framesize = getConfigInt( AUDIO , ALSA_FRAME_SIZE ); - std::string alsaPlugin = getConfigString( AUDIO , ALSA_PLUGIN ); - int numCardIn = getConfigInt( AUDIO , ALSA_CARD_ID_IN ); - int numCardOut = getConfigInt( AUDIO , ALSA_CARD_ID_OUT ); + _debug( "Switching audio manager \n"); - _debug("Deleting current layer... \n" ); - _audiodriver->closeLayer(); - delete _audiodriver; _audiodriver = NULL; - - switch( type ){ - case ALSA: - _debug("Creating Pulseaudio layer...\n"); - _audiodriver = new PulseLayer( this ); - break; - case PULSEAUDIO: - _debug("Creating ALSA layer...\n"); - _audiodriver = new AlsaLayer( this ); - break; - default: - _debug("Error: audio layer unknown\n"); - } - _audiodriver->setErrorMessage(-1); - _audiodriver->openDevice( numCardIn , numCardOut, samplerate, framesize, SFL_PCM_BOTH, alsaPlugin ); - if( _audiodriver -> getErrorMessage() != -1 ) - notifyErrClient( _audiodriver -> getErrorMessage()); -} + if(!_audiodriver) + return; -/** - * Initialize the Zeroconf scanning services loop - * Informations will be store inside a map DNSService->_services - * Initialization: Main Thread - */ - void -ManagerImpl::initZeroconf(void) -{ -#ifdef USE_ZEROCONF - _debugInit("Zeroconf Initialization"); - int useZeroconf = getConfigInt(PREFERENCES, CONFIG_ZEROCONF); + type = _audiodriver->getLayerType(); + samplerate = getConfigInt( AUDIO , ALSA_SAMPLE_RATE ); + framesize = getConfigInt( AUDIO , ALSA_FRAME_SIZE ); + alsaPlugin = getConfigString( AUDIO , ALSA_PLUGIN ); + numCardIn = getConfigInt( AUDIO , ALSA_CARD_ID_IN ); + numCardOut = getConfigInt( AUDIO , ALSA_CARD_ID_OUT ); - if (useZeroconf) { - _DNSService->startScanServices(); - } -#endif -} + _debug("Deleting current layer... \n" ); + //_audiodriver->closeLayer(); + delete _audiodriver; _audiodriver = NULL; + + switch( type ){ + case ALSA: + _debug("Creating Pulseaudio layer...\n"); + _audiodriver = new PulseLayer( this ); + break; + case PULSEAUDIO: + _debug("Creating ALSA layer...\n"); + _audiodriver = new AlsaLayer( this ); + break; + default: + _debug("Error: audio layer unknown\n"); + } + + _audiodriver->setErrorMessage(-1); + _audiodriver->openDevice( numCardIn , numCardOut, samplerate, framesize, SFL_PCM_BOTH, alsaPlugin ); + if( _audiodriver -> getErrorMessage() != -1 ) + notifyErrClient( _audiodriver -> getErrorMessage()); +} /** * Init the volume for speakers/micro from 0 to 100 value @@ -1724,111 +1727,37 @@ ManagerImpl::initVolume() void ManagerImpl::setSpkrVolume(unsigned short spkr_vol) { - AudioLayer *audiolayer = NULL; + PulseLayer *pulselayer = NULL; + /* Set the manager sound volume */ _spkr_volume = spkr_vol; - audiolayer = getAudioDriver(); - if( audiolayer ) + + /* Only for PulseAudio */ + pulselayer = dynamic_cast<PulseLayer*> (getAudioDriver()); + if (pulselayer) { - audiolayer->setPlaybackVolume( spkr_vol ); + if( pulselayer->getLayerType() == PULSEAUDIO ) + { + if(pulselayer) pulselayer->setPlaybackVolume (spkr_vol); + } } - } void ManagerImpl::setMicVolume(unsigned short mic_vol) { - //AudioLayer *audiolayer = NULL; - _mic_volume = mic_vol; - //audiolayer = getAudioDriver(); - //if( audiolayer ) - //{ - // audiolayer->setCaptureVolume( mic_vol ); - //} } -/** - * configuration function requests - * Main Thread - */ - bool -ManagerImpl::getZeroconf(const std::string& sequenceId) +void ManagerImpl::setSipPort( int port ) { - bool returnValue = false; -#ifdef USE_ZEROCONF - int useZeroconf = getConfigInt(PREFERENCES, CONFIG_ZEROCONF); - if (useZeroconf && _dbus != NULL) { - TokenList arg; - TokenList argTXT; - std::string newService = "new service"; - std::string newTXT = "new txt record"; - if (!_DNSService->isStart()) { _DNSService->startScanServices(); } - DNSServiceMap services = _DNSService->getServices(); - DNSServiceMap::iterator iter = services.begin(); - arg.push_back(newService); - while(iter!=services.end()) { - arg.push_front(iter->first); - //_gui->sendMessage("100",sequenceId,arg); - arg.pop_front(); // remove the first, the name - - TXTRecordMap record = iter->second.getTXTRecords(); - TXTRecordMap::iterator iterTXT = record.begin(); - while(iterTXT!=record.end()) { - argTXT.clear(); - argTXT.push_back(iter->first); - argTXT.push_back(iterTXT->first); - argTXT.push_back(iterTXT->second); - argTXT.push_back(newTXT); - // _gui->sendMessage("101",sequenceId,argTXT); - iterTXT++; - } - iter++; - } - returnValue = true; - } -#else - (void)sequenceId; -#endif - return returnValue; } -/** - * Main Thread - */ - bool -ManagerImpl::attachZeroconfEvents(const std::string& sequenceId, Pattern::Observer& observer) +int ManagerImpl::getSipPort( void ) { - bool returnValue = false; - // don't need the _gui like getZeroconf function - // because Observer is here -#ifdef USE_ZEROCONF - int useZeroconf = getConfigInt(PREFERENCES, CONFIG_ZEROCONF); - if (useZeroconf) { - if (!_DNSService->isStart()) { _DNSService->startScanServices(); } - _DNSService->attach(observer); - returnValue = true; - } -#else - (void)sequenceId; - (void)observer; -#endif - return returnValue; -} - bool -ManagerImpl::detachZeroconfEvents(Pattern::Observer& observer) -{ - bool returnValue = false; -#ifdef USE_ZEROCONF - if (_DNSService) { - _DNSService->detach(observer); - returnValue = true; - } -#else - (void)observer; -#endif - return returnValue; + return 5060; } + // TODO: rewrite this /** * Main Thread @@ -1961,7 +1890,7 @@ std::map< std::string, std::string > ManagerImpl::getAccountDetails(const Accoun std::map<std::string, std::string> a; std::string accountType; - enum VoIPLink::RegistrationState state; + RegistrationState state; state = _accountMap[accountID]->getRegistrationState(); accountType = getConfigString(accountID, CONFIG_ACCOUNT_TYPE); @@ -1970,15 +1899,15 @@ std::map< std::string, std::string > ManagerImpl::getAccountDetails(const Accoun a.insert( std::pair<std::string, std::string>( CONFIG_ACCOUNT_ENABLE, getConfigString(accountID, CONFIG_ACCOUNT_ENABLE) == "1" ? "TRUE": "FALSE")); a.insert( std::pair<std::string, std::string>( "Status", - (state == VoIPLink::Registered ? "REGISTERED": - (state == VoIPLink::Unregistered ? "UNREGISTERED": - (state == VoIPLink::Trying ? "TRYING": - (state == VoIPLink::ErrorAuth ? "ERROR_AUTH": - (state == VoIPLink::ErrorNetwork ? "ERROR_NETWORK": - (state == VoIPLink::ErrorHost ? "ERROR_HOST": - (state == VoIPLink::ErrorExistStun ? "ERROR_EXIST_STUN": - (state == VoIPLink::ErrorConfStun ? "ERROR_CONF_STUN": - (state == VoIPLink::Error ? "ERROR": "ERROR"))))))))) + (state == Registered ? "REGISTERED": + (state == Unregistered ? "UNREGISTERED": + (state == Trying ? "TRYING": + (state == ErrorAuth ? "ERROR_AUTH": + (state == ErrorNetwork ? "ERROR_NETWORK": + (state == ErrorHost ? "ERROR_HOST": + (state == ErrorExistStun ? "ERROR_EXIST_STUN": + (state == ErrorConfStun ? "ERROR_CONF_STUN": + (state == Error ? "ERROR": "ERROR"))))))))) ) ); @@ -2003,6 +1932,7 @@ void ManagerImpl::setAccountDetails( const std::string& accountID, const std::ma std::string accountType; Account *acc; + VoIPLink *link; accountType = (*details.find(CONFIG_ACCOUNT_TYPE)).second; @@ -2016,28 +1946,28 @@ void ManagerImpl::setAccountDetails( const std::string& accountID, const std::ma // SIP SPECIFIC if (accountType == "SIP") { + + link = Manager::instance().getAccountLink( accountID ); + + if( link==0 ) + { + _debug("Can not retrieve SIP link...\n"); + return; + } + setConfig(accountID, SIP_STUN_SERVER,(*details.find(SIP_STUN_SERVER)).second); setConfig(accountID, SIP_USE_STUN, (*details.find(SIP_USE_STUN)).second == "TRUE" ? "1" : "0"); - - if(!_userAgentInitlized) { - _userAgentInitlized = true; - if((*details.find(SIP_USE_STUN)).second == "TRUE") - _userAgent->setStunServer((*details.find(SIP_STUN_SERVER)).second.data()); - else - _userAgent->setStunServer(NULL); - - _userAgent->sipCreate(); - _userAgent->sipInit(); - } else { - if((*details.find(SIP_USE_STUN)).second == "TRUE") - _userAgent->setStunServer((*details.find(SIP_STUN_SERVER)).second.data()); - else - _userAgent->setStunServer(NULL); - - restartPjsip(); - } - } + if((*details.find(SIP_USE_STUN)).second == "TRUE") + { + link->setStunServer((*details.find(SIP_STUN_SERVER)).second.data()); + } + else + { + link->setStunServer(""); + } + //restartPjsip(); + } saveConfig(); @@ -2053,6 +1983,8 @@ void ManagerImpl::setAccountDetails( const std::string& accountID, const std::ma // Update account details to the client side if (_dbus) _dbus->getConfigurationManager()->accountsChanged(); + //delete link; link=0; + } void @@ -2079,38 +2011,30 @@ ManagerImpl::sendRegister( const std::string& accountID , const int32_t& expire ManagerImpl::addAccount(const std::map< std::string, std::string >& details) { - /** @todo Deal with both the _accountMap and the Configuration */ - std::string accountType = (*details.find(CONFIG_ACCOUNT_TYPE)).second; - Account* newAccount; - std::stringstream accountID; - accountID << "Account:" << time(NULL); - AccountID newAccountID = accountID.str(); - /** @todo Verify the uniqueness, in case a program adds accounts, two in a row. */ - - if (accountType == "SIP") { - if(!_userAgentInitlized) { - // Initialize the SIP Manager - _userAgent = new UserAgent(); - _userAgent->setSipPort(Manager::instance().getConfigInt(PREFERENCES , CONFIG_SIP_PORT)); - } + /** @todo Deal with both the _accountMap and the Configuration */ + std::string accountType = (*details.find(CONFIG_ACCOUNT_TYPE)).second; + Account* newAccount; + std::stringstream accountID; + accountID << "Account:" << time(NULL); + AccountID newAccountID = accountID.str(); + /** @todo Verify the uniqueness, in case a program adds accounts, two in a row. */ - newAccount = AccountCreator::createAccount(AccountCreator::SIP_ACCOUNT, newAccountID); - } - else if (accountType == "IAX") { - newAccount = AccountCreator::createAccount(AccountCreator::IAX_ACCOUNT, newAccountID); - } - else { - _debug("Unknown %s param when calling addAccount(): %s\n", CONFIG_ACCOUNT_TYPE, accountType.c_str()); - return; - } - _accountMap[newAccountID] = newAccount; - setAccountDetails(accountID.str(), details); - - saveConfig(); + if (accountType == "SIP") { + newAccount = AccountCreator::createAccount(AccountCreator::SIP_ACCOUNT, newAccountID); + } + else if (accountType == "IAX") { + newAccount = AccountCreator::createAccount(AccountCreator::IAX_ACCOUNT, newAccountID); + } + else { + _debug("Unknown %s param when calling addAccount(): %s\n", CONFIG_ACCOUNT_TYPE, accountType.c_str()); + return; + } + _accountMap[newAccountID] = newAccount; + setAccountDetails(accountID.str(), details); - if (_dbus) _dbus->getConfigurationManager()->accountsChanged(); + saveConfig(); - //restartPjsip(); + if (_dbus) _dbus->getConfigurationManager()->accountsChanged(); } void @@ -2189,20 +2113,6 @@ ManagerImpl::getNewCallID() return random_id.str(); } - void -ManagerImpl::restartPjsip() -{ - if ( _userAgentInitlized ){ - unregisterCurSIPAccounts(); - _userAgent->sipDestory(); - //_userAgent->setSipPort(Manager::instance().getConfigInt(PREFERENCES , CONFIG_SIP_PORT)); - - _userAgent->sipCreate(); - _userAgent->sipInit(); - registerCurSIPAccounts(); - } -} - short ManagerImpl::loadAccountMap() { @@ -2211,7 +2121,6 @@ ManagerImpl::loadAccountMap() TokenList sections = _config.getSections(); std::string accountType; Account* tmpAccount; - std::string port; TokenList::iterator iter = sections.begin(); while(iter != sections.end()) { @@ -2223,28 +2132,7 @@ ManagerImpl::loadAccountMap() accountType = getConfigString(*iter, CONFIG_ACCOUNT_TYPE); if (accountType == "SIP") { - if(!_userAgentInitlized) { - // Initialize the SIP Manager - _userAgent = new UserAgent(); - _userAgentInitlized = true; - _userAgent->setSipPort(Manager::instance().getConfigInt(PREFERENCES , CONFIG_SIP_PORT)); - } - tmpAccount = AccountCreator::createAccount(AccountCreator::SIP_ACCOUNT, *iter); - - // Determine whether to use stun for the current account or not - int useStun = Manager::instance().getConfigInt(tmpAccount->getAccountID(),SIP_USE_STUN); - - if(useStun == 1) { - _userAgent->setStunServer(Manager::instance().getConfigString(tmpAccount->getAccountID(), SIP_STUN_SERVER).data()); - } - - /*// Set registration port for all accounts, The last non-5060 port will be recorded in _userAgent. - port = Manager::instance().getConfigString(tmpAccount->getAccountID(), SIP_PORT); - std::istringstream is(port); - is >> iPort; - if (iPort != DEFAULT_SIP_PORT) - _userAgent->setRegPort(iPort); */ } else if (accountType == "IAX") { tmpAccount = AccountCreator::createAccount(AccountCreator::IAX_ACCOUNT, *iter); @@ -2259,8 +2147,6 @@ ManagerImpl::loadAccountMap() nbAccount++; } - _debug("\n"); - iter++; } @@ -2328,8 +2214,25 @@ ManagerImpl::getAccountIdFromNameAndServer(const std::string& userName, const st return AccountNULL; } - VoIPLink* -ManagerImpl::getAccountLink(const AccountID& accountID) +AccountMap ManagerImpl::getSipAccountMap( void ) +{ + + AccountMap::iterator iter; + AccountMap sipaccounts; + AccountID id; + Account *account; + + for(iter = _accountMap.begin(); iter != _accountMap.end(); ++iter) { + if( iter->second->getType() == "sip" ){ + //id = iter->first; + //account = iter->second; + //sipaccounts.insert( std::pair<id, account> ); + } + } + return sipaccounts; +} + +VoIPLink* ManagerImpl::getAccountLink(const AccountID& accountID) { Account* acc = getAccount(accountID); if ( acc ) { @@ -2348,37 +2251,6 @@ pjsip_regc return NULL; } - -/** - * Return the instance of sip manager - */ -UserAgent *ManagerImpl::getUserAgent() -{ - return _userAgent; -} - -int -ManagerImpl::getSipPort() -{ - if( _userAgent ) - return _userAgent->getSipPort(); - else - { - // It means that no SIP accounts are configured, so return a default value - return 0; - } -} - -void -ManagerImpl::setSipPort(int portNum) -{ - if(portNum != _userAgent->getSipPort()) { - _userAgent->setSipPort(portNum); - restartPjsip(); - setConfig( PREFERENCES , CONFIG_SIP_PORT , portNum ); - } -} - void ManagerImpl::unregisterCurSIPAccounts() { AccountMap::iterator iter = _accountMap.begin(); diff --git a/src/managerimpl.h b/src/managerimpl.h index a76987d758b48188f60e853759be06a51d5b75de..cc3411f3ade26470030f558b6cba32e18795bfdd 100644 --- a/src/managerimpl.h +++ b/src/managerimpl.h @@ -47,7 +47,6 @@ class CodecDescriptor; class GuiFramework; class TelephoneTone; class VoIPLink; -class UserAgent; #ifdef USE_ZEROCONF class DNSService; @@ -272,9 +271,6 @@ class ManagerImpl { */ void sendRegister( const ::std::string& accountId , const int32_t& expire ); - bool getZeroconf(const std::string& sequenceId); - bool attachZeroconfEvents(const std::string& sequenceId, Pattern::Observer& observer); - bool detachZeroconfEvents(Pattern::Observer& observer); bool getCallStatus(const std::string& sequenceId); /** @@ -795,14 +791,15 @@ class ManagerImpl { */ void restartPjsip(); - int getSipPort(); - - void setSipPort(int port); - void unregisterCurSIPAccounts(); void registerCurSIPAccounts(); + /** + * Returns a map with only the existing SIP accounts + */ + AccountMap getSipAccountMap( void ); + private: /** @@ -1019,6 +1016,15 @@ public: AccountID getAccountIdFromNameAndServer(const std::string& userName, const std::string& server); + int getSipPort(); + + void setSipPort( int port ); + + std::string getStunServer (void); + void setStunServer (const std::string &server); + + int isStunEnabled (void); + void enableStun (void); private: // Copy Constructor @@ -1027,32 +1033,12 @@ private: // Assignment Operator ManagerImpl& operator=( const ManagerImpl& rh); - /** - * The UserAgent provides sip operation facilities for all sip accounts - */ - UserAgent *_userAgent; - - /** Whether the _UserAgent has been initialized */ - bool _userAgentInitlized; - - bool _sipThreadStop; - #ifdef TEST bool testCallAccountMap(); bool testAccountMap(); #endif friend class ConfigurationTest; - -public: - /** - * Retuun the instance of sip manager - */ - UserAgent *getUserAgent(); - - void setSipThreadStatus(bool status) {_sipThreadStop = status;} - - bool getSipThreadStatus() {return _sipThreadStop;} }; #endif // __MANAGER_H__ diff --git a/src/samplerateconverter.cpp b/src/samplerateconverter.cpp index 3c6d0ed90db1902bc1e1ee0f29224f1e767453b4..52d047ca4cb5c5da507b1d91b4a7851b49cea9b9 100644 --- a/src/samplerateconverter.cpp +++ b/src/samplerateconverter.cpp @@ -78,6 +78,7 @@ void SamplerateConverter::init( void ) { int SamplerateConverter::upsampleData( SFLDataFormat* dataIn , SFLDataFormat* dataOut, int samplerate1 , int samplerate2 , int nbSamples ){ double upsampleFactor = (double)samplerate2 / samplerate1 ; + //_debug("factor = %f\n" , upsampleFactor); int nbSamplesMax = (int) ( samplerate2 * getFramesize() / 1000 ); if( upsampleFactor != 1 && dataIn != NULL ) { @@ -106,7 +107,7 @@ int SamplerateConverter::downsampleData( SFLDataFormat* dataIn , SFLDataFormat* double downsampleFactor = (double)samplerate1 / samplerate2; //_debug("factor = %f\n" , downsampleFactor); int nbSamplesMax = (int) ( samplerate1 * getFramesize() / 1000 ); - if ( downsampleFactor != 1) + if ( downsampleFactor != 1 ) { SRC_DATA src_data; src_data.data_in = _floatBufferUpMic; diff --git a/src/sipaccount.cpp b/src/sipaccount.cpp index acce1bb66ed5fff56fad4d6a3d20b77c6ba146a0..61c8bfd05b2a762891c61ebd8fee8f22fd3f2dda 100644 --- a/src/sipaccount.cpp +++ b/src/sipaccount.cpp @@ -2,8 +2,6 @@ * Copyright (C) 2006-2009 Savoir-Faire Linux inc. * * Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com> - * Author: Alexandre Bourget <alexandre.bourget@savoirfairelinux.com> - * Author: Yan Morin <yan.morin@savoirfairelinux.com> * * 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 @@ -23,80 +21,71 @@ #include "sipaccount.h" #include "manager.h" #include "user_cfg.h" -#include "useragent.h" SIPAccount::SIPAccount(const AccountID& accountID) - : Account(accountID) - , _userName("") - , _server("") + : Account(accountID, "sip") , _cred(NULL) , _contact("") + , _bRegister(false) + , _regc() { - _link = new SIPVoIPLink(accountID); + /* SIPVoIPlink is used as a singleton, because we want to have only one link for all the SIP accounts created */ + /* So instead of creating a new instance, we just fetch the static instance, or create one if it is not yet */ + /* The SIP library initialization is done in the SIPVoIPLink constructor */ + /* The SIP voip link is now independant of the account ID as it can manage several SIP accounts */ + _link = SIPVoIPLink::instance(""); + + /* Represents the number of SIP accounts connected the same link */ + dynamic_cast<SIPVoIPLink*> (_link)->incrementClients(); + } SIPAccount::~SIPAccount() { - delete _link; - _link = NULL; - delete _cred; - _cred = NULL; + /* One SIP account less connected to the sip voiplink */ + dynamic_cast<SIPVoIPLink*> (_link)->decrementClients(); + /* Delete accounts-related information */ + _regc = NULL; + delete _cred; _cred = NULL; } -int -SIPAccount::registerVoIPLink() +int SIPAccount::registerVoIPLink() { + int status; - int status, useStun; - SIPVoIPLink *thislink; - - _link->setHostname(Manager::instance().getConfigString(_accountID,HOSTNAME)); - useStun = Manager::instance().getConfigInt(_accountID,SIP_USE_STUN); - - thislink = dynamic_cast<SIPVoIPLink*> (_link); - thislink->setStunServer(Manager::instance().getConfigString(_accountID,SIP_STUN_SERVER)); - thislink->setUseStun( useStun!=0 ? true : false); - - _link->init(); - - // Stuff needed for SIP registration. - thislink->setUsername(Manager::instance().getConfigString(_accountID, USERNAME)); - thislink->setPassword(Manager::instance().getConfigString(_accountID, PASSWORD)); - thislink->setHostname(Manager::instance().getConfigString(_accountID, HOSTNAME)); + /* Retrieve the account information */ + /* Stuff needed for SIP registration */ + setHostname(Manager::instance().getConfigString(_accountID,HOSTNAME)); + setUsername(Manager::instance().getConfigString(_accountID, USERNAME)); + setPassword(Manager::instance().getConfigString(_accountID, PASSWORD)); - status = _link->sendRegister(); + /* Start registration */ + status = _link->sendRegister( _accountID ); ASSERT( status , SUCCESS ); return SUCCESS; } -int -SIPAccount::unregisterVoIPLink() +int SIPAccount::unregisterVoIPLink() { _debug("SIPAccount: unregister account %s\n" , getAccountID().c_str()); - _link->sendUnregister(); - _link->terminate(); - - return SUCCESS; + return _link->sendUnregister( _accountID ); } -void -SIPAccount::loadConfig() +void SIPAccount::loadConfig() { // Account generic Account::loadConfig(); } -bool -SIPAccount::fullMatch(const std::string& userName, const std::string& server) +bool SIPAccount::fullMatch(const std::string& username, const std::string& hostname) { - return (userName == _userName && server == _server); + return (username == getUsername() && hostname == getHostname()); } -bool -SIPAccount::userMatch(const std::string& userName) +bool SIPAccount::userMatch(const std::string& username) { - return (userName == _userName); + return (username == getUsername()); } diff --git a/src/sipaccount.h b/src/sipaccount.h index 549d1a6d0d857326720e81b9863df9a4ca045083..a49af2d00864a29c674a2ac152dcd2663d75620c 100644 --- a/src/sipaccount.h +++ b/src/sipaccount.h @@ -26,76 +26,86 @@ #include "account.h" #include "sipvoiplink.h" -struct pjsip_cred_info; - class SIPVoIPLink; /** * @file sipaccount.h * @brief A SIP Account specify SIP specific functions and object (SIPCall/SIPVoIPLink) -*/ + */ class SIPAccount : public Account { -public: - /** - * Constructor - * @param accountID The account identifier - */ - SIPAccount(const AccountID& accountID); - - /* Copy Constructor */ - SIPAccount(const SIPAccount& rh); - - /* Assignment Operator */ - SIPAccount& operator=( const SIPAccount& rh); - - /** - * Virtual destructor - */ - virtual ~SIPAccount(); - - /** - * Actually unuseful, since config loading is done in init() - */ - void loadConfig(); - - /** - * Initialize the SIP voip link with the account parameters and send registration - */ - int registerVoIPLink(); - - /** - * Send unregistration and clean all related stuff ( calls , thread ) - */ - int unregisterVoIPLink(); - - - void setUserName(const std::string &name) {_userName = name;} - - std::string getUserName() {return _userName;} - - void setServer(const std::string &server) {_server = server;} - - std::string getServer() {return _server;} - - void setCredInfo(pjsip_cred_info *cred) {_cred = cred;} - - pjsip_cred_info *getCredInfo() {return _cred;} - - void setContact(const std::string contact) {_contact = contact;} - - std::string getContact() {return _contact;} - - bool fullMatch(const std::string& userName, const std::string& server); - - bool userMatch(const std::string& userName); - -private: - std::string _userName; - std::string _server; - pjsip_cred_info *_cred; - std::string _contact; + public: + /** + * Constructor + * @param accountID The account identifier + */ + SIPAccount(const AccountID& accountID); + + /* Copy Constructor */ + SIPAccount(const SIPAccount& rh); + + /* Assignment Operator */ + SIPAccount& operator=( const SIPAccount& rh); + + /** + * Virtual destructor + */ + virtual ~SIPAccount(); + + /** + * Actually unuseful, since config loading is done in init() + */ + void loadConfig(); + + /** + * Initialize the SIP voip link with the account parameters and send registration + */ + int registerVoIPLink(); + + /** + * Send unregistration and clean all related stuff ( calls , thread ) + */ + int unregisterVoIPLink(); + + inline void setCredInfo(pjsip_cred_info *cred) {_cred = cred;} + inline pjsip_cred_info *getCredInfo() {return _cred;} + + inline void setContact(const std::string &contact) {_contact = contact;} + inline std::string getContact() {return _contact;} + + bool fullMatch(const std::string& username, const std::string& hostname); + bool userMatch(const std::string& username); + + pjsip_regc* getRegistrationInfo( void ) { return _regc; } + void setRegistrationInfo( pjsip_regc *regc ) { _regc = regc; } + + /* Registration flag */ + bool isRegister() {return _bRegister;} + void setRegister(bool result) {_bRegister = result;} + + + private: + + /** + * Credential information + */ + pjsip_cred_info *_cred; + + /** + * The pjsip client registration information + */ + pjsip_regc *_regc; + + /** + * To check if the account is registered + */ + bool _bRegister; + + /* + * SIP address + */ + std::string _contact; }; #endif diff --git a/src/sipcall.cpp b/src/sipcall.cpp index 0e53bdd013d066744d80db93204ffbf14a46549f..d43ea938ac230ce691722de456a39a3dcaa7c32a 100644 --- a/src/sipcall.cpp +++ b/src/sipcall.cpp @@ -22,8 +22,6 @@ #include "sipcall.h" #include "global.h" // for _debug -#include <sstream> // for media buffer -#include <string> #define _SENDRECV 0 #define _SENDONLY 1 @@ -51,13 +49,13 @@ SIPCall::SIPCallInvite(pjsip_rx_data *rdata, pj_pool_t *pool) { pj_status_t status; + // We retrieve the remote sdp offer in the rdata struct to begin the negociation pjmedia_sdp_session* remote_sdp = getRemoteSDPFromRequest(rdata); if (remote_sdp == 0) { return false; } // Have to do some stuff here with the SDP - // We retrieve the remote sdp offer in the rdata struct to begin the negociation _localSDP = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_session); _localSDP->conn = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_conn); @@ -68,7 +66,6 @@ SIPCall::SIPCallInvite(pjsip_rx_data *rdata, pj_pool_t *pool) _localSDP->time.start = _localSDP->time.stop = 0; sdpAddMediaDescription(pool); - _debug("Before validate SDP!\n"); status = pjmedia_sdp_validate( _localSDP ); if (status != PJ_SUCCESS) { _debug("Can not generate valid local sdp\n"); diff --git a/src/sipcall.h b/src/sipcall.h index fe23553d5dcdd5edda45b80c6466ea3d6b124725..2ef3d2793451e85bd1fd48151428959cfd7db854 100644 --- a/src/sipcall.h +++ b/src/sipcall.h @@ -22,8 +22,8 @@ #define SIPCALL_H #include "call.h" +#include "sipvoiplink.h" #include "audio/codecDescriptor.h" -#include "useragent.h" class AudioCodec; diff --git a/src/sipvoiplink.cpp b/src/sipvoiplink.cpp index d70e1221216de4257fe15373b637d39446152286..e3fc11b7920e38dd1e813a486f4fdae20ac110f4 100644 --- a/src/sipvoiplink.cpp +++ b/src/sipvoiplink.cpp @@ -2,8 +2,7 @@ * Copyright (C) 2004-2009 Savoir-Faire Linux inc. * * Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com> - * Author: Yan Morin <yan.morin@savoirfairelinux.com> - * Author : Laurielle Lea <laurielle.lea@savoirfairelinux.com> + * Author: Yun Liu <yun.liu@savoirfairelinux.com> * * 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 @@ -19,261 +18,577 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -/* - * YM: 2006-11-15: changes unsigned int to std::string::size_type, thanks to Pierre Pomes (AMD64 compilation) - */ + #include "sipvoiplink.h" #include "eventthread.h" #include "sipcall.h" -#include <sstream> // for istringstream #include "sipaccount.h" -#include "useragent.h" #include "audio/audiortp.h" - -#include "manager.h" -#include "user_cfg.h" // SIGNALISATION / PULSE #define - -#define RANDOM_LOCAL_PORT ((rand() % 27250) + 5250)*2 - -// 1XX responses -#define DIALOG_ESTABLISHED 101 -// see: osip_const.h - -// need for hold/unhold -#define INVITE_METHOD "INVITE" - -SIPVoIPLink::SIPVoIPLink(const AccountID& accountID) - : VoIPLink(accountID) - , _initDone(false) - , _nbTryListenAddr(2) // number of times to try to start SIP listener - , _useStun(false) - , _stunServer("") - , _localExternAddress("") - , _localExternPort(0) - , _audiortp(new AudioRtp()) - , _regc() - , _bRegister(false) + +/**************** EXTERN VARIABLES AND FUNCTIONS (callbacks) **************************/ + +/* + * The global pool factory + */ +pj_caching_pool _cp; + +/* + * The pool to allocate memory + */ +pj_pool_t *_pool; + +/* + * The SIP endpoint + */ +pjsip_endpoint *_endpt; + +/* + * The SIP module + */ +pjsip_module _mod_ua; + +/* + * Thread related + */ +pj_thread_t *thread; +pj_thread_desc desc; + + +/** + * Get the number of voicemail waiting in a SIP message + */ +void set_voicemail_info( AccountID account, pjsip_msg_body *body ); + +/** + * Set audio (SDP) configuration for a call + * localport, localip, localexternalport + * @param call a SIPCall valid pointer + * @return bool True + */ +bool setCallAudioLocal(SIPCall* call, std::string localIP, bool stun, std::string server); + +// Documentated from the PJSIP Developer's Guide, available on the pjsip website/ + +/* + * Session callback + * Called when the invite session state has changed. + * + * @param inv A pointer on a pjsip_inv_session structure + * @param e A pointer on a pjsip_event structure + */ +void call_on_state_changed( pjsip_inv_session *inv, pjsip_event *e); + +/* + * Session callback + * Called after SDP offer/answer session has completed. + * + * @param inv A pointer on a pjsip_inv_session structure + * @param status A pj_status_t structure + */ +void call_on_media_update( pjsip_inv_session *inv UNUSED, pj_status_t status UNUSED); + +/* + * Called when the invote usage module has created a new dialog and invite + * because of forked outgoing request. + * + * @param inv A pointer on a pjsip_inv_session structure + * @param e A pointer on a pjsip_event structure + */ +void call_on_forked(pjsip_inv_session *inv, pjsip_event *e); + +/* + * Session callback + * Called whenever any transactions within the session has changed their state. + * Useful to monitor the progress of an outgoing request. + * + * @param inv A pointer on a pjsip_inv_session structure + * @param tsx A pointer on a pjsip_transaction structure + * @param e A pointer on a pjsip_event structure + */ +void call_on_tsx_changed(pjsip_inv_session *inv, pjsip_transaction *tsx, pjsip_event *e); + +/* + * Registration callback + */ +void regc_cb(struct pjsip_regc_cbparam *param); + +/* + * Called to handle incoming requests outside dialogs + * @param rdata + * @return pj_bool_t + */ +pj_bool_t mod_on_rx_request(pjsip_rx_data *rdata); + +/* + * Called to handle incoming response + * @param rdata + * @return pj_bool_t + */ +pj_bool_t mod_on_rx_response(pjsip_rx_data *rdata UNUSED) ; + +/* + * Transfer callbacks + */ +void xfer_func_cb( pjsip_evsub *sub, pjsip_event *event); +void xfer_svr_cb(pjsip_evsub *sub, pjsip_event *event); +void onCallTransfered(pjsip_inv_session *inv, pjsip_rx_data *rdata); + +/*************************************************************************************************/ + +SIPVoIPLink* SIPVoIPLink::_instance = NULL; + + + SIPVoIPLink::SIPVoIPLink(const AccountID& accountID) + : VoIPLink(accountID) + , _nbTryListenAddr(2) // number of times to try to start SIP listener + , _stunServer("") + , _localExternAddress("") + , _localExternPort(0) + , _audiortp(new AudioRtp()) + ,_regPort(DEFAULT_SIP_PORT) + , _useStun(false) + , _clients(0) { - // to get random number for RANDOM_PORT - srand (time(NULL)); + // to get random number for RANDOM_PORT + srand (time(NULL)); + + /* Instanciate the C++ thread */ + _evThread = new EventThread(this); + + /* Start pjsip initialization step */ + init(); } SIPVoIPLink::~SIPVoIPLink() { - terminate(); + terminate(); +} + +SIPVoIPLink* SIPVoIPLink::instance( const AccountID& id) +{ + + if(!_instance ){ + _instance = new SIPVoIPLink( id ); + } + + return _instance; +} + +void SIPVoIPLink::decrementClients (void) +{ + _clients--; + if(_clients == 0){ + terminate(); + SIPVoIPLink::_instance=NULL; + } } -bool -SIPVoIPLink::init() +bool SIPVoIPLink::init() { - _regc = NULL; - _initDone = true; - return true; + if(initDone()) + return false; + /* Initialize the pjsip library */ + pjsip_init(); + initDone(true); + + return true; } -void + void SIPVoIPLink::terminate() { - _initDone = false; + delete _evThread; _evThread = NULL; + + /* Clean shutdown of pjsip library */ + if( initDone() ) + { + pjsip_shutdown(); + } + initDone(false); } -void + void SIPVoIPLink::terminateSIPCall() { - - ost::MutexLock m(_callMapMutex); - CallMap::iterator iter = _callMap.begin(); - SIPCall *call; - while( iter != _callMap.end() ) { - call = dynamic_cast<SIPCall*>(iter->second); - if (call) { - //TODO terminate the sip call - delete call; call = 0; + + ost::MutexLock m(_callMapMutex); + CallMap::iterator iter = _callMap.begin(); + SIPCall *call; + while( iter != _callMap.end() ) { + call = dynamic_cast<SIPCall*>(iter->second); + if (call) { + // terminate the sip call + delete call; call = 0; + } + iter++; } - iter++; - } - _callMap.clear(); + _callMap.clear(); } -void + void SIPVoIPLink::getEvent() { - // Nothing anymore. PJSIP is based on asynchronous events + // We have to register the external thread so it could access the pjsip framework + if(!pj_thread_is_registered()) + pj_thread_register( NULL, desc, &thread ); + + // PJSIP polling + pj_time_val timeout = {0, 10}; + pjsip_endpt_handle_events( _endpt, &timeout); } -int -SIPVoIPLink::sendRegister() +int SIPVoIPLink::sendRegister( AccountID id ) { - AccountID id; - pj_status_t status; - - id = getAccountID(); + pj_status_t status; + int expire_value; + char contactTmp[256]; + pj_str_t svr, aor, contact; + pjsip_tx_data *tdata; + std::string tmp, hostname, username, password; + SIPAccount *account; + pjsip_regc *regc; + + account = dynamic_cast<SIPAccount *> (Manager::instance().getAccount(id)); + hostname = account->getHostname(); + username = account->getUsername(); + password = account->getPassword(); + + _mutexSIP.enterMutex(); + + /* Get the client registration information for this particular account */ + regc = account->getRegistrationInfo(); + /* If the registration already exists, delete it */ + if(regc) { + status = pjsip_regc_destroy(regc); + regc = NULL; + PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); + } - if(_regc) { - status = pjsip_regc_destroy(_regc); - _regc = NULL; - PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); - } + account->setRegister(true); - _bRegister = true; - - int expire_value = Manager::instance().getRegistrationExpireValue(); - _debug("SIP Registration Expire Value = %i\n" , expire_value); + /* Set the expire value of the message from the config file */ + expire_value = Manager::instance().getRegistrationExpireValue(); - setRegistrationState(Trying); + /* Update the state of the voip link */ + account->setRegistrationState(Trying); - return Manager::instance().getUserAgent()->addAccount(id, &_regc, getHostname(), getUsername(), getPassword(), expire_value); -} + if (!validStunServer) { + account->setRegistrationState(ErrorExistStun); + account->setRegister(false); + _mutexSIP.leaveMutex(); + return false; + } -std::string -SIPVoIPLink::SIPFromHeader(const std::string& userpart, const std::string& hostpart) -{ - return ("\"" + getFullName() + "\"" + " <sip:" + userpart + "@" + hostpart + ">"); -} + /* Create the registration according to the account ID */ + status = pjsip_regc_create(_endpt, (void*)account, ®c_cb, ®c); + if (status != PJ_SUCCESS) { + _debug("UserAgent: Unable to create regc.\n"); + _mutexSIP.leaveMutex(); + return false; + } -bool -SIPVoIPLink::sendSIPAuthentification() -{ - if (getUsername().empty()) { - /** @todo Ajouter ici un call à setRegistrationState(Error, "Fill balh") ? */ - return false; - } - if (getPassword().empty()) { - /** @todo Même chose ici ? */ - return false; - } + tmp = "sip:" + hostname; + pj_strdup2(_pool, &svr, tmp.data()); + + tmp = "<sip:" + username + "@" + hostname + ">"; + pj_strdup2(_pool, &aor, tmp.data()); + + sprintf(contactTmp, "<sip:%s@%s:%d>", username.data(), _localExternAddress.data(), _localExternPort); + pj_strdup2(_pool, &contact, contactTmp); + account->setContact(contactTmp); - return true; + status = pjsip_regc_init(regc, &svr, &aor, &aor, 1, &contact, 600); //timeout); + if (status != PJ_SUCCESS) { + _debug("UserAgent: Unable to initialize regc. %d\n", status); //, regc->str_srv_url.ptr); + _mutexSIP.leaveMutex(); + return false; + } + + pjsip_cred_info *cred = account->getCredInfo(); + + if(!cred) + cred = new pjsip_cred_info(); + + pj_bzero(cred, sizeof (pjsip_cred_info)); + pj_strdup2(_pool, &cred->username, username.data()); + cred->data_type = PJSIP_CRED_DATA_PLAIN_PASSWD; + pj_strdup2(_pool, &cred->data, password.data()); + pj_strdup2(_pool, &cred->realm, "*"); + pj_strdup2(_pool, &cred->scheme, "digest"); + pjsip_regc_set_credentials(regc, 1, cred); + + account->setCredInfo(cred); + + status = pjsip_regc_register(regc, PJ_TRUE, &tdata); + if (status != PJ_SUCCESS) { + _debug("UserAgent: Unable to register regc.\n"); + _mutexSIP.leaveMutex(); + return false; + } + + status = pjsip_regc_send(regc, tdata); + if (status != PJ_SUCCESS) { + _debug("UserAgent: Unable to send regc request.\n"); + _mutexSIP.leaveMutex(); + return false; + } + + _mutexSIP.leaveMutex(); + + account->setRegistrationInfo(regc); + + return true; } -int -SIPVoIPLink::sendUnregister() + int +SIPVoIPLink::sendUnregister( AccountID id ) { - _debug("SEND UNREGISTER for account %s\n" , getAccountID().c_str()); - - if(!_bRegister){ - setRegistrationState(VoIPLink::Unregistered); - return true; - } - - _bRegister = false; - - Manager::instance().getUserAgent()->removeAccount(_regc); - - return true; + pj_status_t status = 0; + pjsip_tx_data *tdata = NULL; + SIPAccount *account; + pjsip_regc *regc; + + account = dynamic_cast<SIPAccount *> (Manager::instance().getAccount(id)); + regc = account->getRegistrationInfo(); + + if(!account->isRegister()){ + account->setRegistrationState(Unregistered); + return true; + } + + if(regc) { + status = pjsip_regc_unregister(regc, &tdata); + if(status != PJ_SUCCESS) { + _debug("UserAgent: Unable to unregister regc.\n"); + return false; + } + + status = pjsip_regc_send( regc, tdata ); + if(status != PJ_SUCCESS) { + _debug("UserAgent: Unable to send regc request.\n"); + return false; + } + } else { + _debug("UserAgent: regc is null!\n"); + return false; + } + + account->setRegistrationInfo(regc); + account->setRegister(false); + + return true; } -Call* + Call* SIPVoIPLink::newOutgoingCall(const CallID& id, const std::string& toUrl) { - SIPCall* call = new SIPCall(id, Call::Outgoing); - if (call) { - //call->setPeerNumber(toUrl); - call->setPeerNumber(getSipTo(toUrl)); - _debug("Try to make a call to: %s with call ID: %s\n", toUrl.data(), id.data()); - // we have to add the codec before using it in SIPOutgoingInvite... - call->setCodecMap(Manager::instance().getCodecDescriptorMap()); - if ( SIPOutgoingInvite(call) ) { - call->setConnectionState(Call::Progressing); - call->setState(Call::Active); - addCall(call); - } else { - delete call; call = 0; + Account* account; + + SIPCall* call = new SIPCall(id, Call::Outgoing); + + if (call) { + account = dynamic_cast<SIPAccount *>(Manager::instance().getAccount(Manager::instance().getAccountFromCall(id))); + if(!account) + { + _debug("Error retrieving the account to the make the call with\n"); + call->setConnectionState(Call::Disconnected); + call->setState(Call::Error); + delete call; call=0; + return call; + } + //call->setPeerNumber(toUrl); + call->setPeerNumber(getSipTo(toUrl, account->getHostname())); + _debug("Try to make a call to: %s with call ID: %s\n", toUrl.data(), id.data()); + // we have to add the codec before using it in SIPOutgoingInvite... + call->setCodecMap(Manager::instance().getCodecDescriptorMap()); + if ( SIPOutgoingInvite(call) ) { + call->setConnectionState(Call::Progressing); + call->setState(Call::Active); + addCall(call); + } else { + delete call; call = 0; + } } - } - return call; + return call; } -bool + bool SIPVoIPLink::answer(const CallID& id) { - _debug("- SIP Action: start answering\n"); - SIPCall* call = getSIPCall(id); - if (call==0) { - _debug("! SIP Failure: SIPCall doesn't exists\n"); - return false; - } - - int i = Manager::instance().getUserAgent()->answer(call); - - if (i != 0) { - _debug("< SIP Building Error: send 400 Bad Request\n"); - } else { - // use exosip, bug locked - i = 0; - _debug("* SIP Info: Starting AudioRTP when answering\n"); - if (_audiortp->createNewSession(call) >= 0) { - call->setAudioStart(true); - call->setConnectionState(Call::Connected); - call->setState(Call::Active); - return true; - } else { - _debug("! SIP Failure: Unable to start sound when answering %s/%d\n", __FILE__, __LINE__); + int i; + SIPCall *call; + pj_status_t status; + pjsip_tx_data *tdata; + + _debug("- SIP Action: start answering\n"); + + call = getSIPCall(id); + + if (call==0) { + _debug("! SIP Failure: SIPCall doesn't exists\n"); + return false; } - } - removeCall(call->getCallId()); - return false; + + // User answered the incoming call, tell peer this news + if (call->startNegociation(_pool)) { + // Create and send a 200(OK) response + _debug("UserAgent: Negociation success!\n"); + status = pjsip_inv_answer(call->getInvSession(), PJSIP_SC_OK, NULL, NULL, &tdata); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); + status = pjsip_inv_send_msg(call->getInvSession(), tdata); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); + + _debug("* SIP Info: Starting AudioRTP when answering\n"); + if (_audiortp->createNewSession(call) >= 0) { + call->setAudioStart(true); + call->setConnectionState(Call::Connected); + call->setState(Call::Active); + return true; + } else { + _debug("! SIP Failure: Unable to start sound when answering %s/%d\n", __FILE__, __LINE__); + } + } + removeCall(call->getCallId()); + return false; } -bool + bool SIPVoIPLink::hangup(const CallID& id) { - SIPCall* call = getSIPCall(id); - if (call==0) { _debug("! SIP Error: Call doesn't exist\n"); return false; } - - Manager::instance().getUserAgent()->hangup(call); - - // Release RTP thread - if (Manager::instance().isCurrentCall(id)) { - _debug("* SIP Info: Stopping AudioRTP for hangup\n"); - _audiortp->closeRtpSession(); - } - removeCall(id); - return true; + pj_status_t status; + pjsip_tx_data *tdata = NULL; + SIPCall* call; + + call = getSIPCall(id); + + if (call==0) { _debug("! SIP Error: Call doesn't exist\n"); return false; } + + // User hangup current call. Notify peer + status = pjsip_inv_end_session(call->getInvSession(), 404, NULL, &tdata); + if(status != PJ_SUCCESS) + return false; + + if(tdata == NULL) + return true; + + status = pjsip_inv_send_msg(call->getInvSession(), tdata); + if(status != PJ_SUCCESS) + return false; + + call->getInvSession()->mod_data[getModId()] = NULL; + return true; + + // Release RTP thread + if (Manager::instance().isCurrentCall(id)) { + _debug("* SIP Info: Stopping AudioRTP for hangup\n"); + _audiortp->closeRtpSession(); + } + + removeCall(id); + + return true; } -bool + bool SIPVoIPLink::cancel(const CallID& id) { - SIPCall* call = getSIPCall(id); - if (call==0) { _debug("! SIP Error: Call doesn't exist\n"); return false; } + SIPCall* call = getSIPCall(id); + if (call==0) { _debug("! SIP Error: Call doesn't exist\n"); return false; } - _debug("- SIP Action: Cancel call %s [cid: %3d]\n", id.data(), call->getCid()); + _debug("- SIP Action: Cancel call %s [cid: %3d]\n", id.data(), call->getCid()); - removeCall(id); + removeCall(id); - return true; + return true; } -bool + bool SIPVoIPLink::onhold(const CallID& id) { - SIPCall* call = getSIPCall(id); - if (call==0) { _debug("! SIP Error: call doesn't exist\n"); return false; } + pj_status_t status; + pjsip_tx_data *tdata; + pjmedia_sdp_attr *attr; + pjmedia_sdp_session* local_sdp; + SIPCall* call; + + call = getSIPCall(id); + + if (call==0) { _debug("! SIP Error: call doesn't exist\n"); return false; } + + + // Stop sound + call->setAudioStart(false); + call->setState(Call::Hold); + _debug("* SIP Info: Stopping AudioRTP for onhold action\n"); + //_mutexSIP.enterMutex(); + _audiortp->closeRtpSession(); + //_mutexSIP.leaveMutex(); + + local_sdp = call->getLocalSDPSession(); + + if( local_sdp == NULL ){ + _debug("! SIP Failure: unable to find local_sdp\n"); + return false; + } - // Stop sound - call->setAudioStart(false); - call->setState(Call::Hold); - _debug("* SIP Info: Stopping AudioRTP for onhold action\n"); - _audiortp->closeRtpSession(); + /* Create re-INVITE with new offer */ + // Remove all the attributes with the specified name + pjmedia_sdp_media_remove_all_attr(local_sdp->media[0], "sendrecv"); + attr = pjmedia_sdp_attr_create(_pool, "sendonly", NULL); + pjmedia_sdp_media_add_attr(local_sdp->media[0], attr); - return Manager::instance().getUserAgent()->onhold(call); + status = pjsip_inv_reinvite( call->getInvSession(), NULL, local_sdp, &tdata); + if( status != PJ_SUCCESS ) + { + _debug("On hold: creation of the Re-invite request failed\n"); + return false; + } + /* Send the request */ + status = pjsip_inv_send_msg( call->getInvSession(), tdata); + + return (status == PJ_SUCCESS); } -bool + bool SIPVoIPLink::offhold(const CallID& id) { SIPCall *call; + pj_status_t status; + pjsip_tx_data *tdata; + pjmedia_sdp_attr *attr; + pjmedia_sdp_session* local_sdp; call = getSIPCall(id); + if (call==0) { _debug("! SIP Error: Call doesn't exist\n"); return false; } - if(!Manager::instance().getUserAgent()->offhold(call)) + local_sdp = call->getLocalSDPSession(); + if( local_sdp == NULL ){ + _debug("! SIP Failure: unable to find local_sdp\n"); + return false; + } + + /* Create re-INVITE with new offer */ + // Remove all the attributes with the specified name + pjmedia_sdp_media_remove_all_attr(local_sdp->media[0], "sendonly"); + attr = pjmedia_sdp_attr_create(_pool, "sendrecv", NULL); + pjmedia_sdp_media_add_attr(local_sdp->media[0], attr); + + status = pjsip_inv_reinvite( call->getInvSession(), NULL, local_sdp , &tdata); + if( status != PJ_SUCCESS ) + { + _debug("Off hold: creation of the Re-invite request failed\n"); + return false; + } + + /* Send the request */ + status = pjsip_inv_send_msg( call->getInvSession(), tdata); + if( status != PJ_SUCCESS ) return false; // Enable audio @@ -284,17 +599,28 @@ SIPVoIPLink::offhold(const CallID& id) _debug("! SIP Failure: Unable to start sound (%s:%d)\n", __FILE__, __LINE__); return false; } - + return true; } -bool + bool SIPVoIPLink::transfer(const CallID& id, const std::string& to) { SIPCall *call; std::string tmp_to; + pjsip_evsub *sub; + pjsip_tx_data *tdata; + struct pjsip_evsub_user xfer_cb; + pj_status_t status; + pj_str_t dest; + AccountID account_id; + Account* account; + call = getSIPCall(id); + account_id = Manager::instance().getAccountFromCall(id); + account = dynamic_cast<SIPAccount *>(Manager::instance().getAccount(account_id)); + if (call==0) { _debug("! SIP Failure: Call doesn't exist\n"); return false; @@ -302,16 +628,48 @@ SIPVoIPLink::transfer(const CallID& id, const std::string& to) tmp_to = SIPToHeader(to); if (tmp_to.find("@") == std::string::npos) { - tmp_to = tmp_to + "@" + getHostname(); + tmp_to = tmp_to + "@" + account->getHostname(); } _debug("In transfer, tmp_to is %s\n", tmp_to.data()); - return Manager::instance().getUserAgent()->transfer(call, tmp_to); + pj_strdup2(_pool, &dest, to.data()); + + /* Create xfer client subscription. */ + pj_bzero(&xfer_cb, sizeof(xfer_cb)); + xfer_cb.on_evsub_state = &xfer_func_cb; + + status = pjsip_xfer_create_uac(call->getInvSession()->dlg, &xfer_cb, &sub); + if (status != PJ_SUCCESS) { + _debug("UserAgent: Unable to create xfer -- %d\n", status); + return false; + } + + /* Associate this voiplink of call with the client subscription + * We can not just associate call with the client subscription + * because after this function, we can not find the cooresponding + * voiplink from the call any more. But the voiplink is useful! + */ + AccountID accId = Manager::instance().getAccountFromCall(call->getCallId()); + pjsip_evsub_set_mod_data(sub, getModId(), this); + + /* + * Create REFER request. + */ + status = pjsip_xfer_initiate(sub, &dest, &tdata); + if (status != PJ_SUCCESS) { + _debug("UserAgent: Unable to create REFER request -- %d\n", status); + return false; + } + + /* Send. */ + status = pjsip_xfer_send_request(sub, tdata); + if (status != PJ_SUCCESS) { + _debug("UserAgent: Unable to send REFER request -- %d\n", status); + return false; + } - //_audiortp->closeRtpSession(); - // shall we delete the call? - //removeCall(id); + return true; } bool SIPVoIPLink::transferStep2() @@ -320,10 +678,13 @@ bool SIPVoIPLink::transferStep2() return true; } -bool + bool SIPVoIPLink::refuse (const CallID& id) { SIPCall *call; + pj_status_t status; + pjsip_tx_data *tdata; + call = getSIPCall(id); @@ -338,10 +699,20 @@ SIPVoIPLink::refuse (const CallID& id) return false; } - return Manager::instance().getUserAgent()->refuse(call); + // User refuse current call. Notify peer + status = pjsip_inv_end_session(call->getInvSession(), PJSIP_SC_DECLINE, NULL, &tdata); //603 + if(status != PJ_SUCCESS) + return false; + + status = pjsip_inv_send_msg(call->getInvSession(), tdata); + if(status != PJ_SUCCESS) + return false; + + call->getInvSession()->mod_data[getModId()] = NULL; + return true; } -bool + bool SIPVoIPLink::carryingDTMFdigits(const CallID& id, char code) { @@ -349,8 +720,14 @@ SIPVoIPLink::carryingDTMFdigits(const CallID& id, char code) int duration; const int body_len = 1000; char *dtmf_body; + pj_status_t status; + pjsip_tx_data *tdata; + pj_str_t methodName, content; + pjsip_method method; + pjsip_media_type ctype; call = getSIPCall(id); + if (call==0) { _debug("Call doesn't exist\n"); return false; @@ -358,210 +735,1431 @@ SIPVoIPLink::carryingDTMFdigits(const CallID& id, char code) duration = Manager::instance().getConfigInt(SIGNALISATION, PULSE_LENGTH); dtmf_body = new char[body_len]; - + snprintf(dtmf_body, body_len - 1, "Signal=%c\r\nDuration=%d\r\n", code, duration); - - return Manager::instance().getUserAgent()->carryingDTMFdigits(call, dtmf_body); + + pj_strdup2(_pool, &methodName, "INFO"); + pjsip_method_init_np(&method, &methodName); + + /* Create request message. */ + status = pjsip_dlg_create_request( call->getInvSession()->dlg, &method, -1, &tdata); + if (status != PJ_SUCCESS) { + _debug("UserAgent: Unable to create INFO request -- %d\n", status); + return false; + } + + /* Get MIME type */ + pj_strdup2(_pool, &ctype.type, "application"); + pj_strdup2(_pool, &ctype.subtype, "dtmf-relay"); + + /* Create "application/dtmf-relay" message body. */ + pj_strdup2(_pool, &content, dtmf_body); + tdata->msg->body = pjsip_msg_body_create( tdata->pool, &ctype.type, &ctype.subtype, &content); + if (tdata->msg->body == NULL) { + _debug("UserAgent: Unable to create msg body!\n"); + pjsip_tx_data_dec_ref(tdata); + return false; + } + + /* Send the request. */ + status = pjsip_dlg_send_request( call->getInvSession()->dlg, tdata, getModId(), NULL); + if (status != PJ_SUCCESS) { + _debug("UserAgent: Unable to send MESSAGE request -- %d\n", status); + return false; + } + + return true; } -bool + bool SIPVoIPLink::SIPOutgoingInvite(SIPCall* call) { - // If no SIP proxy setting for direct call with only IP address - if (!SIPStartCall(call, "")) { - _debug("! SIP Failure: call not started\n"); - return false; - } - return true; + // If no SIP proxy setting for direct call with only IP address + if (!SIPStartCall(call, "")) { + _debug("! SIP Failure: call not started\n"); + return false; + } + return true; } -bool + bool SIPVoIPLink::SIPStartCall(SIPCall* call, const std::string& subject UNUSED) { - std::string to; + std::string strTo, strFrom; + pj_status_t status; + pjsip_dialog *dialog; + pjsip_tx_data *tdata; + pj_str_t from, to, contact; + AccountID id; + SIPAccount *account; if (!call) return false; - to = getSipTo(call->getPeerNumber()); - _debug(" To: %s\n", to.data()); + id = Manager::instance().getAccountFromCall(call->getCallId()); + // Get the basic information about the callee account + account = dynamic_cast<SIPAccount *>(Manager::instance().getAccount(id)); - return Manager::instance().getUserAgent()->makeOutgoingCall(to, call, getAccountID()); -} + strTo = getSipTo(call->getPeerNumber(), account->getHostname()); + _debug(" To: %s\n", strTo.data()); + + // Generate the from URI + strFrom = "sip:" + account->getUsername() + "@" + account->getHostname(); + + // pjsip need the from and to information in pj_str_t format + pj_strdup2(_pool, &from, strFrom.data()); + pj_strdup2(_pool, &to, strTo.data()); + pj_strdup2(_pool, &contact, account->getContact().data()); + + // create the dialog (UAC) + status = pjsip_dlg_create_uac(pjsip_ua_instance(), &from, + &contact, + &to, + NULL, + &dialog); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, false); + + setCallAudioLocal(call, getLocalIPAddress(), useStun(), getStunServer()); + call->setIp(getLocalIP()); + + // Building the local SDP offer + call->createInitialOffer(_pool); + + // Create the invite session for this call + pjsip_inv_session *inv; + status = pjsip_inv_create_uac(dialog, call->getLocalSDPSession(), 0, &inv); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, false); -std::string -SIPVoIPLink::getSipFrom() { + // Set auth information + pjsip_auth_clt_set_credentials(&dialog->auth_sess, 1, account->getCredInfo()); - // Form the From header field basis on configuration panel - std::string hostname; - - hostname = getHostname(); + // Associate current call in the invite session + inv->mod_data[getModId()] = call; - if ( hostname.empty() ) { - hostname = _localIPAddress; - } - return SIPFromHeader(getUsername(), hostname); + status = pjsip_inv_invite(inv, &tdata); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, false); + + // Associate current invite session in the call + call->setInvSession(inv); + + status = pjsip_inv_send_msg(inv, tdata); + if(status != PJ_SUCCESS) { + return false; + } + + return true; } -std::string -SIPVoIPLink::getSipTo(const std::string& to_url) { - // Form the From header field basis on configuration panel - //bool isRegistered = (_eXosipRegID == EXOSIP_ERROR_STD) ? false : true; +std::string SIPVoIPLink::getSipTo(const std::string& to_url, std::string hostname) { + // Form the From header field basis on configuration panel + //bool isRegistered = (_eXosipRegID == EXOSIP_ERROR_STD) ? false : true; - // add a @host if we are registered and there is no one inside the url + // add a @host if we are registered and there is no one inside the url if (to_url.find("@") == std::string::npos) {// && isRegistered) { - std::string host = getHostname(); - if(!host.empty()) { - return SIPToHeader(to_url + "@" + host); + if(!hostname.empty()) { + return SIPToHeader(to_url + "@" + hostname); + } + } + return SIPToHeader(to_url); } - } - return SIPToHeader(to_url); -} -std::string -SIPVoIPLink::SIPToHeader(const std::string& to) -{ - if (to.find("sip:") == std::string::npos) { - return ("sip:" + to ); - } else { - return to; - } -} + std::string SIPVoIPLink::SIPToHeader(const std::string& to) + { + if (to.find("sip:") == std::string::npos) { + return ("sip:" + to ); + } else { + return to; + } + } -bool -SIPVoIPLink::SIPCheckUrl(const std::string& url UNUSED) -{ - return true; -} + bool + SIPVoIPLink::SIPCheckUrl(const std::string& url UNUSED) + { + return true; + } + + bool setCallAudioLocal(SIPCall* call, std::string localIP, bool stun, std::string server) + { + // Setting Audio + unsigned int callLocalAudioPort = RANDOM_LOCAL_PORT; + unsigned int callLocalExternAudioPort = callLocalAudioPort; + if (stun) { + // If use Stun server + if (Manager::instance().behindNat(server, callLocalAudioPort)) { + callLocalExternAudioPort = Manager::instance().getFirewallPort(); + } + } + _debug(" Setting local audio port to: %d\n", callLocalAudioPort); + _debug(" Setting local audio port (external) to: %d\n", callLocalExternAudioPort); + + // Set local audio port for SIPCall(id) + call->setLocalIp(localIP); + call->setLocalAudioPort(callLocalAudioPort); + call->setLocalExternAudioPort(callLocalExternAudioPort); + + return true; + } -bool -SIPVoIPLink::setCallAudioLocal(SIPCall* call) -{ - // Setting Audio - unsigned int callLocalAudioPort = RANDOM_LOCAL_PORT; - unsigned int callLocalExternAudioPort = callLocalAudioPort; - if (_useStun) { - // If use Stun server - if (Manager::instance().behindNat(_stunServer, callLocalAudioPort)) { - callLocalExternAudioPort = Manager::instance().getFirewallPort(); - } - } - _debug(" Setting local audio port to: %d\n", callLocalAudioPort); - _debug(" Setting local audio port (external) to: %d\n", callLocalExternAudioPort); - - // Set local audio port for SIPCall(id) - call->setLocalIp(_localIPAddress); - call->setLocalAudioPort(callLocalAudioPort); - call->setLocalExternAudioPort(callLocalExternAudioPort); - - return true; -} + void + SIPVoIPLink::SIPCallServerFailure(SIPCall *call) + { + //if (!event->response) { return; } + //switch(event->response->status_code) { + //case SIP_SERVICE_UNAVAILABLE: // 500 + //case SIP_BUSY_EVRYWHERE: // 600 + //case SIP_DECLINE: // 603 + //SIPCall* call = findSIPCallWithCid(event->cid); + if (call != 0) { + _debug("Server error!\n"); + CallID id = call->getCallId(); + Manager::instance().callFailure(id); + removeCall(id); + } + //break; + //} + } + + void + SIPVoIPLink::SIPCallClosed(SIPCall *call) + { + // it was without did before + //SIPCall* call = findSIPCallWithCid(event->cid); + if (!call) { return; } + + CallID id = call->getCallId(); + //call->setDid(event->did); + if (Manager::instance().isCurrentCall(id)) { + call->setAudioStart(false); + _debug("* SIP Info: Stopping AudioRTP when closing\n"); + _audiortp->closeRtpSession(); + } + _debug("After close RTP\n"); + Manager::instance().peerHungupCall(id); + removeCall(id); + _debug("After remove call ID\n"); + } + + void + SIPVoIPLink::SIPCallReleased(SIPCall *call) + { + // do cleanup if exists + // only cid because did is always 0 in these case.. + //SIPCall* call = findSIPCallWithCid(event->cid); + if (!call) { return; } + + // if we are here.. something when wrong before... + _debug("SIP call release\n"); + CallID id = call->getCallId(); + Manager::instance().callFailure(id); + removeCall(id); + } + + void + SIPVoIPLink::SIPCallAnswered(SIPCall *call, pjsip_rx_data *rdata) + { + //SIPCall* call = dynamic_cast<SIPCall *>(theCall);//findSIPCallWithCid(event->cid); + if (!call) { + _debug("! SIP Failure: unknown call\n"); + return; + } + //call->setDid(event->did); + + if (call->getConnectionState() != Call::Connected) { + //call->SIPCallAnswered(event); + call->SIPCallAnsweredWithoutHold(rdata); + + call->setConnectionState(Call::Connected); + call->setState(Call::Active); + + Manager::instance().peerAnsweredCall(call->getCallId()); + if (Manager::instance().isCurrentCall(call->getCallId())) { + _debug("* SIP Info: Starting AudioRTP when answering\n"); + if ( _audiortp->createNewSession(call) < 0) { + _debug("RTP Failure: unable to create new session\n"); + } else { + call->setAudioStart(true); + } + } + } else { + _debug("* SIP Info: Answering call (on/off hold to send ACK)\n"); + //call->SIPCallAnswered(event); + } + } + + + SIPCall* + SIPVoIPLink::getSIPCall(const CallID& id) + { + Call* call = getCall(id); + if (call) { + return dynamic_cast<SIPCall*>(call); + } + return NULL; + } + + void SIPVoIPLink::setStunServer( const std::string &server ) + { + if(server != "") { + useStun(true); + _stunServer = server; + } else { + useStun(false); + _stunServer = std::string(""); + } + } -void -SIPVoIPLink::SIPCallServerFailure(SIPCall *call) -{ - //if (!event->response) { return; } - //switch(event->response->status_code) { - //case SIP_SERVICE_UNAVAILABLE: // 500 - //case SIP_BUSY_EVRYWHERE: // 600 - //case SIP_DECLINE: // 603 - //SIPCall* call = findSIPCallWithCid(event->cid); - if (call != 0) { - _debug("Server error!\n"); - CallID id = call->getCallId(); - Manager::instance().callFailure(id); - removeCall(id); - } - //break; - //} -} + /////////////////////////////////////////////////////////////////////////////// + // Private functions + /////////////////////////////////////////////////////////////////////////////// + + bool SIPVoIPLink::pjsip_init() + { + pj_status_t status; + int errPjsip = 0; + int port; + pjsip_inv_callback inv_cb; + pj_str_t accepted; + std::string name_mod; + bool useStun; + validStunServer = true; + + name_mod = "sflphone"; + + // Init PJLIB: must be called before any call to the pjsip library + status = pj_init(); + // Use pjsip macros for sanity check + PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); + + // Init PJLIB-UTIL library + status = pjlib_util_init(); + PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); + + // Set the pjsip log level + pj_log_set_level( PJ_LOG_LEVEL ); + + // Init PJNATH + status = pjnath_init(); + PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); + + // Create a pool factory to allocate memory + pj_caching_pool_init(&_cp, &pj_pool_factory_default_policy, 0); + + // Create memory pool for application. + _pool = pj_pool_create(&_cp.factory, "sflphone", 4000, 4000, NULL); + + if (!_pool) { + _debug("UserAgent: Could not initialize memory pool\n"); + return PJ_ENOMEM; + } + + // Create the SIP endpoint + status = pjsip_endpt_create(&_cp.factory, pj_gethostname()->ptr, &_endpt); + PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); + + /* Start resolving STUN server */ + // if we useStun and we failed to receive something on port 5060, we try a random port + // If use STUN server, firewall address setup + if (!loadSIPLocalIP()) { + _debug("UserAgent: Unable to determine network capabilities\n"); + return false; + } + + port = _regPort; + + /* Retrieve the STUN configuration */ + useStun = Manager::instance().getConfigInt( SIGNALISATION, SIP_USE_STUN ); + this->setStunServer(Manager::instance().getConfigString( SIGNALISATION, SIP_STUN_SERVER )); + this->useStun( useStun!=0 ? true : false); + + if (useStun && !Manager::instance().behindNat(getStunServer(), port)) { + port = RANDOM_SIP_PORT; + if (!Manager::instance().behindNat(getStunServer(), port)) { + _debug("UserAgent: Unable to check NAT setting\n"); + validStunServer = false; + return false; // hoho we can't use the random sip port too... + } + } + + _localPort = port; + if (useStun) { + // set by last behindNat() call (ish)... + stunServerResolve(); + _localExternAddress = Manager::instance().getFirewallAddress(); + _localExternPort = Manager::instance().getFirewallPort(); + errPjsip = createUDPServer(); + if (errPjsip != 0) { + _debug("UserAgent: Could not initialize SIP listener on port %d\n", port); + return errPjsip; + } + } else { + _localExternAddress = _localIPAddress; + _localExternPort = _localPort; + errPjsip = createUDPServer(); + if (errPjsip != 0) { + _debug("UserAgent: Could not initialize SIP listener on port %d\n", _localExternPort); + _localExternPort = _localPort = RANDOM_SIP_PORT; + _debug("UserAgent: Try to initialize SIP listener on port %d\n", _localExternPort); + errPjsip = createUDPServer(); + if (errPjsip != 0) { + _debug("UserAgent: Fail to initialize SIP listener on port %d\n", _localExternPort); + return errPjsip; + } + } + } + + _debug("UserAgent: SIP Init -- listening on port %d\n", _localExternPort); + + // Initialize transaction layer + status = pjsip_tsx_layer_init_module(_endpt); + PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); + + // Initialize UA layer module + status = pjsip_ua_init_module(_endpt, NULL); + PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); + + // Initialize Replaces support. See the Replaces specification in RFC 3891 + status = pjsip_replaces_init_module(_endpt); + PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); + + // Initialize 100rel support + status = pjsip_100rel_init_module(_endpt); + PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); + + // Initialize and register sflphone module + _mod_ua.name = pj_str((char*)name_mod.c_str()); + _mod_ua.id = -1; + _mod_ua.priority = PJSIP_MOD_PRIORITY_APPLICATION; + _mod_ua.on_rx_request = &mod_on_rx_request; + _mod_ua.on_rx_response = &mod_on_rx_response; + + status = pjsip_endpt_register_module(_endpt, &_mod_ua); + PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); + + // Init the event subscription module. + // It extends PJSIP by supporting SUBSCRIBE and NOTIFY methods + status = pjsip_evsub_init_module(_endpt); + PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); + + // Init xfer/REFER module + status = pjsip_xfer_init_module(_endpt); + PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); + + // Init the callback for INVITE session: + pj_bzero(&inv_cb, sizeof (inv_cb)); + + inv_cb.on_state_changed = &call_on_state_changed; + inv_cb.on_new_session = &call_on_forked; + inv_cb.on_media_update = &call_on_media_update; + inv_cb.on_tsx_state_changed = &call_on_tsx_changed; + + // Initialize session invite module + status = pjsip_inv_usage_init(_endpt, &inv_cb); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); + + _debug("UserAgent: VOIP callbacks initialized\n"); + + // Add endpoint capabilities (INFO, OPTIONS, etc) for this UA + pj_str_t allowed[] = { {(char*)"INFO", 4}, {(char*)"REGISTER", 8} }; // //{"INVITE", 6}, {"ACK",3}, {"BYE",3}, {"CANCEL",6}, {"OPTIONS", 7}, + accepted = pj_str((char*)"application/sdp"); + + // Register supported methods + pjsip_endpt_add_capability(_endpt, &_mod_ua, PJSIP_H_ALLOW, NULL, PJ_ARRAY_SIZE(allowed), allowed); + + // Register "application/sdp" in ACCEPT header + pjsip_endpt_add_capability(_endpt, &_mod_ua, PJSIP_H_ACCEPT, NULL, 1, &accepted); + + _debug("UserAgent: pjsip version %s for %s initialized\n", pj_get_version(), PJ_OS_NAME); + + // Create the secondary thread to poll sip events + _evThread->start(); + + /* Done! */ + return PJ_SUCCESS; + } -void -SIPVoIPLink::SIPCallClosed(SIPCall *call) -{ - // it was without did before - //SIPCall* call = findSIPCallWithCid(event->cid); - if (!call) { return; } + pj_status_t SIPVoIPLink::stunServerResolve( void ) + { + pj_str_t stun_adr; + pj_hostent he; + pj_stun_config stunCfg; + pj_status_t stun_status; + pj_sockaddr stun_srv; + size_t pos; + std::string serverName, serverPort; + int nPort; + std::string stun_server; + + stun_server = getStunServer(); + + // Initialize STUN configuration + pj_stun_config_init(&stunCfg, &_cp.factory, 0, pjsip_endpt_get_ioqueue(_endpt), pjsip_endpt_get_timer_heap(_endpt)); + + stun_status = PJ_EPENDING; + + // Init STUN socket + pos = stun_server.find(':'); + if(pos == std::string::npos) { + pj_strdup2(_pool, &stun_adr, stun_server.data()); + stun_status = pj_sockaddr_in_init(&stun_srv.ipv4, &stun_adr, (pj_uint16_t) 3478); + } else { + serverName = stun_server.substr(0, pos); + serverPort = stun_server.substr(pos + 1); + nPort = atoi(serverPort.data()); + pj_strdup2(_pool, &stun_adr, serverName.data()); + stun_status = pj_sockaddr_in_init(&stun_srv.ipv4, &stun_adr, (pj_uint16_t) nPort); + } + + if (stun_status != PJ_SUCCESS) { + _debug("UserAgent: Unresolved stun server!\n"); + stun_status = pj_gethostbyname(&stun_adr, &he); + + if (stun_status == PJ_SUCCESS) { + pj_sockaddr_in_init(&stun_srv.ipv4, NULL, 0); + stun_srv.ipv4.sin_addr = *(pj_in_addr*) he.h_addr; + stun_srv.ipv4.sin_port = pj_htons((pj_uint16_t) 3478); + } + } + + return stun_status; + } - CallID id = call->getCallId(); - //call->setDid(event->did); - if (Manager::instance().isCurrentCall(id)) { - call->setAudioStart(false); - _debug("* SIP Info: Stopping AudioRTP when closing\n"); - _audiortp->closeRtpSession(); - } - _debug("After close RTP\n"); - Manager::instance().peerHungupCall(id); - removeCall(id); - _debug("After remove call ID\n"); -} + int SIPVoIPLink::createUDPServer( void ) + { + + pj_status_t status; + pj_sockaddr_in bound_addr; + pjsip_host_port a_name; + char tmpIP[32]; + pj_sock_t sock; + + // Init bound address to ANY + pj_memset(&bound_addr, 0, sizeof (bound_addr)); + bound_addr.sin_addr.s_addr = PJ_INADDR_ANY; + + // Create UDP server socket + status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &sock); + if (status != PJ_SUCCESS) { + _debug("UserAgent: (%d) UDP socket() error\n", status); + return status; + } + + status = pj_sock_bind_in(sock, pj_ntohl(bound_addr.sin_addr.s_addr), (pj_uint16_t) _localPort); + if (status != PJ_SUCCESS) { + _debug("UserAgent: (%d) UDP bind() error\n", status); + pj_sock_close(sock); + return status; + } + + _debug("UserAgent: Use IP: %s\n", _localExternAddress.data()); + + // Create UDP-Server (default port: 5060) + strcpy(tmpIP, _localExternAddress.data()); + pj_strdup2(_pool, &a_name.host, tmpIP); + a_name.port = (pj_uint16_t) _localExternPort; + + _debug("a_name: host: %s - port : %i\n", a_name.host.ptr, a_name.port); + + status = pjsip_udp_transport_attach(_endpt, sock, &a_name, 1, NULL); + if (status != PJ_SUCCESS) { + _debug("UserAgent: (%d) Unable to start UDP transport!\n", status); + return -1; + } else { + _debug("UserAgent: UDP server listening on port %d\n", _localExternPort); + } + + return PJ_SUCCESS; + } -void -SIPVoIPLink::SIPCallReleased(SIPCall *call) -{ - // do cleanup if exists - // only cid because did is always 0 in these case.. - //SIPCall* call = findSIPCallWithCid(event->cid); - if (!call) { return; } - - // if we are here.. something when wrong before... - _debug("SIP call release\n"); - CallID id = call->getCallId(); - Manager::instance().callFailure(id); - removeCall(id); -} + bool SIPVoIPLink::loadSIPLocalIP() { + + bool returnValue = true; + + if (_localIPAddress == "127.0.0.1") { + pj_sockaddr ip_addr; + if (pj_gethostip(pj_AF_INET(), &ip_addr) != PJ_SUCCESS) { + // Update the registration state if no network capabilities found + _debug("UserAgent: Get host ip failed!\n"); + returnValue = false; + } else { + _localIPAddress = std::string(pj_inet_ntoa(ip_addr.ipv4.sin_addr)); + _debug("UserAgent: Checking network, setting local IP address to: %s\n", _localIPAddress.data()); + } + } + return returnValue; + } -void -SIPVoIPLink::SIPCallAnswered(SIPCall *call, pjsip_rx_data *rdata) -{ - //SIPCall* call = dynamic_cast<SIPCall *>(theCall);//findSIPCallWithCid(event->cid); - if (!call) { - _debug("! SIP Failure: unknown call\n"); - return; - } - //call->setDid(event->did); - - if (call->getConnectionState() != Call::Connected) { - //call->SIPCallAnswered(event); - call->SIPCallAnsweredWithoutHold(rdata); - - call->setConnectionState(Call::Connected); - call->setState(Call::Active); + bool SIPVoIPLink::pjsip_shutdown( void ) + { + /* Destroy endpoint. */ + if (_endpt) { + pjsip_endpt_destroy(_endpt); + _endpt = NULL; + } + + /* Destroy pool and pool factory. */ + if (_pool) { + pj_pool_release(_pool); + _pool = NULL; + pj_caching_pool_destroy(&_cp); + } + + /* Shutdown PJLIB */ + pj_shutdown(); + + /* Done. */ + } - Manager::instance().peerAnsweredCall(call->getCallId()); - if (Manager::instance().isCurrentCall(call->getCallId())) { - _debug("* SIP Info: Starting AudioRTP when answering\n"); - if ( _audiortp->createNewSession(call) < 0) { - _debug("RTP Failure: unable to create new session\n"); - } else { - call->setAudioStart(true); - } - } - } else { - _debug("* SIP Info: Answering call (on/off hold to send ACK)\n"); - //call->SIPCallAnswered(event); - } -} + int SIPVoIPLink::getModId(){ + return _mod_ua.id; + } + void set_voicemail_info( AccountID account, pjsip_msg_body *body ){ -SIPCall* -SIPVoIPLink::getSIPCall(const CallID& id) -{ - Call* call = getCall(id); - if (call) { - return dynamic_cast<SIPCall*>(call); - } - return NULL; -} + int voicemail, pos_begin, pos_end; + std::string voice_str = "Voice-Message: "; + std::string delimiter = "/"; + std::string msg_body, voicemail_str; -/////////////////////////////////////////////////////////////////////////////// -// Private functions -/////////////////////////////////////////////////////////////////////////////// + _debug("UserAgent: checking the voice message!\n"); + // The voicemail message is formated like that: + // Voice-Message: 1/0 . 1 is the number we want to retrieve in this case -pj_str_t SIPVoIPLink::string2PJStr(const std::string &value) -{ - char tmp[256]; - - strcpy(tmp, value.data()); - return pj_str(tmp); -} + // We get the notification body + msg_body = (char*)body->data; + + // We need the position of the first character of the string voice_str + pos_begin = msg_body.find(voice_str); + // We need the position of the delimiter + pos_end = msg_body.find(delimiter); + + // So our voicemail number between the both index + try { + + voicemail_str = msg_body.substr(pos_begin + voice_str.length(), pos_end - ( pos_begin + voice_str.length())); + std::cout << "voicemail number : " << voicemail_str << std::endl; + voicemail = atoi( voicemail_str.c_str() ); + } + catch( std::out_of_range& e ){ + std::cerr << e.what() << std::endl; + } + + // We need now to notify the manager + if( voicemail != 0 ) + Manager::instance().startVoiceMessageNotification(account, voicemail); + } + + /*******************************/ + /* CALLBACKS IMPLEMENTATION */ + /*******************************/ + + void call_on_state_changed( pjsip_inv_session *inv, pjsip_event *e){ + + PJ_UNUSED_ARG(inv); + + SIPCall *call = reinterpret_cast<SIPCall*> (inv->mod_data[_mod_ua.id]); + if(!call) + return; + + /* If this is an outgoing INVITE that was created because of + * REFER/transfer, send NOTIFY to transferer. + */ + if (call->getXferSub() && e->type==PJSIP_EVENT_TSX_STATE) { + int st_code = -1; + pjsip_evsub_state ev_state = PJSIP_EVSUB_STATE_ACTIVE; + + switch (call->getInvSession()->state) { + case PJSIP_INV_STATE_NULL: + case PJSIP_INV_STATE_CALLING: + /* Do nothing */ + break; + + case PJSIP_INV_STATE_EARLY: + case PJSIP_INV_STATE_CONNECTING: + st_code = e->body.tsx_state.tsx->status_code; + ev_state = PJSIP_EVSUB_STATE_ACTIVE; + break; + + case PJSIP_INV_STATE_CONFIRMED: + /* When state is confirmed, send the final 200/OK and terminate + * subscription. + */ + st_code = e->body.tsx_state.tsx->status_code; + ev_state = PJSIP_EVSUB_STATE_TERMINATED; + break; + + case PJSIP_INV_STATE_DISCONNECTED: + st_code = e->body.tsx_state.tsx->status_code; + ev_state = PJSIP_EVSUB_STATE_TERMINATED; + break; + + case PJSIP_INV_STATE_INCOMING: + /* Nothing to do. Just to keep gcc from complaining about + * unused enums. + */ + break; + } + + if (st_code != -1) { + pjsip_tx_data *tdata; + pj_status_t status; + + status = pjsip_xfer_notify( call->getXferSub(), + ev_state, st_code, + NULL, &tdata); + if (status != PJ_SUCCESS) { + _debug("UserAgent: Unable to create NOTIFY -- %d\n", status); + } else { + status = pjsip_xfer_send_request(call->getXferSub(), tdata); + if (status != PJ_SUCCESS) { + _debug("UserAgent: Unable to send NOTIFY -- %d\n", status); + } + } + } + } + } + + void call_on_media_update( pjsip_inv_session *inv UNUSED, pj_status_t status UNUSED) { + _debug("call_on_media_updated\n"); + } + + void call_on_forked(pjsip_inv_session *inv, pjsip_event *e){ + _debug("call_on_forked\n"); + } + + void call_on_tsx_changed(pjsip_inv_session *inv, pjsip_transaction *tsx, pjsip_event *e){ + + pjsip_rx_data *rdata; + AccountID accId; + SIPCall *call; + SIPVoIPLink *link; + pjsip_msg *msg; + + if(pj_strcmp2(&tsx->method.name, "INFO") == 0) { + // Receive a INFO message, ingore it! + return; + } + + //Retrieve the body message + rdata = e->body.tsx_state.src.rdata; + + if (tsx->role == PJSIP_ROLE_UAC) { + switch (tsx->state) { + case PJSIP_TSX_STATE_TERMINATED: + if (tsx->status_code == 200 && + pjsip_method_cmp(&tsx->method, pjsip_get_refer_method()) != 0) { + // Peer answered the outgoing call + _debug("UserAgent: Peer answered the outgoing call!\n"); + call = reinterpret_cast<SIPCall *> (inv->mod_data[_mod_ua.id]); + if (call == NULL) + return; + + //_debug("UserAgent: The call id is %s\n", call->getCallId().data()); + + accId = Manager::instance().getAccountFromCall(call->getCallId()); + link = dynamic_cast<SIPVoIPLink *> (Manager::instance().getAccountLink(accId)); + if (link) + link->SIPCallAnswered(call, rdata); + } else if (tsx->status_code / 100 == 5) { + _debug("UserAgent: 5xx error message received\n"); + } + break; + case PJSIP_TSX_STATE_PROCEEDING: + // Peer is ringing for the outgoing call + msg = rdata->msg_info.msg; + + call = reinterpret_cast<SIPCall *> (inv->mod_data[_mod_ua.id]); + if (call == NULL) + return; + + if (msg->line.status.code == 180) { + _debug("UserAgent: Peer is ringing!\n"); + + call->setConnectionState(Call::Ringing); + Manager::instance().peerRingingCall(call->getCallId()); + } + break; + case PJSIP_TSX_STATE_COMPLETED: + if (tsx->status_code == 407 || tsx->status_code == 401) //FIXME + break; + if (tsx->status_code / 100 == 6 || tsx->status_code / 100 == 4) { + // We get error message of outgoing call from server + _debug("UserAgent: Server error message is received!\n"); + call = reinterpret_cast<SIPCall *> (inv->mod_data[_mod_ua.id]); + if (call == NULL) { + _debug("UserAgent: Call has been removed!\n"); + return; + } + accId = Manager::instance().getAccountFromCall(call->getCallId()); + link = dynamic_cast<SIPVoIPLink *> (Manager::instance().getAccountLink(accId)); + if (link) { + link->SIPCallServerFailure(call); + } + } + break; + default: + break; + } // end of switch + + } else { + switch (tsx->state) { + case PJSIP_TSX_STATE_TRYING: + if (pjsip_method_cmp(&tsx->method, pjsip_get_refer_method()) == 0) { + // Peer ask me to transfer call to another number. + _debug("UserAgent: Incoming REFER request!\n"); + //onCallTransfered(inv, e->body.tsx_state.src.rdata); + } + break; + case PJSIP_TSX_STATE_COMPLETED: + if (tsx->status_code == 200 && tsx->method.id == PJSIP_BYE_METHOD) { + // Peer hangup the call + _debug("UserAgent: Peer hangup(bye) message is received!\n"); + call = reinterpret_cast<SIPCall *> (inv->mod_data[_mod_ua.id]); + if (call == NULL) { + _debug("UserAgent: Call has been removed!\n"); + return; + } + accId = Manager::instance().getAccountFromCall(call->getCallId()); + link = dynamic_cast<SIPVoIPLink *> (Manager::instance().getAccountLink(accId)); + if (link) { + link->SIPCallClosed(call); + } + } else if (tsx->status_code == 200 && tsx->method.id == PJSIP_CANCEL_METHOD) { + // Peer refuse the call + _debug("UserAgent: Cancel message is received!\n"); + call = reinterpret_cast<SIPCall *> (inv->mod_data[_mod_ua.id]); + if (call == NULL) { + _debug("UserAgent: Call has been removed!\n"); + return; + } + + accId = Manager::instance().getAccountFromCall(call->getCallId()); + link = dynamic_cast<SIPVoIPLink *> (Manager::instance().getAccountLink(accId)); + if (link) { + link->SIPCallClosed(call); + } + } + break; + default: + break; + } // end of switch + } + } + + void regc_cb(struct pjsip_regc_cbparam *param){ + + //AccountID *id = static_cast<AccountID *> (param->token); + SIPAccount *account; + + //_debug("UserAgent: Account ID is %s, Register result: %d, Status: %d\n", id->data(), param->status, param->code); + account = static_cast<SIPAccount *>(param->token); + if(!account) + return; + + if (param->status == PJ_SUCCESS) { + if (param->code < 0 || param->code >= 300) { + /* Sometimes, the status is OK, but we still failed. + * So checking the code for real result + */ + _debug("UserAgent: The error is: %d\n", param->code); + switch(param->code) { + case 408: + case 606: + account->setRegistrationState(ErrorConfStun); + break; + case 503: + account->setRegistrationState(ErrorHost); + break; + case 401: + case 403: + case 404: + account->setRegistrationState(ErrorAuth); + break; + default: + account->setRegistrationState(Error); + break; + } + account->setRegister(false); + } else { + // Registration/Unregistration is success + + if(account->isRegister()) + account->setRegistrationState(Registered); + else { + account->setRegistrationState(Unregistered); + account->setRegister(false); + } + } + } else { + account->setRegistrationState(ErrorAuth); + account->setRegister(false); + } + + } + + pj_bool_t + mod_on_rx_request(pjsip_rx_data *rdata) + { + + pj_status_t status; + pj_str_t reason; + unsigned options = 0; + pjsip_dialog* dialog; + pjsip_tx_data *tdata; + AccountID account_id; + pjsip_uri *uri; + pjsip_sip_uri *sip_uri; + std::string userName, server, caller, callerServer, peerNumber; + SIPVoIPLink *link; + CallID id; + SIPCall* call; + + // voicemail part + std::string method_name; + std::string request; + + // Handle the incoming call invite in this function + _debug("UserAgent: Callback on_rx_request is involved!\n"); + + /* First, let's got the username and server name from the invite. + * We will use them to detect which account is the callee. + */ + uri = rdata->msg_info.to->uri; + sip_uri = (pjsip_sip_uri *) pjsip_uri_get_uri(uri); + + userName = std::string(sip_uri->user.ptr, sip_uri->user.slen); + server = std::string(sip_uri->host.ptr, sip_uri->host.slen) ; + + // Get the account id of callee from username and server + account_id = Manager::instance().getAccountIdFromNameAndServer(userName, server); + + /* If we don't find any account to receive the call */ + if(account_id == AccountNULL) { + _debug("UserAgent: Username %s doesn't match any account!\n",userName.c_str()); + return false; + } + + /* Get the voip link associated to the incoming call */ + /* The account must before have been associated to the call in ManagerImpl */ + link = dynamic_cast<SIPVoIPLink *> (Manager::instance().getAccountLink(account_id)); + + /* If we can't find any voIP link to handle the incoming call */ + if( link == 0 ) + { + _debug("ERROR: can not retrieve the voiplink from the account ID...\n"); + return false; + } + + _debug("UserAgent: The receiver is : %s@%s\n", userName.data(), server.data()); + _debug("UserAgent: The callee account id is %s\n", account_id.c_str()); + + /* Now, it is the time to find the information of the caller */ + uri = rdata->msg_info.from->uri; + sip_uri = (pjsip_sip_uri *) pjsip_uri_get_uri(uri); + + /* Retrieve only the fisrt characters */ + caller = std::string(sip_uri->user.ptr, sip_uri->user.slen); + callerServer = std::string(sip_uri->host.ptr, sip_uri->host.slen); + peerNumber = caller + "@" + callerServer; + + // Get the server voicemail notification + // Catch the NOTIFY message + if( rdata->msg_info.msg->line.req.method.id == PJSIP_OTHER_METHOD ) + { + method_name = "NOTIFY"; + // Retrieve all the message. Should contains only the method name but ... + request = rdata->msg_info.msg->line.req.method.name.ptr; + // Check if the message is a notification + if( request.find( method_name ) != (size_t)-1 ) { + /* Notify the right account */ + set_voicemail_info( account_id, rdata->msg_info.msg->body ); + } + pjsip_endpt_respond_stateless(_endpt, rdata, PJSIP_SC_OK, NULL, NULL, NULL); + return true; + } + + // Respond statelessly any non-INVITE requests with 500 + if (rdata->msg_info.msg->line.req.method.id != PJSIP_INVITE_METHOD) { + if (rdata->msg_info.msg->line.req.method.id != PJSIP_ACK_METHOD) { + pj_strdup2(_pool, &reason, "user agent unable to handle this request "); + pjsip_endpt_respond_stateless( _endpt, rdata, PJSIP_SC_METHOD_NOT_ALLOWED, &reason, NULL, + NULL); + return true; + } + } + + // Verify that we can handle the request + status = pjsip_inv_verify_request(rdata, &options, NULL, NULL, _endpt, NULL); + if (status != PJ_SUCCESS) { + pj_strdup2(_pool, &reason, "user agent unable to handle this INVITE "); + pjsip_endpt_respond_stateless( _endpt, rdata, PJSIP_SC_METHOD_NOT_ALLOWED, &reason, NULL, + NULL); + return true; + } + + // Generate a new call ID for the incoming call! + id = Manager::instance().getNewCallID(); + call = new SIPCall(id, Call::Incoming); + + /* If an error occured at the call creation */ + if (!call) { + _debug("UserAgent: unable to create an incoming call"); + return false; + } + + // Set the codec map, IP, peer number and so on... for the SIPCall object + setCallAudioLocal(call, link->getLocalIPAddress(), link->useStun(), link->getStunServer()); + call->setCodecMap(Manager::instance().getCodecDescriptorMap()); + call->setConnectionState(Call::Progressing); + call->setIp(link->getLocalIPAddress()); + call->setPeerNumber(peerNumber); + + /* Call the SIPCallInvite function to generate the local sdp, + * remote sdp and negociator. + * This function is also used to set the parameters of audio RTP, including: + * local IP and port number + * remote IP and port number + * possilbe audio codec will be used in this call + */ + if (call->SIPCallInvite(rdata, _pool)) { + + // Notify UI there is an incoming call + if (Manager::instance().incomingCall(call, account_id)) { + // Add this call to the callAccountMap in ManagerImpl + Manager::instance().getAccountLink(account_id)->addCall(call); + } else { + // Fail to notify UI + delete call; + call = NULL; + _debug("UserAgent: Fail to notify UI!\n"); + return false; + } + } else { + // Fail to collect call information + delete call; + call = NULL; + _debug("UserAgent: Call SIPCallInvite failed!\n"); + return false; + } + + /* Create the local dialog (UAS) */ + status = pjsip_dlg_create_uas(pjsip_ua_instance(), rdata, NULL, &dialog); + if (status != PJ_SUCCESS) { + pjsip_endpt_respond_stateless( _endpt, rdata, PJSIP_SC_INTERNAL_SERVER_ERROR, &reason, NULL, + NULL); + return true; + } + + // Specify media capability during invite session creation + pjsip_inv_session *inv; + status = pjsip_inv_create_uas(dialog, rdata, call->getLocalSDPSession(), 0, &inv); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); + + // Associate the call in the invite session + inv->mod_data[_mod_ua.id] = call; + + // Send a 180/Ringing response + status = pjsip_inv_initial_answer(inv, rdata, PJSIP_SC_RINGING, NULL, NULL, &tdata); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); + status = pjsip_inv_send_msg(inv, tdata); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); + + // Associate invite session to the current call + call->setInvSession(inv); + + // Update the connection state + call->setConnectionState(Call::Ringing); + + /* Done */ + return true; + + } + + pj_bool_t mod_on_rx_response(pjsip_rx_data *rdata UNUSED) { + _debug("mod_on_rx_response\n"); + return PJ_SUCCESS; + } + + void onCallTransfered(pjsip_inv_session *inv, pjsip_rx_data *rdata) + { + pj_status_t status; + pjsip_tx_data *tdata; + SIPCall *existing_call; + const pj_str_t str_refer_to = { (char*)"Refer-To", 8}; + const pj_str_t str_refer_sub = { (char*)"Refer-Sub", 9 }; + const pj_str_t str_ref_by = { (char*)"Referred-By", 11 }; + pjsip_generic_string_hdr *refer_to; + pjsip_generic_string_hdr *refer_sub; + pjsip_hdr *ref_by_hdr; + pj_bool_t no_refer_sub = PJ_FALSE; + char *uri; + std::string tmp; + pjsip_status_code code; + pjsip_evsub *sub; + + existing_call = (SIPCall *) inv->mod_data[_mod_ua.id]; + + /* Find the Refer-To header */ + refer_to = (pjsip_generic_string_hdr*) + pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL); + + if (refer_to == NULL) { + /* Invalid Request. + * No Refer-To header! + */ + _debug("UserAgent: Received REFER without Refer-To header!\n"); + pjsip_dlg_respond( inv->dlg, rdata, 400, NULL, NULL, NULL); + return; + } + + /* Find optional Refer-Sub header */ + refer_sub = (pjsip_generic_string_hdr*) + pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_sub, NULL); + + if (refer_sub) { + if (!pj_strnicmp2(&refer_sub->hvalue, "true", 4)==0) + no_refer_sub = PJ_TRUE; + } + + /* Find optional Referred-By header (to be copied onto outgoing INVITE + * request. + */ + ref_by_hdr = (pjsip_hdr*) + pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_ref_by, + NULL); + + /* Notify callback */ + code = PJSIP_SC_ACCEPTED; + + _debug("UserAgent: Call to %.*s is being transfered to %.*s\n", + (int)inv->dlg->remote.info_str.slen, + inv->dlg->remote.info_str.ptr, + (int)refer_to->hvalue.slen, + refer_to->hvalue.ptr); + + if (no_refer_sub) { + /* + * Always answer with 2xx. + */ + pjsip_tx_data *tdata; + const pj_str_t str_false = { (char*)"false", 5}; + pjsip_hdr *hdr; + + status = pjsip_dlg_create_response(inv->dlg, rdata, code, NULL, + &tdata); + if (status != PJ_SUCCESS) { + _debug("UserAgent: Unable to create 2xx response to REFER -- %d\n", status); + return; + } + + /* Add Refer-Sub header */ + hdr = (pjsip_hdr*) + pjsip_generic_string_hdr_create(tdata->pool, &str_refer_sub, + &str_false); + pjsip_msg_add_hdr(tdata->msg, hdr); + + + /* Send answer */ + status = pjsip_dlg_send_response(inv->dlg, pjsip_rdata_get_tsx(rdata), + tdata); + if (status != PJ_SUCCESS) { + _debug("UserAgent: Unable to create 2xx response to REFER -- %d\n", status); + return; + } + + /* Don't have subscription */ + sub = NULL; + + } else { + struct pjsip_evsub_user xfer_cb; + pjsip_hdr hdr_list; + + /* Init callback */ + pj_bzero(&xfer_cb, sizeof(xfer_cb)); + xfer_cb.on_evsub_state = &xfer_svr_cb; + + /* Init addiTHIS_FILE, THIS_FILE, tional header list to be sent with REFER response */ + pj_list_init(&hdr_list); + + /* Create transferee event subscription */ + status = pjsip_xfer_create_uas( inv->dlg, &xfer_cb, rdata, &sub); + if (status != PJ_SUCCESS) { + _debug("UserAgent: Unable to create xfer uas -- %d\n", status); + pjsip_dlg_respond( inv->dlg, rdata, 500, NULL, NULL, NULL); + return; + } + + /* If there's Refer-Sub header and the value is "true", send back + * Refer-Sub in the response with value "true" too. + */ + if (refer_sub) { + const pj_str_t str_true = { (char*)"true", 4 }; + pjsip_hdr *hdr; + + hdr = (pjsip_hdr*) + pjsip_generic_string_hdr_create(inv->dlg->pool, + &str_refer_sub, + &str_true); + pj_list_push_back(&hdr_list, hdr); + + } + + /* Accept the REFER request, send 2xx. */ + pjsip_xfer_accept(sub, rdata, code, &hdr_list); + + /* Create initial NOTIFY request */ + status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_ACTIVE, + 100, NULL, &tdata); + if (status != PJ_SUCCESS) { + _debug("UserAgent: Unable to create NOTIFY to REFER -- %d", status); + return; + } + + /* Send initial NOTIFY request */ + status = pjsip_xfer_send_request( sub, tdata); + if (status != PJ_SUCCESS) { + _debug("UserAgent: Unable to send NOTIFY to REFER -- %d\n", status); + return; + } + } + + /* We're cheating here. + * We need to get a null terminated string from a pj_str_t. + * So grab the pointer from the hvalue and NULL terminate it, knowing + * that the NULL position will be occupied by a newline. + */ + uri = refer_to->hvalue.ptr; + uri[refer_to->hvalue.slen] = '\0'; + + /* Now make the outgoing call. */ + tmp = std::string(uri); + + if(existing_call == NULL) { + _debug("UserAgent: Call doesn't exist!\n"); + return; + } + + AccountID accId = Manager::instance().getAccountFromCall(existing_call->getCallId()); + CallID newCallId = Manager::instance().getNewCallID(); + + if(!Manager::instance().outgoingCall(accId, newCallId, tmp)) { + + /* Notify xferer about the error (if we have subscription) */ + if (sub) { + status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED, + 500, NULL, &tdata); + if (status != PJ_SUCCESS) { + _debug("UserAgent: Unable to create NOTIFY to REFER -- %d\n", status); + return; + } + status = pjsip_xfer_send_request(sub, tdata); + if (status != PJ_SUCCESS) { + _debug("UserAgent: Unable to send NOTIFY to REFER -- %d\n", status); + return; + } + } + return; + } + + SIPCall* newCall; + SIPVoIPLink *link = dynamic_cast<SIPVoIPLink *> (Manager::instance().getAccountLink(accId)); + if(link) { + newCall = dynamic_cast<SIPCall *>(link->getCall(newCallId)); + if(!newCall) { + _debug("UserAgent: can not find the call from sipvoiplink!\n"); + return; + } + } + + if (sub) { + /* Put the server subscription in inv_data. + * Subsequent state changed in pjsua_inv_on_state_changed() will be + * reported back to the server subscription. + */ + newCall->setXferSub(sub); + + /* Put the invite_data in the subscription. */ + pjsip_evsub_set_mod_data(sub, _mod_ua.id, + newCall); + } + } + + + + + void xfer_func_cb( pjsip_evsub *sub, pjsip_event *event){ + + PJ_UNUSED_ARG(event); + + _debug("UserAgent: Transfer callback is involved!\n"); + /* + * When subscription is accepted (got 200/OK to REFER), check if + * subscription suppressed. + */ + if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACCEPTED) { + + pjsip_rx_data *rdata; + pjsip_generic_string_hdr *refer_sub; + const pj_str_t REFER_SUB = {(char*)"Refer-Sub", 9 }; + + SIPVoIPLink *link = reinterpret_cast<SIPVoIPLink *> (pjsip_evsub_get_mod_data(sub, + _mod_ua.id)); + + /* Must be receipt of response message */ + pj_assert(event->type == PJSIP_EVENT_TSX_STATE && + event->body.tsx_state.type == PJSIP_EVENT_RX_MSG); + rdata = event->body.tsx_state.src.rdata; + + /* Find Refer-Sub header */ + refer_sub = (pjsip_generic_string_hdr*) + pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, + &REFER_SUB, NULL); + + /* Check if subscription is suppressed */ + if (refer_sub && pj_stricmp2(&refer_sub->hvalue, "false")==0) { + /* Since no subscription is desired, assume that call has been + * transfered successfully. + */ + if (link) { + // It's the time to stop the RTP + link->transferStep2(); + } + + /* Yes, subscription is suppressed. + * Terminate our subscription now. + */ + _debug("UserAgent: Xfer subscription suppressed, terminating event subcription...\n"); + pjsip_evsub_terminate(sub, PJ_TRUE); + + } else { + /* Notify application about call transfer progress. + * Initially notify with 100/Accepted status. + */ + _debug("UserAgent: Xfer subscription 100/Accepted received...\n"); + } + } + /* + * On incoming NOTIFY, notify application about call transfer progress. + */ + else if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACTIVE || + pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) + { + pjsip_msg *msg; + pjsip_msg_body *body; + pjsip_status_line status_line; + pj_bool_t is_last; + pj_bool_t cont; + pj_status_t status; + + SIPVoIPLink *link = reinterpret_cast<SIPVoIPLink *> (pjsip_evsub_get_mod_data(sub, + _mod_ua.id)); + + /* When subscription is terminated, clear the xfer_sub member of + * the inv_data. + */ + if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) { + pjsip_evsub_set_mod_data(sub, _mod_ua.id, NULL); + _debug("UserAgent: Xfer client subscription terminated\n"); + + } + + if (!link || !event) { + /* Application is not interested with call progress status */ + _debug("UserAgent: Either link or event is empty!\n"); + return; + } + + // Get current call + SIPCall *call = dynamic_cast<SIPCall *>(link->getCall(Manager::instance().getCurrentCallId())); + if(!call) { + _debug("UserAgent: Call doesn't exit!\n"); + return; + } + + /* This better be a NOTIFY request */ + if (event->type == PJSIP_EVENT_TSX_STATE && + event->body.tsx_state.type == PJSIP_EVENT_RX_MSG) + { + pjsip_rx_data *rdata; + + rdata = event->body.tsx_state.src.rdata; + + /* Check if there's body */ + msg = rdata->msg_info.msg; + body = msg->body; + if (!body) { + _debug("UserAgent: Warning! Received NOTIFY without message body\n"); + return; + } + + /* Check for appropriate content */ + if (pj_stricmp2(&body->content_type.type, "message") != 0 || + pj_stricmp2(&body->content_type.subtype, "sipfrag") != 0) + { + _debug("UserAgent: Warning! Received NOTIFY with non message/sipfrag content\n"); + return; + } + + /* Try to parse the content */ + status = pjsip_parse_status_line((char*)body->data, body->len, + &status_line); + if (status != PJ_SUCCESS) { + _debug("UserAgent: Warning! Received NOTIFY with invalid message/sipfrag content\n"); + return; + } + + } else { + _debug("UserAgent: Set code to 500!\n"); + status_line.code = 500; + status_line.reason = *pjsip_get_status_text(500); + } + + /* Notify application */ + is_last = (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED); + cont = !is_last; + + if(status_line.code/100 == 2) { + _debug("UserAgent: Try to stop rtp!\n"); + pjsip_tx_data *tdata; + + status = pjsip_inv_end_session(call->getInvSession(), PJSIP_SC_GONE, NULL, &tdata); + if(status != PJ_SUCCESS) { + _debug("UserAgent: Fail to create end session msg!\n"); + } else { + status = pjsip_inv_send_msg(call->getInvSession(), tdata); + if(status != PJ_SUCCESS) + _debug("UserAgent: Fail to send end session msg!\n"); + } + + link->transferStep2(); + cont = PJ_FALSE; + } + + if (!cont) { + pjsip_evsub_set_mod_data(sub, _mod_ua.id, NULL); + } + } + + } + + + void xfer_svr_cb(pjsip_evsub *sub, pjsip_event *event) + { + PJ_UNUSED_ARG(event); + + /* + * When subscription is terminated, clear the xfer_sub member of + * the inv_data. + */ + if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) { + SIPCall *call; + + call = (SIPCall*) pjsip_evsub_get_mod_data(sub, _mod_ua.id); + if (!call) + return; + + pjsip_evsub_set_mod_data(sub, _mod_ua.id, NULL); + call->setXferSub(NULL); + + _debug("UserAgent: Xfer server subscription terminated\n"); + } + } diff --git a/src/sipvoiplink.h b/src/sipvoiplink.h index 865703e2151eb4345c40e178b5fa2187aa06230a..03b12e6c38ba2d0dc8eae5dd73f3a9a9916f5720 100644 --- a/src/sipvoiplink.h +++ b/src/sipvoiplink.h @@ -2,7 +2,7 @@ * Copyright (C) 2004-2009 Savoir-Faire Linux inc. * * Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com> - * Author: Yan Morin <yan.morin@savoirfairelinux.com> + * Author: Yun Liu <yun.liu@savoirfairelinux.com> * * 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 @@ -23,284 +23,316 @@ #define SIPVOIPLINK_H #include "voiplink.h" -#include "useragent.h" + +////////////////////////////// +/* PJSIP imports */ +#include <pjsip.h> +#include <pjlib.h> +#include <pjsip_ua.h> +#include <pjlib-util.h> +#include <pjnath/stun_config.h> +/////////////////////////////// + class EventThread; class SIPCall; class AudioRtp; +#define RANDOM_LOCAL_PORT ((rand() % 27250) + 5250)*2 +#define RANDOM_SIP_PORT rand() % 64000 + 1024 + +// To set the verbosity. From 0 (min) to 6 (max) +#define PJ_LOG_LEVEL 1 + /** * @file sipvoiplink.h - * @brief Specific VoIPLink for SIP (SIP core for incoming and outgoing events) + * @brief Specific VoIPLink for SIP (SIP core for incoming and outgoing events). + * This class is based on the singleton design pattern. + * One SIPVoIPLink can handle multiple SIP accounts, but all the SIP accounts have all the same SIPVoIPLink */ class SIPVoIPLink : public VoIPLink { - public: - - /** - * Constructor - * @param accountID The account identifier - */ - SIPVoIPLink(const AccountID& accountID); - - /** - * Destructor - */ - ~SIPVoIPLink(); - - /* Copy Constructor */ - SIPVoIPLink(const SIPVoIPLink& rh); - - /* Assignment Operator */ - SIPVoIPLink& operator=( const SIPVoIPLink& rh); - - /** - * Try to initiate the pjsip engine/thread and set config - * @return bool True if OK - */ - bool init(void); - - /** - * Delete link-related stuuf like calls - */ - void terminate(void); - - /** - * Event listener. Each event send by the call manager is received and handled from here - */ - void getEvent(void); - - /** - * Build and send SIP registration request - * @return bool True on success - * false otherwise - */ - int sendRegister(void); - - /** - * Build and send SIP unregistration request - * @return bool True on success - * false otherwise - */ - int sendUnregister(void); - - /** - * Place a new call - * @param id The call identifier - * @param toUrl The Sip address of the recipient of the call - * @return Call* The current call - */ - Call* newOutgoingCall(const CallID& id, const std::string& toUrl); - - /** - * Answer the call - * @param id The call identifier - * @return bool True on success - */ - bool answer(const CallID& id); - - /** - * Hang up the call - * @param id The call identifier - * @return bool True on success - */ - bool hangup(const CallID& id); - - /** - * Cancel the call - * @param id The call identifier - * @return bool True on success - */ - bool cancel(const CallID& id); - - /** - * Put the call on hold - * @param id The call identifier - * @return bool True on success - */ - bool onhold(const CallID& id); - - /** - * Put the call off hold - * @param id The call identifier - * @return bool True on success - */ - bool offhold(const CallID& id); - - /** - * Transfer the call - * @param id The call identifier - * @param to The recipient of the transfer - * @return bool True on success - */ - bool transfer(const CallID& id, const std::string& to); - - /** Handle the incoming refer msg, not finished yet */ - bool transferStep2(); - - /** - * Refuse the call - * @param id The call identifier - * @return bool True on success - */ - bool refuse (const CallID& id); - - /** - * Send DTMF - * @param id The call identifier - * @param code The char code - * @return bool True on success - */ - bool carryingDTMFdigits(const CallID& id, char code); - - /** - * If set to true, we check for a firewall - * @param use true if we use STUN - */ - void setUseStun(bool use) { _useStun = use; } - - /** - * The name of the STUN server - * @param server Server FQDN/IP - */ - void setStunServer(const std::string& server) { _stunServer = server; } - - bool isRegister() {return _bRegister;} - - void setRegister(bool result) {_bRegister = result;} - - public: - - /** - * Terminate every call not hangup | brutal | Protected by mutex - */ - void terminateSIPCall(); - - /** - * send SIP authentification - * @return bool true if sending succeed - */ - bool sendSIPAuthentification(); - - /** - * Get a SIP From header ("fullname" <sip:userpart@hostpart>) - * @param userpart User part - * @param hostpart Host name - * @return std::string SIP URI for from Header - */ - std::string SIPFromHeader(const std::string& userpart, const std::string& hostpart); - - /** - * Build a sip address with the number that you want to call - * Example: sip:124@domain.com - * @param to The header of the recipient - * @return std::string Result as a string - */ - std::string SIPToHeader(const std::string& to); - - /** - * Check if an url is sip-valid - * @param url The url to check - * @return bool True if osip tell that is valid - */ - bool SIPCheckUrl(const std::string& url); - - - /** - * Send an outgoing call invite - * @param call The current call - * @return bool True if all is correct - */ - bool SIPOutgoingInvite(SIPCall* call); - - /** - * Start a SIP Call - * @param call The current call - * @param subject Undocumented - * @return true if all is correct - */ - bool SIPStartCall(SIPCall* call, const std::string& subject); - - /** - * Get the Sip FROM url (add sip:, add @host, etc...) - * @return std::string The From url - */ - std::string getSipFrom(); - - /** - * Get the Sip TO url (add sip:, add @host, etc...) - * @param to_url The To url - * @return std::string The SIP to address - */ - std::string getSipTo(const std::string& to_url); - - /** - * Set audio (SDP) configuration for a call - * localport, localip, localexternalport - * @param call a SIPCall valid pointer - * @return bool True - */ - bool setCallAudioLocal(SIPCall* call); - - /** - * Tell the user that the call was answered - * @param - */ - void SIPCallAnswered(SIPCall *call, pjsip_rx_data *rdata); - - /** - * Handling 5XX/6XX error - * @param - */ - void SIPCallServerFailure(SIPCall *call); - - /** - * Peer close the connection - * @param - */ - void SIPCallClosed(SIPCall *call); - - /** - * The call pointer was released - * If the call was not cleared before, report an error - * @param - */ - void SIPCallReleased(SIPCall *call); - - /** - * SIPCall accessor - * @param id The call identifier - * @return SIPCall* A pointer on SIPCall object - */ - SIPCall* getSIPCall(const CallID& id); - - /** Tell if the initialisation was done */ - bool _initDone; - - /** when we init the listener, how many times we try to bind a port? */ - int _nbTryListenAddr; - - /** Do we use stun? */ - bool _useStun; - - /** What is the stun server? */ - std::string _stunServer; - - /** Local Extern Address is the IP address seen by peers for SIP listener */ - std::string _localExternAddress; - - /** Local Extern Port is the port seen by peers for SIP listener */ - unsigned int _localExternPort; - - /** Starting sound */ - AudioRtp* _audiortp; - - pj_str_t string2PJStr(const std::string &value); - -private: - pjsip_regc *_regc; - bool _bRegister; + public: + + /** + * Singleton method. Enable to retrieve the unique static instance + * @return SIPVoIPLink* A pointer on the object + */ + static SIPVoIPLink* instance( const AccountID& id ); + + /** + * Destructor + */ + ~SIPVoIPLink(); + + /* Copy Constructor */ + SIPVoIPLink(const SIPVoIPLink& rh); + + /* Assignment Operator */ + SIPVoIPLink& operator=( const SIPVoIPLink& rh); + + /** + * Try to initiate the pjsip engine/thread and set config + * @return bool True if OK + */ + bool init(void); + + /** + * Shut the library and clean up + */ + void terminate( void ); + + /** + * Event listener. Each event send by the call manager is received and handled from here + */ + void getEvent(void); + + /** + * Build and send SIP registration request + * @return bool True on success + * false otherwise + */ + int sendRegister(AccountID id); + + /** + * Build and send SIP unregistration request + * @return bool True on success + * false otherwise + */ + int sendUnregister(AccountID id); + + /** + * Place a new call + * @param id The call identifier + * @param toUrl The Sip address of the recipient of the call + * @return Call* The current call + */ + Call* newOutgoingCall(const CallID& id, const std::string& toUrl); + + /** + * Answer the call + * @param id The call identifier + * @return bool True on success + */ + bool answer(const CallID& id); + + /** + * Hang up the call + * @param id The call identifier + * @return bool True on success + */ + bool hangup(const CallID& id); + + /** + * Cancel the call + * @param id The call identifier + * @return bool True on success + */ + bool cancel(const CallID& id); + + /** + * Put the call on hold + * @param id The call identifier + * @return bool True on success + */ + bool onhold(const CallID& id); + + /** + * Put the call off hold + * @param id The call identifier + * @return bool True on success + */ + bool offhold(const CallID& id); + + /** + * Transfer the call + * @param id The call identifier + * @param to The recipient of the transfer + * @return bool True on success + */ + bool transfer(const CallID& id, const std::string& to); + + /** Handle the incoming refer msg, not finished yet */ + bool transferStep2(); + + /** + * Refuse the call + * @param id The call identifier + * @return bool True on success + */ + bool refuse (const CallID& id); + + /** + * Send DTMF + * @param id The call identifier + * @param code The char code + * @return bool True on success + */ + bool carryingDTMFdigits(const CallID& id, char code); + + /** + * If set to true, we check for a firewall + * @param use true if we use STUN + */ + inline void useStun(bool use) { _useStun=use; } + + inline bool useStun( void ) { return _useStun; } + + /** + * The name of the STUN server + * @param server Server FQDN/IP + */ + void setStunServer(const std::string& server); + + std::string getStunServer (void) { return _stunServer; } + + /** + * Terminate every call not hangup | brutal | Protected by mutex + */ + void terminateSIPCall(); + + /** + * Build a sip address with the number that you want to call + * Example: sip:124@domain.com + * @param to The header of the recipient + * @return std::string Result as a string + */ + std::string SIPToHeader(const std::string& to); + + /** + * Check if an url is sip-valid + * @param url The url to check + * @return bool True if osip tell that is valid + */ + bool SIPCheckUrl(const std::string& url); + + + /** + * Send an outgoing call invite + * @param call The current call + * @return bool True if all is correct + */ + bool SIPOutgoingInvite(SIPCall* call); + + /** + * Start a SIP Call + * @param call The current call + * @param subject Undocumented + * @return true if all is correct + */ + bool SIPStartCall(SIPCall* call, const std::string& subject); + + /** + * Get the Sip TO url (add sip:, add @host, etc...) + * @param to_url The To url + * @return std::string The SIP to address + */ + std::string getSipTo(const std::string& to_url, std::string hostname); + + /** + * Tell the user that the call was answered + * @param + */ + void SIPCallAnswered(SIPCall *call, pjsip_rx_data *rdata); + + /** + * Handling 5XX/6XX error + * @param + */ + void SIPCallServerFailure(SIPCall *call); + + /** + * Peer close the connection + * @param + */ + void SIPCallClosed(SIPCall *call); + + /** + * The call pointer was released + * If the call was not cleared before, report an error + * @param + */ + void SIPCallReleased(SIPCall *call); + + /** + * SIPCall accessor + * @param id The call identifier + * @return SIPCall* A pointer on SIPCall object + */ + SIPCall* getSIPCall(const CallID& id); + + /** when we init the listener, how many times we try to bind a port? */ + int _nbTryListenAddr; + + /** Starting sound */ + AudioRtp* _audiortp; + + /** Increment the number of SIP account connected to this link */ + void incrementClients (void) { _clients++; } + + /** Decrement the number of SIP account connected to this link */ + void decrementClients (void); + + private: + /** + * Constructor + * @param accountID The account identifier + */ + SIPVoIPLink(const AccountID& accountID); + + /* The singleton instance */ + static SIPVoIPLink* _instance; + + int getModId(); + + /** + * Initialize the PJSIP library + * Must be called before any other calls to the SIP layer + * + * @return bool True on success + */ + bool pjsip_init(); + + /** + * Delete link-related stuuf like calls + */ + bool pjsip_shutdown(void); + + /** Do we use stun? */ + bool _useStun; + + pj_status_t stunServerResolve(); + + /** Create SIP UDP Listener */ + int createUDPServer(); + + bool loadSIPLocalIP(); + + std::string getLocalIP() {return _localExternAddress;} + + /** For registration use only */ + int _regPort; + + /* Flag to check if the STUN server is valid or not */ + bool validStunServer; + + /** The current STUN server address */ + std::string _stunServer; + + /** Local Extern Address is the IP address seen by peers for SIP listener */ + std::string _localExternAddress; + + /** Local Extern Port is the port seen by peers for SIP listener */ + unsigned int _localExternPort; + + /** Threading object */ + EventThread* _evThread; + ost::Mutex _mutexSIP; + + /* Number of SIP accounts connected to the link */ + int _clients; + }; #endif diff --git a/src/user_cfg.h b/src/user_cfg.h index 57201f084be7353e84750920d6b4aa74909b64f6..438911822771e894d8c42decabc4a88c8c46be28 100644 --- a/src/user_cfg.h +++ b/src/user_cfg.h @@ -69,6 +69,8 @@ #define PULSE_LENGTH "DTMF.pulseLength" /** Length of the DTMF in millisecond */ #define SEND_DTMF_AS "DTMF.sendDTMFas" /** DTMF send mode */ #define SYMMETRIC "VoIPLink.symmetric" /** VoIP link type */ +#define STUN_ENABLE "STUN.enable" +#define STUN_SERVER "STUN.server" #define EMPTY_FIELD "" /** Default value for empty field */ #define DFT_STUN_SERVER "stun.fwdnet.net:3478" /** Default STUN server address */ @@ -89,6 +91,7 @@ #define DFT_EXPIRE_VALUE "180" /** Default expire value for registration */ #define DFT_AUDIO_MANAGER "1" /** Default audio manager */ #define DFT_SIP_PORT "5060" +#define DFT_STUN_ENABLE "0" #ifdef USE_ZEROCONF #define CONFIG_ZEROCONF_DEFAULT_STR "1" /** Default Zero configuration networking module value */ diff --git a/src/useragent.cpp b/src/useragent.cpp deleted file mode 100644 index fb527784e0a41d0754228debb329860589370d0c..0000000000000000000000000000000000000000 --- a/src/useragent.cpp +++ /dev/null @@ -1,1704 +0,0 @@ -/* - * Copyright (C) 2004-2005 Savoir-Faire Linux inc. - * Author: Yun Liu <yun.liu@savoirfairelinux.com> - * - * 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 3 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 <string> -#include <iostream> - -#include "manager.h" -#include "sipcall.h" -#include "useragent.h" -#include "sipvoiplink.h" -#include "sipaccount.h" - -#define RANDOM_SIP_PORT rand() % 64000 + 1024 -#define RANDOM_LOCAL_PORT ((rand() % 27250) + 5250)*2 - -UserAgent *UserAgent::_current; - -UserAgent::UserAgent():_endpt(NULL) ,_sock(NULL), _cp(), _pool(NULL), _mutex(NULL), _mod(), _useStun(false), _stunHost(), - _stunServer(""), _localExternAddress(""), _localIPAddress("127.0.0.1"), _localExternPort(0), _localPort(0), _regPort(DEFAULT_SIP_PORT), _thread(NULL) { - //_useStun = false; - //_localIPAddress = "127.0.0.1"; - UserAgent::_current = this; -} - -UserAgent::~UserAgent() { - _debug("UserAgent: In dtor!\n"); - sipDestory(); -} - -pj_status_t UserAgent::sipCreate() { - - pj_status_t status; - - // Init PJLIB: must be called before any call to the pjsip library - status = pj_init(); - // Use pjsip macros for sanity check - PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); - - // Init PJLIB-UTIL library - status = pjlib_util_init(); - PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); - - // Set the pjsip log level - pj_log_set_level( PJ_LOG_LEVEL ); - - // Init PJNATH - status = pjnath_init(); - PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); - - // Create a pool factory to allocate memory - pj_caching_pool_init(&_cp, &pj_pool_factory_default_policy, 0); - - // Create memory pool for application. - _pool = pj_pool_create(&_cp.factory, "sflphone", 4000, 4000, NULL); - - if (!_pool) { - _debug("UserAgent: Could not initialize memory pool\n"); - return PJ_ENOMEM; - } - - // Create a recursive mutex. Simple wrapper for pj_mutex_create - status = pj_mutex_create_recursive(_pool, "sflphone", &_mutex); - PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); - - // Create the SIP endpoint - status = pjsip_endpt_create(&_cp.factory, pj_gethostname()->ptr, &_endpt); - PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); - - return PJ_SUCCESS; -} - -pj_status_t UserAgent::sipInit() { - - pj_status_t status; - int errPjsip = 0; - int port; - - validStunServer = true; - /* Init SIP UA: */ - - //FIXME! DNS initialize here! */ - - /* Start resolving STUN server */ - // if we useStun and we failed to receive something on port 5060, we try a random port - // If use STUN server, firewall address setup - if (!loadSIPLocalIP()) { - _debug("UserAgent: Unable to determine network capabilities\n"); - return false; - } - errPjsip = 0; - port = _regPort; - - //_debug("stun host is %s\n", _stunHost.ptr); - if (_useStun && !Manager::instance().behindNat(_stunServer, port)) { - port = RANDOM_SIP_PORT; - if (!Manager::instance().behindNat(_stunServer, port)) { - _debug("UserAgent: Unable to check NAT setting\n"); - validStunServer = false; - return false; // hoho we can't use the random sip port too... - } - } - - _localPort = port; - if (_useStun) { - // set by last behindNat() call (ish)... - stunServerResolve(); - _localExternAddress = Manager::instance().getFirewallAddress(); - _localExternPort = Manager::instance().getFirewallPort(); - errPjsip = createUDPServer(); - if (errPjsip != 0) { - _debug("UserAgent: Could not initialize SIP listener on port %d\n", port); - return errPjsip; - } - } else { - _localExternAddress = _localIPAddress; - _localExternPort = _localPort; - errPjsip = createUDPServer(); - if (errPjsip != 0) { - _debug("UserAgent: Could not initialize SIP listener on port %d\n", _localExternPort); - _localExternPort = _localPort = RANDOM_SIP_PORT; - _debug("UserAgent: Try to initialize SIP listener on port %d\n", _localExternPort); - errPjsip = createUDPServer(); - if (errPjsip != 0) { - _debug("UserAgent: Fail to initialize SIP listener on port %d\n", _localExternPort); - return errPjsip; - } - } - } - - _debug("UserAgent: SIP Init -- listening on port %d\n", _localExternPort); - - // Initialize transaction layer - status = pjsip_tsx_layer_init_module(_endpt); - PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); - - // Initialize UA layer module - status = pjsip_ua_init_module(_endpt, NULL); - PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); - - // Initialize Replaces support. See the Replaces specification in RFC 3891 - status = pjsip_replaces_init_module(_endpt); - PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); - - // Initialize 100rel support - status = pjsip_100rel_init_module(_endpt); - PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); - - // Initialize and register sflphone module - { - const pjsip_module mod_initializer ={ - NULL, NULL, // prev, next. - { (char*)"mod-sflphone", 9}, // Name. - -1, // Id - PJSIP_MOD_PRIORITY_APPLICATION, // Priority - NULL, // load() - NULL, // start() - NULL, // stop() - NULL, // unload() - &mod_on_rx_request, // on_rx_request() - &mod_on_rx_response, // on_rx_response() - NULL, // on_tx_request. - NULL, // on_tx_response() - NULL, // on_tsx_state() - }; - - _mod = mod_initializer; - - status = pjsip_endpt_register_module(_endpt, &_mod); - PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); - - } - - // Init the event subscription module. - // It extends PJSIP by supporting SUBSCRIBE and NOTIFY methods - status = pjsip_evsub_init_module(_endpt); - PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); - - // Init presence module. - // TODO We probably do not need that extension - status = pjsip_pres_init_module(_endpt, pjsip_evsub_instance()); - PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); - - // Init PUBLISH module - // Provide an implementation of SIP Extension for Event State Publication (RFC 3903) - // TODO Check if it is necessary - status = pjsip_publishc_init_module(_endpt); - PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); - - // Init xfer/REFER module - status = pjsip_xfer_init_module(_endpt); - PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); - - // Initialize invite session module - // These callbacks will be called on incoming requests, media session state, etc. - { - pjsip_inv_callback inv_cb; - - // Init the callback for INVITE session: - pj_bzero(&inv_cb, sizeof (inv_cb)); - - inv_cb.on_state_changed = &call_on_state_changed; - inv_cb.on_new_session = &call_on_forked; - inv_cb.on_media_update = &call_on_media_update; - inv_cb.on_tsx_state_changed = &call_on_tsx_changed; - - _debug("UserAgent: VOIP callbacks initialized\n"); - - // Initialize session invite module - status = pjsip_inv_usage_init(_endpt, &inv_cb); - PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); - } - - - // Add endpoint capabilities (INFO, OPTIONS, etc) for this UA - { - pj_str_t allowed[] = { - {(char*)"INFO", 4}, - {(char*)"REGISTER", 8} - }; // //{"INVITE", 6}, {"ACK",3}, {"BYE",3}, {"CANCEL",6}, {"OPTIONS", 7}, - pj_str_t accepted = {(char*)"application/sdp", 15}; - - // Register supported methods - pjsip_endpt_add_capability(_endpt, &_mod, PJSIP_H_ALLOW, NULL, PJ_ARRAY_SIZE(allowed), allowed); - - // Register "application/sdp" in ACCEPT header - pjsip_endpt_add_capability(_endpt, &_mod, PJSIP_H_ACCEPT, NULL, 1, &accepted); - } - - _debug("UserAgent: pjsip version %s for %s initialized\n", pj_get_version(), PJ_OS_NAME); - - Manager::instance().setSipThreadStatus(false); - - // Create the secondary thread to poll sip events - status = pj_thread_create(_pool, "sflphone", &start_thread, NULL, PJ_THREAD_DEFAULT_STACK_SIZE, 0, - &_thread); - PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); - - /* Done! */ - return PJ_SUCCESS; - -} - -void UserAgent::sipDestory() { - /* Signal threads to quit: */ - Manager::instance().setSipThreadStatus(true); - - /* Wait worker thread to quit: */ - if (_thread) { - pj_thread_join(_thread); - pj_thread_destroy(_thread); - _thread = NULL; - } - - if (_endpt) { - /* Terminate all presence subscriptions. */ - //pjsua_pres_shutdown(); - - /* Wait for some time to allow unregistration to complete: */ - _debug("UserAgent: Shutting down...\n"); - busy_sleep(1000); - } - - /* Destroy endpoint. */ - if (_endpt) { - pjsip_endpt_destroy(_endpt); - _endpt = NULL; - } - - /* Destroy mutex */ - if (_mutex) { - pj_mutex_destroy(_mutex); - _mutex = NULL; - } - - /* Destroy pool and pool factory. */ - if (_pool) { - pj_pool_release(_pool); - _pool = NULL; - pj_caching_pool_destroy(&_cp); - - /* Shutdown PJLIB */ - pj_shutdown(); - } - - /* Done. */ -} - -void UserAgent::busy_sleep(unsigned msec) -{ -#if defined(PJ_SYMBIAN) && PJ_SYMBIAN != 0 - /* Ideally we shouldn't call pj_thread_sleep() and rather - * CActiveScheduler::WaitForAnyRequest() here, but that will - * drag in Symbian header and it doesn't look pretty. - */ - pj_thread_sleep(msec); -#else - pj_time_val timeout, now, tv; - - pj_gettimeofday(&timeout); - timeout.msec += msec; - pj_time_val_normalize(&timeout); - - tv.sec = 0; - tv.msec = 10; - pj_time_val_normalize(&tv); - - do { - pjsip_endpt_handle_events(_endpt, &tv); - pj_gettimeofday(&now); - } while (PJ_TIME_VAL_LT(now, timeout)); -#endif -} - -bool UserAgent::addAccount(AccountID id, pjsip_regc **regc2, const std::string& server, const std::string& user, const std::string& passwd, const int& timeout UNUSED) { - - pj_status_t status; - AccountID *currentId = new AccountID(id); - char contactTmp[256]; - pjsip_regc *regc; - pj_str_t svr; - pj_str_t aor; - pj_str_t contact; - pjsip_tx_data *tdata; - - //pj_mutex_lock(_mutex); - std::string tmp; - - - SIPAccount *account; - - if (!validStunServer) { - - SIPVoIPLink *voipLink; - voipLink = dynamic_cast<SIPVoIPLink *>(Manager::instance().getAccountLink(id)); - Manager::instance().getAccountLink(id)->setRegistrationState(VoIPLink::ErrorExistStun); - voipLink->setRegister(false); - return false; - } - - status = pjsip_regc_create(_endpt, (void *) currentId, ®c_cb, ®c); - if (status != PJ_SUCCESS) { - _debug("UserAgent: Unable to create regc.\n"); - return false; - } - - tmp = "sip:" + server; - pj_strdup2(_pool, &svr, tmp.data()); - - tmp = "<sip:" + user + "@" + server + ">"; - pj_strdup2(_pool, &aor, tmp.data()); - - - sprintf(contactTmp, "<sip:%s@%s:%d>", user.data(), _localExternAddress.data(), _localExternPort); - pj_strdup2(_pool, &contact, contactTmp); - - //_debug("UserAgent: Get in %s %d %s\n", svr.ptr, svr.slen, aor.ptr); - status = pjsip_regc_init(regc, &svr, &aor, &aor, 1, &contact, 600); //timeout); - if (status != PJ_SUCCESS) { - _debug("UserAgent: Unable to initialize regc. %d\n", status); //, regc->str_srv_url.ptr); - //pj_mutex_unlock(_mutex); - return false; - } - - - account = dynamic_cast<SIPAccount *> (Manager::instance().getAccount(id)); - pjsip_cred_info *cred = account->getCredInfo(); - - if(!cred) - cred = new pjsip_cred_info(); - - pj_bzero(cred, sizeof (pjsip_cred_info)); - pj_strdup2(_pool, &cred->username, user.data()); - cred->data_type = PJSIP_CRED_DATA_PLAIN_PASSWD; - pj_strdup2(_pool, &cred->data, passwd.data()); - pj_strdup2(_pool, &cred->realm, "*"); - pj_strdup2(_pool, &cred->scheme, "digest"); - pjsip_regc_set_credentials(regc, 1, cred); - - account->setCredInfo(cred); - - status = pjsip_regc_register(regc, PJ_TRUE, &tdata); - if (status != PJ_SUCCESS) { - _debug("UserAgent: Unable to register regc.\n"); - //pj_mutex_unlock(_mutex); - return false; - } - - status = pjsip_regc_send(regc, tdata); - if (status != PJ_SUCCESS) { - _debug("UserAgent: Unable to send regc request.\n"); - //pj_mutex_unlock(_mutex); - return false; - } - - account->setUserName(user); - account->setServer(server); - account->setContact(contactTmp); - - // associate regc with account - *regc2 = regc; - - //pj_mutex_unlock(_mutex); - - return true; -} - -bool UserAgent::removeAccount(pjsip_regc *regc) -{ - pj_status_t status = 0; - pjsip_tx_data *tdata = NULL; - - //pj_mutex_lock(_mutex); - if(regc) { - status = pjsip_regc_unregister(regc, &tdata); - if(status != PJ_SUCCESS) { - _debug("UserAgent: Unable to unregister regc.\n"); - //pj_mutex_unlock(_mutex); - return false; - } - - status = pjsip_regc_send( regc, tdata ); - if(status != PJ_SUCCESS) { - _debug("UserAgent: Unable to send regc request.\n"); - //pj_mutex_unlock(_mutex); - return false; - } - } else { - _debug("UserAgent: regc is null!\n"); - //pj_mutex_unlock(_mutex); - return false; - } - - //pj_mutex_unlock(_mutex); - return true; -} - -pj_str_t UserAgent::buildContact(char *userName) { - //pj_str_t contact; - char tmp[256]; - - //FIXME: IPV6 issue!! - _debug("In build Contact %s %s %d\n", userName, _localExternAddress.data(), _localExternPort); - sprintf(tmp, "<sip:%s@%s:%d>", userName, _localExternAddress.data(), _localExternPort); - //_debug("get tmp\n"); - return pj_str(tmp); -} - -pj_status_t UserAgent::stunServerResolve() { - pj_str_t stun_adr; - pj_hostent he; - pj_stun_config stunCfg; - pj_status_t stun_status; - pj_sockaddr stun_srv; - - // Initialize STUN configuration - pj_stun_config_init(&stunCfg, &_cp.factory, 0, pjsip_endpt_get_ioqueue(_endpt), pjsip_endpt_get_timer_heap(_endpt)); - - stun_status = PJ_EPENDING; - - // Init STUN socket - size_t pos = _stunServer.find(':'); - if(pos == std::string::npos) { - pj_strdup2(_pool, &stun_adr, _stunServer.data()); - stun_status = pj_sockaddr_in_init(&stun_srv.ipv4, &stun_adr, (pj_uint16_t) 3478); - } else { - std::string serverName = _stunServer.substr(0, pos); - std::string serverPort = _stunServer.substr(pos + 1); - int nPort = atoi(serverPort.data()); - pj_strdup2(_pool, &stun_adr, serverName.data()); - stun_status = pj_sockaddr_in_init(&stun_srv.ipv4, &stun_adr, (pj_uint16_t) nPort); - } - - if (stun_status != PJ_SUCCESS) { - _debug("UserAgent: Unresolved stun server!\n"); - stun_status = pj_gethostbyname(&stun_adr, &he); - - if (stun_status == PJ_SUCCESS) { - pj_sockaddr_in_init(&stun_srv.ipv4, NULL, 0); - stun_srv.ipv4.sin_addr = *(pj_in_addr*) he.h_addr; - stun_srv.ipv4.sin_port = pj_htons((pj_uint16_t) 3478); - } - } - - return stun_status; -} - -int UserAgent::createUDPServer() { - pj_status_t status; - //pj_str_t ipAddr; - pj_sockaddr_in bound_addr; - pjsip_host_port a_name; - char tmpIP[32]; - - // Init bound address to ANY - pj_memset(&bound_addr, 0, sizeof (bound_addr)); - bound_addr.sin_addr.s_addr = PJ_INADDR_ANY; - - // Create UDP server socket - status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &_sock); - if (status != PJ_SUCCESS) { - _debug("UserAgent: (%d) UDP socket() error\n", status); - return status; - } - - status = pj_sock_bind_in(_sock, pj_ntohl(bound_addr.sin_addr.s_addr), (pj_uint16_t) _localPort); - if (status != PJ_SUCCESS) { - _debug("UserAgent: (%d) UDP bind() error\n", status); - pj_sock_close(_sock); - return status; - } - - _debug("UserAgent: Use IP: %s\n", _localExternAddress.data()); - - // Create UDP-Server (default port: 5060) - strcpy(tmpIP, _localExternAddress.data()); - pj_strdup2(_pool, &a_name.host, tmpIP); - a_name.port = (pj_uint16_t) _localExternPort; - - status = pjsip_udp_transport_attach(_endpt, _sock, &a_name, 1, NULL); - if (status != PJ_SUCCESS) { - _debug("UserAgent: (%d) Unable to start UDP transport!\n", status); - return -1; - } else { - _debug("UserAgent: UDP server listening on port %d\n", _localExternPort); - } - - return 0; -} - -void UserAgent::setStunServer(const char *server) { - if(server != NULL) { - _useStun = true; - _stunServer = std::string(server); - } else { - _useStun = false; - _stunServer = std::string(""); - } -} - -void UserAgent::regc_cb(struct pjsip_regc_cbparam *param) { - - AccountID *id = static_cast<AccountID *> (param->token); - SIPVoIPLink *voipLink; - - _debug("UserAgent: Account ID is %s, Register result: %d, Status: %d\n", id->data(), param->status, param->code); - voipLink = dynamic_cast<SIPVoIPLink *>(Manager::instance().getAccountLink(*id)); - if(!voipLink) - return; - - if (param->status == PJ_SUCCESS) { - if (param->code < 0 || param->code >= 300) { - /* Sometimes, the status is OK, but we still failed. - * So checking the code for real result - */ - _debug("UserAgent: The error is: %d\n", param->code); - switch(param->code) { - case 408: - case 606: - Manager::instance().getAccountLink(*id)->setRegistrationState(VoIPLink::ErrorConfStun); - break; - case 503: - Manager::instance().getAccountLink(*id)->setRegistrationState(VoIPLink::ErrorHost); - break; - case 401: - case 403: - case 404: - Manager::instance().getAccountLink(*id)->setRegistrationState(VoIPLink::ErrorAuth); - break; - default: - Manager::instance().getAccountLink(*id)->setRegistrationState(VoIPLink::Error); - break; - } - voipLink->setRegister(false); - } else { - // Registration/Unregistration is success - - if(voipLink->isRegister()) - Manager::instance().getAccountLink(*id)->setRegistrationState(VoIPLink::Registered); - else { - Manager::instance().getAccountLink(*id)->setRegistrationState(VoIPLink::Unregistered); - voipLink->setRegister(false); - } - } - } else { - Manager::instance().getAccountLink(*id)->setRegistrationState(VoIPLink::ErrorAuth); - voipLink->setRegister(false); - } -} - -bool -UserAgent::loadSIPLocalIP() { - bool returnValue = true; - if (_localIPAddress == "127.0.0.1") { - pj_sockaddr ip_addr; - if (pj_gethostip(pj_AF_INET(), &ip_addr) != PJ_SUCCESS) { - // Update the registration state if no network capabilities found - _debug("UserAgent: Get host ip failed!\n"); - returnValue = false; - } else { - _localIPAddress = std::string(pj_inet_ntoa(ip_addr.ipv4.sin_addr)); - _debug("UserAgent: Checking network, setting local IP address to: %s\n", _localIPAddress.data()); - } - } - return returnValue; -} - -/* Thread entry point function. */ -int UserAgent::start_thread(void *arg) { - - PJ_UNUSED_ARG(arg); - - // FIXME! maybe we should add a flag for exiting! - // TODO Add the flag. We have to stop the thread when destroying the instance - while (!Manager::instance().getSipThreadStatus()) { - pj_time_val timeout = {0, 10}; - pjsip_endpt_handle_events(getInstance()->getEndPoint(), &timeout); - } - - return 0; -} - -void UserAgent::set_voicemail_info( AccountID account, pjsip_msg_body *body ){ - - int voicemail, pos_begin, pos_end; - std::string voice_str = "Voice-Message: "; - std::string delimiter = "/"; - std::string msg_body, voicemail_str; - - _debug("UserAgent: checking the voice message!\n"); - // The voicemail message is formated like that: - // Voice-Message: 1/0 . 1 is the number we want to retrieve in this case - - // We get the notification body - msg_body = (char*)body->data; - - // We need the position of the first character of the string voice_str - pos_begin = msg_body.find(voice_str); - // We need the position of the delimiter - pos_end = msg_body.find(delimiter); - - // So our voicemail number between the both index - try { - - voicemail_str = msg_body.substr(pos_begin + voice_str.length(), pos_end - ( pos_begin + voice_str.length())); - std::cout << "voicemail number : " << voicemail_str << std::endl; - voicemail = atoi( voicemail_str.c_str() ); - } - catch( std::out_of_range& e ){ - std::cerr << e.what() << std::endl; - } - - // We need now to notify the manager - if( voicemail != 0 ) - Manager::instance().startVoiceMessageNotification(account, voicemail); -} - - -pj_bool_t UserAgent::mod_on_rx_request(pjsip_rx_data *rdata) { - - pj_status_t status; - pj_str_t reason; - unsigned options = 0; - pjsip_dialog* dialog; - pjsip_tx_data *tdata; - //pjmedia_sdp_session *r_sdp; - AccountID account_id; - - // voicemail part - std::string method_name; - std::string request; - - // Handle the incoming call invite in this function - _debug("UserAgent: Callback on_rx_request is involved!\n"); - - /* First, let's got the username and server name from the invite. - * We will use them to detect which account is the callee. - */ - pjsip_uri *uri = rdata->msg_info.to->uri; - pjsip_sip_uri *sip_uri = (pjsip_sip_uri *) pjsip_uri_get_uri(uri); - - std::string userName = std::string(sip_uri->user.ptr, sip_uri->user.slen); - std::string server = std::string(sip_uri->host.ptr, sip_uri->host.slen); - - // Get the account id of callee from username and server - account_id = Manager::instance().getAccountIdFromNameAndServer(userName, server); - if(account_id == AccountNULL) { - _debug("UserAgent: Username %s doesn't match any account!\n",userName.c_str()); - return PJ_FALSE; - } - _debug("UserAgent: The receiver is : %s@%s\n", userName.data(), server.data()); - _debug("UserAgent: The callee account id is %s\n", account_id.c_str()); - - /* Now, it is the time to find the information of the caller */ - uri = rdata->msg_info.from->uri; - sip_uri = (pjsip_sip_uri *) pjsip_uri_get_uri(uri); - - std::string caller = std::string(sip_uri->user.ptr, sip_uri->user.slen); - std::string callerServer = std::string(sip_uri->host.ptr, sip_uri->host.slen); - std::string peerNumber = caller + "@" + callerServer; - - - // Get the server voicemail notification - // Catch the NOTIFY message - if( rdata->msg_info.msg->line.req.method.id == PJSIP_OTHER_METHOD ) - { - method_name = "NOTIFY"; - // Retrieve all the message. Should contains only the method name but ... - request = rdata->msg_info.msg->line.req.method.name.ptr; - // Check if the message is a notification - if( request.find( method_name ) != (size_t)-1 ) { - set_voicemail_info( account_id, rdata->msg_info.msg->body ); - } - pjsip_endpt_respond_stateless(getInstance()->getEndPoint(), rdata, PJSIP_SC_OK, NULL, NULL, NULL); - return PJ_SUCCESS; - } - - // Respond statelessly any non-INVITE requests with 500 - if (rdata->msg_info.msg->line.req.method.id != PJSIP_INVITE_METHOD) { - if (rdata->msg_info.msg->line.req.method.id != PJSIP_ACK_METHOD) { - pj_strdup2(getInstance()->getAppPool(), &reason, "user agent unable to handle this request "); - pjsip_endpt_respond_stateless(getInstance()->getEndPoint(), rdata, PJSIP_SC_METHOD_NOT_ALLOWED, &reason, NULL, - NULL); - return PJ_TRUE; - } - } - - // Verify that we can handle the request - status = pjsip_inv_verify_request(rdata, &options, NULL, NULL, getInstance()->getEndPoint(), NULL); - if (status != PJ_SUCCESS) { - pj_strdup2(getInstance()->getAppPool(), &reason, "user agent unable to handle this INVITE "); - pjsip_endpt_respond_stateless(getInstance()->getEndPoint(), rdata, PJSIP_SC_METHOD_NOT_ALLOWED, &reason, NULL, - NULL); - return PJ_TRUE; - } - - // Generate a new call ID for the incoming call! - CallID id = Manager::instance().getNewCallID(); - - _debug("UserAgent: The call id of the incoming call is %s\n", id.c_str()); - SIPCall* call = new SIPCall(id, Call::Incoming); - if (!call) { - _debug("UserAgent: unable to create an incoming call"); - return PJ_FALSE; - } - - // Set the codec map, IP, peer number and so on... for the SIPCall object - getInstance()->setCallAudioLocal(call); - call->setCodecMap(Manager::instance().getCodecDescriptorMap()); - call->setConnectionState(Call::Progressing); - call->setIp(getInstance()->getLocalIP()); - call->setPeerNumber(peerNumber); - - /* Call the SIPCallInvite function to generate the local sdp, - * remote sdp and negociator. - * This function is also used to set the parameters of audio RTP, including: - * local IP and port number - * remote IP and port number - * possilbe audio codec will be used in this call - */ - if (call->SIPCallInvite(rdata, getInstance()->getAppPool())) { - - // Notify UI there is an incoming call - if (Manager::instance().incomingCall(call, account_id)) { - // Add this call to the callAccountMap in ManagerImpl - Manager::instance().getAccountLink(account_id)->addCall(call); - _debug("UserAgent: Notify UI success!\n"); - } else { - // Fail to notify UI - delete call; - call = NULL; - _debug("UserAgent: Fail to notify UI!\n"); - return PJ_FALSE; - } - } else { - // Fail to collect call information - delete call; - call = NULL; - _debug("UserAgent: Call SIPCallInvite failed!\n"); - return PJ_FALSE; - } - - /* Create the local dialog (UAS) */ - status = pjsip_dlg_create_uas(pjsip_ua_instance(), rdata, NULL, &dialog); - if (status != PJ_SUCCESS) { - pjsip_endpt_respond_stateless(getInstance()->getEndPoint(), rdata, PJSIP_SC_INTERNAL_SERVER_ERROR, &reason, NULL, - NULL); - return PJ_TRUE; - } - - // Specify media capability during invite session creation - pjsip_inv_session *inv; - status = pjsip_inv_create_uas(dialog, rdata, call->getLocalSDPSession(), 0, &inv); - PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); - - // Associate the call in the invite session - inv->mod_data[getInstance()->getModId()] = call; - - // Send a 180/Ringing response - status = pjsip_inv_initial_answer(inv, rdata, PJSIP_SC_RINGING, NULL, NULL, &tdata); - PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); - status = pjsip_inv_send_msg(inv, tdata); - PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); - - // Associate invite session to the current call - call->setInvSession(inv); - - // Update the connection state - call->setConnectionState(Call::Ringing); - - /* Done */ - return PJ_SUCCESS; -} - -bool UserAgent::setCallAudioLocal(SIPCall* call) { - // Firstly, we use the local IP and port number - unsigned int callLocalAudioPort = RANDOM_LOCAL_PORT; - unsigned int callLocalExternAudioPort = callLocalAudioPort; - - if (_useStun) { - // If use Stun server, modify them - if (Manager::instance().behindNat(_stunServer, callLocalAudioPort)) { - callLocalExternAudioPort = Manager::instance().getFirewallPort(); - } - } - _debug("UserAgent: Setting local audio port to: %d\n", callLocalAudioPort); - _debug("UserAgent: Setting local audio port (external) to: %d\n", callLocalExternAudioPort); - - // Set local audio port for SIPCall(id) - call->setLocalIp(_localIPAddress); - call->setLocalAudioPort(callLocalAudioPort); - call->setLocalExternAudioPort(callLocalExternAudioPort); - - return true; -} - -int UserAgent::answer(SIPCall *call) { - pj_status_t status; - pjsip_tx_data *tdata; - - // User answered the incoming call, tell peer this news - if (call->startNegociation(_pool)) { - // Create and send a 200(OK) response - _debug("UserAgent: Negociation success!\n"); - status = pjsip_inv_answer(call->getInvSession(), PJSIP_SC_OK, NULL, NULL, &tdata); - PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); - status = pjsip_inv_send_msg(call->getInvSession(), tdata); - PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); - - return 0; - } - - return 1; -} - -bool UserAgent::makeOutgoingCall(const std::string& strTo, SIPCall* call, const AccountID& id) { - pj_status_t status; - pjsip_dialog *dialog; - pjsip_tx_data *tdata; - pj_str_t from, to, contact; - - _debug("*******************AccountId is %s\n", id.data()); - // Get the basic information about the callee account - SIPAccount* account = dynamic_cast<SIPAccount *>(Manager::instance().getAccount(id)); - - // Generate the from URI - std::string strFrom = "sip:" + account->getUserName() + "@" + account->getServer(); - - _debug("UserAgent: Make a new call from:%s to %s. Contact is %s\n", - strFrom.data(), strTo.data(), account->getContact().data()); - - // pjsip need the from and to information in pj_str_t format - pj_strdup2(_pool, &from, strFrom.data()); - pj_strdup2(_pool, &to, strTo.data()); - pj_strdup2(_pool, &contact, account->getContact().data()); - - // create the dialog (UAC) - status = pjsip_dlg_create_uac(pjsip_ua_instance(), &from, - &contact, - &to, - NULL, - &dialog); - PJ_ASSERT_RETURN(status == PJ_SUCCESS, false); - - setCallAudioLocal(call); - call->setIp(getInstance()->getLocalIP()); - - // Building the local SDP offer - call->createInitialOffer(_pool); - - // Create the invite session for this call - pjsip_inv_session *inv; - status = pjsip_inv_create_uac(dialog, call->getLocalSDPSession(), 0, &inv); - PJ_ASSERT_RETURN(status == PJ_SUCCESS, false); - - // Set auth information - pjsip_auth_clt_set_credentials(&dialog->auth_sess, 1, account->getCredInfo()); - - // Associate current call in the invite session - inv->mod_data[_mod.id] = call; - - status = pjsip_inv_invite(inv, &tdata); - PJ_ASSERT_RETURN(status == PJ_SUCCESS, false); - - // Associate current invite session in the call - call->setInvSession(inv); - - status = pjsip_inv_send_msg(inv, tdata); - //PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); - if(status != PJ_SUCCESS) { - return false; - } - - return true; -} - -void UserAgent::call_on_forked(pjsip_inv_session *inv, pjsip_event *e) { - PJ_UNUSED_ARG(inv); - PJ_UNUSED_ARG(e); -} - -void UserAgent::call_on_tsx_changed(pjsip_inv_session *inv, pjsip_transaction *tsx, pjsip_event *e) { - - pjsip_rx_data *rdata; - AccountID accId; - SIPCall *call; - SIPVoIPLink *link; - pjsip_msg *msg; - - _debug("UserAgent: TSX Changed! The tsx->state is %d; tsx->role is %d; code is %d; method id is %.*s.\n", - tsx->state, tsx->role, tsx->status_code, (int)tsx->method.name.slen, tsx->method.name.ptr); - - if(pj_strcmp2(&tsx->method.name, "INFO") == 0) { - // Receive a INFO message, ingore it! - return; - } - - //Retrieve the body message - rdata = e->body.tsx_state.src.rdata; - - if (tsx->role == PJSIP_ROLE_UAC) { - switch (tsx->state) { - case PJSIP_TSX_STATE_TERMINATED: - if (tsx->status_code == 200 && - pjsip_method_cmp(&tsx->method, pjsip_get_refer_method()) != 0) { - // Peer answered the outgoing call - _debug("UserAgent: Peer answered the outgoing call!\n"); - call = reinterpret_cast<SIPCall *> (inv->mod_data[getInstance()->getModId()]); - if (call == NULL) - return; - - //_debug("UserAgent: The call id is %s\n", call->getCallId().data()); - - accId = Manager::instance().getAccountFromCall(call->getCallId()); - link = dynamic_cast<SIPVoIPLink *> (Manager::instance().getAccountLink(accId)); - if (link) - link->SIPCallAnswered(call, rdata); - } else if (tsx->status_code / 100 == 5) { - _debug("UserAgent: 5xx error message received\n"); - } - break; - case PJSIP_TSX_STATE_PROCEEDING: - // Peer is ringing for the outgoing call - msg = rdata->msg_info.msg; - - call = reinterpret_cast<SIPCall *> (inv->mod_data[getInstance()->getModId()]); - if (call == NULL) - return; - - if (msg->line.status.code == 180) { - _debug("UserAgent: Peer is ringing!\n"); - - call->setConnectionState(Call::Ringing); - Manager::instance().peerRingingCall(call->getCallId()); - } - break; - case PJSIP_TSX_STATE_COMPLETED: - if (tsx->status_code == 407 || tsx->status_code == 401) //FIXME - break; - if (tsx->status_code / 100 == 6 || tsx->status_code / 100 == 4) { - // We get error message of outgoing call from server - _debug("UserAgent: Server error message is received!\n"); - call = reinterpret_cast<SIPCall *> (inv->mod_data[getInstance()->getModId()]); - if (call == NULL) { - _debug("UserAgent: Call has been removed!\n"); - return; - } - accId = Manager::instance().getAccountFromCall(call->getCallId()); - link = dynamic_cast<SIPVoIPLink *> (Manager::instance().getAccountLink(accId)); - if (link) { - link->SIPCallServerFailure(call); - } - } - break; - default: - break; - } // end of switch - - } else { - switch (tsx->state) { - case PJSIP_TSX_STATE_TRYING: - if (pjsip_method_cmp(&tsx->method, pjsip_get_refer_method()) == 0) { - // Peer ask me to transfer call to another number. - _debug("UserAgent: Incoming REFER request!\n"); - getInstance()->onCallTransfered(inv, e->body.tsx_state.src.rdata); - } - break; - case PJSIP_TSX_STATE_COMPLETED: - if (tsx->status_code == 200 && tsx->method.id == PJSIP_BYE_METHOD) { - // Peer hangup the call - _debug("UserAgent: Peer hangup(bye) message is received!\n"); - call = reinterpret_cast<SIPCall *> (inv->mod_data[getInstance()->getModId()]); - if (call == NULL) { - _debug("UserAgent: Call has been removed!\n"); - return; - } - accId = Manager::instance().getAccountFromCall(call->getCallId()); - link = dynamic_cast<SIPVoIPLink *> (Manager::instance().getAccountLink(accId)); - if (link) { - link->SIPCallClosed(call); - } - } else if (tsx->status_code == 200 && tsx->method.id == PJSIP_CANCEL_METHOD) { - // Peer refuse the call - _debug("UserAgent: Cancel message is received!\n"); - call = reinterpret_cast<SIPCall *> (inv->mod_data[getInstance()->getModId()]); - if (call == NULL) { - _debug("UserAgent: Call has been removed!\n"); - return; - } - - accId = Manager::instance().getAccountFromCall(call->getCallId()); - link = dynamic_cast<SIPVoIPLink *> (Manager::instance().getAccountLink(accId)); - if (link) { - link->SIPCallClosed(call); - } - } - break; - default: - break; - } // end of switch - } - -} - -void UserAgent::call_on_state_changed(pjsip_inv_session *inv, pjsip_event *e) { - - PJ_UNUSED_ARG(inv); - - SIPCall *call = reinterpret_cast<SIPCall*> (inv->mod_data[getInstance()->getModId()]); - if(!call) - return; - - /* If this is an outgoing INVITE that was created because of - * REFER/transfer, send NOTIFY to transferer. - */ - if (call->getXferSub() && e->type==PJSIP_EVENT_TSX_STATE) { - int st_code = -1; - pjsip_evsub_state ev_state = PJSIP_EVSUB_STATE_ACTIVE; - - - switch (call->getInvSession()->state) { - case PJSIP_INV_STATE_NULL: - case PJSIP_INV_STATE_CALLING: - /* Do nothing */ - break; - - case PJSIP_INV_STATE_EARLY: - case PJSIP_INV_STATE_CONNECTING: - st_code = e->body.tsx_state.tsx->status_code; - ev_state = PJSIP_EVSUB_STATE_ACTIVE; - break; - - case PJSIP_INV_STATE_CONFIRMED: - /* When state is confirmed, send the final 200/OK and terminate - * subscription. - */ - st_code = e->body.tsx_state.tsx->status_code; - ev_state = PJSIP_EVSUB_STATE_TERMINATED; - break; - - case PJSIP_INV_STATE_DISCONNECTED: - st_code = e->body.tsx_state.tsx->status_code; - ev_state = PJSIP_EVSUB_STATE_TERMINATED; - break; - - case PJSIP_INV_STATE_INCOMING: - /* Nothing to do. Just to keep gcc from complaining about - * unused enums. - */ - break; - } - - if (st_code != -1) { - pjsip_tx_data *tdata; - pj_status_t status; - - status = pjsip_xfer_notify( call->getXferSub(), - ev_state, st_code, - NULL, &tdata); - if (status != PJ_SUCCESS) { - _debug("UserAgent: Unable to create NOTIFY -- %d\n", status); - } else { - status = pjsip_xfer_send_request(call->getXferSub(), tdata); - if (status != PJ_SUCCESS) { - _debug("UserAgent: Unable to send NOTIFY -- %d\n", status); - } - } - } - } - -} - -bool UserAgent::onhold(SIPCall *call) { - - pj_status_t status; - pjsip_tx_data *tdata; - pjmedia_sdp_attr *attr; - pjmedia_sdp_session* local_sdp; - - local_sdp = call->getLocalSDPSession(); - if( local_sdp == NULL ){ - _debug("! SIP Failure: unable to find local_sdp\n"); - return false; - } - - /* Create re-INVITE with new offer */ - // Remove all the attributes with the specified name - pjmedia_sdp_media_remove_all_attr(local_sdp->media[0], "sendrecv"); - attr = pjmedia_sdp_attr_create(_pool, "sendonly", NULL); - pjmedia_sdp_media_add_attr(local_sdp->media[0], attr); - - status = pjsip_inv_reinvite( call->getInvSession(), NULL, local_sdp, &tdata); - if( status != PJ_SUCCESS ) - { - _debug("On hold: creation of the Re-invite request failed\n"); - return false; - } - /* Send the request */ - status = pjsip_inv_send_msg( call->getInvSession(), tdata); - - return (status == PJ_SUCCESS); -} - -bool UserAgent::offhold(SIPCall *call) { - - pj_status_t status; - pjsip_tx_data *tdata; - pjmedia_sdp_attr *attr; - pjmedia_sdp_session* local_sdp; - - local_sdp = call->getLocalSDPSession(); - if( local_sdp == NULL ){ - _debug("! SIP Failure: unable to find local_sdp\n"); - return false; - } - - /* Create re-INVITE with new offer */ - // Remove all the attributes with the specified name - pjmedia_sdp_media_remove_all_attr(local_sdp->media[0], "sendonly"); - attr = pjmedia_sdp_attr_create(_pool, "sendrecv", NULL); - pjmedia_sdp_media_add_attr(local_sdp->media[0], attr); - - status = pjsip_inv_reinvite( call->getInvSession(), NULL, local_sdp , &tdata); - if( status != PJ_SUCCESS ) - { - _debug("Off hold: creation of the Re-invite request failed\n"); - return false; - } - - /* Send the request */ - status = pjsip_inv_send_msg( call->getInvSession(), tdata); - - return (status == PJ_SUCCESS); -} - -bool UserAgent::hangup(SIPCall* call) { - pj_status_t status; - pjsip_tx_data *tdata = NULL; - - // User hangup current call. Notify peer - status = pjsip_inv_end_session(call->getInvSession(), 404, NULL, &tdata); - if(status != PJ_SUCCESS) - return false; - - _debug("UserAgent: Before send msg!\n"); - - if(tdata == NULL) - return true; - - status = pjsip_inv_send_msg(call->getInvSession(), tdata); - if(status != PJ_SUCCESS) - return false; - - call->getInvSession()->mod_data[getInstance()->getModId()] = NULL; - return true; -} - -bool UserAgent::refuse(SIPCall* call) -{ - pj_status_t status; - pjsip_tx_data *tdata; - - // User refuse current call. Notify peer - status = pjsip_inv_end_session(call->getInvSession(), PJSIP_SC_DECLINE, NULL, &tdata); //603 - if(status != PJ_SUCCESS) - return false; - - status = pjsip_inv_send_msg(call->getInvSession(), tdata); - if(status != PJ_SUCCESS) - return false; - - call->getInvSession()->mod_data[getInstance()->getModId()] = NULL; - return true; -} - - -bool UserAgent::carryingDTMFdigits(SIPCall* call, char *msgBody) -{ - pj_status_t status; - pjsip_tx_data *tdata; - pj_str_t methodName, content; - pjsip_method method; - pjsip_media_type ctype; - - pj_strdup2(_pool, &methodName, "INFO"); - pjsip_method_init_np(&method, &methodName); - - /* Create request message. */ - status = pjsip_dlg_create_request( call->getInvSession()->dlg, &method, - -1, &tdata); - if (status != PJ_SUCCESS) { - _debug("UserAgent: Unable to create INFO request -- %d\n", status); - return false; - } - - /* Get MIME type */ - pj_strdup2(_pool, &ctype.type, "application"); - pj_strdup2(_pool, &ctype.subtype, "dtmf-relay"); - - /* Create "application/dtmf-relay" message body. */ - pj_strdup2(_pool, &content, msgBody); - tdata->msg->body = pjsip_msg_body_create( tdata->pool, &ctype.type, - &ctype.subtype, &content); - if (tdata->msg->body == NULL) { - _debug("UserAgent: Unable to create msg body!\n"); - pjsip_tx_data_dec_ref(tdata); - return false; - } - - /* Send the request. */ - status = pjsip_dlg_send_request( call->getInvSession()->dlg, tdata, - _mod.id, NULL); - if (status != PJ_SUCCESS) { - _debug("UserAgent: Unable to send MESSAGE request -- %d\n", status); - return false; - } - - return true; - -} - -bool UserAgent::transfer(SIPCall *call, const std::string& to) -{ - pjsip_evsub *sub; - pjsip_tx_data *tdata; - struct pjsip_evsub_user xfer_cb; - pj_status_t status; - pj_str_t dest; - - pj_strdup2(_pool, &dest, to.data()); - - /* Create xfer client subscription. */ - pj_bzero(&xfer_cb, sizeof(xfer_cb)); - xfer_cb.on_evsub_state = &xfer_func_cb; - - status = pjsip_xfer_create_uac(call->getInvSession()->dlg, &xfer_cb, &sub); - if (status != PJ_SUCCESS) { - _debug("UserAgent: Unable to create xfer -- %d\n", status); - return false; - } - - /* Associate this voiplink of call with the client subscription - * We can not just associate call with the client subscription - * because after this function, we can not find the cooresponding - * voiplink from the call any more. But the voiplink is useful! - */ - AccountID accId = Manager::instance().getAccountFromCall(call->getCallId()); - SIPVoIPLink *link = dynamic_cast<SIPVoIPLink *> (Manager::instance().getAccountLink(accId)); - pjsip_evsub_set_mod_data(sub, _mod.id, link); - - /* - * Create REFER request. - */ - status = pjsip_xfer_initiate(sub, &dest, &tdata); - if (status != PJ_SUCCESS) { - _debug("UserAgent: Unable to create REFER request -- %d\n", status); - return false; - } - - /* Send. */ - status = pjsip_xfer_send_request(sub, tdata); - if (status != PJ_SUCCESS) { - _debug("UserAgent: Unable to send REFER request -- %d\n", status); - return false; - } - - return true; -} - -void UserAgent::xfer_func_cb( pjsip_evsub *sub, pjsip_event *event) -{ - PJ_UNUSED_ARG(event); - - _debug("UserAgent: Transfer callback is involved!\n"); - /* - * When subscription is accepted (got 200/OK to REFER), check if - * subscription suppressed. - */ - if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACCEPTED) { - - pjsip_rx_data *rdata; - pjsip_generic_string_hdr *refer_sub; - const pj_str_t REFER_SUB = {(char*)"Refer-Sub", 9 }; - - SIPVoIPLink *link = reinterpret_cast<SIPVoIPLink *> (pjsip_evsub_get_mod_data(sub, - getInstance()->getModId())); - - /* Must be receipt of response message */ - pj_assert(event->type == PJSIP_EVENT_TSX_STATE && - event->body.tsx_state.type == PJSIP_EVENT_RX_MSG); - rdata = event->body.tsx_state.src.rdata; - - /* Find Refer-Sub header */ - refer_sub = (pjsip_generic_string_hdr*) - pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, - &REFER_SUB, NULL); - - /* Check if subscription is suppressed */ - if (refer_sub && pj_stricmp2(&refer_sub->hvalue, "false")==0) { - /* Since no subscription is desired, assume that call has been - * transfered successfully. - */ - if (link) { - // It's the time to stop the RTP - link->transferStep2(); - } - - /* Yes, subscription is suppressed. - * Terminate our subscription now. - */ - _debug("UserAgent: Xfer subscription suppressed, terminating event subcription...\n"); - pjsip_evsub_terminate(sub, PJ_TRUE); - - } else { - /* Notify application about call transfer progress. - * Initially notify with 100/Accepted status. - */ - _debug("UserAgent: Xfer subscription 100/Accepted received...\n"); - } - } - /* - * On incoming NOTIFY, notify application about call transfer progress. - */ - else if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACTIVE || - pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) - { - pjsip_msg *msg; - pjsip_msg_body *body; - pjsip_status_line status_line; - pj_bool_t is_last; - pj_bool_t cont; - pj_status_t status; - - SIPVoIPLink *link = reinterpret_cast<SIPVoIPLink *> (pjsip_evsub_get_mod_data(sub, - getInstance()->getModId())); - - /* When subscription is terminated, clear the xfer_sub member of - * the inv_data. - */ - if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) { - pjsip_evsub_set_mod_data(sub, getInstance()->getModId(), NULL); - _debug("UserAgent: Xfer client subscription terminated\n"); - - } - - if (!link || !event) { - /* Application is not interested with call progress status */ - _debug("UserAgent: Either link or event is empty!\n"); - return; - } - - // Get current call - SIPCall *call = dynamic_cast<SIPCall *>(link->getCall(Manager::instance().getCurrentCallId())); - if(!call) { - _debug("UserAgent: Call doesn't exit!\n"); - return; - } - - /* This better be a NOTIFY request */ - if (event->type == PJSIP_EVENT_TSX_STATE && - event->body.tsx_state.type == PJSIP_EVENT_RX_MSG) - { - pjsip_rx_data *rdata; - - rdata = event->body.tsx_state.src.rdata; - - /* Check if there's body */ - msg = rdata->msg_info.msg; - body = msg->body; - if (!body) { - _debug("UserAgent: Warning! Received NOTIFY without message body\n"); - return; - } - - /* Check for appropriate content */ - if (pj_stricmp2(&body->content_type.type, "message") != 0 || - pj_stricmp2(&body->content_type.subtype, "sipfrag") != 0) - { - _debug("UserAgent: Warning! Received NOTIFY with non message/sipfrag content\n"); - return; - } - - /* Try to parse the content */ - status = pjsip_parse_status_line((char*)body->data, body->len, - &status_line); - if (status != PJ_SUCCESS) { - _debug("UserAgent: Warning! Received NOTIFY with invalid message/sipfrag content\n"); - return; - } - - } else { - _debug("UserAgent: Set code to 500!\n"); - status_line.code = 500; - status_line.reason = *pjsip_get_status_text(500); - } - - /* Notify application */ - is_last = (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED); - cont = !is_last; - - if(status_line.code/100 == 2) { - _debug("UserAgent: Try to stop rtp!\n"); - pjsip_tx_data *tdata; - - status = pjsip_inv_end_session(call->getInvSession(), PJSIP_SC_GONE, NULL, &tdata); - if(status != PJ_SUCCESS) { - _debug("UserAgent: Fail to create end session msg!\n"); - } else { - status = pjsip_inv_send_msg(call->getInvSession(), tdata); - if(status != PJ_SUCCESS) - _debug("UserAgent: Fail to send end session msg!\n"); - } - - link->transferStep2(); - cont = PJ_FALSE; - } - - if (!cont) { - pjsip_evsub_set_mod_data(sub, getInstance()->getModId(), NULL); - } - } - -} - -void UserAgent::onCallTransfered(pjsip_inv_session *inv, pjsip_rx_data *rdata) -{ - pj_status_t status; - pjsip_tx_data *tdata; - SIPCall *existing_call; - const pj_str_t str_refer_to = { (char*)"Refer-To", 8}; - const pj_str_t str_refer_sub = { (char*)"Refer-Sub", 9 }; - const pj_str_t str_ref_by = { (char*)"Referred-By", 11 }; - pjsip_generic_string_hdr *refer_to; - pjsip_generic_string_hdr *refer_sub; - pjsip_hdr *ref_by_hdr; - pj_bool_t no_refer_sub = PJ_FALSE; - char *uri; - std::string tmp; - pjsip_status_code code; - pjsip_evsub *sub; - - existing_call = (SIPCall *) inv->mod_data[getInstance()->getModId()]; - - /* Find the Refer-To header */ - refer_to = (pjsip_generic_string_hdr*) - pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL); - - if (refer_to == NULL) { - /* Invalid Request. - * No Refer-To header! - */ - _debug("UserAgent: Received REFER without Refer-To header!\n"); - pjsip_dlg_respond( inv->dlg, rdata, 400, NULL, NULL, NULL); - return; - } - - /* Find optional Refer-Sub header */ - refer_sub = (pjsip_generic_string_hdr*) - pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_sub, NULL); - - if (refer_sub) { - if (!pj_strnicmp2(&refer_sub->hvalue, "true", 4)==0) - no_refer_sub = PJ_TRUE; - } - - /* Find optional Referred-By header (to be copied onto outgoing INVITE - * request. - */ - ref_by_hdr = (pjsip_hdr*) - pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_ref_by, - NULL); - - /* Notify callback */ - code = PJSIP_SC_ACCEPTED; - - _debug("UserAgent: Call to %.*s is being transfered to %.*s\n", - (int)inv->dlg->remote.info_str.slen, - inv->dlg->remote.info_str.ptr, - (int)refer_to->hvalue.slen, - refer_to->hvalue.ptr); - - if (no_refer_sub) { - /* - * Always answer with 2xx. - */ - pjsip_tx_data *tdata; - const pj_str_t str_false = { (char*)"false", 5}; - pjsip_hdr *hdr; - - status = pjsip_dlg_create_response(inv->dlg, rdata, code, NULL, - &tdata); - if (status != PJ_SUCCESS) { - _debug("UserAgent: Unable to create 2xx response to REFER -- %d\n", status); - return; - } - - /* Add Refer-Sub header */ - hdr = (pjsip_hdr*) - pjsip_generic_string_hdr_create(tdata->pool, &str_refer_sub, - &str_false); - pjsip_msg_add_hdr(tdata->msg, hdr); - - - /* Send answer */ - status = pjsip_dlg_send_response(inv->dlg, pjsip_rdata_get_tsx(rdata), - tdata); - if (status != PJ_SUCCESS) { - _debug("UserAgent: Unable to create 2xx response to REFER -- %d\n", status); - return; - } - - /* Don't have subscription */ - sub = NULL; - - } else { - struct pjsip_evsub_user xfer_cb; - pjsip_hdr hdr_list; - - /* Init callback */ - pj_bzero(&xfer_cb, sizeof(xfer_cb)); - xfer_cb.on_evsub_state = &xfer_svr_cb; - - /* Init addiTHIS_FILE, THIS_FILE, tional header list to be sent with REFER response */ - pj_list_init(&hdr_list); - - /* Create transferee event subscription */ - status = pjsip_xfer_create_uas( inv->dlg, &xfer_cb, rdata, &sub); - if (status != PJ_SUCCESS) { - _debug("UserAgent: Unable to create xfer uas -- %d\n", status); - pjsip_dlg_respond( inv->dlg, rdata, 500, NULL, NULL, NULL); - return; - } - - /* If there's Refer-Sub header and the value is "true", send back - * Refer-Sub in the response with value "true" too. - */ - if (refer_sub) { - const pj_str_t str_true = { (char*)"true", 4 }; - pjsip_hdr *hdr; - - hdr = (pjsip_hdr*) - pjsip_generic_string_hdr_create(inv->dlg->pool, - &str_refer_sub, - &str_true); - pj_list_push_back(&hdr_list, hdr); - - } - - /* Accept the REFER request, send 2xx. */ - pjsip_xfer_accept(sub, rdata, code, &hdr_list); - - /* Create initial NOTIFY request */ - status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_ACTIVE, - 100, NULL, &tdata); - if (status != PJ_SUCCESS) { - _debug("UserAgent: Unable to create NOTIFY to REFER -- %d", status); - return; - } - - /* Send initial NOTIFY request */ - status = pjsip_xfer_send_request( sub, tdata); - if (status != PJ_SUCCESS) { - _debug("UserAgent: Unable to send NOTIFY to REFER -- %d\n", status); - return; - } - } - - /* We're cheating here. - * We need to get a null terminated string from a pj_str_t. - * So grab the pointer from the hvalue and NULL terminate it, knowing - * that the NULL position will be occupied by a newline. - */ - uri = refer_to->hvalue.ptr; - uri[refer_to->hvalue.slen] = '\0'; - - /* Now make the outgoing call. */ - tmp = std::string(uri); - - if(existing_call == NULL) { - _debug("UserAgent: Call doesn't exist!\n"); - return; - } - - AccountID accId = Manager::instance().getAccountFromCall(existing_call->getCallId()); - CallID newCallId = Manager::instance().getNewCallID(); - - if(!Manager::instance().outgoingCall(accId, newCallId, tmp)) { - - /* Notify xferer about the error (if we have subscription) */ - if (sub) { - status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED, - 500, NULL, &tdata); - if (status != PJ_SUCCESS) { - _debug("UserAgent: Unable to create NOTIFY to REFER -- %d\n", status); - return; - } - status = pjsip_xfer_send_request(sub, tdata); - if (status != PJ_SUCCESS) { - _debug("UserAgent: Unable to send NOTIFY to REFER -- %d\n", status); - return; - } - } - return; - } - - SIPCall* newCall; - SIPVoIPLink *link = dynamic_cast<SIPVoIPLink *> (Manager::instance().getAccountLink(accId)); - if(link) { - newCall = dynamic_cast<SIPCall *>(link->getCall(newCallId)); - if(!newCall) { - _debug("UserAgent: can not find the call from sipvoiplink!\n"); - return; - } - } - - if (sub) { - /* Put the server subscription in inv_data. - * Subsequent state changed in pjsua_inv_on_state_changed() will be - * reported back to the server subscription. - */ - newCall->setXferSub(sub); - - /* Put the invite_data in the subscription. */ - pjsip_evsub_set_mod_data(sub, _mod.id, - newCall); - } -} - -void UserAgent::xfer_svr_cb(pjsip_evsub *sub, pjsip_event *event) -{ - PJ_UNUSED_ARG(event); - - /* - * When subscription is terminated, clear the xfer_sub member of - * the inv_data. - */ - if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) { - SIPCall *call; - - call = (SIPCall*) pjsip_evsub_get_mod_data(sub, getInstance()->getModId()); - if (!call) - return; - - pjsip_evsub_set_mod_data(sub, getInstance()->getModId(), NULL); - call->setXferSub(NULL); - - _debug("UserAgent: Xfer server subscription terminated\n"); - } -} diff --git a/src/useragent.h b/src/useragent.h deleted file mode 100644 index b37257aeae33bd46bf9fae99a5a841fc655e013e..0000000000000000000000000000000000000000 --- a/src/useragent.h +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright (C) 2004-2005 Savoir-Faire Linux inc. - * Author: Yun Liu <yun.liu@savoirfairelinux.com> - * - * 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 3 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. - */ - - -#ifndef _SIPMANAGER_H -#define _SIPMANAGER_H - -#include <pjsip.h> -#include <pjlib-util.h> -#include <pjlib.h> -#include <pjnath/stun_config.h> - -//TODO Remove this include if we don't need anything from it -#include <pjsip_simple.h> - -#include <pjsip_ua.h> -#include <pjmedia/sdp.h> -#include <pjmedia/sdp_neg.h> - -#include <string> -#include <vector> - -#define PJ_LOG_LEVEL 1 - -typedef std::string AccountID; - -class SIPCall; - -class UserAgent -{ -private: - /** PJSIP Endpoint */ - pjsip_endpoint *_endpt; - pj_sock_t _sock; - //pjsip_module _appMod; - pj_caching_pool _cp; - pj_pool_t *_pool; - pj_mutex_t *_mutex; /** Mutex protection for this data */ - pjsip_module _mod; /** PJSIP module. */ - pjsip_module _options_handler; - bool _useStun; - pj_str_t _stunHost; - std::string _stunServer; - bool validStunServer; - - /** Local Extern Address is the IP address seen by peers for SIP listener */ - std::string _localExternAddress; - std::string _localIPAddress; - - /** Local Extern Port is the port seen by peers for SIP listener */ - unsigned int _localExternPort; - unsigned int _localPort; - - /** For registration use only */ - int _regPort; - - pj_thread_t *_thread; - - static UserAgent *_current; - - /* Sleep with polling */ - void busy_sleep(unsigned msec); -public: - UserAgent(); - ~UserAgent(); - - pj_status_t sipCreate(); - - /** - * This method is used to initialize the pjsip - */ - pj_status_t sipInit(); - - - void sipDestory(); - - /** Create SIP UDP Listener */ - int createUDPServer(); - - /** Set whether it will use stun server */ - void setStunServer(const char *server); - - /** Set the port number user designated */ - void setSipPort(int port) { _regPort = port; } - - int getSipPort() { return _regPort; } - - pj_str_t getStunServer() { return _stunHost; } - - bool addAccount(AccountID id, pjsip_regc **regc, const std::string& server, const std::string& user, const std::string& passwd - , const int& timeout); - bool removeAccount(pjsip_regc *regc); - - pj_str_t buildContact(char *userName); - - bool loadSIPLocalIP(); - - pj_status_t stunServerResolve(); - - pjsip_endpoint* getEndPoint() {return _endpt;} - - std::string getLocalIP() {return _localExternAddress;} - - int getModId() {return _mod.id;} - - bool setCallAudioLocal(SIPCall* call); - - int answer(SIPCall* call); - - bool hangup(SIPCall* call); - - bool refuse(SIPCall* call); - - bool onhold(SIPCall *call); - bool offhold(SIPCall *call); - - bool transfer(SIPCall *call, const std::string& to); - - void onCallTransfered(pjsip_inv_session *inv, pjsip_rx_data *rdata); - - bool makeOutgoingCall(const std::string& to, SIPCall* call, const AccountID& id); - - bool carryingDTMFdigits(SIPCall* call, char *msgBody); - - pj_pool_t *getAppPool() {return _pool;} - static pj_bool_t mod_on_rx_request(pjsip_rx_data *rdata); - static pj_bool_t mod_on_rx_response(pjsip_rx_data *rdata UNUSED) {return PJ_SUCCESS;} - static void regc_cb(struct pjsip_regc_cbparam *param); - static void xfer_func_cb( pjsip_evsub *sub, pjsip_event *event); - static void xfer_svr_cb(pjsip_evsub *sub, pjsip_event *event); - static void call_on_media_update( pjsip_inv_session *inv UNUSED, pj_status_t status UNUSED) {} - static void call_on_state_changed( pjsip_inv_session *inv, pjsip_event *e); - static void call_on_forked(pjsip_inv_session *inv, pjsip_event *e); - static void call_on_tsx_changed(pjsip_inv_session *inv, pjsip_transaction *tsx, pjsip_event *e); - static int start_thread(void *arg); - static UserAgent* getInstance() {return _current;} - - static void set_voicemail_info( AccountID account, pjsip_msg_body *body ); -private: - - // Copy Constructor - UserAgent(const UserAgent& rh); - - // Assignment Operator - UserAgent& operator=( const UserAgent& rh); -}; - - -#endif /* _SIPMANAGER_H */ - diff --git a/src/voiplink.cpp b/src/voiplink.cpp index fe62b20467a1047fa2a1965618de1bff4abeea79..77a0c95ed11f7cc05646e0baa21a311501310ab8 100644 --- a/src/voiplink.cpp +++ b/src/voiplink.cpp @@ -26,7 +26,6 @@ VoIPLink::VoIPLink(const AccountID& accountID) : _accountID(accountID), _localIPAddress("127.0.0.1"), _localPort(0), _initDone(false) { - setRegistrationState(VoIPLink::Unregistered); } VoIPLink::~VoIPLink (void) @@ -64,8 +63,7 @@ Call* VoIPLink::getCall(const CallID& id) return 0; } -bool -VoIPLink::clearCallMap() +bool VoIPLink::clearCallMap() { ost::MutexLock m(_callMapMutex); CallMap::iterator iter = _callMap.begin(); @@ -78,9 +76,11 @@ VoIPLink::clearCallMap() return true; } -void VoIPLink::setRegistrationState(const RegistrationState state) +Account* VoIPLink::getAccountPtr(void) { - _registrationState = state; - // Notify the client - Manager::instance().connectionStatusNotification( ); + Account* account; + AccountID id; + + id = getAccountID(); + return Manager::instance().getAccount(id); } diff --git a/src/voiplink.h b/src/voiplink.h index e095c1922ccc24af99754eddd5ed79b1d2ebc83a..4038394ccf6446663bba7e5d0d465d7cd955c2a8 100644 --- a/src/voiplink.h +++ b/src/voiplink.h @@ -24,8 +24,10 @@ #define __VOIP_LINK_H__ #include "call.h" +#include "account.h" class AudioCodec; +class Account; /** Define AccountID type */ typedef std::string AccountID; @@ -38,218 +40,189 @@ typedef std::map<CallID, Call*> CallMap; * @brief Listener and manager interface for each VoIP protocol */ class VoIPLink { - public: - - /** - * Constructor - * @param accountID The account identifier - */ - VoIPLink(const AccountID& accountID); - - /** - * Virtual destructor - */ - virtual ~VoIPLink (void); - - /** Contains all the state an Voip can be in */ - enum RegistrationState {Unregistered, Trying, Registered, Error, ErrorAuth , ErrorNetwork , ErrorHost, ErrorExistStun, ErrorConfStun}; - typedef enum RegistrationState RegistrationState; - - /** - * Virtual method - * Event listener. Each event send by the call manager is received and handled from here - */ - virtual void getEvent (void) = 0; - - /** - * Virtual method - * Try to initiate the pjsip engine/thread and set config - * @return bool True if OK - */ - virtual bool init (void) = 0; - - /** - * Virtual method - * Delete link-related stuuf like calls - */ - virtual void terminate (void) = 0; - - /** - * Virtual method - * Build and send SIP registration request - * @return bool True on success - * false otherwise - */ - virtual int sendRegister (void) = 0; - - /** - * Virtual method - * Build and send SIP unregistration request - * @return bool True on success - * false otherwise - */ - virtual int sendUnregister (void) = 0; - - /** - * Place a new call - * @param id The call identifier - * @param toUrl The Sip address of the recipient of the call - * @return Call* The current call - */ - virtual Call* newOutgoingCall(const CallID& id, const std::string& toUrl) = 0; - /** - * Answer the call - * @param id The call identifier - * @return bool True on success - */ - virtual bool answer(const CallID& id) = 0; - - /** - * Hang up a call - * @param id The call identifier - * @return bool True on success - */ - virtual bool hangup(const CallID& id) = 0; - - /** - * Cancel the call dialing - * @param id The call identifier - * @return bool True on success - */ - virtual bool cancel(const CallID& id) = 0; - - /** - * Put a call on hold - * @param id The call identifier - * @return bool True on success - */ - virtual bool onhold(const CallID& id) = 0; - - /** - * Resume a call from hold state - * @param id The call identifier - * @return bool True on success - */ - virtual bool offhold(const CallID& id) = 0; - - /** - * Transfer a call to specified URI - * @param id The call identifier - * @param to The recipient of the call - * @return bool True on success - */ - virtual bool transfer(const CallID& id, const std::string& to) = 0; - - /** - * Refuse incoming call - * @param id The call identifier - * @return bool True on success - */ - virtual bool refuse(const CallID& id) = 0; - - /** - * Send DTMF - * @param id The call identifier - * @param code The char code - * @return bool True on success - */ - virtual bool carryingDTMFdigits(const CallID& id, char code) = 0; - - /* Accessors */ - std::string& getFullName (void) { return _fullname; } - void setFullName (const std::string& fullname) { _fullname = fullname; } - - std::string& getHostname (void) { return _hostname; } - void setHostname (const std::string& hostname) { _hostname = hostname; } - - std::string& getUsername (void) { return _username; } - void setUsername (const std::string& username) { _username = username; } - - std::string& getPassword (void) { return _password; } - void setPassword (const std::string& password) { _password = password; } - - /** - * @return AccountID parent Account's ID - */ - AccountID& getAccountID(void) { return _accountID; } - - /** - * @param accountID The account identifier - */ - void setAccountID( const AccountID& accountID) { _accountID = accountID; } - - /** Get the call pointer from the call map (protected by mutex) - * @param id A Call ID - * @return Call* Call pointer or 0 - */ - Call* getCall(const CallID& id); - - /** - * Get connection status - * @return Connection status - */ - RegistrationState getRegistrationState() { return _registrationState; } - - /** - * Set new registration state - * @param state The registration state - */ - void setRegistrationState(const RegistrationState state); - - private: - std::string _hostname; - std::string _username; - std::string _password; - std::string _fullname; - - /** - * ID of parent's Account - */ - AccountID _accountID; - - /** - * State of registration - */ - RegistrationState _registrationState; - -public: - /** Add a call to the call map (protected by mutex) - * @param call A call pointer with a unique pointer - * @return bool True if the call was unique and added - */ - bool addCall(Call* call); - - /** Remove a call from the call map (protected by mutex) - * @param id A Call ID - * @return bool True if the call was correctly removed - */ - bool removeCall(const CallID& id); - - /** - * Remove all the call from the map - * @return bool True on success - */ - bool clearCallMap(); - - -protected: - /** Contains all the calls for this Link, protected by mutex */ - CallMap _callMap; - - /** Mutex to protect call map */ - ost::Mutex _callMapMutex; - - /** Get Local IP Address (ie: 127.0.0.1, 192.168.0.1, ...) */ - std::string _localIPAddress; - - /** Get local listening port (5060 for SIP, ...) */ - unsigned int _localPort; - - /** Whether init() was called already or not - * This should be used in [IAX|SIP]VoIPLink::init() and terminate(), to - * indicate that init() was called, or reset by terminate(). - */ - bool _initDone; + + public: + /** + * Constructor + * @param accountID The account identifier + */ + VoIPLink(const AccountID& accountID); + + /** + * Virtual destructor + */ + virtual ~VoIPLink (void); + + + /** + * Virtual method + * Event listener. Each event send by the call manager is received and handled from here + */ + virtual void getEvent (void) = 0; + + /** + * Virtual method + * Try to initiate the communication layer and set config + * @return bool True if OK + */ + virtual bool init (void) = 0; + + /** + * Virtual method + * Delete link-related stuff like calls + */ + virtual void terminate (void) = 0; + + /** + * Virtual method + * Build and send account registration request + * @return bool True on success + * false otherwise + */ + virtual int sendRegister ( AccountID id ) = 0; + + /** + * Virtual method + * Build and send account unregistration request + * @return bool True on success + * false otherwise + */ + virtual int sendUnregister ( AccountID id ) = 0; + + /** + * Place a new call + * @param id The call identifier + * @param toUrl The address of the recipient of the call + * @return Call* The current call + */ + virtual Call* newOutgoingCall(const CallID& id, const std::string& toUrl) = 0; + + /** + * Answer the call + * @param id The call identifier + * @return bool True on success + */ + virtual bool answer(const CallID& id) = 0; + + /** + * Hang up a call + * @param id The call identifier + * @return bool True on success + */ + virtual bool hangup(const CallID& id) = 0; + + /** + * Cancel the call dialing + * @param id The call identifier + * @return bool True on success + */ + virtual bool cancel(const CallID& id) = 0; + + /** + * Put a call on hold + * @param id The call identifier + * @return bool True on success + */ + virtual bool onhold(const CallID& id) = 0; + + /** + * Resume a call from hold state + * @param id The call identifier + * @return bool True on success + */ + virtual bool offhold(const CallID& id) = 0; + + /** + * Transfer a call to specified URI + * @param id The call identifier + * @param to The recipient of the call + * @return bool True on success + */ + virtual bool transfer(const CallID& id, const std::string& to) = 0; + + /** + * Refuse incoming call + * @param id The call identifier + * @return bool True on success + */ + virtual bool refuse(const CallID& id) = 0; + + /** + * Send DTMF + * @param id The call identifier + * @param code The char code + * @return bool True on success + */ + virtual bool carryingDTMFdigits(const CallID& id, char code) = 0; + + bool initDone (void) { return _initDone; } + void initDone (bool state) { _initDone = state; } + + std::string getLocalIPAddress (void) { return _localIPAddress; } + + /** Add a call to the call map (protected by mutex) + * @param call A call pointer with a unique pointer + * @return bool True if the call was unique and added + */ + bool addCall(Call* call); + + /** Remove a call from the call map (protected by mutex) + * @param id A Call ID + * @return bool True if the call was correctly removed + */ + bool removeCall(const CallID& id); + + /** + * Remove all the call from the map + * @return bool True on success + */ + bool clearCallMap(); + + /** + * @return AccountID parent Account's ID + */ + inline AccountID& getAccountID(void) { return _accountID; } + + Account* getAccountPtr(void); + + /** + * @param accountID The account identifier + */ + inline void setAccountID( const AccountID& accountID) { _accountID = accountID; } + + /** + * Get the call pointer from the call map (protected by mutex) + * @param id A Call ID + * @return Call* Call pointer or 0 + */ + Call* getCall(const CallID& id); + + virtual void setStunServer( const std::string &server ) = 0; + + private: + /** + * ID of parent's Account + */ + AccountID _accountID; + + protected: + /** Contains all the calls for this Link, protected by mutex */ + CallMap _callMap; + + /** Mutex to protect call map */ + ost::Mutex _callMapMutex; + + /** Get Local IP Address (ie: 127.0.0.1, 192.168.0.1, ...) */ + std::string _localIPAddress; + + /** Get local listening port (5060 for SIP, ...) */ + unsigned int _localPort; + + /** Whether init() was called already or not + * This should be used in [IAX|SIP]VoIPLink::init() and terminate(), to + * indicate that init() was called, or reset by terminate(). + */ + bool _initDone; }; #endif // __VOIP_LINK_H__ diff --git a/src/zeroconf/DNSQueryThread.cpp b/src/zeroconf/DNSQueryThread.cpp deleted file mode 100644 index a5e9dd3fd2208eb5466264ce7d5c078961a9eedc..0000000000000000000000000000000000000000 --- a/src/zeroconf/DNSQueryThread.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Copyright (C) 2005 Savoir-Faire Linux inc. - * Author: Yan Morin <yan.morin@savoirfairelinux.com> - * - * 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 3 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 "DNSQueryThread.h" -#include "DNSService.h" - -/** - * Construct a DNSQueryThread and initialize the cancel to deferred - */ -DNSQueryThread::DNSQueryThread(DNSService *parent, const char *regtype) : ost::Thread() -{ - _parent = parent; - _regtype = regtype; - _serviceRef = NULL; - setCancel(cancelDeferred); -} - -/** - * Destruct a DNSQueryThread - */ -DNSQueryThread::~DNSQueryThread() -{ - if (_serviceRef) { - DNSServiceRefDeallocate(_serviceRef); - } - terminate(); - _parent = NULL; - _regtype = NULL; - _serviceRef = NULL; -} - -/** - * Running loop - */ -void -DNSQueryThread::run() { - DNSServiceErrorType theErr=0; // NULL; - DNSServiceFlags resultFlags=0; - - theErr = DNSServiceBrowse(&_serviceRef, - resultFlags, - 0, // all interfaces - _regtype, - NULL, - DNSServiceAddServicesCallback, - (void*)_parent); - - if (theErr == kDNSServiceErr_NoError) { - while(!testCancel()) { - DNSServiceProcessResult(_serviceRef); // blockage if none... - } - } -} diff --git a/src/zeroconf/DNSQueryThread.h b/src/zeroconf/DNSQueryThread.h deleted file mode 100644 index 2ea0bc2cab61d49f1493cccee103e8e8fddd9d33..0000000000000000000000000000000000000000 --- a/src/zeroconf/DNSQueryThread.h +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright (C) 2005 Savoir-Faire Linux inc. - * Author: Yan Morin <yan.morin@savoirfairelinux.com> - * - * 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 3 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. - */ -#ifndef __DNSSD_DNSQUERYTHREAD_H__ -#define __DNSSD_DNSQUERYTHREAD_H__ - -#include <cc++/thread.h> -#include <dns_sd.h> - -class DNSService; -class DNSQueryThread : public ost::Thread -{ -public: - DNSQueryThread(DNSService *parent, const char *regtype); - ~DNSQueryThread(); - virtual void run(); // looking for services - -private: - DNSService *_parent; // parent service - DNSServiceRef _serviceRef; // service reference - const char *_regtype; // service type and socket type (_sip._udp by example) -}; - - -#endif // __DNSSD_DNSQUERYTHREAD_H__ diff --git a/src/zeroconf/DNSService.cpp b/src/zeroconf/DNSService.cpp deleted file mode 100644 index e2919fe27d93ec7c431ec7e30cb53d1469b462f2..0000000000000000000000000000000000000000 --- a/src/zeroconf/DNSService.cpp +++ /dev/null @@ -1,237 +0,0 @@ -/** - * Copyright (C) 2005 Savoir-Faire Linux inc. - * Author: Yan Morin <yan.morin@savoirfairelinux.com> - * - * 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 3 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. - */ - - /** - * Inspired by http://braden.machacking.net/zerobrowse.cpp and - * http://developer.kde.org/documentation/library/3.4-api/dnssd/html/remoteservice_8cpp-source.html - */ -#include "DNSService.h" -#include "DNSServiceTXTRecord.h" -#include "DNSQueryThread.h" - -#include <cc++/thread.h> - -/** - * Simple Empty Constructor - */ -DNSService::DNSService() -{ - _start = false; - _regtypeList.push_back("_sip._udp"); -#ifdef USE_IAX2 - _regtypeList.push_back("_iax._udp"); -#endif - - // for the thread, the ifdef add a dynamic _regtypeList problem - for (std::list<std::string>::iterator iterThread=_regtypeList.begin(); - iterThread!=_regtypeList.end(); - iterThread++) { - _queryThread.push_back(new DNSQueryThread(this, (*iterThread).c_str())); - } -} - -/** - * Simple Empty Destructor - */ -DNSService::~DNSService() -{ - int cntThread = _queryThread.size(); - for (int iThread=0;iThread<cntThread;iThread++) { - delete _queryThread[iThread]; _queryThread[iThread] = NULL; - } -} - -/** - * Look for zeroconf services and add them to _services - */ -void -DNSService::startScanServices() -{ - for (std::vector<DNSQueryThread *>::iterator iter = _queryThread.begin();iter!=_queryThread.end();iter++) { - (*iter)->start(); - } - _start = true; -} - -/** - * Add one service to the list of actual services - * @param service Service to add to the list - */ -void DNSService::addService(const std::string &service) -{ - DNSServiceTXTRecord txtRecord; - _mutex.enterMutex(); - _services[service] = txtRecord; - // we leave before the queryService since, each - // thread will modify a DNSServiceTXTRecord of a difference services - _mutex.leaveMutex(); - notify(); - queryService(service); -} - -/** - * Remove one service to the list of actual services - * @param service Service to remove to the list - */ -void DNSService::removeService(const std::string &service) -{ - _mutex.enterMutex(); - _services.erase(service); - _mutex.leaveMutex(); - notify(); -} - -/** - * Return every services - */ -DNSServiceMap -DNSService::getServices() -{ - ost::MutexLock m(_mutex); - return _services; -} - - -/** - * Query a service and wait for the anwser - * the queryCallback will show the result - * @param service The service full adress - */ -void -DNSService::queryService(const std::string &service) -{ - DNSServiceErrorType theErr=0; - DNSServiceRef myServRef=0; - DNSServiceFlags resultFlags=0; - - theErr = DNSServiceQueryRecord(&myServRef, - resultFlags, - 0, - service.c_str(), - kDNSServiceType_TXT, - kDNSServiceClass_IN, - DNSServiceQueryRecordCallback, - (void*)this); - if (theErr == kDNSServiceErr_NoError) { - DNSServiceProcessResult(myServRef); // blockage... - DNSServiceRefDeallocate(myServRef); - } -} - -/** - * Overloadding queryService - * @param service service name - * @param regtype registred type of service - * @param domain domain (habitually local.) - */ -void -DNSService::queryService(const char *service, const char *regtype, const char *domain) -{ - char serviceName[kDNSServiceMaxDomainName+1]; - DNSServiceConstructFullName(serviceName, service, regtype, domain); - queryService(std::string(serviceName)); -} - -/** - * Add a txt record with the queryService callback answser data - * @param rdlen the length of the txt record data - * @param rdata txt record data - */ -void -DNSService::addTXTRecord(const char *fullname, uint16_t rdlen, const void *rdata) -{ - char key[256]; - - const char *value; - uint8_t valueLen; // 0 to 256 by type restriction - char valueTab[256]; - - - uint16_t keyCount = TXTRecordGetCount(rdlen, rdata); - for (int iKey=0; iKey<keyCount; iKey++) { - TXTRecordGetItemAtIndex (rdlen, rdata, iKey, 256, key, &valueLen, (const void **)(&value)); - if (value) { - bcopy(value, valueTab, valueLen); - valueTab[valueLen]='\0'; - _mutex.enterMutex(); // extra-careful - _services[fullname].addKeyValue(key, valueTab); - _mutex.leaveMutex(); - } else { - _mutex.enterMutex(); - _services[fullname].removeKey(key); - _mutex.leaveMutex(); - } - } - - notify(); -} - -void -DNSServiceAddServicesCallback(DNSServiceRef, - DNSServiceFlags flags, - uint32_t, - DNSServiceErrorType errorCode, - const char *serviceName, - const char *replyType, - const char *replyDomain, - void *context) -{ - if (errorCode==kDNSServiceErr_NoError) { - - if (flags) { - DNSService *service = (DNSService*)context; - std::string tempService; - tempService = std::string(serviceName) + "." + std::string(replyType) + std::string(replyDomain); - if (flags&kDNSServiceFlagsAdd) { -// _debug("DNSServiceAddServicesCallback call addService\n"); - service->addService(tempService); - } else { -// _debug("DNSServiceAddServicesCallback call removeService\n"); - service->removeService(tempService); - } - } - } else { - // TODO: error handling - } -} - -void -DNSServiceQueryRecordCallback( - DNSServiceRef, - DNSServiceFlags flags, - uint32_t, - DNSServiceErrorType errorCode, - const char *fullname, - uint16_t, - uint16_t, - uint16_t rdlen, - const void *rdata, - uint32_t, - void *context) -{ - if (errorCode==kDNSServiceErr_NoError) { - if (flags&kDNSServiceFlagsAdd) { -// _debug("DNSServiceQueryRecordCallback call addTXTRecord\n"); - ((DNSService *)context)->addTXTRecord(fullname, rdlen, rdata); - } else { -// _debug("DNSServiceQueryRecordCallback call removeService\n"); - ((DNSService *)context)->removeService(fullname); - } - } -} diff --git a/src/zeroconf/DNSService.h b/src/zeroconf/DNSService.h deleted file mode 100644 index b1a840bf1cf6bf5b8076d9fb79c2785f84cb6ae4..0000000000000000000000000000000000000000 --- a/src/zeroconf/DNSService.h +++ /dev/null @@ -1,92 +0,0 @@ -/** - * Copyright (C) 2005 Savoir-Faire Linux inc. - * Author: Yan Morin <yan.morin@savoirfairelinux.com> - * - * 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 3 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. - */ -#ifndef __DNSSD_DNSSERVICE_H__ -#define __DNSSD_DNSSERVICE_H__ - -#include <string> -#include <map> -#include <list> -#include <vector> - -#include <dns_sd.h> -#include <cc++/thread.h> -#include "../observer.h" - -class DNSQueryThread; -class DNSServiceTXTRecord; - -typedef std::map<std::string, DNSServiceTXTRecord> DNSServiceMap; -class DNSService : public Pattern::Subject -{ -public: - DNSService(); - ~DNSService(); - - void startScanServices(); // looking for services - void addService(const std::string &service); // adding every services - void removeService(const std::string &service); // remove a service - DNSServiceMap getServices(); // get all DNS Service - void stop(); // after the browsing loop stop - - void queryService(const std::string &service); // query the TXT record of a service - void queryService(const char *service, const char *regtype, const char *domain); - void addTXTRecord(const char *fullname, uint16_t rdlen, const void *rdata); - //void removeTXTRecord(const char *fullname); - - bool isStart() const { return _start; } - -private: - DNSServiceMap _services; //map - - std::vector<DNSQueryThread *> _queryThread; - /** - * Mutex to protect access to _services on add/erase - */ - ost::Mutex _mutex; - /** - * RegType List contains zeroconf services to register, like sip, iax2, ... - * It will be use to initialize the DNSQueryThread - */ - std::list<std::string> _regtypeList; - - bool _start; -}; - -void DNSServiceAddServicesCallback(DNSServiceRef sdRef, - DNSServiceFlags flags, - uint32_t interfaceIndex, - DNSServiceErrorType errorCode, - const char *serviceName, - const char *replyType, - const char *replyDomain, - void *context); - -void DNSServiceQueryRecordCallback(DNSServiceRef DNSServiceRef, - DNSServiceFlags flags, - uint32_t interfaceIndex, - DNSServiceErrorType errorCode, - const char *fullname, - uint16_t rrtype, - uint16_t rrclass, - uint16_t rdlen, - const void *rdata, - uint32_t ttl, - void *context); - -#endif // __DNSSD_DNSSERVICE_H__ diff --git a/src/zeroconf/DNSServiceTXTRecord.cpp b/src/zeroconf/DNSServiceTXTRecord.cpp deleted file mode 100644 index f12ed15f5b2b1f9468ffa72f8b71d3567102f5a2..0000000000000000000000000000000000000000 --- a/src/zeroconf/DNSServiceTXTRecord.cpp +++ /dev/null @@ -1,76 +0,0 @@ -/** - * Copyright (C) 2005 Savoir-Faire Linux inc. - * Author: Yan Morin <yan.morin@savoirfairelinux.com> - * - * 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 3 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 "DNSServiceTXTRecord.h" - -/** - * Simple constructor - */ -DNSServiceTXTRecord::DNSServiceTXTRecord() -{ -} - -/** - * Simple destructor - */ -DNSServiceTXTRecord::~DNSServiceTXTRecord() -{ -} - -/** - * add a pair of key/value inside the associative std::map - * @param key unique key inside the std::map - * @param value value associated to the key - */ -void -DNSServiceTXTRecord::addKeyValue(const std::string &key, const std::string &value) -{ - _map[key] = value; -} - -/** - * remove a key inside the map or do nothing if it doesn't exist - * @param key unique key inside the std::map - */ -void -DNSServiceTXTRecord::removeKey(const std::string &key) -{ - _map.erase(key); -} - -/** - * get a value from a key - * @param key unique key inside the std::map - * @return the value or empty - */ -const std::string & -DNSServiceTXTRecord::getValue(const std::string &key) -{ - return _map[key]; // return std::string("") if it's not there -} - -/** - * get a value from a key - * @param key unique key inside the std::map - * @return the value or empty - */ -const std::string & -DNSServiceTXTRecord::getValue(const char* key) -{ - return getValue(std::string(key)); -} diff --git a/src/zeroconf/DNSServiceTXTRecord.h b/src/zeroconf/DNSServiceTXTRecord.h deleted file mode 100644 index d6d2742fd6efa5091dc04f63064f3eb28e555397..0000000000000000000000000000000000000000 --- a/src/zeroconf/DNSServiceTXTRecord.h +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Copyright (C) 2005 Savoir-Faire Linux inc. - * Author: Yan Morin <yan.morin@savoirfairelinux.com> - * - * 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 3 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. - */ -#ifndef __DNSSD_DNSSERVICETXTRECORD_H__ -#define __DNSSD_DNSSERVICETXTRECORD_H__ - -#include <string> -#include <map> - -typedef std::map<std::string, std::string> TXTRecordMap; -class DNSServiceTXTRecord -{ -public: - DNSServiceTXTRecord(); - ~DNSServiceTXTRecord(); - void addKeyValue(const std::string &key, const std::string &value); - void removeKey(const std::string &key); - const std::string &getValue(const std::string &key); - const std::string &getValue(const char *key); - inline void clear(void) { _map.clear(); }; - inline int size(void) { return _map.size(); }; - TXTRecordMap getTXTRecords() { return _map; } - -private: - TXTRecordMap _map; -}; - -#endif // __DNSSD_DNSSERVICETXTRECORD_H__ diff --git a/src/zeroconf/Makefile.am b/src/zeroconf/Makefile.am deleted file mode 100644 index d2ed5b4e4a56d0330b7bad354ea7f2afec291086..0000000000000000000000000000000000000000 --- a/src/zeroconf/Makefile.am +++ /dev/null @@ -1,12 +0,0 @@ -SUBDIRS = - -noinst_LTLIBRARIES = libzeroconf.la - -libzeroconf_la_SOURCES = \ - DNSQueryThread.cpp DNSQueryThread.h \ - DNSService.cpp DNSService.h \ - DNSServiceTXTRecord.cpp DNSServiceTXTRecord.h - -AM_CXXFLAGS = $(libccext2_CFLAGS) -libzeroconf_la_LIBADD = $(LIB_DNSSD) -