mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-06-10 23:04:04 +09:00
TAV: two-pass GOP slicer
This commit is contained in:
@@ -482,11 +482,12 @@ function getVideoRate() {
|
|||||||
|
|
||||||
let FRAME_TIME = 1.0 / header.fps
|
let FRAME_TIME = 1.0 / header.fps
|
||||||
|
|
||||||
let frameCount = 0
|
let frameCount = 0
|
||||||
let trueFrameCount = 0
|
let trueFrameCount = 0
|
||||||
let stopPlay = false
|
let stopPlay = false
|
||||||
let akku = FRAME_TIME
|
let akku = FRAME_TIME
|
||||||
let akku2 = 0.0
|
let akku2 = 0.0
|
||||||
|
let firstFrameIssued = false // Track when first frame has been displayed
|
||||||
let nextFrameTime = 0 // Absolute time when next frame should display (nanoseconds)
|
let nextFrameTime = 0 // Absolute time when next frame should display (nanoseconds)
|
||||||
let currentFileIndex = 1 // Track which file we're playing in concatenated stream
|
let currentFileIndex = 1 // Track which file we're playing in concatenated stream
|
||||||
let totalFilesProcessed = 0
|
let totalFilesProcessed = 0
|
||||||
@@ -825,6 +826,7 @@ try {
|
|||||||
frameCount = 0
|
frameCount = 0
|
||||||
akku = FRAME_TIME
|
akku = FRAME_TIME
|
||||||
akku2 = 0.0
|
akku2 = 0.0
|
||||||
|
firstFrameIssued = false
|
||||||
audio.purgeQueue(0)
|
audio.purgeQueue(0)
|
||||||
if (paused) {
|
if (paused) {
|
||||||
audio.play(0)
|
audio.play(0)
|
||||||
@@ -844,6 +846,7 @@ try {
|
|||||||
frameCount = 0
|
frameCount = 0
|
||||||
akku = FRAME_TIME
|
akku = FRAME_TIME
|
||||||
akku2 = 0.0
|
akku2 = 0.0
|
||||||
|
firstFrameIssued = false
|
||||||
audio.purgeQueue(0)
|
audio.purgeQueue(0)
|
||||||
if (paused) {
|
if (paused) {
|
||||||
audio.play(0)
|
audio.play(0)
|
||||||
@@ -863,6 +866,7 @@ try {
|
|||||||
frameCount = seekTarget.frameNum
|
frameCount = seekTarget.frameNum
|
||||||
akku = FRAME_TIME
|
akku = FRAME_TIME
|
||||||
akku2 -= 5.5
|
akku2 -= 5.5
|
||||||
|
firstFrameIssued = false
|
||||||
audio.purgeQueue(0)
|
audio.purgeQueue(0)
|
||||||
if (paused) {
|
if (paused) {
|
||||||
audio.play(0)
|
audio.play(0)
|
||||||
@@ -890,6 +894,7 @@ try {
|
|||||||
frameCount = seekTarget.frameNum
|
frameCount = seekTarget.frameNum
|
||||||
akku = FRAME_TIME
|
akku = FRAME_TIME
|
||||||
akku2 += 5.0
|
akku2 += 5.0
|
||||||
|
firstFrameIssued = false
|
||||||
audio.purgeQueue(0)
|
audio.purgeQueue(0)
|
||||||
if (paused) {
|
if (paused) {
|
||||||
audio.play(0)
|
audio.play(0)
|
||||||
@@ -925,6 +930,7 @@ try {
|
|||||||
frameCount = 0
|
frameCount = 0
|
||||||
akku = 0.0
|
akku = 0.0
|
||||||
akku2 = 0.0
|
akku2 = 0.0
|
||||||
|
firstFrameIssued = false
|
||||||
FRAME_TIME = 1.0 / header.fps
|
FRAME_TIME = 1.0 / header.fps
|
||||||
audio.purgeQueue(0)
|
audio.purgeQueue(0)
|
||||||
currentFileIndex++
|
currentFileIndex++
|
||||||
@@ -952,19 +958,16 @@ try {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (packetType === TAV_PACKET_SYNC || packetType == TAV_PACKET_SYNC_NTSC) {
|
if (packetType === TAV_PACKET_SYNC || packetType == TAV_PACKET_SYNC_NTSC) {
|
||||||
// Sync packet - no additional data (for I/P frames, not GOPs)
|
// SYNC packets are vestigial in TAV's time-based playback model
|
||||||
akku -= FRAME_TIME
|
// (legacy from TEV's synchronous display model)
|
||||||
if (packetType == TAV_PACKET_SYNC) {
|
//
|
||||||
frameCount++
|
// Frame display timing is controlled by nextFrameTime, not SYNC packets:
|
||||||
}
|
// - I/P frames: Display logic at line 1553-1597
|
||||||
|
// - GOP frames: Display logic at line 1600-1684
|
||||||
trueFrameCount++
|
//
|
||||||
|
// NTSC sync (frame duplication): Handled automatically by audio queue timing
|
||||||
// Swap ping-pong buffers
|
//
|
||||||
let temp = CURRENT_RGB_ADDR
|
// Do nothing - skip to next packet
|
||||||
CURRENT_RGB_ADDR = PREV_RGB_ADDR
|
|
||||||
PREV_RGB_ADDR = temp
|
|
||||||
|
|
||||||
}
|
}
|
||||||
else if (packetType === TAV_PACKET_IFRAME || packetType === TAV_PACKET_PFRAME) {
|
else if (packetType === TAV_PACKET_IFRAME || packetType === TAV_PACKET_PFRAME) {
|
||||||
// Record I-frame position for seeking
|
// Record I-frame position for seeking
|
||||||
@@ -1072,7 +1075,7 @@ try {
|
|||||||
const gopSize = seqread.readOneByte()
|
const gopSize = seqread.readOneByte()
|
||||||
const compressedSize = seqread.readInt()
|
const compressedSize = seqread.readInt()
|
||||||
let compressedPtr = seqread.readBytes(compressedSize)
|
let compressedPtr = seqread.readBytes(compressedSize)
|
||||||
updateDataRateBin(compressedSize)
|
updateDataRateBin(compressedSize / gopSize)
|
||||||
|
|
||||||
// TRIPLE-BUFFERING LOGIC (3 slots: playing, ready, decoding):
|
// TRIPLE-BUFFERING LOGIC (3 slots: playing, ready, decoding):
|
||||||
// - If no GOP playing: decode first GOP to slot 0
|
// - If no GOP playing: decode first GOP to slot 0
|
||||||
@@ -1456,7 +1459,7 @@ try {
|
|||||||
} // end of !paused packet read block
|
} // end of !paused packet read block
|
||||||
|
|
||||||
let t2 = sys.nanoTime()
|
let t2 = sys.nanoTime()
|
||||||
if (!paused) {
|
if (!paused && firstFrameIssued) {
|
||||||
// Only accumulate time if we have a GOP to play
|
// Only accumulate time if we have a GOP to play
|
||||||
// Don't accumulate during first GOP decode or we'll get fast playback
|
// Don't accumulate during first GOP decode or we'll get fast playback
|
||||||
if (currentGopSize > 0) {
|
if (currentGopSize > 0) {
|
||||||
@@ -1484,8 +1487,12 @@ try {
|
|||||||
currentGopBufferSlot = asyncDecodeSlot
|
currentGopBufferSlot = asyncDecodeSlot
|
||||||
asyncDecodeInProgress = false
|
asyncDecodeInProgress = false
|
||||||
|
|
||||||
// Set first frame time to NOW
|
// Initialize timing ONLY if this is the very first frame of the video
|
||||||
nextFrameTime = sys.nanoTime()
|
// If we're transitioning from I-frames, preserve timing continuity
|
||||||
|
if (nextFrameTime === 0) {
|
||||||
|
nextFrameTime = sys.nanoTime()
|
||||||
|
}
|
||||||
|
// Otherwise keep existing nextFrameTime from previous I-frame/GOP
|
||||||
|
|
||||||
// Resume packet reading only if not all 3 buffers are full
|
// Resume packet reading only if not all 3 buffers are full
|
||||||
// (might have buffered GOP 2 and 3 during GOP 1 decode)
|
// (might have buffered GOP 2 and 3 during GOP 1 decode)
|
||||||
@@ -1577,6 +1584,11 @@ try {
|
|||||||
audioFired = true
|
audioFired = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mark first frame as issued (starts akku/akku2 timers)
|
||||||
|
if (!firstFrameIssued) {
|
||||||
|
firstFrameIssued = true
|
||||||
|
}
|
||||||
|
|
||||||
frameCount++
|
frameCount++
|
||||||
trueFrameCount++
|
trueFrameCount++
|
||||||
iframeReady = false
|
iframeReady = false
|
||||||
@@ -1586,7 +1598,7 @@ try {
|
|||||||
CURRENT_RGB_ADDR = PREV_RGB_ADDR
|
CURRENT_RGB_ADDR = PREV_RGB_ADDR
|
||||||
PREV_RGB_ADDR = temp
|
PREV_RGB_ADDR = temp
|
||||||
|
|
||||||
// Schedule next frame
|
// Schedule next frame (advance AFTER display, consistent with GOP timing)
|
||||||
nextFrameTime += (frametime) // frametime is in nanoseconds from header
|
nextFrameTime += (frametime) // frametime is in nanoseconds from header
|
||||||
|
|
||||||
// Log performance data every 60 frames
|
// Log performance data every 60 frames
|
||||||
@@ -1628,6 +1640,11 @@ try {
|
|||||||
audioFired = true
|
audioFired = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mark first frame as issued (starts akku/akku2 timers)
|
||||||
|
if (!firstFrameIssued) {
|
||||||
|
firstFrameIssued = true
|
||||||
|
}
|
||||||
|
|
||||||
currentGopFrameIndex++
|
currentGopFrameIndex++
|
||||||
frameCount++
|
frameCount++
|
||||||
trueFrameCount++
|
trueFrameCount++
|
||||||
@@ -1836,7 +1853,7 @@ try {
|
|||||||
con.color_pair(253, 0)
|
con.color_pair(253, 0)
|
||||||
let guiStatus = {
|
let guiStatus = {
|
||||||
fps: header.fps,
|
fps: header.fps,
|
||||||
videoRate: getVideoRate(),
|
videoRate: getVideoRate().toFixed(0),
|
||||||
frameCount: frameCount,
|
frameCount: frameCount,
|
||||||
totalFrames: header.totalFrames,
|
totalFrames: header.totalFrames,
|
||||||
frameMode: decoderDbgInfo.frameMode,
|
frameMode: decoderDbgInfo.frameMode,
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user