[iOS] Reintegrate camera module to the main repo.

This commit is contained in:
Pāvels Nadtočajevs
2026-01-28 11:43:57 +02:00
parent babc272d44
commit 4d8234d50e
25 changed files with 449 additions and 93 deletions
+1 -1
View File
@@ -6,7 +6,7 @@
<description>
The [CameraServer] keeps track of different cameras accessible in Godot. These are external cameras such as webcams or the cameras on your phone.
It is notably used to provide AR modules with a video feed from the camera.
[b]Note:[/b] This class is currently only implemented on Linux, Android, macOS, and iOS. On other platforms no [CameraFeed]s will be available. To get a [CameraFeed] on iOS, the camera plugin from [url=https://github.com/godotengine/godot-ios-plugins]godot-ios-plugins[/url] is required.
[b]Note:[/b] This class is currently only implemented on Linux, Android, macOS, and iOS. On other platforms no [CameraFeed]s will be available. To get a [CameraFeed] on iOS, enable [member EditorExportPlatformIOS.modules/camera].
</description>
<tutorials>
</tutorials>
@@ -283,6 +283,8 @@ void EditorExportPlatformAppleEmbedded::get_export_options(List<ExportOption> *r
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "application/export_project_only"), false));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "application/delete_old_export_files_unconditionally"), false));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "modules/camera"), false));
Vector<PluginConfigAppleEmbedded> found_plugins = get_plugins(get_platform_name());
for (int i = 0; i < found_plugins.size(); i++) {
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, vformat("%s/%s", PNAME("plugins"), found_plugins[i].name)), false));
@@ -1422,7 +1424,7 @@ Vector<String> EditorExportPlatformAppleEmbedded::_get_preset_architectures(cons
return enabled_archs;
}
Error EditorExportPlatformAppleEmbedded::_export_apple_embedded_plugins(const Ref<EditorExportPreset> &p_preset, AppleEmbeddedConfigData &p_config_data, const String &dest_dir, Vector<AppleEmbeddedExportAsset> &r_exported_assets, bool p_debug) {
Error EditorExportPlatformAppleEmbedded::_export_apple_embedded_plugins(const Ref<EditorExportPreset> &p_preset, AppleEmbeddedConfigData &p_config_data, const String &p_dest_dir, const Vector<String> &p_module_libs, Vector<AppleEmbeddedExportAsset> &r_exported_assets, bool p_debug) {
String plugin_definition_cpp_code;
String plugin_initialization_cpp_code;
String plugin_deinitialization_cpp_code;
@@ -1449,7 +1451,7 @@ Error EditorExportPlatformAppleEmbedded::_export_apple_embedded_plugins(const Re
String plugin_binary_result_file = plugin.binary.get_file();
// We shouldn't embed .xcframework that contains static libraries.
// Static libraries are not embedded anyway.
err = _copy_asset(p_preset, dest_dir, plugin_main_binary, &plugin_binary_result_file, true, false, r_exported_assets);
err = _copy_asset(p_preset, p_dest_dir, plugin_main_binary, &plugin_binary_result_file, true, false, r_exported_assets);
ERR_FAIL_COND_V(err != OK, err);
// Adding dependencies.
@@ -1553,6 +1555,23 @@ Error EditorExportPlatformAppleEmbedded::_export_apple_embedded_plugins(const Re
plugin_deinitialization_cpp_code += "\t" + deinitialization_method;
}
for (const String &lib_name : p_module_libs) {
String definition_comment = "// Module: " + lib_name + "\n";
String initialization_method = "register_" + lib_name + "_external_module();\n";
String deinitialization_method = "unregister_" + lib_name + "_external_module();\n";
plugin_definition_cpp_code += definition_comment +
"extern void " + initialization_method +
"extern void " + deinitialization_method + "\n";
plugin_initialization_cpp_code += "\t" + initialization_method;
plugin_deinitialization_cpp_code += "\t" + deinitialization_method;
String binary_name = p_dest_dir.get_file().get_basename();
AppleEmbeddedExportAsset exported_asset = { binary_name + "_" + lib_name + ".xcframework", true, false };
r_exported_assets.push_back(exported_asset);
}
// Updating `Info.plist`
{
for (const KeyValue<String, String> &E : plist_values) {
@@ -1570,15 +1589,15 @@ Error EditorExportPlatformAppleEmbedded::_export_apple_embedded_plugins(const Re
// Export files
{
// Export linked plugin dependency
err = _export_additional_assets(p_preset, dest_dir, plugin_linked_dependencies, true, false, r_exported_assets);
err = _export_additional_assets(p_preset, p_dest_dir, plugin_linked_dependencies, true, false, r_exported_assets);
ERR_FAIL_COND_V(err != OK, err);
// Export embedded plugin dependency
err = _export_additional_assets(p_preset, dest_dir, plugin_embedded_dependencies, true, true, r_exported_assets);
err = _export_additional_assets(p_preset, p_dest_dir, plugin_embedded_dependencies, true, true, r_exported_assets);
ERR_FAIL_COND_V(err != OK, err);
// Export plugin files
err = _export_additional_assets(p_preset, dest_dir, plugin_files, false, false, r_exported_assets);
err = _export_additional_assets(p_preset, p_dest_dir, plugin_files, false, false, r_exported_assets);
ERR_FAIL_COND_V(err != OK, err);
}
@@ -1765,6 +1784,10 @@ Error EditorExportPlatformAppleEmbedded::_export_project_helper(const Ref<Editor
}
String library_to_use = "libgodot." + get_platform_name() + "." + String(p_debug ? "debug" : "release") + ".xcframework";
Vector<String> module_libs;
if (p_preset->get("modules/camera").operator bool()) {
module_libs.push_back("camera");
}
print_line("Static framework: " + library_to_use);
String pkg_name;
@@ -1774,7 +1797,7 @@ Error EditorExportPlatformAppleEmbedded::_export_project_helper(const Ref<Editor
pkg_name = "Unnamed";
}
bool found_library = false;
int found_libraries = 0;
HashSet<String> files_to_parse;
const String project_file = "godot_apple_embedded.xcodeproj/project.pbxproj";
@@ -1821,7 +1844,7 @@ Error EditorExportPlatformAppleEmbedded::_export_project_helper(const Ref<Editor
return ERR_CANT_OPEN;
}
err = _export_apple_embedded_plugins(p_preset, config_data, binary_dir, assets, p_debug);
err = _export_apple_embedded_plugins(p_preset, config_data, binary_dir, module_libs, assets, p_debug);
if (err != OK) {
// TODO: Improve error reporting by using `add_message` throughout all methods called via `_export_apple_embedded_plugins`.
// For now a generic top level message would be fine, but we're ought to use proper reporting here instead of
@@ -1861,16 +1884,32 @@ Error EditorExportPlatformAppleEmbedded::_export_project_helper(const Ref<Editor
if (files_to_parse.has(file)) {
_fix_config_file(p_preset, data, config_data, p_debug);
} else if (file.begins_with("libgodot." + get_platform_name())) {
if (!file.begins_with(library_to_use) || file.ends_with(String("/empty"))) {
} else if (file.begins_with("libgodot") && file.contains(get_platform_name())) {
String prefix_lib = library_to_use;
String suffix_lib = binary_name;
bool is_lib = file.begins_with(library_to_use);
if (!is_lib) {
for (const String &lib_name : module_libs) {
String prefix = "libgodot_" + lib_name + "." + get_platform_name() + "." + String(p_debug ? "debug" : "release") + ".xcframework";
if (file.begins_with(prefix)) {
is_lib = true;
prefix_lib = prefix;
suffix_lib = binary_name + "_" + lib_name;
break;
}
}
}
if (!is_lib || file.ends_with(String("/empty"))) {
ret = unzGoToNextFile(src_pkg_zip);
continue; //ignore!
}
found_library = true;
if (file.ends_with("Info.plist")) {
found_libraries++;
}
#if defined(MACOS_ENABLED) || defined(LINUXBSD_ENABLED)
is_execute = true;
#endif
file = file.replace(library_to_use, binary_name + ".xcframework");
file = file.replace(prefix_lib, suffix_lib + ".xcframework");
}
if (file == project_file) {
@@ -1924,7 +1963,7 @@ Error EditorExportPlatformAppleEmbedded::_export_project_helper(const Ref<Editor
// We're done with our source zip.
unzClose(src_pkg_zip);
if (!found_library) {
if (found_libraries < module_libs.size() + 1) {
add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Requested template library '%s' not found. It might be missing from your template archive."), library_to_use));
return ERR_FILE_NOT_FOUND;
}
@@ -2194,6 +2233,14 @@ bool EditorExportPlatformAppleEmbedded::has_valid_export_configuration(const Ref
valid = dvalid || rvalid;
r_missing_templates = !valid;
if (p_preset->get("modules/camera").operator bool()) {
String description = p_preset->get("privacy/camera_usage_description");
if (description.is_empty()) {
valid = false;
err += TTR("Camera module enabled, but camera usage description is not set.") + "\n";
}
}
const String &additional_plist_content = p_preset->get("application/additional_plist_content");
if (!additional_plist_content.is_empty()) {
const String &plist = vformat("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
@@ -197,7 +197,7 @@ private:
Error _export_additional_assets(const Ref<EditorExportPreset> &p_preset, const String &p_out_dir, const Vector<String> &p_assets, bool p_is_framework, bool p_should_embed, Vector<AppleEmbeddedExportAsset> &r_exported_assets);
Error _copy_asset(const Ref<EditorExportPreset> &p_preset, const String &p_out_dir, const String &p_asset, const String *p_custom_file_name, bool p_is_framework, bool p_should_embed, Vector<AppleEmbeddedExportAsset> &r_exported_assets);
Error _export_additional_assets(const Ref<EditorExportPreset> &p_preset, const String &p_out_dir, const Vector<SharedObject> &p_libraries, Vector<AppleEmbeddedExportAsset> &r_exported_assets);
Error _export_apple_embedded_plugins(const Ref<EditorExportPreset> &p_preset, AppleEmbeddedConfigData &p_config_data, const String &dest_dir, Vector<AppleEmbeddedExportAsset> &r_exported_assets, bool p_debug);
Error _export_apple_embedded_plugins(const Ref<EditorExportPreset> &p_preset, AppleEmbeddedConfigData &p_config_data, const String &p_dest_dir, const Vector<String> &p_module_libs, Vector<AppleEmbeddedExportAsset> &r_exported_assets, bool p_debug);
Error _export_project_helper(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags, bool p_oneclick);
@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>AvailableLibraries</key>
<array>
<dict>
<key>LibraryIdentifier</key>
<string>ios-arm64</string>
<key>LibraryPath</key>
<string>libgodot_camera.a</string>
<key>SupportedArchitectures</key>
<array>
<string>arm64</string>
</array>
<key>SupportedPlatform</key>
<string>ios</string>
</dict>
<dict>
<key>LibraryIdentifier</key>
<string>ios-arm64_x86_64-simulator</string>
<key>LibraryPath</key>
<string>libgodot_camera.a</string>
<key>SupportedArchitectures</key>
<array>
<string>arm64</string>
<string>x86_64</string>
</array>
<key>SupportedPlatform</key>
<string>ios</string>
<key>SupportedPlatformVariant</key>
<string>simulator</string>
</dict>
</array>
<key>CFBundlePackageType</key>
<string>XFWK</string>
<key>XCFrameworkFormatVersion</key>
<string>1.0</string>
</dict>
</plist>
@@ -0,0 +1 @@
Dummy file to make dylibs folder exported
@@ -0,0 +1 @@
Dummy file to make dylibs folder exported
@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>AvailableLibraries</key>
<array>
<dict>
<key>LibraryIdentifier</key>
<string>ios-arm64</string>
<key>LibraryPath</key>
<string>libgodot_camera.a</string>
<key>SupportedArchitectures</key>
<array>
<string>arm64</string>
</array>
<key>SupportedPlatform</key>
<string>ios</string>
</dict>
<dict>
<key>LibraryIdentifier</key>
<string>ios-arm64_x86_64-simulator</string>
<key>LibraryPath</key>
<string>libgodot_camera.a</string>
<key>SupportedArchitectures</key>
<array>
<string>arm64</string>
<string>x86_64</string>
</array>
<key>SupportedPlatform</key>
<string>ios</string>
<key>SupportedPlatformVariant</key>
<string>simulator</string>
</dict>
</array>
<key>CFBundlePackageType</key>
<string>XFWK</string>
<key>XCFrameworkFormatVersion</key>
<string>1.0</string>
</dict>
</plist>
@@ -0,0 +1 @@
Dummy file to make dylibs folder exported
@@ -0,0 +1 @@
Dummy file to make dylibs folder exported
@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>AvailableLibraries</key>
<array>
<dict>
<key>LibraryIdentifier</key>
<string>xros-arm64</string>
<key>LibraryPath</key>
<string>libgodot_camera.a</string>
<key>SupportedArchitectures</key>
<array>
<string>arm64</string>
</array>
<key>SupportedPlatform</key>
<string>xros</string>
</dict>
<dict>
<key>LibraryIdentifier</key>
<string>xros-arm64-simulator</string>
<key>LibraryPath</key>
<string>libgodot_camera.a</string>
<key>SupportedArchitectures</key>
<array>
<string>arm64</string>
</array>
<key>SupportedPlatform</key>
<string>xros</string>
<key>SupportedPlatformVariant</key>
<string>simulator</string>
</dict>
</array>
<key>CFBundlePackageType</key>
<string>XFWK</string>
<key>XCFrameworkFormatVersion</key>
<string>1.0</string>
</dict>
</plist>
@@ -0,0 +1 @@
Dummy file to make dylibs folder exported
@@ -0,0 +1 @@
Dummy file to make dylibs folder exported
@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>AvailableLibraries</key>
<array>
<dict>
<key>LibraryIdentifier</key>
<string>xros-arm64</string>
<key>LibraryPath</key>
<string>libgodot_camera.a</string>
<key>SupportedArchitectures</key>
<array>
<string>arm64</string>
</array>
<key>SupportedPlatform</key>
<string>xros</string>
</dict>
<dict>
<key>LibraryIdentifier</key>
<string>xros-arm64-simulator</string>
<key>LibraryPath</key>
<string>libgodot_camera.a</string>
<key>SupportedArchitectures</key>
<array>
<string>arm64</string>
</array>
<key>SupportedPlatform</key>
<string>xros</string>
<key>SupportedPlatformVariant</key>
<string>simulator</string>
</dict>
</array>
<key>CFBundlePackageType</key>
<string>XFWK</string>
<key>XCFrameworkFormatVersion</key>
<string>1.0</string>
</dict>
</plist>
@@ -0,0 +1 @@
Dummy file to make dylibs folder exported
@@ -0,0 +1 @@
Dummy file to make dylibs folder exported
+8 -2
View File
@@ -6,19 +6,25 @@ Import("env_modules")
env_camera = env_modules.Clone()
if env["platform"] in ["windows", "macos", "linuxbsd", "android"]:
if env["platform"] in ["windows", "macos", "linuxbsd", "android", "ios", "visionos"]:
env_camera.add_source_files(env.modules_sources, "register_types.cpp")
if env["platform"] == "windows":
env_camera.add_source_files(env.modules_sources, "camera_win.cpp")
elif env["platform"] == "macos":
env_camera.add_source_files(env.modules_sources, "camera_macos.mm")
env_camera.add_source_files(env.modules_sources, "camera_apple.mm")
elif env["platform"] == "android":
env_camera.add_source_files(env.modules_sources, "camera_android.cpp")
env.Append(LIBS=["camera2ndk", "mediandk"])
elif env["platform"] in ["ios", "visionos"]:
ext_module_source = ["camera_apple.mm"]
ext_camera_lib = env_camera.add_library("#bin/libgodot_camera", ext_module_source)
env.Append(LIBS_EXTERNAL=[ext_camera_lib])
env.Append(MODULES_EXTERNAL=["_camera"])
elif env["platform"] == "linuxbsd":
env_camera.add_source_files(env.modules_sources, "camera_linux.cpp")
env_camera.add_source_files(env.modules_sources, "camera_feed_linux.cpp")
@@ -1,5 +1,5 @@
/**************************************************************************/
/* camera_macos.h */
/* camera_apple.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -35,11 +35,11 @@
#include "servers/camera/camera_server.h"
class CameraMacOS : public CameraServer {
GDSOFTCLASS(CameraMacOS, CameraServer);
class CameraApple : public CameraServer {
GDSOFTCLASS(CameraApple, CameraServer);
public:
CameraMacOS() = default;
CameraApple() = default;
void update_feeds();
void set_monitoring_feeds(bool p_monitoring_feeds) override;
@@ -1,5 +1,5 @@
/**************************************************************************/
/* camera_macos.mm */
/* camera_apple.mm */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -31,11 +31,14 @@
///@TODO this is a near duplicate of CameraIOS, we should find a way to combine those to minimize code duplication!!!!
// If you fix something here, make sure you fix it there as well!
#import "camera_macos.h"
#import "camera_apple.h"
#include "servers/camera/camera_feed.h"
#import <AVFoundation/AVFoundation.h>
#ifdef IOS_ENABLED
#import <UIKit/UIKit.h>
#endif
//////////////////////////////////////////////////////////////////////////
// MyCaptureSession - This is a little helper class so we can capture our frames
@@ -63,23 +66,30 @@
width[1] = 0;
height[1] = 0;
#ifdef APPLE_EMBEDDED_ENABLED
[p_device lockForConfiguration:&error];
#ifdef IOS_ENABLED
if ([p_device lockForConfiguration:&error]) {
if ([p_device isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]) {
[p_device setFocusMode:AVCaptureFocusModeContinuousAutoFocus];
}
if ([p_device isExposureModeSupported:AVCaptureExposureModeContinuousAutoExposure]) {
[p_device setExposureMode:AVCaptureExposureModeContinuousAutoExposure];
}
if ([p_device isWhiteBalanceModeSupported:AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance]) {
[p_device setWhiteBalanceMode:AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance];
}
[p_device setFocusMode:AVCaptureFocusModeLocked];
[p_device setExposureMode:AVCaptureExposureModeLocked];
[p_device setWhiteBalanceMode:AVCaptureWhiteBalanceModeLocked];
[p_device unlockForConfiguration];
#endif // APPLE_EMBEDDED_ENABLED
[p_device unlockForConfiguration];
}
#endif // IOS_ENABLED
[self beginConfiguration];
#ifdef APPLE_EMBEDDED_ENABLED
#ifdef IOS_ENABLED
self.sessionPreset = AVCaptureSessionPreset1280x720;
#endif // APPLE_EMBEDDED_ENABLED
#endif // IOS_ENABLED
input = [[AVCaptureDeviceInput alloc] initWithDevice:p_device error:&error];
input = [AVCaptureDeviceInput deviceInputWithDevice:p_device error:&error];
if (!input) {
print_line("Couldn't get input device for camera");
[self commitConfiguration];
@@ -219,6 +229,28 @@
// set our texture...
feed->set_ycbcr_images(img[0], img[1]);
#ifdef IOS_ENABLED
UIInterfaceOrientation orientation = [UIApplication sharedApplication].delegate.window.windowScene.interfaceOrientation;
Transform2D display_transform;
switch (orientation) {
case UIInterfaceOrientationPortrait: {
display_transform = Transform2D(0.0, -1.0, -1.0, 0.0, 1.0, 1.0);
} break;
case UIInterfaceOrientationLandscapeRight: {
display_transform = Transform2D(1.0, 0.0, 0.0, -1.0, 0.0, 1.0);
} break;
case UIInterfaceOrientationLandscapeLeft: {
display_transform = Transform2D(-1.0, 0.0, 0.0, 1.0, 1.0, 0.0);
} break;
default: {
display_transform = Transform2D(0.0, 1.0, 1.0, 0.0, 0.0, 0.0);
} break;
}
feed->set_transform(display_transform);
#endif // IOS_ENABLED
// and unlock
CVPixelBufferUnlockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly);
}
@@ -226,10 +258,10 @@
@end
//////////////////////////////////////////////////////////////////////////
// CameraFeedMacOS - Subclass for camera feeds in macOS
// CameraFeedApple - Subclass for camera feeds in macOS
class CameraFeedMacOS : public CameraFeed {
GDSOFTCLASS(CameraFeedMacOS, CameraFeed);
class CameraFeedApple : public CameraFeed {
GDSOFTCLASS(CameraFeedApple, CameraFeed);
private:
AVCaptureDevice *device;
@@ -238,8 +270,8 @@ private:
public:
AVCaptureDevice *get_device() const;
CameraFeedMacOS();
~CameraFeedMacOS();
CameraFeedApple();
~CameraFeedApple();
void set_device(AVCaptureDevice *p_device);
@@ -247,22 +279,23 @@ public:
void deactivate_feed() override;
};
AVCaptureDevice *CameraFeedMacOS::get_device() const {
AVCaptureDevice *CameraFeedApple::get_device() const {
return device;
}
CameraFeedMacOS::CameraFeedMacOS() {
CameraFeedApple::CameraFeedApple() {
device = nullptr;
capture_session = nullptr;
transform = Transform2D(1.0, 0.0, 0.0, 1.0, 0.0, 0.0); /* should re-orientate this based on device orientation */
}
CameraFeedMacOS::~CameraFeedMacOS() {
CameraFeedApple::~CameraFeedApple() {
if (is_active()) {
deactivate_feed();
}
}
void CameraFeedMacOS::set_device(AVCaptureDevice *p_device) {
void CameraFeedApple::set_device(AVCaptureDevice *p_device) {
device = p_device;
// get some info
@@ -276,14 +309,14 @@ void CameraFeedMacOS::set_device(AVCaptureDevice *p_device) {
};
}
bool CameraFeedMacOS::activate_feed() {
bool CameraFeedApple::activate_feed() {
if (capture_session) {
// Already recording.
return true;
}
// Start camera capture, check permission.
if (@available(macOS 10.14, *)) {
if (@available(macOS 10.14, iOS 14.0, visionOS 1.0, *)) {
AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
if (status == AVAuthorizationStatusAuthorized) {
capture_session = [[MyCaptureSession alloc] initForFeed:this andDevice:device];
@@ -311,7 +344,7 @@ bool CameraFeedMacOS::activate_feed() {
}
}
void CameraFeedMacOS::deactivate_feed() {
void CameraFeedApple::deactivate_feed() {
// end camera capture if we have one
if (capture_session) {
[capture_session cleanup];
@@ -324,7 +357,7 @@ void CameraFeedMacOS::deactivate_feed() {
// when devices are connected/disconnected
@interface MyDeviceNotifications : NSObject {
CameraMacOS *camera_server;
CameraApple *camera_server;
}
@end
@@ -335,7 +368,7 @@ void CameraFeedMacOS::deactivate_feed() {
camera_server->update_feeds();
}
- (id)initForServer:(CameraMacOS *)p_server {
- (id)initForServer:(CameraApple *)p_server {
if (self = [super init]) {
camera_server = p_server;
@@ -356,13 +389,31 @@ void CameraFeedMacOS::deactivate_feed() {
MyDeviceNotifications *device_notifications = nil;
//////////////////////////////////////////////////////////////////////////
// CameraMacOS - Subclass for our camera server on macOS
// CameraApple - Subclass for our camera server on macOS
void CameraMacOS::update_feeds() {
void CameraApple::update_feeds() {
NSArray<AVCaptureDevice *> *devices = nullptr;
#ifdef APPLE_EMBEDDED_ENABLED
{
NSMutableArray *deviceTypes = [NSMutableArray array];
if (@available(iOS 14.0, visionOS 2.1, *)) {
[deviceTypes addObject:AVCaptureDeviceTypeBuiltInWideAngleCamera];
}
#ifdef IOS_ENABLED
[deviceTypes addObject:AVCaptureDeviceTypeBuiltInTelephotoCamera];
[deviceTypes addObject:AVCaptureDeviceTypeBuiltInDualCamera];
[deviceTypes addObject:AVCaptureDeviceTypeBuiltInTrueDepthCamera];
[deviceTypes addObject:AVCaptureDeviceTypeBuiltInUltraWideCamera];
[deviceTypes addObject:AVCaptureDeviceTypeBuiltInDualWideCamera];
[deviceTypes addObject:AVCaptureDeviceTypeBuiltInTripleCamera];
#endif // IOS_ENABLED
AVCaptureDeviceDiscoverySession *session = [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes:deviceTypes mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionUnspecified];
devices = session.devices;
}
#else // APPLE_EMBEDDED_ENABLED
#if defined(__x86_64__)
if (@available(macOS 10.15, *)) {
#endif
#endif // __x86_64__
AVCaptureDeviceDiscoverySession *session;
if (@available(macOS 14.0, *)) {
session = [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes:[NSArray arrayWithObjects:AVCaptureDeviceTypeExternal, AVCaptureDeviceTypeBuiltInWideAngleCamera, AVCaptureDeviceTypeContinuityCamera, nil] mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionUnspecified];
@@ -374,11 +425,12 @@ void CameraMacOS::update_feeds() {
} else {
devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
}
#endif
#endif // __x86_64__
#endif // APPLE_EMBEDDED_ENABLED
// Deactivate feeds that are gone before removing them.
for (int i = feeds.size() - 1; i >= 0; i--) {
Ref<CameraFeedMacOS> feed = (Ref<CameraFeedMacOS>)feeds[i];
Ref<CameraFeedApple> feed = (Ref<CameraFeedApple>)feeds[i];
if (feed.is_null()) {
continue;
}
@@ -394,7 +446,7 @@ void CameraMacOS::update_feeds() {
for (AVCaptureDevice *device in devices) {
bool found = false;
for (int i = 0; i < feeds.size() && !found; i++) {
Ref<CameraFeedMacOS> feed = (Ref<CameraFeedMacOS>)feeds[i];
Ref<CameraFeedApple> feed = (Ref<CameraFeedApple>)feeds[i];
if (feed.is_null()) {
continue;
}
@@ -404,7 +456,7 @@ void CameraMacOS::update_feeds() {
};
if (!found) {
Ref<CameraFeedMacOS> newfeed;
Ref<CameraFeedApple> newfeed;
newfeed.instantiate();
newfeed->set_device(device);
@@ -414,7 +466,7 @@ void CameraMacOS::update_feeds() {
emit_signal(SNAME(CameraServer::feeds_updated_signal_name));
}
void CameraMacOS::set_monitoring_feeds(bool p_monitoring_feeds) {
void CameraApple::set_monitoring_feeds(bool p_monitoring_feeds) {
if (p_monitoring_feeds == monitoring_feeds) {
return;
}
@@ -431,3 +483,14 @@ void CameraMacOS::set_monitoring_feeds(bool p_monitoring_feeds) {
device_notifications = nil;
}
}
#ifdef APPLE_EMBEDDED_ENABLED
void register_camera_external_module() {
CameraServer::make_default<CameraApple>();
}
void unregister_camera_external_module() {
}
#endif // APPLE_EMBEDDED_ENABLED
+8 -1
View File
@@ -3,7 +3,14 @@ def can_build(env, platform):
if sys.platform.startswith("freebsd") or sys.platform.startswith("openbsd"):
return False
return platform == "macos" or platform == "windows" or platform == "linuxbsd" or platform == "android"
return (
platform == "macos"
or platform == "windows"
or platform == "linuxbsd"
or platform == "android"
or platform == "ios"
or platform == "visionos"
)
def configure(env):
+2 -2
View File
@@ -37,7 +37,7 @@
#include "camera_win.h"
#endif
#if defined(MACOS_ENABLED)
#include "camera_macos.h"
#include "camera_apple.h"
#endif
#if defined(ANDROID_ENABLED)
#include "camera_android.h"
@@ -55,7 +55,7 @@ void initialize_camera_module(ModuleInitializationLevel p_level) {
CameraServer::make_default<CameraWindows>();
#endif
#if defined(MACOS_ENABLED)
CameraServer::make_default<CameraMacOS>();
CameraServer::make_default<CameraApple>();
#endif
#if defined(ANDROID_ENABLED)
CameraServer::make_default<CameraAndroid>();
+3
View File
@@ -22,6 +22,9 @@ ios_lib = env_ios.add_library("ios", ios_lib)
# (iOS) Enable module support
env_ios.Append(CCFLAGS=["-fmodules", "-fcxx-modules"])
if "LIBS_EXTERNAL" in env_ios:
env_ios.Depends(ios_lib, env_ios["LIBS_EXTERNAL"])
combine_command = env_ios.CommandNoCache(
"#bin/libgodot" + env_ios["LIBSUFFIX"], [ios_lib] + env_ios["LIBS"], env.Run(combine_libs_apple_embedded)
)
@@ -269,6 +269,9 @@
<member name="icons/spotlight_120x120_tinted" type="String" setter="" getter="">
Spotlight icon file on iPad and iPhone (3x DPI), tinted version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url].
</member>
<member name="modules/camera" type="bool" setter="" getter="">
If [code]true[/code], [CameraServer] module is added to the exported project.
</member>
<member name="privacy/active_keyboard_access_reasons" type="int" setter="" getter="">
The reasons your app use active keyboard API. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_use_of_required_reason_api]Describing use of required reason API[/url].
</member>
+3
View File
@@ -21,6 +21,9 @@ visionos_lib = env_visionos.add_library("visionos", visionos_lib)
# Enable module support
env_visionos.Append(CCFLAGS=["-fmodules", "-fcxx-modules"])
if "LIBS_EXTERNAL" in env_visionos:
env_visionos.Depends(visionos_lib, env_visionos["LIBS_EXTERNAL"])
combine_command = env_visionos.Command(
"#bin/libgodot" + env_visionos["LIBSUFFIX"], [visionos_lib] + env_visionos["LIBS"], combine_libs_apple_embedded
)
@@ -121,6 +121,9 @@
<member name="icons/icon_1024x1024_tinted" type="String" setter="" getter="">
Base application icon used to generate other icons, tinted version. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url].
</member>
<member name="modules/camera" type="bool" setter="" getter="">
If [code]true[/code], [CameraServer] module is added to the exported project.
</member>
<member name="privacy/active_keyboard_access_reasons" type="int" setter="" getter="">
The reasons your app use active keyboard API. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_use_of_required_reason_api]Describing use of required reason API[/url].
</member>
+90 -35
View File
@@ -172,13 +172,93 @@ def combine_libs_apple_embedded(target, source, env):
)
def generate_bundle_apple_embedded(platform, framework_dir, framework_dir_sim, use_mkv, target, source, env):
def lipo_and_copy_apple_embedded(
platform, framework_dir, framework_dir_sim, rel_prefix, dbg_prefix, module_prefix, app_dir, env
):
bin_dir = env.Dir("#bin").abspath
# Lipo template libraries.
#
# env.extra_suffix contains ".simulator" when building for simulator,
# but it's undesired when calling lipo()
extra_suffix = env.extra_suffix.replace(".simulator", "")
rel_target_bin = lipo(bin_dir + "/libgodot" + module_prefix + "." + rel_prefix, extra_suffix + ".a")
dbg_target_bin = lipo(bin_dir + "/libgodot" + module_prefix + "." + dbg_prefix, extra_suffix + ".a")
rel_target_bin_sim = lipo(
bin_dir + "/libgodot" + module_prefix + "." + rel_prefix, ".simulator" + extra_suffix + ".a"
)
dbg_target_bin_sim = lipo(
bin_dir + "/libgodot" + module_prefix + "." + dbg_prefix, ".simulator" + extra_suffix + ".a"
)
# Assemble Xcode project bundle.
if rel_target_bin != "":
print(f' Copying "{platform}" release framework')
shutil.copy(
rel_target_bin,
app_dir
+ "/libgodot"
+ module_prefix
+ "."
+ platform
+ ".release.xcframework/"
+ framework_dir
+ "/libgodot"
+ module_prefix
+ ".a",
)
if dbg_target_bin != "":
print(f' Copying "{platform}" debug framework')
shutil.copy(
dbg_target_bin,
app_dir
+ "/libgodot"
+ module_prefix
+ "."
+ platform
+ ".debug.xcframework/"
+ framework_dir
+ "/libgodot"
+ module_prefix
+ ".a",
)
if rel_target_bin_sim != "":
print(f' Copying "{platform}" (simulator) release framework')
shutil.copy(
rel_target_bin_sim,
app_dir
+ "/libgodot"
+ module_prefix
+ "."
+ platform
+ ".release.xcframework/"
+ framework_dir_sim
+ "/libgodot"
+ module_prefix
+ ".a",
)
if dbg_target_bin_sim != "":
print(f' Copying "{platform}" (simulator) debug framework')
shutil.copy(
dbg_target_bin_sim,
app_dir
+ "/libgodot"
+ module_prefix
+ "."
+ platform
+ ".debug.xcframework/"
+ framework_dir_sim
+ "/libgodot"
+ module_prefix
+ ".a",
)
def generate_bundle_apple_embedded(platform, framework_dir, framework_dir_sim, use_mkv, target, source, env):
# Template bundle.
extra_suffix = env.extra_suffix.replace(".simulator", "")
app_prefix = "godot." + platform
rel_prefix = "libgodot." + platform + "." + "template_release"
dbg_prefix = "libgodot." + platform + "." + "template_debug"
rel_prefix = platform + "." + "template_release"
dbg_prefix = platform + "." + "template_debug"
if env.dev_build:
app_prefix += ".dev"
rel_prefix += ".dev"
@@ -188,43 +268,18 @@ def generate_bundle_apple_embedded(platform, framework_dir, framework_dir_sim, u
rel_prefix += ".double"
dbg_prefix += ".double"
# Lipo template libraries.
#
# env.extra_suffix contains ".simulator" when building for simulator,
# but it's undesired when calling lipo()
extra_suffix = env.extra_suffix.replace(".simulator", "")
rel_target_bin = lipo(bin_dir + "/" + rel_prefix, extra_suffix + ".a")
dbg_target_bin = lipo(bin_dir + "/" + dbg_prefix, extra_suffix + ".a")
rel_target_bin_sim = lipo(bin_dir + "/" + rel_prefix, ".simulator" + extra_suffix + ".a")
dbg_target_bin_sim = lipo(bin_dir + "/" + dbg_prefix, ".simulator" + extra_suffix + ".a")
# Assemble Xcode project bundle.
app_dir = env.Dir("#bin/" + platform + "_xcode").abspath
templ = env.Dir("#misc/dist/apple_embedded_xcode").abspath
if os.path.exists(app_dir):
shutil.rmtree(app_dir)
shutil.copytree(templ, app_dir)
if rel_target_bin != "":
print(f' Copying "{platform}" release framework')
shutil.copy(
rel_target_bin, app_dir + "/libgodot." + platform + ".release.xcframework/" + framework_dir + "/libgodot.a"
)
if dbg_target_bin != "":
print(f' Copying "{platform}" debug framework')
shutil.copy(
dbg_target_bin, app_dir + "/libgodot." + platform + ".debug.xcframework/" + framework_dir + "/libgodot.a"
)
if rel_target_bin_sim != "":
print(f' Copying "{platform}" (simulator) release framework')
shutil.copy(
rel_target_bin_sim,
app_dir + "/libgodot." + platform + ".release.xcframework/" + framework_dir_sim + "/libgodot.a",
)
if dbg_target_bin_sim != "":
print(f' Copying "{platform}" (simulator) debug framework')
shutil.copy(
dbg_target_bin_sim,
app_dir + "/libgodot." + platform + ".debug.xcframework/" + framework_dir_sim + "/libgodot.a",
)
lipo_and_copy_apple_embedded(platform, framework_dir, framework_dir_sim, rel_prefix, dbg_prefix, "", app_dir, env)
if "MODULES_EXTERNAL" in env:
for mod in env["MODULES_EXTERNAL"]:
lipo_and_copy_apple_embedded(
platform, framework_dir, framework_dir_sim, rel_prefix, dbg_prefix, mod, app_dir, env
)
# Remove other platform xcframeworks
for entry in os.listdir(app_dir):