当前位置: 首页 > news >正文

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

一般的在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();
}

相关文章:

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

一般的在arm嵌入式平台&#xff0c;大多数板子都要硬解码硬件渲染的框架&#xff0c;使用即可。 在x86下比较麻烦了。 优化的思路一共有以下几个方面&#xff0c; 1. 软解码变成硬解码 2. 将YUV转QImage的操作转移到GPU 3. QWidget渲染QImage变成opengGL渲染AVFrame 这三点…...

机器学习入门-读书摘要

先看了《深度学习入门&#xff1a;基于python的理论和实践》这本电子书&#xff0c;早上因为入迷还坐过站了。。 因为里面的反向传播和链式法则特别难懂&#xff0c;又网上搜了相关内容进行进一步理解&#xff0c;参考的以下文章&#xff08;个人认为都讲的都非常好&#xff0…...

前端【技术方案】重构项目

1. 明确重构目标 优化性能 减少页面加载时间降低资源占用 提升代码可维护性 更规范的代码风格更清晰的代码结构更明确的模块设计 扩展功能 为项目添加新功能改进现有功能 2. 评估项目现状 审查代码 全面检查现有代码&#xff0c;找出代码中的问题&#xff0c;如代码冗余、耦合…...

大语言模型简史:从Transformer(2017)到DeepSeek-R1(2025)的进化之路

2025年初&#xff0c;中国推出了具有开创性且高性价比的「大型语言模型」&#xff08;Large Language Model — LLM&#xff09;DeepSeek-R1&#xff0c;引发了AI的巨大变革。本文回顾了LLM的发展历程&#xff0c;起点是2017年革命性的Transformer架构&#xff0c;该架构通过「…...

RabbitMQ服务异步通信

消息队列在使用过程中&#xff0c;面临着很多实际问题需要思考&#xff1a; 1. 消息可靠性 消息从发送&#xff0c;到消费者接收&#xff0c;会经理多个过程&#xff1a; 其中的每一步都可能导致消息丢失&#xff0c;常见的丢失原因包括&#xff1a; 发送时丢失&#xff1a; 生…...

Python常见面试题的详解7

1. 内置的数据结构有哪几种 Python 中有多种内置的数据结构&#xff0c;主要分为以下几种&#xff1a; 1.1 数值类型 整数&#xff08;int&#xff09;&#xff1a;用于表示整数&#xff0c;没有大小限制。例如&#xff1a;1, -5, 100。浮点数&#xff08;float&#xff09;…...

Django REST Framework (DRF) 中用于构建 API 视图类解析

Django REST Framework (DRF) 提供了丰富的视图类&#xff0c;用于构建 API 视图。这些视图类可以分为以下几类&#xff1a; 1. 基础视图类 这些是 DRF 中最基础的视图类&#xff0c;通常用于实现自定义逻辑。 常用类 APIView&#xff1a; 最基本的视图类&#xff0c;所有其…...

Huatuo热更新--安装HybridCLR

1.自行安装unity编辑器 支持2019.4.x、2020.3.x、2021.3.x、2022.3.x 中任一版本。推荐安装2019.4.40、2020.3.26、2021.3.x、2022.3.x版本。 根据你打包的目标平台&#xff0c;安装过程中选择必要模块。如果打包Android或iOS&#xff0c;直接选择相应模块即可。如果你想打包…...

读书笔记 - 修改代码的艺术

读书笔记 - 修改代码的艺术 第 1 章 修改软件第 2 章 带着反馈工作系统变更方式反馈方式遗留代码修改方法 第 3 章 感知和分离伪协作程序模拟对象 第 4 章 接缝模型接缝 第 5 章 工具自动化重构工具单元测试用具 第 6 章 时间紧迫&#xff0c;但必须修改新生方法&#xff08;Sp…...

【Go并发编程】Goroutine 调度器揭秘:从 GMP 模型到 Work Stealing 算法

每天一篇Go语言干货&#xff0c;从核心到百万并发实战&#xff0c;快来关注魔法小匠&#xff0c;一起探索Go语言的无限可能&#xff01; 在 Go 语言中&#xff0c;Goroutine 是一种轻量级的并发执行单元&#xff0c;它使得并发编程变得简单高效。而 Goroutine 的高效调度机制是…...

c# -01新属性-模式匹配、弃元、析构元组和其他类型

文章目录 **学习摘抄分享**模式匹配概述Null 检查类型测试比较离散值关系模型多个输入ObServation列表模式弃元元组和对象析构利用switch的模式进行匹配对于out的方法调用独立弃元析构元组和其他类型元组方法一方法二方法三方法四使用弃元元组的元素使用弃元的用户定义类型解构…...

同步异步日志系统-日志落地模块的实现

功能&#xff1a;将格式化完成后的日志消息字符串&#xff0c;输出到指定的位置 扩展&#xff1a;支持同时将日志落地到不同的位置 位置分类&#xff1a; 1.标准输出 2.指定文件&#xff08;时候进行日志分析&#xff09; 3.滚动文件&#xff08;文件按照时间/大小进行滚动…...

LabVIEW 天然气水合物电声联合探测

天然气水合物被认为是潜在的清洁能源&#xff0c;其储量丰富&#xff0c;预计将在未来能源格局中扮演重要角色。由于其独特的物理化学特性&#xff0c;天然气水合物的探测面临诸多挑战&#xff0c;涉及温度、压力、电学信号、声学信号等多个参数。传统的人工操作方式不仅效率低…...

类型通配符上限

主函数 package typeWildcardTop;import java.util.ArrayList;public class typeWildcardTopTest {/**/public static void main(String[] args) { // test1();test2();}/*测试showList接收ArrayList类型 ArrayList接收各种类型参数创建animals cats mincats集合 传入s…...

嵌入式音视频开发(二)ffmpeg音视频同步

系列文章目录 嵌入式音视频开发&#xff08;零&#xff09;移植ffmpeg及推流测试 嵌入式音视频开发&#xff08;一&#xff09;ffmpeg框架及内核解析 嵌入式音视频开发&#xff08;二&#xff09;ffmpeg音视频同步 嵌入式音视频开发&#xff08;三&#xff09;直播协议及编码器…...

Mongodb数据管理

Mongodb数据管理 1.登录数据库&#xff0c;查看默认的库 [rootdb51~]# mongo> show databases; admin 0.000GB config 0.000GB local 0.000GB> use admin switched to db admin > show tables system.version > admin库&#xff1a;admin 是 MongoDB 的管理…...

Django 创建表 choices的妙用:get_<field_name>_display()

1.定义choices 我在创建表时&#xff0c;对于性别这个字段&#xff0c;定义了choices 选项&#xff0c;1代表男&#xff0c;2代表女 mysql中表的数据如下&#xff0c;里面存储的是1或2 如果我们在网页上展示的时候&#xff0c;想展示“男”或“女”&#xff0c;而不是数字1或2…...

Spring Boot 集成 Kettle

Kettle 简介 Kettle 最初由 Matt Casters 开发&#xff0c;是 Pentaho 数据集成平台的一部分。它提供了一个用户友好的界面和丰富的功能集&#xff0c;使用户能够轻松地设计、执行和监控 ETL 任务。Kettle 通过其强大的功能和灵活性&#xff0c;帮助企业高效地处理大规模数据集…...

自学Java-面向对象高级(final、单例类、枚举类、抽象类、接口)

自学Java-面向对象高级&#xff08;final、单例类、枚举类、抽象类、接口&#xff09; 一、final关键字1、认识final关键字2、final修饰变量的注意3、常量 二、单例类&#xff08;设计模式&#xff09;1、设计模式的概念2、单例设计模式3、单例类有很多形式4、懒汉式单例类5、小…...

Hutool - Cache:简单而强大的缓存实现

目录 1. 缓存简介 2. 引入依赖 3. 常见缓存类型及使用示例 3.1 FIFO 缓存&#xff08;先进先出缓存&#xff09; 3.2 LRU 缓存&#xff08;最近最少使用缓存&#xff09; 3.3 定时缓存 4. 缓存的基本操作 5. 总结 1. 缓存简介 在软件开发中&#xff0c;缓存是一种常用的…...

日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻

在如今就业市场竞争日益激烈的背景下&#xff0c;越来越多的求职者将目光投向了日本及中日双语岗位。但是&#xff0c;一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧&#xff1f;面对生疏的日语交流环境&#xff0c;即便提前恶补了…...

stm32G473的flash模式是单bank还是双bank?

今天突然有人stm32G473的flash模式是单bank还是双bank&#xff1f;由于时间太久&#xff0c;我真忘记了。搜搜发现&#xff0c;还真有人和我一样。见下面的链接&#xff1a;https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...

在rocky linux 9.5上在线安装 docker

前面是指南&#xff0c;后面是日志 sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo dnf install docker-ce docker-ce-cli containerd.io -y docker version sudo systemctl start docker sudo systemctl status docker …...

AI编程--插件对比分析:CodeRider、GitHub Copilot及其他

AI编程插件对比分析&#xff1a;CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展&#xff0c;AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者&#xff0c;分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...

Element Plus 表单(el-form)中关于正整数输入的校验规则

目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入&#xff08;联动&#xff09;2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...

云原生玩法三问:构建自定义开发环境

云原生玩法三问&#xff1a;构建自定义开发环境 引言 临时运维一个古董项目&#xff0c;无文档&#xff0c;无环境&#xff0c;无交接人&#xff0c;俗称三无。 运行设备的环境老&#xff0c;本地环境版本高&#xff0c;ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...

视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)

前言&#xff1a; 最近在做行为检测相关的模型&#xff0c;用的是时空图卷积网络&#xff08;STGCN&#xff09;&#xff0c;但原有kinetic-400数据集数据质量较低&#xff0c;需要进行细粒度的标注&#xff0c;同时粗略搜了下已有开源工具基本都集中于图像分割这块&#xff0c…...

uniapp 集成腾讯云 IM 富媒体消息(地理位置/文件)

UniApp 集成腾讯云 IM 富媒体消息全攻略&#xff08;地理位置/文件&#xff09; 一、功能实现原理 腾讯云 IM 通过 消息扩展机制 支持富媒体类型&#xff0c;核心实现方式&#xff1a; 标准消息类型&#xff1a;直接使用 SDK 内置类型&#xff08;文件、图片等&#xff09;自…...

深入浅出WebGL:在浏览器中解锁3D世界的魔法钥匙

WebGL&#xff1a;在浏览器中解锁3D世界的魔法钥匙 引言&#xff1a;网页的边界正在消失 在数字化浪潮的推动下&#xff0c;网页早已不再是静态信息的展示窗口。如今&#xff0c;我们可以在浏览器中体验逼真的3D游戏、交互式数据可视化、虚拟实验室&#xff0c;甚至沉浸式的V…...

UE5 音效系统

一.音效管理 音乐一般都是WAV,创建一个背景音乐类SoudClass,一个音效类SoundClass。所有的音乐都分为这两个类。再创建一个总音乐类&#xff0c;将上述两个作为它的子类。 接着我们创建一个音乐混合类SoundMix&#xff0c;将上述三个类翻入其中&#xff0c;通过它管理每个音乐…...