FFmpeg 의 가치가 자동화에 stitch 할 때 compound: transcript 기반 video trim 하는 agent, frame sequence 생성 후 조립 필요한 AI 도구, 안 좋은 audio level detect + fix 하는 batch 작업.
Pippa 예시
이 codebase 가 세 곳에서 Python 에서 FFmpeg 호출:
backend/services/tts_normalize.py — 모든 Pippa voice clip 에 EBU R128 + denoise chain 돌림.
backend/services/video_export.py — avatar frame + TTS audio 를 WebUI 의 exportable video 로 조립.
Telegram bot voice note — transcription 전 Opus → WAV 변환.
패턴
명령어를 list 로 (shell string 아님) subprocess.run 사용 — 더 안전, 파일이름의 space 자동 처리. Progress parsing 위해 stderr pipe. Long job 엔 machine-readable progress event 위해 -progress pipe:1 사용.
Code
Python wrapper — progress 와 basic encode·python
import subprocess, sys, json
def encode_with_progress(src: str, dst: str) -> int:
cmd = [
"ffmpeg", "-y", "-i", src,
"-c:v", "libx264", "-crf", "20", "-preset", "slow",
"-pix_fmt", "yuv420p",
"-c:a", "aac", "-b:a", "192k",
"-progress", "pipe:1",
"-nostats", "-loglevel", "error",
dst,
]
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, text=True)
for line in p.stdout:
if line.startswith("out_time_ms="):
ms = int(line.strip().split("=", 1)[1])
print(f"\rProgress: {ms / 1_000_000:.1f}s", end="", flush=True)
return p.wait()
if __name__ == "__main__":
sys.exit(encode_with_progress(sys.argv[1], sys.argv[2]))
Agent-driven trim — LLM 이 timestamp 픽·python
import subprocess, json
from pathlib import Path
def transcribe(src: Path) -> list[dict]:
"""Segment list 반환: [{start, end, text}, ...]
Pipeline 에 fit 한 transcriber 사용 (faster-whisper, Cloud STT, etc.)
"""
...
def llm_pick_highlights(segments: list[dict]) -> list[tuple[float, float]]:
"""Segment 를 LLM 에 보내서 30초 quotable start/end pair 반환 요청.
(start, end) tuple list 반환.
"""
...
def build_highlight_reel(src: Path, ranges: list[tuple[float, float]], dst: Path):
# Range trim + concat 하는 complex filtergraph 빌드
parts = []
filters = []
for i, (s, e) in enumerate(ranges):
filters.append(f"[0:v]trim=start={s}:end={e},setpts=PTS-STARTPTS[v{i}]")
filters.append(f"[0:a]atrim=start={s}:end={e},asetpts=PTS-STARTPTS[a{i}]")
parts.extend(f"[v{i}][a{i}]" for i in range(len(ranges)))
filtergraph = "; ".join(filters) + "; " + "".join(parts) + f"concat=n={len(ranges)}:v=1:a=1[outv][outa]"
subprocess.run([
"ffmpeg", "-y", "-i", str(src),
"-filter_complex", filtergraph,
"-map", "[outv]", "-map", "[outa]",
"-c:v", "libx264", "-crf", "20", "-preset", "slow",
"-c:a", "aac", "-b:a", "192k",
str(dst),
], check=True)
# Wire up
src = Path("raw_podcast.mp4")
segs = transcribe(src)
ranges = llm_pick_highlights(segs)
build_highlight_reel(src, ranges, Path("highlights.mp4"))
Video 와 (start, end) tuple list 받아 concatenated highlight reel 생성하는 Python script 작성 (위 recipe 사용). -progress pipe:1 parsing 으로 progress print 추가. 보너스: audio transcript 의 'best' 30초 픽 하는 LLM (Claude API) 통해 segment feed.
Progress
Progress is local-only — sign in to sync across devices.