Merge lp:~phablet-team/media-hub/tracklist-cli into lp:media-hub
- tracklist-cli
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Ricardo Mendoza |
Approved revision: | 139 |
Merged at revision: | 132 |
Proposed branch: | lp:~phablet-team/media-hub/tracklist-cli |
Merge into: | lp:media-hub |
Diff against target: |
2379 lines (+1155/-173) 32 files modified
include/core/media/player.h (+39/-2) include/core/media/track_list.h (+37/-4) src/core/media/codec.h (+2/-0) src/core/media/engine.h (+2/-1) src/core/media/gstreamer/engine.cpp (+5/-3) src/core/media/gstreamer/engine.h (+1/-1) src/core/media/gstreamer/meta_data_extractor.h (+1/-1) src/core/media/gstreamer/playbin.cpp (+6/-2) src/core/media/gstreamer/playbin.h (+3/-1) src/core/media/mpris/player.h (+8/-3) src/core/media/mpris/track_list.h (+101/-9) src/core/media/player_configuration.h (+2/-0) src/core/media/player_implementation.cpp (+81/-13) src/core/media/player_implementation.h (+1/-1) src/core/media/player_skeleton.cpp (+5/-4) src/core/media/player_skeleton.h (+4/-2) src/core/media/player_stub.cpp (+14/-8) src/core/media/player_stub.h (+3/-2) src/core/media/service_implementation.cpp (+15/-12) src/core/media/service_skeleton.cpp (+8/-2) src/core/media/service_stub.cpp (+3/-0) src/core/media/track_list_implementation.cpp (+83/-12) src/core/media/track_list_implementation.h (+10/-4) src/core/media/track_list_skeleton.cpp (+191/-58) src/core/media/track_list_skeleton.h (+23/-6) src/core/media/track_list_stub.cpp (+34/-11) src/core/media/track_list_stub.h (+11/-5) tests/CMakeLists.txt (+1/-0) tests/test-track-list/CMakeLists.txt (+17/-0) tests/test-track-list/test_track_list.cpp (+346/-0) tests/test-track-list/test_track_list.h (+86/-0) tests/unit-tests/test-gstreamer-engine.cpp (+12/-6) |
To merge this branch: | bzr merge lp:~phablet-team/media-hub/tracklist-cli |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Thomas Voß (community) | Needs Information | ||
PS Jenkins bot | continuous-integration | Approve | |
Review via email: mp+255900@code.launchpad.net |
Commit message
* Implements the backend portion of TrackLists required to allow clients to be able to hand off a playlist to media-hub and keep playing music in order, shuffled and/or looped without the app being in the foreground
Description of the change
* Implements the backend portion of TrackLists required to allow clients to be able to hand off a playlist to media-hub and keep playing music in order, shuffled and/or looped without the app being in the foreground
PS Jenkins bot (ps-jenkins) wrote : | # |
- 136. By Jim Hodapp
-
Initialize the TrackList property returned by Tracks().
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:136
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 137. By Jim Hodapp
-
Include codec includes for vector and string.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:137
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 138. By Jim Hodapp
-
Return the dbus Property instances instead of the local ones.
- 139. By Jim Hodapp
-
Clean up no longer necessary cout statements and one unused function add_set_
state_to_ main_context( ) in playbin.cpp
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:138
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Thomas Voß (thomas-voss) wrote : | # |
I noticed the top-approve, still wanted to leave my comments.
- 140. By Jim Hodapp
-
Make sure the skeleton uses the dbus version of the properties instead of a copy.
- 141. By Jim Hodapp
-
Fix up the tracklist dbus object name/path
- 142. By Jim Hodapp
-
Fix video playback not working because of conflicting play(), stop(), play() during setup when using open_uri().
- 143. By Jim Hodapp
-
Don't call open_uri twice and also make sure that the TrackList handle_
add_track_ with_uri_ at() checks apparmor access permissions.
Preview Diff
1 | === modified file 'include/core/media/player.h' |
2 | --- include/core/media/player.h 2015-03-11 16:17:23 +0000 |
3 | +++ include/core/media/player.h 2015-04-20 17:49:07 +0000 |
4 | @@ -27,6 +27,7 @@ |
5 | #include <core/property.h> |
6 | |
7 | #include <chrono> |
8 | +#include <iosfwd> |
9 | #include <memory> |
10 | |
11 | namespace core |
12 | @@ -149,7 +150,7 @@ |
13 | virtual const core::Property<PlaybackStatus>& playback_status() const = 0; |
14 | virtual const core::Property<LoopStatus>& loop_status() const = 0; |
15 | virtual const core::Property<PlaybackRate>& playback_rate() const = 0; |
16 | - virtual const core::Property<bool>& is_shuffle() const = 0; |
17 | + virtual const core::Property<bool>& shuffle() const = 0; |
18 | virtual const core::Property<Track::MetaData>& meta_data_for_current_track() const = 0; |
19 | virtual const core::Property<Volume>& volume() const = 0; |
20 | virtual const core::Property<PlaybackRate>& minimum_playback_rate() const = 0; |
21 | @@ -162,7 +163,7 @@ |
22 | |
23 | virtual core::Property<LoopStatus>& loop_status() = 0; |
24 | virtual core::Property<PlaybackRate>& playback_rate() = 0; |
25 | - virtual core::Property<bool>& is_shuffle() = 0; |
26 | + virtual core::Property<bool>& shuffle() = 0; |
27 | virtual core::Property<Volume>& volume() = 0; |
28 | virtual core::Property<AudioStreamRole>& audio_stream_role() = 0; |
29 | virtual core::Property<Lifetime>& lifetime() = 0; |
30 | @@ -178,6 +179,42 @@ |
31 | Player(); |
32 | |
33 | }; |
34 | + |
35 | +// operator<< pretty prints the given playback status to the given output stream. |
36 | +inline std::ostream& operator<<(std::ostream& out, Player::PlaybackStatus status) |
37 | +{ |
38 | + switch (status) |
39 | + { |
40 | + case Player::PlaybackStatus::null: |
41 | + return out << "PlaybackStatus::null"; |
42 | + case Player::PlaybackStatus::ready: |
43 | + return out << "PlaybackStatus::ready"; |
44 | + case Player::PlaybackStatus::playing: |
45 | + return out << "PlaybackStatus::playing"; |
46 | + case Player::PlaybackStatus::paused: |
47 | + return out << "PlaybackStatus::paused"; |
48 | + case Player::PlaybackStatus::stopped: |
49 | + return out << "PlaybackStatus::stopped"; |
50 | + } |
51 | + |
52 | + return out; |
53 | +} |
54 | + |
55 | +inline std::ostream& operator<<(std::ostream& out, Player::LoopStatus loop_status) |
56 | +{ |
57 | + switch (loop_status) |
58 | + { |
59 | + case Player::LoopStatus::none: |
60 | + return out << "LoopStatus::none"; |
61 | + case Player::LoopStatus::track: |
62 | + return out << "LoopStatus::track"; |
63 | + case Player::LoopStatus::playlist: |
64 | + return out << "LoopStatus::playlist"; |
65 | + } |
66 | + |
67 | + return out; |
68 | +} |
69 | + |
70 | } |
71 | } |
72 | } |
73 | |
74 | === modified file 'include/core/media/track_list.h' |
75 | --- include/core/media/track_list.h 2014-02-12 15:53:57 +0000 |
76 | +++ include/core/media/track_list.h 2015-04-20 17:49:07 +0000 |
77 | @@ -24,7 +24,8 @@ |
78 | #include <core/signal.h> |
79 | |
80 | #include <functional> |
81 | -#include <list> |
82 | +#include <iosfwd> |
83 | +#include <vector> |
84 | #include <memory> |
85 | |
86 | namespace core |
87 | @@ -39,6 +40,7 @@ |
88 | { |
89 | public: |
90 | typedef std::vector<Track::Id> Container; |
91 | + typedef std::tuple<std::vector<Track::Id>, Track::Id> ContainerTrackIdTuple; |
92 | typedef Container::iterator Iterator; |
93 | typedef Container::const_iterator ConstIterator; |
94 | |
95 | @@ -50,23 +52,54 @@ |
96 | TrackList& operator=(const TrackList&) = delete; |
97 | bool operator==(const TrackList&) const = delete; |
98 | |
99 | + /** If set to false, calling add_track_with_uri_at or remove_track will have no effect. */ |
100 | virtual const core::Property<bool>& can_edit_tracks() const = 0; |
101 | + |
102 | + /** An array which contains the identifier of each track in the tracklist, in order. */ |
103 | virtual const core::Property<Container>& tracks() const = 0; |
104 | |
105 | + /** Gets all the metadata available for a given Track. */ |
106 | virtual Track::MetaData query_meta_data_for_track(const Track::Id& id) = 0; |
107 | + |
108 | + /** Adds a URI in the TrackList. */ |
109 | virtual void add_track_with_uri_at(const Track::UriType& uri, const Track::Id& position, bool make_current) = 0; |
110 | + |
111 | + /** Removes a Track from the TrackList. */ |
112 | virtual void remove_track(const Track::Id& id) = 0; |
113 | |
114 | - virtual void go_to(const Track::Id& track) = 0; |
115 | - |
116 | - virtual const core::Signal<void>& on_track_list_replaced() const = 0; |
117 | + /** Skip to the specified TrackId. Calls stop() and play() on the player if toggle_player_state is true. */ |
118 | + virtual void go_to(const Track::Id& track, bool toggle_player_state) = 0; |
119 | + |
120 | + |
121 | + /** Reorders the tracks such that they are in a random order. */ |
122 | + virtual void shuffle_tracks() = 0; |
123 | + |
124 | + /** Restores the original order of tracks before shuffle mode was turned on. */ |
125 | + virtual void unshuffle_tracks() = 0; |
126 | + |
127 | + /** Clears and resets the TrackList to the same as a newly constructed instance. */ |
128 | + virtual void reset() = 0; |
129 | + |
130 | + |
131 | + /** Indicates that the entire tracklist has been replaced. */ |
132 | + virtual const core::Signal<ContainerTrackIdTuple>& on_track_list_replaced() const = 0; |
133 | + |
134 | + /** Indicates that a track has been added to the track list. */ |
135 | virtual const core::Signal<Track::Id>& on_track_added() const = 0; |
136 | + |
137 | + /** Indicates that a track has been removed from the track list. */ |
138 | virtual const core::Signal<Track::Id>& on_track_removed() const = 0; |
139 | + |
140 | + /** Indicates that the track list advanced from one track to another. */ |
141 | virtual const core::Signal<Track::Id>& on_track_changed() const = 0; |
142 | |
143 | + /** Used to notify the Player of when the client requested that the Player should immediately play a new track. */ |
144 | + virtual const core::Signal<std::pair<Track::Id, bool>>& on_go_to_track() const = 0; |
145 | + |
146 | protected: |
147 | TrackList(); |
148 | }; |
149 | + |
150 | } |
151 | } |
152 | } |
153 | |
154 | === modified file 'src/core/media/codec.h' |
155 | --- src/core/media/codec.h 2015-01-26 09:23:41 +0000 |
156 | +++ src/core/media/codec.h 2015-04-20 17:49:07 +0000 |
157 | @@ -23,6 +23,8 @@ |
158 | #include <core/media/player.h> |
159 | #include <core/media/track.h> |
160 | |
161 | +#include <core/dbus/types/stl/string.h> |
162 | +#include <core/dbus/types/stl/vector.h> |
163 | #include <core/dbus/codec.h> |
164 | |
165 | namespace core |
166 | |
167 | === modified file 'src/core/media/engine.h' |
168 | --- src/core/media/engine.h 2015-01-26 09:23:41 +0000 |
169 | +++ src/core/media/engine.h 2015-04-20 17:49:07 +0000 |
170 | @@ -38,6 +38,7 @@ |
171 | |
172 | enum class State |
173 | { |
174 | + no_media, |
175 | ready, |
176 | busy, |
177 | playing, |
178 | @@ -75,7 +76,7 @@ |
179 | |
180 | virtual const core::Property<State>& state() const = 0; |
181 | |
182 | - virtual bool open_resource_for_uri(const core::ubuntu::media::Track::UriType& uri) = 0; |
183 | + virtual bool open_resource_for_uri(const core::ubuntu::media::Track::UriType& uri, bool do_pipeline_reset) = 0; |
184 | virtual bool open_resource_for_uri(const core::ubuntu::media::Track::UriType& uri, const Player::HeadersType&) = 0; |
185 | // Throws core::ubuntu::media::Player::Error::OutOfProcessBufferStreamingNotSupported if the implementation does not |
186 | // support this feature. |
187 | |
188 | === modified file 'src/core/media/gstreamer/engine.cpp' |
189 | --- src/core/media/gstreamer/engine.cpp 2015-03-11 16:17:23 +0000 |
190 | +++ src/core/media/gstreamer/engine.cpp 2015-04-20 17:49:07 +0000 |
191 | @@ -313,12 +313,13 @@ |
192 | gstreamer::Engine::Engine() : d(new Private{}) |
193 | { |
194 | cout << "Creating a new Engine instance in " << __PRETTY_FUNCTION__ << endl; |
195 | - d->state = media::Engine::State::ready; |
196 | + d->state = media::Engine::State::no_media; |
197 | } |
198 | |
199 | gstreamer::Engine::~Engine() |
200 | { |
201 | stop(); |
202 | + d->state = media::Engine::State::no_media; |
203 | } |
204 | |
205 | const std::shared_ptr<media::Engine::MetaDataExtractor>& gstreamer::Engine::meta_data_extractor() const |
206 | @@ -331,9 +332,9 @@ |
207 | return d->state; |
208 | } |
209 | |
210 | -bool gstreamer::Engine::open_resource_for_uri(const media::Track::UriType& uri) |
211 | +bool gstreamer::Engine::open_resource_for_uri(const media::Track::UriType& uri, bool do_pipeline_reset) |
212 | { |
213 | - d->playbin.set_uri(uri, core::ubuntu::media::Player::HeadersType{}); |
214 | + d->playbin.set_uri(uri, core::ubuntu::media::Player::HeadersType{}, do_pipeline_reset); |
215 | return true; |
216 | } |
217 | |
218 | @@ -356,6 +357,7 @@ |
219 | { |
220 | d->state = media::Engine::State::playing; |
221 | cout << __PRETTY_FUNCTION__ << endl; |
222 | + cout << "Engine: playing uri: " << d->playbin.uri() << endl; |
223 | d->playback_status_changed(media::Player::PlaybackStatus::playing); |
224 | } |
225 | |
226 | |
227 | === modified file 'src/core/media/gstreamer/engine.h' |
228 | --- src/core/media/gstreamer/engine.h 2015-01-26 09:23:41 +0000 |
229 | +++ src/core/media/gstreamer/engine.h 2015-04-20 17:49:07 +0000 |
230 | @@ -33,7 +33,7 @@ |
231 | |
232 | const core::Property<State>& state() const; |
233 | |
234 | - bool open_resource_for_uri(const core::ubuntu::media::Track::UriType& uri); |
235 | + bool open_resource_for_uri(const core::ubuntu::media::Track::UriType& uri, bool do_pipeline_reset); |
236 | bool open_resource_for_uri(const core::ubuntu::media::Track::UriType& uri, const core::ubuntu::media::Player::HeadersType& headers); |
237 | void create_video_sink(uint32_t texture_id); |
238 | |
239 | |
240 | === modified file 'src/core/media/gstreamer/meta_data_extractor.h' |
241 | --- src/core/media/gstreamer/meta_data_extractor.h 2014-10-14 20:05:20 +0000 |
242 | +++ src/core/media/gstreamer/meta_data_extractor.h 2015-04-20 17:49:07 +0000 |
243 | @@ -177,7 +177,7 @@ |
244 | bus.on_new_message.connect( |
245 | [&](const gstreamer::Bus::Message& msg) |
246 | { |
247 | - std::cout << __PRETTY_FUNCTION__ << gst_message_type_get_name(msg.type) << std::endl; |
248 | + //std::cout << __PRETTY_FUNCTION__ << gst_message_type_get_name(msg.type) << std::endl; |
249 | if (msg.type == GST_MESSAGE_TAG) |
250 | { |
251 | MetaDataExtractor::on_tag_available(msg.detail.tag, meta_data); |
252 | |
253 | === modified file 'src/core/media/gstreamer/playbin.cpp' |
254 | --- src/core/media/gstreamer/playbin.cpp 2015-03-11 16:17:23 +0000 |
255 | +++ src/core/media/gstreamer/playbin.cpp 2015-04-20 17:49:07 +0000 |
256 | @@ -24,6 +24,8 @@ |
257 | #include <hybris/media/surface_texture_client_hybris.h> |
258 | #include <hybris/media/media_codec_layer.h> |
259 | |
260 | +#include <utility> |
261 | + |
262 | namespace |
263 | { |
264 | void setup_video_sink_for_buffer_streaming(GstElement* video_sink) |
265 | @@ -365,9 +367,11 @@ |
266 | |
267 | void gstreamer::Playbin::set_uri( |
268 | const std::string& uri, |
269 | - const core::ubuntu::media::Player::HeadersType& headers = core::ubuntu::media::Player::HeadersType()) |
270 | + const core::ubuntu::media::Player::HeadersType& headers = core::ubuntu::media::Player::HeadersType(), |
271 | + bool do_pipeline_reset) |
272 | { |
273 | - reset_pipeline(); |
274 | + if (do_pipeline_reset) |
275 | + reset_pipeline(); |
276 | |
277 | g_object_set(pipeline, "uri", uri.c_str(), NULL); |
278 | if (is_video_file(uri)) |
279 | |
280 | === modified file 'src/core/media/gstreamer/playbin.h' |
281 | --- src/core/media/gstreamer/playbin.h 2015-03-11 16:17:23 +0000 |
282 | +++ src/core/media/gstreamer/playbin.h 2015-04-20 17:49:07 +0000 |
283 | @@ -89,11 +89,13 @@ |
284 | uint64_t position() const; |
285 | uint64_t duration() const; |
286 | |
287 | - void set_uri(const std::string& uri, const core::ubuntu::media::Player::HeadersType& headers); |
288 | + void set_uri(const std::string& uri, const core::ubuntu::media::Player::HeadersType& headers, bool do_pipeline_reset = true); |
289 | std::string uri() const; |
290 | |
291 | void setup_source(GstElement *source); |
292 | |
293 | + // Sets the pipeline's state (stopped, playing, paused, etc). Optional parameter makes this call |
294 | + // in the main_loop context. |
295 | bool set_state_and_wait(GstState new_state); |
296 | bool seek(const std::chrono::microseconds& ms); |
297 | |
298 | |
299 | === modified file 'src/core/media/mpris/player.h' |
300 | --- src/core/media/mpris/player.h 2015-03-11 16:17:23 +0000 |
301 | +++ src/core/media/mpris/player.h 2015-04-20 17:49:07 +0000 |
302 | @@ -271,7 +271,7 @@ |
303 | properties.orientation->set(core::ubuntu::media::Player::Orientation::rotate0); |
304 | properties.lifetime->set(core::ubuntu::media::Player::Lifetime::normal); |
305 | properties.playback_rate->set(configuration.defaults.playback_rate); |
306 | - properties.is_shuffle->set(configuration.defaults.shuffle); |
307 | + properties.shuffle->set(configuration.defaults.shuffle); |
308 | properties.position->set(configuration.defaults.position); |
309 | properties.duration->set(configuration.defaults.duration); |
310 | properties.minimum_playback_rate->set(configuration.defaults.minimum_rate); |
311 | @@ -302,6 +302,11 @@ |
312 | { |
313 | on_property_value_changed<Properties::LoopStatus>(status); |
314 | }); |
315 | + |
316 | + properties.shuffle->changed().connect([this](bool shuffle) |
317 | + { |
318 | + on_property_value_changed<Properties::Shuffle>(shuffle); |
319 | + }); |
320 | } |
321 | |
322 | template<typename Property> |
323 | @@ -332,7 +337,7 @@ |
324 | dict[Properties::Orientation::name()] = dbus::types::Variant::encode(properties.orientation->get()); |
325 | dict[Properties::Lifetime::name()] = dbus::types::Variant::encode(properties.lifetime->get()); |
326 | dict[Properties::PlaybackRate::name()] = dbus::types::Variant::encode(properties.playback_rate->get()); |
327 | - dict[Properties::Shuffle::name()] = dbus::types::Variant::encode(properties.is_shuffle->get()); |
328 | + dict[Properties::Shuffle::name()] = dbus::types::Variant::encode(properties.shuffle->get()); |
329 | dict[Properties::Duration::name()] = dbus::types::Variant::encode(properties.duration->get()); |
330 | dict[Properties::Position::name()] = dbus::types::Variant::encode(properties.position->get()); |
331 | dict[Properties::MinimumRate::name()] = dbus::types::Variant::encode(properties.minimum_playback_rate->get()); |
332 | @@ -363,7 +368,7 @@ |
333 | std::shared_ptr<core::dbus::Property<Properties::Orientation>> orientation; |
334 | std::shared_ptr<core::dbus::Property<Properties::Lifetime>> lifetime; |
335 | std::shared_ptr<core::dbus::Property<Properties::PlaybackRate>> playback_rate; |
336 | - std::shared_ptr<core::dbus::Property<Properties::Shuffle>> is_shuffle; |
337 | + std::shared_ptr<core::dbus::Property<Properties::Shuffle>> shuffle; |
338 | std::shared_ptr<core::dbus::Property<Properties::TypedMetaData>> typed_meta_data_for_current_track; |
339 | std::shared_ptr<core::dbus::Property<Properties::Volume>> volume; |
340 | std::shared_ptr<core::dbus::Property<Properties::Position>> position; |
341 | |
342 | === modified file 'src/core/media/mpris/track_list.h' |
343 | --- src/core/media/mpris/track_list.h 2014-08-25 10:18:32 +0000 |
344 | +++ src/core/media/mpris/track_list.h 2015-04-20 17:49:07 +0000 |
345 | @@ -22,7 +22,10 @@ |
346 | #include <core/dbus/macros.h> |
347 | |
348 | #include <core/dbus/types/any.h> |
349 | +#include <core/dbus/macros.h> |
350 | #include <core/dbus/types/object_path.h> |
351 | +#include <core/dbus/object.h> |
352 | +#include <core/dbus/property.h> |
353 | #include <core/dbus/types/variant.h> |
354 | |
355 | #include <boost/utility/identity_type.hpp> |
356 | @@ -37,38 +40,42 @@ |
357 | { |
358 | struct TrackList |
359 | { |
360 | + typedef std::map<std::string, core::dbus::types::Variant> Dictionary; |
361 | + |
362 | static const std::string& name() |
363 | { |
364 | - static const std::string s{"core.ubuntu.media.Service.Player.TrackList"}; |
365 | + static const std::string s{"org.mpris.MediaPlayer2.TrackList"}; |
366 | return s; |
367 | } |
368 | |
369 | - DBUS_CPP_METHOD_WITH_TIMEOUT_DEF(GetTracksMetadata, TrackList, 1000) |
370 | - DBUS_CPP_METHOD_WITH_TIMEOUT_DEF(AddTrack, TrackList, 1000) |
371 | - DBUS_CPP_METHOD_WITH_TIMEOUT_DEF(RemoveTrack, TrackList, 1000) |
372 | - DBUS_CPP_METHOD_WITH_TIMEOUT_DEF(GoTo, TrackList, 1000) |
373 | + DBUS_CPP_METHOD_DEF(GetTracksMetadata, TrackList) |
374 | + DBUS_CPP_METHOD_DEF(AddTrack, TrackList) |
375 | + DBUS_CPP_METHOD_DEF(RemoveTrack, TrackList) |
376 | + DBUS_CPP_METHOD_DEF(GoTo, TrackList) |
377 | |
378 | struct Signals |
379 | { |
380 | + Signals() = delete; |
381 | + |
382 | DBUS_CPP_SIGNAL_DEF |
383 | ( |
384 | TrackListReplaced, |
385 | TrackList, |
386 | - BOOST_IDENTITY_TYPE((std::tuple<std::vector<dbus::types::ObjectPath>, dbus::types::ObjectPath>)) |
387 | + BOOST_IDENTITY_TYPE((std::tuple<std::vector<core::ubuntu::media::Track::Id>, core::ubuntu::media::Track::Id>)) |
388 | ) |
389 | |
390 | DBUS_CPP_SIGNAL_DEF |
391 | ( |
392 | TrackAdded, |
393 | TrackList, |
394 | - BOOST_IDENTITY_TYPE((std::tuple<std::map<std::string, dbus::types::Variant>, dbus::types::ObjectPath>)) |
395 | + core::ubuntu::media::Track::Id |
396 | ) |
397 | |
398 | DBUS_CPP_SIGNAL_DEF |
399 | ( |
400 | TrackRemoved, |
401 | TrackList, |
402 | - dbus::types::ObjectPath |
403 | + core::ubuntu::media::Track::Id |
404 | ) |
405 | |
406 | DBUS_CPP_SIGNAL_DEF |
407 | @@ -81,9 +88,94 @@ |
408 | |
409 | struct Properties |
410 | { |
411 | - DBUS_CPP_READABLE_PROPERTY_DEF(Tracks, TrackList, std::vector<std::string>) |
412 | + Properties() = delete; |
413 | + |
414 | + DBUS_CPP_READABLE_PROPERTY_DEF(Tracks, TrackList, std::vector<core::ubuntu::media::Track::Id>) |
415 | DBUS_CPP_READABLE_PROPERTY_DEF(CanEditTracks, TrackList, bool) |
416 | }; |
417 | + |
418 | + struct Skeleton |
419 | + { |
420 | + static const std::vector<std::string>& the_empty_list_of_invalidated_properties() |
421 | + { |
422 | + static const std::vector<std::string> instance; return instance; |
423 | + } |
424 | + |
425 | + // Object instance creation time properties go here. |
426 | + struct Configuration |
427 | + { |
428 | + // The dbus object that should implement org.mpris.MediaPlayer2 |
429 | + core::dbus::Object::Ptr object; |
430 | + // Default values assigned to exported dbus interface properties on construction |
431 | + struct Defaults |
432 | + { |
433 | + Properties::Tracks::ValueType tracks{std::vector<core::ubuntu::media::Track::Id>()}; |
434 | + Properties::CanEditTracks::ValueType can_edit_tracks{true}; |
435 | + } defaults; |
436 | + }; |
437 | + |
438 | + Skeleton(const Configuration& configuration) |
439 | + : configuration(configuration), |
440 | + properties |
441 | + { |
442 | + configuration.object->get_property<Properties::Tracks>(), |
443 | + configuration.object->get_property<Properties::CanEditTracks>(), |
444 | + }, |
445 | + signals |
446 | + { |
447 | + configuration.object->get_signal<Signals::TrackListReplaced>(), |
448 | + configuration.object->get_signal<Signals::TrackAdded>(), |
449 | + configuration.object->get_signal<Signals::TrackRemoved>(), |
450 | + configuration.object->get_signal<Signals::TrackMetadataChanged>(), |
451 | + configuration.object->template get_signal<core::dbus::interfaces::Properties::Signals::PropertiesChanged>() |
452 | + } |
453 | + { |
454 | + // Set the default value of the properties on the MPRIS TrackList dbus interface |
455 | + properties.tracks->set(configuration.defaults.tracks); |
456 | + properties.can_edit_tracks->set(configuration.defaults.can_edit_tracks); |
457 | + } |
458 | + |
459 | + template<typename Property> |
460 | + void on_property_value_changed(const typename Property::ValueType& value) |
461 | + { |
462 | + Dictionary dict; |
463 | + dict[Property::name()] = dbus::types::Variant::encode(value); |
464 | + |
465 | + signals.properties_changed->emit(std::make_tuple( |
466 | + dbus::traits::Service<TrackList>::interface_name(), |
467 | + dict, |
468 | + the_empty_list_of_invalidated_properties())); |
469 | + } |
470 | + |
471 | + std::map<std::string, core::dbus::types::Variant> get_all_properties() |
472 | + { |
473 | + std::map<std::string, core::dbus::types::Variant> dict; |
474 | + dict[Properties::Tracks::name()] = core::dbus::types::Variant::encode(properties.tracks->get()); |
475 | + dict[Properties::CanEditTracks::name()] = core::dbus::types::Variant::encode(properties.can_edit_tracks->get()); |
476 | + |
477 | + return dict; |
478 | + } |
479 | + |
480 | + Configuration configuration; |
481 | + |
482 | + struct |
483 | + { |
484 | + std::shared_ptr<core::dbus::Property<Properties::Tracks>> tracks; |
485 | + std::shared_ptr<core::dbus::Property<Properties::CanEditTracks>> can_edit_tracks; |
486 | + } properties; |
487 | + |
488 | + struct |
489 | + { |
490 | + core::dbus::Signal<Signals::TrackListReplaced, Signals::TrackListReplaced::ArgumentType>::Ptr tracklist_replaced; |
491 | + core::dbus::Signal<Signals::TrackAdded, Signals::TrackAdded::ArgumentType>::Ptr track_added; |
492 | + core::dbus::Signal<Signals::TrackRemoved, Signals::TrackRemoved::ArgumentType>::Ptr track_removed; |
493 | + core::dbus::Signal<Signals::TrackMetadataChanged, Signals::TrackMetadataChanged::ArgumentType>::Ptr track_metadata_changed; |
494 | + |
495 | + dbus::Signal <core::dbus::interfaces::Properties::Signals::PropertiesChanged, |
496 | + core::dbus::interfaces::Properties::Signals::PropertiesChanged::ArgumentType |
497 | + >::Ptr properties_changed; |
498 | + } signals; |
499 | + }; |
500 | }; |
501 | } |
502 | |
503 | |
504 | === modified file 'src/core/media/player_configuration.h' |
505 | --- src/core/media/player_configuration.h 2014-11-26 16:23:19 +0000 |
506 | +++ src/core/media/player_configuration.h 2015-04-20 17:49:07 +0000 |
507 | @@ -31,6 +31,8 @@ |
508 | core::ubuntu::media::Player::PlayerKey key; |
509 | // The bus connection to expose objects on. |
510 | std::shared_ptr<core::dbus::Bus> bus; |
511 | + // The service instance we live under. |
512 | + std::shared_ptr<core::dbus::Service> service; |
513 | // The actual session object representing a player instance. |
514 | std::shared_ptr<core::dbus::Object> session; |
515 | }; |
516 | |
517 | === modified file 'src/core/media/player_implementation.cpp' |
518 | --- src/core/media/player_implementation.cpp 2015-03-19 19:08:47 +0000 |
519 | +++ src/core/media/player_implementation.cpp 2015-04-20 17:49:07 +0000 |
520 | @@ -1,4 +1,5 @@ |
521 | /* |
522 | + * Copyright © 2013-2015 Canonical Ltd. |
523 | * |
524 | * This program is free software: you can redistribute it and/or modify it |
525 | * under the terms of the GNU Lesser General Public License version 3, |
526 | @@ -23,7 +24,6 @@ |
527 | |
528 | #include "client_death_observer.h" |
529 | #include "engine.h" |
530 | -#include "null_track_list.h" |
531 | #include "track_list_implementation.h" |
532 | |
533 | #include "gstreamer/engine.h" |
534 | @@ -58,14 +58,20 @@ |
535 | display_state_lock(config.power_state_controller->display_state_lock()), |
536 | system_state_lock(config.power_state_controller->system_state_lock()), |
537 | engine(std::make_shared<gstreamer::Engine>()), |
538 | - track_list(std::make_shared<NullTrackList>()), |
539 | + track_list(std::make_shared<TrackListImplementation>( |
540 | + config.parent.bus, |
541 | + config.parent.service->add_object_for_path( |
542 | + dbus::types::ObjectPath(config.parent.session->path().as_string() + "/TrackList")), |
543 | + engine->meta_data_extractor(), |
544 | + config.parent.request_context_resolver, |
545 | + config.parent.request_authenticator)), |
546 | system_wakelock_count(0), |
547 | display_wakelock_count(0), |
548 | previous_state(Engine::State::stopped), |
549 | engine_state_change_connection(engine->state().changed().connect(make_state_change_handler())), |
550 | - engine_playback_status_change_connection(engine->playback_status_changed_signal().connect(make_playback_status_change_handler())) |
551 | + engine_playback_status_change_connection(engine->playback_status_changed_signal().connect(make_playback_status_change_handler())), |
552 | + doing_go_to_track(false) |
553 | { |
554 | - std::cout << "Private parent instance: " << parent << std::endl; |
555 | // Poor man's logging of release/acquire events. |
556 | display_state_lock->acquired().connect([](media::power::DisplayState state) |
557 | { |
558 | @@ -99,7 +105,6 @@ |
559 | // by disconnecting the state change signal |
560 | engine_state_change_connection.disconnect(); |
561 | |
562 | - std::cout << "** Disconnecting playback_status_changed_signal connection"; |
563 | // The engine destructor can lead to a playback status change which will |
564 | // trigger the playback status change handler. Ensure the handler is not called |
565 | // by disconnecting the playback status change signal |
566 | @@ -169,7 +174,7 @@ |
567 | { |
568 | return [this](const media::Player::PlaybackStatus& status) |
569 | { |
570 | - std::cout << "Emiting playback_status_changed for parent: " << parent << std::endl; |
571 | + std::cout << "Emiting playback_status_changed signal: " << status << std::endl; |
572 | parent->emit_playback_status_changed(status); |
573 | }; |
574 | } |
575 | @@ -290,13 +295,15 @@ |
576 | media::power::StateController::Lock<media::power::SystemState>::Ptr system_state_lock; |
577 | |
578 | std::shared_ptr<Engine> engine; |
579 | - std::shared_ptr<media::NullTrackList> track_list; |
580 | + std::shared_ptr<media::TrackListImplementation> track_list; |
581 | std::atomic<int> system_wakelock_count; |
582 | std::atomic<int> display_wakelock_count; |
583 | Engine::State previous_state; |
584 | core::Signal<> on_client_disconnected; |
585 | core::Connection engine_state_change_connection; |
586 | core::Connection engine_playback_status_change_connection; |
587 | + // Prevent the TrackList from auto advancing to the next track |
588 | + std::atomic<bool> doing_go_to_track; |
589 | }; |
590 | |
591 | template<typename Parent> |
592 | @@ -312,7 +319,7 @@ |
593 | Parent::can_go_next().set(true); |
594 | Parent::is_video_source().set(false); |
595 | Parent::is_audio_source().set(false); |
596 | - Parent::is_shuffle().set(true); |
597 | + Parent::shuffle().set(false); |
598 | Parent::playback_rate().set(1.f); |
599 | Parent::playback_status().set(Player::PlaybackStatus::null); |
600 | Parent::loop_status().set(Player::LoopStatus::none); |
601 | @@ -352,6 +359,18 @@ |
602 | }; |
603 | Parent::is_audio_source().install(audio_type_getter); |
604 | |
605 | + // When the client changes the loop status, make sure to update the TrackList |
606 | + Parent::loop_status().changed().connect([this](media::Player::LoopStatus loop_status) |
607 | + { |
608 | + d->track_list->on_loop_status_changed(loop_status); |
609 | + }); |
610 | + |
611 | + // When the client changes the shuffle setting, make sure to update the TrackList |
612 | + Parent::shuffle().changed().connect([this](bool shuffle) |
613 | + { |
614 | + d->track_list->on_shuffle_changed(shuffle); |
615 | + }); |
616 | + |
617 | // Make sure that the audio_stream_role property gets updated on the Engine side |
618 | // whenever the client side sets the role |
619 | Parent::audio_stream_role().changed().connect([this](media::Player::AudioStreamRole new_role) |
620 | @@ -375,12 +394,21 @@ |
621 | { |
622 | Parent::about_to_finish()(); |
623 | |
624 | - if (d->track_list->has_next()) |
625 | + if (!d->doing_go_to_track) |
626 | { |
627 | - Track::UriType uri = d->track_list->query_uri_for_track(d->track_list->next()); |
628 | - if (!uri.empty()) |
629 | - d->parent->open_uri(uri); |
630 | + const media::Track::Id prev_track_id = d->track_list->current(); |
631 | + // Make sure that the TrackList keeps advancing. The logic for what gets played next, |
632 | + // if anything at all, occurs in TrackListSkeleton::next() |
633 | + const Track::UriType uri = d->track_list->query_uri_for_track(d->track_list->next()); |
634 | + if (prev_track_id != d->track_list->current() && !uri.empty()) |
635 | + { |
636 | + std::cout << "Setting next track on playbin: " << uri << std::endl; |
637 | + static const bool do_pipeline_reset = false; |
638 | + d->engine->open_resource_for_uri(uri, do_pipeline_reset); |
639 | + } |
640 | } |
641 | + else |
642 | + std::cout << "Not auto-advancing the TrackList since doing_go_to_track is true" << std::endl; |
643 | }); |
644 | |
645 | d->engine->client_disconnected_signal().connect([this]() |
646 | @@ -388,6 +416,7 @@ |
647 | // If the client disconnects, make sure both wakelock types |
648 | // are cleared |
649 | d->clear_wakelocks(); |
650 | + d->track_list->reset(); |
651 | // And tell the outside world that the client has gone away |
652 | d->on_client_disconnected(); |
653 | }); |
654 | @@ -412,6 +441,33 @@ |
655 | Parent::error()(e); |
656 | }); |
657 | |
658 | + d->track_list->on_go_to_track().connect([this](std::pair<const media::Track::Id, bool> p) |
659 | + { |
660 | + // This prevents the TrackList from auto advancing in other areas such as the about_to_finish signal |
661 | + // handler. |
662 | + d->doing_go_to_track = true; |
663 | + |
664 | + const media::Track::Id id = p.first; |
665 | + const bool toggle_player_state = p.second; |
666 | + |
667 | + if (toggle_player_state) |
668 | + d->engine->stop(); |
669 | + |
670 | + const Track::UriType uri = d->track_list->query_uri_for_track(id); |
671 | + if (!uri.empty()) |
672 | + { |
673 | + std::cout << "Setting next track on playbin (on_go_to_track signal): " << uri << std::endl; |
674 | + std::cout << "\twith a Track::Id: " << id << std::endl; |
675 | + static const bool do_pipeline_reset = true; |
676 | + d->engine->open_resource_for_uri(uri, do_pipeline_reset); |
677 | + } |
678 | + |
679 | + if (toggle_player_state) |
680 | + d->engine->play(); |
681 | + |
682 | + d->doing_go_to_track = false; |
683 | + }); |
684 | + |
685 | // Everything is setup, we now subscribe to death notifications. |
686 | std::weak_ptr<Private> wp{d}; |
687 | |
688 | @@ -487,7 +543,9 @@ |
689 | template<typename Parent> |
690 | bool media::PlayerImplementation<Parent>::open_uri(const Track::UriType& uri) |
691 | { |
692 | - return d->engine->open_resource_for_uri(uri); |
693 | + // Set new track as the current track to play |
694 | + d->track_list->add_track_with_uri_at(uri, media::TrackList::after_empty_track(), true); |
695 | + return true; |
696 | } |
697 | |
698 | template<typename Parent> |
699 | @@ -509,6 +567,16 @@ |
700 | template<typename Parent> |
701 | void media::PlayerImplementation<Parent>::play() |
702 | { |
703 | + if (d->track_list != nullptr && d->track_list->tracks()->size() > 0 && d->engine->state() == media::Engine::State::no_media) |
704 | + { |
705 | + // Using a TrackList for playback, added tracks via add_track(), but open_uri hasn't been called yet |
706 | + // to load a media resource |
707 | + std::cout << "No media loaded yet, calling open_uri on first track in track_list" << std::endl; |
708 | + static const bool do_pipeline_reset = true; |
709 | + d->engine->open_resource_for_uri(d->track_list->query_uri_for_track(d->track_list->current()), do_pipeline_reset); |
710 | + std::cout << *d->track_list << endl; |
711 | + } |
712 | + |
713 | d->engine->play(); |
714 | } |
715 | |
716 | |
717 | === modified file 'src/core/media/player_implementation.h' |
718 | --- src/core/media/player_implementation.h 2015-03-19 19:08:47 +0000 |
719 | +++ src/core/media/player_implementation.h 2015-04-20 17:49:07 +0000 |
720 | @@ -78,8 +78,8 @@ |
721 | struct Private; |
722 | std::shared_ptr<Private> d; |
723 | }; |
724 | + |
725 | } |
726 | } |
727 | } |
728 | #endif // CORE_UBUNTU_MEDIA_PLAYER_IMPLEMENTATION_H_ |
729 | - |
730 | |
731 | === modified file 'src/core/media/player_skeleton.cpp' |
732 | --- src/core/media/player_skeleton.cpp 2015-03-11 16:17:23 +0000 |
733 | +++ src/core/media/player_skeleton.cpp 2015-04-20 17:49:07 +0000 |
734 | @@ -179,6 +179,7 @@ |
735 | Track::UriType uri; |
736 | in->reader() >> uri; |
737 | |
738 | + // Make sure the client has adequate apparmor permissions to open the URI |
739 | auto result = request_authenticator->authenticate_open_uri_request(context, uri); |
740 | |
741 | auto reply = dbus::Message::make_method_return(in); |
742 | @@ -407,9 +408,9 @@ |
743 | return *d->skeleton.properties.playback_rate; |
744 | } |
745 | |
746 | -const core::Property<bool>& media::PlayerSkeleton::is_shuffle() const |
747 | +const core::Property<bool>& media::PlayerSkeleton::shuffle() const |
748 | { |
749 | - return *d->skeleton.properties.is_shuffle; |
750 | + return *d->skeleton.properties.shuffle; |
751 | } |
752 | |
753 | const core::Property<media::Track::MetaData>& media::PlayerSkeleton::meta_data_for_current_track() const |
754 | @@ -467,9 +468,9 @@ |
755 | return *d->skeleton.properties.playback_rate; |
756 | } |
757 | |
758 | -core::Property<bool>& media::PlayerSkeleton::is_shuffle() |
759 | +core::Property<bool>& media::PlayerSkeleton::shuffle() |
760 | { |
761 | - return *d->skeleton.properties.is_shuffle; |
762 | + return *d->skeleton.properties.shuffle; |
763 | } |
764 | |
765 | core::Property<media::Player::Volume>& media::PlayerSkeleton::volume() |
766 | |
767 | === modified file 'src/core/media/player_skeleton.h' |
768 | --- src/core/media/player_skeleton.h 2015-03-11 16:17:23 +0000 |
769 | +++ src/core/media/player_skeleton.h 2015-04-20 17:49:07 +0000 |
770 | @@ -53,6 +53,8 @@ |
771 | { |
772 | // The bus connection we are associated with. |
773 | std::shared_ptr<core::dbus::Bus> bus; |
774 | + // The service instance we are exposed under. |
775 | + std::shared_ptr<core::dbus::Service> service; |
776 | // The session object that we want to expose the skeleton upon. |
777 | std::shared_ptr<core::dbus::Object> session; |
778 | // Our functional dependencies. |
779 | @@ -73,7 +75,7 @@ |
780 | virtual const core::Property<PlaybackStatus>& playback_status() const; |
781 | virtual const core::Property<LoopStatus>& loop_status() const; |
782 | virtual const core::Property<PlaybackRate>& playback_rate() const; |
783 | - virtual const core::Property<bool>& is_shuffle() const; |
784 | + virtual const core::Property<bool>& shuffle() const; |
785 | virtual const core::Property<Track::MetaData>& meta_data_for_current_track() const; |
786 | virtual const core::Property<Volume>& volume() const; |
787 | virtual const core::Property<PlaybackRate>& minimum_playback_rate() const; |
788 | @@ -86,7 +88,7 @@ |
789 | |
790 | virtual core::Property<LoopStatus>& loop_status(); |
791 | virtual core::Property<PlaybackRate>& playback_rate(); |
792 | - virtual core::Property<bool>& is_shuffle(); |
793 | + virtual core::Property<bool>& shuffle(); |
794 | virtual core::Property<Volume>& volume(); |
795 | virtual core::Property<AudioStreamRole>& audio_stream_role(); |
796 | virtual core::Property<Lifetime>& lifetime(); |
797 | |
798 | === modified file 'src/core/media/player_stub.cpp' |
799 | --- src/core/media/player_stub.cpp 2015-03-16 14:54:47 +0000 |
800 | +++ src/core/media/player_stub.cpp 2015-04-20 17:49:07 +0000 |
801 | @@ -43,8 +43,10 @@ |
802 | struct media::PlayerStub::Private |
803 | { |
804 | Private(const std::shared_ptr<Service>& parent, |
805 | + const std::shared_ptr<core::dbus::Service>& service, |
806 | const std::shared_ptr<core::dbus::Object>& object |
807 | ) : parent(parent), |
808 | + service(service), |
809 | object(object), |
810 | key(object->invoke_method_synchronously<mpris::Player::Key, media::Player::PlayerKey>().value()), |
811 | sink_factory(media::video::make_platform_default_sink_factory(key)), |
812 | @@ -91,6 +93,7 @@ |
813 | |
814 | std::shared_ptr<Service> parent; |
815 | std::shared_ptr<TrackList> track_list; |
816 | + dbus::Service::Ptr service; |
817 | dbus::Object::Ptr object; |
818 | media::Player::PlayerKey key; |
819 | media::video::SinkFactory sink_factory; |
820 | @@ -108,7 +111,7 @@ |
821 | std::shared_ptr<core::dbus::Property<mpris::Player::Properties::TypedPlaybackStatus>> playback_status; |
822 | std::shared_ptr<core::dbus::Property<mpris::Player::Properties::TypedLoopStatus>> loop_status; |
823 | std::shared_ptr<core::dbus::Property<mpris::Player::Properties::PlaybackRate>> playback_rate; |
824 | - std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Shuffle>> is_shuffle; |
825 | + std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Shuffle>> shuffle; |
826 | std::shared_ptr<core::dbus::Property<mpris::Player::Properties::TypedMetaData>> meta_data_for_current_track; |
827 | std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Volume>> volume; |
828 | std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Position>> position; |
829 | @@ -153,7 +156,7 @@ |
830 | { |
831 | dbus.seeked_to->connect([this](std::uint64_t value) |
832 | { |
833 | - std::cout << "seeked_to signal arrived via the bus." << std::endl; |
834 | + std::cout << "SeekedTo signal arrived via the bus." << std::endl; |
835 | seeked_to(value); |
836 | }); |
837 | |
838 | @@ -209,8 +212,9 @@ |
839 | |
840 | media::PlayerStub::PlayerStub( |
841 | const std::shared_ptr<Service>& parent, |
842 | + const std::shared_ptr<core::dbus::Service>& service, |
843 | const std::shared_ptr<core::dbus::Object>& object) |
844 | - : d(new Private{parent, object}) |
845 | + : d(new Private{parent, service, object}) |
846 | { |
847 | } |
848 | |
849 | @@ -224,7 +228,9 @@ |
850 | { |
851 | d->track_list = std::make_shared<media::TrackListStub>( |
852 | shared_from_this(), |
853 | - dbus::types::ObjectPath(d->object->path().as_string() + "/TrackList")); |
854 | + d->service->object_for_path( |
855 | + dbus::types::ObjectPath( |
856 | + d->object->path().as_string() + "/TrackList"))); |
857 | } |
858 | return d->track_list; |
859 | } |
860 | @@ -362,9 +368,9 @@ |
861 | return *d->properties.playback_rate; |
862 | } |
863 | |
864 | -const core::Property<bool>& media::PlayerStub::is_shuffle() const |
865 | +const core::Property<bool>& media::PlayerStub::shuffle() const |
866 | { |
867 | - return *d->properties.is_shuffle; |
868 | + return *d->properties.shuffle; |
869 | } |
870 | |
871 | const core::Property<media::Track::MetaData>& media::PlayerStub::meta_data_for_current_track() const |
872 | @@ -422,9 +428,9 @@ |
873 | return *d->properties.playback_rate; |
874 | } |
875 | |
876 | -core::Property<bool>& media::PlayerStub::is_shuffle() |
877 | +core::Property<bool>& media::PlayerStub::shuffle() |
878 | { |
879 | - return *d->properties.is_shuffle; |
880 | + return *d->properties.shuffle; |
881 | } |
882 | |
883 | core::Property<media::Player::Volume>& media::PlayerStub::volume() |
884 | |
885 | === modified file 'src/core/media/player_stub.h' |
886 | --- src/core/media/player_stub.h 2015-03-11 16:17:23 +0000 |
887 | +++ src/core/media/player_stub.h 2015-04-20 17:49:07 +0000 |
888 | @@ -39,6 +39,7 @@ |
889 | public: |
890 | explicit PlayerStub( |
891 | const std::shared_ptr<Service>& parent, |
892 | + const std::shared_ptr<core::dbus::Service>& service, |
893 | const std::shared_ptr<core::dbus::Object>& object); |
894 | |
895 | ~PlayerStub(); |
896 | @@ -67,7 +68,7 @@ |
897 | virtual const core::Property<PlaybackStatus>& playback_status() const; |
898 | virtual const core::Property<LoopStatus>& loop_status() const; |
899 | virtual const core::Property<PlaybackRate>& playback_rate() const; |
900 | - virtual const core::Property<bool>& is_shuffle() const; |
901 | + virtual const core::Property<bool>& shuffle() const; |
902 | virtual const core::Property<Track::MetaData>& meta_data_for_current_track() const; |
903 | virtual const core::Property<Volume>& volume() const; |
904 | virtual const core::Property<PlaybackRate>& minimum_playback_rate() const; |
905 | @@ -80,7 +81,7 @@ |
906 | |
907 | virtual core::Property<LoopStatus>& loop_status(); |
908 | virtual core::Property<PlaybackRate>& playback_rate(); |
909 | - virtual core::Property<bool>& is_shuffle(); |
910 | + virtual core::Property<bool>& shuffle(); |
911 | virtual core::Property<Volume>& volume(); |
912 | virtual core::Property<AudioStreamRole>& audio_stream_role(); |
913 | virtual core::Property<Lifetime>& lifetime(); |
914 | |
915 | === modified file 'src/core/media/service_implementation.cpp' |
916 | --- src/core/media/service_implementation.cpp 2015-04-02 14:46:52 +0000 |
917 | +++ src/core/media/service_implementation.cpp 2015-04-20 17:49:07 +0000 |
918 | @@ -53,6 +53,8 @@ |
919 | |
920 | struct media::ServiceImplementation::Private |
921 | { |
922 | + // Create all of the appropriate observers and helper class instances to be |
923 | + // passed to the PlayerImplementation |
924 | Private(const ServiceImplementation::Configuration& configuration) |
925 | : configuration(configuration), |
926 | resume_key(std::numeric_limits<std::uint32_t>::max()), |
927 | @@ -72,7 +74,7 @@ |
928 | media::ServiceImplementation::Configuration configuration; |
929 | // This holds the key of the multimedia role Player instance that was paused |
930 | // when the battery level reached 10% or 5% |
931 | - media::Player::PlayerKey resume_key; |
932 | + media::Player::PlayerKey resume_key; |
933 | media::power::BatteryObserver::Ptr battery_observer; |
934 | media::power::StateController::Ptr power_state_controller; |
935 | media::power::StateController::Lock<media::power::DisplayState>::Ptr display_state_lock; |
936 | @@ -185,6 +187,7 @@ |
937 | media::PlayerSkeleton::Configuration |
938 | { |
939 | conf.bus, |
940 | + conf.service, |
941 | conf.session, |
942 | d->request_context_resolver, |
943 | d->request_authenticator |
944 | @@ -262,17 +265,17 @@ |
945 | void media::ServiceImplementation::pause_all_multimedia_sessions(bool resume_play_after_phonecall) |
946 | { |
947 | d->configuration.player_store->enumerate_players([this, resume_play_after_phonecall](const media::Player::PlayerKey& key, const std::shared_ptr<media::Player>& player) |
948 | - { |
949 | - if (player->playback_status() == Player::playing |
950 | - && player->audio_stream_role() == media::Player::multimedia) |
951 | - { |
952 | - auto paused_player_pair = std::make_pair(key, resume_play_after_phonecall); |
953 | - d->paused_sessions.push_back(paused_player_pair); |
954 | - std::cout << "Pausing Player with key: " << key << ", resuming after phone call? " |
955 | - << (resume_play_after_phonecall ? "yes" : "no") << std::endl; |
956 | - player->pause(); |
957 | - } |
958 | - }); |
959 | + { |
960 | + if (player->playback_status() == Player::playing |
961 | + && player->audio_stream_role() == media::Player::multimedia) |
962 | + { |
963 | + auto paused_player_pair = std::make_pair(key, resume_play_after_phonecall); |
964 | + d->paused_sessions.push_back(paused_player_pair); |
965 | + std::cout << "Pausing Player with key: " << key << ", resuming after phone call? " |
966 | + << (resume_play_after_phonecall ? "yes" : "no") << std::endl; |
967 | + player->pause(); |
968 | + } |
969 | + }); |
970 | } |
971 | |
972 | void media::ServiceImplementation::resume_paused_multimedia_sessions(bool resume_video_sessions) |
973 | |
974 | === modified file 'src/core/media/service_skeleton.cpp' |
975 | --- src/core/media/service_skeleton.cpp 2014-12-15 14:43:44 +0000 |
976 | +++ src/core/media/service_skeleton.cpp 2015-04-20 17:49:07 +0000 |
977 | @@ -52,7 +52,7 @@ |
978 | Private(media::ServiceSkeleton* impl, const ServiceSkeleton::Configuration& config) |
979 | : impl(impl), |
980 | object(impl->access_service()->add_object_for_path( |
981 | - dbus::traits::Service<media::Service>::object_path())), |
982 | + dbus::traits::Service<media::Service>::object_path())), |
983 | exported(impl->access_bus(), config.cover_art_resolver), |
984 | configuration(config) |
985 | { |
986 | @@ -101,6 +101,7 @@ |
987 | { |
988 | key, |
989 | impl->access_bus(), |
990 | + impl->access_service(), |
991 | impl->access_service()->add_object_for_path(op) |
992 | }; |
993 | |
994 | @@ -130,7 +131,7 @@ |
995 | |
996 | if (named_player_map.count(name) == 0) { |
997 | // Create new session |
998 | - auto session_info = create_session_info(); |
999 | + auto session_info = create_session_info(); |
1000 | |
1001 | dbus::types::ObjectPath op{session_info.first}; |
1002 | media::Player::PlayerKey key{session_info.second}; |
1003 | @@ -139,6 +140,7 @@ |
1004 | { |
1005 | key, |
1006 | impl->access_bus(), |
1007 | + impl->access_service(), |
1008 | impl->access_service()->add_object_for_path(op) |
1009 | }; |
1010 | |
1011 | @@ -266,6 +268,7 @@ |
1012 | |
1013 | static std::string service_name() |
1014 | { |
1015 | +#if 0 |
1016 | static const bool export_to_indicator_sound_via_mpris |
1017 | { |
1018 | core::posix::this_process::env::get("UBUNTU_MEDIA_HUB_EXPORT_TO_INDICATOR_VIA_MPRIS", "0") == "1" |
1019 | @@ -273,6 +276,9 @@ |
1020 | |
1021 | return export_to_indicator_sound_via_mpris ? "org.mpris.MediaPlayer2.MediaHub" : |
1022 | "hidden.org.mpris.MediaPlayer2.MediaHub"; |
1023 | +#else |
1024 | + return "org.mpris.MediaPlayer2.MediaHub"; |
1025 | +#endif |
1026 | } |
1027 | |
1028 | explicit Exported(const dbus::Bus::Ptr& bus, const media::CoverArtResolver& cover_art_resolver) |
1029 | |
1030 | === modified file 'src/core/media/service_stub.cpp' |
1031 | --- src/core/media/service_stub.cpp 2014-10-23 14:42:59 +0000 |
1032 | +++ src/core/media/service_stub.cpp 2015-04-20 17:49:07 +0000 |
1033 | @@ -66,6 +66,7 @@ |
1034 | return std::shared_ptr<media::Player>(new media::PlayerStub |
1035 | { |
1036 | shared_from_this(), |
1037 | + access_service(), |
1038 | access_service()->object_for_path(op.value()) |
1039 | }); |
1040 | } |
1041 | @@ -81,6 +82,7 @@ |
1042 | return std::shared_ptr<media::Player>(new media::PlayerStub |
1043 | { |
1044 | shared_from_this(), |
1045 | + access_service(), |
1046 | access_service()->object_for_path(op.value()) |
1047 | }); |
1048 | } |
1049 | @@ -96,6 +98,7 @@ |
1050 | return std::shared_ptr<media::Player>(new media::PlayerStub |
1051 | { |
1052 | shared_from_this(), |
1053 | + access_service(), |
1054 | access_service()->object_for_path(op.value()) |
1055 | }); |
1056 | } |
1057 | |
1058 | === modified file 'src/core/media/track_list_implementation.cpp' |
1059 | --- src/core/media/track_list_implementation.cpp 2014-03-25 14:57:15 +0000 |
1060 | +++ src/core/media/track_list_implementation.cpp 2015-04-20 17:49:07 +0000 |
1061 | @@ -1,5 +1,5 @@ |
1062 | /* |
1063 | - * Copyright © 2013 Canonical Ltd. |
1064 | + * Copyright © 2013-2015 Canonical Ltd. |
1065 | * |
1066 | * This program is free software: you can redistribute it and/or modify it |
1067 | * under the terms of the GNU Lesser General Public License version 3, |
1068 | @@ -16,8 +16,10 @@ |
1069 | * Authored by: Thomas Voß <thomas.voss@canonical.com> |
1070 | */ |
1071 | |
1072 | +#include <algorithm> |
1073 | #include <stdio.h> |
1074 | #include <stdlib.h> |
1075 | +#include <tuple> |
1076 | |
1077 | #include "track_list_implementation.h" |
1078 | |
1079 | @@ -30,16 +32,23 @@ |
1080 | { |
1081 | typedef std::map<Track::Id, std::tuple<Track::UriType, Track::MetaData>> MetaDataCache; |
1082 | |
1083 | - dbus::types::ObjectPath path; |
1084 | + dbus::Object::Ptr object; |
1085 | + size_t track_counter; |
1086 | MetaDataCache meta_data_cache; |
1087 | std::shared_ptr<media::Engine::MetaDataExtractor> extractor; |
1088 | + // Used for caching the original tracklist order to be used to restore the order |
1089 | + // to the live TrackList after shuffle is turned off |
1090 | + media::TrackList::Container original_tracklist; |
1091 | }; |
1092 | |
1093 | media::TrackListImplementation::TrackListImplementation( |
1094 | - const dbus::types::ObjectPath& op, |
1095 | - const std::shared_ptr<media::Engine::MetaDataExtractor>& extractor) |
1096 | - : media::TrackListSkeleton(op), |
1097 | - d(new Private{op, Private::MetaDataCache{}, extractor}) |
1098 | + const dbus::Bus::Ptr& bus, |
1099 | + const dbus::Object::Ptr& object, |
1100 | + const std::shared_ptr<media::Engine::MetaDataExtractor>& extractor, |
1101 | + const media::apparmor::ubuntu::RequestContextResolver::Ptr& request_context_resolver, |
1102 | + const media::apparmor::ubuntu::RequestAuthenticator::Ptr& request_authenticator) |
1103 | + : media::TrackListSkeleton(bus, object, request_context_resolver, request_authenticator), |
1104 | + d(new Private{object, 0, Private::MetaDataCache{}, extractor, media::TrackList::Container{}}) |
1105 | { |
1106 | can_edit_tracks().set(true); |
1107 | } |
1108 | @@ -73,15 +82,16 @@ |
1109 | const media::Track::Id& position, |
1110 | bool make_current) |
1111 | { |
1112 | - static size_t track_counter = 0; |
1113 | + std::cout << __PRETTY_FUNCTION__ << std::endl; |
1114 | |
1115 | - std::stringstream ss; ss << d->path.as_string() << "/" << track_counter++; |
1116 | + std::stringstream ss; ss << d->object->path().as_string() << "/" << d->track_counter++; |
1117 | Track::Id id{ss.str()}; |
1118 | |
1119 | auto result = tracks().update([this, id, position, make_current](TrackList::Container& container) |
1120 | { |
1121 | auto it = std::find(container.begin(), container.end(), position); |
1122 | container.insert(it, id); |
1123 | + |
1124 | return true; |
1125 | }); |
1126 | |
1127 | @@ -98,8 +108,15 @@ |
1128 | } |
1129 | |
1130 | if (make_current) |
1131 | - go_to(id); |
1132 | + { |
1133 | + // Don't automatically call stop() and play() in player_implementation.cpp on_go_to_track() |
1134 | + // since this breaks video playback when using open_uri() (stop() and play() are unwanted in |
1135 | + // this scenario since the qtubuntu-media will handle this automatically) |
1136 | + const bool toggle_player_state = false; |
1137 | + go_to(id, toggle_player_state); |
1138 | + } |
1139 | |
1140 | + // Signal to the client that a track was added to the TrackList |
1141 | on_track_added()(id); |
1142 | } |
1143 | } |
1144 | @@ -121,7 +138,61 @@ |
1145 | |
1146 | } |
1147 | |
1148 | -void media::TrackListImplementation::go_to(const media::Track::Id& track) |
1149 | -{ |
1150 | - (void) track; |
1151 | +void media::TrackListImplementation::go_to(const media::Track::Id& track, bool toggle_player_state) |
1152 | +{ |
1153 | + std::cout << __PRETTY_FUNCTION__ << std::endl; |
1154 | + std::pair<const media::Track::Id, bool> p = std::make_pair(track, toggle_player_state); |
1155 | + // Signal the Player instance to go to a specific track for playback |
1156 | + on_go_to_track()(p); |
1157 | + on_track_changed()(track); |
1158 | +} |
1159 | + |
1160 | +void media::TrackListImplementation::shuffle_tracks() |
1161 | +{ |
1162 | + std::cout << __PRETTY_FUNCTION__ << std::endl; |
1163 | + |
1164 | + auto result = tracks().update([this](TrackList::Container& container) |
1165 | + { |
1166 | + d->original_tracklist.assign(container.begin(), container.end()); |
1167 | + std::random_shuffle(container.begin(), container.end()); |
1168 | + return true; |
1169 | + }); |
1170 | + |
1171 | + if (result) |
1172 | + { |
1173 | + media::TrackList::ContainerTrackIdTuple t{std::make_tuple(tracks().get(), current())}; |
1174 | + on_track_list_replaced()(t); |
1175 | + } |
1176 | +} |
1177 | + |
1178 | +void media::TrackListImplementation::unshuffle_tracks() |
1179 | +{ |
1180 | + std::cout << __PRETTY_FUNCTION__ << std::endl; |
1181 | + |
1182 | + auto result = tracks().update([this](TrackList::Container& container) |
1183 | + { |
1184 | + container.assign(d->original_tracklist.begin(), d->original_tracklist.end()); |
1185 | + return true; |
1186 | + }); |
1187 | + |
1188 | + if (result) |
1189 | + { |
1190 | + media::TrackList::ContainerTrackIdTuple t{std::make_tuple(tracks().get(), current())}; |
1191 | + on_track_list_replaced()(t); |
1192 | + } |
1193 | +} |
1194 | + |
1195 | +void media::TrackListImplementation::reset() |
1196 | +{ |
1197 | + std::cout << __PRETTY_FUNCTION__ << std::endl; |
1198 | + |
1199 | + auto result = tracks().update([this](TrackList::Container& container) |
1200 | + { |
1201 | + container.clear(); |
1202 | + container.resize(0); |
1203 | + d->track_counter = 0; |
1204 | + return true; |
1205 | + }); |
1206 | + |
1207 | + (void) result; |
1208 | } |
1209 | |
1210 | === modified file 'src/core/media/track_list_implementation.h' |
1211 | --- src/core/media/track_list_implementation.h 2014-03-25 14:57:15 +0000 |
1212 | +++ src/core/media/track_list_implementation.h 2015-04-20 17:49:07 +0000 |
1213 | @@ -1,5 +1,5 @@ |
1214 | /* |
1215 | - * Copyright © 2013 Canonical Ltd. |
1216 | + * Copyright © 2013-2015 Canonical Ltd. |
1217 | * |
1218 | * This program is free software: you can redistribute it and/or modify it |
1219 | * under the terms of the GNU Lesser General Public License version 3, |
1220 | @@ -32,8 +32,11 @@ |
1221 | { |
1222 | public: |
1223 | TrackListImplementation( |
1224 | - const core::dbus::types::ObjectPath& op, |
1225 | - const std::shared_ptr<Engine::MetaDataExtractor>& extractor); |
1226 | + const core::dbus::Bus::Ptr& bus, |
1227 | + const core::dbus::Object::Ptr& object, |
1228 | + const std::shared_ptr<Engine::MetaDataExtractor>& extractor, |
1229 | + const core::ubuntu::media::apparmor::ubuntu::RequestContextResolver::Ptr& request_context_resolver, |
1230 | + const core::ubuntu::media::apparmor::ubuntu::RequestAuthenticator::Ptr& request_authenticator); |
1231 | ~TrackListImplementation(); |
1232 | |
1233 | Track::UriType query_uri_for_track(const Track::Id& id); |
1234 | @@ -42,7 +45,10 @@ |
1235 | void add_track_with_uri_at(const Track::UriType& uri, const Track::Id& position, bool make_current); |
1236 | void remove_track(const Track::Id& id); |
1237 | |
1238 | - void go_to(const Track::Id& track); |
1239 | + void go_to(const Track::Id& track, bool toggle_player_state); |
1240 | + void shuffle_tracks(); |
1241 | + void unshuffle_tracks(); |
1242 | + void reset(); |
1243 | |
1244 | private: |
1245 | struct Private; |
1246 | |
1247 | === modified file 'src/core/media/track_list_skeleton.cpp' |
1248 | --- src/core/media/track_list_skeleton.cpp 2014-03-25 14:57:15 +0000 |
1249 | +++ src/core/media/track_list_skeleton.cpp 2015-04-20 17:49:07 +0000 |
1250 | @@ -1,5 +1,5 @@ |
1251 | /* |
1252 | - * Copyright © 2013 Canonical Ltd. |
1253 | + * Copyright © 2015 Canonical Ltd. |
1254 | * |
1255 | * This program is free software: you can redistribute it and/or modify it |
1256 | * under the terms of the GNU Lesser General Public License version 3, |
1257 | @@ -17,6 +17,7 @@ |
1258 | */ |
1259 | |
1260 | #include "track_list_skeleton.h" |
1261 | +#include "track_list_implementation.h" |
1262 | |
1263 | #include <core/media/player.h> |
1264 | #include <core/media/track_list.h> |
1265 | @@ -35,6 +36,7 @@ |
1266 | #include <core/dbus/types/stl/map.h> |
1267 | #include <core/dbus/types/stl/vector.h> |
1268 | |
1269 | +#include <iostream> |
1270 | #include <limits> |
1271 | |
1272 | namespace dbus = core::dbus; |
1273 | @@ -42,14 +44,24 @@ |
1274 | |
1275 | struct media::TrackListSkeleton::Private |
1276 | { |
1277 | - Private(media::TrackListSkeleton* impl, |
1278 | - dbus::Object::Ptr object) |
1279 | + Private(media::TrackListSkeleton* impl, const dbus::Bus::Ptr& bus, const dbus::Object::Ptr& object, |
1280 | + const apparmor::ubuntu::RequestContextResolver::Ptr& request_context_resolver, |
1281 | + const media::apparmor::ubuntu::RequestAuthenticator::Ptr& request_authenticator) |
1282 | : impl(impl), |
1283 | + bus(bus), |
1284 | object(object), |
1285 | - can_edit_tracks(object->get_property<mpris::TrackList::Properties::CanEditTracks>()), |
1286 | - tracks(object->get_property<mpris::TrackList::Properties::Tracks>()), |
1287 | - current_track(tracks->get().begin()), |
1288 | - empty_iterator(tracks->get().begin()) |
1289 | + request_context_resolver(request_context_resolver), |
1290 | + request_authenticator(request_authenticator), |
1291 | + skeleton(mpris::TrackList::Skeleton::Configuration{object, mpris::TrackList::Skeleton::Configuration::Defaults{}}), |
1292 | + current_track(skeleton.properties.tracks->get().begin()), |
1293 | + empty_iterator(skeleton.properties.tracks->get().begin()), |
1294 | + loop_status(media::Player::LoopStatus::none), |
1295 | + signals |
1296 | + { |
1297 | + skeleton.signals.track_added, |
1298 | + skeleton.signals.track_removed, |
1299 | + skeleton.signals.tracklist_replaced |
1300 | + } |
1301 | { |
1302 | } |
1303 | |
1304 | @@ -62,18 +74,29 @@ |
1305 | |
1306 | auto reply = dbus::Message::make_method_return(msg); |
1307 | reply->writer() << *meta_data; |
1308 | - impl->access_bus()->send(reply); |
1309 | + bus->send(reply); |
1310 | } |
1311 | |
1312 | void handle_add_track_with_uri_at(const core::dbus::Message::Ptr& msg) |
1313 | { |
1314 | - Track::UriType uri; media::Track::Id after; bool make_current; |
1315 | - msg->reader() >> uri >> after >> make_current; |
1316 | - |
1317 | - impl->add_track_with_uri_at(uri, after, make_current); |
1318 | - |
1319 | - auto reply = dbus::Message::make_method_return(msg); |
1320 | - impl->access_bus()->send(reply); |
1321 | + request_context_resolver->resolve_context_for_dbus_name_async(msg->sender(), [this, msg](const media::apparmor::ubuntu::Context& context) |
1322 | + { |
1323 | + Track::UriType uri; media::Track::Id after; bool make_current; |
1324 | + msg->reader() >> uri >> after >> make_current; |
1325 | + |
1326 | + // Make sure the client has adequate apparmor permissions to open the URI |
1327 | + const auto result = request_authenticator->authenticate_open_uri_request(context, uri); |
1328 | + |
1329 | + auto reply = dbus::Message::make_method_return(msg); |
1330 | + // Only add the track to the TrackList if it passes the apparmor permissions check |
1331 | + if (std::get<0>(result)) |
1332 | + impl->add_track_with_uri_at(uri, after, make_current); |
1333 | + else |
1334 | + std::cerr << "Warning: Not adding track " << uri << |
1335 | + " to TrackList because of inadequate client apparmor permissions." << std::endl; |
1336 | + |
1337 | + bus->send(reply); |
1338 | + }); |
1339 | } |
1340 | |
1341 | void handle_remove_track(const core::dbus::Message::Ptr& msg) |
1342 | @@ -84,7 +107,7 @@ |
1343 | impl->remove_track(track); |
1344 | |
1345 | auto reply = dbus::Message::make_method_return(msg); |
1346 | - impl->access_bus()->send(reply); |
1347 | + bus->send(reply); |
1348 | } |
1349 | |
1350 | void handle_go_to(const core::dbus::Message::Ptr& msg) |
1351 | @@ -92,30 +115,64 @@ |
1352 | media::Track::Id track; |
1353 | msg->reader() >> track; |
1354 | |
1355 | - impl->go_to(track); |
1356 | + current_track = std::find(skeleton.properties.tracks->get().begin(), skeleton.properties.tracks->get().end(), track); |
1357 | + const bool toggle_player_state = true; |
1358 | + impl->go_to(track, toggle_player_state); |
1359 | |
1360 | auto reply = dbus::Message::make_method_return(msg); |
1361 | - impl->access_bus()->send(reply); |
1362 | + bus->send(reply); |
1363 | } |
1364 | |
1365 | media::TrackListSkeleton* impl; |
1366 | + dbus::Bus::Ptr bus; |
1367 | dbus::Object::Ptr object; |
1368 | + media::apparmor::ubuntu::RequestContextResolver::Ptr request_context_resolver; |
1369 | + media::apparmor::ubuntu::RequestAuthenticator::Ptr request_authenticator; |
1370 | |
1371 | - std::shared_ptr<core::dbus::Property<mpris::TrackList::Properties::CanEditTracks>> can_edit_tracks; |
1372 | - std::shared_ptr<core::dbus::Property<mpris::TrackList::Properties::Tracks>> tracks; |
1373 | + mpris::TrackList::Skeleton skeleton; |
1374 | TrackList::ConstIterator current_track; |
1375 | TrackList::ConstIterator empty_iterator; |
1376 | - |
1377 | - core::Signal<void> on_track_list_replaced; |
1378 | - core::Signal<Track::Id> on_track_added; |
1379 | - core::Signal<Track::Id> on_track_removed; |
1380 | - core::Signal<Track::Id> on_track_changed; |
1381 | + media::Player::LoopStatus loop_status; |
1382 | + |
1383 | + struct Signals |
1384 | + { |
1385 | + typedef core::dbus::Signal<mpris::TrackList::Signals::TrackAdded, mpris::TrackList::Signals::TrackAdded::ArgumentType> DBusTrackAddedSignal; |
1386 | + typedef core::dbus::Signal<mpris::TrackList::Signals::TrackRemoved, mpris::TrackList::Signals::TrackRemoved::ArgumentType> DBusTrackRemovedSignal; |
1387 | + typedef core::dbus::Signal<mpris::TrackList::Signals::TrackListReplaced, mpris::TrackList::Signals::TrackListReplaced::ArgumentType> DBusTrackListReplacedSignal; |
1388 | + |
1389 | + Signals(const std::shared_ptr<DBusTrackAddedSignal>& remote_track_added, |
1390 | + const std::shared_ptr<DBusTrackRemovedSignal>& remote_track_removed, |
1391 | + const std::shared_ptr<DBusTrackListReplacedSignal>& remote_track_list_replaced) |
1392 | + { |
1393 | + // Connect all of the MPRIS interface signals to be emitted over dbus |
1394 | + on_track_added.connect([remote_track_added](const media::Track::Id &id) |
1395 | + { |
1396 | + remote_track_added->emit(id); |
1397 | + }); |
1398 | + |
1399 | + on_track_removed.connect([remote_track_removed](const media::Track::Id &id) |
1400 | + { |
1401 | + remote_track_removed->emit(id); |
1402 | + }); |
1403 | + |
1404 | + on_track_list_replaced.connect([remote_track_list_replaced](const media::TrackList::ContainerTrackIdTuple &tltuple) |
1405 | + { |
1406 | + remote_track_list_replaced->emit(tltuple); |
1407 | + }); |
1408 | + } |
1409 | + |
1410 | + core::Signal<Track::Id> on_track_added; |
1411 | + core::Signal<Track::Id> on_track_removed; |
1412 | + core::Signal<TrackList::ContainerTrackIdTuple> on_track_list_replaced; |
1413 | + core::Signal<Track::Id> on_track_changed; |
1414 | + core::Signal<std::pair<Track::Id, bool>> on_go_to_track; |
1415 | + } signals; |
1416 | }; |
1417 | |
1418 | -media::TrackListSkeleton::TrackListSkeleton( |
1419 | - const dbus::types::ObjectPath& op) |
1420 | - : dbus::Skeleton<media::TrackList>(the_session_bus()), |
1421 | - d(new Private(this, access_service()->add_object_for_path(op))) |
1422 | +media::TrackListSkeleton::TrackListSkeleton(const core::dbus::Bus::Ptr& bus, const core::dbus::Object::Ptr& object, |
1423 | + const media::apparmor::ubuntu::RequestContextResolver::Ptr& request_context_resolver, |
1424 | + const media::apparmor::ubuntu::RequestAuthenticator::Ptr& request_authenticator) |
1425 | + : d(new Private(this, bus, object, request_context_resolver, request_authenticator)) |
1426 | { |
1427 | d->object->install_method_handler<mpris::TrackList::GetTracksMetadata>( |
1428 | std::bind(&Private::handle_get_tracks_metadata, |
1429 | @@ -144,80 +201,156 @@ |
1430 | |
1431 | bool media::TrackListSkeleton::has_next() const |
1432 | { |
1433 | - return d->current_track != d->tracks->get().end(); |
1434 | + const auto next_track = std::next(d->current_track); |
1435 | + std::cout << "has_next track? " << (next_track != tracks().get().end() ? "yes" : "no") << std::endl; |
1436 | + return next_track != tracks().get().end(); |
1437 | } |
1438 | |
1439 | const media::Track::Id& media::TrackListSkeleton::next() |
1440 | { |
1441 | - if (d->tracks->get().empty()) |
1442 | - return *(d->current_track); |
1443 | - |
1444 | - if (d->tracks->get().size() && (d->current_track == d->empty_iterator)) |
1445 | - { |
1446 | - d->current_track = d->tracks->get().begin(); |
1447 | - return *(d->current_track = std::next(d->current_track)); |
1448 | - } |
1449 | - |
1450 | - d->current_track = std::next(d->current_track); |
1451 | + std::cout << __PRETTY_FUNCTION__ << std::endl; |
1452 | + if (tracks().get().empty()) |
1453 | + return *(d->current_track); |
1454 | + |
1455 | + // Loop on the current track forever |
1456 | + if (d->loop_status == media::Player::LoopStatus::track) |
1457 | + { |
1458 | + std::cout << "Looping on the current track..." << std::endl; |
1459 | + return *(d->current_track); |
1460 | + } |
1461 | + // Loop over the whole playlist and repeat |
1462 | + else if (d->loop_status == media::Player::LoopStatus::playlist && !has_next()) |
1463 | + { |
1464 | + std::cout << "Looping on the entire TrackList..." << std::endl; |
1465 | + d->current_track = tracks().get().begin(); |
1466 | + return *(d->current_track); |
1467 | + } |
1468 | + else if (has_next()) |
1469 | + { |
1470 | + // Keep returning the next track until the last track is reached |
1471 | + d->current_track = std::next(d->current_track); |
1472 | + std::cout << *this << std::endl; |
1473 | + } |
1474 | + |
1475 | + return *(d->current_track); |
1476 | +} |
1477 | + |
1478 | +const media::Track::Id& media::TrackListSkeleton::current() |
1479 | +{ |
1480 | + // Prevent the TrackList from sitting at the end which will cause |
1481 | + // a segfault when calling current() |
1482 | + if (tracks().get().size() && (d->current_track == d->empty_iterator)) |
1483 | + d->current_track = d->skeleton.properties.tracks->get().begin(); |
1484 | + else if (tracks().get().empty()) |
1485 | + std::cerr << "TrackList is empty therefore there is no valid current track" << std::endl; |
1486 | + |
1487 | return *(d->current_track); |
1488 | } |
1489 | |
1490 | const core::Property<bool>& media::TrackListSkeleton::can_edit_tracks() const |
1491 | { |
1492 | - return *d->can_edit_tracks; |
1493 | + return *d->skeleton.properties.can_edit_tracks; |
1494 | } |
1495 | |
1496 | core::Property<bool>& media::TrackListSkeleton::can_edit_tracks() |
1497 | { |
1498 | - return *d->can_edit_tracks; |
1499 | + return *d->skeleton.properties.can_edit_tracks; |
1500 | } |
1501 | |
1502 | core::Property<media::TrackList::Container>& media::TrackListSkeleton::tracks() |
1503 | { |
1504 | - return *d->tracks; |
1505 | + return *d->skeleton.properties.tracks; |
1506 | +} |
1507 | + |
1508 | +void media::TrackListSkeleton::on_loop_status_changed(const media::Player::LoopStatus& loop_status) |
1509 | +{ |
1510 | + d->loop_status = loop_status; |
1511 | +} |
1512 | + |
1513 | +media::Player::LoopStatus media::TrackListSkeleton::loop_status() const |
1514 | +{ |
1515 | + return d->loop_status; |
1516 | +} |
1517 | + |
1518 | +void media::TrackListSkeleton::on_shuffle_changed(bool shuffle) |
1519 | +{ |
1520 | + if (shuffle) |
1521 | + shuffle_tracks(); |
1522 | + else |
1523 | + unshuffle_tracks(); |
1524 | } |
1525 | |
1526 | const core::Property<media::TrackList::Container>& media::TrackListSkeleton::tracks() const |
1527 | { |
1528 | - return *d->tracks; |
1529 | + return *d->skeleton.properties.tracks; |
1530 | } |
1531 | |
1532 | -const core::Signal<void>& media::TrackListSkeleton::on_track_list_replaced() const |
1533 | +const core::Signal<media::TrackList::ContainerTrackIdTuple>& media::TrackListSkeleton::on_track_list_replaced() const |
1534 | { |
1535 | - return d->on_track_list_replaced; |
1536 | + // Print the TrackList instance |
1537 | + std::cout << *this << std::endl; |
1538 | + return d->signals.on_track_list_replaced; |
1539 | } |
1540 | |
1541 | const core::Signal<media::Track::Id>& media::TrackListSkeleton::on_track_added() const |
1542 | { |
1543 | - return d->on_track_added; |
1544 | + return d->signals.on_track_added; |
1545 | } |
1546 | |
1547 | const core::Signal<media::Track::Id>& media::TrackListSkeleton::on_track_removed() const |
1548 | { |
1549 | - return d->on_track_removed; |
1550 | + return d->signals.on_track_removed; |
1551 | } |
1552 | |
1553 | const core::Signal<media::Track::Id>& media::TrackListSkeleton::on_track_changed() const |
1554 | { |
1555 | - return d->on_track_changed; |
1556 | -} |
1557 | - |
1558 | -core::Signal<void>& media::TrackListSkeleton::on_track_list_replaced() |
1559 | -{ |
1560 | - return d->on_track_list_replaced; |
1561 | + return d->signals.on_track_changed; |
1562 | +} |
1563 | + |
1564 | +const core::Signal<std::pair<media::Track::Id, bool>>& media::TrackListSkeleton::on_go_to_track() const |
1565 | +{ |
1566 | + return d->signals.on_go_to_track; |
1567 | +} |
1568 | + |
1569 | +core::Signal<media::TrackList::ContainerTrackIdTuple>& media::TrackListSkeleton::on_track_list_replaced() |
1570 | +{ |
1571 | + return d->signals.on_track_list_replaced; |
1572 | } |
1573 | |
1574 | core::Signal<media::Track::Id>& media::TrackListSkeleton::on_track_added() |
1575 | { |
1576 | - return d->on_track_added; |
1577 | + return d->signals.on_track_added; |
1578 | } |
1579 | |
1580 | core::Signal<media::Track::Id>& media::TrackListSkeleton::on_track_removed() |
1581 | { |
1582 | - return d->on_track_removed; |
1583 | + return d->signals.on_track_removed; |
1584 | } |
1585 | |
1586 | core::Signal<media::Track::Id>& media::TrackListSkeleton::on_track_changed() |
1587 | { |
1588 | - return d->on_track_changed; |
1589 | -} |
1590 | + return d->signals.on_track_changed; |
1591 | +} |
1592 | + |
1593 | +core::Signal<std::pair<media::Track::Id, bool>>& media::TrackListSkeleton::on_go_to_track() |
1594 | +{ |
1595 | + return d->signals.on_go_to_track; |
1596 | +} |
1597 | + |
1598 | +// operator<< pretty prints the given TrackList to the given output stream. |
1599 | +inline std::ostream& media::operator<<(std::ostream& out, const media::TrackList& tracklist) |
1600 | +{ |
1601 | + auto non_const_tl = const_cast<media::TrackList*>(&tracklist); |
1602 | + out << "TrackList\n---------------" << std::endl; |
1603 | + for (const media::Track::Id &id : tracklist.tracks().get()) |
1604 | + { |
1605 | + // '*' denotes the current track |
1606 | + out << "\t" << ((dynamic_cast<media::TrackListSkeleton*>(non_const_tl)->current() == id) ? "*" : ""); |
1607 | + out << "Track Id: " << id << std::endl; |
1608 | + out << "\t\turi: " << dynamic_cast<media::TrackListImplementation*>(non_const_tl)->query_uri_for_track(id) << std::endl; |
1609 | + } |
1610 | + |
1611 | + out << "---------------\nEnd TrackList" << std::endl; |
1612 | + return out; |
1613 | +} |
1614 | + |
1615 | |
1616 | === modified file 'src/core/media/track_list_skeleton.h' |
1617 | --- src/core/media/track_list_skeleton.h 2014-03-25 14:57:15 +0000 |
1618 | +++ src/core/media/track_list_skeleton.h 2015-04-20 17:49:07 +0000 |
1619 | @@ -18,10 +18,13 @@ |
1620 | #ifndef CORE_UBUNTU_MEDIA_TRACK_LIST_SKELETON_H_ |
1621 | #define CORE_UBUNTU_MEDIA_TRACK_LIST_SKELETON_H_ |
1622 | |
1623 | +#include "apparmor/ubuntu.h" |
1624 | + |
1625 | #include <core/media/track_list.h> |
1626 | |
1627 | #include <core/media/player.h> |
1628 | |
1629 | +#include <core/dbus/object.h> |
1630 | #include <core/dbus/skeleton.h> |
1631 | |
1632 | namespace core |
1633 | @@ -30,38 +33,52 @@ |
1634 | { |
1635 | namespace media |
1636 | { |
1637 | -class TrackListSkeleton : public core::dbus::Skeleton<core::ubuntu::media::TrackList> |
1638 | +class TrackListSkeleton : public core::ubuntu::media::TrackList |
1639 | { |
1640 | public: |
1641 | - TrackListSkeleton( |
1642 | - const core::dbus::types::ObjectPath& op); |
1643 | + TrackListSkeleton(const core::dbus::Bus::Ptr& bus, const core::dbus::Object::Ptr& object, |
1644 | + const core::ubuntu::media::apparmor::ubuntu::RequestContextResolver::Ptr& request_context_resolver, |
1645 | + const core::ubuntu::media::apparmor::ubuntu::RequestAuthenticator::Ptr& request_authenticator); |
1646 | ~TrackListSkeleton(); |
1647 | |
1648 | bool has_next() const; |
1649 | const Track::Id& next(); |
1650 | + const Track::Id& current(); |
1651 | |
1652 | const core::Property<bool>& can_edit_tracks() const; |
1653 | const core::Property<Container>& tracks() const; |
1654 | |
1655 | - const core::Signal<void>& on_track_list_replaced() const; |
1656 | + const core::Signal<ContainerTrackIdTuple>& on_track_list_replaced() const; |
1657 | const core::Signal<Track::Id>& on_track_added() const; |
1658 | const core::Signal<Track::Id>& on_track_removed() const; |
1659 | const core::Signal<Track::Id>& on_track_changed() const; |
1660 | + const core::Signal<std::pair<Track::Id, bool>>& on_go_to_track() const; |
1661 | + core::Signal<std::pair<Track::Id, bool>>& on_go_to_track(); |
1662 | + core::Signal<Track::Id>& on_track_removed(); |
1663 | |
1664 | core::Property<Container>& tracks(); |
1665 | + void on_loop_status_changed(const core::ubuntu::media::Player::LoopStatus& loop_status); |
1666 | + core::ubuntu::media::Player::LoopStatus loop_status() const; |
1667 | + |
1668 | + /** Gets called when the shuffle property on the Player interface is changed |
1669 | + * by the client */ |
1670 | + void on_shuffle_changed(bool shuffle); |
1671 | |
1672 | protected: |
1673 | core::Property<bool>& can_edit_tracks(); |
1674 | |
1675 | - core::Signal<void>& on_track_list_replaced(); |
1676 | + core::Signal<ContainerTrackIdTuple>& on_track_list_replaced(); |
1677 | core::Signal<Track::Id>& on_track_added(); |
1678 | - core::Signal<Track::Id>& on_track_removed(); |
1679 | core::Signal<Track::Id>& on_track_changed(); |
1680 | |
1681 | private: |
1682 | struct Private; |
1683 | std::unique_ptr<Private> d; |
1684 | }; |
1685 | + |
1686 | +// operator<< pretty prints the given TrackList status to the given output stream. |
1687 | +std::ostream& operator<<(std::ostream& out, const core::ubuntu::media::TrackList& tracklist); |
1688 | + |
1689 | } |
1690 | } |
1691 | } |
1692 | |
1693 | === modified file 'src/core/media/track_list_stub.cpp' |
1694 | --- src/core/media/track_list_stub.cpp 2014-02-12 15:53:57 +0000 |
1695 | +++ src/core/media/track_list_stub.cpp 2015-04-20 17:49:07 +0000 |
1696 | @@ -43,10 +43,10 @@ |
1697 | Private( |
1698 | TrackListStub* impl, |
1699 | const std::shared_ptr<media::Player>& parent, |
1700 | - const dbus::types::ObjectPath& op) |
1701 | + const dbus::Object::Ptr& object) |
1702 | : impl(impl), |
1703 | parent(parent), |
1704 | - object(impl->access_service()->object_for_path(op)), |
1705 | + object(object), |
1706 | can_edit_tracks(object->get_property<mpris::TrackList::Properties::CanEditTracks>()), |
1707 | tracks(object->get_property<mpris::TrackList::Properties::Tracks>()) |
1708 | { |
1709 | @@ -59,17 +59,17 @@ |
1710 | std::shared_ptr<core::dbus::Property<mpris::TrackList::Properties::CanEditTracks>> can_edit_tracks; |
1711 | std::shared_ptr<core::dbus::Property<mpris::TrackList::Properties::Tracks>> tracks; |
1712 | |
1713 | - core::Signal<void> on_track_list_replaced; |
1714 | + core::Signal<media::TrackList::ContainerTrackIdTuple> on_track_list_replaced; |
1715 | core::Signal<Track::Id> on_track_added; |
1716 | core::Signal<Track::Id> on_track_removed; |
1717 | core::Signal<Track::Id> on_track_changed; |
1718 | + core::Signal<std::pair<Track::Id, bool>> on_go_to_track; |
1719 | }; |
1720 | |
1721 | media::TrackListStub::TrackListStub( |
1722 | const std::shared_ptr<media::Player>& parent, |
1723 | - const core::dbus::types::ObjectPath& op) |
1724 | - : dbus::Stub<media::TrackList>(the_session_bus()), |
1725 | - d(new Private(this, parent, op)) |
1726 | + const core::dbus::Object::Ptr& object) |
1727 | + : d(new Private(this, parent, object)) |
1728 | { |
1729 | } |
1730 | |
1731 | @@ -89,8 +89,7 @@ |
1732 | |
1733 | media::Track::MetaData media::TrackListStub::query_meta_data_for_track(const media::Track::Id& id) |
1734 | { |
1735 | - auto op |
1736 | - = d->object->invoke_method_synchronously< |
1737 | + auto op = d->object->invoke_method_synchronously< |
1738 | mpris::TrackList::GetTracksMetadata, |
1739 | std::map<std::string, std::string>>(id); |
1740 | |
1741 | @@ -128,8 +127,9 @@ |
1742 | throw std::runtime_error("Problem removing track: " + op.error()); |
1743 | } |
1744 | |
1745 | -void media::TrackListStub::go_to(const media::Track::Id& track) |
1746 | +void media::TrackListStub::go_to(const media::Track::Id& track, bool toggle_player_state) |
1747 | { |
1748 | + (void) toggle_player_state; |
1749 | auto op = d->object->invoke_method_synchronously<mpris::TrackList::GoTo, void>( |
1750 | track); |
1751 | |
1752 | @@ -137,18 +137,36 @@ |
1753 | throw std::runtime_error("Problem adding track: " + op.error()); |
1754 | } |
1755 | |
1756 | -const core::Signal<void>& media::TrackListStub::on_track_list_replaced() const |
1757 | -{ |
1758 | +void media::TrackListStub::shuffle_tracks() |
1759 | +{ |
1760 | + std::cerr << "shuffle_tracks() does nothing from the client side" << std::endl; |
1761 | +} |
1762 | + |
1763 | +void media::TrackListStub::unshuffle_tracks() |
1764 | +{ |
1765 | + std::cerr << "unshuffle_tracks() does nothing from the client side" << std::endl; |
1766 | +} |
1767 | + |
1768 | +void media::TrackListStub::reset() |
1769 | +{ |
1770 | + std::cerr << "reset() does nothing from the client side" << std::endl; |
1771 | +} |
1772 | + |
1773 | +const core::Signal<media::TrackList::ContainerTrackIdTuple>& media::TrackListStub::on_track_list_replaced() const |
1774 | +{ |
1775 | + std::cout << "Signal on_track_list_replaced arrived via the bus" << std::endl; |
1776 | return d->on_track_list_replaced; |
1777 | } |
1778 | |
1779 | const core::Signal<media::Track::Id>& media::TrackListStub::on_track_added() const |
1780 | { |
1781 | + std::cout << "Signal on_track_added arrived via the bus" << std::endl; |
1782 | return d->on_track_added; |
1783 | } |
1784 | |
1785 | const core::Signal<media::Track::Id>& media::TrackListStub::on_track_removed() const |
1786 | { |
1787 | + std::cout << "Signal on_track_removed arrived via the bus" << std::endl; |
1788 | return d->on_track_removed; |
1789 | } |
1790 | |
1791 | @@ -156,3 +174,8 @@ |
1792 | { |
1793 | return d->on_track_changed; |
1794 | } |
1795 | + |
1796 | +const core::Signal<std::pair<media::Track::Id, bool>>& media::TrackListStub::on_go_to_track() const |
1797 | +{ |
1798 | + return d->on_go_to_track; |
1799 | +} |
1800 | |
1801 | === modified file 'src/core/media/track_list_stub.h' |
1802 | --- src/core/media/track_list_stub.h 2014-02-12 15:53:57 +0000 |
1803 | +++ src/core/media/track_list_stub.h 2015-04-20 17:49:07 +0000 |
1804 | @@ -33,12 +33,12 @@ |
1805 | { |
1806 | namespace media |
1807 | { |
1808 | -class TrackListStub : public core::dbus::Stub<core::ubuntu::media::TrackList> |
1809 | +class TrackListStub : public core::ubuntu::media::TrackList |
1810 | { |
1811 | public: |
1812 | TrackListStub( |
1813 | const std::shared_ptr<Player>& parent, |
1814 | - const core::dbus::types::ObjectPath& op); |
1815 | + const core::dbus::Object::Ptr& object); |
1816 | ~TrackListStub(); |
1817 | |
1818 | const core::Property<bool>& can_edit_tracks() const; |
1819 | @@ -49,12 +49,18 @@ |
1820 | void add_track_with_uri_at(const Track::UriType& uri, const Track::Id& position, bool make_current); |
1821 | void remove_track(const Track::Id& id); |
1822 | |
1823 | - void go_to(const Track::Id& track); |
1824 | - |
1825 | - const core::Signal<void>& on_track_list_replaced() const; |
1826 | + void go_to(const Track::Id& track, bool toggle_player_state); |
1827 | + |
1828 | + void shuffle_tracks(); |
1829 | + void unshuffle_tracks(); |
1830 | + |
1831 | + void reset(); |
1832 | + |
1833 | + const core::Signal<ContainerTrackIdTuple>& on_track_list_replaced() const; |
1834 | const core::Signal<Track::Id>& on_track_added() const; |
1835 | const core::Signal<Track::Id>& on_track_removed() const; |
1836 | const core::Signal<Track::Id>& on_track_changed() const; |
1837 | + const core::Signal<std::pair<Track::Id, bool>>& on_go_to_track() const; |
1838 | |
1839 | private: |
1840 | struct Private; |
1841 | |
1842 | === modified file 'tests/CMakeLists.txt' |
1843 | --- tests/CMakeLists.txt 2014-11-26 11:04:57 +0000 |
1844 | +++ tests/CMakeLists.txt 2015-04-20 17:49:07 +0000 |
1845 | @@ -51,4 +51,5 @@ |
1846 | ) |
1847 | |
1848 | # add_subdirectory(acceptance-tests) |
1849 | +add_subdirectory(test-track-list) |
1850 | add_subdirectory(unit-tests) |
1851 | |
1852 | === added directory 'tests/test-track-list' |
1853 | === added file 'tests/test-track-list/CMakeLists.txt' |
1854 | --- tests/test-track-list/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
1855 | +++ tests/test-track-list/CMakeLists.txt 2015-04-20 17:49:07 +0000 |
1856 | @@ -0,0 +1,17 @@ |
1857 | +include_directories( |
1858 | + ${CMAKE_SOURCE_DIR}/src) |
1859 | + |
1860 | +add_executable( |
1861 | + test_track_list |
1862 | + test_track_list.cpp |
1863 | + ) |
1864 | + |
1865 | +target_link_libraries( |
1866 | + test_track_list |
1867 | + |
1868 | + media-hub-client |
1869 | + |
1870 | + ${CMAKE_THREAD_LIBS_INIT} |
1871 | + ${DBUS_LIBRARIES} |
1872 | + ${PROCESS_CPP_LDFLAGS} |
1873 | +) |
1874 | |
1875 | === added file 'tests/test-track-list/test_track_list.cpp' |
1876 | --- tests/test-track-list/test_track_list.cpp 1970-01-01 00:00:00 +0000 |
1877 | +++ tests/test-track-list/test_track_list.cpp 2015-04-20 17:49:07 +0000 |
1878 | @@ -0,0 +1,346 @@ |
1879 | +/* |
1880 | + * Copyright © 2013-2015 Canonical Ltd. |
1881 | + * |
1882 | + * This program is free software: you can redistribute it and/or modify it |
1883 | + * under the terms of the GNU Lesser General Public License version 3, |
1884 | + * as published by the Free Software Foundation. |
1885 | + * |
1886 | + * This program is distributed in the hope that it will be useful, |
1887 | + * but WITHOUT ANY WARRANTY {} without even the implied warranty of |
1888 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1889 | + * GNU Lesser General Public License for more details. |
1890 | + * |
1891 | + * You should have received a copy of the GNU Lesser General Public License |
1892 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1893 | + * |
1894 | + * Authored by: Jim Hodapp <jim.hodapp@canonical.com> |
1895 | + */ |
1896 | + |
1897 | +#include "test_track_list.h" |
1898 | +#include "../../src/core/media/util/timeout.h" |
1899 | + |
1900 | +#include <core/media/service.h> |
1901 | +#include <core/media/track_list.h> |
1902 | + |
1903 | +#include <cassert> |
1904 | +#include <future> |
1905 | +#include <iostream> |
1906 | +#include <thread> |
1907 | + |
1908 | +namespace media = core::ubuntu::media; |
1909 | +using namespace std; |
1910 | + |
1911 | +media::TestTrackList::TestTrackList() |
1912 | +{ |
1913 | + try { |
1914 | + m_hubService = media::Service::Client::instance(); |
1915 | + } |
1916 | + catch (std::runtime_error &e) { |
1917 | + cerr << "FATAL: Failed to connect to media-hub service: " << e.what() << endl; |
1918 | + } |
1919 | +} |
1920 | + |
1921 | +media::TestTrackList::~TestTrackList() |
1922 | +{ |
1923 | +} |
1924 | + |
1925 | +void media::TestTrackList::create_new_player_session() |
1926 | +{ |
1927 | + try { |
1928 | + m_hubPlayerSession = m_hubService->create_session(media::Player::Client::default_configuration()); |
1929 | + } |
1930 | + catch (std::runtime_error &e) { |
1931 | + cerr << "FATAL: Failed to start a new media-hub player session: " << e.what() << endl; |
1932 | + } |
1933 | + |
1934 | + try { |
1935 | + m_hubTrackList = m_hubPlayerSession->track_list(); |
1936 | + } |
1937 | + catch (std::runtime_error &e) { |
1938 | + cerr << "FATAL: Failed to retrieve the current player's TrackList: " << e.what() << endl; |
1939 | + } |
1940 | +} |
1941 | + |
1942 | +void media::TestTrackList::destroy_player_session() |
1943 | +{ |
1944 | + // TODO: explicitly add a destroy session to the Service class after ricmm lands his new creation_session |
1945 | + // that returns a session ID. This will allow me to clear the tracklist after each test. |
1946 | + m_hubPlayerSession.reset(); |
1947 | +} |
1948 | + |
1949 | +void media::TestTrackList::add_track(const string &uri, bool make_current) |
1950 | +{ |
1951 | + assert (m_hubTrackList.get() != nullptr); |
1952 | + |
1953 | + cout << "Adding " << uri << " to the TrackList for playback." << endl; |
1954 | + |
1955 | + try { |
1956 | + bool can_edit_tracks = m_hubTrackList->can_edit_tracks(); |
1957 | + cout << "can_edit_tracks: " << (can_edit_tracks ? "yes" : "no") << std::endl; |
1958 | + if (can_edit_tracks) |
1959 | + m_hubTrackList->add_track_with_uri_at(uri, media::TrackList::after_empty_track(), make_current); |
1960 | + else |
1961 | + cerr << "Can't add track to TrackList since can_edit_tracks is false" << endl; |
1962 | + } |
1963 | + catch (std::runtime_error &e) { |
1964 | + cerr << "ERROR: Failed to add track " << uri << " to tracklist: " << e.what() << endl; |
1965 | + } |
1966 | +} |
1967 | + |
1968 | +#include <unistd.h> |
1969 | +void media::TestTrackList::test_basic_playback(const std::string &uri1, const std::string &uri2) |
1970 | +{ |
1971 | + cout << "--> Running test: test_basic_playback" << std::endl; |
1972 | + |
1973 | + core::Connection c =m_hubTrackList->on_track_added().connect([](const media::Track::Id &new_id) |
1974 | + { |
1975 | + cout << "Added track to TrackList with Id: " << new_id << endl; |
1976 | + }); |
1977 | + |
1978 | + //create_new_player_session(); |
1979 | + |
1980 | + m_hubPlayerSession->open_uri(uri1); |
1981 | + if (!uri2.empty()) |
1982 | + add_track(uri2); |
1983 | + |
1984 | + //cout << "Waiting for track to be added to TrackList" << endl; |
1985 | + //media::Track::Id id = wait_for_on_track_added(); |
1986 | + |
1987 | + m_hubPlayerSession->play(); |
1988 | + m_hubPlayerSession->loop_status() = media::Player::LoopStatus::none; |
1989 | + |
1990 | + if (m_hubPlayerSession->playback_status() == media::Player::PlaybackStatus::playing) |
1991 | + { |
1992 | + cout << "Waiting for first track to finish playing..." << endl; |
1993 | + wait_for_about_to_finish(); |
1994 | + cout << "Basic playback was successful" << endl; |
1995 | + } |
1996 | + |
1997 | + c.disconnect(); |
1998 | + |
1999 | + //destroy_player_session(); |
2000 | +} |
2001 | + |
2002 | +void media::TestTrackList::test_ensure_tracklist_is_not_empty(const std::string &uri1, const std::string &uri2) |
2003 | +{ |
2004 | + cout << "--> Running test: test_ensure_tracklist_is_not_empty" << std::endl; |
2005 | + |
2006 | + add_track(uri1); |
2007 | + if (!uri2.empty()) |
2008 | + add_track(uri2); |
2009 | + |
2010 | + if (m_hubTrackList->tracks()->size() == 1 or m_hubTrackList->tracks()->size() == 2) |
2011 | + cout << "TrackList is not empty, test success" << endl; |
2012 | + else |
2013 | + cout << "TrackList is empty, test failure" << endl; |
2014 | +} |
2015 | + |
2016 | +void media::TestTrackList::test_has_next_track(const std::string &uri1, const std::string &uri2) |
2017 | +{ |
2018 | + cout << "--> Running test: test_has_next_track" << std::endl; |
2019 | + |
2020 | + //create_new_player_session(); |
2021 | + |
2022 | + add_track(uri1); |
2023 | + add_track(uri2); |
2024 | + |
2025 | + m_hubPlayerSession->play(); |
2026 | + m_hubPlayerSession->loop_status() = media::Player::LoopStatus::none; |
2027 | + |
2028 | + if (m_hubPlayerSession->playback_status() == media::Player::PlaybackStatus::playing) |
2029 | + { |
2030 | + cout << "Waiting for first track to finish playing..." << endl; |
2031 | + wait_for_about_to_finish(); |
2032 | + cout << "Waiting for second track to finish playing..." << endl; |
2033 | + wait_for_about_to_finish(); |
2034 | + cout << "Both tracks played successfully" << endl; |
2035 | + } |
2036 | + else |
2037 | + cerr << "Playback did not start successfully" << endl; |
2038 | + |
2039 | + //destroy_player_session(); |
2040 | +} |
2041 | + |
2042 | +void media::TestTrackList::test_shuffle(const std::string &uri1, const std::string &uri2, const std::string &uri3) |
2043 | +{ |
2044 | + cout << "--> Running test: test_shuffle" << std::endl; |
2045 | + |
2046 | + add_track(uri1); |
2047 | + add_track(uri2); |
2048 | + add_track(uri3); |
2049 | + add_track(uri3); |
2050 | + add_track(uri2); |
2051 | + add_track(uri1); |
2052 | + add_track(uri1); |
2053 | + add_track(uri2); |
2054 | + |
2055 | + m_hubPlayerSession->play(); |
2056 | + m_hubPlayerSession->loop_status() = media::Player::LoopStatus::playlist; |
2057 | + m_hubPlayerSession->shuffle() = true; |
2058 | + |
2059 | + if (m_hubPlayerSession->playback_status() == media::Player::PlaybackStatus::playing) |
2060 | + { |
2061 | + cout << "Waiting for first track to finish playing..." << endl; |
2062 | + wait_for_about_to_finish(); |
2063 | + |
2064 | + cout << "Turning off shuffle mode" << endl; |
2065 | + m_hubPlayerSession->shuffle() = false; |
2066 | + |
2067 | + cout << "Waiting for second track to finish playing..." << endl; |
2068 | + wait_for_about_to_finish(); |
2069 | + |
2070 | + cout << "Going straight to the Track with Id of '/core/ubuntu/media/Service/sessions/0/TrackList/4'" << std::endl; |
2071 | + const media::Track::Id id{"/core/ubuntu/media/Service/sessions/0/TrackList/4"}; |
2072 | + const bool toggle_player_state = true; |
2073 | + m_hubTrackList->go_to(id, toggle_player_state); |
2074 | + cout << "Waiting for third track to finish playing..." << endl; |
2075 | + wait_for_about_to_finish(); |
2076 | + } |
2077 | + else |
2078 | + cerr << "Playback did not start successfully" << endl; |
2079 | +} |
2080 | + |
2081 | +void media::TestTrackList::test_remove_track(const std::string &uri1, const std::string &uri2, const std::string &uri3) |
2082 | +{ |
2083 | + cout << "--> Running test: test_remove_track" << std::endl; |
2084 | + |
2085 | + core::Connection c =m_hubTrackList->on_track_removed().connect([](const media::Track::Id &new_id) |
2086 | + { |
2087 | + cout << "Removed track from TrackList with Id: " << new_id << endl; |
2088 | + }); |
2089 | + |
2090 | + add_track(uri1); |
2091 | + add_track(uri2); |
2092 | + add_track(uri3); |
2093 | + |
2094 | + m_hubPlayerSession->play(); |
2095 | + |
2096 | + if (m_hubPlayerSession->playback_status() == media::Player::PlaybackStatus::playing) |
2097 | + { |
2098 | + cout << "Waiting for first track to finish playing..." << endl; |
2099 | + wait_for_about_to_finish(); |
2100 | + |
2101 | + const media::Track::Id id{"/core/ubuntu/media/Service/sessions/0/TrackList/1"}; |
2102 | + m_hubTrackList->remove_track(id); |
2103 | + |
2104 | + cout << "Waiting for track after removed track to finish playing..." << endl; |
2105 | + wait_for_about_to_finish(); |
2106 | + } |
2107 | + else |
2108 | + cerr << "Playback did not start successfully" << endl; |
2109 | +} |
2110 | + |
2111 | +template<class T> |
2112 | +bool media::TestTrackList::verify_signal_is_emitted(const core::Signal<T> &signal, const std::chrono::milliseconds &timeout) |
2113 | +{ |
2114 | + bool signal_emitted = false; |
2115 | +#if 0 |
2116 | + static const std::chrono::milliseconds timeout{1000}; |
2117 | + media::timeout(timeout.count(), true, [wp]() |
2118 | + { |
2119 | + if (auto sp = wp.lock()) |
2120 | + sp->on_client_died(); |
2121 | + }); |
2122 | + signal.connect([signal_emitted]() |
2123 | + { |
2124 | + }); |
2125 | +#endif |
2126 | + |
2127 | + return false; |
2128 | +} |
2129 | + |
2130 | +void media::TestTrackList::wait_for_about_to_finish() |
2131 | +{ |
2132 | + bool received_about_to_finish = false; |
2133 | + core::Connection c = m_hubPlayerSession->about_to_finish().connect([&received_about_to_finish]() |
2134 | + { |
2135 | + cout << "AboutToFinish signaled" << endl; |
2136 | + received_about_to_finish = true; |
2137 | + }); |
2138 | + while (!received_about_to_finish) |
2139 | + std::this_thread::yield(); |
2140 | + |
2141 | + c.disconnect(); |
2142 | +} |
2143 | + |
2144 | +void media::TestTrackList::wait_for_end_of_stream() |
2145 | +{ |
2146 | + bool reached_end_of_first_track = false; |
2147 | + core::Connection c = m_hubPlayerSession->end_of_stream().connect([&reached_end_of_first_track]() |
2148 | + { |
2149 | + cout << "EndOfStream signaled" << endl; |
2150 | + reached_end_of_first_track = true; |
2151 | + }); |
2152 | + while (!reached_end_of_first_track) |
2153 | + std::this_thread::yield(); |
2154 | + |
2155 | + c.disconnect(); |
2156 | +} |
2157 | + |
2158 | +void media::TestTrackList::wait_for_playback_status_changed(core::ubuntu::media::Player::PlaybackStatus status) |
2159 | +{ |
2160 | + bool received_playback_status_changed = false; |
2161 | + core::Connection c = m_hubPlayerSession->playback_status().changed().connect([&received_playback_status_changed, &status](media::Player::PlaybackStatus new_status) |
2162 | + { |
2163 | + cout << "PlaybackStatusChanged signaled" << endl; |
2164 | + if (new_status == status) |
2165 | + received_playback_status_changed = true; |
2166 | + }); |
2167 | + while (!received_playback_status_changed) |
2168 | + std::this_thread::yield(); |
2169 | + |
2170 | + c.disconnect(); |
2171 | +} |
2172 | + |
2173 | +core::ubuntu::media::Track::Id media::TestTrackList::wait_for_on_track_added() |
2174 | +{ |
2175 | + media::Track::Id id; |
2176 | + bool received_on_track_added = false; |
2177 | + core::Connection c = m_hubTrackList->on_track_added().connect([&received_on_track_added, &id](const media::Track::Id &new_id) |
2178 | + { |
2179 | + cout << "OnTrackAdded signaled" << endl; |
2180 | + id = new_id; |
2181 | + received_on_track_added = true; |
2182 | + }); |
2183 | + while (!received_on_track_added) |
2184 | + std::this_thread::yield(); |
2185 | + |
2186 | + c.disconnect(); |
2187 | + |
2188 | + return id; |
2189 | +} |
2190 | + |
2191 | +int main (int argc, char **argv) |
2192 | +{ |
2193 | + shared_ptr<media::TestTrackList> tracklist = make_shared<media::TestTrackList>(); |
2194 | + |
2195 | + if (argc == 2) |
2196 | + { |
2197 | + tracklist->create_new_player_session(); |
2198 | + tracklist->test_basic_playback(argv[1]); |
2199 | + tracklist->test_ensure_tracklist_is_not_empty(argv[1]); |
2200 | + } |
2201 | + else if (argc == 3) |
2202 | + { |
2203 | + tracklist->create_new_player_session(); |
2204 | + tracklist->test_basic_playback(argv[1], argv[2]); |
2205 | + tracklist->test_ensure_tracklist_is_not_empty(argv[1], argv[2]); |
2206 | + tracklist->test_has_next_track(argv[1], argv[2]); |
2207 | + } |
2208 | + else if (argc == 4) |
2209 | + { |
2210 | + tracklist->create_new_player_session(); |
2211 | + tracklist->test_basic_playback(argv[1]); |
2212 | + tracklist->test_ensure_tracklist_is_not_empty(argv[1], argv[2]); |
2213 | + tracklist->test_has_next_track(argv[1], argv[2]); |
2214 | + tracklist->test_shuffle(argv[1], argv[2], argv[3]); |
2215 | + tracklist->test_remove_track(argv[1], argv[2], argv[3]); |
2216 | + } |
2217 | + else |
2218 | + { |
2219 | + cout << "Can't test TrackList, no track(s) specified." << endl; |
2220 | + cout << argv[0] << " <track_uri_1> [<track_uri_2>] [<track_uri_3>]" << endl; |
2221 | + } |
2222 | + |
2223 | + return 0; |
2224 | +} |
2225 | |
2226 | === added file 'tests/test-track-list/test_track_list.h' |
2227 | --- tests/test-track-list/test_track_list.h 1970-01-01 00:00:00 +0000 |
2228 | +++ tests/test-track-list/test_track_list.h 2015-04-20 17:49:07 +0000 |
2229 | @@ -0,0 +1,86 @@ |
2230 | +/* |
2231 | + * Copyright © 2013-2015 Canonical Ltd. |
2232 | + * |
2233 | + * This program is free software: you can redistribute it and/or modify it |
2234 | + * under the terms of the GNU Lesser General Public License version 3, |
2235 | + * as published by the Free Software Foundation. |
2236 | + * |
2237 | + * This program is distributed in the hope that it will be useful, |
2238 | + * but WITHOUT ANY WARRANTY {} without even the implied warranty of |
2239 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2240 | + * GNU Lesser General Public License for more details. |
2241 | + * |
2242 | + * You should have received a copy of the GNU Lesser General Public License |
2243 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2244 | + * |
2245 | + * Authored by: Jim Hodapp <jim.hodapp@canonical.com> |
2246 | + */ |
2247 | + |
2248 | +#ifndef TEST_TRACK_LIST_H_ |
2249 | +#define TEST_TRACK_LIST_H_ |
2250 | + |
2251 | +#include <core/media/player.h> |
2252 | +#include <core/media/track.h> |
2253 | + |
2254 | +#include <core/dbus/signal.h> |
2255 | + |
2256 | +#include <chrono> |
2257 | +#include <memory> |
2258 | +#include <string> |
2259 | + |
2260 | +namespace core |
2261 | +{ |
2262 | +namespace ubuntu |
2263 | +{ |
2264 | +namespace media |
2265 | +{ |
2266 | + |
2267 | +class Player; |
2268 | +class Service; |
2269 | +class TrackList; |
2270 | + |
2271 | +class TestTrackList |
2272 | +{ |
2273 | +public: |
2274 | + TestTrackList(); |
2275 | + ~TestTrackList(); |
2276 | + |
2277 | + void create_new_player_session(); |
2278 | + void destroy_player_session(); |
2279 | + |
2280 | + void add_track(const std::string &uri, bool make_current = false); |
2281 | + |
2282 | + // Takes in one or two files for playback, adds it/them to the TrackList, and plays |
2283 | + void test_basic_playback(const std::string &uri1, const std::string &uri2 = std::string{}); |
2284 | + |
2285 | + void test_ensure_tracklist_is_not_empty(const std::string &uri1, const std::string &uri2 = std::string{}); |
2286 | + |
2287 | + // Takes in one or two files for playback, adds it/them to the TrackList, plays and makes sure |
2288 | + // that the Player advances the TrackList |
2289 | + void test_has_next_track(const std::string &uri1, const std::string &uri2); |
2290 | + |
2291 | + void test_shuffle(const std::string &uri1, const std::string &uri2, const std::string &uri3); |
2292 | + |
2293 | + void test_remove_track(const std::string &uri1, const std::string &uri2, const std::string &uri3); |
2294 | + |
2295 | +protected: |
2296 | + // Synchronously verify that a signal is emitted waiting up to timeout milliseconds |
2297 | + template<class T> |
2298 | + bool verify_signal_is_emitted(const core::Signal<T> &signal, const std::chrono::milliseconds &timeout); |
2299 | + |
2300 | + void wait_for_about_to_finish(); |
2301 | + void wait_for_end_of_stream(); |
2302 | + void wait_for_playback_status_changed(core::ubuntu::media::Player::PlaybackStatus status); |
2303 | + core::ubuntu::media::Track::Id wait_for_on_track_added(); |
2304 | + |
2305 | +private: |
2306 | + std::shared_ptr<core::ubuntu::media::Service> m_hubService; |
2307 | + std::shared_ptr<core::ubuntu::media::Player> m_hubPlayerSession; |
2308 | + std::shared_ptr<core::ubuntu::media::TrackList> m_hubTrackList; |
2309 | +}; |
2310 | + |
2311 | +} // media |
2312 | +} // ubuntu |
2313 | +} // core |
2314 | + |
2315 | +#endif |
2316 | |
2317 | === modified file 'tests/unit-tests/test-gstreamer-engine.cpp' |
2318 | --- tests/unit-tests/test-gstreamer-engine.cpp 2015-03-03 22:41:22 +0000 |
2319 | +++ tests/unit-tests/test-gstreamer-engine.cpp 2015-04-20 17:49:07 +0000 |
2320 | @@ -103,7 +103,8 @@ |
2321 | std::ref(wst), |
2322 | std::placeholders::_1)); |
2323 | |
2324 | - EXPECT_TRUE(engine.open_resource_for_uri(test_file_uri)); |
2325 | + static const bool do_pipeline_reset = true; |
2326 | + EXPECT_TRUE(engine.open_resource_for_uri(test_file_uri, do_pipeline_reset)); |
2327 | EXPECT_TRUE(engine.play()); |
2328 | EXPECT_TRUE(wst.wait_for_state_for( |
2329 | core::ubuntu::media::Engine::State::playing, |
2330 | @@ -144,7 +145,8 @@ |
2331 | std::ref(wst), |
2332 | std::placeholders::_1)); |
2333 | |
2334 | - EXPECT_TRUE(engine.open_resource_for_uri(test_file_uri)); |
2335 | + static const bool do_pipeline_reset = true; |
2336 | + EXPECT_TRUE(engine.open_resource_for_uri(test_file_uri, do_pipeline_reset)); |
2337 | EXPECT_TRUE(engine.play()); |
2338 | EXPECT_TRUE(wst.wait_for_state_for( |
2339 | core::ubuntu::media::Engine::State::playing, |
2340 | @@ -234,7 +236,8 @@ |
2341 | std::ref(wst), |
2342 | std::placeholders::_1)); |
2343 | |
2344 | - EXPECT_TRUE(engine.open_resource_for_uri(test_file_uri)); |
2345 | + static const bool do_pipeline_reset = true; |
2346 | + EXPECT_TRUE(engine.open_resource_for_uri(test_file_uri, do_pipeline_reset)); |
2347 | EXPECT_TRUE(engine.play()); |
2348 | EXPECT_TRUE(wst.wait_for_state_for( |
2349 | core::ubuntu::media::Engine::State::playing, |
2350 | @@ -282,7 +285,8 @@ |
2351 | std::ref(wst), |
2352 | std::placeholders::_1)); |
2353 | |
2354 | - EXPECT_TRUE(engine.open_resource_for_uri(test_file_uri)); |
2355 | + static const bool do_pipeline_reset = true; |
2356 | + EXPECT_TRUE(engine.open_resource_for_uri(test_file_uri, do_pipeline_reset)); |
2357 | EXPECT_TRUE(engine.play()); |
2358 | EXPECT_TRUE(wst.wait_for_state_for( |
2359 | core::ubuntu::media::Engine::State::playing, |
2360 | @@ -328,7 +332,8 @@ |
2361 | std::ref(wst), |
2362 | std::placeholders::_1)); |
2363 | |
2364 | - EXPECT_TRUE(engine.open_resource_for_uri(test_file_uri)); |
2365 | + static const bool do_pipeline_reset = true; |
2366 | + EXPECT_TRUE(engine.open_resource_for_uri(test_file_uri, do_pipeline_reset)); |
2367 | EXPECT_TRUE(engine.play()); |
2368 | EXPECT_TRUE(wst.wait_for_state_for( |
2369 | core::ubuntu::media::Engine::State::playing, |
2370 | @@ -363,7 +368,8 @@ |
2371 | &core::testing::WaitableStateTransition<core::ubuntu::media::Engine::State>::trigger, |
2372 | std::ref(wst), |
2373 | std::placeholders::_1)); |
2374 | - EXPECT_TRUE(engine.open_resource_for_uri(test_file_uri)); |
2375 | + static const bool do_pipeline_reset = true; |
2376 | + EXPECT_TRUE(engine.open_resource_for_uri(test_file_uri, do_pipeline_reset)); |
2377 | EXPECT_TRUE(engine.play()); |
2378 | EXPECT_TRUE(wst.wait_for_state_for( |
2379 | core::ubuntu::media::Engine::State::playing, |
PASSED: Continuous integration, rev:135 jenkins. qa.ubuntu. com/job/ media-hub- ci/324/ jenkins. qa.ubuntu. com/job/ media-hub- vivid-amd64- ci/164 jenkins. qa.ubuntu. com/job/ media-hub- vivid-armhf- ci/164 jenkins. qa.ubuntu. com/job/ media-hub- vivid-armhf- ci/164/ artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ media-hub- vivid-i386- ci/164
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/media- hub-ci/ 324/rebuild
http://