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
=== modified file 'include/core/media/player.h'
--- include/core/media/player.h 2015-03-11 16:17:23 +0000
+++ include/core/media/player.h 2015-04-20 17:49:07 +0000
@@ -27,6 +27,7 @@
27#include <core/property.h>27#include <core/property.h>
2828
29#include <chrono>29#include <chrono>
30#include <iosfwd>
30#include <memory>31#include <memory>
3132
32namespace core33namespace core
@@ -149,7 +150,7 @@
149 virtual const core::Property<PlaybackStatus>& playback_status() const = 0;150 virtual const core::Property<PlaybackStatus>& playback_status() const = 0;
150 virtual const core::Property<LoopStatus>& loop_status() const = 0;151 virtual const core::Property<LoopStatus>& loop_status() const = 0;
151 virtual const core::Property<PlaybackRate>& playback_rate() const = 0;152 virtual const core::Property<PlaybackRate>& playback_rate() const = 0;
152 virtual const core::Property<bool>& is_shuffle() const = 0;153 virtual const core::Property<bool>& shuffle() const = 0;
153 virtual const core::Property<Track::MetaData>& meta_data_for_current_track() const = 0;154 virtual const core::Property<Track::MetaData>& meta_data_for_current_track() const = 0;
154 virtual const core::Property<Volume>& volume() const = 0;155 virtual const core::Property<Volume>& volume() const = 0;
155 virtual const core::Property<PlaybackRate>& minimum_playback_rate() const = 0;156 virtual const core::Property<PlaybackRate>& minimum_playback_rate() const = 0;
@@ -162,7 +163,7 @@
162163
163 virtual core::Property<LoopStatus>& loop_status() = 0;164 virtual core::Property<LoopStatus>& loop_status() = 0;
164 virtual core::Property<PlaybackRate>& playback_rate() = 0;165 virtual core::Property<PlaybackRate>& playback_rate() = 0;
165 virtual core::Property<bool>& is_shuffle() = 0;166 virtual core::Property<bool>& shuffle() = 0;
166 virtual core::Property<Volume>& volume() = 0;167 virtual core::Property<Volume>& volume() = 0;
167 virtual core::Property<AudioStreamRole>& audio_stream_role() = 0;168 virtual core::Property<AudioStreamRole>& audio_stream_role() = 0;
168 virtual core::Property<Lifetime>& lifetime() = 0;169 virtual core::Property<Lifetime>& lifetime() = 0;
@@ -178,6 +179,42 @@
178 Player();179 Player();
179180
180};181};
182
183// operator<< pretty prints the given playback status to the given output stream.
184inline std::ostream& operator<<(std::ostream& out, Player::PlaybackStatus status)
185{
186 switch (status)
187 {
188 case Player::PlaybackStatus::null:
189 return out << "PlaybackStatus::null";
190 case Player::PlaybackStatus::ready:
191 return out << "PlaybackStatus::ready";
192 case Player::PlaybackStatus::playing:
193 return out << "PlaybackStatus::playing";
194 case Player::PlaybackStatus::paused:
195 return out << "PlaybackStatus::paused";
196 case Player::PlaybackStatus::stopped:
197 return out << "PlaybackStatus::stopped";
198 }
199
200 return out;
201}
202
203inline std::ostream& operator<<(std::ostream& out, Player::LoopStatus loop_status)
204{
205 switch (loop_status)
206 {
207 case Player::LoopStatus::none:
208 return out << "LoopStatus::none";
209 case Player::LoopStatus::track:
210 return out << "LoopStatus::track";
211 case Player::LoopStatus::playlist:
212 return out << "LoopStatus::playlist";
213 }
214
215 return out;
216}
217
181}218}
182}219}
183}220}
184221
=== modified file 'include/core/media/track_list.h'
--- include/core/media/track_list.h 2014-02-12 15:53:57 +0000
+++ include/core/media/track_list.h 2015-04-20 17:49:07 +0000
@@ -24,7 +24,8 @@
24#include <core/signal.h>24#include <core/signal.h>
2525
26#include <functional>26#include <functional>
27#include <list>27#include <iosfwd>
28#include <vector>
28#include <memory>29#include <memory>
2930
30namespace core31namespace core
@@ -39,6 +40,7 @@
39{40{
40 public:41 public:
41 typedef std::vector<Track::Id> Container;42 typedef std::vector<Track::Id> Container;
43 typedef std::tuple<std::vector<Track::Id>, Track::Id> ContainerTrackIdTuple;
42 typedef Container::iterator Iterator;44 typedef Container::iterator Iterator;
43 typedef Container::const_iterator ConstIterator;45 typedef Container::const_iterator ConstIterator;
4446
@@ -50,23 +52,54 @@
50 TrackList& operator=(const TrackList&) = delete;52 TrackList& operator=(const TrackList&) = delete;
51 bool operator==(const TrackList&) const = delete;53 bool operator==(const TrackList&) const = delete;
5254
55 /** If set to false, calling add_track_with_uri_at or remove_track will have no effect. */
53 virtual const core::Property<bool>& can_edit_tracks() const = 0;56 virtual const core::Property<bool>& can_edit_tracks() const = 0;
57
58 /** An array which contains the identifier of each track in the tracklist, in order. */
54 virtual const core::Property<Container>& tracks() const = 0;59 virtual const core::Property<Container>& tracks() const = 0;
5560
61 /** Gets all the metadata available for a given Track. */
56 virtual Track::MetaData query_meta_data_for_track(const Track::Id& id) = 0;62 virtual Track::MetaData query_meta_data_for_track(const Track::Id& id) = 0;
63
64 /** Adds a URI in the TrackList. */
57 virtual void add_track_with_uri_at(const Track::UriType& uri, const Track::Id& position, bool make_current) = 0;65 virtual void add_track_with_uri_at(const Track::UriType& uri, const Track::Id& position, bool make_current) = 0;
66
67 /** Removes a Track from the TrackList. */
58 virtual void remove_track(const Track::Id& id) = 0;68 virtual void remove_track(const Track::Id& id) = 0;
5969
60 virtual void go_to(const Track::Id& track) = 0;70 /** Skip to the specified TrackId. Calls stop() and play() on the player if toggle_player_state is true. */
6171 virtual void go_to(const Track::Id& track, bool toggle_player_state) = 0;
62 virtual const core::Signal<void>& on_track_list_replaced() const = 0;72
73
74 /** Reorders the tracks such that they are in a random order. */
75 virtual void shuffle_tracks() = 0;
76
77 /** Restores the original order of tracks before shuffle mode was turned on. */
78 virtual void unshuffle_tracks() = 0;
79
80 /** Clears and resets the TrackList to the same as a newly constructed instance. */
81 virtual void reset() = 0;
82
83
84 /** Indicates that the entire tracklist has been replaced. */
85 virtual const core::Signal<ContainerTrackIdTuple>& on_track_list_replaced() const = 0;
86
87 /** Indicates that a track has been added to the track list. */
63 virtual const core::Signal<Track::Id>& on_track_added() const = 0;88 virtual const core::Signal<Track::Id>& on_track_added() const = 0;
89
90 /** Indicates that a track has been removed from the track list. */
64 virtual const core::Signal<Track::Id>& on_track_removed() const = 0;91 virtual const core::Signal<Track::Id>& on_track_removed() const = 0;
92
93 /** Indicates that the track list advanced from one track to another. */
65 virtual const core::Signal<Track::Id>& on_track_changed() const = 0;94 virtual const core::Signal<Track::Id>& on_track_changed() const = 0;
6695
96 /** Used to notify the Player of when the client requested that the Player should immediately play a new track. */
97 virtual const core::Signal<std::pair<Track::Id, bool>>& on_go_to_track() const = 0;
98
67protected:99protected:
68 TrackList();100 TrackList();
69};101};
102
70}103}
71}104}
72}105}
73106
=== modified file 'src/core/media/codec.h'
--- src/core/media/codec.h 2015-01-26 09:23:41 +0000
+++ src/core/media/codec.h 2015-04-20 17:49:07 +0000
@@ -23,6 +23,8 @@
23#include <core/media/player.h>23#include <core/media/player.h>
24#include <core/media/track.h>24#include <core/media/track.h>
2525
26#include <core/dbus/types/stl/string.h>
27#include <core/dbus/types/stl/vector.h>
26#include <core/dbus/codec.h>28#include <core/dbus/codec.h>
2729
28namespace core30namespace core
2931
=== modified file 'src/core/media/engine.h'
--- src/core/media/engine.h 2015-01-26 09:23:41 +0000
+++ src/core/media/engine.h 2015-04-20 17:49:07 +0000
@@ -38,6 +38,7 @@
3838
39 enum class State39 enum class State
40 {40 {
41 no_media,
41 ready,42 ready,
42 busy,43 busy,
43 playing,44 playing,
@@ -75,7 +76,7 @@
7576
76 virtual const core::Property<State>& state() const = 0;77 virtual const core::Property<State>& state() const = 0;
7778
78 virtual bool open_resource_for_uri(const core::ubuntu::media::Track::UriType& uri) = 0;79 virtual bool open_resource_for_uri(const core::ubuntu::media::Track::UriType& uri, bool do_pipeline_reset) = 0;
79 virtual bool open_resource_for_uri(const core::ubuntu::media::Track::UriType& uri, const Player::HeadersType&) = 0;80 virtual bool open_resource_for_uri(const core::ubuntu::media::Track::UriType& uri, const Player::HeadersType&) = 0;
80 // Throws core::ubuntu::media::Player::Error::OutOfProcessBufferStreamingNotSupported if the implementation does not81 // Throws core::ubuntu::media::Player::Error::OutOfProcessBufferStreamingNotSupported if the implementation does not
81 // support this feature.82 // support this feature.
8283
=== modified file 'src/core/media/gstreamer/engine.cpp'
--- src/core/media/gstreamer/engine.cpp 2015-03-11 16:17:23 +0000
+++ src/core/media/gstreamer/engine.cpp 2015-04-20 17:49:07 +0000
@@ -313,12 +313,13 @@
313gstreamer::Engine::Engine() : d(new Private{})313gstreamer::Engine::Engine() : d(new Private{})
314{314{
315 cout << "Creating a new Engine instance in " << __PRETTY_FUNCTION__ << endl;315 cout << "Creating a new Engine instance in " << __PRETTY_FUNCTION__ << endl;
316 d->state = media::Engine::State::ready;316 d->state = media::Engine::State::no_media;
317}317}
318318
319gstreamer::Engine::~Engine()319gstreamer::Engine::~Engine()
320{320{
321 stop();321 stop();
322 d->state = media::Engine::State::no_media;
322}323}
323324
324const std::shared_ptr<media::Engine::MetaDataExtractor>& gstreamer::Engine::meta_data_extractor() const325const std::shared_ptr<media::Engine::MetaDataExtractor>& gstreamer::Engine::meta_data_extractor() const
@@ -331,9 +332,9 @@
331 return d->state;332 return d->state;
332}333}
333334
334bool gstreamer::Engine::open_resource_for_uri(const media::Track::UriType& uri)335bool gstreamer::Engine::open_resource_for_uri(const media::Track::UriType& uri, bool do_pipeline_reset)
335{336{
336 d->playbin.set_uri(uri, core::ubuntu::media::Player::HeadersType{});337 d->playbin.set_uri(uri, core::ubuntu::media::Player::HeadersType{}, do_pipeline_reset);
337 return true;338 return true;
338}339}
339340
@@ -356,6 +357,7 @@
356 {357 {
357 d->state = media::Engine::State::playing;358 d->state = media::Engine::State::playing;
358 cout << __PRETTY_FUNCTION__ << endl;359 cout << __PRETTY_FUNCTION__ << endl;
360 cout << "Engine: playing uri: " << d->playbin.uri() << endl;
359 d->playback_status_changed(media::Player::PlaybackStatus::playing);361 d->playback_status_changed(media::Player::PlaybackStatus::playing);
360 }362 }
361363
362364
=== modified file 'src/core/media/gstreamer/engine.h'
--- src/core/media/gstreamer/engine.h 2015-01-26 09:23:41 +0000
+++ src/core/media/gstreamer/engine.h 2015-04-20 17:49:07 +0000
@@ -33,7 +33,7 @@
3333
34 const core::Property<State>& state() const;34 const core::Property<State>& state() const;
3535
36 bool open_resource_for_uri(const core::ubuntu::media::Track::UriType& uri);36 bool open_resource_for_uri(const core::ubuntu::media::Track::UriType& uri, bool do_pipeline_reset);
37 bool open_resource_for_uri(const core::ubuntu::media::Track::UriType& uri, const core::ubuntu::media::Player::HeadersType& headers);37 bool open_resource_for_uri(const core::ubuntu::media::Track::UriType& uri, const core::ubuntu::media::Player::HeadersType& headers);
38 void create_video_sink(uint32_t texture_id);38 void create_video_sink(uint32_t texture_id);
3939
4040
=== modified file 'src/core/media/gstreamer/meta_data_extractor.h'
--- src/core/media/gstreamer/meta_data_extractor.h 2014-10-14 20:05:20 +0000
+++ src/core/media/gstreamer/meta_data_extractor.h 2015-04-20 17:49:07 +0000
@@ -177,7 +177,7 @@
177 bus.on_new_message.connect(177 bus.on_new_message.connect(
178 [&](const gstreamer::Bus::Message& msg)178 [&](const gstreamer::Bus::Message& msg)
179 {179 {
180 std::cout << __PRETTY_FUNCTION__ << gst_message_type_get_name(msg.type) << std::endl;180 //std::cout << __PRETTY_FUNCTION__ << gst_message_type_get_name(msg.type) << std::endl;
181 if (msg.type == GST_MESSAGE_TAG)181 if (msg.type == GST_MESSAGE_TAG)
182 {182 {
183 MetaDataExtractor::on_tag_available(msg.detail.tag, meta_data);183 MetaDataExtractor::on_tag_available(msg.detail.tag, meta_data);
184184
=== modified file 'src/core/media/gstreamer/playbin.cpp'
--- src/core/media/gstreamer/playbin.cpp 2015-03-11 16:17:23 +0000
+++ src/core/media/gstreamer/playbin.cpp 2015-04-20 17:49:07 +0000
@@ -24,6 +24,8 @@
24#include <hybris/media/surface_texture_client_hybris.h>24#include <hybris/media/surface_texture_client_hybris.h>
25#include <hybris/media/media_codec_layer.h>25#include <hybris/media/media_codec_layer.h>
2626
27#include <utility>
28
27namespace29namespace
28{30{
29void setup_video_sink_for_buffer_streaming(GstElement* video_sink)31void setup_video_sink_for_buffer_streaming(GstElement* video_sink)
@@ -365,9 +367,11 @@
365367
366void gstreamer::Playbin::set_uri(368void gstreamer::Playbin::set_uri(
367 const std::string& uri,369 const std::string& uri,
368 const core::ubuntu::media::Player::HeadersType& headers = core::ubuntu::media::Player::HeadersType())370 const core::ubuntu::media::Player::HeadersType& headers = core::ubuntu::media::Player::HeadersType(),
371 bool do_pipeline_reset)
369{372{
370 reset_pipeline();373 if (do_pipeline_reset)
374 reset_pipeline();
371375
372 g_object_set(pipeline, "uri", uri.c_str(), NULL);376 g_object_set(pipeline, "uri", uri.c_str(), NULL);
373 if (is_video_file(uri))377 if (is_video_file(uri))
374378
=== modified file 'src/core/media/gstreamer/playbin.h'
--- src/core/media/gstreamer/playbin.h 2015-03-11 16:17:23 +0000
+++ src/core/media/gstreamer/playbin.h 2015-04-20 17:49:07 +0000
@@ -89,11 +89,13 @@
89 uint64_t position() const;89 uint64_t position() const;
90 uint64_t duration() const;90 uint64_t duration() const;
9191
92 void set_uri(const std::string& uri, const core::ubuntu::media::Player::HeadersType& headers);92 void set_uri(const std::string& uri, const core::ubuntu::media::Player::HeadersType& headers, bool do_pipeline_reset = true);
93 std::string uri() const;93 std::string uri() const;
9494
95 void setup_source(GstElement *source);95 void setup_source(GstElement *source);
9696
97 // Sets the pipeline's state (stopped, playing, paused, etc). Optional parameter makes this call
98 // in the main_loop context.
97 bool set_state_and_wait(GstState new_state);99 bool set_state_and_wait(GstState new_state);
98 bool seek(const std::chrono::microseconds& ms);100 bool seek(const std::chrono::microseconds& ms);
99101
100102
=== modified file 'src/core/media/mpris/player.h'
--- src/core/media/mpris/player.h 2015-03-11 16:17:23 +0000
+++ src/core/media/mpris/player.h 2015-04-20 17:49:07 +0000
@@ -271,7 +271,7 @@
271 properties.orientation->set(core::ubuntu::media::Player::Orientation::rotate0);271 properties.orientation->set(core::ubuntu::media::Player::Orientation::rotate0);
272 properties.lifetime->set(core::ubuntu::media::Player::Lifetime::normal);272 properties.lifetime->set(core::ubuntu::media::Player::Lifetime::normal);
273 properties.playback_rate->set(configuration.defaults.playback_rate);273 properties.playback_rate->set(configuration.defaults.playback_rate);
274 properties.is_shuffle->set(configuration.defaults.shuffle);274 properties.shuffle->set(configuration.defaults.shuffle);
275 properties.position->set(configuration.defaults.position);275 properties.position->set(configuration.defaults.position);
276 properties.duration->set(configuration.defaults.duration);276 properties.duration->set(configuration.defaults.duration);
277 properties.minimum_playback_rate->set(configuration.defaults.minimum_rate);277 properties.minimum_playback_rate->set(configuration.defaults.minimum_rate);
@@ -302,6 +302,11 @@
302 {302 {
303 on_property_value_changed<Properties::LoopStatus>(status);303 on_property_value_changed<Properties::LoopStatus>(status);
304 });304 });
305
306 properties.shuffle->changed().connect([this](bool shuffle)
307 {
308 on_property_value_changed<Properties::Shuffle>(shuffle);
309 });
305 }310 }
306311
307 template<typename Property>312 template<typename Property>
@@ -332,7 +337,7 @@
332 dict[Properties::Orientation::name()] = dbus::types::Variant::encode(properties.orientation->get());337 dict[Properties::Orientation::name()] = dbus::types::Variant::encode(properties.orientation->get());
333 dict[Properties::Lifetime::name()] = dbus::types::Variant::encode(properties.lifetime->get());338 dict[Properties::Lifetime::name()] = dbus::types::Variant::encode(properties.lifetime->get());
334 dict[Properties::PlaybackRate::name()] = dbus::types::Variant::encode(properties.playback_rate->get());339 dict[Properties::PlaybackRate::name()] = dbus::types::Variant::encode(properties.playback_rate->get());
335 dict[Properties::Shuffle::name()] = dbus::types::Variant::encode(properties.is_shuffle->get());340 dict[Properties::Shuffle::name()] = dbus::types::Variant::encode(properties.shuffle->get());
336 dict[Properties::Duration::name()] = dbus::types::Variant::encode(properties.duration->get());341 dict[Properties::Duration::name()] = dbus::types::Variant::encode(properties.duration->get());
337 dict[Properties::Position::name()] = dbus::types::Variant::encode(properties.position->get());342 dict[Properties::Position::name()] = dbus::types::Variant::encode(properties.position->get());
338 dict[Properties::MinimumRate::name()] = dbus::types::Variant::encode(properties.minimum_playback_rate->get());343 dict[Properties::MinimumRate::name()] = dbus::types::Variant::encode(properties.minimum_playback_rate->get());
@@ -363,7 +368,7 @@
363 std::shared_ptr<core::dbus::Property<Properties::Orientation>> orientation;368 std::shared_ptr<core::dbus::Property<Properties::Orientation>> orientation;
364 std::shared_ptr<core::dbus::Property<Properties::Lifetime>> lifetime;369 std::shared_ptr<core::dbus::Property<Properties::Lifetime>> lifetime;
365 std::shared_ptr<core::dbus::Property<Properties::PlaybackRate>> playback_rate;370 std::shared_ptr<core::dbus::Property<Properties::PlaybackRate>> playback_rate;
366 std::shared_ptr<core::dbus::Property<Properties::Shuffle>> is_shuffle;371 std::shared_ptr<core::dbus::Property<Properties::Shuffle>> shuffle;
367 std::shared_ptr<core::dbus::Property<Properties::TypedMetaData>> typed_meta_data_for_current_track;372 std::shared_ptr<core::dbus::Property<Properties::TypedMetaData>> typed_meta_data_for_current_track;
368 std::shared_ptr<core::dbus::Property<Properties::Volume>> volume;373 std::shared_ptr<core::dbus::Property<Properties::Volume>> volume;
369 std::shared_ptr<core::dbus::Property<Properties::Position>> position;374 std::shared_ptr<core::dbus::Property<Properties::Position>> position;
370375
=== modified file 'src/core/media/mpris/track_list.h'
--- src/core/media/mpris/track_list.h 2014-08-25 10:18:32 +0000
+++ src/core/media/mpris/track_list.h 2015-04-20 17:49:07 +0000
@@ -22,7 +22,10 @@
22#include <core/dbus/macros.h>22#include <core/dbus/macros.h>
2323
24#include <core/dbus/types/any.h>24#include <core/dbus/types/any.h>
25#include <core/dbus/macros.h>
25#include <core/dbus/types/object_path.h>26#include <core/dbus/types/object_path.h>
27#include <core/dbus/object.h>
28#include <core/dbus/property.h>
26#include <core/dbus/types/variant.h>29#include <core/dbus/types/variant.h>
2730
28#include <boost/utility/identity_type.hpp>31#include <boost/utility/identity_type.hpp>
@@ -37,38 +40,42 @@
37{40{
38struct TrackList41struct TrackList
39{42{
43 typedef std::map<std::string, core::dbus::types::Variant> Dictionary;
44
40 static const std::string& name()45 static const std::string& name()
41 {46 {
42 static const std::string s{"core.ubuntu.media.Service.Player.TrackList"};47 static const std::string s{"org.mpris.MediaPlayer2.TrackList"};
43 return s;48 return s;
44 }49 }
4550
46 DBUS_CPP_METHOD_WITH_TIMEOUT_DEF(GetTracksMetadata, TrackList, 1000)51 DBUS_CPP_METHOD_DEF(GetTracksMetadata, TrackList)
47 DBUS_CPP_METHOD_WITH_TIMEOUT_DEF(AddTrack, TrackList, 1000)52 DBUS_CPP_METHOD_DEF(AddTrack, TrackList)
48 DBUS_CPP_METHOD_WITH_TIMEOUT_DEF(RemoveTrack, TrackList, 1000)53 DBUS_CPP_METHOD_DEF(RemoveTrack, TrackList)
49 DBUS_CPP_METHOD_WITH_TIMEOUT_DEF(GoTo, TrackList, 1000)54 DBUS_CPP_METHOD_DEF(GoTo, TrackList)
5055
51 struct Signals56 struct Signals
52 {57 {
58 Signals() = delete;
59
53 DBUS_CPP_SIGNAL_DEF60 DBUS_CPP_SIGNAL_DEF
54 (61 (
55 TrackListReplaced,62 TrackListReplaced,
56 TrackList,63 TrackList,
57 BOOST_IDENTITY_TYPE((std::tuple<std::vector<dbus::types::ObjectPath>, dbus::types::ObjectPath>))64 BOOST_IDENTITY_TYPE((std::tuple<std::vector<core::ubuntu::media::Track::Id>, core::ubuntu::media::Track::Id>))
58 )65 )
5966
60 DBUS_CPP_SIGNAL_DEF67 DBUS_CPP_SIGNAL_DEF
61 (68 (
62 TrackAdded,69 TrackAdded,
63 TrackList,70 TrackList,
64 BOOST_IDENTITY_TYPE((std::tuple<std::map<std::string, dbus::types::Variant>, dbus::types::ObjectPath>))71 core::ubuntu::media::Track::Id
65 )72 )
6673
67 DBUS_CPP_SIGNAL_DEF74 DBUS_CPP_SIGNAL_DEF
68 (75 (
69 TrackRemoved,76 TrackRemoved,
70 TrackList,77 TrackList,
71 dbus::types::ObjectPath78 core::ubuntu::media::Track::Id
72 )79 )
7380
74 DBUS_CPP_SIGNAL_DEF81 DBUS_CPP_SIGNAL_DEF
@@ -81,9 +88,94 @@
8188
82 struct Properties89 struct Properties
83 {90 {
84 DBUS_CPP_READABLE_PROPERTY_DEF(Tracks, TrackList, std::vector<std::string>)91 Properties() = delete;
92
93 DBUS_CPP_READABLE_PROPERTY_DEF(Tracks, TrackList, std::vector<core::ubuntu::media::Track::Id>)
85 DBUS_CPP_READABLE_PROPERTY_DEF(CanEditTracks, TrackList, bool)94 DBUS_CPP_READABLE_PROPERTY_DEF(CanEditTracks, TrackList, bool)
86 };95 };
96
97 struct Skeleton
98 {
99 static const std::vector<std::string>& the_empty_list_of_invalidated_properties()
100 {
101 static const std::vector<std::string> instance; return instance;
102 }
103
104 // Object instance creation time properties go here.
105 struct Configuration
106 {
107 // The dbus object that should implement org.mpris.MediaPlayer2
108 core::dbus::Object::Ptr object;
109 // Default values assigned to exported dbus interface properties on construction
110 struct Defaults
111 {
112 Properties::Tracks::ValueType tracks{std::vector<core::ubuntu::media::Track::Id>()};
113 Properties::CanEditTracks::ValueType can_edit_tracks{true};
114 } defaults;
115 };
116
117 Skeleton(const Configuration& configuration)
118 : configuration(configuration),
119 properties
120 {
121 configuration.object->get_property<Properties::Tracks>(),
122 configuration.object->get_property<Properties::CanEditTracks>(),
123 },
124 signals
125 {
126 configuration.object->get_signal<Signals::TrackListReplaced>(),
127 configuration.object->get_signal<Signals::TrackAdded>(),
128 configuration.object->get_signal<Signals::TrackRemoved>(),
129 configuration.object->get_signal<Signals::TrackMetadataChanged>(),
130 configuration.object->template get_signal<core::dbus::interfaces::Properties::Signals::PropertiesChanged>()
131 }
132 {
133 // Set the default value of the properties on the MPRIS TrackList dbus interface
134 properties.tracks->set(configuration.defaults.tracks);
135 properties.can_edit_tracks->set(configuration.defaults.can_edit_tracks);
136 }
137
138 template<typename Property>
139 void on_property_value_changed(const typename Property::ValueType& value)
140 {
141 Dictionary dict;
142 dict[Property::name()] = dbus::types::Variant::encode(value);
143
144 signals.properties_changed->emit(std::make_tuple(
145 dbus::traits::Service<TrackList>::interface_name(),
146 dict,
147 the_empty_list_of_invalidated_properties()));
148 }
149
150 std::map<std::string, core::dbus::types::Variant> get_all_properties()
151 {
152 std::map<std::string, core::dbus::types::Variant> dict;
153 dict[Properties::Tracks::name()] = core::dbus::types::Variant::encode(properties.tracks->get());
154 dict[Properties::CanEditTracks::name()] = core::dbus::types::Variant::encode(properties.can_edit_tracks->get());
155
156 return dict;
157 }
158
159 Configuration configuration;
160
161 struct
162 {
163 std::shared_ptr<core::dbus::Property<Properties::Tracks>> tracks;
164 std::shared_ptr<core::dbus::Property<Properties::CanEditTracks>> can_edit_tracks;
165 } properties;
166
167 struct
168 {
169 core::dbus::Signal<Signals::TrackListReplaced, Signals::TrackListReplaced::ArgumentType>::Ptr tracklist_replaced;
170 core::dbus::Signal<Signals::TrackAdded, Signals::TrackAdded::ArgumentType>::Ptr track_added;
171 core::dbus::Signal<Signals::TrackRemoved, Signals::TrackRemoved::ArgumentType>::Ptr track_removed;
172 core::dbus::Signal<Signals::TrackMetadataChanged, Signals::TrackMetadataChanged::ArgumentType>::Ptr track_metadata_changed;
173
174 dbus::Signal <core::dbus::interfaces::Properties::Signals::PropertiesChanged,
175 core::dbus::interfaces::Properties::Signals::PropertiesChanged::ArgumentType
176 >::Ptr properties_changed;
177 } signals;
178 };
87};179};
88}180}
89181
90182
=== modified file 'src/core/media/player_configuration.h'
--- src/core/media/player_configuration.h 2014-11-26 16:23:19 +0000
+++ src/core/media/player_configuration.h 2015-04-20 17:49:07 +0000
@@ -31,6 +31,8 @@
31 core::ubuntu::media::Player::PlayerKey key;31 core::ubuntu::media::Player::PlayerKey key;
32 // The bus connection to expose objects on.32 // The bus connection to expose objects on.
33 std::shared_ptr<core::dbus::Bus> bus;33 std::shared_ptr<core::dbus::Bus> bus;
34 // The service instance we live under.
35 std::shared_ptr<core::dbus::Service> service;
34 // The actual session object representing a player instance.36 // The actual session object representing a player instance.
35 std::shared_ptr<core::dbus::Object> session;37 std::shared_ptr<core::dbus::Object> session;
36};38};
3739
=== modified file 'src/core/media/player_implementation.cpp'
--- src/core/media/player_implementation.cpp 2015-03-19 19:08:47 +0000
+++ src/core/media/player_implementation.cpp 2015-04-20 17:49:07 +0000
@@ -1,4 +1,5 @@
1/*1/*
2 * Copyright © 2013-2015 Canonical Ltd.
2 *3 *
3 * This program is free software: you can redistribute it and/or modify it4 * This program is free software: you can redistribute it and/or modify it
4 * under the terms of the GNU Lesser General Public License version 3,5 * under the terms of the GNU Lesser General Public License version 3,
@@ -23,7 +24,6 @@
2324
24#include "client_death_observer.h"25#include "client_death_observer.h"
25#include "engine.h"26#include "engine.h"
26#include "null_track_list.h"
27#include "track_list_implementation.h"27#include "track_list_implementation.h"
2828
29#include "gstreamer/engine.h"29#include "gstreamer/engine.h"
@@ -58,14 +58,20 @@
58 display_state_lock(config.power_state_controller->display_state_lock()),58 display_state_lock(config.power_state_controller->display_state_lock()),
59 system_state_lock(config.power_state_controller->system_state_lock()),59 system_state_lock(config.power_state_controller->system_state_lock()),
60 engine(std::make_shared<gstreamer::Engine>()),60 engine(std::make_shared<gstreamer::Engine>()),
61 track_list(std::make_shared<NullTrackList>()),61 track_list(std::make_shared<TrackListImplementation>(
62 config.parent.bus,
63 config.parent.service->add_object_for_path(
64 dbus::types::ObjectPath(config.parent.session->path().as_string() + "/TrackList")),
65 engine->meta_data_extractor(),
66 config.parent.request_context_resolver,
67 config.parent.request_authenticator)),
62 system_wakelock_count(0),68 system_wakelock_count(0),
63 display_wakelock_count(0),69 display_wakelock_count(0),
64 previous_state(Engine::State::stopped),70 previous_state(Engine::State::stopped),
65 engine_state_change_connection(engine->state().changed().connect(make_state_change_handler())),71 engine_state_change_connection(engine->state().changed().connect(make_state_change_handler())),
66 engine_playback_status_change_connection(engine->playback_status_changed_signal().connect(make_playback_status_change_handler()))72 engine_playback_status_change_connection(engine->playback_status_changed_signal().connect(make_playback_status_change_handler())),
73 doing_go_to_track(false)
67 {74 {
68 std::cout << "Private parent instance: " << parent << std::endl;
69 // Poor man's logging of release/acquire events.75 // Poor man's logging of release/acquire events.
70 display_state_lock->acquired().connect([](media::power::DisplayState state)76 display_state_lock->acquired().connect([](media::power::DisplayState state)
71 {77 {
@@ -99,7 +105,6 @@
99 // by disconnecting the state change signal105 // by disconnecting the state change signal
100 engine_state_change_connection.disconnect();106 engine_state_change_connection.disconnect();
101107
102 std::cout << "** Disconnecting playback_status_changed_signal connection";
103 // The engine destructor can lead to a playback status change which will108 // The engine destructor can lead to a playback status change which will
104 // trigger the playback status change handler. Ensure the handler is not called109 // trigger the playback status change handler. Ensure the handler is not called
105 // by disconnecting the playback status change signal110 // by disconnecting the playback status change signal
@@ -169,7 +174,7 @@
169 {174 {
170 return [this](const media::Player::PlaybackStatus& status)175 return [this](const media::Player::PlaybackStatus& status)
171 {176 {
172 std::cout << "Emiting playback_status_changed for parent: " << parent << std::endl;177 std::cout << "Emiting playback_status_changed signal: " << status << std::endl;
173 parent->emit_playback_status_changed(status);178 parent->emit_playback_status_changed(status);
174 };179 };
175 }180 }
@@ -290,13 +295,15 @@
290 media::power::StateController::Lock<media::power::SystemState>::Ptr system_state_lock;295 media::power::StateController::Lock<media::power::SystemState>::Ptr system_state_lock;
291296
292 std::shared_ptr<Engine> engine;297 std::shared_ptr<Engine> engine;
293 std::shared_ptr<media::NullTrackList> track_list;298 std::shared_ptr<media::TrackListImplementation> track_list;
294 std::atomic<int> system_wakelock_count;299 std::atomic<int> system_wakelock_count;
295 std::atomic<int> display_wakelock_count;300 std::atomic<int> display_wakelock_count;
296 Engine::State previous_state;301 Engine::State previous_state;
297 core::Signal<> on_client_disconnected;302 core::Signal<> on_client_disconnected;
298 core::Connection engine_state_change_connection;303 core::Connection engine_state_change_connection;
299 core::Connection engine_playback_status_change_connection;304 core::Connection engine_playback_status_change_connection;
305 // Prevent the TrackList from auto advancing to the next track
306 std::atomic<bool> doing_go_to_track;
300};307};
301308
302template<typename Parent>309template<typename Parent>
@@ -312,7 +319,7 @@
312 Parent::can_go_next().set(true);319 Parent::can_go_next().set(true);
313 Parent::is_video_source().set(false);320 Parent::is_video_source().set(false);
314 Parent::is_audio_source().set(false);321 Parent::is_audio_source().set(false);
315 Parent::is_shuffle().set(true);322 Parent::shuffle().set(false);
316 Parent::playback_rate().set(1.f);323 Parent::playback_rate().set(1.f);
317 Parent::playback_status().set(Player::PlaybackStatus::null);324 Parent::playback_status().set(Player::PlaybackStatus::null);
318 Parent::loop_status().set(Player::LoopStatus::none);325 Parent::loop_status().set(Player::LoopStatus::none);
@@ -352,6 +359,18 @@
352 };359 };
353 Parent::is_audio_source().install(audio_type_getter);360 Parent::is_audio_source().install(audio_type_getter);
354361
362 // When the client changes the loop status, make sure to update the TrackList
363 Parent::loop_status().changed().connect([this](media::Player::LoopStatus loop_status)
364 {
365 d->track_list->on_loop_status_changed(loop_status);
366 });
367
368 // When the client changes the shuffle setting, make sure to update the TrackList
369 Parent::shuffle().changed().connect([this](bool shuffle)
370 {
371 d->track_list->on_shuffle_changed(shuffle);
372 });
373
355 // Make sure that the audio_stream_role property gets updated on the Engine side374 // Make sure that the audio_stream_role property gets updated on the Engine side
356 // whenever the client side sets the role375 // whenever the client side sets the role
357 Parent::audio_stream_role().changed().connect([this](media::Player::AudioStreamRole new_role)376 Parent::audio_stream_role().changed().connect([this](media::Player::AudioStreamRole new_role)
@@ -375,12 +394,21 @@
375 {394 {
376 Parent::about_to_finish()();395 Parent::about_to_finish()();
377396
378 if (d->track_list->has_next())397 if (!d->doing_go_to_track)
379 {398 {
380 Track::UriType uri = d->track_list->query_uri_for_track(d->track_list->next());399 const media::Track::Id prev_track_id = d->track_list->current();
381 if (!uri.empty())400 // Make sure that the TrackList keeps advancing. The logic for what gets played next,
382 d->parent->open_uri(uri);401 // if anything at all, occurs in TrackListSkeleton::next()
402 const Track::UriType uri = d->track_list->query_uri_for_track(d->track_list->next());
403 if (prev_track_id != d->track_list->current() && !uri.empty())
404 {
405 std::cout << "Setting next track on playbin: " << uri << std::endl;
406 static const bool do_pipeline_reset = false;
407 d->engine->open_resource_for_uri(uri, do_pipeline_reset);
408 }
383 }409 }
410 else
411 std::cout << "Not auto-advancing the TrackList since doing_go_to_track is true" << std::endl;
384 });412 });
385413
386 d->engine->client_disconnected_signal().connect([this]()414 d->engine->client_disconnected_signal().connect([this]()
@@ -388,6 +416,7 @@
388 // If the client disconnects, make sure both wakelock types416 // If the client disconnects, make sure both wakelock types
389 // are cleared417 // are cleared
390 d->clear_wakelocks();418 d->clear_wakelocks();
419 d->track_list->reset();
391 // And tell the outside world that the client has gone away420 // And tell the outside world that the client has gone away
392 d->on_client_disconnected();421 d->on_client_disconnected();
393 });422 });
@@ -412,6 +441,33 @@
412 Parent::error()(e);441 Parent::error()(e);
413 });442 });
414443
444 d->track_list->on_go_to_track().connect([this](std::pair<const media::Track::Id, bool> p)
445 {
446 // This prevents the TrackList from auto advancing in other areas such as the about_to_finish signal
447 // handler.
448 d->doing_go_to_track = true;
449
450 const media::Track::Id id = p.first;
451 const bool toggle_player_state = p.second;
452
453 if (toggle_player_state)
454 d->engine->stop();
455
456 const Track::UriType uri = d->track_list->query_uri_for_track(id);
457 if (!uri.empty())
458 {
459 std::cout << "Setting next track on playbin (on_go_to_track signal): " << uri << std::endl;
460 std::cout << "\twith a Track::Id: " << id << std::endl;
461 static const bool do_pipeline_reset = true;
462 d->engine->open_resource_for_uri(uri, do_pipeline_reset);
463 }
464
465 if (toggle_player_state)
466 d->engine->play();
467
468 d->doing_go_to_track = false;
469 });
470
415 // Everything is setup, we now subscribe to death notifications.471 // Everything is setup, we now subscribe to death notifications.
416 std::weak_ptr<Private> wp{d};472 std::weak_ptr<Private> wp{d};
417473
@@ -487,7 +543,9 @@
487template<typename Parent>543template<typename Parent>
488bool media::PlayerImplementation<Parent>::open_uri(const Track::UriType& uri)544bool media::PlayerImplementation<Parent>::open_uri(const Track::UriType& uri)
489{545{
490 return d->engine->open_resource_for_uri(uri);546 // Set new track as the current track to play
547 d->track_list->add_track_with_uri_at(uri, media::TrackList::after_empty_track(), true);
548 return true;
491}549}
492550
493template<typename Parent>551template<typename Parent>
@@ -509,6 +567,16 @@
509template<typename Parent>567template<typename Parent>
510void media::PlayerImplementation<Parent>::play()568void media::PlayerImplementation<Parent>::play()
511{569{
570 if (d->track_list != nullptr && d->track_list->tracks()->size() > 0 && d->engine->state() == media::Engine::State::no_media)
571 {
572 // Using a TrackList for playback, added tracks via add_track(), but open_uri hasn't been called yet
573 // to load a media resource
574 std::cout << "No media loaded yet, calling open_uri on first track in track_list" << std::endl;
575 static const bool do_pipeline_reset = true;
576 d->engine->open_resource_for_uri(d->track_list->query_uri_for_track(d->track_list->current()), do_pipeline_reset);
577 std::cout << *d->track_list << endl;
578 }
579
512 d->engine->play();580 d->engine->play();
513}581}
514582
515583
=== modified file 'src/core/media/player_implementation.h'
--- src/core/media/player_implementation.h 2015-03-19 19:08:47 +0000
+++ src/core/media/player_implementation.h 2015-04-20 17:49:07 +0000
@@ -78,8 +78,8 @@
78 struct Private;78 struct Private;
79 std::shared_ptr<Private> d;79 std::shared_ptr<Private> d;
80};80};
81
81}82}
82}83}
83}84}
84#endif // CORE_UBUNTU_MEDIA_PLAYER_IMPLEMENTATION_H_85#endif // CORE_UBUNTU_MEDIA_PLAYER_IMPLEMENTATION_H_
85
8686
=== modified file 'src/core/media/player_skeleton.cpp'
--- src/core/media/player_skeleton.cpp 2015-03-11 16:17:23 +0000
+++ src/core/media/player_skeleton.cpp 2015-04-20 17:49:07 +0000
@@ -179,6 +179,7 @@
179 Track::UriType uri;179 Track::UriType uri;
180 in->reader() >> uri;180 in->reader() >> uri;
181181
182 // Make sure the client has adequate apparmor permissions to open the URI
182 auto result = request_authenticator->authenticate_open_uri_request(context, uri);183 auto result = request_authenticator->authenticate_open_uri_request(context, uri);
183184
184 auto reply = dbus::Message::make_method_return(in);185 auto reply = dbus::Message::make_method_return(in);
@@ -407,9 +408,9 @@
407 return *d->skeleton.properties.playback_rate;408 return *d->skeleton.properties.playback_rate;
408}409}
409410
410const core::Property<bool>& media::PlayerSkeleton::is_shuffle() const411const core::Property<bool>& media::PlayerSkeleton::shuffle() const
411{412{
412 return *d->skeleton.properties.is_shuffle;413 return *d->skeleton.properties.shuffle;
413}414}
414415
415const core::Property<media::Track::MetaData>& media::PlayerSkeleton::meta_data_for_current_track() const416const core::Property<media::Track::MetaData>& media::PlayerSkeleton::meta_data_for_current_track() const
@@ -467,9 +468,9 @@
467 return *d->skeleton.properties.playback_rate;468 return *d->skeleton.properties.playback_rate;
468}469}
469470
470core::Property<bool>& media::PlayerSkeleton::is_shuffle()471core::Property<bool>& media::PlayerSkeleton::shuffle()
471{472{
472 return *d->skeleton.properties.is_shuffle;473 return *d->skeleton.properties.shuffle;
473}474}
474475
475core::Property<media::Player::Volume>& media::PlayerSkeleton::volume()476core::Property<media::Player::Volume>& media::PlayerSkeleton::volume()
476477
=== modified file 'src/core/media/player_skeleton.h'
--- src/core/media/player_skeleton.h 2015-03-11 16:17:23 +0000
+++ src/core/media/player_skeleton.h 2015-04-20 17:49:07 +0000
@@ -53,6 +53,8 @@
53 {53 {
54 // The bus connection we are associated with.54 // The bus connection we are associated with.
55 std::shared_ptr<core::dbus::Bus> bus;55 std::shared_ptr<core::dbus::Bus> bus;
56 // The service instance we are exposed under.
57 std::shared_ptr<core::dbus::Service> service;
56 // The session object that we want to expose the skeleton upon.58 // The session object that we want to expose the skeleton upon.
57 std::shared_ptr<core::dbus::Object> session;59 std::shared_ptr<core::dbus::Object> session;
58 // Our functional dependencies.60 // Our functional dependencies.
@@ -73,7 +75,7 @@
73 virtual const core::Property<PlaybackStatus>& playback_status() const;75 virtual const core::Property<PlaybackStatus>& playback_status() const;
74 virtual const core::Property<LoopStatus>& loop_status() const;76 virtual const core::Property<LoopStatus>& loop_status() const;
75 virtual const core::Property<PlaybackRate>& playback_rate() const;77 virtual const core::Property<PlaybackRate>& playback_rate() const;
76 virtual const core::Property<bool>& is_shuffle() const;78 virtual const core::Property<bool>& shuffle() const;
77 virtual const core::Property<Track::MetaData>& meta_data_for_current_track() const;79 virtual const core::Property<Track::MetaData>& meta_data_for_current_track() const;
78 virtual const core::Property<Volume>& volume() const;80 virtual const core::Property<Volume>& volume() const;
79 virtual const core::Property<PlaybackRate>& minimum_playback_rate() const;81 virtual const core::Property<PlaybackRate>& minimum_playback_rate() const;
@@ -86,7 +88,7 @@
8688
87 virtual core::Property<LoopStatus>& loop_status();89 virtual core::Property<LoopStatus>& loop_status();
88 virtual core::Property<PlaybackRate>& playback_rate();90 virtual core::Property<PlaybackRate>& playback_rate();
89 virtual core::Property<bool>& is_shuffle();91 virtual core::Property<bool>& shuffle();
90 virtual core::Property<Volume>& volume();92 virtual core::Property<Volume>& volume();
91 virtual core::Property<AudioStreamRole>& audio_stream_role();93 virtual core::Property<AudioStreamRole>& audio_stream_role();
92 virtual core::Property<Lifetime>& lifetime();94 virtual core::Property<Lifetime>& lifetime();
9395
=== modified file 'src/core/media/player_stub.cpp'
--- src/core/media/player_stub.cpp 2015-03-16 14:54:47 +0000
+++ src/core/media/player_stub.cpp 2015-04-20 17:49:07 +0000
@@ -43,8 +43,10 @@
43struct media::PlayerStub::Private43struct media::PlayerStub::Private
44{44{
45 Private(const std::shared_ptr<Service>& parent,45 Private(const std::shared_ptr<Service>& parent,
46 const std::shared_ptr<core::dbus::Service>& service,
46 const std::shared_ptr<core::dbus::Object>& object47 const std::shared_ptr<core::dbus::Object>& object
47 ) : parent(parent),48 ) : parent(parent),
49 service(service),
48 object(object),50 object(object),
49 key(object->invoke_method_synchronously<mpris::Player::Key, media::Player::PlayerKey>().value()),51 key(object->invoke_method_synchronously<mpris::Player::Key, media::Player::PlayerKey>().value()),
50 sink_factory(media::video::make_platform_default_sink_factory(key)),52 sink_factory(media::video::make_platform_default_sink_factory(key)),
@@ -91,6 +93,7 @@
9193
92 std::shared_ptr<Service> parent;94 std::shared_ptr<Service> parent;
93 std::shared_ptr<TrackList> track_list;95 std::shared_ptr<TrackList> track_list;
96 dbus::Service::Ptr service;
94 dbus::Object::Ptr object;97 dbus::Object::Ptr object;
95 media::Player::PlayerKey key;98 media::Player::PlayerKey key;
96 media::video::SinkFactory sink_factory;99 media::video::SinkFactory sink_factory;
@@ -108,7 +111,7 @@
108 std::shared_ptr<core::dbus::Property<mpris::Player::Properties::TypedPlaybackStatus>> playback_status;111 std::shared_ptr<core::dbus::Property<mpris::Player::Properties::TypedPlaybackStatus>> playback_status;
109 std::shared_ptr<core::dbus::Property<mpris::Player::Properties::TypedLoopStatus>> loop_status;112 std::shared_ptr<core::dbus::Property<mpris::Player::Properties::TypedLoopStatus>> loop_status;
110 std::shared_ptr<core::dbus::Property<mpris::Player::Properties::PlaybackRate>> playback_rate;113 std::shared_ptr<core::dbus::Property<mpris::Player::Properties::PlaybackRate>> playback_rate;
111 std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Shuffle>> is_shuffle;114 std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Shuffle>> shuffle;
112 std::shared_ptr<core::dbus::Property<mpris::Player::Properties::TypedMetaData>> meta_data_for_current_track;115 std::shared_ptr<core::dbus::Property<mpris::Player::Properties::TypedMetaData>> meta_data_for_current_track;
113 std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Volume>> volume;116 std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Volume>> volume;
114 std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Position>> position;117 std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Position>> position;
@@ -153,7 +156,7 @@
153 {156 {
154 dbus.seeked_to->connect([this](std::uint64_t value)157 dbus.seeked_to->connect([this](std::uint64_t value)
155 {158 {
156 std::cout << "seeked_to signal arrived via the bus." << std::endl;159 std::cout << "SeekedTo signal arrived via the bus." << std::endl;
157 seeked_to(value);160 seeked_to(value);
158 });161 });
159162
@@ -209,8 +212,9 @@
209212
210media::PlayerStub::PlayerStub(213media::PlayerStub::PlayerStub(
211 const std::shared_ptr<Service>& parent,214 const std::shared_ptr<Service>& parent,
215 const std::shared_ptr<core::dbus::Service>& service,
212 const std::shared_ptr<core::dbus::Object>& object)216 const std::shared_ptr<core::dbus::Object>& object)
213 : d(new Private{parent, object})217 : d(new Private{parent, service, object})
214{218{
215}219}
216220
@@ -224,7 +228,9 @@
224 {228 {
225 d->track_list = std::make_shared<media::TrackListStub>(229 d->track_list = std::make_shared<media::TrackListStub>(
226 shared_from_this(),230 shared_from_this(),
227 dbus::types::ObjectPath(d->object->path().as_string() + "/TrackList"));231 d->service->object_for_path(
232 dbus::types::ObjectPath(
233 d->object->path().as_string() + "/TrackList")));
228 }234 }
229 return d->track_list;235 return d->track_list;
230}236}
@@ -362,9 +368,9 @@
362 return *d->properties.playback_rate;368 return *d->properties.playback_rate;
363}369}
364370
365const core::Property<bool>& media::PlayerStub::is_shuffle() const371const core::Property<bool>& media::PlayerStub::shuffle() const
366{372{
367 return *d->properties.is_shuffle;373 return *d->properties.shuffle;
368}374}
369375
370const core::Property<media::Track::MetaData>& media::PlayerStub::meta_data_for_current_track() const376const core::Property<media::Track::MetaData>& media::PlayerStub::meta_data_for_current_track() const
@@ -422,9 +428,9 @@
422 return *d->properties.playback_rate;428 return *d->properties.playback_rate;
423}429}
424430
425core::Property<bool>& media::PlayerStub::is_shuffle()431core::Property<bool>& media::PlayerStub::shuffle()
426{432{
427 return *d->properties.is_shuffle;433 return *d->properties.shuffle;
428}434}
429435
430core::Property<media::Player::Volume>& media::PlayerStub::volume()436core::Property<media::Player::Volume>& media::PlayerStub::volume()
431437
=== modified file 'src/core/media/player_stub.h'
--- src/core/media/player_stub.h 2015-03-11 16:17:23 +0000
+++ src/core/media/player_stub.h 2015-04-20 17:49:07 +0000
@@ -39,6 +39,7 @@
39 public:39 public:
40 explicit PlayerStub(40 explicit PlayerStub(
41 const std::shared_ptr<Service>& parent,41 const std::shared_ptr<Service>& parent,
42 const std::shared_ptr<core::dbus::Service>& service,
42 const std::shared_ptr<core::dbus::Object>& object);43 const std::shared_ptr<core::dbus::Object>& object);
4344
44 ~PlayerStub();45 ~PlayerStub();
@@ -67,7 +68,7 @@
67 virtual const core::Property<PlaybackStatus>& playback_status() const;68 virtual const core::Property<PlaybackStatus>& playback_status() const;
68 virtual const core::Property<LoopStatus>& loop_status() const;69 virtual const core::Property<LoopStatus>& loop_status() const;
69 virtual const core::Property<PlaybackRate>& playback_rate() const;70 virtual const core::Property<PlaybackRate>& playback_rate() const;
70 virtual const core::Property<bool>& is_shuffle() const;71 virtual const core::Property<bool>& shuffle() const;
71 virtual const core::Property<Track::MetaData>& meta_data_for_current_track() const;72 virtual const core::Property<Track::MetaData>& meta_data_for_current_track() const;
72 virtual const core::Property<Volume>& volume() const;73 virtual const core::Property<Volume>& volume() const;
73 virtual const core::Property<PlaybackRate>& minimum_playback_rate() const;74 virtual const core::Property<PlaybackRate>& minimum_playback_rate() const;
@@ -80,7 +81,7 @@
8081
81 virtual core::Property<LoopStatus>& loop_status();82 virtual core::Property<LoopStatus>& loop_status();
82 virtual core::Property<PlaybackRate>& playback_rate();83 virtual core::Property<PlaybackRate>& playback_rate();
83 virtual core::Property<bool>& is_shuffle();84 virtual core::Property<bool>& shuffle();
84 virtual core::Property<Volume>& volume();85 virtual core::Property<Volume>& volume();
85 virtual core::Property<AudioStreamRole>& audio_stream_role();86 virtual core::Property<AudioStreamRole>& audio_stream_role();
86 virtual core::Property<Lifetime>& lifetime();87 virtual core::Property<Lifetime>& lifetime();
8788
=== modified file 'src/core/media/service_implementation.cpp'
--- src/core/media/service_implementation.cpp 2015-04-02 14:46:52 +0000
+++ src/core/media/service_implementation.cpp 2015-04-20 17:49:07 +0000
@@ -53,6 +53,8 @@
5353
54struct media::ServiceImplementation::Private54struct media::ServiceImplementation::Private
55{55{
56 // Create all of the appropriate observers and helper class instances to be
57 // passed to the PlayerImplementation
56 Private(const ServiceImplementation::Configuration& configuration)58 Private(const ServiceImplementation::Configuration& configuration)
57 : configuration(configuration),59 : configuration(configuration),
58 resume_key(std::numeric_limits<std::uint32_t>::max()),60 resume_key(std::numeric_limits<std::uint32_t>::max()),
@@ -72,7 +74,7 @@
72 media::ServiceImplementation::Configuration configuration;74 media::ServiceImplementation::Configuration configuration;
73 // This holds the key of the multimedia role Player instance that was paused75 // This holds the key of the multimedia role Player instance that was paused
74 // when the battery level reached 10% or 5%76 // when the battery level reached 10% or 5%
75 media::Player::PlayerKey resume_key; 77 media::Player::PlayerKey resume_key;
76 media::power::BatteryObserver::Ptr battery_observer;78 media::power::BatteryObserver::Ptr battery_observer;
77 media::power::StateController::Ptr power_state_controller;79 media::power::StateController::Ptr power_state_controller;
78 media::power::StateController::Lock<media::power::DisplayState>::Ptr display_state_lock;80 media::power::StateController::Lock<media::power::DisplayState>::Ptr display_state_lock;
@@ -185,6 +187,7 @@
185 media::PlayerSkeleton::Configuration187 media::PlayerSkeleton::Configuration
186 {188 {
187 conf.bus,189 conf.bus,
190 conf.service,
188 conf.session,191 conf.session,
189 d->request_context_resolver,192 d->request_context_resolver,
190 d->request_authenticator193 d->request_authenticator
@@ -262,17 +265,17 @@
262void media::ServiceImplementation::pause_all_multimedia_sessions(bool resume_play_after_phonecall)265void media::ServiceImplementation::pause_all_multimedia_sessions(bool resume_play_after_phonecall)
263{266{
264 d->configuration.player_store->enumerate_players([this, resume_play_after_phonecall](const media::Player::PlayerKey& key, const std::shared_ptr<media::Player>& player)267 d->configuration.player_store->enumerate_players([this, resume_play_after_phonecall](const media::Player::PlayerKey& key, const std::shared_ptr<media::Player>& player)
265 {268 {
266 if (player->playback_status() == Player::playing269 if (player->playback_status() == Player::playing
267 && player->audio_stream_role() == media::Player::multimedia)270 && player->audio_stream_role() == media::Player::multimedia)
268 {271 {
269 auto paused_player_pair = std::make_pair(key, resume_play_after_phonecall);272 auto paused_player_pair = std::make_pair(key, resume_play_after_phonecall);
270 d->paused_sessions.push_back(paused_player_pair);273 d->paused_sessions.push_back(paused_player_pair);
271 std::cout << "Pausing Player with key: " << key << ", resuming after phone call? "274 std::cout << "Pausing Player with key: " << key << ", resuming after phone call? "
272 << (resume_play_after_phonecall ? "yes" : "no") << std::endl;275 << (resume_play_after_phonecall ? "yes" : "no") << std::endl;
273 player->pause();276 player->pause();
274 }277 }
275 });278 });
276}279}
277280
278void media::ServiceImplementation::resume_paused_multimedia_sessions(bool resume_video_sessions)281void media::ServiceImplementation::resume_paused_multimedia_sessions(bool resume_video_sessions)
279282
=== modified file 'src/core/media/service_skeleton.cpp'
--- src/core/media/service_skeleton.cpp 2014-12-15 14:43:44 +0000
+++ src/core/media/service_skeleton.cpp 2015-04-20 17:49:07 +0000
@@ -52,7 +52,7 @@
52 Private(media::ServiceSkeleton* impl, const ServiceSkeleton::Configuration& config)52 Private(media::ServiceSkeleton* impl, const ServiceSkeleton::Configuration& config)
53 : impl(impl),53 : impl(impl),
54 object(impl->access_service()->add_object_for_path(54 object(impl->access_service()->add_object_for_path(
55 dbus::traits::Service<media::Service>::object_path())), 55 dbus::traits::Service<media::Service>::object_path())),
56 exported(impl->access_bus(), config.cover_art_resolver),56 exported(impl->access_bus(), config.cover_art_resolver),
57 configuration(config)57 configuration(config)
58 {58 {
@@ -101,6 +101,7 @@
101 {101 {
102 key,102 key,
103 impl->access_bus(),103 impl->access_bus(),
104 impl->access_service(),
104 impl->access_service()->add_object_for_path(op)105 impl->access_service()->add_object_for_path(op)
105 };106 };
106107
@@ -130,7 +131,7 @@
130131
131 if (named_player_map.count(name) == 0) {132 if (named_player_map.count(name) == 0) {
132 // Create new session133 // Create new session
133 auto session_info = create_session_info();134 auto session_info = create_session_info();
134135
135 dbus::types::ObjectPath op{session_info.first};136 dbus::types::ObjectPath op{session_info.first};
136 media::Player::PlayerKey key{session_info.second};137 media::Player::PlayerKey key{session_info.second};
@@ -139,6 +140,7 @@
139 {140 {
140 key,141 key,
141 impl->access_bus(),142 impl->access_bus(),
143 impl->access_service(),
142 impl->access_service()->add_object_for_path(op)144 impl->access_service()->add_object_for_path(op)
143 };145 };
144146
@@ -266,6 +268,7 @@
266268
267 static std::string service_name()269 static std::string service_name()
268 {270 {
271#if 0
269 static const bool export_to_indicator_sound_via_mpris272 static const bool export_to_indicator_sound_via_mpris
270 {273 {
271 core::posix::this_process::env::get("UBUNTU_MEDIA_HUB_EXPORT_TO_INDICATOR_VIA_MPRIS", "0") == "1"274 core::posix::this_process::env::get("UBUNTU_MEDIA_HUB_EXPORT_TO_INDICATOR_VIA_MPRIS", "0") == "1"
@@ -273,6 +276,9 @@
273276
274 return export_to_indicator_sound_via_mpris ? "org.mpris.MediaPlayer2.MediaHub" :277 return export_to_indicator_sound_via_mpris ? "org.mpris.MediaPlayer2.MediaHub" :
275 "hidden.org.mpris.MediaPlayer2.MediaHub";278 "hidden.org.mpris.MediaPlayer2.MediaHub";
279#else
280 return "org.mpris.MediaPlayer2.MediaHub";
281#endif
276 }282 }
277283
278 explicit Exported(const dbus::Bus::Ptr& bus, const media::CoverArtResolver& cover_art_resolver)284 explicit Exported(const dbus::Bus::Ptr& bus, const media::CoverArtResolver& cover_art_resolver)
279285
=== modified file 'src/core/media/service_stub.cpp'
--- src/core/media/service_stub.cpp 2014-10-23 14:42:59 +0000
+++ src/core/media/service_stub.cpp 2015-04-20 17:49:07 +0000
@@ -66,6 +66,7 @@
66 return std::shared_ptr<media::Player>(new media::PlayerStub66 return std::shared_ptr<media::Player>(new media::PlayerStub
67 {67 {
68 shared_from_this(),68 shared_from_this(),
69 access_service(),
69 access_service()->object_for_path(op.value())70 access_service()->object_for_path(op.value())
70 });71 });
71}72}
@@ -81,6 +82,7 @@
81 return std::shared_ptr<media::Player>(new media::PlayerStub82 return std::shared_ptr<media::Player>(new media::PlayerStub
82 {83 {
83 shared_from_this(),84 shared_from_this(),
85 access_service(),
84 access_service()->object_for_path(op.value())86 access_service()->object_for_path(op.value())
85 });87 });
86}88}
@@ -96,6 +98,7 @@
96 return std::shared_ptr<media::Player>(new media::PlayerStub98 return std::shared_ptr<media::Player>(new media::PlayerStub
97 {99 {
98 shared_from_this(),100 shared_from_this(),
101 access_service(),
99 access_service()->object_for_path(op.value())102 access_service()->object_for_path(op.value())
100 });103 });
101}104}
102105
=== modified file 'src/core/media/track_list_implementation.cpp'
--- src/core/media/track_list_implementation.cpp 2014-03-25 14:57:15 +0000
+++ src/core/media/track_list_implementation.cpp 2015-04-20 17:49:07 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright © 2013 Canonical Ltd.2 * Copyright © 2013-2015 Canonical Ltd.
3 *3 *
4 * This program is free software: you can redistribute it and/or modify it4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License version 3,5 * under the terms of the GNU Lesser General Public License version 3,
@@ -16,8 +16,10 @@
16 * Authored by: Thomas Voß <thomas.voss@canonical.com>16 * Authored by: Thomas Voß <thomas.voss@canonical.com>
17 */17 */
1818
19#include <algorithm>
19#include <stdio.h>20#include <stdio.h>
20#include <stdlib.h>21#include <stdlib.h>
22#include <tuple>
2123
22#include "track_list_implementation.h"24#include "track_list_implementation.h"
2325
@@ -30,16 +32,23 @@
30{32{
31 typedef std::map<Track::Id, std::tuple<Track::UriType, Track::MetaData>> MetaDataCache;33 typedef std::map<Track::Id, std::tuple<Track::UriType, Track::MetaData>> MetaDataCache;
3234
33 dbus::types::ObjectPath path;35 dbus::Object::Ptr object;
36 size_t track_counter;
34 MetaDataCache meta_data_cache;37 MetaDataCache meta_data_cache;
35 std::shared_ptr<media::Engine::MetaDataExtractor> extractor;38 std::shared_ptr<media::Engine::MetaDataExtractor> extractor;
39 // Used for caching the original tracklist order to be used to restore the order
40 // to the live TrackList after shuffle is turned off
41 media::TrackList::Container original_tracklist;
36};42};
3743
38media::TrackListImplementation::TrackListImplementation(44media::TrackListImplementation::TrackListImplementation(
39 const dbus::types::ObjectPath& op,45 const dbus::Bus::Ptr& bus,
40 const std::shared_ptr<media::Engine::MetaDataExtractor>& extractor)46 const dbus::Object::Ptr& object,
41 : media::TrackListSkeleton(op),47 const std::shared_ptr<media::Engine::MetaDataExtractor>& extractor,
42 d(new Private{op, Private::MetaDataCache{}, extractor})48 const media::apparmor::ubuntu::RequestContextResolver::Ptr& request_context_resolver,
49 const media::apparmor::ubuntu::RequestAuthenticator::Ptr& request_authenticator)
50 : media::TrackListSkeleton(bus, object, request_context_resolver, request_authenticator),
51 d(new Private{object, 0, Private::MetaDataCache{}, extractor, media::TrackList::Container{}})
43{52{
44 can_edit_tracks().set(true);53 can_edit_tracks().set(true);
45}54}
@@ -73,15 +82,16 @@
73 const media::Track::Id& position,82 const media::Track::Id& position,
74 bool make_current)83 bool make_current)
75{84{
76 static size_t track_counter = 0;85 std::cout << __PRETTY_FUNCTION__ << std::endl;
7786
78 std::stringstream ss; ss << d->path.as_string() << "/" << track_counter++;87 std::stringstream ss; ss << d->object->path().as_string() << "/" << d->track_counter++;
79 Track::Id id{ss.str()};88 Track::Id id{ss.str()};
8089
81 auto result = tracks().update([this, id, position, make_current](TrackList::Container& container)90 auto result = tracks().update([this, id, position, make_current](TrackList::Container& container)
82 {91 {
83 auto it = std::find(container.begin(), container.end(), position);92 auto it = std::find(container.begin(), container.end(), position);
84 container.insert(it, id);93 container.insert(it, id);
94
85 return true;95 return true;
86 });96 });
8797
@@ -98,8 +108,15 @@
98 }108 }
99109
100 if (make_current)110 if (make_current)
101 go_to(id);111 {
112 // Don't automatically call stop() and play() in player_implementation.cpp on_go_to_track()
113 // since this breaks video playback when using open_uri() (stop() and play() are unwanted in
114 // this scenario since the qtubuntu-media will handle this automatically)
115 const bool toggle_player_state = false;
116 go_to(id, toggle_player_state);
117 }
102118
119 // Signal to the client that a track was added to the TrackList
103 on_track_added()(id);120 on_track_added()(id);
104 }121 }
105}122}
@@ -121,7 +138,61 @@
121138
122}139}
123140
124void media::TrackListImplementation::go_to(const media::Track::Id& track)141void media::TrackListImplementation::go_to(const media::Track::Id& track, bool toggle_player_state)
125{142{
126 (void) track;143 std::cout << __PRETTY_FUNCTION__ << std::endl;
144 std::pair<const media::Track::Id, bool> p = std::make_pair(track, toggle_player_state);
145 // Signal the Player instance to go to a specific track for playback
146 on_go_to_track()(p);
147 on_track_changed()(track);
148}
149
150void media::TrackListImplementation::shuffle_tracks()
151{
152 std::cout << __PRETTY_FUNCTION__ << std::endl;
153
154 auto result = tracks().update([this](TrackList::Container& container)
155 {
156 d->original_tracklist.assign(container.begin(), container.end());
157 std::random_shuffle(container.begin(), container.end());
158 return true;
159 });
160
161 if (result)
162 {
163 media::TrackList::ContainerTrackIdTuple t{std::make_tuple(tracks().get(), current())};
164 on_track_list_replaced()(t);
165 }
166}
167
168void media::TrackListImplementation::unshuffle_tracks()
169{
170 std::cout << __PRETTY_FUNCTION__ << std::endl;
171
172 auto result = tracks().update([this](TrackList::Container& container)
173 {
174 container.assign(d->original_tracklist.begin(), d->original_tracklist.end());
175 return true;
176 });
177
178 if (result)
179 {
180 media::TrackList::ContainerTrackIdTuple t{std::make_tuple(tracks().get(), current())};
181 on_track_list_replaced()(t);
182 }
183}
184
185void media::TrackListImplementation::reset()
186{
187 std::cout << __PRETTY_FUNCTION__ << std::endl;
188
189 auto result = tracks().update([this](TrackList::Container& container)
190 {
191 container.clear();
192 container.resize(0);
193 d->track_counter = 0;
194 return true;
195 });
196
197 (void) result;
127}198}
128199
=== modified file 'src/core/media/track_list_implementation.h'
--- src/core/media/track_list_implementation.h 2014-03-25 14:57:15 +0000
+++ src/core/media/track_list_implementation.h 2015-04-20 17:49:07 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright © 2013 Canonical Ltd.2 * Copyright © 2013-2015 Canonical Ltd.
3 *3 *
4 * This program is free software: you can redistribute it and/or modify it4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License version 3,5 * under the terms of the GNU Lesser General Public License version 3,
@@ -32,8 +32,11 @@
32{32{
33public:33public:
34 TrackListImplementation(34 TrackListImplementation(
35 const core::dbus::types::ObjectPath& op,35 const core::dbus::Bus::Ptr& bus,
36 const std::shared_ptr<Engine::MetaDataExtractor>& extractor);36 const core::dbus::Object::Ptr& object,
37 const std::shared_ptr<Engine::MetaDataExtractor>& extractor,
38 const core::ubuntu::media::apparmor::ubuntu::RequestContextResolver::Ptr& request_context_resolver,
39 const core::ubuntu::media::apparmor::ubuntu::RequestAuthenticator::Ptr& request_authenticator);
37 ~TrackListImplementation();40 ~TrackListImplementation();
3841
39 Track::UriType query_uri_for_track(const Track::Id& id);42 Track::UriType query_uri_for_track(const Track::Id& id);
@@ -42,7 +45,10 @@
42 void add_track_with_uri_at(const Track::UriType& uri, const Track::Id& position, bool make_current);45 void add_track_with_uri_at(const Track::UriType& uri, const Track::Id& position, bool make_current);
43 void remove_track(const Track::Id& id);46 void remove_track(const Track::Id& id);
4447
45 void go_to(const Track::Id& track);48 void go_to(const Track::Id& track, bool toggle_player_state);
49 void shuffle_tracks();
50 void unshuffle_tracks();
51 void reset();
4652
47private:53private:
48 struct Private;54 struct Private;
4955
=== modified file 'src/core/media/track_list_skeleton.cpp'
--- src/core/media/track_list_skeleton.cpp 2014-03-25 14:57:15 +0000
+++ src/core/media/track_list_skeleton.cpp 2015-04-20 17:49:07 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright © 2013 Canonical Ltd.2 * Copyright © 2015 Canonical Ltd.
3 *3 *
4 * This program is free software: you can redistribute it and/or modify it4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License version 3,5 * under the terms of the GNU Lesser General Public License version 3,
@@ -17,6 +17,7 @@
17 */17 */
1818
19#include "track_list_skeleton.h"19#include "track_list_skeleton.h"
20#include "track_list_implementation.h"
2021
21#include <core/media/player.h>22#include <core/media/player.h>
22#include <core/media/track_list.h>23#include <core/media/track_list.h>
@@ -35,6 +36,7 @@
35#include <core/dbus/types/stl/map.h>36#include <core/dbus/types/stl/map.h>
36#include <core/dbus/types/stl/vector.h>37#include <core/dbus/types/stl/vector.h>
3738
39#include <iostream>
38#include <limits>40#include <limits>
3941
40namespace dbus = core::dbus;42namespace dbus = core::dbus;
@@ -42,14 +44,24 @@
4244
43struct media::TrackListSkeleton::Private45struct media::TrackListSkeleton::Private
44{46{
45 Private(media::TrackListSkeleton* impl,47 Private(media::TrackListSkeleton* impl, const dbus::Bus::Ptr& bus, const dbus::Object::Ptr& object,
46 dbus::Object::Ptr object)48 const apparmor::ubuntu::RequestContextResolver::Ptr& request_context_resolver,
49 const media::apparmor::ubuntu::RequestAuthenticator::Ptr& request_authenticator)
47 : impl(impl),50 : impl(impl),
51 bus(bus),
48 object(object),52 object(object),
49 can_edit_tracks(object->get_property<mpris::TrackList::Properties::CanEditTracks>()),53 request_context_resolver(request_context_resolver),
50 tracks(object->get_property<mpris::TrackList::Properties::Tracks>()),54 request_authenticator(request_authenticator),
51 current_track(tracks->get().begin()),55 skeleton(mpris::TrackList::Skeleton::Configuration{object, mpris::TrackList::Skeleton::Configuration::Defaults{}}),
52 empty_iterator(tracks->get().begin())56 current_track(skeleton.properties.tracks->get().begin()),
57 empty_iterator(skeleton.properties.tracks->get().begin()),
58 loop_status(media::Player::LoopStatus::none),
59 signals
60 {
61 skeleton.signals.track_added,
62 skeleton.signals.track_removed,
63 skeleton.signals.tracklist_replaced
64 }
53 {65 {
54 }66 }
5567
@@ -62,18 +74,29 @@
6274
63 auto reply = dbus::Message::make_method_return(msg);75 auto reply = dbus::Message::make_method_return(msg);
64 reply->writer() << *meta_data;76 reply->writer() << *meta_data;
65 impl->access_bus()->send(reply);77 bus->send(reply);
66 }78 }
6779
68 void handle_add_track_with_uri_at(const core::dbus::Message::Ptr& msg)80 void handle_add_track_with_uri_at(const core::dbus::Message::Ptr& msg)
69 {81 {
70 Track::UriType uri; media::Track::Id after; bool make_current;82 request_context_resolver->resolve_context_for_dbus_name_async(msg->sender(), [this, msg](const media::apparmor::ubuntu::Context& context)
71 msg->reader() >> uri >> after >> make_current;83 {
7284 Track::UriType uri; media::Track::Id after; bool make_current;
73 impl->add_track_with_uri_at(uri, after, make_current);85 msg->reader() >> uri >> after >> make_current;
7486
75 auto reply = dbus::Message::make_method_return(msg);87 // Make sure the client has adequate apparmor permissions to open the URI
76 impl->access_bus()->send(reply);88 const auto result = request_authenticator->authenticate_open_uri_request(context, uri);
89
90 auto reply = dbus::Message::make_method_return(msg);
91 // Only add the track to the TrackList if it passes the apparmor permissions check
92 if (std::get<0>(result))
93 impl->add_track_with_uri_at(uri, after, make_current);
94 else
95 std::cerr << "Warning: Not adding track " << uri <<
96 " to TrackList because of inadequate client apparmor permissions." << std::endl;
97
98 bus->send(reply);
99 });
77 }100 }
78101
79 void handle_remove_track(const core::dbus::Message::Ptr& msg)102 void handle_remove_track(const core::dbus::Message::Ptr& msg)
@@ -84,7 +107,7 @@
84 impl->remove_track(track);107 impl->remove_track(track);
85108
86 auto reply = dbus::Message::make_method_return(msg);109 auto reply = dbus::Message::make_method_return(msg);
87 impl->access_bus()->send(reply);110 bus->send(reply);
88 }111 }
89112
90 void handle_go_to(const core::dbus::Message::Ptr& msg)113 void handle_go_to(const core::dbus::Message::Ptr& msg)
@@ -92,30 +115,64 @@
92 media::Track::Id track;115 media::Track::Id track;
93 msg->reader() >> track;116 msg->reader() >> track;
94117
95 impl->go_to(track);118 current_track = std::find(skeleton.properties.tracks->get().begin(), skeleton.properties.tracks->get().end(), track);
119 const bool toggle_player_state = true;
120 impl->go_to(track, toggle_player_state);
96121
97 auto reply = dbus::Message::make_method_return(msg);122 auto reply = dbus::Message::make_method_return(msg);
98 impl->access_bus()->send(reply);123 bus->send(reply);
99 }124 }
100125
101 media::TrackListSkeleton* impl;126 media::TrackListSkeleton* impl;
127 dbus::Bus::Ptr bus;
102 dbus::Object::Ptr object;128 dbus::Object::Ptr object;
129 media::apparmor::ubuntu::RequestContextResolver::Ptr request_context_resolver;
130 media::apparmor::ubuntu::RequestAuthenticator::Ptr request_authenticator;
103131
104 std::shared_ptr<core::dbus::Property<mpris::TrackList::Properties::CanEditTracks>> can_edit_tracks;132 mpris::TrackList::Skeleton skeleton;
105 std::shared_ptr<core::dbus::Property<mpris::TrackList::Properties::Tracks>> tracks;
106 TrackList::ConstIterator current_track;133 TrackList::ConstIterator current_track;
107 TrackList::ConstIterator empty_iterator;134 TrackList::ConstIterator empty_iterator;
108135 media::Player::LoopStatus loop_status;
109 core::Signal<void> on_track_list_replaced;136
110 core::Signal<Track::Id> on_track_added;137 struct Signals
111 core::Signal<Track::Id> on_track_removed;138 {
112 core::Signal<Track::Id> on_track_changed;139 typedef core::dbus::Signal<mpris::TrackList::Signals::TrackAdded, mpris::TrackList::Signals::TrackAdded::ArgumentType> DBusTrackAddedSignal;
140 typedef core::dbus::Signal<mpris::TrackList::Signals::TrackRemoved, mpris::TrackList::Signals::TrackRemoved::ArgumentType> DBusTrackRemovedSignal;
141 typedef core::dbus::Signal<mpris::TrackList::Signals::TrackListReplaced, mpris::TrackList::Signals::TrackListReplaced::ArgumentType> DBusTrackListReplacedSignal;
142
143 Signals(const std::shared_ptr<DBusTrackAddedSignal>& remote_track_added,
144 const std::shared_ptr<DBusTrackRemovedSignal>& remote_track_removed,
145 const std::shared_ptr<DBusTrackListReplacedSignal>& remote_track_list_replaced)
146 {
147 // Connect all of the MPRIS interface signals to be emitted over dbus
148 on_track_added.connect([remote_track_added](const media::Track::Id &id)
149 {
150 remote_track_added->emit(id);
151 });
152
153 on_track_removed.connect([remote_track_removed](const media::Track::Id &id)
154 {
155 remote_track_removed->emit(id);
156 });
157
158 on_track_list_replaced.connect([remote_track_list_replaced](const media::TrackList::ContainerTrackIdTuple &tltuple)
159 {
160 remote_track_list_replaced->emit(tltuple);
161 });
162 }
163
164 core::Signal<Track::Id> on_track_added;
165 core::Signal<Track::Id> on_track_removed;
166 core::Signal<TrackList::ContainerTrackIdTuple> on_track_list_replaced;
167 core::Signal<Track::Id> on_track_changed;
168 core::Signal<std::pair<Track::Id, bool>> on_go_to_track;
169 } signals;
113};170};
114171
115media::TrackListSkeleton::TrackListSkeleton(172media::TrackListSkeleton::TrackListSkeleton(const core::dbus::Bus::Ptr& bus, const core::dbus::Object::Ptr& object,
116 const dbus::types::ObjectPath& op)173 const media::apparmor::ubuntu::RequestContextResolver::Ptr& request_context_resolver,
117 : dbus::Skeleton<media::TrackList>(the_session_bus()),174 const media::apparmor::ubuntu::RequestAuthenticator::Ptr& request_authenticator)
118 d(new Private(this, access_service()->add_object_for_path(op)))175 : d(new Private(this, bus, object, request_context_resolver, request_authenticator))
119{176{
120 d->object->install_method_handler<mpris::TrackList::GetTracksMetadata>(177 d->object->install_method_handler<mpris::TrackList::GetTracksMetadata>(
121 std::bind(&Private::handle_get_tracks_metadata,178 std::bind(&Private::handle_get_tracks_metadata,
@@ -144,80 +201,156 @@
144201
145bool media::TrackListSkeleton::has_next() const202bool media::TrackListSkeleton::has_next() const
146{203{
147 return d->current_track != d->tracks->get().end();204 const auto next_track = std::next(d->current_track);
205 std::cout << "has_next track? " << (next_track != tracks().get().end() ? "yes" : "no") << std::endl;
206 return next_track != tracks().get().end();
148}207}
149208
150const media::Track::Id& media::TrackListSkeleton::next()209const media::Track::Id& media::TrackListSkeleton::next()
151{210{
152 if (d->tracks->get().empty())211 std::cout << __PRETTY_FUNCTION__ << std::endl;
153 return *(d->current_track);212 if (tracks().get().empty())
154213 return *(d->current_track);
155 if (d->tracks->get().size() && (d->current_track == d->empty_iterator))214
156 { 215 // Loop on the current track forever
157 d->current_track = d->tracks->get().begin();216 if (d->loop_status == media::Player::LoopStatus::track)
158 return *(d->current_track = std::next(d->current_track));217 {
159 }218 std::cout << "Looping on the current track..." << std::endl;
160219 return *(d->current_track);
161 d->current_track = std::next(d->current_track);220 }
221 // Loop over the whole playlist and repeat
222 else if (d->loop_status == media::Player::LoopStatus::playlist && !has_next())
223 {
224 std::cout << "Looping on the entire TrackList..." << std::endl;
225 d->current_track = tracks().get().begin();
226 return *(d->current_track);
227 }
228 else if (has_next())
229 {
230 // Keep returning the next track until the last track is reached
231 d->current_track = std::next(d->current_track);
232 std::cout << *this << std::endl;
233 }
234
235 return *(d->current_track);
236}
237
238const media::Track::Id& media::TrackListSkeleton::current()
239{
240 // Prevent the TrackList from sitting at the end which will cause
241 // a segfault when calling current()
242 if (tracks().get().size() && (d->current_track == d->empty_iterator))
243 d->current_track = d->skeleton.properties.tracks->get().begin();
244 else if (tracks().get().empty())
245 std::cerr << "TrackList is empty therefore there is no valid current track" << std::endl;
246
162 return *(d->current_track);247 return *(d->current_track);
163}248}
164249
165const core::Property<bool>& media::TrackListSkeleton::can_edit_tracks() const250const core::Property<bool>& media::TrackListSkeleton::can_edit_tracks() const
166{251{
167 return *d->can_edit_tracks;252 return *d->skeleton.properties.can_edit_tracks;
168}253}
169254
170core::Property<bool>& media::TrackListSkeleton::can_edit_tracks()255core::Property<bool>& media::TrackListSkeleton::can_edit_tracks()
171{256{
172 return *d->can_edit_tracks;257 return *d->skeleton.properties.can_edit_tracks;
173}258}
174259
175core::Property<media::TrackList::Container>& media::TrackListSkeleton::tracks()260core::Property<media::TrackList::Container>& media::TrackListSkeleton::tracks()
176{261{
177 return *d->tracks;262 return *d->skeleton.properties.tracks;
263}
264
265void media::TrackListSkeleton::on_loop_status_changed(const media::Player::LoopStatus& loop_status)
266{
267 d->loop_status = loop_status;
268}
269
270media::Player::LoopStatus media::TrackListSkeleton::loop_status() const
271{
272 return d->loop_status;
273}
274
275void media::TrackListSkeleton::on_shuffle_changed(bool shuffle)
276{
277 if (shuffle)
278 shuffle_tracks();
279 else
280 unshuffle_tracks();
178}281}
179282
180const core::Property<media::TrackList::Container>& media::TrackListSkeleton::tracks() const283const core::Property<media::TrackList::Container>& media::TrackListSkeleton::tracks() const
181{284{
182 return *d->tracks;285 return *d->skeleton.properties.tracks;
183}286}
184287
185const core::Signal<void>& media::TrackListSkeleton::on_track_list_replaced() const288const core::Signal<media::TrackList::ContainerTrackIdTuple>& media::TrackListSkeleton::on_track_list_replaced() const
186{289{
187 return d->on_track_list_replaced;290 // Print the TrackList instance
291 std::cout << *this << std::endl;
292 return d->signals.on_track_list_replaced;
188}293}
189294
190const core::Signal<media::Track::Id>& media::TrackListSkeleton::on_track_added() const295const core::Signal<media::Track::Id>& media::TrackListSkeleton::on_track_added() const
191{296{
192 return d->on_track_added;297 return d->signals.on_track_added;
193}298}
194299
195const core::Signal<media::Track::Id>& media::TrackListSkeleton::on_track_removed() const300const core::Signal<media::Track::Id>& media::TrackListSkeleton::on_track_removed() const
196{301{
197 return d->on_track_removed;302 return d->signals.on_track_removed;
198}303}
199304
200const core::Signal<media::Track::Id>& media::TrackListSkeleton::on_track_changed() const305const core::Signal<media::Track::Id>& media::TrackListSkeleton::on_track_changed() const
201{306{
202 return d->on_track_changed;307 return d->signals.on_track_changed;
203}308}
204309
205core::Signal<void>& media::TrackListSkeleton::on_track_list_replaced()310const core::Signal<std::pair<media::Track::Id, bool>>& media::TrackListSkeleton::on_go_to_track() const
206{311{
207 return d->on_track_list_replaced;312 return d->signals.on_go_to_track;
313}
314
315core::Signal<media::TrackList::ContainerTrackIdTuple>& media::TrackListSkeleton::on_track_list_replaced()
316{
317 return d->signals.on_track_list_replaced;
208}318}
209319
210core::Signal<media::Track::Id>& media::TrackListSkeleton::on_track_added()320core::Signal<media::Track::Id>& media::TrackListSkeleton::on_track_added()
211{321{
212 return d->on_track_added;322 return d->signals.on_track_added;
213}323}
214324
215core::Signal<media::Track::Id>& media::TrackListSkeleton::on_track_removed()325core::Signal<media::Track::Id>& media::TrackListSkeleton::on_track_removed()
216{326{
217 return d->on_track_removed;327 return d->signals.on_track_removed;
218}328}
219329
220core::Signal<media::Track::Id>& media::TrackListSkeleton::on_track_changed()330core::Signal<media::Track::Id>& media::TrackListSkeleton::on_track_changed()
221{331{
222 return d->on_track_changed;332 return d->signals.on_track_changed;
223}333}
334
335core::Signal<std::pair<media::Track::Id, bool>>& media::TrackListSkeleton::on_go_to_track()
336{
337 return d->signals.on_go_to_track;
338}
339
340// operator<< pretty prints the given TrackList to the given output stream.
341inline std::ostream& media::operator<<(std::ostream& out, const media::TrackList& tracklist)
342{
343 auto non_const_tl = const_cast<media::TrackList*>(&tracklist);
344 out << "TrackList\n---------------" << std::endl;
345 for (const media::Track::Id &id : tracklist.tracks().get())
346 {
347 // '*' denotes the current track
348 out << "\t" << ((dynamic_cast<media::TrackListSkeleton*>(non_const_tl)->current() == id) ? "*" : "");
349 out << "Track Id: " << id << std::endl;
350 out << "\t\turi: " << dynamic_cast<media::TrackListImplementation*>(non_const_tl)->query_uri_for_track(id) << std::endl;
351 }
352
353 out << "---------------\nEnd TrackList" << std::endl;
354 return out;
355}
356
224357
=== modified file 'src/core/media/track_list_skeleton.h'
--- src/core/media/track_list_skeleton.h 2014-03-25 14:57:15 +0000
+++ src/core/media/track_list_skeleton.h 2015-04-20 17:49:07 +0000
@@ -18,10 +18,13 @@
18#ifndef CORE_UBUNTU_MEDIA_TRACK_LIST_SKELETON_H_18#ifndef CORE_UBUNTU_MEDIA_TRACK_LIST_SKELETON_H_
19#define CORE_UBUNTU_MEDIA_TRACK_LIST_SKELETON_H_19#define CORE_UBUNTU_MEDIA_TRACK_LIST_SKELETON_H_
2020
21#include "apparmor/ubuntu.h"
22
21#include <core/media/track_list.h>23#include <core/media/track_list.h>
2224
23#include <core/media/player.h>25#include <core/media/player.h>
2426
27#include <core/dbus/object.h>
25#include <core/dbus/skeleton.h>28#include <core/dbus/skeleton.h>
2629
27namespace core30namespace core
@@ -30,38 +33,52 @@
30{33{
31namespace media34namespace media
32{35{
33class TrackListSkeleton : public core::dbus::Skeleton<core::ubuntu::media::TrackList>36class TrackListSkeleton : public core::ubuntu::media::TrackList
34{37{
35public:38public:
36 TrackListSkeleton(39 TrackListSkeleton(const core::dbus::Bus::Ptr& bus, const core::dbus::Object::Ptr& object,
37 const core::dbus::types::ObjectPath& op);40 const core::ubuntu::media::apparmor::ubuntu::RequestContextResolver::Ptr& request_context_resolver,
41 const core::ubuntu::media::apparmor::ubuntu::RequestAuthenticator::Ptr& request_authenticator);
38 ~TrackListSkeleton();42 ~TrackListSkeleton();
3943
40 bool has_next() const;44 bool has_next() const;
41 const Track::Id& next();45 const Track::Id& next();
46 const Track::Id& current();
4247
43 const core::Property<bool>& can_edit_tracks() const;48 const core::Property<bool>& can_edit_tracks() const;
44 const core::Property<Container>& tracks() const;49 const core::Property<Container>& tracks() const;
4550
46 const core::Signal<void>& on_track_list_replaced() const;51 const core::Signal<ContainerTrackIdTuple>& on_track_list_replaced() const;
47 const core::Signal<Track::Id>& on_track_added() const;52 const core::Signal<Track::Id>& on_track_added() const;
48 const core::Signal<Track::Id>& on_track_removed() const;53 const core::Signal<Track::Id>& on_track_removed() const;
49 const core::Signal<Track::Id>& on_track_changed() const;54 const core::Signal<Track::Id>& on_track_changed() const;
55 const core::Signal<std::pair<Track::Id, bool>>& on_go_to_track() const;
56 core::Signal<std::pair<Track::Id, bool>>& on_go_to_track();
57 core::Signal<Track::Id>& on_track_removed();
5058
51 core::Property<Container>& tracks();59 core::Property<Container>& tracks();
60 void on_loop_status_changed(const core::ubuntu::media::Player::LoopStatus& loop_status);
61 core::ubuntu::media::Player::LoopStatus loop_status() const;
62
63 /** Gets called when the shuffle property on the Player interface is changed
64 * by the client */
65 void on_shuffle_changed(bool shuffle);
5266
53protected:67protected:
54 core::Property<bool>& can_edit_tracks();68 core::Property<bool>& can_edit_tracks();
5569
56 core::Signal<void>& on_track_list_replaced();70 core::Signal<ContainerTrackIdTuple>& on_track_list_replaced();
57 core::Signal<Track::Id>& on_track_added();71 core::Signal<Track::Id>& on_track_added();
58 core::Signal<Track::Id>& on_track_removed();
59 core::Signal<Track::Id>& on_track_changed();72 core::Signal<Track::Id>& on_track_changed();
6073
61private:74private:
62 struct Private;75 struct Private;
63 std::unique_ptr<Private> d;76 std::unique_ptr<Private> d;
64};77};
78
79// operator<< pretty prints the given TrackList status to the given output stream.
80std::ostream& operator<<(std::ostream& out, const core::ubuntu::media::TrackList& tracklist);
81
65}82}
66}83}
67}84}
6885
=== modified file 'src/core/media/track_list_stub.cpp'
--- src/core/media/track_list_stub.cpp 2014-02-12 15:53:57 +0000
+++ src/core/media/track_list_stub.cpp 2015-04-20 17:49:07 +0000
@@ -43,10 +43,10 @@
43 Private(43 Private(
44 TrackListStub* impl,44 TrackListStub* impl,
45 const std::shared_ptr<media::Player>& parent,45 const std::shared_ptr<media::Player>& parent,
46 const dbus::types::ObjectPath& op)46 const dbus::Object::Ptr& object)
47 : impl(impl),47 : impl(impl),
48 parent(parent),48 parent(parent),
49 object(impl->access_service()->object_for_path(op)),49 object(object),
50 can_edit_tracks(object->get_property<mpris::TrackList::Properties::CanEditTracks>()),50 can_edit_tracks(object->get_property<mpris::TrackList::Properties::CanEditTracks>()),
51 tracks(object->get_property<mpris::TrackList::Properties::Tracks>())51 tracks(object->get_property<mpris::TrackList::Properties::Tracks>())
52 {52 {
@@ -59,17 +59,17 @@
59 std::shared_ptr<core::dbus::Property<mpris::TrackList::Properties::CanEditTracks>> can_edit_tracks;59 std::shared_ptr<core::dbus::Property<mpris::TrackList::Properties::CanEditTracks>> can_edit_tracks;
60 std::shared_ptr<core::dbus::Property<mpris::TrackList::Properties::Tracks>> tracks;60 std::shared_ptr<core::dbus::Property<mpris::TrackList::Properties::Tracks>> tracks;
6161
62 core::Signal<void> on_track_list_replaced;62 core::Signal<media::TrackList::ContainerTrackIdTuple> on_track_list_replaced;
63 core::Signal<Track::Id> on_track_added;63 core::Signal<Track::Id> on_track_added;
64 core::Signal<Track::Id> on_track_removed;64 core::Signal<Track::Id> on_track_removed;
65 core::Signal<Track::Id> on_track_changed;65 core::Signal<Track::Id> on_track_changed;
66 core::Signal<std::pair<Track::Id, bool>> on_go_to_track;
66};67};
6768
68media::TrackListStub::TrackListStub(69media::TrackListStub::TrackListStub(
69 const std::shared_ptr<media::Player>& parent,70 const std::shared_ptr<media::Player>& parent,
70 const core::dbus::types::ObjectPath& op)71 const core::dbus::Object::Ptr& object)
71 : dbus::Stub<media::TrackList>(the_session_bus()),72 : d(new Private(this, parent, object))
72 d(new Private(this, parent, op))
73{73{
74}74}
7575
@@ -89,8 +89,7 @@
8989
90media::Track::MetaData media::TrackListStub::query_meta_data_for_track(const media::Track::Id& id)90media::Track::MetaData media::TrackListStub::query_meta_data_for_track(const media::Track::Id& id)
91{91{
92 auto op92 auto op = d->object->invoke_method_synchronously<
93 = d->object->invoke_method_synchronously<
94 mpris::TrackList::GetTracksMetadata,93 mpris::TrackList::GetTracksMetadata,
95 std::map<std::string, std::string>>(id);94 std::map<std::string, std::string>>(id);
9695
@@ -128,8 +127,9 @@
128 throw std::runtime_error("Problem removing track: " + op.error());127 throw std::runtime_error("Problem removing track: " + op.error());
129}128}
130129
131void media::TrackListStub::go_to(const media::Track::Id& track)130void media::TrackListStub::go_to(const media::Track::Id& track, bool toggle_player_state)
132{131{
132 (void) toggle_player_state;
133 auto op = d->object->invoke_method_synchronously<mpris::TrackList::GoTo, void>(133 auto op = d->object->invoke_method_synchronously<mpris::TrackList::GoTo, void>(
134 track);134 track);
135135
@@ -137,18 +137,36 @@
137 throw std::runtime_error("Problem adding track: " + op.error());137 throw std::runtime_error("Problem adding track: " + op.error());
138}138}
139139
140const core::Signal<void>& media::TrackListStub::on_track_list_replaced() const140void media::TrackListStub::shuffle_tracks()
141{141{
142 std::cerr << "shuffle_tracks() does nothing from the client side" << std::endl;
143}
144
145void media::TrackListStub::unshuffle_tracks()
146{
147 std::cerr << "unshuffle_tracks() does nothing from the client side" << std::endl;
148}
149
150void media::TrackListStub::reset()
151{
152 std::cerr << "reset() does nothing from the client side" << std::endl;
153}
154
155const core::Signal<media::TrackList::ContainerTrackIdTuple>& media::TrackListStub::on_track_list_replaced() const
156{
157 std::cout << "Signal on_track_list_replaced arrived via the bus" << std::endl;
142 return d->on_track_list_replaced;158 return d->on_track_list_replaced;
143}159}
144160
145const core::Signal<media::Track::Id>& media::TrackListStub::on_track_added() const161const core::Signal<media::Track::Id>& media::TrackListStub::on_track_added() const
146{162{
163 std::cout << "Signal on_track_added arrived via the bus" << std::endl;
147 return d->on_track_added;164 return d->on_track_added;
148}165}
149166
150const core::Signal<media::Track::Id>& media::TrackListStub::on_track_removed() const167const core::Signal<media::Track::Id>& media::TrackListStub::on_track_removed() const
151{168{
169 std::cout << "Signal on_track_removed arrived via the bus" << std::endl;
152 return d->on_track_removed;170 return d->on_track_removed;
153}171}
154172
@@ -156,3 +174,8 @@
156{174{
157 return d->on_track_changed;175 return d->on_track_changed;
158}176}
177
178const core::Signal<std::pair<media::Track::Id, bool>>& media::TrackListStub::on_go_to_track() const
179{
180 return d->on_go_to_track;
181}
159182
=== modified file 'src/core/media/track_list_stub.h'
--- src/core/media/track_list_stub.h 2014-02-12 15:53:57 +0000
+++ src/core/media/track_list_stub.h 2015-04-20 17:49:07 +0000
@@ -33,12 +33,12 @@
33{33{
34namespace media34namespace media
35{35{
36class TrackListStub : public core::dbus::Stub<core::ubuntu::media::TrackList>36class TrackListStub : public core::ubuntu::media::TrackList
37{37{
38public:38public:
39 TrackListStub(39 TrackListStub(
40 const std::shared_ptr<Player>& parent,40 const std::shared_ptr<Player>& parent,
41 const core::dbus::types::ObjectPath& op);41 const core::dbus::Object::Ptr& object);
42 ~TrackListStub();42 ~TrackListStub();
4343
44 const core::Property<bool>& can_edit_tracks() const;44 const core::Property<bool>& can_edit_tracks() const;
@@ -49,12 +49,18 @@
49 void add_track_with_uri_at(const Track::UriType& uri, const Track::Id& position, bool make_current);49 void add_track_with_uri_at(const Track::UriType& uri, const Track::Id& position, bool make_current);
50 void remove_track(const Track::Id& id);50 void remove_track(const Track::Id& id);
5151
52 void go_to(const Track::Id& track);52 void go_to(const Track::Id& track, bool toggle_player_state);
5353
54 const core::Signal<void>& on_track_list_replaced() const;54 void shuffle_tracks();
55 void unshuffle_tracks();
56
57 void reset();
58
59 const core::Signal<ContainerTrackIdTuple>& on_track_list_replaced() const;
55 const core::Signal<Track::Id>& on_track_added() const;60 const core::Signal<Track::Id>& on_track_added() const;
56 const core::Signal<Track::Id>& on_track_removed() const;61 const core::Signal<Track::Id>& on_track_removed() const;
57 const core::Signal<Track::Id>& on_track_changed() const;62 const core::Signal<Track::Id>& on_track_changed() const;
63 const core::Signal<std::pair<Track::Id, bool>>& on_go_to_track() const;
5864
59private:65private:
60 struct Private;66 struct Private;
6167
=== modified file 'tests/CMakeLists.txt'
--- tests/CMakeLists.txt 2014-11-26 11:04:57 +0000
+++ tests/CMakeLists.txt 2015-04-20 17:49:07 +0000
@@ -51,4 +51,5 @@
51)51)
5252
53# add_subdirectory(acceptance-tests)53# add_subdirectory(acceptance-tests)
54add_subdirectory(test-track-list)
54add_subdirectory(unit-tests)55add_subdirectory(unit-tests)
5556
=== added directory 'tests/test-track-list'
=== added file 'tests/test-track-list/CMakeLists.txt'
--- tests/test-track-list/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ tests/test-track-list/CMakeLists.txt 2015-04-20 17:49:07 +0000
@@ -0,0 +1,17 @@
1include_directories(
2 ${CMAKE_SOURCE_DIR}/src)
3
4add_executable(
5 test_track_list
6 test_track_list.cpp
7 )
8
9target_link_libraries(
10 test_track_list
11
12 media-hub-client
13
14 ${CMAKE_THREAD_LIBS_INIT}
15 ${DBUS_LIBRARIES}
16 ${PROCESS_CPP_LDFLAGS}
17)
018
=== added file 'tests/test-track-list/test_track_list.cpp'
--- tests/test-track-list/test_track_list.cpp 1970-01-01 00:00:00 +0000
+++ tests/test-track-list/test_track_list.cpp 2015-04-20 17:49:07 +0000
@@ -0,0 +1,346 @@
1/*
2 * Copyright © 2013-2015 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License version 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY {} without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Jim Hodapp <jim.hodapp@canonical.com>
17 */
18
19#include "test_track_list.h"
20#include "../../src/core/media/util/timeout.h"
21
22#include <core/media/service.h>
23#include <core/media/track_list.h>
24
25#include <cassert>
26#include <future>
27#include <iostream>
28#include <thread>
29
30namespace media = core::ubuntu::media;
31using namespace std;
32
33media::TestTrackList::TestTrackList()
34{
35 try {
36 m_hubService = media::Service::Client::instance();
37 }
38 catch (std::runtime_error &e) {
39 cerr << "FATAL: Failed to connect to media-hub service: " << e.what() << endl;
40 }
41}
42
43media::TestTrackList::~TestTrackList()
44{
45}
46
47void media::TestTrackList::create_new_player_session()
48{
49 try {
50 m_hubPlayerSession = m_hubService->create_session(media::Player::Client::default_configuration());
51 }
52 catch (std::runtime_error &e) {
53 cerr << "FATAL: Failed to start a new media-hub player session: " << e.what() << endl;
54 }
55
56 try {
57 m_hubTrackList = m_hubPlayerSession->track_list();
58 }
59 catch (std::runtime_error &e) {
60 cerr << "FATAL: Failed to retrieve the current player's TrackList: " << e.what() << endl;
61 }
62}
63
64void media::TestTrackList::destroy_player_session()
65{
66 // TODO: explicitly add a destroy session to the Service class after ricmm lands his new creation_session
67 // that returns a session ID. This will allow me to clear the tracklist after each test.
68 m_hubPlayerSession.reset();
69}
70
71void media::TestTrackList::add_track(const string &uri, bool make_current)
72{
73 assert (m_hubTrackList.get() != nullptr);
74
75 cout << "Adding " << uri << " to the TrackList for playback." << endl;
76
77 try {
78 bool can_edit_tracks = m_hubTrackList->can_edit_tracks();
79 cout << "can_edit_tracks: " << (can_edit_tracks ? "yes" : "no") << std::endl;
80 if (can_edit_tracks)
81 m_hubTrackList->add_track_with_uri_at(uri, media::TrackList::after_empty_track(), make_current);
82 else
83 cerr << "Can't add track to TrackList since can_edit_tracks is false" << endl;
84 }
85 catch (std::runtime_error &e) {
86 cerr << "ERROR: Failed to add track " << uri << " to tracklist: " << e.what() << endl;
87 }
88}
89
90#include <unistd.h>
91void media::TestTrackList::test_basic_playback(const std::string &uri1, const std::string &uri2)
92{
93 cout << "--> Running test: test_basic_playback" << std::endl;
94
95 core::Connection c =m_hubTrackList->on_track_added().connect([](const media::Track::Id &new_id)
96 {
97 cout << "Added track to TrackList with Id: " << new_id << endl;
98 });
99
100 //create_new_player_session();
101
102 m_hubPlayerSession->open_uri(uri1);
103 if (!uri2.empty())
104 add_track(uri2);
105
106 //cout << "Waiting for track to be added to TrackList" << endl;
107 //media::Track::Id id = wait_for_on_track_added();
108
109 m_hubPlayerSession->play();
110 m_hubPlayerSession->loop_status() = media::Player::LoopStatus::none;
111
112 if (m_hubPlayerSession->playback_status() == media::Player::PlaybackStatus::playing)
113 {
114 cout << "Waiting for first track to finish playing..." << endl;
115 wait_for_about_to_finish();
116 cout << "Basic playback was successful" << endl;
117 }
118
119 c.disconnect();
120
121 //destroy_player_session();
122}
123
124void media::TestTrackList::test_ensure_tracklist_is_not_empty(const std::string &uri1, const std::string &uri2)
125{
126 cout << "--> Running test: test_ensure_tracklist_is_not_empty" << std::endl;
127
128 add_track(uri1);
129 if (!uri2.empty())
130 add_track(uri2);
131
132 if (m_hubTrackList->tracks()->size() == 1 or m_hubTrackList->tracks()->size() == 2)
133 cout << "TrackList is not empty, test success" << endl;
134 else
135 cout << "TrackList is empty, test failure" << endl;
136}
137
138void media::TestTrackList::test_has_next_track(const std::string &uri1, const std::string &uri2)
139{
140 cout << "--> Running test: test_has_next_track" << std::endl;
141
142 //create_new_player_session();
143
144 add_track(uri1);
145 add_track(uri2);
146
147 m_hubPlayerSession->play();
148 m_hubPlayerSession->loop_status() = media::Player::LoopStatus::none;
149
150 if (m_hubPlayerSession->playback_status() == media::Player::PlaybackStatus::playing)
151 {
152 cout << "Waiting for first track to finish playing..." << endl;
153 wait_for_about_to_finish();
154 cout << "Waiting for second track to finish playing..." << endl;
155 wait_for_about_to_finish();
156 cout << "Both tracks played successfully" << endl;
157 }
158 else
159 cerr << "Playback did not start successfully" << endl;
160
161 //destroy_player_session();
162}
163
164void media::TestTrackList::test_shuffle(const std::string &uri1, const std::string &uri2, const std::string &uri3)
165{
166 cout << "--> Running test: test_shuffle" << std::endl;
167
168 add_track(uri1);
169 add_track(uri2);
170 add_track(uri3);
171 add_track(uri3);
172 add_track(uri2);
173 add_track(uri1);
174 add_track(uri1);
175 add_track(uri2);
176
177 m_hubPlayerSession->play();
178 m_hubPlayerSession->loop_status() = media::Player::LoopStatus::playlist;
179 m_hubPlayerSession->shuffle() = true;
180
181 if (m_hubPlayerSession->playback_status() == media::Player::PlaybackStatus::playing)
182 {
183 cout << "Waiting for first track to finish playing..." << endl;
184 wait_for_about_to_finish();
185
186 cout << "Turning off shuffle mode" << endl;
187 m_hubPlayerSession->shuffle() = false;
188
189 cout << "Waiting for second track to finish playing..." << endl;
190 wait_for_about_to_finish();
191
192 cout << "Going straight to the Track with Id of '/core/ubuntu/media/Service/sessions/0/TrackList/4'" << std::endl;
193 const media::Track::Id id{"/core/ubuntu/media/Service/sessions/0/TrackList/4"};
194 const bool toggle_player_state = true;
195 m_hubTrackList->go_to(id, toggle_player_state);
196 cout << "Waiting for third track to finish playing..." << endl;
197 wait_for_about_to_finish();
198 }
199 else
200 cerr << "Playback did not start successfully" << endl;
201}
202
203void media::TestTrackList::test_remove_track(const std::string &uri1, const std::string &uri2, const std::string &uri3)
204{
205 cout << "--> Running test: test_remove_track" << std::endl;
206
207 core::Connection c =m_hubTrackList->on_track_removed().connect([](const media::Track::Id &new_id)
208 {
209 cout << "Removed track from TrackList with Id: " << new_id << endl;
210 });
211
212 add_track(uri1);
213 add_track(uri2);
214 add_track(uri3);
215
216 m_hubPlayerSession->play();
217
218 if (m_hubPlayerSession->playback_status() == media::Player::PlaybackStatus::playing)
219 {
220 cout << "Waiting for first track to finish playing..." << endl;
221 wait_for_about_to_finish();
222
223 const media::Track::Id id{"/core/ubuntu/media/Service/sessions/0/TrackList/1"};
224 m_hubTrackList->remove_track(id);
225
226 cout << "Waiting for track after removed track to finish playing..." << endl;
227 wait_for_about_to_finish();
228 }
229 else
230 cerr << "Playback did not start successfully" << endl;
231}
232
233template<class T>
234bool media::TestTrackList::verify_signal_is_emitted(const core::Signal<T> &signal, const std::chrono::milliseconds &timeout)
235{
236 bool signal_emitted = false;
237#if 0
238 static const std::chrono::milliseconds timeout{1000};
239 media::timeout(timeout.count(), true, [wp]()
240 {
241 if (auto sp = wp.lock())
242 sp->on_client_died();
243 });
244 signal.connect([signal_emitted]()
245 {
246 });
247#endif
248
249 return false;
250}
251
252void media::TestTrackList::wait_for_about_to_finish()
253{
254 bool received_about_to_finish = false;
255 core::Connection c = m_hubPlayerSession->about_to_finish().connect([&received_about_to_finish]()
256 {
257 cout << "AboutToFinish signaled" << endl;
258 received_about_to_finish = true;
259 });
260 while (!received_about_to_finish)
261 std::this_thread::yield();
262
263 c.disconnect();
264}
265
266void media::TestTrackList::wait_for_end_of_stream()
267{
268 bool reached_end_of_first_track = false;
269 core::Connection c = m_hubPlayerSession->end_of_stream().connect([&reached_end_of_first_track]()
270 {
271 cout << "EndOfStream signaled" << endl;
272 reached_end_of_first_track = true;
273 });
274 while (!reached_end_of_first_track)
275 std::this_thread::yield();
276
277 c.disconnect();
278}
279
280void media::TestTrackList::wait_for_playback_status_changed(core::ubuntu::media::Player::PlaybackStatus status)
281{
282 bool received_playback_status_changed = false;
283 core::Connection c = m_hubPlayerSession->playback_status().changed().connect([&received_playback_status_changed, &status](media::Player::PlaybackStatus new_status)
284 {
285 cout << "PlaybackStatusChanged signaled" << endl;
286 if (new_status == status)
287 received_playback_status_changed = true;
288 });
289 while (!received_playback_status_changed)
290 std::this_thread::yield();
291
292 c.disconnect();
293}
294
295core::ubuntu::media::Track::Id media::TestTrackList::wait_for_on_track_added()
296{
297 media::Track::Id id;
298 bool received_on_track_added = false;
299 core::Connection c = m_hubTrackList->on_track_added().connect([&received_on_track_added, &id](const media::Track::Id &new_id)
300 {
301 cout << "OnTrackAdded signaled" << endl;
302 id = new_id;
303 received_on_track_added = true;
304 });
305 while (!received_on_track_added)
306 std::this_thread::yield();
307
308 c.disconnect();
309
310 return id;
311}
312
313int main (int argc, char **argv)
314{
315 shared_ptr<media::TestTrackList> tracklist = make_shared<media::TestTrackList>();
316
317 if (argc == 2)
318 {
319 tracklist->create_new_player_session();
320 tracklist->test_basic_playback(argv[1]);
321 tracklist->test_ensure_tracklist_is_not_empty(argv[1]);
322 }
323 else if (argc == 3)
324 {
325 tracklist->create_new_player_session();
326 tracklist->test_basic_playback(argv[1], argv[2]);
327 tracklist->test_ensure_tracklist_is_not_empty(argv[1], argv[2]);
328 tracklist->test_has_next_track(argv[1], argv[2]);
329 }
330 else if (argc == 4)
331 {
332 tracklist->create_new_player_session();
333 tracklist->test_basic_playback(argv[1]);
334 tracklist->test_ensure_tracklist_is_not_empty(argv[1], argv[2]);
335 tracklist->test_has_next_track(argv[1], argv[2]);
336 tracklist->test_shuffle(argv[1], argv[2], argv[3]);
337 tracklist->test_remove_track(argv[1], argv[2], argv[3]);
338 }
339 else
340 {
341 cout << "Can't test TrackList, no track(s) specified." << endl;
342 cout << argv[0] << " <track_uri_1> [<track_uri_2>] [<track_uri_3>]" << endl;
343 }
344
345 return 0;
346}
0347
=== added file 'tests/test-track-list/test_track_list.h'
--- tests/test-track-list/test_track_list.h 1970-01-01 00:00:00 +0000
+++ tests/test-track-list/test_track_list.h 2015-04-20 17:49:07 +0000
@@ -0,0 +1,86 @@
1/*
2 * Copyright © 2013-2015 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License version 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY {} without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Jim Hodapp <jim.hodapp@canonical.com>
17 */
18
19#ifndef TEST_TRACK_LIST_H_
20#define TEST_TRACK_LIST_H_
21
22#include <core/media/player.h>
23#include <core/media/track.h>
24
25#include <core/dbus/signal.h>
26
27#include <chrono>
28#include <memory>
29#include <string>
30
31namespace core
32{
33namespace ubuntu
34{
35namespace media
36{
37
38class Player;
39class Service;
40class TrackList;
41
42class TestTrackList
43{
44public:
45 TestTrackList();
46 ~TestTrackList();
47
48 void create_new_player_session();
49 void destroy_player_session();
50
51 void add_track(const std::string &uri, bool make_current = false);
52
53 // Takes in one or two files for playback, adds it/them to the TrackList, and plays
54 void test_basic_playback(const std::string &uri1, const std::string &uri2 = std::string{});
55
56 void test_ensure_tracklist_is_not_empty(const std::string &uri1, const std::string &uri2 = std::string{});
57
58 // Takes in one or two files for playback, adds it/them to the TrackList, plays and makes sure
59 // that the Player advances the TrackList
60 void test_has_next_track(const std::string &uri1, const std::string &uri2);
61
62 void test_shuffle(const std::string &uri1, const std::string &uri2, const std::string &uri3);
63
64 void test_remove_track(const std::string &uri1, const std::string &uri2, const std::string &uri3);
65
66protected:
67 // Synchronously verify that a signal is emitted waiting up to timeout milliseconds
68 template<class T>
69 bool verify_signal_is_emitted(const core::Signal<T> &signal, const std::chrono::milliseconds &timeout);
70
71 void wait_for_about_to_finish();
72 void wait_for_end_of_stream();
73 void wait_for_playback_status_changed(core::ubuntu::media::Player::PlaybackStatus status);
74 core::ubuntu::media::Track::Id wait_for_on_track_added();
75
76private:
77 std::shared_ptr<core::ubuntu::media::Service> m_hubService;
78 std::shared_ptr<core::ubuntu::media::Player> m_hubPlayerSession;
79 std::shared_ptr<core::ubuntu::media::TrackList> m_hubTrackList;
80};
81
82} // media
83} // ubuntu
84} // core
85
86#endif
087
=== modified file 'tests/unit-tests/test-gstreamer-engine.cpp'
--- tests/unit-tests/test-gstreamer-engine.cpp 2015-03-03 22:41:22 +0000
+++ tests/unit-tests/test-gstreamer-engine.cpp 2015-04-20 17:49:07 +0000
@@ -103,7 +103,8 @@
103 std::ref(wst),103 std::ref(wst),
104 std::placeholders::_1));104 std::placeholders::_1));
105105
106 EXPECT_TRUE(engine.open_resource_for_uri(test_file_uri));106 static const bool do_pipeline_reset = true;
107 EXPECT_TRUE(engine.open_resource_for_uri(test_file_uri, do_pipeline_reset));
107 EXPECT_TRUE(engine.play());108 EXPECT_TRUE(engine.play());
108 EXPECT_TRUE(wst.wait_for_state_for(109 EXPECT_TRUE(wst.wait_for_state_for(
109 core::ubuntu::media::Engine::State::playing,110 core::ubuntu::media::Engine::State::playing,
@@ -144,7 +145,8 @@
144 std::ref(wst),145 std::ref(wst),
145 std::placeholders::_1));146 std::placeholders::_1));
146147
147 EXPECT_TRUE(engine.open_resource_for_uri(test_file_uri));148 static const bool do_pipeline_reset = true;
149 EXPECT_TRUE(engine.open_resource_for_uri(test_file_uri, do_pipeline_reset));
148 EXPECT_TRUE(engine.play());150 EXPECT_TRUE(engine.play());
149 EXPECT_TRUE(wst.wait_for_state_for(151 EXPECT_TRUE(wst.wait_for_state_for(
150 core::ubuntu::media::Engine::State::playing,152 core::ubuntu::media::Engine::State::playing,
@@ -234,7 +236,8 @@
234 std::ref(wst),236 std::ref(wst),
235 std::placeholders::_1));237 std::placeholders::_1));
236238
237 EXPECT_TRUE(engine.open_resource_for_uri(test_file_uri));239 static const bool do_pipeline_reset = true;
240 EXPECT_TRUE(engine.open_resource_for_uri(test_file_uri, do_pipeline_reset));
238 EXPECT_TRUE(engine.play());241 EXPECT_TRUE(engine.play());
239 EXPECT_TRUE(wst.wait_for_state_for(242 EXPECT_TRUE(wst.wait_for_state_for(
240 core::ubuntu::media::Engine::State::playing,243 core::ubuntu::media::Engine::State::playing,
@@ -282,7 +285,8 @@
282 std::ref(wst),285 std::ref(wst),
283 std::placeholders::_1));286 std::placeholders::_1));
284287
285 EXPECT_TRUE(engine.open_resource_for_uri(test_file_uri));288 static const bool do_pipeline_reset = true;
289 EXPECT_TRUE(engine.open_resource_for_uri(test_file_uri, do_pipeline_reset));
286 EXPECT_TRUE(engine.play());290 EXPECT_TRUE(engine.play());
287 EXPECT_TRUE(wst.wait_for_state_for(291 EXPECT_TRUE(wst.wait_for_state_for(
288 core::ubuntu::media::Engine::State::playing,292 core::ubuntu::media::Engine::State::playing,
@@ -328,7 +332,8 @@
328 std::ref(wst),332 std::ref(wst),
329 std::placeholders::_1));333 std::placeholders::_1));
330334
331 EXPECT_TRUE(engine.open_resource_for_uri(test_file_uri));335 static const bool do_pipeline_reset = true;
336 EXPECT_TRUE(engine.open_resource_for_uri(test_file_uri, do_pipeline_reset));
332 EXPECT_TRUE(engine.play());337 EXPECT_TRUE(engine.play());
333 EXPECT_TRUE(wst.wait_for_state_for(338 EXPECT_TRUE(wst.wait_for_state_for(
334 core::ubuntu::media::Engine::State::playing,339 core::ubuntu::media::Engine::State::playing,
@@ -363,7 +368,8 @@
363 &core::testing::WaitableStateTransition<core::ubuntu::media::Engine::State>::trigger,368 &core::testing::WaitableStateTransition<core::ubuntu::media::Engine::State>::trigger,
364 std::ref(wst),369 std::ref(wst),
365 std::placeholders::_1));370 std::placeholders::_1));
366 EXPECT_TRUE(engine.open_resource_for_uri(test_file_uri));371 static const bool do_pipeline_reset = true;
372 EXPECT_TRUE(engine.open_resource_for_uri(test_file_uri, do_pipeline_reset));
367 EXPECT_TRUE(engine.play());373 EXPECT_TRUE(engine.play());
368 EXPECT_TRUE(wst.wait_for_state_for(374 EXPECT_TRUE(wst.wait_for_state_for(
369 core::ubuntu::media::Engine::State::playing,375 core::ubuntu::media::Engine::State::playing,

Subscribers

People subscribed via source and target branches

to all changes: