diff --git a/terranmon.txt b/terranmon.txt index 9aedef5..0f643c0 100644 --- a/terranmon.txt +++ b/terranmon.txt @@ -944,7 +944,7 @@ transmission capability, and region-of-interest coding. - bit 2 = is lossless mode (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 4 = reserved (crop encoding?) + - bit 4 = no Zstd compression - bit 7 = has no video 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) @@ -1659,8 +1659,8 @@ start of the next packet uint24 Reed-Solomon Block Count - bytes Zstd-compressed TAD - bytes Parity for Zstd-compressed TAD + bytes TAD (EZBC, no Zstd) + bytes Parity for TAD bytes TAV with forward error correction @@ -1669,8 +1669,8 @@ start of the next packet uint24 Reed-Solomon Block Count - bytes Zstd-compressed Unified Block Data - bytes Parity for Zstd-compressed Unified Block Data + bytes TAV (EZBC, no Zstd) + bytes Parity for TAV Q1. Why headers have such low encoding rate (n byte input -> 2n byte output)? diff --git a/video_encoder/include/encoder_tad.h b/video_encoder/include/encoder_tad.h index 158120d..4be0aa7 100644 --- a/video_encoder/include/encoder_tad.h +++ b/video_encoder/include/encoder_tad.h @@ -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 quantiser_scale Quantiser scaling factor (1.0=baseline, 2.0=2x coarser quantisation) * 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) * @return Number of bytes written to output, or 0 on error * * Output format: * uint16 sample_count (samples per channel) * uint8 max_index (maximum quantisation index) - * uint32 payload_size (bytes in payload) - * * payload (encoded M/S data, Zstd-compressed with 2-bit twobitmap) + * uint32 payload_size (bytes in payload; MSB=1 indicates uncompressed) + * * payload (encoded M/S data, optionally Zstd-compressed) */ size_t tad32_encode_chunk(const float *pcm32_stereo, size_t num_samples, int max_index, - float quantiser_scale, uint8_t *output); + float quantiser_scale, int zstd_level, uint8_t *output); /** * Print accumulated coefficient statistics diff --git a/video_encoder/include/tav_video_decoder.h b/video_encoder/include/tav_video_decoder.h index d3f4ed1..a8b9e58 100644 --- a/video_encoder/include/tav_video_decoder.h +++ b/video_encoder/include/tav_video_decoder.h @@ -27,6 +27,7 @@ typedef struct { uint8_t quantiser_cg; // Base quantiser index for Cg/Cp uint8_t encoder_preset; // Encoder preset flags (sports, anime, etc.) 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; // Create video decoder context diff --git a/video_encoder/lib/libtaddec/decoder_tad.c b/video_encoder/lib/libtaddec/decoder_tad.c index 983d9f1..efa3f0e 100644 --- a/video_encoder/lib/libtaddec/decoder_tad.c +++ b/video_encoder/lib/libtaddec/decoder_tad.c @@ -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; 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); + // 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 int dwt_levels = calculate_dwt_levels(sample_count); 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; } - // Decompress Zstd - const uint8_t *payload; + // Decompress Zstd (or use raw data if uncompressed) uint8_t *decompressed = NULL; + size_t actual_size; + int should_free_decompressed; - // Estimate decompressed size (generous upper bound) - size_t decompressed_size = sample_count * 4 * sizeof(int8_t); - decompressed = malloc(decompressed_size); + if (is_uncompressed) { + // Data is not compressed - use directly + 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)) { - fprintf(stderr, "Error: Zstd decompression failed: %s\n", ZSTD_getErrorName(actual_size)); - free(decompressed); - return -1; + if (ZSTD_isError(actual_size)) { + fprintf(stderr, "Error: Zstd decompression failed: %s\n", ZSTD_getErrorName(actual_size)); + free(decompressed); + return -1; + } + should_free_decompressed = 1; } 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); if (result != 0) { 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(pcm32_left); free(pcm32_right); free(pcm8_left); free(pcm8_right); 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); if (result != 0) { 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(pcm32_left); free(pcm32_right); free(pcm8_left); free(pcm8_right); return -1; @@ -978,7 +992,7 @@ int tad32_decode_chunk(const uint8_t *input, size_t input_size, uint8_t *pcmu8_s // Cleanup free(quant_mid); free(quant_side); free(dwt_mid); free(dwt_side); 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; } diff --git a/video_encoder/lib/libtadenc/encoder_tad.c b/video_encoder/lib/libtadenc/encoder_tad.c index 47a17f2..cdd65ed 100644 --- a/video_encoder/lib/libtadenc/encoder_tad.c +++ b/video_encoder/lib/libtadenc/encoder_tad.c @@ -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, int max_index, - float quantiser_scale, uint8_t *output) { + float quantiser_scale, int zstd_level, uint8_t *output) { // Calculate DWT levels from chunk size int dwt_levels = calculate_dwt_levels(num_samples); 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(side_ezbc); - // Step 6: Zstd compression + // Step 6: Zstd compression (or bypass if zstd_level < 0) uint8_t *write_ptr = output; // 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); size_t payload_size; + int is_uncompressed = 0; - size_t zstd_bound = ZSTD_compressBound(uncompressed_size); - uint8_t *zstd_buffer = malloc(zstd_bound); + if (zstd_level < 0) { + // 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)) { - fprintf(stderr, "Error: Zstd compression failed: %s\n", ZSTD_getErrorName(payload_size)); + if (ZSTD_isError(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(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); - - - *payload_size_ptr = (uint32_t)payload_size; + // Set payload size (MSB=1 indicates uncompressed data) + uint32_t size_field = (uint32_t)payload_size; + if (is_uncompressed) { + size_field |= 0x80000000; + } + *payload_size_ptr = size_field; write_ptr += payload_size; // Cleanup diff --git a/video_encoder/lib/libtavdec/tav_video_decoder.c b/video_encoder/lib/libtavdec/tav_video_decoder.c index 9cb3144..51c2522 100644 --- a/video_encoder/lib/libtavdec/tav_video_decoder.c +++ b/video_encoder/lib/libtavdec/tav_video_decoder.c @@ -1478,25 +1478,38 @@ int tav_video_decode_gop(tav_video_context_t *ctx, const int height = ctx->params.height; const int num_pixels = width * height; - // Decompress with Zstd - 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; - } + // Decompress with Zstd (or use raw data if no_zstd flag is set) + uint8_t *decompressed_data; + size_t decompressed_size; + int should_free_data; - uint8_t *decompressed_data = malloc(decompressed_bound); - if (!decompressed_data) { - snprintf(ctx->error_msg, sizeof(ctx->error_msg), "Memory allocation failed"); - return -1; - } + if (ctx->params.no_zstd) { + // No Zstd compression - use data directly + decompressed_data = (uint8_t *)compressed_data; // Cast away const, won't modify + 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, - 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; + decompressed_data = malloc(decompressed_bound); + if (!decompressed_data) { + snprintf(ctx->error_msg, sizeof(ctx->error_msg), "Memory allocation 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 @@ -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); } - free(decompressed_data); + if (should_free_data) { + free(decompressed_data); + } if (!gop_coeffs) { 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 num_pixels = width * height; - // Decompress - 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; - } + // Decompress (or use raw data if no_zstd flag is set) + uint8_t *decompressed_data; + size_t decompressed_size; + int should_free_data; - uint8_t *decompressed_data = malloc(decompressed_bound); - const size_t 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; + if (ctx->params.no_zstd) { + // No Zstd compression - use data directly + decompressed_data = (uint8_t *)compressed_data; + decompressed_size = packet_size; + should_free_data = 0; + } else { + // 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 @@ -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); } - free(decompressed_data); + if (should_free_data) { + free(decompressed_data); + } // Dequantise 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 num_pixels = width * height; - // Decompress - 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; - } + // Decompress (or use raw data if no_zstd flag is set) + uint8_t *decompressed_data; + size_t decompressed_size; + int should_free_data; - uint8_t *decompressed_data = malloc(decompressed_bound); - const size_t 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; + if (ctx->params.no_zstd) { + // No Zstd compression - use data directly + decompressed_data = (uint8_t *)compressed_data; + decompressed_size = packet_size; + should_free_data = 0; + } else { + // 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 @@ -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); } - free(decompressed_data); + if (should_free_data) { + free(decompressed_data); + } // Dequantise const float base_q_y = QLUT[ctx->params.quantiser_y]; diff --git a/video_encoder/lib/libtavenc/tav_encoder_lib.c b/video_encoder/lib/libtavenc/tav_encoder_lib.c index 70d7ea3..bdfd445 100644 --- a/video_encoder/lib/libtavenc/tav_encoder_lib.c +++ b/video_encoder/lib/libtavenc/tav_encoder_lib.c @@ -779,9 +779,10 @@ int tav_encoder_encode_audio(tav_encoder_context_t *ctx, 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, - ctx->tad_max_index, 1.0f, tad_data); + ctx->tad_max_index, 1.0f, + ctx->zstd_level, tad_data); if (tad_size == 0) { free(tad_data); 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(frame_y); free(frame_co); free(frame_cg); - // Step 5: Zstd compress all tile data - size_t compressed_bound = ZSTD_compressBound(preprocess_offset); - uint8_t *compression_buffer = tav_malloc(compressed_bound); + // Step 5: Zstd compress all tile data (or bypass if zstd_level < 0) + size_t output_size; + uint8_t *output_buffer; + int is_uncompressed = 0; - size_t compressed_size = ZSTD_compress( - compression_buffer, compressed_bound, - preprocess_buffer, preprocess_offset, - ctx->zstd_level - ); + if (ctx->zstd_level < 0) { + // Bypass Zstd compression - use raw data + output_size = preprocess_offset; + 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(compression_buffer); - snprintf(slot->error_message, MAX_ERROR_MESSAGE, - "Zstd compression failed: %s", ZSTD_getErrorName(compressed_size)); - return -1; + free(preprocess_buffer); + + if (ZSTD_isError(output_size)) { + free(output_buffer); + snprintf(slot->error_message, MAX_ERROR_MESSAGE, + "Zstd compression failed: %s", ZSTD_getErrorName(output_size)); + return -1; + } } // Step 6: Format I-frame packet // 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)); pkt->data = malloc(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; *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); write_ptr += 4; - memcpy(write_ptr, compression_buffer, compressed_size); + memcpy(write_ptr, output_buffer, output_size); // Store packet in slot slot->packets = pkt; slot->num_packets = 1; - free(compression_buffer); + free(output_buffer); return 0; // Success } @@ -1443,34 +1457,45 @@ static int encode_gop_unified(tav_encoder_context_t *ctx, gop_slot_t *slot) { preprocess_buffer ); - // Step 5: Zstd compress - size_t compressed_bound = ZSTD_compressBound(preprocessed_size); - uint8_t *compression_buffer = tav_malloc(compressed_bound); + // Step 5: Zstd compress (or bypass if zstd_level < 0) + size_t output_size; + uint8_t *output_buffer; - size_t compressed_size = ZSTD_compress( - compression_buffer, compressed_bound, - preprocess_buffer, preprocessed_size, - ctx->zstd_level - ); + if (ctx->zstd_level < 0) { + // Bypass Zstd compression - use raw preprocessed data + output_size = preprocessed_size; + 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(compression_buffer); - snprintf(slot->error_message, MAX_ERROR_MESSAGE, - "Zstd compression failed: %s", ZSTD_getErrorName(compressed_size)); - return -1; + + if (ZSTD_isError(output_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(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 // 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)); pkt->data = malloc(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; *write_ptr++ = TAV_PACKET_GOP_UNIFIED; *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); write_ptr += 4; - memcpy(write_ptr, compression_buffer, compressed_size); + memcpy(write_ptr, output_buffer, output_size); // Store packet in slot 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(quant_y); free(quant_co); free(quant_cg); - free(preprocess_buffer); - free(compression_buffer); + free(output_buffer); return 0; // Success } diff --git a/video_encoder/src/decoder_tav_dt.c b/video_encoder/src/decoder_tav_dt.c index b67525a..3fbd1e8 100644 --- a/video_encoder/src/decoder_tav_dt.c +++ b/video_encoder/src/decoder_tav_dt.c @@ -1824,12 +1824,35 @@ static int run_decoder(dt_decoder_t *dec) { // 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) { dt_decoder_t dec; memset(&dec, 0, sizeof(dec)); // Default thread count - dec.num_threads = get_default_thread_count(); + dec.num_threads = 0;//get_default_thread_count(); // Initialize FEC libraries rs_init(); @@ -1884,11 +1907,20 @@ int main(int argc, char **argv) { } // Validate arguments - if (!dec.input_file || !dec.output_file) { + if (!dec.input_file) { fprintf(stderr, "Error: Input and output files are required\n"); print_usage(argv[0]); 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); } diff --git a/video_encoder/src/encoder_tad_standalone.c b/video_encoder/src/encoder_tad_standalone.c index 540d6d0..d053129 100644 --- a/video_encoder/src/encoder_tad_standalone.c +++ b/video_encoder/src/encoder_tad_standalone.c @@ -289,7 +289,7 @@ int main(int argc, char *argv[]) { // Encode chunk using linked tad32_encode_chunk() from encoder_tad32.c size_t encoded_size = tad32_encode_chunk(chunk_buffer, TAD32_DEFAULT_CHUNK_SIZE, max_index, - quantiser_scale, output_buffer); + quantiser_scale, TAD32_ZSTD_LEVEL, output_buffer); if (encoded_size == 0) { fprintf(stderr, "Error: Chunk encoding failed at chunk %zu\n", chunk_idx); diff --git a/video_encoder/src/encoder_tav.c b/video_encoder/src/encoder_tav.c index 936d774..6ef2bdb 100644 --- a/video_encoder/src/encoder_tav.c +++ b/video_encoder/src/encoder_tav.c @@ -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) 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) { fprintf(stderr, "Error: TAD encoding failed\n"); diff --git a/video_encoder/src/encoder_tav_dt.c b/video_encoder/src/encoder_tav_dt.c index 66eae66..13c0548 100644 --- a/video_encoder/src/encoder_tav_dt.c +++ b/video_encoder/src/encoder_tav_dt.c @@ -556,7 +556,8 @@ static void *worker_thread_main(void *arg) { if (job->success && job->audio_sample_count > 0) { int max_index = tad32_quality_to_max_index(enc->quality_index); 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); @@ -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); 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, 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) { 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, - max_index, 1.0f, tad_output); + max_index, 1.0f, enc->enc_params.zstd_level, + tad_output); write_packet(enc, enc->current_timecode_ns, 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.monoblock = 1; // Force monoblock enc->enc_params.verbose = enc->verbose; + enc->enc_params.zstd_level = -1; // disable Zstd // For single-threaded mode, create a context to validate params enc->video_ctx = tav_encoder_create(&enc->enc_params);