c - 如何在V4L2中正确设置扩展控件?

标签 c linux camera raspberry-pi v4l2

我想用 C 语言从 V4L2 设备(来自 Raspberry Pi 相机)录制视频。 录音本身有效,我可以将视频保存到文件中。

但是我需要更改视频的比特率。从 v4l2-ctl --set-ctrl video_bitrate=10000000 命令的 strace 输出我知道 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 默认实现,不会对相机设置应用任何实际更改

关于c - 如何在V4L2中正确设置扩展控件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38007305/

相关文章:

c - *** 检测到 glibc *** ./all : realloc(): invalid next size: 0x0804b008 ***

c - "@"在 C/C++ 的宏中意味着什么?

linux - 在 Bash 中,如何将管道的末尾流式传输到变量中?

linux - Linux 应用程序是否(容易)杀死内核或使 init 进程崩溃

android - 通过相机获取最近拍摄的图像的路径和图像名称

ios - 集成自定义相机 View AVCaptureDevice

c - 尝试让进程在没有 SDL(感知到的)崩溃的情况下运行 C

c - 在 gcc 编译器的标志中排除 "lib"前缀

c++ - 无法在 Ubuntu 上编译 C++ 程序

ios - 如何从IOS应用程序访问iPhone/ipad native 相机应用程序?