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