Merge lp:~thomas-voss/media-hub/use-mpris-player-skeleton-and-register-with-indicator into lp:media-hub

Proposed by Thomas Voß
Status: Merged
Approved by: Jim Hodapp
Approved revision: 96
Merged at revision: 63
Proposed branch: lp:~thomas-voss/media-hub/use-mpris-player-skeleton-and-register-with-indicator
Merge into: lp:media-hub
Prerequisite: lp:~thomas-voss/media-hub/complete-mpris-spec
Diff against target: 3386 lines (+1603/-823)
35 files modified
CMakeLists.txt (+1/-1)
debian/changelog (+6/-0)
debian/control (+4/-4)
debian/libmedia-hub-common2.symbols (+2/-19)
include/core/media/player.h (+3/-3)
src/core/media/CMakeLists.txt (+9/-0)
src/core/media/apparmor.h (+62/-20)
src/core/media/cover_art_resolver.cpp (+27/-0)
src/core/media/cover_art_resolver.h (+51/-0)
src/core/media/engine.cpp (+0/-114)
src/core/media/engine.h (+0/-23)
src/core/media/gstreamer/engine.cpp (+5/-99)
src/core/media/gstreamer/meta_data_extractor.h (+92/-86)
src/core/media/mpris/media_player2.h (+64/-2)
src/core/media/mpris/metadata.h (+55/-0)
src/core/media/mpris/player.h (+181/-53)
src/core/media/mpris/playlists.h (+216/-0)
src/core/media/player.cpp (+7/-1)
src/core/media/player_configuration.h (+9/-0)
src/core/media/player_implementation.cpp (+24/-3)
src/core/media/player_implementation.h (+2/-0)
src/core/media/player_skeleton.cpp (+176/-208)
src/core/media/player_skeleton.h (+21/-11)
src/core/media/player_stub.cpp (+4/-4)
src/core/media/player_stub.h (+3/-3)
src/core/media/service_implementation.cpp (+84/-99)
src/core/media/service_implementation.h (+5/-2)
src/core/media/service_skeleton.cpp (+347/-34)
src/core/media/service_skeleton.h (+29/-2)
src/core/media/the_session_bus.cpp (+1/-1)
src/core/media/the_session_bus.h (+9/-1)
src/core/media/xesam.h (+56/-0)
symbols.map (+16/-0)
tests/unit-tests/CMakeLists.txt (+1/-0)
tests/unit-tests/test-gstreamer-engine.cpp (+31/-30)
To merge this branch: bzr merge lp:~thomas-voss/media-hub/use-mpris-player-skeleton-and-register-with-indicator
Reviewer Review Type Date Requested Status
Jim Hodapp (community) code Approve
PS Jenkins bot continuous-integration Needs Fixing
Marcus Tomlinson (community) Approve
Nick Dedekind (community) Approve
Review via email: mp+232178@code.launchpad.net

Commit message

Export the per-app media player objects as proper MPRIS players.

Description of the change

Export the per-app media player objects as proper MPRIS players.

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

Wire up more property changes across different MPRIS representations.

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

Make sure that a valid bus name is requested.

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

Clean up meta-data extraction and property updates.

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

Initialize player properties and communicate track meta data via MPRIS.

68. By Thomas Voß

Add some temporary debugging output.

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

Remove terminal debugging output.
Add method handler for mpris::Player::PlayPause.

70. By Thomas Voß

Rename MetaData property to comply to mpris spec.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Nick Dedekind (nick-dedekind) wrote :

A few cosmetic comments attached.
I probably don't understand every line of code here for the dbus bits, but the code looks ok to me.

Functional testing to commence.

review: Needs Fixing
71. By Thomas Voß

Factor out property initialization into mpris::Player and mpris::MediaPlayer2 classes.

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

Switch to std::int64_t for encoding timestamps.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
73. By Thomas Voß

More uint64_t -> int64_t changes.

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

Bump major version to account for signature changes in public interface.

75. By Thomas Voß

Adjust symbols to account for symbols.map introduction.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
76. By Thomas Voß

Support GetAll calls for properties.
Disable next/previous capabilities.

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

Resolve cover art.

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

Add playlists implementation.

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

Remove emission of change signals on construction.

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

Infer the identity of a player from its app armor profile.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
81. By Thomas Voß

Fix typo in object path.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
82. By Thomas Voß

Add app id translation as an intermediate step to circumvent https://bugs.launchpad.net/indicator-sound/+bug/1364241.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
83. By Thomas Voß

Use the complete versioned app id as identity.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
84. By Thomas Voß

Make stop() a noop if state is already stopped.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
85. By Thomas Voß

[ Jim Hodapp ]
Add an audio stream role that allows the client app to categorize
what type of audio stream it's requesting playback for. Pause
playback of playing Players only if the requesting Player and other
Player are both of role multimedia .

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
86. By Thomas Voß

Remerge changes from prerequisite branch.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
87. By Thomas Voß

Move exposure to MPRIS-handlers to the service, and dynamically switch players as they are created and made current.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
88. By Thomas Voß

Only make player current if it is providing a multi-media stream.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
89. By Thomas Voß

Play/Pause should call play() or pause(), not next().

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
90. By Thomas Voß

Factor out cover art resolver.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
91. By Thomas Voß

[ Jim Hodapp ]
* When power hits the low or very low levels, pause all Players with
  role of multimedia. When the warning notification is cleared from
  the screen, resume playback.
[ Ricardo Salveti de Araujo ]
* playbin: fixing audio-sink gst property name

92. By Thomas Voß

Just a criss-cross merge with prerequisite branch.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
93. By Thomas Voß

Unset current player if client disconnects.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
94. By Thomas Voß

Adjust copyright dates.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Nick Dedekind (nick-dedekind) wrote :

LGTM.
Player appears in sound indicator and responds to feedback.

TODO - previous/next/album-art

review: Approve
Revision history for this message
Marcus Tomlinson (marcustomlinson) wrote :

1965 +media::PlayerSkeleton::PlayerSkeleton(const media::PlayerSkeleton::Configuration& config)

The following case is missing from this method:

    auto play_pause = std::bind(&Private::handle_play_pause, d, std::placeholders::_1);
    d->object->install_method_handler<mpris::Player::PlayPause>(play_pause);

review: Needs Fixing
95. By Thomas Voß

Wire up PlayPause correctly.

Revision history for this message
Marcus Tomlinson (marcustomlinson) wrote :

+1

review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Jim Hodapp (jhodapp) wrote :

Just a few simple changes.

review: Needs Fixing (code)
96. By Thomas Voß

Address reviewer comments.

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

Looks good, thanks!

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CMakeLists.txt'
2--- CMakeLists.txt 2014-06-27 08:06:14 +0000
3+++ CMakeLists.txt 2014-09-10 21:05:53 +0000
4@@ -2,7 +2,7 @@
5
6 project(ubuntu-media-hub)
7
8-set(UBUNTU_MEDIA_HUB_VERSION_MAJOR 1)
9+set(UBUNTU_MEDIA_HUB_VERSION_MAJOR 2)
10 set(UBUNTU_MEDIA_HUB_VERSION_MINOR 0)
11 set(UBUNTU_MEDIA_HUB_VERSION_PATCH 0)
12
13
14=== modified file 'debian/changelog'
15--- debian/changelog 2014-09-08 14:26:38 +0000
16+++ debian/changelog 2014-09-10 21:05:53 +0000
17@@ -1,3 +1,9 @@
18+media-hub (2.0.0) UNRELEASED; urgency=medium
19+
20+ * Bump major version to account for signature changes in public interface.
21+
22+ -- Thomas Voß <thomas.voss@canonical.com> Thu, 28 Aug 2014 09:10:33 +0200
23+
24 media-hub (1.0.0+14.10.20140908-0ubuntu1) utopic; urgency=low
25
26 [ Jim Hodapp ]
27
28=== modified file 'debian/control'
29--- debian/control 2014-06-27 08:06:14 +0000
30+++ debian/control 2014-09-10 21:05:53 +0000
31@@ -39,8 +39,8 @@
32 Architecture: any
33 Multi-Arch: same
34 Pre-Depends: dpkg (>= 1.15.6~)
35-Depends: libmedia-hub-common1 (= ${binary:Version}),
36- libmedia-hub-client1 (= ${binary:Version}),
37+Depends: libmedia-hub-common2 (= ${binary:Version}),
38+ libmedia-hub-client2 (= ${binary:Version}),
39 ${misc:Depends},
40 libproperties-cpp-dev,
41 Suggests: libmedia-hub-doc
42@@ -62,7 +62,7 @@
43 .
44 This package contains the runtime.
45
46-Package: libmedia-hub-common1
47+Package: libmedia-hub-common2
48 Architecture: any
49 Multi-Arch: same
50 Pre-Depends: dpkg (>= 1.15.6~)
51@@ -74,7 +74,7 @@
52 .
53 This package contains the common libraries.
54
55-Package: libmedia-hub-client1
56+Package: libmedia-hub-client2
57 Architecture: any
58 Multi-Arch: same
59 Pre-Depends: dpkg (>= 1.15.6~)
60
61=== renamed file 'debian/libmedia-hub-client1.install' => 'debian/libmedia-hub-client2.install'
62=== renamed file 'debian/libmedia-hub-common1.install' => 'debian/libmedia-hub-common2.install'
63=== renamed file 'debian/libmedia-hub-common1.symbols' => 'debian/libmedia-hub-common2.symbols'
64--- debian/libmedia-hub-common1.symbols 2014-07-12 06:55:19 +0000
65+++ debian/libmedia-hub-common2.symbols 2014-09-10 21:05:53 +0000
66@@ -1,19 +1,2 @@
67-libmedia-hub-common.so.1 libmedia-hub-common1 #MINVER#
68- (c++)"the_session_bus()@Base" 0.0.1
69- (c++)"std::shared_ptr<core::dbus::Bus>::~shared_ptr()@Base" 0.0.1
70- (c++)"std::shared_ptr<core::dbus::Executor>::~shared_ptr()@Base" 0.0.1
71- (c++)"std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_destroy()@Base" 0.0.1
72- (c++)"std::_Sp_counted_ptr_inplace<core::dbus::Bus, std::allocator<core::dbus::Bus>, (__gnu_cxx::_Lock_policy)2>::_M_destroy()@Base" 0.0.1
73- (c++)"std::_Sp_counted_ptr_inplace<core::dbus::Bus, std::allocator<core::dbus::Bus>, (__gnu_cxx::_Lock_policy)2>::_M_dispose()@Base" 0.0.1
74- (c++)"std::_Sp_counted_ptr_inplace<core::dbus::Bus, std::allocator<core::dbus::Bus>, (__gnu_cxx::_Lock_policy)2>::_M_get_deleter(std::type_info const&)@Base" 0.0.1
75- (c++)"std::_Sp_counted_ptr_inplace<core::dbus::Bus, std::allocator<core::dbus::Bus>, (__gnu_cxx::_Lock_policy)2>::~_Sp_counted_ptr_inplace()@Base" 0.0.1
76- (c++)"typeinfo for std::_Mutex_base<(__gnu_cxx::_Lock_policy)2>@Base" 0.0.1
77- (c++)"typeinfo for std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>@Base" 0.0.1
78- (c++)"typeinfo for std::_Sp_make_shared_tag@Base" 0.0.1
79- (c++)"typeinfo for std::_Sp_counted_ptr_inplace<core::dbus::Bus, std::allocator<core::dbus::Bus>, (__gnu_cxx::_Lock_policy)2>@Base" 0.0.1
80- (c++)"typeinfo name for std::_Mutex_base<(__gnu_cxx::_Lock_policy)2>@Base" 0.0.1
81- (c++)"typeinfo name for std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>@Base" 0.0.1
82- (c++)"typeinfo name for std::_Sp_make_shared_tag@Base" 0.0.1
83- (c++)"typeinfo name for std::_Sp_counted_ptr_inplace<core::dbus::Bus, std::allocator<core::dbus::Bus>, (__gnu_cxx::_Lock_policy)2>@Base" 0.0.1
84- (c++)"vtable for std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>@Base" 0.0.1
85- (c++)"vtable for std::_Sp_counted_ptr_inplace<core::dbus::Bus, std::allocator<core::dbus::Bus>, (__gnu_cxx::_Lock_policy)2>@Base" 0.0.1
86+libmedia-hub-common.so.2 libmedia-hub-common2 #MINVER#
87+ (c++)"core::ubuntu::media::the_session_bus()@Base" 0replaceme
88
89=== modified file 'include/core/media/player.h'
90--- include/core/media/player.h 2014-08-29 14:03:42 +0000
91+++ include/core/media/player.h 2014-09-10 21:05:53 +0000
92@@ -124,8 +124,8 @@
93 virtual const core::Property<Volume>& volume() const = 0;
94 virtual const core::Property<PlaybackRate>& minimum_playback_rate() const = 0;
95 virtual const core::Property<PlaybackRate>& maximum_playback_rate() const = 0;
96- virtual const core::Property<uint64_t>& position() const = 0;
97- virtual const core::Property<uint64_t>& duration() const = 0;
98+ virtual const core::Property<int64_t>& position() const = 0;
99+ virtual const core::Property<int64_t>& duration() const = 0;
100 virtual const core::Property<AudioStreamRole>& audio_stream_role() const = 0;
101
102 virtual core::Property<LoopStatus>& loop_status() = 0;
103@@ -134,7 +134,7 @@
104 virtual core::Property<Volume>& volume() = 0;
105 virtual core::Property<AudioStreamRole>& audio_stream_role() = 0;
106
107- virtual const core::Signal<uint64_t>& seeked_to() const = 0;
108+ virtual const core::Signal<int64_t>& seeked_to() const = 0;
109 virtual const core::Signal<void>& end_of_stream() const = 0;
110 virtual core::Signal<PlaybackStatus>& playback_status_changed() = 0;
111 protected:
112
113=== modified file 'src/core/media/CMakeLists.txt'
114--- src/core/media/CMakeLists.txt 2014-09-10 21:05:53 +0000
115+++ src/core/media/CMakeLists.txt 2014-09-10 21:05:53 +0000
116@@ -1,6 +1,10 @@
117 pkg_check_modules(PC_GSTREAMER_1_0 REQUIRED gstreamer-1.0)
118 include_directories(${PC_GSTREAMER_1_0_INCLUDE_DIRS} ${HYBRIS_MEDIA_CFLAGS})
119
120+# We compile with all symbols visible by default. For the shipping library, we strip
121+# out all symbols that are not in core::ubuntu::media*
122+set(symbol_map "${CMAKE_SOURCE_DIR}/symbols.map")
123+
124 file(GLOB MPRIS_HEADERS mpris/ *.h)
125
126 add_library(
127@@ -22,6 +26,8 @@
128 PROPERTIES
129 VERSION ${UBUNTU_MEDIA_HUB_VERSION_MAJOR}.${UBUNTU_MEDIA_HUB_VERSION_MINOR}.${UBUNTU_MEDIA_HUB_VERSION_PATCH}
130 SOVERSION ${UBUNTU_MEDIA_HUB_VERSION_MAJOR}
131+ LINK_FLAGS "${ldflags} -Wl,--version-script,${symbol_map}"
132+ LINK_DEPENDS ${symbol_map}
133 )
134
135 add_library(
136@@ -43,6 +49,8 @@
137 PROPERTIES
138 VERSION ${UBUNTU_MEDIA_HUB_VERSION_MAJOR}.${UBUNTU_MEDIA_HUB_VERSION_MINOR}.${UBUNTU_MEDIA_HUB_VERSION_PATCH}
139 SOVERSION ${UBUNTU_MEDIA_HUB_VERSION_MAJOR}
140+ LINK_FLAGS "${ldflags} -Wl,--version-script,${symbol_map}"
141+ LINK_DEPENDS ${symbol_map}
142 )
143
144 target_link_libraries(
145@@ -66,6 +74,7 @@
146
147 ${MPRIS_HEADERS}
148
149+ cover_art_resolver.cpp
150 engine.cpp
151 gstreamer/engine.cpp
152
153
154=== modified file 'src/core/media/apparmor.h'
155--- src/core/media/apparmor.h 2014-04-22 17:23:43 +0000
156+++ src/core/media/apparmor.h 2014-09-10 21:05:53 +0000
157@@ -16,36 +16,78 @@
158 * Author: Jim Hodapp <jim.hodapp@canonical.com>
159 */
160
161-#ifndef APPARMOR_H_DBUS_
162-#define APPARMOR_H_DBUS_
163+#ifndef APPARMOR_DBUS_H_
164+#define APPARMOR_DBUS_H_
165+
166+#include <core/dbus/macros.h>
167+#include <core/dbus/object.h>
168+#include <core/dbus/service.h>
169
170 #include <string>
171 #include <chrono>
172
173-namespace core
174-{
175-
176-struct Apparmor
177-{
178- static std::string& name()
179+// TODO(tvoss): This really should live in trust-store, providing a straightforward
180+// way for parties involved in managing trust relationships to query peers' apparmor
181+// profiles. Please see https://bugs.launchpad.net/trust-store/+bug/1350736 for the
182+// related bug
183+namespace org
184+{
185+namespace freedesktop
186+{
187+namespace dbus
188+{
189+struct DBus
190+{
191+ static const std::string& name()
192 {
193- static std::string s = "org.freedesktop.DBus";
194+ static const std::string s = "org.freedesktop.DBus";
195 return s;
196 }
197
198- struct getConnectionAppArmorSecurityContext
199+ // Gets the AppArmor confinement string associated with the unique connection name. If
200+ // D-Bus is not performing AppArmor mediation, the
201+ // org.freedesktop.DBus.Error.AppArmorSecurityContextUnknown error is returned.
202+ DBUS_CPP_METHOD_DEF(GetConnectionAppArmorSecurityContext, DBus)
203+
204+ struct Stub
205 {
206- static std::string name()
207- {
208- static std::string s = "GetConnectionAppArmorSecurityContext";
209- return s;
210- }
211-
212- static const std::chrono::milliseconds default_timeout() { return std::chrono::seconds{1}; }
213-
214- typedef Apparmor Interface;
215+ // Creates a new stub instance for the given object to access
216+ // DBus functionality.
217+ Stub(const core::dbus::Object::Ptr& object) : object{object}
218+ {
219+ }
220+
221+ // Creates a new stub instance for the given bus connection
222+ Stub(const core::dbus::Bus::Ptr& bus)
223+ : object
224+ {
225+ core::dbus::Service::use_service<org::freedesktop::dbus::DBus>(bus)
226+ ->object_for_path(core::dbus::types::ObjectPath{"/org/freedesktop/DBus"})
227+ }
228+ {
229+ }
230+
231+ // Gets the AppArmor confinement string associated with the unique connection name. If
232+ // D-Bus is not performing AppArmor mediation, the
233+ // org.freedesktop.DBus.Error.AppArmorSecurityContextUnknown error is returned.
234+ //
235+ // Invokes the given handler on completion.
236+ void get_connection_app_armor_security_async(
237+ const std::string& name,
238+ std::function<void(const std::string&)> handler)
239+ {
240+ object->invoke_method_asynchronously_with_callback<GetConnectionAppArmorSecurityContext, std::string>(
241+ [handler](const core::dbus::Result<std::string>& result)
242+ {
243+ if (not result.is_error()) handler(result.value());
244+ }, name);
245+ }
246+
247+ core::dbus::Object::Ptr object;
248 };
249 };
250 }
251+}
252+}
253
254-#endif
255+#endif // APPARMOR_DBUS_H_
256
257=== added file 'src/core/media/cover_art_resolver.cpp'
258--- src/core/media/cover_art_resolver.cpp 1970-01-01 00:00:00 +0000
259+++ src/core/media/cover_art_resolver.cpp 2014-09-10 21:05:53 +0000
260@@ -0,0 +1,27 @@
261+/**
262+ * Copyright (C) 2013-2014 Canonical Ltd
263+ *
264+ * This program is free software: you can redistribute it and/or modify it
265+ * under the terms of the GNU Lesser General Public License version 3,
266+ * as published by the Free Software Foundation.
267+ *
268+ * This program is distributed in the hope that it will be useful,
269+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
270+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
271+ * GNU Lesser General Public License for more details.
272+ *
273+ * You should have received a copy of the GNU Lesser General Public License
274+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
275+ *
276+ * Authored by: Thomas Voß <thomas.voss@canonical.com>
277+ */
278+
279+#include "cover_art_resolver.h"
280+
281+core::ubuntu::media::CoverArtResolver core::ubuntu::media::always_missing_cover_art_resolver()
282+{
283+ return [](const std::string&, const std::string&, const std::string&)
284+ {
285+ return "file:///usr/share/unity/icons/album_missing.png";
286+ };
287+}
288
289=== added file 'src/core/media/cover_art_resolver.h'
290--- src/core/media/cover_art_resolver.h 1970-01-01 00:00:00 +0000
291+++ src/core/media/cover_art_resolver.h 2014-09-10 21:05:53 +0000
292@@ -0,0 +1,51 @@
293+/**
294+ * Copyright (C) 2013-2014 Canonical Ltd
295+ *
296+ * This program is free software: you can redistribute it and/or modify it
297+ * under the terms of the GNU Lesser General Public License version 3,
298+ * as published by the Free Software Foundation.
299+ *
300+ * This program is distributed in the hope that it will be useful,
301+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
302+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
303+ * GNU Lesser General Public License for more details.
304+ *
305+ * You should have received a copy of the GNU Lesser General Public License
306+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
307+ *
308+ * Authored by: Thomas Voß <thomas.voss@canonical.com>
309+ */
310+
311+#ifndef CORE_UBUNTU_MEDIA_COVER_ART_RESOLVER_H_
312+#define CORE_UBUNTU_MEDIA_COVER_ART_RESOLVER_H_
313+
314+#include <functional>
315+#include <string>
316+
317+namespace core
318+{
319+namespace ubuntu
320+{
321+namespace media
322+{
323+// Functional modelling a helper to resolve artist/album names to
324+// cover art.
325+typedef std::function
326+<
327+ std::string // Returns a URL pointing to the album art
328+ (
329+ const std::string&, // The title of the track
330+ const std::string&, // The name of the album
331+ const std::string& // The name of the artist
332+ )
333+> CoverArtResolver;
334+
335+// Return a CoverArtResolver that always resolves to
336+// file:///usr/share/unity/icons/album_missing.png
337+CoverArtResolver always_missing_cover_art_resolver();
338+}
339+}
340+}
341+
342+#endif // CORE_UBUNTU_MEDIA_COVER_ART_RESOLVER_H_
343+
344
345=== modified file 'src/core/media/engine.cpp'
346--- src/core/media/engine.cpp 2014-08-29 14:03:42 +0000
347+++ src/core/media/engine.cpp 2014-09-10 21:05:53 +0000
348@@ -24,120 +24,6 @@
349
350 namespace media = core::ubuntu::media;
351
352-const std::string& media::Engine::Xesam::album()
353-{
354- static const std::string s{"xesam:album"};
355- return s;
356-}
357-
358-const std::string& media::Engine::Xesam::album_artist()
359-{
360- static const std::string s{"xesam:album_artist"};
361- return s;
362-}
363-
364-const std::string& media::Engine::Xesam::artist()
365-{
366- static const std::string s{"xesam:artist"};
367- return s;
368-}
369-
370-const std::string& media::Engine::Xesam::as_text()
371-{
372- static const std::string s{"xesam:as_text"};
373- return s;
374-}
375-
376-const std::string& media::Engine::Xesam::audio_bpm()
377-{
378- static const std::string s{"xesam:audio_bmp"};
379- return s;
380-}
381-
382-const std::string& media::Engine::Xesam::auto_rating()
383-{
384- static const std::string s{"xesam:autoRating"};
385- return s;
386-}
387-
388-const std::string& media::Engine::Xesam::comment()
389-{
390- static const std::string s{"xesam:comment"};
391- return s;
392-}
393-
394-const std::string& media::Engine::Xesam::composer()
395-{
396- static const std::string s{"xesam:composer"};
397- return s;
398-}
399-
400-const std::string& media::Engine::Xesam::content_created()
401-{
402- static const std::string s{"xesam:contentCreated"};
403- return s;
404-}
405-
406-const std::string& media::Engine::Xesam::disc_number()
407-{
408- static const std::string s{"xesam:discNumber"};
409- return s;
410-}
411-
412-const std::string& media::Engine::Xesam::first_used()
413-{
414- static const std::string s{"xesam:firstUsed"};
415- return s;
416-}
417-
418-const std::string& media::Engine::Xesam::genre()
419-{
420- static const std::string s{"xesam:genre"};
421- return s;
422-}
423-
424-const std::string& media::Engine::Xesam::last_used()
425-{
426- static const std::string s{"xesam:lastUsed"};
427- return s;
428-}
429-
430-const std::string& media::Engine::Xesam::lyricist()
431-{
432- static const std::string s{"xesam:lyricist"};
433- return s;
434-}
435-
436-const std::string& media::Engine::Xesam::title()
437-{
438- static const std::string s{"xesam:title"};
439- return s;
440-}
441-
442-const std::string& media::Engine::Xesam::track_number()
443-{
444- static const std::string s{"xesam:trackNumber"};
445- return s;
446-}
447-
448-const std::string& media::Engine::Xesam::url()
449-{
450- static const std::string s{"xesam:url"};
451- return s;
452-}
453-
454-const std::string& media::Engine::Xesam::use_count()
455-{
456- static const std::string s{"xesam:useCount"};
457- return s;
458-}
459-
460-const std::string& media::Engine::Xesam::user_rating()
461-{
462- static const std::string s{"xesam:userRating"};
463- return s;
464-}
465-
466 double media::Engine::Volume::min()
467 {
468 return 0.;
469
470=== modified file 'src/core/media/engine.h'
471--- src/core/media/engine.h 2014-08-29 14:03:42 +0000
472+++ src/core/media/engine.h 2014-09-10 21:05:53 +0000
473@@ -45,29 +45,6 @@
474 stopped
475 };
476
477- struct Xesam
478- {
479- static const std::string& album();
480- static const std::string& album_artist();
481- static const std::string& artist();
482- static const std::string& as_text();
483- static const std::string& audio_bpm();
484- static const std::string& auto_rating();
485- static const std::string& comment();
486- static const std::string& composer();
487- static const std::string& content_created();
488- static const std::string& disc_number();
489- static const std::string& first_used();
490- static const std::string& genre();
491- static const std::string& last_used();
492- static const std::string& lyricist();
493- static const std::string& title();
494- static const std::string& track_number();
495- static const std::string& url();
496- static const std::string& use_count();
497- static const std::string& user_rating();
498- };
499-
500 struct Volume
501 {
502 static double min();
503
504=== modified file 'src/core/media/gstreamer/engine.cpp'
505--- src/core/media/gstreamer/engine.cpp 2014-08-29 21:45:58 +0000
506+++ src/core/media/gstreamer/engine.cpp 2014-09-10 21:05:53 +0000
507@@ -58,105 +58,7 @@
508 void on_tag_available(const gstreamer::Bus::Message::Detail::Tag& tag)
509 {
510 media::Track::MetaData md;
511-
512- gst_tag_list_foreach(
513- tag.tag_list,
514- [](const GstTagList *list,
515- const gchar* tag,
516- gpointer user_data)
517- {
518- (void) list;
519-
520- static const std::map<std::string, std::string> gstreamer_to_mpris_tag_lut =
521- {
522- {GST_TAG_ALBUM, media::Engine::Xesam::album()},
523- {GST_TAG_ALBUM_ARTIST, media::Engine::Xesam::album_artist()},
524- {GST_TAG_ARTIST, media::Engine::Xesam::artist()},
525- {GST_TAG_LYRICS, media::Engine::Xesam::as_text()},
526- {GST_TAG_COMMENT, media::Engine::Xesam::comment()},
527- {GST_TAG_COMPOSER, media::Engine::Xesam::composer()},
528- {GST_TAG_DATE, media::Engine::Xesam::content_created()},
529- {GST_TAG_ALBUM_VOLUME_NUMBER, media::Engine::Xesam::disc_number()},
530- {GST_TAG_GENRE, media::Engine::Xesam::genre()},
531- {GST_TAG_TITLE, media::Engine::Xesam::title()},
532- {GST_TAG_TRACK_NUMBER, media::Engine::Xesam::track_number()},
533- {GST_TAG_USER_RATING, media::Engine::Xesam::user_rating()}
534- };
535-
536- auto md = static_cast<media::Track::MetaData*>(user_data);
537- std::stringstream ss;
538-
539- switch(gst_tag_get_type(tag))
540- {
541- case G_TYPE_BOOLEAN:
542- {
543- gboolean value;
544- if (gst_tag_list_get_boolean(list, tag, &value))
545- ss << value;
546- break;
547- }
548- case G_TYPE_INT:
549- {
550- gint value;
551- if (gst_tag_list_get_int(list, tag, &value))
552- ss << value;
553- break;
554- }
555- case G_TYPE_UINT:
556- {
557- guint value;
558- if (gst_tag_list_get_uint(list, tag, &value))
559- ss << value;
560- break;
561- }
562- case G_TYPE_INT64:
563- {
564- gint64 value;
565- if (gst_tag_list_get_int64(list, tag, &value))
566- ss << value;
567- break;
568- }
569- case G_TYPE_UINT64:
570- {
571- guint64 value;
572- if (gst_tag_list_get_uint64(list, tag, &value))
573- ss << value;
574- break;
575- }
576- case G_TYPE_FLOAT:
577- {
578- gfloat value;
579- if (gst_tag_list_get_float(list, tag, &value))
580- ss << value;
581- break;
582- }
583- case G_TYPE_DOUBLE:
584- {
585- double value;
586- if (gst_tag_list_get_double(list, tag, &value))
587- ss << value;
588- break;
589- }
590- case G_TYPE_STRING:
591- {
592- gchar* value;
593- if (gst_tag_list_get_string(list, tag, &value))
594- {
595- ss << value;
596- g_free(value);
597- }
598- break;
599- }
600- default:
601- break;
602- }
603-
604- (*md).set(
605- (gstreamer_to_mpris_tag_lut.count(tag) > 0 ? gstreamer_to_mpris_tag_lut.at(tag) : tag),
606- ss.str());
607- },
608- &md);
609-
610+ gstreamer::MetaDataExtractor::on_tag_available(tag, md);
611 track_meta_data.set(std::make_tuple(playbin.uri(), md));
612 }
613
614@@ -318,6 +220,10 @@
615
616 bool gstreamer::Engine::stop()
617 {
618+ // No need to wait, and we can immediately return.
619+ if (d->state == media::Engine::State::stopped)
620+ return true;
621+
622 auto result = d->playbin.set_state_and_wait(GST_STATE_NULL);
623
624 if (result)
625
626=== modified file 'src/core/media/gstreamer/meta_data_extractor.h'
627--- src/core/media/gstreamer/meta_data_extractor.h 2014-02-12 15:53:57 +0000
628+++ src/core/media/gstreamer/meta_data_extractor.h 2014-09-10 21:05:53 +0000
629@@ -20,6 +20,7 @@
630 #define GSTREAMER_META_DATA_EXTRACTOR_H_
631
632 #include "../engine.h"
633+#include "../xesam.h"
634
635 #include "bus.h"
636
637@@ -33,78 +34,25 @@
638 class MetaDataExtractor : public core::ubuntu::media::Engine::MetaDataExtractor
639 {
640 public:
641- MetaDataExtractor()
642- : pipe(gst_pipeline_new("meta_data_extractor_pipeline")),
643- decoder(gst_element_factory_make ("uridecodebin", NULL)),
644- bus(GST_ELEMENT_BUS(pipe))
645- {
646- gst_bin_add(GST_BIN(pipe), decoder);
647-
648- auto sink = gst_element_factory_make ("fakesink", NULL);
649- gst_bin_add (GST_BIN (pipe), sink);
650-
651- g_signal_connect (decoder, "pad-added", G_CALLBACK (on_new_pad), sink);
652- }
653-
654- ~MetaDataExtractor()
655- {
656- gst_element_set_state(pipe, GST_STATE_NULL);
657- // gst_object_unref(pipe);
658- }
659-
660- core::ubuntu::media::Track::MetaData meta_data_for_track_with_uri(const core::ubuntu::media::Track::UriType& uri)
661- {
662- if (!gst_uri_is_valid(uri.c_str()))
663- throw std::runtime_error("Invalid uri");
664-
665- core::ubuntu::media::Track::MetaData meta_data;
666- std::promise<core::ubuntu::media::Track::MetaData> promise;
667- std::future<core::ubuntu::media::Track::MetaData> future{promise.get_future()};
668-
669- core::ScopedConnection on_new_message_connection
670+ static const std::map<std::string, std::string>& gstreamer_to_mpris_tag_lut()
671+ {
672+ static const std::map<std::string, std::string> lut
673 {
674- bus.on_new_message.connect(
675- [&](const gstreamer::Bus::Message& msg)
676- {
677- std::cout << __PRETTY_FUNCTION__ << gst_message_type_get_name(msg.type) << std::endl;
678- if (msg.type == GST_MESSAGE_TAG)
679- {
680- MetaDataExtractor::on_tag_available(msg.detail.tag, meta_data);
681- } else if (msg.type == GST_MESSAGE_ASYNC_DONE)
682- {
683- promise.set_value(meta_data);
684- }
685- })
686+ {GST_TAG_ALBUM, std::string{xesam::Album::name}},
687+ {GST_TAG_ALBUM_ARTIST, std::string{xesam::AlbumArtist::name}},
688+ {GST_TAG_ARTIST, std::string{xesam::Artist::name}},
689+ {GST_TAG_LYRICS, std::string{xesam::AsText::name}},
690+ {GST_TAG_COMMENT, std::string{xesam::Comment::name}},
691+ {GST_TAG_COMPOSER, std::string{xesam::Composer::name}},
692+ {GST_TAG_DATE, std::string{xesam::ContentCreated::name}},
693+ {GST_TAG_ALBUM_VOLUME_NUMBER, std::string{xesam::DiscNumber::name}},
694+ {GST_TAG_GENRE, std::string{xesam::Genre::name}},
695+ {GST_TAG_TITLE, std::string{xesam::Title::name}},
696+ {GST_TAG_TRACK_NUMBER, std::string{xesam::TrackNumber::name}},
697+ {GST_TAG_USER_RATING, std::string{xesam::UserRating::name}}
698 };
699
700- g_object_set(decoder, "uri", uri.c_str(), NULL);
701- gst_element_set_state(pipe, GST_STATE_PAUSED);
702-
703- if (std::future_status::ready != future.wait_for(std::chrono::seconds(2)))
704- {
705- gst_element_set_state(pipe, GST_STATE_NULL);
706- throw std::runtime_error("Problem extracting meta data for track");
707- } else
708- {
709- gst_element_set_state(pipe, GST_STATE_NULL);
710- }
711-
712- return future.get();
713- }
714-
715-private:
716- static void on_new_pad(GstElement*, GstPad* pad, GstElement* fakesink)
717- {
718- GstPad *sinkpad;
719-
720- sinkpad = gst_element_get_static_pad (fakesink, "sink");
721-
722- if (!gst_pad_is_linked (sinkpad)) {
723- if (gst_pad_link (pad, sinkpad) != GST_PAD_LINK_OK)
724- g_error ("Failed to link pads!");
725- }
726-
727- gst_object_unref (sinkpad);
728+ return lut;
729 }
730
731 static void on_tag_available(
732@@ -121,22 +69,6 @@
733 {
734 (void) list;
735
736- static const std::map<std::string, std::string> gstreamer_to_mpris_tag_lut =
737- {
738- {GST_TAG_ALBUM, media::Engine::Xesam::album()},
739- {GST_TAG_ALBUM_ARTIST, media::Engine::Xesam::album_artist()},
740- {GST_TAG_ARTIST, media::Engine::Xesam::artist()},
741- {GST_TAG_LYRICS, media::Engine::Xesam::as_text()},
742- {GST_TAG_COMMENT, media::Engine::Xesam::comment()},
743- {GST_TAG_COMPOSER, media::Engine::Xesam::composer()},
744- {GST_TAG_DATE, media::Engine::Xesam::content_created()},
745- {GST_TAG_ALBUM_VOLUME_NUMBER, media::Engine::Xesam::disc_number()},
746- {GST_TAG_GENRE, media::Engine::Xesam::genre()},
747- {GST_TAG_TITLE, media::Engine::Xesam::title()},
748- {GST_TAG_TRACK_NUMBER, media::Engine::Xesam::track_number()},
749- {GST_TAG_USER_RATING, media::Engine::Xesam::user_rating()}
750- };
751-
752 auto md = static_cast<media::Track::MetaData*>(user_data);
753 std::stringstream ss;
754
755@@ -206,12 +138,86 @@
756 }
757
758 (*md).set(
759- (gstreamer_to_mpris_tag_lut.count(tag) > 0 ? gstreamer_to_mpris_tag_lut.at(tag) : tag),
760+ (gstreamer_to_mpris_tag_lut().count(tag) > 0 ? gstreamer_to_mpris_tag_lut().at(tag) : tag),
761 ss.str());
762 },
763 &md);
764 }
765
766+ MetaDataExtractor()
767+ : pipe(gst_pipeline_new("meta_data_extractor_pipeline")),
768+ decoder(gst_element_factory_make ("uridecodebin", NULL)),
769+ bus(GST_ELEMENT_BUS(pipe))
770+ {
771+ gst_bin_add(GST_BIN(pipe), decoder);
772+
773+ auto sink = gst_element_factory_make ("fakesink", NULL);
774+ gst_bin_add (GST_BIN (pipe), sink);
775+
776+ g_signal_connect (decoder, "pad-added", G_CALLBACK (on_new_pad), sink);
777+ }
778+
779+ ~MetaDataExtractor()
780+ {
781+ gst_element_set_state(pipe, GST_STATE_NULL);
782+ // gst_object_unref(pipe);
783+ }
784+
785+ core::ubuntu::media::Track::MetaData meta_data_for_track_with_uri(const core::ubuntu::media::Track::UriType& uri)
786+ {
787+ if (!gst_uri_is_valid(uri.c_str()))
788+ throw std::runtime_error("Invalid uri");
789+
790+ core::ubuntu::media::Track::MetaData meta_data;
791+ std::promise<core::ubuntu::media::Track::MetaData> promise;
792+ std::future<core::ubuntu::media::Track::MetaData> future{promise.get_future()};
793+
794+ core::ScopedConnection on_new_message_connection
795+ {
796+ bus.on_new_message.connect(
797+ [&](const gstreamer::Bus::Message& msg)
798+ {
799+ std::cout << __PRETTY_FUNCTION__ << gst_message_type_get_name(msg.type) << std::endl;
800+ if (msg.type == GST_MESSAGE_TAG)
801+ {
802+ MetaDataExtractor::on_tag_available(msg.detail.tag, meta_data);
803+ } else if (msg.type == GST_MESSAGE_ASYNC_DONE)
804+ {
805+ promise.set_value(meta_data);
806+ }
807+ })
808+ };
809+
810+ g_object_set(decoder, "uri", uri.c_str(), NULL);
811+ gst_element_set_state(pipe, GST_STATE_PAUSED);
812+
813+ if (std::future_status::ready != future.wait_for(std::chrono::seconds(2)))
814+ {
815+ gst_element_set_state(pipe, GST_STATE_NULL);
816+ throw std::runtime_error("Problem extracting meta data for track");
817+ } else
818+ {
819+ gst_element_set_state(pipe, GST_STATE_NULL);
820+ }
821+
822+ return future.get();
823+ }
824+
825+private:
826+ static void on_new_pad(GstElement*, GstPad* pad, GstElement* fakesink)
827+ {
828+ GstPad *sinkpad;
829+
830+ sinkpad = gst_element_get_static_pad (fakesink, "sink");
831+
832+ if (!gst_pad_is_linked (sinkpad)) {
833+ if (gst_pad_link (pad, sinkpad) != GST_PAD_LINK_OK)
834+ g_error ("Failed to link pads!");
835+ }
836+
837+ gst_object_unref (sinkpad);
838+ }
839+
840 GstElement* pipe;
841 GstElement* decoder;
842 Bus bus;
843
844=== modified file 'src/core/media/mpris/media_player2.h'
845--- src/core/media/mpris/media_player2.h 2014-09-10 21:05:53 +0000
846+++ src/core/media/mpris/media_player2.h 2014-09-10 21:05:53 +0000
847@@ -22,6 +22,8 @@
848 #include <core/dbus/macros.h>
849 #include <core/dbus/object.h>
850 #include <core/dbus/property.h>
851+#include <core/dbus/interfaces/properties.h>
852+#include <core/dbus/types/variant.h>
853
854 #include <string>
855 #include <vector>
856@@ -102,7 +104,20 @@
857 // The bus connection that should be used
858 core::dbus::Bus::Ptr bus;
859 // The dbus object that should implement org.mpris.MediaPlayer2
860- core::dbus::Object::Ptr object;
861+ core::dbus::Object::Ptr object;
862+ // Default values assigned to properties on construction
863+ struct Defaults
864+ {
865+ Properties::CanQuit::ValueType can_quit{false};
866+ Properties::Fullscreen::ValueType fullscreen{false};
867+ Properties::CanSetFullscreen::ValueType can_set_fullscreen{false};
868+ Properties::CanRaise::ValueType can_raise{false};
869+ Properties::HasTrackList::ValueType has_track_list{false};
870+ Properties::Identity::ValueType identity{};
871+ Properties::DesktopEntry::ValueType desktop_entry{};
872+ Properties::SupportedUriSchemes::ValueType supported_uri_schemes{};
873+ Properties::SupportedMimeTypes::ValueType supported_mime_types{};
874+ } defaults;
875 };
876
877 // Creates a new instance, sets up player properties and installs method handlers.
878@@ -119,8 +134,46 @@
879 configuration.object->get_property<Properties::DesktopEntry>(),
880 configuration.object->get_property<Properties::SupportedUriSchemes>(),
881 configuration.object->get_property<Properties::SupportedMimeTypes>()
882+ },
883+ signals
884+ {
885+ configuration.object->get_signal<core::dbus::interfaces::Properties::Signals::PropertiesChanged>()
886 }
887- {
888+ {
889+ // Initialize property values of the media_player instance.
890+ properties.can_quit->set(configuration.defaults.can_quit);
891+ properties.fullscreen->set(configuration.defaults.fullscreen);
892+ properties.can_set_fullscreen->set(configuration.defaults.can_set_fullscreen);
893+ properties.can_raise->set(configuration.defaults.can_raise);
894+ properties.has_track_list->set(configuration.defaults.has_track_list);
895+ properties.desktop_entry->set(configuration.defaults.desktop_entry);
896+ properties.identity->set(configuration.defaults.identity);
897+ properties.supported_mime_types->set(configuration.defaults.supported_mime_types);
898+ }
899+
900+ std::map<std::string, core::dbus::types::Variant> get_all_properties()
901+ {
902+ std::map<std::string, core::dbus::types::Variant> dict;
903+ dict[Properties::CanQuit::name()]
904+ = core::dbus::types::Variant::encode(properties.can_quit->get());
905+ dict[Properties::Fullscreen::name()]
906+ = core::dbus::types::Variant::encode(properties.fullscreen->get());
907+ dict[Properties::CanSetFullscreen::name()]
908+ = core::dbus::types::Variant::encode(properties.can_set_fullscreen->get());
909+ dict[Properties::CanRaise::name()]
910+ = core::dbus::types::Variant::encode(properties.can_raise->get());
911+ dict[Properties::HasTrackList::name()]
912+ = core::dbus::types::Variant::encode(properties.has_track_list->get());
913+ dict[Properties::CanSetFullscreen::name()]
914+ = core::dbus::types::Variant::encode(properties.can_set_fullscreen->get());
915+ dict[Properties::DesktopEntry::name()]
916+ = core::dbus::types::Variant::encode(properties.desktop_entry->get());
917+ dict[Properties::Identity::name()]
918+ = core::dbus::types::Variant::encode(properties.identity->get());
919+ dict[Properties::SupportedMimeTypes::name()]
920+ = core::dbus::types::Variant::encode(properties.supported_mime_types->get());
921+
922+ return dict;
923 }
924
925 // We just store creation time properties here.
926@@ -139,6 +192,15 @@
927 std::shared_ptr<core::dbus::Property<Properties::SupportedUriSchemes>> supported_uri_schemes;
928 std::shared_ptr<core::dbus::Property<Properties::SupportedMimeTypes>> supported_mime_types;
929 } properties;
930+
931+ struct
932+ {
933+ core::dbus::Signal
934+ <
935+ core::dbus::interfaces::Properties::Signals::PropertiesChanged,
936+ core::dbus::interfaces::Properties::Signals::PropertiesChanged::ArgumentType
937+ >::Ptr properties_changed;
938+ } signals;
939 };
940 };
941 }
942
943=== added file 'src/core/media/mpris/metadata.h'
944--- src/core/media/mpris/metadata.h 1970-01-01 00:00:00 +0000
945+++ src/core/media/mpris/metadata.h 2014-09-10 21:05:53 +0000
946@@ -0,0 +1,55 @@
947+/*
948+ * Copyright © 2014 Canonical Ltd.
949+ *
950+ * This program is free software: you can redistribute it and/or modify it
951+ * under the terms of the GNU Lesser General Public License version 3,
952+ * as published by the Free Software Foundation.
953+ *
954+ * This program is distributed in the hope that it will be useful,
955+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
956+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
957+ * GNU Lesser General Public License for more details.
958+ *
959+ * You should have received a copy of the GNU Lesser General Public License
960+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
961+ *
962+ * Authored by: Thomas Voß <thomas.voss@canonical.com>
963+ */
964+
965+#ifndef MPRIS_METADATA_H_
966+#define MPRIS_METADATA_H_
967+
968+#include <core/dbus/types/object_path.h>
969+
970+#include <cstdint>
971+
972+#include <string>
973+
974+namespace mpris
975+{
976+namespace metadata
977+{
978+// D-Bus path: A unique identity for this track within the context of an MPRIS object (eg: tracklist).
979+struct TrackId
980+{
981+ static constexpr const char* name{"mpris:trackid"};
982+ typedef core::dbus::types::ObjectPath ValueType;
983+};
984+// 64-bit integer: The duration of the track in microseconds.
985+struct Length
986+{
987+ static constexpr const char* name{"mpris:length"};
988+ typedef std::int64_t ValueType;
989+};
990+// URI: The location of an image representing the track or album.
991+// Clients should not assume this will continue to exist when the media player
992+// stops giving out the URL.
993+struct ArtUrl
994+{
995+ static constexpr const char* name{"mpris:artUrl"};
996+ typedef std::string ValueType;
997+};
998+}
999+}
1000+
1001+#endif // MPRIS_METADATA_H_
1002
1003=== modified file 'src/core/media/mpris/player.h'
1004--- src/core/media/mpris/player.h 2014-09-10 21:05:53 +0000
1005+++ src/core/media/mpris/player.h 2014-09-10 21:05:53 +0000
1006@@ -29,6 +29,7 @@
1007 #include <core/dbus/macros.h>
1008 #include <core/dbus/object.h>
1009 #include <core/dbus/property.h>
1010+#include <core/dbus/interfaces/properties.h>
1011 #include <core/dbus/types/any.h>
1012 #include <core/dbus/types/object_path.h>
1013 #include <core/dbus/types/variant.h>
1014@@ -55,6 +56,21 @@
1015 {
1016 LoopStatus() = delete;
1017
1018+ static const char* from(core::ubuntu::media::Player::LoopStatus status)
1019+ {
1020+ switch(status)
1021+ {
1022+ case core::ubuntu::media::Player::LoopStatus::none:
1023+ return LoopStatus::none;
1024+ case core::ubuntu::media::Player::LoopStatus::track:
1025+ return LoopStatus::track;
1026+ case core::ubuntu::media::Player::LoopStatus::playlist:
1027+ return LoopStatus::playlist;
1028+ }
1029+
1030+ return nullptr;
1031+ }
1032+
1033 static constexpr const char* none{"None"};
1034 static constexpr const char* track{"Track"};
1035 static constexpr const char* playlist{"Playlist"};
1036@@ -103,7 +119,7 @@
1037
1038 struct Signals
1039 {
1040- DBUS_CPP_SIGNAL_DEF(Seeked, Player, std::uint64_t)
1041+ DBUS_CPP_SIGNAL_DEF(Seeked, Player, std::int64_t)
1042 DBUS_CPP_SIGNAL_DEF(EndOfStream, Player, void)
1043 DBUS_CPP_SIGNAL_DEF(PlaybackStatusChanged, Player, core::ubuntu::media::Player::PlaybackStatus)
1044 };
1045@@ -119,11 +135,11 @@
1046 DBUS_CPP_WRITABLE_PROPERTY_DEF(PlaybackRate, Player, double)
1047 DBUS_CPP_WRITABLE_PROPERTY_DEF(Rate, Player, double)
1048 DBUS_CPP_WRITABLE_PROPERTY_DEF(Shuffle, Player, bool)
1049- DBUS_CPP_READABLE_PROPERTY_DEF(MetaData, Player, Dictionary)
1050+ DBUS_CPP_READABLE_PROPERTY_DEF(Metadata, Player, Dictionary)
1051 DBUS_CPP_READABLE_PROPERTY_DEF(TypedMetaData, Player, core::ubuntu::media::Track::MetaData)
1052 DBUS_CPP_WRITABLE_PROPERTY_DEF(Volume, Player, double)
1053- DBUS_CPP_READABLE_PROPERTY_DEF(Position, Player, std::uint64_t)
1054- DBUS_CPP_READABLE_PROPERTY_DEF(Duration, Player, std::uint64_t)
1055+ DBUS_CPP_READABLE_PROPERTY_DEF(Position, Player, std::int64_t)
1056+ DBUS_CPP_READABLE_PROPERTY_DEF(Duration, Player, std::int64_t)
1057 DBUS_CPP_READABLE_PROPERTY_DEF(MinimumRate, Player, double)
1058 DBUS_CPP_READABLE_PROPERTY_DEF(MaximumRate, Player, double)
1059 DBUS_CPP_READABLE_PROPERTY_DEF(IsVideoSource, Player, bool)
1060@@ -139,6 +155,11 @@
1061 // Convenience struct to create a skeleton implementation for org.mpris.MediaPlayer2.Player
1062 struct Skeleton
1063 {
1064+ static const std::vector<std::string>& the_empty_list_of_invalidated_properties()
1065+ {
1066+ static const std::vector<std::string> instance; return instance;
1067+ }
1068+
1069 // Creation time options go here.
1070 struct Configuration
1071 {
1072@@ -146,41 +167,141 @@
1073 core::dbus::Bus::Ptr bus;
1074 // The dbus object that should implement org.mpris.MediaPlayer2
1075 core::dbus::Object::Ptr object;
1076+
1077+ // Default values for properties
1078+ struct Defaults
1079+ {
1080+ Properties::CanPlay::ValueType can_play{true};
1081+ Properties::CanPause::ValueType can_pause{true};
1082+ Properties::CanSeek::ValueType can_seek{true};
1083+ Properties::CanControl::ValueType can_control{true};
1084+ Properties::CanGoNext::ValueType can_go_next{true};
1085+ Properties::CanGoPrevious::ValueType can_go_previous{true};
1086+ Properties::IsVideoSource::ValueType is_video_source{false};
1087+ Properties::IsAudioSource::ValueType is_audio_source{true};
1088+ Properties::PlaybackStatus::ValueType playback_status{PlaybackStatus::stopped};
1089+ Properties::TypedPlaybackStatus::ValueType typed_playback_status{core::ubuntu::media::Player::PlaybackStatus::null};
1090+ Properties::LoopStatus::ValueType loop_status{LoopStatus::none};
1091+ Properties::TypedLoopStatus::ValueType typed_loop_status{core::ubuntu::media::Player::LoopStatus::none};
1092+ Properties::PlaybackRate::ValueType playback_rate{1.f};
1093+ Properties::Shuffle::ValueType shuffle{false};
1094+ Properties::TypedMetaData::ValueType typed_meta_data{};
1095+ Properties::Volume::ValueType volume{0.f};
1096+ Properties::Position::ValueType position{0};
1097+ Properties::Duration::ValueType duration{0};
1098+ Properties::MinimumRate::ValueType minimum_rate{1.f};
1099+ Properties::MaximumRate::ValueType maximum_rate{1.f};
1100+ } defaults;
1101 };
1102
1103 Skeleton(const Configuration& configuration)
1104 : configuration(configuration),
1105 properties
1106 {
1107- configuration.object->template get_property<mpris::Player::Properties::CanPlay>(),
1108- configuration.object->template get_property<mpris::Player::Properties::CanPause>(),
1109- configuration.object->template get_property<mpris::Player::Properties::CanSeek>(),
1110- configuration.object->template get_property<mpris::Player::Properties::CanControl>(),
1111- configuration.object->template get_property<mpris::Player::Properties::CanGoNext>(),
1112- configuration.object->template get_property<mpris::Player::Properties::CanGoPrevious>(),
1113- configuration.object->template get_property<mpris::Player::Properties::IsVideoSource>(),
1114- configuration.object->template get_property<mpris::Player::Properties::IsAudioSource>(),
1115- configuration.object->template get_property<mpris::Player::Properties::PlaybackStatus>(),
1116- configuration.object->template get_property<mpris::Player::Properties::TypedPlaybackStatus>(),
1117- configuration.object->template get_property<mpris::Player::Properties::LoopStatus>(),
1118- configuration.object->template get_property<mpris::Player::Properties::TypedLoopStatus>(),
1119- configuration.object->template get_property<mpris::Player::Properties::AudioStreamRole>(),
1120- configuration.object->template get_property<mpris::Player::Properties::PlaybackRate>(),
1121- configuration.object->template get_property<mpris::Player::Properties::Shuffle>(),
1122- configuration.object->template get_property<mpris::Player::Properties::TypedMetaData>(),
1123- configuration.object->template get_property<mpris::Player::Properties::Volume>(),
1124- configuration.object->template get_property<mpris::Player::Properties::Position>(),
1125- configuration.object->template get_property<mpris::Player::Properties::Duration>(),
1126- configuration.object->template get_property<mpris::Player::Properties::MinimumRate>(),
1127- configuration.object->template get_property<mpris::Player::Properties::MaximumRate>()
1128+ configuration.object->template get_property<Properties::CanPlay>(),
1129+ configuration.object->template get_property<Properties::CanPause>(),
1130+ configuration.object->template get_property<Properties::CanSeek>(),
1131+ configuration.object->template get_property<Properties::CanControl>(),
1132+ configuration.object->template get_property<Properties::CanGoNext>(),
1133+ configuration.object->template get_property<Properties::CanGoPrevious>(),
1134+ configuration.object->template get_property<Properties::IsVideoSource>(),
1135+ configuration.object->template get_property<Properties::IsAudioSource>(),
1136+ configuration.object->template get_property<Properties::PlaybackStatus>(),
1137+ configuration.object->template get_property<Properties::TypedPlaybackStatus>(),
1138+ configuration.object->template get_property<Properties::LoopStatus>(),
1139+ configuration.object->template get_property<Properties::TypedLoopStatus>(),
1140+ configuration.object->template get_property<Properties::AudioStreamRole>(),
1141+ configuration.object->template get_property<Properties::PlaybackRate>(),
1142+ configuration.object->template get_property<Properties::Shuffle>(),
1143+ configuration.object->template get_property<Properties::TypedMetaData>(),
1144+ configuration.object->template get_property<Properties::Volume>(),
1145+ configuration.object->template get_property<Properties::Position>(),
1146+ configuration.object->template get_property<Properties::Duration>(),
1147+ configuration.object->template get_property<Properties::MinimumRate>(),
1148+ configuration.object->template get_property<Properties::MaximumRate>()
1149 },
1150 signals
1151 {
1152- configuration.object->template get_signal<mpris::Player::Signals::Seeked>(),
1153- configuration.object->template get_signal<mpris::Player::Signals::EndOfStream>(),
1154- configuration.object->template get_signal<mpris::Player::Signals::PlaybackStatusChanged>()
1155+ configuration.object->template get_signal<Signals::Seeked>(),
1156+ configuration.object->template get_signal<Signals::EndOfStream>(),
1157+ configuration.object->template get_signal<Signals::PlaybackStatusChanged>(),
1158+ configuration.object->template get_signal<core::dbus::interfaces::Properties::Signals::PropertiesChanged>()
1159 }
1160- {
1161+ {
1162+ properties.can_play->set(configuration.defaults.can_play);
1163+ properties.can_pause->set(configuration.defaults.can_pause);
1164+ properties.can_seek->set(configuration.defaults.can_seek);
1165+ properties.can_control->set(configuration.defaults.can_control);
1166+ properties.can_go_next->set(configuration.defaults.can_go_next);
1167+ properties.can_go_previous->set(configuration.defaults.can_go_previous);
1168+ properties.is_video_source->set(configuration.defaults.is_video_source);
1169+ properties.is_audio_source->set(configuration.defaults.is_audio_source);
1170+ properties.playback_status->set(configuration.defaults.playback_status);
1171+ properties.typed_playback_status->set(configuration.defaults.typed_playback_status);
1172+ properties.loop_status->set(configuration.defaults.loop_status);
1173+ properties.typed_loop_status->set(configuration.defaults.typed_loop_status);
1174+ properties.audio_stream_role->set(core::ubuntu::media::Player::AudioStreamRole::multimedia);
1175+ properties.playback_rate->set(configuration.defaults.playback_rate);
1176+ properties.is_shuffle->set(configuration.defaults.shuffle);
1177+ properties.position->set(configuration.defaults.position);
1178+ properties.duration->set(configuration.defaults.duration);
1179+ properties.minimum_playback_rate->set(configuration.defaults.minimum_rate);
1180+ properties.maximum_playback_rate->set(configuration.defaults.maximum_rate);
1181+
1182+ properties.position->changed().connect([this](std::int64_t position)
1183+ {
1184+ on_property_value_changed<Properties::Position>(position);
1185+ });
1186+
1187+ properties.duration->changed().connect([this](std::int64_t duration)
1188+ {
1189+ on_property_value_changed<Properties::Duration>(duration);
1190+ });
1191+
1192+ properties.playback_status->changed().connect([this](const std::string& status)
1193+ {
1194+ on_property_value_changed<Properties::PlaybackStatus>(status);
1195+ });
1196+
1197+ properties.loop_status->changed().connect([this](const std::string& status)
1198+ {
1199+ on_property_value_changed<Properties::LoopStatus>(status);
1200+ });
1201+ }
1202+
1203+ template<typename Property>
1204+ void on_property_value_changed(const typename Property::ValueType& value)
1205+ {
1206+ Dictionary dict; dict[Property::name()] = dbus::types::Variant::encode(value);
1207+
1208+ signals.properties_changed->emit(std::make_tuple(
1209+ dbus::traits::Service<Player>::interface_name(),
1210+ dict,
1211+ the_empty_list_of_invalidated_properties()));
1212+ }
1213+
1214+ Dictionary get_all_properties()
1215+ {
1216+ Dictionary dict;
1217+ dict[Properties::CanPlay::name()] = dbus::types::Variant::encode(properties.can_play->get());
1218+ dict[Properties::CanPause::name()] = dbus::types::Variant::encode(properties.can_pause->get());
1219+ dict[Properties::CanSeek::name()] = dbus::types::Variant::encode(properties.can_seek->get());
1220+ dict[Properties::CanControl::name()] = dbus::types::Variant::encode(properties.can_control->get());
1221+ dict[Properties::CanGoNext::name()] = dbus::types::Variant::encode(properties.can_go_next->get());
1222+ dict[Properties::CanGoPrevious::name()] = dbus::types::Variant::encode(properties.can_go_previous->get());
1223+ dict[Properties::PlaybackStatus::name()] = dbus::types::Variant::encode(properties.playback_status->get());
1224+ dict[Properties::TypedPlaybackStatus::name()] = dbus::types::Variant::encode(properties.typed_playback_status->get());
1225+ dict[Properties::LoopStatus::name()] = dbus::types::Variant::encode(properties.loop_status->get());
1226+ dict[Properties::TypedLoopStatus::name()] = dbus::types::Variant::encode(properties.typed_loop_status->get());
1227+ dict[Properties::AudioStreamRole::name()] = dbus::types::Variant::encode(properties.audio_stream_role->get());
1228+ dict[Properties::PlaybackRate::name()] = dbus::types::Variant::encode(properties.playback_rate->get());
1229+ dict[Properties::Shuffle::name()] = dbus::types::Variant::encode(properties.is_shuffle->get());
1230+ dict[Properties::Duration::name()] = dbus::types::Variant::encode(properties.duration->get());
1231+ dict[Properties::Position::name()] = dbus::types::Variant::encode(properties.position->get());
1232+ dict[Properties::MinimumRate::name()] = dbus::types::Variant::encode(properties.minimum_playback_rate->get());
1233+ dict[Properties::MaximumRate::name()] = dbus::types::Variant::encode(properties.maximum_playback_rate->get());
1234+
1235+ return dict;
1236 }
1237
1238 // We just store creation time properties
1239@@ -188,34 +309,41 @@
1240 // All the properties exposed to the bus go here.
1241 struct
1242 {
1243- std::shared_ptr<core::dbus::Property<mpris::Player::Properties::CanPlay>> can_play;
1244- std::shared_ptr<core::dbus::Property<mpris::Player::Properties::CanPause>> can_pause;
1245- std::shared_ptr<core::dbus::Property<mpris::Player::Properties::CanSeek>> can_seek;
1246- std::shared_ptr<core::dbus::Property<mpris::Player::Properties::CanControl>> can_control;
1247- std::shared_ptr<core::dbus::Property<mpris::Player::Properties::CanGoNext>> can_go_next;
1248- std::shared_ptr<core::dbus::Property<mpris::Player::Properties::CanGoPrevious>> can_go_previous;
1249- std::shared_ptr<core::dbus::Property<mpris::Player::Properties::IsVideoSource>> is_video_source;
1250- std::shared_ptr<core::dbus::Property<mpris::Player::Properties::IsAudioSource>> is_audio_source;
1251- std::shared_ptr<core::dbus::Property<mpris::Player::Properties::PlaybackStatus>> playback_status;
1252- std::shared_ptr<core::dbus::Property<mpris::Player::Properties::TypedPlaybackStatus>> typed_playback_status;
1253- std::shared_ptr<core::dbus::Property<mpris::Player::Properties::LoopStatus>> loop_status;
1254- std::shared_ptr<core::dbus::Property<mpris::Player::Properties::TypedLoopStatus>> typed_loop_status;
1255- std::shared_ptr<core::dbus::Property<mpris::Player::Properties::AudioStreamRole>> audio_stream_role;
1256- std::shared_ptr<core::dbus::Property<mpris::Player::Properties::PlaybackRate>> playback_rate;
1257- std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Shuffle>> is_shuffle;
1258- std::shared_ptr<core::dbus::Property<mpris::Player::Properties::TypedMetaData>> meta_data_for_current_track;
1259- std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Volume>> volume;
1260- std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Position>> position;
1261- std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Duration>> duration;
1262- std::shared_ptr<core::dbus::Property<mpris::Player::Properties::MinimumRate>> minimum_playback_rate;
1263- std::shared_ptr<core::dbus::Property<mpris::Player::Properties::MaximumRate>> maximum_playback_rate;
1264+ std::shared_ptr<core::dbus::Property<Properties::CanPlay>> can_play;
1265+ std::shared_ptr<core::dbus::Property<Properties::CanPause>> can_pause;
1266+ std::shared_ptr<core::dbus::Property<Properties::CanSeek>> can_seek;
1267+ std::shared_ptr<core::dbus::Property<Properties::CanControl>> can_control;
1268+ std::shared_ptr<core::dbus::Property<Properties::CanGoNext>> can_go_next;
1269+ std::shared_ptr<core::dbus::Property<Properties::CanGoPrevious>> can_go_previous;
1270+ std::shared_ptr<core::dbus::Property<Properties::IsVideoSource>> is_video_source;
1271+ std::shared_ptr<core::dbus::Property<Properties::IsAudioSource>> is_audio_source;
1272+
1273+ std::shared_ptr<core::dbus::Property<Properties::PlaybackStatus>> playback_status;
1274+ std::shared_ptr<core::dbus::Property<Properties::TypedPlaybackStatus>> typed_playback_status;
1275+ std::shared_ptr<core::dbus::Property<Properties::LoopStatus>> loop_status;
1276+ std::shared_ptr<core::dbus::Property<Properties::TypedLoopStatus>> typed_loop_status;
1277+ std::shared_ptr<core::dbus::Property<Properties::AudioStreamRole>> audio_stream_role;
1278+ std::shared_ptr<core::dbus::Property<Properties::PlaybackRate>> playback_rate;
1279+ std::shared_ptr<core::dbus::Property<Properties::Shuffle>> is_shuffle;
1280+ std::shared_ptr<core::dbus::Property<Properties::TypedMetaData>> typed_meta_data_for_current_track;
1281+ std::shared_ptr<core::dbus::Property<Properties::Volume>> volume;
1282+ std::shared_ptr<core::dbus::Property<Properties::Position>> position;
1283+ std::shared_ptr<core::dbus::Property<Properties::Duration>> duration;
1284+ std::shared_ptr<core::dbus::Property<Properties::MinimumRate>> minimum_playback_rate;
1285+ std::shared_ptr<core::dbus::Property<Properties::MaximumRate>> maximum_playback_rate;
1286 } properties;
1287
1288 struct
1289 {
1290- typename core::dbus::Signal<mpris::Player::Signals::Seeked, mpris::Player::Signals::Seeked::ArgumentType>::Ptr seeked_to;
1291- typename core::dbus::Signal<mpris::Player::Signals::EndOfStream, mpris::Player::Signals::EndOfStream::ArgumentType>::Ptr end_of_stream;
1292- typename core::dbus::Signal<mpris::Player::Signals::PlaybackStatusChanged, mpris::Player::Signals::PlaybackStatusChanged::ArgumentType>::Ptr playback_status_changed;
1293+ typename core::dbus::Signal<Signals::Seeked, Signals::Seeked::ArgumentType>::Ptr seeked_to;
1294+ typename core::dbus::Signal<Signals::EndOfStream, Signals::EndOfStream::ArgumentType>::Ptr end_of_stream;
1295+ typename core::dbus::Signal<Signals::PlaybackStatusChanged, Signals::PlaybackStatusChanged::ArgumentType>::Ptr playback_status_changed;
1296+
1297+ dbus::Signal
1298+ <
1299+ core::dbus::interfaces::Properties::Signals::PropertiesChanged,
1300+ core::dbus::interfaces::Properties::Signals::PropertiesChanged::ArgumentType
1301+ >::Ptr properties_changed;
1302 } signals;
1303 };
1304 };
1305
1306=== added file 'src/core/media/mpris/playlists.h'
1307--- src/core/media/mpris/playlists.h 1970-01-01 00:00:00 +0000
1308+++ src/core/media/mpris/playlists.h 2014-09-10 21:05:53 +0000
1309@@ -0,0 +1,216 @@
1310+/*
1311+ * Copyright © 2014 Canonical Ltd.
1312+ *
1313+ * This program is free software: you can redistribute it and/or modify it
1314+ * under the terms of the GNU Lesser General Public License version 3,
1315+ * as published by the Free Software Foundation.
1316+ *
1317+ * This program is distributed in the hope that it will be useful,
1318+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1319+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1320+ * GNU Lesser General Public License for more details.
1321+ *
1322+ * You should have received a copy of the GNU Lesser General Public License
1323+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1324+ *
1325+ * Authored by: Thomas Voß <thomas.voss@canonical.com>
1326+ */
1327+
1328+#ifndef MPRIS_PLAYLISTS_H_
1329+#define MPRIS_PLAYLISTS_H_
1330+
1331+#include <core/dbus/macros.h>
1332+#include <core/dbus/object.h>
1333+#include <core/dbus/property.h>
1334+#include <core/dbus/interfaces/properties.h>
1335+#include <core/dbus/types/struct.h>
1336+#include <core/dbus/types/variant.h>
1337+
1338+#include <string>
1339+#include <vector>
1340+
1341+namespace core
1342+{
1343+namespace dbus
1344+{
1345+namespace types
1346+{
1347+template<typename T>
1348+bool operator !=(const Struct<T>& lhs, const Struct<T>& rhs)
1349+{
1350+ return lhs.value != rhs.value;
1351+}
1352+}
1353+}
1354+}
1355+
1356+namespace mpris
1357+{
1358+// Models interface org.mpris.MediaPlayer2.Playlists, see:
1359+// http://specifications.freedesktop.org/mpris-spec/latest/Playlists_Interface.html
1360+// for detailed documentation
1361+struct Playlists
1362+{
1363+ static const std::string& name()
1364+ {
1365+ static const std::string s{"org.mpris.MediaPlayer2.Playlists"}; return s;
1366+ }
1367+
1368+ // All known orderings
1369+ struct Orderings
1370+ {
1371+ // Alphabetical ordering by name, ascending.
1372+ static constexpr const char* alphabetical{"Alphabetical"};
1373+ // Ordering by creation date, oldest first.
1374+ static constexpr const char* creation_date{"CreationDate"};
1375+ // Ordering by last modified date, oldest first.
1376+ static constexpr const char* modified_date{"ModifiedDate"};
1377+ // Ordering by date of last playback, oldest first.
1378+ static constexpr const char* last_play_date{"LastPlayDate"};
1379+ // A user-defined ordering.
1380+ static constexpr const char* user_defined{"UserDefined"};
1381+ };
1382+
1383+ // A datastructure describing a playlist.
1384+ typedef core::dbus::types::Struct
1385+ <
1386+ std::tuple
1387+ <
1388+ // A unique identifier for the playlist.
1389+ // This should remain the same if the playlist is renamed.
1390+ core::dbus::types::ObjectPath,
1391+ // The name of the playlist, typically given by the user.
1392+ std::string,
1393+ // The URI of an (optional) icon.
1394+ std::string
1395+ >
1396+ > Playlist;
1397+
1398+ // A data structure describing a playlist, or nothing.
1399+ typedef core::dbus::types::Struct
1400+ <
1401+ std::tuple
1402+ <
1403+ // Whether this structure refers to a valid playlist.
1404+ bool,
1405+ // The playlist, providing Valid is true, otherwise undefined.
1406+ Playlist
1407+ >
1408+ > MaybePlaylist;
1409+
1410+ struct Methods
1411+ {
1412+ Methods() = delete;
1413+
1414+ // Starts playing the given playlist.
1415+ // Note that this must be implemented. If the media player does not
1416+ // allow clients to change the playlist, it should not implement this
1417+ // interface at all.
1418+ DBUS_CPP_METHOD_DEF(ActivatePlaylist, Playlists)
1419+
1420+ // Gets a set of playlists.
1421+ // Parameters
1422+ // Index — u
1423+ // The index of the first playlist to be fetched (according to the ordering).
1424+ // MaxCount — u
1425+ // The maximum number of playlists to fetch.
1426+ // Order — s (Playlist_Ordering)
1427+ // The ordering that should be used.
1428+ // ReverseOrder — b
1429+ // Whether the order should be reversed.
1430+ //
1431+ // Returns
1432+ // Playlists — a(oss) (Playlist_List)
1433+ // A list of (at most MaxCount) playlists.
1434+ DBUS_CPP_METHOD_DEF(GetPlaylists, Playlists)
1435+ };
1436+
1437+ struct Signals
1438+ {
1439+ Signals() = delete;
1440+
1441+ // Indicates that either the Name or Icon attribute of a playlist has changed.
1442+ // Client implementations should be aware that this signal may not be implemented.
1443+ DBUS_CPP_SIGNAL_DEF(PlaylistsChanged, Playlists, Playlist)
1444+ };
1445+
1446+ struct Properties
1447+ {
1448+ Properties() = delete;
1449+
1450+ // The number of playlists available.
1451+ DBUS_CPP_READABLE_PROPERTY_DEF(PlaylistCount, Playlists, std::uint32_t)
1452+ // The available orderings. At least one must be offered.
1453+ DBUS_CPP_READABLE_PROPERTY_DEF(Orderings, Playlists, std::vector<std::string>)
1454+ // The currently-active playlist.
1455+ DBUS_CPP_READABLE_PROPERTY_DEF(ActivePlaylist, Playlists, MaybePlaylist)
1456+ };
1457+
1458+ struct Skeleton
1459+ {
1460+ // Creation time properties go here.
1461+ struct Configuration
1462+ {
1463+ // The bus connection that should be used
1464+ core::dbus::Bus::Ptr bus;
1465+ // The dbus object that should implement org.mpris.MediaPlayer2
1466+ core::dbus::Object::Ptr object;
1467+ // Default values assigned to properties on construction
1468+ struct Defaults
1469+ {
1470+ Properties::PlaylistCount::ValueType playlist_count{0};
1471+ Properties::Orderings::ValueType orderings{{Orderings::alphabetical}};
1472+ Properties::ActivePlaylist::ValueType active_playlist
1473+ {
1474+ std::make_tuple(false, Playlist
1475+ {
1476+ std::make_tuple(
1477+ core::dbus::types::ObjectPath{"/"},
1478+ std::string{},
1479+ std::string{})
1480+ })
1481+ };
1482+ } defaults;
1483+ };
1484+
1485+ Skeleton(const Configuration& configuration)
1486+ : configuration(configuration),
1487+ properties
1488+ {
1489+ configuration.object->get_property<Properties::PlaylistCount>(),
1490+ configuration.object->get_property<Properties::Orderings>()
1491+ },
1492+ signals
1493+ {
1494+ configuration.object->get_signal<Signals::PlaylistsChanged>()
1495+ }
1496+ {
1497+ properties.playlist_count->set(configuration.defaults.playlist_count);
1498+ properties.orderings->set(configuration.defaults.orderings);
1499+ }
1500+
1501+ std::map<std::string, core::dbus::types::Variant> get_all_properties()
1502+ {
1503+ std::map<std::string, core::dbus::types::Variant> dict;
1504+ dict[Properties::PlaylistCount::name()] = core::dbus::types::Variant::encode(properties.playlist_count->get());
1505+ dict[Properties::Orderings::name()] = core::dbus::types::Variant::encode(properties.orderings->get());
1506+
1507+ return dict;
1508+ }
1509+
1510+ Configuration configuration;
1511+
1512+ struct
1513+ {
1514+ std::shared_ptr<core::dbus::Property<Properties::PlaylistCount>> playlist_count;
1515+ std::shared_ptr<core::dbus::Property<Properties::Orderings>> orderings;
1516+ } properties;
1517+
1518+ struct
1519+ {
1520+ core::dbus::Signal<Signals::PlaylistsChanged, Signals::PlaylistsChanged::ArgumentType>::Ptr playlist_changed;
1521+ } signals;
1522+ };
1523+};
1524+}
1525+#endif // MPRIS_PLAYLISTS_H_
1526
1527=== modified file 'src/core/media/player.cpp'
1528--- src/core/media/player.cpp 2014-02-12 15:53:57 +0000
1529+++ src/core/media/player.cpp 2014-09-10 21:05:53 +0000
1530@@ -24,7 +24,13 @@
1531
1532 const media::Player::Configuration& media::Player::Client::default_configuration()
1533 {
1534- static const media::Player::Configuration config;
1535+ static const media::Player::Configuration config
1536+ {
1537+ std::string{""},
1538+ 0,
1539+ nullptr,
1540+ nullptr
1541+ };
1542 return config;
1543 }
1544
1545
1546=== modified file 'src/core/media/player_configuration.h'
1547--- src/core/media/player_configuration.h 2014-09-10 21:05:53 +0000
1548+++ src/core/media/player_configuration.h 2014-09-10 21:05:53 +0000
1549@@ -23,9 +23,18 @@
1550 #include <core/dbus/bus.h>
1551 #include <core/dbus/object.h>
1552
1553+// Our internal structure for handing down parameters from the skeleton
1554+// to the implementation in a way that is opaque to the client.
1555 struct core::ubuntu::media::Player::Configuration
1556 {
1557+ // An identifier that is helpful in referencing the player instance
1558+ // across multiple services.
1559+ std::string identity;
1560+ // Unique key for identifying the session.
1561+ core::ubuntu::media::Player::PlayerKey key;
1562+ // The bus connection to expose objects on.
1563 std::shared_ptr<core::dbus::Bus> bus;
1564+ // The actual session object representing a player instance.
1565 std::shared_ptr<core::dbus::Object> session;
1566 };
1567
1568
1569=== modified file 'src/core/media/player_implementation.cpp'
1570--- src/core/media/player_implementation.cpp 2014-09-10 21:05:53 +0000
1571+++ src/core/media/player_implementation.cpp 2014-09-10 21:05:53 +0000
1572@@ -47,7 +47,7 @@
1573 WAKELOCK_CLEAR_DISPLAY,
1574 WAKELOCK_CLEAR_SYSTEM,
1575 WAKELOCK_CLEAR_INVALID
1576- };
1577+ };
1578
1579 Private(PlayerImplementation* parent,
1580 const dbus::types::ObjectPath& session_path,
1581@@ -102,6 +102,10 @@
1582 }
1583 case Engine::State::playing:
1584 {
1585+ // We update the track meta data prior to updating the playback status.
1586+ // Some MPRIS clients expect this order of events.
1587+ parent->meta_data_for_current_track().set(std::get<1>(engine->track_meta_data().get()));
1588+ // And update our playback status.
1589 parent->playback_status().set(media::Player::playing);
1590 if (previous_state == Engine::State::stopped || previous_state == Engine::State::paused)
1591 {
1592@@ -135,7 +139,6 @@
1593 // Keep track of the previous Engine playback state:
1594 previous_state = state;
1595 });
1596-
1597 }
1598
1599 ~Private()
1600@@ -281,14 +284,24 @@
1601 std::unique_ptr<timeout> wakelock_timeout;
1602 Engine::State previous_state;
1603 PlayerImplementation::PlayerKey key;
1604+ core::Signal<> on_client_disconnected;
1605 };
1606
1607 media::PlayerImplementation::PlayerImplementation(
1608+ const std::string& identity,
1609 const std::shared_ptr<core::dbus::Bus>& bus,
1610 const std::shared_ptr<core::dbus::Object>& session,
1611 const std::shared_ptr<Service>& service,
1612 PlayerKey key)
1613- : media::PlayerSkeleton(bus, session),
1614+ : media::PlayerSkeleton
1615+ {
1616+ media::PlayerSkeleton::Configuration
1617+ {
1618+ bus,
1619+ session,
1620+ identity
1621+ }
1622+ },
1623 d(new Private(
1624 this,
1625 session->path(),
1626@@ -362,6 +375,8 @@
1627 // If the client disconnects, make sure both wakelock types
1628 // are cleared
1629 d->clear_wakelocks();
1630+ // And tell the outside world that the client has gone away
1631+ d->on_client_disconnected();
1632 });
1633
1634 d->engine->seeked_to_signal().connect([this](uint64_t value)
1635@@ -431,6 +446,7 @@
1636
1637 void media::PlayerImplementation::stop()
1638 {
1639+ std::cout << __PRETTY_FUNCTION__ << std::endl;
1640 d->engine->stop();
1641 }
1642
1643@@ -450,3 +466,8 @@
1644 {
1645 d->engine->seek_to(ms);
1646 }
1647+
1648+const core::Signal<>& media::PlayerImplementation::on_client_disconnected() const
1649+{
1650+ return d->on_client_disconnected;
1651+}
1652
1653=== modified file 'src/core/media/player_implementation.h'
1654--- src/core/media/player_implementation.h 2014-09-10 21:05:53 +0000
1655+++ src/core/media/player_implementation.h 2014-09-10 21:05:53 +0000
1656@@ -36,6 +36,7 @@
1657 {
1658 public:
1659 PlayerImplementation(
1660+ const std::string& identity,
1661 const std::shared_ptr<core::dbus::Bus>& bus,
1662 const std::shared_ptr<core::dbus::Object>& session,
1663 const std::shared_ptr<Service>& service,
1664@@ -57,6 +58,7 @@
1665 virtual void set_playback_complete_callback(PlaybackCompleteCb cb, void *context);
1666 virtual void seek_to(const std::chrono::microseconds& offset);
1667
1668+ const core::Signal<>& on_client_disconnected() const;
1669 private:
1670 struct Private;
1671 std::unique_ptr<Private> d;
1672
1673=== modified file 'src/core/media/player_skeleton.cpp'
1674--- src/core/media/player_skeleton.cpp 2014-09-10 21:05:53 +0000
1675+++ src/core/media/player_skeleton.cpp 2014-09-10 21:05:53 +0000
1676@@ -23,56 +23,44 @@
1677 #include "player_traits.h"
1678 #include "property_stub.h"
1679 #include "the_session_bus.h"
1680+#include "xesam.h"
1681
1682+#include "mpris/media_player2.h"
1683+#include "mpris/metadata.h"
1684 #include "mpris/player.h"
1685+#include "mpris/playlists.h"
1686
1687 #include <core/dbus/object.h>
1688 #include <core/dbus/property.h>
1689 #include <core/dbus/stub.h>
1690+
1691 #include <core/dbus/asio/executor.h>
1692+#include <core/dbus/interfaces/properties.h>
1693
1694 namespace dbus = core::dbus;
1695 namespace media = core::ubuntu::media;
1696
1697-struct media::PlayerSkeleton::Private
1698+struct media::PlayerSkeleton::Private : public std::enable_shared_from_this<media::PlayerSkeleton::Private>
1699 {
1700 Private(media::PlayerSkeleton* player,
1701+ const std::string& identity,
1702 const std::shared_ptr<core::dbus::Bus>& bus,
1703 const std::shared_ptr<core::dbus::Object>& session)
1704 : impl(player),
1705+ identity(identity),
1706 bus(bus),
1707 object(session),
1708 apparmor_session(nullptr),
1709- properties
1710- {
1711- object->get_property<mpris::Player::Properties::CanPlay>(),
1712- object->get_property<mpris::Player::Properties::CanPause>(),
1713- object->get_property<mpris::Player::Properties::CanSeek>(),
1714- object->get_property<mpris::Player::Properties::CanControl>(),
1715- object->get_property<mpris::Player::Properties::CanGoNext>(),
1716- object->get_property<mpris::Player::Properties::CanGoPrevious>(),
1717- object->get_property<mpris::Player::Properties::IsVideoSource>(),
1718- object->get_property<mpris::Player::Properties::IsAudioSource>(),
1719- object->get_property<mpris::Player::Properties::TypedPlaybackStatus>(),
1720- object->get_property<mpris::Player::Properties::TypedLoopStatus>(),
1721- object->get_property<mpris::Player::Properties::PlaybackRate>(),
1722- object->get_property<mpris::Player::Properties::Shuffle>(),
1723- object->get_property<mpris::Player::Properties::TypedMetaData>(),
1724- object->get_property<mpris::Player::Properties::Volume>(),
1725- object->get_property<mpris::Player::Properties::Position>(),
1726- object->get_property<mpris::Player::Properties::Duration>(),
1727- object->get_property<mpris::Player::Properties::AudioStreamRole>(),
1728- object->get_property<mpris::Player::Properties::MinimumRate>(),
1729- object->get_property<mpris::Player::Properties::MaximumRate>()
1730- },
1731+ dbus_stub{bus},
1732+ skeleton{mpris::Player::Skeleton::Configuration{bus, session, mpris::Player::Skeleton::Configuration::Defaults{}}},
1733 signals
1734 {
1735- object->get_signal<mpris::Player::Signals::Seeked>(),
1736- object->get_signal<mpris::Player::Signals::EndOfStream>(),
1737- object->get_signal<mpris::Player::Signals::PlaybackStatusChanged>()
1738+ skeleton.signals.seeked_to,
1739+ skeleton.signals.end_of_stream,
1740+ skeleton.signals.playback_status_changed
1741 }
1742 {
1743- }
1744+ }
1745
1746 void handle_next(const core::dbus::Message::Ptr& msg)
1747 {
1748@@ -95,10 +83,6 @@
1749 bus->send(reply);
1750 }
1751
1752- void handle_playpause(DBusMessage*)
1753- {
1754- }
1755-
1756 void handle_stop(const core::dbus::Message::Ptr& msg)
1757 {
1758 impl->stop();
1759@@ -113,6 +97,25 @@
1760 bus->send(reply);
1761 }
1762
1763+ void handle_play_pause(const core::dbus::Message::Ptr& msg)
1764+ {
1765+ switch(impl->playback_status().get())
1766+ {
1767+ case core::ubuntu::media::Player::PlaybackStatus::ready:
1768+ case core::ubuntu::media::Player::PlaybackStatus::paused:
1769+ case core::ubuntu::media::Player::PlaybackStatus::stopped:
1770+ impl->play();
1771+ break;
1772+ case core::ubuntu::media::Player::PlaybackStatus::playing:
1773+ impl->pause();
1774+ break;
1775+ default:
1776+ break;
1777+ }
1778+
1779+ bus->send(dbus::Message::make_method_return(msg));
1780+ }
1781+
1782 void handle_seek(const core::dbus::Message::Ptr& in)
1783 {
1784 uint64_t ticks;
1785@@ -135,25 +138,7 @@
1786
1787 auto reply = dbus::Message::make_method_return(in);
1788 bus->send(reply);
1789- }
1790-
1791- std::string get_client_apparmor_context(const core::dbus::Message::Ptr& msg)
1792- {
1793- auto bus = std::shared_ptr<dbus::Bus>(new dbus::Bus(core::dbus::WellKnownBus::session));
1794- bus->install_executor(dbus::asio::make_executor(bus));
1795-
1796- auto stub_service = dbus::Service::use_service(bus, dbus::traits::Service<core::Apparmor>::interface_name());
1797- apparmor_session = stub_service->object_for_path(dbus::types::ObjectPath("/org/freedesktop/DBus"));
1798- // Get the AppArmor security context for the client
1799- auto result = apparmor_session->invoke_method_synchronously<core::Apparmor::getConnectionAppArmorSecurityContext, std::string>(msg->sender());
1800- if (result.is_error())
1801- {
1802- std::cout << "Error getting apparmor profile: " << result.error().print() << std::endl;
1803- return std::string();
1804- }
1805-
1806- return result.value();
1807- }
1808+ }
1809
1810 bool does_client_have_access(const std::string& context, const std::string& uri)
1811 {
1812@@ -239,49 +224,51 @@
1813 }
1814
1815 void handle_open_uri(const core::dbus::Message::Ptr& in)
1816+ {
1817+ dbus_stub.get_connection_app_armor_security_async(in->sender(), [this, in](const std::string& profile)
1818+ {
1819+ Track::UriType uri;
1820+ in->reader() >> uri;
1821+
1822+ bool have_access = does_client_have_access(profile, uri);
1823+
1824+ auto reply = dbus::Message::make_method_return(in);
1825+ reply->writer() << (have_access ? impl->open_uri(uri) : false);
1826+
1827+ bus->send(reply);
1828+ });
1829+ }
1830+
1831+ template<typename Property>
1832+ void on_property_value_changed(
1833+ const typename Property::ValueType& value,
1834+ const dbus::Signal
1835+ <
1836+ core::dbus::interfaces::Properties::Signals::PropertiesChanged,
1837+ core::dbus::interfaces::Properties::Signals::PropertiesChanged::ArgumentType
1838+ >::Ptr& signal)
1839 {
1840- Track::UriType uri;
1841- in->reader() >> uri;
1842-
1843- std::string context = get_client_apparmor_context(in);
1844- bool have_access = does_client_have_access(context, uri);
1845-
1846- auto reply = dbus::Message::make_method_return(in);
1847- if (have_access)
1848- reply->writer() << impl->open_uri(uri);
1849- else
1850- reply->writer() << false;
1851- bus->send(reply);
1852+ typedef std::map<std::string, dbus::types::Variant> Dictionary;
1853+
1854+ static const std::vector<std::string> the_empty_list_of_invalidated_properties;
1855+
1856+ Dictionary dict; dict[Property::name()] = dbus::types::Variant::encode(value);
1857+
1858+ signal->emit(std::make_tuple(
1859+ dbus::traits::Service<typename Property::Interface>::interface_name(),
1860+ dict,
1861+ the_empty_list_of_invalidated_properties));
1862 }
1863
1864 media::PlayerSkeleton* impl;
1865+ std::string identity;
1866 dbus::Bus::Ptr bus;
1867 dbus::Object::Ptr object;
1868 dbus::Object::Ptr apparmor_session;
1869
1870- struct
1871- {
1872- std::shared_ptr<core::dbus::Property<mpris::Player::Properties::CanPlay>> can_play;
1873- std::shared_ptr<core::dbus::Property<mpris::Player::Properties::CanPause>> can_pause;
1874- std::shared_ptr<core::dbus::Property<mpris::Player::Properties::CanSeek>> can_seek;
1875- std::shared_ptr<core::dbus::Property<mpris::Player::Properties::CanControl>> can_control;
1876- std::shared_ptr<core::dbus::Property<mpris::Player::Properties::CanGoNext>> can_go_next;
1877- std::shared_ptr<core::dbus::Property<mpris::Player::Properties::CanGoPrevious>> can_go_previous;
1878- std::shared_ptr<core::dbus::Property<mpris::Player::Properties::IsVideoSource>> is_video_source;
1879- std::shared_ptr<core::dbus::Property<mpris::Player::Properties::IsAudioSource>> is_audio_source;
1880+ org::freedesktop::dbus::DBus::Stub dbus_stub;
1881
1882- std::shared_ptr<core::dbus::Property<mpris::Player::Properties::TypedPlaybackStatus>> playback_status;
1883- std::shared_ptr<core::dbus::Property<mpris::Player::Properties::TypedLoopStatus>> loop_status;
1884- std::shared_ptr<core::dbus::Property<mpris::Player::Properties::PlaybackRate>> playback_rate;
1885- std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Shuffle>> is_shuffle;
1886- std::shared_ptr<core::dbus::Property<mpris::Player::Properties::TypedMetaData>> meta_data_for_current_track;
1887- std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Volume>> volume;
1888- std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Position>> position;
1889- std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Duration>> duration;
1890- std::shared_ptr<core::dbus::Property<mpris::Player::Properties::AudioStreamRole>> audio_role;
1891- std::shared_ptr<core::dbus::Property<mpris::Player::Properties::MinimumRate>> minimum_playback_rate;
1892- std::shared_ptr<core::dbus::Property<mpris::Player::Properties::MaximumRate>> maximum_playback_rate;
1893- } properties;
1894+ mpris::Player::Skeleton skeleton;
1895
1896 struct Signals
1897 {
1898@@ -289,92 +276,73 @@
1899 typedef core::dbus::Signal<mpris::Player::Signals::EndOfStream, mpris::Player::Signals::EndOfStream::ArgumentType> DBusEndOfStreamSignal;
1900 typedef core::dbus::Signal<mpris::Player::Signals::PlaybackStatusChanged, mpris::Player::Signals::PlaybackStatusChanged::ArgumentType> DBusPlaybackStatusChangedSignal;
1901
1902- Signals(const std::shared_ptr<DBusSeekedToSignal>& seeked,
1903- const std::shared_ptr<DBusEndOfStreamSignal>& eos,
1904- const std::shared_ptr<DBusPlaybackStatusChangedSignal>& status)
1905- : dbus
1906- {
1907- seeked,
1908- eos,
1909- status
1910- },
1911- seeked_to(),
1912- end_of_stream(),
1913- playback_status_changed()
1914+ Signals(const std::shared_ptr<DBusSeekedToSignal>& remote_seeked,
1915+ const std::shared_ptr<DBusEndOfStreamSignal>& remote_eos,
1916+ const std::shared_ptr<DBusPlaybackStatusChangedSignal>& remote_playback_status_changed)
1917 {
1918- seeked_to.connect([this](std::uint64_t value)
1919- {
1920- dbus.seeked_to->emit(value);
1921- });
1922-
1923- end_of_stream.connect([this]()
1924- {
1925- dbus.end_of_stream->emit();
1926- });
1927-
1928- playback_status_changed.connect([this](const media::Player::PlaybackStatus& status)
1929- {
1930- dbus.playback_status_changed->emit(status);
1931+ seeked_to.connect([remote_seeked](std::uint64_t value)
1932+ {
1933+ remote_seeked->emit(value);
1934+ });
1935+
1936+ end_of_stream.connect([remote_eos]()
1937+ {
1938+ remote_eos->emit();
1939+ });
1940+
1941+ playback_status_changed.connect([remote_playback_status_changed](const media::Player::PlaybackStatus& status)
1942+ {
1943+ remote_playback_status_changed->emit(status);
1944 });
1945 }
1946
1947- struct DBus
1948- {
1949- std::shared_ptr<DBusSeekedToSignal> seeked_to;
1950- std::shared_ptr<DBusEndOfStreamSignal> end_of_stream;
1951- std::shared_ptr<DBusPlaybackStatusChangedSignal> playback_status_changed;
1952- } dbus;
1953- core::Signal<uint64_t> seeked_to;
1954+ core::Signal<int64_t> seeked_to;
1955 core::Signal<void> end_of_stream;
1956 core::Signal<media::Player::PlaybackStatus> playback_status_changed;
1957 } signals;
1958
1959 };
1960
1961-media::PlayerSkeleton::PlayerSkeleton(
1962- const std::shared_ptr<core::dbus::Bus>& bus,
1963- const std::shared_ptr<core::dbus::Object>& session)
1964- : d(new Private{this, bus, session})
1965+media::PlayerSkeleton::PlayerSkeleton(const media::PlayerSkeleton::Configuration& config)
1966+ : d(new Private{this, config.identity, config.bus, config.session})
1967 {
1968- d->object->install_method_handler<mpris::Player::Next>(
1969- std::bind(&Private::handle_next,
1970- std::ref(d),
1971- std::placeholders::_1));
1972- d->object->install_method_handler<mpris::Player::Previous>(
1973- std::bind(&Private::handle_previous,
1974- std::ref(d),
1975- std::placeholders::_1));
1976- d->object->install_method_handler<mpris::Player::Pause>(
1977- std::bind(&Private::handle_pause,
1978- std::ref(d),
1979- std::placeholders::_1));
1980- d->object->install_method_handler<mpris::Player::Stop>(
1981- std::bind(&Private::handle_stop,
1982- std::ref(d),
1983- std::placeholders::_1));
1984- d->object->install_method_handler<mpris::Player::Play>(
1985- std::bind(&Private::handle_play,
1986- std::ref(d),
1987- std::placeholders::_1));
1988- d->object->install_method_handler<mpris::Player::Seek>(
1989- std::bind(&Private::handle_seek,
1990- std::ref(d),
1991- std::placeholders::_1));
1992- d->object->install_method_handler<mpris::Player::SetPosition>(
1993- std::bind(&Private::handle_set_position,
1994- std::ref(d),
1995- std::placeholders::_1));
1996+ // Setup method handlers for mpris::Player methods.
1997+ auto next = std::bind(&Private::handle_next, d, std::placeholders::_1);
1998+ d->object->install_method_handler<mpris::Player::Next>(next);
1999+
2000+ auto previous = std::bind(&Private::handle_previous, d, std::placeholders::_1);
2001+ d->object->install_method_handler<mpris::Player::Previous>(previous);
2002+
2003+ auto pause = std::bind(&Private::handle_pause, d, std::placeholders::_1);
2004+ d->object->install_method_handler<mpris::Player::Pause>(pause);
2005+
2006+ auto stop = std::bind(&Private::handle_stop, d, std::placeholders::_1);
2007+ d->object->install_method_handler<mpris::Player::Stop>(stop);
2008+
2009+ auto play = std::bind(&Private::handle_play, d, std::placeholders::_1);
2010+ d->object->install_method_handler<mpris::Player::Play>(play);
2011+
2012+ auto play_pause = std::bind(&Private::handle_play_pause, d, std::placeholders::_1);
2013+ d->object->install_method_handler<mpris::Player::PlayPause>(play_pause);
2014+
2015+ auto seek = std::bind(&Private::handle_seek, d, std::placeholders::_1);
2016+ d->object->install_method_handler<mpris::Player::Seek>(seek);
2017+
2018+ auto set_position = std::bind(&Private::handle_set_position, d, std::placeholders::_1);
2019+ d->object->install_method_handler<mpris::Player::SetPosition>(set_position);
2020+
2021+ auto open_uri = std::bind(&Private::handle_open_uri, d, std::placeholders::_1);
2022+ d->object->install_method_handler<mpris::Player::OpenUri>(open_uri);
2023+
2024+ // All the method handlers that exceed the mpris spec go here.
2025 d->object->install_method_handler<mpris::Player::CreateVideoSink>(
2026 std::bind(&Private::handle_create_video_sink,
2027- std::ref(d),
2028+ d,
2029 std::placeholders::_1));
2030+
2031 d->object->install_method_handler<mpris::Player::Key>(
2032 std::bind(&Private::handle_key,
2033- std::ref(d),
2034- std::placeholders::_1));
2035- d->object->install_method_handler<mpris::Player::OpenUri>(
2036- std::bind(&Private::handle_open_uri,
2037- std::ref(d),
2038+ d,
2039 std::placeholders::_1));
2040 }
2041
2042@@ -384,191 +352,191 @@
2043
2044 const core::Property<bool>& media::PlayerSkeleton::can_play() const
2045 {
2046- return *d->properties.can_play;
2047+ return *d->skeleton.properties.can_play;
2048 }
2049
2050 const core::Property<bool>& media::PlayerSkeleton::can_pause() const
2051 {
2052- return *d->properties.can_pause;
2053+ return *d->skeleton.properties.can_pause;
2054 }
2055
2056 const core::Property<bool>& media::PlayerSkeleton::can_seek() const
2057 {
2058- return *d->properties.can_seek;
2059+ return *d->skeleton.properties.can_seek;
2060 }
2061
2062 const core::Property<bool>& media::PlayerSkeleton::can_go_previous() const
2063 {
2064- return *d->properties.can_go_previous;
2065+ return *d->skeleton.properties.can_go_previous;
2066 }
2067
2068 const core::Property<bool>& media::PlayerSkeleton::can_go_next() const
2069 {
2070- return *d->properties.can_go_next;
2071+ return *d->skeleton.properties.can_go_next;
2072 }
2073
2074 const core::Property<bool>& media::PlayerSkeleton::is_video_source() const
2075 {
2076- return *d->properties.is_video_source;
2077+ return *d->skeleton.properties.is_video_source;
2078 }
2079
2080 const core::Property<bool>& media::PlayerSkeleton::is_audio_source() const
2081 {
2082- return *d->properties.is_audio_source;
2083+ return *d->skeleton.properties.is_audio_source;
2084 }
2085
2086 const core::Property<media::Player::PlaybackStatus>& media::PlayerSkeleton::playback_status() const
2087 {
2088- return *d->properties.playback_status;
2089+ return *d->skeleton.properties.typed_playback_status;
2090 }
2091
2092 const core::Property<media::Player::LoopStatus>& media::PlayerSkeleton::loop_status() const
2093 {
2094- return *d->properties.loop_status;
2095+ return *d->skeleton.properties.typed_loop_status;
2096 }
2097
2098 const core::Property<media::Player::PlaybackRate>& media::PlayerSkeleton::playback_rate() const
2099 {
2100- return *d->properties.playback_rate;
2101+ return *d->skeleton.properties.playback_rate;
2102 }
2103
2104 const core::Property<bool>& media::PlayerSkeleton::is_shuffle() const
2105 {
2106- return *d->properties.is_shuffle;
2107+ return *d->skeleton.properties.is_shuffle;
2108 }
2109
2110 const core::Property<media::Track::MetaData>& media::PlayerSkeleton::meta_data_for_current_track() const
2111 {
2112- return *d->properties.meta_data_for_current_track;
2113+ return *d->skeleton.properties.typed_meta_data_for_current_track;
2114 }
2115
2116 const core::Property<media::Player::Volume>& media::PlayerSkeleton::volume() const
2117 {
2118- return *d->properties.volume;
2119-}
2120-
2121-const core::Property<uint64_t>& media::PlayerSkeleton::position() const
2122-{
2123- return *d->properties.position;
2124-}
2125-
2126-const core::Property<uint64_t>& media::PlayerSkeleton::duration() const
2127-{
2128- return *d->properties.duration;
2129+ return *d->skeleton.properties.volume;
2130+}
2131+
2132+const core::Property<int64_t>& media::PlayerSkeleton::position() const
2133+{
2134+ return *d->skeleton.properties.position;
2135+}
2136+
2137+const core::Property<int64_t>& media::PlayerSkeleton::duration() const
2138+{
2139+ return *d->skeleton.properties.duration;
2140 }
2141
2142 const core::Property<media::Player::AudioStreamRole>& media::PlayerSkeleton::audio_stream_role() const
2143 {
2144- return *d->properties.audio_role;
2145+ return *d->skeleton.properties.audio_stream_role;
2146 }
2147
2148 const core::Property<media::Player::PlaybackRate>& media::PlayerSkeleton::minimum_playback_rate() const
2149 {
2150- return *d->properties.minimum_playback_rate;
2151+ return *d->skeleton.properties.minimum_playback_rate;
2152 }
2153
2154 const core::Property<media::Player::PlaybackRate>& media::PlayerSkeleton::maximum_playback_rate() const
2155 {
2156- return *d->properties.maximum_playback_rate;
2157+ return *d->skeleton.properties.maximum_playback_rate;
2158 }
2159
2160 core::Property<media::Player::LoopStatus>& media::PlayerSkeleton::loop_status()
2161 {
2162- return *d->properties.loop_status;
2163+ return *d->skeleton.properties.typed_loop_status;
2164 }
2165
2166 core::Property<media::Player::PlaybackRate>& media::PlayerSkeleton::playback_rate()
2167 {
2168- return *d->properties.playback_rate;
2169+ return *d->skeleton.properties.playback_rate;
2170 }
2171
2172 core::Property<bool>& media::PlayerSkeleton::is_shuffle()
2173 {
2174- return *d->properties.is_shuffle;
2175+ return *d->skeleton.properties.is_shuffle;
2176 }
2177
2178 core::Property<media::Player::Volume>& media::PlayerSkeleton::volume()
2179 {
2180- return *d->properties.volume;
2181-}
2182-
2183-core::Property<uint64_t>& media::PlayerSkeleton::position()
2184-{
2185- return *d->properties.position;
2186-}
2187-
2188-core::Property<uint64_t>& media::PlayerSkeleton::duration()
2189-{
2190- return *d->properties.duration;
2191+ return *d->skeleton.properties.volume;
2192+}
2193+
2194+core::Property<int64_t>& media::PlayerSkeleton::position()
2195+{
2196+ return *d->skeleton.properties.position;
2197+}
2198+
2199+core::Property<int64_t>& media::PlayerSkeleton::duration()
2200+{
2201+ return *d->skeleton.properties.duration;
2202 }
2203
2204 core::Property<media::Player::AudioStreamRole>& media::PlayerSkeleton::audio_stream_role()
2205 {
2206- return *d->properties.audio_role;
2207+ return *d->skeleton.properties.audio_stream_role;
2208 }
2209
2210 core::Property<media::Player::PlaybackStatus>& media::PlayerSkeleton::playback_status()
2211 {
2212- return *d->properties.playback_status;
2213+ return *d->skeleton.properties.typed_playback_status;
2214 }
2215
2216 core::Property<bool>& media::PlayerSkeleton::can_play()
2217 {
2218- return *d->properties.can_play;
2219+ return *d->skeleton.properties.can_play;
2220 }
2221
2222 core::Property<bool>& media::PlayerSkeleton::can_pause()
2223 {
2224- return *d->properties.can_pause;
2225+ return *d->skeleton.properties.can_pause;
2226 }
2227
2228 core::Property<bool>& media::PlayerSkeleton::can_seek()
2229 {
2230- return *d->properties.can_seek;
2231+ return *d->skeleton.properties.can_seek;
2232 }
2233
2234 core::Property<bool>& media::PlayerSkeleton::can_go_previous()
2235 {
2236- return *d->properties.can_go_previous;
2237+ return *d->skeleton.properties.can_go_previous;
2238 }
2239
2240 core::Property<bool>& media::PlayerSkeleton::can_go_next()
2241 {
2242- return *d->properties.can_go_next;
2243+ return *d->skeleton.properties.can_go_next;
2244 }
2245
2246 core::Property<bool>& media::PlayerSkeleton::is_video_source()
2247 {
2248- return *d->properties.is_video_source;
2249+ return *d->skeleton.properties.is_video_source;
2250 }
2251
2252 core::Property<bool>& media::PlayerSkeleton::is_audio_source()
2253 {
2254- return *d->properties.is_audio_source;
2255+ return *d->skeleton.properties.is_audio_source;
2256 }
2257
2258
2259 core::Property<media::Track::MetaData>& media::PlayerSkeleton::meta_data_for_current_track()
2260 {
2261- return *d->properties.meta_data_for_current_track;
2262+ return *d->skeleton.properties.typed_meta_data_for_current_track;
2263 }
2264
2265 core::Property<media::Player::PlaybackRate>& media::PlayerSkeleton::minimum_playback_rate()
2266 {
2267- return *d->properties.minimum_playback_rate;
2268+ return *d->skeleton.properties.minimum_playback_rate;
2269 }
2270
2271 core::Property<media::Player::PlaybackRate>& media::PlayerSkeleton::maximum_playback_rate()
2272 {
2273- return *d->properties.maximum_playback_rate;
2274+ return *d->skeleton.properties.maximum_playback_rate;
2275 }
2276
2277-const core::Signal<uint64_t>& media::PlayerSkeleton::seeked_to() const
2278+const core::Signal<int64_t>& media::PlayerSkeleton::seeked_to() const
2279 {
2280 return d->signals.seeked_to;
2281 }
2282
2283-core::Signal<uint64_t>& media::PlayerSkeleton::seeked_to()
2284+core::Signal<int64_t>& media::PlayerSkeleton::seeked_to()
2285 {
2286 return d->signals.seeked_to;
2287 }
2288
2289=== modified file 'src/core/media/player_skeleton.h'
2290--- src/core/media/player_skeleton.h 2014-09-10 21:05:53 +0000
2291+++ src/core/media/player_skeleton.h 2014-09-10 21:05:53 +0000
2292@@ -59,8 +59,8 @@
2293 virtual const core::Property<Volume>& volume() const;
2294 virtual const core::Property<PlaybackRate>& minimum_playback_rate() const;
2295 virtual const core::Property<PlaybackRate>& maximum_playback_rate() const;
2296- virtual const core::Property<uint64_t>& position() const;
2297- virtual const core::Property<uint64_t>& duration() const;
2298+ virtual const core::Property<int64_t>& position() const;
2299+ virtual const core::Property<int64_t>& duration() const;
2300 virtual const core::Property<AudioStreamRole>& audio_stream_role() const;
2301
2302 virtual core::Property<LoopStatus>& loop_status();
2303@@ -69,14 +69,24 @@
2304 virtual core::Property<Volume>& volume();
2305 virtual core::Property<AudioStreamRole>& audio_stream_role();
2306
2307- virtual const core::Signal<uint64_t>& seeked_to() const;
2308+ virtual const core::Signal<int64_t>& seeked_to() const;
2309 virtual const core::Signal<void>& end_of_stream() const;
2310 virtual core::Signal<PlaybackStatus>& playback_status_changed();
2311
2312- protected:
2313- PlayerSkeleton(
2314- const std::shared_ptr<core::dbus::Bus>& bus,
2315- const std::shared_ptr<core::dbus::Object>& session);
2316+protected:
2317+ // All creation time arguments go here.
2318+ struct Configuration
2319+ {
2320+ // The bus connection we are associated with.
2321+ std::shared_ptr<core::dbus::Bus> bus;
2322+ // The session object that we want to expose the skeleton upon.
2323+ std::shared_ptr<core::dbus::Object> session;
2324+ // Our identity, an identifier we pass out to other parts of the system.
2325+ // Defaults to the short app id (${PKG_NAME}_${APP}).
2326+ std::string identity;
2327+ };
2328+
2329+ PlayerSkeleton(const Configuration& configuration);
2330
2331 virtual core::Property<PlaybackStatus>& playback_status();
2332 virtual core::Property<bool>& can_play();
2333@@ -89,15 +99,15 @@
2334 virtual core::Property<Track::MetaData>& meta_data_for_current_track();
2335 virtual core::Property<PlaybackRate>& minimum_playback_rate();
2336 virtual core::Property<PlaybackRate>& maximum_playback_rate();
2337- virtual core::Property<uint64_t>& position();
2338- virtual core::Property<uint64_t>& duration();
2339+ virtual core::Property<int64_t>& position();
2340+ virtual core::Property<int64_t>& duration();
2341
2342- virtual core::Signal<uint64_t>& seeked_to();
2343+ virtual core::Signal<int64_t>& seeked_to();
2344 virtual core::Signal<void>& end_of_stream();
2345
2346 private:
2347 struct Private;
2348- std::unique_ptr<Private> d;
2349+ std::shared_ptr<Private> d;
2350 };
2351 }
2352 }
2353
2354=== modified file 'src/core/media/player_stub.cpp'
2355--- src/core/media/player_stub.cpp 2014-09-10 21:05:53 +0000
2356+++ src/core/media/player_stub.cpp 2014-09-10 21:05:53 +0000
2357@@ -221,7 +221,7 @@
2358
2359 PlaybackCompleteCb playback_complete_cb;
2360 void *playback_complete_context;
2361- core::Signal<uint64_t> seeked_to;
2362+ core::Signal<int64_t> seeked_to;
2363 core::Signal<void> end_of_stream;
2364 core::Signal<media::Player::PlaybackStatus> playback_status_changed;
2365 } signals;
2366@@ -411,12 +411,12 @@
2367 return *d->properties.volume;
2368 }
2369
2370-const core::Property<uint64_t>& media::PlayerStub::position() const
2371+const core::Property<int64_t>& media::PlayerStub::position() const
2372 {
2373 return *d->properties.position;
2374 }
2375
2376-const core::Property<uint64_t>& media::PlayerStub::duration() const
2377+const core::Property<int64_t>& media::PlayerStub::duration() const
2378 {
2379 return *d->properties.duration;
2380 }
2381@@ -461,7 +461,7 @@
2382 return *d->properties.audio_role;
2383 }
2384
2385-const core::Signal<uint64_t>& media::PlayerStub::seeked_to() const
2386+const core::Signal<int64_t>& media::PlayerStub::seeked_to() const
2387 {
2388 return d->signals.seeked_to;
2389 }
2390
2391=== modified file 'src/core/media/player_stub.h'
2392--- src/core/media/player_stub.h 2014-09-10 21:05:53 +0000
2393+++ src/core/media/player_stub.h 2014-09-10 21:05:53 +0000
2394@@ -73,8 +73,8 @@
2395 virtual const core::Property<Volume>& volume() const;
2396 virtual const core::Property<PlaybackRate>& minimum_playback_rate() const;
2397 virtual const core::Property<PlaybackRate>& maximum_playback_rate() const;
2398- virtual const core::Property<uint64_t>& position() const;
2399- virtual const core::Property<uint64_t>& duration() const;
2400+ virtual const core::Property<int64_t>& position() const;
2401+ virtual const core::Property<int64_t>& duration() const;
2402 virtual const core::Property<AudioStreamRole>& audio_stream_role() const;
2403
2404 virtual core::Property<LoopStatus>& loop_status();
2405@@ -83,7 +83,7 @@
2406 virtual core::Property<Volume>& volume();
2407 virtual core::Property<AudioStreamRole>& audio_stream_role();
2408
2409- virtual const core::Signal<uint64_t>& seeked_to() const;
2410+ virtual const core::Signal<int64_t>& seeked_to() const;
2411 virtual const core::Signal<void>& end_of_stream() const;
2412 virtual core::Signal<PlaybackStatus>& playback_status_changed();
2413
2414
2415=== modified file 'src/core/media/service_implementation.cpp'
2416--- src/core/media/service_implementation.cpp 2014-09-10 21:05:53 +0000
2417+++ src/core/media/service_implementation.cpp 2014-09-10 21:05:53 +0000
2418@@ -23,9 +23,9 @@
2419 #include "player_configuration.h"
2420 #include "player_implementation.h"
2421
2422+#include <cstdint>
2423 #include <map>
2424 #include <memory>
2425-#include <stdint.h>
2426 #include <thread>
2427
2428 namespace media = core::ubuntu::media;
2429@@ -34,11 +34,8 @@
2430
2431 struct media::ServiceImplementation::Private
2432 {
2433- typedef map<media::Player::PlayerKey, std::shared_ptr<media::Player>> player_map_t;
2434-
2435 Private()
2436- : key_(0),
2437- resume_key(UINT32_MAX)
2438+ : resume_key(std::numeric_limits<std::uint32_t>::max())
2439 {
2440 bus = std::shared_ptr<dbus::Bus>(new dbus::Bus(core::dbus::WellKnownBus::session));
2441 bus->install_executor(dbus::asio::make_executor(bus));
2442@@ -52,22 +49,7 @@
2443 auto stub_service = dbus::Service::use_service(bus, "com.canonical.indicator.power");
2444 indicator_power_session = stub_service->object_for_path(dbus::types::ObjectPath("/com/canonical/indicator/power/Battery"));
2445 power_level = indicator_power_session->get_property<core::IndicatorPower::PowerLevel>();
2446- power_level->changed().connect([this](const core::IndicatorPower::PowerLevel::ValueType &level)
2447- {
2448- // When the battery level hits 10% or 5%, pause all multimedia sessions.
2449- // Playback will resume when the user clears the presented notification.
2450- if (level == "low" || level == "very_low")
2451- pause_all_multimedia_sessions();
2452- });
2453-
2454 is_warning = indicator_power_session->get_property<core::IndicatorPower::IsWarning>();
2455- is_warning->changed().connect([this](const core::IndicatorPower::IsWarning::ValueType &notifying)
2456- {
2457- // If the low battery level notification is no longer being displayed,
2458- // resume what the user was previously playing
2459- if (!notifying)
2460- resume_multimedia_session();
2461- });
2462 }
2463
2464 ~Private()
2465@@ -78,80 +60,6 @@
2466 worker.join();
2467 }
2468
2469- void track_player(const std::shared_ptr<media::Player>& player)
2470- {
2471- player_map.insert(
2472- std::pair<media::Player::PlayerKey,
2473- std::shared_ptr<media::Player>>(key_, player));
2474-
2475- ++key_;
2476- }
2477-
2478- inline media::Player::PlayerKey key() const
2479- {
2480- return key_;
2481- }
2482-
2483- void pause_other_sessions(media::Player::PlayerKey key)
2484- {
2485- auto player_it = player_map.find(key);
2486- if (player_it != player_map.end())
2487- {
2488- auto &current_player = (*player_it).second;
2489- for (auto& player_pair : player_map)
2490- {
2491- // Only pause a Player if all of the following criteria are met:
2492- // 1) currently playing
2493- // 2) not the same player as the one passed in my key
2494- // 3) new Player has an audio stream role set to multimedia
2495- // 4) has an audio stream role set to multimedia
2496- if (player_pair.second->playback_status() == Player::playing
2497- && player_pair.first != key
2498- && current_player->audio_stream_role() == media::Player::multimedia
2499- && player_pair.second->audio_stream_role() == media::Player::multimedia)
2500- {
2501- cout << "Pausing Player with key: " << player_pair.first << endl;
2502- player_pair.second->pause();
2503- }
2504- }
2505- }
2506- else
2507- cerr << "Could not find Player by key: " << key << endl;
2508- }
2509-
2510- // Pauses all multimedia audio stream role type Players
2511- void pause_all_multimedia_sessions()
2512- {
2513- for (auto& player_pair : player_map)
2514- {
2515- if (player_pair.second->playback_status() == Player::playing
2516- && player_pair.second->audio_stream_role() == media::Player::multimedia)
2517- {
2518- resume_key = player_pair.first;
2519- cout << "Will resume playback of Player with key: " << resume_key << endl;
2520- player_pair.second->pause();
2521- }
2522- }
2523- }
2524-
2525- void resume_multimedia_session()
2526- {
2527- auto player_it = player_map.find(resume_key);
2528- if (player_it != player_map.end())
2529- {
2530- auto &player = (*player_it).second;
2531- if (player->playback_status() == Player::paused)
2532- {
2533- cout << "Resuming playback of Player with key: " << resume_key << endl;
2534- player->play();
2535- resume_key = UINT32_MAX;
2536- }
2537- }
2538- }
2539-
2540- // Used for Player instance management
2541- player_map_t player_map;
2542- media::Player::PlayerKey key_;
2543 // This holds the key of the multimedia role Player instance that was paused
2544 // when the battery level reached 10% or 5%
2545 media::Player::PlayerKey resume_key;
2546@@ -160,12 +68,27 @@
2547 std::shared_ptr<dbus::Object> indicator_power_session;
2548 std::shared_ptr<core::dbus::Property<core::IndicatorPower::PowerLevel>> power_level;
2549 std::shared_ptr<core::dbus::Property<core::IndicatorPower::IsWarning>> is_warning;
2550-
2551 };
2552
2553 media::ServiceImplementation::ServiceImplementation() : d(new Private())
2554 {
2555 cout << __PRETTY_FUNCTION__ << endl;
2556+
2557+ d->power_level->changed().connect([this](const core::IndicatorPower::PowerLevel::ValueType &level)
2558+ {
2559+ // When the battery level hits 10% or 5%, pause all multimedia sessions.
2560+ // Playback will resume when the user clears the presented notification.
2561+ if (level == "low" || level == "very_low")
2562+ pause_all_multimedia_sessions();
2563+ });
2564+
2565+ d->is_warning->changed().connect([this](const core::IndicatorPower::IsWarning::ValueType &notifying)
2566+ {
2567+ // If the low battery level notification is no longer being displayed,
2568+ // resume what the user was previously playing
2569+ if (!notifying)
2570+ resume_multimedia_session();
2571+ });
2572 }
2573
2574 media::ServiceImplementation::~ServiceImplementation()
2575@@ -175,13 +98,75 @@
2576 std::shared_ptr<media::Player> media::ServiceImplementation::create_session(
2577 const media::Player::Configuration& conf)
2578 {
2579- std::shared_ptr<media::Player> player = std::make_shared<media::PlayerImplementation>(
2580- conf.bus, conf.session, shared_from_this(), d->key());
2581- d->track_player(player);
2582+ auto player = std::make_shared<media::PlayerImplementation>(
2583+ conf.identity, conf.bus, conf.session, shared_from_this(), conf.key);
2584+
2585+ auto key = conf.key;
2586+ player->on_client_disconnected().connect([this, key]()
2587+ {
2588+ remove_player_for_key(key);
2589+ });
2590+
2591 return player;
2592 }
2593
2594 void media::ServiceImplementation::pause_other_sessions(media::Player::PlayerKey key)
2595 {
2596- d->pause_other_sessions(key);
2597+ if (not has_player_for_key(key))
2598+ {
2599+ cerr << "Could not find Player by key: " << key << endl;
2600+ return;
2601+ }
2602+
2603+ auto current_player = player_for_key(key);
2604+
2605+ // We immediately make the player known as new current player.
2606+ if (current_player->audio_stream_role() == media::Player::multimedia)
2607+ set_current_player_for_key(key);
2608+
2609+ enumerate_players([current_player, key](const media::Player::PlayerKey& other_key, const std::shared_ptr<media::Player>& other_player)
2610+ {
2611+ // Only pause a Player if all of the following criteria are met:
2612+ // 1) currently playing
2613+ // 2) not the same player as the one passed in my key
2614+ // 3) new Player has an audio stream role set to multimedia
2615+ // 4) has an audio stream role set to multimedia
2616+ if (other_player->playback_status() == Player::playing &&
2617+ other_key != key &&
2618+ current_player->audio_stream_role() == media::Player::multimedia &&
2619+ other_player->audio_stream_role() == media::Player::multimedia)
2620+ {
2621+ cout << "Pausing Player with key: " << other_key << endl;
2622+ other_player->pause();
2623+ }
2624+ });
2625+}
2626+
2627+void media::ServiceImplementation::pause_all_multimedia_sessions()
2628+{
2629+ enumerate_players([this](const media::Player::PlayerKey& key, const std::shared_ptr<media::Player>& player)
2630+ {
2631+ if (player->playback_status() == Player::playing
2632+ && player->audio_stream_role() == media::Player::multimedia)
2633+ {
2634+ d->resume_key = key;
2635+ cout << "Will resume playback of Player with key: " << d->resume_key << endl;
2636+ player->pause();
2637+ }
2638+ });
2639+}
2640+
2641+void media::ServiceImplementation::resume_multimedia_session()
2642+{
2643+ if (not has_player_for_key(d->resume_key))
2644+ return;
2645+
2646+ auto player = player_for_key(d->resume_key);
2647+
2648+ if (player->playback_status() == Player::paused)
2649+ {
2650+ cout << "Resuming playback of Player with key: " << d->resume_key << endl;
2651+ player->play();
2652+ d->resume_key = std::numeric_limits<std::uint32_t>::max();
2653+ }
2654 }
2655
2656=== modified file 'src/core/media/service_implementation.h'
2657--- src/core/media/service_implementation.h 2014-04-23 19:00:55 +0000
2658+++ src/core/media/service_implementation.h 2014-09-10 21:05:53 +0000
2659@@ -32,7 +32,7 @@
2660
2661 class ServiceImplementation : public ServiceSkeleton
2662 {
2663- public:
2664+public:
2665 ServiceImplementation ();
2666 ~ServiceImplementation ();
2667
2668@@ -40,7 +40,10 @@
2669
2670 void pause_other_sessions(Player::PlayerKey key);
2671
2672- private:
2673+private:
2674+ void pause_all_multimedia_sessions();
2675+ void resume_multimedia_session();
2676+
2677 struct Private;
2678 std::shared_ptr<Private> d;
2679 };
2680
2681=== modified file 'src/core/media/service_skeleton.cpp'
2682--- src/core/media/service_skeleton.cpp 2014-09-10 21:05:53 +0000
2683+++ src/core/media/service_skeleton.cpp 2014-09-10 21:05:53 +0000
2684@@ -18,15 +18,24 @@
2685
2686 #include "service_skeleton.h"
2687
2688+#include "apparmor.h"
2689+
2690+#include "mpris/media_player2.h"
2691+#include "mpris/metadata.h"
2692+#include "mpris/player.h"
2693+#include "mpris/playlists.h"
2694 #include "mpris/service.h"
2695+
2696 #include "player_configuration.h"
2697 #include "the_session_bus.h"
2698+#include "xesam.h"
2699
2700 #include <core/dbus/message.h>
2701 #include <core/dbus/object.h>
2702 #include <core/dbus/types/object_path.h>
2703
2704 #include <map>
2705+#include <regex>
2706 #include <sstream>
2707
2708 namespace dbus = core::dbus;
2709@@ -34,15 +43,17 @@
2710
2711 namespace
2712 {
2713-std::map<dbus::types::ObjectPath, std::shared_ptr<media::Player>> session_store;
2714+core::Signal<void> the_empty_signal;
2715 }
2716
2717 struct media::ServiceSkeleton::Private
2718 {
2719- Private(media::ServiceSkeleton* impl)
2720+ Private(media::ServiceSkeleton* impl, const media::CoverArtResolver& resolver)
2721 : impl(impl),
2722 object(impl->access_service()->add_object_for_path(
2723- dbus::traits::Service<media::Service>::object_path()))
2724+ dbus::traits::Service<media::Service>::object_path())),
2725+ dbus_stub(impl->access_bus()),
2726+ exported(impl->access_bus(), resolver)
2727 {
2728 object->install_method_handler<mpris::Service::CreateSession>(
2729 std::bind(
2730@@ -64,35 +75,42 @@
2731 ss << "/core/ubuntu/media/Service/sessions/" << session_counter++;
2732
2733 dbus::types::ObjectPath op{ss.str()};
2734- media::Player::Configuration config
2735- {
2736- impl->access_bus(),
2737- impl->access_service()->add_object_for_path(op)
2738- };
2739-
2740- try
2741- {
2742- auto session = impl->create_session(config);
2743-
2744- bool inserted = false;
2745- std::tie(std::ignore, inserted)
2746- = session_store.insert(std::make_pair(op, session));
2747-
2748- if (!inserted)
2749- throw std::runtime_error("Problem persisting session in session store.");
2750-
2751- auto reply = dbus::Message::make_method_return(msg);
2752- reply->writer() << op;
2753-
2754- impl->access_bus()->send(reply);
2755- } catch(const std::runtime_error& e)
2756- {
2757- auto reply = dbus::Message::make_error(
2758- msg,
2759- mpris::Service::Errors::CreatingSession::name(),
2760- e.what());
2761- impl->access_bus()->send(reply);
2762- }
2763+ media::Player::PlayerKey key{session_counter};
2764+
2765+ dbus_stub.get_connection_app_armor_security_async(msg->sender(), [this, msg, op, key](const std::string& profile)
2766+ {
2767+ media::Player::Configuration config
2768+ {
2769+ profile,
2770+ key,
2771+ impl->access_bus(),
2772+ impl->access_service()->add_object_for_path(op)
2773+ };
2774+
2775+ try
2776+ {
2777+ auto session = impl->create_session(config);
2778+
2779+ bool inserted = false;
2780+ std::tie(std::ignore, inserted)
2781+ = session_store.insert(std::make_pair(key, session));
2782+
2783+ if (!inserted)
2784+ throw std::runtime_error("Problem persisting session in session store.");
2785+
2786+ auto reply = dbus::Message::make_method_return(msg);
2787+ reply->writer() << op;
2788+
2789+ impl->access_bus()->send(reply);
2790+ } catch(const std::runtime_error& e)
2791+ {
2792+ auto reply = dbus::Message::make_error(
2793+ msg,
2794+ mpris::Service::Errors::CreatingSession::name(),
2795+ e.what());
2796+ impl->access_bus()->send(reply);
2797+ }
2798+ });
2799 }
2800
2801 void handle_pause_other_sessions(const core::dbus::Message::Ptr& msg)
2802@@ -108,11 +126,271 @@
2803
2804 media::ServiceSkeleton* impl;
2805 dbus::Object::Ptr object;
2806+
2807+ // We query the apparmor profile to obtain an identity for players.
2808+ org::freedesktop::dbus::DBus::Stub dbus_stub;
2809+ // We track all running player instances.
2810+ std::map<media::Player::PlayerKey, std::shared_ptr<media::Player>> session_store;
2811+ // We expose the entire service as an MPRIS player.
2812+ struct Exported
2813+ {
2814+ static mpris::MediaPlayer2::Skeleton::Configuration::Defaults media_player_defaults()
2815+ {
2816+ mpris::MediaPlayer2::Skeleton::Configuration::Defaults defaults;
2817+ // TODO(tvoss): These three elements really should be configurable.
2818+ defaults.identity = "core::media::Hub";
2819+ defaults.desktop_entry = "mediaplayer-app";
2820+ defaults.supported_mime_types = {"audio/mpeg3"};
2821+
2822+ return defaults;
2823+ }
2824+
2825+ static mpris::Player::Skeleton::Configuration::Defaults player_defaults()
2826+ {
2827+ mpris::Player::Skeleton::Configuration::Defaults defaults;
2828+
2829+ // Disabled as track list is not fully implemented yet.
2830+ defaults.can_go_next = false;
2831+ // Disabled as track list is not fully implemented yet.
2832+ defaults.can_go_previous = false;
2833+
2834+ return defaults;
2835+ }
2836+
2837+ explicit Exported(const dbus::Bus::Ptr& bus, const media::CoverArtResolver& cover_art_resolver)
2838+ : bus{bus},
2839+ service{dbus::Service::add_service(bus, "org.mpris.MediaPlayer2.MediaHub")},
2840+ object{service->add_object_for_path(dbus::types::ObjectPath{"/org/mpris/MediaPlayer2"})},
2841+ media_player{mpris::MediaPlayer2::Skeleton::Configuration{bus, object, media_player_defaults()}},
2842+ player{mpris::Player::Skeleton::Configuration{bus, object, player_defaults()}},
2843+ playlists{mpris::Playlists::Skeleton::Configuration{bus, object, mpris::Playlists::Skeleton::Configuration::Defaults{}}},
2844+ cover_art_resolver{cover_art_resolver}
2845+ {
2846+ object->install_method_handler<core::dbus::interfaces::Properties::GetAll>([this](const core::dbus::Message::Ptr& msg)
2847+ {
2848+ // Extract the interface
2849+ std::string itf; msg->reader() >> itf;
2850+ core::dbus::Message::Ptr reply = core::dbus::Message::make_method_return(msg);
2851+
2852+ if (itf == mpris::Player::name())
2853+ reply->writer() << player.get_all_properties();
2854+ else if (itf == mpris::MediaPlayer2::name())
2855+ reply->writer() << media_player.get_all_properties();
2856+ else if (itf == mpris::Playlists::name())
2857+ reply->writer() << playlists.get_all_properties();
2858+
2859+ Exported::bus->send(reply);
2860+ });
2861+
2862+ // Setup method handlers for mpris::Player methods.
2863+ auto next = [this](const core::dbus::Message::Ptr& msg)
2864+ {
2865+ auto sp = current_player.lock();
2866+
2867+ if (sp)
2868+ sp->next();
2869+
2870+ Exported::bus->send(core::dbus::Message::make_method_return(msg));
2871+ };
2872+ object->install_method_handler<mpris::Player::Next>(next);
2873+
2874+ auto previous = [this](const core::dbus::Message::Ptr& msg)
2875+ {
2876+ auto sp = current_player.lock();
2877+
2878+ if (sp)
2879+ sp->previous();
2880+
2881+ Exported::bus->send(core::dbus::Message::make_method_return(msg));
2882+ };
2883+ object->install_method_handler<mpris::Player::Previous>(previous);
2884+
2885+ auto pause = [this](const core::dbus::Message::Ptr& msg)
2886+ {
2887+ auto sp = current_player.lock();
2888+
2889+ if (sp)
2890+ sp->pause();
2891+
2892+ Exported::bus->send(core::dbus::Message::make_method_return(msg));
2893+ };
2894+ object->install_method_handler<mpris::Player::Pause>(pause);
2895+
2896+ auto stop = [this](const core::dbus::Message::Ptr& msg)
2897+ {
2898+ auto sp = current_player.lock();
2899+
2900+ if (sp)
2901+ sp->stop();
2902+
2903+ Exported::bus->send(core::dbus::Message::make_method_return(msg));
2904+ };
2905+ object->install_method_handler<mpris::Player::Stop>(stop);
2906+
2907+ auto play = [this](const core::dbus::Message::Ptr& msg)
2908+ {
2909+ auto sp = current_player.lock();
2910+
2911+ if (sp)
2912+ sp->play();
2913+
2914+ Exported::bus->send(core::dbus::Message::make_method_return(msg));
2915+ };
2916+ object->install_method_handler<mpris::Player::Play>(play);
2917+
2918+ auto play_pause = [this](const core::dbus::Message::Ptr& msg)
2919+ {
2920+ auto sp = current_player.lock();
2921+
2922+ if (sp)
2923+ {
2924+ if (sp->playback_status() == media::Player::PlaybackStatus::playing)
2925+ sp->pause();
2926+ else if (sp->playback_status() != media::Player::PlaybackStatus::null)
2927+ sp->play();
2928+ }
2929+
2930+ Exported::bus->send(core::dbus::Message::make_method_return(msg));
2931+ };
2932+ object->install_method_handler<mpris::Player::PlayPause>(play_pause);
2933+ }
2934+
2935+ void set_current_player(const std::shared_ptr<media::Player>& cp)
2936+ {
2937+ unset_current_player();
2938+
2939+ // We will not keep the object alive.
2940+ current_player = cp;
2941+
2942+ // And announce that we can be controlled again.
2943+ player.properties.can_control->set(false);
2944+
2945+ // We wire up player state changes
2946+ connections.seeked_to = cp->seeked_to().connect([this](std::uint64_t position)
2947+ {
2948+ player.signals.seeked_to->emit(position);
2949+ });
2950+
2951+ connections.duration_changed = cp->duration().changed().connect([this](std::uint64_t duration)
2952+ {
2953+ player.properties.duration->set(duration);
2954+ });
2955+
2956+ connections.position_changed = cp->position().changed().connect([this](std::uint64_t position)
2957+ {
2958+ player.properties.position->set(position);
2959+ });
2960+
2961+ connections.playback_status_changed = cp->playback_status().changed().connect([this](core::ubuntu::media::Player::PlaybackStatus status)
2962+ {
2963+ player.properties.playback_status->set(mpris::Player::PlaybackStatus::from(status));
2964+ });
2965+
2966+ connections.loop_status_changed = cp->loop_status().changed().connect([this](core::ubuntu::media::Player::LoopStatus status)
2967+ {
2968+ player.properties.loop_status->set(mpris::Player::LoopStatus::from(status));
2969+ });
2970+
2971+ connections.meta_data_changed = cp->meta_data_for_current_track().changed().connect([this](const core::ubuntu::media::Track::MetaData& md)
2972+ {
2973+ mpris::Player::Dictionary dict;
2974+
2975+ bool has_title = md.count(xesam::Title::name) > 0;
2976+ bool has_album_name = md.count(xesam::Album::name) > 0;
2977+ bool has_artist_name = md.count(xesam::Artist::name) > 0;
2978+
2979+ if (has_title)
2980+ dict[xesam::Title::name] = dbus::types::Variant::encode(md.get(xesam::Title::name));
2981+ if (has_album_name)
2982+ dict[xesam::Album::name] = dbus::types::Variant::encode(md.get(xesam::Album::name));
2983+ if (has_artist_name)
2984+ dict[xesam::Artist::name] = dbus::types::Variant::encode(md.get(xesam::Artist::name));
2985+
2986+ dict[mpris::metadata::ArtUrl::name] = dbus::types::Variant::encode(
2987+ cover_art_resolver(
2988+ has_title ? md.get(xesam::Title::name) : "",
2989+ has_album_name ? md.get(xesam::Album::name) : "",
2990+ has_artist_name ? md.get(xesam::Artist::name) : ""));
2991+
2992+ mpris::Player::Dictionary wrap;
2993+ wrap[mpris::Player::Properties::Metadata::name()] = dbus::types::Variant::encode(dict);
2994+
2995+ player.signals.properties_changed->emit(
2996+ std::make_tuple(
2997+ dbus::traits::Service<mpris::Player::Properties::Metadata::Interface>::interface_name(),
2998+ wrap,
2999+ std::vector<std::string>()));
3000+ });
3001+ }
3002+
3003+ void unset_current_player()
3004+ {
3005+ current_player.reset();
3006+
3007+ // We disconnect all previous event connections.
3008+ connections.seeked_to.disconnect();
3009+ connections.duration_changed.disconnect();
3010+ connections.position_changed.disconnect();
3011+ connections.playback_status_changed.disconnect();
3012+ connections.loop_status_changed.disconnect();
3013+ connections.meta_data_changed.disconnect();
3014+
3015+ // And announce that we cannot be controlled anymore.
3016+ player.properties.can_control->set(false);
3017+ }
3018+
3019+ void unset_if_current(const std::shared_ptr<media::Player>& cp)
3020+ {
3021+ if (cp == current_player.lock())
3022+ unset_current_player();
3023+ }
3024+
3025+ dbus::Bus::Ptr bus;
3026+ dbus::Service::Ptr service;
3027+ dbus::Object::Ptr object;
3028+
3029+ mpris::MediaPlayer2::Skeleton media_player;
3030+ mpris::Player::Skeleton player;
3031+ mpris::Playlists::Skeleton playlists;
3032+
3033+ // Helper to resolve (title, artist, album) tuples to cover art.
3034+ media::CoverArtResolver cover_art_resolver;
3035+ // The actual player instance.
3036+ std::weak_ptr<media::Player> current_player;
3037+ // We track event connections.
3038+ struct
3039+ {
3040+ core::Connection seeked_to
3041+ {
3042+ the_empty_signal.connect([](){})
3043+ };
3044+ core::Connection duration_changed
3045+ {
3046+ the_empty_signal.connect([](){})
3047+ };
3048+ core::Connection position_changed
3049+ {
3050+ the_empty_signal.connect([](){})
3051+ };
3052+ core::Connection playback_status_changed
3053+ {
3054+ the_empty_signal.connect([](){})
3055+ };
3056+ core::Connection loop_status_changed
3057+ {
3058+ the_empty_signal.connect([](){})
3059+ };
3060+ core::Connection meta_data_changed
3061+ {
3062+ the_empty_signal.connect([](){})
3063+ };
3064+ } connections;
3065+ } exported;
3066 };
3067
3068-media::ServiceSkeleton::ServiceSkeleton()
3069+media::ServiceSkeleton::ServiceSkeleton(const media::CoverArtResolver& resolver)
3070 : dbus::Skeleton<media::Service>(the_session_bus()),
3071- d(new Private(this))
3072+ d(new Private(this, resolver))
3073 {
3074 }
3075
3076@@ -120,6 +398,41 @@
3077 {
3078 }
3079
3080+bool media::ServiceSkeleton::has_player_for_key(const media::Player::PlayerKey& key) const
3081+{
3082+ return d->session_store.count(key) > 0;
3083+}
3084+
3085+std::shared_ptr<media::Player> media::ServiceSkeleton::player_for_key(const media::Player::PlayerKey& key) const
3086+{
3087+ return d->session_store.at(key);
3088+}
3089+
3090+void media::ServiceSkeleton::enumerate_players(const media::ServiceSkeleton::PlayerEnumerator& enumerator) const
3091+{
3092+ for (const auto& pair : d->session_store)
3093+ enumerator(pair.first, pair.second);
3094+}
3095+
3096+void media::ServiceSkeleton::set_current_player_for_key(const media::Player::PlayerKey& key)
3097+{
3098+ if (not has_player_for_key(key))
3099+ return;
3100+
3101+ d->exported.set_current_player(player_for_key(key));
3102+}
3103+
3104+void media::ServiceSkeleton::remove_player_for_key(const media::Player::PlayerKey& key)
3105+{
3106+ if (not has_player_for_key(key))
3107+ return;
3108+
3109+ auto player = player_for_key(key);
3110+
3111+ d->session_store.erase(key);
3112+ d->exported.unset_if_current(player);
3113+}
3114+
3115 void media::ServiceSkeleton::run()
3116 {
3117 access_bus()->run();
3118
3119=== modified file 'src/core/media/service_skeleton.h'
3120--- src/core/media/service_skeleton.h 2014-04-23 19:00:55 +0000
3121+++ src/core/media/service_skeleton.h 2014-09-10 21:05:53 +0000
3122@@ -21,6 +21,7 @@
3123
3124 #include <core/media/service.h>
3125
3126+#include "cover_art_resolver.h"
3127 #include "service_traits.h"
3128
3129 #include <core/dbus/skeleton.h>
3130@@ -35,10 +36,36 @@
3131 {
3132 class ServiceSkeleton : public core::dbus::Skeleton<core::ubuntu::media::Service>
3133 {
3134- public:
3135- ServiceSkeleton();
3136+public:
3137+ // Functor for enumerating all known (key, player) pairs.
3138+ typedef std::function
3139+ <
3140+ void(
3141+ // The key of the player.
3142+ const core::ubuntu::media::Player::PlayerKey&,
3143+ // The actual player instance.
3144+ const std::shared_ptr<core::ubuntu::media::Player>&
3145+ )
3146+ > PlayerEnumerator;
3147+
3148+ ServiceSkeleton(const CoverArtResolver& cover_art_resolver = always_missing_cover_art_resolver());
3149 ~ServiceSkeleton();
3150
3151+ // We keep track of all known player sessions here and render them accessible via
3152+ // the key. All of these functions are thread-safe but not reentrant.
3153+ // Returns true iff a player is known for the given key.
3154+ bool has_player_for_key(const Player::PlayerKey& key) const;
3155+ // Returns the player for the given key or throws std::out_of_range if no player is known
3156+ // for the given key.
3157+ std::shared_ptr<Player> player_for_key(const Player::PlayerKey& key) const;
3158+ // Enumerates all known players and invokes the given enumerator for each
3159+ // (key, player) pair.
3160+ void enumerate_players(const PlayerEnumerator& enumerator) const;
3161+ // Removes the player for the given key, and unsets it if it is the current one.
3162+ void remove_player_for_key(const Player::PlayerKey& key);
3163+ // Makes the player known under the given key current.
3164+ void set_current_player_for_key(const Player::PlayerKey& key);
3165+
3166 void run();
3167 void stop();
3168
3169
3170=== modified file 'src/core/media/the_session_bus.cpp'
3171--- src/core/media/the_session_bus.cpp 2014-02-12 15:53:57 +0000
3172+++ src/core/media/the_session_bus.cpp 2014-09-10 21:05:53 +0000
3173@@ -25,7 +25,7 @@
3174 std::once_flag once;
3175 }
3176
3177-core::dbus::Bus::Ptr the_session_bus()
3178+core::dbus::Bus::Ptr core::ubuntu::media::the_session_bus()
3179 {
3180 static core::dbus::Bus::Ptr bus
3181 = std::make_shared<core::dbus::Bus>(
3182
3183=== modified file 'src/core/media/the_session_bus.h'
3184--- src/core/media/the_session_bus.h 2014-02-12 15:53:57 +0000
3185+++ src/core/media/the_session_bus.h 2014-09-10 21:05:53 +0000
3186@@ -21,6 +21,14 @@
3187
3188 #include <core/dbus/bus.h>
3189
3190+namespace core
3191+{
3192+namespace ubuntu
3193+{
3194+namespace media
3195+{
3196 core::dbus::Bus::Ptr the_session_bus();
3197-
3198+}
3199+}
3200+}
3201 #endif // THE_SESSION_BUS_H_
3202
3203=== added file 'src/core/media/xesam.h'
3204--- src/core/media/xesam.h 1970-01-01 00:00:00 +0000
3205+++ src/core/media/xesam.h 2014-09-10 21:05:53 +0000
3206@@ -0,0 +1,56 @@
3207+/*
3208+ * Copyright © 2014 Canonical Ltd.
3209+ *
3210+ * This program is free software: you can redistribute it and/or modify it
3211+ * under the terms of the GNU Lesser General Public License version 3,
3212+ * as published by the Free Software Foundation.
3213+ *
3214+ * This program is distributed in the hope that it will be useful,
3215+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3216+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3217+ * GNU Lesser General Public License for more details.
3218+ *
3219+ * You should have received a copy of the GNU Lesser General Public License
3220+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3221+ *
3222+ * Authored by: Thomas Voß <thomas.voss@canonical.com>
3223+ */
3224+
3225+#ifndef XESAM_H_
3226+#define XESAM_H_
3227+
3228+#include <cstdint>
3229+
3230+#include <string>
3231+#include <vector>
3232+
3233+#define DATUM(Type, Name, VType) \
3234+ struct Type \
3235+ {\
3236+ static constexpr const char* name{#Name};\
3237+ typedef VType ValueType;\
3238+ };
3239+
3240+namespace xesam
3241+{
3242+DATUM(Album, xesam:album, std::string)
3243+DATUM(AlbumArtist, xesam:albumArtist, std::vector<std::string>)
3244+DATUM(Artist, xesam:artist, std::vector<std::string>)
3245+DATUM(AsText, xesam:asText, std::string)
3246+DATUM(AudioBpm, xesam:audioBpm, std::int32_t)
3247+DATUM(AutoRating, xesam:autoRating, double)
3248+DATUM(Comment, xesam:comment, std::vector<std::string>)
3249+DATUM(Composer, xesam:composer, std::vector<std::string>)
3250+DATUM(ContentCreated, xesam:comment, std::string)
3251+DATUM(DiscNumber, xesam:discNumber, std::int32_t)
3252+DATUM(FirstUsed, xesam:firstUsed, std::string)
3253+DATUM(Genre, xesam:genre, std::vector<std::string>)
3254+DATUM(LastUsed, xesam:lastUsed, std::string)
3255+DATUM(Lyricist, xesam:lyricist, std::vector<std::string>)
3256+DATUM(Title, xesam:title, std::string)
3257+DATUM(TrackNumber, xesam:trackNumber, std::int32_t)
3258+DATUM(Url, xesam:url, std::string)
3259+DATUM(UserRating, xesam:userRating, double)
3260+}
3261+
3262+#endif // XESAM_H_
3263
3264=== added file 'symbols.map'
3265--- symbols.map 1970-01-01 00:00:00 +0000
3266+++ symbols.map 2014-09-10 21:05:53 +0000
3267@@ -0,0 +1,16 @@
3268+{
3269+global:
3270+ extern "C++" {
3271+ core::ubuntu::media::*;
3272+ typeinfo?for?core::ubuntu::media::*;
3273+ typeinfo?name?for?core::ubuntu::media::*;
3274+ VTT?for?core::ubuntu::media::*;
3275+ virtual?thunk?to?core::ubuntu::media::*;
3276+ vtable?for?core::ubuntu::media::*;
3277+ std::hash*;
3278+ };
3279+local:
3280+ extern "C++" {
3281+ *;
3282+ };
3283+};
3284
3285=== modified file 'tests/unit-tests/CMakeLists.txt'
3286--- tests/unit-tests/CMakeLists.txt 2014-04-08 19:49:20 +0000
3287+++ tests/unit-tests/CMakeLists.txt 2014-09-10 21:05:53 +0000
3288@@ -8,6 +8,7 @@
3289 test-gstreamer-engine
3290
3291 libmedia-mock.cpp
3292+ ${CMAKE_SOURCE_DIR}/src/core/media/cover_art_resolver.cpp
3293 ${CMAKE_SOURCE_DIR}/src/core/media/engine.cpp
3294 ${CMAKE_SOURCE_DIR}/src/core/media/gstreamer/engine.cpp
3295 ${CMAKE_SOURCE_DIR}/src/core/media/player_skeleton.cpp
3296
3297=== modified file 'tests/unit-tests/test-gstreamer-engine.cpp'
3298--- tests/unit-tests/test-gstreamer-engine.cpp 2014-06-24 20:37:44 +0000
3299+++ tests/unit-tests/test-gstreamer-engine.cpp 2014-09-10 21:05:53 +0000
3300@@ -20,6 +20,7 @@
3301 #include <core/media/player.h>
3302 #include <core/media/track_list.h>
3303
3304+#include "core/media/xesam.h"
3305 #include "core/media/gstreamer/engine.h"
3306
3307 #include "../test_data.h"
3308@@ -79,18 +80,18 @@
3309 engine.track_meta_data().changed().connect(
3310 [](const std::tuple<media::Track::UriType, media::Track::MetaData>& md)
3311 {
3312- if (0 < std::get<1>(md).count(media::Engine::Xesam::album()))
3313- EXPECT_EQ("Ezwa", std::get<1>(md).get(media::Engine::Xesam::album()));
3314- if (0 < std::get<1>(md).count(media::Engine::Xesam::album_artist()))
3315- EXPECT_EQ("Ezwa", std::get<1>(md).get(media::Engine::Xesam::album_artist()));
3316- if (0 < std::get<1>(md).count(media::Engine::Xesam::artist()))
3317- EXPECT_EQ("Ezwa", std::get<1>(md).get(media::Engine::Xesam::artist()));
3318- if (0 < std::get<1>(md).count(media::Engine::Xesam::disc_number()))
3319- EXPECT_EQ("42", std::get<1>(md).get(media::Engine::Xesam::disc_number()));
3320- if (0 < std::get<1>(md).count(media::Engine::Xesam::genre()))
3321- EXPECT_EQ("Test", std::get<1>(md).get(media::Engine::Xesam::genre()));
3322- if (0 < std::get<1>(md).count(media::Engine::Xesam::track_number()))
3323- EXPECT_EQ("42", std::get<1>(md).get(media::Engine::Xesam::track_number()));
3324+ if (0 < std::get<1>(md).count(xesam::Album::name))
3325+ EXPECT_EQ("Ezwa", std::get<1>(md).get(xesam::Album::name));
3326+ if (0 < std::get<1>(md).count(xesam::AlbumArtist::name))
3327+ EXPECT_EQ("Ezwa", std::get<1>(md).get(xesam::AlbumArtist::name));
3328+ if (0 < std::get<1>(md).count(xesam::Artist::name))
3329+ EXPECT_EQ("Ezwa", std::get<1>(md).get(xesam::Artist::name));
3330+ if (0 < std::get<1>(md).count(xesam::DiscNumber::name))
3331+ EXPECT_EQ("42", std::get<1>(md).get(xesam::DiscNumber::name));
3332+ if (0 < std::get<1>(md).count(xesam::Genre::name))
3333+ EXPECT_EQ("Test", std::get<1>(md).get(xesam::Genre::name));
3334+ if (0 < std::get<1>(md).count(xesam::TrackNumber::name))
3335+ EXPECT_EQ("42", std::get<1>(md).get(xesam::TrackNumber::name));
3336 });
3337
3338 engine.state().changed().connect(
3339@@ -126,12 +127,12 @@
3340 engine.track_meta_data().changed().connect(
3341 [](const std::tuple<media::Track::UriType, media::Track::MetaData>& md)
3342 {
3343- if (0 < std::get<1>(md).count(media::Engine::Xesam::album()))
3344- EXPECT_EQ("Test series", std::get<1>(md).get(media::Engine::Xesam::album()));
3345- if (0 < std::get<1>(md).count(media::Engine::Xesam::artist()))
3346- EXPECT_EQ("Canonical", std::get<1>(md).get(media::Engine::Xesam::artist()));
3347- if (0 < std::get<1>(md).count(media::Engine::Xesam::genre()))
3348- EXPECT_EQ("Documentary", std::get<1>(md).get(media::Engine::Xesam::genre()));
3349+ if (0 < std::get<1>(md).count(xesam::Album::name))
3350+ EXPECT_EQ("Test series", std::get<1>(md).get(xesam::Album::name));
3351+ if (0 < std::get<1>(md).count(xesam::Artist::name))
3352+ EXPECT_EQ("Canonical", std::get<1>(md).get(xesam::Artist::name));
3353+ if (0 < std::get<1>(md).count(xesam::Genre::name))
3354+ EXPECT_EQ("Documentary", std::get<1>(md).get(xesam::Genre::name));
3355 });
3356
3357 engine.state().changed().connect(
3358@@ -342,17 +343,17 @@
3359 gstreamer::Engine engine;
3360 auto md = engine.meta_data_extractor()->meta_data_for_track_with_uri(test_file_uri);
3361
3362- if (0 < md.count(media::Engine::Xesam::album()))
3363- EXPECT_EQ("Test", md.get(media::Engine::Xesam::album()));
3364- if (0 < md.count(media::Engine::Xesam::album_artist()))
3365- EXPECT_EQ("Test", md.get(media::Engine::Xesam::album_artist()));
3366- if (0 < md.count(media::Engine::Xesam::artist()))
3367- EXPECT_EQ("Test", md.get(media::Engine::Xesam::artist()));
3368- if (0 < md.count(media::Engine::Xesam::disc_number()))
3369- EXPECT_EQ("42", md.get(media::Engine::Xesam::disc_number()));
3370- if (0 < md.count(media::Engine::Xesam::genre()))
3371- EXPECT_EQ("Test", md.get(media::Engine::Xesam::genre()));
3372- if (0 < md.count(media::Engine::Xesam::track_number()))
3373- EXPECT_EQ("42", md.get(media::Engine::Xesam::track_number()));
3374+ if (0 < md.count(xesam::Album::name))
3375+ EXPECT_EQ("Test", md.get(xesam::Album::name));
3376+ if (0 < md.count(xesam::AlbumArtist::name))
3377+ EXPECT_EQ("Test", md.get(xesam::AlbumArtist::name));
3378+ if (0 < md.count(xesam::Artist::name))
3379+ EXPECT_EQ("Test", md.get(xesam::Artist::name));
3380+ if (0 < md.count(xesam::DiscNumber::name))
3381+ EXPECT_EQ("42", md.get(xesam::DiscNumber::name));
3382+ if (0 < md.count(xesam::Genre::name))
3383+ EXPECT_EQ("Test", md.get(xesam::Genre::name));
3384+ if (0 < md.count(xesam::TrackNumber::name))
3385+ EXPECT_EQ("42", md.get(xesam::TrackNumber::name));
3386 }
3387

Subscribers

People subscribed via source and target branches