Skip to content

Commit 0a193a4

Browse files
Fix the fast path in capture_frame function, without buffering (#778)
* fix the fast path in capture_frame function, without buffering * added a comment
1 parent 0d2b10e commit 0a193a4

File tree

1 file changed

+63
-0
lines changed

1 file changed

+63
-0
lines changed

libwebrtc/src/native/audio_source.rs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,25 @@ pub struct NativeAudioSource {
2727
}
2828

2929
impl NativeAudioSource {
30+
/// Creates a new [`NativeAudioSource`].
31+
///
32+
/// # Arguments
33+
/// * `options` – Configuration options for the source (e.g. echo cancellation, noise suppression).
34+
/// * `sample_rate` – Sampling rate in Hz (for example, `48000`).
35+
/// * `num_channels` – Number of audio channels (`1` for mono, `2` for stereo, etc.).
36+
/// * `queue_size_ms` – Size of the internal buffering queue, in milliseconds.
37+
///
38+
/// # Behavior
39+
/// - If `queue_size_ms` is **zero**, buffering is **disabled** and audio frames are
40+
/// delivered directly to webrtc sinks. In this mode, the caller **must provide 10 ms frames**
41+
/// (i.e., `sample_rate / 100` samples per channel) when calling [`capture_frame`].
42+
/// - If `queue_size_ms` is **non-zero**, buffering is enabled. The value must be a
43+
/// **multiple of 10**, representing the total buffering duration in milliseconds.
44+
/// Frames will be queued and flushed to sinks asynchronously once the buffer
45+
/// reaches the configured threshold.
46+
///
47+
/// # Panics
48+
/// assert if `queue_size_ms` is not a multiple of 10.
3049
pub fn new(
3150
options: AudioSourceOptions,
3251
sample_rate: u32,
@@ -78,6 +97,49 @@ impl NativeAudioSource {
7897
});
7998
}
8099

100+
// Fast path: no buffering
101+
if self.queue_size_samples == 0 {
102+
// frame size must be 10ms for fast path
103+
let expected_frames_per_ch = (self.sample_rate / 100) as usize;
104+
if frame.data.len() % (self.num_channels as usize) != 0 {
105+
return Err(RtcError {
106+
error_type: RtcErrorType::InvalidState,
107+
message: "frame.data length not divisible by channel count".to_owned(),
108+
});
109+
}
110+
let nb_frames = frame.data.len() / (self.num_channels as usize);
111+
if nb_frames != expected_frames_per_ch {
112+
return Err(RtcError {
113+
error_type: RtcErrorType::InvalidState,
114+
message: format!(
115+
"direct capture requires 10ms frames: got {} frames, expected {}",
116+
nb_frames, expected_frames_per_ch
117+
),
118+
});
119+
}
120+
121+
unsafe {
122+
// Pass null ctx + null callback; C++ ignores them in direct mode
123+
let data: &[i16] = frame.data.as_ref();
124+
let ok = self.sys_handle.capture_frame(
125+
data,
126+
self.sample_rate,
127+
self.num_channels,
128+
nb_frames,
129+
std::ptr::null(),
130+
std::mem::zeroed::<sys_at::CompleteCallback>(),
131+
);
132+
if !ok {
133+
return Err(RtcError {
134+
error_type: RtcErrorType::InvalidState,
135+
message: "failed to capture frame without buffering".to_owned(),
136+
});
137+
}
138+
}
139+
return Ok(());
140+
}
141+
142+
// Buffered path.
81143
extern "C" fn lk_audio_source_complete(userdata: *const sys_at::SourceContext) {
82144
let tx = unsafe { Box::from_raw(userdata as *mut oneshot::Sender<()>) };
83145
let _ = tx.send(());
@@ -91,6 +153,7 @@ impl NativeAudioSource {
91153
let ctx_ptr = Box::into_raw(ctx) as *const sys_at::SourceContext;
92154

93155
unsafe {
156+
// In the fast path, C++ never store / invoke on_complete / ctx.
94157
if !self.sys_handle.capture_frame(
95158
chunk,
96159
self.sample_rate,

0 commit comments

Comments
 (0)