30 #include <boost/asio.hpp>
39 #include <pulse/pulseaudio.h>
43 #include <hybris/media/media_recorder_layer.h>
52 : resume_key(
std::numeric_limits<
std::uint32_t>::max()),
53 keep_alive(io_service),
55 pulse_mainloop_api(nullptr),
56 pulse_context(nullptr),
57 headphones_connected(false),
58 a2dp_connected(false),
62 bus = std::shared_ptr<dbus::Bus>(
new dbus::Bus(core::dbus::WellKnownBus::session));
63 bus->install_executor(dbus::asio::make_executor(bus, io_service));
64 worker = std::move(std::thread([
this]()
70 pulse_mainloop =
nullptr;
71 pulse_worker = std::move(std::thread([
this]()
73 std::unique_lock<std::mutex> lk(pulse_mutex);
76 if (pulse_mainloop !=
nullptr || pulse_context !=
nullptr)
79 if (pulse_context !=
nullptr)
81 pa_threaded_mainloop_lock(pulse_mainloop);
83 o = pa_context_drain(pulse_context,
84 [](pa_context *context,
void *userdata)
89 pa_threaded_mainloop_signal(p->
mainloop(), 0);
94 while (pa_operation_get_state(o) == PA_OPERATION_RUNNING)
95 pa_threaded_mainloop_wait(pulse_mainloop);
97 pa_operation_unref(o);
100 pa_context_set_state_callback(pulse_context, NULL, NULL);
101 pa_context_set_subscribe_callback(pulse_context, NULL, NULL);
102 pa_context_disconnect(pulse_context);
103 pa_context_unref(pulse_context);
104 pulse_context =
nullptr;
105 pa_threaded_mainloop_unlock(pulse_mainloop);
109 if (pulse_mainloop ==
nullptr)
111 pulse_mainloop = pa_threaded_mainloop_new();
113 if (pa_threaded_mainloop_start(pulse_mainloop) != 0)
115 std::cerr <<
"Unable to start pulseaudio mainloop, audio output detection will not function" << std::endl;
116 pa_threaded_mainloop_free(pulse_mainloop);
117 pulse_mainloop =
nullptr;
122 create_pulse_context();
123 }
while (pulse_context ==
nullptr);
132 auto stub_service = dbus::Service::use_service(bus,
"com.canonical.indicator.power");
133 indicator_power_session = stub_service->object_for_path(dbus::types::ObjectPath(
"/com/canonical/indicator/power/Battery"));
138 auto bus = std::shared_ptr<dbus::Bus>(
new dbus::Bus(core::dbus::WellKnownBus::system));
139 bus->install_executor(dbus::asio::make_executor(bus));
141 auto uscreen_stub_service = dbus::Service::use_service(bus, dbus::traits::Service<core::UScreen>::interface_name());
142 uscreen_session = uscreen_stub_service->object_for_path(dbus::types::ObjectPath(
"/com/canonical/Unity/Screen"));
144 observer = android_media_recorder_observer_new();
145 android_media_recorder_observer_set_cb(observer, &Private::media_recording_started_callback,
this);
150 release_pulse_context();
152 if (pulse_mainloop !=
nullptr)
154 pa_threaded_mainloop_stop(pulse_mainloop);
155 pa_threaded_mainloop_free(pulse_mainloop);
156 pulse_mainloop =
nullptr;
161 if (worker.joinable())
164 if (pulse_worker.joinable())
170 if (uscreen_session ==
nullptr)
179 if (result.is_error())
180 throw std::runtime_error(result.error().print());
181 disp_cookie = result.value();
185 if (disp_cookie != -1)
187 timeout(4000,
true, [
this](){
189 this->disp_cookie = -1;
197 if (context ==
nullptr)
200 auto p =
static_cast<Private*
>(context);
206 return pulse_mainloop;
213 if (ports !=
nullptr && n_ports > 0 && name !=
nullptr)
215 for (uint32_t i=0; i<n_ports; i++)
217 if (strstr(ports[i]->name, name) !=
nullptr && ports[i]->available != PA_PORT_AVAILABLE_NO)
230 const pa_operation *o = pa_context_get_card_info_by_index(pulse_context, primary_idx,
231 [](pa_context *context,
const pa_card_info *info,
int eol,
void *userdata)
236 if (info ==
nullptr || userdata ==
nullptr)
243 std::cout <<
"Wired headphones connected" << std::endl;
248 std::cout <<
"Wired headphones disconnected" << std::endl;
259 if (std::get<0>(active_sink) == -1)
262 if (headphones_connected)
266 if (index == primary_idx)
272 const pa_operation *o = pa_context_get_sink_info_by_name(pulse_context, name,
273 [](pa_context *context,
const pa_sink_info *i,
int eol,
void *userdata)
281 std::tuple<uint32_t, uint32_t, std::string> new_sink(std::make_tuple(i->index, i->card, i->name));
283 printf(
"pulsesink: active_sink=('%s',%d,%d) -> ('%s',%d,%d)\n",
285 std::get<1>(p->
active_sink), i->name, i->index, i->card);
296 const pa_operation *o = pa_context_get_server_info(pulse_context,
297 [](pa_context *context,
const pa_server_info *i,
void *userdata)
302 if (i->default_sink_name != std::get<2>(p->
active_sink))
312 if (pulse_context !=
nullptr)
315 active_sink = std::make_tuple(-1, -1,
"");
317 bool keep_going =
true, ok =
true;
319 pulse_mainloop_api = pa_threaded_mainloop_get_api(pulse_mainloop);
320 pa_threaded_mainloop_lock(pulse_mainloop);
322 pulse_context = pa_context_new(pulse_mainloop_api,
"MediaHubPulseContext");
323 pa_context_set_state_callback(pulse_context,
324 [](pa_context *context,
void *userdata)
329 pa_threaded_mainloop_signal(p->
mainloop(), 0);
332 if (pulse_context ==
nullptr)
334 std::cerr <<
"Unable to create new pulseaudio context" << std::endl;
335 pa_threaded_mainloop_unlock(pulse_mainloop);
339 pa_context_connect(pulse_context,
nullptr, pa_context_flags_t((
int) PA_CONTEXT_NOAUTOSPAWN | (
int) PA_CONTEXT_NOFAIL),
nullptr);
340 pa_threaded_mainloop_wait(pulse_mainloop);
344 switch (pa_context_get_state(pulse_context))
346 case PA_CONTEXT_CONNECTING:
347 case PA_CONTEXT_AUTHORIZING:
348 case PA_CONTEXT_SETTING_NAME:
351 case PA_CONTEXT_READY:
352 std::cout <<
"Pulseaudio connection established." << std::endl;
356 case PA_CONTEXT_FAILED:
357 case PA_CONTEXT_TERMINATED:
363 std::cerr <<
"Pulseaudio connection failure: " << pa_strerror(pa_context_errno(pulse_context));
369 pa_threaded_mainloop_wait(pulse_mainloop);
374 pa_context_set_state_callback(pulse_context,
375 [](pa_context *context,
void *userdata)
381 switch (pa_context_get_state(context))
383 case PA_CONTEXT_FAILED:
384 case PA_CONTEXT_TERMINATED:
393 pa_context_get_sink_info_by_name(pulse_context,
"sink.primary",
394 [](pa_context *context,
const pa_sink_info *i,
int eol,
void *userdata)
406 update_active_sink();
408 pa_context_set_subscribe_callback(pulse_context,
409 [](pa_context *context, pa_subscription_event_type_t t, uint32_t idx,
void *userdata)
414 if (userdata ==
nullptr)
418 if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK)
423 pa_context_subscribe(pulse_context, PA_SUBSCRIPTION_MASK_SINK,
nullptr,
this);
427 std::cerr <<
"Connection to pulseaudio failed or was dropped." << std::endl;
428 pa_context_unref(pulse_context);
429 pulse_context =
nullptr;
432 pa_threaded_mainloop_unlock(pulse_mainloop);
437 if (pulse_context !=
nullptr)
439 pa_threaded_mainloop_lock(pulse_mainloop);
440 pa_context_disconnect(pulse_context);
441 pa_context_unref(pulse_context);
442 pa_threaded_mainloop_unlock(pulse_mainloop);
443 pulse_context =
nullptr;
455 std::shared_ptr<core::dbus::Property<core::IndicatorPower::PowerLevel>>
power_level;
456 std::shared_ptr<core::dbus::Property<core::IndicatorPower::IsWarning>>
is_warning;
467 std::condition_variable
pcv;
486 if (level ==
"low" || level ==
"very_low")
487 pause_all_multimedia_sessions();
495 resume_multimedia_session();
498 d->pause_playback.connect([
this]()
500 std::cout <<
"Got pause_playback signal, pausing all multimedia sessions" << std::endl;
501 pause_all_multimedia_sessions();
507 pause_all_multimedia_sessions();
510 resume_paused_multimedia_sessions();
521 const media::Player::Configuration& conf)
523 auto player = std::make_shared<media::PlayerImplementation>(
524 conf.identity, conf.bus, conf.session, shared_from_this(), conf.key);
527 player->on_client_disconnected().connect([
this, key]()
534 d->io_service.post([
this, key]()
547 cerr <<
"Could not find Player by key: " << key << endl;
569 cout <<
"Pausing Player with key: " << other_key << endl;
570 other_player->pause();
575 void media::ServiceImplementation::pause_all_multimedia_sessions()
582 d->paused_sessions.push_back(key);
588 void media::ServiceImplementation::resume_paused_multimedia_sessions()
594 d->paused_sessions.clear();
597 void media::ServiceImplementation::resume_multimedia_session()
606 cout <<
"Resuming playback of Player with key: " << d->resume_key << endl;
608 d->resume_key = std::numeric_limits<std::uint32_t>::max();