mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-03-07 11:51:49 +09:00
TAV-DT: slight format change to allow detailed failure mode inspection
This commit is contained in:
@@ -1650,29 +1650,29 @@ start of the next packet
|
||||
uint64 Timecode in nanoseconds
|
||||
uint32 Offset to video packet
|
||||
uint32 Reserved (zero-fill)
|
||||
uint32 CRC-32 of [sync pattern + above]
|
||||
<packet header end; encoded with rate 256/512 LDPC>
|
||||
uint32 CRC-32 of above
|
||||
<packet header end; encoded with rate 224/448 LDPC> // NOTE: sync pattern must not be LDPC-coded
|
||||
bytes TAD with forward error correction
|
||||
<TAD header start>
|
||||
uint16 Sample Count
|
||||
uint8 Quantiser Bits
|
||||
uint16 Reserved (zero-fill)
|
||||
uint32 Compressed Size
|
||||
uint24 Reed-Solomon Block Count
|
||||
uint32 CRC-32 of above
|
||||
<TAD chunk header end; encoded with rate 128/256 LDPC>
|
||||
<TAD chunk header end; encoded with rate 112/224 LDPC>
|
||||
<Reed-Solomon (255,223) block start>
|
||||
bytes TAD (EZBC, no Zstd)
|
||||
bytes Parity for TAD
|
||||
<Reed-Solomon (255,223) block end>
|
||||
bytes TAV with forward error correction
|
||||
uint32 TAV header sync pattern (0xA3F7C91E)
|
||||
<TAV header start>
|
||||
uint32 TAV header sync pattern (0xA3F7C91E)
|
||||
uint8 GOP Size (number of frames in this GOP)
|
||||
uint16 Reserved (zero-fill)
|
||||
uint32 Compressed Size
|
||||
uint24 Reed-Solomon Block Count
|
||||
uint32 CRC-32 of above
|
||||
<TAV header end; encoded with rate 128/256 LDPC>
|
||||
<TAV header end; encoded with rate 112/224 LDPC> // NOTE: sync pattern must not be LDPC-coded
|
||||
<Reed-Solomon (255,223) block start>
|
||||
bytes TAV (EZBC, no Zstd)
|
||||
bytes Parity for TAV
|
||||
|
||||
@@ -10,21 +10,22 @@
|
||||
* - LDPC rate 1/2 for headers, Reed-Solomon (255,223) for payloads
|
||||
*
|
||||
* Packet structure (revised 2025-12-17):
|
||||
* - Main header: 32 bytes raw (256 bits) -> 64 bytes LDPC encoded (512 bits, rate 256/512)
|
||||
* Layout: sync(4) + fps(1) + flags(1) + reserved(2) + size(4) + timecode(8) + offset(4) + reserved(4) + crc(4)
|
||||
* CRC covers bytes 0-27 (everything except CRC itself)
|
||||
* - TAD subpacket: header 16 bytes raw (128 bits) -> 32 bytes LDPC (256 bits, rate 128/256), + RS-encoded payload
|
||||
* Layout: sample_count(2) + quant_bits(1) + reserved(2) + compressed_size(4) + rs_block_count(3) + crc(4)
|
||||
* - TAV subpacket: header 16 bytes raw (128 bits) -> 32 bytes LDPC (256 bits, rate 128/256), + RS-encoded payload
|
||||
* Layout: sync(4) + gop_size(1) + compressed_size(4) + rs_block_count(3) + crc(4)
|
||||
* - Main packet: sync(4) NOT LDPC + header 28 bytes (224 bits) -> 56 bytes LDPC (448 bits, rate 224/448)
|
||||
* Header layout: fps(1) + flags(1) + reserved(2) + size(4) + timecode(8) + offset(4) + reserved(4) + crc(4)
|
||||
* CRC covers bytes 0-23 (everything except CRC itself)
|
||||
* - TAD subpacket: header 14 bytes (112 bits) -> 28 bytes LDPC (224 bits, rate 112/224), + RS-encoded payload
|
||||
* Layout: sample_count(2) + quant_bits(1) + compressed_size(4) + rs_block_count(3) + crc(4)
|
||||
* - TAV subpacket: sync(4) NOT LDPC + header 14 bytes (112 bits) -> 28 bytes LDPC (224 bits, rate 112/224), + RS payload
|
||||
* Header layout: gop_size(1) + reserved(2) + compressed_size(4) + rs_block_count(3) + crc(4)
|
||||
* - No packet type bytes - always audio then video
|
||||
*
|
||||
* Features (revised 2025-12-17):
|
||||
* - Soft Sync Recovery: Attempts to recover corrupted headers using known values
|
||||
* - Stage 3 recovery uses TAV subpacket sync pattern (0xA3F7C91E) to locate video data
|
||||
* - Sync patterns are NOT LDPC-coded for reliable packet boundary detection
|
||||
*
|
||||
* Created by CuriousTorvald and Claude on 2025-12-09.
|
||||
* Revised 2025-12-17 for power-of-two header sizes, subpacket CRCs, and TAV subpacket sync.
|
||||
* Revised 2025-12-17 for separate sync patterns, power-of-two LDPC sizes, and subpacket CRCs.
|
||||
*/
|
||||
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
@@ -67,11 +68,12 @@
|
||||
#define DT_TEMPORAL_LEVELS 2
|
||||
|
||||
// Header sizes (before LDPC encoding) - revised 2025-12-17
|
||||
// LDPC rates are specified in bits: 256/512 = 256 bits raw -> 512 bits encoded
|
||||
// Converted to bytes: 32 -> 64 bytes, 16 -> 32 bytes
|
||||
#define DT_MAIN_HEADER_SIZE 32 // sync(4) + fps(1) + flags(1) + reserved(2) + size(4) + timecode(8) + offset(4) + reserved(4) + crc(4)
|
||||
#define DT_TAD_HEADER_SIZE 16 // sample_count(2) + quant_bits(1) + reserved(2) + compressed_size(4) + rs_block_count(3) + crc(4)
|
||||
#define DT_TAV_HEADER_SIZE 16 // sync(4) + gop_size(1) + compressed_size(4) + rs_block_count(3) + crc(4)
|
||||
// Sync patterns are written separately (not LDPC-coded)
|
||||
// Main header: 28 bytes raw (224 bits) -> 56 bytes LDPC (448 bits, rate 224/448)
|
||||
// Subpacket headers: 14 bytes raw (112 bits) -> 28 bytes LDPC (224 bits, rate 112/224)
|
||||
#define DT_MAIN_HEADER_SIZE 28 // fps(1) + flags(1) + reserved(2) + size(4) + timecode(8) + offset(4) + reserved(4) + crc(4)
|
||||
#define DT_TAD_HEADER_SIZE 14 // sample_count(2) + quant_bits(1) + compressed_size(4) + rs_block_count(3) + crc(4)
|
||||
#define DT_TAV_HEADER_SIZE 14 // gop_size(1) + reserved(2) + compressed_size(4) + rs_block_count(3) + crc(4)
|
||||
|
||||
// TAV subpacket sync pattern (big endian)
|
||||
#define TAV_SUBPACKET_SYNC 0xA3F7C91E
|
||||
@@ -84,6 +86,12 @@ static const int QUALITY_CG[] = {148, 133, 113, 99, 76, 39};
|
||||
#define MAX_PATH 4096
|
||||
#define MAX_DECODE_THREADS 16
|
||||
|
||||
// Subpacket decode result codes (for detailed statistics tracking)
|
||||
#define DECODE_OK 0 // Success
|
||||
#define DECODE_ERR_HEADER_CRC -1 // Header CRC validation failed
|
||||
#define DECODE_ERR_PAYLOAD_FEC -2 // Payload FEC decode failed (unrecoverable)
|
||||
#define DECODE_ERR_TRUNCATED -3 // Data truncated or other error
|
||||
|
||||
// =============================================================================
|
||||
// Multithreading Structures
|
||||
// =============================================================================
|
||||
@@ -201,6 +209,24 @@ typedef struct {
|
||||
uint64_t fec_corrections;
|
||||
uint64_t sync_losses;
|
||||
|
||||
// Detailed packet outcome statistics
|
||||
// Fully decoded (both audio and video present)
|
||||
uint64_t stat_fully_decoded_intact; // (a) Both A/V intact, no recovery needed
|
||||
uint64_t stat_fully_decoded_recovered; // (b) Header recovered by soft sync recovery
|
||||
|
||||
// Partially decoded (only audio or only video)
|
||||
uint64_t stat_partial_video_ok_audio_hdr_damaged; // (c) Video OK, audio lost - header CRC fail
|
||||
uint64_t stat_partial_video_ok_audio_payload_damaged; // (d) Video OK, audio lost - FEC fail
|
||||
uint64_t stat_partial_video_rec_audio_hdr_damaged; // (e) Video recovered, audio lost - header CRC fail
|
||||
uint64_t stat_partial_video_rec_audio_payload_damaged; // (f) Video recovered, audio lost - FEC fail
|
||||
uint64_t stat_partial_audio_ok_video_hdr_damaged; // (g) Audio OK, video lost - header CRC fail
|
||||
uint64_t stat_partial_audio_ok_video_payload_damaged; // (h) Audio OK, video lost - FEC fail
|
||||
|
||||
// Packets lost (no audio or video)
|
||||
uint64_t stat_lost_sync_recovery_failure; // (j) Main header sync/CRC unrecoverable
|
||||
uint64_t stat_lost_both_headers_damaged; // (k) Has sync but both subpacket headers damaged
|
||||
uint64_t stat_lost_payloads_damaged; // (l) All headers OK but both payloads unrecoverable
|
||||
|
||||
// Options
|
||||
int verbose;
|
||||
int dump_mode; // Just dump packets, don't decode
|
||||
@@ -236,6 +262,7 @@ typedef struct {
|
||||
uint64_t last_valid_timecode_ns;
|
||||
int packets_since_valid_sync;
|
||||
int soft_sync_recoveries; // Statistics counter
|
||||
int current_packet_recovered; // Flag: was current packet's main header recovered?
|
||||
} dt_decoder_t;
|
||||
|
||||
// =============================================================================
|
||||
@@ -461,37 +488,29 @@ typedef struct {
|
||||
*/
|
||||
static int attempt_soft_sync_recovery(dt_decoder_t *dec, uint8_t *decoded_header,
|
||||
dt_packet_header_t *header __attribute__((unused))) {
|
||||
// CRC is now at offset 28-31 (revised layout 2025-12-17)
|
||||
// CRC is at offset 24-27 (revised layout 2025-12-17, sync is separate)
|
||||
uint32_t stored_crc;
|
||||
memcpy(&stored_crc, decoded_header + 28, 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;
|
||||
memcpy(&stored_crc, decoded_header + 24, 4);
|
||||
|
||||
// === Stage 1 ===
|
||||
// Substitute sync pattern and zero-fill reserved fields
|
||||
// Zero-fill reserved fields (sync is now read separately, not in LDPC header)
|
||||
// Layout: fps(1) + flags(1) + reserved(2) + size(4) + timecode(8) + offset(4) + reserved(4) + crc(4)
|
||||
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 fields (offset 2-3 and 20-23)
|
||||
recovery_header[2] = 0;
|
||||
recovery_header[3] = 0;
|
||||
recovery_header[20] = 0;
|
||||
recovery_header[21] = 0;
|
||||
recovery_header[22] = 0;
|
||||
recovery_header[23] = 0;
|
||||
|
||||
// Zero-fill reserved fields (offset 6-7 and 24-27)
|
||||
recovery_header[6] = 0;
|
||||
recovery_header[7] = 0;
|
||||
recovery_header[24] = 0;
|
||||
recovery_header[25] = 0;
|
||||
recovery_header[26] = 0;
|
||||
recovery_header[27] = 0;
|
||||
|
||||
// Recalculate CRC over bytes 0-27
|
||||
uint32_t calculated_crc = calculate_crc32(recovery_header, 28);
|
||||
// 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");
|
||||
printf(" Soft Sync Recovery Stage 1: SUCCESS (reserved fields corrected)\n");
|
||||
}
|
||||
// Use recovered header
|
||||
memcpy(decoded_header, recovery_header, DT_MAIN_HEADER_SIZE);
|
||||
@@ -502,8 +521,8 @@ static int attempt_soft_sync_recovery(dt_decoder_t *dec, uint8_t *decoded_header
|
||||
// === 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;
|
||||
recovery_header[0] = dec->last_valid_framerate;
|
||||
recovery_header[1] = dec->last_valid_flags;
|
||||
|
||||
// Calculate expected timecode based on last known timecode + GOP duration
|
||||
// GOP duration = 16 frames / framerate
|
||||
@@ -511,14 +530,14 @@ static int attempt_soft_sync_recovery(dt_decoder_t *dec, uint8_t *decoded_header
|
||||
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);
|
||||
// Write expected timecode to bytes 8-15
|
||||
memcpy(recovery_header + 8, &expected_timecode, 8);
|
||||
|
||||
// Recalculate CRC
|
||||
calculated_crc = calculate_crc32(recovery_header, 28);
|
||||
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(" Soft Sync Recovery Stage 2: SUCCESS (reserved/fps/flags/timecode corrected)\n");
|
||||
printf(" Reconstructed timecode: %.3f s\n", expected_timecode / 1000000000.0);
|
||||
}
|
||||
// Use recovered header
|
||||
@@ -540,13 +559,28 @@ static int attempt_soft_sync_recovery(dt_decoder_t *dec, uint8_t *decoded_header
|
||||
}
|
||||
|
||||
static int read_and_decode_header(dt_decoder_t *dec, dt_packet_header_t *header) {
|
||||
// Read LDPC-encoded header (64 bytes = 32 bytes * 2, rate 256/512 bits)
|
||||
// Read sync pattern first (4 bytes, NOT LDPC-coded)
|
||||
uint8_t sync_bytes[4];
|
||||
size_t bytes_read = fread(sync_bytes, 1, 4, dec->input_fp);
|
||||
if (bytes_read < 4) return -1;
|
||||
dec->bytes_read += 4;
|
||||
|
||||
header->sync_pattern = ((uint32_t)sync_bytes[0] << 24) | ((uint32_t)sync_bytes[1] << 16) |
|
||||
((uint32_t)sync_bytes[2] << 8) | sync_bytes[3];
|
||||
|
||||
// Verify sync pattern early
|
||||
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);
|
||||
}
|
||||
|
||||
// Read LDPC-encoded header (56 bytes = 28 bytes * 2, rate 224/448 bits)
|
||||
uint8_t encoded_header[DT_MAIN_HEADER_SIZE * 2];
|
||||
size_t bytes_read = fread(encoded_header, 1, DT_MAIN_HEADER_SIZE * 2, dec->input_fp);
|
||||
bytes_read = fread(encoded_header, 1, DT_MAIN_HEADER_SIZE * 2, dec->input_fp);
|
||||
if (bytes_read < DT_MAIN_HEADER_SIZE * 2) return -1;
|
||||
dec->bytes_read += DT_MAIN_HEADER_SIZE * 2;
|
||||
|
||||
// LDPC decode header (64 bytes -> 32 bytes)
|
||||
// LDPC decode header (56 bytes -> 28 bytes)
|
||||
uint8_t decoded_header[DT_MAIN_HEADER_SIZE];
|
||||
int ldpc_result = ldpc_decode(encoded_header, DT_MAIN_HEADER_SIZE * 2, decoded_header);
|
||||
if (ldpc_result < 0) {
|
||||
@@ -560,26 +594,19 @@ static int read_and_decode_header(dt_decoder_t *dec, dt_packet_header_t *header)
|
||||
}
|
||||
|
||||
// Parse header fields (revised layout 2025-12-17)
|
||||
// Layout: sync(4) + fps(1) + flags(1) + reserved(2) + size(4) + timecode(8) + offset(4) + reserved(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->timecode_ns, decoded_header + 12, 8); // At offset 12
|
||||
memcpy(&header->offset_to_video, decoded_header + 20, 4); // At offset 20
|
||||
// Reserved at offset 24-27 (ignored)
|
||||
memcpy(&header->crc32, decoded_header + 28, 4); // At offset 28
|
||||
// Layout: fps(1) + flags(1) + reserved(2) + size(4) + timecode(8) + offset(4) + reserved(4) + crc(4) = 28 bytes
|
||||
// (sync is read separately above)
|
||||
header->framerate = decoded_header[0];
|
||||
header->flags = decoded_header[1];
|
||||
header->reserved = decoded_header[2] | ((uint16_t)decoded_header[3] << 8);
|
||||
memcpy(&header->packet_size, decoded_header + 4, 4);
|
||||
memcpy(&header->timecode_ns, decoded_header + 8, 8); // At offset 8
|
||||
memcpy(&header->offset_to_video, decoded_header + 16, 4); // At offset 16
|
||||
// Reserved at offset 20-23 (ignored)
|
||||
memcpy(&header->crc32, decoded_header + 24, 4); // At offset 24
|
||||
|
||||
// Verify sync pattern
|
||||
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 bytes 0-27: sync + fps + flags + reserved + size + timecode + offset + reserved)
|
||||
uint32_t calculated_crc = calculate_crc32(decoded_header, 28);
|
||||
// Verify CRC-32 (covers bytes 0-23: fps + flags + reserved + size + timecode + offset + reserved)
|
||||
uint32_t calculated_crc = calculate_crc32(decoded_header, 24);
|
||||
int crc_valid = (calculated_crc == header->crc32);
|
||||
|
||||
if (!crc_valid) {
|
||||
@@ -591,17 +618,16 @@ static int read_and_decode_header(dt_decoder_t *dec, dt_packet_header_t *header)
|
||||
// Attempt Soft Sync Recovery
|
||||
if (dec->packets_processed > 0 && attempt_soft_sync_recovery(dec, decoded_header, header)) {
|
||||
// Re-parse header from recovered data (revised layout 2025-12-17)
|
||||
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);
|
||||
// Reserved at offset 24-27 (ignored)
|
||||
memcpy(&header->crc32, decoded_header + 28, 4);
|
||||
header->framerate = decoded_header[0];
|
||||
header->flags = decoded_header[1];
|
||||
header->reserved = decoded_header[2] | ((uint16_t)decoded_header[3] << 8);
|
||||
memcpy(&header->packet_size, decoded_header + 4, 4);
|
||||
memcpy(&header->timecode_ns, decoded_header + 8, 8);
|
||||
memcpy(&header->offset_to_video, decoded_header + 16, 4);
|
||||
// Reserved at offset 20-23 (ignored)
|
||||
memcpy(&header->crc32, decoded_header + 24, 4);
|
||||
crc_valid = 1; // Recovery succeeded
|
||||
dec->current_packet_recovered = 1; // Track for detailed statistics
|
||||
} else {
|
||||
dec->crc_errors++;
|
||||
dec->packets_since_valid_sync++;
|
||||
@@ -611,12 +637,14 @@ static int read_and_decode_header(dt_decoder_t *dec, dt_packet_header_t *header)
|
||||
fprintf(stderr, "Warning: Packet discarded due to unrecoverable CRC error\n");
|
||||
}
|
||||
dec->sync_losses++;
|
||||
dec->stat_lost_sync_recovery_failure++; // (j) Sync recovery failure
|
||||
return -2;
|
||||
}
|
||||
}
|
||||
|
||||
if (!sync_valid) {
|
||||
dec->sync_losses++;
|
||||
dec->stat_lost_sync_recovery_failure++; // (j) Sync recovery failure
|
||||
return -2;
|
||||
}
|
||||
|
||||
@@ -871,12 +899,12 @@ static int decode_audio_subpacket(dt_decoder_t *dec, const uint8_t *data, size_t
|
||||
size_t *consumed, size_t *samples_written) {
|
||||
*samples_written = 0;
|
||||
|
||||
// Minimum: 32 byte LDPC header (16 bytes * 2, rate 128/256 bits)
|
||||
// Minimum: 28 byte LDPC header (14 bytes * 2, rate 112/224 bits)
|
||||
if (data_len < DT_TAD_HEADER_SIZE * 2) return -1;
|
||||
|
||||
size_t offset = 0;
|
||||
|
||||
// LDPC decode TAD header (32 bytes -> 16 bytes)
|
||||
// LDPC decode TAD header (28 bytes -> 14 bytes)
|
||||
uint8_t decoded_tad_header[DT_TAD_HEADER_SIZE];
|
||||
int ldpc_result = ldpc_decode(data + offset, DT_TAD_HEADER_SIZE * 2, decoded_tad_header);
|
||||
if (ldpc_result < 0) {
|
||||
@@ -890,7 +918,7 @@ static int decode_audio_subpacket(dt_decoder_t *dec, const uint8_t *data, size_t
|
||||
offset += DT_TAD_HEADER_SIZE * 2;
|
||||
|
||||
// Parse TAD header (revised layout 2025-12-17)
|
||||
// Layout: sample_count(2) + quant_bits(1) + reserved(2) + compressed_size(4) + rs_block_count(3) + crc(4)
|
||||
// Layout: sample_count(2) + quant_bits(1) + compressed_size(4) + rs_block_count(3) + crc(4) = 14 bytes
|
||||
uint16_t sample_count;
|
||||
uint8_t quant_bits;
|
||||
uint32_t compressed_size;
|
||||
@@ -899,16 +927,15 @@ static int decode_audio_subpacket(dt_decoder_t *dec, const uint8_t *data, size_t
|
||||
|
||||
memcpy(&sample_count, decoded_tad_header, 2);
|
||||
quant_bits = decoded_tad_header[2];
|
||||
// Reserved at offset 3-4 (ignored)
|
||||
memcpy(&compressed_size, decoded_tad_header + 5, 4);
|
||||
// uint24 rs_block_count (little endian) at offset 9-11
|
||||
rs_block_count = decoded_tad_header[9] |
|
||||
((uint32_t)decoded_tad_header[10] << 8) |
|
||||
((uint32_t)decoded_tad_header[11] << 16);
|
||||
memcpy(&stored_crc, decoded_tad_header + 12, 4);
|
||||
memcpy(&compressed_size, decoded_tad_header + 3, 4);
|
||||
// uint24 rs_block_count (little endian) at offset 7-9
|
||||
rs_block_count = decoded_tad_header[7] |
|
||||
((uint32_t)decoded_tad_header[8] << 8) |
|
||||
((uint32_t)decoded_tad_header[9] << 16);
|
||||
memcpy(&stored_crc, decoded_tad_header + 10, 4);
|
||||
|
||||
// Verify CRC-32 (covers bytes 0-11)
|
||||
uint32_t calculated_crc = calculate_crc32(decoded_tad_header, 12);
|
||||
// Verify CRC-32 (covers bytes 0-9)
|
||||
uint32_t calculated_crc = calculate_crc32(decoded_tad_header, 10);
|
||||
int tad_header_valid = (calculated_crc == stored_crc);
|
||||
if (!tad_header_valid) {
|
||||
if (dec->verbose) {
|
||||
@@ -919,7 +946,7 @@ static int decode_audio_subpacket(dt_decoder_t *dec, const uint8_t *data, size_t
|
||||
// Cannot trust header data - skip audio decoding entirely
|
||||
// Error concealment will insert silence
|
||||
*consumed = offset;
|
||||
return -1;
|
||||
return DECODE_ERR_HEADER_CRC;
|
||||
}
|
||||
|
||||
if (dec->verbose) {
|
||||
@@ -939,13 +966,13 @@ static int decode_audio_subpacket(dt_decoder_t *dec, const uint8_t *data, size_t
|
||||
compressed_size, max_data_size);
|
||||
}
|
||||
*consumed = offset;
|
||||
return -1;
|
||||
return DECODE_ERR_HEADER_CRC; // Header data invalid
|
||||
}
|
||||
|
||||
// Handle empty audio packet (no samples in this GOP)
|
||||
if (compressed_size == 0 || rs_block_count == 0 || sample_count == 0) {
|
||||
*consumed = offset;
|
||||
return 0; // Successfully processed empty audio packet
|
||||
return DECODE_OK; // Successfully processed empty audio packet
|
||||
}
|
||||
|
||||
if (offset + rs_total > data_len) {
|
||||
@@ -953,18 +980,18 @@ static int decode_audio_subpacket(dt_decoder_t *dec, const uint8_t *data, size_t
|
||||
fprintf(stderr, "Warning: Audio packet truncated\n");
|
||||
}
|
||||
*consumed = data_len;
|
||||
return -1; // Unrecoverable
|
||||
return DECODE_ERR_TRUNCATED;
|
||||
}
|
||||
|
||||
// RS decode payload
|
||||
uint8_t *rs_data = malloc(rs_total);
|
||||
if (!rs_data) return -1;
|
||||
if (!rs_data) return DECODE_ERR_TRUNCATED;
|
||||
memcpy(rs_data, data + offset, rs_total);
|
||||
|
||||
uint8_t *decoded_payload = malloc(compressed_size);
|
||||
if (!decoded_payload) {
|
||||
free(rs_data);
|
||||
return -1;
|
||||
return DECODE_ERR_TRUNCATED;
|
||||
}
|
||||
|
||||
int fec_result = decode_fec_blocks(rs_data, rs_total, decoded_payload, compressed_size, dec->fec_mode);
|
||||
@@ -975,7 +1002,7 @@ static int decode_audio_subpacket(dt_decoder_t *dec, const uint8_t *data, size_t
|
||||
free(rs_data);
|
||||
free(decoded_payload);
|
||||
*consumed = offset + rs_total;
|
||||
return -1; // Unrecoverable - FEC failed
|
||||
return DECODE_ERR_PAYLOAD_FEC;
|
||||
} else if (fec_result > 0) {
|
||||
dec->fec_corrections += fec_result;
|
||||
}
|
||||
@@ -1013,7 +1040,7 @@ static int decode_audio_subpacket(dt_decoder_t *dec, const uint8_t *data, size_t
|
||||
free(rs_data);
|
||||
free(decoded_payload);
|
||||
*consumed = offset + rs_total;
|
||||
return -1; // Unrecoverable - TAD decode failed
|
||||
return DECODE_ERR_PAYLOAD_FEC; // Treat TAD decode failure as payload error
|
||||
}
|
||||
|
||||
free(pcmu8_output);
|
||||
@@ -1023,7 +1050,7 @@ static int decode_audio_subpacket(dt_decoder_t *dec, const uint8_t *data, size_t
|
||||
offset += rs_total;
|
||||
*consumed = offset;
|
||||
|
||||
return 0; // Success
|
||||
return DECODE_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1033,12 +1060,26 @@ static int decode_video_subpacket_mt(dt_decoder_t *dec, const uint8_t *data, siz
|
||||
size_t *consumed, int *frames_written) {
|
||||
*frames_written = 0;
|
||||
|
||||
// Minimum: 32 byte LDPC header (16 bytes * 2, rate 128/256 bits)
|
||||
if (data_len < DT_TAV_HEADER_SIZE * 2) return -1;
|
||||
// Minimum: 4 byte sync + 28 byte LDPC header (14 bytes * 2, rate 112/224 bits)
|
||||
if (data_len < 4 + DT_TAV_HEADER_SIZE * 2) return -1;
|
||||
|
||||
size_t offset = 0;
|
||||
|
||||
// LDPC decode TAV header (32 bytes -> 16 bytes)
|
||||
// Read TAV sync pattern (4 bytes, NOT LDPC-coded)
|
||||
uint32_t subpacket_sync = ((uint32_t)data[offset] << 24) |
|
||||
((uint32_t)data[offset + 1] << 16) |
|
||||
((uint32_t)data[offset + 2] << 8) |
|
||||
data[offset + 3];
|
||||
offset += 4;
|
||||
|
||||
// Verify sync pattern early
|
||||
int sync_valid = (subpacket_sync == TAV_SUBPACKET_SYNC);
|
||||
if (!sync_valid && dec->verbose) {
|
||||
fprintf(stderr, "Warning: TAV subpacket sync mismatch (MT) (expected 0x%08X, got 0x%08X)\n",
|
||||
TAV_SUBPACKET_SYNC, subpacket_sync);
|
||||
}
|
||||
|
||||
// LDPC decode TAV header (28 bytes -> 14 bytes)
|
||||
uint8_t decoded_tav_header[DT_TAV_HEADER_SIZE];
|
||||
int ldpc_result = ldpc_decode(data + offset, DT_TAV_HEADER_SIZE * 2, decoded_tav_header);
|
||||
if (ldpc_result < 0) {
|
||||
@@ -1052,32 +1093,24 @@ static int decode_video_subpacket_mt(dt_decoder_t *dec, const uint8_t *data, siz
|
||||
offset += DT_TAV_HEADER_SIZE * 2;
|
||||
|
||||
// Parse TAV header (revised layout 2025-12-17)
|
||||
// Layout: sync(4) + gop_size(1) + compressed_size(4) + rs_block_count(3) + crc(4)
|
||||
uint32_t subpacket_sync = ((uint32_t)decoded_tav_header[0] << 24) |
|
||||
((uint32_t)decoded_tav_header[1] << 16) |
|
||||
((uint32_t)decoded_tav_header[2] << 8) |
|
||||
decoded_tav_header[3];
|
||||
uint8_t gop_size = decoded_tav_header[4];
|
||||
// Layout: gop_size(1) + reserved(2) + compressed_size(4) + rs_block_count(3) + crc(4) = 14 bytes
|
||||
// (sync is read separately above)
|
||||
uint8_t gop_size = decoded_tav_header[0];
|
||||
// Reserved at offset 1-2 (ignored)
|
||||
uint32_t compressed_size;
|
||||
uint32_t rs_block_count;
|
||||
uint32_t stored_crc;
|
||||
|
||||
memcpy(&compressed_size, decoded_tav_header + 5, 4);
|
||||
rs_block_count = decoded_tav_header[9] |
|
||||
((uint32_t)decoded_tav_header[10] << 8) |
|
||||
((uint32_t)decoded_tav_header[11] << 16);
|
||||
memcpy(&stored_crc, decoded_tav_header + 12, 4);
|
||||
memcpy(&compressed_size, decoded_tav_header + 3, 4);
|
||||
rs_block_count = decoded_tav_header[7] |
|
||||
((uint32_t)decoded_tav_header[8] << 8) |
|
||||
((uint32_t)decoded_tav_header[9] << 16);
|
||||
memcpy(&stored_crc, decoded_tav_header + 10, 4);
|
||||
|
||||
// Verify sync pattern and CRC-32 (covers bytes 0-11)
|
||||
int sync_valid = (subpacket_sync == TAV_SUBPACKET_SYNC);
|
||||
uint32_t calculated_crc = calculate_crc32(decoded_tav_header, 12);
|
||||
// Verify CRC-32 (covers bytes 0-9)
|
||||
uint32_t calculated_crc = calculate_crc32(decoded_tav_header, 10);
|
||||
int crc_valid = (calculated_crc == stored_crc);
|
||||
|
||||
if (!sync_valid && dec->verbose) {
|
||||
fprintf(stderr, "Warning: TAV subpacket sync mismatch (MT) (expected 0x%08X, got 0x%08X)\n",
|
||||
TAV_SUBPACKET_SYNC, subpacket_sync);
|
||||
}
|
||||
|
||||
if (!crc_valid) {
|
||||
if (dec->verbose) {
|
||||
fprintf(stderr, "Warning: TAV header CRC mismatch (MT) (expected 0x%08X, got 0x%08X) - skipping video\n",
|
||||
@@ -1086,7 +1119,7 @@ static int decode_video_subpacket_mt(dt_decoder_t *dec, const uint8_t *data, siz
|
||||
dec->crc_errors++;
|
||||
// Cannot trust header data - skip video decoding entirely
|
||||
*consumed = offset;
|
||||
return -1;
|
||||
return DECODE_ERR_HEADER_CRC;
|
||||
}
|
||||
|
||||
// Calculate RS payload size
|
||||
@@ -1100,23 +1133,23 @@ static int decode_video_subpacket_mt(dt_decoder_t *dec, const uint8_t *data, siz
|
||||
compressed_size, max_data_size);
|
||||
}
|
||||
*consumed = offset;
|
||||
return -1;
|
||||
return DECODE_ERR_HEADER_CRC; // Header data invalid
|
||||
}
|
||||
|
||||
if (offset + rs_total > data_len) {
|
||||
*consumed = data_len;
|
||||
return -1; // Unrecoverable
|
||||
return DECODE_ERR_TRUNCATED;
|
||||
}
|
||||
|
||||
// RS decode payload
|
||||
uint8_t *rs_data = malloc(rs_total);
|
||||
if (!rs_data) return -1;
|
||||
if (!rs_data) return DECODE_ERR_TRUNCATED;
|
||||
memcpy(rs_data, data + offset, rs_total);
|
||||
|
||||
uint8_t *decoded_payload = malloc(compressed_size);
|
||||
if (!decoded_payload) {
|
||||
free(rs_data);
|
||||
return -1;
|
||||
return DECODE_ERR_TRUNCATED;
|
||||
}
|
||||
|
||||
int fec_result = decode_fec_blocks(rs_data, rs_total, decoded_payload, compressed_size, dec->fec_mode);
|
||||
@@ -1127,7 +1160,7 @@ static int decode_video_subpacket_mt(dt_decoder_t *dec, const uint8_t *data, siz
|
||||
free(rs_data);
|
||||
free(decoded_payload);
|
||||
*consumed = offset + rs_total;
|
||||
return -1; // Unrecoverable - FEC failed
|
||||
return DECODE_ERR_PAYLOAD_FEC;
|
||||
} else if (fec_result > 0) {
|
||||
dec->fec_corrections += fec_result;
|
||||
}
|
||||
@@ -1141,7 +1174,7 @@ static int decode_video_subpacket_mt(dt_decoder_t *dec, const uint8_t *data, siz
|
||||
// Fall back to single-threaded decoding for this packet
|
||||
free(decoded_payload);
|
||||
*consumed = offset + rs_total;
|
||||
return -1;
|
||||
return DECODE_ERR_TRUNCATED; // Treat as truncated/other error
|
||||
}
|
||||
if (dec->verbose) {
|
||||
printf("Initialized multithreaded decoding: %d threads\n", dec->num_threads);
|
||||
@@ -1230,19 +1263,33 @@ static int decode_video_subpacket_mt(dt_decoder_t *dec, const uint8_t *data, siz
|
||||
*consumed = offset;
|
||||
*frames_written = gop_size; // Optimistic - assume decode will succeed
|
||||
|
||||
return 0; // Success - job submitted
|
||||
return DECODE_OK; // Success - job submitted
|
||||
}
|
||||
|
||||
static int decode_video_subpacket(dt_decoder_t *dec, const uint8_t *data, size_t data_len,
|
||||
size_t *consumed, int *frames_written) {
|
||||
*frames_written = 0;
|
||||
|
||||
// Minimum: 32 byte LDPC header (16 bytes * 2, rate 128/256 bits)
|
||||
if (data_len < DT_TAV_HEADER_SIZE * 2) return -1;
|
||||
// Minimum: 4 byte sync + 28 byte LDPC header (14 bytes * 2, rate 112/224 bits)
|
||||
if (data_len < 4 + DT_TAV_HEADER_SIZE * 2) return -1;
|
||||
|
||||
size_t offset = 0;
|
||||
|
||||
// LDPC decode TAV header (32 bytes -> 16 bytes)
|
||||
// Read TAV sync pattern (4 bytes, NOT LDPC-coded)
|
||||
uint32_t subpacket_sync = ((uint32_t)data[offset] << 24) |
|
||||
((uint32_t)data[offset + 1] << 16) |
|
||||
((uint32_t)data[offset + 2] << 8) |
|
||||
data[offset + 3];
|
||||
offset += 4;
|
||||
|
||||
// Verify sync pattern early
|
||||
int sync_valid = (subpacket_sync == TAV_SUBPACKET_SYNC);
|
||||
if (!sync_valid && dec->verbose) {
|
||||
fprintf(stderr, "Warning: TAV subpacket sync mismatch (expected 0x%08X, got 0x%08X)\n",
|
||||
TAV_SUBPACKET_SYNC, subpacket_sync);
|
||||
}
|
||||
|
||||
// LDPC decode TAV header (28 bytes -> 14 bytes)
|
||||
uint8_t decoded_tav_header[DT_TAV_HEADER_SIZE];
|
||||
int ldpc_result = ldpc_decode(data + offset, DT_TAV_HEADER_SIZE * 2, decoded_tav_header);
|
||||
if (ldpc_result < 0) {
|
||||
@@ -1256,33 +1303,25 @@ static int decode_video_subpacket(dt_decoder_t *dec, const uint8_t *data, size_t
|
||||
offset += DT_TAV_HEADER_SIZE * 2;
|
||||
|
||||
// Parse TAV header (revised layout 2025-12-17)
|
||||
// Layout: sync(4) + gop_size(1) + compressed_size(4) + rs_block_count(3) + crc(4)
|
||||
uint32_t subpacket_sync = ((uint32_t)decoded_tav_header[0] << 24) |
|
||||
((uint32_t)decoded_tav_header[1] << 16) |
|
||||
((uint32_t)decoded_tav_header[2] << 8) |
|
||||
decoded_tav_header[3];
|
||||
uint8_t gop_size = decoded_tav_header[4];
|
||||
// Layout: gop_size(1) + reserved(2) + compressed_size(4) + rs_block_count(3) + crc(4) = 14 bytes
|
||||
// (sync is read separately above)
|
||||
uint8_t gop_size = decoded_tav_header[0];
|
||||
// Reserved at offset 1-2 (ignored)
|
||||
uint32_t compressed_size;
|
||||
uint32_t rs_block_count;
|
||||
uint32_t stored_crc;
|
||||
|
||||
memcpy(&compressed_size, decoded_tav_header + 5, 4);
|
||||
// uint24 rs_block_count (little endian) at offset 9-11
|
||||
rs_block_count = decoded_tav_header[9] |
|
||||
((uint32_t)decoded_tav_header[10] << 8) |
|
||||
((uint32_t)decoded_tav_header[11] << 16);
|
||||
memcpy(&stored_crc, decoded_tav_header + 12, 4);
|
||||
memcpy(&compressed_size, decoded_tav_header + 3, 4);
|
||||
// uint24 rs_block_count (little endian) at offset 7-9
|
||||
rs_block_count = decoded_tav_header[7] |
|
||||
((uint32_t)decoded_tav_header[8] << 8) |
|
||||
((uint32_t)decoded_tav_header[9] << 16);
|
||||
memcpy(&stored_crc, decoded_tav_header + 10, 4);
|
||||
|
||||
// Verify sync pattern and CRC-32 (covers bytes 0-11)
|
||||
int sync_valid = (subpacket_sync == TAV_SUBPACKET_SYNC);
|
||||
uint32_t calculated_crc = calculate_crc32(decoded_tav_header, 12);
|
||||
// Verify CRC-32 (covers bytes 0-9)
|
||||
uint32_t calculated_crc = calculate_crc32(decoded_tav_header, 10);
|
||||
int crc_valid = (calculated_crc == stored_crc);
|
||||
|
||||
if (!sync_valid && dec->verbose) {
|
||||
fprintf(stderr, "Warning: TAV subpacket sync mismatch (expected 0x%08X, got 0x%08X)\n",
|
||||
TAV_SUBPACKET_SYNC, subpacket_sync);
|
||||
}
|
||||
|
||||
if (!crc_valid) {
|
||||
if (dec->verbose) {
|
||||
fprintf(stderr, "Warning: TAV header CRC mismatch (expected 0x%08X, got 0x%08X) - skipping video\n",
|
||||
@@ -1292,7 +1331,7 @@ static int decode_video_subpacket(dt_decoder_t *dec, const uint8_t *data, size_t
|
||||
// Cannot trust header data - skip video decoding entirely
|
||||
// Error concealment will use freeze frame
|
||||
*consumed = offset;
|
||||
return -1;
|
||||
return DECODE_ERR_HEADER_CRC;
|
||||
}
|
||||
|
||||
if (dec->verbose) {
|
||||
@@ -1311,7 +1350,7 @@ static int decode_video_subpacket(dt_decoder_t *dec, const uint8_t *data, size_t
|
||||
compressed_size, max_data_size);
|
||||
}
|
||||
*consumed = offset;
|
||||
return -1;
|
||||
return DECODE_ERR_HEADER_CRC; // Header data invalid
|
||||
}
|
||||
|
||||
if (offset + rs_total > data_len) {
|
||||
@@ -1319,18 +1358,18 @@ static int decode_video_subpacket(dt_decoder_t *dec, const uint8_t *data, size_t
|
||||
fprintf(stderr, "Warning: Video packet truncated\n");
|
||||
}
|
||||
*consumed = data_len;
|
||||
return -1; // Unrecoverable
|
||||
return DECODE_ERR_TRUNCATED;
|
||||
}
|
||||
|
||||
// RS decode payload
|
||||
uint8_t *rs_data = malloc(rs_total);
|
||||
if (!rs_data) return -1;
|
||||
if (!rs_data) return DECODE_ERR_TRUNCATED;
|
||||
memcpy(rs_data, data + offset, rs_total);
|
||||
|
||||
uint8_t *decoded_payload = malloc(compressed_size);
|
||||
if (!decoded_payload) {
|
||||
free(rs_data);
|
||||
return -1;
|
||||
return DECODE_ERR_TRUNCATED;
|
||||
}
|
||||
|
||||
int fec_result = decode_fec_blocks(rs_data, rs_total, decoded_payload, compressed_size, dec->fec_mode);
|
||||
@@ -1341,7 +1380,7 @@ static int decode_video_subpacket(dt_decoder_t *dec, const uint8_t *data, size_t
|
||||
free(rs_data);
|
||||
free(decoded_payload);
|
||||
*consumed = offset + rs_total;
|
||||
return -1; // Unrecoverable - FEC failed
|
||||
return DECODE_ERR_PAYLOAD_FEC;
|
||||
} else if (fec_result > 0) {
|
||||
dec->fec_corrections += fec_result;
|
||||
}
|
||||
@@ -1444,7 +1483,7 @@ static int decode_video_subpacket(dt_decoder_t *dec, const uint8_t *data, size_t
|
||||
offset += rs_total;
|
||||
*consumed = offset;
|
||||
|
||||
return 0; // Success
|
||||
return DECODE_OK;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
@@ -1780,31 +1819,80 @@ static int process_packet(dt_decoder_t *dec) {
|
||||
// Decode audio and video
|
||||
size_t audio_samples_written = 0;
|
||||
int video_frames_written = 0;
|
||||
int audio_result = DECODE_OK;
|
||||
int video_result = DECODE_OK;
|
||||
|
||||
// Process TAD subpacket (audio comes first, no type byte)
|
||||
size_t tad_consumed = 0;
|
||||
if (header.offset_to_video > 0) {
|
||||
decode_audio_subpacket(dec, packet_data, header.offset_to_video,
|
||||
&tad_consumed, &audio_samples_written);
|
||||
audio_result = decode_audio_subpacket(dec, packet_data, header.offset_to_video,
|
||||
&tad_consumed, &audio_samples_written);
|
||||
}
|
||||
|
||||
// Process TAV subpacket (video comes after audio)
|
||||
if (header.offset_to_video < header.packet_size) {
|
||||
size_t tav_consumed = 0;
|
||||
if (dec->num_threads > 1) {
|
||||
decode_video_subpacket_mt(dec, packet_data + header.offset_to_video,
|
||||
header.packet_size - header.offset_to_video,
|
||||
&tav_consumed, &video_frames_written);
|
||||
video_result = decode_video_subpacket_mt(dec, packet_data + header.offset_to_video,
|
||||
header.packet_size - header.offset_to_video,
|
||||
&tav_consumed, &video_frames_written);
|
||||
} else {
|
||||
decode_video_subpacket(dec, packet_data + header.offset_to_video,
|
||||
header.packet_size - header.offset_to_video,
|
||||
&tav_consumed, &video_frames_written);
|
||||
video_result = decode_video_subpacket(dec, packet_data + header.offset_to_video,
|
||||
header.packet_size - header.offset_to_video,
|
||||
&tav_consumed, &video_frames_written);
|
||||
}
|
||||
}
|
||||
|
||||
// Categorize packet outcome for detailed statistics
|
||||
int audio_ok = (audio_result == DECODE_OK);
|
||||
int video_ok = (video_result == DECODE_OK);
|
||||
int audio_hdr_fail = (audio_result == DECODE_ERR_HEADER_CRC);
|
||||
int video_hdr_fail = (video_result == DECODE_ERR_HEADER_CRC);
|
||||
|
||||
if (audio_ok && video_ok) {
|
||||
// Fully decoded
|
||||
if (dec->current_packet_recovered) {
|
||||
dec->stat_fully_decoded_recovered++; // (b)
|
||||
} else {
|
||||
dec->stat_fully_decoded_intact++; // (a)
|
||||
}
|
||||
} else if (video_ok && !audio_ok) {
|
||||
// Video OK, audio lost
|
||||
if (dec->current_packet_recovered) {
|
||||
// Video recovered by soft sync
|
||||
if (audio_hdr_fail) {
|
||||
dec->stat_partial_video_rec_audio_hdr_damaged++; // (e)
|
||||
} else {
|
||||
dec->stat_partial_video_rec_audio_payload_damaged++; // (f)
|
||||
}
|
||||
} else {
|
||||
// Video intact
|
||||
if (audio_hdr_fail) {
|
||||
dec->stat_partial_video_ok_audio_hdr_damaged++; // (c)
|
||||
} else {
|
||||
dec->stat_partial_video_ok_audio_payload_damaged++; // (d)
|
||||
}
|
||||
}
|
||||
} else if (audio_ok && !video_ok) {
|
||||
// Audio OK, video lost
|
||||
if (video_hdr_fail) {
|
||||
dec->stat_partial_audio_ok_video_hdr_damaged++; // (g)
|
||||
} else {
|
||||
dec->stat_partial_audio_ok_video_payload_damaged++; // (h)
|
||||
}
|
||||
} else {
|
||||
// Both audio and video lost
|
||||
if (audio_hdr_fail && video_hdr_fail) {
|
||||
dec->stat_lost_both_headers_damaged++; // (k)
|
||||
} else {
|
||||
dec->stat_lost_payloads_damaged++; // (l)
|
||||
}
|
||||
}
|
||||
|
||||
// Update timecode tracking
|
||||
dec->last_timecode_ns = header.timecode_ns;
|
||||
dec->packets_processed++;
|
||||
dec->current_packet_recovered = 0; // Reset for next packet
|
||||
|
||||
if (!dec->verbose && dec->packets_processed % 10 == 0) {
|
||||
fprintf(stderr, "\rDecoding packet %lu, frames: %lu...",
|
||||
@@ -1915,14 +2003,50 @@ static int run_decoder(dt_decoder_t *dec) {
|
||||
}
|
||||
|
||||
fprintf(stderr, "\n");
|
||||
printf("\nDecoding complete:\n");
|
||||
printf(" Packets processed: %lu\n", dec->packets_processed);
|
||||
printf(" Frames decoded: %lu\n", dec->frames_decoded);
|
||||
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);
|
||||
|
||||
// Calculate summary totals
|
||||
uint64_t fully_decoded = dec->stat_fully_decoded_intact + dec->stat_fully_decoded_recovered;
|
||||
uint64_t partially_decoded = dec->stat_partial_video_ok_audio_hdr_damaged +
|
||||
dec->stat_partial_video_ok_audio_payload_damaged +
|
||||
dec->stat_partial_video_rec_audio_hdr_damaged +
|
||||
dec->stat_partial_video_rec_audio_payload_damaged +
|
||||
dec->stat_partial_audio_ok_video_hdr_damaged +
|
||||
dec->stat_partial_audio_ok_video_payload_damaged;
|
||||
uint64_t packets_lost = dec->stat_lost_sync_recovery_failure +
|
||||
dec->stat_lost_both_headers_damaged +
|
||||
dec->stat_lost_payloads_damaged;
|
||||
|
||||
printf("\n=== Decoding Statistics ===\n");
|
||||
printf("Total packets: %lu\n", dec->packets_processed + packets_lost);
|
||||
printf("Frames decoded: %lu\n", dec->frames_decoded);
|
||||
printf("Bytes read: %lu\n", dec->bytes_read);
|
||||
printf("FEC corrections: %lu\n", dec->fec_corrections);
|
||||
printf("\n--- Packet Outcome Breakdown ---\n");
|
||||
|
||||
printf("Packets fully decoded (audio and video present): %lu\n", fully_decoded);
|
||||
printf(" (a) Packets intact: %lu\n", dec->stat_fully_decoded_intact);
|
||||
printf(" (b) Recovered by soft sync recovery: %lu\n", dec->stat_fully_decoded_recovered);
|
||||
|
||||
printf("Packets partially decoded (audio or video only): %lu\n", partially_decoded);
|
||||
printf(" (c) Intact video, audio lost due to subpacket header damage: %lu\n",
|
||||
dec->stat_partial_video_ok_audio_hdr_damaged);
|
||||
printf(" (d) Intact video, audio lost due to payload damage: %lu\n",
|
||||
dec->stat_partial_video_ok_audio_payload_damaged);
|
||||
printf(" (e) Video resync'd by soft sync recovery, audio lost due to subpacket header damage: %lu\n",
|
||||
dec->stat_partial_video_rec_audio_hdr_damaged);
|
||||
printf(" (f) Video resync'd by soft sync recovery, audio lost due to payload damage: %lu\n",
|
||||
dec->stat_partial_video_rec_audio_payload_damaged);
|
||||
printf(" (g) Intact audio, video lost due to subpacket header damage: %lu\n",
|
||||
dec->stat_partial_audio_ok_video_hdr_damaged);
|
||||
printf(" (h) Intact audio, video lost due to payload damage: %lu\n",
|
||||
dec->stat_partial_audio_ok_video_payload_damaged);
|
||||
|
||||
printf("Packets lost (no audio and video): %lu\n", packets_lost);
|
||||
printf(" (j) Sync recovery failure: %lu\n", dec->stat_lost_sync_recovery_failure);
|
||||
printf(" (k) Has sync but both subpacket headers damaged: %lu\n", dec->stat_lost_both_headers_damaged);
|
||||
printf(" (l) All headers intact but damaged payload: %lu\n", dec->stat_lost_payloads_damaged);
|
||||
|
||||
printf("===========================\n");
|
||||
|
||||
// Mux output files
|
||||
mux_output(dec);
|
||||
|
||||
@@ -63,12 +63,9 @@
|
||||
#define DT_SPATIAL_LEVELS 4
|
||||
#define DT_TEMPORAL_LEVELS 2
|
||||
|
||||
// Header sizes (before LDPC encoding) - revised 2025-12-17
|
||||
// LDPC rates are specified in bits: 256/512 = 256 bits raw -> 512 bits encoded
|
||||
// Converted to bytes: 32 -> 64 bytes, 16 -> 32 bytes
|
||||
#define DT_MAIN_HEADER_SIZE 32 // sync(4) + fps(1) + flags(1) + reserved(2) + size(4) + timecode(8) + offset(4) + reserved(4) + crc(4)
|
||||
#define DT_TAD_HEADER_SIZE 16 // sample_count(2) + quant_bits(1) + reserved(2) + compressed_size(4) + rs_block_count(3) + crc(4)
|
||||
#define DT_TAV_HEADER_SIZE 16 // sync(4) + gop_size(1) + compressed_size(4) + rs_block_count(3) + crc(4)
|
||||
#define DT_MAIN_HEADER_SIZE 28 // fps(1) + flags(1) + reserved(2) + size(4) + timecode(8) + offset(4) + reserved(4) + crc(4)
|
||||
#define DT_TAD_HEADER_SIZE 14 // sample_count(2) + quant_bits(1) + compressed_size(4) + rs_block_count(3) + crc(4)
|
||||
#define DT_TAV_HEADER_SIZE 14 // gop_size(1) + reserved(2) + compressed_size(4) + rs_block_count(3) + crc(4)
|
||||
|
||||
// TAV subpacket sync pattern (big endian)
|
||||
#define TAV_SUBPACKET_SYNC 0xA3F7C91E
|
||||
@@ -305,32 +302,33 @@ static int write_packet(dt_encoder_t *enc, uint64_t timecode_ns,
|
||||
size_t tad_rs_size = tad_rs_blocks * RS_BLOCK_SIZE;
|
||||
size_t tav_rs_size = tav_rs_blocks * RS_BLOCK_SIZE;
|
||||
|
||||
// Subpacket sizes: LDPC header (16->32 bytes) + RS payload
|
||||
size_t tad_subpacket_size = DT_TAD_HEADER_SIZE * 2 + tad_rs_size;
|
||||
size_t tav_subpacket_size = DT_TAV_HEADER_SIZE * 2 + tav_rs_size;
|
||||
// Subpacket sizes: LDPC header + RS payload (TAV includes sync)
|
||||
size_t tad_subpacket_size = DT_TAD_HEADER_SIZE * 2 + tad_rs_size; // 28 + RS
|
||||
size_t tav_subpacket_size = 4 + DT_TAV_HEADER_SIZE * 2 + tav_rs_size; // sync(4) + 28 + RS
|
||||
|
||||
uint32_t offset_to_video = tad_subpacket_size;
|
||||
uint32_t offset_to_video = tad_subpacket_size; // Offset from after main header to TAV sync
|
||||
uint32_t packet_size = tad_subpacket_size + tav_subpacket_size;
|
||||
|
||||
// Build main header (32 bytes raw = 256 bits)
|
||||
// Layout: sync(4) + fps(1) + flags(1) + reserved(2) + size(4) + timecode(8) + offset(4) + reserved(4) + crc(4)
|
||||
// CRC is calculated over bytes 0-27 (everything except CRC itself)
|
||||
uint8_t header[DT_MAIN_HEADER_SIZE]; // 32 bytes
|
||||
// Build main header (28 bytes raw = 224 bits, sync written separately)
|
||||
// Layout: fps(1) + flags(1) + reserved(2) + size(4) + timecode(8) + offset(4) + reserved(4) + crc(4)
|
||||
// CRC is calculated over bytes 0-23 (everything except CRC itself)
|
||||
uint8_t master_sync[4];
|
||||
uint8_t header[DT_MAIN_HEADER_SIZE]; // 28 bytes
|
||||
memset(header, 0, 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;
|
||||
header[0] = (sync >> 24) & 0xFF;
|
||||
header[1] = (sync >> 16) & 0xFF;
|
||||
header[2] = (sync >> 8) & 0xFF;
|
||||
header[3] = sync & 0xFF;
|
||||
master_sync[0] = (sync >> 24) & 0xFF;
|
||||
master_sync[1] = (sync >> 16) & 0xFF;
|
||||
master_sync[2] = (sync >> 8) & 0xFF;
|
||||
master_sync[3] = sync & 0xFF;
|
||||
|
||||
// FPS byte: encode framerate
|
||||
uint8_t fps_byte;
|
||||
if (enc->fps_den == 1) fps_byte = enc->fps_num;
|
||||
else if (enc->fps_den == 1001) fps_byte = enc->fps_num / 1000;
|
||||
else fps_byte = enc->fps_num / enc->fps_den;
|
||||
header[4] = fps_byte;
|
||||
header[0] = fps_byte;
|
||||
|
||||
// Flags byte
|
||||
uint8_t flags = 0;
|
||||
@@ -338,77 +336,77 @@ static int write_packet(dt_encoder_t *enc, uint64_t timecode_ns,
|
||||
flags |= (enc->fps_den == 1001 ? 0x02 : 0x00);
|
||||
flags |= ((enc->fec_mode & 0x01) << 2); // FEC mode in bit 2
|
||||
flags |= (enc->quality_index & 0x0F) << 4;
|
||||
header[5] = flags;
|
||||
header[1] = flags;
|
||||
|
||||
// Reserved (2 bytes) at offset 6-7
|
||||
header[6] = 0;
|
||||
header[7] = 0;
|
||||
// Reserved (2 bytes) at offset 2-3
|
||||
header[2] = 0;
|
||||
header[3] = 0;
|
||||
|
||||
// Packet size (4 bytes) at offset 8-11
|
||||
memcpy(header + 8, &packet_size, 4);
|
||||
// Packet size (4 bytes) at offset 4-7
|
||||
memcpy(header + 4, &packet_size, 4);
|
||||
|
||||
// Timecode (8 bytes) at offset 12-19
|
||||
memcpy(header + 12, &timecode_ns, 8);
|
||||
// Timecode (8 bytes) at offset 8-15
|
||||
memcpy(header + 8, &timecode_ns, 8);
|
||||
|
||||
// Offset to video (4 bytes) at offset 20-23
|
||||
memcpy(header + 20, &offset_to_video, 4);
|
||||
// Offset to video (4 bytes) at offset 16-20
|
||||
memcpy(header + 16, &offset_to_video, 4);
|
||||
|
||||
// Reserved (4 bytes) at offset 24-27
|
||||
// Reserved (4 bytes) at offset 20-24
|
||||
// Already zero from memset
|
||||
|
||||
// CRC-32 (4 bytes) at offset 28-31, calculated over bytes 0-27
|
||||
uint32_t crc = calculate_crc32(header, 28);
|
||||
memcpy(header + 28, &crc, 4);
|
||||
// CRC-32 (4 bytes) at offset 24-27, calculated over bytes 0-23
|
||||
uint32_t crc = calculate_crc32(header, 24);
|
||||
memcpy(header + 24, &crc, 4);
|
||||
|
||||
// LDPC encode main header (32 -> 64 bytes, rate 256/512 bits)
|
||||
// LDPC encode main header (28 -> 56 bytes, rate 224/448 bits)
|
||||
uint8_t ldpc_header[DT_MAIN_HEADER_SIZE * 2];
|
||||
ldpc_encode(header, DT_MAIN_HEADER_SIZE, ldpc_header);
|
||||
|
||||
// Build TAD subpacket header (16 bytes raw = 128 bits)
|
||||
// Layout: sample_count(2) + quant_bits(1) + reserved(2) + compressed_size(4) + rs_block_count(3) + crc(4)
|
||||
uint8_t tad_header[DT_TAD_HEADER_SIZE]; // 16 bytes
|
||||
// Build TAD subpacket header (14 bytes raw = 112 bits)
|
||||
// Layout: sample_count(2) + quant_bits(1) + compressed_size(4) + rs_block_count(3) + crc(4)
|
||||
uint8_t tad_header[DT_TAD_HEADER_SIZE]; // 14 bytes
|
||||
memset(tad_header, 0, DT_TAD_HEADER_SIZE);
|
||||
|
||||
memcpy(tad_header + 0, &audio_samples, 2);
|
||||
tad_header[2] = audio_quant_bits;
|
||||
// Reserved (2 bytes) at offset 3-4
|
||||
uint32_t tad_compressed_size = tad_size;
|
||||
memcpy(tad_header + 5, &tad_compressed_size, 4);
|
||||
// RS block count as uint24 at offset 9-11
|
||||
tad_header[9] = tad_rs_blocks & 0xFF;
|
||||
tad_header[10] = (tad_rs_blocks >> 8) & 0xFF;
|
||||
tad_header[11] = (tad_rs_blocks >> 16) & 0xFF;
|
||||
// CRC-32 (4 bytes) at offset 12-15, calculated over bytes 0-11
|
||||
uint32_t tad_crc = calculate_crc32(tad_header, 12);
|
||||
memcpy(tad_header + 12, &tad_crc, 4);
|
||||
memcpy(tad_header + 3, &tad_compressed_size, 4);
|
||||
// RS block count as uint24 at offset 7-9
|
||||
tad_header[7] = tad_rs_blocks & 0xFF;
|
||||
tad_header[8] = (tad_rs_blocks >> 8) & 0xFF;
|
||||
tad_header[9] = (tad_rs_blocks >> 16) & 0xFF;
|
||||
// CRC-32 (4 bytes) at offset 12-15, calculated over bytes 0-9
|
||||
uint32_t tad_crc = calculate_crc32(tad_header, 10);
|
||||
memcpy(tad_header + 10, &tad_crc, 4);
|
||||
|
||||
// LDPC encode TAD header (16 -> 32 bytes, rate 128/256 bits)
|
||||
// LDPC encode TAD header (14 -> 28 bytes, rate 112/224 bits)
|
||||
uint8_t ldpc_tad_header[DT_TAD_HEADER_SIZE * 2];
|
||||
ldpc_encode(tad_header, DT_TAD_HEADER_SIZE, ldpc_tad_header);
|
||||
|
||||
// Build TAV subpacket header (16 bytes raw = 128 bits)
|
||||
// Build TAV subpacket header (14 bytes raw = 112 bits)
|
||||
// Layout: sync(4) + gop_size(1) + compressed_size(4) + rs_block_count(3) + crc(4)
|
||||
uint8_t tav_header[DT_TAV_HEADER_SIZE]; // 16 bytes
|
||||
uint8_t tav_sync[4];
|
||||
uint8_t tav_header[DT_TAV_HEADER_SIZE]; // 14 bytes
|
||||
memset(tav_header, 0, DT_TAV_HEADER_SIZE);
|
||||
|
||||
// Write TAV subpacket sync pattern in big-endian
|
||||
tav_header[0] = (TAV_SUBPACKET_SYNC >> 24) & 0xFF;
|
||||
tav_header[1] = (TAV_SUBPACKET_SYNC >> 16) & 0xFF;
|
||||
tav_header[2] = (TAV_SUBPACKET_SYNC >> 8) & 0xFF;
|
||||
tav_header[3] = TAV_SUBPACKET_SYNC & 0xFF;
|
||||
tav_sync[0] = (TAV_SUBPACKET_SYNC >> 24) & 0xFF;
|
||||
tav_sync[1] = (TAV_SUBPACKET_SYNC >> 16) & 0xFF;
|
||||
tav_sync[2] = (TAV_SUBPACKET_SYNC >> 8) & 0xFF;
|
||||
tav_sync[3] = TAV_SUBPACKET_SYNC & 0xFF;
|
||||
|
||||
tav_header[4] = gop_size;
|
||||
tav_header[0] = gop_size;
|
||||
uint32_t tav_compressed_size = tav_size;
|
||||
memcpy(tav_header + 5, &tav_compressed_size, 4);
|
||||
// RS block count as uint24 at offset 9-11
|
||||
tav_header[9] = tav_rs_blocks & 0xFF;
|
||||
tav_header[10] = (tav_rs_blocks >> 8) & 0xFF;
|
||||
tav_header[11] = (tav_rs_blocks >> 16) & 0xFF;
|
||||
memcpy(tav_header + 3, &tav_compressed_size, 4);
|
||||
// RS block count as uint24 at offset 7-9
|
||||
tav_header[7] = tav_rs_blocks & 0xFF;
|
||||
tav_header[8] = (tav_rs_blocks >> 8) & 0xFF;
|
||||
tav_header[9] = (tav_rs_blocks >> 16) & 0xFF;
|
||||
// CRC-32 (4 bytes) at offset 12-15, calculated over bytes 0-11
|
||||
uint32_t tav_crc = calculate_crc32(tav_header, 12);
|
||||
memcpy(tav_header + 12, &tav_crc, 4);
|
||||
uint32_t tav_crc = calculate_crc32(tav_header, 10);
|
||||
memcpy(tav_header + 10, &tav_crc, 4);
|
||||
|
||||
// LDPC encode TAV header (16 -> 32 bytes, rate 128/256 bits)
|
||||
// LDPC encode TAV header (14 -> 28 bytes, rate 112/224 bits)
|
||||
uint8_t ldpc_tav_header[DT_TAV_HEADER_SIZE * 2];
|
||||
ldpc_encode(tav_header, DT_TAV_HEADER_SIZE, ldpc_tav_header);
|
||||
|
||||
@@ -420,13 +418,16 @@ static int write_packet(dt_encoder_t *enc, uint64_t timecode_ns,
|
||||
encode_fec_blocks(tav_data, tav_size, tav_rs_data, enc->fec_mode);
|
||||
|
||||
// Write everything
|
||||
fwrite(ldpc_header, 1, DT_MAIN_HEADER_SIZE * 2, enc->output_fp);
|
||||
fwrite(ldpc_tad_header, 1, DT_TAD_HEADER_SIZE * 2, enc->output_fp);
|
||||
fwrite(tad_rs_data, 1, tad_rs_size, enc->output_fp);
|
||||
fwrite(ldpc_tav_header, 1, DT_TAV_HEADER_SIZE * 2, enc->output_fp);
|
||||
fwrite(tav_rs_data, 1, tav_rs_size, enc->output_fp);
|
||||
// Sync patterns are written separately (not LDPC-coded) per spec
|
||||
fwrite(master_sync, 1, 4, enc->output_fp); // Main sync (4 bytes)
|
||||
fwrite(ldpc_header, 1, DT_MAIN_HEADER_SIZE * 2, enc->output_fp); // LDPC header (56 bytes)
|
||||
fwrite(ldpc_tad_header, 1, DT_TAD_HEADER_SIZE * 2, enc->output_fp); // TAD LDPC header (28 bytes)
|
||||
fwrite(tad_rs_data, 1, tad_rs_size, enc->output_fp); // TAD RS payload
|
||||
fwrite(tav_sync, 1, 4, enc->output_fp); // TAV sync (4 bytes)
|
||||
fwrite(ldpc_tav_header, 1, DT_TAV_HEADER_SIZE * 2, enc->output_fp); // TAV LDPC header (28 bytes)
|
||||
fwrite(tav_rs_data, 1, tav_rs_size, enc->output_fp); // TAV RS payload
|
||||
|
||||
size_t total_written = DT_MAIN_HEADER_SIZE * 2 + tad_subpacket_size + tav_subpacket_size;
|
||||
size_t total_written = 4 + DT_MAIN_HEADER_SIZE * 2 + tad_subpacket_size + 4 + tav_subpacket_size;
|
||||
|
||||
if (enc->verbose) {
|
||||
printf("GOP %lu: %d frames, header=%zu tad=%zu tav=%zu total=%zu bytes\n",
|
||||
|
||||
Reference in New Issue
Block a user