在 Qt 应用程序中集成 libpulse 的 pa_threaded_mainloop(破解 pavucontrol-qt)

问题描述 投票:0回答:1

我正在尝试让

pavucontrol-qt
(pvcq) 在 Mac 上正常工作(是的,PulseAudio 可以在那里工作)。

这里的障碍是 Qt/Mac 事件调度程序不是基于 GLib 的,因此 qvcq 当前使用

pa_glib_mainloop
的实现(以某种方式挂接到 Qt 的基于 GLib 的主循环)不起作用。 因此,我实现了替代代码路径,其中使用
pa_threaded_mainloop
来代替,并注意与 GUI 交互的所有 libpulse 回调通过
QMetaObject::invokeMethod(qApp, [=]() {qApp->pa_callback(...);}, Qt::BlockingQueuedConnection)
在主循环上执行此操作。

这可行,但应用程序还使用通过

g_idle_add()
安装的延迟事件处理程序,我似乎无法编写替代的基于 GLib 的实现。

我曾希望这将是一个简单的问题,即确保对

g_idle_add()
的呼叫是从
pa_threaded_mainloop
发出的,但到目前为止,我已经证明这个问题根本不简单。

调用所需

g_idle_add()
的函数在主线程上执行,因为GUI功能的一部分。所以我尝试的是创建一个继承
paThread
的自定义类的实例(
QObject
),并在初始启动期间当
QThread::currentThread()
返回 true 时的适当时刻将其发送到
pa_threaded_mainloop_in_thread()
。这一点是有效的,就像
QMetaObject::invokeMethod(paThread, [=]() { g_idle_add(idle_callback, data);}, Qt::QueuedConnection)
一样,但显然我忘记了它不会自动创建事件循环。因此,永远不会发出对
g_idle_add
的延迟调用。所以我尝试推导
QEventLoop
并调用
paThread->processEvents(AllEvents,1000);
paThread->wakeUp()
,这两个都没有任何效果。

然后我尝试通过直接从

g_idle_add
线程调用
pa_threaded_mainloop
来测试上面简单问题的前提,安装一个打印跟踪的回调。它似乎根本没有被调用,所以我开始意识到我一直在以错误的方式处理这个问题?

我没有看到任何通过 libpulse() 进入

g_idle_XXXX
的钩子,甚至没有一种获得“我们空闲”信号的方法。我是否应该将
g_idle
回调替换为由 Qt 本身定期调用的回调?

qt event-handling glib pulseaudio
1个回答
0
投票

嗯,哦。如果我先看看 libpulse 的实现,我就可以省去很多工作。

pa_threaded_mainloop
实现根本不基于 GLib。此外,由
idle_callback
调用的函数主要执行与 GUI 相关的操作,这些操作必须在主线程上执行 - 这就是我们需要注册回调时所在的位置。

由于当

pa_threaded_mainloop
空闲时无法执行回调,因此我决定在主事件调度程序空闲(或近似空闲)时调用它:

    if (!idle_source) {
        idle_source = 1;
        // Do the Qt equivalent of having idle_callback() execute when the event pump is idle:
        pvcApp->sendPostedEvents(nullptr, 0);
        pvcApp->eventDispatcher()->processEvents(QEventLoop::AllEvents);
        idle_callback(data); // resets idle_source
    }

我并不完全满意,因为整个回调设计显然是为了将 GUI 显示的多个方面的多个更新事件聚合到对更新整个 GUI 的函数的单个调用中。我无法检测到上面代码片段的任何递归调用,因此我不会丢失更新事件;我想这是最重要的事情。

© www.soinside.com 2019 - 2024. All rights reserved.