Skip to content
File

Blob: firmware/vendor/str0m/src/media/writer.rs

rust250 lines
1use std::sync::Arc;
2use std::time::Instant;
3 
4use crate::RtcError;
5use crate::format::PayloadParams;
6use crate::rtp_::AbsCaptureTime;
7use crate::rtp_::MidRid;
8use crate::rtp_::VideoOrientation;
9use crate::session::Session;
10 
11use super::{ExtensionValues, KeyframeRequestKind, Media, MediaTime, Mid, Pt, Rid, ToPayload};
12 
13/// Writer of frame level data.
14///
15/// Obtained via [`Rtc::writer`][crate::Rtc::writer].
16///
17/// This is the Frame Level API. For RTP level see
18/// [`DirectApi::stream_tx`][crate::change::DirectApi::stream_tx].
19pub struct Writer<'a> {
20 session: &'a mut Session,
21 mid: Mid,
22 rid: Option<Rid>,
23 start_of_talkspurt: Option<bool>,
24 ext_vals: ExtensionValues,
25}
26 
27impl<'a> Writer<'a> {
28 /// Create a new writer object.
29 ///
30 /// The `mid` parameter is required to have a corresponding media in `self.session`.
31 pub(crate) fn new(session: &'a mut Session, mid: Mid) -> Self {
32 Writer {
33 session,
34 mid,
35 rid: None,
36 start_of_talkspurt: None,
37 ext_vals: ExtensionValues::default(),
38 }
39 }
40 
41 /// Get the configured payload parameters for the `mid` this writer is for.
42 ///
43 /// For the [`Writer::write()`] call, the `pt` must be set correctly.
44 pub fn payload_params(&self) -> impl Iterator<Item = &PayloadParams> {
45 // This unwrap is OK due to the invariant of self.mid being resolvable
46 let media = self.session.media_by_mid(self.mid).unwrap();
47 self.session
48 .codec_config
49 .params()
50 .iter()
51 .filter(|p| media.remote_pts().contains(&p.pt))
52 }
53 
54 /// Match the given parameters to the configured parameters for this [`Media`].
55 ///
56 /// In a server scenario, a certain codec configuration might not have the same
57 /// payload type (PT) for two different peers. We will have incoming data with one
58 /// PT and need to match that against the PT of the outgoing [`Media`].
59 ///
60 /// This call performs matching and if a match is found, returns the _local_ PT
61 /// that can be used for sending media.
62 pub fn match_params(&self, params: PayloadParams) -> Option<Pt> {
63 self.session
64 .codec_config
65 .match_params(params)
66 .map(|p| p.pt())
67 }
68 
69 /// Add on an Rtp Stream Id. This is typically used to separate simulcast layers.
70 pub fn rid(mut self, rid: Rid) -> Self {
71 self.rid = Some(rid);
72 self
73 }
74 
75 /// Add on audio level and voice activity. These values are communicated in the same
76 /// RTP header extension, hence it makes sense setting both at the same time.
77 ///
78 /// Audio level is measured in negative decibel. 0 is max and a "normal" value might be -30.
79 pub fn audio_level(mut self, audio_level: i8, voice_activity: bool) -> Self {
80 self.ext_vals.audio_level = Some(audio_level);
81 self.ext_vals.voice_activity = Some(voice_activity);
82 self
83 }
84 
85 /// First packet of a talkspurt, that is the first packet after a silence period during
86 /// which packets have not been transmitted contiguously.
87 ///
88 /// For audio only when dtx or silence suppression is enabled.
89 /// This will set the marker bit in the RTP header.
90 pub fn start_of_talkspurt(mut self, start_of_talkspurt: bool) -> Self {
91 self.start_of_talkspurt = Some(start_of_talkspurt);
92 self
93 }
94 
95 /// Add video orientation. This can be used by a player on the receiver end to decide
96 /// whether the video requires to be rotated to show correctly.
97 pub fn video_orientation(mut self, o: VideoOrientation) -> Self {
98 self.ext_vals.video_orientation = Some(o);
99 self
100 }
101 
102 /// Set absolute capture time for this frame.
103 pub fn abs_capture_time(mut self, capture_time: AbsCaptureTime) -> Self {
104 self.ext_vals.abs_capture_time = Some(capture_time);
105 self
106 }
107 
108 /// Set the minimum and maximum playout delay values. This can be used by a player
109 /// on the receiver end to determine the size of the jitter buffer.
110 pub fn playout_delay(mut self, min: MediaTime, max: MediaTime) -> Self {
111 self.ext_vals.play_delay_min = Some(min);
112 self.ext_vals.play_delay_max = Some(max);
113 self
114 }
115 
116 /// Set a user extension value.
117 pub fn user_extension_value<T: Send + Sync + 'static>(mut self, val: T) -> Self {
118 self.ext_vals.user_values.set(val);
119 self
120 }
121 
122 /// Write media.
123 ///
124 /// This operation fails if the PT doesn't match a negotiated codec, or the RID (`None` or a value)
125 /// does not match anything negotiated.
126 ///
127 /// Regarding `wallclock` and `rtp_time`, the wallclock is the real world time that corresponds to
128 /// the `MediaTime`. For an SFU, this can be hard to know, since RTP packets typically only
129 /// contain the media time (RTP time). In the simplest SFU setup, the wallclock could simply
130 /// be the arrival time of the incoming RTP data (see
131 /// [`MediaData::network_time`][crate::media::MediaData]). For better synchronization the SFU
132 /// probably needs to weigh in clock drifts and data provided via the statistics.
133 ///
134 /// If you write media before `IceConnectionState` is `Connected` it will be dropped.
135 ///
136 /// Panics if [`RtcConfig::set_rtp_mode()`][crate::RtcConfig::set_rtp_mode] is `true`.
137 pub fn write(
138 self,
139 pt: Pt,
140 wallclock: Instant,
141 rtp_time: MediaTime,
142 data: impl Into<Arc<[u8]>>,
143 ) -> Result<(), RtcError> {
144 // This (indirect) unwrap is OK due to the invariant of self.mid being resolvable
145 let media = media_by_mid_mut(&mut self.session.medias, self.mid);
146 
147 if !self.session.codec_config.has_pt(pt) {
148 return Err(RtcError::UnknownPt(pt));
149 }
150 
151 if let Some(rid) = self.rid {
152 if !media.rids_tx().contains(rid) {
153 return Err(RtcError::UnknownRid(rid));
154 }
155 }
156 
157 let data: Arc<[u8]> = data.into();
158 
159 trace!(
160 "write {:?} {:?} {:?} time: {:?} len: {}",
161 self.mid,
162 self.rid,
163 pt,
164 rtp_time,
165 data.len()
166 );
167 
168 let to_payload = ToPayload {
169 pt,
170 rid: self.rid,
171 wallclock,
172 rtp_time,
173 data,
174 start_of_talk_spurt: self.start_of_talkspurt.unwrap_or(false),
175 ext_vals: self.ext_vals,
176 };
177 
178 media.set_to_payload(to_payload)?;
179 
180 Ok(())
181 }
182 
183 /// Test if the kind of keyframe request is possible.
184 ///
185 /// Sending a keyframe request requires the mechanic to be negotiated as a feedback mechanic
186 /// in the SDP offer/answer dance first.
187 ///
188 /// Specifically these SDP lines would enable FIR and PLI respectively (for payload type 96).
189 ///
190 /// ```text
191 /// a=rtcp-fb:96 ccm fir
192 /// a=rtcp-fb:96 nack pli
193 /// ```
194 pub fn is_request_keyframe_possible(&self, kind: KeyframeRequestKind) -> bool {
195 self.session.is_request_keyframe_possible(kind)
196 }
197 
198 /// Request a keyframe from a remote peer sending media data.
199 ///
200 /// For SDP: This can fail if the kind of request (PLI or FIR), as specified by the
201 /// [`KeyframeRequestKind`], is not negotiated in the SDP answer/offer for this m-line.
202 ///
203 /// To ensure the call will not fail, use [`Writer::is_request_keyframe_possible()`] to
204 /// check whether the feedback mechanism is enabled.
205 ///
206 /// # Example
207 ///
208 /// ```no_run
209 /// # use std::time::Instant;
210 /// # use str0m::Rtc;
211 /// # use str0m::media::{Mid, KeyframeRequestKind};
212 /// let mut rtc = Rtc::new(Instant::now());
213 ///
214 /// // add candidates, do SDP negotiation
215 /// let mid: Mid = todo!(); // obtain mid from Event::MediaAdded.
216 ///
217 /// let writer = rtc.writer(mid).unwrap();
218 ///
219 /// writer.request_keyframe(None, KeyframeRequestKind::Pli).unwrap();
220 /// ```
221 pub fn request_keyframe(
222 &mut self,
223 rid: Option<Rid>,
224 kind: KeyframeRequestKind,
225 ) -> Result<(), RtcError> {
226 if !self.is_request_keyframe_possible(kind) {
227 return Err(RtcError::NotReceivingDirection);
228 }
229 
230 let midrid = MidRid(self.mid, rid);
231 
232 let stream = self
233 .session
234 .streams
235 .stream_rx_by_midrid(midrid, false)
236 .ok_or(RtcError::NoReceiverSource(rid))?;
237 
238 stream.request_keyframe(kind);
239 
240 Ok(())
241 }
242}
243 
244/// Get a &mut Media in a slice for a `mid`.
245///
246/// `mid` must be resolvable or panic will ensue.
247fn media_by_mid_mut(medias: &mut [Media], mid: Mid) -> &mut Media {
248 medias.iter_mut().find(|m| m.mid() == mid).unwrap()
249}