Skip to content

Commit 6a12427

Browse files
feat: Enhance configuration options in README and environment file for video and audio settings
1 parent d0e94d0 commit 6a12427

File tree

3 files changed

+133
-67
lines changed

3 files changed

+133
-67
lines changed

sbc_streamer/README.md

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,28 @@ The streamer is configured through environment variables (values shown with defa
1414

1515
| Variable | Description |
1616
| --- | --- |
17-
| `FFMPEG_BINARY=ffmpeg` | Path to the `ffmpeg` executable (set to `/usr/local/bin/ffmpeg` when using a custom build) |
17+
| `FFMPEG_BINARY=ffmpeg` | Path to the `ffmpeg` executable |
1818
| `CAMERA_DEVICE=/dev/video0` | Video4Linux device supplying the camera feed |
1919
| `AUDIO_DEVICE` | Optional ALSA device (e.g. `hw:1,0`) to include audio |
2020
| `INPUT_FORMAT` | Force a V4L2 input format (e.g. `mjpeg`, `yuyv422`) when autodetect fails |
2121
| `FRAME_RATE=30` | Capture frame rate |
2222
| `VIDEO_SIZE=1280x720` | Capture resolution |
23-
| `VIDEO_BITRATE=1500k` | Target video bitrate |
23+
| `VIDEO_BITRATE=2M` | Target average video bitrate |
24+
| `VIDEO_MAXRATE=2M` | Peak video bitrate (CBR-style when set equal to `VIDEO_BITRATE`) |
25+
| `VIDEO_BUFSIZE=2M` | Encoder buffer size |
26+
| `VIDEO_CODEC=h264_rkmpp` | Video encoder (Rockchip hardware encoder by default) |
27+
| `VIDEO_FORMAT=nv12` | Pixel format fed into the encoder |
2428
| `STREAM_NAME=robot` | Path name on the relay |
25-
| `RELAY_HOST=rtsp.nene.02labs.me:8554` | Relay host (host:port) |
29+
| `RELAY_HOST=rtsp.02labs.me:8554` | Relay host (host:port) |
2630
| `RELAY_PUBLISH_USER` | Username for RTSP publish authentication |
2731
| `RELAY_PUBLISH_PASS` | Password for RTSP publish authentication |
2832
| `RTSP_TRANSPORT=tcp` | Transport to use when pushing (`tcp` or `udp`) |
33+
| `AUDIO_BITRATE=128k` | Audio bitrate when audio is enabled |
34+
| `AUDIO_SAMPLE_RATE=48000` | Audio sampling rate |
35+
| `AUDIO_CHANNELS=2` | Number of audio channels |
36+
| `GENERATE_SINE_AUDIO=true` | Generate a synthetic sine wave when no audio device is supplied |
37+
| `SINE_FREQUENCY=1000` | Frequency (Hz) of the synthetic sine tone |
38+
| `USE_TEST_PATTERN=false` | When `true`, push a `testsrc` pattern instead of the camera |
2939

3040
## Building
3141

@@ -78,5 +88,7 @@ The service will restart automatically if `ffmpeg` exits and will reconnect afte
7888

7989
## Hardware acceleration tips
8090

81-
- For Rockchip hardware encoding, install an `ffmpeg` build compiled with `--enable-rkmpp` or the V4L2 request API and set `FFMPEG_BINARY=/usr/local/bin/ffmpeg` (or whichever path hosts your accelerated binary).
82-
- Update the streamer source if you need to switch codecs (for example, replace `libx264` with `h264_rkmpp`). After editing, rebuild the binary with `go build`.
91+
- The default pipeline now matches the Rockchip GPU sweet spot: 1280x720 @30 fps, NV12 frames, and `h264_rkmpp` with 2 Mbps CBR.
92+
- Ensure the SBC is running an `ffmpeg` build compiled with `--enable-rkmpp` (the provided Radxa builds usually ship this; the custom binary you installed is assumed to replace `ffmpeg`).
93+
- Adjust the `VIDEO_*` variables if you need different bitrates or resolutions, but keep an eye on encoder limits.
94+
- Set `USE_TEST_PATTERN=true` to emit the synthetic `testsrc` pattern while commissioning the pipeline.

sbc_streamer/main.go

Lines changed: 99 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,34 @@ import (
77
"os"
88
"os/exec"
99
"os/signal"
10+
"strings"
1011
"syscall"
1112
"time"
1213
)
1314

1415
type streamerConfig struct {
15-
ffmpegBinary string
16-
cameraDevice string
17-
audioDevice string
18-
frameRate string
19-
resolution string
20-
bitrate string
21-
streamName string
22-
targetHost string
23-
publishUser string
24-
publishPass string
25-
rtspTransport string
26-
inputFormat string
16+
ffmpegBinary string
17+
cameraDevice string
18+
audioDevice string
19+
frameRate string
20+
resolution string
21+
videoBitrate string
22+
videoMaxRate string
23+
videoBufSize string
24+
videoCodec string
25+
videoFormat string
26+
streamName string
27+
targetHost string
28+
publishUser string
29+
publishPass string
30+
rtspTransport string
31+
inputFormat string
32+
audioBitrate string
33+
audioSampleRate string
34+
audioChannels string
35+
sineFrequency string
36+
generateSineAudio bool
37+
useTestPattern bool
2738
}
2839

2940
func main() {
@@ -60,19 +71,30 @@ func main() {
6071
}
6172

6273
func loadConfig() streamerConfig {
74+
baseBitrate := readEnv("VIDEO_BITRATE", "2M")
6375
return streamerConfig{
64-
ffmpegBinary: readEnv("FFMPEG_BINARY", "ffmpeg"),
65-
cameraDevice: readEnv("CAMERA_DEVICE", "/dev/video0"),
66-
audioDevice: os.Getenv("AUDIO_DEVICE"),
67-
frameRate: readEnv("FRAME_RATE", "30"),
68-
resolution: readEnv("VIDEO_SIZE", "1280x720"),
69-
bitrate: readEnv("VIDEO_BITRATE", "1500k"),
70-
streamName: readEnv("STREAM_NAME", "robot"),
71-
targetHost: readEnv("RELAY_HOST", "rtsp.nene.02labs.me:8554"),
72-
publishUser: readEnv("RELAY_PUBLISH_USER", ""),
73-
publishPass: readEnv("RELAY_PUBLISH_PASS", ""),
74-
rtspTransport: readEnv("RTSP_TRANSPORT", "tcp"),
75-
inputFormat: os.Getenv("INPUT_FORMAT"),
76+
ffmpegBinary: readEnv("FFMPEG_BINARY", "ffmpeg"),
77+
cameraDevice: readEnv("CAMERA_DEVICE", "/dev/video0"),
78+
audioDevice: os.Getenv("AUDIO_DEVICE"),
79+
frameRate: readEnv("FRAME_RATE", "30"),
80+
resolution: readEnv("VIDEO_SIZE", "1280x720"),
81+
videoBitrate: baseBitrate,
82+
videoMaxRate: readEnv("VIDEO_MAXRATE", baseBitrate),
83+
videoBufSize: readEnv("VIDEO_BUFSIZE", baseBitrate),
84+
videoCodec: readEnv("VIDEO_CODEC", "h264_rkmpp"),
85+
videoFormat: readEnv("VIDEO_FORMAT", "nv12"),
86+
streamName: readEnv("STREAM_NAME", "robot"),
87+
targetHost: readEnv("RELAY_HOST", "rtsp.02labs.me:8554"),
88+
publishUser: readEnv("RELAY_PUBLISH_USER", ""),
89+
publishPass: readEnv("RELAY_PUBLISH_PASS", ""),
90+
rtspTransport: readEnv("RTSP_TRANSPORT", "tcp"),
91+
inputFormat: os.Getenv("INPUT_FORMAT"),
92+
audioBitrate: readEnv("AUDIO_BITRATE", "128k"),
93+
audioSampleRate: readEnv("AUDIO_SAMPLE_RATE", "48000"),
94+
audioChannels: readEnv("AUDIO_CHANNELS", "2"),
95+
sineFrequency: readEnv("SINE_FREQUENCY", "1000"),
96+
generateSineAudio: readEnvBool("GENERATE_SINE_AUDIO", true),
97+
useTestPattern: readEnvBool("USE_TEST_PATTERN", false),
7698
}
7799
}
78100

@@ -92,50 +114,54 @@ func runFFmpeg(ctx context.Context, cfg streamerConfig, logger *log.Logger) erro
92114
}
93115

94116
func buildFFmpegArgs(cfg streamerConfig) []string {
95-
args := []string{
96-
"-f", "v4l2",
97-
}
98-
if cfg.inputFormat != "" {
99-
args = append(args, "-input_format", cfg.inputFormat)
117+
args := []string{"-re"}
118+
119+
if cfg.useTestPattern {
120+
args = append(args,
121+
"-f", "lavfi",
122+
"-i", fmt.Sprintf("testsrc=size=%s:rate=%s", cfg.resolution, cfg.frameRate),
123+
)
124+
} else {
125+
args = append(args, "-f", "v4l2")
126+
if cfg.inputFormat != "" {
127+
args = append(args, "-input_format", cfg.inputFormat)
128+
}
129+
args = append(args,
130+
"-thread_queue_size", "256",
131+
"-framerate", cfg.frameRate,
132+
"-video_size", cfg.resolution,
133+
"-i", cfg.cameraDevice,
134+
)
100135
}
101-
args = append(args,
102-
"-thread_queue_size", "256",
103-
"-framerate", cfg.frameRate,
104-
"-video_size", cfg.resolution,
105-
"-i", cfg.cameraDevice,
106-
)
107136

108137
if cfg.audioDevice != "" {
109-
audioArgs := []string{
138+
args = append(args,
110139
"-f", "alsa",
111140
"-thread_queue_size", "256",
112141
"-i", cfg.audioDevice,
113-
}
114-
args = append(args, audioArgs...)
142+
)
143+
} else if cfg.generateSineAudio {
144+
args = append(args,
145+
"-f", "lavfi",
146+
"-i", fmt.Sprintf("sine=frequency=%s:sample_rate=%s", cfg.sineFrequency, cfg.audioSampleRate),
147+
)
115148
}
116149

117-
videoOut := []string{
118-
"-vf", "format=yuv420p",
119-
"-c:v", "libx264",
120-
"-preset", "ultrafast",
121-
"-tune", "zerolatency",
122-
"-pix_fmt", "yuv420p",
123-
"-b:v", cfg.bitrate,
124-
"-maxrate", cfg.bitrate,
125-
"-bufsize", cfg.bitrate,
126-
"-g", "60",
127-
"-keyint_min", "30",
128-
}
129-
args = append(args, videoOut...)
150+
args = append(args,
151+
"-vf", fmt.Sprintf("format=%s", cfg.videoFormat),
152+
"-c:v", cfg.videoCodec,
153+
"-b:v", cfg.videoBitrate,
154+
"-maxrate", cfg.videoMaxRate,
155+
"-bufsize", cfg.videoBufSize,
156+
)
130157

131-
if cfg.audioDevice != "" {
132-
audioOut := []string{
158+
if cfg.audioDevice != "" || cfg.generateSineAudio {
159+
args = append(args,
133160
"-c:a", "aac",
134-
"-b:a", "128k",
135-
"-ar", "48000",
136-
"-ac", "2",
137-
}
138-
args = append(args, audioOut...)
161+
"-b:a", cfg.audioBitrate,
162+
"-ar", cfg.audioSampleRate,
163+
"-ac", cfg.audioChannels,
164+
)
139165
}
140166

141167
args = append(args,
@@ -167,3 +193,18 @@ func readEnv(key, fallback string) string {
167193
}
168194
return fallback
169195
}
196+
197+
func readEnvBool(key string, fallback bool) bool {
198+
v, ok := os.LookupEnv(key)
199+
if !ok || v == "" {
200+
return fallback
201+
}
202+
switch strings.ToLower(v) {
203+
case "1", "true", "t", "yes", "y":
204+
return true
205+
case "0", "false", "f", "no", "n":
206+
return false
207+
default:
208+
return fallback
209+
}
210+
}

sbc_streamer/streamer.example.env

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,35 @@
11
# Example environment file for the Radxa SBC streamer
22

33
# Path to the accelerated ffmpeg binary
4-
FFMPEG_BINARY=/usr/local/bin/ffmpeg
4+
# Only override when ffmpeg is not on PATH or uses a custom name
5+
#FFMPEG_BINARY=/usr/local/bin/ffmpeg
56

67
# Video source configuration
78
CAMERA_DEVICE=/dev/video0
89
FRAME_RATE=30
910
VIDEO_SIZE=1280x720
10-
VIDEO_BITRATE=1500k
11+
VIDEO_BITRATE=2M
12+
VIDEO_MAXRATE=2M
13+
VIDEO_BUFSIZE=2M
14+
#VIDEO_CODEC=h264_rkmpp
15+
#VIDEO_FORMAT=nv12
1116
# Uncomment if the camera requires a specific pixel format (e.g. mjpeg)
1217
#INPUT_FORMAT=mjpeg
1318

14-
# Optional audio source (ALSA device ID), leave empty to disable audio capture
19+
# Optional audio source (ALSA device ID); leave empty to synthesize a tone
1520
#AUDIO_DEVICE=hw:1,0
21+
#AUDIO_BITRATE=128k
22+
#AUDIO_SAMPLE_RATE=48000
23+
#AUDIO_CHANNELS=2
24+
#GENERATE_SINE_AUDIO=true
25+
#SINE_FREQUENCY=1000
1626

1727
# Relay connection details
18-
RELAY_HOST=rtsp.nene.02labs.me:8554
28+
RELAY_HOST=rtsp.02labs.me:8554
1929
RELAY_PUBLISH_USER=robot
2030
RELAY_PUBLISH_PASS=replace-with-strong-secret
2131
STREAM_NAME=robot
2232
RTSP_TRANSPORT=tcp
33+
34+
# Set to true to push a color bars test pattern instead of the camera
35+
#USE_TEST_PATTERN=false

0 commit comments

Comments
 (0)