x86平台基于Qt+opengl优化ffmpeg软解码1080P视频渲染效率

devtools/2025/2/22 18:47:02/

一般的在arm嵌入式平台,大多数板子都要硬解码硬件渲染的框架,使用即可。

在x86下比较麻烦了。

优化的思路一共有以下几个方面,

1. 软解码变成硬解码

2. 将YUV转QImage的操作转移到GPU

3. QWidget渲染QImage变成opengGL渲染AVFrame

这三点优化来说2与3是优化的效率是非常显著的。

1的优化效果往往需要将硬解码的数据copy至CPU再使用2-3的优化。

这样一来,解码效率提升了,但是数据copy时候CPU使用率会上升。如果两者抵消后CPU使用率还是上升那就得不偿失。如果能实现硬解码的数据不经过CPU直接打到GPU进行渲染,那就是最完美的方案。这个在x86下需要研究opengl渲染硬件类型数据,难度未知,理论如果用的是比较新的框架,资料会多一些。

本文主要是基于2-3的优化,在qt5.1下面基于opengl实现了这个方案,在多路1080P的使用场景下CPU使用率下降非常明显。

#include "opengl_yuv_shader.h"
#include <QDebug>
#include <iostream>
#include <GL/gl.h>
#include <QGLShader>opengl_yuv_shader::opengl_yuv_shader(QWidget *parent) : QGLWidget(parent), useVBO(false),vboId(0),yuv420p_shaderProgram(0),yuvj422p_shaderProgram(0)
{textures[0]=0;textures[1]=0;textures[2]=0;av_frame = nullptr;connect(this,SIGNAL(render_frame()),this,SLOT(slot_render_frame()),Qt::QueuedConnection);//5 lu 60% cpu
}opengl_yuv_shader::~opengl_yuv_shader() {makeCurrent();glDeleteTextures(3, textures);if (yuv420p_shaderProgram) {glDeleteProgram(yuv420p_shaderProgram);}if (yuvj422p_shaderProgram) {glDeleteProgram(yuvj422p_shaderProgram);}doneCurrent();
}void opengl_yuv_shader::initTextures()
{glGenTextures(3, textures);for (int i = 0; i < 3; ++i) {glBindTexture(GL_TEXTURE_2D, textures[i]);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);glBindTexture(GL_TEXTURE_2D, 0);}
}void opengl_yuv_shader::initShaders()
{QGLShader *vshader = new QGLShader(QGLShader::Vertex, this);const char *vsrc ="attribute vec4 vertex;\n""attribute vec2 texCoord;\n""varying vec2 texc;\n""void main(void)\n""{\n""    gl_Position = vertex;\n""    texc = texCoord;\n""}\n";vshader->compileSourceCode(vsrc);//编译顶点着色器代码QGLShader *fshader = new QGLShader(QGLShader::Fragment, this);//vec4(1.0,0,0,1.0);const char *fsrc ="uniform sampler2D texture;\n""varying vec2 texc;\n""void main(void)\n""{\n""    gl_FragColor =  texture2D(texture,texc);\n""}\n";//本方案的核心点在于这个片段着色器,在GPU上完成YUV转RGB的浮点运算。//由于测试的摄像机是基于YUV J420P转换的所以算法上与YUV420P略有差别。// 实际使用需要根据具体的AVFrame格式,进行转换。可初始化多个SHADER管理器、// 渲染时,根据像素格式选择shader渲染const char* fragmentShaderSource = R"(varying vec2 texc;uniform sampler2D textureY;uniform sampler2D textureU;uniform sampler2D textureV;void main(){float y = texture2D(textureY, texc).r;float u = texture2D(textureU, texc).r;float v = texture2D(textureV, texc).r;float r = y + 1.402 * (v - 0.5);float g = y - 0.344136 * (u - 0.5) - 0.714136 * (v - 0.5);float b = y + 1.772 * (u - 0.5);// 确保 RGB 值在 0-1 范围内r = clamp(r, 0.0, 1.0);g = clamp(g, 0.0, 1.0);b = clamp(b, 0.0, 1.0);gl_FragColor = vec4(r, g, b, 1.0);})";fshader->compileSourceCode(fragmentShaderSource); //编译纹理着色器代码program.addShader(vshader);//添加顶点着色器program.addShader(fshader);//添加纹理碎片着色器program.bindAttributeLocation("vertex", 0);//绑定顶点属性位置program.bindAttributeLocation("texCoord", 1);//绑定纹理属性位置// 链接着色器管道if (!program.link()){close();qDebug()<<"program.link() error"<<endl;}// 绑定着色器管道if (!program.bind()){close();qDebug()<<"program.bind() error"<<endl;}
}void opengl_yuv_shader::initializeGL()
{initializeOpenGLFunctions();glClearColor(0.0f, 0.0f, 0.0f, 1.0f);glEnable(GL_TEXTURE_2D);initTextures();initShaders();
//    glDisable(GL_DEPTH_TEST);
//    glDisable(GL_CULL_FACE);
//    glDisable(GL_BLEND);const GLubyte* renderer = glGetString(GL_RENDERER);const GLubyte* vendor = glGetString(GL_VENDOR);const GLubyte* version = glGetString(GL_VERSION);const GLubyte* glslVersion = glGetString(GL_SHADING_LANGUAGE_VERSION);std::cout << "Renderer: " << renderer<<std::endl;std::cout << "Vendor: " << vendor<<std::endl;std::cout << "OpenGL Version: " << version<<std::endl;std::cout << "GLSL Version: " << glslVersion<<std::endl;texCoords.append(QVector2D(0, 1)); //左上texCoords.append(QVector2D(1, 1)); //右上texCoords.append(QVector2D(0, 0)); //左下texCoords.append(QVector2D(1, 0)); //右下//顶点坐标vertices.append(QVector3D(-1, -1, 1));//左下vertices.append(QVector3D(1, -1, 1)); //右下vertices.append(QVector3D(-1, 1, 1)); //左上vertices.append(QVector3D(1, 1, 1));  //右上
}void opengl_yuv_shader::resizeGL(int w, int h)
{qDebug() << "Oopengl_yuv_shader::resizeGL w=" << w<<endl;glViewport(0, 0, w, h);glMatrixMode(GL_PROJECTION);glLoadIdentity();glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);glMatrixMode(GL_MODELVIEW);
}void opengl_yuv_shader::paintGL()
{glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);render_lock.lock();if (!av_frame) {render_lock.unlock();return;}glEnable(GL_TEXTURE_2D);program.enableAttributeArray(0);//启用顶点属性0,也就是渲染平面的顶点坐标program.enableAttributeArray(1);//启用顶点属性1,也就是渲染平面的纹理坐标//纹理坐标的和顶点的对应关系完成渲染program.setAttributeArray(0, vertices.constData() );program.setAttributeArray(1, texCoords.constData()  );if(av_frame->format == AV_PIX_FMT_YUV420P || av_frame->format == AV_PIX_FMT_YUVJ420P  ){if (av_frame&&av_frame->data[0]) {glActiveTexture(GL_TEXTURE0);glBindTexture(GL_TEXTURE_2D, textures[0]);glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, av_frame->width, av_frame->height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, av_frame->data[0]);glActiveTexture(GL_TEXTURE1);glBindTexture(GL_TEXTURE_2D, textures[1]);glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, av_frame->width/2, av_frame->height/2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, av_frame->data[1]);glActiveTexture(GL_TEXTURE2);glBindTexture(GL_TEXTURE_2D, textures[2]);glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, av_frame->width/2, av_frame->height/2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, av_frame->data[2]);program.setUniformValue("textureY", 0);program.setUniformValue("textureU", 1);program.setUniformValue("textureV", 2);}}render_lock.unlock();// 绘制glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);}void opengl_yuv_shader::set_yuv_frame(AVFrame *frame)
{// 1. 如果 av_frame 已经存在,先释放它render_lock.lock();if (av_frame) {av_frame_free(&av_frame);av_frame = nullptr;}// 2. 深拷贝 AVFrameav_frame = av_frame_clone(frame);if (!av_frame) {av_log(NULL, AV_LOG_ERROR, "Failed to clone frame\n");render_lock.unlock();return;}render_lock.unlock();emit render_frame();}void opengl_yuv_shader::slot_render_frame()
{update();
}


http://www.ppmy.cn/devtools/160999.html

相关文章

影视大数据分析新范式:亮数据动态代理驱动的实时数据采集方案

一、项目背景与挑战 在数据驱动决策的时代&#xff0c;影视数据分析对内容平台至关重要。但豆瓣等平台设有&#xff1a; 高频请求IP封禁机制User-Agent指纹检测请求频率阈值控制验证码验证系统 传统爬虫方案面临&#xff1a; 单一IP存活时间<5分钟采集成功率<30%数据更新…

在UBUNTU下搭建Deepseek

在UBUNTU下搭建Deepseek 一、安装UBUNTU 这个就不多说了&#xff0c;无外乎下载UBUNTU的iso&#xff0c;然后用UltraIso制作U盘&#xff0c;然后重启设置启动盘&#xff0c;安装… 二、安装Ollama curl -sSfL https://ollama.com/install.sh | sh这里可能需要你先安装curl工…

Repeated Sequence

记suma[1]a[2]a[3]...a[n]。 该序列以a[1]&#xff0c;a[2]&#xff0c;a[3]....a[n]为循环节&#xff0c;明显的&#xff0c;问题可转化为:s%sum是否为该序列的某个连续子序列和。 断环为链。将a复制一份。 枚举a[i]为左端点的所有区间的和。再查找s是否存在。二分O&#x…

开题报告——基于Spring Boot的社区居民健康管理平台的设计与实现

关于本科毕业设计(论文)开题报告的规定 为切实做好本科毕业设计(论文)的开题报告工作,保证论文质量,特作如下规定: 一、开题报告是本科毕业设计(论文)的必经过程,所有本科生在写作毕业设计(论文)之前都必须作开题报告。 二、开题报告主要检验学生对专业知识的驾…

使用WebStorm开发Vue3项目

记录一下使用WebStorm开发Vu3项目时的配置 现在WebStorm可以个人免费使用啦&#xff01;?? 基本配置 打包工具&#xff1a;Vite 前端框架&#xff1a;ElementPlus 开发语言&#xff1a;Vue3、TypeScript、Sass 代码检查&#xff1a;ESLint、Prettier IDE&#xff1a;WebSt…

鸿蒙初学者学习手册(HarmonyOSNext_API14)_自定义动画API(@ohos.animator (动画) )

前言 在纯血鸿蒙中最具有用户特色的效果就是自定义的动画效果。在纯血鸿蒙中有多种定义方式&#xff0c;但是今天介绍的是ApI中的自定义动画。 注意: 动画本身具有生命周期&#xff0c;但是不支持在UIAbility的文件使用&#xff0c;简单而言就是不允许在UIAbility生命周期中…

上证50ETF期权交割日是每月几号?

财顺小编上证50ETF期权的交割日固定为每月第四个星期三&#xff0c;若遇法定节假日则顺延至下一交易日。例如&#xff0c;2023年1月因春节假期&#xff0c;原定1月25日的交割日顺延至1月30日。 上证50ETF期权交割日是每月几号&#xff1f; 交割日与行权日&#xff1a; 交割日…

模拟实现分布式文件存储

Q1:如何解决海量数据存的下的问题 传统做法是在单机存储。但是随着数据变多&#xff0c;会遇到存储瓶颈。 单机纵向扩展&#xff1a;内存不够加内存&#xff0c;磁盘不够加磁盘。有上限限制&#xff0c;不能无限制加下去。 多机横向扩展&#xff1a;采用多台机器存储&#x…