working TAV-DT decoder

This commit is contained in:
minjaesong
2025-12-02 14:13:57 +09:00
parent 196b9a0c01
commit 046fa98025
5 changed files with 1018 additions and 93 deletions

View File

@@ -6,6 +6,7 @@ CXX = g++
CFLAGS = -std=c99 -Wall -Wextra -Ofast -D_GNU_SOURCE -march=native -mavx512f -mavx512dq -mavx512bw -mavx512vl
CXXFLAGS = -std=c++11 -Wall -Wextra -Ofast -D_GNU_SOURCE -march=native -mavx512f -mavx512dq -mavx512bw -mavx512vl
DBGFLAGS =
PREFIX = /usr/local
# Zstd flags (use pkg-config if available, fallback for cross-platform compatibility)
ZSTD_CFLAGS = $(shell pkg-config --cflags libzstd 2>/dev/null || echo "")
@@ -17,7 +18,7 @@ OPENCV_CFLAGS = $(shell pkg-config --cflags opencv4)
OPENCV_LIBS = $(shell pkg-config --libs opencv4)
# Source files and targets
TARGETS = tev tav tav_decoder tav_inspector
TARGETS = tev tav tav_decoder tav_inspector tav_dt_decoder
TAD_TARGETS = encoder_tad decoder_tad
TEST_TARGETS = test_mesh_warp test_mesh_roundtrip
@@ -46,6 +47,13 @@ tav_inspector: tav_inspector.c
rm -f tav_inspector
$(CC) $(CFLAGS) $(ZSTD_CFLAGS) -o tav_inspector $< $(LIBS)
tav_dt_decoder: decoder_tav_dt.c decoder_tad.c decoder_tad.h tav_video_decoder.c tav_video_decoder.h
rm -f decoder_tav_dt decoder_tav_dt.o tav_video_decoder.o
$(CC) $(CFLAGS) $(ZSTD_CFLAGS) -DTAD_DECODER_LIB -c decoder_tad.c -o decoder_tad.o
$(CC) $(CFLAGS) $(ZSTD_CFLAGS) -c tav_video_decoder.c -o tav_video_decoder.o
$(CC) $(CFLAGS) $(ZSTD_CFLAGS) -c decoder_tav_dt.c -o decoder_tav_dt.o
$(CC) $(DBGFLAGS) -o decoder_tav_dt decoder_tav_dt.o decoder_tad.o tav_video_decoder.o $(LIBS)
# Build TAD (Terrarum Advanced Audio) tools
encoder_tad: encoder_tad_standalone.c encoder_tad.c encoder_tad.h
rm -f encoder_tad encoder_tad_standalone.o encoder_tad.o
@@ -91,11 +99,12 @@ clean:
# Install (copy to PATH)
install: $(TARGETS) $(TAD_TARGETS)
cp encoder_tev /usr/local/bin/
cp encoder_tav /usr/local/bin/
cp decoder_tav /usr/local/bin/
cp encoder_tad /usr/local/bin/
cp decoder_tad /usr/local/bin/
cp encoder_tev $(PREFIX)/bin/
cp encoder_tav $(PREFIX)/bin/
cp decoder_tav $(PREFIX)/bin/
cp encoder_tad $(PREFIX)/bin/
cp decoder_tad $(PREFIX)/bin/
cp decoder_tav_dt $(PREFIX)/bin/
# Check for required dependencies
check-deps:

View File

@@ -0,0 +1,805 @@
// Created by CuriousTorvald and Claude on 2025-12-02.
// TAV-DT (Digital Tape) Decoder - Headerless streaming format decoder
// Decodes TAV-DT packets to video (FFV1/rawvideo) and audio (PCMu8)
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
#include <getopt.h>
#include "decoder_tad.h" // Shared TAD decoder library
#include "tav_video_decoder.h" // Shared TAV video decoder library
#define DECODER_VENDOR_STRING "Decoder-TAV-DT 20251202"
// TAV-DT sync patterns (big endian)
#define TAV_DT_SYNC_NTSC 0xE3537A1F // 720x480
#define TAV_DT_SYNC_PAL 0xD193A745 // 720x576
// Standard TAV quality arrays (0-5, must match encoder)
static const int QUALITY_Y[] = {79, 47, 23, 11, 5, 2, 0};
static const int QUALITY_CO[] = {123, 108, 91, 76, 59, 29, 3};
static const int QUALITY_CG[] = {148, 133, 113, 99, 76, 39, 5};
// TAV-DT packet types (reused from TAV)
#define TAV_PACKET_IFRAME 0x10
#define TAV_PACKET_GOP_UNIFIED 0x12
#define TAV_PACKET_AUDIO_TAD 0x24
// CRC-32 table and functions
static uint32_t crc32_table[256];
static int crc32_table_initialized = 0;
static void init_crc32_table(void) {
if (crc32_table_initialized) return;
for (uint32_t i = 0; i < 256; i++) {
uint32_t crc = i;
for (int j = 0; j < 8; j++) {
if (crc & 1) {
crc = (crc >> 1) ^ 0xEDB88320;
} else {
crc >>= 1;
}
}
crc32_table[i] = crc;
}
crc32_table_initialized = 1;
}
static uint32_t calculate_crc32(const uint8_t *data, size_t length) {
init_crc32_table();
uint32_t crc = 0xFFFFFFFF;
for (size_t i = 0; i < length; i++) {
crc = (crc >> 8) ^ crc32_table[(crc ^ data[i]) & 0xFF];
}
return crc ^ 0xFFFFFFFF;
}
// DT packet header structure (16 bytes)
typedef struct {
uint32_t sync_pattern; // 0xE3537A1F (NTSC) or 0xD193A745 (PAL)
uint8_t framerate;
uint8_t flags; // bit 0=interlaced, bit 1=NTSC framerate, bits 4-7=quality index
uint16_t reserved;
uint32_t packet_size; // Size of data after header
uint32_t crc32; // CRC-32 of first 12 bytes
} dt_packet_header_t;
// Decoder state
typedef struct {
FILE *input_fp;
FILE *output_video_fp; // For packet dump mode
FILE *output_audio_fp;
// FFmpeg integration
pid_t ffmpeg_pid;
FILE *video_pipe; // Pipe to FFmpeg for RGB24 frames
char *audio_temp_file; // Temporary file for PCMu8 audio
// Video parameters (derived from sync pattern and quality index)
int width;
int height;
int framerate;
int is_interlaced;
int is_ntsc_framerate;
int quality_index;
// Video decoding context (uses shared library)
tav_video_context_t *video_ctx;
// Statistics
uint64_t packets_processed;
uint64_t frames_decoded;
uint64_t bytes_read;
uint64_t crc_errors;
uint64_t sync_losses;
// Options
int verbose;
int ffmpeg_output; // If 1, output to FFmpeg (FFV1/MKV), if 0, dump packets
} dt_decoder_t;
// Read DT packet header and verify
static int read_dt_header(dt_decoder_t *dec, dt_packet_header_t *header) {
uint8_t header_bytes[16];
// Read 16-byte header
size_t bytes_read = fread(header_bytes, 1, 16, dec->input_fp);
if (bytes_read < 16) {
if (bytes_read > 0) {
fprintf(stderr, "Warning: Incomplete header at end of file (%zu bytes)\n", bytes_read);
}
return -1; // EOF or incomplete
}
dec->bytes_read += 16;
// Parse header fields
header->sync_pattern = (header_bytes[0] << 24) | (header_bytes[1] << 16) |
(header_bytes[2] << 8) | header_bytes[3];
header->framerate = header_bytes[4];
header->flags = header_bytes[5];
header->reserved = header_bytes[6] | (header_bytes[7] << 8);
header->packet_size = header_bytes[8] | (header_bytes[9] << 8) |
(header_bytes[10] << 16) | (header_bytes[11] << 24);
header->crc32 = header_bytes[12] | (header_bytes[13] << 8) |
(header_bytes[14] << 16) | (header_bytes[15] << 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 at offset %lu\n",
header->sync_pattern, dec->bytes_read - 16);
}
dec->sync_losses++;
return -2; // Invalid sync
}
// Calculate and verify CRC-32 of first 12 bytes
uint32_t calculated_crc = calculate_crc32(header_bytes, 12);
if (calculated_crc != header->crc32) {
fprintf(stderr, "Warning: CRC mismatch at offset %lu (expected 0x%08X, got 0x%08X)\n",
dec->bytes_read - 16, header->crc32, calculated_crc);
dec->crc_errors++;
// Continue anyway - data might still be usable
}
// Update decoder state from header (first packet only)
if (dec->packets_processed == 0) {
dec->width = (header->sync_pattern == TAV_DT_SYNC_NTSC) ? 720 : 720;
dec->height = (header->sync_pattern == TAV_DT_SYNC_NTSC) ? 480 : 576;
dec->framerate = header->framerate;
dec->is_interlaced = header->flags & 0x01;
dec->is_ntsc_framerate = header->flags & 0x02;
dec->quality_index = (header->flags >> 4) & 0x0F;
if (dec->verbose) {
printf("=== TAV-DT Stream Info ===\n");
printf(" Format: %s %s\n",
(header->sync_pattern == TAV_DT_SYNC_NTSC) ? "NTSC" : "PAL",
dec->is_interlaced ? "interlaced" : "progressive");
printf(" Resolution: %dx%d\n", dec->width, dec->height);
printf(" Framerate: %d fps%s\n", dec->framerate,
dec->is_ntsc_framerate ? " (NTSC)" : "");
printf(" Quality index: %d\n", dec->quality_index);
printf("==========================\n\n");
}
}
return 0;
}
// Search for next sync pattern (for recovery from errors)
static int find_next_sync(dt_decoder_t *dec) {
uint8_t sync_bytes[4] = {0};
uint8_t byte;
// NTSC and PAL sync patterns as byte arrays (big endian)
const uint8_t ntsc_sync[4] = {0xE3, 0x53, 0x7A, 0x1F};
const uint8_t pal_sync[4] = {0xD1, 0x93, 0xA7, 0x45};
// Read first 4 bytes to initialize window
for (int i = 0; i < 4; i++) {
if (fread(&byte, 1, 1, dec->input_fp) != 1) {
return -1; // EOF
}
dec->bytes_read++;
sync_bytes[i] = byte;
}
// Check if we already have a sync pattern at current position
if (memcmp(sync_bytes, ntsc_sync, 4) == 0 || memcmp(sync_bytes, pal_sync, 4) == 0) {
// Rewind to start of sync pattern
fseek(dec->input_fp, -4, SEEK_CUR);
dec->bytes_read -= 4;
if (dec->verbose) {
printf("Found sync at offset %lu\n", dec->bytes_read);
}
return 0;
}
// Sliding window search
while (fread(&byte, 1, 1, dec->input_fp) == 1) {
dec->bytes_read++;
// Shift window
sync_bytes[0] = sync_bytes[1];
sync_bytes[1] = sync_bytes[2];
sync_bytes[2] = sync_bytes[3];
sync_bytes[3] = byte;
// Check NTSC sync
if (memcmp(sync_bytes, ntsc_sync, 4) == 0) {
// Rewind to start of sync pattern
fseek(dec->input_fp, -4, SEEK_CUR);
dec->bytes_read -= 4;
if (dec->verbose) {
printf("Found NTSC sync at offset %lu\n", dec->bytes_read);
}
return 0;
}
// Check PAL sync
if (memcmp(sync_bytes, pal_sync, 4) == 0) {
// Rewind to start of sync pattern
fseek(dec->input_fp, -4, SEEK_CUR);
dec->bytes_read -= 4;
if (dec->verbose) {
printf("Found PAL sync at offset %lu\n", dec->bytes_read);
}
return 0;
}
}
return -1; // EOF without finding sync
}
// Spawn FFmpeg process for video/audio muxing
static int spawn_ffmpeg(dt_decoder_t *dec, const char *output_file) {
int video_pipe_fd[2];
// Create pipe for video data
if (pipe(video_pipe_fd) < 0) {
fprintf(stderr, "Error: Failed to create video pipe\n");
return -1;
}
// Fork FFmpeg process
dec->ffmpeg_pid = fork();
if (dec->ffmpeg_pid < 0) {
fprintf(stderr, "Error: Failed to fork FFmpeg process\n");
close(video_pipe_fd[0]);
close(video_pipe_fd[1]);
return -1;
}
if (dec->ffmpeg_pid == 0) {
// Child process - execute FFmpeg
close(video_pipe_fd[1]); // Close write end
char video_size[32];
char framerate[16];
snprintf(video_size, sizeof(video_size), "%dx%d", dec->width, dec->height);
snprintf(framerate, sizeof(framerate), "%d", dec->framerate);
// Redirect video pipe to fd 3
dup2(video_pipe_fd[0], 3);
close(video_pipe_fd[0]);
execl("/usr/bin/ffmpeg", "ffmpeg",
"-f", "rawvideo",
"-pixel_format", "rgb24",
"-video_size", video_size,
"-framerate", framerate,
"-i", "pipe:3", // Video from fd 3
"-f", "u8", // Raw unsigned 8-bit PCM
"-ar", "32000", // 32 KHz sample rate
"-ac", "2", // Stereo
"-i", dec->audio_temp_file, // Audio from temp file
"-color_range", "2",
"-c:v", "ffv1", // FFV1 codec
"-level", "3", // FFV1 level 3
"-coder", "1", // Range coder
"-context", "1", // Large context
"-g", "1", // GOP size 1 (all I-frames)
"-slices", "24", // 24 slices for threading
"-slicecrc", "1", // CRC per slice
"-pixel_format", "rgb24",
"-color_range", "2",
"-c:a", "pcm_u8", // Audio codec (PCM unsigned 8-bit)
"-f", "matroska", // MKV container
output_file,
"-y", // Overwrite output
"-v", "warning", // Minimal logging
(char*)NULL);
fprintf(stderr, "Error: Failed to execute FFmpeg\n");
exit(1);
} else {
// Parent process
close(video_pipe_fd[0]); // Close read end
dec->video_pipe = fdopen(video_pipe_fd[1], "wb");
if (!dec->video_pipe) {
fprintf(stderr, "Error: Failed to open video pipe for writing\n");
kill(dec->ffmpeg_pid, SIGTERM);
return -1;
}
}
return 0;
}
// Process single DT packet
static int process_dt_packet(dt_decoder_t *dec) {
dt_packet_header_t header;
// Read and verify header
int result = read_dt_header(dec, &header);
if (result == -1) {
return -1; // EOF
} else if (result == -2) {
// Invalid sync - try to recover (sync search always enabled)
if (find_next_sync(dec) == 0) {
// Found sync, try again
return process_dt_packet(dec);
}
return -2; // Unrecoverable sync loss
}
// Allocate buffer for packet data
uint8_t *packet_data = malloc(header.packet_size);
if (!packet_data) {
fprintf(stderr, "Error: Failed to allocate %u bytes for packet data\n", header.packet_size);
return -3;
}
// Read packet data
size_t bytes_read = fread(packet_data, 1, header.packet_size, dec->input_fp);
if (bytes_read < header.packet_size) {
fprintf(stderr, "Error: Incomplete packet data (%zu/%u bytes)\n", bytes_read, header.packet_size);
free(packet_data);
return -4;
}
dec->bytes_read += bytes_read;
// Parse packet contents:
// 1. Timecode (8 bytes, no header)
// 2. TAD audio packet(s) (full packet with 0x24 header)
// 3. TAV video packet (full packet with 0x10 or 0x12 header)
size_t offset = 0;
// 1. Read timecode (8 bytes)
if (offset + 8 > header.packet_size) {
fprintf(stderr, "Error: Packet too small for timecode\n");
free(packet_data);
return -5;
}
uint64_t timecode_ns = 0;
for (int i = 0; i < 8; i++) {
timecode_ns |= ((uint64_t)packet_data[offset + i]) << (i * 8);
}
offset += 8;
if (dec->verbose && dec->packets_processed % 100 == 0) {
double timecode_sec = timecode_ns / 1000000000.0;
printf("Packet %lu: timecode=%.3fs, size=%u bytes\n",
dec->packets_processed, timecode_sec, header.packet_size);
}
// 2. Process TAD audio packet(s)
while (offset < header.packet_size && packet_data[offset] == TAV_PACKET_AUDIO_TAD) {
offset++; // Skip packet type byte (0x24)
// Parse TAD packet format: [sample_count(2)][payload_size+7(4)][sample_count(2)][quant_index(1)][compressed_size(4)][compressed_data]
if (offset + 6 > header.packet_size) break;
uint16_t sample_count = packet_data[offset] | (packet_data[offset+1] << 8);
offset += 2;
uint32_t payload_size_plus_7 = packet_data[offset] | (packet_data[offset+1] << 8) |
(packet_data[offset+2] << 16) | (packet_data[offset+3] << 24);
offset += 4;
// Total TAD packet content size (everything after the payload_size_plus_7 field)
uint32_t tad_content_size = payload_size_plus_7;
// TAD packet data (sample_count repeat + quant_index + compressed_size + compressed_data)
if (offset + tad_content_size > header.packet_size) {
fprintf(stderr, "Warning: TAD packet extends beyond DT packet boundary (offset=%zu, content=%u, packet_size=%u)\n",
offset, tad_content_size, header.packet_size);
break;
}
// The TAD decoder expects: [sample_count(2)][quant_index(1)][compressed_size(4)][compressed_data]
// This is exactly what we have starting at the current offset (the repeated sample_count field)
// Peek at the TAD packet structure for verbose output
uint16_t sample_count_repeat = packet_data[offset] | (packet_data[offset+1] << 8);
uint8_t quant_index = packet_data[offset + 2];
uint32_t compressed_size = packet_data[offset+3] | (packet_data[offset+4] << 8) |
(packet_data[offset+5] << 16) | (packet_data[offset+6] << 24);
if (dec->verbose) {
printf(" TAD: samples=%u, quant=%u, compressed=%u bytes\n",
sample_count, quant_index, compressed_size);
}
// Decode TAD audio using shared decoder
// Allocate output buffer (max chunk size * 2 channels)
uint8_t *pcm_output = malloc(65536 * 2); // Max chunk size for TAD
if (!pcm_output) {
fprintf(stderr, "Error: Failed to allocate audio decode buffer\n");
offset += tad_content_size;
continue;
}
size_t bytes_consumed = 0;
size_t samples_decoded = 0;
// Pass the TAD data starting from repeated sample_count
// The decoder expects: [sample_count(2)][quant(1)][payload_size(4)][compressed_data]
int decode_result = tad32_decode_chunk(packet_data + offset, tad_content_size,
pcm_output, &bytes_consumed, &samples_decoded);
if (decode_result == 0) {
// Write PCMu8 to output (samples * 2 channels)
if (dec->output_audio_fp) {
fwrite(pcm_output, 1, samples_decoded * 2, dec->output_audio_fp);
}
} else {
fprintf(stderr, "Warning: TAD decode failed at offset %zu\n", offset);
}
free(pcm_output);
offset += tad_content_size;
}
// 3. Process TAV video packet
if (offset < header.packet_size) {
uint8_t packet_type = packet_data[offset];
offset++; // Skip packet type byte
if (packet_type == TAV_PACKET_GOP_UNIFIED) {
// Read GOP_UNIFIED packet structure: [gop_size(1)][compressed_size(4)][compressed_data]
if (offset + 5 > header.packet_size) {
fprintf(stderr, "Warning: Incomplete GOP packet header\n");
free(packet_data);
return 0;
}
uint8_t gop_size = packet_data[offset];
offset++;
uint32_t compressed_size = packet_data[offset] | (packet_data[offset+1] << 8) |
(packet_data[offset+2] << 16) | (packet_data[offset+3] << 24);
offset += 4;
if (dec->verbose) {
printf(" Video packet: GOP_UNIFIED, %u frames, %u bytes compressed\n",
gop_size, compressed_size);
}
if (offset + compressed_size > header.packet_size) {
fprintf(stderr, "Warning: GOP data extends beyond packet boundary\n");
free(packet_data);
return 0;
}
// Allocate frame buffers for GOP
uint8_t **rgb_frames = malloc(gop_size * sizeof(uint8_t*));
for (int i = 0; i < gop_size; i++) {
rgb_frames[i] = malloc(dec->width * dec->height * 3);
}
// Decode GOP using shared library
int decode_result = tav_video_decode_gop(dec->video_ctx,
packet_data + offset, compressed_size,
gop_size, rgb_frames);
if (decode_result == 0) {
// Write frames to FFmpeg or dump file
for (int i = 0; i < gop_size; i++) {
if (dec->video_pipe) {
// Write RGB24 frame to FFmpeg
fwrite(rgb_frames[i], 1, dec->width * dec->height * 3, dec->video_pipe);
} else if (dec->output_video_fp) {
// Packet dump mode - write raw packet
if (i == 0) { // Only write packet once
fwrite(&packet_type, 1, 1, dec->output_video_fp);
fwrite(&gop_size, 1, 1, dec->output_video_fp);
fwrite(&compressed_size, 4, 1, dec->output_video_fp);
fwrite(packet_data + offset, 1, compressed_size, dec->output_video_fp);
}
}
}
dec->frames_decoded += gop_size;
} else {
fprintf(stderr, "Warning: GOP decode failed: %s\n",
tav_video_get_error(dec->video_ctx));
}
// Free frame buffers
for (int i = 0; i < gop_size; i++) {
free(rgb_frames[i]);
}
free(rgb_frames);
} else if (packet_type == TAV_PACKET_IFRAME) {
// I-frame packet - for packet dump mode
if (dec->output_video_fp) {
fwrite(&packet_type, 1, 1, dec->output_video_fp);
fwrite(packet_data + offset, 1, header.packet_size - offset, dec->output_video_fp);
}
dec->frames_decoded++;
}
}
free(packet_data);
dec->packets_processed++;
return 0;
}
static void show_usage(const char *prog_name) {
printf("Usage: %s [options] -i input.tav -o output.mkv\n\n", prog_name);
printf("TAV-DT Decoder - Headerless streaming format decoder\n\n");
printf("Options:\n");
printf(" -i, --input FILE Input TAV-DT file (required)\n");
printf(" -o, --output FILE Output MKV file (default: input with .mkv extension)\n");
printf(" -v, --verbose Verbose output\n");
printf(" -h, --help Show this help\n\n");
printf("Notes:\n");
printf(" - Audio is decoded to temporary file in /tmp/\n");
printf(" - Sync pattern searching is always enabled\n\n");
printf("Example:\n");
printf(" %s -i stream.tavdt # Creates stream.mkv\n", prog_name);
printf(" %s -i stream.tavdt -o out.mkv # Creates out.mkv\n\n", prog_name);
}
int main(int argc, char *argv[]) {
dt_decoder_t decoder = {0};
char *input_file = NULL;
char *output_file = NULL;
// Parse command line options
static struct option long_options[] = {
{"input", required_argument, 0, 'i'},
{"output", required_argument, 0, 'o'},
{"verbose", no_argument, 0, 'v'},
{"help", no_argument, 0, 'h'},
{0, 0, 0, 0}
};
int opt;
while ((opt = getopt_long(argc, argv, "i:o:vh", long_options, NULL)) != -1) {
switch (opt) {
case 'i':
input_file = optarg;
break;
case 'o':
output_file = optarg;
break;
case 'v':
decoder.verbose = 1;
break;
case 'h':
show_usage(argv[0]);
return 0;
default:
show_usage(argv[0]);
return 1;
}
}
if (!input_file) {
fprintf(stderr, "Error: Input file must be specified\n");
show_usage(argv[0]);
return 1;
}
// Generate output filename if not provided
if (!output_file) {
size_t input_len = strlen(input_file);
output_file = malloc(input_len + 32); // Extra space for extension
// Find the last directory separator
const char *basename_start = strrchr(input_file, '/');
if (!basename_start) basename_start = strrchr(input_file, '\\');
basename_start = basename_start ? basename_start + 1 : input_file;
// Copy directory part
size_t dir_len = basename_start - input_file;
strncpy(output_file, input_file, dir_len);
// Find the extension
const char *ext = strrchr(basename_start, '.');
if (ext && (strcmp(ext, ".tavdt") == 0 || strcmp(ext, ".tav") == 0 || strcmp(ext, ".dt") == 0)) {
// Copy basename without extension
size_t name_len = ext - basename_start;
strncpy(output_file + dir_len, basename_start, name_len);
output_file[dir_len + name_len] = '\0';
} else {
// No recognized extension, copy entire basename
strcpy(output_file + dir_len, basename_start);
}
// Append .mkv extension
strcat(output_file, ".mkv");
if (decoder.verbose) {
printf("Auto-generated output path: %s\n", output_file);
}
}
// Open input file
decoder.input_fp = fopen(input_file, "rb");
if (!decoder.input_fp) {
fprintf(stderr, "Error: Cannot open input file: %s\n", input_file);
return 1;
}
// Determine output mode based on file extension
int output_is_mkv = (strstr(output_file, ".mkv") != NULL || strstr(output_file, ".MKV") != NULL);
decoder.ffmpeg_output = output_is_mkv;
// Create temporary audio file in /tmp/ (using process ID for uniqueness)
char temp_audio_file[256];
snprintf(temp_audio_file, sizeof(temp_audio_file), "/tmp/tav_dt_audio_%d.pcm", getpid());
decoder.audio_temp_file = strdup(temp_audio_file);
// Open audio output file
decoder.output_audio_fp = fopen(decoder.audio_temp_file, "wb");
if (!decoder.output_audio_fp) {
fprintf(stderr, "Error: Cannot open temporary audio file: %s\n", decoder.audio_temp_file);
fclose(decoder.input_fp);
return 1;
}
// In packet dump mode, open video packet file
char video_packets_file[256];
if (!decoder.ffmpeg_output) {
snprintf(video_packets_file, sizeof(video_packets_file), "%s.packets", output_file);
decoder.output_video_fp = fopen(video_packets_file, "wb");
}
printf("TAV-DT Decoder - %s\n", DECODER_VENDOR_STRING);
printf("Input: %s\n", input_file);
if (decoder.ffmpeg_output) {
printf("Output: %s (FFV1/MKV)\n", output_file);
} else {
printf("Output video: %s (packet dump)\n", video_packets_file);
}
printf("\n");
// Find first sync pattern (works even when sync is at offset 0)
if (decoder.verbose) {
printf("Searching for first sync pattern...\n");
}
if (find_next_sync(&decoder) != 0) {
fprintf(stderr, "Error: No sync pattern found in file\n");
fclose(decoder.input_fp);
fclose(decoder.output_audio_fp);
if (decoder.output_video_fp) fclose(decoder.output_video_fp);
return 1;
}
// Read first DT packet header to get video parameters (without processing content)
dt_packet_header_t first_header;
if (read_dt_header(&decoder, &first_header) != 0) {
fprintf(stderr, "Error: Failed to read first packet header\n");
fclose(decoder.input_fp);
fclose(decoder.output_audio_fp);
if (decoder.output_video_fp) fclose(decoder.output_video_fp);
return 1;
}
// Rewind to start of header so process_dt_packet() can read it again
fseek(decoder.input_fp, -16, SEEK_CUR);
// Validate quality index (0-5)
if (decoder.quality_index > 5) {
fprintf(stderr, "Warning: Quality index %d out of range (0-5), clamping to 5\n", decoder.quality_index);
decoder.quality_index = 5;
}
// Map quality index to actual quantiser values using standard TAV arrays
uint16_t quant_y = QUALITY_Y[decoder.quality_index];
uint16_t quant_co = QUALITY_CO[decoder.quality_index];
uint16_t quant_cg = QUALITY_CG[decoder.quality_index];
// Initialize video decoder with TAV-DT fixed parameters
tav_video_params_t video_params = {
.width = decoder.width,
.height = decoder.height,
.decomp_levels = 4, // TAV-DT fixed: 4 spatial levels
.temporal_levels = 2, // TAV-DT fixed: 2 temporal levels
.wavelet_filter = 1, // TAV-DT fixed: CDF 9/7
.temporal_wavelet = 1, // TAV-DT fixed: CDF 5/3 (NOT Haar!)
.entropy_coder = 1, // TAV-DT fixed: EZBC
.channel_layout = 0, // TAV-DT fixed: YCoCg-R
.perceptual_tuning = 1, // TAV-DT fixed: Perceptual
.quantiser_y = (uint8_t)quant_y, // From DT quality map
.quantiser_co = (uint8_t)quant_co,
.quantiser_cg = (uint8_t)quant_cg,
.encoder_preset = 0, // No special presets
.monoblock = 1 // TAV-DT fixed: Single tile
};
decoder.video_ctx = tav_video_create(&video_params);
if (!decoder.video_ctx) {
fprintf(stderr, "Error: Failed to create video decoder context\n");
fclose(decoder.input_fp);
fclose(decoder.output_audio_fp);
if (decoder.output_video_fp) fclose(decoder.output_video_fp);
return 1;
}
tav_video_set_verbose(decoder.video_ctx, decoder.verbose);
int result;
// In MKV mode, use two-pass approach:
// Pass 1: Extract all audio (video_pipe is NULL)
// Pass 2: Spawn FFmpeg and decode all video (audio file is complete)
if (decoder.ffmpeg_output) {
// Save starting position
long start_pos = ftell(decoder.input_fp);
// Pass 1: Process all packets for audio only
if (decoder.verbose) {
printf("\n=== Pass 1: Extracting audio ===\n");
}
while ((result = process_dt_packet(&decoder)) == 0) {
// Continue processing (only audio is written)
}
// Close and flush audio file
fclose(decoder.output_audio_fp);
decoder.output_audio_fp = NULL;
// Spawn FFmpeg with complete audio file
if (spawn_ffmpeg(&decoder, output_file) != 0) {
fprintf(stderr, "Error: Failed to spawn FFmpeg process\n");
tav_video_free(decoder.video_ctx);
fclose(decoder.input_fp);
return 1;
}
// Pass 2: Rewind and process all packets for video
if (decoder.verbose) {
printf("\n=== Pass 2: Decoding video ===\n");
}
fseek(decoder.input_fp, start_pos, SEEK_SET);
decoder.packets_processed = 0; // Reset statistics
decoder.frames_decoded = 0;
decoder.bytes_read = 0;
while ((result = process_dt_packet(&decoder)) == 0) {
// Continue processing (only video is written)
}
} else {
// Dump mode: Single pass for both audio and video
while ((result = process_dt_packet(&decoder)) == 0) {
// Continue processing
}
}
// Cleanup
if (decoder.video_pipe) {
fclose(decoder.video_pipe);
waitpid(decoder.ffmpeg_pid, NULL, 0);
}
if (decoder.video_ctx) {
tav_video_free(decoder.video_ctx);
}
fclose(decoder.input_fp);
if (decoder.output_audio_fp) fclose(decoder.output_audio_fp);
if (decoder.output_video_fp) fclose(decoder.output_video_fp);
// Clean up temporary audio file
if (decoder.audio_temp_file) {
unlink(decoder.audio_temp_file);
free(decoder.audio_temp_file);
}
// Print statistics
printf("\n=== Decoding Complete ===\n");
printf(" Packets processed: %lu\n", decoder.packets_processed);
printf(" Frames decoded: %lu (estimate)\n", decoder.frames_decoded);
printf(" Bytes read: %lu\n", decoder.bytes_read);
printf(" CRC errors: %lu\n", decoder.crc_errors);
printf(" Sync losses: %lu\n", decoder.sync_losses);
printf("=========================\n");
return 0;
}

View File

@@ -71,31 +71,6 @@
#define TAV_DT_SYNC_NTSC 0xE3537A1F // NTSC dimension (720x480)
#define TAV_DT_SYNC_PAL 0xD193A745 // PAL dimension (720x576)
// TAV-DT quality index mapping table (16 levels)
// Each entry: {Y_quantiser, Co_quantiser, Cg_quantiser, TAD_max_index}
// Note: These are actual quantizer values, not QLUT indices
static const struct {
uint16_t y, co, cg;
uint8_t tad_quality;
} DT_QUALITY_MAP[16] = {
{60, 104, 312, 23}, // Q0
{44, 88, 248, 31}, // Q1
{36, 72, 188, 39}, // Q2
{28, 56, 136, 47}, // Q3
{20, 48, 106, 55}, // Q4
{16, 40, 80, 63}, // Q5
{12, 36, 68, 71}, // Q6
{10, 32, 58, 79}, // Q7
{8, 28, 48, 87}, // Q8
{6, 24, 38, 95}, // Q9
{5, 20, 30, 103}, // Q10
{4, 16, 22, 111}, // Q11
{3, 12, 16, 119}, // Q12
{2, 8, 10, 123}, // Q13
{2, 5, 6, 127}, // Q14
{1, 2, 2, 127} // Q15
};
// TAD (Terrarum Advanced Audio) settings
// TAD32 constants (updated to match Float32 version)
#define TAD32_MIN_CHUNK_SIZE 1024 // Minimum: 1024 samples
@@ -210,7 +185,7 @@ typedef struct subtitle_entry {
typedef struct frame_analysis {
int frame_number;
// Wavelet-based metrics (3-level Haar on subsampled frame)
// Wavelet-based metrics (2-level Haar on subsampled frame)
double ll_diff; // L1 distance between consecutive LL bands
double ll_mean; // Mean brightness (LL band average)
double ll_variance; // Contrast estimate (LL band variance)
@@ -1872,7 +1847,6 @@ typedef struct tav_encoder_s {
// TAV-DT (Digital Tape) mode
int dt_mode; // 1 = TAV-DT mode (headerless streaming format), 0 = normal TAV (default)
uint32_t dt_sync_pattern; // Sync pattern for DT packets (0xE3537A1F for NTSC, 0xD193A745 for PAL)
uint8_t dt_quality_index; // DT quality index (0-15, maps to Y/Co/Cg quantizers and TAD quality)
uint8_t *dt_packet_buffer; // Buffer for accumulating DT packet contents
size_t dt_packet_buffer_size; // Current size of buffered data
size_t dt_packet_buffer_capacity; // Allocated capacity
@@ -8844,7 +8818,7 @@ static long write_dt_packet_header(tav_encoder_t *enc, uint32_t packet_size) {
uint8_t flags = 0;
if (!enc->progressive_mode) flags |= 0x01; // bit 0 = interlaced
if (enc->is_ntsc_framerate) flags |= 0x02; // bit 1 = is NTSC framerate
flags |= ((enc->dt_quality_index) & 0x0F) << 4; // bits 4-7 = quality index (0-15)
flags |= ((enc->quality_level) & 0x0F) << 4; // bits 4-7 = quality index (0-5, stored in bits 4-7)
header[pos++] = flags;
// Write reserved (2 bytes, zero-fill)
@@ -12615,22 +12589,20 @@ int main(int argc, char *argv[]) {
enc->tad_audio = 1; // TAD audio mandatory
enc->enable_temporal_dwt = 1; // Temporal DWT required
// Map quality level (0-5) to DT quality index (0-15)
// Quality 0 -> Q0, Quality 1 -> Q3, Quality 2 -> Q6, Quality 3 -> Q9, Quality 4 -> Q12, Quality 5 -> Q15
if (enc->quality_level >= 0 && enc->quality_level <= 5) {
enc->dt_quality_index = enc->quality_level * 3; // Linear mapping: 0->0, 1->3, 2->6, 3->9, 4->12, 5->15
} else {
enc->dt_quality_index = 9; // Default to Q9 (equivalent to quality level 3)
// Validate and clamp quality level (0-5)
if (enc->quality_level < 0 || enc->quality_level > 5) {
printf("TAV-DT: Warning - quality level %d out of range, clamping to 3\n", enc->quality_level);
enc->quality_level = 3; // Default to quality 3
}
// Apply quantizers from DT quality map
enc->quantiser_y = DT_QUALITY_MAP[enc->dt_quality_index].y;
enc->quantiser_co = DT_QUALITY_MAP[enc->dt_quality_index].co;
enc->quantiser_cg = DT_QUALITY_MAP[enc->dt_quality_index].cg;
// Apply quantizers from standard quality arrays
enc->quantiser_y = QUALITY_Y[enc->quality_level];
enc->quantiser_co = QUALITY_CO[enc->quality_level];
enc->quantiser_cg = QUALITY_CG[enc->quality_level];
printf("TAV-DT: Quality index %d -> Y=%d, Co=%d, Cg=%d, TAD_quality=%d\n",
enc->dt_quality_index, enc->quantiser_y, enc->quantiser_co, enc->quantiser_cg,
DT_QUALITY_MAP[enc->dt_quality_index].tad_quality);
printf("TAV-DT: Quality level %d -> Y=%d, Co=%d, Cg=%d, TAD_quality=%d\n",
enc->quality_level, enc->quantiser_y, enc->quantiser_co, enc->quantiser_cg,
enc->quality_level);
printf("TAV-DT: Enforcing format constraints (9/7 spatial, 5/3 temporal, 4+2 levels, EZBC, monoblock)\n");
}

View File

@@ -1,6 +1,7 @@
// TAV Packet Inspector - Comprehensive packet analysis tool for TAV files
// to compile: gcc -o tav_inspector tav_inspector.c -lzstd -lm
// Created by CuriousTorvald and Claude on 2025-10-14
// Updated 2025-12-02: Added TAV-DT (Digital Tape) format support
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
@@ -9,6 +10,10 @@
#include <getopt.h>
#include <zstd.h>
// TAV-DT sync patterns (big endian)
#define TAV_DT_SYNC_NTSC 0xE3537A1F // 720x480
#define TAV_DT_SYNC_PAL 0xD193A745 // 720x576
// Frame mode constants (from TAV spec)
#define FRAME_MODE_SKIP 0x00
#define FRAME_MODE_INTRA 0x01
@@ -110,6 +115,24 @@ typedef struct {
int summary_only;
} display_options_t;
// Helper to read data from either file or DT payload buffer
static size_t read_packet_data(void *dest, size_t size, size_t count, FILE *fp,
uint8_t *payload, uint32_t payload_size, uint32_t *offset) {
if (payload) {
// DT mode: read from payload buffer
size_t bytes_to_read = size * count;
if (*offset + bytes_to_read > payload_size) {
return 0; // Not enough data
}
memcpy(dest, payload + *offset, bytes_to_read);
*offset += bytes_to_read;
return count;
} else {
// TAV mode: read from file
return fread(dest, size, count, fp);
}
}
const char* get_packet_type_name(uint8_t type) {
switch (type) {
case TAV_PACKET_IFRAME: return "I-FRAME";
@@ -479,24 +502,87 @@ int main(int argc, char *argv[]) {
printf("==================================================\n\n");
}
// Read TAV header (32 bytes)
// Detect format: TAV (with magic) or TAV-DT (with sync pattern)
uint8_t header[32];
if (fread(header, 1, 32, fp) != 32) {
fprintf(stderr, "Error: Failed to read TAV header\n");
int is_dt_format = 0;
uint16_t dt_width = 0, dt_height = 0;
uint8_t dt_framerate = 0;
uint8_t dt_quality = 0;
int dt_is_interlaced = 0;
int dt_is_ntsc_framerate = 0;
// Read first 4 bytes to check format
uint8_t format_check[4];
if (fread(format_check, 1, 4, fp) != 4) {
fprintf(stderr, "Error: Failed to read file header\n");
fclose(fp);
return 1;
}
// Verify magic number
const char *magic = "\x1F\x54\x53\x56\x4D\x54\x41\x56"; // "\x1FTSVM TAV"
if (memcmp(header, magic, 8) != 0) {
fprintf(stderr, "Error: Invalid TAV magic number\n");
fclose(fp);
return 1;
// Check if it's a TAV-DT sync pattern
uint32_t sync = (format_check[0] << 24) | (format_check[1] << 16) |
(format_check[2] << 8) | format_check[3];
if (sync == TAV_DT_SYNC_NTSC || sync == TAV_DT_SYNC_PAL) {
// TAV-DT format detected
is_dt_format = 1;
dt_width = (sync == TAV_DT_SYNC_NTSC) ? 720 : 720;
dt_height = (sync == TAV_DT_SYNC_NTSC) ? 480 : 576;
// Read rest of DT packet header (12 more bytes = 16 total)
uint8_t dt_header[12];
if (fread(dt_header, 1, 12, fp) != 12) {
fprintf(stderr, "Error: Failed to read TAV-DT packet header\n");
fclose(fp);
return 1;
}
dt_framerate = dt_header[0];
uint8_t flags = dt_header[1];
dt_is_interlaced = flags & 0x01;
dt_is_ntsc_framerate = flags & 0x02;
dt_quality = (flags >> 4) & 0x0F;
// Rewind to start of first packet so the loop can process it
fseek(fp, -(4 + 12), SEEK_CUR); // Go back 16 bytes (full DT packet header)
if (!opts.summary_only) {
printf("TAV-DT Header (Headerless Streaming Format):\n");
printf(" Format: %s %s\n",
(sync == TAV_DT_SYNC_NTSC) ? "NTSC" : "PAL",
dt_is_interlaced ? "interlaced" : "progressive");
printf(" Resolution: %dx%d\n", dt_width, dt_height);
printf(" Frame rate: %d fps", dt_framerate);
if (dt_is_ntsc_framerate) printf(" (NTSC)");
printf("\n");
printf(" Quality index: %d (0-5)\n", dt_quality);
printf(" Total frames: Unknown (streaming format)\n");
printf(" Wavelet: 1 (CDF 9/7, fixed for DT)\n");
printf(" Decomp levels: 4 spatial + 2 temporal (fixed for DT)\n");
printf(" Entropy coder: EZBC (fixed for DT)\n");
printf(" Channel layout: YCoCg-R (fixed for DT)\n");
printf("\n");
}
} else {
// Regular TAV format - rewind and read full header
rewind(fp);
if (fread(header, 1, 32, fp) != 32) {
fprintf(stderr, "Error: Failed to read TAV header\n");
fclose(fp);
return 1;
}
// Verify magic number
const char *magic = "\x1F\x54\x53\x56\x4D\x54\x41\x56"; // "\x1FTSVM TAV"
if (memcmp(header, magic, 8) != 0) {
fprintf(stderr, "Error: Invalid TAV magic number\n");
fclose(fp);
return 1;
}
}
if (!opts.summary_only) {
// Parse header fields
if (!opts.summary_only && !is_dt_format) {
// Parse header fields (TAV format only)
uint8_t version = header[8];
uint8_t base_version = (version > 8) ? (version - 8) : version;
uint8_t temporal_motion_coder = (version > 8) ? 1 : 0;
@@ -581,13 +667,76 @@ static const char* TEMPORAL_WAVELET[] = {"Haar", "CDF 5/3"};
while (!feof(fp)) {
long packet_offset = ftell(fp);
uint8_t packet_type;
if (fread(&packet_type, 1, 1, fp) != 1) break;
uint8_t *packet_payload = NULL;
uint32_t payload_size = 0;
uint32_t payload_offset = 1; // Start at 1 to skip packet type byte in DT mode
if (is_dt_format) {
// TAV-DT: Read 16-byte packet header
uint8_t dt_pkt_header[16];
if (fread(dt_pkt_header, 1, 16, fp) != 16) break;
// Parse DT packet header
uint32_t sync_check = (dt_pkt_header[0] << 24) | (dt_pkt_header[1] << 16) |
(dt_pkt_header[2] << 8) | dt_pkt_header[3];
payload_size = dt_pkt_header[8] | (dt_pkt_header[9] << 8) |
(dt_pkt_header[10] << 16) | (dt_pkt_header[11] << 24);
// Verify sync pattern
if (sync_check != TAV_DT_SYNC_NTSC && sync_check != TAV_DT_SYNC_PAL) {
if (!opts.summary_only) {
fprintf(stderr, "Warning: Invalid sync pattern 0x%08X at offset 0x%lX\n",
sync_check, packet_offset);
}
break;
}
// Read packet payload
packet_payload = malloc(payload_size);
if (!packet_payload || fread(packet_payload, 1, payload_size, fp) != payload_size) {
free(packet_payload);
break;
}
// TAV-DT payload structure: [timecode(8)][TAD_packets...][video_packet]
// Skip past timecode (8 bytes) and any TAD packets to find the video packet
payload_offset = 8; // Skip timecode
// Skip TAD audio packets (if any)
while (payload_offset < payload_size && packet_payload[payload_offset] == TAV_PACKET_AUDIO_TAD) {
payload_offset++; // Skip packet type
if (payload_offset + 6 > payload_size) break;
// Skip past TAD packet header to get to payload size
payload_offset += 2; // sample_count
uint32_t tad_payload_size = packet_payload[payload_offset] |
(packet_payload[payload_offset+1] << 8) |
(packet_payload[payload_offset+2] << 16) |
(packet_payload[payload_offset+3] << 24);
payload_offset += 4; // payload_size field
payload_offset += tad_payload_size; // Skip TAD payload
}
// Extract video packet type (should be at current offset)
if (payload_offset < payload_size) {
packet_type = packet_payload[payload_offset];
payload_offset++; // Move past packet type for subsequent reads
} else {
packet_type = 0x00; // No video packet
}
} else {
// Regular TAV: Read packet type directly
if (fread(&packet_type, 1, 1, fp) != 1) break;
}
int display = should_display_packet(packet_type, &opts);
if (!opts.summary_only && display) {
printf("Packet %d (offset 0x%lX): Type 0x%02X (%s)",
packet_num, packet_offset, packet_type, get_packet_type_name(packet_type));
if (is_dt_format) {
printf(" [DT payload: %u bytes]", payload_size);
}
}
switch (packet_type) {
@@ -618,7 +767,7 @@ static const char* TEMPORAL_WAVELET[] = {"Haar", "CDF 5/3"};
case TAV_PACKET_TIMECODE: {
stats.timecode_count++;
uint64_t timecode_ns;
if (fread(&timecode_ns, sizeof(uint64_t), 1, fp) != 1) break;
if (read_packet_data(&timecode_ns, sizeof(uint64_t), 1, fp, packet_payload, payload_size, &payload_offset) != 1) break;
if (!opts.summary_only && display) {
double timecode_sec = timecode_ns / 1000000000.0;
@@ -630,22 +779,25 @@ static const char* TEMPORAL_WAVELET[] = {"Haar", "CDF 5/3"};
case TAV_PACKET_GOP_UNIFIED: case TAV_PACKET_GOP_UNIFIED_MOTION: {
// Unified GOP packet: [gop_size][motion_vectors...][compressed_size][data]
uint8_t gop_size;
if (fread(&gop_size, 1, 1, fp) != 1) break;
if (read_packet_data(&gop_size, 1, 1, fp, packet_payload, payload_size, &payload_offset) != 1) break;
// Read motion vectors
uint32_t size0 = 0;
if (packet_type == TAV_PACKET_GOP_UNIFIED_MOTION) {
if (fread(&size0, sizeof(uint32_t), 1, fp) != 1) { break; }
if (read_packet_data(&size0, sizeof(uint32_t), 1, fp, packet_payload, payload_size, &payload_offset) != 1) { break; }
stats.total_video_bytes += size0;
stats.gop_unified_motion_count++;
fseek(fp, size0, SEEK_CUR);
if (!packet_payload) fseek(fp, size0, SEEK_CUR);
else payload_offset += size0;
}
// Read compressed data size
uint32_t size1;
if (fread(&size1, sizeof(uint32_t), 1, fp) != 1) { break; }
if (read_packet_data(&size1, sizeof(uint32_t), 1, fp, packet_payload, payload_size, &payload_offset) != 1) { break; }
stats.total_video_bytes += size1;
fseek(fp, size1, SEEK_CUR);
if (!packet_payload) fseek(fp, size1, SEEK_CUR);
// else: data is already in payload buffer, skip ahead
else payload_offset += size1;
stats.total_gop_frames += gop_size;
@@ -664,7 +816,7 @@ static const char* TEMPORAL_WAVELET[] = {"Haar", "CDF 5/3"};
case TAV_PACKET_GOP_SYNC: {
// GOP sync packet: [frame_count]
uint8_t frame_count;
if (fread(&frame_count, 1, 1, fp) != 1) break;
if (read_packet_data(&frame_count, 1, 1, fp, packet_payload, payload_size, &payload_offset) != 1) break;
stats.gop_sync_count++;
current_frame += frame_count; // Advance frame counter
@@ -783,23 +935,23 @@ static const char* TEMPORAL_WAVELET[] = {"Haar", "CDF 5/3"};
// Read sample count
uint16_t sample_count0;
if (fread(&sample_count0, sizeof(uint16_t), 1, fp) != 1) break;
if (read_packet_data(&sample_count0, sizeof(uint16_t), 1, fp, packet_payload, payload_size, &payload_offset) != 1) break;
// Read payload_size + 7
uint32_t payload_size_plus_7;
if (fread(&payload_size_plus_7, sizeof(uint32_t), 1, fp) != 1) break;
if (read_packet_data(&payload_size_plus_7, sizeof(uint32_t), 1, fp, packet_payload, payload_size, &payload_offset) != 1) break;
// Read sample count
uint16_t sample_count;
if (fread(&sample_count, sizeof(uint16_t), 1, fp) != 1) break;
if (read_packet_data(&sample_count, sizeof(uint16_t), 1, fp, packet_payload, payload_size, &payload_offset) != 1) break;
// Read quantiser index
uint8_t quantiser;
if (fread(&quantiser, sizeof(uint8_t), 1, fp) != 1) break;
if (read_packet_data(&quantiser, sizeof(uint8_t), 1, fp, packet_payload, payload_size, &payload_offset) != 1) break;
// Read compressed size
uint32_t compressed_size;
if (fread(&compressed_size, sizeof(uint32_t), 1, fp) != 1) break;
if (read_packet_data(&compressed_size, sizeof(uint32_t), 1, fp, packet_payload, payload_size, &payload_offset) != 1) break;
stats.total_audio_bytes += compressed_size;
stats.audio_tad_bytes += compressed_size;
@@ -810,7 +962,8 @@ static const char* TEMPORAL_WAVELET[] = {"Haar", "CDF 5/3"};
}
// Skip compressed data
fseek(fp, compressed_size, SEEK_CUR);
if (!packet_payload) fseek(fp, compressed_size, SEEK_CUR);
else payload_offset += compressed_size;
break;
}
@@ -948,6 +1101,11 @@ static const char* TEMPORAL_WAVELET[] = {"Haar", "CDF 5/3"};
printf("\n");
}
// Free DT packet payload if allocated
if (packet_payload) {
free(packet_payload);
}
packet_num++;
}