27#include <linux/videodev2.h> 
   29#include "camera_v4l.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 
   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);
 
   58static const char* cam_v4l_get_fourcc_str(
unsigned int fourcc, 
char* buffer, 
size_t size)
 
   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);
 
   76static UINT32 ecamToV4L2PixFormat(CAM_MEDIA_FORMAT ecamFormat)
 
   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;
 
   95      WLog_ERR(TAG, 
"Unsupported CAM_MEDIA_FORMAT %d", ecamFormat);
 
  105static BOOL cam_v4l_format_supported(
int fd, UINT32 format)
 
  107  struct v4l2_fmtdesc fmtdesc = { 0 };
 
  108  fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 
  110  for (fmtdesc.index = 0; ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc) == 0; fmtdesc.index++)
 
  112    if (fmtdesc.pixelformat == format)
 
  123static int cam_v4l_open_device(
const char* deviceId, 
int flags)
 
  125  char device[20] = { 0 };
 
  127  struct v4l2_capability cap = { 0 };
 
  132  if (0 == strncmp(deviceId, 
"/dev/video", 10))
 
  133    return open(deviceId, flags);
 
  135  for (UINT n = 0; n < 64; n++)
 
  137    (void)_snprintf(device, 
sizeof(device), 
"/dev/video%" PRIu32, n);
 
  138    if ((fd = open(device, flags)) == -1)
 
  142    if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0 || !(cap.device_caps & V4L2_CAP_VIDEO_CAPTURE))
 
  148    if (cap.bus_info[0] != 0 && 0 == strcmp((
const char*)cap.bus_info, deviceId))
 
  157static BOOL cam_v4l_activate(ICamHal* ihal, 
const char* deviceId, UINT32* errorCode)
 
  160  WINPR_UNUSED(deviceId);
 
  166static BOOL cam_v4l_deactivate(ICamHal* ihal, 
const char* deviceId, UINT32* errorCode)
 
  169  WINPR_UNUSED(deviceId);
 
  181static INT16 cam_v4l_get_media_type_descriptions(ICamHal* ihal, 
const char* deviceId,
 
  184                                                 size_t nSupportedFormats,
 
  188  CamV4lHal* hal = (CamV4lHal*)ihal;
 
  189  size_t maxMediaTypes = *nMediaTypes;
 
  191  BOOL formatFound = FALSE;
 
  197    stream = cam_v4l_stream_create(deviceId, streamIndex);
 
  199      return CAM_ERROR_CODE_OutOfMemory;
 
  201    if (!HashTable_Insert(hal->streams, deviceId, stream))
 
  203      cam_v4l_stream_free(stream);
 
  204      return CAM_ERROR_CODE_UnexpectedError;
 
  208  int fd = cam_v4l_open_device(deviceId, O_RDONLY);
 
  211    WLog_ERR(TAG, 
"Unable to open device %s", deviceId);
 
  215  size_t formatIndex = 0;
 
  216  for (; formatIndex < nSupportedFormats; formatIndex++)
 
  218    UINT32 pixelFormat = 0;
 
  219    if (supportedFormats[formatIndex].inputFormat == CAM_MEDIA_FORMAT_MJPG_H264)
 
  221      if (stream->h264UnitId > 0)
 
  222        pixelFormat = V4L2_PIX_FMT_MJPEG;
 
  228      pixelFormat = ecamToV4L2PixFormat(supportedFormats[formatIndex].inputFormat);
 
  231    WINPR_ASSERT(pixelFormat != 0);
 
  232    struct v4l2_frmsizeenum frmsize = { 0 };
 
  234    if (!cam_v4l_format_supported(fd, pixelFormat))
 
  237    frmsize.pixel_format = pixelFormat;
 
  238    for (frmsize.index = 0; ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frmsize) == 0; frmsize.index++)
 
  240      struct v4l2_frmivalenum frmival = { 0 };
 
  242      if (frmsize.type != V4L2_FRMSIZE_TYPE_DISCRETE)
 
  246      mediaTypes->Width = frmsize.discrete.width;
 
  247      mediaTypes->Height = frmsize.discrete.height;
 
  248      mediaTypes->Format = supportedFormats[formatIndex].inputFormat;
 
  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)
 
  259        mediaTypes->FrameRateNumerator = frmival.discrete.denominator;
 
  260        mediaTypes->FrameRateDenominator = frmival.discrete.numerator;
 
  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;
 
  269      mediaTypes->PixelAspectRatioNumerator = mediaTypes->PixelAspectRatioDenominator = 1;
 
  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);
 
  280      if (nTypes == maxMediaTypes)
 
  282        WLog_ERR(TAG, 
"Media types reached buffer maximum %" PRIu32 
"", maxMediaTypes);
 
  296  *nMediaTypes = nTypes;
 
  298  if (formatIndex > INT16_MAX)
 
  300  return (INT16)formatIndex;
 
  308static UINT cam_v4l_enumerate(WINPR_ATTR_UNUSED ICamHal* ihal, ICamHalEnumCallback callback,
 
  313  for (UINT n = 0; n < 64; n++)
 
  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);
 
  323    if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0 || !(cap.device_caps & V4L2_CAP_VIDEO_CAPTURE))
 
  330    const char* deviceName = (
char*)cap.card;
 
  331    const char* deviceId = device;
 
  332    if (cap.bus_info[0] != 0) 
 
  333      deviceId = (
char*)cap.bus_info;
 
  335    IFCALL(callback, ecam, hchannel, deviceId, deviceName);
 
  343static void cam_v4l_stream_free_buffers(
CamV4lStream* stream)
 
  345  if (!stream || !stream->buffers)
 
  349  for (
size_t i = 0; i < stream->nBuffers; i++)
 
  351    if (stream->buffers[i].length && stream->buffers[i].start != MAP_FAILED)
 
  353      munmap(stream->buffers[i].start, stream->buffers[i].length);
 
  357  free(stream->buffers);
 
  358  stream->buffers = NULL;
 
  359  stream->nBuffers = 0;
 
  367static size_t cam_v4l_stream_alloc_buffers(
CamV4lStream* stream)
 
  369  struct v4l2_requestbuffers rbuffer = { 0 };
 
  371  rbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 
  372  rbuffer.memory = V4L2_MEMORY_MMAP;
 
  373  rbuffer.count = CAM_V4L2_BUFFERS_COUNT;
 
  375  if (ioctl(stream->fd, VIDIOC_REQBUFS, &rbuffer) < 0 || rbuffer.count == 0)
 
  377    char buffer[64] = { 0 };
 
  378    WLog_ERR(TAG, 
"Failure in VIDIOC_REQBUFS, errno  %s [%d], count %d",
 
  379             winpr_strerror(errno, buffer, 
sizeof(buffer)), errno, rbuffer.count);
 
  383  stream->nBuffers = rbuffer.count;
 
  387  if (!stream->buffers)
 
  389    WLog_ERR(TAG, 
"Failure in calloc");
 
  393  for (
unsigned int i = 0; i < rbuffer.count; i++)
 
  395    struct v4l2_buffer vbuffer = { 0 };
 
  396    vbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 
  397    vbuffer.memory = V4L2_MEMORY_MMAP;
 
  400    if (ioctl(stream->fd, VIDIOC_QUERYBUF, &vbuffer) < 0)
 
  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);
 
  409    stream->buffers[i].start = mmap(NULL, vbuffer.length, PROT_READ | PROT_WRITE, MAP_SHARED,
 
  410                                    stream->fd, vbuffer.m.offset);
 
  412    if (MAP_FAILED == stream->buffers[i].start)
 
  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);
 
  421    stream->buffers[i].length = vbuffer.length;
 
  423    WLog_DBG(TAG, 
"Buffer %d mapped, size: %d", i, vbuffer.length);
 
  425    if (ioctl(stream->fd, VIDIOC_QBUF, &vbuffer) < 0)
 
  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);
 
  435  return stream->buffers[0].length;
 
  443static UINT cam_v4l_stream_capture_thread(
void* param)
 
  452    struct pollfd pfd = { 0 };
 
  457    retVal = poll(&pfd, 1, CAM_V4L2_CAPTURE_THREAD_SLEEP_MS);
 
  466      char buffer[64] = { 0 };
 
  467      WLog_DBG(TAG, 
"Failure in poll, errno %s [%d]",
 
  468               winpr_strerror(errno, buffer, 
sizeof(buffer)), errno);
 
  469      Sleep(CAM_V4L2_CAPTURE_THREAD_SLEEP_MS); 
 
  472    else if (!(pfd.revents & POLLIN))
 
  474      WLog_DBG(TAG, 
"poll reported non-read event %d", pfd.revents);
 
  475      Sleep(CAM_V4L2_CAPTURE_THREAD_SLEEP_MS); 
 
  479    EnterCriticalSection(&stream->lock);
 
  480    if (stream->streaming)
 
  482      struct v4l2_buffer buf = { 0 };
 
  483      buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 
  484      buf.memory = V4L2_MEMORY_MMAP;
 
  487      while (ioctl(fd, VIDIOC_DQBUF, &buf) != -1)
 
  489        stream->sampleCallback(stream->dev, stream->streamIndex,
 
  490                               stream->buffers[buf.index].start, buf.bytesused);
 
  493        if (ioctl(fd, VIDIOC_QBUF, &buf) == -1)
 
  495          char buffer[64] = { 0 };
 
  496          WLog_ERR(TAG, 
"Failure in VIDIOC_QBUF, errno %s [%d]",
 
  497                   winpr_strerror(errno, buffer, 
sizeof(buffer)), errno);
 
  501    LeaveCriticalSection(&stream->lock);
 
  503  } 
while (stream->streaming);
 
  505  return CHANNEL_RC_OK;
 
  510  if (stream->fd != -1)
 
  522static CamV4lStream* cam_v4l_stream_create(
const char* deviceId, 
int streamIndex)
 
  528    WLog_ERR(TAG, 
"Failure in calloc");
 
  531  stream->streamIndex = streamIndex;
 
  533  stream->h264UnitId = get_uvc_h624_unit_id(deviceId);
 
  535  if (!InitializeCriticalSectionEx(&stream->lock, 0, 0))
 
  537    WLog_ERR(TAG, 
"Failure in calloc");
 
  552  if (!stream || !stream->streaming)
 
  553    return CHANNEL_RC_OK;
 
  555  stream->streaming = FALSE; 
 
  557  if (stream->captureThread)
 
  559    (void)WaitForSingleObject(stream->captureThread, INFINITE);
 
  560    (void)CloseHandle(stream->captureThread);
 
  561    stream->captureThread = NULL;
 
  564  EnterCriticalSection(&stream->lock);
 
  567  enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 
  568  if (ioctl(stream->fd, VIDIOC_STREAMOFF, &type) < 0)
 
  570    char buffer[64] = { 0 };
 
  571    WLog_ERR(TAG, 
"Failure in VIDIOC_STREAMOFF, errno %s [%d]",
 
  572             winpr_strerror(errno, buffer, 
sizeof(buffer)), errno);
 
  575  cam_v4l_stream_free_buffers(stream);
 
  576  cam_v4l_stream_close_device(stream);
 
  578  LeaveCriticalSection(&stream->lock);
 
  580  return CHANNEL_RC_OK;
 
  588static UINT cam_v4l_stream_start(ICamHal* ihal, 
CameraDevice* dev, 
int streamIndex,
 
  590                                 ICamHalSampleCapturedCallback callback)
 
  592  CamV4lHal* hal = (CamV4lHal*)ihal;
 
  598    WLog_ERR(TAG, 
"Unable to find stream, device %s, streamIndex %d", dev->deviceId,
 
  600    return CAM_ERROR_CODE_UnexpectedError;
 
  603  if (stream->streaming)
 
  605    WLog_ERR(TAG, 
"Streaming already in progress, device %s, streamIndex %d", dev->deviceId,
 
  607    return CAM_ERROR_CODE_UnexpectedError;
 
  611  stream->sampleCallback = callback;
 
  613  if ((stream->fd = cam_v4l_open_device(dev->deviceId, O_RDWR | O_NONBLOCK)) == -1)
 
  615    WLog_ERR(TAG, 
"Unable to open device %s", dev->deviceId);
 
  616    return CAM_ERROR_CODE_UnexpectedError;
 
  619  struct v4l2_format video_fmt = { 0 };
 
  620  video_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 
  622  UINT32 pixelFormat = 0;
 
  623  if (mediaType->Format == CAM_MEDIA_FORMAT_MJPG_H264)
 
  625    if (!set_h264_muxed_format(stream, mediaType))
 
  627      WLog_ERR(TAG, 
"Failure to set H264 muxed format");
 
  628      cam_v4l_stream_close_device(stream);
 
  629      return CAM_ERROR_CODE_UnexpectedError;
 
  632    pixelFormat = V4L2_PIX_FMT_MJPEG;
 
  634    video_fmt.fmt.pix.width = 640;
 
  635    video_fmt.fmt.pix.height = 480;
 
  639    pixelFormat = ecamToV4L2PixFormat(mediaType->Format);
 
  640    video_fmt.fmt.pix.width = mediaType->Width;
 
  641    video_fmt.fmt.pix.height = mediaType->Height;
 
  644  if (pixelFormat == 0)
 
  646    cam_v4l_stream_close_device(stream);
 
  647    return CAM_ERROR_CODE_InvalidMediaType;
 
  650  video_fmt.fmt.pix.pixelformat = pixelFormat;
 
  653  if (ioctl(stream->fd, VIDIOC_S_FMT, &video_fmt) < 0)
 
  655    char buffer[64] = { 0 };
 
  656    WLog_ERR(TAG, 
"Failure in VIDIOC_S_FMT, errno %s [%d]",
 
  657             winpr_strerror(errno, buffer, 
sizeof(buffer)), errno);
 
  658    cam_v4l_stream_close_device(stream);
 
  659    return CAM_ERROR_CODE_InvalidMediaType;
 
  663  struct v4l2_streamparm sp1 = { 0 };
 
  664  struct v4l2_streamparm sp2 = { 0 };
 
  665  sp1.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 
  666  if (ioctl(stream->fd, VIDIOC_G_PARM, &sp1) < 0 ||
 
  667      !(sp1.parm.capture.capability & V4L2_CAP_TIMEPERFRAME))
 
  669    WLog_INFO(TAG, 
"Driver doesn't support setting framerate");
 
  673    sp2.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 
  676    sp2.parm.capture.timeperframe.numerator = mediaType->FrameRateDenominator;
 
  677    sp2.parm.capture.timeperframe.denominator = mediaType->FrameRateNumerator;
 
  679    if (ioctl(stream->fd, VIDIOC_S_PARM, &sp2) < 0)
 
  681      char buffer[64] = { 0 };
 
  682      WLog_INFO(TAG, 
"Failed to set the framerate, errno %s [%d]",
 
  683                winpr_strerror(errno, buffer, 
sizeof(buffer)), errno);
 
  687  size_t maxSample = cam_v4l_stream_alloc_buffers(stream);
 
  690    WLog_ERR(TAG, 
"Failure to allocate video buffers");
 
  691    cam_v4l_stream_close_device(stream);
 
  692    return CAM_ERROR_CODE_OutOfMemory;
 
  695  stream->streaming = TRUE;
 
  698  enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 
  699  if (ioctl(stream->fd, VIDIOC_STREAMON, &type) < 0)
 
  701    char buffer[64] = { 0 };
 
  702    WLog_ERR(TAG, 
"Failure in VIDIOC_STREAMON, errno %s [%d]",
 
  703             winpr_strerror(errno, buffer, 
sizeof(buffer)), errno);
 
  704    cam_v4l_stream_stop(stream);
 
  705    return CAM_ERROR_CODE_UnexpectedError;
 
  708  stream->captureThread = CreateThread(NULL, 0, cam_v4l_stream_capture_thread, stream, 0, NULL);
 
  709  if (!stream->captureThread)
 
  711    WLog_ERR(TAG, 
"CreateThread failure");
 
  712    cam_v4l_stream_stop(stream);
 
  713    return CAM_ERROR_CODE_OutOfMemory;
 
  716  char fourccstr[16] = { 0 };
 
  717  if (mediaType->Format == CAM_MEDIA_FORMAT_MJPG_H264)
 
  718    strncpy(fourccstr, 
"H264 muxed", ARRAYSIZE(fourccstr) - 1);
 
  720    cam_v4l_get_fourcc_str(pixelFormat, fourccstr, ARRAYSIZE(fourccstr));
 
  722  WLog_INFO(TAG, 
"Camera format: %s, width: %u, height: %u, fps: %u/%u", fourccstr,
 
  723            mediaType->Width, mediaType->Height, mediaType->FrameRateNumerator,
 
  724            mediaType->FrameRateDenominator);
 
  726  return CHANNEL_RC_OK;
 
  734static UINT cam_v4l_stream_stop_by_device_id(ICamHal* ihal, 
const char* deviceId,
 
  735                                             WINPR_ATTR_UNUSED 
int streamIndex)
 
  737  CamV4lHal* hal = (CamV4lHal*)ihal;
 
  742    return CHANNEL_RC_OK;
 
  744  return cam_v4l_stream_stop(stream);
 
  753void cam_v4l_stream_free(
void* obj)
 
  759  cam_v4l_stream_stop(stream);
 
  761  DeleteCriticalSection(&stream->lock);
 
  770static UINT cam_v4l_free(ICamHal* ihal)
 
  772  CamV4lHal* hal = (CamV4lHal*)ihal;
 
  775    return ERROR_INVALID_PARAMETER;
 
  777  HashTable_Free(hal->streams);
 
  781  return CHANNEL_RC_OK;
 
  789FREERDP_ENTRY_POINT(UINT VCAPITYPE v4l_freerdp_rdpecam_client_subsystem_entry(
 
  792  UINT ret = CHANNEL_RC_OK;
 
  793  WINPR_ASSERT(pEntryPoints);
 
  795  CamV4lHal* hal = (CamV4lHal*)calloc(1, 
sizeof(CamV4lHal));
 
  798    return CHANNEL_RC_NO_MEMORY;
 
  800  hal->iHal.Enumerate = cam_v4l_enumerate;
 
  801  hal->iHal.GetMediaTypeDescriptions = cam_v4l_get_media_type_descriptions;
 
  802  hal->iHal.Activate = cam_v4l_activate;
 
  803  hal->iHal.Deactivate = cam_v4l_deactivate;
 
  804  hal->iHal.StartStream = cam_v4l_stream_start;
 
  805  hal->iHal.StopStream = cam_v4l_stream_stop_by_device_id;
 
  806  hal->iHal.Free = cam_v4l_free;
 
  808  hal->streams = HashTable_New(FALSE);
 
  811    ret = CHANNEL_RC_NO_MEMORY;
 
  815  HashTable_SetupForStringData(hal->streams, FALSE);
 
  817  wObject* obj = HashTable_ValueObject(hal->streams);
 
  819  obj->fnObjectFree = cam_v4l_stream_free;
 
  821  if ((ret = pEntryPoints->pRegisterCameraHal(pEntryPoints->plugin, &hal->iHal)))
 
  823    WLog_ERR(TAG, 
"RegisterCameraHal failed with error %" PRIu32 
"", ret);
 
  830  cam_v4l_free(&hal->iHal);
 
This struct contains function pointer to initialize/free objects.