mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-03-07 19:51:51 +09:00
ycocg wip
This commit is contained in:
@@ -16,19 +16,15 @@ con.clear();con.curs_set(0)
|
||||
graphics.clearPixels(255)
|
||||
graphics.clearPixels2(240)
|
||||
|
||||
|
||||
let seqreadserial = require("seqread")
|
||||
let seqreadtape = require("seqreadtape")
|
||||
let seqread = undefined
|
||||
let fullFilePathStr = fullFilePath.full
|
||||
|
||||
// select seqread driver to use
|
||||
if (fullFilePathStr.startsWith('$:/TAPE') || fullFilePathStr.startsWith('$:\\TAPE')) {
|
||||
seqread = seqreadtape
|
||||
seqread.seek(0)
|
||||
seqread = require("seqreadtape")
|
||||
}
|
||||
else {
|
||||
seqread = seqreadserial
|
||||
seqread = require("seqread")
|
||||
}
|
||||
|
||||
seqread.prepare(fullFilePathStr)
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
// Created by Claude on 2025-08-17.
|
||||
// TSVM Enhanced Video (TEV) Format Decoder
|
||||
// Created by Claude on 2025-08-18.
|
||||
// TSVM Enhanced Video (TEV) Format Decoder - YCoCg-R 4:2:0 Version
|
||||
// Usage: playtev moviefile.tev [options]
|
||||
|
||||
const WIDTH = 560
|
||||
const HEIGHT = 448
|
||||
const BLOCK_SIZE = 8
|
||||
const BLOCK_SIZE = 16 // 16x16 blocks for YCoCg-R
|
||||
const TEV_MAGIC = [0x1F, 0x54, 0x53, 0x56, 0x4D, 0x54, 0x45, 0x56] // "\x1FTSVM TEV"
|
||||
const TEV_VERSION = 2 // YCoCg-R version
|
||||
|
||||
// Block encoding modes
|
||||
const TEV_MODE_SKIP = 0x00
|
||||
@@ -23,76 +24,80 @@ const interactive = exec_args[2] && exec_args[2].toLowerCase() == "-i"
|
||||
const fullFilePath = _G.shell.resolvePathInput(exec_args[1])
|
||||
const FILE_LENGTH = files.open(fullFilePath.full).size
|
||||
|
||||
// Quantization tables (8 quality levels)
|
||||
const QUANT_TABLES = [
|
||||
// Quality 0 (lowest)
|
||||
[80, 60, 50, 80, 120, 200, 255, 255,
|
||||
55, 60, 70, 95, 130, 255, 255, 255,
|
||||
70, 65, 80, 120, 200, 255, 255, 255,
|
||||
70, 85, 110, 145, 255, 255, 255, 255,
|
||||
90, 110, 185, 255, 255, 255, 255, 255,
|
||||
120, 175, 255, 255, 255, 255, 255, 255,
|
||||
245, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255],
|
||||
// Quality 1-6 (simplified)
|
||||
[40, 30, 25, 40, 60, 100, 128, 150,
|
||||
28, 30, 35, 48, 65, 128, 150, 180,
|
||||
35, 33, 40, 60, 100, 128, 150, 180,
|
||||
35, 43, 55, 73, 128, 150, 180, 200,
|
||||
45, 55, 93, 128, 150, 180, 200, 220,
|
||||
60, 88, 128, 150, 180, 200, 220, 240,
|
||||
123, 128, 150, 180, 200, 220, 240, 250,
|
||||
128, 150, 180, 200, 220, 240, 250, 255],
|
||||
[20, 15, 13, 20, 30, 50, 64, 75,
|
||||
14, 15, 18, 24, 33, 64, 75, 90,
|
||||
18, 17, 20, 30, 50, 64, 75, 90,
|
||||
18, 22, 28, 37, 64, 75, 90, 100,
|
||||
23, 28, 47, 64, 75, 90, 100, 110,
|
||||
30, 44, 64, 75, 90, 100, 110, 120,
|
||||
62, 64, 75, 90, 100, 110, 120, 125,
|
||||
64, 75, 90, 100, 110, 120, 125, 128],
|
||||
[16, 12, 10, 16, 24, 40, 51, 60,
|
||||
11, 12, 14, 19, 26, 51, 60, 72,
|
||||
14, 13, 16, 24, 40, 51, 60, 72,
|
||||
14, 17, 22, 29, 51, 60, 72, 80,
|
||||
18, 22, 37, 51, 60, 72, 80, 88,
|
||||
24, 35, 51, 60, 72, 80, 88, 96,
|
||||
49, 51, 60, 72, 80, 88, 96, 100,
|
||||
51, 60, 72, 80, 88, 96, 100, 102],
|
||||
[12, 9, 8, 12, 18, 30, 38, 45,
|
||||
8, 9, 11, 14, 20, 38, 45, 54,
|
||||
11, 10, 12, 18, 30, 38, 45, 54,
|
||||
11, 13, 17, 22, 38, 45, 54, 60,
|
||||
14, 17, 28, 38, 45, 54, 60, 66,
|
||||
18, 26, 38, 45, 54, 60, 66, 72,
|
||||
37, 38, 45, 54, 60, 66, 72, 75,
|
||||
38, 45, 54, 60, 66, 72, 75, 77],
|
||||
[10, 7, 6, 10, 15, 25, 32, 38,
|
||||
7, 7, 9, 12, 16, 32, 38, 45,
|
||||
9, 8, 10, 15, 25, 32, 38, 45,
|
||||
9, 11, 14, 18, 32, 38, 45, 50,
|
||||
12, 14, 23, 32, 38, 45, 50, 55,
|
||||
15, 22, 32, 38, 45, 50, 55, 60,
|
||||
31, 32, 38, 45, 50, 55, 60, 63,
|
||||
32, 38, 45, 50, 55, 60, 63, 65],
|
||||
[8, 6, 5, 8, 12, 20, 26, 30,
|
||||
6, 6, 7, 10, 13, 26, 30, 36,
|
||||
7, 7, 8, 12, 20, 26, 30, 36,
|
||||
7, 9, 11, 15, 26, 30, 36, 40,
|
||||
10, 11, 19, 26, 30, 36, 40, 44,
|
||||
12, 17, 26, 30, 36, 40, 44, 48,
|
||||
25, 26, 30, 36, 40, 44, 48, 50,
|
||||
26, 30, 36, 40, 44, 48, 50, 52],
|
||||
// Quantization tables for Y channel (16x16 - just use first 8 quality levels)
|
||||
const QUANT_TABLES_Y = [
|
||||
// Quality 0 (lowest) - 8x8 pattern repeated to 16x16
|
||||
(() => {
|
||||
const base = [80, 60, 50, 80, 120, 200, 255, 255,
|
||||
55, 60, 70, 95, 130, 255, 255, 255,
|
||||
70, 65, 80, 120, 200, 255, 255, 255,
|
||||
70, 85, 110, 145, 255, 255, 255, 255,
|
||||
90, 110, 185, 255, 255, 255, 255, 255,
|
||||
120, 175, 255, 255, 255, 255, 255, 255,
|
||||
245, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255]
|
||||
const extended = []
|
||||
for (let y = 0; y < 16; y++) {
|
||||
for (let x = 0; x < 16; x++) {
|
||||
extended.push(base[(y % 8) * 8 + (x % 8)])
|
||||
}
|
||||
}
|
||||
return extended
|
||||
})(),
|
||||
[40, 30, 25, 40, 60, 100, 128, 150, 28, 30, 35, 48, 65, 128, 150, 180], // Quality 1 (simplified)
|
||||
[20, 15, 13, 20, 30, 50, 64, 75, 14, 15, 18, 24, 33, 64, 75, 90], // Quality 2
|
||||
[16, 12, 10, 16, 24, 40, 51, 60, 11, 12, 14, 19, 26, 51, 60, 72], // Quality 3
|
||||
[12, 9, 8, 12, 18, 30, 38, 45, 8, 9, 11, 14, 20, 38, 45, 54], // Quality 4
|
||||
[10, 7, 6, 10, 15, 25, 32, 38, 7, 7, 9, 12, 16, 32, 38, 45], // Quality 5
|
||||
[8, 6, 5, 8, 12, 20, 26, 30, 6, 6, 7, 10, 13, 26, 30, 36], // Quality 6
|
||||
// Quality 7 (highest)
|
||||
[2, 1, 1, 2, 3, 5, 6, 7,
|
||||
1, 1, 1, 2, 3, 6, 7, 9,
|
||||
1, 1, 2, 3, 5, 6, 7, 9,
|
||||
1, 2, 3, 4, 6, 7, 9, 10,
|
||||
2, 3, 5, 6, 7, 9, 10, 11,
|
||||
3, 4, 6, 7, 9, 10, 11, 12,
|
||||
6, 6, 7, 9, 10, 11, 12, 13,
|
||||
6, 7, 9, 10, 11, 12, 13, 13]
|
||||
(() => {
|
||||
const base = [2, 1, 1, 2, 3, 5, 6, 7,
|
||||
1, 1, 1, 2, 3, 6, 7, 9,
|
||||
1, 1, 2, 3, 5, 6, 7, 9,
|
||||
1, 2, 3, 4, 6, 7, 9, 10,
|
||||
2, 3, 5, 6, 7, 9, 10, 11,
|
||||
3, 4, 6, 7, 9, 10, 11, 12,
|
||||
6, 6, 7, 9, 10, 11, 12, 13,
|
||||
6, 7, 9, 10, 11, 12, 13, 13]
|
||||
const extended = []
|
||||
for (let y = 0; y < 16; y++) {
|
||||
for (let x = 0; x < 16; x++) {
|
||||
extended.push(base[(y % 8) * 8 + (x % 8)])
|
||||
}
|
||||
}
|
||||
return extended
|
||||
})()
|
||||
]
|
||||
|
||||
// Quantization tables for chroma channels (8x8)
|
||||
const QUANT_TABLES_C = [
|
||||
// Quality 0 (lowest)
|
||||
[120, 90, 75, 120, 180, 255, 255, 255,
|
||||
83, 90, 105, 143, 195, 255, 255, 255,
|
||||
105, 98, 120, 180, 255, 255, 255, 255,
|
||||
105, 128, 165, 218, 255, 255, 255, 255,
|
||||
135, 165, 278, 255, 255, 255, 255, 255,
|
||||
180, 263, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255],
|
||||
[60, 45, 38, 60, 90, 150, 192, 225], // Quality 1 (simplified)
|
||||
[30, 23, 19, 30, 45, 75, 96, 113], // Quality 2
|
||||
[24, 18, 15, 24, 36, 60, 77, 90], // Quality 3
|
||||
[18, 14, 12, 18, 27, 45, 57, 68], // Quality 4
|
||||
[15, 11, 9, 15, 23, 38, 48, 57], // Quality 5
|
||||
[12, 9, 8, 12, 18, 30, 39, 45], // Quality 6
|
||||
// Quality 7 (highest)
|
||||
[3, 2, 2, 3, 5, 8, 9, 11,
|
||||
2, 2, 2, 3, 5, 9, 11, 14,
|
||||
2, 2, 3, 5, 8, 9, 11, 14,
|
||||
2, 3, 5, 6, 9, 11, 14, 15,
|
||||
3, 5, 8, 9, 11, 14, 15, 17,
|
||||
5, 6, 9, 11, 14, 15, 17, 18,
|
||||
9, 9, 11, 14, 15, 17, 18, 20,
|
||||
9, 11, 14, 15, 17, 18, 20, 20]
|
||||
]
|
||||
|
||||
let videoRateBin = []
|
||||
let errorlevel = 0
|
||||
let notifHideTimer = 0
|
||||
@@ -146,17 +151,20 @@ if (!magicMatching) {
|
||||
|
||||
// Read header
|
||||
let version = seqread.readOneByte()
|
||||
let flags = seqread.readOneByte()
|
||||
if (version !== TEV_VERSION) {
|
||||
println(`Unsupported TEV version: ${version} (expected ${TEV_VERSION})`)
|
||||
return 1
|
||||
}
|
||||
|
||||
let width = seqread.readShort()
|
||||
let height = seqread.readShort()
|
||||
let fps = seqread.readShort()
|
||||
let fps = seqread.readOneByte()
|
||||
let totalFrames = seqread.readInt()
|
||||
let quality = seqread.readOneByte()
|
||||
seqread.skip(5) // Reserved bytes
|
||||
let hasAudio = seqread.readOneByte()
|
||||
|
||||
function updateDataRateBin(rate) {
|
||||
videoRateBin.push(rate)
|
||||
|
||||
if (videoRateBin.length > fps) {
|
||||
videoRateBin.shift()
|
||||
}
|
||||
@@ -168,13 +176,8 @@ function getVideoRate(rate) {
|
||||
return baseRate * mult
|
||||
}
|
||||
|
||||
let hasAudio = (flags & 0x01) != 0
|
||||
let frameTime = 1.0 / fps
|
||||
|
||||
//println(`TEV Video: ${width}x${height}, ${fps} FPS, ${totalFrames} frames, Q${quality}`)
|
||||
//if (hasAudio) println("Audio: MP2 32kHz")
|
||||
//println(`Blocks: ${(width + 7) >> 3}x${(height + 7) >> 3} (${((width + 7) >> 3) * ((height + 7) >> 3)} total)`)
|
||||
|
||||
// Ultra-fast approach: always render to display, use dedicated previous frame buffer
|
||||
const FRAME_PIXELS = width * height
|
||||
|
||||
@@ -187,8 +190,8 @@ const CURRENT_RGB_ADDR = sys.malloc(560*448*3) // Current frame RGB buffer
|
||||
const PREV_RGB_ADDR = sys.malloc(560*448*3) // Previous frame RGB buffer
|
||||
|
||||
// Working memory for blocks (minimal allocation)
|
||||
let rgbWorkspace = sys.malloc(BLOCK_SIZE * BLOCK_SIZE * 3) // 192 bytes
|
||||
let dctWorkspace = sys.malloc(BLOCK_SIZE * BLOCK_SIZE * 3 * 4) // 768 bytes (floats)
|
||||
let ycocgWorkspace = sys.malloc(BLOCK_SIZE * BLOCK_SIZE * 3) // Y+Co+Cg workspace
|
||||
let dctWorkspace = sys.malloc(BLOCK_SIZE * BLOCK_SIZE * 4) // DCT coefficients (floats)
|
||||
|
||||
// Initialize RGB frame buffers to black (0,0,0)
|
||||
for (let i = 0; i < FRAME_PIXELS; i++) {
|
||||
@@ -212,16 +215,6 @@ for (let i = 0; i < FRAME_PIXELS; i++) {
|
||||
let frameCount = 0
|
||||
let stopPlay = false
|
||||
|
||||
// Dequantize DCT coefficient
|
||||
function dequantizeCoeff(coeff, quant, isDC) {
|
||||
if (isDC) {
|
||||
// DC coefficient also needs dequantization
|
||||
return coeff * quant
|
||||
} else {
|
||||
return coeff * quant
|
||||
}
|
||||
}
|
||||
|
||||
// 4x4 Bayer dithering matrix
|
||||
const BAYER_MATRIX = [
|
||||
[ 0, 8, 2,10],
|
||||
@@ -243,144 +236,6 @@ function ditherValue(value, x, y) {
|
||||
return Math.max(0, Math.min(15, Math.floor(dithered * 15 / 255)))
|
||||
}
|
||||
|
||||
// 8x8 Inverse DCT implementation
|
||||
function idct8x8(coeffs, quantTable) {
|
||||
const N = 8
|
||||
let block = new Array(64)
|
||||
|
||||
// Dequantize coefficients
|
||||
for (let i = 0; i < 64; i++) {
|
||||
block[i] = dequantizeCoeff(coeffs[i], quantTable[i], i === 0)
|
||||
}
|
||||
|
||||
// IDCT constants
|
||||
const cos = Math.cos
|
||||
const sqrt2 = Math.sqrt(2)
|
||||
const c = new Array(8)
|
||||
c[0] = 1.0 / sqrt2
|
||||
for (let i = 1; i < 8; i++) {
|
||||
c[i] = 1.0
|
||||
}
|
||||
|
||||
let result = new Array(64)
|
||||
|
||||
// 2D IDCT
|
||||
for (let x = 0; x < N; x++) {
|
||||
for (let y = 0; y < N; y++) {
|
||||
let sum = 0.0
|
||||
for (let u = 0; u < N; u++) {
|
||||
for (let v = 0; v < N; v++) {
|
||||
let coeff = block[v * N + u]
|
||||
let cosU = cos((2 * x + 1) * u * Math.PI / (2 * N))
|
||||
let cosV = cos((2 * y + 1) * v * Math.PI / (2 * N))
|
||||
sum += c[u] * c[v] * coeff * cosU * cosV
|
||||
}
|
||||
}
|
||||
result[y * N + x] = sum / 4.0
|
||||
}
|
||||
}
|
||||
|
||||
// Convert to pixel values (0-255)
|
||||
for (let i = 0; i < 64; i++) {
|
||||
result[i] = Math.max(0, Math.min(255, Math.round(result[i] + 128)))
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Hardware-accelerated decoding uses graphics.tevIdct8x8() instead of pure JS
|
||||
|
||||
// Hardware-accelerated TEV block decoder
|
||||
function decodeBlock(blockData, blockX, blockY, prevRG, prevBA, currRG, currBA, quantTable) {
|
||||
let mode = blockData.mode
|
||||
let startX = blockX * BLOCK_SIZE
|
||||
let startY = blockY * BLOCK_SIZE
|
||||
|
||||
if (mode == TEV_MODE_SKIP) {
|
||||
// Copy from previous frame
|
||||
for (let dy = 0; dy < BLOCK_SIZE; dy++) {
|
||||
for (let dx = 0; dx < BLOCK_SIZE; dx++) {
|
||||
let x = startX + dx
|
||||
let y = startY + dy
|
||||
if (x < width && y < height) {
|
||||
let offset = y * width + x
|
||||
let prevRGVal = sys.peek(prevRG + offset)
|
||||
let prevBAVal = sys.peek(prevBA + offset)
|
||||
sys.poke(currRG - offset, prevRGVal) // Graphics memory uses negative addressing
|
||||
sys.poke(currBA - offset, prevBAVal)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (mode == TEV_MODE_MOTION) {
|
||||
// Motion compensation: copy from previous frame with motion vector offset
|
||||
for (let dy = 0; dy < BLOCK_SIZE; dy++) {
|
||||
for (let dx = 0; dx < BLOCK_SIZE; dx++) {
|
||||
let x = startX + dx
|
||||
let y = startY + dy
|
||||
let refX = x + blockData.mvX
|
||||
let refY = y + blockData.mvY
|
||||
|
||||
if (x < width && y < height && refX >= 0 && refX < width && refY >= 0 && refY < height) {
|
||||
let dstOffset = y * width + x
|
||||
let refOffset = refY * width + refX
|
||||
let refRGVal = sys.peek(prevRG + refOffset)
|
||||
let refBAVal = sys.peek(prevBA + refOffset)
|
||||
sys.poke(currRG - dstOffset, refRGVal) // Graphics memory uses negative addressing
|
||||
sys.poke(currBA - dstOffset, refBAVal)
|
||||
} else if (x < width && y < height) {
|
||||
// Out of bounds reference - use black
|
||||
let dstOffset = y * width + x
|
||||
sys.poke(currRG - dstOffset, 0) // Graphics memory uses negative addressing
|
||||
sys.poke(currBA - dstOffset, 15)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// INTRA or INTER modes: Full DCT decoding
|
||||
|
||||
// Extract DCT coefficients for each channel (R, G, B)
|
||||
let rCoeffs = blockData.dctCoeffs.slice(0 * 64, 1 * 64) // R channel
|
||||
let gCoeffs = blockData.dctCoeffs.slice(1 * 64, 2 * 64) // G channel
|
||||
let bCoeffs = blockData.dctCoeffs.slice(2 * 64, 3 * 64) // B channel
|
||||
|
||||
// Perform IDCT for each channel
|
||||
let rBlock = idct8x8(rCoeffs, quantTable)
|
||||
let gBlock = idct8x8(gCoeffs, quantTable)
|
||||
let bBlock = idct8x8(bCoeffs, quantTable)
|
||||
|
||||
// Fill 8x8 block with IDCT results
|
||||
for (let dy = 0; dy < BLOCK_SIZE; dy++) {
|
||||
for (let dx = 0; dx < BLOCK_SIZE; dx++) {
|
||||
let x = startX + dx
|
||||
let y = startY + dy
|
||||
if (x < width && y < height) {
|
||||
let blockOffset = dy * BLOCK_SIZE + dx
|
||||
let imageOffset = y * width + x
|
||||
|
||||
// Get RGB values from IDCT results
|
||||
let r = rBlock[blockOffset]
|
||||
let g = gBlock[blockOffset]
|
||||
let b = bBlock[blockOffset]
|
||||
|
||||
// Apply Bayer dithering when converting to 4-bit values
|
||||
let r4 = ditherValue(r, x, y)
|
||||
let g4 = ditherValue(g, x, y)
|
||||
let b4 = ditherValue(b, x, y)
|
||||
|
||||
let rgValue = (r4 << 4) | g4 // R in MSB, G in LSB
|
||||
let baValue = (b4 << 4) | 15 // B in MSB, A=15 (opaque) in LSB
|
||||
|
||||
// Write to graphics memory
|
||||
sys.poke(currRG - imageOffset, rgValue) // Graphics memory uses negative addressing
|
||||
sys.poke(currBA - imageOffset, baValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Secondary buffers removed - using frame buffers directly
|
||||
|
||||
// Main decoding loop - simplified for performance
|
||||
try {
|
||||
while (!stopPlay && seqread.getReadCount() < FILE_LENGTH && frameCount < totalFrames) {
|
||||
@@ -393,18 +248,21 @@ try {
|
||||
}
|
||||
}
|
||||
|
||||
// Read packet (2 bytes: type + subtype)
|
||||
let packetType = seqread.readShort()
|
||||
// Read packet (1 byte: type)
|
||||
let packetType = seqread.readOneByte()
|
||||
|
||||
if (packetType == 0xFFFF) { // Sync packet
|
||||
if (packetType == 0xFF) { // Sync packet
|
||||
// Read length (should be 0)
|
||||
let syncLen = seqread.readInt()
|
||||
|
||||
// Sync packet - frame complete
|
||||
frameCount++
|
||||
|
||||
// Copy current RGB frame to previous frame buffer for next frame reference
|
||||
// This is the only copying we need, and it happens once per frame after display
|
||||
sys.memcpy(CURRENT_RGB_ADDR, PREV_RGB_ADDR, FRAME_PIXELS * 3)
|
||||
sys.memcpy(PREV_RGB_ADDR, CURRENT_RGB_ADDR, FRAME_PIXELS * 3)
|
||||
|
||||
} else if ((packetType & 0xFF) == TEV_PACKET_IFRAME || (packetType & 0xFF) == TEV_PACKET_PFRAME) {
|
||||
} else if (packetType == TEV_PACKET_IFRAME || packetType == TEV_PACKET_PFRAME) {
|
||||
// Video frame packet
|
||||
let payloadLen = seqread.readInt()
|
||||
let compressedPtr = seqread.readBytes(payloadLen)
|
||||
@@ -417,16 +275,15 @@ try {
|
||||
continue
|
||||
}
|
||||
|
||||
// Decompress using zstd (if available) or gzip fallback
|
||||
// Calculate proper buffer size for TEV blocks (conservative estimate)
|
||||
let blocksX = (width + 7) >> 3
|
||||
let blocksY = (height + 7) >> 3
|
||||
let tevBlockSize = 1 + 4 + 2 + (64 * 3 * 2) // mode + mv + cbp + dct_coeffs
|
||||
// Decompress using gzip
|
||||
// Calculate proper buffer size for TEV YCoCg-R blocks
|
||||
let blocksX = (width + 15) >> 4 // 16x16 blocks
|
||||
let blocksY = (height + 15) >> 4
|
||||
let tevBlockSize = 1 + 4 + 2 + (256 * 2) + (64 * 2) + (64 * 2) // mode + mv + cbp + Y(16x16) + Co(8x8) + Cg(8x8)
|
||||
let decompressedSize = blocksX * blocksY * tevBlockSize * 2 // Double for safety
|
||||
let blockDataPtr = sys.malloc(decompressedSize)
|
||||
|
||||
let actualSize
|
||||
let decompMethod = "gzip"
|
||||
try {
|
||||
// Use gzip decompression (only compression format supported in TSVM JS)
|
||||
actualSize = gzip.decompFromTo(compressedPtr, payloadLen, blockDataPtr)
|
||||
@@ -438,9 +295,7 @@ try {
|
||||
continue
|
||||
}
|
||||
|
||||
// Hardware decode complete
|
||||
|
||||
// Hardware-accelerated TEV decoding to RGB buffers (blazing fast!)
|
||||
// Hardware-accelerated TEV YCoCg-R decoding to RGB buffers
|
||||
try {
|
||||
graphics.tevDecode(blockDataPtr, CURRENT_RGB_ADDR, PREV_RGB_ADDR,
|
||||
width, height, quality)
|
||||
@@ -449,13 +304,13 @@ try {
|
||||
graphics.uploadRGBToFramebuffer(CURRENT_RGB_ADDR, DISPLAY_RG_ADDR, DISPLAY_BA_ADDR,
|
||||
width, height, frameCount)
|
||||
} catch (e) {
|
||||
serial.println(`Frame ${frameCount}: Hardware decode failed: ${e}`)
|
||||
serial.println(`Frame ${frameCount}: Hardware YCoCg-R decode failed: ${e}`)
|
||||
}
|
||||
|
||||
sys.free(blockDataPtr)
|
||||
sys.free(compressedPtr)
|
||||
|
||||
} else if ((packetType & 0xFF) == TEV_PACKET_AUDIO_MP2) {
|
||||
} else if (packetType == TEV_PACKET_AUDIO_MP2) {
|
||||
// Audio packet - skip for now
|
||||
let audioLen = seqread.readInt()
|
||||
seqread.skip(audioLen)
|
||||
@@ -469,7 +324,7 @@ try {
|
||||
if (interactive) {
|
||||
con.move(31, 1)
|
||||
graphics.setTextFore(161)
|
||||
print(`Frame: ${frameCount}/${totalFrames} (${Math.round(frameCount * 100 / totalFrames)}%)`)
|
||||
print(`Frame: ${frameCount}/${totalFrames} (${Math.round(frameCount * 100 / totalFrames)}%) YCoCg-R`)
|
||||
con.move(32, 1)
|
||||
graphics.setTextFore(161)
|
||||
print(`VRate: ${(getVideoRate() / 1024 * 8)|0} kbps `)
|
||||
@@ -478,16 +333,15 @@ try {
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
printerrln(`TEV decode error: ${e}`)
|
||||
printerrln(`TEV YCoCg-R decode error: ${e}`)
|
||||
errorlevel = 1
|
||||
} finally {
|
||||
// Cleanup working memory (graphics memory is automatically managed)
|
||||
sys.free(rgbWorkspace)
|
||||
sys.free(ycocgWorkspace)
|
||||
sys.free(dctWorkspace)
|
||||
sys.free(CURRENT_RGB_ADDR)
|
||||
sys.free(PREV_RGB_ADDR)
|
||||
|
||||
|
||||
audio.stop(0)
|
||||
audio.purgeQueue(0)
|
||||
|
||||
@@ -496,4 +350,5 @@ try {
|
||||
}
|
||||
}
|
||||
|
||||
con.move(cy, cx) // restore cursor
|
||||
return errorlevel
|
||||
@@ -251,5 +251,6 @@ exports = {
|
||||
getCurrentTapeDevice,
|
||||
isReady,
|
||||
// Enhanced functions
|
||||
seek
|
||||
seek,
|
||||
rewind
|
||||
}
|
||||
Reference in New Issue
Block a user