Skip to content
Search

MediaMTX

lastmod 2025-12-05created 2023-04-1222 edits

A ready-to-use SRT / RTSP / RTMP / LL-HLS / WebRTC media server and media proxy that can read, publish, and proxy video and audio streams.

Its old name is rtsp-simple-server.

Categories

Features

  • Publish live streams to the server
  • Read live streams from the server
  • Proxy streams from other servers or cameras, always or on-demand
  • Streams are automatically converted from one protocol to another. For example, you can publish a stream with RTSP and read it with HLS.
  • Serve multiple streams at once in separate paths
  • Authenticate users; use internal or external authentication
  • Redirect readers to other RTSP servers (load balancing)
  • Query and control the server through the API
  • Reload the configuration without disconnecting existing clients (hot reloading)
  • Read Prometheus-compatible metrics
  • Run external commands when clients connect, disconnect, read or publish streams
  • Compatible with Linux, Windows and macOS, does not require any dependency or interpreter, it's a single executable

Docker Image

docker run --rm -it --network=host bluenviron/mediamtx:latest

FFmpeg-compatible package name: bluenviron/mediamtx:latest-ffmpeg

The --network=host flag is mandatory because Docker can change the source port of UDP packets for routing reasons, and this prevents the RTSP server from identifying the senders of the packets. This issue can be avoided by disabling the UDP transport protocol.

# 8554 - rtsp
# 1935 - rtmp
docker run --rm -it \
-e MTX_PROTOCOLS=tcp \
-p 8554:8554 \
-p 1935:1935 \
-p 8888:8888 \
-p 8889:8889 \
bluenviron/mediamtx

The mediamtx.yml file can be shared with the host at the following path:

-v $PWD/mediamtx.yml:/mediamtx.yml

How to set environment variables for the mystream path:

docker run --rm -it -e MTX_PATHS_MYSTREAM_SOURCE=rtsp://admin:[email protected]/stream1 -e MTX_PROTOCOLS=tcp -p 8554:8554 -p 1935:1935 -p 8888:8888 -p 8889:8889 bluenviron/mediamtx

To connect to the API as an Anonymouse user:

docker run --rm -it \
    -e MTX_API=yes \
    -e MTX_RTSPTRANSPORTS=tcp \
    -e MTX_AUTHINTERNALUSERS_0_USER=any \
    -e MTX_AUTHINTERNALUSERS_0_PASS= \
    -e MTX_AUTHINTERNALUSERS_0_PERMISSIONS_0_ACTION=publish \
    -e MTX_AUTHINTERNALUSERS_0_PERMISSIONS_1_ACTION=read \
    -e MTX_AUTHINTERNALUSERS_0_PERMISSIONS_2_ACTION=playback \
    -e MTX_AUTHINTERNALUSERS_0_PERMISSIONS_3_ACTION=api \
    -e MTX_AUTHINTERNALUSERS_0_PERMISSIONS_4_ACTION=metrics \
    -e MTX_AUTHINTERNALUSERS_0_PERMISSIONS_5_ACTION=pprof \
    -p 8554:8554 \
    -p 9997:9997 \
    bluenviron/mediamtx:latest-ffmpeg

Publishing streams: by program

Assuming Docker is running and the address for publishing is rtsp://localhost:8554/mystream

FFmpeg

You can publish a video/audio file.

ffmpeg -re -stream_loop -1 -i file.ts -c copy -f rtsp rtsp://localhost:8554/mystream

To send over TCP, add -rtsp_transport tcp:

ffmpeg -re -stream_loop -1 -i file.ts -c copy -f rtsp -rtsp_transport tcp rtsp://localhost:8554/mystream

For RTMP, use the flv format:

ffmpeg -re -stream_loop -1 -i file.mp4 -c copy -f flv rtmp://localhost/mystream

To make a demo video:

ffmpeg -f lavfi -i testsrc=size=1280x720:rate=30 -c:v libx264 -preset fast -tune zerolatency -f rtsp rtsp://localhost:8554/demo

GStreamer

gst-launch-1.0 rtspclientsink name=s location=rtsp://localhost:8554/mystream filesrc location=file.mp4 ! qtdemux name=d d.video_0 ! queue ! s.sink_0 d.audio_0 ! queue ! s.sink_1

If the stream is video only

gst-launch-1.0 filesrc location=file.mp4 ! qtdemux name=d d.video_0 ! rtspclientsink name=s location=rtsp://localhost:8554/mystream

The transport protocol can be set with the protocols flag

gst-launch-1.0 filesrc location=file.mp4 ! qtdemux name=d d.video_0 ! rtspclientsink protocols=tcp name=s location=rtsp://localhost:8554/mystream

OBS Studio

You can stream with RTMP, or, with recent versions of OBS Studio, publish to the server using the WebRTC/WHIP protocol:

OpenCV

GStreamer support is required.

Check that python3 -c 'import cv2; print(cv2.getBuildInformation())' prints GStreamer: YES.

out = cv2.VideoWriter('appsrc ! videoconvert' + \
    ' ! x264enc speed-preset=ultrafast bitrate=600 key-int-max=' + str(fps * 2) + \
    ' ! video/x-h264,profile=baseline' + \
    ' ! rtspclientsink location=rtsp://localhost:8554/mystream',
    cv2.CAP_GSTREAMER, 0, fps, (width, height), True)
if not out.isOpened():
    raise Exception("can't open video writer")

Browser

Open http://localhost:8889/mystream/publish to publish with WebRTC.

If needed, embed the page as an iframe.

Publishing streams: by protocol

WebRTC

http://localhost:8889/mystream/publish

WebRTC WHIP

http://localhost:8889/mystream/whip

RTSP

rtsp://localhost:8554/mystream, the resulting stream is /mystream.

For cameras and servers:

paths:
  proxied1:
    source: rtsp://url1

  proxied2:
    source: rtsp://url1

The resulting streams are available at /proxied1 and /proxied2.

RTMP

rtmp://localhost/mystream, the resulting stream is /mystream.

For cameras and servers:

paths:
  proxied:
    # url of the source stream, in the format rtmp://user:pass@host:port/path
    source: rtmp://original-url

HLS cameras and servers

paths:
  proxied:
    # url of the playlist of the stream, in the format http://user:pass@host:port/path
    source: http://original-url/stream/index.m3u8

UDP/MPEG-TS

The server supports ingesting UDP/MPEG-TS packets (i.e. MPEG-TS packets sent with UDP). Packets can be unicast, broadcast or multicast. For instance, you can generate a multicast UDP/MPEG-TS stream with GStreamer:

gst-launch-1.0 -v mpegtsmux name=mux alignment=1 ! udpsink host=238.0.0.1 port=1234 \
videotestsrc ! video/x-raw,width=1280,height=720 ! x264enc speed-preset=ultrafast bitrate=3000 key-int-max=60 ! video/x-h264,profile=high ! mux. \
audiotestsrc ! audioconvert ! avenc_aac ! mux.

mediamtx.yml:

paths:
  udp:
    source: udp://238.0.0.1:1234

Playing streams

For example, you can open the stream with VLC.

vlc --network-caching=50 rtsp://localhost:8554/mystream

Ubuntu 21.10 bug

On Ubuntu 21.10, the bundled VLC does not support RTSP playback due to licensing issues. To fix this, remove the default VLC instance and install the snap version.

Or GStreamer:

gst-play-1.0 rtsp://localhost:8554/mystream

Or FFmpeg:

ffmpeg -i rtsp://localhost:8554/mystream -c copy output.mp4

Unlimited connection for unstable connections

Connect via RTMP with VLC, then turn on "infinite loop" (important).

vlc --no-one-instance rtmp://localhost/cam1

Authentication

Only clients that provide username and passwords will be able to perform a certain action:

ffmpeg -re -stream_loop -1 -i file.ts -c copy -f rtsp rtsp://myuser:mypass@localhost:8554/mystream

If storing plain credentials in the configuration file is a security problem, username and passwords can be stored as hashed strings. The Argon2 and SHA256 hashing algorithms are supported. To use Argon2, the string must be hashed using Argon2id (recommended) or Argon2i:

echo -n "mypass" | argon2 saltItWithSalt -id -l 32 -e

Then stored with the argon2: prefix:

authInternalUsers:
- user: argon2:$argon2id$v=19$m=4096,t=3,p=1$MTIzNDU2Nzg$OGGO0eCMN0ievb4YGSzvS/H+Vajx1pcbUmtLp2tRqRU
  pass: argon2:$argon2i$v=19$m=4096,t=3,p=1$MTIzNDU2Nzg$oct3kOiFywTdDdt19kT07hdvmsPTvt9zxAUho2DLqZw
  permissions:
  - action: publish

To use SHA256, the string must be hashed with SHA256 and encoded with base64:

echo -n "mypass" | openssl dgst -binary -sha256 | openssl base64

Then stored with the sha256: prefix:

authInternalUsers:
- user: sha256:j1tsRqDEw9xvq/D7/9tMx6Jh/jMhk3UfjwIB2f1zgMo=
  pass: sha256:BdSWkrdV+ZxFBLUQQY7+7uv9RmiSVA8nrPmjGjJtZQQ=
  permissions:
  - action: publish

Remuxing, re-encoding, compression (Save streams to disk)

To save available streams to disk, use FFmpeg inside the runOnReady parameter:

paths:
  all:
    runOnReady: >
      ffmpeg -i rtsp://localhost:$RTSP_PORT/$MTX_PATH
      -c copy
      -f segment -strftime 1 -segment_time 60 -segment_format mpegts saved_%Y-%m-%d_%H-%M-%S.ts
    runOnReadyRestart: yes

Record streams to disk

To save available streams to disk, set the record and the recordPath parameter in the configuration file:

pathDefaults:
  # Record streams to disk.
  record: yes
  # Path of recording segments.
  # Extension is added automatically.
  # Available variables are %path (path name), %Y %m %d %H %M %S %f %s (time in strftime format)
  recordPath: ./recordings/%path/%Y-%m-%d_%H-%M-%S-%f

To sync (upload) to a remote storage, install rclone:

pathDefaults:
  # this is needed to sync segments after a crash.
  # replace myconfig with the name of the rclone config.
  runOnInit: rclone sync -v ./recordings myconfig:/my-path/recordings

  # this is called when a segment has been finalized.
  # replace myconfig with the name of the rclone config.
  runOnRecordSegmentComplete: rclone sync -v --min-age=1ms ./recordings myconfig:/my-path/recordings

Setting the auto-delete period or Disable

By default, recordings are kept for only 1 day. To turn off auto-save:

pathDefaults:
  recordDeleteAfter: 0s

Note that in older versions, setting auto-delete to 1 day (==1d) does not work. Use seconds instead. (==86400s)

List of Record options

pathDefaults:
  ###############################################
  # Default path settings -> Record

  # Record streams to disk.
  record: no
  # Path of recording segments.
  # Extension is added automatically.
  # Available variables are %path (path name), %Y %m %d %H %M %S %f %s (time in strftime format)
  recordPath: ./recordings/%path/%Y-%m-%d_%H-%M-%S-%f
  # Format of recorded segments.
  # Available formats are "fmp4" (fragmented MP4) and "mpegts" (MPEG-TS).
  recordFormat: fmp4
  # fMP4 segments are concatenation of small MP4 files (parts), each with this duration.
  # MPEG-TS segments are concatenation of 188-bytes packets, flushed to disk with this period.
  # When a system failure occurs, the last part gets lost.
  # Therefore, the part duration is equal to the RPO (recovery point objective).
  recordPartDuration: 1s
  # Minimum duration of each segment.
  recordSegmentDuration: 1h
  # Delete segments after this timespan.
  # Set to 0s to disable automatic deletion.
  recordDeleteAfter: 1d

See also

Favorite site

author Zer0 sitemap