Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# Changelog

## [0.1.4]

- A system's sites: Trunk Recorder Pro now has systems with several sites, and
a call heard on more than one starts and ends once, its live audio coming
from whichever site's copy has it. Audio from a copy that wasn't the one
that started now still finds its call (its patched talkgroups, radio and
frequency), also when that first copy wasn't recorded.
- Built with trunk-recorder-plugin 0.4.0.

## [0.1.3]

- A system can have streams of its own (the recorder's per-system override of the streams); they carry only its audio, and the recorder-wide streams no longer carry it.
Expand Down
6 changes: 3 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "simplestream"
version = "0.1.3"
version = "0.1.4"
edition = "2024"
description = "Streams the audio of calls as they're recorded to other programs, over UDP or TCP."
license = "GPL-3.0-or-later"
Expand All @@ -9,7 +9,7 @@ authors = ["Luke Berndt <lukekb@gmail.com>"]
publish = false

[dependencies]
trunk-recorder-plugin = "0.3.0"
trunk-recorder-plugin = "0.4.0"
schemars = "1"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ A list of **streams**. Each sends some of the audio to one place:
Talkgroup numbers repeat between systems, so stream each system to a port of
its own, or turn on JSON (which names the system).

A system can have several sites. A call heard on more than one is one call:
its audio streams once, from whichever site's copy has it at the moment, and
`short_name` is the system's.

## Packets

Each packet is one slice of audio, with, depending on the settings:
Expand Down
22 changes: 20 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ impl Out {

struct SimpleStream {
outs: Vec<Out>,
/// The calls recording now.
/// The calls going now (each once, however many sites hear it).
calls: HashMap<u32, CallInfo>,
}

Expand Down Expand Up @@ -167,7 +167,9 @@ impl Plugin for SimpleStream {
}

fn call_start(&mut self, call: CallInfo) {
// (Kept even when not recorded here: another site's copy of it may be, and its audio is this call's.)
if !call.recording {
self.calls.insert(call.id, call);
return;
}
let short_name = call.short_name.clone();
Expand All @@ -194,7 +196,10 @@ impl Plugin for SimpleStream {

fn audio(&mut self, chunk: AudioChunk) {
let pcm = base64::decode(&chunk.pcm);
let call = self.calls.get(&chunk.call_id);
// A call heard on several sites plays from whichever copy has it: one that wasn't
// told to start is the call that was (the same system and talkgroup).
let twin = || self.calls.values().find(|c| c.short_name == chunk.short_name && c.talkgroup == chunk.talkgroup);
let call = self.calls.get(&chunk.call_id).or_else(twin);
let short_name = chunk.short_name.as_str();
let talkgroups = call.map_or(vec![chunk.talkgroup], talkgroups);
for o in self.outs.iter().filter(|o| o.wants_system(short_name)) {
Expand Down Expand Up @@ -372,6 +377,19 @@ mod tests {
assert_eq!(last.extra["packetsSent"], 1);
}

/// A call heard on several sites is started once, by its first site's copy (here not
/// recorded); its audio can come from another site's copy, with another id: it is still the call.
#[test]
fn audio_from_another_sites_copy() {
let (s, url) = udp();
let mut first = call(1, 101, vec![101, 202]);
first.recording = false;
testing::run::<SimpleStream>([hello(json!([{ "url": url, "TGID": 202, "sendJSON": true }])), HostMessage::CallStart(first), audio(9, 101, &[1])]);
let (a, pcm) = split(&recv(&s).unwrap());
assert_eq!((a["talkgroup"].clone(), a["src"].clone(), a["freq"].clone()), (json!(202), json!(1234), json!(851012500u64)), "{a}");
assert_eq!(pcm, [1, 0]);
}

#[test]
fn json_and_call_events() {
let (s, url) = udp();
Expand Down
Loading