Context up front: we’re not on LiveKit Cloud or rooms — we use the livekit Python package standalone for its AudioProcessingModule, doing server-side echo cancellation in a phone-bot pipeline (Twilio Media Streams, PSTN callers, 8 kHz).
Goal: cancel the caller’s speakerphone echo of our bot’s TTS server-side, before VAD/STT — PSTN callers have no client-side WebRTC AEC, and the leaked echo gets transcribed and triggers barge-in, so the bot interrupts itself.
What happens: APM works well when the echo path is short. But in our setup the echo returns 1–2 s (variable) after we feed the reference — outbound audio is buffered by Twilio and the carrier — and AEC3 then fails silently: ~0 dB reduction, no error. set_stream_delay_ms shifts the covered band but can’t span a 1–2 s variable delay.
Environment/versions: livekit 1.1.13 (pip), Python 3.12, Linux x86-64, 10 ms int16 frames at 8 kHz both streams. Usage skeleton:
apm = rtc.AudioProcessingModule(echo_cancellation=True, noise_suppression=False,
high_pass_filter=False, auto_gain_control=False)
apm.set_stream_delay_ms(100)
# on each outbound 10ms frame (as written to Twilio):
apm.process_reverse_stream(ref_frame)
# on each inbound 10ms frame (from Twilio):
apm.process_stream(in_frame)
Questions:
- Does anyone have tips for better usage of AEC3 for this use case?
- Is there an altogether different echo cancellation solution you’d suggest?
- Is there any way to give APM a large or dynamic delay hint? Is silent failure beyond ~500 ms the expected AEC3 behavior, and is there a recommended configuration for long, variable echo paths?
- How does LiveKit’s own SIP/PSTN bridge handle far-end device echo — server-side AEC3, Krisp, or is this handled at a different layer entirely?
- In your experience, is nonlinear residue from the far-end device’s own echo suppressor something APM can meaningfully cancel, or is the accepted answer “you can’t linearly cancel it — suppress or filter downstream instead”?