viewer: implemented basic deinterlace

Implements a very basic GLSL deinterlace that simply halves the vertical
resolution and then interpolates between the fields. This can be toggled
on or off.

The reasons for being so basic is:
- Speed, very quick code running in OpenGL
- It would seem the highest quality deinterlacers are temporally based
  which doesn't make much sense for the viewer, particularly since we can't
  double the frame rate since our timecode is fixed to the frames.

Higher quality interlacing/deinterlacing will be present in the actual
renderer.
This commit is contained in:
itsmattkc
2020-08-11 02:02:50 +10:00
parent 75b4bfe57e
commit ec8b046f02
8 changed files with 85 additions and 29 deletions
+5
View File
@@ -137,6 +137,7 @@ void ViewerOutput::set_video_params(const VideoParams &video)
bool size_changed = video_params_.width() != video.width() || video_params_.height() != video.height();
bool timebase_changed = video_params_.time_base() != video.time_base();
bool pixel_aspect_changed = video_params_.pixel_aspect_ratio() != video.pixel_aspect_ratio();
bool interlacing_changed = video_params_.interlacing() != video.interlacing();
video_params_ = video;
@@ -148,6 +149,10 @@ void ViewerOutput::set_video_params(const VideoParams &video)
emit PixelAspectChanged(video_params_.pixel_aspect_ratio());
}
if (interlacing_changed) {
emit InterlacingChanged(video_params_.interlacing());
}
if (timebase_changed) {
video_frame_cache_.SetTimebase(video_params_.time_base());
emit TimebaseChanged(video_params_.time_base());
+2
View File
@@ -133,6 +133,8 @@ signals:
void PixelAspectChanged(const rational& pixel_aspect);
void InterlacingChanged(VideoParams::Interlacing mode);
void VideoParamsChanged();
void AudioParamsChanged();
+27 -27
View File
@@ -146,8 +146,9 @@ OpenGLShaderPtr OpenGLShader::CreateOCIO(QOpenGLContext* ctx,
return shader;
}
QString OpenGLShader::CodeDefaultFragment(const QString &function_name, const QString &shader_code)
QString OpenGLShader::CodeDefaultFragment(QString function_name, const QString &shader_code)
{
// Create shader header
QString frag_code = QStringLiteral("#version 150\n"
"\n"
"#ifdef GL_ES\n"
@@ -156,48 +157,47 @@ QString OpenGLShader::CodeDefaultFragment(const QString &function_name, const QS
"#endif\n"
"\n"
"uniform sampler2D ove_maintex;\n"
"uniform bool color_only;\n"
"uniform vec4 color_only_color;\n"
"uniform vec2 ove_resolution;\n"
"uniform bool ove_deinterlace;\n"
"\n"
"in vec2 ove_texcoord;\n"
"\n"
"out vec4 fragColor;\n"
"\n");
// Finish the function with the main function
// Check if additional code was passed to this function, add it here
if (shader_code.isEmpty()) {
// If not, just add a pure main() function
frag_code.append(QStringLiteral("\n"
"void main() {\n"
" if (color_only) {\n"
" fragColor = color_only_color;"
" } else {\n"
" vec4 color = texture(ove_maintex, ove_texcoord);\n"
" fragColor = color;\n"
" }\n"
"}\n"));
} else {
if (!function_name.isEmpty() && !shader_code.isEmpty()) {
// If additional code was passed, add it and reference it in main().
//
// The function in the additional code is expected to be `vec4 function_name(vec4 color)`. The texture coordinate
// can be acquired through `ove_texcoord`.
// The function in the additional code is expected to be `vec4 function_name(vec4 color)`.
// The texture coordinate can be acquired through `ove_texcoord`.
frag_code.append(shader_code);
frag_code.append(QStringLiteral("\n"
"void main() {\n"
" vec4 color = %1(texture(ove_maintex, ove_texcoord));\n"
" fragColor = color;\n"
"}\n").arg(function_name));
} else {
// No function to call
function_name = QString();
}
// Our function_name arg will either resolve to the function added to this or to nothing, in
// which case they'll just be benign brackets.
frag_code.append(QStringLiteral("\n"
"void main() {\n"
" vec2 using_texcoord = ove_texcoord;\n"
" if (ove_deinterlace) {\n"
" // A very basic deinterlace that halves the vertical\n"
" // resolution and linearly interpolates the two fields\n"
" // by reading the texture coord between them.\n"
" float half_vert = round(ove_resolution.y / 2.0);\n"
" using_texcoord.y = (round(using_texcoord.y * half_vert) + 0.25) / half_vert;\n"
" }\n"
" vec4 color = %1(texture(ove_maintex, using_texcoord));\n"
" fragColor = color;\n"
"}\n").arg(function_name));
return frag_code;
}
+1 -1
View File
@@ -51,7 +51,7 @@ public:
OCIO::ConstProcessorRcPtr processor,
bool alpha_is_associated);
static QString CodeDefaultFragment(const QString &function_name = QString(),
static QString CodeDefaultFragment(QString function_name = QString(),
const QString &shader_code = QString());
static QString CodeDefaultVertex();
static QString CodeAlphaDisassociate(const QString& function_name);
+22
View File
@@ -167,12 +167,15 @@ void ViewerWidget::ConnectNodeInternal(ViewerOutput *n)
connect(n, &ViewerOutput::SizeChanged, this, &ViewerWidget::SetViewerResolution);
connect(n, &ViewerOutput::PixelAspectChanged, this, &ViewerWidget::SetViewerPixelAspect);
connect(n, &ViewerOutput::LengthChanged, this, &ViewerWidget::LengthChangedSlot);
connect(n, &ViewerOutput::InterlacingChanged, this, &ViewerWidget::InterlacingChangedSlot);
connect(n, &ViewerOutput::VideoParamsChanged, this, &ViewerWidget::UpdateRendererVideoParameters);
connect(n, &ViewerOutput::AudioParamsChanged, this, &ViewerWidget::UpdateRendererAudioParameters);
connect(n->video_frame_cache(), &FrameHashCache::Invalidated, this, &ViewerWidget::ViewerInvalidatedVideoRange);
connect(n->video_frame_cache(), &FrameHashCache::Shifted, this, &ViewerWidget::ViewerShiftedRange);
connect(n, &ViewerOutput::GraphChangedFrom, this, &ViewerWidget::UpdateStack);
InterlacingChangedSlot(n->video_params().interlacing());
ruler()->SetPlaybackCache(n->video_frame_cache());
n->audio_playback_cache()->SetParameters(n->audio_params());
@@ -218,6 +221,7 @@ void ViewerWidget::DisconnectNodeInternal(ViewerOutput *n)
disconnect(n, &ViewerOutput::SizeChanged, this, &ViewerWidget::SetViewerResolution);
disconnect(n, &ViewerOutput::PixelAspectChanged, this, &ViewerWidget::SetViewerPixelAspect);
disconnect(n, &ViewerOutput::LengthChanged, this, &ViewerWidget::LengthChangedSlot);
disconnect(n, &ViewerOutput::InterlacingChanged, this, &ViewerWidget::InterlacingChangedSlot);
disconnect(n, &ViewerOutput::VideoParamsChanged, this, &ViewerWidget::UpdateRendererVideoParameters);
disconnect(n, &ViewerOutput::AudioParamsChanged, this, &ViewerWidget::UpdateRendererAudioParameters);
disconnect(n->video_frame_cache(), &FrameHashCache::Invalidated, this, &ViewerWidget::ViewerInvalidatedVideoRange);
@@ -824,6 +828,16 @@ void ViewerWidget::ShowContextMenu(const QPoint &pos)
connect(full_screen_menu, &QMenu::triggered, this, &ViewerWidget::ContextMenuSetFullScreen);
}
{
// Deinterlace Option
if (GetConnectedNode()->video_params().interlacing() != VideoParams::kInterlaceNone) {
QAction* deinterlace_action = menu.addAction(tr("Deinterlace"));
deinterlace_action->setCheckable(true);
deinterlace_action->setChecked(display_widget_->IsDeinterlacing());
connect(deinterlace_action, &QAction::triggered, display_widget_, &ViewerDisplayWidget::SetDeinterlacing);
}
}
menu.addSeparator();
{
@@ -1089,6 +1103,14 @@ void ViewerWidget::LengthChangedSlot(const rational &length)
}
}
void ViewerWidget::InterlacingChangedSlot(VideoParams::Interlacing interlacing)
{
// Automatically set a "sane" deinterlacing option
display_widget_->SetDeinterlacing(interlacing != VideoParams::kInterlaceNone);
// FIXME: Set windows too
}
void ViewerWidget::UpdateRendererVideoParameters()
{
renderer_->ClearVideoQueue();
+2
View File
@@ -261,6 +261,8 @@ private slots:
void LengthChangedSlot(const rational& length);
void InterlacingChangedSlot(VideoParams::Interlacing interlacing);
void UpdateRendererVideoParameters();
void UpdateRendererAudioParameters();
+14 -1
View File
@@ -43,7 +43,8 @@ ViewerDisplayWidget::ViewerDisplayWidget(QWidget *parent) :
signal_cursor_color_(false),
gizmos_(nullptr),
gizmo_click_(false),
last_loaded_buffer_(nullptr)
last_loaded_buffer_(nullptr),
deinterlace_(false)
{
}
@@ -86,6 +87,12 @@ void ViewerDisplayWidget::SetImage(FramePtr in_buffer)
update();
}
void ViewerDisplayWidget::SetDeinterlacing(bool e)
{
deinterlace_ = e;
update();
}
const ViewerSafeMarginInfo &ViewerDisplayWidget::GetSafeMargin() const
{
return safe_margin_;
@@ -212,6 +219,12 @@ void ViewerDisplayWidget::paintGL()
// Bind retrieved texture
f->glBindTexture(GL_TEXTURE_2D, texture_.texture());
// Set some parameters
color_service()->pipeline()->bind();
color_service()->pipeline()->setUniformValue("ove_resolution", texture_.width(), texture_.height());
color_service()->pipeline()->setUniformValue("ove_deinterlace", deinterlace_);
color_service()->pipeline()->release();
// Blit using the color service
color_service()->ProcessOpenGL(true, matrix_);
+12
View File
@@ -77,6 +77,11 @@ public:
FramePtr last_loaded_buffer() const;
bool IsDeinterlacing() const
{
return deinterlace_;
}
public slots:
/**
* @brief Set the transformation matrix to draw with
@@ -102,6 +107,11 @@ public slots:
*/
void SetImage(FramePtr in_buffer);
/**
* @brief Enables/disables a basic deinterlace on the viewer
*/
void SetDeinterlacing(bool e);
signals:
/**
* @brief Signal emitted when the user starts dragging from the viewer
@@ -174,6 +184,8 @@ private:
FramePtr last_loaded_buffer_;
bool deinterlace_;
private slots:
/**
* @brief Slot to connect just before the OpenGL context is destroyed to clean up resources