diff --git a/terranmon.txt b/terranmon.txt index f55afd9..af3bce9 100644 --- a/terranmon.txt +++ b/terranmon.txt @@ -1633,7 +1633,8 @@ start of the next packet uint8 Flags - bit 0 = interlaced - bit 1 = is NTSC framerate - - bit 4-7 = quality index (0-15) + - bit 4-7 = quality index (0-5) + * Quality indices follow TSVM encoder's int16 Reserved (zero-fill) uint32 Total packet size past header uint32 CRC-32 of 12-byte header @@ -1649,26 +1650,6 @@ start of the next packet 5. Check calculated CRC against stored CRC 6. If they match, sync to the stream; if not, find a next sync pattern -# Quality Indices - -QIndex | Y,Co,Cg (true value) | TAD quality (max index) ------------------------------------------ - 0 | 60,104,312 | 23 - 1 | 44,88,248 | 31 - 2 | 36,72,188 | 39 - 3 | 28,56,136 | 47 - 4 | 20,48,106 | 55 - 5 | 16,40,80 | 63 - 6 | 12,36,68 | 71 - 7 | 10,32,58 | 79 - 8 | 8,28,48 | 87 - 9 | 6,24,38 | 95 -10 | 5,20,30 | 103 -11 | 4,16,22 | 111 -12 | 3,12,16 | 119 -13 | 2,8,10 | 123 -14 | 2,5,6 | 127 -15 | 1,2,2 | 127 -------------------------------------------------------------------------------- diff --git a/video_encoder/Makefile b/video_encoder/Makefile index 58ad921..1cd1f55 100644 --- a/video_encoder/Makefile +++ b/video_encoder/Makefile @@ -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: diff --git a/video_encoder/decoder_tav_dt.c b/video_encoder/decoder_tav_dt.c new file mode 100644 index 0000000..e68d4a9 --- /dev/null +++ b/video_encoder/decoder_tav_dt.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#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; +} diff --git a/video_encoder/encoder_tav.c b/video_encoder/encoder_tav.c index 8668f72..392d751 100644 --- a/video_encoder/encoder_tav.c +++ b/video_encoder/encoder_tav.c @@ -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"); } diff --git a/video_encoder/tav_inspector.c b/video_encoder/tav_inspector.c index cdbdf3a..feb4d7e 100644 --- a/video_encoder/tav_inspector.c +++ b/video_encoder/tav_inspector.c @@ -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 #include #include @@ -9,6 +10,10 @@ #include #include +// 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++; }