V4L2 VIDIOC_REQBUFS cannot allocate enough memory

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

V4L2 VIDIOC_REQBUFS cannot allocate enough memory

3,734 Views
DYD
Contributor I

Dear, 

am writing a MIPI driver without using the I2C functionality, since it is not possible for me to use it. I am writing this for the Google Coral. To write this new driver I looked at this example and adjusted it to only use the MMAP functionality. My new code is this:

    /*
 *  V4L2 video capture example
 *
 *  This program can be used and distributed without restrictions.
 *
 *      This program is provided with the V4L2 API
 * see http://linuxtv.org/docs.php for more information
 */

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

#include <getopt.h>             /* getopt_long() */

#include <fcntl.h>              /* low-level i/o */
#include <unistd.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <sys/ioctl.h>

#include <linux/videodev2.h>


#define CLEAR(x) memset(&(x), 0, sizeof(x))

#ifndef V4L2_PIX_FMT_H264
#define V4L2_PIX_FMT_H264     v4l2_fourcc('H', '2', '6', '4') /* H264 with start codes */
#endif

struct buffer {
        void   *start;
        size_t  length;
};

static char            *dev_name;
static int              fd = -1;
struct buffer          *buffers;
static unsigned int     n_buffers;
static int              out_buf;
static int              force_format;
static int              frame_count = 200;
static int              frame_number = 0;

static void errno_exit(const char *s)
{
        fprintf(stderr, "%s error %d, %s\n", s, errno, strerror(errno));
        exit(EXIT_FAILURE);
}

static int xioctl(int fh, int request, void *arg)
{
        int r;

        do {
                r = ioctl(fh, request, arg);
        } while (-1 == r && EINTR == errno);

        return r;
}

static void process_image(const void *p, int size)
{
        printf("processing image\n");
        frame_number++;
        char filename[15];
        sprintf(filename, "frame-%d.raw", frame_number);    // filename becomes frame-x.raw
        FILE *fp=fopen(filename,"wb");

        if (out_buf)
                fwrite(p, size, 1, fp);                     // write data to file fp

        fflush(fp);
        fclose(fp);
}

static int read_frame(void)
{
        printf("reading frame\n");
        struct v4l2_buffer buf;
        unsigned int i;

        CLEAR(buf);

        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        buf.memory = V4L2_MEMORY_MMAP;

        if (-1 == xioctl(fd, VIDIOC_DQBUF, &buf)) {
                switch (errno) {
                case EAGAIN:
                        return 0;

                case EIO:
                        /* Could ignore EIO, see spec. */

                        /* fall through */

                default:
                        errno_exit("VIDIOC_DQBUF");
                }
        }

        assert(buf.index < n_buffers);

        process_image(buffers[buf.index].start, buf.bytesused);

        if (-1 == xioctl(fd, VIDIOC_QBUF, &buf))
                errno_exit("VIDIOC_QBUF");

        return 1;
}

static void mainloop(void)
{
        printf("mainloop\n");
        unsigned int count;

        count = frame_count;

        while (count-- > 0) {
                printf("count number = %d\n", count );
                for (;;) {
                        fd_set fds;
                        struct timeval tv;
                        int r;

                        FD_ZERO(&fds);      // clear file descriptor
                        FD_SET(fd, &fds);   // set file descriptors to the descriptor fd

                        /* Timeout. */
                        tv.tv_sec = 2;
                        tv.tv_usec = 0;

                        r = select(fd + 1, &fds, NULL, NULL, &tv);    // select uses a timeout, allows program to monitor file descriptors waiting untill files becomes "ready"
                        // returns the number of file descriptors changed. This maybe zero if timeout expires.
                        // probably watching reafds descriptor to change?
                        if (-1 == r) {
                                if (EINTR == errno)
                                        continue;
                                errno_exit("select");
                        }

                        if (0 == r) {
                                fprintf(stderr, "select timeout\n");
                                exit(EXIT_FAILURE);
                        }

                        if (read_frame())   // if one of the descriptors is set, a frame can be read.
                                break;
                        /* EAGAIN - continue select loop. */
                }
        }
        printf("mainloop ended\n");
}

static void stop_capturing(void)
{
        printf("stop capturing\n");
        enum v4l2_buf_type type;

        type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        if (-1 == xioctl(fd, VIDIOC_STREAMOFF, &type))
                errno_exit("VIDIOC_STREAMOFF");
        printf("capturing stopped\n");
}

static void start_capturing(void)
{
        printf("initiating capturing\n");
        unsigned int i;
        enum v4l2_buf_type type;

        for (i = 0; i < n_buffers; ++i) {
                struct v4l2_buffer buf;

                CLEAR(buf);
                buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
                buf.memory = V4L2_MEMORY_MMAP;
                buf.index = i;

                if (-1 == xioctl(fd, VIDIOC_QBUF, &buf))
                        errno_exit("VIDIOC_QBUF");
        }

        type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

        if (-1 == xioctl(fd, VIDIOC_STREAMON, &type)){
                errno_exit("VIDIOC_STREAMON");
        }
        printf("capturing initiated\n");
}

static void uninit_device(void)
{
        unsigned int i;

        for (i = 0; i < n_buffers; ++i){
                if (-1 == munmap(buffers[i].start, buffers[i].length)){
                        errno_exit("munmap");
                }
        }

        free(buffers);
}


static void init_mmap(void)
{
        printf("initiating mmap buffer\n");
        struct v4l2_requestbuffers req;   //struct with details of the buffer to compose

        CLEAR(req);

        req.count = 4;
        req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        req.memory = V4L2_MEMORY_MMAP;

        if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) { // initiate buffer
                if (EINVAL == errno) {
                        fprintf(stderr, "%s does not support "
                                 "memory mapping\n", dev_name);
                        exit(EXIT_FAILURE);
                } else {
                        errno_exit("VIDIOC_REQBUFS");
                }
        }
        printf("memory allocated\n");

        if (req.count < 2) {
                fprintf(stderr, "Insufficient buffer memory on %s\n",
                         dev_name);
                exit(EXIT_FAILURE);
        }

        buffers = calloc(req.count, sizeof(*buffers));    // make the amount of buffers available

        if (!buffers) {
                fprintf(stderr, "Out of memory\n");
                exit(EXIT_FAILURE);
        }

        for (n_buffers = 0; n_buffers < req.count; ++n_buffers) {   // go through buffers and adjust struct in it
                struct v4l2_buffer buf;

                CLEAR(buf);

                buf.type        = V4L2_BUF_TYPE_VIDEO_CAPTURE;
                buf.memory      = V4L2_MEMORY_MMAP;
                buf.index       = n_buffers;

                if (-1 == xioctl(fd, VIDIOC_QUERYBUF, &buf))
                        errno_exit("VIDIOC_QUERYBUF");

                buffers[n_buffers].length = buf.length;
                buffers[n_buffers].start =
                        mmap(NULL /* start anywhere */,
                              buf.length,
                              PROT_READ | PROT_WRITE /* required */,
                              MAP_SHARED /* recommended */,
                              fd, buf.m.offset);

                if (MAP_FAILED == buffers[n_buffers].start)
                        errno_exit("mmap");
        }
        printf("mmap buffer initiated\n");
}


static void init_device(void)
{
        printf("initiating device\n");
        struct v4l2_capability cap;
        struct v4l2_cropcap cropcap;
        struct v4l2_crop crop;
        struct v4l2_format fmt;
        unsigned int min;

        if (-1 == xioctl(fd, VIDIOC_QUERYCAP, &cap)) {  // gets information about driver and harware capabilities
                if (EINVAL == errno) {                  // driver is not compatible with specifications
                        fprintf(stderr, "%s is no V4L2 device\n",
                                 dev_name);
                        exit(EXIT_FAILURE);
                } else {
                        errno_exit("VIDIOC_QUERYCAP");
                }
        }

        if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
                fprintf(stderr, "%s is no video capture device\n",
                         dev_name);
                exit(EXIT_FAILURE);
        }
        if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
                fprintf(stderr, "%s does not support streaming i/o\n",
                         dev_name);
                exit(EXIT_FAILURE);
        }

        /* Select video input, video standard and tune here. */

        CLEAR(cropcap);

        cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

        if (0 == xioctl(fd, VIDIOC_CROPCAP, &cropcap)) {  // used to get cropping limits, pixel aspects, ... fill in type field and get all this information back
                crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
                crop.c = cropcap.defrect; /* reset to default */

                if (-1 == xioctl(fd, VIDIOC_S_CROP, &crop)) { // get cropping rectangle
                        switch (errno) {
                        case EINVAL:
                                printf("EINVAL in VIDIOC_S_CROP\n");
                                /* Cropping not supported. */
                                break;
                        default:
                                printf("other error in VIDIOC_S_CROP\n");
                                /* Errors ignored. */
                                break;
                        }
                }
        } else {
                /* Errors ignored. */
        }


        CLEAR(fmt);   // set the format of the v4l2 video

        fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        if (force_format) {
                  fprintf(stderr, "Set H264\r\n");
                fmt.fmt.pix.width       = 640; //replace
                fmt.fmt.pix.height      = 480; //replace
                fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_H264; //replace
                fmt.fmt.pix.field       = V4L2_FIELD_ANY;

                if (-1 == xioctl(fd, VIDIOC_S_FMT, &fmt))
                        errno_exit("VIDIOC_S_FMT");

                /* Note VIDIOC_S_FMT may change width and height. */
        } else {
                /* Preserve original settings as set by v4l2-ctl for example */
                if (-1 == xioctl(fd, VIDIOC_G_FMT, &fmt))
                        errno_exit("VIDIOC_G_FMT");
        }

        /* Buggy driver paranoia. */
        min = fmt.fmt.pix.width * 2;

        if (fmt.fmt.pix.bytesperline < min)
                fmt.fmt.pix.bytesperline = min;

        min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height;

        if (fmt.fmt.pix.sizeimage < min)
                fmt.fmt.pix.sizeimage = min;

        init_mmap();
        printf("device inititiated\n");
}


static void close_device(void)
{
        printf("closing device\n");
        if (-1 == close(fd))
                errno_exit("close");

        fd = -1;
        printf("device closed\n");
}


/*
struct stat {
               dev_t     st_dev;         ID of device containing file
               ino_t     st_ino;         Inode number
               mode_t    st_mode;        File type and mode
               nlink_t   st_nlink;       Number of hard links
               uid_t     st_uid;         User ID of owner
               gid_t     st_gid;         Group ID of owner
               dev_t     st_rdev;        Device ID (if special file)
               off_t     st_size;        Total size, in bytes
               blksize_t st_blksize;     Block size for filesystem I/O
               blkcnt_t  st_blocks;      Number of 512B blocks allocated

                  Since Linux 2.6, the kernel supports nanosecond
                  precision for the following timestamp fields.
                  For the details before Linux 2.6, see NOTES.

               struct timespec st_atim;  Time of last access
               struct timespec st_mtim;  Time of last modification
               struct timespec st_ctim;  Time of last status change

           #define st_atime st_atim.tv_sec      Backward compatibility
           #define st_mtime st_mtim.tv_sec
           #define st_ctime st_ctim.tv_sec
           };
*/

static void open_device(void)
{
        printf("openening device\n");
        struct stat st;

        if (-1 == stat(dev_name, &st)) {                              // stat() returns info about file into struct
                fprintf(stderr, "Cannot identify '%s': %d, %s\n",
                         dev_name, errno, strerror(errno));
                exit(EXIT_FAILURE);
        }

        if (!S_ISCHR(st.st_mode)) {
                fprintf(stderr, "%s is no device\n", dev_name);
                exit(EXIT_FAILURE);
        }

        fd = open(dev_name, O_RDWR /* required */ | O_NONBLOCK, 0);   // open the file dev/video0, returns a file descriptor

        // if fd == -1, the file could not be opened.
        if (-1 == fd) {
                fprintf(stderr, "Cannot open '%s': %d, %s\n",
                         dev_name, errno, strerror(errno));
                exit(EXIT_FAILURE);
        }
        printf("device opened\n");
}


int main(int argc, char **argv)
{
        printf("main begins\n");
        dev_name = "/dev/video0";

        for (;;) {
                printf("back here\n");
                break;
        }

        open_device();
        init_device();
        start_capturing();
        mainloop();
        stop_capturing();
        uninit_device();
        close_device();
        fprintf(stderr, "\n");
        return 0;
}

However I get the following error:

VIDIOC_REQBUFS error 12, Cannot allocate memory

The entire output is:

main begins
back here
openening device
device opened
initiating device
initiating mmap buffer
VIDIOC_REQBUFS error 12, Cannot allocate memory
make: *** [makefile:3: all] Error 1

In the above code this is caused by:

if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) { // initiate buffer
                if (EINVAL == errno) {
                        fprintf(stderr, "%s does not support "
                                 "memory mapping\n", dev_name);
                        exit(EXIT_FAILURE);
                } else {
                        errno_exit("VIDIOC_REQBUFS");
                }
        }

Thus the ioctl(fd, VIDIOC_REQBUFS, &req) causes this error. I have already looked on StackOverflow and found 1 other person with the same mistake. He suggested to change CONFIG_CMA_SIZE_MBYTES to 32 from 16. I tried this by looking where I could find this setting. I found it in: boot/config-4.14.98-imx . However, it was already 320. (yes tenfold). I am now rather stuck on this. Is there a problem in my code, or do I need to change the setting from 320 to 32 (which seems counterintuitive).

With kind regards.

0 Kudos
1 Reply

3,692 Views
AldoG
NXP TechSupport
NXP TechSupport

Hello,

It looks like the system has not enough memory for operation, unfortunately I cannot say much about the google coral, but you can look into the CMA memory in your dts and increase it if possible.

I would recommend reaching Google trough their support options to see if they have additional information on how to handle this

https://coral.ai/support/

Best regards,
Aldo.

0 Kudos