Merge lp:~phablet-team/media-hub/sync-trunk into lp:media-hub
- sync-trunk
- Merge into trunk
Proposed by
Alfonso Sanchez-Beato
Status: | Merged |
---|---|
Approved by: | Thomas Voß |
Approved revision: | 161 |
Merged at revision: | 162 |
Proposed branch: | lp:~phablet-team/media-hub/sync-trunk |
Merge into: | lp:media-hub |
Diff against target: |
2628 lines (+1182/-630) 18 files modified
README (+4/-0) debian/changelog (+45/-0) include/core/media/track_list.h (+53/-12) src/core/media/gstreamer/playbin.cpp (+9/-1) src/core/media/mpris/track_list.h (+72/-0) src/core/media/player_implementation.cpp (+40/-10) src/core/media/service_skeleton.cpp (+9/-3) src/core/media/track_list.cpp (+27/-0) src/core/media/track_list_implementation.cpp (+246/-92) src/core/media/track_list_implementation.h (+6/-3) src/core/media/track_list_skeleton.cpp (+398/-91) src/core/media/track_list_skeleton.h (+25/-5) src/core/media/track_list_stub.cpp (+118/-21) src/core/media/track_list_stub.h (+7/-5) tests/CMakeLists.txt (+1/-1) tests/acceptance-tests/service.cpp (+119/-382) tests/test-track-list/test_track_list.cpp (+2/-3) tests/test-track-list/test_track_list.h (+1/-1) |
To merge this branch: | bzr merge lp:~phablet-team/media-hub/sync-trunk |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Jim Hodapp (community) | code | Approve | |
Review via email:
|
Commit message
Sync with stable branch
Description of the change
Sync with stable branch
To post a comment you must log in.
- 161. By Thomas Voß
-
Fix bug #1479036 which prevents the out_of_range exception from
causing media-hub-server from crashing when a player key is not
found (LP: #1479036)
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'README' |
2 | --- README 2015-09-09 14:08:12 +0000 |
3 | +++ README 2015-12-23 14:19:01 +0000 |
4 | @@ -6,6 +6,10 @@ |
5 | |
6 | https://google-styleguide.googlecode.com/svn/trunk/cppguide.html |
7 | |
8 | +Deviations from the Google Style Guide above: |
9 | + |
10 | +1. We will agree to maximum of 100 characters in a single line instead of 80. |
11 | + |
12 | |
13 | To Build: |
14 | --------- |
15 | |
16 | === modified file 'debian/changelog' |
17 | --- debian/changelog 2015-12-21 18:55:55 +0000 |
18 | +++ debian/changelog 2015-12-23 14:19:01 +0000 |
19 | @@ -57,12 +57,57 @@ |
20 | |
21 | -- CI Train Bot <ci-train-bot@canonical.com> Tue, 01 Sep 2015 08:46:00 +0000 |
22 | |
23 | +media-hub (4.0.0+15.04.20151209-0ubuntu1) vivid; urgency=medium |
24 | + |
25 | + * Set gstreamer context directly instead of using gobject to |
26 | + communicate with the video sink |
27 | + |
28 | + -- Alfonso Sanchez-Beato <alfonso.sanchez-beato@canonical.com> Wed, 09 Dec 2015 06:49:28 +0000 |
29 | + |
30 | +media-hub (4.0.0+15.04.20151202-0ubuntu1) vivid; urgency=medium |
31 | + |
32 | + [ CI Train Bot ] |
33 | + * No-change rebuild. |
34 | + |
35 | + -- Thomas Voß <ci-train-bot@canonical.com> Wed, 02 Dec 2015 07:40:41 +0000 |
36 | + |
37 | +media-hub (4.0.0+15.04.20151118.1-0ubuntu1) vivid; urgency=medium |
38 | + |
39 | + [ Jim Hodapp ] |
40 | + * Added move_track and various fixes for TrackList. |
41 | + * Major bump for new TrackList API changes. |
42 | + |
43 | + [ Alfonso Sanchez-Beato ] |
44 | + * Make sure our iterator for the current track points to the right |
45 | + place when (un)shuffling (LP #1510219). Fix crash when client tries |
46 | + to set the player for a non-existing key. Do not add empty URIs to |
47 | + the list (LP: #1511029). (LP: #1511073, #1511385, #1510219, |
48 | + #1510227, #1511029) |
49 | + |
50 | + -- Jim Hodapp <ci-train-bot@canonical.com> Wed, 18 Nov 2015 18:36:18 +0000 |
51 | + |
52 | media-hub (4.0.0-0ubuntu1) wily; urgency=medium |
53 | |
54 | * Bump major revision to account for toolchain update. Fixes LP:#1452331 |
55 | |
56 | -- Thomas Voß <thomas.voss@canonical.com> Thu, 23 Jul 2015 08:47:21 +0200 |
57 | |
58 | +media-hub (3.3.0+15.04.20151023.3-0ubuntu2) UNRELEASED; urgency=medium |
59 | + |
60 | + * Force rebuild against dbus-cpp. |
61 | + |
62 | + -- Thomas Voß <thomas.voss@canonical.com> Tue, 17 Nov 2015 09:37:46 +0100 |
63 | + |
64 | +media-hub (3.3.0+15.04.20151023.3-0ubuntu1) vivid; urgency=medium |
65 | + |
66 | + [ Jim Hodapp ] |
67 | + * Add batch adding of tracks to the TrackList. |
68 | + |
69 | + [ CI Train Bot ] |
70 | + * New rebuild forced. |
71 | + |
72 | + -- Jim Hodapp <ci-train-bot@canonical.com> Fri, 23 Oct 2015 19:07:52 +0000 |
73 | + |
74 | media-hub (3.1.0+15.10.20150724-0ubuntu1) wily; urgency=medium |
75 | |
76 | [ Alfonso Sanchez-Beato (email Canonical) ] |
77 | |
78 | === modified file 'include/core/media/track_list.h' |
79 | --- include/core/media/track_list.h 2015-07-20 20:39:42 +0000 |
80 | +++ include/core/media/track_list.h 2015-12-23 14:19:01 +0000 |
81 | @@ -41,10 +41,42 @@ |
82 | { |
83 | public: |
84 | typedef std::vector<Track::Id> Container; |
85 | + typedef std::vector<Track::UriType> ContainerURI; |
86 | typedef std::tuple<std::vector<Track::Id>, Track::Id> ContainerTrackIdTuple; |
87 | + typedef std::tuple<Track::Id, Track::Id> TrackIdTuple; |
88 | typedef Container::iterator Iterator; |
89 | typedef Container::const_iterator ConstIterator; |
90 | |
91 | + struct Errors |
92 | + { |
93 | + Errors() = delete; |
94 | + |
95 | + struct InsufficientPermissionsToAddTrack : public std::runtime_error |
96 | + { |
97 | + InsufficientPermissionsToAddTrack(); |
98 | + }; |
99 | + |
100 | + struct FailedToMoveTrack : public std::runtime_error |
101 | + { |
102 | + FailedToMoveTrack(); |
103 | + }; |
104 | + |
105 | + struct FailedToFindMoveTrackSource : public std::runtime_error |
106 | + { |
107 | + FailedToFindMoveTrackSource(const std::string& err); |
108 | + }; |
109 | + |
110 | + struct FailedToFindMoveTrackDest : public std::runtime_error |
111 | + { |
112 | + FailedToFindMoveTrackDest(const std::string& err); |
113 | + }; |
114 | + |
115 | + struct TrackNotFound : public std::runtime_error |
116 | + { |
117 | + TrackNotFound(); |
118 | + }; |
119 | + }; |
120 | + |
121 | static const Track::Id& after_empty_track(); |
122 | |
123 | TrackList(const TrackList&) = delete; |
124 | @@ -65,14 +97,20 @@ |
125 | /** Gets the URI for a given Track. */ |
126 | virtual Track::UriType query_uri_for_track(const Track::Id& id) = 0; |
127 | |
128 | - /** Adds a URI in the TrackList. */ |
129 | + /** Adds a URI into the TrackList. */ |
130 | virtual void add_track_with_uri_at(const Track::UriType& uri, const Track::Id& position, bool make_current) = 0; |
131 | |
132 | + /** Adds a list of URIs into the TrackList. */ |
133 | + virtual void add_tracks_with_uri_at(const ContainerURI& uris, const Track::Id& position) = 0; |
134 | + |
135 | + /** Moves track 'id' from its old position in the TrackList to new position. */ |
136 | + virtual bool move_track(const Track::Id& id, const Track::Id& to) = 0; |
137 | + |
138 | /** Removes a Track from the TrackList. */ |
139 | virtual void remove_track(const Track::Id& id) = 0; |
140 | |
141 | - /** Skip to the specified TrackId. Calls stop() and play() on the player if toggle_player_state is true. */ |
142 | - virtual void go_to(const Track::Id& track, bool toggle_player_state) = 0; |
143 | + /** Skip to the specified TrackId. */ |
144 | + virtual void go_to(const Track::Id& track) = 0; |
145 | |
146 | /** Returns true if there is a next track in the TrackList after the current one playing */ |
147 | bool has_next() const; |
148 | @@ -86,31 +124,34 @@ |
149 | /** Skip to the previous Track in the TrackList if there is one. */ |
150 | virtual Track::Id previous() = 0; |
151 | |
152 | - |
153 | - /** Reorders the tracks such that they are in a random order. */ |
154 | - virtual void shuffle_tracks() = 0; |
155 | - |
156 | - /** Restores the original order of tracks before shuffle mode was turned on. */ |
157 | - virtual void unshuffle_tracks() = 0; |
158 | - |
159 | /** Clears and resets the TrackList to the same as a newly constructed instance. */ |
160 | virtual void reset() = 0; |
161 | |
162 | - |
163 | /** Indicates that the entire tracklist has been replaced. */ |
164 | virtual const core::Signal<ContainerTrackIdTuple>& on_track_list_replaced() const = 0; |
165 | |
166 | /** Indicates that a track has been added to the track list. */ |
167 | virtual const core::Signal<Track::Id>& on_track_added() const = 0; |
168 | |
169 | + /** Indicates that one or more tracks have been added to the track list. */ |
170 | + virtual const core::Signal<ContainerURI>& on_tracks_added() const = 0; |
171 | + |
172 | + /** Indicates that a track has been moved within the track list. First template param |
173 | + * holds the id of the track being moved. Second param holds the id of the track of the |
174 | + * position to move the track to in the TrackList. */ |
175 | + virtual const core::Signal<TrackIdTuple>& on_track_moved() const = 0; |
176 | + |
177 | /** Indicates that a track has been removed from the track list. */ |
178 | virtual const core::Signal<Track::Id>& on_track_removed() const = 0; |
179 | |
180 | + /** Indicates that the track list has been reset and there are no tracks now */ |
181 | + virtual const core::Signal<void>& on_track_list_reset() const = 0; |
182 | + |
183 | /** Indicates that the track list advanced from one track to another. */ |
184 | virtual const core::Signal<Track::Id>& on_track_changed() const = 0; |
185 | |
186 | /** Used to notify the Player of when the client requested that the Player should immediately play a new track. */ |
187 | - virtual const core::Signal<std::pair<Track::Id, bool>>& on_go_to_track() const = 0; |
188 | + virtual const core::Signal<Track::Id>& on_go_to_track() const = 0; |
189 | |
190 | /** Used to notify the Player of when the end of the tracklist has been reached. */ |
191 | virtual const core::Signal<void>& on_end_of_tracklist() const = 0; |
192 | |
193 | === modified file 'src/core/media/gstreamer/playbin.cpp' |
194 | --- src/core/media/gstreamer/playbin.cpp 2015-10-16 08:32:58 +0000 |
195 | +++ src/core/media/gstreamer/playbin.cpp 2015-12-23 14:19:01 +0000 |
196 | @@ -455,7 +455,13 @@ |
197 | const core::ubuntu::media::Player::HeadersType& headers = core::ubuntu::media::Player::HeadersType(), |
198 | bool do_pipeline_reset) |
199 | { |
200 | - if (do_pipeline_reset) |
201 | + gchar *current_uri = nullptr; |
202 | + g_object_get(pipeline, "current-uri", ¤t_uri, NULL); |
203 | + |
204 | + // Checking for a current_uri being set and not resetting the pipeline |
205 | + // if there isn't a current_uri causes the first play to start playback |
206 | + // sooner since reset_pipeline won't be called |
207 | + if (current_uri and do_pipeline_reset) |
208 | reset_pipeline(); |
209 | |
210 | g_object_set(pipeline, "uri", uri.c_str(), NULL); |
211 | @@ -465,6 +471,8 @@ |
212 | file_type = MEDIA_FILE_TYPE_AUDIO; |
213 | |
214 | request_headers = headers; |
215 | + |
216 | + g_free(current_uri); |
217 | } |
218 | |
219 | void gstreamer::Playbin::setup_source(GstElement *source) |
220 | |
221 | === modified file 'src/core/media/mpris/track_list.h' |
222 | --- src/core/media/mpris/track_list.h 2015-07-27 22:15:33 +0000 |
223 | +++ src/core/media/mpris/track_list.h 2015-12-23 14:19:01 +0000 |
224 | @@ -48,9 +48,54 @@ |
225 | return s; |
226 | } |
227 | |
228 | + struct Error |
229 | + { |
230 | + struct InsufficientPermissionsToAddTrack |
231 | + { |
232 | + static constexpr const char* name |
233 | + { |
234 | + "mpris.TrackList.Error.InsufficientPermissionsToAddTrack" |
235 | + }; |
236 | + }; |
237 | + |
238 | + struct FailedToMoveTrack |
239 | + { |
240 | + static constexpr const char* name |
241 | + { |
242 | + "mpris.TrackList.Error.FailedToMoveTrack" |
243 | + }; |
244 | + }; |
245 | + |
246 | + struct FailedToFindMoveTrackSource |
247 | + { |
248 | + static constexpr const char* name |
249 | + { |
250 | + "mpris.TrackList.Error.FailedToFindMoveTrackSource" |
251 | + }; |
252 | + }; |
253 | + |
254 | + struct FailedToFindMoveTrackDest |
255 | + { |
256 | + static constexpr const char* name |
257 | + { |
258 | + "mpris.TrackList.Error.FailedToFindMoveTrackDest" |
259 | + }; |
260 | + }; |
261 | + |
262 | + struct TrackNotFound |
263 | + { |
264 | + static constexpr const char* name |
265 | + { |
266 | + "mpris.TrackList.Error.TrackNotFound" |
267 | + }; |
268 | + }; |
269 | + }; |
270 | + |
271 | DBUS_CPP_METHOD_DEF(GetTracksMetadata, TrackList) |
272 | DBUS_CPP_METHOD_DEF(GetTracksUri, TrackList) |
273 | DBUS_CPP_METHOD_DEF(AddTrack, TrackList) |
274 | + DBUS_CPP_METHOD_DEF(AddTracks, TrackList) |
275 | + DBUS_CPP_METHOD_DEF(MoveTrack, TrackList) |
276 | DBUS_CPP_METHOD_DEF(RemoveTrack, TrackList) |
277 | DBUS_CPP_METHOD_DEF(GoTo, TrackList) |
278 | DBUS_CPP_METHOD_DEF(Reset, TrackList) |
279 | @@ -75,6 +120,20 @@ |
280 | |
281 | DBUS_CPP_SIGNAL_DEF |
282 | ( |
283 | + TracksAdded, |
284 | + TrackList, |
285 | + core::ubuntu::media::TrackList::ContainerURI |
286 | + ) |
287 | + |
288 | + DBUS_CPP_SIGNAL_DEF |
289 | + ( |
290 | + TrackMoved, |
291 | + TrackList, |
292 | + BOOST_IDENTITY_TYPE((std::tuple<core::ubuntu::media::Track::Id, core::ubuntu::media::Track::Id>)) |
293 | + ) |
294 | + |
295 | + DBUS_CPP_SIGNAL_DEF |
296 | + ( |
297 | TrackRemoved, |
298 | TrackList, |
299 | core::ubuntu::media::Track::Id |
300 | @@ -89,6 +148,13 @@ |
301 | |
302 | DBUS_CPP_SIGNAL_DEF |
303 | ( |
304 | + TrackListReset, |
305 | + TrackList, |
306 | + void |
307 | + ) |
308 | + |
309 | + DBUS_CPP_SIGNAL_DEF |
310 | + ( |
311 | TrackMetadataChanged, |
312 | TrackList, |
313 | BOOST_IDENTITY_TYPE((std::tuple<std::map<std::string, dbus::types::Variant>, dbus::types::ObjectPath>)) |
314 | @@ -134,8 +200,11 @@ |
315 | { |
316 | configuration.object->template get_signal<Signals::TrackListReplaced>(), |
317 | configuration.object->template get_signal<Signals::TrackAdded>(), |
318 | + configuration.object->template get_signal<Signals::TracksAdded>(), |
319 | + configuration.object->template get_signal<Signals::TrackMoved>(), |
320 | configuration.object->template get_signal<Signals::TrackRemoved>(), |
321 | configuration.object->template get_signal<Signals::TrackChanged>(), |
322 | + configuration.object->template get_signal<Signals::TrackListReset>(), |
323 | configuration.object->template get_signal<Signals::TrackMetadataChanged>(), |
324 | configuration.object->template get_signal<core::dbus::interfaces::Properties::Signals::PropertiesChanged>() |
325 | } |
326 | @@ -178,8 +247,11 @@ |
327 | { |
328 | core::dbus::Signal<Signals::TrackListReplaced, Signals::TrackListReplaced::ArgumentType>::Ptr tracklist_replaced; |
329 | core::dbus::Signal<Signals::TrackAdded, Signals::TrackAdded::ArgumentType>::Ptr track_added; |
330 | + core::dbus::Signal<Signals::TracksAdded, Signals::TracksAdded::ArgumentType>::Ptr tracks_added; |
331 | + core::dbus::Signal<Signals::TrackMoved, Signals::TrackMoved::ArgumentType>::Ptr track_moved; |
332 | core::dbus::Signal<Signals::TrackRemoved, Signals::TrackRemoved::ArgumentType>::Ptr track_removed; |
333 | core::dbus::Signal<Signals::TrackChanged, Signals::TrackChanged::ArgumentType>::Ptr track_changed; |
334 | + core::dbus::Signal<Signals::TrackListReset, Signals::TrackListReset::ArgumentType>::Ptr track_list_reset; |
335 | core::dbus::Signal<Signals::TrackMetadataChanged, Signals::TrackMetadataChanged::ArgumentType>::Ptr track_metadata_changed; |
336 | |
337 | dbus::Signal <core::dbus::interfaces::Properties::Signals::PropertiesChanged, |
338 | |
339 | === modified file 'src/core/media/player_implementation.cpp' |
340 | --- src/core/media/player_implementation.cpp 2015-09-29 11:07:54 +0000 |
341 | +++ src/core/media/player_implementation.cpp 2015-12-23 14:19:01 +0000 |
342 | @@ -294,7 +294,8 @@ |
343 | { |
344 | // Using a TrackList for playback, added tracks via add_track(), but open_uri hasn't been called yet |
345 | // to load a media resource |
346 | - std::cout << "Calling d->engine->open_resource_for_uri() for first track added only: " << uri << std::endl; |
347 | + std::cout << "Calling d->engine->open_resource_for_uri() for first track added only: " |
348 | + << uri << std::endl; |
349 | std::cout << "\twith a Track::Id: " << id << std::endl; |
350 | static const bool do_pipeline_reset = false; |
351 | engine->open_resource_for_uri(uri, do_pipeline_reset); |
352 | @@ -375,6 +376,11 @@ |
353 | }; |
354 | Parent::position().install(position_getter); |
355 | |
356 | + d->engine->position().changed().connect([this](uint64_t position) |
357 | + { |
358 | + d->track_list->on_position_changed(position); |
359 | + }); |
360 | + |
361 | // Make sure that the Duration property gets updated from the Engine |
362 | // every time the client requests duration |
363 | std::function<uint64_t()> duration_getter = [this]() |
364 | @@ -507,8 +513,7 @@ |
365 | } |
366 | }); |
367 | |
368 | - |
369 | - d->track_list->on_go_to_track().connect([this](std::pair<const media::Track::Id, bool> p) |
370 | + d->track_list->on_go_to_track().connect([this](const media::Track::Id& id) |
371 | { |
372 | // This lambda needs to be mutually exclusive with the about_to_finish lambda above |
373 | const bool locked = d->doing_go_to_track.try_lock(); |
374 | @@ -517,11 +522,8 @@ |
375 | if (!locked) |
376 | return; |
377 | |
378 | - const media::Track::Id id = p.first; |
379 | - const bool toggle_player_state = p.second; |
380 | - |
381 | - if (toggle_player_state) |
382 | - d->engine->stop(); |
383 | + // Store whether we should restore the current playing state after loading the new uri |
384 | + const bool auto_play = Parent::playback_status().get() == media::Player::playing; |
385 | |
386 | const Track::UriType uri = d->track_list->query_uri_for_track(id); |
387 | if (!uri.empty()) |
388 | @@ -532,8 +534,11 @@ |
389 | d->engine->open_resource_for_uri(uri, do_pipeline_reset); |
390 | } |
391 | |
392 | - if (toggle_player_state) |
393 | + if (auto_play) |
394 | + { |
395 | + std::cout << "Restoring playing state in on_go_to_track()" << std::endl; |
396 | d->engine->play(); |
397 | + } |
398 | |
399 | d->doing_go_to_track.unlock(); |
400 | }); |
401 | @@ -547,11 +552,28 @@ |
402 | d->update_mpris_properties(); |
403 | }); |
404 | |
405 | + d->track_list->on_tracks_added().connect([this](const media::TrackList::ContainerURI& tracks) |
406 | + { |
407 | + std::cout << "** Track was added, handling in PlayerImplementation" << std::endl; |
408 | + // If the two sizes are the same, that means the TrackList was previously empty and we need |
409 | + // to open the first track in the TrackList so that is_audio_source() and is_video_source() |
410 | + // will function correctly. |
411 | + if (tracks.size() >= 1 and d->track_list->tracks()->size() == tracks.size()) |
412 | + d->open_first_track_from_tracklist(tracks.front()); |
413 | + |
414 | + d->update_mpris_properties(); |
415 | + }); |
416 | + |
417 | d->track_list->on_track_removed().connect([this](const media::Track::Id&) |
418 | { |
419 | d->update_mpris_properties(); |
420 | }); |
421 | |
422 | + d->track_list->on_track_list_reset().connect([this](void) |
423 | + { |
424 | + d->update_mpris_properties(); |
425 | + }); |
426 | + |
427 | d->track_list->on_track_changed().connect([this](const media::Track::Id&) |
428 | { |
429 | d->update_mpris_properties(); |
430 | @@ -664,7 +686,15 @@ |
431 | bool media::PlayerImplementation<Parent>::open_uri(const Track::UriType& uri) |
432 | { |
433 | d->track_list->reset(); |
434 | - const bool ret = d->engine->open_resource_for_uri(uri, false); |
435 | + |
436 | + // If empty uri, give the same meaning as QMediaPlayer::setMedia("") |
437 | + if (uri.empty()) { |
438 | + cout << __PRETTY_FUNCTION__ << ": resetting current media" << endl; |
439 | + return true; |
440 | + } |
441 | + |
442 | + static const bool do_pipeline_reset = false; |
443 | + const bool ret = d->engine->open_resource_for_uri(uri, do_pipeline_reset); |
444 | // Don't set new track as the current track to play since we're calling open_resource_for_uri above |
445 | static const bool make_current = false; |
446 | d->track_list->add_track_with_uri_at(uri, media::TrackList::after_empty_track(), make_current); |
447 | |
448 | === modified file 'src/core/media/service_skeleton.cpp' |
449 | --- src/core/media/service_skeleton.cpp 2015-12-18 16:24:01 +0000 |
450 | +++ src/core/media/service_skeleton.cpp 2015-12-23 14:19:01 +0000 |
451 | @@ -46,6 +46,8 @@ |
452 | namespace dbus = core::dbus; |
453 | namespace media = core::ubuntu::media; |
454 | |
455 | +using namespace std; |
456 | + |
457 | namespace |
458 | { |
459 | core::Signal<void> the_empty_signal; |
460 | @@ -132,7 +134,9 @@ |
461 | impl->access_service()->add_object_for_path(op) |
462 | }; |
463 | |
464 | - std::cout << "Session created by request of: " << msg->sender() << ", uuid: " << uuid << ", path:" << op << std::endl; |
465 | + cout << "Session created by request of: " << msg->sender() |
466 | + << ", key: " << key << ", uuid: " << uuid |
467 | + << ", path:" << op << std::endl; |
468 | |
469 | try |
470 | { |
471 | @@ -205,9 +209,11 @@ |
472 | std::string uuid; |
473 | msg->reader() >> uuid; |
474 | |
475 | - if (uuid_player_map.count(uuid) != 0) { |
476 | + if (uuid_player_map.count(uuid) != 0) |
477 | + { |
478 | auto key = uuid_player_map.at(uuid); |
479 | - if (not configuration.player_store->has_player_for_key(key)) { |
480 | + if (not configuration.player_store->has_player_for_key(key)) |
481 | + { |
482 | auto reply = dbus::Message::make_error( |
483 | msg, |
484 | mpris::Service::Errors::ReattachingSession::name(), |
485 | |
486 | === modified file 'src/core/media/track_list.cpp' |
487 | --- src/core/media/track_list.cpp 2015-07-20 20:39:42 +0000 |
488 | +++ src/core/media/track_list.cpp 2015-12-23 14:19:01 +0000 |
489 | @@ -20,6 +20,33 @@ |
490 | |
491 | namespace media = core::ubuntu::media; |
492 | |
493 | +media::TrackList::Errors::InsufficientPermissionsToAddTrack::InsufficientPermissionsToAddTrack() |
494 | + : std::runtime_error{"Insufficient client permissions for adding track to TrackList"} |
495 | +{ |
496 | +} |
497 | + |
498 | +media::TrackList::Errors::FailedToMoveTrack::FailedToMoveTrack() |
499 | + : std::runtime_error{"Failed to move track within TrackList"} |
500 | +{ |
501 | +} |
502 | + |
503 | +media::TrackList::Errors::FailedToFindMoveTrackSource::FailedToFindMoveTrackSource |
504 | + (const std::string &e) |
505 | + : std::runtime_error{e} |
506 | +{ |
507 | +} |
508 | + |
509 | +media::TrackList::Errors::FailedToFindMoveTrackDest::FailedToFindMoveTrackDest |
510 | + (const std::string &e) |
511 | + : std::runtime_error{e} |
512 | +{ |
513 | +} |
514 | + |
515 | +media::TrackList::Errors::TrackNotFound::TrackNotFound() |
516 | + : std::runtime_error{"Track not found in TrackList"} |
517 | +{ |
518 | +} |
519 | + |
520 | const media::Track::Id& media::TrackList::after_empty_track() |
521 | { |
522 | static const media::Track::Id id{"/org/mpris/MediaPlayer2/TrackList/NoTrack"}; |
523 | |
524 | === modified file 'src/core/media/track_list_implementation.cpp' |
525 | --- src/core/media/track_list_implementation.cpp 2015-09-28 13:20:01 +0000 |
526 | +++ src/core/media/track_list_implementation.cpp 2015-12-23 14:19:01 +0000 |
527 | @@ -17,9 +17,13 @@ |
528 | */ |
529 | |
530 | #include <algorithm> |
531 | +#include <random> |
532 | #include <stdio.h> |
533 | #include <stdlib.h> |
534 | #include <tuple> |
535 | +#include <unistd.h> |
536 | + |
537 | +#include <dbus/dbus.h> |
538 | |
539 | #include "track_list_implementation.h" |
540 | |
541 | @@ -38,7 +42,45 @@ |
542 | std::shared_ptr<media::Engine::MetaDataExtractor> extractor; |
543 | // Used for caching the original tracklist order to be used to restore the order |
544 | // to the live TrackList after shuffle is turned off |
545 | - media::TrackList::Container original_tracklist; |
546 | + media::TrackList::Container shuffled_tracks; |
547 | + bool shuffle; |
548 | + |
549 | + void updateCachedTrackMetadata(const media::Track::Id& id, const media::Track::UriType& uri) |
550 | + { |
551 | + if (meta_data_cache.count(id) == 0) |
552 | + { |
553 | + // FIXME: This code seems to conflict badly when called multiple times in a row: causes segfaults |
554 | +#if 0 |
555 | + try { |
556 | + meta_data_cache[id] = std::make_tuple( |
557 | + uri, |
558 | + extractor->meta_data_for_track_with_uri(uri)); |
559 | + } catch (const std::runtime_error &e) { |
560 | + std::cerr << "Failed to retrieve metadata for track '" << uri << "' (" << e.what() << ")" << std::endl; |
561 | + } |
562 | +#else |
563 | + meta_data_cache[id] = std::make_tuple( |
564 | + uri, |
565 | + core::ubuntu::media::Track::MetaData{}); |
566 | +#endif |
567 | + } else |
568 | + { |
569 | + std::get<0>(meta_data_cache[id]) = uri; |
570 | + } |
571 | + } |
572 | + |
573 | + media::TrackList::Container::iterator get_shuffled_insert_it() |
574 | + { |
575 | + media::TrackList::Container::iterator random_it = shuffled_tracks.begin(); |
576 | + if (random_it == shuffled_tracks.end()) |
577 | + return random_it; |
578 | + |
579 | + // This is slightly biased, but not much, as RAND_MAX >= 32767, which is |
580 | + // much more than the average number of tracks. |
581 | + // Note that for N tracks we have N + 1 possible insertion positions. |
582 | + std::advance(random_it, rand() % (shuffled_tracks.size() + 1)); |
583 | + return random_it; |
584 | + } |
585 | }; |
586 | |
587 | media::TrackListImplementation::TrackListImplementation( |
588 | @@ -48,7 +90,8 @@ |
589 | const media::apparmor::ubuntu::RequestContextResolver::Ptr& request_context_resolver, |
590 | const media::apparmor::ubuntu::RequestAuthenticator::Ptr& request_authenticator) |
591 | : media::TrackListSkeleton(bus, object, request_context_resolver, request_authenticator), |
592 | - d(new Private{object, 0, Private::MetaDataCache{}, extractor, media::TrackList::Container{}}) |
593 | + d(new Private{object, 0, Private::MetaDataCache{}, |
594 | + extractor, media::TrackList::Container{}, false}) |
595 | { |
596 | can_edit_tracks().set(true); |
597 | } |
598 | @@ -84,12 +127,15 @@ |
599 | { |
600 | std::cout << __PRETTY_FUNCTION__ << std::endl; |
601 | |
602 | - std::stringstream ss; ss << d->object->path().as_string() << "/" << d->track_counter++; |
603 | + std::stringstream ss; |
604 | + ss << d->object->path().as_string() << "/" << d->track_counter++; |
605 | Track::Id id{ss.str()}; |
606 | |
607 | std::cout << "Adding Track::Id: " << id << std::endl; |
608 | std::cout << "\tURI: " << uri << std::endl; |
609 | |
610 | + const auto current = get_current_track(); |
611 | + |
612 | auto result = tracks().update([this, id, position, make_current](TrackList::Container& container) |
613 | { |
614 | auto it = std::find(container.begin(), container.end(), position); |
615 | @@ -101,50 +147,180 @@ |
616 | |
617 | if (result) |
618 | { |
619 | - if (d->meta_data_cache.count(id) == 0) |
620 | - { |
621 | - // FIXME: This code seems to conflict badly when called multiple times in a row: causes segfaults |
622 | -#if 0 |
623 | - try { |
624 | - d->meta_data_cache[id] = std::make_tuple( |
625 | - uri, |
626 | - d->extractor->meta_data_for_track_with_uri(uri)); |
627 | - } catch (const std::runtime_error &e) { |
628 | - std::cerr << "Failed to retrieve metadata for track '" << uri << "' (" << e.what() << ")" << std::endl; |
629 | - } |
630 | -#else |
631 | - d->meta_data_cache[id] = std::make_tuple( |
632 | - uri, |
633 | - core::ubuntu::media::Track::MetaData{}); |
634 | -#endif |
635 | - } else |
636 | - { |
637 | - std::get<0>(d->meta_data_cache[id]) = uri; |
638 | - } |
639 | + d->updateCachedTrackMetadata(id, uri); |
640 | + |
641 | + if (d->shuffle) |
642 | + d->shuffled_tracks.insert(d->get_shuffled_insert_it(), id); |
643 | |
644 | if (make_current) |
645 | { |
646 | - // Don't automatically call stop() and play() in player_implementation.cpp on_go_to_track() |
647 | - // since this breaks video playback when using open_uri() (stop() and play() are unwanted in |
648 | - // this scenario since the qtubuntu-media will handle this automatically) |
649 | - const bool toggle_player_state = false; |
650 | - go_to(id, toggle_player_state); |
651 | + set_current_track(id); |
652 | + go_to(id); |
653 | + } else { |
654 | + set_current_track(current); |
655 | } |
656 | |
657 | - // Signal to the client that the current track has changed for the first track added to the TrackList |
658 | - if (tracks().get().size() == 1) |
659 | - on_track_changed()(id); |
660 | - |
661 | std::cout << "Signaling that we just added track id: " << id << std::endl; |
662 | // Signal to the client that a track was added to the TrackList |
663 | on_track_added()(id); |
664 | - std::cout << "Signaled that we just added track id: " << id << std::endl; |
665 | - } |
666 | + |
667 | + // Signal to the client that the current track has changed for the first |
668 | + // track added to the TrackList |
669 | + if (tracks().get().size() == 1) |
670 | + on_track_changed()(id); |
671 | + } |
672 | +} |
673 | + |
674 | +void media::TrackListImplementation::add_tracks_with_uri_at(const ContainerURI& uris, const Track::Id& position) |
675 | +{ |
676 | + std::cout << __PRETTY_FUNCTION__ << std::endl; |
677 | + |
678 | + const auto current = get_current_track(); |
679 | + |
680 | + Track::Id current_id; |
681 | + ContainerURI tmp; |
682 | + for (const auto uri : uris) |
683 | + { |
684 | + // TODO: Refactor this code to use a smaller common function shared with add_track_with_uri_at() |
685 | + std::stringstream ss; |
686 | + ss << d->object->path().as_string() << "/" << d->track_counter++; |
687 | + Track::Id id{ss.str()}; |
688 | + std::cout << "Adding Track::Id: " << id << std::endl; |
689 | + std::cout << "\tURI: " << uri << std::endl; |
690 | + |
691 | + tmp.push_back(id); |
692 | + |
693 | + Track::Id insert_position = position; |
694 | + |
695 | + auto it = std::find(tracks().get().begin(), tracks().get().end(), insert_position); |
696 | + const auto result = tracks().update([this, id, position, it, &insert_position](TrackList::Container& container) |
697 | + { |
698 | + container.insert(it, id); |
699 | + // Make sure the next insert position is after the current insert position |
700 | + // Update the Track::Id after which to insert the next one from uris |
701 | + insert_position = id; |
702 | + |
703 | + return true; |
704 | + }); |
705 | + |
706 | + if (result) |
707 | + { |
708 | + d->updateCachedTrackMetadata(id, uri); |
709 | + |
710 | + if (d->shuffle) |
711 | + d->shuffled_tracks.insert(d->get_shuffled_insert_it(), id); |
712 | + |
713 | + // Signal to the client that the current track has changed for the first track added to the TrackList |
714 | + if (tracks().get().size() == 1) |
715 | + current_id = id; |
716 | + } |
717 | + } |
718 | + |
719 | + set_current_track(current); |
720 | + |
721 | + std::cout << "Signaling that we just added " << tmp.size() << " tracks to the TrackList" << std::endl; |
722 | + on_tracks_added()(tmp); |
723 | + |
724 | + if (!current_id.empty()) |
725 | + on_track_changed()(current_id); |
726 | +} |
727 | + |
728 | +bool media::TrackListImplementation::move_track(const media::Track::Id& id, |
729 | + const media::Track::Id& to) |
730 | +{ |
731 | + std::cout << __PRETTY_FUNCTION__ << std::endl; |
732 | + |
733 | + std::cout << "-----------------------------------------------------" << std::endl; |
734 | + if (id.empty() or to.empty()) |
735 | + { |
736 | + std::cerr << "Can't move track since 'id' or 'to' are empty" << std::endl; |
737 | + return false; |
738 | + } |
739 | + |
740 | + if (id == to) |
741 | + { |
742 | + std::cerr << "Can't move track to it's same position" << std::endl; |
743 | + return false; |
744 | + } |
745 | + |
746 | + if (tracks().get().size() == 1) |
747 | + { |
748 | + std::cerr << "Can't move track since TrackList contains only one track" << std::endl; |
749 | + return false; |
750 | + } |
751 | + |
752 | + bool ret = false; |
753 | + const media::Track::Id current_id = *current_iterator(); |
754 | + std::cout << "current_track id: " << current_id << std::endl; |
755 | + // Get an iterator that points to the track that is the insertion point |
756 | + auto insert_point_it = std::find(tracks().get().begin(), tracks().get().end(), to); |
757 | + if (insert_point_it != tracks().get().end()) |
758 | + { |
759 | + const auto result = tracks().update([this, id, to, current_id, &insert_point_it] |
760 | + (TrackList::Container& container) |
761 | + { |
762 | + // Get an iterator that points to the track to move within the TrackList |
763 | + auto to_move_it = std::find(tracks().get().begin(), tracks().get().end(), id); |
764 | + std::cout << "Erasing old track position: " << *to_move_it << std::endl; |
765 | + if (to_move_it != tracks().get().end()) |
766 | + { |
767 | + container.erase(to_move_it); |
768 | + } |
769 | + else |
770 | + { |
771 | + throw media::TrackList::Errors::FailedToFindMoveTrackDest |
772 | + ("Failed to find destination track " + to); |
773 | + } |
774 | + |
775 | + // Insert id at the location just before insert_point_it |
776 | + container.insert(insert_point_it, id); |
777 | + |
778 | + const auto new_current_track_it = std::find(tracks().get().begin(), tracks().get().end(), current_id); |
779 | + if (new_current_track_it != tracks().get().end()) |
780 | + { |
781 | + const bool r = update_current_iterator(new_current_track_it); |
782 | + if (!r) |
783 | + { |
784 | + throw media::TrackList::Errors::FailedToMoveTrack(); |
785 | + } |
786 | + std::cout << "*** Updated current_iterator, id: " << *current_iterator() << std::endl; |
787 | + } |
788 | + else |
789 | + { |
790 | + std::cerr << "Can't update current_iterator - failed to find track after move" << std::endl; |
791 | + throw media::TrackList::Errors::FailedToMoveTrack(); |
792 | + } |
793 | + |
794 | + return true; |
795 | + }); |
796 | + |
797 | + if (result) |
798 | + { |
799 | + std::cout << "TrackList after move" << std::endl; |
800 | + for(auto track : tracks().get()) |
801 | + { |
802 | + std::cout << track << std::endl; |
803 | + } |
804 | + const media::TrackList::TrackIdTuple ids = std::make_tuple(id, to); |
805 | + // Signal to the client that track 'id' was moved within the TrackList |
806 | + on_track_moved()(ids); |
807 | + ret = true; |
808 | + } |
809 | + } |
810 | + else |
811 | + { |
812 | + throw media::TrackList::Errors::FailedToFindMoveTrackSource |
813 | + ("Failed to find source track " + id); |
814 | + } |
815 | + |
816 | + std::cout << "-----------------------------------------------------" << std::endl; |
817 | + |
818 | + return ret; |
819 | } |
820 | |
821 | void media::TrackListImplementation::remove_track(const media::Track::Id& id) |
822 | { |
823 | - auto result = tracks().update([id](TrackList::Container& container) |
824 | + const auto result = tracks().update([id](TrackList::Container& container) |
825 | { |
826 | container.erase(std::find(container.begin(), container.end(), id)); |
827 | return true; |
828 | @@ -156,85 +332,63 @@ |
829 | { |
830 | d->meta_data_cache.erase(id); |
831 | |
832 | + if (d->shuffle) |
833 | + d->shuffled_tracks.erase(find(d->shuffled_tracks.begin(), |
834 | + d->shuffled_tracks.end(), id)); |
835 | + |
836 | on_track_removed()(id); |
837 | + |
838 | + // Make sure playback stops if all tracks were removed |
839 | + if (tracks().get().empty()) |
840 | + on_end_of_tracklist()(); |
841 | } |
842 | - |
843 | } |
844 | |
845 | -void media::TrackListImplementation::go_to(const media::Track::Id& track, bool toggle_player_state) |
846 | +void media::TrackListImplementation::go_to(const media::Track::Id& track) |
847 | { |
848 | std::cout << __PRETTY_FUNCTION__ << std::endl; |
849 | - std::pair<const media::Track::Id, bool> p = std::make_pair(track, toggle_player_state); |
850 | // Signal the Player instance to go to a specific track for playback |
851 | - on_go_to_track()(p); |
852 | + on_go_to_track()(track); |
853 | on_track_changed()(track); |
854 | } |
855 | |
856 | -void media::TrackListImplementation::shuffle_tracks() |
857 | -{ |
858 | - std::cout << __PRETTY_FUNCTION__ << std::endl; |
859 | - |
860 | - if (tracks().get().empty()) |
861 | - return; |
862 | - |
863 | - auto result = tracks().update([this](TrackList::Container& container) |
864 | - { |
865 | - // Save off the original TrackList ordering |
866 | - d->original_tracklist.assign(container.begin(), container.end()); |
867 | - std::random_shuffle(container.begin(), container.end()); |
868 | - return true; |
869 | - }); |
870 | - |
871 | - if (result) |
872 | - { |
873 | - media::TrackList::ContainerTrackIdTuple t{std::make_tuple(tracks().get(), current())}; |
874 | - on_track_list_replaced()(t); |
875 | - } |
876 | -} |
877 | - |
878 | -void media::TrackListImplementation::unshuffle_tracks() |
879 | -{ |
880 | - std::cout << __PRETTY_FUNCTION__ << std::endl; |
881 | - |
882 | - if (tracks().get().empty() or d->original_tracklist.empty()) |
883 | - return; |
884 | - |
885 | - auto result = tracks().update([this](TrackList::Container& container) |
886 | - { |
887 | - // Restore the original TrackList ordering |
888 | - container.assign(d->original_tracklist.begin(), d->original_tracklist.end()); |
889 | - return true; |
890 | - }); |
891 | - |
892 | - if (result) |
893 | - { |
894 | - media::TrackList::ContainerTrackIdTuple t{std::make_tuple(tracks().get(), current())}; |
895 | - on_track_list_replaced()(t); |
896 | - } |
897 | +void media::TrackListImplementation::set_shuffle(bool shuffle) |
898 | +{ |
899 | + d->shuffle = shuffle; |
900 | + |
901 | + if (shuffle) { |
902 | + d->shuffled_tracks = tracks().get(); |
903 | + random_shuffle(d->shuffled_tracks.begin(), d->shuffled_tracks.end()); |
904 | + } |
905 | +} |
906 | + |
907 | +bool media::TrackListImplementation::shuffle() |
908 | +{ |
909 | + return d->shuffle; |
910 | +} |
911 | + |
912 | +const media::TrackList::Container& media::TrackListImplementation::shuffled_tracks() |
913 | +{ |
914 | + return d->shuffled_tracks; |
915 | } |
916 | |
917 | void media::TrackListImplementation::reset() |
918 | { |
919 | + std::cout << __PRETTY_FUNCTION__ << std::endl; |
920 | + |
921 | // Make sure playback stops |
922 | on_end_of_tracklist()(); |
923 | // And make sure there is no "current" track |
924 | media::TrackListSkeleton::reset(); |
925 | |
926 | - auto result = tracks().update([this](TrackList::Container& container) |
927 | + tracks().update([this](TrackList::Container& container) |
928 | { |
929 | - TrackList::Container ids = container; |
930 | - |
931 | - for (auto it = container.begin(); it != container.end(); ) { |
932 | - auto id = *it; |
933 | - it = container.erase(it); |
934 | - on_track_removed()(id); |
935 | - } |
936 | - |
937 | - container.resize(0); |
938 | + container.clear(); |
939 | + on_track_list_reset()(); |
940 | + |
941 | d->track_counter = 0; |
942 | + d->shuffled_tracks.clear(); |
943 | |
944 | return true; |
945 | }); |
946 | - |
947 | - (void) result; |
948 | } |
949 | |
950 | === modified file 'src/core/media/track_list_implementation.h' |
951 | --- src/core/media/track_list_implementation.h 2015-05-22 21:19:28 +0000 |
952 | +++ src/core/media/track_list_implementation.h 2015-12-23 14:19:01 +0000 |
953 | @@ -43,11 +43,14 @@ |
954 | Track::MetaData query_meta_data_for_track(const Track::Id& id); |
955 | |
956 | void add_track_with_uri_at(const Track::UriType& uri, const Track::Id& position, bool make_current); |
957 | + void add_tracks_with_uri_at(const ContainerURI& uris, const Track::Id& position); |
958 | + bool move_track(const Track::Id& id, const Track::Id& to); |
959 | void remove_track(const Track::Id& id); |
960 | |
961 | - void go_to(const Track::Id& track, bool toggle_player_state); |
962 | - void shuffle_tracks(); |
963 | - void unshuffle_tracks(); |
964 | + void go_to(const Track::Id& track); |
965 | + void set_shuffle(bool shuffle); |
966 | + bool shuffle(); |
967 | + const media::TrackList::Container& shuffled_tracks(); |
968 | void reset(); |
969 | |
970 | private: |
971 | |
972 | === modified file 'src/core/media/track_list_skeleton.cpp' |
973 | --- src/core/media/track_list_skeleton.cpp 2015-09-28 15:31:46 +0000 |
974 | +++ src/core/media/track_list_skeleton.cpp 2015-12-23 14:19:01 +0000 |
975 | @@ -38,10 +38,14 @@ |
976 | |
977 | #include <iostream> |
978 | #include <limits> |
979 | +#include <sstream> |
980 | +#include <cstdint> |
981 | |
982 | namespace dbus = core::dbus; |
983 | namespace media = core::ubuntu::media; |
984 | |
985 | +using namespace std; |
986 | + |
987 | struct media::TrackListSkeleton::Private |
988 | { |
989 | Private(media::TrackListSkeleton* impl, const dbus::Bus::Ptr& bus, const dbus::Object::Ptr& object, |
990 | @@ -56,11 +60,16 @@ |
991 | current_track(skeleton.properties.tracks->get().begin()), |
992 | empty_iterator(skeleton.properties.tracks->get().begin()), |
993 | loop_status(media::Player::LoopStatus::none), |
994 | + current_position(0), |
995 | + id_after_remove(), |
996 | signals |
997 | { |
998 | skeleton.signals.track_added, |
999 | + skeleton.signals.tracks_added, |
1000 | + skeleton.signals.track_moved, |
1001 | skeleton.signals.track_removed, |
1002 | skeleton.signals.track_changed, |
1003 | + skeleton.signals.track_list_reset, |
1004 | skeleton.signals.tracklist_replaced |
1005 | } |
1006 | { |
1007 | @@ -93,9 +102,12 @@ |
1008 | void handle_add_track_with_uri_at(const core::dbus::Message::Ptr& msg) |
1009 | { |
1010 | std::cout << "*** " << __PRETTY_FUNCTION__ << std::endl; |
1011 | - request_context_resolver->resolve_context_for_dbus_name_async(msg->sender(), [this, msg](const media::apparmor::ubuntu::Context& context) |
1012 | + request_context_resolver->resolve_context_for_dbus_name_async |
1013 | + (msg->sender(), [this, msg](const media::apparmor::ubuntu::Context& context) |
1014 | { |
1015 | - Track::UriType uri; media::Track::Id after; bool make_current; |
1016 | + Track::UriType uri; |
1017 | + media::Track::Id after; |
1018 | + bool make_current; |
1019 | msg->reader() >> uri >> after >> make_current; |
1020 | |
1021 | // Make sure the client has adequate apparmor permissions to open the URI |
1022 | @@ -104,13 +116,109 @@ |
1023 | auto reply = dbus::Message::make_method_return(msg); |
1024 | // Only add the track to the TrackList if it passes the apparmor permissions check |
1025 | if (std::get<0>(result)) |
1026 | + { |
1027 | impl->add_track_with_uri_at(uri, after, make_current); |
1028 | - else |
1029 | - std::cerr << "Warning: Not adding track " << uri << |
1030 | - " to TrackList because of inadequate client apparmor permissions." << std::endl; |
1031 | - |
1032 | - bus->send(reply); |
1033 | - }); |
1034 | + } |
1035 | + else |
1036 | + { |
1037 | + const std::string err_str = {"Warning: Not adding track " + uri + |
1038 | + " to TrackList because of inadequate client apparmor permissions."}; |
1039 | + std::cerr << err_str << std::endl; |
1040 | + reply = dbus::Message::make_error( |
1041 | + msg, |
1042 | + mpris::TrackList::Error::InsufficientPermissionsToAddTrack::name, |
1043 | + err_str); |
1044 | + } |
1045 | + |
1046 | + bus->send(reply); |
1047 | + }); |
1048 | + } |
1049 | + |
1050 | + void handle_add_tracks_with_uri_at(const core::dbus::Message::Ptr& msg) |
1051 | + { |
1052 | + std::cout << "*** " << __PRETTY_FUNCTION__ << std::endl; |
1053 | + request_context_resolver->resolve_context_for_dbus_name_async |
1054 | + (msg->sender(), [this, msg](const media::apparmor::ubuntu::Context& context) |
1055 | + { |
1056 | + ContainerURI uris; |
1057 | + media::Track::Id after; |
1058 | + msg->reader() >> uris >> after; |
1059 | + |
1060 | + media::apparmor::ubuntu::RequestAuthenticator::Result result; |
1061 | + std::string err_str; |
1062 | + for (const auto uri : uris) |
1063 | + { |
1064 | + // Make sure the client has adequate apparmor permissions to open the URI |
1065 | + result = request_authenticator->authenticate_open_uri_request(context, uri); |
1066 | + if (not std::get<0>(result)) |
1067 | + { |
1068 | + err_str = {"Warning: Not adding track " + uri + |
1069 | + " to TrackList because of inadequate client apparmor permissions."}; |
1070 | + break; |
1071 | + } |
1072 | + } |
1073 | + |
1074 | + core::dbus::Message::Ptr reply; |
1075 | + // Only add the track to the TrackList if it passes the apparmor permissions check |
1076 | + if (std::get<0>(result)) |
1077 | + { |
1078 | + reply = dbus::Message::make_method_return(msg); |
1079 | + impl->add_tracks_with_uri_at(uris, after); |
1080 | + } |
1081 | + else |
1082 | + { |
1083 | + std::cerr << err_str << std::endl; |
1084 | + reply = dbus::Message::make_error( |
1085 | + msg, |
1086 | + mpris::TrackList::Error::InsufficientPermissionsToAddTrack::name, |
1087 | + err_str); |
1088 | + } |
1089 | + |
1090 | + bus->send(reply); |
1091 | + }); |
1092 | + } |
1093 | + |
1094 | + void handle_move_track(const core::dbus::Message::Ptr& msg) |
1095 | + { |
1096 | + media::Track::Id id; |
1097 | + media::Track::Id to; |
1098 | + msg->reader() >> id >> to; |
1099 | + |
1100 | + core::dbus::Message::Ptr reply; |
1101 | + try { |
1102 | + const bool ret = impl->move_track(id, to); |
1103 | + if (!ret) |
1104 | + { |
1105 | + const std::string err_str = {"Error: Not moving track " + id + |
1106 | + " to destination " + to}; |
1107 | + std::cerr << err_str << std::endl; |
1108 | + reply = dbus::Message::make_error( |
1109 | + msg, |
1110 | + mpris::TrackList::Error::FailedToMoveTrack::name, |
1111 | + err_str); |
1112 | + } |
1113 | + else |
1114 | + { |
1115 | + reply = dbus::Message::make_method_return(msg); |
1116 | + } |
1117 | + } catch(media::TrackList::Errors::FailedToMoveTrack& e) { |
1118 | + reply = dbus::Message::make_error( |
1119 | + msg, |
1120 | + mpris::TrackList::Error::FailedToFindMoveTrackSource::name, |
1121 | + e.what()); |
1122 | + } catch(media::TrackList::Errors::FailedToFindMoveTrackSource& e) { |
1123 | + reply = dbus::Message::make_error( |
1124 | + msg, |
1125 | + mpris::TrackList::Error::FailedToFindMoveTrackSource::name, |
1126 | + e.what()); |
1127 | + } catch(media::TrackList::Errors::FailedToFindMoveTrackDest& e) { |
1128 | + reply = dbus::Message::make_error( |
1129 | + msg, |
1130 | + mpris::TrackList::Error::FailedToFindMoveTrackDest::name, |
1131 | + e.what()); |
1132 | + } |
1133 | + |
1134 | + bus->send(reply); |
1135 | } |
1136 | |
1137 | void handle_remove_track(const core::dbus::Message::Ptr& msg) |
1138 | @@ -118,8 +226,63 @@ |
1139 | media::Track::Id track; |
1140 | msg->reader() >> track; |
1141 | |
1142 | + auto id_it = find(impl->tracks().get().begin(), impl->tracks().get().end(), track); |
1143 | + if (id_it == impl->tracks().get().end()) { |
1144 | + ostringstream err_str; |
1145 | + err_str << "Track " << track << " not found in track list"; |
1146 | + cout << __PRETTY_FUNCTION__ << " WARNING " << err_str.str() << endl; |
1147 | + auto reply = dbus::Message::make_error( |
1148 | + msg, |
1149 | + mpris::TrackList::Error::TrackNotFound::name, |
1150 | + err_str.str()); |
1151 | + bus->send(reply); |
1152 | + return; |
1153 | + } |
1154 | + |
1155 | + media::Track::Id next; |
1156 | + bool deleting_current = false; |
1157 | + |
1158 | + if (id_it == impl->current_iterator()) |
1159 | + { |
1160 | + cout << "Removing current track" << endl; |
1161 | + deleting_current = true; |
1162 | + |
1163 | + if (current_track != empty_iterator) |
1164 | + { |
1165 | + ++current_track; |
1166 | + |
1167 | + if (current_track == impl->tracks().get().end() |
1168 | + && loop_status == media::Player::LoopStatus::playlist) |
1169 | + { |
1170 | + // Removed the last track, current is the first track and make sure that |
1171 | + // the player starts playing it |
1172 | + current_track = impl->tracks().get().begin(); |
1173 | + } |
1174 | + |
1175 | + if (current_track == impl->tracks().get().end()) |
1176 | + { |
1177 | + current_track = empty_iterator; |
1178 | + // Nothing else to play, stop playback |
1179 | + impl->emit_on_end_of_tracklist(); |
1180 | + } |
1181 | + else |
1182 | + { |
1183 | + next = *current_track; |
1184 | + } |
1185 | + } |
1186 | + } |
1187 | + else if (current_track != empty_iterator) |
1188 | + { |
1189 | + next = *current_track; |
1190 | + } |
1191 | + id_after_remove = next; |
1192 | + |
1193 | + // Calls reset_current_iterator_if_needed(), which updates the iterator |
1194 | impl->remove_track(track); |
1195 | |
1196 | + if ((not next.empty()) and deleting_current) |
1197 | + impl->go_to(next); |
1198 | + |
1199 | auto reply = dbus::Message::make_method_return(msg); |
1200 | bus->send(reply); |
1201 | } |
1202 | @@ -130,8 +293,7 @@ |
1203 | msg->reader() >> track; |
1204 | |
1205 | current_track = std::find(skeleton.properties.tracks->get().begin(), skeleton.properties.tracks->get().end(), track); |
1206 | - const bool toggle_player_state = true; |
1207 | - impl->go_to(track, toggle_player_state); |
1208 | + impl->go_to(track); |
1209 | |
1210 | auto reply = dbus::Message::make_method_return(msg); |
1211 | bus->send(reply); |
1212 | @@ -155,17 +317,28 @@ |
1213 | TrackList::ConstIterator current_track; |
1214 | TrackList::ConstIterator empty_iterator; |
1215 | media::Player::LoopStatus loop_status; |
1216 | + uint64_t current_position; |
1217 | + media::Track::Id id_after_remove; |
1218 | |
1219 | struct Signals |
1220 | { |
1221 | typedef core::dbus::Signal<mpris::TrackList::Signals::TrackAdded, mpris::TrackList::Signals::TrackAdded::ArgumentType> DBusTrackAddedSignal; |
1222 | + typedef core::dbus::Signal<mpris::TrackList::Signals::TracksAdded, mpris::TrackList::Signals::TracksAdded::ArgumentType> DBusTracksAddedSignal; |
1223 | + typedef core::dbus::Signal<mpris::TrackList::Signals::TrackMoved, mpris::TrackList::Signals::TrackMoved::ArgumentType> DBusTrackMovedSignal; |
1224 | typedef core::dbus::Signal<mpris::TrackList::Signals::TrackRemoved, mpris::TrackList::Signals::TrackRemoved::ArgumentType> DBusTrackRemovedSignal; |
1225 | typedef core::dbus::Signal<mpris::TrackList::Signals::TrackChanged, mpris::TrackList::Signals::TrackChanged::ArgumentType> DBusTrackChangedSignal; |
1226 | + typedef core::dbus::Signal< |
1227 | + mpris::TrackList::Signals::TrackListReset, |
1228 | + mpris::TrackList::Signals::TrackListReset::ArgumentType> |
1229 | + DBusTrackListResetSignal; |
1230 | typedef core::dbus::Signal<mpris::TrackList::Signals::TrackListReplaced, mpris::TrackList::Signals::TrackListReplaced::ArgumentType> DBusTrackListReplacedSignal; |
1231 | |
1232 | Signals(const std::shared_ptr<DBusTrackAddedSignal>& remote_track_added, |
1233 | + const std::shared_ptr<DBusTracksAddedSignal>& remote_tracks_added, |
1234 | + const std::shared_ptr<DBusTrackMovedSignal>& remote_track_moved, |
1235 | const std::shared_ptr<DBusTrackRemovedSignal>& remote_track_removed, |
1236 | const std::shared_ptr<DBusTrackChangedSignal>& remote_track_changed, |
1237 | + const std::shared_ptr<DBusTrackListResetSignal>& remote_track_list_reset, |
1238 | const std::shared_ptr<DBusTrackListReplacedSignal>& remote_track_list_replaced) |
1239 | { |
1240 | // Connect all of the MPRIS interface signals to be emitted over dbus |
1241 | @@ -174,11 +347,26 @@ |
1242 | remote_track_added->emit(id); |
1243 | }); |
1244 | |
1245 | + on_tracks_added.connect([remote_tracks_added](const media::TrackList::ContainerURI &tracks) |
1246 | + { |
1247 | + remote_tracks_added->emit(tracks); |
1248 | + }); |
1249 | + |
1250 | + on_track_moved.connect([remote_track_moved](const media::TrackList::TrackIdTuple &ids) |
1251 | + { |
1252 | + remote_track_moved->emit(ids); |
1253 | + }); |
1254 | + |
1255 | on_track_removed.connect([remote_track_removed](const media::Track::Id &id) |
1256 | { |
1257 | remote_track_removed->emit(id); |
1258 | }); |
1259 | |
1260 | + on_track_list_reset.connect([remote_track_list_reset]() |
1261 | + { |
1262 | + remote_track_list_reset->emit(); |
1263 | + }); |
1264 | + |
1265 | on_track_changed.connect([remote_track_changed](const media::Track::Id &id) |
1266 | { |
1267 | remote_track_changed->emit(id); |
1268 | @@ -191,10 +379,13 @@ |
1269 | } |
1270 | |
1271 | core::Signal<Track::Id> on_track_added; |
1272 | + core::Signal<TrackList::ContainerURI> on_tracks_added; |
1273 | + core::Signal<TrackList::TrackIdTuple> on_track_moved; |
1274 | core::Signal<Track::Id> on_track_removed; |
1275 | + core::Signal<void> on_track_list_reset; |
1276 | core::Signal<Track::Id> on_track_changed; |
1277 | core::Signal<TrackList::ContainerTrackIdTuple> on_track_list_replaced; |
1278 | - core::Signal<std::pair<Track::Id, bool>> on_go_to_track; |
1279 | + core::Signal<Track::Id> on_go_to_track; |
1280 | core::Signal<void> on_end_of_tracklist; |
1281 | } signals; |
1282 | }; |
1283 | @@ -219,6 +410,16 @@ |
1284 | std::ref(d), |
1285 | std::placeholders::_1)); |
1286 | |
1287 | + d->object->install_method_handler<mpris::TrackList::AddTracks>( |
1288 | + std::bind(&Private::handle_add_tracks_with_uri_at, |
1289 | + std::ref(d), |
1290 | + std::placeholders::_1)); |
1291 | + |
1292 | + d->object->install_method_handler<mpris::TrackList::MoveTrack>( |
1293 | + std::bind(&Private::handle_move_track, |
1294 | + std::ref(d), |
1295 | + std::placeholders::_1)); |
1296 | + |
1297 | d->object->install_method_handler<mpris::TrackList::RemoveTrack>( |
1298 | std::bind(&Private::handle_remove_track, |
1299 | std::ref(d), |
1300 | @@ -239,6 +440,11 @@ |
1301 | { |
1302 | } |
1303 | |
1304 | +/* |
1305 | + * NOTE We do not consider the loop status in this function due to the use of it |
1306 | + * we do in TrackListSkeleton::next() (the function is used to know whether we |
1307 | + * need to wrap when looping is active). |
1308 | + */ |
1309 | bool media::TrackListSkeleton::has_next() |
1310 | { |
1311 | const auto n_tracks = tracks().get().size(); |
1312 | @@ -252,37 +458,46 @@ |
1313 | // changed in player_implementation.cpp. |
1314 | // To avoid the crash we consider that current_track will be eventually |
1315 | // initialized to the first track when current_iterator() gets called. |
1316 | - if (d->current_track == d->empty_iterator) { |
1317 | + if (d->current_track == d->empty_iterator) |
1318 | + { |
1319 | if (n_tracks < 2) |
1320 | return false; |
1321 | else |
1322 | return true; |
1323 | } |
1324 | |
1325 | - const auto next_track = std::next(current_iterator()); |
1326 | - return !is_last_track(next_track); |
1327 | + if (shuffle()) |
1328 | + { |
1329 | + auto it = get_current_shuffled(); |
1330 | + return ++it != shuffled_tracks().end(); |
1331 | + } |
1332 | + else |
1333 | + { |
1334 | + const auto next_track = std::next(current_iterator()); |
1335 | + return !is_last_track(next_track); |
1336 | + } |
1337 | } |
1338 | |
1339 | +/* |
1340 | + * NOTE We do not consider the loop status in this function due to the use of it |
1341 | + * we do in TrackListSkeleton::previous() (the function is used to know whether we |
1342 | + * need to wrap when looping is active). |
1343 | + */ |
1344 | bool media::TrackListSkeleton::has_previous() |
1345 | { |
1346 | if (tracks().get().empty() || d->current_track == d->empty_iterator) |
1347 | return false; |
1348 | |
1349 | - // If we are looping over the entire list, then there is always a previous track |
1350 | - if (d->loop_status == media::Player::LoopStatus::playlist) |
1351 | - return true; |
1352 | - |
1353 | - return d->current_track != std::begin(tracks().get()); |
1354 | -} |
1355 | - |
1356 | -bool media::TrackListSkeleton::is_first_track(const ConstIterator &it) |
1357 | -{ |
1358 | - return it == std::begin(tracks().get()); |
1359 | -} |
1360 | - |
1361 | -bool media::TrackListSkeleton::is_last_track(const TrackList::ConstIterator &it) |
1362 | -{ |
1363 | - return it == std::end(tracks().get()); |
1364 | + if (shuffle()) |
1365 | + return get_current_shuffled() != shuffled_tracks().begin(); |
1366 | + else |
1367 | + return d->current_track != std::begin(tracks().get()); |
1368 | +} |
1369 | + |
1370 | +media::TrackList::ConstIterator media::TrackListSkeleton::get_current_shuffled() |
1371 | +{ |
1372 | + auto current_id = *current_iterator(); |
1373 | + return find(shuffled_tracks().begin(), shuffled_tracks().end(), current_id); |
1374 | } |
1375 | |
1376 | media::Track::Id media::TrackListSkeleton::next() |
1377 | @@ -294,50 +509,67 @@ |
1378 | return media::Track::Id{}; |
1379 | } |
1380 | |
1381 | - const auto next_track = std::next(current_iterator()); |
1382 | - bool do_go_to_next_track = false; |
1383 | + bool go_to_track = false; |
1384 | |
1385 | // End of the track reached so loop around to the beginning of the track |
1386 | if (d->loop_status == media::Player::LoopStatus::track) |
1387 | { |
1388 | std::cout << "Looping on the current track since LoopStatus is set to track" << std::endl; |
1389 | - do_go_to_next_track = true; |
1390 | + go_to_track = true; |
1391 | } |
1392 | // End of the tracklist reached so loop around to the beginning of the tracklist |
1393 | else if (d->loop_status == media::Player::LoopStatus::playlist && not has_next()) |
1394 | { |
1395 | std::cout << "Looping on the tracklist since LoopStatus is set to playlist" << std::endl; |
1396 | - d->current_track = tracks().get().begin(); |
1397 | - do_go_to_next_track = true; |
1398 | + |
1399 | + if (shuffle()) |
1400 | + { |
1401 | + const auto id = *shuffled_tracks().begin(); |
1402 | + set_current_track(id); |
1403 | + } |
1404 | + else |
1405 | + { |
1406 | + d->current_track = tracks().get().begin(); |
1407 | + } |
1408 | + go_to_track = true; |
1409 | } |
1410 | else |
1411 | { |
1412 | - // Next track is not the last track |
1413 | - if (not is_last_track(next_track)) |
1414 | + if (shuffle()) |
1415 | { |
1416 | - std::cout << "Advancing to next track: " << *(next_track) << std::endl; |
1417 | - d->current_track = next_track; |
1418 | - do_go_to_next_track = true; |
1419 | + auto it = get_current_shuffled(); |
1420 | + if (++it != shuffled_tracks().end()) { |
1421 | + cout << "Advancing to next track: " << *it << endl; |
1422 | + set_current_track(*it); |
1423 | + go_to_track = true; |
1424 | + } |
1425 | } |
1426 | - // At the end of the tracklist and not set to loop, so we stop advancing the tracklist |
1427 | else |
1428 | { |
1429 | - std::cout << "End of tracklist reached, not advancing to next since LoopStatus is set to none" << std::endl; |
1430 | - on_end_of_tracklist()(); |
1431 | + const auto it = std::next(current_iterator()); |
1432 | + if (not is_last_track(it)) |
1433 | + { |
1434 | + cout << "Advancing to next track: " << *it << endl; |
1435 | + d->current_track = it; |
1436 | + go_to_track = true; |
1437 | + } |
1438 | } |
1439 | + |
1440 | } |
1441 | |
1442 | - if (do_go_to_next_track) |
1443 | + if (go_to_track) |
1444 | { |
1445 | + cout << "next track id is " << *(current_iterator()) << endl; |
1446 | on_track_changed()(*(current_iterator())); |
1447 | - // Don't automatically call stop() and play() in player_implementation.cpp on_go_to_track() |
1448 | - // since this breaks video playback when using open_uri() (stop() and play() are unwanted in |
1449 | - // this scenario since the qtubuntu-media will handle this automatically) |
1450 | - const bool toggle_player_state = false; |
1451 | const media::Track::Id id = *(current_iterator()); |
1452 | - const std::pair<const media::Track::Id, bool> p = std::make_pair(id, toggle_player_state); |
1453 | // Signal the PlayerImplementation to play the next track |
1454 | - on_go_to_track()(p); |
1455 | + on_go_to_track()(id); |
1456 | + } |
1457 | + else |
1458 | + { |
1459 | + // At the end of the tracklist and not set to loop |
1460 | + cout << "End of tracklist reached" << endl; |
1461 | + on_end_of_tracklist()(); |
1462 | } |
1463 | |
1464 | return *(current_iterator()); |
1465 | @@ -352,48 +584,69 @@ |
1466 | return media::Track::Id{}; |
1467 | } |
1468 | |
1469 | - bool do_go_to_previous_track = false; |
1470 | + bool go_to_track = false; |
1471 | + // Position is measured in nanoseconds |
1472 | + const uint64_t max_position = 5 * UINT64_C(1000000000); |
1473 | |
1474 | + // If we're playing the current track for > max_position time then |
1475 | + // repeat it from the beginning |
1476 | + if (d->current_position > max_position) |
1477 | + { |
1478 | + std::cout << "Repeating current track..." << std::endl; |
1479 | + go_to_track = true; |
1480 | + } |
1481 | // Loop on the current track forever |
1482 | - if (d->loop_status == media::Player::LoopStatus::track) |
1483 | + else if (d->loop_status == media::Player::LoopStatus::track) |
1484 | { |
1485 | std::cout << "Looping on the current track..." << std::endl; |
1486 | - do_go_to_previous_track = true; |
1487 | + go_to_track = true; |
1488 | } |
1489 | // Loop over the whole playlist and repeat |
1490 | - else if (d->loop_status == media::Player::LoopStatus::playlist && is_first_track(current_iterator())) |
1491 | + else if (d->loop_status == media::Player::LoopStatus::playlist && not has_previous()) |
1492 | { |
1493 | std::cout << "Looping on the entire TrackList..." << std::endl; |
1494 | - d->current_track = std::prev(tracks().get().end()); |
1495 | - do_go_to_previous_track = true; |
1496 | + |
1497 | + if (shuffle()) |
1498 | + { |
1499 | + const auto id = *std::prev(shuffled_tracks().end()); |
1500 | + set_current_track(id); |
1501 | + } |
1502 | + else |
1503 | + { |
1504 | + d->current_track = std::prev(tracks().get().end()); |
1505 | + } |
1506 | + |
1507 | + go_to_track = true; |
1508 | } |
1509 | else |
1510 | { |
1511 | - // Current track is not the first track |
1512 | - if (not is_first_track(current_iterator())) |
1513 | + if (shuffle()) |
1514 | + { |
1515 | + auto it = get_current_shuffled(); |
1516 | + if (it != shuffled_tracks().begin()) { |
1517 | + set_current_track(*(--it)); |
1518 | + go_to_track = true; |
1519 | + } |
1520 | + } |
1521 | + else if (not is_first_track(current_iterator())) |
1522 | { |
1523 | // Keep returning the previous track until the first track is reached |
1524 | d->current_track = std::prev(current_iterator()); |
1525 | - do_go_to_previous_track = true; |
1526 | - } |
1527 | - // At the beginning of the tracklist and not set to loop, so we stop advancing the tracklist |
1528 | - else |
1529 | - { |
1530 | - std::cout << "Beginning of tracklist reached, not advancing to previous since LoopStatus is set to none" << std::endl; |
1531 | - on_end_of_tracklist()(); |
1532 | + go_to_track = true; |
1533 | } |
1534 | } |
1535 | |
1536 | - if (do_go_to_previous_track) |
1537 | + if (go_to_track) |
1538 | { |
1539 | on_track_changed()(*(current_iterator())); |
1540 | - // Don't automatically call stop() and play() in player_implementation.cpp on_go_to_track() |
1541 | - // since this breaks video playback when using open_uri() (stop() and play() are unwanted in |
1542 | - // this scenario since the qtubuntu-media will handle this automatically) |
1543 | - const bool toggle_player_state = false; |
1544 | const media::Track::Id id = *(current_iterator()); |
1545 | - const std::pair<const media::Track::Id, bool> p = std::make_pair(id, toggle_player_state); |
1546 | - on_go_to_track()(p); |
1547 | + on_go_to_track()(id); |
1548 | + } |
1549 | + else |
1550 | + { |
1551 | + // At the beginning of the tracklist and not set to loop |
1552 | + cout << "Beginning of tracklist reached" << endl; |
1553 | + on_end_of_tracklist()(); |
1554 | } |
1555 | |
1556 | return *(current_iterator()); |
1557 | @@ -421,14 +674,45 @@ |
1558 | return d->current_track; |
1559 | } |
1560 | |
1561 | +bool media::TrackListSkeleton::update_current_iterator(const TrackList::ConstIterator &it) |
1562 | +{ |
1563 | + std::cout << __PRETTY_FUNCTION__ << std::endl; |
1564 | + if (it == tracks().get().end()) |
1565 | + return false; |
1566 | + |
1567 | + std::cout << "Updating current_track iterator" << std::endl; |
1568 | + d->current_track = it; |
1569 | + |
1570 | + return true; |
1571 | +} |
1572 | + |
1573 | void media::TrackListSkeleton::reset_current_iterator_if_needed() |
1574 | { |
1575 | - // If all tracks got removed then we need to keep a sane current |
1576 | - // iterator for further use. |
1577 | - if (tracks().get().empty()) |
1578 | + d->current_track = find(tracks().get().begin(), tracks().get().end(), d->id_after_remove); |
1579 | + if (d->current_track == tracks().get().end()) |
1580 | d->current_track = d->empty_iterator; |
1581 | } |
1582 | |
1583 | +media::Track::Id media::TrackListSkeleton::get_current_track(void) |
1584 | +{ |
1585 | + if (d->current_track == d->empty_iterator || tracks().get().empty()) |
1586 | + return media::Track::Id{}; |
1587 | + |
1588 | + return *(current_iterator()); |
1589 | +} |
1590 | + |
1591 | +void media::TrackListSkeleton::set_current_track(const media::Track::Id& id) |
1592 | +{ |
1593 | + const auto id_it = find(tracks().get().begin(), tracks().get().end(), id); |
1594 | + if (id_it != tracks().get().end()) |
1595 | + d->current_track = id_it; |
1596 | +} |
1597 | + |
1598 | +void media::TrackListSkeleton::emit_on_end_of_tracklist() |
1599 | +{ |
1600 | + on_end_of_tracklist()(); |
1601 | +} |
1602 | + |
1603 | const core::Property<bool>& media::TrackListSkeleton::can_edit_tracks() const |
1604 | { |
1605 | return *d->skeleton.properties.can_edit_tracks; |
1606 | @@ -444,6 +728,11 @@ |
1607 | return *d->skeleton.properties.tracks; |
1608 | } |
1609 | |
1610 | +void media::TrackListSkeleton::on_position_changed(uint64_t position) |
1611 | +{ |
1612 | + d->current_position = position; |
1613 | +} |
1614 | + |
1615 | void media::TrackListSkeleton::on_loop_status_changed(const media::Player::LoopStatus& loop_status) |
1616 | { |
1617 | d->loop_status = loop_status; |
1618 | @@ -456,21 +745,9 @@ |
1619 | |
1620 | void media::TrackListSkeleton::on_shuffle_changed(bool shuffle) |
1621 | { |
1622 | - if (shuffle) |
1623 | - shuffle_tracks(); |
1624 | - else |
1625 | - { |
1626 | - // Save the current Track::Id of what's currently playing to restore after unshuffle |
1627 | - const media::Track::Id current_id = *(current_iterator()); |
1628 | - |
1629 | - unshuffle_tracks(); |
1630 | - |
1631 | - // Since we use assign() in unshuffle_tracks, which invalidates existing iterators, we need |
1632 | - // to make sure that current is pointing to the right place |
1633 | - auto it = std::find(tracks().get().begin(), tracks().get().end(), current_id); |
1634 | - if (it != tracks().get().end()) |
1635 | - d->current_track = it; |
1636 | - } |
1637 | + cout << __PRETTY_FUNCTION__ << endl; |
1638 | + |
1639 | + set_shuffle(shuffle); |
1640 | } |
1641 | |
1642 | const core::Property<media::TrackList::Container>& media::TrackListSkeleton::tracks() const |
1643 | @@ -490,17 +767,32 @@ |
1644 | return d->signals.on_track_added; |
1645 | } |
1646 | |
1647 | +const core::Signal<media::TrackList::ContainerURI>& media::TrackListSkeleton::on_tracks_added() const |
1648 | +{ |
1649 | + return d->signals.on_tracks_added; |
1650 | +} |
1651 | + |
1652 | +const core::Signal<media::TrackList::TrackIdTuple>& media::TrackListSkeleton::on_track_moved() const |
1653 | +{ |
1654 | + return d->signals.on_track_moved; |
1655 | +} |
1656 | + |
1657 | const core::Signal<media::Track::Id>& media::TrackListSkeleton::on_track_removed() const |
1658 | { |
1659 | return d->signals.on_track_removed; |
1660 | } |
1661 | |
1662 | +const core::Signal<void>& media::TrackListSkeleton::on_track_list_reset() const |
1663 | +{ |
1664 | + return d->signals.on_track_list_reset; |
1665 | +} |
1666 | + |
1667 | const core::Signal<media::Track::Id>& media::TrackListSkeleton::on_track_changed() const |
1668 | { |
1669 | return d->signals.on_track_changed; |
1670 | } |
1671 | |
1672 | -const core::Signal<std::pair<media::Track::Id, bool>>& media::TrackListSkeleton::on_go_to_track() const |
1673 | +const core::Signal<media::Track::Id>& media::TrackListSkeleton::on_go_to_track() const |
1674 | { |
1675 | return d->signals.on_go_to_track; |
1676 | } |
1677 | @@ -520,17 +812,32 @@ |
1678 | return d->signals.on_track_added; |
1679 | } |
1680 | |
1681 | +core::Signal<media::TrackList::ContainerURI>& media::TrackListSkeleton::on_tracks_added() |
1682 | +{ |
1683 | + return d->signals.on_tracks_added; |
1684 | +} |
1685 | + |
1686 | +core::Signal<media::TrackList::TrackIdTuple>& media::TrackListSkeleton::on_track_moved() |
1687 | +{ |
1688 | + return d->signals.on_track_moved; |
1689 | +} |
1690 | + |
1691 | core::Signal<media::Track::Id>& media::TrackListSkeleton::on_track_removed() |
1692 | { |
1693 | return d->signals.on_track_removed; |
1694 | } |
1695 | |
1696 | +core::Signal<void>& media::TrackListSkeleton::on_track_list_reset() |
1697 | +{ |
1698 | + return d->signals.on_track_list_reset; |
1699 | +} |
1700 | + |
1701 | core::Signal<media::Track::Id>& media::TrackListSkeleton::on_track_changed() |
1702 | { |
1703 | return d->signals.on_track_changed; |
1704 | } |
1705 | |
1706 | -core::Signal<std::pair<media::Track::Id, bool>>& media::TrackListSkeleton::on_go_to_track() |
1707 | +core::Signal<media::Track::Id>& media::TrackListSkeleton::on_go_to_track() |
1708 | { |
1709 | return d->signals.on_go_to_track; |
1710 | } |
1711 | |
1712 | === modified file 'src/core/media/track_list_skeleton.h' |
1713 | --- src/core/media/track_list_skeleton.h 2015-09-28 13:20:01 +0000 |
1714 | +++ src/core/media/track_list_skeleton.h 2015-12-23 14:19:01 +0000 |
1715 | @@ -54,31 +54,51 @@ |
1716 | core::Signal<ContainerTrackIdTuple>& on_track_list_replaced(); |
1717 | const core::Signal<Track::Id>& on_track_added() const; |
1718 | core::Signal<Track::Id>& on_track_added(); |
1719 | + const core::Signal<ContainerURI>& on_tracks_added() const; |
1720 | + core::Signal<ContainerURI>& on_tracks_added(); |
1721 | + const core::Signal<TrackIdTuple>& on_track_moved() const; |
1722 | + core::Signal<TrackIdTuple>& on_track_moved(); |
1723 | const core::Signal<Track::Id>& on_track_removed() const; |
1724 | + const core::Signal<void>& on_track_list_reset() const; |
1725 | const core::Signal<Track::Id>& on_track_changed() const; |
1726 | core::Signal<Track::Id>& on_track_changed(); |
1727 | - const core::Signal<std::pair<Track::Id, bool>>& on_go_to_track() const; |
1728 | - core::Signal<std::pair<Track::Id, bool>>& on_go_to_track(); |
1729 | + const core::Signal<Track::Id>& on_go_to_track() const; |
1730 | + core::Signal<Track::Id>& on_go_to_track(); |
1731 | const core::Signal<void>& on_end_of_tracklist() const; |
1732 | core::Signal<void>& on_end_of_tracklist(); |
1733 | core::Signal<Track::Id>& on_track_removed(); |
1734 | + core::Signal<void>& on_track_list_reset(); |
1735 | |
1736 | core::Property<Container>& tracks(); |
1737 | void on_loop_status_changed(const core::ubuntu::media::Player::LoopStatus& loop_status); |
1738 | core::ubuntu::media::Player::LoopStatus loop_status() const; |
1739 | |
1740 | + void on_position_changed(uint64_t position); |
1741 | + |
1742 | /** Gets called when the shuffle property on the Player interface is changed |
1743 | * by the client */ |
1744 | void on_shuffle_changed(bool shuffle); |
1745 | |
1746 | + virtual void set_shuffle(bool shuffle) = 0; |
1747 | + virtual bool shuffle() = 0; |
1748 | + virtual const media::TrackList::Container& shuffled_tracks() = 0; |
1749 | + |
1750 | protected: |
1751 | - inline bool is_first_track(const ConstIterator &it); |
1752 | - inline bool is_last_track(const ConstIterator &it); |
1753 | - inline const TrackList::ConstIterator& current_iterator(); |
1754 | + inline bool is_first_track(const ConstIterator &it) |
1755 | + { return it == std::begin(tracks().get()); } |
1756 | + inline bool is_last_track(const ConstIterator &it) |
1757 | + { return it == std::end(tracks().get()); } |
1758 | + const TrackList::ConstIterator& current_iterator(); |
1759 | + bool update_current_iterator(const TrackList::ConstIterator &it); |
1760 | void reset_current_iterator_if_needed(); |
1761 | + media::Track::Id get_current_track(void); |
1762 | + void set_current_track(const media::Track::Id& id); |
1763 | + TrackList::ConstIterator get_current_shuffled(); |
1764 | |
1765 | core::Property<bool>& can_edit_tracks(); |
1766 | |
1767 | + void emit_on_end_of_tracklist(); |
1768 | + |
1769 | void reset(); |
1770 | |
1771 | private: |
1772 | |
1773 | === modified file 'src/core/media/track_list_stub.cpp' |
1774 | --- src/core/media/track_list_stub.cpp 2015-07-27 22:15:33 +0000 |
1775 | +++ src/core/media/track_list_stub.cpp 2015-12-23 14:19:01 +0000 |
1776 | @@ -52,7 +52,10 @@ |
1777 | signals |
1778 | { |
1779 | object->get_signal<mpris::TrackList::Signals::TrackAdded>(), |
1780 | + object->get_signal<mpris::TrackList::Signals::TracksAdded>(), |
1781 | + object->get_signal<mpris::TrackList::Signals::TrackMoved>(), |
1782 | object->get_signal<mpris::TrackList::Signals::TrackRemoved>(), |
1783 | + object->get_signal<mpris::TrackList::Signals::TrackListReset>(), |
1784 | object->get_signal<mpris::TrackList::Signals::TrackListReplaced>(), |
1785 | object->get_signal<mpris::TrackList::Signals::TrackChanged>() |
1786 | } |
1787 | @@ -70,22 +73,37 @@ |
1788 | struct Signals |
1789 | { |
1790 | typedef core::dbus::Signal<mpris::TrackList::Signals::TrackAdded, mpris::TrackList::Signals::TrackAdded::ArgumentType> DBusTrackAddedSignal; |
1791 | + typedef core::dbus::Signal<mpris::TrackList::Signals::TracksAdded, mpris::TrackList::Signals::TracksAdded::ArgumentType> DBusTracksAddedSignal; |
1792 | + typedef core::dbus::Signal<mpris::TrackList::Signals::TrackMoved, mpris::TrackList::Signals::TrackMoved::ArgumentType> DBusTrackMovedSignal; |
1793 | typedef core::dbus::Signal<mpris::TrackList::Signals::TrackRemoved, mpris::TrackList::Signals::TrackRemoved::ArgumentType> DBusTrackRemovedSignal; |
1794 | + typedef core::dbus::Signal< |
1795 | + mpris::TrackList::Signals::TrackListReset, |
1796 | + mpris::TrackList::Signals::TrackListReset::ArgumentType> |
1797 | + DBusTrackListResetSignal; |
1798 | typedef core::dbus::Signal<mpris::TrackList::Signals::TrackListReplaced, mpris::TrackList::Signals::TrackListReplaced::ArgumentType> DBusTrackListReplacedSignal; |
1799 | typedef core::dbus::Signal<mpris::TrackList::Signals::TrackChanged, mpris::TrackList::Signals::TrackChanged::ArgumentType> DBusTrackChangedSignal; |
1800 | |
1801 | Signals(const std::shared_ptr<DBusTrackAddedSignal>& track_added, |
1802 | + const std::shared_ptr<DBusTracksAddedSignal>& tracks_added, |
1803 | + const std::shared_ptr<DBusTrackMovedSignal>& track_moved, |
1804 | const std::shared_ptr<DBusTrackRemovedSignal>& track_removed, |
1805 | + const std::shared_ptr<DBusTrackListResetSignal>& track_list_reset, |
1806 | const std::shared_ptr<DBusTrackListReplacedSignal>& track_list_replaced, |
1807 | const std::shared_ptr<DBusTrackChangedSignal>& track_changed) |
1808 | : on_track_added(), |
1809 | + on_tracks_added(), |
1810 | + on_track_moved(), |
1811 | on_track_removed(), |
1812 | + on_track_list_reset(), |
1813 | on_track_list_replaced(), |
1814 | on_track_changed(), |
1815 | dbus |
1816 | { |
1817 | track_added, |
1818 | + tracks_added, |
1819 | + track_moved, |
1820 | track_removed, |
1821 | + track_list_reset, |
1822 | track_list_replaced, |
1823 | track_changed, |
1824 | } |
1825 | @@ -96,15 +114,33 @@ |
1826 | on_track_added(id); |
1827 | }); |
1828 | |
1829 | + dbus.on_tracks_added->connect([this](const media::TrackList::ContainerURI& tracks) |
1830 | + { |
1831 | + std::cout << "OnTracksAdded signal arrived via the bus." << std::endl; |
1832 | + on_tracks_added(tracks); |
1833 | + }); |
1834 | + |
1835 | + dbus.on_track_moved->connect([this](const media::TrackList::TrackIdTuple& ids) |
1836 | + { |
1837 | + std::cout << "OnTrackMoved signal arrived via the bus." << std::endl; |
1838 | + on_track_moved(ids); |
1839 | + }); |
1840 | + |
1841 | dbus.on_track_removed->connect([this](const Track::Id& id) |
1842 | { |
1843 | std::cout << "OnTrackRemoved signal arrived via the bus." << std::endl; |
1844 | on_track_removed(id); |
1845 | }); |
1846 | |
1847 | + dbus.on_track_list_reset->connect([this](void) |
1848 | + { |
1849 | + std::cout << "OnTrackListReset signal arrived via the bus." << std::endl; |
1850 | + on_track_list_reset(); |
1851 | + }); |
1852 | + |
1853 | dbus.on_track_list_replaced->connect([this](const media::TrackList::ContainerTrackIdTuple& list) |
1854 | { |
1855 | - std::cout << "OnTrackListRemoved signal arrived via the bus." << std::endl; |
1856 | + std::cout << "OnTrackListReplaced signal arrived via the bus." << std::endl; |
1857 | on_track_list_replaced(list); |
1858 | }); |
1859 | |
1860 | @@ -116,16 +152,22 @@ |
1861 | } |
1862 | |
1863 | core::Signal<Track::Id> on_track_added; |
1864 | + core::Signal<media::TrackList::ContainerURI> on_tracks_added; |
1865 | + core::Signal<media::TrackList::TrackIdTuple> on_track_moved; |
1866 | core::Signal<Track::Id> on_track_removed; |
1867 | + core::Signal<void> on_track_list_reset; |
1868 | core::Signal<media::TrackList::ContainerTrackIdTuple> on_track_list_replaced; |
1869 | core::Signal<Track::Id> on_track_changed; |
1870 | - core::Signal<std::pair<Track::Id, bool>> on_go_to_track; |
1871 | + core::Signal<Track::Id> on_go_to_track; |
1872 | core::Signal<void> on_end_of_tracklist; |
1873 | |
1874 | struct DBus |
1875 | { |
1876 | std::shared_ptr<DBusTrackAddedSignal> on_track_added; |
1877 | + std::shared_ptr<DBusTracksAddedSignal> on_tracks_added; |
1878 | + std::shared_ptr<DBusTrackMovedSignal> on_track_moved; |
1879 | std::shared_ptr<DBusTrackRemovedSignal> on_track_removed; |
1880 | + std::shared_ptr<DBusTrackListResetSignal> on_track_list_reset; |
1881 | std::shared_ptr<DBusTrackListReplacedSignal> on_track_list_replaced; |
1882 | std::shared_ptr<DBusTrackChangedSignal> on_track_changed; |
1883 | } dbus; |
1884 | @@ -184,16 +226,62 @@ |
1885 | |
1886 | void media::TrackListStub::add_track_with_uri_at( |
1887 | const media::Track::UriType& uri, |
1888 | - const media::Track::Id& id, |
1889 | + const media::Track::Id& position, |
1890 | bool make_current) |
1891 | { |
1892 | auto op = d->object->invoke_method_synchronously<mpris::TrackList::AddTrack, void>( |
1893 | uri, |
1894 | - id, |
1895 | + position, |
1896 | make_current); |
1897 | |
1898 | if (op.is_error()) |
1899 | - throw std::runtime_error("Problem adding track: " + op.error()); |
1900 | + { |
1901 | + if (op.error().name() == |
1902 | + mpris::TrackList::Error::InsufficientPermissionsToAddTrack::name) |
1903 | + throw media::TrackList::Errors::InsufficientPermissionsToAddTrack{}; |
1904 | + else |
1905 | + throw std::runtime_error{op.error().print()}; |
1906 | + } |
1907 | +} |
1908 | + |
1909 | +void media::TrackListStub::add_tracks_with_uri_at(const ContainerURI& uris, const Track::Id& position) |
1910 | +{ |
1911 | + auto op = d->object->invoke_method_synchronously<mpris::TrackList::AddTracks, void>( |
1912 | + uris, |
1913 | + position); |
1914 | + |
1915 | + if (op.is_error()) |
1916 | + { |
1917 | + if (op.error().name() == |
1918 | + mpris::TrackList::Error::InsufficientPermissionsToAddTrack::name) |
1919 | + throw media::TrackList::Errors::InsufficientPermissionsToAddTrack{}; |
1920 | + else |
1921 | + throw std::runtime_error{op.error().print()}; |
1922 | + } |
1923 | +} |
1924 | + |
1925 | +bool media::TrackListStub::move_track(const media::Track::Id& id, const media::Track::Id& to) |
1926 | +{ |
1927 | + auto op = d->object->invoke_method_synchronously<mpris::TrackList::MoveTrack, void>(id, to); |
1928 | + |
1929 | + if (op.is_error()) |
1930 | + { |
1931 | + if (op.error().name() == |
1932 | + mpris::TrackList::Error::FailedToMoveTrack::name) |
1933 | + throw media::TrackList::Errors::FailedToMoveTrack{}; |
1934 | + else if (op.error().name() == |
1935 | + mpris::TrackList::Error::FailedToFindMoveTrackSource::name) |
1936 | + throw media::TrackList::Errors::FailedToFindMoveTrackSource{op.error().print()}; |
1937 | + else if (op.error().name() == |
1938 | + mpris::TrackList::Error::FailedToFindMoveTrackDest::name) |
1939 | + throw media::TrackList::Errors::FailedToFindMoveTrackDest{op.error().print()}; |
1940 | + else |
1941 | + throw std::runtime_error{op.error().print()}; |
1942 | + |
1943 | + return false; |
1944 | + } |
1945 | + |
1946 | + return true; |
1947 | } |
1948 | |
1949 | void media::TrackListStub::remove_track(const media::Track::Id& track) |
1950 | @@ -202,14 +290,18 @@ |
1951 | track); |
1952 | |
1953 | if (op.is_error()) |
1954 | - throw std::runtime_error("Problem removing track: " + op.error()); |
1955 | + { |
1956 | + if (op.error().name() == |
1957 | + mpris::TrackList::Error::TrackNotFound::name) |
1958 | + throw media::TrackList::Errors::TrackNotFound{}; |
1959 | + else |
1960 | + throw std::runtime_error{"Problem removing track: " + op.error().print()}; |
1961 | + } |
1962 | } |
1963 | |
1964 | -void media::TrackListStub::go_to(const media::Track::Id& track, bool toggle_player_state) |
1965 | +void media::TrackListStub::go_to(const media::Track::Id& track) |
1966 | { |
1967 | - (void) toggle_player_state; |
1968 | - auto op = d->object->invoke_method_synchronously<mpris::TrackList::GoTo, void>( |
1969 | - track); |
1970 | + auto op = d->object->invoke_method_synchronously<mpris::TrackList::GoTo, void>(track); |
1971 | |
1972 | if (op.is_error()) |
1973 | throw std::runtime_error("Problem adding track: " + op.error()); |
1974 | @@ -227,16 +319,6 @@ |
1975 | return media::Track::Id{"/empty/track/id"}; |
1976 | } |
1977 | |
1978 | -void media::TrackListStub::shuffle_tracks() |
1979 | -{ |
1980 | - std::cerr << "shuffle_tracks() does nothing from the client side" << std::endl; |
1981 | -} |
1982 | - |
1983 | -void media::TrackListStub::unshuffle_tracks() |
1984 | -{ |
1985 | - std::cerr << "unshuffle_tracks() does nothing from the client side" << std::endl; |
1986 | -} |
1987 | - |
1988 | void media::TrackListStub::reset() |
1989 | { |
1990 | auto op = d->object->invoke_method_synchronously<mpris::TrackList::Reset, void>(); |
1991 | @@ -255,17 +337,32 @@ |
1992 | return d->signals.on_track_added; |
1993 | } |
1994 | |
1995 | +const core::Signal<media::TrackList::ContainerURI>& media::TrackListStub::on_tracks_added() const |
1996 | +{ |
1997 | + return d->signals.on_tracks_added; |
1998 | +} |
1999 | + |
2000 | +const core::Signal<media::TrackList::TrackIdTuple>& media::TrackListStub::on_track_moved() const |
2001 | +{ |
2002 | + return d->signals.on_track_moved; |
2003 | +} |
2004 | + |
2005 | const core::Signal<media::Track::Id>& media::TrackListStub::on_track_removed() const |
2006 | { |
2007 | return d->signals.on_track_removed; |
2008 | } |
2009 | |
2010 | +const core::Signal<void>& media::TrackListStub::on_track_list_reset() const |
2011 | +{ |
2012 | + return d->signals.on_track_list_reset; |
2013 | +} |
2014 | + |
2015 | const core::Signal<media::Track::Id>& media::TrackListStub::on_track_changed() const |
2016 | { |
2017 | return d->signals.on_track_changed; |
2018 | } |
2019 | |
2020 | -const core::Signal<std::pair<media::Track::Id, bool>>& media::TrackListStub::on_go_to_track() const |
2021 | +const core::Signal<media::Track::Id>& media::TrackListStub::on_go_to_track() const |
2022 | { |
2023 | return d->signals.on_go_to_track; |
2024 | } |
2025 | |
2026 | === modified file 'src/core/media/track_list_stub.h' |
2027 | --- src/core/media/track_list_stub.h 2015-07-20 20:39:42 +0000 |
2028 | +++ src/core/media/track_list_stub.h 2015-12-23 14:19:01 +0000 |
2029 | @@ -48,23 +48,25 @@ |
2030 | Track::UriType query_uri_for_track(const Track::Id& id); |
2031 | |
2032 | void add_track_with_uri_at(const Track::UriType& uri, const Track::Id& position, bool make_current); |
2033 | + void add_tracks_with_uri_at(const ContainerURI& uris, const Track::Id& position); |
2034 | + bool move_track(const Track::Id& id, const Track::Id& to); |
2035 | void remove_track(const Track::Id& id); |
2036 | |
2037 | - void go_to(const Track::Id& track, bool toggle_player_state); |
2038 | + void go_to(const Track::Id& track); |
2039 | |
2040 | Track::Id next(); |
2041 | Track::Id previous(); |
2042 | |
2043 | - void shuffle_tracks(); |
2044 | - void unshuffle_tracks(); |
2045 | - |
2046 | void reset(); |
2047 | |
2048 | const core::Signal<ContainerTrackIdTuple>& on_track_list_replaced() const; |
2049 | const core::Signal<Track::Id>& on_track_added() const; |
2050 | + const core::Signal<ContainerURI>& on_tracks_added() const; |
2051 | + const core::Signal<TrackIdTuple>& on_track_moved() const; |
2052 | const core::Signal<Track::Id>& on_track_removed() const; |
2053 | + const core::Signal<void>& on_track_list_reset() const; |
2054 | const core::Signal<Track::Id>& on_track_changed() const; |
2055 | - const core::Signal<std::pair<Track::Id, bool>>& on_go_to_track() const; |
2056 | + const core::Signal<Track::Id>& on_go_to_track() const; |
2057 | const core::Signal<void>& on_end_of_tracklist() const; |
2058 | |
2059 | private: |
2060 | |
2061 | === modified file 'tests/CMakeLists.txt' |
2062 | --- tests/CMakeLists.txt 2015-03-25 14:30:47 +0000 |
2063 | +++ tests/CMakeLists.txt 2015-12-23 14:19:01 +0000 |
2064 | @@ -50,6 +50,6 @@ |
2065 | "COM_UBUNTU_MEDIA_SERVICE_AUDIO_SINK_NAME=fakesink" |
2066 | ) |
2067 | |
2068 | -# add_subdirectory(acceptance-tests) |
2069 | +#add_subdirectory(acceptance-tests) |
2070 | add_subdirectory(test-track-list) |
2071 | add_subdirectory(unit-tests) |
2072 | |
2073 | === modified file 'tests/acceptance-tests/service.cpp' |
2074 | --- tests/acceptance-tests/service.cpp 2015-03-03 22:41:22 +0000 |
2075 | +++ tests/acceptance-tests/service.cpp 2015-12-23 14:19:01 +0000 |
2076 | @@ -1,5 +1,5 @@ |
2077 | /* |
2078 | - * Copyright © 2013-2014 Canonical Ltd. |
2079 | + * Copyright © 2013-2015 Canonical Ltd. |
2080 | * |
2081 | * This program is free software: you can redistribute it and/or modify it |
2082 | * under the terms of the GNU Lesser General Public License version 3, |
2083 | @@ -26,15 +26,13 @@ |
2084 | |
2085 | #include "test_data.h" |
2086 | |
2087 | -#include <core/testing/cross_process_sync.h> |
2088 | -#include <core/testing/fork_and_run.h> |
2089 | - |
2090 | #include <gtest/gtest.h> |
2091 | |
2092 | #include <cstdio> |
2093 | |
2094 | #include <condition_variable> |
2095 | #include <functional> |
2096 | +#include <iostream> |
2097 | #include <thread> |
2098 | |
2099 | namespace media = core::ubuntu::media; |
2100 | @@ -70,382 +68,121 @@ |
2101 | } |
2102 | } |
2103 | |
2104 | -TEST(MusicService, DISABLED_accessing_and_creating_a_session_works) |
2105 | -{ |
2106 | - core::testing::CrossProcessSync sync_service_start; |
2107 | - |
2108 | - auto service = [this, &sync_service_start]() |
2109 | - { |
2110 | - SigTermCatcher sc; |
2111 | - |
2112 | - auto service = std::make_shared<media::ServiceImplementation>(); |
2113 | - std::thread t([&service](){service->run();}); |
2114 | - |
2115 | - sync_service_start.try_signal_ready_for(std::chrono::milliseconds{500}); |
2116 | - |
2117 | - sc.wait_for_signal(); service->stop(); |
2118 | - |
2119 | - if (t.joinable()) |
2120 | - t.join(); |
2121 | - |
2122 | - return ::testing::Test::HasFailure() ? core::posix::exit::Status::failure : core::posix::exit::Status::success; |
2123 | - }; |
2124 | - |
2125 | - auto client = [this, &sync_service_start]() |
2126 | - { |
2127 | - sync_service_start.wait_for_signal_ready_for(std::chrono::milliseconds{500}); |
2128 | - |
2129 | - auto service = media::Service::Client::instance(); |
2130 | - auto session = service->create_session(media::Player::Client::default_configuration()); |
2131 | - |
2132 | - EXPECT_TRUE(session != nullptr); |
2133 | - |
2134 | - return ::testing::Test::HasFailure() ? core::posix::exit::Status::failure : core::posix::exit::Status::success; |
2135 | - }; |
2136 | - |
2137 | - EXPECT_EQ(core::testing::ForkAndRunResult::empty, |
2138 | - core::testing::fork_and_run(service, client)); |
2139 | -} |
2140 | - |
2141 | -TEST(MusicService, DISABLED_accessing_and_creating_a_fixed_session_works) |
2142 | -{ |
2143 | - core::testing::CrossProcessSync sync_service_start; |
2144 | - |
2145 | - auto service = [this, &sync_service_start]() |
2146 | - { |
2147 | - SigTermCatcher sc; |
2148 | - |
2149 | - auto service = std::make_shared<media::ServiceImplementation>(); |
2150 | - std::thread t([&service](){service->run();}); |
2151 | - |
2152 | - sync_service_start.try_signal_ready_for(std::chrono::milliseconds{500}); |
2153 | - |
2154 | - sc.wait_for_signal(); service->stop(); |
2155 | - |
2156 | - if (t.joinable()) |
2157 | - t.join(); |
2158 | - |
2159 | - return ::testing::Test::HasFailure() ? core::posix::exit::Status::failure : core::posix::exit::Status::success; |
2160 | - }; |
2161 | - |
2162 | - auto client = [this, &sync_service_start]() |
2163 | - { |
2164 | - sync_service_start.wait_for_signal_ready_for(std::chrono::milliseconds{500}); |
2165 | - |
2166 | - auto service = media::Service::Client::instance(); |
2167 | - auto session = service->create_fixed_session("com.ubuntu.test-session", media::Player::Client::default_configuration()); |
2168 | - |
2169 | - EXPECT_TRUE(session != nullptr); |
2170 | - |
2171 | - return ::testing::Test::HasFailure() ? core::posix::exit::Status::failure : core::posix::exit::Status::success; |
2172 | - }; |
2173 | - |
2174 | - EXPECT_EQ(core::testing::ForkAndRunResult::empty, |
2175 | - core::testing::fork_and_run(service, client)); |
2176 | -} |
2177 | - |
2178 | -TEST(MusicService, DISABLED_resuming_a_session_works) |
2179 | -{ |
2180 | - core::testing::CrossProcessSync sync_service_start; |
2181 | - |
2182 | - auto service = [this, &sync_service_start]() |
2183 | - { |
2184 | - SigTermCatcher sc; |
2185 | - |
2186 | - auto service = std::make_shared<media::ServiceImplementation>(); |
2187 | - std::thread t([&service](){service->run();}); |
2188 | - |
2189 | - sync_service_start.try_signal_ready_for(std::chrono::milliseconds{500}); |
2190 | - |
2191 | - sc.wait_for_signal(); service->stop(); |
2192 | - |
2193 | - if (t.joinable()) |
2194 | - t.join(); |
2195 | - |
2196 | - return ::testing::Test::HasFailure() ? core::posix::exit::Status::failure : core::posix::exit::Status::success; |
2197 | - }; |
2198 | - |
2199 | - auto client = [this, &sync_service_start]() |
2200 | - { |
2201 | - sync_service_start.wait_for_signal_ready_for(std::chrono::milliseconds{500}); |
2202 | - |
2203 | - auto service = media::Service::Client::instance(); |
2204 | - auto session = service->create_session(media::Player::Client::default_configuration()); |
2205 | - |
2206 | - EXPECT_TRUE(session != nullptr); |
2207 | - |
2208 | - auto resumed_session = service->resume_session(session->key()); |
2209 | - |
2210 | - EXPECT_TRUE(resumed_session != nullptr); |
2211 | - |
2212 | - return ::testing::Test::HasFailure() ? core::posix::exit::Status::failure : core::posix::exit::Status::success; |
2213 | - }; |
2214 | - |
2215 | - EXPECT_EQ(core::testing::ForkAndRunResult::empty, |
2216 | - core::testing::fork_and_run(service, client)); |
2217 | -} |
2218 | - |
2219 | -TEST(MusicService, DISABLED_remotely_querying_track_meta_data_works) |
2220 | -{ |
2221 | - const std::string test_file{"/tmp/test-audio.ogg"}; |
2222 | - const std::string test_file_uri{"file:///tmp/test-audio.ogg"}; |
2223 | - std::remove(test_file.c_str()); |
2224 | - ASSERT_TRUE(test::copy_test_media_file_to("test-audio.ogg", test_file)); |
2225 | - |
2226 | - core::testing::CrossProcessSync sync_service_start; |
2227 | - |
2228 | - auto service = [this, &sync_service_start]() |
2229 | - { |
2230 | - SigTermCatcher sc; |
2231 | - |
2232 | - auto service = std::make_shared<media::ServiceImplementation>(); |
2233 | - std::thread t([&service](){service->run();}); |
2234 | - |
2235 | - sync_service_start.try_signal_ready_for(std::chrono::milliseconds{500}); |
2236 | - |
2237 | - sc.wait_for_signal(); service->stop(); |
2238 | - |
2239 | - if (t.joinable()) |
2240 | - t.join(); |
2241 | - |
2242 | - return ::testing::Test::HasFailure() ? core::posix::exit::Status::failure : core::posix::exit::Status::success; |
2243 | - }; |
2244 | - |
2245 | - auto client = [this, &sync_service_start]() |
2246 | - { |
2247 | - sync_service_start.wait_for_signal_ready_for(std::chrono::milliseconds{500}); |
2248 | - |
2249 | - static const media::Track::UriType uri{"file:///tmp/test-audio.ogg"}; |
2250 | - |
2251 | - auto service = media::Service::Client::instance(); |
2252 | - auto session = service->create_session(media::Player::Client::default_configuration()); |
2253 | - auto track_list = session->track_list(); |
2254 | - |
2255 | - track_list->add_track_with_uri_at(uri, media::TrackList::after_empty_track(), false); |
2256 | - |
2257 | - EXPECT_EQ(std::uint32_t{1}, track_list->tracks()->size()); |
2258 | - |
2259 | - auto md = track_list->query_meta_data_for_track(track_list->tracks()->front()); |
2260 | - |
2261 | - for (auto pair : *md) |
2262 | - { |
2263 | - std::cout << pair.first << " -> " << pair.second << std::endl; |
2264 | - } |
2265 | - |
2266 | - return ::testing::Test::HasFailure() ? core::posix::exit::Status::failure : core::posix::exit::Status::success; |
2267 | - }; |
2268 | - |
2269 | - EXPECT_EQ(core::testing::ForkAndRunResult::empty, |
2270 | - core::testing::fork_and_run(service, client)); |
2271 | -} |
2272 | - |
2273 | -TEST(MusicService, DISABLED_play_pause_seek_after_open_uri_works) |
2274 | -{ |
2275 | - const std::string test_file{"/tmp/test-audio-1.ogg"}; |
2276 | - std::remove(test_file.c_str()); |
2277 | - ASSERT_TRUE(test::copy_test_media_file_to("test-audio-1.ogg", test_file)); |
2278 | - |
2279 | - core::testing::CrossProcessSync sync_service_start; |
2280 | - |
2281 | - auto service = [this, &sync_service_start]() |
2282 | - { |
2283 | - SigTermCatcher sc; |
2284 | - |
2285 | - auto service = std::make_shared<media::ServiceImplementation>(); |
2286 | - std::thread t([&service](){service->run();}); |
2287 | - |
2288 | - sync_service_start.try_signal_ready_for(std::chrono::milliseconds{500}); |
2289 | - |
2290 | - sc.wait_for_signal(); service->stop(); |
2291 | - |
2292 | - if (t.joinable()) |
2293 | - t.join(); |
2294 | - |
2295 | - return ::testing::Test::HasFailure() ? core::posix::exit::Status::failure : core::posix::exit::Status::success; |
2296 | - }; |
2297 | - |
2298 | - auto client = [this, &sync_service_start]() |
2299 | - { |
2300 | - sync_service_start.wait_for_signal_ready_for(std::chrono::milliseconds{500}); |
2301 | - |
2302 | - static const media::Track::UriType uri{"file:///tmp/test-audio-1.ogg"}; |
2303 | - |
2304 | - auto service = media::Service::Client::instance(); |
2305 | - auto session = service->create_session(media::Player::Client::default_configuration()); |
2306 | - |
2307 | - EXPECT_EQ(true, session->can_play().get()); |
2308 | - EXPECT_EQ(true, session->can_pause().get()); |
2309 | - EXPECT_EQ(true, session->can_seek().get()); |
2310 | - EXPECT_EQ(true, session->can_go_previous().get()); |
2311 | - EXPECT_EQ(true, session->can_go_next().get()); |
2312 | - EXPECT_EQ(media::Player::PlaybackStatus::null, session->playback_status()); |
2313 | - EXPECT_EQ(media::Player::LoopStatus::none, session->loop_status()); |
2314 | - EXPECT_NEAR(1.f, session->playback_rate().get(), 1E-5); |
2315 | - EXPECT_EQ(true, session->is_shuffle()); |
2316 | - EXPECT_EQ(true, session->track_list()->can_edit_tracks().get()); |
2317 | - |
2318 | - EXPECT_TRUE(session->open_uri(uri)); |
2319 | - |
2320 | - core::testing::WaitableStateTransition<media::Player::PlaybackStatus> |
2321 | - state_transition( |
2322 | - media::Player::stopped); |
2323 | - |
2324 | - session->playback_status().changed().connect( |
2325 | - std::bind(&core::testing::WaitableStateTransition<media::Player::PlaybackStatus>::trigger, |
2326 | - std::ref(state_transition), |
2327 | - std::placeholders::_1)); |
2328 | - |
2329 | - session->play(); |
2330 | - sleep_for(std::chrono::seconds{1}); |
2331 | - EXPECT_EQ(media::Player::PlaybackStatus::playing, session->playback_status()); |
2332 | - session->stop(); |
2333 | - sleep_for(std::chrono::seconds{1}); |
2334 | - EXPECT_EQ(media::Player::PlaybackStatus::stopped, session->playback_status()); |
2335 | - session->play(); |
2336 | - sleep_for(std::chrono::seconds{1}); |
2337 | - EXPECT_EQ(media::Player::PlaybackStatus::playing, session->playback_status()); |
2338 | - session->pause(); |
2339 | - sleep_for(std::chrono::seconds{1}); |
2340 | - EXPECT_EQ(media::Player::PlaybackStatus::paused, session->playback_status()); |
2341 | - session->stop(); |
2342 | - sleep_for(std::chrono::seconds{1}); |
2343 | - EXPECT_EQ(media::Player::PlaybackStatus::stopped, session->playback_status()); |
2344 | - |
2345 | - return ::testing::Test::HasFailure() ? core::posix::exit::Status::failure : core::posix::exit::Status::success; |
2346 | - }; |
2347 | - |
2348 | - EXPECT_EQ(core::testing::ForkAndRunResult::empty, |
2349 | - core::testing::fork_and_run(service, client)); |
2350 | -} |
2351 | - |
2352 | -TEST(MusicService, DISABLED_get_position_duration_work) |
2353 | -{ |
2354 | - const std::string test_file{"/tmp/test-audio-1.ogg"}; |
2355 | - std::remove(test_file.c_str()); |
2356 | - ASSERT_TRUE(test::copy_test_media_file_to("test-audio-1.ogg", test_file)); |
2357 | - |
2358 | - core::testing::CrossProcessSync sync_service_start; |
2359 | - |
2360 | - auto service = [this, &sync_service_start]() |
2361 | - { |
2362 | - SigTermCatcher sc; |
2363 | - |
2364 | - auto service = std::make_shared<media::ServiceImplementation>(); |
2365 | - std::thread t([&service](){service->run();}); |
2366 | - |
2367 | - sync_service_start.try_signal_ready_for(std::chrono::milliseconds{500}); |
2368 | - |
2369 | - sc.wait_for_signal(); service->stop(); |
2370 | - |
2371 | - if (t.joinable()) |
2372 | - t.join(); |
2373 | - |
2374 | - return ::testing::Test::HasFailure() ? core::posix::exit::Status::failure : core::posix::exit::Status::success; |
2375 | - }; |
2376 | - |
2377 | - auto client = [this, &sync_service_start]() |
2378 | - { |
2379 | - sync_service_start.wait_for_signal_ready_for(std::chrono::milliseconds{500}); |
2380 | - |
2381 | - static const media::Track::UriType uri{"file:///tmp/test-audio-1.ogg"}; |
2382 | - |
2383 | - auto service = media::Service::Client::instance(); |
2384 | - auto session = service->create_session(media::Player::Client::default_configuration()); |
2385 | - |
2386 | - EXPECT_TRUE(session->open_uri(uri)); |
2387 | - |
2388 | - core::testing::WaitableStateTransition<media::Player::PlaybackStatus> |
2389 | - state_transition( |
2390 | - media::Player::stopped); |
2391 | - |
2392 | - session->playback_status().changed().connect( |
2393 | - std::bind(&core::testing::WaitableStateTransition<media::Player::PlaybackStatus>::trigger, |
2394 | - std::ref(state_transition), |
2395 | - std::placeholders::_1)); |
2396 | - |
2397 | - session->play(); |
2398 | - sleep_for(std::chrono::seconds{1}); |
2399 | - EXPECT_EQ(media::Player::PlaybackStatus::playing, session->playback_status()); |
2400 | - sleep_for(std::chrono::seconds{1}); |
2401 | - EXPECT_TRUE(session->position() > 1e9); |
2402 | - EXPECT_TRUE(session->duration() > 1e9); |
2403 | - |
2404 | - return ::testing::Test::HasFailure() ? core::posix::exit::Status::failure : core::posix::exit::Status::success; |
2405 | - }; |
2406 | - |
2407 | - EXPECT_EQ(core::testing::ForkAndRunResult::empty, |
2408 | - core::testing::fork_and_run(service, client)); |
2409 | -} |
2410 | - |
2411 | -TEST(MusicService, DISABLED_starting_playback_on_non_empty_playqueue_works) |
2412 | -{ |
2413 | - const std::string test_file{"/tmp/test-audio-1.ogg"}; |
2414 | - std::remove(test_file.c_str()); |
2415 | - ASSERT_TRUE(test::copy_test_media_file_to("test-audio-1.ogg", test_file)); |
2416 | - |
2417 | - core::testing::CrossProcessSync sync_service_start; |
2418 | - |
2419 | - auto service = [this, &sync_service_start]() |
2420 | - { |
2421 | - SigTermCatcher sc; |
2422 | - |
2423 | - auto service = std::make_shared<media::ServiceImplementation>(); |
2424 | - std::thread t([&service](){service->run();}); |
2425 | - |
2426 | - sync_service_start.try_signal_ready_for(std::chrono::milliseconds{500}); |
2427 | - |
2428 | - sc.wait_for_signal(); service->stop(); |
2429 | - |
2430 | - if (t.joinable()) |
2431 | - t.join(); |
2432 | - |
2433 | - return ::testing::Test::HasFailure() ? core::posix::exit::Status::failure : core::posix::exit::Status::success; |
2434 | - }; |
2435 | - |
2436 | - auto client = [this, &sync_service_start]() |
2437 | - { |
2438 | - sync_service_start.wait_for_signal_ready_for(std::chrono::milliseconds{500}); |
2439 | - |
2440 | - static const media::Track::UriType uri{"file:///tmp/test-audio-1.ogg"}; |
2441 | - static const bool dont_make_current{false}; |
2442 | - |
2443 | - auto service = media::Service::Client::instance(); |
2444 | - auto session = service->create_session(media::Player::Client::default_configuration()); |
2445 | - |
2446 | - EXPECT_EQ(true, session->can_play().get()); |
2447 | - EXPECT_EQ(true, session->can_play().get()); |
2448 | - EXPECT_EQ(true, session->can_pause().get()); |
2449 | - EXPECT_EQ(true, session->can_seek().get()); |
2450 | - EXPECT_EQ(true, session->can_go_previous().get()); |
2451 | - EXPECT_EQ(true, session->can_go_next().get()); |
2452 | - EXPECT_EQ(media::Player::PlaybackStatus::null, session->playback_status()); |
2453 | - EXPECT_EQ(media::Player::LoopStatus::none, session->loop_status()); |
2454 | - EXPECT_NEAR(1.f, session->playback_rate().get(), 1E-5); |
2455 | - EXPECT_EQ(true, session->is_shuffle()); |
2456 | - EXPECT_EQ(true, session->track_list()->can_edit_tracks().get()); |
2457 | - |
2458 | - session->track_list()->add_track_with_uri_at(uri, media::TrackList::after_empty_track(), dont_make_current); |
2459 | - |
2460 | - EXPECT_EQ(std::uint32_t{1}, session->track_list()->tracks()->size()); |
2461 | - |
2462 | - core::testing::WaitableStateTransition<media::Player::PlaybackStatus> |
2463 | - state_transition( |
2464 | - media::Player::stopped); |
2465 | - |
2466 | - session->playback_status().changed().connect( |
2467 | - std::bind(&core::testing::WaitableStateTransition<media::Player::PlaybackStatus>::trigger, |
2468 | - std::ref(state_transition), |
2469 | - std::placeholders::_1)); |
2470 | - |
2471 | - session->play(); |
2472 | - |
2473 | - EXPECT_TRUE(state_transition.wait_for_state_for( |
2474 | - media::Player::playing, |
2475 | - std::chrono::milliseconds{2000})); |
2476 | - |
2477 | - return ::testing::Test::HasFailure() ? core::posix::exit::Status::failure : core::posix::exit::Status::success; |
2478 | - }; |
2479 | - |
2480 | - EXPECT_EQ(core::testing::ForkAndRunResult::empty, |
2481 | - core::testing::fork_and_run(service, client)); |
2482 | +TEST(MediaService, move_track_in_tracklist_works) |
2483 | +{ |
2484 | + auto service = media::Service::Client::instance(); |
2485 | + auto session = service->create_session(media::Player::Client::default_configuration()); |
2486 | + EXPECT_TRUE(session != nullptr); |
2487 | + |
2488 | + std::vector<media::Track::Id> ids; |
2489 | + |
2490 | + auto tracklist = session->track_list(); |
2491 | + |
2492 | + // Catch the new track Ids that are assigned each track that's added |
2493 | + core::Connection c = tracklist->on_track_added().connect([&ids](const media::Track::Id &new_id) |
2494 | + { |
2495 | + std::cout << "track added: " << new_id << std::endl; |
2496 | + ids.push_back(new_id); |
2497 | + }); |
2498 | + |
2499 | + const media::Track::UriType uri1{"file:///tmp/test-audio.ogg"}; |
2500 | + const media::Track::UriType uri2{"file:///tmp/test-audio1.ogg"}; |
2501 | + const media::Track::UriType uri3{"file:///tmp/test.mp3"}; |
2502 | + const media::Track::UriType uri4{"file:///tmp/test-video.ogg"}; |
2503 | + |
2504 | + tracklist->add_track_with_uri_at(uri1, media::TrackList::after_empty_track(), false); |
2505 | + tracklist->add_track_with_uri_at(uri2, media::TrackList::after_empty_track(), false); |
2506 | + tracklist->add_track_with_uri_at(uri3, media::TrackList::after_empty_track(), false); |
2507 | + tracklist->add_track_with_uri_at(uri4, media::TrackList::after_empty_track(), false); |
2508 | + EXPECT_EQ(std::uint32_t{4}, tracklist->tracks()->size()); |
2509 | + |
2510 | + std::cout << "ids.at(0): " << ids.at(1) << std::endl; |
2511 | + std::cout << "ids.at(2): " << ids.at(2) << std::endl; |
2512 | + // Move track 3 into the second position in the TrackList |
2513 | + tracklist->move_track(ids.at(2), ids.at(1)); |
2514 | + |
2515 | + // Original list order and current order should be different |
2516 | + EXPECT_NE(ids, tracklist->tracks().get()); |
2517 | + EXPECT_EQ(ids[1], tracklist->tracks()->at(2)); |
2518 | + EXPECT_EQ(ids[2], tracklist->tracks()->at(1)); |
2519 | +} |
2520 | + |
2521 | +TEST(MediaService, move_track_to_first_position_in_tracklist_works) |
2522 | +{ |
2523 | + auto service = media::Service::Client::instance(); |
2524 | + auto session = service->create_session(media::Player::Client::default_configuration()); |
2525 | + EXPECT_TRUE(session != nullptr); |
2526 | + |
2527 | + std::vector<media::Track::Id> ids; |
2528 | + |
2529 | + auto tracklist = session->track_list(); |
2530 | + |
2531 | + // Catch the new track Ids that are assigned each track that's added |
2532 | + core::Connection c = tracklist->on_track_added().connect([&ids](const media::Track::Id &new_id) |
2533 | + { |
2534 | + std::cout << "track added: " << new_id << std::endl; |
2535 | + ids.push_back(new_id); |
2536 | + }); |
2537 | + |
2538 | + const media::Track::UriType uri1{"file:///tmp/test-audio.ogg"}; |
2539 | + const media::Track::UriType uri2{"file:///tmp/test-audio1.ogg"}; |
2540 | + const media::Track::UriType uri3{"file:///tmp/test.mp3"}; |
2541 | + const media::Track::UriType uri4{"file:///tmp/test-video.ogg"}; |
2542 | + |
2543 | + tracklist->add_track_with_uri_at(uri1, media::TrackList::after_empty_track(), false); |
2544 | + tracklist->add_track_with_uri_at(uri2, media::TrackList::after_empty_track(), false); |
2545 | + tracklist->add_track_with_uri_at(uri3, media::TrackList::after_empty_track(), false); |
2546 | + tracklist->add_track_with_uri_at(uri4, media::TrackList::after_empty_track(), false); |
2547 | + EXPECT_EQ(std::uint32_t{4}, tracklist->tracks()->size()); |
2548 | + |
2549 | + std::cout << "ids.at(0): " << ids.at(0) << std::endl; |
2550 | + std::cout << "ids.at(2): " << ids.at(2) << std::endl; |
2551 | + // Move track 3 into the first position in the TrackList |
2552 | + tracklist->move_track(ids.at(2), ids.at(0)); |
2553 | + |
2554 | + // Original list order and current order should be different |
2555 | + EXPECT_NE(ids, tracklist->tracks().get()); |
2556 | + EXPECT_EQ(ids[2], tracklist->tracks()->at(0)); |
2557 | + EXPECT_EQ(ids[0], tracklist->tracks()->at(1)); |
2558 | +} |
2559 | + |
2560 | +TEST(MediaService, move_track_to_last_position_in_tracklist_works) |
2561 | +{ |
2562 | + auto service = media::Service::Client::instance(); |
2563 | + auto session = service->create_session(media::Player::Client::default_configuration()); |
2564 | + EXPECT_TRUE(session != nullptr); |
2565 | + |
2566 | + std::vector<media::Track::Id> ids; |
2567 | + |
2568 | + auto tracklist = session->track_list(); |
2569 | + |
2570 | + // Catch the new track Ids that are assigned each track that's added |
2571 | + core::Connection c = tracklist->on_track_added().connect([&ids](const media::Track::Id &new_id) |
2572 | + { |
2573 | + std::cout << "track added: " << new_id << std::endl; |
2574 | + ids.push_back(new_id); |
2575 | + }); |
2576 | + |
2577 | + const media::Track::UriType uri1{"file:///tmp/test-audio.ogg"}; |
2578 | + const media::Track::UriType uri2{"file:///tmp/test-audio1.ogg"}; |
2579 | + const media::Track::UriType uri3{"file:///tmp/test.mp3"}; |
2580 | + const media::Track::UriType uri4{"file:///tmp/test-video.ogg"}; |
2581 | + |
2582 | + tracklist->add_track_with_uri_at(uri1, media::TrackList::after_empty_track(), false); |
2583 | + tracklist->add_track_with_uri_at(uri2, media::TrackList::after_empty_track(), false); |
2584 | + tracklist->add_track_with_uri_at(uri3, media::TrackList::after_empty_track(), false); |
2585 | + tracklist->add_track_with_uri_at(uri4, media::TrackList::after_empty_track(), false); |
2586 | + EXPECT_EQ(std::uint32_t{4}, tracklist->tracks()->size()); |
2587 | + |
2588 | + sleep(1); |
2589 | + |
2590 | + std::cout << "ids.at(0): " << ids.at(0) << std::endl; |
2591 | + std::cout << "ids.at(3): " << ids.at(3) << std::endl; |
2592 | + // Move track 3 into the first position in the TrackList |
2593 | + tracklist->move_track(ids.at(0), ids.at(3)); |
2594 | + |
2595 | + // Original list order and current order should be different |
2596 | + EXPECT_NE(ids, tracklist->tracks().get()); |
2597 | + EXPECT_EQ(ids[1], tracklist->tracks()->at(0)); |
2598 | + EXPECT_EQ(ids[0], tracklist->tracks()->at(3)); |
2599 | } |
2600 | |
2601 | === modified file 'tests/test-track-list/test_track_list.cpp' |
2602 | --- tests/test-track-list/test_track_list.cpp 2015-04-20 15:56:26 +0000 |
2603 | +++ tests/test-track-list/test_track_list.cpp 2015-12-23 14:19:01 +0000 |
2604 | @@ -257,9 +257,8 @@ |
2605 | |
2606 | const media::Track::Id id{m_hubTrackList->tracks().get()[3]}; |
2607 | cout << "Going straight to the Track with Id " << id << std::endl; |
2608 | - const bool toggle_player_state = true; |
2609 | - m_hubTrackList->go_to(id, toggle_player_state); |
2610 | - |
2611 | + m_hubTrackList->go_to(id); |
2612 | + |
2613 | cout << "Waiting for third track to finish playing..." << endl; |
2614 | wait_for_about_to_finish(); |
2615 | } |
2616 | |
2617 | === modified file 'tests/test-track-list/test_track_list.h' |
2618 | --- tests/test-track-list/test_track_list.h 2015-04-15 18:29:39 +0000 |
2619 | +++ tests/test-track-list/test_track_list.h 2015-12-23 14:19:01 +0000 |
2620 | @@ -57,7 +57,7 @@ |
2621 | |
2622 | // Takes two uris and confirms that they remain after a detach/reattach |
2623 | void test_tracklist_resume(const std::string &uri1, const std::string &uri2, const std::string &uuid); |
2624 | - |
2625 | + |
2626 | void test_ensure_tracklist_is_not_empty(const std::string &uri1, const std::string &uri2 = std::string{}); |
2627 | |
2628 | // Takes in one or two files for playback, adds it/them to the TrackList, plays and makes sure |
LGTM