基于Qt播放器的實(shí)現(xiàn)詳解(支持Rgb,YUV格式)
色度空間轉(zhuǎn)換
YUV顏色模型其實(shí)常用于視頻傳輸和圖像壓縮。由于人類的眼睛,對亮度的敏感度遠(yuǎn)超過對色彩的敏感度,所以視頻傳輸過程中,為了減小帶寬,通常將色彩分量 UV的比例減小,以達(dá)到降低帶寬的目的。這就出現(xiàn)了YUV4:4:4、YUV4:2:2、YUV4:1:1等格式。
RGB32使用32位來表示一個(gè)像素,RGB分量各用去8位,剩下的8位用作Alpha通道或者不用。(ARGB32就是帶Alpha通道的RGB32。)注意在內(nèi)存中RGB各分量的排列順序?yàn)椋築GRA BGRA BGRA…。通??梢允褂肦GB32數(shù)據(jù)結(jié)構(gòu)來操作一個(gè)像素,它的定義為:
typedef struct RGB32 { BYTE rgbBlue; // 藍(lán)色分量 BYTE rgbGreen; // 綠色分量 BYTE rgbRed; // 紅色分量 BYTE rgbReserved; // 保留字節(jié)(用作Alpha通道或忽略) } RGB32;
YUV轉(zhuǎn)RGB的公式
R = Y + 1.402 * (V-128) G = Y – 0.34413 * (U-128) – 0.71414*(V-128) B= Y + 1.772*(U-128)
對本地RGB32視頻圖像的播放
1、繪圖顯示函數(shù) 打開目錄函數(shù)
void MainWindow ::paintEvent(QPaintEvent *) { QPainter painter(this); painter.setBrush(Qt::black); QRect rect = ui->widget_video->geometry(); painter.drawRect(rect); if (mImage.size().width() <= 0) return; //將圖像按比例縮放成和窗口一樣大小 QImage img = mImage.scaled(ui->widget_video->size(),Qt::KeepAspectRatio); int x = ui->widget_video->width() - img.width(); int y = ui->widget_video->height() - img.height(); x /= 2; y /= 2; x += ui->widget_video->x(); y += ui->widget_video->y(); painter.drawImage(QPoint(x,y),img); //畫出圖像 } /*接收信號函數(shù)傳遞來過的圖像,并準(zhǔn)備執(zhí)行繪畫函數(shù)*/ void MainWindow::slotGetOneFrame(QImage img) { mImage = img; update(); //調(diào)用update將執(zhí)行 paintEvent函數(shù) } /*打開目錄按鍵的槽函數(shù)*/ void MainWindow::on_pushButton_open_clicked() { QString s = QFileDialog::getOpenFileName(this, "", "視頻絕對路徑","rgb flie(*.rgb);;yuv file(*.yuv)"); if (!s.isEmpty()) { s.replace("/","\\"); ui->lineEdit_filepath->setText(s);//將字符串寫入lineEdit_filepath文本框 } }
2、按開始播放的槽函數(shù)
void MainWindow::on_pushButton_display_clicked() { /*提取三個(gè)文本框的內(nèi)容*/ QString filePath = ui->lineEdit_filepath->text(); int width = ui->lineEdit_2_width->text().toInt();//toInt()表示將類型轉(zhuǎn)化成int int height = ui->lineEdit_height->text().toInt(); //視頻播放幀數(shù)設(shè)置 if(ui->fpsBox->currentIndex()==0)//如果下拉框中的數(shù)值是25fps mThread->Setfps_25(); else//否則 mThread->Setfps_30(); maxValue=filenumber();//滑動條最大值獲取 //設(shè)定滑條的范圍,確?;瑮l的每一步為一幀 ui->horizontalSlider->setRange(0,maxValue-1);//設(shè)定滑動條的范圍 mThread->startPlay(filePath,width,height);//啟動線程顯示播放 }
3、啟動線程 進(jìn)行播放
void TransCodeThread::startPlay(QString infile,int width,int height) { mFilePath = infile; mWidth = width; mHeight = height; start();//啟動線程執(zhí)行run() } /*在獨(dú)立線程中對視頻進(jìn)行解碼,并通過信號函數(shù)sig_GetOneFrame傳送每一幀圖像*/ void TransCodeThread::run() { time.start(); char filePath[1024]={0}; strcpy(filePath,mFilePath.toUtf8().data()); FILE *fp_yuv_rgb = fopen(filePath,"rb"); //打開視頻文件 int width = mWidth; int height = mHeight; if (fp_yuv_rgb == NULL) return; /**判斷文件類型**/ QString fileright = mFilePath.right(3); if(fileright =="rgb") { m_filetype=1; } else if(fileright =="yuv") { m_filetype=0; } //獲取文件的大小 fseek( fp_yuv_rgb,0,SEEK_END ); //把文件指針定位到文件尾 int file_size=ftell( fp_yuv_rgb );//獲取文件的大小 fseek( fp_yuv_rgb,0,SEEK_SET );//把文件指針指向開頭 int yuvSize = width * height *3/2 ; BYTE *yuvBuffer = (BYTE *)malloc(yuvSize); int rgbSize = width *height *sizeof(RGB32);//RGB32為一個(gè)結(jié)構(gòu)體 32位 BYTE *rgbBuffer = (BYTE *)malloc(rgbSize); //調(diào)試顯示 qDebug()<<file_size; qDebug()<<maxValue; int ReadedSize = 0;//已讀文件大小初始化 for(int i=0;; i++) { if (feof(fp_yuv_rgb))//文件讀取結(jié)束 { //qDebug()<<m_i; break; } int readedsize; if(isMoved)//按下滑塊,則修改讀取指針的位置 { //獲取文件指針的位置(滑條被分為與幀數(shù)同等分) int pointPosition = file_size * (double)SliderPosition/maxValue; fseek( fp_yuv_rgb, pointPosition , SEEK_SET ); ReadedSize = pointPosition ; } if(m_filetype==1)//rgb { readedsize= fread(rgbBuffer,1,rgbSize,fp_yuv_rgb);//注釋,YUV2RGB // qDebug()<<readedsize; } else//yuv { readedsize= fread(yuvBuffer,1,yuvSize,fp_yuv_rgb);//讀取yuv文件 Yuv420p2Rgb32(yuvBuffer, rgbBuffer, width, height);//轉(zhuǎn)換 } //把這個(gè)RGB數(shù)據(jù) 用QImage加載 QImage tmpImg((uchar *)rgbBuffer,width,height,QImage::Format_RGB32); QImage image = tmpImg.copy(); //把圖像復(fù)制一份 傳遞給界面顯示 if(fps == 25) { msleep(20); } else if(fps == 30) { msleep(10); } emit sig_GetOneFrame(image); //發(fā)送信號 //更新已讀取文件大小,更改滑塊位置 ReadedSize += readedsize; if(!isMoved) emit moveSlider(((double)ReadedSize/file_size)*maxValue);//發(fā)送進(jìn)度條移動信號 已讀的比例 int time_Diff = time.elapsed();//消逝的時(shí)間 float f = time_Diff/1000.0;//秒轉(zhuǎn)換為毫秒 float TotalTime = 0 ; TotalTime += f;//播放的總時(shí)間 QString TT = QString("%1").arg(TotalTime); qDebug() << TT; } //釋放內(nèi)存 free(yuvBuffer); free(rgbBuffer); }
4、格式轉(zhuǎn)換函數(shù)
void TransCodeThread::Yuv420p2Rgb32(const BYTE *yuvBuffer_in,const BYTE *rgbBuffer_out,int width,int height) { BYTE *yuvBuffer = (BYTE *)yuvBuffer_in; RGB32 *rgb32Buffer = (RGB32 *)rgbBuffer_out; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { int index = y * width + x; int indexY = y * width + x; int indexU = width * height + y / 2 * width / 2 + x / 2; int indexV = width * height + width * height / 4 + y / 2 * width / 2 + x / 2; BYTE Y = yuvBuffer[indexY]; BYTE U = yuvBuffer[indexU]; BYTE V = yuvBuffer[indexV]; RGB32 *rgbNode = &rgb32Buffer[index]; rgbNode->rgbRed = Y + 1.402 * (V-128); rgbNode->rgbGreen = Y - 0.34413 * (U-128) - 0.71414*(V-128); rgbNode->rgbBlue = Y + 1.772*(U-128); } } }
5、實(shí)現(xiàn)進(jìn)度條與暫停的各類函數(shù)
//獲取視頻幀數(shù) int MainWindow::filenumber() { /*提取三個(gè)文本框的內(nèi)容*/ QString filePath = ui->lineEdit_filepath->text(); int width = ui->lineEdit_2_width->text().toInt();//toInt()表示將類型轉(zhuǎn)化成int int height = ui->lineEdit_height->text().toInt(); char curfilePath[1024]={0}; strcpy(curfilePath,filePath.toUtf8().data()); FILE *fp_yuv_rgb = fopen(curfilePath,"rb"); //文件指針移到文件尾 fseek( fp_yuv_rgb,0,SEEK_END ); //獲取文件的大小 int file_size=ftell( fp_yuv_rgb ); int yuvSize = width * height * 3/2 ; int rgbSize = width *height *sizeof(RGB32); /**計(jì)算幀數(shù) 判斷文件類型**/ QString fileright = filePath.right(3); static int number; if(fileright =="rgb") { number = file_size/rgbSize; } else if(fileright =="yuv") { number = file_size/yuvSize; } return number; } //改變滑塊位置的槽函數(shù) void MainWindow::ChangeSliderPosition(int position) { ui->horizontalSlider->setValue(position); } //滑塊按下槽函數(shù) void MainWindow::on_horizontalSlider_sliderPressed() { //qDebug()<<"anxia "; mThread->isMoved = true; } //滑塊移動槽函數(shù) 實(shí)時(shí)獲取滑塊的位置 void MainWindow::on_horizontalSlider_sliderMoved(int position) { mThread->SliderPosition = position; } // 滑塊松開槽函數(shù) void MainWindow::on_horizontalSlider_sliderReleased() { mThread->isMoved = false; } //暫停按鈕槽函數(shù) void MainWindow::on_pushButton_pause_clicked() { mThread->SliderPosition = ui->horizontalSlider->value(); mThread->isMoved = !mThread->isMoved; }
6、各類槽函數(shù)的連接
/*構(gòu)造函數(shù)*/ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this);//創(chuàng)建ui頁面 mThread = new TransCodeThread;//創(chuàng)建類對象 /*槽與信號鏈接*/ connect(mThread,SIGNAL(sig_GetOneFrame(QImage)),this,SLOT(slotGetOneFrame(QImage)));//進(jìn)行圖像傳遞和接收 connect(mThread,&TransCodeThread::moveSlider, this, &MainWindow::ChangeSliderPosition); } /*析構(gòu)函數(shù)*/ MainWindow::~MainWindow() { delete ui;//刪除ui界面 }
以上就是基于Qt播放器的實(shí)現(xiàn)詳解(支持Rgb,YUV格式)的詳細(xì)內(nèi)容,更多關(guān)于Qt播放器的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C++?LeetCode0547題解省份數(shù)量圖的連通分量
這篇文章主要為大家介紹了C++?LeetCode0547題解省份數(shù)量圖的連通分量示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12C++圖形界面開發(fā)Qt教程:嵌套圓環(huán)示例
這篇文章主要介紹了C++實(shí)現(xiàn)圖形界面開發(fā)Qt教程,涉及坐標(biāo)函數(shù)的應(yīng)用及圖形界面程序設(shè)計(jì),需要的朋友可以參考下,希望能給你帶來幫助2021-08-08C語言實(shí)現(xiàn)設(shè)備管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C語言實(shí)現(xiàn)設(shè)備管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-06-06C語言不使用strcpy函數(shù)如何實(shí)現(xiàn)字符串復(fù)制功能
這篇文章主要給大家介紹了關(guān)于C語言不使用strcpy函數(shù)如何實(shí)現(xiàn)字符串復(fù)制功能的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-02-02C語言實(shí)現(xiàn)大整數(shù)加減運(yùn)算詳解
大數(shù)運(yùn)算,顧名思義,就是很大的數(shù)值的數(shù)進(jìn)行一系列的運(yùn)算。本文通過實(shí)例演示如何進(jìn)行C語言中的大整數(shù)加減運(yùn)算,有需要的可以參考借鑒。2016-08-08