1 #include <QMutexLocker> 13 #define LOC QString("Pulse: ") 15 #define IS_READY(arg) ((PA_CONTEXT_READY == (arg)) || \ 16 (PA_CONTEXT_FAILED == (arg)) || \ 17 (PA_CONTEXT_TERMINATED == (arg))) 21 QString ret =
"Unknown";
24 case PA_CONTEXT_UNCONNECTED: ret =
"Unconnected";
break;
25 case PA_CONTEXT_CONNECTING: ret =
"Connecting";
break;
26 case PA_CONTEXT_AUTHORIZING: ret =
"Authorizing";
break;
27 case PA_CONTEXT_SETTING_NAME: ret =
"Setting Name";
break;
28 case PA_CONTEXT_READY: ret =
"Ready!";
break;
29 case PA_CONTEXT_FAILED: ret =
"Failed";
break;
30 case PA_CONTEXT_TERMINATED: ret =
"Terminated";
break;
41 static QMutex global_lock;
42 QMutexLocker locker(&global_lock);
49 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Cleaning up PulseHandler");
56 static int s_iPulseRunning = -1;
61 if (!s_time.isNull() && s_time.elapsed() < 30000)
67 if (
action == s_ePulseAction)
79 LOG(VB_AUDIO, LOG_INFO,
LOC +
"PulseAudio not running");
88 LOG(VB_AUDIO, LOG_INFO,
LOC +
"PulseHandler invalidated. Deleting.");
99 LOG(VB_AUDIO, LOG_INFO,
LOC +
"Created PulseHandler object");
104 LOG(VB_GENERAL, LOG_ERR,
LOC +
105 "Failed to create PulseHandler object");
129 PulseHandler *handler = static_cast<PulseHandler*>(userdata);
132 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Callback: no handler.");
136 if (handler->
m_ctx != ctx)
138 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Callback: handler/context mismatch.");
144 LOG(VB_GENERAL, LOG_ERR,
145 "Callback: returned handler is not the global handler.");
150 pa_context_state state = pa_context_get_state(ctx);
151 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"Callback: State changed %1->%2")
165 LOG(VB_GENERAL, LOG_WARNING,
LOC +
166 "Received a late/unexpected operation callback. Ignoring.");
171 PulseHandler *handler = static_cast<PulseHandler*>(userdata);
174 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Operation: no handler.");
178 if (handler->
m_ctx != ctx)
180 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Operation: handler/context mismatch.");
186 LOG(VB_GENERAL, LOG_ERR,
LOC +
187 "Operation: returned handler is not the global handler.");
193 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"Operation: success %1 remaining %2")
198 : m_ctx_state(PA_CONTEXT_UNCONNECTED), m_ctx(nullptr), m_pending_operations(0),
199 m_loop(nullptr), m_initialised(
false), m_valid(
false),
208 LOG(VB_AUDIO, LOG_INFO,
LOC +
"Destroying PulseAudio handler");
213 pa_context_disconnect(
m_ctx);
214 pa_context_unref(
m_ctx);
241 m_loop = pa_mainloop_new();
244 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to get PulseAudio mainloop");
248 pa_mainloop_api *api = pa_mainloop_get_api(
m_loop);
251 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to get PulseAudio api");
255 if (pa_signal_init(api) != 0)
257 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to initialise signaling");
261 const char *client =
"mythtv";
262 m_ctx = pa_context_new(api, client);
265 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to create context");
270 m_thread = QThread::currentThread();
275 pa_context_connect(
m_ctx,
nullptr, PA_CONTEXT_NOAUTOSPAWN,
nullptr);
280 pa_mainloop_iterate(
m_loop, 0, &ret);
286 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Context not ready after 1000ms");
290 LOG(VB_AUDIO, LOG_INFO,
LOC +
"Initialised handler");
303 LOG(VB_AUDIO, LOG_WARNING,
LOC +
304 "PulseHandler called from a different thread");
306 QString
action = suspend ?
"suspend" :
"resume";
308 if (!pa_context_is_local(
m_ctx))
310 LOG(VB_GENERAL, LOG_ERR,
LOC +
311 "PulseAudio server is remote. No need to " +
action);
318 pa_operation *operation_sink =
319 pa_context_suspend_sink_by_index(
321 pa_operation_unref(operation_sink);
323 pa_operation *operation_source =
324 pa_context_suspend_source_by_index(
326 pa_operation_unref(operation_source);
333 pa_mainloop_iterate(
m_loop, 0, &ret);
346 LOG(VB_GENERAL, LOG_INFO,
LOC +
"PulseAudio " +
action +
" OK");
static void OperationCallback(pa_context *ctx, int success, void *userdata)
static bool g_pulseHandlerActive
bool IsPulseAudioRunning(void)
Is A/V Sync destruction daemon is running on this host?
static void StatusCallback(pa_context *ctx, void *userdata)
static bool Suspend(enum PulseAction action)
#define LOG(_MASK_, _LEVEL_, _STRING_)
bool is_current_thread(MThread *thread)
Use this to determine if you are in the named thread.
bool SuspendInternal(bool suspend)
pa_context_state m_ctx_state
static QString state_to_string(pa_context_state state)
static PulseHandler * g_pulseHandler