mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-06-06 05:28:31 +09:00
faster init time
This commit is contained in:
@@ -45,14 +45,14 @@ static inline float FCLAMP(float x, float min, float max) {
|
|||||||
// from dataset of three videos with Q0..Q95: (real life video, low res pixel art, high res pixel art)
|
// from dataset of three videos with Q0..Q95: (real life video, low res pixel art, high res pixel art)
|
||||||
// 56 96 128 192 256 Claude Opus 4.1 (with data analysis)
|
// 56 96 128 192 256 Claude Opus 4.1 (with data analysis)
|
||||||
// 64 96 128 192 256 ChatGPT-5 (without data analysis)
|
// 64 96 128 192 256 ChatGPT-5 (without data analysis)
|
||||||
static const int MP2_RATE_TABLE[] = {128, 160, 224, 320, 384};
|
static const int MP2_RATE_TABLE[] = {128, 160, 224, 320, 384, 384};
|
||||||
// Which preset should I be using?
|
// Which preset should I be using?
|
||||||
// from dataset of three videos with Q0..Q95: (real life video, low res pixel art, high res pixel art)
|
// from dataset of three videos with Q0..Q95: (real life video, low res pixel art, high res pixel art)
|
||||||
// 5 25 50 75 90 Claude Opus 4.1 (with data analysis)
|
// 5 25 50 75 90 Claude Opus 4.1 (with data analysis)
|
||||||
// 10 25 45 65 85 ChatGPT-5 (without data analysis)
|
// 10 25 45 65 85 ChatGPT-5 (without data analysis)
|
||||||
// 10 30 50 70 90 ChatGPT-5 (with data analysis)
|
// 10 30 50 70 90 ChatGPT-5 (with data analysis)
|
||||||
static const int QUALITY_Y[] = {5, 18, 42, 63, 80};
|
static const int QUALITY_Y[] = {5, 18, 36, 54, 72, 90};
|
||||||
static const int QUALITY_CO[] = {5, 18, 42, 63, 80};
|
static const int QUALITY_CO[] = {5, 18, 36, 54, 72, 90};
|
||||||
|
|
||||||
// Encoding parameters
|
// Encoding parameters
|
||||||
#define MAX_MOTION_SEARCH 16
|
#define MAX_MOTION_SEARCH 16
|
||||||
@@ -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 = 1; // Default quality
|
enc->qualityIndex = 2; // 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;
|
||||||
@@ -1713,6 +1713,11 @@ static int parse_resolution(const char *res_str, int *width, int *height) {
|
|||||||
*height = 144;
|
*height = 144;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
if (strcmp(res_str, "half") == 0 || strcmp(res_str, "HALF") == 0) {
|
||||||
|
*width = DEFAULT_WIDTH >> 1;
|
||||||
|
*height = DEFAULT_HEIGHT >> 1;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
if (strcmp(res_str, "default") == 0 || strcmp(res_str, "DEFAULT") == 0) {
|
if (strcmp(res_str, "default") == 0 || strcmp(res_str, "DEFAULT") == 0) {
|
||||||
*width = DEFAULT_WIDTH;
|
*width = DEFAULT_WIDTH;
|
||||||
*height = DEFAULT_HEIGHT;
|
*height = DEFAULT_HEIGHT;
|
||||||
@@ -1744,10 +1749,10 @@ static int get_video_metadata(tev_encoder_t *config) {
|
|||||||
char command[1024];
|
char command[1024];
|
||||||
char *output;
|
char *output;
|
||||||
|
|
||||||
// Get all metadata in a single ffprobe call
|
// Get all metadata without frame count (much faster)
|
||||||
snprintf(command, sizeof(command),
|
snprintf(command, sizeof(command),
|
||||||
"ffprobe -v quiet -count_frames "
|
"ffprobe -v quiet "
|
||||||
"-show_entries stream=nb_read_frames,r_frame_rate:format=duration "
|
"-show_entries stream=r_frame_rate:format=duration "
|
||||||
"-select_streams v:0 -of csv=p=0 \"%s\" 2>/dev/null; "
|
"-select_streams v:0 -of csv=p=0 \"%s\" 2>/dev/null; "
|
||||||
"ffprobe -v quiet -select_streams a:0 -show_entries stream=index -of csv=p=0 \"%s\" 2>/dev/null",
|
"ffprobe -v quiet -select_streams a:0 -show_entries stream=index -of csv=p=0 \"%s\" 2>/dev/null",
|
||||||
config->input_file, config->input_file);
|
config->input_file, config->input_file);
|
||||||
@@ -1765,12 +1770,9 @@ static int get_video_metadata(tev_encoder_t *config) {
|
|||||||
|
|
||||||
while (line && line_num < 2) {
|
while (line && line_num < 2) {
|
||||||
switch (line_num) {
|
switch (line_num) {
|
||||||
case 0: // Line format: "framerate,framecount" (e.g., "30000/1001,4423"), (e.g., "30/1,7514")
|
case 0: // Line format: "framerate" (e.g., "30000/1001"), (e.g., "30/1")
|
||||||
{
|
{
|
||||||
char *comma = strchr(line, ',');
|
// Parse frame rate
|
||||||
if (comma) {
|
|
||||||
*comma = '\0'; // Split at comma
|
|
||||||
// Parse frame rate (first part)
|
|
||||||
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;
|
||||||
@@ -1780,9 +1782,8 @@ static int get_video_metadata(tev_encoder_t *config) {
|
|||||||
config->fps = (int)round(atof(line));
|
config->fps = (int)round(atof(line));
|
||||||
config->is_ntsc_framerate = 0;
|
config->is_ntsc_framerate = 0;
|
||||||
}
|
}
|
||||||
// Parse frame count (second part)
|
// Frame count will be determined during encoding
|
||||||
config->total_frames = atoi(comma + 1);
|
config->total_frames = 0;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 1: // duration in seconds
|
case 1: // duration in seconds
|
||||||
@@ -1798,12 +1799,7 @@ static int get_video_metadata(tev_encoder_t *config) {
|
|||||||
|
|
||||||
free(output);
|
free(output);
|
||||||
|
|
||||||
// Validate frame count using duration if needed
|
// Store input framerate for later calculations
|
||||||
if (config->total_frames <= 0 && config->duration > 0) {
|
|
||||||
config->total_frames = (int)(config->duration * config->fps);
|
|
||||||
}
|
|
||||||
|
|
||||||
// calculate new total_frames if the user has requested a custom framerate
|
|
||||||
float inputFramerate;
|
float inputFramerate;
|
||||||
if (input_is_ntsc_framerate) {
|
if (input_is_ntsc_framerate) {
|
||||||
inputFramerate = config->fps * 1000.f / 1001.f;
|
inputFramerate = config->fps * 1000.f / 1001.f;
|
||||||
@@ -1811,19 +1807,18 @@ static int get_video_metadata(tev_encoder_t *config) {
|
|||||||
inputFramerate = config->fps * 1.f;
|
inputFramerate = config->fps * 1.f;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config->output_fps > 0) {
|
// Frame count will be determined during encoding
|
||||||
config->total_frames = (int)(config->total_frames * (config->output_fps / inputFramerate));
|
config->total_frames = 0;
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(stderr, "Video metadata:\n");
|
fprintf(stderr, "Video metadata:\n");
|
||||||
fprintf(stderr, " Frames: %d\n", config->total_frames);
|
fprintf(stderr, " Frames: (will be determined during encoding)\n");
|
||||||
fprintf(stderr, " FPS: %.2f\n", inputFramerate);
|
fprintf(stderr, " FPS: %.2f\n", inputFramerate);
|
||||||
fprintf(stderr, " Duration: %.2fs\n", config->duration);
|
fprintf(stderr, " Duration: %.2fs\n", config->duration);
|
||||||
fprintf(stderr, " Audio: %s\n", config->has_audio ? "Yes" : "No");
|
fprintf(stderr, " Audio: %s\n", config->has_audio ? "Yes" : "No");
|
||||||
fprintf(stderr, " Resolution: %dx%d (%s)\n", config->width, config->height,
|
fprintf(stderr, " Resolution: %dx%d (%s)\n", config->width, config->height,
|
||||||
config->progressive_mode ? "progressive" : "interlaced");
|
config->progressive_mode ? "progressive" : "interlaced");
|
||||||
|
|
||||||
return (config->total_frames > 0 && config->fps > 0);
|
return (config->fps > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start FFmpeg process for video conversion with frame rate support
|
// Start FFmpeg process for video conversion with frame rate support
|
||||||
@@ -2043,7 +2038,7 @@ static void show_usage(const char *program_name) {
|
|||||||
printf(" -o, --output FILE Output video file (use '-' for stdout)\n");
|
printf(" -o, --output FILE Output video file (use '-' for stdout)\n");
|
||||||
printf(" -s, --size WxH Video size (default: %dx%d)\n", DEFAULT_WIDTH, DEFAULT_HEIGHT);
|
printf(" -s, --size WxH Video size (default: %dx%d)\n", DEFAULT_WIDTH, DEFAULT_HEIGHT);
|
||||||
printf(" -f, --fps N Output frames per second (enables frame rate conversion)\n");
|
printf(" -f, --fps N Output frames per second (enables frame rate conversion)\n");
|
||||||
printf(" -q, --quality N Quality level 0-4 (default: 1, only decides audio rate in quantiser mode)\n");
|
printf(" -q, --quality N Quality level 0-4 (default: 2, only decides audio rate in quantiser mode)\n");
|
||||||
printf(" -Q, --quantiser N Quantiser level 0-100 (100: lossless, 0: potato)\n");
|
printf(" -Q, --quantiser N Quantiser level 0-100 (100: lossless, 0: potato)\n");
|
||||||
// printf(" -b, --bitrate N Target bitrate in kbps (enables bitrate control mode; DON'T USE - NOT WORKING AS INTENDED)\n");
|
// printf(" -b, --bitrate N Target bitrate in kbps (enables bitrate control mode; DON'T USE - NOT WORKING AS INTENDED)\n");
|
||||||
printf(" -p, --progressive Use progressive scan (default: interlaced)\n");
|
printf(" -p, --progressive Use progressive scan (default: interlaced)\n");
|
||||||
@@ -2067,6 +2062,7 @@ static void show_usage(const char *program_name) {
|
|||||||
printf("\nVideo Size Keywords:");
|
printf("\nVideo Size Keywords:");
|
||||||
printf("\n -s cif: equal to 352x288");
|
printf("\n -s cif: equal to 352x288");
|
||||||
printf("\n -s qcif: equal to 176x144");
|
printf("\n -s qcif: equal to 176x144");
|
||||||
|
printf("\n -s half: equal to %dx%d", DEFAULT_WIDTH >> 1, DEFAULT_HEIGHT >> 1);
|
||||||
printf("\n -s default: equal to %dx%d", DEFAULT_WIDTH, DEFAULT_HEIGHT);
|
printf("\n -s default: equal to %dx%d", DEFAULT_WIDTH, DEFAULT_HEIGHT);
|
||||||
printf("\n\n");
|
printf("\n\n");
|
||||||
printf("Features:\n");
|
printf("Features:\n");
|
||||||
@@ -2179,7 +2175,7 @@ int main(int argc, char *argv[]) {
|
|||||||
break;
|
break;
|
||||||
case 'q':
|
case 'q':
|
||||||
int qi = atoi(optarg);
|
int qi = atoi(optarg);
|
||||||
if (qi < 0 || qi > 4) {
|
if (qi < 0 || qi > 5) {
|
||||||
fprintf(stderr, "Invalid quality index: %d\nUse value between 0 and 4.\n", qi);
|
fprintf(stderr, "Invalid quality index: %d\nUse value between 0 and 4.\n", qi);
|
||||||
cleanup_encoder(enc);
|
cleanup_encoder(enc);
|
||||||
return 1;
|
return 1;
|
||||||
@@ -2258,7 +2254,6 @@ 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
|
||||||
@@ -2326,10 +2321,16 @@ int main(int argc, char *argv[]) {
|
|||||||
printf("Quantiser levels: %d, %d, %d\n", enc->qualityY, enc->qualityCo, enc->qualityCg);
|
printf("Quantiser levels: %d, %d, %d\n", enc->qualityY, enc->qualityCo, enc->qualityCg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process frames
|
// Process frames (read until EOF from FFmpeg, or frame limit in test mode)
|
||||||
int frame_count = 0;
|
int frame_count = 0;
|
||||||
while (frame_count < enc->total_frames) {
|
int continue_encoding = 1;
|
||||||
|
while (continue_encoding) {
|
||||||
if (test_mode) {
|
if (test_mode) {
|
||||||
|
// Test mode has a fixed frame count
|
||||||
|
if (frame_count >= enc->total_frames) {
|
||||||
|
continue_encoding = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
// Generate test frame with solid colours
|
// Generate test frame with solid colours
|
||||||
size_t rgb_size = enc->width * enc->height * 3;
|
size_t rgb_size = enc->width * enc->height * 3;
|
||||||
uint8_t test_r = 0, test_g = 0, test_b = 0;
|
uint8_t test_r = 0, test_g = 0, test_b = 0;
|
||||||
@@ -2389,6 +2390,7 @@ int main(int argc, char *argv[]) {
|
|||||||
printf("FFmpeg pipe error occurred\n");
|
printf("FFmpeg pipe error occurred\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
continue_encoding = 0;
|
||||||
break; // End of video or error
|
break; // End of video or error
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2426,16 +2428,28 @@ int main(int argc, char *argv[]) {
|
|||||||
double elapsed = (now.tv_sec - enc->start_time.tv_sec) +
|
double elapsed = (now.tv_sec - enc->start_time.tv_sec) +
|
||||||
(now.tv_usec - enc->start_time.tv_usec) / 1000000.0;
|
(now.tv_usec - enc->start_time.tv_usec) / 1000000.0;
|
||||||
double fps = frame_count / elapsed;
|
double fps = frame_count / elapsed;
|
||||||
printf("Encoded frame %d/%d (%.1f fps)\n", frame_count, enc->total_frames, fps);
|
printf("Encoded frame %d (%.1f fps)\n", frame_count, fps);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update actual frame count in encoder struct
|
||||||
|
enc->total_frames = frame_count;
|
||||||
|
|
||||||
// Write final sync packet
|
// Write final sync packet
|
||||||
uint8_t sync_packet = TEV_PACKET_SYNC;
|
uint8_t sync_packet = TEV_PACKET_SYNC;
|
||||||
fwrite(&sync_packet, 1, 1, output);
|
fwrite(&sync_packet, 1, 1, output);
|
||||||
sync_packet_count++;
|
sync_packet_count++;
|
||||||
|
|
||||||
|
// Update header with actual frame count (seek back to header position)
|
||||||
if (!enc->output_to_stdout) {
|
if (!enc->output_to_stdout) {
|
||||||
|
long current_pos = ftell(output);
|
||||||
|
fseek(output, 14, SEEK_SET); // Offset of total_frames field in header
|
||||||
|
uint32_t actual_frames = frame_count;
|
||||||
|
fwrite(&actual_frames, 4, 1, output);
|
||||||
|
fseek(output, current_pos, SEEK_SET); // Restore position
|
||||||
|
if (enc->verbose) {
|
||||||
|
printf("Updated header with actual frame count: %d\n", frame_count);
|
||||||
|
}
|
||||||
fclose(output);
|
fclose(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user