Skip to main content

Overview

Joystick uses MediaMTX as its streaming server, providing support for multiple protocols including RTSP, RTMP, HLS, WebRTC, and SRT. MediaMTX runs as a core service in the platform’s Docker stack.

MediaMTX service

MediaMTX is deployed using the official Docker image:
docker-compose.yml
mediamtx:
  image: bluenviron/mediamtx:1.11.2
  network_mode: host
  restart: unless-stopped
  volumes:
    - ./mediamtx/mediamtx.yml:/mediamtx.yml
MediaMTX runs in host network mode to avoid port mapping complexity for UDP-based protocols like RTP and WebRTC.

Configuration

MediaMTX is configured via mediamtx.yml. Here are the key settings:

Control API

The API enables programmatic stream management:
mediamtx/mediamtx.yml
api: yes
apiAddress: :9997
apiAllowOrigin: "*"
api
boolean
default:"yes"
Enables the REST API for stream control
apiAddress
string
default:":9997"
API server listen address
apiAllowOrigin
string
default:"*"
CORS header for cross-origin requests

Streaming protocols

MediaMTX supports multiple protocols simultaneously:
Real-Time Streaming Protocol for device sources:
rtsp: yes
rtspAddress: :8554
rtspTransports: [udp, multicast, tcp]
rtspEncryption: "no"
rtpAddress: :8000
rtcpAddress: :8001
rtspTransports
array
Supported transports: udp, multicast, tcp

Stream paths

Streams are organized by path, which acts as a unique identifier:
rtsp://server:8554/device_id
http://server:8888/device_id/index.m3u8
http://server:8889/device_id/whep

Path configuration

Default settings apply to all paths unless overridden:
mediamtx/mediamtx.yml
pathDefaults:
  source: publisher
  sourceOnDemand: no
  maxReaders: 0
  overridePublisher: yes
  record: no
source
string
default:"publisher"
Stream source. Can be:
  • publisher - Client publishes to this path
  • rtsp://url - Pull from RTSP camera
  • rtmp://url - Pull from RTMP source
  • http://url/stream.m3u8 - Pull from HLS source
  • redirect - Redirect to another path
sourceOnDemand
boolean
default:"no"
Only pull source when readers connect (saves bandwidth)
maxReaders
number
default:"0"
Maximum concurrent readers (0 = unlimited)
overridePublisher
boolean
default:"yes"
Allow new publisher to disconnect existing publisher

Custom path example

paths:
  camera_01:
    source: rtsp://192.168.1.100:554/stream
    sourceOnDemand: yes
    sourceOnDemandCloseAfter: 10s

Authentication

MediaMTX supports multiple authentication methods:

Internal authentication

Defined in the configuration file:
mediamtx/mediamtx.yml
authMethod: internal
authInternalUsers:
  - user: any
    pass:
    permissions:
      - action: api
      - action: publish
      - action: read
      - action: playback

HTTP authentication

External authentication via HTTP callback:
authMethod: http
authHTTPAddress: http://pocketbase:8090/api/mediamtx/auth
authHTTPExclude:
  - action: api
  - action: metrics
The auth endpoint receives:
{
  "user": "username",
  "password": "password",
  "ip": "client_ip",
  "action": "publish|read|playback|api",
  "path": "device_id",
  "protocol": "rtsp|rtmp|hls|webrtc|srt"
}

JWT authentication

Token-based authentication:
authMethod: jwt
authJWTJWKS: https://auth-server/.well-known/jwks.json
authJWTClaimKey: mediamtx_permissions
JWT must include permissions:
{
  "mediamtx_permissions": [
    {
      "action": "publish",
      "path": "device_id"
    },
    {
      "action": "read",
      "path": "device_id"
    }
  ]
}

Recording

MediaMTX can record streams to disk:
record: yes
recordPath: ./recordings/%path/%Y-%m-%d_%H-%M-%S-%f
recordFormat: fmp4
recordPartDuration: 1s
recordSegmentDuration: 1h
recordDeleteAfter: 1d
recordFormat: fmp4  # Fragmented MP4 (recommended)
recordFormat: mpegts  # MPEG-TS
Set recordDeleteAfter to prevent disk exhaustion. Recordings older than this duration are automatically deleted.

API integration

Joystick services interact with MediaMTX via its REST API:

Environment configuration

docker-compose.yml
joystick:
  environment:
    - STREAM_API_URL=http://host.docker.internal:9997

baker:
  environment:
    - STREAM_API_URL=http://host.docker.internal:9997

API endpoints

curl http://localhost:9997/v3/paths/list
Response:
{
  "items": [
    {
      "name": "device_01",
      "source": "publisher",
      "ready": true,
      "readers": 2
    }
  ]
}

Action integration

Actions can configure streams using the $mediamtx parameter:
{
  "action": "start-stream",
  "command": "curl -X POST $mediamtx/v3/config/paths/add/$device -d '{\"source\": \"rtsp://$host:554/stream\"}'",
  "target": "local"
}
See Actions for more details.

Device streaming

Devices typically publish streams to MediaMTX via RTSP:

Publishing from device

# Using ffmpeg
ffmpeg -i /dev/video0 \
  -f rtsp \
  -rtsp_transport tcp \
  rtsp://mediamtx:8554/device_id

# Using GStreamer
gst-launch-1.0 v4l2src device=/dev/video0 ! \
  x264enc ! rtspclientsink location=rtsp://mediamtx:8554/device_id

Playback URLs

Once published, streams are accessible via multiple protocols:
rtsp://server:8554/device_id

Stream quality presets

Device models can define quality presets:
packages/core/src/types/index.ts
export type ModelStreamQuality = {
  [key: string]: {
    excellent: number;
    good: number;
    fair: number;
    poor: number;
  };
};
Example configuration:
{
  "bitrate_presets": {
    "low": 500000,
    "medium": 1500000,
    "high": 3000000,
    "ultra": 6000000
  },
  "fps_presets": {
    "low": 15,
    "medium": 24,
    "high": 30,
    "ultra": 60
  },
  "quality_presets": {
    "low": { "bitrate": 500000, "fps": 15 },
    "medium": { "bitrate": 1500000, "fps": 24 },
    "high": { "bitrate": 3000000, "fps": 30 },
    "ultra": { "bitrate": 6000000, "fps": 60 }
  }
}

Troubleshooting

  1. Check MediaMTX logs:
    docker logs mediamtx
    
  2. Verify path is registered:
    curl http://localhost:9997/v3/paths/list
    
  3. Test publisher connection:
    ffmpeg -re -i test.mp4 -f rtsp rtsp://localhost:8554/test
    
  1. Ensure webrtcIPsFromInterfaces: yes is set
  2. Check firewall rules for UDP port 8189
  3. Verify STUN/TURN server configuration if behind NAT
  4. Test with browser console for WebRTC errors
  1. Use WebRTC instead of HLS for lowest latency
  2. Reduce HLS segment duration:
    hlsSegmentDuration: 1s
    hlsPartDuration: 200ms
    
  3. Use TCP transport for RTSP to avoid packet loss:
    rtsp://server:8554/stream?rtsp_transport=tcp
    
  1. Verify record is enabled for the path
  2. Check disk space availability
  3. Ensure recording directory has write permissions
  4. Check recordPath template syntax

MediaMTX Documentation

Official MediaMTX documentation

Devices

Configure device streaming sources

Actions

Control streams with actions

Build docs developers (and LLMs) love