21 #if defined(HAVE_VIDEO4LINUX) && !defined(CPPPARSER) 25 #include <sys/ioctl.h> 38 #define clamp(x) std::min(std::max(x, 0.0), 255.0) 40 INLINE
static void yuv_to_bgr(
unsigned char *dest,
const unsigned char *src) {
41 double y1 = (255 / 219.0) * (src[0] - 16);
42 double pb = (255 / 224.0) * (src[1] - 128);
43 double pr = (255 / 224.0) * (src[2] - 128);
44 dest[2] = clamp(1.0 * y1 + 0 * pb + 1.402 * pr);
45 dest[1] = clamp(1.0 * y1 - 0.344 * pb - 0.714 * pr);
46 dest[0] = clamp(1.0 * y1 + 1.772 * pb + 0 * pr);
49 INLINE
static void yuyv_to_bgrbgr(
unsigned char *dest,
const unsigned char *src) {
50 unsigned char yuv[] = {src[0], src[1], src[3]};
51 yuv_to_bgr(dest, yuv);
53 yuv_to_bgr(dest + 3, yuv);
56 INLINE
static void yuyv_to_bgrabgra(
unsigned char *dest,
const unsigned char *src) {
57 unsigned char yuv[] = {src[0], src[1], src[3]};
58 yuv_to_bgr(dest, yuv);
60 yuv_to_bgr(dest + 4, yuv);
65 INLINE
static void rgb_to_bgr(
unsigned char *dest,
const unsigned char *src) {
71 INLINE
static void rgb_to_bgra(
unsigned char *dest,
const unsigned char *src) {
78 #if defined(HAVE_JPEG) && !defined(CPPPARSER) 81 struct jpeg_error_mgr pub;
82 jmp_buf setjmp_buffer;
85 typedef struct my_error_mgr *my_error_ptr;
87 static void my_error_exit (j_common_ptr cinfo) {
89 char buffer[JMSG_LENGTH_MAX];
90 (*cinfo->err->format_message) (cinfo, buffer);
91 vision_cat.error() << buffer <<
"\n";
93 my_error_ptr myerr = (my_error_ptr) cinfo->err;
95 longjmp(myerr->setjmp_buffer, 1);
98 static void my_output_message (j_common_ptr cinfo){
99 char buffer[JMSG_LENGTH_MAX];
100 (*cinfo->err->format_message) (cinfo, buffer);
101 vision_cat.warning() << buffer <<
"\n";
104 static void my_init_source(j_decompress_ptr cinfo) {
107 static boolean my_fill_input_buffer(j_decompress_ptr cinfo) {
108 struct jpeg_source_mgr *src = cinfo->src;
109 static JOCTET FakeEOI[] = {0xFF, JPEG_EOI};
111 WARNMS(cinfo, JWRN_JPEG_EOF);
113 src->next_input_byte = FakeEOI;
114 src->bytes_in_buffer = 2;
119 static void my_skip_input_data(j_decompress_ptr cinfo,
long num_bytes) {
120 struct jpeg_source_mgr *src = cinfo->src;
122 if (num_bytes >= (
long) src->bytes_in_buffer) {
123 my_fill_input_buffer(cinfo);
127 src->bytes_in_buffer -= num_bytes;
128 src->next_input_byte += num_bytes;
131 static void my_term_source(j_decompress_ptr cinfo) {
135 static JHUFF_TBL dc_luminance_tbl = {
136 {0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0},
137 {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11},
141 static JHUFF_TBL dc_chrominance_tbl = {
142 {0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0},
143 {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11},
147 static JHUFF_TBL ac_luminance_tbl = {
148 {0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d},
150 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21,
151 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71,
152 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1,
153 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72,
154 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25,
155 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37,
156 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
157 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
158 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a,
159 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83,
160 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93,
161 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3,
162 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3,
163 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3,
164 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3,
165 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
166 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1,
167 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa
172 static JHUFF_TBL ac_chrominance_tbl = {
173 {0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77},
175 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31,
176 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22,
177 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1, 0xc1,
178 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1,
179 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18,
180 0x19, 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36,
181 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47,
182 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
183 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
184 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a,
185 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a,
186 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a,
187 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa,
188 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
189 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca,
190 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
191 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
192 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa
202 WebcamVideoCursorV4L::
204 _size_x = src->_size_x;
205 _size_y = src->_size_y;
209 _can_seek_fast =
false;
213 memset(&_format, 0,
sizeof(
struct v4l2_format));
223 _fd = open(src->_device.c_str(), mode);
225 vision_cat.error() <<
"Failed to open " << src->_device.c_str() <<
"\n";
231 _format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
232 _format.fmt.pix.pixelformat = src->_pformat;
234 switch (_format.fmt.pix.pixelformat) {
236 case V4L2_PIX_FMT_MJPEG:
241 case V4L2_PIX_FMT_YUYV:
245 case V4L2_PIX_FMT_BGR24:
249 case V4L2_PIX_FMT_BGR32:
253 case V4L2_PIX_FMT_RGB24:
257 case V4L2_PIX_FMT_RGB32:
261 case V4L2_PIX_FMT_GREY:
266 vision_cat.error() <<
"Unsupported pixel format " << src->get_pixel_format() <<
"!\n";
274 _format.fmt.pix.width = _size_x;
275 _format.fmt.pix.height = _size_y;
276 _format.fmt.pix.field = V4L2_FIELD_NONE;
279 if (-1 == ioctl(_fd, VIDIOC_S_FMT, &_format)) {
280 vision_cat.error() <<
"Driver rejected format!\n";
287 _size_x = _format.fmt.pix.width;
288 _size_y = _format.fmt.pix.height;
290 struct v4l2_streamparm streamparm;
291 memset(&streamparm, 0,
sizeof streamparm);
292 streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
293 streamparm.parm.capture.timeperframe.numerator = 1;
294 streamparm.parm.capture.timeperframe.denominator = src->_fps;
295 if (ioctl(_fd, VIDIOC_S_PARM, &streamparm) < 0) {
296 vision_cat.error() <<
"Driver rejected framerate!\n";
299 struct v4l2_requestbuffers req;
300 memset(&req, 0,
sizeof req);
302 req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
303 req.memory = V4L2_MEMORY_MMAP;
305 if (-1 == ioctl (_fd, VIDIOC_REQBUFS, &req)) {
306 vision_cat.error() <<
"Failed to request buffers from webcam!\n";
310 vision_cat.error() <<
"Insufficient buffer memory!\n";
313 _bufcount = req.count;
314 _buffers = (
void **) calloc (req.count, sizeof (
void*));
315 _buflens = (
size_t*) calloc (req.count, sizeof (
size_t));
317 if (!_buffers || !_buflens) {
318 vision_cat.error() <<
"Not enough memory!\n";
322 struct v4l2_buffer buf;
323 for (
unsigned int i = 0; i < (
unsigned int)_bufcount; ++i) {
324 memset(&buf, 0,
sizeof buf);
325 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
326 buf.memory = V4L2_MEMORY_MMAP;
329 if (-1 == ioctl(_fd, VIDIOC_QUERYBUF, &buf)) {
330 vision_cat.error() <<
"Failed to query buffer!\n";
333 _buflens[i] = buf.length;
334 _buffers[i] = mmap (
nullptr, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, _fd, buf.m.offset);
336 if (_buffers[i] == MAP_FAILED) {
337 vision_cat.error() <<
"Failed to map buffer!\n";
340 if (-1 == ioctl(_fd, VIDIOC_QBUF, &buf)) {
341 vision_cat.error() <<
"Failed to exchange buffer with driver!\n";
345 enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
346 if (-1 == ioctl(_fd, VIDIOC_STREAMON, &type)) {
347 vision_cat.error() <<
"Failed to stream from buffer!\n";
352 if (_format.fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG) {
353 jpeg_create_decompress(&_cinfo);
355 _cinfo.src = (
struct jpeg_source_mgr *)
356 (*_cinfo.mem->alloc_small) ((j_common_ptr) &_cinfo, JPOOL_PERMANENT,
357 sizeof(
struct jpeg_source_mgr));
359 _cinfo.src->init_source = my_init_source;
360 _cinfo.src->fill_input_buffer = my_fill_input_buffer;
361 _cinfo.src->skip_input_data = my_skip_input_data;
362 _cinfo.src->resync_to_restart = jpeg_resync_to_restart;
363 _cinfo.src->term_source = my_term_source;
372 WebcamVideoCursorV4L::
373 ~WebcamVideoCursorV4L() {
375 if (_format.fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG) {
376 jpeg_destroy_decompress(&_cinfo);
380 enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
381 ioctl(_fd, VIDIOC_STREAMOFF, &type);
385 for (
unsigned int i = 0; i < (
unsigned int)_bufcount; ++i) {
386 munmap(_buffers[i], _buflens[i]);
404 PT(
Buffer) buffer = get_standard_buffer();
405 unsigned char *block = buffer->_block;
406 struct v4l2_buffer vbuf;
407 memset(&vbuf, 0,
sizeof vbuf);
408 vbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
409 vbuf.memory = V4L2_MEMORY_MMAP;
410 if (-1 == ioctl(_fd, VIDIOC_DQBUF, &vbuf) && errno != EIO) {
411 if (errno == EAGAIN) {
415 vision_cat.error() <<
"Failed to dequeue buffer!\n";
418 nassertr(vbuf.index < _bufcount,
nullptr);
419 size_t bufsize = _buflens[vbuf.index];
420 size_t old_bpl = _format.fmt.pix.bytesperline;
421 size_t new_bpl = _size_x * _num_components;
422 unsigned char *buf = (
unsigned char *) _buffers[vbuf.index];
424 switch (_format.fmt.pix.pixelformat) {
425 case V4L2_PIX_FMT_MJPEG: {
427 struct my_error_mgr jerr;
428 _cinfo.err = jpeg_std_error(&jerr.pub);
429 jerr.pub.error_exit = my_error_exit;
430 jerr.pub.output_message = my_output_message;
432 unsigned char *newbuf = (
unsigned char*) malloc(new_bpl * _size_y);
435 if (setjmp(jerr.setjmp_buffer)) {
436 jpeg_abort_decompress(&_cinfo);
439 _cinfo.src->bytes_in_buffer = bufsize;
440 _cinfo.src->next_input_byte = buf;
442 if (jpeg_read_header(&_cinfo, TRUE) == JPEG_HEADER_OK) {
443 if (_cinfo.dc_huff_tbl_ptrs[0] ==
nullptr) {
445 _cinfo.dc_huff_tbl_ptrs[0] = &dc_luminance_tbl;
446 _cinfo.dc_huff_tbl_ptrs[1] = &dc_chrominance_tbl;
447 _cinfo.ac_huff_tbl_ptrs[0] = &ac_luminance_tbl;
448 _cinfo.ac_huff_tbl_ptrs[1] = &ac_chrominance_tbl;
451 _cinfo.scale_num = 1;
452 _cinfo.scale_denom = 1;
453 _cinfo.out_color_space = JCS_RGB;
454 _cinfo.dct_method = JDCT_IFAST;
456 if (jpeg_start_decompress(&_cinfo) && _cinfo.output_components == 3
457 && _size_x == _cinfo.output_width && _size_y == _cinfo.output_height) {
459 JSAMPLE *buffer_end = newbuf + new_bpl * _cinfo.output_height;
460 JSAMPLE *rowptr = newbuf;
461 while (_cinfo.output_scanline < _cinfo.output_height) {
462 nassertd(rowptr + new_bpl <= buffer_end)
break;
463 jpeg_read_scanlines(&_cinfo, &rowptr, _cinfo.output_height);
467 if (_cinfo.output_scanline < _cinfo.output_height) {
468 jpeg_abort_decompress(&_cinfo);
470 jpeg_finish_decompress(&_cinfo);
477 for (
int row = 0; row < _size_y; ++row) {
478 memcpy(block + (_size_y - row - 1) * new_bpl, newbuf + row * new_bpl, new_bpl);
484 for (
size_t i = 0; i < new_bpl * _size_y; i += 3) {
486 block[i] = block[i + 2];
490 nassert_raise(
"JPEG support not compiled-in");
495 case V4L2_PIX_FMT_YUYV:
496 for (
size_t row = 0; row < _size_y; ++row) {
498 for (
size_t i = 0; i < old_bpl; i += 4) {
499 yuyv_to_bgrbgr(block + (_size_y - row - 1) * new_bpl + c, buf + row * old_bpl + i);
505 case V4L2_PIX_FMT_BGR24:
506 case V4L2_PIX_FMT_BGR32:
507 case V4L2_PIX_FMT_GREY:
509 nassertr(old_bpl == new_bpl,
nullptr);
511 for (
size_t row = 0; row < _size_y; ++row) {
512 memcpy(block + (_size_y - row - 1) * new_bpl, buf + row * old_bpl, new_bpl);
516 case V4L2_PIX_FMT_RGB24:
518 nassertr(old_bpl == new_bpl,
nullptr);
520 for (
size_t row = 0; row < _size_y; ++row) {
521 for (
size_t i = 0; i < old_bpl; i += 3) {
522 rgb_to_bgr(block + (_size_y - row - 1) * old_bpl + i, buf + row * old_bpl + i);
527 case V4L2_PIX_FMT_RGB32:
529 nassertr(old_bpl == new_bpl,
nullptr);
531 for (
size_t row = 0; row < _size_y; ++row) {
532 for (
size_t i = 0; i < old_bpl; i += 4) {
533 rgb_to_bgra(block + (_size_y - row - 1) * old_bpl + i, buf + row * old_bpl + i + 1);
539 if (-1 == ioctl(_fd, VIDIOC_QBUF, &vbuf)) {
540 vision_cat.error() <<
"Failed to exchange buffer with driver!\n";
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A MovieVideo is actually any source that provides a sequence of video frames.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
TypeHandle is the identifier used to differentiate C++ class types.