Skip to content

Commit 7e51858

Browse files
authored
add AudioProcessingModule (#580)
1 parent 8e3e1b1 commit 7e51858

File tree

19 files changed

+2032
-275
lines changed

19 files changed

+2032
-275
lines changed

libwebrtc/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ pub mod video_track;
6666
pub mod native {
6767
pub use webrtc_sys::webrtc::ffi::create_random_uuid;
6868

69-
pub use crate::imp::{audio_resampler, frame_cryptor, yuv_helper};
69+
pub use crate::imp::{apm, audio_resampler, frame_cryptor, yuv_helper};
7070
}
7171

7272
#[cfg(target_os = "android")]

libwebrtc/src/native/apm.rs

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
// Copyright 2023 LiveKit, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
use cxx::UniquePtr;
16+
use webrtc_sys::apm::ffi as sys_apm;
17+
18+
use crate::{RtcError, RtcErrorType};
19+
20+
pub struct AudioProcessingModule {
21+
sys_handle: UniquePtr<sys_apm::AudioProcessingModule>,
22+
}
23+
24+
impl AudioProcessingModule {
25+
pub fn new(
26+
echo_canceller_enabled: bool,
27+
gain_controller_enabled: bool,
28+
high_pass_filter_enabled: bool,
29+
noise_suppression_enabled: bool,
30+
) -> Self {
31+
Self {
32+
sys_handle: unsafe {
33+
sys_apm::create_apm(
34+
echo_canceller_enabled,
35+
gain_controller_enabled,
36+
high_pass_filter_enabled,
37+
noise_suppression_enabled,
38+
)
39+
},
40+
}
41+
}
42+
43+
pub fn process_stream(
44+
&mut self,
45+
data: &mut [i16],
46+
sample_rate: i32,
47+
num_channels: i32,
48+
) -> Result<(), RtcError> {
49+
let samples_count = (sample_rate as usize / 100) * num_channels as usize;
50+
assert_eq!(data.len(), samples_count, "slice must have 10ms worth of samples");
51+
52+
if unsafe {
53+
// using the same slice for src and dst is safe
54+
self.sys_handle.pin_mut().process_stream(
55+
data.as_mut_ptr(),
56+
data.len(),
57+
data.as_mut_ptr(),
58+
data.len(),
59+
sample_rate,
60+
num_channels,
61+
)
62+
} == 0
63+
{
64+
Ok(())
65+
} else {
66+
Err(RtcError {
67+
error_type: RtcErrorType::Internal,
68+
message: "Failed to process stream".to_string(),
69+
})
70+
}
71+
}
72+
73+
pub fn process_reverse_stream(
74+
&mut self,
75+
data: &mut [i16],
76+
sample_rate: i32,
77+
num_channels: i32,
78+
) -> Result<(), RtcError> {
79+
let samples_count = (sample_rate as usize / 100) * num_channels as usize;
80+
assert_eq!(data.len(), samples_count, "slice must have 10ms worth of samples");
81+
82+
if unsafe {
83+
// using the same slice for src and dst is safe
84+
self.sys_handle.pin_mut().process_reverse_stream(
85+
data.as_mut_ptr(),
86+
data.len(),
87+
data.as_mut_ptr(),
88+
data.len(),
89+
sample_rate,
90+
num_channels,
91+
)
92+
} == 0
93+
{
94+
Ok(())
95+
} else {
96+
Err(RtcError {
97+
error_type: RtcErrorType::Internal,
98+
message: "Failed to process reverse stream".to_string(),
99+
})
100+
}
101+
}
102+
}

libwebrtc/src/native/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
pub mod apm;
1516
#[cfg(target_os = "android")]
1617
pub mod android;
1718
pub mod audio_resampler;

livekit-api/src/services/egress.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ impl EgressClient {
120120
segment_outputs,
121121
image_outputs,
122122
output: None, // Deprecated
123+
..Default::default()
123124
},
124125
self.base
125126
.auth_header(VideoGrants { room_record: true, ..Default::default() }, None)?,

livekit-api/src/services/sip.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,7 @@ impl SIPClient {
452452
headers: Default::default(),
453453
include_headers: Default::default(),
454454
media_encryption: Default::default(),
455+
..Default::default()
455456
},
456457
self.base.auth_header(
457458
Default::default(),

livekit-ffi/protocol/audio_frame.proto

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,42 @@ message RemixAndResampleResponse {
9191
required OwnedAudioFrameBuffer buffer = 1;
9292
}
9393

94+
// AEC
95+
96+
97+
message NewApmRequest {
98+
required bool echo_canceller_enabled = 1;
99+
required bool gain_controller_enabled = 2;
100+
required bool high_pass_filter_enabled = 3;
101+
required bool noise_suppression_enabled = 4;
102+
}
103+
message NewApmResponse {
104+
required OwnedApm apm = 1;
105+
}
106+
107+
message ApmProcessStreamRequest {
108+
required uint64 apm_handle = 1;
109+
required uint64 data_ptr = 2; // *mut i16
110+
required uint32 size = 3; // in bytes
111+
required uint32 sample_rate = 4;
112+
required uint32 num_channels = 5;
113+
}
114+
115+
message ApmProcessStreamResponse {
116+
optional string error = 1;
117+
}
118+
119+
message ApmProcessReverseStreamRequest {
120+
required uint64 apm_handle = 1;
121+
required uint64 data_ptr = 2; // *mut i16
122+
required uint32 size = 3; // in bytes
123+
required uint32 sample_rate = 4;
124+
required uint32 num_channels = 5;
125+
}
126+
127+
message ApmProcessReverseStreamResponse {
128+
optional string error = 1;
129+
}
94130

95131
// New resampler using SoX (much better quality)
96132

@@ -241,6 +277,13 @@ message OwnedAudioResampler {
241277
}
242278

243279

280+
//
281+
// AEC
282+
//
283+
284+
message OwnedApm {
285+
required FfiOwnedHandle handle = 1;
286+
}
244287

245288
//
246289
// Sox AudioResampler

livekit-ffi/protocol/ffi.proto

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,12 @@ message FfiRequest {
122122

123123
// Audio Filter Plugin
124124
LoadAudioFilterPluginRequest load_audio_filter_plugin = 49;
125+
126+
NewApmRequest new_apm = 50;
127+
ApmProcessStreamRequest apm_process_stream = 51;
128+
ApmProcessReverseStreamRequest apm_process_reverse_stream = 52;
129+
130+
// NEXT_ID: 53
125131
}
126132
}
127133

@@ -191,6 +197,12 @@ message FfiResponse {
191197

192198
// Audio Filter Plugin
193199
LoadAudioFilterPluginResponse load_audio_filter_plugin = 48;
200+
201+
NewApmResponse new_apm = 49;
202+
ApmProcessStreamResponse apm_process_stream = 50;
203+
ApmProcessReverseStreamResponse apm_process_reverse_stream = 51;
204+
205+
// NEXT_ID: 52
194206
}
195207
}
196208

livekit-ffi/src/livekit.proto.rs

Lines changed: 89 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// @generated
2+
// This file is @generated by prost-build.
23
#[allow(clippy::derive_partial_eq_without_eq)]
34
#[derive(Clone, PartialEq, ::prost::Message)]
45
pub struct FrameCryptor {
@@ -3554,6 +3555,70 @@ pub struct RemixAndResampleResponse {
35543555
#[prost(message, required, tag="1")]
35553556
pub buffer: OwnedAudioFrameBuffer,
35563557
}
3558+
// AEC
3559+
3560+
#[allow(clippy::derive_partial_eq_without_eq)]
3561+
#[derive(Clone, PartialEq, ::prost::Message)]
3562+
pub struct NewApmRequest {
3563+
#[prost(bool, required, tag="1")]
3564+
pub echo_canceller_enabled: bool,
3565+
#[prost(bool, required, tag="2")]
3566+
pub gain_controller_enabled: bool,
3567+
#[prost(bool, required, tag="3")]
3568+
pub high_pass_filter_enabled: bool,
3569+
#[prost(bool, required, tag="4")]
3570+
pub noise_suppression_enabled: bool,
3571+
}
3572+
#[allow(clippy::derive_partial_eq_without_eq)]
3573+
#[derive(Clone, PartialEq, ::prost::Message)]
3574+
pub struct NewApmResponse {
3575+
#[prost(message, required, tag="1")]
3576+
pub apm: OwnedApm,
3577+
}
3578+
#[allow(clippy::derive_partial_eq_without_eq)]
3579+
#[derive(Clone, PartialEq, ::prost::Message)]
3580+
pub struct ApmProcessStreamRequest {
3581+
#[prost(uint64, required, tag="1")]
3582+
pub apm_handle: u64,
3583+
/// *mut i16
3584+
#[prost(uint64, required, tag="2")]
3585+
pub data_ptr: u64,
3586+
/// in bytes
3587+
#[prost(uint32, required, tag="3")]
3588+
pub size: u32,
3589+
#[prost(uint32, required, tag="4")]
3590+
pub sample_rate: u32,
3591+
#[prost(uint32, required, tag="5")]
3592+
pub num_channels: u32,
3593+
}
3594+
#[allow(clippy::derive_partial_eq_without_eq)]
3595+
#[derive(Clone, PartialEq, ::prost::Message)]
3596+
pub struct ApmProcessStreamResponse {
3597+
#[prost(string, optional, tag="1")]
3598+
pub error: ::core::option::Option<::prost::alloc::string::String>,
3599+
}
3600+
#[allow(clippy::derive_partial_eq_without_eq)]
3601+
#[derive(Clone, PartialEq, ::prost::Message)]
3602+
pub struct ApmProcessReverseStreamRequest {
3603+
#[prost(uint64, required, tag="1")]
3604+
pub apm_handle: u64,
3605+
/// *mut i16
3606+
#[prost(uint64, required, tag="2")]
3607+
pub data_ptr: u64,
3608+
/// in bytes
3609+
#[prost(uint32, required, tag="3")]
3610+
pub size: u32,
3611+
#[prost(uint32, required, tag="4")]
3612+
pub sample_rate: u32,
3613+
#[prost(uint32, required, tag="5")]
3614+
pub num_channels: u32,
3615+
}
3616+
#[allow(clippy::derive_partial_eq_without_eq)]
3617+
#[derive(Clone, PartialEq, ::prost::Message)]
3618+
pub struct ApmProcessReverseStreamResponse {
3619+
#[prost(string, optional, tag="1")]
3620+
pub error: ::core::option::Option<::prost::alloc::string::String>,
3621+
}
35573622
// New resampler using SoX (much better quality)
35583623

35593624
#[allow(clippy::derive_partial_eq_without_eq)]
@@ -3746,6 +3811,16 @@ pub struct OwnedAudioResampler {
37463811
pub info: AudioResamplerInfo,
37473812
}
37483813
//
3814+
// AEC
3815+
//
3816+
3817+
#[allow(clippy::derive_partial_eq_without_eq)]
3818+
#[derive(Clone, PartialEq, ::prost::Message)]
3819+
pub struct OwnedApm {
3820+
#[prost(message, required, tag="1")]
3821+
pub handle: FfiOwnedHandle,
3822+
}
3823+
//
37493824
// Sox AudioResampler
37503825
//
37513826

@@ -4075,7 +4150,7 @@ pub struct RpcMethodInvocationEvent {
40754150
#[allow(clippy::derive_partial_eq_without_eq)]
40764151
#[derive(Clone, PartialEq, ::prost::Message)]
40774152
pub struct FfiRequest {
4078-
#[prost(oneof="ffi_request::Message", tags="2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 48, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 49")]
4153+
#[prost(oneof="ffi_request::Message", tags="2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 48, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 49, 50, 51, 52")]
40794154
pub message: ::core::option::Option<ffi_request::Message>,
40804155
}
40814156
/// Nested message and enum types in `FfiRequest`.
@@ -4188,13 +4263,19 @@ pub mod ffi_request {
41884263
/// Audio Filter Plugin
41894264
#[prost(message, tag="49")]
41904265
LoadAudioFilterPlugin(super::LoadAudioFilterPluginRequest),
4266+
#[prost(message, tag="50")]
4267+
NewApm(super::NewApmRequest),
4268+
#[prost(message, tag="51")]
4269+
ApmProcessStream(super::ApmProcessStreamRequest),
4270+
#[prost(message, tag="52")]
4271+
ApmProcessReverseStream(super::ApmProcessReverseStreamRequest),
41914272
}
41924273
}
41934274
/// This is the output of livekit_ffi_request function.
41944275
#[allow(clippy::derive_partial_eq_without_eq)]
41954276
#[derive(Clone, PartialEq, ::prost::Message)]
41964277
pub struct FfiResponse {
4197-
#[prost(oneof="ffi_response::Message", tags="2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 47, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 48")]
4278+
#[prost(oneof="ffi_response::Message", tags="2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 47, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 48, 49, 50, 51")]
41984279
pub message: ::core::option::Option<ffi_response::Message>,
41994280
}
42004281
/// Nested message and enum types in `FfiResponse`.
@@ -4305,6 +4386,12 @@ pub mod ffi_response {
43054386
/// Audio Filter Plugin
43064387
#[prost(message, tag="48")]
43074388
LoadAudioFilterPlugin(super::LoadAudioFilterPluginResponse),
4389+
#[prost(message, tag="49")]
4390+
NewApm(super::NewApmResponse),
4391+
#[prost(message, tag="50")]
4392+
ApmProcessStream(super::ApmProcessStreamResponse),
4393+
#[prost(message, tag="51")]
4394+
ApmProcessReverseStream(super::ApmProcessReverseStreamResponse),
43084395
}
43094396
}
43104397
/// To minimize complexity, participant events are not included in the protocol.

livekit-ffi/src/server/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use std::{
2424

2525
use dashmap::{mapref::one::MappedRef, DashMap};
2626
use downcast_rs::{impl_downcast, Downcast};
27-
use livekit::webrtc::{native::audio_resampler::AudioResampler, prelude::*};
27+
use livekit::webrtc::{native::apm::AudioProcessingModule, native::audio_resampler::AudioResampler, prelude::*};
2828
use parking_lot::{deadlock, Mutex};
2929
use tokio::{sync::oneshot, task::JoinHandle};
3030

@@ -67,6 +67,7 @@ pub struct FfiDataBuffer {
6767

6868
impl FfiHandle for FfiDataBuffer {}
6969
impl FfiHandle for Arc<Mutex<AudioResampler>> {}
70+
impl FfiHandle for Arc<Mutex<AudioProcessingModule>> {}
7071
impl FfiHandle for Arc<Mutex<resampler::SoxResampler>> {}
7172
impl FfiHandle for AudioFrame<'static> {}
7273
impl FfiHandle for BoxVideoBuffer {}

0 commit comments

Comments
 (0)