다크 모드
이미지/비디오 처리
truloop-media-service의 미디어 변환 파이프라인을 설명합니다. 이미지는 WebP 포맷으로, 비디오는 HEVC (H.265)로 최적화됩니다.
처리 파이프라인 개요
이미지 처리
처리 단계
원본 업로드
원본 이미지는 업로드 시점에 그대로 S3에 저장됩니다.
Optimized 변환 생성
최대 1440x3200 해상도로 리사이즈 후 WebP 포맷으로 변환합니다. 공개 표시용 이미지입니다.
Thumbnail 변환 생성
최대 500x500 해상도로 리사이즈 후 WebP 포맷으로 변환합니다. 목록/미리보기용 이미지입니다.
변환 사양
| 변환 | 최대 해상도 | 포맷 | WebP 품질 | 용도 |
|---|---|---|---|---|
| Original | 원본 유지 | 원본 유지 | - | 원본 보관 |
| Optimized | 1440 x 3200 | WebP | 90 | 공개 표시 |
| Thumbnail | 500 x 500 | WebP | 75 | 목록, 미리보기 |
기술 구현
- 라이브러리:
disintegration/imaging(리사이즈) +chai2010/webp(WebP 인코딩) - 리사이즈: 비율 유지 Scale-down (원본보다 작을 때만 축소, Lanczos 필터)
- EXIF 처리:
rwcarlsen/goexif로 EXIF orientation 추출 후 자동 회전 적용 - 병렬 처리: 각 변환은 goroutine으로 병렬 생성
- EXIF: 업로드 시 메타데이터 추출 (카메라 모델, GPS, 촬영일시 등)
비디오 처리
처리 단계
원본 업로드
원본 비디오는 _original 접미사로 S3에 저장됩니다.
메타데이터 추출
FFprobe로 해상도, 코덱, HDR 정보, 회전 메타데이터를 추출합니다. 단일 호출로 모든 정보를 가져옵니다.
병렬 처리 (썸네일 + 트랜스코딩)
WebP 썸네일 생성과 비디오 트랜스코딩이 동시에 실행됩니다. 각각 완료 시 독립적으로 media-ready-queue에 메시지를 발행합니다.
인코딩 전략
비디오는 기본적으로 **HEVC (H.265)**로 인코딩됩니다. 입력 코덱과 해상도에 따라 스마트하게 처리 방식을 결정합니다.
| 입력 조건 | 처리 방식 | 설명 |
|---|---|---|
| 이미 HEVC + 해상도 이내 | Remux (복사) | 비디오 스트림을 재인코딩하지 않고 MP4 컨테이너로 복사 |
| 그 외 | HEVC 트랜스코딩 | libx265로 풀 트랜스코딩 |
FFmpeg 인코딩 사양
| 항목 | HEVC (기본) | H.264 (폴백) |
|---|---|---|
| 최대 해상도 | 1920 x 1080 | 1920 x 1080 |
| 비디오 코덱 | libx265 | libx264 |
| 오디오 코덱 | AAC | AAC |
| 오디오 비트레이트 | 128k | 128k |
| CRF | 23 | 20 |
| 프리셋 | 파일 크기 기반 동적 (fast/medium) | medium |
| 출력 포맷 | MP4 (+faststart) | MP4 (+faststart) |
| 태그 | hvc1 (iOS/Safari 호환) | - |
HDR 보존
| HDR 타입 | 감지 기준 | 인코딩 파라미터 |
|---|---|---|
| HDR10 | BT.2020 + SMPTE ST 2084 | 10-bit, master-display, max-cll 보존 |
| HLG | BT.2020 + ARIB STD-B67 | 10-bit, color parameter 보존 |
| SDR | 그 외 | 일반 인코딩 |
정보
비디오 썸네일에서 HDR 콘텐츠는 zscale + tonemap=hable로 SDR 변환하여 WebP로 추출합니다. HDR 그대로 WebP를 만들면 색이 바래 보이기 때문입니다.
변환 사양
| 변환 | 접미사 | 포맷 | 용도 |
|---|---|---|---|
| Original | _original.{ext} | 원본 유지 | 원본 보관 |
| Optimized | _optimized.mp4 | HEVC (H.265) MP4 | 스트리밍 재생 |
| Thumbnail | _thumbnail.webp | WebP | 미리보기 이미지 |
Worker Pool
동적 Worker Pool 관리
DynamicPool이 CPU 코어 수에 따라 Worker를 자동 할당합니다.
| 설정 | 환경변수 | 기본값 |
|---|---|---|
| 미디어 처리 Worker | MAX_MEDIA_PROCESSING_WORKERS | 2 |
| S3 업로드 Worker | MAX_S3_UPLOAD_WORKERS | 0 (auto-detect) |
| 비디오 처리 Worker | MAX_VIDEO_PROCESSING_WORKERS | 0 (auto-detect) |
자동 감지 시 runtime.NumCPU() 기반으로 최적 Worker 수를 계산합니다.
MediaWorker 처리 흐름
정보
비디오는 썸네일과 트랜스코딩이 병렬 goroutine으로 실행되며, 각각 완료 시 독립적인 Ready 메시지를 발행합니다. 이를 통해 썸네일이 먼저 준비되면 클라이언트가 즉시 미리보기를 표시할 수 있습니다.
성능 고려사항
| 항목 | 전략 |
|---|---|
| 메모리 | Streaming 아키텍처로 힙 사용 최소화 |
| CPU | Worker Pool이 코어 수 기반 자동 스케일, 비디오는 numCPU-1 제한 |
| 동시성 | Prometheus 메트릭으로 동시 작업 수 모니터링 |
| 파일 크기 | 최대 2GB, 크기별 업로드 전략 자동 선택 |
| 네트워크 | 30분 타임아웃, Streaming으로 메모리 스파이크 방지 |
| GC | sync.Pool 기반 Buffer Pool 재사용으로 GC 부담 감소 |
| 비디오 최적화 | HEVC 입력 시 Remux 스킵으로 처리 시간 대폭 단축 |
Prometheus 메트릭
모든 메트릭은 truloop_media 네임스페이스를 사용합니다.
| 메트릭 | 타입 | 설명 |
|---|---|---|
truloop_media_media_upload_duration_seconds | Histogram | 업로드 소요 시간 |
truloop_media_media_size_bytes | Histogram | 파일 크기 분포 |
truloop_media_media_processing_duration_seconds | Histogram | 처리 소요 시간 |
truloop_media_media_processing_total | Counter | 처리 작업 횟수 |
truloop_media_concurrent_operations | Gauge | 현재 동시 작업 수 |
truloop_media_s3_operations_total | Counter | S3 작업 횟수 |
truloop_media_ffmpeg_execution_duration_seconds | Histogram | FFmpeg 실행 시간 |
truloop_media_media_worker_messages_processed_total | Counter | Worker 처리 메시지 수 |
truloop_media_media_worker_concurrent_jobs | Gauge | Worker 동시 작업 수 |
truloop_media_http_request_duration_seconds | Histogram | HTTP 요청 지연 시간 |
truloop_media_goroutine_count | Gauge | 현재 goroutine 수 |
변경 이력
| 날짜 | 내용 |
|---|---|
| 2026-03-10 | 소스 코드 기반 전면 검증: HEVC 기본 코덱 반영, 파이프라인 다이어그램 (Redis, 미디어 타입, 비디오 썸네일), 이미지 변환명 (Public→Optimized, 품질값), 비디오 인코딩 전략 (Remux, HDR 보존, 톤매핑), MediaWorker 흐름, 성능/메트릭 섹션 |