Skip to content

Add SDRTrunk direct Icecast stream support#45

Closed
Brantlab wants to merge 4 commits into
W3AXL:mainfrom
Brantlab:SDR-Trunk
Closed

Add SDRTrunk direct Icecast stream support#45
Brantlab wants to merge 4 commits into
W3AXL:mainfrom
Brantlab:SDR-Trunk

Conversation

@Brantlab
Copy link
Copy Markdown

Summary

This PR adds a new SDRTrunk daemon mode that lets RadioConsole2 receive live SDRTrunk audio through an Icecast-compatible source connection.

The daemon can now act as a lightweight Icecast-style ingest point for SDRTrunk, decode the incoming MP3 stream, forward the audio into RC2 over the normal WebRTC path, and update the console display from SDRTrunk metadata.

What Changed

  • Added new SDRTrunk control mode.
  • Added an Icecast-compatible source listener for SDRTrunk.
  • Added direct MP3 stream decoding for live SDRTrunk audio.
  • Added support for SDRTrunk metadata updates, including inline Icecast metadata and /admin/metadata updates.
  • Added RX-only WebRTC handling for stream-based radios.
  • Added receive-state gating based on audio level and metadata activity.
  • Added stale source recovery so the daemon can close and restart a connected SDRTrunk source if it stops producing decodable audio.
  • Added example SDRTrunk configuration.

Notes

This mode is intended for SDRTrunk’s streaming/broadcast output. SDRTrunk should be configured to publish to the daemon as an Icecast source, using the configured mount point such as /sdrtrunk.

The daemon does not require a separate Icecast server for this path; it provides the source listener directly and presents the stream to RC2 as a receive-only radio.

Testing

  • Verified daemon builds successfully.
  • Tested SDRTrunk source connections against the new Icecast-compatible listener.
  • Tested live audio forwarding into RC2.
  • Tested SDRTrunk metadata handling with inline metadata and admin metadata updates.

@W3AXL
Copy link
Copy Markdown
Owner

W3AXL commented May 24, 2026

Overall review notes:

  • This is a major PR, and as such my level of scrutiny is heightened, doubly so because it was done with LLM tools
  • I require comments on code regardless of whether a human or LLM wrote it - this helps the PR submitter understand the code better and helps me fix things later when something breaks
  • You appear to have also pulled in the VOX changes from Vox implementation #44 which muddies up the PR. Please separate these out cleanly

Brantlab added 4 commits May 23, 2026 23:41
Add an SDRTrunk control mode that accepts Icecast-compatible source audio
directly from sdrtrunk and exposes it as an RX-only RC2 radio.

Includes live MP3 stream decoding, inline/admin metadata handling, receive-state
gating, stale source restart handling, and example configuration for direct
sdrtrunk ingest.
- Stop forwarding idle SDRTrunk audio when the receive gate is closed
- Prevent stale talkgroup metadata from keeping RX latched
- Clear SDRTrunk display state after hang timeout
- Build daemon publish artifact successfully for Windows/Linux
- Identify workflow fixes for publish path and release packaging
@Brantlab
Copy link
Copy Markdown
Author

Rebased this branch onto the current main and removed the VOX implementation changes from the SDRTrunk PR. This branch should now contain only the SDRTrunk direct stream work.

I also added section-level comments around the new SDRTrunk code, including the Icecast-compatible stream listener, MP3 decode path, receive gate behavior, metadata handling, source watchdog/reconnect behavior, and launcher script.

Validation:

  • dotnet build daemon\daemon.sln

Note: I kept the rc2-core RX-only/WebRTC fix out of this PR so it stays separate from the SDRTrunk branch.

@DevRanger
Copy link
Copy Markdown

lightweight Icecast-style ingest point for SDRTrunk, decode the incoming MP3 stream, forward the audio into RC2 over the normal WebRTC path, and update the console display from SDRTrunk metadata

@Brantlab I’m not sure I agree with this approach. Using a webstream as the middle of the audio path feels pretty hacky for a console use case.

I think the cleaner design would be for the daemon to consume the SDRTrunk audio/metadata more directly and packetize it into whatever RC2 expects internally, rather than treating SDRTrunk like a broadcast stream first and then trying to push that back into the console. For reference, dvmtrstream is similar-ish however it targets TrunkRecorder and DVMBridge, but the general model is closer to what I’d expect: take source audio, chunk it with the relevant TG/source metadata, and send it forward directly. It avoids converting the audio path into a webstream just to ingest it again later.

My concern is that this proposed method may work, but it works by adding a lot of avoidable moving parts. I’d personally like to see the audio chain kept as direct as possible.

Also piggybacking on what @W3AXL said regarding the LLM-generated code: my concern is less about AI being used and more about what happens after this gets merged. Once this lands in mainline, the team effectively owns it and has to maintain it long-term. If this was something the lead developers wanted in the console, I assume it probably would have been designed and implemented intentionally already. I don’t think it’s fair for the long-term support burden of this feature to fall on the project maintainers, especially if this was not a direction they wanted the console to support in the first place.

Perhaps this could be its own daemon rather than be in mainline.

@Brantlab
Copy link
Copy Markdown
Author

I looked closer at SDRTrunk’s internals, and I think your concern is valid. SDRTrunk’s existing streaming path appears to revolve around completed audio segments being written as temporary MP3 recordings and queued for broadcast. That is not ideal for my goal, which is near-synchronous monitoring where overlapping calls should be heard as quickly as possible rather than queued behind each other.

A cleaner approach may be a separate SDRTrunk-to-RC2 bridge/exporter that hooks closer to SDRTrunk’s live AudioSegment / decoded PCM path and forwards audio plus TG/source metadata directly into RC2. That would avoid treating SDRTrunk as a broadcast stream just to ingest it again, and it would also keep the SDRTrunk-specific ingest/reconnect/metadata logic out of the main daemon if that is the preferred boundary.

Would the maintainers prefer that this live SDRTrunk bridge be maintained as a separate project, or is there a path where a clearly isolated section of RadioConsole2 upstream would be acceptable? I want to support and maintain the pieces I help develop, and over time I’d also be happy to help with other areas if that help is wanted.

To be transparent: yes, I am using LLM assistance, but I am reviewing the generated code and making sure I understand the intent and behavior before submitting it. I know enough to check that it is not doing anything malicious or wildly out of scope, but I am not yet at the point where I could write this from scratch inside a project I have not developed in before. I’m trying to be honest about that and respectful of the maintenance burden this could create.

@W3AXL
Copy link
Copy Markdown
Owner

W3AXL commented May 24, 2026

I think keeping the core RadioConsole2 daemon for real-radio control only is a good distinction to make. One of the primary goals of creating the rc2-core library was to allow for much easier creation of extensions and new, non-radio control modes (like rc2-dvm)

I think RX-only scanner feeding could definitely be handled by its own extension - rc2-scan or something along those lines? I would be curious to see if we could support a variety of the current popular SDR scanning tools - SDRTrunk and trunk-recorder being the two big ones. I absolutely agree that hooking into the real-time audio streaming paths is critical - there's little advantage to a dedicated streaming extension for RC2 if it's no better than something like Rdio-Scanner.

Closing this PR, but encouraging exploration of a new rc2-scan (or perhaps rc2-rx?) extension similar to rc2-dvm.

@W3AXL W3AXL closed this May 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants