Merge lp:~alfonsosanchezbeato/media-hub/bg-playlist into lp:media-hub
- bg-playlist
- Merge into trunk
Proposed by
Alfonso Sanchez-Beato
Status: | Merged |
---|---|
Approved by: | Alfonso Sanchez-Beato |
Approved revision: | 154 |
Merged at revision: | 154 |
Proposed branch: | lp:~alfonsosanchezbeato/media-hub/bg-playlist |
Merge into: | lp:media-hub |
Diff against target: |
1225 lines (+452/-213) 14 files modified
include/core/media/track.h (+1/-0) include/core/media/track_list.h (+8/-1) src/core/media/CMakeLists.txt (+1/-1) src/core/media/gstreamer/engine.cpp (+7/-6) src/core/media/mpris/track_list.h (+17/-6) src/core/media/null_track_list.h (+0/-114) src/core/media/player_implementation.cpp (+43/-17) src/core/media/service_skeleton.cpp (+18/-15) src/core/media/track.cpp (+7/-1) src/core/media/track_list_implementation.cpp (+34/-2) src/core/media/track_list_skeleton.cpp (+204/-32) src/core/media/track_list_skeleton.h (+12/-3) src/core/media/track_list_stub.cpp (+98/-15) src/core/media/track_list_stub.h (+2/-0) |
To merge this branch: | bzr merge lp:~alfonsosanchezbeato/media-hub/bg-playlist |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Alfonso Sanchez-Beato | Approve | ||
Review via email: mp+270011@code.launchpad.net |
Commit message
[ Jim Hodapp ]
* Support for background playlists
Description of the change
[ Jim Hodapp ]
* Support for background playlists
To post a comment you must log in.
Revision history for this message
Alfonso Sanchez-Beato (alfonsosanchezbeato) : | # |
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'include/core/media/track.h' |
2 | --- include/core/media/track.h 2014-02-12 16:02:48 +0000 |
3 | +++ include/core/media/track.h 2015-09-03 07:33:12 +0000 |
4 | @@ -107,6 +107,7 @@ |
5 | bool operator==(const Track&) const; |
6 | |
7 | virtual const Id& id() const; |
8 | + virtual const UriType& uri() const; |
9 | /* |
10 | class MetaData |
11 | { |
12 | |
13 | === modified file 'include/core/media/track_list.h' |
14 | --- include/core/media/track_list.h 2015-04-29 21:18:20 +0000 |
15 | +++ include/core/media/track_list.h 2015-09-03 07:33:12 +0000 |
16 | @@ -25,8 +25,9 @@ |
17 | |
18 | #include <functional> |
19 | #include <iosfwd> |
20 | +#include <memory> |
21 | +#include <string> |
22 | #include <vector> |
23 | -#include <memory> |
24 | |
25 | namespace core |
26 | { |
27 | @@ -61,6 +62,9 @@ |
28 | /** Gets all the metadata available for a given Track. */ |
29 | virtual Track::MetaData query_meta_data_for_track(const Track::Id& id) = 0; |
30 | |
31 | + /** Gets the URI for a given Track. */ |
32 | + virtual Track::UriType query_uri_for_track(const Track::Id& id) = 0; |
33 | + |
34 | /** Adds a URI in the TrackList. */ |
35 | virtual void add_track_with_uri_at(const Track::UriType& uri, const Track::Id& position, bool make_current) = 0; |
36 | |
37 | @@ -108,6 +112,9 @@ |
38 | /** Used to notify the Player of when the client requested that the Player should immediately play a new track. */ |
39 | virtual const core::Signal<std::pair<Track::Id, bool>>& on_go_to_track() const = 0; |
40 | |
41 | + /** Used to notify the Player of when the end of the tracklist has been reached. */ |
42 | + virtual const core::Signal<void>& on_end_of_tracklist() const = 0; |
43 | + |
44 | protected: |
45 | TrackList(); |
46 | }; |
47 | |
48 | === modified file 'src/core/media/CMakeLists.txt' |
49 | --- src/core/media/CMakeLists.txt 2015-02-24 15:22:19 +0000 |
50 | +++ src/core/media/CMakeLists.txt 2015-09-03 07:33:12 +0000 |
51 | @@ -88,7 +88,7 @@ |
52 | |
53 | client_death_observer.cpp |
54 | hashed_keyed_player_store.cpp |
55 | - hybris_client_death_observer.cpp |
56 | + hybris_client_death_observer.cpp |
57 | cover_art_resolver.cpp |
58 | engine.cpp |
59 | |
60 | |
61 | === modified file 'src/core/media/gstreamer/engine.cpp' |
62 | --- src/core/media/gstreamer/engine.cpp 2015-06-01 16:30:59 +0000 |
63 | +++ src/core/media/gstreamer/engine.cpp 2015-09-03 07:33:12 +0000 |
64 | @@ -374,7 +374,6 @@ |
65 | |
66 | gstreamer::Engine::Engine() : d(new Private{}) |
67 | { |
68 | - cout << "Creating a new Engine instance in " << __PRETTY_FUNCTION__ << endl; |
69 | d->state = media::Engine::State::no_media; |
70 | } |
71 | |
72 | @@ -413,7 +412,7 @@ |
73 | |
74 | bool gstreamer::Engine::play() |
75 | { |
76 | - auto result = d->playbin.set_state_and_wait(GST_STATE_PLAYING); |
77 | + const auto result = d->playbin.set_state_and_wait(GST_STATE_PLAYING); |
78 | |
79 | if (result) |
80 | { |
81 | @@ -430,10 +429,12 @@ |
82 | { |
83 | // No need to wait, and we can immediately return. |
84 | if (d->state == media::Engine::State::stopped) |
85 | + { |
86 | + std::cerr << "Current player state is already stopped - no need to change state to stopped" << std::endl; |
87 | return true; |
88 | - |
89 | - auto result = d->playbin.set_state_and_wait(GST_STATE_NULL); |
90 | - |
91 | + } |
92 | + |
93 | + const auto result = d->playbin.set_state_and_wait(GST_STATE_NULL); |
94 | if (result) |
95 | { |
96 | d->state = media::Engine::State::stopped; |
97 | @@ -446,7 +447,7 @@ |
98 | |
99 | bool gstreamer::Engine::pause() |
100 | { |
101 | - auto result = d->playbin.set_state_and_wait(GST_STATE_PAUSED); |
102 | + const auto result = d->playbin.set_state_and_wait(GST_STATE_PAUSED); |
103 | |
104 | if (result) |
105 | { |
106 | |
107 | === modified file 'src/core/media/mpris/track_list.h' |
108 | --- src/core/media/mpris/track_list.h 2015-04-14 17:02:09 +0000 |
109 | +++ src/core/media/mpris/track_list.h 2015-09-03 07:33:12 +0000 |
110 | @@ -49,9 +49,11 @@ |
111 | } |
112 | |
113 | DBUS_CPP_METHOD_DEF(GetTracksMetadata, TrackList) |
114 | + DBUS_CPP_METHOD_DEF(GetTracksUri, TrackList) |
115 | DBUS_CPP_METHOD_DEF(AddTrack, TrackList) |
116 | DBUS_CPP_METHOD_DEF(RemoveTrack, TrackList) |
117 | DBUS_CPP_METHOD_DEF(GoTo, TrackList) |
118 | + DBUS_CPP_METHOD_DEF(Reset, TrackList) |
119 | |
120 | struct Signals |
121 | { |
122 | @@ -80,6 +82,13 @@ |
123 | |
124 | DBUS_CPP_SIGNAL_DEF |
125 | ( |
126 | + TrackChanged, |
127 | + TrackList, |
128 | + core::ubuntu::media::Track::Id |
129 | + ) |
130 | + |
131 | + DBUS_CPP_SIGNAL_DEF |
132 | + ( |
133 | TrackMetadataChanged, |
134 | TrackList, |
135 | BOOST_IDENTITY_TYPE((std::tuple<std::map<std::string, dbus::types::Variant>, dbus::types::ObjectPath>)) |
136 | @@ -118,15 +127,16 @@ |
137 | : configuration(configuration), |
138 | properties |
139 | { |
140 | - configuration.object->get_property<Properties::Tracks>(), |
141 | - configuration.object->get_property<Properties::CanEditTracks>(), |
142 | + configuration.object->template get_property<Properties::Tracks>(), |
143 | + configuration.object->template get_property<Properties::CanEditTracks>(), |
144 | }, |
145 | signals |
146 | { |
147 | - configuration.object->get_signal<Signals::TrackListReplaced>(), |
148 | - configuration.object->get_signal<Signals::TrackAdded>(), |
149 | - configuration.object->get_signal<Signals::TrackRemoved>(), |
150 | - configuration.object->get_signal<Signals::TrackMetadataChanged>(), |
151 | + configuration.object->template get_signal<Signals::TrackListReplaced>(), |
152 | + configuration.object->template get_signal<Signals::TrackAdded>(), |
153 | + configuration.object->template get_signal<Signals::TrackRemoved>(), |
154 | + configuration.object->template get_signal<Signals::TrackChanged>(), |
155 | + configuration.object->template get_signal<Signals::TrackMetadataChanged>(), |
156 | configuration.object->template get_signal<core::dbus::interfaces::Properties::Signals::PropertiesChanged>() |
157 | } |
158 | { |
159 | @@ -169,6 +179,7 @@ |
160 | core::dbus::Signal<Signals::TrackListReplaced, Signals::TrackListReplaced::ArgumentType>::Ptr tracklist_replaced; |
161 | core::dbus::Signal<Signals::TrackAdded, Signals::TrackAdded::ArgumentType>::Ptr track_added; |
162 | core::dbus::Signal<Signals::TrackRemoved, Signals::TrackRemoved::ArgumentType>::Ptr track_removed; |
163 | + core::dbus::Signal<Signals::TrackChanged, Signals::TrackChanged::ArgumentType>::Ptr track_changed; |
164 | core::dbus::Signal<Signals::TrackMetadataChanged, Signals::TrackMetadataChanged::ArgumentType>::Ptr track_metadata_changed; |
165 | |
166 | dbus::Signal <core::dbus::interfaces::Properties::Signals::PropertiesChanged, |
167 | |
168 | === removed file 'src/core/media/null_track_list.h' |
169 | --- src/core/media/null_track_list.h 2014-12-15 21:02:14 +0000 |
170 | +++ src/core/media/null_track_list.h 1970-01-01 00:00:00 +0000 |
171 | @@ -1,114 +0,0 @@ |
172 | -/* |
173 | - * |
174 | - * This program is free software: you can redistribute it and/or modify it |
175 | - * under the terms of the GNU Lesser General Public License version 3, |
176 | - * as published by the Free Software Foundation. |
177 | - * |
178 | - * This program is distributed in the hope that it will be useful, |
179 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
180 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
181 | - * GNU Lesser General Public License for more details. |
182 | - * |
183 | - * You should have received a copy of the GNU Lesser General Public License |
184 | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
185 | - * |
186 | - * Authored by: Thomas Voß <thomas.voss@canonical.com> |
187 | - */ |
188 | - |
189 | -#ifndef CORE_MEDIA_NULL_TRACK_LIST_H_ |
190 | -#define CORE_MEDIA_NULL_TRACK_LIST_H_ |
191 | - |
192 | -#include <core/media/track.h> |
193 | -#include <core/media/track_list.h> |
194 | - |
195 | -namespace core |
196 | -{ |
197 | -namespace ubuntu |
198 | -{ |
199 | -namespace media |
200 | -{ |
201 | -// A helper type to replace the playlist implementation below. |
202 | -// Please note that this type is only a temporary manner. Ideally, |
203 | -// the actual implementation should be injected as a dependency from the |
204 | -// outside. |
205 | -struct NullTrackList : public media::TrackList |
206 | -{ |
207 | - NullTrackList() = default; |
208 | - |
209 | - bool has_next() |
210 | - { |
211 | - return false; |
212 | - } |
213 | - |
214 | - media::Track::Id next() |
215 | - { |
216 | - return media::Track::Id{}; |
217 | - } |
218 | - |
219 | - media::Track::UriType query_uri_for_track(const media::Track::Id&) |
220 | - { |
221 | - return media::Track::UriType{}; |
222 | - } |
223 | - |
224 | - const core::Property<bool>& can_edit_tracks() const override |
225 | - { |
226 | - return props_and_sigs.can_edit_tracks; |
227 | - } |
228 | - |
229 | - const core::Property<Container>& tracks() const override |
230 | - { |
231 | - return props_and_sigs.tracks; |
232 | - } |
233 | - |
234 | - virtual media::Track::MetaData query_meta_data_for_track(const media::Track::Id&) override |
235 | - { |
236 | - return media::Track::MetaData{}; |
237 | - } |
238 | - |
239 | - void add_track_with_uri_at(const media::Track::UriType&, const media::Track::Id&, bool) override |
240 | - { |
241 | - } |
242 | - |
243 | - void remove_track(const media::Track::Id&) override |
244 | - { |
245 | - } |
246 | - |
247 | - void go_to(const media::Track::Id&) override |
248 | - { |
249 | - } |
250 | - |
251 | - const core::Signal<void>& on_track_list_replaced() const override |
252 | - { |
253 | - return props_and_sigs.on_track_list_replaced; |
254 | - } |
255 | - |
256 | - const core::Signal<media::Track::Id>& on_track_added() const override |
257 | - { |
258 | - return props_and_sigs.on_track_added; |
259 | - } |
260 | - |
261 | - const core::Signal<media::Track::Id>& on_track_removed() const override |
262 | - { |
263 | - return props_and_sigs.on_track_removed; |
264 | - } |
265 | - |
266 | - const core::Signal<media::Track::Id>& on_track_changed() const override |
267 | - { |
268 | - return props_and_sigs.on_track_changed; |
269 | - } |
270 | - |
271 | - struct |
272 | - { |
273 | - core::Property<bool> can_edit_tracks; |
274 | - core::Property<TrackList::Container> tracks; |
275 | - core::Signal<void> on_track_list_replaced; |
276 | - core::Signal<media::Track::Id> on_track_added; |
277 | - core::Signal<media::Track::Id> on_track_removed; |
278 | - core::Signal<media::Track::Id> on_track_changed; |
279 | - } props_and_sigs; |
280 | -}; |
281 | -} |
282 | -} |
283 | -} |
284 | - |
285 | -#endif // CORE_MEDIA_NULL_TRACK_LIST_H_ |
286 | |
287 | === modified file 'src/core/media/player_implementation.cpp' |
288 | --- src/core/media/player_implementation.cpp 2015-06-01 16:31:50 +0000 |
289 | +++ src/core/media/player_implementation.cpp 2015-09-03 07:33:12 +0000 |
290 | @@ -287,6 +287,20 @@ |
291 | engine->reset(); |
292 | } |
293 | |
294 | + void open_first_track_from_tracklist(const media::Track::Id& id) |
295 | + { |
296 | + const Track::UriType uri = track_list->query_uri_for_track(id); |
297 | + if (!uri.empty()) |
298 | + { |
299 | + // Using a TrackList for playback, added tracks via add_track(), but open_uri hasn't been called yet |
300 | + // to load a media resource |
301 | + std::cout << "Calling d->engine->open_resource_for_uri() for first track added only: " << uri << std::endl; |
302 | + std::cout << "\twith a Track::Id: " << id << std::endl; |
303 | + static const bool do_pipeline_reset = false; |
304 | + engine->open_resource_for_uri(uri, do_pipeline_reset); |
305 | + } |
306 | + } |
307 | + |
308 | // Our link back to our parent. |
309 | media::PlayerImplementation<Parent>* parent; |
310 | // We just store the parameters passed on construction. |
311 | @@ -375,6 +389,7 @@ |
312 | // When the client changes the loop status, make sure to update the TrackList |
313 | Parent::loop_status().changed().connect([this](media::Player::LoopStatus loop_status) |
314 | { |
315 | + std::cout << "LoopStatus: " << loop_status << std::endl; |
316 | d->track_list->on_loop_status_changed(loop_status); |
317 | }); |
318 | |
319 | @@ -408,18 +423,20 @@ |
320 | if (d->doing_abandon) |
321 | return; |
322 | |
323 | + // Prevent on_go_to_track from executing as it's not needed in this case. on_go_to_track |
324 | + // (see the lambda below) is only needed when the client explicitly calls next() not during |
325 | + // the about_to_finish condition |
326 | + d->doing_go_to_track.lock(); |
327 | + |
328 | Parent::about_to_finish()(); |
329 | |
330 | - // This lambda needs to be mutually exclusive with the on_go_to_track lambda below |
331 | - d->doing_go_to_track.lock(); |
332 | - |
333 | const media::Track::Id prev_track_id = d->track_list->current(); |
334 | // Make sure that the TrackList keeps advancing. The logic for what gets played next, |
335 | // if anything at all, occurs in TrackListSkeleton::next() |
336 | const Track::UriType uri = d->track_list->query_uri_for_track(d->track_list->next()); |
337 | if (prev_track_id != d->track_list->current() && !uri.empty()) |
338 | { |
339 | - std::cout << "Setting next track on playbin: " << uri << std::endl; |
340 | + std::cout << "Advancing to next track on playbin: " << uri << std::endl; |
341 | static const bool do_pipeline_reset = false; |
342 | d->engine->open_resource_for_uri(uri, do_pipeline_reset); |
343 | } |
344 | @@ -457,12 +474,25 @@ |
345 | Parent::error()(e); |
346 | }); |
347 | |
348 | + d->track_list->on_end_of_tracklist().connect([this]() |
349 | + { |
350 | + if (d->engine->state() != gstreamer::Engine::State::ready |
351 | + && d->engine->state() != gstreamer::Engine::State::stopped) |
352 | + { |
353 | + std::cout << "End of tracklist reached, stopping playback" << std::endl; |
354 | + d->engine->stop(); |
355 | + } |
356 | + }); |
357 | + |
358 | + |
359 | d->track_list->on_go_to_track().connect([this](std::pair<const media::Track::Id, bool> p) |
360 | { |
361 | - // This prevents the TrackList from auto advancing in other areas such as the about_to_finish signal |
362 | - // handler. |
363 | // This lambda needs to be mutually exclusive with the about_to_finish lambda above |
364 | - d->doing_go_to_track.lock(); |
365 | + const bool locked = d->doing_go_to_track.try_lock(); |
366 | + // If the try_lock fails, it means that about_to_finish lambda above has it locked and it will |
367 | + // call d->engine->open_resource_for_uri() |
368 | + if (!locked) |
369 | + return; |
370 | |
371 | const media::Track::Id id = p.first; |
372 | const bool toggle_player_state = p.second; |
373 | @@ -483,7 +513,13 @@ |
374 | d->engine->play(); |
375 | |
376 | d->doing_go_to_track.unlock(); |
377 | + }); |
378 | |
379 | + d->track_list->on_track_added().connect([this](const media::Track::Id& id) |
380 | + { |
381 | + std::cout << "** Track was added, handling in PlayerImplementation" << std::endl; |
382 | + if (d->track_list->tracks()->size() == 1) |
383 | + d->open_first_track_from_tracklist(id); |
384 | }); |
385 | |
386 | // Everything is setup, we now subscribe to death notifications. |
387 | @@ -614,16 +650,6 @@ |
388 | template<typename Parent> |
389 | void media::PlayerImplementation<Parent>::play() |
390 | { |
391 | - if (d->track_list != nullptr && d->track_list->tracks()->size() > 0 && d->engine->state() == media::Engine::State::no_media) |
392 | - { |
393 | - // Using a TrackList for playback, added tracks via add_track(), but open_uri hasn't been called yet |
394 | - // to load a media resource |
395 | - std::cout << "No media loaded yet, calling open_uri on first track in track_list" << std::endl; |
396 | - static const bool do_pipeline_reset = true; |
397 | - d->engine->open_resource_for_uri(d->track_list->query_uri_for_track(d->track_list->current()), do_pipeline_reset); |
398 | - std::cout << *d->track_list << endl; |
399 | - } |
400 | - |
401 | d->engine->play(); |
402 | } |
403 | |
404 | |
405 | === modified file 'src/core/media/service_skeleton.cpp' |
406 | --- src/core/media/service_skeleton.cpp 2015-04-27 21:25:03 +0000 |
407 | +++ src/core/media/service_skeleton.cpp 2015-09-03 07:33:12 +0000 |
408 | @@ -139,7 +139,7 @@ |
409 | fprintf(stderr, "%s():%d -- app_name='%s', attached\n", __func__, __LINE__, context.str().c_str()); |
410 | player_owner_map.insert(std::make_pair(key, std::make_tuple(context.str(), true, msg->sender()))); |
411 | }); |
412 | - |
413 | + |
414 | auto reply = dbus::Message::make_method_return(msg); |
415 | reply->writer() << std::make_tuple(op, uuid); |
416 | |
417 | @@ -161,20 +161,24 @@ |
418 | std::string uuid; |
419 | msg->reader() >> uuid; |
420 | |
421 | - auto key = uuid_player_map.at(uuid); |
422 | + // Make sure we don't try to do a lookup if the map is empty |
423 | + if (!uuid_player_map.empty()) |
424 | + { |
425 | + const auto key = uuid_player_map.at(uuid); |
426 | |
427 | - if (player_owner_map.count(key) != 0) { |
428 | - auto info = player_owner_map.at(key); |
429 | - // Check if session is attached(1) and that the detachment |
430 | - // request comes from the same peer(2) that created the session. |
431 | - if (std::get<1>(info) && (std::get<2>(info) == msg->sender())) { // Player is attached |
432 | - std::get<1>(info) = false; // Detached |
433 | - std::get<2>(info).clear(); // Clear registered sender/peer |
434 | - auto player = configuration.player_store->player_for_key(key); |
435 | - player->lifetime().set(media::Player::Lifetime::resumable); |
436 | + if (player_owner_map.count(key) != 0) { |
437 | + auto info = player_owner_map.at(key); |
438 | + // Check if session is attached(1) and that the detachment |
439 | + // request comes from the same peer(2) that created the session. |
440 | + if (std::get<1>(info) && (std::get<2>(info) == msg->sender())) { // Player is attached |
441 | + std::get<1>(info) = false; // Detached |
442 | + std::get<2>(info).clear(); // Clear registered sender/peer |
443 | + auto player = configuration.player_store->player_for_key(key); |
444 | + player->lifetime().set(media::Player::Lifetime::resumable); |
445 | + } |
446 | } |
447 | } |
448 | - |
449 | + |
450 | auto reply = dbus::Message::make_method_return(msg); |
451 | impl->access_bus()->send(reply); |
452 | |
453 | @@ -256,7 +260,6 @@ |
454 | |
455 | void handle_destroy_session(const core::dbus::Message::Ptr& msg) |
456 | { |
457 | - |
458 | try |
459 | { |
460 | std::string uuid; |
461 | @@ -445,9 +448,9 @@ |
462 | // We keep a list of keys and their respective owners and states. |
463 | // value: (owner context, attached state, attached dbus name) |
464 | std::map<media::Player::PlayerKey, std::tuple<std::string, bool, std::string>> player_owner_map; |
465 | - |
466 | + |
467 | boost::uuids::random_generator gen; |
468 | - |
469 | + |
470 | // We expose the entire service as an MPRIS player. |
471 | struct Exported |
472 | { |
473 | |
474 | === modified file 'src/core/media/track.cpp' |
475 | --- src/core/media/track.cpp 2014-02-12 15:53:57 +0000 |
476 | +++ src/core/media/track.cpp 2015-09-03 07:33:12 +0000 |
477 | @@ -25,9 +25,10 @@ |
478 | struct media::Track::Private |
479 | { |
480 | media::Track::Id id; |
481 | + media::Track::UriType uri; |
482 | }; |
483 | |
484 | -media::Track::Track(const media::Track::Id& id) : d(new Private{id}) |
485 | +media::Track::Track(const media::Track::Id& id) : d(new Private{id, std::string{}}) |
486 | { |
487 | } |
488 | |
489 | @@ -39,3 +40,8 @@ |
490 | { |
491 | return d->id; |
492 | } |
493 | + |
494 | +const media::Track::UriType& media::Track::uri() const |
495 | +{ |
496 | + return d->uri; |
497 | +} |
498 | |
499 | === modified file 'src/core/media/track_list_implementation.cpp' |
500 | --- src/core/media/track_list_implementation.cpp 2015-05-22 21:19:28 +0000 |
501 | +++ src/core/media/track_list_implementation.cpp 2015-09-03 07:33:12 +0000 |
502 | @@ -59,7 +59,7 @@ |
503 | |
504 | media::Track::UriType media::TrackListImplementation::query_uri_for_track(const media::Track::Id& id) |
505 | { |
506 | - auto it = d->meta_data_cache.find(id); |
507 | + const auto it = d->meta_data_cache.find(id); |
508 | |
509 | if (it == d->meta_data_cache.end()) |
510 | return Track::UriType{}; |
511 | @@ -69,7 +69,7 @@ |
512 | |
513 | media::Track::MetaData media::TrackListImplementation::query_meta_data_for_track(const media::Track::Id& id) |
514 | { |
515 | - auto it = d->meta_data_cache.find(id); |
516 | + const auto it = d->meta_data_cache.find(id); |
517 | |
518 | if (it == d->meta_data_cache.end()) |
519 | return Track::MetaData{}; |
520 | @@ -87,10 +87,14 @@ |
521 | std::stringstream ss; ss << d->object->path().as_string() << "/" << d->track_counter++; |
522 | Track::Id id{ss.str()}; |
523 | |
524 | + std::cout << "Adding Track::Id: " << id << std::endl; |
525 | + std::cout << "\tURI: " << uri << std::endl; |
526 | + |
527 | auto result = tracks().update([this, id, position, make_current](TrackList::Container& container) |
528 | { |
529 | auto it = std::find(container.begin(), container.end(), position); |
530 | container.insert(it, id); |
531 | + std::cout << "container.size(): " << container.size() << std::endl; |
532 | |
533 | return true; |
534 | }); |
535 | @@ -99,6 +103,8 @@ |
536 | { |
537 | if (d->meta_data_cache.count(id) == 0) |
538 | { |
539 | + // FIXME: This code seems to conflict badly when called multiple times in a row: causes segfaults |
540 | +#if 0 |
541 | try { |
542 | d->meta_data_cache[id] = std::make_tuple( |
543 | uri, |
544 | @@ -106,6 +112,11 @@ |
545 | } catch (const std::runtime_error &e) { |
546 | std::cerr << "Failed to retrieve metadata for track '" << uri << "' (" << e.what() << ")" << std::endl; |
547 | } |
548 | +#else |
549 | + d->meta_data_cache[id] = std::make_tuple( |
550 | + uri, |
551 | + core::ubuntu::media::Track::MetaData{}); |
552 | +#endif |
553 | } else |
554 | { |
555 | std::get<0>(d->meta_data_cache[id]) = uri; |
556 | @@ -120,8 +131,14 @@ |
557 | go_to(id, toggle_player_state); |
558 | } |
559 | |
560 | + // Signal to the client that the current track has changed for the first track added to the TrackList |
561 | + if (tracks().get().size() == 1) |
562 | + on_track_changed()(id); |
563 | + |
564 | + std::cout << "Signaling that we just added track id: " << id << std::endl; |
565 | // Signal to the client that a track was added to the TrackList |
566 | on_track_added()(id); |
567 | + std::cout << "Signaled that we just added track id: " << id << std::endl; |
568 | } |
569 | } |
570 | |
571 | @@ -133,6 +150,8 @@ |
572 | return true; |
573 | }); |
574 | |
575 | + reset_current_iterator_if_needed(); |
576 | + |
577 | if (result) |
578 | { |
579 | d->meta_data_cache.erase(id); |
580 | @@ -155,8 +174,12 @@ |
581 | { |
582 | std::cout << __PRETTY_FUNCTION__ << std::endl; |
583 | |
584 | + if (tracks().get().empty()) |
585 | + return; |
586 | + |
587 | auto result = tracks().update([this](TrackList::Container& container) |
588 | { |
589 | + // Save off the original TrackList ordering |
590 | d->original_tracklist.assign(container.begin(), container.end()); |
591 | std::random_shuffle(container.begin(), container.end()); |
592 | return true; |
593 | @@ -173,8 +196,12 @@ |
594 | { |
595 | std::cout << __PRETTY_FUNCTION__ << std::endl; |
596 | |
597 | + if (tracks().get().empty() or d->original_tracklist.empty()) |
598 | + return; |
599 | + |
600 | auto result = tracks().update([this](TrackList::Container& container) |
601 | { |
602 | + // Restore the original TrackList ordering |
603 | container.assign(d->original_tracklist.begin(), d->original_tracklist.end()); |
604 | return true; |
605 | }); |
606 | @@ -190,6 +217,9 @@ |
607 | { |
608 | std::cout << __PRETTY_FUNCTION__ << std::endl; |
609 | |
610 | + // Make sure playback stops |
611 | + on_end_of_tracklist()(); |
612 | + |
613 | auto result = tracks().update([this](TrackList::Container& container) |
614 | { |
615 | container.clear(); |
616 | @@ -198,5 +228,7 @@ |
617 | return true; |
618 | }); |
619 | |
620 | + media::TrackListSkeleton::reset(); |
621 | + |
622 | (void) result; |
623 | } |
624 | |
625 | === modified file 'src/core/media/track_list_skeleton.cpp' |
626 | --- src/core/media/track_list_skeleton.cpp 2015-04-29 21:18:20 +0000 |
627 | +++ src/core/media/track_list_skeleton.cpp 2015-09-03 07:33:12 +0000 |
628 | @@ -60,6 +60,7 @@ |
629 | { |
630 | skeleton.signals.track_added, |
631 | skeleton.signals.track_removed, |
632 | + skeleton.signals.track_changed, |
633 | skeleton.signals.tracklist_replaced |
634 | } |
635 | { |
636 | @@ -70,15 +71,28 @@ |
637 | media::Track::Id track; |
638 | msg->reader() >> track; |
639 | |
640 | - auto meta_data = impl->query_meta_data_for_track(track); |
641 | + const auto meta_data = impl->query_meta_data_for_track(track); |
642 | |
643 | - auto reply = dbus::Message::make_method_return(msg); |
644 | + const auto reply = dbus::Message::make_method_return(msg); |
645 | reply->writer() << *meta_data; |
646 | bus->send(reply); |
647 | } |
648 | |
649 | + void handle_get_tracks_uri(const core::dbus::Message::Ptr& msg) |
650 | + { |
651 | + media::Track::Id track; |
652 | + msg->reader() >> track; |
653 | + |
654 | + const auto uri = impl->query_uri_for_track(track); |
655 | + |
656 | + const auto reply = dbus::Message::make_method_return(msg); |
657 | + reply->writer() << uri; |
658 | + bus->send(reply); |
659 | + } |
660 | + |
661 | void handle_add_track_with_uri_at(const core::dbus::Message::Ptr& msg) |
662 | { |
663 | + std::cout << "*** " << __PRETTY_FUNCTION__ << std::endl; |
664 | request_context_resolver->resolve_context_for_dbus_name_async(msg->sender(), [this, msg](const media::apparmor::ubuntu::Context& context) |
665 | { |
666 | Track::UriType uri; media::Track::Id after; bool make_current; |
667 | @@ -123,6 +137,14 @@ |
668 | bus->send(reply); |
669 | } |
670 | |
671 | + void handle_reset(const core::dbus::Message::Ptr& msg) |
672 | + { |
673 | + impl->reset(); |
674 | + |
675 | + auto reply = dbus::Message::make_method_return(msg); |
676 | + bus->send(reply); |
677 | + } |
678 | + |
679 | media::TrackListSkeleton* impl; |
680 | dbus::Bus::Ptr bus; |
681 | dbus::Object::Ptr object; |
682 | @@ -138,10 +160,12 @@ |
683 | { |
684 | typedef core::dbus::Signal<mpris::TrackList::Signals::TrackAdded, mpris::TrackList::Signals::TrackAdded::ArgumentType> DBusTrackAddedSignal; |
685 | typedef core::dbus::Signal<mpris::TrackList::Signals::TrackRemoved, mpris::TrackList::Signals::TrackRemoved::ArgumentType> DBusTrackRemovedSignal; |
686 | + typedef core::dbus::Signal<mpris::TrackList::Signals::TrackChanged, mpris::TrackList::Signals::TrackChanged::ArgumentType> DBusTrackChangedSignal; |
687 | typedef core::dbus::Signal<mpris::TrackList::Signals::TrackListReplaced, mpris::TrackList::Signals::TrackListReplaced::ArgumentType> DBusTrackListReplacedSignal; |
688 | |
689 | Signals(const std::shared_ptr<DBusTrackAddedSignal>& remote_track_added, |
690 | const std::shared_ptr<DBusTrackRemovedSignal>& remote_track_removed, |
691 | + const std::shared_ptr<DBusTrackChangedSignal>& remote_track_changed, |
692 | const std::shared_ptr<DBusTrackListReplacedSignal>& remote_track_list_replaced) |
693 | { |
694 | // Connect all of the MPRIS interface signals to be emitted over dbus |
695 | @@ -155,6 +179,11 @@ |
696 | remote_track_removed->emit(id); |
697 | }); |
698 | |
699 | + on_track_changed.connect([remote_track_changed](const media::Track::Id &id) |
700 | + { |
701 | + remote_track_changed->emit(id); |
702 | + }); |
703 | + |
704 | on_track_list_replaced.connect([remote_track_list_replaced](const media::TrackList::ContainerTrackIdTuple &tltuple) |
705 | { |
706 | remote_track_list_replaced->emit(tltuple); |
707 | @@ -163,9 +192,10 @@ |
708 | |
709 | core::Signal<Track::Id> on_track_added; |
710 | core::Signal<Track::Id> on_track_removed; |
711 | + core::Signal<Track::Id> on_track_changed; |
712 | core::Signal<TrackList::ContainerTrackIdTuple> on_track_list_replaced; |
713 | - core::Signal<Track::Id> on_track_changed; |
714 | core::Signal<std::pair<Track::Id, bool>> on_go_to_track; |
715 | + core::Signal<void> on_end_of_tracklist; |
716 | } signals; |
717 | }; |
718 | |
719 | @@ -179,6 +209,11 @@ |
720 | std::ref(d), |
721 | std::placeholders::_1)); |
722 | |
723 | + d->object->install_method_handler<mpris::TrackList::GetTracksUri>( |
724 | + std::bind(&Private::handle_get_tracks_uri, |
725 | + std::ref(d), |
726 | + std::placeholders::_1)); |
727 | + |
728 | d->object->install_method_handler<mpris::TrackList::AddTrack>( |
729 | std::bind(&Private::handle_add_track_with_uri_at, |
730 | std::ref(d), |
731 | @@ -193,74 +228,184 @@ |
732 | std::bind(&Private::handle_go_to, |
733 | std::ref(d), |
734 | std::placeholders::_1)); |
735 | + |
736 | + d->object->install_method_handler<mpris::TrackList::Reset>( |
737 | + std::bind(&Private::handle_reset, |
738 | + std::ref(d), |
739 | + std::placeholders::_1)); |
740 | } |
741 | |
742 | media::TrackListSkeleton::~TrackListSkeleton() |
743 | { |
744 | } |
745 | |
746 | -bool media::TrackListSkeleton::has_next() const |
747 | +bool media::TrackListSkeleton::has_next() |
748 | { |
749 | - const auto next_track = std::next(d->current_track); |
750 | - std::cout << "has_next track? " << (next_track != tracks().get().end() ? "yes" : "no") << std::endl; |
751 | - return next_track != tracks().get().end(); |
752 | + if (tracks().get().empty()) |
753 | + return false; |
754 | + |
755 | + const auto next_track = std::next(current_iterator()); |
756 | + return !is_last_track(next_track); |
757 | } |
758 | |
759 | -bool media::TrackListSkeleton::has_previous() const |
760 | +bool media::TrackListSkeleton::has_previous() |
761 | { |
762 | + if (tracks().get().empty()) |
763 | + return false; |
764 | + |
765 | // If we are looping over the entire list, then there is always a previous track |
766 | if (d->loop_status == media::Player::LoopStatus::playlist) |
767 | return true; |
768 | |
769 | - std::cout << "has_previous track? " << (d->current_track != tracks().get().begin() ? "yes" : "no") << std::endl; |
770 | - return d->current_track != tracks().get().begin(); |
771 | + return d->current_track != std::begin(tracks().get()); |
772 | +} |
773 | + |
774 | +bool media::TrackListSkeleton::is_first_track(const ConstIterator &it) |
775 | +{ |
776 | + return it == std::begin(tracks().get()); |
777 | +} |
778 | + |
779 | +bool media::TrackListSkeleton::is_last_track(const TrackList::ConstIterator &it) |
780 | +{ |
781 | + return it == std::end(tracks().get()); |
782 | } |
783 | |
784 | media::Track::Id media::TrackListSkeleton::next() |
785 | { |
786 | std::cout << __PRETTY_FUNCTION__ << std::endl; |
787 | if (tracks().get().empty()) |
788 | - return *(d->current_track); |
789 | + return *(d->empty_iterator); |
790 | + |
791 | + const auto next_track = std::next(current_iterator()); |
792 | + bool do_go_to_next_track = false; |
793 | + |
794 | + // End of the track reached so loop around to the beginning of the track |
795 | + if (d->loop_status == media::Player::LoopStatus::track) |
796 | + { |
797 | + std::cout << "Looping on the current track since LoopStatus is set to track" << std::endl; |
798 | + do_go_to_next_track = true; |
799 | + } |
800 | + // End of the tracklist reached so loop around to the beginning of the tracklist |
801 | + else if (d->loop_status == media::Player::LoopStatus::playlist && not has_next()) |
802 | + { |
803 | + std::cout << "Looping on the tracklist since LoopStatus is set to playlist" << std::endl; |
804 | + d->current_track = tracks().get().begin(); |
805 | + do_go_to_next_track = true; |
806 | + } |
807 | + else |
808 | + { |
809 | + // Next track is not the last track |
810 | + if (not is_last_track(next_track)) |
811 | + { |
812 | + std::cout << "Advancing to next track: " << *(next_track) << std::endl; |
813 | + d->current_track = next_track; |
814 | + do_go_to_next_track = true; |
815 | + } |
816 | + // At the end of the tracklist and not set to loop, so we stop advancing the tracklist |
817 | + else |
818 | + { |
819 | + std::cout << "End of tracklist reached, not advancing to next since LoopStatus is set to none" << std::endl; |
820 | + on_end_of_tracklist()(); |
821 | + } |
822 | + } |
823 | + |
824 | + if (do_go_to_next_track) |
825 | + { |
826 | + on_track_changed()(*(current_iterator())); |
827 | + // Don't automatically call stop() and play() in player_implementation.cpp on_go_to_track() |
828 | + // since this breaks video playback when using open_uri() (stop() and play() are unwanted in |
829 | + // this scenario since the qtubuntu-media will handle this automatically) |
830 | + const bool toggle_player_state = false; |
831 | + const media::Track::Id id = *(current_iterator()); |
832 | + const std::pair<const media::Track::Id, bool> p = std::make_pair(id, toggle_player_state); |
833 | + // Signal the PlayerImplementation to play the next track |
834 | + on_go_to_track()(p); |
835 | + } |
836 | + |
837 | + return *(current_iterator()); |
838 | +} |
839 | + |
840 | +media::Track::Id media::TrackListSkeleton::previous() |
841 | +{ |
842 | + std::cout << __PRETTY_FUNCTION__ << std::endl; |
843 | + if (tracks().get().empty()) |
844 | + return *(d->empty_iterator); |
845 | + |
846 | + bool do_go_to_previous_track = false; |
847 | |
848 | // Loop on the current track forever |
849 | if (d->loop_status == media::Player::LoopStatus::track) |
850 | { |
851 | std::cout << "Looping on the current track..." << std::endl; |
852 | - return *(d->current_track); |
853 | + do_go_to_previous_track = true; |
854 | } |
855 | // Loop over the whole playlist and repeat |
856 | - else if (d->loop_status == media::Player::LoopStatus::playlist && !has_next()) |
857 | + else if (d->loop_status == media::Player::LoopStatus::playlist && is_first_track(current_iterator())) |
858 | { |
859 | std::cout << "Looping on the entire TrackList..." << std::endl; |
860 | - d->current_track = tracks().get().begin(); |
861 | - return *(d->current_track); |
862 | - } |
863 | - else if (has_next()) |
864 | - { |
865 | - // Keep returning the next track until the last track is reached |
866 | - d->current_track = std::next(d->current_track); |
867 | - std::cout << *this << std::endl; |
868 | - } |
869 | - |
870 | - return *(d->current_track); |
871 | -} |
872 | - |
873 | -media::Track::Id media::TrackListSkeleton::previous() |
874 | -{ |
875 | - // TODO: Add logic to calculate the previous track |
876 | - return *(d->current_track); |
877 | + d->current_track = std::prev(tracks().get().end()); |
878 | + do_go_to_previous_track = true; |
879 | + } |
880 | + else |
881 | + { |
882 | + // Current track is not the first track |
883 | + if (not is_first_track(current_iterator())) |
884 | + { |
885 | + // Keep returning the previous track until the first track is reached |
886 | + d->current_track = std::prev(current_iterator()); |
887 | + do_go_to_previous_track = true; |
888 | + } |
889 | + // At the beginning of the tracklist and not set to loop, so we stop advancing the tracklist |
890 | + else |
891 | + { |
892 | + std::cout << "Beginning of tracklist reached, not advancing to previous since LoopStatus is set to none" << std::endl; |
893 | + on_end_of_tracklist()(); |
894 | + } |
895 | + } |
896 | + |
897 | + if (do_go_to_previous_track) |
898 | + { |
899 | + on_track_changed()(*(current_iterator())); |
900 | + // Don't automatically call stop() and play() in player_implementation.cpp on_go_to_track() |
901 | + // since this breaks video playback when using open_uri() (stop() and play() are unwanted in |
902 | + // this scenario since the qtubuntu-media will handle this automatically) |
903 | + const bool toggle_player_state = false; |
904 | + const media::Track::Id id = *(current_iterator()); |
905 | + const std::pair<const media::Track::Id, bool> p = std::make_pair(id, toggle_player_state); |
906 | + on_go_to_track()(p); |
907 | + } |
908 | + |
909 | + return *(current_iterator()); |
910 | } |
911 | |
912 | const media::Track::Id& media::TrackListSkeleton::current() |
913 | { |
914 | + return *(current_iterator()); |
915 | +} |
916 | + |
917 | +const media::TrackList::ConstIterator& media::TrackListSkeleton::current_iterator() |
918 | +{ |
919 | // Prevent the TrackList from sitting at the end which will cause |
920 | // a segfault when calling current() |
921 | if (tracks().get().size() && (d->current_track == d->empty_iterator)) |
922 | + { |
923 | + std::cout << "Wrapping d->current_track back to begin()" << std::endl; |
924 | d->current_track = d->skeleton.properties.tracks->get().begin(); |
925 | + } |
926 | else if (tracks().get().empty()) |
927 | + { |
928 | std::cerr << "TrackList is empty therefore there is no valid current track" << std::endl; |
929 | - |
930 | - return *(d->current_track); |
931 | + } |
932 | + |
933 | + return d->current_track; |
934 | +} |
935 | + |
936 | +void media::TrackListSkeleton::reset_current_iterator_if_needed() |
937 | +{ |
938 | + // If all tracks got removed then we need to keep a sane current |
939 | + // iterator for further use. |
940 | + if (tracks().get().empty()) |
941 | + d->current_track = d->empty_iterator; |
942 | } |
943 | |
944 | const core::Property<bool>& media::TrackListSkeleton::can_edit_tracks() const |
945 | @@ -293,7 +438,18 @@ |
946 | if (shuffle) |
947 | shuffle_tracks(); |
948 | else |
949 | + { |
950 | + // Save the current Track::Id of what's currently playing to restore after unshuffle |
951 | + const media::Track::Id current_id = *(current_iterator()); |
952 | + |
953 | unshuffle_tracks(); |
954 | + |
955 | + // Since we use assign() in unshuffle_tracks, which invalidates existing iterators, we need |
956 | + // to make sure that current is pointing to the right place |
957 | + auto it = std::find(tracks().get().begin(), tracks().get().end(), current_id); |
958 | + if (it != tracks().get().end()) |
959 | + d->current_track = it; |
960 | + } |
961 | } |
962 | |
963 | const core::Property<media::TrackList::Container>& media::TrackListSkeleton::tracks() const |
964 | @@ -328,6 +484,11 @@ |
965 | return d->signals.on_go_to_track; |
966 | } |
967 | |
968 | +const core::Signal<void>& media::TrackListSkeleton::on_end_of_tracklist() const |
969 | +{ |
970 | + return d->signals.on_end_of_tracklist; |
971 | +} |
972 | + |
973 | core::Signal<media::TrackList::ContainerTrackIdTuple>& media::TrackListSkeleton::on_track_list_replaced() |
974 | { |
975 | return d->signals.on_track_list_replaced; |
976 | @@ -353,6 +514,17 @@ |
977 | return d->signals.on_go_to_track; |
978 | } |
979 | |
980 | +core::Signal<void>& media::TrackListSkeleton::on_end_of_tracklist() |
981 | +{ |
982 | + return d->signals.on_end_of_tracklist; |
983 | +} |
984 | + |
985 | +void media::TrackListSkeleton::reset() |
986 | +{ |
987 | + std::cout << __PRETTY_FUNCTION__ << std::endl; |
988 | + d->current_track = d->empty_iterator; |
989 | +} |
990 | + |
991 | // operator<< pretty prints the given TrackList to the given output stream. |
992 | inline std::ostream& media::operator<<(std::ostream& out, const media::TrackList& tracklist) |
993 | { |
994 | |
995 | === modified file 'src/core/media/track_list_skeleton.h' |
996 | --- src/core/media/track_list_skeleton.h 2015-04-29 21:18:20 +0000 |
997 | +++ src/core/media/track_list_skeleton.h 2015-09-03 07:33:12 +0000 |
998 | @@ -41,8 +41,8 @@ |
999 | const core::ubuntu::media::apparmor::ubuntu::RequestAuthenticator::Ptr& request_authenticator); |
1000 | ~TrackListSkeleton(); |
1001 | |
1002 | - bool has_next() const; |
1003 | - bool has_previous() const; |
1004 | + bool has_next(); |
1005 | + bool has_previous(); |
1006 | Track::Id next(); |
1007 | Track::Id previous(); |
1008 | const Track::Id& current(); |
1009 | @@ -52,10 +52,13 @@ |
1010 | |
1011 | const core::Signal<ContainerTrackIdTuple>& on_track_list_replaced() const; |
1012 | const core::Signal<Track::Id>& on_track_added() const; |
1013 | + core::Signal<Track::Id>& on_track_added(); |
1014 | const core::Signal<Track::Id>& on_track_removed() const; |
1015 | const core::Signal<Track::Id>& on_track_changed() const; |
1016 | const core::Signal<std::pair<Track::Id, bool>>& on_go_to_track() const; |
1017 | core::Signal<std::pair<Track::Id, bool>>& on_go_to_track(); |
1018 | + const core::Signal<void>& on_end_of_tracklist() const; |
1019 | + core::Signal<void>& on_end_of_tracklist(); |
1020 | core::Signal<Track::Id>& on_track_removed(); |
1021 | |
1022 | core::Property<Container>& tracks(); |
1023 | @@ -67,12 +70,18 @@ |
1024 | void on_shuffle_changed(bool shuffle); |
1025 | |
1026 | protected: |
1027 | + inline bool is_first_track(const ConstIterator &it); |
1028 | + inline bool is_last_track(const ConstIterator &it); |
1029 | + inline const TrackList::ConstIterator& current_iterator(); |
1030 | + void reset_current_iterator_if_needed(); |
1031 | + |
1032 | core::Property<bool>& can_edit_tracks(); |
1033 | |
1034 | core::Signal<ContainerTrackIdTuple>& on_track_list_replaced(); |
1035 | - core::Signal<Track::Id>& on_track_added(); |
1036 | core::Signal<Track::Id>& on_track_changed(); |
1037 | |
1038 | + void reset(); |
1039 | + |
1040 | private: |
1041 | struct Private; |
1042 | std::unique_ptr<Private> d; |
1043 | |
1044 | === modified file 'src/core/media/track_list_stub.cpp' |
1045 | --- src/core/media/track_list_stub.cpp 2015-04-29 21:18:20 +0000 |
1046 | +++ src/core/media/track_list_stub.cpp 2015-09-03 07:33:12 +0000 |
1047 | @@ -48,7 +48,14 @@ |
1048 | parent(parent), |
1049 | object(object), |
1050 | can_edit_tracks(object->get_property<mpris::TrackList::Properties::CanEditTracks>()), |
1051 | - tracks(object->get_property<mpris::TrackList::Properties::Tracks>()) |
1052 | + tracks(object->get_property<mpris::TrackList::Properties::Tracks>()), |
1053 | + signals |
1054 | + { |
1055 | + object->get_signal<mpris::TrackList::Signals::TrackAdded>(), |
1056 | + object->get_signal<mpris::TrackList::Signals::TrackRemoved>(), |
1057 | + object->get_signal<mpris::TrackList::Signals::TrackListReplaced>(), |
1058 | + object->get_signal<mpris::TrackList::Signals::TrackChanged>() |
1059 | + } |
1060 | { |
1061 | } |
1062 | |
1063 | @@ -59,11 +66,70 @@ |
1064 | std::shared_ptr<core::dbus::Property<mpris::TrackList::Properties::CanEditTracks>> can_edit_tracks; |
1065 | std::shared_ptr<core::dbus::Property<mpris::TrackList::Properties::Tracks>> tracks; |
1066 | |
1067 | - core::Signal<media::TrackList::ContainerTrackIdTuple> on_track_list_replaced; |
1068 | - core::Signal<Track::Id> on_track_added; |
1069 | - core::Signal<Track::Id> on_track_removed; |
1070 | - core::Signal<Track::Id> on_track_changed; |
1071 | - core::Signal<std::pair<Track::Id, bool>> on_go_to_track; |
1072 | + |
1073 | + struct Signals |
1074 | + { |
1075 | + typedef core::dbus::Signal<mpris::TrackList::Signals::TrackAdded, mpris::TrackList::Signals::TrackAdded::ArgumentType> DBusTrackAddedSignal; |
1076 | + typedef core::dbus::Signal<mpris::TrackList::Signals::TrackRemoved, mpris::TrackList::Signals::TrackRemoved::ArgumentType> DBusTrackRemovedSignal; |
1077 | + typedef core::dbus::Signal<mpris::TrackList::Signals::TrackListReplaced, mpris::TrackList::Signals::TrackListReplaced::ArgumentType> DBusTrackListReplacedSignal; |
1078 | + typedef core::dbus::Signal<mpris::TrackList::Signals::TrackChanged, mpris::TrackList::Signals::TrackChanged::ArgumentType> DBusTrackChangedSignal; |
1079 | + |
1080 | + Signals(const std::shared_ptr<DBusTrackAddedSignal>& track_added, |
1081 | + const std::shared_ptr<DBusTrackRemovedSignal>& track_removed, |
1082 | + const std::shared_ptr<DBusTrackListReplacedSignal>& track_list_replaced, |
1083 | + const std::shared_ptr<DBusTrackChangedSignal>& track_changed) |
1084 | + : on_track_added(), |
1085 | + on_track_removed(), |
1086 | + on_track_list_replaced(), |
1087 | + on_track_changed(), |
1088 | + dbus |
1089 | + { |
1090 | + track_added, |
1091 | + track_removed, |
1092 | + track_list_replaced, |
1093 | + track_changed, |
1094 | + } |
1095 | + { |
1096 | + dbus.on_track_added->connect([this](const Track::Id& id) |
1097 | + { |
1098 | + std::cout << "OnTrackAdded signal arrived via the bus." << std::endl; |
1099 | + on_track_added(id); |
1100 | + }); |
1101 | + |
1102 | + dbus.on_track_removed->connect([this](const Track::Id& id) |
1103 | + { |
1104 | + std::cout << "OnTrackRemoved signal arrived via the bus." << std::endl; |
1105 | + on_track_removed(id); |
1106 | + }); |
1107 | + |
1108 | + dbus.on_track_list_replaced->connect([this](const media::TrackList::ContainerTrackIdTuple& list) |
1109 | + { |
1110 | + std::cout << "OnTrackListRemoved signal arrived via the bus." << std::endl; |
1111 | + on_track_list_replaced(list); |
1112 | + }); |
1113 | + |
1114 | + dbus.on_track_changed->connect([this](const Track::Id& id) |
1115 | + { |
1116 | + std::cout << "OnTrackChanged signal arrived via the bus." << std::endl; |
1117 | + on_track_changed(id); |
1118 | + }); |
1119 | + } |
1120 | + |
1121 | + core::Signal<Track::Id> on_track_added; |
1122 | + core::Signal<Track::Id> on_track_removed; |
1123 | + core::Signal<media::TrackList::ContainerTrackIdTuple> on_track_list_replaced; |
1124 | + core::Signal<Track::Id> on_track_changed; |
1125 | + core::Signal<std::pair<Track::Id, bool>> on_go_to_track; |
1126 | + core::Signal<void> on_end_of_tracklist; |
1127 | + |
1128 | + struct DBus |
1129 | + { |
1130 | + std::shared_ptr<DBusTrackAddedSignal> on_track_added; |
1131 | + std::shared_ptr<DBusTrackRemovedSignal> on_track_removed; |
1132 | + std::shared_ptr<DBusTrackListReplacedSignal> on_track_list_replaced; |
1133 | + std::shared_ptr<DBusTrackChangedSignal> on_track_changed; |
1134 | + } dbus; |
1135 | + } signals; |
1136 | }; |
1137 | |
1138 | media::TrackListStub::TrackListStub( |
1139 | @@ -104,6 +170,18 @@ |
1140 | return md; |
1141 | } |
1142 | |
1143 | +media::Track::UriType media::TrackListStub::query_uri_for_track(const media::Track::Id& id) |
1144 | +{ |
1145 | + auto op = d->object->invoke_method_synchronously< |
1146 | + mpris::TrackList::GetTracksUri, |
1147 | + std::string>(id); |
1148 | + |
1149 | + if (op.is_error()) |
1150 | + throw std::runtime_error("Problem querying track for uri: " + op.error()); |
1151 | + |
1152 | + return op.value(); |
1153 | +} |
1154 | + |
1155 | void media::TrackListStub::add_track_with_uri_at( |
1156 | const media::Track::UriType& uri, |
1157 | const media::Track::Id& id, |
1158 | @@ -161,33 +239,38 @@ |
1159 | |
1160 | void media::TrackListStub::reset() |
1161 | { |
1162 | - std::cerr << "reset() does nothing from the client side" << std::endl; |
1163 | + auto op = d->object->invoke_method_synchronously<mpris::TrackList::Reset, void>(); |
1164 | + |
1165 | + if (op.is_error()) |
1166 | + throw std::runtime_error("Problem resetting tracklist: " + op.error()); |
1167 | } |
1168 | |
1169 | const core::Signal<media::TrackList::ContainerTrackIdTuple>& media::TrackListStub::on_track_list_replaced() const |
1170 | { |
1171 | - std::cout << "Signal on_track_list_replaced arrived via the bus" << std::endl; |
1172 | - return d->on_track_list_replaced; |
1173 | + return d->signals.on_track_list_replaced; |
1174 | } |
1175 | |
1176 | const core::Signal<media::Track::Id>& media::TrackListStub::on_track_added() const |
1177 | { |
1178 | - std::cout << "Signal on_track_added arrived via the bus" << std::endl; |
1179 | - return d->on_track_added; |
1180 | + return d->signals.on_track_added; |
1181 | } |
1182 | |
1183 | const core::Signal<media::Track::Id>& media::TrackListStub::on_track_removed() const |
1184 | { |
1185 | - std::cout << "Signal on_track_removed arrived via the bus" << std::endl; |
1186 | - return d->on_track_removed; |
1187 | + return d->signals.on_track_removed; |
1188 | } |
1189 | |
1190 | const core::Signal<media::Track::Id>& media::TrackListStub::on_track_changed() const |
1191 | { |
1192 | - return d->on_track_changed; |
1193 | + return d->signals.on_track_changed; |
1194 | } |
1195 | |
1196 | const core::Signal<std::pair<media::Track::Id, bool>>& media::TrackListStub::on_go_to_track() const |
1197 | { |
1198 | - return d->on_go_to_track; |
1199 | + return d->signals.on_go_to_track; |
1200 | +} |
1201 | + |
1202 | +const core::Signal<void>& media::TrackListStub::on_end_of_tracklist() const |
1203 | +{ |
1204 | + return d->signals.on_end_of_tracklist; |
1205 | } |
1206 | |
1207 | === modified file 'src/core/media/track_list_stub.h' |
1208 | --- src/core/media/track_list_stub.h 2015-04-29 21:18:20 +0000 |
1209 | +++ src/core/media/track_list_stub.h 2015-09-03 07:33:12 +0000 |
1210 | @@ -45,6 +45,7 @@ |
1211 | const core::Property<Container>& tracks() const; |
1212 | |
1213 | Track::MetaData query_meta_data_for_track(const Track::Id& id); |
1214 | + Track::UriType query_uri_for_track(const Track::Id& id); |
1215 | |
1216 | void add_track_with_uri_at(const Track::UriType& uri, const Track::Id& position, bool make_current); |
1217 | void remove_track(const Track::Id& id); |
1218 | @@ -64,6 +65,7 @@ |
1219 | const core::Signal<Track::Id>& on_track_removed() const; |
1220 | const core::Signal<Track::Id>& on_track_changed() const; |
1221 | const core::Signal<std::pair<Track::Id, bool>>& on_go_to_track() const; |
1222 | + const core::Signal<void>& on_end_of_tracklist() const; |
1223 | |
1224 | private: |
1225 | struct Private; |