Linux 下攝像頭視頻采集與顯示


我將它設置為MJPEG格式,同樣不行,所以圖2最后同樣出錯。(那時正興高采烈地做畢業設計,這個問題讓我足足郁悶了好幾天。我想不通是什么原因)

圖1 攝像頭信息

圖2 又一個信息

下面簡單講一下程序片段,具體的程序,參見附錄中。

(1)、分配內存

    switch (vd_info->format)    /**< format will be also ok */

    {

    case V4L2_PIX_FMT_MJPEG:

        vd_info->tmp_buffer =

            (uint8 *)calloc(1, (size_t)vd_info->frame_size_in);

        if (vd_info->tmp_buffer == NULL)

            error_out("unable alloc tmp_buffer");

        vd_info->frame_buffer =

            (uint8 *)calloc(1, (size_t)vd_info->width * (vd_info->height+8) * 2);

        if (vd_info->frame_buffer == NULL)

            error_out("unable alloc frame_buffer");

        break;

    case V4L2_PIX_FMT_YUYV:

        vd_info->frame_buffer =

            (uint8 *)calloc(1,(size_t)vd_info->frame_size_in);

        if (vd_info->frame_buffer == NULL)

            error_out("unable alloc frame_buffer");

        break;

    default:

        msg_out("error!/n");

        return -1;

        break;

    }

因為YUYV是一種原始數據,可以直接顯示,不需要編解碼,而MJPEG格式的,需要解碼,所以分要分配兩個緩沖區。

(2)、打開,O_NONBLOCK是以非阻塞方式打開。

  vd_info->camfd = open(device, O_RDWR /*| O_NONBLOCK*/, 0);

    if (vd_info->camfd < 0)

        error_out("can not open the device");

(3)、查詢

    if (-1 == ioctl(vd_info->camfd, VIDIOC_QUERYCAP, &vd_info->cap))

        error_out("query camera failed");

    if (0 == (vd_info->cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))

    {

        debug_msg("video capture not supported./n");

        return -1;

    }

(4)、設置格式

memset(&vd_info->fmt, 0, sizeof(struct v4l2_format));

    vd_info->fmt.type             = V4L2_BUF_TYPE_VIDEO_CAPTURE;

    vd_info->fmt.fmt.pix.width       = width;

    vd_info->fmt.fmt.pix.height      = height;

    vd_info->fmt.fmt.pix.field  =V4L2_FIELD_ANY;

    vd_info->fmt.fmt.pix.pixelformat = format;

    if (-1 == ioctl(vd_info->camfd, VIDIOC_S_FMT, &vd_info->fmt))

        error_out("unable to set format ");

(5)、查詢緩沖區、映射到用戶空間內存

memset(&vd_info->rb, 0, sizeof(struct v4l2_requestbuffers));

    vd_info->rb.count      = NB_BUFFER; /**< 4 buffers */

    vd_info->rb.type       = V4L2_BUF_TYPE_VIDEO_CAPTURE;

    vd_info->rb.memory  = V4L2_MEMORY_MMAP;

    if (-1 == ioctl(vd_info->camfd, VIDIOC_REQBUFS, &vd_info->rb))

        error_out("unable to allocte buffers");

    /* map the buffers(4 buffer) */

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

    {

        memset(&vd_info->buf, 0, sizeof(struct v4l2_buffer));

        vd_info->buf.index   = i;

        vd_info->buf.type     = V4L2_BUF_TYPE_VIDEO_CAPTURE;

        vd_info->buf.memory = V4L2_MEMORY_MMAP;

        if (-1 == ioctl(vd_info->camfd, VIDIOC_QUERYBUF, &vd_info->buf))

            error_out("unable to query buffer");

        /* map it, 0 means anywhere */

        vd_info->mem[i] =

            mmap(0, vd_info->buf.length, PROT_READ, MAP_SHARED,

                 vd_info->camfd, vd_info->buf.m.offset);

        /* MAP_FAILED = (void *)-1 */

        if (MAP_FAILED == vd_info->mem[i])

            error_out("unable to map buffer");

    }

(6)、進入隊列

   /* queue the buffers */

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

    {

        memset(&vd_info->buf, 0, sizeof(struct v4l2_buffer));

        vd_info->buf.index   = i;

        vd_info->buf.type     = V4L2_BUF_TYPE_VIDEO_CAPTURE;

        vd_info->buf.memory = V4L2_MEMORY_MMAP;

        if (-1 == ioctl(vd_info->camfd, VIDIOC_QBUF, &vd_info->buf))

            error_out("unable to queue the buffers");

    }

(7)、開始捕獲(發出捕獲信號)

    vd_info->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

    if (-1 == ioctl(vd_info->camfd, VIDIOC_STREAMON, &vd_info->type))

        error_out("unable to start capture");

    vd_info->is_streaming = 1;

    debug_msg("stream on OK!!/n");

    debug_msg("===============================/n/n");

(8)、采集(從緩沖區隊列中取出數據,再將數據的內存復制另一內存區)

static int count = 0;

    if (!vd_info->is_streaming) /**< if stream is off, start it */

    {

        if (v4l2_on(vd_info))  /**< failed */

            goto err;

    }

    memset(&vd_info->buf, 0, sizeof(struct v4l2_buffer));

    vd_info->buf.type      = V4L2_BUF_TYPE_VIDEO_CAPTURE;

    vd_info->buf.memory = V4L2_MEMORY_MMAP;

    /* get data from buffers */

    if (-1 == ioctl(vd_info->camfd, VIDIOC_DQBUF, &vd_info->buf))

    {

        msg_out("unable to dequeue buffer/n");

        goto err;

    }

    switch (vd_info->format)

    {

    case V4L2_PIX_FMT_MJPEG:

        if (vd_info->buf.bytesused <= HEADFRAME1)

        {

            msg_out("ignore empty frame.../n");

            return 0;

        }

        /* we can save tmp_buff to a jpg file,just write it! */

        memcpy(vd_info->tmp_buffer, vd_info->mem[vd_info->buf.index],

               vd_info->buf.bytesused);

        /* here decode MJPEG,so we can dispaly it */

        if (jpeg_decode(&vd_info->frame_buffer, vd_info->tmp_buffer,

                        &vd_info->width, &vd_info->height) < 0 )

        {

            msg_out("decode jpeg error/n");

            goto err;

        }

        break;

    case V4L2_PIX_FMT_YUYV:

        if (vd_info->buf.bytesused > vd_info->frame_size_in)

            memcpy(vd_info->frame_buffer, vd_info->mem[vd_info->buf.index],

                   (size_t)vd_info->frame_size_in);

        else

            memcpy(vd_info->frame_buffer, vd_info->mem[vd_info->buf.index],

                   (size_t)vd_info->buf.bytesused);

        break;

    default:

        goto err;

        break;

    }

    /* here you can process the frame! */

    v4l2_process(vd_info);

    /* queue buffer again */

    if (-1 == ioctl(vd_info->camfd, VIDIOC_QBUF, &vd_info->buf))

    {

        fprintf(stderr,"requeue error/n");

        goto err;

    }

    debug_msg("frame:%d/n", count++);

    debug_msg("frame size in: %d KB/n", vd_info->frame_size_in>>10);

    return 0;

err:

    vd_info->is_quit = 0;

    return -1;

(9)、停止捕獲(發送停止信號)

    vd_info->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

    if (-1 == ioctl(vd_info->camfd, VIDIOC_STREAMOFF, &vd_info->type))

        error_out("unable to stop capture");

    vd_info->is_streaming = 0;

    debug_msg("stream off OK!/n");

    debug_msg("===============================/n/n");

(10)、關閉設備(釋放內存、關閉設備)

    uint16 i = 0;

    if (vd_info->is_streaming)  /**< stop if it is still capturing */

        v4l2_off(vd_info);

    if (vd_info->frame_buffer)

        free(vd_info->frame_buffer);

    if (vd_info->tmp_buffer)

        free(vd_info->tmp_buffer);

    vd_info->frame_buffer = NULL;

    /* it is a good thing to  unmap! */

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

    {

        if (-1 == munmap(vd_info->mem[i], vd_info->buf.length))

            error_out("munmap");

    }

    close(vd_info->camfd);

    debug_msg("close OK!/n");

3、顯示——SDL

SDL是Simple DirectMedia Layer的簡稱,是一個自由的跨平台的多媒體開發包,適用於游戲、游戲SDK、演示軟件、模擬器、MPEG播放器和其他應用軟件。本文將它大材小用,用於顯示采集得到的視頻數據。

顯示的代碼片段如下:

   SDL_Surface *pscreen = NULL;

    SDL_Overlay *overlay = NULL;

    SDL_Rect drect;

    SDL_Event sdlevent;

    SDL_mutex *affmutex = NULL;

    unsigned char frmrate;

    unsigned char *p = NULL;

    uint32 currtime;

    uint32 lasttime;

    char* status = NULL;

    /************* Test SDL capabilities ************/

    /* memory leak here */

    if (SDL_Init(SDL_INIT_VIDEO) < 0)

    {

        fprintf(stderr, "Couldn't initialize SDL: %s/n", SDL_GetError());

        exit(1);

    }

    /* it need to alloc space! */

    vd_info = (struct video_info *) calloc(1, sizeof(struct video_info));

    /* init the camera,you can change the last three params!!! */

    if (v4l2_init(vd_info, V4L2_PIX_FMT_YUYV, 640, 480) < 0)

        return EXIT_FAILURE;

    pscreen =

        SDL_SetVideoMode(vd_info->width, vd_info->height, 0,

                     SDL_VIDEO_Flags);

    overlay =

      SDL_CreateYUVOverlay(vd_info->width, vd_info->height,

                         SDL_YUY2_OVERLAY, pscreen);

    /* here?? */

    p = (unsigned char *) overlay->pixels[0];

    drect.x = 0;

    drect.y = 0;

    drect.w = pscreen->w;

    drect.h = pscreen->h;

    lasttime = SDL_GetTicks();

    affmutex = SDL_CreateMutex();

    /* big loop */

    while (vd_info->is_quit)

    {

        while (SDL_PollEvent(&sdlevent))

        {

            if (sdlevent.type == SDL_QUIT)

            {

                vd_info->is_quit = 0;

                break;

            }

        }

        currtime = SDL_GetTicks();

        if (currtime - lasttime > 0)

        {

            frmrate = 1000/(currtime - lasttime);

        }

        lasttime = currtime;

        if (v4l2_grab(vd_info) < 0)

        {

            printf("Error grabbing /n");

            break;

        }

        SDL_LockYUVOverlay(overlay);

        /* frame_buffer to p */

        memcpy(p, vd_info->frame_buffer,

               vd_info->width * (vd_info->height) * 2);

        SDL_UnlockYUVOverlay(overlay);

        SDL_DisplayYUVOverlay(overlay, &drect); /* dispaly it */

        status = (char *)calloc(1, 20*sizeof(char));

        sprintf(status, "come on fps:%d",frmrate);

        SDL_WM_SetCaption(status, NULL);

        SDL_Delay(10);

    }

    SDL_DestroyMutex(affmutex);

    SDL_FreeYUVOverlay(overlay);

    v4l2_close(vd_info);

    free(status);

    free(vd_info);

    printf("Clean Up done Quit /n");

    SDL_Quit();

    return 0;

在紅旗操作系統下測試如圖3所示。最上面的即為SDL繪制的窗口(至於它向下調用什么圖形庫就不用理會了),中間的為控制台,它不斷輸出采集到的幀數以及每一幀的大小。最下面的是emacs。

圖3 測試示例圖

附:

為了演示emacs的多窗口gdb調試,現附上幾個圖。算是emacs篇的補充吧。

附圖1中,已經加載了可執行文件,並設置了斷點(命令為b main emacs,單步后,中間窗口出現紅點即為斷點處)。

附圖2所示的是正在顯示圖像,左上角即為控制台,出現的信息同上述截圖一樣。

附圖3為結束的情形。重新回到gdb等待命令狀態。

此時的emacs五個窗口中各有自己的功能,從上而下,分別為控制台、變量、源代碼、幀棧以及斷點。

看到了吧?emacs中調試不比VC差吧?

附圖1 開始

附圖2 顯示

附圖3 結束

再附:

UVC官網:

http://www.ideasonboard.org/uvc/

V4L2官網:

http://linux.bytesex.org/v4l2/

轉至http://blog.chinaunix.net/u1/58951/showart_2257609.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM