FreeRDP
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Modules Pages
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, int streamIndex);
49static void cam_v4l_stream_free(void* obj);
50static void cam_v4l_stream_close_device(CamV4lStream* stream);
51static UINT 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 NULL;
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 %d", ecamFormat);
96 return 0;
97 }
98}
99
105static BOOL cam_v4l_format_supported(int fd, UINT32 format)
106{
107 struct v4l2_fmtdesc fmtdesc = { 0 };
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] = { 0 };
126 int fd = -1;
127 struct v4l2_capability cap = { 0 };
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
163static INT16 cam_v4l_get_media_type_descriptions(ICamHal* ihal, const char* deviceId,
164 int streamIndex,
165 const CAM_MEDIA_FORMAT_INFO* supportedFormats,
166 size_t nSupportedFormats,
167 CAM_MEDIA_TYPE_DESCRIPTION* mediaTypes,
168 size_t* nMediaTypes)
169{
170 CamV4lHal* hal = (CamV4lHal*)ihal;
171 size_t maxMediaTypes = *nMediaTypes;
172 size_t nTypes = 0;
173 BOOL formatFound = FALSE;
174
175 CamV4lStream* stream = (CamV4lStream*)HashTable_GetItemValue(hal->streams, deviceId);
176
177 if (!stream)
178 {
179 stream = cam_v4l_stream_create(deviceId, streamIndex);
180 if (!stream)
181 return CAM_ERROR_CODE_OutOfMemory;
182
183 if (!HashTable_Insert(hal->streams, deviceId, stream))
184 {
185 cam_v4l_stream_free(stream);
186 return CAM_ERROR_CODE_UnexpectedError;
187 }
188 }
189
190 int fd = cam_v4l_open_device(deviceId, O_RDONLY);
191 if (fd == -1)
192 {
193 WLog_ERR(TAG, "Unable to open device %s", deviceId);
194 return -1;
195 }
196
197 size_t formatIndex = 0;
198 for (; formatIndex < nSupportedFormats; formatIndex++)
199 {
200 UINT32 pixelFormat = 0;
201 if (supportedFormats[formatIndex].inputFormat == CAM_MEDIA_FORMAT_MJPG_H264)
202 {
203 if (stream->h264UnitId > 0)
204 pixelFormat = V4L2_PIX_FMT_MJPEG;
205 else
206 continue; /* not supported */
207 }
208 else
209 {
210 pixelFormat = ecamToV4L2PixFormat(supportedFormats[formatIndex].inputFormat);
211 }
212
213 WINPR_ASSERT(pixelFormat != 0);
214 struct v4l2_frmsizeenum frmsize = { 0 };
215
216 if (!cam_v4l_format_supported(fd, pixelFormat))
217 continue;
218
219 frmsize.pixel_format = pixelFormat;
220 for (frmsize.index = 0; ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frmsize) == 0; frmsize.index++)
221 {
222 struct v4l2_frmivalenum frmival = { 0 };
223
224 if (frmsize.type != V4L2_FRMSIZE_TYPE_DISCRETE)
225 break; /* don't support size types other than discrete */
226
227 formatFound = TRUE;
228 mediaTypes->Width = frmsize.discrete.width;
229 mediaTypes->Height = frmsize.discrete.height;
230 mediaTypes->Format = supportedFormats[formatIndex].inputFormat;
231
232 /* query frame rate (1st is highest fps supported) */
233 frmival.index = 0;
234 frmival.pixel_format = pixelFormat;
235 frmival.width = frmsize.discrete.width;
236 frmival.height = frmsize.discrete.height;
237 if (ioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &frmival) == 0 &&
238 frmival.type == V4L2_FRMIVAL_TYPE_DISCRETE)
239 {
240 /* inverse of a fraction */
241 mediaTypes->FrameRateNumerator = frmival.discrete.denominator;
242 mediaTypes->FrameRateDenominator = frmival.discrete.numerator;
243 }
244 else
245 {
246 WLog_DBG(TAG, "VIDIOC_ENUM_FRAMEINTERVALS failed, using default framerate");
247 mediaTypes->FrameRateNumerator = CAM_V4L2_FRAMERATE_NUMERATOR_DEFAULT;
248 mediaTypes->FrameRateDenominator = CAM_V4L2_FRAMERATE_DENOMINATOR_DEFAULT;
249 }
250
251 mediaTypes->PixelAspectRatioNumerator = mediaTypes->PixelAspectRatioDenominator = 1;
252
253 char fourccstr[5] = { 0 };
254 WLog_DBG(TAG, "Camera format: %s, width: %u, height: %u, fps: %u/%u",
255 cam_v4l_get_fourcc_str(pixelFormat, fourccstr, ARRAYSIZE(fourccstr)),
256 mediaTypes->Width, mediaTypes->Height, mediaTypes->FrameRateNumerator,
257 mediaTypes->FrameRateDenominator);
258
259 mediaTypes++;
260 nTypes++;
261
262 if (nTypes == maxMediaTypes)
263 {
264 WLog_ERR(TAG, "Media types reached buffer maximum %" PRIu32 "", maxMediaTypes);
265 goto error;
266 }
267 }
268
269 if (formatFound)
270 {
271 /* we are interested in 1st supported format only, with all supported sizes */
272 break;
273 }
274 }
275
276error:
277
278 *nMediaTypes = nTypes;
279 close(fd);
280 if (formatIndex > INT16_MAX)
281 return -1;
282 return (INT16)formatIndex;
283}
284
290static UINT cam_v4l_enumerate(WINPR_ATTR_UNUSED ICamHal* ihal, ICamHalEnumCallback callback,
291 CameraPlugin* ecam, GENERIC_CHANNEL_CALLBACK* hchannel)
292{
293 UINT count = 0;
294
295 for (UINT n = 0; n < 64; n++)
296 {
297 char device[20] = { 0 };
298 struct v4l2_capability cap = { 0 };
299 (void)_snprintf(device, sizeof(device), "/dev/video%" PRIu32, n);
300 int fd = open(device, O_RDONLY);
301 if (fd == -1)
302 continue;
303
304 /* query device capabilities and make sure this is a video capture device */
305 if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0 || !(cap.device_caps & V4L2_CAP_VIDEO_CAPTURE))
306 {
307 close(fd);
308 continue;
309 }
310 count++;
311
312 const char* deviceName = (char*)cap.card;
313 const char* deviceId = device;
314 if (cap.bus_info[0] != 0) /* may not be available in all drivers */
315 deviceId = (char*)cap.bus_info;
316
317 IFCALL(callback, ecam, hchannel, deviceId, deviceName);
318
319 close(fd);
320 }
321
322 return count;
323}
324
325static void cam_v4l_stream_free_buffers(CamV4lStream* stream)
326{
327 if (!stream || !stream->buffers)
328 return;
329
330 /* unmap buffers */
331 for (size_t i = 0; i < stream->nBuffers; i++)
332 {
333 if (stream->buffers[i].length && stream->buffers[i].start != MAP_FAILED)
334 {
335 munmap(stream->buffers[i].start, stream->buffers[i].length);
336 }
337 }
338
339 free(stream->buffers);
340 stream->buffers = NULL;
341 stream->nBuffers = 0;
342}
343
349static size_t cam_v4l_stream_alloc_buffers(CamV4lStream* stream)
350{
351 struct v4l2_requestbuffers rbuffer = { 0 };
352
353 rbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
354 rbuffer.memory = V4L2_MEMORY_MMAP;
355 rbuffer.count = CAM_V4L2_BUFFERS_COUNT;
356
357 if (ioctl(stream->fd, VIDIOC_REQBUFS, &rbuffer) < 0 || rbuffer.count == 0)
358 {
359 char buffer[64] = { 0 };
360 WLog_ERR(TAG, "Failure in VIDIOC_REQBUFS, errno %s [%d], count %d",
361 winpr_strerror(errno, buffer, sizeof(buffer)), errno, rbuffer.count);
362 return 0;
363 }
364
365 stream->nBuffers = rbuffer.count;
366
367 /* Map the buffers */
368 stream->buffers = (CamV4lBuffer*)calloc(rbuffer.count, sizeof(CamV4lBuffer));
369 if (!stream->buffers)
370 {
371 WLog_ERR(TAG, "Failure in calloc");
372 return 0;
373 }
374
375 for (unsigned int i = 0; i < rbuffer.count; i++)
376 {
377 struct v4l2_buffer vbuffer = { 0 };
378 vbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
379 vbuffer.memory = V4L2_MEMORY_MMAP;
380 vbuffer.index = i;
381
382 if (ioctl(stream->fd, VIDIOC_QUERYBUF, &vbuffer) < 0)
383 {
384 char buffer[64] = { 0 };
385 WLog_ERR(TAG, "Failure in VIDIOC_QUERYBUF, errno %s [%d]",
386 winpr_strerror(errno, buffer, sizeof(buffer)), errno);
387 cam_v4l_stream_free_buffers(stream);
388 return 0;
389 }
390
391 stream->buffers[i].start = mmap(NULL, vbuffer.length, PROT_READ | PROT_WRITE, MAP_SHARED,
392 stream->fd, vbuffer.m.offset);
393
394 if (MAP_FAILED == stream->buffers[i].start)
395 {
396 char buffer[64] = { 0 };
397 WLog_ERR(TAG, "Failure in mmap, errno %s [%d]",
398 winpr_strerror(errno, buffer, sizeof(buffer)), errno);
399 cam_v4l_stream_free_buffers(stream);
400 return 0;
401 }
402
403 stream->buffers[i].length = vbuffer.length;
404
405 WLog_DBG(TAG, "Buffer %d mapped, size: %d", i, vbuffer.length);
406
407 if (ioctl(stream->fd, VIDIOC_QBUF, &vbuffer) < 0)
408 {
409 char buffer[64] = { 0 };
410 WLog_ERR(TAG, "Failure in VIDIOC_QBUF, errno %s [%d]",
411 winpr_strerror(errno, buffer, sizeof(buffer)), errno);
412 cam_v4l_stream_free_buffers(stream);
413 return 0;
414 }
415 }
416
417 return stream->buffers[0].length;
418}
419
425static UINT cam_v4l_stream_capture_thread(void* param)
426{
427 CamV4lStream* stream = (CamV4lStream*)param;
428
429 int fd = stream->fd;
430
431 do
432 {
433 int retVal = 0;
434 struct pollfd pfd = { 0 };
435
436 pfd.fd = fd;
437 pfd.events = POLLIN;
438
439 retVal = poll(&pfd, 1, CAM_V4L2_CAPTURE_THREAD_SLEEP_MS);
440
441 if (retVal == 0)
442 {
443 /* poll timed out */
444 continue;
445 }
446 else if (retVal < 0)
447 {
448 char buffer[64] = { 0 };
449 WLog_DBG(TAG, "Failure in poll, errno %s [%d]",
450 winpr_strerror(errno, buffer, sizeof(buffer)), errno);
451 Sleep(CAM_V4L2_CAPTURE_THREAD_SLEEP_MS); /* trying to recover */
452 continue;
453 }
454 else if (!(pfd.revents & POLLIN))
455 {
456 WLog_DBG(TAG, "poll reported non-read event %d", pfd.revents);
457 Sleep(CAM_V4L2_CAPTURE_THREAD_SLEEP_MS); /* also trying to recover */
458 continue;
459 }
460
461 EnterCriticalSection(&stream->lock);
462 if (stream->streaming)
463 {
464 struct v4l2_buffer buf = { 0 };
465 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
466 buf.memory = V4L2_MEMORY_MMAP;
467
468 /* dequeue buffers until empty */
469 while (ioctl(fd, VIDIOC_DQBUF, &buf) != -1)
470 {
471 stream->sampleCallback(stream->dev, stream->streamIndex,
472 stream->buffers[buf.index].start, buf.bytesused);
473
474 /* enqueue buffer back */
475 if (ioctl(fd, VIDIOC_QBUF, &buf) == -1)
476 {
477 char buffer[64] = { 0 };
478 WLog_ERR(TAG, "Failure in VIDIOC_QBUF, errno %s [%d]",
479 winpr_strerror(errno, buffer, sizeof(buffer)), errno);
480 }
481 }
482 }
483 LeaveCriticalSection(&stream->lock);
484
485 } while (stream->streaming);
486
487 return CHANNEL_RC_OK;
488}
489
490void cam_v4l_stream_close_device(CamV4lStream* stream)
491{
492 if (stream->fd != -1)
493 {
494 close(stream->fd);
495 stream->fd = -1;
496 }
497}
498
504static CamV4lStream* cam_v4l_stream_create(const char* deviceId, int streamIndex)
505{
506 CamV4lStream* stream = calloc(1, sizeof(CamV4lStream));
507
508 if (!stream)
509 {
510 WLog_ERR(TAG, "Failure in calloc");
511 return NULL;
512 }
513 stream->streamIndex = streamIndex;
514 stream->fd = -1;
515 stream->h264UnitId = get_uvc_h624_unit_id(deviceId);
516
517 if (!InitializeCriticalSectionEx(&stream->lock, 0, 0))
518 {
519 WLog_ERR(TAG, "Failure in calloc");
520 free(stream);
521 return NULL;
522 }
523
524 return stream;
525}
526
532UINT cam_v4l_stream_stop(CamV4lStream* stream)
533{
534 if (!stream || !stream->streaming)
535 return CHANNEL_RC_OK;
536
537 stream->streaming = FALSE; /* this will terminate capture thread */
538
539 if (stream->captureThread)
540 {
541 (void)WaitForSingleObject(stream->captureThread, INFINITE);
542 (void)CloseHandle(stream->captureThread);
543 stream->captureThread = NULL;
544 }
545
546 EnterCriticalSection(&stream->lock);
547
548 /* stop streaming */
549 enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
550 if (ioctl(stream->fd, VIDIOC_STREAMOFF, &type) < 0)
551 {
552 char buffer[64] = { 0 };
553 WLog_ERR(TAG, "Failure in VIDIOC_STREAMOFF, errno %s [%d]",
554 winpr_strerror(errno, buffer, sizeof(buffer)), errno);
555 }
556
557 cam_v4l_stream_free_buffers(stream);
558 cam_v4l_stream_close_device(stream);
559
560 LeaveCriticalSection(&stream->lock);
561
562 return CHANNEL_RC_OK;
563}
564
570static UINT cam_v4l_stream_start(ICamHal* ihal, CameraDevice* dev, int streamIndex,
571 const CAM_MEDIA_TYPE_DESCRIPTION* mediaType,
572 ICamHalSampleCapturedCallback callback)
573{
574 CamV4lHal* hal = (CamV4lHal*)ihal;
575
576 CamV4lStream* stream = (CamV4lStream*)HashTable_GetItemValue(hal->streams, dev->deviceId);
577
578 if (!stream)
579 {
580 WLog_ERR(TAG, "Unable to find stream, device %s, streamIndex %d", dev->deviceId,
581 streamIndex);
582 return CAM_ERROR_CODE_UnexpectedError;
583 }
584
585 if (stream->streaming)
586 {
587 WLog_ERR(TAG, "Streaming already in progress, device %s, streamIndex %d", dev->deviceId,
588 streamIndex);
589 return CAM_ERROR_CODE_UnexpectedError;
590 }
591
592 stream->dev = dev;
593 stream->sampleCallback = callback;
594
595 if ((stream->fd = cam_v4l_open_device(dev->deviceId, O_RDWR | O_NONBLOCK)) == -1)
596 {
597 WLog_ERR(TAG, "Unable to open device %s", dev->deviceId);
598 return CAM_ERROR_CODE_UnexpectedError;
599 }
600
601 struct v4l2_format video_fmt = { 0 };
602 video_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
603
604 UINT32 pixelFormat = 0;
605 if (mediaType->Format == CAM_MEDIA_FORMAT_MJPG_H264)
606 {
607 if (!set_h264_muxed_format(stream, mediaType))
608 {
609 WLog_ERR(TAG, "Failure to set H264 muxed format");
610 cam_v4l_stream_close_device(stream);
611 return CAM_ERROR_CODE_UnexpectedError;
612 }
613 /* setup container stream format */
614 pixelFormat = V4L2_PIX_FMT_MJPEG;
615 /* limit container stream resolution to save USB bandwidth - required */
616 video_fmt.fmt.pix.width = 640;
617 video_fmt.fmt.pix.height = 480;
618 }
619 else
620 {
621 pixelFormat = ecamToV4L2PixFormat(mediaType->Format);
622 video_fmt.fmt.pix.width = mediaType->Width;
623 video_fmt.fmt.pix.height = mediaType->Height;
624 }
625
626 if (pixelFormat == 0)
627 {
628 cam_v4l_stream_close_device(stream);
629 return CAM_ERROR_CODE_InvalidMediaType;
630 }
631
632 video_fmt.fmt.pix.pixelformat = pixelFormat;
633
634 /* set format and frame size */
635 if (ioctl(stream->fd, VIDIOC_S_FMT, &video_fmt) < 0)
636 {
637 char buffer[64] = { 0 };
638 WLog_ERR(TAG, "Failure in VIDIOC_S_FMT, errno %s [%d]",
639 winpr_strerror(errno, buffer, sizeof(buffer)), errno);
640 cam_v4l_stream_close_device(stream);
641 return CAM_ERROR_CODE_InvalidMediaType;
642 }
643
644 /* trying to set frame rate, if driver supports it */
645 struct v4l2_streamparm sp1 = { 0 };
646 struct v4l2_streamparm sp2 = { 0 };
647 sp1.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
648 if (ioctl(stream->fd, VIDIOC_G_PARM, &sp1) < 0 ||
649 !(sp1.parm.capture.capability & V4L2_CAP_TIMEPERFRAME))
650 {
651 WLog_INFO(TAG, "Driver doesn't support setting framerate");
652 }
653 else
654 {
655 sp2.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
656
657 /* inverse of a fraction */
658 sp2.parm.capture.timeperframe.numerator = mediaType->FrameRateDenominator;
659 sp2.parm.capture.timeperframe.denominator = mediaType->FrameRateNumerator;
660
661 if (ioctl(stream->fd, VIDIOC_S_PARM, &sp2) < 0)
662 {
663 char buffer[64] = { 0 };
664 WLog_INFO(TAG, "Failed to set the framerate, errno %s [%d]",
665 winpr_strerror(errno, buffer, sizeof(buffer)), errno);
666 }
667 }
668
669 size_t maxSample = cam_v4l_stream_alloc_buffers(stream);
670 if (maxSample == 0)
671 {
672 WLog_ERR(TAG, "Failure to allocate video buffers");
673 cam_v4l_stream_close_device(stream);
674 return CAM_ERROR_CODE_OutOfMemory;
675 }
676
677 stream->streaming = TRUE;
678
679 /* start streaming */
680 enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
681 if (ioctl(stream->fd, VIDIOC_STREAMON, &type) < 0)
682 {
683 char buffer[64] = { 0 };
684 WLog_ERR(TAG, "Failure in VIDIOC_STREAMON, errno %s [%d]",
685 winpr_strerror(errno, buffer, sizeof(buffer)), errno);
686 cam_v4l_stream_stop(stream);
687 return CAM_ERROR_CODE_UnexpectedError;
688 }
689
690 stream->captureThread = CreateThread(NULL, 0, cam_v4l_stream_capture_thread, stream, 0, NULL);
691 if (!stream->captureThread)
692 {
693 WLog_ERR(TAG, "CreateThread failure");
694 cam_v4l_stream_stop(stream);
695 return CAM_ERROR_CODE_OutOfMemory;
696 }
697
698 char fourccstr[16] = { 0 };
699 if (mediaType->Format == CAM_MEDIA_FORMAT_MJPG_H264)
700 strncpy(fourccstr, "H264 muxed", ARRAYSIZE(fourccstr) - 1);
701 else
702 cam_v4l_get_fourcc_str(pixelFormat, fourccstr, ARRAYSIZE(fourccstr));
703
704 WLog_INFO(TAG, "Camera format: %s, width: %u, height: %u, fps: %u/%u", fourccstr,
705 mediaType->Width, mediaType->Height, mediaType->FrameRateNumerator,
706 mediaType->FrameRateDenominator);
707
708 return CHANNEL_RC_OK;
709}
710
716static UINT cam_v4l_stream_stop_by_device_id(ICamHal* ihal, const char* deviceId,
717 WINPR_ATTR_UNUSED int streamIndex)
718{
719 CamV4lHal* hal = (CamV4lHal*)ihal;
720
721 CamV4lStream* stream = (CamV4lStream*)HashTable_GetItemValue(hal->streams, deviceId);
722
723 if (!stream)
724 return CHANNEL_RC_OK;
725
726 return cam_v4l_stream_stop(stream);
727}
728
735void cam_v4l_stream_free(void* obj)
736{
737 CamV4lStream* stream = (CamV4lStream*)obj;
738 if (!stream)
739 return;
740
741 cam_v4l_stream_stop(stream);
742
743 DeleteCriticalSection(&stream->lock);
744 free(stream);
745}
746
752static UINT cam_v4l_free(ICamHal* ihal)
753{
754 CamV4lHal* hal = (CamV4lHal*)ihal;
755
756 if (hal == NULL)
757 return ERROR_INVALID_PARAMETER;
758
759 HashTable_Free(hal->streams);
760
761 free(hal);
762
763 return CHANNEL_RC_OK;
764}
765
771FREERDP_ENTRY_POINT(UINT VCAPITYPE v4l_freerdp_rdpecam_client_subsystem_entry(
773{
774 UINT ret = CHANNEL_RC_OK;
775 WINPR_ASSERT(pEntryPoints);
776
777 CamV4lHal* hal = (CamV4lHal*)calloc(1, sizeof(CamV4lHal));
778
779 if (hal == NULL)
780 return CHANNEL_RC_NO_MEMORY;
781
782 hal->iHal.Enumerate = cam_v4l_enumerate;
783 hal->iHal.GetMediaTypeDescriptions = cam_v4l_get_media_type_descriptions;
784 hal->iHal.StartStream = cam_v4l_stream_start;
785 hal->iHal.StopStream = cam_v4l_stream_stop_by_device_id;
786 hal->iHal.Free = cam_v4l_free;
787
788 hal->streams = HashTable_New(FALSE);
789 if (!hal->streams)
790 {
791 ret = CHANNEL_RC_NO_MEMORY;
792 goto error;
793 }
794
795 HashTable_SetupForStringData(hal->streams, FALSE);
796
797 wObject* obj = HashTable_ValueObject(hal->streams);
798 WINPR_ASSERT(obj);
799 obj->fnObjectFree = cam_v4l_stream_free;
800
801 if ((ret = pEntryPoints->pRegisterCameraHal(pEntryPoints->plugin, &hal->iHal)))
802 {
803 WLog_ERR(TAG, "RegisterCameraHal failed with error %" PRIu32 "", ret);
804 goto error;
805 }
806
807 return ret;
808
809error:
810 cam_v4l_free(&hal->iHal);
811 return ret;
812}
Definition camera.h:175
This struct contains function pointer to initialize/free objects.
Definition collections.h:57