TAV-DT: no Zstd

This commit is contained in:
minjaesong
2025-12-16 10:42:10 +09:00
parent 4929d84cec
commit 67413f2749
11 changed files with 273 additions and 136 deletions

View File

@@ -944,7 +944,7 @@ transmission capability, and region-of-interest coding.
- bit 2 = is lossless mode - bit 2 = is lossless mode
(shorthand for `-q 6 -Q0,0,0 -w 0 --intra-only --no-perceptual-tuning --arate 384`) (shorthand for `-q 6 -Q0,0,0 -w 0 --intra-only --no-perceptual-tuning --arate 384`)
- bit 3 = has region-of-interest coding (for still pictures only) - bit 3 = has region-of-interest coding (for still pictures only)
- bit 4 = reserved (crop encoding?) - bit 4 = no Zstd compression
- bit 7 = has no video - bit 7 = has no video
uint8 Encoder quality level (stored with bias of 1 (q0=1); used to derive anisotropy value) uint8 Encoder quality level (stored with bias of 1 (q0=1); used to derive anisotropy value)
uint8 Channel layout (bit-field: bit 0=has alpha, bit 1=has chroma inverted, bit 2=has luma inverted) uint8 Channel layout (bit-field: bit 0=has alpha, bit 1=has chroma inverted, bit 2=has luma inverted)
@@ -1659,8 +1659,8 @@ start of the next packet
uint24 Reed-Solomon Block Count uint24 Reed-Solomon Block Count
<TAD chunk header end; encoded with rate 1/2 LDPC> <TAD chunk header end; encoded with rate 1/2 LDPC>
<Reed-Solomon (255,223) block start> <Reed-Solomon (255,223) block start>
bytes Zstd-compressed TAD bytes TAD (EZBC, no Zstd)
bytes Parity for Zstd-compressed TAD bytes Parity for TAD
<Reed-Solomon (255,223) block end> <Reed-Solomon (255,223) block end>
bytes TAV with forward error correction bytes TAV with forward error correction
<TAV header start> <TAV header start>
@@ -1669,8 +1669,8 @@ start of the next packet
uint24 Reed-Solomon Block Count uint24 Reed-Solomon Block Count
<TAV header end; encoded with rate 1/2 LDPC> <TAV header end; encoded with rate 1/2 LDPC>
<Reed-Solomon (255,223) block start> <Reed-Solomon (255,223) block start>
bytes Zstd-compressed Unified Block Data bytes TAV (EZBC, no Zstd)
bytes Parity for Zstd-compressed Unified Block Data bytes Parity for TAV
<Reed-Solomon (255,223) block end> <Reed-Solomon (255,223) block end>
Q1. Why headers have such low encoding rate (n byte input -> 2n byte output)? Q1. Why headers have such low encoding rate (n byte input -> 2n byte output)?

View File

@@ -33,18 +33,20 @@ static inline int tad32_quality_to_max_index(int quality) {
* @param max_index Maximum quantisation index (7=3bit, 15=4bit, 31=5bit, 63=6bit, 127=7bit) * @param max_index Maximum quantisation index (7=3bit, 15=4bit, 31=5bit, 63=6bit, 127=7bit)
* @param quantiser_scale Quantiser scaling factor (1.0=baseline, 2.0=2x coarser quantisation) * @param quantiser_scale Quantiser scaling factor (1.0=baseline, 2.0=2x coarser quantisation)
* Higher values = more aggressive quantisation = smaller files * Higher values = more aggressive quantisation = smaller files
* @param zstd_level Zstd compression level (1-22). Use negative value to disable compression.
* When disabled, MSB of payload_size is set to indicate uncompressed data.
* @param output Output buffer (must be large enough) * @param output Output buffer (must be large enough)
* @return Number of bytes written to output, or 0 on error * @return Number of bytes written to output, or 0 on error
* *
* Output format: * Output format:
* uint16 sample_count (samples per channel) * uint16 sample_count (samples per channel)
* uint8 max_index (maximum quantisation index) * uint8 max_index (maximum quantisation index)
* uint32 payload_size (bytes in payload) * uint32 payload_size (bytes in payload; MSB=1 indicates uncompressed)
* * payload (encoded M/S data, Zstd-compressed with 2-bit twobitmap) * * payload (encoded M/S data, optionally Zstd-compressed)
*/ */
size_t tad32_encode_chunk(const float *pcm32_stereo, size_t num_samples, size_t tad32_encode_chunk(const float *pcm32_stereo, size_t num_samples,
int max_index, int max_index,
float quantiser_scale, uint8_t *output); float quantiser_scale, int zstd_level, uint8_t *output);
/** /**
* Print accumulated coefficient statistics * Print accumulated coefficient statistics

View File

@@ -27,6 +27,7 @@ typedef struct {
uint8_t quantiser_cg; // Base quantiser index for Cg/Cp uint8_t quantiser_cg; // Base quantiser index for Cg/Cp
uint8_t encoder_preset; // Encoder preset flags (sports, anime, etc.) uint8_t encoder_preset; // Encoder preset flags (sports, anime, etc.)
int monoblock; // 1=single tile (monoblock), 0=multi-tile int monoblock; // 1=single tile (monoblock), 0=multi-tile
int no_zstd; // 1=packets are uncompressed (Video Flags bit 4), 0=Zstd compressed
} tav_video_params_t; } tav_video_params_t;
// Create video decoder context // Create video decoder context

View File

@@ -878,9 +878,13 @@ int tad32_decode_chunk(const uint8_t *input, size_t input_size, uint8_t *pcmu8_s
uint8_t max_index = *read_ptr; uint8_t max_index = *read_ptr;
read_ptr += sizeof(uint8_t); read_ptr += sizeof(uint8_t);
uint32_t payload_size = *((const uint32_t*)read_ptr); uint32_t payload_size_field = *((const uint32_t*)read_ptr);
read_ptr += sizeof(uint32_t); read_ptr += sizeof(uint32_t);
// Check MSB for uncompressed flag
int is_uncompressed = (payload_size_field & 0x80000000) != 0;
uint32_t payload_size = payload_size_field & 0x7FFFFFFF;
// Calculate DWT levels from sample count // Calculate DWT levels from sample count
int dwt_levels = calculate_dwt_levels(sample_count); int dwt_levels = calculate_dwt_levels(sample_count);
if (dwt_levels < 0) { if (dwt_levels < 0) {
@@ -888,20 +892,30 @@ int tad32_decode_chunk(const uint8_t *input, size_t input_size, uint8_t *pcmu8_s
return -1; return -1;
} }
// Decompress Zstd // Decompress Zstd (or use raw data if uncompressed)
const uint8_t *payload;
uint8_t *decompressed = NULL; uint8_t *decompressed = NULL;
size_t actual_size;
int should_free_decompressed;
// Estimate decompressed size (generous upper bound) if (is_uncompressed) {
size_t decompressed_size = sample_count * 4 * sizeof(int8_t); // Data is not compressed - use directly
decompressed = malloc(decompressed_size); decompressed = (uint8_t *)read_ptr; // Cast away const, won't modify
actual_size = payload_size;
should_free_decompressed = 0;
} else {
// Normal Zstd decompression
// Estimate decompressed size (generous upper bound)
size_t decompressed_size = sample_count * 4 * sizeof(int8_t);
decompressed = malloc(decompressed_size);
size_t actual_size = ZSTD_decompress(decompressed, decompressed_size, read_ptr, payload_size); actual_size = ZSTD_decompress(decompressed, decompressed_size, read_ptr, payload_size);
if (ZSTD_isError(actual_size)) { if (ZSTD_isError(actual_size)) {
fprintf(stderr, "Error: Zstd decompression failed: %s\n", ZSTD_getErrorName(actual_size)); fprintf(stderr, "Error: Zstd decompression failed: %s\n", ZSTD_getErrorName(actual_size));
free(decompressed); free(decompressed);
return -1; return -1;
}
should_free_decompressed = 1;
} }
read_ptr += payload_size; read_ptr += payload_size;
@@ -926,7 +940,7 @@ int tad32_decode_chunk(const uint8_t *input, size_t input_size, uint8_t *pcmu8_s
int result = tad_decode_channel_ezbc(decompressed, actual_size, quant_mid, &mid_bytes_consumed); int result = tad_decode_channel_ezbc(decompressed, actual_size, quant_mid, &mid_bytes_consumed);
if (result != 0) { if (result != 0) {
fprintf(stderr, "Error: EZBC decoding failed for Mid channel\n"); fprintf(stderr, "Error: EZBC decoding failed for Mid channel\n");
free(decompressed); if (should_free_decompressed) free(decompressed);
free(quant_mid); free(quant_side); free(dwt_mid); free(dwt_side); free(quant_mid); free(quant_side); free(dwt_mid); free(dwt_side);
free(pcm32_left); free(pcm32_right); free(pcm8_left); free(pcm8_right); free(pcm32_left); free(pcm32_right); free(pcm8_left); free(pcm8_right);
return -1; return -1;
@@ -938,7 +952,7 @@ int tad32_decode_chunk(const uint8_t *input, size_t input_size, uint8_t *pcmu8_s
quant_side, &side_bytes_consumed); quant_side, &side_bytes_consumed);
if (result != 0) { if (result != 0) {
fprintf(stderr, "Error: EZBC decoding failed for Side channel\n"); fprintf(stderr, "Error: EZBC decoding failed for Side channel\n");
free(decompressed); if (should_free_decompressed) free(decompressed);
free(quant_mid); free(quant_side); free(dwt_mid); free(dwt_side); free(quant_mid); free(quant_side); free(dwt_mid); free(dwt_side);
free(pcm32_left); free(pcm32_right); free(pcm8_left); free(pcm8_right); free(pcm32_left); free(pcm32_right); free(pcm8_left); free(pcm8_right);
return -1; return -1;
@@ -978,7 +992,7 @@ int tad32_decode_chunk(const uint8_t *input, size_t input_size, uint8_t *pcmu8_s
// Cleanup // Cleanup
free(quant_mid); free(quant_side); free(dwt_mid); free(dwt_side); free(quant_mid); free(quant_side); free(dwt_mid); free(dwt_side);
free(pcm32_left); free(pcm32_right); free(pcm8_left); free(pcm8_right); free(pcm32_left); free(pcm32_right); free(pcm8_left); free(pcm8_right);
if (decompressed) free(decompressed); if (should_free_decompressed && decompressed) free(decompressed);
return 0; return 0;
} }

View File

@@ -1145,7 +1145,7 @@ size_t tad_encode_channel_ezbc(int8_t *coeffs, size_t count, uint8_t **output) {
size_t tad32_encode_chunk(const float *pcm32_stereo, size_t num_samples, size_t tad32_encode_chunk(const float *pcm32_stereo, size_t num_samples,
int max_index, int max_index,
float quantiser_scale, uint8_t *output) { float quantiser_scale, int zstd_level, uint8_t *output) {
// Calculate DWT levels from chunk size // Calculate DWT levels from chunk size
int dwt_levels = calculate_dwt_levels(num_samples); int dwt_levels = calculate_dwt_levels(num_samples);
if (dwt_levels < 0) { if (dwt_levels < 0) {
@@ -1232,7 +1232,7 @@ size_t tad32_encode_chunk(const float *pcm32_stereo, size_t num_samples,
free(mid_ezbc); free(mid_ezbc);
free(side_ezbc); free(side_ezbc);
// Step 6: Zstd compression // Step 6: Zstd compression (or bypass if zstd_level < 0)
uint8_t *write_ptr = output; uint8_t *write_ptr = output;
// Write chunk header // Write chunk header
@@ -1246,26 +1246,40 @@ size_t tad32_encode_chunk(const float *pcm32_stereo, size_t num_samples,
write_ptr += sizeof(uint32_t); write_ptr += sizeof(uint32_t);
size_t payload_size; size_t payload_size;
int is_uncompressed = 0;
size_t zstd_bound = ZSTD_compressBound(uncompressed_size); if (zstd_level < 0) {
uint8_t *zstd_buffer = malloc(zstd_bound); // Bypass Zstd compression - use raw EZBC data
payload_size = uncompressed_size;
memcpy(write_ptr, temp_buffer, payload_size);
is_uncompressed = 1;
} else {
// Normal Zstd compression
int effective_level = (zstd_level > 0) ? zstd_level : TAD32_ZSTD_LEVEL;
size_t zstd_bound = ZSTD_compressBound(uncompressed_size);
uint8_t *zstd_buffer = malloc(zstd_bound);
payload_size = ZSTD_compress(zstd_buffer, zstd_bound, temp_buffer, uncompressed_size, TAD32_ZSTD_LEVEL); payload_size = ZSTD_compress(zstd_buffer, zstd_bound, temp_buffer, uncompressed_size, effective_level);
if (ZSTD_isError(payload_size)) { if (ZSTD_isError(payload_size)) {
fprintf(stderr, "Error: Zstd compression failed: %s\n", ZSTD_getErrorName(payload_size)); fprintf(stderr, "Error: Zstd compression failed: %s\n", ZSTD_getErrorName(payload_size));
free(zstd_buffer);
free(pcm32_left); free(pcm32_right);
free(pcm32_mid); free(pcm32_side); free(dwt_mid); free(dwt_side);
free(quant_mid); free(quant_side); free(temp_buffer);
return 0;
}
memcpy(write_ptr, zstd_buffer, payload_size);
free(zstd_buffer); free(zstd_buffer);
free(pcm32_left); free(pcm32_right);
free(pcm32_mid); free(pcm32_side); free(dwt_mid); free(dwt_side);
free(quant_mid); free(quant_side); free(temp_buffer);
return 0;
} }
memcpy(write_ptr, zstd_buffer, payload_size); // Set payload size (MSB=1 indicates uncompressed data)
free(zstd_buffer); uint32_t size_field = (uint32_t)payload_size;
if (is_uncompressed) {
size_field |= 0x80000000;
*payload_size_ptr = (uint32_t)payload_size; }
*payload_size_ptr = size_field;
write_ptr += payload_size; write_ptr += payload_size;
// Cleanup // Cleanup

View File

@@ -1478,25 +1478,38 @@ int tav_video_decode_gop(tav_video_context_t *ctx,
const int height = ctx->params.height; const int height = ctx->params.height;
const int num_pixels = width * height; const int num_pixels = width * height;
// Decompress with Zstd // Decompress with Zstd (or use raw data if no_zstd flag is set)
const size_t decompressed_bound = ZSTD_getFrameContentSize(compressed_data, compressed_size); uint8_t *decompressed_data;
if (ZSTD_isError(decompressed_bound)) { size_t decompressed_size;
snprintf(ctx->error_msg, sizeof(ctx->error_msg), "Zstd decompression failed"); int should_free_data;
return -1;
}
uint8_t *decompressed_data = malloc(decompressed_bound); if (ctx->params.no_zstd) {
if (!decompressed_data) { // No Zstd compression - use data directly
snprintf(ctx->error_msg, sizeof(ctx->error_msg), "Memory allocation failed"); decompressed_data = (uint8_t *)compressed_data; // Cast away const, won't modify
return -1; decompressed_size = compressed_size;
} should_free_data = 0;
} else {
// Normal Zstd decompression
const size_t decompressed_bound = ZSTD_getFrameContentSize(compressed_data, compressed_size);
if (ZSTD_isError(decompressed_bound)) {
snprintf(ctx->error_msg, sizeof(ctx->error_msg), "Zstd decompression failed");
return -1;
}
const size_t decompressed_size = ZSTD_decompress(decompressed_data, decompressed_bound, decompressed_data = malloc(decompressed_bound);
compressed_data, compressed_size); if (!decompressed_data) {
if (ZSTD_isError(decompressed_size)) { snprintf(ctx->error_msg, sizeof(ctx->error_msg), "Memory allocation failed");
free(decompressed_data); return -1;
snprintf(ctx->error_msg, sizeof(ctx->error_msg), "Zstd decompression failed"); }
return -1;
decompressed_size = ZSTD_decompress(decompressed_data, decompressed_bound,
compressed_data, compressed_size);
if (ZSTD_isError(decompressed_size)) {
free(decompressed_data);
snprintf(ctx->error_msg, sizeof(ctx->error_msg), "Zstd decompression failed");
return -1;
}
should_free_data = 1;
} }
// Postprocess GOP data based on entropy coder type // Postprocess GOP data based on entropy coder type
@@ -1512,7 +1525,9 @@ int tav_video_decode_gop(tav_video_context_t *ctx,
gop_coeffs = postprocess_gop_raw(decompressed_data, decompressed_size, gop_size, num_pixels, ctx->params.channel_layout); gop_coeffs = postprocess_gop_raw(decompressed_data, decompressed_size, gop_size, num_pixels, ctx->params.channel_layout);
} }
free(decompressed_data); if (should_free_data) {
free(decompressed_data);
}
if (!gop_coeffs) { if (!gop_coeffs) {
snprintf(ctx->error_msg, sizeof(ctx->error_msg), "GOP postprocessing failed"); snprintf(ctx->error_msg, sizeof(ctx->error_msg), "GOP postprocessing failed");
@@ -1638,20 +1653,33 @@ int tav_video_decode_iframe(tav_video_context_t *ctx,
const int height = ctx->params.height; const int height = ctx->params.height;
const int num_pixels = width * height; const int num_pixels = width * height;
// Decompress // Decompress (or use raw data if no_zstd flag is set)
const size_t decompressed_bound = ZSTD_getFrameContentSize(compressed_data, packet_size); uint8_t *decompressed_data;
if (ZSTD_isError(decompressed_bound)) { size_t decompressed_size;
snprintf(ctx->error_msg, sizeof(ctx->error_msg), "Zstd decompression failed"); int should_free_data;
return -1;
}
uint8_t *decompressed_data = malloc(decompressed_bound); if (ctx->params.no_zstd) {
const size_t decompressed_size = ZSTD_decompress(decompressed_data, decompressed_bound, // No Zstd compression - use data directly
compressed_data, packet_size); decompressed_data = (uint8_t *)compressed_data;
if (ZSTD_isError(decompressed_size)) { decompressed_size = packet_size;
free(decompressed_data); should_free_data = 0;
snprintf(ctx->error_msg, sizeof(ctx->error_msg), "Zstd decompression failed"); } else {
return -1; // Normal Zstd decompression
const size_t decompressed_bound = ZSTD_getFrameContentSize(compressed_data, packet_size);
if (ZSTD_isError(decompressed_bound)) {
snprintf(ctx->error_msg, sizeof(ctx->error_msg), "Zstd decompression failed");
return -1;
}
decompressed_data = malloc(decompressed_bound);
decompressed_size = ZSTD_decompress(decompressed_data, decompressed_bound,
compressed_data, packet_size);
if (ZSTD_isError(decompressed_size)) {
free(decompressed_data);
snprintf(ctx->error_msg, sizeof(ctx->error_msg), "Zstd decompression failed");
return -1;
}
should_free_data = 1;
} }
// Allocate coefficient buffers // Allocate coefficient buffers
@@ -1666,7 +1694,9 @@ int tav_video_decode_iframe(tav_video_context_t *ctx,
postprocess_coefficients_ezbc(decompressed_data, num_pixels, coeffs_y, coeffs_co, coeffs_cg, ctx->params.channel_layout); postprocess_coefficients_ezbc(decompressed_data, num_pixels, coeffs_y, coeffs_co, coeffs_cg, ctx->params.channel_layout);
} }
free(decompressed_data); if (should_free_data) {
free(decompressed_data);
}
// Dequantise // Dequantise
const float base_q_y = QLUT[ctx->params.quantiser_y]; const float base_q_y = QLUT[ctx->params.quantiser_y];
@@ -1740,20 +1770,33 @@ int tav_video_decode_pframe(tav_video_context_t *ctx,
const int height = ctx->params.height; const int height = ctx->params.height;
const int num_pixels = width * height; const int num_pixels = width * height;
// Decompress // Decompress (or use raw data if no_zstd flag is set)
const size_t decompressed_bound = ZSTD_getFrameContentSize(compressed_data, packet_size); uint8_t *decompressed_data;
if (ZSTD_isError(decompressed_bound)) { size_t decompressed_size;
snprintf(ctx->error_msg, sizeof(ctx->error_msg), "Zstd decompression failed"); int should_free_data;
return -1;
}
uint8_t *decompressed_data = malloc(decompressed_bound); if (ctx->params.no_zstd) {
const size_t decompressed_size = ZSTD_decompress(decompressed_data, decompressed_bound, // No Zstd compression - use data directly
compressed_data, packet_size); decompressed_data = (uint8_t *)compressed_data;
if (ZSTD_isError(decompressed_size)) { decompressed_size = packet_size;
free(decompressed_data); should_free_data = 0;
snprintf(ctx->error_msg, sizeof(ctx->error_msg), "Zstd decompression failed"); } else {
return -1; // Normal Zstd decompression
const size_t decompressed_bound = ZSTD_getFrameContentSize(compressed_data, packet_size);
if (ZSTD_isError(decompressed_bound)) {
snprintf(ctx->error_msg, sizeof(ctx->error_msg), "Zstd decompression failed");
return -1;
}
decompressed_data = malloc(decompressed_bound);
decompressed_size = ZSTD_decompress(decompressed_data, decompressed_bound,
compressed_data, packet_size);
if (ZSTD_isError(decompressed_size)) {
free(decompressed_data);
snprintf(ctx->error_msg, sizeof(ctx->error_msg), "Zstd decompression failed");
return -1;
}
should_free_data = 1;
} }
// Allocate coefficient buffers // Allocate coefficient buffers
@@ -1768,7 +1811,9 @@ int tav_video_decode_pframe(tav_video_context_t *ctx,
postprocess_coefficients_ezbc(decompressed_data, num_pixels, coeffs_y, coeffs_co, coeffs_cg, ctx->params.channel_layout); postprocess_coefficients_ezbc(decompressed_data, num_pixels, coeffs_y, coeffs_co, coeffs_cg, ctx->params.channel_layout);
} }
free(decompressed_data); if (should_free_data) {
free(decompressed_data);
}
// Dequantise // Dequantise
const float base_q_y = QLUT[ctx->params.quantiser_y]; const float base_q_y = QLUT[ctx->params.quantiser_y];

View File

@@ -779,9 +779,10 @@ int tav_encoder_encode_audio(tav_encoder_context_t *ctx,
return -1; return -1;
} }
// Encode audio with TAD encoder // Encode audio with TAD encoder (use same zstd_level as video)
size_t tad_size = tad32_encode_chunk(pcm_samples, num_samples, size_t tad_size = tad32_encode_chunk(pcm_samples, num_samples,
ctx->tad_max_index, 1.0f, tad_data); ctx->tad_max_index, 1.0f,
ctx->zstd_level, tad_data);
if (tad_size == 0) { if (tad_size == 0) {
free(tad_data); free(tad_data);
snprintf(ctx->error_message, MAX_ERROR_MESSAGE, snprintf(ctx->error_message, MAX_ERROR_MESSAGE,
@@ -1324,28 +1325,41 @@ static int encode_gop_intra_only(tav_encoder_context_t *ctx, gop_slot_t *slot) {
// Free full-frame YCoCg buffers // Free full-frame YCoCg buffers
free(frame_y); free(frame_co); free(frame_cg); free(frame_y); free(frame_co); free(frame_cg);
// Step 5: Zstd compress all tile data // Step 5: Zstd compress all tile data (or bypass if zstd_level < 0)
size_t compressed_bound = ZSTD_compressBound(preprocess_offset); size_t output_size;
uint8_t *compression_buffer = tav_malloc(compressed_bound); uint8_t *output_buffer;
int is_uncompressed = 0;
size_t compressed_size = ZSTD_compress( if (ctx->zstd_level < 0) {
compression_buffer, compressed_bound, // Bypass Zstd compression - use raw data
preprocess_buffer, preprocess_offset, output_size = preprocess_offset;
ctx->zstd_level output_buffer = preprocess_buffer; // Transfer ownership
); is_uncompressed = 1;
} else {
// Normal Zstd compression
size_t compressed_bound = ZSTD_compressBound(preprocess_offset);
output_buffer = tav_malloc(compressed_bound);
free(preprocess_buffer); output_size = ZSTD_compress(
output_buffer, compressed_bound,
preprocess_buffer, preprocess_offset,
ctx->zstd_level
);
if (ZSTD_isError(compressed_size)) { free(preprocess_buffer);
free(compression_buffer);
snprintf(slot->error_message, MAX_ERROR_MESSAGE, if (ZSTD_isError(output_size)) {
"Zstd compression failed: %s", ZSTD_getErrorName(compressed_size)); free(output_buffer);
return -1; snprintf(slot->error_message, MAX_ERROR_MESSAGE,
"Zstd compression failed: %s", ZSTD_getErrorName(output_size));
return -1;
}
} }
// Step 6: Format I-frame packet // Step 6: Format I-frame packet
// Packet format: [type(1)][size(4)][data(N)] // Packet format: [type(1)][size(4)][data(N)]
size_t packet_size = 1 + 4 + compressed_size; // Size field MSB: 0=compressed, 1=uncompressed
size_t packet_size = 1 + 4 + output_size;
tav_encoder_packet_t *pkt = calloc(1, sizeof(tav_encoder_packet_t)); tav_encoder_packet_t *pkt = calloc(1, sizeof(tav_encoder_packet_t));
pkt->data = malloc(packet_size); pkt->data = malloc(packet_size);
pkt->size = packet_size; pkt->size = packet_size;
@@ -1355,16 +1369,16 @@ static int encode_gop_intra_only(tav_encoder_context_t *ctx, gop_slot_t *slot) {
uint8_t *write_ptr = pkt->data; uint8_t *write_ptr = pkt->data;
*write_ptr++ = TAV_PACKET_IFRAME; *write_ptr++ = TAV_PACKET_IFRAME;
uint32_t size_field = (uint32_t)compressed_size; uint32_t size_field = (uint32_t)output_size;
memcpy(write_ptr, &size_field, 4); memcpy(write_ptr, &size_field, 4);
write_ptr += 4; write_ptr += 4;
memcpy(write_ptr, compression_buffer, compressed_size); memcpy(write_ptr, output_buffer, output_size);
// Store packet in slot // Store packet in slot
slot->packets = pkt; slot->packets = pkt;
slot->num_packets = 1; slot->num_packets = 1;
free(compression_buffer); free(output_buffer);
return 0; // Success return 0; // Success
} }
@@ -1443,34 +1457,45 @@ static int encode_gop_unified(tav_encoder_context_t *ctx, gop_slot_t *slot) {
preprocess_buffer preprocess_buffer
); );
// Step 5: Zstd compress // Step 5: Zstd compress (or bypass if zstd_level < 0)
size_t compressed_bound = ZSTD_compressBound(preprocessed_size); size_t output_size;
uint8_t *compression_buffer = tav_malloc(compressed_bound); uint8_t *output_buffer;
size_t compressed_size = ZSTD_compress( if (ctx->zstd_level < 0) {
compression_buffer, compressed_bound, // Bypass Zstd compression - use raw preprocessed data
preprocess_buffer, preprocessed_size, output_size = preprocessed_size;
ctx->zstd_level output_buffer = preprocess_buffer; // Transfer ownership
); } else {
// Normal Zstd compression
size_t compressed_bound = ZSTD_compressBound(preprocessed_size);
output_buffer = tav_malloc(compressed_bound);
output_size = ZSTD_compress(
output_buffer, compressed_bound,
preprocess_buffer, preprocessed_size,
ctx->zstd_level
);
if (ZSTD_isError(compressed_size)) {
// Cleanup and return error
for (int i = 0; i < num_frames; i++) {
free(work_y[i]); free(work_co[i]); free(work_cg[i]);
free(quant_y[i]); free(quant_co[i]); free(quant_cg[i]);
}
free(work_y); free(work_co); free(work_cg);
free(quant_y); free(quant_co); free(quant_cg);
free(preprocess_buffer); free(preprocess_buffer);
free(compression_buffer);
snprintf(slot->error_message, MAX_ERROR_MESSAGE, if (ZSTD_isError(output_size)) {
"Zstd compression failed: %s", ZSTD_getErrorName(compressed_size)); // Cleanup and return error
return -1; for (int i = 0; i < num_frames; i++) {
free(work_y[i]); free(work_co[i]); free(work_cg[i]);
free(quant_y[i]); free(quant_co[i]); free(quant_cg[i]);
}
free(work_y); free(work_co); free(work_cg);
free(quant_y); free(quant_co); free(quant_cg);
free(output_buffer);
snprintf(slot->error_message, MAX_ERROR_MESSAGE,
"Zstd compression failed: %s", ZSTD_getErrorName(output_size));
return -1;
}
} }
// Step 6: Format GOP unified packet // Step 6: Format GOP unified packet
// Packet format: [type(1)][gop_size(1)][size(4)][data(N)] // Packet format: [type(1)][gop_size(1)][size(4)][data(N)]
size_t packet_size = 1 + 1 + 4 + compressed_size; size_t packet_size = 1 + 1 + 4 + output_size;
tav_encoder_packet_t *pkt = calloc(1, sizeof(tav_encoder_packet_t)); tav_encoder_packet_t *pkt = calloc(1, sizeof(tav_encoder_packet_t));
pkt->data = malloc(packet_size); pkt->data = malloc(packet_size);
pkt->size = packet_size; pkt->size = packet_size;
@@ -1481,10 +1506,10 @@ static int encode_gop_unified(tav_encoder_context_t *ctx, gop_slot_t *slot) {
uint8_t *write_ptr = pkt->data; uint8_t *write_ptr = pkt->data;
*write_ptr++ = TAV_PACKET_GOP_UNIFIED; *write_ptr++ = TAV_PACKET_GOP_UNIFIED;
*write_ptr++ = (uint8_t)num_frames; *write_ptr++ = (uint8_t)num_frames;
uint32_t size_field = (uint32_t)compressed_size; uint32_t size_field = (uint32_t)output_size;
memcpy(write_ptr, &size_field, 4); memcpy(write_ptr, &size_field, 4);
write_ptr += 4; write_ptr += 4;
memcpy(write_ptr, compression_buffer, compressed_size); memcpy(write_ptr, output_buffer, output_size);
// Store packet in slot // Store packet in slot
slot->packets = pkt; slot->packets = pkt;
@@ -1497,8 +1522,7 @@ static int encode_gop_unified(tav_encoder_context_t *ctx, gop_slot_t *slot) {
} }
free(work_y); free(work_co); free(work_cg); free(work_y); free(work_co); free(work_cg);
free(quant_y); free(quant_co); free(quant_cg); free(quant_y); free(quant_co); free(quant_cg);
free(preprocess_buffer); free(output_buffer);
free(compression_buffer);
return 0; // Success return 0; // Success
} }

View File

@@ -1824,12 +1824,35 @@ static int run_decoder(dt_decoder_t *dec) {
// Main // Main
// ============================================================================= // =============================================================================
// Generate output filename by replacing extension with .mkv
static char *generate_output_filename(const char *input_file) {
size_t len = strlen(input_file);
char *output = malloc(len + 5); // Worst case: add ".mkv" + null
if (!output) return NULL;
strcpy(output, input_file);
// Find last dot in filename (not in path)
char *last_dot = strrchr(output, '.');
char *last_slash = strrchr(output, '/');
// Only replace if dot is after last slash (i.e., in filename, not path)
if (last_dot && (!last_slash || last_dot > last_slash)) {
strcpy(last_dot, ".mkv");
} else {
// No extension found, append .mkv
strcat(output, ".mkv");
}
return output;
}
int main(int argc, char **argv) { int main(int argc, char **argv) {
dt_decoder_t dec; dt_decoder_t dec;
memset(&dec, 0, sizeof(dec)); memset(&dec, 0, sizeof(dec));
// Default thread count // Default thread count
dec.num_threads = get_default_thread_count(); dec.num_threads = 0;//get_default_thread_count();
// Initialize FEC libraries // Initialize FEC libraries
rs_init(); rs_init();
@@ -1884,11 +1907,20 @@ int main(int argc, char **argv) {
} }
// Validate arguments // Validate arguments
if (!dec.input_file || !dec.output_file) { if (!dec.input_file) {
fprintf(stderr, "Error: Input and output files are required\n"); fprintf(stderr, "Error: Input and output files are required\n");
print_usage(argv[0]); print_usage(argv[0]);
return 1; return 1;
} }
// Generate output filename if not provided
if (!dec.output_file) {
dec.output_file = generate_output_filename(dec.input_file);
if (!dec.output_file) {
fprintf(stderr, "Error: Failed to generate output filename\n");
return 1;
}
}
return run_decoder(&dec); return run_decoder(&dec);
} }

View File

@@ -289,7 +289,7 @@ int main(int argc, char *argv[]) {
// Encode chunk using linked tad32_encode_chunk() from encoder_tad32.c // Encode chunk using linked tad32_encode_chunk() from encoder_tad32.c
size_t encoded_size = tad32_encode_chunk(chunk_buffer, TAD32_DEFAULT_CHUNK_SIZE, size_t encoded_size = tad32_encode_chunk(chunk_buffer, TAD32_DEFAULT_CHUNK_SIZE,
max_index, max_index,
quantiser_scale, output_buffer); quantiser_scale, TAD32_ZSTD_LEVEL, output_buffer);
if (encoded_size == 0) { if (encoded_size == 0) {
fprintf(stderr, "Error: Chunk encoding failed at chunk %zu\n", chunk_idx); fprintf(stderr, "Error: Chunk encoding failed at chunk %zu\n", chunk_idx);

View File

@@ -826,7 +826,8 @@ static int write_audio_packet(FILE *fp, cli_context_t *cli, float *pcm_samples,
// Encode with TAD (returns: sample_count(2) + max_index(1) + payload_size(4) + payload) // Encode with TAD (returns: sample_count(2) + max_index(1) + payload_size(4) + payload)
int max_index = tad32_quality_to_max_index(cli->audio_quality); int max_index = tad32_quality_to_max_index(cli->audio_quality);
size_t tad_chunk_size = tad32_encode_chunk(pcm_samples, num_samples, max_index, 1.0f, tad_buffer); size_t tad_chunk_size = tad32_encode_chunk(pcm_samples, num_samples, max_index, 1.0f,
cli->enc_params.zstd_level, tad_buffer);
if (tad_chunk_size == 0) { if (tad_chunk_size == 0) {
fprintf(stderr, "Error: TAD encoding failed\n"); fprintf(stderr, "Error: TAD encoding failed\n");

View File

@@ -556,7 +556,8 @@ static void *worker_thread_main(void *arg) {
if (job->success && job->audio_sample_count > 0) { if (job->success && job->audio_sample_count > 0) {
int max_index = tad32_quality_to_max_index(enc->quality_index); int max_index = tad32_quality_to_max_index(enc->quality_index);
job->tad_size = tad32_encode_chunk(job->audio_samples, job->audio_sample_count, job->tad_size = tad32_encode_chunk(job->audio_samples, job->audio_sample_count,
max_index, 1.0f, job->tad_output); max_index, 1.0f, enc->enc_params.zstd_level,
job->tad_output);
} }
tav_encoder_free(ctx); tav_encoder_free(ctx);
@@ -783,7 +784,8 @@ static int run_encoder_st(dt_encoder_t *enc, FILE *video_pipe, FILE *audio_pipe,
int max_index = tad32_quality_to_max_index(enc->quality_index); int max_index = tad32_quality_to_max_index(enc->quality_index);
size_t tad_size = tad32_encode_chunk(enc->audio_buffer, enc->audio_buffer_samples, size_t tad_size = tad32_encode_chunk(enc->audio_buffer, enc->audio_buffer_samples,
max_index, 1.0f, tad_output); max_index, 1.0f, enc->enc_params.zstd_level,
tad_output);
write_packet(enc, enc->current_timecode_ns, write_packet(enc, enc->current_timecode_ns,
tad_output, tad_size, tad_output, tad_size,
@@ -827,7 +829,8 @@ static int run_encoder_st(dt_encoder_t *enc, FILE *video_pipe, FILE *audio_pipe,
if (result >= 0 && video_packet) { if (result >= 0 && video_packet) {
int max_index = tad32_quality_to_max_index(enc->quality_index); int max_index = tad32_quality_to_max_index(enc->quality_index);
size_t tad_size = tad32_encode_chunk(enc->audio_buffer, enc->audio_buffer_samples, size_t tad_size = tad32_encode_chunk(enc->audio_buffer, enc->audio_buffer_samples,
max_index, 1.0f, tad_output); max_index, 1.0f, enc->enc_params.zstd_level,
tad_output);
write_packet(enc, enc->current_timecode_ns, write_packet(enc, enc->current_timecode_ns,
tad_output, tad_size, tad_output, tad_size,
@@ -1168,6 +1171,7 @@ static int run_encoder(dt_encoder_t *enc) {
enc->enc_params.encoder_preset = 0x01; // Sports mode enc->enc_params.encoder_preset = 0x01; // Sports mode
enc->enc_params.monoblock = 1; // Force monoblock enc->enc_params.monoblock = 1; // Force monoblock
enc->enc_params.verbose = enc->verbose; enc->enc_params.verbose = enc->verbose;
enc->enc_params.zstd_level = -1; // disable Zstd
// For single-threaded mode, create a context to validate params // For single-threaded mode, create a context to validate params
enc->video_ctx = tav_encoder_create(&enc->enc_params); enc->video_ctx = tav_encoder_create(&enc->enc_params);