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

Proposed by Jim Hodapp
Status: Merged
Approved by: Ricardo Mendoza
Approved revision: 139
Merged at revision: 132
Proposed branch: lp:~phablet-team/media-hub/tracklist-cli
Merge into: lp:media-hub
Diff against target: 2379 lines (+1155/-173)
32 files modified
include/core/media/player.h (+39/-2)
include/core/media/track_list.h (+37/-4)
src/core/media/codec.h (+2/-0)
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 (+6/-2)
src/core/media/gstreamer/playbin.h (+3/-1)
src/core/media/mpris/player.h (+8/-3)
src/core/media/mpris/track_list.h (+101/-9)
src/core/media/player_configuration.h (+2/-0)
src/core/media/player_implementation.cpp (+81/-13)
src/core/media/player_implementation.h (+1/-1)
src/core/media/player_skeleton.cpp (+5/-4)
src/core/media/player_skeleton.h (+4/-2)
src/core/media/player_stub.cpp (+14/-8)
src/core/media/player_stub.h (+3/-2)
src/core/media/service_implementation.cpp (+15/-12)
src/core/media/service_skeleton.cpp (+8/-2)
src/core/media/service_stub.cpp (+3/-0)
src/core/media/track_list_implementation.cpp (+83/-12)
src/core/media/track_list_implementation.h (+10/-4)
src/core/media/track_list_skeleton.cpp (+191/-58)
src/core/media/track_list_skeleton.h (+23/-6)
src/core/media/track_list_stub.cpp (+34/-11)
src/core/media/track_list_stub.h (+11/-5)
tests/CMakeLists.txt (+1/-0)
tests/test-track-list/CMakeLists.txt (+17/-0)
tests/test-track-list/test_track_list.cpp (+346/-0)
tests/test-track-list/test_track_list.h (+86/-0)
tests/unit-tests/test-gstreamer-engine.cpp (+12/-6)
To merge this branch: bzr merge lp:~phablet-team/media-hub/tracklist-cli
Reviewer Review Type Date Requested Status
Thomas Voß (community) Needs Information
PS Jenkins bot continuous-integration Approve
Review via email: mp+255900@code.launchpad.net

Commit message

* Implements the backend portion of TrackLists required to allow clients to be able to hand off a playlist to media-hub and keep playing music in order, shuffled and/or looped without the app being in the foreground

Description of the change

* Implements the backend portion of TrackLists required to allow clients to be able to hand off a playlist to media-hub and keep playing music in order, shuffled and/or looped without the app being in the foreground

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
136. By Jim Hodapp

Initialize the TrackList property returned by Tracks().

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
137. By Jim Hodapp

Include codec includes for vector and string.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
138. By Jim Hodapp

Return the dbus Property instances instead of the local ones.

139. By Jim Hodapp

Clean up no longer necessary cout statements and one unused function add_set_state_to_main_context() in playbin.cpp

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Thomas Voß (thomas-voss) wrote :

I noticed the top-approve, still wanted to leave my comments.

review: Needs Information
140. By Jim Hodapp

Make sure the skeleton uses the dbus version of the properties instead of a copy.

141. By Jim Hodapp

Fix up the tracklist dbus object name/path

142. By Jim Hodapp

Fix video playback not working because of conflicting play(), stop(), play() during setup when using open_uri().

143. By Jim Hodapp

Don't call open_uri twice and also make sure that the TrackList handle_add_track_with_uri_at() checks apparmor access permissions.

Preview Diff

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

Subscribers

People subscribed via source and target branches

to all changes: