Merge lp:~thomas-voss/media-hub/add-display-controller-interface into lp:media-hub
- add-display-controller-interface
- Merge into trunk
Status: | Work in progress |
---|---|
Proposed branch: | lp:~thomas-voss/media-hub/add-display-controller-interface |
Merge into: | lp:media-hub |
Prerequisite: | lp:~thomas-voss/media-hub/refactor-video-sink-interface-to-not-rely-on-hybris-types |
Diff against target: |
2840 lines (+1594/-805) 24 files modified
include/core/media/service.h (+1/-1) src/core/media/CMakeLists.txt (+9/-1) src/core/media/audio/output_observer.cpp (+43/-0) src/core/media/audio/output_observer.h (+74/-0) src/core/media/audio/pulse_audio_output_observer.cpp (+241/-0) src/core/media/audio/pulse_audio_output_observer.h (+60/-0) src/core/media/dispatcher.h (+38/-0) src/core/media/external_services.h (+83/-0) src/core/media/indicator_power_service.h (+0/-74) src/core/media/player_implementation.cpp (+25/-74) src/core/media/player_implementation.h (+18/-6) src/core/media/power/battery_observer.cpp (+127/-0) src/core/media/power/battery_observer.h (+69/-0) src/core/media/power/state_controller.cpp (+302/-0) src/core/media/power/state_controller.h (+102/-0) src/core/media/powerd_service.h (+0/-73) src/core/media/server/server.cpp (+98/-2) src/core/media/service_implementation.cpp (+111/-446) src/core/media/service_implementation.h (+26/-3) src/core/media/service_stub.h (+3/-1) src/core/media/telephony/call_monitor.cpp (+34/-29) src/core/media/telephony/call_monitor.h (+36/-18) src/core/media/unity_screen_service.h (+0/-72) tests/acceptance-tests/service.cpp (+94/-5) |
To merge this branch: | bzr merge lp:~thomas-voss/media-hub/add-display-controller-interface |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Jim Hodapp (community) | code | Needs Fixing | |
Review via email: mp+241570@code.launchpad.net |
Commit message
Factor out functional dependencies into interfaces and implementations.
Description of the change
Factor out functional dependencies into interfaces and implementations.
- 96. By Thomas Voß
-
Move around implementation.
Remove no longer needed interfaces and implementations. - 97. By Thomas Voß
-
Put the CallMonitor interface into its own namespace.
- 98. By Thomas Voß
-
Remerge prerequisite branch.
- 99. By Thomas Voß
-
Make media::
RecorderObserve r an external dependency.
- 100. By Thomas Voß
-
Re-merge prerequisite branch.
- 101. By Thomas Voß
-
Adjust setup of service instance in acceptance tests.
- 102. By Thomas Voß
-
Make ClientDeathObserver instance a dependency passed down through ctors.
- 103. By Thomas Voß
-
* Factor out audio output observation into media::
audio:: OutputObserver. [ Jim Hodapp ]
* Pause playback when a headphone is unplugged or an A2DP device is
unpaired (LP: #1368300)
[ Ricardo Mendoza ]
* Pause playback when a headphone is unplugged or an A2DP device is
unpaired (LP: #1368300) - 104. By Thomas Voß
-
Merge prerequisite branch.
Unmerged revisions
- 104. By Thomas Voß
-
Merge prerequisite branch.
- 103. By Thomas Voß
-
* Factor out audio output observation into media::
audio:: OutputObserver. [ Jim Hodapp ]
* Pause playback when a headphone is unplugged or an A2DP device is
unpaired (LP: #1368300)
[ Ricardo Mendoza ]
* Pause playback when a headphone is unplugged or an A2DP device is
unpaired (LP: #1368300) - 102. By Thomas Voß
-
Make ClientDeathObserver instance a dependency passed down through ctors.
- 101. By Thomas Voß
-
Adjust setup of service instance in acceptance tests.
- 100. By Thomas Voß
-
Re-merge prerequisite branch.
- 99. By Thomas Voß
-
Make media::
RecorderObserve r an external dependency. - 98. By Thomas Voß
-
Remerge prerequisite branch.
- 97. By Thomas Voß
-
Put the CallMonitor interface into its own namespace.
- 96. By Thomas Voß
-
Move around implementation.
Remove no longer needed interfaces and implementations. - 95. By Thomas Voß
-
* Refactor client-facing interfaces to pull out explicit dependency on hybris-based media layer.
[ Ubuntu daily release ]
* New rebuild forced
[ Justin McPherson ]
* #1239432 Music fails to pause on incoming/outgoing calls (LP:
#1239432)
[ thomas-voss ]
* Bump build dependency on dbus-cpp to pull in exception safe dtor.
(LP: #1390618)
Preview Diff
1 | === modified file 'include/core/media/service.h' |
2 | --- include/core/media/service.h 2014-04-23 19:00:55 +0000 |
3 | +++ include/core/media/service.h 2014-11-25 11:08:14 +0000 |
4 | @@ -28,7 +28,7 @@ |
5 | { |
6 | namespace media |
7 | { |
8 | -class Service : public std::enable_shared_from_this<Service> |
9 | +class Service |
10 | { |
11 | public: |
12 | struct Client |
13 | |
14 | === modified file 'src/core/media/CMakeLists.txt' |
15 | --- src/core/media/CMakeLists.txt 2014-11-25 11:08:14 +0000 |
16 | +++ src/core/media/CMakeLists.txt 2014-11-25 11:08:14 +0000 |
17 | @@ -11,7 +11,7 @@ |
18 | |
19 | message(STATUS ${MEDIA_HUB_HEADERS}) |
20 | |
21 | -add_subdirectory(call-monitor) |
22 | +add_subdirectory(telephony) |
23 | |
24 | add_library( |
25 | media-hub-common SHARED |
26 | @@ -90,8 +90,16 @@ |
27 | hybris_client_death_observer.cpp |
28 | cover_art_resolver.cpp |
29 | engine.cpp |
30 | + |
31 | + audio/pulse_audio_output_observer.cpp |
32 | + audio/output_observer.cpp |
33 | + |
34 | + power/battery_observer.cpp |
35 | + power/state_controller.cpp |
36 | + |
37 | recorder_observer.cpp |
38 | hybris_recorder_observer.cpp |
39 | + |
40 | gstreamer/engine.cpp |
41 | gstreamer/playbin.cpp |
42 | |
43 | |
44 | === added directory 'src/core/media/audio' |
45 | === added file 'src/core/media/audio/output_observer.cpp' |
46 | --- src/core/media/audio/output_observer.cpp 1970-01-01 00:00:00 +0000 |
47 | +++ src/core/media/audio/output_observer.cpp 2014-11-25 11:08:14 +0000 |
48 | @@ -0,0 +1,43 @@ |
49 | +/* |
50 | + * Copyright © 2014 Canonical Ltd. |
51 | + * |
52 | + * This program is free software: you can redistribute it and/or modify it |
53 | + * under the terms of the GNU Lesser General Public License version 3, |
54 | + * as published by the Free Software Foundation. |
55 | + * |
56 | + * This program is distributed in the hope that it will be useful, |
57 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
58 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
59 | + * GNU Lesser General Public License for more details. |
60 | + * |
61 | + * You should have received a copy of the GNU Lesser General Public License |
62 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
63 | + * |
64 | + * Authored by: Thomas Voß <thomas.voss@canonical.com> |
65 | + */ |
66 | + |
67 | +#include <core/media/audio/output_observer.h> |
68 | + |
69 | +#include <core/media/audio/pulse_audio_output_observer.h> |
70 | + |
71 | +#include <iostream> |
72 | + |
73 | +namespace audio = core::ubuntu::media::audio; |
74 | + |
75 | +std::ostream& audio::operator<<(std::ostream& out, audio::OutputState state) |
76 | +{ |
77 | + switch (state) |
78 | + { |
79 | + case audio::OutputState::connected: |
80 | + return out << "OutputState::connected"; |
81 | + case audio::OutputState::disconnected: |
82 | + return out << "OutputState::disconnected"; |
83 | + } |
84 | + |
85 | + return out; |
86 | +} |
87 | + |
88 | +audio::OutputObserver::Ptr audio::make_platform_default_output_observer() |
89 | +{ |
90 | + return std::make_shared<audio::PulseAudioOutputObserver>(); |
91 | +} |
92 | |
93 | === added file 'src/core/media/audio/output_observer.h' |
94 | --- src/core/media/audio/output_observer.h 1970-01-01 00:00:00 +0000 |
95 | +++ src/core/media/audio/output_observer.h 2014-11-25 11:08:14 +0000 |
96 | @@ -0,0 +1,74 @@ |
97 | +/* |
98 | + * Copyright © 2014 Canonical Ltd. |
99 | + * |
100 | + * This program is free software: you can redistribute it and/or modify it |
101 | + * under the terms of the GNU Lesser General Public License version 3, |
102 | + * as published by the Free Software Foundation. |
103 | + * |
104 | + * This program is distributed in the hope that it will be useful, |
105 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
106 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
107 | + * GNU Lesser General Public License for more details. |
108 | + * |
109 | + * You should have received a copy of the GNU Lesser General Public License |
110 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
111 | + * |
112 | + * Authored by: Thomas Voß <thomas.voss@canonical.com> |
113 | + */ |
114 | +#ifndef CORE_UBUNTU_MEDIA_AUDIO_OUTPUT_OBSERVER_H_ |
115 | +#define CORE_UBUNTU_MEDIA_AUDIO_OUTPUT_OBSERVER_H_ |
116 | + |
117 | +#include <core/property.h> |
118 | + |
119 | +#include <iosfwd> |
120 | +#include <memory> |
121 | + |
122 | +namespace core |
123 | +{ |
124 | +namespace ubuntu |
125 | +{ |
126 | +namespace media |
127 | +{ |
128 | +namespace audio |
129 | +{ |
130 | +// All known states of an audio output. |
131 | +enum class OutputState |
132 | +{ |
133 | + // The output is connected. |
134 | + connected, |
135 | + // The output is disconnected. |
136 | + disconnected, |
137 | +}; |
138 | + |
139 | +// Models observation of audio outputs of a device. |
140 | +// Right now, we are only interested in monitoring the |
141 | +// state of external outputs to react accordingly if |
142 | +// wired or bluetooth outputs are connected/disconnected. |
143 | +class OutputObserver |
144 | +{ |
145 | +public: |
146 | + // Save us some typing. |
147 | + typedef std::shared_ptr<OutputObserver> Ptr; |
148 | + |
149 | + virtual ~OutputObserver() = default; |
150 | + |
151 | + // Getable/observable property holding the state of external outputs. |
152 | + virtual const core::Property<OutputState>& external_output_state() const = 0; |
153 | + |
154 | +protected: |
155 | + OutputObserver() = default; |
156 | + OutputObserver(const OutputObserver&) = delete; |
157 | + OutputObserver& operator=(const OutputObserver&) = delete; |
158 | +}; |
159 | + |
160 | +// Pretty prints the given state to the given output stream. |
161 | +std::ostream& operator<<(std::ostream&, OutputState); |
162 | + |
163 | +// Creats a platform default instance for observing audio outputs. |
164 | +OutputObserver::Ptr make_platform_default_output_observer(); |
165 | +} |
166 | +} |
167 | +} |
168 | +} |
169 | + |
170 | +#endif // CORE_UBUNTU_MEDIA_AUDIO_OUTPUT_OBSERVER_H_ |
171 | |
172 | === added file 'src/core/media/audio/pulse_audio_output_observer.cpp' |
173 | --- src/core/media/audio/pulse_audio_output_observer.cpp 1970-01-01 00:00:00 +0000 |
174 | +++ src/core/media/audio/pulse_audio_output_observer.cpp 2014-11-25 11:08:14 +0000 |
175 | @@ -0,0 +1,241 @@ |
176 | +/* |
177 | + * Copyright © 2014 Canonical Ltd. |
178 | + * |
179 | + * This program is free software: you can redistribute it and/or modify it |
180 | + * under the terms of the GNU Lesser General Public License version 3, |
181 | + * as published by the Free Software Foundation. |
182 | + * |
183 | + * This program is distributed in the hope that it will be useful, |
184 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
185 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
186 | + * GNU Lesser General Public License for more details. |
187 | + * |
188 | + * You should have received a copy of the GNU Lesser General Public License |
189 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
190 | + * |
191 | + * Authored by: Thomas Voß <thomas.voss@canonical.com> |
192 | + * Ricardo Mendoza <ricardo.mendoza@canonical.com> |
193 | + */ |
194 | + |
195 | +#include <core/media/audio/pulse_audio_output_observer.h> |
196 | + |
197 | +#include <pulse/pulseaudio.h> |
198 | + |
199 | +#include <cstdint> |
200 | + |
201 | +#include <string> |
202 | + |
203 | +namespace audio = core::ubuntu::media::audio; |
204 | + |
205 | +namespace |
206 | +{ |
207 | +namespace pa |
208 | +{ |
209 | +static constexpr const char* a2dp_output_prefix{"output-a2dp"}; |
210 | +static constexpr const char* wired_output_prefix{"output-wired"}; |
211 | + |
212 | +// Wraps a pulseaudio sink |
213 | +struct Sink |
214 | +{ |
215 | + static constexpr const std::int32_t the_invalid_index{-1}; |
216 | + static constexpr const std::int32_t the_invalid_port{-1}; |
217 | + |
218 | + std::int32_t index{the_invalid_index}; |
219 | + std::string name{}; |
220 | +}; |
221 | + |
222 | +typedef std::shared_ptr<pa_threaded_mainloop> ThreadedMainLoopPtr; |
223 | +ThreadedMainLoopPtr make_threaded_main_loop() |
224 | +{ |
225 | + return ThreadedMainLoopPtr |
226 | + { |
227 | + pa_threaded_mainloop_new(), |
228 | + [](pa_threaded_mainloop* ml) |
229 | + { |
230 | + pa_threaded_mainloop_stop(ml); |
231 | + pa_threaded_mainloop_free(ml); |
232 | + } |
233 | + }; |
234 | +} |
235 | + |
236 | +typedef std::shared_ptr<pa_context> ContextPtr; |
237 | +ContextPtr make_context(ThreadedMainLoopPtr main_loop) |
238 | +{ |
239 | + return ContextPtr |
240 | + { |
241 | + pa_context_new(pa_threaded_mainloop_get_api(main_loop.get()), "MediaHubPulseContext"), |
242 | + pa_context_unref |
243 | + }; |
244 | +} |
245 | + |
246 | +void start_main_loop(ThreadedMainLoopPtr ml) |
247 | +{ |
248 | + pa_threaded_mainloop_start(ml.get()); |
249 | +} |
250 | + |
251 | +void get_index_of_primary_sink_async(ContextPtr ctxt, pa_sink_info_cb_t cb, void* cookie) |
252 | +{ |
253 | + pa_context_get_sink_info_by_name(ctxt.get(), "sink.primary", cb, cookie); |
254 | +} |
255 | + |
256 | +void get_sink_info_by_index_async(ContextPtr ctxt, std::uint32_t index, pa_sink_info_cb_t cb, void* cookie) |
257 | +{ |
258 | + pa_context_get_sink_info_by_index(ctxt.get(), index, cb, cookie); |
259 | +} |
260 | + |
261 | +void connect_async(ContextPtr ctxt) |
262 | +{ |
263 | + pa_context_connect(ctxt.get(), nullptr, static_cast<pa_context_flags_t>(PA_CONTEXT_NOAUTOSPAWN | PA_CONTEXT_NOFAIL), nullptr); |
264 | +} |
265 | + |
266 | +bool is_port_available_on_sink(const pa_sink_info* info, const std::string& port) |
267 | +{ |
268 | + if (not info) |
269 | + return false; |
270 | + |
271 | + for (std::uint32_t i = 0; i < info->n_ports; i++) |
272 | + { |
273 | + if (info->ports[i]->available == PA_PORT_AVAILABLE_NO) |
274 | + continue; |
275 | + |
276 | + if (std::string{info->ports[i]->name}.find(port) == 0) |
277 | + return true; |
278 | + } |
279 | + |
280 | + return false; |
281 | +} |
282 | +} |
283 | +} |
284 | + |
285 | +struct audio::PulseAudioOutputObserver::Private |
286 | +{ |
287 | + static void context_notification_cb(pa_context* ctxt, void* cookie) |
288 | + { |
289 | + if (auto thiz = static_cast<Private*>(cookie)) |
290 | + { |
291 | + // Better safe than sorry: Check if we got signaled for the |
292 | + // context we are actually interested in. |
293 | + if (thiz->context.get() != ctxt) |
294 | + return; |
295 | + |
296 | + switch (pa_context_get_state(ctxt)) |
297 | + { |
298 | + case PA_CONTEXT_READY: |
299 | + thiz->on_context_ready(); |
300 | + break; |
301 | + case PA_CONTEXT_FAILED: |
302 | + thiz->on_context_failed(); |
303 | + break; |
304 | + default: |
305 | + break; |
306 | + } |
307 | + } |
308 | + } |
309 | + |
310 | + static void context_subscription_cb(pa_context* ctxt, pa_subscription_event_type_t ev, uint32_t idx, void* cookie) |
311 | + { |
312 | + (void) idx; |
313 | + |
314 | + if (auto thiz = static_cast<Private*>(cookie)) |
315 | + { |
316 | + // Better safe than sorry: Check if we got signaled for the |
317 | + // context we are actually interested in. |
318 | + if (thiz->context.get() != ctxt) |
319 | + return; |
320 | + |
321 | + if ((ev & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) |
322 | + thiz->on_sink_event_with_index(idx); |
323 | + } |
324 | + } |
325 | + |
326 | + static void query_for_primary_sink_finished(pa_context* ctxt, const pa_sink_info* si, int eol, void* cookie) |
327 | + { |
328 | + if (eol) |
329 | + return; |
330 | + |
331 | + if (auto thiz = static_cast<Private*>(cookie)) |
332 | + { |
333 | + // Better safe than sorry: Check if we got signaled for the |
334 | + // context we are actually interested in. |
335 | + if (thiz->context.get() != ctxt) |
336 | + return; |
337 | + |
338 | + thiz->on_query_for_primary_sink_finished(si); |
339 | + } |
340 | + } |
341 | + |
342 | + Private() |
343 | + : external_output_state{audio::OutputState::disconnected}, |
344 | + main_loop{pa::make_threaded_main_loop()}, |
345 | + context{pa::make_context(main_loop)} |
346 | + { |
347 | + pa_context_set_state_callback(context.get(), Private::context_notification_cb, this); |
348 | + pa_context_set_subscribe_callback(context.get(), Private::context_subscription_cb, this); |
349 | + |
350 | + output.a2dp | external_output_state; |
351 | + output.wired | external_output_state; |
352 | + |
353 | + pa::connect_async(context); |
354 | + |
355 | + } |
356 | + |
357 | + // The connection attempt has been successful and we are connected |
358 | + // to pulseaudio now. |
359 | + void on_context_ready() |
360 | + { |
361 | + pa::get_index_of_primary_sink_async(context, Private::query_for_primary_sink_finished, this); |
362 | + } |
363 | + |
364 | + // Either a connection attempt failed, or an existing connection |
365 | + // was unexpectedly terminated. |
366 | + void on_context_failed() |
367 | + { |
368 | + pa::connect_async(context); |
369 | + } |
370 | + |
371 | + // Something changed on the sink with index idx. |
372 | + void on_sink_event_with_index(std::int32_t index) |
373 | + { |
374 | + if (index != primary_sink_index) |
375 | + return; |
376 | + |
377 | + pa::get_sink_info_by_index_async(context, index, Private::query_for_primary_sink_finished, this); |
378 | + } |
379 | + |
380 | + // Query for primary sink finished. |
381 | + void on_query_for_primary_sink_finished(const pa_sink_info* info) |
382 | + { |
383 | + output.wired = pa::is_port_available_on_sink(info, pa::wired_output_prefix) |
384 | + ? media::audio::OutputState::connected |
385 | + : media::audio::OutputState::disconnected; |
386 | + |
387 | + output.a2dp = pa::is_port_available_on_sink(info, pa::a2dp_output_prefix) |
388 | + ? media::audio::OutputState::connected |
389 | + : media::audio::OutputState::disconnected; |
390 | + |
391 | + primary_sink_index = info->index; |
392 | + } |
393 | + |
394 | + core::Property<audio::OutputState> external_output_state; |
395 | + |
396 | + pa::ThreadedMainLoopPtr main_loop; |
397 | + pa::ContextPtr context; |
398 | + std::uint32_t primary_sink_index; |
399 | + struct |
400 | + { |
401 | + core::Property<media::audio::OutputState> wired; |
402 | + core::Property<media::audio::OutputState> a2dp; |
403 | + } output; |
404 | +}; |
405 | + |
406 | +// Constructs a new instance, or throws std::runtime_error |
407 | +// if connection to pulseaudio fails. |
408 | +audio::PulseAudioOutputObserver::PulseAudioOutputObserver() : d{new Private{}} |
409 | +{ |
410 | +} |
411 | + |
412 | +// Getable/observable property holding the state of external outputs. |
413 | +const core::Property<audio::OutputState>& audio::PulseAudioOutputObserver::external_output_state() const |
414 | +{ |
415 | + return d->external_output_state; |
416 | +} |
417 | |
418 | === added file 'src/core/media/audio/pulse_audio_output_observer.h' |
419 | --- src/core/media/audio/pulse_audio_output_observer.h 1970-01-01 00:00:00 +0000 |
420 | +++ src/core/media/audio/pulse_audio_output_observer.h 2014-11-25 11:08:14 +0000 |
421 | @@ -0,0 +1,60 @@ |
422 | +/* |
423 | + * Copyright © 2014 Canonical Ltd. |
424 | + * |
425 | + * This program is free software: you can redistribute it and/or modify it |
426 | + * under the terms of the GNU Lesser General Public License version 3, |
427 | + * as published by the Free Software Foundation. |
428 | + * |
429 | + * This program is distributed in the hope that it will be useful, |
430 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
431 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
432 | + * GNU Lesser General Public License for more details. |
433 | + * |
434 | + * You should have received a copy of the GNU Lesser General Public License |
435 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
436 | + * |
437 | + * Authored by: Thomas Voß <thomas.voss@canonical.com> |
438 | + * Ricardo Mendoza <ricardo.mendoza@canonical.com> |
439 | + */ |
440 | +#ifndef CORE_UBUNTU_MEDIA_AUDIO_PULSE_AUDIO_OUTPUT_OBSERVER_H_ |
441 | +#define CORE_UBUNTU_MEDIA_AUDIO_PULSE_AUDIO_OUTPUT_OBSERVER_H_ |
442 | + |
443 | +#include <core/media/audio/output_observer.h> |
444 | + |
445 | +#include <iosfwd> |
446 | +#include <memory> |
447 | + |
448 | +namespace core |
449 | +{ |
450 | +namespace ubuntu |
451 | +{ |
452 | +namespace media |
453 | +{ |
454 | +namespace audio |
455 | +{ |
456 | +// Implements the audio::OutputObserver interface |
457 | +// relying on pulse to query the connected ports |
458 | +// of the primary card of the system. |
459 | +class PulseAudioOutputObserver : public OutputObserver |
460 | +{ |
461 | +public: |
462 | + // Save us some typing. |
463 | + typedef std::shared_ptr<PulseAudioOutputObserver> Ptr; |
464 | + |
465 | + // Constructs a new instance, or throws std::runtime_error |
466 | + // if connection to pulseaudio fails. |
467 | + PulseAudioOutputObserver(); |
468 | + |
469 | + // Getable/observable property holding the state of external outputs. |
470 | + const core::Property<OutputState>& external_output_state() const override; |
471 | + |
472 | +private: |
473 | + struct Private; |
474 | + std::shared_ptr<Private> d; |
475 | +}; |
476 | +} |
477 | +} |
478 | +} |
479 | +} |
480 | + |
481 | +#endif // CORE_UBUNTU_MEDIA_AUDIO_PULSE_AUDIO_OUTPUT_OBSERVER_H_ |
482 | |
483 | === added file 'src/core/media/dispatcher.h' |
484 | --- src/core/media/dispatcher.h 1970-01-01 00:00:00 +0000 |
485 | +++ src/core/media/dispatcher.h 2014-11-25 11:08:14 +0000 |
486 | @@ -0,0 +1,38 @@ |
487 | +/* |
488 | + * Copyright © 2014 Canonical Ltd. |
489 | + * |
490 | + * This program is free software: you can redistribute it and/or modify it |
491 | + * under the terms of the GNU Lesser General Public License version 3, |
492 | + * as published by the Free Software Foundation. |
493 | + * |
494 | + * This program is distributed in the hope that it will be useful, |
495 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
496 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
497 | + * GNU Lesser General Public License for more details. |
498 | + * |
499 | + * You should have received a copy of the GNU Lesser General Public License |
500 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
501 | + * |
502 | + * Authored by: Thomas Voß <thomas.voss@canonical.com> |
503 | + */ |
504 | +#ifndef CORE_UBUNTU_MEDIA_DISPATCHER_H_ |
505 | +#define CORE_UBUNTU_MEDIA_DISPATCHER_H_ |
506 | + |
507 | +#include <functional> |
508 | + |
509 | +namespace core |
510 | +{ |
511 | +namespace ubuntu |
512 | +{ |
513 | +namespace media |
514 | +{ |
515 | +// Functional describing a task to be executed later. |
516 | +typedef std::function<void()> Task; |
517 | +// Functional describing a Dispatcher that takes tasks |
518 | +// and executes them. The implementation is expected to |
519 | +// execute the task later, on a different thread. |
520 | +typedef std::function<void(Task)> Dispatcher; |
521 | +} |
522 | +} |
523 | +} |
524 | +#endif // CORE_UBUNTU_MEDIA_DISPATCHER_H_ |
525 | |
526 | === added file 'src/core/media/external_services.h' |
527 | --- src/core/media/external_services.h 1970-01-01 00:00:00 +0000 |
528 | +++ src/core/media/external_services.h 2014-11-25 11:08:14 +0000 |
529 | @@ -0,0 +1,83 @@ |
530 | +/* |
531 | + * Copyright © 2014 Canonical Ltd. |
532 | + * |
533 | + * This program is free software: you can redistribute it and/or modify it |
534 | + * under the terms of the GNU Lesser General Public License version 3, |
535 | + * as published by the Free Software Foundation. |
536 | + * |
537 | + * This program is distributed in the hope that it will be useful, |
538 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
539 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
540 | + * GNU Lesser General Public License for more details. |
541 | + * |
542 | + * You should have received a copy of the GNU Lesser General Public License |
543 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
544 | + * |
545 | + * Authored by: Thomas Voß <thomas.voss@canonical.com> |
546 | + */ |
547 | +#ifndef CORE_UBUNTU_MEDIA_EXTERNAL_SERVICES_H_ |
548 | +#define CORE_UBUNTU_MEDIA_EXTERNAL_SERVICES_H_ |
549 | + |
550 | +#include <core/dbus/bus.h> |
551 | + |
552 | +#include <core/dbus/asio/executor.h> |
553 | + |
554 | +#include <boost/asio.hpp> |
555 | + |
556 | +namespace core |
557 | +{ |
558 | +namespace ubuntu |
559 | +{ |
560 | +namespace media |
561 | +{ |
562 | +namespace helper |
563 | +{ |
564 | +// A helper struct that bundles: |
565 | +// * a dispatcher, i.e., the io_service |
566 | +// * access to the system and session bus |
567 | +// |
568 | +// In addtion, it allows us to mock out services and |
569 | +// for acceptance testing purposes. |
570 | +struct ExternalServices |
571 | +{ |
572 | + ExternalServices(const core::dbus::Bus::Ptr& session, const core::dbus::Bus::Ptr& system) |
573 | + : keep_alive{io_service}, |
574 | + session{session}, |
575 | + system{system} |
576 | + { |
577 | + } |
578 | + |
579 | + ExternalServices() |
580 | + : ExternalServices |
581 | + { |
582 | + core::dbus::Bus::Ptr{new core::dbus::Bus{core::dbus::WellKnownBus::session}}, |
583 | + core::dbus::Bus::Ptr{new core::dbus::Bus{core::dbus::WellKnownBus::system}} |
584 | + } |
585 | + { |
586 | + session->install_executor(core::dbus::asio::make_executor(session, io_service)); |
587 | + system->install_executor(core::dbus::asio::make_executor(session, io_service)); |
588 | + } |
589 | + |
590 | + |
591 | + void run() |
592 | + { |
593 | + io_service.run(); |
594 | + } |
595 | + |
596 | + void stop() |
597 | + { |
598 | + io_service.stop(); |
599 | + } |
600 | + |
601 | + boost::asio::io_service io_service; |
602 | + boost::asio::io_service::work keep_alive; |
603 | + |
604 | + core::dbus::Bus::Ptr session; |
605 | + core::dbus::Bus::Ptr system; |
606 | +}; |
607 | +} |
608 | +} |
609 | +} |
610 | +} |
611 | + |
612 | +#endif // CORE_UBUNTU_MEDIA_EXTERNAL_SERVICES_H_ |
613 | |
614 | === removed file 'src/core/media/indicator_power_service.h' |
615 | --- src/core/media/indicator_power_service.h 2014-09-05 18:59:48 +0000 |
616 | +++ src/core/media/indicator_power_service.h 1970-01-01 00:00:00 +0000 |
617 | @@ -1,74 +0,0 @@ |
618 | -/* |
619 | - * Copyright (C) 2014 Canonical Ltd |
620 | - * |
621 | - * This program is free software: you can redistribute it and/or modify |
622 | - * it under the terms of the GNU Lesser General Public License version 3 as |
623 | - * published by the Free Software Foundation. |
624 | - * |
625 | - * This program is distributed in the hope that it will be useful, |
626 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
627 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
628 | - * GNU Lesser General Public License for more details. |
629 | - * |
630 | - * You should have received a copy of the GNU Lesser General Public License |
631 | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
632 | - * |
633 | - * Author: Jim Hodapp <jim.hodapp@canonical.com> |
634 | - */ |
635 | - |
636 | -#include <core/dbus/dbus.h> |
637 | -#include <core/dbus/fixture.h> |
638 | -#include <core/dbus/object.h> |
639 | -#include <core/dbus/property.h> |
640 | -#include <core/dbus/service.h> |
641 | -#include <core/dbus/interfaces/properties.h> |
642 | -#include <core/dbus/types/stl/tuple.h> |
643 | -#include <core/dbus/types/stl/vector.h> |
644 | - |
645 | -#include <core/dbus/asio/executor.h> |
646 | - |
647 | -#include <string> |
648 | - |
649 | -namespace core |
650 | -{ |
651 | - |
652 | -struct IndicatorPower |
653 | -{ |
654 | - static std::string& name() |
655 | - { |
656 | - static std::string s = "com.canonical.indicator.power.Battery"; |
657 | - return s; |
658 | - } |
659 | - |
660 | - struct PowerLevel |
661 | - { |
662 | - static std::string name() |
663 | - { |
664 | - static std::string s = "PowerLevel"; |
665 | - return s; |
666 | - } |
667 | - |
668 | - typedef IndicatorPower Interface; |
669 | - // Possible values: "ok", "low", "very_low", "critical" |
670 | - typedef std::string ValueType; |
671 | - static const bool readable = true; |
672 | - static const bool writable = false; |
673 | - }; |
674 | - |
675 | - struct IsWarning |
676 | - { |
677 | - static std::string name() |
678 | - { |
679 | - static std::string s = "IsWarning"; |
680 | - return s; |
681 | - } |
682 | - |
683 | - typedef IndicatorPower Interface; |
684 | - typedef bool ValueType; |
685 | - static const bool readable = true; |
686 | - static const bool writable = false; |
687 | - }; |
688 | - |
689 | -}; // IndicatorPower |
690 | - |
691 | -} |
692 | |
693 | === modified file 'src/core/media/player_implementation.cpp' |
694 | --- src/core/media/player_implementation.cpp 2014-11-25 11:08:14 +0000 |
695 | +++ src/core/media/player_implementation.cpp 2014-11-25 11:08:14 +0000 |
696 | @@ -25,8 +25,6 @@ |
697 | #include "engine.h" |
698 | #include "track_list_implementation.h" |
699 | |
700 | -#include "powerd_service.h" |
701 | -#include "unity_screen_service.h" |
702 | #include "gstreamer/engine.h" |
703 | |
704 | #include <memory> |
705 | @@ -52,41 +50,25 @@ |
706 | WAKELOCK_CLEAR_INVALID |
707 | }; |
708 | |
709 | - Private(PlayerImplementation* parent, |
710 | - const dbus::types::ObjectPath& session_path, |
711 | - const std::shared_ptr<media::Service>& service, |
712 | - PlayerImplementation::PlayerKey key) |
713 | + Private(PlayerImplementation* parent, const media::PlayerImplementation::Configuration& config) |
714 | : parent(parent), |
715 | - service(service), |
716 | + config(config), |
717 | + display_state_lock(config.power_state_controller->display_state_lock()), |
718 | + system_state_lock(config.power_state_controller->system_state_lock()), |
719 | engine(std::make_shared<gstreamer::Engine>()), |
720 | - session_path(session_path), |
721 | track_list( |
722 | new media::TrackListImplementation( |
723 | - session_path.as_string() + "/TrackList", |
724 | + config.session->path().as_string() + "/TrackList", |
725 | engine->meta_data_extractor())), |
726 | - sys_lock_name("media-hub-music-playback"), |
727 | - disp_cookie(-1), |
728 | system_wakelock_count(0), |
729 | display_wakelock_count(0), |
730 | previous_state(Engine::State::stopped), |
731 | - key(key), |
732 | engine_state_change_connection(engine->state().changed().connect(make_state_change_handler())) |
733 | { |
734 | - auto bus = std::shared_ptr<dbus::Bus>(new dbus::Bus(core::dbus::WellKnownBus::system)); |
735 | - bus->install_executor(dbus::asio::make_executor(bus)); |
736 | - |
737 | - auto stub_service = dbus::Service::use_service(bus, dbus::traits::Service<core::Powerd>::interface_name()); |
738 | - powerd_session = stub_service->object_for_path(dbus::types::ObjectPath("/com/canonical/powerd")); |
739 | - |
740 | - auto uscreen_stub_service = dbus::Service::use_service(bus, dbus::traits::Service<core::UScreen>::interface_name()); |
741 | - uscreen_session = uscreen_stub_service->object_for_path(dbus::types::ObjectPath("/com/canonical/Unity/Screen")); |
742 | - |
743 | - auto client_death_observer = media::platform_default_client_death_observer(); |
744 | - |
745 | - client_death_observer->register_for_death_notifications_with_key(key); |
746 | - client_death_observer->on_client_with_key_died().connect([this](const media::Player::PlayerKey& died) |
747 | + config.client_death_observer->register_for_death_notifications_with_key(config.key); |
748 | + config.client_death_observer->on_client_with_key_died().connect([this](const media::Player::PlayerKey& died) |
749 | { |
750 | - if (died != this->key) |
751 | + if (died != this->config.key) |
752 | return; |
753 | |
754 | on_client_died(); |
755 | @@ -169,24 +151,12 @@ |
756 | if (parent->is_video_source()) |
757 | { |
758 | if (++display_wakelock_count == 1) |
759 | - { |
760 | - auto result = uscreen_session->invoke_method_synchronously<core::UScreen::keepDisplayOn, int>(); |
761 | - if (result.is_error()) |
762 | - throw std::runtime_error(result.error().print()); |
763 | - disp_cookie = result.value(); |
764 | - cout << "Requested new display wakelock" << endl; |
765 | - } |
766 | + display_state_lock->request_acquire(media::power::DisplayState::on); |
767 | } |
768 | else |
769 | { |
770 | if (++system_wakelock_count == 1) |
771 | - { |
772 | - auto result = powerd_session->invoke_method_synchronously<core::Powerd::requestSysState, std::string>(sys_lock_name, static_cast<int>(1)); |
773 | - if (result.is_error()) |
774 | - throw std::runtime_error(result.error().print()); |
775 | - sys_cookie = result.value(); |
776 | - cout << "Requested new system wakelock" << endl; |
777 | - } |
778 | + system_state_lock->request_acquire(media::power::SystemState::active); |
779 | } |
780 | } |
781 | catch(const std::exception& e) |
782 | @@ -208,20 +178,12 @@ |
783 | case wakelock_clear_t::WAKELOCK_CLEAR_SYSTEM: |
784 | // Only actually clear the system wakelock once the count reaches zero |
785 | if (--system_wakelock_count == 0) |
786 | - { |
787 | - cout << "Clearing system wakelock" << endl; |
788 | - powerd_session->invoke_method_synchronously<core::Powerd::clearSysState, void>(sys_cookie); |
789 | - sys_cookie.clear(); |
790 | - } |
791 | + system_state_lock->request_release(media::power::SystemState::active); |
792 | break; |
793 | case wakelock_clear_t::WAKELOCK_CLEAR_DISPLAY: |
794 | // Only actually clear the display wakelock once the count reaches zero |
795 | if (--display_wakelock_count == 0) |
796 | - { |
797 | - cout << "Clearing display wakelock" << endl; |
798 | - uscreen_session->invoke_method_synchronously<core::UScreen::removeDisplayOnRequest, void>(disp_cookie); |
799 | - disp_cookie = -1; |
800 | - } |
801 | + display_state_lock->request_release(media::power::DisplayState::on); |
802 | break; |
803 | case wakelock_clear_t::WAKELOCK_CLEAR_INVALID: |
804 | default: |
805 | @@ -275,44 +237,33 @@ |
806 | engine->reset(); |
807 | } |
808 | |
809 | - PlayerImplementation* parent; |
810 | - std::shared_ptr<Service> service; |
811 | + // Our link back to our parent. |
812 | + media::PlayerImplementation* parent; |
813 | + // We just store the parameters passed on construction. |
814 | + media::PlayerImplementation::Configuration config; |
815 | + media::power::StateController::Lock<media::power::DisplayState>::Ptr display_state_lock; |
816 | + media::power::StateController::Lock<media::power::SystemState>::Ptr system_state_lock; |
817 | + |
818 | std::shared_ptr<Engine> engine; |
819 | - dbus::types::ObjectPath session_path; |
820 | std::shared_ptr<TrackListImplementation> track_list; |
821 | - std::shared_ptr<dbus::Object> powerd_session; |
822 | - std::shared_ptr<dbus::Object> uscreen_session; |
823 | - std::string sys_lock_name; |
824 | - int disp_cookie; |
825 | - std::string sys_cookie; |
826 | std::atomic<int> system_wakelock_count; |
827 | std::atomic<int> display_wakelock_count; |
828 | Engine::State previous_state; |
829 | - PlayerImplementation::PlayerKey key; |
830 | core::Signal<> on_client_disconnected; |
831 | core::Connection engine_state_change_connection; |
832 | }; |
833 | |
834 | -media::PlayerImplementation::PlayerImplementation( |
835 | - const std::string& identity, |
836 | - const std::shared_ptr<core::dbus::Bus>& bus, |
837 | - const std::shared_ptr<core::dbus::Object>& session, |
838 | - const std::shared_ptr<Service>& service, |
839 | - PlayerKey key) |
840 | +media::PlayerImplementation::PlayerImplementation(const media::PlayerImplementation::Configuration& config) |
841 | : media::PlayerSkeleton |
842 | { |
843 | media::PlayerSkeleton::Configuration |
844 | { |
845 | - bus, |
846 | - session, |
847 | - identity |
848 | + config.bus, |
849 | + config.session, |
850 | + config.identity |
851 | } |
852 | }, |
853 | - d(make_shared<Private>( |
854 | - this, |
855 | - session->path(), |
856 | - service, |
857 | - key)) |
858 | + d{std::make_shared<Private>(this, config)} |
859 | { |
860 | // Initialize default values for Player interface properties |
861 | can_play().set(true); |
862 | @@ -452,7 +403,7 @@ |
863 | // TODO: Convert this to be a property instead of sync call |
864 | media::Player::PlayerKey media::PlayerImplementation::key() const |
865 | { |
866 | - return d->key; |
867 | + return d->config.key; |
868 | } |
869 | |
870 | media::video::Sink::Ptr media::PlayerImplementation::create_gl_texture_video_sink(std::uint32_t texture_id) |
871 | |
872 | === modified file 'src/core/media/player_implementation.h' |
873 | --- src/core/media/player_implementation.h 2014-11-25 11:08:14 +0000 |
874 | +++ src/core/media/player_implementation.h 2014-11-25 11:08:14 +0000 |
875 | @@ -21,6 +21,9 @@ |
876 | |
877 | #include "player_skeleton.h" |
878 | |
879 | +#include "client_death_observer.h" |
880 | +#include "power/state_controller.h" |
881 | + |
882 | #include <memory> |
883 | |
884 | namespace core |
885 | @@ -35,12 +38,21 @@ |
886 | class PlayerImplementation : public PlayerSkeleton |
887 | { |
888 | public: |
889 | - PlayerImplementation( |
890 | - const std::string& identity, |
891 | - const std::shared_ptr<core::dbus::Bus>& bus, |
892 | - const std::shared_ptr<core::dbus::Object>& session, |
893 | - const std::shared_ptr<Service>& service, |
894 | - PlayerKey key); |
895 | + // All creation time arguments go here |
896 | + struct Configuration |
897 | + { |
898 | + std::string identity; |
899 | + std::shared_ptr<core::dbus::Bus> bus; |
900 | + std::shared_ptr<core::dbus::Object> session; |
901 | + std::shared_ptr<Service> service; |
902 | + PlayerKey key; |
903 | + |
904 | + // Functional dependencies |
905 | + ClientDeathObserver::Ptr client_death_observer; |
906 | + power::StateController::Ptr power_state_controller; |
907 | + }; |
908 | + |
909 | + PlayerImplementation(const Configuration& configuration); |
910 | ~PlayerImplementation(); |
911 | |
912 | virtual std::shared_ptr<TrackList> track_list(); |
913 | |
914 | === added directory 'src/core/media/power' |
915 | === added file 'src/core/media/power/battery_observer.cpp' |
916 | --- src/core/media/power/battery_observer.cpp 1970-01-01 00:00:00 +0000 |
917 | +++ src/core/media/power/battery_observer.cpp 2014-11-25 11:08:14 +0000 |
918 | @@ -0,0 +1,127 @@ |
919 | +/* |
920 | + * Copyright © 2014 Canonical Ltd. |
921 | + * |
922 | + * This program is free software: you can redistribute it and/or modify it |
923 | + * under the terms of the GNU Lesser General Public License version 3, |
924 | + * as published by the Free Software Foundation. |
925 | + * |
926 | + * This program is distributed in the hope that it will be useful, |
927 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
928 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
929 | + * GNU Lesser General Public License for more details. |
930 | + * |
931 | + * You should have received a copy of the GNU Lesser General Public License |
932 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
933 | + * |
934 | + * Authored by: Thomas Voß <thomas.voss@canonical.com> |
935 | + */ |
936 | + |
937 | +#include <core/media/power/battery_observer.h> |
938 | + |
939 | +#include <core/dbus/macros.h> |
940 | +#include <core/dbus/object.h> |
941 | +#include <core/dbus/property.h> |
942 | + |
943 | +namespace media = core::ubuntu::media; |
944 | + |
945 | +namespace com { namespace canonical { namespace indicator { namespace power { |
946 | +struct Battery |
947 | +{ |
948 | + static std::string& name() |
949 | + { |
950 | + static std::string s = "com.canonical.indicator.power.Battery"; |
951 | + return s; |
952 | + } |
953 | + |
954 | + static const core::dbus::types::ObjectPath& path() |
955 | + { |
956 | + static core::dbus::types::ObjectPath p{"/com/canonical/indicator/power/Battery"}; |
957 | + return p; |
958 | + } |
959 | + |
960 | + // Possible values: "ok", "low", "very_low", "critical" |
961 | + DBUS_CPP_READABLE_PROPERTY_DEF(PowerLevel, Battery, std::string) |
962 | + DBUS_CPP_READABLE_PROPERTY_DEF(IsWarning, Battery, bool) |
963 | +}; // IndicatorPower |
964 | +}}}} |
965 | + |
966 | +namespace |
967 | +{ |
968 | +namespace impl |
969 | +{ |
970 | +struct BatteryObserver : public media::power::BatteryObserver |
971 | +{ |
972 | + static core::ubuntu::media::power::Level power_level_from_string(const std::string& s) |
973 | + { |
974 | + static const std::map<std::string, core::ubuntu::media::power::Level> lut = |
975 | + { |
976 | + {"ok", core::ubuntu::media::power::Level::ok}, |
977 | + {"low", core::ubuntu::media::power::Level::low}, |
978 | + {"very_low", core::ubuntu::media::power::Level::very_low}, |
979 | + {"critical", core::ubuntu::media::power::Level::critical} |
980 | + }; |
981 | + |
982 | + if (lut.count(s) == 0) |
983 | + return core::ubuntu::media::power::Level::unknown; |
984 | + |
985 | + return lut.at(s); |
986 | + } |
987 | + |
988 | + BatteryObserver(const core::dbus::Object::Ptr& object) |
989 | + : object{object}, |
990 | + properties |
991 | + { |
992 | + core::Property<core::ubuntu::media::power::Level>{core::ubuntu::media::power::Level::unknown}, |
993 | + object->get_property<com::canonical::indicator::power::Battery::PowerLevel>(), |
994 | + object->get_property<com::canonical::indicator::power::Battery::IsWarning>(), |
995 | + }, |
996 | + connections |
997 | + { |
998 | + properties.power_level->changed().connect([this](const std::string& value) |
999 | + { |
1000 | + properties.typed_power_level = BatteryObserver::power_level_from_string(value); |
1001 | + }) |
1002 | + } |
1003 | + { |
1004 | + } |
1005 | + |
1006 | + const core::Property<core::ubuntu::media::power::Level>& level() const override |
1007 | + { |
1008 | + return properties.typed_power_level; |
1009 | + } |
1010 | + |
1011 | + const core::Property<bool>& is_warning_active() const override |
1012 | + { |
1013 | + return *properties.is_warning; |
1014 | + } |
1015 | + |
1016 | + // The object representing the remote indicator instance. |
1017 | + core::dbus::Object::Ptr object; |
1018 | + // All properties go here. |
1019 | + struct |
1020 | + { |
1021 | + // We have to translate from the raw strings coming in via the bus to |
1022 | + // the strongly typed enumeration exposed via the interface. |
1023 | + core::Property<core::ubuntu::media::power::Level> typed_power_level; |
1024 | + std::shared_ptr<core::dbus::Property<com::canonical::indicator::power::Battery::PowerLevel>> power_level; |
1025 | + |
1026 | + std::shared_ptr<core::dbus::Property<com::canonical::indicator::power::Battery::IsWarning>> is_warning; |
1027 | + } properties; |
1028 | + // Our event connections |
1029 | + struct |
1030 | + { |
1031 | + core::ScopedConnection power_level; |
1032 | + } connections; |
1033 | + |
1034 | +}; |
1035 | +} |
1036 | +} |
1037 | + |
1038 | +media::power::BatteryObserver::Ptr media::power::make_platform_default_battery_observer(media::helper::ExternalServices& es) |
1039 | +{ |
1040 | + auto service = core::dbus::Service::use_service<com::canonical::indicator::power::Battery>(es.session); |
1041 | + auto object = service->object_for_path(com::canonical::indicator::power::Battery::path()); |
1042 | + |
1043 | + return std::make_shared<impl::BatteryObserver>(object); |
1044 | +} |
1045 | + |
1046 | |
1047 | === added file 'src/core/media/power/battery_observer.h' |
1048 | --- src/core/media/power/battery_observer.h 1970-01-01 00:00:00 +0000 |
1049 | +++ src/core/media/power/battery_observer.h 2014-11-25 11:08:14 +0000 |
1050 | @@ -0,0 +1,69 @@ |
1051 | +/* |
1052 | + * Copyright © 2014 Canonical Ltd. |
1053 | + * |
1054 | + * This program is free software: you can redistribute it and/or modify it |
1055 | + * under the terms of the GNU Lesser General Public License version 3, |
1056 | + * as published by the Free Software Foundation. |
1057 | + * |
1058 | + * This program is distributed in the hope that it will be useful, |
1059 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1060 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1061 | + * GNU Lesser General Public License for more details. |
1062 | + * |
1063 | + * You should have received a copy of the GNU Lesser General Public License |
1064 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1065 | + * |
1066 | + * Authored by: Thomas Voß <thomas.voss@canonical.com> |
1067 | + */ |
1068 | +#ifndef CORE_UBUNTU_MEDIA_POWER_STATE_OBSERVER_H_ |
1069 | +#define CORE_UBUNTU_MEDIA_POWER_STATE_OBSERVER_H_ |
1070 | + |
1071 | +#include <core/media/external_services.h> |
1072 | + |
1073 | +#include <core/property.h> |
1074 | + |
1075 | +#include <memory> |
1076 | + |
1077 | +namespace core |
1078 | +{ |
1079 | +namespace ubuntu |
1080 | +{ |
1081 | +namespace media |
1082 | +{ |
1083 | +namespace power |
1084 | +{ |
1085 | +// Enumerates known power levels. |
1086 | +enum class Level |
1087 | +{ |
1088 | + unknown, |
1089 | + ok, |
1090 | + low, |
1091 | + very_low, |
1092 | + critical |
1093 | +}; |
1094 | + |
1095 | +// Interface that enables observation of the system power state. |
1096 | +struct BatteryObserver |
1097 | +{ |
1098 | + // To safe us some typing. |
1099 | + typedef std::shared_ptr<BatteryObserver> Ptr; |
1100 | + |
1101 | + BatteryObserver() = default; |
1102 | + virtual ~BatteryObserver() = default; |
1103 | + |
1104 | + // A getable/observable property reporting the current power-level |
1105 | + // of the system. |
1106 | + virtual const core::Property<Level>& level() const = 0; |
1107 | + // A getable/observable property indicating whether a power-level |
1108 | + // warning is currently presented to the user. |
1109 | + virtual const core::Property<bool>& is_warning_active() const = 0; |
1110 | +}; |
1111 | + |
1112 | +// Creates a BatteryObserver instance that connects to the platform default |
1113 | +// services to observe battery levels. |
1114 | +core::ubuntu::media::power::BatteryObserver::Ptr make_platform_default_battery_observer(core::ubuntu::media::helper::ExternalServices&); |
1115 | +} |
1116 | +} |
1117 | +} |
1118 | +} |
1119 | +#endif // CORE_UBUNTU_MEDIA_POWER_STATE_OBSERVER_H_ |
1120 | |
1121 | === added file 'src/core/media/power/state_controller.cpp' |
1122 | --- src/core/media/power/state_controller.cpp 1970-01-01 00:00:00 +0000 |
1123 | +++ src/core/media/power/state_controller.cpp 2014-11-25 11:08:14 +0000 |
1124 | @@ -0,0 +1,302 @@ |
1125 | +/* |
1126 | + * Copyright © 2014 Canonical Ltd. |
1127 | + * |
1128 | + * This program is free software: you can redistribute it and/or modify it |
1129 | + * under the terms of the GNU Lesser General Public License version 3, |
1130 | + * as published by the Free Software Foundation. |
1131 | + * |
1132 | + * This program is distributed in the hope that it will be useful, |
1133 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1134 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1135 | + * GNU Lesser General Public License for more details. |
1136 | + * |
1137 | + * You should have received a copy of the GNU Lesser General Public License |
1138 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1139 | + * |
1140 | + * Authored by: Thomas Voß <thomas.voss@canonical.com> |
1141 | + */ |
1142 | + |
1143 | +#include <core/media/power/state_controller.h> |
1144 | + |
1145 | +#include <core/dbus/macros.h> |
1146 | +#include <core/dbus/object.h> |
1147 | + |
1148 | +namespace media = core::ubuntu::media; |
1149 | + |
1150 | +namespace com { namespace canonical { |
1151 | +struct Unity |
1152 | +{ |
1153 | + struct Screen |
1154 | + { |
1155 | + static const std::string& name() |
1156 | + { |
1157 | + static std::string s = "com.canonical.Unity.Screen"; |
1158 | + return s; |
1159 | + } |
1160 | + |
1161 | + static const core::dbus::types::ObjectPath& path() |
1162 | + { |
1163 | + static core::dbus::types::ObjectPath p{"/com/canonical/Unity/Screen"}; |
1164 | + return p; |
1165 | + } |
1166 | + |
1167 | + DBUS_CPP_METHOD_DEF(keepDisplayOn, Screen) |
1168 | + DBUS_CPP_METHOD_DEF(removeDisplayOnRequest, Screen) |
1169 | + }; |
1170 | +}; |
1171 | +namespace powerd { |
1172 | +struct Interface |
1173 | +{ |
1174 | + static std::string& name() |
1175 | + { |
1176 | + static std::string s = "com.canonical.powerd"; |
1177 | + return s; |
1178 | + } |
1179 | + |
1180 | + static const core::dbus::types::ObjectPath& path() |
1181 | + { |
1182 | + static core::dbus::types::ObjectPath p{"/com/canonical/powerd"}; |
1183 | + return p; |
1184 | + } |
1185 | + |
1186 | + DBUS_CPP_METHOD_DEF(requestSysState, com::canonical::powerd::Interface) |
1187 | + DBUS_CPP_METHOD_DEF(clearSysState, com::canonical::powerd::Interface) |
1188 | +}; |
1189 | +}}} |
1190 | + |
1191 | +namespace |
1192 | +{ |
1193 | +namespace impl |
1194 | +{ |
1195 | +struct DisplayStateLock : public media::power::StateController::Lock<media::power::DisplayState>, |
1196 | + public std::enable_shared_from_this<DisplayStateLock> |
1197 | +{ |
1198 | + // To safe us some typing |
1199 | + typedef std::shared_ptr<DisplayStateLock> Ptr; |
1200 | + |
1201 | + // We postpone releasing the display for this amount of time. |
1202 | + static boost::posix_time::seconds timeout_for_release() |
1203 | + { |
1204 | + return boost::posix_time::seconds{4}; |
1205 | + } |
1206 | + |
1207 | + // The invalid cookie marker. |
1208 | + static constexpr const std::int32_t the_invalid_cookie{-1}; |
1209 | + |
1210 | + DisplayStateLock(const media::power::StateController::Ptr& parent, |
1211 | + boost::asio::io_service& io_service, |
1212 | + const core::dbus::Object::Ptr& object) |
1213 | + : parent{parent}, |
1214 | + timeout{io_service}, |
1215 | + object{object}, |
1216 | + cookie{the_invalid_cookie} |
1217 | + { |
1218 | + } |
1219 | + |
1220 | + // From core::ubuntu::media::power::StateController::Lock<DisplayState> |
1221 | + void request_acquire(media::power::DisplayState state) override |
1222 | + { |
1223 | + if (state == media::power::DisplayState::off) |
1224 | + return; |
1225 | + |
1226 | + std::weak_ptr<DisplayStateLock> wp{shared_from_this()}; |
1227 | + |
1228 | + object->invoke_method_asynchronously_with_callback<com::canonical::Unity::Screen::keepDisplayOn, std::int32_t>([wp, state](const core::dbus::Result<std::int32_t>& result) |
1229 | + { |
1230 | + if (result.is_error()) // We should at least log the error case here. |
1231 | + return; |
1232 | + |
1233 | + if (auto sp = wp.lock()) |
1234 | + { |
1235 | + sp->cookie = result.value(); |
1236 | + sp->signals.acquired(state); |
1237 | + } |
1238 | + }); |
1239 | + } |
1240 | + |
1241 | + void request_release(media::power::DisplayState state) override |
1242 | + { |
1243 | + if (state == media::power::DisplayState::off) |
1244 | + return; |
1245 | + |
1246 | + if (cookie == the_invalid_cookie) |
1247 | + return; |
1248 | + |
1249 | + std::weak_ptr<DisplayStateLock> wp{shared_from_this()}; |
1250 | + |
1251 | + auto current_cookie(cookie); |
1252 | + |
1253 | + timeout.expires_from_now(timeout_for_release()); |
1254 | + timeout.async_wait([wp, state, current_cookie](const boost::system::error_code& ec) |
1255 | + { |
1256 | + if (not ec) |
1257 | + { |
1258 | + if (auto sp = wp.lock()) |
1259 | + { |
1260 | + sp->object->invoke_method_asynchronously_with_callback<com::canonical::Unity::Screen::removeDisplayOnRequest, void>([wp, state, current_cookie](const core::dbus::Result<void>& result) |
1261 | + { |
1262 | + if (result.is_error()) // We should at least log the error case here. |
1263 | + return; |
1264 | + |
1265 | + if (auto sp = wp.lock()) |
1266 | + { |
1267 | + sp->signals.released(state); |
1268 | + |
1269 | + // We might have issued a different request before and |
1270 | + // only call the display state done if the original cookie |
1271 | + // corresponds to the one we just gave up. |
1272 | + if (sp->cookie == current_cookie) |
1273 | + sp->cookie = the_invalid_cookie; |
1274 | + } |
1275 | + |
1276 | + }, current_cookie); |
1277 | + } |
1278 | + } |
1279 | + }); |
1280 | + } |
1281 | + |
1282 | + // Emitted whenever the acquire request completes. |
1283 | + const core::Signal<media::power::DisplayState>& acquired() const override |
1284 | + { |
1285 | + return signals.acquired; |
1286 | + } |
1287 | + |
1288 | + // Emitted whenever the release request completes. |
1289 | + const core::Signal<media::power::DisplayState>& released() const override |
1290 | + { |
1291 | + return signals.released; |
1292 | + } |
1293 | + |
1294 | + media::power::StateController::Ptr parent; |
1295 | + boost::asio::deadline_timer timeout; |
1296 | + core::dbus::Object::Ptr object; |
1297 | + std::int32_t cookie; |
1298 | + |
1299 | + struct |
1300 | + { |
1301 | + core::Signal<media::power::DisplayState> acquired; |
1302 | + core::Signal<media::power::DisplayState> released; |
1303 | + } signals; |
1304 | +}; |
1305 | + |
1306 | +struct SystemStateLock : public media::power::StateController::Lock<media::power::SystemState> |
1307 | +{ |
1308 | + static constexpr const char* wake_lock_name |
1309 | + { |
1310 | + "media-hub-playback_lock" |
1311 | + }; |
1312 | + |
1313 | + SystemStateLock(const media::power::StateController::Ptr& parent, const core::dbus::Object::Ptr& object) |
1314 | + : parent{parent}, |
1315 | + object{object} |
1316 | + { |
1317 | + } |
1318 | + |
1319 | + // Informs the system that the caller would like |
1320 | + // the system to stay active. |
1321 | + void request_acquire(media::power::SystemState state) override |
1322 | + { |
1323 | + if (state == media::power::SystemState::suspend) |
1324 | + return; |
1325 | + |
1326 | + std::lock_guard<std::mutex> lg{cookie_store_guard}; |
1327 | + if (cookie_store.count(state) > 0) |
1328 | + return; |
1329 | + |
1330 | + object->invoke_method_asynchronously_with_callback<com::canonical::powerd::Interface::requestSysState, std::string>([this, state](const core::dbus::Result<std::string>& result) |
1331 | + { |
1332 | + if (result.is_error()) // TODO(tvoss): We should log the error condition here. |
1333 | + return; |
1334 | + |
1335 | + std::lock_guard<std::mutex> lg{cookie_store_guard}; |
1336 | + |
1337 | + cookie_store[state] = result.value(); |
1338 | + signals.acquired(state); |
1339 | + }, std::string{wake_lock_name}, static_cast<std::int32_t>(state)); |
1340 | + } |
1341 | + |
1342 | + // Informs the system that the caller does not |
1343 | + // require the system to stay active anymore. |
1344 | + void request_release(media::power::SystemState state) override |
1345 | + { |
1346 | + if (state == media::power::SystemState::suspend) |
1347 | + return; |
1348 | + |
1349 | + std::lock_guard<std::mutex> lg{cookie_store_guard}; |
1350 | + |
1351 | + if (cookie_store.count(state) == 0) |
1352 | + return; |
1353 | + |
1354 | + object->invoke_method_asynchronously_with_callback<com::canonical::powerd::Interface::clearSysState, void>([this, state](const core::dbus::Result<void>& result) |
1355 | + { |
1356 | + if (result.is_error()) {/*TODO(tvoss): We should log the error condition here.*/} |
1357 | + |
1358 | + std::lock_guard<std::mutex> lg{cookie_store_guard}; |
1359 | + |
1360 | + cookie_store.erase(state); |
1361 | + signals.released(state); |
1362 | + }, cookie_store.at(state)); |
1363 | + } |
1364 | + |
1365 | + // Emitted whenever the acquire request completes. |
1366 | + const core::Signal<media::power::SystemState>& acquired() const override |
1367 | + { |
1368 | + return signals.acquired; |
1369 | + } |
1370 | + |
1371 | + // Emitted whenever the release request completes. |
1372 | + const core::Signal<media::power::SystemState>& released() const override |
1373 | + { |
1374 | + return signals.released; |
1375 | + } |
1376 | + |
1377 | + std::mutex cookie_store_guard; |
1378 | + std::map<media::power::SystemState, std::string> cookie_store; |
1379 | + media::power::StateController::Ptr parent; |
1380 | + core::dbus::Object::Ptr object; |
1381 | + struct |
1382 | + { |
1383 | + core::Signal<media::power::SystemState> acquired; |
1384 | + core::Signal<media::power::SystemState> released; |
1385 | + } signals; |
1386 | +}; |
1387 | + |
1388 | +struct StateController : public media::power::StateController, |
1389 | + public std::enable_shared_from_this<impl::StateController> |
1390 | +{ |
1391 | + StateController(media::helper::ExternalServices& es) |
1392 | + : external_services{es}, |
1393 | + powerd |
1394 | + { |
1395 | + core::dbus::Service::use_service<com::canonical::powerd::Interface>(external_services.system) |
1396 | + ->object_for_path(com::canonical::powerd::Interface::path()) |
1397 | + }, |
1398 | + unity_screen |
1399 | + { |
1400 | + core::dbus::Service::use_service<com::canonical::Unity::Screen>(external_services.system) |
1401 | + ->object_for_path(com::canonical::Unity::Screen::path()) |
1402 | + } |
1403 | + { |
1404 | + } |
1405 | + |
1406 | + media::power::StateController::Lock<media::power::SystemState>::Ptr system_state_lock() override |
1407 | + { |
1408 | + return std::make_shared<impl::SystemStateLock>(shared_from_this(), powerd); |
1409 | + } |
1410 | + |
1411 | + media::power::StateController::Lock<media::power::DisplayState>::Ptr display_state_lock() override |
1412 | + { |
1413 | + return std::make_shared<impl::DisplayStateLock>(shared_from_this(), external_services.io_service, unity_screen); |
1414 | + } |
1415 | + |
1416 | + media::helper::ExternalServices& external_services; |
1417 | + core::dbus::Object::Ptr powerd; |
1418 | + core::dbus::Object::Ptr unity_screen; |
1419 | +}; |
1420 | +} |
1421 | +} |
1422 | + |
1423 | +media::power::StateController::Ptr media::power::make_platform_default_state_controller(core::ubuntu::media::helper::ExternalServices& external_services) |
1424 | +{ |
1425 | + return std::make_shared<impl::StateController>(external_services); |
1426 | +} |
1427 | |
1428 | === added file 'src/core/media/power/state_controller.h' |
1429 | --- src/core/media/power/state_controller.h 1970-01-01 00:00:00 +0000 |
1430 | +++ src/core/media/power/state_controller.h 2014-11-25 11:08:14 +0000 |
1431 | @@ -0,0 +1,102 @@ |
1432 | +/* |
1433 | + * Copyright © 2014 Canonical Ltd. |
1434 | + * |
1435 | + * This program is free software: you can redistribute it and/or modify it |
1436 | + * under the terms of the GNU Lesser General Public License version 3, |
1437 | + * as published by the Free Software Foundation. |
1438 | + * |
1439 | + * This program is distributed in the hope that it will be useful, |
1440 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1441 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1442 | + * GNU Lesser General Public License for more details. |
1443 | + * |
1444 | + * You should have received a copy of the GNU Lesser General Public License |
1445 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1446 | + * |
1447 | + * Authored by: Thomas Voß <thomas.voss@canonical.com> |
1448 | + */ |
1449 | +#ifndef CORE_UBUNTU_MEDIA_POWER_STATE_CONTROLLER_H_ |
1450 | +#define CORE_UBUNTU_MEDIA_POWER_STATE_CONTROLLER_H_ |
1451 | + |
1452 | +#include <core/media/external_services.h> |
1453 | + |
1454 | +#include <core/property.h> |
1455 | + |
1456 | +#include <memory> |
1457 | + |
1458 | +namespace core |
1459 | +{ |
1460 | +namespace ubuntu |
1461 | +{ |
1462 | +namespace media |
1463 | +{ |
1464 | +namespace power |
1465 | +{ |
1466 | +// Enumerates all known power states of a display. |
1467 | +enum class DisplayState |
1468 | +{ |
1469 | + // The display is off. |
1470 | + off = 0, |
1471 | + // The display is on. |
1472 | + on = 1 |
1473 | +}; |
1474 | + |
1475 | +// Enumerates known power states of the system. |
1476 | +enum class SystemState |
1477 | +{ |
1478 | + // Note that callers will be notified of suspend state changes |
1479 | + // but may not request this state. |
1480 | + suspend = 0, |
1481 | + // The Active state will prevent system suspend |
1482 | + active = 1, |
1483 | + // Substate of Active with disabled proximity based blanking |
1484 | + blank_on_proximity = 2 |
1485 | +}; |
1486 | + |
1487 | +// Interface that enables observation of the system power state. |
1488 | +struct StateController |
1489 | +{ |
1490 | + // To safe us some typing. |
1491 | + typedef std::shared_ptr<StateController> Ptr; |
1492 | + |
1493 | + // When acquired, ensures that the system stays active, |
1494 | + // and decreases the reference count when released. |
1495 | + template<typename State> |
1496 | + struct Lock |
1497 | + { |
1498 | + // To safe us some typing. |
1499 | + typedef std::shared_ptr<Lock> Ptr; |
1500 | + |
1501 | + Lock() = default; |
1502 | + virtual ~Lock() = default; |
1503 | + |
1504 | + // Informs the system that the caller would like |
1505 | + // the system to stay active. |
1506 | + virtual void request_acquire(State state) = 0; |
1507 | + // Informs the system that the caller does not |
1508 | + // require the system to stay active anymore. |
1509 | + virtual void request_release(State state) = 0; |
1510 | + |
1511 | + // Emitted whenever the acquire request completes. |
1512 | + virtual const core::Signal<State>& acquired() const = 0; |
1513 | + // Emitted whenever the release request completes. |
1514 | + virtual const core::Signal<State>& released() const = 0; |
1515 | + }; |
1516 | + |
1517 | + StateController() = default; |
1518 | + virtual ~StateController() = default; |
1519 | + |
1520 | + // Returns a power::StateController::Lock<DisplayState> instance. |
1521 | + virtual Lock<DisplayState>::Ptr display_state_lock() = 0; |
1522 | + // Returns a power::StateController::Lock<SystemState> instance. |
1523 | + virtual Lock<SystemState>::Ptr system_state_lock() = 0; |
1524 | +}; |
1525 | + |
1526 | +// Creates a StateController instance that connects to the platform default |
1527 | +// services to control system and display power states. |
1528 | +StateController::Ptr make_platform_default_state_controller(core::ubuntu::media::helper::ExternalServices&); |
1529 | +} |
1530 | +} |
1531 | +} |
1532 | +} |
1533 | +#endif // CORE_UBUNTU_MEDIA_POWER_STATE_CONTROLLER_H_ |
1534 | |
1535 | === removed file 'src/core/media/powerd_service.h' |
1536 | --- src/core/media/powerd_service.h 2014-08-08 14:36:29 +0000 |
1537 | +++ src/core/media/powerd_service.h 1970-01-01 00:00:00 +0000 |
1538 | @@ -1,73 +0,0 @@ |
1539 | -/* |
1540 | - * Copyright (C) 2014 Canonical Ltd |
1541 | - * |
1542 | - * This program is free software: you can redistribute it and/or modify |
1543 | - * it under the terms of the GNU Lesser General Public License version 3 as |
1544 | - * published by the Free Software Foundation. |
1545 | - * |
1546 | - * This program is distributed in the hope that it will be useful, |
1547 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1548 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1549 | - * GNU Lesser General Public License for more details. |
1550 | - * |
1551 | - * You should have received a copy of the GNU Lesser General Public License |
1552 | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1553 | - * |
1554 | - * Author: Ricardo Mendoza <ricardo.mendoza@canonical.com> |
1555 | - */ |
1556 | - |
1557 | -#include <core/dbus/dbus.h> |
1558 | -#include <core/dbus/fixture.h> |
1559 | -#include <core/dbus/object.h> |
1560 | -#include <core/dbus/property.h> |
1561 | -#include <core/dbus/service.h> |
1562 | -#include <core/dbus/interfaces/properties.h> |
1563 | -#include <core/dbus/types/stl/tuple.h> |
1564 | -#include <core/dbus/types/stl/vector.h> |
1565 | - |
1566 | -#include <core/dbus/asio/executor.h> |
1567 | - |
1568 | -#include <string> |
1569 | -#include <vector> |
1570 | -#include <chrono> |
1571 | - |
1572 | -namespace core |
1573 | -{ |
1574 | - |
1575 | -struct Powerd |
1576 | -{ |
1577 | - static std::string& name() |
1578 | - { |
1579 | - static std::string s = "com.canonical.powerd"; |
1580 | - return s; |
1581 | - } |
1582 | - |
1583 | - struct requestSysState |
1584 | - { |
1585 | - static std::string name() |
1586 | - { |
1587 | - static std::string s = "requestSysState"; |
1588 | - return s; |
1589 | - } |
1590 | - |
1591 | - static const std::chrono::milliseconds default_timeout() { return std::chrono::seconds{1}; } |
1592 | - |
1593 | - typedef Powerd Interface; |
1594 | - }; |
1595 | - |
1596 | - struct clearSysState |
1597 | - { |
1598 | - static std::string name() |
1599 | - { |
1600 | - static std::string s = "clearSysState"; |
1601 | - return s; |
1602 | - } |
1603 | - |
1604 | - static const std::chrono::milliseconds default_timeout() { return std::chrono::seconds{1}; } |
1605 | - |
1606 | - typedef Powerd Interface; |
1607 | - }; |
1608 | - |
1609 | -}; |
1610 | - |
1611 | -} |
1612 | |
1613 | === modified file 'src/core/media/server/server.cpp' |
1614 | --- src/core/media/server/server.cpp 2014-11-25 11:08:14 +0000 |
1615 | +++ src/core/media/server/server.cpp 2014-11-25 11:08:14 +0000 |
1616 | @@ -20,8 +20,12 @@ |
1617 | #include <core/media/player.h> |
1618 | #include <core/media/track_list.h> |
1619 | |
1620 | +#include <core/posix/signal.h> |
1621 | + |
1622 | #include "core/media/service_implementation.h" |
1623 | |
1624 | +#include "core/media/audio/pulse_audio_output_observer.h" |
1625 | + |
1626 | #include <iostream> |
1627 | |
1628 | namespace media = core::ubuntu::media; |
1629 | @@ -52,12 +56,104 @@ |
1630 | |
1631 | int main() |
1632 | { |
1633 | + auto trap = core::posix::trap_signals_for_all_subsequent_threads( |
1634 | + { |
1635 | + core::posix::Signal::sig_int, |
1636 | + core::posix::Signal::sig_term |
1637 | + }); |
1638 | + |
1639 | + trap->signal_raised().connect([trap](core::posix::Signal) |
1640 | + { |
1641 | + trap->stop(); |
1642 | + }); |
1643 | + |
1644 | + // Init platform-specific functionality. |
1645 | platform_init(); |
1646 | |
1647 | cout << "Starting DecodingService..." << endl; |
1648 | |
1649 | - auto service = std::make_shared<media::ServiceImplementation>(); |
1650 | - service->run(); |
1651 | + // We keep track of our state. |
1652 | + bool shutdown_requested{false}; |
1653 | + |
1654 | + // Our helper for connecting to external services. |
1655 | + core::ubuntu::media::helper::ExternalServices external_services; |
1656 | + |
1657 | + std::thread external_services_worker |
1658 | + { |
1659 | + [&shutdown_requested, &external_services]() |
1660 | + { |
1661 | + while (not shutdown_requested) |
1662 | + { |
1663 | + try |
1664 | + { |
1665 | + external_services.io_service.run(); |
1666 | + } |
1667 | + catch (const std::exception& e) |
1668 | + { |
1669 | + std::cerr << "Error while executing the underlying io_service: " << e.what() << std::endl; |
1670 | + } |
1671 | + catch (...) |
1672 | + { |
1673 | + std::cerr << "Error while executing the underlying io_service." << std::endl; |
1674 | + } |
1675 | + } |
1676 | + } |
1677 | + }; |
1678 | + |
1679 | + // We assemble the configuration for executing the service now. |
1680 | + media::ServiceImplementation::Configuration service_config |
1681 | + { |
1682 | + [&external_services](core::ubuntu::media::Task task) |
1683 | + { |
1684 | + external_services.io_service.post(task); |
1685 | + }, |
1686 | + media::platform_default_client_death_observer(), |
1687 | + media::make_platform_default_recorder_observer(), |
1688 | + media::audio::make_platform_default_output_observer(), |
1689 | + media::power::make_platform_default_state_controller(external_services), |
1690 | + media::power::make_platform_default_battery_observer(external_services), |
1691 | + media::telephony::make_platform_default_call_monitor() |
1692 | + }; |
1693 | + |
1694 | + auto service = std::make_shared<media::ServiceImplementation>(service_config); |
1695 | + |
1696 | + std::thread service_worker |
1697 | + { |
1698 | + [&shutdown_requested, service]() |
1699 | + { |
1700 | + while (not shutdown_requested) |
1701 | + { |
1702 | + try |
1703 | + { |
1704 | + service->run(); |
1705 | + } |
1706 | + catch (const std::exception& e) |
1707 | + { |
1708 | + std::cerr << "Recoverable error while executing the service: " << e.what() << std::endl; |
1709 | + } |
1710 | + catch (...) |
1711 | + { |
1712 | + std::cerr << "Recoverable error while executing the service." << std::endl; |
1713 | + } |
1714 | + } |
1715 | + } |
1716 | + }; |
1717 | + |
1718 | + // We block on waiting for signals telling us to gracefully shutdown. |
1719 | + trap->run(); |
1720 | + |
1721 | + // Inform our workers that we should shutdown gracefully |
1722 | + shutdown_requested = true; |
1723 | + |
1724 | + // And stop execution of helper and actual service. |
1725 | + external_services.stop(); |
1726 | + service->stop(); |
1727 | + |
1728 | + if (external_services_worker.joinable()) |
1729 | + external_services_worker.join(); |
1730 | + |
1731 | + if (service_worker.joinable()) |
1732 | + service_worker.join(); |
1733 | |
1734 | return 0; |
1735 | } |
1736 | |
1737 | === modified file 'src/core/media/service_implementation.cpp' |
1738 | --- src/core/media/service_implementation.cpp 2014-11-25 11:08:14 +0000 |
1739 | +++ src/core/media/service_implementation.cpp 2014-11-25 11:08:14 +0000 |
1740 | @@ -22,11 +22,8 @@ |
1741 | |
1742 | #include "service_implementation.h" |
1743 | |
1744 | -#include "indicator_power_service.h" |
1745 | -#include "call-monitor/call_monitor.h" |
1746 | #include "player_configuration.h" |
1747 | #include "player_implementation.h" |
1748 | -#include "recorder_observer.h" |
1749 | |
1750 | #include <boost/asio.hpp> |
1751 | |
1752 | @@ -40,7 +37,6 @@ |
1753 | #include <pulse/pulseaudio.h> |
1754 | |
1755 | #include "util/timeout.h" |
1756 | -#include "unity_screen_service.h" |
1757 | |
1758 | namespace media = core::ubuntu::media; |
1759 | |
1760 | @@ -48,462 +44,117 @@ |
1761 | |
1762 | struct media::ServiceImplementation::Private |
1763 | { |
1764 | - Private() |
1765 | - : resume_key(std::numeric_limits<std::uint32_t>::max()), |
1766 | - keep_alive(io_service), |
1767 | - disp_cookie(0), |
1768 | - pulse_mainloop_api(nullptr), |
1769 | - pulse_context(nullptr), |
1770 | - headphones_connected(false), |
1771 | - a2dp_connected(false), |
1772 | - primary_idx(-1), |
1773 | - call_monitor(new CallMonitor) |
1774 | - { |
1775 | - bus = std::shared_ptr<dbus::Bus>(new dbus::Bus(core::dbus::WellKnownBus::session)); |
1776 | - bus->install_executor(dbus::asio::make_executor(bus, io_service)); |
1777 | - worker = std::move(std::thread([this]() |
1778 | - { |
1779 | - bus->run(); |
1780 | - })); |
1781 | - |
1782 | - // Spawn pulse watchdog |
1783 | - pulse_mainloop = nullptr; |
1784 | - pulse_worker = std::move(std::thread([this]() |
1785 | - { |
1786 | - std::unique_lock<std::mutex> lk(pulse_mutex); |
1787 | - pcv.wait(lk, |
1788 | - [this]{ |
1789 | - if (pulse_mainloop != nullptr || pulse_context != nullptr) |
1790 | - { |
1791 | - // We come from instance death, destroy and create. |
1792 | - if (pulse_context != nullptr) |
1793 | - { |
1794 | - pa_threaded_mainloop_lock(pulse_mainloop); |
1795 | - pa_operation *o; |
1796 | - o = pa_context_drain(pulse_context, |
1797 | - [](pa_context *context, void *userdata) |
1798 | - { |
1799 | - (void) context; |
1800 | - |
1801 | - Private *p = reinterpret_cast<Private*>(userdata); |
1802 | - pa_threaded_mainloop_signal(p->mainloop(), 0); |
1803 | - }, this); |
1804 | - |
1805 | - if (o) |
1806 | - { |
1807 | - while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) |
1808 | - pa_threaded_mainloop_wait(pulse_mainloop); |
1809 | - |
1810 | - pa_operation_unref(o); |
1811 | - } |
1812 | - |
1813 | - pa_context_set_state_callback(pulse_context, NULL, NULL); |
1814 | - pa_context_set_subscribe_callback(pulse_context, NULL, NULL); |
1815 | - pa_context_disconnect(pulse_context); |
1816 | - pa_context_unref(pulse_context); |
1817 | - pulse_context = nullptr; |
1818 | - pa_threaded_mainloop_unlock(pulse_mainloop); |
1819 | - } |
1820 | - } |
1821 | - |
1822 | - if (pulse_mainloop == nullptr) |
1823 | - { |
1824 | - pulse_mainloop = pa_threaded_mainloop_new(); |
1825 | - |
1826 | - if (pa_threaded_mainloop_start(pulse_mainloop) != 0) |
1827 | - { |
1828 | - std::cerr << "Unable to start pulseaudio mainloop, audio output detection will not function" << std::endl; |
1829 | - pa_threaded_mainloop_free(pulse_mainloop); |
1830 | - pulse_mainloop = nullptr; |
1831 | - } |
1832 | - } |
1833 | - |
1834 | - do { |
1835 | - create_pulse_context(); |
1836 | - } while (pulse_context == nullptr); |
1837 | - |
1838 | - // Wait for next instance death. |
1839 | - return false; |
1840 | - }); |
1841 | - })); |
1842 | - |
1843 | - // Connect the property change signal that will allow media-hub to take appropriate action |
1844 | - // when the battery level reaches critical |
1845 | - auto stub_service = dbus::Service::use_service(bus, "com.canonical.indicator.power"); |
1846 | - indicator_power_session = stub_service->object_for_path(dbus::types::ObjectPath("/com/canonical/indicator/power/Battery")); |
1847 | - power_level = indicator_power_session->get_property<core::IndicatorPower::PowerLevel>(); |
1848 | - is_warning = indicator_power_session->get_property<core::IndicatorPower::IsWarning>(); |
1849 | - |
1850 | - // Obtain session with Unity.Screen so that we request state when doing recording |
1851 | - auto bus = std::shared_ptr<dbus::Bus>(new dbus::Bus(core::dbus::WellKnownBus::system)); |
1852 | - bus->install_executor(dbus::asio::make_executor(bus)); |
1853 | - |
1854 | - auto uscreen_stub_service = dbus::Service::use_service(bus, dbus::traits::Service<core::UScreen>::interface_name()); |
1855 | - uscreen_session = uscreen_stub_service->object_for_path(dbus::types::ObjectPath("/com/canonical/Unity/Screen")); |
1856 | - |
1857 | - recorder_observer = media::make_platform_default_recorder_observer(); |
1858 | - recorder_observer->recording_state().changed().connect([this](media::RecordingState state) |
1859 | - { |
1860 | - media_recording_state_changed(state); |
1861 | - }); |
1862 | + Private(ServiceImplementation& parent, const media::ServiceImplementation::Configuration& config) |
1863 | + : resume_key{std::numeric_limits<std::uint32_t>::max()}, |
1864 | + parent{parent}, |
1865 | + dispatcher{config.dispatcher}, |
1866 | + display_state_lock{config.power_state_controller->display_state_lock()}, |
1867 | + power_state_controller{config.power_state_controller}, |
1868 | + power_state_observer{config.power_state_observer}, |
1869 | + audio_output_observer{config.audio_output_observer}, |
1870 | + call_monitor(config.call_monitor), |
1871 | + client_death_observer(config.client_death_observer), |
1872 | + recorder_observer(config.recorder_observer), |
1873 | + connections |
1874 | + { |
1875 | + power_state_observer->level().changed().connect([this](const core::ubuntu::media::power::Level& level) |
1876 | + { |
1877 | + // When the battery level hits 10% or 5%, pause all multimedia sessions. |
1878 | + // Playback will resume when the user clears the presented notification. |
1879 | + switch (level) |
1880 | + { |
1881 | + case core::ubuntu::media::power::Level::low: |
1882 | + case core::ubuntu::media::power::Level::very_low: |
1883 | + this->parent.pause_all_multimedia_sessions(); |
1884 | + break; |
1885 | + default: |
1886 | + break; |
1887 | + } |
1888 | + }), |
1889 | + power_state_observer->is_warning_active().changed().connect([this](bool notifying) |
1890 | + { |
1891 | + // If the low battery level notification is no longer being displayed, |
1892 | + // resume what the user was previously playing |
1893 | + if (!notifying) |
1894 | + this->parent.resume_multimedia_session(); |
1895 | + }), |
1896 | + call_monitor->on_call_state_changed().connect([this](media::telephony::CallMonitor::State state) { |
1897 | + switch (state) { |
1898 | + case media::telephony::CallMonitor::OffHook: |
1899 | + this->parent.pause_all_multimedia_sessions(); |
1900 | + break; |
1901 | + case media::telephony::CallMonitor::OnHook: |
1902 | + this->parent.resume_paused_multimedia_sessions(); |
1903 | + break; |
1904 | + } |
1905 | + }), |
1906 | + recorder_observer->recording_state().changed().connect([this](media::RecordingState state) |
1907 | + { |
1908 | + media_recording_state_changed(state); |
1909 | + }), |
1910 | + audio_output_observer->external_output_state().changed().connect([this](media::audio::OutputState state) |
1911 | + { |
1912 | + switch (state) |
1913 | + { |
1914 | + case media::audio::OutputState::disconnected: |
1915 | + this->parent.pause_all_multimedia_sessions(); |
1916 | + break; |
1917 | + case media::audio::OutputState::connected: |
1918 | + break; |
1919 | + } |
1920 | + }) |
1921 | + } |
1922 | + { |
1923 | } |
1924 | |
1925 | ~Private() |
1926 | - { |
1927 | - release_pulse_context(); |
1928 | - |
1929 | - if (pulse_mainloop != nullptr) |
1930 | - { |
1931 | - pa_threaded_mainloop_stop(pulse_mainloop); |
1932 | - pa_threaded_mainloop_free(pulse_mainloop); |
1933 | - pulse_mainloop = nullptr; |
1934 | - } |
1935 | - |
1936 | - bus->stop(); |
1937 | - |
1938 | - if (worker.joinable()) |
1939 | - worker.join(); |
1940 | - |
1941 | - if (pulse_worker.joinable()) |
1942 | - pulse_worker.join(); |
1943 | + { |
1944 | } |
1945 | |
1946 | void media_recording_state_changed(media::RecordingState state) |
1947 | { |
1948 | - if (uscreen_session == nullptr) |
1949 | - return; |
1950 | - |
1951 | - if (state == media::RecordingState::started) |
1952 | - { |
1953 | - if (disp_cookie > 0) |
1954 | - return; |
1955 | - |
1956 | - auto result = uscreen_session->invoke_method_synchronously<core::UScreen::keepDisplayOn, int>(); |
1957 | - if (result.is_error()) |
1958 | - throw std::runtime_error(result.error().print()); |
1959 | - disp_cookie = result.value(); |
1960 | - } |
1961 | - else if (state == media::RecordingState::stopped) |
1962 | - { |
1963 | - if (disp_cookie != -1) |
1964 | - { |
1965 | - timeout(4000, true, [this](){ |
1966 | - this->uscreen_session->invoke_method_synchronously<core::UScreen::removeDisplayOnRequest, void>(this->disp_cookie); |
1967 | - this->disp_cookie = -1; |
1968 | - }); |
1969 | - } |
1970 | - } |
1971 | - } |
1972 | - |
1973 | - pa_threaded_mainloop *mainloop() |
1974 | - { |
1975 | - return pulse_mainloop; |
1976 | - } |
1977 | - |
1978 | - bool is_port_available(pa_card_port_info **ports, uint32_t n_ports, const char *name) |
1979 | - { |
1980 | - bool ret = false; |
1981 | - |
1982 | - if (ports != nullptr && n_ports > 0 && name != nullptr) |
1983 | - { |
1984 | - for (uint32_t i=0; i<n_ports; i++) |
1985 | - { |
1986 | - if (strstr(ports[i]->name, name) != nullptr && ports[i]->available != PA_PORT_AVAILABLE_NO) |
1987 | - { |
1988 | - ret = true; |
1989 | - break; |
1990 | - } |
1991 | - } |
1992 | - } |
1993 | - |
1994 | - return ret; |
1995 | - } |
1996 | - |
1997 | - void update_wired_output() |
1998 | - { |
1999 | - const pa_operation *o = pa_context_get_card_info_by_index(pulse_context, primary_idx, |
2000 | - [](pa_context *context, const pa_card_info *info, int eol, void *userdata) |
2001 | - { |
2002 | - (void) context; |
2003 | - (void) eol; |
2004 | - |
2005 | - if (info == nullptr || userdata == nullptr) |
2006 | - return; |
2007 | - |
2008 | - Private *p = reinterpret_cast<Private*>(userdata); |
2009 | - if (p->is_port_available(info->ports, info->n_ports, "output-wired")) |
2010 | - { |
2011 | - if (!p->headphones_connected) |
2012 | - std::cout << "Wired headphones connected" << std::endl; |
2013 | - p->headphones_connected = true; |
2014 | - } |
2015 | - else if (p->headphones_connected == true) |
2016 | - { |
2017 | - std::cout << "Wired headphones disconnected" << std::endl; |
2018 | - p->headphones_connected = false; |
2019 | - p->pause_playback_if_necessary(std::get<0>(p->active_sink)); |
2020 | - } |
2021 | - }, this); |
2022 | - (void) o; |
2023 | - } |
2024 | - |
2025 | - void pause_playback_if_necessary(int index) |
2026 | - { |
2027 | - // Catch uninitialized case (active index == -1) |
2028 | - if (std::get<0>(active_sink) == -1) |
2029 | - return; |
2030 | - |
2031 | - if (headphones_connected) |
2032 | - return; |
2033 | - |
2034 | - // No headphones/fallback on primary sink? Pause. |
2035 | - if (index == primary_idx) |
2036 | - pause_playback(); |
2037 | - } |
2038 | - |
2039 | - void set_active_sink(const char *name) |
2040 | - { |
2041 | - const pa_operation *o = pa_context_get_sink_info_by_name(pulse_context, name, |
2042 | - [](pa_context *context, const pa_sink_info *i, int eol, void *userdata) |
2043 | - { |
2044 | - (void) context; |
2045 | - |
2046 | - if (eol) |
2047 | - return; |
2048 | - |
2049 | - Private *p = reinterpret_cast<Private*>(userdata); |
2050 | - std::tuple<uint32_t, uint32_t, std::string> new_sink(std::make_tuple(i->index, i->card, i->name)); |
2051 | - |
2052 | - printf("pulsesink: active_sink=('%s',%d,%d) -> ('%s',%d,%d)\n", |
2053 | - std::get<2>(p->active_sink).c_str(), std::get<0>(p->active_sink), |
2054 | - std::get<1>(p->active_sink), i->name, i->index, i->card); |
2055 | - |
2056 | - p->pause_playback_if_necessary(i->index); |
2057 | - p->active_sink = new_sink; |
2058 | - }, this); |
2059 | - |
2060 | - (void) o; |
2061 | - } |
2062 | - |
2063 | - void update_active_sink() |
2064 | - { |
2065 | - const pa_operation *o = pa_context_get_server_info(pulse_context, |
2066 | - [](pa_context *context, const pa_server_info *i, void *userdata) |
2067 | - { |
2068 | - (void) context; |
2069 | - |
2070 | - Private *p = reinterpret_cast<Private*>(userdata); |
2071 | - if (i->default_sink_name != std::get<2>(p->active_sink)) |
2072 | - p->set_active_sink(i->default_sink_name); |
2073 | - p->update_wired_output(); |
2074 | - }, this); |
2075 | - |
2076 | - (void) o; |
2077 | - } |
2078 | - |
2079 | - void create_pulse_context() |
2080 | - { |
2081 | - if (pulse_context != nullptr) |
2082 | - return; |
2083 | - |
2084 | - active_sink = std::make_tuple(-1, -1, ""); |
2085 | - |
2086 | - bool keep_going = true, ok = true; |
2087 | - |
2088 | - pulse_mainloop_api = pa_threaded_mainloop_get_api(pulse_mainloop); |
2089 | - pa_threaded_mainloop_lock(pulse_mainloop); |
2090 | - |
2091 | - pulse_context = pa_context_new(pulse_mainloop_api, "MediaHubPulseContext"); |
2092 | - pa_context_set_state_callback(pulse_context, |
2093 | - [](pa_context *context, void *userdata) |
2094 | - { |
2095 | - (void) context; |
2096 | - Private *p = reinterpret_cast<Private*>(userdata); |
2097 | - // Signals the pa_threaded_mainloop_wait below to proceed |
2098 | - pa_threaded_mainloop_signal(p->mainloop(), 0); |
2099 | - }, this); |
2100 | - |
2101 | - if (pulse_context == nullptr) |
2102 | - { |
2103 | - std::cerr << "Unable to create new pulseaudio context" << std::endl; |
2104 | - pa_threaded_mainloop_unlock(pulse_mainloop); |
2105 | - return; |
2106 | - } |
2107 | - |
2108 | - pa_context_connect(pulse_context, nullptr, pa_context_flags_t((int) PA_CONTEXT_NOAUTOSPAWN | (int) PA_CONTEXT_NOFAIL), nullptr); |
2109 | - pa_threaded_mainloop_wait(pulse_mainloop); |
2110 | - |
2111 | - while (keep_going) |
2112 | - { |
2113 | - switch (pa_context_get_state(pulse_context)) |
2114 | - { |
2115 | - case PA_CONTEXT_CONNECTING: // Wait for service to be available |
2116 | - case PA_CONTEXT_AUTHORIZING: |
2117 | - case PA_CONTEXT_SETTING_NAME: |
2118 | - break; |
2119 | - |
2120 | - case PA_CONTEXT_READY: |
2121 | - std::cout << "Pulseaudio connection established." << std::endl; |
2122 | - keep_going = false; |
2123 | - break; |
2124 | - |
2125 | - case PA_CONTEXT_FAILED: |
2126 | - case PA_CONTEXT_TERMINATED: |
2127 | - keep_going = false; |
2128 | - ok = false; |
2129 | - break; |
2130 | - |
2131 | - default: |
2132 | - std::cerr << "Pulseaudio connection failure: " << pa_strerror(pa_context_errno(pulse_context)); |
2133 | - keep_going = false; |
2134 | - ok = false; |
2135 | - } |
2136 | - |
2137 | - if (keep_going) |
2138 | - pa_threaded_mainloop_wait(pulse_mainloop); |
2139 | - } |
2140 | - |
2141 | - if (ok) |
2142 | - { |
2143 | - pa_context_set_state_callback(pulse_context, |
2144 | - [](pa_context *context, void *userdata) |
2145 | - { |
2146 | - (void) context; |
2147 | - (void) userdata; |
2148 | - Private *p = reinterpret_cast<Private*>(userdata); |
2149 | - std::unique_lock<std::mutex> lk(p->pulse_mutex); |
2150 | - switch (pa_context_get_state(context)) |
2151 | - { |
2152 | - case PA_CONTEXT_FAILED: |
2153 | - case PA_CONTEXT_TERMINATED: |
2154 | - p->pcv.notify_all(); |
2155 | - break; |
2156 | - default: |
2157 | - break; |
2158 | - } |
2159 | - }, this); |
2160 | - |
2161 | - //FIXME: Get index for "sink.primary", the default onboard card on Touch. |
2162 | - pa_context_get_sink_info_by_name(pulse_context, "sink.primary", |
2163 | - [](pa_context *context, const pa_sink_info *i, int eol, void *userdata) |
2164 | - { |
2165 | - (void) context; |
2166 | - |
2167 | - if (eol) |
2168 | - return; |
2169 | - |
2170 | - Private *p = reinterpret_cast<Private*>(userdata); |
2171 | - p->primary_idx = i->index; |
2172 | - p->update_wired_output(); |
2173 | - }, this); |
2174 | - |
2175 | - update_active_sink(); |
2176 | - |
2177 | - pa_context_set_subscribe_callback(pulse_context, |
2178 | - [](pa_context *context, pa_subscription_event_type_t t, uint32_t idx, void *userdata) |
2179 | - { |
2180 | - (void) context; |
2181 | - (void) idx; |
2182 | - |
2183 | - if (userdata == nullptr) |
2184 | - return; |
2185 | - |
2186 | - Private *p = reinterpret_cast<Private*>(userdata); |
2187 | - if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) |
2188 | - { |
2189 | - p->update_active_sink(); |
2190 | - } |
2191 | - }, this); |
2192 | - pa_context_subscribe(pulse_context, PA_SUBSCRIPTION_MASK_SINK, nullptr, this); |
2193 | - } |
2194 | - else |
2195 | - { |
2196 | - std::cerr << "Connection to pulseaudio failed or was dropped." << std::endl; |
2197 | - pa_context_unref(pulse_context); |
2198 | - pulse_context = nullptr; |
2199 | - } |
2200 | - |
2201 | - pa_threaded_mainloop_unlock(pulse_mainloop); |
2202 | - } |
2203 | - |
2204 | - void release_pulse_context() |
2205 | - { |
2206 | - if (pulse_context != nullptr) |
2207 | - { |
2208 | - pa_threaded_mainloop_lock(pulse_mainloop); |
2209 | - pa_context_disconnect(pulse_context); |
2210 | - pa_context_unref(pulse_context); |
2211 | - pa_threaded_mainloop_unlock(pulse_mainloop); |
2212 | - pulse_context = nullptr; |
2213 | + switch (state) |
2214 | + { |
2215 | + case media::RecordingState::started: |
2216 | + display_state_lock->request_acquire(media::power::DisplayState::on); |
2217 | + break; |
2218 | + case media::RecordingState::stopped: |
2219 | + display_state_lock->request_release(media::power::DisplayState::on); |
2220 | + break; |
2221 | } |
2222 | } |
2223 | |
2224 | // This holds the key of the multimedia role Player instance that was paused |
2225 | // when the battery level reached 10% or 5% |
2226 | media::Player::PlayerKey resume_key; |
2227 | - std::thread worker; |
2228 | - dbus::Bus::Ptr bus; |
2229 | - boost::asio::io_service io_service; |
2230 | - boost::asio::io_service::work keep_alive; |
2231 | - std::shared_ptr<dbus::Object> indicator_power_session; |
2232 | - std::shared_ptr<core::dbus::Property<core::IndicatorPower::PowerLevel>> power_level; |
2233 | - std::shared_ptr<core::dbus::Property<core::IndicatorPower::IsWarning>> is_warning; |
2234 | - int disp_cookie; |
2235 | - std::shared_ptr<dbus::Object> uscreen_session; |
2236 | + |
2237 | + media::ServiceImplementation& parent; |
2238 | + |
2239 | + media::Dispatcher dispatcher; |
2240 | + |
2241 | + media::power::StateController::Lock<media::power::DisplayState>::Ptr display_state_lock; |
2242 | + media::power::StateController::Ptr power_state_controller; |
2243 | + media::power::BatteryObserver::Ptr power_state_observer; |
2244 | + media::audio::OutputObserver::Ptr audio_output_observer; |
2245 | + media::telephony::CallMonitor::Ptr call_monitor; |
2246 | + media::ClientDeathObserver::Ptr client_death_observer; |
2247 | media::RecorderObserver::Ptr recorder_observer; |
2248 | - // Pulse-specific |
2249 | - pa_mainloop_api *pulse_mainloop_api; |
2250 | - pa_threaded_mainloop *pulse_mainloop; |
2251 | - pa_context *pulse_context; |
2252 | - std::thread pulse_worker; |
2253 | - std::mutex pulse_mutex; |
2254 | - std::condition_variable pcv; |
2255 | - bool headphones_connected; |
2256 | - bool a2dp_connected; |
2257 | - std::tuple<int, int, std::string> active_sink; |
2258 | - int primary_idx; |
2259 | + // All event connections go here. |
2260 | + struct |
2261 | + { |
2262 | + core::ScopedConnection power_level; |
2263 | + core::ScopedConnection is_warning_active; |
2264 | + core::ScopedConnection call_state; |
2265 | + core::ScopedConnection recording_state; |
2266 | + core::ScopedConnection external_audio_output_state; |
2267 | + } connections; |
2268 | |
2269 | - // Gets signaled when both the headphone jack is removed or an A2DP device is |
2270 | - // disconnected and playback needs pausing |
2271 | - core::Signal<void> pause_playback; |
2272 | - std::unique_ptr<CallMonitor> call_monitor; |
2273 | std::list<media::Player::PlayerKey> paused_sessions; |
2274 | }; |
2275 | |
2276 | -media::ServiceImplementation::ServiceImplementation() : d(new Private()) |
2277 | +media::ServiceImplementation::ServiceImplementation(const media::ServiceImplementation::Configuration& config) |
2278 | + : d(new Private(*this, config)) |
2279 | { |
2280 | - d->power_level->changed().connect([this](const core::IndicatorPower::PowerLevel::ValueType &level) |
2281 | - { |
2282 | - // When the battery level hits 10% or 5%, pause all multimedia sessions. |
2283 | - // Playback will resume when the user clears the presented notification. |
2284 | - if (level == "low" || level == "very_low") |
2285 | - pause_all_multimedia_sessions(); |
2286 | - }); |
2287 | - |
2288 | - d->is_warning->changed().connect([this](const core::IndicatorPower::IsWarning::ValueType ¬ifying) |
2289 | - { |
2290 | - // If the low battery level notification is no longer being displayed, |
2291 | - // resume what the user was previously playing |
2292 | - if (!notifying) |
2293 | - resume_multimedia_session(); |
2294 | - }); |
2295 | - |
2296 | - d->pause_playback.connect([this]() |
2297 | - { |
2298 | - std::cout << "Got pause_playback signal, pausing all multimedia sessions" << std::endl; |
2299 | - pause_all_multimedia_sessions(); |
2300 | - }); |
2301 | - |
2302 | - d->call_monitor->on_change([this](CallMonitor::State state) { |
2303 | - switch (state) { |
2304 | - case CallMonitor::OffHook: |
2305 | - pause_all_multimedia_sessions(); |
2306 | - break; |
2307 | - case CallMonitor::OnHook: |
2308 | - resume_paused_multimedia_sessions(); |
2309 | - break; |
2310 | - } |
2311 | - }); |
2312 | } |
2313 | |
2314 | media::ServiceImplementation::~ServiceImplementation() |
2315 | @@ -513,20 +164,34 @@ |
2316 | std::shared_ptr<media::Player> media::ServiceImplementation::create_session( |
2317 | const media::Player::Configuration& conf) |
2318 | { |
2319 | - auto player = std::make_shared<media::PlayerImplementation>( |
2320 | - conf.identity, conf.bus, conf.session, shared_from_this(), conf.key); |
2321 | + media::PlayerImplementation::Configuration player_config |
2322 | + { |
2323 | + conf.identity, |
2324 | + conf.bus, |
2325 | + conf.session, |
2326 | + ServiceImplementation::shared_from_this(), |
2327 | + conf.key, |
2328 | + d->client_death_observer, |
2329 | + d->power_state_controller |
2330 | + }; |
2331 | + |
2332 | + auto player = std::make_shared<media::PlayerImplementation>(player_config); |
2333 | |
2334 | auto key = conf.key; |
2335 | - player->on_client_disconnected().connect([this, key]() |
2336 | + |
2337 | + // We hold a reference to ourselves. |
2338 | + auto sp = ServiceImplementation::shared_from_this(); |
2339 | + |
2340 | + player->on_client_disconnected().connect([sp, key]() |
2341 | { |
2342 | // Call remove_player_for_key asynchronously otherwise deadlock can occur |
2343 | // if called within this dispatcher context. |
2344 | // remove_player_for_key can destroy the player instance which in turn |
2345 | // destroys the "on_client_disconnected" signal whose destructor will wait |
2346 | // until all dispatches are done |
2347 | - d->io_service.post([this, key]() |
2348 | + sp->d->dispatcher([sp, key]() |
2349 | { |
2350 | - remove_player_for_key(key); |
2351 | + sp->remove_player_for_key(key); |
2352 | }); |
2353 | }); |
2354 | |
2355 | |
2356 | === modified file 'src/core/media/service_implementation.h' |
2357 | --- src/core/media/service_implementation.h 2014-10-31 07:49:33 +0000 |
2358 | +++ src/core/media/service_implementation.h 2014-11-25 11:08:14 +0000 |
2359 | @@ -19,6 +19,15 @@ |
2360 | #ifndef CORE_UBUNTU_MEDIA_SERVICE_IMPLEMENTATION_H_ |
2361 | #define CORE_UBUNTU_MEDIA_SERVICE_IMPLEMENTATION_H_ |
2362 | |
2363 | +#include "dispatcher.h" |
2364 | + |
2365 | +#include "client_death_observer.h" |
2366 | +#include "recorder_observer.h" |
2367 | +#include "audio/output_observer.h" |
2368 | +#include "power/battery_observer.h" |
2369 | +#include "power/state_controller.h" |
2370 | +#include "telephony/call_monitor.h" |
2371 | + |
2372 | #include "service_skeleton.h" |
2373 | |
2374 | namespace core |
2375 | @@ -30,10 +39,24 @@ |
2376 | |
2377 | class Player; |
2378 | |
2379 | -class ServiceImplementation : public ServiceSkeleton |
2380 | +class ServiceImplementation |
2381 | + : public ServiceSkeleton, |
2382 | + public std::enable_shared_from_this<ServiceImplementation> |
2383 | { |
2384 | public: |
2385 | - ServiceImplementation (); |
2386 | + // All creation time arguments go here. |
2387 | + struct Configuration |
2388 | + { |
2389 | + Dispatcher dispatcher; |
2390 | + media::ClientDeathObserver::Ptr client_death_observer; |
2391 | + media::RecorderObserver::Ptr recorder_observer; |
2392 | + media::audio::OutputObserver::Ptr audio_output_observer; |
2393 | + power::StateController::Ptr power_state_controller; |
2394 | + power::BatteryObserver::Ptr power_state_observer; |
2395 | + telephony::CallMonitor::Ptr call_monitor; |
2396 | + }; |
2397 | + |
2398 | + ServiceImplementation (const Configuration& configuration); |
2399 | ~ServiceImplementation (); |
2400 | |
2401 | std::shared_ptr<Player> create_session(const Player::Configuration&); |
2402 | @@ -46,7 +69,7 @@ |
2403 | void resume_multimedia_session(); |
2404 | |
2405 | struct Private; |
2406 | - std::shared_ptr<Private> d; |
2407 | + std::unique_ptr<Private> d; |
2408 | }; |
2409 | } |
2410 | } |
2411 | |
2412 | === modified file 'src/core/media/service_stub.h' |
2413 | --- src/core/media/service_stub.h 2014-10-16 15:30:44 +0000 |
2414 | +++ src/core/media/service_stub.h 2014-11-25 11:08:14 +0000 |
2415 | @@ -33,7 +33,9 @@ |
2416 | { |
2417 | namespace media |
2418 | { |
2419 | -class ServiceStub : public core::dbus::Stub<core::ubuntu::media::Service> |
2420 | +class ServiceStub |
2421 | + : public core::dbus::Stub<core::ubuntu::media::Service>, |
2422 | + public std::enable_shared_from_this<ServiceStub> |
2423 | { |
2424 | public: |
2425 | ServiceStub(); |
2426 | |
2427 | === renamed directory 'src/core/media/call-monitor' => 'src/core/media/telephony' |
2428 | === modified file 'src/core/media/telephony/call_monitor.cpp' |
2429 | --- src/core/media/call-monitor/call_monitor.cpp 2014-11-04 23:37:22 +0000 |
2430 | +++ src/core/media/telephony/call_monitor.cpp 2014-11-25 11:08:14 +0000 |
2431 | @@ -29,8 +29,12 @@ |
2432 | #include <list> |
2433 | #include <mutex> |
2434 | |
2435 | +namespace media = core::ubuntu::media; |
2436 | |
2437 | -namespace { |
2438 | +namespace |
2439 | +{ |
2440 | +namespace impl |
2441 | +{ |
2442 | class TelepathyCallMonitor : public QObject |
2443 | { |
2444 | Q_OBJECT |
2445 | @@ -73,7 +77,7 @@ |
2446 | } |
2447 | } |
2448 | |
2449 | - void on_change(const std::function<void(CallMonitor::State)>& func) { |
2450 | + void on_change(const std::function<void(media::telephony::CallMonitor::State)>& func) { |
2451 | std::lock_guard<std::mutex> l(cb_lock); |
2452 | cb = func; |
2453 | } |
2454 | @@ -128,19 +132,19 @@ |
2455 | { |
2456 | std::lock_guard<std::mutex> l(cb_lock); |
2457 | if (cb) |
2458 | - cb(CallMonitor::OffHook); |
2459 | + cb(media::telephony::CallMonitor::OffHook); |
2460 | } |
2461 | |
2462 | void onHook() |
2463 | { |
2464 | std::lock_guard<std::mutex> l(cb_lock); |
2465 | if (cb) |
2466 | - cb(CallMonitor::OnHook); |
2467 | + cb(media::telephony::CallMonitor::OnHook); |
2468 | } |
2469 | |
2470 | private: |
2471 | std::mutex cb_lock; |
2472 | - std::function<void (CallMonitor::State)> cb; |
2473 | + std::function<void (media::telephony::CallMonitor::State)> cb; |
2474 | Tp::AccountManagerPtr mAccountManager; |
2475 | std::list<TelepathyCallMonitor*> mCallMonitors; |
2476 | |
2477 | @@ -157,12 +161,11 @@ |
2478 | } |
2479 | } |
2480 | }; |
2481 | -} |
2482 | |
2483 | -class CallMonitorPrivate |
2484 | +struct CallMonitor : public media::telephony::CallMonitor |
2485 | { |
2486 | -public: |
2487 | - CallMonitorPrivate() { |
2488 | + CallMonitor() |
2489 | + { |
2490 | try { |
2491 | std::thread([this]() { |
2492 | qt::core::world::build_and_run(0, nullptr, [this]() { |
2493 | @@ -176,30 +179,32 @@ |
2494 | } catch(...) { |
2495 | std::cerr << "exception(...) in CallMonitor thread start" << std::endl; |
2496 | } |
2497 | - } |
2498 | - |
2499 | - ~CallMonitorPrivate() { |
2500 | - qt::core::world::destroy(); |
2501 | + |
2502 | + mBridge->on_change([this](CallMonitor::State state) |
2503 | + { |
2504 | + call_state_changed(state); |
2505 | + }); |
2506 | + } |
2507 | + |
2508 | + ~CallMonitor() |
2509 | + { |
2510 | + delete mBridge; |
2511 | + } |
2512 | + |
2513 | + const core::Signal<media::telephony::CallMonitor::State>& on_call_state_changed() const |
2514 | + { |
2515 | + return call_state_changed; |
2516 | } |
2517 | |
2518 | TelepathyBridge *mBridge; |
2519 | + core::Signal<media::telephony::CallMonitor::State> call_state_changed; |
2520 | }; |
2521 | - |
2522 | - |
2523 | -CallMonitor::CallMonitor(): |
2524 | - d(new CallMonitorPrivate) |
2525 | -{ |
2526 | -} |
2527 | - |
2528 | -CallMonitor::~CallMonitor() |
2529 | -{ |
2530 | - delete d->mBridge; |
2531 | - delete d; |
2532 | -} |
2533 | - |
2534 | -void CallMonitor::on_change(const std::function<void(CallMonitor::State)>& func) |
2535 | -{ |
2536 | - d->mBridge->on_change(func); |
2537 | +} |
2538 | +} |
2539 | + |
2540 | +media::telephony::CallMonitor::Ptr media::telephony::make_platform_default_call_monitor() |
2541 | +{ |
2542 | + return std::make_shared<impl::CallMonitor>(); |
2543 | } |
2544 | |
2545 | #include "call_monitor.moc" |
2546 | |
2547 | === modified file 'src/core/media/telephony/call_monitor.h' |
2548 | --- src/core/media/call-monitor/call_monitor.h 2014-10-31 07:49:33 +0000 |
2549 | +++ src/core/media/telephony/call_monitor.h 2014-11-25 11:08:14 +0000 |
2550 | @@ -17,25 +17,43 @@ |
2551 | */ |
2552 | |
2553 | |
2554 | -#ifndef CALLMONITOR_H |
2555 | -#define CALLMONITOR_H |
2556 | +#ifndef CORE_UBUNTU_MEDIA_TELEPHONY_CALL_MONITOR_H_ |
2557 | +#define CORE_UBUNTU_MEDIA_TELEPHONY_CALL_MONITOR_H_ |
2558 | + |
2559 | +#include <core/signal.h> |
2560 | |
2561 | #include <functional> |
2562 | - |
2563 | -class CallMonitorPrivate; |
2564 | - |
2565 | -class CallMonitor |
2566 | -{ |
2567 | -public: |
2568 | - enum State { OffHook, OnHook }; |
2569 | - |
2570 | - CallMonitor(); |
2571 | - ~CallMonitor(); |
2572 | - |
2573 | - void on_change(const std::function<void(CallMonitor::State)>& func); |
2574 | - |
2575 | -private: |
2576 | - CallMonitorPrivate *d; |
2577 | +#include <memory> |
2578 | + |
2579 | +namespace core |
2580 | +{ |
2581 | +namespace ubuntu |
2582 | +{ |
2583 | +namespace media |
2584 | +{ |
2585 | +namespace telephony |
2586 | +{ |
2587 | +struct CallMonitor |
2588 | +{ |
2589 | + typedef std::shared_ptr<CallMonitor> Ptr; |
2590 | + |
2591 | + enum State |
2592 | + { |
2593 | + OffHook, |
2594 | + OnHook |
2595 | + }; |
2596 | + |
2597 | + CallMonitor() = default; |
2598 | + virtual ~CallMonitor() = default; |
2599 | + |
2600 | + virtual const core::Signal<State>& on_call_state_changed() const = 0; |
2601 | }; |
2602 | |
2603 | -#endif // CALLMONITOR_H |
2604 | +CallMonitor::Ptr make_platform_default_call_monitor(); |
2605 | +} |
2606 | +} |
2607 | +} |
2608 | +} |
2609 | + |
2610 | + |
2611 | +#endif // CORE_UBUNTU_MEDIA_TELEPHONY_CALL_MONITOR_H_ |
2612 | |
2613 | === removed file 'src/core/media/unity_screen_service.h' |
2614 | --- src/core/media/unity_screen_service.h 2014-06-16 22:28:53 +0000 |
2615 | +++ src/core/media/unity_screen_service.h 1970-01-01 00:00:00 +0000 |
2616 | @@ -1,72 +0,0 @@ |
2617 | -/* |
2618 | - * Copyright (C) 2014 Canonical Ltd |
2619 | - * |
2620 | - * This program is free software: you can redistribute it and/or modify |
2621 | - * it under the terms of the GNU Lesser General Public License version 3 as |
2622 | - * published by the Free Software Foundation. |
2623 | - * |
2624 | - * This program is distributed in the hope that it will be useful, |
2625 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2626 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2627 | - * GNU Lesser General Public License for more details. |
2628 | - * |
2629 | - * You should have received a copy of the GNU Lesser General Public License |
2630 | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2631 | - * |
2632 | - * Author: Alberto Aguirre <alberto.aguirre@canonical.com> |
2633 | - */ |
2634 | - |
2635 | -#include <core/dbus/dbus.h> |
2636 | -#include <core/dbus/fixture.h> |
2637 | -#include <core/dbus/object.h> |
2638 | -#include <core/dbus/property.h> |
2639 | -#include <core/dbus/service.h> |
2640 | -#include <core/dbus/interfaces/properties.h> |
2641 | -#include <core/dbus/types/stl/tuple.h> |
2642 | -#include <core/dbus/types/stl/vector.h> |
2643 | - |
2644 | -#include <core/dbus/asio/executor.h> |
2645 | - |
2646 | -#include <string> |
2647 | -#include <vector> |
2648 | -#include <chrono> |
2649 | - |
2650 | -namespace core |
2651 | -{ |
2652 | - |
2653 | -struct UScreen |
2654 | -{ |
2655 | - static std::string& name() |
2656 | - { |
2657 | - static std::string s = "com.canonical.Unity.Screen"; |
2658 | - return s; |
2659 | - } |
2660 | - |
2661 | - struct keepDisplayOn |
2662 | - { |
2663 | - static std::string name() |
2664 | - { |
2665 | - static std::string s = "keepDisplayOn"; |
2666 | - return s; |
2667 | - } |
2668 | - |
2669 | - static const std::chrono::milliseconds default_timeout() { return std::chrono::seconds{1}; } |
2670 | - |
2671 | - typedef UScreen Interface; |
2672 | - }; |
2673 | - |
2674 | - struct removeDisplayOnRequest |
2675 | - { |
2676 | - static std::string name() |
2677 | - { |
2678 | - static std::string s = "removeDisplayOnRequest"; |
2679 | - return s; |
2680 | - } |
2681 | - |
2682 | - static const std::chrono::milliseconds default_timeout() { return std::chrono::seconds{1}; } |
2683 | - |
2684 | - typedef UScreen Interface; |
2685 | - }; |
2686 | -}; |
2687 | - |
2688 | -} |
2689 | |
2690 | === modified file 'tests/acceptance-tests/service.cpp' |
2691 | --- tests/acceptance-tests/service.cpp 2014-04-04 14:31:43 +0000 |
2692 | +++ tests/acceptance-tests/service.cpp 2014-11-25 11:08:14 +0000 |
2693 | @@ -21,6 +21,7 @@ |
2694 | #include <core/media/track_list.h> |
2695 | |
2696 | #include "core/media/service_implementation.h" |
2697 | +#include "core/media/audio/pulse_audio_output_observer.h" |
2698 | |
2699 | #include "../waitable_state_transition.h" |
2700 | |
2701 | @@ -68,6 +69,48 @@ |
2702 | timespec ts{ 0, static_cast<long int>(ms.count()) * 1000 * 1000}; |
2703 | ::nanosleep(&ts, nullptr); |
2704 | } |
2705 | + |
2706 | +std::thread make_external_services_worker(bool shutdown_requested, media::helper::ExternalServices& es) |
2707 | +{ |
2708 | + return std::thread |
2709 | + { |
2710 | + [&shutdown_requested, &es]() |
2711 | + { |
2712 | + while (not shutdown_requested) |
2713 | + { |
2714 | + try |
2715 | + { |
2716 | + es.io_service.run(); |
2717 | + } |
2718 | + catch (const std::exception& e) |
2719 | + { |
2720 | + std::cerr << "Error while executing the underlying io_service: " << e.what() << std::endl; |
2721 | + } |
2722 | + catch (...) |
2723 | + { |
2724 | + std::cerr << "Error while executing the underlying io_service." << std::endl; |
2725 | + } |
2726 | + } |
2727 | + } |
2728 | + }; |
2729 | +} |
2730 | + |
2731 | +media::ServiceImplementation::Configuration make_default_service_configuration(media::helper::ExternalServices& es) |
2732 | +{ |
2733 | + return media::ServiceImplementation::Configuration |
2734 | + { |
2735 | + [&es](media::Task task) |
2736 | + { |
2737 | + es.io_service.post(task); |
2738 | + }, |
2739 | + media::platform_default_client_death_observer(), |
2740 | + media::make_platform_default_recorder_observer(), |
2741 | + media::audio::make_platform_default_output_observer(), |
2742 | + media::power::make_platform_default_state_controller(es), |
2743 | + media::power::make_platform_default_battery_observer(es), |
2744 | + media::telephony::make_platform_default_call_monitor() |
2745 | + }; |
2746 | +} |
2747 | } |
2748 | |
2749 | TEST(MusicService, DISABLED_accessing_and_creating_a_session_works) |
2750 | @@ -78,7 +121,17 @@ |
2751 | { |
2752 | SigTermCatcher sc; |
2753 | |
2754 | - auto service = std::make_shared<media::ServiceImplementation>(); |
2755 | + // We keep track of our state. |
2756 | + bool shutdown_requested{false}; |
2757 | + |
2758 | + // Our helper for connecting to external services. |
2759 | + media::helper::ExternalServices external_services; |
2760 | + std::thread external_services_worker |
2761 | + { |
2762 | + make_external_services_worker(shutdown_requested, external_services) |
2763 | + }; |
2764 | + |
2765 | + auto service = std::make_shared<media::ServiceImplementation>(make_default_service_configuration(external_services)); |
2766 | std::thread t([&service](){service->run();}); |
2767 | |
2768 | sync_service_start.try_signal_ready_for(std::chrono::milliseconds{500}); |
2769 | @@ -120,7 +173,16 @@ |
2770 | { |
2771 | SigTermCatcher sc; |
2772 | |
2773 | - auto service = std::make_shared<media::ServiceImplementation>(); |
2774 | + // We keep track of our state. |
2775 | + bool shutdown_requested{false}; |
2776 | + |
2777 | + media::helper::ExternalServices external_services; |
2778 | + std::thread external_services_worker |
2779 | + { |
2780 | + make_external_services_worker(shutdown_requested, external_services) |
2781 | + }; |
2782 | + |
2783 | + auto service = std::make_shared<media::ServiceImplementation>(make_default_service_configuration(external_services)); |
2784 | std::thread t([&service](){service->run();}); |
2785 | |
2786 | sync_service_start.try_signal_ready_for(std::chrono::milliseconds{500}); |
2787 | @@ -173,7 +235,16 @@ |
2788 | { |
2789 | SigTermCatcher sc; |
2790 | |
2791 | - auto service = std::make_shared<media::ServiceImplementation>(); |
2792 | + // We keep track of our state. |
2793 | + bool shutdown_requested{false}; |
2794 | + |
2795 | + media::helper::ExternalServices external_services; |
2796 | + std::thread external_services_worker |
2797 | + { |
2798 | + make_external_services_worker(shutdown_requested, external_services) |
2799 | + }; |
2800 | + |
2801 | + auto service = std::make_shared<media::ServiceImplementation>(make_default_service_configuration(external_services)); |
2802 | std::thread t([&service](){service->run();}); |
2803 | |
2804 | sync_service_start.try_signal_ready_for(std::chrono::milliseconds{500}); |
2805 | @@ -252,7 +323,16 @@ |
2806 | { |
2807 | SigTermCatcher sc; |
2808 | |
2809 | - auto service = std::make_shared<media::ServiceImplementation>(); |
2810 | + // We keep track of our state. |
2811 | + bool shutdown_requested{false}; |
2812 | + |
2813 | + media::helper::ExternalServices external_services; |
2814 | + std::thread external_services_worker |
2815 | + { |
2816 | + make_external_services_worker(shutdown_requested, external_services) |
2817 | + }; |
2818 | + |
2819 | + auto service = std::make_shared<media::ServiceImplementation>(make_default_service_configuration(external_services)); |
2820 | std::thread t([&service](){service->run();}); |
2821 | |
2822 | sync_service_start.try_signal_ready_for(std::chrono::milliseconds{500}); |
2823 | @@ -311,7 +391,16 @@ |
2824 | { |
2825 | SigTermCatcher sc; |
2826 | |
2827 | - auto service = std::make_shared<media::ServiceImplementation>(); |
2828 | + // We keep track of our state. |
2829 | + bool shutdown_requested{false}; |
2830 | + |
2831 | + media::helper::ExternalServices external_services; |
2832 | + std::thread external_services_worker |
2833 | + { |
2834 | + make_external_services_worker(shutdown_requested, external_services) |
2835 | + }; |
2836 | + |
2837 | + auto service = std::make_shared<media::ServiceImplementation>(make_default_service_configuration(external_services)); |
2838 | std::thread t([&service](){service->run();}); |
2839 | |
2840 | sync_service_start.try_signal_ready_for(std::chrono::milliseconds{500}); |
A lot of code but in general looking good. See my comments inline below. Also, I'd like to see unit tests for all of the new classes that you introduced such as BatteryObserver, StateController, etc.