Я знаю, что в общем случае необходим минимальный полный и проверяемый пример, но этот пример не может быть разбит на меньший, потому что слишком много библиотек необходимо для получения и декодирования видео с моей камеры безопасности.Я надеюсь, что кто-то может помочь мне, указав возможные простые ошибки при рендеринге с использованием QQuickFrameBufferObject.
Две проблемы, с которыми я сталкиваюсь, состоят в том, чтоизображения являются КРАСНЫМИ, а изображение одного объекта вторгается в пространство другого объекта очень необъяснимым образом. На изображении выше вы можете видеть, что должно быть 4 различных канала камеры, которые создаются с помощью QML.
Вот класс, который отображает все.Данные YUV420P подаются с использованием функции update()
.Вы можете увидеть простой шейдер, который декодирует YUV420P в RGB.Каждый объект QML (поток камеры) является одним экземпляром из этого класса.
OpenGlBufferQtQuick.cpp :
#include "OpenGlBufferQtQuick.h"
#include <QOpenGLFramebufferObjectFormat>
#include <QRunnable>
#include <QEventLoop>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QMutexLocker>
#include <memory>
#include <iostream>
#include <QTimer>
#define GET_STR(x) #x
#define A_VER 3
#define T_VER 4
static const GLfloat ver[] = {
-1.0f,-1.0f,
1.0f,-1.0f,
-1.0f, 1.0f,
1.0f, 1.0f
};
static const GLfloat tex[] = {
0.0f, 1.0f,
1.0f, 1.0f,
0.0f, 0.0f,
1.0f, 0.0f
};
//Simple shader. Outpus the same location as input, I guess
const char *vString3 = GET_STR(
attribute vec4 vertexIn;
attribute vec2 textureIn;
varying vec2 textureOut;
uniform mat4 u_transform;
void main(void)
{
gl_Position = u_transform * vertexIn;
textureOut = textureIn;
}
);
//The matrix below does YUV420P to RGB conversion https://en.wikipedia.org/wiki/YUV#Y%E2%80%B2UV420p_(and_Y%E2%80%B2V12_or_YV12)_to_RGB888_conversion
//This texture shader replaces the color of the pixel with the new color, but in RGB. (I guess)
const char *tString3 = GET_STR(
varying vec2 textureOut;
uniform sampler2D tex_y;
uniform sampler2D tex_u;
uniform sampler2D tex_v;
void main(void)
{
vec3 yuv;
vec3 rgb;
yuv.x = texture2D(tex_y, textureOut).r;
yuv.y = texture2D(tex_u, textureOut).r - 0.5;
yuv.z = texture2D(tex_v, textureOut).r - 0.5;
rgb = mat3(1.0, 1.0, 1.0,
0.0, -0.39465, 2.03211,
1.13983, -0.58060, 0.0) * yuv;
gl_FragColor = vec4(rgb, 1.0);
}
);
OpenGlBufferItemRenderer::OpenGlBufferItemRenderer(string uri){
this->uri = uri;
}
void OpenGlBufferItemRenderer::render() {
QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
if (firstFrameReceived) {
if (this->firstRender) {
std::cout << "Creating QOpenGLShaderProgram " << std::endl;
program = new QOpenGLShaderProgram();
f->initializeOpenGLFunctions();
//this->m_F = QOpenGLContext::currentContext()->functions();
std::cout << "frameWidth: " << frameWidth << + " frameHeight: " << frameHeight << std::endl;
std::cout << "Fragment Shader compilation: " << program->addShaderFromSourceCode(QOpenGLShader::Fragment, tString3) << std::endl;
std::cout << "Vertex Shader compilation: " << program->addShaderFromSourceCode(QOpenGLShader::Vertex, vString3) << std::endl;
program->bindAttributeLocation("vertexIn",A_VER);
program->bindAttributeLocation("textureIn",T_VER);
std::cout << "program->link() = " << program->link() << std::endl;
f->glGenTextures(3, texs);//TODO: ERASE THIS WITH glDeleteTextures
this->firstRender = false;
}
// Not strictly needed for this example, but generally useful for when
// mixing with raw OpenGL.
//m_window->resetOpenGLState();//COMMENT OR NOT?
program->bind();
QMatrix4x4 transform;
transform.setToIdentity();
program->setUniformValue("u_transform", transform);
f->glVertexAttribPointer(A_VER, 2, GL_FLOAT, 0, 0, ver);
f->glEnableVertexAttribArray(A_VER);
f->glVertexAttribPointer(T_VER, 2, GL_FLOAT, 0, 0, tex);
f->glEnableVertexAttribArray(T_VER);
unis[0] = program->uniformLocation("tex_y");
unis[1] = program->uniformLocation("tex_u");
unis[2] = program->uniformLocation("tex_v");
//Y
f->glBindTexture(GL_TEXTURE_2D, texs[0]);
f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
f->glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, frameWidth, frameHeight, 0, GL_RED, GL_UNSIGNED_BYTE, 0);
//U
f->glBindTexture(GL_TEXTURE_2D, texs[1]);
f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
f->glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, frameWidth/2, frameHeight / 2, 0, GL_RED, GL_UNSIGNED_BYTE, 0);
//V
f->glBindTexture(GL_TEXTURE_2D, texs[2]);
f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
f->glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, frameWidth / 2, frameHeight / 2, 0, GL_RED, GL_UNSIGNED_BYTE, 0);
f->glActiveTexture(GL_TEXTURE0);
f->glBindTexture(GL_TEXTURE_2D, texs[0]);
f->glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, frameWidth, frameHeight, GL_RED, GL_UNSIGNED_BYTE, datas[0]);
f->glUniform1i(unis[0], 0);
f->glActiveTexture(GL_TEXTURE0+1);
f->glBindTexture(GL_TEXTURE_2D, texs[1]);
f->glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, frameWidth/2, frameHeight / 2, GL_RED, GL_UNSIGNED_BYTE, datas[1]);
f->glUniform1i(unis[1],1);
f->glActiveTexture(GL_TEXTURE0+2);
f->glBindTexture(GL_TEXTURE_2D, texs[2]);
f->glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, frameWidth / 2, frameHeight / 2, GL_RED, GL_UNSIGNED_BYTE, datas[2]);
f->glUniform1i(unis[2], 2);
f->glDrawArrays(GL_TRIANGLE_STRIP,0,4);
program->disableAttributeArray(A_VER);
program->disableAttributeArray(T_VER);
program->release();
}
update();
}
QOpenGLFramebufferObject *OpenGlBufferItemRenderer::createFramebufferObject(const QSize &size)
{
QOpenGLFramebufferObjectFormat format;
format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
//format.setSamples(16);
return new QOpenGLFramebufferObject(size, format);
}
//https://blog.qt.io/blog/2015/05/11/integrating-custom-opengl-rendering-with-qt-quick-via-qquickframebufferobject/
void OpenGlBufferItemRenderer::synchronize(QQuickFramebufferObject *item)
{
OpenGlBufferItem *openGlBufferItem = static_cast<OpenGlBufferItem*>(item);
std::cout << "synchronize called " << std::endl;
std::cout << "starting new renderer for uri " << this-> uri << std::endl;
MediaStream* camera1 = new MediaStream(this->uri);
camera1->setFrameUpdater((FrameUpdater *) this);
//TODO: put mutex on std::cout of this thread
//TODO: make this thread actualy run here instead of on a thread, I guess.
boost::thread mediaThread(&MediaStream::run, camera1);
}
OpenGlBufferItem::OpenGlBufferItem(){}
void OpenGlBufferItemRenderer::updateData(unsigned char**data, int frameWidth, int frameHeight)
{
this->frameWidth = frameWidth;
this->frameHeight = frameHeight;
//Before first render, datas pointer isn't even created yet
if (!firstFrameReceived) {
datas[0] = new unsigned char[frameWidth*frameHeight]; //Y
datas[1] = new unsigned char[frameWidth*frameHeight/4]; //U
datas[2] = new unsigned char[frameWidth*frameHeight/4]; //V
firstFrameReceived = true;
} else {
memcpy(datas[0], data[0], frameWidth*frameHeight);
memcpy(datas[1], data[1], frameWidth*frameHeight/4);
memcpy(datas[2], data[2], frameWidth*frameHeight/4);
}
}
QQuickFramebufferObject::Renderer *OpenGlBufferItem::createRenderer() const
{
//std::cout << "createRenderer called ------------------------" << std::endl;
return new OpenGlBufferItemRenderer(this->uri);
}
Вот main.qml :
import QtQuick 2.0
import OpenGlBufferQtQuick 1.0
Grid {
columns: 2
spacing: 2
width: 1280
height: 720
OpenGlBufferQtQuick {
width: 640
height: 360
uri: "rtsp://admin:123456@192.168.0.103:10554/tcp/av0_0"
}
OpenGlBufferQtQuick {
width: 640
height: 360
uri: "rtsp://admin:123456@192.168.0.101:10554/tcp/av0_0"
}
OpenGlBufferQtQuick {
width: 640
height: 360
uri: "rtsp://admin:123456@192.168.0.104:10554/tcp/av0_0"
}
OpenGlBufferQtQuick {
width: 640
height: 360
uri: "rtsp://admin:123456@192.168.1.43:10554/tcp/av0_0"
}
}
Как видите, я вызываю 4 разных потока камеры, но поток с первой камеры вторгается в пространство других потоков, даже если каждый поток является совершенно другим объектом.
Также изображение КРАСНОЕ.Я использовал почти тот же код для рендеринга с использованием класса OpenGlVideoQtQuickRenderer : public QObject, protected QOpenGLFunctions
, и он работает без какого-либо красного экрана или opengl, вторгающегося в другое пространство.