23 #include <sys/ioctl.h>
27 #include <linux/videodev2.h>
30 #include <winpr/handle.h>
32 #define TAG CHANNELS_TAG("rdpecam-v4l.client")
34 #define CAM_V4L2_BUFFERS_COUNT 4
35 #define CAM_V4L2_CAPTURE_THREAD_SLEEP_MS 1000
37 #define CAM_V4L2_FRAMERATE_NUMERATOR_DEFAULT 30
38 #define CAM_V4L2_FRAMERATE_DENOMINATOR_DEFAULT 1
54 ICamHalSampleCapturedCallback sampleCallback;
59 CamV4lBuffer* buffers;
72 static void cam_v4l_stream_free(
void* obj);
73 static void cam_v4l_stream_close_device(CamV4lStream* stream);
74 static UINT cam_v4l_stream_stop(CamV4lStream* stream);
81 static const char* cam_v4l_get_fourcc_str(
unsigned int fourcc,
char* buffer,
size_t size)
86 buffer[0] = (char)(fourcc & 0xFF);
87 buffer[1] = (char)((fourcc >> 8) & 0xFF);
88 buffer[2] = (char)((fourcc >> 16) & 0xFF);
89 buffer[3] = (char)((fourcc >> 24) & 0xFF);
99 static UINT32 ecamToV4L2PixFormat(CAM_MEDIA_FORMAT ecamFormat)
103 case CAM_MEDIA_FORMAT_H264:
104 return V4L2_PIX_FMT_H264;
105 case CAM_MEDIA_FORMAT_MJPG:
106 return V4L2_PIX_FMT_MJPEG;
107 case CAM_MEDIA_FORMAT_YUY2:
108 return V4L2_PIX_FMT_YUYV;
109 case CAM_MEDIA_FORMAT_NV12:
110 return V4L2_PIX_FMT_NV12;
111 case CAM_MEDIA_FORMAT_I420:
112 return V4L2_PIX_FMT_YUV420;
113 case CAM_MEDIA_FORMAT_RGB24:
114 return V4L2_PIX_FMT_RGB24;
115 case CAM_MEDIA_FORMAT_RGB32:
116 return V4L2_PIX_FMT_RGB32;
118 WLog_ERR(TAG,
"Unsupported CAM_MEDIA_FORMAT %d", ecamFormat);
128 static BOOL cam_v4l_format_supported(
int fd, UINT32 format)
130 struct v4l2_fmtdesc fmtdesc = { 0 };
131 fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
133 for (fmtdesc.index = 0; ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc) == 0; fmtdesc.index++)
135 if (fmtdesc.pixelformat == format)
146 static int cam_v4l_open_device(
const char* deviceId,
int flags)
148 char device[20] = { 0 };
150 struct v4l2_capability cap = { 0 };
155 if (0 == strncmp(deviceId,
"/dev/video", 10))
156 return open(deviceId, flags);
158 for (UINT n = 0; n < 64; n++)
160 (void)_snprintf(device,
sizeof(device),
"/dev/video%" PRIu32, n);
161 if ((fd = open(device, flags)) == -1)
165 if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0 || !(cap.device_caps & V4L2_CAP_VIDEO_CAPTURE))
171 if (cap.bus_info[0] != 0 && 0 == strcmp((
const char*)cap.bus_info, deviceId))
186 static INT16 cam_v4l_get_media_type_descriptions(ICamHal* hal,
const char* deviceId,
189 size_t nSupportedFormats,
193 size_t maxMediaTypes = *nMediaTypes;
195 BOOL formatFound = FALSE;
197 int fd = cam_v4l_open_device(deviceId, O_RDONLY);
200 WLog_ERR(TAG,
"Unable to open device %s", deviceId);
204 size_t formatIndex = 0;
205 for (; formatIndex < nSupportedFormats; formatIndex++)
207 UINT32 pixelFormat = ecamToV4L2PixFormat(supportedFormats[formatIndex].inputFormat);
208 WINPR_ASSERT(pixelFormat != 0);
209 struct v4l2_frmsizeenum frmsize = { 0 };
211 if (!cam_v4l_format_supported(fd, pixelFormat))
214 frmsize.pixel_format = pixelFormat;
215 for (frmsize.index = 0; ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frmsize) == 0; frmsize.index++)
217 struct v4l2_frmivalenum frmival = { 0 };
219 if (frmsize.type != V4L2_FRMSIZE_TYPE_DISCRETE)
223 mediaTypes->Width = frmsize.discrete.width;
224 mediaTypes->Height = frmsize.discrete.height;
225 mediaTypes->Format = supportedFormats[formatIndex].inputFormat;
229 frmival.pixel_format = pixelFormat;
230 frmival.width = frmsize.discrete.width;
231 frmival.height = frmsize.discrete.height;
232 if (ioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &frmival) == 0 &&
233 frmival.type == V4L2_FRMIVAL_TYPE_DISCRETE)
236 mediaTypes->FrameRateNumerator = frmival.discrete.denominator;
237 mediaTypes->FrameRateDenominator = frmival.discrete.numerator;
241 WLog_DBG(TAG,
"VIDIOC_ENUM_FRAMEINTERVALS failed, using default framerate");
242 mediaTypes->FrameRateNumerator = CAM_V4L2_FRAMERATE_NUMERATOR_DEFAULT;
243 mediaTypes->FrameRateDenominator = CAM_V4L2_FRAMERATE_DENOMINATOR_DEFAULT;
246 mediaTypes->PixelAspectRatioNumerator = mediaTypes->PixelAspectRatioDenominator = 1;
248 char fourccstr[5] = { 0 };
249 WLog_DBG(TAG,
"Camera format: %s, width: %u, height: %u, fps: %u/%u",
250 cam_v4l_get_fourcc_str(pixelFormat, fourccstr, ARRAYSIZE(fourccstr)),
251 mediaTypes->Width, mediaTypes->Height, mediaTypes->FrameRateNumerator,
252 mediaTypes->FrameRateDenominator);
257 if (nTypes == maxMediaTypes)
259 WLog_ERR(TAG,
"Media types reached buffer maximum %" PRIu32
"", maxMediaTypes);
273 *nMediaTypes = nTypes;
275 if (formatIndex > INT16_MAX)
277 return (INT16)formatIndex;
285 static UINT cam_v4l_enumerate(ICamHal* ihal, ICamHalEnumCallback callback,
CameraPlugin* ecam,
290 for (UINT n = 0; n < 64; n++)
292 char device[20] = { 0 };
293 struct v4l2_capability cap = { 0 };
294 (void)_snprintf(device,
sizeof(device),
"/dev/video%" PRIu32, n);
295 int fd = open(device, O_RDONLY);
300 if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0 || !(cap.device_caps & V4L2_CAP_VIDEO_CAPTURE))
307 const char* deviceName = (
char*)cap.card;
308 const char* deviceId = device;
309 if (cap.bus_info[0] != 0)
310 deviceId = (
char*)cap.bus_info;
312 IFCALL(callback, ecam, hchannel, deviceId, deviceName);
320 static void cam_v4l_stream_free_buffers(CamV4lStream* stream)
322 if (!stream || !stream->buffers)
326 for (
size_t i = 0; i < stream->nBuffers; i++)
328 if (stream->buffers[i].length && stream->buffers[i].start != MAP_FAILED)
330 munmap(stream->buffers[i].start, stream->buffers[i].length);
334 free(stream->buffers);
335 stream->buffers = NULL;
336 stream->nBuffers = 0;
344 static size_t cam_v4l_stream_alloc_buffers(CamV4lStream* stream)
346 struct v4l2_requestbuffers rbuffer = { 0 };
348 rbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
349 rbuffer.memory = V4L2_MEMORY_MMAP;
350 rbuffer.count = CAM_V4L2_BUFFERS_COUNT;
352 if (ioctl(stream->fd, VIDIOC_REQBUFS, &rbuffer) < 0 || rbuffer.count == 0)
354 WLog_ERR(TAG,
"Failure in VIDIOC_REQBUFS, errno %d, count %d", errno, rbuffer.count);
358 stream->nBuffers = rbuffer.count;
361 stream->buffers = (CamV4lBuffer*)calloc(rbuffer.count,
sizeof(CamV4lBuffer));
362 if (!stream->buffers)
364 WLog_ERR(TAG,
"Failure in calloc");
368 for (
unsigned int i = 0; i < rbuffer.count; i++)
370 struct v4l2_buffer buffer = { 0 };
371 buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
372 buffer.memory = V4L2_MEMORY_MMAP;
375 if (ioctl(stream->fd, VIDIOC_QUERYBUF, &buffer) < 0)
377 WLog_ERR(TAG,
"Failure in VIDIOC_QUERYBUF, errno %d", errno);
378 cam_v4l_stream_free_buffers(stream);
382 stream->buffers[i].start = mmap(NULL, buffer.length, PROT_READ | PROT_WRITE, MAP_SHARED,
383 stream->fd, buffer.m.offset);
385 if (MAP_FAILED == stream->buffers[i].start)
387 WLog_ERR(TAG,
"Failure in mmap, errno %d", errno);
388 cam_v4l_stream_free_buffers(stream);
392 stream->buffers[i].length = buffer.length;
394 WLog_DBG(TAG,
"Buffer %d mapped, size: %d", i, buffer.length);
396 if (ioctl(stream->fd, VIDIOC_QBUF, &buffer) < 0)
398 WLog_ERR(TAG,
"Failure in VIDIOC_QBUF, errno %d", errno);
399 cam_v4l_stream_free_buffers(stream);
404 return stream->buffers[0].length;
412 static UINT cam_v4l_stream_capture_thread(
void* param)
414 CamV4lStream* stream = (CamV4lStream*)param;
421 struct pollfd pfd = { 0 };
426 retVal = poll(&pfd, 1, CAM_V4L2_CAPTURE_THREAD_SLEEP_MS);
435 WLog_DBG(TAG,
"Failure in poll, errno %d", errno);
436 Sleep(CAM_V4L2_CAPTURE_THREAD_SLEEP_MS);
439 else if (!(pfd.revents & POLLIN))
441 WLog_DBG(TAG,
"poll reported non-read event %d", pfd.revents);
442 Sleep(CAM_V4L2_CAPTURE_THREAD_SLEEP_MS);
446 EnterCriticalSection(&stream->lock);
447 if (stream->streaming)
449 struct v4l2_buffer buf = { 0 };
450 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
451 buf.memory = V4L2_MEMORY_MMAP;
454 while (ioctl(fd, VIDIOC_DQBUF, &buf) != -1)
456 stream->sampleCallback(stream->dev, stream->streamIndex,
457 stream->buffers[buf.index].start, buf.bytesused);
460 if (ioctl(fd, VIDIOC_QBUF, &buf) == -1)
462 WLog_ERR(TAG,
"Failure in VIDIOC_QBUF, errno %d", errno);
466 LeaveCriticalSection(&stream->lock);
468 }
while (stream->streaming);
470 return CHANNEL_RC_OK;
473 void cam_v4l_stream_close_device(CamV4lStream* stream)
475 if (stream->fd != -1)
487 static CamV4lStream* cam_v4l_stream_create(
CameraDevice* dev,
int streamIndex,
488 ICamHalSampleCapturedCallback callback)
490 CamV4lStream* stream = calloc(1,
sizeof(CamV4lStream));
494 WLog_ERR(TAG,
"Failure in calloc");
498 stream->streamIndex = streamIndex;
499 stream->sampleCallback = callback;
503 if (!InitializeCriticalSectionEx(&stream->lock, 0, 0))
505 WLog_ERR(TAG,
"Failure in calloc");
518 UINT cam_v4l_stream_stop(CamV4lStream* stream)
520 if (!stream || !stream->streaming)
521 return CHANNEL_RC_OK;
523 stream->streaming = FALSE;
525 if (stream->captureThread)
527 (void)WaitForSingleObject(stream->captureThread, INFINITE);
528 (void)CloseHandle(stream->captureThread);
529 stream->captureThread = NULL;
532 EnterCriticalSection(&stream->lock);
535 enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
536 if (ioctl(stream->fd, VIDIOC_STREAMOFF, &type) < 0)
538 WLog_ERR(TAG,
"Failure in VIDIOC_STREAMOFF, errno %d", errno);
541 cam_v4l_stream_free_buffers(stream);
542 cam_v4l_stream_close_device(stream);
544 LeaveCriticalSection(&stream->lock);
546 return CHANNEL_RC_OK;
554 static UINT cam_v4l_stream_start(ICamHal* ihal,
CameraDevice* dev,
int streamIndex,
556 ICamHalSampleCapturedCallback callback)
558 CamV4lHal* hal = (CamV4lHal*)ihal;
560 CamV4lStream* stream = (CamV4lStream*)HashTable_GetItemValue(hal->streams, dev->deviceId);
564 stream = cam_v4l_stream_create(dev, streamIndex, callback);
566 return CAM_ERROR_CODE_OutOfMemory;
568 if (!HashTable_Insert(hal->streams, dev->deviceId, stream))
570 cam_v4l_stream_free(stream);
571 return CAM_ERROR_CODE_UnexpectedError;
575 if (stream->streaming)
577 WLog_ERR(TAG,
"Streaming already in progress, device %s, streamIndex %d", dev->deviceId,
579 return CAM_ERROR_CODE_UnexpectedError;
582 if ((stream->fd = cam_v4l_open_device(dev->deviceId, O_RDWR | O_NONBLOCK)) == -1)
584 WLog_ERR(TAG,
"Unable to open device %s", dev->deviceId);
585 return CAM_ERROR_CODE_UnexpectedError;
588 struct v4l2_format video_fmt = { 0 };
589 video_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
590 video_fmt.fmt.pix.sizeimage = 0;
591 video_fmt.fmt.pix.width = mediaType->Width;
592 video_fmt.fmt.pix.height = mediaType->Height;
593 UINT32 pixelFormat = ecamToV4L2PixFormat(mediaType->Format);
594 if (pixelFormat == 0)
596 cam_v4l_stream_close_device(stream);
597 return CAM_ERROR_CODE_InvalidMediaType;
599 video_fmt.fmt.pix.pixelformat = pixelFormat;
602 if (ioctl(stream->fd, VIDIOC_S_FMT, &video_fmt) < 0)
604 WLog_ERR(TAG,
"Failure in VIDIOC_S_FMT, errno %d", errno);
605 cam_v4l_stream_close_device(stream);
606 return CAM_ERROR_CODE_InvalidMediaType;
610 struct v4l2_streamparm sp1 = { 0 };
611 struct v4l2_streamparm sp2 = { 0 };
612 sp1.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
613 if (ioctl(stream->fd, VIDIOC_G_PARM, &sp1) < 0 ||
614 !(sp1.parm.capture.capability & V4L2_CAP_TIMEPERFRAME))
616 WLog_INFO(TAG,
"Driver doesn't support setting framerate");
620 sp2.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
623 sp2.parm.capture.timeperframe.numerator = mediaType->FrameRateDenominator;
624 sp2.parm.capture.timeperframe.denominator = mediaType->FrameRateNumerator;
626 if (ioctl(stream->fd, VIDIOC_S_PARM, &sp2) < 0)
628 WLog_INFO(TAG,
"Failed to set the framerate, errno %d", errno);
632 size_t maxSample = cam_v4l_stream_alloc_buffers(stream);
635 WLog_ERR(TAG,
"Failure to allocate video buffers");
636 cam_v4l_stream_close_device(stream);
637 return CAM_ERROR_CODE_OutOfMemory;
640 stream->streaming = TRUE;
643 enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
644 if (ioctl(stream->fd, VIDIOC_STREAMON, &type) < 0)
646 WLog_ERR(TAG,
"Failure in VIDIOC_STREAMON, errno %d", errno);
647 cam_v4l_stream_stop(stream);
648 return CAM_ERROR_CODE_UnexpectedError;
651 stream->captureThread = CreateThread(NULL, 0, cam_v4l_stream_capture_thread, stream, 0, NULL);
652 if (!stream->captureThread)
654 WLog_ERR(TAG,
"CreateThread failure");
655 cam_v4l_stream_stop(stream);
656 return CAM_ERROR_CODE_OutOfMemory;
659 char fourccstr[5] = { 0 };
660 WLog_INFO(TAG,
"Camera format: %s, width: %u, height: %u, fps: %u/%u",
661 cam_v4l_get_fourcc_str(pixelFormat, fourccstr, ARRAYSIZE(fourccstr)),
662 mediaType->Width, mediaType->Height, mediaType->FrameRateNumerator,
663 mediaType->FrameRateDenominator);
665 return CHANNEL_RC_OK;
673 static UINT cam_v4l_stream_stop_by_device_id(ICamHal* ihal,
const char* deviceId,
int streamIndex)
675 CamV4lHal* hal = (CamV4lHal*)ihal;
677 CamV4lStream* stream = (CamV4lStream*)HashTable_GetItemValue(hal->streams, deviceId);
680 return CHANNEL_RC_OK;
682 return cam_v4l_stream_stop(stream);
691 void cam_v4l_stream_free(
void* obj)
693 CamV4lStream* stream = (CamV4lStream*)obj;
697 cam_v4l_stream_stop(stream);
699 DeleteCriticalSection(&stream->lock);
708 static UINT cam_v4l_free(ICamHal* ihal)
710 CamV4lHal* hal = (CamV4lHal*)ihal;
713 return ERROR_INVALID_PARAMETER;
715 HashTable_Free(hal->streams);
719 return CHANNEL_RC_OK;
727 FREERDP_ENTRY_POINT(UINT VCAPITYPE v4l_freerdp_rdpecam_client_subsystem_entry(
730 UINT ret = CHANNEL_RC_OK;
731 WINPR_ASSERT(pEntryPoints);
733 CamV4lHal* hal = (CamV4lHal*)calloc(1,
sizeof(CamV4lHal));
736 return CHANNEL_RC_NO_MEMORY;
738 hal->iHal.Enumerate = cam_v4l_enumerate;
739 hal->iHal.GetMediaTypeDescriptions = cam_v4l_get_media_type_descriptions;
740 hal->iHal.StartStream = cam_v4l_stream_start;
741 hal->iHal.StopStream = cam_v4l_stream_stop_by_device_id;
742 hal->iHal.Free = cam_v4l_free;
744 hal->streams = HashTable_New(FALSE);
747 ret = CHANNEL_RC_NO_MEMORY;
751 HashTable_SetupForStringData(hal->streams, FALSE);
753 wObject* obj = HashTable_ValueObject(hal->streams);
755 obj->fnObjectFree = cam_v4l_stream_free;
757 if ((ret = pEntryPoints->pRegisterCameraHal(pEntryPoints->plugin, &hal->iHal)))
759 WLog_ERR(TAG,
"RegisterCameraHal failed with error %" PRIu32
"", ret);
766 cam_v4l_free(&hal->iHal);
This struct contains function pointer to initialize/free objects.