fix: FPS conversion not changing video header

This commit is contained in:
minjaesong
2025-09-05 00:30:17 +09:00
parent c89b977e91
commit 9ff12edecd
5 changed files with 111 additions and 44 deletions

View File

@@ -0,0 +1,21 @@
const fullFilePath = _G.shell.resolvePathInput(exec_args[1])
let seqread = undefined
let fullFilePathStr = fullFilePath.full
let mode = ""
// Select seqread driver to use
if (fullFilePathStr.startsWith('$:/TAPE') || fullFilePathStr.startsWith('$:\\TAPE')) {
seqread = require("seqreadtape")
seqread.prepare(fullFilePathStr)
seqread.seek(0)
mode = "tape"
} else {
seqread = undefined
}
if ("tape" == mode) {
const prg = seqread.readString(65536)
eval(prg)
}

View File

@@ -60,13 +60,13 @@ let fullFilePathStr = fullFilePath.full
// Select seqread driver to use
if (fullFilePathStr.startsWith('$:/TAPE') || fullFilePathStr.startsWith('$:\\TAPE')) {
seqread = seqreadtape
seqread.prepare(fullFilePathStr)
seqread.seek(0)
} else {
seqread = seqreadserial
seqread.prepare(fullFilePathStr)
}
seqread.prepare(fullFilePathStr)
con.clear()
con.curs_set(0)
graphics.setGraphicsMode(4) // 4096-color mode

View File

@@ -38,7 +38,10 @@ function hsdpaDisableSequentialIO() {
}
function hsdpaRewind() {
// send rewind command to the tape drive
sys.poke(HSDPA_REG_SEQ_IO_OPCODE, HSDPA_OPCODE_REWIND)
readCount = 0
}
function hsdpaSkip(bytes) {
@@ -46,8 +49,10 @@ function hsdpaSkip(bytes) {
sys.poke(HSDPA_REG_SEQ_IO_ARG1, bytes & 0xFF) // LSB
sys.poke(HSDPA_REG_SEQ_IO_ARG1 - 1, (bytes >> 8) & 0xFF) // MSB
sys.poke(HSDPA_REG_SEQ_IO_ARG1 - 2, (bytes >> 16) & 0xFF) // MSB2
// Execute skip operation
// Execute skip operation (tape drive should fast forward)
sys.poke(HSDPA_REG_SEQ_IO_OPCODE, HSDPA_OPCODE_SKIP)
readCount += bytes
}
function hsdpaReadToMemory(bytes, vmMemoryPointer) {
@@ -116,8 +121,7 @@ function prepare(fullPath) {
// Reset position for actual reading
hsdpaRewind()
readCount = 0
return 0
} catch (e) {
@@ -190,12 +194,19 @@ function readString(length) {
return s
}
function skip(n) {
if (n <= 0) return
// For HSDPA, we can skip efficiently without reading
hsdpaSkip(n)
readCount += n
function skip(n0) {
if (n0 <= 0) return
if (n0 < 16777215) {
hsdpaSkip(n0)
return
}
let n = n0
while (n > 0) {
let skiplen = Math.min(n, 16777215)
serial.println(`skip ${skiplen}; remaining: ${n}`)
hsdpaSkip(skiplen)
n -= skiplen
}
}
function getReadCount() {
@@ -226,12 +237,15 @@ function isReady() {
}
function seek(position) {
// Seek to absolute position
hsdpaRewind()
if (position > 0) {
hsdpaSkip(position)
let relPos = position - readCount
if (position == 0) {
return
} else if (position > 0) {
skip(relPos)
} else {
hsdpaRewind()
skip(position)
}
readCount = position
}
function rewind() { seek(0) }

View File

@@ -1318,23 +1318,46 @@ class GraphicsJSR223Delegate(private val vm: VM) {
val rgbAddrIncVec = if (rgbAddr >= 0) 1 else -1
val totalPixels = width * height
// Get native resolution
val nativeWidth = gpu.config.width
val nativeHeight = gpu.config.height
// Process in 8KB chunks to balance memory usage and performance
// Calculate centering offset
val offsetX = (nativeWidth - width) / 2
val offsetY = (nativeHeight - height) / 2
// Clear framebuffer with transparent pixels first
val transparentRG = 0.toByte() // r=0, g=0
val transparentBA = 0.toByte() // b=0, a=0 (transparent)
// Fill entire framebuffer with transparent pixels
val totalNativePixels = (nativeWidth * nativeHeight).toLong()
// UnsafeHelper.unsafe.setMemory(gpu.framebuffer.ptr, totalNativePixels, transparentRG)
// UnsafeHelper.unsafe.setMemory(gpu.framebuffer2!!.ptr, totalNativePixels, transparentBA)
// Process video pixels in 8KB chunks to balance memory usage and performance
val totalVideoPixels = width * height
val chunkSize = 8192
val rgChunk = ByteArray(chunkSize)
val baChunk = ByteArray(chunkSize)
val positionChunk = IntArray(chunkSize) // Store framebuffer positions
var pixelsProcessed = 0
while (pixelsProcessed < totalPixels) {
val pixelsInChunk = kotlin.math.min(chunkSize, totalPixels - pixelsProcessed)
while (pixelsProcessed < totalVideoPixels) {
val pixelsInChunk = kotlin.math.min(chunkSize, totalVideoPixels - pixelsProcessed)
// Batch process chunk of pixels
for (i in 0 until pixelsInChunk) {
val pixelIndex = pixelsProcessed + i
val y = pixelIndex / width
val x = pixelIndex % width
val videoY = pixelIndex / width
val videoX = pixelIndex % width
// Calculate position in native framebuffer (centered)
val nativeX = videoX + offsetX
val nativeY = videoY + offsetY
val nativePos = nativeY * nativeWidth + nativeX
positionChunk[i] = nativePos
val rgbOffset = (pixelIndex.toLong() * 3) * rgbAddrIncVec
@@ -1344,25 +1367,27 @@ class GraphicsJSR223Delegate(private val vm: VM) {
val b = vm.peek(rgbAddr + rgbOffset + rgbAddrIncVec * 2)!!.toUint()
// Apply Bayer dithering and convert to 4-bit
val r4 = ditherValue(r, x, y, frameCounter)
val g4 = ditherValue(g, x, y, frameCounter)
val b4 = ditherValue(b, x, y, frameCounter)
val r4 = ditherValue(r, videoX, videoY, frameCounter)
val g4 = ditherValue(g, videoX, videoY, frameCounter)
val b4 = ditherValue(b, videoX, videoY, frameCounter)
// Pack and store in chunk buffers
rgChunk[i] = ((r4 shl 4) or g4).toByte()
baChunk[i] = ((b4 shl 4) or 15).toByte()
}
// Batch write entire chunk to framebuffer
val pixelOffset = (pixelsProcessed).toLong()
gpu.let {
UnsafeHelper.memcpyRaw(
rgChunk, UnsafeHelper.getArrayOffset(rgChunk),
null, it.framebuffer.ptr + pixelOffset, pixelsInChunk.toLong())
UnsafeHelper.memcpyRaw(
baChunk, UnsafeHelper.getArrayOffset(baChunk),
null, it.framebuffer2!!.ptr + pixelOffset, pixelsInChunk.toLong())
// Write pixels to their calculated positions in framebuffer
for (i in 0 until pixelsInChunk) {
val pos = positionChunk[i].toLong()
// Bounds check to ensure we don't write outside framebuffer
if (pos in 0 until totalNativePixels) {
UnsafeHelper.memcpyRaw(
rgChunk, UnsafeHelper.getArrayOffset(rgChunk) + i,
null, gpu.framebuffer.ptr + pos, 1L)
UnsafeHelper.memcpyRaw(
baChunk, UnsafeHelper.getArrayOffset(baChunk) + i,
null, gpu.framebuffer2!!.ptr + pos, 1L)
}
}
pixelsProcessed += pixelsInChunk

View File

@@ -1527,7 +1527,7 @@ static int write_tev_header(FILE *output, tev_encoder_t *enc) {
// Video parameters
uint16_t width = enc->width;
uint16_t height = enc->progressive_mode ? enc->height : enc->height * 2;
uint8_t fps = enc->fps;
uint8_t fps = enc->output_fps;
uint32_t total_frames = enc->total_frames;
uint8_t qualityY = enc->qualityY;
uint8_t qualityCo = enc->qualityCo;
@@ -1780,13 +1780,19 @@ static int get_video_metadata(tev_encoder_t *config) {
config->total_frames = (int)(config->duration * config->fps);
}
// calculate new total_frames if user has requested custom framerate
float inputFramerate;
if (config->is_ntsc_framerate) {
inputFramerate = config->fps * 1000.f / 1001.f;
} else {
inputFramerate = config->fps * 1.f;
}
config->total_frames = (int)(config->total_frames * (config->output_fps / inputFramerate));
fprintf(stderr, "Video metadata:\n");
fprintf(stderr, " Frames: %d\n", config->total_frames);
if (config->is_ntsc_framerate) {
fprintf(stderr, " FPS: %.2f\n", config->fps * 1000.f / 1001.f);
} else {
fprintf(stderr, " FPS: %d\n", config->fps);
}
fprintf(stderr, " FPS: %.2f\n", inputFramerate);
fprintf(stderr, " Duration: %.2fs\n", config->duration);
fprintf(stderr, " Audio: %s\n", config->has_audio ? "Yes" : "No");
fprintf(stderr, " Resolution: %dx%d (%s)\n", config->width, config->height,
@@ -1923,7 +1929,7 @@ static int process_audio(tev_encoder_t *enc, int frame_num, FILE *output) {
}
// Calculate how much audio time each frame represents (in seconds)
double frame_audio_time = 1.0 / enc->fps;
double frame_audio_time = 1.0 / enc->output_fps;
// Calculate how much audio time each MP2 packet represents
// MP2 frame contains 1152 samples at 32kHz = 0.036 seconds
@@ -2198,6 +2204,7 @@ int main(int argc, char *argv[]) {
if (test_mode) {
// Test mode: generate solid colour frames
enc->fps = 1;
enc->output_fps = 1;
enc->total_frames = 15;
enc->has_audio = 0;
printf("Test mode: Generating 15 solid colour frames\n");
@@ -2217,7 +2224,7 @@ int main(int argc, char *argv[]) {
int format = detect_subtitle_format(enc->subtitle_file);
const char *format_name = (format == 1) ? "SAMI" : "SubRip";
enc->subtitle_list = parse_subtitle_file(enc->subtitle_file, enc->fps);
enc->subtitle_list = parse_subtitle_file(enc->subtitle_file, enc->output_fps);
if (enc->subtitle_list) {
enc->has_subtitles = 1;
enc->current_subtitle = enc->subtitle_list;
@@ -2398,7 +2405,7 @@ int main(int argc, char *argv[]) {
printf("\nEncoding complete!\n");
printf(" Frames encoded: %d\n", frame_count);
printf(" - sync packets: %d\n", sync_packet_count);
printf(" Framerate: %d\n", enc->fps);
printf(" Framerate: %d\n", enc->output_fps);
printf(" Output size: %zu bytes\n", enc->total_output_bytes);
// Calculate achieved bitrate