| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645 |
- #include "../base/logger.h"
- #include "opengl_video_renderer.h"
- #include <QPainter>
- #include <QResizeEvent>
- #include <QApplication>
- #include <QDebug>
- #include <QOpenGLContext>
- #include <QSurfaceFormat>
- #include <algorithm>
- extern "C" {
- #include <libavutil/imgutils.h>
- #include <libswscale/swscale.h>
- }
- 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<uint8_t*>(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<uintptr_t>(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<int>(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<QOpenGLShaderProgram>();
-
- // 添加顶点着色器
- 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<QOpenGLVertexArrayObject>();
- 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<uintptr_t>(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<float>(widgetSize.width()) / widgetSize.height();
- float videoAspect = static_cast<float>(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
|