change linux audio playback from cpal to pulse, because stupid cpal 100% cpu usage problem
This commit is contained in:
parent
aa7779bcf7
commit
5bf8e8f001
537
Cargo.lock
generated
537
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -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]
|
||||||
|
104
src/client.rs
104
src/client.rs
@ -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 application’s 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,
|
||||||
|
@ -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) => {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user