基于QT播放器的实现(一)Rgb、YUV格式(附带代码)

基于QT播放器的实现(一)Rgb、YUV格式(附带代码)基于QT播放器的实现(一)Rgb、YUV格式色度空间转换YUV转RGB的公式对本地RGB32视频图像的播放色度空间转换YUV颜色模型其实常用于视频传输和图像压缩。由于人类的眼睛,对亮度的敏感度远超过对色彩的敏感度,所以视频传输过程中,为了减小带宽,通常将色彩分量UV的比例减小,以达到降低带宽的目的。这就出现了YUV4:4:4、YUV4:2:2、YUV4:1:1等格式。RGB32使用32位来…

大家好,又见面了,我是你们的朋友全栈君。

基于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界面
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/137861.html原文链接:https://javaforall.net

(0)
上一篇 2022年6月1日 上午11:36
下一篇 2022年6月1日 上午11:36


相关推荐

  • Android中ListView下拉刷新的实现

    Android中ListView下拉刷新的实现ListView 中的下拉刷新是非常常见的 也是经常使用的 看到有很多同学想要 那我就整理一下 供大家参考 那我就不解释 直接上代码了 这里需要自己重写一下 ListView 重写代码如下 packagenet loonggg listview importjava util Date importandroi content Context importandroi u

    2026年3月18日
    1
  • 如何在 Mac 上卸载 Java?

    如何在 Mac 上卸载 Java?一 java 官网上的步骤在 终端 窗口中 复制和粘贴命令 sudorm fr Library Internet Plug Ins JavaAppletPl pluginsudorm fr Library PreferencesP JavaControlP prefPanesudo fr Library Application Support Oracle Java 但是依据官网步骤 三步执行完 执行 java version 发现本地 java

    2026年3月16日
    2
  • 智谱GLM-Image实战:电商海报生成案例

    智谱GLM-Image实战:电商海报生成案例

    2026年3月12日
    2
  • dede后台登陆后一片空白的解决办法汇总

    dede后台登陆后一片空白的解决办法汇总

    2021年9月19日
    41
  • ba无标度网络python_python绘制BA无标度网络

    ba无标度网络python_python绘制BA无标度网络Copyright c 2017 东北大学软件学院学生 Allrightsres 文件名称 a py 作者 孔云 问题描述 问题分析 代码如下 importnetwor 导入建网络模型包 命名 neimportmatp pyplotasmp 导入科学绘图包 命名 mp BAscale freedegreene

    2026年1月28日
    2
  • break和continue的区别

    break和continue的区别1.break用break语句可以使流程跳出switch语句体,也可以用break语句在循环结构终止本层循环体,从而提前结束本层循环。使用说明:(1)只能在循环体内和switch语句体内使用break;(2)当break出现在循环体中的switch语句体内时,起作用只是跳出该switch语句体,并不能终止循环体的执行。若想强行终止循环体的执行,可以在循环体中,但并不在switch语…

    2022年6月14日
    64

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号