寫在開頭
筆者自從學(xué)習(xí)了Framebuffer編程和V4L2編程之后,就想實(shí)現(xiàn)在LCD屏上顯示實(shí)時視頻
筆者學(xué)習(xí)過正點(diǎn)I.MX6U Linux C編程中的相關(guān)內(nèi)容,然而原子的例程是針對OV5640攝像頭寫的,像素格式是RGB
然而USB攝像頭大多支持MJPEG或者YUYV格式,如果要在屏幕上顯示需要進(jìn)行格式轉(zhuǎn)換,而轉(zhuǎn)換像素格式是需要處理時間的,自然會影響視頻幀率
筆者嘗試寫過YUYV2RGB888或者YUYV2RGB565,實(shí)際跑起來能明顯感受到掉幀;
同時,筆者也考慮通過OpenCV來對JPEG或者YUYV進(jìn)行編解碼來顯示,然而實(shí)際效果也不盡如人意
那有沒有辦法既能實(shí)時顯示視頻也不用進(jìn)行圖像格式轉(zhuǎn)換還能有用戶操作界面?
筆者結(jié)合之前所學(xué)內(nèi)容最終決定用QT寫界面,V4L2編程來獲取實(shí)時的幀并將其顯示在設(shè)計好的界面上
開發(fā)環(huán)境
- 虛擬機(jī)Ubuntu 16.04
- 編輯器VsCode
- 交叉編譯工具 arm-linux-gnueabihf
- 已制作文件系統(tǒng),已使能UVC相關(guān)驅(qū)動
- 正點(diǎn)原子ZYNQ7010啟明星開發(fā)板
- USB攝像頭淘寶隨便買的一個
- QT Creator 5.9.6
必備知識
UVC驅(qū)動配置可以看我的這一篇: Linux 內(nèi)核4.14添加UVC配置
QT移植并顯示圖片可以看我的這一篇: Qt移植正點(diǎn)原子ZYNQ7010-Arm平臺顯示圖片demo,本博客的程序設(shè)計也是基于這一篇文章的
V4L2編程入門可以看我的這一篇: V4L2編程之USB攝像頭采集jpeg圖像
界面布局設(shè)計
可以先看一下運(yùn)行效果,主體部分一共就三個:一個QLabel控件用于顯示實(shí)時圖像,兩個QPushButton,一個用于控制視頻流的顯示,另一個用于解放資源并關(guān)閉窗口。
界面很簡單,本博客只是實(shí)現(xiàn)最基礎(chǔ)的功能,后續(xù)會在此基礎(chǔ)上加控件和其他功能。
程序設(shè)計思路
- 界面布局初始化
- 獲得用戶輸入,打開對應(yīng)攝像頭
- 攝像頭初始化,包括打印攝像頭支持的像素格式、分辨率、幀率
- 設(shè)置采集格式,包括像素格式、幀率設(shè)置
- 申請內(nèi)存空間并建立內(nèi)存映射,進(jìn)行出隊入隊操作
- 開啟視頻流
- 設(shè)置定時器,等待“開始”按鈕信號觸發(fā)定時器開啟
- 定時器定時獲取一幀圖像將其顯示在QLabel上
- 等待“結(jié)束”按鈕信號觸發(fā)釋放資源并關(guān)閉窗口
關(guān)鍵部分在于789三步,這里稍作解釋,不想看的可直接去看源碼——我的Github倉庫: Linux C編程實(shí)戰(zhàn)代碼
我已經(jīng)將源碼連同V4L2編程的API用戶手冊一并上傳了,歡迎大家學(xué)習(xí)交流
要在ARM平臺跑也是可以的,需要自行移植QT到ARM開發(fā)板,移植辦法詳見我往期博客Qt移植正點(diǎn)原子ZYNQ7010-Arm平臺顯示圖片demo
關(guān)鍵代碼分析
獲取用戶輸入賦值給v4l2_dev_init()初始化對應(yīng)的攝像頭
引入QCoreApplication獲取用戶輸入的第二個字段,例如執(zhí)行 ./Qt_V4l2 /dev/video1
,device_parm[1] 的內(nèi)容即/dev/video1
值得注意的是需要進(jìn)行一個數(shù)據(jù)類型轉(zhuǎn)換才能作為open()
函數(shù)的參數(shù)
QStringList device_parm = QCoreApplication::arguments();
QString str = device_parm[1];
v4l2_dev_init(str.toStdString());
......
int MainWindow::v4l2_dev_init(string device_name){
/* 打開攝像頭 */
v4l2_fd = open(device_name.c_str(),O_RDWR);
if(v4l2_fd < 0){
printf("open camera failed\n");
return -1;
}
printf("open camera success\n");
......
}
兩個按鈕的信號連接代碼
//點(diǎn)擊開始按鈕,打開定時器
connect(pushButton[0],SIGNAL(clicked()),this,SLOT(timer_start()));
//每隔固定的時間顯示一幀
timer = new QTimer();
connect(timer, SIGNAL(timeout()), this, SLOT(video_show()));
/* 按鈕窗口關(guān)閉,先釋放設(shè)備,再關(guān)閉窗口 */
connect(pushButton[1],SIGNAL(clicked()),this,SLOT(v4l2_device_release()));
connect(pushButton[1],SIGNAL(clicked()),this,SLOT(close()));
定時器控制幀率,每個33ms觸發(fā)video_show()顯示一幀數(shù)據(jù),一秒鐘正好顯示30幀(筆者的攝像頭最大支持30fps,故如此設(shè)置)
/* 定時器控制幀 */
void MainWindow::timer_start(){
// 1000/33約等于30,也就是每一秒顯示30幀
timer->start(33);
}
初始化select()來進(jìn)行I/O端口復(fù)用
fd_set fds;
FD_ZERO(&fds);
FD_SET(v4l2_fd,&fds);
//設(shè)置等待時間為2s
struct timeval tv;
tv.tv_sec = 2;
tv.tv_usec = 0;
select(v4l2_fd+1,&fds,NULL,NULL,&tv);
此段代碼摘自V4L2 API手冊最后的示例代碼中,相關(guān)pdf文檔已上傳到github
獲取一幀圖像的數(shù)據(jù),見于v4l2_get_one_frame(FrameBuffer *framebuf)
首先從出隊隊列中取一幀視頻數(shù)據(jù)的視頻緩沖區(qū),再將該幀數(shù)據(jù)拷貝到frame中
if(0 > ioctl(v4l2_fd,VIDIOC_DQBUF,&one_buf)){
printf("VIDIOC_DQBUF failed!\n");
return -1;
}
// bytesused 表示buf中已經(jīng)使用的字節(jié)數(shù)
memcpy(framebuf->buf,(char *)buffer_infos[one_buf.index].start,one_buf.bytesused);
framebuf->length = one_buf.bytesused;
Qt 提供了四個用于處理圖像數(shù)據(jù)的類,而 QPixmap 正是為在屏幕上顯示圖像而設(shè)計和優(yōu)化
最后將獲取保存在frame中的數(shù)據(jù)用QPixmap轉(zhuǎn)換就可以顯示了
FrameBuffer frame;
QPixmap pix;
//獲取一幀顯示
v4l2_get_one_frame(&frame);
pix.loadFromData(frame.buf, frame.length);
pix.scaled(displayLabel->width(),displayLabel->height(),Qt::KeepAspectRatio);
displayLabel->setPixmap(pix);
結(jié)束語
本博客的程序已上傳到github中,往期博客有關(guān)Linux C編程的代碼也一并上傳,需要的自行下載
地址:https://github.com/Huge-Hammer/Linux-C-Coding
后續(xù)會繼續(xù)更新,通過程序設(shè)計來實(shí)現(xiàn)拍照截圖、圖像處理、視頻流的存儲和推流等功能,慢慢完善了
我是愛學(xué)習(xí)的諸葛鐵錘,覺得有幫助的話記得點(diǎn)個贊再走吧,wakuwaku!文章來源:http://www.zghlxwxcb.cn/news/detail-401330.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-401330.html
到了這里,關(guān)于V4L2+QT+USB攝像頭實(shí)時顯示視頻(Arm,Linux,window均適用)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!