/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2017 Wang Bin * This file is part of QtAV (from 2014) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #include "../OpenGLVideo.h" #include #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) #include #include #include #endif //5.0 #include "../SurfaceInterop.h" #include "../VideoShader.h" #include "ShaderManager.h" #include "../GeometryRenderer.h" #include "OpenGLHelper.h" #include "../Logger.h" namespace FAV { #if (RM_MODEL_360) // EMT SINGLE 360 #define TEST_EQUIRECTANGULAR 0 inline double snapAngle(double a) { if(a > 360.0) { return a - 360.0; } else if (a < 0.0) { a = 360.0 + a; } return a; } // FIXME: why crash if inherits both QObject and DPtrPrivate? class OpenGLVideoPrivate : public DPtrPrivate { public: OpenGLVideoPrivate() : ctx(0) , manager(0) , material(new VideoMaterial()) , material_type(0) , norm_viewport(true) , update_geo(true) , tex_target(0) , valiad_tex_width(1.0) , mesh_type(OpenGLVideo::RectMesh) // OpenGLVideo::RectMesh,SphereMesh , geometry(NULL) , user_shader(NULL) , dAngleX(0) , dAngleY(0) , mode(0) , bVFlip(false) , bHFlip(false) { } ~OpenGLVideoPrivate() { if (material) { delete material; material = 0; } delete geometry; } // 0~360 = 시계방향 // double dAngle; // double dPitch; double dAngleX; // 0~360 double dAngleY; // 0~180 (아래쪽만 회전 가능) int mode; bool bVFlip; // 상하 반전 bool bHFlip; // 좌우반전 void resetGL() { ctx = 0; gr.updateGeometry(NULL); if (!manager) return; manager->setParent(0); delete manager; manager = 0; if (material) { delete material; material = 0; } } // update geometry(vertex array) set attributes or bind VAO/VBO. void updateGeometry(VideoShader* shader, const QRectF& t, const QRectF& r); public: QOpenGLContext *ctx; ShaderManager *manager; VideoMaterial *material; qint64 material_type; bool norm_viewport; bool has_a; bool update_geo; int tex_target; qreal valiad_tex_width; QSize video_size; QRectF target; QRectF roi; //including invalid padding width OpenGLVideo::MeshType mesh_type; TexturedGeometry *geometry; GeometryRenderer gr; //TexturedGeometry *geometry2; //GeometryRenderer gr2; QRectF rect; QMatrix4x4 matrix; VideoShader *user_shader; }; void OpenGLVideoPrivate::updateGeometry(VideoShader* shader, const QRectF &t, const QRectF &r) { // also check size change for normalizedROI computation if roi is not normalized const bool roi_changed = valiad_tex_width != material->validTextureWidth() || roi != r || video_size != material->frameSize(); const int tc = shader->textureLocationCount(); if (roi_changed) { roi = r; valiad_tex_width = material->validTextureWidth(); video_size = material->frameSize(); } if (tex_target != shader->textureTarget()) { tex_target = shader->textureTarget(); update_geo = true; } // QRectF(-1, 1, 2, -2) = 정상 V-FLIP = QRectF(-1, -1, 2, 2) -> 3D 모드에서는 동작하지 않는다 각도로 조정 qreal top = bVFlip ? -1 : 1; qreal bottom = bVFlip ? 2: -2; qreal left = bHFlip ? 1 : -1; qreal right = bHFlip ? -2 : 2; //qInfo() << top << bottom << left << right << __FUNCTION__; const QRectF gp = QRectF(left,top,right,bottom); //bVFlip ? QRectF(-1, -1, 2, 2) : QRectF(-1, 1, 2, -2); QRectF target_rect = norm_viewport ? gp : rect; if (target.isValid()) { if (roi_changed || target != t) { target = t; update_geo = true; //target_rect = target (if valid). // relate to gvf bug? } } else { if (roi_changed) { update_geo = true; } } if (!update_geo) return; delete geometry; geometry = NULL; if (mesh_type == OpenGLVideo::SphereMesh) { geometry = new Sphere(); } else { geometry = new TexturedGeometry(); } //qDebug() << "target rect: " << target_rect ; geometry->setTextureCount(shader->textureTarget() == GL_TEXTURE_RECTANGLE ? tc : 1); geometry->setGeometryRect(target_rect); //geometry->setTextureRect(QRectF(0,0,1.0,1.0)); geometry->setTextureRect(material->mapToTexture(0, roi)); if (shader->textureTarget() == GL_TEXTURE_RECTANGLE) { for (int i = 1; i < tc; ++i) { // tc can > planes, but that will compute chroma plane geometry->setTextureRect(material->mapToTexture(i, roi), i); } } geometry->create(); update_geo = false; gr.updateGeometry(geometry); } OpenGLVideo::OpenGLVideo() { bVRFlip = false; zoomLevel = 0; } bool OpenGLVideo::isSupported(VideoFormat::PixelFormat pixfmt) { return pixfmt != VideoFormat::Format_RGB48BE && pixfmt != VideoFormat::Format_Invalid; } void OpenGLVideo::getRenderInfo(RENDER_INFO_360* info) { DPTR_D(OpenGLVideo); info->dAngleX = d.dAngleX; info->dAngleY = d.dAngleY; info->mode = d.mode; info->bVFlip = d.bVFlip; info->bHFlip = d.bHFlip; } void OpenGLVideo::setRenderInfo(RENDER_INFO_360* info) { DPTR_D(OpenGLVideo); zoomLevel = 0; zoomLevel2D = 0; setMode(info->mode); // RESET ZOOM if(info->mode == 2) { setAngle(info->dAngleX,info->dAngleY); } d.bHFlip = info->bHFlip; // 설정만 setVFlip(info->bVFlip); // 적용 resetAngle(); } void OpenGLVideo::setOpenGLContext(QOpenGLContext *ctx) { DPTR_D(OpenGLVideo); if (d.ctx == ctx) return; qreal b = 0, c = 0, h = 0, s = 0; if (d.material) { b = d.material->brightness(); c = d.material->contrast(); h = d.material->hue(); s = d.material->saturation(); delete d.material; d.material = 0; } d.resetGL(); //TODO: is it ok to destroygl resources in another context? d.ctx = ctx; // Qt4: set to null in resetGL() if (!ctx) { return; } d.material = new VideoMaterial(); d.material->setBrightness(b); d.material->setContrast(c); d.material->setHue(h); d.material->setSaturation(s); #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) d.manager = ctx->findChild(QStringLiteral("__qtav_shader_manager")); QSizeF surfaceSize = ctx->surface()->size(); //qInfo() << "SURFACE SIZE:(0)" << surfaceSize << __FUNCTION__; #if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0) surfaceSize *= ctx->screen()->devicePixelRatio(); #else surfaceSize *= qApp->devicePixelRatio(); //TODO: window()->devicePixelRatio() is the window screen's #endif #else QSizeF surfaceSize = QSizeF(ctx->device()->width(), ctx->device()->height()); #endif //qInfo() << "SURFACE SIZE:(2)" << surfaceSize << __FUNCTION__; //qInfo() << "DEBUG_OPENGL(13)" << " ctx->surface():" << surfaceSize; // 1,1 //qInfo() << "DEBUG_OPENGL(12)" << " ctx->surface():" << ctx->surface()->size(); // 1,1 //qInfo() << "DEBUG_OPENGL(11)" << " ctx->screen():" << ctx->screen()->availableSize() << __FUNCTION__; // 전체 화면 해상도 setProjectionMatrixToRect(QRectF(QPointF(), surfaceSize)); if (d.manager) return; // TODO: what if ctx is delete? #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) d.manager = new ShaderManager(ctx); QObject::connect(ctx, SIGNAL(aboutToBeDestroyed()), this, SLOT(resetGL()), Qt::DirectConnection); // direct to make sure there is a valid context. makeCurrent in window.aboutToBeDestroyed()? #else d.manager = new ShaderManager(this); #endif d.manager->setObjectName(QStringLiteral("__qtav_shader_manager")); /// get gl info here because context is current(qt ensure it) //const QByteArray extensions(reinterpret_cast(glGetString(GL_EXTENSIONS))); bool hasGLSL = QOpenGLShaderProgram::hasOpenGLShaderPrograms(); qDebug("OpenGL version: %d.%d hasGLSL: %d", ctx->format().majorVersion(), ctx->format().minorVersion(), hasGLSL); static bool sInfo = true; if (sInfo) { sInfo = false; qDebug("GL_VERSION: %s", DYGL(glGetString(GL_VERSION))); qDebug("GL_VENDOR: %s", DYGL(glGetString(GL_VENDOR))); qDebug("GL_RENDERER: %s", DYGL(glGetString(GL_RENDERER))); qDebug("GL_SHADING_LANGUAGE_VERSION: %s", DYGL(glGetString(GL_SHADING_LANGUAGE_VERSION))); /// check here with current context can ensure the right result. If the first check is in VideoShader/VideoMaterial/decoder or somewhere else, the context can be null bool v = OpenGLHelper::isOpenGLES(); qDebug("Is OpenGLES: %d", v); v = OpenGLHelper::isEGL(); qDebug("Is EGL: %d", v); const int glsl_ver = OpenGLHelper::GLSLVersion(); qDebug("GLSL version: %d", glsl_ver); v = OpenGLHelper::isPBOSupported(); qDebug("Has PBO: %d", v); v = OpenGLHelper::has16BitTexture(); qDebug("Has 16bit texture: %d", v); v = OpenGLHelper::hasRG(); qDebug("Has RG texture: %d", v); qDebug() << ctx->format(); } } QOpenGLContext* OpenGLVideo::openGLContext() { return d_func().ctx; } void OpenGLVideo::setCurrentFrame(const VideoFrame &frame) { d_func().material->setCurrentFrame(frame); d_func().has_a = frame.format().hasAlpha(); } void OpenGLVideo::setProjectionMatrixToRect(const QRectF &v) { setViewport(v); } void OpenGLVideo::setViewport(const QRectF &r) { DPTR_D(OpenGLVideo); d.rect = r; if (d.norm_viewport) { d.matrix.setToIdentity(); if (d.mesh_type == SphereMesh) { //setAngle(d.dAngle); setAngle(d.dAngleX,d.dAngleY); // float verticalAngle, float aspectRatio, float nearPlane, float farPlane //d.matrix.perspective(120.0f, 0.8f, 0.1f, 100.0f); // for sphere //d.matrix.perspective(90.0f, 2.0f, 0.0f, 1.0f); // for sphere //d.matrix.rotate(90.0,0,0,1.0); //qInfo() << "lookAt"; //d.matrix.lookAt(QVector3D(0,0,0),QVector3D(120,0,120),QVector3D(0,1,0)); } } else { d.matrix.setToIdentity(); d.matrix.ortho(r); d.update_geo = true; // even true for target_rect != d.rect } // Mirrored relative to the usual Qt coordinate system with origin in the top left corner. //mirrored = mat(0, 0) * mat(1, 1) - mat(0, 1) * mat(1, 0) > 0; if (d.ctx && d.ctx == QOpenGLContext::currentContext()) { //qInfo() << "DEBUG_OPENGL(21)" << " glViewport" << d.rect << __FUNCTION__; // 663x373 DYGL(glViewport(d.rect.x(), d.rect.y(), d.rect.width(), d.rect.height())); } } void OpenGLVideo::setBrightness(qreal value) { d_func().material->setBrightness(value); } void OpenGLVideo::setContrast(qreal value) { d_func().material->setContrast(value); } void OpenGLVideo::setHue(qreal value) { d_func().material->setHue(value); } void OpenGLVideo::setSaturation(qreal value) { d_func().material->setSaturation(value); } void OpenGLVideo::setUserShader(VideoShader *shader) { d_func().user_shader = shader; } VideoShader* OpenGLVideo::userShader() const { return d_func().user_shader; } void OpenGLVideo::setMeshType(MeshType value) { DPTR_D(OpenGLVideo); if (d.mesh_type == value) return; d.mesh_type = value; d.update_geo = true; d.matrix.setToIdentity(); if (d.mesh_type == SphereMesh && d.norm_viewport) { //setAngle(0.0); //d.matrix.setToIdentity(); //d.matrix.perspective(90.0f, 2.0f, 0.0f, 1.0f); //d.matrix.perspective(55.0f, 1.0f, 0.1f, 1.0f); // for sphere } } OpenGLVideo::MeshType OpenGLVideo::meshType() const { return d_func().mesh_type; } void OpenGLVideo::fill(const QColor &color) { //DYGL(glClearColor(1.0, 0.0,0.0, 1.0)); DYGL(glClearColor(color.redF(), color.greenF(), color.blueF(), color.alphaF())); DYGL(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); } //void OpenGLVideo::render(const QRectF &target, const QRectF& roi, const QMatrix4x4& transform) //{ // DPTR_D(OpenGLVideo); // Q_ASSERT(d.manager); // Q_EMIT beforeRendering(); // DYGL(glViewport(d.rect.x(), d.rect.y(), d.rect.width(), d.rect.height())); // viewport was used in gpu filters is wrong, qt quick fbo item's is right(so must ensure setProjectionMatrixToRect was called correctly) // const qint64 mt = d.material->type(); // if (d.material_type != mt) // { //#if !(OFF_OTHER_DEBUG) // qDebug() << "material changed: " << VideoMaterial::typeName(d.material_type) << " => " << VideoMaterial::typeName(mt); //#endif // d.material_type = mt; // } // if (!d.material->bind()) // bind first because texture parameters(target) mapped from native buffer is unknown before it // { // return; // } // VideoShader *shader = d.user_shader; // if (!shader) // shader = d.manager->prepareMaterial(d.material, mt); //TODO: print shader type name if changed. prepareMaterial(,sample_code, pp_code) // shader->update(d.material); // shader->program()->setUniformValue(shader->matrixLocation(), transform*d.matrix); // // uniform end. attribute begin // d.updateGeometry(shader, target, roi); // // normalize? // const bool blending = d.has_a; // if (blending) { // DYGL(glEnable(GL_BLEND)); // gl().BlendFuncSeparate(GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA); // // } // //if (d.mesh_type == OpenGLVideo::SphereMesh) // //DYGL(glEnable(GL_CULL_FACE)); // required for sphere! FIXME: broken in qml and qgvf // d.gr.render(); // if (blending) // DYGL(glDisable(GL_BLEND)); // // d.shader->program()->release(); //glUseProgram(0) // d.material->unbind(); // Q_EMIT afterRendering(); //} #if (!USE_POINT_ZOOM || USE_HYBRID_ZOOM) void OpenGLVideo::setROI(double x, double y, double w, double h) { bROI = true; rROI = QRectF(x,y,w,h); } #endif //USE_POINT_ZOOM void OpenGLVideo::clearROI() { DPTR_D(OpenGLVideo); #if (USE_POINT_ZOOM) zoomLevel2D = 0; centerZoom = QPoint(0.5,0.5); if(d.mode == 2) { zoomLevel = 0; setAngle(d.dAngleX,d.dAngleY); } #endif #if (!USE_POINT_ZOOM || USE_HYBRID_ZOOM) rROI = QRectF(0.0,0.0,1.0,1.0); bROI = false; #endif } bool OpenGLVideo::isVFlip() { DPTR_D(OpenGLVideo); return d.bVFlip; } void OpenGLVideo::setHFlip(bool flip) { DPTR_D(OpenGLVideo); d.bHFlip = flip; d.update_geo = true; } bool OpenGLVideo::isHFlip() { DPTR_D(OpenGLVideo); return d.bHFlip; } void OpenGLVideo::setVFlip(bool flip) { DPTR_D(OpenGLVideo); d.bVFlip = flip; if(d.mode == 2) { // BOUND Y ANGLE 200 ~ 270 ~ 340 double dy; if(d.dAngleY > 270) { dy = 270 - (d.dAngleY - 270); } else { dy = 270 + (270 - d.dAngleY); } setAngle(d.dAngleX+180, dy); } d.update_geo = true; } #if (USE_POINT_ZOOM) QRectF OpenGLVideo::getROI(QRectF src) { DPTR_D(OpenGLVideo); float zw = getZoomScale();// 1.0f / (float)(zoomLevel2D + 1); float w = src.width() * zw; float h = src.height() * zw; float cx = src.width() * centerZoom.x(); float cy = src.height() * centerZoom.y(); float xmin = cx - w/2.0f; float ymin = cy - h/2.0f; // SNAP if(xmin + w > src.width()) { xmin = src.width() - w; } if(ymin + h > src.height()) { ymin = src.height() - h; } // SNAP xmin = qMax(xmin,0.0f); ymin = qMax(ymin,0.0f); centerZoom.setX((xmin + w/2.0f)/src.width()); centerZoom.setY((ymin + h/2.0f)/src.height()); if(d.mode == 0) { float dw = d.rect.width(); float dh = d.rect.height(); if(dw > dh) { float ow = w * dw/dh * h/w; // 1:1 영상이 아닐경우 비율.. xmin -= ((ow - w) * 0.5f); w = ow; } //qInfo() << QRectF(xmin, ymin,w,h) << w/h << dw/dh; } else if (d.mode == 1) { float dw = d.rect.width(); float dh = d.rect.height(); // 가로세로 비율이 16:9 보다 클 경우 if((dw / dh) > (16.0f / 9.0f)) { float ow = w * (dw / dh) * (9.0 / 16.0); xmin -= ((ow - w) * 0.5f); w = ow; } // else { // float oh = h * (dh / dw) * (16.0 / 9.0); // ymin -= ((oh - h) * 0.5f); // h = oh; // } } //qInfo() << xmin << ymin << w << h << __FUNCTION__ << __LINE__; return QRectF(xmin, ymin,w,h); } float OpenGLVideo::getZoomScale() { float zs[] = {1.0f, 0.7f, 0.5f, 0.3f, 0.2f}; if(sizeof(zs) / sizeof(float) > zoomLevel2D) { return zs[zoomLevel2D]; } return 1.0f; } QPointF OpenGLVideo::getZoomCenter(float rx, float ry) { DPTR_D(OpenGLVideo); if(d.bVFlip) { ry = 1.0 - ry; } if(d.bHFlip) { rx = 1.0 - rx; } if(zoomLevel2D == 0) { return QPointF(rx,ry); } float zwh = getZoomScale(); // 현재 Zoom Level 에서의 xmin float xmin = centerZoom.x() - (zwh / 2.0); float ymin = centerZoom.y() - (zwh / 2.0); // 현재 영역에서의 XY float nx = xmin + rx * zwh; float ny = ymin + ry * zwh; return QPointF(nx,ny); } #endif // USE_POINT_ZOOM void OpenGLVideo::render(const QRectF &target, const QRectF& roi, const QMatrix4x4& transform) { #if(TEST_EQUIRECTANGULAR) Q_UNUSED(transform) #endif DPTR_D(OpenGLVideo); Q_ASSERT(d.manager); Q_EMIT beforeRendering(); // viewport was used in gpu filters is wrong, qt quick fbo item's is right(so must ensure setProjectionMatrixToRect was called correctly) // 663x373, 1918x959 DYGL(glViewport(d.rect.x(), d.rect.y(), d.rect.width(), d.rect.height())); //qInfo() << "VP:" << d.rect << "transform:" << transform; const qint64 mt = d.material->type(); if (d.material_type != mt) { #if !(OFF_OTHER_DEBUG) qDebug() << "material changed: " << VideoMaterial::typeName(d.material_type) << " => " << VideoMaterial::typeName(mt); #endif d.material_type = mt; } if (!d.material->bind()) // bind first because texture parameters(target) mapped from native buffer is unknown before it { return; } // TEXTURE SIZE = 2280x2280 영상에서 -> 2304, 2280 문제로 녹색선 발생 //qInfo() << "frameSize:" << d.material->textureSize(0); VideoShader *shader = d.user_shader; if (!shader) shader = d.manager->prepareMaterial(d.material, mt); //TODO: print shader type name if changed. prepareMaterial(,sample_code, pp_code) shader->update(d.material); // WIDTH 처리 float ew = d.material->validTextureWidth(); if(ew < 1.0) { ew *= 0.999f; } shader->program()->setUniformValue(shader->effectiveLocation(),ew); #if (SUB_MODEL_TB5000) //qInfo() << "RECT SIZE:" << d.rect.size() << __FUNCTION__; // 원본 texture 의 크기를 전달해야함..??? // 16:9 크기가 넘을 경우 중복 발생 float ww = (float)d.rect.width(); if(ww / 16.0f * 9.0f > (float)d.rect.height()) { ww = (float)d.rect.height() * (16.0f / 9.0f); } //qInfo() << ww << ew << __FUNCTION__; shader->program()->setUniformValue(shader->realScreenWidthLocation(),(float)d.rect.width()); // 사용하지 않는다... shader->program()->setUniformValue(shader->screenWidthLocation(),ww); shader->program()->setUniformValue(shader->screenHeightLocation(),(float)d.rect.height()); #endif // SUB_MODEL_TB5000 #if (TOP_DOWN_2D_360) shader->program()->setUniformValue(shader->angleLocation(),(float)d.dAngleX); shader->program()->setUniformValue(shader->whratioLocation(),(float)(d.rect.width() / d.rect.height())); #endif // TOP_DOWN_2D_360 // (w - x) / h = 16.0/9.0; // w - ((16.0/9.0) * h); // float wo = ((d.rect.width() - ((16.0 / 9.0) * d.rect.height())) / d.rect.width()) * 0.5; // if(d.mode == 1) { // qInfo() << d.rect.width() << d.rect.height() << wo << __FUNCTION__ << __LINE__; // } // float clipbound = (d.mode == 1 && wo > 0.0) ? wo : 0.0f; shader->program()->setUniformValue(shader->clipBoundLocation(),d.mode == 1 ? 1.0f : 0.0f); shader->program()->setUniformValue(shader->modeLocation(),d.mode); if(d.mode != 0 && d.mode != 1) { QMatrix4x4 m(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1); shader->program()->setUniformValue(shader->matrixLocation(), m*d.matrix); // 파노라마 모드에서는 RAD 사용??? shader->program()->setUniformValue(shader->angleLocation(), (float)(DEG2RAD(d.dAngleX))); } else { shader->program()->setUniformValue(shader->matrixLocation(), transform*d.matrix); } //qInfo() << roi; //QRectF testROI = QRectF(1440,720,1440,1440); //#if (USE_POINT_ZOOM) if(bROI && d.mode != 2) { double w = roi.width(); double h = roi.height(); //double ry = d.bVFlip ? 1.0 - rROI.y() - rROI.height() : rROI.y(); // 상하반전은 ROI 설정시 반영 (하지 않으면 rROI 값이 반전시 틀려짐..) QRectF tr = QRectF(rROI.x() * w, rROI.y() * h, rROI.width() * w, rROI.height() * h); //qInfo() << "ROI:" << tr << " from:" << w * rROI.x(); d.updateGeometry(shader, target, tr); } else if((zoomLevel2D > 0 && d.mode == 0) || d.mode == 1) // MODE 1 에서는 영역외 그림.. { // double w = roi.width(); // double h = roi.height(); // double ry = d.bVFlip ? 1.0 - rROI.y() - rROI.height() : rROI.y(); // // 상하반전은 ROI 설정시 반영 (하지 않으면 rROI 값이 반전시 틀려짐..) // QRectF tr = QRectF(rROI.x() * w, rROI.y() * h, rROI.width() * w, rROI.height() * h); // qInfo() << "ROI:" << tr << " from:" << w * rROI.x(); d.updateGeometry(shader, target, getROI(roi)); } //#else // USE_POINT_ZOOM //#endif // USE_POINT_ZOOM else { d.updateGeometry(shader, target, roi); } //qInfo() << "FLIP" << bVFlip; //shader->program()->setUniformValue(shader->flipLocation(), bVFlip ? 1 : 0); d.gr.render(); d.material->unbind(); Q_EMIT afterRendering(); } QSize OpenGLVideo::frameSize() { DPTR_D(OpenGLVideo); return d.material->frameSize(); } #define HEMISPHERE_3 1 void OpenGLVideo::angle(double* ax, double* ay) { DPTR_D(OpenGLVideo); *ax = d.dAngleX; *ay = d.dAngleY; } void OpenGLVideo::startDrag() { DPTR_D(OpenGLVideo); sx = d.dAngleX; sy = d.dAngleY; // bDragAxis = true; } void OpenGLVideo::drag(double rx,double ry) { //DPTR_D(OpenGLVideo); // 화면 좌우넓이는 90 * 2 = 180 (파노라마 모드는 360) // 화며 상하길이는 90 //double widthX = (d.mode == 1 || d.mode == 2) ? -360 : 180; double dr = bVRFlip ? 1.0 : -1.0; // 180, 90 -> 45, 22 setAngle(sx + (rx * 45) * dr,sy + (ry * 22)); //qInfo() << "setAngle:" << sx + (rx * 45) * dr, sy + (ry * 22); } void OpenGLVideo::scroll(double rx,double ry) { DPTR_D(OpenGLVideo); double dr = bVRFlip ? 1.0 : -1.0; // 180, 90 -> 45, 22 setAngle(d.dAngleX + (rx * 45) * dr,d.dAngleY + (ry * 22)); //qInfo() << "setAngle:" << sx + (rx * 45) * dr, sy + (ry * 22); } void OpenGLVideo::resetAngle() { DPTR_D(OpenGLVideo); zoomLevel = 0; #if (TANDF_360_TEST || MH_360_TEST) setAngle(d.bVFlip ? 0 : 180,d.bVFlip ? 210.0 : 330.0); #else setAngle(d.bVFlip ? 180 : 0,d.bVFlip ? 210.0 : 330.0); #endif } void OpenGLVideo::setZoom(int level) { DPTR_D(OpenGLVideo); zoomLevel = qMax(qMin(level,20),0); setAngle(d.dAngleX,d.dAngleY); } void OpenGLVideo::zoomPos(double rx, double ry) { DPTR_D(OpenGLVideo); // 360 VR if(d.mode == 2) { double perspective = 45.0f / (1.0 + (((double)zoomLevel) / 10.0)); double whr = d.rect.width() / (d.rect.height() * 0.75); //double whr = d.rect.width() / (d.rect.height() * 1.0); double ax = (rx - 0.5) * perspective * whr; double ay = (ry - 0.5) * perspective; // 0.5,0.5 에서 멀어질 수록 각도 변화는 줄어듬 double arx = (fabs(rx - 0.5) * 2.0) * 0.3 + 1.0; ax /= arx; double ary = (fabs(ry - 0.5) * 2.0) * 0.3 + 1.0; ay /= ary; if(bVRFlip) { ax *= -1.0; } int cz = ((zoomLevel / 5) + 1) * 5; // 처음으로 돌아가기 if (cz > 20) { zoomLevel = 0; } else { zoomLevel = cz; } //qInfo() << d.dAngleX << d.dAngleY << ax << ay; setAngle(d.dAngleX+ax,d.dAngleY-ay); } else // 2D MODE { // 실제 화면내의 중심 좌표를 설정해야함 // WIDE 모드는 동일, FISHEYE 모드는 빈공간 처리해야함. centerZoom = getZoomCenter(rx,ry); zoomLevel2D = (zoomLevel2D + 1) % 5; } } void OpenGLVideo::setAngle(double ax, double ay) { DPTR_D(OpenGLVideo); #if (TOP_DOWN_2D_360) d.dAngleX = ax; d.dAngleY = ay; // d.dAngleX = snapAngle(d.dAngleX); // d.dAngleY = snapAngle(d.dAngleY); #else // #if (TOP_DOWN_2D_360) d.dAngleX = ax; d.dAngleY = ay; // 확대에 따라 최대, 최소 Y 각이 변경되고 perspective 도 변경됨 // 1.0 ~ 3.0 double zoomRatio = (0.0 + (((double)zoomLevel) / 10.0)); // BOUND Y ANGLE 200 ~ 270 ~ 340 double yAngleOffset = zoomRatio * 8.0; // 15도 변화? if(d.dAngleY > 340.0 + yAngleOffset) { d.dAngleY = 340 + yAngleOffset; } else if (d.dAngleY < 200.0 - yAngleOffset) { d.dAngleY = 200 - yAngleOffset; } // ANGLE SNAP d.dAngleX = snapAngle(d.dAngleX); d.dAngleY = snapAngle(d.dAngleY); //qInfo() << d.dAngleX << d.dAngleY << __FUNCTION__; // 3D(VR) MODE #if (REMOVE_360_WIDE_SINGLE) if(d.mode == 2) #else if(d.mode == 3) #endif { d.update_geo = true; // https://mathinsight.org/spherical_coordinates // x = 거리 * sin()cos() double theta = DEG2RAD(d.dAngleX) + DEG_PI; double phi = DEG2RAD(d.dAngleY); // 화면 XY + Z: 깊이 double x = cos(phi) * cos(theta) * SPHERE_SCALE; double y = sin(phi) * SPHERE_SCALE; double z = cos(phi) * sin(theta) * SPHERE_SCALE; //y = qMin(y,-7.0); // 화면 반전 계산 double td = 1.0; if (d.dAngleY > 90.0 && d.dAngleY < 270.0) { td = -1.0; bVRFlip = true; // X 축 이동을 역으로 계산 } else { bVRFlip = false; } // Y 각도에 따른 높이 조절 double ds = fabs(d.dAngleY - 270.0) / 70.0 * -0.4; //qInfo() << "ANGLE:" << d.dAngleX << d.dAngleY << ax << ay << zoomLevel << __FUNCTION__ << __LINE__; // 1~10 1:1x, 2:1.1~~~ //double perspective = 45.0f / (1.0 + (((double)zoomLevel) / 10.0)); double perspective = 45.0f / (1.0 + (((double)zoomLevel) / 10.0)); //qInfo() << "XYZ:" << x << y << z << ":" << d.dAngleX << d.dAngleY << bVRFlip << "zoomLevel:" << zoomLevel << "perspective:" << perspective << "ds:" << ds << __FUNCTION__; d.matrix.setToIdentity(); //d.matrix.perspective(85.0f, 1.63f, 0.0f, 1.0f); // 확대시 화각 축소 d.matrix.perspective(perspective, d.rect.width() / (d.rect.height() * 0.75), 0.0f, 1.0); // 확대시 화각 축소 d.matrix.lookAt(QVector3D(0,SPHERE_SCALE*ds,0),QVector3D(x,y,z),QVector3D(0,td,0)); } #endif // #if (TOP_DOWN_2D_360) } /* void OpenGLVideo::setAngle(double a) { DPTR_D(OpenGLVideo); #if (HEMISPHERE_3) if(d.mode == 2) { if(a>1.0) { a = a - 1.0; } else if (a < 0.0) { a = 1.0 + a; } } #endif d.dAngle = a; #if (HEMISPHERE_3) if(d.mode == 2) { d.update_geo = true; float aa = ((d.dAngle * 360) - 180) * 20.0 * atan (1.0) / 180.0; //qInfo() << "d.dAngle" << aa; float x = cos(aa); float y = sin(aa); //qInfo() << "(d.dAngle * 360):" << (d.dAngle * 360) << ":" << x << ":" << y; qInfo() << "(dPitch):" << d.dPitch; //const float h = 18.0 + d.dPitch; const float h = d.dPitch - 15; d.matrix.setToIdentity(); d.matrix.perspective(85.0f, 1.63f, 0.0f, 1.0f); // for sphere d.matrix.lookAt(QVector3D(0,0,0.0),QVector3D(x,h,y),QVector3D(0,1.0,0)); } #endif } double OpenGLVideo::angle() { DPTR_D(OpenGLVideo); return d.dAngle; } void OpenGLVideo::setPitch(double a) { DPTR_D(OpenGLVideo); // // ZOOM 이 아닐 경우 상하단 이동 제한 // double limit = d.bZoom ? 1.0 : 0.2; // if(a>limit) // { // a = limit; // } // else if (a < -limit) // { // a = -limit; // } d.dPitch = a; setAngle(d.dAngle); } double OpenGLVideo::pitch() { DPTR_D(OpenGLVideo); return d.dPitch; } */ void OpenGLVideo::setMode(int mode) { DPTR_D(OpenGLVideo); //qInfo() << "MODE:" << mode << __FUNCTION__; #if (HEMISPHERE_3) #if (REMOVE_360_WIDE_SINGLE) if(d.mode == 2 || mode == 2) // VR #else // REMOVE_360_WIDE_SINGLE if(d.mode == 3 || mode == 3) // VR #endif // REMOVE_360_WIDE_SINGLE { d.mode = mode; #if (TOP_DOWN_2D_360) setMeshType(RectMesh); #else // TOP_DOWN_2D_360 #if (REMOVE_360_WIDE_SINGLE) setMeshType(mode == 2 ? SphereMesh : RectMesh); #else setMeshType(mode == 3 ? SphereMesh : RectMesh); #endif #endif // TOP_DOWN_2D_360 //setAngle(d.dAngle); //setAngle(d.dAngleX,d.dAngleY); d.update_geo = true; } #endif // HEMISPHERE_3 d.mode = mode; #if (USE_POINT_ZOOM) zoomLevel2D = 0; centerZoom = QPointF(0.5,0.5); #endif #if (!USE_POINT_ZOOM || USE_HYBRID_ZOOM) bROI = false; #endif resetAngle(); //setAngle(0,0); } int OpenGLVideo::mode() { DPTR_D(OpenGLVideo); return d.mode; } void OpenGLVideo::resetGL() { qDebug("~~~~~~~~~resetGL %p. from sender %p", d_func().manager, sender()); d_func().resetGL(); } #elif (MODEL_360) // T&F DUAL 360 #define TEST_EQUIRECTANGULAR 0 #define TUNE_360_FIXED 1 static const float T360_FHD[] = {-0.0358f,0.042f,-0.00225f,0.03625f, 0.0464f,0.006f,0.0f,0.0f, -0.1192f,0.012f,-0.0025f,0.1695f}; static const float T360_FHD_SD[] = {0.0976f,0.0412f,0.0f,0.04475f, 0.0592f,0.0f,0.0f,0.0f, 0.0f,0.0154f,0.15825f,0.5f}; static const QMatrix4x4 M360_FHS = QMatrix4x4(T360_FHD); static const QMatrix4x4 M360_SD = QMatrix4x4(T360_FHD_SD); // FIXME: why crash if inherits both QObject and DPtrPrivate? class OpenGLVideoPrivate : public DPtrPrivate { public: OpenGLVideoPrivate() : ctx(0) , manager(0) , material(new VideoMaterial()) , material_type(0) , norm_viewport(true) , update_geo(true) , tex_target(0) , valiad_tex_width(1.0) #if (TEST_EQUIRECTANGULAR) , mesh_type(OpenGLVideo::RectMesh) // OpenGLVideo::RectMesh,SphereMesh #else , mesh_type(OpenGLVideo::SphereMesh) // OpenGLVideo::RectMesh,SphereMesh #endif , geometry(NULL) //, geometry2(NULL) , user_shader(NULL) , dAngle(0.5) , dPitch(0.0) , bZoom(false) , dualMode(0) , resolutionMode(0) , bDualFront(true) // dual front { tune360 = M360_SD;//QMatrix4x4(T360_FHD_SD); // T360_FHD // T360_FHD //#else //#if(TUNE4) // // [-0.035800,0.048400,-0.002250,0.000000,0.046400,0.022000,0.000000,0.000000,-0.112000,0.008600]; // float tv[] = {0.0f,0.0f,0.0f,0.0f, // 0.0f,0.0f,0.0f,0.0f, // 0.0f,0.0f,0.0f,0.0f}; //#else // float tv[] = {0.077400f,-0.002600f,-0.095500f,0.045000f,0.002000f,-0.104500f,0.000000f,0.000000f,0.000000f}; //#endif // tune360 = QMatrix4x4(tv); //#endif //uvOffset = QVector4D(0,0,0,0); //#endif } ~OpenGLVideoPrivate() { if (material) { delete material; material = 0; } delete geometry; } // 0~360 = 시계방향 double dAngle; double dPitch; // 상하각 bool bZoom; bool bDualFront; // Dual Mode 에서 전방 int resolutionMode; void resetGL() { ctx = 0; gr.updateGeometry(NULL); if (!manager) return; manager->setParent(0); delete manager; manager = 0; if (material) { delete material; material = 0; } } // update geometry(vertex array) set attributes or bind VAO/VBO. void updateGeometry(VideoShader* shader, const QRectF& t, const QRectF& r); public: QOpenGLContext *ctx; ShaderManager *manager; VideoMaterial *material; qint64 material_type; bool norm_viewport; bool has_a; bool update_geo; int tex_target; qreal valiad_tex_width; QSize video_size; QRectF target; QRectF roi; //including invalid padding width OpenGLVideo::MeshType mesh_type; TexturedGeometry *geometry; GeometryRenderer gr; QRectF rect; QMatrix4x4 matrix; VideoShader *user_shader; QMatrix4x4 tune360; QMatrix4x4 dual360; int dualMode; }; void OpenGLVideoPrivate::updateGeometry(VideoShader* shader, const QRectF &t, const QRectF &r) { // also check size change for normalizedROI computation if roi is not normalized const bool roi_changed = valiad_tex_width != material->validTextureWidth() || roi != r || video_size != material->frameSize(); const int tc = shader->textureLocationCount(); if (roi_changed) { roi = r; valiad_tex_width = material->validTextureWidth(); video_size = material->frameSize(); } if (tex_target != shader->textureTarget()) { tex_target = shader->textureTarget(); update_geo = true; } // (-1, -1, 2, 2) must flip y QRectF target_rect = norm_viewport ? QRectF(-1, 1, 2, -2) : rect; if (target.isValid()) { if (roi_changed || target != t) { target = t; update_geo = true; //target_rect = target (if valid). // relate to gvf bug? } } else { if (roi_changed) { update_geo = true; } } if (!update_geo) { return; } delete geometry; geometry = NULL; if (mesh_type == OpenGLVideo::SphereMesh) { geometry = new Sphere(); geometry->setTextureCount(shader->textureTarget() == GL_TEXTURE_RECTANGLE ? tc : 1); geometry->setGeometryRect(target_rect); // 좌측 (=-2) geometry->setTextureRect(QRectF(0,0,1.0,1.0)); // 전방 SRC (고정) } else { geometry = new TexturedGeometry(); geometry->setTextureCount(shader->textureTarget() == GL_TEXTURE_RECTANGLE ? tc : 1); geometry->setGeometryRect(target_rect); //qInfo() << target_rect; if(dualMode == 1) { geometry->setTextureRect(material->mapToTexture(0, roi)); } else { geometry->setTextureRect(QRectF(0.0,0.0,1.0,1.0)); } // if (shader->textureTarget() == GL_TEXTURE_RECTANGLE) { // for (int i = 1; i < tc; ++i) { // // tc can > planes, but that will compute chroma plane // geometry->setTextureRect(material->mapToTexture(i, roi), i); // } // } } //qDebug("updating geometry..."); // setTextureCount may change the vertex data. Call it before setRect() //qDebug() << "target rect: " << target_rect ; // 동작하지 않음... geometry->create(); update_geo = false; gr.updateGeometry(geometry); } OpenGLVideo::OpenGLVideo() {} QMatrix4x4& OpenGLVideo::tune360() { DPTR_D(OpenGLVideo); return d.tune360; } void OpenGLVideo::setTune360(QMatrix4x4 m) { DPTR_D(OpenGLVideo); d.tune360 = m; } #if (TUNE_360 && MODEL_360) void OpenGLVideo::setMode(int mode) { setMeshType(mode == 0 ? RectMesh : SphereMesh); } #endif // TUNE_360 && MODEL_360 void OpenGLVideo::setZoom(bool a) { DPTR_D(OpenGLVideo); d.bZoom = a; d.dPitch = 0.0; setAngle(d.dAngle); } bool OpenGLVideo::zoom() { DPTR_D(OpenGLVideo); return d.bZoom; } void OpenGLVideo::setPitch(double a) { DPTR_D(OpenGLVideo); // ZOOM 이 아닐 경우 상하단 이동 제한 double limit = d.bZoom ? 1.0 : 0.2; if(a>limit) { a = limit; } else if (a < -limit) { a = -limit; } d.dPitch = a; setAngle(d.dAngle); } double OpenGLVideo::pitch() { DPTR_D(OpenGLVideo); return d.dPitch; } void OpenGLVideo::setAngle(double a) { DPTR_D(OpenGLVideo); if(d.dualMode == 1) { return; } if(a>1.0) { a = a - 1.0; } else if (a < 0.0) { a = 1.0 + a; } d.dAngle = a; //d.update_geo = true; //float aa = ((d.dAngle * 360) - 90) * 4.0 * atan (1.0) / 180.0; float aa = ((d.dAngle * 360) - 270) * 4.0 * atan (1.0) / 180.0; //qInfo() << "d.dAngle" << aa; float x = cos(aa); float y = sin(aa); //qInfo() << "(d.dAngle * 360):" << (d.dAngle * 360) << ":" << x << ":" << y; float aspect = d.rect.width() / d.rect.height(); d.matrix.setToIdentity(); float zf = d.bZoom ? 45.0 : 90.0; // 45,90 -> 범위를 (105도로) 늘리면 상하 전체가 보임.. d.matrix.perspective(zf, aspect, 0.0f, 1.0f); // for sphere //float pitch = d.bZoom ? d.dPitch : 0.0; // 범위를 줄이고 상/하 이동 활성화 float pitch = d.dPitch;//d.bZoom ? d.dPitch : d.dPitch * 0.3; d.matrix.lookAt(QVector3D(0,0,0),QVector3D(x,pitch,y),QVector3D(0,1,0)); } double OpenGLVideo::angle() { DPTR_D(OpenGLVideo); return d.dAngle; } bool OpenGLVideo::isSupported(VideoFormat::PixelFormat pixfmt) { return pixfmt != VideoFormat::Format_RGB48BE && pixfmt != VideoFormat::Format_Invalid; } void OpenGLVideo::setOpenGLContext(QOpenGLContext *ctx) { DPTR_D(OpenGLVideo); if (d.ctx == ctx) return; qreal b = 0, c = 0, h = 0, s = 0; if (d.material) { b = d.material->brightness(); c = d.material->contrast(); h = d.material->hue(); s = d.material->saturation(); delete d.material; d.material = 0; } d.resetGL(); //TODO: is it ok to destroygl resources in another context? d.ctx = ctx; // Qt4: set to null in resetGL() if (!ctx) { return; } d.material = new VideoMaterial(); d.material->setBrightness(b); d.material->setContrast(c); d.material->setHue(h); d.material->setSaturation(s); #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) d.manager = ctx->findChild(QStringLiteral("__qtav_shader_manager")); QSizeF surfaceSize = ctx->surface()->size(); //qInfo() << "SURFACE SIZE:(0)" << surfaceSize; #if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0) surfaceSize *= ctx->screen()->devicePixelRatio(); #else surfaceSize *= qApp->devicePixelRatio(); //TODO: window()->devicePixelRatio() is the window screen's #endif #else QSizeF surfaceSize = QSizeF(ctx->device()->width(), ctx->device()->height()); #endif //qInfo() << "SURFACE SIZE:(2)" << surfaceSize; //qInfo() << "DEBUG_OPENGL(13)" << " ctx->surface():" << surfaceSize; // 1,1 //qInfo() << "DEBUG_OPENGL(12)" << " ctx->surface():" << ctx->surface()->size(); // 1,1 //qInfo() << "DEBUG_OPENGL(11)" << " ctx->screen():" << ctx->screen()->availableSize(); // 전체 화면 해상도 setProjectionMatrixToRect(QRectF(QPointF(), surfaceSize)); if (d.manager) return; // TODO: what if ctx is delete? #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) d.manager = new ShaderManager(ctx); QObject::connect(ctx, SIGNAL(aboutToBeDestroyed()), this, SLOT(resetGL()), Qt::DirectConnection); // direct to make sure there is a valid context. makeCurrent in window.aboutToBeDestroyed()? #else d.manager = new ShaderManager(this); #endif d.manager->setObjectName(QStringLiteral("__qtav_shader_manager")); /// get gl info here because context is current(qt ensure it) //const QByteArray extensions(reinterpret_cast(glGetString(GL_EXTENSIONS))); bool hasGLSL = QOpenGLShaderProgram::hasOpenGLShaderPrograms(); qDebug("OpenGL version: %d.%d hasGLSL: %d", ctx->format().majorVersion(), ctx->format().minorVersion(), hasGLSL); static bool sInfo = true; if (sInfo) { sInfo = false; qDebug("GL_VERSION: %s", DYGL(glGetString(GL_VERSION))); qDebug("GL_VENDOR: %s", DYGL(glGetString(GL_VENDOR))); qDebug("GL_RENDERER: %s", DYGL(glGetString(GL_RENDERER))); qDebug("GL_SHADING_LANGUAGE_VERSION: %s", DYGL(glGetString(GL_SHADING_LANGUAGE_VERSION))); /// check here with current context can ensure the right result. If the first check is in VideoShader/VideoMaterial/decoder or somewhere else, the context can be null bool v = OpenGLHelper::isOpenGLES(); qDebug("Is OpenGLES: %d", v); v = OpenGLHelper::isEGL(); qDebug("Is EGL: %d", v); const int glsl_ver = OpenGLHelper::GLSLVersion(); qDebug("GLSL version: %d", glsl_ver); v = OpenGLHelper::isPBOSupported(); qDebug("Has PBO: %d", v); v = OpenGLHelper::has16BitTexture(); qDebug("Has 16bit texture: %d", v); v = OpenGLHelper::hasRG(); qDebug("Has RG texture: %d", v); qDebug() << ctx->format(); } } QOpenGLContext* OpenGLVideo::openGLContext() { return d_func().ctx; } void OpenGLVideo::setCurrentFrame(const VideoFrame &frame) { d_func().material->setCurrentFrame(frame); d_func().has_a = frame.format().hasAlpha(); } void OpenGLVideo::setProjectionMatrixToRect(const QRectF &v) { setViewport(v); } void OpenGLVideo::setViewport(const QRectF &r) { DPTR_D(OpenGLVideo); d.rect = r; if (d.norm_viewport) { d.matrix.setToIdentity(); if (d.mesh_type == SphereMesh) { setAngle(d.dAngle); } } else { d.matrix.setToIdentity(); d.matrix.ortho(r); d.update_geo = true; // even true for target_rect != d.rect } // Mirrored relative to the usual Qt coordinate system with origin in the top left corner. //mirrored = mat(0, 0) * mat(1, 1) - mat(0, 1) * mat(1, 0) > 0; if (d.ctx && d.ctx == QOpenGLContext::currentContext()) { //qInfo() << "DEBUG_OPENGL(21)" << " glViewport" << d.rect << d.matrix; DYGL(glViewport(d.rect.x(), d.rect.y(), d.rect.width(), d.rect.height())); } } void OpenGLVideo::setBrightness(qreal value) { d_func().material->setBrightness(value); } void OpenGLVideo::setContrast(qreal value) { d_func().material->setContrast(value); } void OpenGLVideo::setHue(qreal value) { d_func().material->setHue(value); } void OpenGLVideo::setSaturation(qreal value) { d_func().material->setSaturation(value); } void OpenGLVideo::setUserShader(VideoShader *shader) { d_func().user_shader = shader; } VideoShader* OpenGLVideo::userShader() const { return d_func().user_shader; } void OpenGLVideo::setMeshType(MeshType value) { DPTR_D(OpenGLVideo); if (d.mesh_type == value) { return; } d.mesh_type = value; d.update_geo = true; d.matrix.setToIdentity(); if (d.mesh_type == SphereMesh && d.norm_viewport) { d.matrix.perspective(90.0f, 2.0f, 0.0f, 1.0f); } } OpenGLVideo::MeshType OpenGLVideo::meshType() const { return d_func().mesh_type; } void OpenGLVideo::fill(const QColor &color) { //DYGL(glClearColor(0.0, 1.0,0.0, 1.0)); DYGL(glClearColor(color.redF(), color.greenF(), color.blueF(), color.alphaF())); DYGL(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); } void OpenGLVideo::setDualMode(int mode) { DPTR_D(OpenGLVideo); setMeshType(mode == 0 ? SphereMesh : RectMesh); d.dualMode = mode; } void OpenGLVideo::setDualFront(bool front) { DPTR_D(OpenGLVideo); d.bDualFront = front; const float df[16] = {front ? 1.0 : 0.0,}; d.dual360 = QMatrix4x4(df); } void OpenGLVideo::render(const QRectF &target, const QRectF& roi, const QMatrix4x4& transform) { #if(TEST_EQUIRECTANGULAR) Q_UNUSED(transform) #endif QRectF rroi = roi; DPTR_D(OpenGLVideo); Q_ASSERT(d.manager); Q_EMIT beforeRendering(); // viewport was used in gpu filters is wrong, qt quick fbo item's is right(so must ensure setProjectionMatrixToRect was called correctly) // 663x373, 1918x959 DYGL(glViewport(d.rect.x(), d.rect.y(), d.rect.width(), d.rect.height())); //qInfo() << "roi:" << roi; const qint64 mt = d.material->type(); if (d.material_type != mt) { #if !(OFF_OTHER_DEBUG) qDebug() << "material changed: " << VideoMaterial::typeName(d.material_type) << " => " << VideoMaterial::typeName(mt); #endif d.material_type = mt; } if (!d.material->bind()) // bind first because texture parameters(target) mapped from native buffer is unknown before it { return; } VideoShader *shader = d.user_shader; if (!shader) { d.manager->dualMode = d.dualMode; shader = d.manager->prepareMaterial(d.material, mt); //TODO: print shader type name if changed. prepareMaterial(,sample_code, pp_code) // 1920x2176, 1280x1440, 736x960 (2020/07/22 변경) if(d.material->frameSize().width() > 1281) // FHD { d.resolutionMode = 0; } else if(d.material->frameSize().width() > 736) // HD { d.resolutionMode = 1; } else // SD { // D1 1PIXEL 제거 d.resolutionMode = 2; rroi.setWidth(roi.width()-1.0f); // 신규 1.4 펌웨어에서도 발생 //qInfo() << "frame size:" << d.material->frameSize() << rroi; } } shader->update(d.material); //qInfo() << "VS" << shader->vertexShader(); //qInfo() << "----------------------------------\n" << shader->fragmentShader() << "-----------------------------\n"; //float fr = d.rect.height() / d.rect.width(); //float r = d.material->frameSize().height() / d.material->frameSize().width(); QMatrix4x4 m(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1); //d.matrix.lookAt(QVector3D(0,0,0),QVector3D(0,0,1),QVector3D(0,1,0)); //m.scale(QVector3D(0.7f,0.7f,1)); //m.flipCoordinates(); //m.setToIdentity(); //qInfo() << "target999:" << target; //d.matrix.perspective(45.0f, 1.0f, 0.1f, 100.0f); //qInfo() << d.matrix; //m.perspective(70.0f, 1.0f, 0.1f, 100.0f); //shader->program()->setUniformValue(shader->matrixLocation(), transform*d.matrix); shader->program()->setUniformValue(shader->resolutionLocation(), d.resolutionMode); if(d.dualMode) { shader->program()->setUniformValue(shader->matrixLocation(), transform*d.matrix); // transform*d.matrix } else { shader->program()->setUniformValue(shader->matrixLocation(), m*d.matrix); // transform*d.matrix } d.updateGeometry(shader, target, rroi); #if (TUNE_360 && MODEL_360) //shader->program()->setUniformValue(shader->uvOffsetLocation(),d.uvOffset); //shader->program()->setUniformValue(shader->radiusOffsetLocation(),d.radiusOffset); #endif if(d.dualMode) { shader->program()->setUniformValue(shader->tune360Location(),d.dual360); } else { #if (TUNE_360_FIXED) shader->program()->setUniformValue(shader->tune360Location(),d.resolutionMode == 2 ? M360_SD : M360_FHS); #else shader->program()->setUniformValue(shader->tune360Location(),d.tune360); #endif } //d.geometry->dumpVertexData(); // normalize? // const bool blending = d.has_a; // if (blending) { // DYGL(glEnable(GL_BLEND)); // gl().BlendFuncSeparate(GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA); // // } //if (d.mesh_type == OpenGLVideo::SphereMesh) //DYGL(glEnable(GL_CULL_FACE)); // required for sphere! FIXME: broken in qml and qgvf //shader->program()->setUniformValue(shader->yOffsetLocation(), 0.0f); // transform*d.matrix d.gr.render(); // shader->program()->setUniformValue(shader->yOffsetLocation(), 0.5f); // transform*d.matrix // d.gr2.render(); // if (blending) // DYGL(glDisable(GL_BLEND)); // d.shader->program()->release(); //glUseProgram(0) d.material->unbind(); Q_EMIT afterRendering(); } void OpenGLVideo::resetGL() { qDebug("~~~~~~~~~resetGL %p. from sender %p", d_func().manager, sender()); d_func().resetGL(); } #else // FIXME: why crash if inherits both QObject and DPtrPrivate? class OpenGLVideoPrivate : public DPtrPrivate { public: OpenGLVideoPrivate() : ctx(0) , manager(0) , material(new VideoMaterial()) , material_type(0) , norm_viewport(true) , update_geo(true) , tex_target(0) , valiad_tex_width(1.0) , mesh_type(OpenGLVideo::RectMesh) , geometry(NULL) , user_shader(NULL) #if (SUPPORT_WIDE_MODE) , mode(0) , bVFlip(false) , bHFlip(false) #endif // SUPPORT_WIDE_MODE { } ~OpenGLVideoPrivate() { if (material) { delete material; material = 0; } delete geometry; } void resetGL() { ctx = 0; gr.updateGeometry(NULL); if (!manager) return; manager->setParent(0); delete manager; manager = 0; if (material) { delete material; material = 0; } } // update geometry(vertex array) set attributes or bind VAO/VBO. void updateGeometry(VideoShader* shader, const QRectF& t, const QRectF& r); public: QOpenGLContext *ctx; ShaderManager *manager; VideoMaterial *material; qint64 material_type; bool norm_viewport; bool has_a; bool update_geo; int tex_target; qreal valiad_tex_width; QSize video_size; QRectF target; QRectF roi; //including invalid padding width OpenGLVideo::MeshType mesh_type; TexturedGeometry *geometry; GeometryRenderer gr; QRectF rect; QMatrix4x4 matrix; VideoShader *user_shader; #if (SUPPORT_WIDE_MODE || USE_SHADER_FLIP) int mode; bool bVFlip; // 상하 반전 bool bHFlip; // 좌우반전 #endif // USE_SHADER_FLIP }; void OpenGLVideoPrivate::updateGeometry(VideoShader* shader, const QRectF &t, const QRectF &r) { // also check size change for normalizedROI computation if roi is not normalized const bool roi_changed = valiad_tex_width != material->validTextureWidth() || roi != r || video_size != material->frameSize(); const int tc = shader->textureLocationCount(); if (roi_changed) { roi = r; valiad_tex_width = material->validTextureWidth(); video_size = material->frameSize(); } if (tex_target != shader->textureTarget()) { tex_target = shader->textureTarget(); update_geo = true; } #if (SUPPORT_WIDE_MODE || USE_SHADER_FLIP) qreal top = bVFlip ? -1 : 1; qreal bottom = bVFlip ? 2: -2; qreal left = bHFlip ? 1 : -1; qreal right = bHFlip ? -2 : 2; const QRectF gp = QRectF(left,top,right,bottom); QRectF target_rect = norm_viewport ? gp : rect; #else // SUPPORT_WIDE_MODE // (-1, -1, 2, 2) must flip y QRectF target_rect = norm_viewport ? QRectF(-1, 1, 2, -2) : rect; #endif // SUPPORT_WIDE_MODE if (target.isValid()) { if (roi_changed || target != t) { target = t; update_geo = true; //target_rect = target (if valid). // relate to gvf bug? } } else { if (roi_changed) { update_geo = true; } } if (!update_geo) return; delete geometry; geometry = NULL; if (mesh_type == OpenGLVideo::SphereMesh) geometry = new Sphere(); else geometry = new TexturedGeometry(); //qDebug("updating geometry..."); // setTextureCount may change the vertex data. Call it before setRect() qDebug() << "target rect: " << target_rect ; geometry->setTextureCount(shader->textureTarget() == GL_TEXTURE_RECTANGLE ? tc : 1); geometry->setGeometryRect(target_rect); geometry->setTextureRect(material->mapToTexture(0, roi)); if (shader->textureTarget() == GL_TEXTURE_RECTANGLE) { for (int i = 1; i < tc; ++i) { // tc can > planes, but that will compute chroma plane geometry->setTextureRect(material->mapToTexture(i, roi), i); } } geometry->create(); update_geo = false; gr.updateGeometry(geometry); } OpenGLVideo::OpenGLVideo() {} bool OpenGLVideo::isSupported(VideoFormat::PixelFormat pixfmt) { return pixfmt != VideoFormat::Format_RGB48BE && pixfmt != VideoFormat::Format_Invalid; } void OpenGLVideo::setOpenGLContext(QOpenGLContext *ctx) { DPTR_D(OpenGLVideo); if (d.ctx == ctx) return; qreal b = 0, c = 0, h = 0, s = 0; if (d.material) { b = d.material->brightness(); c = d.material->contrast(); h = d.material->hue(); s = d.material->saturation(); delete d.material; d.material = 0; } d.resetGL(); //TODO: is it ok to destroygl resources in another context? d.ctx = ctx; // Qt4: set to null in resetGL() if (!ctx) { return; } d.material = new VideoMaterial(); d.material->setBrightness(b); d.material->setContrast(c); d.material->setHue(h); d.material->setSaturation(s); #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) d.manager = ctx->findChild(QStringLiteral("__qtav_shader_manager")); QSizeF surfaceSize = ctx->surface()->size(); #if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0) surfaceSize *= ctx->screen()->devicePixelRatio(); #else surfaceSize *= qApp->devicePixelRatio(); //TODO: window()->devicePixelRatio() is the window screen's #endif #else QSizeF surfaceSize = QSizeF(ctx->device()->width(), ctx->device()->height()); #endif setProjectionMatrixToRect(QRectF(QPointF(), surfaceSize)); if (d.manager) return; // TODO: what if ctx is delete? #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) d.manager = new ShaderManager(ctx); QObject::connect(ctx, SIGNAL(aboutToBeDestroyed()), this, SLOT(resetGL()), Qt::DirectConnection); // direct to make sure there is a valid context. makeCurrent in window.aboutToBeDestroyed()? #else d.manager = new ShaderManager(this); #endif d.manager->setObjectName(QStringLiteral("__qtav_shader_manager")); /// get gl info here because context is current(qt ensure it) //const QByteArray extensions(reinterpret_cast(glGetString(GL_EXTENSIONS))); bool hasGLSL = QOpenGLShaderProgram::hasOpenGLShaderPrograms(); qDebug("OpenGL version: %d.%d hasGLSL: %d", ctx->format().majorVersion(), ctx->format().minorVersion(), hasGLSL); static bool sInfo = true; if (sInfo) { sInfo = false; qDebug("GL_VERSION: %s", DYGL(glGetString(GL_VERSION))); qDebug("GL_VENDOR: %s", DYGL(glGetString(GL_VENDOR))); qDebug("GL_RENDERER: %s", DYGL(glGetString(GL_RENDERER))); qDebug("GL_SHADING_LANGUAGE_VERSION: %s", DYGL(glGetString(GL_SHADING_LANGUAGE_VERSION))); /// check here with current context can ensure the right result. If the first check is in VideoShader/VideoMaterial/decoder or somewhere else, the context can be null bool v = OpenGLHelper::isOpenGLES(); qDebug("Is OpenGLES: %d", v); v = OpenGLHelper::isEGL(); qDebug("Is EGL: %d", v); const int glsl_ver = OpenGLHelper::GLSLVersion(); qDebug("GLSL version: %d", glsl_ver); v = OpenGLHelper::isPBOSupported(); qDebug("Has PBO: %d", v); v = OpenGLHelper::has16BitTexture(); qDebug("Has 16bit texture: %d", v); v = OpenGLHelper::hasRG(); qDebug("Has RG texture: %d", v); qDebug() << ctx->format(); } } QOpenGLContext* OpenGLVideo::openGLContext() { return d_func().ctx; } void OpenGLVideo::setCurrentFrame(const VideoFrame &frame) { d_func().material->setCurrentFrame(frame); d_func().has_a = frame.format().hasAlpha(); } void OpenGLVideo::setProjectionMatrixToRect(const QRectF &v) { setViewport(v); } void OpenGLVideo::setViewport(const QRectF &r) { DPTR_D(OpenGLVideo); d.rect = r; if (d.norm_viewport) { d.matrix.setToIdentity(); if (d.mesh_type == SphereMesh) d.matrix.perspective(45.0f, 1.0f, 0.1f, 100.0f); // for sphere } else { d.matrix.setToIdentity(); d.matrix.ortho(r); d.update_geo = true; // even true for target_rect != d.rect } // Mirrored relative to the usual Qt coordinate system with origin in the top left corner. //mirrored = mat(0, 0) * mat(1, 1) - mat(0, 1) * mat(1, 0) > 0; if (d.ctx && d.ctx == QOpenGLContext::currentContext()) { DYGL(glViewport(d.rect.x(), d.rect.y(), d.rect.width(), d.rect.height())); } } void OpenGLVideo::setBrightness(qreal value) { d_func().material->setBrightness(value); } void OpenGLVideo::setContrast(qreal value) { d_func().material->setContrast(value); } void OpenGLVideo::setHue(qreal value) { d_func().material->setHue(value); } void OpenGLVideo::setSaturation(qreal value) { d_func().material->setSaturation(value); } void OpenGLVideo::setUserShader(VideoShader *shader) { d_func().user_shader = shader; } VideoShader* OpenGLVideo::userShader() const { return d_func().user_shader; } void OpenGLVideo::setMeshType(MeshType value) { DPTR_D(OpenGLVideo); if (d.mesh_type == value) return; d.mesh_type = value; d.update_geo = true; if (d.mesh_type == SphereMesh && d.norm_viewport) { d.matrix.setToIdentity(); d.matrix.perspective(45.0f, 1.0f, 0.1f, 100.0f); // for sphere } } OpenGLVideo::MeshType OpenGLVideo::meshType() const { return d_func().mesh_type; } void OpenGLVideo::fill(const QColor &color) { DYGL(glClearColor(color.redF(), color.greenF(), color.blueF(), color.alphaF())); DYGL(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); } #if (SUPPORT_WIDE_MODE) bool OpenGLVideo::isVFlip() { DPTR_D(OpenGLVideo); return d.bVFlip; } void OpenGLVideo::setHFlip(bool flip) { DPTR_D(OpenGLVideo); d.bHFlip = flip; d.update_geo = true; } bool OpenGLVideo::isHFlip() { DPTR_D(OpenGLVideo); return d.bHFlip; } void OpenGLVideo::setVFlip(bool flip) { DPTR_D(OpenGLVideo); d.bVFlip = flip; d.update_geo = true; } int OpenGLVideo::mode() { DPTR_D(OpenGLVideo); return d.mode; } void OpenGLVideo::setMode(int mode) { DPTR_D(OpenGLVideo); d.update_geo = true; //? d.mode = mode; } #endif // SUPPORT_WIDE_MODE void OpenGLVideo::render(const QRectF &target, const QRectF& roi, const QMatrix4x4& transform) { DPTR_D(OpenGLVideo); Q_ASSERT(d.manager); Q_EMIT beforeRendering(); DYGL(glViewport(d.rect.x(), d.rect.y(), d.rect.width(), d.rect.height())); // viewport was used in gpu filters is wrong, qt quick fbo item's is right(so must ensure setProjectionMatrixToRect was called correctly) const qint64 mt = d.material->type(); if (d.material_type != mt) { #if !(OFF_OTHER_DEBUG) qDebug() << "material changed: " << VideoMaterial::typeName(d.material_type) << " => " << VideoMaterial::typeName(mt); #endif d.material_type = mt; } if (!d.material->bind()) // bind first because texture parameters(target) mapped from native buffer is unknown before it { return; } VideoShader *shader = d.user_shader; if (!shader) { shader = d.manager->prepareMaterial(d.material, mt); //TODO: print shader type name if changed. prepareMaterial(,sample_code, pp_code) } shader->update(d.material); shader->program()->setUniformValue(shader->matrixLocation(), transform*d.matrix); #if (SUPPORT_WIDE_MODE) shader->program()->setUniformValue(shader->modeLocation(),d.mode); float aspect = (float)(16.0/9.0);//(float)(d.rect.width()) / (float)(d.rect.height()); shader->program()->setUniformValue(shader->aspectLocation(),aspect); #endif // SUPPORT_WIDE_MODE // uniform end. attribute begin //#if (REMOVE_RIGHT_GREEN_LINE) // QRectF rroi = roi; // rroi.setWidth(roi.width()-1.0f); // d.updateGeometry(shader, target, rroi); //#else d.updateGeometry(shader, target, roi); //#endif // normalize? const bool blending = d.has_a; if (blending) { DYGL(glEnable(GL_BLEND)); gl().BlendFuncSeparate(GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA); // } //if (d.mesh_type == OpenGLVideo::SphereMesh) //DYGL(glEnable(GL_CULL_FACE)); // required for sphere! FIXME: broken in qml and qgvf d.gr.render(); if (blending) DYGL(glDisable(GL_BLEND)); // d.shader->program()->release(); //glUseProgram(0) d.material->unbind(); Q_EMIT afterRendering(); } void OpenGLVideo::resetGL() { qDebug("~~~~~~~~~resetGL %p. from sender %p", d_func().manager, sender()); d_func().resetGL(); } #endif // #if (MODEL_360) } //namespace FAV