do not deinterlace progressive

until option "force" is given, do not deinterlace progressive video
This commit is contained in:
Martin Pulec
2023-01-13 15:27:01 +01:00
parent 13bdf14157
commit c4aa64c800
5 changed files with 93 additions and 35 deletions

View File

@@ -692,6 +692,8 @@ static void vc_deinterlace_unaligned(unsigned char *src, long src_linesize, int
*
* @returns false on unsupported codecs
*/
// Sibling of this function is in double-framerate.cpp:avg_lines so consider
// porting changes made here there.
bool vc_deinterlace_ex(codec_t codec, unsigned char *src, size_t src_linesize, unsigned char *dst, size_t dst_pitch, size_t lines)
{
if (is_codec_opaque(codec) && codec_is_planar(codec)) {

View File

@@ -347,7 +347,7 @@ struct state_gl {
GLFWwindow *window = nullptr;
bool fs = false;
bool deinterlace = false;
enum class deint { off, on, force } deinterlace = deint::off;
struct video_frame *current_frame = nullptr;
@@ -385,10 +385,10 @@ struct state_gl {
enum modeset_t { MODESET = -2, MODESET_SIZE_ONLY = GLFW_DONT_CARE, NOMODESET = 0 } modeset = NOMODESET; ///< positive vals force framerate
bool nodecorate = false;
int use_pbo = -1;
#ifdef HWACC_VDPAU
struct state_vdpau vdp;
#endif
vector<char> scratchpad; ///< scratchpad sized WxHx8
state_gl(struct module *parent) {
glfwSetErrorCallback(glfw_print_error);
@@ -408,7 +408,14 @@ struct state_gl {
module_done(&mod);
}
vector<char> scratchpad; ///< scratchpad sized WxHx8
static const char *deint_to_string(state_gl::deint val) {
switch (val) {
case state_gl::deint::off: return "OFF";
case state_gl::deint::on: return "ON";
case state_gl::deint::force: return "FORCE";
}
return NULL;
}
};
static constexpr array gl_supp_codecs = {
@@ -477,7 +484,7 @@ static void gl_show_help(bool full) {
col() << "options:\n";
col() << TBOLD("\taspect=<w>/<h>") << "\trequested video aspect (eg. 16/9). Leave unset if PAR = 1.\n";
col() << TBOLD("\tcursor") << "\t\tshow visible cursor\n";
col() << TBOLD("\td") << "\t\tdeinterlace\n";
col() << TBOLD("\td[force]") << "\tdeinterlace (optionally forcing deinterlace of progressive video)\n";
col() << TBOLD("\tfs[=<monitor>]") << "\tfullscreen with optional display specification\n";
col() << TBOLD("\tgamma[=<val>]") << "\tgamma value to be added _in addition_ to the hardware gamma correction\n";
col() << TBOLD("\thide-window") << "\tdo not show OpenGL window (useful with Syphon/SPOUT)\n";
@@ -549,8 +556,8 @@ static void *display_gl_parse_fmt(struct state_gl *s, char *ptr) {
char *tok, *save_ptr = NULL;
while((tok = strtok_r(ptr, ":", &save_ptr)) != NULL) {
if(!strcmp(tok, "d")) {
s->deinterlace = true;
if (!strcmp(tok, "d") || !strcmp(tok, "dforce")) {
s->deinterlace = !strcmp(tok, "d") ? state_gl::deint::on : state_gl::deint::force;
} else if(!strncmp(tok, "fs", 2)) {
s->fs = true;
if (char *val = strchr(tok, '=')) {
@@ -663,7 +670,7 @@ static void * display_gl_init(struct module *parent, const char *fmt, unsigned i
s->use_pbo = s->use_pbo == -1 ? !check_rpi_pbo_quirks() : s->use_pbo; // don't use PBO for Raspberry Pi (better performance)
log_msg(LOG_LEVEL_INFO,"GL setup: fullscreen: %s, deinterlace: %s\n",
s->fs ? "ON" : "OFF", s->deinterlace ? "ON" : "OFF");
s->fs ? "ON" : "OFF", state_gl::deint_to_string(s->deinterlace));
gl_load_splashscreen(s);
for (auto const &i : keybindings) {
@@ -694,6 +701,9 @@ static int display_gl_reconfigure(void *state, struct video_desc desc)
if (get_bits_per_component(desc.color_spec) > 8) {
LOG(LOG_LEVEL_WARNING) << MOD_NAME "Displaying 10+ bits - performance degradation may occur, consider '--param " GL_DISABLE_10B_OPT_PARAM_NAME "'\n";
}
if (desc.interlacing == INTERLACED_MERGED && s->deinterlace == state_gl::deint::off) {
LOG(LOG_LEVEL_WARNING) << MOD_NAME "Receiving interlaced video but deinterlacing is off - suggesting toggling it on (press 'd' or pass cmdline option)\n";
}
s->current_desc = desc;
@@ -953,7 +963,7 @@ static void gl_reconfigure_screen(struct state_gl *s, struct video_desc desc)
static void gl_render(struct state_gl *s, char *data)
{
if (s->deinterlace) {
if (s->deinterlace == state_gl::deint::force || (s->deinterlace == state_gl::deint::on && s->current_display_desc.interlacing == INTERLACED_MERGED)) {
if (!vc_deinterlace_ex(s->current_display_desc.color_spec,
(unsigned char *) data, vc_get_linesize(s->current_display_desc.width, s->current_display_desc.color_spec),
(unsigned char *) data, vc_get_linesize(s->current_display_desc.width, s->current_display_desc.color_spec),
@@ -1170,8 +1180,8 @@ static bool display_gl_process_key(struct state_gl *s, long long int key)
exit_uv(0);
break;
case 'd':
s->deinterlace = !s->deinterlace;
log_msg(LOG_LEVEL_NOTICE, "Deinterlacing: %s\n", s->deinterlace ? "ON" : "OFF");
s->deinterlace = s->deinterlace == state_gl::deint::off ? state_gl::deint::on : state_gl::deint::off;
log_msg(LOG_LEVEL_NOTICE, "Deinterlacing: %s\n", state_gl::deint_to_string(s->deinterlace));
break;
case 'p':
s->paused = !s->paused;

View File

@@ -119,7 +119,7 @@ struct state_sdl2 {
SDL_Texture *texture{nullptr};
bool fs{false};
bool deinterlace{false};
enum class deint { off, on, force } deinterlace = deint::off;
bool keep_aspect{false};
bool vsync{true};
bool fixed_size{false};
@@ -150,6 +150,14 @@ struct state_sdl2 {
~state_sdl2() {
module_done(&mod);
}
static const char *deint_to_string(deint val) {
switch (val) {
case deint::off: return "OFF";
case deint::on: return "ON";
case deint::force: return "FORCE";
}
return NULL;
}
};
static constexpr array display_sdl2_keybindings{
@@ -169,7 +177,7 @@ static void display_frame(struct state_sdl2 *s, struct video_frame *frame)
}
}
if (!s->deinterlace) {
if (s->deinterlace == state_sdl2::deint::off || (s->deinterlace == state_sdl2::deint::on && frame->interlacing != INTERLACED_MERGED)) {
int pitch;
if (codec_is_planar(frame->color_spec)) {
pitch = frame->tiles[0].width;
@@ -248,9 +256,9 @@ static bool display_sdl2_process_key(struct state_sdl2 *s, int64_t key)
{
switch (key) {
case 'd':
s->deinterlace = !s->deinterlace;
s->deinterlace = s->deinterlace == state_sdl2::deint::off ? state_sdl2::deint::on : state_sdl2::deint::off;
log_msg(LOG_LEVEL_INFO, "Deinterlacing: %s\n",
s->deinterlace ? "ON" : "OFF");
state_sdl2::deint_to_string(s->deinterlace));
return true;
case 'f':
s->fs = !s->fs;
@@ -358,7 +366,7 @@ static void show_help(void)
printf("SDL options:\n");
cout << style::bold << fg::red << "\t-d sdl" << fg::reset << "[[:fs|:d|:display=<didx>|:driver=<drv>|:novsync|:renderer=<ridx>|:nodecorate|:fixed_size[=WxH]|:window_flags=<f>|:pos=<x>,<y>|:keep-aspect]*|:help]\n" << style::reset;
printf("\twhere:\n");
cout << style::bold <<"\t\t d" << style::reset << " - deinterlace\n";
cout << style::bold <<"\t\td[force]" << style::reset << " - deinterlace (force even for progresive video)\n";
cout << style::bold <<"\t\t fs" << style::reset << " - fullscreen\n";
cout << style::bold <<"\t\t <didx>" << style::reset << " - display index, available indices: ";
sdl2_print_displays();
@@ -391,6 +399,10 @@ static int display_sdl2_reconfigure(void *state, struct video_desc desc)
{
struct state_sdl2 *s = (struct state_sdl2 *) state;
if (desc.interlacing == INTERLACED_MERGED && s->deinterlace == state_sdl2::deint::off) {
LOG(LOG_LEVEL_WARNING) << MOD_NAME "Receiving interlaced video but deinterlacing is off - suggesting toggling it on (press 'd' or pass cmdline option)\n";
}
s->current_desc = desc;
return 1;
}
@@ -566,8 +578,8 @@ static void *display_sdl2_init(struct module *parent, const char *fmt, unsigned
char *tok, *save_ptr;
while((tok = strtok_r(tmp, ":", &save_ptr)))
{
if (strcmp(tok, "d") == 0) {
s->deinterlace = true;
if (strcmp(tok, "d") == 0 || strcmp(tok, "dforce") == 0) {
s->deinterlace = strcmp(tok, "d") == 0 ? state_sdl2::deint::on : state_sdl2::deint::off;
} else if (strncmp(tok, "display=", strlen("display=")) == 0) {
s->display_idx = atoi(tok + strlen("display="));
} else if (strncmp(tok, "driver=", strlen("driver=")) == 0) {

View File

@@ -58,6 +58,7 @@
struct state_deinterlace {
struct video_frame *out; ///< for postprocess only
_Bool force;
};
static void usage(_Bool for_postprocessor)
@@ -66,10 +67,12 @@ static void usage(_Bool for_postprocessor)
" by applying linear blend on interleaved odd and even "
" fileds.\n\nUsage:\n");
if (for_postprocessor) {
color_printf(TBOLD(TRED("\t-p deinterlace")) " | " TBOLD(TRED("-p deinterlace_blend")) "\n");
color_printf(TBOLD(TRED("\t-p deinterlace")) "[:options] | " TBOLD(TRED("-p deinterlace_blend")) "[:options]\n");
} else {
color_printf(TBOLD(TRED("\t--capture-filter deinterlace")) " -t <capture>\n");
color_printf(TBOLD(TRED("\t--capture-filter deinterlace")) "[:options] -t <capture>\n");
}
color_printf("\noptions:\n"
"\t" TBOLD("force") " - apply deinterlacing even if input is progressive\n");
}
static void * deinterlace_blend_init(const char *config) {
@@ -81,6 +84,14 @@ static void * deinterlace_blend_init(const char *config) {
struct state_deinterlace *s = calloc(1, sizeof(struct state_deinterlace));
assert(s != NULL);
if (strcmp(config, "force") == 0) {
s->force = 1;
} else {
log_msg(LOG_LEVEL_ERROR, MOD_NAME "Unknown option: %s\n", config);
free(s);
return NULL;
}
return s;
}
@@ -140,16 +151,22 @@ static struct video_frame * deinterlace_getf(void *state)
static bool deinterlace_postprocess(void *state, struct video_frame *in, struct video_frame *out, int req_pitch)
{
UNUSED(state);
assert (req_pitch == vc_get_linesize(in->tiles[0].width, in->color_spec));
assert (video_desc_eq(video_desc_from_frame(out), video_desc_from_frame(in)));
assert (in->tiles[0].data_len <= vc_get_linesize(in->tiles[0].width, in->color_spec) * in->tiles[0].height);
assert (out->tiles[0].data_len <= vc_get_linesize(in->tiles[0].width, in->color_spec) * in->tiles[0].height);
struct state_deinterlace *s = state;
if (in->interlacing != INTERLACED_MERGED && !s->force) {
memcpy(out->tiles[0].data, in->tiles[0].data, in->tiles[0].data_len);
return true;
}
if (!vc_deinterlace_ex(in->color_spec, (unsigned char *) in->tiles[0].data, vc_get_linesize(in->tiles[0].width, in->color_spec),
(unsigned char *) out->tiles[0].data, vc_get_linesize(out->tiles[0].width, in->color_spec),
in->tiles[0].height)) {
log_msg(LOG_LEVEL_ERROR, MOD_NAME "Cannot deinterlace, unsupported pixel format '%s'!\n", get_codec_name(in->color_spec));
memcpy(out->tiles[0].data, in->tiles[0].data, in->tiles[0].data_len);
}
return true;

View File

@@ -57,7 +57,7 @@
#include "video_display.h"
#include "vo_postprocess.h"
#define MOD_NAME "[double_framerate] "
#define MOD_NAME "[temporal deint] "
#define TIMEOUT "20ms"
#define DFR_DEINTERLACE_IMPOSSIBLE_MSG_ID 0x27ff0a78
@@ -70,10 +70,16 @@ struct state_df {
int buffer_current;
bool deinterlace;
bool nodelay;
bool force;
std::chrono::steady_clock::time_point frame_received;
};
static void print_common_opts() {
color_printf("\t" TBOLD("force ") " - apply deinterlacing even if input is not interlaced\n");
color_printf("\t" TBOLD("nodelay") " - do not delay the other frame to keep timing. Both frames are output in burst. May not work correctly (depends on display).\n");
}
static void df_usage()
{
char desc[] = TBOLD("double-framerate") " is an interleaver that "
@@ -86,17 +92,20 @@ static void df_usage()
color_printf("\t" TBOLD(TRED("-p double_framerate") "[:d][:nodelay]") "\n");
color_printf("\nwhere:\n");
color_printf("\t" TBOLD("d ") " - blend the output\n");
color_printf("\t" TBOLD("nodelay") " - do not delay the other frame to keep timing. Both frames are output in burst. May not work correctly (depends on display).\n");
print_common_opts();
}
static void * init_common(enum algo algo, const char *config) {
bool deinterlace = false;
bool force = false;
bool nodelay = false;
if (strcmp(config, "d") == 0) {
deinterlace = true;
} else if (strcmp(config, "nodelay") == 0) {
nodelay = true;
} else if (strcmp(config, "force") == 0) {
force = true;
} else if (strlen(config) > 0) {
log_msg(LOG_LEVEL_ERROR, "Unknown config: %s\n", config);
return NULL;
@@ -110,6 +119,7 @@ static void * init_common(enum algo algo, const char *config) {
s->buffers[0] = s->buffers[1] = NULL;
s->buffer_current = 0;
s->deinterlace = deinterlace;
s->force = force;
s->nodelay = nodelay;
if (s->nodelay && commandline_params.find("decoder-drop-policy") == commandline_params.end()) {
@@ -171,9 +181,9 @@ static int common_postprocess_reconfigure(void *state, struct video_desc desc)
s->in->color_spec = desc.color_spec;
s->in->fps = desc.fps;
s->in->interlacing = desc.interlacing;
if(desc.interlacing != INTERLACED_MERGED) {
log_msg(LOG_LEVEL_ERROR, "[Double Framerate] Warning: %s video detected. This filter is intended "
"mainly for interlaced merged video. The result might be incorrect.\n",
if (desc.interlacing != INTERLACED_MERGED && !s->force) {
log_msg(LOG_LEVEL_WARNING, MOD_NAME "Warning: %s video detected. This filter is intended "
"mainly for interlaced merged video. Framerate will be needlessly doubled.\n",
get_interlacing_description(desc.interlacing));
}
@@ -419,16 +429,23 @@ static bool common_postprocess(void *state, struct video_frame *in, struct video
{
struct state_df *s = (struct state_df *) state;
switch (s->algo) {
case DF:
perform_df(s, in, out, req_pitch);
break;
case BOB:
perform_bob(s, in, out, req_pitch);
break;
case LINEAR:
perform_linear(s, in, out, req_pitch);
break;
if (s->in->interlacing == INTERLACED_MERGED || s->force) {
switch (s->algo) {
case DF:
perform_df(s, in, out, req_pitch);
break;
case BOB:
perform_bob(s, in, out, req_pitch);
break;
case LINEAR:
perform_linear(s, in, out, req_pitch);
break;
}
} else {
s->in->tiles[0].data = s->buffers[0]; // always write to first buffer
if (in) {
memcpy(out->tiles[0].data, in->tiles[0].data, in->tiles[0].data_len);
}
}
if (!s->nodelay) {