Skip to content

Commit db236fd

Browse files
ossrs-aiwinlinvip
authored andcommitted
AI: Setup Claude Code cli.
1 parent 890b62f commit db236fd

File tree

5 files changed

+166
-5
lines changed

5 files changed

+166
-5
lines changed

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
.idea
22
srs-proxy
33
.env
4-
.go-formarted
4+
.go-formarted
5+
.claude
6+
.vscode

CLAUDE.md

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# proxy-go Project Guidelines
2+
3+
## Project Summary
4+
5+
proxy-go is a stateless media streaming proxy with built-in load balancing for building scalable origin clusters. It supports RTMP, SRT, WebRTC (WHIP/WHEP), HLS, and HTTP-FLV protocols.
6+
7+
Key characteristics:
8+
- Stateless design enables horizontal scaling
9+
- Built-in load balancer (memory or Redis-based)
10+
- Protocol handlers for RTMP, WebRTC, SRT, HTTP streaming
11+
- Backend origin servers register via System API
12+
- Official solution for SRS Origin Cluster
13+
14+
## Design Overview
15+
16+
See the "Design" section in @README.md for the complete architecture overview, including:
17+
- Stateless proxy architecture with built-in load balancing
18+
- Single Proxy Mode (memory-based)
19+
- Multi-Proxy Mode (Redis sync, AWS NLB)
20+
- Complete Cluster (Edge + Proxy + Origins)
21+
22+
## Configuration
23+
24+
All configuration via environment variables (`.env` file supported):
25+
26+
### Server Listen Ports (client-facing)
27+
- `PROXY_RTMP_SERVER=11935` - RTMP media server
28+
- `PROXY_HTTP_SERVER=18080` - HTTP streaming (HLS, HTTP-FLV)
29+
- `PROXY_WEBRTC_SERVER=18000` - WebRTC server (UDP)
30+
- `PROXY_SRT_SERVER=20080` - SRT server (UDP)
31+
- `PROXY_HTTP_API=11985` - HTTP API (WHIP/WHEP)
32+
- `PROXY_SYSTEM_API=12025` - System API (origin registration)
33+
34+
### Load Balancer Configuration
35+
- `PROXY_LOAD_BALANCER_TYPE=memory` - Use "memory" (single proxy) or "redis" (multi-proxy)
36+
- `PROXY_REDIS_HOST=127.0.0.1`
37+
- `PROXY_REDIS_PORT=6379`
38+
- `PROXY_REDIS_PASSWORD=` (empty for no password)
39+
- `PROXY_REDIS_DB=0`
40+
41+
### Other Settings
42+
- `PROXY_STATIC_FILES=../srs/trunk/research` - Static web files directory
43+
- `PROXY_FORCE_QUIT_TIMEOUT=30s` - Force shutdown timeout
44+
- `PROXY_GRACE_QUIT_TIMEOUT=20s` - Graceful shutdown timeout
45+
46+
## How to Run
47+
48+
When running the project for testing or development, you should:
49+
1. Build and start the proxy server
50+
2. Publish a test stream using FFmpeg
51+
3. Verify the stream is working using ffprobe
52+
53+
### Step 1: Build and Start Proxy Server
54+
55+
```bash
56+
make && env PROXY_RTMP_SERVER=1935 PROXY_HTTP_SERVER=8080 \
57+
PROXY_HTTP_API=1985 PROXY_WEBRTC_SERVER=8000 PROXY_SRT_SERVER=10080 \
58+
PROXY_SYSTEM_API=12025 PROXY_LOAD_BALANCER_TYPE=memory ./srs-proxy
59+
```
60+
61+
The proxy server should start and listen on the configured ports.
62+
63+
### Step 2: Publish a Test Stream
64+
65+
In a new terminal, publish a test stream using FFmpeg:
66+
67+
```bash
68+
ffmpeg -stream_loop -1 -re -i ~/git/srs/trunk/doc/source.flv -c copy -f flv rtmp://localhost/live/livestream
69+
```
70+
71+
> Note: `-stream_loop -1` makes FFmpeg loop the input file infinitely, ensuring the stream doesn't quit after the file ends.
72+
73+
### Step 3: Verify Stream with ffprobe
74+
75+
In another terminal, use ffprobe to verify the stream is working:
76+
77+
**Test RTMP stream:**
78+
```bash
79+
ffprobe rtmp://localhost/live/livestream
80+
```
81+
82+
**Test HTTP-FLV stream:**
83+
```bash
84+
ffprobe http://localhost:8080/live/livestream.flv
85+
```
86+
87+
Both commands should successfully detect the stream and display video/audio codec information. If ffprobe shows stream details without errors, the proxy is working correctly.

README.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,63 @@ can discover more details from SRS. And this proxy is the official solution to b
1313
an origin cluster for SRS. Please see [SRS Origin Cluster](https://ossrs.io/lts/en-us/docs/v7/doc/origin-cluster)
1414
for details.
1515

16+
## Design
17+
18+
**proxy-go** is a stateless media streaming proxy with built-in load balancing that enables building scalable origin clusters. The proxy itself acts as the load balancer, routing streams from clients to backend origin servers.
19+
20+
```
21+
Client → Proxy (with Load Balancer) → Backend Origin Servers
22+
```
23+
24+
Since the proxy is stateless, you can deploy multiple proxies behind a load balancer (like AWS NLB) for horizontal scaling:
25+
26+
```
27+
Client → AWS NLB → Proxy Servers → Backend Origin Servers
28+
```
29+
30+
**Single Proxy Mode**
31+
32+
Use case: Moderate amount of streams requiring multiple origin servers (each stream has few viewers). The total stream count is manageable by a single proxy server. Uses memory-based load balancing (no Redis needed).
33+
34+
```
35+
+--------------------+
36+
+-------+ Origin Server A +
37+
+ +--------------------+
38+
+
39+
+-----------------------+ + +--------------------+
40+
+ Proxy Server +------+-------+ Origin Server B +
41+
+ (Memory LB) + + +--------------------+
42+
+-----------------------+ +
43+
+ +--------------------+
44+
+-------+ Origin Server C +
45+
+--------------------+
46+
```
47+
48+
**Multi-Proxy Mode (Scalable)**
49+
50+
Use case: When a single proxy becomes a bottleneck. Supports a large number of streams across many origin servers, with limited viewers per stream. Redis is required for state synchronization between proxies.
51+
52+
```
53+
+-----------------------+
54+
+---+ Proxy Server A +------+
55+
+-----------------+ | +-----------+-----------+ +
56+
| AWS NLB +--+ | +
57+
+-----------------+ | (Redis Sync) + Origin Servers
58+
| +-----------+-----------+ +
59+
+---+ Proxy Server B +------+
60+
+-----------------------+
61+
```
62+
63+
**Complete Cluster (Edge + Proxy + Origins)**
64+
65+
Use case: Very large deployments with both numerous streams AND numerous viewers. Edge servers aggregate upstream connections - fetching one stream from upstream to serve multiple viewers, dramatically reducing load on proxy and origin servers.
66+
67+
```
68+
Edge Servers → Proxy Servers → Origin Servers
69+
(Proxy + Cache) (Proxy) (SRS/Media)
70+
```
71+
72+
> **Note**: Future edge servers will be implemented as proxy servers with caching enabled, creating a unified architecture where the same codebase serves both proxy and edge roles. The edge cache aggregates viewer connections, so thousands of viewers can watch the same stream while only requesting it once from upstream.
73+
1674
William Yang<br/>
1775
June 23, 2025

rtmp.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,8 @@ func (v *srsRTMPServer) Run(ctx context.Context) error {
8484
defer conn.Close()
8585

8686
handleErr := func(err error) {
87-
if isPeerClosedError(err) {
88-
logger.Df(ctx, "RTMP peer is closed")
87+
if isPeerClosedError(err) || isClosedNetworkError(err) {
88+
logger.Df(ctx, "RTMP connection closed")
8989
} else {
9090
logger.Wf(ctx, "RTMP serve err %+v", err)
9191
}
@@ -384,9 +384,19 @@ func (v *RTMPConnection) serve(ctx context.Context, conn *net.TCPConn) error {
384384

385385
// Reset the error if caused by another goroutine.
386386
if r0 != nil {
387+
// If backend connection closed normally, treat as normal disconnection
388+
if isClosedNetworkError(r0) || isPeerClosedError(r0) {
389+
logger.Df(ctx, "RTMP backend disconnected")
390+
return nil
391+
}
387392
return errors.Wrapf(r0, "proxy backend->client")
388393
}
389394
if r1 != nil {
395+
// If client connection closed normally, treat as normal disconnection
396+
if isClosedNetworkError(r1) || isPeerClosedError(r1) {
397+
logger.Df(ctx, "RTMP client disconnected")
398+
return nil
399+
}
390400
return errors.Wrapf(r1, "proxy client->backend")
391401
}
392402

utils.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,12 +148,16 @@ func isClosedNetworkError(err error) bool {
148148
return false
149149
}
150150

151+
// Unwrap to get the underlying error
152+
causeErr := errors.Cause(err)
153+
151154
// Check for "use of closed network connection" error
152-
if netErr, ok := err.(*net.OpError); ok {
155+
if netErr, ok := causeErr.(*net.OpError); ok {
153156
return netErr.Err.Error() == "use of closed network connection"
154157
}
155158

156-
return false
159+
// Also check if the error message contains the text
160+
return strings.Contains(causeErr.Error(), "use of closed network connection")
157161
}
158162

159163
// convertURLToStreamURL convert the URL in HTTP request to special URLs. The unifiedURL is the URL

0 commit comments

Comments
 (0)