diff --git a/assets/disk0/tvdos/bin/playtav.js b/assets/disk0/tvdos/bin/playtav.js index a3c7906..3f430ba 100644 --- a/assets/disk0/tvdos/bin/playtav.js +++ b/assets/disk0/tvdos/bin/playtav.js @@ -2,9 +2,7 @@ // TSVM Advanced Video (TAV) Format Decoder - DWT-based compression // Adapted from the working playtev.js decoder // Usage: playtav moviefile.tav [options] -// Options: -i (interactive), -debug-mv (show motion vector debug visualization) -// -deinterlace=algorithm (yadif or bwdif, default: yadif) -// -deblock (enable post-processing deblocking filter) +// Options: -i (interactive) const WIDTH = 560 const HEIGHT = 448 @@ -70,7 +68,7 @@ let notifHideTimer = 0 const NOTIF_SHOWUPTIME = 3000000000 let [cy, cx] = con.getyx() -//let playgui = require("playgui") +let gui = require("playgui") let seqread = undefined let fullFilePathStr = fullFilePath.full @@ -96,176 +94,9 @@ audio.purgeQueue(0) audio.setPcmMode(0) audio.setMasterVolume(0, 255) -// Subtitle display functions -function clearSubtitleArea() { - // Clear the subtitle area at the bottom of the screen - // Text mode is 80x32, so clear the bottom few lines - let oldFgColour = con.get_color_fore() - let oldBgColour = con.get_color_back() +// set colour zero as half-opaque black +graphics.setPalette(0, 0, 0, 0, 9) - con.color_pair(255, 255) // transparent to clear - - // Clear bottom 4 lines for subtitles - for (let row = 29; row <= 32; row++) { - con.move(row, 1) - for (let col = 1; col <= 80; col++) { - print(" ") - } - } - - con.color_pair(oldFgColour, oldBgColour) -} - -function getVisualLength(line) { - // Calculate the visual length of a line excluding formatting tags - let visualLength = 0 - let i = 0 - - while (i < line.length) { - if (i < line.length - 2 && line[i] === '<') { - // Check for formatting tags and skip them - if (line.substring(i, i + 3).toLowerCase() === '' || - line.substring(i, i + 3).toLowerCase() === '') { - i += 3 // Skip tag - } else if (i < line.length - 3 && - (line.substring(i, i + 4).toLowerCase() === '' || - line.substring(i, i + 4).toLowerCase() === '')) { - i += 4 // Skip closing tag - } else { - // Not a formatting tag, count the character - visualLength++ - i++ - } - } else { - // Regular character, count it - visualLength++ - i++ - } - } - - return visualLength -} - -function displayFormattedLine(line) { - // Parse line and handle and tags with colour changes - // Default subtitle colour: yellow (231), formatted text: white (254) - - let i = 0 - let inBoldOrItalic = false - - // insert initial padding block - con.color_pair(0, 255) - con.prnch(0xDE) - con.color_pair(231, 0) - - while (i < line.length) { - if (i < line.length - 2 && line[i] === '<') { - // Check for opening tags - if (line.substring(i, i + 3).toLowerCase() === '' || - line.substring(i, i + 3).toLowerCase() === '') { - con.color_pair(254, 0) // Switch to white for formatted text - inBoldOrItalic = true - i += 3 - } else if (i < line.length - 3 && - (line.substring(i, i + 4).toLowerCase() === '' || - line.substring(i, i + 4).toLowerCase() === '')) { - con.color_pair(231, 0) // Switch back to yellow for normal text - inBoldOrItalic = false - i += 4 - } else { - // Not a formatting tag, print the character - print(line[i]) - i++ - } - } else { - // Regular character, print it - print(line[i]) - i++ - } - } - - // insert final padding block - con.color_pair(0, 255) - con.prnch(0xDD) - con.color_pair(231, 0) -} - -function displaySubtitle(text, position = 0) { - if (!text || text.length === 0) { - clearSubtitleArea() - return - } - - // Set subtitle colours: yellow (231) on black (0) - let oldFgColour = con.get_color_fore() - let oldBgColour = con.get_color_back() - con.color_pair(231, 0) - - // Split text into lines - let lines = text.split('\n') - - // Calculate position based on subtitle position setting - let startRow, startCol - // Calculate visual length without formatting tags for positioning - let longestLineLength = lines.map(s => getVisualLength(s)).sort().last() - - switch (position) { - case 2: // center left - case 6: // center right - case 8: // dead center - startRow = 16 - Math.floor(lines.length / 2) - break - case 3: // top left - case 4: // top center - case 5: // top right - startRow = 2 - break - case 0: // bottom center - case 1: // bottom left - case 7: // bottom right - default: - startRow = 32 - lines.length - startRow = 32 - lines.length - startRow = 32 - lines.length // Default to bottom center - } - - // Display each line - for (let i = 0; i < lines.length; i++) { - let line = lines[i].trim() - if (line.length === 0) continue - - let row = startRow + i - if (row < 1) row = 1 - if (row > 32) row = 32 - - // Calculate column based on alignment - switch (position) { - case 1: // bottom left - case 2: // center left - case 3: // top left - startCol = 1 - break - case 5: // top right - case 6: // center right - case 7: // bottom right - startCol = Math.max(1, 78 - getVisualLength(line) - 2) - break - case 0: // bottom center - case 4: // top center - case 8: // dead center - default: - startCol = Math.max(1, Math.floor((80 - longestLineLength - 2) / 2) + 1) - break - } - - con.move(row, startCol) - - // Parse and display line with formatting tag support - displayFormattedLine(line) - } - - con.color_pair(oldFgColour, oldBgColour) -} function processSubtitlePacket(packetSize) { // Read subtitle packet data according to SSF format @@ -298,7 +129,7 @@ function processSubtitlePacket(packetSize) { sys.free(textBytes) subtitleText = textStr subtitleVisible = true - displaySubtitle(subtitleText, subtitlePosition) + gui.displaySubtitle(subtitleText, subtitlePosition) } break } @@ -306,7 +137,7 @@ function processSubtitlePacket(packetSize) { case SSF_OP_HIDE: { subtitleVisible = false subtitleText = "" - clearSubtitleArea() + gui.clearSubtitleArea() break } @@ -320,8 +151,8 @@ function processSubtitlePacket(packetSize) { // Re-display current subtitle at new position if visible if (subtitleVisible && subtitleText.length > 0) { - clearSubtitleArea() - displaySubtitle(subtitleText, subtitlePosition) + gui.clearSubtitleArea() + gui.displaySubtitle(subtitleText, subtitlePosition) } } } @@ -809,24 +640,15 @@ try { if (interactive) { notifHideTimer += (t2 - t1) if (!notifHidden && notifHideTimer > (NOTIF_SHOWUPTIME + FRAME_TIME)) { - con.move(1, 1) - print(' '.repeat(79)) + // clearing function here notifHidden = true } - if (!hasSubtitles) { - con.move(31, 1) - con.color_pair(253, 0) - if (currentFileIndex > 1) { - print(`File ${currentFileIndex}: ${frameCount}/${header.totalFrames} (qY=${decoderDbgInfo.qY}, ${((frameCount / akku2 * 100)|0) / 100}f) `) - } else { - print(`Frame: ${frameCount}/${header.totalFrames} (qY=${decoderDbgInfo.qY}, ${((frameCount / akku2 * 100)|0) / 100}f) `) - } - con.move(32, 1) - con.color_pair(253, 0) - print(`VRate: ${(getVideoRate() / 1024 * 8)|0} kbps `) - con.move(1, 1) - } + con.move(32, 1) + con.color_pair(253, 0) + print(`B=${(getVideoRate() / 1024 * 8)|0}k F=${frameCount}/${header.totalFrames} qY=${decoderDbgInfo.qY} f=${(frameCount / akku2).toFixed(2)}`) + con.color_pair(255, 255);print(" ") + con.move(1, 1) } t1 = t2 diff --git a/assets/disk0/tvdos/include/playgui.mjs b/assets/disk0/tvdos/include/playgui.mjs new file mode 100644 index 0000000..ca027e7 --- /dev/null +++ b/assets/disk0/tvdos/include/playgui.mjs @@ -0,0 +1,178 @@ +// Common GUI for media player +// Created by CuriousTorvald on 2025-09-30. + +// Subtitle display functions +function clearSubtitleArea() { + // Clear the subtitle area at the bottom of the screen + // Text mode is 80x32, so clear the bottom few lines + let oldFgColour = con.get_color_fore() + let oldBgColour = con.get_color_back() + + con.color_pair(255, 255) // transparent to clear + + // Clear bottom 4 lines for subtitles + for (let row = 28; row <= 31; row++) { + con.move(row, 1) + for (let col = 1; col <= 80; col++) { + print(" ") + } + } + + con.color_pair(oldFgColour, oldBgColour) +} + +function getVisualLength(line) { + // Calculate the visual length of a line excluding formatting tags + let visualLength = 0 + let i = 0 + + while (i < line.length) { + if (i < line.length - 2 && line[i] === '<') { + // Check for formatting tags and skip them + if (line.substring(i, i + 3).toLowerCase() === '' || + line.substring(i, i + 3).toLowerCase() === '') { + i += 3 // Skip tag + } else if (i < line.length - 3 && + (line.substring(i, i + 4).toLowerCase() === '' || + line.substring(i, i + 4).toLowerCase() === '')) { + i += 4 // Skip closing tag + } else { + // Not a formatting tag, count the character + visualLength++ + i++ + } + } else { + // Regular character, count it + visualLength++ + i++ + } + } + + return visualLength +} + +function displayFormattedLine(line) { + // Parse line and handle and tags with colour changes + // Default subtitle colour: yellow (231), formatted text: white (254) + + let i = 0 + let inBoldOrItalic = false + + // insert initial padding block + con.color_pair(0, 255) + con.prnch(0xDE) + con.color_pair(231, 0) + + while (i < line.length) { + if (i < line.length - 2 && line[i] === '<') { + // Check for opening tags + if (line.substring(i, i + 3).toLowerCase() === '' || + line.substring(i, i + 3).toLowerCase() === '') { + con.color_pair(254, 0) // Switch to white for formatted text + inBoldOrItalic = true + i += 3 + } else if (i < line.length - 3 && + (line.substring(i, i + 4).toLowerCase() === '' || + line.substring(i, i + 4).toLowerCase() === '')) { + con.color_pair(231, 0) // Switch back to yellow for normal text + inBoldOrItalic = false + i += 4 + } else { + // Not a formatting tag, print the character + print(line[i]) + i++ + } + } else { + // Regular character, print it + print(line[i]) + i++ + } + } + + // insert final padding block + con.color_pair(0, 255) + con.prnch(0xDD) + con.color_pair(231, 0) +} + +function displaySubtitle(text, position = 0) { + if (!text || text.length === 0) { + clearSubtitleArea() + return + } + + // Set subtitle colours: yellow (231) on black (0) + let oldFgColour = con.get_color_fore() + let oldBgColour = con.get_color_back() + con.color_pair(231, 0) + + // Split text into lines + let lines = text.split('\n') + + // Calculate position based on subtitle position setting + let startRow, startCol + // Calculate visual length without formatting tags for positioning + let longestLineLength = lines.map(s => getVisualLength(s)).sort().last() + + switch (position) { + case 2: // center left + case 6: // center right + case 8: // dead center + startRow = 16 - Math.floor(lines.length / 2) + break + case 3: // top left + case 4: // top center + case 5: // top right + startRow = 2 + break + case 0: // bottom center + case 1: // bottom left + case 7: // bottom right + default: + startRow = 31 - lines.length + startRow = 31 - lines.length + startRow = 31 - lines.length // Default to bottom center + } + + // Display each line + for (let i = 0; i < lines.length; i++) { + let line = lines[i].trim() + if (line.length === 0) continue + + let row = startRow + i + if (row < 1) row = 1 + if (row > 32) row = 32 + + // Calculate column based on alignment + switch (position) { + case 1: // bottom left + case 2: // center left + case 3: // top left + startCol = 1 + break + case 5: // top right + case 6: // center right + case 7: // bottom right + startCol = Math.max(1, 78 - getVisualLength(line) - 2) + break + case 0: // bottom center + case 4: // top center + case 8: // dead center + default: + startCol = Math.max(1, Math.floor((80 - longestLineLength - 2) / 2) + 1) + break + } + + con.move(row, startCol) + + // Parse and display line with formatting tag support + displayFormattedLine(line) + } + + con.color_pair(oldFgColour, oldBgColour) +} + +exports = { + clearSubtitleArea, + displaySubtitle +} \ No newline at end of file diff --git a/video_encoder/encoder_tav.c b/video_encoder/encoder_tav.c index 1b64ea9..aa85044 100644 --- a/video_encoder/encoder_tav.c +++ b/video_encoder/encoder_tav.c @@ -3219,15 +3219,15 @@ int main(int argc, char *argv[]) { enc->target_bitrate = bitrate; // Choose initial q-index based on target bitrate - if (bitrate >= 64000) { + if (bitrate >= 32000) { enc->quality_level = 5; - } else if (bitrate >= 32000) { - enc->quality_level = 4; } else if (bitrate >= 16000) { - enc->quality_level = 3; + enc->quality_level = 4; } else if (bitrate >= 8000) { - enc->quality_level = 2; + enc->quality_level = 3; } else if (bitrate >= 4000) { + enc->quality_level = 2; + } else if (bitrate >= 2000) { enc->quality_level = 1; } else { enc->quality_level = 0;