width and height params gone in favour of single size param with string parsing

This commit is contained in:
minjaesong
2025-09-05 19:12:49 +09:00
parent 9ff12edecd
commit ee03809899

View File

@@ -128,7 +128,7 @@ static void generate_random_filename(char *filename) {
filename[41] = '\0'; // Null terminate filename[41] = '\0'; // Null terminate
} }
char TEMP_AUDIO_FILE[42];// "/tmp/tev_temp_audio.mp2" char TEMP_AUDIO_FILE[42];
typedef struct __attribute__((packed)) { typedef struct __attribute__((packed)) {
uint8_t mode; // Block encoding mode uint8_t mode; // Block encoding mode
@@ -1407,7 +1407,7 @@ static tev_encoder_t* init_encoder(void) {
if (!enc) return NULL; if (!enc) return NULL;
// set defaults // set defaults
enc->qualityIndex = 2; // Default quality enc->qualityIndex = 1; // Default quality
enc->qualityY = QUALITY_Y[enc->qualityIndex]; enc->qualityY = QUALITY_Y[enc->qualityIndex];
enc->qualityCo = QUALITY_CO[enc->qualityIndex]; enc->qualityCo = QUALITY_CO[enc->qualityIndex];
enc->qualityCg = enc->qualityCo / 2; enc->qualityCg = enc->qualityCo / 2;
@@ -1700,6 +1700,27 @@ static int encode_frame(tev_encoder_t *enc, FILE *output, int frame_num, int fie
return 1; return 1;
} }
// Parse resolution string like "1024x768"
static int parse_resolution(const char *res_str, int *width, int *height) {
if (!res_str) return 0;
if (strcmp(res_str, "cif") == 0 || strcmp(res_str, "CIF") == 0) {
*width = 352;
*height = 288;
return 1;
}
if (strcmp(res_str, "qcif") == 0 || strcmp(res_str, "QCIF") == 0) {
*width = 176;
*height = 144;
return 1;
}
if (strcmp(res_str, "default") == 0 || strcmp(res_str, "DEFAULT") == 0) {
*width = DEFAULT_WIDTH;
*height = DEFAULT_HEIGHT;
return 1;
}
return sscanf(res_str, "%dx%d", width, height) == 2;
}
// Execute command and capture output // Execute command and capture output
static char *execute_command(const char *command) { static char *execute_command(const char *command) {
FILE *pipe = popen(command, "r"); FILE *pipe = popen(command, "r");
@@ -1733,13 +1754,14 @@ static int get_video_metadata(tev_encoder_t *config) {
output = execute_command(command); output = execute_command(command);
if (!output) { if (!output) {
fprintf(stderr, "Failed to get video metadata\n"); fprintf(stderr, "Failed to get video metadata (ffprobe failed)\n");
return 0; return 0;
} }
// Parse the combined output // Parse the combined output
char *line = strtok(output, "\n"); char *line = strtok(output, "\n");
int line_num = 0; int line_num = 0;
int input_is_ntsc_framerate = 0;
while (line && line_num < 2) { while (line && line_num < 2) {
switch (line_num) { switch (line_num) {
@@ -1752,7 +1774,8 @@ static int get_video_metadata(tev_encoder_t *config) {
int num, den; int num, den;
if (sscanf(line, "%d/%d", &num, &den) == 2) { if (sscanf(line, "%d/%d", &num, &den) == 2) {
config->fps = (den > 0) ? (int)round((float)num/(float)den) : 30; config->fps = (den > 0) ? (int)round((float)num/(float)den) : 30;
config->is_ntsc_framerate = (den == 1001) ? 1 : 0; config->is_ntsc_framerate = (den == 1001 && config->output_fps == 0) ? 1 : 0; // set NTSC framerate mode only when the user did not supply fps option
input_is_ntsc_framerate = 1;
} else { } else {
config->fps = (int)round(atof(line)); config->fps = (int)round(atof(line));
config->is_ntsc_framerate = 0; config->is_ntsc_framerate = 0;
@@ -1780,15 +1803,17 @@ static int get_video_metadata(tev_encoder_t *config) {
config->total_frames = (int)(config->duration * config->fps); config->total_frames = (int)(config->duration * config->fps);
} }
// calculate new total_frames if user has requested custom framerate // calculate new total_frames if the user has requested a custom framerate
float inputFramerate; float inputFramerate;
if (config->is_ntsc_framerate) { if (input_is_ntsc_framerate) {
inputFramerate = config->fps * 1000.f / 1001.f; inputFramerate = config->fps * 1000.f / 1001.f;
} else { } else {
inputFramerate = config->fps * 1.f; inputFramerate = config->fps * 1.f;
} }
config->total_frames = (int)(config->total_frames * (config->output_fps / inputFramerate)); if (config->output_fps > 0) {
config->total_frames = (int)(config->total_frames * (config->output_fps / inputFramerate));
}
fprintf(stderr, "Video metadata:\n"); fprintf(stderr, "Video metadata:\n");
fprintf(stderr, " Frames: %d\n", config->total_frames); fprintf(stderr, " Frames: %d\n", config->total_frames);
@@ -2011,22 +2036,21 @@ static int process_audio(tev_encoder_t *enc, int frame_num, FILE *output) {
// Show usage information // Show usage information
static void show_usage(const char *program_name) { static void show_usage(const char *program_name) {
printf("TEV YCoCg-R 4:2:0 Video Encoder with Bitrate Control\n"); printf("TEV YCoCg-R 4:2:0 Video Encoder\n");
printf("Usage: %s [options] -i input.mp4 -o output.mv2\n\n", program_name); printf("Usage: %s [options] -i input.mp4 -o output.mv2\n\n", program_name);
printf("Options:\n"); printf("Options:\n");
printf(" -i, --input FILE Input video file\n"); printf(" -i, --input FILE Input video file\n");
printf(" -o, --output FILE Output video file (use '-' for stdout)\n"); printf(" -o, --output FILE Output video file (use '-' for stdout)\n");
printf(" -s, --subtitles FILE SubRip (.srt) or SAMI (.smi) subtitle file\n"); printf(" -s, --size WxH Video size (default: %dx%d)\n", DEFAULT_WIDTH, DEFAULT_HEIGHT);
printf(" -w, --width N Video width (default: %d)\n", DEFAULT_WIDTH); printf(" -f, --fps N Output frames per second (enables frame rate conversion)\n");
printf(" -h, --height N Video height (default: %d)\n", DEFAULT_HEIGHT); printf(" -q, --quality N Quality level 0-4 (default: 1, only decides audio rate in quantiser mode)\n");
printf(" -f, --fps N Output frames per second (enables frame rate conversion)\n"); printf(" -Q, --quantiser N Quantiser level 0-100 (100: lossless, 0: potato)\n");
printf(" -q, --quality N Quality level 0-4 (default: 2, only decides audio rate in bitrate mode and quantiser mode)\n"); // printf(" -b, --bitrate N Target bitrate in kbps (enables bitrate control mode; DON'T USE - NOT WORKING AS INTENDED)\n");
printf(" -Q, --quantiser N Quantiser level 0-100 (100: lossless, 0: potato)\n"); printf(" -p, --progressive Use progressive scan (default: interlaced)\n");
// printf(" -b, --bitrate N Target bitrate in kbps (enables bitrate control mode; DON'T USE - NOT WORKING AS INTENDED)\n"); printf(" -S, --subtitles FILE SubRip (.srt) or SAMI (.smi) subtitle file\n");
printf(" -p, --progressive Use progressive scan (default: interlaced)\n"); printf(" -v, --verbose Verbose output\n");
printf(" -v, --verbose Verbose output\n"); printf(" -t, --test Test mode: generate solid colour frames\n");
printf(" -t, --test Test mode: generate solid colour frames\n"); printf(" --help Show this help\n\n");
printf(" --help Show this help\n\n");
// printf("Rate Control Modes:\n"); // printf("Rate Control Modes:\n");
// printf(" Quality mode (default): Fixed quantisation based on -q parameter\n"); // printf(" Quality mode (default): Fixed quantisation based on -q parameter\n");
// printf(" Bitrate mode (-b N): Dynamic quantisation targeting N kbps average\n\n"); // printf(" Bitrate mode (-b N): Dynamic quantisation targeting N kbps average\n\n");
@@ -2040,6 +2064,10 @@ static void show_usage(const char *program_name) {
for (int i = 0; i < sizeof(QUALITY_Y) / sizeof(int); i++) { for (int i = 0; i < sizeof(QUALITY_Y) / sizeof(int); i++) {
printf("%d: -Q %d \t", i, QUALITY_Y[i]); printf("%d: -Q %d \t", i, QUALITY_Y[i]);
} }
printf("\nVideo Size Keywords:");
printf("\n -s cif: equal to 352x288");
printf("\n -s qcif: equal to 176x144");
printf("\n -s default: equal to %dx%d", DEFAULT_WIDTH, DEFAULT_HEIGHT);
printf("\n\n"); printf("\n\n");
printf("Features:\n"); printf("Features:\n");
printf(" - YCoCg-R 4:2:0 chroma subsampling for 50%% compression improvement\n"); printf(" - YCoCg-R 4:2:0 chroma subsampling for 50%% compression improvement\n");
@@ -2048,9 +2076,10 @@ static void show_usage(const char *program_name) {
printf(" - Adaptive quality control with complexity-based adjustment\n"); printf(" - Adaptive quality control with complexity-based adjustment\n");
printf("Examples:\n"); printf("Examples:\n");
printf(" %s -i input.mp4 -o output.mv2 # Use default setting (q=2)\n", program_name); printf(" %s -i input.mp4 -o output.mv2 # Use default setting (q=2)\n", program_name);
printf(" %s -i input.avi -f 15 -q 3 -o output.mv2 # 15fps @ q=3\n", program_name); printf(" %s -i input.mp4 -s 352x288 -o output.mv2 # Encode at 352x288 resolution\n", program_name);
printf(" %s -i input.mp4 -s input.srt -o output.mv2 # With SubRip subtitles\n", program_name); printf(" %s -i input.avi -f 15 -q 3 -o output.mv2 # Encode at 15 frames per second with higher quality\n", program_name);
printf(" %s -i input.mp4 -s input.smi -o output.mv2 # With SAMI subtitles\n", program_name); printf(" %s -i input.mp4 -S input.srt -o output.mv2 # With SubRip subtitles\n", program_name);
printf(" %s -i input.mp4 -S input.smi -o output.mv2 # With SAMI subtitles\n", program_name);
// printf(" %s -i input.mp4 -b 800 -o output.mv2 # 800 kbps bitrate target\n", program_name); // printf(" %s -i input.mp4 -b 800 -o output.mv2 # 800 kbps bitrate target\n", program_name);
// printf(" %s -i input.avi -f 15 -b 500 -o output.mv2 # 15fps @ 500 kbps\n", program_name); // printf(" %s -i input.avi -f 15 -b 500 -o output.mv2 # 15fps @ 500 kbps\n", program_name);
// printf(" %s --test -b 1000 -o test.mv2 # Test with 1000 kbps target\n", program_name); // printf(" %s --test -b 1000 -o test.mv2 # Test with 1000 kbps target\n", program_name);
@@ -2097,9 +2126,8 @@ int main(int argc, char *argv[]) {
static struct option long_options[] = { static struct option long_options[] = {
{"input", required_argument, 0, 'i'}, {"input", required_argument, 0, 'i'},
{"output", required_argument, 0, 'o'}, {"output", required_argument, 0, 'o'},
{"subtitles", required_argument, 0, 's'}, {"size", required_argument, 0, 's'},
{"width", required_argument, 0, 'w'}, {"subtitles", required_argument, 0, 'S'},
{"height", required_argument, 0, 'h'},
{"fps", required_argument, 0, 'f'}, {"fps", required_argument, 0, 'f'},
{"quality", required_argument, 0, 'q'}, {"quality", required_argument, 0, 'q'},
{"quantiser", required_argument, 0, 'Q'}, {"quantiser", required_argument, 0, 'Q'},
@@ -2115,7 +2143,7 @@ int main(int argc, char *argv[]) {
int option_index = 0; int option_index = 0;
int c; int c;
while ((c = getopt_long(argc, argv, "i:o:s:w:h:f:q:b:Q:pvt", long_options, &option_index)) != -1) { while ((c = getopt_long(argc, argv, "i:o:s:S:w:h:f:q:b:Q:pvt", long_options, &option_index)) != -1) {
switch (c) { switch (c) {
case 'i': case 'i':
enc->input_file = strdup(optarg); enc->input_file = strdup(optarg);
@@ -2125,6 +2153,13 @@ int main(int argc, char *argv[]) {
enc->output_to_stdout = (strcmp(optarg, "-") == 0); enc->output_to_stdout = (strcmp(optarg, "-") == 0);
break; break;
case 's': case 's':
if (!parse_resolution(optarg, &enc->width, &enc->height)) {
fprintf(stderr, "Invalid resolution format: %s\n", optarg);
cleanup_encoder(enc);
return 1;
}
break;
case 'S':
enc->subtitle_file = strdup(optarg); enc->subtitle_file = strdup(optarg);
break; break;
case 'w': case 'w':
@@ -2135,6 +2170,7 @@ int main(int argc, char *argv[]) {
break; break;
case 'f': case 'f':
enc->output_fps = atoi(optarg); enc->output_fps = atoi(optarg);
enc->is_ntsc_framerate = 0;
if (enc->output_fps <= 0) { if (enc->output_fps <= 0) {
fprintf(stderr, "Invalid FPS: %d\n", enc->output_fps); fprintf(stderr, "Invalid FPS: %d\n", enc->output_fps);
cleanup_encoder(enc); cleanup_encoder(enc);
@@ -2142,10 +2178,16 @@ int main(int argc, char *argv[]) {
} }
break; break;
case 'q': case 'q':
enc->qualityIndex = CLAMP(atoi(optarg), 0, 4); int qi = atoi(optarg);
if (qi < 0 || qi > 4) {
fprintf(stderr, "Invalid quality index: %d\nUse value between 0 and 4.\n", qi);
cleanup_encoder(enc);
return 1;
}
enc->qualityIndex = qi;
enc->qualityY = QUALITY_Y[enc->qualityIndex]; enc->qualityY = QUALITY_Y[enc->qualityIndex];
enc->qualityCo = QUALITY_CO[enc->qualityIndex]; enc->qualityCo = QUALITY_CO[enc->qualityIndex];
enc->qualityCg = enc->qualityCo / 2; enc->qualityCg = enc->qualityCo >> 1; // bitshift instead of division so it would round up
break; break;
case 'b': case 'b':
enc->target_bitrate_kbps = atoi(optarg); enc->target_bitrate_kbps = atoi(optarg);
@@ -2216,6 +2258,7 @@ int main(int argc, char *argv[]) {
cleanup_encoder(enc); cleanup_encoder(enc);
return 1; return 1;
} }
enc->output_fps = enc->fps;
} }
// Load subtitle file if specified // Load subtitle file if specified