|
a/libupnpp/device.cxx |
|
b/libupnpp/device.cxx |
|
... |
|
... |
18 |
using namespace std;
|
18 |
using namespace std;
|
19 |
|
19 |
|
20 |
#include "upnpplib.hxx"
|
20 |
#include "upnpplib.hxx"
|
21 |
#include "vdir.hxx"
|
21 |
#include "vdir.hxx"
|
22 |
#include "device.hxx"
|
22 |
#include "device.hxx"
|
|
|
23 |
#include "log.hxx"
|
23 |
|
24 |
|
24 |
unordered_map<std::string, UpnpDevice *> UpnpDevice::o_devices;
|
25 |
unordered_map<std::string, UpnpDevice *> UpnpDevice::o_devices;
|
|
|
26 |
|
25 |
static string xmlquote(const string& in)
|
27 |
static string xmlquote(const string& in)
|
26 |
{
|
28 |
{
|
27 |
string out;
|
29 |
string out;
|
28 |
for (unsigned int i = 0; i < in.size(); i++) {
|
30 |
for (unsigned int i = 0; i < in.size(); i++) {
|
29 |
switch(in[i]) {
|
31 |
switch(in[i]) {
|
|
... |
|
... |
36 |
}
|
38 |
}
|
37 |
}
|
39 |
}
|
38 |
return out;
|
40 |
return out;
|
39 |
}
|
41 |
}
|
40 |
|
42 |
|
41 |
|
|
|
42 |
static bool vectorstoargslists(const vector<string>& names,
|
43 |
static bool vectorstoargslists(const vector<string>& names,
|
43 |
const vector<string>& values,
|
44 |
const vector<string>& values,
|
44 |
vector<string>& qvalues,
|
45 |
vector<string>& qvalues,
|
45 |
vector<const char *>& cnames,
|
46 |
vector<const char *>& cnames,
|
46 |
vector<const char *>& cvalues)
|
47 |
vector<const char *>& cvalues)
|
47 |
{
|
48 |
{
|
48 |
if (names.size() != values.size()) {
|
49 |
if (names.size() != values.size()) {
|
49 |
cerr << "vectorstoargslists: bad sizes" << endl;
|
50 |
LOGERR("vectorstoargslists: bad sizes" << endl);
|
50 |
return false;
|
51 |
return false;
|
51 |
}
|
52 |
}
|
52 |
|
53 |
|
53 |
cnames.reserve(names.size());
|
54 |
cnames.reserve(names.size());
|
54 |
for (unsigned int i = 0; i < names.size(); i++) {
|
55 |
for (unsigned int i = 0; i < names.size(); i++) {
|
|
... |
|
... |
66 |
|
67 |
|
67 |
UpnpDevice::UpnpDevice(const string& deviceId,
|
68 |
UpnpDevice::UpnpDevice(const string& deviceId,
|
68 |
const unordered_map<string, string>& xmlfiles)
|
69 |
const unordered_map<string, string>& xmlfiles)
|
69 |
: m_deviceId(deviceId)
|
70 |
: m_deviceId(deviceId)
|
70 |
{
|
71 |
{
|
71 |
cerr << "UpnpDevice::UpnpDevice(" << m_deviceId << ")" << endl;
|
72 |
//LOGDEB("UpnpDevice::UpnpDevice(" << m_deviceId << ")" << endl);
|
72 |
|
73 |
|
73 |
m_lib = LibUPnP::getLibUPnP(true);
|
74 |
m_lib = LibUPnP::getLibUPnP(true);
|
74 |
if (!m_lib) {
|
75 |
if (!m_lib) {
|
75 |
cerr << " Can't get LibUPnP" << endl;
|
76 |
LOGFAT(" Can't get LibUPnP" << endl);
|
76 |
return;
|
77 |
return;
|
77 |
}
|
78 |
}
|
78 |
if (!m_lib->ok()) {
|
79 |
if (!m_lib->ok()) {
|
79 |
cerr << "Lib init failed: " <<
|
80 |
LOGFAT("Lib init failed: " <<
|
80 |
m_lib->errAsString("main", m_lib->getInitError()) << endl;
|
81 |
m_lib->errAsString("main", m_lib->getInitError()) << endl);
|
81 |
m_lib = 0;
|
82 |
m_lib = 0;
|
82 |
return;
|
83 |
return;
|
83 |
}
|
84 |
}
|
84 |
|
85 |
|
85 |
if (o_devices.empty()) {
|
86 |
if (o_devices.empty()) {
|
|
... |
|
... |
89 |
m_lib->registerHandler(UPNP_EVENT_SUBSCRIPTION_REQUEST, sCallBack,this);
|
90 |
m_lib->registerHandler(UPNP_EVENT_SUBSCRIPTION_REQUEST, sCallBack,this);
|
90 |
}
|
91 |
}
|
91 |
|
92 |
|
92 |
VirtualDir* theVD = VirtualDir::getVirtualDir();
|
93 |
VirtualDir* theVD = VirtualDir::getVirtualDir();
|
93 |
if (theVD == 0) {
|
94 |
if (theVD == 0) {
|
94 |
cerr << "UpnpDevice::UpnpDevice: can't get VirtualDir" << endl;
|
95 |
LOGFAT("UpnpDevice::UpnpDevice: can't get VirtualDir" << endl);
|
95 |
return;
|
96 |
return;
|
96 |
}
|
97 |
}
|
97 |
|
98 |
|
98 |
unordered_map<string,string>::const_iterator it =
|
99 |
unordered_map<string,string>::const_iterator it =
|
99 |
xmlfiles.find("description.xml");
|
100 |
xmlfiles.find("description.xml");
|
100 |
if (it == xmlfiles.end()) {
|
101 |
if (it == xmlfiles.end()) {
|
101 |
cerr << "UpnpDevice::UpnpDevice: no description.xml found in xmlfiles"
|
102 |
LOGFAT("UpnpDevice::UpnpDevice: no description.xml found in xmlfiles"
|
102 |
<< endl;
|
103 |
<< endl);
|
103 |
return;
|
104 |
return;
|
104 |
}
|
105 |
}
|
105 |
|
106 |
|
106 |
const string& description = it->second;
|
107 |
const string& description = it->second;
|
107 |
|
108 |
|
|
... |
|
... |
113 |
m_lib->setupWebServer(description);
|
114 |
m_lib->setupWebServer(description);
|
114 |
|
115 |
|
115 |
o_devices[m_deviceId] = this;
|
116 |
o_devices[m_deviceId] = this;
|
116 |
}
|
117 |
}
|
117 |
|
118 |
|
|
|
119 |
static PTMutexInit cblock;
|
|
|
120 |
|
118 |
// Main libupnp callback: use the device id and call the right device
|
121 |
// Main libupnp callback: use the device id and call the right device
|
119 |
int UpnpDevice::sCallBack(Upnp_EventType et, void* evp, void* tok)
|
122 |
int UpnpDevice::sCallBack(Upnp_EventType et, void* evp, void* tok)
|
120 |
{
|
123 |
{
|
121 |
//cerr << "UpnpDevice::sCallBack" << endl;
|
124 |
//LOGDEB("UpnpDevice::sCallBack" << endl);
|
|
|
125 |
PTMutexLocker lock(cblock);
|
122 |
|
126 |
|
123 |
string deviceid;
|
127 |
string deviceid;
|
124 |
switch (et) {
|
128 |
switch (et) {
|
125 |
case UPNP_CONTROL_ACTION_REQUEST:
|
129 |
case UPNP_CONTROL_ACTION_REQUEST:
|
126 |
deviceid = ((struct Upnp_Action_Request *)evp)->DevUDN;
|
130 |
deviceid = ((struct Upnp_Action_Request *)evp)->DevUDN;
|
|
... |
|
... |
133 |
case UPNP_EVENT_SUBSCRIPTION_REQUEST:
|
137 |
case UPNP_EVENT_SUBSCRIPTION_REQUEST:
|
134 |
deviceid = ((struct Upnp_Subscription_Request*)evp)->UDN;
|
138 |
deviceid = ((struct Upnp_Subscription_Request*)evp)->UDN;
|
135 |
break;
|
139 |
break;
|
136 |
|
140 |
|
137 |
default:
|
141 |
default:
|
138 |
cerr << "UpnpDevice::sCallBack: unknown event " << et << endl;
|
142 |
LOGERR("UpnpDevice::sCallBack: unknown event " << et << endl);
|
139 |
return UPNP_E_INVALID_PARAM;
|
143 |
return UPNP_E_INVALID_PARAM;
|
140 |
}
|
144 |
}
|
141 |
// cerr << "UpnpDevice::sCallBack: deviceid[" << deviceid << "]" << endl;
|
145 |
// LOGDEB("UpnpDevice::sCallBack: deviceid[" << deviceid << "]" << endl);
|
142 |
|
146 |
|
143 |
unordered_map<std::string, UpnpDevice *> *devmap =
|
147 |
unordered_map<std::string, UpnpDevice *> *devmap =
|
144 |
(unordered_map<std::string, UpnpDevice *> *) tok;
|
148 |
(unordered_map<std::string, UpnpDevice *> *) tok;
|
145 |
unordered_map<std::string, UpnpDevice *>::iterator it =
|
149 |
unordered_map<std::string, UpnpDevice *>::iterator it =
|
146 |
o_devices.find(deviceid);
|
150 |
o_devices.find(deviceid);
|
147 |
|
151 |
|
148 |
if (it == o_devices.end()) {
|
152 |
if (it == o_devices.end()) {
|
149 |
cerr << "UpnpDevice::sCallBack: Device not found: [" <<
|
153 |
LOGERR("UpnpDevice::sCallBack: Device not found: [" <<
|
150 |
deviceid << "]" << endl;
|
154 |
deviceid << "]" << endl);
|
151 |
return UPNP_E_INVALID_PARAM;
|
155 |
return UPNP_E_INVALID_PARAM;
|
152 |
}
|
156 |
}
|
153 |
// cerr << "UpnpDevice::sCallBack: device found: [" << it->second
|
157 |
// LOGDEB("UpnpDevice::sCallBack: device found: [" << it->second
|
154 |
// << "]" << endl;
|
158 |
// << "]" << endl);
|
155 |
return (it->second)->callBack(et, evp);
|
159 |
return (it->second)->callBack(et, evp);
|
156 |
}
|
160 |
}
|
157 |
|
161 |
|
158 |
static PTMutexInit cblock;
|
|
|
159 |
int UpnpDevice::callBack(Upnp_EventType et, void* evp)
|
162 |
int UpnpDevice::callBack(Upnp_EventType et, void* evp)
|
160 |
{
|
163 |
{
|
161 |
PTMutexLocker lock(cblock);
|
|
|
162 |
cerr << "UpnpDevice::callBack: evt type:" <<
|
|
|
163 |
LibUPnP::evTypeAsString(et).c_str() << endl;
|
|
|
164 |
|
|
|
165 |
switch (et) {
|
164 |
switch (et) {
|
166 |
case UPNP_CONTROL_ACTION_REQUEST:
|
165 |
case UPNP_CONTROL_ACTION_REQUEST:
|
167 |
{
|
166 |
{
|
168 |
struct Upnp_Action_Request *act = (struct Upnp_Action_Request *)evp;
|
167 |
struct Upnp_Action_Request *act = (struct Upnp_Action_Request *)evp;
|
169 |
cerr << "UPNP_CONTROL_ACTION_REQUEST: " << act->ActionName <<
|
168 |
LOGDEB("UPNP_CONTROL_ACTION_REQUEST: " << act->ActionName <<
|
170 |
" Params: " << ixmlPrintDocument(act->ActionRequest) << endl;
|
169 |
". Params: " << ixmlPrintDocument(act->ActionRequest) << endl);
|
171 |
|
170 |
|
172 |
unordered_map<string, string>::const_iterator servit =
|
171 |
unordered_map<string, string>::const_iterator servit =
|
173 |
m_serviceTypes.find(act->ServiceID);
|
172 |
m_serviceTypes.find(act->ServiceID);
|
174 |
if (servit == m_serviceTypes.end()) {
|
173 |
if (servit == m_serviceTypes.end()) {
|
175 |
cerr << "Bad serviceID" << endl;
|
174 |
LOGERR("Bad serviceID" << endl);
|
176 |
return UPNP_E_INVALID_PARAM;
|
175 |
return UPNP_E_INVALID_PARAM;
|
177 |
}
|
176 |
}
|
178 |
const string& servicetype = servit->second;
|
177 |
const string& servicetype = servit->second;
|
179 |
|
178 |
|
180 |
unordered_map<string, soapfun>::iterator callit =
|
179 |
unordered_map<string, soapfun>::iterator callit =
|
181 |
m_calls.find(act->ActionName);
|
180 |
m_calls.find(act->ActionName);
|
182 |
if (callit == m_calls.end()) {
|
181 |
if (callit == m_calls.end()) {
|
183 |
cerr << "No such action: " << act->ActionName << endl;
|
182 |
LOGINF("No such action: " << act->ActionName << endl);
|
184 |
return UPNP_E_INVALID_PARAM;
|
183 |
return UPNP_E_INVALID_PARAM;
|
185 |
}
|
184 |
}
|
186 |
|
185 |
|
187 |
SoapArgs sc;
|
186 |
SoapArgs sc;
|
188 |
if (!decodeSoapBody(act->ActionName, act->ActionRequest, &sc)) {
|
187 |
if (!decodeSoapBody(act->ActionName, act->ActionRequest, &sc)) {
|
189 |
cerr << "Error decoding Action call arguments" << endl;
|
188 |
LOGERR("Error decoding Action call arguments" << endl);
|
190 |
return UPNP_E_INVALID_PARAM;
|
189 |
return UPNP_E_INVALID_PARAM;
|
191 |
}
|
190 |
}
|
192 |
SoapData dt;
|
191 |
SoapData dt;
|
193 |
dt.name = act->ActionName;
|
192 |
dt.name = act->ActionName;
|
194 |
dt.serviceType = servicetype;
|
193 |
dt.serviceType = servicetype;
|
195 |
|
194 |
|
196 |
// Call the action routine
|
195 |
// Call the action routine
|
197 |
int ret = callit->second(sc, dt);
|
196 |
int ret = callit->second(sc, dt);
|
198 |
if (ret != UPNP_E_SUCCESS) {
|
197 |
if (ret != UPNP_E_SUCCESS) {
|
199 |
cerr << "Action failed: " << sc.name << endl;
|
198 |
LOGERR("Action failed: " << sc.name << endl);
|
200 |
return ret;
|
199 |
return ret;
|
201 |
}
|
200 |
}
|
202 |
|
201 |
|
203 |
// Encode result data
|
202 |
// Encode result data
|
204 |
act->ActionResult = buildSoapBody(dt);
|
203 |
act->ActionResult = buildSoapBody(dt);
|
205 |
cerr << "Response data: " <<
|
204 |
//LOGDEB("Response data: " <<
|
206 |
ixmlPrintDocument(act->ActionResult) << endl;
|
205 |
// ixmlPrintDocument(act->ActionResult) << endl);
|
207 |
|
206 |
|
208 |
return ret;
|
207 |
return ret;
|
209 |
|
|
|
210 |
}
|
208 |
}
|
211 |
break;
|
209 |
break;
|
212 |
|
210 |
|
213 |
case UPNP_CONTROL_GET_VAR_REQUEST:
|
211 |
case UPNP_CONTROL_GET_VAR_REQUEST:
|
214 |
// Note that the "Control: query for variable" action is
|
212 |
// Note that the "Control: query for variable" action is
|
215 |
// deprecated (upnp arch v1), and we should never get these.
|
213 |
// deprecated (upnp arch v1), and we should never get these.
|
216 |
{
|
214 |
{
|
217 |
struct Upnp_State_Var_Request *act =
|
215 |
struct Upnp_State_Var_Request *act =
|
218 |
(struct Upnp_State_Var_Request *)evp;
|
216 |
(struct Upnp_State_Var_Request *)evp;
|
219 |
cerr << "UPNP_CONTROL_GET_VAR__REQUEST?: " << act->StateVarName << endl;
|
217 |
LOGDEB("UPNP_CONTROL_GET_VAR__REQUEST?: " << act->StateVarName << endl);
|
220 |
}
|
218 |
}
|
221 |
break;
|
219 |
break;
|
222 |
|
220 |
|
223 |
case UPNP_EVENT_SUBSCRIPTION_REQUEST:
|
221 |
case UPNP_EVENT_SUBSCRIPTION_REQUEST:
|
224 |
{
|
222 |
{
|
225 |
struct Upnp_Subscription_Request *act =
|
223 |
struct Upnp_Subscription_Request *act =
|
226 |
(struct Upnp_Subscription_Request*)evp;
|
224 |
(struct Upnp_Subscription_Request*)evp;
|
227 |
cerr << "UPNP_EVENT_SUBSCRIPTION_REQUEST: " << act->ServiceId << endl;
|
225 |
LOGDEB("UPNP_EVENT_SUBSCRIPTION_REQUEST: " << act->ServiceId << endl);
|
228 |
|
226 |
|
229 |
vector<string> names, values, qvalues;
|
227 |
vector<string> names, values, qvalues;
|
230 |
if (!getEventData(true, act->ServiceId, names, values)) {
|
228 |
if (!getEventData(true, act->ServiceId, names, values)) {
|
231 |
break;
|
229 |
break;
|
232 |
}
|
230 |
}
|
|
... |
|
... |
235 |
int ret =
|
233 |
int ret =
|
236 |
UpnpAcceptSubscription(m_lib->getdvh(), act->UDN, act->ServiceId,
|
234 |
UpnpAcceptSubscription(m_lib->getdvh(), act->UDN, act->ServiceId,
|
237 |
&cnames[0], &cvalues[0],
|
235 |
&cnames[0], &cvalues[0],
|
238 |
int(cnames.size()), act->Sid);
|
236 |
int(cnames.size()), act->Sid);
|
239 |
if (ret != UPNP_E_SUCCESS) {
|
237 |
if (ret != UPNP_E_SUCCESS) {
|
240 |
cerr << "UpnpDevice::callBack: UpnpAcceptSubscription failed: "
|
238 |
LOGERR("UpnpDevice::callBack: UpnpAcceptSubscription failed: "
|
241 |
<< ret << endl;
|
239 |
<< ret << endl);
|
242 |
}
|
240 |
}
|
243 |
|
241 |
|
244 |
return ret;
|
242 |
return ret;
|
245 |
}
|
243 |
}
|
246 |
break;
|
244 |
break;
|
247 |
|
245 |
|
248 |
default:
|
246 |
default:
|
249 |
cerr << "UpnpDevice::callBack: unknown op" << endl;
|
247 |
LOGINF("UpnpDevice::callBack: unknown libupnp event type: " <<
|
|
|
248 |
LibUPnP::evTypeAsString(et).c_str() << endl);
|
250 |
return UPNP_E_INVALID_PARAM;
|
249 |
return UPNP_E_INVALID_PARAM;
|
251 |
}
|
250 |
}
|
252 |
return UPNP_E_INVALID_PARAM;
|
251 |
return UPNP_E_INVALID_PARAM;
|
253 |
}
|
252 |
}
|
254 |
|
253 |
|
255 |
void UpnpDevice::addServiceType(const std::string& serviceId,
|
254 |
void UpnpDevice::addServiceType(const std::string& serviceId,
|
256 |
const std::string& serviceType)
|
255 |
const std::string& serviceType)
|
257 |
{
|
256 |
{
|
258 |
cerr << "UpnpDevice::addServiceType: [" <<
|
257 |
//LOGDEB("UpnpDevice::addServiceType: [" <<
|
259 |
serviceId << "] -> [" << serviceType << endl;
|
258 |
// serviceId << "] -> [" << serviceType << endl);
|
260 |
m_serviceTypes[serviceId] = serviceType;
|
259 |
m_serviceTypes[serviceId] = serviceType;
|
261 |
}
|
260 |
}
|
262 |
|
261 |
|
263 |
void UpnpDevice::addActionMapping(const std::string& actName, soapfun fun)
|
262 |
void UpnpDevice::addActionMapping(const std::string& actName, soapfun fun)
|
264 |
{
|
263 |
{
|
265 |
// cerr << "UpnpDevice::addActionMapping:" << actName << endl;
|
264 |
// LOGDEB("UpnpDevice::addActionMapping:" << actName << endl);
|
266 |
m_calls[actName] = fun;
|
265 |
m_calls[actName] = fun;
|
267 |
}
|
266 |
}
|
268 |
|
267 |
|
269 |
void UpnpDevice::notifyEvent(const string& serviceId,
|
268 |
void UpnpDevice::notifyEvent(const string& serviceId,
|
270 |
const vector<string>& names,
|
269 |
const vector<string>& names,
|
271 |
const vector<string>& values)
|
270 |
const vector<string>& values)
|
272 |
{
|
271 |
{
|
273 |
cerr << "UpnpDevice::notifyEvent" << endl;
|
272 |
LOGDEB("UpnpDevice::notifyEvent: "
|
274 |
|
273 |
<< (names.empty()?"Empty names??":names[0]) << endl);
|
|
|
274 |
if (names.empty())
|
|
|
275 |
return;
|
275 |
vector<const char *> cnames, cvalues;
|
276 |
vector<const char *> cnames, cvalues;
|
276 |
vector<string> qvalues;
|
277 |
vector<string> qvalues;
|
277 |
vectorstoargslists(names, values, qvalues, cnames, cvalues);
|
278 |
vectorstoargslists(names, values, qvalues, cnames, cvalues);
|
278 |
|
279 |
|
279 |
int ret = UpnpNotify(m_lib->getdvh(), m_deviceId.c_str(),
|
280 |
int ret = UpnpNotify(m_lib->getdvh(), m_deviceId.c_str(),
|
280 |
serviceId.c_str(), &cnames[0], &cvalues[0],
|
281 |
serviceId.c_str(), &cnames[0], &cvalues[0],
|
281 |
int(cnames.size()));
|
282 |
int(cnames.size()));
|
282 |
if (ret != UPNP_E_SUCCESS) {
|
283 |
if (ret != UPNP_E_SUCCESS) {
|
283 |
cerr << "UpnpDevice::notifyEvent: UpnpNotify failed: " << ret << endl;
|
284 |
LOGERR("UpnpDevice::notifyEvent: UpnpNotify failed: " << ret << endl);
|
|
|
285 |
}
|
|
|
286 |
}
|
|
|
287 |
|
|
|
288 |
static pthread_cond_t evloopcond = PTHREAD_COND_INITIALIZER;
|
|
|
289 |
|
|
|
290 |
static void timespec_addnanos(struct timespec *ts, int nanos)
|
|
|
291 |
{
|
|
|
292 |
ts->tv_nsec += nanos;
|
|
|
293 |
if (ts->tv_nsec > 1000 * 1000 * 1000) {
|
|
|
294 |
int secs = ts->tv_nsec / (1000 * 1000 * 1000);
|
|
|
295 |
ts->tv_sec += secs;
|
|
|
296 |
ts->tv_nsec -= secs * (1000 * 1000 * 1000);
|
284 |
}
|
297 |
}
|
285 |
}
|
298 |
}
|
|
|
299 |
int timespec_diffms(const struct timespec& old, const struct timespec& recent)
|
|
|
300 |
{
|
|
|
301 |
return (recent.tv_sec - old.tv_sec) * 1000 +
|
|
|
302 |
(recent.tv_nsec - old.tv_nsec) / (1000 * 1000);
|
|
|
303 |
}
|
286 |
|
304 |
|
|
|
305 |
// Loop on services, and poll each for changed data. Generate event
|
|
|
306 |
// only if changed data exists. Every 3 S we generate an artificial
|
|
|
307 |
// event with all the current state.
|
287 |
void UpnpDevice::eventloop()
|
308 |
void UpnpDevice::eventloop()
|
288 |
{
|
309 |
{
|
289 |
struct timespec duration;
|
|
|
290 |
duration.tv_sec = 0;
|
|
|
291 |
duration.tv_nsec = 500 * 1000 * 1000; // 1/2 S
|
|
|
292 |
while (nanosleep(&duration, 0) == 0) {
|
|
|
293 |
static int count = 0;
|
310 |
int count = 0;
|
294 |
//cerr << "UpnpDevice::eventloop: " << count++ << endl;
|
311 |
const int loopwait_ms = 500; // Polling mpd every 1/2 S
|
|
|
312 |
const int nloopstofull = 10; // Full state every 5 S
|
|
|
313 |
struct timespec wkuptime, earlytime;
|
|
|
314 |
bool didearly = false;
|
|
|
315 |
|
|
|
316 |
for (;;) {
|
|
|
317 |
clock_gettime(CLOCK_REALTIME, &wkuptime);
|
|
|
318 |
timespec_addnanos(&wkuptime, loopwait_ms * 1000 * 1000);
|
|
|
319 |
|
|
|
320 |
//LOGDEB("eventloop: now " << time(0) << " wkup at "<<
|
|
|
321 |
// wkuptime.tv_sec << " S " << wkuptime.tv_nsec << " ns" << endl);
|
|
|
322 |
|
295 |
PTMutexLocker lock(cblock);
|
323 |
PTMutexLocker lock(cblock);
|
|
|
324 |
int err = pthread_cond_timedwait(&evloopcond, lock.getMutex(),
|
|
|
325 |
&wkuptime);
|
|
|
326 |
if (err && err != ETIMEDOUT) {
|
|
|
327 |
LOGINF("UpnpDevice:eventloop: wait errno " << errno << endl);
|
|
|
328 |
break;
|
|
|
329 |
} else if (err == 0) {
|
|
|
330 |
// Early wakeup. Only does something if it did not already
|
|
|
331 |
// happen recently
|
|
|
332 |
if (didearly) {
|
|
|
333 |
int millis = timespec_diffms(earlytime, wkuptime);
|
|
|
334 |
if (millis < loopwait_ms) {
|
|
|
335 |
// Do nothing. didearly stays true
|
|
|
336 |
// LOGDEB("eventloop: early, previous too close "<<endl);
|
|
|
337 |
continue;
|
|
|
338 |
} else {
|
|
|
339 |
// had an early wakeup previously, but it was a
|
|
|
340 |
// long time ago. Update state and wakeup
|
|
|
341 |
// LOGDEB("eventloop: early, previous is old "<<endl);
|
|
|
342 |
earlytime = wkuptime;
|
|
|
343 |
}
|
|
|
344 |
} else {
|
|
|
345 |
// Early wakeup, previous one was normal. Remember.
|
|
|
346 |
// LOGDEB("eventloop: early, no previous" << endl);
|
|
|
347 |
didearly = true;
|
|
|
348 |
earlytime = wkuptime;
|
|
|
349 |
}
|
|
|
350 |
} else {
|
|
|
351 |
// Normal wakeup
|
|
|
352 |
// LOGDEB("eventloop: normal wakeup" << endl);
|
|
|
353 |
didearly = false;
|
|
|
354 |
}
|
|
|
355 |
|
|
|
356 |
count++;
|
|
|
357 |
bool all = count && ((count % nloopstofull) == 0);
|
|
|
358 |
//LOGDEB("UpnpDevice::eventloop count "<<count<<" all "<<all<<endl);
|
|
|
359 |
|
296 |
for (unordered_map<string, string>::const_iterator it =
|
360 |
for (unordered_map<string, string>::const_iterator it =
|
297 |
m_serviceTypes.begin();
|
|
|
298 |
it != m_serviceTypes.end(); it++) {
|
361 |
m_serviceTypes.begin(); it != m_serviceTypes.end(); it++) {
|
299 |
vector<string> names, values;
|
362 |
vector<string> names, values;
|
300 |
if (!getEventData(false, it->first, names, values) ||
|
363 |
if (!getEventData(all, it->first, names, values) || names.empty()) {
|
301 |
names.empty()) {
|
|
|
302 |
continue;
|
364 |
continue;
|
303 |
}
|
365 |
}
|
304 |
notifyEvent(it->first, names, values);
|
366 |
notifyEvent(it->first, names, values);
|
305 |
}
|
367 |
}
|
306 |
}
|
368 |
}
|
307 |
}
|
369 |
}
|
|
|
370 |
|
|
|
371 |
void UpnpDevice::loopWakeup()
|
|
|
372 |
{
|
|
|
373 |
pthread_cond_broadcast(&evloopcond);
|
|
|
374 |
}
|
|
|
375 |
|