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 }