Merge lp:~thomas-voss/media-hub/enable-integration-testing into lp:media-hub
- enable-integration-testing
- Merge into trunk
Status: | Rejected |
---|---|
Rejected by: | Thomas Voß |
Proposed branch: | lp:~thomas-voss/media-hub/enable-integration-testing |
Merge into: | lp:media-hub |
Prerequisite: | lp:~thomas-voss/media-hub/add-display-controller-interface |
Diff against target: |
5174 lines (+3484/-862) 44 files modified
include/core/media/player.h (+1/-1) src/core/media/CMakeLists.txt (+10/-4) src/core/media/base_engine.cpp (+181/-0) src/core/media/base_engine.h (+100/-0) src/core/media/base_player.cpp (+250/-0) src/core/media/base_player.h (+127/-0) src/core/media/engine.cpp (+20/-0) src/core/media/engine.h (+4/-0) src/core/media/engine_factory.h (+39/-0) src/core/media/gstreamer/engine_factory.cpp (+31/-0) src/core/media/gstreamer/engine_factory.h (+40/-0) src/core/media/gstreamer/playbin.cpp (+1/-1) src/core/media/hashed_keyed_player_store.cpp (+80/-0) src/core/media/hashed_keyed_player_store.h (+76/-0) src/core/media/keyed_player_store.h (+83/-0) src/core/media/mpris/exported.h (+305/-0) src/core/media/mutable_player.h (+60/-0) src/core/media/player.cpp (+1/-7) src/core/media/player_configuration.h (+3/-9) src/core/media/player_implementation.cpp (+8/-19) src/core/media/player_implementation.h (+10/-8) src/core/media/player_skeleton.cpp (+214/-233) src/core/media/player_skeleton.h (+26/-36) src/core/media/player_stub.h (+1/-1) src/core/media/power/state_controller.cpp (+30/-0) src/core/media/power/state_controller.h (+5/-0) src/core/media/server/server.cpp (+33/-7) src/core/media/service_implementation.cpp (+53/-50) src/core/media/service_implementation.h (+18/-9) src/core/media/service_skeleton.cpp (+62/-384) src/core/media/service_skeleton.h (+25/-32) tests/CMakeLists.txt (+27/-12) tests/acceptance-tests/service.cpp (+2/-0) tests/integration-tests/CMakeLists.txt (+4/-0) tests/integration-tests/player_implementation_test.cpp (+274/-0) tests/integration-tests/player_skeleton_test.cpp (+363/-0) tests/integration-tests/service_implementation_test.cpp (+421/-0) tests/mock/client_death_observer.h (+48/-0) tests/mock/engine.h (+83/-0) tests/mock/keyed_player_store.h (+64/-0) tests/mock/player.h (+75/-0) tests/mock/power_state_controller.h (+127/-0) tests/unit-tests/CMakeLists.txt (+2/-49) tests/unit-tests/hashed_keyed_player_store_test.cpp (+97/-0) |
To merge this branch: | bzr merge lp:~thomas-voss/media-hub/enable-integration-testing |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ubuntu Phablet Team | Pending | ||
Review via email: mp+242028@code.launchpad.net |
Commit message
Refactor implementation to allow for testing PlayerImplement
Description of the change
Refactor implementation to allow for testing PlayerImplement
- 104. By Thomas Voß
-
Add a first set of test cases around media::
PlayerImplement ation. - 105. By Thomas Voß
-
Add the first test cases for PlayerSkeleton.
- 106. By Thomas Voß
-
Add further testing for PlayerSkeleton.
- 107. By Thomas Voß
-
Add tests for HashedKeyedPlay
erStore.
Add tests for ServiceImplementation. - 108. By Thomas Voß
-
Add further tests around ServiceImplemen
tation. - 109. By Thomas Voß
-
Move mocks and tests around to make them reusable and correctly classified.
Unmerged revisions
- 109. By Thomas Voß
-
Move mocks and tests around to make them reusable and correctly classified.
- 108. By Thomas Voß
-
Add further tests around ServiceImplemen
tation. - 107. By Thomas Voß
-
Add tests for HashedKeyedPlay
erStore.
Add tests for ServiceImplementation. - 106. By Thomas Voß
-
Add further testing for PlayerSkeleton.
- 105. By Thomas Voß
-
Add the first test cases for PlayerSkeleton.
- 104. By Thomas Voß
-
Add a first set of test cases around media::
PlayerImplement ation. - 103. By Thomas Voß
-
Refactor implementation to allow for testing PlayerImplement
ation in isolation. - 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.
Preview Diff
1 | === modified file 'include/core/media/player.h' |
2 | --- include/core/media/player.h 2014-11-19 10:23:50 +0000 |
3 | +++ include/core/media/player.h 2014-11-19 10:23:50 +0000 |
4 | @@ -152,7 +152,7 @@ |
5 | virtual core::Signal<PlaybackStatus>& playback_status_changed() = 0; |
6 | virtual const core::Signal<video::Dimensions>& video_dimension_changed() const = 0; |
7 | protected: |
8 | - Player(); |
9 | + Player(); |
10 | |
11 | }; |
12 | } |
13 | |
14 | === modified file 'src/core/media/CMakeLists.txt' |
15 | --- src/core/media/CMakeLists.txt 2014-11-19 10:23:50 +0000 |
16 | +++ src/core/media/CMakeLists.txt 2014-11-19 10:23:50 +0000 |
17 | @@ -15,6 +15,10 @@ |
18 | add_library( |
19 | media-hub-common SHARED |
20 | |
21 | + player.cpp |
22 | + track.cpp |
23 | + track_list.cpp |
24 | + |
25 | the_session_bus.cpp |
26 | ) |
27 | |
28 | @@ -40,10 +44,7 @@ |
29 | |
30 | ${MEDIA_HUB_HEADERS} |
31 | |
32 | - player.cpp |
33 | service.cpp |
34 | - track.cpp |
35 | - track_list.cpp |
36 | |
37 | player_stub.cpp |
38 | service_stub.cpp |
39 | @@ -85,8 +86,11 @@ |
40 | ${MEDIA_HUB_HEADERS} |
41 | ${MPRIS_HEADERS} |
42 | |
43 | + base_engine.cpp |
44 | + base_player.cpp |
45 | client_death_observer.cpp |
46 | - hybris_client_death_observer.cpp |
47 | + hashed_keyed_player_store.cpp |
48 | + hybris_client_death_observer.cpp |
49 | cover_art_resolver.cpp |
50 | engine.cpp |
51 | |
52 | @@ -97,6 +101,7 @@ |
53 | hybris_recorder_observer.cpp |
54 | |
55 | gstreamer/engine.cpp |
56 | + gstreamer/engine_factory.cpp |
57 | gstreamer/playbin.cpp |
58 | |
59 | player_skeleton.cpp |
60 | @@ -134,6 +139,7 @@ |
61 | |
62 | media-hub-service |
63 | media-hub-client |
64 | + media-hub-common |
65 | call-monitor |
66 | |
67 | ${DBUS_LIBRARIES} |
68 | |
69 | === added file 'src/core/media/base_engine.cpp' |
70 | --- src/core/media/base_engine.cpp 1970-01-01 00:00:00 +0000 |
71 | +++ src/core/media/base_engine.cpp 2014-11-19 10:23:50 +0000 |
72 | @@ -0,0 +1,181 @@ |
73 | +/** |
74 | + * Copyright (C) 2014 Canonical Ltd |
75 | + * |
76 | + * This program is free software: you can redistribute it and/or modify it |
77 | + * under the terms of the GNU Lesser General Public License version 3, |
78 | + * as published by the Free Software Foundation. |
79 | + * |
80 | + * This program is distributed in the hope that it will be useful, |
81 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
82 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
83 | + * GNU Lesser General Public License for more details. |
84 | + * |
85 | + * You should have received a copy of the GNU Lesser General Public License |
86 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
87 | + * |
88 | + * Authored by: Thomas Voß <thomas.voss@canonical.com> |
89 | + */ |
90 | + |
91 | +#include <core/media/base_engine.h> |
92 | + |
93 | +namespace media = core::ubuntu::media; |
94 | + |
95 | +const core::Property<media::Engine::State>& media::BaseEngine::state() const |
96 | +{ |
97 | + return properties.state; |
98 | +} |
99 | + |
100 | +const core::Property<bool>& media::BaseEngine::is_video_source() const |
101 | +{ |
102 | + return properties.is_video_source; |
103 | +} |
104 | + |
105 | +const core::Property<bool>& media::BaseEngine::is_audio_source() const |
106 | +{ |
107 | + return properties.is_audio_source; |
108 | +} |
109 | + |
110 | +const core::Property<uint64_t>& media::BaseEngine::position() const |
111 | +{ |
112 | + return properties.position; |
113 | +} |
114 | + |
115 | +const core::Property<uint64_t>& media::BaseEngine::duration() const |
116 | +{ |
117 | + return properties.duration; |
118 | +} |
119 | + |
120 | +const core::Property<media::Engine::Volume>& media::BaseEngine::volume() const |
121 | +{ |
122 | + return properties.volume; |
123 | +} |
124 | + |
125 | +core::Property<media::Engine::Volume>& media::BaseEngine::volume() |
126 | +{ |
127 | + return properties.volume; |
128 | +} |
129 | + |
130 | +const core::Property<media::Player::AudioStreamRole>& media::BaseEngine::audio_stream_role() const |
131 | +{ |
132 | + return properties.audio_stream_role; |
133 | +} |
134 | + |
135 | +core::Property<media::Player::AudioStreamRole>& media::BaseEngine::audio_stream_role() |
136 | +{ |
137 | + return properties.audio_stream_role; |
138 | +} |
139 | + |
140 | +const core::Property<media::Player::Orientation>& media::BaseEngine::orientation() const |
141 | +{ |
142 | + return properties.orientation; |
143 | +} |
144 | + |
145 | +const core::Property<std::tuple<media::Track::UriType, media::Track::MetaData>>& media::BaseEngine::track_meta_data() const |
146 | +{ |
147 | + return properties.track_meta_data; |
148 | +} |
149 | + |
150 | +const core::Signal<void>& media::BaseEngine::about_to_finish_signal() const |
151 | +{ |
152 | + return signals.about_to_finish; |
153 | +} |
154 | + |
155 | +const core::Signal<uint64_t>& media::BaseEngine::seeked_to_signal() const |
156 | +{ |
157 | + return signals.seeked_to; |
158 | +} |
159 | + |
160 | +const core::Signal<void>& media::BaseEngine::client_disconnected_signal() const |
161 | +{ |
162 | + return signals.client_disconnected; |
163 | +} |
164 | + |
165 | +const core::Signal<void>& media::BaseEngine::end_of_stream_signal() const |
166 | +{ |
167 | + return signals.end_of_stream; |
168 | +} |
169 | + |
170 | +const core::Signal<media::Player::PlaybackStatus>& media::BaseEngine::playback_status_changed_signal() const |
171 | +{ |
172 | + return signals.playback_status_changed; |
173 | +} |
174 | + |
175 | +const core::Signal<media::video::Dimensions>& media::BaseEngine::video_dimension_changed_signal() const |
176 | +{ |
177 | + return signals.video_dimension_changed; |
178 | +} |
179 | + |
180 | +core::Property<media::Engine::State>& media::BaseEngine::mutable_state() |
181 | +{ |
182 | + return properties.state; |
183 | +} |
184 | + |
185 | +core::Property<bool>& media::BaseEngine::mutable_is_video_source() |
186 | +{ |
187 | + return properties.is_video_source; |
188 | +} |
189 | + |
190 | +core::Property<bool>& media::BaseEngine::mutable_is_audio_source() |
191 | +{ |
192 | + return properties.is_audio_source; |
193 | +} |
194 | + |
195 | +core::Property<uint64_t>& media::BaseEngine::mutable_position() |
196 | +{ |
197 | + return properties.position; |
198 | +} |
199 | + |
200 | +core::Property<uint64_t>& media::BaseEngine::mutable_duration() |
201 | +{ |
202 | + return properties.duration; |
203 | +} |
204 | + |
205 | +core::Property<media::Engine::Volume>& media::BaseEngine::mutable_volume() |
206 | +{ |
207 | + return properties.volume; |
208 | +} |
209 | + |
210 | +core::Property<media::Player::AudioStreamRole>& media::BaseEngine::mutable_audio_stream_role() |
211 | +{ |
212 | + return properties.audio_stream_role; |
213 | +} |
214 | + |
215 | +core::Property<media::Player::Orientation>& media::BaseEngine::mutable_orientation() |
216 | +{ |
217 | + return properties.orientation; |
218 | +} |
219 | + |
220 | +core::Property<std::tuple<media::Track::UriType, media::Track::MetaData>>& media::BaseEngine::mutable_track_meta_data() |
221 | +{ |
222 | + return properties.track_meta_data; |
223 | +} |
224 | + |
225 | +core::Signal<void>& media::BaseEngine::mutable_about_to_finish_signal() |
226 | +{ |
227 | + return signals.about_to_finish; |
228 | +} |
229 | + |
230 | +core::Signal<uint64_t>& media::BaseEngine::mutable_seeked_to_signal() |
231 | +{ |
232 | + return signals.seeked_to; |
233 | +} |
234 | + |
235 | +core::Signal<void>& media::BaseEngine::mutable_client_disconnected_signal() |
236 | +{ |
237 | + return signals.client_disconnected; |
238 | +} |
239 | + |
240 | +core::Signal<void>& media::BaseEngine::mutable_end_of_stream_signal() |
241 | +{ |
242 | + return signals.end_of_stream; |
243 | +} |
244 | + |
245 | +core::Signal<media::Player::PlaybackStatus>& media::BaseEngine::mutable_playback_status_changed_signal() |
246 | +{ |
247 | + return signals.playback_status_changed; |
248 | +} |
249 | + |
250 | +core::Signal<media::video::Dimensions>& media::BaseEngine::mutable_video_dimension_changed_signal() |
251 | +{ |
252 | + return signals.video_dimension_changed; |
253 | +} |
254 | |
255 | === added file 'src/core/media/base_engine.h' |
256 | --- src/core/media/base_engine.h 1970-01-01 00:00:00 +0000 |
257 | +++ src/core/media/base_engine.h 2014-11-19 10:23:50 +0000 |
258 | @@ -0,0 +1,100 @@ |
259 | +/** |
260 | + * Copyright (C) 2014 Canonical Ltd |
261 | + * |
262 | + * This program is free software: you can redistribute it and/or modify it |
263 | + * under the terms of the GNU Lesser General Public License version 3, |
264 | + * as published by the Free Software Foundation. |
265 | + * |
266 | + * This program is distributed in the hope that it will be useful, |
267 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
268 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
269 | + * GNU Lesser General Public License for more details. |
270 | + * |
271 | + * You should have received a copy of the GNU Lesser General Public License |
272 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
273 | + * |
274 | + * Authored by: Thomas Voß <thomas.voss@canonical.com> |
275 | + */ |
276 | + |
277 | +#ifndef CORE_UBUNTU_MEDIA_BASE_ENGINE_H_ |
278 | +#define CORE_UBUNTU_MEDIA_BASE_ENGINE_H_ |
279 | + |
280 | +#include <core/media/engine.h> |
281 | + |
282 | +namespace core |
283 | +{ |
284 | +namespace ubuntu |
285 | +{ |
286 | +namespace media |
287 | +{ |
288 | +// Helper class providing a default setup for |
289 | +// properties and signals. |
290 | +class BaseEngine : public Engine |
291 | +{ |
292 | +public: |
293 | + virtual const core::Property<State>& state() const override; |
294 | + virtual const core::Property<bool>& is_video_source() const override; |
295 | + virtual const core::Property<bool>& is_audio_source() const override; |
296 | + virtual const core::Property<uint64_t>& position() const override; |
297 | + virtual const core::Property<uint64_t>& duration() const override; |
298 | + virtual const core::Property<Engine::Volume>& volume() const override; |
299 | + virtual core::Property<Engine::Volume>& volume() override; |
300 | + virtual const core::Property<Player::AudioStreamRole>& audio_stream_role() const override; |
301 | + virtual core::Property<Player::AudioStreamRole>& audio_stream_role() override; |
302 | + virtual const core::Property<Player::Orientation>& orientation() const override; |
303 | + virtual const core::Property<std::tuple<Track::UriType, Track::MetaData>>& track_meta_data() const override; |
304 | + virtual const core::Signal<void>& about_to_finish_signal() const override; |
305 | + virtual const core::Signal<uint64_t>& seeked_to_signal() const override; |
306 | + virtual const core::Signal<void>& client_disconnected_signal() const override; |
307 | + virtual const core::Signal<void>& end_of_stream_signal() const override; |
308 | + virtual const core::Signal<Player::PlaybackStatus>& playback_status_changed_signal() const override; |
309 | + virtual const core::Signal<video::Dimensions>& video_dimension_changed_signal() const override; |
310 | + |
311 | +protected: |
312 | + BaseEngine() = default; |
313 | + |
314 | + virtual core::Property<State>& mutable_state(); |
315 | + virtual core::Property<bool>& mutable_is_video_source(); |
316 | + virtual core::Property<bool>& mutable_is_audio_source(); |
317 | + virtual core::Property<uint64_t>& mutable_position(); |
318 | + virtual core::Property<uint64_t>& mutable_duration(); |
319 | + virtual core::Property<Engine::Volume>& mutable_volume(); |
320 | + virtual core::Property<Player::AudioStreamRole>& mutable_audio_stream_role(); |
321 | + virtual core::Property<Player::Orientation>& mutable_orientation(); |
322 | + virtual core::Property<std::tuple<Track::UriType, Track::MetaData>>& mutable_track_meta_data(); |
323 | + virtual core::Signal<void>& mutable_about_to_finish_signal(); |
324 | + virtual core::Signal<uint64_t>& mutable_seeked_to_signal(); |
325 | + virtual core::Signal<void>& mutable_client_disconnected_signal(); |
326 | + virtual core::Signal<void>& mutable_end_of_stream_signal(); |
327 | + virtual core::Signal<Player::PlaybackStatus>& mutable_playback_status_changed_signal(); |
328 | + virtual core::Signal<video::Dimensions>& mutable_video_dimension_changed_signal(); |
329 | + |
330 | +private: |
331 | + struct |
332 | + { |
333 | + core::Property<State> state; |
334 | + core::Property<bool> is_video_source; |
335 | + core::Property<bool> is_audio_source; |
336 | + core::Property<uint64_t> position; |
337 | + core::Property<uint64_t> duration; |
338 | + core::Property<Engine::Volume> volume; |
339 | + core::Property<Player::AudioStreamRole> audio_stream_role; |
340 | + core::Property<Player::Orientation> orientation; |
341 | + core::Property<std::tuple<Track::UriType, Track::MetaData>> track_meta_data; |
342 | + } properties; |
343 | + |
344 | + struct |
345 | + { |
346 | + core::Signal<void> about_to_finish; |
347 | + core::Signal<uint64_t> seeked_to; |
348 | + core::Signal<void> client_disconnected; |
349 | + core::Signal<void> end_of_stream; |
350 | + core::Signal<Player::PlaybackStatus> playback_status_changed; |
351 | + core::Signal<video::Dimensions> video_dimension_changed; |
352 | + } signals; |
353 | +}; |
354 | +} |
355 | +} |
356 | +} |
357 | + |
358 | +#endif // CORE_UBUNTU_MEDIA_BASE_ENGINE_H_ |
359 | |
360 | === added file 'src/core/media/base_player.cpp' |
361 | --- src/core/media/base_player.cpp 1970-01-01 00:00:00 +0000 |
362 | +++ src/core/media/base_player.cpp 2014-11-19 10:23:50 +0000 |
363 | @@ -0,0 +1,250 @@ |
364 | +/** |
365 | + * Copyright (C) 2014 Canonical Ltd |
366 | + * |
367 | + * This program is free software: you can redistribute it and/or modify it |
368 | + * under the terms of the GNU Lesser General Public License version 3, |
369 | + * as published by the Free Software Foundation. |
370 | + * |
371 | + * This program is distributed in the hope that it will be useful, |
372 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
373 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
374 | + * GNU Lesser General Public License for more details. |
375 | + * |
376 | + * You should have received a copy of the GNU Lesser General Public License |
377 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
378 | + * |
379 | + * Authored by: Thomas Voß <thomas.voss@canonical.com> |
380 | + */ |
381 | + |
382 | +#include <core/media/base_player.h> |
383 | + |
384 | +namespace media = core::ubuntu::media; |
385 | + |
386 | +media::BasePlayer::BasePlayer() |
387 | +{ |
388 | +} |
389 | + |
390 | +const core::Property<bool>& media::BasePlayer::can_play() const |
391 | +{ |
392 | + return properties.can_play; |
393 | +} |
394 | + |
395 | +const core::Property<bool>& media::BasePlayer::can_pause() const |
396 | +{ |
397 | + return properties.can_pause; |
398 | +} |
399 | + |
400 | +const core::Property<bool>& media::BasePlayer::can_seek() const |
401 | +{ |
402 | + return properties.can_seek; |
403 | +} |
404 | + |
405 | +const core::Property<bool>& media::BasePlayer::can_go_previous() const |
406 | +{ |
407 | + return properties.can_go_previous; |
408 | +} |
409 | + |
410 | +const core::Property<bool>& media::BasePlayer::can_go_next() const |
411 | +{ |
412 | + return properties.can_go_next; |
413 | +} |
414 | + |
415 | +const core::Property<bool>& media::BasePlayer::is_video_source() const |
416 | +{ |
417 | + return properties.is_video_source; |
418 | +} |
419 | + |
420 | +const core::Property<bool>& media::BasePlayer::is_audio_source() const |
421 | +{ |
422 | + return properties.is_audio_source; |
423 | +} |
424 | + |
425 | +const core::Property<media::Player::PlaybackStatus>& media::BasePlayer::playback_status() const |
426 | +{ |
427 | + return properties.playback_status; |
428 | +} |
429 | + |
430 | +const core::Property<media::Player::LoopStatus>& media::BasePlayer::loop_status() const |
431 | +{ |
432 | + return properties.loop_status; |
433 | +} |
434 | + |
435 | +const core::Property<media::Player::PlaybackRate>& media::BasePlayer::playback_rate() const |
436 | +{ |
437 | + return properties.playback_rate; |
438 | +} |
439 | + |
440 | +const core::Property<bool>& media::BasePlayer::is_shuffle() const |
441 | +{ |
442 | + return properties.is_shuffle; |
443 | +} |
444 | + |
445 | +const core::Property<media::Track::MetaData>& media::BasePlayer::meta_data_for_current_track() const |
446 | +{ |
447 | + return properties.meta_data_for_current_track; |
448 | +} |
449 | + |
450 | +const core::Property<media::Player::Volume>& media::BasePlayer::volume() const |
451 | +{ |
452 | + return properties.volume; |
453 | +} |
454 | + |
455 | +const core::Property<media::Player::PlaybackRate>& media::BasePlayer::minimum_playback_rate() const |
456 | +{ |
457 | + return properties.minimum_playback_rate; |
458 | +} |
459 | + |
460 | +const core::Property<media::Player::PlaybackRate>& media::BasePlayer::maximum_playback_rate() const |
461 | +{ |
462 | + return properties.maximum_playback_rate; |
463 | +} |
464 | + |
465 | +const core::Property<int64_t>& media::BasePlayer::position() const |
466 | +{ |
467 | + return properties.position; |
468 | +} |
469 | + |
470 | +const core::Property<int64_t>& media::BasePlayer::duration() const |
471 | +{ |
472 | + return properties.duration; |
473 | +} |
474 | + |
475 | +const core::Property<media::Player::AudioStreamRole>& media::BasePlayer::audio_stream_role() const |
476 | +{ |
477 | + return properties.audio_stream_role; |
478 | +} |
479 | + |
480 | +const core::Property<media::Player::Orientation>& media::BasePlayer::orientation() const |
481 | +{ |
482 | + return properties.orientation; |
483 | +} |
484 | + |
485 | +core::Property<media::Player::LoopStatus>& media::BasePlayer::loop_status() |
486 | +{ |
487 | + return properties.loop_status; |
488 | +} |
489 | + |
490 | +core::Property<media::Player::PlaybackRate>& media::BasePlayer::playback_rate() |
491 | +{ |
492 | + return properties.playback_rate; |
493 | +} |
494 | + |
495 | +core::Property<bool>& media::BasePlayer::is_shuffle() |
496 | +{ |
497 | + return properties.is_shuffle; |
498 | +} |
499 | + |
500 | +core::Property<media::Player::Volume>& media::BasePlayer::volume() |
501 | +{ |
502 | + return properties.volume; |
503 | +} |
504 | + |
505 | +core::Property<media::Player::AudioStreamRole>& media::BasePlayer::audio_stream_role() |
506 | +{ |
507 | + return properties.audio_stream_role; |
508 | +} |
509 | + |
510 | +const core::Signal<int64_t>& media::BasePlayer::seeked_to() const |
511 | +{ |
512 | + return signals.seeked_to; |
513 | +} |
514 | + |
515 | +const core::Signal<void>& media::BasePlayer::end_of_stream() const |
516 | +{ |
517 | + return signals.end_of_stream; |
518 | +} |
519 | + |
520 | +core::Signal<media::Player::PlaybackStatus>& media::BasePlayer::playback_status_changed() |
521 | +{ |
522 | + return signals.playback_status_changed; |
523 | +} |
524 | + |
525 | +const core::Signal<media::video::Dimensions>& media::BasePlayer::video_dimension_changed() const |
526 | +{ |
527 | + return signals.video_dimension_changed; |
528 | +} |
529 | + |
530 | +core::Property<media::Player::PlaybackStatus>& media::BasePlayer::playback_status() |
531 | +{ |
532 | + return properties.playback_status; |
533 | +} |
534 | + |
535 | +core::Property<bool>& media::BasePlayer::can_play() |
536 | +{ |
537 | + return properties.can_play; |
538 | +} |
539 | + |
540 | +core::Property<bool>& media::BasePlayer::can_pause() |
541 | +{ |
542 | + return properties.can_pause; |
543 | +} |
544 | + |
545 | +core::Property<bool>& media::BasePlayer::can_seek() |
546 | +{ |
547 | + return properties.can_seek; |
548 | +} |
549 | + |
550 | +core::Property<bool>& media::BasePlayer::can_go_previous() |
551 | +{ |
552 | + return properties.can_go_previous; |
553 | +} |
554 | + |
555 | +core::Property<bool>& media::BasePlayer::can_go_next() |
556 | +{ |
557 | + return properties.can_go_next; |
558 | +} |
559 | + |
560 | +core::Property<bool>& media::BasePlayer::is_video_source() |
561 | +{ |
562 | + return properties.is_video_source; |
563 | +} |
564 | + |
565 | +core::Property<bool>& media::BasePlayer::is_audio_source() |
566 | +{ |
567 | + return properties.is_audio_source; |
568 | +} |
569 | + |
570 | +core::Property<media::Track::MetaData>& media::BasePlayer::meta_data_for_current_track() |
571 | +{ |
572 | + return properties.meta_data_for_current_track; |
573 | +} |
574 | + |
575 | +core::Property<media::Player::PlaybackRate>& media::BasePlayer::minimum_playback_rate() |
576 | +{ |
577 | + return properties.minimum_playback_rate; |
578 | +} |
579 | + |
580 | +core::Property<media::Player::PlaybackRate>& media::BasePlayer::maximum_playback_rate() |
581 | +{ |
582 | + return properties.maximum_playback_rate; |
583 | +} |
584 | + |
585 | +core::Property<int64_t>& media::BasePlayer::position() |
586 | +{ |
587 | + return properties.position; |
588 | +} |
589 | + |
590 | +core::Property<int64_t>& media::BasePlayer::duration() |
591 | +{ |
592 | + return properties.duration; |
593 | +} |
594 | + |
595 | +core::Property<media::Player::Orientation>& media::BasePlayer::orientation() |
596 | +{ |
597 | + return properties.orientation; |
598 | +} |
599 | + |
600 | +core::Signal<int64_t>& media::BasePlayer::seeked_to() |
601 | +{ |
602 | + return signals.seeked_to; |
603 | +} |
604 | + |
605 | +core::Signal<void>& media::BasePlayer::end_of_stream() |
606 | +{ |
607 | + return signals.end_of_stream; |
608 | +} |
609 | + |
610 | +core::Signal<media::video::Dimensions>& media::BasePlayer::video_dimension_changed() |
611 | +{ |
612 | + return signals.video_dimension_changed; |
613 | +} |
614 | |
615 | === added file 'src/core/media/base_player.h' |
616 | --- src/core/media/base_player.h 1970-01-01 00:00:00 +0000 |
617 | +++ src/core/media/base_player.h 2014-11-19 10:23:50 +0000 |
618 | @@ -0,0 +1,127 @@ |
619 | +/** |
620 | + * Copyright (C) 2014 Canonical Ltd |
621 | + * |
622 | + * This program is free software: you can redistribute it and/or modify it |
623 | + * under the terms of the GNU Lesser General Public License version 3, |
624 | + * as published by the Free Software Foundation. |
625 | + * |
626 | + * This program is distributed in the hope that it will be useful, |
627 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
628 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
629 | + * GNU Lesser General Public License for more details. |
630 | + * |
631 | + * You should have received a copy of the GNU Lesser General Public License |
632 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
633 | + * |
634 | + * Authored by: Thomas Voß <thomas.voss@canonical.com> |
635 | + */ |
636 | + |
637 | +#ifndef CORE_UBUNTU_MEDIA_BASE_PLAYER_H_ |
638 | +#define CORE_UBUNTU_MEDIA_BASE_PLAYER_H_ |
639 | + |
640 | +#include <core/media/mutable_player.h> |
641 | + |
642 | +#include <memory> |
643 | + |
644 | +namespace core |
645 | +{ |
646 | +namespace ubuntu |
647 | +{ |
648 | +namespace media |
649 | +{ |
650 | +// A player implementation that provides a default implementation |
651 | +// for accessing properties and signals. |
652 | +class BasePlayer : public MutablePlayer |
653 | +{ |
654 | +public: |
655 | + virtual const core::Property<bool>& can_play() const override; |
656 | + virtual const core::Property<bool>& can_pause() const override; |
657 | + virtual const core::Property<bool>& can_seek() const override; |
658 | + virtual const core::Property<bool>& can_go_previous() const override; |
659 | + virtual const core::Property<bool>& can_go_next() const override; |
660 | + virtual const core::Property<bool>& is_video_source() const override; |
661 | + virtual const core::Property<bool>& is_audio_source() const override; |
662 | + virtual const core::Property<PlaybackStatus>& playback_status() const override; |
663 | + virtual const core::Property<LoopStatus>& loop_status() const override; |
664 | + virtual const core::Property<PlaybackRate>& playback_rate() const override; |
665 | + virtual const core::Property<bool>& is_shuffle() const override; |
666 | + virtual const core::Property<Track::MetaData>& meta_data_for_current_track() const override; |
667 | + virtual const core::Property<Volume>& volume() const override; |
668 | + virtual const core::Property<PlaybackRate>& minimum_playback_rate() const override; |
669 | + virtual const core::Property<PlaybackRate>& maximum_playback_rate() const override; |
670 | + virtual const core::Property<int64_t>& position() const override; |
671 | + virtual const core::Property<int64_t>& duration() const override; |
672 | + virtual const core::Property<AudioStreamRole>& audio_stream_role() const override; |
673 | + virtual const core::Property<Orientation>& orientation() const override; |
674 | + |
675 | + virtual core::Property<LoopStatus>& loop_status() override; |
676 | + virtual core::Property<PlaybackRate>& playback_rate() override; |
677 | + virtual core::Property<bool>& is_shuffle() override; |
678 | + virtual core::Property<Volume>& volume() override; |
679 | + virtual core::Property<AudioStreamRole>& audio_stream_role() override; |
680 | + |
681 | + virtual const core::Signal<int64_t>& seeked_to() const override; |
682 | + virtual const core::Signal<void>& end_of_stream() const override; |
683 | + virtual core::Signal<PlaybackStatus>& playback_status_changed() override; |
684 | + virtual const core::Signal<video::Dimensions>& video_dimension_changed() const override; |
685 | + |
686 | +protected: |
687 | + BasePlayer(); |
688 | + // These properties are not exposed to the client, but still need to be |
689 | + // able to be settable from within the Player: |
690 | + virtual core::Property<PlaybackStatus>& playback_status(); |
691 | + virtual core::Property<bool>& can_play(); |
692 | + virtual core::Property<bool>& can_pause(); |
693 | + virtual core::Property<bool>& can_seek(); |
694 | + virtual core::Property<bool>& can_go_previous(); |
695 | + virtual core::Property<bool>& can_go_next(); |
696 | + virtual core::Property<bool>& is_video_source(); |
697 | + virtual core::Property<bool>& is_audio_source(); |
698 | + virtual core::Property<Track::MetaData>& meta_data_for_current_track(); |
699 | + virtual core::Property<PlaybackRate>& minimum_playback_rate(); |
700 | + virtual core::Property<PlaybackRate>& maximum_playback_rate(); |
701 | + virtual core::Property<int64_t>& position(); |
702 | + virtual core::Property<int64_t>& duration(); |
703 | + virtual core::Property<Orientation>& orientation(); |
704 | + |
705 | + virtual core::Signal<int64_t>& seeked_to(); |
706 | + virtual core::Signal<void>& end_of_stream(); |
707 | + virtual core::Signal<video::Dimensions>& video_dimension_changed(); |
708 | + |
709 | +private: |
710 | + struct |
711 | + { |
712 | + core::Property<bool> can_play; |
713 | + core::Property<bool> can_pause; |
714 | + core::Property<bool> can_seek; |
715 | + core::Property<bool> can_go_previous; |
716 | + core::Property<bool> can_go_next; |
717 | + core::Property<bool> is_video_source; |
718 | + core::Property<bool> is_audio_source; |
719 | + core::Property<PlaybackStatus> playback_status; |
720 | + core::Property<LoopStatus> loop_status; |
721 | + core::Property<PlaybackRate> playback_rate; |
722 | + core::Property<bool> is_shuffle; |
723 | + core::Property<Track::MetaData> meta_data_for_current_track; |
724 | + core::Property<Volume> volume; |
725 | + core::Property<PlaybackRate> minimum_playback_rate; |
726 | + core::Property<PlaybackRate> maximum_playback_rate; |
727 | + core::Property<int64_t> position; |
728 | + core::Property<int64_t> duration; |
729 | + core::Property<AudioStreamRole> audio_stream_role; |
730 | + core::Property<Orientation> orientation; |
731 | + } properties; |
732 | + |
733 | + struct |
734 | + { |
735 | + core::Signal<int64_t> seeked_to; |
736 | + core::Signal<void> end_of_stream; |
737 | + core::Signal<PlaybackStatus> playback_status_changed; |
738 | + core::Signal<video::Dimensions> video_dimension_changed; |
739 | + } signals; |
740 | +}; |
741 | +} |
742 | +} |
743 | +} |
744 | + |
745 | +#endif // CORE_UBUNTU_MEDIA_COMPOSITED_PLAYER_H_ |
746 | |
747 | === modified file 'src/core/media/engine.cpp' |
748 | --- src/core/media/engine.cpp 2014-09-08 10:16:38 +0000 |
749 | +++ src/core/media/engine.cpp 2014-11-19 10:23:50 +0000 |
750 | @@ -49,3 +49,23 @@ |
751 | { |
752 | return value == rhs.value; |
753 | } |
754 | + |
755 | +// Pretty prints the engine state to the given output stream; |
756 | +std::ostream& media::operator<<(std::ostream& out, media::Engine::State state) |
757 | +{ |
758 | + switch (state) |
759 | + { |
760 | + case media::Engine::State::ready: |
761 | + return out << "Engine::State::ready"; |
762 | + case media::Engine::State::busy: |
763 | + return out << "Engine::State::busy"; |
764 | + case media::Engine::State::playing: |
765 | + return out << "Engine::State::playing"; |
766 | + case media::Engine::State::paused: |
767 | + return out << "Engine::State::paused"; |
768 | + case media::Engine::State::stopped: |
769 | + return out << "Engine::State::stopped"; |
770 | + } |
771 | + |
772 | + return out; |
773 | +} |
774 | |
775 | === modified file 'src/core/media/engine.h' |
776 | --- src/core/media/engine.h 2014-11-19 10:23:50 +0000 |
777 | +++ src/core/media/engine.h 2014-11-19 10:23:50 +0000 |
778 | @@ -35,6 +35,7 @@ |
779 | class Engine |
780 | { |
781 | public: |
782 | + typedef std::shared_ptr<Engine> Ptr; |
783 | |
784 | enum class State |
785 | { |
786 | @@ -110,6 +111,9 @@ |
787 | |
788 | virtual void reset() = 0; |
789 | }; |
790 | + |
791 | +// Pretty prints the engine state to the given output stream; |
792 | +std::ostream& operator<<(std::ostream&, Engine::State); |
793 | } |
794 | } |
795 | } |
796 | |
797 | === added file 'src/core/media/engine_factory.h' |
798 | --- src/core/media/engine_factory.h 1970-01-01 00:00:00 +0000 |
799 | +++ src/core/media/engine_factory.h 2014-11-19 10:23:50 +0000 |
800 | @@ -0,0 +1,39 @@ |
801 | +/* |
802 | + * Copyright © 2014 Canonical Ltd. |
803 | + * |
804 | + * This program is free software: you can redistribute it and/or modify it |
805 | + * under the terms of the GNU Lesser General Public License version 3, |
806 | + * as published by the Free Software Foundation. |
807 | + * |
808 | + * This program is distributed in the hope that it will be useful, |
809 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
810 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
811 | + * GNU Lesser General Public License for more details. |
812 | + * |
813 | + * You should have received a copy of the GNU Lesser General Public License |
814 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
815 | + * |
816 | + * Authored by: Thomas Voß <thomas.voss@canonical.com> |
817 | + */ |
818 | + |
819 | +#ifndef CORE_UBUNTU_MEDIA_ENGINE_FACTORY_H_ |
820 | +#define CORE_UBUNTU_MEDIA_ENGINE_FACTORY_H_ |
821 | + |
822 | +#include <functional> |
823 | +#include <memory> |
824 | + |
825 | +namespace core |
826 | +{ |
827 | +namespace ubuntu |
828 | +{ |
829 | +namespace media |
830 | +{ |
831 | +// Forward declared to avoid include. |
832 | +class Engine; |
833 | +// A functor for creating Engine instances. |
834 | +typedef std::function<std::shared_ptr<Engine>()> EngineFactory; |
835 | +} |
836 | +} |
837 | +} |
838 | + |
839 | +#endif // CORE_UBUNTU_MEDIA_ENGINE_FACTORY_H_ |
840 | |
841 | === added file 'src/core/media/gstreamer/engine_factory.cpp' |
842 | --- src/core/media/gstreamer/engine_factory.cpp 1970-01-01 00:00:00 +0000 |
843 | +++ src/core/media/gstreamer/engine_factory.cpp 2014-11-19 10:23:50 +0000 |
844 | @@ -0,0 +1,31 @@ |
845 | +/* |
846 | + * Copyright © 2014 Canonical Ltd. |
847 | + * |
848 | + * This program is free software: you can redistribute it and/or modify it |
849 | + * under the terms of the GNU Lesser General Public License version 3, |
850 | + * as published by the Free Software Foundation. |
851 | + * |
852 | + * This program is distributed in the hope that it will be useful, |
853 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
854 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
855 | + * GNU Lesser General Public License for more details. |
856 | + * |
857 | + * You should have received a copy of the GNU Lesser General Public License |
858 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
859 | + * |
860 | + * Authored by: Thomas Voß <thomas.voss@canonical.com> |
861 | + */ |
862 | + |
863 | +#include <core/media/gstreamer/engine_factory.h> |
864 | + |
865 | +#include <core/media/gstreamer/engine.h> |
866 | + |
867 | +namespace media = core::ubuntu::media; |
868 | + |
869 | +media::EngineFactory media::gstreamer::engine_factory() |
870 | +{ |
871 | + return []() |
872 | + { |
873 | + return std::make_shared<::gstreamer::Engine>(); |
874 | + }; |
875 | +} |
876 | |
877 | === added file 'src/core/media/gstreamer/engine_factory.h' |
878 | --- src/core/media/gstreamer/engine_factory.h 1970-01-01 00:00:00 +0000 |
879 | +++ src/core/media/gstreamer/engine_factory.h 2014-11-19 10:23:50 +0000 |
880 | @@ -0,0 +1,40 @@ |
881 | +/* |
882 | + * Copyright © 2014 Canonical Ltd. |
883 | + * |
884 | + * This program is free software: you can redistribute it and/or modify it |
885 | + * under the terms of the GNU Lesser General Public License version 3, |
886 | + * as published by the Free Software Foundation. |
887 | + * |
888 | + * This program is distributed in the hope that it will be useful, |
889 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
890 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
891 | + * GNU Lesser General Public License for more details. |
892 | + * |
893 | + * You should have received a copy of the GNU Lesser General Public License |
894 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
895 | + * |
896 | + * Authored by: Thomas Voß <thomas.voss@canonical.com> |
897 | + */ |
898 | + |
899 | +#ifndef CORE_UBUNTU_MEDIA_GSTREAMER_ENGINE_FACTORY_H_ |
900 | +#define CORE_UBUNTU_MEDIA_GSTREAMER_ENGINE_FACTORY_H_ |
901 | + |
902 | +#include <core/media/engine_factory.h> |
903 | + |
904 | +namespace core |
905 | +{ |
906 | +namespace ubuntu |
907 | +{ |
908 | +namespace media |
909 | +{ |
910 | +namespace gstreamer |
911 | +{ |
912 | +// Returns a functor that in turn allows for |
913 | +// creating gstreamer::Engine instances. |
914 | +EngineFactory engine_factory(); |
915 | +} |
916 | +} |
917 | +} |
918 | +} |
919 | + |
920 | +#endif // CORE_UBUNTU_MEDIA_GSTREAMER_ENGINE_FACTORY_H_ |
921 | |
922 | === modified file 'src/core/media/gstreamer/playbin.cpp' |
923 | --- src/core/media/gstreamer/playbin.cpp 2014-11-19 10:23:50 +0000 |
924 | +++ src/core/media/gstreamer/playbin.cpp 2014-11-19 10:23:50 +0000 |
925 | @@ -124,7 +124,7 @@ |
926 | |
927 | void gstreamer::Playbin::reset_pipeline() |
928 | { |
929 | - std::cout << __PRETTY_FUNCTION__ << std::endl; |
930 | + std::cout << __PRETTY_FUNCTION__ << std::endl; |
931 | auto ret = gst_element_set_state(pipeline, GST_STATE_NULL); |
932 | switch(ret) |
933 | { |
934 | |
935 | === added file 'src/core/media/hashed_keyed_player_store.cpp' |
936 | --- src/core/media/hashed_keyed_player_store.cpp 1970-01-01 00:00:00 +0000 |
937 | +++ src/core/media/hashed_keyed_player_store.cpp 2014-11-19 10:23:50 +0000 |
938 | @@ -0,0 +1,80 @@ |
939 | +/* |
940 | + * Copyright © 2014 Canonical Ltd. |
941 | + * |
942 | + * This program is free software: you can redistribute it and/or modify it |
943 | + * under the terms of the GNU Lesser General Public License version 3, |
944 | + * as published by the Free Software Foundation. |
945 | + * |
946 | + * This program is distributed in the hope that it will be useful, |
947 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
948 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
949 | + * GNU Lesser General Public License for more details. |
950 | + * |
951 | + * You should have received a copy of the GNU Lesser General Public License |
952 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
953 | + * |
954 | + * Authored by: Thomas Voß <thomas.voss@canonical.com> |
955 | + */ |
956 | + |
957 | +#include <core/media/hashed_keyed_player_store.h> |
958 | + |
959 | +namespace media = core::ubuntu::media; |
960 | + |
961 | +media::HashedKeyedPlayerStore::HashedKeyedPlayerStore() |
962 | +{ |
963 | +} |
964 | + |
965 | +const core::Property<std::shared_ptr<media::Player>>& media::HashedKeyedPlayerStore::current_player() const |
966 | +{ |
967 | + return prop_current_player; |
968 | +} |
969 | + |
970 | +bool media::HashedKeyedPlayerStore::has_player_for_key(const media::Player::PlayerKey& key) const |
971 | +{ |
972 | + std::lock_guard<std::mutex> lg{guard}; |
973 | + return map.count(key) > 0; |
974 | +} |
975 | + |
976 | +std::shared_ptr<media::Player> media::HashedKeyedPlayerStore::player_for_key(const media::Player::PlayerKey& key) const |
977 | +{ |
978 | + std::lock_guard<std::mutex> lg{guard}; |
979 | + auto it = map.find(key); |
980 | + |
981 | + if (it == map.end()) throw std::out_of_range |
982 | + { |
983 | + "HashedKeyedPlayerStore::player_for_key: No player known for " + std::to_string(key) |
984 | + }; |
985 | + |
986 | + return it->second; |
987 | +} |
988 | + |
989 | +void media::HashedKeyedPlayerStore::enumerate_players(const media::KeyedPlayerStore::PlayerEnumerator& enumerator) const |
990 | +{ |
991 | + std::lock_guard<std::mutex> lg{guard}; |
992 | + for (const auto& pair : map) |
993 | + enumerator(pair.first, pair.second); |
994 | +} |
995 | + |
996 | +void media::HashedKeyedPlayerStore::add_player_for_key(const media::Player::PlayerKey& key, const std::shared_ptr<media::Player>& player) |
997 | +{ |
998 | + std::lock_guard<std::mutex> lg{guard}; |
999 | + map[key] = player; |
1000 | +} |
1001 | + |
1002 | +void media::HashedKeyedPlayerStore::remove_player_for_key(const media::Player::PlayerKey& key) |
1003 | +{ |
1004 | + std::lock_guard<std::mutex> lg{guard}; |
1005 | + auto it = map.find(key); |
1006 | + if (it != map.end()) |
1007 | + { |
1008 | + if (prop_current_player == it->second) |
1009 | + prop_current_player = nullptr; |
1010 | + |
1011 | + map.erase(it); |
1012 | + } |
1013 | +} |
1014 | + |
1015 | +void media::HashedKeyedPlayerStore::set_current_player_for_key(const media::Player::PlayerKey& key) |
1016 | +{ |
1017 | + prop_current_player = player_for_key(key); |
1018 | +} |
1019 | |
1020 | === added file 'src/core/media/hashed_keyed_player_store.h' |
1021 | --- src/core/media/hashed_keyed_player_store.h 1970-01-01 00:00:00 +0000 |
1022 | +++ src/core/media/hashed_keyed_player_store.h 2014-11-19 10:23:50 +0000 |
1023 | @@ -0,0 +1,76 @@ |
1024 | +/* |
1025 | + * Copyright © 2014 Canonical Ltd. |
1026 | + * |
1027 | + * This program is free software: you can redistribute it and/or modify it |
1028 | + * under the terms of the GNU Lesser General Public License version 3, |
1029 | + * as published by the Free Software Foundation. |
1030 | + * |
1031 | + * This program is distributed in the hope that it will be useful, |
1032 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1033 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1034 | + * GNU Lesser General Public License for more details. |
1035 | + * |
1036 | + * You should have received a copy of the GNU Lesser General Public License |
1037 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1038 | + * |
1039 | + * Authored by: Thomas Voß <thomas.voss@canonical.com> |
1040 | + */ |
1041 | + |
1042 | +#ifndef CORE_UBUNTU_MEDIA_HASHED_KEYED_PLAYER_STORE_H_ |
1043 | +#define CORE_UBUNTU_MEDIA_HASHED_KEYED_PLAYER_STORE_H_ |
1044 | + |
1045 | +#include <core/media/keyed_player_store.h> |
1046 | + |
1047 | +#include <mutex> |
1048 | +#include <unordered_map> |
1049 | + |
1050 | +namespace core |
1051 | +{ |
1052 | +namespace ubuntu |
1053 | +{ |
1054 | +namespace media |
1055 | +{ |
1056 | +// Implements KeyedPlayerStore using a std::unordered_map. |
1057 | +class HashedKeyedPlayerStore : public KeyedPlayerStore |
1058 | +{ |
1059 | +public: |
1060 | + HashedKeyedPlayerStore(); |
1061 | + // We keep track of the "current" player, that is, the one |
1062 | + // that has been created most recently and provide a getable/observable |
1063 | + // access to that designated instance. |
1064 | + const core::Property<std::shared_ptr<media::Player>>& current_player() const override; |
1065 | + |
1066 | + // We keep track of all known player sessions here and render them accessible via |
1067 | + // the key. All of these functions are thread-safe but not reentrant. |
1068 | + // Returns true iff a player is known for the given key. |
1069 | + bool has_player_for_key(const Player::PlayerKey& key) const override; |
1070 | + |
1071 | + // Returns the player for the given key or throws std::out_of_range if no player is known |
1072 | + // for the given key. |
1073 | + // Throws std::out_of_range if no player is known for the key. |
1074 | + std::shared_ptr<Player> player_for_key(const Player::PlayerKey& key) const override; |
1075 | + |
1076 | + // Enumerates all known players and invokes the given enumerator for each |
1077 | + // (key, player) pair. |
1078 | + void enumerate_players(const PlayerEnumerator& enumerator) const override; |
1079 | + |
1080 | + // Adds the given player with the given key. |
1081 | + void add_player_for_key(const Player::PlayerKey& key, const std::shared_ptr<Player>& player) override; |
1082 | + |
1083 | + // Removes the player for the given key, and unsets it if it is the current one. |
1084 | + void remove_player_for_key(const Player::PlayerKey& key) override; |
1085 | + |
1086 | + // Makes the player known under the given key current. |
1087 | + // Throws std::out_of_range if no player is known for the key. |
1088 | + void set_current_player_for_key(const Player::PlayerKey& key) override; |
1089 | + |
1090 | +private: |
1091 | + core::Property<std::shared_ptr<Player>> prop_current_player; |
1092 | + mutable std::mutex guard; |
1093 | + std::unordered_map<Player::PlayerKey, std::shared_ptr<Player>> map; |
1094 | +}; |
1095 | +} |
1096 | +} |
1097 | +} |
1098 | + |
1099 | +#endif // CORE_UBUNTU_MEDIA_KEYED_PLAYER_STORE_H_ |
1100 | |
1101 | === added file 'src/core/media/keyed_player_store.cpp' |
1102 | === added file 'src/core/media/keyed_player_store.h' |
1103 | --- src/core/media/keyed_player_store.h 1970-01-01 00:00:00 +0000 |
1104 | +++ src/core/media/keyed_player_store.h 2014-11-19 10:23:50 +0000 |
1105 | @@ -0,0 +1,83 @@ |
1106 | +/* |
1107 | + * Copyright © 2014 Canonical Ltd. |
1108 | + * |
1109 | + * This program is free software: you can redistribute it and/or modify it |
1110 | + * under the terms of the GNU Lesser General Public License version 3, |
1111 | + * as published by the Free Software Foundation. |
1112 | + * |
1113 | + * This program is distributed in the hope that it will be useful, |
1114 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1115 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1116 | + * GNU Lesser General Public License for more details. |
1117 | + * |
1118 | + * You should have received a copy of the GNU Lesser General Public License |
1119 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1120 | + * |
1121 | + * Authored by: Thomas Voß <thomas.voss@canonical.com> |
1122 | + */ |
1123 | + |
1124 | +#ifndef CORE_UBUNTU_MEDIA_KEYED_PLAYER_STORE_H_ |
1125 | +#define CORE_UBUNTU_MEDIA_KEYED_PLAYER_STORE_H_ |
1126 | + |
1127 | +#include <core/media/player.h> |
1128 | + |
1129 | +#include <core/property.h> |
1130 | + |
1131 | +#include <functional> |
1132 | +#include <memory> |
1133 | + |
1134 | +namespace core |
1135 | +{ |
1136 | +namespace ubuntu |
1137 | +{ |
1138 | +namespace media |
1139 | +{ |
1140 | +// An interface abstracting keyed lookups of known Player instances. |
1141 | +class KeyedPlayerStore |
1142 | +{ |
1143 | +public: |
1144 | + // Save us some typing. |
1145 | + typedef std::shared_ptr<KeyedPlayerStore> Ptr; |
1146 | + // Functor for enumerating all known (key, player) pairs. |
1147 | + typedef std::function |
1148 | + < |
1149 | + void( |
1150 | + // The key of the player. |
1151 | + const Player::PlayerKey&, |
1152 | + // The actual player instance. |
1153 | + const std::shared_ptr<Player>& |
1154 | + ) |
1155 | + > PlayerEnumerator; |
1156 | + // We keep track of the "current" player, that is, the one |
1157 | + // that has been created most recently and provide a getable/observable |
1158 | + // access to that designated instance. |
1159 | + virtual const core::Property<std::shared_ptr<Player>>& current_player() const = 0; |
1160 | + |
1161 | + // We keep track of all known player sessions here and render them accessible via |
1162 | + // the key. All of these functions are thread-safe but not reentrant. |
1163 | + // Returns true iff a player is known for the given key. |
1164 | + virtual bool has_player_for_key(const Player::PlayerKey& key) const = 0; |
1165 | + // Returns the player for the given key or throws std::out_of_range if no player is known |
1166 | + // for the given key. |
1167 | + virtual std::shared_ptr<Player> player_for_key(const Player::PlayerKey& key) const = 0; |
1168 | + // Enumerates all known players and invokes the given enumerator for each |
1169 | + // (key, player) pair. |
1170 | + virtual void enumerate_players(const PlayerEnumerator& enumerator) const = 0; |
1171 | + // Adds the given player with the given key. |
1172 | + virtual void add_player_for_key(const Player::PlayerKey& key, const std::shared_ptr<Player>& player) = 0; |
1173 | + // Removes the player for the given key, and unsets it if it is the current one. |
1174 | + virtual void remove_player_for_key(const Player::PlayerKey& key) = 0; |
1175 | + // Makes the player known under the given key current. |
1176 | + virtual void set_current_player_for_key(const Player::PlayerKey& key) = 0; |
1177 | + |
1178 | +protected: |
1179 | + KeyedPlayerStore() = default; |
1180 | + KeyedPlayerStore(const KeyedPlayerStore&) = delete; |
1181 | + virtual ~KeyedPlayerStore() = default; |
1182 | + KeyedPlayerStore& operator=(const KeyedPlayerStore&) = delete; |
1183 | +}; |
1184 | +} |
1185 | +} |
1186 | +} |
1187 | + |
1188 | +#endif // CORE_UBUNTU_MEDIA_KEYED_PLAYER_STORE_H_ |
1189 | |
1190 | === added file 'src/core/media/mpris/exported.h' |
1191 | --- src/core/media/mpris/exported.h 1970-01-01 00:00:00 +0000 |
1192 | +++ src/core/media/mpris/exported.h 2014-11-19 10:23:50 +0000 |
1193 | @@ -0,0 +1,305 @@ |
1194 | +/* |
1195 | + * Copyright © 2014 Canonical Ltd. |
1196 | + * |
1197 | + * This program is free software: you can redistribute it and/or modify it |
1198 | + * under the terms of the GNU Lesser General Public License version 3, |
1199 | + * as published by the Free Software Foundation. |
1200 | + * |
1201 | + * This program is distributed in the hope that it will be useful, |
1202 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1203 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1204 | + * GNU Lesser General Public License for more details. |
1205 | + * |
1206 | + * You should have received a copy of the GNU Lesser General Public License |
1207 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1208 | + * |
1209 | + * Authored by: Thomas Voß <thomas.voss@canonical.com> |
1210 | + */ |
1211 | + |
1212 | +#ifndef MPRIS_EXPORTED_H_ |
1213 | +#define MPRIS_EXPORTED_H_ |
1214 | + |
1215 | +#include <core/media/cover_art_resolver.h> |
1216 | +#include <core/media/xesam.h> |
1217 | + |
1218 | +#include <core/media/mpris/media_player2.h> |
1219 | +#include <core/media/mpris/metadata.h> |
1220 | +#include <core/media/mpris/player.h> |
1221 | +#include <core/media/mpris/playlists.h> |
1222 | +#include <core/media/mpris/service.h> |
1223 | + |
1224 | +#include <core/posix/this_process.h> |
1225 | + |
1226 | +namespace mpris |
1227 | +{ |
1228 | +struct Exported |
1229 | +{ |
1230 | + static mpris::MediaPlayer2::Skeleton::Configuration::Defaults media_player_defaults() |
1231 | + { |
1232 | + mpris::MediaPlayer2::Skeleton::Configuration::Defaults defaults; |
1233 | + // TODO(tvoss): These three elements really should be configurable. |
1234 | + defaults.identity = "core::media::Hub"; |
1235 | + defaults.desktop_entry = "mediaplayer-app"; |
1236 | + defaults.supported_mime_types = {"audio/mpeg3"}; |
1237 | + |
1238 | + return defaults; |
1239 | + } |
1240 | + |
1241 | + static mpris::Player::Skeleton::Configuration::Defaults player_defaults() |
1242 | + { |
1243 | + mpris::Player::Skeleton::Configuration::Defaults defaults; |
1244 | + |
1245 | + // Disabled as track list is not fully implemented yet. |
1246 | + defaults.can_go_next = false; |
1247 | + // Disabled as track list is not fully implemented yet. |
1248 | + defaults.can_go_previous = false; |
1249 | + |
1250 | + return defaults; |
1251 | + } |
1252 | + |
1253 | + static std::string service_name() |
1254 | + { |
1255 | + static const bool export_to_indicator_sound_via_mpris |
1256 | + { |
1257 | + core::posix::this_process::env::get("UBUNTU_MEDIA_HUB_EXPORT_TO_INDICATOR_VIA_MPRIS", "0") == "1" |
1258 | + }; |
1259 | + |
1260 | + return export_to_indicator_sound_via_mpris ? "org.mpris.MediaPlayer2.MediaHub" : |
1261 | + "hidden.org.mpris.MediaPlayer2.MediaHub"; |
1262 | + } |
1263 | + |
1264 | + static const core::Signal<void>& the_empty_signal() |
1265 | + { |
1266 | + static const core::Signal<void> instance; |
1267 | + return instance; |
1268 | + } |
1269 | + |
1270 | + explicit Exported(const dbus::Bus::Ptr& bus, const core::ubuntu::media::CoverArtResolver& cover_art_resolver) |
1271 | + : bus{bus}, |
1272 | + service{dbus::Service::add_service(bus, service_name())}, |
1273 | + object{service->add_object_for_path(dbus::types::ObjectPath{"/org/mpris/MediaPlayer2"})}, |
1274 | + media_player{mpris::MediaPlayer2::Skeleton::Configuration{bus, object, media_player_defaults()}}, |
1275 | + player{mpris::Player::Skeleton::Configuration{bus, object, player_defaults()}}, |
1276 | + playlists{mpris::Playlists::Skeleton::Configuration{bus, object, mpris::Playlists::Skeleton::Configuration::Defaults{}}}, |
1277 | + cover_art_resolver{cover_art_resolver} |
1278 | + { |
1279 | + object->install_method_handler<core::dbus::interfaces::Properties::GetAll>([this](const core::dbus::Message::Ptr& msg) |
1280 | + { |
1281 | + // Extract the interface |
1282 | + std::string itf; msg->reader() >> itf; |
1283 | + core::dbus::Message::Ptr reply = core::dbus::Message::make_method_return(msg); |
1284 | + |
1285 | + if (itf == mpris::Player::name()) |
1286 | + reply->writer() << player.get_all_properties(); |
1287 | + else if (itf == mpris::MediaPlayer2::name()) |
1288 | + reply->writer() << media_player.get_all_properties(); |
1289 | + else if (itf == mpris::Playlists::name()) |
1290 | + reply->writer() << playlists.get_all_properties(); |
1291 | + |
1292 | + Exported::bus->send(reply); |
1293 | + }); |
1294 | + |
1295 | + // Setup method handlers for mpris::Player methods. |
1296 | + auto next = [this](const core::dbus::Message::Ptr& msg) |
1297 | + { |
1298 | + auto sp = current_player.lock(); |
1299 | + |
1300 | + if (sp) |
1301 | + sp->next(); |
1302 | + |
1303 | + Exported::bus->send(core::dbus::Message::make_method_return(msg)); |
1304 | + }; |
1305 | + object->install_method_handler<mpris::Player::Next>(next); |
1306 | + |
1307 | + auto previous = [this](const core::dbus::Message::Ptr& msg) |
1308 | + { |
1309 | + auto sp = current_player.lock(); |
1310 | + |
1311 | + if (sp) |
1312 | + sp->previous(); |
1313 | + |
1314 | + Exported::bus->send(core::dbus::Message::make_method_return(msg)); |
1315 | + }; |
1316 | + object->install_method_handler<mpris::Player::Previous>(previous); |
1317 | + |
1318 | + auto pause = [this](const core::dbus::Message::Ptr& msg) |
1319 | + { |
1320 | + auto sp = current_player.lock(); |
1321 | + |
1322 | + if (sp) |
1323 | + sp->pause(); |
1324 | + |
1325 | + Exported::bus->send(core::dbus::Message::make_method_return(msg)); |
1326 | + }; |
1327 | + object->install_method_handler<mpris::Player::Pause>(pause); |
1328 | + |
1329 | + auto stop = [this](const core::dbus::Message::Ptr& msg) |
1330 | + { |
1331 | + auto sp = current_player.lock(); |
1332 | + |
1333 | + if (sp) |
1334 | + sp->stop(); |
1335 | + |
1336 | + Exported::bus->send(core::dbus::Message::make_method_return(msg)); |
1337 | + }; |
1338 | + object->install_method_handler<mpris::Player::Stop>(stop); |
1339 | + |
1340 | + auto play = [this](const core::dbus::Message::Ptr& msg) |
1341 | + { |
1342 | + auto sp = current_player.lock(); |
1343 | + |
1344 | + if (sp) |
1345 | + sp->play(); |
1346 | + |
1347 | + Exported::bus->send(core::dbus::Message::make_method_return(msg)); |
1348 | + }; |
1349 | + object->install_method_handler<mpris::Player::Play>(play); |
1350 | + |
1351 | + auto play_pause = [this](const core::dbus::Message::Ptr& msg) |
1352 | + { |
1353 | + auto sp = current_player.lock(); |
1354 | + |
1355 | + if (sp) |
1356 | + { |
1357 | + if (sp->playback_status() == core::ubuntu::media::Player::PlaybackStatus::playing) |
1358 | + sp->pause(); |
1359 | + else if (sp->playback_status() != core::ubuntu::media::Player::PlaybackStatus::null) |
1360 | + sp->play(); |
1361 | + } |
1362 | + |
1363 | + Exported::bus->send(core::dbus::Message::make_method_return(msg)); |
1364 | + }; |
1365 | + object->install_method_handler<mpris::Player::PlayPause>(play_pause); |
1366 | + } |
1367 | + |
1368 | + void set_current_player(const std::shared_ptr<core::ubuntu::media::Player>& cp) |
1369 | + { |
1370 | + unset_current_player(); |
1371 | + |
1372 | + if (not cp) |
1373 | + return; |
1374 | + |
1375 | + // We will not keep the object alive. |
1376 | + current_player = cp; |
1377 | + |
1378 | + // And announce that we can be controlled again. |
1379 | + player.properties.can_control->set(false); |
1380 | + |
1381 | + // We wire up player state changes |
1382 | + connections.seeked_to = cp->seeked_to().connect([this](std::uint64_t position) |
1383 | + { |
1384 | + player.signals.seeked_to->emit(position); |
1385 | + }); |
1386 | + |
1387 | + connections.duration_changed = cp->duration().changed().connect([this](std::uint64_t duration) |
1388 | + { |
1389 | + player.properties.duration->set(duration); |
1390 | + }); |
1391 | + |
1392 | + connections.position_changed = cp->position().changed().connect([this](std::uint64_t position) |
1393 | + { |
1394 | + player.properties.position->set(position); |
1395 | + }); |
1396 | + |
1397 | + connections.playback_status_changed = cp->playback_status().changed().connect([this](core::ubuntu::media::Player::PlaybackStatus status) |
1398 | + { |
1399 | + player.properties.playback_status->set(mpris::Player::PlaybackStatus::from(status)); |
1400 | + }); |
1401 | + |
1402 | + connections.loop_status_changed = cp->loop_status().changed().connect([this](core::ubuntu::media::Player::LoopStatus status) |
1403 | + { |
1404 | + player.properties.loop_status->set(mpris::Player::LoopStatus::from(status)); |
1405 | + }); |
1406 | + |
1407 | + connections.meta_data_changed = cp->meta_data_for_current_track().changed().connect([this](const core::ubuntu::media::Track::MetaData& md) |
1408 | + { |
1409 | + mpris::Player::Dictionary dict; |
1410 | + |
1411 | + bool has_title = md.count(xesam::Title::name) > 0; |
1412 | + bool has_album_name = md.count(xesam::Album::name) > 0; |
1413 | + bool has_artist_name = md.count(xesam::Artist::name) > 0; |
1414 | + |
1415 | + if (has_title) |
1416 | + dict[xesam::Title::name] = dbus::types::Variant::encode(md.get(xesam::Title::name)); |
1417 | + if (has_album_name) |
1418 | + dict[xesam::Album::name] = dbus::types::Variant::encode(md.get(xesam::Album::name)); |
1419 | + if (has_artist_name) |
1420 | + dict[xesam::Artist::name] = dbus::types::Variant::encode(md.get(xesam::Artist::name)); |
1421 | + |
1422 | + dict[mpris::metadata::ArtUrl::name] = dbus::types::Variant::encode( |
1423 | + cover_art_resolver( |
1424 | + has_title ? md.get(xesam::Title::name) : "", |
1425 | + has_album_name ? md.get(xesam::Album::name) : "", |
1426 | + has_artist_name ? md.get(xesam::Artist::name) : "")); |
1427 | + |
1428 | + mpris::Player::Dictionary wrap; |
1429 | + wrap[mpris::Player::Properties::Metadata::name()] = dbus::types::Variant::encode(dict); |
1430 | + |
1431 | + player.signals.properties_changed->emit( |
1432 | + std::make_tuple( |
1433 | + dbus::traits::Service<mpris::Player::Properties::Metadata::Interface>::interface_name(), |
1434 | + wrap, |
1435 | + std::vector<std::string>())); |
1436 | + }); |
1437 | + } |
1438 | + |
1439 | + void unset_current_player() |
1440 | + { |
1441 | + current_player.reset(); |
1442 | + |
1443 | + // We disconnect all previous event connections. |
1444 | + connections.seeked_to.disconnect(); |
1445 | + connections.duration_changed.disconnect(); |
1446 | + connections.position_changed.disconnect(); |
1447 | + connections.playback_status_changed.disconnect(); |
1448 | + connections.loop_status_changed.disconnect(); |
1449 | + connections.meta_data_changed.disconnect(); |
1450 | + |
1451 | + // And announce that we cannot be controlled anymore. |
1452 | + player.properties.can_control->set(false); |
1453 | + } |
1454 | + |
1455 | + dbus::Bus::Ptr bus; |
1456 | + dbus::Service::Ptr service; |
1457 | + dbus::Object::Ptr object; |
1458 | + |
1459 | + mpris::MediaPlayer2::Skeleton media_player; |
1460 | + mpris::Player::Skeleton player; |
1461 | + mpris::Playlists::Skeleton playlists; |
1462 | + |
1463 | + // Helper to resolve (title, artist, album) tuples to cover art. |
1464 | + core::ubuntu::media::CoverArtResolver cover_art_resolver; |
1465 | + // The actual player instance. |
1466 | + std::weak_ptr<core::ubuntu::media::Player> current_player; |
1467 | + // We track event connections. |
1468 | + struct |
1469 | + { |
1470 | + core::Connection seeked_to |
1471 | + { |
1472 | + the_empty_signal().connect([](){}) |
1473 | + }; |
1474 | + core::Connection duration_changed |
1475 | + { |
1476 | + the_empty_signal().connect([](){}) |
1477 | + }; |
1478 | + core::Connection position_changed |
1479 | + { |
1480 | + the_empty_signal().connect([](){}) |
1481 | + }; |
1482 | + core::Connection playback_status_changed |
1483 | + { |
1484 | + the_empty_signal().connect([](){}) |
1485 | + }; |
1486 | + core::Connection loop_status_changed |
1487 | + { |
1488 | + the_empty_signal().connect([](){}) |
1489 | + }; |
1490 | + core::Connection meta_data_changed |
1491 | + { |
1492 | + the_empty_signal().connect([](){}) |
1493 | + }; |
1494 | + } connections; |
1495 | +}; |
1496 | +} |
1497 | + |
1498 | +#endif // MPRIS_EXPORTED_H_ |
1499 | |
1500 | === added file 'src/core/media/mutable_player.h' |
1501 | --- src/core/media/mutable_player.h 1970-01-01 00:00:00 +0000 |
1502 | +++ src/core/media/mutable_player.h 2014-11-19 10:23:50 +0000 |
1503 | @@ -0,0 +1,60 @@ |
1504 | +/** |
1505 | + * Copyright (C) 2014 Canonical Ltd |
1506 | + * |
1507 | + * This program is free software: you can redistribute it and/or modify it |
1508 | + * under the terms of the GNU Lesser General Public License version 3, |
1509 | + * as published by the Free Software Foundation. |
1510 | + * |
1511 | + * This program is distributed in the hope that it will be useful, |
1512 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1513 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1514 | + * GNU Lesser General Public License for more details. |
1515 | + * |
1516 | + * You should have received a copy of the GNU Lesser General Public License |
1517 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1518 | + * |
1519 | + * Authored by: Thomas Voß <thomas.voss@canonical.com> |
1520 | + */ |
1521 | + |
1522 | +#ifndef CORE_UBUNTU_MEDIA_MUTABLE_PLAYER_H_ |
1523 | +#define CORE_UBUNTU_MEDIA_MUTABLE_PLAYER_H_ |
1524 | + |
1525 | +#include <core/media/player.h> |
1526 | + |
1527 | +namespace core |
1528 | +{ |
1529 | +namespace ubuntu |
1530 | +{ |
1531 | +namespace media |
1532 | +{ |
1533 | +// A player implementation that provides mutable |
1534 | +// access to properties and signals to its subclasses. |
1535 | +class MutablePlayer : public Player |
1536 | +{ |
1537 | +public: |
1538 | + MutablePlayer() = default; |
1539 | + |
1540 | + virtual core::Property<PlaybackStatus>& playback_status() = 0; |
1541 | + virtual core::Property<bool>& can_play() = 0; |
1542 | + virtual core::Property<bool>& can_pause() = 0; |
1543 | + virtual core::Property<bool>& can_seek() = 0; |
1544 | + virtual core::Property<bool>& can_go_previous() = 0; |
1545 | + virtual core::Property<bool>& can_go_next() = 0; |
1546 | + virtual core::Property<bool>& is_video_source() = 0; |
1547 | + virtual core::Property<bool>& is_audio_source() = 0; |
1548 | + virtual core::Property<Track::MetaData>& meta_data_for_current_track() = 0; |
1549 | + virtual core::Property<PlaybackRate>& minimum_playback_rate() = 0; |
1550 | + virtual core::Property<PlaybackRate>& maximum_playback_rate() = 0; |
1551 | + virtual core::Property<int64_t>& position() = 0; |
1552 | + virtual core::Property<int64_t>& duration() = 0; |
1553 | + virtual core::Property<Orientation>& orientation() = 0; |
1554 | + |
1555 | + virtual core::Signal<int64_t>& seeked_to() = 0; |
1556 | + virtual core::Signal<void>& end_of_stream() = 0; |
1557 | + virtual core::Signal<video::Dimensions>& video_dimension_changed() = 0; |
1558 | +}; |
1559 | +} |
1560 | +} |
1561 | +} |
1562 | + |
1563 | +#endif // CORE_UBUNTU_MEDIA_MUTABLE_PLAYER_H_ |
1564 | |
1565 | === modified file 'src/core/media/player.cpp' |
1566 | --- src/core/media/player.cpp 2014-11-19 10:23:50 +0000 |
1567 | +++ src/core/media/player.cpp 2014-11-19 10:23:50 +0000 |
1568 | @@ -29,13 +29,7 @@ |
1569 | |
1570 | const media::Player::Configuration& media::Player::Client::default_configuration() |
1571 | { |
1572 | - static const media::Player::Configuration config |
1573 | - { |
1574 | - std::string{""}, |
1575 | - 0, |
1576 | - nullptr, |
1577 | - nullptr |
1578 | - }; |
1579 | + static const media::Player::Configuration config{core::dbus::types::ObjectPath{"/"}, 0}; |
1580 | return config; |
1581 | } |
1582 | |
1583 | |
1584 | === modified file 'src/core/media/player_configuration.h' |
1585 | --- src/core/media/player_configuration.h 2014-09-09 10:28:32 +0000 |
1586 | +++ src/core/media/player_configuration.h 2014-11-19 10:23:50 +0000 |
1587 | @@ -20,22 +20,16 @@ |
1588 | |
1589 | #include <core/media/player.h> |
1590 | |
1591 | -#include <core/dbus/bus.h> |
1592 | -#include <core/dbus/object.h> |
1593 | +#include <core/dbus/types/object_path.h> |
1594 | |
1595 | // Our internal structure for handing down parameters from the skeleton |
1596 | // to the implementation in a way that is opaque to the client. |
1597 | struct core::ubuntu::media::Player::Configuration |
1598 | { |
1599 | - // An identifier that is helpful in referencing the player instance |
1600 | - // across multiple services. |
1601 | - std::string identity; |
1602 | + // Object path for the new player instance. |
1603 | + core::dbus::types::ObjectPath path; |
1604 | // Unique key for identifying the session. |
1605 | core::ubuntu::media::Player::PlayerKey key; |
1606 | - // The bus connection to expose objects on. |
1607 | - std::shared_ptr<core::dbus::Bus> bus; |
1608 | - // The actual session object representing a player instance. |
1609 | - std::shared_ptr<core::dbus::Object> session; |
1610 | }; |
1611 | |
1612 | #endif // CORE_UBUNTU_MEDIA_PLAYER_CLIENT_CONFIGURATION_H_ |
1613 | |
1614 | === modified file 'src/core/media/player_implementation.cpp' |
1615 | --- src/core/media/player_implementation.cpp 2014-11-19 10:23:50 +0000 |
1616 | +++ src/core/media/player_implementation.cpp 2014-11-19 10:23:50 +0000 |
1617 | @@ -25,8 +25,6 @@ |
1618 | #include "engine.h" |
1619 | #include "track_list_implementation.h" |
1620 | |
1621 | -#include "gstreamer/engine.h" |
1622 | - |
1623 | #include <memory> |
1624 | #include <exception> |
1625 | #include <iostream> |
1626 | @@ -55,11 +53,11 @@ |
1627 | config(config), |
1628 | display_state_lock(config.power_state_controller->display_state_lock()), |
1629 | system_state_lock(config.power_state_controller->system_state_lock()), |
1630 | - engine(std::make_shared<gstreamer::Engine>()), |
1631 | - track_list( |
1632 | + engine(config.engine), |
1633 | + /*track_list( |
1634 | new media::TrackListImplementation( |
1635 | config.session->path().as_string() + "/TrackList", |
1636 | - engine->meta_data_extractor())), |
1637 | + engine->meta_data_extractor())),*/ |
1638 | system_wakelock_count(0), |
1639 | display_wakelock_count(0), |
1640 | previous_state(Engine::State::stopped), |
1641 | @@ -103,7 +101,7 @@ |
1642 | parent->playback_status().set(media::Player::ready); |
1643 | if (previous_state == Engine::State::playing) |
1644 | { |
1645 | - timeout(4000, true, make_clear_wakelock_functor()); |
1646 | + timeout(config.default_timeout_for_holding_to_power_state_locks.count(), config.timeouts_are_async, make_clear_wakelock_functor()); |
1647 | } |
1648 | break; |
1649 | } |
1650 | @@ -122,7 +120,7 @@ |
1651 | parent->playback_status().set(media::Player::stopped); |
1652 | if (previous_state == Engine::State::playing) |
1653 | { |
1654 | - timeout(4000, true, make_clear_wakelock_functor()); |
1655 | + timeout(config.default_timeout_for_holding_to_power_state_locks.count(), config.timeouts_are_async, make_clear_wakelock_functor()); |
1656 | } |
1657 | break; |
1658 | } |
1659 | @@ -131,7 +129,7 @@ |
1660 | parent->playback_status().set(media::Player::paused); |
1661 | if (previous_state == Engine::State::playing) |
1662 | { |
1663 | - timeout(4000, true, make_clear_wakelock_functor()); |
1664 | + timeout(config.default_timeout_for_holding_to_power_state_locks.count(), config.timeouts_are_async, make_clear_wakelock_functor()); |
1665 | } |
1666 | break; |
1667 | } |
1668 | @@ -244,7 +242,7 @@ |
1669 | media::power::StateController::Lock<media::power::DisplayState>::Ptr display_state_lock; |
1670 | media::power::StateController::Lock<media::power::SystemState>::Ptr system_state_lock; |
1671 | |
1672 | - std::shared_ptr<Engine> engine; |
1673 | + media::Engine::Ptr engine; |
1674 | std::shared_ptr<TrackListImplementation> track_list; |
1675 | std::atomic<int> system_wakelock_count; |
1676 | std::atomic<int> display_wakelock_count; |
1677 | @@ -254,16 +252,7 @@ |
1678 | }; |
1679 | |
1680 | media::PlayerImplementation::PlayerImplementation(const media::PlayerImplementation::Configuration& config) |
1681 | - : media::PlayerSkeleton |
1682 | - { |
1683 | - media::PlayerSkeleton::Configuration |
1684 | - { |
1685 | - config.bus, |
1686 | - config.session, |
1687 | - config.identity |
1688 | - } |
1689 | - }, |
1690 | - d{std::make_shared<Private>(this, config)} |
1691 | + : d{std::make_shared<Private>(this, config)} |
1692 | { |
1693 | // Initialize default values for Player interface properties |
1694 | can_play().set(true); |
1695 | |
1696 | === modified file 'src/core/media/player_implementation.h' |
1697 | --- src/core/media/player_implementation.h 2014-11-19 10:23:50 +0000 |
1698 | +++ src/core/media/player_implementation.h 2014-11-19 10:23:50 +0000 |
1699 | @@ -19,9 +19,10 @@ |
1700 | #ifndef CORE_UBUNTU_MEDIA_PLAYER_IMPLEMENTATION_H_ |
1701 | #define CORE_UBUNTU_MEDIA_PLAYER_IMPLEMENTATION_H_ |
1702 | |
1703 | -#include "player_skeleton.h" |
1704 | +#include "base_player.h" |
1705 | |
1706 | #include "client_death_observer.h" |
1707 | +#include "engine.h" |
1708 | #include "power/state_controller.h" |
1709 | |
1710 | #include <memory> |
1711 | @@ -35,19 +36,20 @@ |
1712 | class Engine; |
1713 | class Service; |
1714 | |
1715 | -class PlayerImplementation : public PlayerSkeleton |
1716 | +class PlayerImplementation : public BasePlayer |
1717 | { |
1718 | public: |
1719 | // All creation time arguments go here |
1720 | struct Configuration |
1721 | { |
1722 | - std::string identity; |
1723 | - std::shared_ptr<core::dbus::Bus> bus; |
1724 | - std::shared_ptr<core::dbus::Object> session; |
1725 | - std::shared_ptr<Service> service; |
1726 | + // A unique player key. |
1727 | PlayerKey key; |
1728 | - |
1729 | - // Functional dependencies |
1730 | + // Whether timeouts should be executed asynchronously. |
1731 | + bool timeouts_are_async; |
1732 | + // The default timeout we use for holding to power state locks. |
1733 | + std::chrono::milliseconds default_timeout_for_holding_to_power_state_locks; |
1734 | + // Functional dependencies. |
1735 | + Engine::Ptr engine; |
1736 | ClientDeathObserver::Ptr client_death_observer; |
1737 | power::StateController::Ptr power_state_controller; |
1738 | }; |
1739 | |
1740 | === modified file 'src/core/media/player_skeleton.cpp' |
1741 | --- src/core/media/player_skeleton.cpp 2014-11-19 10:23:50 +0000 |
1742 | +++ src/core/media/player_skeleton.cpp 2014-11-19 10:23:50 +0000 |
1743 | @@ -41,91 +41,164 @@ |
1744 | namespace dbus = core::dbus; |
1745 | namespace media = core::ubuntu::media; |
1746 | |
1747 | +namespace |
1748 | +{ |
1749 | +mpris::Player::Skeleton::Configuration::Defaults make_configuration_defaults(const std::shared_ptr<media::Player>& impl) |
1750 | +{ |
1751 | + mpris::Player::Skeleton::Configuration::Defaults defaults; |
1752 | + defaults.can_play = impl->can_play(); |
1753 | + defaults.can_pause = impl->can_pause(); |
1754 | + defaults.can_seek = impl->can_seek(); |
1755 | + defaults.can_go_next = impl->can_go_next(); |
1756 | + defaults.can_go_previous = impl->can_go_previous(); |
1757 | + defaults.is_video_source = impl->is_video_source(); |
1758 | + defaults.is_audio_source = impl->is_audio_source(); |
1759 | + defaults.playback_status = mpris::Player::PlaybackStatus::from(impl->playback_status()); |
1760 | + defaults.typed_playback_status = impl->playback_status(); |
1761 | + defaults.loop_status = mpris::Player::LoopStatus::from(impl->loop_status()); |
1762 | + defaults.typed_loop_status = impl->loop_status(); |
1763 | + defaults.playback_rate = impl->playback_rate(); |
1764 | + defaults.shuffle = impl->is_shuffle(); |
1765 | + defaults.playback_rate = impl->playback_rate(); |
1766 | + defaults.typed_meta_data = impl->meta_data_for_current_track(); |
1767 | + defaults.volume = impl->volume(); |
1768 | + defaults.position = impl->position(); |
1769 | + defaults.duration = impl->duration(); |
1770 | + defaults.minimum_rate = impl->minimum_playback_rate(); |
1771 | + defaults.maximum_rate = impl->maximum_playback_rate(); |
1772 | + defaults.orientation = impl->orientation(); |
1773 | + |
1774 | + return defaults; |
1775 | +} |
1776 | +} |
1777 | struct media::PlayerSkeleton::Private |
1778 | { |
1779 | - Private(media::PlayerSkeleton* player, |
1780 | - const std::string& identity, |
1781 | - const std::shared_ptr<core::dbus::Bus>& bus, |
1782 | - const std::shared_ptr<core::dbus::Object>& session) |
1783 | - : impl(player), |
1784 | - identity(identity), |
1785 | - bus(bus), |
1786 | - object(session), |
1787 | - apparmor_session(nullptr), |
1788 | - dbus_stub{bus}, |
1789 | - skeleton{mpris::Player::Skeleton::Configuration{bus, session, mpris::Player::Skeleton::Configuration::Defaults{}}}, |
1790 | - signals |
1791 | - { |
1792 | - skeleton.signals.seeked_to, |
1793 | - skeleton.signals.end_of_stream, |
1794 | - skeleton.signals.playback_status_changed, |
1795 | - skeleton.signals.video_dimension_changed |
1796 | + Private(media::PlayerSkeleton* parent, |
1797 | + const media::PlayerSkeleton::Configuration& config) |
1798 | + : parent(parent), |
1799 | + configuration(config), |
1800 | + dbus_stub{configuration.bus}, |
1801 | + skeleton |
1802 | + { |
1803 | + mpris::Player::Skeleton::Configuration |
1804 | + { |
1805 | + configuration.bus, |
1806 | + configuration.session, |
1807 | + make_configuration_defaults(configuration.impl) |
1808 | + } |
1809 | + }, |
1810 | + connections |
1811 | + { |
1812 | + configuration.impl->seeked_to().connect([this](std::uint64_t value) |
1813 | + { |
1814 | + skeleton.signals.seeked_to->emit(value); |
1815 | + }), |
1816 | + configuration.impl->end_of_stream().connect([this]() |
1817 | + { |
1818 | + skeleton.signals.end_of_stream->emit(); |
1819 | + }), |
1820 | + configuration.impl->playback_status_changed().connect([this](const media::Player::PlaybackStatus& status) |
1821 | + { |
1822 | + skeleton.signals.playback_status_changed->emit(status); |
1823 | + }), |
1824 | + configuration.impl->video_dimension_changed().connect([this](const media::video::Dimensions& mask) |
1825 | + { |
1826 | + skeleton.signals.video_dimension_changed->emit(mask); |
1827 | + }) |
1828 | } |
1829 | { |
1830 | + // We wire properties together at this point. Whenever something changes |
1831 | + // on the implementation, we forward the new value to the skeleton. |
1832 | + configuration.impl->can_play() | *skeleton.properties.can_play; |
1833 | + configuration.impl->can_pause() | *skeleton.properties.can_pause; |
1834 | + configuration.impl->can_seek() | *skeleton.properties.can_seek; |
1835 | + configuration.impl->can_go_previous() | *skeleton.properties.can_go_previous; |
1836 | + configuration.impl->can_go_next() | *skeleton.properties.can_go_next; |
1837 | + configuration.impl->is_video_source() | *skeleton.properties.is_video_source; |
1838 | + configuration.impl->is_audio_source() | *skeleton.properties.is_audio_source; |
1839 | + configuration.impl->playback_status() | *skeleton.properties.typed_playback_status; |
1840 | + configuration.impl->loop_status() | *skeleton.properties.typed_loop_status; |
1841 | + configuration.impl->playback_rate() | *skeleton.properties.playback_rate; |
1842 | + configuration.impl->is_shuffle() | *skeleton.properties.is_shuffle; |
1843 | + configuration.impl->meta_data_for_current_track() | *skeleton.properties.typed_meta_data_for_current_track; |
1844 | + configuration.impl->volume() | *skeleton.properties.volume; |
1845 | + configuration.impl->minimum_playback_rate() | *skeleton.properties.minimum_playback_rate; |
1846 | + configuration.impl->maximum_playback_rate() | *skeleton.properties.maximum_playback_rate; |
1847 | + configuration.impl->position() | *skeleton.properties.position; |
1848 | + configuration.impl->duration() | *skeleton.properties.duration; |
1849 | + configuration.impl->audio_stream_role() | *skeleton.properties.audio_stream_role; |
1850 | + configuration.impl->orientation() | *skeleton.properties.orientation; |
1851 | + // And vice versa. Please note that we will not enter an infinite loop as Property<> |
1852 | + // only invokes changed handlers if the new value is actually different from the old value. |
1853 | + *skeleton.properties.typed_loop_status | configuration.impl->loop_status(); |
1854 | + *skeleton.properties.playback_rate | configuration.impl->playback_rate(); |
1855 | + *skeleton.properties.is_shuffle | configuration.impl->is_shuffle(); |
1856 | + *skeleton.properties.volume | configuration.impl->volume(); |
1857 | + *skeleton.properties.audio_stream_role | configuration.impl->audio_stream_role(); |
1858 | } |
1859 | |
1860 | void handle_next(const core::dbus::Message::Ptr& msg) |
1861 | { |
1862 | - impl->next(); |
1863 | + parent->next(); |
1864 | auto reply = dbus::Message::make_method_return(msg); |
1865 | - bus->send(reply); |
1866 | + configuration.bus->send(reply); |
1867 | } |
1868 | |
1869 | void handle_previous(const core::dbus::Message::Ptr& msg) |
1870 | { |
1871 | - impl->previous(); |
1872 | + parent->previous(); |
1873 | auto reply = dbus::Message::make_method_return(msg); |
1874 | - bus->send(reply); |
1875 | + configuration.bus->send(reply); |
1876 | } |
1877 | |
1878 | void handle_pause(const core::dbus::Message::Ptr& msg) |
1879 | { |
1880 | - impl->pause(); |
1881 | + parent->pause(); |
1882 | auto reply = dbus::Message::make_method_return(msg); |
1883 | - bus->send(reply); |
1884 | + configuration.bus->send(reply); |
1885 | } |
1886 | |
1887 | void handle_stop(const core::dbus::Message::Ptr& msg) |
1888 | { |
1889 | - impl->stop(); |
1890 | + parent->stop(); |
1891 | auto reply = dbus::Message::make_method_return(msg); |
1892 | - bus->send(reply); |
1893 | + configuration.bus->send(reply); |
1894 | } |
1895 | |
1896 | void handle_play(const core::dbus::Message::Ptr& msg) |
1897 | { |
1898 | - impl->play(); |
1899 | + parent->play(); |
1900 | auto reply = dbus::Message::make_method_return(msg); |
1901 | - bus->send(reply); |
1902 | + configuration.bus->send(reply); |
1903 | } |
1904 | |
1905 | void handle_play_pause(const core::dbus::Message::Ptr& msg) |
1906 | { |
1907 | - switch(impl->playback_status().get()) |
1908 | + switch(parent->playback_status().get()) |
1909 | { |
1910 | case core::ubuntu::media::Player::PlaybackStatus::ready: |
1911 | case core::ubuntu::media::Player::PlaybackStatus::paused: |
1912 | case core::ubuntu::media::Player::PlaybackStatus::stopped: |
1913 | - impl->play(); |
1914 | + parent->play(); |
1915 | break; |
1916 | case core::ubuntu::media::Player::PlaybackStatus::playing: |
1917 | - impl->pause(); |
1918 | + parent->pause(); |
1919 | break; |
1920 | default: |
1921 | break; |
1922 | } |
1923 | |
1924 | - bus->send(dbus::Message::make_method_return(msg)); |
1925 | + configuration.bus->send(dbus::Message::make_method_return(msg)); |
1926 | } |
1927 | |
1928 | void handle_seek(const core::dbus::Message::Ptr& in) |
1929 | { |
1930 | uint64_t ticks; |
1931 | in->reader() >> ticks; |
1932 | - impl->seek_to(std::chrono::microseconds(ticks)); |
1933 | + parent->seek_to(std::chrono::microseconds(ticks)); |
1934 | |
1935 | auto reply = dbus::Message::make_method_return(in); |
1936 | - bus->send(reply); |
1937 | + configuration.bus->send(reply); |
1938 | } |
1939 | |
1940 | void handle_set_position(const core::dbus::Message::Ptr&) |
1941 | @@ -141,7 +214,7 @@ |
1942 | |
1943 | try |
1944 | { |
1945 | - impl->create_gl_texture_video_sink(texture_id); |
1946 | + parent->create_gl_texture_video_sink(texture_id); |
1947 | reply = dbus::Message::make_method_return(in); |
1948 | } |
1949 | catch (const media::Player::Error::OutOfProcessBufferStreamingNotSupported& e) |
1950 | @@ -159,7 +232,7 @@ |
1951 | std::string{}); |
1952 | } |
1953 | |
1954 | - bus->send(reply); |
1955 | + configuration.bus->send(reply); |
1956 | } |
1957 | |
1958 | bool does_client_have_access(const std::string& context, const std::string& uri) |
1959 | @@ -241,8 +314,8 @@ |
1960 | void handle_key(const core::dbus::Message::Ptr& in) |
1961 | { |
1962 | auto reply = dbus::Message::make_method_return(in); |
1963 | - reply->writer() << impl->key(); |
1964 | - bus->send(reply); |
1965 | + reply->writer() << parent->key(); |
1966 | + configuration.bus->send(reply); |
1967 | } |
1968 | |
1969 | void handle_open_uri(const core::dbus::Message::Ptr& in) |
1970 | @@ -255,122 +328,65 @@ |
1971 | bool have_access = does_client_have_access(profile, uri); |
1972 | |
1973 | auto reply = dbus::Message::make_method_return(in); |
1974 | - reply->writer() << (have_access ? impl->open_uri(uri) : false); |
1975 | + reply->writer() << (have_access ? parent->open_uri(uri) : false); |
1976 | |
1977 | - bus->send(reply); |
1978 | + configuration.bus->send(reply); |
1979 | }); |
1980 | } |
1981 | |
1982 | - template<typename Property> |
1983 | - void on_property_value_changed( |
1984 | - const typename Property::ValueType& value, |
1985 | - const dbus::Signal |
1986 | - < |
1987 | - core::dbus::interfaces::Properties::Signals::PropertiesChanged, |
1988 | - core::dbus::interfaces::Properties::Signals::PropertiesChanged::ArgumentType |
1989 | - >::Ptr& signal) |
1990 | - { |
1991 | - typedef std::map<std::string, dbus::types::Variant> Dictionary; |
1992 | - |
1993 | - static const std::vector<std::string> the_empty_list_of_invalidated_properties; |
1994 | - |
1995 | - Dictionary dict; dict[Property::name()] = dbus::types::Variant::encode(value); |
1996 | - |
1997 | - signal->emit(std::make_tuple( |
1998 | - dbus::traits::Service<typename Property::Interface>::interface_name(), |
1999 | - dict, |
2000 | - the_empty_list_of_invalidated_properties)); |
2001 | - } |
2002 | - |
2003 | - media::PlayerSkeleton* impl; |
2004 | - std::string identity; |
2005 | - dbus::Bus::Ptr bus; |
2006 | - dbus::Object::Ptr object; |
2007 | - dbus::Object::Ptr apparmor_session; |
2008 | - |
2009 | + media::PlayerSkeleton* parent; |
2010 | + media::PlayerSkeleton::Configuration configuration; |
2011 | org::freedesktop::dbus::DBus::Stub dbus_stub; |
2012 | |
2013 | mpris::Player::Skeleton skeleton; |
2014 | - |
2015 | - struct Signals |
2016 | + // Event connections go here. |
2017 | + struct |
2018 | { |
2019 | - typedef core::dbus::Signal<mpris::Player::Signals::Seeked, mpris::Player::Signals::Seeked::ArgumentType> DBusSeekedToSignal; |
2020 | - typedef core::dbus::Signal<mpris::Player::Signals::EndOfStream, mpris::Player::Signals::EndOfStream::ArgumentType> DBusEndOfStreamSignal; |
2021 | - typedef core::dbus::Signal<mpris::Player::Signals::PlaybackStatusChanged, mpris::Player::Signals::PlaybackStatusChanged::ArgumentType> DBusPlaybackStatusChangedSignal; |
2022 | - typedef core::dbus::Signal<mpris::Player::Signals::VideoDimensionChanged, mpris::Player::Signals::VideoDimensionChanged::ArgumentType> DBusVideoDimensionChangedSignal; |
2023 | - |
2024 | - Signals(const std::shared_ptr<DBusSeekedToSignal>& remote_seeked, |
2025 | - const std::shared_ptr<DBusEndOfStreamSignal>& remote_eos, |
2026 | - const std::shared_ptr<DBusPlaybackStatusChangedSignal>& remote_playback_status_changed, |
2027 | - const std::shared_ptr<DBusVideoDimensionChangedSignal>& remote_video_dimension_changed) |
2028 | - { |
2029 | - seeked_to.connect([remote_seeked](std::uint64_t value) |
2030 | - { |
2031 | - remote_seeked->emit(value); |
2032 | - }); |
2033 | - |
2034 | - end_of_stream.connect([remote_eos]() |
2035 | - { |
2036 | - remote_eos->emit(); |
2037 | - }); |
2038 | - |
2039 | - playback_status_changed.connect([remote_playback_status_changed](const media::Player::PlaybackStatus& status) |
2040 | - { |
2041 | - remote_playback_status_changed->emit(status); |
2042 | - }); |
2043 | - |
2044 | - video_dimension_changed.connect([remote_video_dimension_changed](const media::video::Dimensions& mask) |
2045 | - { |
2046 | - remote_video_dimension_changed->emit(mask); |
2047 | - }); |
2048 | - } |
2049 | - |
2050 | - core::Signal<int64_t> seeked_to; |
2051 | - core::Signal<void> end_of_stream; |
2052 | - core::Signal<media::Player::PlaybackStatus> playback_status_changed; |
2053 | - core::Signal<media::video::Dimensions> video_dimension_changed; |
2054 | - } signals; |
2055 | - |
2056 | + core::ScopedConnection seeked_to; |
2057 | + core::ScopedConnection end_of_stream; |
2058 | + core::ScopedConnection playback_status_changed; |
2059 | + core::ScopedConnection video_dimension_changed; |
2060 | + } connections; |
2061 | }; |
2062 | |
2063 | media::PlayerSkeleton::PlayerSkeleton(const media::PlayerSkeleton::Configuration& config) |
2064 | - : d(new Private{this, config.identity, config.bus, config.session}) |
2065 | + : d(new Private{this, config}) |
2066 | { |
2067 | // Setup method handlers for mpris::Player methods. |
2068 | auto next = std::bind(&Private::handle_next, d, std::placeholders::_1); |
2069 | - d->object->install_method_handler<mpris::Player::Next>(next); |
2070 | + d->configuration.session->install_method_handler<mpris::Player::Next>(next); |
2071 | |
2072 | auto previous = std::bind(&Private::handle_previous, d, std::placeholders::_1); |
2073 | - d->object->install_method_handler<mpris::Player::Previous>(previous); |
2074 | + d->configuration.session->install_method_handler<mpris::Player::Previous>(previous); |
2075 | |
2076 | auto pause = std::bind(&Private::handle_pause, d, std::placeholders::_1); |
2077 | - d->object->install_method_handler<mpris::Player::Pause>(pause); |
2078 | + d->configuration.session->install_method_handler<mpris::Player::Pause>(pause); |
2079 | |
2080 | auto stop = std::bind(&Private::handle_stop, d, std::placeholders::_1); |
2081 | - d->object->install_method_handler<mpris::Player::Stop>(stop); |
2082 | + d->configuration.session->install_method_handler<mpris::Player::Stop>(stop); |
2083 | |
2084 | auto play = std::bind(&Private::handle_play, d, std::placeholders::_1); |
2085 | - d->object->install_method_handler<mpris::Player::Play>(play); |
2086 | + d->configuration.session->install_method_handler<mpris::Player::Play>(play); |
2087 | |
2088 | auto play_pause = std::bind(&Private::handle_play_pause, d, std::placeholders::_1); |
2089 | - d->object->install_method_handler<mpris::Player::PlayPause>(play_pause); |
2090 | + d->configuration.session->install_method_handler<mpris::Player::PlayPause>(play_pause); |
2091 | |
2092 | auto seek = std::bind(&Private::handle_seek, d, std::placeholders::_1); |
2093 | - d->object->install_method_handler<mpris::Player::Seek>(seek); |
2094 | + d->configuration.session->install_method_handler<mpris::Player::Seek>(seek); |
2095 | |
2096 | auto set_position = std::bind(&Private::handle_set_position, d, std::placeholders::_1); |
2097 | - d->object->install_method_handler<mpris::Player::SetPosition>(set_position); |
2098 | + d->configuration.session->install_method_handler<mpris::Player::SetPosition>(set_position); |
2099 | |
2100 | auto open_uri = std::bind(&Private::handle_open_uri, d, std::placeholders::_1); |
2101 | - d->object->install_method_handler<mpris::Player::OpenUri>(open_uri); |
2102 | + d->configuration.session->install_method_handler<mpris::Player::OpenUri>(open_uri); |
2103 | |
2104 | // All the method handlers that exceed the mpris spec go here. |
2105 | - d->object->install_method_handler<mpris::Player::CreateVideoSink>( |
2106 | + d->configuration.session->install_method_handler<mpris::Player::CreateVideoSink>( |
2107 | std::bind(&Private::handle_create_video_sink, |
2108 | d, |
2109 | std::placeholders::_1)); |
2110 | |
2111 | - d->object->install_method_handler<mpris::Player::Key>( |
2112 | + d->configuration.session->install_method_handler<mpris::Player::Key>( |
2113 | std::bind(&Private::handle_key, |
2114 | d, |
2115 | std::placeholders::_1)); |
2116 | @@ -380,17 +396,67 @@ |
2117 | { |
2118 | // The session object may outlive the private instance |
2119 | // so uninstall all method handlers. |
2120 | - d->object->uninstall_method_handler<mpris::Player::Next>(); |
2121 | - d->object->uninstall_method_handler<mpris::Player::Previous>(); |
2122 | - d->object->uninstall_method_handler<mpris::Player::Pause>(); |
2123 | - d->object->uninstall_method_handler<mpris::Player::Stop>(); |
2124 | - d->object->uninstall_method_handler<mpris::Player::Play>(); |
2125 | - d->object->uninstall_method_handler<mpris::Player::PlayPause>(); |
2126 | - d->object->uninstall_method_handler<mpris::Player::Seek>(); |
2127 | - d->object->uninstall_method_handler<mpris::Player::SetPosition>(); |
2128 | - d->object->uninstall_method_handler<mpris::Player::OpenUri>(); |
2129 | - d->object->uninstall_method_handler<mpris::Player::CreateVideoSink>(); |
2130 | - d->object->uninstall_method_handler<mpris::Player::Key>(); |
2131 | + d->configuration.session->uninstall_method_handler<mpris::Player::Next>(); |
2132 | + d->configuration.session->uninstall_method_handler<mpris::Player::Previous>(); |
2133 | + d->configuration.session->uninstall_method_handler<mpris::Player::Pause>(); |
2134 | + d->configuration.session->uninstall_method_handler<mpris::Player::Stop>(); |
2135 | + d->configuration.session->uninstall_method_handler<mpris::Player::Play>(); |
2136 | + d->configuration.session->uninstall_method_handler<mpris::Player::PlayPause>(); |
2137 | + d->configuration.session->uninstall_method_handler<mpris::Player::Seek>(); |
2138 | + d->configuration.session->uninstall_method_handler<mpris::Player::SetPosition>(); |
2139 | + d->configuration.session->uninstall_method_handler<mpris::Player::OpenUri>(); |
2140 | + d->configuration.session->uninstall_method_handler<mpris::Player::CreateVideoSink>(); |
2141 | + d->configuration.session->uninstall_method_handler<mpris::Player::Key>(); |
2142 | +} |
2143 | + |
2144 | +std::shared_ptr<media::TrackList> media::PlayerSkeleton::track_list() |
2145 | +{ |
2146 | + return d->configuration.impl->track_list(); |
2147 | +} |
2148 | + |
2149 | +media::Player::PlayerKey media::PlayerSkeleton::key() const |
2150 | +{ |
2151 | + return d->configuration.impl->key(); |
2152 | +} |
2153 | + |
2154 | +media::video::Sink::Ptr media::PlayerSkeleton::create_gl_texture_video_sink(std::uint32_t texture_id) |
2155 | +{ |
2156 | + return d->configuration.impl->create_gl_texture_video_sink(texture_id); |
2157 | +} |
2158 | + |
2159 | +bool media::PlayerSkeleton::open_uri(const Track::UriType& uri) |
2160 | +{ |
2161 | + return d->configuration.impl->open_uri(uri); |
2162 | +} |
2163 | + |
2164 | +void media::PlayerSkeleton::next() |
2165 | +{ |
2166 | + return d->configuration.impl->next(); |
2167 | +} |
2168 | + |
2169 | +void media::PlayerSkeleton::previous() |
2170 | +{ |
2171 | + return d->configuration.impl->previous(); |
2172 | +} |
2173 | + |
2174 | +void media::PlayerSkeleton::play() |
2175 | +{ |
2176 | + return d->configuration.impl->play(); |
2177 | +} |
2178 | + |
2179 | +void media::PlayerSkeleton::pause() |
2180 | +{ |
2181 | + return d->configuration.impl->pause(); |
2182 | +} |
2183 | + |
2184 | +void media::PlayerSkeleton::stop() |
2185 | +{ |
2186 | + return d->configuration.impl->stop(); |
2187 | +} |
2188 | + |
2189 | +void media::PlayerSkeleton::seek_to(const std::chrono::microseconds& offset) |
2190 | +{ |
2191 | + return d->configuration.impl->seek_to(offset); |
2192 | } |
2193 | |
2194 | const core::Property<bool>& media::PlayerSkeleton::can_play() const |
2195 | @@ -488,6 +554,26 @@ |
2196 | return *d->skeleton.properties.maximum_playback_rate; |
2197 | } |
2198 | |
2199 | +const core::Signal<int64_t>& media::PlayerSkeleton::seeked_to() const |
2200 | +{ |
2201 | + return d->configuration.impl->seeked_to(); |
2202 | +} |
2203 | + |
2204 | +const core::Signal<void>& media::PlayerSkeleton::end_of_stream() const |
2205 | +{ |
2206 | + return d->configuration.impl->end_of_stream(); |
2207 | +} |
2208 | + |
2209 | +core::Signal<media::Player::PlaybackStatus>& media::PlayerSkeleton::playback_status_changed() |
2210 | +{ |
2211 | + return d->configuration.impl->playback_status_changed(); |
2212 | +} |
2213 | + |
2214 | +const core::Signal<media::video::Dimensions>& media::PlayerSkeleton::video_dimension_changed() const |
2215 | +{ |
2216 | + return d->configuration.impl->video_dimension_changed(); |
2217 | +} |
2218 | + |
2219 | core::Property<media::Player::LoopStatus>& media::PlayerSkeleton::loop_status() |
2220 | { |
2221 | return *d->skeleton.properties.typed_loop_status; |
2222 | @@ -508,112 +594,7 @@ |
2223 | return *d->skeleton.properties.volume; |
2224 | } |
2225 | |
2226 | -core::Property<int64_t>& media::PlayerSkeleton::position() |
2227 | -{ |
2228 | - return *d->skeleton.properties.position; |
2229 | -} |
2230 | - |
2231 | -core::Property<int64_t>& media::PlayerSkeleton::duration() |
2232 | -{ |
2233 | - return *d->skeleton.properties.duration; |
2234 | -} |
2235 | - |
2236 | core::Property<media::Player::AudioStreamRole>& media::PlayerSkeleton::audio_stream_role() |
2237 | { |
2238 | return *d->skeleton.properties.audio_stream_role; |
2239 | } |
2240 | - |
2241 | -core::Property<media::Player::Orientation>& media::PlayerSkeleton::orientation() |
2242 | -{ |
2243 | - return *d->skeleton.properties.orientation; |
2244 | -} |
2245 | - |
2246 | -core::Property<media::Player::PlaybackStatus>& media::PlayerSkeleton::playback_status() |
2247 | -{ |
2248 | - return *d->skeleton.properties.typed_playback_status; |
2249 | -} |
2250 | - |
2251 | -core::Property<bool>& media::PlayerSkeleton::can_play() |
2252 | -{ |
2253 | - return *d->skeleton.properties.can_play; |
2254 | -} |
2255 | - |
2256 | -core::Property<bool>& media::PlayerSkeleton::can_pause() |
2257 | -{ |
2258 | - return *d->skeleton.properties.can_pause; |
2259 | -} |
2260 | - |
2261 | -core::Property<bool>& media::PlayerSkeleton::can_seek() |
2262 | -{ |
2263 | - return *d->skeleton.properties.can_seek; |
2264 | -} |
2265 | - |
2266 | -core::Property<bool>& media::PlayerSkeleton::can_go_previous() |
2267 | -{ |
2268 | - return *d->skeleton.properties.can_go_previous; |
2269 | -} |
2270 | - |
2271 | -core::Property<bool>& media::PlayerSkeleton::can_go_next() |
2272 | -{ |
2273 | - return *d->skeleton.properties.can_go_next; |
2274 | -} |
2275 | - |
2276 | -core::Property<bool>& media::PlayerSkeleton::is_video_source() |
2277 | -{ |
2278 | - return *d->skeleton.properties.is_video_source; |
2279 | -} |
2280 | - |
2281 | -core::Property<bool>& media::PlayerSkeleton::is_audio_source() |
2282 | -{ |
2283 | - return *d->skeleton.properties.is_audio_source; |
2284 | -} |
2285 | - |
2286 | -core::Property<media::Track::MetaData>& media::PlayerSkeleton::meta_data_for_current_track() |
2287 | -{ |
2288 | - return *d->skeleton.properties.typed_meta_data_for_current_track; |
2289 | -} |
2290 | - |
2291 | -core::Property<media::Player::PlaybackRate>& media::PlayerSkeleton::minimum_playback_rate() |
2292 | -{ |
2293 | - return *d->skeleton.properties.minimum_playback_rate; |
2294 | -} |
2295 | - |
2296 | -core::Property<media::Player::PlaybackRate>& media::PlayerSkeleton::maximum_playback_rate() |
2297 | -{ |
2298 | - return *d->skeleton.properties.maximum_playback_rate; |
2299 | -} |
2300 | - |
2301 | -const core::Signal<int64_t>& media::PlayerSkeleton::seeked_to() const |
2302 | -{ |
2303 | - return d->signals.seeked_to; |
2304 | -} |
2305 | - |
2306 | -core::Signal<int64_t>& media::PlayerSkeleton::seeked_to() |
2307 | -{ |
2308 | - return d->signals.seeked_to; |
2309 | -} |
2310 | - |
2311 | -const core::Signal<void>& media::PlayerSkeleton::end_of_stream() const |
2312 | -{ |
2313 | - return d->signals.end_of_stream; |
2314 | -} |
2315 | - |
2316 | -core::Signal<void>& media::PlayerSkeleton::end_of_stream() |
2317 | -{ |
2318 | - return d->signals.end_of_stream; |
2319 | -} |
2320 | - |
2321 | -core::Signal<media::Player::PlaybackStatus>& media::PlayerSkeleton::playback_status_changed() |
2322 | -{ |
2323 | - return d->signals.playback_status_changed; |
2324 | -} |
2325 | - |
2326 | -const core::Signal<media::video::Dimensions>& media::PlayerSkeleton::video_dimension_changed() const |
2327 | -{ |
2328 | - return d->signals.video_dimension_changed; |
2329 | -} |
2330 | - |
2331 | -core::Signal<media::video::Dimensions>& media::PlayerSkeleton::video_dimension_changed() |
2332 | -{ |
2333 | - return d->signals.video_dimension_changed; |
2334 | -} |
2335 | |
2336 | === modified file 'src/core/media/player_skeleton.h' |
2337 | --- src/core/media/player_skeleton.h 2014-11-19 10:23:50 +0000 |
2338 | +++ src/core/media/player_skeleton.h 2014-11-19 10:23:50 +0000 |
2339 | @@ -22,6 +22,7 @@ |
2340 | |
2341 | #include <core/media/player.h> |
2342 | |
2343 | +#include "mutable_player.h" |
2344 | #include "player_traits.h" |
2345 | |
2346 | #include "mpris/player.h" |
2347 | @@ -42,8 +43,33 @@ |
2348 | class PlayerSkeleton : public core::ubuntu::media::Player |
2349 | { |
2350 | public: |
2351 | + // All creation time arguments go here. |
2352 | + struct Configuration |
2353 | + { |
2354 | + // The actual Player implementation. |
2355 | + std::shared_ptr<Player> impl; |
2356 | + // The bus connection we are associated with. |
2357 | + std::shared_ptr<core::dbus::Bus> bus; |
2358 | + // The session object that we want to expose the skeleton upon. |
2359 | + std::shared_ptr<core::dbus::Object> session; |
2360 | + }; |
2361 | + |
2362 | + PlayerSkeleton(const Configuration& configuration); |
2363 | ~PlayerSkeleton(); |
2364 | |
2365 | + virtual std::shared_ptr<TrackList> track_list() override; |
2366 | + virtual PlayerKey key() const override; |
2367 | + |
2368 | + virtual video::Sink::Ptr create_gl_texture_video_sink(std::uint32_t texture_id) override; |
2369 | + |
2370 | + virtual bool open_uri(const Track::UriType& uri) override; |
2371 | + virtual void next() override; |
2372 | + virtual void previous() override; |
2373 | + virtual void play() override; |
2374 | + virtual void pause() override; |
2375 | + virtual void stop() override; |
2376 | + virtual void seek_to(const std::chrono::microseconds& offset) override; |
2377 | + |
2378 | virtual const core::Property<bool>& can_play() const; |
2379 | virtual const core::Property<bool>& can_pause() const; |
2380 | virtual const core::Property<bool>& can_seek() const; |
2381 | @@ -75,42 +101,6 @@ |
2382 | virtual core::Signal<PlaybackStatus>& playback_status_changed(); |
2383 | virtual const core::Signal<video::Dimensions>& video_dimension_changed() const; |
2384 | |
2385 | -protected: |
2386 | - // All creation time arguments go here. |
2387 | - struct Configuration |
2388 | - { |
2389 | - // The bus connection we are associated with. |
2390 | - std::shared_ptr<core::dbus::Bus> bus; |
2391 | - // The session object that we want to expose the skeleton upon. |
2392 | - std::shared_ptr<core::dbus::Object> session; |
2393 | - // Our identity, an identifier we pass out to other parts of the system. |
2394 | - // Defaults to the short app id (${PKG_NAME}_${APP}). |
2395 | - std::string identity; |
2396 | - }; |
2397 | - |
2398 | - PlayerSkeleton(const Configuration& configuration); |
2399 | - |
2400 | - // These properties are not exposed to the client, but still need to be |
2401 | - // able to be settable from within the Player: |
2402 | - virtual core::Property<PlaybackStatus>& playback_status(); |
2403 | - virtual core::Property<bool>& can_play(); |
2404 | - virtual core::Property<bool>& can_pause(); |
2405 | - virtual core::Property<bool>& can_seek(); |
2406 | - virtual core::Property<bool>& can_go_previous(); |
2407 | - virtual core::Property<bool>& can_go_next(); |
2408 | - virtual core::Property<bool>& is_video_source(); |
2409 | - virtual core::Property<bool>& is_audio_source(); |
2410 | - virtual core::Property<Track::MetaData>& meta_data_for_current_track(); |
2411 | - virtual core::Property<PlaybackRate>& minimum_playback_rate(); |
2412 | - virtual core::Property<PlaybackRate>& maximum_playback_rate(); |
2413 | - virtual core::Property<int64_t>& position(); |
2414 | - virtual core::Property<int64_t>& duration(); |
2415 | - virtual core::Property<Orientation>& orientation(); |
2416 | - |
2417 | - virtual core::Signal<int64_t>& seeked_to(); |
2418 | - virtual core::Signal<void>& end_of_stream(); |
2419 | - virtual core::Signal<video::Dimensions>& video_dimension_changed(); |
2420 | - |
2421 | private: |
2422 | struct Private; |
2423 | std::shared_ptr<Private> d; |
2424 | |
2425 | === modified file 'src/core/media/player_stub.h' |
2426 | --- src/core/media/player_stub.h 2014-11-19 10:23:50 +0000 |
2427 | +++ src/core/media/player_stub.h 2014-11-19 10:23:50 +0000 |
2428 | @@ -87,7 +87,7 @@ |
2429 | virtual core::Signal<PlaybackStatus>& playback_status_changed(); |
2430 | virtual const core::Signal<video::Dimensions>& video_dimension_changed() const; |
2431 | |
2432 | - private: |
2433 | +private: |
2434 | struct Private; |
2435 | std::unique_ptr<Private> d; |
2436 | }; |
2437 | |
2438 | === modified file 'src/core/media/power/state_controller.cpp' |
2439 | --- src/core/media/power/state_controller.cpp 2014-11-19 10:23:50 +0000 |
2440 | +++ src/core/media/power/state_controller.cpp 2014-11-19 10:23:50 +0000 |
2441 | @@ -300,3 +300,33 @@ |
2442 | { |
2443 | return std::make_shared<impl::StateController>(external_services); |
2444 | } |
2445 | + |
2446 | +// Pretty prints the given state to the given output stream. |
2447 | +std::ostream& media::power::operator<<(std::ostream& out, media::power::DisplayState state) |
2448 | +{ |
2449 | + switch (state) |
2450 | + { |
2451 | + case media::power::DisplayState::off: |
2452 | + return out << "DisplayState::off"; |
2453 | + case media::power::DisplayState::on: |
2454 | + return out << "DisplayState::on"; |
2455 | + } |
2456 | + |
2457 | + return out; |
2458 | +} |
2459 | + |
2460 | +// Pretty prints the given state to the given output stream. |
2461 | +std::ostream& media::power::operator<<(std::ostream& out, media::power::SystemState state) |
2462 | +{ |
2463 | + switch (state) |
2464 | + { |
2465 | + case media::power::SystemState::suspend: |
2466 | + return out << "SystemState::suspend"; |
2467 | + case media::power::SystemState::active: |
2468 | + return out << "SystemState::active"; |
2469 | + case media::power::SystemState::blank_on_proximity: |
2470 | + return out << "SystemState::blank_on_proximity"; |
2471 | + } |
2472 | + |
2473 | + return out; |
2474 | +} |
2475 | |
2476 | === modified file 'src/core/media/power/state_controller.h' |
2477 | --- src/core/media/power/state_controller.h 2014-11-19 10:23:50 +0000 |
2478 | +++ src/core/media/power/state_controller.h 2014-11-19 10:23:50 +0000 |
2479 | @@ -95,6 +95,11 @@ |
2480 | // Creates a StateController instance that connects to the platform default |
2481 | // services to control system and display power states. |
2482 | StateController::Ptr make_platform_default_state_controller(core::ubuntu::media::helper::ExternalServices&); |
2483 | + |
2484 | +// Pretty prints the given state to the given output stream. |
2485 | +std::ostream& operator<<(std::ostream&, DisplayState); |
2486 | +// Pretty prints the given state to the given output stream. |
2487 | +std::ostream& operator<<(std::ostream&, SystemState); |
2488 | } |
2489 | } |
2490 | } |
2491 | |
2492 | === modified file 'src/core/media/server/server.cpp' |
2493 | --- src/core/media/server/server.cpp 2014-11-19 10:23:50 +0000 |
2494 | +++ src/core/media/server/server.cpp 2014-11-19 10:23:50 +0000 |
2495 | @@ -22,7 +22,12 @@ |
2496 | |
2497 | #include <core/posix/signal.h> |
2498 | |
2499 | +#include "core/media/hashed_keyed_player_store.h" |
2500 | #include "core/media/service_implementation.h" |
2501 | +#include "core/media/service_skeleton.h" |
2502 | +#include "core/media/service_traits.h" |
2503 | + |
2504 | +#include "core/media/gstreamer/engine_factory.h" |
2505 | |
2506 | #include <iostream> |
2507 | |
2508 | @@ -98,31 +103,52 @@ |
2509 | } |
2510 | }; |
2511 | |
2512 | - // We assemble the configuration for executing the service now. |
2513 | - media::ServiceImplementation::Configuration service_config |
2514 | + // We use a simple, hashed store for our player intances. |
2515 | + auto player_store = std::make_shared<media::HashedKeyedPlayerStore>(); |
2516 | + // We assemble the configuration for executing the service now. |
2517 | + media::ServiceImplementation::Configuration impl_config |
2518 | { |
2519 | [&external_services](core::ubuntu::media::Task task) |
2520 | { |
2521 | external_services.io_service.post(task); |
2522 | }, |
2523 | + player_store, |
2524 | media::platform_default_client_death_observer(), |
2525 | + media::gstreamer::engine_factory(), |
2526 | media::make_platform_default_recorder_observer(), |
2527 | media::power::make_platform_default_state_controller(external_services), |
2528 | media::power::make_platform_default_battery_observer(external_services), |
2529 | media::telephony::make_platform_default_call_monitor() |
2530 | }; |
2531 | - |
2532 | - auto service = std::make_shared<media::ServiceImplementation>(service_config); |
2533 | + auto impl = media::ServiceImplementation::create(impl_config); |
2534 | + |
2535 | + // And we assemble the skeleton. |
2536 | + auto bus = std::make_shared<core::dbus::Bus>(core::dbus::WellKnownBus::session); |
2537 | + bus->install_executor(core::dbus::asio::make_executor(bus)); |
2538 | + |
2539 | + auto service = core::dbus::Service::add_service<media::Service>(bus); |
2540 | + auto object = service->add_object_for_path( |
2541 | + core::dbus::types::ObjectPath{core::dbus::traits::Service<media::Service>::object_path()}); |
2542 | + media::ServiceSkeleton::Configuration skeleton_config |
2543 | + { |
2544 | + impl, |
2545 | + bus, |
2546 | + service, |
2547 | + object, |
2548 | + player_store, |
2549 | + media::always_missing_cover_art_resolver() |
2550 | + }; |
2551 | + auto skeleton = std::make_shared<media::ServiceSkeleton>(skeleton_config); |
2552 | |
2553 | std::thread service_worker |
2554 | { |
2555 | - [&shutdown_requested, service]() |
2556 | + [&shutdown_requested, bus]() |
2557 | { |
2558 | while (not shutdown_requested) |
2559 | { |
2560 | try |
2561 | { |
2562 | - service->run(); |
2563 | + bus->run(); |
2564 | } |
2565 | catch (const std::exception& e) |
2566 | { |
2567 | @@ -144,7 +170,7 @@ |
2568 | |
2569 | // And stop execution of helper and actual service. |
2570 | external_services.stop(); |
2571 | - service->stop(); |
2572 | + bus->stop(); |
2573 | |
2574 | if (external_services_worker.joinable()) |
2575 | external_services_worker.join(); |
2576 | |
2577 | === modified file 'src/core/media/service_implementation.cpp' |
2578 | --- src/core/media/service_implementation.cpp 2014-11-19 10:23:50 +0000 |
2579 | +++ src/core/media/service_implementation.cpp 2014-11-19 10:23:50 +0000 |
2580 | @@ -38,10 +38,11 @@ |
2581 | struct media::ServiceImplementation::Private |
2582 | { |
2583 | Private(ServiceImplementation& parent, const media::ServiceImplementation::Configuration& config) |
2584 | - : resume_key{std::numeric_limits<std::uint32_t>::max()}, |
2585 | - parent{parent}, |
2586 | + : parent{parent}, |
2587 | dispatcher{config.dispatcher}, |
2588 | + player_store{config.player_store}, |
2589 | display_state_lock{config.power_state_controller->display_state_lock()}, |
2590 | + engine_factory{config.engine_factory}, |
2591 | power_state_controller{config.power_state_controller}, |
2592 | power_state_observer{config.power_state_observer}, |
2593 | call_monitor(config.call_monitor), |
2594 | @@ -64,19 +65,29 @@ |
2595 | } |
2596 | }), |
2597 | power_state_observer->is_warning_active().changed().connect([this](bool notifying) |
2598 | - { |
2599 | - // If the low battery level notification is no longer being displayed, |
2600 | - // resume what the user was previously playing |
2601 | - if (!notifying) |
2602 | - this->parent.resume_multimedia_session(); |
2603 | + { |
2604 | + if (notifying) |
2605 | + { |
2606 | + // We likely will have received a change in battery power level |
2607 | + // prior to this notification. However, let's be safe and just |
2608 | + // pause all multimedia sessions here. It's a no-op if they are |
2609 | + // already paused. |
2610 | + this->parent.pause_all_multimedia_sessions(); |
2611 | + } |
2612 | + else |
2613 | + { |
2614 | + // If the low battery level notification is no longer being displayed, |
2615 | + // resume what the user was previously playing |
2616 | + this->parent.resume_paused_multimedia_sessions(); |
2617 | + } |
2618 | }), |
2619 | call_monitor->on_call_state_changed().connect([this](media::telephony::CallMonitor::State state) { |
2620 | switch (state) { |
2621 | case media::telephony::CallMonitor::OffHook: |
2622 | - this->parent.pause_all_multimedia_sessions(); |
2623 | + this->parent.resume_paused_multimedia_sessions(); |
2624 | break; |
2625 | case media::telephony::CallMonitor::OnHook: |
2626 | - this->parent.resume_paused_multimedia_sessions(); |
2627 | + this->parent.pause_all_multimedia_sessions(); |
2628 | break; |
2629 | } |
2630 | }), |
2631 | @@ -103,17 +114,15 @@ |
2632 | display_state_lock->request_release(media::power::DisplayState::on); |
2633 | break; |
2634 | } |
2635 | - } |
2636 | - |
2637 | - // This holds the key of the multimedia role Player instance that was paused |
2638 | - // when the battery level reached 10% or 5% |
2639 | - media::Player::PlayerKey resume_key; |
2640 | + } |
2641 | |
2642 | media::ServiceImplementation& parent; |
2643 | |
2644 | media::Dispatcher dispatcher; |
2645 | + media::KeyedPlayerStore::Ptr player_store; |
2646 | |
2647 | media::power::StateController::Lock<media::power::DisplayState>::Ptr display_state_lock; |
2648 | + media::EngineFactory engine_factory; |
2649 | media::power::StateController::Ptr power_state_controller; |
2650 | media::power::BatteryObserver::Ptr power_state_observer; |
2651 | media::telephony::CallMonitor::Ptr call_monitor; |
2652 | @@ -131,6 +140,12 @@ |
2653 | std::list<media::Player::PlayerKey> paused_sessions; |
2654 | }; |
2655 | |
2656 | +// Create an instance with the given configuration. |
2657 | +media::ServiceImplementation::Ptr media::ServiceImplementation::create(const media::ServiceImplementation::Configuration& config) |
2658 | +{ |
2659 | + return media::ServiceImplementation::Ptr{new media::ServiceImplementation{config}}; |
2660 | +} |
2661 | + |
2662 | media::ServiceImplementation::ServiceImplementation(const media::ServiceImplementation::Configuration& config) |
2663 | : d(new Private(*this, config)) |
2664 | { |
2665 | @@ -143,13 +158,15 @@ |
2666 | std::shared_ptr<media::Player> media::ServiceImplementation::create_session( |
2667 | const media::Player::Configuration& conf) |
2668 | { |
2669 | + static const bool timeouts_are_async_in_production{true}; |
2670 | + static const std::chrono::milliseconds default_timeout_for_power_state_locks{4000}; |
2671 | + |
2672 | media::PlayerImplementation::Configuration player_config |
2673 | { |
2674 | - conf.identity, |
2675 | - conf.bus, |
2676 | - conf.session, |
2677 | - ServiceImplementation::shared_from_this(), |
2678 | conf.key, |
2679 | + timeouts_are_async_in_production, |
2680 | + default_timeout_for_power_state_locks, |
2681 | + d->engine_factory(), |
2682 | d->client_death_observer, |
2683 | d->power_state_controller |
2684 | }; |
2685 | @@ -170,7 +187,7 @@ |
2686 | // until all dispatches are done |
2687 | sp->d->dispatcher([sp, key]() |
2688 | { |
2689 | - sp->remove_player_for_key(key); |
2690 | + sp->d->player_store->remove_player_for_key(key); |
2691 | }); |
2692 | }); |
2693 | |
2694 | @@ -179,19 +196,19 @@ |
2695 | |
2696 | void media::ServiceImplementation::pause_other_sessions(media::Player::PlayerKey key) |
2697 | { |
2698 | - if (not has_player_for_key(key)) |
2699 | + if (not d->player_store->has_player_for_key(key)) |
2700 | { |
2701 | cerr << "Could not find Player by key: " << key << endl; |
2702 | return; |
2703 | } |
2704 | |
2705 | - auto current_player = player_for_key(key); |
2706 | + auto current_player = d->player_store->player_for_key(key); |
2707 | |
2708 | // We immediately make the player known as new current player. |
2709 | if (current_player->audio_stream_role() == media::Player::multimedia) |
2710 | - set_current_player_for_key(key); |
2711 | + d->player_store->set_current_player_for_key(key); |
2712 | |
2713 | - enumerate_players([current_player, key](const media::Player::PlayerKey& other_key, const std::shared_ptr<media::Player>& other_player) |
2714 | + d->player_store->enumerate_players([current_player, key](const media::Player::PlayerKey& other_key, const std::shared_ptr<media::Player>& other_player) |
2715 | { |
2716 | // Only pause a Player if all of the following criteria are met: |
2717 | // 1) currently playing |
2718 | @@ -211,37 +228,23 @@ |
2719 | |
2720 | void media::ServiceImplementation::pause_all_multimedia_sessions() |
2721 | { |
2722 | - enumerate_players([this](const media::Player::PlayerKey& key, const std::shared_ptr<media::Player>& player) |
2723 | - { |
2724 | - if (player->playback_status() == Player::playing |
2725 | - && player->audio_stream_role() == media::Player::multimedia) |
2726 | - { |
2727 | - d->paused_sessions.push_back(key); |
2728 | - player->pause(); |
2729 | - } |
2730 | - }); |
2731 | + d->player_store->enumerate_players([this](const media::Player::PlayerKey& key, const std::shared_ptr<media::Player>& player) |
2732 | + { |
2733 | + if (player->playback_status() == Player::playing |
2734 | + && player->audio_stream_role() == media::Player::multimedia) |
2735 | + { |
2736 | + d->paused_sessions.push_back(key); |
2737 | + player->pause(); |
2738 | + } |
2739 | + }); |
2740 | } |
2741 | |
2742 | void media::ServiceImplementation::resume_paused_multimedia_sessions() |
2743 | { |
2744 | - std::for_each(d->paused_sessions.begin(), d->paused_sessions.end(), [this](const media::Player::PlayerKey& key) { |
2745 | - player_for_key(key)->play(); |
2746 | - }); |
2747 | + std::for_each(d->paused_sessions.begin(), d->paused_sessions.end(), [this](const media::Player::PlayerKey& key) |
2748 | + { |
2749 | + d->player_store->player_for_key(key)->play(); |
2750 | + }); |
2751 | |
2752 | d->paused_sessions.clear(); |
2753 | } |
2754 | - |
2755 | -void media::ServiceImplementation::resume_multimedia_session() |
2756 | -{ |
2757 | - if (not has_player_for_key(d->resume_key)) |
2758 | - return; |
2759 | - |
2760 | - auto player = player_for_key(d->resume_key); |
2761 | - |
2762 | - if (player->playback_status() == Player::paused) |
2763 | - { |
2764 | - cout << "Resuming playback of Player with key: " << d->resume_key << endl; |
2765 | - player->play(); |
2766 | - d->resume_key = std::numeric_limits<std::uint32_t>::max(); |
2767 | - } |
2768 | -} |
2769 | |
2770 | === modified file 'src/core/media/service_implementation.h' |
2771 | --- src/core/media/service_implementation.h 2014-11-19 10:23:50 +0000 |
2772 | +++ src/core/media/service_implementation.h 2014-11-19 10:23:50 +0000 |
2773 | @@ -19,52 +19,61 @@ |
2774 | #ifndef CORE_UBUNTU_MEDIA_SERVICE_IMPLEMENTATION_H_ |
2775 | #define CORE_UBUNTU_MEDIA_SERVICE_IMPLEMENTATION_H_ |
2776 | |
2777 | +#include <core/media/service.h> |
2778 | + |
2779 | #include "dispatcher.h" |
2780 | |
2781 | #include "client_death_observer.h" |
2782 | +#include "engine_factory.h" |
2783 | +#include "keyed_player_store.h" |
2784 | #include "recorder_observer.h" |
2785 | #include "power/battery_observer.h" |
2786 | #include "power/state_controller.h" |
2787 | #include "telephony/call_monitor.h" |
2788 | |
2789 | -#include "service_skeleton.h" |
2790 | - |
2791 | namespace core |
2792 | { |
2793 | namespace ubuntu |
2794 | { |
2795 | namespace media |
2796 | { |
2797 | - |
2798 | class Player; |
2799 | |
2800 | class ServiceImplementation |
2801 | - : public ServiceSkeleton, |
2802 | + : public Service, |
2803 | public std::enable_shared_from_this<ServiceImplementation> |
2804 | { |
2805 | public: |
2806 | + // To save us some typing |
2807 | + typedef std::shared_ptr<ServiceImplementation> Ptr; |
2808 | + |
2809 | // All creation time arguments go here. |
2810 | struct Configuration |
2811 | { |
2812 | Dispatcher dispatcher; |
2813 | + media::KeyedPlayerStore::Ptr player_store; |
2814 | media::ClientDeathObserver::Ptr client_death_observer; |
2815 | + media::EngineFactory engine_factory; |
2816 | media::RecorderObserver::Ptr recorder_observer; |
2817 | power::StateController::Ptr power_state_controller; |
2818 | power::BatteryObserver::Ptr power_state_observer; |
2819 | telephony::CallMonitor::Ptr call_monitor; |
2820 | }; |
2821 | |
2822 | - ServiceImplementation (const Configuration& configuration); |
2823 | + // Create an instance with the given configuration. |
2824 | + static Ptr create(const Configuration&); |
2825 | + |
2826 | ~ServiceImplementation (); |
2827 | |
2828 | - std::shared_ptr<Player> create_session(const Player::Configuration&); |
2829 | - |
2830 | - void pause_other_sessions(Player::PlayerKey key); |
2831 | + // From media::Service |
2832 | + std::shared_ptr<Player> create_session(const Player::Configuration&) override; |
2833 | + void pause_other_sessions(Player::PlayerKey key) override; |
2834 | |
2835 | private: |
2836 | + ServiceImplementation (const Configuration& configuration); |
2837 | + |
2838 | void pause_all_multimedia_sessions(); |
2839 | void resume_paused_multimedia_sessions(); |
2840 | - void resume_multimedia_session(); |
2841 | |
2842 | struct Private; |
2843 | std::unique_ptr<Private> d; |
2844 | |
2845 | === modified file 'src/core/media/service_skeleton.cpp' |
2846 | --- src/core/media/service_skeleton.cpp 2014-10-14 16:21:47 +0000 |
2847 | +++ src/core/media/service_skeleton.cpp 2014-11-19 10:23:50 +0000 |
2848 | @@ -20,22 +20,15 @@ |
2849 | |
2850 | #include "apparmor.h" |
2851 | |
2852 | -#include "mpris/media_player2.h" |
2853 | -#include "mpris/metadata.h" |
2854 | -#include "mpris/player.h" |
2855 | -#include "mpris/playlists.h" |
2856 | -#include "mpris/service.h" |
2857 | +#include "mpris/exported.h" |
2858 | |
2859 | #include "player_configuration.h" |
2860 | -#include "the_session_bus.h" |
2861 | -#include "xesam.h" |
2862 | +#include "player_skeleton.h" |
2863 | |
2864 | #include <core/dbus/message.h> |
2865 | #include <core/dbus/object.h> |
2866 | #include <core/dbus/types/object_path.h> |
2867 | |
2868 | -#include <core/posix/this_process.h> |
2869 | - |
2870 | #include <map> |
2871 | #include <regex> |
2872 | #include <sstream> |
2873 | @@ -43,367 +36,82 @@ |
2874 | namespace dbus = core::dbus; |
2875 | namespace media = core::ubuntu::media; |
2876 | |
2877 | -namespace |
2878 | -{ |
2879 | -core::Signal<void> the_empty_signal; |
2880 | -} |
2881 | - |
2882 | struct media::ServiceSkeleton::Private |
2883 | { |
2884 | - Private(media::ServiceSkeleton* impl, const media::CoverArtResolver& resolver) |
2885 | + Private(media::ServiceSkeleton* impl, const media::ServiceSkeleton::Configuration& configuration) |
2886 | : impl(impl), |
2887 | - object(impl->access_service()->add_object_for_path( |
2888 | - dbus::traits::Service<media::Service>::object_path())), |
2889 | - dbus_stub(impl->access_bus()), |
2890 | - exported(impl->access_bus(), resolver) |
2891 | + configuration(configuration), |
2892 | + exported(configuration.bus, configuration.cover_art_resolver), |
2893 | + connections |
2894 | + { |
2895 | + configuration.player_store->current_player().changed().connect([this](const std::shared_ptr<media::Player>& player) |
2896 | + { |
2897 | + exported.set_current_player(player); |
2898 | + }) |
2899 | + } |
2900 | { |
2901 | - object->install_method_handler<mpris::Service::CreateSession>( |
2902 | + configuration.object->install_method_handler<mpris::Service::CreateSession>( |
2903 | std::bind( |
2904 | &Private::handle_create_session, |
2905 | this, |
2906 | std::placeholders::_1)); |
2907 | - object->install_method_handler<mpris::Service::PauseOtherSessions>( |
2908 | + configuration.object->install_method_handler<mpris::Service::PauseOtherSessions>( |
2909 | std::bind( |
2910 | &Private::handle_pause_other_sessions, |
2911 | this, |
2912 | std::placeholders::_1)); |
2913 | } |
2914 | |
2915 | + ~Private() |
2916 | + { |
2917 | + configuration.object->uninstall_method_handler<mpris::Service::CreateSession>(); |
2918 | + configuration.object->uninstall_method_handler<mpris::Service::PauseOtherSessions>(); |
2919 | + } |
2920 | + |
2921 | void handle_create_session(const core::dbus::Message::Ptr& msg) |
2922 | { |
2923 | - static unsigned int session_counter = 0; |
2924 | - |
2925 | - std::stringstream ss; |
2926 | - ss << "/core/ubuntu/media/Service/sessions/" << session_counter++; |
2927 | - |
2928 | - dbus::types::ObjectPath op{ss.str()}; |
2929 | - media::Player::PlayerKey key{session_counter}; |
2930 | - |
2931 | - dbus_stub.get_connection_app_armor_security_async(msg->sender(), [this, msg, op, key](const std::string& profile) |
2932 | - { |
2933 | - media::Player::Configuration config |
2934 | - { |
2935 | - profile, |
2936 | - key, |
2937 | - impl->access_bus(), |
2938 | - impl->access_service()->add_object_for_path(op) |
2939 | - }; |
2940 | - |
2941 | - try |
2942 | - { |
2943 | - auto session = impl->create_session(config); |
2944 | - |
2945 | - bool inserted = false; |
2946 | - std::tie(std::ignore, inserted) |
2947 | - = session_store.insert(std::make_pair(key, session)); |
2948 | - |
2949 | - if (!inserted) |
2950 | - throw std::runtime_error("Problem persisting session in session store."); |
2951 | - |
2952 | - auto reply = dbus::Message::make_method_return(msg); |
2953 | - reply->writer() << op; |
2954 | - |
2955 | - impl->access_bus()->send(reply); |
2956 | - } catch(const std::runtime_error& e) |
2957 | - { |
2958 | - auto reply = dbus::Message::make_error( |
2959 | - msg, |
2960 | - mpris::Service::Errors::CreatingSession::name(), |
2961 | - e.what()); |
2962 | - impl->access_bus()->send(reply); |
2963 | - } |
2964 | - }); |
2965 | + static std::atomic<unsigned int> session_counter{0}; |
2966 | + media::Player::PlayerKey key{session_counter++}; |
2967 | + std::stringstream ss; ss << "/core/ubuntu/media/Service/sessions/" << key; |
2968 | + media::Player::Configuration config{dbus::types::ObjectPath{ss.str()}, key}; |
2969 | + |
2970 | + dbus::Message::Ptr reply; |
2971 | + try |
2972 | + { |
2973 | + configuration.player_store->add_player_for_key(config.key, impl->create_session(config)); |
2974 | + reply = dbus::Message::make_method_return(msg); |
2975 | + reply->writer() << config.path; |
2976 | + } |
2977 | + catch (...) |
2978 | + { |
2979 | + reply = dbus::Message::make_error(msg, mpris::Service::Errors::CreatingSession::name(), std::string{}); |
2980 | + } |
2981 | + |
2982 | + configuration.bus->send(reply); |
2983 | } |
2984 | |
2985 | void handle_pause_other_sessions(const core::dbus::Message::Ptr& msg) |
2986 | { |
2987 | - std::cout << __PRETTY_FUNCTION__ << std::endl; |
2988 | - Player::PlayerKey key; |
2989 | - msg->reader() >> key; |
2990 | + Player::PlayerKey key; msg->reader() >> key; |
2991 | impl->pause_other_sessions(key); |
2992 | |
2993 | - auto reply = dbus::Message::make_method_return(msg); |
2994 | - impl->access_bus()->send(reply); |
2995 | + configuration.bus->send(dbus::Message::make_method_return(msg)); |
2996 | } |
2997 | |
2998 | media::ServiceSkeleton* impl; |
2999 | - dbus::Object::Ptr object; |
3000 | - |
3001 | - // We query the apparmor profile to obtain an identity for players. |
3002 | - org::freedesktop::dbus::DBus::Stub dbus_stub; |
3003 | - // We track all running player instances. |
3004 | - std::map<media::Player::PlayerKey, std::shared_ptr<media::Player>> session_store; |
3005 | + // We store the creation time arguments |
3006 | + ServiceSkeleton::Configuration configuration; |
3007 | // We expose the entire service as an MPRIS player. |
3008 | - struct Exported |
3009 | + mpris::Exported exported; |
3010 | + // All event connections go here. |
3011 | + struct |
3012 | { |
3013 | - static mpris::MediaPlayer2::Skeleton::Configuration::Defaults media_player_defaults() |
3014 | - { |
3015 | - mpris::MediaPlayer2::Skeleton::Configuration::Defaults defaults; |
3016 | - // TODO(tvoss): These three elements really should be configurable. |
3017 | - defaults.identity = "core::media::Hub"; |
3018 | - defaults.desktop_entry = "mediaplayer-app"; |
3019 | - defaults.supported_mime_types = {"audio/mpeg3"}; |
3020 | - |
3021 | - return defaults; |
3022 | - } |
3023 | - |
3024 | - static mpris::Player::Skeleton::Configuration::Defaults player_defaults() |
3025 | - { |
3026 | - mpris::Player::Skeleton::Configuration::Defaults defaults; |
3027 | - |
3028 | - // Disabled as track list is not fully implemented yet. |
3029 | - defaults.can_go_next = false; |
3030 | - // Disabled as track list is not fully implemented yet. |
3031 | - defaults.can_go_previous = false; |
3032 | - |
3033 | - return defaults; |
3034 | - } |
3035 | - |
3036 | - static std::string service_name() |
3037 | - { |
3038 | - static const bool export_to_indicator_sound_via_mpris |
3039 | - { |
3040 | - core::posix::this_process::env::get("UBUNTU_MEDIA_HUB_EXPORT_TO_INDICATOR_VIA_MPRIS", "0") == "1" |
3041 | - }; |
3042 | - |
3043 | - return export_to_indicator_sound_via_mpris ? "org.mpris.MediaPlayer2.MediaHub" : |
3044 | - "hidden.org.mpris.MediaPlayer2.MediaHub"; |
3045 | - } |
3046 | - |
3047 | - explicit Exported(const dbus::Bus::Ptr& bus, const media::CoverArtResolver& cover_art_resolver) |
3048 | - : bus{bus}, |
3049 | - service{dbus::Service::add_service(bus, service_name())}, |
3050 | - object{service->add_object_for_path(dbus::types::ObjectPath{"/org/mpris/MediaPlayer2"})}, |
3051 | - media_player{mpris::MediaPlayer2::Skeleton::Configuration{bus, object, media_player_defaults()}}, |
3052 | - player{mpris::Player::Skeleton::Configuration{bus, object, player_defaults()}}, |
3053 | - playlists{mpris::Playlists::Skeleton::Configuration{bus, object, mpris::Playlists::Skeleton::Configuration::Defaults{}}}, |
3054 | - cover_art_resolver{cover_art_resolver} |
3055 | - { |
3056 | - object->install_method_handler<core::dbus::interfaces::Properties::GetAll>([this](const core::dbus::Message::Ptr& msg) |
3057 | - { |
3058 | - // Extract the interface |
3059 | - std::string itf; msg->reader() >> itf; |
3060 | - core::dbus::Message::Ptr reply = core::dbus::Message::make_method_return(msg); |
3061 | - |
3062 | - if (itf == mpris::Player::name()) |
3063 | - reply->writer() << player.get_all_properties(); |
3064 | - else if (itf == mpris::MediaPlayer2::name()) |
3065 | - reply->writer() << media_player.get_all_properties(); |
3066 | - else if (itf == mpris::Playlists::name()) |
3067 | - reply->writer() << playlists.get_all_properties(); |
3068 | - |
3069 | - Exported::bus->send(reply); |
3070 | - }); |
3071 | - |
3072 | - // Setup method handlers for mpris::Player methods. |
3073 | - auto next = [this](const core::dbus::Message::Ptr& msg) |
3074 | - { |
3075 | - auto sp = current_player.lock(); |
3076 | - |
3077 | - if (sp) |
3078 | - sp->next(); |
3079 | - |
3080 | - Exported::bus->send(core::dbus::Message::make_method_return(msg)); |
3081 | - }; |
3082 | - object->install_method_handler<mpris::Player::Next>(next); |
3083 | - |
3084 | - auto previous = [this](const core::dbus::Message::Ptr& msg) |
3085 | - { |
3086 | - auto sp = current_player.lock(); |
3087 | - |
3088 | - if (sp) |
3089 | - sp->previous(); |
3090 | - |
3091 | - Exported::bus->send(core::dbus::Message::make_method_return(msg)); |
3092 | - }; |
3093 | - object->install_method_handler<mpris::Player::Previous>(previous); |
3094 | - |
3095 | - auto pause = [this](const core::dbus::Message::Ptr& msg) |
3096 | - { |
3097 | - auto sp = current_player.lock(); |
3098 | - |
3099 | - if (sp) |
3100 | - sp->pause(); |
3101 | - |
3102 | - Exported::bus->send(core::dbus::Message::make_method_return(msg)); |
3103 | - }; |
3104 | - object->install_method_handler<mpris::Player::Pause>(pause); |
3105 | - |
3106 | - auto stop = [this](const core::dbus::Message::Ptr& msg) |
3107 | - { |
3108 | - auto sp = current_player.lock(); |
3109 | - |
3110 | - if (sp) |
3111 | - sp->stop(); |
3112 | - |
3113 | - Exported::bus->send(core::dbus::Message::make_method_return(msg)); |
3114 | - }; |
3115 | - object->install_method_handler<mpris::Player::Stop>(stop); |
3116 | - |
3117 | - auto play = [this](const core::dbus::Message::Ptr& msg) |
3118 | - { |
3119 | - auto sp = current_player.lock(); |
3120 | - |
3121 | - if (sp) |
3122 | - sp->play(); |
3123 | - |
3124 | - Exported::bus->send(core::dbus::Message::make_method_return(msg)); |
3125 | - }; |
3126 | - object->install_method_handler<mpris::Player::Play>(play); |
3127 | - |
3128 | - auto play_pause = [this](const core::dbus::Message::Ptr& msg) |
3129 | - { |
3130 | - auto sp = current_player.lock(); |
3131 | - |
3132 | - if (sp) |
3133 | - { |
3134 | - if (sp->playback_status() == media::Player::PlaybackStatus::playing) |
3135 | - sp->pause(); |
3136 | - else if (sp->playback_status() != media::Player::PlaybackStatus::null) |
3137 | - sp->play(); |
3138 | - } |
3139 | - |
3140 | - Exported::bus->send(core::dbus::Message::make_method_return(msg)); |
3141 | - }; |
3142 | - object->install_method_handler<mpris::Player::PlayPause>(play_pause); |
3143 | - } |
3144 | - |
3145 | - void set_current_player(const std::shared_ptr<media::Player>& cp) |
3146 | - { |
3147 | - unset_current_player(); |
3148 | - |
3149 | - // We will not keep the object alive. |
3150 | - current_player = cp; |
3151 | - |
3152 | - // And announce that we can be controlled again. |
3153 | - player.properties.can_control->set(false); |
3154 | - |
3155 | - // We wire up player state changes |
3156 | - connections.seeked_to = cp->seeked_to().connect([this](std::uint64_t position) |
3157 | - { |
3158 | - player.signals.seeked_to->emit(position); |
3159 | - }); |
3160 | - |
3161 | - connections.duration_changed = cp->duration().changed().connect([this](std::uint64_t duration) |
3162 | - { |
3163 | - player.properties.duration->set(duration); |
3164 | - }); |
3165 | - |
3166 | - connections.position_changed = cp->position().changed().connect([this](std::uint64_t position) |
3167 | - { |
3168 | - player.properties.position->set(position); |
3169 | - }); |
3170 | - |
3171 | - connections.playback_status_changed = cp->playback_status().changed().connect([this](core::ubuntu::media::Player::PlaybackStatus status) |
3172 | - { |
3173 | - player.properties.playback_status->set(mpris::Player::PlaybackStatus::from(status)); |
3174 | - }); |
3175 | - |
3176 | - connections.loop_status_changed = cp->loop_status().changed().connect([this](core::ubuntu::media::Player::LoopStatus status) |
3177 | - { |
3178 | - player.properties.loop_status->set(mpris::Player::LoopStatus::from(status)); |
3179 | - }); |
3180 | - |
3181 | - connections.meta_data_changed = cp->meta_data_for_current_track().changed().connect([this](const core::ubuntu::media::Track::MetaData& md) |
3182 | - { |
3183 | - mpris::Player::Dictionary dict; |
3184 | - |
3185 | - bool has_title = md.count(xesam::Title::name) > 0; |
3186 | - bool has_album_name = md.count(xesam::Album::name) > 0; |
3187 | - bool has_artist_name = md.count(xesam::Artist::name) > 0; |
3188 | - |
3189 | - if (has_title) |
3190 | - dict[xesam::Title::name] = dbus::types::Variant::encode(md.get(xesam::Title::name)); |
3191 | - if (has_album_name) |
3192 | - dict[xesam::Album::name] = dbus::types::Variant::encode(md.get(xesam::Album::name)); |
3193 | - if (has_artist_name) |
3194 | - dict[xesam::Artist::name] = dbus::types::Variant::encode(md.get(xesam::Artist::name)); |
3195 | - |
3196 | - dict[mpris::metadata::ArtUrl::name] = dbus::types::Variant::encode( |
3197 | - cover_art_resolver( |
3198 | - has_title ? md.get(xesam::Title::name) : "", |
3199 | - has_album_name ? md.get(xesam::Album::name) : "", |
3200 | - has_artist_name ? md.get(xesam::Artist::name) : "")); |
3201 | - |
3202 | - mpris::Player::Dictionary wrap; |
3203 | - wrap[mpris::Player::Properties::Metadata::name()] = dbus::types::Variant::encode(dict); |
3204 | - |
3205 | - player.signals.properties_changed->emit( |
3206 | - std::make_tuple( |
3207 | - dbus::traits::Service<mpris::Player::Properties::Metadata::Interface>::interface_name(), |
3208 | - wrap, |
3209 | - std::vector<std::string>())); |
3210 | - }); |
3211 | - } |
3212 | - |
3213 | - void unset_current_player() |
3214 | - { |
3215 | - current_player.reset(); |
3216 | - |
3217 | - // We disconnect all previous event connections. |
3218 | - connections.seeked_to.disconnect(); |
3219 | - connections.duration_changed.disconnect(); |
3220 | - connections.position_changed.disconnect(); |
3221 | - connections.playback_status_changed.disconnect(); |
3222 | - connections.loop_status_changed.disconnect(); |
3223 | - connections.meta_data_changed.disconnect(); |
3224 | - |
3225 | - // And announce that we cannot be controlled anymore. |
3226 | - player.properties.can_control->set(false); |
3227 | - } |
3228 | - |
3229 | - void unset_if_current(const std::shared_ptr<media::Player>& cp) |
3230 | - { |
3231 | - if (cp == current_player.lock()) |
3232 | - unset_current_player(); |
3233 | - } |
3234 | - |
3235 | - dbus::Bus::Ptr bus; |
3236 | - dbus::Service::Ptr service; |
3237 | - dbus::Object::Ptr object; |
3238 | - |
3239 | - mpris::MediaPlayer2::Skeleton media_player; |
3240 | - mpris::Player::Skeleton player; |
3241 | - mpris::Playlists::Skeleton playlists; |
3242 | - |
3243 | - // Helper to resolve (title, artist, album) tuples to cover art. |
3244 | - media::CoverArtResolver cover_art_resolver; |
3245 | - // The actual player instance. |
3246 | - std::weak_ptr<media::Player> current_player; |
3247 | - // We track event connections. |
3248 | - struct |
3249 | - { |
3250 | - core::Connection seeked_to |
3251 | - { |
3252 | - the_empty_signal.connect([](){}) |
3253 | - }; |
3254 | - core::Connection duration_changed |
3255 | - { |
3256 | - the_empty_signal.connect([](){}) |
3257 | - }; |
3258 | - core::Connection position_changed |
3259 | - { |
3260 | - the_empty_signal.connect([](){}) |
3261 | - }; |
3262 | - core::Connection playback_status_changed |
3263 | - { |
3264 | - the_empty_signal.connect([](){}) |
3265 | - }; |
3266 | - core::Connection loop_status_changed |
3267 | - { |
3268 | - the_empty_signal.connect([](){}) |
3269 | - }; |
3270 | - core::Connection meta_data_changed |
3271 | - { |
3272 | - the_empty_signal.connect([](){}) |
3273 | - }; |
3274 | - } connections; |
3275 | - } exported; |
3276 | + core::ScopedConnection current_player_changed; |
3277 | + } connections; |
3278 | }; |
3279 | |
3280 | -media::ServiceSkeleton::ServiceSkeleton(const media::CoverArtResolver& resolver) |
3281 | - : dbus::Skeleton<media::Service>(the_session_bus()), |
3282 | - d(new Private(this, resolver)) |
3283 | +media::ServiceSkeleton::ServiceSkeleton(const media::ServiceSkeleton::Configuration& configuration) |
3284 | + : d(new Private(this, configuration)) |
3285 | { |
3286 | } |
3287 | |
3288 | @@ -411,47 +119,17 @@ |
3289 | { |
3290 | } |
3291 | |
3292 | -bool media::ServiceSkeleton::has_player_for_key(const media::Player::PlayerKey& key) const |
3293 | -{ |
3294 | - return d->session_store.count(key) > 0; |
3295 | -} |
3296 | - |
3297 | -std::shared_ptr<media::Player> media::ServiceSkeleton::player_for_key(const media::Player::PlayerKey& key) const |
3298 | -{ |
3299 | - return d->session_store.at(key); |
3300 | -} |
3301 | - |
3302 | -void media::ServiceSkeleton::enumerate_players(const media::ServiceSkeleton::PlayerEnumerator& enumerator) const |
3303 | -{ |
3304 | - for (const auto& pair : d->session_store) |
3305 | - enumerator(pair.first, pair.second); |
3306 | -} |
3307 | - |
3308 | -void media::ServiceSkeleton::set_current_player_for_key(const media::Player::PlayerKey& key) |
3309 | -{ |
3310 | - if (not has_player_for_key(key)) |
3311 | - return; |
3312 | - |
3313 | - d->exported.set_current_player(player_for_key(key)); |
3314 | -} |
3315 | - |
3316 | -void media::ServiceSkeleton::remove_player_for_key(const media::Player::PlayerKey& key) |
3317 | -{ |
3318 | - if (not has_player_for_key(key)) |
3319 | - return; |
3320 | - |
3321 | - auto player = player_for_key(key); |
3322 | - |
3323 | - d->session_store.erase(key); |
3324 | - d->exported.unset_if_current(player); |
3325 | -} |
3326 | - |
3327 | -void media::ServiceSkeleton::run() |
3328 | -{ |
3329 | - access_bus()->run(); |
3330 | -} |
3331 | - |
3332 | -void media::ServiceSkeleton::stop() |
3333 | -{ |
3334 | - access_bus()->stop(); |
3335 | +std::shared_ptr<media::Player> media::ServiceSkeleton::create_session(const media::Player::Configuration& config) |
3336 | +{ |
3337 | + return std::make_shared<media::PlayerSkeleton>(media::PlayerSkeleton::Configuration |
3338 | + { |
3339 | + d->configuration.impl->create_session(config), |
3340 | + d->configuration.bus, |
3341 | + d->configuration.service->add_object_for_path(config.path) |
3342 | + }); |
3343 | +} |
3344 | + |
3345 | +void media::ServiceSkeleton::pause_other_sessions(media::Player::PlayerKey key) |
3346 | +{ |
3347 | + d->configuration.impl->pause_other_sessions(key); |
3348 | } |
3349 | |
3350 | === modified file 'src/core/media/service_skeleton.h' |
3351 | --- src/core/media/service_skeleton.h 2014-09-09 21:27:29 +0000 |
3352 | +++ src/core/media/service_skeleton.h 2014-11-19 10:23:50 +0000 |
3353 | @@ -22,8 +22,9 @@ |
3354 | #include <core/media/service.h> |
3355 | |
3356 | #include "cover_art_resolver.h" |
3357 | -#include "service_traits.h" |
3358 | +#include "keyed_player_store.h" |
3359 | |
3360 | +#include <core/dbus/object.h> |
3361 | #include <core/dbus/skeleton.h> |
3362 | |
3363 | #include <memory> |
3364 | @@ -34,42 +35,34 @@ |
3365 | { |
3366 | namespace media |
3367 | { |
3368 | -class ServiceSkeleton : public core::dbus::Skeleton<core::ubuntu::media::Service> |
3369 | +class ServiceSkeleton : public media::Service |
3370 | { |
3371 | public: |
3372 | - // Functor for enumerating all known (key, player) pairs. |
3373 | - typedef std::function |
3374 | - < |
3375 | - void( |
3376 | - // The key of the player. |
3377 | - const core::ubuntu::media::Player::PlayerKey&, |
3378 | - // The actual player instance. |
3379 | - const std::shared_ptr<core::ubuntu::media::Player>& |
3380 | - ) |
3381 | - > PlayerEnumerator; |
3382 | + // Construction time arguments go here. |
3383 | + struct Configuration |
3384 | + { |
3385 | + // The actual service implementation that we forward to. |
3386 | + std::shared_ptr<Service> impl; |
3387 | + // The bus instance that we expose the service on. |
3388 | + core::dbus::Bus::Ptr bus; |
3389 | + // The service instance owned on the bus. |
3390 | + core::dbus::Service::Ptr service; |
3391 | + // The object that represents the service instance. |
3392 | + core::dbus::Object::Ptr object; |
3393 | + // The store of currently known players. |
3394 | + KeyedPlayerStore::Ptr player_store; |
3395 | + // The cover art resolver. |
3396 | + CoverArtResolver cover_art_resolver; |
3397 | + }; |
3398 | |
3399 | - ServiceSkeleton(const CoverArtResolver& cover_art_resolver = always_missing_cover_art_resolver()); |
3400 | + ServiceSkeleton(const Configuration&); |
3401 | ~ServiceSkeleton(); |
3402 | |
3403 | - // We keep track of all known player sessions here and render them accessible via |
3404 | - // the key. All of these functions are thread-safe but not reentrant. |
3405 | - // Returns true iff a player is known for the given key. |
3406 | - bool has_player_for_key(const Player::PlayerKey& key) const; |
3407 | - // Returns the player for the given key or throws std::out_of_range if no player is known |
3408 | - // for the given key. |
3409 | - std::shared_ptr<Player> player_for_key(const Player::PlayerKey& key) const; |
3410 | - // Enumerates all known players and invokes the given enumerator for each |
3411 | - // (key, player) pair. |
3412 | - void enumerate_players(const PlayerEnumerator& enumerator) const; |
3413 | - // Removes the player for the given key, and unsets it if it is the current one. |
3414 | - void remove_player_for_key(const Player::PlayerKey& key); |
3415 | - // Makes the player known under the given key current. |
3416 | - void set_current_player_for_key(const Player::PlayerKey& key); |
3417 | - |
3418 | - void run(); |
3419 | - void stop(); |
3420 | - |
3421 | - private: |
3422 | + // From media::Service |
3423 | + std::shared_ptr<Player> create_session(const Player::Configuration&) override; |
3424 | + void pause_other_sessions(Player::PlayerKey) override; |
3425 | + |
3426 | +private: |
3427 | struct Private; |
3428 | std::shared_ptr<Private> d; |
3429 | }; |
3430 | |
3431 | === modified file 'tests/CMakeLists.txt' |
3432 | --- tests/CMakeLists.txt 2014-02-14 08:12:35 +0000 |
3433 | +++ tests/CMakeLists.txt 2014-11-19 10:23:50 +0000 |
3434 | @@ -1,14 +1,6 @@ |
3435 | find_package(Threads) |
3436 | |
3437 | -add_definitions(-DBOOST_NO_CXX11_SCOPED_ENUMS) |
3438 | - |
3439 | -include_directories(${CMAKE_SOURCE_DIR}/include) |
3440 | - |
3441 | -option( |
3442 | - MEDIA_HUB_ENABLE_DBUS_TEST_RUNNER |
3443 | - "Rely on dbus test runner to start a private session for testing purposes" |
3444 | - ON |
3445 | -) |
3446 | +add_definitions(-DBOOST_NO_CXX11_SCOPED_ENUMS -DCORE_DBUS_ENABLE_GOOGLE_TEST_FIXTURE) |
3447 | |
3448 | if (MEDIA_HUB_ENABLE_DBUS_TEST_RUNNER) |
3449 | find_program(DBUS_TEST_RUNNER_EXECUTABLE dbus-test-runner) |
3450 | @@ -23,8 +15,8 @@ |
3451 | add_subdirectory(${GMOCK_SOURCE_DIR} "${CMAKE_CURRENT_BINARY_DIR}/gmock") |
3452 | |
3453 | include_directories ( |
3454 | - . |
3455 | - |
3456 | + ${CMAKE_CURRENT_SOURCE_DIR} |
3457 | + ${CMAKE_SOURCE_DIR}/src |
3458 | ${GMOCK_INCLUDE_DIR} |
3459 | ${GTEST_INCLUDE_DIR} |
3460 | ) |
3461 | @@ -50,5 +42,28 @@ |
3462 | "COM_UBUNTU_MEDIA_SERVICE_AUDIO_SINK_NAME=fakesink" |
3463 | ) |
3464 | |
3465 | -add_subdirectory(acceptance-tests) |
3466 | +macro(MEDIA_HUB_ADD_TEST test_name src) |
3467 | + add_executable( |
3468 | + ${test_name} |
3469 | + ${src}) |
3470 | + |
3471 | + target_link_libraries( |
3472 | + ${test_name} |
3473 | + |
3474 | + media-hub-client |
3475 | + media-hub-common |
3476 | + media-hub-service |
3477 | + media-hub-test-framework |
3478 | + |
3479 | + gmock |
3480 | + gmock_main |
3481 | + gtest |
3482 | + ) |
3483 | + |
3484 | + add_test(${test_name} ${CMAKE_CURRENT_BINARY_DIR}/${test_name} --gtest_filter=*-*requires*) |
3485 | +endmacro(MEDIA_HUB_ADD_TEST) |
3486 | + |
3487 | + |
3488 | +#add_subdirectory(acceptance-tests) |
3489 | +add_subdirectory(integration-tests) |
3490 | add_subdirectory(unit-tests) |
3491 | |
3492 | === modified file 'tests/acceptance-tests/service.cpp' |
3493 | --- tests/acceptance-tests/service.cpp 2014-11-19 10:23:50 +0000 |
3494 | +++ tests/acceptance-tests/service.cpp 2014-11-19 10:23:50 +0000 |
3495 | @@ -21,6 +21,7 @@ |
3496 | #include <core/media/track_list.h> |
3497 | |
3498 | #include "core/media/service_implementation.h" |
3499 | +#include "core/media/gstreamer/engine_factory.h" |
3500 | |
3501 | #include "../waitable_state_transition.h" |
3502 | |
3503 | @@ -103,6 +104,7 @@ |
3504 | es.io_service.post(task); |
3505 | }, |
3506 | media::platform_default_client_death_observer(), |
3507 | + media::gstreamer::engine_factory(), |
3508 | media::make_platform_default_recorder_observer(), |
3509 | media::power::make_platform_default_state_controller(es), |
3510 | media::power::make_platform_default_battery_observer(es), |
3511 | |
3512 | === added directory 'tests/integration-tests' |
3513 | === added file 'tests/integration-tests/CMakeLists.txt' |
3514 | --- tests/integration-tests/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
3515 | +++ tests/integration-tests/CMakeLists.txt 2014-11-19 10:23:50 +0000 |
3516 | @@ -0,0 +1,4 @@ |
3517 | +MEDIA_HUB_ADD_TEST(player_implementation_test player_implementation_test.cpp) |
3518 | +MEDIA_HUB_ADD_TEST(player_skeleton_test player_skeleton_test.cpp) |
3519 | +MEDIA_HUB_ADD_TEST(service_implementation_test service_implementation_test.cpp) |
3520 | + |
3521 | |
3522 | === added file 'tests/integration-tests/player_implementation_test.cpp' |
3523 | --- tests/integration-tests/player_implementation_test.cpp 1970-01-01 00:00:00 +0000 |
3524 | +++ tests/integration-tests/player_implementation_test.cpp 2014-11-19 10:23:50 +0000 |
3525 | @@ -0,0 +1,274 @@ |
3526 | +/* |
3527 | + * Copyright © 2014 Canonical Ltd. |
3528 | + * |
3529 | + * This program is free software: you can redistribute it and/or modify it |
3530 | + * under the terms of the GNU Lesser General Public License version 3, |
3531 | + * as published by the Free Software Foundation. |
3532 | + * |
3533 | + * This program is distributed in the hope that it will be useful, |
3534 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
3535 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3536 | + * GNU Lesser General Public License for more details. |
3537 | + * |
3538 | + * You should have received a copy of the GNU Lesser General Public License |
3539 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
3540 | + * |
3541 | + * Authored by: Thomas Voß <thomas.voss@canonical.com> |
3542 | + */ |
3543 | + |
3544 | +#include <core/media/base_engine.h> |
3545 | +#include <core/media/player_implementation.h> |
3546 | + |
3547 | +#include "mock/client_death_observer.h" |
3548 | +#include "mock/engine.h" |
3549 | +#include "mock/power_state_controller.h" |
3550 | + |
3551 | +#include <gmock/gmock.h> |
3552 | + |
3553 | +namespace media = core::ubuntu::media; |
3554 | + |
3555 | +namespace |
3556 | +{ |
3557 | +// Our default player key we use in testing. |
3558 | +constexpr const media::Player::PlayerKey the_player_key{42}; |
3559 | +// Our default texture handle we use in testing. |
3560 | +constexpr const std::uint32_t the_texture_id{13}; |
3561 | +// Constructs a PlayerImplementation::Configuration instance containing mocked |
3562 | +// instances for the functional dependencies. |
3563 | +media::PlayerImplementation::Configuration make_player_configuration_for_testing() |
3564 | +{ |
3565 | + using namespace ::testing; |
3566 | + |
3567 | + static const bool timeouts_are_sync_in_testing{false}; |
3568 | + static const std::chrono::milliseconds default_timeout_for_power_state_locks{0}; |
3569 | + |
3570 | + return media::PlayerImplementation::Configuration |
3571 | + { |
3572 | + the_player_key, |
3573 | + timeouts_are_sync_in_testing, |
3574 | + default_timeout_for_power_state_locks, |
3575 | + std::make_shared<NiceMock<MockEngine>>(), |
3576 | + std::make_shared<NiceMock<MockClientDeathObserver>>(), |
3577 | + std::make_shared<NiceMock<MockPowerStateController>>() |
3578 | + }; |
3579 | +} |
3580 | +} |
3581 | + |
3582 | +TEST(PlayerImplementation, forwards_calls_to_engine_instance) |
3583 | +{ |
3584 | + using namespace ::testing; |
3585 | + |
3586 | + const media::Track::UriType the_uri{"file:///does/not/exist"}; |
3587 | + const std::chrono::microseconds the_timestamp{42}; |
3588 | + |
3589 | + auto config = make_player_configuration_for_testing(); |
3590 | + auto engine = std::make_shared<NiceMock<MockEngine>>(); |
3591 | + config.engine = engine; |
3592 | + |
3593 | + EXPECT_CALL(*engine, read_duration()).Times(1); |
3594 | + EXPECT_CALL(*engine, read_is_audio()).Times(1); |
3595 | + EXPECT_CALL(*engine, read_is_video()).Times(1); |
3596 | + |
3597 | + EXPECT_CALL(*engine, open_resource_for_uri(the_uri)).Times(1); |
3598 | + EXPECT_CALL(*engine, create_video_sink(the_texture_id)).Times(1); |
3599 | + EXPECT_CALL(*engine, play()).Times(1); |
3600 | + EXPECT_CALL(*engine, stop()).Times(1); |
3601 | + EXPECT_CALL(*engine, pause()).Times(1); |
3602 | + EXPECT_CALL(*engine, seek_to(the_timestamp)).Times(1); |
3603 | + |
3604 | + media::PlayerImplementation impl{config}; |
3605 | + |
3606 | + static_cast<const media::PlayerImplementation&>(impl).is_audio_source().get(); |
3607 | + static_cast<const media::PlayerImplementation&>(impl).is_video_source().get(); |
3608 | + static_cast<const media::PlayerImplementation&>(impl).duration().get(); |
3609 | + |
3610 | + impl.open_uri(the_uri); |
3611 | + impl.create_gl_texture_video_sink(the_texture_id); |
3612 | + impl.play(); |
3613 | + impl.stop(); |
3614 | + impl.pause(); |
3615 | + impl.seek_to(the_timestamp); |
3616 | +} |
3617 | + |
3618 | +TEST(PlayerImplementation, subscribes_to_client_death_notifications_on_construction) |
3619 | +{ |
3620 | + using namespace ::testing; |
3621 | + |
3622 | + auto config = make_player_configuration_for_testing(); |
3623 | + auto client_death_observer = std::make_shared<NiceMock<MockClientDeathObserver>>(); |
3624 | + EXPECT_CALL(*client_death_observer, register_for_death_notifications_with_key(the_player_key)) |
3625 | + .Times(1); |
3626 | + config.client_death_observer = client_death_observer; |
3627 | + |
3628 | + media::PlayerImplementation impl{config}; |
3629 | +} |
3630 | + |
3631 | +TEST(PlayerImplementation, unsubscribes_from_client_death_notifications_on_destruction) |
3632 | +{ |
3633 | + using namespace ::testing; |
3634 | + |
3635 | + auto config = make_player_configuration_for_testing(); |
3636 | + auto client_death_observer = std::make_shared<NiceMock<MockClientDeathObserver>>(); |
3637 | + EXPECT_CALL(*client_death_observer, register_for_death_notifications_with_key(the_player_key)) |
3638 | + .Times(1); |
3639 | + config.client_death_observer = client_death_observer; |
3640 | + |
3641 | + // We scope the instance to force destruction. |
3642 | + { |
3643 | + media::PlayerImplementation impl{config}; |
3644 | + } |
3645 | + |
3646 | + // We assume that the implementation correctly unsubscribed |
3647 | + // and that we are free to report death here. |
3648 | + client_death_observer->signals.on_client_with_key_died(the_player_key); |
3649 | +} |
3650 | + |
3651 | +TEST(PlayerImplementation, resets_engine_when_client_with_correct_key_dies) |
3652 | +{ |
3653 | + using namespace ::testing; |
3654 | + |
3655 | + auto config = make_player_configuration_for_testing(); |
3656 | + auto engine = std::make_shared<NiceMock<MockEngine>>(); |
3657 | + auto client_death_observer = std::make_shared<NiceMock<MockClientDeathObserver>>(); |
3658 | + |
3659 | + EXPECT_CALL(*engine, reset()) |
3660 | + .Times(1); |
3661 | + EXPECT_CALL(*client_death_observer, register_for_death_notifications_with_key(the_player_key)) |
3662 | + .Times(1); |
3663 | + |
3664 | + config.engine = engine; |
3665 | + config.client_death_observer = client_death_observer; |
3666 | + |
3667 | + media::PlayerImplementation impl{config}; |
3668 | + |
3669 | + // Signal death of known client. |
3670 | + client_death_observer->signals.on_client_with_key_died(the_player_key); |
3671 | + // Signal death of unknown client. |
3672 | + client_death_observer->signals.on_client_with_key_died(2*the_player_key); |
3673 | +} |
3674 | + |
3675 | +TEST(PlayerImplementation, acquires_display_and_system_power_state_locks_on_construction) |
3676 | +{ |
3677 | + using namespace ::testing; |
3678 | + |
3679 | + auto config = make_player_configuration_for_testing(); |
3680 | + auto power_state_controller = std::make_shared<NiceMock<MockPowerStateController>>(); |
3681 | + EXPECT_CALL(*power_state_controller, display_state_lock()) |
3682 | + .Times(1); |
3683 | + EXPECT_CALL(*power_state_controller, system_state_lock()) |
3684 | + .Times(1); |
3685 | + config.power_state_controller = power_state_controller; |
3686 | + |
3687 | + media::PlayerImplementation impl{config}; |
3688 | +} |
3689 | + |
3690 | +namespace |
3691 | +{ |
3692 | +// A fixture that helps in testing for multiple different engine states. |
3693 | +struct PlayerImplementationEngineState : public ::testing::TestWithParam<media::Engine::State> |
3694 | +{ |
3695 | +}; |
3696 | +} |
3697 | + |
3698 | +TEST(PlayerImplementation, acquires_display_power_state_on_when_video_playback_starts) |
3699 | +{ |
3700 | + using namespace ::testing; |
3701 | + |
3702 | + auto config = make_player_configuration_for_testing(); |
3703 | + |
3704 | + auto engine = std::make_shared<NiceMock<MockEngine>>(); |
3705 | + ON_CALL(*engine, read_is_video()).WillByDefault(Return(true)); |
3706 | + auto power_state_controller = std::make_shared<NiceMock<MockPowerStateController>>(); |
3707 | + |
3708 | + EXPECT_CALL(*power_state_controller->system_lock, request_acquire(media::power::SystemState::active)) |
3709 | + .Times(0); |
3710 | + EXPECT_CALL(*power_state_controller->display_lock, request_acquire(media::power::DisplayState::on)) |
3711 | + .Times(1); |
3712 | + |
3713 | + config.engine = engine; |
3714 | + config.power_state_controller = power_state_controller; |
3715 | + |
3716 | + media::PlayerImplementation impl{config}; |
3717 | + |
3718 | + // And switch our state. |
3719 | + engine->mutable_state() = media::Engine::State::playing; |
3720 | +} |
3721 | + |
3722 | +TEST_P(PlayerImplementationEngineState, releases_display_power_state_on_when_engine_state_changes_away_from_playing) |
3723 | +{ |
3724 | + using namespace ::testing; |
3725 | + |
3726 | + auto config = make_player_configuration_for_testing(); |
3727 | + |
3728 | + auto engine = std::make_shared<NiceMock<MockEngine>>(); |
3729 | + ON_CALL(*engine, read_is_video()).WillByDefault(Return(true)); |
3730 | + auto power_state_controller = std::make_shared<NiceMock<MockPowerStateController>>(); |
3731 | + |
3732 | + EXPECT_CALL(*power_state_controller->system_lock, request_release(media::power::SystemState::active)) |
3733 | + .Times(0); |
3734 | + EXPECT_CALL(*power_state_controller->display_lock, request_release(media::power::DisplayState::on)) |
3735 | + .Times(1); |
3736 | + |
3737 | + config.engine = engine; |
3738 | + config.power_state_controller = power_state_controller; |
3739 | + |
3740 | + media::PlayerImplementation impl{config}; |
3741 | + |
3742 | + // And switch our state. |
3743 | + engine->mutable_state() = media::Engine::State::playing; |
3744 | + engine->mutable_state() = GetParam(); |
3745 | +} |
3746 | + |
3747 | +TEST(PlayerImplementation, acquires_system_power_state_active_when_audio_playback_starts) |
3748 | +{ |
3749 | + using namespace ::testing; |
3750 | + |
3751 | + auto config = make_player_configuration_for_testing(); |
3752 | + |
3753 | + auto engine = std::make_shared<NiceMock<MockEngine>>(); |
3754 | + ON_CALL(*engine, read_is_audio()).WillByDefault(Return(true)); |
3755 | + auto power_state_controller = std::make_shared<NiceMock<MockPowerStateController>>(); |
3756 | + |
3757 | + EXPECT_CALL(*power_state_controller->system_lock, request_acquire(media::power::SystemState::active)) |
3758 | + .Times(1); |
3759 | + EXPECT_CALL(*power_state_controller->display_lock, request_acquire(media::power::DisplayState::on)) |
3760 | + .Times(0); |
3761 | + |
3762 | + config.engine = engine; |
3763 | + config.power_state_controller = power_state_controller; |
3764 | + |
3765 | + media::PlayerImplementation impl{config}; |
3766 | + |
3767 | + // And switch our state. |
3768 | + engine->mutable_state() = media::Engine::State::playing; |
3769 | +} |
3770 | + |
3771 | +TEST_P(PlayerImplementationEngineState, releases_system_power_state_active_when_engine_state_changes_away_from_playing) |
3772 | +{ |
3773 | + using namespace ::testing; |
3774 | + |
3775 | + auto config = make_player_configuration_for_testing(); |
3776 | + |
3777 | + auto engine = std::make_shared<NiceMock<MockEngine>>(); |
3778 | + ON_CALL(*engine, read_is_audio()).WillByDefault(Return(true)); |
3779 | + auto power_state_controller = std::make_shared<NiceMock<MockPowerStateController>>(); |
3780 | + |
3781 | + EXPECT_CALL(*power_state_controller->system_lock, request_release(media::power::SystemState::active)) |
3782 | + .Times(1); |
3783 | + EXPECT_CALL(*power_state_controller->display_lock, request_release(media::power::DisplayState::on)) |
3784 | + .Times(0); |
3785 | + |
3786 | + config.engine = engine; |
3787 | + config.power_state_controller = power_state_controller; |
3788 | + |
3789 | + media::PlayerImplementation impl{config}; |
3790 | + |
3791 | + // And switch our state. |
3792 | + engine->mutable_state() = media::Engine::State::playing; |
3793 | + engine->mutable_state() = GetParam(); |
3794 | +} |
3795 | + |
3796 | +INSTANTIATE_TEST_CASE_P( |
3797 | + Power, |
3798 | + PlayerImplementationEngineState, |
3799 | + ::testing::Values(media::Engine::State::stopped, media::Engine::State::paused, media::Engine::State::ready)); |
3800 | |
3801 | === added file 'tests/integration-tests/player_skeleton_test.cpp' |
3802 | --- tests/integration-tests/player_skeleton_test.cpp 1970-01-01 00:00:00 +0000 |
3803 | +++ tests/integration-tests/player_skeleton_test.cpp 2014-11-19 10:23:50 +0000 |
3804 | @@ -0,0 +1,363 @@ |
3805 | +/* |
3806 | + * Copyright © 2014 Canonical Ltd. |
3807 | + * |
3808 | + * This program is free software: you can redistribute it and/or modify it |
3809 | + * under the terms of the GNU Lesser General Public License version 3, |
3810 | + * as published by the Free Software Foundation. |
3811 | + * |
3812 | + * This program is distributed in the hope that it will be useful, |
3813 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
3814 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3815 | + * GNU Lesser General Public License for more details. |
3816 | + * |
3817 | + * You should have received a copy of the GNU Lesser General Public License |
3818 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
3819 | + * |
3820 | + * Authored by: Thomas Voß <thomas.voss@canonical.com> |
3821 | + */ |
3822 | + |
3823 | +#include <core/media/player_skeleton.h> |
3824 | + |
3825 | +#include <core/media/base_player.h> |
3826 | +#include <core/media/track.h> |
3827 | + |
3828 | +#include "mock/player.h" |
3829 | + |
3830 | +#include <core/dbus/fixture.h> |
3831 | + |
3832 | +#include <core/testing/fork_and_run.h> |
3833 | + |
3834 | +#include <gmock/gmock.h> |
3835 | + |
3836 | +namespace media = core::ubuntu::media; |
3837 | + |
3838 | +namespace |
3839 | +{ |
3840 | +// Custom predicate to help asserting that a child process in a test finished successfully. |
3841 | +::testing::AssertionResult did_finish_successfully(const core::posix::wait::Result& result) |
3842 | +{ |
3843 | + if (result.status != core::posix::wait::Result::Status::exited) |
3844 | + return ::testing::AssertionFailure() << "Process did not exit, but: " << (int)result.status; |
3845 | + if (result.detail.if_exited.status != core::posix::exit::Status::success) |
3846 | + return ::testing::AssertionFailure() << "Process did exit with failure."; |
3847 | + |
3848 | + return ::testing::AssertionSuccess(); |
3849 | +} |
3850 | + |
3851 | +// Our custom fixture that provides us with private session and system |
3852 | +// bus instances. In addition, we define a custom service name and object |
3853 | +// path to enable maximum test isolation. |
3854 | +struct PlayerSkeleton : public core::dbus::testing::Fixture |
3855 | +{ |
3856 | + static constexpr const char* service_name_for_testing |
3857 | + { |
3858 | + "this.is.the.media.service.for.testing" |
3859 | + }; |
3860 | + |
3861 | + static constexpr const char* object_path_for_testing |
3862 | + { |
3863 | + "/this/is/the/media/player/skeleton/for/testing" |
3864 | + }; |
3865 | +}; |
3866 | + |
3867 | +// A helper to track changed signal emissions on properties. |
3868 | +template<typename T> |
3869 | +struct EventSink |
3870 | +{ |
3871 | + typedef T Expected; |
3872 | + typedef std::size_t Cardinality; |
3873 | + |
3874 | + EventSink(const core::Property<T>& property) |
3875 | + : event_connection |
3876 | + { |
3877 | + property.changed().connect([this](const T& value) |
3878 | + { |
3879 | + on_new_event(value); |
3880 | + }) |
3881 | + } |
3882 | + { |
3883 | + } |
3884 | + |
3885 | + EventSink(const core::Signal<T>& sig) |
3886 | + : event_connection |
3887 | + { |
3888 | + sig.connect([this](const T& value) |
3889 | + { |
3890 | + on_new_event(value); |
3891 | + }) |
3892 | + } |
3893 | + { |
3894 | + } |
3895 | + |
3896 | + EventSink(const core::Property<T>& property, Expected expected_value, Cardinality cardinality) |
3897 | + : EventSink(property) |
3898 | + { |
3899 | + EXPECT_CALL(*this, on_new_event(expected_value)).Times(cardinality); |
3900 | + } |
3901 | + |
3902 | + EventSink(const core::Signal<T>& sig, Expected expected_value, Cardinality cardinality) |
3903 | + : EventSink(sig) |
3904 | + { |
3905 | + EXPECT_CALL(*this, on_new_event(expected_value)).Times(cardinality); |
3906 | + } |
3907 | + |
3908 | + // Invoked for every changed signal emission. |
3909 | + MOCK_METHOD1_T(on_new_event, void(const T&)); |
3910 | + |
3911 | + // We keep track of the event connection here. |
3912 | + // On destruction, the connection is automatically cut. |
3913 | + core::ScopedConnection event_connection; |
3914 | +}; |
3915 | +// And a void overload |
3916 | +template<> |
3917 | +struct EventSink<void> |
3918 | +{ |
3919 | + typedef std::size_t Cardinality; |
3920 | + |
3921 | + EventSink(const core::Signal<void>& sig) |
3922 | + : event_connection |
3923 | + { |
3924 | + sig.connect([this]() |
3925 | + { |
3926 | + on_new_event(); |
3927 | + }) |
3928 | + } |
3929 | + { |
3930 | + } |
3931 | + |
3932 | + EventSink(const core::Signal<void>& sig, Cardinality cardinality) |
3933 | + : EventSink(sig) |
3934 | + { |
3935 | + EXPECT_CALL(*this, on_new_event()).Times(cardinality); |
3936 | + } |
3937 | + |
3938 | + // Invoked for every changed signal emission. |
3939 | + MOCK_METHOD0_T(on_new_event, void()); |
3940 | + |
3941 | + // We keep track of the event connection here. |
3942 | + // On destruction, the connection is automatically cut. |
3943 | + core::ScopedConnection event_connection; |
3944 | +}; |
3945 | + |
3946 | +// Our default player key we use in testing. |
3947 | +constexpr const media::Player::PlayerKey the_player_key{42}; |
3948 | +// Our default texture handle we use in testing. |
3949 | +constexpr const std::uint32_t the_texture_id{13}; |
3950 | +// Our default uri we use in testing. |
3951 | +const media::Track::UriType the_uri{"file:///does/not/exist"}; |
3952 | +// Our default timestamp we use in testing. |
3953 | +const std::chrono::microseconds the_timestamp{42}; |
3954 | +// A helper to create a mock player instance. |
3955 | +std::shared_ptr<testing::NiceMock<MockPlayer>> make_mock_player() |
3956 | +{ |
3957 | + return std::make_shared<testing::NiceMock<MockPlayer>>(); |
3958 | +} |
3959 | +// A helper return our default track meta data we use in testing. |
3960 | +media::Track::MetaData the_track_meta_data() |
3961 | +{ |
3962 | + media::Track::MetaData md; |
3963 | + md.set("key1", "value1"); |
3964 | + md.set("key2", "value2"); |
3965 | + |
3966 | + return md; |
3967 | +} |
3968 | +} |
3969 | + |
3970 | +// Please make sure that tests are executed in child processes |
3971 | +// to ensure maximum test isolation. |
3972 | +TEST_F(PlayerSkeleton, forwards_calls_to_implementation) |
3973 | +{ |
3974 | + auto child = core::posix::fork([this]() |
3975 | + { |
3976 | + using namespace ::testing; |
3977 | + |
3978 | + auto bus = session_bus(); |
3979 | + auto service = core::dbus::Service::add_service(bus, PlayerSkeleton::service_name_for_testing); |
3980 | + auto object = service->add_object_for_path(core::dbus::types::ObjectPath{PlayerSkeleton::object_path_for_testing}); |
3981 | + |
3982 | + auto impl = make_mock_player(); |
3983 | + |
3984 | + media::PlayerSkeleton skeleton |
3985 | + { |
3986 | + media::PlayerSkeleton::Configuration |
3987 | + { |
3988 | + impl, bus, object |
3989 | + } |
3990 | + }; |
3991 | + |
3992 | + EXPECT_CALL(*impl, track_list()).Times(1); |
3993 | + EXPECT_CALL(*impl, key()).Times(1); |
3994 | + EXPECT_CALL(*impl, create_gl_texture_video_sink(the_texture_id)).Times(1); |
3995 | + EXPECT_CALL(*impl, open_uri(the_uri)).Times(1); |
3996 | + EXPECT_CALL(*impl, next()).Times(1); |
3997 | + EXPECT_CALL(*impl, previous()).Times(1); |
3998 | + EXPECT_CALL(*impl, play()).Times(1); |
3999 | + EXPECT_CALL(*impl, pause()).Times(1); |
4000 | + EXPECT_CALL(*impl, stop()).Times(1); |
4001 | + EXPECT_CALL(*impl, seek_to(the_timestamp)).Times(1); |
4002 | + |
4003 | + skeleton.track_list(); |
4004 | + skeleton.key(); |
4005 | + skeleton.create_gl_texture_video_sink(the_texture_id); |
4006 | + skeleton.open_uri(the_uri); |
4007 | + skeleton.next(); |
4008 | + skeleton.previous(); |
4009 | + skeleton.play(); |
4010 | + skeleton.pause(); |
4011 | + skeleton.stop(); |
4012 | + skeleton.seek_to(the_timestamp); |
4013 | + |
4014 | + return HasFailure() ? core::posix::exit::Status::failure |
4015 | + : core::posix::exit::Status::success; |
4016 | + }, core::posix::StandardStream::empty); |
4017 | + |
4018 | + EXPECT_TRUE(did_finish_successfully(child.wait_for(core::posix::wait::Flags::untraced))); |
4019 | +} |
4020 | + |
4021 | +TEST_F(PlayerSkeleton, property_changes_and_signals_are_forwarded_from_impl) |
4022 | +{ |
4023 | + auto child = core::posix::fork([this]() |
4024 | + { |
4025 | + using namespace ::testing; |
4026 | + |
4027 | + auto bus = session_bus(); |
4028 | + auto service = core::dbus::Service::add_service(bus, PlayerSkeleton::service_name_for_testing); |
4029 | + auto object = service->add_object_for_path(core::dbus::types::ObjectPath{PlayerSkeleton::object_path_for_testing}); |
4030 | + |
4031 | + auto impl = make_mock_player(); |
4032 | + |
4033 | + media::PlayerSkeleton skeleton |
4034 | + { |
4035 | + media::PlayerSkeleton::Configuration |
4036 | + { |
4037 | + impl, bus, object |
4038 | + } |
4039 | + }; |
4040 | + |
4041 | + // We have to scope our expectations to make sure we catch them at exit |
4042 | + { |
4043 | + EventSink<bool> es_can_play{skeleton.can_play()}; |
4044 | + EXPECT_CALL(es_can_play, on_new_event(true)).Times(1); |
4045 | + EventSink<bool> es_can_pause{skeleton.can_pause()}; |
4046 | + EXPECT_CALL(es_can_pause, on_new_event(true)).Times(1); |
4047 | + EventSink<bool> es_can_seek{skeleton.can_seek()}; |
4048 | + EXPECT_CALL(es_can_seek, on_new_event(true)).Times(1); |
4049 | + EventSink<bool> es_can_go_previous{skeleton.can_go_previous()}; |
4050 | + EXPECT_CALL(es_can_go_previous, on_new_event(true)).Times(1); |
4051 | + EventSink<bool> es_can_go_next{skeleton.can_go_next()}; |
4052 | + EXPECT_CALL(es_can_go_next, on_new_event(true)).Times(1); |
4053 | + EventSink<bool> es_is_video_source{skeleton.is_video_source()}; |
4054 | + EXPECT_CALL(es_is_video_source, on_new_event(true)).Times(1); |
4055 | + EventSink<bool> es_is_audio_source{skeleton.is_audio_source()}; |
4056 | + EXPECT_CALL(es_is_audio_source, on_new_event(true)).Times(1); |
4057 | + |
4058 | + EventSink<media::Player::PlaybackStatus> es_playback_status{skeleton.playback_status()}; |
4059 | + EXPECT_CALL(es_playback_status, on_new_event(media::Player::PlaybackStatus::playing)).Times(1); |
4060 | + EventSink<media::Player::LoopStatus> es_loop_status{skeleton.loop_status()}; |
4061 | + EXPECT_CALL(es_loop_status, on_new_event(media::Player::LoopStatus::track)).Times(1); |
4062 | + EventSink<media::Player::PlaybackRate> es_playback_rate{skeleton.playback_rate()}; |
4063 | + EXPECT_CALL(es_playback_rate, on_new_event(media::Player::PlaybackRate{42})).Times(1); |
4064 | + EventSink<bool> es_is_shuffle{skeleton.is_shuffle()}; |
4065 | + EXPECT_CALL(es_is_shuffle, on_new_event(true)).Times(1); |
4066 | + EventSink<media::Track::MetaData> es_meta_data{skeleton.meta_data_for_current_track()}; |
4067 | + EXPECT_CALL(es_meta_data, on_new_event(the_track_meta_data())).Times(1); |
4068 | + EventSink<media::Player::Volume> es_volume{skeleton.volume()}; |
4069 | + EXPECT_CALL(es_volume, on_new_event(media::Player::Volume{42})).Times(1); |
4070 | + EventSink<media::Player::PlaybackRate> es_minimum_playback_rate{skeleton.minimum_playback_rate()}; |
4071 | + EXPECT_CALL(es_minimum_playback_rate, on_new_event(media::Player::PlaybackRate{42})).Times(1); |
4072 | + EventSink<media::Player::PlaybackRate> es_maximum_playback_rate{skeleton.maximum_playback_rate()}; |
4073 | + EXPECT_CALL(es_maximum_playback_rate, on_new_event(media::Player::PlaybackRate{42})).Times(1); |
4074 | + EventSink<std::int64_t> es_position{skeleton.position()}; |
4075 | + EXPECT_CALL(es_position, on_new_event(42)).Times(1); |
4076 | + EventSink<std::int64_t> es_duration{skeleton.duration()}; |
4077 | + EXPECT_CALL(es_duration, on_new_event(42)).Times(1); |
4078 | + EventSink<media::Player::AudioStreamRole> es_audio_stream_role{skeleton.audio_stream_role()}; |
4079 | + EXPECT_CALL(es_audio_stream_role, on_new_event(media::Player::AudioStreamRole::phone)).Times(1); |
4080 | + EventSink<media::Player::Orientation> es_orientation{skeleton.orientation()}; |
4081 | + EXPECT_CALL(es_orientation, on_new_event(media::Player::Orientation::rotate180)).Times(1); |
4082 | + |
4083 | + EventSink<int64_t> es_seeked_to{skeleton.seeked_to()}; |
4084 | + EXPECT_CALL(es_seeked_to, on_new_event(42)).Times(1); |
4085 | + EventSink<void> es_end_of_stream{skeleton.end_of_stream()}; |
4086 | + EXPECT_CALL(es_end_of_stream, on_new_event()).Times(1); |
4087 | + EventSink<media::Player::PlaybackStatus> es_playback_status_changed{skeleton.playback_status_changed()}; |
4088 | + EXPECT_CALL(es_playback_status_changed, on_new_event(media::Player::PlaybackStatus::playing)).Times(1); |
4089 | + EventSink<media::video::Dimensions> es_video_dimension_changed{skeleton.video_dimension_changed()}; |
4090 | + EXPECT_CALL(es_video_dimension_changed, on_new_event(std::make_tuple(media::video::Height{42}, media::video::Width{84}))).Times(1); |
4091 | + |
4092 | + impl->can_play() = true; |
4093 | + impl->can_pause() = true; |
4094 | + impl->can_seek() = true; |
4095 | + impl->can_go_previous() = true; |
4096 | + impl->can_go_next() = true; |
4097 | + impl->is_video_source() = true; |
4098 | + impl->is_audio_source() = true; |
4099 | + impl->playback_status() = media::Player::PlaybackStatus::playing; |
4100 | + impl->loop_status() = media::Player::LoopStatus::track; |
4101 | + impl->playback_rate() = media::Player::PlaybackRate{42}; |
4102 | + impl->is_shuffle() = true; |
4103 | + impl->meta_data_for_current_track() = the_track_meta_data(); |
4104 | + impl->volume() = media::Player::Volume{42}; |
4105 | + impl->minimum_playback_rate() = media::Player::PlaybackRate{42}; |
4106 | + impl->maximum_playback_rate() = media::Player::PlaybackRate{42}; |
4107 | + impl->position() = 42; |
4108 | + impl->duration() = 42; |
4109 | + impl->audio_stream_role() = media::Player::AudioStreamRole::phone; |
4110 | + impl->orientation() = media::Player::Orientation::rotate180; |
4111 | + |
4112 | + impl->seeked_to()(42); |
4113 | + impl->end_of_stream()(); |
4114 | + impl->playback_status_changed()(media::Player::PlaybackStatus::playing); |
4115 | + impl->video_dimension_changed()(std::make_tuple(media::video::Height{42}, media::video::Width{84})); |
4116 | + } |
4117 | + |
4118 | + return ::testing::Test::HasFailure() ? core::posix::exit::Status::failure |
4119 | + : core::posix::exit::Status::success; |
4120 | + }, core::posix::StandardStream::empty); |
4121 | + |
4122 | + EXPECT_TRUE(did_finish_successfully(child.wait_for(core::posix::wait::Flags::untraced))); |
4123 | +} |
4124 | + |
4125 | +TEST_F(PlayerSkeleton, property_changes_and_signals_are_forwarded_from_skeleton_to_impl) |
4126 | +{ |
4127 | + auto child = core::posix::fork([this]() |
4128 | + { |
4129 | + using namespace ::testing; |
4130 | + |
4131 | + auto bus = session_bus(); |
4132 | + auto service = core::dbus::Service::add_service(bus, PlayerSkeleton::service_name_for_testing); |
4133 | + auto object = service->add_object_for_path(core::dbus::types::ObjectPath{PlayerSkeleton::object_path_for_testing}); |
4134 | + |
4135 | + auto impl = make_mock_player(); |
4136 | + |
4137 | + media::PlayerSkeleton skeleton |
4138 | + { |
4139 | + media::PlayerSkeleton::Configuration |
4140 | + { |
4141 | + impl, bus, object |
4142 | + } |
4143 | + }; |
4144 | + |
4145 | + // We have to scope our expectations to make sure we catch them at exit |
4146 | + { |
4147 | + EventSink<media::Player::LoopStatus> es_loop_status{impl->loop_status()}; |
4148 | + EXPECT_CALL(es_loop_status, on_new_event(media::Player::LoopStatus::track)).Times(1); |
4149 | + EventSink<media::Player::PlaybackRate> es_playback_rate{impl->playback_rate()}; |
4150 | + EXPECT_CALL(es_playback_rate, on_new_event(media::Player::PlaybackRate{42})).Times(1); |
4151 | + EventSink<bool> es_is_shuffle{impl->is_shuffle()}; |
4152 | + EXPECT_CALL(es_is_shuffle, on_new_event(true)).Times(1); |
4153 | + EventSink<media::Player::Volume> es_volume{impl->volume()}; |
4154 | + EXPECT_CALL(es_volume, on_new_event(media::Player::Volume{42})).Times(1); |
4155 | + |
4156 | + skeleton.loop_status() = media::Player::LoopStatus::track; |
4157 | + skeleton.playback_rate() = media::Player::PlaybackRate{42}; |
4158 | + skeleton.is_shuffle() = true; |
4159 | + skeleton.volume() = media::Player::Volume{42}; |
4160 | + } |
4161 | + |
4162 | + return ::testing::Test::HasFailure() ? core::posix::exit::Status::failure |
4163 | + : core::posix::exit::Status::success; |
4164 | + }, core::posix::StandardStream::empty); |
4165 | + |
4166 | + EXPECT_TRUE(did_finish_successfully(child.wait_for(core::posix::wait::Flags::untraced))); |
4167 | +} |
4168 | |
4169 | === added file 'tests/integration-tests/service_implementation_test.cpp' |
4170 | --- tests/integration-tests/service_implementation_test.cpp 1970-01-01 00:00:00 +0000 |
4171 | +++ tests/integration-tests/service_implementation_test.cpp 2014-11-19 10:23:50 +0000 |
4172 | @@ -0,0 +1,421 @@ |
4173 | +/* |
4174 | + * Copyright © 2014 Canonical Ltd. |
4175 | + * |
4176 | + * This program is free software: you can redistribute it and/or modify it |
4177 | + * under the terms of the GNU Lesser General Public License version 3, |
4178 | + * as published by the Free Software Foundation. |
4179 | + * |
4180 | + * This program is distributed in the hope that it will be useful, |
4181 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
4182 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4183 | + * GNU Lesser General Public License for more details. |
4184 | + * |
4185 | + * You should have received a copy of the GNU Lesser General Public License |
4186 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
4187 | + * |
4188 | + * Authored by: Thomas Voß <thomas.voss@canonical.com> |
4189 | + */ |
4190 | + |
4191 | +#include <core/media/service_implementation.h> |
4192 | +#include <core/media/player_configuration.h> |
4193 | + |
4194 | +#include "mock/client_death_observer.h" |
4195 | +#include "mock/keyed_player_store.h" |
4196 | +#include "mock/player.h" |
4197 | +#include "mock/power_state_controller.h" |
4198 | +#include "mock/engine.h" |
4199 | + |
4200 | +#include <gmock/gmock.h> |
4201 | + |
4202 | +namespace media = core::ubuntu::media; |
4203 | + |
4204 | +namespace |
4205 | +{ |
4206 | +struct MockRecorderObserver : public media::RecorderObserver |
4207 | +{ |
4208 | + MockRecorderObserver() |
4209 | + { |
4210 | + using namespace ::testing; |
4211 | + |
4212 | + ON_CALL(*this, recording_state()) |
4213 | + .WillByDefault(ReturnRef(properties.recording_state)); |
4214 | + } |
4215 | + |
4216 | + // Getable/observable property describing the recording state of the system. |
4217 | + MOCK_CONST_METHOD0(recording_state, const core::Property<media::RecordingState>&()); |
4218 | + |
4219 | + struct |
4220 | + { |
4221 | + core::Property<media::RecordingState> recording_state; |
4222 | + } properties; |
4223 | +}; |
4224 | + |
4225 | +struct MockBatteryObserver : public media::power::BatteryObserver |
4226 | +{ |
4227 | + MockBatteryObserver() |
4228 | + { |
4229 | + using namespace ::testing; |
4230 | + |
4231 | + ON_CALL(*this, level()) |
4232 | + .WillByDefault(ReturnRef(properties.level)); |
4233 | + ON_CALL(*this, is_warning_active()) |
4234 | + .WillByDefault(ReturnRef(properties.is_warning_active)); |
4235 | + } |
4236 | + |
4237 | + // A getable/observable property reporting the current power-level |
4238 | + // of the system. |
4239 | + MOCK_CONST_METHOD0(level, const core::Property<media::power::Level>&()); |
4240 | + // A getable/observable property indicating whether a power-level |
4241 | + // warning is currently presented to the user. |
4242 | + MOCK_CONST_METHOD0(is_warning_active, const core::Property<bool>&()); |
4243 | + |
4244 | + struct |
4245 | + { |
4246 | + core::Property<media::power::Level> level; |
4247 | + core::Property<bool> is_warning_active; |
4248 | + } properties; |
4249 | +}; |
4250 | + |
4251 | +struct MockCallMonitor : public media::telephony::CallMonitor |
4252 | +{ |
4253 | + MockCallMonitor() |
4254 | + { |
4255 | + using namespace ::testing; |
4256 | + |
4257 | + ON_CALL(*this, on_call_state_changed()) |
4258 | + .WillByDefault(ReturnRef(signals.on_call_state_changed)); |
4259 | + } |
4260 | + |
4261 | + MOCK_CONST_METHOD0(on_call_state_changed, const core::Signal<State>&()); |
4262 | + |
4263 | + struct |
4264 | + { |
4265 | + core::Signal<State> on_call_state_changed; |
4266 | + } signals; |
4267 | +}; |
4268 | + |
4269 | +struct MockDispatcher : public std::enable_shared_from_this<MockDispatcher> |
4270 | +{ |
4271 | + MockDispatcher() |
4272 | + { |
4273 | + using namespace ::testing; |
4274 | + ON_CALL(*this, dispatch(_)) |
4275 | + .WillByDefault(Invoke(this, &MockDispatcher::run_task)); |
4276 | + } |
4277 | + |
4278 | + MOCK_METHOD1(dispatch, void(media::Task)); |
4279 | + |
4280 | + media::Dispatcher to_dispatcher() |
4281 | + { |
4282 | + auto sp = shared_from_this(); |
4283 | + |
4284 | + return [sp](media::Task task) |
4285 | + { |
4286 | + sp->dispatch(task); |
4287 | + }; |
4288 | + } |
4289 | + |
4290 | + void run_task(media::Task task) |
4291 | + { |
4292 | + task(); |
4293 | + } |
4294 | +}; |
4295 | + |
4296 | +struct MockEngineFactory : public std::enable_shared_from_this<MockEngineFactory> |
4297 | +{ |
4298 | + MockEngineFactory() |
4299 | + { |
4300 | + using namespace ::testing; |
4301 | + ON_CALL(*this, make_engine()) |
4302 | + .WillByDefault(Return(std::make_shared<MockEngine>())); |
4303 | + } |
4304 | + |
4305 | + media::EngineFactory to_engine_factory() |
4306 | + { |
4307 | + auto sp = shared_from_this(); |
4308 | + return [sp]() |
4309 | + { |
4310 | + return sp->make_engine(); |
4311 | + }; |
4312 | + } |
4313 | + |
4314 | + MOCK_METHOD0(make_engine, media::Engine::Ptr()); |
4315 | +}; |
4316 | + |
4317 | +// We use a tuple to pass around mocked instances |
4318 | +// and to allow for setting up expectations. |
4319 | +typedef std::tuple |
4320 | +< |
4321 | + std::shared_ptr<MockDispatcher>, |
4322 | + std::shared_ptr<MockKeyedPlayerStore>, |
4323 | + std::shared_ptr<MockClientDeathObserver>, |
4324 | + std::shared_ptr<MockEngineFactory>, |
4325 | + std::shared_ptr<MockRecorderObserver>, |
4326 | + std::shared_ptr<MockPowerStateController>, |
4327 | + std::shared_ptr<MockBatteryObserver>, |
4328 | + std::shared_ptr<MockCallMonitor> |
4329 | +> MockedConfiguration; |
4330 | + |
4331 | +namespace idx |
4332 | +{ |
4333 | + constexpr const std::size_t dispatcher{0}; |
4334 | + constexpr const std::size_t keyed_player_store{1}; |
4335 | + constexpr const std::size_t death_observer{2}; |
4336 | + constexpr const std::size_t engine_factory{3}; |
4337 | + constexpr const std::size_t recorder_observer{4}; |
4338 | + constexpr const std::size_t power_state_controller{5}; |
4339 | + constexpr const std::size_t battery_observer{6}; |
4340 | + constexpr const std::size_t call_monitor{7}; |
4341 | +}; |
4342 | + |
4343 | +MockedConfiguration make_mocked_configuration() |
4344 | +{ |
4345 | + return std::make_tuple( |
4346 | + std::make_shared<testing::NiceMock<MockDispatcher>>(), |
4347 | + std::make_shared<testing::NiceMock<MockKeyedPlayerStore>>(), |
4348 | + std::make_shared<testing::NiceMock<MockClientDeathObserver>>(), |
4349 | + std::make_shared<testing::NiceMock<MockEngineFactory>>(), |
4350 | + std::make_shared<testing::NiceMock<MockRecorderObserver>>(), |
4351 | + std::make_shared<testing::NiceMock<MockPowerStateController>>(), |
4352 | + std::make_shared<testing::NiceMock<MockBatteryObserver>>(), |
4353 | + std::make_shared<testing::NiceMock<MockCallMonitor>>()); |
4354 | +} |
4355 | + |
4356 | +media::ServiceImplementation::Configuration tie_to_mocked_configuration(MockedConfiguration& mc) |
4357 | +{ |
4358 | + return media::ServiceImplementation::Configuration |
4359 | + { |
4360 | + std::get<idx::dispatcher>(mc)->to_dispatcher(), |
4361 | + std::get<idx::keyed_player_store>(mc), |
4362 | + std::get<idx::death_observer>(mc), |
4363 | + std::get<idx::engine_factory>(mc)->to_engine_factory(), |
4364 | + std::get<idx::recorder_observer>(mc), |
4365 | + std::get<idx::power_state_controller>(mc), |
4366 | + std::get<idx::battery_observer>(mc), |
4367 | + std::get<idx::call_monitor>(mc) |
4368 | + }; |
4369 | +} |
4370 | + |
4371 | +std::shared_ptr<::testing::NiceMock<MockPlayer>> make_alarm_player() |
4372 | +{ |
4373 | + auto player = std::make_shared<::testing::NiceMock<MockPlayer>>(); |
4374 | + player->playback_status() = media::Player::PlaybackStatus::playing; |
4375 | + player->audio_stream_role() = media::Player::AudioStreamRole::alarm; |
4376 | + return player; |
4377 | +} |
4378 | + |
4379 | +std::shared_ptr<::testing::NiceMock<MockPlayer>> make_multimedia_player() |
4380 | +{ |
4381 | + auto player = std::make_shared<::testing::NiceMock<MockPlayer>>(); |
4382 | + player->playback_status() = media::Player::PlaybackStatus::playing; |
4383 | + player->audio_stream_role() = media::Player::AudioStreamRole::multimedia; |
4384 | + return player; |
4385 | +} |
4386 | + |
4387 | +// Our default player key we use in testing. |
4388 | +constexpr const media::Player::PlayerKey the_player_key{42}; |
4389 | +} |
4390 | + |
4391 | +TEST(ServiceImplementation, requests_display_state_on_when_recording_starts) |
4392 | +{ |
4393 | + auto mc = make_mocked_configuration(); |
4394 | + |
4395 | + auto recorder_observer = std::get<idx::recorder_observer>(mc); |
4396 | + auto power_state_controller = std::get<idx::power_state_controller>(mc); |
4397 | + |
4398 | + EXPECT_CALL(*power_state_controller->display_lock, request_acquire(media::power::DisplayState::on)).Times(1); |
4399 | + |
4400 | + auto impl = media::ServiceImplementation::create(tie_to_mocked_configuration(mc)); |
4401 | + |
4402 | + // We report that the recording started. |
4403 | + recorder_observer->properties.recording_state = media::RecordingState::started; |
4404 | +} |
4405 | + |
4406 | +TEST(ServiceImplementation, releases_display_state_on_when_recording_starts) |
4407 | +{ |
4408 | + auto mc = make_mocked_configuration(); |
4409 | + |
4410 | + auto recorder_observer = std::get<idx::recorder_observer>(mc); |
4411 | + auto power_state_controller = std::get<idx::power_state_controller>(mc); |
4412 | + |
4413 | + EXPECT_CALL(*power_state_controller->display_lock, request_release(media::power::DisplayState::on)).Times(1); |
4414 | + |
4415 | + auto impl = media::ServiceImplementation::create(tie_to_mocked_configuration(mc)); |
4416 | + |
4417 | + // We report that the recording started ... |
4418 | + recorder_observer->properties.recording_state = media::RecordingState::started; |
4419 | + // prior to stopping it. |
4420 | + recorder_observer->properties.recording_state = media::RecordingState::stopped; |
4421 | +} |
4422 | + |
4423 | +TEST(ServiceImplementation, pauses_all_multimedia_session_if_battery_level_reaches_low) |
4424 | +{ |
4425 | + auto multimedia_player = make_multimedia_player(); |
4426 | + auto alarm_player = make_alarm_player(); |
4427 | + |
4428 | + EXPECT_CALL(*multimedia_player, pause()).Times(1); |
4429 | + EXPECT_CALL(*alarm_player, pause()).Times(0); |
4430 | + |
4431 | + auto mc = make_mocked_configuration(); |
4432 | + |
4433 | + auto keyed_player_store = std::get<idx::keyed_player_store>(mc); |
4434 | + auto battery_observer = std::get<idx::battery_observer>(mc); |
4435 | + |
4436 | + keyed_player_store->properties.map[0] = multimedia_player; |
4437 | + keyed_player_store->properties.map[1] = alarm_player; |
4438 | + |
4439 | + auto impl = media::ServiceImplementation::create(tie_to_mocked_configuration(mc)); |
4440 | + |
4441 | + // We report the battery reaching low. |
4442 | + battery_observer->properties.level = media::power::Level::low; |
4443 | +} |
4444 | + |
4445 | +TEST(ServiceImplementation, pauses_all_multimedia_session_if_battery_level_reaches_very_low) |
4446 | +{ |
4447 | + auto multimedia_player = make_multimedia_player(); |
4448 | + auto alarm_player = make_alarm_player(); |
4449 | + |
4450 | + EXPECT_CALL(*multimedia_player, pause()).Times(1); |
4451 | + EXPECT_CALL(*alarm_player, pause()).Times(0); |
4452 | + |
4453 | + auto mc = make_mocked_configuration(); |
4454 | + |
4455 | + auto keyed_player_store = std::get<idx::keyed_player_store>(mc); |
4456 | + auto battery_observer = std::get<idx::battery_observer>(mc); |
4457 | + |
4458 | + keyed_player_store->properties.map[0] = multimedia_player; |
4459 | + keyed_player_store->properties.map[1] = alarm_player; |
4460 | + |
4461 | + auto impl = media::ServiceImplementation::create(tie_to_mocked_configuration(mc)); |
4462 | + |
4463 | + // We report the battery reaching low. |
4464 | + battery_observer->properties.level = media::power::Level::very_low; |
4465 | +} |
4466 | + |
4467 | +TEST(ServiceImplementation, resumes_all_multimedia_sessions_when_battery_critical_notification_is_gone) |
4468 | +{ |
4469 | + auto multimedia_player = make_multimedia_player(); |
4470 | + auto alarm_player = make_alarm_player(); |
4471 | + |
4472 | + EXPECT_CALL(*multimedia_player, play()).Times(1); |
4473 | + EXPECT_CALL(*alarm_player, play()).Times(0); |
4474 | + |
4475 | + auto mc = make_mocked_configuration(); |
4476 | + |
4477 | + auto keyed_player_store = std::get<idx::keyed_player_store>(mc); |
4478 | + auto battery_observer = std::get<idx::battery_observer>(mc); |
4479 | + |
4480 | + keyed_player_store->properties.map[0] = multimedia_player; |
4481 | + keyed_player_store->properties.map[1] = alarm_player; |
4482 | + |
4483 | + auto impl = media::ServiceImplementation::create(tie_to_mocked_configuration(mc)); |
4484 | + |
4485 | + // We report a critical notification to be shown. |
4486 | + battery_observer->properties.is_warning_active = true; |
4487 | + // Which is acknowledged by the user, and we subsequently resume playback. |
4488 | + battery_observer->properties.is_warning_active = false; |
4489 | +} |
4490 | + |
4491 | +TEST(ServiceImplementation, pauses_all_multimedia_sessions_when_accepting_a_call) |
4492 | +{ |
4493 | + auto multimedia_player = make_multimedia_player(); |
4494 | + auto alarm_player = make_alarm_player(); |
4495 | + |
4496 | + EXPECT_CALL(*multimedia_player, pause()).Times(1); |
4497 | + EXPECT_CALL(*alarm_player, pause()).Times(0); |
4498 | + |
4499 | + auto mc = make_mocked_configuration(); |
4500 | + |
4501 | + auto keyed_player_store = std::get<idx::keyed_player_store>(mc); |
4502 | + auto call_monitor = std::get<idx::call_monitor>(mc); |
4503 | + |
4504 | + keyed_player_store->properties.map[0] = multimedia_player; |
4505 | + keyed_player_store->properties.map[1] = alarm_player; |
4506 | + |
4507 | + auto impl = media::ServiceImplementation::create(tie_to_mocked_configuration(mc)); |
4508 | + |
4509 | + // We report an incoming call. |
4510 | + call_monitor->signals.on_call_state_changed(media::telephony::CallMonitor::State::OnHook); |
4511 | +} |
4512 | + |
4513 | +TEST(ServiceImplementation, resumes_all_paused_multimedias_session_when_accepting_a_call) |
4514 | +{ |
4515 | + auto multimedia_player1 = make_multimedia_player(); |
4516 | + auto multimedia_player2 = make_multimedia_player(); |
4517 | + multimedia_player2->playback_status() = media::Player::PlaybackStatus::stopped; |
4518 | + auto alarm_player = make_alarm_player(); |
4519 | + |
4520 | + EXPECT_CALL(*multimedia_player1, pause()).Times(1); |
4521 | + EXPECT_CALL(*multimedia_player2, pause()).Times(0); |
4522 | + EXPECT_CALL(*alarm_player, pause()).Times(0); |
4523 | + |
4524 | + EXPECT_CALL(*multimedia_player1, play()).Times(1); |
4525 | + EXPECT_CALL(*multimedia_player2, play()).Times(0); |
4526 | + EXPECT_CALL(*alarm_player, play()).Times(0); |
4527 | + |
4528 | + auto mc = make_mocked_configuration(); |
4529 | + |
4530 | + auto keyed_player_store = std::get<idx::keyed_player_store>(mc); |
4531 | + auto call_monitor = std::get<idx::call_monitor>(mc); |
4532 | + |
4533 | + keyed_player_store->properties.map[0] = multimedia_player1; |
4534 | + keyed_player_store->properties.map[1] = multimedia_player2; |
4535 | + keyed_player_store->properties.map[2] = alarm_player; |
4536 | + |
4537 | + auto impl = media::ServiceImplementation::create(tie_to_mocked_configuration(mc)); |
4538 | + |
4539 | + // We report an incoming call. |
4540 | + call_monitor->signals.on_call_state_changed(media::telephony::CallMonitor::State::OnHook); |
4541 | + // That is terminated afterwards. |
4542 | + call_monitor->signals.on_call_state_changed(media::telephony::CallMonitor::State::OffHook); |
4543 | +} |
4544 | + |
4545 | +TEST(ServiceImplementation, calls_into_engine_factory_when_creating_a_session) |
4546 | +{ |
4547 | + using namespace ::testing; |
4548 | + |
4549 | + auto engine = std::make_shared<MockEngine>(); |
4550 | + |
4551 | + auto mc = make_mocked_configuration(); |
4552 | + auto engine_factory = std::get<idx::engine_factory>(mc); |
4553 | + |
4554 | + EXPECT_CALL(*engine_factory, make_engine()) |
4555 | + .Times(1).WillRepeatedly(Return(engine)); |
4556 | + |
4557 | + auto impl = media::ServiceImplementation::create(tie_to_mocked_configuration(mc)); |
4558 | + |
4559 | + impl->create_session(media::Player::Configuration{core::dbus::types::ObjectPath::root(), 42}); |
4560 | +} |
4561 | + |
4562 | +TEST(ServiceImplementation, hands_down_correct_player_key_when_creating_a_session) |
4563 | +{ |
4564 | + using namespace ::testing; |
4565 | + |
4566 | + auto mc = make_mocked_configuration(); |
4567 | + auto impl = media::ServiceImplementation::create(tie_to_mocked_configuration(mc)); |
4568 | + auto session = impl->create_session(media::Player::Configuration{core::dbus::types::ObjectPath::root(), 42}); |
4569 | + |
4570 | + EXPECT_EQ(42, session->key()); |
4571 | +} |
4572 | + |
4573 | +// Fails right now, thus disabling it. We have to investigate why the player is not correctly removed from |
4574 | +// the player store. |
4575 | +TEST(ServiceImplementation, DISABLED_subscribes_to_on_client_disconnected_signal_and_removes_disconnected_clients_from_store) |
4576 | +{ |
4577 | + using namespace ::testing; |
4578 | + |
4579 | + auto mc = make_mocked_configuration(); |
4580 | + |
4581 | + auto player_store = std::get<idx::keyed_player_store>(mc); |
4582 | + auto client_death_observer = std::get<idx::death_observer>(mc); |
4583 | + |
4584 | + EXPECT_CALL(*player_store, remove_player_for_key(the_player_key)).Times(1); |
4585 | + |
4586 | + auto impl = media::ServiceImplementation::create(tie_to_mocked_configuration(mc)); |
4587 | + auto session = impl->create_session(media::Player::Configuration |
4588 | + { |
4589 | + core::dbus::types::ObjectPath::root(), the_player_key |
4590 | + }); |
4591 | + |
4592 | + client_death_observer->signals.on_client_with_key_died(the_player_key); |
4593 | +} |
4594 | |
4595 | === added directory 'tests/mock' |
4596 | === added file 'tests/mock/client_death_observer.h' |
4597 | --- tests/mock/client_death_observer.h 1970-01-01 00:00:00 +0000 |
4598 | +++ tests/mock/client_death_observer.h 2014-11-19 10:23:50 +0000 |
4599 | @@ -0,0 +1,48 @@ |
4600 | +/* |
4601 | + * Copyright © 2014 Canonical Ltd. |
4602 | + * |
4603 | + * This program is free software: you can redistribute it and/or modify it |
4604 | + * under the terms of the GNU Lesser General Public License version 3, |
4605 | + * as published by the Free Software Foundation. |
4606 | + * |
4607 | + * This program is distributed in the hope that it will be useful, |
4608 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
4609 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4610 | + * GNU Lesser General Public License for more details. |
4611 | + * |
4612 | + * You should have received a copy of the GNU Lesser General Public License |
4613 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
4614 | + * |
4615 | + * Authored by: Thomas Voß <thomas.voss@canonical.com> |
4616 | + */ |
4617 | + |
4618 | +#ifndef MOCK_CLIENT_DEATH_OBSERVER_H_ |
4619 | +#define MOCK_CLIENT_DEATH_OBSERVER_H_ |
4620 | + |
4621 | +#include <core/media/client_death_observer.h> |
4622 | + |
4623 | +#include <gmock/gmock.h> |
4624 | + |
4625 | +struct MockClientDeathObserver : public core::ubuntu::media::ClientDeathObserver |
4626 | +{ |
4627 | + MockClientDeathObserver() |
4628 | + { |
4629 | + using namespace ::testing; |
4630 | + |
4631 | + ON_CALL(*this, on_client_with_key_died()) |
4632 | + .WillByDefault(ReturnRef(signals.on_client_with_key_died)); |
4633 | + } |
4634 | + |
4635 | + // Registers the client with the given key for death notifications. |
4636 | + MOCK_METHOD1(register_for_death_notifications_with_key, void(const core::ubuntu::media::Player::PlayerKey&)); |
4637 | + // Emitted whenver a client dies, reporting the key under which the |
4638 | + // respective client was known. |
4639 | + MOCK_CONST_METHOD0(on_client_with_key_died, const core::Signal<core::ubuntu::media::Player::PlayerKey>&()); |
4640 | + |
4641 | + struct |
4642 | + { |
4643 | + core::Signal<core::ubuntu::media::Player::PlayerKey> on_client_with_key_died; |
4644 | + } signals; |
4645 | +}; |
4646 | + |
4647 | +#endif // MOCK_CLIENT_DEATH_OBSERVER_H_ |
4648 | |
4649 | === added file 'tests/mock/engine.h' |
4650 | --- tests/mock/engine.h 1970-01-01 00:00:00 +0000 |
4651 | +++ tests/mock/engine.h 2014-11-19 10:23:50 +0000 |
4652 | @@ -0,0 +1,83 @@ |
4653 | +/* |
4654 | + * Copyright © 2014 Canonical Ltd. |
4655 | + * |
4656 | + * This program is free software: you can redistribute it and/or modify it |
4657 | + * under the terms of the GNU Lesser General Public License version 3, |
4658 | + * as published by the Free Software Foundation. |
4659 | + * |
4660 | + * This program is distributed in the hope that it will be useful, |
4661 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
4662 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4663 | + * GNU Lesser General Public License for more details. |
4664 | + * |
4665 | + * You should have received a copy of the GNU Lesser General Public License |
4666 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
4667 | + * |
4668 | + * Authored by: Thomas Voß <thomas.voss@canonical.com> |
4669 | + */ |
4670 | + |
4671 | +#ifndef MOCK_ENGINE_H_ |
4672 | +#define MOCK_ENGINE_H_ |
4673 | + |
4674 | +#include <core/media/base_engine.h> |
4675 | + |
4676 | +#include <gmock/gmock.h> |
4677 | + |
4678 | +#include <chrono> |
4679 | + |
4680 | +struct MockEngine : public core::ubuntu::media::BaseEngine |
4681 | +{ |
4682 | + MockEngine() |
4683 | + { |
4684 | + using namespace ::testing; |
4685 | + |
4686 | + mutable_is_audio_source().install([this]() -> bool |
4687 | + { |
4688 | + return read_is_audio(); |
4689 | + }); |
4690 | + |
4691 | + mutable_is_video_source().install([this]() -> bool |
4692 | + { |
4693 | + return read_is_video(); |
4694 | + }); |
4695 | + |
4696 | + mutable_duration().install([this]() -> std::uint64_t |
4697 | + { |
4698 | + return read_duration(); |
4699 | + }); |
4700 | + |
4701 | + ON_CALL(*this, reset()) |
4702 | + .WillByDefault(Invoke(this, &MockEngine::do_reset)); |
4703 | + } |
4704 | + |
4705 | + // Pull out to make accessible to test cases. |
4706 | + using BaseEngine::mutable_state; |
4707 | + using BaseEngine::mutable_is_audio_source; |
4708 | + using BaseEngine::mutable_is_video_source; |
4709 | + using BaseEngine::mutable_duration; |
4710 | + |
4711 | + MOCK_METHOD0(read_duration, std::uint64_t()); |
4712 | + MOCK_METHOD0(read_is_audio, bool()); |
4713 | + MOCK_METHOD0(read_is_video, bool()); |
4714 | + |
4715 | + // Dependency resolving |
4716 | + MOCK_CONST_METHOD0(meta_data_extractor, const std::shared_ptr<core::ubuntu::media::Engine::MetaDataExtractor>&()); |
4717 | + // Functional part of the interface. |
4718 | + MOCK_METHOD1(open_resource_for_uri, bool(const core::ubuntu::media::Track::UriType&)); |
4719 | + MOCK_METHOD1(create_video_sink, void(uint32_t)); |
4720 | + MOCK_METHOD0(play, bool()); |
4721 | + MOCK_METHOD0(stop, bool()); |
4722 | + MOCK_METHOD0(pause, bool()); |
4723 | + MOCK_METHOD1(seek_to, bool(const std::chrono::microseconds&)); |
4724 | + MOCK_METHOD0(reset, void()); |
4725 | + |
4726 | + void do_reset() |
4727 | + { |
4728 | + // This is ugly and relies on implementation-specific behavior. |
4729 | + // We should refactor PlayerImplementation to manually emit the |
4730 | + // signal. |
4731 | + mutable_client_disconnected_signal(); |
4732 | + } |
4733 | +}; |
4734 | + |
4735 | +#endif // MOCK_ENGINE_H_ |
4736 | |
4737 | === added file 'tests/mock/keyed_player_store.h' |
4738 | --- tests/mock/keyed_player_store.h 1970-01-01 00:00:00 +0000 |
4739 | +++ tests/mock/keyed_player_store.h 2014-11-19 10:23:50 +0000 |
4740 | @@ -0,0 +1,64 @@ |
4741 | +/* |
4742 | + * Copyright © 2014 Canonical Ltd. |
4743 | + * |
4744 | + * This program is free software: you can redistribute it and/or modify it |
4745 | + * under the terms of the GNU Lesser General Public License version 3, |
4746 | + * as published by the Free Software Foundation. |
4747 | + * |
4748 | + * This program is distributed in the hope that it will be useful, |
4749 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
4750 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4751 | + * GNU Lesser General Public License for more details. |
4752 | + * |
4753 | + * You should have received a copy of the GNU Lesser General Public License |
4754 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
4755 | + * |
4756 | + * Authored by: Thomas Voß <thomas.voss@canonical.com> |
4757 | + */ |
4758 | + |
4759 | +#ifndef MOCK_KEYED_PLAYER_STORE_H_ |
4760 | +#define MOCK_KEYED_PLAYER_STORE_H_ |
4761 | + |
4762 | +#include <core/media/keyed_player_store.h> |
4763 | + |
4764 | +struct MockKeyedPlayerStore : public core::ubuntu::media::KeyedPlayerStore |
4765 | +{ |
4766 | + MockKeyedPlayerStore() |
4767 | + { |
4768 | + using namespace ::testing; |
4769 | + |
4770 | + ON_CALL(*this, current_player()) |
4771 | + .WillByDefault(ReturnRef(properties.current_player)); |
4772 | + ON_CALL(*this, enumerate_players(_)) |
4773 | + .WillByDefault(Invoke(this, &MockKeyedPlayerStore::do_enumerate)); |
4774 | + ON_CALL(*this, player_for_key(_)) |
4775 | + .WillByDefault(Invoke(this, &MockKeyedPlayerStore::do_player_for_key)); |
4776 | + } |
4777 | + |
4778 | + MOCK_CONST_METHOD0(current_player, const core::Property<std::shared_ptr<core::ubuntu::media::Player>>&()); |
4779 | + MOCK_CONST_METHOD1(has_player_for_key, bool(const core::ubuntu::media::Player::PlayerKey& key)); |
4780 | + MOCK_CONST_METHOD1(player_for_key, std::shared_ptr<core::ubuntu::media::Player>(const core::ubuntu::media::Player::PlayerKey&)); |
4781 | + MOCK_CONST_METHOD1(enumerate_players, void(const core::ubuntu::media::KeyedPlayerStore::PlayerEnumerator&)); |
4782 | + MOCK_METHOD2(add_player_for_key, void(const core::ubuntu::media::Player::PlayerKey&, const std::shared_ptr<core::ubuntu::media::Player>&)); |
4783 | + MOCK_METHOD1(remove_player_for_key, void(const core::ubuntu::media::Player::PlayerKey&)); |
4784 | + MOCK_METHOD1(set_current_player_for_key, void(const core::ubuntu::media::Player::PlayerKey&)); |
4785 | + |
4786 | + void do_enumerate(PlayerEnumerator enumerator) const |
4787 | + { |
4788 | + for (const auto& pair : properties.map) |
4789 | + enumerator(pair.first, pair.second); |
4790 | + } |
4791 | + |
4792 | + std::shared_ptr<core::ubuntu::media::Player> do_player_for_key(const core::ubuntu::media::Player::PlayerKey& key) |
4793 | + { |
4794 | + return properties.map.at(key); |
4795 | + } |
4796 | + |
4797 | + struct |
4798 | + { |
4799 | + std::map<core::ubuntu::media::Player::PlayerKey, std::shared_ptr<core::ubuntu::media::Player>> map; |
4800 | + core::Property<std::shared_ptr<core::ubuntu::media::Player>> current_player; |
4801 | + } properties; |
4802 | +}; |
4803 | + |
4804 | +#endif // MOCK_KEYED_PLAYER_STORE_H_ |
4805 | |
4806 | === added file 'tests/mock/player.h' |
4807 | --- tests/mock/player.h 1970-01-01 00:00:00 +0000 |
4808 | +++ tests/mock/player.h 2014-11-19 10:23:50 +0000 |
4809 | @@ -0,0 +1,75 @@ |
4810 | +/* |
4811 | + * Copyright © 2014 Canonical Ltd. |
4812 | + * |
4813 | + * This program is free software: you can redistribute it and/or modify it |
4814 | + * under the terms of the GNU Lesser General Public License version 3, |
4815 | + * as published by the Free Software Foundation. |
4816 | + * |
4817 | + * This program is distributed in the hope that it will be useful, |
4818 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
4819 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4820 | + * GNU Lesser General Public License for more details. |
4821 | + * |
4822 | + * You should have received a copy of the GNU Lesser General Public License |
4823 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
4824 | + * |
4825 | + * Authored by: Thomas Voß <thomas.voss@canonical.com> |
4826 | + */ |
4827 | + |
4828 | +#ifndef MOCK_PLAYER_H_ |
4829 | +#define MOCK_PLAYER_H_ |
4830 | + |
4831 | +#include <core/media/base_player.h> |
4832 | + |
4833 | +#include <gmock/gmock.h> |
4834 | + |
4835 | +#include <chrono> |
4836 | + |
4837 | +struct MockPlayer : public core::ubuntu::media::BasePlayer |
4838 | +{ |
4839 | + MockPlayer() |
4840 | + { |
4841 | + using namespace ::testing; |
4842 | + |
4843 | + ON_CALL(*this, track_list()) |
4844 | + .WillByDefault(Return(std::shared_ptr<core::ubuntu::media::TrackList>{})); |
4845 | + ON_CALL(*this, key()) |
4846 | + .WillByDefault(Return(std::numeric_limits<core::ubuntu::media::Player::PlayerKey>::max())); |
4847 | + ON_CALL(*this, create_gl_texture_video_sink(_)) |
4848 | + .WillByDefault(Return(core::ubuntu::media::video::Sink::Ptr{})); |
4849 | + } |
4850 | + |
4851 | + // We pull in the non-const overloads from BasePlayer |
4852 | + using BasePlayer::can_play; |
4853 | + using BasePlayer::playback_status; |
4854 | + using BasePlayer::can_play; |
4855 | + using BasePlayer::can_pause; |
4856 | + using BasePlayer::can_seek; |
4857 | + using BasePlayer::can_go_previous; |
4858 | + using BasePlayer::can_go_next; |
4859 | + using BasePlayer::is_video_source; |
4860 | + using BasePlayer::is_audio_source; |
4861 | + using BasePlayer::meta_data_for_current_track; |
4862 | + using BasePlayer::minimum_playback_rate; |
4863 | + using BasePlayer::maximum_playback_rate; |
4864 | + using BasePlayer::position; |
4865 | + using BasePlayer::duration; |
4866 | + using BasePlayer::orientation; |
4867 | + |
4868 | + using BasePlayer::seeked_to; |
4869 | + using BasePlayer::end_of_stream; |
4870 | + using BasePlayer::video_dimension_changed; |
4871 | + |
4872 | + MOCK_METHOD0(track_list, std::shared_ptr<core::ubuntu::media::TrackList>()); |
4873 | + MOCK_CONST_METHOD0(key, core::ubuntu::media::Player::PlayerKey()); |
4874 | + MOCK_METHOD1(create_gl_texture_video_sink, core::ubuntu::media::video::Sink::Ptr(std::uint32_t)); |
4875 | + |
4876 | + MOCK_METHOD1(open_uri, bool(const core::ubuntu::media::Track::UriType&)); |
4877 | + MOCK_METHOD0(next, void()); |
4878 | + MOCK_METHOD0(previous, void()); |
4879 | + MOCK_METHOD0(play, void()); |
4880 | + MOCK_METHOD0(pause, void()); |
4881 | + MOCK_METHOD0(stop, void()); |
4882 | + MOCK_METHOD1(seek_to, void(const std::chrono::microseconds&)); |
4883 | +}; |
4884 | +#endif // MOCK_PLAYER_H_ |
4885 | |
4886 | === added file 'tests/mock/power_state_controller.h' |
4887 | --- tests/mock/power_state_controller.h 1970-01-01 00:00:00 +0000 |
4888 | +++ tests/mock/power_state_controller.h 2014-11-19 10:23:50 +0000 |
4889 | @@ -0,0 +1,127 @@ |
4890 | +/* |
4891 | + * Copyright © 2014 Canonical Ltd. |
4892 | + * |
4893 | + * This program is free software: you can redistribute it and/or modify it |
4894 | + * under the terms of the GNU Lesser General Public License version 3, |
4895 | + * as published by the Free Software Foundation. |
4896 | + * |
4897 | + * This program is distributed in the hope that it will be useful, |
4898 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
4899 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4900 | + * GNU Lesser General Public License for more details. |
4901 | + * |
4902 | + * You should have received a copy of the GNU Lesser General Public License |
4903 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
4904 | + * |
4905 | + * Authored by: Thomas Voß <thomas.voss@canonical.com> |
4906 | + */ |
4907 | + |
4908 | +#ifndef MOCK_POWER_STATE_CONTROLLER_H |
4909 | +#define MOCK_POWER_STATE_CONTROLLER_H |
4910 | + |
4911 | +#include <core/media/power/state_controller.h> |
4912 | + |
4913 | +#include <gmock/gmock.h> |
4914 | + |
4915 | +struct MockPowerStateController : public core::ubuntu::media::power::StateController |
4916 | +{ |
4917 | + // We really would like to use a templated approach here. However, Google Mock lacks |
4918 | + // a typename and the resulting error message for resolving the result type is: |
4919 | + // player_implementation_test.cpp:70:9: error: need 'typename' before 'testing::internal::Function<void(State)>::Result' because 'testing::internal::Function<void(State)>' is a dependent scope |
4920 | + // MOCK_METHOD1(request_acquire, void(State)); |
4921 | + // With that, just replicate the class. |
4922 | + struct DisplayStateLock : public core::ubuntu::media::power::StateController::Lock<core::ubuntu::media::power::DisplayState> |
4923 | + { |
4924 | + // Save us some typing here. |
4925 | + typedef std::shared_ptr<MockPowerStateController::DisplayStateLock> Ptr; |
4926 | + |
4927 | + DisplayStateLock() |
4928 | + { |
4929 | + using namespace ::testing; |
4930 | + |
4931 | + ON_CALL(*this, acquired()) |
4932 | + .WillByDefault(ReturnRef(signals.acquired)); |
4933 | + ON_CALL(*this, acquired()) |
4934 | + .WillByDefault(ReturnRef(signals.released)); |
4935 | + } |
4936 | + |
4937 | + // Informs the system that the caller would like |
4938 | + // the system to stay active. |
4939 | + MOCK_METHOD1(request_acquire, void(core::ubuntu::media::power::DisplayState)); |
4940 | + // Informs the system that the caller does not |
4941 | + // require the system to stay active anymore. |
4942 | + MOCK_METHOD1(request_release, void(core::ubuntu::media::power::DisplayState)); |
4943 | + |
4944 | + // Emitted whenever the acquire request completes. |
4945 | + MOCK_CONST_METHOD0(acquired, const core::Signal<core::ubuntu::media::power::DisplayState>&()); |
4946 | + // Emitted whenever the release request completes. |
4947 | + MOCK_CONST_METHOD0(released, const core::Signal<core::ubuntu::media::power::DisplayState>&()); |
4948 | + |
4949 | + struct |
4950 | + { |
4951 | + core::Signal<core::ubuntu::media::power::DisplayState> acquired; |
4952 | + core::Signal<core::ubuntu::media::power::DisplayState> released; |
4953 | + } signals; |
4954 | + }; |
4955 | + |
4956 | + struct SystemStateLock : public core::ubuntu::media::power::StateController::Lock<core::ubuntu::media::power::SystemState> |
4957 | + { |
4958 | + // Save us some typing here. |
4959 | + typedef std::shared_ptr<MockPowerStateController::SystemStateLock> Ptr; |
4960 | + |
4961 | + SystemStateLock() |
4962 | + { |
4963 | + using namespace ::testing; |
4964 | + |
4965 | + ON_CALL(*this, acquired()) |
4966 | + .WillByDefault(ReturnRef(signals.acquired)); |
4967 | + ON_CALL(*this, acquired()) |
4968 | + .WillByDefault(ReturnRef(signals.released)); |
4969 | + } |
4970 | + |
4971 | + // Informs the system that the caller would like |
4972 | + // the system to stay active. |
4973 | + MOCK_METHOD1(request_acquire, void(core::ubuntu::media::power::SystemState)); |
4974 | + // Informs the system that the caller does not |
4975 | + // require the system to stay active anymore. |
4976 | + MOCK_METHOD1(request_release, void(core::ubuntu::media::power::SystemState)); |
4977 | + |
4978 | + // Emitted whenever the acquire request completes. |
4979 | + MOCK_CONST_METHOD0(acquired, const core::Signal<core::ubuntu::media::power::SystemState>&()); |
4980 | + // Emitted whenever the release request completes. |
4981 | + MOCK_CONST_METHOD0(released, const core::Signal<core::ubuntu::media::power::SystemState>&()); |
4982 | + |
4983 | + struct |
4984 | + { |
4985 | + core::Signal<core::ubuntu::media::power::SystemState> acquired; |
4986 | + core::Signal<core::ubuntu::media::power::SystemState> released; |
4987 | + } signals; |
4988 | + }; |
4989 | + |
4990 | + MockPowerStateController() |
4991 | + { |
4992 | + using namespace ::testing; |
4993 | + |
4994 | + ON_CALL(*this, display_state_lock()) |
4995 | + .WillByDefault(Return(display_lock)); |
4996 | + ON_CALL(*this, system_state_lock()) |
4997 | + .WillByDefault(Return(system_lock)); |
4998 | + } |
4999 | + |
5000 | + // Returns a power::StateController::Lock<DisplayState> instance. |