在(原創)基於ZedBoard的Webcam設計(一):USB攝像頭(V4L2接口)的圖片采集和(原創)基於ZedBoard的Webcam設計(二):USB攝像頭圖片采集+QT顯示兩篇博客中,我們完成了ZedBoard上USB攝像頭的圖片采集,以及將圖片顯示到Qt界面上。可是畢竟采集和顯示到只是一幀圖像,並非動態的視頻數據。本小節將在介紹V4L2更新視頻緩沖的方式、Qt paintEvent更新控件的基礎上,實現如何實現視頻的動態顯示。
更多更新請關注我的博客:@超群天晴 http://www.cnblogs.com/surpassal/
相關閱讀 :
(原創)基於ZedBoard的Webcam設計(一):USB攝像頭(V4L2接口)的圖片采集
(原創)基於ZedBoard的Webcam設計(二):USB攝像頭圖片采集+QT顯示
(原創)基於ZedBoard的Webcam設計(三):視頻的采集和動態顯示
(原創)基於ZedBoard的Webcam設計(四):MJPG編碼和AVI封裝
(原創)基於ZedBoard的Webcam設計(五):x264編碼在zedboard上的實現(軟編碼)
硬件平台:Digilent ZedBoard
開發環境:Windows XP 32 bit + Wmare 8.0 + Ubuntu 10.04 +Qt+ arm-linux-xilinx-gnueabi交叉編譯環境
Zedboard linux: Digilent OOB Design
一、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上進行測試,測試視頻:
==================================