You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(echo): add a robot mode that transforms the audio (#58)
echo dropped audio in three places, so trickle looked video-only: the
client decoded video=0, the runner returned None for non-video frames,
and only a video track was published. A stream with sound came back
silent with no explanation.
robot ring-modulates the audio and leaves the video alone, which makes
the round trip audible. Ring modulation rather than a pitch shift
because it preserves the sample count, so audio stays in sync without
resampling. The output track is pinned to 48 kHz since opus rejects
44.1, which every consumer source produces.
Only robot publishes an audio track: the container opens once every
declared track has a first frame, so declaring audio for a silent input
would stall the video.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: echo/README.md
+19-2Lines changed: 19 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,6 +1,6 @@
1
1
# Echo app (trickle realtime video)
2
2
3
-
A realtime video app on the Livepeer network: it receives a live video stream over **trickle** channels, optionally transforms each frame (gray / invert / blur), and echoes it back. This is the **live/stateful** path — continuous media over trickle, not request/response — so the app embeds the SDK and self-registers (dynamic).
3
+
A realtime video app on the Livepeer network: it receives a live video stream over **trickle** channels, optionally transforms each frame (gray / invert / blur) or the audio (robot), and echoes it back. This is the **live/stateful** path — continuous media over trickle, not request/response — so the app embeds the SDK and self-registers (dynamic).
@@ -73,10 +73,27 @@ Swap `/dev/video0` for your node. If that size/format isn't supported, list the
73
73
74
74
The `ffplay` low-delay flags (`-fflags nobuffer -flags low_delay -framedrop`) keep the preview close to realtime; drop them and it buffers.
75
75
76
-
-`--mode` picks the transform: `echo` (passthrough, the default), `gray`, `invert`, or `blur`. Use `--mode blur` on any command above to see the echo visibly transform the stream.
76
+
-`--mode` picks the transform: `echo` (passthrough, the default), `gray`, `invert`, `blur`, or `robot`. Use `--mode blur` on any command above to see the echo visibly transform the stream.
77
+
-`robot` ring-modulates the audio and leaves the video alone. It is the only mode that publishes an audio track, since a declared track that never gets a frame stalls the stream.
77
78
-`blur` sweeps the radius `0 -> max -> 0` live (driving `/update`); `--blur-period N` sets the seconds per sweep cycle (default 2; larger is slower). `gray`/`invert` are static.
**Hearing `robot`** — every command above is video-only, so `robot` would refuse them. Record yourself with a microphone (`arecord -l` lists capture devices), then play both files:
uv run client.py --mode robot --output me-robot.ts me.ts
90
+
ffplay -autoexit me.ts && ffplay -autoexit me-robot.ts # you, then you ring-modulated
91
+
```
92
+
93
+
To hear it live instead, keep the same capture and swap the tail for `--output - -` piped into `ffplay -fflags nobuffer -i -`. Expect 2 to 4 seconds of lag, since trickle publishes in 2s segments, and wear headphones or the mic re-records the playback.
94
+
95
+
The camera and the audio device are separate clocks, so `aresample` and `-fps_mode cfr` align them; without both the publisher fails at the first segment boundary. On a multi-input interface add `-channels 6` and pick one input with `pan=mono|c0=c0`.
0 commit comments