大家好,又见面了,我是你们的朋友全栈君。
基于QT播放器的实现(一)Rgb、YUV格式
色度空间转换
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界面
}
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/137861.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...