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:
@@ -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());
|
||||
|
||||
@@ -133,6 +133,8 @@ signals:
|
||||
|
||||
void PixelAspectChanged(const rational& pixel_aspect);
|
||||
|
||||
void InterlacingChanged(VideoParams::Interlacing mode);
|
||||
|
||||
void VideoParamsChanged();
|
||||
void AudioParamsChanged();
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -261,6 +261,8 @@ private slots:
|
||||
|
||||
void LengthChangedSlot(const rational& length);
|
||||
|
||||
void InterlacingChangedSlot(VideoParams::Interlacing interlacing);
|
||||
|
||||
void UpdateRendererVideoParameters();
|
||||
|
||||
void UpdateRendererAudioParameters();
|
||||
|
||||
@@ -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_);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user