change linux audio playback from cpal to pulse, because stupid cpal 100% cpu usage problem

This commit is contained in:
rustdesk 2022-04-23 02:17:33 +08:00
parent aa7779bcf7
commit 5bf8e8f001
4 changed files with 293 additions and 358 deletions

537
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -47,8 +47,8 @@ base64 = "0.13"
sysinfo = "0.23" sysinfo = "0.23"
num_cpus = "1.13" num_cpus = "1.13"
[target.'cfg(not(any(target_os = "android")))'.dependencies] [target.'cfg(not(any(target_os = "android", target_os = "linux")))'.dependencies]
cpal = { git = "https://github.com/open-trade/cpal" } cpal = "0.13.5"
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies] [target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
machine-uid = "0.2" machine-uid = "0.2"
@ -75,8 +75,8 @@ core-graphics = "0.22"
notify = "4.0.17" notify = "4.0.17"
include_dir = "0.7.2" include_dir = "0.7.2"
[target.'cfg(target_os = "linux")'.dependencies] [target.'cfg(target_os = "linux")'.dependencies]
libpulse-simple-binding = "2.24" psimple = { package = "libpulse-simple-binding", version = "2.25" }
libpulse-binding = "2.25" pulse = { package = "libpulse-binding", version = "2.26" }
rust-pulsectl = { git = "https://github.com/open-trade/pulsectl" } rust-pulsectl = { git = "https://github.com/open-trade/pulsectl" }
[target.'cfg(target_os = "android")'.dependencies] [target.'cfg(target_os = "android")'.dependencies]

View File

@ -1,5 +1,5 @@
pub use async_trait::async_trait; pub use async_trait::async_trait;
#[cfg(not(any(target_os = "android")))] #[cfg(not(any(target_os = "android", target_os = "linux")))]
use cpal::{ use cpal::{
traits::{DeviceTrait, HostTrait, StreamTrait}, traits::{DeviceTrait, HostTrait, StreamTrait},
Device, Host, StreamConfig, Device, Host, StreamConfig,
@ -26,7 +26,7 @@ use std::{
collections::HashMap, collections::HashMap,
net::SocketAddr, net::SocketAddr,
ops::Deref, ops::Deref,
sync::{mpsc, Arc, Mutex, RwLock}, sync::{mpsc, Arc, RwLock},
}; };
use uuid::Uuid; use uuid::Uuid;
@ -36,7 +36,7 @@ pub struct Client;
pub use super::lang::*; pub use super::lang::*;
#[cfg(not(any(target_os = "android")))] #[cfg(not(any(target_os = "android", target_os = "linux")))]
lazy_static::lazy_static! { lazy_static::lazy_static! {
static ref AUDIO_HOST: Host = cpal::default_host(); static ref AUDIO_HOST: Host = cpal::default_host();
} }
@ -45,7 +45,6 @@ cfg_if::cfg_if! {
if #[cfg(target_os = "android")] { if #[cfg(target_os = "android")] {
use libc::{c_float, c_int, c_void}; use libc::{c_float, c_int, c_void};
use std::cell::RefCell;
type Oboe = *mut c_void; type Oboe = *mut c_void;
extern "C" { extern "C" {
fn create_oboe_player(channels: c_int, sample_rate: c_int) -> Oboe; fn create_oboe_player(channels: c_int, sample_rate: c_int) -> Oboe;
@ -472,24 +471,59 @@ impl Client {
#[derive(Default)] #[derive(Default)]
pub struct AudioHandler { pub struct AudioHandler {
audio_decoder: Option<(AudioDecoder, Vec<f32>)>, audio_decoder: Option<(AudioDecoder, Vec<f32>)>,
#[cfg(any(target_os = "android"))] #[cfg(target_os = "android")]
oboe: RefCell<OboePlayer>, oboe: Option<OboePlayer>,
#[cfg(not(any(target_os = "android")))] #[cfg(target_os = "linux")]
audio_buffer: Arc<Mutex<std::collections::vec_deque::VecDeque<f32>>>, simple: Option<psimple::Simple>,
#[cfg(not(any(target_os = "android", target_os = "linux")))]
audio_buffer: Arc<std::sync::Mutex<std::collections::vec_deque::VecDeque<f32>>>,
sample_rate: (u32, u32), sample_rate: (u32, u32),
#[cfg(not(any(target_os = "android")))] #[cfg(not(any(target_os = "android", target_os = "linux")))]
audio_stream: Option<Box<dyn StreamTrait>>, audio_stream: Option<Box<dyn StreamTrait>>,
channels: u16, channels: u16,
} }
impl AudioHandler { impl AudioHandler {
#[cfg(any(target_os = "android"))] #[cfg(target_os = "linux")]
fn start_audio(&mut self, format0: AudioFormat) -> ResultType<()> { fn start_audio(&mut self, format0: AudioFormat) -> ResultType<()> {
use psimple::Simple;
use pulse::sample::{Format, Spec};
use pulse::stream::Direction;
let spec = Spec {
format: Format::F32le,
channels: format0.channels as _,
rate: format0.sample_rate as _,
};
if !spec.is_valid() {
bail!("Invalid audio format");
}
self.simple = Some(Simple::new(
None, // Use the default server
&crate::get_app_name(), // Our applications name
Direction::Playback, // We want a playback stream
None, // Use the default device
&crate::get_app_name(), // Description of our stream
&spec, // Our sample format
None, // Use default channel map
None, // Use default buffering attributes
)?);
self.sample_rate = (format0.sample_rate, format0.sample_rate); self.sample_rate = (format0.sample_rate, format0.sample_rate);
Ok(()) Ok(())
} }
#[cfg(not(any(target_os = "android")))] #[cfg(target_os = "android")]
fn start_audio(&mut self, format0: AudioFormat) -> ResultType<()> {
self.oboe = Some(OboePlayer::new(
format0.channels as _,
format0.sample_rate as _,
));
self.sample_rate = (format0.sample_rate, format0.sample_rate);
Ok(())
}
#[cfg(not(any(target_os = "android", target_os = "linux")))]
fn start_audio(&mut self, format0: AudioFormat) -> ResultType<()> { fn start_audio(&mut self, format0: AudioFormat) -> ResultType<()> {
let device = AUDIO_HOST let device = AUDIO_HOST
.default_output_device() .default_output_device()
@ -528,35 +562,31 @@ impl AudioHandler {
} }
pub fn handle_frame(&mut self, frame: AudioFrame) { pub fn handle_frame(&mut self, frame: AudioFrame) {
#[cfg(not(any(target_os = "android")))] #[cfg(not(any(target_os = "android", target_os = "linux")))]
if self.audio_stream.is_none() { if self.audio_stream.is_none() {
return; return;
} }
let sample_rate0 = self.sample_rate.0; #[cfg(target_os = "linux")]
let sample_rate = self.sample_rate.1; if self.simple.is_none() {
let channels = self.channels; return;
cfg_if::cfg_if! {
if #[cfg(not(target_os = "android"))] {
let audio_buffer = self.audio_buffer.clone();
// avoiding memory overflow if audio_buffer consumer side has problem
if audio_buffer.lock().unwrap().len() as u32 > sample_rate * 120 {
*audio_buffer.lock().unwrap() = Default::default();
}
} else {
if self.oboe.borrow().is_null() {
self.oboe = RefCell::new(OboePlayer::new(
channels as _,
sample_rate0 as _,
));
}
let mut oboe = self.oboe.borrow_mut();
} }
#[cfg(target_os = "android")]
if self.oboe.is_none() {
return;
} }
self.audio_decoder.as_mut().map(|(d, buffer)| { self.audio_decoder.as_mut().map(|(d, buffer)| {
if let Ok(n) = d.decode_float(&frame.data, buffer, false) { if let Ok(n) = d.decode_float(&frame.data, buffer, false) {
let channels = self.channels;
let n = n * (channels as usize); let n = n * (channels as usize);
#[cfg(not(any(target_os = "android")))] #[cfg(not(any(target_os = "android", target_os = "linux")))]
{ {
let sample_rate0 = self.sample_rate.0;
let sample_rate = self.sample_rate.1;
let audio_buffer = self.audio_buffer.clone();
// avoiding memory overflow if audio_buffer consumer side has problem
if audio_buffer.lock().unwrap().len() as u32 > sample_rate * 120 {
*audio_buffer.lock().unwrap() = Default::default();
}
if sample_rate != sample_rate0 { if sample_rate != sample_rate0 {
let buffer = crate::resample_channels( let buffer = crate::resample_channels(
&buffer[0..n], &buffer[0..n],
@ -572,15 +602,21 @@ impl AudioHandler {
.extend(buffer[0..n].iter().cloned()); .extend(buffer[0..n].iter().cloned());
} }
} }
#[cfg(any(target_os = "android"))] #[cfg(target_os = "android")]
{ {
oboe.push(&buffer[0..n]); self.oboe.as_mut().map(|x| x.push(&buffer[0..n]));
}
#[cfg(target_os = "linux")]
{
let data_u8 =
unsafe { std::slice::from_raw_parts::<u8>(buffer.as_ptr() as _, n * 4) };
self.simple.as_mut().map(|x| x.write(data_u8));
} }
} }
}); });
} }
#[cfg(not(any(target_os = "android")))] #[cfg(not(any(target_os = "android", target_os = "linux")))]
fn build_output_stream<T: cpal::Sample>( fn build_output_stream<T: cpal::Sample>(
&mut self, &mut self,
config: &StreamConfig, config: &StreamConfig,

View File

@ -429,8 +429,6 @@ async fn start_ipc(cm: ConnectionManager) {
async fn start_pa() { async fn start_pa() {
use crate::audio_service::AUDIO_DATA_SIZE_U8; use crate::audio_service::AUDIO_DATA_SIZE_U8;
use hbb_common::config::APP_NAME; use hbb_common::config::APP_NAME;
use libpulse_binding as pulse;
use libpulse_simple_binding as psimple;
match new_listener("_pa").await { match new_listener("_pa").await {
Ok(mut incoming) => { Ok(mut incoming) => {