PipeWire camera: filter out devices with no capabilities

Filter out devices that do not support any format supported by WebRTC.
This will for example be IR cameras that show as duplicated in the list
of cameras, but support only GRAY8 format and for that reason do not
work at all.

Bug: webrtc:42225999
Change-Id: Ic2905bc66b55c3f48b49ff4097167f10d17ad656
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/358864
Commit-Queue: Jan Grulich <grulja@gmail.com>
Reviewed-by: Andreas Pehrson <apehrson@mozilla.com>
Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#42785}
This commit is contained in:
Jan Grulich 2024-08-14 17:32:11 +02:00 committed by WebRTC LUCI CQ
parent db561d4174
commit b4aba7834e
3 changed files with 57 additions and 27 deletions

View File

@ -55,31 +55,31 @@ int32_t DeviceInfoPipeWire::GetDeviceName(uint32_t deviceNumber,
if (deviceNumber >= NumberOfDevices())
return -1;
const PipeWireNode& node = pipewire_session_->nodes().at(deviceNumber);
const auto& node = pipewire_session_->nodes().at(deviceNumber);
if (deviceNameLength <= node.display_name().length()) {
if (deviceNameLength <= node->display_name().length()) {
RTC_LOG(LS_INFO) << "deviceNameUTF8 buffer passed is too small";
return -1;
}
if (deviceUniqueIdUTF8Length <= node.unique_id().length()) {
if (deviceUniqueIdUTF8Length <= node->unique_id().length()) {
RTC_LOG(LS_INFO) << "deviceUniqueIdUTF8 buffer passed is too small";
return -1;
}
if (productUniqueIdUTF8 &&
productUniqueIdUTF8Length <= node.model_id().length()) {
productUniqueIdUTF8Length <= node->model_id().length()) {
RTC_LOG(LS_INFO) << "productUniqueIdUTF8 buffer passed is too small";
return -1;
}
memset(deviceNameUTF8, 0, deviceNameLength);
node.display_name().copy(deviceNameUTF8, deviceNameLength);
node->display_name().copy(deviceNameUTF8, deviceNameLength);
memset(deviceUniqueIdUTF8, 0, deviceUniqueIdUTF8Length);
node.unique_id().copy(deviceUniqueIdUTF8, deviceUniqueIdUTF8Length);
node->unique_id().copy(deviceUniqueIdUTF8, deviceUniqueIdUTF8Length);
if (productUniqueIdUTF8) {
memset(productUniqueIdUTF8, 0, productUniqueIdUTF8Length);
node.model_id().copy(productUniqueIdUTF8, productUniqueIdUTF8Length);
node->model_id().copy(productUniqueIdUTF8, productUniqueIdUTF8Length);
}
return 0;
@ -90,11 +90,11 @@ int32_t DeviceInfoPipeWire::CreateCapabilityMap(
RTC_CHECK(pipewire_session_);
for (auto& node : pipewire_session_->nodes()) {
if (node.unique_id().compare(deviceUniqueIdUTF8) != 0)
if (node->unique_id().compare(deviceUniqueIdUTF8) != 0)
continue;
_captureCapabilities = node.capabilities();
_lastUsedDeviceNameLength = node.unique_id().length();
_captureCapabilities = node->capabilities();
_lastUsedDeviceNameLength = node->unique_id().length();
_lastUsedDeviceName = static_cast<char*>(
realloc(_lastUsedDeviceName, _lastUsedDeviceNameLength + 1));
memcpy(_lastUsedDeviceName, deviceUniqueIdUTF8,

View File

@ -53,6 +53,19 @@ VideoType PipeWireRawFormatToVideoType(uint32_t id) {
}
}
void PipeWireNode::PipeWireNodeDeleter::operator()(
PipeWireNode* node) const noexcept {
pw_proxy_destroy(node->proxy_);
spa_hook_remove(&node->node_listener_);
}
// static
PipeWireNode::PipeWireNodePtr PipeWireNode::Create(PipeWireSession* session,
uint32_t id,
const spa_dict* props) {
return PipeWireNodePtr(new PipeWireNode(session, id, props));
}
RTC_NO_SANITIZE("cfi-icall")
PipeWireNode::PipeWireNode(PipeWireSession* session,
uint32_t id,
@ -75,11 +88,6 @@ PipeWireNode::PipeWireNode(PipeWireSession* session,
pw_node_add_listener(proxy_, &node_listener_, &node_events, this);
}
PipeWireNode::~PipeWireNode() {
pw_proxy_destroy(proxy_);
spa_hook_remove(&node_listener_);
}
// static
RTC_NO_SANITIZE("cfi-icall")
void PipeWireNode::OnNodeInfo(void* data, const pw_node_info* info) {
@ -102,7 +110,9 @@ void PipeWireNode::OnNodeInfo(void* data, const pw_node_info* info) {
pid.value());
that->model_id_ = model_str;
}
} else if (info->change_mask & PW_NODE_CHANGE_MASK_PARAMS) {
}
if (info->change_mask & PW_NODE_CHANGE_MASK_PARAMS) {
for (uint32_t i = 0; i < info->n_params; i++) {
uint32_t id = info->params[i].id;
if (id == SPA_PARAM_EnumFormat &&
@ -356,6 +366,14 @@ void PipeWireSession::OnCoreDone(void* data, uint32_t id, int seq) {
if (id == PW_ID_CORE) {
if (seq == that->sync_seq_) {
RTC_LOG(LS_VERBOSE) << "Enumerating PipeWire camera devices complete.";
// Remove camera devices with no capabilities
auto it = std::remove_if(that->nodes_.begin(), that->nodes_.end(),
[](const PipeWireNode::PipeWireNodePtr& node) {
return node->capabilities().empty();
});
that->nodes_.erase(it, that->nodes_.end());
that->Finish(VideoCaptureOptions::Status::SUCCESS);
}
}
@ -373,8 +391,8 @@ void PipeWireSession::OnRegistryGlobal(void* data,
// Skip already added nodes to avoid duplicate camera entries
if (std::find_if(that->nodes_.begin(), that->nodes_.end(),
[id](const PipeWireNode& node) {
return node.id() == id;
[id](const PipeWireNode::PipeWireNodePtr& node) {
return node->id() == id;
}) != that->nodes_.end())
return;
@ -388,7 +406,7 @@ void PipeWireSession::OnRegistryGlobal(void* data,
if (!node_role || strcmp(node_role, "Camera"))
return;
that->nodes_.emplace_back(that, id, props);
that->nodes_.push_back(PipeWireNode::Create(that, id, props));
that->PipeWireSync();
}
@ -396,9 +414,10 @@ void PipeWireSession::OnRegistryGlobal(void* data,
void PipeWireSession::OnRegistryGlobalRemove(void* data, uint32_t id) {
PipeWireSession* that = static_cast<PipeWireSession*>(data);
auto it = std::remove_if(
that->nodes_.begin(), that->nodes_.end(),
[id](const PipeWireNode& node) { return node.id() == id; });
auto it = std::remove_if(that->nodes_.begin(), that->nodes_.end(),
[id](const PipeWireNode::PipeWireNodePtr& node) {
return node->id() == id;
});
that->nodes_.erase(it, that->nodes_.end());
}

View File

@ -37,8 +37,15 @@ class VideoCaptureModulePipeWire;
// So they all represent one camera that is available via PipeWire.
class PipeWireNode {
public:
PipeWireNode(PipeWireSession* session, uint32_t id, const spa_dict* props);
~PipeWireNode();
struct PipeWireNodeDeleter {
void operator()(PipeWireNode* node) const noexcept;
};
using PipeWireNodePtr =
std::unique_ptr<PipeWireNode, PipeWireNode::PipeWireNodeDeleter>;
static PipeWireNodePtr Create(PipeWireSession* session,
uint32_t id,
const spa_dict* props);
uint32_t id() const { return id_; }
std::string display_name() const { return display_name_; }
@ -48,6 +55,9 @@ class PipeWireNode {
return capabilities_;
}
protected:
PipeWireNode(PipeWireSession* session, uint32_t id, const spa_dict* props);
private:
static void OnNodeInfo(void* data, const pw_node_info* info);
static void OnNodeParam(void* data,
@ -87,8 +97,9 @@ class PipeWireSession : public rtc::RefCountedNonVirtual<PipeWireSession> {
void Init(VideoCaptureOptions::Callback* callback,
int fd = kInvalidPipeWireFd);
const std::deque<PipeWireNode>& nodes() const { return nodes_; }
const std::deque<PipeWireNode::PipeWireNodePtr>& nodes() const {
return nodes_;
}
friend class CameraPortalNotifier;
friend class PipeWireNode;
@ -134,7 +145,7 @@ class PipeWireSession : public rtc::RefCountedNonVirtual<PipeWireSession> {
int sync_seq_ = 0;
std::deque<PipeWireNode> nodes_;
std::deque<PipeWireNode::PipeWireNodePtr> nodes_;
std::unique_ptr<CameraPortal> portal_;
std::unique_ptr<CameraPortalNotifier> portal_notifier_;
};