mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-03-07 11:51:49 +09:00
TAV-DT format revision and soft sync recovery
This commit is contained in:
@@ -1647,9 +1647,9 @@ start of the next packet
|
|||||||
* Quality indices follow TSVM encoder's
|
* Quality indices follow TSVM encoder's
|
||||||
int16 Reserved (zero-fill)
|
int16 Reserved (zero-fill)
|
||||||
uint32 Total packet size (sum of TAD packet and TAV packet size)
|
uint32 Total packet size (sum of TAD packet and TAV packet size)
|
||||||
uint32 CRC-32 of [sync pattern + framerate + flags + reserved + total packet size]
|
|
||||||
uint64 Timecode in nanoseconds
|
uint64 Timecode in nanoseconds
|
||||||
uint32 Offset to video packet
|
uint32 Offset to video packet
|
||||||
|
uint32 CRC-32 of [sync pattern + framerate + flags + reserved + total packet size + timecode + offset]
|
||||||
<packet header end; encoded with rate 1/2 LDPC>
|
<packet header end; encoded with rate 1/2 LDPC>
|
||||||
bytes TAD with forward error correction
|
bytes TAD with forward error correction
|
||||||
<TAD header start>
|
<TAD header start>
|
||||||
@@ -1688,7 +1688,24 @@ start of the next packet
|
|||||||
6. If they match, sync to the stream; if not, find a next sync pattern
|
6. If they match, sync to the stream; if not, find a next sync pattern
|
||||||
7. "Offset to video packet" and the actual length of the TAD packet can be used together to recover video packet when stream is damaged, using the fact that in error-free stream, length of TAD packet is equal to "Offset to video packet", and the internal packet order is always audio-then-video
|
7. "Offset to video packet" and the actual length of the TAD packet can be used together to recover video packet when stream is damaged, using the fact that in error-free stream, length of TAD packet is equal to "Offset to video packet", and the internal packet order is always audio-then-video
|
||||||
|
|
||||||
The decoder "may" try to sync to the sync pattern that appears damaged when its contents are seem to be intact.
|
## Soft Sync Recovery
|
||||||
|
|
||||||
|
The decoder MAY try to sync to the sync pattern that appears damaged when its contents are seem to be intact, under the following strategies.
|
||||||
|
|
||||||
|
### Stage 1
|
||||||
|
|
||||||
|
On the stream position where the sync pattern is supposed to be:
|
||||||
|
|
||||||
|
1. Substitute damaged sync pattern with known sync pattern (videos are not allowed to change NTSC/PAL mode mid-stream, so there's only one known value)
|
||||||
|
2. Zero-fill the reserved area if haven't already
|
||||||
|
3. Re-calculate CRC. If match, sync. If not, head to the next stage
|
||||||
|
|
||||||
|
### Stage 2
|
||||||
|
|
||||||
|
1. Further substitute the framerate, flags, timecode to the last known value (as these values rarely change mid-stream; timecode must be incremented appropriately. e.g. FPS=16, last known timecode=5.0, packets missed so far=4, then assumed timecode is 5.0 + 4 + 1 = 10.0)
|
||||||
|
2. Re-calculate CRC. If match, sync. If not, soft sync recovery is failed, and discard the packet
|
||||||
|
|
||||||
|
Note: If CRC is unmatched, the packet MUST be discarded, as the header content cannot be trusted if all soft recovery stages have failed
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ LIBTADENC_OBJ = lib/libtadenc/encoder_tad.o
|
|||||||
LIBTADDEC_OBJ = lib/libtaddec/decoder_tad.o
|
LIBTADDEC_OBJ = lib/libtaddec/decoder_tad.o
|
||||||
|
|
||||||
# libfec - Forward Error Correction library (LDPC + Reed-Solomon)
|
# 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
|
# 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)
|
# TAV-DT noise injector (channel simulator)
|
||||||
tavdt_noise_injector: tavdt_noise_injector.c
|
tavdt_noise_injector: tavdt_noise_injector.c
|
||||||
rm -f tavdt_noise_injector
|
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 ""
|
||||||
@echo "TAV-DT noise injector built: tavdt_noise_injector"
|
@echo "TAV-DT noise injector built: tavdt_noise_injector"
|
||||||
@echo "Simulates QPSK satellite channel noise (AWGN + burst)"
|
@echo "Simulates QPSK satellite channel noise (AWGN + burst)"
|
||||||
@@ -180,7 +180,7 @@ install: $(TARGETS)
|
|||||||
# Check for required dependencies
|
# Check for required dependencies
|
||||||
check-deps:
|
check-deps:
|
||||||
@echo "Checking dependencies..."
|
@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."
|
@echo "All dependencies found."
|
||||||
|
|
||||||
# Help
|
# Help
|
||||||
|
|||||||
@@ -9,15 +9,19 @@
|
|||||||
* - Mandatory TAD audio
|
* - Mandatory TAD audio
|
||||||
* - LDPC rate 1/2 for headers, Reed-Solomon (255,223) for payloads
|
* - 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: 24 bytes → 48 bytes LDPC encoded
|
* - 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
|
* - TAD subpacket: header (10→20 bytes LDPC) + RS-encoded payload
|
||||||
* - TAV subpacket: header (8→16 bytes LDPC) + RS-encoded payload
|
* - TAV subpacket: header (8→16 bytes LDPC) + RS-encoded payload
|
||||||
* - No packet type bytes - always audio then video
|
* - 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.
|
* 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
|
#define _POSIX_C_SOURCE 200809L
|
||||||
@@ -217,6 +221,13 @@ typedef struct {
|
|||||||
uint64_t last_timecode_ns; // Last processed timecode
|
uint64_t last_timecode_ns; // Last processed timecode
|
||||||
uint64_t audio_samples_written; // Total audio samples written
|
uint64_t audio_samples_written; // Total audio samples written
|
||||||
uint64_t video_frames_written; // Total video frames written (for sync check)
|
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;
|
} dt_decoder_t;
|
||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
@@ -419,11 +430,97 @@ typedef struct {
|
|||||||
uint8_t flags;
|
uint8_t flags;
|
||||||
uint16_t reserved;
|
uint16_t reserved;
|
||||||
uint32_t packet_size;
|
uint32_t packet_size;
|
||||||
uint32_t crc32;
|
uint64_t timecode_ns; // Now at offset 12 (moved before CRC)
|
||||||
uint64_t timecode_ns;
|
uint32_t offset_to_video; // Now at offset 20 (moved before CRC)
|
||||||
uint32_t offset_to_video;
|
uint32_t crc32; // Now at offset 24 (last field)
|
||||||
} dt_packet_header_t;
|
} 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) {
|
static int read_and_decode_header(dt_decoder_t *dec, dt_packet_header_t *header) {
|
||||||
// Read LDPC-encoded header (56 bytes = 28 bytes * 2)
|
// Read LDPC-encoded header (56 bytes = 28 bytes * 2)
|
||||||
uint8_t encoded_header[DT_MAIN_HEADER_SIZE * 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++;
|
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) |
|
header->sync_pattern = ((uint32_t)decoded_header[0] << 24) | ((uint32_t)decoded_header[1] << 16) |
|
||||||
((uint32_t)decoded_header[2] << 8) | decoded_header[3];
|
((uint32_t)decoded_header[2] << 8) | decoded_header[3];
|
||||||
header->framerate = decoded_header[4];
|
header->framerate = decoded_header[4];
|
||||||
header->flags = decoded_header[5];
|
header->flags = decoded_header[5];
|
||||||
header->reserved = decoded_header[6] | ((uint16_t)decoded_header[7] << 8);
|
header->reserved = decoded_header[6] | ((uint16_t)decoded_header[7] << 8);
|
||||||
memcpy(&header->packet_size, decoded_header + 8, 4);
|
memcpy(&header->packet_size, decoded_header + 8, 4);
|
||||||
memcpy(&header->crc32, decoded_header + 12, 4);
|
memcpy(&header->timecode_ns, decoded_header + 12, 8); // Now at offset 12
|
||||||
memcpy(&header->timecode_ns, decoded_header + 16, 8);
|
memcpy(&header->offset_to_video, decoded_header + 20, 4); // Now at offset 20
|
||||||
memcpy(&header->offset_to_video, decoded_header + 24, 4);
|
memcpy(&header->crc32, decoded_header + 24, 4); // Now at offset 24
|
||||||
|
|
||||||
// Verify sync pattern
|
// Verify sync pattern
|
||||||
if (header->sync_pattern != TAV_DT_SYNC_NTSC && header->sync_pattern != TAV_DT_SYNC_PAL) {
|
int sync_valid = (header->sync_pattern == TAV_DT_SYNC_NTSC || header->sync_pattern == TAV_DT_SYNC_PAL);
|
||||||
if (dec->verbose) {
|
if (!sync_valid && dec->verbose) {
|
||||||
fprintf(stderr, "Warning: Invalid sync pattern 0x%08X\n", header->sync_pattern);
|
fprintf(stderr, "Warning: Invalid sync pattern 0x%08X\n", header->sync_pattern);
|
||||||
}
|
|
||||||
dec->sync_losses++;
|
|
||||||
return -2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify CRC-32 (covers first 12 bytes: sync + fps + flags + reserved + size)
|
// Verify CRC-32 (covers bytes 0-23: sync + fps + flags + reserved + size + timecode + offset)
|
||||||
uint32_t calculated_crc = calculate_crc32(decoded_header, 12);
|
uint32_t calculated_crc = calculate_crc32(decoded_header, 24);
|
||||||
if (calculated_crc != header->crc32) {
|
int crc_valid = (calculated_crc == header->crc32);
|
||||||
|
|
||||||
|
if (!crc_valid) {
|
||||||
if (dec->verbose) {
|
if (dec->verbose) {
|
||||||
fprintf(stderr, "Warning: CRC mismatch (expected 0x%08X, got 0x%08X)\n",
|
fprintf(stderr, "Warning: CRC mismatch (expected 0x%08X, got 0x%08X)\n",
|
||||||
header->crc32, calculated_crc);
|
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
|
// Update decoder state from first packet
|
||||||
if (dec->packets_processed == 0) {
|
if (dec->packets_processed == 0) {
|
||||||
dec->width = DT_WIDTH;
|
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(" Bytes read: %lu\n", dec->bytes_read);
|
||||||
printf(" FEC corrections: %lu\n", dec->fec_corrections);
|
printf(" FEC corrections: %lu\n", dec->fec_corrections);
|
||||||
printf(" CRC errors: %lu\n", dec->crc_errors);
|
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);
|
printf(" Sync losses: %lu\n", dec->sync_losses);
|
||||||
|
|
||||||
// Mux output files
|
// Mux output files
|
||||||
|
|||||||
@@ -9,15 +9,16 @@
|
|||||||
* - Mandatory TAD audio
|
* - Mandatory TAD audio
|
||||||
* - LDPC rate 1/2 for headers, Reed-Solomon (255,223) for payloads
|
* - 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
|
* - 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
|
* - TAD subpacket: header (10->20 bytes LDPC) + RS-encoded payload
|
||||||
* - TAV subpacket: header (8->16 bytes LDPC) + RS-encoded payload
|
* - TAV subpacket: header (8->16 bytes LDPC) + RS-encoded payload
|
||||||
* - No packet type bytes - always audio then video
|
* - No packet type bytes - always audio then video
|
||||||
*
|
*
|
||||||
* Created by CuriousTorvald and Claude on 2025-12-09.
|
* 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
|
#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;
|
uint32_t packet_size = tad_subpacket_size + tav_subpacket_size;
|
||||||
|
|
||||||
// Build main header (28 bytes)
|
// 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];
|
uint8_t header[DT_MAIN_HEADER_SIZE];
|
||||||
// Write sync pattern in big-endian (network byte order)
|
// Write sync pattern in big-endian (network byte order)
|
||||||
uint32_t sync = enc->is_pal ? TAV_DT_SYNC_PAL : TAV_DT_SYNC_NTSC;
|
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)
|
// Packet size (4 bytes)
|
||||||
memcpy(header + 8, &packet_size, 4);
|
memcpy(header + 8, &packet_size, 4);
|
||||||
|
|
||||||
// CRC placeholder (will be calculated over header bytes 0-11)
|
// Timecode (8 bytes) - now at offset 12
|
||||||
uint32_t crc = calculate_crc32(header, 12);
|
memcpy(header + 12, &timecode_ns, 8);
|
||||||
memcpy(header + 12, &crc, 4);
|
|
||||||
|
|
||||||
// Timecode (8 bytes)
|
// Offset to video (4 bytes) - now at offset 20
|
||||||
memcpy(header + 16, &timecode_ns, 8);
|
memcpy(header + 20, &offset_to_video, 4);
|
||||||
|
|
||||||
// Offset to video (4 bytes)
|
// CRC-32 (4 bytes) - calculated over bytes 0-23 (sync + fps + flags + reserved + size + timecode + offset)
|
||||||
memcpy(header + 24, &offset_to_video, 4);
|
uint32_t crc = calculate_crc32(header, 24);
|
||||||
|
memcpy(header + 24, &crc, 4);
|
||||||
|
|
||||||
// LDPC encode main header
|
// LDPC encode main header
|
||||||
uint8_t ldpc_header[DT_MAIN_HEADER_SIZE * 2];
|
uint8_t ldpc_header[DT_MAIN_HEADER_SIZE * 2];
|
||||||
|
|||||||
Reference in New Issue
Block a user