Merge lp:~ricmm/media-hub/session-reattach-nonfixed into lp:media-hub

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
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.

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

Merge lp:~phablet-team/media-hub/tracklist-cli

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,

Subscribers

People subscribed via source and target branches