You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用LGI从D-Bus读取属性?信号回调call_sync阻塞问题

问题:LGI订阅DBus信号后调用call_sync卡住无法获取PlaybackStatus

我尝试使用lgi订阅DBus信号并读取属性,代码如下:

local lgi = require("lgi")
local Gio, GLib = lgi.Gio, lgi.GLib

local name = "org.freedesktop.DBus"
local path = "/org/freedesktop/DBus"
local iface = "org.freedesktop.DBus"
Gio.DBusProxy.new_for_bus(
        Gio.BusType.SESSION,
        Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES,
        nil,
        name,
        path,
        iface,
        nil,
        function(bus_proxy)
            local conn = bus_proxy:get_connection()

            local sub_id = conn:signal_subscribe(
                    nil,
                    "org.freedesktop.DBus.Properties",
                    "PropertiesChanged",
                    "/org/mpris/MediaPlayer2",
                    nil,
                    Gio.DBusSignalFlags.NONE,
                    function()
                        local value = GLib.Variant.new(
                                "(ss)",
                                { "org.mpris.MediaPlayer2.Player",
                                  "PlaybackStatus" }
                        )
                        local playback_status = bus_proxy:call_sync(
                                "org.freedesktop.DBus.Properties.Get",
                                value,
                                Gio.DBusCallFlags.NONE,
                                -1,
                                nil
                        )
                        print(playback_status)
                    end)
        end,
        nil
)

我能确认信号已正确订阅,每次在Youtube上暂停或播放视频时回调都会触发,但bus_proxy:call_sync会卡住,始终无法返回PlaybackStatus的值。请问我哪里操作有误?


问题原因及解决方法

错误根源

你创建的bus_proxy是连接到org.freedesktop.DBus服务的代理,但org.freedesktop.DBus.Properties.Get方法需要调用MPRIS播放器服务(比如Chrome对应的org.mpris.MediaPlayer2.chrome),而不是DBus自身服务。用当前的bus_proxy发起请求,相当于给DBus服务发了它无法处理的MPRIS属性查询,导致调用卡住。

解决步骤

  1. 复用DBus连接对象发起调用:在信号回调里,使用conn:call_sync代替bus_proxy:call_sync,并指定正确的目标服务名、对象路径和接口。
  2. 从信号参数获取服务名:信号回调的第二个参数sender就是发送信号的MPRIS服务名,直接用它指定目标服务,兼容所有MPRIS播放器,无需硬编码。
  3. 优先从信号参数读取属性:PropertiesChanged信号本身会携带变化的属性,直接解析参数就能获取值,无需额外发起DBus调用,效率更高。

修正后的代码

local lgi = require("lgi")
local Gio, GLib = lgi.Gio, lgi.GLib

local name = "org.freedesktop.DBus"
local path = "/org/freedesktop/DBus"
local iface = "org.freedesktop.DBus"
Gio.DBusProxy.new_for_bus(
        Gio.BusType.SESSION,
        Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES,
        nil,
        name,
        path,
        iface,
        nil,
        function(bus_proxy)
            local conn = bus_proxy:get_connection()

            local sub_id = conn:signal_subscribe(
                    nil,
                    "org.freedesktop.DBus.Properties",
                    "PropertiesChanged",
                    "/org/mpris/MediaPlayer2",
                    nil,
                    Gio.DBusSignalFlags.NONE,
                    function(conn, sender, object_path, interface_name, signal_name, parameters)
                        -- 优先从信号参数读取变化的PlaybackStatus
                        local changed_props = parameters:get_child_value(1)
                        local playback_status_variant = changed_props:lookup_value("PlaybackStatus", nil)
                        if playback_status_variant then
                            print("PlaybackStatus:", playback_status_variant.value)
                            return
                        end

                        -- 信号未携带时,主动查询目标服务
                        local value = GLib.Variant.new(
                                "(ss)",
                                { "org.mpris.MediaPlayer2.Player", "PlaybackStatus" }
                        )
                        local playback_status = conn:call_sync(
                                sender,
                                "/org/mpris/MediaPlayer2",
                                "org.freedesktop.DBus.Properties",
                                "Get",
                                value,
                                GLib.VariantType.new("(v)"),
                                Gio.DBusCallFlags.NONE,
                                -1,
                                nil
                        )
                        -- 解析返回的Variant值
                        local status = playback_status:get_child_value(0).value
                        print("PlaybackStatus (查询):", status)
                    end)
        end,
        nil
)

内容的提问来源于stack exchange,提问作者arielnmz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 11:03:24