mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-06-10 06:54:04 +09:00
tav_inspector update
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
// TAV Packet Inspector - Comprehensive packet analysis tool for TAV files
|
// TAV Packet Inspector - Comprehensive packet analysis tool for TAV files
|
||||||
|
// to compile: gcc -o tav_inspector tav_inspector.c -lzstd -lm
|
||||||
// Created by Claude on 2025-10-14
|
// Created by Claude on 2025-10-14
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
@@ -173,12 +174,12 @@ void print_subtitle_packet(FILE *fp, uint32_t size, int is_karaoke, int verbose)
|
|||||||
text[remaining] = '\0';
|
text[remaining] = '\0';
|
||||||
|
|
||||||
// Truncate long text for display
|
// Truncate long text for display
|
||||||
if (remaining > 60) {
|
/*if (remaining > 60) {
|
||||||
text[57] = '.';
|
text[57] = '.';
|
||||||
text[58] = '.';
|
text[58] = '.';
|
||||||
text[59] = '.';
|
text[59] = '.';
|
||||||
text[60] = '\0';
|
text[60] = '\0';
|
||||||
}
|
}*/
|
||||||
|
|
||||||
// Clean up newlines and control characters for display
|
// Clean up newlines and control characters for display
|
||||||
for (int i = 0; text[i]; i++) {
|
for (int i = 0; text[i]; i++) {
|
||||||
@@ -221,9 +222,20 @@ void print_extended_header(FILE *fp, int verbose) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
printf(" %.4s (type=0x%02X): ", key, value_type);
|
const char *value_type_str = "Unknown";
|
||||||
|
switch (value_type) {
|
||||||
|
case 0x00: value_type_str = "Int16"; break;
|
||||||
|
case 0x01: value_type_str = "Int24"; break;
|
||||||
|
case 0x02: value_type_str = "Int32"; break;
|
||||||
|
case 0x03: value_type_str = "Int48"; break;
|
||||||
|
case 0x04: value_type_str = "Int64"; break;
|
||||||
|
case 0x10: value_type_str = "Bytes"; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf(" %.4s (type: %s (0x%02X)): ", key, value_type_str, value_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (value_type == 0x04) { // Uint64
|
if (value_type == 0x04) { // Uint64
|
||||||
uint64_t value;
|
uint64_t value;
|
||||||
if (fread(&value, sizeof(uint64_t), 1, fp) != 1) {
|
if (fread(&value, sizeof(uint64_t), 1, fp) != 1) {
|
||||||
@@ -261,12 +273,12 @@ void print_extended_header(FILE *fp, int verbose) {
|
|||||||
data[length] = '\0';
|
data[length] = '\0';
|
||||||
|
|
||||||
// Truncate long strings
|
// Truncate long strings
|
||||||
if (length > 60) {
|
/*if (length > 60) {
|
||||||
data[57] = '.';
|
data[57] = '.';
|
||||||
data[58] = '.';
|
data[58] = '.';
|
||||||
data[59] = '.';
|
data[59] = '.';
|
||||||
data[60] = '\0';
|
data[60] = '\0';
|
||||||
}
|
}*/
|
||||||
printf("\"%s\"", data);
|
printf("\"%s\"", data);
|
||||||
}
|
}
|
||||||
free(data);
|
free(data);
|
||||||
@@ -274,25 +286,33 @@ void print_extended_header(FILE *fp, int verbose) {
|
|||||||
if (verbose) printf("Unknown type");
|
if (verbose) printf("Unknown type");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (verbose) {
|
if (verbose && i < num_pairs - 1) {
|
||||||
printf("\n");
|
printf("\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read frame mode from compressed P-frame data
|
// Frame info structure
|
||||||
// Returns: 0=SKIP, 1=INTRA, 2=DELTA, -1=error
|
typedef struct {
|
||||||
int get_frame_mode(FILE *fp, uint32_t compressed_size) {
|
int mode; // 0=SKIP, 1=INTRA, 2=DELTA, -1=error
|
||||||
|
uint8_t quantiser; // Quantiser override (0xFF = default)
|
||||||
|
} frame_info_t;
|
||||||
|
|
||||||
|
// Read frame mode and quantiser from compressed frame data
|
||||||
|
// Works for both I-frames and P-frames
|
||||||
|
frame_info_t get_frame_info(FILE *fp, uint32_t compressed_size) {
|
||||||
|
frame_info_t info = {-1, 0xFF};
|
||||||
|
|
||||||
// Read compressed data
|
// Read compressed data
|
||||||
uint8_t *compressed_data = malloc(compressed_size);
|
uint8_t *compressed_data = malloc(compressed_size);
|
||||||
if (!compressed_data) {
|
if (!compressed_data) {
|
||||||
fseek(fp, compressed_size, SEEK_CUR);
|
fseek(fp, compressed_size, SEEK_CUR);
|
||||||
return -1;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fread(compressed_data, 1, compressed_size, fp) != compressed_size) {
|
if (fread(compressed_data, 1, compressed_size, fp) != compressed_size) {
|
||||||
free(compressed_data);
|
free(compressed_data);
|
||||||
return -1;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allocate buffer for decompression
|
// Allocate buffer for decompression
|
||||||
@@ -301,23 +321,28 @@ int get_frame_mode(FILE *fp, uint32_t compressed_size) {
|
|||||||
uint8_t *decompressed_data = malloc(decompress_size);
|
uint8_t *decompressed_data = malloc(decompress_size);
|
||||||
if (!decompressed_data) {
|
if (!decompressed_data) {
|
||||||
free(compressed_data);
|
free(compressed_data);
|
||||||
return -1;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decompress
|
// Decompress
|
||||||
size_t actual_size = ZSTD_decompress(decompressed_data, decompress_size, compressed_data, compressed_size);
|
size_t actual_size = ZSTD_decompress(decompressed_data, decompress_size, compressed_data, compressed_size);
|
||||||
free(compressed_data);
|
free(compressed_data);
|
||||||
|
|
||||||
if (ZSTD_isError(actual_size) || actual_size < 1) {
|
if (ZSTD_isError(actual_size) || actual_size < 2) {
|
||||||
free(decompressed_data);
|
free(decompressed_data);
|
||||||
return -1;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read mode byte (first byte of decompressed data)
|
// Read mode byte (first byte of decompressed data)
|
||||||
uint8_t mode_byte = decompressed_data[0];
|
info.mode = decompressed_data[0];
|
||||||
free(decompressed_data);
|
|
||||||
|
|
||||||
return mode_byte;
|
// Read quantiser override (second byte) if mode is not SKIP
|
||||||
|
if (info.mode != FRAME_MODE_SKIP && actual_size >= 2) {
|
||||||
|
info.quantiser = decompressed_data[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
free(decompressed_data);
|
||||||
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
void print_help(const char *program_name) {
|
void print_help(const char *program_name) {
|
||||||
@@ -332,7 +357,6 @@ void print_help(const char *program_name) {
|
|||||||
printf(" -m, --metadata Show metadata packets only\n");
|
printf(" -m, --metadata Show metadata packets only\n");
|
||||||
printf(" -x, --extended Show extended header only\n");
|
printf(" -x, --extended Show extended header only\n");
|
||||||
printf(" -S, --sync Show sync packets\n");
|
printf(" -S, --sync Show sync packets\n");
|
||||||
printf(" -V, --verbose Verbose output with packet details\n");
|
|
||||||
printf(" --summary Show summary statistics only\n");
|
printf(" --summary Show summary statistics only\n");
|
||||||
printf(" -h, --help Show this help\n\n");
|
printf(" -h, --help Show this help\n\n");
|
||||||
printf("Examples:\n");
|
printf("Examples:\n");
|
||||||
@@ -346,6 +370,9 @@ int main(int argc, char *argv[]) {
|
|||||||
display_options_t opts = {0};
|
display_options_t opts = {0};
|
||||||
opts.show_all = 1; // Default: show all
|
opts.show_all = 1; // Default: show all
|
||||||
|
|
||||||
|
// Track absolute frame number
|
||||||
|
int current_frame = 0;
|
||||||
|
|
||||||
static struct option long_options[] = {
|
static struct option long_options[] = {
|
||||||
{"all", no_argument, 0, 'a'},
|
{"all", no_argument, 0, 'a'},
|
||||||
{"video", no_argument, 0, 'v'},
|
{"video", no_argument, 0, 'v'},
|
||||||
@@ -355,7 +382,6 @@ int main(int argc, char *argv[]) {
|
|||||||
{"metadata", no_argument, 0, 'm'},
|
{"metadata", no_argument, 0, 'm'},
|
||||||
{"extended", no_argument, 0, 'x'},
|
{"extended", no_argument, 0, 'x'},
|
||||||
{"sync", no_argument, 0, 'S'},
|
{"sync", no_argument, 0, 'S'},
|
||||||
{"verbose", no_argument, 0, 'V'},
|
|
||||||
{"summary", no_argument, 0, 1000},
|
{"summary", no_argument, 0, 1000},
|
||||||
{"help", no_argument, 0, 'h'},
|
{"help", no_argument, 0, 'h'},
|
||||||
{0, 0, 0, 0}
|
{0, 0, 0, 0}
|
||||||
@@ -372,7 +398,6 @@ int main(int argc, char *argv[]) {
|
|||||||
case 'm': opts.show_metadata = 1; opts.show_all = 0; break;
|
case 'm': opts.show_metadata = 1; opts.show_all = 0; break;
|
||||||
case 'x': opts.show_extended = 1; opts.show_all = 0; break;
|
case 'x': opts.show_extended = 1; opts.show_all = 0; break;
|
||||||
case 'S': opts.show_sync = 1; opts.show_all = 0; break;
|
case 'S': opts.show_sync = 1; opts.show_all = 0; break;
|
||||||
case 'V': opts.verbose = 1; break;
|
|
||||||
case 1000: opts.summary_only = 1; break;
|
case 1000: opts.summary_only = 1; break;
|
||||||
case 'h':
|
case 'h':
|
||||||
print_help(argv[0]);
|
print_help(argv[0]);
|
||||||
@@ -383,6 +408,8 @@ int main(int argc, char *argv[]) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
opts.verbose = 1;
|
||||||
|
|
||||||
if (optind >= argc) {
|
if (optind >= argc) {
|
||||||
fprintf(stderr, "Error: No input file specified\n\n");
|
fprintf(stderr, "Error: No input file specified\n\n");
|
||||||
print_help(argv[0]);
|
print_help(argv[0]);
|
||||||
@@ -452,7 +479,7 @@ int main(int argc, char *argv[]) {
|
|||||||
|
|
||||||
if (!opts.summary_only && display) {
|
if (!opts.summary_only && display) {
|
||||||
double timecode_sec = timecode_ns / 1000000000.0;
|
double timecode_sec = timecode_ns / 1000000000.0;
|
||||||
printf(" - %.6f seconds", timecode_sec);
|
printf(" - %.6f seconds (Frame %d)", timecode_sec, current_frame);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -486,12 +513,12 @@ int main(int argc, char *argv[]) {
|
|||||||
printf(" - GOP size=%u, data size=%u bytes (%.2f bytes/frame)",
|
printf(" - GOP size=%u, data size=%u bytes (%.2f bytes/frame)",
|
||||||
gop_size, size, (double)size / gop_size);
|
gop_size, size, (double)size / gop_size);
|
||||||
|
|
||||||
// Always show motion vectors for GOP packets
|
// Always show motion vectors for GOP packets with absolute frame numbers
|
||||||
if (gop_size > 0) {
|
if (gop_size > 0) {
|
||||||
printf("\n Motion vectors (quarter-pixel):");
|
printf("\n Motion vectors (quarter-pixel):");
|
||||||
for (int i = 0; i < gop_size; i++) {
|
for (int i = 0; i < gop_size; i++) {
|
||||||
printf("\n Frame %d: (%.2f, %.2f) px",
|
printf("\n Frame %d (#%d): (%.2f, %.2f) px",
|
||||||
i, motion_x[i] / 4.0, motion_y[i] / 4.0);
|
current_frame + i, i, motion_x[i] / 4.0, motion_y[i] / 4.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -508,6 +535,7 @@ int main(int argc, char *argv[]) {
|
|||||||
if (fread(&frame_count, 1, 1, fp) != 1) break;
|
if (fread(&frame_count, 1, 1, fp) != 1) break;
|
||||||
|
|
||||||
stats.gop_sync_count++;
|
stats.gop_sync_count++;
|
||||||
|
current_frame += frame_count; // Advance frame counter
|
||||||
|
|
||||||
if (!opts.summary_only && display) {
|
if (!opts.summary_only && display) {
|
||||||
printf(" - %u frames decoded from GOP block", frame_count);
|
printf(" - %u frames decoded from GOP block", frame_count);
|
||||||
@@ -537,36 +565,46 @@ int main(int argc, char *argv[]) {
|
|||||||
if (fread(&size, sizeof(uint32_t), 1, fp) != 1) break;
|
if (fread(&size, sizeof(uint32_t), 1, fp) != 1) break;
|
||||||
stats.total_video_bytes += size;
|
stats.total_video_bytes += size;
|
||||||
|
|
||||||
// Determine frame mode for P-frames
|
// Get frame info (mode and quantiser) for both I-frames and P-frames
|
||||||
int frame_mode = -1;
|
frame_info_t frame_info = get_frame_info(fp, size);
|
||||||
|
|
||||||
if (packet_type == TAV_PACKET_PFRAME ||
|
if (packet_type == TAV_PACKET_PFRAME ||
|
||||||
(packet_type >= 0x71 && packet_type <= 0x7F && (packet_type & 1))) {
|
(packet_type >= 0x71 && packet_type <= 0x7F && (packet_type & 1))) {
|
||||||
// This is a P-frame (main or multiplexed)
|
// This is a P-frame (main or multiplexed)
|
||||||
frame_mode = get_frame_mode(fp, size);
|
|
||||||
|
|
||||||
if (packet_type == TAV_PACKET_PFRAME) {
|
if (packet_type == TAV_PACKET_PFRAME) {
|
||||||
stats.pframe_count++;
|
stats.pframe_count++;
|
||||||
if (frame_mode == FRAME_MODE_INTRA) stats.pframe_intra_count++;
|
if (frame_info.mode == FRAME_MODE_INTRA) stats.pframe_intra_count++;
|
||||||
else if (frame_mode == FRAME_MODE_DELTA) stats.pframe_delta_count++;
|
else if (frame_info.mode == FRAME_MODE_DELTA) stats.pframe_delta_count++;
|
||||||
else if (frame_mode == FRAME_MODE_SKIP) stats.pframe_skip_count++;
|
else if (frame_info.mode == FRAME_MODE_SKIP) stats.pframe_skip_count++;
|
||||||
|
current_frame++; // Increment for P-frame
|
||||||
} else {
|
} else {
|
||||||
stats.mux_video_count++;
|
stats.mux_video_count++;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// I-frame
|
// I-frame
|
||||||
if (packet_type == TAV_PACKET_IFRAME) stats.iframe_count++;
|
if (packet_type == TAV_PACKET_IFRAME) {
|
||||||
else stats.mux_video_count++;
|
stats.iframe_count++;
|
||||||
fseek(fp, size, SEEK_CUR);
|
current_frame++; // Increment for I-frame
|
||||||
|
} else {
|
||||||
|
stats.mux_video_count++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!opts.summary_only && display) {
|
if (!opts.summary_only && display) {
|
||||||
printf(" - size=%u bytes", size);
|
printf(" - size=%u bytes", size);
|
||||||
|
|
||||||
// Show frame mode for P-frames
|
// Show frame mode (for both I-frames and P-frames)
|
||||||
if (frame_mode >= 0) {
|
if (frame_info.mode >= 0) {
|
||||||
if (frame_mode == FRAME_MODE_SKIP) printf(" [SKIP]");
|
if (frame_info.mode == FRAME_MODE_SKIP) printf(" [SKIP]");
|
||||||
else if (frame_mode == FRAME_MODE_DELTA) printf(" [DELTA]");
|
else if (frame_info.mode == FRAME_MODE_DELTA) printf(" [DELTA]");
|
||||||
else if (frame_mode == FRAME_MODE_INTRA) printf(" [INTRA]");
|
else if (frame_info.mode == FRAME_MODE_INTRA) printf(" [INTRA]");
|
||||||
|
|
||||||
|
// Show quantiser override if not default
|
||||||
|
if (frame_info.mode != FRAME_MODE_SKIP) {
|
||||||
|
if (frame_info.quantiser != 0xFF) {
|
||||||
|
printf(" [Q=%u]", frame_info.quantiser);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (packet_type >= 0x70 && packet_type <= 0x7F) {
|
if (packet_type >= 0x70 && packet_type <= 0x7F) {
|
||||||
|
|||||||
Reference in New Issue
Block a user