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:

committed by
George Kiagiadakis

parent
ae983e6fd7
commit
06e11dc4be
@@ -25,36 +25,92 @@ function mutils.find_duplicate (parent, id, property, value)
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
function mutils.get_cam_data (self, dev_string)
|
function get_cam_data(self, dev_id)
|
||||||
local dev_num = tonumber (dev_string)
|
if not self.cam_data[dev_id] then
|
||||||
if not dev_num then
|
self.cam_data[dev_id] = {}
|
||||||
return
|
self.cam_data[dev_id]["libcamera"] = {}
|
||||||
|
self.cam_data[dev_id]["v4l2"] = {}
|
||||||
end
|
end
|
||||||
|
return self.cam_data[dev_id]
|
||||||
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
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function mutils.clear_cam_data (self, dev_string)
|
function parse_devids_get_cam_data(self, devids)
|
||||||
local dev_num = tonumber (dev_string)
|
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
|
if not dev_num then
|
||||||
return
|
return
|
||||||
end
|
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
|
self.cam_data[dev_num] = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
function mutils.create_cam_node (self, dev_num)
|
function mutils.create_cam_node(self, dev_num)
|
||||||
local api = nil
|
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["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"
|
api = "v4l2"
|
||||||
else
|
else
|
||||||
api = "libcamera"
|
api = "libcamera"
|
||||||
@@ -63,8 +119,8 @@ function mutils.create_cam_node (self, dev_num)
|
|||||||
api = cam_data["v4l2"].enum_status and "v4l2" or "libcamera"
|
api = cam_data["v4l2"].enum_status and "v4l2" or "libcamera"
|
||||||
end
|
end
|
||||||
|
|
||||||
log:info (string.format ("create \"%s\" node for device:%s%s", api,
|
log:info (string.format ("create \"%s\" node for device:%s", api,
|
||||||
cam_data.dev_path, (cam_data.is_device_uvc and "(uvc)" or "")))
|
cam_data.dev_path))
|
||||||
|
|
||||||
source = source or Plugin.find ("standard-event-source")
|
source = source or Plugin.find ("standard-event-source")
|
||||||
local e = source:call ("create-event", "create-" .. api .. "-device-node",
|
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
|
-- for a device, logic is based on the device number of the device given by both
|
||||||
-- the parties.
|
-- the parties.
|
||||||
function mutils.register_cam_node (self, parent, id, factory, properties)
|
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 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
|
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"]))
|
api, properties["device.name"]))
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
-- only v4l2 can give this info
|
-- only v4l2 can give this info
|
||||||
if properties["api.v4l2.cap.driver"] == "uvcvideo" then
|
if properties["api.v4l2.cap.driver"] == "uvcvideo" then
|
||||||
|
log:debug ("Device " .. dev_num .. " is a UVC device")
|
||||||
cam_data.is_device_uvc = true
|
cam_data.is_device_uvc = true
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -107,6 +167,7 @@ function mutils.register_cam_node (self, parent, id, factory, properties)
|
|||||||
-- cache info, it comes handy when creating node
|
-- cache info, it comes handy when creating node
|
||||||
cam_api_data.parent = parent
|
cam_api_data.parent = parent
|
||||||
cam_api_data.id = id
|
cam_api_data.id = id
|
||||||
|
cam_api_data.name = properties["device.name"]
|
||||||
cam_api_data.factory = factory
|
cam_api_data.factory = factory
|
||||||
cam_api_data.properties = properties
|
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
|
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))
|
log:trace (string.format ("\"%s\" armed a timer for %d", api, dev_num))
|
||||||
cam_data.source = Core.timeout_add (
|
cam_data.source = Core.timeout_add (
|
||||||
Settings.get_int ("monitor.camera-discovery-timeout"), function()
|
Settings.get_int ("monitor.camera-discovery-timeout"), function()
|
||||||
log:trace (string.format ("\"%s\" armed timer expired for %d", api, dev_num))
|
log:trace (string.format ("\"%s\" armed timer expired for %d", api, dev_num))
|
||||||
self:create_cam_node (dev_num)
|
self:create_cam_node (dev_num)
|
||||||
cam_data.source = nil
|
cam_data.source = nil
|
||||||
end)
|
end)
|
||||||
elseif cam_data.source then
|
elseif cam_data.source then
|
||||||
log:trace (string.format ("\"%s\" disarmed timer for %d", api, dev_num))
|
log:trace (string.format ("\"%s\" disarmed timer for %d", api, dev_num))
|
||||||
cam_data.source:destroy ()
|
cam_data.source:destroy ()
|
||||||
|
Reference in New Issue
Block a user