|
a/doc/libupnpp-ctl.txt |
|
b/doc/libupnpp-ctl.txt |
1 |
= Libupnpp for Control Points
|
1 |
= Libupnpp for Control Points
|
2 |
|
2 |
|
3 |
Libupnpp is a C++ wrapper for libupnp, a.k.a Portable UPnP, which is a
|
3 |
Libupnpp is a C++ wrapper for link:http://pupnp.sourceforge.net/[libupnp],
|
4 |
direct descendant of the Open Source SDK released by Intel in support of
|
4 |
a.k.a Portable UPnP, which is a direct descendant of the Open Source SDK
|
5 |
UPnP development.
|
5 |
released by Intel in support of UPnP development.
|
6 |
|
6 |
|
7 |
The Control Point side of libupnpp allows a C++ program to discover
|
7 |
Libupnpp can be used to implement UPnP devices and services, or Control
|
|
|
8 |
Points.
|
|
|
9 |
|
|
|
10 |
The Control Point side of libupnpp, which is documented here, allows a C++
|
8 |
devices, and exchange commands and status with them.
|
11 |
program to discover UPnP devices, and exchange commands and status with
|
|
|
12 |
them.
|
9 |
|
13 |
|
10 |
The library has a number of predefined modules for controlling specific
|
14 |
The library has a number of predefined modules for controlling specific
|
11 |
AVTransport or OpenHome audio services, and it is relatively easy to add
|
15 |
AVTransport or OpenHome audio services, and it is relatively easy to add
|
12 |
modules for other services externally (the internal modules have no more
|
16 |
modules for other services externally (the internal modules have no more
|
13 |
access to library internals than an external module would).
|
17 |
access to library internals than an external module would).
|
14 |
|
18 |
|
15 |
Limitations:
|
19 |
Limitations:
|
16 |
|
20 |
|
17 |
- The underlying libupnp only supports a single IP interface.
|
21 |
- The underlying libupnp only supports a single IP interface.
|
18 |
The libupnpp methods are blocking, it is supposed that you will be using
|
22 |
- The libupnpp methods are blocking. Multithreading will be needed to
|
19 |
threads to achieve any needed parallelism in your program.
|
23 |
achieve parallelism in your program (in any case the underlying libupnp
|
|
|
24 |
uses threads, so multithreading support is a requirement).
|
|
|
25 |
|
|
|
26 |
link:refdoc/html/index.html[Reference documentation (doxygen)]
|
20 |
|
27 |
|
21 |
== The Library object
|
28 |
== The Library object
|
22 |
|
29 |
|
23 |
The library is represented by a global singleton with a number of utility
|
30 |
The library is represented by a global singleton with a number of utility
|
24 |
methods, mostly related to setting parameters for the underlying libupnp
|
31 |
methods, mostly related to setting parameters for the underlying libupnp
|
|
... |
|
... |
61 |
In libupnpp, the content of the main description document for a given
|
68 |
In libupnpp, the content of the main description document for a given
|
62 |
device is provided by a
|
69 |
device is provided by a
|
63 |
link:refdoc/html/classUPnPClient_1_1UPnPDeviceDesc.html[UPnPClient::UPnPDeviceDesc]
|
70 |
link:refdoc/html/classUPnPClient_1_1UPnPDeviceDesc.html[UPnPClient::UPnPDeviceDesc]
|
64 |
object.
|
71 |
object.
|
65 |
|
72 |
|
66 |
If you are mostly using the devices and services predefined in the library,
|
73 |
When using the devices and services predefined by the library, this is
|
67 |
this is largely an opaque structure, which you will get through the
|
74 |
largely an opaque structure, which you will get through the discovery
|
68 |
discovery interface, and pass to an actual device constructor.
|
75 |
interface, and pass to a Device or Service constructor.
|
69 |
|
76 |
|
70 |
== Devices
|
77 |
== Devices
|
71 |
|
78 |
|
72 |
UPnP device are entities which can contain other, embedded devices, and
|
79 |
UPnP devices are entities which can contain other, embedded devices, and
|
73 |
services.
|
80 |
services.
|
74 |
|
81 |
|
75 |
Embedded devices are quite rare, and in my experience badly supported by
|
82 |
Embedded devices are quite rare, and in my experience badly supported by
|
76 |
typical control points.
|
83 |
common control points.
|
77 |
|
84 |
|
78 |
In general, the service is the interesting entity, and the wise approach is
|
85 |
In general, the service is the interesting entity, and the wise approach is
|
79 |
the Pythonic "quacks like a duck" one: if a device has the service you
|
86 |
the Pythonic "quacks like a duck" one: if a device has the service you
|
80 |
need, you can use it.
|
87 |
need, you can use it.
|
81 |
|
88 |
|
82 |
For example the predefined 'MediaRenderer' class in libupnpp does not even
|
89 |
For example the predefined 'MediaRenderer' class in libupnpp does not even
|
83 |
bother to verify its own type when built from a description: it just
|
90 |
bother to verify its own type when built from a description: it just
|
84 |
provides methods to query and retrieve handles to interesting services
|
91 |
provides methods to query and retrieve handles to interesting services
|
85 |
usually found in a Media Renderer (both OpenHome and UPnP AV). In most
|
92 |
usually found in a Media Renderer (both OpenHome and UPnP AV). In most
|
86 |
cases, not all services will be available, and the caller will compose an "a
|
93 |
cases, not all services will be available, and the caller will compose an "a
|
87 |
la carte" object to serve its need (e.g. using either UPnP AV Rendering
|
94 |
la carte" object to serve its needs (e.g. using either UPnP AV Rendering
|
88 |
Control or OpenHome Volume for controlling volume, depending on
|
95 |
Control or OpenHome Volume for controlling volume, depending on
|
89 |
availability).
|
96 |
availability).
|
90 |
|
97 |
|
91 |
As another example, the 'myrdcvolume' program from the libupnpp samples
|
98 |
As another example, the 'myrdcvolume' program from the libupnpp samples
|
92 |
shows how to implement a service interface without using the predefined
|
99 |
shows how to implement a service interface without using the predefined
|
|
... |
|
... |
95 |
the appropriate service
|
102 |
the appropriate service
|
96 |
(i.e. 'urn:schemas-upnp-org:service:RenderingControl'), and action
|
103 |
(i.e. 'urn:schemas-upnp-org:service:RenderingControl'), and action
|
97 |
('Volume').
|
104 |
('Volume').
|
98 |
|
105 |
|
99 |
The library predefines two device classes, for Media Server devices and
|
106 |
The library predefines two device classes, for Media Server devices and
|
100 |
Media Renderer ones. In both cases the only use for the class is to retrieve
|
107 |
Media Renderer ones. Both are convenience classes with utility code to
|
101 |
handles to the underlying services.
|
108 |
build the underlying service objects and retrieve handles for them.
|
102 |
|
109 |
|
103 |
|
110 |
|
104 |
== Services
|
111 |
== Services
|
105 |
|
112 |
|
106 |
Most UPnP functionality is provided through services, which are end points
|
113 |
Most UPnP functionality is provided through services, which are end points
|
|
... |
|
... |
110 |
(some of which also work with video). You will usually not need to bother
|
117 |
(some of which also work with video). You will usually not need to bother
|
111 |
constructing the service interface objects: rather you will let the device
|
118 |
constructing the service interface objects: rather you will let the device
|
112 |
classes do it and return a handle (but there is nothing to prevent you from
|
119 |
classes do it and return a handle (but there is nothing to prevent you from
|
113 |
building the service objects directly).
|
120 |
building the service objects directly).
|
114 |
|
121 |
|
115 |
It also has helper functions for helping with the independant implementation
|
122 |
The library also has helper functions for helping with the independant
|
116 |
of a service interface.
|
123 |
implementation of a service interface.
|
117 |
|
124 |
|
118 |
== Predefined Service classes
|
125 |
== Predefined Service classes
|
119 |
|
126 |
|
120 |
* link:refdoc/html/classUPnPClient_1_1RenderingControl.html[UPnP AV
|
127 |
* link:refdoc/html/classUPnPClient_1_1RenderingControl.html[UPnP AV
|
121 |
Rendering Control]
|
128 |
Rendering Control]
|
|
... |
|
... |
132 |
|
139 |
|
133 |
The role of most methods in these classes is to marshal the input data into
|
140 |
The role of most methods in these classes is to marshal the input data into
|
134 |
SOAP format, perform the SOAP call, then marshal and return the output
|
141 |
SOAP format, perform the SOAP call, then marshal and return the output
|
135 |
data. They are all synchronous.
|
142 |
data. They are all synchronous.
|
136 |
|
143 |
|
|
|
144 |
== Eventing
|
|
|
145 |
|
|
|
146 |
UPnP services can report changes in their state to Control Points through
|
|
|
147 |
events. In practise, the Control Point implements an internal HTTP server
|
|
|
148 |
to which the services connect to report events.
|
|
|
149 |
|
|
|
150 |
Event reporting is not active by default and needs to be activated by the
|
|
|
151 |
Control Point by 'subscribing' to the service.
|
|
|
152 |
|
|
|
153 |
Users of Service classes can receive asynchronous events by calling the
|
|
|
154 |
link:refdoc/html/classUPnPClient_1_1Service.html#a2d9aad17b90587f8c6a3791944edcdde[installReporter()]
|
|
|
155 |
method, to specify what functions should be called when an event arrives.
|
|
|
156 |
|
|
|
157 |
NOTE: The event functions are called from a separate thread
|
|
|
158 |
and some synchronization will usually be required.
|
|
|
159 |
|
|
|
160 |
Some details of event handling in libupnpp have changed as of version
|
|
|
161 |
0.16.
|
|
|
162 |
|
|
|
163 |
== Event handling in version 0.15 and before
|
|
|
164 |
|
|
|
165 |
Services implemented by the library always subscribe to events. This
|
|
|
166 |
happens in the object constructor, by a call to the class derived
|
|
|
167 |
registerCallback() method. Example:
|
|
|
168 |
|
|
|
169 |
----
|
|
|
170 |
avtransport.hxx:
|
|
|
171 |
|
|
|
172 |
class AVTransport : public Service {
|
|
|
173 |
...
|
|
|
174 |
AVTransport(const UPnPDeviceDesc& device,
|
|
|
175 |
const UPnPServiceDesc& service)
|
|
|
176 |
: Service(device, service) {
|
|
|
177 |
registerCallback();
|
|
|
178 |
}
|
|
|
179 |
...
|
|
|
180 |
|
|
|
181 |
avtransport.cxx:
|
|
|
182 |
|
|
|
183 |
void AVTransport::registerCallback()
|
|
|
184 |
{
|
|
|
185 |
Service::registerCallback(bind(&AVTransport::evtCallback, this, _1));
|
|
|
186 |
}
|
|
|
187 |
----
|
|
|
188 |
|
|
|
189 |
'Service::registerCallback()' performs the UPnP subscription and takes note
|
|
|
190 |
of the function to call when an event arrive.
|
|
|
191 |
|
|
|
192 |
This means that a subscription (needing a network exchange) is performed
|
|
|
193 |
each time a Service object is built, even if events are not actually needed
|
|
|
194 |
by the user.
|
|
|
195 |
|
|
|
196 |
The Service base class destructor erases the callback hook and cancels the
|
|
|
197 |
event subscription.
|
|
|
198 |
|
|
|
199 |
=== Event handling for version 0.16 and later
|
|
|
200 |
|
|
|
201 |
As of version 0.16 of the library, the event subscription is only performed
|
|
|
202 |
if and when 'installReporter()' is called. If you don't need events, you
|
|
|
203 |
will not incur their overhead.
|
|
|
204 |
|
|
|
205 |
== Logging
|
|
|
206 |
|
|
|
207 |
Both 'libupnp' and 'libupnpp' have configurable error and debug logging.
|
|
|
208 |
|
|
|
209 |
=== libupnp logging
|
|
|
210 |
|
|
|
211 |
The log from 'libupnp' is very detailed and mostly useful for low level
|
|
|
212 |
debugging of UPnP issues. The logging functions in 'libupnp' are
|
|
|
213 |
conditionnally compiled, and may not be enabled for your distribution (you
|
|
|
214 |
can check UPNP_HAVE_DEBUG in 'include/upnp/upnpconfig.h').
|
|
|
215 |
|
|
|
216 |
If the 'libupnp' logging functions are enabled, you can control them
|
|
|
217 |
through the
|
|
|
218 |
link:refdoc/html/classUPnPP_1_1LibUPnP.html#afb21929cea7859786d93dec1086563bc[LibUPnP::setLogFileName()]
|
|
|
219 |
and 'LibUPnP::setLogLevel()' methods.
|
|
|
220 |
|
|
|
221 |
=== libupnpp logging
|
|
|
222 |
|
|
|
223 |
'libupnpp' logging is distinct from the 'libupnp' functions, and always
|
|
|
224 |
enabled, at a configurable level of verbosity.
|
|
|
225 |
|
|
|
226 |
The log is initialized by a call to
|
|
|
227 |
link:refdoc/html/classLogger.html[Logger::getTheLog()]. The
|
|
|
228 |
verbosity level can be adjusted through 'setLogLevel()', and macros are
|
|
|
229 |
used to emit the actual messages. The printing is based on the C++ iostreams.
|
|
|
230 |
|
|
|
231 |
See libupnpp/log.h for more details.
|
|
|
232 |
|
|
|
233 |
Exemple:
|
|
|
234 |
|
|
|
235 |
----
|
|
|
236 |
if (Logger::getTheLog(logfilename) == 0) {
|
|
|
237 |
cerr << "Can't initialize log" << endl;
|
|
|
238 |
return 1;
|
|
|
239 |
}
|
|
|
240 |
Logger::getTheLog("")->setLogLevel(Logger::LLINF);
|
|
|
241 |
|
|
|
242 |
...
|
|
|
243 |
|
|
|
244 |
LOGINF("Message at level INFO, it outputs some value: " << value);
|
|
|
245 |
----
|
|
|
246 |
|
|
|
247 |
Of course you can use the 'LOGXX' macros in your own code.
|
|
|
248 |
|
|
|
249 |
|
137 |
== Implementing a Service class
|
250 |
== Implementing a Service class
|
138 |
|
251 |
|
|
|
252 |
If you want to access a service for which no predefined class exists in
|
|
|
253 |
libupnpp, you need to define its interface yourself, by deriving the
|
|
|
254 |
'libupnpp' link:refdoc/html/classUPnPClient_1_1Service.html[Service] class.
|
|
|
255 |
|
|
|
256 |
The methods in the base class and in the helper modules make it very easy
|
|
|
257 |
to write the derived class.
|
|
|
258 |
|
|
|
259 |
NOTE: libupnpp has no provision to use the service description XML document
|
|
|
260 |
to define the service client methods. However, the device side has a script
|
|
|
261 |
to turn a service description into an implementation device-side skeleton.
|
|
|
262 |
|
|
|
263 |
The derived class main constructor will take Device and Service description
|
|
|
264 |
structures are arguments and will need to call the base class
|
|
|
265 |
constructor. Example:
|
|
|
266 |
|
|
|
267 |
----
|
|
|
268 |
class OHPlaylist : public Service {
|
|
|
269 |
public:
|
|
|
270 |
|
|
|
271 |
OHPlaylist(const UPnPDeviceDesc& device, const UPnPServiceDesc& service)
|
|
|
272 |
: Service(device, service) {
|
|
|
273 |
}
|
|
|
274 |
...
|
|
|
275 |
----
|
|
|
276 |
|
|
|
277 |
If there are initialization steps which are specific to the service type,
|
|
|
278 |
they should be done inside the 'serviceInit()' method, which
|
|
|
279 |
should be called from the constructor (see below for a more detailed
|
|
|
280 |
description). Most services don't need 'serviceInit()', so an empty
|
|
|
281 |
implementation is provided by the base class.
|
|
|
282 |
|
|
|
283 |
Action methods are then just ordinary methods, which will call the base
|
|
|
284 |
class methods to perform the networky things.
|
|
|
285 |
|
|
|
286 |
Example of an action setting a value:
|
|
|
287 |
|
|
|
288 |
----
|
|
|
289 |
int RenderingControl::setMute(bool mute, const string& channel)
|
|
|
290 |
{
|
|
|
291 |
SoapOutgoing args(getServiceType(), "SetMute");
|
|
|
292 |
args("InstanceID", "0")("Channel", channel)
|
|
|
293 |
("DesiredMute", SoapHelp::i2s(mute?1:0));
|
|
|
294 |
SoapIncoming data;
|
|
|
295 |
return runAction(args, data);
|
|
|
296 |
}
|
|
|
297 |
----
|
|
|
298 |
|
|
|
299 |
The link:refdoc/html/classUPnPP_1_1SoapOutgoing.html[SoapOutgoing]
|
|
|
300 |
constructor takes a service type and action name arguments. Its 'operator ()'
|
|
|
301 |
accepts additional named string arguments.
|
|
|
302 |
|
|
|
303 |
The
|
|
|
304 |
link:refdoc/html/classUPnPClient_1_1Service.html#a7eee43639eff25893117ab52b0b036db[Service::runAction()]
|
|
|
305 |
method performs the SOAP call and provides any resulting data in a
|
|
|
306 |
link:refdoc/html/classUPnPP_1_1SoapIncoming.html[SoapIncoming] object, from
|
|
|
307 |
which named values can be easily extracted. There is no return data in the
|
|
|
308 |
above example, see below for an action using 'SoapIncoming::get()' to
|
|
|
309 |
extract data.
|
|
|
310 |
|
|
|
311 |
Example of an action retrieving a value (note there is nothing to prevent
|
|
|
312 |
an action to both set and retrieve data).
|
|
|
313 |
|
|
|
314 |
----
|
|
|
315 |
bool RenderingControl::getMute(const string& channel)
|
|
|
316 |
{
|
|
|
317 |
SoapOutgoing args(getServiceType(), "GetMute");
|
|
|
318 |
args("InstanceID", "0")("Channel", channel);
|
|
|
319 |
SoapIncoming data;
|
|
|
320 |
int ret = runAction(args, data);
|
|
|
321 |
if (ret != UPNP_E_SUCCESS) {
|
|
|
322 |
return false;
|
|
|
323 |
}
|
|
|
324 |
bool mute;
|
|
|
325 |
if (!data.get("CurrentMute", &mute)) {
|
|
|
326 |
LOGERR("RenderingControl:getMute: missing CurrentMute in response"
|
|
|
327 |
<< endl);
|
|
|
328 |
return false;
|
|
|
329 |
}
|
|
|
330 |
return mute;
|
|
|
331 |
}
|
|
|
332 |
----
|
|
|
333 |
|
|
|
334 |
There are utility methods in the base class for actions which either
|
|
|
335 |
transport no data, or send or receive a single value:
|
|
|
336 |
link:refdoc/html/classUPnPClient_1_1Service.html#a9ec23e2f36c2bbdefaac4991d80d6063[runTrivialAction()],
|
|
|
337 |
link:///net/y/home/dockes/projets/mpdupnp/0.16-libupnpp/doc/refdoc/html/classUPnPClient_1_1Service.html#a7cde71d714f07a6bb655760238e52e25[runSimpleAction()],
|
|
|
338 |
link:refdoc/html/classUPnPClient_1_1Service.html#a1de907ba9604a20aaf3611e71ded445d[runSimpleGet()]
|
|
|
339 |
|
|
|
340 |
|
|
|
341 |
=== Methods which may need reimplementation
|
|
|
342 |
|
|
|
343 |
----
|
|
|
344 |
virtual bool UPnPClient::Service::serviceTypeMatch(const std::string& tp)=0;
|
|
|
345 |
----
|
|
|
346 |
|
|
|
347 |
This is only needed for library version 0.16 and later. It is used by the
|
|
|
348 |
base class to find a matching service description in the device description
|
|
|
349 |
service list, which is done when calling
|
|
|
350 |
link:refdoc/html/classUPnPClient_1_1Service.html#afb13c0cc50b44c9c62e024961cf23b75[initFromDescription()].
|
|
|
351 |
|
|
|
352 |
The latter method is useful for building an object in two phases, first an
|
|
|
353 |
empty constructor, then initialization from the device description.
|
|
|
354 |
|
|
|
355 |
Derived classes compare the service types in the list with their own type
|
|
|
356 |
string.
|
|
|
357 |
|
|
|
358 |
----
|
|
|
359 |
virtual bool UPnPClient::Service::serviceInit(const UPnPDeviceDesc& device,
|
|
|
360 |
const UPnPServiceDesc& service);
|
|
|
361 |
----
|
|
|
362 |
|
|
|
363 |
This is the part of the initialization specific to the service type. When
|
|
|
364 |
it is called, the Service class is fully initialized. Most services
|
|
|
365 |
don't need to do anything in there. An example of use would be the
|
|
|
366 |
Rendering Control service calling the device to retrieve the range of
|
|
|
367 |
volume control values.
|
|
|
368 |
|
|
|
369 |
An empty implementation is provided by the base class.
|
|
|
370 |
|
|
|
371 |
The method is called from
|
|
|
372 |
link:refdoc/html/classUPnPClient_1_1Service.html#afb13c0cc50b44c9c62e024961cf23b75[initFromDescription()],
|
|
|
373 |
and, if it does anything, it should be called from the non-empty derived class
|
|
|
374 |
constructor too.
|
|
|
375 |
|
139 |
== Eventing
|
376 |
=== Eventing
|
140 |
|
377 |
|
141 |
== Logging
|
378 |
Derived service classes can define a
|
|
|
379 |
link:refdoc/html/classUPnPClient_1_1Service.html#ac4ba122d2fe124eff620c62cbf5ac1a1[registerCallBack()]
|
|
|
380 |
method to register a function to be called when an event arrives.
|
|
|
381 |
'registerCallBack()' will be called by 'Service::installReporter()' when
|
|
|
382 |
the user register their interest in events. In turn, 'registerCallback()'
|
|
|
383 |
should call
|
|
|
384 |
link:refdoc/html/classUPnPClient_1_1Service.html#a8b8580b88b4fbfc1de18a14d7811169d[Service::registerCallBack(evtCBFunc)]
|
|
|
385 |
to register their actual callback routine.
|
|
|
386 |
|
|
|
387 |
Callback functions can be any 'std::function' taking appropriate arguments,
|
|
|
388 |
usually a member function of the service object.
|