dimanche 26 juin 2016

V4L2 not working for USB camera but works with Laptop webcam

I am trying to understand how to use V4L2 and am writing my own camera driver in C. I have followed this tutorial mostly to get a feel for how V4L2 works, and have copied most of the code to implement my own here, I am however stuck at trying to get USB webcams working, I have got this code to work with the laptop's integrated webcam, just not external USB ones (Logitech c270 - works in OpenCV). Any help would be much appreciated.

The source code camera.c is as follows:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/ioctl.h>

#include <linux/videodev2.h>

/* #include <SDL/SDL.h> */
/* #include <SDL/SDL_image.h> */


static int webcam_open(const char *webcam_dev)
{
    int fd;
    struct v4l2_capability cap;

    fd = open(webcam_dev, O_RDWR);
    if (fd < 0) {
        perror("open");
        exit(1);
    }

    if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0) {
        perror("VIDIOC_QUERYCAP");
        exit(1);
    }

    if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
        fprintf(stderr, "Device cannot handle single-planar video capture.n");
        exit(1);
    }

    return fd;
}

static void *webcam_setup(int fd)
{
    struct v4l2_format format;
    struct v4l2_requestbuffers bufrequest;
    struct v4l2_buffer bufferinfo;
    void *buffer_start;

    /* configure video format */
    memset(&format, 0, sizeof(format));
    format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    format.fmt.pix.width = 800;
    format.fmt.pix.height = 600;
    format.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
    format.fmt.pix.field = V4L2_FIELD_NONE;

    if (ioctl(fd, VIDIOC_S_FMT, &format) < 0) {
        perror("VIDIOC_S_FMT");
        exit(1);
    }

    /* configure buffers */
    memset(&bufrequest, 0, sizeof(bufrequest));
    bufrequest.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    bufrequest.memory = V4L2_MEMORY_MMAP;
    bufrequest.count = 1;

    if (ioctl(fd, VIDIOC_REQBUFS, &bufrequest) < 0) {
        perror("VIDIOC_REQBUFS");
        exit(1);
    }

    /* allocate buffer */
    memset(&bufferinfo, 0, sizeof(bufferinfo));
    bufferinfo.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    bufferinfo.memory = V4L2_MEMORY_MMAP;
    bufferinfo.index = 0;

    if (ioctl(fd, VIDIOC_QUERYBUF, &bufferinfo) < 0) {
        perror("VIDIOC_QUERYBUF");
        exit(1);
    }

    // allocate memory map
    buffer_start = mmap(
        NULL,
        bufferinfo.length,
        PROT_READ | PROT_WRITE,
        MAP_SHARED,
        fd,
        bufferinfo.m.offset
    );
    if (buffer_start == MAP_FAILED) {
        perror("mmap");
        exit(1);
    }
    memset(buffer_start, 0, bufferinfo.length);

    return buffer_start;
}

static void webcam_stream(int fd, void *buffer_start)
{
    int retval;
    int buffer_type;
    struct v4l2_buffer bufferinfo;

    /* setup buffer info */
    memset(&bufferinfo, 0, sizeof(bufferinfo));
    bufferinfo.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    bufferinfo.memory = V4L2_MEMORY_MMAP;
    bufferinfo.index = 0;  /* queueing buffer index 0. */

    /* queue buffer */
    if (ioctl(fd, VIDIOC_QBUF, &bufferinfo) < 0){
        perror("VIDIOC_QBUF");
        exit(1);
    }

    /* activate streaming */
    buffer_type = bufferinfo.type;
    if (ioctl(fd, VIDIOC_STREAMON, &buffer_type) < 0){
        perror("VIDIOC_STREAMON");
        exit(1);
    }

    /* dequeue buffer */
    if (ioctl(fd, VIDIOC_DQBUF, &bufferinfo) < 0){
        perror("VIDIOC_QBUF");
        exit(1);
    }
    /* printf("Length: %dnAddress: %pn", bufferinfo.length, buffer_start); */
    /* printf("Image Length: %dn", bufferinfo.bytesused); */

    /* save frame to file */
    int jpgfile;
    if ((jpgfile = open("test.jpg", O_WRONLY | O_CREAT)) < 0){
        perror("open");
        exit(1);
    }

    retval = write(jpgfile, buffer_start, bufferinfo.bytesused);
    if (retval < 0) {
        perror("write");
        exit(1);
    }

    retval = close(jpgfile);
    if (retval < 0) {
        perror("close");
        exit(1);
    }

    /* deactivate streaming */
    if (ioctl(fd, VIDIOC_STREAMOFF, &buffer_type) < 0){
        perror("VIDIOC_STREAMOFF");
        exit(1);
    }
}

int main()
{
    int fd;
    void *buffer_start;

    // setup
    fd = webcam_open("/dev/video0");
    buffer_start = webcam_setup(fd);
    webcam_stream(fd, buffer_start);

    close(fd);
    return 0;
}

To compile this code, simply run:

gcc camera.c -o camera

Note: I believe you may need V4L2 installed.

Aucun commentaire:

Enregistrer un commentaire