50 lines
1.6 KiB
Python
50 lines
1.6 KiB
Python
"""将幻灯片与配音逐段合成为 MP4,再拼接为完整视频。"""
|
||
import subprocess
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
import imageio_ffmpeg
|
||
|
||
FFMPEG = imageio_ffmpeg.get_ffmpeg_exe()
|
||
WS = Path(__file__).resolve().parent
|
||
REPO_ROOT = WS.parent
|
||
PARTS = WS / "parts"
|
||
PARTS.mkdir(exist_ok=True)
|
||
|
||
N = 12
|
||
part_files = []
|
||
for i in range(1, N + 1):
|
||
slide = WS / "slides" / f"slide{i:02d}.png"
|
||
audio = WS / "audio" / f"seg{i:02d}.mp3"
|
||
part = PARTS / f"part{i:02d}.mp4"
|
||
cmd = [
|
||
FFMPEG, "-y",
|
||
"-loop", "1", "-framerate", "30", "-i", str(slide),
|
||
"-i", str(audio),
|
||
"-c:v", "libx264", "-preset", "medium", "-tune", "stillimage",
|
||
"-c:a", "aac", "-b:a", "192k",
|
||
"-pix_fmt", "yuv420p",
|
||
"-shortest", "-movflags", "+faststart",
|
||
str(part),
|
||
]
|
||
r = subprocess.run(cmd, capture_output=True, text=True, timeout=600)
|
||
if r.returncode != 0 or not part.exists():
|
||
print(f"part{i:02d} 合成失败:\n{r.stderr[-800:]}")
|
||
sys.exit(1)
|
||
part_files.append(part)
|
||
print(f"part{i:02d}.mp4 ✓ ({part.stat().st_size // 1024} KB)")
|
||
|
||
lst = WS / "concat.txt"
|
||
lst.write_text("".join(f"file 'parts/{p.name}'\n" for p in part_files), encoding="utf-8")
|
||
|
||
final = REPO_ROOT / "APS智能体演示视频.mp4"
|
||
r = subprocess.run(
|
||
[FFMPEG, "-y", "-f", "concat", "-safe", "0", "-i", str(lst),
|
||
"-c", "copy", "-movflags", "+faststart", str(final)],
|
||
capture_output=True, text=True, timeout=600,
|
||
)
|
||
if r.returncode != 0 or not final.exists():
|
||
print("拼接失败:\n", r.stderr[-800:])
|
||
sys.exit(1)
|
||
print(f"\n最终视频: {final} ({final.stat().st_size / 1024 / 1024:.1f} MB)")
|