--arate option to override audio bitrate

This commit is contained in:
minjaesong
2025-09-18 01:28:02 +09:00
parent 89aada888d
commit c14b692114
2 changed files with 79 additions and 3 deletions

View File

@@ -125,6 +125,19 @@ static int calculate_max_decomp_levels(int width, int height) {
// MP2 audio rate table (same as TEV)
static const int MP2_RATE_TABLE[] = {128, 160, 224, 320, 384, 384};
// Valid MP2 bitrates as per MPEG-1 Layer II specification
static const int MP2_VALID_BITRATES[] = {32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384};
// Validate and return closest valid MP2 bitrate, or 0 if invalid
static int validate_mp2_bitrate(int bitrate) {
for (int i = 0; i < sizeof(MP2_VALID_BITRATES) / sizeof(int); i++) {
if (MP2_VALID_BITRATES[i] == bitrate) {
return bitrate; // Exact match
}
}
return 0; // Invalid bitrate
}
// Quality level to quantisation mapping for different channels
static const int QUALITY_Y[] = {60, 42, 25, 12, 6, 2};
static const int QUALITY_CO[] = {120, 90, 60, 30, 15, 3};
@@ -203,6 +216,7 @@ typedef struct {
size_t mp2_buffer_size;
int mp2_packet_size;
int mp2_rate_index;
int audio_bitrate; // Custom audio bitrate (0 = use quality table)
int target_audio_buffer_size;
double audio_frames_in_buffer;
@@ -306,9 +320,11 @@ static void show_usage(const char *program_name) {
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(" -q, --quality N Quality level 0-5 (default: 2)\n");
printf(" -Q, --quantiser Y,Co,Cg Quantiser levels 0-255 for each channel (1: lossless, 255: potato)\n");
printf(" -Q, --quantiser Y,Co,Cg Quantiser levels 1-255 for each channel (1: lossless, 255: potato)\n");
// printf(" -w, --wavelet N Wavelet filter: 0=5/3 reversible, 1=9/7 irreversible (default: 1)\n");
// printf(" -b, --bitrate N Target bitrate in kbps (enables bitrate control mode)\n");
printf(" --arate N MP2 audio bitrate in kbps (overrides quality-based audio rate)\n");
printf(" Valid values: 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384\n");
printf(" -S, --subtitles FILE SubRip (.srt) or SAMI (.smi) subtitle file\n");
printf(" -v, --verbose Verbose output\n");
printf(" -t, --test Test mode: generate solid colour frames\n");
@@ -370,6 +386,7 @@ static tav_encoder_t* create_encoder(void) {
enc->quantiser_cg = QUALITY_CG[DEFAULT_QUALITY];
enc->intra_only = 1;
enc->monoblock = 1; // Default to monoblock mode
enc->audio_bitrate = 0; // 0 = use quality table
return enc;
}
@@ -1418,9 +1435,15 @@ static int start_audio_conversion(tav_encoder_t *enc) {
if (!enc->has_audio) return 1;
char command[2048];
int bitrate;
if (enc->audio_bitrate > 0) {
bitrate = enc->audio_bitrate;
} else {
bitrate = enc->lossless ? 384 : MP2_RATE_TABLE[enc->quality_level];
}
snprintf(command, sizeof(command),
"ffmpeg -v quiet -i \"%s\" -acodec libtwolame -psymodel 4 -b:a %dk -ar 32000 -ac 2 -y \"%s\" 2>/dev/null",
enc->input_file, enc->lossless ? 384 : MP2_RATE_TABLE[enc->quality_level], TEMP_AUDIO_FILE);
enc->input_file, bitrate, TEMP_AUDIO_FILE);
int result = system(command);
if (result == 0) {
@@ -2200,6 +2223,7 @@ int main(int argc, char *argv[]) {
{"quantizer", required_argument, 0, 'Q'},
// {"wavelet", required_argument, 0, 'w'},
{"bitrate", required_argument, 0, 'b'},
{"arate", required_argument, 0, 1400},
{"subtitle", required_argument, 0, 'S'},
{"subtitles", required_argument, 0, 'S'},
{"verbose", no_argument, 0, 'v'},
@@ -2277,6 +2301,22 @@ int main(int argc, char *argv[]) {
case 1006: // --intra-only
enc->intra_only = 0;
break;
case 1400: // --arate
{
int bitrate = atoi(optarg);
int valid_bitrate = validate_mp2_bitrate(bitrate);
if (valid_bitrate == 0) {
fprintf(stderr, "Error: Invalid MP2 bitrate %d. Valid values are: ", bitrate);
for (int i = 0; i < sizeof(MP2_VALID_BITRATES) / sizeof(int); i++) {
fprintf(stderr, "%d%s", MP2_VALID_BITRATES[i],
(i < sizeof(MP2_VALID_BITRATES) / sizeof(int) - 1) ? ", " : "\n");
}
cleanup_encoder(enc);
return 1;
}
enc->audio_bitrate = valid_bitrate;
}
break;
case 1004: // --help
show_usage(argv[0]);
cleanup_encoder(enc);

View File

@@ -48,6 +48,20 @@ static inline float FCLAMP(float x, float min, float max) {
// 56 96 128 192 256 Claude Opus 4.1 (with data analysis)
// 64 96 128 192 256 ChatGPT-5 (without data analysis)
static const int MP2_RATE_TABLE[] = {128, 160, 224, 320, 384, 384};
// Valid MP2 bitrates as per MPEG-1 Layer II specification
static const int MP2_VALID_BITRATES[] = {32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384};
// Validate and return closest valid MP2 bitrate, or 0 if invalid
static int validate_mp2_bitrate(int bitrate) {
for (int i = 0; i < sizeof(MP2_VALID_BITRATES) / sizeof(int); i++) {
if (MP2_VALID_BITRATES[i] == bitrate) {
return bitrate; // Exact match
}
}
return 0; // Invalid bitrate
}
// 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)
// 5 25 50 75 90 Claude Opus 4.1 (with data analysis)
@@ -191,6 +205,7 @@ typedef struct {
FILE *mp2_file;
int mp2_packet_size;
int mp2_rate_index;
int audio_bitrate; // Custom audio bitrate (0 = use quality table)
size_t audio_remaining;
uint8_t *mp2_buffer;
double audio_frames_in_buffer;
@@ -1899,6 +1914,7 @@ static tev_encoder_t* init_encoder(void) {
enc->qualityCg = enc->qualityCo / 2;
enc->mp2_packet_size = 0; // Will be detected from MP2 header
enc->mp2_rate_index = 0;
enc->audio_bitrate = 0; // 0 = use quality table
enc->audio_frames_in_buffer = 0;
enc->target_audio_buffer_size = 4;
enc->width = DEFAULT_WIDTH;
@@ -2387,9 +2403,10 @@ static int start_audio_conversion(tev_encoder_t *enc) {
if (!enc->has_audio) return 1;
char command[2048];
int bitrate = (enc->audio_bitrate > 0) ? enc->audio_bitrate : MP2_RATE_TABLE[enc->qualityIndex];
snprintf(command, sizeof(command),
"ffmpeg -v quiet -i \"%s\" -acodec libtwolame -psymodel 4 -b:a %dk -ar %d -ac 2 -y \"%s\" 2>/dev/null",
enc->input_file, MP2_RATE_TABLE[enc->qualityIndex], MP2_SAMPLE_RATE, TEMP_AUDIO_FILE);
enc->input_file, bitrate, MP2_SAMPLE_RATE, TEMP_AUDIO_FILE);
int result = system(command);
if (result == 0) {
@@ -2540,6 +2557,8 @@ static void show_usage(const char *program_name) {
printf(" -q, --quality N Quality level 0-4 (default: 2, only decides audio rate in quantiser/lossless mode)\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(" --arate N MP2 audio bitrate in kbps (overrides quality-based audio rate)\n");
printf(" Valid values: 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384\n");
printf(" -p, --progressive Use progressive scan (default: interlaced)\n");
printf(" -S, --subtitles FILE SubRip (.srt) or SAMI (.smi) subtitle file\n");
printf(" -v, --verbose Verbose output\n");
@@ -2633,6 +2652,7 @@ int main(int argc, char *argv[]) {
{"quantiser", required_argument, 0, 'Q'},
{"quantizer", required_argument, 0, 'Q'},
{"bitrate", required_argument, 0, 'b'},
{"arate", required_argument, 0, 1400},
{"progressive", no_argument, 0, 'p'},
{"verbose", no_argument, 0, 'v'},
{"test", no_argument, 0, 't'},
@@ -2716,6 +2736,22 @@ int main(int argc, char *argv[]) {
case 1300: // --ictcp
enc->ictcp_mode = 1;
break;
case 1400: // --arate
{
int bitrate = atoi(optarg);
int valid_bitrate = validate_mp2_bitrate(bitrate);
if (valid_bitrate == 0) {
fprintf(stderr, "Error: Invalid MP2 bitrate %d. Valid values are: ", bitrate);
for (int i = 0; i < sizeof(MP2_VALID_BITRATES) / sizeof(int); i++) {
fprintf(stderr, "%d%s", MP2_VALID_BITRATES[i],
(i < sizeof(MP2_VALID_BITRATES) / sizeof(int) - 1) ? ", " : "\n");
}
cleanup_encoder(enc);
return 1;
}
enc->audio_bitrate = valid_bitrate;
}
break;
case 0:
if (strcmp(long_options[option_index].name, "help") == 0) {
show_usage(argv[0]);