FreeRDP
Loading...
Searching...
No Matches
camera_v4l.c
1
20#include <errno.h>
21#include <fcntl.h>
22#include <poll.h>
23#include <sys/ioctl.h>
24#include <sys/mman.h>
25
26/* v4l includes */
27#include <linux/videodev2.h>
28
29#include "camera_v4l.h"
30#include "uvc_h264.h"
31
32#define TAG CHANNELS_TAG("rdpecam-v4l.client")
33
34#define CAM_V4L2_BUFFERS_COUNT 4
35#define CAM_V4L2_CAPTURE_THREAD_SLEEP_MS 1000
36
37#define CAM_V4L2_FRAMERATE_NUMERATOR_DEFAULT 30
38#define CAM_V4L2_FRAMERATE_DENOMINATOR_DEFAULT 1
39
40typedef struct
41{
42 ICamHal iHal;
43
44 wHashTable* streams; /* Index: deviceId, Value: CamV4lStream */
45
46} CamV4lHal;
47
48static CamV4lStream* cam_v4l_stream_create(const char* deviceId, size_t streamIndex);
49static void cam_v4l_stream_free(void* obj);
50static void cam_v4l_stream_close_device(CamV4lStream* stream);
51static CAM_ERROR_CODE cam_v4l_stream_stop(CamV4lStream* stream);
52
58static const char* cam_v4l_get_fourcc_str(unsigned int fourcc, char* buffer, size_t size)
59{
60 if (size < 5)
61 return nullptr;
62
63 buffer[0] = (char)(fourcc & 0xFF);
64 buffer[1] = (char)((fourcc >> 8) & 0xFF);
65 buffer[2] = (char)((fourcc >> 16) & 0xFF);
66 buffer[3] = (char)((fourcc >> 24) & 0xFF);
67 buffer[4] = '\0';
68 return buffer;
69}
70
76static UINT32 ecamToV4L2PixFormat(CAM_MEDIA_FORMAT ecamFormat)
77{
78 switch (ecamFormat)
79 {
80 case CAM_MEDIA_FORMAT_H264:
81 return V4L2_PIX_FMT_H264;
82 case CAM_MEDIA_FORMAT_MJPG:
83 return V4L2_PIX_FMT_MJPEG;
84 case CAM_MEDIA_FORMAT_YUY2:
85 return V4L2_PIX_FMT_YUYV;
86 case CAM_MEDIA_FORMAT_NV12:
87 return V4L2_PIX_FMT_NV12;
88 case CAM_MEDIA_FORMAT_I420:
89 return V4L2_PIX_FMT_YUV420;
90 case CAM_MEDIA_FORMAT_RGB24:
91 return V4L2_PIX_FMT_RGB24;
92 case CAM_MEDIA_FORMAT_RGB32:
93 return V4L2_PIX_FMT_RGB32;
94 default:
95 WLog_ERR(TAG, "Unsupported CAM_MEDIA_FORMAT %u", ecamFormat);
96 return 0;
97 }
98}
99
105static BOOL cam_v4l_format_supported(int fd, UINT32 format)
106{
107 struct v4l2_fmtdesc fmtdesc = WINPR_C_ARRAY_INIT;
108 fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
109
110 for (fmtdesc.index = 0; ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc) == 0; fmtdesc.index++)
111 {
112 if (fmtdesc.pixelformat == format)
113 return TRUE;
114 }
115 return FALSE;
116}
117
123static int cam_v4l_open_device(const char* deviceId, int flags)
124{
125 char device[20] = WINPR_C_ARRAY_INIT;
126 int fd = -1;
127 struct v4l2_capability cap = WINPR_C_ARRAY_INIT;
128
129 if (!deviceId)
130 return -1;
131
132 if (0 == strncmp(deviceId, "/dev/video", 10))
133 return open(deviceId, flags);
134
135 for (UINT n = 0; n < 64; n++)
136 {
137 (void)_snprintf(device, sizeof(device), "/dev/video%" PRIu32, n);
138 if ((fd = open(device, flags)) == -1)
139 continue;
140
141 /* query device capabilities and make sure this is a video capture device */
142 if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0 || !(cap.device_caps & V4L2_CAP_VIDEO_CAPTURE))
143 {
144 close(fd);
145 continue;
146 }
147
148 if (cap.bus_info[0] != 0 && 0 == strcmp((const char*)cap.bus_info, deviceId))
149 return fd;
150
151 close(fd);
152 }
153
154 return fd;
155}
156
157static BOOL cam_v4l_activate(ICamHal* ihal, const char* deviceId, CAM_ERROR_CODE* errorCode)
158{
159 WINPR_UNUSED(ihal);
160 WINPR_UNUSED(deviceId);
161
162 *errorCode = CAM_ERROR_CODE_None;
163 return TRUE;
164}
165
166static BOOL cam_v4l_deactivate(ICamHal* ihal, const char* deviceId, CAM_ERROR_CODE* errorCode)
167{
168 WINPR_UNUSED(ihal);
169 WINPR_UNUSED(deviceId);
170
171 *errorCode = CAM_ERROR_CODE_None;
172 return TRUE;
173}
174
181static INT16 cam_v4l_get_media_type_descriptions(ICamHal* ihal, const char* deviceId,
182 size_t streamIndex,
183 const CAM_MEDIA_FORMAT_INFO* supportedFormats,
184 size_t nSupportedFormats,
185 CAM_MEDIA_TYPE_DESCRIPTION* mediaTypes,
186 size_t* nMediaTypes)
187{
188 CamV4lHal* hal = (CamV4lHal*)ihal;
189 size_t maxMediaTypes = *nMediaTypes;
190 size_t nTypes = 0;
191 BOOL formatFound = FALSE;
192
193 CamV4lStream* stream = (CamV4lStream*)HashTable_GetItemValue(hal->streams, deviceId);
194
195 if (!stream)
196 {
197 stream = cam_v4l_stream_create(deviceId, streamIndex);
198 if (!stream)
199 return CAM_ERROR_CODE_OutOfMemory;
200
201 if (!HashTable_Insert(hal->streams, deviceId, stream))
202 {
203 cam_v4l_stream_free(stream);
204 return CAM_ERROR_CODE_UnexpectedError;
205 }
206 }
207
208 int fd = cam_v4l_open_device(deviceId, O_RDONLY);
209 if (fd == -1)
210 {
211 WLog_ERR(TAG, "Unable to open device %s", deviceId);
212 return -1;
213 }
214
215 size_t formatIndex = 0;
216 for (; formatIndex < nSupportedFormats; formatIndex++)
217 {
218 const UINT32 pixelFormat = ecamToV4L2PixFormat(supportedFormats[formatIndex].inputFormat);
219
220 WINPR_ASSERT(pixelFormat != 0);
221 struct v4l2_frmsizeenum frmsize = WINPR_C_ARRAY_INIT;
222
223 if (!cam_v4l_format_supported(fd, pixelFormat))
224 continue;
225
226 frmsize.pixel_format = pixelFormat;
227 for (frmsize.index = 0; ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frmsize) == 0; frmsize.index++)
228 {
229 struct v4l2_frmivalenum frmival = WINPR_C_ARRAY_INIT;
230
231 if (frmsize.type != V4L2_FRMSIZE_TYPE_DISCRETE)
232 break; /* don't support size types other than discrete */
233
234 formatFound = TRUE;
235 mediaTypes->Width = frmsize.discrete.width;
236 mediaTypes->Height = frmsize.discrete.height;
237 mediaTypes->Format = supportedFormats[formatIndex].inputFormat;
238
239 /* query frame rate (1st is highest fps supported) */
240 frmival.index = 0;
241 frmival.pixel_format = pixelFormat;
242 frmival.width = frmsize.discrete.width;
243 frmival.height = frmsize.discrete.height;
244 if (ioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &frmival) == 0 &&
245 frmival.type == V4L2_FRMIVAL_TYPE_DISCRETE)
246 {
247 /* inverse of a fraction */
248 mediaTypes->FrameRateNumerator = frmival.discrete.denominator;
249 mediaTypes->FrameRateDenominator = frmival.discrete.numerator;
250 }
251 else
252 {
253 WLog_DBG(TAG, "VIDIOC_ENUM_FRAMEINTERVALS failed, using default framerate");
254 mediaTypes->FrameRateNumerator = CAM_V4L2_FRAMERATE_NUMERATOR_DEFAULT;
255 mediaTypes->FrameRateDenominator = CAM_V4L2_FRAMERATE_DENOMINATOR_DEFAULT;
256 }
257
258 mediaTypes->PixelAspectRatioNumerator = mediaTypes->PixelAspectRatioDenominator = 1;
259
260 char fourccstr[5] = WINPR_C_ARRAY_INIT;
261 WLog_DBG(TAG, "Camera format: %s, width: %u, height: %u, fps: %u/%u",
262 cam_v4l_get_fourcc_str(pixelFormat, fourccstr, ARRAYSIZE(fourccstr)),
263 mediaTypes->Width, mediaTypes->Height, mediaTypes->FrameRateNumerator,
264 mediaTypes->FrameRateDenominator);
265
266 mediaTypes++;
267 nTypes++;
268
269 if (nTypes == maxMediaTypes)
270 {
271 WLog_ERR(TAG, "Media types reached buffer maximum %" PRIuz "", maxMediaTypes);
272 goto error;
273 }
274 }
275
276 if (formatFound)
277 {
278 /* we are interested in 1st supported format only, with all supported sizes */
279 break;
280 }
281 }
282
283error:
284
285 *nMediaTypes = nTypes;
286 close(fd);
287 if (formatIndex > INT16_MAX)
288 return -1;
289 return (INT16)formatIndex;
290}
291
292static bool cam_v4l_formats_supported(int fd, const struct v4l2_capability* cap, const char* device)
293{
294 WINPR_ASSERT(cap);
295 WINPR_ASSERT(device);
296 if (fd < 0)
297 return false;
298
299 struct v4l2_fmtdesc fmtdesc = WINPR_C_ARRAY_INIT;
300 fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
301
302 size_t flen = 0;
303 char* formats = nullptr;
304 bool supported = false;
305 for (fmtdesc.index = 0; ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc) == 0; fmtdesc.index++)
306 {
307 char fmt[32] = WINPR_C_ARRAY_INIT;
308 memcpy(fmt, &fmtdesc.pixelformat, sizeof(fmtdesc.pixelformat));
309 if (formats)
310 {
311 char* tmp = nullptr;
312 size_t tlen = 0;
313 winpr_asprintf(&tmp, &tlen, "%s, %s [%s]", formats, fmtdesc.description, fmt);
314 free(formats);
315 formats = tmp;
316 flen = tlen;
317 }
318 else
319 winpr_asprintf(&formats, &flen, "%s [%s]", fmtdesc.description, fmt);
320
321 switch (fmtdesc.pixelformat)
322 {
323 case V4L2_PIX_FMT_H264:
324 case V4L2_PIX_FMT_MJPEG:
325 case V4L2_PIX_FMT_YUYV:
326 case V4L2_PIX_FMT_NV12:
327 case V4L2_PIX_FMT_YUV420:
328 case V4L2_PIX_FMT_RGB24:
329 case V4L2_PIX_FMT_RGB32:
330 supported = true;
331 break;
332 default:
333 break;
334 }
335 }
336
337 if (supported)
338 WLog_DBG(TAG, "Device %s (%s) selected, formats supported: {%s}", cap->card, device,
339 formats);
340 else
341 WLog_DBG(TAG, "Device %s (%s) filtered, no formats supported: {%s}", cap->card, device,
342 formats);
343 free(formats);
344 return supported;
345}
346
352static UINT cam_v4l_enumerate(WINPR_ATTR_UNUSED ICamHal* ihal, ICamHalEnumCallback callback,
353 CameraPlugin* ecam, GENERIC_CHANNEL_CALLBACK* hchannel)
354{
355 UINT count = 0;
356
357 for (UINT n = 0; n < 64; n++)
358 {
359 char device[20] = WINPR_C_ARRAY_INIT;
360 struct v4l2_capability cap = WINPR_C_ARRAY_INIT;
361 (void)_snprintf(device, sizeof(device), "/dev/video%" PRIu32, n);
362 int fd = open(device, O_RDONLY);
363 if (fd == -1)
364 continue;
365
366 /* query device capabilities and make sure this is a video capture device */
367 if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0 || !(cap.device_caps & V4L2_CAP_VIDEO_CAPTURE) ||
368 !cam_v4l_formats_supported(fd, &cap, device))
369 {
370 close(fd);
371 continue;
372 }
373 count++;
374
375 const char* deviceName = (char*)cap.card;
376 const char* deviceId = device;
377 if (cap.bus_info[0] != 0) /* may not be available in all drivers */
378 deviceId = (char*)cap.bus_info;
379
380 IFCALL(callback, ecam, hchannel, deviceId, deviceName);
381
382 close(fd);
383 }
384
385 return count;
386}
387
388static void cam_v4l_stream_free_buffers(CamV4lStream* stream)
389{
390 if (!stream || !stream->buffers)
391 return;
392
393 /* unmap buffers */
394 for (size_t i = 0; i < stream->nBuffers; i++)
395 {
396 if (stream->buffers[i].length && stream->buffers[i].start != MAP_FAILED)
397 {
398 munmap(stream->buffers[i].start, stream->buffers[i].length);
399 }
400 }
401
402 free(stream->buffers);
403 stream->buffers = nullptr;
404 stream->nBuffers = 0;
405}
406
412static size_t cam_v4l_stream_alloc_buffers(CamV4lStream* stream)
413{
414 struct v4l2_requestbuffers rbuffer = WINPR_C_ARRAY_INIT;
415
416 rbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
417 rbuffer.memory = V4L2_MEMORY_MMAP;
418 rbuffer.count = CAM_V4L2_BUFFERS_COUNT;
419
420 if (ioctl(stream->fd, VIDIOC_REQBUFS, &rbuffer) < 0 || rbuffer.count == 0)
421 {
422 char buffer[64] = WINPR_C_ARRAY_INIT;
423 WLog_ERR(TAG, "Failure in VIDIOC_REQBUFS, errno %s [%d], count %u",
424 winpr_strerror(errno, buffer, sizeof(buffer)), errno, rbuffer.count);
425 return 0;
426 }
427
428 stream->nBuffers = rbuffer.count;
429
430 /* Map the buffers */
431 stream->buffers = (CamV4lBuffer*)calloc(rbuffer.count, sizeof(CamV4lBuffer));
432 if (!stream->buffers)
433 {
434 WLog_ERR(TAG, "Failure in calloc");
435 return 0;
436 }
437
438 for (unsigned int i = 0; i < rbuffer.count; i++)
439 {
440 struct v4l2_buffer vbuffer = WINPR_C_ARRAY_INIT;
441 vbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
442 vbuffer.memory = V4L2_MEMORY_MMAP;
443 vbuffer.index = i;
444
445 if (ioctl(stream->fd, VIDIOC_QUERYBUF, &vbuffer) < 0)
446 {
447 char buffer[64] = WINPR_C_ARRAY_INIT;
448 WLog_ERR(TAG, "Failure in VIDIOC_QUERYBUF, errno %s [%d]",
449 winpr_strerror(errno, buffer, sizeof(buffer)), errno);
450 cam_v4l_stream_free_buffers(stream);
451 return 0;
452 }
453
454 stream->buffers[i].start = mmap(nullptr, vbuffer.length, PROT_READ | PROT_WRITE, MAP_SHARED,
455 stream->fd, vbuffer.m.offset);
456
457 if (MAP_FAILED == stream->buffers[i].start)
458 {
459 char buffer[64] = WINPR_C_ARRAY_INIT;
460 WLog_ERR(TAG, "Failure in mmap, errno %s [%d]",
461 winpr_strerror(errno, buffer, sizeof(buffer)), errno);
462 cam_v4l_stream_free_buffers(stream);
463 return 0;
464 }
465
466 stream->buffers[i].length = vbuffer.length;
467
468 WLog_DBG(TAG, "Buffer %u mapped, size: %u", i, vbuffer.length);
469
470 if (ioctl(stream->fd, VIDIOC_QBUF, &vbuffer) < 0)
471 {
472 char buffer[64] = WINPR_C_ARRAY_INIT;
473 WLog_ERR(TAG, "Failure in VIDIOC_QBUF, errno %s [%d]",
474 winpr_strerror(errno, buffer, sizeof(buffer)), errno);
475 cam_v4l_stream_free_buffers(stream);
476 return 0;
477 }
478 }
479
480 return stream->buffers[0].length;
481}
482
488static DWORD WINAPI cam_v4l_stream_capture_thread(LPVOID param)
489{
490 CamV4lStream* stream = (CamV4lStream*)param;
491 WINPR_ASSERT(stream);
492
493 int fd = stream->fd;
494 BOOL releaseDevice = FALSE;
495
496 do
497 {
498 int retVal = 0;
499 struct pollfd pfd = WINPR_C_ARRAY_INIT;
500
501 pfd.fd = fd;
502 pfd.events = POLLIN;
503
504 retVal = poll(&pfd, 1, CAM_V4L2_CAPTURE_THREAD_SLEEP_MS);
505
506 if (retVal == 0)
507 {
508 /* poll timed out */
509 continue;
510 }
511 else if (retVal < 0)
512 {
513 char buffer[64] = WINPR_C_ARRAY_INIT;
514 WLog_DBG(TAG, "Failure in poll, errno %s [%d]",
515 winpr_strerror(errno, buffer, sizeof(buffer)), errno);
516 Sleep(CAM_V4L2_CAPTURE_THREAD_SLEEP_MS); /* trying to recover */
517 continue;
518 }
519 else if (!(pfd.revents & POLLIN))
520 {
521 WLog_DBG(TAG, "poll reported non-read event %d", pfd.revents);
522 Sleep(CAM_V4L2_CAPTURE_THREAD_SLEEP_MS); /* also trying to recover */
523 continue;
524 }
525
526 EnterCriticalSection(&stream->lock);
527 if (stream->streaming)
528 {
529 struct v4l2_buffer buf = WINPR_C_ARRAY_INIT;
530 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
531 buf.memory = V4L2_MEMORY_MMAP;
532
533 /* dequeue buffers until empty, or until we are asked to stop */
534 while (stream->streaming && ioctl(fd, VIDIOC_DQBUF, &buf) != -1)
535 {
536 const UINT error =
537 stream->sampleCallback(stream->dev, stream->streamIndex,
538 stream->buffers[buf.index].start, buf.bytesused);
539 if (error == ECAM_SAMPLE_CAPTURE_DRAINED)
540 {
541 stream->streaming = FALSE;
542 releaseDevice = TRUE;
543 break;
544 }
545 else if (error != CHANNEL_RC_OK)
546 {
547 WLog_ERR(TAG, "Failure in sampleCallback: %" PRIu32, error);
548 }
549
550 /* enqueue buffer back */
551 if (ioctl(fd, VIDIOC_QBUF, &buf) == -1)
552 {
553 char buffer[64] = WINPR_C_ARRAY_INIT;
554 WLog_ERR(TAG, "Failure in VIDIOC_QBUF, errno %s [%d]",
555 winpr_strerror(errno, buffer, sizeof(buffer)), errno);
556 }
557 }
558 }
559 LeaveCriticalSection(&stream->lock);
560
561 } while (stream->streaming);
562
563 if (releaseDevice)
564 {
565 enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
566 if (ioctl(stream->fd, VIDIOC_STREAMOFF, &type) < 0)
567 {
568 char buffer[64] = WINPR_C_ARRAY_INIT;
569 WLog_ERR(TAG, "Failure in VIDIOC_STREAMOFF, errno %s [%d]",
570 winpr_strerror(errno, buffer, sizeof(buffer)), errno);
571 }
572
573 cam_v4l_stream_free_buffers(stream);
574 cam_v4l_stream_close_device(stream);
575 }
576
577 return CHANNEL_RC_OK;
578}
579
580void cam_v4l_stream_close_device(CamV4lStream* stream)
581{
582 if (stream->fd != -1)
583 {
584 close(stream->fd);
585 stream->fd = -1;
586 }
587}
588
594WINPR_ATTR_MALLOC(cam_v4l_stream_free, 1)
595CamV4lStream* cam_v4l_stream_create(const char* deviceId, size_t streamIndex)
596{
597 CamV4lStream* stream = calloc(1, sizeof(CamV4lStream));
598
599 if (!stream)
600 {
601 WLog_ERR(TAG, "Failure in calloc");
602 return nullptr;
603 }
604 stream->streamIndex = streamIndex;
605 stream->fd = -1;
606 stream->h264UnitId = get_uvc_h624_unit_id(deviceId);
607
608 if (!InitializeCriticalSectionEx(&stream->lock, 0, 0))
609 {
610 WLog_ERR(TAG, "Failure in calloc");
611 free(stream);
612 return nullptr;
613 }
614
615 return stream;
616}
617
623CAM_ERROR_CODE cam_v4l_stream_stop(CamV4lStream* stream)
624{
625 if (!stream)
626 return CAM_ERROR_CODE_None;
627
628 stream->streaming = FALSE; /* this will terminate capture thread */
629
630 if (stream->captureThread)
631 {
632 (void)WaitForSingleObject(stream->captureThread, INFINITE);
633 (void)CloseHandle(stream->captureThread);
634 stream->captureThread = nullptr;
635 }
636
637 EnterCriticalSection(&stream->lock);
638
639 /* stop streaming */
640 if (stream->fd != -1)
641 {
642 enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
643 if (ioctl(stream->fd, VIDIOC_STREAMOFF, &type) < 0)
644 {
645 char buffer[64] = WINPR_C_ARRAY_INIT;
646 WLog_ERR(TAG, "Failure in VIDIOC_STREAMOFF, errno %s [%d]",
647 winpr_strerror(errno, buffer, sizeof(buffer)), errno);
648 }
649 }
650
651 cam_v4l_stream_free_buffers(stream);
652 cam_v4l_stream_close_device(stream);
653
654 LeaveCriticalSection(&stream->lock);
655
656 return CAM_ERROR_CODE_None;
657}
658
659static CAM_ERROR_CODE cam_v4l_stream_start(ICamHal* ihal, CameraDevice* dev, size_t streamIndex,
660 const CAM_MEDIA_TYPE_DESCRIPTION* mediaType,
661 ICamHalSampleCapturedCallback callback)
662{
663 CamV4lHal* hal = (CamV4lHal*)ihal;
664 WINPR_ASSERT(hal);
665
666 CamV4lStream* stream = (CamV4lStream*)HashTable_GetItemValue(hal->streams, dev->deviceId);
667
668 if (!stream)
669 {
670 WLog_ERR(TAG, "Unable to find stream, device %s, streamIndex %" PRIuz, dev->deviceId,
671 streamIndex);
672 return CAM_ERROR_CODE_UnexpectedError;
673 }
674
675 EnterCriticalSection(&stream->lock);
676 const BOOL streaming = stream->streaming;
677 LeaveCriticalSection(&stream->lock);
678
679 if (streaming)
680 {
681 WLog_ERR(TAG, "Streaming already in progress, device %s, streamIndex %" PRIuz,
682 dev->deviceId, streamIndex);
683 return CAM_ERROR_CODE_UnexpectedError;
684 }
685
686 if (stream->captureThread)
687 {
688 (void)WaitForSingleObject(stream->captureThread, INFINITE);
689 (void)CloseHandle(stream->captureThread);
690 stream->captureThread = nullptr;
691 }
692
693 stream->dev = dev;
694 stream->sampleCallback = callback;
695
696 if ((stream->fd = cam_v4l_open_device(dev->deviceId, O_RDWR | O_NONBLOCK)) == -1)
697 {
698 WLog_ERR(TAG, "Unable to open device %s", dev->deviceId);
699 return CAM_ERROR_CODE_UnexpectedError;
700 }
701
702 struct v4l2_format video_fmt = WINPR_C_ARRAY_INIT;
703 video_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
704
705 const UINT32 pixelFormat = ecamToV4L2PixFormat(mediaType->Format);
706 video_fmt.fmt.pix.width = mediaType->Width;
707 video_fmt.fmt.pix.height = mediaType->Height;
708
709 if (pixelFormat == 0)
710 {
711 cam_v4l_stream_close_device(stream);
712 return CAM_ERROR_CODE_InvalidMediaType;
713 }
714
715 video_fmt.fmt.pix.pixelformat = pixelFormat;
716
717 /* set format and frame size */
718 if (ioctl(stream->fd, VIDIOC_S_FMT, &video_fmt) < 0)
719 {
720 char buffer[64] = WINPR_C_ARRAY_INIT;
721 WLog_ERR(TAG, "Failure in VIDIOC_S_FMT, errno %s [%d]",
722 winpr_strerror(errno, buffer, sizeof(buffer)), errno);
723 cam_v4l_stream_close_device(stream);
724 return CAM_ERROR_CODE_InvalidMediaType;
725 }
726
727 /* trying to set frame rate, if driver supports it */
728 struct v4l2_streamparm sp1 = WINPR_C_ARRAY_INIT;
729 struct v4l2_streamparm sp2 = WINPR_C_ARRAY_INIT;
730 sp1.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
731 if (ioctl(stream->fd, VIDIOC_G_PARM, &sp1) < 0 ||
732 !(sp1.parm.capture.capability & V4L2_CAP_TIMEPERFRAME))
733 {
734 WLog_INFO(TAG, "Driver doesn't support setting framerate");
735 }
736 else
737 {
738 sp2.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
739
740 /* inverse of a fraction */
741 sp2.parm.capture.timeperframe.numerator = mediaType->FrameRateDenominator;
742 sp2.parm.capture.timeperframe.denominator = mediaType->FrameRateNumerator;
743
744 if (ioctl(stream->fd, VIDIOC_S_PARM, &sp2) < 0)
745 {
746 char buffer[64] = WINPR_C_ARRAY_INIT;
747 WLog_INFO(TAG, "Failed to set the framerate, errno %s [%d]",
748 winpr_strerror(errno, buffer, sizeof(buffer)), errno);
749 }
750 }
751
752 size_t maxSample = cam_v4l_stream_alloc_buffers(stream);
753 if (maxSample == 0)
754 {
755 WLog_ERR(TAG, "Failure to allocate video buffers");
756 cam_v4l_stream_close_device(stream);
757 return CAM_ERROR_CODE_OutOfMemory;
758 }
759
760 stream->streaming = TRUE;
761
762 /* start streaming */
763 enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
764 if (ioctl(stream->fd, VIDIOC_STREAMON, &type) < 0)
765 {
766 char buffer[64] = WINPR_C_ARRAY_INIT;
767 WLog_ERR(TAG, "Failure in VIDIOC_STREAMON, errno %s [%d]",
768 winpr_strerror(errno, buffer, sizeof(buffer)), errno);
769 cam_v4l_stream_stop(stream);
770 return CAM_ERROR_CODE_UnexpectedError;
771 }
772
773 stream->captureThread =
774 CreateThread(nullptr, 0, cam_v4l_stream_capture_thread, stream, 0, nullptr);
775 if (!stream->captureThread)
776 {
777 WLog_ERR(TAG, "CreateThread failure");
778 cam_v4l_stream_stop(stream);
779 return CAM_ERROR_CODE_OutOfMemory;
780 }
781
782 char fourccstr[16] = WINPR_C_ARRAY_INIT;
783 cam_v4l_get_fourcc_str(pixelFormat, fourccstr, ARRAYSIZE(fourccstr));
784
785 WLog_INFO(TAG, "Camera format: %s, width: %u, height: %u, fps: %u/%u", fourccstr,
786 mediaType->Width, mediaType->Height, mediaType->FrameRateNumerator,
787 mediaType->FrameRateDenominator);
788
789 return CAM_ERROR_CODE_None;
790}
791
797static CAM_ERROR_CODE cam_v4l_stream_stop_by_device_id(ICamHal* ihal, const char* deviceId,
798 WINPR_ATTR_UNUSED size_t streamIndex)
799{
800 CamV4lHal* hal = (CamV4lHal*)ihal;
801
802 CamV4lStream* stream = (CamV4lStream*)HashTable_GetItemValue(hal->streams, deviceId);
803
804 if (!stream)
805 return CAM_ERROR_CODE_NotInitialized;
806
807 return cam_v4l_stream_stop(stream);
808}
809
816void cam_v4l_stream_free(void* obj)
817{
818 CamV4lStream* stream = (CamV4lStream*)obj;
819 if (!stream)
820 return;
821
822 cam_v4l_stream_stop(stream);
823
824 DeleteCriticalSection(&stream->lock);
825 free(stream);
826}
827
833static CAM_ERROR_CODE cam_v4l_free(ICamHal* ihal)
834{
835 CamV4lHal* hal = (CamV4lHal*)ihal;
836
837 if (hal == nullptr)
838 return CAM_ERROR_CODE_NotInitialized;
839
840 HashTable_Free(hal->streams);
841
842 free(hal);
843
844 return CAM_ERROR_CODE_None;
845}
846
852FREERDP_ENTRY_POINT(UINT VCAPITYPE v4l_freerdp_rdpecam_client_subsystem_entry(
854{
855 UINT ret = ERROR_INTERNAL_ERROR;
856 WINPR_ASSERT(pEntryPoints);
857
858 CamV4lHal* hal = (CamV4lHal*)calloc(1, sizeof(CamV4lHal));
859
860 if (hal == nullptr)
861 return CHANNEL_RC_NO_MEMORY;
862
863 hal->iHal.Enumerate = cam_v4l_enumerate;
864 hal->iHal.RequestDrivenCapture = TRUE;
865 hal->iHal.GetMediaTypeDescriptions = cam_v4l_get_media_type_descriptions;
866 hal->iHal.Activate = cam_v4l_activate;
867 hal->iHal.Deactivate = cam_v4l_deactivate;
868 hal->iHal.StartStream = cam_v4l_stream_start;
869 hal->iHal.StopStream = cam_v4l_stream_stop_by_device_id;
870 hal->iHal.Free = cam_v4l_free;
871
872 hal->streams = HashTable_New(FALSE);
873 if (!hal->streams)
874 goto error;
875
876 if (!HashTable_SetupForStringData(hal->streams, FALSE))
877 goto error;
878
879 wObject* obj = HashTable_ValueObject(hal->streams);
880 WINPR_ASSERT(obj);
881 obj->fnObjectFree = cam_v4l_stream_free;
882
883 ret = pEntryPoints->pRegisterCameraHal(pEntryPoints->plugin, &hal->iHal);
884 if (ret != CHANNEL_RC_OK)
885 {
886 WLog_ERR(TAG, "RegisterCameraHal failed with error %" PRIu32 "", ret);
887 goto error;
888 }
889
890 return ret;
891
892error:
893 cam_v4l_free(&hal->iHal);
894 return ret;
895}
Definition camera.h:229
This struct contains function pointer to initialize/free objects.
Definition collections.h:52
OBJECT_FREE_FN fnObjectFree
Definition collections.h:59