Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/modules/camera/camera_linux.cpp
10277 views
1
/**************************************************************************/
2
/* camera_linux.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_linux.h"
32
33
#include "camera_feed_linux.h"
34
35
#include <dirent.h>
36
#include <fcntl.h>
37
#include <sys/ioctl.h>
38
#include <sys/stat.h>
39
#include <unistd.h>
40
41
void CameraLinux::camera_thread_func(void *p_camera_linux) {
42
if (p_camera_linux) {
43
CameraLinux *camera_linux = (CameraLinux *)p_camera_linux;
44
camera_linux->_update_devices();
45
}
46
}
47
48
void CameraLinux::_update_devices() {
49
while (!exit_flag.is_set()) {
50
{
51
MutexLock lock(camera_mutex);
52
53
for (int i = feeds.size() - 1; i >= 0; i--) {
54
Ref<CameraFeedLinux> feed = (Ref<CameraFeedLinux>)feeds[i];
55
if (feed.is_null()) {
56
continue;
57
}
58
String device_name = feed->get_device_name();
59
if (!_is_active(device_name)) {
60
remove_feed(feed);
61
}
62
}
63
64
struct dirent **devices;
65
int count = scandir("/dev", &devices, nullptr, alphasort);
66
67
if (count != -1) {
68
for (int i = 0; i < count; i++) {
69
struct dirent *device = devices[i];
70
if (strncmp(device->d_name, "video", 5) == 0) {
71
String device_name = String("/dev/") + String(device->d_name);
72
if (!_has_device(device_name)) {
73
_add_device(device_name);
74
}
75
}
76
free(device);
77
}
78
}
79
80
free(devices);
81
}
82
83
call_deferred("emit_signal", SNAME(CameraServer::feeds_updated_signal_name));
84
usleep(1000000);
85
}
86
}
87
88
bool CameraLinux::_has_device(const String &p_device_name) {
89
for (int i = 0; i < feeds.size(); i++) {
90
Ref<CameraFeedLinux> feed = (Ref<CameraFeedLinux>)feeds[i];
91
if (feed.is_null()) {
92
continue;
93
}
94
if (feed->get_device_name() == p_device_name) {
95
return true;
96
}
97
}
98
return false;
99
}
100
101
void CameraLinux::_add_device(const String &p_device_name) {
102
int file_descriptor = _open_device(p_device_name);
103
104
if (file_descriptor != -1) {
105
if (_is_video_capture_device(file_descriptor)) {
106
Ref<CameraFeedLinux> feed = memnew(CameraFeedLinux(p_device_name));
107
add_feed(feed);
108
}
109
}
110
111
close(file_descriptor);
112
}
113
114
int CameraLinux::_open_device(const String &p_device_name) {
115
struct stat s;
116
117
if (stat(p_device_name.ascii().get_data(), &s) == -1) {
118
return -1;
119
}
120
121
if (!S_ISCHR(s.st_mode)) {
122
return -1;
123
}
124
125
return open(p_device_name.ascii().get_data(), O_RDWR | O_NONBLOCK, 0);
126
}
127
128
// TODO any cheaper/cleaner way to check if file descriptor is invalid?
129
bool CameraLinux::_is_active(const String &p_device_name) {
130
struct v4l2_capability capability;
131
bool result = false;
132
int file_descriptor = _open_device(p_device_name);
133
if (file_descriptor != -1 && ioctl(file_descriptor, VIDIOC_QUERYCAP, &capability) != -1) {
134
result = true;
135
}
136
close(file_descriptor);
137
return result;
138
}
139
140
bool CameraLinux::_is_video_capture_device(int p_file_descriptor) {
141
struct v4l2_capability capability;
142
143
if (ioctl(p_file_descriptor, VIDIOC_QUERYCAP, &capability) == -1) {
144
return false;
145
}
146
147
if (!(capability.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
148
return false;
149
}
150
151
if (!(capability.capabilities & V4L2_CAP_STREAMING)) {
152
return false;
153
}
154
155
return _can_query_format(p_file_descriptor, V4L2_BUF_TYPE_VIDEO_CAPTURE);
156
}
157
158
bool CameraLinux::_can_query_format(int p_file_descriptor, int p_type) {
159
struct v4l2_format format;
160
memset(&format, 0, sizeof(format));
161
format.type = p_type;
162
163
return ioctl(p_file_descriptor, VIDIOC_G_FMT, &format) != -1;
164
}
165
166
inline void CameraLinux::set_monitoring_feeds(bool p_monitoring_feeds) {
167
if (p_monitoring_feeds == monitoring_feeds) {
168
return;
169
}
170
171
CameraServer::set_monitoring_feeds(p_monitoring_feeds);
172
if (p_monitoring_feeds) {
173
exit_flag.clear();
174
camera_thread.start(CameraLinux::camera_thread_func, this);
175
} else {
176
exit_flag.set();
177
if (camera_thread.is_started()) {
178
camera_thread.wait_to_finish();
179
}
180
}
181
}
182
183
CameraLinux::~CameraLinux() {
184
exit_flag.set();
185
if (camera_thread.is_started()) {
186
camera_thread.wait_to_finish();
187
}
188
}
189
190