|
| 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 | + CameraFeedWeb *feed = reinterpret_cast<CameraFeedWeb *>(context); |
| 37 | + if (error) { |
| 38 | + if (feed->is_active()) { |
| 39 | + feed->deactivate_feed(); |
| 40 | + }; |
| 41 | + String error_str = String::utf8(error); |
| 42 | + ERR_PRINT(vformat("Camera feed error from JS: %s", error_str)); |
| 43 | + return; |
| 44 | + } |
| 45 | + |
| 46 | + if (context == nullptr || rawdata == nullptr || length < 0 || p_width <= 0 || p_height <= 0) { |
| 47 | + if (feed->is_active()) { |
| 48 | + feed->deactivate_feed(); |
| 49 | + }; |
| 50 | + ERR_PRINT("Camera feed error: Invalid pixel data received."); |
| 51 | + return; |
| 52 | + } |
| 53 | + |
| 54 | + Vector<uint8_t> data = feed->data; |
| 55 | + Ref<Image> image = feed->image; |
| 56 | + |
| 57 | + if (length != data.size()) { |
| 58 | + int64_t size = Image::get_image_data_size(p_width, p_height, Image::FORMAT_RGBA8, false); |
| 59 | + data.resize(length > size ? length : size); |
| 60 | + } |
| 61 | + memcpy(data.ptrw(), rawdata, length); |
| 62 | + |
| 63 | + image->initialize_data(p_width, p_height, false, Image::FORMAT_RGBA8, data); |
| 64 | + feed->set_rgb_image(image); |
| 65 | + feed->emit_signal(SNAME("frame_changed")); |
| 66 | +} |
| 67 | + |
| 68 | +void CameraFeedWeb::_on_denied_callback(void *context) { |
| 69 | + CameraFeedWeb *feed = reinterpret_cast<CameraFeedWeb *>(context); |
| 70 | + feed->deactivate_feed(); |
| 71 | +} |
| 72 | + |
| 73 | +bool CameraFeedWeb::activate_feed() { |
| 74 | + ERR_FAIL_COND_V_MSG(selected_format == -1, false, "CameraFeed format needs to be set before activating."); |
| 75 | + |
| 76 | + CameraFeed::FeedFormat f = formats[selected_format]; |
| 77 | + int width = parameters.get(KEY_WIDTH, 0); |
| 78 | + int height = parameters.get(KEY_HEIGHT, 0); |
| 79 | + width = width > 0 ? width : f.width; |
| 80 | + height = height > 0 ? height : f.height; |
| 81 | + CameraDriverWeb::get_singleton()->get_pixel_data(this, device_id, width, height, &_on_get_pixeldata, &_on_denied_callback); |
| 82 | + return true; |
| 83 | +} |
| 84 | + |
| 85 | +void CameraFeedWeb::deactivate_feed() { |
| 86 | + CameraDriverWeb::get_singleton()->stop_stream(device_id); |
| 87 | +} |
| 88 | + |
| 89 | +bool CameraFeedWeb::set_format(int p_index, const Dictionary &p_parameters) { |
| 90 | + ERR_FAIL_COND_V_MSG(active, false, "Feed is active."); |
| 91 | + ERR_FAIL_INDEX_V_MSG(p_index, formats.size(), false, "Invalid format index."); |
| 92 | + |
| 93 | + selected_format = p_index; |
| 94 | + parameters = p_parameters; |
| 95 | + return true; |
| 96 | +} |
| 97 | + |
| 98 | +Array CameraFeedWeb::get_formats() const { |
| 99 | + Array result; |
| 100 | + for (const FeedFormat &feed_format : formats) { |
| 101 | + Dictionary dictionary; |
| 102 | + dictionary["width"] = feed_format.width; |
| 103 | + dictionary["height"] = feed_format.height; |
| 104 | + dictionary["format"] = feed_format.format; |
| 105 | + result.push_back(dictionary); |
| 106 | + } |
| 107 | + return result; |
| 108 | +} |
| 109 | + |
| 110 | +CameraFeed::FeedFormat CameraFeedWeb::get_format() const { |
| 111 | + CameraFeed::FeedFormat feed_format = {}; |
| 112 | + return selected_format == -1 ? feed_format : formats[selected_format]; |
| 113 | +} |
| 114 | + |
| 115 | +CameraFeedWeb::CameraFeedWeb(const CameraInfo &info) { |
| 116 | + name = info.label; |
| 117 | + device_id = info.device_id; |
| 118 | + |
| 119 | + Vector<CapabilityInfo> capabilities; |
| 120 | + CameraDriverWeb::get_singleton()->get_capabilities(&capabilities, device_id); |
| 121 | + for (int i = 0; i < capabilities.size(); i++) { |
| 122 | + CapabilityInfo capability = capabilities[i]; |
| 123 | + FeedFormat feed_format; |
| 124 | + feed_format.width = capability.width; |
| 125 | + feed_format.height = capability.height; |
| 126 | + feed_format.format = String("RGBA"); |
| 127 | + formats.append(feed_format); |
| 128 | + } |
| 129 | + |
| 130 | + image.instantiate(); |
| 131 | +} |
| 132 | + |
| 133 | +CameraFeedWeb::~CameraFeedWeb() { |
| 134 | + if (is_active()) { |
| 135 | + deactivate_feed(); |
| 136 | + } |
| 137 | +} |
| 138 | + |
| 139 | +void CameraWeb::_update_feeds() { |
| 140 | + for (int i = feeds.size() - 1; i >= 0; i--) { |
| 141 | + remove_feed(feeds[i]); |
| 142 | + } |
| 143 | + |
| 144 | + Vector<CameraInfo> camera_info; |
| 145 | + camera_driver_web->get_cameras(&camera_info); |
| 146 | + for (int i = 0; i < camera_info.size(); i++) { |
| 147 | + CameraInfo info = camera_info[i]; |
| 148 | + Ref<CameraFeedWeb> feed = memnew(CameraFeedWeb(info)); |
| 149 | + add_feed(feed); |
| 150 | + } |
| 151 | +} |
| 152 | + |
| 153 | +void CameraWeb::_cleanup() { |
| 154 | + if (camera_driver_web != nullptr) { |
| 155 | + camera_driver_web->stop_stream(); |
| 156 | + memdelete(camera_driver_web); |
| 157 | + camera_driver_web = nullptr; |
| 158 | + } |
| 159 | +} |
| 160 | + |
| 161 | +void CameraWeb::set_monitoring_feeds(bool p_monitoring_feeds) { |
| 162 | + if (p_monitoring_feeds == monitoring_feeds) { |
| 163 | + return; |
| 164 | + } |
| 165 | + |
| 166 | + CameraServer::set_monitoring_feeds(p_monitoring_feeds); |
| 167 | + if (p_monitoring_feeds) { |
| 168 | + if (camera_driver_web == nullptr) { |
| 169 | + camera_driver_web = new CameraDriverWeb(); |
| 170 | + } |
| 171 | + _update_feeds(); |
| 172 | + } else { |
| 173 | + _cleanup(); |
| 174 | + } |
| 175 | +} |
| 176 | + |
| 177 | +CameraWeb::~CameraWeb() { |
| 178 | + _cleanup(); |
| 179 | +} |
0 commit comments