결국 블로그에 사용할 이미지를 위해 내 pc의 FFmpeg 코드로 내가 가지고 있는 동영상을 캡처하는 기능을 쓰고 있었는데 아내의 pc에서도 구현 하고 싶어져서 프로그램을 만들게 되었습니다. 프로그램은 tkinter로 GUI를 구성하고 FFmpeg로 장면 캡처를 구현했습니다. 이 글에서는 개발 과정, 아키텍처, 코드 구문별 설명을 다루며, 저와 같은 니즈가 있는 분들을 위해 핵심 부분들을 정리했습니다.
개발 과정
1. 초기 문제 정의 및 요구사항 수집 (2025년 3월 22일 기준)
문제: 내가 가지고 있는 나의 영상 내 특정 장면을 캡처하려 함. 초기 코드에서 파일 이름의 한글 자소 분리와 FFmpeg 오류 발생.
요구사항:
- 한글 파일 이름 유지
- FFmpeg로 안정적인 장면 캡처
- GUI로 직관적인 사용자 경험 제공
2. 초기 구현 및 디버깅
첫 번째 시도:
- tkinter로 기본 GUI 구성
- FFmpeg로 장면 캡처 구현
문제: restrictfilenames=True로 한글이 제거되고, h264_cuvid 디코더 오류 발생
해결:
- restrictfilenames 제거로 한글 유지
- h264_cuvid 대신 기본 디코더 사용
3. 기능 개선
1: 캡처 경로 저장, 실행 중 버튼 비활성화, 종료 버튼 추가 → 전역 변수로 경로 관리, 버튼 상태 제어, exit_program 함수 구현
2: 창 닫기("X")로 완전 종료, 모드 버튼 시각화 → root.protocol로 창 닫기 이벤트 연결, 버튼 스타일로 모드 표시
4. 최종 안정화
문제: PC 종료 시 경고창 발생
해결: exit_program에 root.quit(), sys.exit(0) 추가로 리소스 완전 해제
프로그램 아키텍처
1. 계층 구조
프론트엔드 (UI):
- tkinter: GUI 프레임워크로 창, 버튼, 입력 필드 구성
- ttk.Style: 버튼 및 위젯 스타일링
백엔드 (로직):
- FFmpeg: 영상 장면 캡처 및 처리
- subprocess: FFmpeg 실행 및 출력 관리
스레드 관리:
- threading: FFmpeg 작업을 비동기로 실행해 UI 반응성 유지
2. 데이터 흐름
장면 캡처: 영상 파일 선택 → FFmpeg 명령어 생성 → 스레드로 실행 → 진행률 표시 → 이미지 저장
3. 모듈화
- init_capture_ui(): UI 초기화 함수
- run_ffmpeg(): 핵심 기능 함수
- exit_program(): 리소스 정리 및 종료
코드 구문별 상세 설명
1. 모듈 임포트 및 초기 설정
import tkinter as tk
from tkinter import filedialog, messagebox, ttk
import subprocess
import os
import sys
import re
import threading
if getattr(sys, 'frozen', False):
base_path = sys._MEIPASS
else:
base_path = os.path.dirname(os.path.abspath(__file__))
ffmpeg_path = os.path.join(base_path, 'ffmpeg', 'ffmpeg.exe')
2. 전역 변수
download_path = os.getcwd()
capture_output_path = os.getcwd()
ffmpeg_process = None
current_mode = "downloader"
3. GUI 설정
root = tk.Tk()
root.title("DoNExtEnd 도우미")
root.geometry("600x550")
root.configure(bg="#e6e6fa")
root.resizable(False, False)
style = ttk.Style()
style.theme_use("clam")
style.configure("TButton", font=("Arial", 11, "bold"), padding=8, background="#9370db", foreground="white")
style.map("TButton", background=[("active", "#7b68ee"), ("disabled", "#d3d3d3")])
style.configure("Selected.TButton", background="#6a5acd")
4. 종료 함수
def exit_program():
global ffmpeg_process
try:
if ffmpeg_process and ffmpeg_process.poll() is None:
ffmpeg_process.terminate()
ffmpeg_process.wait(timeout=2)
print("종료 중: FFmpeg 프로세스 정리 완료")
else:
print("종료 중: FFmpeg 프로세스 없음")
except subprocess.TimeoutExpired:
ffmpeg_process.kill()
print("종료 중: FFmpeg 프로세스 강제 종료")
except Exception as e:
print(f"종료 중 오류: {str(e)}")
root.quit()
root.destroy()
print("프로그램 종료")
sys.exit(0)
6. 장면 캡처 기능
def run_ffmpeg():
global ffmpeg_process
video_path = video_entry.get().strip()
sensitivity = sensitivity_entry.get().strip()
skip_time = skip_entry.get().strip()
output_folder = output_entry.get().strip()
if not os.path.exists(video_path) or not os.path.exists(output_folder):
messagebox.showerror("오류", "파일 또는 폴더가 존재하지 않습니다!", icon="error")
return
try:
float(sensitivity)
float(skip_time)
except ValueError:
messagebox.showerror("오류", "민감도와 스킵 시간은 숫자여야 합니다!", icon="error")
return
video_name = os.path.splitext(os.path.basename(video_path))[0]
output_pattern = os.path.join(output_folder, f"{video_name}_%04d.jpg")
filter_str = f"select='eq(n\\,0)+gt(scene\\,{sensitivity})*between(t\\,prev_selected_t+{skip_time}\\,inf)',showinfo"
cmd = [ffmpeg_path, "-i", video_path, "-vf", filter_str, "-vsync", "vfr", "-threads", "8", "-thread_type", "slice", "-c:v", "mjpeg", output_pattern]
capture_button.config(state="disabled")
def ffmpeg_thread():
global ffmpeg_process
ffmpeg_process = subprocess.Popen(cmd, stderr=subprocess.PIPE, universal_newlines=True, encoding='utf-8', errors='ignore', creationflags=subprocess.CREATE_NO_WINDOW)
# 진행률 계산 및 UI 업데이트 생략
7. 모드 전환
def switch_mode(mode):
global current_mode
current_mode = mode
if mode == "capture":
downloader_btn.configure(style="TButton")
capture_btn.configure(style="Selected.TButton")
for widget in content_frame.winfo_children():
widget.destroy()
if mode == "capture":
init_capture_ui()
마치면서..
최종적으로 영상을 선택하면 장면 캡처를 간편하게 수행합니다. tkinter, FFmpeg를 결합한 아키텍처와 단계적인 개선 과정을 통해 안정성과 사용자 경험을 나름 추가해봤습니다. 이 문서를 통해 개발 과정과 코드 구조를 이해하고, 유사한 프로젝트에 적용할 수 있기를 바랍니다.