monitors/camera: fix camera device deduplication not working for certain devices.

Enhance the parsing logic to consider more than one device number. libcamera
devices some times use up multiple V4L2 devices, in this case libcamera should
be given a chance to enumerate the device.

Fixes #623
This commit is contained in:
Ashok Sidipotu
2024-04-08 13:27:28 +05:30
committed by George Kiagiadakis
parent ae983e6fd7
commit 06e11dc4be

View File

@@ -25,36 +25,92 @@ function mutils.find_duplicate (parent, id, property, value)
return false
end
function mutils.get_cam_data (self, dev_string)
local dev_num = tonumber (dev_string)
if not dev_num then
return
function get_cam_data(self, dev_id)
if not self.cam_data[dev_id] then
self.cam_data[dev_id] = {}
self.cam_data[dev_id]["libcamera"] = {}
self.cam_data[dev_id]["v4l2"] = {}
end
if not self.cam_data[dev_num] then
self.cam_data[dev_num] = {}
self.cam_data[dev_num]["libcamera"] = {}
self.cam_data[dev_num]["v4l2"] = {}
end
return self.cam_data[dev_num], dev_num
return self.cam_data[dev_id]
end
function mutils.clear_cam_data (self, dev_string)
local dev_num = tonumber (dev_string)
function parse_devids_get_cam_data(self, devids)
local dev_ids_json = Json.Raw(devids)
local dev_ids_table = {}
if dev_ids_json:is_array() then
dev_ids_table = dev_ids_json:parse()
else
-- to maintain the backward compatibility with earlier pipewire versions.
for dev_id_str in devids:gmatch("%S+") do
local dev_id = tonumber(dev_id_str)
if dev_id then
table.insert(dev_ids_table, dev_id)
end
end
end
local dev_num = nil
-- `device.devids` is a json array of device numbers
for _, dev_id_str in ipairs(dev_ids_table) do
local dev_id = tonumber(dev_id_str)
if not dev_id then
log:notice ("invalid device number")
return
end
log:debug ("Working on device " .. dev_id)
local dev_cam_data = get_cam_data (self, dev_id)
if not dev_num then
dev_num = dev_id
if #dev_ids_table > 1 then
-- libcam node can some times use more tha one V4L2 devices, in this
-- case, return the first device id and mark rest of the them as peers
-- to the first one.
log:debug ("Device " .. dev_id .. " uses multi V4L2 devices")
dev_cam_data.uses_multi_v4l2_devices = true
end
else
log:debug ("Device " .. dev_id .. " is peer to " .. dev_num)
dev_cam_data.peer_id = dev_num
end
end
if dev_num then
return self.cam_data[dev_num], dev_num
end
end
function mutils.clear_cam_data (self, dev_num)
local dev_cam_data = self.cam_data[dev_num]
if not dev_num then
return
end
if dev_cam_data.uses_multi_v4l2_devices then
for dev_id, cam_data_ in pairs(self.cam_data) do
if cam_data_.peer_id == dev_num then
log:debug("clear " .. dev_id .. " it is peer to " .. dev_num)
self.cam_data[dev_id] = nil
end
end
end
self.cam_data[dev_num] = nil
end
function mutils.create_cam_node (self, dev_num)
function mutils.create_cam_node(self, dev_num)
local api = nil
local cam_data = self:get_cam_data (dev_num)
local cam_data = get_cam_data (self, dev_num)
if cam_data["v4l2"].enum_status and cam_data["libcamera"].enum_status then
if cam_data.is_device_uvc then
if cam_data.uses_multi_v4l2_devices then
api = "libcamera"
elseif cam_data.peer_id ~= nil then
-- no need to create node for peer
log:notice ("timer expired for peer device " .. dev_num)
return
elseif cam_data.is_device_uvc then
api = "v4l2"
else
api = "libcamera"
@@ -63,8 +119,8 @@ function mutils.create_cam_node (self, dev_num)
api = cam_data["v4l2"].enum_status and "v4l2" or "libcamera"
end
log:info (string.format ("create \"%s\" node for device:%s%s", api,
cam_data.dev_path, (cam_data.is_device_uvc and "(uvc)" or "")))
log:info (string.format ("create \"%s\" node for device:%s", api,
cam_data.dev_path))
source = source or Plugin.find ("standard-event-source")
local e = source:call ("create-event", "create-" .. api .. "-device-node",
@@ -82,17 +138,21 @@ end
-- for a device, logic is based on the device number of the device given by both
-- the parties.
function mutils.register_cam_node (self, parent, id, factory, properties)
local cam_data, dev_num = self:get_cam_data (properties["device.devids"])
local api = properties["device.api"]
local dev_ids = properties["device.devids"]
log:debug(api .. " reported " .. dev_ids)
local cam_data, dev_num = parse_devids_get_cam_data(self, dev_ids)
if not cam_data then
log:notice (string.format ("device number invalid for %s device:%s",
log:notice (string.format ("device numbers invalid for %s device:%s",
api, properties["device.name"]))
return false
end
-- only v4l2 can give this info
if properties["api.v4l2.cap.driver"] == "uvcvideo" then
log:debug ("Device " .. dev_num .. " is a UVC device")
cam_data.is_device_uvc = true
end
@@ -107,6 +167,7 @@ function mutils.register_cam_node (self, parent, id, factory, properties)
-- cache info, it comes handy when creating node
cam_api_data.parent = parent
cam_api_data.id = id
cam_api_data.name = properties["device.name"]
cam_api_data.factory = factory
cam_api_data.properties = properties
@@ -114,11 +175,11 @@ function mutils.register_cam_node (self, parent, id, factory, properties)
if cam_api_data.enum_status and not cam_data[other_api].enum_status then
log:trace (string.format ("\"%s\" armed a timer for %d", api, dev_num))
cam_data.source = Core.timeout_add (
Settings.get_int ("monitor.camera-discovery-timeout"), function()
log:trace (string.format ("\"%s\" armed timer expired for %d", api, dev_num))
self:create_cam_node (dev_num)
cam_data.source = nil
end)
Settings.get_int ("monitor.camera-discovery-timeout"), function()
log:trace (string.format ("\"%s\" armed timer expired for %d", api, dev_num))
self:create_cam_node (dev_num)
cam_data.source = nil
end)
elseif cam_data.source then
log:trace (string.format ("\"%s\" disarmed timer for %d", api, dev_num))
cam_data.source:destroy ()