diff --git a/gui/CMakeLists.txt b/gui/CMakeLists.txt index fcdb4b3..f227622 100644 --- a/gui/CMakeLists.txt +++ b/gui/CMakeLists.txt @@ -31,7 +31,9 @@ add_executable(chiaki include/streamsession.h src/streamsession.cpp include/avopenglwidget.h - src/avopenglwidget.cpp) + src/avopenglwidget.cpp + include/avopenglframeuploader.h + src/avopenglframeuploader.cpp) target_include_directories(chiaki PRIVATE include) target_link_libraries(chiaki chiaki-lib) diff --git a/gui/include/avopenglframeuploader.h b/gui/include/avopenglframeuploader.h new file mode 100644 index 0000000..3d0ab48 --- /dev/null +++ b/gui/include/avopenglframeuploader.h @@ -0,0 +1,42 @@ +/* + * This file is part of Chiaki. + * + * Chiaki is free 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. + * + * Chiaki 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chiaki. If not, see . + */ + +#ifndef CHIAKI_AVOPENGLFRAMEUPLOADER_H +#define CHIAKI_AVOPENGLFRAMEUPLOADER_H + +#include +#include + +class AVOpenGLWidget; +class VideoDecoder; + +class AVOpenGLFrameUploader: public QObject +{ + Q_OBJECT + + private: + VideoDecoder *decoder; + AVOpenGLWidget *widget; + + private slots: + void UpdateFrame(); + + public: + AVOpenGLFrameUploader(VideoDecoder *decoder, AVOpenGLWidget *widget); +}; + +#endif // CHIAKI_AVOPENGLFRAMEUPLOADER_H diff --git a/gui/include/avopenglwidget.h b/gui/include/avopenglwidget.h index 1b3cccb..47dbb1b 100644 --- a/gui/include/avopenglwidget.h +++ b/gui/include/avopenglwidget.h @@ -19,6 +19,7 @@ #define CHIAKI_AVOPENGLWIDGET_H #include +#include extern "C" { @@ -26,6 +27,16 @@ extern "C" } class VideoDecoder; +class AVOpenGLFrameUploader; + +struct AVOpenGLFrame +{ + GLuint tex[3]; + unsigned int width; + unsigned int height; + + bool Update(AVFrame *frame); +}; class AVOpenGLWidget: public QOpenGLWidget { @@ -37,13 +48,18 @@ class AVOpenGLWidget: public QOpenGLWidget GLuint program; GLuint vbo; GLuint vao; - GLuint tex[3]; - unsigned int frame_width, frame_height; - void UpdateTextures(AVFrame *frame); + AVOpenGLFrame frames[2]; + int frame_fg; + QMutex frames_mutex; + AVOpenGLFrameUploader *frame_uploader; public: explicit AVOpenGLWidget(VideoDecoder *decoder, QWidget *parent = nullptr); + ~AVOpenGLWidget() override; + + void SwapFrames(); + AVOpenGLFrame *GetBackgroundFrame() { return &frames[1 - frame_fg]; } protected: void initializeGL() override; diff --git a/gui/src/avopenglframeuploader.cpp b/gui/src/avopenglframeuploader.cpp new file mode 100644 index 0000000..811fce9 --- /dev/null +++ b/gui/src/avopenglframeuploader.cpp @@ -0,0 +1,45 @@ +/* + * This file is part of Chiaki. + * + * Chiaki is free 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. + * + * Chiaki 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chiaki. If not, see . + */ + +#include +#include +#include + +#include +#include + +AVOpenGLFrameUploader::AVOpenGLFrameUploader(VideoDecoder *decoder, AVOpenGLWidget *widget) + : QObject(nullptr), + decoder(decoder), + widget(widget) +{ + + connect(decoder, SIGNAL(FramesAvailable()), this, SLOT(UpdateFrame())); +} + +void AVOpenGLFrameUploader::UpdateFrame() +{ + AVFrame *next_frame = decoder->PullFrame(); + if(!next_frame) + return; + + bool success = widget->GetBackgroundFrame()->Update(next_frame); + av_frame_free(&next_frame); + + if(success) + widget->SwapFrames(); +} \ No newline at end of file diff --git a/gui/src/avopenglwidget.cpp b/gui/src/avopenglwidget.cpp index 938b2a0..bbf4759 100644 --- a/gui/src/avopenglwidget.cpp +++ b/gui/src/avopenglwidget.cpp @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -84,10 +85,51 @@ AVOpenGLWidget::AVOpenGLWidget(VideoDecoder *decoder, QWidget *parent) #endif setFormat(format); - frame_width = 0; - frame_height = 0; + frame_uploader = nullptr; +} - connect(decoder, SIGNAL(FramesAvailable()), this, SLOT(update())); +AVOpenGLWidget::~AVOpenGLWidget() +{ + delete frame_uploader; +} + +void AVOpenGLWidget::SwapFrames() +{ + QMutexLocker lock(&frames_mutex); + frame_fg = 1 - frame_fg; + QMetaObject::invokeMethod(this, "update"); +} + +bool AVOpenGLFrame::Update(AVFrame *frame) +{ + auto f = QOpenGLContext::currentContext()->functions(); + + if(frame->format != AV_PIX_FMT_YUV420P) + { + // TODO: log to somewhere else + printf("Invalid Format\n"); + return false; + } + + width = frame->width; + height = frame->height; + + for(int i=0; i<3; i++) + { + f->glBindTexture(GL_TEXTURE_2D, tex[i]); + int width = frame->width; + int height = frame->height; + if(i > 0) + { + width /= 2; + height /= 2; + } + f->glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, width, height, 0, GL_RED, GL_UNSIGNED_BYTE, frame->data[i]); + } + + f->glFinish(); // TODO: fence + + return true; } void AVOpenGLWidget::initializeGL() @@ -131,16 +173,21 @@ void AVOpenGLWidget::initializeGL() return; } - f->glGenTextures(3, tex); - uint8_t uv_default = 127; - for(int i=0; i<3; i++) + for(int i=0; i<2; i++) { - f->glBindTexture(GL_TEXTURE_2D, tex[i]); - f->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - f->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - f->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - f->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - f->glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, 1, 1, 0, GL_RED, GL_UNSIGNED_BYTE, i > 0 ? &uv_default : nullptr); + f->glGenTextures(3, frames[i].tex); + uint8_t uv_default = 127; + for(int j=0; j<3; j++) + { + f->glBindTexture(GL_TEXTURE_2D, frames[i].tex[j]); + f->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + f->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + f->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + f->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + f->glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, 1, 1, 0, GL_RED, GL_UNSIGNED_BYTE, j > 0 ? &uv_default : nullptr); + } + frames[i].width = 0; + frames[i].height = 0; } f->glUseProgram(program); @@ -162,62 +209,33 @@ void AVOpenGLWidget::initializeGL() f->glCullFace(GL_BACK); f->glEnable(GL_CULL_FACE); f->glClearColor(0.0, 0.0, 0.0, 1.0); + + frame_uploader = new AVOpenGLFrameUploader(decoder, this); + frame_fg = 0; } void AVOpenGLWidget::resizeGL(int w, int h) { } -void AVOpenGLWidget::UpdateTextures(AVFrame *frame) -{ - auto f = QOpenGLContext::currentContext()->functions(); - - if(frame->format != AV_PIX_FMT_YUV420P) - { - // TODO: log to somewhere else - printf("Invalid Format\n"); - return; - } - - frame_width = frame->width; - frame_height = frame->height; - - for(int i=0; i<3; i++) - { - f->glBindTexture(GL_TEXTURE_2D, tex[i]); - int width = frame->width; - int height = frame->height; - if(i > 0) - { - width /= 2; - height /= 2; - } - f->glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, width, height, 0, GL_RED, GL_UNSIGNED_BYTE, frame->data[i]); - } -} - void AVOpenGLWidget::paintGL() { auto f = QOpenGLContext::currentContext()->extraFunctions(); f->glClear(GL_COLOR_BUFFER_BIT); - AVFrame *next_frame = decoder->PullFrame(); - if(next_frame) - { - UpdateTextures(next_frame); - av_frame_free(&next_frame); - } + QMutexLocker lock(&frames_mutex); + AVOpenGLFrame *frame = &frames[frame_fg]; GLsizei vp_width, vp_height; - if(!frame_width || !frame_height) + if(!frame->width || !frame->height) { vp_width = width(); vp_height = height(); } else { - float aspect = (float)frame_width / (float)frame_height; + float aspect = (float)frame->width / (float)frame->height; if(aspect < (float)width() / (float)height()) { vp_height = height(); @@ -235,8 +253,10 @@ void AVOpenGLWidget::paintGL() for(int i=0; i<3; i++) { f->glActiveTexture(GL_TEXTURE0 + i); - f->glBindTexture(GL_TEXTURE_2D, tex[i]); + f->glBindTexture(GL_TEXTURE_2D, frame->tex[i]); } f->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + f->glFinish(); // TODO: fence }