#include "opengl_video_renderer.h" // #include "../base/logger.h" #include #include #include #include #include #include #include extern "C" { #include #include } namespace av { namespace player { // 顶点着色器源码 static const char* vertexShaderSource = R"( #version 330 core layout(location = 0) in vec2 position; layout(location = 1) in vec2 texCoord; out vec2 TexCoord; uniform mat4 projection; void main() { gl_Position = projection * vec4(position, 0.0, 1.0); TexCoord = texCoord; } )"; // 片段着色器源码 (YUV420P) static const char* fragmentShaderSource = R"( #version 330 core in vec2 TexCoord; out vec4 FragColor; uniform sampler2D yTexture; uniform sampler2D uTexture; uniform sampler2D vTexture; void main() { float y = texture(yTexture, TexCoord).r; float u = texture(uTexture, TexCoord).r - 0.5; float v = texture(vTexture, TexCoord).r - 0.5; // YUV to RGB conversion float r = y + 1.402 * v; float g = y - 0.344 * u - 0.714 * v; float b = y + 1.772 * u; FragColor = vec4(r, g, b, 1.0); } )"; OpenGLVideoRenderer::OpenGLVideoRenderer(QWidget* parent) : QOpenGLWidget(parent) , m_videoWidth(0) , m_videoHeight(0) , m_inputFormat(AV_PIX_FMT_NONE) , m_swsContext(nullptr) , m_yuvBuffer(nullptr) , m_yuvBufferSize(0) , m_backgroundColor(Qt::black) , m_keepAspectRatio(true) , m_renderQuality(1.0f) , m_vSyncEnabled(true) , m_initialized(false) , m_glInitialized(false) , m_hasFrame(false) , m_updateTimer(new QTimer(this)) { // 设置OpenGL格式 QSurfaceFormat format; format.setVersion(3, 3); format.setProfile(QSurfaceFormat::CoreProfile); format.setSwapBehavior(QSurfaceFormat::DoubleBuffer); format.setSwapInterval(m_vSyncEnabled ? 1 : 0); setFormat(format); // 设置基本属性 setMinimumSize(320, 240); setFocusPolicy(Qt::StrongFocus); // 连接更新定时器 connect(m_updateTimer, &QTimer::timeout, this, &OpenGLVideoRenderer::updateDisplay); m_updateTimer->setSingleShot(true); // av::Logger::instance().info("OpenGLVideoRenderer created"); } OpenGLVideoRenderer::~OpenGLVideoRenderer() { cleanupOpenGLResources(); if (m_swsContext) { sws_freeContext(m_swsContext); m_swsContext = nullptr; } if (m_yuvBuffer) { av_free(m_yuvBuffer); m_yuvBuffer = nullptr; } // av::Logger::instance().info("OpenGLVideoRenderer destroyed"); } void OpenGLVideoRenderer::initializeGL() { // av::Logger::instance().info("Initializing OpenGL context"); // 初始化OpenGL函数 initializeOpenGLFunctions(); // 设置清除颜色 glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // 启用混合 glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // 初始化OpenGL资源 if (initializeOpenGLResources()) { m_glInitialized = true; // av::Logger::instance().info("OpenGL resources initialized successfully"); } else { // av::Logger::instance().error("Failed to initialize OpenGL resources"); } } void OpenGLVideoRenderer::paintGL() { if (!m_glInitialized || !m_hasFrame) { // 绘制背景 glClear(GL_COLOR_BUFFER_BIT); return; } QMutexLocker locker(&m_mutex); // 清除缓冲区 glClear(GL_COLOR_BUFFER_BIT); // 渲染当前帧 renderCurrentFrame(); // 检查OpenGL错误 checkGLError("paintGL"); } void OpenGLVideoRenderer::resizeGL(int width, int height) { if (!m_glInitialized) { return; } // av::Logger::instance().debugf("Resizing OpenGL viewport to %dx%d", width, height); // 设置视口 glViewport(0, 0, width, height); // 更新投影矩阵 setupProjectionMatrix(); } void OpenGLVideoRenderer::resizeEvent(QResizeEvent* event) { QOpenGLWidget::resizeEvent(event); // 触发重绘 if (m_glInitialized) { update(); } } bool OpenGLVideoRenderer::initialize(int width, int height, AVPixelFormat pixelFormat, double fps) { QMutexLocker locker(&m_mutex); if (m_initialized) { // av::Logger::instance().warning("OpenGL video renderer already initialized"); return true; } if (width <= 0 || height <= 0) { // av::Logger::instance().error("Invalid video dimensions"); return false; } if (fps <= 0) { fps = 25.0; // 默认帧率 } m_videoWidth = width; m_videoHeight = height; m_inputFormat = pixelFormat; m_fps = fps; // 初始化图像转换器 if (m_inputFormat != AV_PIX_FMT_YUV420P) { m_swsContext = sws_getContext( m_videoWidth, m_videoHeight, m_inputFormat, m_videoWidth, m_videoHeight, AV_PIX_FMT_YUV420P, SWS_BILINEAR, nullptr, nullptr, nullptr ); if (!m_swsContext) { // av::Logger::instance().error("Failed to create SwsContext"); return false; } // 分配YUV缓冲区 m_yuvBufferSize = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, m_videoWidth, m_videoHeight, 1); m_yuvBuffer = static_cast(av_malloc(m_yuvBufferSize)); if (!m_yuvBuffer) { // av::Logger::instance().error("Failed to allocate YUV buffer"); return false; } } // 设置控件大小提示 setMinimumSize(m_videoWidth / 4, m_videoHeight / 4); m_initialized = true; // av::Logger::instance().info( // QString("OpenGL video renderer initialized: %1x%2 @ %3fps").arg(width).arg(height).arg(fps).toStdString()); return true; } bool OpenGLVideoRenderer::renderFrame(const AVFramePtr& frame) { qDebug() << "------->>>>>>>>>>>>>>>>>>-" << frame.get() << m_initialized.load(); if (!frame || !m_initialized) { return false; } QMutexLocker locker(&m_mutex); // 更新纹理数据 updateTextures(frame); // 标记有帧数据 m_hasFrame = true; // 触发更新显示 if (!m_updateTimer->isActive()) { int interval = static_cast(1000.0 / m_fps); // 根据帧率计算间隔 m_updateTimer->start(interval); } return true; } void OpenGLVideoRenderer::clear() { QMutexLocker locker(&m_mutex); m_hasFrame = false; update(); } void OpenGLVideoRenderer::setKeepAspectRatio(bool keepAspectRatio) { if (m_keepAspectRatio != keepAspectRatio) { m_keepAspectRatio = keepAspectRatio; if (m_glInitialized) { setupProjectionMatrix(); update(); } } } bool OpenGLVideoRenderer::getKeepAspectRatio() const { return m_keepAspectRatio; } void OpenGLVideoRenderer::setBackgroundColor(const QColor& color) { if (m_backgroundColor != color) { m_backgroundColor = color; if (m_glInitialized) { glClearColor(color.redF(), color.greenF(), color.blueF(), color.alphaF()); update(); } } } QSize OpenGLVideoRenderer::getVideoSize() const { return QSize(m_videoWidth, m_videoHeight); } QSize OpenGLVideoRenderer::getDisplaySize() const { return size(); } bool OpenGLVideoRenderer::isInitialized() const { return m_initialized && m_glInitialized; } void OpenGLVideoRenderer::setRenderQuality(float quality) { m_renderQuality = std::clamp(quality, 0.0f, 1.0f); if (m_glInitialized) { update(); } } void OpenGLVideoRenderer::setVSync(bool enable) { m_vSyncEnabled = enable; if (m_glInitialized) { QSurfaceFormat format = context()->format(); format.setSwapInterval(enable ? 1 : 0); context()->setFormat(format); } } void OpenGLVideoRenderer::updateDisplay() { if (m_glInitialized) { update(); } } bool OpenGLVideoRenderer::initializeOpenGLResources() { // 初始化着色器程序 if (!initializeShaders()) { return false; } // 初始化顶点数据 if (!initializeVertexData()) { return false; } // 创建纹理 if (!createTextures()) { return false; } return true; } void OpenGLVideoRenderer::cleanupOpenGLResources() { m_shaderProgram.reset(); m_vertexBuffer.reset(); m_indexBuffer.reset(); m_vao.reset(); m_yTexture.reset(); m_uTexture.reset(); m_vTexture.reset(); m_glInitialized = false; } bool OpenGLVideoRenderer::initializeShaders() { m_shaderProgram = std::make_unique(); // 添加顶点着色器 if (!m_shaderProgram->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource)) { // av::Logger::instance().error("Failed to compile vertex shader: " + m_shaderProgram->log().toStdString()); return false; } // 添加片段着色器 if (!m_shaderProgram->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource)) { // av::Logger::instance().error("Failed to compile fragment shader: " + m_shaderProgram->log().toStdString()); return false; } // 链接着色器程序 if (!m_shaderProgram->link()) { // av::Logger::instance().error("Failed to link shader program: " + m_shaderProgram->log().toStdString()); return false; } // av::Logger::instance().info("Shader program initialized successfully"); return true; } bool OpenGLVideoRenderer::initializeVertexData() { // 顶点数据 (位置 + 纹理坐标) static const float vertices[] = { // 位置 // 纹理坐标 -1.0f, -1.0f, 0.0f, 1.0f, // 左下 1.0f, -1.0f, 1.0f, 1.0f, // 右下 1.0f, 1.0f, 1.0f, 0.0f, // 右上 -1.0f, 1.0f, 0.0f, 0.0f // 左上 }; // 索引数据 static const unsigned int indices[] = { 0, 1, 2, // 第一个三角形 2, 3, 0 // 第二个三角形 }; // 创建VAO m_vao = std::make_unique(); m_vao->create(); m_vao->bind(); // 创建顶点缓冲区 m_vertexBuffer = std::make_unique(QOpenGLBuffer::VertexBuffer); m_vertexBuffer->create(); m_vertexBuffer->bind(); m_vertexBuffer->allocate(vertices, sizeof(vertices)); // 设置顶点属性 m_shaderProgram->enableAttributeArray(0); m_shaderProgram->setAttributeBuffer(0, GL_FLOAT, 0, 2, 4 * sizeof(float)); m_shaderProgram->enableAttributeArray(1); m_shaderProgram->setAttributeBuffer(1, GL_FLOAT, 2 * sizeof(float), 2, 4 * sizeof(float)); // 创建索引缓冲区 m_indexBuffer = std::make_unique(QOpenGLBuffer::IndexBuffer); m_indexBuffer->create(); m_indexBuffer->bind(); m_indexBuffer->allocate(indices, sizeof(indices)); m_vao->release(); // av::Logger::instance().info("Vertex data initialized successfully"); return true; } bool OpenGLVideoRenderer::createTextures() { // 创建Y纹理 m_yTexture = std::make_unique(QOpenGLTexture::Target2D); m_yTexture->create(); m_yTexture->bind(); m_yTexture->setFormat(QOpenGLTexture::R8_UNorm); m_yTexture->setSize(m_videoWidth, m_videoHeight); m_yTexture->allocateStorage(); m_yTexture->setMinificationFilter(QOpenGLTexture::Linear); m_yTexture->setMagnificationFilter(QOpenGLTexture::Linear); m_yTexture->setWrapMode(QOpenGLTexture::ClampToEdge); // 创建U纹理 m_uTexture = std::make_unique(QOpenGLTexture::Target2D); m_uTexture->create(); m_uTexture->bind(); m_uTexture->setFormat(QOpenGLTexture::R8_UNorm); m_uTexture->setSize(m_videoWidth / 2, m_videoHeight / 2); m_uTexture->allocateStorage(); m_uTexture->setMinificationFilter(QOpenGLTexture::Linear); m_uTexture->setMagnificationFilter(QOpenGLTexture::Linear); m_uTexture->setWrapMode(QOpenGLTexture::ClampToEdge); // 创建V纹理 m_vTexture = std::make_unique(QOpenGLTexture::Target2D); m_vTexture->create(); m_vTexture->bind(); m_vTexture->setFormat(QOpenGLTexture::R8_UNorm); m_vTexture->setSize(m_videoWidth / 2, m_videoHeight / 2); m_vTexture->allocateStorage(); m_vTexture->setMinificationFilter(QOpenGLTexture::Linear); m_vTexture->setMagnificationFilter(QOpenGLTexture::Linear); m_vTexture->setWrapMode(QOpenGLTexture::ClampToEdge); // av::Logger::instance().info("Textures created successfully"); return true; } void OpenGLVideoRenderer::updateTextures(const AVFramePtr& frame) { if (!m_glInitialized || !frame) { return; } // 转换帧格式(如果需要) AVFrame* yuvFrame = frame.get(); if (m_inputFormat != AV_PIX_FMT_YUV420P && m_swsContext) { // 创建临时帧 AVFrame* tempFrame = av_frame_alloc(); tempFrame->format = AV_PIX_FMT_YUV420P; tempFrame->width = m_videoWidth; tempFrame->height = m_videoHeight; av_image_fill_arrays(tempFrame->data, tempFrame->linesize, m_yuvBuffer, AV_PIX_FMT_YUV420P, m_videoWidth, m_videoHeight, 1); // 转换格式 sws_scale(m_swsContext, frame->data, frame->linesize, 0, m_videoHeight, tempFrame->data, tempFrame->linesize); yuvFrame = tempFrame; // 更新纹理数据 m_yTexture->bind(); m_yTexture->setData(QOpenGLTexture::Red, QOpenGLTexture::UInt8, yuvFrame->data[0], nullptr); m_uTexture->bind(); m_uTexture->setData(QOpenGLTexture::Red, QOpenGLTexture::UInt8, yuvFrame->data[1], nullptr); m_vTexture->bind(); m_vTexture->setData(QOpenGLTexture::Red, QOpenGLTexture::UInt8, yuvFrame->data[2], nullptr); av_frame_free(&tempFrame); } else { // 直接更新纹理数据 m_yTexture->bind(); m_yTexture->setData(QOpenGLTexture::Red, QOpenGLTexture::UInt8, frame->data[0], nullptr); m_uTexture->bind(); m_uTexture->setData(QOpenGLTexture::Red, QOpenGLTexture::UInt8, frame->data[1], nullptr); m_vTexture->bind(); m_vTexture->setData(QOpenGLTexture::Red, QOpenGLTexture::UInt8, frame->data[2], nullptr); } // 强制更新显示 update(); } QRectF OpenGLVideoRenderer::calculateDisplayRect() const { QSize widgetSize = size(); QSize videoSize(m_videoWidth, m_videoHeight); if (!m_keepAspectRatio) { return QRectF(-1.0f, -1.0f, 2.0f, 2.0f); } // 计算保持宽高比的显示矩形 float widgetAspect = static_cast(widgetSize.width()) / widgetSize.height(); float videoAspect = static_cast(videoSize.width()) / videoSize.height(); QRectF rect; if (widgetAspect > videoAspect) { // 控件更宽,以高度为准 float width = 2.0f * videoAspect / widgetAspect; rect = QRectF(-width / 2.0f, -1.0f, width, 2.0f); } else { // 控件更高,以宽度为准 float height = 2.0f * widgetAspect / videoAspect; rect = QRectF(-1.0f, -height / 2.0f, 2.0f, height); } return rect; } void OpenGLVideoRenderer::setupProjectionMatrix() { if (!m_shaderProgram) { return; } QRectF displayRect = calculateDisplayRect(); // 创建投影矩阵 QMatrix4x4 projection; projection.setToIdentity(); projection.ortho(displayRect.left(), displayRect.right(), displayRect.bottom(), displayRect.top(), -1.0f, 1.0f); m_shaderProgram->bind(); m_shaderProgram->setUniformValue("projection", projection); m_shaderProgram->release(); } void OpenGLVideoRenderer::renderCurrentFrame() { if (!m_shaderProgram || !m_vao) { return; } m_shaderProgram->bind(); m_vao->bind(); // 绑定纹理 m_yTexture->bind(0); m_uTexture->bind(1); m_vTexture->bind(2); m_shaderProgram->setUniformValue("yTexture", 0); m_shaderProgram->setUniformValue("uTexture", 1); m_shaderProgram->setUniformValue("vTexture", 2); // 绘制 glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr); m_vao->release(); m_shaderProgram->release(); } bool OpenGLVideoRenderer::checkGLError(const QString& operation) { GLenum error = glGetError(); if (error != GL_NO_ERROR) { // av::Logger::instance().error("OpenGL error in " + operation.toStdString() + ": " + std::to_string(error)); return false; } return true; } } // namespace player } // namespace av