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