7#include <QElapsedTimer>
15#define LOC QString("Pulse: ")
17static inline bool IS_READY(pa_context_state arg)
19 return ((PA_CONTEXT_READY == arg) ||
20 (PA_CONTEXT_FAILED == arg) ||
21 (PA_CONTEXT_TERMINATED == arg));
26 QString ret =
"Unknown";
29 case PA_CONTEXT_UNCONNECTED: ret =
"Unconnected";
break;
30 case PA_CONTEXT_CONNECTING: ret =
"Connecting";
break;
31 case PA_CONTEXT_AUTHORIZING: ret =
"Authorizing";
break;
32 case PA_CONTEXT_SETTING_NAME: ret =
"Setting Name";
break;
33 case PA_CONTEXT_READY: ret =
"Ready!";
break;
34 case PA_CONTEXT_FAILED: ret =
"Failed";
break;
35 case PA_CONTEXT_TERMINATED: ret =
"Terminated";
break;
46 static QMutex s_globalLock;
47 QMutexLocker locker(&s_globalLock);
54 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Cleaning up PulseHandler");
61 static int s_iPulseRunning = -1;
62 static QElapsedTimer s_time;
66 if (s_time.isValid() && !s_time.hasExpired(30000))
72 if (
action == s_ePulseAction)
84 LOG(VB_AUDIO, LOG_INFO,
LOC +
"PulseAudio not running");
93 LOG(VB_AUDIO, LOG_INFO,
LOC +
"PulseHandler invalidated. Deleting.");
104 LOG(VB_AUDIO, LOG_INFO,
LOC +
"Created PulseHandler object");
109 LOG(VB_GENERAL, LOG_ERR,
LOC +
110 "Failed to create PulseHandler object");
136 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Callback: no handler.");
140 if (handler->m_ctx != ctx)
142 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Callback: handler/context mismatch.");
148 LOG(VB_GENERAL, LOG_ERR,
149 "Callback: returned handler is not the global handler.");
154 pa_context_state state = pa_context_get_state(ctx);
155 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"Callback: State changed %1->%2")
157 handler->m_ctxState = state;
168 LOG(VB_GENERAL, LOG_WARNING,
LOC +
169 "Received a late/unexpected operation callback. Ignoring.");
177 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Operation: no handler.");
181 if (handler->m_ctx != ctx)
183 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Operation: handler/context mismatch.");
189 LOG(VB_GENERAL, LOG_ERR,
LOC +
190 "Operation: returned handler is not the global handler.");
195 handler->m_pendingOperations--;
196 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"Operation: success %1 remaining %2")
197 .arg(success).arg(handler->m_pendingOperations));
204 LOG(VB_AUDIO, LOG_INFO,
LOC +
"Destroying PulseAudio handler");
209 pa_context_disconnect(
m_ctx);
210 pa_context_unref(
m_ctx);
237 m_loop = pa_mainloop_new();
240 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to get PulseAudio mainloop");
244 pa_mainloop_api *api = pa_mainloop_get_api(
m_loop);
247 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to get PulseAudio api");
251 if (pa_signal_init(api) != 0)
253 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to initialise signaling");
257 const char *client =
"mythtv";
258 m_ctx = pa_context_new(api, client);
261 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to create context");
266 m_thread = QThread::currentThread();
271 pa_context_connect(
m_ctx,
nullptr, PA_CONTEXT_NOAUTOSPAWN,
nullptr);
276 pa_mainloop_iterate(
m_loop, 0, &ret);
277 std::this_thread::sleep_for(10ms);
282 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Context not ready after 1000ms");
286 LOG(VB_AUDIO, LOG_INFO,
LOC +
"Initialised handler");
299 LOG(VB_AUDIO, LOG_WARNING,
LOC +
300 "PulseHandler called from a different thread");
302 QString
action = suspend ?
"suspend" :
"resume";
304 if (!pa_context_is_local(
m_ctx))
306 LOG(VB_GENERAL, LOG_ERR,
LOC +
307 "PulseAudio server is remote. No need to " +
action);
314 pa_operation *operation_sink =
315 pa_context_suspend_sink_by_index(
317 pa_operation_unref(operation_sink);
319 pa_operation *operation_source =
320 pa_context_suspend_source_by_index(
322 pa_operation_unref(operation_source);
329 pa_mainloop_iterate(
m_loop, 0, &ret);
330 std::this_thread::sleep_for(10ms);
342 LOG(VB_GENERAL, LOG_INFO,
LOC +
"PulseAudio " +
action +
" OK");
static void OperationCallback(pa_context *ctx, int success, void *userdata)
static bool IS_READY(pa_context_state arg)
static void StatusCallback(pa_context *ctx, void *userdata)
static QString state_to_string(pa_context_state state)
pa_context_state m_ctxState
static PulseHandler * g_pulseHandler
static bool g_pulseHandlerActive
static bool Suspend(enum PulseAction action)
PulseHandler(void)=default
bool SuspendInternal(bool suspend)
bool is_current_thread(MThread *thread)
Use this to determine if you are in the named thread.
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
bool IsPulseAudioRunning(void)
Is A/V Sync destruction daemon is running on this host?
void(*)(int, void *) StatusCallback