Skip to content

Commit 140bdef

Browse files
committed
Add CameraFeed support for Web
1 parent 45fc515 commit 140bdef

File tree

10 files changed

+1028
-2
lines changed

10 files changed

+1028
-2
lines changed

modules/camera/SCsub

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Import("env_modules")
66

77
env_camera = env_modules.Clone()
88

9-
if env["platform"] in ["windows", "macos", "linuxbsd", "android"]:
9+
if env["platform"] in ["windows", "macos", "linuxbsd", "android", "web"]:
1010
env_camera.add_source_files(env.modules_sources, "register_types.cpp")
1111

1212
if env["platform"] == "windows":
@@ -23,3 +23,6 @@ elif env["platform"] == "linuxbsd":
2323
env_camera.add_source_files(env.modules_sources, "camera_linux.cpp")
2424
env_camera.add_source_files(env.modules_sources, "camera_feed_linux.cpp")
2525
env_camera.add_source_files(env.modules_sources, "buffer_decoder.cpp")
26+
27+
elif env["platform"] == "web":
28+
env_camera.add_source_files(env.modules_sources, "camera_web.cpp")

modules/camera/camera_web.cpp

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
/**************************************************************************/
2+
/* camera_web.cpp */
3+
/**************************************************************************/
4+
/* This file is part of: */
5+
/* GODOT ENGINE */
6+
/* https://godotengine.org */
7+
/**************************************************************************/
8+
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9+
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10+
/* */
11+
/* Permission is hereby granted, free of charge, to any person obtaining */
12+
/* a copy of this software and associated documentation files (the */
13+
/* "Software"), to deal in the Software without restriction, including */
14+
/* without limitation the rights to use, copy, modify, merge, publish, */
15+
/* distribute, sublicense, and/or sell copies of the Software, and to */
16+
/* permit persons to whom the Software is furnished to do so, subject to */
17+
/* the following conditions: */
18+
/* */
19+
/* The above copyright notice and this permission notice shall be */
20+
/* included in all copies or substantial portions of the Software. */
21+
/* */
22+
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23+
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24+
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25+
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26+
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27+
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28+
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29+
/**************************************************************************/
30+
31+
#include "camera_web.h"
32+
33+
#include "core/io/json.h"
34+
35+
void CameraFeedWeb::_on_get_pixeldata(void *context, const uint8_t *rawdata, const int length, const int p_width, const int p_height, const char *error) {
36+
String error_json_str = String::utf8(error);
37+
if (!error_json_str.is_empty()) {
38+
Variant json_variant = JSON::parse_string(error_json_str);
39+
if (json_variant.get_type() == Variant::DICTIONARY) {
40+
Dictionary json_dict = json_variant;
41+
String error_str = json_dict.get(KEY_ERROR, String());
42+
if (!error_str.is_empty()) {
43+
ERR_PRINT(vformat("Camera feed error from JS: %s", error_str));
44+
}
45+
} else {
46+
ERR_PRINT("Camera feed error: Failed to parse JSON response or response is not a Dictionary.");
47+
}
48+
}
49+
50+
CameraFeedWeb *feed = reinterpret_cast<CameraFeedWeb *>(context);
51+
Vector<uint8_t> data = feed->data;
52+
Ref<Image> image = feed->image;
53+
54+
if (length != data.size()) {
55+
int64_t size = Image::get_image_data_size(p_width, p_height, Image::FORMAT_RGBA8, false);
56+
data.resize(length > size ? length : size);
57+
}
58+
memcpy(data.ptrw(), rawdata, length);
59+
60+
image->initialize_data(p_width, p_height, false, Image::FORMAT_RGBA8, data);
61+
feed->set_rgb_image(image);
62+
feed->emit_signal(SNAME("frame_changed"));
63+
}
64+
65+
bool CameraFeedWeb::activate_feed() {
66+
ERR_FAIL_COND_V_MSG(selected_format == -1, false, "CameraFeed format needs to be set before activating.");
67+
if (is_active()) {
68+
deactivate_feed();
69+
};
70+
71+
CameraFeed::FeedFormat f = formats[selected_format];
72+
CameraDriverWeb::get_singleton()->get_pixel_data(this, device_id, f.width, f.height, &_on_get_pixeldata);
73+
return true;
74+
}
75+
76+
void CameraFeedWeb::deactivate_feed() {
77+
CameraDriverWeb::get_singleton()->stop_stream(device_id);
78+
}
79+
80+
bool CameraFeedWeb::set_format(int p_index, const Dictionary &p_parameters) {
81+
ERR_FAIL_COND_V_MSG(active, false, "Feed is active.");
82+
ERR_FAIL_INDEX_V_MSG(p_index, formats.size(), false, "Invalid format index.");
83+
84+
selected_format = p_index;
85+
return true;
86+
}
87+
88+
Array CameraFeedWeb::get_formats() const {
89+
Array result;
90+
for (const FeedFormat &feed_format : formats) {
91+
Dictionary dictionary;
92+
dictionary["width"] = feed_format.width;
93+
dictionary["height"] = feed_format.height;
94+
dictionary["format"] = feed_format.format;
95+
result.push_back(dictionary);
96+
}
97+
return result;
98+
}
99+
100+
CameraFeed::FeedFormat CameraFeedWeb::get_format() const {
101+
CameraFeed::FeedFormat feed_format = {};
102+
return selected_format == -1 ? feed_format : formats[selected_format];
103+
}
104+
105+
CameraFeedWeb::CameraFeedWeb(const CameraInfo &info) {
106+
name = info.label;
107+
device_id = info.device_id;
108+
109+
Vector<CapabilityInfo> capabilities;
110+
CameraDriverWeb::get_singleton()->get_capabilities(&capabilities, device_id);
111+
for (int i = 0; i < capabilities.size(); i++) {
112+
CapabilityInfo capability = capabilities[i];
113+
FeedFormat feed_format;
114+
feed_format.width = capability.width;
115+
feed_format.height = capability.height;
116+
feed_format.format = String("RGBA");
117+
formats.append(feed_format);
118+
}
119+
120+
image.instantiate();
121+
}
122+
123+
CameraFeedWeb::~CameraFeedWeb() {
124+
if (is_active()) {
125+
deactivate_feed();
126+
}
127+
}
128+
129+
void CameraWeb::_update_feeds() {
130+
for (int i = feeds.size() - 1; i >= 0; i--) {
131+
remove_feed(feeds[i]);
132+
}
133+
134+
Vector<CameraInfo> camera_info;
135+
camera_driver_web->get_cameras(&camera_info);
136+
for (int i = 0; i < camera_info.size(); i++) {
137+
CameraInfo info = camera_info[i];
138+
Ref<CameraFeedWeb> feed = memnew(CameraFeedWeb(info));
139+
add_feed(feed);
140+
}
141+
}
142+
143+
void CameraWeb::_cleanup() {
144+
if (camera_driver_web != nullptr) {
145+
camera_driver_web->stop_stream();
146+
memdelete(camera_driver_web);
147+
camera_driver_web = nullptr;
148+
}
149+
}
150+
151+
void CameraWeb::set_monitoring_feeds(bool p_monitoring_feeds) {
152+
if (p_monitoring_feeds == monitoring_feeds) {
153+
return;
154+
}
155+
156+
CameraServer::set_monitoring_feeds(p_monitoring_feeds);
157+
if (p_monitoring_feeds) {
158+
if (camera_driver_web == nullptr) {
159+
camera_driver_web = new CameraDriverWeb();
160+
}
161+
camera_driver_web->initialize_camera();
162+
_update_feeds();
163+
} else {
164+
_cleanup();
165+
}
166+
}
167+
168+
CameraWeb::~CameraWeb() {
169+
_cleanup();
170+
}

modules/camera/camera_web.h

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/**************************************************************************/
2+
/* camera_web.h */
3+
/**************************************************************************/
4+
/* This file is part of: */
5+
/* GODOT ENGINE */
6+
/* https://godotengine.org */
7+
/**************************************************************************/
8+
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9+
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10+
/* */
11+
/* Permission is hereby granted, free of charge, to any person obtaining */
12+
/* a copy of this software and associated documentation files (the */
13+
/* "Software"), to deal in the Software without restriction, including */
14+
/* without limitation the rights to use, copy, modify, merge, publish, */
15+
/* distribute, sublicense, and/or sell copies of the Software, and to */
16+
/* permit persons to whom the Software is furnished to do so, subject to */
17+
/* the following conditions: */
18+
/* */
19+
/* The above copyright notice and this permission notice shall be */
20+
/* included in all copies or substantial portions of the Software. */
21+
/* */
22+
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23+
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24+
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25+
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26+
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27+
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28+
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29+
/**************************************************************************/
30+
31+
#pragma once
32+
33+
#include "platform/web/camera_driver_web.h"
34+
#include "servers/camera/camera_feed.h"
35+
#include "servers/camera_server.h"
36+
37+
class CameraFeedWeb : public CameraFeed {
38+
GDCLASS(CameraFeedWeb, CameraFeed);
39+
40+
private:
41+
String device_id;
42+
Ref<Image> image;
43+
Vector<uint8_t> data;
44+
static void _on_get_pixeldata(void *, const uint8_t *, const int, const int, const int, const char *error);
45+
46+
protected:
47+
public:
48+
bool activate_feed() override;
49+
void deactivate_feed() override;
50+
bool set_format(int p_index, const Dictionary &p_parameters) override;
51+
Array get_formats() const override;
52+
FeedFormat get_format() const override;
53+
54+
CameraFeedWeb(const CameraInfo &info);
55+
~CameraFeedWeb();
56+
};
57+
58+
class CameraWeb : public CameraServer {
59+
GDCLASS(CameraWeb, CameraServer);
60+
61+
private:
62+
CameraDriverWeb *camera_driver_web = nullptr;
63+
void _cleanup();
64+
void _update_feeds();
65+
66+
protected:
67+
public:
68+
void set_monitoring_feeds(bool p_monitoring_feeds) override;
69+
70+
~CameraWeb();
71+
};

modules/camera/config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ def can_build(env, platform):
33

44
if sys.platform.startswith("freebsd"):
55
return False
6-
return platform == "macos" or platform == "windows" or platform == "linuxbsd" or platform == "android"
6+
return platform in ["macos", "windows", "linuxbsd", "android", "web"]
77

88

99
def configure(env):

modules/camera/register_types.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@
4242
#if defined(ANDROID_ENABLED)
4343
#include "camera_android.h"
4444
#endif
45+
#if defined(WEB_ENABLED)
46+
#include "camera_web.h"
47+
#endif
4548

4649
void initialize_camera_module(ModuleInitializationLevel p_level) {
4750
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
@@ -60,6 +63,9 @@ void initialize_camera_module(ModuleInitializationLevel p_level) {
6063
#if defined(ANDROID_ENABLED)
6164
CameraServer::make_default<CameraAndroid>();
6265
#endif
66+
#if defined(WEB_ENABLED)
67+
CameraServer::make_default<CameraWeb>();
68+
#endif
6369
}
6470

6571
void uninitialize_camera_module(ModuleInitializationLevel p_level) {

platform/web/SCsub

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ if "serve" in COMMAND_LINE_TARGETS or "run" in COMMAND_LINE_TARGETS:
2222

2323
web_files = [
2424
"audio_driver_web.cpp",
25+
"camera_driver_web.cpp",
2526
"webmidi_driver.cpp",
2627
"display_server_web.cpp",
2728
"http_client_web.cpp",
@@ -39,6 +40,7 @@ sys_env = env.Clone()
3940
sys_env.AddJSLibraries(
4041
[
4142
"js/libs/library_godot_audio.js",
43+
"js/libs/library_godot_camera.js",
4244
"js/libs/library_godot_display.js",
4345
"js/libs/library_godot_fetch.js",
4446
"js/libs/library_godot_webmidi.js",

0 commit comments

Comments
 (0)