Skip to content

Releases: hendrikvh/ESPHome-RTSP-Audio

v0.2.0

v0.2.0 Pre-release
Pre-release

Choose a tag to compare

@hendrikvh hendrikvh released this 29 May 19:44

Fixed

  • INMP441 I²S aliasing when bits_per_sample: 16bit is set
    (#7).

    Setting bits_per_sample: 16bit on the microphone: block causes
    the I²S peripheral to misalign the 24-bit INMP441 data word, halving
    the effective sample rate and aliasing all energy above 8 kHz back
    onto the lower half of the spectrum. The fix is to use
    bits_per_sample: 32bit — the 32-bit frame keeps the word correctly
    aligned; the 8 LSBs are padding zeros and the driver handles them
    correctly.

Added

  • DC blocker. A dedicated 1-pole high-pass at a fixed 5 Hz now
    sits upstream of every other DSP stage and runs by default on every
    packet — its only job is to kill the DC offset MEMS capsules
    (INMP441 and friends) ship with, before it eats headroom in any of
    the downstream stages. Separates concerns cleanly: the DC
    blocker is hygiene; the user-tunable low-cut is tone shaping. One
    MAC per sample, sub-audible cutoff. Note: the default install
    is no longer bit-identical to v0.1 — the DC blocker runs by
    default. The audible change is the removal of MEMS DC bias, which
    is desirable on every device the component targets.
  • Low-cut bypass at 0 Hz. The low-cut slider now ranges from
    0 Hz to 500 Hz, with anything below the existing active
    minimum (20 Hz) disabling the stage entirely — mirroring how
    16 kHz (Nyquist) already disables the high-cut. Dragging the
    slider to 0 is the natural "off" position. The always-on DC
    blocker keeps running in either case, so disabling the audible
    low-cut never re-introduces DC bias downstream.

Changed

  • Cut filters upgraded to 2nd-order Butterworth. Both the low-cut
    and high-cut stages now use a 2nd-order Butterworth (biquad) shape
    instead of the previous 1-pole IIR, doubling the rolloff from
    −6 dB/oct to −12 dB/oct. At the default 100 Hz low-cut, 50 Hz
    rumble is now ~12 dB rejected (vs ~6 dB), so HVAC and handling noise
    drop noticeably more without moving the cutoff up into the voice
    band. The Butterworth's defining feature — a maximally-flat
    passband
    — also means voice timbre is preserved cleanly instead
    of being gently tilted by the 1-pole's roll-in starting an octave
    above the cutoff. The −3 dB cutoff frequency convention is unchanged,
    so any persisted Home Assistant slider value keeps the same audible
    meaning; the slope at the cutoff is the only thing that changes.
    Implementation uses single-precision float in a direct-form-I
    biquad — the same audio cost lands on every supported MCU
    (ESP32-S3 uses its FPU, S2 and C3 use software float) rather than
    depending on FPU-only fixed-point math that would have to choose one
    target. User-facing config (cutoff ranges, defaults,
    bypass-at-Nyquist behaviour) is unchanged. See
    docs/images/filter-response.png
    for a side-by-side magnitude plot, regenerable via
    scripts/plot_filter_response.py.

v0.1.0

v0.1.0 Pre-release
Pre-release

Choose a tag to compare

@hendrikvh hendrikvh released this 25 May 20:54

Changed

  • Audio sample rate raised from 16 kHz to 32 kHz. The mic source, RTP
    payload (L16/32000/1), filter coefficients, and high-cut bounds
    (now capped at the new 16 kHz Nyquist) all move together. Bit depth
    stays at 16-bit mono. The MEMS capsules this targets (e.g. INMP441)
    can't faithfully capture much above 16 kHz, so 32 kHz is the balance
    point — voice plus the ambient detail (footsteps, doors, bird calls)
    in the 8–16 kHz octave, without spending CPU and Wi-Fi bandwidth on
    content the mic can't reproduce. On an ESP32-S3 the audio path still
    sits well under the comfort budget reported by cpu_use_pct. RTP
    packets roughly double in size (~1.3 KB at 20 ms), still well under
    MTU. See the README's "Audio format" section.
  • Ring buffer reduced from 2 s to 1 s of jitter slack so the
    component fits on bare ESP32 boards without PSRAM. At 32 kHz the
    2 s buffer was 128 KB, which routinely fails to allocate as a
    contiguous chunk on WROOM / RISC-V parts that only have ~320 KB
    internal SRAM total. 1 s (~64 KB) keeps the same byte budget as the
    previous 16 kHz / 2 s buffer; the RTSP client's own jitter buffer
    brings end-to-end resilience back to ~1.5–2 s. See the README's
    "Sized to fit ESP32 internal SRAM" section for the rationale and a
    note on possible PSRAM-aware sizing in future.

Added

  • High-cut filter, tunable from Home Assistant. Opt-in number: on
    platform: rtsp_audio exposing a high-cut frequency slider
    (high_cut_frequency_hz, default 20000 Hz / off, 1000–20000 Hz
    range) that rolls off energy above the cutoff — useful for taming
    mic hiss, wind noise, and out-of-band content downstream consumers
    don't need (narrow-band voice models, NVR storage). At the maximum
    the stage is skipped and the audio path is bit-identical to a build
    without it. See
    docs/configuration.md.
  • Low-cut filter, tunable from Home Assistant. Opt-in number: on
    platform: rtsp_audio exposing a low-cut frequency slider
    (low_cut_frequency_hz, default 100 Hz, 20–500 Hz range) that
    strips MEMS-mic DC bias and sub-100 Hz rumble (HVAC, handling, wind)
    before the audio leaves the device, so downstream consumers get a
    cleaner signal with more usable dynamic range. See
    docs/configuration.md.
  • RTSP session inactivity timeout to better handle silent client disconnects.
    A client that disappears without sending TEARDOWN (ue to crash,
    sleep, Wi-Fi drop) is now reaped after 60 s instead of blocking the
    single client slot for minutes until the OS-level TCP timeout fires.
  • Peak-level diagnostic sensor (peak_level_dbfs). Opt-in sensor:
    on platform: rtsp_audio reporting peak audio level in dBFS
    (0 dBFS = clipping) over the last 5 s window. Designed as the
    missing feedback channel for the gain_db slider — previously the
    user had to guess the right gain by listening to the receiving
    client. See
    docs/configuration.md.
  • CPU-use diagnostic sensor (cpu_use_pct). Opt-in sensor: on
    platform: rtsp_audio reporting what fraction of wall-clock the
    RTSP audio path consumes, so you can tell whether the chip has
    headroom for the current sample rate, DSP settings, and enabled
    features — and decide whether to upgrade or back off. See
    docs/configuration.md.
  • Audio gain in dB, tunable from Home Assistant. Opt-in number:
    on platform: rtsp_audio exposing a software audio gain slider
    (gain_db, -20 to +40 dB, 1 dB step, default 0 dB, persisted) so a
    quiet mic can be lifted without re-flashing. 0 dB is bit-identical
    to a no-gain build; overflow is saturating-clamped, never wrapped.
    See docs/configuration.md.
  • Diagnostic sensors for Home Assistant. Opt-in client_connected
    (binary_sensor), client_ip (text_sensor), and bytes_sent (sensor)
    so an HA dashboard can see in real time whether a client is pulling
    audio, who it is, and at what bitrate without watching serial logs.
    See
    docs/configuration.md.

Fixed

  • Use-after-free on session teardown
    (#5).

    deallocate_stream_buffers_() could free the ring buffer while a
    trailing I²S DMA callback on the mic's own FreeRTOS task was still
    writing into it, occasionally crashing the node on TEARDOWN /
    Wi-Fi drop / peer close. Deallocation is now deferred until
    mic_source_->is_stopped() confirms the mic task has actually
    exited, so the writer is guaranteed gone before the buffer goes
    away.

v0.0.1

v0.0.1 Pre-release
Pre-release

Choose a tag to compare

@hendrikvh hendrikvh released this 23 May 12:57

First semi-stable release. Working nicely with BirdNET-Go so my wife is happy!

Added

  • Single-viewer RTSP/1.0 server on ESP32 + ESP-IDF, exposing one
    microphone as a stream.
  • TCP and UDP RTP transports, both supported. Transport is
    negotiated per client at SETUP: UDP (RTP/AVP) works with VLC
    and most players; TCP-interleaved (RTP/AVP/TCP) works with
    FFmpeg, BirdNET-Go, Frigate, and most NVRs. No YAML toggle.
  • Tests covering ESP32-S2 + ESP-IDF and ESP32-S3 + ESP-IDF,
    run on every PR (S3 only) and on push to main (both) via GitHub
    Actions.
  • Local Dockerised development workflow: make config,
    make compile, make lint. Only Docker required on the host.

Fixed

  • Crash on no-PSRAM boards. tx_buffer_ is now reserved up front so
    the RTP send path no longer triggers OOM at runtime.