mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-03-17 00:16:04 +09:00
TAV fix: '--intra-only --interlaced' would cause crash due to buffer size mismatch
This commit is contained in:
@@ -10979,6 +10979,14 @@ int main(int argc, char *argv[]) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Halve internal height for interlaced mode (FFmpeg will output half-height fields)
|
||||||
|
if (!enc->progressive_mode) {
|
||||||
|
enc->height = enc->height / 2;
|
||||||
|
if (enc->verbose) {
|
||||||
|
printf("Interlaced mode: internal height adjusted to %d\n", enc->height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// generate division series
|
// generate division series
|
||||||
enc->widths = malloc((enc->decomp_levels + 2) * sizeof(int));
|
enc->widths = malloc((enc->decomp_levels + 2) * sizeof(int));
|
||||||
enc->heights = malloc((enc->decomp_levels + 2) * sizeof(int));
|
enc->heights = malloc((enc->decomp_levels + 2) * sizeof(int));
|
||||||
@@ -10994,15 +11002,6 @@ int main(int argc, char *argv[]) {
|
|||||||
enc->quantiser_cg = enc->quantiser_co;
|
enc->quantiser_cg = enc->quantiser_co;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Halve internal height for interlaced mode (FFmpeg will output half-height fields)
|
|
||||||
if (!enc->progressive_mode) {
|
|
||||||
enc->height = enc->height / 2;
|
|
||||||
if (enc->verbose) {
|
|
||||||
printf("Interlaced mode: internal height adjusted to %d\n", enc->height);
|
|
||||||
}
|
|
||||||
enc->intra_only = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// disable perceptual tuning if wavelet filter is not CDF 9/7
|
// disable perceptual tuning if wavelet filter is not CDF 9/7
|
||||||
if (enc->wavelet_filter != WAVELET_9_7_IRREVERSIBLE) {
|
if (enc->wavelet_filter != WAVELET_9_7_IRREVERSIBLE) {
|
||||||
enc->perceptual_tuning = 0;
|
enc->perceptual_tuning = 0;
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ Usage:
|
|||||||
#define PIXEL_H (GRID_H * CHAR_H) // 448
|
#define PIXEL_H (GRID_H * CHAR_H) // 448
|
||||||
#define PATCH_SZ (CHAR_W * CHAR_H)
|
#define PATCH_SZ (CHAR_W * CHAR_H)
|
||||||
#define SAMPLE_RATE 32000
|
#define SAMPLE_RATE 32000
|
||||||
|
#define MP2_DEFAULT_PACKET_SIZE 1152
|
||||||
|
|
||||||
// TAV packet types
|
// TAV packet types
|
||||||
#define PACKET_TIMECODE 0xFD
|
#define PACKET_TIMECODE 0xFD
|
||||||
@@ -70,6 +71,28 @@ Usage:
|
|||||||
#define COLOR_BLACK 0xF0
|
#define COLOR_BLACK 0xF0
|
||||||
#define COLOR_WHITE 0xFE
|
#define COLOR_WHITE 0xFE
|
||||||
|
|
||||||
|
// Generate random filename for temporary audio file
|
||||||
|
static void generate_random_filename(char *filename) {
|
||||||
|
srand(time(NULL));
|
||||||
|
|
||||||
|
const char charset[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||||
|
const int charset_size = sizeof(charset) - 1;
|
||||||
|
|
||||||
|
// Start with the prefix
|
||||||
|
strcpy(filename, "/tmp/");
|
||||||
|
|
||||||
|
// Generate 32 random characters
|
||||||
|
for (int i = 0; i < 32; i++) {
|
||||||
|
filename[5 + i] = charset[rand() % charset_size];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the .mp2 extension
|
||||||
|
strcpy(filename + 37, ".mp2");
|
||||||
|
filename[41] = '\0'; // Null terminate
|
||||||
|
}
|
||||||
|
|
||||||
|
char TEMP_AUDIO_FILE[42];
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t *data; // Binary glyph data (PATCH_SZ bytes per glyph)
|
uint8_t *data; // Binary glyph data (PATCH_SZ bytes per glyph)
|
||||||
int count; // Number of glyphs
|
int count; // Number of glyphs
|
||||||
@@ -244,6 +267,24 @@ uint64_t get_current_time_ns(void) {
|
|||||||
return (uint64_t)tv.tv_sec * 1000000000ULL + (uint64_t)tv.tv_usec * 1000ULL;
|
return (uint64_t)tv.tv_sec * 1000000000ULL + (uint64_t)tv.tv_usec * 1000ULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parse MP2 packet header to get accurate packet size
|
||||||
|
int get_mp2_packet_size(uint8_t *header) {
|
||||||
|
int bitrate_index = (header[2] >> 4) & 0x0F;
|
||||||
|
int bitrates[] = {0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384};
|
||||||
|
if (bitrate_index >= 15) return MP2_DEFAULT_PACKET_SIZE;
|
||||||
|
|
||||||
|
int bitrate = bitrates[bitrate_index];
|
||||||
|
if (bitrate == 0) return MP2_DEFAULT_PACKET_SIZE;
|
||||||
|
|
||||||
|
int sampling_freq_index = (header[2] >> 2) & 0x03;
|
||||||
|
int sampling_freqs[] = {44100, 48000, 32000, 0};
|
||||||
|
int sampling_freq = sampling_freqs[sampling_freq_index];
|
||||||
|
if (sampling_freq == 0) return MP2_DEFAULT_PACKET_SIZE;
|
||||||
|
|
||||||
|
int padding = (header[2] >> 1) & 0x01;
|
||||||
|
return (144 * bitrate * 1000) / sampling_freq + padding;
|
||||||
|
}
|
||||||
|
|
||||||
// Write Videotex header (32 bytes, similar to TAV but simpler)
|
// Write Videotex header (32 bytes, similar to TAV but simpler)
|
||||||
void write_videotex_header(FILE *f, uint8_t fps, uint32_t total_frames) {
|
void write_videotex_header(FILE *f, uint8_t fps, uint32_t total_frames) {
|
||||||
fwrite("\x1FTSVMTAV", 8, 1, f);
|
fwrite("\x1FTSVMTAV", 8, 1, f);
|
||||||
@@ -252,10 +293,10 @@ void write_videotex_header(FILE *f, uint8_t fps, uint32_t total_frames) {
|
|||||||
fputc(1, f);
|
fputc(1, f);
|
||||||
|
|
||||||
// Grid dimensions (uint8 each)
|
// Grid dimensions (uint8 each)
|
||||||
fputc(GRID_W, f); // cols = 80
|
uint16_t width = GRID_W;
|
||||||
fputc(0, f);
|
uint16_t height = GRID_H;
|
||||||
fputc(GRID_H, f); // rows = 32
|
fwrite(&width, sizeof(uint16_t), 1, f); // cols = 80
|
||||||
fputc(0, f);
|
fwrite(&height, sizeof(uint16_t), 1, f); // rows = 32
|
||||||
|
|
||||||
// FPS (uint8)
|
// FPS (uint8)
|
||||||
fputc(fps, f);
|
fputc(fps, f);
|
||||||
@@ -457,6 +498,9 @@ int main(int argc, char **argv) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generate random temp filename for audio
|
||||||
|
generate_random_filename(TEMP_AUDIO_FILE);
|
||||||
|
|
||||||
// Capture creation time and FFmpeg version for extended header
|
// Capture creation time and FFmpeg version for extended header
|
||||||
uint64_t creation_time_ns = get_current_time_ns();
|
uint64_t creation_time_ns = get_current_time_ns();
|
||||||
char *ffmpeg_version = get_ffmpeg_version();
|
char *ffmpeg_version = get_ffmpeg_version();
|
||||||
@@ -487,18 +531,29 @@ int main(int argc, char **argv) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open FFmpeg pipe for MP2 audio (32 KHz stereo)
|
// Extract MP2 audio to temporary file using libtwolame
|
||||||
|
fprintf(stderr, "Extracting MP2 audio...\n");
|
||||||
char audio_cmd[1024];
|
char audio_cmd[1024];
|
||||||
snprintf(audio_cmd, sizeof(audio_cmd),
|
snprintf(audio_cmd, sizeof(audio_cmd),
|
||||||
"ffmpeg -i \"%s\" -vn -ar %d -ac 2 -f mp2 -b:a 96k - 2>/dev/null",
|
"ffmpeg -v quiet -i \"%s\" -acodec libtwolame -psymodel 4 -b:a 224k -ar %d -ac 2 -y \"%s\" 2>/dev/null",
|
||||||
input_video, SAMPLE_RATE);
|
input_video, SAMPLE_RATE, TEMP_AUDIO_FILE);
|
||||||
|
|
||||||
fprintf(stderr, "Opening audio stream...\n");
|
int audio_result = system(audio_cmd);
|
||||||
FILE *audio_pipe = popen(audio_cmd, "r");
|
if (audio_result != 0) {
|
||||||
if (!audio_pipe) {
|
fprintf(stderr, "Warning: Audio extraction failed, continuing without audio\n");
|
||||||
fprintf(stderr, "Failed to open audio FFmpeg pipe\n");
|
}
|
||||||
pclose(video_pipe);
|
|
||||||
return 1;
|
// Open MP2 file for reading
|
||||||
|
FILE *mp2_file = NULL;
|
||||||
|
long audio_remaining = 0;
|
||||||
|
if (audio_result == 0) {
|
||||||
|
mp2_file = fopen(TEMP_AUDIO_FILE, "rb");
|
||||||
|
if (mp2_file) {
|
||||||
|
fseek(mp2_file, 0, SEEK_END);
|
||||||
|
audio_remaining = ftell(mp2_file);
|
||||||
|
fseek(mp2_file, 0, SEEK_SET);
|
||||||
|
fprintf(stderr, "Audio ready: %ld bytes\n", audio_remaining);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open output file
|
// Open output file
|
||||||
@@ -506,7 +561,7 @@ int main(int argc, char **argv) {
|
|||||||
if (!out) {
|
if (!out) {
|
||||||
fprintf(stderr, "Failed to open output file\n");
|
fprintf(stderr, "Failed to open output file\n");
|
||||||
pclose(video_pipe);
|
pclose(video_pipe);
|
||||||
pclose(audio_pipe);
|
if (mp2_file) fclose(mp2_file);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -555,14 +610,19 @@ int main(int argc, char **argv) {
|
|||||||
uint8_t *fg_col = malloc(GRID_W * GRID_H);
|
uint8_t *fg_col = malloc(GRID_W * GRID_H);
|
||||||
uint8_t *chars = malloc(GRID_W * GRID_H);
|
uint8_t *chars = malloc(GRID_W * GRID_H);
|
||||||
|
|
||||||
// Audio buffer (read MP2 frames in 1152-sample chunks, ~36ms at 32 KHz)
|
// Audio buffer for MP2 packets
|
||||||
#define AUDIO_CHUNK_SIZE 8192 // Arbitrary MP2 frame buffer size
|
#define MP2_BUFFER_SIZE 2048
|
||||||
uint8_t *audio_buffer = malloc(AUDIO_CHUNK_SIZE);
|
uint8_t *audio_buffer = malloc(MP2_BUFFER_SIZE);
|
||||||
size_t audio_available = 0;
|
|
||||||
|
|
||||||
uint32_t frame_num = 0;
|
uint32_t frame_num = 0;
|
||||||
uint64_t total_audio_bytes = 0;
|
uint64_t total_audio_bytes = 0;
|
||||||
|
|
||||||
|
// Audio timing calculation
|
||||||
|
double frame_audio_time = 1.0 / fps_float; // Time per video frame
|
||||||
|
double packet_audio_time = (double)MP2_DEFAULT_PACKET_SIZE / SAMPLE_RATE; // Time per audio packet
|
||||||
|
double packets_per_frame = frame_audio_time / packet_audio_time;
|
||||||
|
double audio_frames_in_buffer = 0.0; // Simulated audio buffer level
|
||||||
|
|
||||||
fprintf(stderr, "Encoding text-mode video (%dx%d chars, %dx%d pixels)...\n",
|
fprintf(stderr, "Encoding text-mode video (%dx%d chars, %dx%d pixels)...\n",
|
||||||
GRID_W, GRID_H, PIXEL_W, PIXEL_H);
|
GRID_W, GRID_H, PIXEL_W, PIXEL_H);
|
||||||
|
|
||||||
@@ -575,12 +635,54 @@ int main(int argc, char **argv) {
|
|||||||
// Calculate timecode in nanoseconds
|
// Calculate timecode in nanoseconds
|
||||||
uint64_t timecode_ns = (uint64_t)(frame_num * 1000000000.0 / fps_float);
|
uint64_t timecode_ns = (uint64_t)(frame_num * 1000000000.0 / fps_float);
|
||||||
|
|
||||||
// Write audio packet first (if available)
|
// Write audio packets for this frame (based on timing)
|
||||||
// Try to read ~1 frame worth of audio
|
if (mp2_file && audio_remaining > 0) {
|
||||||
audio_available = fread(audio_buffer, 1, AUDIO_CHUNK_SIZE, audio_pipe);
|
// Simulate buffer consumption
|
||||||
if (audio_available > 0) {
|
audio_frames_in_buffer -= packets_per_frame;
|
||||||
write_audio_mp2(out, audio_buffer, audio_available);
|
|
||||||
total_audio_bytes += audio_available;
|
// Calculate how many packets we need to maintain buffer
|
||||||
|
double target_level = fmax(packets_per_frame, 2.0);
|
||||||
|
int packets_to_insert = 0;
|
||||||
|
|
||||||
|
if (audio_frames_in_buffer < target_level) {
|
||||||
|
double deficit = target_level - audio_frames_in_buffer;
|
||||||
|
packets_to_insert = (int)ceil(deficit);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert the calculated number of audio packets
|
||||||
|
for (int q = 0; q < packets_to_insert; q++) {
|
||||||
|
// Peek at header to get actual packet size
|
||||||
|
long pos = ftell(mp2_file);
|
||||||
|
uint8_t header[4];
|
||||||
|
if (fread(header, 1, 4, mp2_file) != 4) break;
|
||||||
|
fseek(mp2_file, pos, SEEK_SET); // Rewind to re-read with full packet
|
||||||
|
|
||||||
|
int actual_packet_size = get_mp2_packet_size(header);
|
||||||
|
size_t bytes_to_read = actual_packet_size;
|
||||||
|
|
||||||
|
// Clamp to remaining audio
|
||||||
|
if (bytes_to_read > audio_remaining) {
|
||||||
|
bytes_to_read = audio_remaining;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanity check
|
||||||
|
if (bytes_to_read > MP2_BUFFER_SIZE) {
|
||||||
|
fprintf(stderr, "ERROR: MP2 packet size %zu exceeds buffer\n", bytes_to_read);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read full packet
|
||||||
|
size_t bytes_read = fread(audio_buffer, 1, bytes_to_read, mp2_file);
|
||||||
|
if (bytes_read == 0) break;
|
||||||
|
|
||||||
|
// Write MP2 audio packet
|
||||||
|
write_audio_mp2(out, audio_buffer, bytes_read);
|
||||||
|
|
||||||
|
// Track audio
|
||||||
|
audio_remaining -= bytes_read;
|
||||||
|
audio_frames_in_buffer++;
|
||||||
|
total_audio_bytes += bytes_read;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write timecode
|
// Write timecode
|
||||||
@@ -608,10 +710,27 @@ int main(int argc, char **argv) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read any remaining audio
|
// Write any remaining audio
|
||||||
while ((audio_available = fread(audio_buffer, 1, AUDIO_CHUNK_SIZE, audio_pipe)) > 0) {
|
if (mp2_file && audio_remaining > 0) {
|
||||||
write_audio_mp2(out, audio_buffer, audio_available);
|
while (audio_remaining > 0) {
|
||||||
total_audio_bytes += audio_available;
|
// Peek at header to get actual packet size
|
||||||
|
long pos = ftell(mp2_file);
|
||||||
|
uint8_t header[4];
|
||||||
|
if (fread(header, 1, 4, mp2_file) != 4) break;
|
||||||
|
fseek(mp2_file, pos, SEEK_SET);
|
||||||
|
|
||||||
|
int actual_packet_size = get_mp2_packet_size(header);
|
||||||
|
size_t bytes_to_read = (actual_packet_size < audio_remaining) ? actual_packet_size : audio_remaining;
|
||||||
|
|
||||||
|
if (bytes_to_read > MP2_BUFFER_SIZE) break;
|
||||||
|
|
||||||
|
size_t bytes_read = fread(audio_buffer, 1, bytes_to_read, mp2_file);
|
||||||
|
if (bytes_read == 0) break;
|
||||||
|
|
||||||
|
write_audio_mp2(out, audio_buffer, bytes_read);
|
||||||
|
audio_remaining -= bytes_read;
|
||||||
|
total_audio_bytes += bytes_read;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Final timing
|
// Final timing
|
||||||
@@ -628,7 +747,7 @@ int main(int argc, char **argv) {
|
|||||||
|
|
||||||
// Update total_frames in header
|
// Update total_frames in header
|
||||||
if (frame_num > 0) {
|
if (frame_num > 0) {
|
||||||
fseek(out, header_offset + 12, SEEK_SET); // Offset to total_frames field
|
fseek(out, header_offset + 14, SEEK_SET); // Offset to total_frames field
|
||||||
fwrite(&frame_num, sizeof(uint32_t), 1, out);
|
fwrite(&frame_num, sizeof(uint32_t), 1, out);
|
||||||
fprintf(stderr, "Updated total_frames in header: %u\n", frame_num);
|
fprintf(stderr, "Updated total_frames in header: %u\n", frame_num);
|
||||||
}
|
}
|
||||||
@@ -647,7 +766,10 @@ int main(int argc, char **argv) {
|
|||||||
|
|
||||||
// Cleanup
|
// Cleanup
|
||||||
pclose(video_pipe);
|
pclose(video_pipe);
|
||||||
pclose(audio_pipe);
|
if (mp2_file) {
|
||||||
|
fclose(mp2_file);
|
||||||
|
unlink(TEMP_AUDIO_FILE); // Remove temporary audio file
|
||||||
|
}
|
||||||
fclose(out);
|
fclose(out);
|
||||||
free(gray_pixels);
|
free(gray_pixels);
|
||||||
free(bg_col);
|
free(bg_col);
|
||||||
|
|||||||
Reference in New Issue
Block a user