1.시스템&인프라/스트리밍

[FFmpeg] RTSP 스트림을 fMP4로 변환해 초저지연 TCP 전송하기

쿼드큐브 2025. 11. 5. 15:46
반응형
반응형

[FFmpeg] RTSP 스트림을 fMP4로 변환해 초저지연 TCP 전송하기

 

📚 목차
1. RTSP 입력 스트림을 안정적으로 수신하는 FFmpeg 옵션 구성법
2. 실시간 스트리밍에 최적화된 fMP4 포맷 설정 방법
3. fMP4 스트림 송출하기 (TCP 소켓 vs Pipe 출력 방식 비교)
✔ 마무리 - 실시간 스트림 파이프라인의 완성

 

 

이 글에서는 FFmpeg 7.x를 활용해 RTSP 입력을 받고, 지연 시간을 최소화하면서 TCP 소켓으로 fMP4 스트림을 내보내는 과정을 명령어 중심으로 실습합니다.

복잡한 개념은 최소화하고, 바로 적용 가능한 명령어와 결과를 중심으로 정리합니다.

FFmpeg로 RTSP 스트림의 fMP4 변환 개념
FFmpeg로 RTSP 스트림의 fMP4 변환 개념

 

1. RTSP 입력 스트림을 안정적으로 수신하는 FFmpeg 옵션 구성법

실시간 파이프라인을 구성할 때 가장 먼저 챙겨야 할 부분은 입력 스트림을 안정적으로 받는 것입니다.

아무리 후단에서 인코딩이나 송출을 잘해도, 입력 단계에서 끊기거나 딜레이가 길면 전체 서비스 품질이 떨어질 수밖에 없습니다.


RTSP(Real Time Streaming Protocol)는 기본적으로 RTP over UDP 방식을 사용합니다. 그런데 UDP는 네트워크 지터나 패킷 손실에 취약하기 때문에, 영상이 자주 끊기거나 프레임이 빠지는 문제가 생길 수 있습니다.

이 때문에 실무에서는 대부분 TCP 기반 RTSP 수신을 사용합니다. TCP는 지연이 조금 늘어날 수는 있지만, 안정성과 일관성이 훨씬 좋아집니다.

 

🔷 실시간 스트림 입력을 위한 추천 옵션

-rtsp_transport tcp     # RTP over TCP 강제 – 패킷 손실 최소화
-fflags nobuffer        # 디코더 버퍼 제거 – 입력 지연 최소화
-flags low_delay        # 인코더 버퍼 제거 – 출력 지연 최소화
-timeout 5000000        # RTSP 서버 응답 및 데이터 수신 타임아웃(단위: μs)

▸ -rtsp_transport tcp : UDP 대신 TCP로 받기 때문에 안정적인 연결을 유지할 수 있습니다.
▸ -fflags nobuffer : 입력 버퍼를 제거해 영상이 도착하는 즉시 디코딩을 시작합니다.
▸ -flags low_delay : 인코더의 내부 지연을 줄여 출력도 빠르게 이어갑니다.
▸ -timeout : 응답이 없거나 데이터 수신이 지연될 때 일정 시간만 기다리고 종료합니다. (단위는 마이크로초, 예: 5000000 = 5초)

 

 

🔷 실습: RTSP 입력만 빠르게 처리해보기

가장 먼저 확인할 것은, 입력 스트림이 끊기지 않고 안정적으로 들어오는가입니다. 이때는 출력은 생략하고, 입력만 처리하도록 null 포맷을 활용합니다.

ffmpeg -hide_banner -rtsp_transport tcp -fflags nobuffer -flags low_delay \
-i rtsp://xxxxxxxxxxxx \
-an -f null -

▸ -i rtsp://... : RTSP 카메라/서버 스트림 입력

▸ -an : 오디오는 제거 (불필요한 인코딩 최소화)

▸ -f null - : 디코딩은 하지만 출력은 버림 (속도, 안정성 체크용)

이 명령은 화면에 아무것도 출력하지 않지만, RTSP로부터 스트림을 안정적으로 받고 있는지를 확인하는 데 유용합니다.
ffmpeg 로그를 통해 프레임 드랍 유무, 연결 상태, 디코딩 속도 등을 바로 확인할 수 있습니다.

RTSP 입력 처리 테스트 결과 화면
RTSP 입력 처리 테스트 결과 화면

 

speed는 “컴퓨팅 처리 여유”를 보여줍니다. 지연 그 자체의 절대값은 아니지만, 저지연을 만들 수 있는 여지가 있는지 판단하는 지표로 매우 유용합니다.

여기서 time=02.40은 처리한 미디어 시간, speed=2.35x는 그 2.4초 분량을 실시간 대비 2.35배 빠르게 처리했다는 뜻입니다.

 

🔸speed가 의미하는 것

ffmpeg가 미디어 시간(media time)을 실시간(wall-clock) 대비 어느 속도로 처리하고 있는지의 비율.

▸ speed = 1.00x → 실시간과 동일한 속도
▸ speed > 1.00x → 실시간보다 빠르게 처리(여유 있음)
▸ speed < 1.00x → 실시간을 못 따라감(큐가 쌓이고 지연↑)

 

🔷 실전 팁: 입력 오류 대응 전략

RTSP 스트리밍 환경에서는 네트워크 상황, 카메라 설정, ffmpeg의 기본 동작 방식에 따라 다양한 문제가 발생할 수 있습니다. 아래는 실무에서 자주 겪는 오류 상황과 대응 방법을 정리한 내용입니다.

 

1. RTSP 연결이 느리거나 계속 대기 상태에 머문다면?

▸ 카메라에 접속 시, 초기 응답이 너무 느리거나 아예 끊기는 문제

-timeout 5000000      # RTSP 서버 응답 및 데이터 수신 타임아웃(단위: μs)

▸ 타임아웃 설정이 없으면, 네트워크가 느리거나 장비 응답이 없을 경우 ffmpeg가 무한정 대기할 수 있습니다.

 

2. 연결은 되는데 스트림이 자주 끊긴다?

▸ FFmpeg가 연결은 성공했지만, 일정 시간 후에 "connection reset" 또는 "frame drop" 현상이 발생

▸ 기본 RTSP 연결이 UDP 기반으로 되어 있어 네트워크 패킷 손실에 민감

-rtsp_transport tcp    # RTP over TCP로 변경하여 안정성 확보

TCP로 전환하면 영상의 지연이 소폭 늘어날 수 있지만, 패킷 유실 없이 더 안정적인 스트림이 유지됩니다.

 

3. 프레임 드랍이 자주 발생하거나 초반 지연이 길다?

▸ 스트림 재생 시작 시 버벅이거나, 디코딩이 늦게 시작되는 현상

-fflags nobuffer               # 디코더 버퍼 제거
-probesize 32                  # 입력 스트림 분석을 최소화
-analyzeduration 0            # 분석 시간 제거 (즉시 시작)

이 옵션들은 ffmpeg가 "스트림을 이해하려고" 기다리는 시간을 줄이고, 즉시 처리에 집중하도록 만듭니다.

 

2. 실시간 스트리밍에 최적화된 fMP4 포맷 설정 방법

RTSP 입력을 받아 다른 장치나 애플리케이션으로 영상을 전달할 때 가장 널리 쓰이는 출력 포맷 중 하나가 fMP4(Fragmented MP4)입니다.
fMP4는 이름 그대로 조각(Fragment) 단위로 잘라 전송할 수 있는 구조를 가지고 있어, 실시간 스트리밍 환경에 훨씬 잘 맞습니다.

 

🔷 왜 일반 MP4가 아니라 fMP4인가요?

일반 MP4는 원래 파일 저장에 최적화된 포맷입니다.

영상 재생에 필요한 핵심 정보(moov atom)가 파일 끝 부분에 기록되기 때문에, 전체 파일이 다 도착하기 전에는 제대로 재생할 수 없습니다. 따라서 스트리밍에는 긴 대기 시간이 발생하거나 아예 재생이 불가능해질 수 있습니다.

 

반면 fMP4는 다음 특징 덕분에 실시간 처리에 강합니다:

▸조각 단위(fragment) 전송 가능 → 데이터가 도착하는 즉시 재생 가능
▸moov atom 없이도 재생 가능 → 전체 파일이 없어도 즉시 시작
▸플레이어 호환성 우수 → ffplay, VLC, Safari, 브라우저(MSE) 등과 잘 맞음

 

🔷 FFmpeg에서 fMP4 스트리밍 설정하는 방법

실시간 송출을 위해선 단순히 -f mp4만 지정하는 것으로는 부족합니다.
추가적인 -movflags 옵션을 더해 fragmented 모드를 활성화해야 합니다.

-f fmp4
-movflags frag_keyframe+empty_moov+default_base_moof

▸ frag_keyframe : I-frame(=keyframe) 단위로 fragment를 나눕니다. → 안정적인 분할 송출

▸ empty_moov : 초기 재생을 위해 빈 moov atom을 생성합니다. 전체 파일이 없더라도 재생 가능

▸ default_base_moof : 플레이어가 moof atom을 참조할 때 계산을 단순화하여 지연 감소에 유리

 

이 조합을 쓰면 fMP4 스트림을 끊김 없이 재생할 수 있습니다.

 

🔷 실습 예시: fMP4 옵션만 적용해 보기

아래 명령어는 RTSP 입력을 받아 오디오를 제거하고, 비디오를 그대로 복사하여 fMP4 형식으로 출력합니다.

출력은 stdout(pipe:1)으로 흘려 보내므로, 다른 프로세스나 TCP 전송과 쉽게 연동할 수 있습니다.

ffmpeg -hide_banner \
-rtsp_transport tcp -i rtsp://xxxxxxxxxxx \
-c:v copy -an -avoid_negative_ts make_zero \
-f mp4 -movflags frag_keyframe+empty_moov+default_base_moof \
pipe:1 | ffplay -hide_banner -fflags nobuffer -flags low_delay -f mp4 -i -

주의할 점은 movflags 설정이 없으면 실시간 수신 시 재생이 되지 않거나, 영상 시작이 지연될 수 있다는 것입니다.

 

🔷 실무 적용 팁

▸ 카메라가 이미 H.264/초저지연 세팅이라면 -c:v copy가 최선입니다.
▸ 하지만 카메라의 GOP 구조가 너무 길거나, B-frame을 많이 쓰면 실시간성이 떨어질 수 있습니다.
▸ 이런 경우에는 -c:v libx264 -preset ultrafast -tune zerolatency -g N (GOP 강제)로 재인코딩하는 게 더 안정적입니다.

▸ 브라우저 스트리밍 → empty_moov와 default_base_moof는 웹 브라우저(MSE 기반) 재생에서 필수 옵션으로, 재생 시작을 빠르게 하고 호환성을 보장합니다.

 

3. fMP4 스트림 송출하기 (TCP 소켓 vs Pipe 출력 방식 비교)

fMP4 포맷으로 변환된 스트림을 다른 프로그램으로 전달하는 방법에는 크게 TCP 소켓 전송 방식과 Pipe 출력 방식이 있습니다. 두 방식 모두 FFmpeg에서 자주 사용되며, 목적과 환경에 따라 장단점이 뚜렷하게 갈립니다.


단순히 “TCP는 안정적이고 Pipe는 빠르다” 정도로 설명하면 부족합니다. 실제 실무에서는 서비스 안정성, 재연결 가능 여부, 지연 시간, 시스템 자원 사용량까지 고려해야 하므로 두 방식을 깊이 이해하는 것이 중요합니다.

 

🔷 방법 1: TCP 소켓 전송 방식

TCP 출력은 FFmpeg가 마치 스트리밍 서버처럼 동작하는 구조입니다.
송출 측은 특정 IP와 포트로 데이터를 흘려보내고, 수신 측(클라이언트)은 해당 포트에 접속해 스트림을 받습니다.


예를 들어, 다음 명령어를 실행하면 FFmpeg는 127.0.0.1의 9000번 포트로 fMP4 스트림을 실시간 송출합니다.

 

✔️ 실습 예: TCP로 fMP4 전송(Windows CMD환경)

1. ffplay로 TCP 수신 대기

먼저 수신 측(클라이언트)을 준비합니다. ffplay를 리스너 모드로 실행해 포트를 열어 둡니다.

ffplay -hide_banner -fflags nobuffer -flags low_delay -i tcp://127.0.0.1:9000?listen=1

즉, ffplay가 TCP 서버 역할을 하여 포트 9000에서 들어오는 fMP4 데이터를 실시간으로 재생합니다.

 

2. ffmpeg로 TCP 송출

이제 송신 측(프로듀서)을 실행합니다. RTSP 카메라에서 받은 영상을 fMP4로 변환해 127.0.0.1:9000으로 전송합니다.

ffmpeg -hide_banner -rtsp_transport tcp \
-fflags nobuffer -flags low_delay \
-i rtsp://xxxxxxxxxx1 \
-c:v copy -an -avoid_negative_ts make_zero \
-f mp4 -movflags frag_keyframe+empty_moov+default_base_moof \
tcp://127.0.0.1:9000

 

3. 테스트 결과

rtsp를 fmp4형식으로 tcp://127.0.0.1:9000으로 전송하는 화면
ffplay로 fmp4를 수신하여 재생하는 화면
ffplay로 fmp4를 수신하여 재생하는 화면
ffplay로 수신된 영상 화면
ffplay로 수신된 영상 화면

 

▸ ffplay 창에서 RTSP 카메라 영상이 지연 없이 재생되는 것을 확인할 수 있습니다.
▸ -c:v copy를 사용했기 때문에 CPU 부하가 거의 없고, 원본과 동일한 품질이 유지됩니다.
▸ TCP 전송이므로 UDP 대비 안정성이 높으며, 프레임 드랍이나 깨짐 현상이 줄어듭니다.

 

 

🔷 방법 2: Pipe 출력 방식

Pipe 출력은 FFmpeg가 처리한 결과를 표준 출력(stdout)으로 흘려보내고, 다른 프로그램이 이를 표준 입력(stdin)으로 받아 처리하는 구조입니다.

즉, “한 프로그램의 출력 → 다른 프로그램의 입력”이라는 유닉스 철학을 그대로 따른 방식입니다.


이 방법은 디스크에 파일을 남기지 않고 메모리 버퍼만 이용하기 때문에, 실시간 처리와 초저지연 환경에서 매우 강력합니다.

 

✔️ 실습 예: ffmpeg → pipe → ffplay

ffmpeg -hide_banner -rtsp_transport tcp \
-fflags nobuffer -flags low_delay \
-i rtsp://10.1.73.227/ONVIF/MediaInput?profile=1_def_profile1 \
-c:v copy -an -avoid_negative_ts make_zero \
-f mp4 -movflags frag_keyframe+empty_moov+default_base_moof \
pipe:1 | ffplay -hide_banner -fflags nobuffer -flags low_delay -f mp4 -i -

ffmpeg가 pipe:1로 stdout에 흘려보낸 fMP4 데이터를 ffplay가 stdin(-)에서 받아 재생하는 구조입니다.

 

🔸Windows 환경 주의 사항

1. CMD(명령 프롬프트)

▸ 위 명령어 그대로 정상 작동합니다.
▸ CMD는 전통적인 파이프(|) 문법을 그대로 지원하므로 문제없이 연결됩니다.

 

2. PowerShell

▸ 기본 파이프(|)가 “텍스트(Object 파이프라인)” 처리로 동작합니다.
▸ 이 때문에 바이너리 스트림(fMP4 데이터) 이 깨지거나 전달되지 않아 재생이 안 됩니다.

▸ PowerShell에서 cmd /c "…" 로 감싸 실행

cmd /c "ffmpeg ... pipe:1 | ffplay ..."

 

테스트 결과

Pipe 출력 방식 테스트 화면
Pipe 출력 방식 테스트 화면
Pipe 출력 방식 영상 재생 화면
Pipe 출력 방식 영상 재생 화면

 

🔷 TCP vs Pipe 비교 요약

구분 TCP 소켓 Pipe 출력
지연 시간 짧음(UDP보단 길고, Pipe보단 김) 최단 (초저지연)
안정성 패킷 유실 자동 복구, 재접속 가능 중단 시 전체 종료
재연결 가능 (클라이언트 다시 붙을 수 있음) 불가능
멀티 클라이언트 중간 서버 통해 가능 불가능
실무 활용 뷰어 앱, 스트리밍 서버, 다수 수신 환경 AI 추론, 단일 파이프라인, CLI 분석

 

🔷 실무 팁

▸ TCP 소켓은 지속적 서비스(CCTV 뷰어, 웹 스트리밍 서버)에 적합합니다. 안정적이며 재연결이 가능해 사용자 경험이 좋습니다.
▸ Pipe 출력은 실시간 분석 파이프라인에 적합합니다. 예를 들어 “RTSP → FFmpeg → Pipe → AI 모델” 같은 구조에서 가장 강력합니다. 단, 장애에 민감하므로 자동 재시작 스크립트와 함께 사용해야 합니다.
▸ 윈도우 환경에서는 Named Pipe(\\.\pipe\mypipe)를 활용하면 리눅스 파이프처럼 동작시킬 수 있습니다.
▸ 네트워크 분배가 필요하다면 TCP, 단일 빠른 처리가 필요하다면 Pipe를 선택하는 것이 이상적입니다.

 

✔ 마무리 - 실시간 스트림 파이프라인의 완성

이번 글에서는 RTSP 입력 → fMP4 변환 → TCP/파이프 출력이라는 단계를 통해, FFmpeg로 실시간 스트림을 안정적으로 처리하는 방법을 실습했습니다.

 

핵심은 크게 세 가지였습니다.
1. 안정적 입력 확보
-rtsp_transport tcp, -timeout 같은 옵션으로 네트워크 불안정성을 보완하고, -fflags nobuffer, -flags low_delay로 지연을 최소화했습니다.


2. fMP4 포맷으로의 변환
일반 MP4와 달리 fragmented MP4는 moov atom이 없어도 조각 단위로 전송할 수 있어, 즉시 재생·브라우저 호환성·저지연 스트리밍에서 강력합니다.

frag_keyframe+empty_moov+default_base_moof 조합은 사실상 필수 세트였습니다.


3. 송출 방식의 선택
TCP 소켓은 안정성과 재접속이 필요한 뷰어/스트리밍 서비스에 적합했고,
Pipe 출력은 초저지연 단일 파이프라인을 원하는 AI 추론·CLI 분석 환경에 최적화되어 있었습니다.

 

✔️ 실무 적용 팁

목적 옵션 세트
지연 최소화 -fflags nobuffer, -probesize 32, -analyzeduration 0
연결 안정화 -rtsp_transport tcp, -timeout
브라우저 호환 스트림 -f mp4, -movflags frag_keyframe+empty_moov+default_base_moof
스트리밍 품질 개선 -g 30, -keyint_min 30, -sc_threshold 0

 

 

👉 관련 글 링크

1. [FFmpeg] Windows 환경에서 FFmpeg 7.1 정적 빌드하기 – MinGW64 + H.264 인코딩 지원

2. [FFmpeg] FFmpeg 7.1 + H.265(libx265) 지원 정적 빌드 – 기존 MinGW64 환경 확장

3. [FFmpeg] CUDA 가속 포함 FFmpeg 7.1 정적 빌드 – MinGW64 + libx264/x265 + NVENC

4. [FFmpeg] SHA-256 Digest 인증 대응 FFmpeg 7.1 정적 빌드 – MinGW64 + CUDA + 인증 로직 패치

5. [FFmpeg] RTSP 스트림을 fMP4로 변환해 초저지연 TCP 전송하기

6. [FFmpeg] 인코딩 실습: CPU(libx264)와 CUDA(h264_nvenc) 비교

 

 

반응형

※ 게시된 글 및 이미지 중 일부는 AI 도구의 도움을 받아 생성되거나 다듬어졌습니다.

반응형