mirror of
https://github.com/outbackdingo/UltraGrid.git
synced 2026-03-20 14:40:10 +00:00
Now it would transitively depend on at least utils/{color_out,text},
which is perhaps not worth including.
294 lines
6.9 KiB
C++
294 lines
6.9 KiB
C++
#include <stdio.h>
|
|
#include <QMessageBox>
|
|
#include <QOpenGLFunctions_3_3_Core>
|
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
|
#include <QOpenGLVersionFunctionsFactory>
|
|
#endif
|
|
#include "previewWidget.hpp"
|
|
#include "debug.hpp"
|
|
|
|
static const GLfloat rectangle[] = {
|
|
1.0f, 1.0f, 1.0f, 0.0f,
|
|
-1.0f, 1.0f, 0.0f, 0.0f,
|
|
-1.0f, -1.0f, 0.0f, 1.0f,
|
|
|
|
1.0f, 1.0f, 1.0f, 0.0f,
|
|
-1.0f, -1.0f, 0.0f, 1.0f,
|
|
1.0f, -1.0f, 1.0f, 1.0f
|
|
};
|
|
|
|
static const char *vert_src = R"END(
|
|
#version 330 core
|
|
layout(location = 0) in vec2 vert_pos;
|
|
layout(location = 1) in vec2 vert_uv;
|
|
|
|
out vec2 UV;
|
|
|
|
uniform vec2 scale_vec;
|
|
|
|
void main(){
|
|
gl_Position = vec4(vert_pos * scale_vec, 0.0f, 1.0f);
|
|
UV = vert_uv;
|
|
}
|
|
)END";
|
|
|
|
static const char *frag_src = R"END(
|
|
#version 330 core
|
|
in vec2 UV;
|
|
out vec3 color;
|
|
uniform sampler2D tex;
|
|
void main(){
|
|
color = texture(tex, UV).rgb;
|
|
}
|
|
)END";
|
|
|
|
static void compileShader(GLuint shaderId, QOpenGLFunctions_3_3_Core *f){
|
|
f->glCompileShader(shaderId);
|
|
|
|
GLint ret = GL_FALSE;
|
|
int len;
|
|
|
|
f->glGetShaderiv(shaderId, GL_COMPILE_STATUS, &ret);
|
|
f->glGetShaderiv(shaderId, GL_INFO_LOG_LENGTH, &len);
|
|
if (len > 0){
|
|
std::vector<char> errorMsg(len+1);
|
|
f->glGetShaderInfoLog(shaderId, len, NULL, &errorMsg[0]);
|
|
printf("%s\n", errorMsg.data());
|
|
}
|
|
}
|
|
|
|
|
|
static unsigned char pixels[] = {
|
|
255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255,
|
|
255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255,
|
|
255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255,
|
|
255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255
|
|
};
|
|
|
|
static QOpenGLFunctions_3_3_Core *getOpenGLFuncs(){
|
|
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
|
return QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_3_3_Core>();
|
|
#else
|
|
return QOpenGLVersionFunctionsFactory::get<QOpenGLFunctions_3_3_Core>(QOpenGLContext::currentContext());
|
|
#endif
|
|
}
|
|
|
|
PreviewWidget::PreviewWidget(QWidget *parent) :
|
|
QOpenGLWidget(parent),
|
|
ipc_frame(ipc_frame_new())
|
|
{
|
|
connect(&timer, SIGNAL(timeout()), this, SLOT(update()));
|
|
}
|
|
|
|
PreviewWidget::~PreviewWidget(){
|
|
makeCurrent();
|
|
|
|
auto f = getOpenGLFuncs();
|
|
f->glDeleteBuffers(1, &vertexBuffer);
|
|
f->glDeleteProgram(program);
|
|
f->glDeleteTextures(1, &frame_texture);
|
|
f->glDeleteTextures(1, &testbars_texture);
|
|
vao.destroy();
|
|
doneCurrent();
|
|
}
|
|
|
|
void PreviewWidget::initializeGL(){
|
|
auto f = getOpenGLFuncs();
|
|
|
|
if(!f) {
|
|
QMessageBox warningBox(this);
|
|
warningBox.setText("OpenGL 3.3 is required!");
|
|
warningBox.exec();
|
|
exit(1);
|
|
}
|
|
|
|
vao.create();
|
|
vao.bind();
|
|
|
|
f->glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
|
|
|
f->glDisable(GL_DEPTH_TEST);
|
|
f->glDisable(GL_SCISSOR_TEST);
|
|
f->glDisable(GL_STENCIL_TEST);
|
|
|
|
GLuint vertexShader = f->glCreateShader(GL_VERTEX_SHADER);
|
|
GLuint fragShader = f->glCreateShader(GL_FRAGMENT_SHADER);
|
|
|
|
f->glShaderSource(vertexShader, 1, &vert_src, NULL);
|
|
compileShader(vertexShader, f);
|
|
f->glShaderSource(fragShader, 1, &frag_src, NULL);
|
|
compileShader(fragShader, f);
|
|
|
|
program = f->glCreateProgram();
|
|
f->glAttachShader(program, vertexShader);
|
|
f->glAttachShader(program, fragShader);
|
|
f->glLinkProgram(program);
|
|
f->glUseProgram(program);
|
|
|
|
f->glDetachShader(program, vertexShader);
|
|
f->glDetachShader(program, fragShader);
|
|
f->glDeleteShader(vertexShader);
|
|
f->glDeleteShader(fragShader);
|
|
|
|
f->glGenTextures(1, &testbars_texture);
|
|
f->glBindTexture(GL_TEXTURE_2D, testbars_texture);
|
|
f->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 4, 4, 0, GL_RGB, GL_UNSIGNED_BYTE, pixels);
|
|
f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
|
|
f->glGenTextures(1, &frame_texture);
|
|
f->glBindTexture(GL_TEXTURE_2D, frame_texture);
|
|
f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
f->glPixelStorei(GL_UNPACK_ALIGNMENT, 1); //UltraGrid formats dont have padding between rows
|
|
|
|
f->glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
f->glGenBuffers(1, &vertexBuffer);
|
|
f->glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
|
|
f->glBufferData(GL_ARRAY_BUFFER, sizeof(rectangle), rectangle, GL_STATIC_DRAW);
|
|
|
|
f->glEnableVertexAttribArray(0);
|
|
f->glVertexAttribPointer(
|
|
0,
|
|
2,
|
|
GL_FLOAT,
|
|
GL_FALSE,
|
|
4 * sizeof(float),
|
|
(void*)0
|
|
);
|
|
f->glEnableVertexAttribArray(1);
|
|
f->glVertexAttribPointer(
|
|
1,
|
|
2,
|
|
GL_FLOAT,
|
|
GL_FALSE,
|
|
4 * sizeof(float),
|
|
(void*)(2 * sizeof(float))
|
|
);
|
|
|
|
f->glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
|
|
vao.release();
|
|
|
|
scaleVec[0] = 0.75f;
|
|
scaleVec[1] = 0.5;
|
|
|
|
vidW = 1280;
|
|
vidH = 720;
|
|
}
|
|
|
|
void PreviewWidget::calculateScale(){
|
|
double videoAspect = (double) vidW / vidH;
|
|
double widgetAspect = (double) width / height;
|
|
|
|
if(videoAspect > widgetAspect){
|
|
float scale = widgetAspect / videoAspect;
|
|
scaleVec[0] = 1;
|
|
scaleVec[1] = scale;
|
|
} else {
|
|
float scale = videoAspect / widgetAspect;
|
|
scaleVec[0] = scale;
|
|
scaleVec[1] = 1;
|
|
}
|
|
}
|
|
|
|
void PreviewWidget::resizeGL(int w, int h){
|
|
width = w;
|
|
height = h;
|
|
|
|
calculateScale();
|
|
}
|
|
|
|
void PreviewWidget::setVidSize(int w, int h){
|
|
if(vidW == w && vidH == h)
|
|
return;
|
|
|
|
vidW = w;
|
|
vidH = h;
|
|
calculateScale();
|
|
}
|
|
|
|
bool PreviewWidget::loadFrame(){
|
|
auto f = getOpenGLFuncs();
|
|
|
|
if(!ipc_frame_reader || !ipc_frame_reader_is_connected(ipc_frame_reader.get())){
|
|
selected_texture = testbars_texture;
|
|
return true;
|
|
}
|
|
|
|
|
|
if(ipc_frame_reader_has_frame(ipc_frame_reader.get())){
|
|
if(!ipc_frame_reader_read(ipc_frame_reader.get(), ipc_frame.get()))
|
|
return false;
|
|
|
|
assert(ipc_frame->header.color_spec == IPC_FRAME_COLOR_RGB);
|
|
selected_texture = frame_texture;
|
|
f->glBindTexture(GL_TEXTURE_2D, frame_texture);
|
|
f->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,
|
|
ipc_frame->header.width, ipc_frame->header.height,
|
|
0, GL_RGB, GL_UNSIGNED_BYTE, ipc_frame->data);
|
|
setVidSize(ipc_frame->header.width, ipc_frame->header.height);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void PreviewWidget::render(){
|
|
auto f = getOpenGLFuncs();
|
|
|
|
vao.bind();
|
|
f->glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
f->glUseProgram(program);
|
|
|
|
f->glBindTexture(GL_TEXTURE_2D, selected_texture);
|
|
GLuint loc;
|
|
loc = f->glGetUniformLocation(program, "scale_vec");
|
|
f->glUniform2fv(loc, 1, scaleVec);
|
|
|
|
f->glDrawArrays(GL_TRIANGLES, 0, 6);
|
|
|
|
vao.release();
|
|
}
|
|
|
|
void PreviewWidget::paintGL(){
|
|
loadFrame();
|
|
render();
|
|
}
|
|
|
|
static const char *get_temp_dir(void)
|
|
{
|
|
#ifdef _WIN32
|
|
static __thread char temp_dir[MAX_PATH];
|
|
if (GetTempPathA(sizeof temp_dir, temp_dir) == 0) {
|
|
return NULL;
|
|
}
|
|
#else
|
|
static __thread char temp_dir[PATH_MAX];
|
|
if (char *req_tmp_dir = getenv("TMPDIR")) {
|
|
temp_dir[sizeof temp_dir - 1] = '\0';
|
|
strncpy(temp_dir, req_tmp_dir, sizeof temp_dir - 1);
|
|
} else {
|
|
strcpy(temp_dir, P_tmpdir);
|
|
}
|
|
strncat(temp_dir, "/", sizeof temp_dir - strlen(temp_dir) - 1);
|
|
#endif
|
|
|
|
return temp_dir;
|
|
}
|
|
|
|
void PreviewWidget::setKey(std::string_view key){
|
|
std::string path = get_temp_dir();
|
|
path += key;
|
|
ipc_frame_reader.reset(ipc_frame_reader_new(path.c_str()));
|
|
}
|
|
|
|
void PreviewWidget::start(){
|
|
timer.start(1000/24);
|
|
}
|
|
|
|
void PreviewWidget::stop(){
|
|
timer.stop();
|
|
}
|