Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/modules/camera/camera_feed_linux.cpp
10277 views
1
/**************************************************************************/
2
/* camera_feed_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_feed_linux.h"
32
33
#include "servers/rendering_server.h"
34
35
#include <fcntl.h>
36
#include <sys/ioctl.h>
37
#include <sys/mman.h>
38
#include <unistd.h>
39
40
void CameraFeedLinux::update_buffer_thread_func(void *p_func) {
41
if (p_func) {
42
CameraFeedLinux *camera_feed_linux = (CameraFeedLinux *)p_func;
43
camera_feed_linux->_update_buffer();
44
}
45
}
46
47
void CameraFeedLinux::_update_buffer() {
48
while (!exit_flag.is_set()) {
49
_read_frame();
50
usleep(10000);
51
}
52
}
53
54
void CameraFeedLinux::_query_device(const String &p_device_name) {
55
file_descriptor = open(p_device_name.ascii().get_data(), O_RDWR | O_NONBLOCK, 0);
56
ERR_FAIL_COND_MSG(file_descriptor == -1, vformat("Cannot open file descriptor for %s. Error: %d.", p_device_name, errno));
57
58
struct v4l2_capability capability;
59
if (ioctl(file_descriptor, VIDIOC_QUERYCAP, &capability) == -1) {
60
ERR_FAIL_MSG(vformat("Cannot query device. Error: %d.", errno));
61
}
62
name = String((char *)capability.card);
63
64
for (int index = 0;; index++) {
65
struct v4l2_fmtdesc fmtdesc;
66
memset(&fmtdesc, 0, sizeof(fmtdesc));
67
fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
68
fmtdesc.index = index;
69
70
if (ioctl(file_descriptor, VIDIOC_ENUM_FMT, &fmtdesc) == -1) {
71
break;
72
}
73
74
for (int res_index = 0;; res_index++) {
75
struct v4l2_frmsizeenum frmsizeenum;
76
memset(&frmsizeenum, 0, sizeof(frmsizeenum));
77
frmsizeenum.pixel_format = fmtdesc.pixelformat;
78
frmsizeenum.index = res_index;
79
80
if (ioctl(file_descriptor, VIDIOC_ENUM_FRAMESIZES, &frmsizeenum) == -1) {
81
break;
82
}
83
84
for (int framerate_index = 0;; framerate_index++) {
85
struct v4l2_frmivalenum frmivalenum;
86
memset(&frmivalenum, 0, sizeof(frmivalenum));
87
frmivalenum.pixel_format = fmtdesc.pixelformat;
88
frmivalenum.width = frmsizeenum.discrete.width;
89
frmivalenum.height = frmsizeenum.discrete.height;
90
frmivalenum.index = framerate_index;
91
92
if (ioctl(file_descriptor, VIDIOC_ENUM_FRAMEINTERVALS, &frmivalenum) == -1) {
93
if (framerate_index == 0) {
94
_add_format(fmtdesc, frmsizeenum.discrete, -1, 1);
95
}
96
break;
97
}
98
99
_add_format(fmtdesc, frmsizeenum.discrete, frmivalenum.discrete.numerator, frmivalenum.discrete.denominator);
100
}
101
}
102
}
103
104
close(file_descriptor);
105
}
106
107
void CameraFeedLinux::_add_format(v4l2_fmtdesc p_description, v4l2_frmsize_discrete p_size, int p_frame_numerator, int p_frame_denominator) {
108
FeedFormat feed_format;
109
feed_format.width = p_size.width;
110
feed_format.height = p_size.height;
111
feed_format.format = String((char *)p_description.description);
112
feed_format.frame_numerator = p_frame_numerator;
113
feed_format.frame_denominator = p_frame_denominator;
114
feed_format.pixel_format = p_description.pixelformat;
115
print_verbose(vformat("%s %dx%d@%d/%dfps", (char *)p_description.description, p_size.width, p_size.height, p_frame_denominator, p_frame_numerator));
116
formats.push_back(feed_format);
117
}
118
119
bool CameraFeedLinux::_request_buffers() {
120
struct v4l2_requestbuffers requestbuffers;
121
122
memset(&requestbuffers, 0, sizeof(requestbuffers));
123
requestbuffers.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
124
requestbuffers.memory = V4L2_MEMORY_MMAP;
125
requestbuffers.count = 4;
126
127
if (ioctl(file_descriptor, VIDIOC_REQBUFS, &requestbuffers) == -1) {
128
ERR_FAIL_V_MSG(false, vformat("ioctl(VIDIOC_REQBUFS) error: %d.", errno));
129
}
130
131
ERR_FAIL_COND_V_MSG(requestbuffers.count < 2, false, "Not enough buffers granted.");
132
133
buffer_count = requestbuffers.count;
134
buffers = new StreamingBuffer[buffer_count];
135
136
for (unsigned int i = 0; i < buffer_count; i++) {
137
struct v4l2_buffer buffer;
138
139
memset(&buffer, 0, sizeof(buffer));
140
buffer.type = requestbuffers.type;
141
buffer.memory = V4L2_MEMORY_MMAP;
142
buffer.index = i;
143
144
if (ioctl(file_descriptor, VIDIOC_QUERYBUF, &buffer) == -1) {
145
delete[] buffers;
146
ERR_FAIL_V_MSG(false, vformat("ioctl(VIDIOC_QUERYBUF) error: %d.", errno));
147
}
148
149
buffers[i].length = buffer.length;
150
buffers[i].start = mmap(nullptr, buffer.length, PROT_READ | PROT_WRITE, MAP_SHARED, file_descriptor, buffer.m.offset);
151
152
if (buffers[i].start == MAP_FAILED) {
153
for (unsigned int b = 0; b < i; b++) {
154
_unmap_buffers(i);
155
}
156
delete[] buffers;
157
ERR_FAIL_V_MSG(false, "Mapping buffers failed.");
158
}
159
}
160
161
return true;
162
}
163
164
bool CameraFeedLinux::_start_capturing() {
165
for (unsigned int i = 0; i < buffer_count; i++) {
166
struct v4l2_buffer buffer;
167
168
memset(&buffer, 0, sizeof(buffer));
169
buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
170
buffer.memory = V4L2_MEMORY_MMAP;
171
buffer.index = i;
172
173
if (ioctl(file_descriptor, VIDIOC_QBUF, &buffer) == -1) {
174
ERR_FAIL_V_MSG(false, vformat("ioctl(VIDIOC_QBUF) error: %d.", errno));
175
}
176
}
177
178
enum v4l2_buf_type type;
179
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
180
181
if (ioctl(file_descriptor, VIDIOC_STREAMON, &type) == -1) {
182
ERR_FAIL_V_MSG(false, vformat("ioctl(VIDIOC_STREAMON) error: %d.", errno));
183
}
184
185
return true;
186
}
187
188
void CameraFeedLinux::_read_frame() {
189
struct v4l2_buffer buffer;
190
memset(&buffer, 0, sizeof(buffer));
191
buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
192
buffer.memory = V4L2_MEMORY_MMAP;
193
194
if (ioctl(file_descriptor, VIDIOC_DQBUF, &buffer) == -1) {
195
if (errno != EAGAIN) {
196
print_error(vformat("ioctl(VIDIOC_DQBUF) error: %d.", errno));
197
exit_flag.set();
198
}
199
return;
200
}
201
202
buffer_decoder->decode(buffers[buffer.index]);
203
204
if (ioctl(file_descriptor, VIDIOC_QBUF, &buffer) == -1) {
205
print_error(vformat("ioctl(VIDIOC_QBUF) error: %d.", errno));
206
}
207
}
208
209
void CameraFeedLinux::_stop_capturing() {
210
enum v4l2_buf_type type;
211
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
212
213
if (ioctl(file_descriptor, VIDIOC_STREAMOFF, &type) == -1) {
214
print_error(vformat("ioctl(VIDIOC_STREAMOFF) error: %d.", errno));
215
}
216
}
217
218
void CameraFeedLinux::_unmap_buffers(unsigned int p_count) {
219
for (unsigned int i = 0; i < p_count; i++) {
220
munmap(buffers[i].start, buffers[i].length);
221
}
222
}
223
224
void CameraFeedLinux::_start_thread() {
225
exit_flag.clear();
226
thread = memnew(Thread);
227
thread->start(CameraFeedLinux::update_buffer_thread_func, this);
228
}
229
230
String CameraFeedLinux::get_device_name() const {
231
return device_name;
232
}
233
234
bool CameraFeedLinux::activate_feed() {
235
ERR_FAIL_COND_V_MSG(selected_format == -1, false, "CameraFeed format needs to be set before activating.");
236
file_descriptor = open(device_name.ascii().get_data(), O_RDWR | O_NONBLOCK, 0);
237
if (_request_buffers() && _start_capturing()) {
238
buffer_decoder = _create_buffer_decoder();
239
_start_thread();
240
return true;
241
}
242
ERR_FAIL_V_MSG(false, "Could not activate feed.");
243
}
244
245
BufferDecoder *CameraFeedLinux::_create_buffer_decoder() {
246
switch (formats[selected_format].pixel_format) {
247
case V4L2_PIX_FMT_MJPEG:
248
case V4L2_PIX_FMT_JPEG:
249
return memnew(JpegBufferDecoder(this));
250
case V4L2_PIX_FMT_YUYV:
251
case V4L2_PIX_FMT_YYUV:
252
case V4L2_PIX_FMT_YVYU:
253
case V4L2_PIX_FMT_UYVY:
254
case V4L2_PIX_FMT_VYUY: {
255
String output = parameters["output"];
256
if (output == "separate") {
257
return memnew(SeparateYuyvBufferDecoder(this));
258
}
259
if (output == "grayscale") {
260
return memnew(YuyvToGrayscaleBufferDecoder(this));
261
}
262
if (output == "copy") {
263
return memnew(CopyBufferDecoder(this, false));
264
}
265
return memnew(YuyvToRgbBufferDecoder(this));
266
}
267
default:
268
return memnew(CopyBufferDecoder(this, true));
269
}
270
}
271
272
void CameraFeedLinux::deactivate_feed() {
273
exit_flag.set();
274
thread->wait_to_finish();
275
memdelete(thread);
276
_stop_capturing();
277
_unmap_buffers(buffer_count);
278
delete[] buffers;
279
memdelete(buffer_decoder);
280
for (int i = 0; i < CameraServer::FEED_IMAGES; i++) {
281
RID placeholder = RenderingServer::get_singleton()->texture_2d_placeholder_create();
282
RenderingServer::get_singleton()->texture_replace(texture[i], placeholder);
283
}
284
base_width = 0;
285
base_height = 0;
286
close(file_descriptor);
287
288
emit_signal(SNAME("format_changed"));
289
}
290
291
Array CameraFeedLinux::get_formats() const {
292
Array result;
293
for (const FeedFormat &format : formats) {
294
Dictionary dictionary;
295
dictionary["width"] = format.width;
296
dictionary["height"] = format.height;
297
dictionary["format"] = format.format;
298
dictionary["frame_numerator"] = format.frame_numerator;
299
dictionary["frame_denominator"] = format.frame_denominator;
300
result.push_back(dictionary);
301
}
302
return result;
303
}
304
305
CameraFeed::FeedFormat CameraFeedLinux::get_format() const {
306
FeedFormat feed_format = {};
307
return selected_format == -1 ? feed_format : formats[selected_format];
308
}
309
310
bool CameraFeedLinux::set_format(int p_index, const Dictionary &p_parameters) {
311
ERR_FAIL_COND_V_MSG(active, false, "Feed is active.");
312
ERR_FAIL_INDEX_V_MSG(p_index, formats.size(), false, "Invalid format index.");
313
314
FeedFormat feed_format = formats[p_index];
315
316
file_descriptor = open(device_name.ascii().get_data(), O_RDWR | O_NONBLOCK, 0);
317
318
struct v4l2_format format;
319
memset(&format, 0, sizeof(format));
320
format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
321
format.fmt.pix.width = feed_format.width;
322
format.fmt.pix.height = feed_format.height;
323
format.fmt.pix.pixelformat = feed_format.pixel_format;
324
325
if (ioctl(file_descriptor, VIDIOC_S_FMT, &format) == -1) {
326
close(file_descriptor);
327
ERR_FAIL_V_MSG(false, vformat("Cannot set format, error: %d.", errno));
328
}
329
330
if (feed_format.frame_numerator > 0) {
331
struct v4l2_streamparm param;
332
memset(&param, 0, sizeof(param));
333
334
param.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
335
param.parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
336
param.parm.capture.timeperframe.numerator = feed_format.frame_numerator;
337
param.parm.capture.timeperframe.denominator = feed_format.frame_denominator;
338
339
if (ioctl(file_descriptor, VIDIOC_S_PARM, &param) == -1) {
340
close(file_descriptor);
341
ERR_FAIL_V_MSG(false, vformat("Cannot set framerate, error: %d.", errno));
342
}
343
}
344
close(file_descriptor);
345
346
parameters = p_parameters.duplicate();
347
selected_format = p_index;
348
emit_signal(SNAME("format_changed"));
349
350
return true;
351
}
352
353
CameraFeedLinux::CameraFeedLinux(const String &p_device_name) :
354
CameraFeed() {
355
device_name = p_device_name;
356
_query_device(device_name);
357
}
358
359
CameraFeedLinux::~CameraFeedLinux() {
360
if (is_active()) {
361
deactivate_feed();
362
}
363
}
364
365