TAV-DT format revision and soft sync recovery

This commit is contained in:
minjaesong
2025-12-15 17:42:08 +09:00
parent 3d76006ad9
commit 4929d84cec
4 changed files with 189 additions and 37 deletions

View File

@@ -36,7 +36,7 @@ LIBTADENC_OBJ = lib/libtadenc/encoder_tad.o
LIBTADDEC_OBJ = lib/libtaddec/decoder_tad.o
# libfec - Forward Error Correction library (LDPC + Reed-Solomon)
LIBFEC_OBJ = lib/libfec/ldpc.o lib/libfec/reed_solomon.o
LIBFEC_OBJ = lib/libfec/ldpc.o lib/libfec/reed_solomon.o lib/libfec/ldpc_payload.o
# =============================================================================
# Targets
@@ -150,7 +150,7 @@ decoder_tav_dt: src/decoder_tav_dt.c lib/libtavdec.a lib/libtaddec.a lib/libfec.
# TAV-DT noise injector (channel simulator)
tavdt_noise_injector: tavdt_noise_injector.c
rm -f tavdt_noise_injector
$(CC) -std=c99 -Wall -O2 -D_GNU_SOURCE -o tavdt_noise_injector tavdt_noise_injector.c -lm
$(CC) -std=c99 -Wall -Ofast -D_GNU_SOURCE -o tavdt_noise_injector tavdt_noise_injector.c -lm
@echo ""
@echo "TAV-DT noise injector built: tavdt_noise_injector"
@echo "Simulates QPSK satellite channel noise (AWGN + burst)"
@@ -180,7 +180,7 @@ install: $(TARGETS)
# Check for required dependencies
check-deps:
@echo "Checking dependencies..."
@pkg-config --exists libzstd || (echo "Error: libzstd-dev not found. Install with: sudo apt install libzstd-dev" && exit 1)
@pkg-config --exists libzstd || (echo "Error: libzstd-dev not found. Install libzstd-dev or equivalent" && exit 1)
@echo "All dependencies found."
# Help

View File

@@ -9,15 +9,19 @@
* - Mandatory TAD audio
* - LDPC rate 1/2 for headers, Reed-Solomon (255,223) for payloads
*
* Packet structure (revised 2025-12-11):
* - Main header: 24 bytes → 48 bytes LDPC encoded
* (sync + fps + flags + reserved + size + crc + timecode + offset_to_video)
* Packet structure (revised 2025-12-15):
* - Main header: 28 bytes → 56 bytes LDPC encoded
* Layout: sync(4) + fps(1) + flags(1) + reserved(2) + size(4) + timecode(8) + offset(4) + crc(4)
* CRC covers bytes 0-23 (everything except CRC itself)
* - TAD subpacket: header (10→20 bytes LDPC) + RS-encoded payload
* - TAV subpacket: header (8→16 bytes LDPC) + RS-encoded payload
* - No packet type bytes - always audio then video
*
* Features (revised 2025-12-15):
* - Soft Sync Recovery: Attempts to recover corrupted headers using known values
*
* Created by CuriousTorvald and Claude on 2025-12-09.
* Revised 2025-12-11 for updated TAV-DT specification.
* Revised 2025-12-15 for updated TAV-DT specification (CRC over timecode+offset, Soft Sync Recovery).
*/
#define _POSIX_C_SOURCE 200809L
@@ -217,6 +221,13 @@ typedef struct {
uint64_t last_timecode_ns; // Last processed timecode
uint64_t audio_samples_written; // Total audio samples written
uint64_t video_frames_written; // Total video frames written (for sync check)
// Soft Sync Recovery state
uint8_t last_valid_framerate;
uint8_t last_valid_flags;
uint64_t last_valid_timecode_ns;
int packets_since_valid_sync;
int soft_sync_recoveries; // Statistics counter
} dt_decoder_t;
// =============================================================================
@@ -419,11 +430,97 @@ typedef struct {
uint8_t flags;
uint16_t reserved;
uint32_t packet_size;
uint32_t crc32;
uint64_t timecode_ns;
uint32_t offset_to_video;
uint64_t timecode_ns; // Now at offset 12 (moved before CRC)
uint32_t offset_to_video; // Now at offset 20 (moved before CRC)
uint32_t crc32; // Now at offset 24 (last field)
} dt_packet_header_t;
// Soft Sync Recovery state
typedef struct {
uint8_t last_framerate;
uint8_t last_flags;
uint64_t last_timecode_ns;
int packets_since_valid;
int is_initialized;
} soft_sync_state_t;
/**
* Attempt Soft Sync Recovery on a header with CRC mismatch.
* Returns 1 if recovery succeeded, 0 if failed.
*
* Stage 1: Substitute known sync pattern, zero-fill reserved, recalculate CRC
* Stage 2: Also substitute framerate, flags, timecode with last known values
*/
static int attempt_soft_sync_recovery(dt_decoder_t *dec, uint8_t *decoded_header,
dt_packet_header_t *header __attribute__((unused))) {
uint32_t stored_crc;
memcpy(&stored_crc, decoded_header + 24, 4);
// Get the expected sync pattern (NTSC or PAL based on first packet)
uint32_t expected_sync = dec->is_pal ? TAV_DT_SYNC_PAL : TAV_DT_SYNC_NTSC;
// === Stage 1 ===
// Substitute sync pattern and zero-fill reserved
uint8_t recovery_header[DT_MAIN_HEADER_SIZE];
memcpy(recovery_header, decoded_header, DT_MAIN_HEADER_SIZE);
// Substitute sync pattern (big-endian)
recovery_header[0] = (expected_sync >> 24) & 0xFF;
recovery_header[1] = (expected_sync >> 16) & 0xFF;
recovery_header[2] = (expected_sync >> 8) & 0xFF;
recovery_header[3] = expected_sync & 0xFF;
// Zero-fill reserved
recovery_header[6] = 0;
recovery_header[7] = 0;
// Recalculate CRC over bytes 0-23
uint32_t calculated_crc = calculate_crc32(recovery_header, 24);
if (calculated_crc == stored_crc) {
if (dec->verbose) {
printf(" Soft Sync Recovery Stage 1: SUCCESS (sync/reserved corrected)\n");
}
// Use recovered header
memcpy(decoded_header, recovery_header, DT_MAIN_HEADER_SIZE);
dec->soft_sync_recoveries++;
return 1;
}
// === Stage 2 ===
// Also substitute framerate, flags, and timecode with last known values
if (dec->packets_processed > 0) {
recovery_header[4] = dec->last_valid_framerate;
recovery_header[5] = dec->last_valid_flags;
// Calculate expected timecode based on last known timecode + GOP duration
// GOP duration = 16 frames / framerate
uint64_t gop_duration_ns = (16ULL * 1000000000ULL) / dec->framerate;
uint64_t expected_timecode = dec->last_valid_timecode_ns +
(dec->packets_since_valid_sync + 1) * gop_duration_ns;
// Write expected timecode to bytes 12-19
memcpy(recovery_header + 12, &expected_timecode, 8);
// Recalculate CRC
calculated_crc = calculate_crc32(recovery_header, 24);
if (calculated_crc == stored_crc) {
if (dec->verbose) {
printf(" Soft Sync Recovery Stage 2: SUCCESS (sync/reserved/fps/flags/timecode corrected)\n");
printf(" Reconstructed timecode: %.3f s\n", expected_timecode / 1000000000.0);
}
// Use recovered header
memcpy(decoded_header, recovery_header, DT_MAIN_HEADER_SIZE);
dec->soft_sync_recoveries++;
return 1;
}
}
if (dec->verbose) {
fprintf(stderr, " Soft Sync Recovery: FAILED (all stages exhausted)\n");
}
return 0;
}
static int read_and_decode_header(dt_decoder_t *dec, dt_packet_header_t *header) {
// Read LDPC-encoded header (56 bytes = 28 bytes * 2)
uint8_t encoded_header[DT_MAIN_HEADER_SIZE * 2];
@@ -444,37 +541,71 @@ static int read_and_decode_header(dt_decoder_t *dec, dt_packet_header_t *header)
dec->fec_corrections++;
}
// Parse header fields
// Parse header fields (revised layout 2025-12-15)
// Layout: sync(4) + fps(1) + flags(1) + reserved(2) + size(4) + timecode(8) + offset(4) + crc(4)
header->sync_pattern = ((uint32_t)decoded_header[0] << 24) | ((uint32_t)decoded_header[1] << 16) |
((uint32_t)decoded_header[2] << 8) | decoded_header[3];
header->framerate = decoded_header[4];
header->flags = decoded_header[5];
header->reserved = decoded_header[6] | ((uint16_t)decoded_header[7] << 8);
memcpy(&header->packet_size, decoded_header + 8, 4);
memcpy(&header->crc32, decoded_header + 12, 4);
memcpy(&header->timecode_ns, decoded_header + 16, 8);
memcpy(&header->offset_to_video, decoded_header + 24, 4);
memcpy(&header->timecode_ns, decoded_header + 12, 8); // Now at offset 12
memcpy(&header->offset_to_video, decoded_header + 20, 4); // Now at offset 20
memcpy(&header->crc32, decoded_header + 24, 4); // Now at offset 24
// Verify sync pattern
if (header->sync_pattern != TAV_DT_SYNC_NTSC && header->sync_pattern != TAV_DT_SYNC_PAL) {
if (dec->verbose) {
fprintf(stderr, "Warning: Invalid sync pattern 0x%08X\n", header->sync_pattern);
}
dec->sync_losses++;
return -2;
int sync_valid = (header->sync_pattern == TAV_DT_SYNC_NTSC || header->sync_pattern == TAV_DT_SYNC_PAL);
if (!sync_valid && dec->verbose) {
fprintf(stderr, "Warning: Invalid sync pattern 0x%08X\n", header->sync_pattern);
}
// Verify CRC-32 (covers first 12 bytes: sync + fps + flags + reserved + size)
uint32_t calculated_crc = calculate_crc32(decoded_header, 12);
if (calculated_crc != header->crc32) {
// Verify CRC-32 (covers bytes 0-23: sync + fps + flags + reserved + size + timecode + offset)
uint32_t calculated_crc = calculate_crc32(decoded_header, 24);
int crc_valid = (calculated_crc == header->crc32);
if (!crc_valid) {
if (dec->verbose) {
fprintf(stderr, "Warning: CRC mismatch (expected 0x%08X, got 0x%08X)\n",
header->crc32, calculated_crc);
}
dec->crc_errors++;
// Continue anyway
// Attempt Soft Sync Recovery
if (dec->packets_processed > 0 && attempt_soft_sync_recovery(dec, decoded_header, header)) {
// Re-parse header from recovered data
header->sync_pattern = ((uint32_t)decoded_header[0] << 24) | ((uint32_t)decoded_header[1] << 16) |
((uint32_t)decoded_header[2] << 8) | decoded_header[3];
header->framerate = decoded_header[4];
header->flags = decoded_header[5];
header->reserved = decoded_header[6] | ((uint16_t)decoded_header[7] << 8);
memcpy(&header->packet_size, decoded_header + 8, 4);
memcpy(&header->timecode_ns, decoded_header + 12, 8);
memcpy(&header->offset_to_video, decoded_header + 20, 4);
memcpy(&header->crc32, decoded_header + 24, 4);
crc_valid = 1; // Recovery succeeded
} else {
dec->crc_errors++;
dec->packets_since_valid_sync++;
// Per spec: If CRC is unmatched after all soft recovery stages, packet MUST be discarded
if (dec->verbose) {
fprintf(stderr, "Warning: Packet discarded due to unrecoverable CRC error\n");
}
dec->sync_losses++;
return -2;
}
}
if (!sync_valid) {
dec->sync_losses++;
return -2;
}
// CRC is valid - update soft sync recovery state
dec->last_valid_framerate = header->framerate;
dec->last_valid_flags = header->flags;
dec->last_valid_timecode_ns = header->timecode_ns;
dec->packets_since_valid_sync = 0;
// Update decoder state from first packet
if (dec->packets_processed == 0) {
dec->width = DT_WIDTH;
@@ -1660,6 +1791,7 @@ static int run_decoder(dt_decoder_t *dec) {
printf(" Bytes read: %lu\n", dec->bytes_read);
printf(" FEC corrections: %lu\n", dec->fec_corrections);
printf(" CRC errors: %lu\n", dec->crc_errors);
printf(" Soft sync recoveries: %d\n", dec->soft_sync_recoveries);
printf(" Sync losses: %lu\n", dec->sync_losses);
// Mux output files

View File

@@ -9,15 +9,16 @@
* - Mandatory TAD audio
* - LDPC rate 1/2 for headers, Reed-Solomon (255,223) for payloads
*
* Packet structure (revised 2025-12-11):
* Packet structure (revised 2025-12-15):
* - Main header: 28 bytes -> 56 bytes LDPC encoded
* (sync + fps + flags + reserved + size + crc + timecode + offset_to_video)
* Layout: sync(4) + fps(1) + flags(1) + reserved(2) + size(4) + timecode(8) + offset(4) + crc(4)
* CRC covers bytes 0-23 (everything except CRC itself)
* - TAD subpacket: header (10->20 bytes LDPC) + RS-encoded payload
* - TAV subpacket: header (8->16 bytes LDPC) + RS-encoded payload
* - No packet type bytes - always audio then video
*
* Created by CuriousTorvald and Claude on 2025-12-09.
* Revised 2025-12-11 for updated TAV-DT specification.
* Revised 2025-12-15 for updated TAV-DT specification (CRC now covers timecode and offset).
*/
#define _POSIX_C_SOURCE 200809L
@@ -299,6 +300,8 @@ static int write_packet(dt_encoder_t *enc, uint64_t timecode_ns,
uint32_t packet_size = tad_subpacket_size + tav_subpacket_size;
// Build main header (28 bytes)
// Layout (revised 2025-12-15): sync(4) + fps(1) + flags(1) + reserved(2) + size(4) + timecode(8) + offset(4) + crc(4)
// CRC is calculated over bytes 0-23 (everything except CRC itself)
uint8_t header[DT_MAIN_HEADER_SIZE];
// Write sync pattern in big-endian (network byte order)
uint32_t sync = enc->is_pal ? TAV_DT_SYNC_PAL : TAV_DT_SYNC_NTSC;
@@ -328,15 +331,15 @@ static int write_packet(dt_encoder_t *enc, uint64_t timecode_ns,
// Packet size (4 bytes)
memcpy(header + 8, &packet_size, 4);
// CRC placeholder (will be calculated over header bytes 0-11)
uint32_t crc = calculate_crc32(header, 12);
memcpy(header + 12, &crc, 4);
// Timecode (8 bytes) - now at offset 12
memcpy(header + 12, &timecode_ns, 8);
// Timecode (8 bytes)
memcpy(header + 16, &timecode_ns, 8);
// Offset to video (4 bytes) - now at offset 20
memcpy(header + 20, &offset_to_video, 4);
// Offset to video (4 bytes)
memcpy(header + 24, &offset_to_video, 4);
// CRC-32 (4 bytes) - calculated over bytes 0-23 (sync + fps + flags + reserved + size + timecode + offset)
uint32_t crc = calculate_crc32(header, 24);
memcpy(header + 24, &crc, 4);
// LDPC encode main header
uint8_t ldpc_header[DT_MAIN_HEADER_SIZE * 2];