简体   繁体   中英

How to set extended controls in V4L2 correctly?

I want to record a video from a V4L2 device (from the Raspberry Pi camera) in C. The recording itself works and I can save the video to a file.

However I need to change the bitrate of the video. From the strace output of the v4l2-ctl --set-ctrl video_bitrate=10000000 command I know that the extended controls API of v4l2 is used to achieve this.

Here's my code which doesn't work so far:

#include <iostream>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/mman.h>   //mmap
#include <fcntl.h>
#include <unistd.h>
#include <linux/videodev2.h>

using namespace std;

#define numbuffers 3

struct picturebuffer
{
   void *startadress;
   size_t length;
};

//array in which the buffer pointer are being stored
picturebuffer pb[numbuffers];

int main()
{
   //open camera
   int fd;
   fd = open("/dev/video0", O_RDWR);
   if(fd < 0)
   {
      cout << "error during opening the camera device!";
      cout.flush();
   }
   cout << "camera opened";

   //read capabilities
   struct v4l2_capability caps;
   if(ioctl(fd, VIDIOC_QUERYCAP, &caps) < 0)
   {
      cout << "error while reading the capabilities!";
      cout.flush();
   }
   cout << "Capabilities " << caps.capabilities << endl;
   //ToDo: check for required capabilities

   //set image data
   struct v4l2_format format;
   format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
   format.fmt.pix.pixelformat = V4L2_PIX_FMT_H264;
   format.fmt.pix.width = 1920;
   format.fmt.pix.height = 1080;
   if(ioctl(fd, VIDIOC_S_FMT, &format) < 0)
   {
      cout << "error in the image format";
   }
   cout << "Image properties set" << endl;
   //Todo: check if width and height fit together (VIDIOC_ENUM_FRAMESIZES)

   //set extended Controls
   struct v4l2_ext_controls ecs;
   struct v4l2_ext_control ec;
   memset(&ecs, 0, sizeof(ecs));
   memset(&ec, 0, sizeof(ec));
   ec.id = V4L2_CID_MPEG_VIDEO_BITRATE;
   ec.value = 10000000;
   ec.size = 0;
   ecs.controls = &ec;
   ecs.count = 1;
   ecs.ctrl_class = V4L2_CTRL_CLASS_MPEG;
   if(ioctl(fd, VIDIOC_S_EXT_CTRLS, &ecs) < 0)
   {
      cout << "error in extended controls bitrate";
      cout.flush();
   }

   //allocate buffer in the kernel
   struct v4l2_requestbuffers req;
   req.count = numbuffers;
   req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
   req.memory = V4L2_MEMORY_MMAP;
   if(ioctl(fd, VIDIOC_REQBUFS, &req) < 0)
   {
      cout << "errro while allocating buffer";
      cout.flush();
   }
   cout << "number of buffers: " << req.count << endl;
   cout.flush();

   //map buffers into userspace
   for(int i=0; i<numbuffers; i++)
   {
      struct v4l2_buffer bufferinfo;
      memset(&bufferinfo, 0, sizeof(bufferinfo));
      bufferinfo.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
      bufferinfo.memory = V4L2_MEMORY_MMAP;
      bufferinfo.index = i;
      if(ioctl(fd, VIDIOC_QUERYBUF, &bufferinfo) < 0)
      {
         cout << "error while querying bufferinfo";
         cout.flush();
      }
      pb[i].startadress = mmap(NULL, bufferinfo.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, bufferinfo.m.offset);
      pb[i].length = bufferinfo.length;
      if(pb[i].startadress == MAP_FAILED)
      {
         cout << "error during mmap" << endl;
      }
      memset(pb[i].startadress, 0, bufferinfo.length);
      cout << "size of buffer: " << bufferinfo.length << endl;
   }
   cout << "buffers mapped into userspace" << endl;
   cout.flush();

   //queue in the buffers
   for(int i=0; i<numbuffers; i++)
   {
      struct v4l2_buffer bufferinfo;
      memset(&bufferinfo, 0, sizeof(bufferinfo));
      bufferinfo.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
      bufferinfo.memory = V4L2_MEMORY_MMAP;
      bufferinfo.index = i;
      if(ioctl(fd, VIDIOC_QBUF, &bufferinfo) < 0)
      {
         cout << "error while queueing the buffers in" << endl;
      }
   }

   //since that point the driver starts capturing the pics
   int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
   if(ioctl(fd, VIDIOC_STREAMON, &type) < 0)
   {
      cout << "error while starting the stream" << endl;
   }

   int file;
   if((file = open("/home/pi/image.h264", O_WRONLY | O_CREAT, 0660)) < 0)
   {
      cout << "error while writing the file";
   }

   //loop for managing the pics
   for(int i=0; i<100; i++)
   {
      struct v4l2_buffer bufferinfo;
      memset(&bufferinfo, 0, sizeof(bufferinfo));
      bufferinfo.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
      bufferinfo.memory = V4L2_MEMORY_MMAP;
      if(ioctl(fd, VIDIOC_DQBUF, &bufferinfo) < 0)
      {
         cout << "error while getting the buffer!" << endl;
      }

      //do anything with the pic
      char buf[pb[bufferinfo.index].length];
      memcpy(&buf, pb[bufferinfo.index].startadress, pb[bufferinfo.index].length);
      cout << bufferinfo.index << endl;
      cout.flush();

      //write picture into the file
      write(file, pb[bufferinfo.index].startadress, pb[bufferinfo.index].length);

      if(ioctl(fd, VIDIOC_QBUF, &bufferinfo) < 0)
      {
         cout << "error while enqueuing the buffer" << endl;
      }
   }
   close(file);
   if(ioctl(fd, VIDIOC_STREAMOFF, &type) < 0)
   {
      cout << "error while stopping the stream" << endl;
   }

   //clean up
   for(int i=0; i<numbuffers; i++)
   {
      if(munmap(pb[i].startadress, pb[i].length) < 0)
      {
         cout << "error during unmap";
      }
   }

   //close camera file
   close(fd);
   cout << "!!!Hello World!!!" << endl;
   cout.flush();
   return 0;
}

The ioctl call seems to succeed, however my output file always has the same size as of 199,2 MB. Does someone know what´s wrong in the code ?

You need to check if the camera driver supports that IOCTL command. If the driver doesn't support the IOCTL command by not implementing it, you still can execute the command and it is routed to v4l2 default implementation, no actual changes are applied to the camera setting

Try to change the lines:

pb[bufferinfo.index].length

By:

pb[bufferinfo.index].bytesused

For example:

write(file, pb[bufferinfo.index].startadress, pb[bufferinfo.index].bytesused);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM