[완벽 가이드] Python과 FFmpeg로 전문가급 오디오북 영상 자동 제작하기

2026. 1. 14.

 

영상 콘텐츠, 특히 오디오북이나 팟캐스트 같은 '듣는' 콘텐츠를 제작하다 보면 항상 고민되는 것이 있습니다. 바로 '배경 영상'입니다. 내용은 좋은데 배경이 너무 밋밋하거나, 반대로 너무 화려해서 집중을 방해하면 안 되겠죠.

 

특히 여러 편의 시리즈를 제작할 때, 매번 다른 소스 영상을 사용하면서도 채널만의 일관된 톤앤매너를 유지하는 것은 매우 어렵고 번거로운 작업입니다.

 

이 글에서는 강력한 영상 처리 도구인 FFmpeg와 자동화의 왕 Python을 조합하여, 어떤 영상 소스를 넣어도 항상 일관된 '따스한 감성'을 지닌 고품질 오디오북 배경 영상을 자동으로 만들어내는 파이프라인을 구축하는 전 과정을 상세하게 공유하고자 합니다.

 

최종 목표: 우리가 만들 영상은?

  1. 자막 가독성 확보: 하단은 어둡게 처리하여 어떤 상황에서도 자막이 선명하게 보입니다.
  2. 감성적인 분위기 연출: 상단은 원본 영상의 느낌을 살리면서, 채널의 정체성에 맞는 따스한 색감을 더합니다.
  3. 전문적인 연출:
    • 시작: 인트로에서는 원본 영상을 그대로 보여주다가, 본 내용이 시작될 때 필터와 오버레이가 부드럽게 '페이드 인'됩니다.
    • 마무리: 엔딩 크레딧이나 이미지가 시작될 때, 적용되었던 오버레이가 부드럽게 '페이드 아웃'되며 깔끔하게 마무리됩니다.
  4. 완전 자동화: 이 모든 과정을 Python 스크립트 실행 한 번으로 완료합니다.

 


1단계: 디자인 철학 세우기 - "무엇을, 왜 이렇게 만들 것인가?"

코드를 작성하기 전에, 우리가 만들 영상이 왜 시청자에게 더 좋은 경험을 주는지 이해하는 것이 중요합니다.

컨셉 1: 시각적 안정감을 주는 '투톤 그라데이션' 오버레이

  • 상단 (0px ~ 820px): 원본 영상의 느낌을 살리기 위해 아주 옅게(25%) '따스한 브라운' 색상을 입힙니다. 이는 영상의 전체적인 톤을 우리 채널의 감성으로 물들이는 역할을 합니다.
  • 하단 (840px ~ 1080px): 자막 가독성을 위해 '짙은 고동색'으로 확실하게(70%) 어둡게 처리합니다. 순수한 검정보다 따뜻한 느낌을 주면서도 명도 대비를 확실히 합니다.
  • 경계 (820px ~ 840px): 두 구역이 갑자기 나뉘지 않도록 매우 부드러운 그라데이션으로 연결하여 시각적 안정감을 줍니다.

컨셉 2: 어떤 영상도 따스하게 만드는 '범용 감성 필터'

성경 오디오북과 같은 콘텐츠는 경건하고 따스한 분위기가 중요합니다. 우리는 세 가지 효과를 중첩하여 어떤 영상이든 이런 분위기로 탈바꿈시킬 것입니다.

  1. 색감 보정 (colorchannelmixer): 차가운 푸른빛은 줄이고 따스한 붉은빛을 살짝 더해 아날로그 필름 같은 색감을 만듭니다.
  2. 빛 집중 효과 (vignette): 영상 가장자리를 자연스럽게 어둡혀 시선을 중앙으로 모으고, 성스러운 느낌을 더합니다.
  3. 부드러운 질감 (gblur): 디지털 영상의 날카로움을 아주 살짝 뭉개주어, 전체적으로 부드럽고 몽환적인 질감을 표현합니다.

2단계: FFmpeg 마법의 주문 - 필터 체인 완전 정복

이제 위 컨셉을 구현할 FFmpeg 명령어를 단계별로 해부해 보겠습니다. 페이드 인/아웃 기능까지 추가하여 더욱 정교해졌습니다.

Part 1: 원본 영상 보정 ([base_video] 만들기)

[0:v]
colorchannelmixer=rr=1.1:gg=1.0:bb=0.9,
vignette=angle=PI/4:d=1.2,
gblur=sigma=0.3
[base_video];
  • [0:v]: 첫 번째 입력(input.mp4)의 비디오 스트림을 의미합니다.
  • colorchannelmixer=...: Red 채널은 1.1배 강화, Blue 채널은 0.9배 약화시켜 따스한 색감을 만듭니다.
  • vignette=...: 화면 가장자리를 부드럽게 어둡게 처리합니다.
  • gblur=sigma=0.3: 아주 약한 가우시안 블러를 적용합니다.
  • [base_video]: 이렇게 처리된 결과물을 나중에 사용하기 위해 base_video라는 임시 이름을 붙여줍니다.

Part 2: 투톤 그라데이션 오버레이 제작 및 페이드 인/아웃 ([faded_overlay] 만들기)

color=s=1920x1080:c=0x21170D@0.7,
drawbox=y=0:h=830:c=0x4d331a@0.25:t=fill,
avgblur=sizeX=1:sizeY=21
[gradient_overlay];

[gradient_overlay]fade=in:st=5:d=1:alpha=1,fade=out:st=55:d=1:alpha=1[faded_overlay];
  • color, drawbox, avgblur: 이 부분까지는 동일하게 부드러운 그라데이션 오버레이([gradient_overlay])를 만듭니다.
  • fade=in:...,fade=out:...: 이것이 바로 업데이트의 핵심입니다! 하나의 필터 체인 안에서 fade 필터를 콤마(,)로 연결하여 두 번 적용할 수 있습니다.
    • fade=in:st=5:d=1:alpha=1: 페이드 인
      • st=5: 5초(Start Time)에 시작.
      • d=1: 1초(Duration) 동안 진행.
      • alpha=1: 투명도를 페이드인하는 핵심 옵션입니다.
    • fade=out:st=55:d=1:alpha=1: 페이드 아웃
      • st=55: 55초에 시작. (예시: 전체 영상이 60초이고 엔딩이 5초일 경우)
      • d=1: 1초 동안 진행되며 사라짐.
      • alpha=1: 투명도를 페이드아웃.
  • [faded_overlay]: 페이드 인/아웃 효과가 모두 적용된 최종 오버레이에 faded_overlay라는 이름을 붙입니다.

Part 3: 최종 합성

[base_video][faded_overlay]overlay=format=auto[output]

(보정된 영상 위에 최종 오버레이를 덮어씌워 결과물을 완성합니다.)


3단계: Python으로 자동화 날개 달기 (페이드 아웃 기능 추가)

이제 이 강력한 FFmpeg 명령어를 Python 스크립트에 통합하여 누구나 쉽게 사용할 수 있도록 만들어 보겠습니다. 페이드 아웃 시작 시간을 자동으로 계산하는 로직을 추가하여 훨씬 더 스마트해졌습니다.

최종 Python 스크립트 (video_creator.py)

import subprocess
import os

def get_video_duration(video_path: str) -> float:
    """ffprobe를 사용하여 영상의 길이를 초 단위로 반환합니다."""
    command = [
        'ffprobe',
        '-v', 'error',
        '-show_entries', 'format=duration',
        '-of', 'default=noprint_wrappers=1:nokey=1',
        video_path
    ]
    try:
        result = subprocess.run(command, check=True, capture_output=True, text=True)
        return float(result.stdout.strip())
    except (subprocess.CalledProcessError, FileNotFoundError):
        print("경고: ffprobe를 실행할 수 없거나 영상 길이를 가져올 수 없습니다. 페이드 아웃이 정확하지 않을 수 있습니다.")
        return 0

def create_bible_audiobook_video(
    input_path: str,
    output_path: str,
    intro_duration: int = 5,
    ending_duration: int = 5,
    resolution: str = "1920x1080"
):
    """
    따스한 감성의 오디오북 배경 영상을 페이드 인/아웃 기능과 함께 제작합니다.

    Args:
        input_path (str): 원본 배경 영상 파일 경로.
        output_path (str): 최종 결과물을 저장할 경로.
        intro_duration (int, optional): 인트로 길이(초).
        ending_duration (int, optional): 엔딩 길이(초).
        resolution (str, optional): 영상 해상도.
    """
    if not os.path.exists(input_path):
        print(f"오류: 입력 파일 '{input_path}'을(를) 찾을 수 없습니다.")
        return

    print(f"영상 처리를 시작합니다: '{input_path}'")

    # 1. 영상 전체 길이를 가져와서 페이드 아웃 시작 시간 계산
    total_duration = get_video_duration(input_path)
    if total_duration == 0:
        return

    fade_out_start_time = total_duration - ending_duration
    if fade_out_start_time < intro_duration + 1: # 페이드인/아웃이 겹치지 않도록
        print("경고: 영상 길이가 너무 짧아 페이드 아웃 시작 시간이 페이드 인 시간과 겹칩니다.")
        fade_out_start_time = total_duration - 1 # 최소한의 시간 확보

    print(f"총 영상 길이: {total_duration:.2f}초, 페이드 아웃 시작: {fade_out_start_time:.2f}초")

    # 2. 페이드 아웃 시간을 반영한 필터 체인 구성
    filter_complex = f"""
    [0:v]
    colorchannelmixer=rr=1.1:gg=1.0:bb=0.9,
    vignette=angle=PI/4:d=1.2,
    gblur=sigma=0.3
    [base_video];

    color=s={resolution}:c=0x21170D@0.7,
    drawbox=y=0:h=830:c=0x4d331a@0.25:t=fill,
    avgblur=sizeX=1:sizeY=21
    [gradient_overlay];

    [gradient_overlay]fade=in:st={intro_duration}:d=1:alpha=1,fade=out:st={fade_out_start_time}:d=1:alpha=1[faded_overlay];

    [base_video][faded_overlay]overlay=format=auto[output]
    """

    filter_complex_oneline = "".join(filter_complex.split())

    # 3. 최종 FFmpeg 명령어 구성 및 실행
    command = [
        'ffmpeg',
        '-i', input_path,
        '-filter_complex', filter_complex_oneline,
        '-map', '[output]',
        '-map', '0:a?',      # 오디오가 있으면 복사, 없으면 무시
        '-c:a', 'copy',      # 오디오는 재인코딩 없이 복사
        '-y',                # 덮어쓰기 허용
        output_path
    ]

    try:
        print("FFmpeg를 실행합니다. 영상 길이에 따라 시간이 걸릴 수 있습니다...")
        subprocess.run(command, check=True, capture_output=True, text=True)
        print("✓ 영상 처리가 성공적으로 완료되었습니다!")
        print(f"결과 파일: '{output_path}'")
    except subprocess.CalledProcessError as e:
        print("✗ 오류: 영상 처리 중 문제가 발생했습니다.")
        print("FFmpeg 오류 로그:")
        print(e.stderr)

# --- 스크립트 사용 예시 ---
if __name__ == "__main__":
    # 1. 여기에 실제 파일 경로를 입력하세요. (윈도우 경로는 / 또는 \\ 로 사용)
    input_video = "C:/MyVideos/raw/ocean_background.mp4"
    output_video = "C:/MyVideos/final/audiobook_part2.mp4"

    # 2. 영상의 인트로와 엔딩 길이에 맞춰 시간을 조절하세요.
    create_bible_audiobook_video(
        input_path=input_video,
        output_path=output_video,
        intro_duration=10,  # 인트로 10초
        ending_duration=8   # 엔딩 8초
    )

이 스크립트를 사용하는 방법

  1. 사전 준비: 컴퓨터에 PythonFFmpeg가 설치되어 있고, 환경 변수에 등록되어 있어야 합니다.
  2. 코드 저장: 위 코드를 video_creator.py 와 같은 이름으로 저장합니다.
  3. 경로 수정: 코드 하단의 if __name__ == "__main__": 블록에 있는 input_videooutput_video 경로를 여러분의 파일 경로에 맞게 수정합니다.
  4. 시간 조절: intro_durationending_duration 값을 실제 영상의 인트로/엔딩 길이에 맞춰 초 단위로 입력합니다.
  5. 실행: 터미널(명령 프롬프트)을 열고 저장된 파일이 있는 폴더로 이동한 뒤, python video_creator.py 명령을 실행하면 끝!

마무리하며

이제 우리의 자동화 스크립트는 단순히 영상을 편집하는 것을 넘어, 시작과 끝의 연출까지 책임지는 스마트한 영상 제작 파이프라인으로 거듭났습니다. 인트로와 엔딩의 길이를 파라미터로 지정해 주기만 하면, 어떤 길이의 영상이라도 일관된 고품질의 결과물을 안정적으로 생산해 낼 수 있습니다.

 

여기서 소개된 색상 코드(0x21170D, 0x4d331a)나 필터의 강도(gblur=sigma=0.3 등)를 여러분의 채널 컨셉에 맞게 조금씩 수정해 보세요. 여러분만의 시그니처 스타일을 만드는 재미를 느끼실 수 있을 겁니다.

 

이 글이 여러분의 콘텐츠 제작 과정에 효율성과 전문성을 더하는 데 도움이 되었기를 바랍니다. 긴 글 읽어주셔서 감사합니다

댓글