Merge lp:~jhodapp/media-hub/fix-1421620-rtm into lp:media-hub
- fix-1421620-rtm
- Merge into trunk
Proposed by
Jim Hodapp
Status: | Superseded | ||||
---|---|---|---|---|---|
Proposed branch: | lp:~jhodapp/media-hub/fix-1421620-rtm | ||||
Merge into: | lp:media-hub | ||||
Diff against target: |
1068 lines (+827/-3) (has conflicts) 14 files modified
debian/changelog (+99/-0) debian/control (+7/-0) include/core/media/player.h (+13/-0) src/core/media/call-monitor/CMakeLists.txt (+23/-0) src/core/media/call-monitor/call_monitor.cpp (+222/-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/codec.h (+43/-0) src/core/media/gstreamer/playbin.h (+3/-0) src/core/media/service_implementation.cpp (+85/-3) src/core/media/service_implementation.h (+5/-0) tests/unit-tests/CMakeLists.txt (+4/-0) tests/unit-tests/test-gstreamer-engine.cpp (+9/-0) Text conflict in debian/changelog Text conflict in debian/control Text conflict in include/core/media/player.h Conflict adding file src/core/media/call-monitor. Moved existing file to src/core/media/call-monitor.moved. Text conflict in src/core/media/codec.h Text conflict in src/core/media/gstreamer/playbin.h Text conflict in src/core/media/service_implementation.cpp Text conflict in src/core/media/service_implementation.h Text conflict in tests/unit-tests/CMakeLists.txt Text conflict in tests/unit-tests/test-gstreamer-engine.cpp |
||||
To merge this branch: | bzr merge lp:~jhodapp/media-hub/fix-1421620-rtm | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ubuntu Phablet Team | Pending | ||
Review via email:
|
Commit message
Fix the bug which caused music playback to start playing again after a phonecall hung up after being auto-paused by disconnecting a headphone jack
Description of the change
Fix the bug which caused music playback to start playing again after a phonecall hung up after being auto-paused by disconnecting a headphone jack
To post a comment you must log in.
Unmerged revisions
- 88. By Jim Hodapp
-
Fix the bug which caused music playback to start playing again after a phonecall hung up after being auto-paused by disconnecting a headphone jack.
- 87. By Ricardo Salveti
-
releasing package media-hub version 2.0.0+15.
04.20150120~ rtm-0ubuntu1
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'CMakeLists.txt' |
2 | === modified file 'debian/changelog' |
3 | --- debian/changelog 2015-03-12 03:06:40 +0000 |
4 | +++ debian/changelog 2015-03-31 21:19:24 +0000 |
5 | @@ -1,3 +1,4 @@ |
6 | +<<<<<<< TREE |
7 | media-hub (2.0.0+15.04.20150303-0ubuntu2) vivid; urgency=medium |
8 | |
9 | * debian/control: |
10 | @@ -122,6 +123,104 @@ |
11 | |
12 | -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Tue, 04 Nov 2014 06:10:54 +0000 |
13 | |
14 | +======= |
15 | +media-hub (2.0.0+15.04.20150120~rtm-0ubuntu1) 14.09; urgency=low |
16 | + |
17 | + [ Jim Hodapp ] |
18 | + * Error reporting all the way up to the app level from the playbin |
19 | + pipeline (LP: #1413824). |
20 | + |
21 | + [ Ubuntu daily release ] |
22 | + * New rebuild forced |
23 | + |
24 | + -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Tue, 20 Jan 2015 01:21:47 +0000 |
25 | + |
26 | +media-hub (2.0.0+15.04.20150116-0ubuntu1) vivid; urgency=low |
27 | + |
28 | + [ Jim Hodapp ] |
29 | + * Don't auto-resume playback of videos after a phone call ends. (LP: |
30 | + #1411273) |
31 | + |
32 | + -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Fri, 16 Jan 2015 18:17:56 +0000 |
33 | + |
34 | +media-hub (2.0.0+15.04.20150112.2-0ubuntu1) vivid; urgency=low |
35 | + |
36 | + [ Ubuntu daily release ] |
37 | + * New rebuild forced |
38 | + |
39 | + [ Ricardo Salveti de Araujo ] |
40 | + * service_implementation: adding debug for call started/ended signals. |
41 | + Make sure account and connection are available when setting up |
42 | + account manager (patch from Gustavo Boiko). call_monitor: don't |
43 | + check caps when hooking up on/off signals, until bug 1409125 is |
44 | + fixed. Enable parallel building . (LP: #1409125) |
45 | + |
46 | + -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Mon, 12 Jan 2015 21:38:39 +0000 |
47 | + |
48 | +media-hub (2.0.0+15.04.20150108-0ubuntu1) vivid; urgency=low |
49 | + |
50 | + [ Jim Hodapp ] |
51 | + * Pause playback when recording begins. (LP: #1398047) |
52 | + |
53 | + [ Ricardo Salveti de Araujo ] |
54 | + * call_monitor.cpp: waiting for bridge to be up, and also protecting |
55 | + the on_change call (LP: #1408137) |
56 | + |
57 | + -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Thu, 08 Jan 2015 12:58:01 +0000 |
58 | + |
59 | +media-hub (2.0.0+15.04.20141120.1-0ubuntu1) vivid; urgency=low |
60 | + |
61 | + [ Jim Hodapp ] |
62 | + * Pause playback when a headphone is unplugged or an A2DP device is |
63 | + unpaired (LP: #1368300) |
64 | + |
65 | + [ Ricardo Mendoza ] |
66 | + * Pause playback when a headphone is unplugged or an A2DP device is |
67 | + unpaired (LP: #1368300) |
68 | + |
69 | + -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Thu, 20 Nov 2014 18:33:08 +0000 |
70 | + |
71 | +media-hub (2.0.0+15.04.20141111-0ubuntu1) vivid; urgency=low |
72 | + |
73 | + [ Ubuntu daily release ] |
74 | + * New rebuild forced |
75 | + |
76 | + [ Justin McPherson ] |
77 | + * #1239432 Music fails to pause on incoming/outgoing calls (LP: |
78 | + #1239432) |
79 | + |
80 | + -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Tue, 11 Nov 2014 20:18:50 +0000 |
81 | + |
82 | +media-hub (2.0.0+15.04.20141110.1-0ubuntu1) vivid; urgency=low |
83 | + |
84 | + [ thomas-voss ] |
85 | + * Bump build dependency on dbus-cpp to pull in exception safe dtor. |
86 | + (LP: #1390618) |
87 | + |
88 | + -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Mon, 10 Nov 2014 11:53:11 +0000 |
89 | + |
90 | +media-hub (2.0.0+15.04.20141105.1-0ubuntu1) vivid; urgency=low |
91 | + |
92 | + [ Ricardo Mendoza ] |
93 | + * Use new hybris interface to correctly register for client deaths. |
94 | + (LP: #1380848) |
95 | + |
96 | + -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 05 Nov 2014 20:41:14 +0000 |
97 | + |
98 | +media-hub (2.0.0+15.04.20141105-0ubuntu1) vivid; urgency=low |
99 | + |
100 | + [ thomas-voss ] |
101 | + * Disconnect signal translation layer on destruction. (LP: #1386803) |
102 | + |
103 | + -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 05 Nov 2014 08:24:08 +0000 |
104 | + |
105 | +media-hub (2.0.0+15.04.20141104-0ubuntu1) vivid; urgency=low |
106 | + |
107 | + * New rebuild forced |
108 | + |
109 | + -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Tue, 04 Nov 2014 06:10:54 +0000 |
110 | + |
111 | +>>>>>>> MERGE-SOURCE |
112 | media-hub (2.0.0+14.10.20141030~rtm-0ubuntu1) 14.09; urgency=low |
113 | |
114 | [ thomas-voss ] |
115 | |
116 | === modified file 'debian/control' |
117 | --- debian/control 2015-03-12 03:06:29 +0000 |
118 | +++ debian/control 2015-03-31 21:19:24 +0000 |
119 | @@ -26,10 +26,17 @@ |
120 | libproperties-cpp-dev, |
121 | libgstreamer1.0-dev, |
122 | pkg-config, |
123 | +<<<<<<< TREE |
124 | libpulse-dev, |
125 | qtbase5-dev, |
126 | libtelepathy-qt5-dev, |
127 | Standards-Version: 3.9.6 |
128 | +======= |
129 | + libpulse-dev, |
130 | + qtbase5-dev, |
131 | + libtelepathy-qt5-dev, |
132 | +Standards-Version: 3.9.5 |
133 | +>>>>>>> MERGE-SOURCE |
134 | Homepage: https://launchpad.net/media-hub |
135 | # If you aren't a member of ~phablet-team but need to upload packaging changes, |
136 | # just go ahead. ~phablet-team will notice and sync up the code again. |
137 | |
138 | === modified file 'include/core/media/player.h' |
139 | --- include/core/media/player.h 2015-01-19 21:48:01 +0000 |
140 | +++ include/core/media/player.h 2015-03-31 21:19:24 +0000 |
141 | @@ -95,6 +95,7 @@ |
142 | rotate270 |
143 | }; |
144 | |
145 | +<<<<<<< TREE |
146 | enum Lifetime |
147 | { |
148 | normal, |
149 | @@ -111,6 +112,18 @@ |
150 | service_missing_error |
151 | }; |
152 | |
153 | +======= |
154 | + enum Error |
155 | + { |
156 | + no_error, |
157 | + resource_error, |
158 | + format_error, |
159 | + network_error, |
160 | + access_denied_error, |
161 | + service_missing_error |
162 | + }; |
163 | + |
164 | +>>>>>>> MERGE-SOURCE |
165 | Player(const Player&) = delete; |
166 | virtual ~Player(); |
167 | |
168 | |
169 | === added directory 'src/core/media/call-monitor' |
170 | === renamed directory 'src/core/media/call-monitor' => 'src/core/media/call-monitor.moved' |
171 | === added file 'src/core/media/call-monitor/CMakeLists.txt' |
172 | --- src/core/media/call-monitor/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
173 | +++ src/core/media/call-monitor/CMakeLists.txt 2015-03-31 21:19:24 +0000 |
174 | @@ -0,0 +1,23 @@ |
175 | +SET (CMAKE_INCLUDE_CURRENT_DIR ON) |
176 | +SET (CMAKE_AUTOMOC ON) |
177 | + |
178 | +find_package(Qt5Core REQUIRED) |
179 | +find_package(PkgConfig REQUIRED) |
180 | +pkg_check_modules(TP_QT5 REQUIRED TelepathyQt5) |
181 | + |
182 | +#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -pedantic -Wextra -fPIC -pthread") |
183 | + |
184 | +include_directories( |
185 | + ${TP_QT5_INCLUDE_DIRS} |
186 | +) |
187 | + |
188 | +add_library(call-monitor STATIC |
189 | + call_monitor.cpp |
190 | + qtbridge.cpp |
191 | +) |
192 | + |
193 | +target_link_libraries(call-monitor |
194 | + ${TP_QT5_LIBRARIES} |
195 | +) |
196 | + |
197 | +qt5_use_modules(call-monitor Core DBus) |
198 | |
199 | === added file 'src/core/media/call-monitor/call_monitor.cpp' |
200 | --- src/core/media/call-monitor/call_monitor.cpp 1970-01-01 00:00:00 +0000 |
201 | +++ src/core/media/call-monitor/call_monitor.cpp 2015-03-31 21:19:24 +0000 |
202 | @@ -0,0 +1,222 @@ |
203 | +/* |
204 | + * Copyright (C) 2014 Canonical Ltd |
205 | + * |
206 | + * This program is free software: you can redistribute it and/or modify |
207 | + * it under the terms of the GNU Lesser General Public License version 3 as |
208 | + * published by the Free Software Foundation. |
209 | + * |
210 | + * This program is distributed in the hope that it will be useful, |
211 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
212 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
213 | + * GNU Lesser General Public License for more details. |
214 | + * |
215 | + * You should have received a copy of the GNU Lesser General Public License |
216 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
217 | + * |
218 | + * Author: Justin McPherson <justin.mcpherson@canonical.com> |
219 | + */ |
220 | + |
221 | + |
222 | +#include "call_monitor.h" |
223 | + |
224 | +#include "qtbridge.h" |
225 | +#include <TelepathyQt/AccountManager> |
226 | +#include <TelepathyQt/SimpleCallObserver> |
227 | +#include <TelepathyQt/PendingOperation> |
228 | +#include <TelepathyQt/PendingReady> |
229 | +#include <TelepathyQt/PendingAccount> |
230 | + |
231 | +#include <list> |
232 | +#include <mutex> |
233 | + |
234 | + |
235 | +namespace { |
236 | +class TelepathyCallMonitor : public QObject |
237 | +{ |
238 | + Q_OBJECT |
239 | +public: |
240 | + TelepathyCallMonitor(const Tp::AccountPtr& account): |
241 | + mAccount(account), |
242 | + mCallObserver(Tp::SimpleCallObserver::create(mAccount)) { |
243 | + connect(mCallObserver.data(), SIGNAL(callStarted(Tp::CallChannelPtr)), SIGNAL(offHook())); |
244 | + connect(mCallObserver.data(), SIGNAL(callEnded(Tp::CallChannelPtr,QString,QString)), SIGNAL(onHook())); |
245 | + connect(mCallObserver.data(), SIGNAL(streamedMediaCallStarted(Tp::StreamedMediaChannelPtr)), SIGNAL(offHook())); |
246 | + connect(mCallObserver.data(), SIGNAL(streamedMediaCallEnded(Tp::StreamedMediaChannelPtr,QString,QString)), SIGNAL(onHook())); |
247 | + } |
248 | + |
249 | +Q_SIGNALS: |
250 | + void offHook(); |
251 | + void onHook(); |
252 | + |
253 | +private: |
254 | + Tp::AccountPtr mAccount; |
255 | + Tp::SimpleCallObserverPtr mCallObserver; |
256 | +}; |
257 | + |
258 | + |
259 | +class TelepathyBridge : public QObject |
260 | +{ |
261 | + Q_OBJECT |
262 | +public: |
263 | + TelepathyBridge(): |
264 | + QObject(0) { |
265 | + Tp::registerTypes(); |
266 | + |
267 | + QTimer::singleShot(0, this, SLOT(accountManagerSetup())); |
268 | + } |
269 | + |
270 | + ~TelepathyBridge() { |
271 | + for (std::list<TelepathyCallMonitor*>::iterator it = mCallMonitors.begin(); |
272 | + it != mCallMonitors.end(); |
273 | + ++it) { |
274 | + delete *it; |
275 | + } |
276 | + } |
277 | + |
278 | + void on_change(const std::function<void(CallMonitor::State)>& func) { |
279 | + std::lock_guard<std::mutex> l(cb_lock); |
280 | + cb = func; |
281 | + } |
282 | + |
283 | +private Q_SLOTS: |
284 | + void accountManagerSetup() { |
285 | + mAccountManager = Tp::AccountManager::create(Tp::AccountFactory::create(QDBusConnection::sessionBus(), |
286 | + Tp::Account::FeatureCore), |
287 | + Tp::ConnectionFactory::create(QDBusConnection::sessionBus(), |
288 | + Tp::Connection::FeatureCore)); |
289 | + connect(mAccountManager->becomeReady(), |
290 | + SIGNAL(finished(Tp::PendingOperation*)), |
291 | + SLOT(accountManagerReady(Tp::PendingOperation*))); |
292 | + } |
293 | + |
294 | + void accountManagerReady(Tp::PendingOperation* operation) { |
295 | + if (operation->isError()) { |
296 | + std::cerr << "TelepathyBridge: Operation failed (accountManagerReady)" << std::endl; |
297 | + QTimer::singleShot(1000, this, SLOT(accountManagerSetup())); // again |
298 | + return; |
299 | + } |
300 | + |
301 | + Q_FOREACH(const Tp::AccountPtr& account, mAccountManager->allAccounts()) { |
302 | + connect(account->becomeReady(Tp::Account::FeatureCapabilities), |
303 | + SIGNAL(finished(Tp::PendingOperation*)), |
304 | + SLOT(accountReady(Tp::PendingOperation*))); |
305 | + } |
306 | + |
307 | + connect(mAccountManager.data(), SIGNAL(newAccount(Tp::AccountPtr)), SLOT(newAccount(Tp::AccountPtr))); |
308 | + } |
309 | + |
310 | + void newAccount(const Tp::AccountPtr& account) |
311 | + { |
312 | + connect(account->becomeReady(Tp::Account::FeatureCapabilities), |
313 | + SIGNAL(finished(Tp::PendingOperation*)), |
314 | + SLOT(accountReady(Tp::PendingOperation*))); |
315 | + } |
316 | + |
317 | + void accountReady(Tp::PendingOperation* operation) { |
318 | + if (operation->isError()) { |
319 | + std::cerr << "TelepathyAccount: Operation failed (accountReady)" << std::endl; |
320 | + return; |
321 | + } |
322 | + |
323 | + Tp::PendingReady* pendingReady = qobject_cast<Tp::PendingReady*>(operation); |
324 | + if (pendingReady == 0) { |
325 | + std::cerr << "Rejecting account because could not understand ready status" << std::endl; |
326 | + return; |
327 | + } |
328 | + |
329 | + checkAndAddAccount(Tp::AccountPtr::qObjectCast(pendingReady->proxy())); |
330 | + } |
331 | + |
332 | + void offHook() |
333 | + { |
334 | + std::lock_guard<std::mutex> l(cb_lock); |
335 | + if (cb) |
336 | + cb(CallMonitor::OffHook); |
337 | + } |
338 | + |
339 | + void onHook() |
340 | + { |
341 | + std::lock_guard<std::mutex> l(cb_lock); |
342 | + if (cb) |
343 | + cb(CallMonitor::OnHook); |
344 | + } |
345 | + |
346 | +private: |
347 | + std::mutex cb_lock; |
348 | + std::function<void (CallMonitor::State)> cb; |
349 | + Tp::AccountManagerPtr mAccountManager; |
350 | + std::list<TelepathyCallMonitor*> mCallMonitors; |
351 | + |
352 | + void checkAndAddAccount(const Tp::AccountPtr& account) |
353 | + { |
354 | + Tp::ConnectionCapabilities caps = account->capabilities(); |
355 | + // TODO: Later on we will need to filter for the right capabilities, and also allow dynamic account detection |
356 | + // Don't check caps for now as a workaround for https://bugs.launchpad.net/ubuntu/+source/media-hub/+bug/1409125 |
357 | + // at least until we are able to find out the root cause of it (check rev 107 for the caps check) |
358 | + auto tcm = new TelepathyCallMonitor(account); |
359 | + connect(tcm, SIGNAL(offHook()), SLOT(offHook())); |
360 | + connect(tcm, SIGNAL(onHook()), SLOT(onHook())); |
361 | + mCallMonitors.push_back(tcm); |
362 | + } |
363 | +}; |
364 | +} |
365 | + |
366 | +class CallMonitorPrivate |
367 | +{ |
368 | +public: |
369 | + CallMonitorPrivate() { |
370 | + mBridge = nullptr; |
371 | + try { |
372 | + std::thread([this]() { |
373 | + qt::core::world::build_and_run(0, nullptr, [this]() { |
374 | + qt::core::world::enter_with_task([this]() { |
375 | + std::cout << "CallMonitor: Creating TelepathyBridge" << std::endl; |
376 | + mBridge = new TelepathyBridge(); |
377 | + cv.notify_all(); |
378 | + }); |
379 | + }); |
380 | + }).detach(); |
381 | + } catch(const std::system_error& error) { |
382 | + std::cerr << "exception(std::system_error) in CallMonitor thread start" << error.what() << std::endl; |
383 | + } catch(...) { |
384 | + std::cerr << "exception(...) in CallMonitor thread start" << std::endl; |
385 | + } |
386 | + // Wait until telepathy bridge is set, so we can hook up the change signals |
387 | + std::unique_lock<std::mutex> lck(mtx); |
388 | + cv.wait_for(lck, std::chrono::seconds(3)); |
389 | + } |
390 | + |
391 | + ~CallMonitorPrivate() { |
392 | + qt::core::world::destroy(); |
393 | + } |
394 | + |
395 | + TelepathyBridge *mBridge; |
396 | + |
397 | +private: |
398 | + std::mutex mtx; |
399 | + std::condition_variable cv; |
400 | +}; |
401 | + |
402 | + |
403 | +CallMonitor::CallMonitor(): |
404 | + d(new CallMonitorPrivate) |
405 | +{ |
406 | +} |
407 | + |
408 | +CallMonitor::~CallMonitor() |
409 | +{ |
410 | + delete d->mBridge; |
411 | + delete d; |
412 | +} |
413 | + |
414 | +void CallMonitor::on_change(const std::function<void(CallMonitor::State)>& func) |
415 | +{ |
416 | + if (d->mBridge != nullptr) { |
417 | + std::cout << "CallMonitor: Setting up callback for TelepathyBridge on_change" << std::endl; |
418 | + d->mBridge->on_change(func); |
419 | + } else |
420 | + std::cerr << "TelepathyBridge: Failed to hook on_change signal, bridge not yet set" << std::endl; |
421 | +} |
422 | + |
423 | +#include "call_monitor.moc" |
424 | + |
425 | |
426 | === added file 'src/core/media/call-monitor/call_monitor.h' |
427 | --- src/core/media/call-monitor/call_monitor.h 1970-01-01 00:00:00 +0000 |
428 | +++ src/core/media/call-monitor/call_monitor.h 2015-03-31 21:19:24 +0000 |
429 | @@ -0,0 +1,41 @@ |
430 | +/* |
431 | + * Copyright (C) 2014 Canonical Ltd |
432 | + * |
433 | + * This program is free software: you can redistribute it and/or modify |
434 | + * it under the terms of the GNU Lesser General Public License version 3 as |
435 | + * published by the Free Software Foundation. |
436 | + * |
437 | + * This program is distributed in the hope that it will be useful, |
438 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
439 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
440 | + * GNU Lesser General Public License for more details. |
441 | + * |
442 | + * You should have received a copy of the GNU Lesser General Public License |
443 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
444 | + * |
445 | + * Author: Justin McPherson <justin.mcpherson@canonical.com> |
446 | + */ |
447 | + |
448 | + |
449 | +#ifndef CALLMONITOR_H |
450 | +#define CALLMONITOR_H |
451 | + |
452 | +#include <functional> |
453 | + |
454 | +class CallMonitorPrivate; |
455 | + |
456 | +class CallMonitor |
457 | +{ |
458 | +public: |
459 | + enum State { OffHook, OnHook }; |
460 | + |
461 | + CallMonitor(); |
462 | + ~CallMonitor(); |
463 | + |
464 | + void on_change(const std::function<void(CallMonitor::State)>& func); |
465 | + |
466 | +private: |
467 | + CallMonitorPrivate *d; |
468 | +}; |
469 | + |
470 | +#endif // CALLMONITOR_H |
471 | |
472 | === added file 'src/core/media/call-monitor/qtbridge.cpp' |
473 | --- src/core/media/call-monitor/qtbridge.cpp 1970-01-01 00:00:00 +0000 |
474 | +++ src/core/media/call-monitor/qtbridge.cpp 2015-03-31 21:19:24 +0000 |
475 | @@ -0,0 +1,186 @@ |
476 | +/* |
477 | + * Copyright © 2014 Canonical Ltd. |
478 | + * |
479 | + * This program is free software: you can redistribute it and/or modify it |
480 | + * under the terms of the GNU Lesser General Public License version 3, |
481 | + * as published by the Free Software Foundation. |
482 | + * |
483 | + * This program is distributed in the hope that it will be useful, |
484 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
485 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
486 | + * GNU Lesser General Public License for more details. |
487 | + * |
488 | + * You should have received a copy of the GNU Lesser General Public License |
489 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
490 | + * |
491 | + * Authored by: Jussi Pakkanen <jussi.pakkanen@canonical.com> |
492 | + * Thomas Voß <thomas.voss@canonical.com> |
493 | + */ |
494 | + |
495 | +#include "qtbridge.h" |
496 | + |
497 | +#include<QCoreApplication> |
498 | +#include<QNetworkAccessManager> |
499 | +#include<QNetworkRequest> |
500 | +#include<QNetworkReply> |
501 | +#include<QThread> |
502 | +#include<QDebug> |
503 | + |
504 | +#include <iostream> |
505 | + |
506 | +namespace |
507 | +{ |
508 | +QCoreApplication* app = nullptr; |
509 | +} |
510 | + |
511 | +namespace qt |
512 | +{ |
513 | +namespace core |
514 | +{ |
515 | +namespace world |
516 | +{ |
517 | +namespace detail |
518 | +{ |
519 | +QEvent::Type qt_core_world_task_event_type() |
520 | +{ |
521 | + static QEvent::Type event_type = static_cast<QEvent::Type>(QEvent::registerEventType()); |
522 | + return event_type; |
523 | +} |
524 | + |
525 | +class TaskEvent : public QEvent |
526 | +{ |
527 | +public: |
528 | + TaskEvent(const std::function<void()>& task) |
529 | + : QEvent(qt_core_world_task_event_type()), |
530 | + task(task) |
531 | + { |
532 | + } |
533 | + |
534 | + void run() |
535 | + { |
536 | + try |
537 | + { |
538 | + task(); |
539 | + promise.set_value(); |
540 | + } catch(...) |
541 | + { |
542 | + promise.set_exception(std::current_exception()); |
543 | + } |
544 | + } |
545 | + |
546 | + std::future<void> get_future() |
547 | + { |
548 | + return promise.get_future(); |
549 | + } |
550 | + |
551 | +private: |
552 | + std::function<void()> task; |
553 | + std::promise<void> promise; |
554 | +}; |
555 | + |
556 | +class TaskHandler : public QObject |
557 | +{ |
558 | + Q_OBJECT |
559 | + |
560 | +public: |
561 | + TaskHandler(QObject* parent) : QObject(parent) |
562 | + { |
563 | + } |
564 | + |
565 | + bool event(QEvent* e); |
566 | +}; |
567 | + |
568 | + |
569 | + |
570 | +void createCoreApplicationInstanceWithArgs(int argc, char** argv) |
571 | +{ |
572 | + app = new QCoreApplication(argc, argv); |
573 | +} |
574 | + |
575 | +void destroyCoreApplicationInstace() |
576 | +{ |
577 | + delete app; |
578 | +} |
579 | + |
580 | +QCoreApplication* coreApplicationInstance() |
581 | +{ |
582 | + return app; |
583 | +} |
584 | + |
585 | +TaskHandler* task_handler() |
586 | +{ |
587 | + static TaskHandler* instance = new TaskHandler(coreApplicationInstance()); |
588 | + return instance; |
589 | +} |
590 | + |
591 | +bool TaskHandler::event(QEvent *e) |
592 | +{ |
593 | + if (e->type() != qt_core_world_task_event_type()) |
594 | + return QObject::event(e); |
595 | + |
596 | + auto te = dynamic_cast<TaskEvent*>(e); |
597 | + if (te) |
598 | + { |
599 | + te->run(); |
600 | + return true; |
601 | + } |
602 | + |
603 | + return false; |
604 | +} |
605 | +} |
606 | + |
607 | +void build_and_run(int argc, char** argv, const std::function<void()>& ready) |
608 | +{ |
609 | + QThread::currentThread(); |
610 | + if (QCoreApplication::instance() != nullptr) |
611 | + throw std::runtime_error( |
612 | + "qt::core::world::build_and_run: " |
613 | + "There is already a QCoreApplication running."); |
614 | + |
615 | + detail::createCoreApplicationInstanceWithArgs(argc, argv); |
616 | + |
617 | + detail::task_handler()->moveToThread( |
618 | + detail::coreApplicationInstance()->thread()); |
619 | + |
620 | + // Signal to other worlds that we are good to go. |
621 | + ready(); |
622 | + |
623 | + detail::coreApplicationInstance()->exec(); |
624 | + |
625 | + // Someone has called quit and we clean up on the correct thread here. |
626 | + detail::destroyCoreApplicationInstace(); |
627 | +} |
628 | + |
629 | +void destroy() |
630 | +{ |
631 | + enter_with_task([]() |
632 | + { |
633 | + // We make sure that all tasks have completed before quitting the app. |
634 | + QEventLoopLocker locker; |
635 | + }).wait_for(std::chrono::seconds{1}); |
636 | +} |
637 | + |
638 | +std::future<void> enter_with_task(const std::function<void()>& task) |
639 | +{ |
640 | + QCoreApplication* instance = QCoreApplication::instance(); |
641 | + |
642 | + if (!instance) |
643 | + { |
644 | + throw std::runtime_error("Qt world has not been built before calling this function."); |
645 | + } |
646 | + |
647 | + detail::TaskEvent* te = new detail::TaskEvent(task); |
648 | + auto future = te->get_future(); |
649 | + |
650 | + // We hand over ownership of te here. The event is deleted later after it has |
651 | + // been processed by the event loop. |
652 | + instance->postEvent(detail::task_handler(), te); |
653 | + |
654 | + return future; |
655 | +} |
656 | + |
657 | +} |
658 | +} |
659 | +} |
660 | + |
661 | +#include "qtbridge.moc" |
662 | |
663 | === added file 'src/core/media/call-monitor/qtbridge.h' |
664 | --- src/core/media/call-monitor/qtbridge.h 1970-01-01 00:00:00 +0000 |
665 | +++ src/core/media/call-monitor/qtbridge.h 2015-03-31 21:19:24 +0000 |
666 | @@ -0,0 +1,87 @@ |
667 | +/* |
668 | + * Copyright © 2014 Canonical Ltd. |
669 | + * |
670 | + * This program is free software: you can redistribute it and/or modify it |
671 | + * under the terms of the GNU Lesser General Public License version 3, |
672 | + * as published by the Free Software Foundation. |
673 | + * |
674 | + * This program is distributed in the hope that it will be useful, |
675 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
676 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
677 | + * GNU Lesser General Public License for more details. |
678 | + * |
679 | + * You should have received a copy of the GNU Lesser General Public License |
680 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
681 | + * |
682 | + * Authored by: Jussi Pakkanen <jussi.pakkanen@canonical.com> |
683 | + * Thomas Voß <thomas.voss@canonical.com> |
684 | + */ |
685 | + |
686 | +#ifndef QT_CORE_WORLD_BRIDGE_H_ |
687 | +#define QT_CORE_WORLD_BRIDGE_H_ |
688 | + |
689 | +#include <QObject> |
690 | + |
691 | +#include <functional> |
692 | +#include <future> |
693 | +#include <iostream> |
694 | + |
695 | +namespace qt |
696 | +{ |
697 | +namespace core |
698 | +{ |
699 | +namespace world |
700 | +{ |
701 | +/** |
702 | + * @brief Sets up the Qt core world and executes its event loop. Blocks until destroy() is called. |
703 | + * @param argc Number of arguments in argv. |
704 | + * @param argv Array of command-line arguments. |
705 | + * @param ready Functor be called when the world has been setup and is about to be executed. |
706 | + * @throw std::runtime_error in case of errors. |
707 | + */ |
708 | +void build_and_run(int argc, char** argv, const std::function<void()>& ready); |
709 | + |
710 | +/** |
711 | + * @brief Destroys the Qt core world and quits its event loop. |
712 | + */ |
713 | +void destroy(); |
714 | + |
715 | +/** |
716 | + * @brief Enters the Qt core world and schedules the given task for execution. |
717 | + * @param task The task to be executed in the Qt core world. |
718 | + * @return A std::future that can be waited for to synchronize to the world's internal event loop. |
719 | + */ |
720 | +std::future<void> enter_with_task(const std::function<void()>& task); |
721 | + |
722 | + |
723 | +/** |
724 | + * @brief Enters the Qt core world and schedules the given task for execution. |
725 | + * @param task The task to be executed in the Qt core world. |
726 | + * @return A std::future that can be waited for to get hold of the result of the task. |
727 | + */ |
728 | +template<typename T> |
729 | +inline std::future<T> enter_with_task_and_expect_result(const std::function<T()>& task) |
730 | +{ |
731 | + std::shared_ptr<std::promise<T>> promise = std::make_shared<std::promise<T>>(); |
732 | + std::future<T> future = promise->get_future(); |
733 | + |
734 | + auto t = [promise, task]() |
735 | + { |
736 | + try |
737 | + { |
738 | + promise->set_value(task()); |
739 | + } catch(...) |
740 | + { |
741 | + promise->set_exception(std::current_exception()); |
742 | + } |
743 | + }; |
744 | + |
745 | + enter_with_task(t); |
746 | + |
747 | + return future; |
748 | +} |
749 | +} |
750 | +} |
751 | +} |
752 | + |
753 | +#endif // QT_CORE_WORLD_BRIDGE_H_ |
754 | |
755 | === modified file 'src/core/media/codec.h' |
756 | --- src/core/media/codec.h 2015-01-13 14:18:59 +0000 |
757 | +++ src/core/media/codec.h 2015-03-31 21:19:24 +0000 |
758 | @@ -230,6 +230,7 @@ |
759 | } |
760 | }; |
761 | |
762 | +<<<<<<< TREE |
763 | namespace helper |
764 | { |
765 | template<> |
766 | @@ -310,6 +311,48 @@ |
767 | } |
768 | }; |
769 | |
770 | +======= |
771 | +namespace helper |
772 | +{ |
773 | +template<> |
774 | +struct TypeMapper<core::ubuntu::media::Player::Error> |
775 | +{ |
776 | + constexpr static ArgumentType type_value() |
777 | + { |
778 | + return core::dbus::ArgumentType::int16; |
779 | + } |
780 | + constexpr static bool is_basic_type() |
781 | + { |
782 | + return false; |
783 | + } |
784 | + constexpr static bool requires_signature() |
785 | + { |
786 | + return false; |
787 | + } |
788 | + |
789 | + static std::string signature() |
790 | + { |
791 | + static const std::string s = TypeMapper<std::int16_t>::signature(); |
792 | + return s; |
793 | + } |
794 | +}; |
795 | +} |
796 | + |
797 | +template<> |
798 | +struct Codec<core::ubuntu::media::Player::Error> |
799 | +{ |
800 | + static void encode_argument(core::dbus::Message::Writer& out, const core::ubuntu::media::Player::Error& in) |
801 | + { |
802 | + out.push_int16(static_cast<std::int16_t>(in)); |
803 | + } |
804 | + |
805 | + static void decode_argument(core::dbus::Message::Reader& out, core::ubuntu::media::Player::Error& in) |
806 | + { |
807 | + in = static_cast<core::ubuntu::media::Player::Error>(out.pop_int16()); |
808 | + } |
809 | +}; |
810 | + |
811 | +>>>>>>> MERGE-SOURCE |
812 | } |
813 | } |
814 | |
815 | |
816 | === modified file 'src/core/media/engine.h' |
817 | === modified file 'src/core/media/gstreamer/engine.cpp' |
818 | === modified file 'src/core/media/gstreamer/engine.h' |
819 | === modified file 'src/core/media/gstreamer/playbin.h' |
820 | --- src/core/media/gstreamer/playbin.h 2014-11-11 23:59:07 +0000 |
821 | +++ src/core/media/gstreamer/playbin.h 2015-03-31 21:19:24 +0000 |
822 | @@ -109,6 +109,7 @@ |
823 | G_CALLBACK(about_to_finish), |
824 | this |
825 | ); |
826 | +<<<<<<< TREE |
827 | |
828 | g_signal_connect( |
829 | pipeline, |
830 | @@ -117,6 +118,8 @@ |
831 | this |
832 | ); |
833 | |
834 | +======= |
835 | +>>>>>>> MERGE-SOURCE |
836 | } |
837 | |
838 | ~Playbin() |
839 | |
840 | === modified file 'src/core/media/mpris/player.h' |
841 | === modified file 'src/core/media/player_implementation.cpp' |
842 | === modified file 'src/core/media/player_implementation.h' |
843 | === modified file 'src/core/media/player_skeleton.cpp' |
844 | === modified file 'src/core/media/player_skeleton.h' |
845 | === modified file 'src/core/media/player_stub.cpp' |
846 | === modified file 'src/core/media/player_stub.h' |
847 | === modified file 'src/core/media/service_implementation.cpp' |
848 | --- src/core/media/service_implementation.cpp 2015-01-16 17:17:30 +0000 |
849 | +++ src/core/media/service_implementation.cpp 2015-03-31 21:19:24 +0000 |
850 | @@ -35,6 +35,9 @@ |
851 | #include <map> |
852 | #include <memory> |
853 | #include <thread> |
854 | +#include <utility> |
855 | + |
856 | +#include <pulse/pulseaudio.h> |
857 | |
858 | #include <pulse/pulseaudio.h> |
859 | |
860 | @@ -460,6 +463,7 @@ |
861 | int disp_cookie; |
862 | std::shared_ptr<dbus::Object> uscreen_session; |
863 | MediaRecorderObserver *observer; |
864 | +<<<<<<< TREE |
865 | |
866 | // Pulse-specific |
867 | pa_mainloop_api *pulse_mainloop_api; |
868 | @@ -479,6 +483,29 @@ |
869 | core::Signal<void> pause_playback; |
870 | std::unique_ptr<CallMonitor> call_monitor; |
871 | std::list<media::Player::PlayerKey> paused_sessions; |
872 | +======= |
873 | + |
874 | + // Pulse-specific |
875 | + pa_mainloop_api *pulse_mainloop_api; |
876 | + pa_threaded_mainloop *pulse_mainloop; |
877 | + pa_context *pulse_context; |
878 | + std::thread pulse_worker; |
879 | + std::mutex pulse_mutex; |
880 | + std::condition_variable pcv; |
881 | + bool headphones_connected; |
882 | + bool a2dp_connected; |
883 | + std::tuple<int, int, std::string> active_sink; |
884 | + int primary_idx; |
885 | + |
886 | + // Gets signaled when both the headphone jack is removed or an A2DP device is |
887 | + // disconnected and playback needs pausing. Also gets signaled when recording |
888 | + // begins. |
889 | + core::Signal<void> pause_playback; |
890 | + std::unique_ptr<CallMonitor> call_monitor; |
891 | + // Holds a pair of a Player key denoting what player to resume playback, and a bool |
892 | + // for if it should be resumed after a phone call is hung up |
893 | + std::list<std::pair<media::Player::PlayerKey, bool>> paused_sessions; |
894 | +>>>>>>> MERGE-SOURCE |
895 | }; |
896 | |
897 | media::ServiceImplementation::ServiceImplementation() : d(new Private()) |
898 | @@ -488,7 +515,9 @@ |
899 | // When the battery level hits 10% or 5%, pause all multimedia sessions. |
900 | // Playback will resume when the user clears the presented notification. |
901 | if (level == "low" || level == "very_low") |
902 | - pause_all_multimedia_sessions(); |
903 | + // Whatever player session is currently playing, make sure it is NOT resumed after |
904 | + // a phonecall is hung up |
905 | + pause_all_multimedia_sessions(false); |
906 | }); |
907 | |
908 | d->is_warning->changed().connect([this](const core::IndicatorPower::IsWarning::ValueType ¬ifying) |
909 | @@ -498,6 +527,7 @@ |
910 | if (!notifying) |
911 | resume_multimedia_session(); |
912 | }); |
913 | +<<<<<<< TREE |
914 | |
915 | d->pause_playback.connect([this]() |
916 | { |
917 | @@ -518,6 +548,32 @@ |
918 | break; |
919 | } |
920 | }); |
921 | +======= |
922 | + |
923 | + d->pause_playback.connect([this]() |
924 | + { |
925 | + std::cout << "Got pause_playback signal, pausing all multimedia sessions" << std::endl; |
926 | + // Whatever player session is currently playing, make sure it is NOT resumed after |
927 | + // a phonecall is hung up |
928 | + pause_all_multimedia_sessions(false); |
929 | + }); |
930 | + |
931 | + d->call_monitor->on_change([this](CallMonitor::State state) { |
932 | + switch (state) { |
933 | + case CallMonitor::OffHook: |
934 | + std::cout << "Got call started signal, pausing all multimedia sessions" << std::endl; |
935 | + // Whatever player session is currently playing, make sure it gets resumed after |
936 | + // a phonecall is hung up |
937 | + pause_all_multimedia_sessions(true); |
938 | + break; |
939 | + case CallMonitor::OnHook: |
940 | + std::cout << "Got call ended signal, resuming paused multimedia sessions" << std::endl; |
941 | + // Don't auto-resume any paused video playback sessions |
942 | + resume_paused_multimedia_sessions(false); |
943 | + break; |
944 | + } |
945 | + }); |
946 | +>>>>>>> MERGE-SOURCE |
947 | } |
948 | |
949 | media::ServiceImplementation::~ServiceImplementation() |
950 | @@ -595,20 +651,27 @@ |
951 | }); |
952 | } |
953 | |
954 | -void media::ServiceImplementation::pause_all_multimedia_sessions() |
955 | +void media::ServiceImplementation::pause_all_multimedia_sessions(bool resume_play_after_phonecall) |
956 | { |
957 | - enumerate_players([this](const media::Player::PlayerKey& key, const std::shared_ptr<media::Player>& player) |
958 | + enumerate_players([this, resume_play_after_phonecall](const media::Player::PlayerKey& key, const std::shared_ptr<media::Player>& player) |
959 | { |
960 | if (player->playback_status() == Player::playing |
961 | && player->audio_stream_role() == media::Player::multimedia) |
962 | { |
963 | +<<<<<<< TREE |
964 | d->paused_sessions.push_back(key); |
965 | std::cout << "Pausing Player with key: " << key << std::endl; |
966 | +======= |
967 | + auto paused_player_pair = std::make_pair(key, resume_play_after_phonecall); |
968 | + d->paused_sessions.push_back(paused_player_pair); |
969 | + std::cout << "Pausing Player with key: " << key << std::endl; |
970 | +>>>>>>> MERGE-SOURCE |
971 | player->pause(); |
972 | } |
973 | }); |
974 | } |
975 | |
976 | +<<<<<<< TREE |
977 | void media::ServiceImplementation::resume_paused_multimedia_sessions(bool resume_video_sessions) |
978 | { |
979 | std::for_each(d->paused_sessions.begin(), d->paused_sessions.end(), [this, resume_video_sessions](const media::Player::PlayerKey& key) { |
980 | @@ -623,6 +686,25 @@ |
981 | d->paused_sessions.clear(); |
982 | } |
983 | |
984 | +======= |
985 | +void media::ServiceImplementation::resume_paused_multimedia_sessions(bool resume_video_sessions) |
986 | +{ |
987 | + std::for_each(d->paused_sessions.begin(), d->paused_sessions.end(), |
988 | + [this, resume_video_sessions](const std::pair<media::Player::PlayerKey, bool> &paused_player_pair) { |
989 | + const media::Player::PlayerKey key = paused_player_pair.first; |
990 | + const bool resume_play_after_phonecall = paused_player_pair.second; |
991 | + auto player = player_for_key(key); |
992 | + // Only resume video playback if explicitly desired |
993 | + if ((resume_video_sessions || player->is_audio_source()) && resume_play_after_phonecall) |
994 | + player->play(); |
995 | + else |
996 | + std::cout << "Not auto-resuming video player session or other type of player session." << std::endl; |
997 | + }); |
998 | + |
999 | + d->paused_sessions.clear(); |
1000 | +} |
1001 | + |
1002 | +>>>>>>> MERGE-SOURCE |
1003 | void media::ServiceImplementation::resume_multimedia_session() |
1004 | { |
1005 | if (not has_player_for_key(d->resume_key)) |
1006 | |
1007 | === modified file 'src/core/media/service_implementation.h' |
1008 | --- src/core/media/service_implementation.h 2015-01-16 17:17:30 +0000 |
1009 | +++ src/core/media/service_implementation.h 2015-03-31 21:19:24 +0000 |
1010 | @@ -42,8 +42,13 @@ |
1011 | void pause_other_sessions(Player::PlayerKey key); |
1012 | |
1013 | private: |
1014 | +<<<<<<< TREE |
1015 | void pause_all_multimedia_sessions(); |
1016 | void resume_paused_multimedia_sessions(bool resume_video_sessions = true); |
1017 | +======= |
1018 | + void pause_all_multimedia_sessions(bool resume_play_after_phonecall); |
1019 | + void resume_paused_multimedia_sessions(bool resume_video_sessions = true); |
1020 | +>>>>>>> MERGE-SOURCE |
1021 | void resume_multimedia_session(); |
1022 | |
1023 | struct Private; |
1024 | |
1025 | === modified file 'src/core/media/service_skeleton.cpp' |
1026 | === modified file 'tests/unit-tests/CMakeLists.txt' |
1027 | --- tests/unit-tests/CMakeLists.txt 2014-11-18 20:29:26 +0000 |
1028 | +++ tests/unit-tests/CMakeLists.txt 2015-03-31 21:19:24 +0000 |
1029 | @@ -41,8 +41,12 @@ |
1030 | ${PC_GSTREAMER_1_0_LIBRARIES} |
1031 | ${PROCESS_CPP_LDFLAGS} |
1032 | ${GIO_LIBRARIES} |
1033 | +<<<<<<< TREE |
1034 | ${PROCESS_CPP_LIBRARIES} |
1035 | ${PC_PULSE_AUDIO_LIBRARIES} |
1036 | +======= |
1037 | + ${PC_PULSE_AUDIO_LIBRARIES} |
1038 | +>>>>>>> MERGE-SOURCE |
1039 | |
1040 | gmock |
1041 | gmock_main |
1042 | |
1043 | === modified file 'tests/unit-tests/test-gstreamer-engine.cpp' |
1044 | --- tests/unit-tests/test-gstreamer-engine.cpp 2015-03-03 22:41:22 +0000 |
1045 | +++ tests/unit-tests/test-gstreamer-engine.cpp 2015-03-31 21:19:24 +0000 |
1046 | @@ -155,6 +155,7 @@ |
1047 | std::chrono::seconds{10})); |
1048 | } |
1049 | |
1050 | +<<<<<<< TREE |
1051 | TEST(GStreamerEngine, setting_uri_and_audio_playback_with_http_headers_works) |
1052 | { |
1053 | const std::string test_file{"/tmp/test-audio-1.ogg"}; |
1054 | @@ -222,6 +223,14 @@ |
1055 | const std::string test_file_uri{"file:///tmp/test-audio.ogg"}; |
1056 | std::remove(test_file.c_str()); |
1057 | ASSERT_TRUE(test::copy_test_media_file_to("test-audio.ogg", test_file)); |
1058 | +======= |
1059 | +TEST(GStreamerEngine, DISABLED_stop_pause_play_seek_audio_only_works) |
1060 | +{ |
1061 | + const std::string test_file{"/tmp/test.ogg"}; |
1062 | + const std::string test_file_uri{"file:///tmp/test.ogg"}; |
1063 | + std::remove(test_file.c_str()); |
1064 | + ASSERT_TRUE(test::copy_test_mp3_file_to(test_file)); |
1065 | +>>>>>>> MERGE-SOURCE |
1066 | |
1067 | core::testing::WaitableStateTransition<core::ubuntu::media::Engine::State> wst( |
1068 | core::ubuntu::media::Engine::State::ready); |