1
0
mirror of https://github.com/qTox/qTox.git synced 2024-03-22 14:00:36 +08:00
qTox/src/widget/videosurface.cpp

273 lines
8.1 KiB
C++
Raw Normal View History

2014-07-07 00:19:45 +08:00
/*
Copyright (C) 2014 by Project Tox <https://tox.im>
This file is part of qTox, a Qt-based graphical interface for Tox.
This program is libre software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program 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 COPYING file for more details.
*/
2014-10-08 22:42:09 +08:00
#include "videosurface.h"
2014-10-24 04:07:44 +08:00
#include "src/video/camera.h"
2014-09-11 21:44:34 +08:00
#include <QTimer>
#include <QOpenGLBuffer>
#include <QOpenGLShaderProgram>
#include <QDebug>
2014-08-29 19:40:37 +08:00
2014-10-12 02:53:40 +08:00
VideoSurface::VideoSurface(QWidget* parent)
: QGLWidget(QGLFormat(QGL::SampleBuffers | QGL::SingleBuffer), parent)
2014-10-12 02:53:40 +08:00
, source(nullptr)
, pbo{nullptr, nullptr}
, textureId(0)
, pboAllocSize(0)
2014-10-08 20:25:32 +08:00
, hasSubscribed(false)
, pboIndex(0)
2014-10-12 02:53:40 +08:00
{
setAutoBufferSwap(false);
2014-10-12 02:53:40 +08:00
}
2014-10-15 20:46:01 +08:00
VideoSurface::VideoSurface(VideoSource *source, QWidget* parent)
2014-10-12 02:53:40 +08:00
: VideoSurface(parent)
2014-06-29 04:24:26 +08:00
{
2014-10-15 20:46:01 +08:00
setSource(source);
2014-06-29 04:24:26 +08:00
}
2014-10-08 22:42:09 +08:00
VideoSurface::~VideoSurface()
2014-06-29 04:24:26 +08:00
{
if (pbo[0])
{
delete pbo[0];
delete pbo[1];
}
if (textureId != 0)
glDeleteTextures(1, &textureId);
2014-10-07 22:59:21 +08:00
2014-10-15 20:46:01 +08:00
unsubscribe();
2014-10-12 02:53:40 +08:00
}
void VideoSurface::setSource(VideoSource *src)
{
2014-10-15 20:46:01 +08:00
if (source == src)
return;
2014-06-29 04:24:26 +08:00
unsubscribe();
2014-10-15 20:46:01 +08:00
source = src;
subscribe();
2014-10-12 02:53:40 +08:00
}
2014-10-08 22:42:09 +08:00
void VideoSurface::initializeGL()
2014-06-29 04:24:26 +08:00
{
qDebug() << "VideoSurface: Init";
// pbo
pbo[0] = new QOpenGLBuffer(QOpenGLBuffer::PixelUnpackBuffer);
pbo[0]->setUsagePattern(QOpenGLBuffer::StreamDraw);
pbo[0]->create();
pbo[1] = new QOpenGLBuffer(QOpenGLBuffer::PixelUnpackBuffer);
pbo[1]->setUsagePattern(QOpenGLBuffer::StreamDraw);
pbo[1]->create();
// shaders
bgrProgramm = new QOpenGLShaderProgram;
bgrProgramm->addShaderFromSourceCode(QOpenGLShader::Vertex,
"attribute vec4 vertices;"
"varying vec2 coords;"
"void main() {"
" gl_Position = vec4(vertices.xy,0.0,1.0);"
" coords = vertices.xy*vec2(0.5,0.5)+vec2(0.5,0.5);"
"}");
// brg frag-shader
bgrProgramm->addShaderFromSourceCode(QOpenGLShader::Fragment,
"uniform sampler2D texture0;"
"varying vec2 coords;"
"void main() {"
" vec4 color = texture2D(texture0,coords*vec2(1.0, -1.0));"
" gl_FragColor = vec4(color.b, color.g, color.r, 1);"
"}");
bgrProgramm->bindAttributeLocation("vertices", 0);
bgrProgramm->link();
2014-10-15 20:46:01 +08:00
// shaders
yuvProgramm = new QOpenGLShaderProgram;
yuvProgramm->addShaderFromSourceCode(QOpenGLShader::Vertex,
"attribute vec4 vertices;"
"varying vec2 coords;"
"void main() {"
" gl_Position = vec4(vertices.xy,0.0,1.0);"
" coords = vertices.xy*vec2(0.5,0.5)+vec2(0.5,0.5);"
"}");
2014-10-15 22:37:43 +08:00
// yuv frag-shader
2014-10-15 20:46:01 +08:00
yuvProgramm->addShaderFromSourceCode(QOpenGLShader::Fragment,
"uniform sampler2D texture0;"
"varying vec2 coords;"
"void main() {"
" vec3 yuv = texture2D(texture0,coords*vec2(1.0, -1.0)).rgb - vec3(0,0.5,0.5);"
2014-10-15 20:46:01 +08:00
" vec3 rgb = mat3(1,1,1,0,-0.21482,2.12798,1.28033,-0.38059,0) * yuv;"
" gl_FragColor = vec4(rgb,1);"
"}");
yuvProgramm->bindAttributeLocation("vertices", 0);
yuvProgramm->link();
2014-06-29 04:24:26 +08:00
}
2014-10-08 22:42:09 +08:00
void VideoSurface::paintGL()
{
mutex.lock();
VideoFrame currFrame = frame;
2014-10-24 04:31:39 +08:00
frame.invalidate();
mutex.unlock();
2014-10-07 22:59:21 +08:00
2014-10-24 04:31:39 +08:00
if (currFrame.isValid() && res != currFrame.resolution)
2014-10-07 22:59:21 +08:00
{
res = currFrame.resolution;
// delete old texture
if (textureId != 0)
glDeleteTextures(1, &textureId);
// a texture used to render the pbo (has the match the pixelformat of the source)
2014-10-07 22:59:21 +08:00
glGenTextures(1,&textureId);
glBindTexture(GL_TEXTURE_2D, textureId);
glTexImage2D(GL_TEXTURE_2D,0, GL_RGB, res.width(), res.height(), 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
2014-10-24 04:31:39 +08:00
if (currFrame.isValid())
{
pboIndex = (pboIndex + 1) % 2;
int nextPboIndex = (pboIndex + 1) % 2;
2014-10-08 20:25:32 +08:00
2014-10-15 20:46:01 +08:00
if (pboAllocSize != currFrame.frameData.size())
2014-10-08 20:25:32 +08:00
{
qDebug() << "VideoSurface: Resize pbo " << currFrame.frameData.size() << "(" << currFrame.resolution << ")" << "bytes (before" << pboAllocSize << ")";
2014-10-08 20:25:32 +08:00
pbo[0]->bind();
2014-10-15 20:46:01 +08:00
pbo[0]->allocate(currFrame.frameData.size());
pbo[0]->release();
pbo[1]->bind();
2014-10-15 20:46:01 +08:00
pbo[1]->allocate(currFrame.frameData.size());
pbo[1]->release();
2014-10-15 20:46:01 +08:00
pboAllocSize = currFrame.frameData.size();
}
pbo[pboIndex]->bind();
2014-10-08 20:25:32 +08:00
glBindTexture(GL_TEXTURE_2D, textureId);
glTexSubImage2D(GL_TEXTURE_2D,0,0,0, res.width(), res.height(), GL_RGB, GL_UNSIGNED_BYTE, 0);
pbo[pboIndex]->unmap();
pbo[pboIndex]->release();
2014-10-08 22:19:42 +08:00
// transfer data
pbo[nextPboIndex]->bind();
void* ptr = pbo[nextPboIndex]->map(QOpenGLBuffer::WriteOnly);
if (ptr)
2014-10-15 20:46:01 +08:00
memcpy(ptr, currFrame.frameData.data(), currFrame.frameData.size());
pbo[nextPboIndex]->unmap();
pbo[nextPboIndex]->release();
2014-10-08 20:25:32 +08:00
}
// render pbo
static float values[] = {
-1, -1,
1, -1,
-1, 1,
1, 1
};
// background
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
2014-10-12 02:53:40 +08:00
// keep aspect ratio
float aspectRatio = float(res.width()) / float(res.height());
if (width() < float(height()) * aspectRatio)
{
float h = float(width()) / aspectRatio;
glViewport(0, (height() - h)*0.5f, width(), h);
}
else
{
float w = float(height()) * float(aspectRatio);
glViewport((width() - w)*0.5f, 0, w, height());
}
2014-10-15 20:46:01 +08:00
QOpenGLShaderProgram* programm = nullptr;
switch (frame.format)
{
case VideoFrame::YUV:
programm = yuvProgramm;
break;
case VideoFrame::BGR:
programm = bgrProgramm;
break;
2014-10-15 22:54:48 +08:00
default:
break;
2014-10-15 20:46:01 +08:00
}
if (programm)
{
programm->bind();
programm->setAttributeArray(0, GL_FLOAT, values, 2);
programm->enableAttributeArray(0);
}
glBindTexture(GL_TEXTURE_2D, textureId);
//draw fullscreen quad
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
2014-10-08 20:25:32 +08:00
glBindTexture(GL_TEXTURE_2D, 0);
2014-10-15 20:46:01 +08:00
if (programm)
{
programm->disableAttributeArray(0);
programm->release();
}
}
void VideoSurface::subscribe()
{
if (source && !hasSubscribed)
{
source->subscribe();
hasSubscribed = true;
connect(source, &VideoSource::frameAvailable, this, &VideoSurface::onNewFrameAvailable);
}
}
void VideoSurface::unsubscribe()
{
if (source && hasSubscribed)
{
source->unsubscribe();
hasSubscribed = false;
disconnect(source, &VideoSource::frameAvailable, this, &VideoSurface::onNewFrameAvailable);
}
}
void VideoSurface::onNewFrameAvailable(const VideoFrame newFrame)
{
mutex.lock();
frame = newFrame;
mutex.unlock();
updateGL();
}
2014-10-12 02:53:40 +08:00