Repository: godotengine/godot (language: cpp)

Issue:
Adding custom camera feeds to `CameraServer` leads to crash on Linux
### Tested versions

v4.4.dev.custom_build [4364ed6cc]

### System information

Godot v4.4.dev (4364ed6cc) - Freedesktop SDK 24.08 (Flatpak runtime) on Wayland - Wayland display driver, Single-window, 1 monitor - Vulkan (Forward+) - dedicated AMD Radeon RX 6650 XT (RADV NAVI23) - 12th Gen Intel(R) Core(TM) i5-12500 (12 threads)

### Issue description

When a custom `CameraFeed` is added to `CameraServer` through either GDScript or GDExtension, the scene will crash with error about caller thread, then crash with signal 11:

```
ERROR: Caller thread can't call this function in this node (/root). Use call_deferred() or call_thread_group() instead.
   at: propagate_notification (scene/main/node.cpp:2523)
```

### Steps to reproduce

```gdscript
var feed := CameraFeed.new()

func _ready() -> void:
	CameraServer.add_feed(feed)
```

### Minimal reproduction project (MRP)

None


Discussion / hints:
No crash on windows 10, maybe OS related.

Godot v4.4.dev (2b7ea6223) - Windows 10 (build 19045) - Multi-window, 2 monitors - Vulkan (Forward+) - dedicated AMD Radeon RX 580 2048SP (Advanced Micro Devices, Inc.; 31.0.21921.1000) - AMD Ryzen 5 3600 6-Core Processor (12 threads)

Fix patch:
diff --git a/modules/camera/camera_linux.cpp b/modules/camera/camera_linux.cpp
index 60217709af8c..b774d1be3554 100644
--- a/modules/camera/camera_linux.cpp
+++ b/modules/camera/camera_linux.cpp
@@ -52,6 +52,9 @@ void CameraLinux::_update_devices() {
 
 			for (int i = feeds.size() - 1; i >= 0; i--) {
 				Ref<CameraFeedLinux> feed = (Ref<CameraFeedLinux>)feeds[i];
+				if (feed.is_null()) {
+					continue;
+				}
 				String device_name = feed->get_device_name();
 				if (!_is_active(device_name)) {
 					remove_feed(feed);
@@ -84,6 +87,9 @@ void CameraLinux::_update_devices() {
 bool CameraLinux::_has_device(const String &p_device_name) {
 	for (int i = 0; i < feeds.size(); i++) {
 		Ref<CameraFeedLinux> feed = (Ref<CameraFeedLinux>)feeds[i];
+		if (feed.is_null()) {
+			continue;
+		}
 		if (feed->get_device_name() == p_device_name) {
 			return true;
 		}
diff --git a/modules/camera/camera_macos.mm b/modules/camera/camera_macos.mm
index bd718a0cb6c8..d242444b0b5a 100644
--- a/modules/camera/camera_macos.mm
+++ b/modules/camera/camera_macos.mm
@@ -323,6 +323,9 @@ - (void)dealloc {
 	// remove devices that are gone..
 	for (int i = feeds.size() - 1; i >= 0; i--) {
 		Ref<CameraFeedMacOS> feed = (Ref<CameraFeedMacOS>)feeds[i];
+		if (feed.is_null()) {
+			continue;
+		}
 
 		if (![devices containsObject:feed->get_device()]) {
 			// remove it from our array, this will also destroy it ;)
@@ -334,6 +337,9 @@ - (void)dealloc {
 		bool found = false;
 		for (int i = 0; i < feeds.size() && !found; i++) {
 			Ref<CameraFeedMacOS> feed = (Ref<CameraFeedMacOS>)feeds[i];
+			if (feed.is_null()) {
+				continue;
+			}
 			if (feed->get_device() == device) {
 				found = true;
 			};


Test patch: