newer movprobe.js

This commit is contained in:
minjaesong
2024-08-15 02:46:09 +09:00
parent bc1635c578
commit ff32200b0a
4 changed files with 146 additions and 154 deletions

View File

@@ -1,153 +0,0 @@
let filename = exec_args[1]
const MAGIC = [0x1F, 0x54, 0x53, 0x56, 0x4D, 0x4D, 0x4F, 0x56]
const port = filesystem._toPorts("A")[0]
com.sendMessage(port, "DEVRST\x17")
com.sendMessage(port, `OPENR"${filename}",1`)
let statusCode = com.getStatusCode(port)
if (statusCode != 0) {
printerrln(`No such file (${statusCode})`)
return statusCode
}
com.sendMessage(port, "READ")
statusCode = com.getStatusCode(port)
if (statusCode != 0) {
printerrln("READ failed with "+statusCode)
return statusCode
}
let readCount = 0
function readBytes(length) {
let ptr = sys.malloc(length)
let requiredBlocks = Math.floor((readCount + length) / 4096) - Math.floor(readCount / 4096)
let completedReads = 0
//serial.println(`readBytes(${length}); readCount = ${readCount}`)
for (let bc = 0; bc < requiredBlocks + 1; bc++) {
if (completedReads >= length) break
if (readCount % 4096 == 0) {
//serial.println("READ from serial")
// pull the actual message
sys.poke(-4093 - port, 6);sys.sleep(0) // spinning is required as Graal run is desynced with the Java side
let blockTransferStatus = ((sys.peek(-4085 - port*2) & 255) | ((sys.peek(-4086 - port*2) & 255) << 8))
let thisBlockLen = blockTransferStatus & 4095
if (thisBlockLen == 0) thisBlockLen = 4096 // [1, 4096]
let hasMore = (blockTransferStatus & 0x8000 != 0)
//serial.println(`block: (${thisBlockLen})[${[...Array(thisBlockLen).keys()].map(k => (sys.peek(-4097 - k) & 255).toString(16).padStart(2,'0')).join()}]`)
let remaining = Math.min(thisBlockLen, length - completedReads)
//serial.println(`Pulled a block (${thisBlockLen}); readCount = ${readCount}, completedReads = ${completedReads}, remaining = ${remaining}`)
// copy from read buffer to designated position
sys.memcpy(-4097, ptr + completedReads, remaining)
// increment readCount properly
readCount += remaining
completedReads += remaining
}
else {
let padding = readCount % 4096
let remaining = length - completedReads
let thisBlockLen = Math.min(4096 - padding, length - completedReads)
//serial.println(`padding = ${padding}; remaining = ${remaining}`)
//serial.println(`block: (${thisBlockLen})[${[...Array(thisBlockLen).keys()].map(k => (sys.peek(-4097 - padding - k) & 255).toString(16).padStart(2,'0')).join()}]`)
//serial.println(`Reusing a block (${thisBlockLen}); readCount = ${readCount}, completedReads = ${completedReads}`)
// copy from read buffer to designated position
sys.memcpy(-4097 - padding, ptr + completedReads, thisBlockLen)
// increment readCount properly
readCount += thisBlockLen
completedReads += thisBlockLen
}
}
//serial.println(`END readBytes(${length}); readCount = ${readCount}\n`)
return ptr
}
function readInt() {
let b = readBytes(4)
let i = (sys.peek(b) & 255) | ((sys.peek(b+1) & 255) << 8) | ((sys.peek(b+2) & 255) << 16) | ((sys.peek(b+3) & 255) << 24)
//serial.println(`readInt(); bytes: ${sys.peek(b)}, ${sys.peek(b+1)}, ${sys.peek(b+2)}, ${sys.peek(b+3)} = ${i}\n`)
sys.free(b)
return i
}
function readShort() {
let b = readBytes(2)
let i = (sys.peek(b) & 255) | ((sys.peek(b+1) & 255) << 8)
//serial.println(`readShort(); bytes: ${sys.peek(b)}, ${sys.peek(b+1)} = ${i}\n`)
sys.free(b)
return i
}
let magic = readBytes(8)
let magicMatching = true
// check if magic number matches
MAGIC.forEach((b,i) => {
let testb = sys.peek(magic + i) & 255 // for some reason this must be located here
if (testb != b) {
magicMatching = false
}
})
sys.free(magic)
if (!magicMatching) {
println("Not a movie file (MAGIC mismatch)")
return 1
}
function toCodecStr(i) {
let low = i & 255
let high = (i >> 8) & 255
return (0 == low) ? "RAW256.gz" :
(1 == low) ? "RAW256PAL.gz" :
(2 == low) ? "RAW4096.gz" :
(4 == low || 5 == low) ? `IPF${high+1}${(5 == low) ? "A" : ""}.gz` :
(16 == low) ? "MJPEG" :
(18 == low) ? "MPNG" :
(20 == low) ? "MTGA" :
(21 == low) ? "MTGA.gz" :
(255 == low) ? "(frame-dependent)" :
`Unknown: ${low},${high}`
}
let width = readShort()
let height = readShort()
let fps = readShort(); if (fps == 0) fps = 9999
let frameTime = 1.0 / fps
let frameCount = readInt()
let type = readShort()
sys.free(readBytes(12)) // skip 12 bytes
let akku = frameTime
let framesRendered = 0
//serial.println(readCount) // must say 18
//serial.println(`Dim: (${width}x${height}), FPS: ${fps}, Frames: ${frameCount}`)
println(`Dim: ${width}x${height}`)
println(`Frames: ${frameCount} at rate of ${fps} fps`)
println(`Codec: ${toCodecStr(type)}`)

View File

@@ -306,6 +306,9 @@ shell.parse = function(input) {
}
/** @return fully resolved path, starting with '\' but not a drive letter */
shell.resolvePathInput = function(input) {
if (input === undefined) return undefined
// replace slashes
let pathstr0 = input.replaceAll('\\','/') // JS thinks '/' as a regex, so we're doing this to circumvent the issue
let pathstr = ''

View File

@@ -0,0 +1,142 @@
// usage: playmov moviefile.mov [/i]
const MAGIC = [0x1F, 0x54, 0x53, 0x56, 0x4D, 0x4D, 0x4F, 0x56]
const pcm = require("pcm")
const MP2_FRAME_SIZE = [144,216,252,288,360,432,504,576,720,864,1008,1152,1440,1728]
const fullFilePath = _G.shell.resolvePathInput(exec_args[1])
const FILE_LENGTH = files.open(fullFilePath.full).size
let seqread = require("seqread")
seqread.prepare(fullFilePath.full)
let magic = seqread.readBytes(8)
let magicMatching = true
// check if magic number matches
MAGIC.forEach((b,i) => {
let testb = sys.peek(magic + i) & 255 // for some reason this must be located here
if (testb != b) {
magicMatching = false
}
})
sys.free(magic)
if (!magicMatching) {
println("Not a movie file (MAGIC mismatch)")
return 1
}
let mp2Initialised = false
let width = seqread.readShort()
let height = seqread.readShort()
let fps = seqread.readShort(); if (fps == 0) fps = 9999
const FRAME_TIME = 1.0 / fps
const FRAME_COUNT = seqread.readInt() % 16777216
seqread.readShort() // skip unused field
const audioQueueInfo = seqread.readShort()
const AUDIO_QUEUE_LENGTH = (audioQueueInfo >> 12) + 1
const AUDIO_QUEUE_BYTES = (audioQueueInfo & 0xFFF) << 2
seqread.skip(10)
let stats = {
"sync":0,
"background":0,
"ipf1":0,
"ipf2":0,
"ipf1a":0,
"ipf2a":0,
"ipf1_delta":0,
"ipf2_delta":0,
"audio_mp2":0,
"audio_pcm":0
}
let errorlevel = 0
try {
renderLoop:
while (seqread.getReadCount() < FILE_LENGTH) {
let packetType = seqread.readShort()
// ideally, first two packets will be audio packets
// sync packets
if (65535 == packetType) {
stats["sync"] += 1
}
// background colour packets
else if (65279 == packetType) {
seqread.skip(4)
stats["background"] += 1
}
// video packets
else if (packetType < 2047) {
// iPF
if (packetType == 4) {
stats["ipf1"] += 1
}
else if (packetType == 5) {
stats["ipf1a"] += 1
}
else if (packetType == 260) {
stats["ipf2"] += 1
}
else if (packetType == 261) {
stats["ipf2a"] += 1
}
else {
throw Error(`Unknown Video Packet with type ${packetType} at offset ${seqread.getReadCount() - 2}`)
}
let payloadLen = seqread.readInt()
seqread.skip(payloadLen)
}
// audio packets
else if (4096 <= packetType && packetType <= 6143) {
let readLength = (packetType >>> 8 == 17) ?
MP2_FRAME_SIZE[(packetType & 255) >>> 1] // if the packet is MP2, deduce it from the packet type
: seqread.readInt() // else, read 4 more bytes
if (readLength == 0) throw Error("Readlength is zero")
// MP2
if (packetType >>> 8 == 17) {
stats["audio_mp2"] += 1
seqread.skip(readLength)
}
// RAW PCM packets (decode on the fly)
else if (packetType == 0x1000 || packetType == 0x1001) {
stats["audio_pcm"] += 1
seqread.skip(readLength)
}
else {
throw Error(`Audio Packet with type ${packetType} at offset ${seqread.getReadCount() - 2}`)
}
}
else {
println(`Unknown Packet with type ${packetType} at offset ${seqread.getReadCount() - 2}`)
}
}
}
catch (e) {
printerrln(e)
errorlevel = 1
}
finally {
println("** Video Stats **")
println(`Dimension: ${width}x${height}`)
println(`Framerate: ${fps}`)
println(`Syncs: ${stats["sync"]}`)
println(`iPF1: ${stats["ipf1"]}`)
println(`iPF2: ${stats["ipf2"]}`)
println(`iPF1a: ${stats["ipf1a"]}`)
println(`iPF2a: ${stats["ipf2a"]}`)
println("** Audio Stats **")
println(`MP2: ${stats["audio_mp2"]}`)
println(`PCM: ${stats["audio_pcm"]}`)
}
return errorlevel

View File

@@ -51,7 +51,7 @@ seqread.readShort() // skip unused field
const audioQueueInfo = seqread.readShort()
const AUDIO_QUEUE_LENGTH = (audioQueueInfo >> 12) + 1
const AUDIO_QUEUE_BYTES = (audioQueueInfo & 0xFFF) << 2
sys.free(seqread.readBytes(10)) // skip 12 bytes
seqread.skip(10) // skip 12 bytes
let audioQueuePos = 0
let akku = FRAME_TIME
let framesRendered = 0