Initial Commit

This commit is contained in:
CanadianBaconBoi 2025-07-13 13:20:13 +02:00
commit db5568c12c
8 changed files with 4152 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target

8
.idea/.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

8
.idea/modules.xml Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/rvoip-testing.iml" filepath="$PROJECT_DIR$/.idea/rvoip-testing.iml" />
</modules>
</component>
</project>

11
.idea/rvoip-testing.iml Normal file
View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="EMPTY_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

7
.idea/vcs.xml Normal file
View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
<mapping directory="/mnt/data/User Library/Documents/Projects/Rust/rvoip-testing" vcs="Git" />
</component>
</project>

3938
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

14
Cargo.toml Normal file
View File

@ -0,0 +1,14 @@
[package]
name = "rvoip-testing"
version = "0.1.0"
edition = "2024"
[dependencies]
cpal = "0.16.0"
rvoip-session-core = "0.1.12"
rvoip-media-core = {version = "0.1.12", features = ["all-codecs"]}
rvoip-rtp-core = "0.1.12"
tokio = { version = "1.46.1", features = ["rt", "rt-multi-thread", "macros"] }
anyhow = "1.0.98"
tracing-subscriber = "0.3.19"
futures = "0.3.31"

165
src/main.rs Normal file
View File

@ -0,0 +1,165 @@
use std::cmp::min;
use std::sync::Arc;
use cpal::{InputCallbackInfo, OutputCallbackInfo, StreamConfig, StreamInstant};
use rvoip_media_core::prelude::*;
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
use rvoip_media_core::codec::AudioCodec;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
tracing_subscriber::fmt::init();
let the_beginning: StreamInstant = StreamInstant::new(0, 0);
let buffer_size = 32;
let opus_buffer: Arc<RingBuffer<Vec<u8>>> = Arc::new(RingBuffer::new(buffer_size).expect("Failed to create ring buffer"));
for _ in 0..(buffer_size/4)*3 {
opus_buffer.push(vec![0; 128]).await.expect("Failed to push to ring buffer");
}
let input_opus_codec = OpusCodec::new(SampleRate::Rate48000, 1, OpusConfig {
bitrate: 100000,
complexity: 4,
vbr: false,
application: OpusApplication::Voip,
frame_size_ms: 10.0,
}).expect("Failed to create Opus codec");
let output_opus_codec = OpusCodec::new(SampleRate::Rate48000, 1, OpusConfig {
bitrate: 100000,
complexity: 4,
vbr: false,
application: OpusApplication::Voip,
frame_size_ms: 10.0,
}).expect("Failed to create Opus codec");
let host = cpal::default_host();
let input_device = host.default_input_device().unwrap();
let input_device_config: StreamConfig = input_device.default_input_config().expect("Failed to get input config").into();
let output_device = host.default_output_device().unwrap();
let output_device_config: StreamConfig = output_device.default_output_config().expect("Failed to get output config").into();
let input_fn = {
let bufbuf = opus_buffer.clone();
let mut opus_codec = input_opus_codec;
let opus_info = opus_codec.get_info();
let sample_frame_size = (opus_info.sample_rate as usize/100)*(opus_info.channels as usize);
move |input_data: &[i16], icbi: &InputCallbackInfo| {
futures::executor::block_on(async {
let mut idx = 0;
while idx < input_data.len() {
let data_to_encode = if idx + sample_frame_size > input_data.len() {
let mut buffer = Vec::with_capacity(sample_frame_size);
buffer.extend_from_slice(&input_data[idx..]);
for _ in 0..(sample_frame_size - buffer.len()) {
buffer.push(0);
}
buffer
} else {
input_data[idx..min(idx + sample_frame_size, input_data.len())].to_vec()
};
let frame = AudioFrame::new(
data_to_encode,
input_device_config.sample_rate.0,
input_device_config.channels as u8,
icbi.timestamp().capture.duration_since(&the_beginning)
.expect("Failed to get timestamp").as_millis() as u32,
);
idx += sample_frame_size;
match opus_codec.encode(
&frame
) {
Ok(encoded_frame) => {
let _ = bufbuf.push(encoded_frame).await;
}
Err(e) => {
eprintln!("Error while encoding: {:?}", e);
}
}
}
});
}
};
let output_fn = {
let opus_buffer = opus_buffer.clone();
let mut opus_codec = output_opus_codec;
let opus_info = opus_codec.get_info();
let leftover_buffer: RingBuffer<Vec<i16>> = RingBuffer::new(128).expect("Failed to create ring buffer");
move |output_data: &mut [i16], ocbi: &OutputCallbackInfo| {
futures::executor::block_on(async {
let mut idx = 0;
let out_len = output_data.len();
while idx < out_len {
loop {
match leftover_buffer.peek().await {
Ok(data) => {
output_data[idx..idx+data.len()].copy_from_slice(
&(data)
);
idx += data.len();
leftover_buffer.pop().await.expect("Failed to pop from ring buffer");
}
Err(e) => break
}
}
match opus_buffer.peek().await {
Ok(data) => {
let frame = opus_codec.decode(&data).expect("Failed to decode");
if idx+frame.samples.len() > out_len {
output_data[idx..out_len].copy_from_slice(
&(frame.samples[0..out_len-idx])
);
let leftover = frame.samples[out_len-idx..].to_vec();
leftover_buffer.push(leftover).await.expect("Failed to push to ring buffer");
break;
}
output_data[idx..idx+frame.samples.len()].copy_from_slice(
&(frame.samples)
);
idx += frame.samples.len();
opus_buffer.pop().await.expect("Failed to pop from ring buffer");
}
Err(e) => {
eprintln!("Error while popping from ring buffer: {:?}", e);
}
}
}
})
}
};
let input_stream = input_device.build_input_stream(
&input_device.default_input_config().expect("Failed to get input config").into(),
input_fn, err_fn, None
).expect("Failed to build input stream");
let output_stream = output_device.build_output_stream(
&output_device.default_output_config().expect("Failed to get output config").into(),
output_fn, err_fn, None
).expect("Failed to build output stream");
input_stream.play().expect("Failed to play input stream");
output_stream.play().expect("Failed to play output stream");
tokio::time::sleep(std::time::Duration::from_secs(10)).await;
Ok(())
}
fn err_fn(err: cpal::StreamError) {
eprintln!("Error: {:?}", err);
}