File indexing completed on 2024-06-16 04:38:29

0001 /*
0002     SPDX-FileCopyrightText: 2020-2022 Mladen Milinkovic <max@smoothware.net>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "glrenderer.h"
0008 
0009 #include <QOpenGLShader>
0010 #include <QMutexLocker>
0011 #include <QStringBuilder>
0012 #include <QScreen>
0013 #include <QWindow>
0014 
0015 #include "helpers/common.h"
0016 #include "videoplayer/backend/ffplayer.h"
0017 #include "videoplayer/videoplayer.h"
0018 #include "videoplayer/subtitletextoverlay.h"
0019 #include "videoplayer/backend/glcolorspace.h"
0020 
0021 extern "C" {
0022 #include "libavutil/pixdesc.h"
0023 #include "libswscale/swscale.h"
0024 }
0025 
0026 #define DEBUG_GL 0
0027 #define FORCE_GLES 0
0028 #define OPENGL_CORE 0
0029 #define OPENGL_VER 2,0
0030 
0031 #if DEBUG_GL
0032 #define asGL(glCall) glCall; {\
0033     GLenum __glErr__ = glGetError(); \
0034     if(__glErr__ != GL_NO_ERROR) \
0035         qCritical("GL error 0x%04x during %s line %d in @%s", __glErr__, #glCall, __LINE__, __PRETTY_FUNCTION__); \
0036 }
0037 #else
0038 #define asGL(glCall) glCall
0039 #endif
0040 
0041 #if defined(GL_ES_VERSION_2_0) || FORCE_GLES
0042 #define USE_GLES
0043 #define TEXTURE_RGB_FORMAT GL_RGBA
0044 // NOTE: we don't support rendering >8bpp on GLES, so 16bit textures are never used
0045 //       and cpu will convert the frame to 8bpp
0046 #define TEXTURE_U16_FORMAT 0x822A
0047 #else
0048 #undef USE_GLES
0049 #define TEXTURE_RGB_FORMAT GL_BGRA
0050 #define TEXTURE_U16_FORMAT GL_R16
0051 #endif
0052 
0053 #define FRAME_IS_YUV(f) ((f & AV_PIX_FMT_FLAG_RGB) == 0)
0054 #define FRAME_IS_PLANAR(f) ((f & AV_PIX_FMT_FLAG_PLANAR) != 0)
0055 
0056 using namespace SubtitleComposer;
0057 
0058 enum { ID_Y, ID_U, ID_V, ID_OVR, ID_SIZE };
0059 enum { AV_POS, AV_VIDTEX, AV_OVRTEX, A_SIZE };
0060 
0061 GLRenderer::GLRenderer(QWidget *parent)
0062     : QOpenGLWidget(parent),
0063       m_overlay(nullptr),
0064       m_mmOvr(nullptr),
0065       m_frameConvCtx(nullptr),
0066       m_bufYUV(nullptr),
0067       m_mmYUV(nullptr),
0068       m_bufSize(0),
0069       m_bufWidth(0),
0070       m_bufHeight(0),
0071       m_crWidth(0),
0072       m_crHeight(0),
0073       m_csNeedInit(true),
0074       m_vertShader(nullptr),
0075       m_fragShader(nullptr),
0076       m_shaderProg(nullptr),
0077       m_texNeedInit(true),
0078       m_lastFormat(-1),
0079       m_idTex(nullptr),
0080       m_vaBuf(nullptr)
0081 {
0082     setUpdateBehavior(NoPartialUpdate);
0083 }
0084 
0085 GLRenderer::~GLRenderer()
0086 {
0087     makeCurrent();
0088     if(m_idTex) {
0089         asGL(glDeleteTextures(ID_SIZE, m_idTex));
0090         delete[] m_idTex;
0091     }
0092     if(m_vaBuf) {
0093         asGL(glDeleteBuffers(A_SIZE, m_vaBuf));
0094         delete[] m_vaBuf;
0095     }
0096     m_vao.destroy();
0097     doneCurrent();
0098     sws_freeContext(m_frameConvCtx);
0099     delete[] m_bufYUV;
0100     delete[] m_mmYUV;
0101     delete[] m_mmOvr;
0102 }
0103 
0104 void
0105 GLRenderer::setupProfile()
0106 {
0107     QSurfaceFormat format(QSurfaceFormat::defaultFormat());
0108 #if FORCE_GLES
0109     format.setRenderableType(QSurfaceFormat::OpenGLES);
0110 #endif
0111     format.setVersion(OPENGL_VER);
0112 #if DEBUG_GL
0113     format.setOption(QSurfaceFormat::DebugContext);
0114 #endif
0115 #if OPENGL_CORE
0116     format.setProfile(QSurfaceFormat::CoreProfile);
0117 #endif
0118     QSurfaceFormat::setDefaultFormat(format);
0119 }
0120 
0121 void
0122 GLRenderer::setOverlay(SubtitleTextOverlay *overlay)
0123 {
0124     if(m_overlay)
0125         disconnect(m_overlay, nullptr, this, nullptr);
0126     m_overlay = overlay;
0127 #ifdef USE_GLES
0128     overlay->invertPixels(true);
0129 #endif
0130     connect(m_overlay, &SubtitleTextOverlay::repaintNeeded, this, QOverload<>::of(&GLRenderer::update));
0131 }
0132 
0133 void
0134 GLRenderer::reset()
0135 {
0136     m_csNeedInit = true;
0137     m_texNeedInit = true;
0138 }
0139 
0140 void
0141 GLRenderer::setFrameFormat(int width, int height, int compBits, int crWidthShift, int crHeightShift)
0142 {
0143     const quint8 compBytes = compBits > 8 ? 2 : 1;
0144     const GLsizei crWidth = AV_CEIL_RSHIFT(width, crWidthShift);
0145     const GLsizei crHeight = AV_CEIL_RSHIFT(height, crHeightShift);
0146     const quint32 bufSize = width * height * compBytes + 2 * crWidth * crHeight * compBytes;
0147 
0148     if(m_bufWidth == width && m_bufHeight == height
0149     && m_bufSize == bufSize && m_crWidth == crWidth && m_crHeight == crHeight)
0150         return;
0151 
0152     m_bufWidth = width;
0153     m_bufHeight = height;
0154     m_crWidth = crWidth;
0155     m_crHeight = crHeight;
0156 
0157     m_glType = compBytes == 1 ? GL_UNSIGNED_BYTE : GL_UNSIGNED_SHORT;
0158     m_glFormat = compBytes == 1 ? GL_R8 : TEXTURE_U16_FORMAT;
0159 
0160     delete[] m_bufYUV;
0161     m_bufSize = bufSize;
0162     m_bufYUV = new quint8[m_bufSize];
0163 
0164     delete[] m_mmYUV;
0165     m_mmYUV = new quint8[(m_bufWidth >> 1) * (m_bufHeight >> 1) * compBytes];
0166 
0167 #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
0168     QWindow *w = windowHandle();
0169     if(!w)
0170         w = nativeParentWidget()->windowHandle();
0171     const QSize sr = w->screen()->size();
0172 #else
0173     const QSize sr = screen()->size();
0174 #endif
0175     const double rr = qMin(sr.width() / width, sr.height() / height);
0176     m_overlay->setImageSize(rr * width, rr * height);
0177     delete[] m_mmOvr;
0178     m_mmOvr = new quint8[(m_overlay->width() >> 1) * (m_overlay->height() >> 1) * 4];
0179 
0180     m_pitch[0] = m_bufWidth * compBytes;
0181     m_pitch[1] = m_pitch[2] = m_crWidth * compBytes;
0182 
0183     m_pixels[0] = m_bufYUV;
0184     m_pixels[1] = m_pixels[0] + m_pitch[0] * m_bufHeight;
0185     m_pixels[2] = m_pixels[1] + m_pitch[1] * m_crHeight;
0186 
0187     m_texNeedInit = true;
0188     m_csNeedInit = true;
0189 
0190     emit resolutionChanged();
0191 }
0192 
0193 void
0194 GLRenderer::setColorspace(const AVFrame *frame)
0195 {
0196     if(!m_csNeedInit)
0197         return;
0198 
0199     const AVPixFmtDescriptor *fd = av_pix_fmt_desc_get(AVPixelFormat(frame->format));
0200     const quint8 compBits = fd->comp[0].depth;
0201     const quint8 compBytes = compBits > 8 ? 2 : 1;
0202     const bool isYUV = FRAME_IS_YUV(fd->flags);
0203 
0204     qDebug("Color range: %s(%d); primaries: %s(%d); xfer: %s(%d); space: %s(%d); depth: %d",
0205            av_color_range_name(frame->color_range), frame->color_range,
0206            av_color_primaries_name(frame->color_primaries), frame->color_primaries,
0207            av_color_transfer_name(frame->color_trc), frame->color_trc,
0208            av_color_space_name(frame->colorspace), frame->colorspace,
0209            compBits);
0210 
0211     // gamma/transfer function
0212     auto ctfit = _ctfi.constFind(frame->color_trc);
0213     if(ctfit == _ctfi.constEnd())
0214         ctfit = _ctfi.constFind(AVCOL_TRC_BT709);
0215     Q_ASSERT(ctfit != _ctfi.constEnd());
0216     m_ctfIn = ctfit.value();
0217 
0218     ctfit = _ctf.constFind(AVCOL_TRC_IEC61966_2_1); // sRGB
0219     Q_ASSERT(ctfit != _ctf.constEnd());
0220     m_ctfOut = ctfit.value();
0221 
0222     // colorspace conversion
0223     QMap<int, QVector<GLfloat>>::const_iterator cs;
0224     if((cs = _csm.constFind(frame->color_primaries)) != _csm.constEnd()) {
0225         m_csCM = QMatrix4x4(cs.value().constData(), 3, 3);
0226     } else if((cs = _csc.constFind(frame->colorspace)) != _csc.constEnd()) {
0227         m_csCM = QMatrix4x4(cs.value().constData(), 3, 3);
0228     } else if(isYUV) {
0229         cs = _csm.constFind(AVCOL_PRI_BT709);
0230         m_csCM = QMatrix4x4(cs.value().constData(), 3, 3);
0231     } else {
0232         m_csCM = QMatrix4x4();
0233     }
0234 
0235     if(isYUV) {
0236         if(frame->color_range == AVCOL_RANGE_MPEG || frame->color_range == AVCOL_RANGE_UNSPECIFIED) {
0237             // convert to full range and offset UV channels
0238             m_csCM.scale(255.0f / (219.0f + 16.0f), 255.0f / 224.0f, 255.0f / 224.0f);
0239             m_csCM.translate(-16.0f / 255.0f, -128.0f / 255.0f, -128.0f / 255.0f);
0240         } else {
0241             // offset signed UV channels
0242             m_csCM.translate(0.0f, -128.0f / 255.0f, -128.0f / 255.0f);
0243         }
0244     }
0245 
0246     // scale to full range when some bits are unused
0247     const float pixMult = double(1 << (compBytes << 3)) / double(1 << compBits);
0248     m_csCM.scale(pixMult, pixMult, pixMult);
0249 }
0250 
0251 bool
0252 GLRenderer::validTextureFormat(const AVPixFmtDescriptor *fd)
0253 {
0254     const uint64_t &f = fd->flags;
0255     if((f & AV_PIX_FMT_FLAG_BITSTREAM)) {
0256         qCritical("uploadTexture() failed: unsupported frame format [%s] - bitstream", fd->name);
0257         return false;
0258     }
0259     if((f & AV_PIX_FMT_FLAG_PAL)) {
0260         qCritical("uploadTexture() failed: unsupported frame format [%s] - palette", fd->name);
0261         return false;
0262     }
0263     if((f & AV_PIX_FMT_FLAG_BE)) {
0264         qCritical("uploadTexture() failed: unsupported frame format [%s] - bigendian", fd->name);
0265         return false;
0266     }
0267 
0268     if(FRAME_IS_YUV(f) && FRAME_IS_PLANAR(f)) {
0269         const quint8 b = fd->comp[0].depth > 8 ? 2 : 1;
0270         if(fd->comp[0].step != b || fd->comp[1].step != b || fd->comp[2].step != b) {
0271             qCritical("validTextureFormat() failed: unsupported plane step [%d, %d, %d] %s",
0272                    fd->comp[0].step, fd->comp[1].step, fd->comp[2].step, fd->name);
0273             return false;
0274         }
0275         if(fd->comp[0].offset || fd->comp[1].offset || fd->comp[2].offset) {
0276             qCritical("validTextureFormat() failed: unsupported plane offset [%d, %d, %d] %s",
0277                    fd->comp[0].offset, fd->comp[1].offset, fd->comp[2].offset, fd->name);
0278             return false;
0279         }
0280         if(fd->comp[0].shift || fd->comp[1].shift || fd->comp[2].shift) {
0281             qCritical("validTextureFormat() failed: unsupported plane shift [%d, %d, %d] %s",
0282                    fd->comp[0].shift, fd->comp[1].shift, fd->comp[2].shift, fd->name);
0283             return false;
0284         }
0285         if(fd->comp[0].depth != fd->comp[1].depth || fd->comp[0].depth != fd->comp[2].depth) {
0286             qCritical("validTextureFormat() failed: unsupported plane depths [%d, %d, %d] %s",
0287                    fd->comp[0].depth, fd->comp[1].depth, fd->comp[2].depth, fd->name);
0288             return false;
0289         }
0290         if(fd->nb_components < 3) {
0291             qCritical("validTextureFormat() failed: unsupported plane count [%d] %s",
0292                       fd->nb_components, fd->name);
0293             return false;
0294         }
0295     } else {
0296         qCritical("validTextureFormat() failed: unsupported frame format [%s]", fd->name);
0297         return false;
0298     }
0299     return true;
0300 }
0301 
0302 int
0303 GLRenderer::uploadTexture(AVFrame *frame)
0304 {
0305     const AVPixFmtDescriptor *fd = av_pix_fmt_desc_get(AVPixelFormat(frame->format));
0306     if(m_lastFormat != frame->format) {
0307         if(!validTextureFormat(fd))
0308             return -1;
0309         m_lastFormat = frame->format;
0310     }
0311 
0312     if(!frame->linesize[0] || !frame->linesize[1] || !frame->linesize[2]) {
0313         qCritical("uploadTexture() failed: invalid linesize [%d, %d, %d]",
0314                frame->linesize[0], frame->linesize[1], frame->linesize[2]);
0315         return -1;
0316     }
0317 
0318     QMutexLocker l(&m_texMutex);
0319 
0320 #ifdef USE_GLES
0321     if(fd->comp[0].depth > 8) {
0322         // convert >8bpp YUV
0323         frame->format = AV_PIX_FMT_YUV420P;
0324 
0325         const static AVPixFmtDescriptor *fd8 = av_pix_fmt_desc_get(AVPixelFormat(frame->format));
0326         m_frameConvCtx = sws_getCachedContext(m_frameConvCtx,
0327                 frame->width, frame->height, AVPixelFormat(m_lastFormat),
0328                 frame->width, frame->height, AVPixelFormat(frame->format),
0329                 0, nullptr, nullptr, nullptr);
0330 
0331         setFrameFormat(frame->width, frame->height,
0332             fd8->comp[0].depth, fd8->log2_chroma_w, fd8->log2_chroma_h);
0333 
0334         setColorspace(frame);
0335 
0336         sws_scale(m_frameConvCtx, frame->data, frame->linesize, 0, frame->height,
0337                 m_pixels, reinterpret_cast<const int *>(m_pitch));
0338     } else
0339 #endif
0340     {
0341         setFrameFormat(frame->width, frame->height,
0342             fd->comp[0].depth, fd->log2_chroma_w, fd->log2_chroma_h);
0343 
0344         setColorspace(frame);
0345 
0346         if(frame->linesize[0] > 0)
0347             setFrameY(frame->data[0], frame->linesize[0]);
0348         else
0349             setFrameY(frame->data[0] + frame->linesize[0] * (frame->height - 1), -frame->linesize[0]);
0350 
0351         if(frame->linesize[1] > 0)
0352             setFrameU(frame->data[1], frame->linesize[1]);
0353         else
0354             setFrameU(frame->data[1] + frame->linesize[1] * (AV_CEIL_RSHIFT(frame->height, 1) - 1), -frame->linesize[1]);
0355 
0356         if(frame->linesize[2] > 0)
0357             setFrameV(frame->data[2], frame->linesize[2]);
0358         else
0359             setFrameV(frame->data[2] + frame->linesize[2] * (AV_CEIL_RSHIFT(frame->height, 1) - 1), -frame->linesize[2]);
0360     }
0361 
0362     m_texUploaded = false;
0363     update();
0364 
0365     return 0;
0366 }
0367 
0368 void
0369 GLRenderer::setFrameY(quint8 *buf, quint32 pitch)
0370 {
0371     if(pitch == m_pitch[0]) {
0372         memcpy(m_pixels[0], buf, pitch * m_bufHeight);
0373     } else {
0374         quint8 *dbuf = m_pixels[0];
0375         for(int i = 0; i < m_bufHeight; i++) {
0376             memcpy(dbuf, buf, m_pitch[0]);
0377             dbuf += m_pitch[0];
0378             buf += pitch;
0379         }
0380     }
0381 }
0382 
0383 void
0384 GLRenderer::setFrameU(quint8 *buf, quint32 pitch)
0385 {
0386     if(pitch == m_pitch[1]) {
0387         memcpy(m_pixels[1], buf, pitch * m_crHeight);
0388     } else {
0389         quint8 *dbuf = m_pixels[1];
0390         for(int i = 0; i < m_crHeight; i++) {
0391             memcpy(dbuf, buf, m_pitch[1]);
0392             dbuf += m_pitch[1];
0393             buf += pitch;
0394         }
0395     }
0396 }
0397 
0398 void
0399 GLRenderer::setFrameV(quint8 *buf, quint32 pitch)
0400 {
0401     if(pitch == m_pitch[2]) {
0402         memcpy(m_pixels[2], buf, pitch * m_crHeight);
0403     } else {
0404         quint8 *dbuf = m_pixels[2];
0405         for(int i = 0; i < m_crHeight; i++) {
0406             memcpy(dbuf, buf, m_pitch[2]);
0407             dbuf += m_pitch[2];
0408             buf += pitch;
0409         }
0410     }
0411 }
0412 
0413 void
0414 GLRenderer::initShader()
0415 {
0416     delete m_vertShader;
0417     m_vertShader = new QOpenGLShader(QOpenGLShader::Vertex, this);
0418     bool success = m_vertShader->compileSourceCode(
0419 #ifdef USE_GLES
0420         "#version 100\n"
0421 #else
0422         "#version 120\n"
0423 #endif
0424         "attribute vec4 vPos;"
0425         "attribute vec2 vVidTex;"
0426         "attribute vec2 vOvrTex;"
0427         "varying vec2 vfVidTex;"
0428         "varying vec2 vfOvrTex;"
0429         "void main(void) {"
0430             "gl_Position = vPos;"
0431             "vfVidTex = vVidTex;"
0432             "vfOvrTex = vOvrTex;"
0433         "}");
0434     if(!success) {
0435         qCritical() << "GLRenderer: vertex shader compilation failed";
0436         return;
0437     }
0438 
0439     delete m_fragShader;
0440     m_fragShader = new QOpenGLShader(QOpenGLShader::Fragment, this);
0441 //  asGL(glUniformMatrix4fv(m_texCSLoc, 1, GL_FALSE, m_csCM.constData()));
0442 
0443     const float *csm = m_csCM.constData();
0444     QString csms;
0445     for(int i = 0; i < 16; i++) {
0446         if(i) csms.append(QLatin1Char(','));
0447         csms.append(QString::number(csm[i], 'g', 10));
0448     }
0449 
0450     success = m_fragShader->compileSourceCode(
0451 #ifdef USE_GLES
0452         $("#version 100\n"
0453         "precision mediump float;\n") %
0454 #else
0455         $("#version 120\n") %
0456 #endif
0457         $("varying vec2 vfVidTex;"
0458         "varying vec2 vfOvrTex;"
0459         "uniform sampler2D texY;"
0460         "uniform sampler2D texU;"
0461         "uniform sampler2D texV;"
0462         "uniform sampler2D texOvr;"
0463         "float toLinear(float vExp) {") % m_ctfIn % $("}"
0464         "float toDisplay(float vLin) {") % m_ctfOut % $("}"
0465         "void main(void) {"
0466             "vec3 yuv;"
0467             "yuv.x = texture2D(texY, vfVidTex).r;"
0468             "yuv.y = texture2D(texU, vfVidTex).r;"
0469             "yuv.z = texture2D(texV, vfVidTex).r;"
0470             "mat4 texCS = mat4(") % csms % $(");"
0471             "vec3 rgb = (texCS * vec4(yuv, 1)).xyz;"
0472             // ideally gamma would be applied to Y, but video signals apply to RGB so we do the same
0473             "rgb.r = toLinear(rgb.r);"
0474             "rgb.g = toLinear(rgb.g);"
0475             "rgb.b = toLinear(rgb.b);"
0476             // apply display (sRGB) gamma transfer
0477             "rgb.r = toDisplay(rgb.r);"
0478             "rgb.g = toDisplay(rgb.g);"
0479             "rgb.b = toDisplay(rgb.b);"
0480             "vec4 o = texture2D(texOvr, vfOvrTex);"
0481             "gl_FragColor = vec4(mix(rgb, o.rgb, o.w), 1);"
0482         "}"));
0483     if(!success) {
0484         qCritical() << "GLRenderer: fragment shader compilation failed";
0485         return;
0486     }
0487 
0488     delete m_shaderProg;
0489     m_shaderProg = new QOpenGLShaderProgram(this);
0490     m_shaderProg->addShader(m_fragShader);
0491     m_shaderProg->addShader(m_vertShader);
0492 
0493     m_shaderProg->bindAttributeLocation("vPos", AV_POS);
0494     m_shaderProg->bindAttributeLocation("vVidTex", AV_VIDTEX);
0495     m_shaderProg->bindAttributeLocation("vOvrTex", AV_OVRTEX);
0496     if(!m_shaderProg->link()) {
0497         qCritical() << "GLRenderer: shader linking failed:" << m_shaderProg->log();
0498         return;
0499     }
0500     m_shaderProg->bind();
0501 
0502     m_texY = m_shaderProg->uniformLocation("texY");
0503     m_texU = m_shaderProg->uniformLocation("texU");
0504     m_texV = m_shaderProg->uniformLocation("texV");
0505     m_texOvr = m_shaderProg->uniformLocation("texOvr");
0506 
0507     m_csNeedInit = true;
0508 }
0509 
0510 void
0511 GLRenderer::initializeGL()
0512 {
0513     QMutexLocker l(&m_texMutex);
0514 
0515     initializeOpenGLFunctions();
0516     qDebug().nospace() << "GL API: OpenGL " << (format().renderableType() == QSurfaceFormat::OpenGLES ? "ES" : "Desktop")
0517         << ' ' << format().majorVersion() << "." << format().minorVersion()
0518 #ifdef USE_GLES
0519         << " (compiled for OpenGL ES)";
0520 #else
0521         << " (compiled for OpenGL Desktop)";
0522 #endif
0523     qDebug() << "OpenGL version:" << reinterpret_cast<const char *>(glGetString(GL_VERSION));
0524     qDebug() << "GLSL version:" << reinterpret_cast<const char *>(glGetString(GL_SHADING_LANGUAGE_VERSION));
0525 
0526     if(m_vao.create())
0527         m_vao.bind();
0528 
0529     if(m_vaBuf) {
0530         asGL(glDeleteBuffers(A_SIZE, m_vaBuf));
0531     } else {
0532         delete[] m_vaBuf;
0533     }
0534     m_vaBuf = new GLuint[A_SIZE];
0535     asGL(glGenBuffers(A_SIZE, m_vaBuf));
0536 
0537     {
0538         static const GLfloat v[] = {
0539             -1.0f, 1.0f,
0540             1.0f, 1.0f,
0541             -1.0f, -1.0f,
0542             1.0f, -1.0f,
0543         };
0544         asGL(glBindBuffer(GL_ARRAY_BUFFER, m_vaBuf[AV_POS]));
0545         asGL(glBufferData(GL_ARRAY_BUFFER, sizeof(v), v, GL_STATIC_DRAW));
0546         asGL(glVertexAttribPointer(AV_POS, 2, GL_FLOAT, GL_FALSE, 0, nullptr));
0547         asGL(glEnableVertexAttribArray(AV_POS));
0548     }
0549 
0550     {
0551         static const GLfloat v[] = {
0552             0.0f,  0.0f,
0553             1.0f,  0.0f,
0554             0.0f,  1.0f,
0555             1.0f,  1.0f,
0556         };
0557         asGL(glBindBuffer(GL_ARRAY_BUFFER, m_vaBuf[AV_VIDTEX]));
0558         asGL(glBufferData(GL_ARRAY_BUFFER, sizeof(v), v, GL_STATIC_DRAW));
0559         asGL(glVertexAttribPointer(AV_VIDTEX, 2, GL_FLOAT, GL_FALSE, 0, nullptr));
0560         asGL(glEnableVertexAttribArray(AV_VIDTEX));
0561     }
0562 
0563     asGL(glBindBuffer(GL_ARRAY_BUFFER, m_vaBuf[AV_OVRTEX]));
0564     asGL(glVertexAttribPointer(AV_OVRTEX, 2, GL_FLOAT, GL_FALSE, 0, nullptr));
0565     asGL(glEnableVertexAttribArray(AV_OVRTEX));
0566     m_overlayPos[2] = 0.0f; // uploadSubtitle() will upload vertex data
0567 
0568     if(m_idTex) {
0569         asGL(glDeleteTextures(ID_SIZE, m_idTex));
0570     } else {
0571         m_idTex = new GLuint[ID_SIZE];
0572     }
0573     asGL(glGenTextures(ID_SIZE, m_idTex));
0574 
0575     asGL(glClearColor(.0f, .0f, .0f, .0f)); // background color
0576 
0577     asGL(glDisable(GL_DEPTH_TEST));
0578 
0579     m_texNeedInit = true;
0580     m_csNeedInit = true;
0581 }
0582 
0583 void
0584 GLRenderer::resizeGL(int width, int height)
0585 {
0586     QMutexLocker l(&m_texMutex);
0587     asGL(glViewport(0, 0, width, height));
0588     m_vpWidth = width;
0589     m_vpHeight = height;
0590     m_texNeedInit = true;
0591     m_overlay->setRenderScale(double(m_overlay->height()) / height);
0592     update();
0593 }
0594 
0595 void
0596 GLRenderer::paintGL()
0597 {
0598     QMutexLocker l(&m_texMutex);
0599 
0600     glClear(GL_COLOR_BUFFER_BIT);
0601 
0602     if(!m_bufYUV)
0603         return;
0604 
0605     if(m_texNeedInit) {
0606         asGL(glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
0607     }
0608 
0609     if(m_csNeedInit)
0610         initShader();
0611 
0612     uploadYUV();
0613     uploadSubtitle();
0614 
0615     asGL(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4));
0616 
0617     m_texNeedInit = false;
0618     m_csNeedInit = false;
0619 }
0620 
0621 template<class T, int D>
0622 void
0623 GLRenderer::uploadMM(int texWidth, int texHeight, T *texBuf, const T *texSrc)
0624 {
0625     int level = 0;
0626     for(;;) {
0627         if(m_texNeedInit) {
0628             if(D == 1) {
0629                 asGL(glTexImage2D(GL_TEXTURE_2D, level, m_glFormat, texWidth, texHeight, 0, GL_RED, m_glType, texSrc));
0630             } else { // D == 4
0631                 asGL(glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA8, texWidth, texHeight, 0, TEXTURE_RGB_FORMAT, GL_UNSIGNED_BYTE, texSrc));
0632             }
0633         } else {
0634             if(D == 1) {
0635                 asGL(glTexSubImage2D(GL_TEXTURE_2D, level, 0, 0, texWidth, texHeight, GL_RED, m_glType, texSrc));
0636             } else { // D == 4
0637                 asGL(glTexSubImage2D(GL_TEXTURE_2D, level, 0, 0, texWidth, texHeight, TEXTURE_RGB_FORMAT, GL_UNSIGNED_BYTE, texSrc));
0638             }
0639         }
0640 
0641         const int srcStride = texWidth * D;
0642         const int srcStridePD = srcStride + D;
0643         const int srcStrideDiff = texWidth & 1;
0644         texWidth >>= 1;
0645         texHeight >>= 1;
0646         if(texWidth < m_vpWidth && texHeight < m_vpHeight) {
0647             if(m_texNeedInit) {
0648                 asGL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR));
0649                 asGL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
0650                 asGL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0));
0651                 asGL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, level));
0652             }
0653             break;
0654         }
0655         level++;
0656 
0657         T *dst = texBuf;
0658         const int texStride = texWidth * D;
0659         for(int y = 0; y < texHeight; y++) {
0660             const T *dstEnd = dst + texStride;
0661             while(dst != dstEnd) {
0662                 if(D == 1) { // if should get optimized away
0663                     *dst++ = (texSrc[0] + texSrc[D] + texSrc[srcStride] + texSrc[srcStridePD]) >> 2;
0664                     texSrc += 2;
0665                 } else {
0666                     *dst++ = (texSrc[0] + texSrc[D] + texSrc[srcStride] + texSrc[srcStridePD]) >> 2;
0667                     texSrc++;
0668                     *dst++ = (texSrc[0] + texSrc[D] + texSrc[srcStride] + texSrc[srcStridePD]) >> 2;
0669                     texSrc++;
0670                     *dst++ = (texSrc[0] + texSrc[D] + texSrc[srcStride] + texSrc[srcStridePD]) >> 2;
0671                     texSrc++;
0672                     *dst++ = (texSrc[0] + texSrc[D] + texSrc[srcStride] + texSrc[srcStridePD]) >> 2;
0673                     texSrc += D + 1;
0674                 }
0675             }
0676             texSrc += srcStride + srcStrideDiff;
0677         }
0678         texSrc = texBuf;
0679     }
0680 }
0681 
0682 void
0683 GLRenderer::uploadYUV()
0684 {
0685     if(!m_texNeedInit && m_texUploaded)
0686         return;
0687 
0688     m_texUploaded = true;
0689 
0690     // load Y data
0691     asGL(glActiveTexture(GL_TEXTURE0 + ID_Y));
0692     asGL(glBindTexture(GL_TEXTURE_2D, m_idTex[ID_Y]));
0693     if(m_glType == GL_UNSIGNED_BYTE)
0694         uploadMM<quint8, 1>(m_bufWidth, m_bufHeight, m_mmYUV, m_pixels[0]);
0695     else
0696         uploadMM<quint16, 1>(m_bufWidth, m_bufHeight, reinterpret_cast<quint16 *>(m_mmYUV), reinterpret_cast<quint16 *>(m_pixels[0]));
0697     if(m_texNeedInit) {
0698         asGL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
0699         asGL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
0700         asGL(glUniform1i(m_texY, ID_Y));
0701     }
0702 
0703     // load U data
0704     asGL(glActiveTexture(GL_TEXTURE0 + ID_U));
0705     asGL(glBindTexture(GL_TEXTURE_2D, m_idTex[ID_U]));
0706     if(m_glType == GL_UNSIGNED_BYTE)
0707         uploadMM<quint8, 1>(m_crWidth, m_crHeight, m_mmYUV, m_pixels[1]);
0708     else
0709         uploadMM<quint16, 1>(m_crWidth, m_crHeight, reinterpret_cast<quint16 *>(m_mmYUV), reinterpret_cast<quint16 *>(m_pixels[1]));
0710     if(m_texNeedInit) {
0711         asGL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
0712         asGL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
0713         asGL(glUniform1i(m_texU, ID_U));
0714     }
0715 
0716     // load V data
0717     asGL(glActiveTexture(GL_TEXTURE0 + ID_V));
0718     asGL(glBindTexture(GL_TEXTURE_2D, m_idTex[ID_V]));
0719     if(m_glType == GL_UNSIGNED_BYTE)
0720         uploadMM<quint8, 1>(m_crWidth, m_crHeight, m_mmYUV, m_pixels[2]);
0721     else
0722         uploadMM<quint16, 1>(m_crWidth, m_crHeight, reinterpret_cast<quint16 *>(m_mmYUV), reinterpret_cast<quint16 *>(m_pixels[2]));
0723     if(m_texNeedInit) {
0724         asGL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
0725         asGL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
0726         asGL(glUniform1i(m_texV, ID_V));
0727     }
0728 }
0729 
0730 void
0731 GLRenderer::uploadSubtitle()
0732 {
0733     if(!m_texNeedInit && !m_overlay->isDirty())
0734         return;
0735 
0736     const QImage &img = m_overlay->image();
0737 
0738     const GLfloat rs = qMin(1.0 / m_overlay->renderScale(), 1.0);
0739     if(rs != m_overlayPos[2]) {
0740         m_overlayPos[2] = m_overlayPos[6] = m_overlayPos[5] = m_overlayPos[7] = rs;
0741         asGL(glBindBuffer(GL_ARRAY_BUFFER, m_vaBuf[AV_OVRTEX]));
0742         asGL(glBufferData(GL_ARRAY_BUFFER, sizeof(m_overlayPos), m_overlayPos, GL_STATIC_DRAW));
0743         asGL(glBindBuffer(GL_ARRAY_BUFFER, 0));
0744     }
0745 
0746     // overlay
0747     asGL(glActiveTexture(GL_TEXTURE0 + ID_OVR));
0748     asGL(glBindTexture(GL_TEXTURE_2D, m_idTex[ID_OVR]));
0749     uploadMM<quint8, 4>(img.width(), img.height(), m_mmOvr, img.constBits());
0750     if(m_texNeedInit) {
0751         asGL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER));
0752         asGL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER));
0753         static const float borderColor[] = { .0f, .0f, .0f, .0f };
0754         asGL(glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor));
0755         asGL(glUniform1i(m_texOvr, ID_OVR));
0756     }
0757 }