Skip to content
File

Blob: firmware/crates/radio-webrtc/src/audio.rs

rust73 lines
1use crate::{Error, Result};
2use std::time::Instant;
3use str0m::{
4 Rtc,
5 format::Codec,
6 media::{Frequency, Mid},
7 rtp::{RtpWrite, SeqNo},
8};
9 
10/// One Opus packet per RTP packet, with sequence state tied to the SSRC lifetime.
11#[derive(Debug)]
12pub(crate) struct Audio {
13 mid: Mid,
14 next_seq: SeqNo,
15}
16 
17impl Audio {
18 pub(crate) fn new(mid: Mid) -> Self {
19 Self {
20 mid,
21 // Match str0m's random initial sequence and start with a zero ROC.
22 next_seq: SeqNo::default(),
23 }
24 }
25 
26 pub(crate) fn write(&mut self, rtc: &mut Rtc, pts_ms: u32, bytes: &[u8]) -> Result<()> {
27 if bytes.is_empty() || bytes.len() > radio_core::music::MAX_OPUS_BYTES {
28 return Err(Error::new("invalid Opus packet size"));
29 }
30 // Direct writes retain their payload, so never queue audio without a
31 // live ICE/DTLS/SRTP transport to consume it.
32 if !rtc.is_alive() || !rtc.is_connected() {
33 return Err(Error::new("audio transport unavailable"));
34 }
35 let media = rtc
36 .media(self.mid)
37 .filter(|media| !media.disabled() && media.direction().is_sending())
38 .ok_or(Error::new("audio media unavailable"))?;
39 let pt = rtc
40 .codec_config()
41 .params()
42 .iter()
43 .find(|params| {
44 let spec = params.spec();
45 media.remote_pts().contains(&params.pt())
46 && spec.codec == Codec::Opus
47 && spec.clock_rate == Frequency::FORTY_EIGHT_KHZ
48 && spec.channels == Some(2)
49 })
50 .ok_or(Error::new("Opus was not negotiated"))?
51 .pt();
52 let mut direct = rtc.direct_api();
53 let stream = direct
54 .stream_tx_by_mid(self.mid, None)
55 .ok_or(Error::new("audio stream unavailable"))?;
56 
57 // Pauses send encoded silence, without DTX/talkspurt markers. str0m
58 // supplies negotiated header extensions, pacing and SRTP encryption.
59 // The owned Arc payload outlives the caller's reusable music buffer.
60 stream.write_rtp(RtpWrite::new(
61 pt,
62 self.next_seq.inc(),
63 pts_ms.wrapping_mul(48),
64 Instant::now(),
65 bytes,
66 ));
67 Ok(())
68 }
69}
70 
71#[cfg(test)]
72mod tests;