1. 程式人生 > >ARM9+Linux下USB攝像頭影象採集

ARM9+Linux下USB攝像頭影象採集

終於實現了在Micro2440+Linux2.6.29環境下通過NFS檔案系統實現USB攝像頭影象採集。在此,做一個總結,以備後續參考。 

實驗條件:RedHat9.0(核心版本2.4.18),Micro2440開發板,Linux2.6.29(arm9),ANC (Vimicro Corp.) USB PC攝像頭,GCC,SDL-1.2.14.tar,servfox-R1_1_3[1].tar,spcaview-20061208[1].tar。

一、升級Redhat9.0核心

由於Readhat9.0核心版本是2.4,很多核心驅動未包含其中,尤其是USB萬能攝像頭GSPCA和UVC。

二、配置編譯Micro2440上執行核心

重新編譯Micro2440上執行的核心,配置V4L和USB驅動。選擇linux-2.6.29中的config-mini2440為參考,執行:cp config-mini2440 .config;然後make menuconfig ARCH=arm CROSS_COMPILE=arm-linux-;出現相應的圖形化配置視窗。

如果需要在Linux作業系統中使用USB攝像頭進行視訊資料採集,則必須在進行核心配置時,應檢查Linux 核心中是否已經添加了對Video4Linux驅動和對USB攝像頭驅動模組的支援。

利用Linux核心配置make menuconfig命令對Video4Linux支援驅動進行配置,選中多媒體裝置選項“Multimedia device->”(如圖15-1所示)按回車,進入多媒體裝置配置介面。在多媒體配置介面中,選中“Video For Linux”,就可以使核心實現對Video4Linux驅動的支援,為視訊採集裝置提供程式設計介面。

         配置好核心對Video4Linux驅動後,返回核心配置主介面,選中USB支援選項(如圖15-3 所示)“USB support—>” 按回車,進入USB支援配置介面

         配置完成後,執行make uImage ARCH=arm CROSS_COMPILE=arm-linux-。完成後,uImage就生成在arch/arm/boot/中。通過uboot下載uImage到記憶體中,啟動核心,實現NFS起檔案系統。

三、安裝SDL-1.2.14

因為在redhat9.0中顯示視訊影象,呼叫spcaview(後面介紹)需要有SDL庫的支援,否則會報錯。所以,將SDL-1.2.14.tar下載傳送到系統中,解壓後執行.configure;make;make install。完成SDL庫的安裝。

四、servfox-R1_1_3工具

     下載Servfox-R1_1_3解壓進入其目錄,將其中的Makefile.arm修改為Makefile,並修改Makefile裡面的CC為arm -linux -gcc,因為預設的是載入的動態庫,因此還需要修改Makefile中的SERVFLAGS為SERVFLAGS= -O2 –DLINUX $(WARNINGS) –static,其中-static是我們新增的內容,make之後生成servfox。

五、              spcaview,redhat9.0下的視訊顯示工具
下載spcaview原始碼包解壓,進入解壓後的目錄,make後會報錯,一般需要安裝SDL庫,去網上下載後安裝SDL庫,在PC 機上安裝SDL庫:

解壓:tar xvf SDL-1.2.13.tar.tar

配置:./configure

編譯:make

安裝:make install

進入spcaview解壓目錄修改Makefile中的修改Makefile中的SDLLIBS =$(shell sdl-config --libs) 和 SDLFLAGS = $(shell sdl-config --cflags),這兩句主要是指定了SDL庫的庫路徑和編譯選項。Make之後生成spcaview,在客戶端執行spcaview –w 伺服器ip:伺服器埠號。就可以採集影象了,

[[email protected] spcaview-20061208]# ./spcaview -g -w 10.103.4.211:7070
 Spcaview version: 1.1.7 date: 06:11:2006 (C) [email protected] 
using Server 10.103.4.211 Port 7070

六、測試

將新編譯的核心載入到ARM上,通過NFS檔案系統執行servfox :

# ./servfox -d /dev/video0 -g -s 640x480 -w 7070                            

 servfox version: 1.1.3 date: 11:12:2005 (C) [email protected]             

wrong spca5xx device                                                           

Waiting .... for connection. CTrl_c to stop !!!!                             

Got connection from 10.103.4.216

         在redhat9.0中執行spcaview:

[[email protected] spcaview-20061208]# ./spcaview -g -w 10.103.4.211:7070
 Spcaview version: 1.1.7 date: 06:11:2006 (C) [email protected] 
using Server 10.103.4.211 Port 7070

此時,視訊能在redhat9.0視窗中顯示,如圖所示。 

七、總結

在實現影象採集的過程中,碰到很多問題,嘗試過很多方法,走了很多彎路,雖然很多碰壁,但也有不少收穫。

編譯核心:嘗試編譯高版本的核心,或者編譯新核心的時候,最好選擇和現有的配置檔案相近的系統的配置檔案作為參考。在原有基礎上增加一些模組,編譯,然後試驗。如果一次性增加模組過多,容易造成編譯錯誤等問題。

編譯工具:在編譯某些外掛的時候,如:servfox、spcaview等,經常會報一些檔案未定義、庫不存在等錯誤,一般是由於系統缺少相應的庫檔案,需要下載安裝,如SDL,或者由於系統本身版本不匹配,庫檔案安裝位置不符造成。熟練運用goole、百度進行搜尋,找到問題解決方法。同時,很多工具、庫、外掛等在開源社群網站都有穩定版本下載,使用這些網站提供的版本可靠性更好。

重點難點:makefile、kconfig等檔案的書寫,kit工具使用等。

附錄:視訊採集原始碼分析(看程式碼採用Lucida Console或者Consolas等寬字型較好,0和o區別明顯,不易混淆)

/*

 *  V4L2 video capture example

 *

 *  This program can be used and distributed without restrictions.

 */

#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;        //一共有三種視訊採集方式:使用read、write方式;記憶體對映方式和使用者指標模式

struct buffer {

        void *                  start;

        size_t                  length;

};  // 定義緩衝區

static char *           dev_name        = NULL;   // 裝置名稱,可根據裝置名開啟裝置

static io_method        io              = IO_METHOD_MMAP;// 視訊採集方式,記憶體對映方式

static int              fd              = -1;

struct buffer *         buffers         = NULL;

static unsigned int     n_buffers       = 0;

static void errno_exit (const char *s)// 錯誤退出,並列印錯誤資訊

{

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

        exit (EXIT_FAILURE);

}

static intxioctl  (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)// 影象處理

{

        fputc ('.', stdout);

        fflush (stdout);// 重新整理輸出緩衝

}

static int read_frame (void)// 對影象幀進行讀取

{

        struct v4l2_buffer buf;//這是視訊介面V4L2中相關檔案定義的,在videodev2.h中

        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");

                        }

                }

                process_image (buffers[0].start);

                break;

        case IO_METHOD_MMAP://採用記憶體對映的方式

                CLEAR (buf);//對緩衝區清零

                buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

                buf.memory = V4L2_MEMORY_MMAP;

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

                  //VIDIOC_DQBUF控制引數作用是申請視訊緩衝區

                        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);

                process_image (buffers[buf.index].start);

                if (-1 == ioctl (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 == ioctl (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);

                process_image ((void *) buf.m.userptr);

                if (-1 == ioctl (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);//等待一幀影象採集完成,超時定為2秒

                        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 == ioctl (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 == ioctl (fd, VIDIOC_QBUF, &buf))

                                errno_exit ("VIDIOC_QBUF");

                }

                type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

                if (-1 == ioctl (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 == ioctl (fd, VIDIOC_QBUF, &buf))

                                errno_exit ("VIDIOC_QBUF");

                }

                type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

                if (-1 == ioctl (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 == ioctl (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 == ioctl (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 ();

相關推薦

no