Skip to content

Commit 102ebbb

Browse files
committed
Merge branch 'audio-mixer'
2 parents 48a7851 + 06d2f9d commit 102ebbb

File tree

10 files changed

+368
-4
lines changed

10 files changed

+368
-4
lines changed

Cargo.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

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::{apm, audio_resampler, frame_cryptor, yuv_helper};
69+
pub use crate::imp::{apm, audio_mixer, audio_resampler, frame_cryptor, yuv_helper};
7070
}
7171

7272
#[cfg(target_os = "android")]
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
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 crate::audio_frame::AudioFrame;
16+
use cxx::UniquePtr;
17+
use std::sync::Arc;
18+
use webrtc_sys::audio_mixer as sys;
19+
use webrtc_sys::audio_mixer::ffi;
20+
21+
pub struct AudioMixer {
22+
sys_handle: UniquePtr<ffi::AudioMixer>,
23+
}
24+
25+
pub use ffi::AudioFrameInfo;
26+
27+
pub trait AudioMixerSource {
28+
fn ssrc(&self) -> i32;
29+
fn preferred_sample_rate(&self) -> u32;
30+
fn get_audio_frame_with_info<'a>(&self, target_sample_rate: u32) -> Option<AudioFrame>;
31+
}
32+
33+
struct AudioMixerSourceImpl<T> {
34+
inner: T,
35+
}
36+
impl<T: AudioMixerSource> sys::AudioMixerSource for AudioMixerSourceImpl<T> {
37+
fn ssrc(&self) -> i32 {
38+
self.inner.ssrc()
39+
}
40+
41+
fn preferred_sample_rate(&self) -> i32 {
42+
self.inner.preferred_sample_rate() as i32
43+
}
44+
45+
fn get_audio_frame_with_info<'a>(
46+
&self,
47+
target_sample_rate: i32,
48+
native_frame: sys::NativeAudioFrame,
49+
) -> AudioFrameInfo {
50+
if let Some(frame) = self.inner.get_audio_frame_with_info(target_sample_rate as u32) {
51+
let samples_count = (frame.sample_rate as usize / 100) as usize;
52+
assert_eq!(
53+
frame.sample_rate, target_sample_rate as u32,
54+
"sample rate must match target_sample_rate"
55+
);
56+
assert_eq!(
57+
frame.samples_per_channel as usize, samples_count,
58+
"frame must contain 10ms of samples"
59+
);
60+
assert_eq!(
61+
frame.data.len(),
62+
samples_count * frame.num_channels as usize,
63+
"slice must contain 10ms of samples"
64+
);
65+
66+
unsafe {
67+
native_frame.update_frame(
68+
0,
69+
frame.data.as_ptr(),
70+
frame.samples_per_channel as usize,
71+
frame.sample_rate as i32,
72+
frame.num_channels as usize,
73+
);
74+
}
75+
return ffi::AudioFrameInfo::Normal;
76+
} else {
77+
return ffi::AudioFrameInfo::Muted;
78+
}
79+
}
80+
}
81+
82+
impl AudioMixer {
83+
pub fn new() -> Self {
84+
let sys_handle = ffi::create_audio_mixer();
85+
Self { sys_handle }
86+
}
87+
88+
pub fn add_source(&mut self, source: impl AudioMixerSource + 'static) {
89+
let source_impl = AudioMixerSourceImpl { inner: source };
90+
let wrapper = Box::new(sys::AudioMixerSourceWrapper::new(Arc::new(source_impl)));
91+
unsafe {
92+
self.sys_handle.pin_mut().add_source(wrapper);
93+
}
94+
}
95+
96+
pub fn remove_source(&mut self, ssrc: i32) {
97+
unsafe {
98+
self.sys_handle.pin_mut().remove_source(ssrc);
99+
}
100+
}
101+
102+
pub fn mix(&mut self, num_channels: usize) -> &[i16] {
103+
unsafe {
104+
let len = self.sys_handle.pin_mut().mix(num_channels);
105+
std::slice::from_raw_parts(self.sys_handle.data(), len)
106+
}
107+
}
108+
}

libwebrtc/src/native/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
pub mod android;
1717
pub mod apm;
1818
pub mod audio_resampler;
19+
pub mod audio_mixer;
1920
pub mod audio_source;
2021
pub mod audio_stream;
2122
pub mod audio_track;

webrtc-sys/build.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ fn main() {
4949
"src/android.rs",
5050
"src/prohibit_libsrtp_initialization.rs",
5151
"src/apm.rs",
52+
"src/audio_mixer.rs",
5253
]);
5354

5455
builder.files(&[
@@ -77,6 +78,7 @@ fn main() {
7778
"src/global_task_queue.cpp",
7879
"src/prohibit_libsrtp_initialization.cpp",
7980
"src/apm.cpp",
81+
"src/audio_mixer.cpp",
8082
]);
8183

8284
let webrtc_dir = webrtc_sys_build::webrtc_dir();
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
* Copyright 2023 LiveKit
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the “License”);
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an “AS IS” BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#pragma once
18+
19+
#include <memory>
20+
21+
#include "api/scoped_refptr.h"
22+
#include "api/audio/audio_mixer.h"
23+
#include "modules/audio_mixer/audio_mixer_impl.h"
24+
#include "modules/audio_processing/audio_buffer.h"
25+
26+
#include "rust/cxx.h"
27+
28+
namespace livekit {
29+
class AudioMixer;
30+
class NativeAudioFrame;
31+
} // namespace livekit
32+
33+
#include "webrtc-sys/src/audio_mixer.rs.h"
34+
35+
namespace livekit {
36+
37+
class NativeAudioFrame {
38+
public:
39+
NativeAudioFrame(webrtc::AudioFrame* frame) : frame_(frame) {}
40+
void update_frame(uint32_t timestamp, const int16_t* data, size_t samples_per_channel, int sample_rate_hz, size_t num_channels);
41+
private:
42+
webrtc::AudioFrame* frame_;
43+
};
44+
45+
class AudioMixerSource: public webrtc::AudioMixer::Source {
46+
public:
47+
AudioMixerSource(rust::Box<AudioMixerSourceWrapper> source);
48+
49+
AudioFrameInfo GetAudioFrameWithInfo(int sample_rate_hz, webrtc::AudioFrame* audio_frame) override;
50+
51+
int Ssrc() const override;
52+
53+
int PreferredSampleRate() const override;
54+
55+
~AudioMixerSource() {}
56+
57+
private:
58+
rust::Box<AudioMixerSourceWrapper> source_;
59+
};
60+
61+
class AudioMixer {
62+
public:
63+
AudioMixer();
64+
65+
void add_source(rust::Box<AudioMixerSourceWrapper> source);
66+
67+
void remove_source(int ssrc);
68+
69+
size_t mix(size_t num_channels);
70+
const int16_t* data() const;
71+
72+
private:
73+
webrtc::AudioFrame frame_;
74+
std::vector<std::shared_ptr<AudioMixerSource>> sources_;
75+
rtc::scoped_refptr<webrtc::AudioMixer> audio_mixer_;
76+
};
77+
78+
std::unique_ptr<AudioMixer> create_audio_mixer();
79+
80+
81+
} // namespace livekit

webrtc-sys/src/audio_mixer.cpp

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
#include "livekit/audio_mixer.h"
2+
#include "api/audio/audio_frame.h"
3+
#include "api/audio/audio_mixer.h"
4+
#include "modules/audio_mixer/audio_mixer_impl.h"
5+
#include "webrtc-sys/src/audio_mixer.rs.h"
6+
7+
#include <memory>
8+
#include <iostream>
9+
10+
namespace livekit {
11+
12+
AudioMixer::AudioMixer() {
13+
audio_mixer_ = webrtc::AudioMixerImpl::Create();
14+
}
15+
16+
void AudioMixer::add_source(rust::Box<AudioMixerSourceWrapper> source) {
17+
auto native_source = std::make_shared<AudioMixerSource>(std::move(source));
18+
19+
audio_mixer_->AddSource(native_source.get());
20+
sources_.push_back(native_source);
21+
}
22+
23+
void AudioMixer::remove_source(int source_ssrc) {
24+
auto it = std::find_if(sources_.begin(), sources_.end(),
25+
[source_ssrc](const auto& s) { return s->Ssrc() == source_ssrc; });
26+
27+
if (it != sources_.end()) {
28+
audio_mixer_->RemoveSource(it->get());
29+
sources_.erase(it);
30+
}
31+
}
32+
33+
size_t AudioMixer::mix(size_t number_of_channels) {
34+
audio_mixer_->Mix(number_of_channels, &frame_);
35+
return frame_.num_channels() * frame_.samples_per_channel() * sizeof(int16_t);
36+
}
37+
38+
const int16_t* AudioMixer::data() const {
39+
return frame_.data();
40+
}
41+
42+
std::unique_ptr<AudioMixer> create_audio_mixer() {
43+
return std::make_unique<AudioMixer>();
44+
}
45+
46+
AudioMixerSource::AudioMixerSource(rust::Box<AudioMixerSourceWrapper> source) : source_(std::move(source)) {
47+
}
48+
49+
int AudioMixerSource::Ssrc() const {
50+
return source_->ssrc();
51+
}
52+
53+
int AudioMixerSource::PreferredSampleRate() const {
54+
return source_->preferred_sample_rate();
55+
}
56+
57+
webrtc::AudioMixer::Source::AudioFrameInfo AudioMixerSource::GetAudioFrameWithInfo(int sample_rate, webrtc::AudioFrame* audio_frame) {
58+
NativeAudioFrame frame(audio_frame);
59+
60+
livekit::AudioFrameInfo result = source_->get_audio_frame_with_info(sample_rate, frame);
61+
62+
if (result == livekit::AudioFrameInfo::Normal) {
63+
return webrtc::AudioMixer::Source::AudioFrameInfo::kNormal;
64+
} else if (result == livekit::AudioFrameInfo::Muted) {
65+
return webrtc::AudioMixer::Source::AudioFrameInfo::kMuted;
66+
} else {
67+
return webrtc::AudioMixer::Source::AudioFrameInfo::kError;
68+
}
69+
}
70+
71+
void NativeAudioFrame::update_frame(uint32_t timestamp, const int16_t* data,
72+
size_t samples_per_channel, int sample_rate_hz, size_t num_channels) {
73+
frame_->UpdateFrame(timestamp, data, samples_per_channel, sample_rate_hz,
74+
webrtc::AudioFrame::SpeechType::kNormalSpeech, webrtc::AudioFrame::VADActivity::kVadUnknown,
75+
num_channels);
76+
}
77+
78+
} // namespace livekit

0 commit comments

Comments
 (0)