V4L2ビデオキャプチャ原則

I.はじめに

Linuxtwo(Video4Linux2)のためのビデオはV4L2、V4Lの改良版である呼びます。V4L2は、画像を取得するためのLinuxオペレーティングシステムであり、映像と音声のデータへのAPIインターフェース、適切なビデオキャプチャデバイスとそれに対応するドライバと、画像、ビデオ、オーディオのコレクションを実現することができます。無料UVCは、直接カメラ操作を駆動することができます。遠隔会議、ビデオ電話、ビデオ監視システムと組み込みマルチメディア端末でアプリケーションの広い範囲を持っています。

二、V4L2ビデオキャプチャ原理

V4L2サポートメモリマッピングモード(MMAP)と、データを収集するためのダイレクトアクセス(リード)、前者は通常、静止画データを捕捉するために使用される連続映像データを、捕捉するために使用されます。私たちは、一般的にビデオキャプチャにメモリマップされたアプローチを使用します。
  V4L2ビデオデータ取り込みの5つのステップ:
  まず、ビデオファイル装置を開き、パラメータの初期化ビデオ・キャプチャ、ドットサイズ及びフォーマット収集インタフェースV4L2によって提供される映像取得ウィンドウを行い、
  第二に、ビデオキャプチャフレームバッファの数を適用しますゾーンとユーザ空間にカーネル空間からフレームバッファのマッピング、アプリケーション・リード/処理されたビデオデータを容易にするために、
  第三の、入力キューにキューイングビデオキャプチャフレームバッファに適用され、映像取得を開始するために、
  第;、ビデオデータ取り込みの開始を駆動する、ビデオキャプチャアプリケーションが処理した後、背面入力キュー、ビデオデータをキャプチャするために使用されるサイクルにビデオキャプチャフレームバッファ、フレームバッファ出力キューから削除される
  第五のは、ビデオキャプチャを停止します。

            取得フローチャートV4L2

其实其他的都比较简单,就是通过ioctl这个接口去设置一些参数。最主要的就是buf管理。他有一个或者多个输入队列和输出队列。
  启动视频采集后,驱动程序开始采集一帧数据,把采集的数据放入视频采集输入队列的第一个帧缓冲区,一帧数据采集完成,也就是第一个帧缓冲区存满一帧数据后,驱动程序将该帧缓冲区移至视频采集输出队列,等待应用程序从输出队列取出。驱动程序接下来采集下一帧数据,放入第二个帧缓冲区,同样帧缓冲区存满下一帧数据后,被放入视频采集输出队列。
  应用程序从视频采集输出队列中取出含有视频数据的帧缓冲区,处理帧缓冲区中的视频数据,如存储或压缩。
  最后,应用程序将处理完数据的帧缓冲区重新放入视频采集输入队列,这样可以循环采集,如图所示

                循环采集图

三、基于v4l2的远程监控测试程序

测试程序属于未完成的阶段,v4l2部分已经完成。
  V4l2各项函数定义在测试程序的camera.cpp中。
  程序设计师按照以上流程设计,查看源码的时候可以对照调用流程图,对于其中一些参数理解可以参考参考文献的第一篇文章。

3.1打开摄像头

<pre>

void open_camera(Camera* cam)
{
cam->fd=open(cam->device_name,O_RDWR);
if(cam->fd==-1)
{
cout<<"Cannot open the device."??endl;
exit(1);
}
else
{
cout<<"Open the device."??endl;
}
}

 


</pre>

3.2查看摄像头支持的模式已经初始化

需要用到的结构体:
<pre>

struct v4l2_capability
{
__u8 driver[16]; // 驱动名字
__u8 card[32]; // 设备名字
__u8 bus_info[32]; // 设备在系统中的位置
__u32 version; // 驱动版本号
__u32 capabilities; // 设备支持的操作
__u32 reserved[4]; // 保留字段
};
</pre>
  capabilities常用值:
<pre>
V4L2_CAP_VIDEO_CAPTURE // 是否支持图像获取
struct v4l2_format
{
enum v4l2_buf_type type;// 帧类型,应用程序设置
union fmt
{
struct v4l2_pix_format pix;// 视频设备使用
struct v4l2_window win;
struct v4l2_vbi_format vbi;
struct v4l2_sliced_vbi_format sliced;
__u8 raw_data[200];
};
</pre>
  实现函数:
<pre>
void init_camera(Camera* cam){
struct v4l2_capability cap;

if (-1 == ioctl(cam->fd, VIDIOC_QUERYCAP, &cap))
{
    if (EINVAL == errno)
    {
        fprintf(stderr, "%s is no V4L2 device\n", cam->device_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", cam->device_name);
    exit(EXIT_FAILURE);
}

if (!(cap.capabilities & V4L2_CAP_STREAMING))
{
    fprintf(stderr, "%s does not support streaming i/o\n",cam->device_name);
    exit(EXIT_FAILURE);
}

//#ifdef DEBUG_CAM
printf("\nVIDOOC_QUERYCAP\n");
printf("the camera driver is %s\n", cap.driver);
printf("the camera card is %s\n", cap.card);
printf("the camera bus info is %s\n", cap.bus_info);
printf("the version is %d\n", cap.version);

struct v4l2_format fmt;
memset(&fmt, 0, sizeof(fmt));
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = cam->width;
fmt.fmt.pix.height = cam->height;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;

if (ioctl(cam->fd, VIDIOC_S_FMT, &fmt) < 0)
{
    close(cam->fd);
}
cout<<"init the camera."<<endl;
init_mmap(cam);
}

 

</pre>

3.3内存映射

需要用到的结构体:
<pre>

structv4l2_requestbuffers
{
__u32count;//缓冲区内缓冲帧的数目
enumv4l2_buf_typetype;//缓冲帧数据格式
enumv4l2_memorymemory;//区别是内存映射还是用户指针方式
__u32 reserved[2];
};
struct v4l2_buffer
{
__u32index;//buffer序号
enumv4l2_buf_typetype;//buffer类型
__u32byteused;//buffer中已使用的字节数
__u32flags;//区分是MMAP还是USERPTR
enumv4l2_fieldfield;
structtimevaltimestamp;//获取第一个字节时的系统时间
structv4l2_timecode timecode;
__u32sequence;//队列中的序号
enum v4l2_memorymemory;//IO方式,被应用程序设置
union m
{
__u32 offset;//缓冲帧地址,只对MMAP有效
unsignedlonguserptr;
};
__u32length;//缓冲帧长度
__u32input;
__u32reserved;
};

 


</pre>
  自己定义的一个结构体来映射每个缓存帧:
<pre>

struct buffer
{
void* start;
unsigned int length;
}buffers;
</pre>
  实现函数:
<pre>
void init_mmap(Camera cam)
{
struct v4l2_requestbuffers req;
memset(&req, 0, sizeof(req));
req.count = 4;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;

if (ioctl(cam->fd, VIDIOC_REQBUFS, &req) < 0)
{
    fprintf(stderr, "Request buffers failure.\n");
    exit(EXIT_FAILURE);
}
if (req.count < 2)
{
    fprintf(stderr, "Insufficient buffer memory on %s\n",
            cam->device_name);
    return;
}
cam->buffers = (Buffer *)calloc(req.count, sizeof(*cam->buffers));
struct v4l2_buffer buf;
for (unsigned int numBufs = 0; numBufs < req.count; numBufs++)
{
    memset(&buf, 0, sizeof(buf));
    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    buf.memory = V4L2_MEMORY_MMAP;
    buf.index = numBufs;
    if (ioctl(cam->fd, VIDIOC_QUERYBUF, &buf) == -1)
    {
        return ;
    }
    cam->buffers[numBufs].length = buf.length;
    cam->buffers[numBufs].start = mmap(NULL, buf.length,PROT_READ | PROT_WRITE,
        MAP_SHARED,
        cam->fd, buf.m.offset);
    if (cam->buffers[numBufs].start == MAP_FAILED)
    {
        return ;
    }
}
cout<<"mmap the camera."<<endl;
}

 

</pre>

3.4开启流

<pre>

void start_capturing(Camera* cam)
{
struct v4l2_buffer buf;
enum v4l2_buf_type type;
for (int i = 0; i < 4; i++)
{
memset(&buf, 0, sizeof(buf));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
// buf.m.offset = buffer[i].offset;

     if (ioctl(cam->fd, VIDIOC_QBUF, &buf) < 0)
     {

     }
 }

 type =  V4L2_BUF_TYPE_VIDEO_CAPTURE;;
 if (ioctl(cam->fd, VIDIOC_STREAMON, &type) < 0)
 {

 }
 cout<<"STREAMON"<<endl;
}

 

</pre>

3.5读取一帧并交给用户程序处理

<pre>

int read_and_encode_frame(Camera* cam)
{
struct v4l2_buffer capture_buf;
memset(&capture_buf, 0, sizeof(capture_buf));
capture_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
capture_buf.memory = V4L2_MEMORY_MMAP;
if (ioctl(cam->fd, VIDIOC_DQBUF, &capture_buf) < 0)
{
cout<<"cannot get buf"<<endl;
}

cout<<"read_and_encode_frame"<<endl;
encode_frame(cam,capture_buf.index,capture_buf.length);

if (-1 == ioctl(cam->fd, VIDIOC_QBUF, &capture_buf))
            return -1;
return 0;
}

 

</pre>

3.6自定义处理程序:

在这里,我把获得的帧数据保存到自己定义的队列中,相对应的可以将此函数改为你所需的功能。
<pre>

void encode_frame(Camera* cam,unsigned int i,unsigned int length)
{
unsigned char *yuv_frame=static_cast<unsigned char *>(cam->buffers[i].start);
if(yuv_frame[0]=='\0')
{
cout<<"yuv_frame[0]=='\0' "<<endl;
return;
}
//fwrite(yuv_frame,length,1,cam->yuv_fp);

mBuffer *inBuffer=(mBuffer*)malloc(sizeof(mBuffer));
inBuffer->mpBuffer=(char*)malloc(length);
memcpy(inBuffer->mpBuffer, yuv_frame, length);
//inBuffer->mpBuffer=(char*)yuv_frame;
inBuffer->mSize=length;
putBufferWithData(&cam->buffer_list,inBuffer );

//fwrite(inBuffer->mpBuffer,length,1,outfile);
 free(yuv_frame);
cout<<"fwrite done."<<endl;
}

 

</pre>

3.7结束采集

后面就是采集结束后的释放过程,原先demo程序在释放资源过程中一直存在问题,一直还没解决。

参考资料

1.v4l2参数和机构体说明http://blog.sina.com.cn/s/blog_602f87700100znq7.html
2.V4l2采集流程http://blog.csdn.net/eastmoon502136/article/details/8190262



作者:onesixthree
链接:https://www.jianshu.com/p/fd5730e939e7
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

おすすめ

転載: www.cnblogs.com/cyyljw/p/11274725.html
おすすめ