V4L2驅動的移植與應用

V4L2的應用

    下面簡單介紹一下V4L2驅動的應用流程。

1、  視頻採集的基本流程

一般的,視頻採集都有如下流程: 

2、  打開視頻設備

V4L2中,視頻設備被看做一個文件。使用open函數打開這個設備:

// 用非阻塞模式打開攝像頭設備
int cameraFd;
cameraFd = open("/dev/video0", O_RDWR | O_NONBLOCK, 0);
// 
如果用阻塞模式打開攝像頭設備,上述代碼變爲:
//cameraFd = open("/dev/video0", O_RDWR, 0);

關於阻塞模式和非阻塞模式:應用程序能夠使用阻塞模式或非阻塞模式打開視頻設備,如果使用非阻塞模式調用視頻設備,即使尚未捕獲到信息,驅動依舊會把緩存(DQBUFF)裏的東西返回給應用程序。

3、  設定屬性及採集方式

打開視頻設備後,可以設置該視頻設備的屬性,例如裁剪、縮放等。這一步是可選的。在Linux編程中,一般使用ioctl函數來對設備的I/O通道進行管理:

extern int ioctl (int __fd, unsigned long int __request, ...) __THROW;

__fd:設備的ID,例如剛纔用open函數打開視頻通道後返回的cameraFd

__request:具體的命令標誌符。

在進行V4L2開發中,一般會用到以下的命令標誌符:

VIDIOC_REQBUFS:分配內存

VIDIOC_QUERYBUF:把VIDIOC_REQBUFS中分配的數據緩存轉換成物理地址

VIDIOC_QUERYCAP:查詢驅動功能

VIDIOC_ENUM_FMT:獲取當前驅動支持的視頻格式

VIDIOC_S_FMT:設置當前驅動的頻捕獲格式

VIDIOC_G_FMT:讀取當前驅動的頻捕獲格式

VIDIOC_TRY_FMT:驗證當前驅動的顯示格式

VIDIOC_CROPCAP:查詢驅動的修剪能力

VIDIOC_S_CROP:設置視頻信號的邊框

VIDIOC_G_CROP:讀取視頻信號的邊框

VIDIOC_QBUF:把數據從緩存中讀取出來

VIDIOC_DQBUF:把數據放回緩存隊列

VIDIOC_STREAMON:開始視頻顯示函數

VIDIOC_STREAMOFF:結束視頻顯示函數

VIDIOC_QUERYSTD:檢查當前視頻設備支持的標準,例如PAL或NTSC。

這些IO調用,有些是必須的,有些是可選擇的。

4、  檢查當前視頻設備支持的標準

在亞洲,一般使用PAL720X576)制式的攝像頭,而歐洲一般使用NTSC720X480),使用VIDIOC_QUERYSTD來檢測:

v4l2_std_id std;
do {
  ret = ioctl(fd, VIDIOC_QUERYSTD, &std);
} while (ret == -1 && errno == EAGAIN);
switch (std) {
    case V4L2_STD_NTSC:
        //……
    case V4L2_STD_PAL:
        //……
}

5、  設置視頻捕獲格式

當檢測完視頻設備支持的標準後,還需要設定視頻捕獲格式:

struct v4l2_format    fmt;
memset ( &fmt, 0, sizeof(fmt) );
fmt.type                = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width       = 720;
fmt.fmt.pix.height      = 576;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
fmt.fmt.pix.field       = V4L2_FIELD_INTERLACED;
if (ioctl(fd, VIDIOC_S_FMT, &fmt) == -1) {
  return -1;
}

v4l2_format結構體定義如下:

struct v4l2_format
{
    enum v4l2_buf_type type;    // 
數據流類型,必須永遠是
V4L2_BUF_TYPE_VIDEO_CAPTURE 
    union
    {
        struct v4l2_pix_format    pix;  
        struct v4l2_window        win;  
        struct v4l2_vbi_format    vbi;  
        __u8    raw_data[200];          
    } fmt;
};
struct v4l2_pix_format
{
    __u32                   width;         // 
寬,必須是16的倍數

    __u32                   height;        // 
高,必須是16的倍數
    __u32                   pixelformat;   // 
視頻數據存儲類型,例如是YUV422還是RGB
    enum v4l2_field         field;
    __u32                   bytesperline;    
    __u32                   sizeimage;
    enum v4l2_colorspace    colorspace;
    __u32                   priv;       
};

6、  分配內存

接下來可以爲視頻捕獲分配內存:

struct v4l2_requestbuffers  req;
if (ioctl(fd, VIDIOC_REQBUFS, &req) == -1) {
  return -1;
}

v4l2_requestbuffers定義如下:

struct v4l2_requestbuffers
{
    __u32               count;  // 
緩存數量,也就是說在緩存隊列裏保持多少張照片

    enum v4l2_buf_type  type;   // 
數據流類型,必須永遠是V4L2_BUF_TYPE_VIDEO_CAPTURE 
    enum v4l2_memory    memory; // V4L2_MEMORY_MMAP 
 V4L2_MEMORY_USERPTR
    __u32               reserved[2];
};

7、  獲取並記錄緩存的物理空間

使用VIDIOC_REQBUFS,我們獲取了req.count個緩存,下一步通過調用VIDIOC_QUERYBUF命令來獲取這些緩存的地址,然後使用mmap函數轉換成應用程序中的絕對地址,最後把這段緩存放入緩存隊列:

 

typedef struct VideoBuffer {
    void   *start;
    size_t  length;
} VideoBuffer;

VideoBuffer*          buffers = calloc( req.count, sizeof(*buffers) );
struct v4l2_buffer    buf;

for (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(fd, VIDIOC_QUERYBUF, &buf) == -1) {
        return -1;
    }

    buffers[numBufs].length = buf.length;
    // 
轉換成相對地址
    buffers[numBufs].start = mmap(NULL, buf.length,
        PROT_READ | PROT_WRITE,
        MAP_SHARED,
        fd, buf.m.offset);

    if (buffers[numBufs].start == MAP_FAILED) {
        return -1;
    }

    // 
放入緩存隊列
    if (ioctl(fd, VIDIOC_QBUF, &buf) == -1) {
        return -1;
    }
}

8、  關於視頻採集方式

操作系統一般把系統使用的內存劃分成用戶空間和內核空間,分別由應用程序管理和操作系統管理。應用程序可以直接訪問內存的地址,而內核空間存放的是 供內核訪問的代碼和數據,用戶不能直接訪問。v4l2捕獲的數據,最初是存放在內核空間的,這意味着用戶不能直接訪問該段內存,必須通過某些手段來轉換地址。

一共有三種視頻採集方式:

1)使用readwrite方式:直接使用 read  write 函數進行讀寫。這種方式最簡單,但是這種方式會在 用戶空間和內核空間不斷拷貝數據 ,同時在用戶空間和內核空間佔用  大量內存,效率不高。

2)內存映射方式(mmap):把設備裏的內存映射到應用程序中的內存控件,直接處理設備內存,這是一種有效的方式。上面的mmap函數就是使用這種方式。

3)用戶指針模式:內存由用戶空間的應用程序分配,並把地址傳遞到內核中的驅動程序,然後由 v4l2 驅動程序直接將數據填充到用戶空間的內存中。這點需要在v4l2_requestbuffers裏將memory字段設置成V4L2_MEMORY_USERPTR。

第一種方式效率是最低的,後面兩種方法都能提高執行的效率,但是對於mmap 方式,文檔中有這樣一句描述 --Remember the buffers are allocated in physical memory, as opposed to virtual memory which can be swapped out to disk. Applications should free the buffers as soon as possible with the munmap () function .(使用mmap方法的時候,buffers相當於是在內核空間中分配的,這種情況下,這些buffer是不能被交換到虛擬內存中,雖然這種方法不怎麼影響讀寫效率,但是它一直佔用着內核空間中的內存,當系統的內存有限的時候,如果同時運行有大量的進程,則對系統的整體性能會有一定的影響。)

       所以,對於三種視頻採集方式的選擇,推薦的順序是 userptr  mmap  read-write 。當使用 mmap  userptr 方式的時候,有一個環形緩衝隊列的概念,這個隊列中,有 n  buffer ,驅動程序採集到的視頻幀數據,就是存儲在每個buffer 中。在每次用 VIDIOC_DQBUF 取出一個 buffer ,並且處理完數據後,一定要用 VIDIOC_QBUF 將這個 buffer 再次放回到環形緩衝隊列中。環形緩衝隊列,也使得這兩種視頻採集方式的效率高於直接 read/write 

9、  處理採集數據

V4L2有一個數據緩存,存放req.count數量的緩存數據。數據緩存採用FIFO的方式,當應用程序調用緩存數據時,緩存隊列將最先採集到的 視頻數據緩存送出,並重新採集一張視頻數據。這個過程需要用到兩個ioctl命令,VIDIOC_DQBUF和VIDIOC_QBUF:

struct v4l2_buffer buf;
memset(&buf,0,sizeof(buf));
buf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory=V4L2_MEMORY_MMAP;
buf.index=0;

//讀取緩存
if (ioctl(cameraFd, VIDIOC_DQBUF, &buf) == -1)
{
    return -1;
}
//…………
視頻處理算法
//
重新放入緩存隊列
if (ioctl(cameraFd, VIDIOC_QBUF, &buf) == -1) {

    return -1;
}

10、              關閉視頻設備

使用close函數關閉一個視頻設備

close(cameraFd)

(待續)

=======================================================================================

=======================================================================================

三、V4L2demo

capture.c是官方示例程序。

capture.c 程序中的 process_image 函數:

       capture.c 程序主要是用來演示怎樣使用 v4l2 接口,並沒有對採集到的視頻幀數據做任何實際的處理,僅僅用process_image 函數表示了處理圖像的代碼位置。

       process_image 函數只有一個參數,就是存儲視頻幀的內存的地址指針,但是在真正的應用中,通常還需要知道該指針指向的數據的大小。

       因此可以修改函數,改成 void process_image ( const void * p, int len ) ,但是每次調用 process_image 的時候,第 2個參數該傳遞什麼值? 

考慮到程序中對 buffer 的定義

  struct buffer {

  void * start;

  size_t length};  

       如果將 buffer.length 作爲第 個參數傳遞到修改後的 process_image 函數中,這樣做是不正確的。 process_image 需要的第二個參數應該是每幀圖像的大小,仔細閱讀代碼後會發現, buffer.length 並不一定就等於圖像幀的大小。(buffer 的大小,還需要考慮其他的一些因素,比如內存對齊等 )

capture.c只是一個示例程序,僅僅是演示怎樣使用v4l2中最基本的接口。尤其是在main函數中的那幾個函數調用,表明了在使用v4l2時的最基本的一個流程,包括open_device,init_device,start_capturing,mainloop,stop_capturing,uninit_device,close_device。在寫程序的時候,可以充分的利用這幾個基本模塊,把他們分散在不同的代碼位置上,靈活的調用,有興趣的可以看一下gstreamer中v4l2src的源代碼或者其他的大型程序的相關部分。

總之一句話,capture.c僅僅是一個演示程序,不要侷限於它的代碼結構,要靈活的使用。

       下面是capture.c的源代碼:

 

#include <stdio.h>  

#include <stdlib.h>  

#include <string.h>  

#include <assert.h>  

#include <getopt.h>             /* getopt_long() */  

#include <fcntl.h>              /* low-level i/o */  

#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>          /* for videodev2.h */  

#include <linux/videodev2.h>  

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

typedef enum {  

    IO_METHOD_READ, IO_METHOD_MMAP, IO_METHOD_USERPTR,  

} io_method;  

struct buffer {  

    void * start;  

    size_t length;//buffer's length is different from cap_image_size  

};  

static char * dev_name = NULL;  

static io_method io = IO_METHOD_MMAP;//IO_METHOD_READ;//IO_METHOD_MMAP;  

static int fd = -1;  

struct buffer * buffers = NULL;  

static unsigned int n_buffers = 0;  

static FILE * outf = 0;  

static unsigned int cap_image_size = 0;//to keep the real image size!!  

//////////////////////////////////////////  

static void errno_exit(const char * s) {  

    fprintf(stderr, "%s error %d, %s\n", s, errno, strerror(errno));  

    exit(EXIT_FAILURE);  

}  

static int xioctl(int fd, int request, void * arg) {  

    int r;  

    do  

        r = ioctl(fd, request, arg);  

    while (-1 == r && EINTR == errno);  

    return r;  

}  

static void process_image(const void * p, int len) {  

    //  static char[115200] Outbuff ;  

    fputc('.', stdout);  

    if (len > 0) {  

        fputc('.', stdout);  

        fwrite(p, 1, len, outf);  

    }  

    fflush(stdout);  

}  

static int read_frame(void) {  

    struct v4l2_buffer buf;  

    unsigned int i;  

    switch (io) {  

    case IO_METHOD_READ:  

        if (-1 == read(fd, buffers[0].start, buffers[0].length)) {  

            switch (errno) {  

            case EAGAIN:  

                return 0;  

            case EIO:  

                /* Could ignore EIO, see spec. */  

                /* fall through */  

            default:  

                errno_exit("read");  

            }  

        }  

        //      printf("length = %d\r", buffers[0].length);  

        //      process_image(buffers[0].start, buffers[0].length);  

        printf("image_size = %d,\t IO_METHOD_READ buffer.length=%d\r",  

                cap_image_size, buffers[0].length);  

        process_image(buffers[0].start, cap_image_size);  

        break;  

    case IO_METHOD_MMAP:  

        CLEAR (buf);  

        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  

        buf.memory = V4L2_MEMORY_MMAP;  

        if (-1 == xioctl(fd, VIDIOC_DQBUF, &buf)) {  

            switch (errno) {  

            case EAGAIN:  

                return 0;  

            case EIO:  

                /* Could ignore EIO, see spec. */  

                /* fall through */  

            default:  

                errno_exit("VIDIOC_DQBUF");  

            }  

        }  

        assert(buf.index < n_buffers);  

        //      printf("length = %d\r", buffers[buf.index].length);  

        //      process_image(buffers[buf.index].start, buffers[buf.index].length);  

        printf("image_size = %d,\t IO_METHOD_MMAP buffer.length=%d\r",  

                cap_image_size, buffers[0].length);  

        process_image(buffers[0].start, cap_image_size);  

        if (-1 == xioctl(fd, VIDIOC_QBUF, &buf))  

            errno_exit("VIDIOC_QBUF");  

        break;  

    case IO_METHOD_USERPTR:  

        CLEAR (buf);  

        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  

        buf.memory = V4L2_MEMORY_USERPTR;  

        if (-1 == xioctl(fd, VIDIOC_DQBUF, &buf)) {  

            switch (errno) {  

            case EAGAIN:  

                return 0;  

            case EIO:  

                /* Could ignore EIO, see spec. */  

                /* fall through */  

            default:  

                errno_exit("VIDIOC_DQBUF");  

            }  

        }  

        for (i = 0; i < n_buffers; ++i)  

            if (buf.m.userptr == (unsigned long) buffers[i].start && buf.length  

                    == buffers[i].length)  

                break;  

        assert(i < n_buffers);  

        //      printf("length = %d\r", buffers[i].length);  

        //      process_image((void *) buf.m.userptr, buffers[i].length);  

        printf("image_size = %d,\t IO_METHOD_USERPTR buffer.length=%d\r",  

                cap_image_size, buffers[0].length);  

        process_image(buffers[0].start, cap_image_size);  

        if (-1 == xioctl(fd, VIDIOC_QBUF, &buf))  

            errno_exit("VIDIOC_QBUF");  

        break;  

    }  

    return 1;  

}  

static void mainloop(void) {  

    unsigned int count;  

    count = 100;  

    while (count-- > 0) {  

        for (;;) {  

            fd_set fds;  

            struct timeval tv;  

            int r;  

            FD_ZERO(&fds);  

            FD_SET(fd, &fds);  

            /* Timeout. */  

            tv.tv_sec = 2;  

            tv.tv_usec = 0;  

            r = select(fd + 1, &fds, NULL, NULL, &tv);  

            if (-1 == r) {  

                if (EINTR == errno)  

                    continue;  

                errno_exit("select");  

            }  

            if (0 == r) {  

                fprintf(stderr, "select timeout\n");  

                exit(EXIT_FAILURE);  

            }  

            if (read_frame())  

                break;  

            /* EAGAIN - continue select loop. */  

        }  

    }  

}  

static void stop_capturing(void) {  

    enum v4l2_buf_type type;  

    switch (io) {  

    case IO_METHOD_READ:  

        /* Nothing to do. */  

        break;  

    case IO_METHOD_MMAP:  

    case IO_METHOD_USERPTR:  

        type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  

        if (-1 == xioctl(fd, VIDIOC_STREAMOFF, &type))  

            errno_exit("VIDIOC_STREAMOFF");  

        break;  

    }  

}  

static void start_capturing(void) {  

    unsigned int i;  

    enum v4l2_buf_type type;  

    switch (io) {  

    case IO_METHOD_READ:  

        /* Nothing to do. */  

        break;  

    case IO_METHOD_MMAP:  

        for (i = 0; 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 (-1 == xioctl(fd, VIDIOC_QBUF, &buf))  

                errno_exit("VIDIOC_QBUF");  

        }  

        type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  

        if (-1 == xioctl(fd, VIDIOC_STREAMON, &type))  

            errno_exit("VIDIOC_STREAMON");  

        break;  

    case IO_METHOD_USERPTR:  

        for (i = 0; i < n_buffers; ++i) {  

            struct v4l2_buffer buf;  

            CLEAR (buf);  

            buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  

            buf.memory = V4L2_MEMORY_USERPTR;  

            buf.index = i;  

            buf.m.userptr = (unsigned long) buffers[i].start;  

            buf.length = buffers[i].length;  

            if (-1 == xioctl(fd, VIDIOC_QBUF, &buf))  

                errno_exit("VIDIOC_QBUF");  

        }  

        type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  

        if (-1 == xioctl(fd, VIDIOC_STREAMON, &type))  

            errno_exit("VIDIOC_STREAMON");  

        break;  

    }  

}  

static void uninit_device(void) {  

    unsigned int i;  

    switch (io) {  

    case IO_METHOD_READ:  

        free(buffers[0].start);  

        break;  

    case IO_METHOD_MMAP:  

        for (i = 0; i < n_buffers; ++i)  

            if (-1 == munmap(buffers[i].start, buffers[i].length))  

                errno_exit("munmap");  

        break;  

    case IO_METHOD_USERPTR:  

        for (i = 0; i < n_buffers; ++i)  

            free(buffers[i].start);  

        break;  

    }  

    free(buffers);  

}  

static void init_read(unsigned int buffer_size) {  

    buffers = calloc(1, sizeof(*buffers));  

    if (!buffers) {  

        fprintf(stderr, "Out of memory\n");  

        exit(EXIT_FAILURE);  

    }  

    buffers[0].length = buffer_size;  

    buffers[0].start = malloc(buffer_size);  

    if (!buffers[0].start) {  

        fprintf(stderr, "Out of memory\n");  

        exit(EXIT_FAILURE);  

    }  

}  

static void init_mmap(void) {  

    struct v4l2_requestbuffers req;  

    CLEAR (req);  

    req.count = 4;  

    req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  

    req.memory = V4L2_MEMORY_MMAP;  

    if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) {  

        if (EINVAL == errno) {  

            fprintf(stderr, "%s does not support "  

                "memory mapping\n", dev_name);  

            exit(EXIT_FAILURE);  

        } else {  

            errno_exit("VIDIOC_REQBUFS");  

        }  

    }  

    if (req.count < 2) {  

        fprintf(stderr, "Insufficient buffer memory on %s\n", dev_name);  

        exit(EXIT_FAILURE);  

    }  

    buffers = calloc(req.count, sizeof(*buffers));  

    if (!buffers) {  

        fprintf(stderr, "Out of memory\n");  

        exit(EXIT_FAILURE);  

    }  

    for (n_buffers = 0; 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 (-1 == xioctl(fd, VIDIOC_QUERYBUF, &buf))  

            errno_exit("VIDIOC_QUERYBUF");  

        buffers[n_buffers].length = buf.length;  

        buffers[n_buffers].start = mmap(NULL /* start anywhere */, buf.length,  

                PROT_READ | PROT_WRITE /* required */,  

                MAP_SHARED /* recommended */, fd, buf.m.offset);  

        if (MAP_FAILED == buffers[n_buffers].start)  

            errno_exit("mmap");  

    }  

}  

static void init_userp(unsigned int buffer_size) {  

    struct v4l2_requestbuffers req;  

    unsigned int page_size;  

    page_size = getpagesize();  

    buffer_size = (buffer_size + page_size - 1) & ~(page_size - 1);  

    CLEAR (req);  

    req.count = 4;  

    req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  

    req.memory = V4L2_MEMORY_USERPTR;  

    if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) {  

        if (EINVAL == errno) {  

            fprintf(stderr, "%s does not support "  

                "user pointer i/o\n", dev_name);  

            exit(EXIT_FAILURE);  

        } else {  

            errno_exit("VIDIOC_REQBUFS");  

        }  

    }  

    buffers = calloc(4, sizeof(*buffers));  

    if (!buffers) {  

        fprintf(stderr, "Out of memory\n");  

        exit(EXIT_FAILURE);  

    }  

    for (n_buffers = 0; n_buffers < 4; ++n_buffers) {  

        buffers[n_buffers].length = buffer_size;  

        buffers[n_buffers].start = memalign(/* boundary */page_size,  

                buffer_size);  

        if (!buffers[n_buffers].start) {  

            fprintf(stderr, "Out of memory\n");  

            exit(EXIT_FAILURE);  

        }  

    }  

}  

static void init_device(void) {  

    struct v4l2_capability cap;  

    struct v4l2_cropcap cropcap;  

    struct v4l2_crop crop;  

    struct v4l2_format fmt;  

    unsigned int min;  

    if (-1 == xioctl(fd, VIDIOC_QUERYCAP, &cap)) {  

        if (EINVAL == errno) {  

            fprintf(stderr, "%s is no V4L2 device\n", dev_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", dev_name);  

        exit(EXIT_FAILURE);  

    }  

    switch (io) {  

    case IO_METHOD_READ:  

        if (!(cap.capabilities & V4L2_CAP_READWRITE)) {  

            fprintf(stderr, "%s does not support read i/o\n", dev_name);  

            exit(EXIT_FAILURE);  

        }  

        break;  

    case IO_METHOD_MMAP:  

    case IO_METHOD_USERPTR:  

        if (!(cap.capabilities & V4L2_CAP_STREAMING)) {  

            fprintf(stderr, "%s does not support streaming i/o\n", dev_name);  

            exit(EXIT_FAILURE);  

        }  

        break;  

    }  

    //////not all capture support crop!!!!!!!  

    /* Select video input, video standard and tune here. */  

    printf("-#-#-#-#-#-#-#-#-#-#-#-#-#-\n");  

    CLEAR (cropcap);  

    cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  

    if (0 == xioctl(fd, VIDIOC_CROPCAP, &cropcap)) {  

        crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  

#ifndef CROP_BY_JACK  

        crop.c = cropcap.defrect; /* reset to default */  

#else  

        crop.c.left = cropcap.defrect.left;  

        crop.c.top = cropcap.defrect.top;  

        crop.c.width = 352;  

        crop.c.height = 288;  

#endif  

        printf("----->has ability to crop!!\n");  

        printf("cropcap.defrect = (%d, %d, %d, %d)\n", cropcap.defrect.left,  

                cropcap.defrect.top, cropcap.defrect.width,  

                cropcap.defrect.height);  

        if (-1 == xioctl(fd, VIDIOC_S_CROP, &crop)) {  

            switch (errno) {  

            case EINVAL:  

                /* Cropping not supported. */  

                break;  

            default:  

                /* Errors ignored. */  

                break;  

            }  

            printf("-----!!but crop to (%d, %d, %d, %d) Failed!!\n",  

                    crop.c.left, crop.c.top, crop.c.width, crop.c.height);  

        } else {  

            printf("----->sussess crop to (%d, %d, %d, %d)\n", crop.c.left,  

                    crop.c.top, crop.c.width, crop.c.height);  

        }  

    } else {  

        /* Errors ignored. */  

        printf("!! has no ability to crop!!\n");  

    }  

    printf("-#-#-#-#-#-#-#-#-#-#-#-#-#-\n");  

    printf("\n");  

    ////////////crop finished!  

    //////////set the format  

    CLEAR (fmt);  

    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  

    fmt.fmt.pix.width = 640;  

    fmt.fmt.pix.height = 480;  

    //V4L2_PIX_FMT_YVU420, V4L2_PIX_FMT_YUV420 — Planar formats with 1/2 horizontal and vertical chroma resolution, also known as YUV 4:2:0  

    //V4L2_PIX_FMT_YUYV — Packed format with 1/2 horizontal chroma resolution, also known as YUV 4:2:2  

    fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;//V4L2_PIX_FMT_YUV420;//V4L2_PIX_FMT_YUYV;  

    fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;  

    {  

        printf("-#-#-#-#-#-#-#-#-#-#-#-#-#-\n");  

        printf("=====will set fmt to (%d, %d)--", fmt.fmt.pix.width,  

                fmt.fmt.pix.height);  

        if (fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) {  

            printf("V4L2_PIX_FMT_YUYV\n");  

        } else if (fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) {  

            printf("V4L2_PIX_FMT_YUV420\n");  

        } else if (fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_NV12) {  

            printf("V4L2_PIX_FMT_NV12\n");  

        }  

    }  

    if (-1 == xioctl(fd, VIDIOC_S_FMT, &fmt))  

        errno_exit("VIDIOC_S_FMT");  

    {  

        printf("=====after set fmt\n");  

        printf("    fmt.fmt.pix.width = %d\n", fmt.fmt.pix.width);  

        printf("    fmt.fmt.pix.height = %d\n", fmt.fmt.pix.height);  

        printf("    fmt.fmt.pix.sizeimage = %d\n", fmt.fmt.pix.sizeimage);  

        cap_image_size = fmt.fmt.pix.sizeimage;  

        printf("    fmt.fmt.pix.bytesperline = %d\n", fmt.fmt.pix.bytesperline);  

        printf("-#-#-#-#-#-#-#-#-#-#-#-#-#-\n");  

        printf("\n");  

    }  

    cap_image_size = fmt.fmt.pix.sizeimage;  

    /* Note VIDIOC_S_FMT may change width and height. */  

    printf("-#-#-#-#-#-#-#-#-#-#-#-#-#-\n");  

    /* Buggy driver paranoia. */  

    min = fmt.fmt.pix.width * 2;  

    if (fmt.fmt.pix.bytesperline < min)  

        fmt.fmt.pix.bytesperline = min;  

    min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height;  

    if (fmt.fmt.pix.sizeimage < min)  

        fmt.fmt.pix.sizeimage = min;  

    printf("After Buggy driver paranoia\n");  

    printf("    >>fmt.fmt.pix.sizeimage = %d\n", fmt.fmt.pix.sizeimage);  

    printf("    >>fmt.fmt.pix.bytesperline = %d\n", fmt.fmt.pix.bytesperline);  

    printf("-#-#-#-#-#-#-#-#-#-#-#-#-#-\n");  

    printf("\n");  

    switch (io) {  

    case IO_METHOD_READ:  

        init_read(fmt.fmt.pix.sizeimage);  

        break;  

    case IO_METHOD_MMAP:  

        init_mmap();  

        break;  

    case IO_METHOD_USERPTR:  

        init_userp(fmt.fmt.pix.sizeimage);  

        break;  

    }  

}  

static void close_device(void) {  

    if (-1 == close(fd))  

        errno_exit("close");  

    fd = -1;  

}  

static void open_device(void) {  

    struct stat st;  

    if (-1 == stat(dev_name, &st)) {  

        fprintf(stderr, "Cannot identify '%s': %d, %s\n", dev_name, errno,  

                strerror(errno));  

        exit(EXIT_FAILURE);  

    }  

    if (!S_ISCHR(st.st_mode)) {  

        fprintf(stderr, "%s is no device\n", dev_name);  

        exit(EXIT_FAILURE);  

    }  

    fd = open(dev_name, O_RDWR /* required */| O_NONBLOCK, 0);  

    if (-1 == fd) {  

        fprintf(stderr, "Cannot open '%s': %d, %s\n", dev_name, errno,  

                strerror(errno));  

        exit(EXIT_FAILURE);  

    }  

}  

static void usage(FILE * fp, int argc, char ** argv) {  

    fprintf(fp, "Usage: %s [options]\n\n"  

        "Options:\n"  

        "-d | --device name   Video device name [/dev/video0]\n"  

        "-h | --help          Print this message\n"  

        "-m | --mmap          Use memory mapped buffers\n"  

        "-r | --read          Use read() calls\n"  

        "-u | --userp         Use application allocated buffers\n"  

        "", argv[0]);  

}  

static const char short_options[] = "d:hmru";  

static const struct option long_options[] = { { "device", required_argument,  

        NULL, 'd' }, { "help", no_argument, NULL, 'h' }, { "mmap", no_argument,  

        NULL, 'm' }, { "read", no_argument, NULL, 'r' }, { "userp",  

        no_argument, NULL, 'u' }, { 0, 0, 0, 0 } };  

int main(int argc, char ** argv) {  

    dev_name = "/dev/video0";  

    outf = fopen("out.yuv", "wb");  

    for (;;) {  

        int index;  

        int c;  

        c = getopt_long(argc, argv, short_options, long_options, &index);  

        if (-1 == c)  

            break;  

        switch (c) {  

        case 0: /* getopt_long() flag */  

            break;  

        case 'd':  

            dev_name = optarg;  

            break;  

        case 'h':  

            usage(stdout, argc, argv);  

            exit(EXIT_SUCCESS);  

        case 'm':  

            io = IO_METHOD_MMAP;  

            break;  

        case 'r':  

            io = IO_METHOD_READ;  

            break;  

        case 'u':  

            io = IO_METHOD_USERPTR;  

            break;  

        default:  

            usage(stderr, argc, argv);  

            exit(EXIT_FAILURE);  

        }  

    }  

    open_device();  

    init_device();  

    start_capturing();  

    mainloop();  

    printf("\n");  

    stop_capturing();  

    fclose(outf);  

    uninit_device();  

    close_device();  

    exit(EXIT_SUCCESS);  

    return 0;  

}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章