Merge lp:~thomas-voss/media-hub/indicator-and-bluez-integration into lp:media-hub
- indicator-and-bluez-integration
- Merge into trunk
Status: | Needs review |
---|---|
Proposed branch: | lp:~thomas-voss/media-hub/indicator-and-bluez-integration |
Merge into: | lp:media-hub |
Diff against target: |
4022 lines (+2214/-787) 26 files modified
src/core/media/apparmor.h (+5/-12) src/core/media/bluez/bluez.h (+350/-0) src/core/media/gstreamer/engine.cpp (+3/-1) src/core/media/mpris/media_player2.h (+177/-0) src/core/media/mpris/mpris.h (+27/-0) src/core/media/mpris/player.h (+236/-40) src/core/media/player_implementation.cpp (+268/-118) src/core/media/player_implementation.h (+32/-4) src/core/media/player_p.h (+62/-0) src/core/media/player_skeleton.cpp (+269/-366) src/core/media/player_skeleton.h (+112/-7) src/core/media/player_stub.cpp (+108/-97) src/core/media/player_stub.h (+12/-6) src/core/media/server/server.cpp (+9/-1) src/core/media/service_implementation.cpp (+285/-7) src/core/media/service_implementation.h (+23/-3) src/core/media/service_skeleton.cpp (+5/-1) src/core/media/service_stub.cpp (+51/-18) src/core/media/service_stub.h (+5/-4) src/core/media/track_list_implementation.cpp (+6/-9) src/core/media/track_list_implementation.h (+10/-3) src/core/media/track_list_skeleton.cpp (+17/-19) src/core/media/track_list_skeleton.h (+11/-3) src/core/media/track_list_stub.cpp (+32/-55) src/core/media/track_list_stub.h (+32/-8) tests/acceptance-tests/service.cpp (+67/-5) |
To merge this branch: | bzr merge lp:~thomas-voss/media-hub/indicator-and-bluez-integration |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
PS Jenkins bot | continuous-integration | Needs Fixing | |
Jim Hodapp | Pending | ||
Review via email: mp+225839@code.launchpad.net |
Commit message
Wires up the per-player sessions to:
* MPRIS, by owning an MPRIS-compliant name on the bus
* Bluez, for enabling remote control of music-playback from bluetooth devices
Description of the change
Wires up the per-player sessions to:
* MPRIS, by owning an MPRIS-compliant name on the bus
* Bluez, for enabling remote control of music-playback from bluetooth devices
PS Jenkins bot (ps-jenkins) wrote : | # |
Unmerged revisions
- 63. By Thomas Voß
-
Factor out initialization of player properties.
Wire up property changes. - 62. By Thomas Voß
-
Switch to immediate announcement policy for property updates on the MPRIS player skeleton.
- 61. By Thomas Voß
-
Add DesktopEntry property for mpris skeleton.
Make sure that org.mpris.MediaPlayer2 properties are initialized to sensible values. - 60. By Thomas Voß
-
Switch to custom service names to expose mpris instances on the session bus.
- 59. By Thomas Voß
-
Restore original interface naming rules.
- 58. By Thomas Voß
-
Fix service name generation.
- 57. By Thomas Voß
-
Make mpris skeletons reusable.
- 56. By Thomas Voß
-
Add some debugging output.
- 55. By Thomas Voß
-
Announce track changes to bluez only when track actually changes.
- 54. By Thomas Voß
-
Query duration of tracks whenever meta-data changes.
Preview Diff
1 | === modified file 'src/core/media/apparmor.h' |
2 | --- src/core/media/apparmor.h 2014-04-22 17:23:43 +0000 |
3 | +++ src/core/media/apparmor.h 2014-07-07 14:32:42 +0000 |
4 | @@ -19,6 +19,8 @@ |
5 | #ifndef APPARMOR_H_DBUS_ |
6 | #define APPARMOR_H_DBUS_ |
7 | |
8 | +#include <core/dbus/macros.h> |
9 | + |
10 | #include <string> |
11 | #include <chrono> |
12 | |
13 | @@ -33,18 +35,9 @@ |
14 | return s; |
15 | } |
16 | |
17 | - struct getConnectionAppArmorSecurityContext |
18 | - { |
19 | - static std::string name() |
20 | - { |
21 | - static std::string s = "GetConnectionAppArmorSecurityContext"; |
22 | - return s; |
23 | - } |
24 | - |
25 | - static const std::chrono::milliseconds default_timeout() { return std::chrono::seconds{1}; } |
26 | - |
27 | - typedef Apparmor Interface; |
28 | - }; |
29 | + DBUS_CPP_METHOD_DEF(GetConnectionUnixUser, Apparmor) |
30 | + DBUS_CPP_METHOD_DEF(GetConnectionUnixProcessID, Apparmor) |
31 | + DBUS_CPP_METHOD_DEF(GetConnectionAppArmorSecurityContext, Apparmor) |
32 | }; |
33 | } |
34 | |
35 | |
36 | === added directory 'src/core/media/bluez' |
37 | === added file 'src/core/media/bluez/bluez.h' |
38 | --- src/core/media/bluez/bluez.h 1970-01-01 00:00:00 +0000 |
39 | +++ src/core/media/bluez/bluez.h 2014-07-07 14:32:42 +0000 |
40 | @@ -0,0 +1,350 @@ |
41 | +/* |
42 | + * Copyright © 2013 Canonical Ltd. |
43 | + * |
44 | + * This program is free software: you can redistribute it and/or modify it |
45 | + * under the terms of the GNU Lesser General Public License version 3, |
46 | + * as published by the Free Software Foundation. |
47 | + * |
48 | + * This program is distributed in the hope that it will be useful, |
49 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
50 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
51 | + * GNU Lesser General Public License for more details. |
52 | + * |
53 | + * You should have received a copy of the GNU Lesser General Public License |
54 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
55 | + * |
56 | + * Authored by: Thomas Voß <thomas.voss@canonical.com> |
57 | + */ |
58 | + |
59 | +#ifndef ORG_BLUEZ_H_ |
60 | +#define ORG_BLUEZ_H_ |
61 | + |
62 | +#include <core/dbus/macros.h> |
63 | +#include <core/dbus/object.h> |
64 | +#include <core/dbus/service.h> |
65 | +#include <core/dbus/types/variant.h> |
66 | + |
67 | +#include <core/dbus/types/stl/map.h> |
68 | +#include <core/dbus/types/stl/string.h> |
69 | + |
70 | +#include <functional> |
71 | +#include <string> |
72 | + |
73 | +namespace org |
74 | +{ |
75 | +struct Bluez |
76 | +{ |
77 | + static const std::string& name() |
78 | + { |
79 | + static const std::string s{"org.bluez"}; return s; |
80 | + } |
81 | + |
82 | + struct Manager |
83 | + { |
84 | + static const std::string& name() |
85 | + { |
86 | + static const std::string s{"org.bluez.Manager"}; return s; |
87 | + } |
88 | + |
89 | + // Returns object path for the default adapter. |
90 | + DBUS_CPP_METHOD_DEF(DefaultAdapter, Manager) |
91 | + }; |
92 | + |
93 | + struct Media |
94 | + { |
95 | + static const std::string& name() |
96 | + { |
97 | + static const std::string s{"org.bluez.Media"}; return s; |
98 | + } |
99 | + |
100 | + struct Methods |
101 | + { |
102 | + // Register a media player object to sender, the sender |
103 | + // can register as many objets as it likes. |
104 | + // Note: If the sender disconnects its objects are |
105 | + // automatically unregistered. |
106 | + DBUS_CPP_METHOD_DEF(RegisterPlayer, Media) |
107 | + // Unregister sender media player. |
108 | + DBUS_CPP_METHOD_DEF(UnregisterPlayer, Media) |
109 | + }; |
110 | + }; |
111 | + |
112 | + struct MediaPlayer |
113 | + { |
114 | + typedef std::tuple<std::string, core::dbus::types::Variant> DictionaryEntry; |
115 | + typedef std::map<std::string, core::dbus::types::Variant> Dictionary; |
116 | + |
117 | + static const std::string& name() |
118 | + { |
119 | + static const std::string s{"org.bluez.MediaPlayer"}; return s; |
120 | + } |
121 | + |
122 | + static core::dbus::types::ObjectPath create_unique_instance_path() |
123 | + { |
124 | + static std::uint32_t counter = 0; |
125 | + return core::dbus::types::ObjectPath |
126 | + { |
127 | + "/org/bluez/MediaPlayer/" + std::to_string(counter++) |
128 | + }; |
129 | + } |
130 | + |
131 | + struct Methods |
132 | + { |
133 | + DBUS_CPP_METHOD_DEF(SetProperty, MediaPlayer) |
134 | + DBUS_CPP_METHOD_DEF(Release, MediaPlayer) |
135 | + }; |
136 | + |
137 | + struct Signals |
138 | + { |
139 | + DBUS_CPP_SIGNAL_DEF(PropertyChanged, MediaPlayer, DictionaryEntry) |
140 | + DBUS_CPP_SIGNAL_DEF(TrackChanged, MediaPlayer, Dictionary) |
141 | + }; |
142 | + |
143 | + // The function call takes a dictionary of properties describing the |
144 | + // player object. We list all the properties including their names and value types here. |
145 | + struct Properties |
146 | + { |
147 | + // Just a "namespace", thus no creation. |
148 | + Properties() = delete; |
149 | + |
150 | + // Whether an equalizer is in place for the player instance. |
151 | + struct Equalizer |
152 | + { |
153 | + static constexpr const char* name{"Equalizer"}; |
154 | + |
155 | + // The possible values. |
156 | + static constexpr const char* on{"on"}; |
157 | + static constexpr const char* off{"off"}; |
158 | + |
159 | + typedef std::string ValueType; |
160 | + }; |
161 | + |
162 | + // The repeat mode of the player instance. |
163 | + struct Repeat |
164 | + { |
165 | + static constexpr const char* name{"Repeat"}; |
166 | + |
167 | + // The possible values. |
168 | + static constexpr const char* off{"off"}; |
169 | + static constexpr const char* singletrack{"singletrack"}; |
170 | + static constexpr const char* alltracks{"alltracks"}; |
171 | + static constexpr const char* group{"group"}; |
172 | + |
173 | + typedef std::string ValueType; |
174 | + }; |
175 | + |
176 | + struct Scan |
177 | + { |
178 | + static constexpr const char* name{"Scan"}; |
179 | + |
180 | + // The possible values. |
181 | + static constexpr const char* off{"off"}; |
182 | + static constexpr const char* alltracks{"alltracks"}; |
183 | + static constexpr const char* group{"group"}; |
184 | + |
185 | + typedef std::string ValueType; |
186 | + }; |
187 | + |
188 | + struct Status |
189 | + { |
190 | + static constexpr const char* name{"Status"}; |
191 | + |
192 | + // The possible values. |
193 | + static constexpr const char* playing{"playing"}; |
194 | + static constexpr const char* stopped{"stopped"}; |
195 | + static constexpr const char* paused{"paused"}; |
196 | + static constexpr const char* forward_seek{"forward-seek"}; |
197 | + static constexpr const char* reverse_seek{"reverse-seek"}; |
198 | + static constexpr const char* error{"error"}; |
199 | + |
200 | + typedef std::string ValueType; |
201 | + }; |
202 | + |
203 | + struct Position |
204 | + { |
205 | + static constexpr const char* name{"Position"}; |
206 | + typedef std::uint32_t ValueType; |
207 | + }; |
208 | + |
209 | + struct Shuffle |
210 | + { |
211 | + static constexpr const char* name{"Shuffle"}; |
212 | + |
213 | + // The possible values. |
214 | + static constexpr const char* off{"off"}; |
215 | + static constexpr const char* alltracks{"alltracks"}; |
216 | + static constexpr const char* group{"group"}; |
217 | + |
218 | + typedef std::string ValueType; |
219 | + }; |
220 | + }; |
221 | + |
222 | + // This is only here to summarize all the Track properties. |
223 | + struct Track |
224 | + { |
225 | + // Just a "namespace", thus no creation. |
226 | + Track() = delete; |
227 | + |
228 | + // The function call takes a dictionary of meta-data describing the |
229 | + // current track/playlist. We list all the known meta-data here. |
230 | + struct MetaData |
231 | + { |
232 | + // Track title name |
233 | + struct Title |
234 | + { |
235 | + static constexpr const char* name{"Title"}; |
236 | + typedef std::string ValueType; |
237 | + }; |
238 | + |
239 | + // Track artist name |
240 | + struct Artist |
241 | + { |
242 | + static constexpr const char* name{"Artist"}; |
243 | + typedef std::string ValueType; |
244 | + }; |
245 | + |
246 | + // Track album name |
247 | + struct Album |
248 | + { |
249 | + static constexpr const char* name{"Album"}; |
250 | + typedef std::string ValueType; |
251 | + }; |
252 | + |
253 | + // Track genre name |
254 | + struct Genre |
255 | + { |
256 | + static constexpr const char* name{"Genre"}; |
257 | + typedef std::string ValueType; |
258 | + }; |
259 | + |
260 | + // Number of tracks in total |
261 | + struct NumberOfTracks |
262 | + { |
263 | + static constexpr const char* name{"NumberOfTracks"}; |
264 | + typedef std::uint32_t ValueType; |
265 | + }; |
266 | + |
267 | + // Track number |
268 | + struct Number |
269 | + { |
270 | + static constexpr const char* name{"Number"}; |
271 | + typedef std::uint32_t ValueType; |
272 | + }; |
273 | + |
274 | + // Track duration in milliseconds |
275 | + struct Duration |
276 | + { |
277 | + static constexpr const char* name{"Duration"}; |
278 | + typedef std::uint32_t ValueType; |
279 | + }; |
280 | + }; |
281 | + }; |
282 | + |
283 | + struct Configuration |
284 | + { |
285 | + // Helper type for processing incoming dbus method invocations. |
286 | + // Implementations have to return the reply to the incoming message |
287 | + // provided as an argument to the handler. |
288 | + typedef std::function |
289 | + < |
290 | + core::dbus::Message::Ptr(const core::dbus::Message::Ptr&) |
291 | + > InvocationHandler; |
292 | + |
293 | + // Returns a fallback invocation handler that just tells the other side: Not implemented. |
294 | + static const InvocationHandler& an_invocation_handler_returning_error() |
295 | + { |
296 | + static InvocationHandler invocation_handler = [](const core::dbus::Message::Ptr& msg) |
297 | + { |
298 | + return core::dbus::Message::make_error(msg, "not.implemented", "Message is not implemented"); |
299 | + }; |
300 | + |
301 | + return invocation_handler; |
302 | + } |
303 | + |
304 | + // The current dictionary of org.bluez.MediaPlayer properties. |
305 | + MediaPlayer::Dictionary media_player_properties; |
306 | + // The current dictionary of track metadata. |
307 | + MediaPlayer::Dictionary track_metadata; |
308 | + // The bus connection to use for dispatching replies. |
309 | + core::dbus::Bus::Ptr bus; |
310 | + // An existing stub object representing org.bluez.Media |
311 | + core::dbus::Object::Ptr bluez_media; |
312 | + // An existing service skeleton known on the bus. |
313 | + core::dbus::Service::Ptr service; |
314 | + // Method handler for invocations of SetProperty |
315 | + std::function<core::dbus::Message::Ptr(const core::dbus::Message::Ptr&)> on_set_property; |
316 | + // Method handler for invocations of Release. |
317 | + std::function<core::dbus::Message::Ptr(const core::dbus::Message::Ptr&)> on_release; |
318 | + }; |
319 | + |
320 | + MediaPlayer(const Configuration& configuration) |
321 | + : configuration(configuration), |
322 | + object(configuration.service->add_object_for_path(create_unique_instance_path())), |
323 | + sigs |
324 | + { |
325 | + object->get_signal<Signals::PropertyChanged>(), |
326 | + object->get_signal<Signals::TrackChanged>() |
327 | + } |
328 | + { |
329 | + object->install_method_handler<Methods::SetProperty>([this](const core::dbus::Message::Ptr& in) |
330 | + { |
331 | + auto reply = MediaPlayer::configuration.on_set_property(in); |
332 | + MediaPlayer::configuration.bus->send(reply); |
333 | + }); |
334 | + |
335 | + object->install_method_handler<Methods::Release>([this](const core::dbus::Message::Ptr& in) |
336 | + { |
337 | + auto reply = MediaPlayer::configuration.on_release(in); |
338 | + MediaPlayer::configuration.bus->send(reply); |
339 | + }); |
340 | + |
341 | + configuration.bluez_media->transact_method<org::Bluez::Media::Methods::RegisterPlayer, void>( |
342 | + // The path that this skeleton instance lives on. |
343 | + object->path(), |
344 | + // The properties of the underlying player instance. |
345 | + configuration.media_player_properties, |
346 | + // The current track metadata. |
347 | + configuration.track_metadata); |
348 | + } |
349 | + |
350 | + ~MediaPlayer() |
351 | + { |
352 | + object->uninstall_method_handler<Methods::SetProperty>(); |
353 | + object->uninstall_method_handler<Methods::Release>(); |
354 | + |
355 | + configuration.bluez_media->transact_method<org::Bluez::Media::Methods::UnregisterPlayer, void>( |
356 | + // The path that this skeleton instance lives on. |
357 | + object->path()); |
358 | + } |
359 | + |
360 | + template<typename Property> |
361 | + void set_property(const typename Property::ValueType& value) |
362 | + { |
363 | + sigs.property_changed->emit( |
364 | + std::make_tuple( |
365 | + std::string{Property::name}, |
366 | + core::dbus::types::Variant::encode(value))); |
367 | + } |
368 | + |
369 | + void set_track(const Dictionary& meta_data) |
370 | + { |
371 | + sigs.track_changed->emit(meta_data); |
372 | + } |
373 | + |
374 | + // All creation-time configuration options |
375 | + Configuration configuration; |
376 | + // The actual object representing the player. |
377 | + core::dbus::Object::Ptr object; |
378 | + // All the signals defined on the object; |
379 | + struct |
380 | + { |
381 | + // Should be emitted whenever a property of the player instance changes. |
382 | + core::dbus::Signal<Signals::PropertyChanged, typename Signals::PropertyChanged::ArgumentType>::Ptr property_changed; |
383 | + // Should be emitted whenever the currently playing track changes. |
384 | + core::dbus::Signal<Signals::TrackChanged, typename Signals::TrackChanged::ArgumentType>::Ptr track_changed; |
385 | + } sigs; |
386 | + }; |
387 | +}; |
388 | +} |
389 | + |
390 | +#endif // ORG_BLUEZ_H_ |
391 | |
392 | === modified file 'src/core/media/gstreamer/engine.cpp' |
393 | --- src/core/media/gstreamer/engine.cpp 2014-04-25 17:53:00 +0000 |
394 | +++ src/core/media/gstreamer/engine.cpp 2014-07-07 14:32:42 +0000 |
395 | @@ -56,7 +56,8 @@ |
396 | |
397 | void on_tag_available(const gstreamer::Bus::Message::Detail::Tag& tag) |
398 | { |
399 | - media::Track::MetaData md; |
400 | + std::cout << __PRETTY_FUNCTION__ << std::endl; |
401 | + media::Track::MetaData md = std::get<1>(track_meta_data.get()); |
402 | |
403 | gst_tag_list_foreach( |
404 | tag.tag_list, |
405 | @@ -64,6 +65,7 @@ |
406 | const gchar* tag, |
407 | gpointer user_data) |
408 | { |
409 | + std::cout << "\t" << tag << std::endl; |
410 | (void) list; |
411 | |
412 | static const std::map<std::string, std::string> gstreamer_to_mpris_tag_lut = |
413 | |
414 | === added file 'src/core/media/mpris/media_player2.h' |
415 | --- src/core/media/mpris/media_player2.h 1970-01-01 00:00:00 +0000 |
416 | +++ src/core/media/mpris/media_player2.h 2014-07-07 14:32:42 +0000 |
417 | @@ -0,0 +1,177 @@ |
418 | +/* |
419 | + * Copyright © 2013 Canonical Ltd. |
420 | + * |
421 | + * This program is free software: you can redistribute it and/or modify it |
422 | + * under the terms of the GNU Lesser General Public License version 3, |
423 | + * as published by the Free Software Foundation. |
424 | + * |
425 | + * This program is distributed in the hope that it will be useful, |
426 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
427 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
428 | + * GNU Lesser General Public License for more details. |
429 | + * |
430 | + * You should have received a copy of the GNU Lesser General Public License |
431 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
432 | + * |
433 | + * Authored by: Thomas Voß <thomas.voss@canonical.com> |
434 | + */ |
435 | + |
436 | +#ifndef MPRIS_MEDIA_PLAYER2_H_ |
437 | +#define MPRIS_MEDIA_PLAYER2_H_ |
438 | + |
439 | +#include <core/dbus/macros.h> |
440 | +#include <core/dbus/object.h> |
441 | +#include <core/dbus/property.h> |
442 | + |
443 | +#include <string> |
444 | +#include <vector> |
445 | + |
446 | +namespace mpris |
447 | +{ |
448 | +// Models interface org.mpris.MediaPlayer2, see: |
449 | +// http://specifications.freedesktop.org/mpris-spec/latest/Media_Player.html |
450 | +// for detailed documentation |
451 | +struct MediaPlayer2 |
452 | +{ |
453 | + static const std::string& name() |
454 | + { |
455 | + static const std::string s{"org.mpris.MediaPlayer2"}; return s; |
456 | + } |
457 | + |
458 | + struct Methods |
459 | + { |
460 | + // Brings the media player's user interface to the front using any appropriate |
461 | + // mechanism available. |
462 | + // The media player may be unable to control how its user interface is displayed, |
463 | + // or it may not have a graphical user interface at all. In this case, |
464 | + // the CanRaise property is false and this method does nothing. |
465 | + DBUS_CPP_METHOD_DEF(Raise, MediaPlayer2) |
466 | + |
467 | + // Causes the media player to stop running. |
468 | + // The media player may refuse to allow clients to shut it down. In this case, the |
469 | + // CanQuit property is false and this method does nothing. |
470 | + DBUS_CPP_METHOD_DEF(Quit, MediaPlayer2) |
471 | + }; |
472 | + |
473 | + struct Properties |
474 | + { |
475 | + // If false, calling Quit will have no effect, and may raise a NotSupported error. |
476 | + // If true, calling Quit will cause the media application to attempt to quit |
477 | + // (although it may still be prevented from quitting by the user, for example). |
478 | + DBUS_CPP_READABLE_PROPERTY_DEF(CanQuit, MediaPlayer2, bool) |
479 | + |
480 | + // Whether the media player is occupying the fullscreen. |
481 | + // This property is optional. Clients should handle its absence gracefully. |
482 | + DBUS_CPP_WRITABLE_PROPERTY_DEF(Fullscreen, MediaPlayer2, bool) |
483 | + |
484 | + // If false, attempting to set Fullscreen will have no effect, and may raise an error. |
485 | + // If true, attempting to set Fullscreen will not raise an error, and (if it is different |
486 | + // from the current value) will cause the media player to attempt to enter or exit fullscreen mode. |
487 | + // This property is optional. Clients should handle its absence gracefully. |
488 | + DBUS_CPP_READABLE_PROPERTY_DEF(CanSetFullscreen, MediaPlayer2, bool) |
489 | + |
490 | + // If false, calling Raise will have no effect, and may raise a NotSupported error. If true, calling Raise |
491 | + // will cause the media application to attempt to bring its user interface to the front, |
492 | + // although it may be prevented from doing so (by the window manager, for example). |
493 | + DBUS_CPP_READABLE_PROPERTY_DEF(CanRaise, MediaPlayer2, bool) |
494 | + |
495 | + // Indicates whether the /org/mpris/MediaPlayer2 object implements the |
496 | + // org.mpris.MediaPlayer2.TrackList interface. |
497 | + DBUS_CPP_READABLE_PROPERTY_DEF(HasTrackList, MediaPlayer2, bool) |
498 | + |
499 | + // A friendly name to identify the media player to users. |
500 | + DBUS_CPP_READABLE_PROPERTY_DEF(Identity, MediaPlayer2, std::string) |
501 | + |
502 | + // The basename of an installed .desktop file which complies with the Desktop entry specification, |
503 | + // with the ".desktop" extension stripped. |
504 | + // This property is optional. Clients should handle its absence gracefully. |
505 | + DBUS_CPP_READABLE_PROPERTY_DEF(DesktopEntry, MediaPlayer2, std::string) |
506 | + |
507 | + // The URI schemes supported by the media player. |
508 | + DBUS_CPP_READABLE_PROPERTY_DEF(SupportedUriSchemes, MediaPlayer2, std::vector<std::string>) |
509 | + |
510 | + // The mime-types supported by the media player. |
511 | + DBUS_CPP_READABLE_PROPERTY_DEF(SupportedMimeTypes, MediaPlayer2, std::vector<std::string>) |
512 | + }; |
513 | + |
514 | + struct Skeleton |
515 | + { |
516 | + // Creation time properties go here. |
517 | + struct Configuration |
518 | + { |
519 | + // Functor for handling incoming dbus calls |
520 | + typedef std::function<core::dbus::Message::Ptr(const core::dbus::Message::Ptr&)> InvocationHandler; |
521 | + |
522 | + // Returns |
523 | + static InvocationHandler a_handler_returning_error() |
524 | + { |
525 | + return [](const core::dbus::Message::Ptr& in) |
526 | + { |
527 | + return core::dbus::Message::make_error(in, "org.freedesktop.DBus.Error.Failed", "Not implemented"); |
528 | + }; |
529 | + } |
530 | + |
531 | + // The bus connection that should be used |
532 | + core::dbus::Bus::Ptr bus; |
533 | + // The dbus object that should implement org.mpris.MediaPlayer2 |
534 | + core::dbus::Object::Ptr object; |
535 | + // Invocation handler for raise requests. |
536 | + InvocationHandler on_raise; |
537 | + // Invocation handler for quit requests. |
538 | + InvocationHandler on_quit; |
539 | + }; |
540 | + |
541 | + // Creates a new instance, sets up player properties and installs method handlers. |
542 | + Skeleton(const Configuration& configuration) |
543 | + : configuration(configuration), |
544 | + properties |
545 | + { |
546 | + configuration.object->get_property<Properties::CanQuit>(), |
547 | + configuration.object->get_property<Properties::Fullscreen>(), |
548 | + configuration.object->get_property<Properties::CanSetFullscreen>(), |
549 | + configuration.object->get_property<Properties::CanRaise>(), |
550 | + configuration.object->get_property<Properties::HasTrackList>(), |
551 | + configuration.object->get_property<Properties::Identity>(), |
552 | + configuration.object->get_property<Properties::DesktopEntry>(), |
553 | + configuration.object->get_property<Properties::SupportedUriSchemes>(), |
554 | + configuration.object->get_property<Properties::SupportedMimeTypes>() |
555 | + } |
556 | + { |
557 | + configuration.object->install_method_handler<Methods::Raise>([this](const core::dbus::Message::Ptr& msg) |
558 | + { |
559 | + Skeleton::configuration.bus->send(Skeleton::configuration.on_raise(msg)); |
560 | + }); |
561 | + |
562 | + configuration.object->install_method_handler<Methods::Quit>([this](const core::dbus::Message::Ptr& msg) |
563 | + { |
564 | + Skeleton::configuration.bus->send(Skeleton::configuration.on_quit(msg)); |
565 | + }); |
566 | + } |
567 | + |
568 | + ~Skeleton() |
569 | + { |
570 | + configuration.object->uninstall_method_handler<Methods::Quit>(); |
571 | + configuration.object->uninstall_method_handler<Methods::Raise>(); |
572 | + } |
573 | + |
574 | + // We just store creation time properties here. |
575 | + Configuration configuration; |
576 | + |
577 | + // All property instances go here. |
578 | + struct |
579 | + { |
580 | + std::shared_ptr<core::dbus::Property<Properties::CanQuit>> can_quit; |
581 | + std::shared_ptr<core::dbus::Property<Properties::Fullscreen>> fullscreen; |
582 | + std::shared_ptr<core::dbus::Property<Properties::CanSetFullscreen>> can_set_fullscreen; |
583 | + std::shared_ptr<core::dbus::Property<Properties::CanRaise>> can_raise; |
584 | + std::shared_ptr<core::dbus::Property<Properties::HasTrackList>> has_track_list; |
585 | + std::shared_ptr<core::dbus::Property<Properties::Identity>> identity; |
586 | + std::shared_ptr<core::dbus::Property<Properties::DesktopEntry>> desktop_entry; |
587 | + std::shared_ptr<core::dbus::Property<Properties::SupportedUriSchemes>> supported_uri_schemes; |
588 | + std::shared_ptr<core::dbus::Property<Properties::SupportedMimeTypes>> supported_mime_types; |
589 | + } properties; |
590 | + }; |
591 | +}; |
592 | +} |
593 | + |
594 | +#endif // MPRIS_MEDIA_PLAYER2_H_ |
595 | |
596 | === added file 'src/core/media/mpris/mpris.h' |
597 | --- src/core/media/mpris/mpris.h 1970-01-01 00:00:00 +0000 |
598 | +++ src/core/media/mpris/mpris.h 2014-07-07 14:32:42 +0000 |
599 | @@ -0,0 +1,27 @@ |
600 | +/* |
601 | + * Copyright © 2013 Canonical Ltd. |
602 | + * |
603 | + * This program is free software: you can redistribute it and/or modify it |
604 | + * under the terms of the GNU Lesser General Public License version 3, |
605 | + * as published by the Free Software Foundation. |
606 | + * |
607 | + * This program is distributed in the hope that it will be useful, |
608 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
609 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
610 | + * GNU Lesser General Public License for more details. |
611 | + * |
612 | + * You should have received a copy of the GNU Lesser General Public License |
613 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
614 | + * |
615 | + * Authored by: Thomas Voß <thomas.voss@canonical.com> |
616 | + */ |
617 | + |
618 | +#ifndef MPRIS_MPRIS_H_ |
619 | +#define MPRIS_MPRIS_H_ |
620 | + |
621 | +namespace mpris |
622 | +{ |
623 | +struct |
624 | +} |
625 | + |
626 | +#define MPRIS_MPRIS_H_ |
627 | |
628 | === modified file 'src/core/media/mpris/player.h' |
629 | --- src/core/media/mpris/player.h 2014-04-25 17:53:00 +0000 |
630 | +++ src/core/media/mpris/player.h 2014-07-07 14:32:42 +0000 |
631 | @@ -22,8 +22,12 @@ |
632 | #include <core/media/player.h> |
633 | #include <core/media/track.h> |
634 | |
635 | -#include "macros.h" |
636 | +#include "core/media/codec.h" |
637 | |
638 | +#include <core/dbus/bus.h> |
639 | +#include <core/dbus/macros.h> |
640 | +#include <core/dbus/object.h> |
641 | +#include <core/dbus/property.h> |
642 | #include <core/dbus/types/any.h> |
643 | #include <core/dbus/types/object_path.h> |
644 | #include <core/dbus/types/variant.h> |
645 | @@ -38,54 +42,246 @@ |
646 | |
647 | namespace mpris |
648 | { |
649 | +template<typename Tag> |
650 | struct Player |
651 | { |
652 | - static const std::string& name() |
653 | - { |
654 | - static const std::string s{"core.ubuntu.media.Service.Player"}; |
655 | - return s; |
656 | - } |
657 | - |
658 | - METHOD(Next, Player, std::chrono::seconds(1)) |
659 | - METHOD(Previous, Player, std::chrono::seconds(1)) |
660 | - METHOD(Pause, Player, std::chrono::seconds(1)) |
661 | - METHOD(PlayPause, Player, std::chrono::seconds(1)) |
662 | - METHOD(Stop, Player, std::chrono::seconds(1)) |
663 | - METHOD(Play, Player, std::chrono::seconds(1)) |
664 | - METHOD(Seek, Player, std::chrono::seconds(1)) |
665 | - METHOD(SetPosition, Player, std::chrono::seconds(1)) |
666 | - METHOD(CreateVideoSink, Player, std::chrono::seconds(1)) |
667 | - METHOD(Key, Player, std::chrono::seconds(1)) |
668 | - METHOD(OpenUri, Player, std::chrono::seconds(1)) |
669 | + struct LoopStatus |
670 | + { |
671 | + LoopStatus() = delete; |
672 | + |
673 | + static constexpr const char* none{"None"}; |
674 | + static constexpr const char* track{"Track"}; |
675 | + static constexpr const char* playlist{"Playlist"}; |
676 | + }; |
677 | + |
678 | + struct PlaybackStatus |
679 | + { |
680 | + PlaybackStatus() = delete; |
681 | + |
682 | + static const char* from(core::ubuntu::media::Player::PlaybackStatus status) |
683 | + { |
684 | + switch(status) |
685 | + { |
686 | + case core::ubuntu::media::Player::PlaybackStatus::null: |
687 | + case core::ubuntu::media::Player::PlaybackStatus::ready: |
688 | + case core::ubuntu::media::Player::PlaybackStatus::stopped: |
689 | + return PlaybackStatus::stopped; |
690 | + |
691 | + case core::ubuntu::media::Player::PlaybackStatus::playing: |
692 | + return PlaybackStatus::playing; |
693 | + case core::ubuntu::media::Player::PlaybackStatus::paused: |
694 | + return PlaybackStatus::paused; |
695 | + } |
696 | + |
697 | + return nullptr; |
698 | + } |
699 | + |
700 | + static constexpr const char* playing{"Playing"}; |
701 | + static constexpr const char* paused{"Paused"}; |
702 | + static constexpr const char* stopped{"Stopped"}; |
703 | + }; |
704 | + |
705 | + typedef std::map<std::string, core::dbus::types::Variant> Dictionary; |
706 | + |
707 | + DBUS_CPP_METHOD_DEF(Next, Player) |
708 | + DBUS_CPP_METHOD_DEF(Previous, Player) |
709 | + DBUS_CPP_METHOD_DEF(Pause, Player) |
710 | + DBUS_CPP_METHOD_DEF(PlayPause, Player) |
711 | + DBUS_CPP_METHOD_DEF(Stop, Player) |
712 | + DBUS_CPP_METHOD_DEF(Play, Player) |
713 | + DBUS_CPP_METHOD_DEF(Seek, Player) |
714 | + DBUS_CPP_METHOD_DEF(SetPosition, Player) |
715 | + DBUS_CPP_METHOD_DEF(CreateVideoSink, Player) |
716 | + DBUS_CPP_METHOD_DEF(Key, Player) |
717 | + DBUS_CPP_METHOD_DEF(OpenUri, Player) |
718 | |
719 | struct Signals |
720 | { |
721 | - SIGNAL(Seeked, Player, uint64_t) |
722 | - SIGNAL(EndOfStream, Player, void) |
723 | - SIGNAL(PlaybackStatusChanged, Player, core::ubuntu::media::Player::PlaybackStatus) |
724 | + DBUS_CPP_SIGNAL_DEF(Seeked, Player, std::uint64_t) |
725 | + DBUS_CPP_SIGNAL_DEF(EndOfStream, Player, void) |
726 | + DBUS_CPP_SIGNAL_DEF(PlaybackStatusChanged, Player, core::ubuntu::media::Player::PlaybackStatus) |
727 | }; |
728 | |
729 | struct Properties |
730 | { |
731 | - READABLE_PROPERTY(PlaybackStatus, Player, core::ubuntu::media::Player::PlaybackStatus) |
732 | - WRITABLE_PROPERTY(LoopStatus, Player, core::ubuntu::media::Player::LoopStatus) |
733 | - WRITABLE_PROPERTY(PlaybackRate, Player, core::ubuntu::media::Player::PlaybackRate) |
734 | - WRITABLE_PROPERTY(Rate, Player, double) |
735 | - WRITABLE_PROPERTY(Shuffle, Player, bool) |
736 | - READABLE_PROPERTY(MetaData, Player, core::ubuntu::media::Track::MetaData) |
737 | - WRITABLE_PROPERTY(Volume, Player, double) |
738 | - READABLE_PROPERTY(Position, Player, uint64_t) |
739 | - READABLE_PROPERTY(Duration, Player, uint64_t) |
740 | - READABLE_PROPERTY(MinimumRate, Player, double) |
741 | - READABLE_PROPERTY(MaximumRate, Player, double) |
742 | - READABLE_PROPERTY(IsVideoSource, Player, bool) |
743 | - READABLE_PROPERTY(IsAudioSource, Player, bool) |
744 | - READABLE_PROPERTY(CanGoNext, Player, bool) |
745 | - READABLE_PROPERTY(CanGoPrevious, Player, bool) |
746 | - READABLE_PROPERTY(CanPlay, Player, bool) |
747 | - READABLE_PROPERTY(CanPause, Player, bool) |
748 | - READABLE_PROPERTY(CanSeek, Player, bool) |
749 | - READABLE_PROPERTY(CanControl, Player, bool) |
750 | + DBUS_CPP_READABLE_PROPERTY_DEF(PlaybackStatus, Player, std::string) |
751 | + DBUS_CPP_READABLE_PROPERTY_DEF(TypedPlaybackStatus, Player, core::ubuntu::media::Player::PlaybackStatus) |
752 | + |
753 | + DBUS_CPP_WRITABLE_PROPERTY_DEF(LoopStatus, Player, std::string) |
754 | + DBUS_CPP_WRITABLE_PROPERTY_DEF(TypedLoopStatus, Player, core::ubuntu::media::Player::LoopStatus) |
755 | + |
756 | + DBUS_CPP_WRITABLE_PROPERTY_DEF(PlaybackRate, Player, double) |
757 | + DBUS_CPP_WRITABLE_PROPERTY_DEF(Rate, Player, double) |
758 | + DBUS_CPP_WRITABLE_PROPERTY_DEF(Shuffle, Player, bool) |
759 | + DBUS_CPP_READABLE_PROPERTY_DEF(MetaData, Player, Dictionary) |
760 | + DBUS_CPP_READABLE_PROPERTY_DEF(TypedMetaData, Player, core::ubuntu::media::Track::MetaData) |
761 | + DBUS_CPP_WRITABLE_PROPERTY_DEF(Volume, Player, double) |
762 | + DBUS_CPP_READABLE_PROPERTY_DEF(Position, Player, std::uint64_t) |
763 | + DBUS_CPP_READABLE_PROPERTY_DEF(Duration, Player, std::uint64_t) |
764 | + DBUS_CPP_READABLE_PROPERTY_DEF(MinimumRate, Player, double) |
765 | + DBUS_CPP_READABLE_PROPERTY_DEF(MaximumRate, Player, double) |
766 | + DBUS_CPP_READABLE_PROPERTY_DEF(IsVideoSource, Player, bool) |
767 | + DBUS_CPP_READABLE_PROPERTY_DEF(IsAudioSource, Player, bool) |
768 | + DBUS_CPP_READABLE_PROPERTY_DEF(CanGoNext, Player, bool) |
769 | + DBUS_CPP_READABLE_PROPERTY_DEF(CanGoPrevious, Player, bool) |
770 | + DBUS_CPP_READABLE_PROPERTY_DEF(CanPlay, Player, bool) |
771 | + DBUS_CPP_READABLE_PROPERTY_DEF(CanPause, Player, bool) |
772 | + DBUS_CPP_READABLE_PROPERTY_DEF(CanSeek, Player, bool) |
773 | + DBUS_CPP_READABLE_PROPERTY_DEF(CanControl, Player, bool) |
774 | + }; |
775 | + |
776 | + // Convenience struct to create a skeleton implementation for org.mpris.MediaPlayer2.Player |
777 | + struct Skeleton |
778 | + { |
779 | + // Creation time options go here. |
780 | + struct Configuration |
781 | + { |
782 | + // Functor for handling incoming dbus calls |
783 | + typedef std::function<core::dbus::Message::Ptr(const core::dbus::Message::Ptr&)> InvocationHandler; |
784 | + |
785 | + // The bus connection that should be used |
786 | + core::dbus::Bus::Ptr bus; |
787 | + // The dbus object that should implement org.mpris.MediaPlayer2 |
788 | + core::dbus::Object::Ptr object; |
789 | + // Handles incoming calls for next. Never throws. |
790 | + InvocationHandler handle_next; |
791 | + // Handles incoming calls for previous. Never throws. |
792 | + InvocationHandler handle_previous; |
793 | + // Handles incoming calls for pause. Never throws. |
794 | + InvocationHandler handle_pause; |
795 | + // Handles incoming calls for stpo. Never throws. |
796 | + InvocationHandler handle_stop; |
797 | + // Handles incoming calls for play. Never throws. |
798 | + InvocationHandler handle_play; |
799 | + // Handles incoming calls for seek. Never throws. |
800 | + InvocationHandler handle_seek; |
801 | + // Handles incoming calls for video sink creation. Never throws. |
802 | + InvocationHandler handle_create_video_sink; |
803 | + // Handles incoming calls for key generation. Never throws. |
804 | + InvocationHandler handle_key; |
805 | + // Handles incoming calls for opening a URI. Never throws. |
806 | + InvocationHandler handle_open_uri; |
807 | + }; |
808 | + |
809 | + Skeleton(const Configuration& configuration) |
810 | + : configuration(configuration), |
811 | + properties |
812 | + { |
813 | + configuration.object->template get_property<typename mpris::Player<Tag>::Properties::CanPlay>(), |
814 | + configuration.object->template get_property<typename mpris::Player<Tag>::Properties::CanPause>(), |
815 | + configuration.object->template get_property<typename mpris::Player<Tag>::Properties::CanSeek>(), |
816 | + configuration.object->template get_property<typename mpris::Player<Tag>::Properties::CanControl>(), |
817 | + configuration.object->template get_property<typename mpris::Player<Tag>::Properties::CanGoNext>(), |
818 | + configuration.object->template get_property<typename mpris::Player<Tag>::Properties::CanGoPrevious>(), |
819 | + configuration.object->template get_property<typename mpris::Player<Tag>::Properties::IsVideoSource>(), |
820 | + configuration.object->template get_property<typename mpris::Player<Tag>::Properties::IsAudioSource>(), |
821 | + configuration.object->template get_property<typename mpris::Player<Tag>::Properties::PlaybackStatus>(), |
822 | + configuration.object->template get_property<typename mpris::Player<Tag>::Properties::TypedPlaybackStatus>(), |
823 | + configuration.object->template get_property<typename mpris::Player<Tag>::Properties::LoopStatus>(), |
824 | + configuration.object->template get_property<typename mpris::Player<Tag>::Properties::TypedLoopStatus>(), |
825 | + configuration.object->template get_property<typename mpris::Player<Tag>::Properties::PlaybackRate>(), |
826 | + configuration.object->template get_property<typename mpris::Player<Tag>::Properties::Shuffle>(), |
827 | + configuration.object->template get_property<typename mpris::Player<Tag>::Properties::TypedMetaData>(), |
828 | + configuration.object->template get_property<typename mpris::Player<Tag>::Properties::Volume>(), |
829 | + configuration.object->template get_property<typename mpris::Player<Tag>::Properties::Position>(), |
830 | + configuration.object->template get_property<typename mpris::Player<Tag>::Properties::Duration>(), |
831 | + configuration.object->template get_property<typename mpris::Player<Tag>::Properties::MinimumRate>(), |
832 | + configuration.object->template get_property<typename mpris::Player<Tag>::Properties::MaximumRate>() |
833 | + }, |
834 | + signals |
835 | + { |
836 | + configuration.object->template get_signal<typename mpris::Player<Tag>::Signals::Seeked>(), |
837 | + configuration.object->template get_signal<typename mpris::Player<Tag>::Signals::EndOfStream>(), |
838 | + configuration.object->template get_signal<typename mpris::Player<Tag>::Signals::PlaybackStatusChanged>() |
839 | + } |
840 | + { |
841 | + Skeleton::configuration.object->template install_method_handler<typename mpris::Player<Tag>::Next>( |
842 | + [this](const core::dbus::Message::Ptr& in) |
843 | + { |
844 | + Skeleton::configuration.bus->send(Skeleton::configuration.handle_next(in)); |
845 | + }); |
846 | + |
847 | + Skeleton::configuration.object->template install_method_handler<typename mpris::Player<Tag>::Previous>( |
848 | + [this](const core::dbus::Message::Ptr& in) |
849 | + { |
850 | + Skeleton::configuration.bus->send(Skeleton::configuration.handle_previous(in)); |
851 | + }); |
852 | + Skeleton::configuration.object->template install_method_handler<typename mpris::Player<Tag>::Pause>( |
853 | + [this](const core::dbus::Message::Ptr& in) |
854 | + { |
855 | + Skeleton::configuration.bus->send(Skeleton::configuration.handle_pause(in)); |
856 | + }); |
857 | + Skeleton::configuration.object->template install_method_handler<typename mpris::Player<Tag>::Stop>( |
858 | + [this](const core::dbus::Message::Ptr& in) |
859 | + { |
860 | + Skeleton::configuration.bus->send(Skeleton::configuration.handle_stop(in)); |
861 | + }); |
862 | + Skeleton::configuration.object->template install_method_handler<typename mpris::Player<Tag>::Play>( |
863 | + [this](const core::dbus::Message::Ptr& in) |
864 | + { |
865 | + Skeleton::configuration.bus->send(Skeleton::configuration.handle_play(in)); |
866 | + }); |
867 | + Skeleton::configuration.object->template install_method_handler<typename mpris::Player<Tag>::Seek>( |
868 | + [this](const core::dbus::Message::Ptr& in) |
869 | + { |
870 | + Skeleton::configuration.bus->send(Skeleton::configuration.handle_seek(in)); |
871 | + }); |
872 | + Skeleton::configuration.object->template install_method_handler<typename mpris::Player<Tag>::SetPosition>( |
873 | + [this](const core::dbus::Message::Ptr&) |
874 | + { |
875 | + // Skeleton::configuration.handle_set_position(in)); |
876 | + }); |
877 | + Skeleton::configuration.object->template install_method_handler<typename mpris::Player<Tag>::CreateVideoSink>( |
878 | + [this](const core::dbus::Message::Ptr& in) |
879 | + { |
880 | + Skeleton::configuration.bus->send(Skeleton::configuration.handle_create_video_sink(in)); |
881 | + }); |
882 | + Skeleton::configuration.object->template install_method_handler<typename mpris::Player<Tag>::Key>( |
883 | + [this](const core::dbus::Message::Ptr& in) |
884 | + { |
885 | + Skeleton::configuration.bus->send(Skeleton::configuration.handle_key(in)); |
886 | + }); |
887 | + Skeleton::configuration.object->template install_method_handler<typename mpris::Player<Tag>::OpenUri>( |
888 | + [this](const core::dbus::Message::Ptr& in) |
889 | + { |
890 | + Skeleton::configuration.bus->send(Skeleton::configuration.handle_open_uri(in)); |
891 | + }); |
892 | + } |
893 | + |
894 | + // We just store creation time properties |
895 | + Configuration configuration; |
896 | + // All the properties exposed to the bus go here. |
897 | + struct |
898 | + { |
899 | + std::shared_ptr<core::dbus::Property<typename mpris::Player<Tag>::Properties::CanPlay>> can_play; |
900 | + std::shared_ptr<core::dbus::Property<typename mpris::Player<Tag>::Properties::CanPause>> can_pause; |
901 | + std::shared_ptr<core::dbus::Property<typename mpris::Player<Tag>::Properties::CanSeek>> can_seek; |
902 | + std::shared_ptr<core::dbus::Property<typename mpris::Player<Tag>::Properties::CanControl>> can_control; |
903 | + std::shared_ptr<core::dbus::Property<typename mpris::Player<Tag>::Properties::CanGoNext>> can_go_next; |
904 | + std::shared_ptr<core::dbus::Property<typename mpris::Player<Tag>::Properties::CanGoPrevious>> can_go_previous; |
905 | + std::shared_ptr<core::dbus::Property<typename mpris::Player<Tag>::Properties::IsVideoSource>> is_video_source; |
906 | + std::shared_ptr<core::dbus::Property<typename mpris::Player<Tag>::Properties::IsAudioSource>> is_audio_source; |
907 | + |
908 | + std::shared_ptr<core::dbus::Property<typename mpris::Player<Tag>::Properties::PlaybackStatus>> playback_status; |
909 | + std::shared_ptr<core::dbus::Property<typename mpris::Player<Tag>::Properties::TypedPlaybackStatus>> typed_playback_status; |
910 | + std::shared_ptr<core::dbus::Property<typename mpris::Player<Tag>::Properties::LoopStatus>> loop_status; |
911 | + std::shared_ptr<core::dbus::Property<typename mpris::Player<Tag>::Properties::TypedLoopStatus>> typed_loop_status; |
912 | + std::shared_ptr<core::dbus::Property<typename mpris::Player<Tag>::Properties::PlaybackRate>> playback_rate; |
913 | + std::shared_ptr<core::dbus::Property<typename mpris::Player<Tag>::Properties::Shuffle>> is_shuffle; |
914 | + std::shared_ptr<core::dbus::Property<typename mpris::Player<Tag>::Properties::TypedMetaData>> meta_data_for_current_track; |
915 | + std::shared_ptr<core::dbus::Property<typename mpris::Player<Tag>::Properties::Volume>> volume; |
916 | + std::shared_ptr<core::dbus::Property<typename mpris::Player<Tag>::Properties::Position>> position; |
917 | + std::shared_ptr<core::dbus::Property<typename mpris::Player<Tag>::Properties::Duration>> duration; |
918 | + std::shared_ptr<core::dbus::Property<typename mpris::Player<Tag>::Properties::MinimumRate>> minimum_playback_rate; |
919 | + std::shared_ptr<core::dbus::Property<typename mpris::Player<Tag>::Properties::MaximumRate>> maximum_playback_rate; |
920 | + } properties; |
921 | + |
922 | + struct |
923 | + { |
924 | + typename core::dbus::Signal<typename mpris::Player<Tag>::Signals::Seeked, typename mpris::Player<Tag>::Signals::Seeked::ArgumentType>::Ptr seeked_to; |
925 | + typename core::dbus::Signal<typename mpris::Player<Tag>::Signals::EndOfStream, typename mpris::Player<Tag>::Signals::EndOfStream::ArgumentType>::Ptr end_of_stream; |
926 | + typename core::dbus::Signal<typename mpris::Player<Tag>::Signals::PlaybackStatusChanged, typename mpris::Player<Tag>::Signals::PlaybackStatusChanged::ArgumentType>::Ptr playback_status_changed; |
927 | + } signals; |
928 | }; |
929 | }; |
930 | } |
931 | |
932 | === modified file 'src/core/media/player_implementation.cpp' |
933 | --- src/core/media/player_implementation.cpp 2014-06-24 17:03:27 +0000 |
934 | +++ src/core/media/player_implementation.cpp 2014-07-07 14:32:42 +0000 |
935 | @@ -19,12 +19,12 @@ |
936 | |
937 | #include <unistd.h> |
938 | |
939 | +#include "bluez/bluez.h" |
940 | #include "engine.h" |
941 | #include "track_list_implementation.h" |
942 | |
943 | #include "powerd_service.h" |
944 | #include "unity_screen_service.h" |
945 | -#include "gstreamer/engine.h" |
946 | |
947 | #include <exception> |
948 | #include <iostream> |
949 | @@ -38,65 +38,122 @@ |
950 | |
951 | struct media::PlayerImplementation::Private |
952 | { |
953 | - Private(PlayerImplementation* parent, |
954 | - const dbus::types::ObjectPath& session_path, |
955 | - const std::shared_ptr<media::Service>& service, |
956 | - PlayerImplementation::PlayerKey key) |
957 | - : parent(parent), |
958 | - service(service), |
959 | - engine(std::make_shared<gstreamer::Engine>()), |
960 | - session_path(session_path), |
961 | - track_list( |
962 | - new media::TrackListImplementation( |
963 | - session_path.as_string() + "/TrackList", |
964 | - engine->meta_data_extractor())), |
965 | - sys_lock_name("media-hub-music-playback"), |
966 | - active_display_on_request(false), |
967 | - key(key) |
968 | - { |
969 | - auto bus = std::shared_ptr<dbus::Bus>(new dbus::Bus(core::dbus::WellKnownBus::system)); |
970 | - bus->install_executor(dbus::asio::make_executor(bus)); |
971 | - |
972 | - auto stub_service = dbus::Service::use_service(bus, dbus::traits::Service<core::Powerd>::interface_name()); |
973 | - powerd_session = stub_service->object_for_path(dbus::types::ObjectPath("/com/canonical/powerd")); |
974 | - |
975 | - auto uscreen_stub_service = dbus::Service::use_service(bus, dbus::traits::Service<core::UScreen>::interface_name()); |
976 | - uscreen_session = uscreen_stub_service->object_for_path(dbus::types::ObjectPath("/com/canonical/Unity/Screen")); |
977 | - |
978 | - engine->state().changed().connect( |
979 | - [parent, this](const Engine::State& state) |
980 | - { |
981 | - switch(state) |
982 | - { |
983 | - case Engine::State::ready: |
984 | - { |
985 | - parent->playback_status().set(media::Player::ready); |
986 | - clear_power_state(); |
987 | - break; |
988 | - } |
989 | - case Engine::State::playing: |
990 | - { |
991 | - parent->playback_status().set(media::Player::playing); |
992 | - request_power_state(); |
993 | - break; |
994 | - } |
995 | - case Engine::State::stopped: |
996 | - { |
997 | - parent->playback_status().set(media::Player::stopped); |
998 | - clear_power_state(); |
999 | - break; |
1000 | - } |
1001 | - case Engine::State::paused: |
1002 | - { |
1003 | - parent->playback_status().set(media::Player::paused); |
1004 | - clear_power_state(); |
1005 | - break; |
1006 | - } |
1007 | - default: |
1008 | - break; |
1009 | - }; |
1010 | - }); |
1011 | - |
1012 | + static org::Bluez::MediaPlayer::Dictionary engine_track_meta_data_to_bluez(const Track::MetaData& md) |
1013 | + { |
1014 | + org::Bluez::MediaPlayer::Dictionary dict; |
1015 | + |
1016 | + if (md.count(media::Engine::Xesam::title()) > 0) |
1017 | + dict[org::Bluez::MediaPlayer::Track::MetaData::Title::name] |
1018 | + = core::dbus::types::Variant::encode |
1019 | + < |
1020 | + org::Bluez::MediaPlayer::Track::MetaData::Title::ValueType |
1021 | + >(md.get(media::Engine::Xesam::title())); |
1022 | + |
1023 | + if (md.count(media::Engine::Xesam::artist()) > 0) |
1024 | + dict[org::Bluez::MediaPlayer::Track::MetaData::Artist::name] |
1025 | + = core::dbus::types::Variant::encode |
1026 | + < |
1027 | + org::Bluez::MediaPlayer::Track::MetaData::Artist::ValueType |
1028 | + >(md.get(media::Engine::Xesam::artist())); |
1029 | + |
1030 | + if (md.count(media::Engine::Xesam::album()) > 0) |
1031 | + dict[org::Bluez::MediaPlayer::Track::MetaData::Album::name] |
1032 | + = core::dbus::types::Variant::encode |
1033 | + < |
1034 | + org::Bluez::MediaPlayer::Track::MetaData::Album::ValueType |
1035 | + >(md.get(media::Engine::Xesam::album())); |
1036 | + |
1037 | + if (md.count(media::Engine::Xesam::genre()) > 0) |
1038 | + dict[org::Bluez::MediaPlayer::Track::MetaData::Genre::name] |
1039 | + = core::dbus::types::Variant::encode |
1040 | + < |
1041 | + org::Bluez::MediaPlayer::Track::MetaData::Genre::ValueType |
1042 | + >(md.get(media::Engine::Xesam::genre())); |
1043 | + |
1044 | + if (md.count(media::Engine::Xesam::track_number()) > 0) |
1045 | + dict[org::Bluez::MediaPlayer::Track::MetaData::Number::name] |
1046 | + = core::dbus::types::Variant::encode |
1047 | + < |
1048 | + org::Bluez::MediaPlayer::Track::MetaData::Number::ValueType |
1049 | + >(std::stoi(md.get(media::Engine::Xesam::track_number()))); |
1050 | + |
1051 | + return dict; |
1052 | + } |
1053 | + |
1054 | + Private(const media::PlayerImplementation::Configuration& config, |
1055 | + PlayerImplementation* parent) |
1056 | + : configuration(config), |
1057 | + parent(parent), |
1058 | + bluez_player |
1059 | + { |
1060 | + org::Bluez::MediaPlayer::Configuration |
1061 | + { |
1062 | + // We synchronize the initial player state with bluez |
1063 | + org::Bluez::MediaPlayer::Dictionary |
1064 | + { |
1065 | + // We do not support equalizers, yet |
1066 | + { |
1067 | + std::string{org::Bluez::MediaPlayer::Properties::Equalizer::name}, |
1068 | + core::dbus::types::Variant::encode<std::string> |
1069 | + ( |
1070 | + org::Bluez::MediaPlayer::Properties::Equalizer::off |
1071 | + ) |
1072 | + }, |
1073 | + // And our repeat mode is initially off |
1074 | + { |
1075 | + std::string{org::Bluez::MediaPlayer::Properties::Repeat::name}, |
1076 | + core::dbus::types::Variant::encode<std::string> |
1077 | + ( |
1078 | + org::Bluez::MediaPlayer::Properties::Repeat::off |
1079 | + ) |
1080 | + }, |
1081 | + // Scan mode is initially off |
1082 | + { |
1083 | + std::string{org::Bluez::MediaPlayer::Properties::Scan::name}, |
1084 | + core::dbus::types::Variant::encode<std::string>( |
1085 | + std::string(org::Bluez::MediaPlayer::Properties::Scan::off)) |
1086 | + }, |
1087 | + // And we are in state stopped in the beginning |
1088 | + { |
1089 | + std::string{org::Bluez::MediaPlayer::Properties::Status::name}, |
1090 | + core::dbus::types::Variant::encode<std::string> |
1091 | + ( |
1092 | + org::Bluez::MediaPlayer::Properties::Status::stopped |
1093 | + ) |
1094 | + }, |
1095 | + // For that, we are at position 0 |
1096 | + { |
1097 | + std::string{org::Bluez::MediaPlayer::Properties::Position::name}, |
1098 | + core::dbus::types::Variant::encode<std::uint32_t> |
1099 | + ( |
1100 | + 0 |
1101 | + ) |
1102 | + }, |
1103 | + // And we do not shuffle |
1104 | + { |
1105 | + std::string{org::Bluez::MediaPlayer::Properties::Shuffle::name}, |
1106 | + core::dbus::types::Variant::encode<std::string> |
1107 | + ( |
1108 | + org::Bluez::MediaPlayer::Properties::Shuffle::off |
1109 | + ) |
1110 | + } |
1111 | + }, |
1112 | + // The current track is empty on construction, and with that the set of meta data. |
1113 | + org::Bluez::MediaPlayer::Dictionary{}, |
1114 | + // The bus connection we want to expose the player skeleton upon. |
1115 | + configuration.system_bus, |
1116 | + // Stub for accessing org.bluez.Media |
1117 | + configuration.bluez_media, |
1118 | + // The already existing skeleton to piggy-back upon for exposing the player skeleton. |
1119 | + configuration.bluez, |
1120 | + // By default, we do not handle property updates. |
1121 | + org::Bluez::MediaPlayer::Configuration::an_invocation_handler_returning_error(), |
1122 | + // And we do not handle release messages. |
1123 | + org::Bluez::MediaPlayer::Configuration::an_invocation_handler_returning_error() |
1124 | + } |
1125 | + }, |
1126 | + active_display_on_request(false) |
1127 | + { |
1128 | } |
1129 | |
1130 | void request_power_state() |
1131 | @@ -107,7 +164,11 @@ |
1132 | { |
1133 | if (!active_display_on_request) |
1134 | { |
1135 | - auto result = uscreen_session->invoke_method_synchronously<core::UScreen::keepDisplayOn, int>(); |
1136 | + auto result = configuration.uscreen_session->invoke_method_synchronously |
1137 | + < |
1138 | + core::UScreen::keepDisplayOn, |
1139 | + int |
1140 | + >(); |
1141 | if (result.is_error()) |
1142 | throw std::runtime_error(result.error().print()); |
1143 | |
1144 | @@ -119,7 +180,11 @@ |
1145 | { |
1146 | if (sys_cookie.empty()) |
1147 | { |
1148 | - auto result = powerd_session->invoke_method_synchronously<core::Powerd::requestSysState, std::string>(sys_lock_name, static_cast<int>(1)); |
1149 | + auto result = configuration.powerd_session->invoke_method_synchronously |
1150 | + < |
1151 | + core::Powerd::requestSysState, |
1152 | + std::string |
1153 | + >(std::string{PlayerImplementation::wake_lock_name}, static_cast<int>(1)); |
1154 | if (result.is_error()) |
1155 | throw std::runtime_error(result.error().print()); |
1156 | |
1157 | @@ -142,7 +207,11 @@ |
1158 | { |
1159 | if (active_display_on_request) |
1160 | { |
1161 | - uscreen_session->invoke_method_synchronously<core::UScreen::removeDisplayOnRequest, void>(disp_cookie); |
1162 | + configuration.uscreen_session->invoke_method_synchronously |
1163 | + < |
1164 | + core::UScreen::removeDisplayOnRequest, |
1165 | + void |
1166 | + >(disp_cookie); |
1167 | active_display_on_request = false; |
1168 | } |
1169 | } |
1170 | @@ -150,7 +219,11 @@ |
1171 | { |
1172 | if (!sys_cookie.empty()) |
1173 | { |
1174 | - powerd_session->invoke_method_synchronously<core::Powerd::clearSysState, void>(sys_cookie); |
1175 | + configuration.powerd_session->invoke_method_synchronously |
1176 | + < |
1177 | + core::Powerd::clearSysState, |
1178 | + void |
1179 | + >(sys_cookie); |
1180 | sys_cookie.clear(); |
1181 | } |
1182 | } |
1183 | @@ -162,30 +235,130 @@ |
1184 | } |
1185 | } |
1186 | |
1187 | + void setup_connections_to_engine(const media::Engine& eng) |
1188 | + { |
1189 | + /* TODO(tvoss): Re-enable |
1190 | + eng.about_to_finish_signal().connect([this]() |
1191 | + { |
1192 | + if (configuration.track_list->has_next()) |
1193 | + { |
1194 | + Track::UriType uri = configuration.track_list->query_uri_for_track(configuration.track_list->next()); |
1195 | + if (!uri.empty()) |
1196 | + parent->open_uri(uri); |
1197 | + } |
1198 | + }); |
1199 | + */ |
1200 | + |
1201 | + eng.state().changed().connect( |
1202 | + [this](const Engine::State& state) |
1203 | + { |
1204 | + switch(state) |
1205 | + { |
1206 | + case Engine::State::ready: |
1207 | + { |
1208 | + parent->playback_status().set(media::Player::ready); |
1209 | + clear_power_state(); |
1210 | + break; |
1211 | + } |
1212 | + case Engine::State::playing: |
1213 | + { |
1214 | + parent->playback_status().set(media::Player::playing); |
1215 | + request_power_state(); |
1216 | + break; |
1217 | + } |
1218 | + case Engine::State::stopped: |
1219 | + { |
1220 | + parent->playback_status().set(media::Player::stopped); |
1221 | + clear_power_state(); |
1222 | + break; |
1223 | + } |
1224 | + case Engine::State::paused: |
1225 | + { |
1226 | + parent->playback_status().set(media::Player::paused); |
1227 | + clear_power_state(); |
1228 | + break; |
1229 | + } |
1230 | + default: |
1231 | + break; |
1232 | + }; |
1233 | + }); |
1234 | + |
1235 | + eng.seeked_to_signal().connect([this](uint64_t value) |
1236 | + { |
1237 | + parent->seeked_to()(value); |
1238 | + // Update the bluez player. |
1239 | + bluez_player.set_property<org::Bluez::MediaPlayer::Properties::Position>(value); |
1240 | + }); |
1241 | + |
1242 | + eng.end_of_stream_signal().connect([this]() |
1243 | + { |
1244 | + parent->end_of_stream()(); |
1245 | + }); |
1246 | + |
1247 | + eng.playback_status_changed_signal().connect([this](const Player::PlaybackStatus& status) |
1248 | + { |
1249 | + parent->playback_status_changed()(status); |
1250 | + |
1251 | + // Update the bluez player. |
1252 | + switch(status) |
1253 | + { |
1254 | + case Player::PlaybackStatus::null: |
1255 | + case Player::PlaybackStatus::ready: |
1256 | + case Player::PlaybackStatus::stopped: |
1257 | + bluez_player.set_property<org::Bluez::MediaPlayer::Properties::Status>( |
1258 | + org::Bluez::MediaPlayer::Properties::Status::stopped); |
1259 | + break; |
1260 | + case Player::PlaybackStatus::playing: |
1261 | + { |
1262 | + // We update the current track data in one go. |
1263 | + auto dict = engine_track_meta_data_to_bluez(std::get<1>(configuration.engine->track_meta_data().get())); |
1264 | + // Duration of the track is not reported as meta data but has to be queried explicitly. |
1265 | + dict[org::Bluez::MediaPlayer::Track::MetaData::Duration::name] |
1266 | + = core::dbus::types::Variant::encode |
1267 | + < |
1268 | + org::Bluez::MediaPlayer::Track::MetaData::Duration::ValueType |
1269 | + >(configuration.engine->duration().get()); |
1270 | + // Announce the track to bluez. |
1271 | + bluez_player.set_track(dict); |
1272 | + // And set state to playing. |
1273 | + bluez_player.set_property<org::Bluez::MediaPlayer::Properties::Status>( |
1274 | + org::Bluez::MediaPlayer::Properties::Status::playing); |
1275 | + break; |
1276 | + } |
1277 | + case Player::PlaybackStatus::paused: |
1278 | + bluez_player.set_property<org::Bluez::MediaPlayer::Properties::Status>( |
1279 | + org::Bluez::MediaPlayer::Properties::Status::paused); |
1280 | + break; |
1281 | + } |
1282 | + }); |
1283 | + |
1284 | + /*eng.track_meta_data().changed().connect([this](const std::tuple<Track::UriType, Track::MetaData>& tuple) |
1285 | + { |
1286 | + auto dict = engine_track_meta_data_to_bluez(std::get<1>(tuple)); |
1287 | + dict[org::Bluez::MediaPlayer::Track::MetaData::Duration::name] |
1288 | + = core::dbus::types::Variant::encode |
1289 | + < |
1290 | + org::Bluez::MediaPlayer::Track::MetaData::Duration::ValueType |
1291 | + >(configuration.engine->duration().get()); |
1292 | + |
1293 | + // And finally update the bluez_player instance. |
1294 | + bluez_player.set_track(dict); |
1295 | + });*/ |
1296 | + } |
1297 | + |
1298 | + media::PlayerImplementation::Configuration configuration; |
1299 | PlayerImplementation* parent; |
1300 | - std::shared_ptr<Service> service; |
1301 | - std::shared_ptr<Engine> engine; |
1302 | - dbus::types::ObjectPath session_path; |
1303 | - std::shared_ptr<TrackListImplementation> track_list; |
1304 | - std::shared_ptr<dbus::Object> powerd_session; |
1305 | - std::shared_ptr<dbus::Object> uscreen_session; |
1306 | - std::string sys_lock_name; |
1307 | + // All bluez-specific fields go here. |
1308 | + org::Bluez::MediaPlayer bluez_player; |
1309 | + // All powerd-specific fields go here. |
1310 | int disp_cookie; |
1311 | bool active_display_on_request; |
1312 | std::string sys_cookie; |
1313 | - PlayerImplementation::PlayerKey key; |
1314 | }; |
1315 | |
1316 | -media::PlayerImplementation::PlayerImplementation( |
1317 | - const dbus::types::ObjectPath& session_path, |
1318 | - const std::shared_ptr<Service>& service, |
1319 | - PlayerKey key) |
1320 | - : media::PlayerSkeleton(session_path), |
1321 | - d(new Private( |
1322 | - this, |
1323 | - session_path, |
1324 | - service, |
1325 | - key)) |
1326 | +media::PlayerImplementation::PlayerImplementation(const media::PlayerImplementation::Configuration& config) |
1327 | + : media::PlayerSkeleton(config.skeleton_configuration), |
1328 | + d(new Private(config, this)) |
1329 | { |
1330 | // Initializing default values for properties |
1331 | can_play().set(true); |
1332 | @@ -206,7 +379,7 @@ |
1333 | // every time the client requests position |
1334 | std::function<uint64_t()> position_getter = [this]() |
1335 | { |
1336 | - return d->engine->position().get(); |
1337 | + return d->configuration.engine->position().get(); |
1338 | }; |
1339 | position().install(position_getter); |
1340 | |
1341 | @@ -214,46 +387,23 @@ |
1342 | // every time the client requests duration |
1343 | std::function<uint64_t()> duration_getter = [this]() |
1344 | { |
1345 | - return d->engine->duration().get(); |
1346 | + return d->configuration.engine->duration().get(); |
1347 | }; |
1348 | duration().install(duration_getter); |
1349 | |
1350 | std::function<bool()> video_type_getter = [this]() |
1351 | { |
1352 | - return d->engine->is_video_source().get(); |
1353 | + return d->configuration.engine->is_video_source().get(); |
1354 | }; |
1355 | is_video_source().install(video_type_getter); |
1356 | |
1357 | std::function<bool()> audio_type_getter = [this]() |
1358 | { |
1359 | - return d->engine->is_audio_source().get(); |
1360 | + return d->configuration.engine->is_audio_source().get(); |
1361 | }; |
1362 | is_audio_source().install(audio_type_getter); |
1363 | |
1364 | - d->engine->about_to_finish_signal().connect([this]() |
1365 | - { |
1366 | - if (d->track_list->has_next()) |
1367 | - { |
1368 | - Track::UriType uri = d->track_list->query_uri_for_track(d->track_list->next()); |
1369 | - if (!uri.empty()) |
1370 | - d->parent->open_uri(uri); |
1371 | - } |
1372 | - }); |
1373 | - |
1374 | - d->engine->seeked_to_signal().connect([this](uint64_t value) |
1375 | - { |
1376 | - seeked_to()(value); |
1377 | - }); |
1378 | - |
1379 | - d->engine->end_of_stream_signal().connect([this]() |
1380 | - { |
1381 | - end_of_stream()(); |
1382 | - }); |
1383 | - |
1384 | - d->engine->playback_status_changed_signal().connect([this](const Player::PlaybackStatus& status) |
1385 | - { |
1386 | - playback_status_changed()(status); |
1387 | - }); |
1388 | + d->setup_connections_to_engine(*d->configuration.engine); |
1389 | } |
1390 | |
1391 | media::PlayerImplementation::~PlayerImplementation() |
1392 | @@ -262,23 +412,23 @@ |
1393 | |
1394 | std::shared_ptr<media::TrackList> media::PlayerImplementation::track_list() |
1395 | { |
1396 | - return d->track_list; |
1397 | + return d->configuration.track_list; |
1398 | } |
1399 | |
1400 | // TODO: Convert this to be a property instead of sync call |
1401 | media::Player::PlayerKey media::PlayerImplementation::key() const |
1402 | { |
1403 | - return d->key; |
1404 | + return d->configuration.key; |
1405 | } |
1406 | |
1407 | bool media::PlayerImplementation::open_uri(const Track::UriType& uri) |
1408 | { |
1409 | - return d->engine->open_resource_for_uri(uri); |
1410 | + return d->configuration.engine->open_resource_for_uri(uri); |
1411 | } |
1412 | |
1413 | void media::PlayerImplementation::create_video_sink(uint32_t texture_id) |
1414 | { |
1415 | - d->engine->create_video_sink(texture_id); |
1416 | + d->configuration.engine->create_video_sink(texture_id); |
1417 | } |
1418 | |
1419 | media::Player::GLConsumerWrapperHybris media::PlayerImplementation::gl_consumer() const |
1420 | @@ -297,17 +447,17 @@ |
1421 | |
1422 | void media::PlayerImplementation::play() |
1423 | { |
1424 | - d->engine->play(); |
1425 | + d->configuration.engine->play(); |
1426 | } |
1427 | |
1428 | void media::PlayerImplementation::pause() |
1429 | { |
1430 | - d->engine->pause(); |
1431 | + d->configuration.engine->pause(); |
1432 | } |
1433 | |
1434 | void media::PlayerImplementation::stop() |
1435 | { |
1436 | - d->engine->stop(); |
1437 | + d->configuration.engine->stop(); |
1438 | } |
1439 | |
1440 | void media::PlayerImplementation::set_frame_available_callback( |
1441 | @@ -324,5 +474,5 @@ |
1442 | |
1443 | void media::PlayerImplementation::seek_to(const std::chrono::microseconds& ms) |
1444 | { |
1445 | - d->engine->seek_to(ms); |
1446 | + d->configuration.engine->seek_to(ms); |
1447 | } |
1448 | |
1449 | === modified file 'src/core/media/player_implementation.h' |
1450 | --- src/core/media/player_implementation.h 2014-04-25 17:53:00 +0000 |
1451 | +++ src/core/media/player_implementation.h 2014-07-07 14:32:42 +0000 |
1452 | @@ -35,10 +35,38 @@ |
1453 | class PlayerImplementation : public PlayerSkeleton |
1454 | { |
1455 | public: |
1456 | - PlayerImplementation( |
1457 | - const core::dbus::types::ObjectPath& session_path, |
1458 | - const std::shared_ptr<Service>& service, |
1459 | - PlayerKey key); |
1460 | + // The name of the wake-lock we request from powerd. |
1461 | + static constexpr const char* wake_lock_name |
1462 | + { |
1463 | + "media-hub-music-playback" |
1464 | + }; |
1465 | + |
1466 | + // All creation time options go here. |
1467 | + struct Configuration |
1468 | + { |
1469 | + // The configuration object of the parent class. |
1470 | + PlayerSkeleton::Configuration skeleton_configuration; |
1471 | + // The unique key identifying the instance. |
1472 | + PlayerKey key; |
1473 | + // The engine implementation. |
1474 | + std::shared_ptr<Engine> engine; |
1475 | + // The track list implementation. |
1476 | + std::shared_ptr<TrackList> track_list; |
1477 | + // We have to interact with the system bus. |
1478 | + core::dbus::Bus::Ptr system_bus; |
1479 | + // We have to talk to bluez to wire up the players to bluetooth |
1480 | + core::dbus::Service::Ptr bluez; |
1481 | + // Used to query the default bluetooth adapter. |
1482 | + core::dbus::Object::Ptr bluez_manager; |
1483 | + // Used to announce the player instance. |
1484 | + core::dbus::Object::Ptr bluez_media; |
1485 | + // And we have to talk to powerd to make sure that the display |
1486 | + // stays on if video playback is active. |
1487 | + core::dbus::Object::Ptr powerd_session; |
1488 | + core::dbus::Object::Ptr uscreen_session; |
1489 | + }; |
1490 | + |
1491 | + PlayerImplementation(const Configuration& configuration); |
1492 | ~PlayerImplementation(); |
1493 | |
1494 | virtual std::shared_ptr<TrackList> track_list(); |
1495 | |
1496 | === added file 'src/core/media/player_p.h' |
1497 | --- src/core/media/player_p.h 1970-01-01 00:00:00 +0000 |
1498 | +++ src/core/media/player_p.h 2014-07-07 14:32:42 +0000 |
1499 | @@ -0,0 +1,62 @@ |
1500 | +/* |
1501 | + * Copyright © 2013 Canonical Ltd. |
1502 | + * |
1503 | + * This program is free software: you can redistribute it and/or modify it |
1504 | + * under the terms of the GNU Lesser General Public License version 3, |
1505 | + * as published by the Free Software Foundation. |
1506 | + * |
1507 | + * This program is distributed in the hope that it will be useful, |
1508 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1509 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1510 | + * GNU Lesser General Public License for more details. |
1511 | + * |
1512 | + * You should have received a copy of the GNU Lesser General Public License |
1513 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1514 | + * |
1515 | + * Authored by: Thomas Voß <thomas.voss@canonical.com> |
1516 | + */ |
1517 | + |
1518 | +#ifndef CORE_MEDIA_PLAYER_PRIVATE_H_ |
1519 | +#define CORE_MEDIA_PLAYER_PRIVATE_H_ |
1520 | + |
1521 | +#include "mpris/media_player2.h" |
1522 | +#include "mpris/player.h" |
1523 | + |
1524 | +#include <core/dbus/traits/service.h> |
1525 | + |
1526 | +namespace core |
1527 | +{ |
1528 | +namespace dbus |
1529 | +{ |
1530 | +namespace traits |
1531 | +{ |
1532 | +template<> |
1533 | +struct Service<mpris::Player<core::ubuntu::media::Player>> |
1534 | +{ |
1535 | + inline static const std::string& interface_name() |
1536 | + { |
1537 | + static const std::string s |
1538 | + { |
1539 | + "org.mpris.MediaPlayer2.MediaHub" |
1540 | + }; |
1541 | + return s; |
1542 | + } |
1543 | +}; |
1544 | + |
1545 | +template<> |
1546 | +struct Service<mpris::Player<mpris::MediaPlayer2>> |
1547 | +{ |
1548 | + static std::string interface_name() |
1549 | + { |
1550 | + static const std::string base_service_name{"org.mpris.MediaPlayer2.instance"}; |
1551 | + // Incrementing counter for distinguishing instances. |
1552 | + static std::uint32_t counter{0}; |
1553 | + |
1554 | + return base_service_name + std::to_string(counter++); |
1555 | + } |
1556 | +}; |
1557 | +} |
1558 | +} |
1559 | +} |
1560 | + |
1561 | +#endif // CORE_MEDIA_PLAYER_PRIVATE_H_ |
1562 | |
1563 | === modified file 'src/core/media/player_skeleton.cpp' |
1564 | --- src/core/media/player_skeleton.cpp 2014-05-21 08:25:44 +0000 |
1565 | +++ src/core/media/player_skeleton.cpp 2014-07-07 14:32:42 +0000 |
1566 | @@ -18,350 +18,102 @@ |
1567 | |
1568 | #include "apparmor.h" |
1569 | #include "codec.h" |
1570 | +#include "player_p.h" |
1571 | #include "player_skeleton.h" |
1572 | #include "player_traits.h" |
1573 | #include "property_stub.h" |
1574 | #include "the_session_bus.h" |
1575 | |
1576 | -#include "mpris/player.h" |
1577 | - |
1578 | #include <core/dbus/object.h> |
1579 | #include <core/dbus/property.h> |
1580 | #include <core/dbus/stub.h> |
1581 | #include <core/dbus/asio/executor.h> |
1582 | +#include <core/dbus/traits/service.h> |
1583 | |
1584 | namespace dbus = core::dbus; |
1585 | namespace media = core::ubuntu::media; |
1586 | |
1587 | -struct media::PlayerSkeleton::Private |
1588 | -{ |
1589 | - Private(media::PlayerSkeleton* player, const dbus::types::ObjectPath& session) |
1590 | - : impl(player), |
1591 | - object(impl->access_service()->add_object_for_path(session)), |
1592 | - apparmor_session(nullptr), |
1593 | - properties |
1594 | +namespace core |
1595 | +{ |
1596 | +namespace dbus |
1597 | +{ |
1598 | +namespace traits |
1599 | +{ |
1600 | +template<> struct Service<mpris::MediaPlayer2> |
1601 | +{ |
1602 | + static std::string interface_name() |
1603 | + { |
1604 | + return "org.mpris.MediaPlayer2"; |
1605 | + } |
1606 | +}; |
1607 | +} |
1608 | +} |
1609 | +} |
1610 | + |
1611 | +namespace |
1612 | +{ |
1613 | +std::uint32_t instance_counter{0}; |
1614 | +std::string service_base_name{"org.mpris.MediaPlayer2.umh.instance"}; |
1615 | +} |
1616 | + |
1617 | +media::PlayerSkeleton::PlayerSkeleton(const media::PlayerSkeleton::Configuration& config) |
1618 | + : configuration(config), |
1619 | + client_skeleton |
1620 | { |
1621 | - object->get_property<mpris::Player::Properties::CanPlay>(), |
1622 | - object->get_property<mpris::Player::Properties::CanPause>(), |
1623 | - object->get_property<mpris::Player::Properties::CanSeek>(), |
1624 | - object->get_property<mpris::Player::Properties::CanControl>(), |
1625 | - object->get_property<mpris::Player::Properties::CanGoNext>(), |
1626 | - object->get_property<mpris::Player::Properties::CanGoPrevious>(), |
1627 | - object->get_property<mpris::Player::Properties::IsVideoSource>(), |
1628 | - object->get_property<mpris::Player::Properties::IsAudioSource>(), |
1629 | - object->get_property<mpris::Player::Properties::PlaybackStatus>(), |
1630 | - object->get_property<mpris::Player::Properties::LoopStatus>(), |
1631 | - object->get_property<mpris::Player::Properties::PlaybackRate>(), |
1632 | - object->get_property<mpris::Player::Properties::Shuffle>(), |
1633 | - object->get_property<mpris::Player::Properties::MetaData>(), |
1634 | - object->get_property<mpris::Player::Properties::Volume>(), |
1635 | - object->get_property<mpris::Player::Properties::Position>(), |
1636 | - object->get_property<mpris::Player::Properties::Duration>(), |
1637 | - object->get_property<mpris::Player::Properties::MinimumRate>(), |
1638 | - object->get_property<mpris::Player::Properties::MaximumRate>() |
1639 | + mpris::Player<core::ubuntu::media::Player>::Skeleton::Configuration |
1640 | + { |
1641 | + config.bus, |
1642 | + config.object, |
1643 | + [this](const core::dbus::Message::Ptr& in) { return handle_next(in); }, |
1644 | + [this](const core::dbus::Message::Ptr& in) { return handle_previous(in); }, |
1645 | + [this](const core::dbus::Message::Ptr& in) { return handle_pause(in); }, |
1646 | + [this](const core::dbus::Message::Ptr& in) { return handle_stop(in); }, |
1647 | + [this](const core::dbus::Message::Ptr& in) { return handle_play(in); }, |
1648 | + [this](const core::dbus::Message::Ptr& in) { return handle_seek(in); }, |
1649 | + [this](const core::dbus::Message::Ptr& in) { return handle_create_video_sink(in); }, |
1650 | + [this](const core::dbus::Message::Ptr& in) { return handle_key(in); }, |
1651 | + [this](const core::dbus::Message::Ptr& in) { return handle_open_uri(in); } |
1652 | + } |
1653 | }, |
1654 | - signals |
1655 | + mpris_skeleton |
1656 | { |
1657 | - object->get_signal<mpris::Player::Signals::Seeked>(), |
1658 | - object->get_signal<mpris::Player::Signals::EndOfStream>(), |
1659 | - object->get_signal<mpris::Player::Signals::PlaybackStatusChanged>() |
1660 | + MprisSkeleton::Configuration |
1661 | + { |
1662 | + configuration.bus, |
1663 | + core::dbus::Service::add_service(configuration.bus, service_base_name + std::to_string(instance_counter++)), |
1664 | + [this](const core::dbus::Message::Ptr& in) { return handle_next(in); }, |
1665 | + [this](const core::dbus::Message::Ptr& in) { return handle_previous(in); }, |
1666 | + [this](const core::dbus::Message::Ptr& in) { return handle_pause(in); }, |
1667 | + [this](const core::dbus::Message::Ptr& in) { return handle_stop(in); }, |
1668 | + [this](const core::dbus::Message::Ptr& in) { return handle_play(in); } |
1669 | + } |
1670 | } |
1671 | - { |
1672 | - } |
1673 | - |
1674 | - void handle_next(const core::dbus::Message::Ptr& msg) |
1675 | - { |
1676 | - impl->next(); |
1677 | - auto reply = dbus::Message::make_method_return(msg); |
1678 | - impl->access_bus()->send(reply); |
1679 | - } |
1680 | - |
1681 | - void handle_previous(const core::dbus::Message::Ptr& msg) |
1682 | - { |
1683 | - impl->previous(); |
1684 | - auto reply = dbus::Message::make_method_return(msg); |
1685 | - impl->access_bus()->send(reply); |
1686 | - } |
1687 | - |
1688 | - void handle_pause(const core::dbus::Message::Ptr& msg) |
1689 | - { |
1690 | - impl->pause(); |
1691 | - auto reply = dbus::Message::make_method_return(msg); |
1692 | - impl->access_bus()->send(reply); |
1693 | - } |
1694 | - |
1695 | - void handle_playpause(DBusMessage*) |
1696 | - { |
1697 | - } |
1698 | - |
1699 | - void handle_stop(const core::dbus::Message::Ptr& msg) |
1700 | - { |
1701 | - impl->stop(); |
1702 | - auto reply = dbus::Message::make_method_return(msg); |
1703 | - impl->access_bus()->send(reply); |
1704 | - } |
1705 | - |
1706 | - void handle_play(const core::dbus::Message::Ptr& msg) |
1707 | - { |
1708 | - impl->play(); |
1709 | - auto reply = dbus::Message::make_method_return(msg); |
1710 | - impl->access_bus()->send(reply); |
1711 | - } |
1712 | - |
1713 | - void handle_seek(const core::dbus::Message::Ptr& in) |
1714 | - { |
1715 | - uint64_t ticks; |
1716 | - in->reader() >> ticks; |
1717 | - impl->seek_to(std::chrono::microseconds(ticks)); |
1718 | - |
1719 | - auto reply = dbus::Message::make_method_return(in); |
1720 | - impl->access_bus()->send(reply); |
1721 | - } |
1722 | - |
1723 | - void handle_set_position(const core::dbus::Message::Ptr&) |
1724 | - { |
1725 | - } |
1726 | - |
1727 | - void handle_create_video_sink(const core::dbus::Message::Ptr& in) |
1728 | - { |
1729 | - uint32_t texture_id; |
1730 | - in->reader() >> texture_id; |
1731 | - impl->create_video_sink(texture_id); |
1732 | - |
1733 | - auto reply = dbus::Message::make_method_return(in); |
1734 | - impl->access_bus()->send(reply); |
1735 | - } |
1736 | - |
1737 | - std::string get_client_apparmor_context(const core::dbus::Message::Ptr& msg) |
1738 | - { |
1739 | - auto bus = std::shared_ptr<dbus::Bus>(new dbus::Bus(core::dbus::WellKnownBus::session)); |
1740 | - bus->install_executor(dbus::asio::make_executor(bus)); |
1741 | - |
1742 | - auto stub_service = dbus::Service::use_service(bus, dbus::traits::Service<core::Apparmor>::interface_name()); |
1743 | - apparmor_session = stub_service->object_for_path(dbus::types::ObjectPath("/org/freedesktop/DBus")); |
1744 | - // Get the AppArmor security context for the client |
1745 | - auto result = apparmor_session->invoke_method_synchronously<core::Apparmor::getConnectionAppArmorSecurityContext, std::string>(msg->sender()); |
1746 | - if (result.is_error()) |
1747 | - { |
1748 | - std::cout << "Error getting apparmor profile: " << result.error().print() << std::endl; |
1749 | - return std::string(); |
1750 | - } |
1751 | - |
1752 | - return result.value(); |
1753 | - } |
1754 | - |
1755 | - bool does_client_have_access(const std::string& context, const std::string& uri) |
1756 | - { |
1757 | - if (context.empty() || uri.empty()) |
1758 | - { |
1759 | - std::cout << "Client denied access since context or uri are empty" << std::endl; |
1760 | - return false; |
1761 | - } |
1762 | - |
1763 | - if (context == "unconfined") |
1764 | - { |
1765 | - std::cout << "Client allowed access since it's unconfined" << std::endl; |
1766 | - return true; |
1767 | - } |
1768 | - |
1769 | - size_t pos = context.find_first_of('_'); |
1770 | - if (pos == std::string::npos) |
1771 | - { |
1772 | - std::cout << "Client denied access since it's an invalid apparmor security context" << std::endl; |
1773 | - return false; |
1774 | - } |
1775 | - |
1776 | - const std::string pkgname = context.substr(0, pos); |
1777 | - std::cout << "client pkgname: " << pkgname << std::endl; |
1778 | - std::cout << "uri: " << uri << std::endl; |
1779 | - |
1780 | - // All confined apps can access their own files |
1781 | - if (uri.find(std::string(".local/share/" + pkgname + "/")) != std::string::npos |
1782 | - || uri.find(std::string(".cache/" + pkgname + "/")) != std::string::npos) |
1783 | - { |
1784 | - std::cout << "Client can access content in ~/.local/share/" << pkgname << " or ~/.cache/" << pkgname << std::endl; |
1785 | - return true; |
1786 | - } |
1787 | - else if (uri.find(std::string("opt/click.ubuntu.com/")) != std::string::npos |
1788 | - && uri.find(pkgname) != std::string::npos) |
1789 | - { |
1790 | - std::cout << "Client can access content in own opt directory" << std::endl; |
1791 | - return true; |
1792 | - } |
1793 | - else if ((uri.find(std::string("/system/media/audio/ui/")) != std::string::npos |
1794 | - || uri.find(std::string("/android/system/media/audio/ui/")) != std::string::npos) |
1795 | - && pkgname == "com.ubuntu.camera") |
1796 | - { |
1797 | - std::cout << "Camera app can access ui sounds" << std::endl; |
1798 | - return true; |
1799 | - } |
1800 | - // TODO: Check if the trust store previously allowed direct access to uri |
1801 | - |
1802 | - // Check in ~/Music and ~/Videos |
1803 | - // TODO: when the trust store lands, check it to see if this app can access the dirs and |
1804 | - // then remove the explicit whitelist of the music-app, and gallery-app |
1805 | - else if ((pkgname == "com.ubuntu.music" || pkgname == "com.ubuntu.gallery") && |
1806 | - (uri.find(std::string("Music/")) != std::string::npos |
1807 | - || uri.find(std::string("Videos/")) != std::string::npos)) |
1808 | - { |
1809 | - std::cout << "Client can access content in ~/Music or ~/Videos" << std::endl; |
1810 | - return true; |
1811 | - } |
1812 | - else if (uri.find(std::string("http://")) != std::string::npos |
1813 | - || uri.find(std::string("rtsp://")) != std::string::npos) |
1814 | - { |
1815 | - std::cout << "Client can access streaming content" << std::endl; |
1816 | - return true; |
1817 | - } |
1818 | - else |
1819 | - { |
1820 | - std::cout << "Client denied access to open_uri()" << std::endl; |
1821 | - return false; |
1822 | - } |
1823 | - } |
1824 | - |
1825 | - void handle_key(const core::dbus::Message::Ptr& in) |
1826 | - { |
1827 | - auto reply = dbus::Message::make_method_return(in); |
1828 | - reply->writer() << impl->key(); |
1829 | - impl->access_bus()->send(reply); |
1830 | - } |
1831 | - |
1832 | - void handle_open_uri(const core::dbus::Message::Ptr& in) |
1833 | - { |
1834 | - Track::UriType uri; |
1835 | - in->reader() >> uri; |
1836 | - |
1837 | - std::string context = get_client_apparmor_context(in); |
1838 | - bool have_access = does_client_have_access(context, uri); |
1839 | - |
1840 | - auto reply = dbus::Message::make_method_return(in); |
1841 | - if (have_access) |
1842 | - reply->writer() << impl->open_uri(uri); |
1843 | - else |
1844 | - reply->writer() << false; |
1845 | - impl->access_bus()->send(reply); |
1846 | - } |
1847 | - |
1848 | - media::PlayerSkeleton* impl; |
1849 | - dbus::Object::Ptr object; |
1850 | - dbus::Object::Ptr apparmor_session; |
1851 | - struct |
1852 | - { |
1853 | - std::shared_ptr<core::dbus::Property<mpris::Player::Properties::CanPlay>> can_play; |
1854 | - std::shared_ptr<core::dbus::Property<mpris::Player::Properties::CanPause>> can_pause; |
1855 | - std::shared_ptr<core::dbus::Property<mpris::Player::Properties::CanSeek>> can_seek; |
1856 | - std::shared_ptr<core::dbus::Property<mpris::Player::Properties::CanControl>> can_control; |
1857 | - std::shared_ptr<core::dbus::Property<mpris::Player::Properties::CanGoNext>> can_go_next; |
1858 | - std::shared_ptr<core::dbus::Property<mpris::Player::Properties::CanGoPrevious>> can_go_previous; |
1859 | - std::shared_ptr<core::dbus::Property<mpris::Player::Properties::IsVideoSource>> is_video_source; |
1860 | - std::shared_ptr<core::dbus::Property<mpris::Player::Properties::IsAudioSource>> is_audio_source; |
1861 | - |
1862 | - std::shared_ptr<core::dbus::Property<mpris::Player::Properties::PlaybackStatus>> playback_status; |
1863 | - std::shared_ptr<core::dbus::Property<mpris::Player::Properties::LoopStatus>> loop_status; |
1864 | - std::shared_ptr<core::dbus::Property<mpris::Player::Properties::PlaybackRate>> playback_rate; |
1865 | - std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Shuffle>> is_shuffle; |
1866 | - std::shared_ptr<core::dbus::Property<mpris::Player::Properties::MetaData>> meta_data_for_current_track; |
1867 | - std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Volume>> volume; |
1868 | - std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Position>> position; |
1869 | - std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Duration>> duration; |
1870 | - std::shared_ptr<core::dbus::Property<mpris::Player::Properties::MinimumRate>> minimum_playback_rate; |
1871 | - std::shared_ptr<core::dbus::Property<mpris::Player::Properties::MaximumRate>> maximum_playback_rate; |
1872 | - } properties; |
1873 | - |
1874 | - struct Signals |
1875 | - { |
1876 | - typedef core::dbus::Signal<mpris::Player::Signals::Seeked, mpris::Player::Signals::Seeked::ArgumentType> DBusSeekedToSignal; |
1877 | - typedef core::dbus::Signal<mpris::Player::Signals::EndOfStream, mpris::Player::Signals::EndOfStream::ArgumentType> DBusEndOfStreamSignal; |
1878 | - typedef core::dbus::Signal<mpris::Player::Signals::PlaybackStatusChanged, mpris::Player::Signals::PlaybackStatusChanged::ArgumentType> DBusPlaybackStatusChangedSignal; |
1879 | - |
1880 | - Signals(const std::shared_ptr<DBusSeekedToSignal>& seeked, |
1881 | - const std::shared_ptr<DBusEndOfStreamSignal>& eos, |
1882 | - const std::shared_ptr<DBusPlaybackStatusChangedSignal>& status) |
1883 | - : dbus |
1884 | - { |
1885 | - seeked, |
1886 | - eos, |
1887 | - status |
1888 | - }, |
1889 | - seeked_to(), |
1890 | - end_of_stream(), |
1891 | - playback_status_changed() |
1892 | - { |
1893 | - seeked_to.connect([this](std::uint64_t value) |
1894 | - { |
1895 | - dbus.seeked_to->emit(value); |
1896 | - }); |
1897 | - |
1898 | - end_of_stream.connect([this]() |
1899 | - { |
1900 | - dbus.end_of_stream->emit(); |
1901 | - }); |
1902 | - |
1903 | - playback_status_changed.connect([this](const media::Player::PlaybackStatus& status) |
1904 | - { |
1905 | - dbus.playback_status_changed->emit(status); |
1906 | - }); |
1907 | - } |
1908 | - |
1909 | - struct DBus |
1910 | - { |
1911 | - std::shared_ptr<DBusSeekedToSignal> seeked_to; |
1912 | - std::shared_ptr<DBusEndOfStreamSignal> end_of_stream; |
1913 | - std::shared_ptr<DBusPlaybackStatusChangedSignal> playback_status_changed; |
1914 | - } dbus; |
1915 | - core::Signal<uint64_t> seeked_to; |
1916 | - core::Signal<void> end_of_stream; |
1917 | - core::Signal<media::Player::PlaybackStatus> playback_status_changed; |
1918 | - } signals; |
1919 | - |
1920 | -}; |
1921 | - |
1922 | -media::PlayerSkeleton::PlayerSkeleton( |
1923 | - const core::dbus::types::ObjectPath& session_path) |
1924 | - : dbus::Skeleton<media::Player>(the_session_bus()), |
1925 | - d(new Private{this, session_path}) |
1926 | { |
1927 | - d->object->install_method_handler<mpris::Player::Next>( |
1928 | - std::bind(&Private::handle_next, |
1929 | - std::ref(d), |
1930 | - std::placeholders::_1)); |
1931 | - d->object->install_method_handler<mpris::Player::Previous>( |
1932 | - std::bind(&Private::handle_previous, |
1933 | - std::ref(d), |
1934 | - std::placeholders::_1)); |
1935 | - d->object->install_method_handler<mpris::Player::Pause>( |
1936 | - std::bind(&Private::handle_pause, |
1937 | - std::ref(d), |
1938 | - std::placeholders::_1)); |
1939 | - d->object->install_method_handler<mpris::Player::Stop>( |
1940 | - std::bind(&Private::handle_stop, |
1941 | - std::ref(d), |
1942 | - std::placeholders::_1)); |
1943 | - d->object->install_method_handler<mpris::Player::Play>( |
1944 | - std::bind(&Private::handle_play, |
1945 | - std::ref(d), |
1946 | - std::placeholders::_1)); |
1947 | - d->object->install_method_handler<mpris::Player::Seek>( |
1948 | - std::bind(&Private::handle_seek, |
1949 | - std::ref(d), |
1950 | - std::placeholders::_1)); |
1951 | - d->object->install_method_handler<mpris::Player::SetPosition>( |
1952 | - std::bind(&Private::handle_set_position, |
1953 | - std::ref(d), |
1954 | - std::placeholders::_1)); |
1955 | - d->object->install_method_handler<mpris::Player::CreateVideoSink>( |
1956 | - std::bind(&Private::handle_create_video_sink, |
1957 | - std::ref(d), |
1958 | - std::placeholders::_1)); |
1959 | - d->object->install_method_handler<mpris::Player::Key>( |
1960 | - std::bind(&Private::handle_key, |
1961 | - std::ref(d), |
1962 | - std::placeholders::_1)); |
1963 | - d->object->install_method_handler<mpris::Player::OpenUri>( |
1964 | - std::bind(&Private::handle_open_uri, |
1965 | - std::ref(d), |
1966 | - std::placeholders::_1)); |
1967 | + // Wire up local -> remote signal propagation |
1968 | + signals.seeked_to.connect([this](std::uint64_t value) |
1969 | + { |
1970 | + client_skeleton.signals.seeked_to->emit(value); |
1971 | + }); |
1972 | + |
1973 | + signals.end_of_stream.connect([this]() |
1974 | + { |
1975 | + client_skeleton.signals.end_of_stream->emit(); |
1976 | + }); |
1977 | + |
1978 | + signals.playback_status_changed.connect([this](const media::Player::PlaybackStatus& status) |
1979 | + { |
1980 | + client_skeleton.signals.playback_status_changed->emit(status); |
1981 | + }); |
1982 | + |
1983 | + playback_status().changed().connect([this](core::ubuntu::media::Player::PlaybackStatus status) |
1984 | + { |
1985 | + mpris_skeleton.player.properties.playback_status->set(mpris::Player<mpris::MediaPlayer2>::PlaybackStatus::from(status)); |
1986 | + }); |
1987 | + |
1988 | + position().changed().connect([this](std::uint64_t pos) |
1989 | + { |
1990 | + mpris_skeleton.player.properties.position->set(pos); |
1991 | + }); |
1992 | } |
1993 | |
1994 | media::PlayerSkeleton::~PlayerSkeleton() |
1995 | @@ -370,196 +122,347 @@ |
1996 | |
1997 | const core::Property<bool>& media::PlayerSkeleton::can_play() const |
1998 | { |
1999 | - return *d->properties.can_play; |
2000 | + return *client_skeleton.properties.can_play; |
2001 | } |
2002 | |
2003 | const core::Property<bool>& media::PlayerSkeleton::can_pause() const |
2004 | { |
2005 | - return *d->properties.can_pause; |
2006 | + return *client_skeleton.properties.can_pause; |
2007 | } |
2008 | |
2009 | const core::Property<bool>& media::PlayerSkeleton::can_seek() const |
2010 | { |
2011 | - return *d->properties.can_seek; |
2012 | + return *client_skeleton.properties.can_seek; |
2013 | } |
2014 | |
2015 | const core::Property<bool>& media::PlayerSkeleton::can_go_previous() const |
2016 | { |
2017 | - return *d->properties.can_go_previous; |
2018 | + return *client_skeleton.properties.can_go_previous; |
2019 | } |
2020 | |
2021 | const core::Property<bool>& media::PlayerSkeleton::can_go_next() const |
2022 | { |
2023 | - return *d->properties.can_go_next; |
2024 | + return *client_skeleton.properties.can_go_next; |
2025 | } |
2026 | |
2027 | const core::Property<bool>& media::PlayerSkeleton::is_video_source() const |
2028 | { |
2029 | - return *d->properties.is_video_source; |
2030 | + return *client_skeleton.properties.is_video_source; |
2031 | } |
2032 | |
2033 | const core::Property<bool>& media::PlayerSkeleton::is_audio_source() const |
2034 | { |
2035 | - return *d->properties.is_audio_source; |
2036 | + return *client_skeleton.properties.is_audio_source; |
2037 | } |
2038 | |
2039 | const core::Property<media::Player::PlaybackStatus>& media::PlayerSkeleton::playback_status() const |
2040 | { |
2041 | - return *d->properties.playback_status; |
2042 | + return *client_skeleton.properties.typed_playback_status; |
2043 | } |
2044 | |
2045 | const core::Property<media::Player::LoopStatus>& media::PlayerSkeleton::loop_status() const |
2046 | { |
2047 | - return *d->properties.loop_status; |
2048 | + return *client_skeleton.properties.typed_loop_status; |
2049 | } |
2050 | |
2051 | const core::Property<media::Player::PlaybackRate>& media::PlayerSkeleton::playback_rate() const |
2052 | { |
2053 | - return *d->properties.playback_rate; |
2054 | + return *client_skeleton.properties.playback_rate; |
2055 | } |
2056 | |
2057 | const core::Property<bool>& media::PlayerSkeleton::is_shuffle() const |
2058 | { |
2059 | - return *d->properties.is_shuffle; |
2060 | + return *client_skeleton.properties.is_shuffle; |
2061 | } |
2062 | |
2063 | const core::Property<media::Track::MetaData>& media::PlayerSkeleton::meta_data_for_current_track() const |
2064 | { |
2065 | - return *d->properties.meta_data_for_current_track; |
2066 | + return *client_skeleton.properties.meta_data_for_current_track; |
2067 | } |
2068 | |
2069 | const core::Property<media::Player::Volume>& media::PlayerSkeleton::volume() const |
2070 | { |
2071 | - return *d->properties.volume; |
2072 | + return *client_skeleton.properties.volume; |
2073 | } |
2074 | |
2075 | const core::Property<uint64_t>& media::PlayerSkeleton::position() const |
2076 | { |
2077 | - return *d->properties.position; |
2078 | + return *client_skeleton.properties.position; |
2079 | } |
2080 | |
2081 | const core::Property<uint64_t>& media::PlayerSkeleton::duration() const |
2082 | { |
2083 | - return *d->properties.duration; |
2084 | + return *client_skeleton.properties.duration; |
2085 | } |
2086 | |
2087 | const core::Property<media::Player::PlaybackRate>& media::PlayerSkeleton::minimum_playback_rate() const |
2088 | { |
2089 | - return *d->properties.minimum_playback_rate; |
2090 | + return *client_skeleton.properties.minimum_playback_rate; |
2091 | } |
2092 | |
2093 | const core::Property<media::Player::PlaybackRate>& media::PlayerSkeleton::maximum_playback_rate() const |
2094 | { |
2095 | - return *d->properties.maximum_playback_rate; |
2096 | + return *client_skeleton.properties.maximum_playback_rate; |
2097 | } |
2098 | |
2099 | core::Property<media::Player::LoopStatus>& media::PlayerSkeleton::loop_status() |
2100 | { |
2101 | - return *d->properties.loop_status; |
2102 | + return *client_skeleton.properties.typed_loop_status; |
2103 | } |
2104 | |
2105 | core::Property<media::Player::PlaybackRate>& media::PlayerSkeleton::playback_rate() |
2106 | { |
2107 | - return *d->properties.playback_rate; |
2108 | + return *client_skeleton.properties.playback_rate; |
2109 | } |
2110 | |
2111 | core::Property<bool>& media::PlayerSkeleton::is_shuffle() |
2112 | { |
2113 | - return *d->properties.is_shuffle; |
2114 | + return *client_skeleton.properties.is_shuffle; |
2115 | } |
2116 | |
2117 | core::Property<media::Player::Volume>& media::PlayerSkeleton::volume() |
2118 | { |
2119 | - return *d->properties.volume; |
2120 | + return *client_skeleton.properties.volume; |
2121 | } |
2122 | |
2123 | core::Property<uint64_t>& media::PlayerSkeleton::position() |
2124 | { |
2125 | - return *d->properties.position; |
2126 | + return *client_skeleton.properties.position; |
2127 | } |
2128 | |
2129 | core::Property<uint64_t>& media::PlayerSkeleton::duration() |
2130 | { |
2131 | - return *d->properties.duration; |
2132 | + return *client_skeleton.properties.duration; |
2133 | } |
2134 | |
2135 | core::Property<media::Player::PlaybackStatus>& media::PlayerSkeleton::playback_status() |
2136 | { |
2137 | - return *d->properties.playback_status; |
2138 | + return *client_skeleton.properties.typed_playback_status; |
2139 | } |
2140 | |
2141 | core::Property<bool>& media::PlayerSkeleton::can_play() |
2142 | { |
2143 | - return *d->properties.can_play; |
2144 | + return *client_skeleton.properties.can_play; |
2145 | } |
2146 | |
2147 | core::Property<bool>& media::PlayerSkeleton::can_pause() |
2148 | { |
2149 | - return *d->properties.can_pause; |
2150 | + return *client_skeleton.properties.can_pause; |
2151 | } |
2152 | |
2153 | core::Property<bool>& media::PlayerSkeleton::can_seek() |
2154 | { |
2155 | - return *d->properties.can_seek; |
2156 | + return *client_skeleton.properties.can_seek; |
2157 | } |
2158 | |
2159 | core::Property<bool>& media::PlayerSkeleton::can_go_previous() |
2160 | { |
2161 | - return *d->properties.can_go_previous; |
2162 | + return *client_skeleton.properties.can_go_previous; |
2163 | } |
2164 | |
2165 | core::Property<bool>& media::PlayerSkeleton::can_go_next() |
2166 | { |
2167 | - return *d->properties.can_go_next; |
2168 | + return *client_skeleton.properties.can_go_next; |
2169 | } |
2170 | |
2171 | core::Property<bool>& media::PlayerSkeleton::is_video_source() |
2172 | { |
2173 | - return *d->properties.is_video_source; |
2174 | + return *client_skeleton.properties.is_video_source; |
2175 | } |
2176 | |
2177 | core::Property<bool>& media::PlayerSkeleton::is_audio_source() |
2178 | { |
2179 | - return *d->properties.is_audio_source; |
2180 | + return *client_skeleton.properties.is_audio_source; |
2181 | } |
2182 | |
2183 | - |
2184 | core::Property<media::Track::MetaData>& media::PlayerSkeleton::meta_data_for_current_track() |
2185 | { |
2186 | - return *d->properties.meta_data_for_current_track; |
2187 | + return *client_skeleton.properties.meta_data_for_current_track; |
2188 | } |
2189 | |
2190 | core::Property<media::Player::PlaybackRate>& media::PlayerSkeleton::minimum_playback_rate() |
2191 | { |
2192 | - return *d->properties.minimum_playback_rate; |
2193 | + return *client_skeleton.properties.minimum_playback_rate; |
2194 | } |
2195 | |
2196 | core::Property<media::Player::PlaybackRate>& media::PlayerSkeleton::maximum_playback_rate() |
2197 | { |
2198 | - return *d->properties.maximum_playback_rate; |
2199 | + return *client_skeleton.properties.maximum_playback_rate; |
2200 | } |
2201 | |
2202 | const core::Signal<uint64_t>& media::PlayerSkeleton::seeked_to() const |
2203 | { |
2204 | - return d->signals.seeked_to; |
2205 | + return signals.seeked_to; |
2206 | } |
2207 | |
2208 | core::Signal<uint64_t>& media::PlayerSkeleton::seeked_to() |
2209 | { |
2210 | - return d->signals.seeked_to; |
2211 | + return signals.seeked_to; |
2212 | } |
2213 | |
2214 | const core::Signal<void>& media::PlayerSkeleton::end_of_stream() const |
2215 | { |
2216 | - return d->signals.end_of_stream; |
2217 | + return signals.end_of_stream; |
2218 | } |
2219 | |
2220 | core::Signal<void>& media::PlayerSkeleton::end_of_stream() |
2221 | { |
2222 | - return d->signals.end_of_stream; |
2223 | + return signals.end_of_stream; |
2224 | } |
2225 | |
2226 | core::Signal<media::Player::PlaybackStatus>& media::PlayerSkeleton::playback_status_changed() |
2227 | { |
2228 | - return d->signals.playback_status_changed; |
2229 | + return signals.playback_status_changed; |
2230 | +} |
2231 | + |
2232 | +// Private functions |
2233 | +core::dbus::Message::Ptr media::PlayerSkeleton::handle_next(const core::dbus::Message::Ptr& msg) |
2234 | +{ |
2235 | + next(); |
2236 | + return dbus::Message::make_method_return(msg); |
2237 | +} |
2238 | + |
2239 | +core::dbus::Message::Ptr media::PlayerSkeleton::handle_previous(const core::dbus::Message::Ptr& msg) |
2240 | +{ |
2241 | + previous(); |
2242 | + return dbus::Message::make_method_return(msg); |
2243 | +} |
2244 | + |
2245 | +core::dbus::Message::Ptr media::PlayerSkeleton::handle_pause(const core::dbus::Message::Ptr& msg) |
2246 | +{ |
2247 | + pause(); |
2248 | + return dbus::Message::make_method_return(msg); |
2249 | +} |
2250 | + |
2251 | +core::dbus::Message::Ptr media::PlayerSkeleton::handle_stop(const core::dbus::Message::Ptr& msg) |
2252 | +{ |
2253 | + stop(); |
2254 | + return dbus::Message::make_method_return(msg); |
2255 | +} |
2256 | + |
2257 | +core::dbus::Message::Ptr media::PlayerSkeleton::handle_play(const core::dbus::Message::Ptr& msg) |
2258 | +{ |
2259 | + play(); |
2260 | + return dbus::Message::make_method_return(msg); |
2261 | +} |
2262 | + |
2263 | +core::dbus::Message::Ptr media::PlayerSkeleton::handle_seek(const core::dbus::Message::Ptr& in) |
2264 | +{ |
2265 | + uint64_t ticks; |
2266 | + in->reader() >> ticks; |
2267 | + seek_to(std::chrono::microseconds(ticks)); |
2268 | + |
2269 | + return dbus::Message::make_method_return(in); |
2270 | +} |
2271 | + |
2272 | +core::dbus::Message::Ptr media::PlayerSkeleton::handle_create_video_sink(const core::dbus::Message::Ptr& in) |
2273 | +{ |
2274 | + uint32_t texture_id; |
2275 | + in->reader() >> texture_id; |
2276 | + create_video_sink(texture_id); |
2277 | + |
2278 | + return dbus::Message::make_method_return(in); |
2279 | +} |
2280 | + |
2281 | +core::dbus::Message::Ptr media::PlayerSkeleton::handle_key(const core::dbus::Message::Ptr& in) |
2282 | +{ |
2283 | + auto reply = dbus::Message::make_method_return(in); |
2284 | + reply->writer() << key(); |
2285 | + return reply; |
2286 | +} |
2287 | + |
2288 | +core::dbus::Message::Ptr media::PlayerSkeleton::handle_open_uri(const core::dbus::Message::Ptr& in) |
2289 | +{ |
2290 | + Track::UriType uri; |
2291 | + in->reader() >> uri; |
2292 | + |
2293 | + // Let's first resolve the credentials of the caller. |
2294 | + bool have_access = configuration.is_client_with_credentials_allowed_to_access_uri( |
2295 | + configuration.resolve_incoming_msg_to_client_credentials(in), uri); |
2296 | + |
2297 | + core::dbus::Message::Ptr reply; |
2298 | + |
2299 | + if (have_access) |
2300 | + { |
2301 | + reply = dbus::Message::make_method_return(in); |
2302 | + reply->writer() << open_uri(uri); |
2303 | + } else |
2304 | + { |
2305 | + reply = dbus::Message::make_error(in, "core.ubuntu.media.Error.Permission", "Not allowed to access: " + uri); |
2306 | + } |
2307 | + |
2308 | + return reply; |
2309 | +} |
2310 | + |
2311 | +media::PlayerSkeleton::MprisSkeleton::MprisSkeleton(const media::PlayerSkeleton::MprisSkeleton::Configuration& config) |
2312 | + : configuration(config), |
2313 | + object |
2314 | + { |
2315 | + config.service->add_object_for_path_with_policy( |
2316 | + core::dbus::types::ObjectPath{"/org/mpris/MediaPlayer2"}, |
2317 | + core::dbus::immediate_announcement_property_changed_policy()) |
2318 | + }, |
2319 | + media_player2 |
2320 | + { |
2321 | + mpris::MediaPlayer2::Skeleton::Configuration |
2322 | + { |
2323 | + configuration.bus, |
2324 | + object, |
2325 | + // We return errors for raise requests |
2326 | + mpris::MediaPlayer2::Skeleton::Configuration::a_handler_returning_error(), |
2327 | + // We return errors for quit requests |
2328 | + mpris::MediaPlayer2::Skeleton::Configuration::a_handler_returning_error() |
2329 | + } |
2330 | + }, |
2331 | + player |
2332 | + { |
2333 | + mpris::Player<mpris::MediaPlayer2>::Skeleton::Configuration |
2334 | + { |
2335 | + configuration.bus, |
2336 | + object, |
2337 | + config.handle_next, |
2338 | + config.handle_previous, |
2339 | + config.handle_pause, |
2340 | + config.handle_stop, |
2341 | + config.handle_play, |
2342 | + // We return errors for seek requests |
2343 | + mpris::MediaPlayer2::Skeleton::Configuration::a_handler_returning_error(), |
2344 | + // We return errors for create video sink requests |
2345 | + mpris::MediaPlayer2::Skeleton::Configuration::a_handler_returning_error(), |
2346 | + // We return errors for key generation requests |
2347 | + mpris::MediaPlayer2::Skeleton::Configuration::a_handler_returning_error(), |
2348 | + // We return erros for open uri requests |
2349 | + mpris::MediaPlayer2::Skeleton::Configuration::a_handler_returning_error(), |
2350 | + } |
2351 | + } |
2352 | +{ |
2353 | + // Report general capabilities here. |
2354 | + media_player2.properties.can_quit->set(false); |
2355 | + media_player2.properties.can_raise->set(false); |
2356 | + media_player2.properties.can_set_fullscreen->set(false); |
2357 | + media_player2.properties.has_track_list->set(true); |
2358 | + |
2359 | + // And the rest of the properties are initialized here. |
2360 | + media_player2.properties.identity->set("Ubuntu Media Hub"); |
2361 | + // Just for testing purposes. |
2362 | + media_player2.properties.desktop_entry->set("mediaplayer-app"); |
2363 | + // We report an empty list of mimetypes. |
2364 | + // TODO(tvoss): We should query this information from the underlying engine. |
2365 | + media_player2.properties.supported_mime_types->set({""}); |
2366 | + // And an empty list of supported uri schemes |
2367 | + media_player2.properties.supported_uri_schemes->set({""}); |
2368 | + |
2369 | + // Initialize all the capabilities of the player instance. |
2370 | + player.properties.can_play->set(true); |
2371 | + player.properties.can_pause->set(true); |
2372 | + player.properties.can_seek->set(true); |
2373 | + player.properties.can_control->set(true); |
2374 | + player.properties.can_go_next->set(true); |
2375 | + player.properties.can_go_previous->set(true); |
2376 | + |
2377 | + player.properties.playback_status->set(mpris::Player<mpris::MediaPlayer2>::PlaybackStatus::stopped); |
2378 | + player.properties.loop_status->set(mpris::Player<mpris::MediaPlayer2>::LoopStatus::none); |
2379 | + player.properties.playback_rate->set(1.f); |
2380 | + player.properties.is_shuffle->set(false); |
2381 | + |
2382 | } |
2383 | |
2384 | === modified file 'src/core/media/player_skeleton.h' |
2385 | --- src/core/media/player_skeleton.h 2014-04-25 17:53:00 +0000 |
2386 | +++ src/core/media/player_skeleton.h 2014-07-07 14:32:42 +0000 |
2387 | @@ -23,12 +23,16 @@ |
2388 | |
2389 | #include "player_traits.h" |
2390 | |
2391 | +#include "mpris/media_player2.h" |
2392 | #include "mpris/player.h" |
2393 | |
2394 | +#include <core/dbus/object.h> |
2395 | #include <core/dbus/skeleton.h> |
2396 | #include <core/dbus/types/object_path.h> |
2397 | |
2398 | +#include <functional> |
2399 | #include <memory> |
2400 | +#include <tuple> |
2401 | |
2402 | namespace core |
2403 | { |
2404 | @@ -38,11 +42,34 @@ |
2405 | { |
2406 | class Service; |
2407 | |
2408 | -class PlayerSkeleton : public core::dbus::Skeleton<core::ubuntu::media::Player> |
2409 | +class PlayerSkeleton : public core::ubuntu::media::Player |
2410 | { |
2411 | - public: |
2412 | +public: |
2413 | + // Functor resolving dbus message and its sender to |
2414 | + // process id, user id and apparmor confinement profile. |
2415 | + typedef std::function<std::tuple<pid_t, uid_t, std::string>(const core::dbus::Message::Ptr&)> CredentialsResolver; |
2416 | + // Functor returning false if the client corresponding to the credentials tuple |
2417 | + // is allowed to access the uri given as argument. |
2418 | + typedef std::function<bool(const std::tuple<pid_t, uid_t, std::string>&, const std::string&)> PermissionManager; |
2419 | + |
2420 | + // Creation time options being passed to c'tor go here. |
2421 | + struct Configuration |
2422 | + { |
2423 | + // The outgoing bus connection for querying other services. |
2424 | + core::dbus::Bus::Ptr bus; |
2425 | + // Endpoint for mapping this instance to the bus. |
2426 | + core::dbus::Object::Ptr object; |
2427 | + |
2428 | + // The functor called by the skeleton to resolve credentials. |
2429 | + CredentialsResolver resolve_incoming_msg_to_client_credentials; |
2430 | + // The functor called by the skeleton to verify if a client is allowed to access a uri. |
2431 | + PermissionManager is_client_with_credentials_allowed_to_access_uri; |
2432 | + }; |
2433 | + |
2434 | + PlayerSkeleton(const Configuration& configuration); |
2435 | ~PlayerSkeleton(); |
2436 | |
2437 | + // From com::ubuntu::media::Player |
2438 | virtual const core::Property<bool>& can_play() const; |
2439 | virtual const core::Property<bool>& can_pause() const; |
2440 | virtual const core::Property<bool>& can_seek() const; |
2441 | @@ -70,8 +97,7 @@ |
2442 | virtual const core::Signal<void>& end_of_stream() const; |
2443 | virtual core::Signal<PlaybackStatus>& playback_status_changed(); |
2444 | |
2445 | - protected: |
2446 | - PlayerSkeleton(const core::dbus::types::ObjectPath& session_path); |
2447 | +protected: |
2448 | |
2449 | virtual core::Property<PlaybackStatus>& playback_status(); |
2450 | virtual core::Property<bool>& can_play(); |
2451 | @@ -90,9 +116,88 @@ |
2452 | virtual core::Signal<uint64_t>& seeked_to(); |
2453 | virtual core::Signal<void>& end_of_stream(); |
2454 | |
2455 | - private: |
2456 | - struct Private; |
2457 | - std::unique_ptr<Private> d; |
2458 | +private: |
2459 | + // Handles incoming calls for next. Never throws. |
2460 | + core::dbus::Message::Ptr handle_next(const core::dbus::Message::Ptr& msg); |
2461 | + |
2462 | + // Handles incoming calls for previous. Never throws. |
2463 | + core::dbus::Message::Ptr handle_previous(const core::dbus::Message::Ptr& msg); |
2464 | + |
2465 | + // Handles incoming calls for pause. Never throws. |
2466 | + core::dbus::Message::Ptr handle_pause(const core::dbus::Message::Ptr& msg); |
2467 | + |
2468 | + // Handles incoming calls for stpo. Never throws. |
2469 | + core::dbus::Message::Ptr handle_stop(const core::dbus::Message::Ptr& msg); |
2470 | + |
2471 | + // Handles incoming calls for play. Never throws. |
2472 | + core::dbus::Message::Ptr handle_play(const core::dbus::Message::Ptr& msg); |
2473 | + |
2474 | + // Handles incoming calls for seek. Never throws. |
2475 | + core::dbus::Message::Ptr handle_seek(const core::dbus::Message::Ptr& in); |
2476 | + |
2477 | + // Handles incoming calls for video sink creation. Never throws. |
2478 | + core::dbus::Message::Ptr handle_create_video_sink(const core::dbus::Message::Ptr& in); |
2479 | + |
2480 | + // Handles incoming calls for key generation. Never throws. |
2481 | + core::dbus::Message::Ptr handle_key(const core::dbus::Message::Ptr& in); |
2482 | + |
2483 | + // Handles incoming calls for opening a URI. Never throws. |
2484 | + core::dbus::Message::Ptr handle_open_uri(const core::dbus::Message::Ptr& in); |
2485 | + |
2486 | + // We store all creation time properties; |
2487 | + Configuration configuration; |
2488 | + |
2489 | + // Our client-facing org.mpris.MediaPlayer2.Player skeleton, just piggy-backing |
2490 | + // on the MPRIS-interface spec without adhering to the announcement spec that says: |
2491 | + // |
2492 | + // The media player must expose the /org/mpris/MediaPlayer2 object path, which must |
2493 | + // implement the following interfaces: |
2494 | + // - org.mpris.MediaPlayer2 |
2495 | + // - org.mpris.MediaPlayer2.Player |
2496 | + mpris::Player<core::ubuntu::media::Player>::Skeleton client_skeleton; |
2497 | + |
2498 | + // And we have an actual mpris-implementation owning a bus name; |
2499 | + struct MprisSkeleton |
2500 | + { |
2501 | + // All creation time options go here. |
2502 | + struct Configuration |
2503 | + { |
2504 | + // Bus connection for sending replies. |
2505 | + core::dbus::Bus::Ptr bus; |
2506 | + // Service instance to add objects to. |
2507 | + core::dbus::Service::Ptr service; |
2508 | + // Invocation handlers for all the functions we support for MPRIS. |
2509 | + // Please note that only a subset is covered here as we just |
2510 | + // error out for the rest of them |
2511 | + mpris::Player<mpris::MediaPlayer2>::Skeleton::Configuration::InvocationHandler |
2512 | + // Handles incoming next calls |
2513 | + handle_next, |
2514 | + // Handles incoming previous calls |
2515 | + handle_previous, |
2516 | + // Handles incoming pause calls |
2517 | + handle_pause, |
2518 | + // Handles incoming stop calls |
2519 | + handle_stop, |
2520 | + // Handles incoming play calls |
2521 | + handle_play; |
2522 | + }; |
2523 | + |
2524 | + MprisSkeleton(const Configuration& config); |
2525 | + |
2526 | + Configuration configuration; |
2527 | + core::dbus::Object::Ptr object; |
2528 | + mpris::MediaPlayer2::Skeleton media_player2; |
2529 | + mpris::Player<mpris::MediaPlayer2>::Skeleton player; |
2530 | + } mpris_skeleton; |
2531 | + |
2532 | + // Maps between local signals and those exposed on the bus. |
2533 | + struct |
2534 | + { |
2535 | + // And these are the local ones. |
2536 | + core::Signal<uint64_t> seeked_to; |
2537 | + core::Signal<void> end_of_stream; |
2538 | + core::Signal<media::Player::PlaybackStatus> playback_status_changed; |
2539 | + } signals; |
2540 | }; |
2541 | } |
2542 | } |
2543 | |
2544 | === modified file 'src/core/media/player_stub.cpp' |
2545 | --- src/core/media/player_stub.cpp 2014-04-28 21:10:03 +0000 |
2546 | +++ src/core/media/player_stub.cpp 2014-07-07 14:32:42 +0000 |
2547 | @@ -20,6 +20,7 @@ |
2548 | #include <core/media/track_list.h> |
2549 | |
2550 | #include "codec.h" |
2551 | +#include "player_p.h" |
2552 | #include "player_stub.h" |
2553 | #include "player_traits.h" |
2554 | #include "property_stub.h" |
2555 | @@ -44,45 +45,41 @@ |
2556 | |
2557 | struct media::PlayerStub::Private |
2558 | { |
2559 | - Private(const std::shared_ptr<Service>& parent, |
2560 | - const std::shared_ptr<dbus::Service>& remote, |
2561 | - const dbus::types::ObjectPath& path |
2562 | - ) : parent(parent), |
2563 | - texture_id(0), |
2564 | - igbc_wrapper(nullptr), |
2565 | - glc_wrapper(nullptr), |
2566 | - decoding_session(decoding_service_create_session()), |
2567 | - frame_available_cb(nullptr), |
2568 | - frame_available_context(nullptr), |
2569 | - path(path), |
2570 | - object(remote->object_for_path(path)), |
2571 | - properties |
2572 | - { |
2573 | - object->get_property<mpris::Player::Properties::CanPlay>(), |
2574 | - object->get_property<mpris::Player::Properties::CanPause>(), |
2575 | - object->get_property<mpris::Player::Properties::CanSeek>(), |
2576 | - object->get_property<mpris::Player::Properties::CanControl>(), |
2577 | - object->get_property<mpris::Player::Properties::CanGoNext>(), |
2578 | - object->get_property<mpris::Player::Properties::CanGoPrevious>(), |
2579 | - object->get_property<mpris::Player::Properties::IsVideoSource>(), |
2580 | - object->get_property<mpris::Player::Properties::IsAudioSource>(), |
2581 | - object->get_property<mpris::Player::Properties::PlaybackStatus>(), |
2582 | - object->get_property<mpris::Player::Properties::LoopStatus>(), |
2583 | - object->get_property<mpris::Player::Properties::PlaybackRate>(), |
2584 | - object->get_property<mpris::Player::Properties::Shuffle>(), |
2585 | - object->get_property<mpris::Player::Properties::MetaData>(), |
2586 | - object->get_property<mpris::Player::Properties::Volume>(), |
2587 | - object->get_property<mpris::Player::Properties::Position>(), |
2588 | - object->get_property<mpris::Player::Properties::Duration>(), |
2589 | - object->get_property<mpris::Player::Properties::MinimumRate>(), |
2590 | - object->get_property<mpris::Player::Properties::MaximumRate>() |
2591 | - }, |
2592 | - signals |
2593 | - { |
2594 | - object->get_signal<mpris::Player::Signals::Seeked>(), |
2595 | - object->get_signal<mpris::Player::Signals::EndOfStream>(), |
2596 | - object->get_signal<mpris::Player::Signals::PlaybackStatusChanged>() |
2597 | - } |
2598 | + Private(const media::PlayerStub::Configuration& configuration) |
2599 | + : configuration(configuration), |
2600 | + texture_id(0), |
2601 | + igbc_wrapper(nullptr), |
2602 | + glc_wrapper(nullptr), |
2603 | + decoding_session(decoding_service_create_session()), |
2604 | + frame_available_cb(nullptr), |
2605 | + frame_available_context(nullptr), |
2606 | + properties |
2607 | + { |
2608 | + configuration.object->get_property<mpris::Player<core::ubuntu::media::Player>::Properties::CanPlay>(), |
2609 | + configuration.object->get_property<mpris::Player<core::ubuntu::media::Player>::Properties::CanPause>(), |
2610 | + configuration.object->get_property<mpris::Player<core::ubuntu::media::Player>::Properties::CanSeek>(), |
2611 | + configuration.object->get_property<mpris::Player<core::ubuntu::media::Player>::Properties::CanControl>(), |
2612 | + configuration.object->get_property<mpris::Player<core::ubuntu::media::Player>::Properties::CanGoNext>(), |
2613 | + configuration.object->get_property<mpris::Player<core::ubuntu::media::Player>::Properties::CanGoPrevious>(), |
2614 | + configuration.object->get_property<mpris::Player<core::ubuntu::media::Player>::Properties::IsVideoSource>(), |
2615 | + configuration.object->get_property<mpris::Player<core::ubuntu::media::Player>::Properties::IsAudioSource>(), |
2616 | + configuration.object->get_property<mpris::Player<core::ubuntu::media::Player>::Properties::TypedPlaybackStatus>(), |
2617 | + configuration.object->get_property<mpris::Player<core::ubuntu::media::Player>::Properties::TypedLoopStatus>(), |
2618 | + configuration.object->get_property<mpris::Player<core::ubuntu::media::Player>::Properties::PlaybackRate>(), |
2619 | + configuration.object->get_property<mpris::Player<core::ubuntu::media::Player>::Properties::Shuffle>(), |
2620 | + configuration.object->get_property<mpris::Player<core::ubuntu::media::Player>::Properties::TypedMetaData>(), |
2621 | + configuration.object->get_property<mpris::Player<core::ubuntu::media::Player>::Properties::Volume>(), |
2622 | + configuration.object->get_property<mpris::Player<core::ubuntu::media::Player>::Properties::Position>(), |
2623 | + configuration.object->get_property<mpris::Player<core::ubuntu::media::Player>::Properties::Duration>(), |
2624 | + configuration.object->get_property<mpris::Player<core::ubuntu::media::Player>::Properties::MinimumRate>(), |
2625 | + configuration.object->get_property<mpris::Player<core::ubuntu::media::Player>::Properties::MaximumRate>() |
2626 | + }, |
2627 | + signals |
2628 | + { |
2629 | + configuration.object->get_signal<mpris::Player<core::ubuntu::media::Player>::Signals::Seeked>(), |
2630 | + configuration.object->get_signal<mpris::Player<core::ubuntu::media::Player>::Signals::EndOfStream>(), |
2631 | + configuration.object->get_signal<mpris::Player<core::ubuntu::media::Player>::Signals::PlaybackStatusChanged>() |
2632 | + } |
2633 | { |
2634 | } |
2635 | |
2636 | @@ -126,8 +123,7 @@ |
2637 | |
2638 | } |
2639 | |
2640 | - std::shared_ptr<Service> parent; |
2641 | - std::shared_ptr<TrackList> track_list; |
2642 | + media::PlayerStub::Configuration configuration; |
2643 | |
2644 | uint32_t texture_id; |
2645 | IGBCWrapperHybris igbc_wrapper; |
2646 | @@ -138,38 +134,34 @@ |
2647 | FrameAvailableCb frame_available_cb; |
2648 | void *frame_available_context; |
2649 | |
2650 | - dbus::Bus::Ptr bus; |
2651 | - dbus::types::ObjectPath path; |
2652 | - dbus::Object::Ptr object; |
2653 | - |
2654 | struct |
2655 | { |
2656 | - std::shared_ptr<core::dbus::Property<mpris::Player::Properties::CanPlay>> can_play; |
2657 | - std::shared_ptr<core::dbus::Property<mpris::Player::Properties::CanPause>> can_pause; |
2658 | - std::shared_ptr<core::dbus::Property<mpris::Player::Properties::CanSeek>> can_seek; |
2659 | - std::shared_ptr<core::dbus::Property<mpris::Player::Properties::CanControl>> can_control; |
2660 | - std::shared_ptr<core::dbus::Property<mpris::Player::Properties::CanGoNext>> can_go_next; |
2661 | - std::shared_ptr<core::dbus::Property<mpris::Player::Properties::CanGoPrevious>> can_go_previous; |
2662 | - std::shared_ptr<core::dbus::Property<mpris::Player::Properties::IsVideoSource>> is_video_source; |
2663 | - std::shared_ptr<core::dbus::Property<mpris::Player::Properties::IsAudioSource>> is_audio_source; |
2664 | + std::shared_ptr<core::dbus::Property<mpris::Player<core::ubuntu::media::Player>::Properties::CanPlay>> can_play; |
2665 | + std::shared_ptr<core::dbus::Property<mpris::Player<core::ubuntu::media::Player>::Properties::CanPause>> can_pause; |
2666 | + std::shared_ptr<core::dbus::Property<mpris::Player<core::ubuntu::media::Player>::Properties::CanSeek>> can_seek; |
2667 | + std::shared_ptr<core::dbus::Property<mpris::Player<core::ubuntu::media::Player>::Properties::CanControl>> can_control; |
2668 | + std::shared_ptr<core::dbus::Property<mpris::Player<core::ubuntu::media::Player>::Properties::CanGoNext>> can_go_next; |
2669 | + std::shared_ptr<core::dbus::Property<mpris::Player<core::ubuntu::media::Player>::Properties::CanGoPrevious>> can_go_previous; |
2670 | + std::shared_ptr<core::dbus::Property<mpris::Player<core::ubuntu::media::Player>::Properties::IsVideoSource>> is_video_source; |
2671 | + std::shared_ptr<core::dbus::Property<mpris::Player<core::ubuntu::media::Player>::Properties::IsAudioSource>> is_audio_source; |
2672 | |
2673 | - std::shared_ptr<core::dbus::Property<mpris::Player::Properties::PlaybackStatus>> playback_status; |
2674 | - std::shared_ptr<core::dbus::Property<mpris::Player::Properties::LoopStatus>> loop_status; |
2675 | - std::shared_ptr<core::dbus::Property<mpris::Player::Properties::PlaybackRate>> playback_rate; |
2676 | - std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Shuffle>> is_shuffle; |
2677 | - std::shared_ptr<core::dbus::Property<mpris::Player::Properties::MetaData>> meta_data_for_current_track; |
2678 | - std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Volume>> volume; |
2679 | - std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Position>> position; |
2680 | - std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Duration>> duration; |
2681 | - std::shared_ptr<core::dbus::Property<mpris::Player::Properties::MinimumRate>> minimum_playback_rate; |
2682 | - std::shared_ptr<core::dbus::Property<mpris::Player::Properties::MaximumRate>> maximum_playback_rate; |
2683 | + std::shared_ptr<core::dbus::Property<mpris::Player<core::ubuntu::media::Player>::Properties::TypedPlaybackStatus>> playback_status; |
2684 | + std::shared_ptr<core::dbus::Property<mpris::Player<core::ubuntu::media::Player>::Properties::TypedLoopStatus>> loop_status; |
2685 | + std::shared_ptr<core::dbus::Property<mpris::Player<core::ubuntu::media::Player>::Properties::PlaybackRate>> playback_rate; |
2686 | + std::shared_ptr<core::dbus::Property<mpris::Player<core::ubuntu::media::Player>::Properties::Shuffle>> is_shuffle; |
2687 | + std::shared_ptr<core::dbus::Property<mpris::Player<core::ubuntu::media::Player>::Properties::TypedMetaData>> meta_data_for_current_track; |
2688 | + std::shared_ptr<core::dbus::Property<mpris::Player<core::ubuntu::media::Player>::Properties::Volume>> volume; |
2689 | + std::shared_ptr<core::dbus::Property<mpris::Player<core::ubuntu::media::Player>::Properties::Position>> position; |
2690 | + std::shared_ptr<core::dbus::Property<mpris::Player<core::ubuntu::media::Player>::Properties::Duration>> duration; |
2691 | + std::shared_ptr<core::dbus::Property<mpris::Player<core::ubuntu::media::Player>::Properties::MinimumRate>> minimum_playback_rate; |
2692 | + std::shared_ptr<core::dbus::Property<mpris::Player<core::ubuntu::media::Player>::Properties::MaximumRate>> maximum_playback_rate; |
2693 | } properties; |
2694 | |
2695 | struct Signals |
2696 | { |
2697 | - typedef core::dbus::Signal<mpris::Player::Signals::Seeked, mpris::Player::Signals::Seeked::ArgumentType> DBusSeekedToSignal; |
2698 | - typedef core::dbus::Signal<mpris::Player::Signals::EndOfStream, mpris::Player::Signals::EndOfStream::ArgumentType> DBusEndOfStreamSignal; |
2699 | - typedef core::dbus::Signal<mpris::Player::Signals::PlaybackStatusChanged, mpris::Player::Signals::PlaybackStatusChanged::ArgumentType> DBusPlaybackStatusChangedSignal; |
2700 | + typedef core::dbus::Signal<mpris::Player<core::ubuntu::media::Player>::Signals::Seeked, mpris::Player<core::ubuntu::media::Player>::Signals::Seeked::ArgumentType> DBusSeekedToSignal; |
2701 | + typedef core::dbus::Signal<mpris::Player<core::ubuntu::media::Player>::Signals::EndOfStream, mpris::Player<core::ubuntu::media::Player>::Signals::EndOfStream::ArgumentType> DBusEndOfStreamSignal; |
2702 | + typedef core::dbus::Signal<mpris::Player<core::ubuntu::media::Player>::Signals::PlaybackStatusChanged, mpris::Player<core::ubuntu::media::Player>::Signals::PlaybackStatusChanged::ArgumentType> DBusPlaybackStatusChangedSignal; |
2703 | |
2704 | Signals(const std::shared_ptr<DBusSeekedToSignal>& seeked, |
2705 | const std::shared_ptr<DBusEndOfStreamSignal>& eos, |
2706 | @@ -228,56 +220,50 @@ |
2707 | } signals; |
2708 | }; |
2709 | |
2710 | -media::PlayerStub::PlayerStub( |
2711 | - const std::shared_ptr<Service>& parent, |
2712 | - const dbus::types::ObjectPath& object_path) |
2713 | - : dbus::Stub<Player>(the_session_bus()), |
2714 | - d(new Private{parent, access_service(), object_path}) |
2715 | +media::PlayerStub::PlayerStub(const media::PlayerStub::Configuration& config) |
2716 | + : d(new Private{config}) |
2717 | { |
2718 | - auto bus = the_session_bus(); |
2719 | - worker = std::move(std::thread([bus]() |
2720 | - { |
2721 | - bus->run(); |
2722 | - })); |
2723 | } |
2724 | |
2725 | media::PlayerStub::~PlayerStub() |
2726 | { |
2727 | - auto bus = the_session_bus(); |
2728 | - bus->stop(); |
2729 | - |
2730 | - if (worker.joinable()) |
2731 | - worker.join(); |
2732 | } |
2733 | |
2734 | std::shared_ptr<media::TrackList> media::PlayerStub::track_list() |
2735 | { |
2736 | - if (!d->track_list) |
2737 | - { |
2738 | - d->track_list = std::make_shared<media::TrackListStub>( |
2739 | - shared_from_this(), |
2740 | - dbus::types::ObjectPath(d->path.as_string() + "/TrackList")); |
2741 | - } |
2742 | - return d->track_list; |
2743 | + return d->configuration.track_list; |
2744 | } |
2745 | |
2746 | media::Player::PlayerKey media::PlayerStub::key() const |
2747 | { |
2748 | - auto op = d->object->invoke_method_synchronously<mpris::Player::Key, media::Player::PlayerKey>(); |
2749 | + auto op = d->configuration.object->invoke_method_synchronously |
2750 | + < |
2751 | + mpris::Player<core::ubuntu::media::Player>::Key, |
2752 | + media::Player::PlayerKey |
2753 | + >(); |
2754 | |
2755 | return op.value(); |
2756 | } |
2757 | |
2758 | bool media::PlayerStub::open_uri(const media::Track::UriType& uri) |
2759 | { |
2760 | - auto op = d->object->invoke_method_synchronously<mpris::Player::OpenUri, bool>(uri); |
2761 | + auto op = d->configuration.object->invoke_method_synchronously |
2762 | + < |
2763 | + mpris::Player<core::ubuntu::media::Player>::OpenUri, |
2764 | + bool |
2765 | + >(uri); |
2766 | |
2767 | return op.value(); |
2768 | } |
2769 | |
2770 | void media::PlayerStub::create_video_sink(uint32_t texture_id) |
2771 | { |
2772 | - auto op = d->object->invoke_method_synchronously<mpris::Player::CreateVideoSink, void>(texture_id); |
2773 | + auto op = d->configuration.object->invoke_method_synchronously |
2774 | + < |
2775 | + mpris::Player<core::ubuntu::media::Player>::CreateVideoSink, |
2776 | + void |
2777 | + >(texture_id); |
2778 | + |
2779 | d->texture_id = texture_id; |
2780 | d->get_gl_consumer(); |
2781 | |
2782 | @@ -292,7 +278,11 @@ |
2783 | |
2784 | void media::PlayerStub::next() |
2785 | { |
2786 | - auto op = d->object->invoke_method_synchronously<mpris::Player::Next, void>(); |
2787 | + auto op = d->configuration.object->invoke_method_synchronously |
2788 | + < |
2789 | + mpris::Player<core::ubuntu::media::Player>::Next, |
2790 | + void |
2791 | + >(); |
2792 | |
2793 | if (op.is_error()) |
2794 | throw std::runtime_error("Problem switching to next track on remote object"); |
2795 | @@ -300,7 +290,11 @@ |
2796 | |
2797 | void media::PlayerStub::previous() |
2798 | { |
2799 | - auto op = d->object->invoke_method_synchronously<mpris::Player::Previous, void>(); |
2800 | + auto op = d->configuration.object->invoke_method_synchronously |
2801 | + < |
2802 | + mpris::Player<core::ubuntu::media::Player>::Previous, |
2803 | + void |
2804 | + >(); |
2805 | |
2806 | if (op.is_error()) |
2807 | throw std::runtime_error("Problem switching to previous track on remote object"); |
2808 | @@ -308,7 +302,11 @@ |
2809 | |
2810 | void media::PlayerStub::play() |
2811 | { |
2812 | - auto op = d->object->invoke_method_synchronously<mpris::Player::Play, void>(); |
2813 | + auto op = d->configuration.object->invoke_method_synchronously |
2814 | + < |
2815 | + mpris::Player<core::ubuntu::media::Player>::Play, |
2816 | + void |
2817 | + >(); |
2818 | |
2819 | if (op.is_error()) |
2820 | throw std::runtime_error("Problem starting playback on remote object"); |
2821 | @@ -316,7 +314,11 @@ |
2822 | |
2823 | void media::PlayerStub::pause() |
2824 | { |
2825 | - auto op = d->object->invoke_method_synchronously<mpris::Player::Pause, void>(); |
2826 | + auto op = d->configuration.object->invoke_method_synchronously |
2827 | + < |
2828 | + mpris::Player<core::ubuntu::media::Player>::Pause, |
2829 | + void |
2830 | + >(); |
2831 | |
2832 | if (op.is_error()) |
2833 | throw std::runtime_error("Problem pausing playback on remote object"); |
2834 | @@ -324,7 +326,12 @@ |
2835 | |
2836 | void media::PlayerStub::seek_to(const std::chrono::microseconds& offset) |
2837 | { |
2838 | - auto op = d->object->invoke_method_synchronously<mpris::Player::Seek, void, uint64_t>(offset.count()); |
2839 | + auto op = d->configuration.object->invoke_method_synchronously |
2840 | + < |
2841 | + mpris::Player<core::ubuntu::media::Player>::Seek, |
2842 | + void, |
2843 | + uint64_t |
2844 | + >(offset.count()); |
2845 | |
2846 | if (op.is_error()) |
2847 | throw std::runtime_error("Problem seeking on remote object"); |
2848 | @@ -332,7 +339,11 @@ |
2849 | |
2850 | void media::PlayerStub::stop() |
2851 | { |
2852 | - auto op = d->object->invoke_method_synchronously<mpris::Player::Stop, void>(); |
2853 | + auto op = d->configuration.object->invoke_method_synchronously |
2854 | + < |
2855 | + mpris::Player<core::ubuntu::media::Player>::Stop, |
2856 | + void |
2857 | + >(); |
2858 | |
2859 | if (op.is_error()) |
2860 | throw std::runtime_error("Problem stopping playback on remote object"); |
2861 | |
2862 | === modified file 'src/core/media/player_stub.h' |
2863 | --- src/core/media/player_stub.h 2014-04-25 17:53:00 +0000 |
2864 | +++ src/core/media/player_stub.h 2014-07-07 14:32:42 +0000 |
2865 | @@ -33,12 +33,19 @@ |
2866 | { |
2867 | class Service; |
2868 | |
2869 | -class PlayerStub : public core::dbus::Stub<Player> |
2870 | +class PlayerStub : public Player |
2871 | { |
2872 | - public: |
2873 | - explicit PlayerStub( |
2874 | - const std::shared_ptr<Service>& parent, |
2875 | - const core::dbus::types::ObjectPath& object); |
2876 | +public: |
2877 | + // Creation time options being passed to c'tor go here. |
2878 | + struct Configuration |
2879 | + { |
2880 | + // An object instance corresponding to the remote player instance. |
2881 | + core::dbus::Object::Ptr object; |
2882 | + // TrackList instance |
2883 | + std::shared_ptr<TrackList> track_list; |
2884 | + }; |
2885 | + |
2886 | + explicit PlayerStub(const Configuration& configuration); |
2887 | |
2888 | ~PlayerStub(); |
2889 | |
2890 | @@ -88,7 +95,6 @@ |
2891 | private: |
2892 | struct Private; |
2893 | std::unique_ptr<Private> d; |
2894 | - std::thread worker; |
2895 | }; |
2896 | } |
2897 | } |
2898 | |
2899 | === modified file 'src/core/media/server/server.cpp' |
2900 | --- src/core/media/server/server.cpp 2014-04-04 14:31:43 +0000 |
2901 | +++ src/core/media/server/server.cpp 2014-07-07 14:32:42 +0000 |
2902 | @@ -23,6 +23,7 @@ |
2903 | #include <hybris/media/media_codec_layer.h> |
2904 | |
2905 | #include "core/media/service_implementation.h" |
2906 | +#include "core/media/the_session_bus.h" |
2907 | |
2908 | #include <iostream> |
2909 | |
2910 | @@ -36,7 +37,14 @@ |
2911 | decoding_service_init(); |
2912 | cout << "Starting DecodingService..." << endl; |
2913 | |
2914 | - auto service = std::make_shared<media::ServiceImplementation>(); |
2915 | + |
2916 | + media::ServiceImplementation::Configuration config |
2917 | + { |
2918 | + media::ServiceImplementation::Configuration::dbus_app_armor_credentials_resolver_for_bus(the_session_bus()), |
2919 | + media::ServiceImplementation::Configuration::app_armor_permission_manager() |
2920 | + }; |
2921 | + |
2922 | + auto service = std::make_shared<media::ServiceImplementation>(config); |
2923 | service->run(); |
2924 | |
2925 | return 0; |
2926 | |
2927 | === modified file 'src/core/media/service_implementation.cpp' |
2928 | --- src/core/media/service_implementation.cpp 2014-04-23 19:00:55 +0000 |
2929 | +++ src/core/media/service_implementation.cpp 2014-07-07 14:32:42 +0000 |
2930 | @@ -20,6 +20,19 @@ |
2931 | |
2932 | #include "player_configuration.h" |
2933 | #include "player_implementation.h" |
2934 | +#include "track_list_implementation.h" |
2935 | + |
2936 | +// By default, we use gstreamer as media-streaming backend |
2937 | +#include "gstreamer/engine.h" |
2938 | + |
2939 | +// All the services we use |
2940 | +#include "apparmor.h" |
2941 | +#include "powerd_service.h" |
2942 | +#include "unity_screen_service.h" |
2943 | +#include "bluez/bluez.h" |
2944 | + |
2945 | +#include <core/dbus/dbus.h> |
2946 | +#include <core/dbus/service.h> |
2947 | |
2948 | #include <map> |
2949 | |
2950 | @@ -27,11 +40,61 @@ |
2951 | |
2952 | using namespace std; |
2953 | |
2954 | +namespace |
2955 | +{ |
2956 | +core::dbus::Bus::Ptr make_bus_connection_with_executor(core::dbus::WellKnownBus bus) |
2957 | +{ |
2958 | + core::dbus::Bus::Ptr connection |
2959 | + { |
2960 | + new core::dbus::Bus |
2961 | + { |
2962 | + bus |
2963 | + } |
2964 | + }; |
2965 | + connection->install_executor(core::dbus::asio::make_executor(connection)); |
2966 | + return connection; |
2967 | +} |
2968 | +} |
2969 | + |
2970 | struct media::ServiceImplementation::Private |
2971 | { |
2972 | - Private() |
2973 | - : key_(0) |
2974 | - { |
2975 | + Private(const media::ServiceImplementation::Configuration& config) |
2976 | + : configuration(config), |
2977 | + key_{0}, |
2978 | + session_bus{make_bus_connection_with_executor(core::dbus::WellKnownBus::session)}, |
2979 | + session_worker{[this]() { session_bus->run(); }}, |
2980 | + system_bus{make_bus_connection_with_executor(core::dbus::WellKnownBus::system)}, |
2981 | + system_worker{[this]() { system_bus->run(); }}, |
2982 | + bluez{system_bus}, |
2983 | + powerd_stub |
2984 | + { |
2985 | + core::dbus::Service::use_service<core::Powerd>(system_bus) |
2986 | + }, |
2987 | + powerd |
2988 | + { |
2989 | + powerd_stub->object_for_path(core::dbus::types::ObjectPath{"/com/canonical/powerd"}) |
2990 | + }, |
2991 | + screen_stub |
2992 | + { |
2993 | + core::dbus::Service::use_service<core::UScreen>(system_bus) |
2994 | + }, |
2995 | + screen |
2996 | + { |
2997 | + screen_stub->object_for_path(core::dbus::types::ObjectPath{"/com/canonical/Unity/Screen"}) |
2998 | + } |
2999 | + { |
3000 | + } |
3001 | + ~Private() |
3002 | + { |
3003 | + session_bus->stop(); |
3004 | + |
3005 | + if (session_worker.joinable()) |
3006 | + session_worker.join(); |
3007 | + |
3008 | + system_bus->stop(); |
3009 | + |
3010 | + if (system_worker.joinable()) |
3011 | + system_worker.join(); |
3012 | } |
3013 | |
3014 | void track_player(const std::shared_ptr<media::Player>& player) |
3015 | @@ -69,15 +132,63 @@ |
3016 | } |
3017 | } |
3018 | |
3019 | + // We store all creation time options |
3020 | + media::ServiceImplementation::Configuration configuration; |
3021 | // Used for Player instance management |
3022 | std::map<media::Player::PlayerKey, std::shared_ptr<media::Player>> player_map; |
3023 | + // A simple session counter |
3024 | media::Player::PlayerKey key_; |
3025 | + // We maintain our own connection to the session bus to query other services. |
3026 | + core::dbus::Bus::Ptr session_bus; |
3027 | + // We execute our private connection to the session bus internally |
3028 | + std::thread session_worker; |
3029 | + // We have to access the system bus and run it on its own thread. |
3030 | + core::dbus::Bus::Ptr system_bus; |
3031 | + // We execute the connection to the system bus internally |
3032 | + std::thread system_worker; |
3033 | + // We maintain a bluez stub here, together with manager and media objects |
3034 | + struct Bluez |
3035 | + { |
3036 | + Bluez(const core::dbus::Bus::Ptr& connection) |
3037 | + : stub |
3038 | + { |
3039 | + core::dbus::Service::use_service<org::Bluez>(connection) |
3040 | + }, |
3041 | + manager |
3042 | + { |
3043 | + stub->object_for_path(core::dbus::types::ObjectPath{"/"}) |
3044 | + }, |
3045 | + default_adapter |
3046 | + { |
3047 | + stub->object_for_path( |
3048 | + manager->transact_method |
3049 | + < |
3050 | + org::Bluez::Manager::DefaultAdapter, |
3051 | + core::dbus::types::ObjectPath |
3052 | + >().value()) |
3053 | + } |
3054 | + { |
3055 | + } |
3056 | + // Accesses bluez on the bus. |
3057 | + core::dbus::Service::Ptr stub; |
3058 | + // See bluez/docs/manager-api.txt. |
3059 | + core::dbus::Object::Ptr manager; |
3060 | + // See bluez/docs/media-api.txt. |
3061 | + core::dbus::Object::Ptr default_adapter; |
3062 | + } bluez; |
3063 | |
3064 | + // And we reach out to powerd/unity screen service. |
3065 | + // Makes sure that the device does not go to sleep while audio |
3066 | + // is being played back. |
3067 | + core::dbus::Service::Ptr powerd_stub; |
3068 | + core::dbus::Object::Ptr powerd; |
3069 | + // Makes sure that the display stays on while a video is played back. |
3070 | + core::dbus::Service::Ptr screen_stub; |
3071 | + core::dbus::Object::Ptr screen; |
3072 | }; |
3073 | |
3074 | -media::ServiceImplementation::ServiceImplementation() : d(new Private()) |
3075 | +media::ServiceImplementation::ServiceImplementation(const media::ServiceImplementation::Configuration& config) : d(new Private(config)) |
3076 | { |
3077 | - cout << __PRETTY_FUNCTION__ << endl; |
3078 | } |
3079 | |
3080 | media::ServiceImplementation::~ServiceImplementation() |
3081 | @@ -87,9 +198,53 @@ |
3082 | std::shared_ptr<media::Player> media::ServiceImplementation::create_session( |
3083 | const media::Player::Configuration& conf) |
3084 | { |
3085 | - std::shared_ptr<media::Player> player = std::make_shared<media::PlayerImplementation>( |
3086 | - conf.object_path, shared_from_this(), d->key()); |
3087 | + std::shared_ptr<media::Engine> engine |
3088 | + { |
3089 | + new gstreamer::Engine() |
3090 | + }; |
3091 | + |
3092 | + media::PlayerImplementation::Configuration config |
3093 | + { |
3094 | + media::PlayerSkeleton::Configuration |
3095 | + { |
3096 | + access_bus(), |
3097 | + access_service()->add_object_for_path(conf.object_path), |
3098 | + d->configuration.resolve_incoming_msg_to_client_credentials, |
3099 | + d->configuration.is_client_with_credentials_allowed_to_access_uri |
3100 | + |
3101 | + }, |
3102 | + d->key(), |
3103 | + engine, |
3104 | + std::shared_ptr<media::TrackList> |
3105 | + { |
3106 | + new media::TrackListImplementation |
3107 | + { |
3108 | + media::TrackListImplementation::Configuration |
3109 | + { |
3110 | + media::TrackListSkeleton::Configuration |
3111 | + { |
3112 | + access_bus(), |
3113 | + access_service()->add_object_for_path( |
3114 | + core::dbus::types::ObjectPath |
3115 | + { |
3116 | + conf.object_path.as_string() + "/TrackList" |
3117 | + }) |
3118 | + }, |
3119 | + engine->meta_data_extractor() |
3120 | + } |
3121 | + } |
3122 | + }, |
3123 | + d->system_bus, |
3124 | + d->bluez.stub, |
3125 | + d->bluez.manager, |
3126 | + d->bluez.default_adapter, |
3127 | + d->powerd, |
3128 | + d->screen |
3129 | + }; |
3130 | + |
3131 | + auto player = std::make_shared<media::PlayerImplementation>(config); |
3132 | d->track_player(player); |
3133 | + |
3134 | return player; |
3135 | } |
3136 | |
3137 | @@ -97,3 +252,126 @@ |
3138 | { |
3139 | d->pause_other_sessions(key); |
3140 | } |
3141 | + |
3142 | +// Returns a functor that queries the dbus daemon and apparmor to resolve |
3143 | +// client credentials from incoming dbus messages. |
3144 | +media::PlayerSkeleton::CredentialsResolver media::ServiceImplementation::Configuration::dbus_app_armor_credentials_resolver_for_bus( |
3145 | + const core::dbus::Bus::Ptr& bus) |
3146 | +{ |
3147 | + return [bus](const core::dbus::Message::Ptr& in) -> std::tuple<pid_t, uid_t, std::string> |
3148 | + { |
3149 | + static const auto invalid_credentials = std::make_tuple(-1, -1, std::string{"unknown"}); |
3150 | + |
3151 | + auto stub_service = dbus::Service::use_service<core::Apparmor>(bus); |
3152 | + auto apparmor_session = stub_service->object_for_path(dbus::types::ObjectPath("/org/freedesktop/DBus")); |
3153 | + // Get the AppArmor security context for the client |
3154 | + auto res_pid = apparmor_session->invoke_method_synchronously |
3155 | + < |
3156 | + core::Apparmor::GetConnectionUnixProcessID, |
3157 | + std::uint32_t |
3158 | + >(in->sender()); |
3159 | + |
3160 | + if (res_pid.is_error()) return invalid_credentials; |
3161 | + |
3162 | + auto res_uid = apparmor_session->invoke_method_synchronously |
3163 | + < |
3164 | + core::Apparmor::GetConnectionUnixUser, |
3165 | + std::uint32_t |
3166 | + >(in->sender()); |
3167 | + |
3168 | + if (res_uid.is_error()) return invalid_credentials; |
3169 | + |
3170 | + auto res_context = apparmor_session->invoke_method_synchronously |
3171 | + < |
3172 | + core::Apparmor::GetConnectionAppArmorSecurityContext, |
3173 | + std::string |
3174 | + >(in->sender()); |
3175 | + |
3176 | + if (res_context.is_error()) return invalid_credentials; |
3177 | + |
3178 | + return std::make_tuple( |
3179 | + pid_t{static_cast<pid_t>(res_pid.value())}, |
3180 | + uid_t{static_cast<uid_t>(res_uid.value())}, |
3181 | + res_context.value()); |
3182 | + }; |
3183 | +} |
3184 | + |
3185 | +// Returns a functor that analyzes the app armor confinement profile of the client, |
3186 | +// queries a whitelist for unconfined apps and makes sure that a URI requested to |
3187 | +// be opened is accessible by the client. |
3188 | +media::PlayerSkeleton::PermissionManager media::ServiceImplementation::Configuration::app_armor_permission_manager() |
3189 | +{ |
3190 | + return [](const std::tuple<pid_t, uid_t, std::string>& credentials, const std::string& uri) |
3191 | + { |
3192 | + const auto& context = std::get<2>(credentials); |
3193 | + |
3194 | + if (context.empty() || uri.empty()) |
3195 | + { |
3196 | + std::cout << "Client denied access since context or uri are empty" << std::endl; |
3197 | + return false; |
3198 | + } |
3199 | + |
3200 | + if (context == "unconfined") |
3201 | + { |
3202 | + std::cout << "Client allowed access since it's unconfined" << std::endl; |
3203 | + return true; |
3204 | + } |
3205 | + |
3206 | + size_t pos = context.find_first_of('_'); |
3207 | + if (pos == std::string::npos) |
3208 | + { |
3209 | + std::cout << "Client denied access since it's an invalid apparmor security context" << std::endl; |
3210 | + return false; |
3211 | + } |
3212 | + |
3213 | + const std::string pkgname = context.substr(0, pos); |
3214 | + std::cout << "client pkgname: " << pkgname << std::endl; |
3215 | + std::cout << "uri: " << uri << std::endl; |
3216 | + |
3217 | + // All confined apps can access their own files |
3218 | + if (uri.find(std::string(".local/share/" + pkgname + "/")) != std::string::npos |
3219 | + || uri.find(std::string(".cache/" + pkgname + "/")) != std::string::npos) |
3220 | + { |
3221 | + std::cout << "Client can access content in ~/.local/share/" << pkgname << " or ~/.cache/" << pkgname << std::endl; |
3222 | + return true; |
3223 | + } |
3224 | + else if (uri.find(std::string("opt/click.ubuntu.com/")) != std::string::npos |
3225 | + && uri.find(pkgname) != std::string::npos) |
3226 | + { |
3227 | + std::cout << "Client can access content in own opt directory" << std::endl; |
3228 | + return true; |
3229 | + } |
3230 | + else if ((uri.find(std::string("/system/media/audio/ui/")) != std::string::npos |
3231 | + || uri.find(std::string("/android/system/media/audio/ui/")) != std::string::npos) |
3232 | + && pkgname == "com.ubuntu.camera") |
3233 | + { |
3234 | + std::cout << "Camera app can access ui sounds" << std::endl; |
3235 | + return true; |
3236 | + } |
3237 | + // TODO: Check if the trust store previously allowed direct access to uri |
3238 | + |
3239 | + // Check in ~/Music and ~/Videos |
3240 | + // TODO: when the trust store lands, check it to see if this app can access the dirs and |
3241 | + // then remove the explicit whitelist of the music-app, and gallery-app |
3242 | + else if ((pkgname == "com.ubuntu.music" || pkgname == "com.ubuntu.gallery") && |
3243 | + (uri.find(std::string("Music/")) != std::string::npos |
3244 | + || uri.find(std::string("Videos/")) != std::string::npos)) |
3245 | + { |
3246 | + std::cout << "Client can access content in ~/Music or ~/Videos" << std::endl; |
3247 | + return true; |
3248 | + } |
3249 | + else if (uri.find(std::string("http://")) != std::string::npos |
3250 | + || uri.find(std::string("rtsp://")) != std::string::npos) |
3251 | + { |
3252 | + std::cout << "Client can access streaming content" << std::endl; |
3253 | + return true; |
3254 | + } |
3255 | + else |
3256 | + { |
3257 | + std::cout << "Client denied access to open_uri()" << std::endl; |
3258 | + return false; |
3259 | + } |
3260 | + |
3261 | + return false; |
3262 | + }; |
3263 | +} |
3264 | |
3265 | === modified file 'src/core/media/service_implementation.h' |
3266 | --- src/core/media/service_implementation.h 2014-04-23 19:00:55 +0000 |
3267 | +++ src/core/media/service_implementation.h 2014-07-07 14:32:42 +0000 |
3268 | @@ -21,6 +21,8 @@ |
3269 | |
3270 | #include "service_skeleton.h" |
3271 | |
3272 | +#include "player_skeleton.h" |
3273 | + |
3274 | namespace core |
3275 | { |
3276 | namespace ubuntu |
3277 | @@ -32,15 +34,33 @@ |
3278 | |
3279 | class ServiceImplementation : public ServiceSkeleton |
3280 | { |
3281 | - public: |
3282 | - ServiceImplementation (); |
3283 | +public: |
3284 | + // All creation-time properties go here. |
3285 | + struct Configuration |
3286 | + { |
3287 | + // Returns a functor that queries the dbus daemon and apparmor to resolve |
3288 | + // client credentials from incoming dbus messages. |
3289 | + static PlayerSkeleton::CredentialsResolver dbus_app_armor_credentials_resolver_for_bus(const core::dbus::Bus::Ptr& connection); |
3290 | + |
3291 | + // Returns a functor that analyzes the app armor confinement profile of the client, |
3292 | + // queries a whitelist for unconfined apps and makes sure that a URI requested to |
3293 | + // be opened is accessible by the client. |
3294 | + static PlayerSkeleton::PermissionManager app_armor_permission_manager(); |
3295 | + |
3296 | + // The functor called by the skeleton to resolve credentials. |
3297 | + PlayerSkeleton::CredentialsResolver resolve_incoming_msg_to_client_credentials; |
3298 | + // The functor called by the skeleton to verify if a client is allowed to access a uri. |
3299 | + PlayerSkeleton::PermissionManager is_client_with_credentials_allowed_to_access_uri; |
3300 | + }; |
3301 | + |
3302 | + ServiceImplementation (const Configuration& config); |
3303 | ~ServiceImplementation (); |
3304 | |
3305 | std::shared_ptr<Player> create_session(const Player::Configuration&); |
3306 | |
3307 | void pause_other_sessions(Player::PlayerKey key); |
3308 | |
3309 | - private: |
3310 | +private: |
3311 | struct Private; |
3312 | std::shared_ptr<Private> d; |
3313 | }; |
3314 | |
3315 | === modified file 'src/core/media/service_skeleton.cpp' |
3316 | --- src/core/media/service_skeleton.cpp 2014-04-23 19:00:55 +0000 |
3317 | +++ src/core/media/service_skeleton.cpp 2014-07-07 14:32:42 +0000 |
3318 | @@ -20,6 +20,7 @@ |
3319 | |
3320 | #include "mpris/service.h" |
3321 | #include "player_configuration.h" |
3322 | +#include "player_p.h" |
3323 | #include "the_session_bus.h" |
3324 | |
3325 | #include <core/dbus/message.h> |
3326 | @@ -56,6 +57,10 @@ |
3327 | std::placeholders::_1)); |
3328 | } |
3329 | |
3330 | + ~Private() |
3331 | + { |
3332 | + } |
3333 | + |
3334 | void handle_create_session(const core::dbus::Message::Ptr& msg) |
3335 | { |
3336 | static unsigned int session_counter = 0; |
3337 | @@ -104,7 +109,6 @@ |
3338 | |
3339 | media::ServiceSkeleton* impl; |
3340 | dbus::Object::Ptr object; |
3341 | - |
3342 | }; |
3343 | |
3344 | media::ServiceSkeleton::ServiceSkeleton() |
3345 | |
3346 | === modified file 'src/core/media/service_stub.cpp' |
3347 | --- src/core/media/service_stub.cpp 2014-04-23 19:00:55 +0000 |
3348 | +++ src/core/media/service_stub.cpp 2014-07-07 14:32:42 +0000 |
3349 | @@ -20,6 +20,8 @@ |
3350 | #include "service_traits.h" |
3351 | |
3352 | #include "player_stub.h" |
3353 | +#include "track_list_stub.h" |
3354 | + |
3355 | #include "the_session_bus.h" |
3356 | |
3357 | #include "mpris/service.h" |
3358 | @@ -27,41 +29,72 @@ |
3359 | namespace dbus = core::dbus; |
3360 | namespace media = core::ubuntu::media; |
3361 | |
3362 | -struct media::ServiceStub::Private |
3363 | -{ |
3364 | - dbus::Object::Ptr object; |
3365 | -}; |
3366 | - |
3367 | media::ServiceStub::ServiceStub() |
3368 | : core::dbus::Stub<media::Service>(the_session_bus()), |
3369 | - d(new Private{ |
3370 | - access_service()->object_for_path( |
3371 | - dbus::types::ObjectPath( |
3372 | - dbus::traits::Service<media::Service>::object_path()))}) |
3373 | + object |
3374 | + { |
3375 | + access_service()->object_for_path(dbus::types::ObjectPath |
3376 | + { |
3377 | + dbus::traits::Service<media::Service>::object_path() |
3378 | + }) |
3379 | + }, |
3380 | + worker |
3381 | + { |
3382 | + []() { the_session_bus()->run(); } |
3383 | + } |
3384 | { |
3385 | } |
3386 | |
3387 | media::ServiceStub::~ServiceStub() |
3388 | { |
3389 | + the_session_bus()->stop(); |
3390 | + |
3391 | + if (worker.joinable()) |
3392 | + worker.join(); |
3393 | } |
3394 | |
3395 | std::shared_ptr<media::Player> media::ServiceStub::create_session(const media::Player::Configuration&) |
3396 | { |
3397 | - auto op = d->object->invoke_method_synchronously<mpris::Service::CreateSession, |
3398 | + auto op = object->invoke_method_synchronously<mpris::Service::CreateSession, |
3399 | dbus::types::ObjectPath>(); |
3400 | |
3401 | - if (op.is_error()) |
3402 | - throw std::runtime_error("Problem creating session: " + op.error()); |
3403 | - |
3404 | - return std::shared_ptr<media::Player>(new media::PlayerStub(shared_from_this(), op.value())); |
3405 | + if (op.is_error()) throw std::runtime_error |
3406 | + { |
3407 | + "Problem creating session: " + op.error() |
3408 | + }; |
3409 | + |
3410 | + media::PlayerStub::Configuration config |
3411 | + { |
3412 | + access_service()->object_for_path(op.value()), |
3413 | + std::shared_ptr<media::TrackList> |
3414 | + { |
3415 | + new media::TrackListStub |
3416 | + { |
3417 | + media::TrackListStub::Configuration |
3418 | + { |
3419 | + access_service()->object_for_path(core::dbus::types::ObjectPath |
3420 | + { |
3421 | + op.value().as_string() + "/TrackList" |
3422 | + }) |
3423 | + } |
3424 | + } |
3425 | + } |
3426 | + }; |
3427 | + |
3428 | + return std::shared_ptr<media::Player>(new media::PlayerStub(config)); |
3429 | } |
3430 | |
3431 | void media::ServiceStub::pause_other_sessions(media::Player::PlayerKey key) |
3432 | { |
3433 | std::cout << __PRETTY_FUNCTION__ << std::endl; |
3434 | - auto op = d->object->invoke_method_synchronously<mpris::Service::PauseOtherSessions, |
3435 | - void>(key); |
3436 | + auto op = object->invoke_method_synchronously |
3437 | + < |
3438 | + mpris::Service::PauseOtherSessions, |
3439 | + void |
3440 | + >(key); |
3441 | |
3442 | - if (op.is_error()) |
3443 | - throw std::runtime_error("Problem pausing other sessions: " + op.error()); |
3444 | + if (op.is_error()) throw std::runtime_error |
3445 | + { |
3446 | + "Problem pausing other sessions: " + op.error() |
3447 | + }; |
3448 | } |
3449 | |
3450 | === modified file 'src/core/media/service_stub.h' |
3451 | --- src/core/media/service_stub.h 2014-04-23 19:00:55 +0000 |
3452 | +++ src/core/media/service_stub.h 2014-07-07 14:32:42 +0000 |
3453 | @@ -23,6 +23,7 @@ |
3454 | |
3455 | #include "service_traits.h" |
3456 | |
3457 | +#include <core/dbus/object.h> |
3458 | #include <core/dbus/stub.h> |
3459 | |
3460 | #include <memory> |
3461 | @@ -35,16 +36,16 @@ |
3462 | { |
3463 | class ServiceStub : public core::dbus::Stub<core::ubuntu::media::Service> |
3464 | { |
3465 | - public: |
3466 | +public: |
3467 | ServiceStub(); |
3468 | ~ServiceStub(); |
3469 | |
3470 | std::shared_ptr<Player> create_session(const Player::Configuration&); |
3471 | void pause_other_sessions(Player::PlayerKey key); |
3472 | |
3473 | - private: |
3474 | - struct Private; |
3475 | - std::unique_ptr<Private> d; |
3476 | +private: |
3477 | + core::dbus::Object::Ptr object; |
3478 | + std::thread worker; |
3479 | }; |
3480 | } |
3481 | } |
3482 | |
3483 | === modified file 'src/core/media/track_list_implementation.cpp' |
3484 | --- src/core/media/track_list_implementation.cpp 2014-03-25 14:57:15 +0000 |
3485 | +++ src/core/media/track_list_implementation.cpp 2014-07-07 14:32:42 +0000 |
3486 | @@ -30,16 +30,13 @@ |
3487 | { |
3488 | typedef std::map<Track::Id, std::tuple<Track::UriType, Track::MetaData>> MetaDataCache; |
3489 | |
3490 | - dbus::types::ObjectPath path; |
3491 | + media::TrackListImplementation::Configuration configuration; |
3492 | MetaDataCache meta_data_cache; |
3493 | - std::shared_ptr<media::Engine::MetaDataExtractor> extractor; |
3494 | }; |
3495 | |
3496 | -media::TrackListImplementation::TrackListImplementation( |
3497 | - const dbus::types::ObjectPath& op, |
3498 | - const std::shared_ptr<media::Engine::MetaDataExtractor>& extractor) |
3499 | - : media::TrackListSkeleton(op), |
3500 | - d(new Private{op, Private::MetaDataCache{}, extractor}) |
3501 | +media::TrackListImplementation::TrackListImplementation(const media::TrackListImplementation::Configuration& config) |
3502 | + : media::TrackListSkeleton(config.skeleton_configuration), |
3503 | + d(new Private{config, Private::MetaDataCache{}}) |
3504 | { |
3505 | can_edit_tracks().set(true); |
3506 | } |
3507 | @@ -75,7 +72,7 @@ |
3508 | { |
3509 | static size_t track_counter = 0; |
3510 | |
3511 | - std::stringstream ss; ss << d->path.as_string() << "/" << track_counter++; |
3512 | + std::stringstream ss; ss << d->configuration.skeleton_configuration.object->path().as_string() << "/" << track_counter++; |
3513 | Track::Id id{ss.str()}; |
3514 | |
3515 | auto result = tracks().update([this, id, position, make_current](TrackList::Container& container) |
3516 | @@ -91,7 +88,7 @@ |
3517 | { |
3518 | d->meta_data_cache[id] = std::make_tuple( |
3519 | uri, |
3520 | - d->extractor->meta_data_for_track_with_uri(uri)); |
3521 | + d->configuration.extractor->meta_data_for_track_with_uri(uri)); |
3522 | } else |
3523 | { |
3524 | std::get<0>(d->meta_data_cache[id]) = uri; |
3525 | |
3526 | === modified file 'src/core/media/track_list_implementation.h' |
3527 | --- src/core/media/track_list_implementation.h 2014-03-25 14:57:15 +0000 |
3528 | +++ src/core/media/track_list_implementation.h 2014-07-07 14:32:42 +0000 |
3529 | @@ -31,9 +31,16 @@ |
3530 | class TrackListImplementation : public TrackListSkeleton |
3531 | { |
3532 | public: |
3533 | - TrackListImplementation( |
3534 | - const core::dbus::types::ObjectPath& op, |
3535 | - const std::shared_ptr<Engine::MetaDataExtractor>& extractor); |
3536 | + // All creation time options go here. |
3537 | + struct Configuration |
3538 | + { |
3539 | + // All creation options for the skeleton. |
3540 | + TrackListSkeleton::Configuration skeleton_configuration; |
3541 | + // The meta-data implementation that should be used. |
3542 | + std::shared_ptr<Engine::MetaDataExtractor> extractor; |
3543 | + }; |
3544 | + |
3545 | + TrackListImplementation(const Configuration& config); |
3546 | ~TrackListImplementation(); |
3547 | |
3548 | Track::UriType query_uri_for_track(const Track::Id& id); |
3549 | |
3550 | === modified file 'src/core/media/track_list_skeleton.cpp' |
3551 | --- src/core/media/track_list_skeleton.cpp 2014-03-25 14:57:15 +0000 |
3552 | +++ src/core/media/track_list_skeleton.cpp 2014-07-07 14:32:42 +0000 |
3553 | @@ -42,12 +42,12 @@ |
3554 | |
3555 | struct media::TrackListSkeleton::Private |
3556 | { |
3557 | - Private(media::TrackListSkeleton* impl, |
3558 | - dbus::Object::Ptr object) |
3559 | - : impl(impl), |
3560 | - object(object), |
3561 | - can_edit_tracks(object->get_property<mpris::TrackList::Properties::CanEditTracks>()), |
3562 | - tracks(object->get_property<mpris::TrackList::Properties::Tracks>()), |
3563 | + Private(const media::TrackListSkeleton::Configuration& configuration, |
3564 | + media::TrackListSkeleton* impl) |
3565 | + : configuration(configuration), |
3566 | + impl(impl), |
3567 | + can_edit_tracks(configuration.object->get_property<mpris::TrackList::Properties::CanEditTracks>()), |
3568 | + tracks(configuration.object->get_property<mpris::TrackList::Properties::Tracks>()), |
3569 | current_track(tracks->get().begin()), |
3570 | empty_iterator(tracks->get().begin()) |
3571 | { |
3572 | @@ -62,7 +62,7 @@ |
3573 | |
3574 | auto reply = dbus::Message::make_method_return(msg); |
3575 | reply->writer() << *meta_data; |
3576 | - impl->access_bus()->send(reply); |
3577 | + configuration.bus->send(reply); |
3578 | } |
3579 | |
3580 | void handle_add_track_with_uri_at(const core::dbus::Message::Ptr& msg) |
3581 | @@ -73,7 +73,7 @@ |
3582 | impl->add_track_with_uri_at(uri, after, make_current); |
3583 | |
3584 | auto reply = dbus::Message::make_method_return(msg); |
3585 | - impl->access_bus()->send(reply); |
3586 | + configuration.bus->send(reply); |
3587 | } |
3588 | |
3589 | void handle_remove_track(const core::dbus::Message::Ptr& msg) |
3590 | @@ -84,7 +84,7 @@ |
3591 | impl->remove_track(track); |
3592 | |
3593 | auto reply = dbus::Message::make_method_return(msg); |
3594 | - impl->access_bus()->send(reply); |
3595 | + configuration.bus->send(reply); |
3596 | } |
3597 | |
3598 | void handle_go_to(const core::dbus::Message::Ptr& msg) |
3599 | @@ -95,11 +95,11 @@ |
3600 | impl->go_to(track); |
3601 | |
3602 | auto reply = dbus::Message::make_method_return(msg); |
3603 | - impl->access_bus()->send(reply); |
3604 | + configuration.bus->send(reply); |
3605 | } |
3606 | |
3607 | + media::TrackListSkeleton::Configuration configuration; |
3608 | media::TrackListSkeleton* impl; |
3609 | - dbus::Object::Ptr object; |
3610 | |
3611 | std::shared_ptr<core::dbus::Property<mpris::TrackList::Properties::CanEditTracks>> can_edit_tracks; |
3612 | std::shared_ptr<core::dbus::Property<mpris::TrackList::Properties::Tracks>> tracks; |
3613 | @@ -112,27 +112,25 @@ |
3614 | core::Signal<Track::Id> on_track_changed; |
3615 | }; |
3616 | |
3617 | -media::TrackListSkeleton::TrackListSkeleton( |
3618 | - const dbus::types::ObjectPath& op) |
3619 | - : dbus::Skeleton<media::TrackList>(the_session_bus()), |
3620 | - d(new Private(this, access_service()->add_object_for_path(op))) |
3621 | +media::TrackListSkeleton::TrackListSkeleton(const media::TrackListSkeleton::Configuration& config) |
3622 | + : d(new Private(config, this)) |
3623 | { |
3624 | - d->object->install_method_handler<mpris::TrackList::GetTracksMetadata>( |
3625 | + d->configuration.object->install_method_handler<mpris::TrackList::GetTracksMetadata>( |
3626 | std::bind(&Private::handle_get_tracks_metadata, |
3627 | std::ref(d), |
3628 | std::placeholders::_1)); |
3629 | |
3630 | - d->object->install_method_handler<mpris::TrackList::AddTrack>( |
3631 | + d->configuration.object->install_method_handler<mpris::TrackList::AddTrack>( |
3632 | std::bind(&Private::handle_add_track_with_uri_at, |
3633 | std::ref(d), |
3634 | std::placeholders::_1)); |
3635 | |
3636 | - d->object->install_method_handler<mpris::TrackList::RemoveTrack>( |
3637 | + d->configuration.object->install_method_handler<mpris::TrackList::RemoveTrack>( |
3638 | std::bind(&Private::handle_remove_track, |
3639 | std::ref(d), |
3640 | std::placeholders::_1)); |
3641 | |
3642 | - d->object->install_method_handler<mpris::TrackList::GoTo>( |
3643 | + d->configuration.object->install_method_handler<mpris::TrackList::GoTo>( |
3644 | std::bind(&Private::handle_go_to, |
3645 | std::ref(d), |
3646 | std::placeholders::_1)); |
3647 | |
3648 | === modified file 'src/core/media/track_list_skeleton.h' |
3649 | --- src/core/media/track_list_skeleton.h 2014-03-25 14:57:15 +0000 |
3650 | +++ src/core/media/track_list_skeleton.h 2014-07-07 14:32:42 +0000 |
3651 | @@ -22,6 +22,7 @@ |
3652 | |
3653 | #include <core/media/player.h> |
3654 | |
3655 | +#include <core/dbus/object.h> |
3656 | #include <core/dbus/skeleton.h> |
3657 | |
3658 | namespace core |
3659 | @@ -30,11 +31,18 @@ |
3660 | { |
3661 | namespace media |
3662 | { |
3663 | -class TrackListSkeleton : public core::dbus::Skeleton<core::ubuntu::media::TrackList> |
3664 | +class TrackListSkeleton : public core::ubuntu::media::TrackList |
3665 | { |
3666 | public: |
3667 | - TrackListSkeleton( |
3668 | - const core::dbus::types::ObjectPath& op); |
3669 | + struct Configuration |
3670 | + { |
3671 | + // Bus connection for replying to incoming calls. |
3672 | + core::dbus::Bus::Ptr bus; |
3673 | + // The dbus object representing the tracklist instance. |
3674 | + core::dbus::Object::Ptr object; |
3675 | + }; |
3676 | + |
3677 | + TrackListSkeleton(const Configuration& configuration); |
3678 | ~TrackListSkeleton(); |
3679 | |
3680 | bool has_next() const; |
3681 | |
3682 | === modified file 'src/core/media/track_list_stub.cpp' |
3683 | --- src/core/media/track_list_stub.cpp 2014-02-12 15:53:57 +0000 |
3684 | +++ src/core/media/track_list_stub.cpp 2014-07-07 14:32:42 +0000 |
3685 | @@ -25,8 +25,6 @@ |
3686 | #include "track_list_traits.h" |
3687 | #include "the_session_bus.h" |
3688 | |
3689 | -#include "mpris/track_list.h" |
3690 | - |
3691 | #include <core/dbus/property.h> |
3692 | #include <core/dbus/types/object_path.h> |
3693 | #include <core/dbus/types/variant.h> |
3694 | @@ -38,61 +36,33 @@ |
3695 | namespace dbus = core::dbus; |
3696 | namespace media = core::ubuntu::media; |
3697 | |
3698 | -struct media::TrackListStub::Private |
3699 | -{ |
3700 | - Private( |
3701 | - TrackListStub* impl, |
3702 | - const std::shared_ptr<media::Player>& parent, |
3703 | - const dbus::types::ObjectPath& op) |
3704 | - : impl(impl), |
3705 | - parent(parent), |
3706 | - object(impl->access_service()->object_for_path(op)), |
3707 | - can_edit_tracks(object->get_property<mpris::TrackList::Properties::CanEditTracks>()), |
3708 | - tracks(object->get_property<mpris::TrackList::Properties::Tracks>()) |
3709 | - { |
3710 | - } |
3711 | - |
3712 | - TrackListStub* impl; |
3713 | - std::shared_ptr<media::Player> parent; |
3714 | - dbus::Object::Ptr object; |
3715 | - |
3716 | - std::shared_ptr<core::dbus::Property<mpris::TrackList::Properties::CanEditTracks>> can_edit_tracks; |
3717 | - std::shared_ptr<core::dbus::Property<mpris::TrackList::Properties::Tracks>> tracks; |
3718 | - |
3719 | - core::Signal<void> on_track_list_replaced; |
3720 | - core::Signal<Track::Id> on_track_added; |
3721 | - core::Signal<Track::Id> on_track_removed; |
3722 | - core::Signal<Track::Id> on_track_changed; |
3723 | -}; |
3724 | - |
3725 | -media::TrackListStub::TrackListStub( |
3726 | - const std::shared_ptr<media::Player>& parent, |
3727 | - const core::dbus::types::ObjectPath& op) |
3728 | - : dbus::Stub<media::TrackList>(the_session_bus()), |
3729 | - d(new Private(this, parent, op)) |
3730 | -{ |
3731 | -} |
3732 | - |
3733 | -media::TrackListStub::~TrackListStub() |
3734 | +media::TrackListStub::TrackListStub(const media::TrackListStub::Configuration& config) |
3735 | + : configuration(config), |
3736 | + properties |
3737 | + { |
3738 | + configuration.object->get_property<mpris::TrackList::Properties::CanEditTracks>(), |
3739 | + configuration.object->get_property<mpris::TrackList::Properties::Tracks>() |
3740 | + } |
3741 | { |
3742 | } |
3743 | |
3744 | const core::Property<bool>& media::TrackListStub::can_edit_tracks() const |
3745 | { |
3746 | - return *d->can_edit_tracks; |
3747 | + return *properties.can_edit_tracks; |
3748 | } |
3749 | |
3750 | const core::Property<media::TrackList::Container>& media::TrackListStub::tracks() const |
3751 | { |
3752 | - return *d->tracks; |
3753 | + return *properties.tracks; |
3754 | } |
3755 | |
3756 | media::Track::MetaData media::TrackListStub::query_meta_data_for_track(const media::Track::Id& id) |
3757 | { |
3758 | - auto op |
3759 | - = d->object->invoke_method_synchronously< |
3760 | + auto op = configuration.object->invoke_method_synchronously |
3761 | + < |
3762 | mpris::TrackList::GetTracksMetadata, |
3763 | - std::map<std::string, std::string>>(id); |
3764 | + std::map<std::string, std::string> |
3765 | + >(id); |
3766 | |
3767 | if (op.is_error()) |
3768 | throw std::runtime_error("Problem querying meta data for track: " + op.error()); |
3769 | @@ -110,10 +80,11 @@ |
3770 | const media::Track::Id& id, |
3771 | bool make_current) |
3772 | { |
3773 | - auto op = d->object->invoke_method_synchronously<mpris::TrackList::AddTrack, void>( |
3774 | - uri, |
3775 | - id, |
3776 | - make_current); |
3777 | + auto op = configuration.object->invoke_method_synchronously |
3778 | + < |
3779 | + mpris::TrackList::AddTrack, |
3780 | + void |
3781 | + >(uri, id, make_current); |
3782 | |
3783 | if (op.is_error()) |
3784 | throw std::runtime_error("Problem adding track: " + op.error()); |
3785 | @@ -121,8 +92,11 @@ |
3786 | |
3787 | void media::TrackListStub::remove_track(const media::Track::Id& track) |
3788 | { |
3789 | - auto op = d->object->invoke_method_synchronously<mpris::TrackList::RemoveTrack, void>( |
3790 | - track); |
3791 | + auto op = configuration.object->invoke_method_synchronously |
3792 | + < |
3793 | + mpris::TrackList::RemoveTrack, |
3794 | + void |
3795 | + >(track); |
3796 | |
3797 | if (op.is_error()) |
3798 | throw std::runtime_error("Problem removing track: " + op.error()); |
3799 | @@ -130,8 +104,11 @@ |
3800 | |
3801 | void media::TrackListStub::go_to(const media::Track::Id& track) |
3802 | { |
3803 | - auto op = d->object->invoke_method_synchronously<mpris::TrackList::GoTo, void>( |
3804 | - track); |
3805 | + auto op = configuration.object->invoke_method_synchronously |
3806 | + < |
3807 | + mpris::TrackList::GoTo, |
3808 | + void |
3809 | + >(track); |
3810 | |
3811 | if (op.is_error()) |
3812 | throw std::runtime_error("Problem adding track: " + op.error()); |
3813 | @@ -139,20 +116,20 @@ |
3814 | |
3815 | const core::Signal<void>& media::TrackListStub::on_track_list_replaced() const |
3816 | { |
3817 | - return d->on_track_list_replaced; |
3818 | + return signals.on_track_list_replaced; |
3819 | } |
3820 | |
3821 | const core::Signal<media::Track::Id>& media::TrackListStub::on_track_added() const |
3822 | { |
3823 | - return d->on_track_added; |
3824 | + return signals.on_track_added; |
3825 | } |
3826 | |
3827 | const core::Signal<media::Track::Id>& media::TrackListStub::on_track_removed() const |
3828 | { |
3829 | - return d->on_track_removed; |
3830 | + return signals.on_track_removed; |
3831 | } |
3832 | |
3833 | const core::Signal<media::Track::Id>& media::TrackListStub::on_track_changed() const |
3834 | { |
3835 | - return d->on_track_changed; |
3836 | + return signals.on_track_changed; |
3837 | } |
3838 | |
3839 | === modified file 'src/core/media/track_list_stub.h' |
3840 | --- src/core/media/track_list_stub.h 2014-02-12 15:53:57 +0000 |
3841 | +++ src/core/media/track_list_stub.h 2014-07-07 14:32:42 +0000 |
3842 | @@ -21,6 +21,7 @@ |
3843 | |
3844 | #include <core/media/track_list.h> |
3845 | |
3846 | +#include "mpris/track_list.h" |
3847 | #include "track_list_traits.h" |
3848 | |
3849 | #include <core/dbus/stub.h> |
3850 | @@ -33,14 +34,21 @@ |
3851 | { |
3852 | namespace media |
3853 | { |
3854 | -class TrackListStub : public core::dbus::Stub<core::ubuntu::media::TrackList> |
3855 | +class TrackListStub : public core::ubuntu::media::TrackList |
3856 | { |
3857 | public: |
3858 | - TrackListStub( |
3859 | - const std::shared_ptr<Player>& parent, |
3860 | - const core::dbus::types::ObjectPath& op); |
3861 | - ~TrackListStub(); |
3862 | - |
3863 | + // Creation time options passed to c'tors go here. |
3864 | + struct Configuration |
3865 | + { |
3866 | + // The remote tracklist instance. |
3867 | + core::dbus::Object::Ptr object; |
3868 | + }; |
3869 | + |
3870 | + // Creates a new stub instance, setting up local endpoints for communicating |
3871 | + // with the remote track list instance. |
3872 | + TrackListStub(const Configuration& config); |
3873 | + |
3874 | + // from core::ubuntu::media::Player |
3875 | const core::Property<bool>& can_edit_tracks() const; |
3876 | const core::Property<Container>& tracks() const; |
3877 | |
3878 | @@ -57,8 +65,24 @@ |
3879 | const core::Signal<Track::Id>& on_track_changed() const; |
3880 | |
3881 | private: |
3882 | - struct Private; |
3883 | - std::unique_ptr<Private> d; |
3884 | + // We store all options passed at creation time. |
3885 | + media::TrackListStub::Configuration configuration; |
3886 | + |
3887 | + // Stubs for access remote properties. |
3888 | + struct |
3889 | + { |
3890 | + std::shared_ptr<core::dbus::Property<mpris::TrackList::Properties::CanEditTracks>> can_edit_tracks; |
3891 | + std::shared_ptr<core::dbus::Property<mpris::TrackList::Properties::Tracks>> tracks; |
3892 | + } properties; |
3893 | + |
3894 | + // And the signals notifying us of changes. |
3895 | + struct |
3896 | + { |
3897 | + core::Signal<void> on_track_list_replaced; |
3898 | + core::Signal<Track::Id> on_track_added; |
3899 | + core::Signal<Track::Id> on_track_removed; |
3900 | + core::Signal<Track::Id> on_track_changed; |
3901 | + } signals; |
3902 | }; |
3903 | } |
3904 | } |
3905 | |
3906 | === modified file 'tests/acceptance-tests/service.cpp' |
3907 | --- tests/acceptance-tests/service.cpp 2014-04-04 14:31:43 +0000 |
3908 | +++ tests/acceptance-tests/service.cpp 2014-07-07 14:32:42 +0000 |
3909 | @@ -26,6 +26,8 @@ |
3910 | |
3911 | #include "test_data.h" |
3912 | |
3913 | +#include <core/dbus/asio/executor.h> |
3914 | + |
3915 | #include <core/testing/cross_process_sync.h> |
3916 | #include <core/testing/fork_and_run.h> |
3917 | |
3918 | @@ -78,7 +80,19 @@ |
3919 | { |
3920 | SigTermCatcher sc; |
3921 | |
3922 | - auto service = std::make_shared<media::ServiceImplementation>(); |
3923 | + core::dbus::Bus::Ptr session_bus |
3924 | + { |
3925 | + new core::dbus::Bus(core::dbus::WellKnownBus::session) |
3926 | + }; |
3927 | + session_bus->install_executor(core::dbus::asio::make_executor(session_bus)); |
3928 | + |
3929 | + media::ServiceImplementation::Configuration config |
3930 | + { |
3931 | + media::ServiceImplementation::Configuration::dbus_app_armor_credentials_resolver_for_bus(session_bus), |
3932 | + media::ServiceImplementation::Configuration::app_armor_permission_manager() |
3933 | + }; |
3934 | + |
3935 | + auto service = std::make_shared<media::ServiceImplementation>(config); |
3936 | std::thread t([&service](){service->run();}); |
3937 | |
3938 | sync_service_start.try_signal_ready_for(std::chrono::milliseconds{500}); |
3939 | @@ -120,7 +134,19 @@ |
3940 | { |
3941 | SigTermCatcher sc; |
3942 | |
3943 | - auto service = std::make_shared<media::ServiceImplementation>(); |
3944 | + core::dbus::Bus::Ptr session_bus |
3945 | + { |
3946 | + new core::dbus::Bus(core::dbus::WellKnownBus::session) |
3947 | + }; |
3948 | + session_bus->install_executor(core::dbus::asio::make_executor(session_bus)); |
3949 | + |
3950 | + media::ServiceImplementation::Configuration config |
3951 | + { |
3952 | + media::ServiceImplementation::Configuration::dbus_app_armor_credentials_resolver_for_bus(session_bus), |
3953 | + media::ServiceImplementation::Configuration::app_armor_permission_manager() |
3954 | + }; |
3955 | + |
3956 | + auto service = std::make_shared<media::ServiceImplementation>(config); |
3957 | std::thread t([&service](){service->run();}); |
3958 | |
3959 | sync_service_start.try_signal_ready_for(std::chrono::milliseconds{500}); |
3960 | @@ -173,7 +199,19 @@ |
3961 | { |
3962 | SigTermCatcher sc; |
3963 | |
3964 | - auto service = std::make_shared<media::ServiceImplementation>(); |
3965 | + core::dbus::Bus::Ptr session_bus |
3966 | + { |
3967 | + new core::dbus::Bus(core::dbus::WellKnownBus::session) |
3968 | + }; |
3969 | + session_bus->install_executor(core::dbus::asio::make_executor(session_bus)); |
3970 | + |
3971 | + media::ServiceImplementation::Configuration config |
3972 | + { |
3973 | + media::ServiceImplementation::Configuration::dbus_app_armor_credentials_resolver_for_bus(session_bus), |
3974 | + media::ServiceImplementation::Configuration::app_armor_permission_manager() |
3975 | + }; |
3976 | + |
3977 | + auto service = std::make_shared<media::ServiceImplementation>(config); |
3978 | std::thread t([&service](){service->run();}); |
3979 | |
3980 | sync_service_start.try_signal_ready_for(std::chrono::milliseconds{500}); |
3981 | @@ -252,7 +290,19 @@ |
3982 | { |
3983 | SigTermCatcher sc; |
3984 | |
3985 | - auto service = std::make_shared<media::ServiceImplementation>(); |
3986 | + core::dbus::Bus::Ptr session_bus |
3987 | + { |
3988 | + new core::dbus::Bus(core::dbus::WellKnownBus::session) |
3989 | + }; |
3990 | + session_bus->install_executor(core::dbus::asio::make_executor(session_bus)); |
3991 | + |
3992 | + media::ServiceImplementation::Configuration config |
3993 | + { |
3994 | + media::ServiceImplementation::Configuration::dbus_app_armor_credentials_resolver_for_bus(session_bus), |
3995 | + media::ServiceImplementation::Configuration::app_armor_permission_manager() |
3996 | + }; |
3997 | + |
3998 | + auto service = std::make_shared<media::ServiceImplementation>(config); |
3999 | std::thread t([&service](){service->run();}); |
4000 | |
4001 | sync_service_start.try_signal_ready_for(std::chrono::milliseconds{500}); |
4002 | @@ -311,7 +361,19 @@ |
4003 | { |
4004 | SigTermCatcher sc; |
4005 | |
4006 | - auto service = std::make_shared<media::ServiceImplementation>(); |
4007 | + core::dbus::Bus::Ptr session_bus |
4008 | + { |
4009 | + new core::dbus::Bus(core::dbus::WellKnownBus::session) |
4010 | + }; |
4011 | + session_bus->install_executor(core::dbus::asio::make_executor(session_bus)); |
4012 | + |
4013 | + media::ServiceImplementation::Configuration config |
4014 | + { |
4015 | + media::ServiceImplementation::Configuration::dbus_app_armor_credentials_resolver_for_bus(session_bus), |
4016 | + media::ServiceImplementation::Configuration::app_armor_permission_manager() |
4017 | + }; |
4018 | + |
4019 | + auto service = std::make_shared<media::ServiceImplementation>(config); |
4020 | std::thread t([&service](){service->run();}); |
4021 | |
4022 | sync_service_start.try_signal_ready_for(std::chrono::milliseconds{500}); |
FAILED: Continuous integration, rev:63 jenkins. qa.ubuntu. com/job/ media-hub- ci/76/ jenkins. qa.ubuntu. com/job/ media-hub- utopic- amd64-ci/ 18/console jenkins. qa.ubuntu. com/job/ media-hub- utopic- armhf-ci/ 17/console jenkins. qa.ubuntu. com/job/ media-hub- utopic- i386-ci/ 16/console
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/media- hub-ci/ 76/rebuild
http://