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 * @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) 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)) { if (is_codec_opaque(codec) && codec_is_planar(codec)) {

View File

@@ -347,7 +347,7 @@ struct state_gl {
GLFWwindow *window = nullptr; GLFWwindow *window = nullptr;
bool fs = false; bool fs = false;
bool deinterlace = false; enum class deint { off, on, force } deinterlace = deint::off;
struct video_frame *current_frame = nullptr; 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 enum modeset_t { MODESET = -2, MODESET_SIZE_ONLY = GLFW_DONT_CARE, NOMODESET = 0 } modeset = NOMODESET; ///< positive vals force framerate
bool nodecorate = false; bool nodecorate = false;
int use_pbo = -1; int use_pbo = -1;
#ifdef HWACC_VDPAU #ifdef HWACC_VDPAU
struct state_vdpau vdp; struct state_vdpau vdp;
#endif #endif
vector<char> scratchpad; ///< scratchpad sized WxHx8
state_gl(struct module *parent) { state_gl(struct module *parent) {
glfwSetErrorCallback(glfw_print_error); glfwSetErrorCallback(glfw_print_error);
@@ -408,7 +408,14 @@ struct state_gl {
module_done(&mod); 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 = { static constexpr array gl_supp_codecs = {
@@ -477,7 +484,7 @@ static void gl_show_help(bool full) {
col() << "options:\n"; col() << "options:\n";
col() << TBOLD("\taspect=<w>/<h>") << "\trequested video aspect (eg. 16/9). Leave unset if PAR = 1.\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("\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("\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("\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"; 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; char *tok, *save_ptr = NULL;
while((tok = strtok_r(ptr, ":", &save_ptr)) != NULL) { while((tok = strtok_r(ptr, ":", &save_ptr)) != NULL) {
if(!strcmp(tok, "d")) { if (!strcmp(tok, "d") || !strcmp(tok, "dforce")) {
s->deinterlace = true; s->deinterlace = !strcmp(tok, "d") ? state_gl::deint::on : state_gl::deint::force;
} else if(!strncmp(tok, "fs", 2)) { } else if(!strncmp(tok, "fs", 2)) {
s->fs = true; s->fs = true;
if (char *val = strchr(tok, '=')) { 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) 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", 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); gl_load_splashscreen(s);
for (auto const &i : keybindings) { 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) { 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"; 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; 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) 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, 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),
(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); exit_uv(0);
break; break;
case 'd': case 'd':
s->deinterlace = !s->deinterlace; s->deinterlace = s->deinterlace == state_gl::deint::off ? state_gl::deint::on : state_gl::deint::off;
log_msg(LOG_LEVEL_NOTICE, "Deinterlacing: %s\n", s->deinterlace ? "ON" : "OFF"); log_msg(LOG_LEVEL_NOTICE, "Deinterlacing: %s\n", state_gl::deint_to_string(s->deinterlace));
break; break;
case 'p': case 'p':
s->paused = !s->paused; s->paused = !s->paused;

View File

@@ -119,7 +119,7 @@ struct state_sdl2 {
SDL_Texture *texture{nullptr}; SDL_Texture *texture{nullptr};
bool fs{false}; bool fs{false};
bool deinterlace{false}; enum class deint { off, on, force } deinterlace = deint::off;
bool keep_aspect{false}; bool keep_aspect{false};
bool vsync{true}; bool vsync{true};
bool fixed_size{false}; bool fixed_size{false};
@@ -150,6 +150,14 @@ struct state_sdl2 {
~state_sdl2() { ~state_sdl2() {
module_done(&mod); 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{ 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; int pitch;
if (codec_is_planar(frame->color_spec)) { if (codec_is_planar(frame->color_spec)) {
pitch = frame->tiles[0].width; pitch = frame->tiles[0].width;
@@ -248,9 +256,9 @@ static bool display_sdl2_process_key(struct state_sdl2 *s, int64_t key)
{ {
switch (key) { switch (key) {
case 'd': 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", log_msg(LOG_LEVEL_INFO, "Deinterlacing: %s\n",
s->deinterlace ? "ON" : "OFF"); state_sdl2::deint_to_string(s->deinterlace));
return true; return true;
case 'f': case 'f':
s->fs = !s->fs; s->fs = !s->fs;
@@ -358,7 +366,7 @@ static void show_help(void)
printf("SDL options:\n"); 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; 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"); 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 fs" << style::reset << " - fullscreen\n";
cout << style::bold <<"\t\t <didx>" << style::reset << " - display index, available indices: "; cout << style::bold <<"\t\t <didx>" << style::reset << " - display index, available indices: ";
sdl2_print_displays(); 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; 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; s->current_desc = desc;
return 1; return 1;
} }
@@ -566,8 +578,8 @@ static void *display_sdl2_init(struct module *parent, const char *fmt, unsigned
char *tok, *save_ptr; char *tok, *save_ptr;
while((tok = strtok_r(tmp, ":", &save_ptr))) while((tok = strtok_r(tmp, ":", &save_ptr)))
{ {
if (strcmp(tok, "d") == 0) { if (strcmp(tok, "d") == 0 || strcmp(tok, "dforce") == 0) {
s->deinterlace = true; s->deinterlace = strcmp(tok, "d") == 0 ? state_sdl2::deint::on : state_sdl2::deint::off;
} else if (strncmp(tok, "display=", strlen("display=")) == 0) { } else if (strncmp(tok, "display=", strlen("display=")) == 0) {
s->display_idx = atoi(tok + strlen("display=")); s->display_idx = atoi(tok + strlen("display="));
} else if (strncmp(tok, "driver=", strlen("driver=")) == 0) { } else if (strncmp(tok, "driver=", strlen("driver=")) == 0) {

View File

@@ -58,6 +58,7 @@
struct state_deinterlace { struct state_deinterlace {
struct video_frame *out; ///< for postprocess only struct video_frame *out; ///< for postprocess only
_Bool force;
}; };
static void usage(_Bool for_postprocessor) 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 " " by applying linear blend on interleaved odd and even "
" fileds.\n\nUsage:\n"); " fileds.\n\nUsage:\n");
if (for_postprocessor) { 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 { } 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) { 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)); struct state_deinterlace *s = calloc(1, sizeof(struct state_deinterlace));
assert(s != NULL); 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; 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) 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 (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 (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 (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); 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), 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), (unsigned char *) out->tiles[0].data, vc_get_linesize(out->tiles[0].width, in->color_spec),
in->tiles[0].height)) { in->tiles[0].height)) {
log_msg(LOG_LEVEL_ERROR, MOD_NAME "Cannot deinterlace, unsupported pixel format '%s'!\n", get_codec_name(in->color_spec)); 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; return true;

View File

@@ -57,7 +57,7 @@
#include "video_display.h" #include "video_display.h"
#include "vo_postprocess.h" #include "vo_postprocess.h"
#define MOD_NAME "[double_framerate] " #define MOD_NAME "[temporal deint] "
#define TIMEOUT "20ms" #define TIMEOUT "20ms"
#define DFR_DEINTERLACE_IMPOSSIBLE_MSG_ID 0x27ff0a78 #define DFR_DEINTERLACE_IMPOSSIBLE_MSG_ID 0x27ff0a78
@@ -70,10 +70,16 @@ struct state_df {
int buffer_current; int buffer_current;
bool deinterlace; bool deinterlace;
bool nodelay; bool nodelay;
bool force;
std::chrono::steady_clock::time_point frame_received; 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() static void df_usage()
{ {
char desc[] = TBOLD("double-framerate") " is an interleaver that " 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("\t" TBOLD(TRED("-p double_framerate") "[:d][:nodelay]") "\n");
color_printf("\nwhere:\n"); color_printf("\nwhere:\n");
color_printf("\t" TBOLD("d ") " - blend the output\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) { static void * init_common(enum algo algo, const char *config) {
bool deinterlace = false; bool deinterlace = false;
bool force = false;
bool nodelay = false; bool nodelay = false;
if (strcmp(config, "d") == 0) { if (strcmp(config, "d") == 0) {
deinterlace = true; deinterlace = true;
} else if (strcmp(config, "nodelay") == 0) { } else if (strcmp(config, "nodelay") == 0) {
nodelay = true; nodelay = true;
} else if (strcmp(config, "force") == 0) {
force = true;
} else if (strlen(config) > 0) { } else if (strlen(config) > 0) {
log_msg(LOG_LEVEL_ERROR, "Unknown config: %s\n", config); log_msg(LOG_LEVEL_ERROR, "Unknown config: %s\n", config);
return NULL; return NULL;
@@ -110,6 +119,7 @@ static void * init_common(enum algo algo, const char *config) {
s->buffers[0] = s->buffers[1] = NULL; s->buffers[0] = s->buffers[1] = NULL;
s->buffer_current = 0; s->buffer_current = 0;
s->deinterlace = deinterlace; s->deinterlace = deinterlace;
s->force = force;
s->nodelay = nodelay; s->nodelay = nodelay;
if (s->nodelay && commandline_params.find("decoder-drop-policy") == commandline_params.end()) { 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->color_spec = desc.color_spec;
s->in->fps = desc.fps; s->in->fps = desc.fps;
s->in->interlacing = desc.interlacing; s->in->interlacing = desc.interlacing;
if(desc.interlacing != INTERLACED_MERGED) { if (desc.interlacing != INTERLACED_MERGED && !s->force) {
log_msg(LOG_LEVEL_ERROR, "[Double Framerate] Warning: %s video detected. This filter is intended " log_msg(LOG_LEVEL_WARNING, MOD_NAME "Warning: %s video detected. This filter is intended "
"mainly for interlaced merged video. The result might be incorrect.\n", "mainly for interlaced merged video. Framerate will be needlessly doubled.\n",
get_interlacing_description(desc.interlacing)); 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; struct state_df *s = (struct state_df *) state;
switch (s->algo) { if (s->in->interlacing == INTERLACED_MERGED || s->force) {
case DF: switch (s->algo) {
perform_df(s, in, out, req_pitch); case DF:
break; perform_df(s, in, out, req_pitch);
case BOB: break;
perform_bob(s, in, out, req_pitch); case BOB:
break; perform_bob(s, in, out, req_pitch);
case LINEAR: break;
perform_linear(s, in, out, req_pitch); case LINEAR:
break; 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) { if (!s->nodelay) {