What is WHIP?
WHIP (WebRTC HTTP Ingestion Protocol) is a standardized protocol for publishing WebRTC streams to a server using plain HTTP. It defines how a client sends an SDP offer to a server via HTTP POST, receives the SDP answer in the 201 Created response, and terminates the session via HTTP DELETE.
Unlike traditional WebRTC signaling (which requires a custom WebSocket signaling server), WHIP simplifies ingest to a single, standards-based HTTP exchange. This makes it ideal for:
- Cloud Contribution: Push streams to third-party WebRTC media servers, SFUs, or cloud live-streaming platforms without proprietary signaling.
- Interoperability: Any WHIP-compliant server can accept streams from any WHIP-compliant client, regardless of vendor.
- Firewall Simplicity: WHIP uses standard HTTP(S) ports (80/443), avoiding the need to open custom WebSocket or media ports.
Portable RTC acts as a WHIP client, pushing its media sources (local files, RTSP/RTMP/SRT streams, capture devices) to a remote WHIP server over WebRTC.
How Portable RTC WHIP Push Works
Media Source --> Portable RTC --> PeerConnection --> HTTP POST SDP offer --> WHIP Server
| |
|<-------------- 201 Created (SDP answer) --<|
| |
|===== WebRTC SRTP / H264 / OPUS ============>|
Steps:
1. Load the media source (local file, RTSP proxy stream, capture device, etc.)
2. Create a WebRTC PeerConnection and generate an SDP offer
3. Send the offer to the WHIP destination via HTTP POST
4. Receive the 201 response from the WHIP server (containing the SDP answer)
5. WebRTC connection is established and media starts streaming
6. An HTTP DELETE is sent automatically when the session is closed
Prerequisites
- Portable RTC built with the WHIP feature enabled (preprocessor macro
RTC_WHIP_CLIENT). The WHIP client module is implemented inrtc/whip_client.cppand declared inrtc/whip_client.h. - A WHIP server reachable over the network, exposing a WHIP endpoint URL (e.g.,
http://server:1985/rtc/v1/whip/?app=live&stream=stream).
Step 1: Configure the WHIP Client
In portablertc.cfg, WHIP push is configured through the <whip_client> node inside an <application> block. Multiple <whip_client> nodes can be configured to push different streams or to push the same stream to multiple WHIP servers.
<application>
<name>myapp</name>
<whip_client>
<srcurl>test.mp4</srcurl>
<dsturl>http://192.168.3.100:1985/rtc/v1/whip/?app=live&stream=stream</dsturl>
<video>
<codec>H264</codec>
<width></width>
<height></height>
<framerate></framerate>
<bitrate></bitrate>
</video>
<audio>
<codec>OPUS</codec>
<samplerate>8000</samplerate>
<channels>2</channels>
<bitrate></bitrate>
</audio>
</whip_client>
</application>
Configuration parameters:
| Parameter | Required | Description |
|---|---|---|
<srcurl> |
Yes | Media source to push: local file, capture device URL, or proxy stream (rtsp/rtmp/srt). |
<dsturl> |
Yes | WHIP destination URL, format http://host:port/path or https://host:port/path. |
video/codec |
No | Video codec: H264 or H265 (default H264). |
video/width |
No | Output video width. 0 or empty = use original width. |
video/height |
No | Output video height. 0 or empty = use original height. |
video/framerate |
No | Output frame rate. 0 or empty = use original frame rate. |
video/bitrate |
No | Video bitrate in kb/s. 0 or empty = auto-calculate. |
audio/codec |
No | Audio codec: G711A, G711U, or OPUS (default G711A). |
audio/samplerate |
No | Audio sample rate in Hz (default 8000). |
audio/channels |
No | Audio channels: 1 = mono, 2 = stereo (default 1). |
audio/bitrate |
No | Audio bitrate in kb/s. 0 or empty = auto-calculate. |
Step 2: Understand the srcurl Formats
The <srcurl> element supports multiple source types:
| Source Type | Format | Example |
|---|---|---|
| Local File | filename.ext |
test.mp4, demo.mp4 |
| Proxy Stream | rtsp://, rtmp://, or srt:// URL |
rtsp://192.168.1.100:554/live/stream1 |
| Screen Capture | screenlive |
screenlive (or screenliveN for a specific monitor) |
| Camera Capture | videodevice |
videodevice |
| Audio Capture | audiodevice |
audiodevice |
| Window Capture | window=[window-title] |
window=Notepad |
Important note on proxy streams: When <srcurl> references a proxy stream (RTSP/RTMP/SRT), a matching <proxy> configuration must exist in the same application. Alternatively, the <srcurl> can reference a proxy <suffix> value directly (as shown in Example 2 below).
Step 3: Push a Local File to a WHIP Server
The simplest use case — push a local media file as a live stream to a WHIP server. This is ideal for testing WHIP connectivity or for continuously looping a demo video.
<application>
<name>myapp</name>
<whip_client>
<srcurl>demo.mp4</srcurl>
<dsturl>http://192.168.3.100:1985/rtc/v1/whip/?app=live&stream=test</dsturl>
<video>
<codec>H264</codec>
</video>
<audio>
<codec>G711A</codec>
<samplerate>8000</samplerate>
<channels>1</channels>
</audio>
</whip_client>
</application>
File looping: When pushing a local file, the global <loop_nums> parameter controls the number of loop plays. Setting <loop_nums>-1</loop_nums> loops the file indefinitely.
Step 4: Push an RTSP Stream to a WHIP Server
To push an IP camera's RTSP stream to a WHIP server, first define a <proxy> for the camera, then reference its suffix in the <srcurl> of the <whip_client>.
<application>
<name>myapp</name>
<!-- RTSP proxy for the camera -->
<proxy>
<suffix>camera1</suffix>
<url>rtsp://192.168.1.100:554/live/stream1</url>
<transfer>TCP</transfer>
</proxy>
<!-- Push the camera to a WHIP server -->
<whip_client>
<srcurl>camera1</srcurl>
<dsturl>http://whip-server.com:8080/whip/stream1</dsturl>
<video>
<codec>H264</codec>
</video>
<audio>
<codec>OPUS</codec>
<samplerate>48000</samplerate>
<channels>2</channels>
</audio>
</whip_client>
</application>
Step 5: Push the Same Stream to Multiple WHIP Servers
A single media source can be pushed to multiple WHIP servers simultaneously by configuring multiple <whip_client> nodes referencing the same source. Each destination can use different codecs and quality settings.
<application>
<name>myapp</name>
<proxy>
<suffix>camera1</suffix>
<url>rtsp://192.168.1.100:554/live/stream1</url>
<transfer>TCP</transfer>
</proxy>
<!-- Push to WHIP server A (H.264 + OPUS) -->
<whip_client>
<srcurl>camera1</srcurl>
<dsturl>http://whip-server-a.com:8080/whip/stream1</dsturl>
<video>
<codec>H264</codec>
</video>
<audio>
<codec>OPUS</codec>
<samplerate>48000</samplerate>
<channels>2</channels>
</audio>
</whip_client>
<!-- Push to WHIP server B (H.265 + G711U) -->
<whip_client>
<srcurl>camera1</srcurl>
<dsturl>http://whip-server-b.com:8080/whip/stream1</dsturl>
<video>
<codec>H265</codec>
</video>
<audio>
<codec>G711U</codec>
<samplerate>8000</samplerate>
<channels>1</channels>
</audio>
</whip_client>
</application>
Step 6: Push Screen Capture to a WHIP Server
Push your desktop screen or an application window to a WHIP server for remote monitoring, training, or collaboration. Use screenlive for full-desktop capture, screenliveN for a specific monitor, or window=[window-title] for a single application window.
<application>
<name>myapp</name>
<!-- Push full screen (1920x1080 @ 30fps) to a secure WHIP server -->
<whip_client>
<srcurl>screenlive</srcurl>
<dsturl>https://whip-server.example.com:443/whip/screen</dsturl>
<video>
<codec>H264</codec>
<width>1920</width>
<height>1080</height>
<framerate>30</framerate>
<bitrate>4000</bitrate>
</video>
<audio>
<codec>OPUS</codec>
<samplerate>48000</samplerate>
<channels>2</channels>
</audio>
</whip_client>
<!-- Push an application window (e.g., Notepad) to a WHIP server -->
<whip_client>
<srcurl>window=Notepad</srcurl>
<dsturl>https://whip-server.example.com:443/whip/window</dsturl>
<video>
<codec>H264</codec>
<width></width>
<height></height>
<framerate>15</framerate>
<bitrate></bitrate>
</video>
</whip_client>
</application>
Notes:
- Use the
https://prefix in<dsturl>to enable TLS encryption between Portable RTC and the WHIP server. - Screen capture typically uses higher framerates (25-30 fps) for smooth motion, and higher bitrates (4000+ kb/s) to preserve UI detail.
- For window capture, the target window must be open and not minimized. The window title must match exactly (e.g.,
window=Notepad). - To capture a camera instead of the screen, use
videodevice. To capture audio only, useaudiodevice.
WHIP Protocol Interaction Details
HTTP POST (Sending the SDP Offer)
Portable RTC sends an HTTP POST request to the WHIP server, with the SDP offer in the request body:
POST /rtc/v1/whip/?app=live&stream=stream HTTP/1.1
Host: 192.168.3.100:1985
Accept: */*
Content-Type: application/sdp
User-Agent: Happytimesoft Portable RTC
Content-Length: <length>
<SDP Offer>
HTTP 201 (Receiving the SDP Answer)
The WHIP server responds with 201 Created, containing the SDP answer in the response body and a Location header identifying the session resource:
HTTP/1.1 201 Created
Content-Type: application/sdp
Location: http://192.168.3.100:1985/rtc/v1/whip/?app=live&stream=stream
Content-Length: <length>
<SDP Answer>
HTTP DELETE (Closing the Session)
When the WHIP session is closed, Portable RTC automatically sends an HTTP DELETE to terminate the session on the server:
DELETE /rtc/v1/whip/?app=live&stream=stream HTTP/1.1
Host: 192.168.3.100:1985
SDP Negotiation Notes
- ICE mode: ICE candidates are bundled in the SDP and sent in a single request; Trickle ICE is not used.
- Video codecs: H.264 (PT=96) or H.265.
- Audio codecs: G711A (PT=8), G711U (PT=0), or OPUS (PT=111).
- Transport: SRTP over DTLS.
Step 7: Launch and Verify via Logs
After saving portablertc.cfg, launch Portable RTC. Enable logging (<log_enable>1</log_enable>) and monitor the log output for WHIP-related messages:
[INFO] whip_start_all: starting WHIP: src=test.mp4 dst=http://192.168.3.100:1985/rtc/v1/whip/?app=live&stream=stream
[INFO] whip_init_sua: sid=XXXXXXXX-XXXXXXXX url=http://192.168.3.100:1985/rtc/v1/whip/?app=live&stream=stream
[INFO] whip_init_sua: streamid:myapp/test.mp4
[INFO] whip_client_start: WHIP connected, resource: http://192.168.3.100:1985/rtc/v1/whip/?app=live&stream=stream
[INFO] whip_client_close: WHIP session closed
A successful push is indicated by the WHIP connected log message. If the session ends, the WHIP session closed message appears.
Troubleshooting
| Issue | Possible Cause | Resolution |
|---|---|---|
| WHIP client not available at startup | The RTC_WHIP_CLIENT preprocessor macro was not defined at build time. |
Rebuild Portable RTC with CFLAGS += -DRTC_WHIP_CLIENT in the Makefile. Verify the build output includes rtc/whip_client.cpp. |
| HTTP POST times out | WHIP server unreachable, wrong URL, or firewall blocking the HTTP/HTTPS port. | The HTTP connection timeout is 5 seconds. Verify network connectivity to the WHIP server (ping / telnet host port). Confirm the <dsturl> is correct and the WHIP server is running. |
| WHIP server returns 4xx/5xx error | Invalid WHIP endpoint path, stream key already in use, or the server rejected the SDP offer. | Check the WHIP server's log for the specific error. Verify the endpoint path matches the server's WHIP configuration. Ensure the stream name in the URL is not already occupied by another publisher. |
| Media source fails to load | Local file missing, RTSP URL incorrect, or proxy not configured. | For local files, verify the file exists in the working directory. For proxy streams, confirm a matching <proxy> block exists and the source URL is reachable. |
| Video plays but has no audio | Audio codec not configured, or the source has no audio track. | Check the <audio> settings in the <whip_client>. If the source file or stream has no audio track, no audio will be pushed regardless of configuration. |
| SDP offer too large / negotiation fails | The SDP offer/answer exceeds the 8192-byte limit. | Reduce the number of codecs and ICE candidates in the SDP. Use a single video codec and a single audio codec to keep the SDP small. |
| Stream stops when the source ends | For local files, the file has reached its end and <loop_nums> is not set to loop. |
Set <loop_nums>-1</loop_nums> in the global configuration to loop the file indefinitely. |
Best Practices
- Use HTTPS for Production: Always use
https://in<dsturl>when pushing over the public internet. WHIP media is already encrypted via SRTP/DTLS, but HTTPS also protects the SDP exchange (which contains ICE credentials and codec information) from eavesdropping. - Match Codecs to the WHIP Server: Check which codecs your WHIP server supports before configuring. Most WHIP servers accept H.264 + OPUS, but H.265 and G.711 support varies. Mismatched codecs will cause the server to reject the SDP offer.
- Use TCP for RTSP Proxies: When the source is an RTSP stream, set
<transfer>TCP</transfer>in the proxy configuration for reliable delivery before the stream is pushed to the WHIP server. - Leverage Multi-Destination Push: If you need to distribute a single stream to multiple WHIP servers (e.g., primary + backup, or different codec profiles), use multiple
<whip_client>nodes referencing the same source. The source is ingested once and pushed to all destinations. - Monitor the WHIP Session State: Enable logging and watch for the
WHIP connectedandWHIP session closedmessages. Unexpected session closures are the first sign of network or server-side issues. - Set Explicit Output Parameters for Screen Capture: When pushing screen content, always set explicit
<width>,<height>, and<framerate>values. Screen capture at native resolution with an unset framerate can produce unexpectedly high bandwidth consumption.