0

I'v written a simple C shared object library which calls v4l2(Video for Linux two) API e.g. v4l2_open(). Then I'm trying to poll() on the returned device handle but it always return POLLERR in the revents. I tried different parameters with timeout but it does not help. Here is the complete code.

/*
 * libwebcam.h
 *
 *  Created on: 13.04.2016
 *      Author: max
 */

#ifndef LIBWEBCAM_H_
#define LIBWEBCAM_H_

#include <stdint.h>

struct webcam
{
    int fd;
    uint32_t width;
    uint32_t height;
    uint32_t sizeimage;
    uint32_t bytesperline;
    uint8_t *image_buffer;
    void *priv_data;
};

int webcam_open(struct webcam *w);
int webcam_close(struct webcam *w);
int webcam_take_image(struct webcam *w);
int webcam_poll(struct webcam *w);

#endif /* LIBWEBCAM_H_ */

/*
 * libwebcam.c
 *
 *  Created on: 13.04.2016
 *      Author: max
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <linux/videodev2.h>
#include <libv4l2.h>
#include <poll.h>
#include "libwebcam.h"

int webcam_open(struct webcam *w)
{
    struct v4l2_capability caps;
    struct v4l2_format fmt;
    int dev_index;
    int dev;
    char buffer[255];

    for (dev_index = 0; dev_index < 64; dev_index++) {
        memset(&buffer, 0, sizeof(buffer));
        sprintf(buffer, "/dev/video%d", dev_index);
#ifdef DEBUG
        printf("libwebcam: Probing %s\n", buffer);
#endif
        dev = v4l2_open(buffer, O_RDWR | O_NONBLOCK, 0);
        if (dev != -1) {
            memset(&caps, 0, sizeof(caps));
            if (v4l2_ioctl(dev, VIDIOC_QUERYCAP, &caps) == -1) {
                return -1;
            }
            if (caps.capabilities & V4L2_CAP_VIDEO_CAPTURE) {
#ifdef DEBUG
                printf("libwebcam: %s is video capture device\n", buffer);
#endif
                memset(&fmt, 0, sizeof(fmt));
                fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
                if(v4l2_ioctl(dev, VIDIOC_G_FMT, &fmt) == -1) {
                    return -1;
                }
                fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB24;
                if(v4l2_ioctl(dev, VIDIOC_S_FMT, &fmt) == -1) {
                    return -1;
                }
                if(v4l2_ioctl(dev, VIDIOC_G_FMT, &fmt) == -1) {
                    return -1;
                }
                if(w)
                {
                    w->fd = dev;
                    w->bytesperline = fmt.fmt.pix.bytesperline;
                    w->height = fmt.fmt.pix.height;
                    w->sizeimage = fmt.fmt.pix.sizeimage;
                    w->width = fmt.fmt.pix.width;
                    w->image_buffer = calloc(fmt.fmt.pix.sizeimage, sizeof(uint8_t));
                    if(w->image_buffer == NULL)
                        return -1;
                    w->priv_data = calloc(1, sizeof(struct pollfd));
                    if(w->priv_data == NULL)
                        return -1;
                    return 0;
                }
                else
                {
                    errno = EINVAL;
                    return -1;
                }
            }
        }
    }
    errno = ENODEV;
    return -1;
}

int webcam_close(struct webcam *w)
{
    if(w)
    {
        if(w->image_buffer != NULL){
            free(w->image_buffer);
            w->image_buffer = NULL;
        }
        if(w->priv_data != NULL) {
            free(w->priv_data);
            w->priv_data = NULL;
        }
        if(w->fd != -1)
            if(v4l2_close(w->fd) == -1)
                return -1;
        return 0;
    }
    errno = EINVAL;
    return -1;
}

int webcam_take_image(struct webcam *w)
{
    if(w)
    {
        return v4l2_read(w->fd, w->image_buffer, w->sizeimage * sizeof(uint8_t));
    }
    errno = EINVAL;
    return -1;
}

int webcam_poll(struct webcam *w)
{
    if(w)
    {
        ((struct pollfd *)w->priv_data)->events = POLLIN;
        ((struct pollfd *)w->priv_data)->revents = 0;
        ((struct pollfd *)w->priv_data)->fd = w->fd;
        if(poll(((struct pollfd*)w->priv_data), 1, -1) == -1)
        {
            return -1;
        }

        if(((struct pollfd*)w->priv_data)->revents & POLLIN) {
#ifdef DEBUG
            printf("libwebcam: Data is available...\n");
#endif
            return 1;
        }
        if(((struct pollfd*)w->priv_data)->revents & POLLERR) {
#ifdef DEBUG
            printf("libwebcam: Error in poll...\n");
#endif
            return -1;
        }

#ifdef DEBUG
        printf("libwebcam: Timeout...\n");
#endif
        return 0;
    }
#ifdef DEBUG
    printf("libwebcam: Struct not valid...\n");
#endif
    errno = EINVAL;
    return -1;
}

/*
 * test.c
 *
 *  Created on: 14.04.2016
 *      Author: max
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <libwebcam.h>

int main(int argc, char **argv)
{
    struct webcam w;
    int polled;

    memset(&w, 0, sizeof(w));

    if(webcam_open(&w) == -1)
    {
        perror("Unable to find webcam");
        return EXIT_FAILURE;
    }

    while(1)
    {
        polled = webcam_poll(&w);
        if(polled == -1)
        {
            perror("Error in poll");
            return EXIT_FAILURE;
        }

        if(polled == 1)
        {
            webcam_take_image(&w);
        }
    }

    if(webcam_close(&w) == -1)
    {
        perror("Unable to close webcam");
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}

Can anyone tell me what is going on in the code?

1 Answers1

0

I tested your code, and on my system the poll call is returning 0. From the man page, "A value of 0 indicates that the call timed out and no file descriptors were ready." So you should test for 0 in addition to -1 and 1.

I don't know why it fills in revents with POLLERR, but I would only check the resulting revents field if poll returned 1.

Also, not all V4L2 devices support read(), so you should check that your device supports it by testingcaps.capabilities & V4L2_CAP_READWRITE. My laptop webcam where I tested this does not support read/write.

See this example program for reference.

jamieguinan
  • 1,640
  • 1
  • 10
  • 14
  • UVC devices in particular [don't support read](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/drivers/media/usb/uvc/uvc_v4l2.c?id=refs/tags/v4.6-rc3#n1456). – jamieguinan Apr 14 '16 at 16:49
  • I did check for V4L2_CAP_READWRITE... and actually it is a UVC device. I tried select() and it works. But when select returns 1 the v4l2_read() operation returns EINVAL. The documentation states that if EINVAL is set in errno then the read() operation is not supported by the driver. But why does the capability query returns READ/WRITE supported by driver? :| –  Apr 14 '16 at 19:02
  • It looks like its related to libv4l2. ["We always support read() as we fake it using mmap mode"](https://github.com/philips/libv4l/blob/master/libv4l2/libv4l2.c#L871). – jamieguinan Apr 14 '16 at 19:29
  • ["So using mmap mode under the hood will fail if a select() or poll() is done before the first emulated read() call.](https://github.com/philips/libv4l/blob/master/libv4l2/libv4l2.c#L1160). Thanks, guys! Sigh... – jamieguinan Apr 14 '16 at 19:31
  • Ok. So calling select() or poll() will fail read() calls. Doh! –  Apr 14 '16 at 19:36
  • I get that libv4l2 is trying to be helpful, but you seem to have hit the *one* corner case that misbehaves. Maybe after you do one v4l2_read() call it will work? That's what the comment seems to indicate. – jamieguinan Apr 14 '16 at 19:42
  • Tried it. Did call v4l2_read() once then select() repeated, and it fails. –  Apr 14 '16 at 19:45
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/109193/discussion-between-jamieguinan-and-maxpa1n87). – jamieguinan Apr 14 '16 at 19:50