Why RTSP-to-WebRTC?
RTSP (Real-Time Streaming Protocol) is the dominant video streaming protocol used by IP cameras worldwide. However, no modern browser supports RTSP natively — users must install plugins like VLC Web Plugin or Flash, both of which are deprecated or blocked.
WebRTC solves this by enabling direct browser-to-server real-time communication using standard APIs built into every modern browser. By converting RTSP to WebRTC with Portable RTC, you achieve:
- Zero Browser Plugins: View camera feeds directly in Chrome, Firefox, Edge, or Safari using built-in WebRTC support.
- Low Latency: Portable RTC's optimized C-based stack delivers sub-second glass-to-glass latency, far lower than HLS or DASH alternatives.
- Secure Transport: DTLS-SRTP encrypts both signaling and media streams, ensuring camera feeds are protected end-to-end.
- NAT Traversal: ICE/STUN protocols automatically handle NAT and firewall traversal, enabling remote camera access without complex port forwarding.
- Bandwidth Efficiency: On-demand proxy mode connects to the RTSP source only when a browser is actively viewing, conserving camera and network resources.
Architecture Overview
Before configuration, it is important to understand how the components fit together:
- RTSP IP Camera — Any ONVIF-compliant or generic IP camera producing an RTSP stream (e.g.,
rtsp://192.168.1.64:554/Streaming/Channels/101). - Portable RTC — The C-based media engine that ingests the RTSP stream, transcodes it if needed, and packages it into WebRTC-compatible RTP packets. It exposes streams via WebSocket to the signal server.
- Signal Server — A WebSocket-based server that handles signaling (SDP offer/answer exchange and ICE candidate negotiation) between Portable RTC and the browser. Portable RTC connects to the signal server as a client.
- Browser Client — A standard web page using the WebRTC API. The browser connects to the signal server via WebSocket, exchanges SDP with Portable RTC, and receives the media stream via peer-to-peer (or relayed) RTP.
The complete data flow: Camera (RTSP) → Portable RTC (ingest + transcode + WebRTC-pack) → Signal Server (signaling relay) → Browser (WebRTC playback).
Step 1: Deploy the Signal Server
The signal server (SignalServer.exe) is the communication hub that Portable RTC and browsers connect to. It runs an HTTP server for serving the WebRTC player page and handles WebSocket signaling (SDP offer/answer exchange and ICE candidate negotiation) between Portable RTC and the browser.
Place SignalServer.exe and its configuration file signalserver.cfg on a machine reachable by both Portable RTC and the browser client.
Signal Server Configuration
<http_enable>1</http_enable>
<http_server_ip></http_server_ip>
<http_port>8080</http_port>
<https_enable>1</https_enable>
<https_server_ip></https_server_ip>
<https_port>5443</https_port>
<cert_file>cert.pem</cert_file>
<key_file>key.pem</key_file>
<ipv6_enable>1</ipv6_enable>
<wan_ip></wan_ip>
<log_enable>1</log_enable>
<log_level>1</log_level>
<log_path></log_path>
<log_mode>loop</log_mode>
Start the signal server:
# On Windows
SignalServer.exe
# On Linux
./SignalServer
Verify the signal server is running by checking the console output. It should display the HTTP port (default 8080) and HTTPS port (default 5443) it is listening on. Note these addresses — you will need them for both the Portable RTC configuration and the browser URL.
For detailed deployment guidance including STUN/TURN ICE server configuration (via the <ice_servers> section in signalserver.cfg) and LAN/WAN/cloud placement, refer to the How to deploy signal server guide.
Step 2: Configure Portable RTC for RTSP Proxy
The core configuration file is portablertc.cfg. RTSP streams are converted to WebRTC through the <proxy> mechanism within an <application> block.
Configuration Structure
| Section | Purpose |
|---|---|
| Global | HTTP/HTTPS server, logging, signal server address, device name, authentication, data channel. |
| <application> | Defines a streaming application. Multiple applications can serve different stream groups. Each application is identified by its <name>. |
| <output> | Defines encoding parameters matched by stream URL. When the browser requests a stream whose URL matches a specific <url> pattern, the corresponding output settings are applied. |
| <proxy> | Defines a proxied RTSP/RTMP/SRT source. Specifies the original stream URL, credentials, transfer protocol, on-demand mode, and per-proxy output parameters. |
Global Configuration
<http_enable>1</http_enable>
<http_server_ip></http_server_ip>
<http_port>80</http_port>
<https_enable>0</https_enable>
<need_auth>0</need_auth>
<ipv6_enable>1</ipv6_enable>
<data_channel>0</data_channel>
<ws_signal_server>ws://192.168.3.36:8080</ws_signal_server>
<wss_signal_server>wss://192.168.3.36:5443</wss_signal_server>
<device_name>MyPortableRTC</device_name>
<log_enable>1</log_enable>
<log_level>1</log_level>
<log_path></log_path>
<log_mode>loop</log_mode>
<log_max_size></log_max_size>
<log_max_index></log_max_index>
Key global parameters:
| Parameter | Description |
|---|---|
<ws_signal_server> |
The WebSocket address of the signal server. Format: ws://signal-server-ip:port. Portable RTC registers itself with this server. |
<wss_signal_server> |
The secure WebSocket address. Format: wss://signal-server-ip:port. Used when HTTPS/WSS transport is required. |
<device_name> |
A human-readable name that Portable RTC sends to the signal server. The browser can use this name to identify and select the device. |
<data_channel> |
Enable (1) or disable (0) the WebRTC data channel. Enable this if you need bidirectional data messaging alongside the video stream. |
<need_auth> |
Enable (1) or disable (0) authentication. When enabled, the browser client must provide valid credentials from the <user> block to access streams. |
Step 3: Define the RTSP Proxy
The <proxy> block tells Portable RTC where to pull the source RTSP stream from, how to authenticate, and what encoding parameters to use for the WebRTC output.
Single RTSP Camera Configuration
<application>
<name>myapp</name>
<!-- Default output settings -->
<output>
<url></url>
<video>
<codec>H264</codec>
<width></width>
<height></height>
<framerate></framerate>
<bitrate></bitrate>
</video>
<audio>
<codec>G711A</codec>
<samplerate>8000</samplerate>
<channels>1</channels>
<bitrate></bitrate>
</audio>
</output>
<!-- RTSP proxy for front entrance camera -->
<proxy>
<suffix>front-entrance</suffix>
<url>rtsp://192.168.1.64:554/Streaming/Channels/101</url>
<user>admin</user>
<pass>mypassword</pass>
<transfer>TCP</transfer>
<ondemand>1</ondemand>
<output>
<video>
<codec>H264</codec>
<width></width>
<height></height>
<framerate></framerate>
<bitrate></bitrate>
</video>
<audio>
<codec>G711A</codec>
<samplerate>8000</samplerate>
<channels>1</channels>
<bitrate></bitrate>
</audio>
</output>
</proxy>
</application>
Proxy configuration parameters:
| Parameter | Description |
|---|---|
<suffix> |
The unique stream identifier. Combined with the application name to form the streamid URL parameter ([app]/[suffix]). Each proxy must have a unique suffix within the same application. |
<url> |
The full RTSP URL of the IP camera. Format: rtsp://[user:pass@]ip:port/path. Also supports rtmp://, srt://, and HTTP MJPEG URLs. |
<user> |
Username for the source RTSP stream. Leave empty if the camera does not require authentication. This authenticates Portable RTC to the camera, not the browser client. |
<pass> |
Password for the source RTSP stream. |
<transfer> |
RTSP client transport protocol. TCP is recommended for reliability and firewall traversal. Also supports UDP and MULTICAST. |
<ondemand> |
Bandwidth-saving feature. When 1, Portable RTC connects to the RTSP source only when a browser client is actively viewing the stream. When the browser disconnects, the RTSP connection is released. When 0, the RTSP connection is maintained continuously. |
Step 4: Configure for Multiple Cameras
To serve multiple cameras from a single Portable RTC instance, add additional <proxy> blocks with unique suffixes. The demo version supports up to 4 concurrent streams; the release version supports up to 100.
<application>
<name>myapp</name>
<!-- Proxy 1: Front Entrance -->
<proxy>
<suffix>front-entrance</suffix>
<url>rtsp://admin:pass1@192.168.1.64:554/Streaming/Channels/101</url>
<transfer>TCP</transfer>
<ondemand>1</ondemand>
</proxy>
<!-- Proxy 2: Parking Lot -->
<proxy>
<suffix>parking-lot</suffix>
<url>rtsp://admin:pass2@192.168.1.65:554/Streaming/Channels/101</url>
<transfer>TCP</transfer>
<ondemand>1</ondemand>
</proxy>
<!-- Proxy 3: Warehouse -->
<proxy>
<suffix>warehouse</suffix>
<url>rtsp://admin:pass3@192.168.1.66:554/Streaming/Channels/101</url>
<transfer>TCP</transfer>
<ondemand>1</ondemand>
</proxy>
</application>
Each proxy's <suffix> combined with the application <name> forms the streamid parameter used in the browser URL. With the configuration above, the three cameras are accessible at:
http://192.168.3.36:8080/webrtc?streamid=myapp/front-entrancehttp://192.168.3.36:8080/webrtc?streamid=myapp/parking-lothttp://192.168.3.36:8080/webrtc?streamid=myapp/warehouse
Step 5: Complete Configuration Example
Below is a complete portablertc.cfg with all global settings and a single RTSP proxy configured. This is a ready-to-use configuration — only change the signal server IP and the RTSP camera URL to match your environment.
<?xml version="1.0" encoding="utf-8"?>
<config>
<http_enable>1</http_enable>
<http_server_ip></http_server_ip>
<http_port>80</http_port>
<https_enable>0</https_enable>
<need_auth>0</need_auth>
<ipv6_enable>1</ipv6_enable>
<data_channel>0</data_channel>
<ws_signal_server>ws://192.168.3.36:8080</ws_signal_server>
<wss_signal_server>wss://192.168.3.36:5443</wss_signal_server>
<device_name>MyPortableRTC</device_name>
<loop_nums>-1</loop_nums>
<log_enable>1</log_enable>
<log_level>1</log_level>
<log_path></log_path>
<log_mode>loop</log_mode>
<log_max_size></log_max_size>
<log_max_index></log_max_index>
<user>
<username>admin</username>
<password>admin</password>
</user>
<application>
<name>myapp</name>
<proxy>
<suffix>front-entrance</suffix>
<url>rtsp://admin:mypassword@192.168.1.64:554/Streaming/Channels/101</url>
<transfer>TCP</transfer>
<ondemand>1</ondemand>
</proxy>
</application>
</config>
Step 6: Launch Portable RTC and Verify
- Save the
portablertc.cfgfile in the Portable RTC working directory. - Ensure the signal server (
SignalServer.exe) is running and reachable at the address specified in<ws_signal_server>. - Launch the Portable RTC executable. It will read the configuration, start its HTTP server, and connect to the signal server via WebSocket.
- Check the logs (with
<log_enable>1</log_enable>and<log_level>1</log_level>) for:- A successful connection to the signal server at the WebSocket address.
- Registration of the device with the signal server (using the
<device_name>). - Successful registration of each proxy suffix with the signal server.
Step 7: Access the Camera Stream in a Browser
Once both the signal server and Portable RTC are running, you can view the camera feed in any modern browser.
The signal server's built-in HTTP server (port 8080, or HTTPS port 5443) serves the WebRTC player page. Access a stream using the following URL format:
# HTTP (unencrypted)
http://SIGNAL_SERVER_IP:8080/webrtc?streamid=APP_NAME/STREAM_SUFFIX
# HTTPS (encrypted)
https://SIGNAL_SERVER_IP:5443/webrtc?streamid=APP_NAME/STREAM_SUFFIX
For the configuration in this guide, open the following URL in Chrome, Firefox, Edge, or Safari:
http://192.168.3.36:8080/webrtc?streamid=myapp/front-entrance
URL parameter breakdown:
| Component | Value | Source |
|---|---|---|
SIGNAL_SERVER_IP:8080 |
The host and port of the signal server | signalserver.cfg → <http_port> |
/webrtc |
Fixed path for the built-in WebRTC player page | Served by the signal server |
streamid= |
Query parameter identifying which stream to play | Format: [app]/[stream] |
myapp |
The application name | portablertc.cfg → <application><name> |
front-entrance |
The stream suffix | portablertc.cfg → <proxy><suffix> |
The browser will load the WebRTC player page, establish a WebSocket connection to the signal server for signaling, and negotiate a peer connection with Portable RTC. The live RTSP camera feed will appear in the browser window within a few seconds. No plugins are required.
Output Encoding Parameters
Each <proxy> can specify its own <output> parameters to control the video and audio encoding for the WebRTC stream. If not specified, the proxy inherits the application-level default output settings.
| Parameter | Values | Recommendation |
|---|---|---|
<video><codec> |
H264 or H265 |
H264 for maximum browser compatibility. H.265 browser support is limited. |
<video><width> |
Pixel width. Leave empty or 0 for original. |
Leave empty to preserve the camera's native resolution. |
<video><height> |
Pixel height. Leave empty or 0 for original. |
Leave empty for native. Reduce for bandwidth-constrained links. |
<video><framerate> |
Frames per second. Leave empty for original. | 15 for surveillance, 25 for live events. |
<video><bitrate> |
kb/s. 0 for auto-calculation. |
2048 for 1080p, 4096 for high-detail content. Set 0 to let the engine auto-calculate. |
<audio><codec> |
G711A, G711U, or OPUS |
OPUS for best quality and browser compatibility. Use G711A if the camera's original audio must be preserved without transcoding. |
<audio><samplerate> |
Hz. 8000, 16000, 44100, 48000 |
48000 for OPUS; 8000 for G711. |
<audio><channels> |
1 (mono) or 2 (stereo) |
1 for surveillance; 2 for music or multimedia. |
Troubleshooting
| Issue | Possible Cause | Resolution |
|---|---|---|
| Portable RTC fails to connect to signal server | SignalServer.exe not running, wrong IP/port in <ws_signal_server>, or firewall blocking port 8080. |
Verify SignalServer.exe is running. Check the WebSocket address in <ws_signal_server> matches the signal server's IP and HTTP port. Ensure port 8080 is reachable from the Portable RTC host. |
Browser shows blank page at /webrtc URL |
The signal server's HTTP server (port 8080) is not running or not reachable from the browser. | Verify SignalServer.exe is running and its <http_enable> is 1. Test by opening http://signal-server-ip:8080 in the browser — the root page should respond. Ensure the browser can reach the signal server IP. |
| No video appears in browser | RTSP URL is incorrect, camera requires authentication but credentials are missing, or the RTSP stream is not accessible. | Test the RTSP URL with VLC Media Player first: Media > Open Network Stream. If VLC cannot play it, neither can Portable RTC. Verify camera IP, port, and credentials in the <proxy> config. |
| Video is choppy or freezes | Network packet loss on the RTSP source, insufficient bandwidth, or aggressive encoding settings. | Use <transfer> set to TCP for the RTSP transport. Reduce <bitrate> and <framerate> in the proxy output settings. Check for network congestion on the camera link. |
| Browser shows "ICE connection failed" | The browser and Portable RTC cannot establish a direct peer connection due to NAT or firewall restrictions. | Ensure both the browser client and the signal server are on the same network for initial testing. For WAN access, configure STUN/TURN servers in the <ice_servers> section of signalserver.cfg. |
| Stream works on local network but not remotely | ICE cannot establish a connection because the browser is on a different network and the NAT type blocks direct connection. | Configure a TURN server in signalserver.cfg under <ice_servers> to relay media when direct peer connection fails. Format: turn:user:pass@ip:port. |
| Camera stream times out after a few minutes | The RTSP session has expired due to camera-side session timeout. | Most cameras allow the RTSP session timeout to be configured. Increase the timeout on the camera side, or use <ondemand> mode so the session is re-established on each browser request. |
| Portable RTC log shows "RTSP 401 Unauthorized" | RTSP credentials are incorrect or missing. | Check <user> and <pass> in the proxy configuration. Verify that the credentials work by testing the RTSP URL directly in VLC using the rtsp://user:pass@ip/path format. |
Best Practices
- Use TCP for RTSP Transport: Set
<transfer>toTCPfor all RTSP proxies. TCP guarantees reliable delivery of video frames and avoids the packet loss and out-of-order issues common with UDP-based RTSP over congested networks. - Enable On-Demand Mode: Set
<ondemand>to1for all proxies. This prevents Portable RTC from maintaining idle RTSP connections when no one is watching, saving camera resources and network bandwidth. - Use Descriptive Suffix Names: Choose meaningful
<suffix>values likefront-entranceorwarehouse-east. These appear in thestreamidURL parameter and are visible to users selecting streams. - Test RTSP URLs with VLC First: Before configuring a proxy in Portable RTC, validate the RTSP URL with VLC Media Player or ffplay. If a generic RTSP client cannot play the stream, Portable RTC will also fail.
- Start with Default Output Settings: Begin with empty output parameters to use the camera's native encoding. Only override
<width>,<height>, or<bitrate>when you need to reduce bandwidth or adjust for browser display constraints. - Deploy Signal Server Close to Portable RTC: For minimum signaling latency, run
SignalServer.exeon the same machine or same LAN as Portable RTC. The browser client connects to the signal server independently and does not need direct network access to Portable RTC. - Configure ICE Servers for WAN Access: If browsers need to access streams from outside the local network, add STUN/TURN server URLs in the
<ice_servers>section ofsignalserver.cfg. This enables NAT traversal for remote viewers. - Secure Production Deployments: In production, enable HTTPS on the signal server (
<https_enable>1</https_enable>), configure certificates, and usehttps://ip:5443/webrtc?streamid=app/streamfor browser access. Enable<need_auth>inportablertc.cfgand change default credentials.