Merge lp:~phablet-team/media-hub/sync-trunk into lp:media-hub
- sync-trunk
- Merge into trunk
Proposed by
Alfonso Sanchez-Beato
on 2015-12-16
| Status: | Merged |
|---|---|
| Approved by: | Thomas Voß on 2016-01-07 |
| 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 | code | 2015-12-16 | Approve on 2015-12-16 |
|
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.
lp:~phablet-team/media-hub/sync-trunk
updated
on 2015-12-23
- 161. By Thomas Voß on 2015-12-23
-
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