Auto-transcode AV1/HEVC videos to H.264 before processing
Docker Build and Push / check-secrets (push) Successful in 5s
Docker Build and Push / build-and-push (cuda, 12.8) (push) Has been skipped
Docker Build and Push / build-and-push (directml, latest) (push) Has been skipped
Docker Build and Push / build-and-push (cuda, 11.8) (push) Has been skipped
Docker Build and Push / build-and-push (cpu, latest) (push) Has been skipped
Docker Build and Push / build-and-push (cuda, 12.6) (push) Has been skipped
Build Windows CPU / build (push) Has been cancelled
Build Windows CUDA 11.8 / build (push) Has been cancelled
Build Windows CUDA 12.6 / build (push) Has been cancelled
Build Windows CUDA 12.8 / build (push) Has been cancelled
Build Windows DirectML / build (push) Has been cancelled
Docker Build and Push / check-secrets (push) Successful in 5s
Docker Build and Push / build-and-push (cuda, 12.8) (push) Has been skipped
Docker Build and Push / build-and-push (directml, latest) (push) Has been skipped
Docker Build and Push / build-and-push (cuda, 11.8) (push) Has been skipped
Docker Build and Push / build-and-push (cpu, latest) (push) Has been skipped
Docker Build and Push / build-and-push (cuda, 12.6) (push) Has been skipped
Build Windows CPU / build (push) Has been cancelled
Build Windows CUDA 11.8 / build (push) Has been cancelled
Build Windows CUDA 12.6 / build (push) Has been cancelled
Build Windows CUDA 12.8 / build (push) Has been cancelled
Build Windows DirectML / build (push) Has been cancelled
Root cause: FramePrefetcher._read_loop exits after cap.read() returns False (AV1 decode failure), putting only one (False, None) sentinel in the queue. If the caller reads past that, queue.get() blocks forever → deadlock. Fix: _ensure_h264() checks if OpenCV can read the first frame. If not, runs FFmpeg to transcode to H.264 NVENC (falls back to libx264 if NVENC fails). Uses cached converted file on repeated attempts. Processing then runs on the H.264 version which OpenCV can decode normally. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Haiku 4.5
parent
8dc1276a7c
commit
0b2a3ec6e3
+53
@@ -389,6 +389,56 @@ def _download(task_id: str, url: str, dest: Path) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
def _ensure_h264(task_id: str, video_path: str) -> str:
|
||||
"""如果视频无法被 OpenCV 解码(如 AV1/HEVC),先用 FFmpeg 转成 H.264。
|
||||
返回可用的视频路径(原路径或转码后路径)。"""
|
||||
cap = cv2.VideoCapture(video_path)
|
||||
ret, _ = cap.read()
|
||||
cap.release()
|
||||
if ret:
|
||||
return video_path # 可正常解码,无需转换
|
||||
|
||||
# 无法解码,转码
|
||||
converted = str(Path(video_path).with_suffix('')) + '_h264.mp4'
|
||||
if Path(converted).exists():
|
||||
push_log(task_id, f"使用已有 H.264 转码版本: {Path(converted).name}")
|
||||
return converted
|
||||
|
||||
push_log(task_id, "⚠ 视频格式(可能是 AV1/HEVC)OpenCV 无法解码,自动转换为 H.264…")
|
||||
try:
|
||||
from backend.tools.ffmpeg_cli import FFmpegCLI
|
||||
import subprocess
|
||||
cmd = [
|
||||
FFmpegCLI.instance().ffmpeg_path,
|
||||
'-y', '-i', video_path,
|
||||
'-c:v', 'h264_nvenc', '-cq', '18', '-preset', 'p4',
|
||||
'-c:a', 'copy',
|
||||
'-loglevel', 'error',
|
||||
converted,
|
||||
]
|
||||
push_log(task_id, "FFmpeg 转码中(H.264 NVENC)…")
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=3600)
|
||||
if result.returncode != 0 or not Path(converted).exists():
|
||||
push_log(task_id, "NVENC 转码失败,回退 libx264…")
|
||||
cmd_x264 = [
|
||||
FFmpegCLI.instance().ffmpeg_path,
|
||||
'-y', '-i', video_path,
|
||||
'-c:v', 'libx264', '-crf', '18', '-preset', 'fast',
|
||||
'-c:a', 'copy', '-loglevel', 'error', converted,
|
||||
]
|
||||
result = subprocess.run(cmd_x264, capture_output=True, text=True, timeout=3600)
|
||||
if result.returncode == 0 and Path(converted).exists():
|
||||
size_mb = Path(converted).stat().st_size / 1048576
|
||||
push_log(task_id, f"✓ 转码完成 → {Path(converted).name} ({size_mb:.1f} MB)")
|
||||
return converted
|
||||
else:
|
||||
push_log(task_id, f"转码失败: {result.stderr[:200]},尝试用原始文件继续")
|
||||
return video_path
|
||||
except Exception as e:
|
||||
push_log(task_id, f"转码异常: {e},尝试用原始文件继续")
|
||||
return video_path
|
||||
|
||||
|
||||
def _process(task_id: str, video_path: str, inpaint_mode_str: str, subtitle_areas: list,
|
||||
preview_mode: bool = False, detect_mode: str = "text"):
|
||||
if not VSR_AVAILABLE:
|
||||
@@ -401,6 +451,9 @@ def _process(task_id: str, video_path: str, inpaint_mode_str: str, subtitle_area
|
||||
push_log(task_id, f"启动 — 算法:{modeLabels.get(inpaint_mode_str, inpaint_mode_str)}")
|
||||
update_task(task_id, status="processing", progress=0, started_at=time.time())
|
||||
|
||||
# ── 预检:AV1/HEVC 等 OpenCV 无法解码的格式,先转换为 H.264 ──────────────
|
||||
video_path = _ensure_h264(task_id, video_path)
|
||||
|
||||
class _WebRemover(SubtitleRemover):
|
||||
_static_boxes = []
|
||||
|
||||
|
||||
Reference in New Issue
Block a user