1. 程式人生 > >第五章:ffmpeg和QT開發播放器之使用QT播放

第五章: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();
}