diff --git a/video-daemon/Cargo.lock b/video-daemon/Cargo.lock index dab49ed..b1de4cb 100644 --- a/video-daemon/Cargo.lock +++ b/video-daemon/Cargo.lock @@ -26,6 +26,28 @@ dependencies = [ "memchr", ] +[[package]] +name = "alsa" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2562ad8dcf0f789f65c6fdaad8a8a9708ed6b488e649da28c01656ad66b8b47" +dependencies = [ + "alsa-sys", + "bitflags 1.3.2", + "libc", + "nix", +] + +[[package]] +name = "alsa-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db8fee663d06c4e303404ef5f40488a53e062f89ba8bfed81f42325aafad1527" +dependencies = [ + "libc", + "pkg-config", +] + [[package]] name = "ansi_term" version = "0.12.1" @@ -112,6 +134,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "audiopus_sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62314a1546a2064e033665d658e88c620a62904be945f8147e6b16c3db9f8651" +dependencies = [ + "cmake", + "log", + "pkg-config", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -194,6 +227,26 @@ dependencies = [ "which 4.4.2", ] +[[package]] +name = "bindgen" +version = "0.69.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ffcebc3849946a7170a05992aac39da343a90676ab392c51a4280981d6379c2" +dependencies = [ + "bitflags 2.4.1", + "cexpr 0.6.0", + "clang-sys", + "lazy_static", + "lazycell", + "peeking_take_while", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex 1.2.0", + "syn 2.0.41", +] + [[package]] name = "bit_field" version = "0.10.2" @@ -269,6 +322,12 @@ dependencies = [ "libc", ] +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + [[package]] name = "cexpr" version = "0.4.0" @@ -365,6 +424,15 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" +[[package]] +name = "cmake" +version = "0.1.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130" +dependencies = [ + "cc", +] + [[package]] name = "cocoa" version = "0.20.2" @@ -406,6 +474,16 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +[[package]] +name = "combine" +version = "4.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" +dependencies = [ + "bytes", + "memchr", +] + [[package]] name = "console_error_panic_hook" version = "0.1.7" @@ -496,6 +574,51 @@ dependencies = [ "objc", ] +[[package]] +name = "coreaudio-rs" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "321077172d79c662f64f5071a03120748d5bb652f5231570141be24cfcd2bace" +dependencies = [ + "bitflags 1.3.2", + "core-foundation-sys 0.8.6", + "coreaudio-sys", +] + +[[package]] +name = "coreaudio-sys" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3120ebb80a9de008e638ad833d4127d50ea3d3a960ea23ea69bc66d9358a028" +dependencies = [ + "bindgen 0.69.1", +] + +[[package]] +name = "cpal" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d959d90e938c5493000514b446987c07aed46c668faaa7d34d6c7a67b1a578c" +dependencies = [ + "alsa", + "core-foundation-sys 0.8.6", + "coreaudio-rs", + "dasp_sample", + "jni 0.19.0", + "js-sys", + "libc", + "mach2", + "ndk", + "ndk-context", + "oboe", + "once_cell", + "parking_lot", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows 0.46.0", +] + [[package]] name = "crc32fast" version = "1.3.2" @@ -553,6 +676,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +[[package]] +name = "dasp_sample" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c87e182de0887fd5361989c677c4e8f5000cd9491d6d563161a8f3a5519fc7f" + [[package]] name = "deranged" version = "0.3.10" @@ -618,6 +747,12 @@ dependencies = [ "termcolor", ] +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "errno" version = "0.3.8" @@ -1040,6 +1175,12 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + [[package]] name = "heck" version = "0.4.1" @@ -1122,7 +1263,7 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfd6201e7c30ccb24773cac7efa6fec1e06189d414b7439ce756a481c8bfbf53" dependencies = [ - "indexmap", + "indexmap 1.9.3", ] [[package]] @@ -1132,7 +1273,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.12.3", +] + +[[package]] +name = "indexmap" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +dependencies = [ + "equivalent", + "hashbrown 0.14.3", ] [[package]] @@ -1141,6 +1292,40 @@ version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +[[package]] +name = "jni" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6df18c2e3db7e453d3c6ac5b3e9d5182664d28788126d39b91f2d1e22b017ec" +dependencies = [ + "cesu8", + "combine", + "jni-sys", + "log", + "thiserror", + "walkdir", +] + +[[package]] +name = "jni" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "039022cdf4d7b1cf548d31f60ae783138e5fd42013f6271049d7df7afadef96c" +dependencies = [ + "cesu8", + "combine", + "jni-sys", + "log", + "thiserror", + "walkdir", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + [[package]] name = "jobserver" version = "0.1.27" @@ -1235,6 +1420,15 @@ version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +[[package]] +name = "mach2" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709" +dependencies = [ + "libc", +] + [[package]] name = "malloc_buf" version = "0.0.6" @@ -1349,6 +1543,46 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe4d98d0065f4b1daf164b3eafb11974c94662e5e2396cf03f32d0bb5c17da51" +[[package]] +name = "ndk" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0" +dependencies = [ + "bitflags 1.3.2", + "jni-sys", + "ndk-sys", + "num_enum", + "raw-window-handle", + "thiserror", +] + +[[package]] +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + +[[package]] +name = "ndk-sys" +version = "0.4.1+23.1.7779620" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cf2aae958bd232cac5069850591667ad422d263686d75b52a065f9badeee5a3" +dependencies = [ + "jni-sys", +] + +[[package]] +name = "nix" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" +dependencies = [ + "bitflags 1.3.2", + "cfg-if 1.0.0", + "libc", +] + [[package]] name = "nokhwa" version = "0.10.4" @@ -1400,7 +1634,7 @@ checksum = "a9b9bae36d0ce413568cefb94c43748e80f9ae97aa0a9e2d8e18f0275a909746" dependencies = [ "nokhwa-core", "once_cell", - "windows", + "windows 0.43.0", ] [[package]] @@ -1445,6 +1679,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-derive" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "num-integer" version = "0.1.45" @@ -1485,6 +1730,27 @@ dependencies = [ "libc", ] +[[package]] +name = "num_enum" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "num_threads" version = "0.1.6" @@ -1522,6 +1788,29 @@ dependencies = [ "memchr", ] +[[package]] +name = "oboe" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8868cc237ee02e2d9618539a23a8d228b9bb3fc2e7a5b11eed3831de77c395d0" +dependencies = [ + "jni 0.20.0", + "ndk", + "ndk-context", + "num-derive", + "num-traits", + "oboe-sys", +] + +[[package]] +name = "oboe-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f44155e7fb718d3cfddcf70690b2b51ac4412f347cd9e4fbe511abe9cd7b5f2" +dependencies = [ + "cc", +] + [[package]] name = "once_cell" version = "1.19.0" @@ -1534,6 +1823,16 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +[[package]] +name = "opus" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6526409b274a7e98e55ff59d96aafd38e6cd34d46b7dbbc32ce126dffcd75e8e" +dependencies = [ + "audiopus_sys", + "libc", +] + [[package]] name = "overload" version = "0.1.1" @@ -1675,6 +1974,16 @@ dependencies = [ "syn 2.0.41", ] +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -1841,6 +2150,12 @@ dependencies = [ "getrandom", ] +[[package]] +name = "raw-window-handle" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" + [[package]] name = "rayon" version = "1.8.0" @@ -2043,6 +2358,15 @@ version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "schannel" version = "0.1.22" @@ -2401,6 +2725,23 @@ dependencies = [ "tokio", ] +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap 2.1.0", + "toml_datetime", + "winnow", +] + [[package]] name = "tracing" version = "0.1.40" @@ -2576,11 +2917,13 @@ dependencies = [ "base64", "bus", "clap 4.4.11", + "cpal", "directories-next", "env-libvpx-sys", "futures-util", "image", "nokhwa", + "opus", "protobuf", "quinn", "rayon", @@ -2597,6 +2940,16 @@ dependencies = [ "yuv", ] +[[package]] +name = "walkdir" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -2758,6 +3111,15 @@ dependencies = [ "windows_x86_64_msvc 0.42.2", ] +[[package]] +name = "windows" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdacb41e6a96a052c6cb63a144f24900236121c6f63f4f8219fef5977ecb0c25" +dependencies = [ + "windows-targets 0.42.2", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -2776,6 +3138,21 @@ dependencies = [ "windows-targets 0.52.0", ] +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + [[package]] name = "windows-targets" version = "0.48.5" @@ -2932,6 +3309,15 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +[[package]] +name = "winnow" +version = "0.5.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b5c3db89721d50d0e2a673f5043fc4722f76dcc352d7b1ab8b8288bed4ed2c5" +dependencies = [ + "memchr", +] + [[package]] name = "yew" version = "0.20.0" @@ -2942,7 +3328,7 @@ dependencies = [ "futures", "gloo", "implicit-clone", - "indexmap", + "indexmap 1.9.3", "js-sys", "prokio", "rustversion", diff --git a/video-daemon/Cargo.toml b/video-daemon/Cargo.toml index 6b630ef..64a3966 100644 --- a/video-daemon/Cargo.toml +++ b/video-daemon/Cargo.toml @@ -29,3 +29,5 @@ types = { path= "../types"} yuv = "0.1.5" rayon = "1.8.0" thiserror = "1.0.51" +cpal = "0.15.2" +opus = "0.3.0" diff --git a/video-daemon/README.md b/video-daemon/README.md index 8c339c8..7a19a74 100644 --- a/video-daemon/README.md +++ b/video-daemon/README.md @@ -5,7 +5,7 @@ ### Dependencies ```sh -sudo apt install build-essential pkg-config libclang-dev libvpx-dev +sudo apt install build-essential pkg-config libclang-dev libvpx-dev libasound2-dev cmake ``` ## Running locally diff --git a/video-daemon/src/lib.rs b/video-daemon/src/lib.rs index f979d15..2b2ef01 100644 --- a/video-daemon/src/lib.rs +++ b/video-daemon/src/lib.rs @@ -1,5 +1,6 @@ pub mod camera; pub mod fake_cert_verifier; +pub mod microphone; pub mod quic; pub mod video_encoder; pub mod yuyv_format; diff --git a/video-daemon/src/main.rs b/video-daemon/src/main.rs index 57d0a2a..fa4b2ec 100644 --- a/video-daemon/src/main.rs +++ b/video-daemon/src/main.rs @@ -2,6 +2,7 @@ use clap::Parser; use tokio::sync::mpsc::channel; use video_daemon::camera::{CameraConfig, CameraDaemon}; +use video_daemon::microphone::MicrophoneDaemon; use video_daemon::quic::{Client, ClientError, Opt}; #[tokio::main] @@ -15,6 +16,7 @@ async fn main() { let opt = Opt::parse(); let user_id = opt.user_id.clone(); let video_device_index = opt.video_device_index; + let audio_device = opt.audio_device.clone(); let mut client = Client::new(opt).expect("failed to create client"); client.connect().await.expect("failed to connect"); @@ -26,8 +28,10 @@ async fn main() { video_device_index, }; let (quic_tx, mut quic_rx) = channel::>(10); - let mut camera = CameraDaemon::from_config(camera_config, user_id, quic_tx); + let mut camera = CameraDaemon::from_config(camera_config, user_id.clone(), quic_tx.clone()); camera.start().expect("failed to start camera"); + let mut microphone = MicrophoneDaemon::default(); + microphone.start(quic_tx, audio_device, user_id).expect("failed to start microphone"); while let Some(data) = quic_rx.recv().await { if let Err(e) = client.send(data).await { match e { diff --git a/video-daemon/src/microphone.rs b/video-daemon/src/microphone.rs new file mode 100644 index 0000000..dcd8c9e --- /dev/null +++ b/video-daemon/src/microphone.rs @@ -0,0 +1,171 @@ +use cpal::SampleRate; +use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; +use opus::Channels; +use protobuf::{MessageField, Message}; +use tokio::sync::mpsc::Sender; +use std::sync::Arc; +use std::sync::atomic::AtomicBool; +use std::thread::JoinHandle; +use std::time::Duration; +use tracing::{info, error}; +use types::protos::media_packet::{MediaPacket, VideoMetadata}; +use types::protos::media_packet::media_packet::MediaType; + +pub struct MicrophoneDaemon { + stop: Arc, + handles: Vec>> +} + +impl Default for MicrophoneDaemon { + fn default() -> Self { + Self::new() + } +} + +impl MicrophoneDaemon { + pub fn new() -> Self { + Self { + stop: Arc::new(AtomicBool::new(false)), + handles: vec![] + } + } + + pub fn start(&mut self, quic_tx: Sender>, device: String, email: String) -> anyhow::Result<()> { + self.handles.push(start_microphone(device.clone(), quic_tx.clone(), email, self.stop.clone())?); + Ok(()) + } + + pub fn stop(&mut self) { + self.stop.store(true, std::sync::atomic::Ordering::Relaxed); + for handle in self.handles.drain(..) { + if let Err(e) = handle.join() { + error!("Failed to join microphone thread: {:?}", e); + } + } + } +} + +fn start_microphone(device: String, quic_tx: Sender>, email: String, stop: Arc) -> anyhow::Result>> { + let host = cpal::default_host(); + + // Set up the input device and stream with the default input config. + let device = if device == "default" { + host.default_input_device() + } else { + host.input_devices()? + .find(|x| x.name().map(|y| y == device).unwrap_or(false)) + } + .expect("failed to find input device"); + + info!("Input device: {}", device.name()?); + + let mut config = device.default_input_config()?; + info!("Default input config: {:?}", config); + // Opus only supports 48kHz sample rate so find a compatible config. + for supported_config in device.supported_input_configs()?.filter_map(|x| Some(x.with_sample_rate(SampleRate(48000)))) { + if supported_config.channels() != 2 { + continue; + } + info!("Supported input config: {:?}", supported_config); + if supported_config.sample_format() == cpal::SampleFormat::F32 || supported_config.sample_format() == cpal::SampleFormat::I16 { + info!("Using supported input config: {:?}", supported_config); + config = supported_config; + break; + } + } + + let mut encoder = opus::Encoder::new(config.sample_rate().0, Channels::Stereo, opus::Application::Voip)?; + + info!("Opus encoder created {:?}", encoder); + + let err_fn = move |err| { + error!("an error occurred on stream: {}", err); + }; + + Ok(std::thread::spawn(move || { + let stream = match config.sample_format() { + cpal::SampleFormat::I16 => device.build_input_stream( + &config.into(), + move |data, _: &_| { + // Chunk data into 1024 samples. + for chunk in data.chunks_exact(960) { + match encode_and_send_i16(chunk, &mut encoder, &quic_tx, email.clone()) { + Ok(_) => {} + Err(e) => { + error!("Failed to encode and send audio: {}", e); + } + } + } + }, + err_fn, + None, + )?, + cpal::SampleFormat::F32 => device.build_input_stream( + &config.into(), + move |data, _: &_| { + for chunk in data.chunks_exact(960) { + match encode_and_send_f32(chunk, &mut encoder, &quic_tx, email.clone()) { + Ok(_) => {} + Err(e) => { + error!("Failed to encode and send audio: {}", e); + } + } + } + }, + err_fn, + None, + )?, + sample_format => { + return Err(anyhow::Error::msg(format!( + "Unsupported sample format '{sample_format}'" + ))) + } + }; + info!("Begin streaming audio..."); + stream.play().expect("failed to play stream"); + + loop { + if stop.load(std::sync::atomic::Ordering::Relaxed) { + break; + } + std::thread::sleep(Duration::from_secs(1)); + } + Ok(()) + })) +} + +fn encode_and_send_i16(input: &[i16], encoder: &mut opus::Encoder, quic_tx: &Sender>, email: String) -> anyhow::Result<()> +{ + println!("Encoding {} samples", input.len()); + let output = encoder.encode_vec( input, 1024)?; + let output = convert_to_media_packet(output, email, 0); + let output = output.write_to_bytes()?; + quic_tx.try_send(output)?; + Ok(()) +} + +fn encode_and_send_f32(input: &[f32], encoder: &mut opus::Encoder, quic_tx: &Sender>, email: String) -> anyhow::Result<()> +{ + let output = encoder.encode_vec_float(input, 1024)?; + let output = convert_to_media_packet(output, email, 0); + let output = output.write_to_bytes()?; + quic_tx.try_send(output)?; + Ok(()) +} + +fn convert_to_media_packet(data: Vec, email: String, sequence: u64) -> MediaPacket { + MediaPacket { + media_type: MediaType::AUDIO.into(), + data, + email, + // TODO: Do we need to include the timestamp? + timestamp: 0.0, + // TODO: Do we need to include the duration? + duration: 0.0, + video_metadata: MessageField(Some(Box::new(VideoMetadata { + sequence, + ..Default::default() + }))), + ..Default::default() + } +} \ No newline at end of file diff --git a/video-daemon/src/quic.rs b/video-daemon/src/quic.rs index f19ae07..19083df 100644 --- a/video-daemon/src/quic.rs +++ b/video-daemon/src/quic.rs @@ -122,6 +122,9 @@ pub struct Opt { #[clap(long = "video-device-index")] pub video_device_index: usize, + + #[clap(long = "audio-device", default_value = "default")] + pub audio_device: String, } pub struct Client {