Add capture filter for GUI preview

This commit is contained in:
Martin Piatka
2018-08-06 14:41:30 +02:00
parent af1ad2a9d8
commit 6fa4e9887f
10 changed files with 314 additions and 119 deletions

View File

@@ -2426,7 +2426,8 @@ then
CXXFLAGS="`$PKG_CONFIG --cflags-only-I Qt5Gui ` $CXXFLAGS"
CFLAGS="`$PKG_CONFIG --cflags-only-I Qt5Gui ` $CFLAGS"
ADD_MODULE("display_preview", "src/video_display/preview.o", "$QT_LIBS")
ADD_MODULE("display_preview", "src/video_display/preview.o src/shared_mem_frame.o", "$QT_LIBS")
ADD_MODULE("capture_filter_preview", "src/capture_filter/preview.o src/shared_mem_frame.o", "$QT_LIBS")
AC_PATH_PROGS(QMAKE, [qmake-qt5 qmake], [:], [/usr/lib/qt5/bin:$PATH])
GUI_TARGET=gui/QT/uv-qt

View File

@@ -109,9 +109,6 @@ void PreviewWidget::initializeGL(){
vidW = 1280;
vidH = 720;
shared_mem.setKey("ultragrid_preview");
}
void PreviewWidget::calculateScale(){
@@ -179,9 +176,8 @@ void PreviewWidget::paintGL(){
f->glUniform2fv(loc, 1, scaleVec);
f->glBindTexture(GL_TEXTURE_2D, texture);
if(shared_mem.attach()){
shared_mem.lock();
struct Shared_mem_frame *sframe = (Shared_mem_frame*) shared_mem.data();
struct Shared_mem_frame *sframe = shared_mem.get_frame_and_lock();
if(sframe){
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, sframe->width, sframe->height, 0, GL_RGB, GL_UNSIGNED_BYTE, sframe->pixels);
setVidSize(sframe->width, sframe->height);
shared_mem.unlock();

View File

@@ -7,6 +7,8 @@
#include <QOpenGLVertexArrayObject>
#include <QTimer>
#include "shared_mem_frame.hpp"
class PreviewWidget : public QOpenGLWidget{
public:
PreviewWidget(QWidget *parent) : QOpenGLWidget(parent) {
@@ -35,7 +37,7 @@ private:
void setVidSize(int w, int h);
void calculateScale();
QSharedMemory shared_mem;
Shared_mem shared_mem;
QTimer timer;
};

View File

@@ -1,10 +0,0 @@
#ifndef SHARED_MEM_FRAME_HPP
#define SHARED_MEM_FRAME_HPP
struct Shared_mem_frame{
int width, height;
unsigned char pixels[];
};
#endif

View File

@@ -6,6 +6,9 @@ TEMPLATE = app
TARGET = uv-qt
INCLUDEPATH += .
INCLUDEPATH += ../../tools/
INCLUDEPATH += ../../src
DEFINES += GUI_BUILD
QT += widgets
@@ -30,6 +33,7 @@ v4l2.hpp \
previewWidget.hpp \
log_window.hpp \
../../tools/astat.h \
../../src/shared_mem_frame.hpp \
vuMeterWidget.hpp \
settings_window.hpp \
@@ -44,4 +48,5 @@ SOURCES += ultragrid_window.cpp \
log_window.cpp \
vuMeterWidget.cpp \
settings_window.cpp \
../../src/shared_mem_frame.cpp \
main.cpp

View File

@@ -0,0 +1,60 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#include "config_unix.h"
#include "config_win32.h"
#endif /* HAVE_CONFIG_H */
#include "capture_filter.h"
#include "debug.h"
#include "lib_common.h"
#include "video.h"
#include "video_codec.h"
#include "shared_mem_frame.hpp"
struct module;
static int init(struct module *parent, const char *cfg, void **state);
static void done(void *state);
static struct video_frame *filter(void *state, struct video_frame *in);
struct state_preview{
Shared_mem shared_mem;
};
static int init(struct module *parent, const char *cfg, void **state){
UNUSED(parent);
UNUSED(cfg);
struct state_preview *s = new state_preview();
s->shared_mem.create();
*state = s;
return 0;
}
static void done(void *state){
delete (state_preview *) state;
}
static struct video_frame *filter(void *state, struct video_frame *in){
struct state_preview *s = (state_preview *) state;
s->shared_mem.put_frame(in);
return in;
}
static const struct capture_filter_info capture_filter_preview = {
.init = init,
.done = done,
.filter = filter,
};
REGISTER_MODULE(preview, &capture_filter_preview, LIBRARY_CLASS_CAPTURE_FILTER, CAPTURE_FILTER_ABI_VERSION);

184
src/shared_mem_frame.cpp Normal file
View File

@@ -0,0 +1,184 @@
#include "shared_mem_frame.hpp"
#ifndef GUI_BUILD
#include "config.h"
#include "config_unix.h"
#include "config_win32.h"
#include "video_codec.h"
#include "video_frame.h"
#endif // GUI_BUILD
#include <stdio.h>
Shared_mem::Shared_mem(const char *key) :
key(key),
mem_size(4096),
locked(false)
{
shared_mem.setKey(key);
}
Shared_mem::Shared_mem() : Shared_mem("ultragrid_preview") { }
bool Shared_mem::create(){
if(shared_mem.create(mem_size) == false){
/* Creating shared memory could fail because of shared memory
* left over after crash. Here we try to release such memory.*/
shared_mem.attach();
shared_mem.detach();
if(shared_mem.create(mem_size) == false){
fprintf(stderr, "Can't create shared memory!\n");
return false;
}
}
Shared_mem_frame *frame = get_frame_and_lock();
if(!frame)
return false;
frame->width = 20;
frame->height = 20;
frame->should_detach = false;
unlock();
return true;
}
bool Shared_mem::attach(){
return shared_mem.attach();
}
bool Shared_mem::detach(){
if(locked)
unlock();
return shared_mem.detach();
}
bool Shared_mem::isAttached(){
return shared_mem.isAttached();
}
bool Shared_mem::lock(){
if(locked){
printf("LOCKED\n");
printf("LOCKED\n");
printf("LOCKED\n");
}
if(shared_mem.lock()){
locked = true;
return true;
}
return false;
}
bool Shared_mem::unlock(){
if(shared_mem.unlock()){
locked = false;
return true;
}
return false;
}
Shared_mem_frame *Shared_mem::get_frame_and_lock(){
#ifndef GUI_BUILD
if(reconfiguring){
if(shared_mem.attach()){
// Shared mem is still not detached by the GUI
shared_mem.detach();
return nullptr;
} else {
if(shared_mem.create(mem_size) == false){
fprintf(stderr, "Can't create shared memory!\n");
}
lock();
struct Shared_mem_frame *sframe = (Shared_mem_frame*) shared_mem.data();
sframe->width = scaledW_pad;
sframe->height = scaledH;
sframe->should_detach = false;
reconfiguring = false;
}
} else {
if(!isAttached()) attach();
lock();
}
#else
if(!isAttached()) attach();
lock();
#endif // GUI_BUILD
struct Shared_mem_frame *sframe = (Shared_mem_frame*) shared_mem.data();
if(sframe && sframe->should_detach){
detach();
return nullptr;
}
return (Shared_mem_frame *) shared_mem.data();
}
#ifndef GUI_BUILD
void Shared_mem::check_reconf(struct video_desc in_desc){
if (video_desc_eq(desc, in_desc))
return;
desc = in_desc;
/* We need to destroy the shared memory segment
* and recreate it with a new size. To destroy it all processes
* must detach it. We detach here and then wait until the GUI detaches */
reconfiguring = true;
Shared_mem_frame *sframe = get_frame_and_lock();
if(sframe){
sframe->should_detach = true;
}
detach();
const float target_width = 960;
const float target_height = 540;
float scale = ((in_desc.width / target_width) + (in_desc.height / target_height)) / 2.f;
if(scale < 1)
scale = 1;
scale = std::round(scale);
scaleF = (int) scale;
scaledW = in_desc.width / scaleF;
//OpenGL wants the width to be divisable by 4
scaledW_pad = ((scaledW + 4 - 1) / 4) * 4;
scaledH = in_desc.height / scaleF;
scaled_frame.resize(get_bpp(in_desc.color_spec) * scaledW * scaledH);
mem_size = get_bpp(preview_codec) * scaledW_pad * scaledH + sizeof(Shared_mem_frame);
}
void Shared_mem::put_frame(struct video_frame *frame){
check_reconf(video_desc_from_frame(frame));
struct Shared_mem_frame *sframe = get_frame_and_lock();
if(!sframe)
return;
decoder_t dec = get_decoder_from_to(frame->color_spec, preview_codec, true);
int src_line_len = vc_get_linesize(desc.width, frame->color_spec);
int block_size = get_pf_block_size(frame->color_spec);
int dst = 0;
for(unsigned y = 0; y < desc.height; y += scaleF){
for(int x = 0; x + scaleF * block_size <= src_line_len; x += scaleF * block_size){
memcpy(scaled_frame.data() + dst, frame->tiles[0].data + y*src_line_len + x, block_size);
dst += block_size;
}
}
int dst_line_len_pad = vc_get_linesize(scaledW_pad, preview_codec);
int dst_line_len = vc_get_linesize(scaledW, preview_codec);
src_line_len = vc_get_linesize(scaledW, frame->color_spec);
for(int i = 0; i < scaledH; i++){
dec(sframe->pixels + dst_line_len_pad * i,
scaled_frame.data() + src_line_len * i,
dst_line_len,
0, 8, 16);
}
unlock();
}
#endif // GUI_BUILD

54
src/shared_mem_frame.hpp Normal file
View File

@@ -0,0 +1,54 @@
#ifndef SHARED_MEM_FRAME_HPP
#define SHARED_MEM_FRAME_HPP
#include <QSharedMemory>
#ifndef GUI_BUILD
#include "types.h"
#endif // GUI_BUILD
struct Shared_mem_frame{
int width, height;
bool should_detach;
unsigned char pixels[];
};
class Shared_mem{
public:
Shared_mem(const char *key);
Shared_mem();
bool create();
bool attach();
bool detach();
bool isAttached();
bool lock();
bool unlock();
#ifndef GUI_BUILD
void put_frame(struct video_frame *frame);
#endif // GUI_BUILD
Shared_mem_frame *get_frame_and_lock();
private:
const char *key;
size_t mem_size;
bool locked;
QSharedMemory shared_mem;
#ifndef GUI_BUILD
void check_reconf(struct video_desc in_desc);
static const codec_t preview_codec = RGB;
int scaledW, scaledH;
int scaleF;
int scaledW_pad;
struct video_desc desc = {};
std::vector<unsigned char> scaled_frame;
bool reconfiguring = false;
#endif // GUI_BUILD
};
#endif

View File

@@ -77,7 +77,7 @@ struct state_preview_common {
mutex lock;
condition_variable cv;
QSharedMemory shared_mem;
Shared_mem shared_mem;
bool reconfiguring;
size_t mem_size;
codec_t frame_fmt;
@@ -134,31 +134,7 @@ static void *display_preview_init(struct module *parent, const char *fmt, unsign
s->common = shared_ptr<state_preview_common>(new state_preview_common());
s->common->parent = parent;
s->common->shared_mem.setKey("ultragrid_preview");
s->common->mem_size = 4096;
s->common->frame_fmt = RGB;
if(s->common->shared_mem.create(s->common->mem_size) == false){
/* Creating shared memory could fail because of shared memory
* left over after crash. Here we try to release such memory. */
s->common->shared_mem.attach();
s->common->shared_mem.detach();
if(s->common->shared_mem.create(s->common->mem_size) == false){
fprintf(stderr, "Can't create shared memory!\n");
return &display_init_noerr;
}
}
s->common->reconfiguring = false;
s->common->shared_mem.lock();
struct Shared_mem_frame *sframe = (Shared_mem_frame*) s->common->shared_mem.data();
sframe->width = 20;
sframe->height = 20;
s->common->shared_mem.unlock();
s->common->scaleF = 4;
s->common->shared_mem.create();
return s;
}
@@ -169,27 +145,6 @@ static void check_reconf(struct state_preview_common *s, struct video_desc desc)
s->display_desc = desc;
fprintf(stderr, "RECONFIGURED\n");
/* We need to destroy the shared memory segment
* and recreate it with a new size. To destroy it all processes
* must detach it. We detach here and then wait until the GUI detaches */
s->reconfiguring = true;
const float target_width = 960;
const float target_height = 540;
float scale = ((desc.width / target_width) + (desc.height / target_height)) / 2.f;
if(scale < 1)
scale = 1;
scale = std::round(scale);
s->scaleF = (int) scale;
s->scaledW = desc.width / s->scaleF;
//OpenGL wants the width to be divisable by 4
s->scaledW_pad = ((s->scaledW + 4 - 1) / 4) * 4;
s->scaledH = desc.height / s->scaleF;
s->scaled_frame.resize(get_bpp(desc.color_spec) * s->scaledW * s->scaledH);
s->shared_mem.detach();
s->mem_size = get_bpp(s->frame_fmt) * s->scaledW_pad * s->scaledH + sizeof(Shared_mem_frame);
}
static void display_preview_run(void *state)
@@ -219,50 +174,8 @@ static void display_preview_run(void *state)
check_reconf(s.get(), video_desc_from_frame(frame));
if(s->reconfiguring){
if(s->shared_mem.attach()){
// Shared mem is still not detached by the GUI
s->shared_mem.detach();
vf_free(frame);
continue;
} else {
if(s->shared_mem.create(s->mem_size) == false){
fprintf(stderr, "Can't create shared memory!\n");
}
s->shared_mem.lock();
struct Shared_mem_frame *sframe = (Shared_mem_frame*) s->shared_mem.data();
sframe->width = s->scaledW_pad;
sframe->height = s->scaledH;
s->reconfiguring = false;
}
} else {
s->shared_mem.lock();
}
s->shared_mem.put_frame(frame);
struct Shared_mem_frame *sframe = (Shared_mem_frame*) s->shared_mem.data();
decoder_t dec = get_decoder_from_to(frame->color_spec, s->frame_fmt, true);
int src_line_len = vc_get_linesize(s->display_desc.width, frame->color_spec);
int block_size = get_pf_block_size(frame->color_spec);
int dst = 0;
for(unsigned y = 0; y < s->display_desc.height; y += s->scaleF){
for(int x = 0; x + s->scaleF * block_size <= src_line_len; x += s->scaleF * block_size){
memcpy(s->scaled_frame.data() + dst, frame->tiles[0].data + y*src_line_len + x, block_size);
dst += block_size;
}
}
int dst_line_len_pad = vc_get_linesize(s->scaledW_pad, s->frame_fmt);
int dst_line_len = vc_get_linesize(s->scaledW, s->frame_fmt);
src_line_len = vc_get_linesize(s->scaledW, frame->color_spec);
for(int i = 0; i < s->scaledH; i++){
dec(sframe->pixels + dst_line_len_pad * i,
s->scaled_frame.data() + src_line_len * i,
dst_line_len,
0, 8, 16);
}
s->shared_mem.unlock();
vf_free(frame);
}
}
@@ -289,7 +202,7 @@ static int display_preview_putf(void *state, struct video_frame *frame, int flag
} else {
unique_lock<mutex> lg(s->lock);
if (s->incoming_queue.size() >= IN_QUEUE_MAX_BUFFER_LEN) {
fprintf(stderr, "Multiplier: queue full!\n");
fprintf(stderr, "Preview: queue full!\n");
}
if (flags == PUTF_NONBLOCK && s->incoming_queue.size() >= IN_QUEUE_MAX_BUFFER_LEN) {
vf_free(frame);

View File

@@ -1,10 +0,0 @@
#ifndef SHARED_MEM_FRAME_HPP
#define SHARED_MEM_FRAME_HPP
struct Shared_mem_frame{
int width, height;
unsigned char pixels[];
};
#endif