webrtp

package module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 18, 2026 License: Apache-2.0 Imports: 47 Imported by: 0

README

go-webrtp

Golang library for streaming video from RTSP or native USB camera sources directly to web in real-time.

Screenshot

WebRTP Dashboard

Usage

Download Binary

Download the latest release binary from GitHub Releases:

  • macOS (Apple Silicon)

    curl -L -o webrtp https://github.com/bronystylecrazy/go-webrtp/releases/latest/download/webrtp-darwin-arm64
    chmod +x webrtp
    
  • Linux (x64)

    curl -L -o webrtp https://github.com/bronystylecrazy/go-webrtp/releases/latest/download/webrtp-linux-amd64
    chmod +x webrtp
    
  • Windows (x64)

    Invoke-WebRequest -Uri "https://github.com/bronystylecrazy/go-webrtp/releases/latest/download/webrtp-windows-amd64.exe" -OutFile webrtp.exe
    
Run Server
./webrtp -i -c config.yml
Command Options
  -c, --config string    Config file path (default: config.yml)
  -i, --interface       Use graphical interface (default: false)
      --list-usb-devices List available USB video devices and exit
  -p, --port int        HTTP server port (default: 8080)
Configuration

Create a config.yml file:

telemetryServiceName: streamer1     # Optional: OTEL service name (default: "webrtp")
telemetryEndpoint: "localhost:4317" # Optional: OTEL gRPC endpoint for pushing metrics
upstreams:
  - name: camera1
    enabled: true
    rtspUrl: rtsp://192.168.1.100:554/stream
    h264Profile: baseline
  - name: usbCamera
    sourceType: usb
    enabled: true
    device: default
    codec: h264
    onDemand: true
    renditions:
      - name: low
        bitrateKbps: 450
      - name: mid
        bitrateKbps: 900
      - name: high
        bitrateKbps: 1600
  - name: demoLoop
    sourceType: file
    enabled: true
    path: ./test.mkv
    h264Profile: baseline
    onDemand: true

sourceType defaults to rtsp.

For RTSP sources:

  • rtspUrl is required
  • h264Profile is optional and supports baseline, main, or high
  • when h264Profile: baseline is set, go-webrtp transcodes the RTSP stream through ffmpeg/libx264 before publishing it
  • the RTSP baseline transcode path requires ffmpeg with libx264 available on PATH
  • width, height, frameRate, and bitrateKbps act as optional transcode output controls when the baseline path is used

For file sources:

  • path is required and can point to a single video file or a directory
  • enabled is optional and defaults to true; disabled streams stay in config and API results but are not playable
  • raw .h264 / .264 files are streamed directly without ffmpeg
  • for raw .h264 / .264, set frameRate to control playback pacing
  • when path is a directory, supported files are sorted by name and looped continuously
  • file sources are transcoded to H264 with ffmpeg, so ffmpeg and ffprobe must be available on PATH
  • h264Profile is optional and supports baseline, main, or high for the libx264 output profile
  • width, height, frameRate, and bitrateKbps are optional output controls for the mock stream
  • onDemand is optional; when true, playback starts on first viewer and stops a few seconds after the last viewer disconnects
  • keyframeSink is optional and currently supports fs
  • keyframeOutput is optional; when set, each IDR frame is decoded server-side and written to that directory
  • keyframeFormat is optional and supports jpg, png, or h264 (default: jpg)
  • keyframeFormat: h264 publishes raw H.264 keyframes directly and skips server-side image decode/render/encode
  • keyframeFormat: h264 does not support undistort or desk-view rectification
  • keyframe undistortion and desk crop are synced live from the frontend calibration endpoint, not from static config

For USB sources:

  • device accepts default, the macOS camera name, or the macOS device unique ID
  • enabled is optional and defaults to true
  • codec must be h264 or h265
  • h264Profile is optional and supports baseline, main, or high
  • frameRate is optional
  • bitrateKbps is optional
  • onDemand is optional; when true, renditions start on first viewer and stop a few seconds after the last viewer disconnects
  • renditions is optional for macOS and Windows USB streams; select one with ?quality=low, ?quality=mid, or ?quality=high
  • each USB rendition can override width, height, frameRate, and bitrateKbps
  • on macOS, the stream framerate is derived from actual camera timestamps; frameRate acts only as an optional capture/encoder hint
  • on macOS, bitrateKbps configures the native VideoToolbox encoder target bitrate for USB streams
  • on Windows, width, height, and frameRate select the closest native compressed Media Foundation mode exposed by the camera
  • on Linux, frameRate is still used when synthesizing timestamps for compressed v4l2 sources

Native USB support currently includes:

  • macOS: AVFoundation capture with native VideoToolbox H264/H265 encoding
  • Linux: v4l2 ingest for devices that already expose H264/H265 elementary streams
  • Windows: native Media Foundation ingest for devices that expose H264/H265 output natively

To list available USB cameras before configuring a stream:

go run ./command/webrtp --list-usb-devices

Windows notes:

  • device should be the device name or symbolic link returned by --list-usb-devices
  • codec should be h264 or h265
  • the current native Windows path does not transcode raw webcam formats in-process yet; the camera must expose compressed H264/H265 media types
Endpoint
Stream API
  • GET /api/streams list all streams
  • POST /api/streams create a stream
  • GET /api/streams/:name get one stream
  • PUT /api/streams/:name update and restart one stream
  • DELETE /api/streams/:name delete and stop one stream

Example request body:

{
  "name": "camera2",
  "sourceType": "rtsp",
  "enabled": true,
  "rtspUrl": "rtsp://192.168.1.101:554/stream",
  "h264Profile": "baseline"
}

USB example:

{
  "name": "usbCamera",
  "sourceType": "usb",
  "enabled": true,
  "device": "default",
  "codec": "h264",
  "onDemand": true,
  "renditions": [
    {"name": "low", "bitrateKbps": 450},
    {"name": "mid", "bitrateKbps": 900},
    {"name": "high", "bitrateKbps": 1600}
  ]
}

File example:

{
  "name": "demoLoop",
  "sourceType": "file",
  "enabled": true,
  "path": "./test.mkv",
  "h264Profile": "baseline",
  "onDemand": true
}

Examples:

  • http://localhost:8080/streams/usbCamera?quality=low
  • ws://localhost:8080/stream/usbCamera?quality=high
Telemetry

Metrics are available at /metrics endpoint in Prometheus format:

  • streamer_clients{name="camera1"} - Current number of clients
  • streamer_bitrate_kbps{name="camera1"} - Current bitrate in Kbps
  • streamer_framerate{name="camera1"} - Current framerate
Embedded Server

You can embed the WebRTP handler in your own Fiber application:

package main

import (
	"fmt"
	"log"

	"github.com/bronystylecrazy/go-webrtp"
	"github.com/gofiber/fiber/v3"
	"github.com/gofiber/fiber/v3/middleware/cors"
)

func main() {
	// Initialize WebRTP instance
	inst := webrtp.Init(&webrtp.Config{
		Rtsp:   "rtsp://192.168.1.100:554/stream",
		Logger: log.Default(),
	})

	// Connect to RTSP source
	if err := inst.Connect(); err != nil {
		log.Fatalf("connect: %v", err)
	}

	// Create Fiber app
	app := fiber.New()
	app.Use(cors.New())

	// Register WebRTP handler at /stream endpoint
	app.All("/stream", inst.Handler())

	// Start server
	log.Printf("Server started on :8080")
	log.Fatal(app.Listen(":8080"))
}

Libraries

JavaScript / TypeScript

See client/javascript for the JavaScript client library.

npm install @connectedtechco/webrtp
import { createClient } from '@connectedtechco/webrtp';

const client = createClient('ws://localhost:8080/stream/no/0');
client.render(document.getElementById('canvas'));

client.onInfo((info) => {
    console.log('Info:', info);
});

client.onFrame((frameNo, data, isKey) => {
    console.log(`Frame ${frameNo}: ${data.byteLength} bytes, keyframe: ${isKey}`);
});
Python

See client/python for the Python client library.

cd client/python
uv pip install -e .
from webrtp import WebRtpClient
import cv2

client = WebRtpClient("ws://localhost:8080/stream/no/0")

# Get raw frame data with callback
client.on_raw(lambda frame_no, data, is_key: print(f"Frame: {frame_no}, size: {len(data)}"))

# Get decoded frame with callback
client.on_frame(lambda frame_no, frame: cv2.imshow('video', frame))

client.start()

Development

  1. Generate self-signed certificate for TLS connection

    mkdir -p .local
    openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:P-256 -keyout .local/x509-key.pem -out .local/x509-cer.pem -days 365 -nodes -subj "/CN=localhost" -addext "subjectAltName=DNS:localhost,IP:127.0.0.1"
    
  2. Build styles (run in background)

    sass --watch command/webrtp/index.scss:command/webrtp/index.css
    
  3. Run the server

    go run ./command/webrtp/
    

Documentation

Overview

Package webrtp provides RTSP to WebSocket streaming with fMP4 output.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AnnexbToAvcc

func AnnexbToAvcc(au [][]byte) []byte

AnnexbToAvcc converts Annex-B NAL units to AVCC format.

func AnnexbToNalus

func AnnexbToNalus(data []byte) [][]byte

AnnexbToNalus splits an Annex-B byte stream into NAL units.

func BuildFragment

func BuildFragment(seqNr uint32, dts uint64, dur uint32, isIDR bool, avcc []byte) ([]byte, error)

BuildFragment creates an fMP4 media fragment.

func BuildInitH264

func BuildInitH264(sps, pps []byte) ([]byte, error)

BuildInitH264 creates an fMP4 init segment for H264 video.

func BuildInitH265

func BuildInitH265(vps, sps, pps []byte) ([]byte, error)

BuildInitH265 creates an fMP4 init segment for H265 video.

Types

type Config

type Config struct {
	SourceType        string
	StreamName        string
	Rtsp              string
	Device            string
	Path              string
	Codec             string
	H264Profile       string
	Width             int
	Height            int
	FrameRate         float64
	BitrateKbps       int
	KeyframeSink      string
	KeyframeOutput    string
	KeyframeFormat    string
	KeyframeMqttURL   string
	KeyframeMqttTopic string
	Keyframer         Keyframer
	Logger            Logger
	WriteTimeout      time.Duration
	ReadBufferSize    int
	WriteBufferSize   int
}

type Frame

type Frame struct {
	FrameNo uint64
	Data    []byte
	IsKey   bool
}

type Hub

type Hub struct {
	// contains filtered or unexported fields
}

func NewHub

func NewHub() *Hub

func (*Hub) Broadcast

func (r *Hub) Broadcast(data []byte, isKey bool)

func (*Hub) GetInit

func (r *Hub) GetInit() []byte

func (*Hub) GetStartupSnapshot

func (r *Hub) GetStartupSnapshot() ([]byte, []*Frame)

func (*Hub) GetStats

func (r *Hub) GetStats(name string) StreamStats

func (*Hub) GetStatus

func (r *Hub) GetStatus() Status

func (*Hub) IsReceivingFrames

func (r *Hub) IsReceivingFrames() bool

func (*Hub) Reset

func (r *Hub) Reset()

func (*Hub) SetFramerate

func (r *Hub) SetFramerate(framerate float64)

func (*Hub) SetInfo

func (r *Hub) SetInfo(codec string, width, height int, frameRate float64)

func (*Hub) SetInit

func (r *Hub) SetInit(data []byte)

func (*Hub) Subscribe

func (r *Hub) Subscribe() chan *Frame

func (*Hub) SubscribeWithStartupSnapshot

func (r *Hub) SubscribeWithStartupSnapshot() ([]byte, []*Frame, chan *Frame)

func (*Hub) Unsubscribe

func (r *Hub) Unsubscribe(ch chan *Frame)

type Instance

type Instance struct {
	// contains filtered or unexported fields
}

func Init

func Init(cfg *Config) *Instance

func (*Instance) Connect

func (r *Instance) Connect() error

func (*Instance) GetHub

func (r *Instance) GetHub() *Hub

func (*Instance) HandleWebsocket

func (r *Instance) HandleWebsocket(conn *websocket.Conn)

func (*Instance) Handler

func (r *Instance) Handler() fiber.Handler

func (*Instance) InstanceReady

func (r *Instance) InstanceReady() bool

func (*Instance) PublishDeskViewMetadata

func (r *Instance) PublishDeskViewMetadata(topic string, payload []byte) error

func (*Instance) RecordingStatus

func (r *Instance) RecordingStatus() RecordingStatus

func (*Instance) Start

func (r *Instance) Start(addr string) error

func (*Instance) StartRecording

func (r *Instance) StartRecording(path, mode, offlineMode string) error

func (*Instance) Stop

func (r *Instance) Stop() error

func (*Instance) StopRecording

func (r *Instance) StopRecording() error

func (*Instance) UpdateKeyframeCalibration

func (r *Instance) UpdateKeyframeCalibration(distort, deskEnabled bool, fx, fy, scale float64, desk string) error

type Keyframe

type Keyframe struct {
	StreamName  string
	FrameNo     uint32
	Codec       string
	Format      string
	Width       int
	Height      int
	Payload     []byte
	Distort     bool
	DeskEnabled bool
	Fx          float64
	Fy          float64
	Scale       float64
	PublishedAt time.Time
}

type Keyframer

type Keyframer interface {
	HandleKeyframe(frame *Keyframe) error
}

type Logger

type Logger interface {
	Print(v ...interface{})
	Printf(format string, v ...interface{})
}

type Recorder

type Recorder struct {
	// contains filtered or unexported fields
}

func NewRecorder

func NewRecorder(logger Logger) *Recorder

func (*Recorder) OnOffline

func (r *Recorder) OnOffline()

func (*Recorder) RecordSample

func (r *Recorder) RecordSample(avcc []byte, dur uint32, isIDR bool)

func (*Recorder) SetInit

func (r *Recorder) SetInit(initData []byte)

func (*Recorder) SetSourceInfo

func (r *Recorder) SetSourceInfo(codec string, width, height int, frameRate float64)

func (*Recorder) Start

func (r *Recorder) Start(path, mode, offlineMode string) error

func (*Recorder) Status

func (r *Recorder) Status() RecordingStatus

func (*Recorder) Stop

func (r *Recorder) Stop() error

type RecordingStatus

type RecordingStatus struct {
	Active           bool      `json:"active"`
	Path             string    `json:"path,omitempty"`
	OfflineMode      string    `json:"offlineMode,omitempty"`
	StartedAt        time.Time `json:"startedAt,omitempty"`
	BytesWritten     int64     `json:"bytesWritten"`
	RequestedStartAt time.Time `json:"requestedStartAt,omitempty"`
	ActualStartAt    time.Time `json:"actualStartAt,omitempty"`
	RequestedStopAt  time.Time `json:"requestedStopAt,omitempty"`
	ActualStopAt     time.Time `json:"actualStopAt,omitempty"`
	StartDriftMs     int64     `json:"startDriftMs"`
	StopDriftMs      int64     `json:"stopDriftMs"`
	MediaDurationMs  int64     `json:"mediaDurationMs"`
	TrimStartMs      int64     `json:"trimStartMs"`
	TrimEndMs        int64     `json:"trimEndMs"`
	MissingStartMs   int64     `json:"missingStartMs"`
	MissingEndMs     int64     `json:"missingEndMs"`
}

type Status

type Status struct {
	Streams []*StreamStats `json:"streams"`
}

type StreamStats

type StreamStats struct {
	Name        string        `json:"name"`
	Ready       bool          `json:"ready"`
	Codec       string        `json:"codec"`
	Width       int           `json:"width"`
	Height      int           `json:"height"`
	Framerate   float64       `json:"framerate"`
	FrameNo     uint64        `json:"frameNo"`
	ClientCount int32         `json:"clientCount"`
	BytesRecv   uint64        `json:"bytesRecv"`
	Bitrate     float64       `json:"bitrateKbps"`
	Uptime      time.Duration `json:"uptime"`
}

type UsbCapabilityMode

type UsbCapabilityMode struct {
	Width  int       `json:"width"`
	Height int       `json:"height"`
	Fps    []float64 `json:"fps,omitempty"`
}

type UsbCapabilityRendition

type UsbCapabilityRendition struct {
	Name      string   `json:"name"`
	Width     int      `json:"width"`
	Height    int      `json:"height"`
	FrameRate *float64 `json:"frameRate,omitempty"`
}

type UsbDevice

type UsbDevice struct {
	Id   string `json:"id"`
	Name string `json:"name"`
}

func UsbDeviceList

func UsbDeviceList() ([]*UsbDevice, error)

type UsbDeviceCapabilities

type UsbDeviceCapabilities struct {
	Device              *UsbDevice                `json:"device,omitempty"`
	Codecs              []string                  `json:"codecs,omitempty"`
	Modes               []*UsbCapabilityMode      `json:"modes,omitempty"`
	SuggestedRenditions []*UsbCapabilityRendition `json:"suggestedRenditions,omitempty"`
	BitrateControl      string                    `json:"bitrateControl,omitempty"`
}

func UsbDeviceCapabilitiesGet

func UsbDeviceCapabilitiesGet(device string) (*UsbDeviceCapabilities, error)

Directories

Path Synopsis
command
webrtp command
experiment
streamable command
webtransport command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL