= Libupnpp for Control Points

Libupnpp is a pass:[C++] wrapper for link:http://pupnp.sourceforge.net/[libupnp]
a.k.a Portable UPnP (up to branch 0.17), or its pass:[C++] descendant,
link:http://framagit.org/medoc92/npupnp[libnpupnp] (versions 0.18 and
later).

Libupnpp can be used to implement UPnP devices and services, or Control
Points.

The Control Point side of libupnpp, which is documented here, allows a C++
program to discover UPnP devices, and exchange commands and status with
them.

The library has a number of predefined classes for controlling specific
AVTransport or OpenHome audio services, and it is easy to add classes for
other services externally (the internal classes have no more access to
library internals than an external module would).

Limitations:

- The old libupnp underlying library only supports a single IP
  interface. The newer libnpupnp supports multiple interfaces for IPV4, but
  a single one for IPV6 at the moment.
- Most libupnpp methods are blocking. Multithreading will be needed to
  achieve parallelism in your program (in any case libupnpp and the
  underlying library  use threads, so multithreading support is a requirement).

link:refdoc/html/index.html[Reference documentation (doxygen)]

== The Library object

The library is represented by a global singleton with a number of utility
methods, mostly related to setting parameters for the underlying library.

The link:refdoc/html/classUPnPP_1_1LibUPnP.html#a00ef52e413804a5f6dca74e2459749c7[instanciation call] takes a number of arguments to specify
non-default values, e.g. for the IP address or interface to use.

If you have no need for the non-default parameter values, or the singleton
utility methods, there is no necessity to explicitly instanciate the
library object, this will be done internally as needed.


== Discovery

The UPnP Discovery framework allows searching for devices on a network and
maintaining a directory of active devices.

Libupnpp implements this function with the
link:refdoc/html/classUPnPClient_1_1UPnPDeviceDirectory.html[UPnPDeviceDirectory]
class, which is instanciated as a single object.

Example code for accessing a device for which you know the 'friendly name':

----
    auto *superdir = UPnPClient::UPnPDeviceDirectory::getTheDir();

    UPnPClient::UPnPDeviceDesc devicedesc;
    bool ok = superdir->getDevByFName(name, devicedesc);

----

This call may delay for a couple seconds if the library was just activated,
and an UPnP search is being waited for. During program execution, the call
should return immediately.

Other methods of the device directory objects allow enumerating the devices
which advertised themselves on the network.

== Description

UPnP devices describe their capabilities in XML documents which can be
retrieved from them through HTTP.

In libupnpp, the content of the main description document for a given
device is provided by a
link:refdoc/html/classUPnPClient_1_1UPnPDeviceDesc.html[UPnPClient::UPnPDeviceDesc]
object.

When using the devices and services predefined by the library, this may be
managed as an opaque structure, which you will get through the discovery
interface, and pass to a Device or Service constructor. However its
internals are accessible and contain the full parsed description of the
device, its services and possible sub-devices, and a copy of the raw data.


== Devices

UPnP devices are entities which can contain other, embedded devices, and
services.

Embedded devices are quite rare, and in my experience badly supported by
common control points.

In general, the service is the interesting entity, and the wise approach is
the Pythonic "quacks like a duck" one: if a device has the service you
need, you can use it.

For example the predefined 'MediaRenderer' class in libupnpp does not even
bother to verify its own type when built from a description: it just
provides methods to query and retrieve handles to interesting services
usually found in a Media Renderer (both OpenHome and UPnP AV). In most
cases, not all services will be available, and the caller will compose an "a
la carte" object to serve its needs (e.g. using either UPnP AV Rendering
Control or OpenHome Volume for controlling volume, depending on
availability).

As another example, the 'myrdcvolume' program from the libupnpp samples
shows how to implement a service interface without using the predefined
ones. It does not bother with the device type either (actually it does not
bother at all with a device object), but just checks for the existence of
the appropriate service
(i.e. 'urn:schemas-upnp-org:service:RenderingControl'), and action
('Volume').

The library predefines two device classes, for Media Server devices and
Media Renderer ones. Both are convenience classes with utility code to
build the underlying service objects and retrieve handles for them.


== Services

Most UPnP functionality is provided through services, which are end points
implemented inside a device, callable through HTTP/SOAP.

libupnpp provides a set of easy C++ interfaces for most UPnP audio services
(some of which also work with video). You will usually not need to bother
constructing the service interface objects: rather you will let the device
classes do it and return a handle (but there is nothing to prevent you from
building the service objects directly).

There are two possible approaches for accessing a service which does not
have a predefined interface class in the library:

- Use the library helper functions for implementing a specific service
  interface, by deriving from the base
  link:refdoc/html/classUPnPClient_1_1Service.html[Service] class.
- Use the xref:TypedService[TypedService] generic string-based interface
  class.
  

== Predefined Service classes

* link:refdoc/html/classUPnPClient_1_1RenderingControl.html[UPnP AV
  Rendering Control] 
* link:refdoc/html/classUPnPClient_1_1AVTransport.html[UpnP AV AVTransport]
* link:refdoc/html/classUPnPClient_1_1OHPlaylist.html[Open Home Playlist]
* link:refdoc/html/classUPnPClient_1_1OHInfo.html[Open Home Info]
* link:refdoc/html/classUPnPClient_1_1OHProduct.html[Open Home  Product]
* link:refdoc/html/classUPnPClient_1_1OHRadio.html[Open Home Radio]
* link:refdoc/html/classUPnPClient_1_1OHTime.html[Open Home Time]
* link:refdoc/html/classUPnPClient_1_1OHVolume.html[Open Home Volume]
* link:refdoc/html/classUPnPClient_1_1OHReceiver.html[Open Home Songcast
  Receiver] 
* link:refdoc/html/classUPnPClient_1_1OHSender.html[Open Home Songcast Sender]

The role of most methods in these classes is to marshal the input data into
SOAP format, perform the SOAP call, then marshal and return the output
data. They are all synchronous.

[[TypedService]]
== String based interface

The link:refdoc/html/classUPnPClient_1_1TypedService.html[TypedService]
class provides access to the actions in a service through a simple
string-based interface.

The user only needs specify an action name and a list of arguments to call
an action. The class uses the service description retrieved from the
device to generate the actual SOAP call. Any returned data is provided
through a C++ name-value map.

The class also has an associated
link:refdoc/html/namespaceUPnPClient.html#ad3edfb36acb86cb98961233b127fc3df[helper
function] which provides a simplified interface to discovery.

While the class is less convenient to use than one customized to a single
service, which can provide full encoding/decoding between SOAP data and a
natural C++ interface, it avoids having to write the corresponding code. It
was mostly implemented as a convenient interface for the SWIG module, but
it can probably have other uses.


== Eventing

UPnP services can report changes in their state to Control Points through
events. In practise, the Control Point implements an internal HTTP server
to which the services connect to report events.

Event reporting is not active by default and needs to be activated by the
Control Point by 'subscribing' to the service.

Users of Service classes can receive asynchronous events by calling the
link:refdoc/html/classUPnPClient_1_1Service.html#a2d9aad17b90587f8c6a3791944edcdde[installReporter()]
method, to specify what functions should be called when an event arrives.

NOTE: The event functions are called from a separate thread
and some synchronization will usually be required.

The event subscription is only performed by libupnpp if and when the
service `installReporter()` method is called.

== Logging

Both 'libupnp/libnpupnp' and 'libupnpp' have configurable error and debug logging.

=== libupnp logging

The log from the base library is very detailed and mostly useful for low
level debugging of UPnP issues. The logging functions in are conditionally
compiled, and may not be enabled for your distribution (you can check
UPNP_HAVE_DEBUG in 'include/upnp/upnpconfig.h').

If the 'libupnp' logging functions are enabled, you can control them
through the
link:refdoc/html/classUPnPP_1_1LibUPnP.html#afb21929cea7859786d93dec1086563bc[LibUPnP::setLogFileName()]
and 'LibUPnP::setLogLevel()' methods.

=== libupnpp logging

'libupnpp' logging is distinct from the 'libupnp' functions, and always
enabled, at a configurable level of verbosity.

The log is initialized by a call to
link:refdoc/html/classLogger.html[Logger::getTheLog()]. The
verbosity level can be adjusted through 'setLogLevel()', and macros are
used to emit the actual messages. The printing is based on the C++ iostreams.

See libupnpp/log.h for more details.

Exemple:

----
    if (Logger::getTheLog(logfilename) == 0) {
        cerr << "Can't initialize log" << endl;
        return 1;
    }
    Logger::getTheLog("")->setLogLevel(Logger::LLINF);

    ...

    LOGINF("Message at level INFO, it outputs some value: " << value);
----

Of course you can use the 'LOGXX' macros in your own code.


== Implementing a Service class

If you want to access a service for which no predefined class exists in
libupnpp, you need to define its interface yourself, by deriving the
'libupnpp' link:refdoc/html/classUPnPClient_1_1Service.html[Service] class.

The methods in the base class and in the helper modules make it very easy
to write the derived class.

NOTE: libupnpp has no provision to use the service description XML document
to define the service client methods, except, partially, when using the
link:refdoc/html/classUPnPClient_1_1TypedService.html[TypedService]
class. However, the device side has a script to turn a service description
into an implementation device-side skeleton.

The derived class main constructor will take Device and Service description
structures are arguments and will need to call the base class
constructor. Example:

----
class OHPlaylist : public Service {
public:

    OHPlaylist(const UPnPDeviceDesc& device, const UPnPServiceDesc& service)
        : Service(device, service) {
    }
    ...
----

If there are initialization steps which are specific to the service type,
they should be done inside the 'serviceInit()' method, which 
should be called from the constructor (see below for a more detailed
description). Most services don't need 'serviceInit()', so an empty
implementation is provided by the base class.

Action methods are then just ordinary methods, which will call the base
class methods to perform the networky things.

Example of an action setting a value:

----
int RenderingControl::setMute(bool mute, const string& channel)
{
    SoapOutgoing args(getServiceType(), "SetMute");
    args("InstanceID", "0")("Channel", channel)
    ("DesiredMute", SoapHelp::i2s(mute?1:0));
    SoapIncoming data;
    return runAction(args, data);
}
----

The link:refdoc/html/classUPnPP_1_1SoapOutgoing.html[SoapOutgoing]
constructor takes a service type and action name arguments. Its 'operator ()'
accepts additional named string arguments.

The
link:refdoc/html/classUPnPClient_1_1Service.html#a7eee43639eff25893117ab52b0b036db[Service::runAction()]
method performs the SOAP call and provides any resulting data in a
link:refdoc/html/classUPnPP_1_1SoapIncoming.html[SoapIncoming] object, from
which named values can be easily extracted. There is no return data in the
above example, see below for an action using 'SoapIncoming::get()' to
extract data.

Example of an action retrieving a value (note there is nothing to prevent
an action to both set and retrieve data).

----
bool RenderingControl::getMute(const string& channel)
{
    SoapOutgoing args(getServiceType(), "GetMute");
    args("InstanceID", "0")("Channel", channel);
    SoapIncoming data;
    int ret = runAction(args, data);
    if (ret != UPNP_E_SUCCESS) {
        return false;
    }
    bool mute;
    if (!data.get("CurrentMute", &mute)) {
        LOGERR("RenderingControl:getMute: missing CurrentMute in response"
               << endl);
        return false;
    }
    return mute;
}
----

There are utility methods in the base class for actions which either
transport no data, or send or receive a single value:
link:refdoc/html/classUPnPClient_1_1Service.html#a9ec23e2f36c2bbdefaac4991d80d6063[runTrivialAction()],
link:refdoc/html/classUPnPClient_1_1Service.html#a7cde71d714f07a6bb655760238e52e25[runSimpleAction()],
link:refdoc/html/classUPnPClient_1_1Service.html#a1de907ba9604a20aaf3611e71ded445d[runSimpleGet()]


=== Methods which may need reimplementation

----
virtual bool UPnPClient::Service::serviceTypeMatch(const std::string& tp)=0; 	
----

This is used by the base class to find a matching service description in
the device description service list, which is done when calling
link:refdoc/html/classUPnPClient_1_1Service.html#afb13c0cc50b44c9c62e024961cf23b75[initFromDescription()].

The latter method is useful for building an object in two phases, first an
empty constructor, then initialization from the device description.

Derived classes compare the service types in the list with their own type
string.

----
virtual bool UPnPClient::Service::serviceInit(const UPnPDeviceDesc& device,
                                          const UPnPServiceDesc& service);
----

This is the part of the initialization specific to the service type. When
it is called, the Service class is fully initialized. Most services
don't need to do anything in there. An example of use would be the
Rendering Control service calling the device to retrieve the range of
volume control values.

An empty implementation is provided by the base class.

The method is called from
link:refdoc/html/classUPnPClient_1_1Service.html#afb13c0cc50b44c9c62e024961cf23b75[initFromDescription()],
and, if it does anything, it should be called from the non-empty derived class
constructor too.

=== Eventing

Derived service classes can define a
link:refdoc/html/classUPnPClient_1_1Service.html#ac4ba122d2fe124eff620c62cbf5ac1a1[registerCallBack()]
method to register a function to be called when an event arrives. 
'registerCallBack()' will be called by 'Service::installReporter()' when
the user register their interest in events. In turn, 'registerCallback()'
should call
link:refdoc/html/classUPnPClient_1_1Service.html#a8b8580b88b4fbfc1de18a14d7811169d[Service::registerCallBack(evtCBFunc)]
to register their actual callback routine.

Callback functions can be any 'std::function' taking appropriate arguments,
usually a member function of the service object.
