Download this file

libupnpp-ctl.txt    419 lines (309 with data), 15.8 kB

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
= Libupnpp for Control Points
Libupnpp is a C++ wrapper for link:http://pupnp.sourceforge.net/[libupnp],
a.k.a Portable UPnP, which is a direct descendant of the Open Source SDK
released by Intel in support of UPnP development.
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 modules for controlling specific
AVTransport or OpenHome audio services, and it is relatively easy to add
modules for other services externally (the internal modules have no more
access to library internals than an external module would).
Limitations:
- The underlying libupnp only supports a single IP interface.
- The libupnpp methods are blocking. Multithreading will be needed to
achieve parallelism in your program (in any case the underlying libupnp
uses 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 libupnp
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 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);
----
== 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 is
largely an opaque structure, which you will get through the discovery
interface, and pass to a Device or Service constructor.
== 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.
Some details of event handling in libupnpp have changed as of version
0.16.
== Event handling in version 0.15 and before
Services implemented by the library always subscribe to events. This
happens in the object constructor, by a call to the class derived
registerCallback() method. Example:
----
avtransport.hxx:
class AVTransport : public Service {
...
AVTransport(const UPnPDeviceDesc& device,
const UPnPServiceDesc& service)
: Service(device, service) {
registerCallback();
}
...
avtransport.cxx:
void AVTransport::registerCallback()
{
Service::registerCallback(bind(&AVTransport::evtCallback, this, _1));
}
----
'Service::registerCallback()' performs the UPnP subscription and takes note
of the function to call when an event arrive.
This means that a subscription (needing a network exchange) is performed
each time a Service object is built, even if events are not actually needed
by the user.
The Service base class destructor erases the callback hook and cancels the
event subscription.
=== Event handling for version 0.16 and later
As of version 0.16 of the library, the event subscription is only performed
if and when 'installReporter()' is called. If you don't need events, you
will not incur their overhead.
== Logging
Both 'libupnp' and 'libupnpp' have configurable error and debug logging.
=== libupnp logging
The log from 'libupnp' is very detailed and mostly useful for low level
debugging of UPnP issues. The logging functions in 'libupnp' are
conditionnally 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. 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 only needed for library version 0.16 and later. It 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.