1 #include <QMutexLocker>
4 #include <QElapsedTimer>
14 #define LOC QString("Pulse: ")
16 static inline bool IS_READY(pa_context_state arg)
18 return ((PA_CONTEXT_READY == arg) ||
19 (PA_CONTEXT_FAILED == arg) ||
20 (PA_CONTEXT_TERMINATED == arg));
25 QString ret =
"Unknown";
28 case PA_CONTEXT_UNCONNECTED: ret =
"Unconnected";
break;
29 case PA_CONTEXT_CONNECTING: ret =
"Connecting";
break;
30 case PA_CONTEXT_AUTHORIZING: ret =
"Authorizing";
break;
31 case PA_CONTEXT_SETTING_NAME: ret =
"Setting Name";
break;
32 case PA_CONTEXT_READY: ret =
"Ready!";
break;
33 case PA_CONTEXT_FAILED: ret =
"Failed";
break;
34 case PA_CONTEXT_TERMINATED: ret =
"Terminated";
break;
45 static QMutex s_globalLock;
46 QMutexLocker locker(&s_globalLock);
53 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Cleaning up PulseHandler");
60 static int s_iPulseRunning = -1;
61 static QElapsedTimer s_time;
65 if (s_time.isValid() && !s_time.hasExpired(30000))
71 if (
action == s_ePulseAction)
83 LOG(VB_AUDIO, LOG_INFO,
LOC +
"PulseAudio not running");
92 LOG(VB_AUDIO, LOG_INFO,
LOC +
"PulseHandler invalidated. Deleting.");
103 LOG(VB_AUDIO, LOG_INFO,
LOC +
"Created PulseHandler object");
108 LOG(VB_GENERAL, LOG_ERR,
LOC +
109 "Failed to create PulseHandler object");
135 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Callback: no handler.");
139 if (handler->m_ctx != ctx)
141 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Callback: handler/context mismatch.");
147 LOG(VB_GENERAL, LOG_ERR,
148 "Callback: returned handler is not the global handler.");
153 pa_context_state state = pa_context_get_state(ctx);
154 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"Callback: State changed %1->%2")
156 handler->m_ctxState = state;
167 LOG(VB_GENERAL, LOG_WARNING,
LOC +
168 "Received a late/unexpected operation callback. Ignoring.");
176 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Operation: no handler.");
180 if (handler->m_ctx != ctx)
182 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Operation: handler/context mismatch.");
188 LOG(VB_GENERAL, LOG_ERR,
LOC +
189 "Operation: returned handler is not the global handler.");
194 handler->m_pendingOperations--;
195 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"Operation: success %1 remaining %2")
196 .arg(success).arg(handler->m_pendingOperations));
203 LOG(VB_AUDIO, LOG_INFO,
LOC +
"Destroying PulseAudio handler");
208 pa_context_disconnect(
m_ctx);
209 pa_context_unref(
m_ctx);
236 m_loop = pa_mainloop_new();
239 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to get PulseAudio mainloop");
243 pa_mainloop_api *api = pa_mainloop_get_api(
m_loop);
246 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to get PulseAudio api");
250 if (pa_signal_init(api) != 0)
252 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to initialise signaling");
256 const char *client =
"mythtv";
257 m_ctx = pa_context_new(api, client);
260 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to create context");
265 m_thread = QThread::currentThread();
270 pa_context_connect(
m_ctx,
nullptr, PA_CONTEXT_NOAUTOSPAWN,
nullptr);
275 pa_mainloop_iterate(
m_loop, 0, &ret);
281 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Context not ready after 1000ms");
285 LOG(VB_AUDIO, LOG_INFO,
LOC +
"Initialised handler");
298 LOG(VB_AUDIO, LOG_WARNING,
LOC +
299 "PulseHandler called from a different thread");
301 QString
action = suspend ?
"suspend" :
"resume";
303 if (!pa_context_is_local(
m_ctx))
305 LOG(VB_GENERAL, LOG_ERR,
LOC +
306 "PulseAudio server is remote. No need to " +
action);
313 pa_operation *operation_sink =
314 pa_context_suspend_sink_by_index(
316 pa_operation_unref(operation_sink);
318 pa_operation *operation_source =
319 pa_context_suspend_source_by_index(
321 pa_operation_unref(operation_source);
328 pa_mainloop_iterate(
m_loop, 0, &ret);
341 LOG(VB_GENERAL, LOG_INFO,
LOC +
"PulseAudio " +
action +
" OK");