1. 程式人生 > >基於ZedBoard的Webcam設計(三):視訊的採集和動態顯示

基於ZedBoard的Webcam設計(三):視訊的採集和動態顯示

一、V4l2更新緩衝Buffer的方法

 回顧上一節中,我們使用v4l2控制usb 攝像頭,對攝像頭的靜態圖片採集流程操作過程可以歸納為圖1:

圖1 靜態圖片採集流程圖

所用到的函式和引數都在旁邊標註出。可以看到使用命令VIDIOC_DQBUF將快取中的影象幀取出,然後攝像頭裝置是一直在採集影象,如果沒有更新快取區命令,採集到的新資料是不會被更新到快取中的。v4l2提供了與VIDIOC_DQBUF命令相對的命令VIDIOC_QBUF,我對這個命令的理解就是允許攝像頭裝置將採集影象更新到快取區。假設開闢的快取FIFO大小為4幀,如圖2(a),當使用VIDIOC_DQBUF命令後,當前幀n從FIFO中取走,FIFO留下一個空缺,如圖2(b),這種情況下如果使用VIDIOC_QBUF命令,新一幀n+4將被寫入快取,如圖2(c)。

圖2 快取FIFO與VIDIOC_DQBUF命令、VIDIOC_QBUF命令

所以為了實現快取區影象資料的動態更新,需要在每一次處理完資料後使用VIDIOC_QBUF更新快取區,以便下一次VIDIOC_DQBUF獲取到新的一幀資料。因而動態更新快取的視訊採集流程應該如圖3所示:

 

圖3 動態視訊採集流程

 為此,需要重新定義兩個函式,一個我們定義為get_frame獲取視訊幀:

複製程式碼
 1 int VideoDevice::get_frame(void **frame_buf, size_t* len)
 2 {
 3     v4l2_buffer queue_buf;
 4
5 queue_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 6 queue_buf.memory = V4L2_MEMORY_MMAP; 7 8 if(ioctl(fd, VIDIOC_DQBUF, &queue_buf) == -1) 9 { 10 return FALSE; 11 } 12 13 *frame_buf = buffers[queue_buf.index].start; 14 *len = buffers[queue_buf.index].length;
15 index = queue_buf.index; 16 17 return TRUE; 18 }
複製程式碼

再定義free_frame釋放視訊幀,讓出快取空間準備新的視訊幀資料:

複製程式碼
 1 int VideoDevice::free_frame()
 2 {
 3     if(index != -1)
 4     {
 5         v4l2_buffer queue_buf;
 6         queue_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 7         queue_buf.memory = V4L2_MEMORY_MMAP;
 8         queue_buf.index = index;
 9 
10         if(ioctl(fd, VIDIOC_QBUF, &queue_buf) == -1)
11         {
12             return FALSE;
13         }
14         return TRUE;
15     }
16     return FALSE;
17 }
複製程式碼

 二、Qt的paintEvent事件

在上篇部落格裡面,我們對採集的的視訊幀資料的顯示,採用的方法是使用了一個QLabel和QPixmap,並使用loadfromdata函式將採集的資料轉為QPixmap中的資料,並顯示到QLabel上。這樣的做法導致的結果是QLabel和QPixmap資料只能被更新一次,所以只能顯示靜態圖片。

在完成了視訊快取資料更新後,我們所面臨的問題就是怎麼樣才能把這個資料動態顯示出來。好在Qt提供了視窗重新整理事件paintEvent,在這裡,我們可以使用兩種方式觸發paintEvent事件:

1、使用定時器QTimer,定時為33ms(因為攝像頭的幀頻為30pfs);

2、不使用定時器,由QLabel自身內容改變產生。這裡採用這種方式。paintEvent函式內容:

複製程式碼
 1 void Widget::paintEvent(QPaintEvent *)
 2 {
 3     rs = vd->get_frame((void **)&yuv_buffer,&len);
 4     convert_yuv_to_rgb_buffer(yuv_buffer,rgb_buffer,640,480);
 5 
 6     frame->loadFromData((uchar *)rgb_buffer,640 * 480 * 3);
 7 
 8     ui->label->setPixmap(QPixmap::fromImage(*frame,Qt::AutoColor));
 9 
10     rs = vd->unget_frame();
11 }
複製程式碼

三、測試效果

在ubuntu上測試通過後,移植到Zedboard上進行測試,測試視訊:

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