Skip to content

Expose AEC dump #632

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions libwebrtc/src/native/apm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
// limitations under the License.

use cxx::UniquePtr;
use std::path::Path;
use webrtc_sys::apm::ffi as sys_apm;

use crate::{RtcError, RtcErrorType};
Expand Down Expand Up @@ -110,4 +111,37 @@ impl AudioProcessingModule {
})
}
}

/// Creates and attaches an AEC dump for recording debugging information.
pub fn create_and_attach_aec_dump(
&mut self,
file_path: impl AsRef<Path>,
max_log_size_bytes: Option<i64>,
) -> Result<(), RtcError> {
let Some(file_path) = file_path.as_ref().to_str() else {
Err(RtcError {
error_type: RtcErrorType::Internal,
message: "Invalid file path".to_string(),
})?
};
let max_size = max_log_size_bytes.unwrap_or(-1);

if self.sys_handle.pin_mut().create_and_attach_aec_dump(file_path, max_size) {
Ok(())
} else {
Err(RtcError {
error_type: RtcErrorType::Internal,
message: "Failed to create and attach AEC dump".to_string(),
})
}
}

/// Ends an in-progress AEC dump.
///
/// If no AEC dump was created with [`create_and_attach_aec_dump`], this
/// method has no effect.
///
pub fn detach_aec_dump(&mut self) {
self.sys_handle.pin_mut().detach_aec_dump();
}
}
23 changes: 19 additions & 4 deletions livekit-ffi/protocol/audio_frame.proto
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,9 @@ message NewAudioSourceRequest {
}
message NewAudioSourceResponse { required OwnedAudioSource source = 1; }

// Push a frame to an AudioSource
// Push a frame to an AudioSource
// The data provided must be available as long as the client receive the callback.
message CaptureAudioFrameRequest {
message CaptureAudioFrameRequest {
required uint64 source_handle = 1;
required AudioFrameBufferInfo buffer = 2;
}
Expand Down Expand Up @@ -137,6 +137,21 @@ message ApmSetStreamDelayResponse {
optional string error = 1;
}

message ApmAecDumpCreateAndAttachRequest {
required uint64 apm_handle = 1;
required string file_path = 2;
optional int64 max_log_size_bytes = 3;
}

message ApmAecDumpCreateAndAttachResponse {
optional string error = 1;
}

message ApmAecDumpDetachRequest {
required uint64 apm_handle = 1;
}

message ApmAecDumpDetachResponse {}

// New resampler using SoX (much better quality)

Expand All @@ -154,7 +169,7 @@ message NewSoxResamplerResponse {
OwnedSoxResampler resampler = 1;
string error = 2;
}

}

message PushSoxResamplerRequest {
Expand Down Expand Up @@ -240,7 +255,7 @@ message OwnedAudioStream {

message AudioStreamEvent {
required uint64 stream_handle = 1;
oneof message {
oneof message {
AudioFrameReceived frame_received = 2;
AudioStreamEOS eos = 3;
}
Expand Down
10 changes: 8 additions & 2 deletions livekit-ffi/protocol/ffi.proto
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,10 @@ message FfiRequest {
TextStreamWriterWriteRequest text_stream_write = 65;
TextStreamWriterCloseRequest text_stream_close = 66;

// NEXT_ID: 67
ApmAecDumpCreateAndAttachRequest apm_aec_dump_create_and_attach = 67;
ApmAecDumpDetachRequest apm_aec_dump_detach = 68;

// NEXT_ID: 69
}
}

Expand Down Expand Up @@ -243,7 +246,10 @@ message FfiResponse {
TextStreamWriterWriteResponse text_stream_write = 64;
TextStreamWriterCloseResponse text_stream_close = 65;

// NEXT_ID: 66
ApmAecDumpCreateAndAttachResponse apm_aec_dump_create_and_attach = 66;
ApmAecDumpDetachResponse apm_aec_dump_detach = 67;

// NEXT_ID: 68
}
}

Expand Down
40 changes: 37 additions & 3 deletions livekit-ffi/src/livekit.proto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4131,7 +4131,7 @@ pub struct NewAudioSourceResponse {
#[prost(message, required, tag="1")]
pub source: OwnedAudioSource,
}
/// Push a frame to an AudioSource
/// Push a frame to an AudioSource
/// The data provided must be available as long as the client receive the callback.
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
Expand Down Expand Up @@ -4273,6 +4273,32 @@ pub struct ApmSetStreamDelayResponse {
#[prost(string, optional, tag="1")]
pub error: ::core::option::Option<::prost::alloc::string::String>,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct ApmAecDumpCreateAndAttachRequest {
#[prost(uint64, required, tag="1")]
pub apm_handle: u64,
#[prost(string, required, tag="2")]
pub file_path: ::prost::alloc::string::String,
#[prost(int64, optional, tag="3")]
pub max_log_size_bytes: ::core::option::Option<i64>,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct ApmAecDumpCreateAndAttachResponse {
#[prost(string, optional, tag="1")]
pub error: ::core::option::Option<::prost::alloc::string::String>,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct ApmAecDumpDetachRequest {
#[prost(uint64, required, tag="1")]
pub apm_handle: u64,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct ApmAecDumpDetachResponse {
}
// New resampler using SoX (much better quality)

#[allow(clippy::derive_partial_eq_without_eq)]
Expand Down Expand Up @@ -4804,7 +4830,7 @@ pub struct RpcMethodInvocationEvent {
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct FfiRequest {
#[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, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66")]
#[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, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68")]
pub message: ::core::option::Option<ffi_request::Message>,
}
/// Nested message and enum types in `FfiRequest`.
Expand Down Expand Up @@ -4952,13 +4978,17 @@ pub mod ffi_request {
TextStreamWrite(super::TextStreamWriterWriteRequest),
#[prost(message, tag="66")]
TextStreamClose(super::TextStreamWriterCloseRequest),
#[prost(message, tag="67")]
ApmAecDumpCreateAndAttach(super::ApmAecDumpCreateAndAttachRequest),
#[prost(message, tag="68")]
ApmAecDumpDetach(super::ApmAecDumpDetachRequest),
}
}
/// This is the output of livekit_ffi_request function.
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct FfiResponse {
#[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, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65")]
#[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, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67")]
pub message: ::core::option::Option<ffi_response::Message>,
}
/// Nested message and enum types in `FfiResponse`.
Expand Down Expand Up @@ -5104,6 +5134,10 @@ pub mod ffi_response {
TextStreamWrite(super::TextStreamWriterWriteResponse),
#[prost(message, tag="65")]
TextStreamClose(super::TextStreamWriterCloseResponse),
#[prost(message, tag="66")]
ApmAecDumpCreateAndAttach(super::ApmAecDumpCreateAndAttachResponse),
#[prost(message, tag="67")]
ApmAecDumpDetach(super::ApmAecDumpDetachResponse),
}
}
/// To minimize complexity, participant events are not included in the protocol.
Expand Down
38 changes: 38 additions & 0 deletions livekit-ffi/src/server/requests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -958,6 +958,36 @@ fn on_apm_set_stream_delay(
Ok(proto::ApmSetStreamDelayResponse { error: None })
}

fn on_apm_aec_dump_create_and_attach(
server: &'static FfiServer,
request: proto::ApmAecDumpCreateAndAttachRequest,
) -> FfiResult<proto::ApmAecDumpCreateAndAttachResponse> {
let aec = server
.retrieve_handle::<Arc<Mutex<apm::AudioProcessingModule>>>(request.apm_handle)?
.clone();

let mut aec = aec.lock();

if let Err(e) = aec.create_and_attach_aec_dump(&request.file_path, request.max_log_size_bytes) {
return Ok(proto::ApmAecDumpCreateAndAttachResponse { error: Some(e.to_string()) });
}

Ok(proto::ApmAecDumpCreateAndAttachResponse { error: None })
}

fn on_apm_aec_dump_detach(
server: &'static FfiServer,
request: proto::ApmAecDumpDetachRequest,
) -> FfiResult<proto::ApmAecDumpDetachResponse> {
let aec = server
.retrieve_handle::<Arc<Mutex<apm::AudioProcessingModule>>>(request.apm_handle)?
.clone();

let mut aec = aec.lock();
aec.detach_aec_dump();
Ok(proto::ApmAecDumpDetachResponse {})
}

fn on_perform_rpc(
server: &'static FfiServer,
request: proto::PerformRpcRequest,
Expand Down Expand Up @@ -1311,6 +1341,14 @@ pub fn handle_request(
server, request,
)?)
}
proto::ffi_request::Message::ApmAecDumpCreateAndAttach(request) => {
proto::ffi_response::Message::ApmAecDumpCreateAndAttach(
on_apm_aec_dump_create_and_attach(server, request)?,
)
}
proto::ffi_request::Message::ApmAecDumpDetach(request) => {
proto::ffi_response::Message::ApmAecDumpDetach(on_apm_aec_dump_detach(server, request)?)
}
proto::ffi_request::Message::PerformRpc(request) => {
proto::ffi_response::Message::PerformRpc(on_perform_rpc(server, request)?)
}
Expand Down
9 changes: 9 additions & 0 deletions webrtc-sys/include/livekit/apm.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,13 @@
#include <memory>

#include "api/scoped_refptr.h"
#include "api/task_queue/task_queue_base.h"
#include "api/video_codecs/video_decoder_factory.h"
#include "api/video_codecs/video_encoder_factory.h"
#include "modules/audio_processing/aec3/echo_canceller3.h"
#include "modules/audio_processing/audio_buffer.h"
#include "livekit/global_task_queue.h"
#include "rust/cxx.h"

namespace livekit {

Expand Down Expand Up @@ -62,8 +65,14 @@ class AudioProcessingModule {

int set_stream_delay_ms(int delay_ms);

bool create_and_attach_aec_dump(rust::Str file_name,
int64_t max_log_size_bytes);

void detach_aec_dump();

private:
rtc::scoped_refptr<webrtc::AudioProcessing> apm_;
std::unique_ptr<webrtc::TaskQueueBase, webrtc::TaskQueueDeleter> aec_dump_queue_;
};

std::unique_ptr<AudioProcessingModule> create_apm(
Expand Down
17 changes: 17 additions & 0 deletions webrtc-sys/src/apm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,21 @@ std::unique_ptr<AudioProcessingModule> create_apm(
return std::make_unique<AudioProcessingModule>(config);
}

bool AudioProcessingModule::create_and_attach_aec_dump(rust::Str file_name,
int64_t max_log_size_bytes) {
if (!aec_dump_queue_) {
aec_dump_queue_ = GetGlobalTaskQueueFactory()->CreateTaskQueue(
"aec-dump", webrtc::TaskQueueFactory::Priority::LOW);
}
return apm_->CreateAndAttachAecDump(
absl::string_view(file_name.data(), file_name.size()),
max_log_size_bytes,
aec_dump_queue_.get());
}

void AudioProcessingModule::detach_aec_dump() {
apm_->DetachAecDump();
aec_dump_queue_.reset();
}

} // namespace livekit
8 changes: 8 additions & 0 deletions webrtc-sys/src/apm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,14 @@ pub mod ffi {

fn set_stream_delay_ms(self: Pin<&mut AudioProcessingModule>, delay: i32) -> i32;

fn create_and_attach_aec_dump(
self: Pin<&mut AudioProcessingModule>,
file_name: &str,
max_log_size_bytes: i64,
) -> bool;

fn detach_aec_dump(self: Pin<&mut AudioProcessingModule>);

fn create_apm(
echo_canceller_enabled: bool,
gain_controller_enabled: bool,
Expand Down
Loading