From 11443380591dae8c07e8e014e86ecf74cde66e9b Mon Sep 17 00:00:00 2001 From: minjaesong Date: Wed, 17 Dec 2025 02:06:36 +0900 Subject: [PATCH] TAV: different filter for FPS up/downconversion --- video_encoder/src/encoder_tav.c | 58 ++++++++++++++++++++++------- video_encoder/src/encoder_tav_dt.c | 60 +++++++++++++++++++++--------- 2 files changed, 87 insertions(+), 31 deletions(-) diff --git a/video_encoder/src/encoder_tav.c b/video_encoder/src/encoder_tav.c index 04f2dae..288eeed 100644 --- a/video_encoder/src/encoder_tav.c +++ b/video_encoder/src/encoder_tav.c @@ -401,18 +401,35 @@ static int get_video_info(const char *input_file, int *width, int *height, * - Filtergraph: scale/crop to full size, then tinterlace weave halves * framerate, then separatefields restores framerate at half height * - * When target_fps_num > 0: - * - Applies fps filter at the start to convert to target framerate + * Framerate conversion: + * - If target_fps > source_fps: uses minterpolate for motion interpolation + * - If target_fps < source_fps: uses fps filter for frame dropping + * - If target_fps == source_fps: no fps filter applied */ static FILE* open_ffmpeg_pipe(const char *input_file, int width, int height, int interlaced, int full_height, - int target_fps_num, int target_fps_den) { + int target_fps_num, int target_fps_den, + int source_fps_num, int source_fps_den) { char cmd[MAX_PATH * 2]; - char fps_filter[64] = ""; + char fps_filter[128] = ""; // Build fps filter string if conversion is requested (applied first) - if (target_fps_num > 0 && target_fps_den > 0) { - snprintf(fps_filter, sizeof(fps_filter), "fps=%d/%d,", target_fps_num, target_fps_den); + if (target_fps_num > 0 && target_fps_den > 0 && + source_fps_num > 0 && source_fps_den > 0) { + // Compare framerates: target/1 vs source/1 -> target * source_den vs source * target_den + double target_rate = (double)target_fps_num / (double)source_fps_den; + double source_rate = (double)source_fps_num / (double)target_fps_den; + + if (target_rate > source_rate) { + // Upsampling: use motion interpolation + snprintf(fps_filter, sizeof(fps_filter), "minterpolate=fps=%d/%d,", + target_fps_num, target_fps_den); + } else if (target_rate < source_rate) { + // Downsampling: use fps filter + snprintf(fps_filter, sizeof(fps_filter), "fps=%d/%d,", + target_fps_num, target_fps_den); + } + // If equal, fps_filter remains empty (no conversion needed) } if (interlaced) { @@ -1439,7 +1456,9 @@ static int encode_video_mt(cli_context_t *cli) { cli->interlaced, cli->header_height, cli->target_fps_num, - cli->target_fps_den); + cli->target_fps_den, + cli->original_fps_num, + cli->original_fps_den); if (!cli->ffmpeg_pipe) { return -1; } @@ -1905,7 +1924,9 @@ static int encode_video(cli_context_t *cli) { cli->interlaced, cli->header_height, cli->target_fps_num, - cli->target_fps_den); + cli->target_fps_den, + cli->original_fps_num, + cli->original_fps_den); if (!cli->ffmpeg_pipe) { return -1; } @@ -2484,7 +2505,8 @@ int main(int argc, char *argv[]) { fprintf(stderr, "Error: Invalid fps format. Use NUM or NUM/DEN\n"); return 1; } - need_probe_fps = 0; + // Keep need_probe_fps = 1 so we always probe source fps + // (needed for minterpolate vs fps filter decision) cli.target_fps_num = num; cli.target_fps_den = den; cli.enc_params.fps_num = num; @@ -2647,11 +2669,13 @@ int main(int argc, char *argv[]) { printf(" Resolution: %dx%d\n", cli.original_width, cli.original_height); } + // Always print source framerate + printf(" Framerate: %d/%d\n", cli.original_fps_num, cli.original_fps_den); + // Use probed framerate if not specified by -f - if (need_probe_fps) { + if (cli.target_fps_num == 0) { cli.enc_params.fps_num = cli.original_fps_num; cli.enc_params.fps_den = cli.original_fps_den; - printf(" Framerate: %d/%d\n", cli.original_fps_num, cli.original_fps_den); } } @@ -2671,11 +2695,19 @@ int main(int argc, char *argv[]) { // Report fps conversion if enabled if (cli.target_fps_num > 0 && cli.original_fps_num > 0) { - if (cli.target_fps_num != cli.original_fps_num || cli.target_fps_den != cli.original_fps_den) { - printf("Framerate conversion: %d/%d -> %d/%d\n", + long long target_rate = (long long)cli.target_fps_num * cli.original_fps_den; + long long source_rate = (long long)cli.original_fps_num * cli.target_fps_den; + + if (target_rate > source_rate) { + printf("Framerate conversion: %d/%d -> %d/%d (minterpolate)\n", + cli.original_fps_num, cli.original_fps_den, + cli.target_fps_num, cli.target_fps_den); + } else if (target_rate < source_rate) { + printf("Framerate conversion: %d/%d -> %d/%d (fps)\n", cli.original_fps_num, cli.original_fps_den, cli.target_fps_num, cli.target_fps_den); } + // If equal, no message needed (no conversion) } else if (cli.target_fps_num > 0) { printf("Output framerate: %d/%d\n", cli.target_fps_num, cli.target_fps_den); } diff --git a/video_encoder/src/encoder_tav_dt.c b/video_encoder/src/encoder_tav_dt.c index 4578a03..275a6da 100644 --- a/video_encoder/src/encoder_tav_dt.c +++ b/video_encoder/src/encoder_tav_dt.c @@ -180,6 +180,8 @@ typedef struct { int fps_den; int target_fps_num; // Target output framerate numerator (0 = no conversion) int target_fps_den; // Target output framerate denominator + int original_fps_num; // Source framerate (always probed) + int original_fps_den; int is_interlaced; int is_pal; int quality_index; @@ -435,10 +437,23 @@ static FILE *spawn_ffmpeg_video(dt_encoder_t *enc, pid_t *pid) { snprintf(video_size, sizeof(video_size), "%dx%d", enc->width, enc->height); // Build fps filter prefix if conversion is requested - char fps_filter[64] = ""; - if (enc->target_fps_num > 0 && enc->target_fps_den > 0) { - snprintf(fps_filter, sizeof(fps_filter), "fps=%d/%d,", - enc->target_fps_num, enc->target_fps_den); + char fps_filter[128] = ""; + if (enc->target_fps_num > 0 && enc->target_fps_den > 0 && + enc->original_fps_num > 0 && enc->original_fps_den > 0) { + // Compare framerates + long long target_rate = (long long)enc->target_fps_num * enc->original_fps_den; + long long source_rate = (long long)enc->original_fps_num * enc->target_fps_den; + + if (target_rate > source_rate) { + // Upsampling: use motion interpolation + snprintf(fps_filter, sizeof(fps_filter), "minterpolate=fps=%d/%d,", + enc->target_fps_num, enc->target_fps_den); + } else if (target_rate < source_rate) { + // Downsampling: use fps filter + snprintf(fps_filter, sizeof(fps_filter), "fps=%d/%d,", + enc->target_fps_num, enc->target_fps_den); + } + // If equal, fps_filter remains empty } // Use same filtergraph as reference TAV encoder @@ -1390,8 +1405,9 @@ int main(int argc, char **argv) { return 1; } - // Probe input file for framerate - int original_fps_num = 24, original_fps_den = 1; + // Probe input file for framerate (always probe to get original fps) + enc.original_fps_num = 24; + enc.original_fps_den = 1; char probe_cmd[4096]; snprintf(probe_cmd, sizeof(probe_cmd), "ffprobe -v error -select_streams v:0 -show_entries stream=r_frame_rate -of default=nw=1:nk=1 '%s'", @@ -1401,9 +1417,9 @@ int main(int argc, char **argv) { if (probe) { char line[256]; if (fgets(line, sizeof(line), probe)) { - if (sscanf(line, "%d/%d", &original_fps_num, &original_fps_den) != 2) { - original_fps_num = 24; - original_fps_den = 1; + if (sscanf(line, "%d/%d", &enc.original_fps_num, &enc.original_fps_den) != 2) { + enc.original_fps_num = 24; + enc.original_fps_den = 1; } } pclose(probe); @@ -1411,8 +1427,8 @@ int main(int argc, char **argv) { // If user didn't specify target fps, use probed fps if (enc.target_fps_num == 0) { - enc.fps_num = original_fps_num; - enc.fps_den = original_fps_den; + enc.fps_num = enc.original_fps_num; + enc.fps_den = enc.original_fps_den; } printf("\nTAV-DT Encoder (Revised Spec 2025-12-11)\n"); @@ -1420,15 +1436,23 @@ int main(int argc, char **argv) { enc.is_interlaced ? "interlaced" : "progressive"); printf(" Resolution: %dx%d (internal: %dx%d)\n", enc.width, enc.height, enc.width, enc.is_interlaced ? enc.height / 2 : enc.height); + printf(" Source framerate: %d/%d\n", enc.original_fps_num, enc.original_fps_den); // Report fps conversion if enabled - if (enc.target_fps_num > 0 && - (enc.target_fps_num != original_fps_num || enc.target_fps_den != original_fps_den)) { - printf(" Framerate: %d/%d -> %d/%d (conversion)\n", - original_fps_num, original_fps_den, - enc.target_fps_num, enc.target_fps_den); - } else { - printf(" Framerate: %d/%d\n", enc.fps_num, enc.fps_den); + if (enc.target_fps_num > 0) { + long long target_rate = (long long)enc.target_fps_num * enc.original_fps_den; + long long source_rate = (long long)enc.original_fps_num * enc.target_fps_den; + + if (target_rate > source_rate) { + printf(" Framerate conversion: %d/%d -> %d/%d (minterpolate)\n", + enc.original_fps_num, enc.original_fps_den, + enc.target_fps_num, enc.target_fps_den); + } else if (target_rate < source_rate) { + printf(" Framerate conversion: %d/%d -> %d/%d (fps)\n", + enc.original_fps_num, enc.original_fps_den, + enc.target_fps_num, enc.target_fps_den); + } + // If equal, no conversion message needed } printf(" Quality: %d\n", enc.quality_index); printf(" GOP size: %d\n", DT_GOP_SIZE);