Music Hub  ..
A session-wide music playback service
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
service_implementation.cpp
Go to the documentation of this file.
1 /*
2  * Copyright © 2013-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  * Jim Hodapp <jim.hodapp@canonical.com>
18  */
19 
20 #include "service_implementation.h"
21 
23 #include "player_configuration.h"
24 #include "player_implementation.h"
25 
26 #include <boost/asio.hpp>
27 
28 #include <cstdint>
29 #include <map>
30 #include <memory>
31 #include <thread>
32 
33 #include "util/timeout.h"
34 #include "unity_screen_service.h"
35 #include <hybris/media/media_recorder_layer.h>
36 
37 namespace media = core::ubuntu::media;
38 
39 using namespace std;
40 
42 {
44  : resume_key(std::numeric_limits<std::uint32_t>::max()),
45  keep_alive(io_service),
46  disp_cookie(0)
47  {
48  bus = std::shared_ptr<dbus::Bus>(new dbus::Bus(core::dbus::WellKnownBus::session));
49  bus->install_executor(dbus::asio::make_executor(bus, io_service));
50  worker = std::move(std::thread([this]()
51  {
52  bus->run();
53  }));
54 
55  // Connect the property change signal that will allow media-hub to take appropriate action
56  // when the battery level reaches critical
57  auto stub_service = dbus::Service::use_service(bus, "com.canonical.indicator.power");
58  indicator_power_session = stub_service->object_for_path(dbus::types::ObjectPath("/com/canonical/indicator/power/Battery"));
59  power_level = indicator_power_session->get_property<core::IndicatorPower::PowerLevel>();
60  is_warning = indicator_power_session->get_property<core::IndicatorPower::IsWarning>();
61 
62  // Obtain session with Unity.Screen so that we request state when doing recording
63  auto bus = std::shared_ptr<dbus::Bus>(new dbus::Bus(core::dbus::WellKnownBus::system));
64  bus->install_executor(dbus::asio::make_executor(bus));
65 
66  auto uscreen_stub_service = dbus::Service::use_service(bus, dbus::traits::Service<core::UScreen>::interface_name());
67  uscreen_session = uscreen_stub_service->object_for_path(dbus::types::ObjectPath("/com/canonical/Unity/Screen"));
68 
69  observer = android_media_recorder_observer_new();
70  android_media_recorder_observer_set_cb(observer, &Private::media_recording_started_callback, this);
71  }
72 
74  {
75  bus->stop();
76 
77  if (worker.joinable())
78  worker.join();
79  }
80 
81  void media_recording_started(bool started)
82  {
83  if (uscreen_session == nullptr)
84  return;
85 
86  if (started)
87  {
88  if (disp_cookie > 0)
89  return;
90 
91  auto result = uscreen_session->invoke_method_synchronously<core::UScreen::keepDisplayOn, int>();
92  if (result.is_error())
93  throw std::runtime_error(result.error().print());
94  disp_cookie = result.value();
95  }
96  else
97  {
98  if (disp_cookie != -1)
99  {
100  timeout(4000, true, [this](){
101  this->uscreen_session->invoke_method_synchronously<core::UScreen::removeDisplayOnRequest, void>(this->disp_cookie);
102  this->disp_cookie = -1;
103  });
104  }
105  }
106  }
107 
108  static void media_recording_started_callback(bool started, void *context)
109  {
110  if (context == nullptr)
111  return;
112 
113  auto p = static_cast<Private*>(context);
114  p->media_recording_started(started);
115  }
116 
117  // This holds the key of the multimedia role Player instance that was paused
118  // when the battery level reached 10% or 5%
120  std::thread worker;
121  dbus::Bus::Ptr bus;
122  boost::asio::io_service io_service;
123  boost::asio::io_service::work keep_alive;
124  std::shared_ptr<dbus::Object> indicator_power_session;
125  std::shared_ptr<core::dbus::Property<core::IndicatorPower::PowerLevel>> power_level;
126  std::shared_ptr<core::dbus::Property<core::IndicatorPower::IsWarning>> is_warning;
128  std::shared_ptr<dbus::Object> uscreen_session;
129  MediaRecorderObserver *observer;
130 };
131 
133 {
134  cout << __PRETTY_FUNCTION__ << endl;
135 
136  d->power_level->changed().connect([this](const core::IndicatorPower::PowerLevel::ValueType &level)
137  {
138  // When the battery level hits 10% or 5%, pause all multimedia sessions.
139  // Playback will resume when the user clears the presented notification.
140  if (level == "low" || level == "very_low")
141  pause_all_multimedia_sessions();
142  });
143 
144  d->is_warning->changed().connect([this](const core::IndicatorPower::IsWarning::ValueType &notifying)
145  {
146  // If the low battery level notification is no longer being displayed,
147  // resume what the user was previously playing
148  if (!notifying)
149  resume_multimedia_session();
150  });
151 }
152 
154 {
155 }
156 
157 std::shared_ptr<media::Player> media::ServiceImplementation::create_session(
158  const media::Player::Configuration& conf)
159 {
160  auto player = std::make_shared<media::PlayerImplementation>(
161  conf.identity, conf.bus, conf.session, shared_from_this(), conf.key);
162 
163  auto key = conf.key;
164  player->on_client_disconnected().connect([this, key]()
165  {
166  // Call remove_player_for_key asynchronously otherwise deadlock can occur
167  // if called within this dispatcher context.
168  // remove_player_for_key can destroy the player instance which in turn
169  // destroys the "on_client_disconnected" signal whose destructor will wait
170  // until all dispatches are done
171  d->io_service.post([this, key]()
172  {
174  });
175  });
176 
177  return player;
178 }
179 
181 {
182  if (not has_player_for_key(key))
183  {
184  cerr << "Could not find Player by key: " << key << endl;
185  return;
186  }
187 
188  auto current_player = player_for_key(key);
189 
190  // We immediately make the player known as new current player.
191  if (current_player->audio_stream_role() == media::Player::multimedia)
193 
194  enumerate_players([current_player, key](const media::Player::PlayerKey& other_key, const std::shared_ptr<media::Player>& other_player)
195  {
196  // Only pause a Player if all of the following criteria are met:
197  // 1) currently playing
198  // 2) not the same player as the one passed in my key
199  // 3) new Player has an audio stream role set to multimedia
200  // 4) has an audio stream role set to multimedia
201  if (other_player->playback_status() == Player::playing &&
202  other_key != key &&
203  current_player->audio_stream_role() == media::Player::multimedia &&
204  other_player->audio_stream_role() == media::Player::multimedia)
205  {
206  cout << "Pausing Player with key: " << other_key << endl;
207  other_player->pause();
208  }
209  });
210 }
211 
212 void media::ServiceImplementation::pause_all_multimedia_sessions()
213 {
214  enumerate_players([this](const media::Player::PlayerKey& key, const std::shared_ptr<media::Player>& player)
215  {
216  if (player->playback_status() == Player::playing
217  && player->audio_stream_role() == media::Player::multimedia)
218  {
219  d->resume_key = key;
220  cout << "Will resume playback of Player with key: " << d->resume_key << endl;
221  player->pause();
222  }
223  });
224 }
225 
226 void media::ServiceImplementation::resume_multimedia_session()
227 {
228  if (not has_player_for_key(d->resume_key))
229  return;
230 
231  auto player = player_for_key(d->resume_key);
232 
233  if (player->playback_status() == Player::paused)
234  {
235  cout << "Resuming playback of Player with key: " << d->resume_key << endl;
236  player->play();
237  d->resume_key = std::numeric_limits<std::uint32_t>::max();
238  }
239 }
void set_current_player_for_key(const Player::PlayerKey &key)
bool has_player_for_key(const Player::PlayerKey &key) const
STL namespace.
void enumerate_players(const PlayerEnumerator &enumerator) const
std::shared_ptr< core::dbus::Property< core::IndicatorPower::PowerLevel > > power_level
static void media_recording_started_callback(bool started, void *context)
void pause_other_sessions(Player::PlayerKey key)
std::shared_ptr< Player > create_session(const Player::Configuration &)
void remove_player_for_key(const Player::PlayerKey &key)
std::shared_ptr< core::dbus::Property< core::IndicatorPower::IsWarning > > is_warning
std::shared_ptr< dbus::Object > uscreen_session
std::shared_ptr< Player > player_for_key(const Player::PlayerKey &key) const
std::shared_ptr< dbus::Object > indicator_power_session