Merge lp:~phablet-team/media-hub/wily-sync into lp:media-hub

Proposed by Alfonso Sanchez-Beato
Status: Merged
Approved by: Jim Hodapp
Approved revision: 158
Merged at revision: 158
Proposed branch: lp:~phablet-team/media-hub/wily-sync
Merge into: lp:media-hub
Diff against target: 1082 lines (+428/-137)
19 files modified
CMakeLists.txt (+1/-1)
README (+9/-0)
debian/changelog (+22/-0)
include/core/media/service.h (+3/-0)
src/core/media/gstreamer/meta_data_extractor.h (+37/-5)
src/core/media/gstreamer/playbin.cpp (+30/-7)
src/core/media/gstreamer/playbin.h (+1/-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/wily-sync
Reviewer Review Type Date Requested Status
Jim Hodapp (community) code Approve
Review via email: mp+274676@code.launchpad.net

Description of the change

[ Simon Fels ]
  * Prevent us from leaking file descriptors and memory due to not
    releasing gstreamer elements correctly.

  [ Alfonso Sanchez-Beato ]
  * Avoid getting additional references for audio_sink, which were not
    being freed. Also, remove unneeded unrefs that must be performed in
    gstreamer instead.
  * Emit CanPlay, CanPause, and CanGo[Next|Previous] in the mpris object
    path

  [ Jim Hodapp ]
  * Make sure the correct player is set as the current player controled by
    MPRIS.
  * Improved the can_go_next() and can_go_previous() logic to always
    return true if the loop_status is currently set to loop over the
    entire playlist.

To post a comment you must log in.
Revision history for this message
Jim Hodapp (jhodapp) wrote :

LGTM.

review: Approve (code)

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-09-01 08:30:08 +0000
3+++ CMakeLists.txt 2015-10-16 08:36:42 +0000
4@@ -3,7 +3,7 @@
5 project(ubuntu-media-hub)
6
7 set(UBUNTU_MEDIA_HUB_VERSION_MAJOR 4)
8-set(UBUNTU_MEDIA_HUB_VERSION_MINOR 0)
9+set(UBUNTU_MEDIA_HUB_VERSION_MINOR 1)
10 set(UBUNTU_MEDIA_HUB_VERSION_PATCH 0)
11
12 set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -fPIC -pthread")
13
14=== modified file 'README'
15--- README 2014-08-08 14:36:29 +0000
16+++ README 2015-10-16 08:36:42 +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-09-07 10:26:04 +0000
33+++ debian/changelog 2015-10-16 08:36:42 +0000
34@@ -1,3 +1,25 @@
35+media-hub (4.1.0+15.10.20151016-0ubuntu1) UNRELEASED; urgency=medium
36+
37+ [ Simon Fels ]
38+ * Prevent us from leaking file descriptors and memory due to not releasing
39+ gstreamer elements correctly.
40+
41+ [ Alfonso Sanchez-Beato ]
42+ * Avoid getting additional references for audio_sink, which were not
43+ being freed. Also, remove unneeded unrefs that must be performed in
44+ gstreamer instead.
45+ * Emit CanPlay, CanPause, and CanGo[Next|Previous] in the mpris object path
46+
47+ [ Jim Hodapp ]
48+ * Make sure the correct player is set as the current player controled by
49+ MPRIS.
50+ * Improved the can_go_next() and can_go_previous() logic to always return
51+ true if the loop_status is currently set to loop over the entire
52+ playlist.
53+
54+
55+ -- Alfonso Sanchez-Beato (email Canonical) <alfonso.sanchez-beato@canonical.com> Fri, 16 Oct 2015 10:28:38 +0200
56+
57 media-hub (4.0.0+15.10.20150907-0ubuntu1) wily; urgency=medium
58
59 * Gather information about missing codecs and selected streams from
60
61=== modified file 'include/core/media/service.h'
62--- include/core/media/service.h 2015-04-14 02:43:56 +0000
63+++ include/core/media/service.h 2015-10-16 08:36:42 +0000
64@@ -60,6 +60,9 @@
65 /** @brief Resumes a fixed-name session directly by player key. */
66 virtual std::shared_ptr<Player> resume_session(Player::PlayerKey) = 0;
67
68+ /** @brief Sets the current player that the MPRIS interface will control */
69+ virtual void set_current_player(Player::PlayerKey) = 0;
70+
71 /** @brief Pauses sessions other than the supplied one. */
72 virtual void pause_other_sessions(Player::PlayerKey) = 0;
73
74
75=== modified file 'src/core/media/gstreamer/meta_data_extractor.h'
76--- src/core/media/gstreamer/meta_data_extractor.h 2015-04-27 23:01:48 +0000
77+++ src/core/media/gstreamer/meta_data_extractor.h 2015-10-16 08:36:42 +0000
78@@ -147,7 +147,7 @@
79 MetaDataExtractor()
80 : pipe(gst_pipeline_new("meta_data_extractor_pipeline")),
81 decoder(gst_element_factory_make ("uridecodebin", NULL)),
82- bus(GST_ELEMENT_BUS(pipe))
83+ bus(gst_element_get_bus(pipe))
84 {
85 gst_bin_add(GST_BIN(pipe), decoder);
86
87@@ -159,8 +159,40 @@
88
89 ~MetaDataExtractor()
90 {
91- gst_element_set_state(pipe, GST_STATE_NULL);
92- // gst_object_unref(pipe);
93+ set_state_and_wait(GST_STATE_NULL);
94+ gst_object_unref(pipe);
95+ }
96+
97+ bool set_state_and_wait(GstState new_state)
98+ {
99+ static const std::chrono::nanoseconds state_change_timeout
100+ {
101+ // We choose a quite high value here as tests are run under valgrind
102+ // and gstreamer pipeline setup/state changes take longer in that scenario.
103+ // The value does not negatively impact runtime performance.
104+ std::chrono::milliseconds{5000}
105+ };
106+
107+ auto ret = gst_element_set_state(pipe, new_state);
108+
109+ bool result = false; GstState current, pending;
110+ switch(ret)
111+ {
112+ case GST_STATE_CHANGE_FAILURE:
113+ result = false; break;
114+ case GST_STATE_CHANGE_NO_PREROLL:
115+ case GST_STATE_CHANGE_SUCCESS:
116+ result = true; break;
117+ case GST_STATE_CHANGE_ASYNC:
118+ result = GST_STATE_CHANGE_SUCCESS == gst_element_get_state(
119+ pipe,
120+ &current,
121+ &pending,
122+ state_change_timeout.count());
123+ break;
124+ }
125+
126+ return result;
127 }
128
129 core::ubuntu::media::Track::MetaData meta_data_for_track_with_uri(const core::ubuntu::media::Track::UriType& uri)
130@@ -193,11 +225,11 @@
131
132 if (std::future_status::ready != future.wait_for(std::chrono::seconds(4)))
133 {
134- gst_element_set_state(pipe, GST_STATE_NULL);
135+ set_state_and_wait(GST_STATE_NULL);
136 throw std::runtime_error("Problem extracting meta data for track");
137 } else
138 {
139- gst_element_set_state(pipe, GST_STATE_NULL);
140+ set_state_and_wait(GST_STATE_NULL);
141 }
142
143 return future.get();
144
145=== modified file 'src/core/media/gstreamer/playbin.cpp'
146--- src/core/media/gstreamer/playbin.cpp 2015-09-07 10:15:51 +0000
147+++ src/core/media/gstreamer/playbin.cpp 2015-10-16 08:36:42 +0000
148@@ -101,6 +101,7 @@
149 bus{gst_element_get_bus(pipeline)},
150 file_type(MEDIA_FILE_TYPE_NONE),
151 video_sink(nullptr),
152+ audio_sink(nullptr),
153 on_new_message_connection_async(
154 bus.on_new_message_async.connect(
155 std::bind(
156@@ -142,13 +143,39 @@
157 );
158 }
159
160+// Note that we might be accessing freed memory here, so activate DEBUG_REFS
161+// only for debugging
162+//#define DEBUG_REFS
163+#ifdef DEBUG_REFS
164+static void print_refs(const gstreamer::Playbin &pb, const char *func)
165+{
166+ using namespace std;
167+
168+ cout << func << endl;
169+ if (pb.pipeline)
170+ cout << "pipeline: " << GST_OBJECT_REFCOUNT(pb.pipeline) << endl;
171+ if (pb.video_sink)
172+ cout << "video_sink: " << GST_OBJECT_REFCOUNT(pb.video_sink) << endl;
173+ if (pb.audio_sink)
174+ cout << "audio_sink: " << GST_OBJECT_REFCOUNT(pb.audio_sink) << endl;
175+}
176+#endif
177+
178 gstreamer::Playbin::~Playbin()
179 {
180+#ifdef DEBUG_REFS
181+ print_refs(*this, "gstreamer::Playbin::~Playbin pipeline");
182+#endif
183+
184 g_signal_handler_disconnect(pipeline, about_to_finish_handler_id);
185 g_signal_handler_disconnect(pipeline, source_setup_handler_id);
186
187 if (pipeline)
188 gst_object_unref(pipeline);
189+
190+#ifdef DEBUG_REFS
191+ print_refs(*this, "gstreamer::Playbin::~Playbin pipeline");
192+#endif
193 }
194
195 void gstreamer::Playbin::reset()
196@@ -290,7 +317,7 @@
197
198 if (::getenv("CORE_UBUNTU_MEDIA_SERVICE_AUDIO_SINK_NAME") != nullptr)
199 {
200- auto audio_sink = gst_element_factory_make (
201+ audio_sink = gst_element_factory_make (
202 ::getenv("CORE_UBUNTU_MEDIA_SERVICE_AUDIO_SINK_NAME"),
203 "audio-sink");
204
205@@ -374,17 +401,13 @@
206 /** Sets the new audio stream role on the pulsesink in playbin */
207 void gstreamer::Playbin::set_audio_stream_role(media::Player::AudioStreamRole new_audio_role)
208 {
209- GstElement *audio_sink = NULL;
210- g_object_get (pipeline, "audio-sink", &audio_sink, NULL);
211-
212 std::string role_str("props,media.role=" + get_audio_role_str(new_audio_role));
213 std::cout << "Audio stream role: " << role_str << std::endl;
214
215 GstStructure *props = gst_structure_from_string (role_str.c_str(), NULL);
216- if (audio_sink != nullptr && props != nullptr)
217+ if (audio_sink != nullptr && props != nullptr) {
218 g_object_set (audio_sink, "stream-properties", props, NULL);
219- else
220- {
221+ } else {
222 std::cerr <<
223 "Warning: couldn't set audio stream role - couldn't get audio_sink from pipeline" <<
224 std::endl;
225
226=== modified file 'src/core/media/gstreamer/playbin.h'
227--- src/core/media/gstreamer/playbin.h 2015-08-10 13:07:04 +0000
228+++ src/core/media/gstreamer/playbin.h 2015-10-16 08:36:42 +0000
229@@ -117,6 +117,7 @@
230 gstreamer::Bus bus;
231 MediaFileType file_type;
232 GstElement* video_sink;
233+ GstElement* audio_sink;
234 core::Connection on_new_message_connection_async;
235 bool is_seeking;
236 mutable uint64_t previous_position;
237
238=== modified file 'src/core/media/mpris/player.h'
239--- src/core/media/mpris/player.h 2015-04-06 20:59:14 +0000
240+++ src/core/media/mpris/player.h 2015-10-16 08:36:42 +0000
241@@ -192,12 +192,12 @@
242 // Default values for properties
243 struct Defaults
244 {
245- Properties::CanPlay::ValueType can_play{true};
246- Properties::CanPause::ValueType can_pause{true};
247+ Properties::CanPlay::ValueType can_play{false};
248+ Properties::CanPause::ValueType can_pause{false};
249 Properties::CanSeek::ValueType can_seek{true};
250 Properties::CanControl::ValueType can_control{true};
251- Properties::CanGoNext::ValueType can_go_next{true};
252- Properties::CanGoPrevious::ValueType can_go_previous{true};
253+ Properties::CanGoNext::ValueType can_go_next{false};
254+ Properties::CanGoPrevious::ValueType can_go_previous{false};
255 Properties::IsVideoSource::ValueType is_video_source{false};
256 Properties::IsAudioSource::ValueType is_audio_source{true};
257 Properties::PlaybackStatus::ValueType playback_status{PlaybackStatus::stopped};
258@@ -307,6 +307,26 @@
259 {
260 on_property_value_changed<Properties::Shuffle>(shuffle);
261 });
262+
263+ properties.can_play->changed().connect([this](bool can_play)
264+ {
265+ on_property_value_changed<Properties::CanPlay>(can_play);
266+ });
267+
268+ properties.can_pause->changed().connect([this](bool can_pause)
269+ {
270+ on_property_value_changed<Properties::CanPause>(can_pause);
271+ });
272+
273+ properties.can_go_next->changed().connect([this](bool can_go_next)
274+ {
275+ on_property_value_changed<Properties::CanGoNext>(can_go_next);
276+ });
277+
278+ properties.can_go_previous->changed().connect([this](bool can_go_previous)
279+ {
280+ on_property_value_changed<Properties::CanGoPrevious>(can_go_previous);
281+ });
282 }
283
284 template<typename Property>
285
286=== modified file 'src/core/media/mpris/service.h'
287--- src/core/media/mpris/service.h 2015-04-10 16:13:55 +0000
288+++ src/core/media/mpris/service.h 2015-10-16 08:36:42 +0000
289@@ -115,6 +115,7 @@
290 DBUS_CPP_METHOD_WITH_TIMEOUT_DEF(DestroySession, Service, 1000)
291 DBUS_CPP_METHOD_WITH_TIMEOUT_DEF(CreateFixedSession, Service, 1000)
292 DBUS_CPP_METHOD_WITH_TIMEOUT_DEF(ResumeSession, Service, 1000)
293+ DBUS_CPP_METHOD_WITH_TIMEOUT_DEF(SetCurrentPlayer, Service, 1000)
294 DBUS_CPP_METHOD_WITH_TIMEOUT_DEF(PauseOtherSessions, Service, 1000)
295 };
296 }
297
298=== modified file 'src/core/media/player_implementation.cpp'
299--- src/core/media/player_implementation.cpp 2015-07-27 22:15:33 +0000
300+++ src/core/media/player_implementation.cpp 2015-10-16 08:36:42 +0000
301@@ -301,6 +301,27 @@
302 }
303 }
304
305+ void update_mpris_properties(void)
306+ {
307+ bool has_previous = track_list->has_previous()
308+ or parent->Parent::loop_status() != Player::LoopStatus::none;
309+ bool has_next = track_list->has_next()
310+ or parent->Parent::loop_status() != Player::LoopStatus::none;
311+ auto n_tracks = track_list->tracks()->size();
312+ bool has_tracks = (n_tracks > 0) ? true : false;
313+
314+ std::cout << "Updating MPRIS TrackList properties"
315+ << "; Tracks: " << n_tracks
316+ << ", has_previous: " << has_previous
317+ << ", has_next: " << has_next << std::endl;
318+
319+ // Update properties
320+ parent->can_play().set(has_tracks);
321+ parent->can_pause().set(has_tracks);
322+ parent->can_go_previous().set(has_previous);
323+ parent->can_go_next().set(has_next);
324+ }
325+
326 // Our link back to our parent.
327 media::PlayerImplementation<Parent>* parent;
328 // We just store the parameters passed on construction.
329@@ -327,8 +348,8 @@
330 d{std::make_shared<Private>(this, config)}
331 {
332 // Initialize default values for Player interface properties
333- Parent::can_play().set(true);
334- Parent::can_pause().set(true);
335+ Parent::can_play().set(false);
336+ Parent::can_pause().set(false);
337 Parent::can_seek().set(true);
338 Parent::can_go_previous().set(false);
339 Parent::can_go_next().set(false);
340@@ -376,13 +397,15 @@
341
342 std::function<bool()> can_go_next_getter = [this]()
343 {
344- return d->track_list->has_next();
345+ // If LoopStatus == playlist, then there is always a next track
346+ return d->track_list->has_next() or Parent::loop_status() != Player::LoopStatus::none;
347 };
348 Parent::can_go_next().install(can_go_next_getter);
349
350 std::function<bool()> can_go_previous_getter = [this]()
351 {
352- return d->track_list->has_previous();
353+ // If LoopStatus == playlist, then there is always a next previous
354+ return d->track_list->has_previous() or Parent::loop_status() != Player::LoopStatus::none;
355 };
356 Parent::can_go_previous().install(can_go_previous_getter);
357
358@@ -520,7 +543,26 @@
359 std::cout << "** Track was added, handling in PlayerImplementation" << std::endl;
360 if (d->track_list->tracks()->size() == 1)
361 d->open_first_track_from_tracklist(id);
362- });
363+
364+ d->update_mpris_properties();
365+ });
366+
367+ d->track_list->on_track_removed().connect([this](const media::Track::Id&)
368+ {
369+ d->update_mpris_properties();
370+ });
371+
372+ d->track_list->on_track_changed().connect([this](const media::Track::Id&)
373+ {
374+ d->update_mpris_properties();
375+ });
376+
377+ d->track_list->on_track_list_replaced().connect(
378+ [this](const media::TrackList::ContainerTrackIdTuple&)
379+ {
380+ d->update_mpris_properties();
381+ }
382+ );
383
384 // Everything is setup, we now subscribe to death notifications.
385 std::weak_ptr<Private> wp{d};
386
387=== modified file 'src/core/media/service_implementation.cpp'
388--- src/core/media/service_implementation.cpp 2015-04-17 16:44:30 +0000
389+++ src/core/media/service_implementation.cpp 2015-10-16 08:36:42 +0000
390@@ -246,8 +246,15 @@
391 return std::shared_ptr<media::Player>();
392 }
393
394+void media::ServiceImplementation::set_current_player(Player::PlayerKey key)
395+{
396+ // no impl
397+}
398+
399 void media::ServiceImplementation::pause_other_sessions(media::Player::PlayerKey key)
400 {
401+ std::cout << __PRETTY_FUNCTION__ << std::endl;
402+
403 if (not d->configuration.player_store->has_player_for_key(key))
404 {
405 cerr << "Could not find Player by key: " << key << endl;
406
407=== modified file 'src/core/media/service_implementation.h'
408--- src/core/media/service_implementation.h 2015-04-16 15:19:35 +0000
409+++ src/core/media/service_implementation.h 2015-10-16 08:36:42 +0000
410@@ -49,6 +49,7 @@
411 void destroy_session(const std::string&, const Player::Configuration&);
412 std::shared_ptr<Player> create_fixed_session(const std::string& name, const Player::Configuration&);
413 std::shared_ptr<Player> resume_session(Player::PlayerKey key);
414+ void set_current_player(Player::PlayerKey key);
415 void pause_other_sessions(Player::PlayerKey key);
416
417 private:
418
419=== modified file 'src/core/media/service_skeleton.cpp'
420--- src/core/media/service_skeleton.cpp 2015-08-11 16:10:14 +0000
421+++ src/core/media/service_skeleton.cpp 2015-10-16 08:36:42 +0000
422@@ -59,7 +59,7 @@
423 object(impl->access_service()->add_object_for_path(
424 dbus::traits::Service<media::Service>::object_path())),
425 configuration(config),
426- exported(impl->access_bus(), config.cover_art_resolver)
427+ exported(impl->access_bus(), config.cover_art_resolver, impl)
428 {
429 object->install_method_handler<mpris::Service::CreateSession>(
430 std::bind(
431@@ -91,6 +91,11 @@
432 &Private::handle_resume_session,
433 this,
434 std::placeholders::_1));
435+ object->install_method_handler<mpris::Service::SetCurrentPlayer>(
436+ std::bind(
437+ &Private::handle_set_current_player,
438+ this,
439+ std::placeholders::_1));
440 object->install_method_handler<mpris::Service::PauseOtherSessions>(
441 std::bind(
442 &Private::handle_pause_other_sessions,
443@@ -102,7 +107,7 @@
444 {
445 static unsigned int session_counter = 0;
446
447- unsigned int current_session = session_counter++;
448+ const unsigned int current_session = session_counter++;
449 boost::uuids::uuid uuid = gen();
450
451 std::stringstream ss;
452@@ -134,7 +139,8 @@
453 configuration.player_store->add_player_for_key(key, impl->create_session(config));
454 uuid_player_map.insert(std::make_pair(uuid, key));
455
456- request_context_resolver->resolve_context_for_dbus_name_async(msg->sender(), [this, key, msg](const media::apparmor::ubuntu::Context& context)
457+ request_context_resolver->resolve_context_for_dbus_name_async(msg->sender(),
458+ [this, key, msg](const media::apparmor::ubuntu::Context& context)
459 {
460 fprintf(stderr, "%s():%d -- app_name='%s', attached\n", __func__, __LINE__, context.str().c_str());
461 player_owner_map.insert(std::make_pair(key, std::make_tuple(context.str(), true, msg->sender())));
462@@ -213,7 +219,8 @@
463 ss << "/core/ubuntu/media/Service/sessions/" << key;
464 dbus::types::ObjectPath op{ss.str()};
465
466- request_context_resolver->resolve_context_for_dbus_name_async(msg->sender(), [this, msg, key, op](const media::apparmor::ubuntu::Context& context)
467+ request_context_resolver->resolve_context_for_dbus_name_async(msg->sender(),
468+ [this, msg, key, op](const media::apparmor::ubuntu::Context& context)
469 {
470 auto info = player_owner_map.at(key);
471 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());
472@@ -224,6 +231,12 @@
473 // Signal player reconnection
474 auto player = configuration.player_store->player_for_key(key);
475 player->reconnect();
476+ // We only care to allow the MPRIS controls to apply to multimedia player (i.e. audio, video)
477+ if (player->audio_stream_role() == media::Player::AudioStreamRole::multimedia)
478+ {
479+ std::cout << "Setting current_player" << std::endl;
480+ exported.set_current_player(player);
481+ }
482
483 auto reply = dbus::Message::make_method_return(msg);
484 reply->writer() << op;
485@@ -280,7 +293,8 @@
486 // the session is no longer usable.
487 uuid_player_map.erase(uuid);
488
489- request_context_resolver->resolve_context_for_dbus_name_async(msg->sender(), [this, msg, key](const media::apparmor::ubuntu::Context& context)
490+ request_context_resolver->resolve_context_for_dbus_name_async(msg->sender(),
491+ [this, msg, key](const media::apparmor::ubuntu::Context& context)
492 {
493 auto info = player_owner_map.at(key);
494 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());
495@@ -424,9 +438,18 @@
496 }
497 }
498
499+ void handle_set_current_player(const core::dbus::Message::Ptr& msg)
500+ {
501+ Player::PlayerKey key;
502+ msg->reader() >> key;
503+ impl->set_current_player(key);
504+
505+ auto reply = dbus::Message::make_method_return(msg);
506+ impl->access_bus()->send(reply);
507+ }
508+
509 void handle_pause_other_sessions(const core::dbus::Message::Ptr& msg)
510 {
511- std::cout << __PRETTY_FUNCTION__ << std::endl;
512 Player::PlayerKey key;
513 msg->reader() >> key;
514 impl->pause_other_sessions(key);
515@@ -469,45 +492,33 @@
516 {
517 mpris::Player::Skeleton::Configuration::Defaults defaults;
518
519- // Disabled as track list is not fully implemented yet.
520- defaults.can_go_next = false;
521- // Disabled as track list is not fully implemented yet.
522- defaults.can_go_previous = false;
523-
524 return defaults;
525 }
526
527- static std::string service_name()
528- {
529- static const bool export_to_indicator_sound_via_mpris
530- {
531- core::posix::this_process::env::get("UBUNTU_MEDIA_HUB_EXPORT_TO_INDICATOR_VIA_MPRIS", "0") == "1"
532- };
533-
534- return export_to_indicator_sound_via_mpris ? "org.mpris.MediaPlayer2.MediaHub" :
535- "hidden.org.mpris.MediaPlayer2.MediaHub";
536- }
537-
538- explicit Exported(const dbus::Bus::Ptr& bus, const media::CoverArtResolver& cover_art_resolver)
539+ explicit Exported(const dbus::Bus::Ptr& bus, const media::CoverArtResolver& cover_art_resolver,
540+ media::ServiceSkeleton* impl)
541 : bus{bus},
542- service{dbus::Service::add_service(bus, service_name())},
543+ /* Export MediaHub service interface on dbus */
544+ service{dbus::Service::add_service(bus, "org.mpris.MediaPlayer2.MediaHub")},
545 object{service->add_object_for_path(dbus::types::ObjectPath{"/org/mpris/MediaPlayer2"})},
546 media_player{mpris::MediaPlayer2::Skeleton::Configuration{bus, object, media_player_defaults()}},
547 player{mpris::Player::Skeleton::Configuration{bus, object, player_defaults()}},
548 playlists{mpris::Playlists::Skeleton::Configuration{bus, object, mpris::Playlists::Skeleton::Configuration::Defaults{}}},
549- cover_art_resolver{cover_art_resolver}
550+ cover_art_resolver{cover_art_resolver},
551+ impl{impl}
552 {
553 object->install_method_handler<core::dbus::interfaces::Properties::GetAll>([this](const core::dbus::Message::Ptr& msg)
554 {
555 // Extract the interface
556- std::string itf; msg->reader() >> itf;
557+ std::string interface;
558+ msg->reader() >> interface;
559 core::dbus::Message::Ptr reply = core::dbus::Message::make_method_return(msg);
560
561- if (itf == mpris::Player::name())
562+ if (interface == mpris::Player::name())
563 reply->writer() << player.get_all_properties();
564- else if (itf == mpris::MediaPlayer2::name())
565+ else if (interface == mpris::MediaPlayer2::name())
566 reply->writer() << media_player.get_all_properties();
567- else if (itf == mpris::Playlists::name())
568+ else if (interface == mpris::Playlists::name())
569 reply->writer() << playlists.get_all_properties();
570
571 Exported::bus->send(reply);
572@@ -516,9 +527,9 @@
573 // Setup method handlers for mpris::Player methods.
574 auto next = [this](const core::dbus::Message::Ptr& msg)
575 {
576- auto sp = current_player.lock();
577+ const auto sp = current_player.lock();
578
579- if (sp)
580+ if (is_multimedia_role())
581 sp->next();
582
583 Exported::bus->send(core::dbus::Message::make_method_return(msg));
584@@ -527,9 +538,9 @@
585
586 auto previous = [this](const core::dbus::Message::Ptr& msg)
587 {
588- auto sp = current_player.lock();
589+ const auto sp = current_player.lock();
590
591- if (sp)
592+ if (is_multimedia_role())
593 sp->previous();
594
595 Exported::bus->send(core::dbus::Message::make_method_return(msg));
596@@ -538,9 +549,9 @@
597
598 auto pause = [this](const core::dbus::Message::Ptr& msg)
599 {
600- auto sp = current_player.lock();
601+ const auto sp = current_player.lock();
602
603- if (sp)
604+ if (is_multimedia_role() and sp->can_pause())
605 sp->pause();
606
607 Exported::bus->send(core::dbus::Message::make_method_return(msg));
608@@ -549,36 +560,52 @@
609
610 auto stop = [this](const core::dbus::Message::Ptr& msg)
611 {
612- auto sp = current_player.lock();
613+ const auto sp = current_player.lock();
614
615- if (sp)
616+ if (is_multimedia_role())
617 sp->stop();
618
619 Exported::bus->send(core::dbus::Message::make_method_return(msg));
620 };
621 object->install_method_handler<mpris::Player::Stop>(stop);
622
623- auto play = [this](const core::dbus::Message::Ptr& msg)
624+ auto play = [this, impl](const core::dbus::Message::Ptr& msg)
625 {
626- auto sp = current_player.lock();
627-
628- if (sp)
629+ const auto sp = current_player.lock();
630+
631+ if (is_multimedia_role() and sp->can_play())
632+ {
633+ // Make sure other player sessions that are already playing
634+ // are paused before triggering new player (sp) to play
635+ if (impl)
636+ impl->pause_other_sessions(sp->key());
637+
638 sp->play();
639+ }
640
641 Exported::bus->send(core::dbus::Message::make_method_return(msg));
642 };
643 object->install_method_handler<mpris::Player::Play>(play);
644
645- auto play_pause = [this](const core::dbus::Message::Ptr& msg)
646+ auto play_pause = [this, impl](const core::dbus::Message::Ptr& msg)
647 {
648- auto sp = current_player.lock();
649+ const auto sp = current_player.lock();
650
651- if (sp)
652+ if (is_multimedia_role())
653 {
654- if (sp->playback_status() == media::Player::PlaybackStatus::playing)
655+ if (sp->playback_status() == media::Player::PlaybackStatus::playing
656+ and sp->can_pause())
657 sp->pause();
658- else if (sp->playback_status() != media::Player::PlaybackStatus::null)
659+ else if (sp->playback_status() != media::Player::PlaybackStatus::null
660+ and sp->can_play())
661+ {
662+ // Make sure other player sessions that are already playing
663+ // are paused before triggering new player (sp) to play
664+ if (impl)
665+ impl->pause_other_sessions(sp->key());
666+
667 sp->play();
668+ }
669 }
670
671 Exported::bus->send(core::dbus::Message::make_method_return(msg));
672@@ -586,15 +613,21 @@
673 object->install_method_handler<mpris::Player::PlayPause>(play_pause);
674 }
675
676+ inline bool is_multimedia_role()
677+ {
678+ const auto sp = current_player.lock();
679+
680+ return (sp ? sp->audio_stream_role() == media::Player::AudioStreamRole::multimedia : false);
681+ }
682+
683 void set_current_player(const std::shared_ptr<media::Player>& cp)
684 {
685- unset_current_player();
686-
687+ std::cout << "*** " << __PRETTY_FUNCTION__ << std::endl;
688 // We will not keep the object alive.
689 current_player = cp;
690
691 // And announce that we can be controlled again.
692- player.properties.can_control->set(false);
693+ player.properties.can_control->set(true);
694
695 // We wire up player state changes
696 connections.seeked_to = cp->seeked_to().connect([this](std::uint64_t position)
697@@ -612,68 +645,103 @@
698 player.properties.position->set(position);
699 });
700
701- connections.playback_status_changed = cp->playback_status().changed().connect([this](core::ubuntu::media::Player::PlaybackStatus status)
702+ connections.playback_status_changed = cp->playback_status().changed().connect(
703+ [this](core::ubuntu::media::Player::PlaybackStatus status)
704 {
705 player.properties.playback_status->set(mpris::Player::PlaybackStatus::from(status));
706 });
707
708- connections.loop_status_changed = cp->loop_status().changed().connect([this](core::ubuntu::media::Player::LoopStatus status)
709+ connections.loop_status_changed = cp->loop_status().changed().connect(
710+ [this](core::ubuntu::media::Player::LoopStatus status)
711 {
712 player.properties.loop_status->set(mpris::Player::LoopStatus::from(status));
713 });
714
715- connections.meta_data_changed = cp->meta_data_for_current_track().changed().connect([this](const core::ubuntu::media::Track::MetaData& md)
716- {
717- mpris::Player::Dictionary dict;
718-
719- bool has_title = md.count(xesam::Title::name) > 0;
720- bool has_album_name = md.count(xesam::Album::name) > 0;
721- bool has_artist_name = md.count(xesam::Artist::name) > 0;
722-
723- if (has_title)
724- dict[xesam::Title::name] = dbus::types::Variant::encode(md.get(xesam::Title::name));
725- if (has_album_name)
726- dict[xesam::Album::name] = dbus::types::Variant::encode(md.get(xesam::Album::name));
727- if (has_artist_name)
728- dict[xesam::Artist::name] = dbus::types::Variant::encode(md.get(xesam::Artist::name));
729-
730- dict[mpris::metadata::ArtUrl::name] = dbus::types::Variant::encode(
731- cover_art_resolver(
732- has_title ? md.get(xesam::Title::name) : "",
733- has_album_name ? md.get(xesam::Album::name) : "",
734- has_artist_name ? md.get(xesam::Artist::name) : ""));
735-
736- mpris::Player::Dictionary wrap;
737- wrap[mpris::Player::Properties::Metadata::name()] = dbus::types::Variant::encode(dict);
738-
739- player.signals.properties_changed->emit(
740- std::make_tuple(
741- dbus::traits::Service<mpris::Player::Properties::Metadata::Interface>::interface_name(),
742- wrap,
743- std::vector<std::string>()));
744- });
745+ connections.can_play_changed = cp->can_play().changed().connect(
746+ [this](bool can_play)
747+ {
748+ player.properties.can_play->set(can_play);
749+ });
750+
751+ connections.can_pause_changed = cp->can_pause().changed().connect(
752+ [this](bool can_pause)
753+ {
754+ player.properties.can_pause->set(can_pause);
755+ });
756+
757+ connections.can_go_previous_changed = cp->can_go_previous().changed().connect(
758+ [this](bool can_go_previous)
759+ {
760+ player.properties.can_go_previous->set(can_go_previous);
761+ });
762+
763+ connections.can_go_next_changed = cp->can_go_next().changed().connect(
764+ [this](bool can_go_next)
765+ {
766+ player.properties.can_go_next->set(can_go_next);
767+ });
768+
769+ // Sync property values between session and player mpris::Player instances
770+ // TODO Getters from media::Player actually return values from a
771+ // mpris::Player::Skeleton instance different from "player". Each of them use
772+ // different DBus object paths, /core/ubuntu/media/Service/sessions/<n>
773+ // and /org/mpris/MediaPlayer2 (this is the one enforced by the MPRIS spec).
774+ // Discuss why this is needed with tvoss.
775+ player.properties.duration->set(cp->duration().get());
776+ player.properties.position->set(cp->position().get());
777+ player.properties.playback_status->set(mpris::Player::PlaybackStatus::from(
778+ cp->playback_status().get()));
779+ player.properties.loop_status->set(mpris::Player::LoopStatus::from(
780+ cp->loop_status().get()));
781+ player.properties.can_play->set(cp->can_play().get());
782+ player.properties.can_pause->set(cp->can_pause().get());
783+ player.properties.can_go_previous->set(cp->can_go_previous().get());
784+ player.properties.can_go_next->set(cp->can_go_next().get());
785+
786+#if 0
787+ // TODO cover_art_resolver() is not implemented yet
788+ connections.meta_data_changed = cp->meta_data_for_current_track().changed().connect(
789+ [this](const core::ubuntu::media::Track::MetaData& md)
790+ {
791+ mpris::Player::Dictionary dict;
792+
793+ bool has_title = md.count(xesam::Title::name) > 0;
794+ bool has_album_name = md.count(xesam::Album::name) > 0;
795+ bool has_artist_name = md.count(xesam::Artist::name) > 0;
796+
797+ if (has_title)
798+ dict[xesam::Title::name] = dbus::types::Variant::encode(md.get(xesam::Title::name));
799+ if (has_album_name)
800+ dict[xesam::Album::name] = dbus::types::Variant::encode(md.get(xesam::Album::name));
801+ if (has_artist_name)
802+ dict[xesam::Artist::name] = dbus::types::Variant::encode(md.get(xesam::Artist::name));
803+
804+ dict[mpris::metadata::ArtUrl::name] = dbus::types::Variant::encode(
805+ cover_art_resolver(
806+ has_title ? md.get(xesam::Title::name) : "",
807+ has_album_name ? md.get(xesam::Album::name) : "",
808+ has_artist_name ? md.get(xesam::Artist::name) : ""));
809+
810+ mpris::Player::Dictionary wrap;
811+ wrap[mpris::Player::Properties::Metadata::name()] = dbus::types::Variant::encode(dict);
812+
813+ player.signals.properties_changed->emit(
814+ std::make_tuple(
815+ dbus::traits::Service<
816+ mpris::Player::Properties::Metadata::Interface>
817+ ::interface_name(),
818+ wrap,
819+ std::vector<std::string>()));
820+ });
821+#endif
822 }
823
824- void unset_current_player()
825+ void reset_current_player()
826 {
827+ std::cout << __PRETTY_FUNCTION__ << std::endl;
828+ // And announce that we can no longer be controlled.
829+ player.properties.can_control->set(false);
830 current_player.reset();
831-
832- // We disconnect all previous event connections.
833- connections.seeked_to.disconnect();
834- connections.duration_changed.disconnect();
835- connections.position_changed.disconnect();
836- connections.playback_status_changed.disconnect();
837- connections.loop_status_changed.disconnect();
838- connections.meta_data_changed.disconnect();
839-
840- // And announce that we cannot be controlled anymore.
841- player.properties.can_control->set(false);
842- }
843-
844- void unset_if_current(const std::shared_ptr<media::Player>& cp)
845- {
846- if (cp == current_player.lock())
847- unset_current_player();
848 }
849
850 dbus::Bus::Ptr bus;
851@@ -688,6 +756,9 @@
852 media::CoverArtResolver cover_art_resolver;
853 // The actual player instance.
854 std::weak_ptr<media::Player> current_player;
855+
856+ media::ServiceSkeleton* impl;
857+
858 // We track event connections.
859 struct
860 {
861@@ -711,6 +782,22 @@
862 {
863 the_empty_signal.connect([](){})
864 };
865+ core::Connection can_play_changed
866+ {
867+ the_empty_signal.connect([](){})
868+ };
869+ core::Connection can_pause_changed
870+ {
871+ the_empty_signal.connect([](){})
872+ };
873+ core::Connection can_go_previous_changed
874+ {
875+ the_empty_signal.connect([](){})
876+ };
877+ core::Connection can_go_next_changed
878+ {
879+ the_empty_signal.connect([](){})
880+ };
881 core::Connection meta_data_changed
882 {
883 the_empty_signal.connect([](){})
884@@ -759,6 +846,14 @@
885 return d->configuration.impl->resume_session(key);
886 }
887
888+void media::ServiceSkeleton::set_current_player(media::Player::PlayerKey key)
889+{
890+ const auto player = d->configuration.player_store->player_for_key(key);
891+ // We only care to allow the MPRIS controls to apply to multimedia player (i.e. audio, video)
892+ if (player->audio_stream_role() == media::Player::AudioStreamRole::multimedia)
893+ d->exported.set_current_player(player);
894+}
895+
896 void media::ServiceSkeleton::pause_other_sessions(media::Player::PlayerKey key)
897 {
898 d->configuration.impl->pause_other_sessions(key);
899
900=== modified file 'src/core/media/service_skeleton.h'
901--- src/core/media/service_skeleton.h 2015-04-16 15:19:35 +0000
902+++ src/core/media/service_skeleton.h 2015-10-16 08:36:42 +0000
903@@ -59,6 +59,7 @@
904 void destroy_session(const std::string&, const media::Player::Configuration&);
905 std::shared_ptr<Player> create_fixed_session(const std::string& name, const Player::Configuration&);
906 std::shared_ptr<Player> resume_session(Player::PlayerKey);
907+ void set_current_player(Player::PlayerKey key);
908 void pause_other_sessions(Player::PlayerKey key);
909
910 void run();
911
912=== modified file 'src/core/media/service_stub.cpp'
913--- src/core/media/service_stub.cpp 2015-04-17 16:44:30 +0000
914+++ src/core/media/service_stub.cpp 2015-10-16 08:36:42 +0000
915@@ -139,9 +139,17 @@
916 });
917 }
918
919+void media::ServiceStub::set_current_player(Player::PlayerKey key)
920+{
921+ auto op = d->object->invoke_method_synchronously<mpris::Service::SetCurrentPlayer,
922+ void>(key);
923+
924+ if (op.is_error())
925+ throw std::runtime_error("Problem setting current player: " + op.error());
926+}
927+
928 void media::ServiceStub::pause_other_sessions(media::Player::PlayerKey key)
929 {
930- std::cout << __PRETTY_FUNCTION__ << std::endl;
931 auto op = d->object->invoke_method_synchronously<mpris::Service::PauseOtherSessions,
932 void>(key);
933
934
935=== modified file 'src/core/media/service_stub.h'
936--- src/core/media/service_stub.h 2015-04-10 16:13:55 +0000
937+++ src/core/media/service_stub.h 2015-10-16 08:36:42 +0000
938@@ -45,6 +45,7 @@
939 void destroy_session(const std::string& uuid, const Player::Configuration&);
940 std::shared_ptr<Player> create_fixed_session(const std::string& name, const Player::Configuration&);
941 std::shared_ptr<Player> resume_session(Player::PlayerKey key);
942+ void set_current_player(Player::PlayerKey key);
943 void pause_other_sessions(Player::PlayerKey key);
944
945 private:
946
947=== modified file 'src/core/media/track_list_implementation.cpp'
948--- src/core/media/track_list_implementation.cpp 2015-08-11 15:25:50 +0000
949+++ src/core/media/track_list_implementation.cpp 2015-10-16 08:36:42 +0000
950@@ -215,20 +215,26 @@
951
952 void media::TrackListImplementation::reset()
953 {
954- std::cout << __PRETTY_FUNCTION__ << std::endl;
955-
956 // Make sure playback stops
957 on_end_of_tracklist()();
958+ // And make sure there is no "current" track
959+ media::TrackListSkeleton::reset();
960
961 auto result = tracks().update([this](TrackList::Container& container)
962 {
963- container.clear();
964+ TrackList::Container ids = container;
965+
966+ for (auto it = container.begin(); it != container.end(); ) {
967+ auto id = *it;
968+ it = container.erase(it);
969+ on_track_removed()(id);
970+ }
971+
972 container.resize(0);
973 d->track_counter = 0;
974+
975 return true;
976 });
977
978- media::TrackListSkeleton::reset();
979-
980 (void) result;
981 }
982
983=== modified file 'src/core/media/track_list_skeleton.cpp'
984--- src/core/media/track_list_skeleton.cpp 2015-08-11 15:25:50 +0000
985+++ src/core/media/track_list_skeleton.cpp 2015-10-16 08:36:42 +0000
986@@ -241,16 +241,31 @@
987
988 bool media::TrackListSkeleton::has_next()
989 {
990- if (tracks().get().empty())
991+ const auto n_tracks = tracks().get().size();
992+
993+ if (n_tracks == 0)
994 return false;
995
996+ // TODO Using current_iterator() makes media-hub crash later. Logic for
997+ // handling the iterators must be reviewed. As a minimum updates to the
998+ // track list should update current_track instead of the list being sneakly
999+ // changed in player_implementation.cpp.
1000+ // To avoid the crash we consider that current_track will be eventually
1001+ // initialized to the first track when current_iterator() gets called.
1002+ if (d->current_track == d->empty_iterator) {
1003+ if (n_tracks < 2)
1004+ return false;
1005+ else
1006+ return true;
1007+ }
1008+
1009 const auto next_track = std::next(current_iterator());
1010 return !is_last_track(next_track);
1011 }
1012
1013 bool media::TrackListSkeleton::has_previous()
1014 {
1015- if (tracks().get().empty())
1016+ if (tracks().get().empty() || d->current_track == d->empty_iterator)
1017 return false;
1018
1019 // If we are looping over the entire list, then there is always a previous track
1020@@ -273,8 +288,11 @@
1021 media::Track::Id media::TrackListSkeleton::next()
1022 {
1023 std::cout << __PRETTY_FUNCTION__ << std::endl;
1024- if (tracks().get().empty())
1025- return *(d->empty_iterator);
1026+ if (tracks().get().empty()) {
1027+ // TODO Change ServiceSkeleton to return with error from DBus call
1028+ std::cerr << "ERROR: no tracks, cannot go to next" << std::endl;
1029+ return media::Track::Id{};
1030+ }
1031
1032 const auto next_track = std::next(current_iterator());
1033 bool do_go_to_next_track = false;
1034@@ -328,8 +346,11 @@
1035 media::Track::Id media::TrackListSkeleton::previous()
1036 {
1037 std::cout << __PRETTY_FUNCTION__ << std::endl;
1038- if (tracks().get().empty())
1039- return *(d->empty_iterator);
1040+ if (tracks().get().empty()) {
1041+ // TODO Change ServiceSkeleton to return with error from DBus call
1042+ std::cerr << "ERROR: no tracks, cannot go to previous" << std::endl;
1043+ return media::Track::Id{};
1044+ }
1045
1046 bool do_go_to_previous_track = false;
1047
1048@@ -521,7 +542,6 @@
1049
1050 void media::TrackListSkeleton::reset()
1051 {
1052- std::cout << __PRETTY_FUNCTION__ << std::endl;
1053 d->current_track = d->empty_iterator;
1054 }
1055
1056
1057=== modified file 'src/core/media/track_list_skeleton.h'
1058--- src/core/media/track_list_skeleton.h 2015-07-27 22:15:33 +0000
1059+++ src/core/media/track_list_skeleton.h 2015-10-16 08:36:42 +0000
1060@@ -51,10 +51,12 @@
1061 const core::Property<Container>& tracks() const;
1062
1063 const core::Signal<ContainerTrackIdTuple>& on_track_list_replaced() const;
1064+ core::Signal<ContainerTrackIdTuple>& on_track_list_replaced();
1065 const core::Signal<Track::Id>& on_track_added() const;
1066 core::Signal<Track::Id>& on_track_added();
1067 const core::Signal<Track::Id>& on_track_removed() const;
1068 const core::Signal<Track::Id>& on_track_changed() const;
1069+ core::Signal<Track::Id>& on_track_changed();
1070 const core::Signal<std::pair<Track::Id, bool>>& on_go_to_track() const;
1071 core::Signal<std::pair<Track::Id, bool>>& on_go_to_track();
1072 const core::Signal<void>& on_end_of_tracklist() const;
1073@@ -77,9 +79,6 @@
1074
1075 core::Property<bool>& can_edit_tracks();
1076
1077- core::Signal<ContainerTrackIdTuple>& on_track_list_replaced();
1078- core::Signal<Track::Id>& on_track_changed();
1079-
1080 void reset();
1081
1082 private:

Subscribers

People subscribed via source and target branches

to all changes: