Sign inSign up

nirvana777/hls-converter

By nirvana777

β€’Updated 8 months ago

MPEG-TS Stream Proxy - Convert DASH (MPD) to MPEG-TS streams on-the-fly

Image
0

5.0K

nirvana777/hls-converter repository overview

⁠MPEG-TS Stream Proxy

Convert DASH (MPD) streams to MPEG-TS format on-the-fly for direct streaming.

Docker Build Docker Pulls

⁠Features

  • πŸŽ₯ Real-time DASH to MPEG-TS conversion
  • πŸš€ FFmpeg-based streaming (copy mode - no re-encoding)
  • 🧹 Automatic cleanup of dead/stale streams
  • πŸ“Š Health monitoring with stream statistics
  • πŸ”’ Resource limits (max streams, max age)
  • πŸ”„ Smart stream reuse (same URL shares FFmpeg process)
  • 🐳 Docker ready (Alpine-based, small footprint)

⁠Quick Start

⁠Using Docker
docker pull nirvana777/hls-converter:latest

docker run -d \
  --name mpegts-proxy \
  -p 8000:8000 \
  nirvana777/hls-converter:latest
⁠Using Docker Compose
version: '3.8'

services:
  mpegts-proxy:
    image: nirvana777/hls-converter:latest
    container_name: mpegts-proxy
    ports:
      - "8000:8000"
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8000/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 10s
    deploy:
      resources:
        limits:
          cpus: '2.0'
          memory: 2G
        reservations:
          cpus: '0.5'
          memory: 512M
⁠Local Development
# Clone repository
git clone https://github.com/YOUR_USERNAME/YOUR_REPO.git
cd YOUR_REPO

# Install dependencies
pip install -r requirements.txt

# Install FFmpeg (Ubuntu/Debian)
sudo apt-get install ffmpeg

# Run service
python mpegts-proxy.py

⁠API Usage

⁠Stream a DASH URL
# Direct streaming (outputs MPEG-TS)
curl "http://localhost:8000/stream?url=https://example.com/stream.mpd&name=MyStream" > output.ts

# Use in media player (VLC, ffplay, etc.)
vlc "http://localhost:8000/stream?url=https://example.com/stream.mpd"
ffplay "http://localhost:8000/stream?url=https://example.com/stream.mpd"
⁠Parameters
ParameterRequiredDescription
urlYesDASH manifest URL (.mpd)
nameNoStream name for metadata (default: "Stream")
⁠Health Check
curl http://localhost:8000/health

Response:

{
  "status": "healthy",
  "streams": 2,
  "max_streams": 10,
  "stream_details": [
    {
      "id": "a1b2c3d4",
      "age": 120,
      "clients": 2,
      "alive": true
    }
  ]
}

⁠Configuration

Configuration is done via code constants in mpegts-proxy.py:

manager = StreamManager(
    max_streams=10,      # Maximum concurrent streams
    max_stream_age=3600  # Maximum stream lifetime (1 hour)
)

Cleanup intervals (in StreamManager._cleanup_loop):

  • Cleanup frequency: 30 seconds
  • Idle timeout: 300 seconds (5 minutes)

⁠Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”                                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Client  │───── HTTP GET /stream?url=... ────▢│  aiohttp β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                                    β”‚   API    β”‚
     β”‚                                         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
     β”‚                                              β”‚
     β”‚  ◄─── MPEG-TS Stream ───                    β”‚
     β”‚                                              β–Ό
     β”‚                                         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
     └─────────────────────────────────────────│  FFmpeg  β”‚
                                               β”‚ Process  β”‚
                                               β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                                    β”‚
                                                    β–Ό
                                            β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                                            β”‚  DASH Source  β”‚
                                            β”‚  (.mpd URL)   β”‚
                                            β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Key Features:
β€’ Stream Reuse: Same URL = same FFmpeg process (multiple clients)
β€’ Auto Cleanup: Dead/idle/old streams removed every 30s
β€’ Process Monitoring: Health checks on FFmpeg processes
β€’ Resource Limits: Max 10 concurrent streams (configurable)

⁠How It Works

  1. Client requests stream β†’ /stream?url=<mpd_url>
  2. Proxy checks if stream already exists for this URL
  3. If exists: Reuses existing FFmpeg process (increments client count)
  4. If new: Creates FFmpeg process with MPEG-TS output to stdout
  5. Streams data β†’ Reads from FFmpeg stdout, writes to HTTP response
  6. Background cleanup β†’ Every 30s, removes:
    • Dead FFmpeg processes
    • Streams older than 1 hour
    • Streams idle for 5+ minutes (no clients)
  7. Client disconnect β†’ Decrements client count
  8. No clients β†’ Stream marked for cleanup after 5 min idle

⁠Performance Tips

  1. Memory: Each stream uses ~50-100MB. Monitor with /health
  2. Concurrent streams: Default 10, adjust max_streams for your hardware
  3. Stream reuse: Multiple clients can watch the same URL efficiently
  4. CPU: FFmpeg uses copy mode (no transcoding), minimal CPU usage
  5. Network: Outbound bandwidth = (stream bitrate Γ— active clients)

⁠Monitoring

⁠Check service health
curl http://localhost:8000/health | jq
⁠Watch FFmpeg logs
# Container logs show stream lifecycle
docker logs -f mpegts-proxy
⁠Resource usage
# Inside container
docker exec mpegts-proxy ps aux
docker stats mpegts-proxy

⁠Troubleshooting

⁠Stream won't start
  • Check URL: Verify DASH manifest is accessible
  • Check logs: docker logs mpegts-proxy for FFmpeg errors
  • Network: Ensure container can reach external URLs
⁠"Max streams reached" (503 error)
  • Increase max_streams in code
  • Check /health to see if streams are stuck
  • Wait for cleanup (runs every 30s)
⁠Stream stops unexpectedly
  • FFmpeg process may have crashed (check logs)
  • Source stream may have ended
  • Stream exceeded max age (1 hour default)
⁠High memory usage
  • Reduce max_streams
  • Check for stuck streams in /health
  • Restart container to clear all streams
⁠No cleanup happening
  • Background cleanup task runs every 30s automatically
  • Check logs for "Started stream cleanup task"
  • Verify app is running (not just FFmpeg processes)

⁠Differences from HLS Converter

This is not an HLS converter. Key differences:

FeatureMPEG-TS ProxyHLS Converter
Output formatMPEG-TS streamHLS playlist + segments
StorageNo disk storageRequires disk for segments
VolumesNone needed/tmp/hls_segments required
APISimple GET endpointPOST to convert, GET playlist
Use caseDirect streamingCDN distribution
LatencyReal-timeSegment duration delay

⁠Use Cases

βœ… Good for:

  • Direct streaming to media players (VLC, ffplay)
  • Low-latency proxy for DASH sources
  • Simple DASH β†’ MPEG-TS conversion
  • Temporary stream access (no recording)

❌ Not for:

  • HLS playlist generation
  • CDN distribution
  • DVR/recording functionality
  • Multiple quality levels

⁠GitHub Actions

Automated CI/CD pipeline:

  • βœ… Testing with FFmpeg validation
  • 🐳 Multi-platform builds (amd64, arm64)
  • πŸ”’ Security scanning with Trivy
  • πŸ“¦ Auto-push to Docker Hub
  • 🏷️ Semantic versioning
⁠Required Secrets

Set in GitHub repository settings:

  • DOCKERHUB_USERNAME: Docker Hub username
  • DOCKERHUB_TOKEN: Docker Hub access token

⁠Roadmap

Potential improvements:

  • Authentication/API keys
  • Rate limiting per IP
  • Configuration via environment variables
  • Prometheus metrics endpoint
  • Stream quality/bitrate selection
  • Input URL validation (prevent SSRF)
  • Configurable timeout on FFmpeg startup

⁠License

MIT

⁠Contributing

Pull requests welcome! Ensure:

  • Code follows existing async patterns
  • Add logging for new features
  • Test with actual DASH streams
  • Docker build succeeds

⁠Support

Tag summary

Content type

Image

Digest

sha256:162a41afd…

Size

277.7 MB

Last updated

8 months ago

docker pull nirvana777/hls-converter