Merge lp:~phablet-team/media-hub/mpris-take-2 into lp:media-hub/stable

Proposed by Alfonso Sanchez-Beato
Status: Merged
Approved by: Jim Hodapp
Approved revision: 186
Merged at revision: 159
Proposed branch: lp:~phablet-team/media-hub/mpris-take-2
Merge into: lp:media-hub/stable
Diff against target: 907 lines (+348/-125)
16 files modified
CMakeLists.txt (+1/-1)
README (+9/-0)
debian/changelog (+10/-0)
include/core/media/service.h (+3/-0)
src/core/media/mpris/player.h (+24/-4)
src/core/media/mpris/service.h (+1/-0)
src/core/media/player_implementation.cpp (+47/-5)
src/core/media/service_implementation.cpp (+7/-0)
src/core/media/service_implementation.h (+1/-0)
src/core/media/service_skeleton.cpp (+194/-99)
src/core/media/service_skeleton.h (+1/-0)
src/core/media/service_stub.cpp (+9/-1)
src/core/media/service_stub.h (+1/-0)
src/core/media/track_list_implementation.cpp (+11/-5)
src/core/media/track_list_skeleton.cpp (+27/-7)
src/core/media/track_list_skeleton.h (+2/-3)
To merge this branch: bzr merge lp:~phablet-team/media-hub/mpris-take-2
Reviewer Review Type Date Requested Status
Jim Hodapp (community) Approve
Manuel de la Peña (community) Approve
Simon Fels Approve
Review via email: mp+271942@code.launchpad.net

Commit message

Re-enable MPRIS player controls and improve background playlists

Description of the change

Re-enable MPRIS player controls and improve background playlists

To post a comment you must log in.
Revision history for this message
Simon Fels (morphis) :
Revision history for this message
Alfonso Sanchez-Beato (alfonsosanchezbeato) wrote :

Bugs created to track the "TODO" comments:

https://bugs.launchpad.net/ubuntu/+source/media-hub/+bug/1498962
https://bugs.launchpad.net/ubuntu/+source/media-hub/+bug/1498961

Re: the trace in reset_current_player() I prefer to keep it so it is symmetrical with the one in set_current_player(). These traces are not printed very frequently and are quite useful for debugging as they happen in kind of critical moments.

Revision history for this message
Simon Fels (morphis) :
review: Approve
Revision history for this message
Manuel de la Peña (mandel) :
review: Approve
183. By Alfonso Sanchez-Beato

[ Simon Fels ]
* Prevent us from leaking file descriptors and memory due to not releasing
  gstreamer elements correctly.
[ CI Train Bot ]
* No-change rebuild.

184. By Alfonso Sanchez-Beato

Fix changelog

Revision history for this message
Jim Hodapp (jhodapp) wrote :

A few comments inline below.

review: Needs Fixing (code)
Revision history for this message
Jim Hodapp (jhodapp) wrote :

One more change below

review: Needs Fixing
185. By Alfonso Sanchez-Beato

Addess review comments

186. By Alfonso Sanchez-Beato

Update CanPlay and CanPause depending on whether there are items on the
playlist or not.

Revision history for this message
Jim Hodapp (jhodapp) wrote :

Looks good

review: Approve
187. By Jim Hodapp

Merged with stable

188. By Jim Hodapp

Make sure that other sessions pause when the MPRIS play method is called.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CMakeLists.txt'
2--- CMakeLists.txt 2015-04-10 16:21:16 +0000
3+++ CMakeLists.txt 2015-10-14 20:54:59 +0000
4@@ -3,7 +3,7 @@
5 project(ubuntu-media-hub)
6
7 set(UBUNTU_MEDIA_HUB_VERSION_MAJOR 3)
8-set(UBUNTU_MEDIA_HUB_VERSION_MINOR 1)
9+set(UBUNTU_MEDIA_HUB_VERSION_MINOR 2)
10 set(UBUNTU_MEDIA_HUB_VERSION_PATCH 0)
11
12 set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -fPIC -pthread")
13
14=== modified file 'README'
15--- README 2014-08-08 14:36:29 +0000
16+++ README 2015-10-14 20:54:59 +0000
17@@ -1,3 +1,12 @@
18+Coding Convention:
19+------------------
20+
21+NOTE: the media-hub code did not start out with the following coding convention, but it is being introduced to try and
22+converge on a standard from this point forward:
23+
24+https://google-styleguide.googlecode.com/svn/trunk/cppguide.html
25+
26+
27 To Build:
28 ---------
29
30
31=== modified file 'debian/changelog'
32--- debian/changelog 2015-10-02 14:23:08 +0000
33+++ debian/changelog 2015-10-14 20:54:59 +0000
34@@ -1,3 +1,13 @@
35+media-hub (3.2.0+15.04.20150928-0ubuntu1) UNRELEASED; urgency=medium
36+
37+ * Make sure the correct player is set as the current player controled by
38+ MPRIS.
39+ * Improved the can_go_next() and can_go_previous() logic to always return
40+ true if the loop_status is currently set to loop over the entire
41+ playlist.
42+
43+ -- Jim Hodapp <jim.hodapp@canonical.com> Tue, 08 Sep 2015 17:18:15 -0400
44+
45 media-hub (3.1.0+15.04.20151002-0ubuntu1) vivid; urgency=medium
46
47 * Avoid getting additional references for audio_sink, which were not
48
49=== modified file 'include/core/media/service.h'
50--- include/core/media/service.h 2015-04-14 02:43:56 +0000
51+++ include/core/media/service.h 2015-10-14 20:54:59 +0000
52@@ -60,6 +60,9 @@
53 /** @brief Resumes a fixed-name session directly by player key. */
54 virtual std::shared_ptr<Player> resume_session(Player::PlayerKey) = 0;
55
56+ /** @brief Sets the current player that the MPRIS interface will control */
57+ virtual void set_current_player(Player::PlayerKey) = 0;
58+
59 /** @brief Pauses sessions other than the supplied one. */
60 virtual void pause_other_sessions(Player::PlayerKey) = 0;
61
62
63=== modified file 'src/core/media/mpris/player.h'
64--- src/core/media/mpris/player.h 2015-04-06 20:59:14 +0000
65+++ src/core/media/mpris/player.h 2015-10-14 20:54:59 +0000
66@@ -192,12 +192,12 @@
67 // Default values for properties
68 struct Defaults
69 {
70- Properties::CanPlay::ValueType can_play{true};
71- Properties::CanPause::ValueType can_pause{true};
72+ Properties::CanPlay::ValueType can_play{false};
73+ Properties::CanPause::ValueType can_pause{false};
74 Properties::CanSeek::ValueType can_seek{true};
75 Properties::CanControl::ValueType can_control{true};
76- Properties::CanGoNext::ValueType can_go_next{true};
77- Properties::CanGoPrevious::ValueType can_go_previous{true};
78+ Properties::CanGoNext::ValueType can_go_next{false};
79+ Properties::CanGoPrevious::ValueType can_go_previous{false};
80 Properties::IsVideoSource::ValueType is_video_source{false};
81 Properties::IsAudioSource::ValueType is_audio_source{true};
82 Properties::PlaybackStatus::ValueType playback_status{PlaybackStatus::stopped};
83@@ -307,6 +307,26 @@
84 {
85 on_property_value_changed<Properties::Shuffle>(shuffle);
86 });
87+
88+ properties.can_play->changed().connect([this](bool can_play)
89+ {
90+ on_property_value_changed<Properties::CanPlay>(can_play);
91+ });
92+
93+ properties.can_pause->changed().connect([this](bool can_pause)
94+ {
95+ on_property_value_changed<Properties::CanPause>(can_pause);
96+ });
97+
98+ properties.can_go_next->changed().connect([this](bool can_go_next)
99+ {
100+ on_property_value_changed<Properties::CanGoNext>(can_go_next);
101+ });
102+
103+ properties.can_go_previous->changed().connect([this](bool can_go_previous)
104+ {
105+ on_property_value_changed<Properties::CanGoPrevious>(can_go_previous);
106+ });
107 }
108
109 template<typename Property>
110
111=== modified file 'src/core/media/mpris/service.h'
112--- src/core/media/mpris/service.h 2015-04-10 16:13:55 +0000
113+++ src/core/media/mpris/service.h 2015-10-14 20:54:59 +0000
114@@ -115,6 +115,7 @@
115 DBUS_CPP_METHOD_WITH_TIMEOUT_DEF(DestroySession, Service, 1000)
116 DBUS_CPP_METHOD_WITH_TIMEOUT_DEF(CreateFixedSession, Service, 1000)
117 DBUS_CPP_METHOD_WITH_TIMEOUT_DEF(ResumeSession, Service, 1000)
118+ DBUS_CPP_METHOD_WITH_TIMEOUT_DEF(SetCurrentPlayer, Service, 1000)
119 DBUS_CPP_METHOD_WITH_TIMEOUT_DEF(PauseOtherSessions, Service, 1000)
120 };
121 }
122
123=== modified file 'src/core/media/player_implementation.cpp'
124--- src/core/media/player_implementation.cpp 2015-07-27 22:15:33 +0000
125+++ src/core/media/player_implementation.cpp 2015-10-14 20:54:59 +0000
126@@ -301,6 +301,27 @@
127 }
128 }
129
130+ void update_mpris_properties(void)
131+ {
132+ bool has_previous = track_list->has_previous()
133+ or parent->Parent::loop_status() != Player::LoopStatus::none;
134+ bool has_next = track_list->has_next()
135+ or parent->Parent::loop_status() != Player::LoopStatus::none;
136+ auto n_tracks = track_list->tracks()->size();
137+ bool has_tracks = (n_tracks > 0) ? true : false;
138+
139+ std::cout << "Updating MPRIS TrackList properties"
140+ << "; Tracks: " << n_tracks
141+ << ", has_previous: " << has_previous
142+ << ", has_next: " << has_next << std::endl;
143+
144+ // Update properties
145+ parent->can_play().set(has_tracks);
146+ parent->can_pause().set(has_tracks);
147+ parent->can_go_previous().set(has_previous);
148+ parent->can_go_next().set(has_next);
149+ }
150+
151 // Our link back to our parent.
152 media::PlayerImplementation<Parent>* parent;
153 // We just store the parameters passed on construction.
154@@ -327,8 +348,8 @@
155 d{std::make_shared<Private>(this, config)}
156 {
157 // Initialize default values for Player interface properties
158- Parent::can_play().set(true);
159- Parent::can_pause().set(true);
160+ Parent::can_play().set(false);
161+ Parent::can_pause().set(false);
162 Parent::can_seek().set(true);
163 Parent::can_go_previous().set(false);
164 Parent::can_go_next().set(false);
165@@ -376,13 +397,15 @@
166
167 std::function<bool()> can_go_next_getter = [this]()
168 {
169- return d->track_list->has_next();
170+ // If LoopStatus == playlist, then there is always a next track
171+ return d->track_list->has_next() or Parent::loop_status() != Player::LoopStatus::none;
172 };
173 Parent::can_go_next().install(can_go_next_getter);
174
175 std::function<bool()> can_go_previous_getter = [this]()
176 {
177- return d->track_list->has_previous();
178+ // If LoopStatus == playlist, then there is always a next previous
179+ return d->track_list->has_previous() or Parent::loop_status() != Player::LoopStatus::none;
180 };
181 Parent::can_go_previous().install(can_go_previous_getter);
182
183@@ -520,7 +543,26 @@
184 std::cout << "** Track was added, handling in PlayerImplementation" << std::endl;
185 if (d->track_list->tracks()->size() == 1)
186 d->open_first_track_from_tracklist(id);
187- });
188+
189+ d->update_mpris_properties();
190+ });
191+
192+ d->track_list->on_track_removed().connect([this](const media::Track::Id&)
193+ {
194+ d->update_mpris_properties();
195+ });
196+
197+ d->track_list->on_track_changed().connect([this](const media::Track::Id&)
198+ {
199+ d->update_mpris_properties();
200+ });
201+
202+ d->track_list->on_track_list_replaced().connect(
203+ [this](const media::TrackList::ContainerTrackIdTuple&)
204+ {
205+ d->update_mpris_properties();
206+ }
207+ );
208
209 // Everything is setup, we now subscribe to death notifications.
210 std::weak_ptr<Private> wp{d};
211
212=== modified file 'src/core/media/service_implementation.cpp'
213--- src/core/media/service_implementation.cpp 2015-04-17 16:44:30 +0000
214+++ src/core/media/service_implementation.cpp 2015-10-14 20:54:59 +0000
215@@ -246,8 +246,15 @@
216 return std::shared_ptr<media::Player>();
217 }
218
219+void media::ServiceImplementation::set_current_player(Player::PlayerKey key)
220+{
221+ // no impl
222+}
223+
224 void media::ServiceImplementation::pause_other_sessions(media::Player::PlayerKey key)
225 {
226+ std::cout << __PRETTY_FUNCTION__ << std::endl;
227+
228 if (not d->configuration.player_store->has_player_for_key(key))
229 {
230 cerr << "Could not find Player by key: " << key << endl;
231
232=== modified file 'src/core/media/service_implementation.h'
233--- src/core/media/service_implementation.h 2015-04-16 15:19:35 +0000
234+++ src/core/media/service_implementation.h 2015-10-14 20:54:59 +0000
235@@ -49,6 +49,7 @@
236 void destroy_session(const std::string&, const Player::Configuration&);
237 std::shared_ptr<Player> create_fixed_session(const std::string& name, const Player::Configuration&);
238 std::shared_ptr<Player> resume_session(Player::PlayerKey key);
239+ void set_current_player(Player::PlayerKey key);
240 void pause_other_sessions(Player::PlayerKey key);
241
242 private:
243
244=== modified file 'src/core/media/service_skeleton.cpp'
245--- src/core/media/service_skeleton.cpp 2015-08-11 16:10:14 +0000
246+++ src/core/media/service_skeleton.cpp 2015-10-14 20:54:59 +0000
247@@ -59,7 +59,7 @@
248 object(impl->access_service()->add_object_for_path(
249 dbus::traits::Service<media::Service>::object_path())),
250 configuration(config),
251- exported(impl->access_bus(), config.cover_art_resolver)
252+ exported(impl->access_bus(), config.cover_art_resolver, impl)
253 {
254 object->install_method_handler<mpris::Service::CreateSession>(
255 std::bind(
256@@ -91,6 +91,11 @@
257 &Private::handle_resume_session,
258 this,
259 std::placeholders::_1));
260+ object->install_method_handler<mpris::Service::SetCurrentPlayer>(
261+ std::bind(
262+ &Private::handle_set_current_player,
263+ this,
264+ std::placeholders::_1));
265 object->install_method_handler<mpris::Service::PauseOtherSessions>(
266 std::bind(
267 &Private::handle_pause_other_sessions,
268@@ -102,7 +107,7 @@
269 {
270 static unsigned int session_counter = 0;
271
272- unsigned int current_session = session_counter++;
273+ const unsigned int current_session = session_counter++;
274 boost::uuids::uuid uuid = gen();
275
276 std::stringstream ss;
277@@ -134,7 +139,8 @@
278 configuration.player_store->add_player_for_key(key, impl->create_session(config));
279 uuid_player_map.insert(std::make_pair(uuid, key));
280
281- request_context_resolver->resolve_context_for_dbus_name_async(msg->sender(), [this, key, msg](const media::apparmor::ubuntu::Context& context)
282+ request_context_resolver->resolve_context_for_dbus_name_async(msg->sender(),
283+ [this, key, msg](const media::apparmor::ubuntu::Context& context)
284 {
285 fprintf(stderr, "%s():%d -- app_name='%s', attached\n", __func__, __LINE__, context.str().c_str());
286 player_owner_map.insert(std::make_pair(key, std::make_tuple(context.str(), true, msg->sender())));
287@@ -213,7 +219,8 @@
288 ss << "/core/ubuntu/media/Service/sessions/" << key;
289 dbus::types::ObjectPath op{ss.str()};
290
291- request_context_resolver->resolve_context_for_dbus_name_async(msg->sender(), [this, msg, key, op](const media::apparmor::ubuntu::Context& context)
292+ request_context_resolver->resolve_context_for_dbus_name_async(msg->sender(),
293+ [this, msg, key, op](const media::apparmor::ubuntu::Context& context)
294 {
295 auto info = player_owner_map.at(key);
296 fprintf(stderr, "%s():%d -- reattach app_name='%s', info='%s', '%s'\n", __func__, __LINE__, context.str().c_str(), std::get<0>(info).c_str(), std::get<2>(info).c_str());
297@@ -224,6 +231,12 @@
298 // Signal player reconnection
299 auto player = configuration.player_store->player_for_key(key);
300 player->reconnect();
301+ // We only care to allow the MPRIS controls to apply to multimedia player (i.e. audio, video)
302+ if (player->audio_stream_role() == media::Player::AudioStreamRole::multimedia)
303+ {
304+ std::cout << "Setting current_player" << std::endl;
305+ exported.set_current_player(player);
306+ }
307
308 auto reply = dbus::Message::make_method_return(msg);
309 reply->writer() << op;
310@@ -280,7 +293,8 @@
311 // the session is no longer usable.
312 uuid_player_map.erase(uuid);
313
314- request_context_resolver->resolve_context_for_dbus_name_async(msg->sender(), [this, msg, key](const media::apparmor::ubuntu::Context& context)
315+ request_context_resolver->resolve_context_for_dbus_name_async(msg->sender(),
316+ [this, msg, key](const media::apparmor::ubuntu::Context& context)
317 {
318 auto info = player_owner_map.at(key);
319 fprintf(stderr, "%s():%d -- Destroying app_name='%s', info='%s', '%s'\n", __func__, __LINE__, context.str().c_str(), std::get<0>(info).c_str(), std::get<2>(info).c_str());
320@@ -424,9 +438,18 @@
321 }
322 }
323
324+ void handle_set_current_player(const core::dbus::Message::Ptr& msg)
325+ {
326+ Player::PlayerKey key;
327+ msg->reader() >> key;
328+ impl->set_current_player(key);
329+
330+ auto reply = dbus::Message::make_method_return(msg);
331+ impl->access_bus()->send(reply);
332+ }
333+
334 void handle_pause_other_sessions(const core::dbus::Message::Ptr& msg)
335 {
336- std::cout << __PRETTY_FUNCTION__ << std::endl;
337 Player::PlayerKey key;
338 msg->reader() >> key;
339 impl->pause_other_sessions(key);
340@@ -469,45 +492,33 @@
341 {
342 mpris::Player::Skeleton::Configuration::Defaults defaults;
343
344- // Disabled as track list is not fully implemented yet.
345- defaults.can_go_next = false;
346- // Disabled as track list is not fully implemented yet.
347- defaults.can_go_previous = false;
348-
349 return defaults;
350 }
351
352- static std::string service_name()
353- {
354- static const bool export_to_indicator_sound_via_mpris
355- {
356- core::posix::this_process::env::get("UBUNTU_MEDIA_HUB_EXPORT_TO_INDICATOR_VIA_MPRIS", "0") == "1"
357- };
358-
359- return export_to_indicator_sound_via_mpris ? "org.mpris.MediaPlayer2.MediaHub" :
360- "hidden.org.mpris.MediaPlayer2.MediaHub";
361- }
362-
363- explicit Exported(const dbus::Bus::Ptr& bus, const media::CoverArtResolver& cover_art_resolver)
364+ explicit Exported(const dbus::Bus::Ptr& bus, const media::CoverArtResolver& cover_art_resolver,
365+ media::ServiceSkeleton* impl)
366 : bus{bus},
367- service{dbus::Service::add_service(bus, service_name())},
368+ /* Export MediaHub service interface on dbus */
369+ service{dbus::Service::add_service(bus, "org.mpris.MediaPlayer2.MediaHub")},
370 object{service->add_object_for_path(dbus::types::ObjectPath{"/org/mpris/MediaPlayer2"})},
371 media_player{mpris::MediaPlayer2::Skeleton::Configuration{bus, object, media_player_defaults()}},
372 player{mpris::Player::Skeleton::Configuration{bus, object, player_defaults()}},
373 playlists{mpris::Playlists::Skeleton::Configuration{bus, object, mpris::Playlists::Skeleton::Configuration::Defaults{}}},
374- cover_art_resolver{cover_art_resolver}
375+ cover_art_resolver{cover_art_resolver},
376+ impl{impl}
377 {
378 object->install_method_handler<core::dbus::interfaces::Properties::GetAll>([this](const core::dbus::Message::Ptr& msg)
379 {
380 // Extract the interface
381- std::string itf; msg->reader() >> itf;
382+ std::string interface;
383+ msg->reader() >> interface;
384 core::dbus::Message::Ptr reply = core::dbus::Message::make_method_return(msg);
385
386- if (itf == mpris::Player::name())
387+ if (interface == mpris::Player::name())
388 reply->writer() << player.get_all_properties();
389- else if (itf == mpris::MediaPlayer2::name())
390+ else if (interface == mpris::MediaPlayer2::name())
391 reply->writer() << media_player.get_all_properties();
392- else if (itf == mpris::Playlists::name())
393+ else if (interface == mpris::Playlists::name())
394 reply->writer() << playlists.get_all_properties();
395
396 Exported::bus->send(reply);
397@@ -516,9 +527,9 @@
398 // Setup method handlers for mpris::Player methods.
399 auto next = [this](const core::dbus::Message::Ptr& msg)
400 {
401- auto sp = current_player.lock();
402+ const auto sp = current_player.lock();
403
404- if (sp)
405+ if (is_multimedia_role())
406 sp->next();
407
408 Exported::bus->send(core::dbus::Message::make_method_return(msg));
409@@ -527,9 +538,9 @@
410
411 auto previous = [this](const core::dbus::Message::Ptr& msg)
412 {
413- auto sp = current_player.lock();
414+ const auto sp = current_player.lock();
415
416- if (sp)
417+ if (is_multimedia_role())
418 sp->previous();
419
420 Exported::bus->send(core::dbus::Message::make_method_return(msg));
421@@ -538,9 +549,9 @@
422
423 auto pause = [this](const core::dbus::Message::Ptr& msg)
424 {
425- auto sp = current_player.lock();
426+ const auto sp = current_player.lock();
427
428- if (sp)
429+ if (is_multimedia_role() and sp->can_pause())
430 sp->pause();
431
432 Exported::bus->send(core::dbus::Message::make_method_return(msg));
433@@ -549,36 +560,52 @@
434
435 auto stop = [this](const core::dbus::Message::Ptr& msg)
436 {
437- auto sp = current_player.lock();
438+ const auto sp = current_player.lock();
439
440- if (sp)
441+ if (is_multimedia_role())
442 sp->stop();
443
444 Exported::bus->send(core::dbus::Message::make_method_return(msg));
445 };
446 object->install_method_handler<mpris::Player::Stop>(stop);
447
448- auto play = [this](const core::dbus::Message::Ptr& msg)
449+ auto play = [this, impl](const core::dbus::Message::Ptr& msg)
450 {
451- auto sp = current_player.lock();
452-
453- if (sp)
454+ const auto sp = current_player.lock();
455+
456+ if (is_multimedia_role() and sp->can_play())
457+ {
458+ // Make sure other player sessions that are already playing
459+ // are paused before triggering new player (sp) to play
460+ if (impl)
461+ impl->pause_other_sessions(sp->key());
462+
463 sp->play();
464+ }
465
466 Exported::bus->send(core::dbus::Message::make_method_return(msg));
467 };
468 object->install_method_handler<mpris::Player::Play>(play);
469
470- auto play_pause = [this](const core::dbus::Message::Ptr& msg)
471+ auto play_pause = [this, impl](const core::dbus::Message::Ptr& msg)
472 {
473- auto sp = current_player.lock();
474+ const auto sp = current_player.lock();
475
476- if (sp)
477+ if (is_multimedia_role())
478 {
479- if (sp->playback_status() == media::Player::PlaybackStatus::playing)
480+ if (sp->playback_status() == media::Player::PlaybackStatus::playing
481+ and sp->can_pause())
482 sp->pause();
483- else if (sp->playback_status() != media::Player::PlaybackStatus::null)
484+ else if (sp->playback_status() != media::Player::PlaybackStatus::null
485+ and sp->can_play())
486+ {
487+ // Make sure other player sessions that are already playing
488+ // are paused before triggering new player (sp) to play
489+ if (impl)
490+ impl->pause_other_sessions(sp->key());
491+
492 sp->play();
493+ }
494 }
495
496 Exported::bus->send(core::dbus::Message::make_method_return(msg));
497@@ -586,15 +613,21 @@
498 object->install_method_handler<mpris::Player::PlayPause>(play_pause);
499 }
500
501+ inline bool is_multimedia_role()
502+ {
503+ const auto sp = current_player.lock();
504+
505+ return (sp ? sp->audio_stream_role() == media::Player::AudioStreamRole::multimedia : false);
506+ }
507+
508 void set_current_player(const std::shared_ptr<media::Player>& cp)
509 {
510- unset_current_player();
511-
512+ std::cout << "*** " << __PRETTY_FUNCTION__ << std::endl;
513 // We will not keep the object alive.
514 current_player = cp;
515
516 // And announce that we can be controlled again.
517- player.properties.can_control->set(false);
518+ player.properties.can_control->set(true);
519
520 // We wire up player state changes
521 connections.seeked_to = cp->seeked_to().connect([this](std::uint64_t position)
522@@ -612,68 +645,103 @@
523 player.properties.position->set(position);
524 });
525
526- connections.playback_status_changed = cp->playback_status().changed().connect([this](core::ubuntu::media::Player::PlaybackStatus status)
527+ connections.playback_status_changed = cp->playback_status().changed().connect(
528+ [this](core::ubuntu::media::Player::PlaybackStatus status)
529 {
530 player.properties.playback_status->set(mpris::Player::PlaybackStatus::from(status));
531 });
532
533- connections.loop_status_changed = cp->loop_status().changed().connect([this](core::ubuntu::media::Player::LoopStatus status)
534+ connections.loop_status_changed = cp->loop_status().changed().connect(
535+ [this](core::ubuntu::media::Player::LoopStatus status)
536 {
537 player.properties.loop_status->set(mpris::Player::LoopStatus::from(status));
538 });
539
540- connections.meta_data_changed = cp->meta_data_for_current_track().changed().connect([this](const core::ubuntu::media::Track::MetaData& md)
541- {
542- mpris::Player::Dictionary dict;
543-
544- bool has_title = md.count(xesam::Title::name) > 0;
545- bool has_album_name = md.count(xesam::Album::name) > 0;
546- bool has_artist_name = md.count(xesam::Artist::name) > 0;
547-
548- if (has_title)
549- dict[xesam::Title::name] = dbus::types::Variant::encode(md.get(xesam::Title::name));
550- if (has_album_name)
551- dict[xesam::Album::name] = dbus::types::Variant::encode(md.get(xesam::Album::name));
552- if (has_artist_name)
553- dict[xesam::Artist::name] = dbus::types::Variant::encode(md.get(xesam::Artist::name));
554-
555- dict[mpris::metadata::ArtUrl::name] = dbus::types::Variant::encode(
556- cover_art_resolver(
557- has_title ? md.get(xesam::Title::name) : "",
558- has_album_name ? md.get(xesam::Album::name) : "",
559- has_artist_name ? md.get(xesam::Artist::name) : ""));
560-
561- mpris::Player::Dictionary wrap;
562- wrap[mpris::Player::Properties::Metadata::name()] = dbus::types::Variant::encode(dict);
563-
564- player.signals.properties_changed->emit(
565- std::make_tuple(
566- dbus::traits::Service<mpris::Player::Properties::Metadata::Interface>::interface_name(),
567- wrap,
568- std::vector<std::string>()));
569- });
570+ connections.can_play_changed = cp->can_play().changed().connect(
571+ [this](bool can_play)
572+ {
573+ player.properties.can_play->set(can_play);
574+ });
575+
576+ connections.can_pause_changed = cp->can_pause().changed().connect(
577+ [this](bool can_pause)
578+ {
579+ player.properties.can_pause->set(can_pause);
580+ });
581+
582+ connections.can_go_previous_changed = cp->can_go_previous().changed().connect(
583+ [this](bool can_go_previous)
584+ {
585+ player.properties.can_go_previous->set(can_go_previous);
586+ });
587+
588+ connections.can_go_next_changed = cp->can_go_next().changed().connect(
589+ [this](bool can_go_next)
590+ {
591+ player.properties.can_go_next->set(can_go_next);
592+ });
593+
594+ // Sync property values between session and player mpris::Player instances
595+ // TODO Getters from media::Player actually return values from a
596+ // mpris::Player::Skeleton instance different from "player". Each of them use
597+ // different DBus object paths, /core/ubuntu/media/Service/sessions/<n>
598+ // and /org/mpris/MediaPlayer2 (this is the one enforced by the MPRIS spec).
599+ // Discuss why this is needed with tvoss.
600+ player.properties.duration->set(cp->duration().get());
601+ player.properties.position->set(cp->position().get());
602+ player.properties.playback_status->set(mpris::Player::PlaybackStatus::from(
603+ cp->playback_status().get()));
604+ player.properties.loop_status->set(mpris::Player::LoopStatus::from(
605+ cp->loop_status().get()));
606+ player.properties.can_play->set(cp->can_play().get());
607+ player.properties.can_pause->set(cp->can_pause().get());
608+ player.properties.can_go_previous->set(cp->can_go_previous().get());
609+ player.properties.can_go_next->set(cp->can_go_next().get());
610+
611+#if 0
612+ // TODO cover_art_resolver() is not implemented yet
613+ connections.meta_data_changed = cp->meta_data_for_current_track().changed().connect(
614+ [this](const core::ubuntu::media::Track::MetaData& md)
615+ {
616+ mpris::Player::Dictionary dict;
617+
618+ bool has_title = md.count(xesam::Title::name) > 0;
619+ bool has_album_name = md.count(xesam::Album::name) > 0;
620+ bool has_artist_name = md.count(xesam::Artist::name) > 0;
621+
622+ if (has_title)
623+ dict[xesam::Title::name] = dbus::types::Variant::encode(md.get(xesam::Title::name));
624+ if (has_album_name)
625+ dict[xesam::Album::name] = dbus::types::Variant::encode(md.get(xesam::Album::name));
626+ if (has_artist_name)
627+ dict[xesam::Artist::name] = dbus::types::Variant::encode(md.get(xesam::Artist::name));
628+
629+ dict[mpris::metadata::ArtUrl::name] = dbus::types::Variant::encode(
630+ cover_art_resolver(
631+ has_title ? md.get(xesam::Title::name) : "",
632+ has_album_name ? md.get(xesam::Album::name) : "",
633+ has_artist_name ? md.get(xesam::Artist::name) : ""));
634+
635+ mpris::Player::Dictionary wrap;
636+ wrap[mpris::Player::Properties::Metadata::name()] = dbus::types::Variant::encode(dict);
637+
638+ player.signals.properties_changed->emit(
639+ std::make_tuple(
640+ dbus::traits::Service<
641+ mpris::Player::Properties::Metadata::Interface>
642+ ::interface_name(),
643+ wrap,
644+ std::vector<std::string>()));
645+ });
646+#endif
647 }
648
649- void unset_current_player()
650+ void reset_current_player()
651 {
652+ std::cout << __PRETTY_FUNCTION__ << std::endl;
653+ // And announce that we can no longer be controlled.
654+ player.properties.can_control->set(false);
655 current_player.reset();
656-
657- // We disconnect all previous event connections.
658- connections.seeked_to.disconnect();
659- connections.duration_changed.disconnect();
660- connections.position_changed.disconnect();
661- connections.playback_status_changed.disconnect();
662- connections.loop_status_changed.disconnect();
663- connections.meta_data_changed.disconnect();
664-
665- // And announce that we cannot be controlled anymore.
666- player.properties.can_control->set(false);
667- }
668-
669- void unset_if_current(const std::shared_ptr<media::Player>& cp)
670- {
671- if (cp == current_player.lock())
672- unset_current_player();
673 }
674
675 dbus::Bus::Ptr bus;
676@@ -688,6 +756,9 @@
677 media::CoverArtResolver cover_art_resolver;
678 // The actual player instance.
679 std::weak_ptr<media::Player> current_player;
680+
681+ media::ServiceSkeleton* impl;
682+
683 // We track event connections.
684 struct
685 {
686@@ -711,6 +782,22 @@
687 {
688 the_empty_signal.connect([](){})
689 };
690+ core::Connection can_play_changed
691+ {
692+ the_empty_signal.connect([](){})
693+ };
694+ core::Connection can_pause_changed
695+ {
696+ the_empty_signal.connect([](){})
697+ };
698+ core::Connection can_go_previous_changed
699+ {
700+ the_empty_signal.connect([](){})
701+ };
702+ core::Connection can_go_next_changed
703+ {
704+ the_empty_signal.connect([](){})
705+ };
706 core::Connection meta_data_changed
707 {
708 the_empty_signal.connect([](){})
709@@ -759,6 +846,14 @@
710 return d->configuration.impl->resume_session(key);
711 }
712
713+void media::ServiceSkeleton::set_current_player(media::Player::PlayerKey key)
714+{
715+ const auto player = d->configuration.player_store->player_for_key(key);
716+ // We only care to allow the MPRIS controls to apply to multimedia player (i.e. audio, video)
717+ if (player->audio_stream_role() == media::Player::AudioStreamRole::multimedia)
718+ d->exported.set_current_player(player);
719+}
720+
721 void media::ServiceSkeleton::pause_other_sessions(media::Player::PlayerKey key)
722 {
723 d->configuration.impl->pause_other_sessions(key);
724
725=== modified file 'src/core/media/service_skeleton.h'
726--- src/core/media/service_skeleton.h 2015-04-16 15:19:35 +0000
727+++ src/core/media/service_skeleton.h 2015-10-14 20:54:59 +0000
728@@ -59,6 +59,7 @@
729 void destroy_session(const std::string&, const media::Player::Configuration&);
730 std::shared_ptr<Player> create_fixed_session(const std::string& name, const Player::Configuration&);
731 std::shared_ptr<Player> resume_session(Player::PlayerKey);
732+ void set_current_player(Player::PlayerKey key);
733 void pause_other_sessions(Player::PlayerKey key);
734
735 void run();
736
737=== modified file 'src/core/media/service_stub.cpp'
738--- src/core/media/service_stub.cpp 2015-04-17 16:44:30 +0000
739+++ src/core/media/service_stub.cpp 2015-10-14 20:54:59 +0000
740@@ -139,9 +139,17 @@
741 });
742 }
743
744+void media::ServiceStub::set_current_player(Player::PlayerKey key)
745+{
746+ auto op = d->object->invoke_method_synchronously<mpris::Service::SetCurrentPlayer,
747+ void>(key);
748+
749+ if (op.is_error())
750+ throw std::runtime_error("Problem setting current player: " + op.error());
751+}
752+
753 void media::ServiceStub::pause_other_sessions(media::Player::PlayerKey key)
754 {
755- std::cout << __PRETTY_FUNCTION__ << std::endl;
756 auto op = d->object->invoke_method_synchronously<mpris::Service::PauseOtherSessions,
757 void>(key);
758
759
760=== modified file 'src/core/media/service_stub.h'
761--- src/core/media/service_stub.h 2015-04-10 16:13:55 +0000
762+++ src/core/media/service_stub.h 2015-10-14 20:54:59 +0000
763@@ -45,6 +45,7 @@
764 void destroy_session(const std::string& uuid, const Player::Configuration&);
765 std::shared_ptr<Player> create_fixed_session(const std::string& name, const Player::Configuration&);
766 std::shared_ptr<Player> resume_session(Player::PlayerKey key);
767+ void set_current_player(Player::PlayerKey key);
768 void pause_other_sessions(Player::PlayerKey key);
769
770 private:
771
772=== modified file 'src/core/media/track_list_implementation.cpp'
773--- src/core/media/track_list_implementation.cpp 2015-08-11 15:25:50 +0000
774+++ src/core/media/track_list_implementation.cpp 2015-10-14 20:54:59 +0000
775@@ -215,20 +215,26 @@
776
777 void media::TrackListImplementation::reset()
778 {
779- std::cout << __PRETTY_FUNCTION__ << std::endl;
780-
781 // Make sure playback stops
782 on_end_of_tracklist()();
783+ // And make sure there is no "current" track
784+ media::TrackListSkeleton::reset();
785
786 auto result = tracks().update([this](TrackList::Container& container)
787 {
788- container.clear();
789+ TrackList::Container ids = container;
790+
791+ for (auto it = container.begin(); it != container.end(); ) {
792+ auto id = *it;
793+ it = container.erase(it);
794+ on_track_removed()(id);
795+ }
796+
797 container.resize(0);
798 d->track_counter = 0;
799+
800 return true;
801 });
802
803- media::TrackListSkeleton::reset();
804-
805 (void) result;
806 }
807
808=== modified file 'src/core/media/track_list_skeleton.cpp'
809--- src/core/media/track_list_skeleton.cpp 2015-08-11 15:25:50 +0000
810+++ src/core/media/track_list_skeleton.cpp 2015-10-14 20:54:59 +0000
811@@ -241,16 +241,31 @@
812
813 bool media::TrackListSkeleton::has_next()
814 {
815- if (tracks().get().empty())
816+ const auto n_tracks = tracks().get().size();
817+
818+ if (n_tracks == 0)
819 return false;
820
821+ // TODO Using current_iterator() makes media-hub crash later. Logic for
822+ // handling the iterators must be reviewed. As a minimum updates to the
823+ // track list should update current_track instead of the list being sneakly
824+ // changed in player_implementation.cpp.
825+ // To avoid the crash we consider that current_track will be eventually
826+ // initialized to the first track when current_iterator() gets called.
827+ if (d->current_track == d->empty_iterator) {
828+ if (n_tracks < 2)
829+ return false;
830+ else
831+ return true;
832+ }
833+
834 const auto next_track = std::next(current_iterator());
835 return !is_last_track(next_track);
836 }
837
838 bool media::TrackListSkeleton::has_previous()
839 {
840- if (tracks().get().empty())
841+ if (tracks().get().empty() || d->current_track == d->empty_iterator)
842 return false;
843
844 // If we are looping over the entire list, then there is always a previous track
845@@ -273,8 +288,11 @@
846 media::Track::Id media::TrackListSkeleton::next()
847 {
848 std::cout << __PRETTY_FUNCTION__ << std::endl;
849- if (tracks().get().empty())
850- return *(d->empty_iterator);
851+ if (tracks().get().empty()) {
852+ // TODO Change ServiceSkeleton to return with error from DBus call
853+ std::cerr << "ERROR: no tracks, cannot go to next" << std::endl;
854+ return media::Track::Id{};
855+ }
856
857 const auto next_track = std::next(current_iterator());
858 bool do_go_to_next_track = false;
859@@ -328,8 +346,11 @@
860 media::Track::Id media::TrackListSkeleton::previous()
861 {
862 std::cout << __PRETTY_FUNCTION__ << std::endl;
863- if (tracks().get().empty())
864- return *(d->empty_iterator);
865+ if (tracks().get().empty()) {
866+ // TODO Change ServiceSkeleton to return with error from DBus call
867+ std::cerr << "ERROR: no tracks, cannot go to previous" << std::endl;
868+ return media::Track::Id{};
869+ }
870
871 bool do_go_to_previous_track = false;
872
873@@ -521,7 +542,6 @@
874
875 void media::TrackListSkeleton::reset()
876 {
877- std::cout << __PRETTY_FUNCTION__ << std::endl;
878 d->current_track = d->empty_iterator;
879 }
880
881
882=== modified file 'src/core/media/track_list_skeleton.h'
883--- src/core/media/track_list_skeleton.h 2015-07-27 22:15:33 +0000
884+++ src/core/media/track_list_skeleton.h 2015-10-14 20:54:59 +0000
885@@ -51,10 +51,12 @@
886 const core::Property<Container>& tracks() const;
887
888 const core::Signal<ContainerTrackIdTuple>& on_track_list_replaced() const;
889+ core::Signal<ContainerTrackIdTuple>& on_track_list_replaced();
890 const core::Signal<Track::Id>& on_track_added() const;
891 core::Signal<Track::Id>& on_track_added();
892 const core::Signal<Track::Id>& on_track_removed() const;
893 const core::Signal<Track::Id>& on_track_changed() const;
894+ core::Signal<Track::Id>& on_track_changed();
895 const core::Signal<std::pair<Track::Id, bool>>& on_go_to_track() const;
896 core::Signal<std::pair<Track::Id, bool>>& on_go_to_track();
897 const core::Signal<void>& on_end_of_tracklist() const;
898@@ -77,9 +79,6 @@
899
900 core::Property<bool>& can_edit_tracks();
901
902- core::Signal<ContainerTrackIdTuple>& on_track_list_replaced();
903- core::Signal<Track::Id>& on_track_changed();
904-
905 void reset();
906
907 private:

Subscribers

People subscribed via source and target branches

to all changes: