Merge lp:~jhodapp/media-hub/fix-zero-position-while-seeking into lp:media-hub

Proposed by Jim Hodapp
Status: Merged
Approved by: Ricardo Mendoza
Approved revision: 124
Merged at revision: 126
Proposed branch: lp:~jhodapp/media-hub/fix-zero-position-while-seeking
Merge into: lp:media-hub
Prerequisite: lp:~thomas-voss/media-hub/decouple-player-skeleton-and-implementation
Diff against target: 3163 lines (+1445/-633)
34 files modified
debian/changelog (+6/-9)
debian/control (+0/-6)
include/core/media/player.h (+1/-0)
src/core/media/CMakeLists.txt (+6/-2)
src/core/media/apparmor.h (+0/-93)
src/core/media/apparmor/context.cpp (+34/-0)
src/core/media/apparmor/context.h (+51/-0)
src/core/media/apparmor/dbus.h (+94/-0)
src/core/media/apparmor/ubuntu.cpp (+198/-0)
src/core/media/apparmor/ubuntu.h (+167/-0)
src/core/media/gstreamer/engine.cpp (+3/-3)
src/core/media/gstreamer/playbin.cpp (+23/-6)
src/core/media/gstreamer/playbin.h (+2/-0)
src/core/media/hashed_keyed_player_store.cpp (+80/-0)
src/core/media/hashed_keyed_player_store.h (+76/-0)
src/core/media/keyed_player_store.h (+83/-0)
src/core/media/mpris/player.h (+3/-0)
src/core/media/null_track_list.h (+114/-0)
src/core/media/player.cpp (+0/-1)
src/core/media/player_configuration.h (+0/-3)
src/core/media/player_implementation.cpp (+104/-81)
src/core/media/player_implementation.h (+9/-9)
src/core/media/player_skeleton.cpp (+35/-93)
src/core/media/player_skeleton.h (+22/-16)
src/core/media/player_stub.cpp (+18/-0)
src/core/media/player_stub.h (+1/-0)
src/core/media/power/state_controller.cpp (+21/-25)
src/core/media/server/server.cpp (+19/-4)
src/core/media/service_implementation.cpp (+35/-27)
src/core/media/service_implementation.h (+2/-1)
src/core/media/service_skeleton.cpp (+125/-174)
src/core/media/service_skeleton.h (+14/-25)
src/core/media/telephony/call_monitor.cpp (+56/-37)
src/core/media/telephony/call_monitor.h (+43/-18)
To merge this branch: bzr merge lp:~jhodapp/media-hub/fix-zero-position-while-seeking
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration Approve
Ubuntu Phablet Team Pending
Review via email: mp+252605@code.launchpad.net

Commit message

* Prevent a 0 position from being reported to the app which happens while seeking. Covers bad behavior that happens from GStreamer. Also expose the about_to_finish signal to the client.
* Enable playback again after manually seeking all the way to EOS. Also only send VideoDimensionsChanged signal only when necessary.

Description of the change

* Prevent a 0 position from being reported to the app which happens while seeking. Covers bad behavior that happens from GStreamer. Also expose the about_to_finish signal to the client.
* Enable playback again after manually seeking all the way to EOS. Also only send VideoDimensionsChanged signal only when necessary.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
122. By Jim Hodapp

Merged prereqs.

123. By Jim Hodapp

Merge prereqs.

124. By Jim Hodapp

Fix the last merge.

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

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'debian/changelog'
--- debian/changelog 2015-03-16 15:38:27 +0000
+++ debian/changelog 2015-03-16 15:38:27 +0000
@@ -1,4 +1,9 @@
1<<<<<<< TREE1media-hub (3.0.0) UNRELEASED; urgency=medium
2
3 * Refactor client-facing interfaces to pull out explicit dependency on hybris-based media layer.
4
5 -- Thomas Voss <thomas.voss@canonical.com> Tue, 11 Nov 2014 17:15:52 +0100
6
2media-hub (2.0.0+15.04.20150303-0ubuntu2) vivid; urgency=medium7media-hub (2.0.0+15.04.20150303-0ubuntu2) vivid; urgency=medium
38
4 * debian/control:9 * debian/control:
@@ -17,14 +22,6 @@
1722
18 -- CI Train Bot <ci-train-bot@canonical.com> Tue, 03 Mar 2015 22:56:52 +000023 -- CI Train Bot <ci-train-bot@canonical.com> Tue, 03 Mar 2015 22:56:52 +0000
1924
20=======
21media-hub (3.0.0) UNRELEASED; urgency=medium
22
23 * Refactor client-facing interfaces to pull out explicit dependency on hybris-based media layer.
24
25 -- Thomas Voss <thomas.voss@canonical.com> Tue, 11 Nov 2014 17:15:52 +0100
26
27>>>>>>> MERGE-SOURCE
28media-hub (2.0.0+15.04.20150120-0ubuntu1) vivid; urgency=low25media-hub (2.0.0+15.04.20150120-0ubuntu1) vivid; urgency=low
2926
30 [ Jim Hodapp ]27 [ Jim Hodapp ]
3128
=== modified file 'debian/control'
--- debian/control 2015-03-16 15:38:27 +0000
+++ debian/control 2015-03-16 15:38:27 +0000
@@ -39,14 +39,8 @@
39Section: libdevel39Section: libdevel
40Architecture: any40Architecture: any
41Multi-Arch: same41Multi-Arch: same
42<<<<<<< TREE
43Depends: libmedia-hub-common2 (= ${binary:Version}),
44 libmedia-hub-client2 (= ${binary:Version}),
45=======
46Pre-Depends: dpkg (>= 1.15.6~)
47Depends: libmedia-hub-common3 (= ${binary:Version}),42Depends: libmedia-hub-common3 (= ${binary:Version}),
48 libmedia-hub-client3 (= ${binary:Version}),43 libmedia-hub-client3 (= ${binary:Version}),
49>>>>>>> MERGE-SOURCE
50 ${misc:Depends},44 ${misc:Depends},
51 libproperties-cpp-dev,45 libproperties-cpp-dev,
52Suggests: libmedia-hub-doc46Suggests: libmedia-hub-doc
5347
=== modified file 'include/core/media/player.h'
--- include/core/media/player.h 2015-03-16 15:38:27 +0000
+++ include/core/media/player.h 2015-03-16 15:38:27 +0000
@@ -168,6 +168,7 @@
168 virtual core::Property<Lifetime>& lifetime() = 0;168 virtual core::Property<Lifetime>& lifetime() = 0;
169169
170 virtual const core::Signal<int64_t>& seeked_to() const = 0;170 virtual const core::Signal<int64_t>& seeked_to() const = 0;
171 virtual const core::Signal<void>& about_to_finish() const = 0;
171 virtual const core::Signal<void>& end_of_stream() const = 0;172 virtual const core::Signal<void>& end_of_stream() const = 0;
172 virtual core::Signal<PlaybackStatus>& playback_status_changed() = 0;173 virtual core::Signal<PlaybackStatus>& playback_status_changed() = 0;
173 virtual const core::Signal<video::Dimensions>& video_dimension_changed() const = 0;174 virtual const core::Signal<video::Dimensions>& video_dimension_changed() const = 0;
174175
=== modified file 'src/core/media/CMakeLists.txt'
--- src/core/media/CMakeLists.txt 2015-03-16 15:38:27 +0000
+++ src/core/media/CMakeLists.txt 2015-03-16 15:38:27 +0000
@@ -11,7 +11,7 @@
1111
12message(STATUS ${MEDIA_HUB_HEADERS})12message(STATUS ${MEDIA_HUB_HEADERS})
1313
14add_subdirectory(call-monitor)14add_subdirectory(telephony)
1515
16add_library(16add_library(
17 media-hub-common SHARED17 media-hub-common SHARED
@@ -87,10 +87,14 @@
87 ${MPRIS_HEADERS}87 ${MPRIS_HEADERS}
8888
89 client_death_observer.cpp89 client_death_observer.cpp
90 hybris_client_death_observer.cpp90 hashed_keyed_player_store.cpp
91 hybris_client_death_observer.cpp
91 cover_art_resolver.cpp92 cover_art_resolver.cpp
92 engine.cpp93 engine.cpp
9394
95 apparmor/context.cpp
96 apparmor/ubuntu.cpp
97
94 audio/pulse_audio_output_observer.cpp98 audio/pulse_audio_output_observer.cpp
95 audio/ostream_reporter.cpp99 audio/ostream_reporter.cpp
96 audio/output_observer.cpp100 audio/output_observer.cpp
97101
=== added directory 'src/core/media/apparmor'
=== removed file 'src/core/media/apparmor.h'
--- src/core/media/apparmor.h 2014-09-01 07:49:45 +0000
+++ src/core/media/apparmor.h 1970-01-01 00:00:00 +0000
@@ -1,93 +0,0 @@
1/*
2 * Copyright (C) 2013-2014 Canonical Ltd
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Author: Jim Hodapp <jim.hodapp@canonical.com>
17 */
18
19#ifndef APPARMOR_DBUS_H_
20#define APPARMOR_DBUS_H_
21
22#include <core/dbus/macros.h>
23#include <core/dbus/object.h>
24#include <core/dbus/service.h>
25
26#include <string>
27#include <chrono>
28
29// TODO(tvoss): This really should live in trust-store, providing a straightforward
30// way for parties involved in managing trust relationships to query peers' apparmor
31// profiles. Please see https://bugs.launchpad.net/trust-store/+bug/1350736 for the
32// related bug
33namespace org
34{
35namespace freedesktop
36{
37namespace dbus
38{
39struct DBus
40{
41 static const std::string& name()
42 {
43 static const std::string s = "org.freedesktop.DBus";
44 return s;
45 }
46
47 // Gets the AppArmor confinement string associated with the unique connection name. If
48 // D-Bus is not performing AppArmor mediation, the
49 // org.freedesktop.DBus.Error.AppArmorSecurityContextUnknown error is returned.
50 DBUS_CPP_METHOD_DEF(GetConnectionAppArmorSecurityContext, DBus)
51
52 struct Stub
53 {
54 // Creates a new stub instance for the given object to access
55 // DBus functionality.
56 Stub(const core::dbus::Object::Ptr& object) : object{object}
57 {
58 }
59
60 // Creates a new stub instance for the given bus connection
61 Stub(const core::dbus::Bus::Ptr& bus)
62 : object
63 {
64 core::dbus::Service::use_service<org::freedesktop::dbus::DBus>(bus)
65 ->object_for_path(core::dbus::types::ObjectPath{"/org/freedesktop/DBus"})
66 }
67 {
68 }
69
70 // Gets the AppArmor confinement string associated with the unique connection name. If
71 // D-Bus is not performing AppArmor mediation, the
72 // org.freedesktop.DBus.Error.AppArmorSecurityContextUnknown error is returned.
73 //
74 // Invokes the given handler on completion.
75 void get_connection_app_armor_security_async(
76 const std::string& name,
77 std::function<void(const std::string&)> handler)
78 {
79 object->invoke_method_asynchronously_with_callback<GetConnectionAppArmorSecurityContext, std::string>(
80 [handler](const core::dbus::Result<std::string>& result)
81 {
82 if (not result.is_error()) handler(result.value());
83 }, name);
84 }
85
86 core::dbus::Object::Ptr object;
87 };
88};
89}
90}
91}
92
93#endif // APPARMOR_DBUS_H_
940
=== added file 'src/core/media/apparmor/context.cpp'
--- src/core/media/apparmor/context.cpp 1970-01-01 00:00:00 +0000
+++ src/core/media/apparmor/context.cpp 2015-03-16 15:38:27 +0000
@@ -0,0 +1,34 @@
1/*
2 * Copyright © 2014 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License version 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Thomas Voß <thomas.voss@canonical.com>
17 */
18
19#include <core/media/apparmor/context.h>
20
21namespace apparmor = core::ubuntu::media::apparmor;
22
23apparmor::Context::Context(const std::string& name) : name{name}
24{
25 if (name.empty()) throw std::runtime_error
26 {
27 "apparmor::Context cannot be created for empty name."
28 };
29}
30
31const std::string& apparmor::Context::str() const
32{
33 return name;
34}
035
=== added file 'src/core/media/apparmor/context.h'
--- src/core/media/apparmor/context.h 1970-01-01 00:00:00 +0000
+++ src/core/media/apparmor/context.h 2015-03-16 15:38:27 +0000
@@ -0,0 +1,51 @@
1/*
2 * Copyright © 2014 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License version 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Thomas Voß <thomas.voss@canonical.com>
17 */
18#ifndef CORE_UBUNTU_MEDIA_APPARMOR_AUTHENTICATOR_H_
19#define CORE_UBUNTU_MEDIA_APPARMOR_AUTHENTICATOR_H_
20
21#include <memory>
22#include <string>
23
24namespace core
25{
26namespace ubuntu
27{
28namespace media
29{
30namespace apparmor
31{
32// Models an apparmor context name, and provides convenience functionality
33// on top of it.
34class Context
35{
36public:
37 // Constructs a new Context instance for the given raw name.
38 // Throws std::logic_error for empty names.
39 explicit Context(const std::string& name);
40 virtual ~Context() = default;
41 // Returns the raw string describing the context.
42 const std::string& str() const;
43
44private:
45 const std::string name;
46};
47}
48}
49}
50}
51#endif // CORE_UBUNTU_MEDIA_APPARMOR_AUTHENTICATOR_H_
052
=== added file 'src/core/media/apparmor/dbus.h'
--- src/core/media/apparmor/dbus.h 1970-01-01 00:00:00 +0000
+++ src/core/media/apparmor/dbus.h 2015-03-16 15:38:27 +0000
@@ -0,0 +1,94 @@
1/*
2 * Copyright (C) 2013-2014 Canonical Ltd
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Author: Jim Hodapp <jim.hodapp@canonical.com>
17 */
18
19#ifndef CORE_UBUNTU_MEDIA_APPARMOR_DBUS_H_
20#define CORE_UBUNTU_MEDIA_APPARMOR_DBUS_H_
21
22#include <core/dbus/bus.h>
23#include <core/dbus/macros.h>
24#include <core/dbus/object.h>
25#include <core/dbus/service.h>
26
27#include <string>
28#include <chrono>
29
30// TODO(tvoss): This really should live in trust-store, providing a straightforward
31// way for parties involved in managing trust relationships to query peers' apparmor
32// profiles. Please see https://bugs.launchpad.net/trust-store/+bug/1350736 for the
33// related bug
34namespace org
35{
36namespace freedesktop
37{
38namespace dbus
39{
40struct DBus
41{
42 static const std::string& name()
43 {
44 static const std::string s = "org.freedesktop.DBus";
45 return s;
46 }
47
48 // Gets the AppArmor confinement string associated with the unique connection name. If
49 // D-Bus is not performing AppArmor mediation, the
50 // org.freedesktop.DBus.Error.AppArmorSecurityContextUnknown error is returned.
51 DBUS_CPP_METHOD_DEF(GetConnectionAppArmorSecurityContext, DBus)
52
53 struct Stub
54 {
55 // Creates a new stub instance for the given object to access
56 // DBus functionality.
57 Stub(const core::dbus::Object::Ptr& object) : object{object}
58 {
59 }
60
61 // Creates a new stub instance for the given bus connection
62 Stub(const core::dbus::Bus::Ptr& bus)
63 : object
64 {
65 core::dbus::Service::use_service<org::freedesktop::dbus::DBus>(bus)
66 ->object_for_path(core::dbus::types::ObjectPath{"/org/freedesktop/DBus"})
67 }
68 {
69 }
70
71 // Gets the AppArmor confinement string associated with the unique connection name. If
72 // D-Bus is not performing AppArmor mediation, the
73 // org.freedesktop.DBus.Error.AppArmorSecurityContextUnknown error is returned.
74 //
75 // Invokes the given handler on completion.
76 void get_connection_app_armor_security_async(
77 const std::string& name,
78 std::function<void(const std::string&)> handler)
79 {
80 object->invoke_method_asynchronously_with_callback<GetConnectionAppArmorSecurityContext, std::string>(
81 [handler](const core::dbus::Result<std::string>& result)
82 {
83 if (not result.is_error()) handler(result.value());
84 }, name);
85 }
86
87 core::dbus::Object::Ptr object;
88 };
89};
90}
91}
92}
93
94#endif // CORE_UBUNTU_MEDIA_APPARMOR_DBUS_H_
095
=== added file 'src/core/media/apparmor/ubuntu.cpp'
--- src/core/media/apparmor/ubuntu.cpp 1970-01-01 00:00:00 +0000
+++ src/core/media/apparmor/ubuntu.cpp 2015-03-16 15:38:27 +0000
@@ -0,0 +1,198 @@
1/*
2 * Copyright © 2014 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License version 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Thomas Voß <thomas.voss@canonical.com>
17 */
18
19#include <core/media/apparmor/ubuntu.h>
20
21#include <core/media/external_services.h>
22
23#include <iostream>
24#include <regex>
25
26namespace apparmor = core::ubuntu::media::apparmor;
27namespace media = core::ubuntu::media;
28namespace ubuntu = apparmor::ubuntu;
29
30namespace
31{
32struct Uri
33{
34 std::string scheme;
35 std::string authority;
36 std::string path;
37 std::string query;
38 std::string fragment;
39};
40
41// Poor mans version of a uri parser.
42// See https://tools.ietf.org/html/rfc3986#appendix-B
43Uri parse_uri(const std::string& s)
44{
45 // Indices into the regex match go here.
46 struct Index
47 {
48 const std::size_t scheme{2};
49 const std::size_t authority{4};
50 const std::size_t path{5};
51 const std::size_t query{7};
52 const std::size_t fragment{9};
53 } static index;
54
55 static const std::regex regex{R"delim(^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?)delim"};
56 std::smatch match;
57
58 if (not std::regex_match(s, match, regex)) throw std::runtime_error
59 {
60 "Not a valid URI: " + s
61 };
62
63 return Uri
64 {
65 match.str(index.scheme),
66 match.str(index.authority),
67 match.str(index.path),
68 match.str(index.query),
69 match.str(index.fragment)
70 };
71}
72
73static constexpr std::size_t index_package{1};
74static constexpr std::size_t index_app{2};
75
76// Returns true if the context name is a valid Ubuntu app id.
77// If it is, out is populated with the package and app name.
78bool process_context_name(const std::string& s, std::smatch& out)
79{
80 // See https://wiki.ubuntu.com/AppStore/Interfaces/ApplicationId.
81 static const std::regex short_re{"(.*)_(.*)"};
82 static const std::regex full_re{"(.*)_(.*)_(.*)"};
83
84 if (std::regex_match(s, out, full_re))
85 return true;
86
87 if (std::regex_match(s, out, short_re))
88 return true;
89
90 return false;
91}
92}
93
94apparmor::ubuntu::Context::Context(const std::string& name)
95 : apparmor::Context{name},
96 unconfined_{str() == ubuntu::unconfined},
97 has_package_name_{process_context_name(str(), match_)}
98{
99 if (not is_unconfined() && not has_package_name()) throw std::logic_error
100 {
101 "apparmor::ubuntu::Context: Invalid profile name " + str()
102 };
103}
104
105bool apparmor::ubuntu::Context::is_unconfined() const
106{
107 return unconfined_;
108}
109
110bool apparmor::ubuntu::Context::has_package_name() const
111{
112 return has_package_name_;
113}
114
115
116std::string apparmor::ubuntu::Context::package_name() const
117{
118 return std::string{match_[index_package]};
119}
120
121apparmor::ubuntu::DBusDaemonRequestContextResolver::DBusDaemonRequestContextResolver(const core::dbus::Bus::Ptr& bus) : dbus_daemon{bus}
122{
123}
124
125void apparmor::ubuntu::DBusDaemonRequestContextResolver::resolve_context_for_dbus_name_async(
126 const std::string& name,
127 apparmor::ubuntu::RequestContextResolver::ResolveCallback cb)
128{
129 dbus_daemon.get_connection_app_armor_security_async(name, [cb](const std::string& context_name)
130 {
131 cb(apparmor::ubuntu::Context{context_name});
132 });
133}
134
135apparmor::ubuntu::RequestAuthenticator::Result apparmor::ubuntu::ExistingAuthenticator::authenticate_open_uri_request(const apparmor::ubuntu::Context& context, const std::string& uri)
136{
137 if (context.is_unconfined())
138 return Result{true, "Client allowed access since it's unconfined"};
139
140 Uri parsed_uri = parse_uri(uri);
141
142 // All confined apps can access their own files
143 if (parsed_uri.path.find(std::string(".local/share/" + context.package_name() + "/")) != std::string::npos ||
144 parsed_uri.path.find(std::string(".cache/" + context.package_name() + "/")) != std::string::npos)
145 {
146 return Result
147 {
148 true,
149 "Client can access content in ~/.local/share/" + context.package_name() + " or ~/.cache/" + context.package_name()
150 };
151 }
152 else if (parsed_uri.path.find(std::string("opt/click.ubuntu.com/")) != std::string::npos &&
153 parsed_uri.path.find(context.package_name()) != std::string::npos)
154 {
155 return Result{true, "Client can access content in own opt directory"};
156 }
157 else if ((parsed_uri.path.find(std::string("/system/media/audio/ui/")) != std::string::npos ||
158 parsed_uri.path.find(std::string("/android/system/media/audio/ui/")) != std::string::npos) &&
159 context.package_name() == "com.ubuntu.camera")
160 {
161 return Result{true, "Camera app can access ui sounds"};
162 }
163
164 // TODO: Check if the trust store previously allowed direct access to uri
165
166 // Check in ~/Music and ~/Videos
167 // TODO: when the trust store lands, check it to see if this app can access the dirs and
168 // then remove the explicit whitelist of the music-app, and gallery-app
169 else if ((context.package_name() == "com.ubuntu.music" || context.package_name() == "com.ubuntu.gallery") &&
170 (parsed_uri.path.find(std::string("Music/")) != std::string::npos ||
171 parsed_uri.path.find(std::string("Videos/")) != std::string::npos ||
172 parsed_uri.path.find(std::string("/media")) != std::string::npos))
173 {
174 return Result{true, "Client can access content in ~/Music or ~/Videos"};
175 }
176 else if (parsed_uri.path.find(std::string("/usr/share/sounds")) != std::string::npos)
177 {
178 return Result{true, "Client can access content in /usr/share/sounds"};
179 }
180 else if (parsed_uri.scheme == "http" || parsed_uri.scheme == "rtsp")
181 {
182 return Result{true, "Client can access streaming content"};
183 }
184
185 return Result{false, "Client is not allowed to access: " + uri};
186}
187
188// Returns the platform-default implementation of RequestContextResolver.
189apparmor::ubuntu::RequestContextResolver::Ptr apparmor::ubuntu::make_platform_default_request_context_resolver(media::helper::ExternalServices& es)
190{
191 return std::make_shared<apparmor::ubuntu::DBusDaemonRequestContextResolver>(es.session);
192}
193
194// Returns the platform-default implementation of RequestAuthenticator.
195apparmor::ubuntu::RequestAuthenticator::Ptr apparmor::ubuntu::make_platform_default_request_authenticator()
196{
197 return std::make_shared<apparmor::ubuntu::ExistingAuthenticator>();
198}
0199
=== added file 'src/core/media/apparmor/ubuntu.h'
--- src/core/media/apparmor/ubuntu.h 1970-01-01 00:00:00 +0000
+++ src/core/media/apparmor/ubuntu.h 2015-03-16 15:38:27 +0000
@@ -0,0 +1,167 @@
1/*
2 * Copyright © 2014 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License version 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Thomas Voß <thomas.voss@canonical.com>
17 */
18#ifndef CORE_UBUNTU_MEDIA_APPARMOR_UBUNTU_H_
19#define CORE_UBUNTU_MEDIA_APPARMOR_UBUNTU_H_
20
21#include <core/media/apparmor/context.h>
22#include <core/media/apparmor/dbus.h>
23
24#include <functional>
25#include <memory>
26#include <regex>
27#include <string>
28#include <vector>
29
30namespace core
31{
32namespace dbus
33{
34class Bus;
35}
36
37namespace ubuntu
38{
39namespace media
40{
41namespace helper
42{
43struct ExternalServices;
44}
45namespace apparmor
46{
47// Collects Ubuntu-specific apparmor conventions, e.g., format
48// of short and full package names as well as convenience functionality
49// to inspect apparmor::Context instances.
50namespace ubuntu
51{
52// The unconfined profile, unconditionally trusted
53// by the system.
54static constexpr const char* unconfined
55{
56 "unconfined"
57};
58
59class Context : public apparmor::Context
60{
61public:
62 // Constructs a new Context instance for the given raw name.
63 // Throws std::logic_error for empty names or for names not
64 // complying to Ubuntu conventions.
65 Context(const std::string& name);
66
67 // Returns true iff the context is unconfined.
68 virtual bool is_unconfined() const;
69
70 // Returns true iff the context contains a package name.
71 virtual bool has_package_name() const;
72
73 // Returns the package name or throws if no package name can be found.
74 virtual std::string package_name() const;
75
76private:
77 std::smatch match_;
78 const bool unconfined_;
79 const bool has_package_name_;
80};
81
82// Abstracts query for the apparmor context of an incoming request
83class RequestContextResolver
84{
85public:
86 // To save us some typing.
87 typedef std::shared_ptr<RequestContextResolver> Ptr;
88
89 // Callback for resolve context operations.
90 typedef std::function<void(const Context&)> ResolveCallback;
91
92 // Resolves the given name (of a dbus participant) to its apparmor context,
93 // invoking the callback whenever a result is available.
94 virtual void resolve_context_for_dbus_name_async(const std::string& name, ResolveCallback cb) = 0;
95
96protected:
97 RequestContextResolver() = default;
98 RequestContextResolver(const RequestContextResolver&) = delete;
99 virtual ~RequestContextResolver() = default;
100 RequestContextResolver& operator=(const RequestContextResolver&) = delete;
101};
102
103// An implementation of RequestContextResolver that queries the dbus
104// daemon to resolve the apparmor context.
105class DBusDaemonRequestContextResolver : public RequestContextResolver
106{
107public:
108 // To save us some typing.
109 typedef std::shared_ptr<DBusDaemonRequestContextResolver> Ptr;
110
111 // Constructs a new instance for the given bus connection.
112 DBusDaemonRequestContextResolver(const core::dbus::Bus::Ptr &);
113
114 // From RequestContextResolver
115 void resolve_context_for_dbus_name_async(const std::string& name, ResolveCallback) override;
116
117private:
118 org::freedesktop::dbus::DBus::Stub dbus_daemon;
119};
120
121// Abstracts an apparmor-based authentication of
122// incoming requests from clients.
123class RequestAuthenticator
124{
125public:
126 // To save us some typing.
127 typedef std::shared_ptr<RequestAuthenticator> Ptr;
128
129 // Return type of an authentication call.
130 typedef std::tuple
131 <
132 bool, // True if authenticated, false if not.
133 std::string // Reason for the result.
134 > Result;
135
136 virtual ~RequestAuthenticator() = default;
137
138 // Returns true iff the client identified by the given apparmor::Context is allowed
139 // to access the given uri, false otherwise.
140 virtual Result authenticate_open_uri_request(const Context&, const std::string& uri) = 0;
141
142protected:
143 RequestAuthenticator() = default;
144 RequestAuthenticator(const RequestAuthenticator&) = default;
145 RequestAuthenticator& operator=(const RequestAuthenticator&) = default;
146};
147
148// Takes the existing logic and exposes it as an implementation
149// of the RequestAuthenticator interface.
150struct ExistingAuthenticator : public RequestAuthenticator
151{
152 ExistingAuthenticator() = default;
153 // From RequestAuthenticator
154 Result authenticate_open_uri_request(const Context&, const std::string& uri) override;
155};
156
157// Returns the platform-default implementation of RequestContextResolver.
158RequestContextResolver::Ptr make_platform_default_request_context_resolver(helper::ExternalServices& es);
159// Returns the platform-default implementation of RequestAuthenticator.
160RequestAuthenticator::Ptr make_platform_default_request_authenticator();
161}
162}
163}
164}
165}
166
167#endif // CORE_UBUNTU_MEDIA_APPARMOR_UBUNTU_H_
0168
=== modified file 'src/core/media/gstreamer/engine.cpp'
--- src/core/media/gstreamer/engine.cpp 2015-03-16 15:38:27 +0000
+++ src/core/media/gstreamer/engine.cpp 2015-03-16 15:38:27 +0000
@@ -355,7 +355,7 @@
355 if (result)355 if (result)
356 {356 {
357 d->state = media::Engine::State::playing;357 d->state = media::Engine::State::playing;
358 cout << "play" << endl;358 cout << __PRETTY_FUNCTION__ << endl;
359 d->playback_status_changed(media::Player::PlaybackStatus::playing);359 d->playback_status_changed(media::Player::PlaybackStatus::playing);
360 }360 }
361361
@@ -373,7 +373,7 @@
373 if (result)373 if (result)
374 {374 {
375 d->state = media::Engine::State::stopped;375 d->state = media::Engine::State::stopped;
376 cout << "stop" << endl;376 cout << __PRETTY_FUNCTION__ << endl;
377 d->playback_status_changed(media::Player::PlaybackStatus::stopped);377 d->playback_status_changed(media::Player::PlaybackStatus::stopped);
378 }378 }
379379
@@ -387,7 +387,7 @@
387 if (result)387 if (result)
388 {388 {
389 d->state = media::Engine::State::paused;389 d->state = media::Engine::State::paused;
390 cout << "pause" << endl;390 cout << __PRETTY_FUNCTION__ << endl;
391 d->playback_status_changed(media::Player::PlaybackStatus::paused);391 d->playback_status_changed(media::Player::PlaybackStatus::paused);
392 }392 }
393393
394394
=== modified file 'src/core/media/gstreamer/playbin.cpp'
--- src/core/media/gstreamer/playbin.cpp 2015-03-16 15:38:27 +0000
+++ src/core/media/gstreamer/playbin.cpp 2015-03-16 15:38:27 +0000
@@ -97,11 +97,15 @@
97 this,97 this,
98 std::placeholders::_1))),98 std::placeholders::_1))),
99 is_seeking(false),99 is_seeking(false),
100 player_lifetime(media::Player::Lifetime::normal)100 previous_position(0),
101 player_lifetime(media::Player::Lifetime::normal),
102 is_eos(false)
101{103{
102 if (!pipeline)104 if (!pipeline)
103 throw std::runtime_error("Could not create pipeline for playbin.");105 throw std::runtime_error("Could not create pipeline for playbin.");
104106
107 is_eos = false;
108
105 // Add audio and/or video sink elements depending on environment variables109 // Add audio and/or video sink elements depending on environment variables
106 // being set or not set110 // being set or not set
107 setup_pipeline_for_audio_video();111 setup_pipeline_for_audio_video();
@@ -199,6 +203,7 @@
199 }203 }
200 break;204 break;
201 case GST_MESSAGE_EOS:205 case GST_MESSAGE_EOS:
206 is_eos = true;
202 signals.on_end_of_stream();207 signals.on_end_of_stream();
203 default:208 default:
204 break;209 break;
@@ -334,6 +339,17 @@
334 int64_t pos = 0;339 int64_t pos = 0;
335 gst_element_query_position (pipeline, GST_FORMAT_TIME, &pos);340 gst_element_query_position (pipeline, GST_FORMAT_TIME, &pos);
336341
342 // This prevents a 0 position from being reported to the app which happens while seeking.
343 // This is covering over a GStreamer issue
344 if ((static_cast<uint64_t>(pos) < duration()) && is_seeking && pos == 0)
345 {
346 return previous_position;
347 }
348
349 // Save the current position to use just in case it's needed the next time position is
350 // requested
351 previous_position = static_cast<uint64_t>(pos);
352
337 // FIXME: this should be int64_t, but dbus-cpp doesn't seem to handle it correctly353 // FIXME: this should be int64_t, but dbus-cpp doesn't seem to handle it correctly
338 return static_cast<uint64_t>(pos);354 return static_cast<uint64_t>(pos);
339}355}
@@ -358,7 +374,7 @@
358 file_type = MEDIA_FILE_TYPE_VIDEO;374 file_type = MEDIA_FILE_TYPE_VIDEO;
359 else if (is_audio_file(uri))375 else if (is_audio_file(uri))
360 file_type = MEDIA_FILE_TYPE_AUDIO;376 file_type = MEDIA_FILE_TYPE_AUDIO;
361 377
362 request_headers = headers;378 request_headers = headers;
363}379}
364380
@@ -366,7 +382,7 @@
366{382{
367 if (source == NULL || request_headers.empty())383 if (source == NULL || request_headers.empty())
368 return;384 return;
369 385
370 if (request_headers.find("Cookie") != request_headers.end()) {386 if (request_headers.find("Cookie") != request_headers.end()) {
371 if (g_object_class_find_property(G_OBJECT_GET_CLASS(source),387 if (g_object_class_find_property(G_OBJECT_GET_CLASS(source),
372 "cookies") != NULL) {388 "cookies") != NULL) {
@@ -375,7 +391,7 @@
375 g_strfreev(cookies);391 g_strfreev(cookies);
376 }392 }
377 }393 }
378 394
379 if (request_headers.find("User-Agent") != request_headers.end()) {395 if (request_headers.find("User-Agent") != request_headers.end()) {
380 if (g_object_class_find_property(G_OBJECT_GET_CLASS(source),396 if (g_object_class_find_property(G_OBJECT_GET_CLASS(source),
381 "user-agent") != NULL) {397 "user-agent") != NULL) {
@@ -427,8 +443,9 @@
427 }443 }
428444
429 // We only should query the pipeline if we actually succeeded in445 // We only should query the pipeline if we actually succeeded in
430 // setting the requested state.446 // setting the requested state. Also don't send on_video_dimensions_changed
431 if (result && new_state == GST_STATE_PLAYING)447 // signal during EOS.
448 if (result && new_state == GST_STATE_PLAYING && !is_eos)
432 {449 {
433 // Get the video height/width from the video sink450 // Get the video height/width from the video sink
434 try451 try
435452
=== modified file 'src/core/media/gstreamer/playbin.h'
--- src/core/media/gstreamer/playbin.h 2015-03-16 15:38:27 +0000
+++ src/core/media/gstreamer/playbin.h 2015-03-16 15:38:27 +0000
@@ -112,8 +112,10 @@
112 GstElement* video_sink;112 GstElement* video_sink;
113 core::Connection on_new_message_connection;113 core::Connection on_new_message_connection;
114 bool is_seeking;114 bool is_seeking;
115 mutable uint64_t previous_position;
115 core::ubuntu::media::Player::HeadersType request_headers;116 core::ubuntu::media::Player::HeadersType request_headers;
116 core::ubuntu::media::Player::Lifetime player_lifetime;117 core::ubuntu::media::Player::Lifetime player_lifetime;
118 bool is_eos;
117 struct119 struct
118 {120 {
119 core::Signal<void> about_to_finish;121 core::Signal<void> about_to_finish;
120122
=== added file 'src/core/media/hashed_keyed_player_store.cpp'
--- src/core/media/hashed_keyed_player_store.cpp 1970-01-01 00:00:00 +0000
+++ src/core/media/hashed_keyed_player_store.cpp 2015-03-16 15:38:27 +0000
@@ -0,0 +1,80 @@
1/*
2 * Copyright © 2014 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License version 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Thomas Voß <thomas.voss@canonical.com>
17 */
18
19#include <core/media/hashed_keyed_player_store.h>
20
21namespace media = core::ubuntu::media;
22
23media::HashedKeyedPlayerStore::HashedKeyedPlayerStore()
24{
25}
26
27const core::Property<std::shared_ptr<media::Player>>& media::HashedKeyedPlayerStore::current_player() const
28{
29 return prop_current_player;
30}
31
32bool media::HashedKeyedPlayerStore::has_player_for_key(const media::Player::PlayerKey& key) const
33{
34 std::lock_guard<std::mutex> lg{guard};
35 return map.count(key) > 0;
36}
37
38std::shared_ptr<media::Player> media::HashedKeyedPlayerStore::player_for_key(const media::Player::PlayerKey& key) const
39{
40 std::lock_guard<std::mutex> lg{guard};
41 auto it = map.find(key);
42
43 if (it == map.end()) throw std::out_of_range
44 {
45 "HashedKeyedPlayerStore::player_for_key: No player known for " + std::to_string(key)
46 };
47
48 return it->second;
49}
50
51void media::HashedKeyedPlayerStore::enumerate_players(const media::KeyedPlayerStore::PlayerEnumerator& enumerator) const
52{
53 std::lock_guard<std::mutex> lg{guard};
54 for (const auto& pair : map)
55 enumerator(pair.first, pair.second);
56}
57
58void media::HashedKeyedPlayerStore::add_player_for_key(const media::Player::PlayerKey& key, const std::shared_ptr<media::Player>& player)
59{
60 std::lock_guard<std::mutex> lg{guard};
61 map[key] = player;
62}
63
64void media::HashedKeyedPlayerStore::remove_player_for_key(const media::Player::PlayerKey& key)
65{
66 std::lock_guard<std::mutex> lg{guard};
67 auto it = map.find(key);
68 if (it != map.end())
69 {
70 if (prop_current_player == it->second)
71 prop_current_player = nullptr;
72
73 map.erase(it);
74 }
75}
76
77void media::HashedKeyedPlayerStore::set_current_player_for_key(const media::Player::PlayerKey& key)
78{
79 prop_current_player = player_for_key(key);
80}
081
=== added file 'src/core/media/hashed_keyed_player_store.h'
--- src/core/media/hashed_keyed_player_store.h 1970-01-01 00:00:00 +0000
+++ src/core/media/hashed_keyed_player_store.h 2015-03-16 15:38:27 +0000
@@ -0,0 +1,76 @@
1/*
2 * Copyright © 2014 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License version 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Thomas Voß <thomas.voss@canonical.com>
17 */
18
19#ifndef CORE_UBUNTU_MEDIA_HASHED_KEYED_PLAYER_STORE_H_
20#define CORE_UBUNTU_MEDIA_HASHED_KEYED_PLAYER_STORE_H_
21
22#include <core/media/keyed_player_store.h>
23
24#include <mutex>
25#include <unordered_map>
26
27namespace core
28{
29namespace ubuntu
30{
31namespace media
32{
33// Implements KeyedPlayerStore using a std::unordered_map.
34class HashedKeyedPlayerStore : public KeyedPlayerStore
35{
36public:
37 HashedKeyedPlayerStore();
38 // We keep track of the "current" player, that is, the one
39 // that has been created most recently, or has been explicitly foregrounded, or has been enabled for
40 // background playback. We provide a getable/observable access to that designated instance.
41 const core::Property<std::shared_ptr<media::Player>>& current_player() const override;
42
43 // We keep track of all known player sessions here and render them accessible via
44 // the key. All of these functions are thread-safe but not reentrant.
45 // Returns true iff a player is known for the given key.
46 bool has_player_for_key(const Player::PlayerKey& key) const override;
47
48 // Returns the player for the given key or throws std::out_of_range if no player is known
49 // for the given key.
50 // Throws std::out_of_range if no player is known for the key.
51 std::shared_ptr<Player> player_for_key(const Player::PlayerKey& key) const override;
52
53 // Enumerates all known players and invokes the given enumerator for each
54 // (key, player) pair.
55 void enumerate_players(const PlayerEnumerator& enumerator) const override;
56
57 // Adds the given player with the given key.
58 void add_player_for_key(const Player::PlayerKey& key, const std::shared_ptr<Player>& player) override;
59
60 // Removes the player for the given key, and unsets it if it is the current one.
61 void remove_player_for_key(const Player::PlayerKey& key) override;
62
63 // Makes the player known under the given key current.
64 // Throws std::out_of_range if no player is known for the key.
65 void set_current_player_for_key(const Player::PlayerKey& key) override;
66
67private:
68 core::Property<std::shared_ptr<Player>> prop_current_player;
69 mutable std::mutex guard;
70 std::unordered_map<Player::PlayerKey, std::shared_ptr<Player>> map;
71};
72}
73}
74}
75
76#endif // CORE_UBUNTU_MEDIA_KEYED_PLAYER_STORE_H_
077
=== added file 'src/core/media/keyed_player_store.cpp'
=== added file 'src/core/media/keyed_player_store.h'
--- src/core/media/keyed_player_store.h 1970-01-01 00:00:00 +0000
+++ src/core/media/keyed_player_store.h 2015-03-16 15:38:27 +0000
@@ -0,0 +1,83 @@
1/*
2 * Copyright © 2014 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License version 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Thomas Voß <thomas.voss@canonical.com>
17 */
18
19#ifndef CORE_UBUNTU_MEDIA_KEYED_PLAYER_STORE_H_
20#define CORE_UBUNTU_MEDIA_KEYED_PLAYER_STORE_H_
21
22#include <core/media/player.h>
23
24#include <core/property.h>
25
26#include <functional>
27#include <memory>
28
29namespace core
30{
31namespace ubuntu
32{
33namespace media
34{
35// An interface abstracting keyed lookups of known Player instances.
36class KeyedPlayerStore
37{
38public:
39 // Save us some typing.
40 typedef std::shared_ptr<KeyedPlayerStore> Ptr;
41 // Functor for enumerating all known (key, player) pairs.
42 typedef std::function
43 <
44 void(
45 // The key of the player.
46 const Player::PlayerKey&,
47 // The actual player instance.
48 const std::shared_ptr<Player>&
49 )
50 > PlayerEnumerator;
51 // We keep track of the "current" player, that is, the one
52 // that has been created most recently and provide a getable/observable
53 // access to that designated instance.
54 virtual const core::Property<std::shared_ptr<Player>>& current_player() const = 0;
55
56 // We keep track of all known player sessions here and render them accessible via
57 // the key. All of these functions are thread-safe but not reentrant.
58 // Returns true iff a player is known for the given key.
59 virtual bool has_player_for_key(const Player::PlayerKey& key) const = 0;
60 // Returns the player for the given key or throws std::out_of_range if no player is known
61 // for the given key.
62 virtual std::shared_ptr<Player> player_for_key(const Player::PlayerKey& key) const = 0;
63 // Enumerates all known players and invokes the given enumerator for each
64 // (key, player) pair.
65 virtual void enumerate_players(const PlayerEnumerator& enumerator) const = 0;
66 // Adds the given player with the given key.
67 virtual void add_player_for_key(const Player::PlayerKey& key, const std::shared_ptr<Player>& player) = 0;
68 // Removes the player for the given key, and unsets it if it is the current one.
69 virtual void remove_player_for_key(const Player::PlayerKey& key) = 0;
70 // Makes the player known under the given key current.
71 virtual void set_current_player_for_key(const Player::PlayerKey& key) = 0;
72
73protected:
74 KeyedPlayerStore() = default;
75 KeyedPlayerStore(const KeyedPlayerStore&) = delete;
76 virtual ~KeyedPlayerStore() = default;
77 KeyedPlayerStore& operator=(const KeyedPlayerStore&) = delete;
78};
79}
80}
81}
82
83#endif // CORE_UBUNTU_MEDIA_KEYED_PLAYER_STORE_H_
084
=== modified file 'src/core/media/mpris/player.h'
--- src/core/media/mpris/player.h 2015-03-16 15:38:27 +0000
+++ src/core/media/mpris/player.h 2015-03-16 15:38:27 +0000
@@ -136,6 +136,7 @@
136 struct Signals136 struct Signals
137 {137 {
138 DBUS_CPP_SIGNAL_DEF(Seeked, Player, std::int64_t)138 DBUS_CPP_SIGNAL_DEF(Seeked, Player, std::int64_t)
139 DBUS_CPP_SIGNAL_DEF(AboutToFinish, Player, void)
139 DBUS_CPP_SIGNAL_DEF(EndOfStream, Player, void)140 DBUS_CPP_SIGNAL_DEF(EndOfStream, Player, void)
140 DBUS_CPP_SIGNAL_DEF(PlaybackStatusChanged, Player, core::ubuntu::media::Player::PlaybackStatus)141 DBUS_CPP_SIGNAL_DEF(PlaybackStatusChanged, Player, core::ubuntu::media::Player::PlaybackStatus)
141 DBUS_CPP_SIGNAL_DEF(VideoDimensionChanged, Player, core::ubuntu::media::video::Dimensions)142 DBUS_CPP_SIGNAL_DEF(VideoDimensionChanged, Player, core::ubuntu::media::video::Dimensions)
@@ -246,6 +247,7 @@
246 signals247 signals
247 {248 {
248 configuration.object->template get_signal<Signals::Seeked>(),249 configuration.object->template get_signal<Signals::Seeked>(),
250 configuration.object->template get_signal<Signals::AboutToFinish>(),
249 configuration.object->template get_signal<Signals::EndOfStream>(),251 configuration.object->template get_signal<Signals::EndOfStream>(),
250 configuration.object->template get_signal<Signals::PlaybackStatusChanged>(),252 configuration.object->template get_signal<Signals::PlaybackStatusChanged>(),
251 configuration.object->template get_signal<Signals::VideoDimensionChanged>(),253 configuration.object->template get_signal<Signals::VideoDimensionChanged>(),
@@ -373,6 +375,7 @@
373 struct375 struct
374 {376 {
375 typename core::dbus::Signal<Signals::Seeked, Signals::Seeked::ArgumentType>::Ptr seeked_to;377 typename core::dbus::Signal<Signals::Seeked, Signals::Seeked::ArgumentType>::Ptr seeked_to;
378 typename core::dbus::Signal<Signals::AboutToFinish, Signals::AboutToFinish::ArgumentType>::Ptr about_to_finish;
376 typename core::dbus::Signal<Signals::EndOfStream, Signals::EndOfStream::ArgumentType>::Ptr end_of_stream;379 typename core::dbus::Signal<Signals::EndOfStream, Signals::EndOfStream::ArgumentType>::Ptr end_of_stream;
377 typename core::dbus::Signal<Signals::PlaybackStatusChanged, Signals::PlaybackStatusChanged::ArgumentType>::Ptr playback_status_changed;380 typename core::dbus::Signal<Signals::PlaybackStatusChanged, Signals::PlaybackStatusChanged::ArgumentType>::Ptr playback_status_changed;
378 typename core::dbus::Signal<Signals::VideoDimensionChanged, Signals::VideoDimensionChanged::ArgumentType>::Ptr video_dimension_changed;381 typename core::dbus::Signal<Signals::VideoDimensionChanged, Signals::VideoDimensionChanged::ArgumentType>::Ptr video_dimension_changed;
379382
=== added file 'src/core/media/null_track_list.h'
--- src/core/media/null_track_list.h 1970-01-01 00:00:00 +0000
+++ src/core/media/null_track_list.h 2015-03-16 15:38:27 +0000
@@ -0,0 +1,114 @@
1/*
2 *
3 * This program is free software: you can redistribute it and/or modify it
4 * under the terms of the GNU Lesser General Public License version 3,
5 * as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU Lesser General Public License for more details.
11 *
12 * You should have received a copy of the GNU Lesser General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 *
15 * Authored by: Thomas Voß <thomas.voss@canonical.com>
16 */
17
18#ifndef CORE_MEDIA_NULL_TRACK_LIST_H_
19#define CORE_MEDIA_NULL_TRACK_LIST_H_
20
21#include <core/media/track.h>
22#include <core/media/track_list.h>
23
24namespace core
25{
26namespace ubuntu
27{
28namespace media
29{
30// A helper type to replace the playlist implementation below.
31// Please note that this type is only a temporary manner. Ideally,
32// the actual implementation should be injected as a dependency from the
33// outside.
34struct NullTrackList : public media::TrackList
35{
36 NullTrackList() = default;
37
38 bool has_next()
39 {
40 return false;
41 }
42
43 media::Track::Id next()
44 {
45 return media::Track::Id{};
46 }
47
48 media::Track::UriType query_uri_for_track(const media::Track::Id&)
49 {
50 return media::Track::UriType{};
51 }
52
53 const core::Property<bool>& can_edit_tracks() const override
54 {
55 return props_and_sigs.can_edit_tracks;
56 }
57
58 const core::Property<Container>& tracks() const override
59 {
60 return props_and_sigs.tracks;
61 }
62
63 virtual media::Track::MetaData query_meta_data_for_track(const media::Track::Id&) override
64 {
65 return media::Track::MetaData{};
66 }
67
68 void add_track_with_uri_at(const media::Track::UriType&, const media::Track::Id&, bool) override
69 {
70 }
71
72 void remove_track(const media::Track::Id&) override
73 {
74 }
75
76 void go_to(const media::Track::Id&) override
77 {
78 }
79
80 const core::Signal<void>& on_track_list_replaced() const override
81 {
82 return props_and_sigs.on_track_list_replaced;
83 }
84
85 const core::Signal<media::Track::Id>& on_track_added() const override
86 {
87 return props_and_sigs.on_track_added;
88 }
89
90 const core::Signal<media::Track::Id>& on_track_removed() const override
91 {
92 return props_and_sigs.on_track_removed;
93 }
94
95 const core::Signal<media::Track::Id>& on_track_changed() const override
96 {
97 return props_and_sigs.on_track_changed;
98 }
99
100 struct
101 {
102 core::Property<bool> can_edit_tracks;
103 core::Property<TrackList::Container> tracks;
104 core::Signal<void> on_track_list_replaced;
105 core::Signal<media::Track::Id> on_track_added;
106 core::Signal<media::Track::Id> on_track_removed;
107 core::Signal<media::Track::Id> on_track_changed;
108 } props_and_sigs;
109};
110}
111}
112}
113
114#endif // CORE_MEDIA_NULL_TRACK_LIST_H_
0115
=== modified file 'src/core/media/player.cpp'
--- src/core/media/player.cpp 2015-03-16 15:38:27 +0000
+++ src/core/media/player.cpp 2015-03-16 15:38:27 +0000
@@ -31,7 +31,6 @@
31{31{
32 static const media::Player::Configuration config32 static const media::Player::Configuration config
33 {33 {
34 std::string{""},
35 0,34 0,
36 nullptr,35 nullptr,
37 nullptr36 nullptr
3837
=== modified file 'src/core/media/player_configuration.h'
--- src/core/media/player_configuration.h 2014-09-09 10:28:32 +0000
+++ src/core/media/player_configuration.h 2015-03-16 15:38:27 +0000
@@ -27,9 +27,6 @@
27// to the implementation in a way that is opaque to the client.27// to the implementation in a way that is opaque to the client.
28struct core::ubuntu::media::Player::Configuration28struct core::ubuntu::media::Player::Configuration
29{29{
30 // An identifier that is helpful in referencing the player instance
31 // across multiple services.
32 std::string identity;
33 // Unique key for identifying the session.30 // Unique key for identifying the session.
34 core::ubuntu::media::Player::PlayerKey key;31 core::ubuntu::media::Player::PlayerKey key;
35 // The bus connection to expose objects on.32 // The bus connection to expose objects on.
3633
=== modified file 'src/core/media/player_implementation.cpp'
--- src/core/media/player_implementation.cpp 2015-03-16 15:38:27 +0000
+++ src/core/media/player_implementation.cpp 2015-03-16 15:38:27 +0000
@@ -23,6 +23,7 @@
2323
24#include "client_death_observer.h"24#include "client_death_observer.h"
25#include "engine.h"25#include "engine.h"
26#include "null_track_list.h"
26#include "track_list_implementation.h"27#include "track_list_implementation.h"
2728
28#include "gstreamer/engine.h"29#include "gstreamer/engine.h"
@@ -39,7 +40,8 @@
3940
40using namespace std;41using namespace std;
4142
42struct media::PlayerImplementation::Private :43template<typename Parent>
44struct media::PlayerImplementation<Parent>::Private :
43 public std::enable_shared_from_this<Private>45 public std::enable_shared_from_this<Private>
44{46{
45 enum class wakelock_clear_t47 enum class wakelock_clear_t
@@ -50,30 +52,18 @@
50 WAKELOCK_CLEAR_INVALID52 WAKELOCK_CLEAR_INVALID
51 };53 };
5254
53 Private(PlayerImplementation* parent, const media::PlayerImplementation::Configuration& config)55 Private(PlayerImplementation* parent, const media::PlayerImplementation<Parent>::Configuration& config)
54 : parent(parent),56 : parent(parent),
55 config(config),57 config(config),
56 display_state_lock(config.power_state_controller->display_state_lock()),58 display_state_lock(config.power_state_controller->display_state_lock()),
57 system_state_lock(config.power_state_controller->system_state_lock()),59 system_state_lock(config.power_state_controller->system_state_lock()),
58 engine(std::make_shared<gstreamer::Engine>()),60 engine(std::make_shared<gstreamer::Engine>()),
59 track_list(61 track_list(std::make_shared<NullTrackList>()),
60 new media::TrackListImplementation(
61 config.session->path().as_string() + "/TrackList",
62 engine->meta_data_extractor())),
63 system_wakelock_count(0),62 system_wakelock_count(0),
64 display_wakelock_count(0),63 display_wakelock_count(0),
65 previous_state(Engine::State::stopped),64 previous_state(Engine::State::stopped),
66 engine_state_change_connection(engine->state().changed().connect(make_state_change_handler()))65 engine_state_change_connection(engine->state().changed().connect(make_state_change_handler()))
67 {66 {
68 config.client_death_observer->register_for_death_notifications_with_key(config.key);
69 config.client_death_observer->on_client_with_key_died().connect([this](const media::Player::PlayerKey& died)
70 {
71 if (died != this->config.key)
72 return;
73
74 on_client_died();
75 });
76
77 // Poor man's logging of release/acquire events.67 // Poor man's logging of release/acquire events.
78 display_state_lock->acquired().connect([](media::power::DisplayState state)68 display_state_lock->acquired().connect([](media::power::DisplayState state)
79 {69 {
@@ -207,7 +197,7 @@
207 if (--system_wakelock_count == 0)197 if (--system_wakelock_count == 0)
208 {198 {
209 std::cout << "Clearing system wakelock." << std::endl;199 std::cout << "Clearing system wakelock." << std::endl;
210 system_state_lock->request_release(media::power::SystemState::active); 200 system_state_lock->request_release(media::power::SystemState::active);
211 }201 }
212 break;202 break;
213 case wakelock_clear_t::WAKELOCK_CLEAR_DISPLAY:203 case wakelock_clear_t::WAKELOCK_CLEAR_DISPLAY:
@@ -257,7 +247,7 @@
257 // the execution of the functor may surpass the lifetime of this Private247 // the execution of the functor may surpass the lifetime of this Private
258 // object instance. By keeping a weak_ptr to the private object instance248 // object instance. By keeping a weak_ptr to the private object instance
259 // we can check if the object is dead before calling methods on it249 // we can check if the object is dead before calling methods on it
260 std::weak_ptr<Private> weak_self{shared_from_this()};250 std::weak_ptr<Private> weak_self{this->shared_from_this()};
261 auto wakelock_type = current_wakelock_type();251 auto wakelock_type = current_wakelock_type();
262 return [weak_self, wakelock_type] {252 return [weak_self, wakelock_type] {
263 if (auto self = weak_self.lock())253 if (auto self = weak_self.lock())
@@ -271,14 +261,14 @@
271 }261 }
272262
273 // Our link back to our parent.263 // Our link back to our parent.
274 media::PlayerImplementation* parent;264 media::PlayerImplementation<Parent>* parent;
275 // We just store the parameters passed on construction.265 // We just store the parameters passed on construction.
276 media::PlayerImplementation::Configuration config;266 media::PlayerImplementation<Parent>::Configuration config;
277 media::power::StateController::Lock<media::power::DisplayState>::Ptr display_state_lock;267 media::power::StateController::Lock<media::power::DisplayState>::Ptr display_state_lock;
278 media::power::StateController::Lock<media::power::SystemState>::Ptr system_state_lock;268 media::power::StateController::Lock<media::power::SystemState>::Ptr system_state_lock;
279269
280 std::shared_ptr<Engine> engine;270 std::shared_ptr<Engine> engine;
281 std::shared_ptr<TrackListImplementation> track_list;271 std::shared_ptr<media::NullTrackList> track_list;
282 std::atomic<int> system_wakelock_count;272 std::atomic<int> system_wakelock_count;
283 std::atomic<int> display_wakelock_count;273 std::atomic<int> display_wakelock_count;
284 Engine::State previous_state;274 Engine::State previous_state;
@@ -286,36 +276,29 @@
286 core::Connection engine_state_change_connection;276 core::Connection engine_state_change_connection;
287};277};
288278
289media::PlayerImplementation::PlayerImplementation(const media::PlayerImplementation::Configuration& config)279template<typename Parent>
290 : media::PlayerSkeleton280media::PlayerImplementation<Parent>::PlayerImplementation(const media::PlayerImplementation<Parent>::Configuration& config)
291 {281 : Parent{config.parent},
292 media::PlayerSkeleton::Configuration
293 {
294 config.bus,
295 config.session,
296 config.identity
297 }
298 },
299 d{std::make_shared<Private>(this, config)}282 d{std::make_shared<Private>(this, config)}
300{283{
301 // Initialize default values for Player interface properties284 // Initialize default values for Player interface properties
302 can_play().set(true);285 Parent::can_play().set(true);
303 can_pause().set(true);286 Parent::can_pause().set(true);
304 can_seek().set(true);287 Parent::can_seek().set(true);
305 can_go_previous().set(true);288 Parent::can_go_previous().set(true);
306 can_go_next().set(true);289 Parent::can_go_next().set(true);
307 is_video_source().set(false);290 Parent::is_video_source().set(false);
308 is_audio_source().set(false);291 Parent::is_audio_source().set(false);
309 is_shuffle().set(true);292 Parent::is_shuffle().set(true);
310 playback_rate().set(1.f);293 Parent::playback_rate().set(1.f);
311 playback_status().set(Player::PlaybackStatus::null);294 Parent::playback_status().set(Player::PlaybackStatus::null);
312 loop_status().set(Player::LoopStatus::none);295 Parent::loop_status().set(Player::LoopStatus::none);
313 position().set(0);296 Parent::position().set(0);
314 duration().set(0);297 Parent::duration().set(0);
315 audio_stream_role().set(Player::AudioStreamRole::multimedia);298 Parent::audio_stream_role().set(Player::AudioStreamRole::multimedia);
316 d->engine->audio_stream_role().set(Player::AudioStreamRole::multimedia);299 d->engine->audio_stream_role().set(Player::AudioStreamRole::multimedia);
317 orientation().set(Player::Orientation::rotate0);300 Parent::orientation().set(Player::Orientation::rotate0);
318 lifetime().set(Player::Lifetime::normal);301 Parent::lifetime().set(Player::Lifetime::normal);
319 d->engine->lifetime().set(Player::Lifetime::normal);302 d->engine->lifetime().set(Player::Lifetime::normal);
320303
321 // Make sure that the Position property gets updated from the Engine304 // Make sure that the Position property gets updated from the Engine
@@ -324,7 +307,7 @@
324 {307 {
325 return d->engine->position().get();308 return d->engine->position().get();
326 };309 };
327 position().install(position_getter);310 Parent::position().install(position_getter);
328311
329 // Make sure that the Duration property gets updated from the Engine312 // Make sure that the Duration property gets updated from the Engine
330 // every time the client requests duration313 // every time the client requests duration
@@ -332,23 +315,23 @@
332 {315 {
333 return d->engine->duration().get();316 return d->engine->duration().get();
334 };317 };
335 duration().install(duration_getter);318 Parent::duration().install(duration_getter);
336319
337 std::function<bool()> video_type_getter = [this]()320 std::function<bool()> video_type_getter = [this]()
338 {321 {
339 return d->engine->is_video_source().get();322 return d->engine->is_video_source().get();
340 };323 };
341 is_video_source().install(video_type_getter);324 Parent::is_video_source().install(video_type_getter);
342325
343 std::function<bool()> audio_type_getter = [this]()326 std::function<bool()> audio_type_getter = [this]()
344 {327 {
345 return d->engine->is_audio_source().get();328 return d->engine->is_audio_source().get();
346 };329 };
347 is_audio_source().install(audio_type_getter);330 Parent::is_audio_source().install(audio_type_getter);
348331
349 // Make sure that the audio_stream_role property gets updated on the Engine side332 // Make sure that the audio_stream_role property gets updated on the Engine side
350 // whenever the client side sets the role333 // whenever the client side sets the role
351 audio_stream_role().changed().connect([this](media::Player::AudioStreamRole new_role)334 Parent::audio_stream_role().changed().connect([this](media::Player::AudioStreamRole new_role)
352 {335 {
353 d->engine->audio_stream_role().set(new_role);336 d->engine->audio_stream_role().set(new_role);
354 });337 });
@@ -357,16 +340,18 @@
357 // update the Player's cached value340 // update the Player's cached value
358 d->engine->orientation().changed().connect([this](const Player::Orientation& o)341 d->engine->orientation().changed().connect([this](const Player::Orientation& o)
359 {342 {
360 orientation().set(o);343 Parent::orientation().set(o);
361 });344 });
362345
363 lifetime().changed().connect([this](media::Player::Lifetime lifetime)346 Parent::lifetime().changed().connect([this](media::Player::Lifetime lifetime)
364 {347 {
365 d->engine->lifetime().set(lifetime);348 d->engine->lifetime().set(lifetime);
366 });349 });
367350
368 d->engine->about_to_finish_signal().connect([this]()351 d->engine->about_to_finish_signal().connect([this]()
369 {352 {
353 Parent::about_to_finish()();
354
370 if (d->track_list->has_next())355 if (d->track_list->has_next())
371 {356 {
372 Track::UriType uri = d->track_list->query_uri_for_track(d->track_list->next());357 Track::UriType uri = d->track_list->query_uri_for_track(d->track_list->next());
@@ -386,31 +371,52 @@
386371
387 d->engine->seeked_to_signal().connect([this](uint64_t value)372 d->engine->seeked_to_signal().connect([this](uint64_t value)
388 {373 {
389 seeked_to()(value);374 Parent::seeked_to()(value);
390 });375 });
391376
392 d->engine->end_of_stream_signal().connect([this]()377 d->engine->end_of_stream_signal().connect([this]()
393 {378 {
394 end_of_stream()();379 Parent::end_of_stream()();
395 });380 });
396381
397 d->engine->playback_status_changed_signal().connect([this](const Player::PlaybackStatus& status)382 d->engine->playback_status_changed_signal().connect([this](const Player::PlaybackStatus& status)
398 {383 {
399 playback_status_changed()(status);384 Parent::playback_status_changed()(status);
400 });385 });
401386
402 d->engine->video_dimension_changed_signal().connect([this](const media::video::Dimensions& dimensions)387 d->engine->video_dimension_changed_signal().connect([this](const media::video::Dimensions& dimensions)
403 {388 {
404 video_dimension_changed()(dimensions);389 Parent::video_dimension_changed()(dimensions);
405 });390 });
406391
407 d->engine->error_signal().connect([this](const Player::Error& e)392 d->engine->error_signal().connect([this](const Player::Error& e)
408 {393 {
409 error()(e);394 Parent::error()(e);
395 });
396
397 // Everything is setup, we now subscribe to death notifications.
398 std::weak_ptr<Private> wp{d};
399
400 d->config.client_death_observer->register_for_death_notifications_with_key(config.key);
401 d->config.client_death_observer->on_client_with_key_died().connect([wp](const media::Player::PlayerKey& died)
402 {
403 if (auto sp = wp.lock())
404 {
405 if (died != sp->config.key)
406 return;
407
408 static const std::chrono::milliseconds timeout{1000};
409 media::timeout(timeout.count(), true, [wp]()
410 {
411 if (auto sp = wp.lock())
412 sp->on_client_died();
413 });
414 }
410 });415 });
411}416}
412417
413media::PlayerImplementation::~PlayerImplementation()418template<typename Parent>
419media::PlayerImplementation<Parent>::~PlayerImplementation()
414{420{
415 // Install null getters as these properties may be destroyed421 // Install null getters as these properties may be destroyed
416 // after the engine has been destroyed since they are owned by the422 // after the engine has been destroyed since they are owned by the
@@ -419,84 +425,101 @@
419 {425 {
420 return static_cast<uint64_t>(0);426 return static_cast<uint64_t>(0);
421 };427 };
422 position().install(position_getter);428 Parent::position().install(position_getter);
423429
424 std::function<uint64_t()> duration_getter = [this]()430 std::function<uint64_t()> duration_getter = [this]()
425 {431 {
426 return static_cast<uint64_t>(0);432 return static_cast<uint64_t>(0);
427 };433 };
428 duration().install(duration_getter);434 Parent::duration().install(duration_getter);
429435
430 std::function<bool()> video_type_getter = [this]()436 std::function<bool()> video_type_getter = [this]()
431 {437 {
432 return false;438 return false;
433 };439 };
434 is_video_source().install(video_type_getter);440 Parent::is_video_source().install(video_type_getter);
435441
436 std::function<bool()> audio_type_getter = [this]()442 std::function<bool()> audio_type_getter = [this]()
437 {443 {
438 return false;444 return false;
439 };445 };
440 is_audio_source().install(audio_type_getter);446 Parent::is_audio_source().install(audio_type_getter);
441}447}
442448
443std::shared_ptr<media::TrackList> media::PlayerImplementation::track_list()449template<typename Parent>
450std::shared_ptr<media::TrackList> media::PlayerImplementation<Parent>::track_list()
444{451{
445 return d->track_list;452 return d->track_list;
446}453}
447454
448// TODO: Convert this to be a property instead of sync call455// TODO: Convert this to be a property instead of sync call
449media::Player::PlayerKey media::PlayerImplementation::key() const456template<typename Parent>
457media::Player::PlayerKey media::PlayerImplementation<Parent>::key() const
450{458{
451 return d->config.key;459 return d->config.key;
452}460}
453461
454media::video::Sink::Ptr media::PlayerImplementation::create_gl_texture_video_sink(std::uint32_t texture_id)462template<typename Parent>
463media::video::Sink::Ptr media::PlayerImplementation<Parent>::create_gl_texture_video_sink(std::uint32_t texture_id)
455{464{
456 d->engine->create_video_sink(texture_id);465 d->engine->create_video_sink(texture_id);
457 return media::video::Sink::Ptr{};466 return media::video::Sink::Ptr{};
458}467}
459468
460bool media::PlayerImplementation::open_uri(const Track::UriType& uri)469template<typename Parent>
470bool media::PlayerImplementation<Parent>::open_uri(const Track::UriType& uri)
461{471{
462 return d->engine->open_resource_for_uri(uri);472 return d->engine->open_resource_for_uri(uri);
463}473}
464474
465bool media::PlayerImplementation::open_uri(const Track::UriType& uri, const Player::HeadersType& headers)475template<typename Parent>
476bool media::PlayerImplementation<Parent>::open_uri(const Track::UriType& uri, const Player::HeadersType& headers)
466{477{
467 return d->engine->open_resource_for_uri(uri, headers);478 return d->engine->open_resource_for_uri(uri, headers);
468}479}
469480
470void media::PlayerImplementation::next()481template<typename Parent>
471{482void media::PlayerImplementation<Parent>::next()
472}483{
473484}
474void media::PlayerImplementation::previous()485
475{486template<typename Parent>
476}487void media::PlayerImplementation<Parent>::previous()
477488{
478void media::PlayerImplementation::play()489}
490
491template<typename Parent>
492void media::PlayerImplementation<Parent>::play()
479{493{
480 d->engine->play();494 d->engine->play();
481}495}
482496
483void media::PlayerImplementation::pause()497template<typename Parent>
498void media::PlayerImplementation<Parent>::pause()
484{499{
485 d->engine->pause();500 d->engine->pause();
486}501}
487502
488void media::PlayerImplementation::stop()503template<typename Parent>
504void media::PlayerImplementation<Parent>::stop()
489{505{
490 std::cout << __PRETTY_FUNCTION__ << std::endl;506 std::cout << __PRETTY_FUNCTION__ << std::endl;
491 d->engine->stop();507 d->engine->stop();
492}508}
493509
494void media::PlayerImplementation::seek_to(const std::chrono::microseconds& ms)510template<typename Parent>
511void media::PlayerImplementation<Parent>::seek_to(const std::chrono::microseconds& ms)
495{512{
496 d->engine->seek_to(ms);513 d->engine->seek_to(ms);
497}514}
498515
499const core::Signal<>& media::PlayerImplementation::on_client_disconnected() const516template<typename Parent>
517const core::Signal<>& media::PlayerImplementation<Parent>::on_client_disconnected() const
500{518{
501 return d->on_client_disconnected;519 return d->on_client_disconnected;
502}520}
521
522#include <core/media/player_skeleton.h>
523
524// For linking purposes, we have to make sure that we have all symbols included within the dso.
525template class media::PlayerImplementation<media::PlayerSkeleton>;
503526
=== modified file 'src/core/media/player_implementation.h'
--- src/core/media/player_implementation.h 2015-03-16 15:38:27 +0000
+++ src/core/media/player_implementation.h 2015-03-16 15:38:27 +0000
@@ -19,8 +19,9 @@
19#ifndef CORE_UBUNTU_MEDIA_PLAYER_IMPLEMENTATION_H_19#ifndef CORE_UBUNTU_MEDIA_PLAYER_IMPLEMENTATION_H_
20#define CORE_UBUNTU_MEDIA_PLAYER_IMPLEMENTATION_H_20#define CORE_UBUNTU_MEDIA_PLAYER_IMPLEMENTATION_H_
2121
22#include "player_skeleton.h"22#include <core/media/player.h>
2323
24#include "apparmor/ubuntu.h"
24#include "client_death_observer.h"25#include "client_death_observer.h"
25#include "power/state_controller.h"26#include "power/state_controller.h"
2627
@@ -35,18 +36,17 @@
35class Engine;36class Engine;
36class Service;37class Service;
3738
38class PlayerImplementation : public PlayerSkeleton39template<typename Parent>
40class PlayerImplementation : public Parent
39{41{
40public:42public:
41 // All creation time arguments go here43 // All creation time arguments go here
42 struct Configuration44 struct Configuration
43 {45 {
44 std::string identity;46 // All creation time configuration options of the Parent class.
45 std::shared_ptr<core::dbus::Bus> bus;47 typename Parent::Configuration parent;
46 std::shared_ptr<core::dbus::Object> session;48 // The unique key identifying the player instance.
47 std::shared_ptr<Service> service;49 Player::PlayerKey key;
48 PlayerKey key;
49
50 // Functional dependencies50 // Functional dependencies
51 ClientDeathObserver::Ptr client_death_observer;51 ClientDeathObserver::Ptr client_death_observer;
52 power::StateController::Ptr power_state_controller;52 power::StateController::Ptr power_state_controller;
@@ -56,7 +56,7 @@
56 ~PlayerImplementation();56 ~PlayerImplementation();
5757
58 virtual std::shared_ptr<TrackList> track_list();58 virtual std::shared_ptr<TrackList> track_list();
59 virtual PlayerKey key() const;59 virtual Player::PlayerKey key() const;
6060
61 virtual video::Sink::Ptr create_gl_texture_video_sink(std::uint32_t texture_id);61 virtual video::Sink::Ptr create_gl_texture_video_sink(std::uint32_t texture_id);
6262
6363
=== modified file 'src/core/media/player_skeleton.cpp'
--- src/core/media/player_skeleton.cpp 2015-03-16 15:38:27 +0000
+++ src/core/media/player_skeleton.cpp 2015-03-16 15:38:27 +0000
@@ -17,15 +17,16 @@
17 * Jim Hodapp <jim.hodapp@canonical.com>17 * Jim Hodapp <jim.hodapp@canonical.com>
18 */18 */
1919
20#include "apparmor.h"
21#include "codec.h"20#include "codec.h"
22#include "engine.h"21#include "engine.h"
22#include "external_services.h"
23#include "player_skeleton.h"23#include "player_skeleton.h"
24#include "player_traits.h"24#include "player_traits.h"
25#include "property_stub.h"25#include "property_stub.h"
26#include "the_session_bus.h"26#include "the_session_bus.h"
27#include "xesam.h"27#include "xesam.h"
2828
29#include "apparmor/ubuntu.h"
29#include "mpris/media_player2.h"30#include "mpris/media_player2.h"
30#include "mpris/metadata.h"31#include "mpris/metadata.h"
31#include "mpris/player.h"32#include "mpris/player.h"
@@ -44,19 +45,20 @@
44struct media::PlayerSkeleton::Private45struct media::PlayerSkeleton::Private
45{46{
46 Private(media::PlayerSkeleton* player,47 Private(media::PlayerSkeleton* player,
47 const std::string& identity,
48 const std::shared_ptr<core::dbus::Bus>& bus,48 const std::shared_ptr<core::dbus::Bus>& bus,
49 const std::shared_ptr<core::dbus::Object>& session)49 const std::shared_ptr<core::dbus::Object>& session,
50 const apparmor::ubuntu::RequestContextResolver::Ptr& request_context_resolver,
51 const apparmor::ubuntu::RequestAuthenticator::Ptr& request_authenticator)
50 : impl(player),52 : impl(player),
51 identity(identity),
52 bus(bus),53 bus(bus),
53 object(session),54 object(session),
54 apparmor_session(nullptr),55 request_context_resolver{request_context_resolver},
55 dbus_stub{bus},56 request_authenticator{request_authenticator},
56 skeleton{mpris::Player::Skeleton::Configuration{bus, session, mpris::Player::Skeleton::Configuration::Defaults{}}},57 skeleton{mpris::Player::Skeleton::Configuration{bus, session, mpris::Player::Skeleton::Configuration::Defaults{}}},
57 signals58 signals
58 {59 {
59 skeleton.signals.seeked_to,60 skeleton.signals.seeked_to,
61 skeleton.signals.about_to_finish,
60 skeleton.signals.end_of_stream,62 skeleton.signals.end_of_stream,
61 skeleton.signals.playback_status_changed,63 skeleton.signals.playback_status_changed,
62 skeleton.signals.video_dimension_changed,64 skeleton.signals.video_dimension_changed,
@@ -163,82 +165,6 @@
163 bus->send(reply);165 bus->send(reply);
164 }166 }
165167
166 bool does_client_have_access(const std::string& context, const std::string& uri)
167 {
168 if (context.empty() || uri.empty())
169 {
170 std::cout << "Client denied access since context or uri are empty" << std::endl;
171 return false;
172 }
173
174 if (context == "unconfined")
175 {
176 std::cout << "Client allowed access since it's unconfined" << std::endl;
177 return true;
178 }
179
180 size_t pos = context.find_first_of('_');
181 if (pos == std::string::npos)
182 {
183 std::cout << "Client denied access since it's an invalid apparmor security context" << std::endl;
184 return false;
185 }
186
187 const std::string pkgname = context.substr(0, pos);
188 std::cout << "client pkgname: " << pkgname << std::endl;
189 std::cout << "uri: " << uri << std::endl;
190
191 // All confined apps can access their own files
192 if (uri.find(std::string(".local/share/" + pkgname + "/")) != std::string::npos
193 || uri.find(std::string(".cache/" + pkgname + "/")) != std::string::npos)
194 {
195 std::cout << "Client can access content in ~/.local/share/" << pkgname << " or ~/.cache/" << pkgname << std::endl;
196 return true;
197 }
198 else if (uri.find(std::string("opt/click.ubuntu.com/")) != std::string::npos
199 && uri.find(pkgname) != std::string::npos)
200 {
201 std::cout << "Client can access content in own opt directory" << std::endl;
202 return true;
203 }
204 else if ((uri.find(std::string("/system/media/audio/ui/")) != std::string::npos
205 || uri.find(std::string("/android/system/media/audio/ui/")) != std::string::npos)
206 && pkgname == "com.ubuntu.camera")
207 {
208 std::cout << "Camera app can access ui sounds" << std::endl;
209 return true;
210 }
211 // TODO: Check if the trust store previously allowed direct access to uri
212
213 // Check in ~/Music and ~/Videos
214 // TODO: when the trust store lands, check it to see if this app can access the dirs and
215 // then remove the explicit whitelist of the music-app, and gallery-app
216 else if ((pkgname == "com.ubuntu.music" || pkgname == "com.ubuntu.gallery") &&
217 (uri.find(std::string("Music/")) != std::string::npos
218 || uri.find(std::string("Videos/")) != std::string::npos
219 || uri.find(std::string("/media")) != std::string::npos))
220 {
221 std::cout << "Client can access content in ~/Music or ~/Videos" << std::endl;
222 return true;
223 }
224 else if (uri.find(std::string("/usr/share/sounds")) != std::string::npos)
225 {
226 std::cout << "Client can access content in /usr/share/sounds" << std::endl;
227 return true;
228 }
229 else if (uri.find(std::string("http://")) != std::string::npos
230 || uri.find(std::string("rtsp://")) != std::string::npos)
231 {
232 std::cout << "Client can access streaming content" << std::endl;
233 return true;
234 }
235 else
236 {
237 std::cout << "Client denied access to open_uri()" << std::endl;
238 return false;
239 }
240 }
241
242 void handle_key(const core::dbus::Message::Ptr& in)168 void handle_key(const core::dbus::Message::Ptr& in)
243 {169 {
244 auto reply = dbus::Message::make_method_return(in);170 auto reply = dbus::Message::make_method_return(in);
@@ -248,15 +174,15 @@
248174
249 void handle_open_uri(const core::dbus::Message::Ptr& in)175 void handle_open_uri(const core::dbus::Message::Ptr& in)
250 {176 {
251 dbus_stub.get_connection_app_armor_security_async(in->sender(), [this, in](const std::string& profile)177 request_context_resolver->resolve_context_for_dbus_name_async(in->sender(), [this, in](const media::apparmor::ubuntu::Context& context)
252 {178 {
253 Track::UriType uri;179 Track::UriType uri;
254 in->reader() >> uri;180 in->reader() >> uri;
255181
256 bool have_access = does_client_have_access(profile, uri);182 auto result = request_authenticator->authenticate_open_uri_request(context, uri);
257183
258 auto reply = dbus::Message::make_method_return(in);184 auto reply = dbus::Message::make_method_return(in);
259 reply->writer() << (have_access ? impl->open_uri(uri) : false);185 reply->writer() << (std::get<0>(result) ? impl->open_uri(uri) : false);
260186
261 bus->send(reply);187 bus->send(reply);
262 });188 });
@@ -264,16 +190,16 @@
264190
265 void handle_open_uri_extended(const core::dbus::Message::Ptr& in)191 void handle_open_uri_extended(const core::dbus::Message::Ptr& in)
266 {192 {
267 dbus_stub.get_connection_app_armor_security_async(in->sender(), [this, in](const std::string& profile)193 request_context_resolver->resolve_context_for_dbus_name_async(in->sender(), [this, in](const media::apparmor::ubuntu::Context& context)
268 {194 {
269 Track::UriType uri;195 Track::UriType uri;
270 Player::HeadersType headers;196 Player::HeadersType headers;
271197
272 in->reader() >> uri >> headers;198 in->reader() >> uri >> headers;
273199
274 bool have_access = does_client_have_access(profile, uri);200 auto result = request_authenticator->authenticate_open_uri_request(context, uri);
275 auto reply = dbus::Message::make_method_return(in);201 auto reply = dbus::Message::make_method_return(in);
276 reply->writer() << (have_access ? impl->open_uri(uri, headers) : false);202 reply->writer() << (std::get<0>(result) ? impl->open_uri(uri, headers) : false);
277203
278 bus->send(reply);204 bus->send(reply);
279 });205 });
@@ -301,12 +227,10 @@
301 }227 }
302228
303 media::PlayerSkeleton* impl;229 media::PlayerSkeleton* impl;
304 std::string identity;
305 dbus::Bus::Ptr bus;230 dbus::Bus::Ptr bus;
306 dbus::Object::Ptr object;231 dbus::Object::Ptr object;
307 dbus::Object::Ptr apparmor_session;232 media::apparmor::ubuntu::RequestContextResolver::Ptr request_context_resolver;
308233 media::apparmor::ubuntu::RequestAuthenticator::Ptr request_authenticator;
309 org::freedesktop::dbus::DBus::Stub dbus_stub;
310234
311 mpris::Player::Skeleton skeleton;235 mpris::Player::Skeleton skeleton;
312236
@@ -314,11 +238,13 @@
314 {238 {
315 typedef core::dbus::Signal<mpris::Player::Signals::Seeked, mpris::Player::Signals::Seeked::ArgumentType> DBusSeekedToSignal;239 typedef core::dbus::Signal<mpris::Player::Signals::Seeked, mpris::Player::Signals::Seeked::ArgumentType> DBusSeekedToSignal;
316 typedef core::dbus::Signal<mpris::Player::Signals::EndOfStream, mpris::Player::Signals::EndOfStream::ArgumentType> DBusEndOfStreamSignal;240 typedef core::dbus::Signal<mpris::Player::Signals::EndOfStream, mpris::Player::Signals::EndOfStream::ArgumentType> DBusEndOfStreamSignal;
241 typedef core::dbus::Signal<mpris::Player::Signals::AboutToFinish, mpris::Player::Signals::AboutToFinish::ArgumentType> DBusAboutToFinishSignal;
317 typedef core::dbus::Signal<mpris::Player::Signals::PlaybackStatusChanged, mpris::Player::Signals::PlaybackStatusChanged::ArgumentType> DBusPlaybackStatusChangedSignal;242 typedef core::dbus::Signal<mpris::Player::Signals::PlaybackStatusChanged, mpris::Player::Signals::PlaybackStatusChanged::ArgumentType> DBusPlaybackStatusChangedSignal;
318 typedef core::dbus::Signal<mpris::Player::Signals::VideoDimensionChanged, mpris::Player::Signals::VideoDimensionChanged::ArgumentType> DBusVideoDimensionChangedSignal;243 typedef core::dbus::Signal<mpris::Player::Signals::VideoDimensionChanged, mpris::Player::Signals::VideoDimensionChanged::ArgumentType> DBusVideoDimensionChangedSignal;
319 typedef core::dbus::Signal<mpris::Player::Signals::Error, mpris::Player::Signals::Error::ArgumentType> DBusErrorSignal;244 typedef core::dbus::Signal<mpris::Player::Signals::Error, mpris::Player::Signals::Error::ArgumentType> DBusErrorSignal;
320245
321 Signals(const std::shared_ptr<DBusSeekedToSignal>& remote_seeked,246 Signals(const std::shared_ptr<DBusSeekedToSignal>& remote_seeked,
247 const std::shared_ptr<DBusAboutToFinishSignal>& remote_atf,
322 const std::shared_ptr<DBusEndOfStreamSignal>& remote_eos,248 const std::shared_ptr<DBusEndOfStreamSignal>& remote_eos,
323 const std::shared_ptr<DBusPlaybackStatusChangedSignal>& remote_playback_status_changed,249 const std::shared_ptr<DBusPlaybackStatusChangedSignal>& remote_playback_status_changed,
324 const std::shared_ptr<DBusVideoDimensionChangedSignal>& remote_video_dimension_changed,250 const std::shared_ptr<DBusVideoDimensionChangedSignal>& remote_video_dimension_changed,
@@ -329,6 +255,11 @@
329 remote_seeked->emit(value);255 remote_seeked->emit(value);
330 });256 });
331257
258 about_to_finish.connect([remote_atf]()
259 {
260 remote_atf->emit();
261 });
262
332 end_of_stream.connect([remote_eos]()263 end_of_stream.connect([remote_eos]()
333 {264 {
334 remote_eos->emit();265 remote_eos->emit();
@@ -351,6 +282,7 @@
351 }282 }
352283
353 core::Signal<int64_t> seeked_to;284 core::Signal<int64_t> seeked_to;
285 core::Signal<void> about_to_finish;
354 core::Signal<void> end_of_stream;286 core::Signal<void> end_of_stream;
355 core::Signal<media::Player::PlaybackStatus> playback_status_changed;287 core::Signal<media::Player::PlaybackStatus> playback_status_changed;
356 core::Signal<media::video::Dimensions> video_dimension_changed;288 core::Signal<media::video::Dimensions> video_dimension_changed;
@@ -360,7 +292,7 @@
360};292};
361293
362media::PlayerSkeleton::PlayerSkeleton(const media::PlayerSkeleton::Configuration& config)294media::PlayerSkeleton::PlayerSkeleton(const media::PlayerSkeleton::Configuration& config)
363 : d(new Private{this, config.identity, config.bus, config.session})295 : d(new Private{this, config.bus, config.session, config.request_context_resolver, config.request_authenticator})
364{296{
365 // Setup method handlers for mpris::Player methods.297 // Setup method handlers for mpris::Player methods.
366 auto next = std::bind(&Private::handle_next, d, std::placeholders::_1);298 auto next = std::bind(&Private::handle_next, d, std::placeholders::_1);
@@ -635,6 +567,16 @@
635 return d->signals.seeked_to;567 return d->signals.seeked_to;
636}568}
637569
570const core::Signal<void>& media::PlayerSkeleton::about_to_finish() const
571{
572 return d->signals.about_to_finish;
573}
574
575core::Signal<void>& media::PlayerSkeleton::about_to_finish()
576{
577 return d->signals.about_to_finish;
578}
579
638const core::Signal<void>& media::PlayerSkeleton::end_of_stream() const580const core::Signal<void>& media::PlayerSkeleton::end_of_stream() const
639{581{
640 return d->signals.end_of_stream;582 return d->signals.end_of_stream;
641583
=== modified file 'src/core/media/player_skeleton.h'
--- src/core/media/player_skeleton.h 2015-03-16 15:38:27 +0000
+++ src/core/media/player_skeleton.h 2015-03-16 15:38:27 +0000
@@ -24,6 +24,7 @@
2424
25#include "player_traits.h"25#include "player_traits.h"
2626
27#include "apparmor/ubuntu.h"
27#include "mpris/player.h"28#include "mpris/player.h"
2829
29#include <core/dbus/skeleton.h>30#include <core/dbus/skeleton.h>
@@ -37,11 +38,29 @@
37{38{
38namespace media39namespace media
39{40{
41namespace helper
42{
43struct ExternalServices;
44}
45
40class Service;46class Service;
4147
42class PlayerSkeleton : public core::ubuntu::media::Player48class PlayerSkeleton : public core::ubuntu::media::Player
43{49{
44 public:50 public:
51 // All creation time arguments go here.
52 struct Configuration
53 {
54 // The bus connection we are associated with.
55 std::shared_ptr<core::dbus::Bus> bus;
56 // The session object that we want to expose the skeleton upon.
57 std::shared_ptr<core::dbus::Object> session;
58 // Our functional dependencies.
59 apparmor::ubuntu::RequestContextResolver::Ptr request_context_resolver;
60 apparmor::ubuntu::RequestAuthenticator::Ptr request_authenticator;
61 };
62
63 PlayerSkeleton(const Configuration& configuration);
45 ~PlayerSkeleton();64 ~PlayerSkeleton();
4665
47 virtual const core::Property<bool>& can_play() const;66 virtual const core::Property<bool>& can_play() const;
@@ -73,26 +92,11 @@
73 virtual core::Property<Lifetime>& lifetime();92 virtual core::Property<Lifetime>& lifetime();
7493
75 virtual const core::Signal<int64_t>& seeked_to() const;94 virtual const core::Signal<int64_t>& seeked_to() const;
95 virtual const core::Signal<void>& about_to_finish() const;
76 virtual const core::Signal<void>& end_of_stream() const;96 virtual const core::Signal<void>& end_of_stream() const;
77 virtual core::Signal<PlaybackStatus>& playback_status_changed();
78 virtual const core::Signal<video::Dimensions>& video_dimension_changed() const;97 virtual const core::Signal<video::Dimensions>& video_dimension_changed() const;
79 virtual const core::Signal<Error>& error() const;98 virtual const core::Signal<Error>& error() const;
8099
81protected:
82 // All creation time arguments go here.
83 struct Configuration
84 {
85 // The bus connection we are associated with.
86 std::shared_ptr<core::dbus::Bus> bus;
87 // The session object that we want to expose the skeleton upon.
88 std::shared_ptr<core::dbus::Object> session;
89 // Our identity, an identifier we pass out to other parts of the system.
90 // Defaults to the short app id (${PKG_NAME}_${APP}).
91 std::string identity;
92 };
93
94 PlayerSkeleton(const Configuration& configuration);
95
96 // These properties are not exposed to the client, but still need to be100 // These properties are not exposed to the client, but still need to be
97 // able to be settable from within the Player:101 // able to be settable from within the Player:
98 virtual core::Property<PlaybackStatus>& playback_status();102 virtual core::Property<PlaybackStatus>& playback_status();
@@ -111,7 +115,9 @@
111 virtual core::Property<Orientation>& orientation();115 virtual core::Property<Orientation>& orientation();
112116
113 virtual core::Signal<int64_t>& seeked_to();117 virtual core::Signal<int64_t>& seeked_to();
118 virtual core::Signal<void>& about_to_finish();
114 virtual core::Signal<void>& end_of_stream();119 virtual core::Signal<void>& end_of_stream();
120 virtual core::Signal<PlaybackStatus>& playback_status_changed();
115 virtual core::Signal<video::Dimensions>& video_dimension_changed();121 virtual core::Signal<video::Dimensions>& video_dimension_changed();
116 virtual core::Signal<Error>& error();122 virtual core::Signal<Error>& error();
117123
118124
=== modified file 'src/core/media/player_stub.cpp'
--- src/core/media/player_stub.cpp 2015-03-16 15:38:27 +0000
+++ src/core/media/player_stub.cpp 2015-03-16 15:38:27 +0000
@@ -76,6 +76,7 @@
76 signals76 signals
77 {77 {
78 object->get_signal<mpris::Player::Signals::Seeked>(),78 object->get_signal<mpris::Player::Signals::Seeked>(),
79 object->get_signal<mpris::Player::Signals::AboutToFinish>(),
79 object->get_signal<mpris::Player::Signals::EndOfStream>(),80 object->get_signal<mpris::Player::Signals::EndOfStream>(),
80 object->get_signal<mpris::Player::Signals::PlaybackStatusChanged>(),81 object->get_signal<mpris::Player::Signals::PlaybackStatusChanged>(),
81 object->get_signal<mpris::Player::Signals::VideoDimensionChanged>(),82 object->get_signal<mpris::Player::Signals::VideoDimensionChanged>(),
@@ -122,17 +123,20 @@
122 struct Signals123 struct Signals
123 {124 {
124 typedef core::dbus::Signal<mpris::Player::Signals::Seeked, mpris::Player::Signals::Seeked::ArgumentType> DBusSeekedToSignal;125 typedef core::dbus::Signal<mpris::Player::Signals::Seeked, mpris::Player::Signals::Seeked::ArgumentType> DBusSeekedToSignal;
126 typedef core::dbus::Signal<mpris::Player::Signals::AboutToFinish, mpris::Player::Signals::AboutToFinish::ArgumentType> DBusAboutToFinishSignal;
125 typedef core::dbus::Signal<mpris::Player::Signals::EndOfStream, mpris::Player::Signals::EndOfStream::ArgumentType> DBusEndOfStreamSignal;127 typedef core::dbus::Signal<mpris::Player::Signals::EndOfStream, mpris::Player::Signals::EndOfStream::ArgumentType> DBusEndOfStreamSignal;
126 typedef core::dbus::Signal<mpris::Player::Signals::PlaybackStatusChanged, mpris::Player::Signals::PlaybackStatusChanged::ArgumentType> DBusPlaybackStatusChangedSignal;128 typedef core::dbus::Signal<mpris::Player::Signals::PlaybackStatusChanged, mpris::Player::Signals::PlaybackStatusChanged::ArgumentType> DBusPlaybackStatusChangedSignal;
127 typedef core::dbus::Signal<mpris::Player::Signals::VideoDimensionChanged, mpris::Player::Signals::VideoDimensionChanged::ArgumentType> DBusVideoDimensionChangedSignal;129 typedef core::dbus::Signal<mpris::Player::Signals::VideoDimensionChanged, mpris::Player::Signals::VideoDimensionChanged::ArgumentType> DBusVideoDimensionChangedSignal;
128 typedef core::dbus::Signal<mpris::Player::Signals::Error, mpris::Player::Signals::Error::ArgumentType> DBusErrorSignal;130 typedef core::dbus::Signal<mpris::Player::Signals::Error, mpris::Player::Signals::Error::ArgumentType> DBusErrorSignal;
129131
130 Signals(const std::shared_ptr<DBusSeekedToSignal>& seeked,132 Signals(const std::shared_ptr<DBusSeekedToSignal>& seeked,
133 const std::shared_ptr<DBusAboutToFinishSignal>& atf,
131 const std::shared_ptr<DBusEndOfStreamSignal>& eos,134 const std::shared_ptr<DBusEndOfStreamSignal>& eos,
132 const std::shared_ptr<DBusPlaybackStatusChangedSignal>& status,135 const std::shared_ptr<DBusPlaybackStatusChangedSignal>& status,
133 const std::shared_ptr<DBusVideoDimensionChangedSignal>& d,136 const std::shared_ptr<DBusVideoDimensionChangedSignal>& d,
134 const std::shared_ptr<DBusErrorSignal>& e)137 const std::shared_ptr<DBusErrorSignal>& e)
135 : seeked_to(),138 : seeked_to(),
139 about_to_finish(),
136 end_of_stream(),140 end_of_stream(),
137 playback_status_changed(),141 playback_status_changed(),
138 video_dimension_changed(),142 video_dimension_changed(),
@@ -140,6 +144,7 @@
140 dbus144 dbus
141 {145 {
142 seeked,146 seeked,
147 atf,
143 eos,148 eos,
144 status,149 status,
145 d,150 d,
@@ -152,6 +157,12 @@
152 seeked_to(value);157 seeked_to(value);
153 });158 });
154159
160 dbus.about_to_finish->connect([this]()
161 {
162 std::cout << "AboutToFinish signal arrived via the bus." << std::endl;
163 about_to_finish();
164 });
165
155 dbus.end_of_stream->connect([this]()166 dbus.end_of_stream->connect([this]()
156 {167 {
157 std::cout << "EndOfStream signal arrived via the bus." << std::endl;168 std::cout << "EndOfStream signal arrived via the bus." << std::endl;
@@ -178,6 +189,7 @@
178 }189 }
179190
180 core::Signal<int64_t> seeked_to;191 core::Signal<int64_t> seeked_to;
192 core::Signal<void> about_to_finish;
181 core::Signal<void> end_of_stream;193 core::Signal<void> end_of_stream;
182 core::Signal<media::Player::PlaybackStatus> playback_status_changed;194 core::Signal<media::Player::PlaybackStatus> playback_status_changed;
183 core::Signal<media::video::Dimensions> video_dimension_changed;195 core::Signal<media::video::Dimensions> video_dimension_changed;
@@ -186,6 +198,7 @@
186 struct DBus198 struct DBus
187 {199 {
188 std::shared_ptr<DBusSeekedToSignal> seeked_to;200 std::shared_ptr<DBusSeekedToSignal> seeked_to;
201 std::shared_ptr<DBusAboutToFinishSignal> about_to_finish;
189 std::shared_ptr<DBusEndOfStreamSignal> end_of_stream;202 std::shared_ptr<DBusEndOfStreamSignal> end_of_stream;
190 std::shared_ptr<DBusPlaybackStatusChangedSignal> playback_status_changed;203 std::shared_ptr<DBusPlaybackStatusChangedSignal> playback_status_changed;
191 std::shared_ptr<DBusVideoDimensionChangedSignal> video_dimension_changed;204 std::shared_ptr<DBusVideoDimensionChangedSignal> video_dimension_changed;
@@ -434,6 +447,11 @@
434 return d->signals.seeked_to;447 return d->signals.seeked_to;
435}448}
436449
450const core::Signal<void>& media::PlayerStub::about_to_finish() const
451{
452 return d->signals.about_to_finish;
453}
454
437const core::Signal<void>& media::PlayerStub::end_of_stream() const455const core::Signal<void>& media::PlayerStub::end_of_stream() const
438{456{
439 return d->signals.end_of_stream;457 return d->signals.end_of_stream;
440458
=== modified file 'src/core/media/player_stub.h'
--- src/core/media/player_stub.h 2015-03-16 15:38:27 +0000
+++ src/core/media/player_stub.h 2015-03-16 15:38:27 +0000
@@ -86,6 +86,7 @@
86 virtual core::Property<Lifetime>& lifetime();86 virtual core::Property<Lifetime>& lifetime();
8787
88 virtual const core::Signal<int64_t>& seeked_to() const;88 virtual const core::Signal<int64_t>& seeked_to() const;
89 virtual const core::Signal<void>& about_to_finish() const;
89 virtual const core::Signal<void>& end_of_stream() const;90 virtual const core::Signal<void>& end_of_stream() const;
90 virtual core::Signal<PlaybackStatus>& playback_status_changed();91 virtual core::Signal<PlaybackStatus>& playback_status_changed();
91 virtual const core::Signal<video::Dimensions>& video_dimension_changed() const;92 virtual const core::Signal<video::Dimensions>& video_dimension_changed() const;
9293
=== modified file 'src/core/media/power/state_controller.cpp'
--- src/core/media/power/state_controller.cpp 2015-03-16 15:38:27 +0000
+++ src/core/media/power/state_controller.cpp 2015-03-16 15:38:27 +0000
@@ -128,42 +128,38 @@
128 if (cookie == the_invalid_cookie)128 if (cookie == the_invalid_cookie)
129 return;129 return;
130130
131 std::weak_ptr<DisplayStateLock> wp{shared_from_this()};131 // We make sure that we keep ourselves alive to make sure
132 // that release requests are always correctly issued.
133 auto sp = shared_from_this();
132134
133 auto current_cookie(cookie);135 auto current_cookie(cookie);
134136
135 timeout.expires_from_now(timeout_for_release());137 timeout.expires_from_now(timeout_for_release());
136 timeout.async_wait([wp, state, current_cookie](const boost::system::error_code& ec)138 timeout.async_wait([sp, state, current_cookie](const boost::system::error_code& ec)
137 {139 {
138 // We only return early from the timeout handler if the operation has been140 // We only return early from the timeout handler if the operation has been
139 // explicitly aborted before.141 // explicitly aborted before.
140 if (ec == boost::asio::error::operation_aborted)142 if (ec == boost::asio::error::operation_aborted)
141 return;143 return;
142144
143 if (auto sp = wp.lock())145 sp->object->invoke_method_asynchronously_with_callback<com::canonical::Unity::Screen::removeDisplayOnRequest, void>(
144 {146 [sp, state, current_cookie](const core::dbus::Result<void>& result)
145 sp->object->invoke_method_asynchronously_with_callback<com::canonical::Unity::Screen::removeDisplayOnRequest, void>(147 {
146 [wp, state, current_cookie](const core::dbus::Result<void>& result)148 if (result.is_error())
147 {149 {
148 if (result.is_error())150 std::cerr << result.error().print() << std::endl;
149 {151 return;
150 std::cerr << result.error().print() << std::endl;152 }
151 return;153
152 }154 sp->signals.released(state);
153155
154 if (auto sp = wp.lock())156 // We might have issued a different request before and
155 {157 // only call the display state done if the original cookie
156 sp->signals.released(state);158 // corresponds to the one we just gave up.
157159 if (sp->cookie == current_cookie)
158 // We might have issued a different request before and160 sp->cookie = the_invalid_cookie;
159 // only call the display state done if the original cookie161
160 // corresponds to the one we just gave up.162 }, current_cookie);
161 if (sp->cookie == current_cookie)
162 sp->cookie = the_invalid_cookie;
163 }
164
165 }, current_cookie);
166 }
167 });163 });
168 }164 }
169165
170166
=== modified file 'src/core/media/server/server.cpp'
--- src/core/media/server/server.cpp 2015-03-16 15:38:27 +0000
+++ src/core/media/server/server.cpp 2015-03-16 15:38:27 +0000
@@ -20,6 +20,7 @@
20#include <core/media/player.h>20#include <core/media/player.h>
21#include <core/media/track_list.h>21#include <core/media/track_list.h>
2222
23#include "core/media/hashed_keyed_player_store.h"
23#include "core/media/service_implementation.h"24#include "core/media/service_implementation.h"
2425
25#include <core/posix/signal.h>26#include <core/posix/signal.h>
@@ -103,23 +104,37 @@
103 }104 }
104 };105 };
105106
107 // Our common player store instance for tracking player instances.
108 auto player_store = std::make_shared<media::HashedKeyedPlayerStore>();
106 // We assemble the configuration for executing the service now.109 // We assemble the configuration for executing the service now.
107 media::ServiceImplementation::Configuration service_config110 media::ServiceImplementation::Configuration service_config
108 {111 {
112 std::make_shared<media::HashedKeyedPlayerStore>(),
109 external_services113 external_services
110 };114 };
111115
112 auto service = std::make_shared<media::ServiceImplementation>(service_config);116 auto impl = std::make_shared<media::ServiceImplementation>(media::ServiceImplementation::Configuration
117 {
118 player_store,
119 external_services
120 });
121
122 auto skeleton = std::make_shared<media::ServiceSkeleton>(media::ServiceSkeleton::Configuration
123 {
124 impl,
125 player_store,
126
127 });
113128
114 std::thread service_worker129 std::thread service_worker
115 {130 {
116 [&shutdown_requested, service]()131 [&shutdown_requested, skeleton]()
117 {132 {
118 while (not shutdown_requested)133 while (not shutdown_requested)
119 {134 {
120 try135 try
121 {136 {
122 service->run();137 skeleton->run();
123 }138 }
124 catch (const std::exception& e)139 catch (const std::exception& e)
125 {140 {
@@ -142,7 +157,7 @@
142 shutdown_requested = true;157 shutdown_requested = true;
143158
144 // And stop execution of helper and actual service.159 // And stop execution of helper and actual service.
145 service->stop();160 skeleton->stop();
146161
147 if (service_worker.joinable())162 if (service_worker.joinable())
148 service_worker.join();163 service_worker.join();
149164
=== modified file 'src/core/media/service_implementation.cpp'
--- src/core/media/service_implementation.cpp 2015-03-16 15:38:27 +0000
+++ src/core/media/service_implementation.cpp 2015-03-16 15:38:27 +0000
@@ -22,14 +22,16 @@
2222
23#include "service_implementation.h"23#include "service_implementation.h"
2424
25#include "apparmor/ubuntu.h"
25#include "audio/output_observer.h"26#include "audio/output_observer.h"
26#include "client_death_observer.h"27#include "client_death_observer.h"
27#include "call-monitor/call_monitor.h"
28#include "player_configuration.h"28#include "player_configuration.h"
29#include "player_skeleton.h"
29#include "player_implementation.h"30#include "player_implementation.h"
30#include "power/battery_observer.h"31#include "power/battery_observer.h"
31#include "power/state_controller.h"32#include "power/state_controller.h"
32#include "recorder_observer.h"33#include "recorder_observer.h"
34#include "telephony/call_monitor.h"
3335
34#include <boost/asio.hpp>36#include <boost/asio.hpp>
3537
@@ -59,8 +61,10 @@
59 client_death_observer(media::platform_default_client_death_observer()),61 client_death_observer(media::platform_default_client_death_observer()),
60 recorder_observer(media::make_platform_default_recorder_observer()),62 recorder_observer(media::make_platform_default_recorder_observer()),
61 audio_output_observer(media::audio::make_platform_default_output_observer()),63 audio_output_observer(media::audio::make_platform_default_output_observer()),
64 request_context_resolver(media::apparmor::ubuntu::make_platform_default_request_context_resolver(configuration.external_services)),
65 request_authenticator(media::apparmor::ubuntu::make_platform_default_request_authenticator()),
62 audio_output_state(media::audio::OutputState::Speaker),66 audio_output_state(media::audio::OutputState::Speaker),
63 call_monitor(new CallMonitor)67 call_monitor(media::telephony::make_platform_default_call_monitor())
64 {68 {
65 }69 }
6670
@@ -74,13 +78,16 @@
74 media::ClientDeathObserver::Ptr client_death_observer;78 media::ClientDeathObserver::Ptr client_death_observer;
75 media::RecorderObserver::Ptr recorder_observer;79 media::RecorderObserver::Ptr recorder_observer;
76 media::audio::OutputObserver::Ptr audio_output_observer;80 media::audio::OutputObserver::Ptr audio_output_observer;
77 media::audio::OutputObserver audio_output_state;81 media::apparmor::ubuntu::RequestContextResolver::Ptr request_context_resolver;
82 media::apparmor::ubuntu::RequestAuthenticator::Ptr request_authenticator;
83 media::audio::OutputState audio_output_state;
7884
79 std::unique_ptr<CallMonitor> call_monitor;85 media::telephony::CallMonitor::Ptr call_monitor;
80 std::list<media::Player::PlayerKey> paused_sessions;86 std::list<media::Player::PlayerKey> paused_sessions;
81};87};
8288
83media::ServiceImplementation::ServiceImplementation(const Configuration& configuration) : d(new Private(configuration))89media::ServiceImplementation::ServiceImplementation(const Configuration& configuration)
90 : d(new Private(configuration))
84{91{
85 d->battery_observer->level().changed().connect([this](const media::power::Level& level)92 d->battery_observer->level().changed().connect([this](const media::power::Level& level)
86 {93 {
@@ -118,22 +125,20 @@
118 break;125 break;
119 case audio::OutputState::External:126 case audio::OutputState::External:
120 std::cout << "AudioOutputObserver reports that output is now External." << std::endl;127 std::cout << "AudioOutputObserver reports that output is now External." << std::endl;
121 if (d->audio_output_state == audio::OutputState::Earpiece)
122 pause_all_multimedia_sessions();
123 break;128 break;
124 }129 }
125 d->audio_output_state = state;130 d->audio_output_state = state;
126 });131 });
127132
128 d->call_monitor->on_change([this](CallMonitor::State state) {133 d->call_monitor->on_call_state_changed().connect([this](media::telephony::CallMonitor::State state)
134 {
129 switch (state) {135 switch (state) {
130 case CallMonitor::OffHook:136 case media::telephony::CallMonitor::State::OffHook:
131 std::cout << "Got call started signal, pausing all multimedia sessions" << std::endl;137 std::cout << "Got call started signal, pausing all multimedia sessions" << std::endl;
132 pause_all_multimedia_sessions();138 pause_all_multimedia_sessions();
133 break;139 break;
134 case CallMonitor::OnHook:140 case media::telephony::CallMonitor::State::OnHook:
135 std::cout << "Got call ended signal, resuming paused multimedia sessions" << std::endl;141 std::cout << "Got call ended signal, resuming paused multimedia sessions" << std::endl;
136 // Don't auto-resume any paused video playback sessions
137 resume_paused_multimedia_sessions(false);142 resume_paused_multimedia_sessions(false);
138 break;143 break;
139 }144 }
@@ -160,12 +165,15 @@
160std::shared_ptr<media::Player> media::ServiceImplementation::create_session(165std::shared_ptr<media::Player> media::ServiceImplementation::create_session(
161 const media::Player::Configuration& conf)166 const media::Player::Configuration& conf)
162{167{
163 auto player = std::make_shared<media::PlayerImplementation>(media::PlayerImplementation::Configuration168 auto player = std::make_shared<media::PlayerImplementation<media::PlayerSkeleton>>(media::PlayerImplementation<media::PlayerSkeleton>::Configuration
164 {169 {
165 conf.identity,170 media::PlayerSkeleton::Configuration
166 conf.bus,171 {
167 conf.session,172 conf.bus,
168 shared_from_this(),173 conf.session,
174 d->request_context_resolver,
175 d->request_authenticator
176 },
169 conf.key,177 conf.key,
170 d->client_death_observer,178 d->client_death_observer,
171 d->power_state_controller179 d->power_state_controller
@@ -181,11 +189,11 @@
181 // until all dispatches are done189 // until all dispatches are done
182 d->configuration.external_services.io_service.post([this, key]()190 d->configuration.external_services.io_service.post([this, key]()
183 {191 {
184 if (!has_player_for_key(key))192 if (!d->configuration.player_store->has_player_for_key(key))
185 return;193 return;
186194
187 if (player_for_key(key)->lifetime() == Player::Lifetime::normal)195 if (d->configuration.player_store->player_for_key(key)->lifetime() == Player::Lifetime::normal)
188 remove_player_for_key(key);196 d->configuration.player_store->remove_player_for_key(key);
189 });197 });
190 });198 });
191199
@@ -206,19 +214,19 @@
206214
207void media::ServiceImplementation::pause_other_sessions(media::Player::PlayerKey key)215void media::ServiceImplementation::pause_other_sessions(media::Player::PlayerKey key)
208{216{
209 if (not has_player_for_key(key))217 if (not d->configuration.player_store->has_player_for_key(key))
210 {218 {
211 cerr << "Could not find Player by key: " << key << endl;219 cerr << "Could not find Player by key: " << key << endl;
212 return;220 return;
213 }221 }
214222
215 auto current_player = player_for_key(key);223 auto current_player = d->configuration.player_store->player_for_key(key);
216224
217 // We immediately make the player known as new current player.225 // We immediately make the player known as new current player.
218 if (current_player->audio_stream_role() == media::Player::multimedia)226 if (current_player->audio_stream_role() == media::Player::multimedia)
219 set_current_player_for_key(key);227 d->configuration.player_store->set_current_player_for_key(key);
220228
221 enumerate_players([current_player, key](const media::Player::PlayerKey& other_key, const std::shared_ptr<media::Player>& other_player)229 d->configuration.player_store->enumerate_players([current_player, key](const media::Player::PlayerKey& other_key, const std::shared_ptr<media::Player>& other_player)
222 {230 {
223 // Only pause a Player if all of the following criteria are met:231 // Only pause a Player if all of the following criteria are met:
224 // 1) currently playing232 // 1) currently playing
@@ -238,7 +246,7 @@
238246
239void media::ServiceImplementation::pause_all_multimedia_sessions()247void media::ServiceImplementation::pause_all_multimedia_sessions()
240{248{
241 enumerate_players([this](const media::Player::PlayerKey& key, const std::shared_ptr<media::Player>& player)249 d->configuration.player_store->enumerate_players([this](const media::Player::PlayerKey& key, const std::shared_ptr<media::Player>& player)
242 {250 {
243 if (player->playback_status() == Player::playing251 if (player->playback_status() == Player::playing
244 && player->audio_stream_role() == media::Player::multimedia)252 && player->audio_stream_role() == media::Player::multimedia)
@@ -253,7 +261,7 @@
253void media::ServiceImplementation::resume_paused_multimedia_sessions(bool resume_video_sessions)261void media::ServiceImplementation::resume_paused_multimedia_sessions(bool resume_video_sessions)
254{262{
255 std::for_each(d->paused_sessions.begin(), d->paused_sessions.end(), [this, resume_video_sessions](const media::Player::PlayerKey& key) {263 std::for_each(d->paused_sessions.begin(), d->paused_sessions.end(), [this, resume_video_sessions](const media::Player::PlayerKey& key) {
256 auto player = player_for_key(key);264 auto player = d->configuration.player_store->player_for_key(key);
257 // Only resume video playback if explicitly desired265 // Only resume video playback if explicitly desired
258 if (resume_video_sessions || player->is_audio_source())266 if (resume_video_sessions || player->is_audio_source())
259 player->play();267 player->play();
@@ -266,10 +274,10 @@
266274
267void media::ServiceImplementation::resume_multimedia_session()275void media::ServiceImplementation::resume_multimedia_session()
268{276{
269 if (not has_player_for_key(d->resume_key))277 if (not d->configuration.player_store->has_player_for_key(d->resume_key))
270 return;278 return;
271279
272 auto player = player_for_key(d->resume_key);280 auto player = d->configuration.player_store->player_for_key(d->resume_key);
273281
274 if (player->playback_status() == Player::paused)282 if (player->playback_status() == Player::paused)
275 {283 {
276284
=== modified file 'src/core/media/service_implementation.h'
--- src/core/media/service_implementation.h 2015-03-16 15:38:27 +0000
+++ src/core/media/service_implementation.h 2015-03-16 15:38:27 +0000
@@ -30,12 +30,13 @@
30{30{
31class Player;31class Player;
3232
33class ServiceImplementation : public ServiceSkeleton33class ServiceImplementation : public Service
34{34{
35public:35public:
36 // All creation time arguments go here.36 // All creation time arguments go here.
37 struct Configuration37 struct Configuration
38 {38 {
39 KeyedPlayerStore::Ptr player_store;
39 helper::ExternalServices& external_services;40 helper::ExternalServices& external_services;
40 };41 };
4142
4243
=== modified file 'src/core/media/service_skeleton.cpp'
--- src/core/media/service_skeleton.cpp 2014-11-18 20:29:26 +0000
+++ src/core/media/service_skeleton.cpp 2015-03-16 15:38:27 +0000
@@ -19,8 +19,6 @@
1919
20#include "service_skeleton.h"20#include "service_skeleton.h"
2121
22#include "apparmor.h"
23
24#include "mpris/media_player2.h"22#include "mpris/media_player2.h"
25#include "mpris/metadata.h"23#include "mpris/metadata.h"
26#include "mpris/player.h"24#include "mpris/player.h"
@@ -51,12 +49,12 @@
5149
52struct media::ServiceSkeleton::Private50struct media::ServiceSkeleton::Private
53{51{
54 Private(media::ServiceSkeleton* impl, const media::CoverArtResolver& resolver)52 Private(media::ServiceSkeleton* impl, const ServiceSkeleton::Configuration& config)
55 : impl(impl),53 : impl(impl),
56 object(impl->access_service()->add_object_for_path(54 object(impl->access_service()->add_object_for_path(
57 dbus::traits::Service<media::Service>::object_path())),55 dbus::traits::Service<media::Service>::object_path())),
58 dbus_stub(impl->access_bus()),56 exported(impl->access_bus(), config.cover_art_resolver),
59 exported(impl->access_bus(), resolver)57 configuration(config)
60 {58 {
61 object->install_method_handler<mpris::Service::CreateSession>(59 object->install_method_handler<mpris::Service::CreateSession>(
62 std::bind(60 std::bind(
@@ -99,129 +97,71 @@
99 dbus::types::ObjectPath op{session_info.first};97 dbus::types::ObjectPath op{session_info.first};
100 media::Player::PlayerKey key{session_info.second};98 media::Player::PlayerKey key{session_info.second};
10199
102 dbus_stub.get_connection_app_armor_security_async(msg->sender(), [this, msg, op, key](const std::string& profile)100 media::Player::Configuration config
103 {101 {
104 media::Player::Configuration config102 key,
105 {103 impl->access_bus(),
106 profile,104 impl->access_service()->add_object_for_path(op)
107 key,105 };
108 impl->access_bus(),106
109 impl->access_service()->add_object_for_path(op)107 try
110 };108 {
111109 configuration.player_store->add_player_for_key(key, impl->create_session(config));
112 try110 auto reply = dbus::Message::make_method_return(msg);
113 {111 reply->writer() << op;
112
113 impl->access_bus()->send(reply);
114 } catch(const std::runtime_error& e)
115 {
116 auto reply = dbus::Message::make_error(
117 msg,
118 mpris::Service::Errors::CreatingSession::name(),
119 e.what());
120 impl->access_bus()->send(reply);
121 }
122 }
123
124 void handle_create_fixed_session(const core::dbus::Message::Ptr& msg)
125 {
126 try
127 {
128 std::string name;
129 msg->reader() >> name;
130
131 if (named_player_map.count(name) == 0) {
132 // Create new session
133 auto session_info = create_session_info();
134
135 dbus::types::ObjectPath op{session_info.first};
136 media::Player::PlayerKey key{session_info.second};
137
138 media::Player::Configuration config
139 {
140 key,
141 impl->access_bus(),
142 impl->access_service()->add_object_for_path(op)
143 };
144
114 auto session = impl->create_session(config);145 auto session = impl->create_session(config);
115146 session->lifetime().set(media::Player::Lifetime::resumable);
116 bool inserted = false;147
117 std::tie(std::ignore, inserted)148 configuration.player_store->add_player_for_key(key, session);
118 = session_store.insert(std::make_pair(key, session));149
119150
120 if (!inserted)151 named_player_map.insert(std::make_pair(name, key));
121 throw std::runtime_error("Problem persisting session in session store.");
122
123152
124 auto reply = dbus::Message::make_method_return(msg);153 auto reply = dbus::Message::make_method_return(msg);
125 reply->writer() << op;154 reply->writer() << op;
126155
127 impl->access_bus()->send(reply);156 impl->access_bus()->send(reply);
128 } catch(const std::runtime_error& e)157 }
129 {158 else {
130 auto reply = dbus::Message::make_error(159 // Resume previous session
131 msg,160 auto key = named_player_map.at(name);
132 mpris::Service::Errors::CreatingSession::name(),161 if (not configuration.player_store->has_player_for_key(key)) {
133 e.what());
134 impl->access_bus()->send(reply);
135 }
136 });
137 }
138
139 void handle_create_fixed_session(const core::dbus::Message::Ptr& msg)
140 {
141 dbus_stub.get_connection_app_armor_security_async(msg->sender(), [this, msg](const std::string& profile)
142 {
143 try
144 {
145 std::string name;
146 msg->reader() >> name;
147
148 if (fixed_session_store.count(name) == 0) {
149 // Create new session
150 auto session_info = create_session_info();
151
152 dbus::types::ObjectPath op{session_info.first};
153 media::Player::PlayerKey key{session_info.second};
154
155 media::Player::Configuration config
156 {
157 profile,
158 key,
159 impl->access_bus(),
160 impl->access_service()->add_object_for_path(op)
161 };
162
163 auto session = impl->create_session(config);
164 session->lifetime().set(media::Player::Lifetime::resumable);
165
166 bool inserted = false;
167 std::tie(std::ignore, inserted)
168 = session_store.insert(std::make_pair(key, session));
169
170 if (!inserted)
171 throw std::runtime_error("Problem persisting session in session store.");
172
173 fixed_session_store.insert(std::make_pair(name, key));
174
175 auto reply = dbus::Message::make_method_return(msg);
176 reply->writer() << op;
177
178 impl->access_bus()->send(reply);
179 }
180 else {
181 // Resume previous session
182 auto key = fixed_session_store[name];
183 if (session_store.count(key) == 0) {
184 auto reply = dbus::Message::make_error(
185 msg,
186 mpris::Service::Errors::CreatingFixedSession::name(),
187 "Unable to locate player session");
188 impl->access_bus()->send(reply);
189 return;
190 }
191
192 std::stringstream ss;
193 ss << "/core/ubuntu/media/Service/sessions/" << key;
194 dbus::types::ObjectPath op{ss.str()};
195
196 auto reply = dbus::Message::make_method_return(msg);
197 reply->writer() << op;
198
199 impl->access_bus()->send(reply);
200 }
201 } catch(const std::runtime_error& e)
202 {
203 auto reply = dbus::Message::make_error(
204 msg,
205 mpris::Service::Errors::CreatingSession::name(),
206 e.what());
207 impl->access_bus()->send(reply);
208 }
209 });
210 }
211
212 void handle_resume_session(const core::dbus::Message::Ptr& msg)
213 {
214 dbus_stub.get_connection_app_armor_security_async(msg->sender(), [this, msg](const std::string&)
215 {
216 try
217 {
218 Player::PlayerKey key;
219 msg->reader() >> key;
220
221 if (session_store.count(key) == 0) {
222 auto reply = dbus::Message::make_error(162 auto reply = dbus::Message::make_error(
223 msg,163 msg,
224 mpris::Service::Errors::ResumingSession::name(),164 mpris::Service::Errors::CreatingFixedSession::name(),
225 "Unable to locate player session");165 "Unable to locate player session");
226 impl->access_bus()->send(reply);166 impl->access_bus()->send(reply);
227 return;167 return;
@@ -235,15 +175,49 @@
235 reply->writer() << op;175 reply->writer() << op;
236176
237 impl->access_bus()->send(reply);177 impl->access_bus()->send(reply);
238 } catch(const std::runtime_error& e)178 }
239 {179 } catch(const std::runtime_error& e)
180 {
181 auto reply = dbus::Message::make_error(
182 msg,
183 mpris::Service::Errors::CreatingSession::name(),
184 e.what());
185 impl->access_bus()->send(reply);
186 }
187 }
188
189 void handle_resume_session(const core::dbus::Message::Ptr& msg)
190 {
191 try
192 {
193 Player::PlayerKey key;
194 msg->reader() >> key;
195
196 if (not configuration.player_store->has_player_for_key(key)) {
240 auto reply = dbus::Message::make_error(197 auto reply = dbus::Message::make_error(
241 msg,198 msg,
242 mpris::Service::Errors::CreatingSession::name(),199 mpris::Service::Errors::ResumingSession::name(),
243 e.what());200 "Unable to locate player session");
244 impl->access_bus()->send(reply);201 impl->access_bus()->send(reply);
202 return;
245 }203 }
246 });204
205 std::stringstream ss;
206 ss << "/core/ubuntu/media/Service/sessions/" << key;
207 dbus::types::ObjectPath op{ss.str()};
208
209 auto reply = dbus::Message::make_method_return(msg);
210 reply->writer() << op;
211
212 impl->access_bus()->send(reply);
213 } catch(const std::runtime_error& e)
214 {
215 auto reply = dbus::Message::make_error(
216 msg,
217 mpris::Service::Errors::CreatingSession::name(),
218 e.what());
219 impl->access_bus()->send(reply);
220 }
247 }221 }
248222
249 void handle_pause_other_sessions(const core::dbus::Message::Ptr& msg)223 void handle_pause_other_sessions(const core::dbus::Message::Ptr& msg)
@@ -260,11 +234,10 @@
260 media::ServiceSkeleton* impl;234 media::ServiceSkeleton* impl;
261 dbus::Object::Ptr object;235 dbus::Object::Ptr object;
262236
263 // We query the apparmor profile to obtain an identity for players.237 // We remember all our creation time arguments.
264 org::freedesktop::dbus::DBus::Stub dbus_stub;238 ServiceSkeleton::Configuration configuration;
265 // We track all running player instances.239 // We map named/fixed player instances to their respective keys.
266 std::map<media::Player::PlayerKey, std::shared_ptr<media::Player>> session_store;240 std::map<std::string, media::Player::PlayerKey> named_player_map;
267 std::map<std::string, media::Player::PlayerKey> fixed_session_store;
268 // We expose the entire service as an MPRIS player.241 // We expose the entire service as an MPRIS player.
269 struct Exported242 struct Exported
270 {243 {
@@ -498,7 +471,7 @@
498 mpris::Player::Skeleton player;471 mpris::Player::Skeleton player;
499 mpris::Playlists::Skeleton playlists;472 mpris::Playlists::Skeleton playlists;
500473
501 // Helper to resolve (title, artist, album) tuples to cover art.474 // The CoverArtResolver used by the exported player.
502 media::CoverArtResolver cover_art_resolver;475 media::CoverArtResolver cover_art_resolver;
503 // The actual player instance.476 // The actual player instance.
504 std::weak_ptr<media::Player> current_player;477 std::weak_ptr<media::Player> current_player;
@@ -533,9 +506,9 @@
533 } exported;506 } exported;
534};507};
535508
536media::ServiceSkeleton::ServiceSkeleton(const media::CoverArtResolver& resolver)509media::ServiceSkeleton::ServiceSkeleton(const Configuration& configuration)
537 : dbus::Skeleton<media::Service>(the_session_bus()),510 : dbus::Skeleton<media::Service>(the_session_bus()),
538 d(new Private(this, resolver))511 d(new Private(this, configuration))
539{512{
540}513}
541514
@@ -543,46 +516,24 @@
543{516{
544}517}
545518
546bool media::ServiceSkeleton::has_player_for_key(const media::Player::PlayerKey& key) const519std::shared_ptr<media::Player> media::ServiceSkeleton::create_session(const media::Player::Configuration& config)
547{520{
548 return d->session_store.count(key) > 0;521 return d->configuration.impl->create_session(config);
549}522}
550523
551std::shared_ptr<media::Player> media::ServiceSkeleton::player_for_key(const media::Player::PlayerKey& key) const524std::shared_ptr<media::Player> media::ServiceSkeleton::create_fixed_session(const std::string& name, const media::Player::Configuration&config)
552{525{
553 return d->session_store.at(key);526 return d->configuration.impl->create_fixed_session(name, config);
554}527}
555528
556void media::ServiceSkeleton::enumerate_players(const media::ServiceSkeleton::PlayerEnumerator& enumerator) const529std::shared_ptr<media::Player> media::ServiceSkeleton::resume_session(media::Player::PlayerKey key)
557{530{
558 for (const auto& pair : d->session_store)531 return d->configuration.impl->resume_session(key);
559 enumerator(pair.first, pair.second);532}
560}533
561534void media::ServiceSkeleton::pause_other_sessions(media::Player::PlayerKey key)
562void media::ServiceSkeleton::set_current_player_for_key(const media::Player::PlayerKey& key)535{
563{536 d->configuration.impl->pause_other_sessions(key);
564 if (not has_player_for_key(key))
565 return;
566
567 d->exported.set_current_player(player_for_key(key));
568}
569
570void media::ServiceSkeleton::remove_player_for_key(const media::Player::PlayerKey& key)
571{
572 if (not has_player_for_key(key))
573 return;
574
575 auto player = player_for_key(key);
576
577 d->session_store.erase(key);
578 d->exported.unset_if_current(player);
579 // All non-durable fixed sessions are also removed
580 for (auto it: d->fixed_session_store) {
581 if (it.second == key) {
582 d->fixed_session_store.erase(it.first);
583 break;
584 }
585 }
586}537}
587538
588void media::ServiceSkeleton::run()539void media::ServiceSkeleton::run()
589540
=== modified file 'src/core/media/service_skeleton.h'
--- src/core/media/service_skeleton.h 2014-09-09 21:27:29 +0000
+++ src/core/media/service_skeleton.h 2015-03-16 15:38:27 +0000
@@ -22,6 +22,7 @@
22#include <core/media/service.h>22#include <core/media/service.h>
2323
24#include "cover_art_resolver.h"24#include "cover_art_resolver.h"
25#include "keyed_player_store.h"
25#include "service_traits.h"26#include "service_traits.h"
2627
27#include <core/dbus/skeleton.h>28#include <core/dbus/skeleton.h>
@@ -37,34 +38,22 @@
37class ServiceSkeleton : public core::dbus::Skeleton<core::ubuntu::media::Service>38class ServiceSkeleton : public core::dbus::Skeleton<core::ubuntu::media::Service>
38{39{
39public:40public:
40 // Functor for enumerating all known (key, player) pairs.41 // Creation time arguments go here.
41 typedef std::function42 struct Configuration
42 <43 {
43 void(44 std::shared_ptr<Service> impl;
44 // The key of the player.45 KeyedPlayerStore::Ptr player_store;
45 const core::ubuntu::media::Player::PlayerKey&,46 CoverArtResolver cover_art_resolver;
46 // The actual player instance.47 };
47 const std::shared_ptr<core::ubuntu::media::Player>&
48 )
49 > PlayerEnumerator;
5048
51 ServiceSkeleton(const CoverArtResolver& cover_art_resolver = always_missing_cover_art_resolver());49 ServiceSkeleton(const Configuration& configuration);
52 ~ServiceSkeleton();50 ~ServiceSkeleton();
5351
54 // We keep track of all known player sessions here and render them accessible via52 // From media::Service
55 // the key. All of these functions are thread-safe but not reentrant.53 std::shared_ptr<Player> create_session(const Player::Configuration&);
56 // Returns true iff a player is known for the given key.54 std::shared_ptr<Player> create_fixed_session(const std::string& name, const Player::Configuration&);
57 bool has_player_for_key(const Player::PlayerKey& key) const;55 std::shared_ptr<Player> resume_session(Player::PlayerKey);
58 // Returns the player for the given key or throws std::out_of_range if no player is known56 void pause_other_sessions(Player::PlayerKey key);
59 // for the given key.
60 std::shared_ptr<Player> player_for_key(const Player::PlayerKey& key) const;
61 // Enumerates all known players and invokes the given enumerator for each
62 // (key, player) pair.
63 void enumerate_players(const PlayerEnumerator& enumerator) const;
64 // Removes the player for the given key, and unsets it if it is the current one.
65 void remove_player_for_key(const Player::PlayerKey& key);
66 // Makes the player known under the given key current.
67 void set_current_player_for_key(const Player::PlayerKey& key);
6857
69 void run();58 void run();
70 void stop();59 void stop();
7160
=== renamed directory 'src/core/media/call-monitor' => 'src/core/media/telephony'
=== modified file 'src/core/media/telephony/call_monitor.cpp'
--- src/core/media/call-monitor/call_monitor.cpp 2015-01-12 21:35:36 +0000
+++ src/core/media/telephony/call_monitor.cpp 2015-03-16 15:38:27 +0000
@@ -29,8 +29,12 @@
29#include <list>29#include <list>
30#include <mutex>30#include <mutex>
3131
32namespace media = core::ubuntu::media;
3233
33namespace {34namespace
35{
36namespace impl
37{
34class TelepathyCallMonitor : public QObject38class TelepathyCallMonitor : public QObject
35{39{
36 Q_OBJECT40 Q_OBJECT
@@ -73,7 +77,7 @@
73 }77 }
74 }78 }
7579
76 void on_change(const std::function<void(CallMonitor::State)>& func) {80 void on_change(const std::function<void(media::telephony::CallMonitor::State)>& func) {
77 std::lock_guard<std::mutex> l(cb_lock);81 std::lock_guard<std::mutex> l(cb_lock);
78 cb = func;82 cb = func;
79 }83 }
@@ -131,19 +135,19 @@
131 {135 {
132 std::lock_guard<std::mutex> l(cb_lock);136 std::lock_guard<std::mutex> l(cb_lock);
133 if (cb)137 if (cb)
134 cb(CallMonitor::OffHook);138 cb(media::telephony::CallMonitor::State::OffHook);
135 }139 }
136140
137 void onHook()141 void onHook()
138 {142 {
139 std::lock_guard<std::mutex> l(cb_lock);143 std::lock_guard<std::mutex> l(cb_lock);
140 if (cb)144 if (cb)
141 cb(CallMonitor::OnHook);145 cb(media::telephony::CallMonitor::State::OnHook);
142 }146 }
143147
144private:148private:
145 std::mutex cb_lock;149 std::mutex cb_lock;
146 std::function<void (CallMonitor::State)> cb;150 std::function<void (media::telephony::CallMonitor::State)> cb;
147 Tp::AccountManagerPtr mAccountManager;151 Tp::AccountManagerPtr mAccountManager;
148 std::list<TelepathyCallMonitor*> mCallMonitors;152 std::list<TelepathyCallMonitor*> mCallMonitors;
149153
@@ -159,63 +163,78 @@
159 mCallMonitors.push_back(tcm);163 mCallMonitors.push_back(tcm);
160 }164 }
161};165};
162}
163166
164class CallMonitorPrivate167struct CallMonitor : public media::telephony::CallMonitor
165{168{
166public:169 CallMonitor() : mBridge{nullptr}
167 CallMonitorPrivate() {170 {
168 mBridge = nullptr;171 try
169 try {172 {
170 std::thread([this]() {173 qt_world = std::move(std::thread([this]()
171 qt::core::world::build_and_run(0, nullptr, [this]() {174 {
172 qt::core::world::enter_with_task([this]() {175 qt::core::world::build_and_run(0, nullptr, [this]()
176 {
177 qt::core::world::enter_with_task([this]()
178 {
173 std::cout << "CallMonitor: Creating TelepathyBridge" << std::endl;179 std::cout << "CallMonitor: Creating TelepathyBridge" << std::endl;
174 mBridge = new TelepathyBridge();180 mBridge = new TelepathyBridge();
175 cv.notify_all();181 cv.notify_all();
176 });182 });
177 });183 });
178 }).detach();184 }));
179 } catch(const std::system_error& error) {185 } catch(const std::system_error& error) {
180 std::cerr << "exception(std::system_error) in CallMonitor thread start" << error.what() << std::endl;186 std::cerr << "exception(std::system_error) in CallMonitor thread start" << error.what() << std::endl;
181 } catch(...) {187 } catch(...) {
182 std::cerr << "exception(...) in CallMonitor thread start" << std::endl;188 std::cerr << "exception(...) in CallMonitor thread start" << std::endl;
183 }189 }
190
184 // Wait until telepathy bridge is set, so we can hook up the change signals191 // Wait until telepathy bridge is set, so we can hook up the change signals
185 std::unique_lock<std::mutex> lck(mtx);192 std::unique_lock<std::mutex> lck(mtx);
186 cv.wait_for(lck, std::chrono::seconds(3));193 cv.wait_for(lck, std::chrono::seconds(3));
194
195 if (mBridge)
196 {
197 mBridge->on_change([this](CallMonitor::State state)
198 {
199 call_state_changed(state);
200 });
201 }
187 }202 }
188203
189 ~CallMonitorPrivate() {204 ~CallMonitor()
205 {
206 // We first clean up the bridge instance.
207 qt::core::world::enter_with_task([this]()
208 {
209 delete mBridge;
210 }).get();
211
212 // We then request destruction of the qt world.
190 qt::core::world::destroy();213 qt::core::world::destroy();
214
215 // Before we finally join the worker.
216 if (qt_world.joinable())
217 qt_world.join();
218 }
219
220 const core::Signal<media::telephony::CallMonitor::State>& on_call_state_changed() const
221 {
222 return call_state_changed;
191 }223 }
192224
193 TelepathyBridge *mBridge;225 TelepathyBridge *mBridge;
226 core::Signal<media::telephony::CallMonitor::State> call_state_changed;
194227
195private:228 std::thread qt_world;
196 std::mutex mtx;229 std::mutex mtx;
197 std::condition_variable cv;230 std::condition_variable cv;
198};231};
199232}
200233}
201CallMonitor::CallMonitor():234
202 d(new CallMonitorPrivate)235media::telephony::CallMonitor::Ptr media::telephony::make_platform_default_call_monitor()
203{236{
204}237 return std::make_shared<impl::CallMonitor>();
205
206CallMonitor::~CallMonitor()
207{
208 delete d->mBridge;
209 delete d;
210}
211
212void CallMonitor::on_change(const std::function<void(CallMonitor::State)>& func)
213{
214 if (d->mBridge != nullptr) {
215 std::cout << "CallMonitor: Setting up callback for TelepathyBridge on_change" << std::endl;
216 d->mBridge->on_change(func);
217 } else
218 std::cerr << "TelepathyBridge: Failed to hook on_change signal, bridge not yet set" << std::endl;
219}238}
220239
221#include "call_monitor.moc"240#include "call_monitor.moc"
222241
=== modified file 'src/core/media/telephony/call_monitor.h'
--- src/core/media/call-monitor/call_monitor.h 2014-10-31 07:49:33 +0000
+++ src/core/media/telephony/call_monitor.h 2015-03-16 15:38:27 +0000
@@ -17,25 +17,50 @@
17 */17 */
1818
1919
20#ifndef CALLMONITOR_H20#ifndef CORE_UBUNTU_MEDIA_TELEPHONY_CALL_MONITOR_H_
21#define CALLMONITOR_H21#define CORE_UBUNTU_MEDIA_TELEPHONY_CALL_MONITOR_H_
22
23#include <core/signal.h>
2224
23#include <functional>25#include <functional>
2426#include <memory>
25class CallMonitorPrivate;27
2628namespace core
27class CallMonitor29{
28{30namespace ubuntu
29public:31{
30 enum State { OffHook, OnHook };32namespace media
3133{
32 CallMonitor();34namespace telephony
33 ~CallMonitor();35{
3436// CallMonitor models the ability to observe and react
35 void on_change(const std::function<void(CallMonitor::State)>& func);37// to changes of the overall call state of the system.
3638struct CallMonitor
37private:39{
38 CallMonitorPrivate *d;40 // Save us some typing.
41 typedef std::shared_ptr<CallMonitor> Ptr;
42
43 // All known call states
44 enum class State
45 {
46 // No current call.
47 OffHook,
48 // Call in progress.
49 OnHook
50 };
51
52 CallMonitor() = default;
53 virtual ~CallMonitor() = default;
54
55 // Emitted whenever the current call state of the system changes.
56 virtual const core::Signal<State>& on_call_state_changed() const = 0;
39};57};
4058
41#endif // CALLMONITOR_H59// Returns a platform default implementation of CallMonitor.
60CallMonitor::Ptr make_platform_default_call_monitor();
61}
62}
63}
64}
65
66#endif // CORE_UBUNTU_MEDIA_TELEPHONY_CALL_MONITOR_H_

Subscribers

People subscribed via source and target branches