Initial Commit
This commit is contained in:
commit
db5568c12c
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/target
|
||||
8
.idea/.gitignore
vendored
Normal file
8
.idea/.gitignore
vendored
Normal 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
8
.idea/modules.xml
Normal 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
11
.idea/rvoip-testing.iml
Normal 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
7
.idea/vcs.xml
Normal 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
3938
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
14
Cargo.toml
Normal file
14
Cargo.toml
Normal 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
165
src/main.rs
Normal 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);
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user