본문으로 건너뛰기 MediaMTX 완벽 가이드 | 하나의 서버로 RTSP·RTMP·HLS·WebRTC·SRT 브리징하기

MediaMTX 완벽 가이드 | 하나의 서버로 RTSP·RTMP·HLS·WebRTC·SRT 브리징하기

MediaMTX 완벽 가이드 | 하나의 서버로 RTSP·RTMP·HLS·WebRTC·SRT 브리징하기

이 글의 핵심

MediaMTX는 의존성 없는 단일 바이너리로 RTSP·RTMP·HLS·WebRTC·SRT를 서로 변환·중계하는 미디어 서버입니다. 이 글은 설치와 기본 설정부터, RTSP IP카메라 영상을 브라우저에서 WebRTC/HLS로 보는 실전 구성, 녹화, 인증, API 자동화, 그리고 실무에서 자주 막히는 NAT/방화벽·WebRTC ICE 문제 해결까지 정리합니다.

들어가며

IP카메라나 드론, 로봇에 달린 카메라의 RTSP 스트림을 브라우저에서 그냥 보고 싶을 뿐인데, 막상 시도해보면 대부분의 웹 브라우저는 RTSP를 직접 재생하지 못한다는 사실에 부딪힙니다. RTSP를 브라우저가 이해하는 형식(HLS나 WebRTC)으로 바꿔주는 중계 서버가 필요한데, 예전에는 이걸 직접 구현하려면 GStreamer 파이프라인을 손으로 짜거나 nginx-rtmp 모듈을 컴파일하는 등 진입장벽이 꽤 높았습니다.

MediaMTX는 이 문제를 단일 바이너리로 해결합니다. 별도의 의존성 설치 없이 실행 파일 하나와 YAML 설정 파일만으로 RTSP·RTMP·HLS·WebRTC·SRT 사이를 자유롭게 변환하고 중계할 수 있습니다. Go로 작성되어 있어 빌드된 바이너리를 그대로 복사해 실행하면 되고, Docker 이미지도 공식으로 제공됩니다. 이 글은 설치부터 실전에서 가장 많이 쓰이는 “RTSP 카메라 → 브라우저 시청” 구성, 녹화, 자동화, 그리고 실무에서 자주 마주치는 문제 해결까지 다룹니다. RTSP 프로토콜 자체의 상세한 동작 원리는 RTSP 완전 참조를, WebRTC의 시그널링/ICE 흐름은 WebRTC 완전 참조를 함께 참고하면 이해가 빨라집니다.


1. 설치

바이너리로 직접 실행

# Linux amd64 예시 — 최신 릴리스는 GitHub Releases 페이지에서 버전 확인
curl -L -O https://github.com/bluenviron/mediamtx/releases/latest/download/mediamtx_linux_amd64.tar.gz
tar -xzf mediamtx_linux_amd64.tar.gz
./mediamtx

실행하면 기본 설정으로 RTSP(8554), RTMP(1935), HLS(8888), WebRTC(8889), SRT(8890), API(9997) 포트가 동시에 열립니다. 별도 설치 과정 없이 바이너리 하나로 모든 프로토콜 서버가 뜬다는 점이 다른 미디어 서버 대비 가장 큰 진입장벽 차이입니다.

Docker로 실행

docker run --rm -it \
  -p 8554:8554 -p 1935:1935 -p 8888:8888 -p 8889:8889 -p 8890:8890/udp \
  -v $(pwd)/mediamtx.yml:/mediamtx.yml \
  bluenviron/mediamtx:latest

2. 기본 개념: path

MediaMTX 설정의 핵심 단위는 path입니다. path는 하나의 스트림 채널을 의미하며, 두 가지 방식으로 채워집니다.

  1. 누군가 push(발행)하는 경우: FFmpeg나 카메라가 RTSP/RTMP로 이 path에 스트림을 보냄
  2. MediaMTX가 직접 pull(가져오는)하는 경우: 설정 파일에 이미 존재하는 RTSP 카메라 주소를 지정해두면 MediaMTX가 능동적으로 연결해서 가져옴
# mediamtx.yml
paths:
  cam1:
    source: rtsp://admin:[email protected]:554/stream1
    sourceOnDemand: true   # 시청자가 없으면 카메라에 연결하지 않음 (대역폭 절약)

  live:
    # source를 지정하지 않으면 누군가 RTMP/RTSP로 push하기를 기다림

sourceOnDemand: true는 실무에서 특히 중요한 옵션입니다. 이게 없으면 MediaMTX가 항상 카메라에 연결된 상태를 유지하는데, 카메라가 동시 접속 수 제한이 있는 저가형 IP카메라라면 다른 클라이언트(카메라 자체 앱 등)가 접속하지 못하는 문제가 생길 수 있습니다. sourceOnDemand를 켜두면 실제로 누군가 이 path를 시청할 때만 카메라에 연결하고, 시청자가 없으면 연결을 끊어둡니다.


3. 실전 구성: RTSP 카메라를 브라우저에서 WebRTC로 보기

가장 흔한 요구사항인 “IP카메라 영상을 별도 앱 없이 웹페이지에서 실시간으로 보기”를 구성해봅니다.

paths:
  cam1:
    source: rtsp://admin:[email protected]:554/stream1
    sourceOnDemand: true

webrtcAdditionalHosts:
  - 203.0.113.10   # 서버의 공인 IP (외부 접속을 지원하려면 필수)

카메라가 연결되면 브라우저에서 http://서버주소:8889/cam1으로 접속하는 것만으로 자동 생성된 재생 페이지에서 스트림을 볼 수 있습니다. 직접 만든 웹페이지에 임베드하고 싶다면 WHEP(WebRTC-HTTP Egress Protocol) 엔드포인트를 JavaScript에서 호출하면 됩니다.

지연시간이 중요하지 않고 다수의 시청자를 대상으로 한다면 HLS가 더 적합합니다. 같은 path에 대해 http://서버주소:8888/cam1/index.m3u8가 자동으로 함께 제공되므로, 별도 설정 없이 WebRTC와 HLS 두 가지 방식을 동시에 제공할 수 있습니다.

언제 어느 쪽을 쓸지 판단 기준은 다음과 같습니다.

상황추천 프로토콜이유
CCTV 실시간 모니터링, 원격 로봇 조종WebRTC지연시간 500ms 이하가 필요
다수 시청자, 지연시간 3~10초 허용HLSHTTP 캐싱/CDN 활용이 쉬움
방송 송출 장비와의 호환RTMP대부분의 인코더가 기본 지원
열악한 네트워크 환경SRT패킷 손실 복구(ARQ) 내장

4. 녹화

MediaMTX는 별도의 녹화 도구 없이 path 단위로 세그먼트 녹화를 지원합니다.

paths:
  cam1:
    source: rtsp://admin:[email protected]:554/stream1
    record: yes
    recordPath: /recordings/%path/%Y-%m-%d_%H-%M-%S.mp4
    recordSegmentDuration: 1h
    recordDeleteAfter: 168h   # 7일 후 자동 삭제

recordDeleteAfter를 설정해두지 않으면 디스크가 조용히 가득 차는 사고로 이어지기 쉽습니다. CCTV처럼 24시간 녹화하는 용도라면 보관 기간을 반드시 명시하고, 디스크 사용량을 모니터링하는 별도 알림도 함께 두는 것을 권합니다.


5. 인증

기본 설정에서는 누구나 스트림을 발행하거나 시청할 수 있어, 인터넷에 노출된 서버라면 인증 설정이 필수입니다.

authInternalUsers:
  - user: publisher
    pass: strong-password-here
    permissions:
      - action: publish
        path: cam1

  - user: viewer
    pass: another-password
    permissions:
      - action: read
        path: cam1

  - user: any
    permissions: []   # 그 외 모든 접근 거부

발행 권한과 시청 권한을 분리해두면, 카메라 인코더 쪽 자격증명이 유출되더라도 시청 전용 계정까지 함께 뚫리지는 않습니다. 실무에서는 여기서 더 나아가 외부 인증 서버(HTTP 콜백 방식의 authHTTPAddress)와 연동해 기존 사용자 시스템의 토큰을 그대로 검증하는 구성도 흔히 씁니다.


6. runOnPublish / runOnReady로 자동화하기

스트림 상태 변화에 맞춰 외부 명령을 실행할 수 있습니다. 예를 들어 스트림이 시작되면 알림을 보내거나, FFmpeg로 추가 처리를 트리거하는 식입니다.

paths:
  cam1:
    source: rtsp://admin:[email protected]:554/stream1
    runOnReady: >
      ffmpeg -i rtsp://localhost:8554/cam1
      -vf "drawtext=text='%{localtime}':x=10:y=10:fontcolor=white"
      -c:v libx264 -f rtsp rtsp://localhost:8554/cam1_timestamped
    runOnReadyRestart: yes

이 예시는 원본 스트림에 타임스탬프를 입히는 FFmpeg 프로세스를 별도 path로 다시 발행하는 구성입니다. runOnReadyRestart: yes를 켜두면 FFmpeg 프로세스가 예기치 않게 죽어도 MediaMTX가 자동으로 재시작해줘서, 장시간 운영 중 발생하는 크래시에도 사람이 매번 개입할 필요가 없습니다.


7. API로 path 동적 관리

설정 파일을 재시작 없이 바꾸고 싶다면 내장 HTTP API를 사용합니다.

# 새 path를 런타임에 추가
curl -X POST http://localhost:9997/v3/config/paths/add/cam2 \
  -H "Content-Type: application/json" \
  -d '{"source": "rtsp://admin:[email protected]:554/stream1"}'

# 현재 활성 스트림 목록 조회
curl http://localhost:9997/v3/paths/list

카메라를 관리자 화면에서 등록/삭제하는 서비스를 만든다면, 서버를 재시작하지 않고도 이 API를 호출해 path를 즉시 추가/제거할 수 있어 실질적인 다중 카메라 관리 시스템의 기반으로 쓰기 좋습니다.


8. 실무에서 자주 겪는 문제

NAT/방화벽 뒤의 RTSP 카메라

RTSP는 제어 채널(TCP)과 미디어 채널(UDP, 기본적으로 RTP)이 분리되어 있어, NAT 환경에서는 미디어 채널만 막히는 경우가 흔합니다. MediaMTX 설정에서 rtspTransportstcp로 고정하면 미디어 데이터도 제어 채널과 같은 TCP 연결에 실어 보내므로, UDP가 막힌 네트워크에서도 안정적으로 동작합니다.

rtspTransports: [tcp]

WebRTC가 로컬에서는 되는데 외부에서 안 됨

앞서 FAQ에서 다룬 것처럼 webrtcAdditionalHosts에 공인 IP를 등록하지 않으면 ICE candidate가 사설 IP로만 생성되어 외부 브라우저가 연결 경로를 찾지 못합니다. 클라우드 환경이라면 인스턴스의 공인 IP를, 도메인을 쓴다면 해당 도메인을 등록합니다.

카메라 연결이 주기적으로 끊김

일부 저가형 IP카메라는 동시 RTSP 세션 수를 1~2개로 제한합니다. MediaMTX의 sourceOnDemand를 켜두지 않은 상태에서 다른 클라이언트(카메라 제조사 앱, NVR 등)가 동시에 접속하면 카메라가 세션을 강제로 끊는 경우가 있습니다. 이런 증상이 반복된다면 카메라의 동시 접속 제한을 먼저 확인하고, MediaMTX를 유일한 RTSP 클라이언트로 두는 구조로 정리하는 것이 근본적인 해결책입니다.


정리

MediaMTX는 프로토콜 변환이라는 좁지만 반복적으로 필요한 문제를 단일 바이너리로 해결해주는 도구입니다.

  1. path 단위로 스트림을 정의하고, push/pull 두 가지 방식으로 소스를 연결
  2. 같은 path를 RTSP/RTMP/HLS/WebRTC/SRT로 동시에 노출 가능
  3. sourceOnDemand로 불필요한 카메라 연결을 줄이고
  4. runOnReady 훅으로 FFmpeg 등 외부 도구와 조합해 자동화
  5. 인터넷에 노출한다면 인증(authInternalUsers)과 WebRTC용 공인 IP 등록(webrtcAdditionalHosts)은 필수

카메라 영상을 브라우저에서 보여줘야 하는 대부분의 요구사항은 이 정도 구성만으로 별도의 트랜스코딩 서버 없이 해결됩니다.