基于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


相关推荐

  • python正则表达式匹配字符串相关方法

    python正则表达式匹配字符串相关方法python 正则表达式匹配字符串相关方法一 python 正则表达式相关函数简要介绍 re compile 函数 re match 函数 re search 函数 findall 函数 re finditer 函数其他函数二 python 正则表达式匹配方法简要介绍一 python 正则表达式相关函数简要介绍 re compile 函数 re compile 函数用于编译正则表达式 生成一个正则表达式 Pattern 对象 供 match search 和 findall 这三个函数使用 re compile

    2026年3月17日
    2
  • 听听各位对Ubuntu的UI的看法

    听听各位对Ubuntu的UI的看法

    2021年8月26日
    54
  • mybatispluslog激活码[最新免费获取]

    (mybatispluslog激活码)这是一篇idea技术相关文章,由全栈君为大家提供,主要知识点是关于2021JetBrains全家桶永久激活码的内容https://javaforall.net/100143.htmlIntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,上面是详细链接哦~1STL5S9V8F-eyJsaWNlb…

    2022年3月27日
    226
  • c#-RTF文本编辑器

    c#-RTF文本编辑器

    2022年1月11日
    183
  • 现代语音信号处理笔记 (一)

    现代语音信号处理笔记 (一)本系列笔记对胡航老师的现代语音信号处理这本书的语音处理部分进行总结,包含语音信号处理基础、语音信号分析、语音编码三部分。一开始以为三部分总结到一篇文章里就可以了,但写着写着发现事情并没有那么简单。。。因此还是老老实实的总结吧,扎实的基础最重要。语音信号处理基础语音信号的处理简称语音处理,是用数字信号处理技术对语音信号进行处理的一门学科。语音信号均采用数字方式进行处理,语音信号的数字…

    2022年5月26日
    44
  • java面试题及答案整理(解决方案经理面试题)

    2012年毕业,2016年转行,没有一个体面的工作,机缘巧合之下,来到了大连,Java培训,一个全新的领域,迷茫、困惑、漫无目的的努力,转行真的被歧视,真的不行吗?我命由我不由天,我觉得我行!相信我,只要你足够努力,总有成为架构师,独挡一面的一天。最近参加了一些面试,效果不是很理想,项目介绍只有大框,没有突出重点,没有项目中的具体细节,因为都是看的B站视频,实际工作中都是在做重复的CRUD工作,愁人啊。618买的新书塑料还没拆!视频计划已经执行到第二篇了!熬夜学习,是刻苦奋斗还是自欺欺人?面试

    2022年4月16日
    162

发表回复

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

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