Текстовый файл из учебного комплекта, не свидетельство установленного runtime. Вернуться к учебнику · Исходный файл
1 //! A single Opus RTP track. Muted capture sends generated silence to keep the peer alive; 2 //! elapsed time still advances its clock without inventing packet loss. 3 4 use std::sync::Arc; 5 use std::time::Duration; 6 use std::time::Instant; 7 8 use rtc::media::Sample; 9 use rtc::rtp_transceiver::rtp_sender::RTCRtpCodec; 10 use rtc::rtp_transceiver::rtp_sender::RTCRtpCodecParameters; 11 use rtc::rtp_transceiver::rtp_sender::RTCRtpCodingParameters; 12 use rtc::rtp_transceiver::rtp_sender::RTCRtpEncodingParameters; 13 use rtc::rtp_transceiver::rtp_sender::RtpCodecKind; 14 use webrtc::media_stream::MediaStreamTrack; 15 use webrtc::media_stream::track_local::static_sample::TrackLocalStaticSample; 16 use webrtc::peer_connection::MediaEngine; 17 18 pub(crate) const OPUS_PAYLOAD_TYPE: u8 = 111; 19 pub(crate) const SEND_TIMEOUT: Duration = Duration::from_millis(/*millis*/ 100); 20 21 pub(crate) struct EncodedAudio { 22 pub(crate) data: Vec<u8>, 23 pub(crate) at: Instant, 24 } 25 26 pub(crate) struct AudioTrack { 27 pub(crate) track: Arc<TrackLocalStaticSample>, 28 ssrc: u32, 29 end: Option<Instant>, 30 silence: Option<Vec<u8>>, 31 } 32 33 impl AudioTrack { 34 pub(crate) fn new() -> Result<(MediaEngine, Self), &'static str> { 35 let ssrc = rand::random(); 36 let codec = RTCRtpCodec { 37 mime_type: "audio/opus".into(), 38 clock_rate: 48_000, 39 channels: 2, 40 sdp_fmtp_line: "minptime=10;useinbandfec=1".into(), 41 rtcp_feedback: vec![], 42 }; 43 let mut media = MediaEngine::default(); 44 media 45 .register_codec( 46 RTCRtpCodecParameters { 47 rtp_codec: codec.clone(), 48 payload_type: OPUS_PAYLOAD_TYPE, 49 }, 50 RtpCodecKind::Audio, 51 ) 52 .map_err(|_| "failed to register voice codec")?; 53 let track = TrackLocalStaticSample::new(MediaStreamTrack::new( 54 "realtime".into(), 55 format!("audio-{ssrc}"), 56 "microphone".into(), 57 RtpCodecKind::Audio, 58 vec![RTCRtpEncodingParameters { 59 rtp_coding_parameters: RTCRtpCodingParameters { 60 ssrc: Some(ssrc), 61 ..Default::default() 62 }, 63 codec, 64 active: true, 65 ..Default::default() 66 }], 67 )) 68 .map_err(|_| "failed to create voice track")?; 69 Ok(( 70 media, 71 Self { 72 track: Arc::new(track), 73 ssrc, 74 end: None, 75 silence: None, 76 }, 77 )) 78 } 79 80 /// Send only synthetic silence while muted, never a device or processing buffer. 81 /// Reuse the encoded packet and pace it by the same RTP clock as live capture. 82 #[cfg(any( 83 target_os = "macos", 84 all(target_os = "linux", target_env = "gnu"), 85 all(windows, target_env = "msvc") 86 ))] 87 pub(crate) async fn send_muted_silence(&mut self, at: Instant) -> Result<(), &'static str> { 88 if self.end.is_some_and(|end| at < end) { 89 return Ok(()); 90 } 91 if self.silence.is_none() { 92 let mut encoder = opus::Encoder::new( 93 /*sample_rate*/ 48_000, 94 opus::Channels::Mono, 95 opus::Application::Voip, 96 ) 97 .map_err(|_| "failed to create voice silence encoder")?; 98 let mut data = vec![0; 1275]; 99 let len = encoder 100 .encode_float(&[0.0; 960], &mut data) 101 .map_err(|_| "failed to encode voice silence")?; 102 data.truncate(len); 103 self.silence = Some(data); 104 } 105 self.send(EncodedAudio { 106 data: self 107 .silence 108 .as_ref() 109 .ok_or("voice silence unavailable")? 110 .clone(), 111 at, 112 }) 113 .await 114 } 115 116 pub(crate) async fn send(&mut self, frame: EncodedAudio) -> Result<(), &'static str> { 117 tokio::time::timeout(SEND_TIMEOUT, async { 118 let mut gap = self 119 .end 120 .map(|end| frame.at.saturating_duration_since(end)) 121 .unwrap_or_default(); 122 while !gap.is_zero() { 123 let duration = gap.min(Duration::from_secs(/*secs*/ 3600)); 124 self.track 125 .write_sample( 126 self.ssrc, 127 /*payload_type*/ OPUS_PAYLOAD_TYPE, 128 &Sample { 129 duration, 130 ..Default::default() 131 }, 132 &[], 133 ) 134 .await 135 .map_err(|_| "failed to advance voice clock")?; 136 gap -= duration; 137 } 138 let duration = Duration::from_millis(/*millis*/ 20); 139 self.track 140 .write_sample( 141 self.ssrc, 142 /*payload_type*/ OPUS_PAYLOAD_TYPE, 143 &Sample { 144 data: frame.data.into(), 145 duration, 146 ..Default::default() 147 }, 148 &[], 149 ) 150 .await 151 .map_err(|_| "failed to send voice audio")?; 152 self.end = Some(self.end.unwrap_or(frame.at).max(frame.at) + duration); 153 Ok(()) 154 }) 155 .await 156 .map_err(|_| "voice audio sender stalled")? 157 } 158 } 159 160 #[cfg(test)] 161 #[path = "audio_track_tests.rs"] 162 mod tests;