Merge lp:~justinmcp/media-hub/1239432 into lp:media-hub
- 1239432
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Jim Hodapp |
Approved revision: | 77 |
Merged at revision: | 95 |
Proposed branch: | lp:~justinmcp/media-hub/1239432 |
Merge into: | lp:media-hub |
Diff against target: |
723 lines (+579/-6) 11 files modified
CMakeLists.txt (+2/-2) debian/control (+2/-0) src/core/media/CMakeLists.txt (+4/-1) src/core/media/call-monitor/CMakeLists.txt (+23/-0) src/core/media/call-monitor/call_monitor.cpp (+206/-0) src/core/media/call-monitor/call_monitor.h (+41/-0) src/core/media/call-monitor/qtbridge.cpp (+186/-0) src/core/media/call-monitor/qtbridge.h (+87/-0) src/core/media/service_implementation.cpp (+26/-3) src/core/media/service_implementation.h (+1/-0) tests/unit-tests/CMakeLists.txt (+1/-0) |
To merge this branch: | bzr merge lp:~justinmcp/media-hub/1239432 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Jim Hodapp (community) | code | Approve | |
PS Jenkins bot | continuous-integration | Needs Fixing | |
Review via email: mp+240387@code.launchpad.net |
Commit message
#1239432 Music fails to pause on incoming/outgoing calls
Description of the change
PS Jenkins bot (ps-jenkins) wrote : | # |
- 76. By Justin McPherson <justin@phablet-dev>
-
Merge from trunk.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:76
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Jim Hodapp (jhodapp) wrote : | # |
Have you seen this example that gets used by the telephony service to monitor the same call signals? http://
Jim Hodapp (jhodapp) wrote : | # |
Looks really good. Only one minor inline comment to address. Would like to help you test it after you get it built in a silo.
- 77. By Justin McPherson <justin@phablet-dev>
-
Removed not needed #include
Jim Hodapp (jhodapp) wrote : | # |
Looks good, thanks
Preview Diff
1 | === modified file 'CMakeLists.txt' |
2 | --- CMakeLists.txt 2014-10-14 16:21:47 +0000 |
3 | +++ CMakeLists.txt 2014-11-04 23:38:46 +0000 |
4 | @@ -6,8 +6,8 @@ |
5 | set(UBUNTU_MEDIA_HUB_VERSION_MINOR 0) |
6 | set(UBUNTU_MEDIA_HUB_VERSION_PATCH 0) |
7 | |
8 | -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror -Wall -pedantic -Wextra -fPIC -pthread") |
9 | -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Werror -Wall -fno-strict-aliasing -pedantic -Wextra -fPIC -pthread") |
10 | +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -fPIC -pthread") |
11 | +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -fno-strict-aliasing -Wextra -fPIC -pthread") |
12 | |
13 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake) |
14 | |
15 | |
16 | === modified file 'debian/control' |
17 | --- debian/control 2014-10-23 21:25:38 +0000 |
18 | +++ debian/control 2014-11-04 23:38:46 +0000 |
19 | @@ -28,6 +28,8 @@ |
20 | gstreamer1.0-libav, |
21 | libgstreamer1.0-dev, |
22 | pkg-config, |
23 | + qtbase5-dev, |
24 | + libtelepathy-qt5-dev, |
25 | Standards-Version: 3.9.5 |
26 | Homepage: https://launchpad.net/media-hub |
27 | # If you aren't a member of ~phablet-team but need to upload packaging changes, |
28 | |
29 | === modified file 'src/core/media/CMakeLists.txt' |
30 | --- src/core/media/CMakeLists.txt 2014-10-14 16:21:47 +0000 |
31 | +++ src/core/media/CMakeLists.txt 2014-11-04 23:38:46 +0000 |
32 | @@ -7,6 +7,8 @@ |
33 | |
34 | file(GLOB MPRIS_HEADERS mpris/ *.h) |
35 | |
36 | +add_subdirectory(call-monitor) |
37 | + |
38 | add_library( |
39 | media-hub-common SHARED |
40 | |
41 | @@ -90,7 +92,7 @@ |
42 | media-hub-service |
43 | |
44 | media-hub-common |
45 | - |
46 | + call-monitor |
47 | ${DBUS_LIBRARIES} |
48 | ${DBUS_CPP_LDFLAGS} |
49 | ${GLog_LIBRARY} |
50 | @@ -113,6 +115,7 @@ |
51 | |
52 | media-hub-service |
53 | media-hub-client |
54 | + call-monitor |
55 | |
56 | ${DBUS_LIBRARIES} |
57 | ${DBUS_CPP_LDFLAGS} |
58 | |
59 | === added directory 'src/core/media/call-monitor' |
60 | === added file 'src/core/media/call-monitor/CMakeLists.txt' |
61 | --- src/core/media/call-monitor/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
62 | +++ src/core/media/call-monitor/CMakeLists.txt 2014-11-04 23:38:46 +0000 |
63 | @@ -0,0 +1,23 @@ |
64 | +SET (CMAKE_INCLUDE_CURRENT_DIR ON) |
65 | +SET (CMAKE_AUTOMOC ON) |
66 | + |
67 | +find_package(Qt5Core REQUIRED) |
68 | +find_package(PkgConfig REQUIRED) |
69 | +pkg_check_modules(TP_QT5 REQUIRED TelepathyQt5) |
70 | + |
71 | +#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -pedantic -Wextra -fPIC -pthread") |
72 | + |
73 | +include_directories( |
74 | + ${TP_QT5_INCLUDE_DIRS} |
75 | +) |
76 | + |
77 | +add_library(call-monitor STATIC |
78 | + call_monitor.cpp |
79 | + qtbridge.cpp |
80 | +) |
81 | + |
82 | +target_link_libraries(call-monitor |
83 | + ${TP_QT5_LIBRARIES} |
84 | +) |
85 | + |
86 | +qt5_use_modules(call-monitor Core DBus) |
87 | |
88 | === added file 'src/core/media/call-monitor/call_monitor.cpp' |
89 | --- src/core/media/call-monitor/call_monitor.cpp 1970-01-01 00:00:00 +0000 |
90 | +++ src/core/media/call-monitor/call_monitor.cpp 2014-11-04 23:38:46 +0000 |
91 | @@ -0,0 +1,206 @@ |
92 | +/* |
93 | + * Copyright (C) 2014 Canonical Ltd |
94 | + * |
95 | + * This program is free software: you can redistribute it and/or modify |
96 | + * it under the terms of the GNU Lesser General Public License version 3 as |
97 | + * published by the Free Software Foundation. |
98 | + * |
99 | + * This program is distributed in the hope that it will be useful, |
100 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
101 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
102 | + * GNU Lesser General Public License for more details. |
103 | + * |
104 | + * You should have received a copy of the GNU Lesser General Public License |
105 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
106 | + * |
107 | + * Author: Justin McPherson <justin.mcpherson@canonical.com> |
108 | + */ |
109 | + |
110 | + |
111 | +#include "call_monitor.h" |
112 | + |
113 | +#include "qtbridge.h" |
114 | +#include <TelepathyQt/AccountManager> |
115 | +#include <TelepathyQt/SimpleCallObserver> |
116 | +#include <TelepathyQt/PendingOperation> |
117 | +#include <TelepathyQt/PendingReady> |
118 | +#include <TelepathyQt/PendingAccount> |
119 | + |
120 | +#include <list> |
121 | +#include <mutex> |
122 | + |
123 | + |
124 | +namespace { |
125 | +class TelepathyCallMonitor : public QObject |
126 | +{ |
127 | + Q_OBJECT |
128 | +public: |
129 | + TelepathyCallMonitor(const Tp::AccountPtr& account): |
130 | + mAccount(account), |
131 | + mCallObserver(Tp::SimpleCallObserver::create(mAccount)) { |
132 | + connect(mCallObserver.data(), SIGNAL(callStarted(Tp::CallChannelPtr)), SIGNAL(offHook())); |
133 | + connect(mCallObserver.data(), SIGNAL(callEnded(Tp::CallChannelPtr,QString,QString)), SIGNAL(onHook())); |
134 | + connect(mCallObserver.data(), SIGNAL(streamedMediaCallStarted(Tp::StreamedMediaChannelPtr)), SIGNAL(offHook())); |
135 | + connect(mCallObserver.data(), SIGNAL(streamedMediaCallEnded(Tp::StreamedMediaChannelPtr,QString,QString)), SIGNAL(onHook())); |
136 | + } |
137 | + |
138 | +Q_SIGNALS: |
139 | + void offHook(); |
140 | + void onHook(); |
141 | + |
142 | +private: |
143 | + Tp::AccountPtr mAccount; |
144 | + Tp::SimpleCallObserverPtr mCallObserver; |
145 | +}; |
146 | + |
147 | + |
148 | +class TelepathyBridge : public QObject |
149 | +{ |
150 | + Q_OBJECT |
151 | +public: |
152 | + TelepathyBridge(): |
153 | + QObject(0) { |
154 | + Tp::registerTypes(); |
155 | + |
156 | + QTimer::singleShot(0, this, SLOT(accountManagerSetup())); |
157 | + } |
158 | + |
159 | + ~TelepathyBridge() { |
160 | + for (std::list<TelepathyCallMonitor*>::iterator it = mCallMonitors.begin(); |
161 | + it != mCallMonitors.end(); |
162 | + ++it) { |
163 | + delete *it; |
164 | + } |
165 | + } |
166 | + |
167 | + void on_change(const std::function<void(CallMonitor::State)>& func) { |
168 | + std::lock_guard<std::mutex> l(cb_lock); |
169 | + cb = func; |
170 | + } |
171 | + |
172 | +private Q_SLOTS: |
173 | + void accountManagerSetup() { |
174 | + mAccountManager = Tp::AccountManager::create(); |
175 | + connect(mAccountManager->becomeReady(), |
176 | + SIGNAL(finished(Tp::PendingOperation*)), |
177 | + SLOT(accountManagerReady(Tp::PendingOperation*))); |
178 | + } |
179 | + |
180 | + void accountManagerReady(Tp::PendingOperation* operation) { |
181 | + if (operation->isError()) { |
182 | + std::cerr << "TelepathyBridge: Operation failed (accountManagerReady)" << std::endl; |
183 | + QTimer::singleShot(1000, this, SLOT(accountManagerSetup())); // again |
184 | + return; |
185 | + } |
186 | + |
187 | + Q_FOREACH(const Tp::AccountPtr& account, mAccountManager->allAccounts()) { |
188 | + connect(account->becomeReady(Tp::Account::FeatureCapabilities), |
189 | + SIGNAL(finished(Tp::PendingOperation*)), |
190 | + SLOT(accountReady(Tp::PendingOperation*))); |
191 | + } |
192 | + |
193 | + connect(mAccountManager.data(), SIGNAL(newAccount(Tp::AccountPtr)), SLOT(newAccount(Tp::AccountPtr))); |
194 | + } |
195 | + |
196 | + void newAccount(const Tp::AccountPtr& account) |
197 | + { |
198 | + connect(account->becomeReady(Tp::Account::FeatureCapabilities), |
199 | + SIGNAL(finished(Tp::PendingOperation*)), |
200 | + SLOT(accountReady(Tp::PendingOperation*))); |
201 | + } |
202 | + |
203 | + void accountReady(Tp::PendingOperation* operation) { |
204 | + if (operation->isError()) { |
205 | + std::cerr << "TelepathyAccount: Operation failed (accountReady)" << std::endl; |
206 | + return; |
207 | + } |
208 | + |
209 | + Tp::PendingReady* pendingReady = qobject_cast<Tp::PendingReady*>(operation); |
210 | + if (pendingReady == 0) { |
211 | + std::cerr << "Rejecting account because could not understand ready status" << std::endl; |
212 | + return; |
213 | + } |
214 | + |
215 | + checkAndAddAccount(Tp::AccountPtr::qObjectCast(pendingReady->proxy())); |
216 | + } |
217 | + |
218 | + void offHook() |
219 | + { |
220 | + std::lock_guard<std::mutex> l(cb_lock); |
221 | + if (cb) |
222 | + cb(CallMonitor::OffHook); |
223 | + } |
224 | + |
225 | + void onHook() |
226 | + { |
227 | + std::lock_guard<std::mutex> l(cb_lock); |
228 | + if (cb) |
229 | + cb(CallMonitor::OnHook); |
230 | + } |
231 | + |
232 | +private: |
233 | + std::mutex cb_lock; |
234 | + std::function<void (CallMonitor::State)> cb; |
235 | + Tp::AccountManagerPtr mAccountManager; |
236 | + std::list<TelepathyCallMonitor*> mCallMonitors; |
237 | + |
238 | + void checkAndAddAccount(const Tp::AccountPtr& account) |
239 | + { |
240 | + Tp::ConnectionCapabilities caps = account->capabilities(); |
241 | + |
242 | + // anything call like, perhaps overkill? |
243 | + if (caps.audioCalls() || caps.videoCalls() || caps.videoCallsWithAudio() || caps.streamedMediaCalls()) { |
244 | + auto tcm = new TelepathyCallMonitor(account); |
245 | + connect(tcm, SIGNAL(offHook()), SLOT(offHook())); |
246 | + connect(tcm, SIGNAL(onHook()), SLOT(onHook())); |
247 | + mCallMonitors.push_back(tcm); |
248 | + } |
249 | + } |
250 | +}; |
251 | +} |
252 | + |
253 | +class CallMonitorPrivate |
254 | +{ |
255 | +public: |
256 | + CallMonitorPrivate() { |
257 | + try { |
258 | + std::thread([this]() { |
259 | + qt::core::world::build_and_run(0, nullptr, [this]() { |
260 | + qt::core::world::enter_with_task([this]() { |
261 | + mBridge = new TelepathyBridge(); |
262 | + }); |
263 | + }); |
264 | + }).detach(); |
265 | + } catch(const std::system_error& error) { |
266 | + std::cerr << "exception(std::system_error) in CallMonitor thread start" << error.what() << std::endl; |
267 | + } catch(...) { |
268 | + std::cerr << "exception(...) in CallMonitor thread start" << std::endl; |
269 | + } |
270 | + } |
271 | + |
272 | + ~CallMonitorPrivate() { |
273 | + qt::core::world::destroy(); |
274 | + } |
275 | + |
276 | + TelepathyBridge *mBridge; |
277 | +}; |
278 | + |
279 | + |
280 | +CallMonitor::CallMonitor(): |
281 | + d(new CallMonitorPrivate) |
282 | +{ |
283 | +} |
284 | + |
285 | +CallMonitor::~CallMonitor() |
286 | +{ |
287 | + delete d->mBridge; |
288 | + delete d; |
289 | +} |
290 | + |
291 | +void CallMonitor::on_change(const std::function<void(CallMonitor::State)>& func) |
292 | +{ |
293 | + d->mBridge->on_change(func); |
294 | +} |
295 | + |
296 | +#include "call_monitor.moc" |
297 | + |
298 | |
299 | === added file 'src/core/media/call-monitor/call_monitor.h' |
300 | --- src/core/media/call-monitor/call_monitor.h 1970-01-01 00:00:00 +0000 |
301 | +++ src/core/media/call-monitor/call_monitor.h 2014-11-04 23:38:46 +0000 |
302 | @@ -0,0 +1,41 @@ |
303 | +/* |
304 | + * Copyright (C) 2014 Canonical Ltd |
305 | + * |
306 | + * This program is free software: you can redistribute it and/or modify |
307 | + * it under the terms of the GNU Lesser General Public License version 3 as |
308 | + * published by the Free Software Foundation. |
309 | + * |
310 | + * This program is distributed in the hope that it will be useful, |
311 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
312 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
313 | + * GNU Lesser General Public License for more details. |
314 | + * |
315 | + * You should have received a copy of the GNU Lesser General Public License |
316 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
317 | + * |
318 | + * Author: Justin McPherson <justin.mcpherson@canonical.com> |
319 | + */ |
320 | + |
321 | + |
322 | +#ifndef CALLMONITOR_H |
323 | +#define CALLMONITOR_H |
324 | + |
325 | +#include <functional> |
326 | + |
327 | +class CallMonitorPrivate; |
328 | + |
329 | +class CallMonitor |
330 | +{ |
331 | +public: |
332 | + enum State { OffHook, OnHook }; |
333 | + |
334 | + CallMonitor(); |
335 | + ~CallMonitor(); |
336 | + |
337 | + void on_change(const std::function<void(CallMonitor::State)>& func); |
338 | + |
339 | +private: |
340 | + CallMonitorPrivate *d; |
341 | +}; |
342 | + |
343 | +#endif // CALLMONITOR_H |
344 | |
345 | === added file 'src/core/media/call-monitor/qtbridge.cpp' |
346 | --- src/core/media/call-monitor/qtbridge.cpp 1970-01-01 00:00:00 +0000 |
347 | +++ src/core/media/call-monitor/qtbridge.cpp 2014-11-04 23:38:46 +0000 |
348 | @@ -0,0 +1,186 @@ |
349 | +/* |
350 | + * Copyright © 2014 Canonical Ltd. |
351 | + * |
352 | + * This program is free software: you can redistribute it and/or modify it |
353 | + * under the terms of the GNU Lesser General Public License version 3, |
354 | + * as published by the Free Software Foundation. |
355 | + * |
356 | + * This program is distributed in the hope that it will be useful, |
357 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
358 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
359 | + * GNU Lesser General Public License for more details. |
360 | + * |
361 | + * You should have received a copy of the GNU Lesser General Public License |
362 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
363 | + * |
364 | + * Authored by: Jussi Pakkanen <jussi.pakkanen@canonical.com> |
365 | + * Thomas Voß <thomas.voss@canonical.com> |
366 | + */ |
367 | + |
368 | +#include "qtbridge.h" |
369 | + |
370 | +#include<QCoreApplication> |
371 | +#include<QNetworkAccessManager> |
372 | +#include<QNetworkRequest> |
373 | +#include<QNetworkReply> |
374 | +#include<QThread> |
375 | +#include<QDebug> |
376 | + |
377 | +#include <iostream> |
378 | + |
379 | +namespace |
380 | +{ |
381 | +QCoreApplication* app = nullptr; |
382 | +} |
383 | + |
384 | +namespace qt |
385 | +{ |
386 | +namespace core |
387 | +{ |
388 | +namespace world |
389 | +{ |
390 | +namespace detail |
391 | +{ |
392 | +QEvent::Type qt_core_world_task_event_type() |
393 | +{ |
394 | + static QEvent::Type event_type = static_cast<QEvent::Type>(QEvent::registerEventType()); |
395 | + return event_type; |
396 | +} |
397 | + |
398 | +class TaskEvent : public QEvent |
399 | +{ |
400 | +public: |
401 | + TaskEvent(const std::function<void()>& task) |
402 | + : QEvent(qt_core_world_task_event_type()), |
403 | + task(task) |
404 | + { |
405 | + } |
406 | + |
407 | + void run() |
408 | + { |
409 | + try |
410 | + { |
411 | + task(); |
412 | + promise.set_value(); |
413 | + } catch(...) |
414 | + { |
415 | + promise.set_exception(std::current_exception()); |
416 | + } |
417 | + } |
418 | + |
419 | + std::future<void> get_future() |
420 | + { |
421 | + return promise.get_future(); |
422 | + } |
423 | + |
424 | +private: |
425 | + std::function<void()> task; |
426 | + std::promise<void> promise; |
427 | +}; |
428 | + |
429 | +class TaskHandler : public QObject |
430 | +{ |
431 | + Q_OBJECT |
432 | + |
433 | +public: |
434 | + TaskHandler(QObject* parent) : QObject(parent) |
435 | + { |
436 | + } |
437 | + |
438 | + bool event(QEvent* e); |
439 | +}; |
440 | + |
441 | + |
442 | + |
443 | +void createCoreApplicationInstanceWithArgs(int argc, char** argv) |
444 | +{ |
445 | + app = new QCoreApplication(argc, argv); |
446 | +} |
447 | + |
448 | +void destroyCoreApplicationInstace() |
449 | +{ |
450 | + delete app; |
451 | +} |
452 | + |
453 | +QCoreApplication* coreApplicationInstance() |
454 | +{ |
455 | + return app; |
456 | +} |
457 | + |
458 | +TaskHandler* task_handler() |
459 | +{ |
460 | + static TaskHandler* instance = new TaskHandler(coreApplicationInstance()); |
461 | + return instance; |
462 | +} |
463 | + |
464 | +bool TaskHandler::event(QEvent *e) |
465 | +{ |
466 | + if (e->type() != qt_core_world_task_event_type()) |
467 | + return QObject::event(e); |
468 | + |
469 | + auto te = dynamic_cast<TaskEvent*>(e); |
470 | + if (te) |
471 | + { |
472 | + te->run(); |
473 | + return true; |
474 | + } |
475 | + |
476 | + return false; |
477 | +} |
478 | +} |
479 | + |
480 | +void build_and_run(int argc, char** argv, const std::function<void()>& ready) |
481 | +{ |
482 | + QThread::currentThread(); |
483 | + if (QCoreApplication::instance() != nullptr) |
484 | + throw std::runtime_error( |
485 | + "qt::core::world::build_and_run: " |
486 | + "There is already a QCoreApplication running."); |
487 | + |
488 | + detail::createCoreApplicationInstanceWithArgs(argc, argv); |
489 | + |
490 | + detail::task_handler()->moveToThread( |
491 | + detail::coreApplicationInstance()->thread()); |
492 | + |
493 | + // Signal to other worlds that we are good to go. |
494 | + ready(); |
495 | + |
496 | + detail::coreApplicationInstance()->exec(); |
497 | + |
498 | + // Someone has called quit and we clean up on the correct thread here. |
499 | + detail::destroyCoreApplicationInstace(); |
500 | +} |
501 | + |
502 | +void destroy() |
503 | +{ |
504 | + enter_with_task([]() |
505 | + { |
506 | + // We make sure that all tasks have completed before quitting the app. |
507 | + QEventLoopLocker locker; |
508 | + }).wait_for(std::chrono::seconds{1}); |
509 | +} |
510 | + |
511 | +std::future<void> enter_with_task(const std::function<void()>& task) |
512 | +{ |
513 | + QCoreApplication* instance = QCoreApplication::instance(); |
514 | + |
515 | + if (!instance) |
516 | + { |
517 | + throw std::runtime_error("Qt world has not been built before calling this function."); |
518 | + } |
519 | + |
520 | + detail::TaskEvent* te = new detail::TaskEvent(task); |
521 | + auto future = te->get_future(); |
522 | + |
523 | + // We hand over ownership of te here. The event is deleted later after it has |
524 | + // been processed by the event loop. |
525 | + instance->postEvent(detail::task_handler(), te); |
526 | + |
527 | + return future; |
528 | +} |
529 | + |
530 | +} |
531 | +} |
532 | +} |
533 | + |
534 | +#include "qtbridge.moc" |
535 | |
536 | === added file 'src/core/media/call-monitor/qtbridge.h' |
537 | --- src/core/media/call-monitor/qtbridge.h 1970-01-01 00:00:00 +0000 |
538 | +++ src/core/media/call-monitor/qtbridge.h 2014-11-04 23:38:46 +0000 |
539 | @@ -0,0 +1,87 @@ |
540 | +/* |
541 | + * Copyright © 2014 Canonical Ltd. |
542 | + * |
543 | + * This program is free software: you can redistribute it and/or modify it |
544 | + * under the terms of the GNU Lesser General Public License version 3, |
545 | + * as published by the Free Software Foundation. |
546 | + * |
547 | + * This program is distributed in the hope that it will be useful, |
548 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
549 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
550 | + * GNU Lesser General Public License for more details. |
551 | + * |
552 | + * You should have received a copy of the GNU Lesser General Public License |
553 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
554 | + * |
555 | + * Authored by: Jussi Pakkanen <jussi.pakkanen@canonical.com> |
556 | + * Thomas Voß <thomas.voss@canonical.com> |
557 | + */ |
558 | + |
559 | +#ifndef QT_CORE_WORLD_BRIDGE_H_ |
560 | +#define QT_CORE_WORLD_BRIDGE_H_ |
561 | + |
562 | +#include <QObject> |
563 | + |
564 | +#include <functional> |
565 | +#include <future> |
566 | +#include <iostream> |
567 | + |
568 | +namespace qt |
569 | +{ |
570 | +namespace core |
571 | +{ |
572 | +namespace world |
573 | +{ |
574 | +/** |
575 | + * @brief Sets up the Qt core world and executes its event loop. Blocks until destroy() is called. |
576 | + * @param argc Number of arguments in argv. |
577 | + * @param argv Array of command-line arguments. |
578 | + * @param ready Functor be called when the world has been setup and is about to be executed. |
579 | + * @throw std::runtime_error in case of errors. |
580 | + */ |
581 | +void build_and_run(int argc, char** argv, const std::function<void()>& ready); |
582 | + |
583 | +/** |
584 | + * @brief Destroys the Qt core world and quits its event loop. |
585 | + */ |
586 | +void destroy(); |
587 | + |
588 | +/** |
589 | + * @brief Enters the Qt core world and schedules the given task for execution. |
590 | + * @param task The task to be executed in the Qt core world. |
591 | + * @return A std::future that can be waited for to synchronize to the world's internal event loop. |
592 | + */ |
593 | +std::future<void> enter_with_task(const std::function<void()>& task); |
594 | + |
595 | + |
596 | +/** |
597 | + * @brief Enters the Qt core world and schedules the given task for execution. |
598 | + * @param task The task to be executed in the Qt core world. |
599 | + * @return A std::future that can be waited for to get hold of the result of the task. |
600 | + */ |
601 | +template<typename T> |
602 | +inline std::future<T> enter_with_task_and_expect_result(const std::function<T()>& task) |
603 | +{ |
604 | + std::shared_ptr<std::promise<T>> promise = std::make_shared<std::promise<T>>(); |
605 | + std::future<T> future = promise->get_future(); |
606 | + |
607 | + auto t = [promise, task]() |
608 | + { |
609 | + try |
610 | + { |
611 | + promise->set_value(task()); |
612 | + } catch(...) |
613 | + { |
614 | + promise->set_exception(std::current_exception()); |
615 | + } |
616 | + }; |
617 | + |
618 | + enter_with_task(t); |
619 | + |
620 | + return future; |
621 | +} |
622 | +} |
623 | +} |
624 | +} |
625 | + |
626 | +#endif // QT_CORE_WORLD_BRIDGE_H_ |
627 | |
628 | === modified file 'src/core/media/service_implementation.cpp' |
629 | --- src/core/media/service_implementation.cpp 2014-10-15 14:20:03 +0000 |
630 | +++ src/core/media/service_implementation.cpp 2014-11-04 23:38:46 +0000 |
631 | @@ -20,6 +20,7 @@ |
632 | #include "service_implementation.h" |
633 | |
634 | #include "indicator_power_service.h" |
635 | +#include "call-monitor/call_monitor.h" |
636 | #include "player_configuration.h" |
637 | #include "player_implementation.h" |
638 | |
639 | @@ -43,7 +44,8 @@ |
640 | Private() |
641 | : resume_key(std::numeric_limits<std::uint32_t>::max()), |
642 | keep_alive(io_service), |
643 | - disp_cookie(0) |
644 | + disp_cookie(0), |
645 | + call_monitor(new CallMonitor) |
646 | { |
647 | bus = std::shared_ptr<dbus::Bus>(new dbus::Bus(core::dbus::WellKnownBus::session)); |
648 | bus->install_executor(dbus::asio::make_executor(bus, io_service)); |
649 | @@ -127,6 +129,8 @@ |
650 | int disp_cookie; |
651 | std::shared_ptr<dbus::Object> uscreen_session; |
652 | MediaRecorderObserver *observer; |
653 | + std::unique_ptr<CallMonitor> call_monitor; |
654 | + std::list<media::Player::PlayerKey> paused_sessions; |
655 | }; |
656 | |
657 | media::ServiceImplementation::ServiceImplementation() : d(new Private()) |
658 | @@ -148,6 +152,17 @@ |
659 | if (!notifying) |
660 | resume_multimedia_session(); |
661 | }); |
662 | + |
663 | + d->call_monitor->on_change([this](CallMonitor::State state) { |
664 | + switch (state) { |
665 | + case CallMonitor::OffHook: |
666 | + pause_all_multimedia_sessions(); |
667 | + break; |
668 | + case CallMonitor::OnHook: |
669 | + resume_paused_multimedia_sessions(); |
670 | + break; |
671 | + } |
672 | + }); |
673 | } |
674 | |
675 | media::ServiceImplementation::~ServiceImplementation() |
676 | @@ -216,13 +231,21 @@ |
677 | if (player->playback_status() == Player::playing |
678 | && player->audio_stream_role() == media::Player::multimedia) |
679 | { |
680 | - d->resume_key = key; |
681 | - cout << "Will resume playback of Player with key: " << d->resume_key << endl; |
682 | + d->paused_sessions.push_back(key); |
683 | player->pause(); |
684 | } |
685 | }); |
686 | } |
687 | |
688 | +void media::ServiceImplementation::resume_paused_multimedia_sessions() |
689 | +{ |
690 | + std::for_each(d->paused_sessions.begin(), d->paused_sessions.end(), [this](const media::Player::PlayerKey& key) { |
691 | + player_for_key(key)->play(); |
692 | + }); |
693 | + |
694 | + d->paused_sessions.clear(); |
695 | +} |
696 | + |
697 | void media::ServiceImplementation::resume_multimedia_session() |
698 | { |
699 | if (not has_player_for_key(d->resume_key)) |
700 | |
701 | === modified file 'src/core/media/service_implementation.h' |
702 | --- src/core/media/service_implementation.h 2014-09-09 14:48:51 +0000 |
703 | +++ src/core/media/service_implementation.h 2014-11-04 23:38:46 +0000 |
704 | @@ -42,6 +42,7 @@ |
705 | |
706 | private: |
707 | void pause_all_multimedia_sessions(); |
708 | + void resume_paused_multimedia_sessions(); |
709 | void resume_multimedia_session(); |
710 | |
711 | struct Private; |
712 | |
713 | === modified file 'tests/unit-tests/CMakeLists.txt' |
714 | --- tests/unit-tests/CMakeLists.txt 2014-10-14 16:21:47 +0000 |
715 | +++ tests/unit-tests/CMakeLists.txt 2014-11-04 23:38:46 +0000 |
716 | @@ -25,6 +25,7 @@ |
717 | |
718 | media-hub-common |
719 | media-hub-client |
720 | + call-monitor |
721 | media-hub-test-framework |
722 | |
723 | ${CMAKE_THREAD_LIBS_INIT} |
FAILED: Continuous integration, rev:75 jenkins. qa.ubuntu. com/job/ media-hub- ci/153/ jenkins. qa.ubuntu. com/job/ media-hub- utopic- amd64-ci/ 95/console jenkins. qa.ubuntu. com/job/ media-hub- utopic- armhf-ci/ 94/console jenkins. qa.ubuntu. com/job/ media-hub- utopic- i386-ci/ 93/console
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/media- hub-ci/ 153/rebuild
http://