第五章:ffmpeg和QT開發播放器之使用QT播放
寫在前面:
編寫完視訊的編碼轉碼程式之後,就需要將整個程式重新封裝一下,以便於後續的工作,這裡對應視訊課程中的4-1~4-2。前陣子忙著工作上的事情,也就沒什麼進度,想想還是不應該,QT稍微接觸了下,感覺還是很多不會的。1、繪製QT播放介面
雙擊ui檔案,開啟QT的設計介面。
然後在右側的屬性編輯器中的geometry屬性中修改高度和寬度為我們程式碼中設定的800*600
那麼創建出來的視窗就可以用來顯示視訊了,但是顯示視訊我們可以呼叫顯示卡來加速,在這裡我們選擇使用QT自帶的Open GL Widget來顯示。
我們直接在QT設計器的左側找到Open GL Widget,並拖放到ui介面上,然後點選拖放好的模組,在右側屬性編輯器設定好Open GL Widge的X、Y以及寬度高度這四個引數,引數和上面一致。
2、新增圖示
在專案程式碼主目錄中,追加圖示檔案,圖示檔案直接用老師提供的好了,將Resources檔案放入主目錄中。
然後再ui介面上新增按鈕。按鈕的新增在左側工具欄的Buttons可以看到,我們選擇Push Button按鈕。拖放到ui介面上後,在button上右擊,選擇改變樣式表,然後選擇新增資源選項的下按鈕,選擇第二個選項border-image。然後再選擇資源欄中點選那個筆。
在出現的編輯資源中,點選紅色框框,在選擇新增我們剛剛放入工程目錄中的Resources資料夾裡面的圖示。
然後按步驟點ok,最後我們會發現那個botton會變成我們設定的圖示了,通過修改編輯樣式表的內容,可以做的將滑鼠移動到按鈕時,切換圖示。
QPushButton:!hover{border-image: url(:/Xplay/open_normal.png);}//滑鼠沒碰到時候
QPushButton:hover{border-image: url(:/Xplay/open_hot.png);} //滑鼠碰到的時候
同樣再新增一個播放按鈕,然後也和上面設定的一樣,最後在右側物件檢視器中,選擇Qwidget,然後在下方屬性編輯器中,找到WindowIcon選項,選擇整個程式的logo。
之後儲存好qt介面,編譯一下,應該就能夠顯示出我們繪製的qt介面。
3、建立類
將ffmpeg解碼出來的視訊傳遞到QT中播放。使用的方法是過載這個OpenGLWidget。
在QT設計器右側的物件檢視器,單擊右鍵,選擇提升。然後在新建提升類中輸入提升類的名稱,然後按新增,再按提升。
然後回到VS中,建立VideoWidget類,然後還需要繼承QOpenGLWidget這個類。
class VideoWidget :public QOpenGLWidget
{
public:
VideoWidget(QWidget *p = NULL);
virtual ~VideoWidget();
};
同時修改建構函式。
VideoWidget::VideoWidget(QWidget *p) :QOpenGLWidget(p)
{
}
4、繪製函式的構建
void paintEvent(QPaintEvent *e); //當視窗發生繪製的時候,呼叫這個函式
void timerEvent(QTimerEvent *e); //定時器重新整理
我們需要通過OpenGL,toRGB之後的值來給到paintEvent裡面。
paintEvent函式中首先會用到QPainter painter,所以要先包含標頭檔案#include <QPainter>
使用painter.begin開始繪製。painter.end是結束繪製,也就能顯示影象。在兩者之間就需要做影象的顯示出來
painter.drawImage來繪製圖像,傳入顯示的起始位置,和顯示影象資料image。但是image的空間還未被分配,所以還需要new一個空間給image存放影象資料的。
使用QImage函式建立image的空間。
5、視訊的解碼和顯示
先開啟視訊檔案,這時候就可以引用之前建立的XFFmpeg類裡面的實現函式。
在VideoWidget類中的建構函式中開啟XFFmpeg::Get()->Open("my.mp4");
之後在paintEvent函式中呼叫之前XFFmpeg中的開啟視訊函式
1)AVPacket pkt = XFFmpeg::Get()->Read(); //讀取視訊幀
2)AVFrame *yuv = XFFmpeg::Get()->Decode(&pkt); //解碼
3)XFFmpeg::Get()->ToRGB(yuv, (char*)image->bits(), width(), height());//轉碼
6、重新整理螢幕
視訊播放的時候還需要對它進行重新整理,那麼就需要用到timerEvent函式,由於在測試階段,所以就簡單的編寫這個重新整理函式。
通過在VideoWidget類中的建構函式中設定定時器的間隔時間,然後再timeEvent函式中呼叫update函式進行重新整理。
main.cpp
#include "xplay.h"
#include <QtWidgets/QApplication>
#include "XFFmpeg.h"
//static double r2d(AVRational r)
//{
// return r.num == 0 || r.den == 0 ? 0. : (double)r.num / (double)r.den;
//}
#define OUT_WIDTH 800
#define OUT_HEIGHT 600
int main(int argc, char *argv[])
{
char *rgb = new char[OUT_WIDTH*OUT_HEIGHT * 4]; //影象的資料RGBA
{
printf("open success!\n");
}
else
{
printf("open failed!%s", XFFmpeg::Get()->GetError().c_str());
getchar();
return -1;
}
while (1)
{
if (pkt.size == 0)
break;
printf("pts=%lld\n", pkt.pts);
if (pkt.stream_index != XFFmpeg::Get()->videoStream)
{
av_packet_unref(&pkt); //如果不是視訊格式的 就釋放掉
continue;
}
AVFrame *yuv = XFFmpeg::Get()->Decode(&pkt); //解碼視訊
if (yuv)
{
printf("[D]");
XFFmpeg::Get()->ToRGB(yuv, rgb, OUT_WIDTH, OUT_HEIGHT);
}
av_packet_unref(&pkt);
}
//AVFrame *yuv = av_frame_alloc(); //分配的視訊幀
QApplication a(argc, argv);
Xplay w;
w.show();
return a.exec();
}