基于Qt播放器的实现详解(支持Rgb,YUV格式)
音视频开发老舅 人气:0色度空间转换
YUV颜色模型其实常用于视频传输和图像压缩。由于人类的眼睛,对亮度的敏感度远超过对色彩的敏感度,所以视频传输过程中,为了减小带宽,通常将色彩分量 UV的比例减小,以达到降低带宽的目的。这就出现了YUV4:4:4、YUV4:2:2、YUV4:1:1等格式。
RGB32使用32位来表示一个像素,RGB分量各用去8位,剩下的8位用作Alpha通道或者不用。(ARGB32就是带Alpha通道的RGB32。)注意在内存中RGB各分量的排列顺序为:BGRA BGRA BGRA…。通常可以使用RGB32数据结构来操作一个像素,它的定义为:
typedef struct RGB32 { BYTE rgbBlue; // 蓝色分量 BYTE rgbGreen; // 绿色分量 BYTE rgbRed; // 红色分量 BYTE rgbReserved; // 保留字节(用作Alpha通道或忽略) } RGB32;
YUV转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、绘图显示函数 打开目录函数
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); //画出图像 } /*接收信号函数传递来过的图像,并准备执行绘画函数*/ void MainWindow::slotGetOneFrame(QImage img) { mImage = img; update(); //调用update将执行 paintEvent函数 } /*打开目录按键的槽函数*/ 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、按开始播放的槽函数
void MainWindow::on_pushButton_display_clicked() { /*提取三个文本框的内容*/ QString filePath = ui->lineEdit_filepath->text(); int width = ui->lineEdit_2_width->text().toInt();//toInt()表示将类型转化成int int height = ui->lineEdit_height->text().toInt(); //视频播放帧数设置 if(ui->fpsBox->currentIndex()==0)//如果下拉框中的数值是25fps mThread->Setfps_25(); else//否则 mThread->Setfps_30(); maxValue=filenumber();//滑动条最大值获取 //设定滑条的范围,确保滑条的每一步为一帧 ui->horizontalSlider->setRange(0,maxValue-1);//设定滑动条的范围 mThread->startPlay(filePath,width,height);//启动线程显示播放 }
3、启动线程 进行播放
void TransCodeThread::startPlay(QString infile,int width,int height) { mFilePath = infile; mWidth = width; mHeight = height; start();//启动线程执行run() } /*在独立线程中对视频进行解码,并通过信号函数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为一个结构体 32位 BYTE *rgbBuffer = (BYTE *)malloc(rgbSize); //调试显示 qDebug()<<file_size; qDebug()<<maxValue; int ReadedSize = 0;//已读文件大小初始化 for(int i=0;; i++) { if (feof(fp_yuv_rgb))//文件读取结束 { //qDebug()<<m_i; break; } int readedsize; if(isMoved)//按下滑块,则修改读取指针的位置 { //获取文件指针的位置(滑条被分为与帧数同等分) 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);//转换 } //把这个RGB数据 用QImage加载 QImage tmpImg((uchar *)rgbBuffer,width,height,QImage::Format_RGB32); QImage image = tmpImg.copy(); //把图像复制一份 传递给界面显示 if(fps == 25) { msleep(20); } else if(fps == 30) { msleep(10); } emit sig_GetOneFrame(image); //发送信号 //更新已读取文件大小,更改滑块位置 ReadedSize += readedsize; if(!isMoved) emit moveSlider(((double)ReadedSize/file_size)*maxValue);//发送进度条移动信号 已读的比例 int time_Diff = time.elapsed();//消逝的时间 float f = time_Diff/1000.0;//秒转换为毫秒 float TotalTime = 0 ; TotalTime += f;//播放的总时间 QString TT = QString("%1").arg(TotalTime); qDebug() << TT; } //释放内存 free(yuvBuffer); free(rgbBuffer); }
4、格式转换函数
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、实现进度条与暂停的各类函数
//获取视频帧数 int MainWindow::filenumber() { /*提取三个文本框的内容*/ QString filePath = ui->lineEdit_filepath->text(); int width = ui->lineEdit_2_width->text().toInt();//toInt()表示将类型转化成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); /**计算帧数 判断文件类型**/ 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; } //改变滑块位置的槽函数 void MainWindow::ChangeSliderPosition(int position) { ui->horizontalSlider->setValue(position); } //滑块按下槽函数 void MainWindow::on_horizontalSlider_sliderPressed() { //qDebug()<<"anxia "; mThread->isMoved = true; } //滑块移动槽函数 实时获取滑块的位置 void MainWindow::on_horizontalSlider_sliderMoved(int position) { mThread->SliderPosition = position; } // 滑块松开槽函数 void MainWindow::on_horizontalSlider_sliderReleased() { mThread->isMoved = false; } //暂停按钮槽函数 void MainWindow::on_pushButton_pause_clicked() { mThread->SliderPosition = ui->horizontalSlider->value(); mThread->isMoved = !mThread->isMoved; }
6、各类槽函数的连接
/*构造函数*/ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this);//创建ui页面 mThread = new TransCodeThread;//创建类对象 /*槽与信号链接*/ connect(mThread,SIGNAL(sig_GetOneFrame(QImage)),this,SLOT(slotGetOneFrame(QImage)));//进行图像传递和接收 connect(mThread,&TransCodeThread::moveSlider, this, &MainWindow::ChangeSliderPosition); } /*析构函数*/ MainWindow::~MainWindow() { delete ui;//删除ui界面 }
加载全部内容