mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-03-07 11:51:49 +09:00
tav: encoder ENDT fix
This commit is contained in:
@@ -8,31 +8,32 @@ if (!exec_args[1]) {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
let lowfilename = exec_args[1] + "_low.chr"
|
const fullFilePath = _G.shell.resolvePathInput(exec_args[1]).full
|
||||||
let highfilename = exec_args[1] + "_high.chr"
|
let lowfilename = fullFilePath + "_low.chr"
|
||||||
|
let highfilename = fullFilePath + "_high.chr"
|
||||||
|
|
||||||
let workarea = sys.malloc(1920)
|
let workarea = sys.malloc(1920)
|
||||||
|
|
||||||
// dump low rom
|
// dump low rom
|
||||||
sys.poke(-1299460, 16)
|
sys.poke(-1299460, 16)
|
||||||
for (let i = 0; i < 1920; i++) {
|
for (let i = 0; i < 1920; i++) {
|
||||||
let byte = sys.peek(-1300607 - i)
|
let byte = sys.peek(-133121 - i)
|
||||||
sys.poke(workarea + i, byte)
|
sys.poke(workarea + i, byte)
|
||||||
}
|
}
|
||||||
|
|
||||||
filesystem.open("A", lowfilename, "W")
|
const lowfile = files.open(lowfilename)
|
||||||
dma.ramToCom(workarea, filesystem._toPorts("A")[0], 1920)
|
lowfile.pwrite(workarea, 1920, 0)
|
||||||
println("Wrote CHR rom " + lowfilename)
|
println("Wrote CHR rom " + lowfilename)
|
||||||
|
|
||||||
// dump high rom
|
// dump high rom
|
||||||
sys.poke(-1299460, 17)
|
sys.poke(-1299460, 17)
|
||||||
for (let i = 0; i < 1920; i++) {
|
for (let i = 0; i < 1920; i++) {
|
||||||
let byte = sys.peek(-1300607 - i)
|
let byte = sys.peek(-133121 - i)
|
||||||
sys.poke(workarea + i, byte)
|
sys.poke(workarea + i, byte)
|
||||||
}
|
}
|
||||||
|
|
||||||
filesystem.open("A", highfilename, "W")
|
const highfile = files.open(highfilename)
|
||||||
dma.ramToCom(workarea, filesystem._toPorts("A")[0], 1920)
|
highfile.pwrite(workarea, 1920, 0)
|
||||||
println("Wrote CHR rom " + highfilename)
|
println("Wrote CHR rom " + highfilename)
|
||||||
|
|
||||||
sys.free(workarea)
|
sys.free(workarea)
|
||||||
@@ -1005,7 +1005,7 @@ transmission capability, and region-of-interest coding.
|
|||||||
0x31: Subtitle in "Simple" format with timecodes
|
0x31: Subtitle in "Simple" format with timecodes
|
||||||
0x32: Subtitle in "Karaoke" format
|
0x32: Subtitle in "Karaoke" format
|
||||||
0x33: Subtitle in "Karaoke" format with timecodes
|
0x33: Subtitle in "Karaoke" format with timecodes
|
||||||
0x3F: Videotex (full-frame text buffer image)
|
0x3F: Videotex (full-frame text buffer memory image)
|
||||||
<synchronised tracks>
|
<synchronised tracks>
|
||||||
0x40: MP2 audio track (32 KHz)
|
0x40: MP2 audio track (32 KHz)
|
||||||
0x41: Zstd-compressed 8-bit PCM (32 KHz, audio hardware's native format)
|
0x41: Zstd-compressed 8-bit PCM (32 KHz, audio hardware's native format)
|
||||||
@@ -1083,9 +1083,9 @@ transmission capability, and region-of-interest coding.
|
|||||||
|
|
||||||
### List of Keys
|
### List of Keys
|
||||||
- Uint64 BGNT: Video begin time in nanoseconds (must be equal to the value of the first Timecode packet)
|
- Uint64 BGNT: Video begin time in nanoseconds (must be equal to the value of the first Timecode packet)
|
||||||
- Uint64 ENDT: Video end time in nanoseconds (must be equal to the value of the last Timecode packet)
|
- Uint64 ENDT: Video end time in nanoseconds (must be equal to BGNT + playback time)
|
||||||
- Uint64 CDAT: Creation time in microseconds since UNIX Epoch (must be in UTC timezone)
|
- Uint64 CDAT: Creation time in microseconds since UNIX Epoch (must be in UTC timezone)
|
||||||
- Bytes VNDR: Name and version of the encoder (for Reference encoder: "Encoder-TAV 20251014 (list,of,features)")
|
- Bytes VNDR: Name and version of the encoder (for Reference encoder: "Encoder-TAV 20251014 (list,of,features)")
|
||||||
- Bytes FMPG: FFmpeg version (typically "ffmpeg version 8.0 Copyright (c) 2000-2025 the FFmpeg developers"; the first line of text FFmpeg emits)
|
- Bytes FMPG: FFmpeg version (typically "ffmpeg version 8.0 Copyright (c) 2000-2025 the FFmpeg developers"; the first line of text FFmpeg emits)
|
||||||
|
|
||||||
## Extensible Packet Structure
|
## Extensible Packet Structure
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
// === UNIFORMS ===
|
// === UNIFORMS ===
|
||||||
uniform float time = 0.0; // Frame count
|
uniform float time = 0.0; // Frame count
|
||||||
uniform vec2 resolution = vec2(640.0, 480.0); // Virtual resolution (e.g., 640x480)
|
uniform vec2 resolution = vec2(640.0, 480.0); // Virtual resolution (e.g., 640x480)
|
||||||
|
uniform float displayScale = 2.0;
|
||||||
uniform sampler2D u_texture; // Input texture
|
uniform sampler2D u_texture; // Input texture
|
||||||
uniform vec2 flip = vec2(0.0, 0.0); // UV flip control (0,1 = flip Y)
|
uniform vec2 flip = vec2(0.0, 0.0); // UV flip control (0,1 = flip Y)
|
||||||
|
|
||||||
@@ -256,7 +257,7 @@ vec3 decodeCGAComposite(vec2 uv, vec2 texelSize, float pixelX, float pixelY) {
|
|||||||
// === TRINITRON PHOSPHOR MASK ===
|
// === TRINITRON PHOSPHOR MASK ===
|
||||||
vec3 trinitronMask(vec2 screenPos) {
|
vec3 trinitronMask(vec2 screenPos) {
|
||||||
float strength = getPhosphorStrength();
|
float strength = getPhosphorStrength();
|
||||||
float outputX = screenPos.x * 2.0; // 2x display scale
|
float outputX = screenPos.x * displayScale;
|
||||||
float stripe = mod(outputX, 3.0);
|
float stripe = mod(outputX, 3.0);
|
||||||
|
|
||||||
float bleed = 0.15;
|
float bleed = 0.15;
|
||||||
@@ -279,7 +280,7 @@ vec3 trinitronMask(vec2 screenPos) {
|
|||||||
// === SCANLINE MASK ===
|
// === SCANLINE MASK ===
|
||||||
float scanlineMask(vec2 screenPos) {
|
float scanlineMask(vec2 screenPos) {
|
||||||
float strength = getScanlineStrength();
|
float strength = getScanlineStrength();
|
||||||
float outputY = screenPos.y * 2.0; // 2x display scale
|
float outputY = screenPos.y * displayScale;
|
||||||
|
|
||||||
float scanline = sin(outputY * PI);
|
float scanline = sin(outputY * PI);
|
||||||
scanline = scanline * 0.5 + 0.5;
|
scanline = scanline * 0.5 + 0.5;
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
#include "decoder_tad.h" // Shared TAD decoder library
|
#include "decoder_tad.h" // Shared TAD decoder library
|
||||||
#include "tav_avx512.h" // AVX-512 SIMD optimisations
|
#include "tav_avx512.h" // AVX-512 SIMD optimisations
|
||||||
|
|
||||||
#define DECODER_VENDOR_STRING "Decoder-TAV 20251124 (avx512,presets)"
|
#define DECODER_VENDOR_STRING "Decoder-TAV 20251126 (presets)"
|
||||||
|
|
||||||
// TAV format constants
|
// TAV format constants
|
||||||
#define TAV_MAGIC "\x1F\x54\x53\x56\x4D\x54\x41\x56"
|
#define TAV_MAGIC "\x1F\x54\x53\x56\x4D\x54\x41\x56"
|
||||||
@@ -315,7 +315,7 @@ static void dequantise_dwt_subbands_perceptual(int q_index, int q_y_global, cons
|
|||||||
// Previous denormalization in EZBC caused int16_t overflow (clipping at 32767)
|
// Previous denormalization in EZBC caused int16_t overflow (clipping at 32767)
|
||||||
// for bright pixels, creating dark DWT-pattern blemishes
|
// for bright pixels, creating dark DWT-pattern blemishes
|
||||||
|
|
||||||
#ifdef __AVX512F__
|
/*#ifdef __AVX512F__
|
||||||
// Use AVX-512 optimised dequantization if available (1.1x speedup against -Ofast)
|
// Use AVX-512 optimised dequantization if available (1.1x speedup against -Ofast)
|
||||||
// Check: subband has >=16 elements AND won't exceed buffer bounds
|
// Check: subband has >=16 elements AND won't exceed buffer bounds
|
||||||
const int subband_end = subband->coeff_start + subband->coeff_count;
|
const int subband_end = subband->coeff_start + subband->coeff_count;
|
||||||
@@ -327,7 +327,7 @@ static void dequantise_dwt_subbands_perceptual(int q_index, int q_y_global, cons
|
|||||||
effective_quantiser
|
effective_quantiser
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
#endif
|
#endif*/
|
||||||
// Scalar fallback or small subbands
|
// Scalar fallback or small subbands
|
||||||
for (int i = 0; i < subband->coeff_count; i++) {
|
for (int i = 0; i < subband->coeff_count; i++) {
|
||||||
const int idx = subband->coeff_start + i;
|
const int idx = subband->coeff_start + i;
|
||||||
@@ -336,9 +336,9 @@ static void dequantise_dwt_subbands_perceptual(int q_index, int q_y_global, cons
|
|||||||
dequantised[idx] = untruncated;
|
dequantised[idx] = untruncated;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#ifdef __AVX512F__
|
/*#ifdef __AVX512F__
|
||||||
}
|
}
|
||||||
#endif
|
#endif*/
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debug: Verify LL band was dequantised correctly
|
// Debug: Verify LL band was dequantised correctly
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
#include <float.h>
|
#include <float.h>
|
||||||
#include "tav_avx512.h" // AVX-512 SIMD optimisations
|
#include "tav_avx512.h" // AVX-512 SIMD optimisations
|
||||||
|
|
||||||
#define ENCODER_VENDOR_STRING "Encoder-TAV 20251124 (3d-dwt,tad,ssf-tc,cdf53-motion,avx512,presets)"
|
#define ENCODER_VENDOR_STRING "Encoder-TAV 20251128 (3d-dwt,tad,ssf-tc,cdf53-motion,avx512,presets)"
|
||||||
|
|
||||||
// TSVM Advanced Video (TAV) format constants
|
// TSVM Advanced Video (TAV) format constants
|
||||||
#define TAV_MAGIC "\x1F\x54\x53\x56\x4D\x54\x41\x56" // "\x1FTSVM TAV"
|
#define TAV_MAGIC "\x1F\x54\x53\x56\x4D\x54\x41\x56" // "\x1FTSVM TAV"
|
||||||
@@ -12158,13 +12158,13 @@ int main(int argc, char *argv[]) {
|
|||||||
printf("Updated header with actual frame count: %d\n", frame_count);
|
printf("Updated header with actual frame count: %d\n", frame_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update ENDT in extended header (calculate end time for last frame)
|
// Update ENDT in extended header (calculate end time of video)
|
||||||
uint64_t endt_ns;
|
uint64_t endt_ns;
|
||||||
if (enc->is_ntsc_framerate) {
|
if (enc->is_ntsc_framerate) {
|
||||||
// NTSC framerates use denominator 1001 (e.g., 24000/1001, 30000/1001, 60000/1001)
|
// NTSC framerates use denominator 1001 (e.g., 24000/1001, 30000/1001, 60000/1001)
|
||||||
endt_ns = ((uint64_t)(frame_count - 1) * 1001ULL * 1000000000ULL) / ((uint64_t)enc->output_fps * 1000ULL);
|
endt_ns = ((uint64_t)frame_count * 1001ULL * 1000000000ULL) / ((uint64_t)enc->output_fps * 1000ULL);
|
||||||
} else {
|
} else {
|
||||||
endt_ns = ((uint64_t)(frame_count - 1) * 1000000000ULL) / (uint64_t)enc->output_fps;
|
endt_ns = ((uint64_t)frame_count * 1000000000ULL) / (uint64_t)enc->output_fps;
|
||||||
}
|
}
|
||||||
fseek(enc->output_fp, enc->extended_header_offset, SEEK_SET);
|
fseek(enc->output_fp, enc->extended_header_offset, SEEK_SET);
|
||||||
fwrite(&endt_ns, sizeof(uint64_t), 1, enc->output_fp);
|
fwrite(&endt_ns, sizeof(uint64_t), 1, enc->output_fp);
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ Audio: MP2 encoding at 96 kbps, 32 KHz stereo (packet 0x20)
|
|||||||
Each text frame is treated as an I-frame with sync packet
|
Each text frame is treated as an I-frame with sync packet
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
gcc -O3 -std=c11 -Wall encoder_tav_text.c -o encoder_tav_text -lm -lzstd
|
gcc -Ofast -std=c11 -Wall encoder_tav_text.c -o encoder_tav_text -lm -lzstd
|
||||||
./encoder_tav_text -i video.mp4 -f font.chr -o output.vtx
|
./encoder_tav_text -i video.mp4 -f font.chr -o output.mv3
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define _POSIX_C_SOURCE 200809L
|
#define _POSIX_C_SOURCE 200809L
|
||||||
@@ -93,6 +93,9 @@ static void generate_random_filename(char *filename) {
|
|||||||
|
|
||||||
char TEMP_AUDIO_FILE[42];
|
char TEMP_AUDIO_FILE[42];
|
||||||
|
|
||||||
|
// Global flag to disable inverted character matching
|
||||||
|
int g_no_invert_char = 0;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t *data; // Binary glyph data (PATCH_SZ bytes per glyph)
|
uint8_t *data; // Binary glyph data (PATCH_SZ bytes per glyph)
|
||||||
int count; // Number of glyphs
|
int count; // Number of glyphs
|
||||||
@@ -193,7 +196,7 @@ FontROM *load_font_rom(const char *path) {
|
|||||||
|
|
||||||
// Find best matching glyph for a grayscale patch
|
// Find best matching glyph for a grayscale patch
|
||||||
int find_best_glyph(const uint8_t *patch, const FontROM *rom, uint8_t *out_bg, uint8_t *out_fg) {
|
int find_best_glyph(const uint8_t *patch, const FontROM *rom, uint8_t *out_bg, uint8_t *out_fg) {
|
||||||
// Try both normal and inverted matching
|
// Try both normal and inverted matching (unless --no-invert-char is set)
|
||||||
int best_glyph = 0;
|
int best_glyph = 0;
|
||||||
float best_error = INFINITY;
|
float best_error = INFINITY;
|
||||||
uint8_t best_bg = COLOR_BLACK, best_fg = COLOR_WHITE;
|
uint8_t best_bg = COLOR_BLACK, best_fg = COLOR_WHITE;
|
||||||
@@ -209,25 +212,28 @@ int find_best_glyph(const uint8_t *patch, const FontROM *rom, uint8_t *out_bg, u
|
|||||||
err_normal += diff * diff;
|
err_normal += diff * diff;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try inverted: glyph 0 = fg, glyph 1 = bg
|
|
||||||
float err_inverted = 0;
|
|
||||||
for (int i = 0; i < PATCH_SZ; i++) {
|
|
||||||
int expected = glyph[i] ? 0 : 255;
|
|
||||||
int diff = patch[i] - expected;
|
|
||||||
err_inverted += diff * diff;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (err_normal < best_error) {
|
if (err_normal < best_error) {
|
||||||
best_error = err_normal;
|
best_error = err_normal;
|
||||||
best_glyph = g;
|
best_glyph = g;
|
||||||
best_bg = COLOR_BLACK;
|
best_bg = COLOR_BLACK;
|
||||||
best_fg = COLOR_WHITE;
|
best_fg = COLOR_WHITE;
|
||||||
}
|
}
|
||||||
if (err_inverted < best_error) {
|
|
||||||
best_error = err_inverted;
|
// Try inverted: glyph 0 = fg, glyph 1 = bg (skip if --no-invert-char)
|
||||||
best_glyph = g;
|
if (!g_no_invert_char) {
|
||||||
best_bg = COLOR_WHITE;
|
float err_inverted = 0;
|
||||||
best_fg = COLOR_BLACK;
|
for (int i = 0; i < PATCH_SZ; i++) {
|
||||||
|
int expected = glyph[i] ? 0 : 255;
|
||||||
|
int diff = patch[i] - expected;
|
||||||
|
err_inverted += diff * diff;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err_inverted < best_error) {
|
||||||
|
best_error = err_inverted;
|
||||||
|
best_glyph = g;
|
||||||
|
best_bg = COLOR_WHITE;
|
||||||
|
best_fg = COLOR_BLACK;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -479,7 +485,7 @@ void write_text_packet(FILE *f, const uint8_t *bg_col, const uint8_t *fg_col,
|
|||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
if (argc < 7) {
|
if (argc < 7) {
|
||||||
fprintf(stderr, "Usage: %s -i <video> -f <font.chr> -o <output.tav>\n", argv[0]);
|
fprintf(stderr, "Usage: %s -i <video> -f <font.chr> -o <output.tav> [--no-invert-char]\n", argv[0]);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -491,6 +497,7 @@ int main(int argc, char **argv) {
|
|||||||
if (strcmp(argv[i], "-i") == 0 && i+1 < argc) input_video = argv[++i];
|
if (strcmp(argv[i], "-i") == 0 && i+1 < argc) input_video = argv[++i];
|
||||||
else if (strcmp(argv[i], "-f") == 0 && i+1 < argc) font_path = argv[++i];
|
else if (strcmp(argv[i], "-f") == 0 && i+1 < argc) font_path = argv[++i];
|
||||||
else if (strcmp(argv[i], "-o") == 0 && i+1 < argc) output_path = argv[++i];
|
else if (strcmp(argv[i], "-o") == 0 && i+1 < argc) output_path = argv[++i];
|
||||||
|
else if (strcmp(argv[i], "--no-invert-char") == 0) g_no_invert_char = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!input_video || !font_path || !output_path) {
|
if (!input_video || !font_path || !output_path) {
|
||||||
@@ -498,6 +505,10 @@ int main(int argc, char **argv) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (g_no_invert_char) {
|
||||||
|
fprintf(stderr, "Inverted character matching disabled\n");
|
||||||
|
}
|
||||||
|
|
||||||
// Generate random temp filename for audio
|
// Generate random temp filename for audio
|
||||||
generate_random_filename(TEMP_AUDIO_FILE);
|
generate_random_filename(TEMP_AUDIO_FILE);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user