Merge lp:~thomas-voss/media-hub/use-mpris-player-skeleton-and-register-with-indicator into lp:media-hub
- use-mpris-player-skeleton-and-register-with-indicator
- Merge into trunk
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 |
Related bugs: |
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.
PS Jenkins bot (ps-jenkins) wrote : | # |
- 64. By Thomas Voß
-
Wire up more property changes across different MPRIS representations.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:64
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 65. By Thomas Voß
-
Make sure that a valid bus name is requested.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:65
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 66. By Thomas Voß
-
Clean up meta-data extraction and property updates.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:66
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 67. By Thomas Voß
-
Initialize player properties and communicate track meta data via MPRIS.
- 68. By Thomas Voß
-
Add some temporary debugging output.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:67
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 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.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:69
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:70
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
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.
- 71. By Thomas Voß
-
Factor out property initialization into mpris::Player and mpris::MediaPlayer2 classes.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:71
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 72. By Thomas Voß
-
Switch to std::int64_t for encoding timestamps.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:72
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 73. By Thomas Voß
-
More uint64_t -> int64_t changes.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:73
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 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.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:74
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:75
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 76. By Thomas Voß
-
Support GetAll calls for properties.
Disable next/previous capabilities.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:76
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 77. By Thomas Voß
-
Resolve cover art.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:77
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 78. By Thomas Voß
-
Add playlists implementation.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:78
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 79. By Thomas Voß
-
Remove emission of change signals on construction.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:79
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 80. By Thomas Voß
-
Infer the identity of a player from its app armor profile.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:80
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 81. By Thomas Voß
-
Fix typo in object path.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:81
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 82. By Thomas Voß
-
Add app id translation as an intermediate step to circumvent https:/
/bugs.launchpad .net/indicator- sound/+ bug/1364241.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:82
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 83. By Thomas Voß
-
Use the complete versioned app id as identity.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:83
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 84. By Thomas Voß
-
Make stop() a noop if state is already stopped.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:84
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 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 .
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:85
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 86. By Thomas Voß
-
Remerge changes from prerequisite branch.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:86
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 87. By Thomas Voß
-
Move exposure to MPRIS-handlers to the service, and dynamically switch players as they are created and made current.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:87
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 88. By Thomas Voß
-
Only make player current if it is providing a multi-media stream.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:88
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 89. By Thomas Voß
-
Play/Pause should call play() or pause(), not next().
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:89
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 90. By Thomas Voß
-
Factor out cover art resolver.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:90
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 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.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:91
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:92
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 93. By Thomas Voß
-
Unset current player if client disconnects.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:93
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 94. By Thomas Voß
-
Adjust copyright dates.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:94
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Nick Dedekind (nick-dedekind) wrote : | # |
LGTM.
Player appears in sound indicator and responds to feedback.
TODO - previous/
Marcus Tomlinson (marcustomlinson) wrote : | # |
1965 +media:
The following case is missing from this method:
auto play_pause = std::bind(
d->
- 95. By Thomas Voß
-
Wire up PlayPause correctly.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:95
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Jim Hodapp (jhodapp) wrote : | # |
Just a few simple changes.
- 96. By Thomas Voß
-
Address reviewer comments.
Jim Hodapp (jhodapp) wrote : | # |
Looks good, thanks!
Preview Diff
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 ¬ifying) |
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 ¤t_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 ¬ifying) |
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 |
PASSED: Continuous integration, rev:63 jenkins. qa.ubuntu. com/job/ media-hub- ci/90/ jenkins. qa.ubuntu. com/job/ media-hub- utopic- amd64-ci/ 32 jenkins. qa.ubuntu. com/job/ media-hub- utopic- armhf-ci/ 31 jenkins. qa.ubuntu. com/job/ media-hub- utopic- armhf-ci/ 31/artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ media-hub- utopic- i386-ci/ 30
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/media- hub-ci/ 90/rebuild
http://