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