Files
flavioy 93d822d067 修复LAMA模式100%卡死:帧区间扩展超出视频总帧数导致FramePrefetcher死锁
- 限制字幕区间end不超过frame_count,防止内循环消费哨兵后外层永久阻塞
- LAMA批量推理改为mini-batch(4帧),避免GPU OOM
- 各inpaint模型空inpaint_area时返回原始帧
- FFmpeg子进程添加600s超时保护

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-08 23:34:53 +08:00

105 lines
2.8 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import os
import queue
import subprocess
import threading
import cv2
import numpy as np
from .ffmpeg_cli import FFmpegCLI
class FramePrefetcher:
"""
后台线程预解码视频帧,使 I/O 与模型推理重叠。
接口兼容 cv2.VideoCaptureread/release
"""
def __init__(self, video_cap, buffer_size=10):
self.cap = video_cap
self._buffer = queue.Queue(maxsize=buffer_size)
self._stopped = False
self._thread = threading.Thread(target=self._read_loop, daemon=True)
self._thread.start()
def _read_loop(self):
while not self._stopped:
ret, frame = self.cap.read()
self._buffer.put((ret, frame))
if not ret:
break
def read(self):
"""读取下一帧,接口与 cv2.VideoCapture.read() 一致。"""
return self._buffer.get()
def get(self, propId):
return self.cap.get(propId)
def stop(self):
"""停止预读取,不释放底层 video_cap。"""
self._stopped = True
try:
while not self._buffer.empty():
self._buffer.get_nowait()
except queue.Empty:
pass
self._thread.join(timeout=5)
def release(self):
self.stop()
self.cap.release()
class FFmpegVideoWriter:
"""
通过 FFmpeg 管道写入帧,使用 libx264 编码。
接口兼容 cv2.VideoWriterwrite/release
"""
def __init__(self, output_path, fps, size):
w, h = size
cmd = [
FFmpegCLI.instance().ffmpeg_path,
'-y',
'-f', 'rawvideo',
'-vcodec', 'rawvideo',
'-s', f'{w}x{h}',
'-pix_fmt', 'bgr24',
'-r', str(fps),
'-i', '-',
'-c:v', 'libx264',
'-pix_fmt', 'yuv420p',
'-crf', '18',
'-preset', 'fast',
'-loglevel', 'error',
output_path
]
self._process = subprocess.Popen(
cmd,
stdin=subprocess.PIPE,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
)
def write(self, frame):
"""写入一帧numpy BGR 数组)。"""
if frame.dtype != np.uint8:
frame = np.clip(frame, 0, 255).astype(np.uint8)
try:
self._process.stdin.write(frame.tobytes())
except BrokenPipeError:
pass
def release(self):
"""关闭管道并等待编码完成。"""
try:
self._process.stdin.close()
except BrokenPipeError:
pass
try:
self._process.wait(timeout=600)
except subprocess.TimeoutExpired:
self._process.terminate()
self._process.wait(timeout=5)