Merge lp:~thomas-voss/media-hub/enable-integration-testing into lp:media-hub

Proposed by Thomas Voß
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
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 PlayerImplementation in isolation.

Description of the change

Refactor implementation to allow for testing PlayerImplementation in isolation.

To post a comment you must log in.
104. By Thomas Voß

Add a first set of test cases around media::PlayerImplementation.

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 HashedKeyedPlayerStore.
Add tests for ServiceImplementation.

108. By Thomas Voß

Add further tests around ServiceImplementation.

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 ServiceImplementation.

107. By Thomas Voß

Add tests for HashedKeyedPlayerStore.
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::PlayerImplementation.

103. By Thomas Voß

Refactor implementation to allow for testing PlayerImplementation 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

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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.
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches