#include "../base/logger.h" #include "opengl_video_renderer.h" #include #include #include #include #include #include #include extern "C" { #include #include } namespace av { namespace player { // 顶点着色器源码 - 参考yuvopenglwidget.cpp static const char* vertexShaderSource = R"( attribute vec4 vertexIn; attribute vec2 textureIn; varying vec2 TexCoord; void main(void) { gl_Position = vertexIn; TexCoord = textureIn; } )"; // 片段着色器源码 (YUV420P) - 参考yuvopenglwidget.cpp static const char* fragmentShaderSource = R"( varying mediump vec2 TexCoord; uniform sampler2D yTexture; uniform sampler2D uTexture; uniform sampler2D vTexture; void main(void) { vec3 yuv; vec3 rgb; yuv.r = texture2D(yTexture, TexCoord).r; yuv.g = texture2D(uTexture, TexCoord).r - 0.5; yuv.b = texture2D(vTexture, TexCoord).r - 0.5; rgb = mat3(1.0, 1.0, 1.0, 0.0, -0.138, 1.816, 1.540, -0.459, 0.0) * yuv; gl_FragColor = vec4(rgb, 1.0); } )"; OpenGLVideoRenderer::OpenGLVideoRenderer(QWidget* parent) : QOpenGLWidget(parent) , m_videoWidth(0) , m_videoHeight(0) , m_inputFormat(AV_PIX_FMT_NONE) , m_fps(25.0) , m_textureY(0) , m_textureU(0) , m_textureV(0) , 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() { // 清理OpenGL资源 makeCurrent(); cleanupOpenGLResources(); // 删除纹理 if (m_textureY) glDeleteTextures(1, &m_textureY); if (m_textureU) glDeleteTextures(1, &m_textureU); if (m_textureV) glDeleteTextures(1, &m_textureV); doneCurrent(); 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) { // 使用Logger输出调试信息 std::string debugMsg = "[VideoRenderer] renderFrame called - frame: " + std::to_string(reinterpret_cast(frame.get())) + ", initialized: " + std::to_string(m_initialized.load()) + ", glInitialized: " + std::to_string(m_glInitialized); av::Logger::instance().debug(debugMsg); if (!frame || !m_initialized) { av::Logger::instance().warning("[VideoRenderer] renderFrame failed - frame or not initialized"); return false; } std::string frameInfo = "[VideoRenderer] Frame info - width: " + std::to_string(frame->width) + ", height: " + std::to_string(frame->height) + ", format: " + std::to_string(frame->format) + ", linesize[0]: " + std::to_string(frame->linesize[0]) + ", linesize[1]: " + std::to_string(frame->linesize[1]) + ", linesize[2]: " + std::to_string(frame->linesize[2]); av::Logger::instance().debug(frameInfo); 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); } av::Logger::instance().debug("[VideoRenderer] renderFrame completed successfully"); 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_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; } // 绑定属性位置 - 参考yuvopenglwidget.cpp m_shaderProgram->bindAttributeLocation("vertexIn", 0); m_shaderProgram->bindAttributeLocation("textureIn", 1); // 链接着色器程序 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() { // 参考yuvopenglwidget.cpp的顶点数据设置 static const GLfloat vertices[] = {-1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f}; static const GLfloat texCoords[] = {0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f}; // 创建VAO m_vao = std::make_unique(); m_vao->create(); m_vao->bind(); // 设置顶点,纹理数组并启用 glVertexAttribPointer(0, 2, GL_FLOAT, 0, 0, vertices); glEnableVertexAttribArray(0); glVertexAttribPointer(1, 2, GL_FLOAT, 0, 0, texCoords); glEnableVertexAttribArray(1); m_vao->release(); // av::Logger::instance().info("Vertex data initialized successfully"); return true; } bool OpenGLVideoRenderer::createTextures() { // 参考yuvopenglwidget.cpp创建纹理 // 创建Y纹理 glGenTextures(1, &m_textureY); glBindTexture(GL_TEXTURE_2D, m_textureY); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); // 创建U纹理 glGenTextures(1, &m_textureU); glBindTexture(GL_TEXTURE_2D, m_textureU); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); // 创建V纹理 glGenTextures(1, &m_textureV); glBindTexture(GL_TEXTURE_2D, m_textureV); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); // av::Logger::instance().info("Textures created successfully"); return true; } void OpenGLVideoRenderer::updateTextures(const AVFramePtr& frame) { if (!m_glInitialized || !frame) { av::Logger::instance().warning("[VideoRenderer] updateTextures failed - glInitialized: " + std::to_string(m_glInitialized) + ", frame: " + std::to_string(reinterpret_cast(frame.get()))); return; } av::Logger::instance().debug("[VideoRenderer] updateTextures - video size: " + std::to_string(m_videoWidth) + "x" + std::to_string(m_videoHeight) + ", input format: " + std::to_string(m_inputFormat) + ", frame format: " + std::to_string(frame->format) + ", linesize: [" + std::to_string(frame->linesize[0]) + ", " + std::to_string(frame->linesize[1]) + ", " + std::to_string(frame->linesize[2]) + "]"); // 转换帧格式(如果需要) 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; // 更新Y纹理数据 glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, m_textureY); glPixelStorei(GL_UNPACK_ROW_LENGTH, yuvFrame->linesize[0]); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_videoWidth, m_videoHeight, GL_RED, GL_UNSIGNED_BYTE, yuvFrame->data[0]); // 更新U纹理数据 glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, m_textureU); glPixelStorei(GL_UNPACK_ROW_LENGTH, yuvFrame->linesize[1]); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_videoWidth / 2, m_videoHeight / 2, GL_RED, GL_UNSIGNED_BYTE, yuvFrame->data[1]); // 更新V纹理数据 glActiveTexture(GL_TEXTURE2); glBindTexture(GL_TEXTURE_2D, m_textureV); glPixelStorei(GL_UNPACK_ROW_LENGTH, yuvFrame->linesize[2]); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_videoWidth / 2, m_videoHeight / 2, GL_RED, GL_UNSIGNED_BYTE, yuvFrame->data[2]); // 重置像素存储参数 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); av_frame_free(&tempFrame); } else { // 参考yuvopenglwidget.cpp的纹理更新方式 av::Logger::instance().debug("[VideoRenderer] Updating textures for YUV420P format"); // Y分量 glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, m_textureY); glPixelStorei(GL_UNPACK_ROW_LENGTH, frame->linesize[0]); glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, frame->width, frame->height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, frame->data[0]); // U分量 glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, m_textureU); glPixelStorei(GL_UNPACK_ROW_LENGTH, frame->linesize[1]); glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, frame->width >> 1, frame->height >> 1, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, frame->data[1]); // V分量 glActiveTexture(GL_TEXTURE2); glBindTexture(GL_TEXTURE_2D, m_textureV); glPixelStorei(GL_UNPACK_ROW_LENGTH, frame->linesize[2]); glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, frame->width >> 1, frame->height >> 1, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, frame->data[2]); } // 重置像素存储参数 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); // 检查OpenGL错误 checkGLError("updateTextures"); // 强制更新显示 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(); // 参考yuvopenglwidget.cpp的纹理绑定和uniform设置 glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, m_textureY); glUniform1i(m_shaderProgram->uniformLocation("yTexture"), 0); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, m_textureU); glUniform1i(m_shaderProgram->uniformLocation("uTexture"), 1); glActiveTexture(GL_TEXTURE2); glBindTexture(GL_TEXTURE_2D, m_textureV); glUniform1i(m_shaderProgram->uniformLocation("vTexture"), 2); // 参考yuvopenglwidget.cpp使用GL_TRIANGLE_STRIP绘制 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); 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