我想用C语言录制一段来自V4L2设备(来自覆盆子Pi摄像机)的视频。
录音本身工作,我可以保存到一个文件的视频。
但是我需要改变视频的比特率。从v4l2 ctl的strace输出——set ctrl video_bitrate=10000000命令,我知道v4l2的扩展控件API用于实现这一点。
这是我的代码到目前为止还不起作用:

#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;
}

ioctl调用似乎成功,但是我的输出文件的大小始终与199,2 MB的大小相同。有人知道密码有什么问题吗?

最佳答案

您需要检查相机驱动程序是否支持IOCTL命令。如果驱动程序不通过实现IOCTL命令而不支持它,您仍然可以执行该命令并将其路由到v4l2默认实现,则不会对相机设置应用实际更改

09-28 01:15