ckb-next  v0.2.8 at branch master
ckb-next driver for corsair devices
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
media_linux.cpp
Go to the documentation of this file.
1 #ifndef __APPLE__
2 
3 #include <QDateTime>
4 #include <QMutex>
5 #ifdef USE_LIBPULSE
6 #include <pulse/context.h>
7 #include <pulse/introspect.h>
8 #include <pulse/subscribe.h>
9 #include <pulse/thread-mainloop.h>
10 #endif
11 #include "media.h"
12 
14 
15 #ifdef USE_LIBPULSE
16 static pa_context* paContext = nullptr; //Context for communicating with Pulse Audio.
17 static qint64 reconnectTime = 0; //Time (in MSecs since epoch) to attempt reconnect.
18 static QString defaultSink; //Name of sink being checked if muted.
19 static QMutex mutex;
20 
21 static bool CheckPAOperation(pa_operation* operation){
22  if(operation == nullptr)
23  return false;
24 
25  pa_operation_unref(operation);
26  return true;
27 }
28 
29 static void SinkCallback(pa_context* context, const pa_sink_info* info, int eol, void* data){
30  QMutexLocker locker(&mutex);
31 
32  if(info == nullptr || info->name != defaultSink)
33  return;
34 
35  lastKnown = info->mute ? MUTED : UNMUTED;
36 }
37 
38 static void ServerCallback(pa_context* context, const pa_server_info* info, void* data){
39  QMutexLocker locker(&mutex);
40 
41  //Keep track of the default sink. This is the only one checked if muted. If
42  //the user changes this, SinkCallback will be called afterwards
43  //automatically. This will then check if the new default sink is muted.
44  defaultSink = info->default_sink_name;
45 }
46 
47 static void SubscribeCallback(pa_context* context, pa_subscription_event_type_t type, uint32_t index, void* data){
48  const pa_subscription_event_type_t eventFacility = static_cast<pa_subscription_event_type_t>(type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK);
49  if(eventFacility == PA_SUBSCRIPTION_EVENT_SINK){
50  //A sink was added or changed in some way. Get new info to see if it
51  //was muted.
52  if(!CheckPAOperation(pa_context_get_sink_info_by_index(context, index, SinkCallback, nullptr)))
53  qWarning("getMuteState(): pa_context_get_sink_info_by_index() error");
54  }
55  else if(eventFacility == PA_SUBSCRIPTION_EVENT_SERVER){
56  //Server settings were modified. Get new info to see if default sink
57  //was changed.
58  if(!CheckPAOperation(pa_context_get_server_info(context, ServerCallback, nullptr)))
59  qWarning("getMuteState(): pa_context_get_server_info() error");
60  }
61 }
62 
63 static void ContextStateCallback(pa_context* context, void* data){
64  if(context != paContext)
65  return;
66 
67  pa_context_state_t state = pa_context_get_state(context);
68  if(state == PA_CONTEXT_READY){
69  pa_context_set_subscribe_callback(context, SubscribeCallback, nullptr);
70  if(!CheckPAOperation(pa_context_subscribe(context,
71  static_cast<pa_subscription_mask_t>(PA_SUBSCRIPTION_MASK_SINK |
72  PA_SUBSCRIPTION_MASK_SERVER),
73  nullptr,
74  nullptr)))
75  qWarning("getMuteState(): pa_context_subscribe() error");
76 
77  //Find initial default device.
78  if(!CheckPAOperation(pa_context_get_server_info(context, ServerCallback, nullptr)))
79  qWarning("getMuteState(): pa_context_get_server_info() error");
80 
81  //Find initial mute state.
82  if(!CheckPAOperation(pa_context_get_sink_info_list(context, SinkCallback, nullptr)))
83  qWarning("getMuteState(): pa_context_get_sink_info_list() error");
84  }
85  else if(state == PA_CONTEXT_FAILED || state == PA_CONTEXT_TERMINATED){
86  QMutexLocker locker(&mutex);
87 
88  //Either we could not connect to the server or the on going connection
89  //was dropped.
90  if(paContext != nullptr){
91  pa_context_unref(paContext);
92  paContext = nullptr;
93 
94  //Try to reconnect again shortly.
95  reconnectTime = QDateTime::currentMSecsSinceEpoch() + 1000;
96  }
97  }
98 }
99 
100 #endif
101 
103 #ifdef USE_LIBPULSE
104  static pa_threaded_mainloop* mainLoop = nullptr;
105 
106  QMutexLocker locker(&mutex);
107 
108  //Setup main loop thread used for communicating with Pulse Audio. All
109  //communication is done asynchronously.
110  if(mainLoop == nullptr){
111  mainLoop = pa_threaded_mainloop_new();
112  if(mainLoop == nullptr)
113  return lastKnown;
114 
115  if(pa_threaded_mainloop_start(mainLoop) != 0){
116  pa_threaded_mainloop_free(mainLoop);
117  mainLoop = nullptr;
118  return lastKnown;
119  }
120  }
121 
122  //Connect to the local Pulse Audio server. It's usually running but a
123  //reconnect is attempted periodically whenever the connection fails or is
124  //terminated.
125  if(paContext == nullptr && QDateTime::currentMSecsSinceEpoch() >= reconnectTime){
126  pa_threaded_mainloop_lock(mainLoop);
127  {
128  pa_mainloop_api* api = pa_threaded_mainloop_get_api(mainLoop);
129  Q_ASSERT(api != nullptr);
130 
131  paContext = pa_context_new(api, "QPulse");
132  Q_ASSERT(paContext != nullptr);
133  pa_context_set_state_callback(paContext, &ContextStateCallback, nullptr);
134  pa_context_connect(paContext, nullptr, PA_CONTEXT_NOFAIL, nullptr);
135  }
136  pa_threaded_mainloop_unlock(mainLoop);
137  }
138 #endif
139 
140  return lastKnown;
141 }
142 
143 #endif
Definition: media.h:20
muteState getMuteState()
muteState
Definition: media.h:19
Definition: media.h:21
Definition: media.h:22
static muteState lastKnown
Definition: media_linux.cpp:13