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)
182 if (result.is_error())
183 throw std::runtime_error(result.error().print());
184 disp_cookie = result.value();
188 if (disp_cookie != -1)
190 timeout(4000,
true, [
this](){
192 this->disp_cookie = -1;
200 if (context ==
nullptr)
203 auto p =
static_cast<Private*
>(context);
209 return pulse_mainloop;
216 if (ports !=
nullptr && n_ports > 0 && name !=
nullptr)
218 for (uint32_t i=0; i<n_ports; i++)
220 if (strstr(ports[i]->name, name) !=
nullptr && ports[i]->available != PA_PORT_AVAILABLE_NO)
233 const pa_operation *o = pa_context_get_card_info_by_index(pulse_context, primary_idx,
234 [](pa_context *context,
const pa_card_info *info,
int eol,
void *userdata)
239 if (info ==
nullptr || userdata ==
nullptr)
246 std::cout <<
"Wired headphones connected" << std::endl;
251 std::cout <<
"Wired headphones disconnected" << std::endl;
262 if (std::get<0>(active_sink) == -1)
265 if (headphones_connected)
269 if (index == primary_idx)
275 const pa_operation *o = pa_context_get_sink_info_by_name(pulse_context, name,
276 [](pa_context *context,
const pa_sink_info *i,
int eol,
void *userdata)
284 std::tuple<uint32_t, uint32_t, std::string> new_sink(std::make_tuple(i->index, i->card, i->name));
286 printf(
"pulsesink: active_sink=('%s',%d,%d) -> ('%s',%d,%d)\n",
288 std::get<1>(p->
active_sink), i->name, i->index, i->card);
299 const pa_operation *o = pa_context_get_server_info(pulse_context,
300 [](pa_context *context,
const pa_server_info *i,
void *userdata)
305 if (i->default_sink_name != std::get<2>(p->
active_sink))
315 if (pulse_context !=
nullptr)
318 active_sink = std::make_tuple(-1, -1,
"");
320 bool keep_going =
true, ok =
true;
322 pulse_mainloop_api = pa_threaded_mainloop_get_api(pulse_mainloop);
323 pa_threaded_mainloop_lock(pulse_mainloop);
325 pulse_context = pa_context_new(pulse_mainloop_api,
"MediaHubPulseContext");
326 pa_context_set_state_callback(pulse_context,
327 [](pa_context *context,
void *userdata)
332 pa_threaded_mainloop_signal(p->
mainloop(), 0);
335 if (pulse_context ==
nullptr)
337 std::cerr <<
"Unable to create new pulseaudio context" << std::endl;
338 pa_threaded_mainloop_unlock(pulse_mainloop);
342 pa_context_connect(pulse_context,
nullptr, pa_context_flags_t((
int) PA_CONTEXT_NOAUTOSPAWN | (
int) PA_CONTEXT_NOFAIL),
nullptr);
343 pa_threaded_mainloop_wait(pulse_mainloop);
347 switch (pa_context_get_state(pulse_context))
349 case PA_CONTEXT_CONNECTING:
350 case PA_CONTEXT_AUTHORIZING:
351 case PA_CONTEXT_SETTING_NAME:
354 case PA_CONTEXT_READY:
355 std::cout <<
"Pulseaudio connection established." << std::endl;
359 case PA_CONTEXT_FAILED:
360 case PA_CONTEXT_TERMINATED:
366 std::cerr <<
"Pulseaudio connection failure: " << pa_strerror(pa_context_errno(pulse_context));
372 pa_threaded_mainloop_wait(pulse_mainloop);
377 pa_context_set_state_callback(pulse_context,
378 [](pa_context *context,
void *userdata)
384 switch (pa_context_get_state(context))
386 case PA_CONTEXT_FAILED:
387 case PA_CONTEXT_TERMINATED:
396 pa_context_get_sink_info_by_name(pulse_context,
"sink.primary",
397 [](pa_context *context,
const pa_sink_info *i,
int eol,
void *userdata)
409 update_active_sink();
411 pa_context_set_subscribe_callback(pulse_context,
412 [](pa_context *context, pa_subscription_event_type_t t, uint32_t idx,
void *userdata)
417 if (userdata ==
nullptr)
421 if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK)
426 pa_context_subscribe(pulse_context, PA_SUBSCRIPTION_MASK_SINK,
nullptr,
this);
430 std::cerr <<
"Connection to pulseaudio failed or was dropped." << std::endl;
431 pa_context_unref(pulse_context);
432 pulse_context =
nullptr;
435 pa_threaded_mainloop_unlock(pulse_mainloop);
440 if (pulse_context !=
nullptr)
442 pa_threaded_mainloop_lock(pulse_mainloop);
443 pa_context_disconnect(pulse_context);
444 pa_context_unref(pulse_context);
445 pa_threaded_mainloop_unlock(pulse_mainloop);
446 pulse_context =
nullptr;
458 std::shared_ptr<core::dbus::Property<core::IndicatorPower::PowerLevel>>
power_level;
459 std::shared_ptr<core::dbus::Property<core::IndicatorPower::IsWarning>>
is_warning;
470 std::condition_variable
pcv;
490 if (level ==
"low" || level ==
"very_low")
491 pause_all_multimedia_sessions();
499 resume_multimedia_session();
502 d->pause_playback.connect([
this]()
504 std::cout <<
"Got pause_playback signal, pausing all multimedia sessions" << std::endl;
505 pause_all_multimedia_sessions();
511 std::cout <<
"Got call started signal, pausing all multimedia sessions" << std::endl;
512 pause_all_multimedia_sessions();
515 std::cout <<
"Got call ended signal, resuming paused multimedia sessions" << std::endl;
517 resume_paused_multimedia_sessions(
false);
528 const media::Player::Configuration& conf)
530 auto player = std::make_shared<media::PlayerImplementation>(
531 conf.identity, conf.bus, conf.session, shared_from_this(), conf.key);
534 player->on_client_disconnected().connect([
this, key]()
541 d->io_service.post([
this, key]()
557 return std::shared_ptr<media::Player>();
563 return std::shared_ptr<media::Player>();
570 cerr <<
"Could not find Player by key: " << key << endl;
592 cout <<
"Pausing Player with key: " << other_key << endl;
593 other_player->pause();
598 void media::ServiceImplementation::pause_all_multimedia_sessions()
605 d->paused_sessions.push_back(key);
606 std::cout <<
"Pausing Player with key: " << key << std::endl;
612 void media::ServiceImplementation::resume_paused_multimedia_sessions(
bool resume_video_sessions)
614 std::for_each(d->paused_sessions.begin(), d->paused_sessions.end(), [
this, resume_video_sessions](
const media::Player::PlayerKey& key) {
617 if (resume_video_sessions || player->is_audio_source())
620 std::cout <<
"Not auto-resuming video playback session." << std::endl;
623 d->paused_sessions.clear();
626 void media::ServiceImplementation::resume_multimedia_session()
635 cout <<
"Resuming playback of Player with key: " << d->resume_key << endl;
637 d->resume_key = std::numeric_limits<std::uint32_t>::max();