mltframework/shotcut · 라이선스 GPLv3 · 언어 C++(~8.8만 줄) + QML(~7.4만 줄) · UI 프레임워크 Qt 6(6.4+) · 엔진 MLT++ ≥ 7.36 · 빌드 CMake + Ninja · 제작 Dan Dennedy / Meltytech, LLC · 버전 날짜식 v26.8.1 · 최신 커밋 2026-08-03 · TrendShift Daily #15)
Shotcut은 "MLT라는 멀티미디어 엔진 위에 Qt6로 얹은, 타임라인 기반의 무료·크로스플랫폼 영상편집기"다. 여러분이 화면에서 보는 것 — 클립을 끌어다 놓는 타임라인, 필터 목록, 미리보기 재생기 — 은 전부 Qt/QML로 그린 껍데기(GUI)이고, 그 뒤에서 실제로 영상을 읽고·자르고·합치고·내보내는 일은 MLT 엔진이 한다. Shotcut의 소스를 읽는다는 건 곧 "GUI가 엔진에게 일을 어떻게 시키는가"를 읽는 것이다.
고급 레스토랑에 가면 여러분은 홀에서 메뉴를 고르고, 주문하고, 접시가 나오는 걸 본다. 하지만 실제 조리 — 불 조절, 재료 손질, 플레이팅 — 는 보이지 않는 주방에서 일어난다. 홀 직원은 "테이블 5번, 스테이크 미디엄"이라고 주문서를 넘길 뿐, 직접 굽지 않는다.
Shotcut이 홀이고 MLT가 주방이다. 여러분이 타임라인에서 클립을 3초 위치로 옮기면, Shotcut은 그걸 직접 자르지 않는다. 대신 "이 클립을 이 위치로"라는 주문서(명령 객체)를 만들어 MLT 엔진에 넘긴다. 엔진이 실제 프레임을 재배치하고, 결과를 다시 신호로 알려주면, 홀(UI)이 화면을 새로 그린다. 이 분업이 Shotcut 코드 전체를 관통하는 뼈대다.
mlt++)를 통해 이 엔진을 조종한다. Kdenlive 등 다른 편집기도 MLT를 쓴다..cpp와 .qml로 나뉘어 있다.사용자가 타임라인에서 클립 하나를 드래그해 옮겼을 때, Shotcut 내부에서 벌어지는 일을 압축하면 이렇다:
이 7단계가 Shotcut의 데이터 흐름 한 사이클이다. 눈여겨볼 것은 ③에서 명령 객체(Command)를 거친다는 점 — 덕분에 모든 편집이 Ctrl+Z로 되돌려진다. 그리고 ④~⑦처럼 "직접 UI를 고치지 않고, 엔진을 바꾼 뒤 그 변화가 신호로 돌아와 UI를 갱신"하는 단방향 흐름이 앱 전체에서 반복된다.
Shotcut은 2011년에 시작된 15년차 프로젝트다. 신생 레포가 아닌데 TrendShift에 다시 오른 데는 이유가 있다. 요즘 영상편집 시장은 구독제(Premiere)와 무료지만 폐쇄형(DaVinci Resolve 무료판)으로 양분돼 있는데, Shotcut은 "진짜로 공짜이고, 소스가 열려 있고, 계정·워터마크·기능 제한이 전혀 없는" 몇 안 되는 선택지다. 최근 AI 자막·TTS 기능이 붙으면서 다시 주목받았다.
| 제품 | 비용/제약 | 오픈소스 | 크로스플랫폼 |
|---|---|---|---|
| Shotcut | 완전 무료·워터마크 없음·계정 불필요 | ✅ GPLv3 | ✅ Linux/macOS/Win |
| Adobe Premiere | 월 구독 필수 | ❌ | macOS/Win |
| DaVinci Resolve(무료) | 무료지만 코덱·기능 제한, 계정 권장 | ❌ | Linux/macOS/Win |
| Kdenlive | 무료·오픈소스(같은 MLT 엔진) | ✅ GPL | 주로 Linux 중심 |
| OpenShot | 무료·오픈소스 | ✅ GPL | ✅ 3종 |
"임대 스튜디오 vs 내 집 작업실." Premiere·Resolve가 매달 임대료를 내거나 관리사무소(회사 서버·계정)의 허락을 받아야 쓰는 스튜디오라면, Shotcut은 등기까지 내 이름으로 된 작업실이다. 인터넷이 끊겨도, 회사가 망해도, 10년 뒤에도 그대로 돌아간다. 소스가 GPLv3라 원하면 뜯어고칠 수도 있다.
① 엔진/GUI 분리 설계가 교과서적이다. 앞서 본 MLT(엔진) ↔ Qt(GUI) 분업은 대형 데스크탑 앱을 어떻게 층으로 쪼개는지 보여주는 살아있는 예시다. UI를 바꿔도 엔진은 그대로, 엔진을 업그레이드해도 UI는 그대로다.
② 되돌리기(undo)가 전 기능에 일관되게 박혀 있다. 편집·필터·자막·마커 등 모든 변경이 QUndoCommand 객체로 표현돼 하나의 스택에 쌓인다. "모든 사용자 행동을 객체로 만든다"는 명령 패턴(Command Pattern)의 대규모 실전 사례다.
③ AI 기능이 외부 프로세스로 깔끔하게 붙었다. 음성→자막(Whisper), 텍스트→음성(Kokoro TTS)이 앱 본체를 무겁게 만들지 않고 백그라운드 작업(Job)으로 분리돼 있다. 무거운 AI를 앱에 통합하는 현실적인 방법을 보여준다.
Shotcut은 "엔진 + GUI + 백그라운드 도구"의 3층 구조다. 각 층이 어떤 라이브러리를 쓰는지 지도로 펼치면 이렇다.
| 기술 | 역할 |
|---|---|
| MLT (mlt++ ≥ 7.36) | 영상편집의 심장. 프로듀서(입력)·컨슈머(출력)·필터·트랜지션·타임라인을 모두 제공. Shotcut은 이걸 mlt++ C++ 래퍼로 조종 |
| FFmpeg | MLT 뒤에서 실제 코덱을 담당 — 수백 종의 영상·오디오 포맷 디코딩/인코딩 |
| Frei0r | 영상 효과 플러그인 표준. 블러·색보정 등 필터 상당수가 여기서 옴 |
| FFTW | 고속 푸리에 변환 — 오디오 분석·주파수 기반 필터에 사용 |
| SDL2 | 크로스플랫폼 오디오 재생(미리보기 소리 출력) |
| 기술 | 역할 |
|---|---|
| Qt 6 (6.4 최소, 프리셋은 6.10) | 앱 골격 전체 — 창·메뉴·도킹 패널·이벤트 루프. Widgets + Quick(QML) 둘 다 사용 |
| Qt Widgets (C++) | 메인 창, 13개 도킹 패널, 대화상자 58종 등 "정적인 골격" UI |
| Qt Quick / QML | 타임라인·필터 파라미터·키프레임처럼 "동적인" UI. src/qml/에 551개 파일 |
| QtWebEngine | 텍스트/타이틀 자막을 HTML로 렌더링(htmlgenerator) |
| CuteLogger | 번들된 로깅 라이브러리. MLT의 로그까지 여기로 흘려보냄 |
| 기술 | 역할 |
|---|---|
| whisper.cpp | 음성 인식(STT). WAV → SRT 자막 자동 생성. GPU 우선, 실패 시 CPU 폴백 |
| Kokoro TTS | 텍스트 → 음성. voices/에 54개 음색 .opus 샘플 내장(af_bella, af_heart 등) |
| 모션 트래킹 | MotionTrackerModel — 화면 속 물체를 추적해 필터를 따라붙임(모자이크 추적 등) |
| ffmpeg / ffprobe / melt | 인코딩·미디어 정보 조회·렌더링을 CLI 프로세스로 호출(src/jobs/) |
"오케스트라 편성." MLT는 실제 소리를 내는 연주자 전원, Qt/QML은 악보를 보여주고 지휘 신호를 받는 지휘대와 무대, 백그라운드 Job들은 무대 뒤에서 조명·음향을 처리하는 스태프다. 관객(사용자)은 무대만 보지만, 세 층이 맞물려야 공연이 된다.
Shotcut을 이해하는 열쇠는 4개의 전역 싱글턴(singleton)이다. 이 넷이 앱의 기둥이고, 나머지 코드는 대부분 이들 사이의 심부름이다. 개발자들은 편하게 부르려고 짧은 매크로 별명까지 붙여놨다.
MAIN, Settings 같은 별명으로 그 하나에 접근한다. 남용하면 결합도가 높아지는 양날의 검이지만, 이런 중앙 허브형 앱에서는 흔한 선택이다.| 싱글턴 | 매크로 | 역할 |
|---|---|---|
| Mlt::Controller | MLT | 미디어 엔진 창구 — 프로듀서/컨슈머/시크/프로파일 관리 |
| MainWindow | MAIN | 중앙 허브 — 모든 도크·되돌리기 스택·레이아웃 소유 |
| ShotcutActions | Actions | 전역 액션 등록소 — 문자열 키로 QAction 관리 |
| ShotcutSettings | Settings | 영구 설정 — QSettings 기반, QML에서 바인딩 가능 |
실제 코드에서 별명은 이렇게 정의돼 있다(mltcontroller.h 등):
// 각 헤더 끝에 매크로로 "짧은 별명"을 심어둠
#define MLT Mlt::Controller::singleton()
#define MAIN MainWindow::singleton()
#define Actions ShotcutActions::singleton()
#define Settings ShotcutSettings::singleton()
// 그래서 코드 어디서든 이렇게 쓴다:
MAIN.undoStack()->push(command); // 되돌리기 스택에 명령 추가
MLT.setProducer(producer); // 엔진에 새 입력 지정
가장 중요한 설계 결정은 "UI가 엔진을 직접 만지지 않는다"는 것이다. 모든 편집은 반드시 QUndoCommand 객체를 거쳐 간다. 이 우회로 덕분에 되돌리기/다시하기가 공짜로 따라온다.
redo()(실행)와 undo()(되돌리기)를 짝으로 가진다. 이 객체들을 QUndoStack이라는 스택에 쌓아두면, Ctrl+Z는 맨 위 명령의 undo()를, Ctrl+Y는 redo()를 부르면 끝이다. Shotcut은 src/commands/에 타임라인·필터·마커·자막·플레이리스트 명령을 모아뒀다.MultitrackModel이 이 Tractor를 감싸 QML 타임라인에 노출한다(코드에서 m_tractor 확인).QML은 C++ 객체를 직접 못 만진다. 그래서 그 사이에 모델(Model)이 다리를 놓는다. MultitrackModel은 QAbstractItemModel을 상속해 "트랙-클립 2단계" 데이터를 QML이 읽을 수 있는 형태(role)로 노출한다. 클립의 이름·색·길이·잠금 여부 등이 모두 role로 정의돼 있어, QML 쪽은 model.name, model.duration처럼 접근한다.
// multitrackmodel.h — 트랙 종류를 enum으로 정의
typedef enum {
PlaylistTrackType, // 일반 트랙
BlackTrackType, // 배경(맨 밑 검은 트랙)
SilentTrackType,
AudioTrackType, // 오디오 트랙
VideoTrackType // 비디오 트랙
} TrackType;
// 2단계 모델: 트랙, 그리고 트랙 위의 클립
Mlt::Tractor *tractor() const { return m_tractor; }
src/ 아래 1,022개 파일이 어디에 사는가규모가 큰 만큼 폴더가 곧 기능 지도다. 파일 수와 함께 보면 "어디에 무게가 실렸는지"가 한눈에 들어온다.
AGENTS.md(아키텍처 지도, 5분) → src/main.cpp(앱이 어떻게 켜지나) → src/mainwindow.cpp(허브가 도크들을 어떻게 조립하나) → src/commands/timelinecommands.cpp(명령 하나가 어떻게 생겼나) → src/models/multitrackmodel.cpp(타임라인 데이터). 이 5개만 훑어도 흐름이 잡힌다. QML 551개는 처음부터 다 볼 필요 없다.
QDockWidget 서브클래스다(src/docks/). 사용자가 패널 배치를 자유롭게 바꾸고 저장할 수 있는 이유가 이 구조 덕분이다.Shotcut은 "실무급 데스크탑 앱이 어떻게 조립되는가"의 완성된 표본이다. 튜토리얼용 장난감이 아니라 15년간 다듬어진 실전 코드라, 아래 주제들을 "이론"이 아니라 "현장 코드"로 배울 수 있다.
엔진(MLT) ↔ 명령(commands) ↔ 모델(models) ↔ 뷰(qml)의 4겹 분리는 어떤 언어·프레임워크로 옮겨도 통하는 보편 설계다. "UI 코드에서 비즈니스 로직을 어떻게 떼어내는가"를 배우려면 src/commands/와 src/models/를 대조해 읽어라. UI는 명령만 만들고, 명령이 엔진을 바꾸고, 모델이 그 변화를 UI에 되돌려준다.
모든 편집을 QUndoCommand로 표현하는 방식은 그림판·문서편집기·IDE 등 되돌리기가 필요한 모든 앱의 핵심이다. undohelper.cpp를 보면 "되돌리기 전에 상태를 어떻게 저장하고, 최적화 힌트(SkipXML 등)로 어떻게 빠르게 복원하는지"까지 나온다.
NameRole(이름), DurationRole(길이), IsLockedRole(잠김?) 같은 role을 정의하면, QML이 model.name·model.duration으로 각각 꺼내 쓴다. C++의 데이터를 QML에 "칸막이 쳐서" 노출하는 방법이다.요즘 Qt 앱의 정석은 "골격은 C++, 동적 UI는 QML"이다. Shotcut은 src/qmltypes/에서 색상휠·파일다이얼로그 같은 C++ 위젯을 QML에 등록하는 법을, src/models/에서 데이터를 넘기는 법을 보여준다. Qt로 앱을 만들 계획이라면 이 다리 놓는 패턴이 가장 값지다.
인코딩·AI 자막 같은 몇 분짜리 작업을 UI를 얼리지 않고 돌리는 법. src/jobs/의 AbstractJob을 상속한 각 Job은 대개 외부 프로세스(ffmpeg·whisper)를 QProcess로 띄우고, 출력을 파싱해 진행률(%)을 갱신한다. whisperjob.h를 보면 GPU 실패 시 CPU로 재시도하는 폴백까지 들어있다.
// whisperjob.h — 외부 프로세스로 AI 자막 생성
class WhisperJob : public AbstractJob {
WhisperJob(const QString &name,
const QString &iWavFile, // 입력: WAV 오디오
const QString &oSrtFile, // 출력: SRT 자막
const QString &lang,
bool translate, // 번역까지?
int maxLength,
bool useGpu = true); // GPU 우선, 실패시 CPU
private:
bool m_retryingWithoutGpu; // ← 폴백 상태 플래그
int m_previousPercent; // ← 진행률 추적
};
main.cpp 하나에도 #ifdef Q_OS_WIN / Q_OS_MAC / Q_OS_LINUX 분기가 가득하다. 윈도우는 discrete GPU 힌트(NvOptimus)와 뮤텍스 기반 단일 실행, 리눅스는 PipeWire/PulseAudio 선택 등 OS별 차이를 하나의 코드베이스에서 어떻게 흡수하는지 실전 사례가 즐비하다.
src/qml/filters/<이름>/의 UI QML을 열어보고, 슬라이더 범위를 바꿔 빌드 → 파라미터 UI가 어떻게 값과 연결되는지 관찰src/commands/markercommands.cpp에서 가장 단순한 명령 하나를 골라 redo()/undo() 짝을 읽고 그림으로 그려보기AGENTS.md의 grid/zoom 정수 인코딩 표(10010=10px, 20169=16:9)를 코드에서 실제로 쓰는 곳 찾아보기Shotcut을 그냥 쓰고 싶다면 빌드하지 말고 공식 바이너리를 받으면 된다(리눅스 AppImage·flatpak, macOS·Win 설치본). 소스 빌드는 MLT·Qt·frei0r 등 의존성 경로를 손수 맞춰야 해서 초보에겐 함정이 많다. 아래는 "코드를 고쳐보고 싶은 사람"을 위한 정보다.
| 항목 | 내용 |
|---|---|
| OS | Linux(64bit) / macOS / Windows — 3종 모두 공식 지원 |
| GPU | 필수 아님. --gpu 옵션으로 GPU 처리 켤 수 있음(실험적) |
| 비용 | 0원. 계정·워터마크·기능 제한 전혀 없음 |
| 의존성 | 최소 버전 |
|---|---|
| Qt 6 | 6.4 이상 (프리셋 기본은 6.10.3) |
| MLT++ | 7.36.0 이상 (pkg-config --modversion mlt++로 확인) |
| 빌드 도구 | CMake 3.12+ · Ninja 권장 · C++ 컴파일러(gcc/clang/MSVC) |
| 기타 라이브러리 | FFmpeg · FFTW · Frei0r · SDL2 · (Linux) X11·DBus |
# AGENTS.md가 알려주는 가장 빠른 빌드 (프리셋 사용)
cmake --preset cc-debug # 구성: Ninja + Qt6 + Debug
cmake --build build/cc-debug # 빌드
build/cc-debug/src/shotcut # 빌드 트리에서 바로 실행
# 설치하지 않으면 QML 파일을 못 찾아 실행이 실패할 수 있다
cmake --install . # (별도 빌드 디렉토리 사용 시)
CMakePresets.json)=자주 쓰는 빌드 조합을 이름 하나로 저장해둔 것(여기선 cc-debug). Ninja=make보다 빠른 빌드 실행기. "구성(configure)→빌드(build)→설치(install)" 3단계가 현대 C++ 프로젝트의 표준 흐름이다.AGENTS.md가 명시한다 — Shotcut에는 C++ 자동 유닛테스트가 없다. 자동 테스트는 EDL/챕터 내보내기용 JavaScript 테스트 2개뿐(node src/qml/export-edl/test-node.js). 즉 이 규모의 앱이 수동 QA에 크게 의존한다는 뜻. 코드를 고칠 때 "테스트가 지켜주지 않는다"는 점을 감안해야 한다(역설적으로, 테스트 문화를 배우려는 사람에겐 "없을 때의 리스크"를 보여주는 사례).
AGENTS.md를 읽고, 이 문서 4장의 4겹 흐름도(UI→명령→엔진→모델→뷰)를 보지 않고 직접 그려본다. 그런 다음 src/commands/·src/models/·src/qml/views/ 폴더를 열어 각 층에 실제 파일이 사는지 대조. "폴더 = 아키텍처 층"임을 눈으로 확인하는 게 목표.
Shotcut을 빌드하지 않고도 그 엔진을 맛볼 수 있다. MLT를 설치하면 딸려오는 melt CLI로 melt clip1.mp4 clip2.mp4 -consumer avformat:out.mp4처럼 두 영상을 이어붙여 본다. "Shotcut이 GUI로 하는 일을 엔진은 이렇게 명령으로 받는구나"를 체감.
src/qml/filters/에서 밝기·크롭 같은 간단한 필터 폴더를 골라 ui.qml을 연다. 슬라이더의 최소/최대값이나 라벨 텍스트를 바꿔 재빌드하고, 그 값이 MLT 필터 속성(filter.set(...))과 어떻게 연결되는지 추적한다. QML↔C++↔엔진 3단 연결을 한 눈에 보는 훈련.
Shotcut 코드 밖에서, 작은 Qt 연습 프로젝트를 만들어 QUndoCommand를 상속한 "텍스트 추가/삭제" 명령을 구현하고 QUndoStack에 연결한다. Shotcut의 markercommands.cpp를 참고 삼아, "행동=객체" 패턴을 작은 규모로 재현하는 것.
src/jobs/whisperjob.cpp의 onReadyRead()를 정독한다. whisper.cpp 프로세스가 표준출력으로 뱉는 로그에서 어떻게 진행률 %를 뽑아내 UI에 반영하는지, GPU 실패 감지 후 m_retryingWithoutGpu로 어떻게 CPU 재시도하는지 흐름을 정리. "외부 AI 프로세스를 앱에 안전하게 붙이는 법"의 실전 답안이다.
이 레포는 두 갈래 학습의 교차로다 — (A) Qt 데스크탑 앱 개발과 (B) 영상/오디오 처리. 6주 코스로 짜면 이렇다.
| 주차 | 주제 | 할 것 |
|---|---|---|
| 1주 | Qt/QML 기초 | Qt 공식 튜토리얼로 창·시그널/슬롯·간단한 QML 뷰 만들기. Shotcut의 main.cpp·mainwindow.cpp 겉핥기 |
| 2주 | 모델-뷰 구조 | QAbstractItemModel로 리스트/트리 모델 직접 구현. Shotcut PlaylistModel과 비교 |
| 3주 | 명령 패턴 & Undo | QUndoStack+QUndoCommand로 되돌리기 되는 미니 에디터. Shotcut commands/ 대조 |
| 4주 | MLT 엔진 | melt CLI로 클립 이어붙이기·필터·트랜지션 실습. Producer/Consumer/Tractor 개념 체득 |
| 5주 | FFmpeg & 코덱 | FFmpeg CLI로 인코딩·컨테이너·비트레이트 이해. MLT가 왜 FFmpeg에 의존하는지 연결 |
| 6주 | 비동기 & 외부 프로세스 | QProcess로 외부 CLI를 띄우고 출력 파싱해 진행률 표시. Shotcut jobs/ 재현 |
"데스크탑 앱을 만들고 싶다"면 1→2→3주(Qt/모델/Undo)에 집중 — Shotcut은 이 세 가지의 대형 참조 구현이다. "영상 처리가 궁금하다"면 4→5주(MLT/FFmpeg)로 바로. "AI를 앱에 붙이고 싶다"면 6주 + whisperjob·kokorodokijob 정독이 지름길.
AGENTS.md(아키텍처 요약·코드 관례) · src/main.cpp(앱 부트) · src/mainwindow.cpp(허브) · src/commands/(되돌리기) · src/models/multitrackmodel.cpp(타임라인)