mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-06-08 22:34:03 +09:00
tav player: seek and pause
This commit is contained in:
@@ -470,6 +470,75 @@ let decoderDbgInfo = {}
|
|||||||
|
|
||||||
let cueElements = []
|
let cueElements = []
|
||||||
let currentCueIndex = -1 // Track current cue position
|
let currentCueIndex = -1 // Track current cue position
|
||||||
|
let iframePositions = [] // Track I-frame positions for seeking: [{offset, frameNum}]
|
||||||
|
|
||||||
|
// Function to find nearest I-frame before or at target frame
|
||||||
|
function findNearestIframe(targetFrame) {
|
||||||
|
if (iframePositions.length === 0) return null
|
||||||
|
|
||||||
|
// Find the largest I-frame position <= targetFrame
|
||||||
|
let result = null
|
||||||
|
for (let i = iframePositions.length - 1; i >= 0; i--) {
|
||||||
|
if (iframePositions[i].frameNum <= targetFrame) {
|
||||||
|
result = iframePositions[i]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If targetFrame is before first I-frame, return first I-frame
|
||||||
|
return result || iframePositions[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to scan forward and find next I-frame at or after target frame
|
||||||
|
function scanForwardToIframe(targetFrame, currentPos) {
|
||||||
|
// Save current position
|
||||||
|
let savedPos = seqread.getReadCount()
|
||||||
|
|
||||||
|
try {
|
||||||
|
let scanFrameCount = frameCount
|
||||||
|
|
||||||
|
// Scan forward through packets
|
||||||
|
while (seqread.getReadCount() < FILE_LENGTH) {
|
||||||
|
let packetPos = seqread.getReadCount()
|
||||||
|
let pType = seqread.readOneByte()
|
||||||
|
|
||||||
|
// Handle sync packets (increment frame counter)
|
||||||
|
if (pType === TAV_PACKET_SYNC || pType === TAV_PACKET_SYNC_NTSC) {
|
||||||
|
if (pType === TAV_PACKET_SYNC) {
|
||||||
|
scanFrameCount++
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Found I-frame at or after target?
|
||||||
|
if (pType === TAV_PACKET_IFRAME && scanFrameCount >= targetFrame) {
|
||||||
|
// Record this I-frame position for future use
|
||||||
|
iframePositions.push({offset: packetPos, frameNum: scanFrameCount})
|
||||||
|
return {offset: packetPos, frameNum: scanFrameCount}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip over packet payload (all non-sync packets have uint32 size)
|
||||||
|
if (pType !== TAV_PACKET_SYNC && pType !== TAV_PACKET_SYNC_NTSC && pType !== TAV_FILE_HEADER_FIRST) {
|
||||||
|
let payloadSize = seqread.readInt()
|
||||||
|
seqread.skip(payloadSize)
|
||||||
|
} else if (pType === TAV_FILE_HEADER_FIRST) {
|
||||||
|
// Hit next file header, stop scanning
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Didn't find I-frame, restore position
|
||||||
|
return null
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
// Error or EOF during scan
|
||||||
|
serial.printerr(`Scan error: ${e}`)
|
||||||
|
return null
|
||||||
|
} finally {
|
||||||
|
// Restore original position
|
||||||
|
seqread.seek(savedPos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Function to try reading next TAV file header at current position
|
// Function to try reading next TAV file header at current position
|
||||||
function tryReadNextTAVHeader() {
|
function tryReadNextTAVHeader() {
|
||||||
@@ -624,6 +693,7 @@ function tryReadNextTAVHeader() {
|
|||||||
|
|
||||||
let lastKey = 0
|
let lastKey = 0
|
||||||
let skipped = false
|
let skipped = false
|
||||||
|
let paused = false
|
||||||
|
|
||||||
// Playback loop - properly adapted from TEV with multi-file support
|
// Playback loop - properly adapted from TEV with multi-file support
|
||||||
try {
|
try {
|
||||||
@@ -642,6 +712,16 @@ try {
|
|||||||
stopPlay = true
|
stopPlay = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
else if (keyCode == 62) { // SPACE - pause/resume
|
||||||
|
paused = !paused
|
||||||
|
if (paused) {
|
||||||
|
audio.stop(0)
|
||||||
|
serial.println(`Paused at frame ${frameCount}`)
|
||||||
|
} else {
|
||||||
|
audio.play(0)
|
||||||
|
serial.println(`Resumed`)
|
||||||
|
}
|
||||||
|
}
|
||||||
else if (keyCode == 19 && cueElements.length > 0) { // Up arrow - previous cue
|
else if (keyCode == 19 && cueElements.length > 0) { // Up arrow - previous cue
|
||||||
currentCueIndex = (currentCueIndex <= 0) ? cueElements.length - 1 : currentCueIndex - 1
|
currentCueIndex = (currentCueIndex <= 0) ? cueElements.length - 1 : currentCueIndex - 1
|
||||||
let cue = cueElements[currentCueIndex]
|
let cue = cueElements[currentCueIndex]
|
||||||
@@ -653,6 +733,10 @@ try {
|
|||||||
akku = FRAME_TIME
|
akku = FRAME_TIME
|
||||||
akku2 = 0.0
|
akku2 = 0.0
|
||||||
audio.purgeQueue(0)
|
audio.purgeQueue(0)
|
||||||
|
if (paused) {
|
||||||
|
audio.play(0)
|
||||||
|
audio.stop(0)
|
||||||
|
}
|
||||||
skipped = true
|
skipped = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -667,17 +751,70 @@ try {
|
|||||||
akku = FRAME_TIME
|
akku = FRAME_TIME
|
||||||
akku2 = 0.0
|
akku2 = 0.0
|
||||||
audio.purgeQueue(0)
|
audio.purgeQueue(0)
|
||||||
|
if (paused) {
|
||||||
|
audio.play(0)
|
||||||
|
audio.stop(0)
|
||||||
|
}
|
||||||
skipped = true
|
skipped = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (keyCode == 21) { // Left arrow - seek back 5.5s
|
||||||
|
let targetFrame = Math.max(0, frameCount - Math.floor(header.fps * 5.5))
|
||||||
|
let seekTarget = findNearestIframe(targetFrame)
|
||||||
|
|
||||||
|
if (seekTarget) {
|
||||||
|
serial.println(`Seeking back to frame ${seekTarget.frameNum} (offset ${seekTarget.offset})`)
|
||||||
|
seqread.seek(seekTarget.offset)
|
||||||
|
frameCount = seekTarget.frameNum
|
||||||
|
akku = FRAME_TIME
|
||||||
|
akku2 -= 5.5
|
||||||
|
audio.purgeQueue(0)
|
||||||
|
if (paused) {
|
||||||
|
audio.play(0)
|
||||||
|
audio.stop(0)
|
||||||
|
}
|
||||||
|
skipped = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (keyCode == 22) { // Right arrow - seek forward 5s
|
||||||
|
let targetFrame = Math.min(header.totalFrames - 1, frameCount + Math.floor(header.fps * 5.0))
|
||||||
|
|
||||||
|
// Try to find in already-decoded I-frames first
|
||||||
|
let seekTarget = findNearestIframe(targetFrame)
|
||||||
|
|
||||||
|
// If not found or behind current position, scan forward
|
||||||
|
if (!seekTarget || seekTarget.frameNum <= frameCount) {
|
||||||
|
serial.println(`Scanning forward for I-frame near frame ${targetFrame}...`)
|
||||||
|
seekTarget = scanForwardToIframe(targetFrame, seqread.getReadCount())
|
||||||
|
}
|
||||||
|
|
||||||
|
if (seekTarget && seekTarget.frameNum > frameCount) {
|
||||||
|
serial.println(`Seeking forward to frame ${seekTarget.frameNum} (offset ${seekTarget.offset})`)
|
||||||
|
seqread.seek(seekTarget.offset)
|
||||||
|
frameCount = seekTarget.frameNum
|
||||||
|
akku = FRAME_TIME
|
||||||
|
akku2 += 5.0
|
||||||
|
audio.purgeQueue(0)
|
||||||
|
if (paused) {
|
||||||
|
audio.play(0)
|
||||||
|
audio.stop(0)
|
||||||
|
}
|
||||||
|
skipped = true
|
||||||
|
} else if (!seekTarget) {
|
||||||
|
serial.println(`No I-frame found ahead`)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lastKey = keyCode
|
lastKey = keyCode
|
||||||
}
|
}
|
||||||
|
|
||||||
if (akku >= FRAME_TIME) {
|
if (akku >= FRAME_TIME) {
|
||||||
// Read packet header
|
// When paused, just reset accumulator and skip frame processing
|
||||||
var packetType = seqread.readOneByte()
|
if (!paused) {
|
||||||
|
// Read packet header (record position before reading for I-frame tracking)
|
||||||
|
let packetOffset = seqread.getReadCount()
|
||||||
|
var packetType = seqread.readOneByte()
|
||||||
|
|
||||||
// serial.println(`Packet ${packetType} at offset ${seqread.getReadCount() - 1}`)
|
// serial.println(`Packet ${packetType} at offset ${seqread.getReadCount() - 1}`)
|
||||||
|
|
||||||
@@ -733,6 +870,11 @@ try {
|
|||||||
|
|
||||||
}
|
}
|
||||||
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
|
||||||
|
if (packetType === TAV_PACKET_IFRAME) {
|
||||||
|
iframePositions.push({offset: packetOffset, frameNum: frameCount})
|
||||||
|
}
|
||||||
|
|
||||||
// Video packet
|
// Video packet
|
||||||
const compressedSize = seqread.readInt()
|
const compressedSize = seqread.readInt()
|
||||||
|
|
||||||
@@ -870,11 +1012,14 @@ try {
|
|||||||
println(`Unknown packet type: 0x${packetType.toString(16)}`)
|
println(`Unknown packet type: 0x${packetType.toString(16)}`)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
} // end of !paused block
|
||||||
}
|
}
|
||||||
|
|
||||||
let t2 = sys.nanoTime()
|
let t2 = sys.nanoTime()
|
||||||
akku += (t2 - t1) / 1000000000.0
|
if (!paused) {
|
||||||
akku2 += (t2 - t1) / 1000000000.0
|
akku += (t2 - t1) / 1000000000.0
|
||||||
|
akku2 += (t2 - t1) / 1000000000.0
|
||||||
|
}
|
||||||
|
|
||||||
// Simple progress display
|
// Simple progress display
|
||||||
if (interactive) {
|
if (interactive) {
|
||||||
@@ -899,7 +1044,7 @@ try {
|
|||||||
fileOrd: (cueElements.length > 0) ? currentCueIndex+1 : currentFileIndex,
|
fileOrd: (cueElements.length > 0) ? currentCueIndex+1 : currentFileIndex,
|
||||||
resolution: `${header.width}x${header.height}${(isInterlaced) ? 'i' : ''}`,
|
resolution: `${header.width}x${header.height}${(isInterlaced) ? 'i' : ''}`,
|
||||||
colourSpace: header.version % 2 == 0 ? "ICtCp" : "YCoCg",
|
colourSpace: header.version % 2 == 0 ? "ICtCp" : "YCoCg",
|
||||||
currentStatus: 1
|
currentStatus: paused ? 2 : 1 // 2 = paused, 1 = playing
|
||||||
}
|
}
|
||||||
gui.printBottomBar(guiStatus)
|
gui.printBottomBar(guiStatus)
|
||||||
gui.printTopBar(guiStatus, 1)
|
gui.printTopBar(guiStatus, 1)
|
||||||
|
|||||||
@@ -659,7 +659,7 @@ try {
|
|||||||
// For interlaced: decode current frame into currentFieldAddr
|
// For interlaced: decode current frame into currentFieldAddr
|
||||||
// For display: use prevFieldAddr as current, currentFieldAddr as next
|
// For display: use prevFieldAddr as current, currentFieldAddr as next
|
||||||
graphics.tevDecode(blockDataPtr, nextFieldAddr, currentFieldAddr, width, decodingHeight, qualityY, qualityCo, qualityCg, trueFrameCount, debugMotionVectors, version, enableDeblocking, enableBoundaryAwareDecoding)
|
graphics.tevDecode(blockDataPtr, nextFieldAddr, currentFieldAddr, width, decodingHeight, qualityY, qualityCo, qualityCg, trueFrameCount, debugMotionVectors, version, enableDeblocking, enableBoundaryAwareDecoding)
|
||||||
graphics.tevDeinterlace(trueFrameCount, width, decodingHeight, prevFieldAddr, currentFieldAddr, nextFieldAddr, CURRENT_RGB_ADDR, deinterlaceAlgorithm)
|
graphics.tevDeinterlace(trueFrameCount + 1, width, decodingHeight, prevFieldAddr, currentFieldAddr, nextFieldAddr, CURRENT_RGB_ADDR, deinterlaceAlgorithm)
|
||||||
|
|
||||||
// Rotate field buffers for next frame: NEXT -> CURRENT -> PREV
|
// Rotate field buffers for next frame: NEXT -> CURRENT -> PREV
|
||||||
rotateFieldBuffers()
|
rotateFieldBuffers()
|
||||||
|
|||||||
@@ -4683,8 +4683,8 @@ class GraphicsJSR223Delegate(private val vm: VM) {
|
|||||||
val random = java.util.Random()
|
val random = java.util.Random()
|
||||||
for (i in 0 until coeffCount) {
|
for (i in 0 until coeffCount) {
|
||||||
yTile[i] += (random.nextInt(filmGrainLevel * 2 + 1) - filmGrainLevel).toFloat()
|
yTile[i] += (random.nextInt(filmGrainLevel * 2 + 1) - filmGrainLevel).toFloat()
|
||||||
coTile[i] += (random.nextInt(filmGrainLevel * 2 + 1) - filmGrainLevel).toFloat()
|
// coTile[i] += (random.nextInt(filmGrainLevel * 2 + 1) - filmGrainLevel).toFloat()
|
||||||
cgTile[i] += (random.nextInt(filmGrainLevel * 2 + 1) - filmGrainLevel).toFloat()
|
// cgTile[i] += (random.nextInt(filmGrainLevel * 2 + 1) - filmGrainLevel).toFloat()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4749,8 +4749,8 @@ class GraphicsJSR223Delegate(private val vm: VM) {
|
|||||||
val random = java.util.Random()
|
val random = java.util.Random()
|
||||||
for (i in 0 until coeffCount) {
|
for (i in 0 until coeffCount) {
|
||||||
yTile[i] += (random.nextInt(filmGrainLevel * 2 + 1) - filmGrainLevel).toFloat()
|
yTile[i] += (random.nextInt(filmGrainLevel * 2 + 1) - filmGrainLevel).toFloat()
|
||||||
coTile[i] += (random.nextInt(filmGrainLevel * 2 + 1) - filmGrainLevel).toFloat()
|
// coTile[i] += (random.nextInt(filmGrainLevel * 2 + 1) - filmGrainLevel).toFloat()
|
||||||
cgTile[i] += (random.nextInt(filmGrainLevel * 2 + 1) - filmGrainLevel).toFloat()
|
// cgTile[i] += (random.nextInt(filmGrainLevel * 2 + 1) - filmGrainLevel).toFloat()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5331,8 +5331,8 @@ class GraphicsJSR223Delegate(private val vm: VM) {
|
|||||||
val random = java.util.Random()
|
val random = java.util.Random()
|
||||||
for (i in 0 until coeffCount) {
|
for (i in 0 until coeffCount) {
|
||||||
currentY[i] += (random.nextInt(filmGrainLevel * 2 + 1) - filmGrainLevel).toFloat()
|
currentY[i] += (random.nextInt(filmGrainLevel * 2 + 1) - filmGrainLevel).toFloat()
|
||||||
currentCo[i] += (random.nextInt(filmGrainLevel * 2 + 1) - filmGrainLevel).toFloat()
|
// currentCo[i] += (random.nextInt(filmGrainLevel * 2 + 1) - filmGrainLevel).toFloat()
|
||||||
currentCg[i] += (random.nextInt(filmGrainLevel * 2 + 1) - filmGrainLevel).toFloat()
|
// currentCg[i] += (random.nextInt(filmGrainLevel * 2 + 1) - filmGrainLevel).toFloat()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3433,6 +3433,83 @@ static int detect_still_frame(tav_encoder_t *enc) {
|
|||||||
return (changed_pixels == 0);
|
return (changed_pixels == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Detect still frames by comparing quantised DWT coefficients
|
||||||
|
// Returns 1 if quantised coefficients are identical (frame is truly still), 0 otherwise
|
||||||
|
// Benefits: quality-aware (lower quality = more SKIP frames), pure integer math
|
||||||
|
// DISABLED - should work in theory, not actually
|
||||||
|
static int detect_still_frame_dwt(tav_encoder_t *enc) {
|
||||||
|
if (!enc->previous_coeffs_allocated || enc->intra_only) {
|
||||||
|
return 0; // No previous coefficients to compare or intra-only mode
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only compare against I-frames to avoid DELTA quantization drift
|
||||||
|
// previous_coeffs are updated by DELTA frames with reconstructed values that accumulate error
|
||||||
|
if (enc->last_frame_packet_type != TAV_PACKET_IFRAME) {
|
||||||
|
return 0; // Must compare against clean I-frame, not DELTA reconstruction
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get current quantisers (use adjusted quantiser from bitrate control if applicable)
|
||||||
|
int qY = enc->bitrate_mode ? quantiser_float_to_int_dithered(enc) : enc->quantiser_y;
|
||||||
|
int this_frame_qY = QLUT[qY];
|
||||||
|
int this_frame_qCo = QLUT[enc->quantiser_co];
|
||||||
|
int this_frame_qCg = QLUT[enc->quantiser_cg];
|
||||||
|
|
||||||
|
// Coefficient count (monoblock mode)
|
||||||
|
const int coeff_count = enc->width * enc->height;
|
||||||
|
|
||||||
|
// Quantise current DWT coefficients
|
||||||
|
int16_t *quantised_y = enc->reusable_quantised_y;
|
||||||
|
int16_t *quantised_co = enc->reusable_quantised_co;
|
||||||
|
int16_t *quantised_cg = enc->reusable_quantised_cg;
|
||||||
|
|
||||||
|
if (enc->perceptual_tuning) {
|
||||||
|
quantise_dwt_coefficients_perceptual_per_coeff(enc, enc->current_dwt_y, quantised_y, coeff_count, this_frame_qY, enc->width, enc->height, enc->decomp_levels, 0, enc->frame_count);
|
||||||
|
quantise_dwt_coefficients_perceptual_per_coeff(enc, enc->current_dwt_co, quantised_co, coeff_count, this_frame_qCo, enc->width, enc->height, enc->decomp_levels, 1, enc->frame_count);
|
||||||
|
quantise_dwt_coefficients_perceptual_per_coeff(enc, enc->current_dwt_cg, quantised_cg, coeff_count, this_frame_qCg, enc->width, enc->height, enc->decomp_levels, 1, enc->frame_count);
|
||||||
|
} else {
|
||||||
|
quantise_dwt_coefficients(enc->current_dwt_y, quantised_y, coeff_count, this_frame_qY, enc->dead_zone_threshold, enc->width, enc->height, enc->decomp_levels, 0);
|
||||||
|
quantise_dwt_coefficients(enc->current_dwt_co, quantised_co, coeff_count, this_frame_qCo, enc->dead_zone_threshold, enc->width, enc->height, enc->decomp_levels, 1);
|
||||||
|
quantise_dwt_coefficients(enc->current_dwt_cg, quantised_cg, coeff_count, this_frame_qCg, enc->dead_zone_threshold, enc->width, enc->height, enc->decomp_levels, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Quantise previous DWT coefficients (stored from last I-frame)
|
||||||
|
int16_t *prev_quantised_y = malloc(coeff_count * sizeof(int16_t));
|
||||||
|
int16_t *prev_quantised_co = malloc(coeff_count * sizeof(int16_t));
|
||||||
|
int16_t *prev_quantised_cg = malloc(coeff_count * sizeof(int16_t));
|
||||||
|
|
||||||
|
if (enc->perceptual_tuning) {
|
||||||
|
quantise_dwt_coefficients_perceptual_per_coeff(enc, enc->previous_coeffs_y, prev_quantised_y, coeff_count, this_frame_qY, enc->width, enc->height, enc->decomp_levels, 0, enc->frame_count);
|
||||||
|
quantise_dwt_coefficients_perceptual_per_coeff(enc, enc->previous_coeffs_co, prev_quantised_co, coeff_count, this_frame_qCo, enc->width, enc->height, enc->decomp_levels, 1, enc->frame_count);
|
||||||
|
quantise_dwt_coefficients_perceptual_per_coeff(enc, enc->previous_coeffs_cg, prev_quantised_cg, coeff_count, this_frame_qCg, enc->width, enc->height, enc->decomp_levels, 1, enc->frame_count);
|
||||||
|
} else {
|
||||||
|
quantise_dwt_coefficients(enc->previous_coeffs_y, prev_quantised_y, coeff_count, this_frame_qY, enc->dead_zone_threshold, enc->width, enc->height, enc->decomp_levels, 0);
|
||||||
|
quantise_dwt_coefficients(enc->previous_coeffs_co, prev_quantised_co, coeff_count, this_frame_qCo, enc->dead_zone_threshold, enc->width, enc->height, enc->decomp_levels, 1);
|
||||||
|
quantise_dwt_coefficients(enc->previous_coeffs_cg, prev_quantised_cg, coeff_count, this_frame_qCg, enc->dead_zone_threshold, enc->width, enc->height, enc->decomp_levels, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare quantised coefficients - pure integer math
|
||||||
|
int diff_count = 0;
|
||||||
|
for (int i = 0; i < coeff_count; i++) {
|
||||||
|
if (quantised_y[i] != prev_quantised_y[i] ||
|
||||||
|
quantised_co[i] != prev_quantised_co[i] ||
|
||||||
|
quantised_cg[i] != prev_quantised_cg[i]) {
|
||||||
|
diff_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(prev_quantised_y);
|
||||||
|
free(prev_quantised_co);
|
||||||
|
free(prev_quantised_cg);
|
||||||
|
|
||||||
|
if (enc->verbose) {
|
||||||
|
printf("Still frame detection (DWT): %d/%d coeffs differ\n", diff_count, coeff_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If all quantised coefficients match, frames are identical after compression
|
||||||
|
return (diff_count == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Main function
|
// Main function
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
generate_random_filename(TEMP_AUDIO_FILE);
|
generate_random_filename(TEMP_AUDIO_FILE);
|
||||||
@@ -3892,18 +3969,14 @@ int main(int argc, char *argv[]) {
|
|||||||
int is_scene_change = detect_scene_change(enc);
|
int is_scene_change = detect_scene_change(enc);
|
||||||
int is_time_keyframe = (frame_count % KEYFRAME_INTERVAL) == 0;
|
int is_time_keyframe = (frame_count % KEYFRAME_INTERVAL) == 0;
|
||||||
|
|
||||||
// Check if we can use SKIP mode
|
// Check if we can use SKIP mode (DWT coefficient-based detection)
|
||||||
int is_still = detect_still_frame(enc);
|
int is_still = detect_still_frame(enc);
|
||||||
enc->is_still_frame_cached = is_still; // Cache for use in compress_and_write_frame
|
enc->is_still_frame_cached = is_still; // Cache for use in compress_and_write_frame
|
||||||
|
|
||||||
// SKIP mode can be used if:
|
// SKIP mode can be used if frame is still (detect_still_frame_dwt already checks against I-frame)
|
||||||
// 1. Frame is still AND
|
// SKIP runs can continue as long as frames remain identical to the reference I-frame
|
||||||
// 2. Previous coeffs allocated AND
|
|
||||||
// 3. (Last frame was I-frame OR we're continuing a SKIP run)
|
|
||||||
int in_skip_run = enc->used_skip_mode_last_frame;
|
int in_skip_run = enc->used_skip_mode_last_frame;
|
||||||
int can_use_skip = is_still &&
|
int can_use_skip = is_still && enc->previous_coeffs_allocated;
|
||||||
enc->previous_coeffs_allocated &&
|
|
||||||
(enc->last_frame_packet_type == TAV_PACKET_IFRAME || in_skip_run);
|
|
||||||
|
|
||||||
// During a SKIP run, suppress keyframe timer unless content changes enough to un-skip
|
// During a SKIP run, suppress keyframe timer unless content changes enough to un-skip
|
||||||
// Un-skip threshold is the negation of SKIP threshold: content must change to break the run
|
// Un-skip threshold is the negation of SKIP threshold: content must change to break the run
|
||||||
|
|||||||
Reference in New Issue
Block a user