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 namespace media = core::ubuntu::media;
34 
35 using namespace std;
36 
38 {
40  : resume_key(std::numeric_limits<std::uint32_t>::max()),
41  keep_alive(io_service)
42  {
43  bus = std::shared_ptr<dbus::Bus>(new dbus::Bus(core::dbus::WellKnownBus::session));
44  bus->install_executor(dbus::asio::make_executor(bus, io_service));
45  worker = std::move(std::thread([this]()
46  {
47  bus->run();
48  }));
49 
50  // Connect the property change signal that will allow media-hub to take appropriate action
51  // when the battery level reaches critical
52  auto stub_service = dbus::Service::use_service(bus, "com.canonical.indicator.power");
53  indicator_power_session = stub_service->object_for_path(dbus::types::ObjectPath("/com/canonical/indicator/power/Battery"));
54  power_level = indicator_power_session->get_property<core::IndicatorPower::PowerLevel>();
55  is_warning = indicator_power_session->get_property<core::IndicatorPower::IsWarning>();
56  }
57 
59  {
60  bus->stop();
61 
62  if (worker.joinable())
63  worker.join();
64  }
65 
66  // This holds the key of the multimedia role Player instance that was paused
67  // when the battery level reached 10% or 5%
69  std::thread worker;
70  dbus::Bus::Ptr bus;
71  boost::asio::io_service io_service;
72  boost::asio::io_service::work keep_alive;
73  std::shared_ptr<dbus::Object> indicator_power_session;
74  std::shared_ptr<core::dbus::Property<core::IndicatorPower::PowerLevel>> power_level;
75  std::shared_ptr<core::dbus::Property<core::IndicatorPower::IsWarning>> is_warning;
76 };
77 
79 {
80  cout << __PRETTY_FUNCTION__ << endl;
81 
82  d->power_level->changed().connect([this](const core::IndicatorPower::PowerLevel::ValueType &level)
83  {
84  // When the battery level hits 10% or 5%, pause all multimedia sessions.
85  // Playback will resume when the user clears the presented notification.
86  if (level == "low" || level == "very_low")
87  pause_all_multimedia_sessions();
88  });
89 
90  d->is_warning->changed().connect([this](const core::IndicatorPower::IsWarning::ValueType &notifying)
91  {
92  // If the low battery level notification is no longer being displayed,
93  // resume what the user was previously playing
94  if (!notifying)
95  resume_multimedia_session();
96  });
97 }
98 
100 {
101 }
102 
103 std::shared_ptr<media::Player> media::ServiceImplementation::create_session(
104  const media::Player::Configuration& conf)
105 {
106  auto player = std::make_shared<media::PlayerImplementation>(
107  conf.identity, conf.bus, conf.session, shared_from_this(), conf.key);
108 
109  auto key = conf.key;
110  player->on_client_disconnected().connect([this, key]()
111  {
112  // Call remove_player_for_key asynchronously otherwise deadlock can occur
113  // if called within this dispatcher context.
114  // remove_player_for_key can destroy the player instance which in turn
115  // destroys the "on_client_disconnected" signal whose destructor will wait
116  // until all dispatches are done
117  d->io_service.post([this, key]()
118  {
120  });
121  });
122 
123  return player;
124 }
125 
127 {
128  if (not has_player_for_key(key))
129  {
130  cerr << "Could not find Player by key: " << key << endl;
131  return;
132  }
133 
134  auto current_player = player_for_key(key);
135 
136  // We immediately make the player known as new current player.
137  if (current_player->audio_stream_role() == media::Player::multimedia)
139 
140  enumerate_players([current_player, key](const media::Player::PlayerKey& other_key, const std::shared_ptr<media::Player>& other_player)
141  {
142  // Only pause a Player if all of the following criteria are met:
143  // 1) currently playing
144  // 2) not the same player as the one passed in my key
145  // 3) new Player has an audio stream role set to multimedia
146  // 4) has an audio stream role set to multimedia
147  if (other_player->playback_status() == Player::playing &&
148  other_key != key &&
149  current_player->audio_stream_role() == media::Player::multimedia &&
150  other_player->audio_stream_role() == media::Player::multimedia)
151  {
152  cout << "Pausing Player with key: " << other_key << endl;
153  other_player->pause();
154  }
155  });
156 }
157 
158 void media::ServiceImplementation::pause_all_multimedia_sessions()
159 {
160  enumerate_players([this](const media::Player::PlayerKey& key, const std::shared_ptr<media::Player>& player)
161  {
162  if (player->playback_status() == Player::playing
163  && player->audio_stream_role() == media::Player::multimedia)
164  {
165  d->resume_key = key;
166  cout << "Will resume playback of Player with key: " << d->resume_key << endl;
167  player->pause();
168  }
169  });
170 }
171 
172 void media::ServiceImplementation::resume_multimedia_session()
173 {
174  if (not has_player_for_key(d->resume_key))
175  return;
176 
177  auto player = player_for_key(d->resume_key);
178 
179  if (player->playback_status() == Player::paused)
180  {
181  cout << "Resuming playback of Player with key: " << d->resume_key << endl;
182  player->play();
183  d->resume_key = std::numeric_limits<std::uint32_t>::max();
184  }
185 }
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
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)
boost::asio::io_service::work keep_alive
std::shared_ptr< core::dbus::Property< core::IndicatorPower::IsWarning > > is_warning
std::shared_ptr< Player > player_for_key(const Player::PlayerKey &key) const
std::shared_ptr< dbus::Object > indicator_power_session