이 프로젝트는 업로드한 비디오 파일을 HLS, MPEG‑DASH, RTSP 프로토콜로 스트리밍할 수 있는 웹 애플리케이션입니다. 패키징/트랜스코딩, 기본 인코딩 설정, 생성된 세그먼트 확인 기능에 초점을 맞추고 있습니다.
- 주요 기능
- 사전 요구 사항
- Docker로 실행 (권장)
- 수동 설치
- 애플리케이션 실행
- 사용 방법
- API 개요
- 미디어 포맷 및 프로토콜 호환성
- Docker 설정
- FFmpeg 명령 설명
- 프로젝트 구조
- 라이선스
- 🎥 MP4/MOV 등 일반적인 비디오 파일 업로드
- 🔄 다양한 미디어 포맷으로 패키징 (출력 레이아웃 기준)
- HLS:
playlist.m3u8+*.ts세그먼트 (MPEG‑TS) - DASH:
playlist.mpd+ fragmented MP4 세그먼트 (*.m4s,init-*.mp4) - CMAF: CMAF (Common Media Application Format) fragmented MP4 – 공통 fMP4 자산을 만들어 두고 HLS/DASH 매니페스트에서 재사용 가능한 형식
- RTSP (TS): 저지연 스트리밍을 위한 MPEG‑TS over RTSP
- HLS:
- 🌐 지원 스트리밍 프로토콜
- HLS (HTTP Live Streaming)
- MPEG‑DASH
- RTSP (Real Time Streaming Protocol)
- 🎚️ 업로드 시 인코딩 설정
- HLS/DASH/CMAF에 사용할 세그먼트 길이 (초)
- H.264 화질/비트레이트를 제어하기 위한 CRF 값 (낮을수록 고화질/고용량)
- 출력 해상도 선택 (Source / 360p / 720p / 1080p)
- 🧭 세그먼트 내비게이션 UI
- 재생 중인 HLS/DASH 세그먼트 목록을 UI에 표시
- 세그먼트 뱃지를 클릭하면 해당 세그먼트 시점으로 시킹
- 📱 반응형 웹 UI
- Docker 및 Docker Compose (권장)
- 또는 수동 설치 시:
- Python 3.7+
- FFmpeg
- Node.js 및 npm (프론트엔드 의존성용)
-
저장소 클론 (아직 하지 않았다면)
git clone https://github.com/YebinLeee/Streaming_project.git cd Streaming_project -
컨테이너 빌드 및 실행
docker compose up --build
-
애플리케이션 접속
- 웹 UI: http://localhost:8000
- API 문서(Swagger): http://localhost:8000/docs
-
애플리케이션 종료
docker compose down
-
저장소 클론
git clone <repository-url> cd streaming-app
-
Python 의존성 설치
pip install -r requirements.txt
-
FFmpeg 설치
- macOS:
brew install ffmpeg - Ubuntu/Debian:
sudo apt-get install ffmpeg - Windows: ffmpeg.org에서 다운로드
- macOS:
-
FastAPI 서버 실행
uvicorn main:app --reload
-
브라우저에서 접속
http://localhost:8000
-
비디오 업로드
- "Choose File" 버튼을 눌러 비디오 파일(MP4, MOV 등)을 선택합니다.
- 원하는 media_format (패키징 방식)을 선택합니다.
hls→ HLS:m3u8 + TS세그먼트dash→ DASH:mpd + fMP4세그먼트cmaf→ CMAF 기반 패키징 (HLS/DASH에서 재사용 가능한 공통 fMP4 세그먼트 생성)ts→ TS 기반 패키징 (내부적으로 HLS 파이프라인 사용, 주로 RTSP와 조합)
- 선택한 media_format에 따라 사용 가능한 streaming_protocol 옵션이 자동으로 활성/비활성화됩니다.
- Segment Duration(초)을 설정합니다.
- CRF 값을 설정합니다 (일반적으로 18–24 권장, 낮을수록 고화질/고용량).
- Resolution (Source / 360p / 720p / 1080p)을 선택합니다.
- Upload & Convert 버튼을 눌러 패키징 및 트랜스코딩을 시작합니다.
-
플레이어 컨트롤
- 플레이어 컨트롤로 재생/일시정지
- 볼륨 슬라이더로 볼륨 조절
- 전체 화면 토글
-
해상도 / 화질 제어
- 업로드 시 선택한 Resolution과 CRF에 따라 실제 출력 화질과 용량이 결정됩니다.
- 해상도를 낮추고 CRF 값을 높이면(예: 24 이상) 용량과 대역폭이 줄어드는 대신 화질이 떨어집니다.
-
세그먼트 내비게이션 (HLS / DASH)
- HLS 또는 DASH 스트림 재생 중, 최근 로드된 세그먼트 목록이 플레이어 아래에 표시됩니다.
- 현재 재생 중인 세그먼트가 강조 표시됩니다.
- 각 세그먼트 뱃지를 클릭하면 해당 세그먼트 시점(대략 세그먼트 인덱스 × 세그먼트 길이)으로 시킹합니다.
- Base URL:
http://localhost:8000 - API base prefix:
/api/v1
| 메소드 | 엔드포인트 | 설명 |
|---|---|---|
GET |
/docs |
인터랙티브 API 문서 (Swagger UI) |
GET |
/health |
서버 상태 확인 엔드포인트 |
POST |
/api/v1/upload/ |
비디오 파일 업로드 및 변환 |
GET |
/api/v1/tasks/ |
모든 변환 작업 목록 조회 |
GET |
/api/v1/tasks/{task_id} |
특정 작업의 상태 조회 |
GET |
/api/v1/stream/{task_id} |
작업의 스트림 정보 조회 |
GET |
/api/v1/chunks/{task_id} |
스트림의 특청 청크/세그먼트 조회 |
| 필드 | 설명 |
|---|---|
| HTTP Method | POST |
| URL | /api/v1/upload/ |
| Request Body (form-data) | 설명 |
|---|---|
file |
입력 비디오 파일 (MP4, MOV 등), 필수 |
media_format |
hls (m3u8/ts) | ts (MPEG‑2 TS) | cmaf (mpd/fMP4) | dash (mpd/fMP4) |
streaming_protocol |
hls | dash | rtsp |
segment_duration |
세그먼트 길이(초, 기본값: 6) |
crf |
H.264 인코딩용 CRF 값 (기본값: 20) |
resolution |
source | 360p | 720p | 1080p |
| Success Response (200) | 설명 |
|---|---|
| JSON | task_id, status, output_path, stream_url, status_url 를 포함하는 응답 객체 |
{
"file": "<binary MP4>",
"media_format": "hls",
"streaming_protocol": "hls",
"segment_duration": 6,
"crf": 20,
"resolution": "source"
}{
"task_id": 1,
"status": "processing",
"message": "Upload and conversion started",
"output_path": "static/output/example_1234/playlist.m3u8",
"stream_url": "/api/v1/stream/1",
"status_url": "/api/v1/tasks/1"
}| 필드 | 설명 |
|---|---|
| HTTP Method | GET |
| URL | /api/v1/tasks/{task_id} |
| Path Params | 설명 |
|---|---|
task_id (int) |
업로드/변환 작업 ID |
| Success Response (200) | 설명 |
|---|---|
| JSON | task_id, status, progress, error, stream_url 를 포함하는 객체 |
{
"task_id": 1,
"status": "completed",
"progress": 100,
"error": null,
"stream_url": "/api/v1/stream/1"
}| 필드 | 설명 |
|---|---|
| HTTP Method | GET |
| URL | /api/v1/tasks/ |
| Success Response (200) | 설명 |
|---|---|
| JSON 배열 | 각 항목에 task_id, status, filename, created_at 필드 포함 |
[
{
"task_id": 1,
"status": "completed",
"filename": "example_1234.mp4",
"created_at": "2025-11-21T01:23:45Z"
},
{
"task_id": 2,
"status": "processing",
"filename": "sample_5678.mp4",
"created_at": "2025-11-21T01:25:10Z"
}
]| 필드 | 설명 |
|---|---|
| HTTP Method | GET |
| URL | /api/v1/stream/{task_id} |
| Path Params | 설명 |
|---|---|
task_id (int) |
업로드/변환 작업 ID |
| Success Response (200) | 설명 |
|---|---|
| JSON | hls_url, dash_url, rtsp_url, streaming_protocol (hls/dash/rtsp), status 포함 |
{
"hls_url": "/static/output/example_1234/playlist.m3u8",
"dash_url": null,
"rtsp_url": null,
"streaming_protocol": "hls",
"status": "completed"
}| 필드 | 설명 |
|---|---|
| HTTP Method | GET |
| URL | /api/v1/chunks/{task_id} |
| Path Params | 설명 |
|---|---|
task_id (int) |
업로드/변환 작업 ID |
| Query Params | 설명 |
|---|---|
chunk_name |
파일 이름 (예: playlist.m3u8, segment_000.ts, playlist.mpd 등) |
chunk_type |
hls | dash |
| Success Response (200) | 설명 |
|---|---|
| File | 요청한 미디어 조각 파일 (FileResponse) |
- 요청 예시:
GET /api/v1/chunks/1?chunk_name=playlist.m3u8&chunk_type=hls- 응답: HLS 플레이리스트 텍스트 (
application/vnd.apple.mpegurl)
Note:
media_format는 출력 파일 레이아웃(패키징)을 의미하고,streaming_protocol은 클라이언트가 스트림에 접근하는 방식(HLS/DASH/RTSP)을 의미합니다.
Packaging format (media_format) |
HLS (HTTP) | MPEG‑DASH (HTTP) | RTSP |
|---|---|---|---|
HLS (hls) – m3u8 + TS |
✔ | ✖ | ✖ |
DASH (dash) – mpd + fMP4 |
✖ | ✔ | ✖ |
TS (ts) – MPEG‑TS segments |
✖ | ✖ | ✔* |
CMAF (cmaf) – CMAF fMP4 segments |
✔ (HLS에서 사용 가능) | ✔ (DASH에서 사용 가능) | ✖ |
* TS 포맷은 이 프로젝트에서 내부적으로 HLS 파이프라인을 사용하지만, 주 사용 목적은 RTSP(MPEG‑TS over RTSP) 조합입니다.
- 기본 RTSP 포트:
8554(환경 변수RTSP_PORT로 변경 가능) - 업로드 시
streaming_protocol=rtsp로 요청하면, 내부적으로 FFmpeg RTSP 서버가 해당 스트림 ID로 실행됩니다.
- 로컬 서버 기준:
rtsp://localhost:8554/<stream_id>- 예:
rtsp://localhost:8554/1
- 원격 서버 예시 (
192.168.0.10):rtsp://192.168.0.10:8554/<stream_id>
stream_id는 업로드/변환 작업에 매핑된 값이며, RTSP 스트림을 생성한 뒤 /api/v1/stream/{task_id} 응답의 rtsp_url 필드에서 확인할 수 있습니다.
- VLC 실행
- 메뉴에서 Media → Open Network Stream... 선택
- "Network URL" 입력 필드에 RTSP URL 입력 (예:
rtsp://localhost:8554/1) - Play 클릭 → RTSP 스트림 재생
Docker 설정은 다음과 같습니다.
-
Ports:
8000: 웹 UI 및 API8554: RTSP 스트리밍 포트
-
Volumes:
./uploads: 업로드된 비디오 파일./static/output: 패키징/트랜스코딩된 출력 파일
-
Environment Variables:
UPLOAD_DIR: 업로드 파일 디렉터리 (기본값:/app/uploads)OUTPUT_DIR: 처리된 파일 디렉터리 (기본값:/app/static/output)RTSP_PORT: RTSP 스트리밍 포트 (기본값:8554)
이 프로젝트는 services/video_converter.py 에서 FFmpeg를 사용해 업로드된 파일을 HLS, DASH, RTSP 스트림으로 변환합니다. 주요 명령과 옵션은 다음과 같습니다.
개념적인 명령 구조:
ffmpeg -y -i <input> \
-c:v libx264 -preset veryfast -crf <crf> [ -vf scale=... ] \
-c:a aac \
-hls_time <segment_duration> \
-hls_playlist_type vod \
-hls_segment_filename playlist_%03d.ts \
-hls_flags independent_segments \
-start_number 0 \
<output>.m3u8-y: 기존 출력 파일을 확인 없이 덮어쓰기-c:v libx264: 비디오를 H.264(AVC)로 재인코딩-preset veryfast: 인코딩 속도/압축 효율 트레이드오프 (개발/테스트에서는 빠른 프리셋 사용)-crf <value>: H.264 화질 제어 (값이 낮을수록 고화질/고용량, 일반적인 범위 18–24)-vf scale=...:resolution설정에 따라 해상도 조정 (예: 360p/720p/1080p)-c:a aac: 오디오를 AAC로 인코딩-hls_time: 세그먼트 길이(초). 예: 6이면 약 6초 단위 TS 세그먼트 생성-hls_playlist_type vod: VOD 스타일 HLS 플레이리스트-hls_segment_filename: 세그먼트 파일 이름 패턴 (예:playlist_000.ts)-hls_flags independent_segments: 세그먼트 경계를 독립 GOP에 맞춰 시킹/전환 안정화-start_number 0: 세그먼트 번호를 0부터 시작
개념적인 명령 구조:
ffmpeg -y -i <input> \
-map 0:v:0 -map 0:a:0 \
-c:v libx264 -preset veryfast -crf <crf> [ -vf scale=... ] \
-c:a aac \
-f dash \
-use_timeline 1 -use_template 1 \
-seg_duration <segment_duration> \
-frag_duration <segment_duration> \
-window_size 5 \
-adaptation_sets "id=0,streams=v id=1,streams=a" \
-init_seg_name "init-stream$RepresentationID$.$ext$" \
-media_seg_name "chunk-stream$RepresentationID$-$Number%05d$.$ext$" \
<output>.mpd-map 0:v:0 -map 0:a:0: 입력의 첫 번째 비디오/오디오 트랙을 명시적으로 선택-c:v libx264,-preset,-crf,-vf,-c:a aac: HLS와 동일한 인코딩 설정-f dash: MPEG‑DASH (MPD + fMP4 세그먼트) 출력-use_timeline 1,-use_template 1: MPD에서 timeline, template addressing 사용-seg_duration,-frag_duration: 세그먼트/프래그먼트 길이(초)-window_size 5: 슬라이딩 윈도우 크기 (라이브 환경에 더 유용하지만 VOD에도 사용 가능)-adaptation_sets: 비디오/오디오를 별도 AdaptationSet으로 정의-init_seg_name,-media_seg_name: init/미디어 세그먼트 파일 이름 패턴
개념적인 명령 구조:
ffmpeg -re -stream_loop -1 -i <input> \
-c:v libx264 -preset veryfast -tune zerolatency \
-f rtsp rtsp://0.0.0.0:<RTSP_PORT>/<stream_id>-re: 입력을 원래 프레임 레이트에 맞춰 읽어 실시간에 가깝게 재생-stream_loop -1: 입력 파일을 무한 반복 (데모/테스트용)-c:v libx264 -preset veryfast: 실시간 H.264 인코딩-tune zerolatency: 버퍼를 최소화해 지연 감소-f rtsp: RTSP 프로토콜로 출력rtsp://0.0.0.0:<port>/<stream_id>: 컨테이너 내부의 모든 인터페이스(0.0.0.0)에 RTSP 서버 바인딩
위 설정들은 데모/개발 환경에 맞춰진 값입니다. 실제 서비스 환경에서는 다음과 같이 조정할 수 있습니다.
crf,preset,resolution을 조정해 화질/비트레이트/CPU 사용량을 튜닝- HLS/DASH용으로 다중 해상도/비트레이트(ABR)를 구성하기 위해 추가
-map,scale, (HLS의 경우)-var_stream_map등을 사용하는 확장
main.py: FastAPI 애플리케이션 및 엔드포인트 진입점templates/: HTML 템플릿index.html: 메인 웹 UI
static/: 정적 파일 (CSS, JS, 출력 비디오)uploads/: 업로드된 원본 비디오 파일
MIT

