Viadeo Twitter Google Bookmarks ! Facebook Digg del.icio.us MySpace Yahoo MyWeb Blinklist Netvouz Reddit Simpy StumbleUpon Bookmarks Windows Live Favorites 
Logo Documentation Qt ·  Page d'accueil  ·  Toutes les classes  ·  Toutes les fonctions  ·  Vues d'ensemble  · 

VoIP Integration

VoIP Integration

This document describes how to integrate a third-party Voice over IP (VoIP) agent into Qt Extended. However large parts of this guide refer to general telephony interfaces and classes. This means that this document also describes the essential steps to integrate an arbitrary telephony service.

Third parties need to supply at least three components as follows:

  1. A VoIP agent daemon that provides the call management facilities to the Qt Extended telephony server. This is a Qt Extended application that links against the Qt Extended telephony libraries to communicate with the Qt Extended telephony server. It typically does not have any user interface and runs in the background as a daemon.
  2. A settings program that allows the user to configure the VoIP options for the VoIP agent daemon.
  3. A call policy manager task in the qpe server to provide user interface integration with the Qt Extended call screen.

To demonstrate the process, this document describes how to integrate the IAX2 protocol used by the Asterisk telephony server. We will use the open source iaxclient library to provide the IAX2 protocol implementation. The full source code for the Asterisk integration can be found in the Qt Extended source tree under examples/asterisk.

Note that Asterisk does not support presence.

In the examples below, the virtual keyword will be shown on methods that must be overridden from a parent class. If a method does not have the virtual keyword, then it is unique to the subclass.

Qt Extended Phone Library

The integration model for VoIP agents uses the Phone Library to provide the basic infrastructure. The phone library divides up all telephony functionality into services and interfaces.

Handlers in the telephony system are referred to as services. Each service has a unique name, such as modem, voip, etc. By convention, service names are lower case. Within each service is a list of interfaces for functionality areas such as network registration, phone calls, presence, SMS, etc.

Interface names correspond to class names in the Qt Extended Telephony API. For example, the QNetworkRegistration interface provides access to network registration features, and the QPresence interface provides methods to access user presence information.

A more thorough overview of the library can be found on the Phone Library page in the documentation.

Service names should be unique. The name modem is reserved for use by GSM modems, and the name voip is reserved for SIP-based VoIP services. The example in this document uses the name asterisk.

Basic Asterisk Telephony Service

To integrate Asterisk, we need to provide a telephony service called asterisk which inherits from the QTelephonyService class. The most basic definition is as follows (from iaxtelephonyservice.h):

    class IaxTelephonyService : public QTelephonyService
    {
        Q_OBJECT
    public:
        IaxTelephonyService( const QString& service, QObject *parent = 0 );
        ~IaxTelephonyService();

        virtual void initialize();

        ...
    };

The constructor and destructor initialize and shut down the iaxclient library (for the full details, see the source code in the examples/asterisk/iaxagent directory):

    IaxTelephonyService::IaxTelephonyService
            ( const QString& service, QObject *parent )
        : QTelephonyService( service, parent )
    {
        // Initialize the iaxclient library
        ...
    }

    IaxTelephonyService::~IaxTelephonyService()
    {
        // Shut down the iaxclient library
        ...
    }

All telephony services must override the initialize() method to create the telephony interfaces that it requires. For Asterisk, we need the QNetworkRegistration, QTelephonyConfiguration, QServiceChecker, and QPhoneCallProvider interfaces:

    void IaxTelephonyService::initialize()
    {
        if ( !supports<QNetworkRegistration>() )
            addInterface( new IaxNetworkRegistration( this ) );

        if ( !supports<QTelephonyConfiguration>() )
            addInterface( new IaxConfiguration( this ) );

        if ( !supports<QServiceChecker>() )
            addInterface( new IaxServiceChecker( this ) );

        if ( !callProvider() )
            setCallProvider( new IaxCallProvider( this ) );

        QTelephonyService::initialize();
    }

The telephony service must call the base QTelephonyService::initialize() function to complete the initialization process.

Starting up the Asterisk Agent

When VoIP telephony services are started, they receive a QCop message Telephony::start() on their application channel. When they are shut down, they receive a QCop message Telephony::stop() on their application channel. For more information on QCop service messages, see the Qt Extended Services documentation.

We can intercept these messages using the QtopiaAbstractService class:

    class IaxTelephonyServiceQCop : public QtopiaAbstractService
    {
        Q_OBJECT
    public:
        IaxTelephonyServiceQCop( QObject *parent = 0 );
        ~IaxTelephonyServiceQCop() {}

    public slots:
        void start();
        void stop();

    private:
        IaxTelephonyService *service;
    };

    IaxTelephonyServiceQCop::IaxTelephonyServiceQCop( QObject *parent )
        : QtopiaAbstractService( "Telephony", parent )
    {
        publishAll();
        service = 0;
    }

    void IaxTelephonyServiceQCop::start()
    {
        if ( !service ) {
            // Register a task to keep us alive while the service is running.
            QtopiaApplication::instance()->registerRunningTask
                ( "IaxTelephonyService", this );

            // Create the service handler, registered under the name "asterisk".
            qLog(VoIP) << "Starting iaxclient service handler";
            service = new IaxTelephonyService( "asterisk", this );
            service->initialize();
        }
    }

    void IaxTelephonyServiceQCop::stop()
    {
        if ( service ) {
            // Delete the service handler.
            qLog(VoIP) << "Stopping iaxclient service handler";
            delete service;
            service = 0;

            // Deregister the task which allows this daemon to shut down.
            QtopiaApplication::instance()->unregisterRunningTask
                ( "IaxTelephonyService" );
        }
    }

It is important that the agent register a running task using QtopiaApplication::registerRunningTask() so that Qt Extended will not shut down the agent prematurely. It is also important to create the telephony service properly, and to not forget the call to initialize():

    service = new IaxTelephonyService( "asterisk", this );
    service->initialize();

If you forget the call to initialize(), the service will not be registered with the system and phone calls will not work.

The final coding step is to start the QCop service from the agent's main function. See main.cpp in the examples/asterisk/iaxagent directory for the details.

We now need to register the iaxagent daemon as a Qt Extended telephony service by adding an iaxagent to the directory $QPEDIR/services/Telephony whose contents is as follows:

    [Extensions]
    [Standard]
    Version = 100

Now when Qt Extended starts up, it will detect the new telephony service and send it the Telephony::start() QCop message.

Service Checking

When a telephony service starts up, it may not be ready to register to the network immediately. Some time may be required to properly initialize the service, or the service may not be properly configured.

The Qt Extended server uses the QServiceChecker interface to determine if the service is ready to be used. If the telephony service does not provide an implementation of QServiceChecker, then Qt Extended will assume that the service is ready to use as soon as initialize() returns.

For our Asterisk service, we need to wait for the registration server to be configured in the Asterisk.conf file before it can be used. So we have to implement a new service checker as follows (the actual code is in iaxservicechecker.h and iaxservicechecker.cpp):

    class IaxServiceChecker : public QServiceChecker
    {
        Q_OBJECT
    public:
        explicit IaxServiceChecker( IaxTelephonyService *service );
        ~IaxServiceChecker() {}

    public slots:
        void updateRegistrationConfig();
    };

    IaxServiceChecker::IaxServiceChecker( IaxTelephonyService *service )
        : QServiceChecker( service->service(), service, Server )
    {
        updateRegistrationConfig();
    }

    void IaxServiceChecker::updateRegistrationConfig()
    {
        QSettings config( "Trolltech", "Asterisk" );
        config.beginGroup( "Registration" );
        if ( config.value( "Server" ).toString().isEmpty() )
            setValid( false );
        else
            setValid( true );
    }

Now, we can call updateRegistrationConfig() whenever the configuration file is updated and it will notify Qt Extended of whether the Asterisk service can be used or not.

Network Registration

Once a telephony service has been successfully initialized and checked, the next step is to register to the network. Qt Extended uses the QNetworkRegistration interface to control the network registration state of telephony services.

There are two general modes that can be used by a telephony service for network registration: automatic and manual. In automatic mode, the telephony service will attempt to register to the network as soon as possible after service initialization. In manual mode, the user must explicitly launch the appropriate settings program and request network registration.

For Asterisk, we override the QNetworkRegistration interface to create the IaxNetworkRegistration class (the actual code is in iaxnetworkregistration.h and iaxnetworkregistration.cpp):

    class IaxNetworkRegistration : public QNetworkRegistrationServer
    {
        Q_OBJECT
    public:
        explicit IaxNetworkRegistration( IaxTelephonyService *service );
        ~IaxNetworkRegistration();

    public slots:
        virtual void setCurrentOperator
            ( QTelephony::OperatorMode mode, const QString& id,
              const QString& technology );
        virtual void requestAvailableOperators();
        void registerToServer();
        void deregisterFromServer();

        ...

    private:
        bool pendingSetCurrentOperator;
    };

    IaxNetworkRegistration::IaxNetworkRegistration( IaxTelephonyService *service )
        : QNetworkRegistrationServer( service->service(), service )
    {
        pendingSetCurrentOperator = false;
    }

    IaxNetworkRegistration::~IaxNetworkRegistration()
    {
        deregisterFromServer();
    }

    void IaxNetworkRegistration::setCurrentOperator
            ( QTelephony::OperatorMode mode, const QString&id,
              const QString& technology)
    {
        if ( mode == QTelephony::OperatorModeDeregister ) {
            pendingSetCurrentOperator = true;
            deregisterFromServer();
        } else {
            pendingSetCurrentOperator = true;
            registerToServer();
        }
    }

    void IaxNetworkRegistration::requestAvailableOperators()
    {
        QList<QNetworkRegistration::AvailableOperator> list;
        emit availableOperators( list );
    }

    void IaxNetworkRegistration::registerToServer()
    {
        ...
    }

    void IaxNetworkRegistration::deregisterFromServer()
    {
        ...
    }

The actual code that performs the registration and deregistration is not shown here. See the source code for the full details. The important point to note is that once the registration has completed, or been lost, the Asterisk service should call QNetworkRegistrationServer::updateRegistrationState() to update the registration state to one of the following values:

QTelephony::RegistrationNoneRegistration is not currently available.
QTelephony::RegistrationHomeThe user is registered to their home network.
QTelephony::RegistrationSearchingThe service is attempting to register but has not done so yet.
QTelephony::RegistrationDeniedThe service tried to register but was denied, probably because the user's authentication credentials were invalid.
QTelephony::RegistrationUnknownThe registration state is unknown. This normally only makes sense for GSM networks. For VoIP implementations, use QTelephony::RegistrationNone instead.
QTelephony::RegistrationRoamingThe user is registered to a network and can place calls, but it is not their usual home network.

If the Asterisk service is set to the automatic registration mode, then these registration state changes will happen shortly after service initialization. If the service is set to the manual registration mode, then these registration state changes will only happen after the user launches the settings program and manually requests registration.

The other main component of network registration is the list of available network operators. This is used by settings programs to allow the user to choose an alternative network in their current location.

For Asterisk, there is only a single operator: the one configured by the user in the Asterisk.conf file. It therefore doesn't make sense to allow the user to choose another operator and we return an empty list from requestAvailableOperators().

For other VoIP implementations, especially those that can roam between public WiFi hotspots, it may make sense to return a non-empty list from requestAvailableOperators(), giving the user to ability to choose from a list of the networks that are in range.

Phone Calls

Once the service has been checked, and the network registered, the next step is to enable the placement of phone calls. Qt Extended achieves this in telephony services with the QPhoneCallProvider interface. Our Asterisk agent needs to inherit from QPhoneCallProvider and override the create() method (iaxcallprovider.h and iaxcallprovider.cpp):

    class IaxCallProvider : public QPhoneCallProvider
    {
        Q_OBJECT
    public:
        IaxCallProvider( IaxTelephonyService *service );
        ~IaxCallProvider() {}

        void stateEvent( struct iaxc_ev_call_state *e );

    protected:
        virtual QPhoneCallImpl *create
            ( const QString& identifier, const QString& callType );
    };

    IaxCallProvider::IaxCallProvider( IaxTelephonyService *service )
        : QPhoneCallProvider( service->service(), service )
    {
        setCallTypes( QStringList( "Asterisk" ) );
        ...
    }

    QPhoneCallImpl *IaxCallProvider::create
            ( const QString& identifier, const QString& callType )
    {
        return new IaxPhoneCall( this, identifier, callType, -1 );
    }

The call to QPhoneCallProvider::setCallTypes() in the constructor is very important. It publishes the call types that are understood by the telephony service into the value space. Qt Extended uses this information to select an appropriate service to place an outgoing call. If the setCallTypes() function is not called in the constructor, then it will not be possible to place phone calls using the service.

Call types are typically simple names such as Voice, VoIP, Data, Fax, Video, etc. See the documentation for QPhoneCallManager::create() for a description of how call types are used to select an appropriate telephony service.

The VoIP call type is reserved for use by SIP-based telephony services. We cannot use that for our IAX2 implementation, so we publish the service's call type as Asterisk instead.

By convention, service names start with a lower-case letter, and call type names start with an upper-case letter. This convention is not strictly enforced by Qt Extended, but it can help when diagnosing telephony problems to be able to distinguish a word used as a service name and the same word used as a call type.

This code creates an instance of IaxPhoneCall whenever the user attempts to place an outgoing phone call. It will then be followed by a call to IaxPhoneCall::dial(). The IaxPhoneCall class is declared as follows:

    class IaxPhoneCall : public QPhoneCallImpl
    {
        Q_OBJECT
    public:
        IaxPhoneCall( IaxCallProvider *provider, const QString& identifier,
                      const QString& callType, int callNo );
        virtual ~IaxPhoneCall();

        virtual void dial( const QDialOptions& options );
        virtual void hangup( QPhoneCall::Scope scope );
        virtual void accept();
        virtual void hold();
        virtual void activate( QPhoneCall::Scope scope );
        virtual void tone( const QString& tones );
        virtual void transfer( const QString& number );

        void stateEvent( struct iaxc_ev_call_state *e );

        ...
    };

All telephony services must inherit from QPhoneCallImpl to provide the functions on phone calls. See the documentation for that class for more information on the functionality that is required.

The above applies to outgoing calls. For incoming calls, the Asterisk service detects the call in IaxCallProvider::stateEvent() and then constructs an instance of IaxPhoneCall directly:

    void IaxCallProvider::stateEvent( struct iaxc_ev_call_state *e )
    {
        IaxPhoneCall *call = fromCallNo( e->callNo );
        if ( call ) {
            // State change on a known call.
            call->stateEvent( e );
        } else if ( ( e->state & IAXC_CALL_STATE_RINGING ) != 0 ) {
            // Newly arrived incoming call.
            beginStateTransaction();
            QString identifier = QUuid::createUuid().toString();
            IaxPhoneCall *call = new IaxPhoneCall
                ( this, identifier, "Asterisk", e->callNo );
            call->setNumber( e->remote );
            call->setActions( QPhoneCallImpl::Transfer );
            call->setState( QPhoneCall::Incoming );
            endStateTransaction();
        }
    }

Presence

While the IAX2 protocol does not support presence, most other VoIP protocols do. Presence can be handled by inheriting from QPresence:

    class SipPresence : public QPresence
    {
        Q_OBJECT
    public:
        SipPresence( SipTelephonyService *service );
        ~SipPresence();

    public slots:
        virtual bool startMonitoring( const QString& uri );
        virtual bool stopMonitoring( const QString& uri );
        virtual void setLocalPresence( QPresence::Status status );

        ...
    };

The VoIP agent must also add two lines to the initialize() function to create the presence interface at startup time:

    if ( !supports<QPresence>() )
        addInterface( new SipPresence( this ) );

See the documentation for QPresence for more information on the required functionality for the presence interface.

Configuration

Configuration of VoIP services can be very complicated, which is why we recommend that you write a separate settings program that asks the user for the relevant details and then writes them to a configuration file that the agent daemon can access. For the Asterisk example, the source code for its settings program can be found under examples/asterisk/iaxsettings.

Once the configuration file has been updated, it is necessary to notify the agent daemon that it needs to reload the configuration. If your VoIP agent already has a way of performing this notification, then you can stop here. Or you can use the QTelephonyConfiguration interface that Qt Extended provides.

Our Asterisk agent daemon understands two configuration messages from the iaxsettings program: registration and callerid. The former changes the registration settings, and the latter changes the user's caller-id (name and number) information. We implement this in the IaxConfiguration class (iaxconfiguration.h and iaxconfiguration.cpp):

    class IaxConfiguration : public QTelephonyConfiguration
    {
        Q_OBJECT
    public:
        IaxConfiguration( IaxTelephonyService *service );
        ~IaxConfiguration() {}

    public slots:
        virtual void update( const QString& name, const QString& value );
        virtual void request( const QString& name );

    private:
        IaxTelephonyService *service;
    };

    IaxConfiguration::IaxConfiguration( IaxTelephonyService *service )
        : QTelephonyConfiguration( service->service(), service, Server )
    {
        this->service = service;
    }

    void IaxConfiguration::update( const QString& name, const QString& )
    {
        // Process messages from the "iaxsettings" program for config updates.
        if ( name == "registration" )
            service->updateRegistrationConfig();
        else if ( name == "callerid" )
            service->updateCallerIdConfig();
    }

    void IaxConfiguration::request( const QString& name )
    {
        // Not supported - just return an empty value.
        emit notification( name, QString() );
    }

There are two other forms of configuration that can be handled in a special manner: network registration changes and presence changes. The settings program can use QNetworkRegistration::setCurrentOperator() to register to and deregister from the network, and it can use QPresence::setLocalPresence() to update the user's presence state.

It is not a requirement that you use these configuration API's. If your VoIP agent has some other method for changing configuration settings, you are free to use that instead.

Call Policy Managers

New VoIP services need some user interface support in the server to complete the integration. They do this by creating a server task that inherits from QAbstractCallPolicyManager.

The call policy manager tells Qt Extended when the service is active, when network registration changes occur, what call type to use for outgoing call requests, and the icons to display to represent network registration and call types. For Asterisk, the call policy manager is declared as follows (asteriskmanager.h in the server sources):

    class AsteriskManager : public QAbstractCallPolicyManager
    {
        Q_OBJECT
    public:
        AsteriskManager( QObject *parent=0 );
        ~AsteriskManager();

        virtual QString callType() const;
        virtual QString trCallType() const;
        virtual QString callTypeIcon() const;
        virtual QTelephony::RegistrationState registrationState() const;
        virtual QAbstractCallPolicyManager::CallHandling handling(const QString& number);
        virtual bool isAvailable(const QString& number);
        virtual QString registrationMessage() const;
        virtual QString registrationIcon() const;

        ...
    };

    QTOPIA_TASK_INTERFACE(AsteriskManager);

The most important function is QAbstractCallPolicyManager::handling(). This helps the server choose an appropriate telephony service when placing an outgoing call (incoming calls are handled implicitly by the service that first announced them). The handling() function inspects the phone number to determine if it can handle it at present. For Asterisk, the code is as follows (asteriskmanager.cpp):

    QAbstractCallPolicyManager::CallHandling AsteriskManager::handling
            (const QString& number)
    {
        // Cannot handle URI's that contain '@' or ':'.
        if (number.contains(QChar('@')) || number.contains(QChar(':')))
            return CannotHandle;

        // If no network registration, then cannot handle at this time.
        if (registrationState() != QTelephony::RegistrationHome)
            return CannotHandle;

        // Assume that this is a number that we can dial.
        return CanHandle;
    }

We first filter out URI's for SIP and other call types that do not use phone numbers. Then we check to see if the Asterisk service is currently registered to the user's home network. If it is, then we indicate that Asterisk can handle the call.

The return value is used by Qt Extended to select the appropriate telephony service to make the call. If two or more call policy managers return CanHandle, then the user will be presented with a list to choose from. In the case of Asterisk, this may happen if the user is registered to both Asterisk via WiFi and a GSM cellular network at the same time.

The allowable return values and their meanings are as follows:

QAbstractCallPolicyManager::CannotHandleThis telephony service cannot handle the requested number.
QAbstractCallPolicyManager::CanHandleThis telephony service can handle the requested number.
QAbstractCallPolicyManager::MustHandleThis telephony service must handle the requested number. This is typically used for special emergency calls that must be placed over a GSM network even if an Asterisk network is also registered at the same time. This value overrides the CanHandle answers from all other call policy managers.
QAbstractCallPolicyManager::NeverHandleThis telephony service would normally return MustHandle because it is supposed to handle the call, but the call cannot be placed right now. This is typically used for emergency numbers when the phone device is in flight mode. This value causes Qt Extended to reject the call immediately without attempting to place it.

Implementation Checklist

The following summarises the steps involved in integrating a third-party VoIP agent into Qt Extended:

  • Implement the telephony service object that inherits from QTelephonyService (IaxTelephonyService in our example), overridding the initialize() function in particular.
  • Implement the QCop service handler for processing the Telephony::start() and Telephony::stop() messages (IaxTelephonyServiceQCop in our example). Remember to call the service's initialize() function in start() and to register a running task.
  • Register the daemon with the Qt Extended service system underneath $QPEDIR/services/Telephony.
  • Implement a service checker that inherits from QServiceChecker if the telephony service will not be immediately ready for use after the initialize() function returns (IaxServiceChecker in our example).
  • Implement a network registration interface that inherits from QNetworkRegistrationServer (IaxNetworkRegistration in our example).
  • Implement a phone call provider that inherits from QPhoneCallProvider, and an individual phone call handling class that inherits from QPhoneCallImpl (IaxCallProvider and IaxPhoneCall in our example).
  • Implement a presence handler that inherits from QPresence if the VoIP agent supports presence.
  • Implement a separate settings application to provide the user interface for the VoIP agent (iaxsettings in our example).
  • Implement a configuration handler that inherits from QTelephonyConfiguration if the VoIP agent does not have some other means to receive information about configuration changes (IaxConfiguration in our example).
  • Implement a call policy manager task in the Qt Extended server to inform the server about the new VoIP agent and when to use it when placing outgoing calls (AsteriskManager in our example).

Troubleshooting

The following are the most common problems that you are likely to encounter while integrating a third-party VoIP agent:

Service not startedYou must add a service definition file to $QPEDIR/services/Telephony to tell the Qt Extended server how to launch your telephony service daemon.
Service starts and then immediately exitsYou must use QtopiaApplication::registerRunningTask() to ensure that Qt Extended will keep the daemon running after the Telephony::start() QCop message is received. Upon shutdown, you can call QtopiaApplication::unregisterRunningTask() and your daemon will gracefully exit.
Service not registeredThe service will not be properly registered if you do not call the initialize() function on your QTelephonyService instance. You can detect if this is the case by using vsexplorer to inspect the value space underneath /Communications/QNetworkRegistration/asterisk where asterisk is replaced with the name of your service. There should be entries for requestChannel and responseChannel if the service is properly registered.
Call types are not registeredIt will not be possible to place phone calls if the call types have not been properly registered with setCallTypes(). You can detect this by using vsexplorer to inspect the value space underneath /Communications/QPhoneCallProvider/CallTypes. There should be an entry for your service name (asterisk in our example} listing the supported call types.
Incoming calls are not announcedMake sure that you have created the QPhoneCallImpl instance correctly, and have emitted a state change using QPhoneCallProvider::beginStateTransaction() and QPhoneCallProvider::endStateTransaction(). The same applies when calls change state. If the state transaction is not sent, the rest of Qt Extended will be unaware that the call has changed state.
Call policy manager not registeredMake sure that QTOPIA_TASK_INTERFACE(AsteriskManager) appears in the .h file and that QTOPIA_TASK(Asterisk, AsteriskManager), QTOPIA_TASK_PROVIDES(Asterisk, AsteriskManager), and QTOPIA_TASK_PROVIDES(Asterisk, QAbstractCallPolicyManager) appear in the .cpp file, where Asterisk is replaced with the name of your telephony service.

Cette page est une traduction d'une page de la documentation de Qt, écrite par Nokia Corporation and/or its subsidiary(-ies). Les éventuels problèmes résultant d'une mauvaise traduction ne sont pas imputables à Nokia. Qt qtextended4.4
Copyright © 2012 Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon, vous encourez selon la loi jusqu'à 3 ans de prison et jusqu'à 300 000 E de dommages et intérêts. Cette page est déposée à la SACD.
Vous avez déniché une erreur ? Un bug ? Une redirection cassée ? Ou tout autre problème, quel qu'il soit ? Ou bien vous désirez participer à ce projet de traduction ? N'hésitez pas à nous contacter ou par MP !
 
 
 
 
Partenaires

Hébergement Web