diff --git a/modules/video_capture/linux/device_info_pipewire.cc b/modules/video_capture/linux/device_info_pipewire.cc index b608648194..0ec0da3933 100644 --- a/modules/video_capture/linux/device_info_pipewire.cc +++ b/modules/video_capture/linux/device_info_pipewire.cc @@ -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( realloc(_lastUsedDeviceName, _lastUsedDeviceNameLength + 1)); memcpy(_lastUsedDeviceName, deviceUniqueIdUTF8, diff --git a/modules/video_capture/linux/pipewire_session.cc b/modules/video_capture/linux/pipewire_session.cc index 0642713f91..fd93b31063 100644 --- a/modules/video_capture/linux/pipewire_session.cc +++ b/modules/video_capture/linux/pipewire_session.cc @@ -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(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()); } diff --git a/modules/video_capture/linux/pipewire_session.h b/modules/video_capture/linux/pipewire_session.h index fdc06a6b2a..84273ea695 100644 --- a/modules/video_capture/linux/pipewire_session.h +++ b/modules/video_capture/linux/pipewire_session.h @@ -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; + 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 { void Init(VideoCaptureOptions::Callback* callback, int fd = kInvalidPipeWireFd); - - const std::deque& nodes() const { return nodes_; } + const std::deque& nodes() const { + return nodes_; + } friend class CameraPortalNotifier; friend class PipeWireNode; @@ -134,7 +145,7 @@ class PipeWireSession : public rtc::RefCountedNonVirtual { int sync_seq_ = 0; - std::deque nodes_; + std::deque nodes_; std::unique_ptr portal_; std::unique_ptr portal_notifier_; };