天天看點

V4L2攝像頭擷取圖檔

昨天拷了一段以前寫的在Windows下運作的攝像頭錄像代碼,發現在Ubuntu14.04下這樣寫有問題:

/****************************/
/*攝像頭錄像測試程式           */
/****************************/
#include <cv.h>
#include <iostream>
#include <opencv2/highgui/highgui.hpp>

const char g_inPath[] = "in.avi";
const char g_outPath[] = "out.avi";

int main()
{

    CvCapture *capture = ;
    int camera_index = -;

    capture = cvCreateCameraCapture(camera_index);

    if (!capture)
    {
        std::cout << "Camera "<<camera_index << " can not open!"<<std::endl;
        return -;
    }

    IplImage *frame = cvQueryFrame(capture);

    double fps = cvGetCaptureProperty(capture,CV_CAP_PROP_FPS);
    int width = (int)cvGetCaptureProperty(capture,CV_CAP_PROP_FRAME_WIDTH);
    int height = (int)cvGetCaptureProperty(capture,CV_CAP_PROP_FRAME_HEIGHT);
    CvSize size = cvSize(width,height);

    CvVideoWriter *writer = cvCreateVideoWriter(g_outPath, -, fps, size);
    IplImage *out_frame = cvCreateImage(size,frame->depth,frame->nChannels);

    while ((frame = cvQueryFrame(capture)) != NULL)
    {
        cvCopy(frame, out_frame);
        cvWriteFrame(writer,frame);
        cvShowImage("Video",out_frame);

        char c = cvWaitKey();
        if (c == )
        {
            break;
        }

    }

    cvReleaseVideoWriter(&writer);
    cvReleaseImage(&out_frame);
    cvReleaseImage(&frame);
    cvReleaseCapture(&capture);
    system("pause");

    return ;
}
           

運作結果:

V4L2攝像頭擷取圖檔

有時間能運作,有時間報錯,沒找到原因。網上資料很少,無意中搜到一篇文章:

http://blog.chinaunix.net/uid-26851094-id-3270803.html

接觸到了V4L2,沿着前輩的思路入門,先抄個例程,看一下效果

參考:

http://www.linuxidc.com/Linux/2011-03/33020.htm

http://www.linuxidc.com/Linux/2011-03/33021.htm

/****************************/
/*V4L2攝像頭擷取單幅圖檔測試程式*/
/****************************/

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

#include <getopt.h>

#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <malloc.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <sys/ioctl.h>

#include <asm/types.h>
#include <linux/videodev2.h>

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

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

// 攝像頭裝置名
static char *           dev_name    = "/dev/video1";
static int              fd          = -;
struct buffer *         buffers     = NULL;
static unsigned int     n_buffers   = ;

FILE *file_fd;
static unsigned long file_length;
static unsigned char *file_name;

//
//擷取一幀資料
//

static int read_frame(void)
{
    struct v4l2_buffer buf;
    unsigned int i;

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

    // 出列采集的幀緩沖
    int ff = ioctl(fd,VIDIOC_DQBUF, &buf);
    if (ff < )
    {
        printf("failture \n");
    }

    assert(buf.index < n_buffers);

    // 将其寫入檔案中
    fwrite(buffers[buf.index].start,buffers[buf.index].length, , file_fd);

    // 再将其入列
    ff = ioctl(fd, VIDIOC_QBUF, &buf);
    if (ff < )
    {
        printf("failture VIDIOC_QBUF\n");
    }

    return ;
}

int main(int argc, char **argv)
{
    struct v4l2_capability cap;
    struct v4l2_format fmt;
    unsigned int i;
    enum v4l2_buf_type type;

    // 圖檔檔案名
    file_fd = fopen("test-mmap.jpg","w");

    // 打開裝置
    fd = open(dev_name, O_RDWR /*required*/ | O_NONBLOCK, );

    // 擷取攝像頭參數
    ioctl(fd, VIDIOC_QUERYCAP, &cap);

    CLEAR(fmt);
    fmt.type                = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    fmt.fmt.pix.width       = ;
    fmt.fmt.pix.height      = ;
    fmt.fmt.pix.pixelformat = V4L2_FIELD_INTERLACED;

    // 設定圖像格式
    ioctl(fd, VIDIOC_S_FMT, &fmt);

    // 計算圖檔大小
    file_length = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height;

    struct v4l2_requestbuffers req;
    CLEAR(req);

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

    // 申請緩沖,count是申請的數量
    ioctl(fd,VIDIOC_REQBUFS,&req);

    if (req.count < )
    {
        printf("Insufficient buffer memory\n");
    }

    // 記憶體中建立對應的空間
    buffers = (buffer *)calloc(req.count, sizeof(*buffers));


    for (n_buffers = ; n_buffers < req.count; ++n_buffers)
    {
        // 驅動中的一幀
        struct v4l2_buffer buf;
        CLEAR(buf);
        buf.type    = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        buf.memory  = V4L2_MEMORY_MMAP;
        buf.index   = n_buffers;

        // 映射使用者空間
        if (- == ioctl(fd, VIDIOC_QUERYBUF, &buf))
        {
            printf("VIDIOC_QUERYBUF error\n");
        }

        buffers[n_buffers].length = buf.length;

        // 通過mmap建立映射關系
        buffers[n_buffers].start = mmap(
                    NULL, /*start anywhere*/
                    buf.length,
                    PROT_READ | PROT_WRITE, /*required*/
                    MAP_SHARED, /*required*/
                    fd,
                    buf.m.offset
                    );

        if (MAP_FAILED == buffers[n_buffers].start)
        {
            printf("mmap failed\n");
        }
    }

    for (i = ; 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 (- == ioctl(fd,VIDIOC_QBUF, &buf))
        {
            printf("VIDIOC_QBUF failed\n");
        }
    }

    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

    // 開始捕捉圖像資料
    if (- == ioctl(fd, VIDIOC_STREAMON, &type))
    {
        printf("VIDIOC_STREAMON failed\n");
    }

    // 這一段涉及到異步IO
    for (;;)
    {
        fd_set fds;
        struct timeval tv;
        int r;

        // 将指定的檔案描述符集清空
        FD_ZERO(&fds);

        // 在檔案描述符集合中增加一個新的檔案描述符
        FD_SET(fd, &fds);

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

        // 判斷是否可讀(即攝像頭是否準備好),tv是定時
        r = select(fd + , &fds, NULL, NULL, &tv);

        if (- == r)
        {
            if( EINTR == errno)
            {
                continue;
            }
            printf("select err\n");
        }

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

        // 如果可讀,執行read_frame()函數,并跳出循環
        if (read_frame())
        {
            break;
        }
    }

unmap:

    for (i = ; i < n_buffers; ++i)
    {
        if (- == munmap(buffers[i].start, buffers[i].length))
        {
            printf("munmap error\n");
        }
    }

    close (fd);
    fclose (file_fd);
    exit(EXIT_SUCCESS);
    return ;

}
           

運作效果:

V4L2攝像頭擷取圖檔

報錯了,尚不知什麼原因,明天繼續。