From 6f5607d9abd3880842f5abc935239c0e357e7d67 Mon Sep 17 00:00:00 2001 From: ashthespy Date: Sat, 23 Jan 2021 22:21:42 +0000 Subject: [PATCH 001/103] [Core] Bump hyper to ~0.12 --- core/Cargo.toml | 4 ++-- core/src/apresolve.rs | 32 ++++++++++++++++---------------- core/src/proxytunnel.rs | 2 +- core/src/session.rs | 3 +-- 4 files changed, 20 insertions(+), 21 deletions(-) diff --git a/core/Cargo.toml b/core/Cargo.toml index 8511878..d2eec63 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -19,8 +19,8 @@ bytes = "0.4" error-chain = { version = "0.12", default_features = false } futures = "0.1" httparse = "1.3" -hyper = "0.11" -hyper-proxy = { version = "0.4", default_features = false } +hyper = "0.12" +hyper-proxy = { version = "0.5", default_features = false } lazy_static = "1.3" log = "0.4" num-bigint = "0.3" diff --git a/core/src/apresolve.rs b/core/src/apresolve.rs index 94d9424..bea2331 100644 --- a/core/src/apresolve.rs +++ b/core/src/apresolve.rs @@ -3,11 +3,10 @@ const APRESOLVE_ENDPOINT: &'static str = "http://apresolve.spotify.com/"; use futures::{Future, Stream}; use hyper::client::HttpConnector; -use hyper::{self, Client, Method, Request, Uri}; +use hyper::{self, Client, Request, Uri}; use hyper_proxy::{Intercept, Proxy, ProxyConnector}; use serde_json; use std::str::FromStr; -use tokio_core::reactor::Handle; use url::Url; error_chain! {} @@ -18,35 +17,37 @@ pub struct APResolveData { } fn apresolve( - handle: &Handle, proxy: &Option, ap_port: &Option, ) -> Box> { let url = Uri::from_str(APRESOLVE_ENDPOINT).expect("invalid AP resolve URL"); let use_proxy = proxy.is_some(); - let mut req = Request::new(Method::Get, url.clone()); + // let mut req = Request::new(url.clone()); + let mut req = Request::get(url.clone()) + .body(hyper::Body::from(vec![])) + .unwrap(); let response = match *proxy { Some(ref val) => { let proxy_url = Uri::from_str(val.as_str()).expect("invalid http proxy"); let proxy = Proxy::new(Intercept::All, proxy_url); - let connector = HttpConnector::new(4, handle); + let connector = HttpConnector::new(4); let proxy_connector = ProxyConnector::from_proxy_unsecured(connector, proxy); if let Some(headers) = proxy_connector.http_headers(&url) { - req.headers_mut().extend(headers.iter()); - req.set_proxy(true); + req.headers_mut().extend(headers.clone().into_iter()); + // req.set_proxy(true); } - let client = Client::configure().connector(proxy_connector).build(handle); + let client = Client::builder().build(proxy_connector); client.request(req) } _ => { - let client = Client::new(handle); + let client = Client::new(); client.request(req) } }; let body = response.and_then(|response| { - response.body().fold(Vec::new(), |mut acc, chunk| { + response.into_body().fold(Vec::new(), |mut acc, chunk| { acc.extend_from_slice(chunk.as_ref()); Ok::<_, hyper::Error>(acc) }) @@ -64,13 +65,13 @@ fn apresolve( let mut aps = data.ap_list.iter().filter(|ap| { if p.is_some() { Uri::from_str(ap).ok().map_or(false, |uri| { - uri.port().map_or(false, |port| port == p.unwrap()) + uri.port_u16().map_or(false, |port| port == p.unwrap()) }) } else if use_proxy { // It is unlikely that the proxy will accept CONNECT on anything other than 443. - Uri::from_str(ap) - .ok() - .map_or(false, |uri| uri.port().map_or(false, |port| port == 443)) + Uri::from_str(ap).ok().map_or(false, |uri| { + uri.port_u16().map_or(false, |port| port == 443) + }) } else { true } @@ -84,14 +85,13 @@ fn apresolve( } pub(crate) fn apresolve_or_fallback( - handle: &Handle, proxy: &Option, ap_port: &Option, ) -> Box> where E: 'static, { - let ap = apresolve(handle, proxy, ap_port).or_else(|e| { + let ap = apresolve(proxy, ap_port).or_else(|e| { warn!("Failed to resolve Access Point: {}", e.description()); warn!("Using fallback \"{}\"", AP_FALLBACK); Ok(AP_FALLBACK.into()) diff --git a/core/src/proxytunnel.rs b/core/src/proxytunnel.rs index b136384..e8fb137 100644 --- a/core/src/proxytunnel.rs +++ b/core/src/proxytunnel.rs @@ -102,7 +102,7 @@ fn proxy_connect(connection: T, connect_url: &str) -> WriteAll, handle: Handle, ) -> Box> { - let access_point = - apresolve_or_fallback::(&handle, &config.proxy, &config.ap_port); + let access_point = apresolve_or_fallback::(&config.proxy, &config.ap_port); let handle_ = handle.clone(); let proxy = config.proxy.clone(); From 931c8207fc29e28728a8f034a7b540724a12733a Mon Sep 17 00:00:00 2001 From: ashthespy Date: Sat, 23 Jan 2021 22:21:42 +0000 Subject: [PATCH 002/103] [Connect] Migrate to hyper ~v12 --- connect/Cargo.toml | 2 +- connect/src/discovery.rs | 60 +++++++++++++++++++++++----------------- 2 files changed, 36 insertions(+), 26 deletions(-) diff --git a/connect/Cargo.toml b/connect/Cargo.toml index 8235870..f21faef 100644 --- a/connect/Cargo.toml +++ b/connect/Cargo.toml @@ -20,7 +20,7 @@ version = "0.1.3" [dependencies] base64 = "0.13" futures = "0.1" -hyper = "0.11" +hyper = "0.12" log = "0.4" num-bigint = "0.3" protobuf = "~2.14.0" diff --git a/connect/src/discovery.rs b/connect/src/discovery.rs index 9779e6f..e3bc227 100644 --- a/connect/src/discovery.rs +++ b/connect/src/discovery.rs @@ -5,8 +5,10 @@ use base64; use futures::sync::mpsc; use futures::{Future, Poll, Stream}; use hmac::{Hmac, Mac}; -use hyper::server::{Http, Request, Response, Service}; -use hyper::{self, Get, Post, StatusCode}; + +use hyper::{ + self, server::conn::Http, service::Service, Body, Method, Request, Response, StatusCode, +}; use sha1::{Digest, Sha1}; #[cfg(feature = "with-dns-sd")] @@ -67,7 +69,7 @@ impl Discovery { fn handle_get_info( &self, _params: &BTreeMap, - ) -> ::futures::Finished { + ) -> ::futures::Finished, hyper::Error> { let public_key = self.0.public_key.to_bytes_be(); let public_key = base64::encode(&public_key); @@ -88,13 +90,13 @@ impl Discovery { }); let body = result.to_string(); - ::futures::finished(Response::new().with_body(body)) + ::futures::finished(Response::new(Body::from(body))) } fn handle_add_user( &self, params: &BTreeMap, - ) -> ::futures::Finished { + ) -> ::futures::Finished, hyper::Error> { let username = params.get("userName").unwrap(); let encrypted_blob = params.get("blob").unwrap(); let client_key = params.get("clientKey").unwrap(); @@ -136,7 +138,7 @@ impl Discovery { }); let body = result.to_string(); - return ::futures::finished(Response::new().with_body(body)); + return ::futures::finished(Response::new(Body::from(body))); } let decrypted = { @@ -161,30 +163,33 @@ impl Discovery { }); let body = result.to_string(); - ::futures::finished(Response::new().with_body(body)) + return ::futures::finished(Response::new(Body::from(body))); } - fn not_found(&self) -> ::futures::Finished { - ::futures::finished(Response::new().with_status(StatusCode::NotFound)) + fn not_found(&self) -> ::futures::Finished, hyper::Error> { + let mut res = Response::default(); + *res.status_mut() = StatusCode::NOT_FOUND; + ::futures::finished(res) } } impl Service for Discovery { - type Request = Request; - type Response = Response; + type ReqBody = Body; + type ResBody = Body; type Error = hyper::Error; - type Future = Box>; - fn call(&self, request: Request) -> Self::Future { + type Future = Box, Error = hyper::Error> + Send>; + fn call(&mut self, request: Request<(Self::ReqBody)>) -> Self::Future { let mut params = BTreeMap::new(); - let (method, uri, _, _, body) = request.deconstruct(); - if let Some(query) = uri.query() { + let (parts, body) = request.into_parts(); + + if let Some(query) = parts.uri.query() { params.extend(url::form_urlencoded::parse(query.as_bytes()).into_owned()); } - if method != Get { - debug!("{:?} {:?} {:?}", method, uri.path(), params); + if parts.method != Method::GET { + debug!("{:?} {:?} {:?}", parts.method, parts.uri.path(), params); } let this = self.clone(); @@ -198,9 +203,9 @@ impl Service for Discovery { params }) .and_then(move |params| { - match (method, params.get("action").map(AsRef::as_ref)) { - (Get, Some("getInfo")) => this.handle_get_info(¶ms), - (Post, Some("addUser")) => this.handle_add_user(¶ms), + match (parts.method, params.get("action").map(AsRef::as_ref)) { + (Method::GET, Some("getInfo")) => this.handle_get_info(¶ms), + (Method::POST, Some("addUser")) => this.handle_add_user(¶ms), _ => this.not_found(), } }), @@ -232,7 +237,7 @@ pub fn discovery( let http = Http::new(); http.serve_addr_handle( &format!("0.0.0.0:{}", port).parse().unwrap(), - &handle, + handle.new_tokio_handle(), move || Ok(discovery.clone()), ) .unwrap() @@ -244,10 +249,15 @@ pub fn discovery( let server_future = { let handle = handle.clone(); serve - .for_each(move |connection| { - handle.spawn(connection.then(|_| Ok(()))); - Ok(()) - }) + .for_each( + move |connecting: hyper::server::conn::Connecting< + hyper::server::conn::AddrStream, + futures::Failed<_, hyper::Error>, + >| { + handle.spawn(connecting.then(|_| Ok(()))); + Ok(()) + }, + ) .then(|_| Ok(())) }; handle.spawn(server_future); From 962d7af24df7bc2df24bacc7bd1068e402d9e0d2 Mon Sep 17 00:00:00 2001 From: ashthespy Date: Sat, 23 Jan 2021 22:21:42 +0000 Subject: [PATCH 003/103] Clean up hyper from binary --- Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 6405ca8..89c4b46 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,7 +44,6 @@ base64 = "0.13" env_logger = {version = "0.8", default-features = false, features = ["termcolor","humantime","atty"]} futures = "0.1" getopts = "0.2" -hyper = "0.11" log = "0.4" num-bigint = "0.3" protobuf = "~2.14.0" From 9bbf8c3b26cef284eea5e193ce5755c4897b9d73 Mon Sep 17 00:00:00 2001 From: ashthespy Date: Sat, 23 Jan 2021 22:21:42 +0000 Subject: [PATCH 004/103] WIP tokio-core -> tokio migration --- audio/src/fetch.rs | 12 ++++++------ core/Cargo.toml | 2 +- core/src/connection/mod.rs | 6 ++---- core/src/lib.rs | 2 +- core/src/session.rs | 33 +++++++++++++++------------------ rustfmt.toml | 1 + src/main.rs | 33 ++++++++++++++------------------- 7 files changed, 40 insertions(+), 49 deletions(-) diff --git a/audio/src/fetch.rs b/audio/src/fetch.rs index c47cb4d..2214cde 100644 --- a/audio/src/fetch.rs +++ b/audio/src/fetch.rs @@ -328,7 +328,7 @@ impl AudioFileOpenStreaming { stream_loader_command_rx, complete_tx, ); - self.session.spawn(move |_| fetcher); + self.session.spawn(fetcher); AudioFileStreaming { read_file: read_file, @@ -425,7 +425,7 @@ impl AudioFile { }; let session_ = session.clone(); - session.spawn(move |_| { + session.spawn( complete_rx .map(move |mut file| { if let Some(cache) = session_.cache() { @@ -435,8 +435,8 @@ impl AudioFile { debug!("File {} complete", file_id); } }) - .or_else(|oneshot::Canceled| Ok(())) - }); + .or_else(|oneshot::Canceled| Ok(())), + ); return AudioFileOpen::Streaming(open); } @@ -671,7 +671,7 @@ impl AudioFileFetch { initial_request_sent_time, ); - session.spawn(move |_| initial_data_receiver); + session.spawn(initial_data_receiver); AudioFileFetch { session: session, @@ -746,7 +746,7 @@ impl AudioFileFetch { Instant::now(), ); - self.session.spawn(move |_| receiver); + self.session.spawn(receiver); } } diff --git a/core/Cargo.toml b/core/Cargo.toml index d2eec63..65ba047 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -33,7 +33,7 @@ serde_derive = "1.0" serde_json = "1.0" shannon = "0.2.0" tokio-codec = "0.1" -tokio-core = "0.1" +tokio = "0.1" tokio-io = "0.1" url = "1.7" uuid = { version = "0.8", features = ["v4"] } diff --git a/core/src/connection/mod.rs b/core/src/connection/mod.rs index 7249779..c0e95f5 100644 --- a/core/src/connection/mod.rs +++ b/core/src/connection/mod.rs @@ -8,9 +8,8 @@ use futures::{Future, Sink, Stream}; use protobuf::{self, Message}; use std::io; use std::net::ToSocketAddrs; +use tokio::net::TcpStream; use tokio_codec::Framed; -use tokio_core::net::TcpStream; -use tokio_core::reactor::Handle; use url::Url; use crate::authentication::Credentials; @@ -22,7 +21,6 @@ pub type Transport = Framed; pub fn connect( addr: String, - handle: &Handle, proxy: &Option, ) -> Box> { let (addr, connect_url) = match *proxy { @@ -51,7 +49,7 @@ pub fn connect( } }; - let socket = TcpStream::connect(&addr, handle); + let socket = TcpStream::connect(&addr); if let Some(connect_url) = connect_url { let connection = socket .and_then(move |socket| proxytunnel::connect(socket, &connect_url).and_then(handshake)); diff --git a/core/src/lib.rs b/core/src/lib.rs index c65878c..278478c 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -29,8 +29,8 @@ extern crate serde; extern crate serde_json; extern crate sha1; extern crate shannon; +extern crate tokio; extern crate tokio_codec; -extern crate tokio_core; extern crate tokio_io; extern crate url; extern crate uuid; diff --git a/core/src/session.rs b/core/src/session.rs index f1a24c1..4db2f1d 100644 --- a/core/src/session.rs +++ b/core/src/session.rs @@ -7,7 +7,8 @@ use byteorder::{BigEndian, ByteOrder}; use bytes::Bytes; use futures::sync::mpsc; use futures::{Async, Future, IntoFuture, Poll, Stream}; -use tokio_core::reactor::{Handle, Remote}; +use tokio::runtime::current_thread; +// use tokio::runtime::current_thread::Handle; use crate::apresolve::apresolve_or_fallback; use crate::audio_key::AudioKeyManager; @@ -37,8 +38,7 @@ struct SessionInternal { mercury: Lazy, cache: Option>, - handle: Remote, - + // handle: Handle, session_id: usize, } @@ -52,15 +52,14 @@ impl Session { config: SessionConfig, credentials: Credentials, cache: Option, - handle: Handle, + // handle: Handle, ) -> Box> { let access_point = apresolve_or_fallback::(&config.proxy, &config.ap_port); - let handle_ = handle.clone(); let proxy = config.proxy.clone(); let connection = access_point.and_then(move |addr| { info!("Connecting to AP \"{}\"", addr); - connection::connect(addr, &handle_, &proxy) + connection::connect(addr, &proxy) }); let device_id = config.device_id.clone(); @@ -75,15 +74,16 @@ impl Session { } let (session, task) = Session::create( - &handle, + // &handle, transport, config, cache, reusable_credentials.username.clone(), ); - handle.spawn(task.map_err(|e| { - error!("{:?}", e); + current_thread::spawn(task.map_err(|e| { + error!("SessionError: {}", e.to_string()); + std::process::exit(0); })); session @@ -93,7 +93,7 @@ impl Session { } fn create( - handle: &Handle, + // handle: &Handle, transport: connection::Transport, config: SessionConfig, cache: Option, @@ -123,8 +123,7 @@ impl Session { channel: Lazy::new(), mercury: Lazy::new(), - handle: handle.remote().clone(), - + // handle: handle.clone(), session_id: session_id, })); @@ -159,13 +158,11 @@ impl Session { self.0.data.read().unwrap().time_delta } - pub fn spawn(&self, f: F) + pub fn spawn(&self, f: F) where - F: FnOnce(&Handle) -> R + Send + 'static, - R: IntoFuture, - R::Future: 'static, + F: Future + 'static, { - self.0.handle.spawn(f) + current_thread::spawn(f); } fn debug_info(&self) { @@ -203,7 +200,7 @@ impl Session { 0x9 | 0xa => self.channel().dispatch(cmd, data), 0xd | 0xe => self.audio_key().dispatch(cmd, data), 0xb2..=0xb6 => self.mercury().dispatch(cmd, data), - _ => (), + _ => trace!("Unknown dispatch cmd :{:?} {:?}", cmd, data), } } diff --git a/rustfmt.toml b/rustfmt.toml index 25c1fc1..aefd6aa 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,3 +1,4 @@ # max_width = 105 reorder_imports = true reorder_modules = true +edition = "2018" diff --git a/src/main.rs b/src/main.rs index 4f80657..8cc0690 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,7 +9,7 @@ use std::path::PathBuf; use std::process::exit; use std::str::FromStr; use std::time::Instant; -use tokio_core::reactor::{Core, Handle}; +// use tokio_core::reactor::{Core, Handle}; use tokio_io::IoStream; use url::Url; @@ -26,6 +26,8 @@ use librespot::playback::config::{Bitrate, PlayerConfig}; use librespot::playback::mixer::{self, Mixer, MixerConfig}; use librespot::playback::player::{Player, PlayerEvent}; +use tokio::runtime::current_thread; + mod player_event_handler; use crate::player_event_handler::{emit_sink_event, run_program_on_events}; @@ -399,8 +401,6 @@ struct Main { device: Option, mixer: fn(Option) -> Box, mixer_config: MixerConfig, - handle: Handle, - discovery: Option, signal: IoStream<()>, @@ -418,9 +418,8 @@ struct Main { } impl Main { - fn new(handle: Handle, setup: Setup) -> Main { + fn new(setup: Setup) -> Main { let mut task = Main { - handle: handle.clone(), cache: setup.cache, session_config: setup.session_config, player_config: setup.player_config, @@ -444,13 +443,12 @@ impl Main { emit_sink_events: setup.emit_sink_events, }; - if setup.enable_discovery { - let config = task.connect_config.clone(); - let device_id = task.session_config.device_id.clone(); - - task.discovery = - Some(discovery(&handle, config, device_id, setup.zeroconf_port).unwrap()); - } + // if setup.enable_discovery { + // let config = task.connect_config.clone(); + // let device_id = task.session_config.device_id.clone(); + // + // task.discovery = Some(discovery(config, device_id, setup.zeroconf_port).unwrap()); + // } if let Some(credentials) = setup.credentials { task.credentials(credentials); @@ -462,15 +460,14 @@ impl Main { fn credentials(&mut self, credentials: Credentials) { self.last_credentials = Some(credentials.clone()); let config = self.session_config.clone(); - let handle = self.handle.clone(); - let connection = Session::connect(config, credentials, self.cache.clone(), handle); + let connection = Session::connect(config, credentials, self.cache.clone()); self.connect = connection; self.spirc = None; let task = mem::replace(&mut self.spirc_task, None); if let Some(task) = task { - self.handle.spawn(task); + current_thread::spawn(Box::new(task)); } } } @@ -594,7 +591,7 @@ impl Future for Main { }) .map_err(|e| error!("failed to wait on child process: {}", e)); - self.handle.spawn(child); + current_thread::spawn(child); } } } @@ -611,10 +608,8 @@ fn main() { if env::var("RUST_BACKTRACE").is_err() { env::set_var("RUST_BACKTRACE", "full") } - let mut core = Core::new().unwrap(); - let handle = core.handle(); let args: Vec = std::env::args().collect(); - core.run(Main::new(handle, setup(&args))).unwrap() + current_thread::block_on_all(Main::new(setup(&args))).unwrap() } From 53b4ab05ba9f1627d568c0193cc246fb9cae3fca Mon Sep 17 00:00:00 2001 From: ashthespy Date: Sat, 23 Jan 2021 22:21:42 +0000 Subject: [PATCH 005/103] Migrate to `tokio` 0.1 --- Cargo.toml | 3 ++- core/src/session.rs | 46 +++++++++++++++++++++++++++++++++++---------- src/main.rs | 20 ++++++++++++++------ 3 files changed, 52 insertions(+), 17 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 89c4b46..f56649d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,7 +49,8 @@ num-bigint = "0.3" protobuf = "~2.14.0" rand = "0.7" rpassword = "5.0" -tokio-core = "0.1" +rpassword = "3.0" +tokio = "0.1" tokio-io = "0.1" tokio-process = "0.2" tokio-signal = "0.2" diff --git a/core/src/session.rs b/core/src/session.rs index 4db2f1d..1f8313f 100644 --- a/core/src/session.rs +++ b/core/src/session.rs @@ -1,14 +1,13 @@ use std::io; use std::sync::atomic::{AtomicUsize, Ordering}; -use std::sync::{Arc, RwLock, Weak}; +use std::sync::{Arc, Mutex, RwLock, Weak}; use std::time::{SystemTime, UNIX_EPOCH}; use byteorder::{BigEndian, ByteOrder}; use bytes::Bytes; use futures::sync::mpsc; use futures::{Async, Future, IntoFuture, Poll, Stream}; -use tokio::runtime::current_thread; -// use tokio::runtime::current_thread::Handle; +use tokio::runtime::{current_thread, current_thread::Handle}; use crate::apresolve::apresolve_or_fallback; use crate::audio_key::AudioKeyManager; @@ -38,7 +37,7 @@ struct SessionInternal { mercury: Lazy, cache: Option>, - // handle: Handle, + handle: Mutex, session_id: usize, } @@ -52,7 +51,7 @@ impl Session { config: SessionConfig, credentials: Credentials, cache: Option, - // handle: Handle, + handle: Handle, ) -> Box> { let access_point = apresolve_or_fallback::(&config.proxy, &config.ap_port); @@ -74,7 +73,7 @@ impl Session { } let (session, task) = Session::create( - // &handle, + &handle, transport, config, cache, @@ -93,7 +92,7 @@ impl Session { } fn create( - // handle: &Handle, + handle: &Handle, transport: connection::Transport, config: SessionConfig, cache: Option, @@ -123,7 +122,7 @@ impl Session { channel: Lazy::new(), mercury: Lazy::new(), - // handle: handle.clone(), + handle: Mutex::new(handle.clone()), session_id: session_id, })); @@ -158,13 +157,40 @@ impl Session { self.0.data.read().unwrap().time_delta } + // Spawn a future directly pub fn spawn(&self, f: F) where - F: Future + 'static, + F: Future + Send + 'static, { - current_thread::spawn(f); + let handle = self.0.handle.lock().unwrap(); + let spawn_res = handle.spawn(f); + match spawn_res { + Ok(_) => (), + Err(e) => error!("Session SpawnErr {:?}", e), + } } + // pub fn spawn(&self, f: F) + // where + // F: FnOnce() -> R + Send + 'static, + // R: Future + Send + 'static, + // { + // // This fails when called from a different thread + // // current_thread::spawn(future::lazy(|| f())); + // + // // These fail when the Future doesn't implement Send + // let handle = self.0.handle.lock().unwrap(); + // let spawn_res = handle.spawn(lazy(|| f())); + // + // // let mut te = current_thread::TaskExecutor::current(); + // // let spawn_res = te.spawn_local(Box::new(future::lazy(|| f()))); + // + // match spawn_res { + // Ok(_) => (), + // Err(e) => error!("Session SpawnErr {:?}", e), + // } + // } + fn debug_info(&self) { debug!( "Session[{}] strong={} weak={}", diff --git a/src/main.rs b/src/main.rs index 8cc0690..6673310 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,7 +9,6 @@ use std::path::PathBuf; use std::process::exit; use std::str::FromStr; use std::time::Instant; -// use tokio_core::reactor::{Core, Handle}; use tokio_io::IoStream; use url::Url; @@ -26,7 +25,10 @@ use librespot::playback::config::{Bitrate, PlayerConfig}; use librespot::playback::mixer::{self, Mixer, MixerConfig}; use librespot::playback::player::{Player, PlayerEvent}; -use tokio::runtime::current_thread; +use tokio::runtime::{ + current_thread, + current_thread::{Handle, Runtime}, +}; mod player_event_handler; use crate::player_event_handler::{emit_sink_event, run_program_on_events}; @@ -401,6 +403,7 @@ struct Main { device: Option, mixer: fn(Option) -> Box, mixer_config: MixerConfig, + handle: Handle, discovery: Option, signal: IoStream<()>, @@ -418,8 +421,9 @@ struct Main { } impl Main { - fn new(setup: Setup) -> Main { + fn new(handle: Handle, setup: Setup) -> Main { let mut task = Main { + handle: handle, cache: setup.cache, session_config: setup.session_config, player_config: setup.player_config, @@ -460,8 +464,8 @@ impl Main { fn credentials(&mut self, credentials: Credentials) { self.last_credentials = Some(credentials.clone()); let config = self.session_config.clone(); - - let connection = Session::connect(config, credentials, self.cache.clone()); + let handle = self.handle.clone(); + let connection = Session::connect(config, credentials, self.cache.clone(), handle); self.connect = connection; self.spirc = None; @@ -611,5 +615,9 @@ fn main() { let args: Vec = std::env::args().collect(); - current_thread::block_on_all(Main::new(setup(&args))).unwrap() + let mut runtime = Runtime::new().unwrap(); + let handle = runtime.handle(); + runtime.block_on(Main::new(handle, setup(&args))).unwrap(); + runtime.run().unwrap(); + // current_thread::block_on_all(Main::new(setup(&args))).unwrap() } From c69ccf77e99c6d19ca10818d4841de12e6329c33 Mon Sep 17 00:00:00 2001 From: ashthespy Date: Sat, 23 Jan 2021 22:21:42 +0000 Subject: [PATCH 006/103] [Connect] Migrate to `tokio` 0.1 --- connect/Cargo.toml | 3 ++- connect/src/discovery.rs | 14 ++++++-------- connect/src/lib.rs | 2 +- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/connect/Cargo.toml b/connect/Cargo.toml index f21faef..b5cd4fd 100644 --- a/connect/Cargo.toml +++ b/connect/Cargo.toml @@ -28,7 +28,7 @@ rand = "0.7" serde = "1.0" serde_derive = "1.0" serde_json = "1.0" -tokio-core = "0.1" +tokio = "0.1" url = "1.7" sha-1 = "0.8" hmac = "0.7" @@ -38,6 +38,7 @@ block-modes = "0.3" dns-sd = { version = "0.1.3", optional = true } libmdns = { version = "0.2.7", optional = true } + [features] default = ["libmdns"] with-dns-sd = ["dns-sd"] diff --git a/connect/src/discovery.rs b/connect/src/discovery.rs index e3bc227..6c542e6 100644 --- a/connect/src/discovery.rs +++ b/connect/src/discovery.rs @@ -22,7 +22,7 @@ use rand; use std::collections::BTreeMap; use std::io; use std::sync::Arc; -use tokio_core::reactor::Handle; +use tokio::runtime::current_thread::Handle; use url; use librespot_core::authentication::Credentials; @@ -235,11 +235,9 @@ pub fn discovery( let serve = { let http = Http::new(); - http.serve_addr_handle( - &format!("0.0.0.0:{}", port).parse().unwrap(), - handle.new_tokio_handle(), - move || Ok(discovery.clone()), - ) + http.serve_addr(&format!("0.0.0.0:{}", port).parse().unwrap(), move || { + Ok(discovery.clone()) + }) .unwrap() }; @@ -254,13 +252,13 @@ pub fn discovery( hyper::server::conn::AddrStream, futures::Failed<_, hyper::Error>, >| { - handle.spawn(connecting.then(|_| Ok(()))); + handle.spawn(connecting.flatten().then(|_| Ok(()))).unwrap(); Ok(()) }, ) .then(|_| Ok(())) }; - handle.spawn(server_future); + handle.spawn(server_future).unwrap(); #[cfg(feature = "with-dns-sd")] let svc = DNSService::register( diff --git a/connect/src/lib.rs b/connect/src/lib.rs index 118c85d..4777760 100644 --- a/connect/src/lib.rs +++ b/connect/src/lib.rs @@ -12,7 +12,7 @@ extern crate hyper; extern crate num_bigint; extern crate protobuf; extern crate rand; -extern crate tokio_core; +extern crate tokio; extern crate url; extern crate aes_ctr; From 47a1575c00ee1b0abee98599e3e2d478638e18ca Mon Sep 17 00:00:00 2001 From: ashthespy Date: Sat, 23 Jan 2021 22:21:42 +0000 Subject: [PATCH 007/103] WIP Futures Fix apresolve WIP session [Core] More migration Playing with `ReadExact` and `WriteAll` Add some simple checks Take little steps --- audio/Cargo.toml | 3 +- audio/src/fetch.rs | 122 ++++++++++++++-------------- core/Cargo.toml | 13 +-- core/src/apresolve.rs | 72 +++++++---------- core/src/audio_key.rs | 19 +++-- core/src/channel.rs | 63 ++++++++------- core/src/connection/codec.rs | 8 +- core/src/connection/handshake.rs | 86 +++++++++++--------- core/src/connection/mod.rs | 128 ++++++++++++++--------------- core/src/keymaster.rs | 12 +-- core/src/lib.rs | 12 +-- core/src/mercury/mod.rs | 28 ++++--- core/src/mercury/sender.rs | 24 +++--- core/src/proxytunnel.rs | 58 +++++++++----- core/src/session.rs | 133 ++++++++++++++++++------------- core/tests/connect.rs | 23 ++++++ 16 files changed, 431 insertions(+), 373 deletions(-) create mode 100644 core/tests/connect.rs diff --git a/audio/Cargo.toml b/audio/Cargo.toml index cde907c..5f6a942 100644 --- a/audio/Cargo.toml +++ b/audio/Cargo.toml @@ -14,7 +14,8 @@ version = "0.1.3" bit-set = "0.5" byteorder = "1.3" bytes = "0.4" -futures = "0.1" +futures = "0.3" +tokio = { version = "0.2", features = ["full"] } # Temp "rt-core", "sync" lewton = "0.9" log = "0.4" num-bigint = "0.3" diff --git a/audio/src/fetch.rs b/audio/src/fetch.rs index 2214cde..5ed4ccd 100644 --- a/audio/src/fetch.rs +++ b/audio/src/fetch.rs @@ -1,9 +1,6 @@ use crate::range_set::{Range, RangeSet}; use byteorder::{BigEndian, ByteOrder, WriteBytesExt}; use bytes::Bytes; -use futures::sync::{mpsc, oneshot}; -use futures::Stream; -use futures::{Async, Future, Poll}; use std::cmp::{max, min}; use std::fs; use std::io::{self, Read, Seek, SeekFrom, Write}; @@ -11,13 +8,23 @@ use std::sync::{Arc, Condvar, Mutex}; use std::time::{Duration, Instant}; use tempfile::NamedTempFile; -use futures::sync::mpsc::unbounded; use librespot_core::channel::{Channel, ChannelData, ChannelError, ChannelHeaders}; use librespot_core::session::Session; use librespot_core::spotify_id::FileId; use std::sync::atomic; use std::sync::atomic::AtomicUsize; +use futures::{ + channel::{mpsc, mpsc::unbounded, oneshot}, + ready, Future, Stream, +}; +use std::{ + pin::Pin, + task::{Context, Poll}, +}; + +use tokio::task; + const MINIMUM_DOWNLOAD_SIZE: usize = 1024 * 16; // The minimum size of a block that is requested from the Spotify servers in one request. // This is the block size that is typically requested while doing a seek() on a file. @@ -329,6 +336,7 @@ impl AudioFileOpenStreaming { complete_tx, ); self.session.spawn(fetcher); + // tokio::spawn(move |_| fetcher); AudioFileStreaming { read_file: read_file, @@ -343,36 +351,37 @@ impl AudioFileOpenStreaming { } impl Future for AudioFileOpen { - type Item = AudioFile; - type Error = ChannelError; + type Output = Result; - fn poll(&mut self) -> Poll { + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { match *self { AudioFileOpen::Streaming(ref mut open) => { - let file = try_ready!(open.poll()); - Ok(Async::Ready(AudioFile::Streaming(file))) + let file = ready!(open.poll()); + Poll::Ready(Ok(AudioFile::Streaming(file))) } AudioFileOpen::Cached(ref mut file) => { let file = file.take().unwrap(); - Ok(Async::Ready(AudioFile::Cached(file))) + Poll::Ready(Ok(AudioFile::Cached(file))) } } } } impl Future for AudioFileOpenStreaming { - type Item = AudioFileStreaming; - type Error = ChannelError; + type Output = Result; - fn poll(&mut self) -> Poll { + fn poll( + self: Pin<&mut Self>, + cx: &mut Context, + ) -> Poll> { loop { - let (id, data) = try_ready!(self.headers.poll()).unwrap(); + let (id, data) = ready!(self.headers.poll()).unwrap(); if id == 0x3 { let size = BigEndian::read_u32(&data) as usize * 4; let file = self.finish(size); - return Ok(Async::Ready(file)); + return Poll::Ready(Ok(file)); } } } @@ -563,13 +572,12 @@ impl AudioFileFetchDataReceiver { } impl Future for AudioFileFetchDataReceiver { - type Item = (); - type Error = (); + type Output = (); - fn poll(&mut self) -> Poll<(), ()> { + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<()> { loop { match self.data_rx.poll() { - Ok(Async::Ready(Some(data))) => { + Poll::Ready(Some(data)) => { if self.measure_ping_time { if let Some(request_sent_time) = self.request_sent_time { let duration = Instant::now() - request_sent_time; @@ -603,26 +611,24 @@ impl Future for AudioFileFetchDataReceiver { } if self.request_length == 0 { self.finish(); - return Ok(Async::Ready(())); + return Poll::Ready(()); } } - Ok(Async::Ready(None)) => { + Poll::Ready(None) => { if self.request_length > 0 { warn!("Data receiver for range {} (+{}) received less data from server than requested.", self.initial_data_offset, self.initial_request_length); } self.finish(); - return Ok(Async::Ready(())); - } - Ok(Async::NotReady) => { - return Ok(Async::NotReady); + return Poll::Ready(()); } + Poll::Pending => return Poll::Pending, Err(ChannelError) => { warn!( "Error from channel for data receiver for range {} (+{}).", self.initial_data_offset, self.initial_request_length ); self.finish(); - return Ok(Async::Ready(())); + return Poll::Ready(()); } } } @@ -672,6 +678,7 @@ impl AudioFileFetch { ); session.spawn(initial_data_receiver); + // tokio::spawn(move |_| initial_data_receiver); AudioFileFetch { session: session, @@ -747,6 +754,7 @@ impl AudioFileFetch { ); self.session.spawn(receiver); + // tokio::spawn(move |_| receiver); } } @@ -794,13 +802,11 @@ impl AudioFileFetch { } } - fn poll_file_data_rx(&mut self) -> Poll<(), ()> { + fn poll_file_data_rx(&mut self) -> Poll<()> { loop { match self.file_data_rx.poll() { - Ok(Async::Ready(None)) => { - return Ok(Async::Ready(())); - } - Ok(Async::Ready(Some(ReceivedData::ResponseTimeMs(response_time_ms)))) => { + Poll::Ready(None) => return Poll::Ready(()), + Poll::Ready(Some(ReceivedData::ResponseTimeMs(response_time_ms))) => { trace!("Ping time estimated as: {} ms.", response_time_ms); // record the response time @@ -832,7 +838,7 @@ impl AudioFileFetch { .ping_time_ms .store(ping_time_ms, atomic::Ordering::Relaxed); } - Ok(Async::Ready(Some(ReceivedData::Data(data)))) => { + Poll::Ready(Some(ReceivedData::Data(data))) => { self.output .as_mut() .unwrap() @@ -864,39 +870,34 @@ impl AudioFileFetch { if full { self.finish(); - return Ok(Async::Ready(())); + return Poll::Ready(()); } } - Ok(Async::NotReady) => { - return Ok(Async::NotReady); - } - Err(()) => unreachable!(), + Poll::Pending => return Poll::Pending, + // Err(()) => unreachable!(), } } } - fn poll_stream_loader_command_rx(&mut self) -> Poll<(), ()> { + fn poll_stream_loader_command_rx(&mut self) -> Poll<()> { loop { match self.stream_loader_command_rx.poll() { - Ok(Async::Ready(None)) => { - return Ok(Async::Ready(())); - } - Ok(Async::Ready(Some(StreamLoaderCommand::Fetch(request)))) => { + Poll::Ready(None) => return Poll::Ready(()), + + Poll::Ready(Some(StreamLoaderCommand::Fetch(request))) => { self.download_range(request.start, request.length); } - Ok(Async::Ready(Some(StreamLoaderCommand::RandomAccessMode()))) => { + Poll::Ready(Some(StreamLoaderCommand::RandomAccessMode())) => { *(self.shared.download_strategy.lock().unwrap()) = DownloadStrategy::RandomAccess(); } - Ok(Async::Ready(Some(StreamLoaderCommand::StreamMode()))) => { + Poll::Ready(Some(StreamLoaderCommand::StreamMode())) => { *(self.shared.download_strategy.lock().unwrap()) = DownloadStrategy::Streaming(); } - Ok(Async::Ready(Some(StreamLoaderCommand::Close()))) => { - return Ok(Async::Ready(())); - } - Ok(Async::NotReady) => return Ok(Async::NotReady), - Err(()) => unreachable!(), + Poll::Ready(Some(StreamLoaderCommand::Close())) => return Poll::Ready(()), + Poll::Pending => return Poll::Pending, + // Err(()) => unreachable!(), } } } @@ -911,24 +912,19 @@ impl AudioFileFetch { } impl Future for AudioFileFetch { - type Item = (); - type Error = (); + type Output = (); - fn poll(&mut self) -> Poll<(), ()> { + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<()> { match self.poll_stream_loader_command_rx() { - Ok(Async::NotReady) => (), - Ok(Async::Ready(_)) => { - return Ok(Async::Ready(())); - } - Err(()) => unreachable!(), + Poll::Pending => (), + Poll::Ready(_) => return Poll::Ready(()), + // Err(()) => unreachable!(), } match self.poll_file_data_rx() { - Ok(Async::NotReady) => (), - Ok(Async::Ready(_)) => { - return Ok(Async::Ready(())); - } - Err(()) => unreachable!(), + Poll::Pending => (), + Poll::Ready(_) => return Poll::Ready(()), + // Err(()) => unreachable!(), } if let DownloadStrategy::Streaming() = self.get_download_strategy() { @@ -969,7 +965,7 @@ impl Future for AudioFileFetch { } } - return Ok(Async::NotReady); + return Poll::Pending; } } diff --git a/core/Cargo.toml b/core/Cargo.toml index 65ba047..8c9475a 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -17,10 +17,10 @@ base64 = "0.13" byteorder = "1.3" bytes = "0.4" error-chain = { version = "0.12", default_features = false } -futures = "0.1" +futures = {version = "0.3",features =["unstable","bilock"]} httparse = "1.3" -hyper = "0.12" -hyper-proxy = { version = "0.5", default_features = false } +hyper = "0.13" +hyper-proxy = { version = "0.6", default_features = false } lazy_static = "1.3" log = "0.4" num-bigint = "0.3" @@ -32,9 +32,10 @@ serde = "1.0" serde_derive = "1.0" serde_json = "1.0" shannon = "0.2.0" -tokio-codec = "0.1" -tokio = "0.1" -tokio-io = "0.1" +tokio = {version = "0.2", features = ["full","io-util","tcp"]} # io-util +tokio-util = {version = "0.3", features = ["compat","codec"]} +# tokio-codec = "0.1" +# tokio-io = "0.1" url = "1.7" uuid = { version = "0.8", features = ["v4"] } sha-1 = "0.8" diff --git a/core/src/apresolve.rs b/core/src/apresolve.rs index bea2331..cf30178 100644 --- a/core/src/apresolve.rs +++ b/core/src/apresolve.rs @@ -1,41 +1,33 @@ const AP_FALLBACK: &'static str = "ap.spotify.com:443"; const APRESOLVE_ENDPOINT: &'static str = "http://apresolve.spotify.com/"; -use futures::{Future, Stream}; use hyper::client::HttpConnector; -use hyper::{self, Client, Request, Uri}; +use hyper::{self, Body, Client, Request, Uri}; use hyper_proxy::{Intercept, Proxy, ProxyConnector}; use serde_json; +use std::error; use std::str::FromStr; use url::Url; -error_chain! {} - #[derive(Clone, Debug, Serialize, Deserialize)] pub struct APResolveData { ap_list: Vec, } +type Result = std::result::Result>; -fn apresolve( - proxy: &Option, - ap_port: &Option, -) -> Box> { - let url = Uri::from_str(APRESOLVE_ENDPOINT).expect("invalid AP resolve URL"); +async fn apresolve(proxy: &Option, ap_port: &Option) -> Result { + let url = Uri::from_str(APRESOLVE_ENDPOINT)?; //.expect("invalid AP resolve URL"); let use_proxy = proxy.is_some(); - // let mut req = Request::new(url.clone()); - let mut req = Request::get(url.clone()) - .body(hyper::Body::from(vec![])) - .unwrap(); + let mut req = Request::get(&url).body(Body::empty())?; let response = match *proxy { Some(ref val) => { let proxy_url = Uri::from_str(val.as_str()).expect("invalid http proxy"); let proxy = Proxy::new(Intercept::All, proxy_url); - let connector = HttpConnector::new(4); + let connector = HttpConnector::new(); let proxy_connector = ProxyConnector::from_proxy_unsecured(connector, proxy); if let Some(headers) = proxy_connector.http_headers(&url) { req.headers_mut().extend(headers.clone().into_iter()); - // req.set_proxy(true); } let client = Client::builder().build(proxy_connector); client.request(req) @@ -44,29 +36,19 @@ fn apresolve( let client = Client::new(); client.request(req) } - }; + } + .await?; - let body = response.and_then(|response| { - response.into_body().fold(Vec::new(), |mut acc, chunk| { - acc.extend_from_slice(chunk.as_ref()); - Ok::<_, hyper::Error>(acc) - }) - }); - let body = body.then(|result| result.chain_err(|| "HTTP error")); - let body = - body.and_then(|body| String::from_utf8(body).chain_err(|| "invalid UTF8 in response")); + let body = hyper::body::to_bytes(response.into_body()).await?; + let body = String::from_utf8(body.to_vec())?; + let data = serde_json::from_str::(&body)?; - let data = body - .and_then(|body| serde_json::from_str::(&body).chain_err(|| "invalid JSON")); - - let p = ap_port.clone(); - - let ap = data.and_then(move |data| { + let ap = { let mut aps = data.ap_list.iter().filter(|ap| { - if p.is_some() { - Uri::from_str(ap).ok().map_or(false, |uri| { - uri.port_u16().map_or(false, |port| port == p.unwrap()) - }) + if let Some(p) = ap_port { + Uri::from_str(ap) + .ok() + .map_or(false, |uri| uri.port_u16().map_or(false, |port| &port == p)) } else if use_proxy { // It is unlikely that the proxy will accept CONNECT on anything other than 443. Uri::from_str(ap).ok().map_or(false, |uri| { @@ -79,23 +61,23 @@ fn apresolve( let ap = aps.next().ok_or("empty AP List")?; Ok(ap.clone()) - }); + }; - Box::new(ap) + ap } -pub(crate) fn apresolve_or_fallback( +pub(crate) async fn apresolve_or_fallback( proxy: &Option, ap_port: &Option, -) -> Box> -where - E: 'static, -{ - let ap = apresolve(proxy, ap_port).or_else(|e| { - warn!("Failed to resolve Access Point: {}", e.description()); +) -> Result { + // match apresolve.await { + // Ok(ap) + // } + let ap = apresolve(proxy, ap_port).await.or_else(|e| { + warn!("Failed to resolve Access Point: {:?}", e); warn!("Using fallback \"{}\"", AP_FALLBACK); Ok(AP_FALLBACK.into()) }); - Box::new(ap) + ap } diff --git a/core/src/audio_key.rs b/core/src/audio_key.rs index 1e5310c..39eef72 100644 --- a/core/src/audio_key.rs +++ b/core/src/audio_key.rs @@ -1,10 +1,14 @@ use byteorder::{BigEndian, ByteOrder, WriteBytesExt}; use bytes::Bytes; -use futures::sync::oneshot; -use futures::{Async, Future, Poll}; use std::collections::HashMap; use std::io::Write; +use futures::{channel::oneshot, Future}; +use std::{ + pin::Pin, + task::{Context, Poll}, +}; + use crate::spotify_id::{FileId, SpotifyId}; use crate::util::SeqGenerator; @@ -73,14 +77,13 @@ impl AudioKeyManager { pub struct AudioKeyFuture(oneshot::Receiver>); impl Future for AudioKeyFuture { - type Item = T; - type Error = AudioKeyError; + type Output = Result; - fn poll(&mut self) -> Poll { + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { match self.0.poll() { - Ok(Async::Ready(Ok(value))) => Ok(Async::Ready(value)), - Ok(Async::Ready(Err(err))) => Err(err), - Ok(Async::NotReady) => Ok(Async::NotReady), + Poll::Ready(Ok(Ok(value))) => Poll::Ready(Ok(value)), + Poll::Ready(Ok(Err(err))) => Err(err), + Poll::Pending => Poll::Pending, Err(oneshot::Canceled) => Err(AudioKeyError), } } diff --git a/core/src/channel.rs b/core/src/channel.rs index b614fac..f789bfe 100644 --- a/core/src/channel.rs +++ b/core/src/channel.rs @@ -1,12 +1,16 @@ use byteorder::{BigEndian, ByteOrder}; use bytes::Bytes; -use futures::sync::{mpsc, BiLock}; -use futures::{Async, Poll, Stream}; use std::collections::HashMap; use std::time::Instant; use crate::util::SeqGenerator; +use futures::{channel::mpsc, lock::BiLock, Stream}; +use std::{ + pin::Pin, + task::{Context, Poll}, +}; + component! { ChannelManager : ChannelManagerInner { sequence: SeqGenerator = SeqGenerator::new(0), @@ -101,11 +105,11 @@ impl ChannelManager { } impl Channel { - fn recv_packet(&mut self) -> Poll { + fn recv_packet(&mut self) -> Poll> { let (cmd, packet) = match self.receiver.poll() { - Ok(Async::Ready(Some(t))) => t, - Ok(Async::Ready(None)) => return Err(ChannelError), // The channel has been closed. - Ok(Async::NotReady) => return Ok(Async::NotReady), + Poll::Ready(Ok(Some(t))) => t, + Poll::Ready(Ok(t)) => return Err(ChannelError), // The channel has been closed. + Poll::Pending => return Poll::Pending, Err(()) => unreachable!(), }; @@ -117,7 +121,7 @@ impl Channel { Err(ChannelError) } else { - Ok(Async::Ready(packet)) + Poll::Ready(Ok(packet)) } } @@ -129,16 +133,15 @@ impl Channel { } impl Stream for Channel { - type Item = ChannelEvent; - type Error = ChannelError; + type Item = Result, ChannelError>; - fn poll(&mut self) -> Poll, Self::Error> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { loop { match self.state.clone() { ChannelState::Closed => panic!("Polling already terminated channel"), ChannelState::Header(mut data) => { if data.len() == 0 { - data = try_ready!(self.recv_packet()); + data = ready!(self.recv_packet()); } let length = BigEndian::read_u16(data.split_to(2).as_ref()) as usize; @@ -152,19 +155,19 @@ impl Stream for Channel { self.state = ChannelState::Header(data); let event = ChannelEvent::Header(header_id, header_data); - return Ok(Async::Ready(Some(event))); + return Poll::Ready(Ok(Some(event))); } } ChannelState::Data => { - let data = try_ready!(self.recv_packet()); + let data = ready!(self.recv_packet()); if data.len() == 0 { self.receiver.close(); self.state = ChannelState::Closed; - return Ok(Async::Ready(None)); + return Poll::Ready(Ok(None)); } else { let event = ChannelEvent::Data(data); - return Ok(Async::Ready(Some(event))); + return Poll::Ready(Ok(Some(event))); } } } @@ -173,38 +176,36 @@ impl Stream for Channel { } impl Stream for ChannelData { - type Item = Bytes; - type Error = ChannelError; + type Item = Result, ChannelError>; - fn poll(&mut self) -> Poll, Self::Error> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { let mut channel = match self.0.poll_lock() { - Async::Ready(c) => c, - Async::NotReady => return Ok(Async::NotReady), + Poll::Ready(c) => c, + Poll::Pending => return Poll::Pending, }; loop { - match try_ready!(channel.poll()) { + match ready!(channel.poll()) { Some(ChannelEvent::Header(..)) => (), - Some(ChannelEvent::Data(data)) => return Ok(Async::Ready(Some(data))), - None => return Ok(Async::Ready(None)), + Some(ChannelEvent::Data(data)) => return Poll::Ready(Ok(Some(data))), + None => return Poll::Ready(Ok(None)), } } } } impl Stream for ChannelHeaders { - type Item = (u8, Vec); - type Error = ChannelError; + type Item = Result)>, ChannelError>; - fn poll(&mut self) -> Poll, Self::Error> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { let mut channel = match self.0.poll_lock() { - Async::Ready(c) => c, - Async::NotReady => return Ok(Async::NotReady), + Poll::Ready(c) => c, + Poll::Pending => return Poll::Pending, }; - match try_ready!(channel.poll()) { - Some(ChannelEvent::Header(id, data)) => Ok(Async::Ready(Some((id, data)))), - Some(ChannelEvent::Data(..)) | None => Ok(Async::Ready(None)), + match ready!(channel.poll()) { + Some(ChannelEvent::Header(id, data)) => Poll::Ready(Ok(Some((id, data)))), + Some(ChannelEvent::Data(..)) | None => Poll::Ready(Ok(None)), } } } diff --git a/core/src/connection/codec.rs b/core/src/connection/codec.rs index fa4cd9d..47e1163 100644 --- a/core/src/connection/codec.rs +++ b/core/src/connection/codec.rs @@ -2,7 +2,7 @@ use byteorder::{BigEndian, ByteOrder}; use bytes::{BufMut, Bytes, BytesMut}; use shannon::Shannon; use std::io; -use tokio_io::codec::{Decoder, Encoder}; +use tokio_util::codec::{Decoder, Encoder}; const HEADER_SIZE: usize = 3; const MAC_SIZE: usize = 4; @@ -35,11 +35,11 @@ impl APCodec { } } -impl Encoder for APCodec { - type Item = (u8, Vec); +type APCodecItem = (u8, Vec); +impl Encoder for APCodec { type Error = io::Error; - fn encode(&mut self, item: (u8, Vec), buf: &mut BytesMut) -> io::Result<()> { + fn encode(&mut self, item: APCodecItem, buf: &mut BytesMut) -> io::Result<()> { let (cmd, payload) = item; let offset = buf.len(); diff --git a/core/src/connection/handshake.rs b/core/src/connection/handshake.rs index 220ab6e..512f61c 100644 --- a/core/src/connection/handshake.rs +++ b/core/src/connection/handshake.rs @@ -1,14 +1,13 @@ use byteorder::{BigEndian, ByteOrder, WriteBytesExt}; -use futures::{Async, Future, Poll}; use hmac::{Hmac, Mac}; use protobuf::{self, Message}; use rand::thread_rng; use sha1::Sha1; use std::io::{self, Read}; use std::marker::PhantomData; -use tokio_codec::{Decoder, Framed}; -use tokio_io::io::{read_exact, write_all, ReadExact, Window, WriteAll}; -use tokio_io::{AsyncRead, AsyncWrite}; +// use tokio_codec::{Decoder, Framed}; +// use tokio_io::io::{read_exact, write_all, ReadExact, Window, WriteAll}; +// use tokio_io::{AsyncRead, AsyncWrite}; use super::codec::APCodec; use crate::diffie_hellman::DHLocalKeys; @@ -16,18 +15,30 @@ use crate::protocol; use crate::protocol::keyexchange::{APResponseMessage, ClientHello, ClientResponsePlaintext}; use crate::util; -pub struct Handshake { +use futures::{ + io::{ReadExact, Window, WriteAll}, + Future, +}; +use std::{ + pin::Pin, + task::{Context, Poll}, +}; +use tokio::io::{AsyncRead, AsyncWrite, AsyncWriteExt}; + +use tokio_util::codec::{Decoder, Framed}; + +pub struct Handshake<'a, T> { keys: DHLocalKeys, - state: HandshakeState, + state: HandshakeState<'a, T>, } -enum HandshakeState { - ClientHello(WriteAll>), - APResponse(RecvPacket), - ClientResponse(Option, WriteAll>), +enum HandshakeState<'a, T> { + ClientHello(WriteAll<'a, T>), + APResponse(RecvPacket<'a, T, APResponseMessage>), + ClientResponse(Option, WriteAll<'a, T>), } -pub fn handshake(connection: T) -> Handshake { +pub fn handshake<'a, T: AsyncRead + AsyncWrite>(connection: T) -> Handshake<'a, T> { let local_keys = DHLocalKeys::random(&mut thread_rng()); let client_hello = client_hello(connection, local_keys.public_key()); @@ -37,23 +48,22 @@ pub fn handshake(connection: T) -> Handshake { } } -impl Future for Handshake { - type Item = Framed; - type Error = io::Error; +impl<'a, T: AsyncRead + AsyncWrite> Future for Handshake<'a, T> { + type Output = Result, io::Error>; - fn poll(&mut self) -> Poll { + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { use self::HandshakeState::*; loop { self.state = match self.state { ClientHello(ref mut write) => { - let (connection, accumulator) = try_ready!(write.poll()); + let (connection, accumulator) = ready!(write.poll()); let read = recv_packet(connection, accumulator); APResponse(read) } APResponse(ref mut read) => { - let (connection, message, accumulator) = try_ready!(read.poll()); + let (connection, message, accumulator) = ready!(read.poll()); let remote_key = message .get_challenge() .get_login_crypto_challenge() @@ -71,17 +81,17 @@ impl Future for Handshake { } ClientResponse(ref mut codec, ref mut write) => { - let (connection, _) = try_ready!(write.poll()); + let (connection, _) = ready!(write.poll()); let codec = codec.take().unwrap(); let framed = codec.framed(connection); - return Ok(Async::Ready(framed)); + return Poll::Ready(Ok(framed)); } } } } } -fn client_hello(connection: T, gc: Vec) -> WriteAll> { +fn client_hello<'a, T: AsyncWrite>(connection: T, gc: Vec) -> WriteAll<'a, T> { let mut packet = ClientHello::new(); packet .mut_build_info() @@ -109,10 +119,11 @@ fn client_hello(connection: T, gc: Vec) -> WriteAll(size).unwrap(); packet.write_to_vec(&mut buffer).unwrap(); - write_all(connection, buffer) + // write_all(connection, buffer) + connection.write_all(&buffer) } -fn client_response(connection: T, challenge: Vec) -> WriteAll> { +fn client_response<'a, T: AsyncWrite>(connection: T, challenge: Vec) -> WriteAll<'a, T> { let mut packet = ClientResponsePlaintext::new(); packet .mut_login_crypto_response() @@ -126,15 +137,16 @@ fn client_response(connection: T, challenge: Vec) -> WriteAll buffer.write_u32::(size).unwrap(); packet.write_to_vec(&mut buffer).unwrap(); - write_all(connection, buffer) + // write_all(connection, buffer) + connection.write_all(&buffer) } -enum RecvPacket { - Header(ReadExact>>, PhantomData), - Body(ReadExact>>, PhantomData), +enum RecvPacket<'a, T, M: Message> { + Header(ReadExact<'a, T>, PhantomData), + Body(ReadExact<'a, T>, PhantomData), } -fn recv_packet(connection: T, acc: Vec) -> RecvPacket +fn recv_packet<'a, T: AsyncRead, M>(connection: T, acc: Vec) -> RecvPacket<'a, T, M> where T: Read, M: Message, @@ -142,20 +154,19 @@ where RecvPacket::Header(read_into_accumulator(connection, 4, acc), PhantomData) } -impl Future for RecvPacket +impl<'a, T: AsyncRead, M> Future for RecvPacket<'a, T, M> where T: Read, M: Message, { - type Item = (T, M, Vec); - type Error = io::Error; + type Output = Result<(T, M, Vec), io::Error>; - fn poll(&mut self) -> Poll { + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { use self::RecvPacket::*; loop { *self = match *self { Header(ref mut read, _) => { - let (connection, header) = try_ready!(read.poll()); + let (connection, header) = ready!(read.poll()); let size = BigEndian::read_u32(header.as_ref()) as usize; let acc = header.into_inner(); @@ -164,29 +175,30 @@ where } Body(ref mut read, _) => { - let (connection, data) = try_ready!(read.poll()); + let (connection, data) = ready!(read.poll()); let message = protobuf::parse_from_bytes(data.as_ref()).unwrap(); let acc = data.into_inner(); - return Ok(Async::Ready((connection, message, acc))); + return Poll::Ready(Ok((connection, message, acc))); } } } } } -fn read_into_accumulator( +fn read_into_accumulator<'a, T: AsyncRead>( connection: T, size: usize, mut acc: Vec, -) -> ReadExact>> { +) -> ReadExact<'a, T> { let offset = acc.len(); acc.resize(offset + size, 0); let mut window = Window::new(acc); window.set_start(offset); - read_exact(connection, window) + // read_exact(connection, window) + connection.read_exact(window) } fn compute_keys(shared_secret: &[u8], packets: &[u8]) -> (Vec, Vec, Vec) { diff --git a/core/src/connection/mod.rs b/core/src/connection/mod.rs index c0e95f5..21550de 100644 --- a/core/src/connection/mod.rs +++ b/core/src/connection/mod.rs @@ -3,13 +3,18 @@ mod handshake; pub use self::codec::APCodec; pub use self::handshake::handshake; +use tokio::net::TcpStream; -use futures::{Future, Sink, Stream}; +use futures::{AsyncRead, AsyncWrite, Future, Sink, SinkExt, Stream, StreamExt}; use protobuf::{self, Message}; use std::io; use std::net::ToSocketAddrs; -use tokio::net::TcpStream; -use tokio_codec::Framed; +use tokio_util::codec::Framed; +// use futures::compat::{AsyncWrite01CompatExt, AsyncRead01CompatExt}; +// use tokio_util::compat::{self, Tokio02AsyncReadCompatExt, Tokio02AsyncWriteCompatExt}; +// use tokio_codec::Framed; +// use tokio_core::net::TcpStream; +// use tokio_core::reactor::Handle; use url::Url; use crate::authentication::Credentials; @@ -19,52 +24,46 @@ use crate::proxytunnel; pub type Transport = Framed; -pub fn connect( - addr: String, - proxy: &Option, -) -> Box> { - let (addr, connect_url) = match *proxy { +pub async fn connect(addr: String, proxy: &Option) -> Result { + let (addr, connect_url): (_, Option) = match *proxy { Some(ref url) => { - info!("Using proxy \"{}\"", url); - match url.to_socket_addrs().and_then(|mut iter| { - iter.next().ok_or(io::Error::new( - io::ErrorKind::NotFound, - "Can't resolve proxy server address", - )) - }) { - Ok(socket_addr) => (socket_addr, Some(addr)), - Err(error) => return Box::new(futures::future::err(error)), - } + unimplemented!() + // info!("Using proxy \"{}\"", url); + // + // let mut iter = url.to_socket_addrs()?; + // let socket_addr = iter.next().ok_or(io::Error::new( + // io::ErrorKind::NotFound, + // "Can't resolve proxy server address", + // ))?; + // (socket_addr, Some(addr)) } None => { - match addr.to_socket_addrs().and_then(|mut iter| { - iter.next().ok_or(io::Error::new( - io::ErrorKind::NotFound, - "Can't resolve server address", - )) - }) { - Ok(socket_addr) => (socket_addr, None), - Err(error) => return Box::new(futures::future::err(error)), - } + let mut iter = addr.to_socket_addrs()?; + let socket_addr = iter.next().ok_or(io::Error::new( + io::ErrorKind::NotFound, + "Can't resolve server address", + ))?; + (socket_addr, None) } }; - let socket = TcpStream::connect(&addr); + let connection = TcpStream::connect(&addr).await?; if let Some(connect_url) = connect_url { - let connection = socket - .and_then(move |socket| proxytunnel::connect(socket, &connect_url).and_then(handshake)); - Box::new(connection) + unimplemented!() + // let connection = proxytunnel::connect(connection, &connect_url).await?; + // let connection = handshake(connection).await?; + // Ok(connection) } else { - let connection = socket.and_then(handshake); - Box::new(connection) + let connection = handshake(connection).await?; + Ok(connection) } } -pub fn authenticate( - transport: Transport, +pub async fn authenticate( + mut transport: Transport, credentials: Credentials, device_id: String, -) -> Box> { +) -> Result<(Transport, Credentials), io::Error> { use crate::protocol::authentication::{APWelcome, ClientResponseEncrypted, CpuFamily, Os}; use crate::protocol::keyexchange::APLoginFailed; @@ -92,38 +91,39 @@ pub fn authenticate( packet.mut_system_info().set_device_id(device_id); packet.set_version_string(version::version_string()); - let cmd = 0xab; + let cmd: u8 = 0xab; let data = packet.write_to_bytes().unwrap(); - Box::new( - transport - .send((cmd, data)) - .and_then(|transport| transport.into_future().map_err(|(err, _stream)| err)) - .and_then(|(packet, transport)| match packet { - Some((0xac, data)) => { - let welcome_data: APWelcome = - protobuf::parse_from_bytes(data.as_ref()).unwrap(); + transport.send((cmd, data)).await; - let reusable_credentials = Credentials { - username: welcome_data.get_canonical_username().to_owned(), - auth_type: welcome_data.get_reusable_auth_credentials_type(), - auth_data: welcome_data.get_reusable_auth_credentials().to_owned(), - }; + let packet = transport.next().await; + // let (packet, transport) = transport + // .into_future() + // .map_err(|(err, _stream)| err) + // .await?; + match packet { + Some(Ok((0xac, data))) => { + let welcome_data: APWelcome = protobuf::parse_from_bytes(data.as_ref()).unwrap(); - Ok((transport, reusable_credentials)) - } + let reusable_credentials = Credentials { + username: welcome_data.get_canonical_username().to_owned(), + auth_type: welcome_data.get_reusable_auth_credentials_type(), + auth_data: welcome_data.get_reusable_auth_credentials().to_owned(), + }; - Some((0xad, data)) => { - let error_data: APLoginFailed = - protobuf::parse_from_bytes(data.as_ref()).unwrap(); - panic!( - "Authentication failed with reason: {:?}", - error_data.get_error_code() - ) - } + Ok((transport, reusable_credentials)) + } - Some((cmd, _)) => panic!("Unexpected packet {:?}", cmd), - None => panic!("EOF"), - }), - ) + Some(Ok((0xad, data))) => { + let error_data: APLoginFailed = protobuf::parse_from_bytes(data.as_ref()).unwrap(); + panic!( + "Authentication failed with reason: {:?}", + error_data.get_error_code() + ) + } + + Some(Ok((cmd, _))) => panic!("Unexpected packet {:?}", cmd), + Some(err @ Err(_)) => panic!("Packet error: {:?}", err), + None => panic!("EOF"), + } } diff --git a/core/src/keymaster.rs b/core/src/keymaster.rs index f2d7b77..c7be11b 100644 --- a/core/src/keymaster.rs +++ b/core/src/keymaster.rs @@ -1,4 +1,4 @@ -use futures::Future; +// use futures::Future; use serde_json; use crate::mercury::MercuryError; @@ -13,20 +13,22 @@ pub struct Token { pub scope: Vec, } -pub fn get_token( +pub async fn get_token( session: &Session, client_id: &str, scopes: &str, -) -> Box> { +) -> Result { let url = format!( "hm://keymaster/token/authenticated?client_id={}&scope={}", client_id, scopes ); - Box::new(session.mercury().get(url).map(move |response| { + + // Box::new(session.mercury().get(url).map(move |response| { + session.mercury().get(url).await.map(move |response| { let data = response.payload.first().expect("Empty payload"); let data = String::from_utf8(data.clone()).unwrap(); let token: Token = serde_json::from_str(&data).unwrap(); token - })) + }) } diff --git a/core/src/lib.rs b/core/src/lib.rs index 278478c..2d50ec7 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -1,7 +1,7 @@ #![cfg_attr(feature = "cargo-clippy", allow(unused_io_amount))] -#[macro_use] -extern crate error_chain; +// #[macro_use] +// extern crate error_chain; #[macro_use] extern crate futures; #[macro_use] @@ -30,8 +30,8 @@ extern crate serde_json; extern crate sha1; extern crate shannon; extern crate tokio; -extern crate tokio_codec; -extern crate tokio_io; +// extern crate tokio_codec; +// extern crate tokio_io; extern crate url; extern crate uuid; @@ -45,11 +45,11 @@ pub mod authentication; pub mod cache; pub mod channel; pub mod config; -mod connection; +pub mod connection; pub mod diffie_hellman; pub mod keymaster; pub mod mercury; -mod proxytunnel; +pub mod proxytunnel; pub mod session; pub mod spotify_id; pub mod util; diff --git a/core/src/mercury/mod.rs b/core/src/mercury/mod.rs index 20e3f0d..7a80f44 100644 --- a/core/src/mercury/mod.rs +++ b/core/src/mercury/mod.rs @@ -2,12 +2,16 @@ use crate::protocol; use crate::util::url_encode; use byteorder::{BigEndian, ByteOrder}; use bytes::Bytes; -use futures::sync::{mpsc, oneshot}; -use futures::{Async, Future, Poll}; use protobuf; use std::collections::HashMap; use std::mem; +use futures::{ + channel::{mpsc, oneshot}, + Future, FutureExt, +}; +use std::task::Poll; + use crate::util::SeqGenerator; mod types; @@ -33,14 +37,13 @@ pub struct MercuryPending { pub struct MercuryFuture(oneshot::Receiver>); impl Future for MercuryFuture { - type Item = T; - type Error = MercuryError; + type Output = Result; - fn poll(&mut self) -> Poll { + fn poll(&mut self) -> Poll { match self.0.poll() { - Ok(Async::Ready(Ok(value))) => Ok(Async::Ready(value)), - Ok(Async::Ready(Err(err))) => Err(err), - Ok(Async::NotReady) => Ok(Async::NotReady), + Poll::Ready(Ok(Ok(value))) => Poll::Ready(Ok(value)), + Poll::Ready(Ok(Err(err))) => Err(err), + Poll::Pending => Poll::Pending, Err(oneshot::Canceled) => Err(MercuryError), } } @@ -98,11 +101,10 @@ impl MercuryManager { MercurySender::new(self.clone(), uri.into()) } - pub fn subscribe>( + pub async fn subscribe>( &self, uri: T, - ) -> Box, Error = MercuryError>> - { + ) -> Result, MercuryError> { let uri = uri.into(); let request = self.request(MercuryRequest { method: MercuryMethod::SUB, @@ -112,7 +114,7 @@ impl MercuryManager { }); let manager = self.clone(); - Box::new(request.map(move |response| { + request.await.map(move |response| { let (tx, rx) = mpsc::unbounded(); manager.lock(move |inner| { @@ -137,7 +139,7 @@ impl MercuryManager { }); rx - })) + }) } pub(crate) fn dispatch(&self, cmd: u8, mut data: Bytes) { diff --git a/core/src/mercury/sender.rs b/core/src/mercury/sender.rs index f00235e..f406a52 100644 --- a/core/src/mercury/sender.rs +++ b/core/src/mercury/sender.rs @@ -1,7 +1,11 @@ -use futures::{Async, AsyncSink, Future, Poll, Sink, StartSend}; +use futures::{Future, Sink}; use std::collections::VecDeque; use super::*; +use std::{ + pin::Pin, + task::{Context, Poll}, +}; pub struct MercurySender { mercury: MercuryManager, @@ -30,25 +34,23 @@ impl Clone for MercurySender { } } -impl Sink for MercurySender { - type SinkItem = Vec; - type SinkError = MercuryError; +type SinkItem = Vec; +impl Sink for MercurySender { + type Error = MercuryError; - fn start_send(&mut self, item: Self::SinkItem) -> StartSend { + fn start_send(self: Pin<&mut Self>, item: SinkItem) -> Result<(), Self::Error> { let task = self.mercury.send(self.uri.clone(), item); self.pending.push_back(task); - Ok(AsyncSink::Ready) + Poll::Ready(Ok(())) } - fn poll_complete(&mut self) -> Poll<(), Self::SinkError> { + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { loop { match self.pending.front_mut() { Some(task) => { - try_ready!(task.poll()); - } - None => { - return Ok(Async::Ready(())); + ready!(task.poll()); } + None => return Poll::Ready(Ok(())), } self.pending.pop_front(); } diff --git a/core/src/proxytunnel.rs b/core/src/proxytunnel.rs index e8fb137..fbc17f6 100644 --- a/core/src/proxytunnel.rs +++ b/core/src/proxytunnel.rs @@ -1,49 +1,61 @@ use std::io; use std::str::FromStr; -use futures::{Async, Future, Poll}; use httparse; use hyper::Uri; -use tokio_io::io::{read, write_all, Read, Window, WriteAll}; -use tokio_io::{AsyncRead, AsyncWrite}; +// use tokio_io::io::{read, write_all, Read, Window, WriteAll}; +// use tokio_io::{AsyncRead, AsyncWrite}; -pub struct ProxyTunnel { - state: ProxyState, +use futures::{ + io::{Read, Window, WriteAll}, + AsyncRead, AsyncWrite, Future, +}; +use std::{ + pin::Pin, + task::{Context, Poll}, +}; +// use tokio::io::{AsyncReadExt, AsyncWriteExt}; + +pub struct ProxyTunnel<'a, T> { + state: ProxyState<'a, T>, } -enum ProxyState { - ProxyConnect(WriteAll>), - ProxyResponse(Read>>), +enum ProxyState<'a, T> { + ProxyConnect(WriteAll<'a, T>), + ProxyResponse(Read<'a, T>), } -pub fn connect(connection: T, connect_url: &str) -> ProxyTunnel { +pub fn connect<'a, T: AsyncRead + AsyncWrite>( + connection: T, + connect_url: &str, +) -> ProxyTunnel<'a, T> { let proxy = proxy_connect(connection, connect_url); ProxyTunnel { state: ProxyState::ProxyConnect(proxy), } } -impl Future for ProxyTunnel { - type Item = T; - type Error = io::Error; +impl<'a, T: AsyncRead + AsyncWrite> Future for ProxyTunnel<'a, T> { + type Output = Result; - fn poll(&mut self) -> Poll { + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { use self::ProxyState::*; loop { self.state = match self.state { ProxyConnect(ref mut write) => { - let (connection, mut accumulator) = try_ready!(write.poll()); + let (connection, mut accumulator) = ready!(write.poll()); let capacity = accumulator.capacity(); accumulator.resize(capacity, 0); let window = Window::new(accumulator); - let read = read(connection, window); - ProxyResponse(read) + // let read = read(connection, window); + // ProxyResponse(read) + ProxyResponse(connection.read(window)) } ProxyResponse(ref mut read_f) => { - let (connection, mut window, bytes_read) = try_ready!(read_f.poll()); + let (connection, mut window, bytes_read) = ready!(read_f.poll()); if bytes_read == 0 { return Err(io::Error::new(io::ErrorKind::Other, "Early EOF from proxy")); @@ -65,7 +77,7 @@ impl Future for ProxyTunnel { if let Some(code) = response.code { if code == 200 { // Proxy says all is well - return Ok(Async::Ready(connection)); + return Poll::Ready(connection); } else { let reason = response.reason.unwrap_or("no reason"); let msg = format!("Proxy responded with {}: {}", code, reason); @@ -87,8 +99,9 @@ impl Future for ProxyTunnel { } // We did not get a full header window.set_start(data_end); - let read = read(connection, window); - ProxyResponse(read) + // let read = read(connection, window); + // ProxyResponse(read) + ProxyResponse(connection.read(window)) } } } @@ -96,7 +109,7 @@ impl Future for ProxyTunnel { } } -fn proxy_connect(connection: T, connect_url: &str) -> WriteAll> { +fn proxy_connect(connection: T, connect_url: &str) -> WriteAll { let uri = Uri::from_str(connect_url).unwrap(); let buffer = format!( "CONNECT {0}:{1} HTTP/1.1\r\n\ @@ -106,5 +119,6 @@ fn proxy_connect(connection: T, connect_url: &str) -> WriteAll); +// TODO: Define better errors! +type Result = std::result::Result>; + impl Session { - pub fn connect( + pub async fn connect( config: SessionConfig, credentials: Credentials, cache: Option, handle: Handle, - ) -> Box> { - let access_point = apresolve_or_fallback::(&config.proxy, &config.ap_port); - - let proxy = config.proxy.clone(); - let connection = access_point.and_then(move |addr| { - info!("Connecting to AP \"{}\"", addr); - connection::connect(addr, &proxy) - }); - - let device_id = config.device_id.clone(); - let authentication = connection.and_then(move |connection| { - connection::authenticate(connection, credentials, device_id) - }); - - let result = authentication.map(move |(transport, reusable_credentials)| { - info!("Authenticated as \"{}\" !", reusable_credentials.username); - if let Some(ref cache) = cache { - cache.save_credentials(&reusable_credentials); - } - - let (session, task) = Session::create( - &handle, - transport, - config, - cache, - reusable_credentials.username.clone(), - ); - - current_thread::spawn(task.map_err(|e| { - error!("SessionError: {}", e.to_string()); - std::process::exit(0); - })); - - session - }); - - Box::new(result) + ) -> Result { + unimplemented!() + // let access_point_addr = + // apresolve_or_fallback::(&config.proxy, &config.ap_port).await?; + // + // let proxy = config.proxy.clone(); + // info!("Connecting to AP \"{}\"", access_point_addr); + // let connection = connection::connect(access_point_addr, &proxy); + // + // let device_id = config.device_id.clone(); + // let authentication = connection.and_then(move |connection| { + // connection::authenticate(connection, credentials, device_id) + // }); + // + // let result = authentication.map(move |(transport, reusable_credentials)| { + // info!("Authenticated as \"{}\" !", reusable_credentials.username); + // if let Some(ref cache) = cache { + // cache.save_credentials(&reusable_credentials); + // } + // + // let (session, task) = Session::create( + // &handle, + // transport, + // config, + // cache, + // reusable_credentials.username.clone(), + // ); + // + // tokio::spawn(task.map_err(|e| { + // error!("SessionError: {}", e.to_string()); + // std::process::exit(0); + // })); + // + // session + // }); + // + // result } fn create( @@ -97,7 +115,7 @@ impl Session { config: SessionConfig, cache: Option, username: String, - ) -> (Session, Box>) { + ) -> (Session, Box>>) { let (sink, stream) = transport.split(); let (sender_tx, sender_rx) = mpsc::unbounded(); @@ -160,7 +178,7 @@ impl Session { // Spawn a future directly pub fn spawn(&self, f: F) where - F: Future + Send + 'static, + F: Future + Send + 'static, { let handle = self.0.handle.lock().unwrap(); let spawn_res = handle.spawn(f); @@ -293,34 +311,35 @@ impl Drop for SessionInternal { } } +// type SErr = ::std::fmt::Debug; + struct DispatchTask(S, SessionWeak) where - S: Stream; + S: Stream>; impl Future for DispatchTask where - S: Stream, - ::Error: ::std::fmt::Debug, + // SErr: ::std::fmt::Debug, + S: Stream>, { - type Item = (); - type Error = S::Error; + type Output = Result<((), ())>; - fn poll(&mut self) -> Poll { + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { let session = match self.1.try_upgrade() { Some(session) => session, - None => return Ok(Async::Ready(())), + None => return Poll::Ready(()), }; loop { - let (cmd, data) = match self.0.poll() { - Ok(Async::Ready(Some(t))) => t, - Ok(Async::Ready(None)) => { + let (cmd, data) = match self.unwrap().0.poll() { + Poll::Ready(Ok(Some(t))) => t, + Poll::Ready(Ok(None)) => { warn!("Connection to server closed."); session.shutdown(); - return Ok(Async::Ready(())); + return Ok(Poll::Ready(())); } - Ok(Async::NotReady) => return Ok(Async::NotReady), - Err(e) => { + Poll::Pending => return Poll::Pending, + Poll::Ready(Err(e)) => { session.shutdown(); return Err(From::from(e)); } @@ -333,7 +352,7 @@ where impl Drop for DispatchTask where - S: Stream, + S: Stream>, { fn drop(&mut self) { debug!("drop Dispatch"); diff --git a/core/tests/connect.rs b/core/tests/connect.rs new file mode 100644 index 0000000..388db25 --- /dev/null +++ b/core/tests/connect.rs @@ -0,0 +1,23 @@ +use env_logger; +use std::env; +use tokio::runtime::Runtime; + +use librespot_core::{apresolve::apresolve_or_fallback, connection}; + +// TODO: Rewrite this into an actual test instead of this wonder +fn main() { + env_logger::init(); + let mut rt = Runtime::new().unwrap(); + + let args: Vec<_> = env::args().collect(); + if args.len() != 4 { + println!("Usage: {} USERNAME PASSWORD PLAYLIST", args[0]); + } + // let username = args[1].to_owned(); + // let password = args[2].to_owned(); + + let ap = rt.block_on(apresolve_or_fallback(&None, &Some(80))); + + println!("AP: {:?}", ap); + let connection = rt.block_on(connection::connect(&None)); +} From 94fc0a12da8c9bdc9358a7c255e138a7aebb734b Mon Sep 17 00:00:00 2001 From: ashthespy Date: Sat, 23 Jan 2021 22:21:42 +0000 Subject: [PATCH 008/103] [Core/connection] Refactor to async/await --- core/Cargo.toml | 2 +- core/src/connection/codec.rs | 2 +- core/src/connection/handshake.rs | 226 ++++++++++--------------------- core/src/connection/mod.rs | 64 ++++----- core/tests/connect.rs | 58 +++++--- 5 files changed, 139 insertions(+), 213 deletions(-) diff --git a/core/Cargo.toml b/core/Cargo.toml index 8c9475a..a76b45a 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -15,7 +15,7 @@ version = "0.1.3" [dependencies] base64 = "0.13" byteorder = "1.3" -bytes = "0.4" +bytes = "0.5" error-chain = { version = "0.12", default_features = false } futures = {version = "0.3",features =["unstable","bilock"]} httparse = "1.3" diff --git a/core/src/connection/codec.rs b/core/src/connection/codec.rs index 47e1163..1417a5c 100644 --- a/core/src/connection/codec.rs +++ b/core/src/connection/codec.rs @@ -45,7 +45,7 @@ impl Encoder for APCodec { buf.reserve(3 + payload.len()); buf.put_u8(cmd); - buf.put_u16_be(payload.len() as u16); + buf.put_u16(payload.len() as u16); buf.extend_from_slice(&payload); self.encode_cipher.nonce_u32(self.encode_nonce); diff --git a/core/src/connection/handshake.rs b/core/src/connection/handshake.rs index 512f61c..39bce7c 100644 --- a/core/src/connection/handshake.rs +++ b/core/src/connection/handshake.rs @@ -1,97 +1,71 @@ -use byteorder::{BigEndian, ByteOrder, WriteBytesExt}; +use super::codec::APCodec; +use crate::{ + diffie_hellman::DHLocalKeys, + protocol, + protocol::keyexchange::{APResponseMessage, ClientHello, ClientResponsePlaintext}, + util, +}; + use hmac::{Hmac, Mac}; use protobuf::{self, Message}; use rand::thread_rng; use sha1::Sha1; -use std::io::{self, Read}; -use std::marker::PhantomData; -// use tokio_codec::{Decoder, Framed}; -// use tokio_io::io::{read_exact, write_all, ReadExact, Window, WriteAll}; -// use tokio_io::{AsyncRead, AsyncWrite}; - -use super::codec::APCodec; -use crate::diffie_hellman::DHLocalKeys; -use crate::protocol; -use crate::protocol::keyexchange::{APResponseMessage, ClientHello, ClientResponsePlaintext}; -use crate::util; - -use futures::{ - io::{ReadExact, Window, WriteAll}, - Future, -}; -use std::{ - pin::Pin, - task::{Context, Poll}, -}; -use tokio::io::{AsyncRead, AsyncWrite, AsyncWriteExt}; - +use std::{io, marker::Unpin}; +use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; use tokio_util::codec::{Decoder, Framed}; -pub struct Handshake<'a, T> { - keys: DHLocalKeys, - state: HandshakeState<'a, T>, -} +// struct handshake { +// keys: DHLocalKeys, +// connection: T, +// accumulator: Vec, +// } -enum HandshakeState<'a, T> { - ClientHello(WriteAll<'a, T>), - APResponse(RecvPacket<'a, T, APResponseMessage>), - ClientResponse(Option, WriteAll<'a, T>), -} - -pub fn handshake<'a, T: AsyncRead + AsyncWrite>(connection: T) -> Handshake<'a, T> { +pub async fn handshake( + mut connection: T, +) -> Result, io::Error> { let local_keys = DHLocalKeys::random(&mut thread_rng()); - let client_hello = client_hello(connection, local_keys.public_key()); + // Send ClientHello + let client_hello: Vec = client_hello(local_keys.public_key()).await?; + connection.write_all(&client_hello).await?; - Handshake { - keys: local_keys, - state: HandshakeState::ClientHello(client_hello), - } + // Receive APResponseMessage + let size = connection.read_u32().await?; + let mut buffer = Vec::with_capacity(size as usize - 4); + let bytes = connection.read_buf(&mut buffer).await?; + let message = protobuf::parse_from_bytes::(&buffer[..bytes])?; + + let mut accumulator = client_hello.clone(); + accumulator.extend_from_slice(&size.to_be_bytes()); + accumulator.extend_from_slice(&buffer); + let remote_key = message + .get_challenge() + .get_login_crypto_challenge() + .get_diffie_hellman() + .get_gs() + .to_owned(); + + // Solve the challenge + let shared_secret = local_keys.shared_secret(&remote_key); + let (challenge, send_key, recv_key) = compute_keys(&shared_secret, &accumulator); + let codec = APCodec::new(&send_key, &recv_key); + + let buffer: Vec = client_response(challenge).await?; + connection.write_all(&buffer).await?; + let framed = codec.framed(connection); + Ok(framed) } -impl<'a, T: AsyncRead + AsyncWrite> Future for Handshake<'a, T> { - type Output = Result, io::Error>; +// async fn recv_packet( +// mut connection: T, +// ) -> Result<(Message, &Vec), io::Error> { +// let size = connection.read_u32().await?; +// let mut buffer = Vec::with_capacity(size as usize - 4); +// let bytes = connection.read_buf(&mut buffer).await?; +// let proto = protobuf::parse_from_bytes(&buffer[..bytes])?; +// Ok(proto) +// } - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { - use self::HandshakeState::*; - loop { - self.state = match self.state { - ClientHello(ref mut write) => { - let (connection, accumulator) = ready!(write.poll()); - - let read = recv_packet(connection, accumulator); - APResponse(read) - } - - APResponse(ref mut read) => { - let (connection, message, accumulator) = ready!(read.poll()); - let remote_key = message - .get_challenge() - .get_login_crypto_challenge() - .get_diffie_hellman() - .get_gs() - .to_owned(); - - let shared_secret = self.keys.shared_secret(&remote_key); - let (challenge, send_key, recv_key) = - compute_keys(&shared_secret, &accumulator); - let codec = APCodec::new(&send_key, &recv_key); - - let write = client_response(connection, challenge); - ClientResponse(Some(codec), write) - } - - ClientResponse(ref mut codec, ref mut write) => { - let (connection, _) = ready!(write.poll()); - let codec = codec.take().unwrap(); - let framed = codec.framed(connection); - return Poll::Ready(Ok(framed)); - } - } - } - } -} - -fn client_hello<'a, T: AsyncWrite>(connection: T, gc: Vec) -> WriteAll<'a, T> { +async fn client_hello(gc: Vec) -> Result, io::Error> { let mut packet = ClientHello::new(); packet .mut_build_info() @@ -99,7 +73,7 @@ fn client_hello<'a, T: AsyncWrite>(connection: T, gc: Vec) -> WriteAll<'a, T packet .mut_build_info() .set_platform(protocol::keyexchange::Platform::PLATFORM_LINUX_X86); - packet.mut_build_info().set_version(109800078); + packet.mut_build_info().set_version(109_800_078); packet .mut_cryptosuites_supported() .push(protocol::keyexchange::Cryptosuite::CRYPTO_SUITE_SHANNON); @@ -114,16 +88,15 @@ fn client_hello<'a, T: AsyncWrite>(connection: T, gc: Vec) -> WriteAll<'a, T packet.set_client_nonce(util::rand_vec(&mut thread_rng(), 0x10)); packet.set_padding(vec![0x1e]); - let mut buffer = vec![0, 4]; let size = 2 + 4 + packet.compute_size(); - buffer.write_u32::(size).unwrap(); - packet.write_to_vec(&mut buffer).unwrap(); - - // write_all(connection, buffer) - connection.write_all(&buffer) + let mut buffer = Vec::with_capacity(size as usize); + buffer.extend(&[0, 4]); + buffer.write_u32(size).await?; + buffer.extend(packet.write_to_bytes()?); + Ok(buffer) } -fn client_response<'a, T: AsyncWrite>(connection: T, challenge: Vec) -> WriteAll<'a, T> { +async fn client_response(challenge: Vec) -> Result, io::Error> { let mut packet = ClientResponsePlaintext::new(); packet .mut_login_crypto_response() @@ -132,73 +105,14 @@ fn client_response<'a, T: AsyncWrite>(connection: T, challenge: Vec) -> Writ packet.mut_pow_response(); packet.mut_crypto_response(); - let mut buffer = vec![]; + // let mut buffer = vec![]; let size = 4 + packet.compute_size(); - buffer.write_u32::(size).unwrap(); - packet.write_to_vec(&mut buffer).unwrap(); - - // write_all(connection, buffer) - connection.write_all(&buffer) -} - -enum RecvPacket<'a, T, M: Message> { - Header(ReadExact<'a, T>, PhantomData), - Body(ReadExact<'a, T>, PhantomData), -} - -fn recv_packet<'a, T: AsyncRead, M>(connection: T, acc: Vec) -> RecvPacket<'a, T, M> -where - T: Read, - M: Message, -{ - RecvPacket::Header(read_into_accumulator(connection, 4, acc), PhantomData) -} - -impl<'a, T: AsyncRead, M> Future for RecvPacket<'a, T, M> -where - T: Read, - M: Message, -{ - type Output = Result<(T, M, Vec), io::Error>; - - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { - use self::RecvPacket::*; - loop { - *self = match *self { - Header(ref mut read, _) => { - let (connection, header) = ready!(read.poll()); - let size = BigEndian::read_u32(header.as_ref()) as usize; - - let acc = header.into_inner(); - let read = read_into_accumulator(connection, size - 4, acc); - RecvPacket::Body(read, PhantomData) - } - - Body(ref mut read, _) => { - let (connection, data) = ready!(read.poll()); - let message = protobuf::parse_from_bytes(data.as_ref()).unwrap(); - - let acc = data.into_inner(); - return Poll::Ready(Ok((connection, message, acc))); - } - } - } - } -} - -fn read_into_accumulator<'a, T: AsyncRead>( - connection: T, - size: usize, - mut acc: Vec, -) -> ReadExact<'a, T> { - let offset = acc.len(); - acc.resize(offset + size, 0); - - let mut window = Window::new(acc); - window.set_start(offset); - - // read_exact(connection, window) - connection.read_exact(window) + let mut buffer = Vec::with_capacity(size as usize); + buffer.write_u32(size).await?; + // This seems to reallocate + // packet.write_to_vec(&mut buffer)?; + buffer.extend(packet.write_to_bytes()?); + Ok(buffer) } fn compute_keys(shared_secret: &[u8], packets: &[u8]) -> (Vec, Vec, Vec) { diff --git a/core/src/connection/mod.rs b/core/src/connection/mod.rs index 21550de..6540f4f 100644 --- a/core/src/connection/mod.rs +++ b/core/src/connection/mod.rs @@ -1,48 +1,39 @@ mod codec; mod handshake; -pub use self::codec::APCodec; -pub use self::handshake::handshake; -use tokio::net::TcpStream; +pub use self::{codec::APCodec, handshake::handshake}; +use crate::{authentication::Credentials, version}; -use futures::{AsyncRead, AsyncWrite, Future, Sink, SinkExt, Stream, StreamExt}; +use futures::{SinkExt, StreamExt}; use protobuf::{self, Message}; -use std::io; -use std::net::ToSocketAddrs; +use std::{io, net::ToSocketAddrs}; +use tokio::net::TcpStream; use tokio_util::codec::Framed; -// use futures::compat::{AsyncWrite01CompatExt, AsyncRead01CompatExt}; -// use tokio_util::compat::{self, Tokio02AsyncReadCompatExt, Tokio02AsyncWriteCompatExt}; -// use tokio_codec::Framed; -// use tokio_core::net::TcpStream; -// use tokio_core::reactor::Handle; use url::Url; -use crate::authentication::Credentials; -use crate::version; - -use crate::proxytunnel; +// use crate::proxytunnel; pub type Transport = Framed; pub async fn connect(addr: String, proxy: &Option) -> Result { let (addr, connect_url): (_, Option) = match *proxy { Some(ref url) => { - unimplemented!() - // info!("Using proxy \"{}\"", url); - // - // let mut iter = url.to_socket_addrs()?; - // let socket_addr = iter.next().ok_or(io::Error::new( - // io::ErrorKind::NotFound, - // "Can't resolve proxy server address", - // ))?; - // (socket_addr, Some(addr)) + info!("Using proxy \"{}\"", url); + + let mut iter = url.to_socket_addrs()?; + let socket_addr = iter.next().ok_or_else(|| { + io::Error::new( + io::ErrorKind::NotFound, + "Can't resolve proxy server address", + ) + })?; + (socket_addr, Some(addr)) } None => { let mut iter = addr.to_socket_addrs()?; - let socket_addr = iter.next().ok_or(io::Error::new( - io::ErrorKind::NotFound, - "Can't resolve server address", - ))?; + let socket_addr = iter.next().ok_or_else(|| { + io::Error::new(io::ErrorKind::NotFound, "Can't resolve server address") + })?; (socket_addr, None) } }; @@ -54,8 +45,7 @@ pub async fn connect(addr: String, proxy: &Option) -> Result Result<(Transport, Credentials), io::Error> { - use crate::protocol::authentication::{APWelcome, ClientResponseEncrypted, CpuFamily, Os}; - use crate::protocol::keyexchange::APLoginFailed; + use crate::protocol::{ + authentication::{APWelcome, ClientResponseEncrypted, CpuFamily, Os}, + keyexchange::APLoginFailed, + }; let mut packet = ClientResponseEncrypted::new(); packet @@ -94,13 +86,11 @@ pub async fn authenticate( let cmd: u8 = 0xab; let data = packet.write_to_bytes().unwrap(); - transport.send((cmd, data)).await; + transport.send((cmd, data)).await?; let packet = transport.next().await; - // let (packet, transport) = transport - // .into_future() - // .map_err(|(err, _stream)| err) - // .await?; + + // TODO: Don't panic? match packet { Some(Ok((0xac, data))) => { let welcome_data: APWelcome = protobuf::parse_from_bytes(data.as_ref()).unwrap(); diff --git a/core/tests/connect.rs b/core/tests/connect.rs index 388db25..abc75a0 100644 --- a/core/tests/connect.rs +++ b/core/tests/connect.rs @@ -1,23 +1,45 @@ -use env_logger; -use std::env; -use tokio::runtime::Runtime; +use futures::future::TryFutureExt; +use librespot_core::*; +use tokio::runtime; -use librespot_core::{apresolve::apresolve_or_fallback, connection}; - -// TODO: Rewrite this into an actual test instead of this wonder -fn main() { - env_logger::init(); - let mut rt = Runtime::new().unwrap(); - - let args: Vec<_> = env::args().collect(); - if args.len() != 4 { - println!("Usage: {} USERNAME PASSWORD PLAYLIST", args[0]); +#[cfg(test)] +mod tests { + use super::*; + // Test AP Resolve + use apresolve::apresolve_or_fallback; + #[test] + fn test_ap_resolve() { + let mut rt = runtime::Runtime::new().unwrap(); + let ap = rt.block_on(apresolve_or_fallback(&None, &Some(80))); + println!("AP: {:?}", ap); } - // let username = args[1].to_owned(); - // let password = args[2].to_owned(); - let ap = rt.block_on(apresolve_or_fallback(&None, &Some(80))); + // Test connect + use authentication::Credentials; + use config::SessionConfig; + use connection; + #[test] + fn test_connection() { + println!("Running connection test"); + let mut rt = runtime::Runtime::new().unwrap(); + let access_point_addr = rt.block_on(apresolve_or_fallback(&None, &None)).unwrap(); + let credentials = Credentials::with_password(String::from("test"), String::from("test")); + let session_config = SessionConfig::default(); + let proxy = None; - println!("AP: {:?}", ap); - let connection = rt.block_on(connection::connect(&None)); + println!("Connecting to AP \"{}\"", access_point_addr); + let connection = connection::connect(access_point_addr, &proxy); + + let device_id = session_config.device_id.clone(); + let authentication = connection.and_then(move |connection| { + connection::authenticate(connection, credentials, device_id) + }); + match rt.block_on(authentication) { + Ok((_transport, reusable_credentials)) => { + println!("Authenticated as \"{}\" !", reusable_credentials.username) + } + // TODO assert that we get BadCredentials once we don't panic + Err(e) => println!("ConnectError: {:?}", e), + } + } } From c273d51a1df00d73935538c3952ef016216fc131 Mon Sep 17 00:00:00 2001 From: ashthespy Date: Sat, 23 Jan 2021 22:21:42 +0000 Subject: [PATCH 009/103] [AudioKeyManager] Convert to async --- core/Cargo.toml | 1 + core/src/audio_key.rs | 41 +++++++++++++++-------------------------- 2 files changed, 16 insertions(+), 26 deletions(-) diff --git a/core/Cargo.toml b/core/Cargo.toml index a76b45a..077efe0 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -14,6 +14,7 @@ version = "0.1.3" [dependencies] base64 = "0.13" +thiserror = "1.0" byteorder = "1.3" bytes = "0.5" error-chain = { version = "0.12", default_features = false } diff --git a/core/src/audio_key.rs b/core/src/audio_key.rs index 39eef72..976361d 100644 --- a/core/src/audio_key.rs +++ b/core/src/audio_key.rs @@ -1,13 +1,9 @@ use byteorder::{BigEndian, ByteOrder, WriteBytesExt}; use bytes::Bytes; +use futures::channel::oneshot; use std::collections::HashMap; use std::io::Write; - -use futures::{channel::oneshot, Future}; -use std::{ - pin::Pin, - task::{Context, Poll}, -}; +use thiserror::Error; use crate::spotify_id::{FileId, SpotifyId}; use crate::util::SeqGenerator; @@ -15,8 +11,13 @@ use crate::util::SeqGenerator; #[derive(Debug, Hash, PartialEq, Eq, Copy, Clone)] pub struct AudioKey(pub [u8; 16]); -#[derive(Debug, Hash, PartialEq, Eq, Copy, Clone)] -pub struct AudioKeyError; +#[derive(Error, Debug)] +pub enum AudioKeyError { + #[error("AudioKey sender disconnected")] + Cancelled(#[from] oneshot::Canceled), + #[error("Unknown server response: `{0:?}`")] + UnknownResponse(Vec), +} component! { AudioKeyManager : AudioKeyManagerInner { @@ -44,14 +45,16 @@ impl AudioKeyManager { data.as_ref()[0], data.as_ref()[1] ); - let _ = sender.send(Err(AudioKeyError)); + let _ = sender.send(Err(AudioKeyError::UnknownResponse( + data.as_ref()[..1].to_vec(), + ))); } - _ => (), + _ => warn!("Unexpected audioKey response: 0x{:x?} {:?}", cmd, data), } } } - pub fn request(&self, track: SpotifyId, file: FileId) -> AudioKeyFuture { + pub async fn request(&self, track: SpotifyId, file: FileId) -> Result { let (tx, rx) = oneshot::channel(); let seq = self.lock(move |inner| { @@ -61,7 +64,7 @@ impl AudioKeyManager { }); self.send_key_request(seq, track, file); - AudioKeyFuture(rx) + rx.await? } fn send_key_request(&self, seq: u32, track: SpotifyId, file: FileId) { @@ -74,17 +77,3 @@ impl AudioKeyManager { self.session().send_packet(0xc, data) } } - -pub struct AudioKeyFuture(oneshot::Receiver>); -impl Future for AudioKeyFuture { - type Output = Result; - - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { - match self.0.poll() { - Poll::Ready(Ok(Ok(value))) => Poll::Ready(Ok(value)), - Poll::Ready(Ok(Err(err))) => Err(err), - Poll::Pending => Poll::Pending, - Err(oneshot::Canceled) => Err(AudioKeyError), - } - } -} From 20dd94fe2079e998480a72e04acd50ab46a2393b Mon Sep 17 00:00:00 2001 From: ashthespy Date: Sat, 23 Jan 2021 22:21:42 +0000 Subject: [PATCH 010/103] Fix tokio dependency in main --- Cargo.lock | 3346 +++++++++++++++++++++++++++++----------------------- Cargo.toml | 4 +- 2 files changed, 1876 insertions(+), 1474 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c4fc5c4..470dd32 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,3506 +2,3908 @@ # It is not intended for manual editing. [[package]] name = "addr2line" -version = "0.14.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a55f82cfe485775d02112886f4169bde0c5894d75e79ead7eafe7e40a25e45f7" dependencies = [ - "gimli 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gimli", ] [[package]] name = "adler" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e" [[package]] name = "adler32" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" [[package]] name = "aes" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54eb1d8fe354e5fc611daf4f2ea97dd45a765f4f1e4512306ec183ae2e8f20c9" dependencies = [ - "aes-soft 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "aesni 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "aes-soft", + "aesni", + "block-cipher-trait", ] [[package]] name = "aes-ctr" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2e5b0458ea3beae0d1d8c0f3946564f8e10f90646cf78c06b4351052058d1ee" dependencies = [ - "aes-soft 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "aesni 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ctr 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "stream-cipher 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "aes-soft", + "aesni", + "ctr", + "stream-cipher", ] [[package]] name = "aes-soft" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfd7e7ae3f9a1fb5c03b389fc6bb9a51400d0c13053f0dca698c832bfd893a0d" dependencies = [ - "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "block-cipher-trait", + "byteorder", + "opaque-debug", ] [[package]] name = "aesni" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f70a6b5f971e473091ab7cfb5ffac6cde81666c4556751d8d5620ead8abf100" dependencies = [ - "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "stream-cipher 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "block-cipher-trait", + "opaque-debug", + "stream-cipher", ] [[package]] name = "alsa" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4a0d4ebc8b23041c5de9bc9aee13b4bad844a589479701f31a5934cfe4aeb32" dependencies = [ - "alsa-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "nix 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "alsa-sys 0.1.2", + "bitflags 0.9.1", + "libc", + "nix 0.9.0", ] [[package]] name = "alsa" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb213f6b3e4b1480a60931ca2035794aa67b73103d254715b1db7b70dcb3c934" dependencies = [ - "alsa-sys 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "nix 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", + "alsa-sys 0.3.1", + "bitflags 1.2.1", + "libc", + "nix 0.15.0", ] [[package]] name = "alsa-sys" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0edcbbf9ef68f15ae1b620f722180b82a98b6f0628d30baa6b8d2a5abc87d58" dependencies = [ - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "pkg-config", ] [[package]] name = "alsa-sys" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db8fee663d06c4e303404ef5f40488a53e062f89ba8bfed81f42325aafad1527" dependencies = [ - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "pkg-config", ] [[package]] name = "anyhow" -version = "1.0.35" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "arc-swap" -version = "0.4.7" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afddf7f520a80dbf76e6f50a35bca42a2331ef227a28b3b6dc5c2e2338d114b1" [[package]] name = "ascii" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eab1c04a571841102f5345a8fc0f6bb3d31c315dec879b5c6e42e40ce7ffa34e" [[package]] name = "atty" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "hermit-abi", + "libc", + "winapi 0.3.9", ] [[package]] name = "autocfg" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" [[package]] name = "backtrace" -version = "0.3.55" +version = "0.3.56" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d117600f438b1707d4e4ae15d3595657288f8235a0eb593e80ecc98ab34e1bc" dependencies = [ - "addr2line 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "miniz_oxide 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "object 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-demangle 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", + "addr2line", + "cfg-if 1.0.0", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", ] +[[package]] +name = "base-x" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4521f3e3d031370679b3b140beb36dfe4801b09ac77e30c61941f97df3ef28b" + [[package]] name = "base64" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643" dependencies = [ - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "safemem 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "base64" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "safemem", ] [[package]] name = "base64" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" [[package]] name = "base64" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" [[package]] name = "bindgen" -version = "0.53.3" +version = "0.56.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2da379dbebc0b76ef63ca68d8fc6e71c0f13e59432e0987e508c1820e6ab5239" dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "cexpr 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "clang-sys 0.29.3 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hash 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "shlex 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "peeking_take_while", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", ] [[package]] name = "bit-set" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de" dependencies = [ - "bit-vec 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "bit-vec", ] [[package]] name = "bit-vec" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" [[package]] name = "bitflags" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" [[package]] name = "bitflags" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "block-buffer" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" dependencies = [ - "block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", + "block-padding", + "byte-tools", + "byteorder", + "generic-array", ] [[package]] name = "block-cipher-trait" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c924d49bd09e7c06003acda26cd9742e796e34282ec6c1189404dee0c1f4774" dependencies = [ - "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array", ] [[package]] name = "block-modes" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31aa8410095e39fdb732909fb5730a48d5bd7c2e3cd76bd1b07b3dbea130c529" dependencies = [ - "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "block-cipher-trait", + "block-padding", ] [[package]] name = "block-padding" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" dependencies = [ - "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "byte-tools", ] [[package]] name = "bumpalo" -version = "3.4.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f07aa6688c702439a1be0307b6a94dffe1168569e45b9500c1372bc580740d59" [[package]] name = "byte-tools" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" [[package]] name = "byteorder" -version = "1.3.4" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b" [[package]] name = "bytes" version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" dependencies = [ - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "either", + "iovec", ] [[package]] name = "bytes" version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" + +[[package]] +name = "bytes" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" [[package]] name = "cc" -version = "1.0.65" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48" [[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" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4aedb84272dbe89af497cf81375129abda4fc0a9e7c5d317498c15cc30c0d27" dependencies = [ - "nom 5.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "nom", ] [[package]] name = "cfg-if" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.13" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" dependencies = [ - "num-integer 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "num-integer", + "num-traits", + "time 0.1.43", + "winapi 0.3.9", ] [[package]] name = "chunked_transfer" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7477065d45a8fe57167bf3cf8bcd3729b54cfcb81cca49bda2d038ea89ae82ca" [[package]] name = "clang-sys" -version = "0.29.3" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0659001ab56b791be01d4b729c44376edc6718cf389a502e579b77b758f3296c" dependencies = [ - "glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "libloading 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "glob", + "libc", + "libloading", ] [[package]] name = "cloudabi" version = "0.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "cloudabi" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1", ] [[package]] name = "combine" version = "3.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da3da6baa321ec19e1cc41d31bf599f00c783d0517095cdaf0332e3fe8d20680" dependencies = [ - "ascii 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "either 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ascii", + "byteorder", + "either", + "memchr", + "unreachable", ] [[package]] name = "combine" -version = "4.4.0" +version = "4.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc4369b5e4c0cddf64ad8981c0111e7df4f7078f4d6ba98fb31f2e17c4c57b7e" dependencies = [ - "bytes 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-project-lite 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 1.0.1", + "memchr", ] +[[package]] +name = "const_fn" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28b9d6de7f49e22cf97ad17fc4036ece69300032f45f78f30b4a4482cdc3f4a6" + [[package]] name = "cookie" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784ad0fbab4f3e9cef09f20e0aea6000ae08d2cb98ac4c0abc53df18803d702f" +dependencies = [ + "percent-encoding 2.1.0", + "time 0.2.25", + "version_check", +] + +[[package]] +name = "cookie_store" version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3818dfca4b0cb5211a659bbcbb94225b7127407b2b135e650d717bfb78ab10d3" dependencies = [ - "time 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "cookie", + "idna 0.2.0", + "log", + "publicsuffix", + "serde", + "serde_json", + "time 0.2.25", + "url 2.2.0", ] [[package]] name = "core-foundation-sys" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b" [[package]] name = "coreaudio-rs" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f229761965dad3e9b11081668a6ea00f1def7aa46062321b5ec245b834f6e491" dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "coreaudio-sys 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1", + "coreaudio-sys", ] [[package]] name = "coreaudio-sys" -version = "0.2.5" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b7e3347be6a09b46aba228d6608386739fb70beff4f61e07422da87b0bb31fa" dependencies = [ - "bindgen 0.53.3 (registry+https://github.com/rust-lang/crates.io-index)", + "bindgen", ] [[package]] name = "cpal" version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05631e2089dfa5d3b6ea1cfbbfd092e2ee5deeb69698911bc976b28b746d3657" dependencies = [ - "alsa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "coreaudio-rs 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "jni 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", - "js-sys 0.3.46 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "mach 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ndk 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "ndk-glue 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "nix 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", - "oboe 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)", - "stdweb 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "thiserror 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)", - "web-sys 0.3.46 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "alsa 0.4.3", + "core-foundation-sys", + "coreaudio-rs", + "jni 0.17.0", + "js-sys", + "lazy_static", + "libc", + "mach", + "ndk", + "ndk-glue", + "nix 0.15.0", + "oboe", + "parking_lot 0.11.1", + "stdweb 0.1.3", + "thiserror", + "web-sys", + "winapi 0.3.9", ] [[package]] name = "crc32fast" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" dependencies = [ - "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 1.0.0", ] [[package]] name = "crossbeam-deque" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285" dependencies = [ - "crossbeam-epoch 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-epoch", + "crossbeam-utils 0.7.2", + "maybe-uninit", ] [[package]] name = "crossbeam-epoch" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" dependencies = [ - "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "memoffset 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", - "scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg", + "cfg-if 0.1.10", + "crossbeam-utils 0.7.2", + "lazy_static", + "maybe-uninit", + "memoffset", + "scopeguard", ] [[package]] name = "crossbeam-queue" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b" dependencies = [ - "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.6", ] [[package]] name = "crossbeam-queue" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10", + "crossbeam-utils 0.7.2", + "maybe-uninit", ] [[package]] name = "crossbeam-utils" version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10", + "lazy_static", ] [[package]] name = "crossbeam-utils" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" dependencies = [ - "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg", + "cfg-if 0.1.10", + "lazy_static", ] [[package]] name = "crypto-mac" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" dependencies = [ - "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", - "subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array", + "subtle", ] [[package]] name = "ctr" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "022cd691704491df67d25d006fe8eca083098253c4d43516c2206479c58c6736" dependencies = [ - "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "stream-cipher 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "block-cipher-trait", + "stream-cipher", ] [[package]] name = "darling" version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858" dependencies = [ - "darling_core 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", - "darling_macro 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", + "darling_core", + "darling_macro", ] [[package]] name = "darling_core" version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b" dependencies = [ - "fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "ident_case 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "strsim 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)", + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", ] [[package]] name = "darling_macro" version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72" dependencies = [ - "darling_core 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)", + "darling_core", + "quote", + "syn", ] [[package]] name = "derivative" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ - "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "digest" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" dependencies = [ - "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array", ] +[[package]] +name = "discard" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" + [[package]] name = "dns-sd" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d748509dea20228f63ba519bf142ce2593396386125b01f5b0d6412dab972087" dependencies = [ - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "pkg-config", ] [[package]] name = "either" version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" [[package]] name = "env_logger" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26ecb66b4bdca6c1409b40fb255eefc2bd4f6d135dab3c3124f80ffa2a9661e" dependencies = [ - "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "humantime 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "termcolor 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "atty", + "humantime", + "log", + "termcolor", ] [[package]] name = "error-chain" -version = "0.12.2" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" dependencies = [ - "backtrace 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)", - "version_check 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace", + "version_check", ] [[package]] name = "fake-simd" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" [[package]] name = "fetch_unroll" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d44807d562d137f063cbfe209da1c3f9f2fa8375e11166ef495daab7b847f9" dependencies = [ - "libflate 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "tar 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "ureq 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)", + "libflate", + "tar", + "ureq", ] [[package]] name = "filetime" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d34cfa13a63ae058bfa601fe9e313bbdb3746427c1459185464ce0fcf62e1e8" dependencies = [ - "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.57 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 1.0.0", + "libc", + "redox_syscall 0.2.4", + "winapi 0.3.9", ] [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "form_urlencoded" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ece68d15c92e84fa4f19d3780f1294e5ca82a78a6d515f1efaabcc144688be00" dependencies = [ - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "matches", + "percent-encoding 2.1.0", ] [[package]] name = "fuchsia-cprng" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" [[package]] name = "fuchsia-zircon" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1", + "fuchsia-zircon-sys", ] [[package]] name = "fuchsia-zircon-sys" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" [[package]] name = "futures" -version = "0.1.29" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7e4c2612746b0df8fed4ce0c69156021b704c9aefa360311c04e6e9e002eed" + +[[package]] +name = "futures" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da9052a1a50244d8d5aa9bf55cbc2fb6f357c86cc52e46c62ed390a7180cf150" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] [[package]] name = "futures-channel" -version = "0.3.5" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2d31b7ec7efab6eefc7c57233bb10b847986139d88cc2f5a02a1ae6871a1846" dependencies = [ - "futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core", + "futures-sink", ] [[package]] name = "futures-core" -version = "0.3.5" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79e5145dde8da7d1b3892dad07a9c98fc04bc39892b1ecc9692cf53e2b780a65" [[package]] name = "futures-cpupool" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" dependencies = [ - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.30", + "num_cpus", ] [[package]] name = "futures-executor" -version = "0.3.5" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9e59fdc009a4b3096bf94f740a0f2424c082521f20a9b08c5c07c48d90fd9b9" dependencies = [ - "futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-task 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-util 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core", + "futures-task", + "futures-util", ] [[package]] -name = "futures-macro" -version = "0.3.5" +name = "futures-io" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28be053525281ad8259d47e4de5de657b25e7bac113458555bb4b70bc6870500" + +[[package]] +name = "futures-macro" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c287d25add322d9f9abdcdc5927ca398917996600182178774032e9f8258fedd" dependencies = [ - "proc-macro-hack 0.5.16 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "futures-sink" -version = "0.3.5" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caf5c69029bda2e743fddd0582d1083951d65cc9539aebf8812f36c3491342d6" [[package]] name = "futures-task" -version = "0.3.5" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13de07eb8ea81ae445aca7b69f5f7bf15d7bf4912d8ca37d6645c77ae8a58d86" dependencies = [ - "once_cell 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "once_cell", ] [[package]] name = "futures-util" -version = "0.3.5" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "632a8cd0f2a4b3fdea1657f08bde063848c3bd00f9bbf6e256b8be78802e624b" dependencies = [ - "futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-macro 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-task 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-project 0.4.22 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-utils 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-hack 0.5.16 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-nested 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite 0.2.4", + "pin-utils", + "proc-macro-hack", + "proc-macro-nested", + "slab", ] [[package]] name = "gcc" version = "0.3.55" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" [[package]] name = "generic-array" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" dependencies = [ - "typenum 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "typenum", ] [[package]] name = "getopts" version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" dependencies = [ - "unicode-width 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width", ] [[package]] name = "getrandom" -version = "0.1.14" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 1.0.0", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi 0.10.1+wasi-snapshot-preview1", ] [[package]] name = "gimli" version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6503fe142514ca4799d4c26297c4248239fe8838d827db6bd6065c6ed29a6ce" [[package]] name = "glib" version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c685013b7515e668f1b57a165b009d4d28cb139a8a989bbd699c10dad29d0c5" dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-channel 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-executor 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-task 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-util 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "glib-macros 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "glib-sys 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "gobject-sys 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "once_cell 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "futures-util", + "glib-macros", + "glib-sys", + "gobject-sys", + "libc", + "once_cell", ] [[package]] name = "glib-macros" version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41486a26d1366a8032b160b59065a59fb528530a46a49f627e7048fb8c064039" dependencies = [ - "anyhow 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)", - "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "itertools 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-crate 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-error 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)", + "anyhow", + "heck", + "itertools", + "proc-macro-crate", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "glib-sys" version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7e9b997a66e9a23d073f2b1abb4dbfc3925e0b8952f67efd8d9b6e168e4cdc1" dependencies = [ - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "system-deps 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "system-deps", ] [[package]] name = "glob" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" [[package]] name = "gobject-sys" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "952133b60c318a62bf82ee75b93acc7e84028a093e06b9e27981c2b6fe68218c" dependencies = [ - "glib-sys 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "system-deps 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "glib-sys", + "libc", + "system-deps", ] [[package]] name = "gstreamer" version = "0.16.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d50f822055923f1cbede233aa5dfd4ee957cf328fb3076e330886094e11d6cf" dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-channel 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-util 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "glib 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)", - "glib-sys 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "gobject-sys 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gstreamer-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "muldiv 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "num-rational 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "once_cell 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "paste 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "pretty-hex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "thiserror 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1", + "cfg-if 1.0.0", + "futures-channel", + "futures-core", + "futures-util", + "glib", + "glib-sys", + "gobject-sys", + "gstreamer-sys", + "libc", + "muldiv", + "num-rational", + "once_cell", + "paste", + "pretty-hex", + "thiserror", ] [[package]] name = "gstreamer-app" version = "0.16.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc80888271338c3ede875d8cafc452eb207476ff5539dcbe0018a8f5b827af0e" dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-sink 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "glib 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)", - "glib-sys 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "gobject-sys 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gstreamer 0.16.5 (registry+https://github.com/rust-lang/crates.io-index)", - "gstreamer-app-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "gstreamer-base 0.16.5 (registry+https://github.com/rust-lang/crates.io-index)", - "gstreamer-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "once_cell 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1", + "futures-core", + "futures-sink", + "glib", + "glib-sys", + "gobject-sys", + "gstreamer", + "gstreamer-app-sys", + "gstreamer-base", + "gstreamer-sys", + "libc", + "once_cell", ] [[package]] name = "gstreamer-app-sys" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "813f64275c9e7b33b828b9efcf9dfa64b95996766d4de996e84363ac65b87e3d" dependencies = [ - "glib-sys 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "gstreamer-base-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "gstreamer-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "system-deps 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "glib-sys", + "gstreamer-base-sys", + "gstreamer-sys", + "libc", + "system-deps", ] [[package]] name = "gstreamer-base" version = "0.16.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bafd01c56f59cb10f4b5a10f97bb4bdf8c2b2784ae5b04da7e2d400cf6e6afcf" dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "glib 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)", - "glib-sys 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "gobject-sys 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gstreamer 0.16.5 (registry+https://github.com/rust-lang/crates.io-index)", - "gstreamer-base-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "gstreamer-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1", + "glib", + "glib-sys", + "gobject-sys", + "gstreamer", + "gstreamer-base-sys", + "gstreamer-sys", + "libc", ] [[package]] name = "gstreamer-base-sys" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4b7b6dc2d6e160a1ae28612f602bd500b3fa474ce90bf6bb2f08072682beef5" dependencies = [ - "glib-sys 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "gobject-sys 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gstreamer-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "system-deps 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "glib-sys", + "gobject-sys", + "gstreamer-sys", + "libc", + "system-deps", ] [[package]] name = "gstreamer-sys" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1f154082d01af5718c5f8a8eb4f565a4ea5586ad8833a8fc2c2aa6844b601d" dependencies = [ - "glib-sys 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "gobject-sys 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "system-deps 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", ] [[package]] -name = "heck" -version = "0.3.1" +name = "h2" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5b34c246847f938a410a03c5458c7fee2274436675e76d8b903c08efc29c462" dependencies = [ - "unicode-segmentation 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "bytes 0.4.12", + "fnv", + "futures 0.1.30", + "http 0.1.21", + "indexmap", + "log", + "slab", + "string", + "tokio-io", +] + +[[package]] +name = "h2" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e4728fd124914ad25e99e3d15a9361a879f6620f63cb56bbb08f95abb97a535" +dependencies = [ + "bytes 0.5.6", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.3", + "indexmap", + "slab", + "tokio 0.2.24", + "tokio-util", + "tracing", + "tracing-futures", +] + +[[package]] +name = "hashbrown" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" + +[[package]] +name = "heck" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cbf45460356b7deeb5e3415b5563308c0a9b057c85e12b06ad551f98d0a6ac" +dependencies = [ + "unicode-segmentation", ] [[package]] name = "hermit-abi" -version = "0.1.15" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" dependencies = [ - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", ] [[package]] name = "hex" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35" [[package]] name = "hmac" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dcb5e64cda4c23119ab41ba960d1e170a774c8e4b9d9e6a9bc18aabf5e59695" dependencies = [ - "crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crypto-mac", + "digest", ] [[package]] name = "hostname" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" dependencies = [ - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "match_cfg 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "match_cfg", + "winapi 0.3.9", +] + +[[package]] +name = "http" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6ccf5ede3a895d8856620237b2f02972c1bbc78d2965ad7fe8838d4a0ed41f0" +dependencies = [ + "bytes 0.4.12", + "fnv", + "itoa", +] + +[[package]] +name = "http" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7245cd7449cc792608c3c8a9eaf69bd4eabbabf802713748fd739c98b82f0747" +dependencies = [ + "bytes 1.0.1", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6741c859c1b2463a423a1dbce98d418e6c3c3fc720fb0d45528657320920292d" +dependencies = [ + "bytes 0.4.12", + "futures 0.1.30", + "http 0.1.21", + "tokio-buf", +] + +[[package]] +name = "http-body" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13d5ff830006f7646652e057693569bfe0d51760c0085a071769d142a205111b" +dependencies = [ + "bytes 0.5.6", + "http 0.2.3", ] [[package]] name = "httparse" version = "1.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" + +[[package]] +name = "httpdate" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47" [[package]] name = "humantime" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.11.27" +version = "0.12.35" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dbe6ed1438e1f8ad955a4701e9a944938e9519f6888d12d8558b645e247d5f6" dependencies = [ - "base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "mime 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)", - "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "relay 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "unicase 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "want 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12", + "futures 0.1.30", + "futures-cpupool", + "h2 0.1.26", + "http 0.1.21", + "http-body 0.1.0", + "httparse", + "iovec", + "itoa", + "log", + "net2", + "rustc_version", + "time 0.1.43", + "tokio 0.1.22", + "tokio-buf", + "tokio-executor", + "tokio-io", + "tokio-reactor", + "tokio-tcp", + "tokio-threadpool", + "tokio-timer", + "want 0.2.0", +] + +[[package]] +name = "hyper" +version = "0.13.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ad767baac13b44d4529fcf58ba2cd0995e36e7b435bc5b039de6f47e880dbf" +dependencies = [ + "bytes 0.5.6", + "futures-channel", + "futures-core", + "futures-util", + "h2 0.2.7", + "http 0.2.3", + "http-body 0.3.1", + "httparse", + "httpdate", + "itoa", + "pin-project 1.0.4", + "socket2", + "tokio 0.2.24", + "tower-service", + "tracing", + "want 0.3.0", ] [[package]] name = "hyper-proxy" -version = "0.4.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cf120ed868e8e0cd22279cc8196c8db126884a5dbb01e0f528018048efd8fee" dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.11.27 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.5.6", + "futures 0.3.12", + "http 0.2.3", + "hyper 0.13.9", + "tokio 0.2.24", + "tower-service", + "typed-headers", ] [[package]] name = "ident_case" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" dependencies = [ - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-normalization 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "matches", + "unicode-bidi", + "unicode-normalization", ] [[package]] name = "idna" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" dependencies = [ - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-normalization 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "matches", + "unicode-bidi", + "unicode-normalization", ] [[package]] name = "if-addrs" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28538916eb3f3976311f5dfbe67b5362d0add1293d0a9cad17debf86f8e3aa48" dependencies = [ - "if-addrs-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "if-addrs-sys", + "libc", + "winapi 0.3.9", ] [[package]] name = "if-addrs-sys" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de74b9dd780476e837e5eb5ab7c88b49ed304126e412030a0adba99c8efe79ea" dependencies = [ - "cc 1.0.65 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", + "cc", + "libc", +] + +[[package]] +name = "indexmap" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb1fa934250de4de8aef298d81c729a7d33d8c239daa3a7575e6b92bfc7313b" +dependencies = [ + "autocfg", + "hashbrown", ] [[package]] name = "instant" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" dependencies = [ - "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 1.0.0", ] [[package]] name = "iovec" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" dependencies = [ - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", ] [[package]] name = "itertools" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" dependencies = [ - "either 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "either", ] [[package]] name = "itoa" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" [[package]] name = "jack" version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c1871c91fa65aa328f3bedbaa54a6e5d1de009264684c153eb708ba933aa6f5" dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "jack-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1", + "jack-sys", + "lazy_static", + "libc", ] [[package]] name = "jack-sys" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d6ab7ada402b6a27912a2b86504be62a48c58313c886fe72a059127acb4d7" dependencies = [ - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "libloading 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static", + "libc", + "libloading", ] [[package]] name = "jni" version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1981310da491a4f0f815238097d0d43d8072732b5ae5f8bd0d8eadf5bf245402" dependencies = [ - "cesu8 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "combine 3.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "error-chain 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "jni-sys 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cesu8", + "combine 3.8.1", + "error-chain", + "jni-sys", + "log", + "walkdir", ] [[package]] name = "jni" version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36bcc950632e48b86da402c5c077590583da5ac0d480103611d5374e7c967a3c" dependencies = [ - "cesu8 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "combine 4.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "error-chain 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "jni-sys 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cesu8", + "combine 4.5.2", + "error-chain", + "jni-sys", + "log", + "walkdir", ] [[package]] name = "jni-sys" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "js-sys" version = "0.3.46" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf3d7383929f7c9c7c2d0fa596f325832df98c3704f2c60553080f7127a58175" dependencies = [ - "wasm-bindgen 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen", ] [[package]] name = "kernel32-sys" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" dependencies = [ - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8", + "winapi-build", ] -[[package]] -name = "language-tags" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "lazycell" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "lewton" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d542c1a317036c45c2aa1cf10cc9d403ca91eb2d333ef1a4917e5cb10628bd0" dependencies = [ - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "ogg 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "ogg", + "smallvec 0.6.14", ] [[package]] name = "libc" -version = "0.2.73" +version = "0.2.82" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89203f3fba0a3795506acaad8ebce3c80c0af93f994d5a1d7a0b1eeb23271929" [[package]] name = "libflate" -version = "0.1.27" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389de7875e06476365974da3e7ff85d55f1972188ccd9f6020dd7c8156e17914" dependencies = [ - "adler32 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "crc32fast 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rle-decode-fast 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "take_mut 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "adler32", + "crc32fast", + "libflate_lz77", + "rle-decode-fast", ] [[package]] -name = "libloading" -version = "0.4.3" +name = "libflate_lz77" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "3286f09f7d4926fc486334f28d8d2e6ebe4f7f9994494b6dab27ddfad2c9b11b" [[package]] name = "libloading" -version = "0.5.2" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "351a32417a12d5f7e82c368a66781e307834dae04c6ce0cd4456d52989229883" dependencies = [ - "cc 1.0.65 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 1.0.0", + "winapi 0.3.9", ] [[package]] name = "libmdns" version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d8582c174736c53633bc482ac709b24527c018356c3dc6d8e25a788b06b394e" dependencies = [ - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "hostname 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "if-addrs 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "multimap 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)", - "quick-error 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "futures 0.1.30", + "hostname", + "if-addrs", + "log", + "multimap", + "net2", + "quick-error", + "rand 0.7.3", + "tokio-core", ] [[package]] name = "libpulse-binding" -version = "2.19.0" +version = "2.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce89ab17bd343b08bd4321c674ef1477d34f83be18b1ab2ee47a5e5fbee64a91" dependencies = [ - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "libpulse-sys 1.15.3 (registry+https://github.com/rust-lang/crates.io-index)", - "num-derive 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1", + "libc", + "libpulse-sys", + "num-derive", + "num-traits", + "winapi 0.3.9", ] [[package]] name = "libpulse-simple-binding" -version = "2.18.1" +version = "2.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e47f6cda2748fb86f15e5e65cc33be306577140f4b500622b5d98df2ca17240" dependencies = [ - "libpulse-binding 2.19.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libpulse-simple-sys 1.15.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libpulse-sys 1.15.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libpulse-binding", + "libpulse-simple-sys", + "libpulse-sys", ] [[package]] name = "libpulse-simple-sys" -version = "1.15.1" +version = "1.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "468cf582b7b022c0d1b266fefc7fc8fa7b1ddcb61214224f2f105c95a9c2d5c1" dependencies = [ - "libpulse-sys 1.15.3 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", + "libpulse-sys", + "pkg-config", ] [[package]] name = "libpulse-sys" -version = "1.15.3" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fcfb56118765adba111da47e36278b77d00aebf822e4f014a832fbfa183a13b" dependencies = [ - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "num-derive 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "num-derive", + "num-traits", + "pkg-config", + "winapi 0.3.9", ] [[package]] name = "librespot" version = "0.1.3" dependencies = [ - "base64 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", - "hex 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.11.27 (registry+https://github.com/rust-lang/crates.io-index)", - "librespot-audio 0.1.3", - "librespot-connect 0.1.3", - "librespot-core 0.1.3", - "librespot-metadata 0.1.3", - "librespot-playback 0.1.3", - "librespot-protocol 0.1.3", - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "num-bigint 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "protobuf 2.14.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rpassword 5.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sha-1 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-process 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-signal 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "base64 0.13.0", + "env_logger", + "futures 0.1.30", + "getopts", + "hex", + "librespot-audio", + "librespot-connect", + "librespot-core", + "librespot-metadata", + "librespot-playback", + "librespot-protocol", + "log", + "num-bigint", + "protobuf", + "rand 0.7.3", + "rpassword", + "sha-1", + "tokio 0.2.24", + "tokio-io", + "tokio-process", + "tokio-signal", + "url 1.7.2", ] [[package]] name = "librespot-audio" version = "0.1.3" dependencies = [ - "aes-ctr 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "bit-set 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "lewton 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)", - "librespot-core 0.1.3", - "librespot-tremor 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "num-bigint 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "vorbis 0.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "aes-ctr", + "bit-set", + "byteorder", + "bytes 0.4.12", + "futures 0.3.12", + "lewton", + "librespot-core", + "librespot-tremor", + "log", + "num-bigint", + "num-traits", + "tempfile", + "tokio 0.2.24", + "vorbis", ] [[package]] name = "librespot-connect" version = "0.1.3" dependencies = [ - "aes-ctr 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "base64 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", - "block-modes 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "dns-sd 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.11.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libmdns 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", - "librespot-core 0.1.3", - "librespot-playback 0.1.3", - "librespot-protocol 0.1.3", - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "num-bigint 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "protobuf 2.14.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", - "sha-1 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "aes-ctr", + "base64 0.13.0", + "block-modes", + "dns-sd", + "futures 0.1.30", + "hmac", + "hyper 0.12.35", + "libmdns", + "librespot-core", + "librespot-playback", + "librespot-protocol", + "log", + "num-bigint", + "protobuf", + "rand 0.7.3", + "serde", + "serde_derive", + "serde_json", + "sha-1", + "tokio 0.1.22", + "url 1.7.2", ] [[package]] name = "librespot-core" version = "0.1.3" dependencies = [ - "aes 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "base64 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "error-chain 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.11.27 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper-proxy 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "librespot-protocol 0.1.3", - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "num-bigint 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "num-integer 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", - "pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "protobuf 2.14.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", - "sha-1 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", - "shannon 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-codec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "uuid 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "vergen 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "aes", + "base64 0.13.0", + "byteorder", + "bytes 0.5.6", + "error-chain", + "futures 0.3.12", + "hmac", + "httparse", + "hyper 0.13.9", + "hyper-proxy", + "lazy_static", + "librespot-protocol", + "log", + "num-bigint", + "num-integer", + "num-traits", + "pbkdf2", + "protobuf", + "rand 0.7.3", + "serde", + "serde_derive", + "serde_json", + "sha-1", + "shannon", + "thiserror", + "tokio 0.2.24", + "tokio-util", + "url 1.7.2", + "uuid", + "vergen", ] [[package]] name = "librespot-metadata" version = "0.1.3" dependencies = [ - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "librespot-core 0.1.3", - "librespot-protocol 0.1.3", - "linear-map 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "protobuf 2.14.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "futures 0.1.30", + "librespot-core", + "librespot-protocol", + "linear-map", + "log", + "protobuf", ] [[package]] name = "librespot-playback" version = "0.1.3" dependencies = [ - "alsa 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "cpal 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "glib 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)", - "gstreamer 0.16.5 (registry+https://github.com/rust-lang/crates.io-index)", - "gstreamer-app 0.16.5 (registry+https://github.com/rust-lang/crates.io-index)", - "jack 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "libpulse-binding 2.19.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libpulse-simple-binding 2.18.1 (registry+https://github.com/rust-lang/crates.io-index)", - "librespot-audio 0.1.3", - "librespot-core 0.1.3", - "librespot-metadata 0.1.3", - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "portaudio-rs 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rodio 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sdl2 0.34.3 (registry+https://github.com/rust-lang/crates.io-index)", - "shell-words 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "zerocopy 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "alsa 0.2.2", + "byteorder", + "cpal", + "futures 0.1.30", + "glib", + "gstreamer", + "gstreamer-app", + "jack", + "libc", + "libpulse-binding", + "libpulse-simple-binding", + "librespot-audio", + "librespot-core", + "librespot-metadata", + "log", + "portaudio-rs", + "rodio", + "sdl2", + "shell-words", + "zerocopy", ] [[package]] name = "librespot-protocol" version = "0.1.3" dependencies = [ - "glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "protobuf 2.14.0 (registry+https://github.com/rust-lang/crates.io-index)", - "protobuf-codegen 2.14.0 (registry+https://github.com/rust-lang/crates.io-index)", - "protobuf-codegen-pure 2.14.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glob", + "protobuf", + "protobuf-codegen", + "protobuf-codegen-pure", ] [[package]] name = "librespot-tremor" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b155a7dc4e4d272e01c37a1b85c1ee1bee7f04980ad4a7784c1a6e0f2de5929b" dependencies = [ - "cc 1.0.65 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "ogg-sys 0.0.9 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", + "cc", + "libc", + "ogg-sys", + "pkg-config", ] [[package]] name = "linear-map" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfae20f6b19ad527b550c223fddc3077a547fc70cda94b9b566575423fd303ee" [[package]] name = "lock_api" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" dependencies = [ - "scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard", ] [[package]] name = "lock_api" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312" dependencies = [ - "scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard", ] [[package]] name = "log" -version = "0.3.9" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcf3805d4480bb5b86070dcfeb9e2cb2ebc148adb753c5cca5f884d1d65a42b2" dependencies = [ - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "log" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10", ] [[package]] name = "mach" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" dependencies = [ - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", ] [[package]] name = "match_cfg" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" [[package]] name = "matches" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" [[package]] name = "maybe-uninit" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" [[package]] name = "memchr" -version = "2.3.3" +version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" [[package]] name = "memoffset" -version = "0.5.5" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa" dependencies = [ - "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg", ] [[package]] name = "mime" version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" [[package]] name = "miniz_oxide" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f2d26ec3309788e423cfbf68ad1800f061638098d76a83681af979dc4eda19d" dependencies = [ - "adler 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "adler", + "autocfg", ] [[package]] name = "mio" -version = "0.6.22" +version = "0.6.23" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10", + "fuchsia-zircon", + "fuchsia-zircon-sys", + "iovec", + "kernel32-sys", + "libc", + "log", + "miow 0.2.2", + "net2", + "slab", + "winapi 0.2.8", ] [[package]] name = "mio-named-pipes" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0840c1c50fd55e521b247f949c241c9997709f23bd7f023b9762cd561e935656" dependencies = [ - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.22 (registry+https://github.com/rust-lang/crates.io-index)", - "miow 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "log", + "mio", + "miow 0.3.6", + "winapi 0.3.9", ] [[package]] name = "mio-uds" version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0" dependencies = [ - "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.22 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec", + "libc", + "mio", ] [[package]] name = "miow" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" dependencies = [ - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "kernel32-sys", + "net2", + "winapi 0.2.8", + "ws2_32-sys", ] [[package]] name = "miow" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a33c1b55807fbed163481b5ba66db4b2fa6cde694a5027be10fb724206c5897" dependencies = [ - "socket2 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "socket2", + "winapi 0.3.9", ] [[package]] name = "muldiv" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0419348c027fa7be448d2ae7ea0e4e04c2334c31dc4e74ab29f00a2a7ca69204" [[package]] name = "multimap" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1255076139a83bb467426e7f8d0134968a8118844faa755985e077cf31850333" dependencies = [ - "serde 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)", + "serde", ] [[package]] name = "ndk" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eb167c1febed0a496639034d0c76b3b74263636045db5489eee52143c246e73" dependencies = [ - "jni-sys 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ndk-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "num_enum 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "thiserror 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)", + "jni-sys", + "ndk-sys", + "num_enum", + "thiserror", ] [[package]] name = "ndk-glue" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdf399b8b7a39c6fb153c4ec32c72fd5fe789df24a647f229c239aa7adb15241" dependencies = [ - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "ndk 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "ndk-macro 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ndk-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static", + "libc", + "log", + "ndk", + "ndk-macro", + "ndk-sys", ] [[package]] name = "ndk-macro" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05d1c6307dc424d0f65b9b06e94f88248e6305726b14729fd67a5e47b2dc481d" dependencies = [ - "darling 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-crate 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)", + "darling", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "ndk-sys" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c44922cb3dbb1c70b5e5f443d63b64363a898564d739ba5198e3a9138442868d" [[package]] name = "net2" -version = "0.2.34" +version = "0.2.37" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10", + "libc", + "winapi 0.3.9", ] [[package]] name = "nix" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2c5afeb0198ec7be8569d666644b574345aad2e95a53baf3a532da3e0f3fb32" dependencies = [ - "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 0.9.1", + "cfg-if 0.1.10", + "libc", + "void", ] [[package]] name = "nix" version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2e0b4f3320ed72aaedb9a5ac838690a8047c7b275da22711fddff4f8a14229" dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.65 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1", + "cc", + "cfg-if 0.1.10", + "libc", + "void", ] [[package]] name = "nom" version = "5.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" dependencies = [ - "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "version_check 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr", + "version_check", ] [[package]] name = "num-bigint" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e9a41747ae4633fce5adffb4d2e81ffc5e89593cb19917f8fb2cc5ff76507bf" dependencies = [ - "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "num-integer 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg", + "num-integer", + "num-traits", ] [[package]] name = "num-derive" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" dependencies = [ - "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "num-integer" -version = "0.1.43" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" dependencies = [ - "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg", + "num-traits", ] [[package]] name = "num-rational" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" dependencies = [ - "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "num-integer 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg", + "num-integer", + "num-traits", ] [[package]] name = "num-traits" -version = "0.2.12" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" dependencies = [ - "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg", ] [[package]] name = "num_cpus" version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" dependencies = [ - "hermit-abi 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", + "hermit-abi", + "libc", ] [[package]] name = "num_enum" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca565a7df06f3d4b485494f25ba05da1435950f4dc263440eda7a6fa9b8e36e4" dependencies = [ - "derivative 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "num_enum_derive 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "derivative", + "num_enum_derive", ] [[package]] name = "num_enum_derive" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffa5a33ddddfee04c0283a7653987d634e880347e96b5b2ed64de07efb59db9d" dependencies = [ - "proc-macro-crate 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "object" -version = "0.22.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9a7ab5d64814df0fe4a4b5ead45ed6c5f181ee3ff04ba344313a6c80446c5d4" [[package]] name = "oboe" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aadc2b0867bdbb9a81c4d99b9b682958f49dbea1295a81d2f646cca2afdd9fc" dependencies = [ - "jni 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ndk 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "ndk-glue 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "num-derive 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", - "oboe-sys 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "jni 0.14.0", + "ndk", + "ndk-glue", + "num-derive", + "num-traits", + "oboe-sys", ] [[package]] name = "oboe-sys" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ff7a51600eabe34e189eec5c995a62f151d8d97e5fbca39e87ca738bb99b82" dependencies = [ - "fetch_unroll 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "fetch_unroll", ] [[package]] name = "ogg" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13e571c3517af9e1729d4c63571a27edd660ade0667973bfc74a67c660c2b651" dependencies = [ - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", ] [[package]] name = "ogg-sys" version = "0.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a95b8c172e17df1a41bf8d666301d3b2c4efeb90d9d0415e2a4dc0668b35fdb2" dependencies = [ - "gcc 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", + "gcc", + "libc", + "pkg-config", ] [[package]] name = "once_cell" version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0" [[package]] name = "opaque-debug" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" [[package]] name = "parking_lot" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" dependencies = [ - "lock_api 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "lock_api 0.3.4", + "parking_lot_core 0.6.2", + "rustc_version", ] [[package]] name = "parking_lot" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" dependencies = [ - "instant 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "lock_api 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot_core 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "instant", + "lock_api 0.4.2", + "parking_lot_core 0.8.2", ] [[package]] name = "parking_lot_core" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.57 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10", + "cloudabi", + "libc", + "redox_syscall 0.1.57", + "rustc_version", + "smallvec 0.6.14", + "winapi 0.3.9", ] [[package]] name = "parking_lot_core" -version = "0.8.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ccb628cad4f84851442432c60ad8e1f607e29752d0bf072cbd0baf28aa34272" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "cloudabi 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "instant 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.57 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 1.0.0", + "instant", + "libc", + "redox_syscall 0.1.57", + "smallvec 1.6.1", + "winapi 0.3.9", ] [[package]] name = "paste" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5d65c4d95931acda4498f675e332fcbdc9a06705cd07086c510e9b6009cd1c1" [[package]] name = "pbkdf2" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "006c038a43a45995a9670da19e67600114740e8511d4333bf97a56e66a7542d9" dependencies = [ - "base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", - "subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "base64 0.9.3", + "byteorder", + "crypto-mac", + "hmac", + "rand 0.5.6", + "sha2", + "subtle", ] [[package]] name = "peeking_take_while" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" [[package]] name = "percent-encoding" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" [[package]] name = "percent-encoding" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" [[package]] name = "pin-project" -version = "0.4.22" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ffbc8e94b38ea3d2d8ba92aea2983b503cd75d0888d75b86bb37970b5698e15" dependencies = [ - "pin-project-internal 0.4.22 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-project-internal 0.4.27", +] + +[[package]] +name = "pin-project" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95b70b68509f17aa2857863b6fa00bf21fc93674c7a8893de2f469f6aa7ca2f2" +dependencies = [ + "pin-project-internal 1.0.4", ] [[package]] name = "pin-project-internal" -version = "0.4.22" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65ad2ae56b6abe3a1ee25f15ee605bacadb9a764edaba9c2bf4103800d4a1895" dependencies = [ - "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caa25a6393f22ce819b0f50e0be89287292fda8d425be38ee0ca14c4931d9e71" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] name = "pin-project-lite" version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c917123afa01924fc84bb20c4c03f004d9c38e5127e3c039bbf7f4b9c76a2f6b" + +[[package]] +name = "pin-project-lite" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439697af366c49a6d0a010c56a0d97685bc140ce0d377b13a2ea2aa42d64a827" [[package]] name = "pin-utils" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.18" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" [[package]] name = "portaudio-rs" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb6b5eff96ccc9bf44d34c379ab03ae944426d83d1694345bdf8159d561d562" dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "portaudio-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1", + "libc", + "portaudio-sys", ] [[package]] name = "portaudio-sys" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5194a4fa953b4ffd851c320ef6f0484cd7278cb7169ea9d6c433e49b23f7b7f5" dependencies = [ - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "pkg-config", ] [[package]] name = "ppv-lite86" -version = "0.2.8" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" [[package]] name = "pretty-hex" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc5c99d529f0d30937f6f4b8a86d988047327bb88d04d2c4afc356de74722131" [[package]] name = "proc-macro-crate" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" dependencies = [ - "toml 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)", + "toml", ] [[package]] name = "proc-macro-error" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ - "proc-macro-error-attr 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)", - "version_check 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", ] [[package]] name = "proc-macro-error-attr" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "version_check 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "version_check", ] [[package]] name = "proc-macro-hack" -version = "0.5.16" +version = "0.5.19" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" [[package]] name = "proc-macro-nested" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" [[package]] name = "proc-macro2" -version = "1.0.19" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" dependencies = [ - "unicode-xid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid", ] [[package]] name = "protobuf" version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e86d370532557ae7573551a1ec8235a0f8d6cb276c7c9e6aa490b511c447485" [[package]] name = "protobuf-codegen" version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de113bba758ccf2c1ef816b127c958001b7831136c9bc3f8e9ec695ac4e82b0c" dependencies = [ - "protobuf 2.14.0 (registry+https://github.com/rust-lang/crates.io-index)", + "protobuf", ] [[package]] name = "protobuf-codegen-pure" version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d1a4febc73bf0cada1d77c459a0c8e5973179f1cfd5b0f1ab789d45b17b6440" dependencies = [ - "protobuf 2.14.0 (registry+https://github.com/rust-lang/crates.io-index)", - "protobuf-codegen 2.14.0 (registry+https://github.com/rust-lang/crates.io-index)", + "protobuf", + "protobuf-codegen", +] + +[[package]] +name = "publicsuffix" +version = "1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bbaa49075179162b49acac1c6aa45fb4dafb5f13cf6794276d77bc7fd95757b" +dependencies = [ + "error-chain", + "idna 0.2.0", + "lazy_static", + "regex", + "url 2.2.0", ] [[package]] name = "qstring" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d464fae65fff2680baf48019211ce37aaec0c78e9264c84a3e484717f965104e" dependencies = [ - "percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "percent-encoding 2.1.0", ] [[package]] name = "quick-error" version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quote" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df" dependencies = [ - "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand" -version = "0.3.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", ] [[package]] name = "rand" version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9" dependencies = [ - "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cloudabi", + "fuchsia-cprng", + "libc", + "rand_core 0.3.1", + "winapi 0.3.9", ] [[package]] name = "rand" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" dependencies = [ - "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc 0.2.0", +] + +[[package]] +name = "rand" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18519b42a40024d661e1714153e9ad0c3de27cd495760ceb09710920f1098b1e" +dependencies = [ + "libc", + "rand_chacha 0.3.0", + "rand_core 0.6.1", + "rand_hc 0.3.0", ] [[package]] name = "rand_chacha" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" dependencies = [ - "ppv-lite86 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.1", ] [[package]] name = "rand_core" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" dependencies = [ - "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.2", ] [[package]] name = "rand_core" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" [[package]] name = "rand_core" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" dependencies = [ - "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c026d7df8b298d90ccbbc5190bd04d85e159eaf5576caeacf8741da93ccbd2e5" +dependencies = [ + "getrandom 0.2.2", ] [[package]] name = "rand_hc" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" dependencies = [ - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1", ] [[package]] -name = "rdrand" -version = "0.4.0" +name = "rand_hc" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" dependencies = [ - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.6.1", ] [[package]] name = "redox_syscall" version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" + +[[package]] +name = "redox_syscall" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05ec8ca9416c5ea37062b502703cd7fcb207736bc294f6e0cf367ac6fc234570" +dependencies = [ + "bitflags 1.2.1", +] [[package]] name = "regex" -version = "1.3.9" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a" dependencies = [ - "regex-syntax 0.6.18 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax", ] [[package]] name = "regex-syntax" -version = "0.6.18" +version = "0.6.22" source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "relay" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581" [[package]] name = "remove_dir_all" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" dependencies = [ - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.9", ] [[package]] name = "ring" -version = "0.16.18" +version = "0.16.19" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "024a1e66fea74c66c66624ee5622a7ff0e4b73a13b4f5c326ddb50c708944226" dependencies = [ - "cc 1.0.65 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "once_cell 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "untrusted 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "web-sys 0.3.46 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi 0.3.9", ] [[package]] name = "rle-decode-fast" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cabe4fa914dec5870285fa7f71f602645da47c486e68486d2b4ceb4a343e90ac" [[package]] name = "rodio" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9683532495146e98878d4948fa1a1953f584cd923f2a5f5c26b7a8701b56943" dependencies = [ - "cpal 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cpal", ] [[package]] name = "rpassword" -version = "5.0.0" +version = "5.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffc936cf8a7ea60c58f030fd36a612a48f440610214dc54bc36431f9ea0c3efb" dependencies = [ - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "winapi 0.3.9", ] [[package]] name = "rustc-demangle" version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e3bad0ee36814ca07d7968269dd4b7ec89ec2da10c4bb613928d3077083c232" [[package]] name = "rustc-hash" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustc_version" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" dependencies = [ - "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "semver", ] [[package]] name = "rustls" -version = "0.16.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "064fd21ff87c6e87ed4506e68beb42459caa4a0e2eb144932e6776768556980b" dependencies = [ - "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "ring 0.16.18 (registry+https://github.com/rust-lang/crates.io-index)", - "sct 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "webpki 0.21.3 (registry+https://github.com/rust-lang/crates.io-index)", + "base64 0.13.0", + "log", + "ring", + "sct", + "webpki", ] [[package]] name = "ryu" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" [[package]] name = "safemem" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" [[package]] name = "same-file" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" dependencies = [ - "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-util", ] [[package]] name = "scoped-tls" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "332ffa32bf586782a3efaeb58f127980944bbc8c4d6913a86107ac2a5ab24b28" [[package]] name = "scopeguard" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "sct" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3042af939fca8c3453b7af0f1c66e533a15a86169e39de2657310ade8f98d3c" dependencies = [ - "ring 0.16.18 (registry+https://github.com/rust-lang/crates.io-index)", - "untrusted 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ring", + "untrusted", ] [[package]] name = "sdl2" version = "0.34.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcbb85f4211627a7291c83434d6bbfa723e28dcaa53c7606087e3c61929e4b9c" dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "sdl2-sys 0.34.3 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1", + "lazy_static", + "libc", + "sdl2-sys", ] [[package]] name = "sdl2-sys" version = "0.34.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d81feded049b9c14eceb4a4f6d596a98cebbd59abdba949c5552a015466d33" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "version-compare 0.0.10 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10", + "libc", + "version-compare", ] [[package]] name = "semver" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" dependencies = [ - "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "semver-parser", ] [[package]] name = "semver-parser" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.114" +version = "1.0.122" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "974ef1bd2ad8a507599b336595454081ff68a9599b4890af7643c0c0ed73a62c" +dependencies = [ + "serde_derive", +] [[package]] name = "serde_derive" -version = "1.0.114" +version = "1.0.122" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dee1f300f838c8ac340ecb0112b3ac472464fa67e87292bdb3dfc9c49128e17" dependencies = [ - "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "serde_json" -version = "1.0.56" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fceb2595057b6891a4ee808f70054bd2d12f0e97f1cbb78689b59f676df325a" dependencies = [ - "itoa 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "ryu 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa", + "ryu", + "serde", ] [[package]] name = "sha-1" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" dependencies = [ - "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "block-buffer", + "digest", + "fake-simd", + "opaque-debug", ] +[[package]] +name = "sha1" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" + [[package]] name = "sha2" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69" dependencies = [ - "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "block-buffer", + "digest", + "fake-simd", + "opaque-debug", ] [[package]] name = "shannon" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ea5b41c9427b56caa7b808cb548a04fb50bb5b9e98590b53f28064ff4174561" dependencies = [ - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", ] [[package]] name = "shell-words" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fa3938c99da4914afedd13bf3d79bcb6c277d1b2c398d23257a304d9e1b074" [[package]] name = "shlex" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" [[package]] name = "signal-hook-registry" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16f1d0fef1604ba8f7a073c7e701f213e056707210e9020af4528e0101ce11a6" dependencies = [ - "arc-swap 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", ] -[[package]] -name = "slab" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "slab" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" [[package]] name = "smallvec" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "smallvec" -version = "0.6.13" +version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97fcaeba89edba30f044a10c6a3cc39df9c3f17d7cd829dd1446cab35f890e0" dependencies = [ - "maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "maybe-uninit", ] [[package]] name = "smallvec" -version = "1.5.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" [[package]] name = "socket2" -version = "0.3.12" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.57 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 1.0.0", + "libc", + "winapi 0.3.9", ] [[package]] name = "spin" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "standback" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c66a8cff4fa24853fdf6b51f75c6d7f8206d7c75cab4e467bcd7f25c2b1febe0" +dependencies = [ + "version_check", +] [[package]] name = "stdweb" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef5430c8e36b713e13b48a9f709cc21e046723fe44ce34587b73a830203b533e" + +[[package]] +name = "stdweb" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" +dependencies = [ + "discard", + "rustc_version", + "stdweb-derive", + "stdweb-internal-macros", + "stdweb-internal-runtime", + "wasm-bindgen", +] + +[[package]] +name = "stdweb-derive" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "serde_derive", + "syn", +] + +[[package]] +name = "stdweb-internal-macros" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" +dependencies = [ + "base-x", + "proc-macro2", + "quote", + "serde", + "serde_derive", + "serde_json", + "sha1", + "syn", +] + +[[package]] +name = "stdweb-internal-runtime" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" [[package]] name = "stream-cipher" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8131256a5896cabcf5eb04f4d6dacbe1aefda854b0d9896e09cb58829ec5638c" dependencies = [ - "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array", +] + +[[package]] +name = "string" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d24114bfcceb867ca7f71a0d3fe45d45619ec47a6fbfa98cb14e14250bfa5d6d" +dependencies = [ + "bytes 0.4.12", ] [[package]] name = "strsim" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" [[package]] name = "strum" version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57bd81eb48f4c437cadc685403cad539345bf703d78e63707418431cecd4522b" [[package]] name = "strum_macros" version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87c85aa3f8ea653bfd3ddf25f7ee357ee4d204731f6aa9ad04002306f6e2774c" dependencies = [ - "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)", + "heck", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "subtle" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" [[package]] name = "syn" -version = "1.0.35" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081" dependencies = [ - "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "unicode-xid", ] [[package]] name = "synstructure" version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701" dependencies = [ - "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "syn", + "unicode-xid", ] [[package]] name = "system-deps" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f3ecc17269a19353b3558b313bba738b25d82993e30d62a18406a24aba4649b" dependencies = [ - "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", - "strum 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)", - "strum_macros 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)", - "thiserror 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)", - "toml 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)", - "version-compare 0.0.10 (registry+https://github.com/rust-lang/crates.io-index)", + "heck", + "pkg-config", + "strum", + "strum_macros", + "thiserror", + "toml", + "version-compare", ] -[[package]] -name = "take" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "take_mut" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "tar" -version = "0.4.30" +version = "0.4.31" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a69b4283cf44997cad75fd635aa70e16f3317248c4c8dfd96ad134d15d4d34db" dependencies = [ - "filetime 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.57 (registry+https://github.com/rust-lang/crates.io-index)", - "xattr 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "filetime", + "libc", + "xattr", ] [[package]] name = "tempfile" -version = "3.1.0" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.57 (registry+https://github.com/rust-lang/crates.io-index)", - "remove_dir_all 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 1.0.0", + "libc", + "rand 0.8.2", + "redox_syscall 0.2.4", + "remove_dir_all", + "winapi 0.3.9", ] [[package]] name = "termcolor" -version = "1.1.0" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" dependencies = [ - "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-util", ] [[package]] name = "thiserror" -version = "1.0.21" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76cc616c6abf8c8928e2fdcc0dbfab37175edd8fb49a4641066ad1364fdab146" dependencies = [ - "thiserror-impl 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)", + "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.21" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9be73a2caec27583d0046ef3796c3794f868a5bc813db689eed00c7631275cd1" dependencies = [ - "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "time" version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" dependencies = [ - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "time" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1195b046942c221454c2539395f85413b33383a067449d78aab2b7b052a142f7" +dependencies = [ + "const_fn", + "libc", + "standback", + "stdweb 0.4.20", + "time-macros", + "version_check", + "winapi 0.3.9", +] + +[[package]] +name = "time-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "957e9c6e26f12cb6d0dd7fc776bb67a706312e7299aed74c8dd5b17ebb27e2f1" +dependencies = [ + "proc-macro-hack", + "time-macros-impl", +] + +[[package]] +name = "time-macros-impl" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5c3be1edfad6027c69f5491cf4cb310d1a71ecd6af742788c6ff8bced86b8fa" +dependencies = [ + "proc-macro-hack", + "proc-macro2", + "quote", + "standback", + "syn", ] [[package]] name = "tinyvec" -version = "0.3.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf8dbc19eb42fba10e8feaaec282fb50e2c14b2726d6301dbfeed0f73306a6f" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a09c0b5bb588872ab2f09afa13ee6e9dac11e10a0ec9e8e3ba39a5a5d530af6" dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.22 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-codec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-current-thread 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-fs 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-sync 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-tcp 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-threadpool 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-timer 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-udp 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-uds 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12", + "futures 0.1.30", + "mio", + "num_cpus", + "tokio-codec", + "tokio-current-thread", + "tokio-executor", + "tokio-fs", + "tokio-io", + "tokio-reactor", + "tokio-sync", + "tokio-tcp", + "tokio-threadpool", + "tokio-timer", + "tokio-udp", + "tokio-uds", +] + +[[package]] +name = "tokio" +version = "0.2.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "099837d3464c16a808060bb3f02263b412f6fafcb5d01c533d309985fbeebe48" +dependencies = [ + "bytes 0.5.6", + "fnv", + "futures-core", + "iovec", + "lazy_static", + "libc", + "memchr", + "mio", + "mio-named-pipes", + "mio-uds", + "num_cpus", + "pin-project-lite 0.1.11", + "signal-hook-registry", + "slab", + "tokio-macros", + "winapi 0.3.9", +] + +[[package]] +name = "tokio-buf" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fb220f46c53859a4b7ec083e41dec9778ff0b1851c0942b211edb89e0ccdc46" +dependencies = [ + "bytes 0.4.12", + "either", + "futures 0.1.30", ] [[package]] name = "tokio-codec" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25b2998660ba0e70d18684de5d06b70b70a3a747469af9dea7618cc59e75976b" dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12", + "futures 0.1.30", + "tokio-io", ] [[package]] name = "tokio-core" -version = "0.1.17" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87b1395334443abca552f63d4f61d0486f12377c2ba8b368e523f89e828cffd4" dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.22 (registry+https://github.com/rust-lang/crates.io-index)", - "scoped-tls 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-timer 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12", + "futures 0.1.30", + "iovec", + "log", + "mio", + "scoped-tls", + "tokio 0.1.22", + "tokio-executor", + "tokio-io", + "tokio-reactor", + "tokio-timer", ] [[package]] name = "tokio-current-thread" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1de0e32a83f131e002238d7ccde18211c0a5397f60cbfffcb112868c2e0e20e" dependencies = [ - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.30", + "tokio-executor", ] [[package]] name = "tokio-executor" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb2d1b8f4548dbf5e1f7818512e9c406860678f29c300cdf0ebac72d1a3a1671" dependencies = [ - "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.7.2", + "futures 0.1.30", ] [[package]] name = "tokio-fs" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "297a1206e0ca6302a0eed35b700d292b275256f596e2f3fea7729d5e629b6ff4" dependencies = [ - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-threadpool 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.30", + "tokio-io", + "tokio-threadpool", ] [[package]] name = "tokio-io" version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57fc868aae093479e3131e3d165c93b1c7474109d13c90ec0dda2a1bbfff0674" dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12", + "futures 0.1.30", + "log", +] + +[[package]] +name = "tokio-macros" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e44da00bfc73a25f814cd8d7e57a68a5c31b74b3152a0a1d1f590c97ed06265a" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] name = "tokio-process" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "382d90f43fa31caebe5d3bc6cfd854963394fff3b8cb59d5146607aaae7e7e43" dependencies = [ - "crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.22 (registry+https://github.com/rust-lang/crates.io-index)", - "mio-named-pipes 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-signal 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-proto" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.3.23 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "take 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-queue 0.1.2", + "futures 0.1.30", + "lazy_static", + "libc", + "log", + "mio", + "mio-named-pipes", + "tokio-io", + "tokio-reactor", + "tokio-signal", + "winapi 0.3.9", ] [[package]] name = "tokio-reactor" version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09bc590ec4ba8ba87652da2068d150dcada2cfa2e07faae270a5e0409aa51351" dependencies = [ - "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.22 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-sync 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-service" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.7.2", + "futures 0.1.30", + "lazy_static", + "log", + "mio", + "num_cpus", + "parking_lot 0.9.0", + "slab", + "tokio-executor", + "tokio-io", + "tokio-sync", ] [[package]] name = "tokio-signal" version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0c34c6e548f101053321cba3da7cbb87a610b85555884c41b07da2eb91aff12" dependencies = [ - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.22 (registry+https://github.com/rust-lang/crates.io-index)", - "mio-uds 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", - "signal-hook-registry 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.30", + "libc", + "mio", + "mio-uds", + "signal-hook-registry", + "tokio-executor", + "tokio-io", + "tokio-reactor", + "winapi 0.3.9", ] [[package]] name = "tokio-sync" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edfe50152bc8164fcc456dab7891fa9bf8beaf01c5ee7e1dd43a397c3cf87dee" dependencies = [ - "fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "fnv", + "futures 0.1.30", ] [[package]] name = "tokio-tcp" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98df18ed66e3b72e742f185882a9e201892407957e45fbff8da17ae7a7c51f72" dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.22 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12", + "futures 0.1.30", + "iovec", + "mio", + "tokio-io", + "tokio-reactor", ] [[package]] name = "tokio-threadpool" version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df720b6581784c118f0eb4310796b12b1d242a7eb95f716a8367855325c25f89" dependencies = [ - "crossbeam-deque 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-queue 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-deque", + "crossbeam-queue 0.2.3", + "crossbeam-utils 0.7.2", + "futures 0.1.30", + "lazy_static", + "log", + "num_cpus", + "slab", + "tokio-executor", ] [[package]] name = "tokio-timer" version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93044f2d313c95ff1cb7809ce9a7a05735b012288a888b62d4434fd58c94f296" dependencies = [ - "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.7.2", + "futures 0.1.30", + "slab", + "tokio-executor", ] [[package]] name = "tokio-udp" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2a0b10e610b39c38b031a2fcab08e4b82f16ece36504988dcbd81dbba650d82" dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.22 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-codec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12", + "futures 0.1.30", + "log", + "mio", + "tokio-codec", + "tokio-io", + "tokio-reactor", ] [[package]] name = "tokio-uds" version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab57a4ac4111c8c9dbcf70779f6fc8bc35ae4b2454809febac840ad19bd7e4e0" dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.22 (registry+https://github.com/rust-lang/crates.io-index)", - "mio-uds 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-codec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12", + "futures 0.1.30", + "iovec", + "libc", + "log", + "mio", + "mio-uds", + "tokio-codec", + "tokio-io", + "tokio-reactor", +] + +[[package]] +name = "tokio-util" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be8242891f2b6cbef26a2d7e8605133c2c554cd35b3e4948ea892d6d68436499" +dependencies = [ + "bytes 0.5.6", + "futures-core", + "futures-io", + "futures-sink", + "log", + "pin-project-lite 0.1.11", + "tokio 0.2.24", ] [[package]] name = "toml" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" dependencies = [ - "serde 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)", + "serde", +] + +[[package]] +name = "tower-service" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" + +[[package]] +name = "tracing" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f47026cdc4080c07e49b37087de021820269d996f581aac150ef9e5583eefe3" +dependencies = [ + "cfg-if 1.0.0", + "log", + "pin-project-lite 0.2.4", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f50de3927f93d202783f4513cda820ab47ef17f624b03c096e86ef00c67e6b5f" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "tracing-futures" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab7bb6f14721aa00656086e9335d363c5c8747bae02ebe32ea2c7dece5689b4c" +dependencies = [ + "pin-project 0.4.27", + "tracing", ] [[package]] name = "try-lock" -version = "0.1.0" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" + +[[package]] +name = "typed-headers" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3179a61e9eccceead5f1574fd173cf2e162ac42638b9bf214c6ad0baf7efa24a" +dependencies = [ + "base64 0.11.0", + "bytes 0.5.6", + "chrono", + "http 0.2.3", + "mime", +] [[package]] name = "typenum" version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "unicase" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "version_check 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" [[package]] name = "unicode-bidi" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" dependencies = [ - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "matches", ] [[package]] name = "unicode-normalization" -version = "0.1.13" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a13e63ab62dbe32aeee58d1c5408d35c36c392bba5d9d3142287219721afe606" dependencies = [ - "tinyvec 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tinyvec", ] [[package]] name = "unicode-segmentation" version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796" [[package]] name = "unicode-width" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" [[package]] name = "unicode-xid" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" [[package]] name = "unreachable" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" dependencies = [ - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "void", ] [[package]] name = "untrusted" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "ureq" -version = "0.11.4" +version = "1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "294b85ef5dbc3670a72e82a89971608a1fcc4ed5c7c5a2895230d31a95f0569b" dependencies = [ - "base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "chunked_transfer 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cookie 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "qstring 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rustls 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", - "url 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "webpki 0.21.3 (registry+https://github.com/rust-lang/crates.io-index)", - "webpki-roots 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)", + "base64 0.13.0", + "chunked_transfer", + "cookie", + "cookie_store", + "log", + "once_cell", + "qstring", + "rustls", + "url 2.2.0", + "webpki", + "webpki-roots", ] [[package]] name = "url" version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a" dependencies = [ - "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "idna 0.1.5", + "matches", + "percent-encoding 1.0.1", ] [[package]] name = "url" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5909f2b0817350449ed73e8bcd81c8c3c8d9a7a5d8acba4b27db277f1868976e" dependencies = [ - "form_urlencoded 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "form_urlencoded", + "idna 0.2.0", + "matches", + "percent-encoding 2.1.0", ] [[package]] name = "uuid" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" dependencies = [ - "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "getrandom 0.2.2", ] [[package]] name = "vergen" version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ce50d8996df1f85af15f2cd8d33daae6e479575123ef4314a51a70a230739cb" dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "chrono 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1", + "chrono", ] [[package]] name = "version-compare" version = "0.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d63556a25bae6ea31b52e640d7c41d1ab27faba4ccb600013837a3d0b3994ca1" [[package]] name = "version_check" version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" [[package]] name = "void" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" [[package]] name = "vorbis" version = "0.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e8a194457075360557b82dac78f7ca2d65bbb6679bccfabae5f7c8c706cc776" dependencies = [ - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "ogg-sys 0.0.9 (registry+https://github.com/rust-lang/crates.io-index)", - "vorbis-sys 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "vorbisfile-sys 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "ogg-sys", + "vorbis-sys", + "vorbisfile-sys", ] [[package]] name = "vorbis-sys" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9ed6ef5361a85e68ccc005961d995c2d44e31f0816f142025f2ca2383dfbfd" dependencies = [ - "gcc 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "ogg-sys 0.0.9 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", + "cc", + "libc", + "ogg-sys", + "pkg-config", ] [[package]] name = "vorbisfile-sys" version = "0.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f4306d7e1ac4699b55e20de9483750b90c250913188efd7484db6bfbe9042d1" dependencies = [ - "gcc 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "ogg-sys 0.0.9 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", - "vorbis-sys 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gcc", + "libc", + "ogg-sys", + "pkg-config", + "vorbis-sys", ] [[package]] name = "walkdir" version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d" dependencies = [ - "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "same-file", + "winapi 0.3.9", + "winapi-util", ] [[package]] name = "want" -version = "0.0.4" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6395efa4784b027708f7451087e647ec73cc74f5d9bc2e418404248d679a230" dependencies = [ - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "try-lock 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.30", + "log", + "try-lock", +] + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", ] [[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.10.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93c6c3420963c5c64bca373b25e77acb562081b9bb4dd5bb864187742186cea9" [[package]] name = "wasm-bindgen" version = "0.2.69" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cd364751395ca0f68cafb17666eee36b63077fb5ecd972bbcd74c90c4bf736e" dependencies = [ - "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-macro 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 1.0.0", + "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" version = "0.2.69" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1114f89ab1f4106e5b55e688b828c0ab0ea593a1ea7c094b141b14cbaaec2d62" dependencies = [ - "bumpalo 3.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-shared 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" version = "0.2.69" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6ac8995ead1f084a8dea1e65f194d0973800c7f571f6edd70adf06ecf77084" dependencies = [ - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-macro-support 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", + "quote", + "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" version = "0.2.69" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5a48c72f299d80557c7c62e37e7225369ecc0c963964059509fbafe917c7549" dependencies = [ - "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-backend 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-shared 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" version = "0.2.69" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e7811dd7f9398f14cc76efd356f98f03aa30419dea46aa810d71e819fc97158" [[package]] name = "web-sys" version = "0.3.46" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "222b1ef9334f92a21d3fb53dc3fd80f30836959a90f9274a626d7e06315ba3c3" dependencies = [ - "js-sys 0.3.46 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", + "js-sys", + "wasm-bindgen", ] [[package]] name = "webpki" -version = "0.21.3" +version = "0.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" dependencies = [ - "ring 0.16.18 (registry+https://github.com/rust-lang/crates.io-index)", - "untrusted 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ring", + "untrusted", ] [[package]] name = "webpki-roots" -version = "0.18.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82015b7e0b8bad8185994674a13a93306bea76cf5a16c5a181382fd3a5ec2376" dependencies = [ - "webpki 0.21.3 (registry+https://github.com/rust-lang/crates.io-index)", + "webpki", ] [[package]] name = "winapi" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ - "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-build" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" dependencies = [ - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.9", ] [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "ws2_32-sys" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" dependencies = [ - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8", + "winapi-build", ] [[package]] name = "xattr" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "244c3741f4240ef46274860397c7c74e50eb23624996930e484c16679633a54c" dependencies = [ - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", ] [[package]] name = "zerocopy" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6580539ad917b7c026220c4b3f2c08d52ce54d6ce0dc491e66002e35388fab46" dependencies = [ - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "zerocopy-derive 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "zerocopy-derive", ] [[package]] name = "zerocopy-derive" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d498dbd1fd7beb83c86709ae1c33ca50942889473473d287d56ce4770a18edfb" dependencies = [ - "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)", - "synstructure 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "syn", + "synstructure", ] - -[metadata] -"checksum addr2line 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7c0929d69e78dd9bf5408269919fcbcaeb2e35e5d43e5815517cdc6a8e11a423" -"checksum adler 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e" -"checksum adler32 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" -"checksum aes 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "54eb1d8fe354e5fc611daf4f2ea97dd45a765f4f1e4512306ec183ae2e8f20c9" -"checksum aes-ctr 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d2e5b0458ea3beae0d1d8c0f3946564f8e10f90646cf78c06b4351052058d1ee" -"checksum aes-soft 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cfd7e7ae3f9a1fb5c03b389fc6bb9a51400d0c13053f0dca698c832bfd893a0d" -"checksum aesni 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2f70a6b5f971e473091ab7cfb5ffac6cde81666c4556751d8d5620ead8abf100" -"checksum alsa 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b4a0d4ebc8b23041c5de9bc9aee13b4bad844a589479701f31a5934cfe4aeb32" -"checksum alsa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "eb213f6b3e4b1480a60931ca2035794aa67b73103d254715b1db7b70dcb3c934" -"checksum alsa-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b0edcbbf9ef68f15ae1b620f722180b82a98b6f0628d30baa6b8d2a5abc87d58" -"checksum alsa-sys 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d5a0559bcd3f7a482690d98be41c08a43e92f669b179433e95ddf5e8b8fd36a3" -"checksum anyhow 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)" = "2c0df63cb2955042487fad3aefd2c6e3ae7389ac5dc1beb28921de0b69f779d4" -"checksum arc-swap 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "4d25d88fd6b8041580a654f9d0c581a047baee2b3efee13275f2fc392fc75034" -"checksum ascii 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "eab1c04a571841102f5345a8fc0f6bb3d31c315dec879b5c6e42e40ce7ffa34e" -"checksum atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -"checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" -"checksum backtrace 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)" = "ef5140344c85b01f9bbb4d4b7288a8aa4b3287ccef913a14bcc78a1063623598" -"checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" -"checksum base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" -"checksum base64 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" -"checksum base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643" -"checksum bindgen 0.53.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c72a978d268b1d70b0e963217e60fdabd9523a941457a6c42a7315d15c7e89e5" -"checksum bit-set 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de" -"checksum bit-vec 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5f0dc55f2d8a1a85650ac47858bb001b4c0dd73d79e3c455a842925e68d29cd3" -"checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" -"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" -"checksum block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" -"checksum block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1c924d49bd09e7c06003acda26cd9742e796e34282ec6c1189404dee0c1f4774" -"checksum block-modes 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "31aa8410095e39fdb732909fb5730a48d5bd7c2e3cd76bd1b07b3dbea130c529" -"checksum block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" -"checksum bumpalo 3.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820" -"checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" -"checksum byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" -"checksum bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" -"checksum bytes 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" -"checksum cc 1.0.65 (registry+https://github.com/rust-lang/crates.io-index)" = "95752358c8f7552394baf48cd82695b345628ad3f170d607de3ca03b8dacca15" -"checksum cesu8 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" -"checksum cexpr 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f4aedb84272dbe89af497cf81375129abda4fc0a9e7c5d317498c15cc30c0d27" -"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" -"checksum cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -"checksum chrono 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)" = "c74d84029116787153e02106bf53e66828452a4b325cc8652b788b5967c0a0b6" -"checksum chunked_transfer 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7477065d45a8fe57167bf3cf8bcd3729b54cfcb81cca49bda2d038ea89ae82ca" -"checksum clang-sys 0.29.3 (registry+https://github.com/rust-lang/crates.io-index)" = "fe6837df1d5cba2397b835c8530f51723267e16abbf83892e9e5af4f0e5dd10a" -"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" -"checksum cloudabi 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4344512281c643ae7638bbabc3af17a11307803ec8f0fcad9fae512a8bf36467" -"checksum combine 3.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "da3da6baa321ec19e1cc41d31bf599f00c783d0517095cdaf0332e3fe8d20680" -"checksum combine 4.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b9417a0c314565e2abffaece67e95a8cb51f9238cd39f3764d9dfdf09e72b20c" -"checksum cookie 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "888604f00b3db336d2af898ec3c1d5d0ddf5e6d462220f2ededc33a87ac4bbd5" -"checksum core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b" -"checksum coreaudio-rs 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f229761965dad3e9b11081668a6ea00f1def7aa46062321b5ec245b834f6e491" -"checksum coreaudio-sys 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d6570ee6e089131e928d5ec9236db9e818aa3cf850f48b0eec6ef700571271d4" -"checksum cpal 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05631e2089dfa5d3b6ea1cfbbfd092e2ee5deeb69698911bc976b28b746d3657" -"checksum crc32fast 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" -"checksum crossbeam-deque 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285" -"checksum crossbeam-epoch 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" -"checksum crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b" -"checksum crossbeam-queue 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570" -"checksum crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6" -"checksum crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" -"checksum crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" -"checksum ctr 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "022cd691704491df67d25d006fe8eca083098253c4d43516c2206479c58c6736" -"checksum darling 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858" -"checksum darling_core 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b" -"checksum darling_macro 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72" -"checksum derivative 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cb582b60359da160a9477ee80f15c8d784c477e69c217ef2cdd4169c24ea380f" -"checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" -"checksum dns-sd 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d748509dea20228f63ba519bf142ce2593396386125b01f5b0d6412dab972087" -"checksum either 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" -"checksum env_logger 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f26ecb66b4bdca6c1409b40fb255eefc2bd4f6d135dab3c3124f80ffa2a9661e" -"checksum error-chain 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d371106cc88ffdfb1eabd7111e432da544f16f3e2d7bf1dfe8bf575f1df045cd" -"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" -"checksum fetch_unroll 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b5c55005e95bbe15f5f72a73b6597d0dc82ddc97ffe2ca097a99dcd591fefbca" -"checksum filetime 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "0c122a393ea57648015bf06fbd3d372378992e86b9ff5a7a497b076a28c79efe" -"checksum fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -"checksum form_urlencoded 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ece68d15c92e84fa4f19d3780f1294e5ca82a78a6d515f1efaabcc144688be00" -"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" -"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" -"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" -"checksum futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)" = "1b980f2816d6ee8673b6517b52cb0e808a180efc92e5c19d02cdda79066703ef" -"checksum futures-channel 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f366ad74c28cca6ba456d95e6422883cfb4b252a83bed929c83abfdbbf2967d5" -"checksum futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "59f5fff90fd5d971f936ad674802482ba441b6f09ba5e15fd8b39145582ca399" -"checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" -"checksum futures-executor 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "10d6bb888be1153d3abeb9006b11b02cf5e9b209fda28693c31ae1e4e012e314" -"checksum futures-macro 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d0b5a30a4328ab5473878237c447333c093297bded83a4983d10f4deea240d39" -"checksum futures-sink 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3f2032893cb734c7a05d85ce0cc8b8c4075278e93b24b66f9de99d6eb0fa8acc" -"checksum futures-task 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "bdb66b5f09e22019b1ab0830f7785bcea8e7a42148683f99214f73f8ec21a626" -"checksum futures-util 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8764574ff08b701a084482c3c7031349104b07ac897393010494beaa18ce32c6" -"checksum gcc 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)" = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" -"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" -"checksum getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)" = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" -"checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" -"checksum gimli 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f6503fe142514ca4799d4c26297c4248239fe8838d827db6bd6065c6ed29a6ce" -"checksum glib 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0c685013b7515e668f1b57a165b009d4d28cb139a8a989bbd699c10dad29d0c5" -"checksum glib-macros 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "41486a26d1366a8032b160b59065a59fb528530a46a49f627e7048fb8c064039" -"checksum glib-sys 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c7e9b997a66e9a23d073f2b1abb4dbfc3925e0b8952f67efd8d9b6e168e4cdc1" -"checksum glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" -"checksum gobject-sys 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "952133b60c318a62bf82ee75b93acc7e84028a093e06b9e27981c2b6fe68218c" -"checksum gstreamer 0.16.5 (registry+https://github.com/rust-lang/crates.io-index)" = "5d50f822055923f1cbede233aa5dfd4ee957cf328fb3076e330886094e11d6cf" -"checksum gstreamer-app 0.16.5 (registry+https://github.com/rust-lang/crates.io-index)" = "cc80888271338c3ede875d8cafc452eb207476ff5539dcbe0018a8f5b827af0e" -"checksum gstreamer-app-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "813f64275c9e7b33b828b9efcf9dfa64b95996766d4de996e84363ac65b87e3d" -"checksum gstreamer-base 0.16.5 (registry+https://github.com/rust-lang/crates.io-index)" = "bafd01c56f59cb10f4b5a10f97bb4bdf8c2b2784ae5b04da7e2d400cf6e6afcf" -"checksum gstreamer-base-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a4b7b6dc2d6e160a1ae28612f602bd500b3fa474ce90bf6bb2f08072682beef5" -"checksum gstreamer-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fc1f154082d01af5718c5f8a8eb4f565a4ea5586ad8833a8fc2c2aa6844b601d" -"checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" -"checksum hermit-abi 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "3deed196b6e7f9e44a2ae8d94225d80302d81208b1bb673fd21fe634645c85a9" -"checksum hex 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35" -"checksum hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5dcb5e64cda4c23119ab41ba960d1e170a774c8e4b9d9e6a9bc18aabf5e59695" -"checksum hostname 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" -"checksum httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" -"checksum humantime 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3c1ad908cc71012b7bea4d0c53ba96a8cba9962f048fa68d143376143d863b7a" -"checksum hyper 0.11.27 (registry+https://github.com/rust-lang/crates.io-index)" = "34a590ca09d341e94cddf8e5af0bbccde205d5fbc2fa3c09dd67c7f85cea59d7" -"checksum hyper-proxy 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "44f0925de2747e481e6e477dd212c25e8f745567f02f6182e04d27b97c3fbece" -"checksum ident_case 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" -"checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" -"checksum idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" -"checksum if-addrs 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f12906406f12abf5569643c46b29aec78313dc1537b17dd5c5250169790c4db9" -"checksum if-addrs-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9e2556f16544202bcfe0aa5d20a01a6b815f736b136b3ad76dc547ee6b5bb1df" -"checksum instant 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" -"checksum iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" -"checksum itertools 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" -"checksum itoa 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" -"checksum jack 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "7c1871c91fa65aa328f3bedbaa54a6e5d1de009264684c153eb708ba933aa6f5" -"checksum jack-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0d4ca501477fd3cd93a36df581046e5d6338ed826cf7e9b8d302603521e6cc3" -"checksum jni 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1981310da491a4f0f815238097d0d43d8072732b5ae5f8bd0d8eadf5bf245402" -"checksum jni 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "36bcc950632e48b86da402c5c077590583da5ac0d480103611d5374e7c967a3c" -"checksum jni-sys 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" -"checksum js-sys 0.3.46 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d7383929f7c9c7c2d0fa596f325832df98c3704f2c60553080f7127a58175" -"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" -"checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" -"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -"checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" -"checksum lewton 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8d542c1a317036c45c2aa1cf10cc9d403ca91eb2d333ef1a4917e5cb10628bd0" -"checksum libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)" = "bd7d4bd64732af4bf3a67f367c27df8520ad7e230c5817b8ff485864d80242b9" -"checksum libflate 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)" = "d9135df43b1f5d0e333385cb6e7897ecd1a43d7d11b91ac003f4d2c2d2401fdd" -"checksum libloading 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "fd38073de8f7965d0c17d30546d4bb6da311ab428d1c7a3fc71dff7f9d4979b9" -"checksum libloading 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f2b111a074963af1d37a139918ac6d49ad1d0d5e47f72fd55388619691a7d753" -"checksum libmdns 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "5d8582c174736c53633bc482ac709b24527c018356c3dc6d8e25a788b06b394e" -"checksum libpulse-binding 2.19.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1e8f85a42300c868de4849bb72eda5a65cea08c3ca61396b72c2d7c28a87f055" -"checksum libpulse-simple-binding 2.18.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a047f4502997eed57b3e9d8e71f2b860da91a20bb7e15c65d1f183a7b4fb1226" -"checksum libpulse-simple-sys 1.15.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9b72cb239bc4de6858fa0bbad27419e72cd4466f079ca56f21d94b0a712ab02e" -"checksum libpulse-sys 1.15.3 (registry+https://github.com/rust-lang/crates.io-index)" = "706e95c4b87ebb81c1e7763c74bf7d5ba897208f1a8aa5fc7bea8298dee8f2ca" -"checksum librespot-tremor 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b155a7dc4e4d272e01c37a1b85c1ee1bee7f04980ad4a7784c1a6e0f2de5929b" -"checksum linear-map 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bfae20f6b19ad527b550c223fddc3077a547fc70cda94b9b566575423fd303ee" -"checksum lock_api 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" -"checksum lock_api 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312" -"checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" -"checksum log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" -"checksum mach 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" -"checksum match_cfg 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" -"checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" -"checksum maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" -"checksum memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" -"checksum memoffset 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "c198b026e1bbf08a937e94c6c60f9ec4a2267f5b0d2eec9c1b21b061ce2be55f" -"checksum mime 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)" = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" -"checksum miniz_oxide 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0f2d26ec3309788e423cfbf68ad1800f061638098d76a83681af979dc4eda19d" -"checksum mio 0.6.22 (registry+https://github.com/rust-lang/crates.io-index)" = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430" -"checksum mio-named-pipes 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "0840c1c50fd55e521b247f949c241c9997709f23bd7f023b9762cd561e935656" -"checksum mio-uds 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0" -"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" -"checksum miow 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "07b88fb9795d4d36d62a012dfbf49a8f5cf12751f36d31a9dbe66d528e58979e" -"checksum muldiv 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0419348c027fa7be448d2ae7ea0e4e04c2334c31dc4e74ab29f00a2a7ca69204" -"checksum multimap 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d8883adfde9756c1d30b0f519c9b8c502a94b41ac62f696453c37c7fc0a958ce" -"checksum ndk 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5eb167c1febed0a496639034d0c76b3b74263636045db5489eee52143c246e73" -"checksum ndk-glue 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bdf399b8b7a39c6fb153c4ec32c72fd5fe789df24a647f229c239aa7adb15241" -"checksum ndk-macro 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05d1c6307dc424d0f65b9b06e94f88248e6305726b14729fd67a5e47b2dc481d" -"checksum ndk-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c44922cb3dbb1c70b5e5f443d63b64363a898564d739ba5198e3a9138442868d" -"checksum net2 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)" = "2ba7c918ac76704fb42afcbbb43891e72731f3dcca3bef2a19786297baf14af7" -"checksum nix 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3b2e0b4f3320ed72aaedb9a5ac838690a8047c7b275da22711fddff4f8a14229" -"checksum nix 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a2c5afeb0198ec7be8569d666644b574345aad2e95a53baf3a532da3e0f3fb32" -"checksum nom 5.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" -"checksum num-bigint 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5e9a41747ae4633fce5adffb4d2e81ffc5e89593cb19917f8fb2cc5ff76507bf" -"checksum num-derive 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" -"checksum num-integer 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b" -"checksum num-rational 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" -"checksum num-traits 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)" = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611" -"checksum num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" -"checksum num_enum 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ca565a7df06f3d4b485494f25ba05da1435950f4dc263440eda7a6fa9b8e36e4" -"checksum num_enum_derive 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ffa5a33ddddfee04c0283a7653987d634e880347e96b5b2ed64de07efb59db9d" -"checksum object 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8d3b63360ec3cb337817c2dbd47ab4a0f170d285d8e5a2064600f3def1402397" -"checksum oboe 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1aadc2b0867bdbb9a81c4d99b9b682958f49dbea1295a81d2f646cca2afdd9fc" -"checksum oboe-sys 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "68ff7a51600eabe34e189eec5c995a62f151d8d97e5fbca39e87ca738bb99b82" -"checksum ogg 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d79f1db9148be9d0e174bb3ac890f6030fcb1ed947267c5a91ee4c91b5a91e15" -"checksum ogg-sys 0.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "a95b8c172e17df1a41bf8d666301d3b2c4efeb90d9d0415e2a4dc0668b35fdb2" -"checksum once_cell 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0" -"checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" -"checksum parking_lot 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" -"checksum parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" -"checksum parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" -"checksum parking_lot_core 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c361aa727dd08437f2f1447be8b59a33b0edd15e0fcee698f935613d9efbca9b" -"checksum paste 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c5d65c4d95931acda4498f675e332fcbdc9a06705cd07086c510e9b6009cd1c1" -"checksum pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "006c038a43a45995a9670da19e67600114740e8511d4333bf97a56e66a7542d9" -"checksum peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" -"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" -"checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" -"checksum pin-project 0.4.22 (registry+https://github.com/rust-lang/crates.io-index)" = "12e3a6cdbfe94a5e4572812a0201f8c0ed98c1c452c7b8563ce2276988ef9c17" -"checksum pin-project-internal 0.4.22 (registry+https://github.com/rust-lang/crates.io-index)" = "6a0ffd45cf79d88737d7cc85bfd5d2894bee1139b356e616fe85dc389c61aaf7" -"checksum pin-project-lite 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c917123afa01924fc84bb20c4c03f004d9c38e5127e3c039bbf7f4b9c76a2f6b" -"checksum pin-utils 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" -"checksum pkg-config 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)" = "d36492546b6af1463394d46f0c834346f31548646f6ba10849802c9c9a27ac33" -"checksum portaudio-rs 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "cdb6b5eff96ccc9bf44d34c379ab03ae944426d83d1694345bdf8159d561d562" -"checksum portaudio-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5194a4fa953b4ffd851c320ef6f0484cd7278cb7169ea9d6c433e49b23f7b7f5" -"checksum ppv-lite86 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea" -"checksum pretty-hex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5c99d529f0d30937f6f4b8a86d988047327bb88d04d2c4afc356de74722131" -"checksum proc-macro-crate 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" -"checksum proc-macro-error 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -"checksum proc-macro-error-attr 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -"checksum proc-macro-hack 0.5.16 (registry+https://github.com/rust-lang/crates.io-index)" = "7e0456befd48169b9f13ef0f0ad46d492cf9d2dbb918bcf38e01eed4ce3ec5e4" -"checksum proc-macro-nested 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "eba180dafb9038b050a4c280019bbedf9f2467b61e5d892dcad585bb57aadc5a" -"checksum proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)" = "04f5f085b5d71e2188cb8271e5da0161ad52c3f227a661a3c135fdf28e258b12" -"checksum protobuf 2.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8e86d370532557ae7573551a1ec8235a0f8d6cb276c7c9e6aa490b511c447485" -"checksum protobuf-codegen 2.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de113bba758ccf2c1ef816b127c958001b7831136c9bc3f8e9ec695ac4e82b0c" -"checksum protobuf-codegen-pure 2.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2d1a4febc73bf0cada1d77c459a0c8e5973179f1cfd5b0f1ab789d45b17b6440" -"checksum qstring 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d464fae65fff2680baf48019211ce37aaec0c78e9264c84a3e484717f965104e" -"checksum quick-error 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" -"checksum quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" -"checksum rand 0.3.23 (registry+https://github.com/rust-lang/crates.io-index)" = "64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c" -"checksum rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" -"checksum rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9" -"checksum rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -"checksum rand_chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" -"checksum rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" -"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -"checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" -"checksum redox_syscall 0.1.57 (registry+https://github.com/rust-lang/crates.io-index)" = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" -"checksum regex 1.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "9c3780fcf44b193bc4d09f36d2a3c87b251da4a046c87795a0d35f4f927ad8e6" -"checksum regex-syntax 0.6.18 (registry+https://github.com/rust-lang/crates.io-index)" = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8" -"checksum relay 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1576e382688d7e9deecea24417e350d3062d97e32e45d70b1cde65994ff1489a" -"checksum remove_dir_all 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -"checksum ring 0.16.18 (registry+https://github.com/rust-lang/crates.io-index)" = "70017ed5c555d79ee3538fc63ca09c70ad8f317dcadc1adc2c496b60c22bb24f" -"checksum rle-decode-fast 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cabe4fa914dec5870285fa7f71f602645da47c486e68486d2b4ceb4a343e90ac" -"checksum rodio 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c9683532495146e98878d4948fa1a1953f584cd923f2a5f5c26b7a8701b56943" -"checksum rpassword 5.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d755237fc0f99d98641540e66abac8bc46a0652f19148ac9e21de2da06b326c9" -"checksum rustc-demangle 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "6e3bad0ee36814ca07d7968269dd4b7ec89ec2da10c4bb613928d3077083c232" -"checksum rustc-hash 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" -"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -"checksum rustls 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b25a18b1bf7387f0145e7f8324e700805aade3842dd3db2e74e4cdeb4677c09e" -"checksum ryu 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" -"checksum safemem 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" -"checksum same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -"checksum scoped-tls 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "332ffa32bf586782a3efaeb58f127980944bbc8c4d6913a86107ac2a5ab24b28" -"checksum scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" -"checksum sct 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e3042af939fca8c3453b7af0f1c66e533a15a86169e39de2657310ade8f98d3c" -"checksum sdl2 0.34.3 (registry+https://github.com/rust-lang/crates.io-index)" = "fcbb85f4211627a7291c83434d6bbfa723e28dcaa53c7606087e3c61929e4b9c" -"checksum sdl2-sys 0.34.3 (registry+https://github.com/rust-lang/crates.io-index)" = "28d81feded049b9c14eceb4a4f6d596a98cebbd59abdba949c5552a015466d33" -"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" -"checksum serde 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)" = "5317f7588f0a5078ee60ef675ef96735a1442132dc645eb1d12c018620ed8cd3" -"checksum serde_derive 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)" = "2a0be94b04690fbaed37cddffc5c134bf537c8e3329d53e982fe04c374978f8e" -"checksum serde_json 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)" = "3433e879a558dde8b5e8feb2a04899cf34fdde1fafb894687e52105fc1162ac3" -"checksum sha-1 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" -"checksum sha2 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69" -"checksum shannon 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7ea5b41c9427b56caa7b808cb548a04fb50bb5b9e98590b53f28064ff4174561" -"checksum shell-words 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b6fa3938c99da4914afedd13bf3d79bcb6c277d1b2c398d23257a304d9e1b074" -"checksum shlex 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" -"checksum signal-hook-registry 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94f478ede9f64724c5d173d7bb56099ec3e2d9fc2774aac65d34b8b890405f41" -"checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23" -"checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" -"checksum smallvec 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4c8cbcd6df1e117c2210e13ab5109635ad68a929fcbb8964dc965b76cb5ee013" -"checksum smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "f7b0758c52e15a8b5e3691eae6cc559f08eee9406e548a4477ba4e67770a82b6" -"checksum smallvec 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7acad6f34eb9e8a259d3283d1e8c1d34d7415943d4895f65cc73813c7396fc85" -"checksum socket2 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)" = "03088793f677dce356f3ccc2edb1b314ad191ab702a5de3faf49304f7e104918" -"checksum spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" -"checksum stdweb 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ef5430c8e36b713e13b48a9f709cc21e046723fe44ce34587b73a830203b533e" -"checksum stream-cipher 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8131256a5896cabcf5eb04f4d6dacbe1aefda854b0d9896e09cb58829ec5638c" -"checksum strsim 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" -"checksum strum 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)" = "57bd81eb48f4c437cadc685403cad539345bf703d78e63707418431cecd4522b" -"checksum strum_macros 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)" = "87c85aa3f8ea653bfd3ddf25f7ee357ee4d204731f6aa9ad04002306f6e2774c" -"checksum subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" -"checksum syn 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)" = "fb7f4c519df8c117855e19dd8cc851e89eb746fe7a73f0157e0d95fdec5369b0" -"checksum synstructure 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701" -"checksum system-deps 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0f3ecc17269a19353b3558b313bba738b25d82993e30d62a18406a24aba4649b" -"checksum take 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b157868d8ac1f56b64604539990685fa7611d8fa9e5476cf0c02cf34d32917c5" -"checksum take_mut 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" -"checksum tar 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "489997b7557e9a43e192c527face4feacc78bfbe6eed67fd55c4c9e381cba290" -"checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" -"checksum termcolor 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f" -"checksum thiserror 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)" = "318234ffa22e0920fe9a40d7b8369b5f649d490980cf7aadcf1eb91594869b42" -"checksum thiserror-impl 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)" = "cae2447b6282786c3493999f40a9be2a6ad20cb8bd268b0a0dbf5a065535c0ab" -"checksum time 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" -"checksum tinyvec 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "53953d2d3a5ad81d9f844a32f14ebb121f50b650cd59d0ee2a07cf13c617efed" -"checksum tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)" = "5a09c0b5bb588872ab2f09afa13ee6e9dac11e10a0ec9e8e3ba39a5a5d530af6" -"checksum tokio-codec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "25b2998660ba0e70d18684de5d06b70b70a3a747469af9dea7618cc59e75976b" -"checksum tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "aeeffbbb94209023feaef3c196a41cbcdafa06b4a6f893f68779bb5e53796f71" -"checksum tokio-current-thread 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "b1de0e32a83f131e002238d7ccde18211c0a5397f60cbfffcb112868c2e0e20e" -"checksum tokio-executor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "fb2d1b8f4548dbf5e1f7818512e9c406860678f29c300cdf0ebac72d1a3a1671" -"checksum tokio-fs 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "297a1206e0ca6302a0eed35b700d292b275256f596e2f3fea7729d5e629b6ff4" -"checksum tokio-io 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "57fc868aae093479e3131e3d165c93b1c7474109d13c90ec0dda2a1bbfff0674" -"checksum tokio-process 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "382d90f43fa31caebe5d3bc6cfd854963394fff3b8cb59d5146607aaae7e7e43" -"checksum tokio-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8fbb47ae81353c63c487030659494b295f6cb6576242f907f203473b191b0389" -"checksum tokio-reactor 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "09bc590ec4ba8ba87652da2068d150dcada2cfa2e07faae270a5e0409aa51351" -"checksum tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "24da22d077e0f15f55162bdbdc661228c1581892f52074fb242678d015b45162" -"checksum tokio-signal 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)" = "d0c34c6e548f101053321cba3da7cbb87a610b85555884c41b07da2eb91aff12" -"checksum tokio-sync 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "edfe50152bc8164fcc456dab7891fa9bf8beaf01c5ee7e1dd43a397c3cf87dee" -"checksum tokio-tcp 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "98df18ed66e3b72e742f185882a9e201892407957e45fbff8da17ae7a7c51f72" -"checksum tokio-threadpool 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "df720b6581784c118f0eb4310796b12b1d242a7eb95f716a8367855325c25f89" -"checksum tokio-timer 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "93044f2d313c95ff1cb7809ce9a7a05735b012288a888b62d4434fd58c94f296" -"checksum tokio-udp 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "e2a0b10e610b39c38b031a2fcab08e4b82f16ece36504988dcbd81dbba650d82" -"checksum tokio-uds 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "ab57a4ac4111c8c9dbcf70779f6fc8bc35ae4b2454809febac840ad19bd7e4e0" -"checksum toml 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)" = "75cf45bb0bef80604d001caaec0d09da99611b3c0fd39d3080468875cdb65645" -"checksum try-lock 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee2aa4715743892880f70885373966c83d73ef1b0838a664ef0c76fffd35e7c2" -"checksum typenum 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" -"checksum unicase 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" -"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" -"checksum unicode-normalization 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6fb19cf769fa8c6a80a162df694621ebeb4dafb606470b2b2fce0be40a98a977" -"checksum unicode-segmentation 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796" -"checksum unicode-width 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" -"checksum unicode-xid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" -"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" -"checksum untrusted 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" -"checksum ureq 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)" = "801125e6d1ba6864cf3a5a92cfb2f0b0a3ee73e40602a0cd206ad2f3c040aa96" -"checksum url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a" -"checksum url 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5909f2b0817350449ed73e8bcd81c8c3c8d9a7a5d8acba4b27db277f1868976e" -"checksum uuid 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9fde2f6a4bea1d6e007c4ad38c6839fa71cbb63b6dbf5b595aa38dc9b1093c11" -"checksum vergen 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ce50d8996df1f85af15f2cd8d33daae6e479575123ef4314a51a70a230739cb" -"checksum version-compare 0.0.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d63556a25bae6ea31b52e640d7c41d1ab27faba4ccb600013837a3d0b3994ca1" -"checksum version_check 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" -"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" -"checksum vorbis 0.0.14 (registry+https://github.com/rust-lang/crates.io-index)" = "5e8a194457075360557b82dac78f7ca2d65bbb6679bccfabae5f7c8c706cc776" -"checksum vorbis-sys 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3a0a8d7034313748da1d84b0adfa501f83f9ec83250f37fbacfa92a3580327c4" -"checksum vorbisfile-sys 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "4f4306d7e1ac4699b55e20de9483750b90c250913188efd7484db6bfbe9042d1" -"checksum walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d" -"checksum want 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a05d9d966753fa4b5c8db73fcab5eed4549cfe0e1e4e66911e5564a0085c35d1" -"checksum wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" -"checksum wasm-bindgen 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)" = "3cd364751395ca0f68cafb17666eee36b63077fb5ecd972bbcd74c90c4bf736e" -"checksum wasm-bindgen-backend 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)" = "1114f89ab1f4106e5b55e688b828c0ab0ea593a1ea7c094b141b14cbaaec2d62" -"checksum wasm-bindgen-macro 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6ac8995ead1f084a8dea1e65f194d0973800c7f571f6edd70adf06ecf77084" -"checksum wasm-bindgen-macro-support 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)" = "b5a48c72f299d80557c7c62e37e7225369ecc0c963964059509fbafe917c7549" -"checksum wasm-bindgen-shared 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)" = "7e7811dd7f9398f14cc76efd356f98f03aa30419dea46aa810d71e819fc97158" -"checksum web-sys 0.3.46 (registry+https://github.com/rust-lang/crates.io-index)" = "222b1ef9334f92a21d3fb53dc3fd80f30836959a90f9274a626d7e06315ba3c3" -"checksum webpki 0.21.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ab146130f5f790d45f82aeeb09e55a256573373ec64409fc19a6fb82fb1032ae" -"checksum webpki-roots 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)" = "91cd5736df7f12a964a5067a12c62fa38e1bd8080aff1f80bc29be7c80d19ab4" -"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" -"checksum winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" -"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -"checksum winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" -"checksum xattr 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "244c3741f4240ef46274860397c7c74e50eb23624996930e484c16679633a54c" -"checksum zerocopy 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6580539ad917b7c026220c4b3f2c08d52ce54d6ce0dc491e66002e35388fab46" -"checksum zerocopy-derive 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d498dbd1fd7beb83c86709ae1c33ca50942889473473d287d56ce4770a18edfb" diff --git a/Cargo.toml b/Cargo.toml index f56649d..cba117d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,8 +49,8 @@ num-bigint = "0.3" protobuf = "~2.14.0" rand = "0.7" rpassword = "5.0" -rpassword = "3.0" -tokio = "0.1" +# tokio = "0.1" +tokio = { version = "0.2", features = ["rt-core"] } tokio-io = "0.1" tokio-process = "0.2" tokio-signal = "0.2" From 0892587c0ef88d7ee6b1aaab604525b88c760b43 Mon Sep 17 00:00:00 2001 From: ashthespy Date: Sat, 23 Jan 2021 22:21:42 +0000 Subject: [PATCH 011/103] [Core] WIP: Sessions --- core/src/session.rs | 214 +++++++++++++++++++++----------------------- 1 file changed, 102 insertions(+), 112 deletions(-) diff --git a/core/src/session.rs b/core/src/session.rs index 821ae87..9a8df2b 100644 --- a/core/src/session.rs +++ b/core/src/session.rs @@ -10,13 +10,7 @@ use bytes::Bytes; // use tokio::runtime::{current_thread, current_thread::Handle}; // use futures::future::{IntoFuture, Remote}; -use futures::{ - channel::mpsc, - // future::{IntoFuture, Remote}, - Future, - Stream, - TryFutureExt, -}; +use futures::{channel::mpsc, future, Future, Stream, StreamExt, TryFutureExt}; use std::{ pin::Pin, task::{Context, Poll}, @@ -25,14 +19,14 @@ use std::{ use tokio::runtime::Handle; use crate::apresolve::apresolve_or_fallback; -use crate::audio_key::AudioKeyManager; +// use crate::audio_key::AudioKeyManager; use crate::authentication::Credentials; use crate::cache::Cache; -use crate::channel::ChannelManager; -use crate::component::Lazy; +// use crate::channel::ChannelManager; +// use crate::component::Lazy; use crate::config::SessionConfig; use crate::connection; -use crate::mercury::MercuryManager; +// use crate::mercury::MercuryManager; struct SessionData { country: String, @@ -45,13 +39,12 @@ struct SessionInternal { config: SessionConfig, data: RwLock, - tx_connection: mpsc::UnboundedSender<(u8, Vec)>, + tx_connection: mpsc::UnboundedSender)>>, - audio_key: Lazy, - channel: Lazy, - mercury: Lazy, + // audio_key: Lazy, + // channel: Lazy, + // mercury: Lazy, cache: Option>, - handle: Mutex, session_id: usize, } @@ -71,42 +64,44 @@ impl Session { cache: Option, handle: Handle, ) -> Result { - unimplemented!() - // let access_point_addr = - // apresolve_or_fallback::(&config.proxy, &config.ap_port).await?; - // - // let proxy = config.proxy.clone(); - // info!("Connecting to AP \"{}\"", access_point_addr); - // let connection = connection::connect(access_point_addr, &proxy); - // - // let device_id = config.device_id.clone(); - // let authentication = connection.and_then(move |connection| { - // connection::authenticate(connection, credentials, device_id) - // }); - // - // let result = authentication.map(move |(transport, reusable_credentials)| { - // info!("Authenticated as \"{}\" !", reusable_credentials.username); - // if let Some(ref cache) = cache { - // cache.save_credentials(&reusable_credentials); - // } - // - // let (session, task) = Session::create( - // &handle, - // transport, - // config, - // cache, - // reusable_credentials.username.clone(), - // ); - // - // tokio::spawn(task.map_err(|e| { - // error!("SessionError: {}", e.to_string()); - // std::process::exit(0); - // })); - // - // session - // }); - // - // result + let access_point_addr = + apresolve_or_fallback::(&config.proxy, &config.ap_port).await?; + + let proxy = config.proxy.clone(); + info!("Connecting to AP \"{}\"", access_point_addr); + let connection = connection::connect(access_point_addr, &proxy); + + let device_id = config.device_id.clone(); + let authentication = connection.and_then(move |connection| { + connection::authenticate(connection, credentials, device_id) + }); + + let result = match authentication.await { + Ok((transport, reusable_credentials)) => { + info!("Authenticated as \"{}\" !", reusable_credentials.username); + if let Some(ref cache) = cache { + cache.save_credentials(&reusable_credentials); + } + + let (session, tasks) = Session::create( + &handle, + transport, + config, + cache, + reusable_credentials.username.clone(), + ); + + tokio::task::spawn_local(async move { tasks }); + + Ok(session) + } + Err(e) => { + error!("Unable to Connect"); + Err(e.into()) + } + }; + + result } fn create( @@ -115,7 +110,7 @@ impl Session { config: SessionConfig, cache: Option, username: String, - ) -> (Session, Box>>) { + ) -> (Session, Box, Result<()>)>>) { let (sink, stream) = transport.split(); let (sender_tx, sender_rx) = mpsc::unbounded(); @@ -124,7 +119,7 @@ impl Session { debug!("new Session[{}]", session_id); let session = Session(Arc::new(SessionInternal { - config: config, + config, data: RwLock::new(SessionData { country: String::new(), canonical_username: username, @@ -136,57 +131,52 @@ impl Session { cache: cache.map(Arc::new), - audio_key: Lazy::new(), - channel: Lazy::new(), - mercury: Lazy::new(), - + // audio_key: Lazy::new(), + // channel: Lazy::new(), + // mercury: Lazy::new(), handle: Mutex::new(handle.clone()), - session_id: session_id, + session_id, })); let sender_task = sender_rx - .map_err(|e| -> io::Error { panic!(e) }) .forward(sink) - .map(|_| ()); + .map_err(|e| -> Box { Box::new(e) }); + let receiver_task = DispatchTask(stream, session.weak()); - let task = Box::new( - (receiver_task, sender_task) - .into_future() - .map(|((), ())| ()), - ); + let task = Box::new(future::join(receiver_task, sender_task)); (session, task) } - pub fn audio_key(&self) -> &AudioKeyManager { - self.0.audio_key.get(|| AudioKeyManager::new(self.weak())) - } + // pub fn audio_key(&self) -> &AudioKeyManager { + // self.0.audio_key.get(|| AudioKeyManager::new(self.weak())) + // } - pub fn channel(&self) -> &ChannelManager { - self.0.channel.get(|| ChannelManager::new(self.weak())) - } + // pub fn channel(&self) -> &ChannelManager { + // self.0.channel.get(|| ChannelManager::new(self.weak())) + // } - pub fn mercury(&self) -> &MercuryManager { - self.0.mercury.get(|| MercuryManager::new(self.weak())) - } + // pub fn mercury(&self) -> &MercuryManager { + // self.0.mercury.get(|| MercuryManager::new(self.weak())) + // } pub fn time_delta(&self) -> i64 { self.0.data.read().unwrap().time_delta } // Spawn a future directly - pub fn spawn(&self, f: F) - where - F: Future + Send + 'static, - { - let handle = self.0.handle.lock().unwrap(); - let spawn_res = handle.spawn(f); - match spawn_res { - Ok(_) => (), - Err(e) => error!("Session SpawnErr {:?}", e), - } - } + // pub fn spawn(&self, f: F) + // where + // F: Future + Send + 'static, + // { + // let handle = self.0.handle.lock().unwrap(); + // let spawn_res = handle.spawn(f); + // match spawn_res { + // Ok(_) => (), + // Err(e) => error!("Session SpawnErr {:?}", e), + // } + // } // pub fn spawn(&self, f: F) // where @@ -218,7 +208,7 @@ impl Session { ); } - #[cfg_attr(feature = "cargo-clippy", allow(match_same_arms))] + // #[cfg_attr(feature = "cargo-clippy", allow(match_same_arms))] fn dispatch(&self, cmd: u8, data: Bytes) { match cmd { 0x4 => { @@ -241,15 +231,18 @@ impl Session { self.0.data.write().unwrap().country = country; } - 0x9 | 0xa => self.channel().dispatch(cmd, data), - 0xd | 0xe => self.audio_key().dispatch(cmd, data), - 0xb2..=0xb6 => self.mercury().dispatch(cmd, data), + // 0x9 | 0xa => self.channel().dispatch(cmd, data), + // 0xd | 0xe => self.audio_key().dispatch(cmd, data), + // 0xb2..=0xb6 => self.mercury().dispatch(cmd, data), _ => trace!("Unknown dispatch cmd :{:?} {:?}", cmd, data), } } pub fn send_packet(&self, cmd: u8, data: Vec) { - self.0.tx_connection.unbounded_send((cmd, data)).unwrap(); + self.0 + .tx_connection + .unbounded_send(Ok((cmd, data))) + .unwrap(); } pub fn cache(&self) -> Option<&Arc> { @@ -283,8 +276,8 @@ impl Session { pub fn shutdown(&self) { debug!("Invalidating session[{}]", self.0.session_id); self.0.data.write().unwrap().invalid = true; - self.mercury().shutdown(); - self.channel().shutdown(); + // self.mercury().shutdown(); + // self.channel().shutdown(); } pub fn is_invalid(&self) -> bool { @@ -311,40 +304,37 @@ impl Drop for SessionInternal { } } -// type SErr = ::std::fmt::Debug; - struct DispatchTask(S, SessionWeak) where - S: Stream>; + S: Stream> + Unpin; -impl Future for DispatchTask +impl>> Future for DispatchTask where - // SErr: ::std::fmt::Debug, - S: Stream>, + S: Stream> + Unpin, { - type Output = Result<((), ())>; + type Output = Result<()>; - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { let session = match self.1.try_upgrade() { Some(session) => session, - None => return Poll::Ready(()), + None => return Poll::Ready(Ok(())), }; loop { - let (cmd, data) = match self.unwrap().0.poll() { - Poll::Ready(Ok(Some(t))) => t, - Poll::Ready(Ok(None)) => { + let (cmd, data) = match Pin::new(&mut self.0).poll_next(cx) { + Poll::Ready(Some(Ok(t))) => t, + Poll::Ready(Some(Err(e))) => { + warn!("Server Connectioned errored"); + session.shutdown(); + return Poll::Ready(Err(Box::new(e))); + } + Poll::Ready(None) => { warn!("Connection to server closed."); session.shutdown(); - return Ok(Poll::Ready(())); + return Poll::Ready(Ok(())); } Poll::Pending => return Poll::Pending, - Poll::Ready(Err(e)) => { - session.shutdown(); - return Err(From::from(e)); - } }; - session.dispatch(cmd, data); } } @@ -352,7 +342,7 @@ where impl Drop for DispatchTask where - S: Stream>, + S: Stream> + Unpin, { fn drop(&mut self) { debug!("drop Dispatch"); From 40e6355c34a72f7ac0980770082e06099eb91451 Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Thu, 21 Jan 2021 21:49:39 +0100 Subject: [PATCH 012/103] Migrate core to tokio 1.0 --- core/Cargo.toml | 26 +++-- core/src/apresolve.rs | 126 +++++++++-------------- core/src/audio_key.rs | 22 +--- core/src/authentication.rs | 10 +- core/src/channel.rs | 91 +++++++++------- core/src/component.rs | 26 ----- core/src/connection/codec.rs | 7 +- core/src/connection/handshake.rs | 171 +++++++++---------------------- core/src/connection/mod.rs | 122 +++++++++------------- core/src/diffie_hellman.rs | 12 +-- core/src/keymaster.rs | 20 ++-- core/src/lib.rs | 24 ++--- core/src/mercury/mod.rs | 94 ++++++++--------- core/src/mercury/sender.rs | 35 ++++--- core/src/proxytunnel.rs | 135 +++++++----------------- core/src/session.rs | 146 +++++++++++--------------- 16 files changed, 406 insertions(+), 661 deletions(-) diff --git a/core/Cargo.toml b/core/Cargo.toml index 8511878..a9fcc24 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -13,34 +13,32 @@ path = "../protocol" version = "0.1.3" [dependencies] +aes = "0.6" base64 = "0.13" -byteorder = "1.3" -bytes = "0.4" -error-chain = { version = "0.12", default_features = false } -futures = "0.1" +byteorder = "1.4" +bytes = "1.0" +futures = { version = "0.3", features = ["bilock", "unstable"] } +hmac = "0.7" httparse = "1.3" -hyper = "0.11" -hyper-proxy = { version = "0.4", default_features = false } -lazy_static = "1.3" +hyper = { version = "0.14", features = ["client", "tcp", "http1", "http2", "stream"] } log = "0.4" num-bigint = "0.3" num-integer = "0.1" num-traits = "0.2" +once_cell = "1.5.2" +pbkdf2 = "0.3" +pin-project = "1.0" protobuf = "~2.14.0" rand = "0.7" serde = "1.0" serde_derive = "1.0" serde_json = "1.0" +sha-1 = "~0.8" shannon = "0.2.0" -tokio-codec = "0.1" -tokio-core = "0.1" -tokio-io = "0.1" +tokio = { version = "1.0", features = ["io-util", "rt-multi-thread", "macros" ] } +tokio-util = { version = "0.6", features = ["codec"] } url = "1.7" uuid = { version = "0.8", features = ["v4"] } -sha-1 = "0.8" -hmac = "0.7" -pbkdf2 = "0.3" -aes = "0.3" [build-dependencies] rand = "0.7" diff --git a/core/src/apresolve.rs b/core/src/apresolve.rs index 94d9424..07c2958 100644 --- a/core/src/apresolve.rs +++ b/core/src/apresolve.rs @@ -1,101 +1,69 @@ const AP_FALLBACK: &'static str = "ap.spotify.com:443"; const APRESOLVE_ENDPOINT: &'static str = "http://apresolve.spotify.com/"; -use futures::{Future, Stream}; -use hyper::client::HttpConnector; -use hyper::{self, Client, Method, Request, Uri}; -use hyper_proxy::{Intercept, Proxy, ProxyConnector}; -use serde_json; -use std::str::FromStr; -use tokio_core::reactor::Handle; +use hyper::{Body, Client, Method, Request, Uri}; +use std::error::Error; use url::Url; -error_chain! {} - #[derive(Clone, Debug, Serialize, Deserialize)] pub struct APResolveData { ap_list: Vec, } -fn apresolve( - handle: &Handle, - proxy: &Option, - ap_port: &Option, -) -> Box> { - let url = Uri::from_str(APRESOLVE_ENDPOINT).expect("invalid AP resolve URL"); - let use_proxy = proxy.is_some(); +async fn apresolve(proxy: &Option, ap_port: &Option) -> Result> { + let port = ap_port.unwrap_or(443); - let mut req = Request::new(Method::Get, url.clone()); - let response = match *proxy { - Some(ref val) => { - let proxy_url = Uri::from_str(val.as_str()).expect("invalid http proxy"); - let proxy = Proxy::new(Intercept::All, proxy_url); - let connector = HttpConnector::new(4, handle); + let req = Request::builder() + .method(Method::GET) + .uri( + APRESOLVE_ENDPOINT + .parse::() + .expect("invalid AP resolve URL"), + ) + .body(Body::empty())?; + + let client = if proxy.is_some() { + todo!("proxies not yet supported") + /*let proxy = { + let proxy_url = val.as_str().parse().expect("invalid http proxy"); + let mut proxy = Proxy::new(Intercept::All, proxy_url); + let connector = HttpConnector::new(); let proxy_connector = ProxyConnector::from_proxy_unsecured(connector, proxy); - if let Some(headers) = proxy_connector.http_headers(&url) { - req.headers_mut().extend(headers.iter()); - req.set_proxy(true); - } - let client = Client::configure().connector(proxy_connector).build(handle); - client.request(req) - } - _ => { - let client = Client::new(handle); - client.request(req) - } + proxy_connector + }; + + if let Some(headers) = proxy.http_headers(&APRESOLVE_ENDPOINT.parse().unwrap()) { + req.headers_mut().extend(headers.clone()); + }; + Client::builder().build(proxy)*/ + } else { + Client::new() }; - let body = response.and_then(|response| { - response.body().fold(Vec::new(), |mut acc, chunk| { - acc.extend_from_slice(chunk.as_ref()); - Ok::<_, hyper::Error>(acc) - }) - }); - let body = body.then(|result| result.chain_err(|| "HTTP error")); - let body = - body.and_then(|body| String::from_utf8(body).chain_err(|| "invalid UTF8 in response")); + let response = client.request(req).await?; - let data = body - .and_then(|body| serde_json::from_str::(&body).chain_err(|| "invalid JSON")); + let body = hyper::body::to_bytes(response.into_body()).await?; + let data: APResolveData = serde_json::from_slice(body.as_ref())?; - let p = ap_port.clone(); - - let ap = data.and_then(move |data| { - let mut aps = data.ap_list.iter().filter(|ap| { - if p.is_some() { - Uri::from_str(ap).ok().map_or(false, |uri| { - uri.port().map_or(false, |port| port == p.unwrap()) - }) - } else if use_proxy { - // It is unlikely that the proxy will accept CONNECT on anything other than 443. - Uri::from_str(ap) - .ok() - .map_or(false, |uri| uri.port().map_or(false, |port| port == 443)) + let ap = if ap_port.is_some() || proxy.is_some() { + data.ap_list.into_iter().find_map(|ap| { + if ap.parse::().ok()?.port()? == port { + Some(ap) } else { - true + None } - }); - - let ap = aps.next().ok_or("empty AP List")?; - Ok(ap.clone()) - }); - - Box::new(ap) + }) + } else { + data.ap_list.into_iter().next() + } + .ok_or("empty AP List")?; + Ok(ap) } -pub(crate) fn apresolve_or_fallback( - handle: &Handle, - proxy: &Option, - ap_port: &Option, -) -> Box> -where - E: 'static, -{ - let ap = apresolve(handle, proxy, ap_port).or_else(|e| { - warn!("Failed to resolve Access Point: {}", e.description()); +pub async fn apresolve_or_fallback(proxy: &Option, ap_port: &Option) -> String { + apresolve(proxy, ap_port).await.unwrap_or_else(|e| { + warn!("Failed to resolve Access Point: {}", e); warn!("Using fallback \"{}\"", AP_FALLBACK); - Ok(AP_FALLBACK.into()) - }); - - Box::new(ap) + AP_FALLBACK.into() + }) } diff --git a/core/src/audio_key.rs b/core/src/audio_key.rs index 1e5310c..b9f0c23 100644 --- a/core/src/audio_key.rs +++ b/core/src/audio_key.rs @@ -1,7 +1,6 @@ use byteorder::{BigEndian, ByteOrder, WriteBytesExt}; use bytes::Bytes; -use futures::sync::oneshot; -use futures::{Async, Future, Poll}; +use futures::channel::oneshot; use std::collections::HashMap; use std::io::Write; @@ -47,7 +46,7 @@ impl AudioKeyManager { } } - pub fn request(&self, track: SpotifyId, file: FileId) -> AudioKeyFuture { + pub async fn request(&self, track: SpotifyId, file: FileId) -> Result { let (tx, rx) = oneshot::channel(); let seq = self.lock(move |inner| { @@ -57,7 +56,7 @@ impl AudioKeyManager { }); self.send_key_request(seq, track, file); - AudioKeyFuture(rx) + rx.await.map_err(|_| AudioKeyError)? } fn send_key_request(&self, seq: u32, track: SpotifyId, file: FileId) { @@ -70,18 +69,3 @@ impl AudioKeyManager { self.session().send_packet(0xc, data) } } - -pub struct AudioKeyFuture(oneshot::Receiver>); -impl Future for AudioKeyFuture { - type Item = T; - type Error = AudioKeyError; - - fn poll(&mut self) -> Poll { - match self.0.poll() { - Ok(Async::Ready(Ok(value))) => Ok(Async::Ready(value)), - Ok(Async::Ready(Err(err))) => Err(err), - Ok(Async::NotReady) => Ok(Async::NotReady), - Err(oneshot::Canceled) => Err(AudioKeyError), - } - } -} diff --git a/core/src/authentication.rs b/core/src/authentication.rs index 36cbd43..dd39fd8 100644 --- a/core/src/authentication.rs +++ b/core/src/authentication.rs @@ -1,11 +1,9 @@ use aes::Aes192; -use base64; +use aes::NewBlockCipher; use byteorder::{BigEndian, ByteOrder}; use hmac::Hmac; use pbkdf2::pbkdf2; use protobuf::ProtobufEnum; -use serde; -use serde_json; use sha1::{Digest, Sha1}; use std::fs::File; use std::io::{self, Read, Write}; @@ -76,9 +74,9 @@ impl Credentials { // decrypt data using ECB mode without padding let blob = { - use aes::block_cipher_trait::generic_array::typenum::Unsigned; - use aes::block_cipher_trait::generic_array::GenericArray; - use aes::block_cipher_trait::BlockCipher; + use aes::cipher::generic_array::typenum::Unsigned; + use aes::cipher::generic_array::GenericArray; + use aes::cipher::BlockCipher; let mut data = base64::decode(encrypted_blob).unwrap(); let cipher = Aes192::new(GenericArray::from_slice(&key)); diff --git a/core/src/channel.rs b/core/src/channel.rs index b614fac..7ada05d 100644 --- a/core/src/channel.rs +++ b/core/src/channel.rs @@ -1,9 +1,12 @@ use byteorder::{BigEndian, ByteOrder}; use bytes::Bytes; -use futures::sync::{mpsc, BiLock}; -use futures::{Async, Poll, Stream}; -use std::collections::HashMap; -use std::time::Instant; +use futures::{channel::mpsc, lock::BiLock, Stream, StreamExt}; +use std::{ + collections::HashMap, + pin::Pin, + task::{Context, Poll}, + time::Instant, +}; use crate::util::SeqGenerator; @@ -101,12 +104,10 @@ impl ChannelManager { } impl Channel { - fn recv_packet(&mut self) -> Poll { - let (cmd, packet) = match self.receiver.poll() { - Ok(Async::Ready(Some(t))) => t, - Ok(Async::Ready(None)) => return Err(ChannelError), // The channel has been closed. - Ok(Async::NotReady) => return Ok(Async::NotReady), - Err(()) => unreachable!(), + fn recv_packet(&mut self, cx: &mut Context<'_>) -> Poll> { + let (cmd, packet) = match self.receiver.poll_next_unpin(cx) { + Poll::Pending => return Poll::Pending, + Poll::Ready(o) => o.ok_or(ChannelError)?, }; if cmd == 0xa { @@ -115,9 +116,9 @@ impl Channel { self.state = ChannelState::Closed; - Err(ChannelError) + Poll::Ready(Err(ChannelError)) } else { - Ok(Async::Ready(packet)) + Poll::Ready(Ok(packet)) } } @@ -129,16 +130,19 @@ impl Channel { } impl Stream for Channel { - type Item = ChannelEvent; - type Error = ChannelError; + type Item = Result; - fn poll(&mut self) -> Poll, Self::Error> { + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { loop { match self.state.clone() { ChannelState::Closed => panic!("Polling already terminated channel"), ChannelState::Header(mut data) => { if data.len() == 0 { - data = try_ready!(self.recv_packet()); + data = match self.recv_packet(cx) { + Poll::Ready(Ok(x)) => x, + Poll::Ready(Err(x)) => return Poll::Ready(Some(Err(x))), + Poll::Pending => return Poll::Pending, + }; } let length = BigEndian::read_u16(data.split_to(2).as_ref()) as usize; @@ -152,19 +156,23 @@ impl Stream for Channel { self.state = ChannelState::Header(data); let event = ChannelEvent::Header(header_id, header_data); - return Ok(Async::Ready(Some(event))); + return Poll::Ready(Some(Ok(event))); } } ChannelState::Data => { - let data = try_ready!(self.recv_packet()); + let data = match self.recv_packet(cx) { + Poll::Ready(Ok(x)) => x, + Poll::Ready(Err(x)) => return Poll::Ready(Some(Err(x))), + Poll::Pending => return Poll::Pending, + }; if data.len() == 0 { self.receiver.close(); self.state = ChannelState::Closed; - return Ok(Async::Ready(None)); + return Poll::Ready(None); } else { let event = ChannelEvent::Data(data); - return Ok(Async::Ready(Some(event))); + return Poll::Ready(Some(Ok(event))); } } } @@ -173,38 +181,45 @@ impl Stream for Channel { } impl Stream for ChannelData { - type Item = Bytes; - type Error = ChannelError; + type Item = Result; - fn poll(&mut self) -> Poll, Self::Error> { - let mut channel = match self.0.poll_lock() { - Async::Ready(c) => c, - Async::NotReady => return Ok(Async::NotReady), + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut channel = match self.0.poll_lock(cx) { + Poll::Ready(c) => c, + Poll::Pending => return Poll::Pending, }; loop { - match try_ready!(channel.poll()) { + let x = match channel.poll_next_unpin(cx) { + Poll::Ready(x) => x.transpose()?, + Poll::Pending => return Poll::Pending, + }; + match x { Some(ChannelEvent::Header(..)) => (), - Some(ChannelEvent::Data(data)) => return Ok(Async::Ready(Some(data))), - None => return Ok(Async::Ready(None)), + Some(ChannelEvent::Data(data)) => return Poll::Ready(Some(Ok(data))), + None => return Poll::Ready(None), } } } } impl Stream for ChannelHeaders { - type Item = (u8, Vec); - type Error = ChannelError; + type Item = Result<(u8, Vec), ChannelError>; - fn poll(&mut self) -> Poll, Self::Error> { - let mut channel = match self.0.poll_lock() { - Async::Ready(c) => c, - Async::NotReady => return Ok(Async::NotReady), + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut channel = match self.0.poll_lock(cx) { + Poll::Ready(c) => c, + Poll::Pending => return Poll::Pending, }; - match try_ready!(channel.poll()) { - Some(ChannelEvent::Header(id, data)) => Ok(Async::Ready(Some((id, data)))), - Some(ChannelEvent::Data(..)) | None => Ok(Async::Ready(None)), + let x = match channel.poll_next_unpin(cx) { + Poll::Ready(x) => x.transpose()?, + Poll::Pending => return Poll::Pending, + }; + + match x { + Some(ChannelEvent::Header(id, data)) => Poll::Ready(Some(Ok((id, data)))), + Some(ChannelEvent::Data(..)) | None => Poll::Ready(None), } } } diff --git a/core/src/component.rs b/core/src/component.rs index 50ab7b3..a761c45 100644 --- a/core/src/component.rs +++ b/core/src/component.rs @@ -35,29 +35,3 @@ macro_rules! component { } } } - -use std::cell::UnsafeCell; -use std::sync::Mutex; - -pub(crate) struct Lazy(Mutex, UnsafeCell>); -unsafe impl Sync for Lazy {} -unsafe impl Send for Lazy {} - -#[cfg_attr(feature = "cargo-clippy", allow(mutex_atomic))] -impl Lazy { - pub(crate) fn new() -> Lazy { - Lazy(Mutex::new(false), UnsafeCell::new(None)) - } - - pub(crate) fn get T>(&self, f: F) -> &T { - let mut inner = self.0.lock().unwrap(); - if !*inner { - unsafe { - *self.1.get() = Some(f()); - } - *inner = true; - } - - unsafe { &*self.1.get() }.as_ref().unwrap() - } -} diff --git a/core/src/connection/codec.rs b/core/src/connection/codec.rs index fa4cd9d..ead07b6 100644 --- a/core/src/connection/codec.rs +++ b/core/src/connection/codec.rs @@ -2,7 +2,7 @@ use byteorder::{BigEndian, ByteOrder}; use bytes::{BufMut, Bytes, BytesMut}; use shannon::Shannon; use std::io; -use tokio_io::codec::{Decoder, Encoder}; +use tokio_util::codec::{Decoder, Encoder}; const HEADER_SIZE: usize = 3; const MAC_SIZE: usize = 4; @@ -35,8 +35,7 @@ impl APCodec { } } -impl Encoder for APCodec { - type Item = (u8, Vec); +impl Encoder<(u8, Vec)> for APCodec { type Error = io::Error; fn encode(&mut self, item: (u8, Vec), buf: &mut BytesMut) -> io::Result<()> { @@ -45,7 +44,7 @@ impl Encoder for APCodec { buf.reserve(3 + payload.len()); buf.put_u8(cmd); - buf.put_u16_be(payload.len() as u16); + buf.put_u16(payload.len() as u16); buf.extend_from_slice(&payload); self.encode_cipher.nonce_u32(self.encode_nonce); diff --git a/core/src/connection/handshake.rs b/core/src/connection/handshake.rs index 220ab6e..3810fc9 100644 --- a/core/src/connection/handshake.rs +++ b/core/src/connection/handshake.rs @@ -1,14 +1,11 @@ use byteorder::{BigEndian, ByteOrder, WriteBytesExt}; -use futures::{Async, Future, Poll}; use hmac::{Hmac, Mac}; use protobuf::{self, Message}; use rand::thread_rng; use sha1::Sha1; -use std::io::{self, Read}; -use std::marker::PhantomData; -use tokio_codec::{Decoder, Framed}; -use tokio_io::io::{read_exact, write_all, ReadExact, Window, WriteAll}; -use tokio_io::{AsyncRead, AsyncWrite}; +use std::io; +use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; +use tokio_util::codec::{Decoder, Framed}; use super::codec::APCodec; use crate::diffie_hellman::DHLocalKeys; @@ -16,72 +13,33 @@ use crate::protocol; use crate::protocol::keyexchange::{APResponseMessage, ClientHello, ClientResponsePlaintext}; use crate::util; -pub struct Handshake { - keys: DHLocalKeys, - state: HandshakeState, -} - -enum HandshakeState { - ClientHello(WriteAll>), - APResponse(RecvPacket), - ClientResponse(Option, WriteAll>), -} - -pub fn handshake(connection: T) -> Handshake { +pub async fn handshake( + mut connection: T, +) -> io::Result> { let local_keys = DHLocalKeys::random(&mut thread_rng()); - let client_hello = client_hello(connection, local_keys.public_key()); + let gc = local_keys.public_key(); + let mut accumulator = client_hello(&mut connection, gc).await?; + let message: APResponseMessage = recv_packet(&mut connection, &mut accumulator).await?; + let remote_key = message + .get_challenge() + .get_login_crypto_challenge() + .get_diffie_hellman() + .get_gs() + .to_owned(); - Handshake { - keys: local_keys, - state: HandshakeState::ClientHello(client_hello), - } + let shared_secret = local_keys.shared_secret(&remote_key); + let (challenge, send_key, recv_key) = compute_keys(&shared_secret, &accumulator); + let codec = APCodec::new(&send_key, &recv_key); + + client_response(&mut connection, challenge).await?; + + Ok(codec.framed(connection)) } -impl Future for Handshake { - type Item = Framed; - type Error = io::Error; - - fn poll(&mut self) -> Poll { - use self::HandshakeState::*; - loop { - self.state = match self.state { - ClientHello(ref mut write) => { - let (connection, accumulator) = try_ready!(write.poll()); - - let read = recv_packet(connection, accumulator); - APResponse(read) - } - - APResponse(ref mut read) => { - let (connection, message, accumulator) = try_ready!(read.poll()); - let remote_key = message - .get_challenge() - .get_login_crypto_challenge() - .get_diffie_hellman() - .get_gs() - .to_owned(); - - let shared_secret = self.keys.shared_secret(&remote_key); - let (challenge, send_key, recv_key) = - compute_keys(&shared_secret, &accumulator); - let codec = APCodec::new(&send_key, &recv_key); - - let write = client_response(connection, challenge); - ClientResponse(Some(codec), write) - } - - ClientResponse(ref mut codec, ref mut write) => { - let (connection, _) = try_ready!(write.poll()); - let codec = codec.take().unwrap(); - let framed = codec.framed(connection); - return Ok(Async::Ready(framed)); - } - } - } - } -} - -fn client_hello(connection: T, gc: Vec) -> WriteAll> { +async fn client_hello(connection: &mut T, gc: Vec) -> io::Result> +where + T: AsyncWrite + Unpin, +{ let mut packet = ClientHello::new(); packet .mut_build_info() @@ -106,13 +64,17 @@ fn client_hello(connection: T, gc: Vec) -> WriteAll(size).unwrap(); + as WriteBytesExt>::write_u32::(&mut buffer, size).unwrap(); packet.write_to_vec(&mut buffer).unwrap(); - write_all(connection, buffer) + connection.write_all(&buffer[..]).await?; + Ok(buffer) } -fn client_response(connection: T, challenge: Vec) -> WriteAll> { +async fn client_response(connection: &mut T, challenge: Vec) -> io::Result<()> +where + T: AsyncWrite + Unpin, +{ let mut packet = ClientResponsePlaintext::new(); packet .mut_login_crypto_response() @@ -123,70 +85,35 @@ fn client_response(connection: T, challenge: Vec) -> WriteAll let mut buffer = vec![]; let size = 4 + packet.compute_size(); - buffer.write_u32::(size).unwrap(); + as WriteBytesExt>::write_u32::(&mut buffer, size).unwrap(); packet.write_to_vec(&mut buffer).unwrap(); - write_all(connection, buffer) + connection.write_all(&buffer[..]).await?; + Ok(()) } -enum RecvPacket { - Header(ReadExact>>, PhantomData), - Body(ReadExact>>, PhantomData), -} - -fn recv_packet(connection: T, acc: Vec) -> RecvPacket +async fn recv_packet(connection: &mut T, acc: &mut Vec) -> io::Result where - T: Read, + T: AsyncRead + Unpin, M: Message, { - RecvPacket::Header(read_into_accumulator(connection, 4, acc), PhantomData) + let header = read_into_accumulator(connection, 4, acc).await?; + let size = BigEndian::read_u32(header) as usize; + let data = read_into_accumulator(connection, size - 4, acc).await?; + let message = protobuf::parse_from_bytes(data).unwrap(); + Ok(message) } -impl Future for RecvPacket -where - T: Read, - M: Message, -{ - type Item = (T, M, Vec); - type Error = io::Error; - - fn poll(&mut self) -> Poll { - use self::RecvPacket::*; - loop { - *self = match *self { - Header(ref mut read, _) => { - let (connection, header) = try_ready!(read.poll()); - let size = BigEndian::read_u32(header.as_ref()) as usize; - - let acc = header.into_inner(); - let read = read_into_accumulator(connection, size - 4, acc); - RecvPacket::Body(read, PhantomData) - } - - Body(ref mut read, _) => { - let (connection, data) = try_ready!(read.poll()); - let message = protobuf::parse_from_bytes(data.as_ref()).unwrap(); - - let acc = data.into_inner(); - return Ok(Async::Ready((connection, message, acc))); - } - } - } - } -} - -fn read_into_accumulator( - connection: T, +async fn read_into_accumulator<'a, T: AsyncRead + Unpin>( + connection: &mut T, size: usize, - mut acc: Vec, -) -> ReadExact>> { + acc: &'a mut Vec, +) -> io::Result<&'a mut [u8]> { let offset = acc.len(); acc.resize(offset + size, 0); - let mut window = Window::new(acc); - window.set_start(offset); - - read_exact(connection, window) + connection.read_exact(&mut acc[offset..]).await?; + Ok(&mut acc[offset..]) } fn compute_keys(shared_secret: &[u8], packets: &[u8]) -> (Vec, Vec, Vec) { diff --git a/core/src/connection/mod.rs b/core/src/connection/mod.rs index 7249779..eba6407 100644 --- a/core/src/connection/mod.rs +++ b/core/src/connection/mod.rs @@ -4,13 +4,12 @@ mod handshake; pub use self::codec::APCodec; pub use self::handshake::handshake; -use futures::{Future, Sink, Stream}; +use futures::{SinkExt, StreamExt}; use protobuf::{self, Message}; use std::io; use std::net::ToSocketAddrs; -use tokio_codec::Framed; -use tokio_core::net::TcpStream; -use tokio_core::reactor::Handle; +use tokio::net::TcpStream; +use tokio_util::codec::Framed; use url::Url; use crate::authentication::Credentials; @@ -20,53 +19,36 @@ use crate::proxytunnel; pub type Transport = Framed; -pub fn connect( - addr: String, - handle: &Handle, - proxy: &Option, -) -> Box> { - let (addr, connect_url) = match *proxy { - Some(ref url) => { - info!("Using proxy \"{}\"", url); - match url.to_socket_addrs().and_then(|mut iter| { - iter.next().ok_or(io::Error::new( +pub async fn connect(addr: String, proxy: &Option) -> io::Result { + let socket = if let Some(proxy) = proxy { + info!("Using proxy \"{}\"", proxy); + let socket_addr = proxy.to_socket_addrs().and_then(|mut iter| { + iter.next().ok_or_else(|| { + io::Error::new( io::ErrorKind::NotFound, "Can't resolve proxy server address", - )) - }) { - Ok(socket_addr) => (socket_addr, Some(addr)), - Err(error) => return Box::new(futures::future::err(error)), - } - } - None => { - match addr.to_socket_addrs().and_then(|mut iter| { - iter.next().ok_or(io::Error::new( - io::ErrorKind::NotFound, - "Can't resolve server address", - )) - }) { - Ok(socket_addr) => (socket_addr, None), - Err(error) => return Box::new(futures::future::err(error)), - } - } + ) + }) + })?; + let socket = TcpStream::connect(&socket_addr).await?; + proxytunnel::connect(socket, &addr).await? + } else { + let socket_addr = addr.to_socket_addrs().and_then(|mut iter| { + iter.next().ok_or_else(|| { + io::Error::new(io::ErrorKind::NotFound, "Can't resolve server address") + }) + })?; + TcpStream::connect(&socket_addr).await? }; - let socket = TcpStream::connect(&addr, handle); - if let Some(connect_url) = connect_url { - let connection = socket - .and_then(move |socket| proxytunnel::connect(socket, &connect_url).and_then(handshake)); - Box::new(connection) - } else { - let connection = socket.and_then(handshake); - Box::new(connection) - } + handshake(socket).await } -pub fn authenticate( - transport: Transport, +pub async fn authenticate( + transport: &mut Transport, credentials: Credentials, - device_id: String, -) -> Box> { + device_id: &str, +) -> io::Result { use crate::protocol::authentication::{APWelcome, ClientResponseEncrypted, CpuFamily, Os}; use crate::protocol::keyexchange::APLoginFailed; @@ -91,41 +73,37 @@ pub fn authenticate( version::short_sha(), version::build_id() )); - packet.mut_system_info().set_device_id(device_id); + packet + .mut_system_info() + .set_device_id(device_id.to_string()); packet.set_version_string(version::version_string()); let cmd = 0xab; let data = packet.write_to_bytes().unwrap(); - Box::new( - transport - .send((cmd, data)) - .and_then(|transport| transport.into_future().map_err(|(err, _stream)| err)) - .and_then(|(packet, transport)| match packet { - Some((0xac, data)) => { - let welcome_data: APWelcome = - protobuf::parse_from_bytes(data.as_ref()).unwrap(); + transport.send((cmd, data)).await?; + let (cmd, data) = transport.next().await.expect("EOF")?; + match cmd { + 0xac => { + let welcome_data: APWelcome = protobuf::parse_from_bytes(data.as_ref()).unwrap(); - let reusable_credentials = Credentials { - username: welcome_data.get_canonical_username().to_owned(), - auth_type: welcome_data.get_reusable_auth_credentials_type(), - auth_data: welcome_data.get_reusable_auth_credentials().to_owned(), - }; + let reusable_credentials = Credentials { + username: welcome_data.get_canonical_username().to_owned(), + auth_type: welcome_data.get_reusable_auth_credentials_type(), + auth_data: welcome_data.get_reusable_auth_credentials().to_owned(), + }; - Ok((transport, reusable_credentials)) - } + Ok(reusable_credentials) + } - Some((0xad, data)) => { - let error_data: APLoginFailed = - protobuf::parse_from_bytes(data.as_ref()).unwrap(); - panic!( - "Authentication failed with reason: {:?}", - error_data.get_error_code() - ) - } + 0xad => { + let error_data: APLoginFailed = protobuf::parse_from_bytes(data.as_ref()).unwrap(); + panic!( + "Authentication failed with reason: {:?}", + error_data.get_error_code() + ) + } - Some((cmd, _)) => panic!("Unexpected packet {:?}", cmd), - None => panic!("EOF"), - }), - ) + _ => panic!("Unexpected packet {:?}", cmd), + } } diff --git a/core/src/diffie_hellman.rs b/core/src/diffie_hellman.rs index dec34a3..358901b 100644 --- a/core/src/diffie_hellman.rs +++ b/core/src/diffie_hellman.rs @@ -1,12 +1,12 @@ use num_bigint::BigUint; -use num_traits::FromPrimitive; +use once_cell::sync::Lazy; use rand::Rng; use crate::util; -lazy_static! { - pub static ref DH_GENERATOR: BigUint = BigUint::from_u64(0x2).unwrap(); - pub static ref DH_PRIME: BigUint = BigUint::from_bytes_be(&[ +pub static DH_GENERATOR: Lazy = Lazy::new(|| BigUint::from_bytes_be(&[0x02])); +pub static DH_PRIME: Lazy = Lazy::new(|| { + BigUint::from_bytes_be(&[ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc9, 0x0f, 0xda, 0xa2, 0x21, 0x68, 0xc2, 0x34, 0xc4, 0xc6, 0x62, 0x8b, 0x80, 0xdc, 0x1c, 0xd1, 0x29, 0x02, 0x4e, 0x08, 0x8a, 0x67, 0xcc, 0x74, 0x02, 0x0b, 0xbe, 0xa6, 0x3b, 0x13, 0x9b, 0x22, 0x51, 0x4a, 0x08, 0x79, 0x8e, @@ -14,8 +14,8 @@ lazy_static! { 0xf2, 0x5f, 0x14, 0x37, 0x4f, 0xe1, 0x35, 0x6d, 0x6d, 0x51, 0xc2, 0x45, 0xe4, 0x85, 0xb5, 0x76, 0x62, 0x5e, 0x7e, 0xc6, 0xf4, 0x4c, 0x42, 0xe9, 0xa6, 0x3a, 0x36, 0x20, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - ]); -} + ]) +}); pub struct DHLocalKeys { private_key: BigUint, diff --git a/core/src/keymaster.rs b/core/src/keymaster.rs index f2d7b77..87b3f1e 100644 --- a/core/src/keymaster.rs +++ b/core/src/keymaster.rs @@ -1,8 +1,4 @@ -use futures::Future; -use serde_json; - -use crate::mercury::MercuryError; -use crate::session::Session; +use crate::{mercury::MercuryError, session::Session}; #[derive(Deserialize, Debug, Clone)] #[serde(rename_all = "camelCase")] @@ -13,20 +9,16 @@ pub struct Token { pub scope: Vec, } -pub fn get_token( +pub async fn get_token( session: &Session, client_id: &str, scopes: &str, -) -> Box> { +) -> Result { let url = format!( "hm://keymaster/token/authenticated?client_id={}&scope={}", client_id, scopes ); - Box::new(session.mercury().get(url).map(move |response| { - let data = response.payload.first().expect("Empty payload"); - let data = String::from_utf8(data.clone()).unwrap(); - let token: Token = serde_json::from_str(&data).unwrap(); - - token - })) + let response = session.mercury().get(url).await?; + let data = response.payload.first().expect("Empty payload"); + serde_json::from_slice(data.as_ref()).map_err(|_| MercuryError) } diff --git a/core/src/lib.rs b/core/src/lib.rs index c65878c..65f6f81 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -1,27 +1,23 @@ -#![cfg_attr(feature = "cargo-clippy", allow(unused_io_amount))] +#![allow(clippy::unused_io_amount)] -#[macro_use] -extern crate error_chain; -#[macro_use] -extern crate futures; -#[macro_use] -extern crate lazy_static; #[macro_use] extern crate log; #[macro_use] extern crate serde_derive; - +#[macro_use] +extern crate pin_project; extern crate aes; extern crate base64; extern crate byteorder; extern crate bytes; +extern crate futures; extern crate hmac; extern crate httparse; extern crate hyper; -extern crate hyper_proxy; extern crate num_bigint; extern crate num_integer; extern crate num_traits; +extern crate once_cell; extern crate pbkdf2; extern crate protobuf; extern crate rand; @@ -29,9 +25,8 @@ extern crate serde; extern crate serde_json; extern crate sha1; extern crate shannon; -extern crate tokio_codec; -extern crate tokio_core; -extern crate tokio_io; +extern crate tokio; +extern crate tokio_util; extern crate url; extern crate uuid; @@ -39,13 +34,14 @@ extern crate librespot_protocol as protocol; #[macro_use] mod component; -mod apresolve; + +pub mod apresolve; pub mod audio_key; pub mod authentication; pub mod cache; pub mod channel; pub mod config; -mod connection; +pub mod connection; pub mod diffie_hellman; pub mod keymaster; pub mod mercury; diff --git a/core/src/mercury/mod.rs b/core/src/mercury/mod.rs index 20e3f0d..e77b4a4 100644 --- a/core/src/mercury/mod.rs +++ b/core/src/mercury/mod.rs @@ -1,14 +1,14 @@ use crate::protocol; use crate::util::url_encode; +use crate::util::SeqGenerator; use byteorder::{BigEndian, ByteOrder}; use bytes::Bytes; -use futures::sync::{mpsc, oneshot}; -use futures::{Async, Future, Poll}; -use protobuf; -use std::collections::HashMap; -use std::mem; - -use crate::util::SeqGenerator; +use futures::{ + channel::{mpsc, oneshot}, + Future, +}; +use std::{collections::HashMap, task::Poll}; +use std::{mem, pin::Pin, task::Context}; mod types; pub use self::types::*; @@ -31,17 +31,17 @@ pub struct MercuryPending { callback: Option>>, } -pub struct MercuryFuture(oneshot::Receiver>); -impl Future for MercuryFuture { - type Item = T; - type Error = MercuryError; +#[pin_project] +pub struct MercuryFuture(#[pin] oneshot::Receiver>); - fn poll(&mut self) -> Poll { - match self.0.poll() { - Ok(Async::Ready(Ok(value))) => Ok(Async::Ready(value)), - Ok(Async::Ready(Err(err))) => Err(err), - Ok(Async::NotReady) => Ok(Async::NotReady), - Err(oneshot::Canceled) => Err(MercuryError), +impl Future for MercuryFuture { + type Output = Result; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + match self.project().0.poll(cx) { + Poll::Ready(Ok(x)) => Poll::Ready(x), + Poll::Ready(Err(_)) => Poll::Ready(Err(MercuryError)), + Poll::Pending => Poll::Pending, } } } @@ -98,46 +98,46 @@ impl MercuryManager { MercurySender::new(self.clone(), uri.into()) } - pub fn subscribe>( + pub async fn subscribe>( &self, uri: T, - ) -> Box, Error = MercuryError>> - { + ) -> Result, MercuryError> { let uri = uri.into(); - let request = self.request(MercuryRequest { - method: MercuryMethod::SUB, - uri: uri.clone(), - content_type: None, - payload: Vec::new(), - }); + let response = self + .request(MercuryRequest { + method: MercuryMethod::SUB, + uri: uri.clone(), + content_type: None, + payload: Vec::new(), + }) + .await?; + + let (tx, rx) = mpsc::unbounded(); let manager = self.clone(); - Box::new(request.map(move |response| { - let (tx, rx) = mpsc::unbounded(); - manager.lock(move |inner| { - if !inner.invalid { - debug!("subscribed uri={} count={}", uri, response.payload.len()); - if response.payload.len() > 0 { - // Old subscription protocol, watch the provided list of URIs - for sub in response.payload { - let mut sub: protocol::pubsub::Subscription = - protobuf::parse_from_bytes(&sub).unwrap(); - let sub_uri = sub.take_uri(); + manager.lock(move |inner| { + if !inner.invalid { + debug!("subscribed uri={} count={}", uri, response.payload.len()); + if !response.payload.is_empty() { + // Old subscription protocol, watch the provided list of URIs + for sub in response.payload { + let mut sub: protocol::pubsub::Subscription = + protobuf::parse_from_bytes(&sub).unwrap(); + let sub_uri = sub.take_uri(); - debug!("subscribed sub_uri={}", sub_uri); + debug!("subscribed sub_uri={}", sub_uri); - inner.subscriptions.push((sub_uri, tx.clone())); - } - } else { - // New subscription protocol, watch the requested URI - inner.subscriptions.push((uri, tx)); + inner.subscriptions.push((sub_uri, tx.clone())); } + } else { + // New subscription protocol, watch the requested URI + inner.subscriptions.push((uri, tx)); } - }); + } + }); - rx - })) + Ok(rx) } pub(crate) fn dispatch(&self, cmd: u8, mut data: Bytes) { @@ -193,7 +193,7 @@ impl MercuryManager { let header: protocol::mercury::Header = protobuf::parse_from_bytes(&header_data).unwrap(); let response = MercuryResponse { - uri: url_encode(header.get_uri()).to_owned(), + uri: url_encode(header.get_uri()), status_code: header.get_status_code(), payload: pending.parts, }; diff --git a/core/src/mercury/sender.rs b/core/src/mercury/sender.rs index f00235e..860c2f3 100644 --- a/core/src/mercury/sender.rs +++ b/core/src/mercury/sender.rs @@ -1,5 +1,5 @@ -use futures::{Async, AsyncSink, Future, Poll, Sink, StartSend}; -use std::collections::VecDeque; +use futures::Sink; +use std::{collections::VecDeque, pin::Pin, task::Context}; use super::*; @@ -30,27 +30,38 @@ impl Clone for MercurySender { } } -impl Sink for MercurySender { - type SinkItem = Vec; - type SinkError = MercuryError; +impl Sink> for MercurySender { + type Error = MercuryError; - fn start_send(&mut self, item: Self::SinkItem) -> StartSend { - let task = self.mercury.send(self.uri.clone(), item); - self.pending.push_back(task); - Ok(AsyncSink::Ready) + fn poll_ready(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) } - fn poll_complete(&mut self) -> Poll<(), Self::SinkError> { + fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.poll_flush(cx) + } + + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { loop { match self.pending.front_mut() { Some(task) => { - try_ready!(task.poll()); + match Pin::new(task).poll(cx) { + Poll::Ready(Err(x)) => return Poll::Ready(Err(x)), + Poll::Pending => return Poll::Pending, + _ => (), + }; } None => { - return Ok(Async::Ready(())); + return Poll::Ready(Ok(())); } } self.pending.pop_front(); } } + + fn start_send(mut self: Pin<&mut Self>, item: Vec) -> Result<(), Self::Error> { + let task = self.mercury.send(self.uri.clone(), item); + self.pending.push_back(task); + Ok(()) + } } diff --git a/core/src/proxytunnel.rs b/core/src/proxytunnel.rs index b136384..508de7f 100644 --- a/core/src/proxytunnel.rs +++ b/core/src/proxytunnel.rs @@ -1,110 +1,45 @@ use std::io; -use std::str::FromStr; -use futures::{Async, Future, Poll}; -use httparse; use hyper::Uri; -use tokio_io::io::{read, write_all, Read, Window, WriteAll}; -use tokio_io::{AsyncRead, AsyncWrite}; +use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; -pub struct ProxyTunnel { - state: ProxyState, -} - -enum ProxyState { - ProxyConnect(WriteAll>), - ProxyResponse(Read>>), -} - -pub fn connect(connection: T, connect_url: &str) -> ProxyTunnel { - let proxy = proxy_connect(connection, connect_url); - ProxyTunnel { - state: ProxyState::ProxyConnect(proxy), - } -} - -impl Future for ProxyTunnel { - type Item = T; - type Error = io::Error; - - fn poll(&mut self) -> Poll { - use self::ProxyState::*; - loop { - self.state = match self.state { - ProxyConnect(ref mut write) => { - let (connection, mut accumulator) = try_ready!(write.poll()); - - let capacity = accumulator.capacity(); - accumulator.resize(capacity, 0); - let window = Window::new(accumulator); - - let read = read(connection, window); - ProxyResponse(read) - } - - ProxyResponse(ref mut read_f) => { - let (connection, mut window, bytes_read) = try_ready!(read_f.poll()); - - if bytes_read == 0 { - return Err(io::Error::new(io::ErrorKind::Other, "Early EOF from proxy")); - } - - let data_end = window.start() + bytes_read; - - let buf = window.get_ref()[0..data_end].to_vec(); - let mut headers = [httparse::EMPTY_HEADER; 16]; - let mut response = httparse::Response::new(&mut headers); - let status = match response.parse(&buf) { - Ok(status) => status, - Err(err) => { - return Err(io::Error::new(io::ErrorKind::Other, err.to_string())); - } - }; - - if status.is_complete() { - if let Some(code) = response.code { - if code == 200 { - // Proxy says all is well - return Ok(Async::Ready(connection)); - } else { - let reason = response.reason.unwrap_or("no reason"); - let msg = format!("Proxy responded with {}: {}", code, reason); - - return Err(io::Error::new(io::ErrorKind::Other, msg)); - } - } else { - return Err(io::Error::new( - io::ErrorKind::Other, - "Malformed response from proxy", - )); - } - } else { - if data_end >= window.end() { - // Allocate some more buffer space - let newsize = data_end + 100; - window.get_mut().resize(newsize, 0); - window.set_end(newsize); - } - // We did not get a full header - window.set_start(data_end); - let read = read(connection, window); - ProxyResponse(read) - } - } - } - } - } -} - -fn proxy_connect(connection: T, connect_url: &str) -> WriteAll> { - let uri = Uri::from_str(connect_url).unwrap(); - let buffer = format!( +pub async fn connect( + mut connection: T, + connect_url: &str, +) -> io::Result { + let uri = connect_url.parse::().unwrap(); + let mut buffer = format!( "CONNECT {0}:{1} HTTP/1.1\r\n\ \r\n", - uri.host().expect(&format!("No host in {}", uri)), - uri.port().expect(&format!("No port in {}", uri)) + uri.host().unwrap_or_else(|| panic!("No host in {}", uri)), + uri.port().unwrap_or_else(|| panic!("No port in {}", uri)) ) .into_bytes(); + connection.write_all(buffer.as_ref()).await?; - write_all(connection, buffer) + buffer.clear(); + connection.read_to_end(&mut buffer).await?; + if buffer.is_empty() { + return Err(io::Error::new(io::ErrorKind::Other, "Early EOF from proxy")); + } + + let mut headers = [httparse::EMPTY_HEADER; 16]; + let mut response = httparse::Response::new(&mut headers); + + response + .parse(&buffer[..]) + .map_err(|err| io::Error::new(io::ErrorKind::Other, err.to_string()))?; + + match response.code { + Some(200) => Ok(connection), // Proxy says all is well + Some(code) => { + let reason = response.reason.unwrap_or("no reason"); + let msg = format!("Proxy responded with {}: {}", code, reason); + Err(io::Error::new(io::ErrorKind::Other, msg)) + } + None => Err(io::Error::new( + io::ErrorKind::Other, + "Malformed response from proxy", + )), + } } diff --git a/core/src/session.rs b/core/src/session.rs index 4d86a02..2def408 100644 --- a/core/src/session.rs +++ b/core/src/session.rs @@ -1,20 +1,20 @@ -use std::io; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, RwLock, Weak}; +use std::task::Poll; use std::time::{SystemTime, UNIX_EPOCH}; +use std::{io, pin::Pin, task::Context}; + +use once_cell::sync::OnceCell; use byteorder::{BigEndian, ByteOrder}; use bytes::Bytes; -use futures::sync::mpsc; -use futures::{Async, Future, IntoFuture, Poll, Stream}; -use tokio_core::reactor::{Handle, Remote}; +use futures::{channel::mpsc, Future, FutureExt, StreamExt, TryStream, TryStreamExt}; use crate::apresolve::apresolve_or_fallback; use crate::audio_key::AudioKeyManager; use crate::authentication::Credentials; use crate::cache::Cache; use crate::channel::ChannelManager; -use crate::component::Lazy; use crate::config::SessionConfig; use crate::connection; use crate::mercury::MercuryManager; @@ -32,13 +32,11 @@ struct SessionInternal { tx_connection: mpsc::UnboundedSender<(u8, Vec)>, - audio_key: Lazy, - channel: Lazy, - mercury: Lazy, + audio_key: OnceCell, + channel: OnceCell, + mercury: OnceCell, cache: Option>, - handle: Remote, - session_id: usize, } @@ -48,58 +46,34 @@ static SESSION_COUNTER: AtomicUsize = AtomicUsize::new(0); pub struct Session(Arc); impl Session { - pub fn connect( + pub async fn connect( config: SessionConfig, credentials: Credentials, cache: Option, - handle: Handle, - ) -> Box> { - let access_point = - apresolve_or_fallback::(&handle, &config.proxy, &config.ap_port); + ) -> io::Result { + let ap = apresolve_or_fallback(&config.proxy, &config.ap_port).await; - let handle_ = handle.clone(); - let proxy = config.proxy.clone(); - let connection = access_point.and_then(move |addr| { - info!("Connecting to AP \"{}\"", addr); - connection::connect(addr, &handle_, &proxy) - }); + info!("Connecting to AP \"{}\"", ap); + let mut conn = connection::connect(ap, &config.proxy).await?; - let device_id = config.device_id.clone(); - let authentication = connection.and_then(move |connection| { - connection::authenticate(connection, credentials, device_id) - }); + let reusable_credentials = + connection::authenticate(&mut conn, credentials, &config.device_id).await?; + info!("Authenticated as \"{}\" !", reusable_credentials.username); + if let Some(cache) = &cache { + cache.save_credentials(&reusable_credentials); + } - let result = authentication.map(move |(transport, reusable_credentials)| { - info!("Authenticated as \"{}\" !", reusable_credentials.username); - if let Some(ref cache) = cache { - cache.save_credentials(&reusable_credentials); - } + let session = Session::create(conn, config, cache, reusable_credentials.username); - let (session, task) = Session::create( - &handle, - transport, - config, - cache, - reusable_credentials.username.clone(), - ); - - handle.spawn(task.map_err(|e| { - error!("{:?}", e); - })); - - session - }); - - Box::new(result) + Ok(session) } fn create( - handle: &Handle, transport: connection::Transport, config: SessionConfig, cache: Option, username: String, - ) -> (Session, Box>) { + ) -> Session { let (sink, stream) = transport.split(); let (sender_tx, sender_rx) = mpsc::unbounded(); @@ -120,53 +94,50 @@ impl Session { cache: cache.map(Arc::new), - audio_key: Lazy::new(), - channel: Lazy::new(), - mercury: Lazy::new(), - - handle: handle.remote().clone(), + audio_key: OnceCell::new(), + channel: OnceCell::new(), + mercury: OnceCell::new(), session_id: session_id, })); - let sender_task = sender_rx - .map_err(|e| -> io::Error { panic!(e) }) - .forward(sink) - .map(|_| ()); + let sender_task = sender_rx.map(Ok::<_, io::Error>).forward(sink); let receiver_task = DispatchTask(stream, session.weak()); - let task = Box::new( - (receiver_task, sender_task) - .into_future() - .map(|((), ())| ()), - ); - - (session, task) + let task = + futures::future::join(sender_task, receiver_task).map(|_| io::Result::<_>::Ok(())); + tokio::spawn(task); + session } pub fn audio_key(&self) -> &AudioKeyManager { - self.0.audio_key.get(|| AudioKeyManager::new(self.weak())) + self.0 + .audio_key + .get_or_init(|| AudioKeyManager::new(self.weak())) } pub fn channel(&self) -> &ChannelManager { - self.0.channel.get(|| ChannelManager::new(self.weak())) + self.0 + .channel + .get_or_init(|| ChannelManager::new(self.weak())) } pub fn mercury(&self) -> &MercuryManager { - self.0.mercury.get(|| MercuryManager::new(self.weak())) + self.0 + .mercury + .get_or_init(|| MercuryManager::new(self.weak())) } pub fn time_delta(&self) -> i64 { self.0.data.read().unwrap().time_delta } - pub fn spawn(&self, f: F) + pub fn spawn(&self, task: T) where - F: FnOnce(&Handle) -> R + Send + 'static, - R: IntoFuture, - R::Future: 'static, + T: Future + Send + 'static, + T::Output: Send + 'static, { - self.0.handle.spawn(f) + tokio::spawn(task); } fn debug_info(&self) { @@ -178,7 +149,7 @@ impl Session { ); } - #[cfg_attr(feature = "cargo-clippy", allow(match_same_arms))] + #[allow(clippy::match_same_arms)] fn dispatch(&self, cmd: u8, data: Bytes) { match cmd { 0x4 => { @@ -273,35 +244,34 @@ impl Drop for SessionInternal { struct DispatchTask(S, SessionWeak) where - S: Stream; + S: TryStream + Unpin; impl Future for DispatchTask where - S: Stream, - ::Error: ::std::fmt::Debug, + S: TryStream + Unpin, + ::Ok: std::fmt::Debug, { - type Item = (); - type Error = S::Error; + type Output = Result<(), S::Error>; - fn poll(&mut self) -> Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let session = match self.1.try_upgrade() { Some(session) => session, - None => return Ok(Async::Ready(())), + None => return Poll::Ready(Ok(())), }; loop { - let (cmd, data) = match self.0.poll() { - Ok(Async::Ready(Some(t))) => t, - Ok(Async::Ready(None)) => { + let (cmd, data) = match self.0.try_poll_next_unpin(cx) { + Poll::Ready(Some(Ok(t))) => t, + Poll::Ready(None) => { warn!("Connection to server closed."); session.shutdown(); - return Ok(Async::Ready(())); + return Poll::Ready(Ok(())); } - Ok(Async::NotReady) => return Ok(Async::NotReady), - Err(e) => { + Poll::Ready(Some(Err(e))) => { session.shutdown(); - return Err(From::from(e)); + return Poll::Ready(Err(e)); } + Poll::Pending => return Poll::Pending, }; session.dispatch(cmd, data); @@ -311,7 +281,7 @@ where impl Drop for DispatchTask where - S: Stream, + S: TryStream + Unpin, { fn drop(&mut self) { debug!("drop Dispatch"); From 6867ad0750d6be9e95d3e11f38d09cc53a38c3d3 Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Thu, 21 Jan 2021 21:56:23 +0100 Subject: [PATCH 013/103] Added test --- core/tests/connect.rs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 core/tests/connect.rs diff --git a/core/tests/connect.rs b/core/tests/connect.rs new file mode 100644 index 0000000..44d418a --- /dev/null +++ b/core/tests/connect.rs @@ -0,0 +1,32 @@ +use librespot_core::*; + +#[cfg(test)] +mod tests { + use super::*; + // Test AP Resolve + use apresolve::apresolve_or_fallback; + #[tokio::test] + async fn test_ap_resolve() { + let ap = apresolve_or_fallback(&None, &None).await; + println!("AP: {:?}", ap); + } + + // Test connect + use authentication::Credentials; + use config::SessionConfig; + #[tokio::test] + async fn test_connection() -> Result<(), Box> { + println!("Running connection test"); + let ap = apresolve_or_fallback(&None, &None).await; + let credentials = Credentials::with_password(String::from("test"), String::from("test")); + let session_config = SessionConfig::default(); + let proxy = None; + + println!("Connecting to AP \"{}\"", ap); + let mut connection = connection::connect(ap, &proxy).await?; + let rc = connection::authenticate(&mut connection, credentials, &session_config.device_id) + .await?; + println!("Authenticated as \"{}\"", rc.username); + Ok(()) + } +} From 424ba3ae2596854514767c824ac2a24d5abb355d Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Thu, 21 Jan 2021 22:07:16 +0100 Subject: [PATCH 014/103] Migrated metadata crate to futures 0.3 --- metadata/Cargo.toml | 3 +- metadata/src/lib.rs | 97 ++++++++++++++++++++------------------------- 2 files changed, 45 insertions(+), 55 deletions(-) diff --git a/metadata/Cargo.toml b/metadata/Cargo.toml index 8f3b4c9..f5e6123 100644 --- a/metadata/Cargo.toml +++ b/metadata/Cargo.toml @@ -8,8 +8,9 @@ repository = "https://github.com/librespot-org/librespot" edition = "2018" [dependencies] +async-trait = "0.1" byteorder = "1.3" -futures = "0.1" +futures = "0.3" linear-map = "1.2" protobuf = "~2.14.0" log = "0.4" diff --git a/metadata/src/lib.rs b/metadata/src/lib.rs index 8bd422c..f71bae9 100644 --- a/metadata/src/lib.rs +++ b/metadata/src/lib.rs @@ -1,6 +1,11 @@ +#![allow(clippy::unused_io_amount)] +#![allow(clippy::redundant_field_names)] #[macro_use] extern crate log; +#[macro_use] +extern crate async_trait; + extern crate byteorder; extern crate futures; extern crate linear_map; @@ -11,8 +16,6 @@ extern crate librespot_protocol as protocol; pub mod cover; -use futures::future; -use futures::Future; use linear_map::LinearMap; use librespot_core::mercury::MercuryError; @@ -69,81 +72,67 @@ pub struct AudioItem { } impl AudioItem { - pub fn get_audio_item( - session: &Session, - id: SpotifyId, - ) -> Box> { + pub async fn get_audio_item(session: &Session, id: SpotifyId) -> Result { match id.audio_type { - SpotifyAudioType::Track => Track::get_audio_item(session, id), - SpotifyAudioType::Podcast => Episode::get_audio_item(session, id), - SpotifyAudioType::NonPlayable => { - Box::new(future::err::(MercuryError)) - } + SpotifyAudioType::Track => Track::get_audio_item(session, id).await, + SpotifyAudioType::Podcast => Episode::get_audio_item(session, id).await, + SpotifyAudioType::NonPlayable => Err(MercuryError), } } } +#[async_trait] trait AudioFiles { - fn get_audio_item( - session: &Session, - id: SpotifyId, - ) -> Box>; + async fn get_audio_item(session: &Session, id: SpotifyId) -> Result; } +#[async_trait] impl AudioFiles for Track { - fn get_audio_item( - session: &Session, - id: SpotifyId, - ) -> Box> { - Box::new(Self::get(session, id).and_then(move |item| { - Ok(AudioItem { - id: id, - uri: format!("spotify:track:{}", id.to_base62()), - files: item.files, - name: item.name, - duration: item.duration, - available: item.available, - alternatives: Some(item.alternatives), - }) - })) + async fn get_audio_item(session: &Session, id: SpotifyId) -> Result { + let item = Self::get(session, id).await?; + Ok(AudioItem { + id: id, + uri: format!("spotify:track:{}", id.to_base62()), + files: item.files, + name: item.name, + duration: item.duration, + available: item.available, + alternatives: Some(item.alternatives), + }) } } +#[async_trait] impl AudioFiles for Episode { - fn get_audio_item( - session: &Session, - id: SpotifyId, - ) -> Box> { - Box::new(Self::get(session, id).and_then(move |item| { - Ok(AudioItem { - id: id, - uri: format!("spotify:episode:{}", id.to_base62()), - files: item.files, - name: item.name, - duration: item.duration, - available: item.available, - alternatives: None, - }) - })) + async fn get_audio_item(session: &Session, id: SpotifyId) -> Result { + let item = Self::get(session, id).await?; + + Ok(AudioItem { + id: id, + uri: format!("spotify:episode:{}", id.to_base62()), + files: item.files, + name: item.name, + duration: item.duration, + available: item.available, + alternatives: None, + }) } } + +#[async_trait] pub trait Metadata: Send + Sized + 'static { type Message: protobuf::Message; fn request_url(id: SpotifyId) -> String; fn parse(msg: &Self::Message, session: &Session) -> Self; - fn get(session: &Session, id: SpotifyId) -> Box> { + async fn get(session: &Session, id: SpotifyId) -> Result { let uri = Self::request_url(id); - let request = session.mercury().get(uri); + let response = session.mercury().get(uri).await?; + let data = response.payload.first().expect("Empty payload"); + let msg: Self::Message = protobuf::parse_from_bytes(data).unwrap(); - let session = session.clone(); - Box::new(request.and_then(move |response| { - let data = response.payload.first().expect("Empty payload"); - let msg: Self::Message = protobuf::parse_from_bytes(data).unwrap(); - - Ok(Self::parse(&msg, &session)) - })) + Ok(Self::parse(&msg, &session)) } } From 80d384e00164b61f4ef9c52acee345300e091341 Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Thu, 21 Jan 2021 22:12:35 +0100 Subject: [PATCH 015/103] Migrated audio crate to futures 0.3 --- audio/Cargo.toml | 9 +- audio/src/decrypt.rs | 4 +- audio/src/fetch.rs | 765 +++++++++++++++++++++++-------------------- audio/src/lib.rs | 7 +- 4 files changed, 418 insertions(+), 367 deletions(-) diff --git a/audio/Cargo.toml b/audio/Cargo.toml index cde907c..f9d232b 100644 --- a/audio/Cargo.toml +++ b/audio/Cargo.toml @@ -11,16 +11,17 @@ path = "../core" version = "0.1.3" [dependencies] +aes-ctr = "0.6" bit-set = "0.5" -byteorder = "1.3" -bytes = "0.4" -futures = "0.1" +byteorder = "1.4" +bytes = "1.0" +futures = "0.3" lewton = "0.9" log = "0.4" num-bigint = "0.3" num-traits = "0.2" +pin-project = "1.0" tempfile = "3.1" -aes-ctr = "0.3" librespot-tremor = { version = "0.1.0", optional = true } vorbis = { version ="0.0.14", optional = true } diff --git a/audio/src/decrypt.rs b/audio/src/decrypt.rs index 818eb34..616ef4f 100644 --- a/audio/src/decrypt.rs +++ b/audio/src/decrypt.rs @@ -1,7 +1,7 @@ use std::io; -use aes_ctr::stream_cipher::generic_array::GenericArray; -use aes_ctr::stream_cipher::{NewStreamCipher, SyncStreamCipher, SyncStreamCipherSeek}; +use aes_ctr::cipher::generic_array::GenericArray; +use aes_ctr::cipher::{NewStreamCipher, SyncStreamCipher, SyncStreamCipherSeek}; use aes_ctr::Aes128Ctr; use librespot_core::audio_key::AudioKey; diff --git a/audio/src/fetch.rs b/audio/src/fetch.rs index c47cb4d..5d15866 100644 --- a/audio/src/fetch.rs +++ b/audio/src/fetch.rs @@ -1,17 +1,25 @@ use crate::range_set::{Range, RangeSet}; use byteorder::{BigEndian, ByteOrder, WriteBytesExt}; use bytes::Bytes; -use futures::sync::{mpsc, oneshot}; -use futures::Stream; -use futures::{Async, Future, Poll}; -use std::cmp::{max, min}; +use futures::{ + channel::{mpsc, oneshot}, + future, +}; +use futures::{Future, Stream, StreamExt, TryFutureExt, TryStreamExt}; + use std::fs; use std::io::{self, Read, Seek, SeekFrom, Write}; use std::sync::{Arc, Condvar, Mutex}; +use std::task::Poll; use std::time::{Duration, Instant}; +use std::{ + cmp::{max, min}, + pin::Pin, + task::Context, +}; use tempfile::NamedTempFile; -use futures::sync::mpsc::unbounded; +use futures::channel::mpsc::unbounded; use librespot_core::channel::{Channel, ChannelData, ChannelError, ChannelHeaders}; use librespot_core::session::Session; use librespot_core::spotify_id::FileId; @@ -88,22 +96,6 @@ pub enum AudioFile { Streaming(AudioFileStreaming), } -pub enum AudioFileOpen { - Cached(Option), - Streaming(AudioFileOpenStreaming), -} - -pub struct AudioFileOpenStreaming { - session: Session, - initial_data_rx: Option, - initial_data_length: Option, - initial_request_sent_time: Instant, - headers: ChannelHeaders, - file_id: FileId, - complete_tx: Option>, - streaming_data_rate: usize, -} - enum StreamLoaderCommand { Fetch(Range), // signal the stream loader to fetch a range of the file RandomAccessMode(), // optimise download strategy for random access @@ -120,45 +112,36 @@ pub struct StreamLoaderController { impl StreamLoaderController { pub fn len(&self) -> usize { - return self.file_size; + self.file_size + } + + pub fn is_empty(&self) -> bool { + self.file_size == 0 } pub fn range_available(&self, range: Range) -> bool { if let Some(ref shared) = self.stream_shared { let download_status = shared.download_status.lock().unwrap(); - if range.length + range.length <= download_status .downloaded .contained_length_from_value(range.start) - { - return true; - } else { - return false; - } } else { - if range.length <= self.len() - range.start { - return true; - } else { - return false; - } + range.length <= self.len() - range.start } } pub fn range_to_end_available(&self) -> bool { - if let Some(ref shared) = self.stream_shared { + self.stream_shared.as_ref().map_or(true, |shared| { let read_position = shared.read_position.load(atomic::Ordering::Relaxed); self.range_available(Range::new(read_position, self.len() - read_position)) - } else { - true - } + }) } pub fn ping_time_ms(&self) -> usize { - if let Some(ref shared) = self.stream_shared { - return shared.ping_time_ms.load(atomic::Ordering::Relaxed); - } else { - return 0; - } + self.stream_shared.as_ref().map_or(0, |shared| { + shared.ping_time_ms.load(atomic::Ordering::Relaxed) + }) } fn send_stream_loader_command(&mut self, command: StreamLoaderCommand) { @@ -216,27 +199,23 @@ impl StreamLoaderController { } pub fn fetch_next(&mut self, length: usize) { - let range: Range = if let Some(ref shared) = self.stream_shared { - Range { + if let Some(ref shared) = self.stream_shared { + let range = Range { start: shared.read_position.load(atomic::Ordering::Relaxed), length: length, - } - } else { - return; - }; - self.fetch(range); + }; + self.fetch(range) + } } pub fn fetch_next_blocking(&mut self, length: usize) { - let range: Range = if let Some(ref shared) = self.stream_shared { - Range { + if let Some(ref shared) = self.stream_shared { + let range = Range { start: shared.read_position.load(atomic::Ordering::Relaxed), length: length, - } - } else { - return; - }; - self.fetch_blocking(range); + }; + self.fetch_blocking(range); + } } pub fn set_random_access_mode(&mut self) { @@ -288,108 +267,16 @@ struct AudioFileShared { read_position: AtomicUsize, } -impl AudioFileOpenStreaming { - fn finish(&mut self, size: usize) -> AudioFileStreaming { - let shared = Arc::new(AudioFileShared { - file_id: self.file_id, - file_size: size, - stream_data_rate: self.streaming_data_rate, - cond: Condvar::new(), - download_status: Mutex::new(AudioFileDownloadStatus { - requested: RangeSet::new(), - downloaded: RangeSet::new(), - }), - download_strategy: Mutex::new(DownloadStrategy::RandomAccess()), // start with random access mode until someone tells us otherwise - number_of_open_requests: AtomicUsize::new(0), - ping_time_ms: AtomicUsize::new(0), - read_position: AtomicUsize::new(0), - }); - - let mut write_file = NamedTempFile::new().unwrap(); - write_file.as_file().set_len(size as u64).unwrap(); - write_file.seek(SeekFrom::Start(0)).unwrap(); - - let read_file = write_file.reopen().unwrap(); - - let initial_data_rx = self.initial_data_rx.take().unwrap(); - let initial_data_length = self.initial_data_length.take().unwrap(); - let complete_tx = self.complete_tx.take().unwrap(); - //let (seek_tx, seek_rx) = mpsc::unbounded(); - let (stream_loader_command_tx, stream_loader_command_rx) = - mpsc::unbounded::(); - - let fetcher = AudioFileFetch::new( - self.session.clone(), - shared.clone(), - initial_data_rx, - self.initial_request_sent_time, - initial_data_length, - write_file, - stream_loader_command_rx, - complete_tx, - ); - self.session.spawn(move |_| fetcher); - - AudioFileStreaming { - read_file: read_file, - - position: 0, - //seek: seek_tx, - stream_loader_command_tx: stream_loader_command_tx, - - shared: shared, - } - } -} - -impl Future for AudioFileOpen { - type Item = AudioFile; - type Error = ChannelError; - - fn poll(&mut self) -> Poll { - match *self { - AudioFileOpen::Streaming(ref mut open) => { - let file = try_ready!(open.poll()); - Ok(Async::Ready(AudioFile::Streaming(file))) - } - AudioFileOpen::Cached(ref mut file) => { - let file = file.take().unwrap(); - Ok(Async::Ready(AudioFile::Cached(file))) - } - } - } -} - -impl Future for AudioFileOpenStreaming { - type Item = AudioFileStreaming; - type Error = ChannelError; - - fn poll(&mut self) -> Poll { - loop { - let (id, data) = try_ready!(self.headers.poll()).unwrap(); - - if id == 0x3 { - let size = BigEndian::read_u32(&data) as usize * 4; - let file = self.finish(size); - - return Ok(Async::Ready(file)); - } - } - } -} - impl AudioFile { - pub fn open( + pub async fn open( session: &Session, file_id: FileId, bytes_per_second: usize, play_from_beginning: bool, - ) -> AudioFileOpen { - let cache = session.cache().cloned(); - - if let Some(file) = cache.as_ref().and_then(|cache| cache.file(file_id)) { + ) -> Result { + if let Some(file) = session.cache().and_then(|cache| cache.file(file_id)) { debug!("File {} already in cache", file_id); - return AudioFileOpen::Cached(Some(file)); + return Ok(AudioFile::Cached(file)); } debug!("Downloading file {}", file_id); @@ -411,56 +298,112 @@ impl AudioFile { } let (headers, data) = request_range(session, file_id, 0, initial_data_length).split(); - let open = AudioFileOpenStreaming { - session: session.clone(), - file_id: file_id, - - headers: headers, - initial_data_rx: Some(data), - initial_data_length: Some(initial_data_length), - initial_request_sent_time: Instant::now(), - - complete_tx: Some(complete_tx), - streaming_data_rate: bytes_per_second, - }; + let streaming = AudioFileStreaming::open( + session.clone(), + data, + initial_data_length, + Instant::now(), + headers, + file_id, + complete_tx, + bytes_per_second, + ); let session_ = session.clone(); - session.spawn(move |_| { - complete_rx - .map(move |mut file| { - if let Some(cache) = session_.cache() { - cache.save_file(file_id, &mut file); - debug!("File {} complete, saving to cache", file_id); - } else { - debug!("File {} complete", file_id); - } - }) - .or_else(|oneshot::Canceled| Ok(())) - }); + session.spawn(complete_rx.map_ok(move |mut file| { + if let Some(cache) = session_.cache() { + cache.save_file(file_id, &mut file); + debug!("File {} complete, saving to cache", file_id); + } else { + debug!("File {} complete", file_id); + } + })); - return AudioFileOpen::Streaming(open); + Ok(AudioFile::Streaming(streaming.await?)) } pub fn get_stream_loader_controller(&self) -> StreamLoaderController { match self { - AudioFile::Streaming(ref stream) => { - return StreamLoaderController { - channel_tx: Some(stream.stream_loader_command_tx.clone()), - stream_shared: Some(stream.shared.clone()), - file_size: stream.shared.file_size, - }; - } - AudioFile::Cached(ref file) => { - return StreamLoaderController { - channel_tx: None, - stream_shared: None, - file_size: file.metadata().unwrap().len() as usize, - }; - } + AudioFile::Streaming(ref stream) => StreamLoaderController { + channel_tx: Some(stream.stream_loader_command_tx.clone()), + stream_shared: Some(stream.shared.clone()), + file_size: stream.shared.file_size, + }, + AudioFile::Cached(ref file) => StreamLoaderController { + channel_tx: None, + stream_shared: None, + file_size: file.metadata().unwrap().len() as usize, + }, } } } +impl AudioFileStreaming { + pub async fn open( + session: Session, + initial_data_rx: ChannelData, + initial_data_length: usize, + initial_request_sent_time: Instant, + headers: ChannelHeaders, + file_id: FileId, + complete_tx: oneshot::Sender, + streaming_data_rate: usize, + ) -> Result { + let (_, data) = headers + .try_filter(|(id, _)| future::ready(*id == 0x3)) + .next() + .await + .unwrap()?; + + let size = BigEndian::read_u32(&data) as usize * 4; + + let shared = Arc::new(AudioFileShared { + file_id: file_id, + file_size: size, + stream_data_rate: streaming_data_rate, + cond: Condvar::new(), + download_status: Mutex::new(AudioFileDownloadStatus { + requested: RangeSet::new(), + downloaded: RangeSet::new(), + }), + download_strategy: Mutex::new(DownloadStrategy::RandomAccess()), // start with random access mode until someone tells us otherwise + number_of_open_requests: AtomicUsize::new(0), + ping_time_ms: AtomicUsize::new(0), + read_position: AtomicUsize::new(0), + }); + + let mut write_file = NamedTempFile::new().unwrap(); + write_file.as_file().set_len(size as u64).unwrap(); + write_file.seek(SeekFrom::Start(0)).unwrap(); + + let read_file = write_file.reopen().unwrap(); + + //let (seek_tx, seek_rx) = mpsc::unbounded(); + let (stream_loader_command_tx, stream_loader_command_rx) = + mpsc::unbounded::(); + + let fetcher = AudioFileFetch::new( + session.clone(), + shared.clone(), + initial_data_rx, + initial_request_sent_time, + initial_data_length, + write_file, + stream_loader_command_rx, + complete_tx, + ); + + session.spawn(fetcher); + Ok(AudioFileStreaming { + read_file: read_file, + position: 0, + //seek: seek_tx, + stream_loader_command_tx: stream_loader_command_tx, + shared: shared, + }) + } +} + fn request_range(session: &Session, file: FileId, offset: usize, length: usize) -> Channel { assert!( offset % 4 == 0, @@ -502,141 +445,261 @@ enum ReceivedData { Data(PartialFileData), } -struct AudioFileFetchDataReceiver { +async fn audio_file_fetch_receive_data( shared: Arc, file_data_tx: mpsc::UnboundedSender, data_rx: ChannelData, initial_data_offset: usize, initial_request_length: usize, - data_offset: usize, - request_length: usize, - request_sent_time: Option, - measure_ping_time: bool, -} + request_sent_time: Instant, +) { + let mut data_offset = initial_data_offset; + let mut request_length = initial_request_length; + let mut measure_ping_time = shared + .number_of_open_requests + .load(atomic::Ordering::SeqCst) + == 0; -impl AudioFileFetchDataReceiver { - fn new( - shared: Arc, - file_data_tx: mpsc::UnboundedSender, - data_rx: ChannelData, - data_offset: usize, - request_length: usize, - request_sent_time: Instant, - ) -> AudioFileFetchDataReceiver { - let measure_ping_time = shared - .number_of_open_requests - .load(atomic::Ordering::SeqCst) - == 0; + shared + .number_of_open_requests + .fetch_add(1, atomic::Ordering::SeqCst); - shared - .number_of_open_requests - .fetch_add(1, atomic::Ordering::SeqCst); + enum TryFoldErr { + ChannelError, + FinishEarly, + } - AudioFileFetchDataReceiver { - shared: shared, - data_rx: data_rx, - file_data_tx: file_data_tx, - initial_data_offset: data_offset, - initial_request_length: request_length, - data_offset: data_offset, - request_length: request_length, - request_sent_time: Some(request_sent_time), - measure_ping_time: measure_ping_time, - } + let result = data_rx + .map_err(|_| TryFoldErr::ChannelError) + .try_for_each(|data| { + if measure_ping_time { + let duration = Instant::now() - request_sent_time; + let duration_ms: u64; + if 0.001 * (duration.as_millis() as f64) + > MAXIMUM_ASSUMED_PING_TIME_SECONDS + { + duration_ms = (MAXIMUM_ASSUMED_PING_TIME_SECONDS * 1000.0) as u64; + } else { + duration_ms = duration.as_millis() as u64; + } + let _ = file_data_tx + .unbounded_send(ReceivedData::ResponseTimeMs(duration_ms as usize)); + measure_ping_time = false; + } + let data_size = data.len(); + let _ = file_data_tx + .unbounded_send(ReceivedData::Data(PartialFileData { + offset: data_offset, + data: data, + })); + data_offset += data_size; + if request_length < data_size { + warn!("Data receiver for range {} (+{}) received more data from server than requested.", initial_data_offset, initial_request_length); + request_length = 0; + } else { + request_length -= data_size; + } + + future::ready(if request_length == 0 { + Err(TryFoldErr::FinishEarly) + } else { + Ok(()) + }) + }) + .await; + + if request_length > 0 { + let missing_range = Range::new(data_offset, request_length); + + let mut download_status = shared.download_status.lock().unwrap(); + download_status.requested.subtract_range(&missing_range); + shared.cond.notify_all(); + } + + shared + .number_of_open_requests + .fetch_sub(1, atomic::Ordering::SeqCst); + + if let Err(TryFoldErr::ChannelError) = result { + warn!( + "Error from channel for data receiver for range {} (+{}).", + initial_data_offset, initial_request_length + ); + } else if request_length > 0 { + warn!( + "Data receiver for range {} (+{}) received less data from server than requested.", + initial_data_offset, initial_request_length + ); } } +/* +async fn audio_file_fetch( + session: Session, + shared: Arc, + initial_data_rx: ChannelData, + initial_request_sent_time: Instant, + initial_data_length: usize, -impl AudioFileFetchDataReceiver { - fn finish(&mut self) { - if self.request_length > 0 { - let missing_range = Range::new(self.data_offset, self.request_length); + output: NamedTempFile, + stream_loader_command_rx: mpsc::UnboundedReceiver, + complete_tx: oneshot::Sender, +) { + let (file_data_tx, file_data_rx) = unbounded::(); - let mut download_status = self.shared.download_status.lock().unwrap(); - download_status.requested.subtract_range(&missing_range); - self.shared.cond.notify_all(); - } + let requested_range = Range::new(0, initial_data_length); + let mut download_status = shared.download_status.lock().unwrap(); + download_status.requested.add_range(&requested_range); - self.shared - .number_of_open_requests - .fetch_sub(1, atomic::Ordering::SeqCst); - } -} + session.spawn(audio_file_fetch_receive_data( + shared.clone(), + file_data_tx.clone(), + initial_data_rx, + 0, + initial_data_length, + initial_request_sent_time, + )); -impl Future for AudioFileFetchDataReceiver { - type Item = (); - type Error = (); + let mut network_response_times_ms: Vec::new(); - fn poll(&mut self) -> Poll<(), ()> { - loop { - match self.data_rx.poll() { - Ok(Async::Ready(Some(data))) => { - if self.measure_ping_time { - if let Some(request_sent_time) = self.request_sent_time { - let duration = Instant::now() - request_sent_time; - let duration_ms: u64; - if 0.001 * (duration.as_millis() as f64) - > MAXIMUM_ASSUMED_PING_TIME_SECONDS - { - duration_ms = (MAXIMUM_ASSUMED_PING_TIME_SECONDS * 1000.0) as u64; - } else { - duration_ms = duration.as_millis() as u64; - } - let _ = self - .file_data_tx - .unbounded_send(ReceivedData::ResponseTimeMs(duration_ms as usize)); - self.measure_ping_time = false; - } - } - let data_size = data.len(); - let _ = self - .file_data_tx - .unbounded_send(ReceivedData::Data(PartialFileData { - offset: self.data_offset, - data: data, - })); - self.data_offset += data_size; - if self.request_length < data_size { - warn!("Data receiver for range {} (+{}) received more data from server than requested.", self.initial_data_offset, self.initial_request_length); - self.request_length = 0; - } else { - self.request_length -= data_size; - } - if self.request_length == 0 { - self.finish(); - return Ok(Async::Ready(())); - } + let f1 = file_data_rx.map(|x| Ok::<_, ()>(x)).try_for_each(|x| { + match x { + ReceivedData::ResponseTimeMs(response_time_ms) => { + trace!("Ping time estimated as: {} ms.", response_time_ms); + + // record the response time + network_response_times_ms.push(response_time_ms); + + // prune old response times. Keep at most three. + while network_response_times_ms.len() > 3 { + network_response_times_ms.remove(0); } - Ok(Async::Ready(None)) => { - if self.request_length > 0 { - warn!("Data receiver for range {} (+{}) received less data from server than requested.", self.initial_data_offset, self.initial_request_length); + + // stats::median is experimental. So we calculate the median of up to three ourselves. + let ping_time_ms: usize = match network_response_times_ms.len() { + 1 => network_response_times_ms[0] as usize, + 2 => { + ((network_response_times_ms[0] + network_response_times_ms[1]) / 2) as usize } + 3 => { + let mut times = network_response_times_ms.clone(); + times.sort(); + times[1] + } + _ => unreachable!(), + }; + + // store our new estimate for everyone to see + shared + .ping_time_ms + .store(ping_time_ms, atomic::Ordering::Relaxed); + } + ReceivedData::Data(data) => { + output + .as_mut() + .unwrap() + .seek(SeekFrom::Start(data.offset as u64)) + .unwrap(); + output + .as_mut() + .unwrap() + .write_all(data.data.as_ref()) + .unwrap(); + + let mut full = false; + + { + let mut download_status = shared.download_status.lock().unwrap(); + + let received_range = Range::new(data.offset, data.data.len()); + download_status.downloaded.add_range(&received_range); + shared.cond.notify_all(); + + if download_status.downloaded.contained_length_from_value(0) + >= shared.file_size + { + full = true; + } + + drop(download_status); + } + + if full { self.finish(); - return Ok(Async::Ready(())); - } - Ok(Async::NotReady) => { - return Ok(Async::NotReady); - } - Err(ChannelError) => { - warn!( - "Error from channel for data receiver for range {} (+{}).", - self.initial_data_offset, self.initial_request_length - ); - self.finish(); - return Ok(Async::Ready(())); + return future::ready(Err(())); } } } - } -} + future::ready(Ok(())) + }); + let f2 = stream_loader_command_rx.map(Ok::<_, ()>).try_for_each(|x| { + match cmd { + StreamLoaderCommand::Fetch(request) => { + self.download_range(request.start, request.length); + } + StreamLoaderCommand::RandomAccessMode() => { + *(shared.download_strategy.lock().unwrap()) = DownloadStrategy::RandomAccess(); + } + StreamLoaderCommand::StreamMode() => { + *(shared.download_strategy.lock().unwrap()) = DownloadStrategy::Streaming(); + } + StreamLoaderCommand::Close() => return future::ready(Err(())), + } + Ok(()) + }); + + let f3 = future::poll_fn(|_| { + if let DownloadStrategy::Streaming() = self.get_download_strategy() { + let number_of_open_requests = shared + .number_of_open_requests + .load(atomic::Ordering::SeqCst); + let max_requests_to_send = + MAX_PREFETCH_REQUESTS - min(MAX_PREFETCH_REQUESTS, number_of_open_requests); + + if max_requests_to_send > 0 { + let bytes_pending: usize = { + let download_status = shared.download_status.lock().unwrap(); + download_status + .requested + .minus(&download_status.downloaded) + .len() + }; + + let ping_time_seconds = + 0.001 * shared.ping_time_ms.load(atomic::Ordering::Relaxed) as f64; + let download_rate = session.channel().get_download_rate_estimate(); + + let desired_pending_bytes = max( + (PREFETCH_THRESHOLD_FACTOR * ping_time_seconds * shared.stream_data_rate as f64) + as usize, + (FAST_PREFETCH_THRESHOLD_FACTOR * ping_time_seconds * download_rate as f64) + as usize, + ); + + if bytes_pending < desired_pending_bytes { + self.pre_fetch_more_data( + desired_pending_bytes - bytes_pending, + max_requests_to_send, + ); + } + } + } + Poll::Pending + }); + future::select_all(vec![f1, f2, f3]).await +}*/ + +#[pin_project] struct AudioFileFetch { session: Session, shared: Arc, output: Option, file_data_tx: mpsc::UnboundedSender, + #[pin] file_data_rx: mpsc::UnboundedReceiver, + #[pin] stream_loader_command_rx: mpsc::UnboundedReceiver, complete_tx: Option>, network_response_times_ms: Vec, @@ -662,16 +725,14 @@ impl AudioFileFetch { download_status.requested.add_range(&requested_range); } - let initial_data_receiver = AudioFileFetchDataReceiver::new( + session.spawn(audio_file_fetch_receive_data( shared.clone(), file_data_tx.clone(), initial_data_rx, 0, initial_data_length, initial_request_sent_time, - ); - - session.spawn(move |_| initial_data_receiver); + )); AudioFileFetch { session: session, @@ -701,7 +762,7 @@ impl AudioFileFetch { return; } - if length <= 0 { + if length == 0 { return; } @@ -737,16 +798,14 @@ impl AudioFileFetch { download_status.requested.add_range(range); - let receiver = AudioFileFetchDataReceiver::new( + self.session.spawn(audio_file_fetch_receive_data( self.shared.clone(), self.file_data_tx.clone(), data, range.start, range.length, Instant::now(), - ); - - self.session.spawn(move |_| receiver); + )); } } @@ -794,13 +853,13 @@ impl AudioFileFetch { } } - fn poll_file_data_rx(&mut self) -> Poll<(), ()> { + + + fn poll_file_data_rx(&mut self, cx: &mut Context<'_>) -> Poll<()> { loop { - match self.file_data_rx.poll() { - Ok(Async::Ready(None)) => { - return Ok(Async::Ready(())); - } - Ok(Async::Ready(Some(ReceivedData::ResponseTimeMs(response_time_ms)))) => { + match Pin::new(&mut self.file_data_rx).poll_next(cx) { + Poll::Ready(None) => return Poll::Ready(()), + Poll::Ready(Some(ReceivedData::ResponseTimeMs(response_time_ms))) => { trace!("Ping time estimated as: {} ms.", response_time_ms); // record the response time @@ -821,7 +880,7 @@ impl AudioFileFetch { } 3 => { let mut times = self.network_response_times_ms.clone(); - times.sort(); + times.sort_unstable(); times[1] } _ => unreachable!(), @@ -832,7 +891,7 @@ impl AudioFileFetch { .ping_time_ms .store(ping_time_ms, atomic::Ordering::Relaxed); } - Ok(Async::Ready(Some(ReceivedData::Data(data)))) => { + Poll::Ready(Some(ReceivedData::Data(data))) => { self.output .as_mut() .unwrap() @@ -864,39 +923,40 @@ impl AudioFileFetch { if full { self.finish(); - return Ok(Async::Ready(())); + return Poll::Ready(()) } } - Ok(Async::NotReady) => { - return Ok(Async::NotReady); + Poll::Pending => { + return Poll::Pending } - Err(()) => unreachable!(), } } } - fn poll_stream_loader_command_rx(&mut self) -> Poll<(), ()> { + fn poll_stream_loader_command_rx(&mut self, cx: &mut Context<'_>) -> Poll<()> { loop { - match self.stream_loader_command_rx.poll() { - Ok(Async::Ready(None)) => { - return Ok(Async::Ready(())); + match Pin::new(&mut self.stream_loader_command_rx).poll_next(cx) { + Poll::Ready(None) => + return Poll::Ready(()), + Poll::Ready(Some(cmd)) => { + match cmd { + StreamLoaderCommand::Fetch(request) => { + self.download_range(request.start, request.length); + } + StreamLoaderCommand::RandomAccessMode() => { + *(self.shared.download_strategy.lock().unwrap()) = + DownloadStrategy::RandomAccess(); + } + StreamLoaderCommand::StreamMode() => { + + *(self.shared.download_strategy.lock().unwrap()) = + DownloadStrategy::Streaming(); + } + StreamLoaderCommand::Close() => return Poll::Ready(()) + + } } - Ok(Async::Ready(Some(StreamLoaderCommand::Fetch(request)))) => { - self.download_range(request.start, request.length); - } - Ok(Async::Ready(Some(StreamLoaderCommand::RandomAccessMode()))) => { - *(self.shared.download_strategy.lock().unwrap()) = - DownloadStrategy::RandomAccess(); - } - Ok(Async::Ready(Some(StreamLoaderCommand::StreamMode()))) => { - *(self.shared.download_strategy.lock().unwrap()) = - DownloadStrategy::Streaming(); - } - Ok(Async::Ready(Some(StreamLoaderCommand::Close()))) => { - return Ok(Async::Ready(())); - } - Ok(Async::NotReady) => return Ok(Async::NotReady), - Err(()) => unreachable!(), + Poll::Pending => return Poll::Pending } } } @@ -909,26 +969,16 @@ impl AudioFileFetch { let _ = complete_tx.send(output); } } - impl Future for AudioFileFetch { - type Item = (); - type Error = (); + type Output = (); - fn poll(&mut self) -> Poll<(), ()> { - match self.poll_stream_loader_command_rx() { - Ok(Async::NotReady) => (), - Ok(Async::Ready(_)) => { - return Ok(Async::Ready(())); - } - Err(()) => unreachable!(), + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> { + if let Poll::Ready(()) = self.poll_stream_loader_command_rx(cx) { + return Poll::Ready(()) } - match self.poll_file_data_rx() { - Ok(Async::NotReady) => (), - Ok(Async::Ready(_)) => { - return Ok(Async::Ready(())); - } - Err(()) => unreachable!(), + if let Poll::Ready(()) = self.poll_file_data_rx(cx) { + return Poll::Ready(()) } if let DownloadStrategy::Streaming() = self.get_download_strategy() { @@ -968,8 +1018,7 @@ impl Future for AudioFileFetch { } } } - - return Ok(Async::NotReady); + Poll::Pending } } @@ -1009,9 +1058,9 @@ impl Read for AudioFileStreaming { ranges_to_request.subtract_range_set(&download_status.downloaded); ranges_to_request.subtract_range_set(&download_status.requested); - for range in ranges_to_request.iter() { + for &range in ranges_to_request.iter() { self.stream_loader_command_tx - .unbounded_send(StreamLoaderCommand::Fetch(range.clone())) + .unbounded_send(StreamLoaderCommand::Fetch(range)) .unwrap(); } @@ -1058,7 +1107,7 @@ impl Read for AudioFileStreaming { .read_position .store(self.position as usize, atomic::Ordering::Relaxed); - return Ok(read_len); + Ok(read_len) } } diff --git a/audio/src/lib.rs b/audio/src/lib.rs index 3e13c07..6955588 100644 --- a/audio/src/lib.rs +++ b/audio/src/lib.rs @@ -1,12 +1,13 @@ #[macro_use] -extern crate futures; -#[macro_use] extern crate log; +#[macro_use] +extern crate pin_project; extern crate aes_ctr; extern crate bit_set; extern crate byteorder; extern crate bytes; +extern crate futures; extern crate num_bigint; extern crate num_traits; extern crate tempfile; @@ -24,7 +25,7 @@ mod libvorbis_decoder; mod range_set; pub use decrypt::AudioDecrypt; -pub use fetch::{AudioFile, AudioFileOpen, StreamLoaderController}; +pub use fetch::{AudioFile, StreamLoaderController}; pub use fetch::{ READ_AHEAD_BEFORE_PLAYBACK_ROUNDTRIPS, READ_AHEAD_BEFORE_PLAYBACK_SECONDS, READ_AHEAD_DURING_PLAYBACK_ROUNDTRIPS, READ_AHEAD_DURING_PLAYBACK_SECONDS, From 90905b81bb681d3bd44832513caf58c2a8737019 Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Thu, 21 Jan 2021 22:13:09 +0100 Subject: [PATCH 016/103] Improved RangeSet implementation --- audio/src/range_set.rs | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/audio/src/range_set.rs b/audio/src/range_set.rs index 4495538..8712dfd 100644 --- a/audio/src/range_set.rs +++ b/audio/src/range_set.rs @@ -54,11 +54,7 @@ impl RangeSet { } pub fn len(&self) -> usize { - let mut result = 0; - for range in self.ranges.iter() { - result += range.length; - } - return result; + self.ranges.iter().map(|r| r.length).fold(0, std::ops::Add::add) } pub fn get_range(&self, index: usize) -> Range { @@ -98,12 +94,12 @@ impl RangeSet { return false; } } - return true; + true } pub fn add_range(&mut self, range: &Range) { - if range.length <= 0 { - // the interval is empty or invalid -> nothing to do. + if range.length == 0 { + // the interval is empty -> nothing to do. return; } @@ -111,7 +107,7 @@ impl RangeSet { // the new range is clear of any ranges we already iterated over. if range.end() < self.ranges[index].start { // the new range starts after anything we already passed and ends before the next range starts (they don't touch) -> insert it. - self.ranges.insert(index, range.clone()); + self.ranges.insert(index, *range); return; } else if range.start <= self.ranges[index].end() && self.ranges[index].start <= range.end() @@ -119,7 +115,7 @@ impl RangeSet { // the new range overlaps (or touches) the first range. They are to be merged. // In addition we might have to merge further ranges in as well. - let mut new_range = range.clone(); + let mut new_range = *range; while index < self.ranges.len() && self.ranges[index].start <= new_range.end() { let new_end = max(new_range.end(), self.ranges[index].end()); @@ -134,7 +130,7 @@ impl RangeSet { } // the new range is after everything else -> just add it - self.ranges.push(range.clone()); + self.ranges.push(*range); } #[allow(dead_code)] @@ -152,7 +148,7 @@ impl RangeSet { } pub fn subtract_range(&mut self, range: &Range) { - if range.length <= 0 { + if range.length == 0 { return; } From 0895f17f8a85bdcf1624138fac5c3960796240b7 Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Thu, 21 Jan 2021 22:22:32 +0100 Subject: [PATCH 017/103] Migrated playback crate to futures 0.3 --- playback/Cargo.toml | 4 +- playback/src/player.rs | 211 +++++++++++++++++++++-------------------- 2 files changed, 109 insertions(+), 106 deletions(-) diff --git a/playback/Cargo.toml b/playback/Cargo.toml index edd0951..1045185 100644 --- a/playback/Cargo.toml +++ b/playback/Cargo.toml @@ -18,9 +18,9 @@ path = "../metadata" version = "0.1.3" [dependencies] -futures = "0.1" +futures = "0.3" log = "0.4" -byteorder = "1.3" +byteorder = "1.4" shell-words = "1.0.0" alsa = { version = "0.2", optional = true } diff --git a/playback/src/player.rs b/playback/src/player.rs index 125184a..df442f0 100644 --- a/playback/src/player.rs +++ b/playback/src/player.rs @@ -1,20 +1,3 @@ -use byteorder::{LittleEndian, ReadBytesExt}; -use futures; -use futures::{future, Async, Future, Poll, Stream}; -use std; -use std::borrow::Cow; -use std::cmp::max; -use std::io::{Read, Result, Seek, SeekFrom}; -use std::mem; -use std::thread; -use std::time::{Duration, Instant}; - -use crate::config::{Bitrate, PlayerConfig}; -use librespot_core::session::Session; -use librespot_core::spotify_id::SpotifyId; - -use librespot_core::util::SeqGenerator; - use crate::audio::{AudioDecrypt, AudioFile, StreamLoaderController}; use crate::audio::{VorbisDecoder, VorbisPacket}; use crate::audio::{ @@ -22,13 +5,33 @@ use crate::audio::{ READ_AHEAD_DURING_PLAYBACK_ROUNDTRIPS, READ_AHEAD_DURING_PLAYBACK_SECONDS, }; use crate::audio_backend::Sink; +use crate::config::{Bitrate, PlayerConfig}; use crate::metadata::{AudioItem, FileFormat}; use crate::mixer::AudioFilter; +use librespot_core::session::Session; +use librespot_core::spotify_id::SpotifyId; +use librespot_core::util::SeqGenerator; + +use byteorder::{LittleEndian, ReadBytesExt}; +use futures::{ + channel::{mpsc, oneshot}, + future, Future, Stream, StreamExt, +}; +use std::io::{Read, Seek, SeekFrom}; +use std::mem; +use std::thread; +use std::time::{Duration, Instant}; +use std::{borrow::Cow, io}; +use std::{ + cmp::max, + pin::Pin, + task::{Context, Poll}, +}; const PRELOAD_NEXT_TRACK_BEFORE_END_DURATION_MS: u32 = 30000; pub struct Player { - commands: Option>, + commands: Option>, thread_handle: Option>, play_request_id_generator: SeqGenerator, } @@ -45,7 +48,7 @@ pub type SinkEventCallback = Box; struct PlayerInternal { session: Session, config: PlayerConfig, - commands: futures::sync::mpsc::UnboundedReceiver, + commands: mpsc::UnboundedReceiver, state: PlayerState, preload: PlayerPreload, @@ -53,7 +56,7 @@ struct PlayerInternal { sink_status: SinkStatus, sink_event_callback: Option, audio_filter: Option>, - event_senders: Vec>, + event_senders: Vec>, } enum PlayerCommand { @@ -70,7 +73,7 @@ enum PlayerCommand { Pause, Stop, Seek(u32), - AddEventSender(futures::sync::mpsc::UnboundedSender), + AddEventSender(mpsc::UnboundedSender), SetSinkEventCallback(Option), EmitVolumeSetEvent(u16), } @@ -182,7 +185,7 @@ impl PlayerEvent { } } -pub type PlayerEventChannel = futures::sync::mpsc::UnboundedReceiver; +pub type PlayerEventChannel = mpsc::UnboundedReceiver; #[derive(Clone, Copy, Debug)] struct NormalisationData { @@ -193,7 +196,7 @@ struct NormalisationData { } impl NormalisationData { - fn parse_from_file(mut file: T) -> Result { + fn parse_from_file(mut file: T) -> io::Result { const SPOTIFY_NORMALIZATION_HEADER_START_OFFSET: u64 = 144; file.seek(SeekFrom::Start(SPOTIFY_NORMALIZATION_HEADER_START_OFFSET)) .unwrap(); @@ -241,8 +244,8 @@ impl Player { where F: FnOnce() -> Box + Send + 'static, { - let (cmd_tx, cmd_rx) = futures::sync::mpsc::unbounded(); - let (event_sender, event_receiver) = futures::sync::mpsc::unbounded(); + let (cmd_tx, cmd_rx) = mpsc::unbounded(); + let (event_sender, event_receiver) = mpsc::unbounded(); let handle = thread::spawn(move || { debug!("new Player[{}]", session.session_id()); @@ -263,7 +266,7 @@ impl Player { // While PlayerInternal is written as a future, it still contains blocking code. // It must be run by using wait() in a dedicated thread. - let _ = internal.wait(); + todo!("How to block in futures 0.3?"); debug!("PlayerInternal thread finished."); }); @@ -314,22 +317,21 @@ impl Player { } pub fn get_player_event_channel(&self) -> PlayerEventChannel { - let (event_sender, event_receiver) = futures::sync::mpsc::unbounded(); + let (event_sender, event_receiver) = mpsc::unbounded(); self.command(PlayerCommand::AddEventSender(event_sender)); event_receiver } - pub fn get_end_of_track_future(&self) -> Box> { - let result = self - .get_player_event_channel() - .filter(|event| match event { - PlayerEvent::EndOfTrack { .. } | PlayerEvent::Stopped { .. } => true, - _ => false, + pub async fn get_end_of_track_future(&self) { + self.get_player_event_channel() + .filter(|event| { + future::ready(matches!( + event, + PlayerEvent::EndOfTrack { .. } | PlayerEvent::Stopped { .. } + )) }) - .into_future() - .map_err(|_| ()) - .map(|_| ()); - Box::new(result) + .for_each(|_| future::ready(())) + .await } pub fn set_sink_event_callback(&self, callback: Option) { @@ -367,7 +369,7 @@ enum PlayerPreload { None, Loading { track_id: SpotifyId, - loader: Box>, + loader: Pin>>>, }, Ready { track_id: SpotifyId, @@ -383,7 +385,7 @@ enum PlayerState { track_id: SpotifyId, play_request_id: u64, start_playback: bool, - loader: Box>, + loader: Pin>>>, }, Paused { track_id: SpotifyId, @@ -573,22 +575,23 @@ struct PlayerTrackLoader { } impl PlayerTrackLoader { - fn find_available_alternative<'a>(&self, audio: &'a AudioItem) -> Option> { + async fn find_available_alternative<'a>( + &self, + audio: &'a AudioItem, + ) -> Option> { if audio.available { Some(Cow::Borrowed(audio)) + } else if let Some(alternatives) = &audio.alternatives { + let alternatives = alternatives + .iter() + .map(|alt_id| AudioItem::get_audio_item(&self.session, *alt_id)); + let alternatives = future::try_join_all(alternatives).await.unwrap(); + alternatives + .into_iter() + .find(|alt| alt.available) + .map(Cow::Owned) } else { - if let Some(alternatives) = &audio.alternatives { - let alternatives = alternatives - .iter() - .map(|alt_id| AudioItem::get_audio_item(&self.session, *alt_id)); - let alternatives = future::join_all(alternatives).wait().unwrap(); - alternatives - .into_iter() - .find(|alt| alt.available) - .map(Cow::Owned) - } else { - None - } + None } } @@ -611,8 +614,12 @@ impl PlayerTrackLoader { } } - fn load_track(&self, spotify_id: SpotifyId, position_ms: u32) -> Option { - let audio = match AudioItem::get_audio_item(&self.session, spotify_id).wait() { + async fn load_track( + &self, + spotify_id: SpotifyId, + position_ms: u32, + ) -> Option { + let audio = match AudioItem::get_audio_item(&self.session, spotify_id).await { Ok(audio) => audio, Err(_) => { error!("Unable to load audio item."); @@ -622,7 +629,7 @@ impl PlayerTrackLoader { info!("Loading <{}> with Spotify URI <{}>", audio.name, audio.uri); - let audio = match self.find_available_alternative(&audio) { + let audio = match self.find_available_alternative(&audio).await { Some(audio) => audio, None => { warn!("<{}> is not available", audio.uri); @@ -675,7 +682,7 @@ impl PlayerTrackLoader { play_from_beginning, ); - let encrypted_file = match encrypted_file.wait() { + let encrypted_file = match encrypted_file.await { Ok(encrypted_file) => encrypted_file, Err(_) => { error!("Unable to load encrypted file."); @@ -693,7 +700,7 @@ impl PlayerTrackLoader { stream_loader_controller.set_random_access_mode(); } - let key = match key.wait() { + let key = match key.await { Ok(key) => key, Err(_) => { error!("Unable to load decryption key"); @@ -738,10 +745,9 @@ impl PlayerTrackLoader { } impl Future for PlayerInternal { - type Item = (); - type Error = (); + type Output = (); - fn poll(&mut self) -> Poll<(), ()> { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> { // While this is written as a future, it still contains blocking code. // It must be run on its own thread. @@ -749,14 +755,13 @@ impl Future for PlayerInternal { let mut all_futures_completed_or_not_ready = true; // process commands that were sent to us - let cmd = match self.commands.poll() { - Ok(Async::Ready(None)) => return Ok(Async::Ready(())), // client has disconnected - shut down. - Ok(Async::Ready(Some(cmd))) => { + let cmd = match Pin::new(&mut self.commands).poll_next(cx) { + Poll::Ready(None) => return Poll::Ready(()), // client has disconnected - shut down. + Poll::Ready(Some(cmd)) => { all_futures_completed_or_not_ready = false; Some(cmd) } - Ok(Async::NotReady) => None, - Err(_) => None, + _ => None, }; if let Some(cmd) = cmd { @@ -771,8 +776,8 @@ impl Future for PlayerInternal { play_request_id, } = self.state { - match loader.poll() { - Ok(Async::Ready(loaded_track)) => { + match loader.as_mut().poll(cx) { + Poll::Ready(Ok(loaded_track)) => { self.start_playback( track_id, play_request_id, @@ -783,8 +788,7 @@ impl Future for PlayerInternal { panic!("The state wasn't changed by start_playback()"); } } - Ok(Async::NotReady) => (), - Err(_) => { + Poll::Ready(Err(_)) => { warn!("Unable to load <{:?}>\nSkipping to next track", track_id); assert!(self.state.is_loading()); self.send_event(PlayerEvent::EndOfTrack { @@ -792,6 +796,7 @@ impl Future for PlayerInternal { play_request_id, }) } + Poll::Pending => (), } } @@ -801,16 +806,15 @@ impl Future for PlayerInternal { track_id, } = self.preload { - match loader.poll() { - Ok(Async::Ready(loaded_track)) => { + match loader.as_mut().poll(cx) { + Poll::Ready(Ok(loaded_track)) => { self.send_event(PlayerEvent::Preloading { track_id }); self.preload = PlayerPreload::Ready { track_id, loaded_track, }; } - Ok(Async::NotReady) => (), - Err(_) => { + Poll::Ready(Err(_)) => { debug!("Unable to preload {:?}", track_id); self.preload = PlayerPreload::None; // Let Spirc know that the track was unavailable. @@ -827,6 +831,7 @@ impl Future for PlayerInternal { }); } } + Poll::Pending => (), } } @@ -847,8 +852,7 @@ impl Future for PlayerInternal { let packet = decoder.next_packet().expect("Vorbis error"); if let Some(ref packet) = packet { - *stream_position_pcm = - *stream_position_pcm + (packet.data().len() / 2) as u64; + *stream_position_pcm += (packet.data().len() / 2) as u64; let stream_position_millis = Self::position_pcm_to_ms(*stream_position_pcm); let notify_about_position = match *reported_nominal_start_time { @@ -858,11 +862,7 @@ impl Future for PlayerInternal { let lag = (Instant::now() - reported_nominal_start_time).as_millis() as i64 - stream_position_millis as i64; - if lag > 1000 { - true - } else { - false - } + lag > 1000 } }; if notify_about_position { @@ -918,11 +918,11 @@ impl Future for PlayerInternal { } if self.session.is_invalid() { - return Ok(Async::Ready(())); + return Poll::Ready(()); } if (!self.state.is_playing()) && all_futures_completed_or_not_ready { - return Ok(Async::NotReady); + return Poll::Pending; } } } @@ -1066,7 +1066,9 @@ impl PlayerInternal { editor.modify_stream(&mut packet.data_mut()) }; - if self.config.normalisation && normalisation_factor != 1.0 { + if self.config.normalisation + && (normalisation_factor - 1.0).abs() < f32::EPSILON + { for x in packet.data_mut().iter_mut() { *x = (*x as f32 * normalisation_factor) as i16; } @@ -1363,9 +1365,7 @@ impl PlayerInternal { self.preload = PlayerPreload::None; // If we don't have a loader yet, create one from scratch. - let loader = loader - .or_else(|| Some(self.load_track(track_id, position_ms))) - .unwrap(); + let loader = loader.unwrap_or_else(|| Box::pin(self.load_track(track_id, position_ms))); // Set ourselves to a loading state. self.state = PlayerState::Loading { @@ -1420,7 +1420,10 @@ impl PlayerInternal { // schedule the preload of the current track if desired. if preload_track { let loader = self.load_track(track_id, 0); - self.preload = PlayerPreload::Loading { track_id, loader } + self.preload = PlayerPreload::Loading { + track_id, + loader: Box::pin(loader), + } } } @@ -1532,34 +1535,34 @@ impl PlayerInternal { } } - fn load_track( + pub fn load_track( &self, spotify_id: SpotifyId, position_ms: u32, - ) -> Box> { + ) -> impl Future> + 'static { // This method creates a future that returns the loaded stream and associated info. // Ideally all work should be done using asynchronous code. However, seek() on the // audio stream is implemented in a blocking fashion. Thus, we can't turn it into future // easily. Instead we spawn a thread to do the work and return a one-shot channel as the // future to work with. - let loader = PlayerTrackLoader { - session: self.session.clone(), - config: self.config.clone(), - }; + let session = self.session.clone(); + let config = self.config.clone(); - let (result_tx, result_rx) = futures::sync::oneshot::channel(); + async move { + let loader = PlayerTrackLoader { session, config }; - std::thread::spawn(move || { - loader - .load_track(spotify_id, position_ms) - .and_then(move |data| { + let (result_tx, result_rx) = oneshot::channel(); + + std::thread::spawn(move || { + todo!("How to block in futures 0.3?") + /*if let Some(data) = block_on(loader.load_track(spotify_id, position_ms)) { let _ = result_tx.send(data); - Some(()) - }); - }); + }*/ + }); - Box::new(result_rx.map_err(|_| ())) + result_rx.await.map_err(|_| ()) + } } fn preload_data_before_playback(&mut self) { @@ -1689,13 +1692,13 @@ impl Subfile { } impl Read for Subfile { - fn read(&mut self, buf: &mut [u8]) -> Result { + fn read(&mut self, buf: &mut [u8]) -> io::Result { self.stream.read(buf) } } impl Seek for Subfile { - fn seek(&mut self, mut pos: SeekFrom) -> Result { + fn seek(&mut self, mut pos: SeekFrom) -> io::Result { pos = match pos { SeekFrom::Start(offset) => SeekFrom::Start(offset + self.offset), x => x, From 6c9d8c8d83dd23cc69f1a00941a4feeadcb370d7 Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Fri, 22 Jan 2021 22:32:45 +0100 Subject: [PATCH 018/103] Replace pin_project and updated dependencies --- audio/Cargo.toml | 4 +-- audio/src/fetch.rs | 78 ++++++++++++++++++----------------------- audio/src/lib.rs | 4 ++- core/Cargo.toml | 9 +++-- core/src/lib.rs | 4 +-- core/src/mercury/mod.rs | 12 ++++--- 6 files changed, 56 insertions(+), 55 deletions(-) diff --git a/audio/Cargo.toml b/audio/Cargo.toml index f9d232b..5e950cd 100644 --- a/audio/Cargo.toml +++ b/audio/Cargo.toml @@ -16,11 +16,11 @@ bit-set = "0.5" byteorder = "1.4" bytes = "1.0" futures = "0.3" -lewton = "0.9" +lewton = "0.10" log = "0.4" num-bigint = "0.3" num-traits = "0.2" -pin-project = "1.0" +pin-project-lite = "0.2.4" tempfile = "3.1" librespot-tremor = { version = "0.1.0", optional = true } diff --git a/audio/src/fetch.rs b/audio/src/fetch.rs index 5d15866..51dddc6 100644 --- a/audio/src/fetch.rs +++ b/audio/src/fetch.rs @@ -502,8 +502,8 @@ async fn audio_file_fetch_receive_data( future::ready(if request_length == 0 { Err(TryFoldErr::FinishEarly) - } else { - Ok(()) + } else { + Ok(()) }) }) .await; @@ -532,7 +532,7 @@ async fn audio_file_fetch_receive_data( ); } } -/* +/* async fn audio_file_fetch( session: Session, shared: Arc, @@ -689,20 +689,21 @@ async fn audio_file_fetch( future::select_all(vec![f1, f2, f3]).await }*/ -#[pin_project] -struct AudioFileFetch { - session: Session, - shared: Arc, - output: Option, +pin_project! { + struct AudioFileFetch { + session: Session, + shared: Arc, + output: Option, - file_data_tx: mpsc::UnboundedSender, - #[pin] - file_data_rx: mpsc::UnboundedReceiver, + file_data_tx: mpsc::UnboundedSender, + #[pin] + file_data_rx: mpsc::UnboundedReceiver, - #[pin] - stream_loader_command_rx: mpsc::UnboundedReceiver, - complete_tx: Option>, - network_response_times_ms: Vec, + #[pin] + stream_loader_command_rx: mpsc::UnboundedReceiver, + complete_tx: Option>, + network_response_times_ms: Vec, + } } impl AudioFileFetch { @@ -853,8 +854,6 @@ impl AudioFileFetch { } } - - fn poll_file_data_rx(&mut self, cx: &mut Context<'_>) -> Poll<()> { loop { match Pin::new(&mut self.file_data_rx).poll_next(cx) { @@ -923,12 +922,10 @@ impl AudioFileFetch { if full { self.finish(); - return Poll::Ready(()) + return Poll::Ready(()); } } - Poll::Pending => { - return Poll::Pending - } + Poll::Pending => return Poll::Pending, } } } @@ -936,27 +933,22 @@ impl AudioFileFetch { fn poll_stream_loader_command_rx(&mut self, cx: &mut Context<'_>) -> Poll<()> { loop { match Pin::new(&mut self.stream_loader_command_rx).poll_next(cx) { - Poll::Ready(None) => - return Poll::Ready(()), - Poll::Ready(Some(cmd)) => { - match cmd { - StreamLoaderCommand::Fetch(request) => { - self.download_range(request.start, request.length); - } - StreamLoaderCommand::RandomAccessMode() => { - *(self.shared.download_strategy.lock().unwrap()) = - DownloadStrategy::RandomAccess(); - } - StreamLoaderCommand::StreamMode() => { - - *(self.shared.download_strategy.lock().unwrap()) = - DownloadStrategy::Streaming(); - } - StreamLoaderCommand::Close() => return Poll::Ready(()) - + Poll::Ready(None) => return Poll::Ready(()), + Poll::Ready(Some(cmd)) => match cmd { + StreamLoaderCommand::Fetch(request) => { + self.download_range(request.start, request.length); } - } - Poll::Pending => return Poll::Pending + StreamLoaderCommand::RandomAccessMode() => { + *(self.shared.download_strategy.lock().unwrap()) = + DownloadStrategy::RandomAccess(); + } + StreamLoaderCommand::StreamMode() => { + *(self.shared.download_strategy.lock().unwrap()) = + DownloadStrategy::Streaming(); + } + StreamLoaderCommand::Close() => return Poll::Ready(()), + }, + Poll::Pending => return Poll::Pending, } } } @@ -974,11 +966,11 @@ impl Future for AudioFileFetch { fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> { if let Poll::Ready(()) = self.poll_stream_loader_command_rx(cx) { - return Poll::Ready(()) + return Poll::Ready(()); } if let Poll::Ready(()) = self.poll_file_data_rx(cx) { - return Poll::Ready(()) + return Poll::Ready(()); } if let DownloadStrategy::Streaming() = self.get_download_strategy() { diff --git a/audio/src/lib.rs b/audio/src/lib.rs index 6955588..1be1ba8 100644 --- a/audio/src/lib.rs +++ b/audio/src/lib.rs @@ -1,7 +1,9 @@ +#![allow(clippy::unused_io_amount)] + #[macro_use] extern crate log; #[macro_use] -extern crate pin_project; +extern crate pin_project_lite; extern crate aes_ctr; extern crate bit_set; diff --git a/core/Cargo.toml b/core/Cargo.toml index a9fcc24..c092c04 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -20,14 +20,14 @@ bytes = "1.0" futures = { version = "0.3", features = ["bilock", "unstable"] } hmac = "0.7" httparse = "1.3" -hyper = { version = "0.14", features = ["client", "tcp", "http1", "http2", "stream"] } +hyper = { version = "0.14", features = ["client", "tcp", "http1", "http2"] } log = "0.4" num-bigint = "0.3" num-integer = "0.1" num-traits = "0.2" once_cell = "1.5.2" pbkdf2 = "0.3" -pin-project = "1.0" +pin-project-lite = "0.2.4" protobuf = "~2.14.0" rand = "0.7" serde = "1.0" @@ -35,7 +35,7 @@ serde_derive = "1.0" serde_json = "1.0" sha-1 = "~0.8" shannon = "0.2.0" -tokio = { version = "1.0", features = ["io-util", "rt-multi-thread", "macros" ] } +tokio = { version = "1.0", features = ["io-util", "rt-multi-thread"] } tokio-util = { version = "0.6", features = ["codec"] } url = "1.7" uuid = { version = "0.8", features = ["v4"] } @@ -43,3 +43,6 @@ uuid = { version = "0.8", features = ["v4"] } [build-dependencies] rand = "0.7" vergen = "3.0.4" + +[dev-dependencies] +tokio = {version = "1.0", features = ["macros"] } \ No newline at end of file diff --git a/core/src/lib.rs b/core/src/lib.rs index 65f6f81..a15aa7a 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -5,7 +5,7 @@ extern crate log; #[macro_use] extern crate serde_derive; #[macro_use] -extern crate pin_project; +extern crate pin_project_lite; extern crate aes; extern crate base64; extern crate byteorder; @@ -25,7 +25,7 @@ extern crate serde; extern crate serde_json; extern crate sha1; extern crate shannon; -extern crate tokio; +pub extern crate tokio; extern crate tokio_util; extern crate url; extern crate uuid; diff --git a/core/src/mercury/mod.rs b/core/src/mercury/mod.rs index e77b4a4..72360c9 100644 --- a/core/src/mercury/mod.rs +++ b/core/src/mercury/mod.rs @@ -31,14 +31,18 @@ pub struct MercuryPending { callback: Option>>, } -#[pin_project] -pub struct MercuryFuture(#[pin] oneshot::Receiver>); +pin_project! { + pub struct MercuryFuture { + #[pin] + receiver: oneshot::Receiver> + } +} impl Future for MercuryFuture { type Output = Result; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - match self.project().0.poll(cx) { + match self.project().receiver.poll(cx) { Poll::Ready(Ok(x)) => Poll::Ready(x), Poll::Ready(Err(_)) => Poll::Ready(Err(MercuryError)), Poll::Pending => Poll::Pending, @@ -73,7 +77,7 @@ impl MercuryManager { let data = req.encode(&seq); self.session().send_packet(cmd, data); - MercuryFuture(rx) + MercuryFuture { receiver: rx } } pub fn get>(&self, uri: T) -> MercuryFuture { From fe371868046222690f6104ba2742ce73589c2e25 Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Fri, 22 Jan 2021 22:51:41 +0100 Subject: [PATCH 019/103] Make librespot_playback work --- playback/src/audio_backend/mod.rs | 8 +- playback/src/audio_backend/pipe.rs | 2 +- playback/src/player.rs | 118 ++++++++++++++--------------- src/lib.rs | 2 +- 4 files changed, 64 insertions(+), 66 deletions(-) diff --git a/playback/src/audio_backend/mod.rs b/playback/src/audio_backend/mod.rs index a9840d4..21ee3c0 100644 --- a/playback/src/audio_backend/mod.rs +++ b/playback/src/audio_backend/mod.rs @@ -10,7 +10,9 @@ pub trait Sink { fn write(&mut self, data: &[i16]) -> io::Result<()>; } -fn mk_sink(device: Option) -> Box { +pub type SinkBuilder = fn(Option) -> Box; + +fn mk_sink(device: Option) -> Box { Box::new(S::open(device)) } @@ -54,7 +56,7 @@ use self::pipe::StdoutSink; mod subprocess; use self::subprocess::SubprocessSink; -pub const BACKENDS: &'static [(&'static str, fn(Option) -> Box)] = &[ +pub const BACKENDS: &'static [(&'static str, SinkBuilder)] = &[ #[cfg(feature = "alsa-backend")] ("alsa", mk_sink::), #[cfg(feature = "portaudio-backend")] @@ -73,7 +75,7 @@ pub const BACKENDS: &'static [(&'static str, fn(Option) -> Box ("subprocess", mk_sink::), ]; -pub fn find(name: Option) -> Option) -> Box> { +pub fn find(name: Option) -> Option { if let Some(name) = name { BACKENDS .iter() diff --git a/playback/src/audio_backend/pipe.rs b/playback/src/audio_backend/pipe.rs index 2adafe1..02b8faf 100644 --- a/playback/src/audio_backend/pipe.rs +++ b/playback/src/audio_backend/pipe.rs @@ -4,7 +4,7 @@ use std::io::{self, Write}; use std::mem; use std::slice; -pub struct StdoutSink(Box); +pub struct StdoutSink(Box); impl Open for StdoutSink { fn open(path: Option) -> StdoutSink { diff --git a/playback/src/player.rs b/playback/src/player.rs index df442f0..ff0fba2 100644 --- a/playback/src/player.rs +++ b/playback/src/player.rs @@ -6,6 +6,7 @@ use crate::audio::{ }; use crate::audio_backend::Sink; use crate::config::{Bitrate, PlayerConfig}; +use crate::librespot_core::tokio; use crate::metadata::{AudioItem, FileFormat}; use crate::mixer::AudioFilter; use librespot_core::session::Session; @@ -19,7 +20,6 @@ use futures::{ }; use std::io::{Read, Seek, SeekFrom}; use std::mem; -use std::thread; use std::time::{Duration, Instant}; use std::{borrow::Cow, io}; use std::{ @@ -32,7 +32,7 @@ const PRELOAD_NEXT_TRACK_BEFORE_END_DURATION_MS: u32 = 30000; pub struct Player { commands: Option>, - thread_handle: Option>, + task_handle: Option>, play_request_id_generator: SeqGenerator, } @@ -52,7 +52,7 @@ struct PlayerInternal { state: PlayerState, preload: PlayerPreload, - sink: Box, + sink: Box, sink_status: SinkStatus, sink_event_callback: Option, audio_filter: Option>, @@ -242,38 +242,38 @@ impl Player { sink_builder: F, ) -> (Player, PlayerEventChannel) where - F: FnOnce() -> Box + Send + 'static, + F: FnOnce() -> Box + Send + 'static, { let (cmd_tx, cmd_rx) = mpsc::unbounded(); let (event_sender, event_receiver) = mpsc::unbounded(); - let handle = thread::spawn(move || { - debug!("new Player[{}]", session.session_id()); + debug!("new Player[{}]", session.session_id()); - let internal = PlayerInternal { - session: session, - config: config, - commands: cmd_rx, + let internal = PlayerInternal { + session: session, + config: config, + commands: cmd_rx, - state: PlayerState::Stopped, - preload: PlayerPreload::None, - sink: sink_builder(), - sink_status: SinkStatus::Closed, - sink_event_callback: None, - audio_filter: audio_filter, - event_senders: [event_sender].to_vec(), - }; + state: PlayerState::Stopped, + preload: PlayerPreload::None, + sink: sink_builder(), + sink_status: SinkStatus::Closed, + sink_event_callback: None, + audio_filter: audio_filter, + event_senders: [event_sender].to_vec(), + }; - // While PlayerInternal is written as a future, it still contains blocking code. - // It must be run by using wait() in a dedicated thread. - todo!("How to block in futures 0.3?"); + // While PlayerInternal is written as a future, it still contains blocking code. + // It must be run by using wait() in a dedicated thread. + let handle = tokio::spawn(async move { + internal.await; debug!("PlayerInternal thread finished."); }); ( Player { commands: Some(cmd_tx), - thread_handle: Some(handle), + task_handle: Some(handle), play_request_id_generator: SeqGenerator::new(0), }, event_receiver, @@ -347,11 +347,13 @@ impl Drop for Player { fn drop(&mut self) { debug!("Shutting down player thread ..."); self.commands = None; - if let Some(handle) = self.thread_handle.take() { - match handle.join() { - Ok(_) => (), - Err(_) => error!("Player thread panicked!"), - } + if let Some(handle) = self.task_handle.take() { + tokio::spawn(async { + match handle.await { + Ok(_) => (), + Err(_) => error!("Player thread panicked!"), + } + }); } } } @@ -369,11 +371,11 @@ enum PlayerPreload { None, Loading { track_id: SpotifyId, - loader: Pin>>>, + loader: Pin> + Send>>, }, Ready { track_id: SpotifyId, - loaded_track: PlayerLoadedTrackData, + loaded_track: Box, }, } @@ -385,7 +387,7 @@ enum PlayerState { track_id: SpotifyId, play_request_id: u64, start_playback: bool, - loader: Pin>>>, + loader: Pin> + Send>>, }, Paused { track_id: SpotifyId, @@ -430,23 +432,15 @@ impl PlayerState { #[allow(dead_code)] fn is_stopped(&self) -> bool { - use self::PlayerState::*; - match *self { - Stopped => true, - _ => false, - } + matches!(self, Self::Stopped) } fn is_loading(&self) -> bool { - use self::PlayerState::*; - match *self { - Loading { .. } => true, - _ => false, - } + matches!(self, Self::Loading { .. }) } fn decoder(&mut self) -> Option<&mut Decoder> { - use self::PlayerState::*; + use PlayerState::*; match *self { Stopped | EndOfTrack { .. } | Loading { .. } => None, Paused { @@ -575,10 +569,10 @@ struct PlayerTrackLoader { } impl PlayerTrackLoader { - async fn find_available_alternative<'a>( - &self, - audio: &'a AudioItem, - ) -> Option> { + async fn find_available_alternative<'a, 'b>( + &'a self, + audio: &'b AudioItem, + ) -> Option> { if audio.available { Some(Cow::Borrowed(audio)) } else if let Some(alternatives) = &audio.alternatives { @@ -716,7 +710,7 @@ impl PlayerTrackLoader { } Err(_) => { warn!("Unable to extract normalisation data, using default value."); - 1.0 as f32 + 1.0_f32 } }; @@ -811,7 +805,7 @@ impl Future for PlayerInternal { self.send_event(PlayerEvent::Preloading { track_id }); self.preload = PlayerPreload::Ready { track_id, - loaded_track, + loaded_track: Box::new(loaded_track), }; } Poll::Ready(Err(_)) => { @@ -1061,7 +1055,7 @@ impl PlayerInternal { fn handle_packet(&mut self, packet: Option, normalisation_factor: f32) { match packet { Some(mut packet) => { - if packet.data().len() > 0 { + if !packet.data().is_empty() { if let Some(ref editor) = self.audio_filter { editor.modify_stream(&mut packet.data_mut()) }; @@ -1216,10 +1210,9 @@ impl PlayerInternal { loaded_track .stream_loader_controller .set_random_access_mode(); - let _ = loaded_track.decoder.seek(position_ms as i64); // This may be blocking. - // But most likely the track is fully - // loaded already because we played - // to the end of it. + let _ = tokio::task::block_in_place(|| { + loaded_track.decoder.seek(position_ms as i64) + }); loaded_track.stream_loader_controller.set_stream_mode(); loaded_track.stream_position_pcm = Self::position_ms_to_pcm(position_ms); } @@ -1252,7 +1245,7 @@ impl PlayerInternal { // we can use the current decoder. Ensure it's at the correct position. if Self::position_ms_to_pcm(position_ms) != *stream_position_pcm { stream_loader_controller.set_random_access_mode(); - let _ = decoder.seek(position_ms as i64); // This may be blocking. + let _ = tokio::task::block_in_place(|| decoder.seek(position_ms as i64)); stream_loader_controller.set_stream_mode(); *stream_position_pcm = Self::position_ms_to_pcm(position_ms); } @@ -1320,10 +1313,12 @@ impl PlayerInternal { loaded_track .stream_loader_controller .set_random_access_mode(); - let _ = loaded_track.decoder.seek(position_ms as i64); // This may be blocking + let _ = tokio::task::block_in_place(|| { + loaded_track.decoder.seek(position_ms as i64) + }); loaded_track.stream_loader_controller.set_stream_mode(); } - self.start_playback(track_id, play_request_id, loaded_track, play); + self.start_playback(track_id, play_request_id, *loaded_track, play); return; } else { unreachable!(); @@ -1539,7 +1534,7 @@ impl PlayerInternal { &self, spotify_id: SpotifyId, position_ms: u32, - ) -> impl Future> + 'static { + ) -> impl Future> + Send + 'static { // This method creates a future that returns the loaded stream and associated info. // Ideally all work should be done using asynchronous code. However, seek() on the // audio stream is implemented in a blocking fashion. Thus, we can't turn it into future @@ -1554,11 +1549,10 @@ impl PlayerInternal { let (result_tx, result_rx) = oneshot::channel(); - std::thread::spawn(move || { - todo!("How to block in futures 0.3?") - /*if let Some(data) = block_on(loader.load_track(spotify_id, position_ms)) { + tokio::spawn(async move { + if let Some(data) = loader.load_track(spotify_id, position_ms).await { let _ = result_tx.send(data); - }*/ + } }); result_rx.await.map_err(|_| ()) @@ -1588,7 +1582,9 @@ impl PlayerInternal { * bytes_per_second as f64) as usize, (READ_AHEAD_BEFORE_PLAYBACK_SECONDS * bytes_per_second as f64) as usize, ); - stream_loader_controller.fetch_next_blocking(wait_for_data_length); + tokio::task::block_in_place(|| { + stream_loader_controller.fetch_next_blocking(wait_for_data_length) + }); } } } diff --git a/src/lib.rs b/src/lib.rs index 610062e..31bac34 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,7 +2,7 @@ #![cfg_attr(feature = "cargo-clippy", allow(unused_io_amount))] pub extern crate librespot_audio as audio; -pub extern crate librespot_connect as connect; +// pub extern crate librespot_connect as connect; pub extern crate librespot_core as core; pub extern crate librespot_metadata as metadata; pub extern crate librespot_playback as playback; From 91d7d0422b05b03f437e4789f284ceac0592131f Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Sat, 23 Jan 2021 00:02:49 +0100 Subject: [PATCH 020/103] Preparing main crate for testing --- Cargo.lock | 3286 ++++++++++++++++++++++----------------------------- Cargo.toml | 40 +- src/lib.rs | 1 - src/main.rs | 620 ---------- 4 files changed, 1445 insertions(+), 2502 deletions(-) delete mode 100644 src/main.rs diff --git a/Cargo.lock b/Cargo.lock index c4fc5c4..5813a70 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,3506 +2,3084 @@ # It is not intended for manual editing. [[package]] name = "addr2line" -version = "0.14.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a55f82cfe485775d02112886f4169bde0c5894d75e79ead7eafe7e40a25e45f7" dependencies = [ - "gimli 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gimli", ] [[package]] name = "adler" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e" [[package]] name = "adler32" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" [[package]] name = "aes" -version = "0.3.2" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884391ef1066acaa41e766ba8f596341b96e93ce34f9a43e7d24bf0a0eaf0561" dependencies = [ - "aes-soft 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "aesni 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "aes-soft", + "aesni", + "cipher", ] [[package]] name = "aes-ctr" -version = "0.3.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7729c3cde54d67063be556aeac75a81330d802f0259500ca40cb52967f975763" dependencies = [ - "aes-soft 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "aesni 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ctr 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "stream-cipher 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "aes-soft", + "aesni", + "cipher", + "ctr", ] [[package]] name = "aes-soft" -version = "0.3.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be14c7498ea50828a38d0e24a765ed2effe92a705885b57d029cd67d45744072" dependencies = [ - "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "cipher", + "opaque-debug 0.3.0", ] [[package]] name = "aesni" -version = "0.6.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2e11f5e94c2f7d386164cc2aa1f97823fed6f259e486940a71c174dd01b0ce" dependencies = [ - "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "stream-cipher 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "cipher", + "opaque-debug 0.3.0", ] [[package]] name = "alsa" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4a0d4ebc8b23041c5de9bc9aee13b4bad844a589479701f31a5934cfe4aeb32" dependencies = [ - "alsa-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "nix 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "alsa-sys 0.1.2", + "bitflags 0.9.1", + "libc", + "nix 0.9.0", ] [[package]] name = "alsa" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb213f6b3e4b1480a60931ca2035794aa67b73103d254715b1db7b70dcb3c934" dependencies = [ - "alsa-sys 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "nix 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", + "alsa-sys 0.3.1", + "bitflags 1.2.1", + "libc", + "nix 0.15.0", ] [[package]] name = "alsa-sys" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0edcbbf9ef68f15ae1b620f722180b82a98b6f0628d30baa6b8d2a5abc87d58" dependencies = [ - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "pkg-config", ] [[package]] name = "alsa-sys" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db8fee663d06c4e303404ef5f40488a53e062f89ba8bfed81f42325aafad1527" dependencies = [ - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "pkg-config", ] [[package]] name = "anyhow" -version = "1.0.35" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "arc-swap" -version = "0.4.7" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afddf7f520a80dbf76e6f50a35bca42a2331ef227a28b3b6dc5c2e2338d114b1" [[package]] name = "ascii" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eab1c04a571841102f5345a8fc0f6bb3d31c315dec879b5c6e42e40ce7ffa34e" [[package]] -name = "atty" -version = "0.2.14" +name = "async-stream" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3670df70cbc01729f901f94c887814b3c68db038aad1329a418bae178bc5295c" dependencies = [ - "hermit-abi 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "async-stream-impl", + "futures-core", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3548b8efc9f8e8a5a0a2808c5bd8451a9031b9e5b879a79590304ae928b0a70" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "async-trait" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d3a45e77e34375a7923b1e8febb049bb011f064714a8e17a1a616fef01da13d" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] name = "autocfg" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" [[package]] name = "backtrace" -version = "0.3.55" +version = "0.3.56" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d117600f438b1707d4e4ae15d3595657288f8235a0eb593e80ecc98ab34e1bc" dependencies = [ - "addr2line 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "miniz_oxide 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "object 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-demangle 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", + "addr2line", + "cfg-if 1.0.0", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", ] +[[package]] +name = "base-x" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4521f3e3d031370679b3b140beb36dfe4801b09ac77e30c61941f97df3ef28b" + [[package]] name = "base64" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643" dependencies = [ - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "safemem 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "safemem", ] -[[package]] -name = "base64" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "base64" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "base64" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" [[package]] name = "bindgen" -version = "0.53.3" +version = "0.56.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2da379dbebc0b76ef63ca68d8fc6e71c0f13e59432e0987e508c1820e6ab5239" dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "cexpr 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "clang-sys 0.29.3 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hash 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "shlex 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "peeking_take_while", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", ] [[package]] name = "bit-set" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de" dependencies = [ - "bit-vec 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "bit-vec", ] [[package]] name = "bit-vec" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" [[package]] name = "bitflags" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" [[package]] name = "bitflags" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "block-buffer" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" dependencies = [ - "block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "block-cipher-trait" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "block-modes" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "block-padding", + "byte-tools", + "byteorder", + "generic-array 0.12.3", ] [[package]] name = "block-padding" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" dependencies = [ - "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "byte-tools", ] [[package]] name = "bumpalo" version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820" [[package]] name = "byte-tools" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" [[package]] name = "byteorder" -version = "1.3.4" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b" [[package]] name = "bytes" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "bytes" -version = "0.5.6" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" [[package]] name = "cc" -version = "1.0.65" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48" [[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" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4aedb84272dbe89af497cf81375129abda4fc0a9e7c5d317498c15cc30c0d27" dependencies = [ - "nom 5.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "nom", ] [[package]] name = "cfg-if" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.13" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" dependencies = [ - "num-integer 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "num-integer", + "num-traits", + "time 0.1.43", + "winapi", ] [[package]] name = "chunked_transfer" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7477065d45a8fe57167bf3cf8bcd3729b54cfcb81cca49bda2d038ea89ae82ca" + +[[package]] +name = "cipher" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801" +dependencies = [ + "generic-array 0.14.4", +] [[package]] name = "clang-sys" -version = "0.29.3" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0659001ab56b791be01d4b729c44376edc6718cf389a502e579b77b758f3296c" dependencies = [ - "glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "libloading 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "glob", + "libc", + "libloading", ] [[package]] name = "cloudabi" version = "0.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "cloudabi" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1", ] [[package]] name = "combine" version = "3.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da3da6baa321ec19e1cc41d31bf599f00c783d0517095cdaf0332e3fe8d20680" dependencies = [ - "ascii 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "either 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ascii", + "byteorder", + "either", + "memchr", + "unreachable", ] [[package]] name = "combine" -version = "4.4.0" +version = "4.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc4369b5e4c0cddf64ad8981c0111e7df4f7078f4d6ba98fb31f2e17c4c57b7e" dependencies = [ - "bytes 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-project-lite 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes", + "memchr", ] +[[package]] +name = "const_fn" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28b9d6de7f49e22cf97ad17fc4036ece69300032f45f78f30b4a4482cdc3f4a6" + [[package]] name = "cookie" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784ad0fbab4f3e9cef09f20e0aea6000ae08d2cb98ac4c0abc53df18803d702f" +dependencies = [ + "percent-encoding 2.1.0", + "time 0.2.24", + "version_check", +] + +[[package]] +name = "cookie_store" version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3818dfca4b0cb5211a659bbcbb94225b7127407b2b135e650d717bfb78ab10d3" dependencies = [ - "time 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "cookie", + "idna 0.2.0", + "log", + "publicsuffix", + "serde", + "serde_json", + "time 0.2.24", + "url 2.2.0", ] [[package]] name = "core-foundation-sys" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b" [[package]] name = "coreaudio-rs" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f229761965dad3e9b11081668a6ea00f1def7aa46062321b5ec245b834f6e491" dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "coreaudio-sys 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1", + "coreaudio-sys", ] [[package]] name = "coreaudio-sys" -version = "0.2.5" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b7e3347be6a09b46aba228d6608386739fb70beff4f61e07422da87b0bb31fa" dependencies = [ - "bindgen 0.53.3 (registry+https://github.com/rust-lang/crates.io-index)", + "bindgen", ] [[package]] name = "cpal" version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05631e2089dfa5d3b6ea1cfbbfd092e2ee5deeb69698911bc976b28b746d3657" dependencies = [ - "alsa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "coreaudio-rs 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "jni 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", - "js-sys 0.3.46 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "mach 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ndk 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "ndk-glue 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "nix 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", - "oboe 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)", - "stdweb 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "thiserror 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)", - "web-sys 0.3.46 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "alsa 0.4.3", + "core-foundation-sys", + "coreaudio-rs", + "jni 0.17.0", + "js-sys", + "lazy_static", + "libc", + "mach", + "ndk", + "ndk-glue", + "nix 0.15.0", + "oboe", + "parking_lot", + "stdweb 0.1.3", + "thiserror", + "web-sys", + "winapi", ] [[package]] name = "crc32fast" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" dependencies = [ - "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam-deque" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-epoch 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "memoffset 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", - "scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam-queue" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam-queue" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam-utils" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam-utils" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 1.0.0", ] [[package]] name = "crypto-mac" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" dependencies = [ - "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", - "subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array 0.12.3", + "subtle", ] [[package]] name = "ctr" -version = "0.3.2" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb4a30d54f7443bf3d6191dcd486aca19e67cb3c49fa7a06a319966346707e7f" dependencies = [ - "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "stream-cipher 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "cipher", ] [[package]] name = "darling" version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858" dependencies = [ - "darling_core 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", - "darling_macro 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", + "darling_core", + "darling_macro", ] [[package]] name = "darling_core" version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b" dependencies = [ - "fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "ident_case 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "strsim 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)", + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", ] [[package]] name = "darling_macro" version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72" dependencies = [ - "darling_core 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)", + "darling_core", + "quote", + "syn", ] [[package]] name = "derivative" -version = "2.1.1" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaed5874effa6cde088c644ddcdcb4ffd1511391c5be4fdd7a5ccd02c7e4a183" dependencies = [ - "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "digest" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" dependencies = [ - "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array 0.12.3", ] [[package]] -name = "dns-sd" -version = "0.1.3" +name = "discard" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" [[package]] name = "either" version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "env_logger" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "humantime 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "termcolor 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" [[package]] name = "error-chain" -version = "0.12.2" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" dependencies = [ - "backtrace 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)", - "version_check 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace", + "version_check", ] [[package]] name = "fake-simd" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" [[package]] name = "fetch_unroll" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d44807d562d137f063cbfe209da1c3f9f2fa8375e11166ef495daab7b847f9" dependencies = [ - "libflate 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "tar 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "ureq 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)", + "libflate", + "tar", + "ureq", ] [[package]] name = "filetime" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d34cfa13a63ae058bfa601fe9e313bbdb3746427c1459185464ce0fcf62e1e8" dependencies = [ - "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.57 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 1.0.0", + "libc", + "redox_syscall 0.2.4", + "winapi", ] [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "form_urlencoded" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ece68d15c92e84fa4f19d3780f1294e5ca82a78a6d515f1efaabcc144688be00" dependencies = [ - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "matches", + "percent-encoding 2.1.0", ] [[package]] name = "fuchsia-cprng" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "fuchsia-zircon" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "fuchsia-zircon-sys" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" [[package]] name = "futures" -version = "0.1.29" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da9052a1a50244d8d5aa9bf55cbc2fb6f357c86cc52e46c62ed390a7180cf150" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] [[package]] name = "futures-channel" -version = "0.3.5" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2d31b7ec7efab6eefc7c57233bb10b847986139d88cc2f5a02a1ae6871a1846" dependencies = [ - "futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core", + "futures-sink", ] [[package]] name = "futures-core" -version = "0.3.5" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "futures-cpupool" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "79e5145dde8da7d1b3892dad07a9c98fc04bc39892b1ecc9692cf53e2b780a65" [[package]] name = "futures-executor" -version = "0.3.5" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9e59fdc009a4b3096bf94f740a0f2424c082521f20a9b08c5c07c48d90fd9b9" dependencies = [ - "futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-task 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-util 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core", + "futures-task", + "futures-util", ] [[package]] -name = "futures-macro" -version = "0.3.5" +name = "futures-io" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28be053525281ad8259d47e4de5de657b25e7bac113458555bb4b70bc6870500" + +[[package]] +name = "futures-macro" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c287d25add322d9f9abdcdc5927ca398917996600182178774032e9f8258fedd" dependencies = [ - "proc-macro-hack 0.5.16 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "futures-sink" -version = "0.3.5" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caf5c69029bda2e743fddd0582d1083951d65cc9539aebf8812f36c3491342d6" [[package]] name = "futures-task" -version = "0.3.5" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13de07eb8ea81ae445aca7b69f5f7bf15d7bf4912d8ca37d6645c77ae8a58d86" dependencies = [ - "once_cell 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "once_cell", ] [[package]] name = "futures-util" -version = "0.3.5" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "632a8cd0f2a4b3fdea1657f08bde063848c3bd00f9bbf6e256b8be78802e624b" dependencies = [ - "futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-macro 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-task 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-project 0.4.22 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-utils 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-hack 0.5.16 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-nested 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "proc-macro-hack", + "proc-macro-nested", + "slab", ] [[package]] name = "gcc" version = "0.3.55" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" [[package]] name = "generic-array" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" dependencies = [ - "typenum 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "typenum", ] [[package]] -name = "getopts" -version = "0.2.21" +name = "generic-array" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" dependencies = [ - "unicode-width 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "typenum", + "version_check", ] [[package]] name = "getrandom" -version = "0.1.14" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 1.0.0", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi 0.10.1+wasi-snapshot-preview1", ] [[package]] name = "gimli" version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6503fe142514ca4799d4c26297c4248239fe8838d827db6bd6065c6ed29a6ce" [[package]] name = "glib" version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c685013b7515e668f1b57a165b009d4d28cb139a8a989bbd699c10dad29d0c5" dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-channel 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-executor 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-task 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-util 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "glib-macros 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "glib-sys 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "gobject-sys 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "once_cell 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "futures-util", + "glib-macros", + "glib-sys", + "gobject-sys", + "libc", + "once_cell", ] [[package]] name = "glib-macros" version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41486a26d1366a8032b160b59065a59fb528530a46a49f627e7048fb8c064039" dependencies = [ - "anyhow 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)", - "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "itertools 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-crate 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-error 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)", + "anyhow", + "heck", + "itertools", + "proc-macro-crate", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "glib-sys" version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7e9b997a66e9a23d073f2b1abb4dbfc3925e0b8952f67efd8d9b6e168e4cdc1" dependencies = [ - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "system-deps 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "system-deps", ] [[package]] name = "glob" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" [[package]] name = "gobject-sys" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "952133b60c318a62bf82ee75b93acc7e84028a093e06b9e27981c2b6fe68218c" dependencies = [ - "glib-sys 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "system-deps 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "glib-sys", + "libc", + "system-deps", ] [[package]] name = "gstreamer" version = "0.16.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d50f822055923f1cbede233aa5dfd4ee957cf328fb3076e330886094e11d6cf" dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-channel 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-util 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "glib 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)", - "glib-sys 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "gobject-sys 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gstreamer-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "muldiv 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "num-rational 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "once_cell 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "paste 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "pretty-hex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "thiserror 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1", + "cfg-if 1.0.0", + "futures-channel", + "futures-core", + "futures-util", + "glib", + "glib-sys", + "gobject-sys", + "gstreamer-sys", + "libc", + "muldiv", + "num-rational", + "once_cell", + "paste", + "pretty-hex", + "thiserror", ] [[package]] name = "gstreamer-app" version = "0.16.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc80888271338c3ede875d8cafc452eb207476ff5539dcbe0018a8f5b827af0e" dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-sink 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "glib 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)", - "glib-sys 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "gobject-sys 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gstreamer 0.16.5 (registry+https://github.com/rust-lang/crates.io-index)", - "gstreamer-app-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "gstreamer-base 0.16.5 (registry+https://github.com/rust-lang/crates.io-index)", - "gstreamer-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "once_cell 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1", + "futures-core", + "futures-sink", + "glib", + "glib-sys", + "gobject-sys", + "gstreamer", + "gstreamer-app-sys", + "gstreamer-base", + "gstreamer-sys", + "libc", + "once_cell", ] [[package]] name = "gstreamer-app-sys" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "813f64275c9e7b33b828b9efcf9dfa64b95996766d4de996e84363ac65b87e3d" dependencies = [ - "glib-sys 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "gstreamer-base-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "gstreamer-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "system-deps 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "glib-sys", + "gstreamer-base-sys", + "gstreamer-sys", + "libc", + "system-deps", ] [[package]] name = "gstreamer-base" version = "0.16.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bafd01c56f59cb10f4b5a10f97bb4bdf8c2b2784ae5b04da7e2d400cf6e6afcf" dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "glib 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)", - "glib-sys 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "gobject-sys 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gstreamer 0.16.5 (registry+https://github.com/rust-lang/crates.io-index)", - "gstreamer-base-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "gstreamer-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1", + "glib", + "glib-sys", + "gobject-sys", + "gstreamer", + "gstreamer-base-sys", + "gstreamer-sys", + "libc", ] [[package]] name = "gstreamer-base-sys" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4b7b6dc2d6e160a1ae28612f602bd500b3fa474ce90bf6bb2f08072682beef5" dependencies = [ - "glib-sys 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "gobject-sys 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gstreamer-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "system-deps 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "glib-sys", + "gobject-sys", + "gstreamer-sys", + "libc", + "system-deps", ] [[package]] name = "gstreamer-sys" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1f154082d01af5718c5f8a8eb4f565a4ea5586ad8833a8fc2c2aa6844b601d" dependencies = [ - "glib-sys 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "gobject-sys 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "system-deps 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", ] [[package]] -name = "heck" -version = "0.3.1" +name = "h2" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b67e66362108efccd8ac053abafc8b7a8d86a37e6e48fc4f6f7485eb5e9e6a5" dependencies = [ - "unicode-segmentation 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", + "tracing-futures", +] + +[[package]] +name = "hashbrown" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" + +[[package]] +name = "heck" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cbf45460356b7deeb5e3415b5563308c0a9b057c85e12b06ad551f98d0a6ac" +dependencies = [ + "unicode-segmentation", ] [[package]] name = "hermit-abi" -version = "0.1.15" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" dependencies = [ - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", ] -[[package]] -name = "hex" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "hmac" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dcb5e64cda4c23119ab41ba960d1e170a774c8e4b9d9e6a9bc18aabf5e59695" dependencies = [ - "crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crypto-mac", + "digest", ] [[package]] -name = "hostname" -version = "0.3.1" +name = "http" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7245cd7449cc792608c3c8a9eaf69bd4eabbabf802713748fd739c98b82f0747" dependencies = [ - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "match_cfg 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2861bd27ee074e5ee891e8b539837a9430012e249d7f0ca2d795650f579c1994" +dependencies = [ + "bytes", + "http", ] [[package]] name = "httparse" version = "1.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" [[package]] -name = "humantime" -version = "2.0.1" +name = "httpdate" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47" [[package]] name = "hyper" -version = "0.11.27" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12219dc884514cb4a6a03737f4413c0e01c23a1b059b0156004b23f1e19dccbe" dependencies = [ - "base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "mime 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)", - "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "relay 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "unicase 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "want 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "hyper-proxy" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.11.27 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project 1.0.4", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", ] [[package]] name = "ident_case" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" dependencies = [ - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-normalization 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "matches", + "unicode-bidi", + "unicode-normalization", ] [[package]] name = "idna" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" dependencies = [ - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-normalization 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "matches", + "unicode-bidi", + "unicode-normalization", ] [[package]] -name = "if-addrs" -version = "0.6.4" +name = "indexmap" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb1fa934250de4de8aef298d81c729a7d33d8c239daa3a7575e6b92bfc7313b" dependencies = [ - "if-addrs-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "if-addrs-sys" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cc 1.0.65 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg", + "hashbrown", ] [[package]] name = "instant" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" dependencies = [ - "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "iovec" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 1.0.0", ] [[package]] name = "itertools" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" dependencies = [ - "either 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "either", ] [[package]] name = "itoa" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" [[package]] name = "jack" version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c1871c91fa65aa328f3bedbaa54a6e5d1de009264684c153eb708ba933aa6f5" dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "jack-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1", + "jack-sys", + "lazy_static", + "libc", ] [[package]] name = "jack-sys" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d6ab7ada402b6a27912a2b86504be62a48c58313c886fe72a059127acb4d7" dependencies = [ - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "libloading 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static", + "libc", + "libloading", ] [[package]] name = "jni" version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1981310da491a4f0f815238097d0d43d8072732b5ae5f8bd0d8eadf5bf245402" dependencies = [ - "cesu8 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "combine 3.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "error-chain 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "jni-sys 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cesu8", + "combine 3.8.1", + "error-chain", + "jni-sys", + "log", + "walkdir", ] [[package]] name = "jni" version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36bcc950632e48b86da402c5c077590583da5ac0d480103611d5374e7c967a3c" dependencies = [ - "cesu8 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "combine 4.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "error-chain 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "jni-sys 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cesu8", + "combine 4.5.2", + "error-chain", + "jni-sys", + "log", + "walkdir", ] [[package]] name = "jni-sys" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "js-sys" version = "0.3.46" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf3d7383929f7c9c7c2d0fa596f325832df98c3704f2c60553080f7127a58175" dependencies = [ - "wasm-bindgen 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen", ] -[[package]] -name = "kernel32-sys" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "language-tags" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "lazycell" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "lewton" -version = "0.9.4" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "777b48df9aaab155475a83a7df3070395ea1ac6902f5cd062b8f2b028075c030" dependencies = [ - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "ogg 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "ogg", + "tinyvec", ] [[package]] name = "libc" -version = "0.2.73" +version = "0.2.82" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89203f3fba0a3795506acaad8ebce3c80c0af93f994d5a1d7a0b1eeb23271929" [[package]] name = "libflate" -version = "0.1.27" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389de7875e06476365974da3e7ff85d55f1972188ccd9f6020dd7c8156e17914" dependencies = [ - "adler32 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "crc32fast 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rle-decode-fast 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "take_mut 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "adler32", + "crc32fast", + "libflate_lz77", + "rle-decode-fast", ] +[[package]] +name = "libflate_lz77" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3286f09f7d4926fc486334f28d8d2e6ebe4f7f9994494b6dab27ddfad2c9b11b" + [[package]] name = "libloading" -version = "0.4.3" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "351a32417a12d5f7e82c368a66781e307834dae04c6ce0cd4456d52989229883" dependencies = [ - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libloading" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cc 1.0.65 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libmdns" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "hostname 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "if-addrs 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "multimap 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)", - "quick-error 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 1.0.0", + "winapi", ] [[package]] name = "libpulse-binding" -version = "2.19.0" +version = "2.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce89ab17bd343b08bd4321c674ef1477d34f83be18b1ab2ee47a5e5fbee64a91" dependencies = [ - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "libpulse-sys 1.15.3 (registry+https://github.com/rust-lang/crates.io-index)", - "num-derive 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1", + "libc", + "libpulse-sys", + "num-derive", + "num-traits", + "winapi", ] [[package]] name = "libpulse-simple-binding" -version = "2.18.1" +version = "2.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e47f6cda2748fb86f15e5e65cc33be306577140f4b500622b5d98df2ca17240" dependencies = [ - "libpulse-binding 2.19.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libpulse-simple-sys 1.15.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libpulse-sys 1.15.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libpulse-binding", + "libpulse-simple-sys", + "libpulse-sys", ] [[package]] name = "libpulse-simple-sys" -version = "1.15.1" +version = "1.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "468cf582b7b022c0d1b266fefc7fc8fa7b1ddcb61214224f2f105c95a9c2d5c1" dependencies = [ - "libpulse-sys 1.15.3 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", + "libpulse-sys", + "pkg-config", ] [[package]] name = "libpulse-sys" -version = "1.15.3" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fcfb56118765adba111da47e36278b77d00aebf822e4f014a832fbfa183a13b" dependencies = [ - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "num-derive 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "num-derive", + "num-traits", + "pkg-config", + "winapi", ] [[package]] name = "librespot" version = "0.1.3" dependencies = [ - "base64 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", - "hex 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.11.27 (registry+https://github.com/rust-lang/crates.io-index)", - "librespot-audio 0.1.3", - "librespot-connect 0.1.3", - "librespot-core 0.1.3", - "librespot-metadata 0.1.3", - "librespot-playback 0.1.3", - "librespot-protocol 0.1.3", - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "num-bigint 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "protobuf 2.14.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rpassword 5.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sha-1 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-process 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-signal 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "librespot-audio", + "librespot-core", + "librespot-metadata", + "librespot-playback", + "librespot-protocol", ] [[package]] name = "librespot-audio" version = "0.1.3" dependencies = [ - "aes-ctr 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "bit-set 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "lewton 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)", - "librespot-core 0.1.3", - "librespot-tremor 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "num-bigint 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "vorbis 0.0.14 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "librespot-connect" -version = "0.1.3" -dependencies = [ - "aes-ctr 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "base64 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", - "block-modes 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "dns-sd 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.11.27 (registry+https://github.com/rust-lang/crates.io-index)", - "libmdns 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", - "librespot-core 0.1.3", - "librespot-playback 0.1.3", - "librespot-protocol 0.1.3", - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "num-bigint 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "protobuf 2.14.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", - "sha-1 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "aes-ctr", + "bit-set", + "byteorder", + "bytes", + "futures", + "lewton", + "librespot-core", + "librespot-tremor", + "log", + "num-bigint", + "num-traits", + "pin-project-lite", + "tempfile", + "vorbis", ] [[package]] name = "librespot-core" version = "0.1.3" dependencies = [ - "aes 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "base64 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "error-chain 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.11.27 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper-proxy 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "librespot-protocol 0.1.3", - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "num-bigint 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "num-integer 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", - "pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "protobuf 2.14.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", - "sha-1 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", - "shannon 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-codec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "uuid 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "vergen 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "aes", + "base64 0.13.0", + "byteorder", + "bytes", + "futures", + "hmac", + "httparse", + "hyper", + "librespot-protocol", + "log", + "num-bigint", + "num-integer", + "num-traits", + "once_cell", + "pbkdf2", + "pin-project-lite", + "protobuf", + "rand 0.7.3", + "serde", + "serde_derive", + "serde_json", + "sha-1", + "shannon", + "tokio", + "tokio-util", + "url 1.7.2", + "uuid", + "vergen", ] [[package]] name = "librespot-metadata" version = "0.1.3" dependencies = [ - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "librespot-core 0.1.3", - "librespot-protocol 0.1.3", - "linear-map 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "protobuf 2.14.0 (registry+https://github.com/rust-lang/crates.io-index)", + "async-trait", + "byteorder", + "futures", + "librespot-core", + "librespot-protocol", + "linear-map", + "log", + "protobuf", ] [[package]] name = "librespot-playback" version = "0.1.3" dependencies = [ - "alsa 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "cpal 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "glib 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)", - "gstreamer 0.16.5 (registry+https://github.com/rust-lang/crates.io-index)", - "gstreamer-app 0.16.5 (registry+https://github.com/rust-lang/crates.io-index)", - "jack 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "libpulse-binding 2.19.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libpulse-simple-binding 2.18.1 (registry+https://github.com/rust-lang/crates.io-index)", - "librespot-audio 0.1.3", - "librespot-core 0.1.3", - "librespot-metadata 0.1.3", - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "portaudio-rs 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rodio 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sdl2 0.34.3 (registry+https://github.com/rust-lang/crates.io-index)", - "shell-words 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "zerocopy 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "alsa 0.2.2", + "byteorder", + "cpal", + "futures", + "glib", + "gstreamer", + "gstreamer-app", + "jack", + "libc", + "libpulse-binding", + "libpulse-simple-binding", + "librespot-audio", + "librespot-core", + "librespot-metadata", + "log", + "portaudio-rs", + "rodio", + "sdl2", + "shell-words", + "zerocopy", ] [[package]] name = "librespot-protocol" version = "0.1.3" dependencies = [ - "glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "protobuf 2.14.0 (registry+https://github.com/rust-lang/crates.io-index)", - "protobuf-codegen 2.14.0 (registry+https://github.com/rust-lang/crates.io-index)", - "protobuf-codegen-pure 2.14.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glob", + "protobuf", + "protobuf-codegen", + "protobuf-codegen-pure", ] [[package]] name = "librespot-tremor" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b155a7dc4e4d272e01c37a1b85c1ee1bee7f04980ad4a7784c1a6e0f2de5929b" dependencies = [ - "cc 1.0.65 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "ogg-sys 0.0.9 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", + "cc", + "libc", + "ogg-sys", + "pkg-config", ] [[package]] name = "linear-map" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "lock_api" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "bfae20f6b19ad527b550c223fddc3077a547fc70cda94b9b566575423fd303ee" [[package]] name = "lock_api" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312" dependencies = [ - "scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard", ] [[package]] name = "log" -version = "0.3.9" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcf3805d4480bb5b86070dcfeb9e2cb2ebc148adb753c5cca5f884d1d65a42b2" dependencies = [ - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "log" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10", ] [[package]] name = "mach" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" dependencies = [ - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", ] -[[package]] -name = "match_cfg" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "matches" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "maybe-uninit" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" [[package]] name = "memchr" -version = "2.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "memoffset" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "mime" -version = "0.3.16" +version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" [[package]] name = "miniz_oxide" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f2d26ec3309788e423cfbf68ad1800f061638098d76a83681af979dc4eda19d" dependencies = [ - "adler 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "adler", + "autocfg", ] [[package]] name = "mio" -version = "0.6.22" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e50ae3f04d169fcc9bde0b547d1c205219b7157e07ded9c5aff03e0637cb3ed7" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "mio-named-pipes" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.22 (registry+https://github.com/rust-lang/crates.io-index)", - "miow 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "mio-uds" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.22 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "log", + "miow", + "ntapi", + "winapi", ] [[package]] name = "miow" -version = "0.2.1" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a33c1b55807fbed163481b5ba66db4b2fa6cde694a5027be10fb724206c5897" dependencies = [ - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "miow" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "socket2 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "socket2", + "winapi", ] [[package]] name = "muldiv" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "multimap" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "serde 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "0419348c027fa7be448d2ae7ea0e4e04c2334c31dc4e74ab29f00a2a7ca69204" [[package]] name = "ndk" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eb167c1febed0a496639034d0c76b3b74263636045db5489eee52143c246e73" dependencies = [ - "jni-sys 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ndk-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "num_enum 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "thiserror 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)", + "jni-sys", + "ndk-sys", + "num_enum", + "thiserror", ] [[package]] name = "ndk-glue" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdf399b8b7a39c6fb153c4ec32c72fd5fe789df24a647f229c239aa7adb15241" dependencies = [ - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "ndk 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "ndk-macro 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ndk-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static", + "libc", + "log", + "ndk", + "ndk-macro", + "ndk-sys", ] [[package]] name = "ndk-macro" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05d1c6307dc424d0f65b9b06e94f88248e6305726b14729fd67a5e47b2dc481d" dependencies = [ - "darling 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-crate 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)", + "darling", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "ndk-sys" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "net2" -version = "0.2.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "c44922cb3dbb1c70b5e5f443d63b64363a898564d739ba5198e3a9138442868d" [[package]] name = "nix" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2c5afeb0198ec7be8569d666644b574345aad2e95a53baf3a532da3e0f3fb32" dependencies = [ - "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 0.9.1", + "cfg-if 0.1.10", + "libc", + "void", ] [[package]] name = "nix" version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2e0b4f3320ed72aaedb9a5ac838690a8047c7b275da22711fddff4f8a14229" dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.65 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1", + "cc", + "cfg-if 0.1.10", + "libc", + "void", ] [[package]] name = "nom" version = "5.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" dependencies = [ - "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "version_check 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr", + "version_check", +] + +[[package]] +name = "ntapi" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" +dependencies = [ + "winapi", ] [[package]] name = "num-bigint" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e9a41747ae4633fce5adffb4d2e81ffc5e89593cb19917f8fb2cc5ff76507bf" dependencies = [ - "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "num-integer 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg", + "num-integer", + "num-traits", ] [[package]] name = "num-derive" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" dependencies = [ - "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "num-integer" -version = "0.1.43" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" dependencies = [ - "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg", + "num-traits", ] [[package]] name = "num-rational" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" dependencies = [ - "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "num-integer 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg", + "num-integer", + "num-traits", ] [[package]] name = "num-traits" -version = "0.2.12" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" dependencies = [ - "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg", ] [[package]] name = "num_cpus" version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" dependencies = [ - "hermit-abi 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", + "hermit-abi", + "libc", ] [[package]] name = "num_enum" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca565a7df06f3d4b485494f25ba05da1435950f4dc263440eda7a6fa9b8e36e4" dependencies = [ - "derivative 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "num_enum_derive 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "derivative", + "num_enum_derive", ] [[package]] name = "num_enum_derive" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffa5a33ddddfee04c0283a7653987d634e880347e96b5b2ed64de07efb59db9d" dependencies = [ - "proc-macro-crate 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "object" -version = "0.22.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9a7ab5d64814df0fe4a4b5ead45ed6c5f181ee3ff04ba344313a6c80446c5d4" [[package]] name = "oboe" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aadc2b0867bdbb9a81c4d99b9b682958f49dbea1295a81d2f646cca2afdd9fc" dependencies = [ - "jni 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ndk 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "ndk-glue 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "num-derive 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", - "oboe-sys 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "jni 0.14.0", + "ndk", + "ndk-glue", + "num-derive", + "num-traits", + "oboe-sys", ] [[package]] name = "oboe-sys" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ff7a51600eabe34e189eec5c995a62f151d8d97e5fbca39e87ca738bb99b82" dependencies = [ - "fetch_unroll 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "fetch_unroll", ] [[package]] name = "ogg" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6951b4e8bf21c8193da321bcce9c9dd2e13c858fe078bf9054a288b419ae5d6e" dependencies = [ - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", ] [[package]] name = "ogg-sys" version = "0.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a95b8c172e17df1a41bf8d666301d3b2c4efeb90d9d0415e2a4dc0668b35fdb2" dependencies = [ - "gcc 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", + "gcc", + "libc", + "pkg-config", ] [[package]] name = "once_cell" version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0" [[package]] name = "opaque-debug" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" [[package]] -name = "parking_lot" -version = "0.9.0" +name = "opaque-debug" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "lock_api 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "parking_lot" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" dependencies = [ - "instant 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "lock_api 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot_core 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "instant", + "lock_api", + "parking_lot_core", ] [[package]] name = "parking_lot_core" -version = "0.6.2" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ccb628cad4f84851442432c60ad8e1f607e29752d0bf072cbd0baf28aa34272" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.57 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parking_lot_core" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "cloudabi 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "instant 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.57 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 1.0.0", + "instant", + "libc", + "redox_syscall 0.1.57", + "smallvec", + "winapi", ] [[package]] name = "paste" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5d65c4d95931acda4498f675e332fcbdc9a06705cd07086c510e9b6009cd1c1" [[package]] name = "pbkdf2" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "006c038a43a45995a9670da19e67600114740e8511d4333bf97a56e66a7542d9" dependencies = [ - "base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", - "subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "base64 0.9.3", + "byteorder", + "crypto-mac", + "hmac", + "rand 0.5.6", + "sha2", + "subtle", ] [[package]] name = "peeking_take_while" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" [[package]] name = "percent-encoding" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" [[package]] name = "percent-encoding" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" [[package]] name = "pin-project" -version = "0.4.22" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ffbc8e94b38ea3d2d8ba92aea2983b503cd75d0888d75b86bb37970b5698e15" dependencies = [ - "pin-project-internal 0.4.22 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-project-internal 0.4.27", +] + +[[package]] +name = "pin-project" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95b70b68509f17aa2857863b6fa00bf21fc93674c7a8893de2f469f6aa7ca2f2" +dependencies = [ + "pin-project-internal 1.0.4", ] [[package]] name = "pin-project-internal" -version = "0.4.22" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65ad2ae56b6abe3a1ee25f15ee605bacadb9a764edaba9c2bf4103800d4a1895" dependencies = [ - "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caa25a6393f22ce819b0f50e0be89287292fda8d425be38ee0ca14c4931d9e71" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] name = "pin-project-lite" -version = "0.1.11" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439697af366c49a6d0a010c56a0d97685bc140ce0d377b13a2ea2aa42d64a827" [[package]] name = "pin-utils" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.18" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" [[package]] name = "portaudio-rs" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb6b5eff96ccc9bf44d34c379ab03ae944426d83d1694345bdf8159d561d562" dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "portaudio-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1", + "libc", + "portaudio-sys", ] [[package]] name = "portaudio-sys" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5194a4fa953b4ffd851c320ef6f0484cd7278cb7169ea9d6c433e49b23f7b7f5" dependencies = [ - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "pkg-config", ] [[package]] name = "ppv-lite86" -version = "0.2.8" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" [[package]] name = "pretty-hex" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc5c99d529f0d30937f6f4b8a86d988047327bb88d04d2c4afc356de74722131" [[package]] name = "proc-macro-crate" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" dependencies = [ - "toml 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)", + "toml", ] [[package]] name = "proc-macro-error" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ - "proc-macro-error-attr 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)", - "version_check 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", ] [[package]] name = "proc-macro-error-attr" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "version_check 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "version_check", ] [[package]] name = "proc-macro-hack" -version = "0.5.16" +version = "0.5.19" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" [[package]] name = "proc-macro-nested" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" [[package]] name = "proc-macro2" -version = "1.0.19" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" dependencies = [ - "unicode-xid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid", ] [[package]] name = "protobuf" version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e86d370532557ae7573551a1ec8235a0f8d6cb276c7c9e6aa490b511c447485" [[package]] name = "protobuf-codegen" version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de113bba758ccf2c1ef816b127c958001b7831136c9bc3f8e9ec695ac4e82b0c" dependencies = [ - "protobuf 2.14.0 (registry+https://github.com/rust-lang/crates.io-index)", + "protobuf", ] [[package]] name = "protobuf-codegen-pure" version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d1a4febc73bf0cada1d77c459a0c8e5973179f1cfd5b0f1ab789d45b17b6440" dependencies = [ - "protobuf 2.14.0 (registry+https://github.com/rust-lang/crates.io-index)", - "protobuf-codegen 2.14.0 (registry+https://github.com/rust-lang/crates.io-index)", + "protobuf", + "protobuf-codegen", +] + +[[package]] +name = "publicsuffix" +version = "1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bbaa49075179162b49acac1c6aa45fb4dafb5f13cf6794276d77bc7fd95757b" +dependencies = [ + "error-chain", + "idna 0.2.0", + "lazy_static", + "regex", + "url 2.2.0", ] [[package]] name = "qstring" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d464fae65fff2680baf48019211ce37aaec0c78e9264c84a3e484717f965104e" dependencies = [ - "percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "percent-encoding 2.1.0", ] -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "quote" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df" dependencies = [ - "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand" -version = "0.3.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", ] [[package]] name = "rand" version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9" dependencies = [ - "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cloudabi", + "fuchsia-cprng", + "libc", + "rand_core 0.3.1", + "winapi", ] [[package]] name = "rand" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" dependencies = [ - "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc 0.2.0", +] + +[[package]] +name = "rand" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18519b42a40024d661e1714153e9ad0c3de27cd495760ceb09710920f1098b1e" +dependencies = [ + "libc", + "rand_chacha 0.3.0", + "rand_core 0.6.1", + "rand_hc 0.3.0", ] [[package]] name = "rand_chacha" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" dependencies = [ - "ppv-lite86 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.1", ] [[package]] name = "rand_core" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" dependencies = [ - "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.2", ] [[package]] name = "rand_core" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" [[package]] name = "rand_core" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" dependencies = [ - "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c026d7df8b298d90ccbbc5190bd04d85e159eaf5576caeacf8741da93ccbd2e5" +dependencies = [ + "getrandom 0.2.2", ] [[package]] name = "rand_hc" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" dependencies = [ - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1", ] [[package]] -name = "rdrand" -version = "0.4.0" +name = "rand_hc" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" dependencies = [ - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.6.1", ] [[package]] name = "redox_syscall" version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" + +[[package]] +name = "redox_syscall" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05ec8ca9416c5ea37062b502703cd7fcb207736bc294f6e0cf367ac6fc234570" +dependencies = [ + "bitflags 1.2.1", +] [[package]] name = "regex" -version = "1.3.9" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a" dependencies = [ - "regex-syntax 0.6.18 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax", ] [[package]] name = "regex-syntax" -version = "0.6.18" +version = "0.6.22" source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "relay" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581" [[package]] name = "remove_dir_all" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" dependencies = [ - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi", ] [[package]] name = "ring" -version = "0.16.18" +version = "0.16.19" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "024a1e66fea74c66c66624ee5622a7ff0e4b73a13b4f5c326ddb50c708944226" dependencies = [ - "cc 1.0.65 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "once_cell 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "untrusted 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "web-sys 0.3.46 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", ] [[package]] name = "rle-decode-fast" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cabe4fa914dec5870285fa7f71f602645da47c486e68486d2b4ceb4a343e90ac" [[package]] name = "rodio" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9683532495146e98878d4948fa1a1953f584cd923f2a5f5c26b7a8701b56943" dependencies = [ - "cpal 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rpassword" -version = "5.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cpal", ] [[package]] name = "rustc-demangle" version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e3bad0ee36814ca07d7968269dd4b7ec89ec2da10c4bb613928d3077083c232" [[package]] name = "rustc-hash" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustc_version" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" dependencies = [ - "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "semver", ] [[package]] name = "rustls" -version = "0.16.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "064fd21ff87c6e87ed4506e68beb42459caa4a0e2eb144932e6776768556980b" dependencies = [ - "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "ring 0.16.18 (registry+https://github.com/rust-lang/crates.io-index)", - "sct 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "webpki 0.21.3 (registry+https://github.com/rust-lang/crates.io-index)", + "base64 0.13.0", + "log", + "ring", + "sct", + "webpki", ] [[package]] name = "ryu" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" [[package]] name = "safemem" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" [[package]] name = "same-file" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" dependencies = [ - "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-util", ] -[[package]] -name = "scoped-tls" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "scopeguard" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "sct" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3042af939fca8c3453b7af0f1c66e533a15a86169e39de2657310ade8f98d3c" dependencies = [ - "ring 0.16.18 (registry+https://github.com/rust-lang/crates.io-index)", - "untrusted 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ring", + "untrusted", ] [[package]] name = "sdl2" version = "0.34.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcbb85f4211627a7291c83434d6bbfa723e28dcaa53c7606087e3c61929e4b9c" dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "sdl2-sys 0.34.3 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1", + "lazy_static", + "libc", + "sdl2-sys", ] [[package]] name = "sdl2-sys" version = "0.34.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d81feded049b9c14eceb4a4f6d596a98cebbd59abdba949c5552a015466d33" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "version-compare 0.0.10 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10", + "libc", + "version-compare", ] [[package]] name = "semver" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" dependencies = [ - "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "semver-parser", ] [[package]] name = "semver-parser" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.114" +version = "1.0.120" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "166b2349061381baf54a58e4b13c89369feb0ef2eaa57198899e2312aac30aab" +dependencies = [ + "serde_derive", +] [[package]] name = "serde_derive" -version = "1.0.114" +version = "1.0.120" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ca2a8cb5805ce9e3b95435e3765b7b553cecc762d938d409434338386cb5775" dependencies = [ - "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "serde_json" -version = "1.0.56" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fceb2595057b6891a4ee808f70054bd2d12f0e97f1cbb78689b59f676df325a" dependencies = [ - "itoa 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "ryu 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa", + "ryu", + "serde", ] [[package]] name = "sha-1" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" dependencies = [ - "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "block-buffer", + "digest", + "fake-simd", + "opaque-debug 0.2.3", ] +[[package]] +name = "sha1" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" + [[package]] name = "sha2" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69" dependencies = [ - "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "block-buffer", + "digest", + "fake-simd", + "opaque-debug 0.2.3", ] [[package]] name = "shannon" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ea5b41c9427b56caa7b808cb548a04fb50bb5b9e98590b53f28064ff4174561" dependencies = [ - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", ] [[package]] name = "shell-words" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fa3938c99da4914afedd13bf3d79bcb6c277d1b2c398d23257a304d9e1b074" [[package]] name = "shlex" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "signal-hook-registry" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "arc-swap 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "slab" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" [[package]] name = "slab" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" [[package]] name = "smallvec" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "smallvec" -version = "0.6.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "smallvec" -version = "1.5.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" [[package]] name = "socket2" -version = "0.3.12" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.57 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 1.0.0", + "libc", + "winapi", ] [[package]] name = "spin" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "standback" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c66a8cff4fa24853fdf6b51f75c6d7f8206d7c75cab4e467bcd7f25c2b1febe0" +dependencies = [ + "version_check", +] [[package]] name = "stdweb" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef5430c8e36b713e13b48a9f709cc21e046723fe44ce34587b73a830203b533e" [[package]] -name = "stream-cipher" -version = "0.3.2" +name = "stdweb" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" dependencies = [ - "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", + "discard", + "rustc_version", + "stdweb-derive", + "stdweb-internal-macros", + "stdweb-internal-runtime", + "wasm-bindgen", ] +[[package]] +name = "stdweb-derive" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "serde_derive", + "syn", +] + +[[package]] +name = "stdweb-internal-macros" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" +dependencies = [ + "base-x", + "proc-macro2", + "quote", + "serde", + "serde_derive", + "serde_json", + "sha1", + "syn", +] + +[[package]] +name = "stdweb-internal-runtime" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" + [[package]] name = "strsim" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" [[package]] name = "strum" version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57bd81eb48f4c437cadc685403cad539345bf703d78e63707418431cecd4522b" [[package]] name = "strum_macros" version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87c85aa3f8ea653bfd3ddf25f7ee357ee4d204731f6aa9ad04002306f6e2774c" dependencies = [ - "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)", + "heck", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "subtle" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" [[package]] name = "syn" -version = "1.0.35" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc60a3d73ea6594cd712d830cc1f0390fd71542d8c8cd24e70cc54cdfd5e05d5" dependencies = [ - "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "unicode-xid", ] [[package]] name = "synstructure" version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701" dependencies = [ - "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "syn", + "unicode-xid", ] [[package]] name = "system-deps" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f3ecc17269a19353b3558b313bba738b25d82993e30d62a18406a24aba4649b" dependencies = [ - "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", - "strum 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)", - "strum_macros 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)", - "thiserror 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)", - "toml 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)", - "version-compare 0.0.10 (registry+https://github.com/rust-lang/crates.io-index)", + "heck", + "pkg-config", + "strum", + "strum_macros", + "thiserror", + "toml", + "version-compare", ] -[[package]] -name = "take" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "take_mut" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "tar" version = "0.4.30" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "489997b7557e9a43e192c527face4feacc78bfbe6eed67fd55c4c9e381cba290" dependencies = [ - "filetime 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.57 (registry+https://github.com/rust-lang/crates.io-index)", - "xattr 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "filetime", + "libc", + "redox_syscall 0.1.57", + "xattr", ] [[package]] name = "tempfile" -version = "3.1.0" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.57 (registry+https://github.com/rust-lang/crates.io-index)", - "remove_dir_all 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "termcolor" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 1.0.0", + "libc", + "rand 0.8.2", + "redox_syscall 0.2.4", + "remove_dir_all", + "winapi", ] [[package]] name = "thiserror" -version = "1.0.21" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76cc616c6abf8c8928e2fdcc0dbfab37175edd8fb49a4641066ad1364fdab146" dependencies = [ - "thiserror-impl 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)", + "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.21" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9be73a2caec27583d0046ef3796c3794f868a5bc813db689eed00c7631275cd1" dependencies = [ - "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "time" version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" dependencies = [ - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "winapi", +] + +[[package]] +name = "time" +version = "0.2.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "273d3ed44dca264b0d6b3665e8d48fb515042d42466fad93d2a45b90ec4058f7" +dependencies = [ + "const_fn", + "libc", + "standback", + "stdweb 0.4.20", + "time-macros", + "version_check", + "winapi", +] + +[[package]] +name = "time-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "957e9c6e26f12cb6d0dd7fc776bb67a706312e7299aed74c8dd5b17ebb27e2f1" +dependencies = [ + "proc-macro-hack", + "time-macros-impl", +] + +[[package]] +name = "time-macros-impl" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5c3be1edfad6027c69f5491cf4cb310d1a71ecd6af742788c6ff8bced86b8fa" +dependencies = [ + "proc-macro-hack", + "proc-macro2", + "quote", + "standback", + "syn", ] [[package]] name = "tinyvec" -version = "0.3.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf8dbc19eb42fba10e8feaaec282fb50e2c14b2726d6301dbfeed0f73306a6f" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "0.1.22" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8efab2086f17abcddb8f756117665c958feee6b2e39974c2f1600592ab3a4195" dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.22 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-codec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-current-thread 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-fs 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-sync 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-tcp 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-threadpool 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-timer 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-udp 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-uds 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "pin-project-lite", + "tokio-macros", ] [[package]] -name = "tokio-codec" +name = "tokio-macros" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42517d2975ca3114b22a16192634e8241dc5cc1f130be194645970cc1c371494" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-stream" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76066865172052eb8796c686f0b441a93df8b08d40a950b062ffb9a426f00edd" dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core", + "pin-project-lite", + "tokio", ] [[package]] -name = "tokio-core" -version = "0.1.17" +name = "tokio-util" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "feb971a26599ffd28066d387f109746df178eff14d5ea1e235015c5601967a4b" dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.22 (registry+https://github.com/rust-lang/crates.io-index)", - "scoped-tls 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-timer 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-current-thread" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-executor" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-fs" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-threadpool 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-io" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-process" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.22 (registry+https://github.com/rust-lang/crates.io-index)", - "mio-named-pipes 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-signal 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-proto" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.3.23 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "take 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-reactor" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.22 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-sync 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-service" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-signal" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.22 (registry+https://github.com/rust-lang/crates.io-index)", - "mio-uds 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", - "signal-hook-registry 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-sync" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-tcp" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.22 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-threadpool" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-deque 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-queue 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-timer" -version = "0.2.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-udp" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.22 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-codec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-uds" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.22 (registry+https://github.com/rust-lang/crates.io-index)", - "mio-uds 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-codec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "async-stream", + "bytes", + "futures-core", + "futures-sink", + "log", + "pin-project-lite", + "tokio", + "tokio-stream", ] [[package]] name = "toml" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" dependencies = [ - "serde 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)", + "serde", +] + +[[package]] +name = "tower-service" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e987b6bf443f4b5b3b6f38704195592cca41c5bb7aedd3c3693c7081f8289860" + +[[package]] +name = "tracing" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f47026cdc4080c07e49b37087de021820269d996f581aac150ef9e5583eefe3" +dependencies = [ + "cfg-if 1.0.0", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f50de3927f93d202783f4513cda820ab47ef17f624b03c096e86ef00c67e6b5f" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "tracing-futures" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab7bb6f14721aa00656086e9335d363c5c8747bae02ebe32ea2c7dece5689b4c" +dependencies = [ + "pin-project 0.4.27", + "tracing", ] [[package]] name = "try-lock" -version = "0.1.0" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" [[package]] name = "typenum" version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "unicase" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "version_check 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" [[package]] name = "unicode-bidi" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" dependencies = [ - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "matches", ] [[package]] name = "unicode-normalization" -version = "0.1.13" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a13e63ab62dbe32aeee58d1c5408d35c36c392bba5d9d3142287219721afe606" dependencies = [ - "tinyvec 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tinyvec", ] [[package]] name = "unicode-segmentation" version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "unicode-width" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796" [[package]] name = "unicode-xid" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" [[package]] name = "unreachable" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" dependencies = [ - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "void", ] [[package]] name = "untrusted" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "ureq" -version = "0.11.4" +version = "1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "294b85ef5dbc3670a72e82a89971608a1fcc4ed5c7c5a2895230d31a95f0569b" dependencies = [ - "base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "chunked_transfer 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cookie 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "qstring 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rustls 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", - "url 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "webpki 0.21.3 (registry+https://github.com/rust-lang/crates.io-index)", - "webpki-roots 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)", + "base64 0.13.0", + "chunked_transfer", + "cookie", + "cookie_store", + "log", + "once_cell", + "qstring", + "rustls", + "url 2.2.0", + "webpki", + "webpki-roots", ] [[package]] name = "url" version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a" dependencies = [ - "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "idna 0.1.5", + "matches", + "percent-encoding 1.0.1", ] [[package]] name = "url" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5909f2b0817350449ed73e8bcd81c8c3c8d9a7a5d8acba4b27db277f1868976e" dependencies = [ - "form_urlencoded 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "form_urlencoded", + "idna 0.2.0", + "matches", + "percent-encoding 2.1.0", ] [[package]] name = "uuid" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" dependencies = [ - "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "getrandom 0.2.2", ] [[package]] name = "vergen" version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ce50d8996df1f85af15f2cd8d33daae6e479575123ef4314a51a70a230739cb" dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "chrono 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1", + "chrono", ] [[package]] name = "version-compare" version = "0.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d63556a25bae6ea31b52e640d7c41d1ab27faba4ccb600013837a3d0b3994ca1" [[package]] name = "version_check" version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" [[package]] name = "void" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" [[package]] name = "vorbis" version = "0.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e8a194457075360557b82dac78f7ca2d65bbb6679bccfabae5f7c8c706cc776" dependencies = [ - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "ogg-sys 0.0.9 (registry+https://github.com/rust-lang/crates.io-index)", - "vorbis-sys 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "vorbisfile-sys 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "ogg-sys", + "vorbis-sys", + "vorbisfile-sys", ] [[package]] name = "vorbis-sys" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9ed6ef5361a85e68ccc005961d995c2d44e31f0816f142025f2ca2383dfbfd" dependencies = [ - "gcc 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "ogg-sys 0.0.9 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", + "cc", + "libc", + "ogg-sys", + "pkg-config", ] [[package]] name = "vorbisfile-sys" version = "0.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f4306d7e1ac4699b55e20de9483750b90c250913188efd7484db6bfbe9042d1" dependencies = [ - "gcc 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", - "ogg-sys 0.0.9 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", - "vorbis-sys 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gcc", + "libc", + "ogg-sys", + "pkg-config", + "vorbis-sys", ] [[package]] name = "walkdir" version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d" dependencies = [ - "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "same-file", + "winapi", + "winapi-util", ] [[package]] name = "want" -version = "0.0.4" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" dependencies = [ - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "try-lock 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log", + "try-lock", ] [[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.10.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93c6c3420963c5c64bca373b25e77acb562081b9bb4dd5bb864187742186cea9" [[package]] name = "wasm-bindgen" version = "0.2.69" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cd364751395ca0f68cafb17666eee36b63077fb5ecd972bbcd74c90c4bf736e" dependencies = [ - "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-macro 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 1.0.0", + "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" version = "0.2.69" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1114f89ab1f4106e5b55e688b828c0ab0ea593a1ea7c094b141b14cbaaec2d62" dependencies = [ - "bumpalo 3.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-shared 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" version = "0.2.69" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6ac8995ead1f084a8dea1e65f194d0973800c7f571f6edd70adf06ecf77084" dependencies = [ - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-macro-support 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", + "quote", + "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" version = "0.2.69" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5a48c72f299d80557c7c62e37e7225369ecc0c963964059509fbafe917c7549" dependencies = [ - "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-backend 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-shared 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" version = "0.2.69" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e7811dd7f9398f14cc76efd356f98f03aa30419dea46aa810d71e819fc97158" [[package]] name = "web-sys" version = "0.3.46" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "222b1ef9334f92a21d3fb53dc3fd80f30836959a90f9274a626d7e06315ba3c3" dependencies = [ - "js-sys 0.3.46 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", + "js-sys", + "wasm-bindgen", ] [[package]] name = "webpki" -version = "0.21.3" +version = "0.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" dependencies = [ - "ring 0.16.18 (registry+https://github.com/rust-lang/crates.io-index)", - "untrusted 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ring", + "untrusted", ] [[package]] name = "webpki-roots" -version = "0.18.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82015b7e0b8bad8185994674a13a93306bea76cf5a16c5a181382fd3a5ec2376" dependencies = [ - "webpki 0.21.3 (registry+https://github.com/rust-lang/crates.io-index)", + "webpki", ] -[[package]] -name = "winapi" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ - "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", ] -[[package]] -name = "winapi-build" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" dependencies = [ - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi", ] [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "ws2_32-sys" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "xattr" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "244c3741f4240ef46274860397c7c74e50eb23624996930e484c16679633a54c" dependencies = [ - "libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", ] [[package]] name = "zerocopy" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6580539ad917b7c026220c4b3f2c08d52ce54d6ce0dc491e66002e35388fab46" dependencies = [ - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "zerocopy-derive 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "zerocopy-derive", ] [[package]] name = "zerocopy-derive" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d498dbd1fd7beb83c86709ae1c33ca50942889473473d287d56ce4770a18edfb" dependencies = [ - "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)", - "synstructure 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "syn", + "synstructure", ] - -[metadata] -"checksum addr2line 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7c0929d69e78dd9bf5408269919fcbcaeb2e35e5d43e5815517cdc6a8e11a423" -"checksum adler 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e" -"checksum adler32 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" -"checksum aes 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "54eb1d8fe354e5fc611daf4f2ea97dd45a765f4f1e4512306ec183ae2e8f20c9" -"checksum aes-ctr 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d2e5b0458ea3beae0d1d8c0f3946564f8e10f90646cf78c06b4351052058d1ee" -"checksum aes-soft 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cfd7e7ae3f9a1fb5c03b389fc6bb9a51400d0c13053f0dca698c832bfd893a0d" -"checksum aesni 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2f70a6b5f971e473091ab7cfb5ffac6cde81666c4556751d8d5620ead8abf100" -"checksum alsa 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b4a0d4ebc8b23041c5de9bc9aee13b4bad844a589479701f31a5934cfe4aeb32" -"checksum alsa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "eb213f6b3e4b1480a60931ca2035794aa67b73103d254715b1db7b70dcb3c934" -"checksum alsa-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b0edcbbf9ef68f15ae1b620f722180b82a98b6f0628d30baa6b8d2a5abc87d58" -"checksum alsa-sys 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d5a0559bcd3f7a482690d98be41c08a43e92f669b179433e95ddf5e8b8fd36a3" -"checksum anyhow 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)" = "2c0df63cb2955042487fad3aefd2c6e3ae7389ac5dc1beb28921de0b69f779d4" -"checksum arc-swap 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "4d25d88fd6b8041580a654f9d0c581a047baee2b3efee13275f2fc392fc75034" -"checksum ascii 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "eab1c04a571841102f5345a8fc0f6bb3d31c315dec879b5c6e42e40ce7ffa34e" -"checksum atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -"checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" -"checksum backtrace 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)" = "ef5140344c85b01f9bbb4d4b7288a8aa4b3287ccef913a14bcc78a1063623598" -"checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" -"checksum base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" -"checksum base64 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" -"checksum base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643" -"checksum bindgen 0.53.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c72a978d268b1d70b0e963217e60fdabd9523a941457a6c42a7315d15c7e89e5" -"checksum bit-set 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de" -"checksum bit-vec 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5f0dc55f2d8a1a85650ac47858bb001b4c0dd73d79e3c455a842925e68d29cd3" -"checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" -"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" -"checksum block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" -"checksum block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1c924d49bd09e7c06003acda26cd9742e796e34282ec6c1189404dee0c1f4774" -"checksum block-modes 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "31aa8410095e39fdb732909fb5730a48d5bd7c2e3cd76bd1b07b3dbea130c529" -"checksum block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" -"checksum bumpalo 3.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820" -"checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" -"checksum byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" -"checksum bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" -"checksum bytes 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" -"checksum cc 1.0.65 (registry+https://github.com/rust-lang/crates.io-index)" = "95752358c8f7552394baf48cd82695b345628ad3f170d607de3ca03b8dacca15" -"checksum cesu8 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" -"checksum cexpr 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f4aedb84272dbe89af497cf81375129abda4fc0a9e7c5d317498c15cc30c0d27" -"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" -"checksum cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -"checksum chrono 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)" = "c74d84029116787153e02106bf53e66828452a4b325cc8652b788b5967c0a0b6" -"checksum chunked_transfer 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7477065d45a8fe57167bf3cf8bcd3729b54cfcb81cca49bda2d038ea89ae82ca" -"checksum clang-sys 0.29.3 (registry+https://github.com/rust-lang/crates.io-index)" = "fe6837df1d5cba2397b835c8530f51723267e16abbf83892e9e5af4f0e5dd10a" -"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" -"checksum cloudabi 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4344512281c643ae7638bbabc3af17a11307803ec8f0fcad9fae512a8bf36467" -"checksum combine 3.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "da3da6baa321ec19e1cc41d31bf599f00c783d0517095cdaf0332e3fe8d20680" -"checksum combine 4.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b9417a0c314565e2abffaece67e95a8cb51f9238cd39f3764d9dfdf09e72b20c" -"checksum cookie 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "888604f00b3db336d2af898ec3c1d5d0ddf5e6d462220f2ededc33a87ac4bbd5" -"checksum core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b" -"checksum coreaudio-rs 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f229761965dad3e9b11081668a6ea00f1def7aa46062321b5ec245b834f6e491" -"checksum coreaudio-sys 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d6570ee6e089131e928d5ec9236db9e818aa3cf850f48b0eec6ef700571271d4" -"checksum cpal 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05631e2089dfa5d3b6ea1cfbbfd092e2ee5deeb69698911bc976b28b746d3657" -"checksum crc32fast 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" -"checksum crossbeam-deque 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285" -"checksum crossbeam-epoch 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" -"checksum crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b" -"checksum crossbeam-queue 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570" -"checksum crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6" -"checksum crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" -"checksum crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" -"checksum ctr 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "022cd691704491df67d25d006fe8eca083098253c4d43516c2206479c58c6736" -"checksum darling 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858" -"checksum darling_core 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b" -"checksum darling_macro 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72" -"checksum derivative 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cb582b60359da160a9477ee80f15c8d784c477e69c217ef2cdd4169c24ea380f" -"checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" -"checksum dns-sd 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d748509dea20228f63ba519bf142ce2593396386125b01f5b0d6412dab972087" -"checksum either 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" -"checksum env_logger 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f26ecb66b4bdca6c1409b40fb255eefc2bd4f6d135dab3c3124f80ffa2a9661e" -"checksum error-chain 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d371106cc88ffdfb1eabd7111e432da544f16f3e2d7bf1dfe8bf575f1df045cd" -"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" -"checksum fetch_unroll 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b5c55005e95bbe15f5f72a73b6597d0dc82ddc97ffe2ca097a99dcd591fefbca" -"checksum filetime 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "0c122a393ea57648015bf06fbd3d372378992e86b9ff5a7a497b076a28c79efe" -"checksum fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -"checksum form_urlencoded 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ece68d15c92e84fa4f19d3780f1294e5ca82a78a6d515f1efaabcc144688be00" -"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" -"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" -"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" -"checksum futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)" = "1b980f2816d6ee8673b6517b52cb0e808a180efc92e5c19d02cdda79066703ef" -"checksum futures-channel 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f366ad74c28cca6ba456d95e6422883cfb4b252a83bed929c83abfdbbf2967d5" -"checksum futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "59f5fff90fd5d971f936ad674802482ba441b6f09ba5e15fd8b39145582ca399" -"checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" -"checksum futures-executor 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "10d6bb888be1153d3abeb9006b11b02cf5e9b209fda28693c31ae1e4e012e314" -"checksum futures-macro 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d0b5a30a4328ab5473878237c447333c093297bded83a4983d10f4deea240d39" -"checksum futures-sink 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3f2032893cb734c7a05d85ce0cc8b8c4075278e93b24b66f9de99d6eb0fa8acc" -"checksum futures-task 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "bdb66b5f09e22019b1ab0830f7785bcea8e7a42148683f99214f73f8ec21a626" -"checksum futures-util 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8764574ff08b701a084482c3c7031349104b07ac897393010494beaa18ce32c6" -"checksum gcc 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)" = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" -"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" -"checksum getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)" = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" -"checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" -"checksum gimli 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f6503fe142514ca4799d4c26297c4248239fe8838d827db6bd6065c6ed29a6ce" -"checksum glib 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0c685013b7515e668f1b57a165b009d4d28cb139a8a989bbd699c10dad29d0c5" -"checksum glib-macros 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "41486a26d1366a8032b160b59065a59fb528530a46a49f627e7048fb8c064039" -"checksum glib-sys 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c7e9b997a66e9a23d073f2b1abb4dbfc3925e0b8952f67efd8d9b6e168e4cdc1" -"checksum glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" -"checksum gobject-sys 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "952133b60c318a62bf82ee75b93acc7e84028a093e06b9e27981c2b6fe68218c" -"checksum gstreamer 0.16.5 (registry+https://github.com/rust-lang/crates.io-index)" = "5d50f822055923f1cbede233aa5dfd4ee957cf328fb3076e330886094e11d6cf" -"checksum gstreamer-app 0.16.5 (registry+https://github.com/rust-lang/crates.io-index)" = "cc80888271338c3ede875d8cafc452eb207476ff5539dcbe0018a8f5b827af0e" -"checksum gstreamer-app-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "813f64275c9e7b33b828b9efcf9dfa64b95996766d4de996e84363ac65b87e3d" -"checksum gstreamer-base 0.16.5 (registry+https://github.com/rust-lang/crates.io-index)" = "bafd01c56f59cb10f4b5a10f97bb4bdf8c2b2784ae5b04da7e2d400cf6e6afcf" -"checksum gstreamer-base-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a4b7b6dc2d6e160a1ae28612f602bd500b3fa474ce90bf6bb2f08072682beef5" -"checksum gstreamer-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fc1f154082d01af5718c5f8a8eb4f565a4ea5586ad8833a8fc2c2aa6844b601d" -"checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" -"checksum hermit-abi 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "3deed196b6e7f9e44a2ae8d94225d80302d81208b1bb673fd21fe634645c85a9" -"checksum hex 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35" -"checksum hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5dcb5e64cda4c23119ab41ba960d1e170a774c8e4b9d9e6a9bc18aabf5e59695" -"checksum hostname 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" -"checksum httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" -"checksum humantime 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3c1ad908cc71012b7bea4d0c53ba96a8cba9962f048fa68d143376143d863b7a" -"checksum hyper 0.11.27 (registry+https://github.com/rust-lang/crates.io-index)" = "34a590ca09d341e94cddf8e5af0bbccde205d5fbc2fa3c09dd67c7f85cea59d7" -"checksum hyper-proxy 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "44f0925de2747e481e6e477dd212c25e8f745567f02f6182e04d27b97c3fbece" -"checksum ident_case 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" -"checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" -"checksum idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" -"checksum if-addrs 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f12906406f12abf5569643c46b29aec78313dc1537b17dd5c5250169790c4db9" -"checksum if-addrs-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9e2556f16544202bcfe0aa5d20a01a6b815f736b136b3ad76dc547ee6b5bb1df" -"checksum instant 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" -"checksum iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" -"checksum itertools 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" -"checksum itoa 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" -"checksum jack 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "7c1871c91fa65aa328f3bedbaa54a6e5d1de009264684c153eb708ba933aa6f5" -"checksum jack-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0d4ca501477fd3cd93a36df581046e5d6338ed826cf7e9b8d302603521e6cc3" -"checksum jni 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1981310da491a4f0f815238097d0d43d8072732b5ae5f8bd0d8eadf5bf245402" -"checksum jni 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "36bcc950632e48b86da402c5c077590583da5ac0d480103611d5374e7c967a3c" -"checksum jni-sys 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" -"checksum js-sys 0.3.46 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d7383929f7c9c7c2d0fa596f325832df98c3704f2c60553080f7127a58175" -"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" -"checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" -"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -"checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" -"checksum lewton 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8d542c1a317036c45c2aa1cf10cc9d403ca91eb2d333ef1a4917e5cb10628bd0" -"checksum libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)" = "bd7d4bd64732af4bf3a67f367c27df8520ad7e230c5817b8ff485864d80242b9" -"checksum libflate 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)" = "d9135df43b1f5d0e333385cb6e7897ecd1a43d7d11b91ac003f4d2c2d2401fdd" -"checksum libloading 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "fd38073de8f7965d0c17d30546d4bb6da311ab428d1c7a3fc71dff7f9d4979b9" -"checksum libloading 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f2b111a074963af1d37a139918ac6d49ad1d0d5e47f72fd55388619691a7d753" -"checksum libmdns 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "5d8582c174736c53633bc482ac709b24527c018356c3dc6d8e25a788b06b394e" -"checksum libpulse-binding 2.19.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1e8f85a42300c868de4849bb72eda5a65cea08c3ca61396b72c2d7c28a87f055" -"checksum libpulse-simple-binding 2.18.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a047f4502997eed57b3e9d8e71f2b860da91a20bb7e15c65d1f183a7b4fb1226" -"checksum libpulse-simple-sys 1.15.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9b72cb239bc4de6858fa0bbad27419e72cd4466f079ca56f21d94b0a712ab02e" -"checksum libpulse-sys 1.15.3 (registry+https://github.com/rust-lang/crates.io-index)" = "706e95c4b87ebb81c1e7763c74bf7d5ba897208f1a8aa5fc7bea8298dee8f2ca" -"checksum librespot-tremor 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b155a7dc4e4d272e01c37a1b85c1ee1bee7f04980ad4a7784c1a6e0f2de5929b" -"checksum linear-map 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bfae20f6b19ad527b550c223fddc3077a547fc70cda94b9b566575423fd303ee" -"checksum lock_api 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" -"checksum lock_api 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312" -"checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" -"checksum log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" -"checksum mach 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" -"checksum match_cfg 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" -"checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" -"checksum maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" -"checksum memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" -"checksum memoffset 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "c198b026e1bbf08a937e94c6c60f9ec4a2267f5b0d2eec9c1b21b061ce2be55f" -"checksum mime 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)" = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" -"checksum miniz_oxide 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0f2d26ec3309788e423cfbf68ad1800f061638098d76a83681af979dc4eda19d" -"checksum mio 0.6.22 (registry+https://github.com/rust-lang/crates.io-index)" = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430" -"checksum mio-named-pipes 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "0840c1c50fd55e521b247f949c241c9997709f23bd7f023b9762cd561e935656" -"checksum mio-uds 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0" -"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" -"checksum miow 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "07b88fb9795d4d36d62a012dfbf49a8f5cf12751f36d31a9dbe66d528e58979e" -"checksum muldiv 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0419348c027fa7be448d2ae7ea0e4e04c2334c31dc4e74ab29f00a2a7ca69204" -"checksum multimap 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d8883adfde9756c1d30b0f519c9b8c502a94b41ac62f696453c37c7fc0a958ce" -"checksum ndk 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5eb167c1febed0a496639034d0c76b3b74263636045db5489eee52143c246e73" -"checksum ndk-glue 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bdf399b8b7a39c6fb153c4ec32c72fd5fe789df24a647f229c239aa7adb15241" -"checksum ndk-macro 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05d1c6307dc424d0f65b9b06e94f88248e6305726b14729fd67a5e47b2dc481d" -"checksum ndk-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c44922cb3dbb1c70b5e5f443d63b64363a898564d739ba5198e3a9138442868d" -"checksum net2 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)" = "2ba7c918ac76704fb42afcbbb43891e72731f3dcca3bef2a19786297baf14af7" -"checksum nix 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3b2e0b4f3320ed72aaedb9a5ac838690a8047c7b275da22711fddff4f8a14229" -"checksum nix 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a2c5afeb0198ec7be8569d666644b574345aad2e95a53baf3a532da3e0f3fb32" -"checksum nom 5.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" -"checksum num-bigint 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5e9a41747ae4633fce5adffb4d2e81ffc5e89593cb19917f8fb2cc5ff76507bf" -"checksum num-derive 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" -"checksum num-integer 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b" -"checksum num-rational 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" -"checksum num-traits 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)" = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611" -"checksum num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" -"checksum num_enum 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ca565a7df06f3d4b485494f25ba05da1435950f4dc263440eda7a6fa9b8e36e4" -"checksum num_enum_derive 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ffa5a33ddddfee04c0283a7653987d634e880347e96b5b2ed64de07efb59db9d" -"checksum object 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8d3b63360ec3cb337817c2dbd47ab4a0f170d285d8e5a2064600f3def1402397" -"checksum oboe 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1aadc2b0867bdbb9a81c4d99b9b682958f49dbea1295a81d2f646cca2afdd9fc" -"checksum oboe-sys 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "68ff7a51600eabe34e189eec5c995a62f151d8d97e5fbca39e87ca738bb99b82" -"checksum ogg 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d79f1db9148be9d0e174bb3ac890f6030fcb1ed947267c5a91ee4c91b5a91e15" -"checksum ogg-sys 0.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "a95b8c172e17df1a41bf8d666301d3b2c4efeb90d9d0415e2a4dc0668b35fdb2" -"checksum once_cell 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0" -"checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" -"checksum parking_lot 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" -"checksum parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" -"checksum parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" -"checksum parking_lot_core 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c361aa727dd08437f2f1447be8b59a33b0edd15e0fcee698f935613d9efbca9b" -"checksum paste 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c5d65c4d95931acda4498f675e332fcbdc9a06705cd07086c510e9b6009cd1c1" -"checksum pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "006c038a43a45995a9670da19e67600114740e8511d4333bf97a56e66a7542d9" -"checksum peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" -"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" -"checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" -"checksum pin-project 0.4.22 (registry+https://github.com/rust-lang/crates.io-index)" = "12e3a6cdbfe94a5e4572812a0201f8c0ed98c1c452c7b8563ce2276988ef9c17" -"checksum pin-project-internal 0.4.22 (registry+https://github.com/rust-lang/crates.io-index)" = "6a0ffd45cf79d88737d7cc85bfd5d2894bee1139b356e616fe85dc389c61aaf7" -"checksum pin-project-lite 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c917123afa01924fc84bb20c4c03f004d9c38e5127e3c039bbf7f4b9c76a2f6b" -"checksum pin-utils 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" -"checksum pkg-config 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)" = "d36492546b6af1463394d46f0c834346f31548646f6ba10849802c9c9a27ac33" -"checksum portaudio-rs 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "cdb6b5eff96ccc9bf44d34c379ab03ae944426d83d1694345bdf8159d561d562" -"checksum portaudio-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5194a4fa953b4ffd851c320ef6f0484cd7278cb7169ea9d6c433e49b23f7b7f5" -"checksum ppv-lite86 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea" -"checksum pretty-hex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5c99d529f0d30937f6f4b8a86d988047327bb88d04d2c4afc356de74722131" -"checksum proc-macro-crate 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" -"checksum proc-macro-error 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -"checksum proc-macro-error-attr 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -"checksum proc-macro-hack 0.5.16 (registry+https://github.com/rust-lang/crates.io-index)" = "7e0456befd48169b9f13ef0f0ad46d492cf9d2dbb918bcf38e01eed4ce3ec5e4" -"checksum proc-macro-nested 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "eba180dafb9038b050a4c280019bbedf9f2467b61e5d892dcad585bb57aadc5a" -"checksum proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)" = "04f5f085b5d71e2188cb8271e5da0161ad52c3f227a661a3c135fdf28e258b12" -"checksum protobuf 2.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8e86d370532557ae7573551a1ec8235a0f8d6cb276c7c9e6aa490b511c447485" -"checksum protobuf-codegen 2.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de113bba758ccf2c1ef816b127c958001b7831136c9bc3f8e9ec695ac4e82b0c" -"checksum protobuf-codegen-pure 2.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2d1a4febc73bf0cada1d77c459a0c8e5973179f1cfd5b0f1ab789d45b17b6440" -"checksum qstring 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d464fae65fff2680baf48019211ce37aaec0c78e9264c84a3e484717f965104e" -"checksum quick-error 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" -"checksum quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" -"checksum rand 0.3.23 (registry+https://github.com/rust-lang/crates.io-index)" = "64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c" -"checksum rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" -"checksum rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9" -"checksum rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -"checksum rand_chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" -"checksum rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" -"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -"checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" -"checksum redox_syscall 0.1.57 (registry+https://github.com/rust-lang/crates.io-index)" = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" -"checksum regex 1.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "9c3780fcf44b193bc4d09f36d2a3c87b251da4a046c87795a0d35f4f927ad8e6" -"checksum regex-syntax 0.6.18 (registry+https://github.com/rust-lang/crates.io-index)" = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8" -"checksum relay 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1576e382688d7e9deecea24417e350d3062d97e32e45d70b1cde65994ff1489a" -"checksum remove_dir_all 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -"checksum ring 0.16.18 (registry+https://github.com/rust-lang/crates.io-index)" = "70017ed5c555d79ee3538fc63ca09c70ad8f317dcadc1adc2c496b60c22bb24f" -"checksum rle-decode-fast 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cabe4fa914dec5870285fa7f71f602645da47c486e68486d2b4ceb4a343e90ac" -"checksum rodio 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c9683532495146e98878d4948fa1a1953f584cd923f2a5f5c26b7a8701b56943" -"checksum rpassword 5.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d755237fc0f99d98641540e66abac8bc46a0652f19148ac9e21de2da06b326c9" -"checksum rustc-demangle 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "6e3bad0ee36814ca07d7968269dd4b7ec89ec2da10c4bb613928d3077083c232" -"checksum rustc-hash 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" -"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -"checksum rustls 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b25a18b1bf7387f0145e7f8324e700805aade3842dd3db2e74e4cdeb4677c09e" -"checksum ryu 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" -"checksum safemem 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" -"checksum same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -"checksum scoped-tls 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "332ffa32bf586782a3efaeb58f127980944bbc8c4d6913a86107ac2a5ab24b28" -"checksum scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" -"checksum sct 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e3042af939fca8c3453b7af0f1c66e533a15a86169e39de2657310ade8f98d3c" -"checksum sdl2 0.34.3 (registry+https://github.com/rust-lang/crates.io-index)" = "fcbb85f4211627a7291c83434d6bbfa723e28dcaa53c7606087e3c61929e4b9c" -"checksum sdl2-sys 0.34.3 (registry+https://github.com/rust-lang/crates.io-index)" = "28d81feded049b9c14eceb4a4f6d596a98cebbd59abdba949c5552a015466d33" -"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" -"checksum serde 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)" = "5317f7588f0a5078ee60ef675ef96735a1442132dc645eb1d12c018620ed8cd3" -"checksum serde_derive 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)" = "2a0be94b04690fbaed37cddffc5c134bf537c8e3329d53e982fe04c374978f8e" -"checksum serde_json 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)" = "3433e879a558dde8b5e8feb2a04899cf34fdde1fafb894687e52105fc1162ac3" -"checksum sha-1 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" -"checksum sha2 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69" -"checksum shannon 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7ea5b41c9427b56caa7b808cb548a04fb50bb5b9e98590b53f28064ff4174561" -"checksum shell-words 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b6fa3938c99da4914afedd13bf3d79bcb6c277d1b2c398d23257a304d9e1b074" -"checksum shlex 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" -"checksum signal-hook-registry 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94f478ede9f64724c5d173d7bb56099ec3e2d9fc2774aac65d34b8b890405f41" -"checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23" -"checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" -"checksum smallvec 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4c8cbcd6df1e117c2210e13ab5109635ad68a929fcbb8964dc965b76cb5ee013" -"checksum smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "f7b0758c52e15a8b5e3691eae6cc559f08eee9406e548a4477ba4e67770a82b6" -"checksum smallvec 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7acad6f34eb9e8a259d3283d1e8c1d34d7415943d4895f65cc73813c7396fc85" -"checksum socket2 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)" = "03088793f677dce356f3ccc2edb1b314ad191ab702a5de3faf49304f7e104918" -"checksum spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" -"checksum stdweb 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ef5430c8e36b713e13b48a9f709cc21e046723fe44ce34587b73a830203b533e" -"checksum stream-cipher 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8131256a5896cabcf5eb04f4d6dacbe1aefda854b0d9896e09cb58829ec5638c" -"checksum strsim 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" -"checksum strum 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)" = "57bd81eb48f4c437cadc685403cad539345bf703d78e63707418431cecd4522b" -"checksum strum_macros 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)" = "87c85aa3f8ea653bfd3ddf25f7ee357ee4d204731f6aa9ad04002306f6e2774c" -"checksum subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" -"checksum syn 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)" = "fb7f4c519df8c117855e19dd8cc851e89eb746fe7a73f0157e0d95fdec5369b0" -"checksum synstructure 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701" -"checksum system-deps 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0f3ecc17269a19353b3558b313bba738b25d82993e30d62a18406a24aba4649b" -"checksum take 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b157868d8ac1f56b64604539990685fa7611d8fa9e5476cf0c02cf34d32917c5" -"checksum take_mut 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" -"checksum tar 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "489997b7557e9a43e192c527face4feacc78bfbe6eed67fd55c4c9e381cba290" -"checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" -"checksum termcolor 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f" -"checksum thiserror 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)" = "318234ffa22e0920fe9a40d7b8369b5f649d490980cf7aadcf1eb91594869b42" -"checksum thiserror-impl 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)" = "cae2447b6282786c3493999f40a9be2a6ad20cb8bd268b0a0dbf5a065535c0ab" -"checksum time 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" -"checksum tinyvec 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "53953d2d3a5ad81d9f844a32f14ebb121f50b650cd59d0ee2a07cf13c617efed" -"checksum tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)" = "5a09c0b5bb588872ab2f09afa13ee6e9dac11e10a0ec9e8e3ba39a5a5d530af6" -"checksum tokio-codec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "25b2998660ba0e70d18684de5d06b70b70a3a747469af9dea7618cc59e75976b" -"checksum tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "aeeffbbb94209023feaef3c196a41cbcdafa06b4a6f893f68779bb5e53796f71" -"checksum tokio-current-thread 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "b1de0e32a83f131e002238d7ccde18211c0a5397f60cbfffcb112868c2e0e20e" -"checksum tokio-executor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "fb2d1b8f4548dbf5e1f7818512e9c406860678f29c300cdf0ebac72d1a3a1671" -"checksum tokio-fs 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "297a1206e0ca6302a0eed35b700d292b275256f596e2f3fea7729d5e629b6ff4" -"checksum tokio-io 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "57fc868aae093479e3131e3d165c93b1c7474109d13c90ec0dda2a1bbfff0674" -"checksum tokio-process 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "382d90f43fa31caebe5d3bc6cfd854963394fff3b8cb59d5146607aaae7e7e43" -"checksum tokio-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8fbb47ae81353c63c487030659494b295f6cb6576242f907f203473b191b0389" -"checksum tokio-reactor 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "09bc590ec4ba8ba87652da2068d150dcada2cfa2e07faae270a5e0409aa51351" -"checksum tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "24da22d077e0f15f55162bdbdc661228c1581892f52074fb242678d015b45162" -"checksum tokio-signal 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)" = "d0c34c6e548f101053321cba3da7cbb87a610b85555884c41b07da2eb91aff12" -"checksum tokio-sync 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "edfe50152bc8164fcc456dab7891fa9bf8beaf01c5ee7e1dd43a397c3cf87dee" -"checksum tokio-tcp 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "98df18ed66e3b72e742f185882a9e201892407957e45fbff8da17ae7a7c51f72" -"checksum tokio-threadpool 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "df720b6581784c118f0eb4310796b12b1d242a7eb95f716a8367855325c25f89" -"checksum tokio-timer 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "93044f2d313c95ff1cb7809ce9a7a05735b012288a888b62d4434fd58c94f296" -"checksum tokio-udp 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "e2a0b10e610b39c38b031a2fcab08e4b82f16ece36504988dcbd81dbba650d82" -"checksum tokio-uds 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "ab57a4ac4111c8c9dbcf70779f6fc8bc35ae4b2454809febac840ad19bd7e4e0" -"checksum toml 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)" = "75cf45bb0bef80604d001caaec0d09da99611b3c0fd39d3080468875cdb65645" -"checksum try-lock 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee2aa4715743892880f70885373966c83d73ef1b0838a664ef0c76fffd35e7c2" -"checksum typenum 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" -"checksum unicase 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" -"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" -"checksum unicode-normalization 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6fb19cf769fa8c6a80a162df694621ebeb4dafb606470b2b2fce0be40a98a977" -"checksum unicode-segmentation 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796" -"checksum unicode-width 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" -"checksum unicode-xid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" -"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" -"checksum untrusted 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" -"checksum ureq 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)" = "801125e6d1ba6864cf3a5a92cfb2f0b0a3ee73e40602a0cd206ad2f3c040aa96" -"checksum url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a" -"checksum url 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5909f2b0817350449ed73e8bcd81c8c3c8d9a7a5d8acba4b27db277f1868976e" -"checksum uuid 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9fde2f6a4bea1d6e007c4ad38c6839fa71cbb63b6dbf5b595aa38dc9b1093c11" -"checksum vergen 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ce50d8996df1f85af15f2cd8d33daae6e479575123ef4314a51a70a230739cb" -"checksum version-compare 0.0.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d63556a25bae6ea31b52e640d7c41d1ab27faba4ccb600013837a3d0b3994ca1" -"checksum version_check 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" -"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" -"checksum vorbis 0.0.14 (registry+https://github.com/rust-lang/crates.io-index)" = "5e8a194457075360557b82dac78f7ca2d65bbb6679bccfabae5f7c8c706cc776" -"checksum vorbis-sys 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3a0a8d7034313748da1d84b0adfa501f83f9ec83250f37fbacfa92a3580327c4" -"checksum vorbisfile-sys 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "4f4306d7e1ac4699b55e20de9483750b90c250913188efd7484db6bfbe9042d1" -"checksum walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d" -"checksum want 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a05d9d966753fa4b5c8db73fcab5eed4549cfe0e1e4e66911e5564a0085c35d1" -"checksum wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" -"checksum wasm-bindgen 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)" = "3cd364751395ca0f68cafb17666eee36b63077fb5ecd972bbcd74c90c4bf736e" -"checksum wasm-bindgen-backend 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)" = "1114f89ab1f4106e5b55e688b828c0ab0ea593a1ea7c094b141b14cbaaec2d62" -"checksum wasm-bindgen-macro 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6ac8995ead1f084a8dea1e65f194d0973800c7f571f6edd70adf06ecf77084" -"checksum wasm-bindgen-macro-support 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)" = "b5a48c72f299d80557c7c62e37e7225369ecc0c963964059509fbafe917c7549" -"checksum wasm-bindgen-shared 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)" = "7e7811dd7f9398f14cc76efd356f98f03aa30419dea46aa810d71e819fc97158" -"checksum web-sys 0.3.46 (registry+https://github.com/rust-lang/crates.io-index)" = "222b1ef9334f92a21d3fb53dc3fd80f30836959a90f9274a626d7e06315ba3c3" -"checksum webpki 0.21.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ab146130f5f790d45f82aeeb09e55a256573373ec64409fc19a6fb82fb1032ae" -"checksum webpki-roots 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)" = "91cd5736df7f12a964a5067a12c62fa38e1bd8080aff1f80bc29be7c80d19ab4" -"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" -"checksum winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" -"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -"checksum winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" -"checksum xattr 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "244c3741f4240ef46274860397c7c74e50eb23624996930e484c16679633a54c" -"checksum zerocopy 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6580539ad917b7c026220c4b3f2c08d52ce54d6ce0dc491e66002e35388fab46" -"checksum zerocopy-derive 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d498dbd1fd7beb83c86709ae1c33ca50942889473473d287d56ce4770a18edfb" diff --git a/Cargo.toml b/Cargo.toml index 6405ca8..a7ef8ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,49 +15,35 @@ edition = "2018" name = "librespot" path = "src/lib.rs" -[[bin]] -name = "librespot" -path = "src/main.rs" -doc = false +# [[bin]] +# name = "librespot" +# path = "src/main.rs" +# doc = false [dependencies.librespot-audio] path = "audio" version = "0.1.3" -[dependencies.librespot-connect] -path = "connect" -version = "0.1.3" + +# [dependencies.librespot-connect] +# path = "connect" +# version = "0.1.3" + [dependencies.librespot-core] path = "core" version = "0.1.3" + [dependencies.librespot-metadata] path = "metadata" version = "0.1.3" + [dependencies.librespot-playback] path = "playback" version = "0.1.3" + [dependencies.librespot-protocol] path = "protocol" version = "0.1.3" -[dependencies] -base64 = "0.13" -env_logger = {version = "0.8", default-features = false, features = ["termcolor","humantime","atty"]} -futures = "0.1" -getopts = "0.2" -hyper = "0.11" -log = "0.4" -num-bigint = "0.3" -protobuf = "~2.14.0" -rand = "0.7" -rpassword = "5.0" -tokio-core = "0.1" -tokio-io = "0.1" -tokio-process = "0.2" -tokio-signal = "0.2" -url = "1.7" -sha-1 = "0.8" -hex = "0.4" - [features] alsa-backend = ["librespot-playback/alsa-backend"] portaudio-backend = ["librespot-playback/portaudio-backend"] @@ -70,7 +56,7 @@ gstreamer-backend = ["librespot-playback/gstreamer-backend"] with-tremor = ["librespot-audio/with-tremor"] with-vorbis = ["librespot-audio/with-vorbis"] -with-dns-sd = ["librespot-connect/with-dns-sd"] +# with-dns-sd = ["librespot-connect/with-dns-sd"] default = ["librespot-playback/rodio-backend"] diff --git a/src/lib.rs b/src/lib.rs index 31bac34..4304e18 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,4 @@ #![crate_name = "librespot"] -#![cfg_attr(feature = "cargo-clippy", allow(unused_io_amount))] pub extern crate librespot_audio as audio; // pub extern crate librespot_connect as connect; diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index 4f80657..0000000 --- a/src/main.rs +++ /dev/null @@ -1,620 +0,0 @@ -use futures::sync::mpsc::UnboundedReceiver; -use futures::{Async, Future, Poll, Stream}; -use log::{error, info, trace, warn}; -use sha1::{Digest, Sha1}; -use std::env; -use std::io::{self, stderr, Write}; -use std::mem; -use std::path::PathBuf; -use std::process::exit; -use std::str::FromStr; -use std::time::Instant; -use tokio_core::reactor::{Core, Handle}; -use tokio_io::IoStream; -use url::Url; - -use librespot::core::authentication::{get_credentials, Credentials}; -use librespot::core::cache::Cache; -use librespot::core::config::{ConnectConfig, DeviceType, SessionConfig, VolumeCtrl}; -use librespot::core::session::Session; -use librespot::core::version; - -use librespot::connect::discovery::{discovery, DiscoveryStream}; -use librespot::connect::spirc::{Spirc, SpircTask}; -use librespot::playback::audio_backend::{self, Sink, BACKENDS}; -use librespot::playback::config::{Bitrate, PlayerConfig}; -use librespot::playback::mixer::{self, Mixer, MixerConfig}; -use librespot::playback::player::{Player, PlayerEvent}; - -mod player_event_handler; -use crate::player_event_handler::{emit_sink_event, run_program_on_events}; - -fn device_id(name: &str) -> String { - hex::encode(Sha1::digest(name.as_bytes())) -} - -fn usage(program: &str, opts: &getopts::Options) -> String { - let brief = format!("Usage: {} [options]", program); - opts.usage(&brief) -} - -fn setup_logging(verbose: bool) { - let mut builder = env_logger::Builder::new(); - match env::var("RUST_LOG") { - Ok(config) => { - builder.parse_filters(&config); - builder.init(); - - if verbose { - warn!("`--verbose` flag overidden by `RUST_LOG` environment variable"); - } - } - Err(_) => { - if verbose { - builder.parse_filters("libmdns=info,librespot=trace"); - } else { - builder.parse_filters("libmdns=info,librespot=info"); - } - builder.init(); - } - } -} - -fn list_backends() { - println!("Available Backends : "); - for (&(name, _), idx) in BACKENDS.iter().zip(0..) { - if idx == 0 { - println!("- {} (default)", name); - } else { - println!("- {}", name); - } - } -} - -#[derive(Clone)] -struct Setup { - backend: fn(Option) -> Box, - device: Option, - - mixer: fn(Option) -> Box, - - cache: Option, - player_config: PlayerConfig, - session_config: SessionConfig, - connect_config: ConnectConfig, - mixer_config: MixerConfig, - credentials: Option, - enable_discovery: bool, - zeroconf_port: u16, - player_event_program: Option, - emit_sink_events: bool, -} - -fn setup(args: &[String]) -> Setup { - let mut opts = getopts::Options::new(); - opts.optopt( - "c", - "cache", - "Path to a directory where files will be cached.", - "CACHE", - ).optopt( - "", - "system-cache", - "Path to a directory where system files (credentials, volume) will be cached. Can be different from cache option value", - "SYTEMCACHE", - ).optflag("", "disable-audio-cache", "Disable caching of the audio data.") - .reqopt("n", "name", "Device name", "NAME") - .optopt("", "device-type", "Displayed device type", "DEVICE_TYPE") - .optopt( - "b", - "bitrate", - "Bitrate (96, 160 or 320). Defaults to 160", - "BITRATE", - ) - .optopt( - "", - "onevent", - "Run PROGRAM when playback is about to begin.", - "PROGRAM", - ) - .optflag("", "emit-sink-events", "Run program set by --onevent before sink is opened and after it is closed.") - .optflag("v", "verbose", "Enable verbose output") - .optopt("u", "username", "Username to sign in with", "USERNAME") - .optopt("p", "password", "Password", "PASSWORD") - .optopt("", "proxy", "HTTP proxy to use when connecting", "PROXY") - .optopt("", "ap-port", "Connect to AP with specified port. If no AP with that port are present fallback AP will be used. Available ports are usually 80, 443 and 4070", "AP_PORT") - .optflag("", "disable-discovery", "Disable discovery mode") - .optopt( - "", - "backend", - "Audio backend to use. Use '?' to list options", - "BACKEND", - ) - .optopt( - "", - "device", - "Audio device to use. Use '?' to list options if using portaudio or alsa", - "DEVICE", - ) - .optopt("", "mixer", "Mixer to use (alsa or softvol)", "MIXER") - .optopt( - "m", - "mixer-name", - "Alsa mixer name, e.g \"PCM\" or \"Master\". Defaults to 'PCM'", - "MIXER_NAME", - ) - .optopt( - "", - "mixer-card", - "Alsa mixer card, e.g \"hw:0\" or similar from `aplay -l`. Defaults to 'default' ", - "MIXER_CARD", - ) - .optopt( - "", - "mixer-index", - "Alsa mixer index, Index of the cards mixer. Defaults to 0", - "MIXER_INDEX", - ) - .optflag( - "", - "mixer-linear-volume", - "Disable alsa's mapped volume scale (cubic). Default false", - ) - .optopt( - "", - "initial-volume", - "Initial volume in %, once connected (must be from 0 to 100)", - "VOLUME", - ) - .optopt( - "", - "zeroconf-port", - "The port the internal server advertised over zeroconf uses.", - "ZEROCONF_PORT", - ) - .optflag( - "", - "enable-volume-normalisation", - "Play all tracks at the same volume", - ) - .optopt( - "", - "normalisation-pregain", - "Pregain (dB) applied by volume normalisation", - "PREGAIN", - ) - .optopt( - "", - "volume-ctrl", - "Volume control type - [linear, log, fixed]. Default is logarithmic", - "VOLUME_CTRL" - ) - .optflag( - "", - "autoplay", - "autoplay similar songs when your music ends.", - ) - .optflag( - "", - "disable-gapless", - "disable gapless playback.", - ); - - let matches = match opts.parse(&args[1..]) { - Ok(m) => m, - Err(f) => { - writeln!( - stderr(), - "error: {}\n{}", - f.to_string(), - usage(&args[0], &opts) - ) - .unwrap(); - exit(1); - } - }; - - let verbose = matches.opt_present("verbose"); - setup_logging(verbose); - - info!( - "librespot {} ({}). Built on {}. Build ID: {}", - version::short_sha(), - version::commit_date(), - version::short_now(), - version::build_id() - ); - - let backend_name = matches.opt_str("backend"); - if backend_name == Some("?".into()) { - list_backends(); - exit(0); - } - - let backend = audio_backend::find(backend_name).expect("Invalid backend"); - - let device = matches.opt_str("device"); - if device == Some("?".into()) { - backend(device); - exit(0); - } - - let mixer_name = matches.opt_str("mixer"); - let mixer = mixer::find(mixer_name.as_ref()).expect("Invalid mixer"); - - let mixer_config = MixerConfig { - card: matches - .opt_str("mixer-card") - .unwrap_or(String::from("default")), - mixer: matches.opt_str("mixer-name").unwrap_or(String::from("PCM")), - index: matches - .opt_str("mixer-index") - .map(|index| index.parse::().unwrap()) - .unwrap_or(0), - mapped_volume: !matches.opt_present("mixer-linear-volume"), - }; - - let cache = matches.opt_str("c").map(|cache_path| { - let use_audio_cache = !matches.opt_present("disable-audio-cache"); - let system_cache_directory = matches - .opt_str("system-cache") - .unwrap_or(String::from(cache_path.clone())); - - Cache::new( - PathBuf::from(cache_path), - PathBuf::from(system_cache_directory), - use_audio_cache, - ) - }); - - let initial_volume = matches - .opt_str("initial-volume") - .map(|volume| { - let volume = volume.parse::().unwrap(); - if volume > 100 { - panic!("Initial volume must be in the range 0-100"); - } - (volume as i32 * 0xFFFF / 100) as u16 - }) - .or_else(|| cache.as_ref().and_then(Cache::volume)) - .unwrap_or(0x8000); - - let zeroconf_port = matches - .opt_str("zeroconf-port") - .map(|port| port.parse::().unwrap()) - .unwrap_or(0); - - let name = matches.opt_str("name").unwrap(); - - let credentials = { - let cached_credentials = cache.as_ref().and_then(Cache::credentials); - - let password = |username: &String| -> String { - write!(stderr(), "Password for {}: ", username).unwrap(); - stderr().flush().unwrap(); - rpassword::read_password().unwrap() - }; - - get_credentials( - matches.opt_str("username"), - matches.opt_str("password"), - cached_credentials, - password, - ) - }; - - let session_config = { - let device_id = device_id(&name); - - SessionConfig { - user_agent: version::version_string(), - device_id: device_id, - proxy: matches.opt_str("proxy").or(std::env::var("http_proxy").ok()).map( - |s| { - match Url::parse(&s) { - Ok(url) => { - if url.host().is_none() || url.port_or_known_default().is_none() { - panic!("Invalid proxy url, only urls on the format \"http://host:port\" are allowed"); - } - - if url.scheme() != "http" { - panic!("Only unsecure http:// proxies are supported"); - } - url - }, - Err(err) => panic!("Invalid proxy url: {}, only urls on the format \"http://host:port\" are allowed", err) - } - }, - ), - ap_port: matches - .opt_str("ap-port") - .map(|port| port.parse::().expect("Invalid port")), - } - }; - - let player_config = { - let bitrate = matches - .opt_str("b") - .as_ref() - .map(|bitrate| Bitrate::from_str(bitrate).expect("Invalid bitrate")) - .unwrap_or(Bitrate::default()); - PlayerConfig { - bitrate: bitrate, - gapless: !matches.opt_present("disable-gapless"), - normalisation: matches.opt_present("enable-volume-normalisation"), - normalisation_pregain: matches - .opt_str("normalisation-pregain") - .map(|pregain| pregain.parse::().expect("Invalid pregain float value")) - .unwrap_or(PlayerConfig::default().normalisation_pregain), - } - }; - - let connect_config = { - let device_type = matches - .opt_str("device-type") - .as_ref() - .map(|device_type| DeviceType::from_str(device_type).expect("Invalid device type")) - .unwrap_or(DeviceType::default()); - - let volume_ctrl = matches - .opt_str("volume-ctrl") - .as_ref() - .map(|volume_ctrl| VolumeCtrl::from_str(volume_ctrl).expect("Invalid volume ctrl type")) - .unwrap_or(VolumeCtrl::default()); - - ConnectConfig { - name: name, - device_type: device_type, - volume: initial_volume, - volume_ctrl: volume_ctrl, - autoplay: matches.opt_present("autoplay"), - } - }; - - let enable_discovery = !matches.opt_present("disable-discovery"); - - Setup { - backend: backend, - cache: cache, - session_config: session_config, - player_config: player_config, - connect_config: connect_config, - credentials: credentials, - device: device, - enable_discovery: enable_discovery, - zeroconf_port: zeroconf_port, - mixer: mixer, - mixer_config: mixer_config, - player_event_program: matches.opt_str("onevent"), - emit_sink_events: matches.opt_present("emit-sink-events"), - } -} - -struct Main { - cache: Option, - player_config: PlayerConfig, - session_config: SessionConfig, - connect_config: ConnectConfig, - backend: fn(Option) -> Box, - device: Option, - mixer: fn(Option) -> Box, - mixer_config: MixerConfig, - handle: Handle, - - discovery: Option, - signal: IoStream<()>, - - spirc: Option, - spirc_task: Option, - connect: Box>, - - shutdown: bool, - last_credentials: Option, - auto_connect_times: Vec, - - player_event_channel: Option>, - player_event_program: Option, - emit_sink_events: bool, -} - -impl Main { - fn new(handle: Handle, setup: Setup) -> Main { - let mut task = Main { - handle: handle.clone(), - cache: setup.cache, - session_config: setup.session_config, - player_config: setup.player_config, - connect_config: setup.connect_config, - backend: setup.backend, - device: setup.device, - mixer: setup.mixer, - mixer_config: setup.mixer_config, - - connect: Box::new(futures::future::empty()), - discovery: None, - spirc: None, - spirc_task: None, - shutdown: false, - last_credentials: None, - auto_connect_times: Vec::new(), - signal: Box::new(tokio_signal::ctrl_c().flatten_stream()), - - player_event_channel: None, - player_event_program: setup.player_event_program, - emit_sink_events: setup.emit_sink_events, - }; - - if setup.enable_discovery { - let config = task.connect_config.clone(); - let device_id = task.session_config.device_id.clone(); - - task.discovery = - Some(discovery(&handle, config, device_id, setup.zeroconf_port).unwrap()); - } - - if let Some(credentials) = setup.credentials { - task.credentials(credentials); - } - - task - } - - fn credentials(&mut self, credentials: Credentials) { - self.last_credentials = Some(credentials.clone()); - let config = self.session_config.clone(); - let handle = self.handle.clone(); - - let connection = Session::connect(config, credentials, self.cache.clone(), handle); - - self.connect = connection; - self.spirc = None; - let task = mem::replace(&mut self.spirc_task, None); - if let Some(task) = task { - self.handle.spawn(task); - } - } -} - -impl Future for Main { - type Item = (); - type Error = (); - - fn poll(&mut self) -> Poll<(), ()> { - loop { - let mut progress = false; - - if let Some(Async::Ready(Some(creds))) = - self.discovery.as_mut().map(|d| d.poll().unwrap()) - { - if let Some(ref spirc) = self.spirc { - spirc.shutdown(); - } - self.auto_connect_times.clear(); - self.credentials(creds); - - progress = true; - } - - match self.connect.poll() { - Ok(Async::Ready(session)) => { - self.connect = Box::new(futures::future::empty()); - let mixer_config = self.mixer_config.clone(); - let mixer = (self.mixer)(Some(mixer_config)); - let player_config = self.player_config.clone(); - let connect_config = self.connect_config.clone(); - - let audio_filter = mixer.get_audio_filter(); - let backend = self.backend; - let device = self.device.clone(); - let (player, event_channel) = - Player::new(player_config, session.clone(), audio_filter, move || { - (backend)(device) - }); - - if self.emit_sink_events { - if let Some(player_event_program) = &self.player_event_program { - let player_event_program = player_event_program.clone(); - player.set_sink_event_callback(Some(Box::new(move |sink_status| { - emit_sink_event(sink_status, &player_event_program) - }))); - } - } - - let (spirc, spirc_task) = Spirc::new(connect_config, session, player, mixer); - self.spirc = Some(spirc); - self.spirc_task = Some(spirc_task); - self.player_event_channel = Some(event_channel); - - progress = true; - } - Ok(Async::NotReady) => (), - Err(error) => { - error!("Could not connect to server: {}", error); - self.connect = Box::new(futures::future::empty()); - } - } - - if let Async::Ready(Some(())) = self.signal.poll().unwrap() { - trace!("Ctrl-C received"); - if !self.shutdown { - if let Some(ref spirc) = self.spirc { - spirc.shutdown(); - } else { - return Ok(Async::Ready(())); - } - self.shutdown = true; - } else { - return Ok(Async::Ready(())); - } - - progress = true; - } - - let mut drop_spirc_and_try_to_reconnect = false; - if let Some(ref mut spirc_task) = self.spirc_task { - if let Async::Ready(()) = spirc_task.poll().unwrap() { - if self.shutdown { - return Ok(Async::Ready(())); - } else { - warn!("Spirc shut down unexpectedly"); - drop_spirc_and_try_to_reconnect = true; - } - progress = true; - } - } - if drop_spirc_and_try_to_reconnect { - self.spirc_task = None; - while (!self.auto_connect_times.is_empty()) - && ((Instant::now() - self.auto_connect_times[0]).as_secs() > 600) - { - let _ = self.auto_connect_times.remove(0); - } - - if let Some(credentials) = self.last_credentials.clone() { - if self.auto_connect_times.len() >= 5 { - warn!("Spirc shut down too often. Not reconnecting automatically."); - } else { - self.auto_connect_times.push(Instant::now()); - self.credentials(credentials); - } - } - } - - if let Some(ref mut player_event_channel) = self.player_event_channel { - if let Async::Ready(Some(event)) = player_event_channel.poll().unwrap() { - progress = true; - if let Some(ref program) = self.player_event_program { - if let Some(child) = run_program_on_events(event, program) { - let child = child - .expect("program failed to start") - .map(|status| { - if !status.success() { - error!("child exited with status {:?}", status.code()); - } - }) - .map_err(|e| error!("failed to wait on child process: {}", e)); - - self.handle.spawn(child); - } - } - } - } - - if !progress { - return Ok(Async::NotReady); - } - } - } -} - -fn main() { - if env::var("RUST_BACKTRACE").is_err() { - env::set_var("RUST_BACKTRACE", "full") - } - let mut core = Core::new().unwrap(); - let handle = core.handle(); - - let args: Vec = std::env::args().collect(); - - core.run(Main::new(handle, setup(&args))).unwrap() -} From 07514c9dcca59bd13b149cd19a276fa27c6d6986 Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Mon, 25 Jan 2021 20:55:49 +0100 Subject: [PATCH 021/103] Add proxy support to apresolve --- Cargo.lock | 46 +++++++++++++++++++++++++++++++++++++++++++ core/Cargo.toml | 1 + core/src/apresolve.rs | 27 ++++++++++++------------- core/src/lib.rs | 1 + 4 files changed, 61 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5813a70..3754fd6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -962,6 +962,31 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" +[[package]] +name = "headers" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62689dc57c7456e69712607ffcbd0aa1dfcccf9af73727e9b25bc1825375cac3" +dependencies = [ + "base64 0.13.0", + "bitflags 1.2.1", + "bytes", + "headers-core", + "http", + "mime", + "sha-1", + "time 0.1.43", +] + +[[package]] +name = "headers-core" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" +dependencies = [ + "http", +] + [[package]] name = "heck" version = "0.3.2" @@ -1047,6 +1072,20 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-proxy" +version = "0.8.0" +source = "git+https://github.com/e00E/hyper-proxy.git?branch=upgrade-tokio#4be706f2f0297bd3d14f301b6ea0be8f3078bb17" +dependencies = [ + "bytes", + "futures", + "headers", + "http", + "hyper", + "tokio", + "tower-service", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -1323,6 +1362,7 @@ dependencies = [ "hmac", "httparse", "hyper", + "hyper-proxy", "librespot-protocol", "log", "num-bigint", @@ -1452,6 +1492,12 @@ version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + [[package]] name = "miniz_oxide" version = "0.4.3" diff --git a/core/Cargo.toml b/core/Cargo.toml index c092c04..4ff4693 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -21,6 +21,7 @@ futures = { version = "0.3", features = ["bilock", "unstable"] } hmac = "0.7" httparse = "1.3" hyper = { version = "0.14", features = ["client", "tcp", "http1", "http2"] } +hyper-proxy = { git = "https://github.com/e00E/hyper-proxy.git", branch="upgrade-tokio", default_features = false } log = "0.4" num-bigint = "0.3" num-integer = "0.1" diff --git a/core/src/apresolve.rs b/core/src/apresolve.rs index 07c2958..d35b209 100644 --- a/core/src/apresolve.rs +++ b/core/src/apresolve.rs @@ -1,7 +1,8 @@ const AP_FALLBACK: &'static str = "ap.spotify.com:443"; const APRESOLVE_ENDPOINT: &'static str = "http://apresolve.spotify.com/"; -use hyper::{Body, Client, Method, Request, Uri}; +use hyper::{client::HttpConnector, Body, Client, Method, Request, Uri}; +use hyper_proxy::{Intercept, Proxy, ProxyConnector}; use std::error::Error; use url::Url; @@ -13,7 +14,7 @@ pub struct APResolveData { async fn apresolve(proxy: &Option, ap_port: &Option) -> Result> { let port = ap_port.unwrap_or(443); - let req = Request::builder() + let mut req = Request::builder() .method(Method::GET) .uri( APRESOLVE_ENDPOINT @@ -22,25 +23,22 @@ async fn apresolve(proxy: &Option, ap_port: &Option) -> Result, ap_port: &Option) -> Result Date: Mon, 25 Jan 2021 20:56:22 +0100 Subject: [PATCH 022/103] Replaced .fold(0, add) by .sum() --- audio/src/range_set.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/audio/src/range_set.rs b/audio/src/range_set.rs index 8712dfd..d01d888 100644 --- a/audio/src/range_set.rs +++ b/audio/src/range_set.rs @@ -54,7 +54,7 @@ impl RangeSet { } pub fn len(&self) -> usize { - self.ranges.iter().map(|r| r.length).fold(0, std::ops::Add::add) + self.ranges.iter().map(|r| r.length).sum() } pub fn get_range(&self, index: usize) -> Range { From a45fe85c27512b9d1a25b191731dc90103905f62 Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Sat, 30 Jan 2021 13:53:44 +0100 Subject: [PATCH 023/103] Enable logging in test --- Cargo.lock | 61 +++++++++++++++++++++++++++++++++++++++++++ core/Cargo.toml | 1 + core/tests/connect.rs | 1 + 3 files changed, 63 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 3754fd6..e417651 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -64,6 +64,15 @@ dependencies = [ "opaque-debug 0.3.0", ] +[[package]] +name = "aho-corasick" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" +dependencies = [ + "memchr", +] + [[package]] name = "alsa" version = "0.2.2" @@ -152,6 +161,17 @@ dependencies = [ "syn", ] +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + [[package]] name = "autocfg" version = "1.0.1" @@ -567,6 +587,19 @@ version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" +[[package]] +name = "env_logger" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26ecb66b4bdca6c1409b40fb255eefc2bd4f6d135dab3c3124f80ffa2a9661e" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + [[package]] name = "error-chain" version = "0.12.4" @@ -1048,6 +1081,12 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47" +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + [[package]] name = "hyper" version = "0.14.2" @@ -1358,6 +1397,7 @@ dependencies = [ "base64 0.13.0", "byteorder", "bytes", + "env_logger", "futures", "hmac", "httparse", @@ -2172,7 +2212,10 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a" dependencies = [ + "aho-corasick", + "memchr", "regex-syntax", + "thread_local", ] [[package]] @@ -2598,6 +2641,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "termcolor" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" +dependencies = [ + "winapi-util", +] + [[package]] name = "thiserror" version = "1.0.23" @@ -2618,6 +2670,15 @@ dependencies = [ "syn", ] +[[package]] +name = "thread_local" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8208a331e1cb318dd5bd76951d2b8fc48ca38a69f5f4e4af1b6a9f8c6236915" +dependencies = [ + "once_cell", +] + [[package]] name = "time" version = "0.1.43" diff --git a/core/Cargo.toml b/core/Cargo.toml index 4ff4693..1f7b9af 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -46,4 +46,5 @@ rand = "0.7" vergen = "3.0.4" [dev-dependencies] +env_logger = "*" tokio = {version = "1.0", features = ["macros"] } \ No newline at end of file diff --git a/core/tests/connect.rs b/core/tests/connect.rs index 44d418a..4ea2a1f 100644 --- a/core/tests/connect.rs +++ b/core/tests/connect.rs @@ -7,6 +7,7 @@ mod tests { use apresolve::apresolve_or_fallback; #[tokio::test] async fn test_ap_resolve() { + env_logger::init(); let ap = apresolve_or_fallback(&None, &None).await; println!("AP: {:?}", ap); } From c1d62d72a7bd12f757d7d5b09b02b2c2387eda38 Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Sat, 30 Jan 2021 14:03:34 +0100 Subject: [PATCH 024/103] Fixed ProxyTunnel --- core/src/proxytunnel.rs | 55 ++++++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 22 deletions(-) diff --git a/core/src/proxytunnel.rs b/core/src/proxytunnel.rs index 508de7f..c8e9eab 100644 --- a/core/src/proxytunnel.rs +++ b/core/src/proxytunnel.rs @@ -17,29 +17,40 @@ pub async fn connect( .into_bytes(); connection.write_all(buffer.as_ref()).await?; - buffer.clear(); - connection.read_to_end(&mut buffer).await?; - if buffer.is_empty() { - return Err(io::Error::new(io::ErrorKind::Other, "Early EOF from proxy")); - } + buffer.resize(buffer.capacity(), 0); - let mut headers = [httparse::EMPTY_HEADER; 16]; - let mut response = httparse::Response::new(&mut headers); - - response - .parse(&buffer[..]) - .map_err(|err| io::Error::new(io::ErrorKind::Other, err.to_string()))?; - - match response.code { - Some(200) => Ok(connection), // Proxy says all is well - Some(code) => { - let reason = response.reason.unwrap_or("no reason"); - let msg = format!("Proxy responded with {}: {}", code, reason); - Err(io::Error::new(io::ErrorKind::Other, msg)) + let mut offset = 0; + loop { + let bytes_read = connection.read(&mut buffer[offset..]).await?; + if bytes_read == 0 { + return Err(io::Error::new(io::ErrorKind::Other, "Early EOF from proxy")); + } + offset += bytes_read; + + let mut headers = [httparse::EMPTY_HEADER; 16]; + let mut response = httparse::Response::new(&mut headers); + + let status = response + .parse(&buffer[..offset]) + .map_err(|err| io::Error::new(io::ErrorKind::Other, err))?; + + if status.is_complete() { + return match response.code { + Some(200) => Ok(connection), // Proxy says all is well + Some(code) => { + let reason = response.reason.unwrap_or("no reason"); + let msg = format!("Proxy responded with {}: {}", code, reason); + Err(io::Error::new(io::ErrorKind::Other, msg)) + } + None => Err(io::Error::new( + io::ErrorKind::Other, + "Malformed response from proxy", + )), + }; + } + + if offset >= buffer.len() { + buffer.resize(buffer.len() * 2, 0); } - None => Err(io::Error::new( - io::ErrorKind::Other, - "Malformed response from proxy", - )), } } From bb44b99c92f16c2770651e75647a3e19cad8b77a Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Sat, 30 Jan 2021 14:45:31 +0100 Subject: [PATCH 025/103] Use proxytunnel in apresolve Implementing the tower_service::Service trait for a newly created ProxyTunnel struct, so it can be used as connector in hyper. --- Cargo.lock | 47 +--------------------- core/Cargo.toml | 2 +- core/src/apresolve.rs | 25 +++++------- core/src/connection/mod.rs | 18 ++++++++- core/src/lib.rs | 2 +- core/src/proxytunnel.rs | 82 ++++++++++++++++++++++++++++++-------- 6 files changed, 95 insertions(+), 81 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e417651..7606ed8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -995,31 +995,6 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" -[[package]] -name = "headers" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62689dc57c7456e69712607ffcbd0aa1dfcccf9af73727e9b25bc1825375cac3" -dependencies = [ - "base64 0.13.0", - "bitflags 1.2.1", - "bytes", - "headers-core", - "http", - "mime", - "sha-1", - "time 0.1.43", -] - -[[package]] -name = "headers-core" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" -dependencies = [ - "http", -] - [[package]] name = "heck" version = "0.3.2" @@ -1111,20 +1086,6 @@ dependencies = [ "want", ] -[[package]] -name = "hyper-proxy" -version = "0.8.0" -source = "git+https://github.com/e00E/hyper-proxy.git?branch=upgrade-tokio#4be706f2f0297bd3d14f301b6ea0be8f3078bb17" -dependencies = [ - "bytes", - "futures", - "headers", - "http", - "hyper", - "tokio", - "tower-service", -] - [[package]] name = "ident_case" version = "1.0.1" @@ -1402,7 +1363,6 @@ dependencies = [ "hmac", "httparse", "hyper", - "hyper-proxy", "librespot-protocol", "log", "num-bigint", @@ -1420,6 +1380,7 @@ dependencies = [ "shannon", "tokio", "tokio-util", + "tower-service", "url 1.7.2", "uuid", "vergen", @@ -1532,12 +1493,6 @@ version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" -[[package]] -name = "mime" -version = "0.3.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" - [[package]] name = "miniz_oxide" version = "0.4.3" diff --git a/core/Cargo.toml b/core/Cargo.toml index 1f7b9af..e0d7952 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -21,7 +21,6 @@ futures = { version = "0.3", features = ["bilock", "unstable"] } hmac = "0.7" httparse = "1.3" hyper = { version = "0.14", features = ["client", "tcp", "http1", "http2"] } -hyper-proxy = { git = "https://github.com/e00E/hyper-proxy.git", branch="upgrade-tokio", default_features = false } log = "0.4" num-bigint = "0.3" num-integer = "0.1" @@ -38,6 +37,7 @@ sha-1 = "~0.8" shannon = "0.2.0" tokio = { version = "1.0", features = ["io-util", "rt-multi-thread"] } tokio-util = { version = "0.6", features = ["codec"] } +tower-service = "0.3" url = "1.7" uuid = { version = "0.8", features = ["v4"] } diff --git a/core/src/apresolve.rs b/core/src/apresolve.rs index d35b209..81340c9 100644 --- a/core/src/apresolve.rs +++ b/core/src/apresolve.rs @@ -1,11 +1,12 @@ const AP_FALLBACK: &'static str = "ap.spotify.com:443"; -const APRESOLVE_ENDPOINT: &'static str = "http://apresolve.spotify.com/"; +const APRESOLVE_ENDPOINT: &'static str = "http://apresolve.spotify.com:80"; -use hyper::{client::HttpConnector, Body, Client, Method, Request, Uri}; -use hyper_proxy::{Intercept, Proxy, ProxyConnector}; +use hyper::{Body, Client, Method, Request, Uri}; use std::error::Error; use url::Url; +use crate::proxytunnel::ProxyTunnel; + #[derive(Clone, Debug, Serialize, Deserialize)] pub struct APResolveData { ap_list: Vec, @@ -14,7 +15,7 @@ pub struct APResolveData { async fn apresolve(proxy: &Option, ap_port: &Option) -> Result> { let port = ap_port.unwrap_or(443); - let mut req = Request::builder() + let req = Request::builder() .method(Method::GET) .uri( APRESOLVE_ENDPOINT @@ -24,18 +25,10 @@ async fn apresolve(proxy: &Option, ap_port: &Option) -> Result; pub async fn connect(addr: String, proxy: &Option) -> io::Result { let socket = if let Some(proxy) = proxy { info!("Using proxy \"{}\"", proxy); + + let mut split = addr.rsplit(':'); + + let port = split + .next() + .unwrap() // will never panic, split iterator contains at least one element + .parse() + .map_err(|e| { + io::Error::new(io::ErrorKind::InvalidInput, format!("Invalid port: {}", e)) + })?; + + let host = split + .next() + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "Missing port"))?; + let socket_addr = proxy.to_socket_addrs().and_then(|mut iter| { iter.next().ok_or_else(|| { io::Error::new( @@ -31,7 +46,8 @@ pub async fn connect(addr: String, proxy: &Option) -> io::Result }) })?; let socket = TcpStream::connect(&socket_addr).await?; - proxytunnel::connect(socket, &addr).await? + + proxytunnel::connect(socket, host, port).await? } else { let socket_addr = addr.to_socket_addrs().and_then(|mut iter| { iter.next().ok_or_else(|| { diff --git a/core/src/lib.rs b/core/src/lib.rs index 4fb632a..3e332c2 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -14,7 +14,6 @@ extern crate futures; extern crate hmac; extern crate httparse; extern crate hyper; -extern crate hyper_proxy; extern crate num_bigint; extern crate num_integer; extern crate num_traits; @@ -28,6 +27,7 @@ extern crate sha1; extern crate shannon; pub extern crate tokio; extern crate tokio_util; +extern crate tower_service; extern crate url; extern crate uuid; diff --git a/core/src/proxytunnel.rs b/core/src/proxytunnel.rs index c8e9eab..c2033c8 100644 --- a/core/src/proxytunnel.rs +++ b/core/src/proxytunnel.rs @@ -1,27 +1,36 @@ -use std::io; - +use futures::Future; use hyper::Uri; -use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; +use std::{ + io, + net::{SocketAddr, ToSocketAddrs}, + pin::Pin, + task::Poll, +}; +use tokio::{ + io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}, + net::TcpStream, +}; +use tower_service::Service; pub async fn connect( - mut connection: T, - connect_url: &str, + mut proxy_connection: T, + connect_host: &str, + connect_port: u16, ) -> io::Result { - let uri = connect_url.parse::().unwrap(); - let mut buffer = format!( - "CONNECT {0}:{1} HTTP/1.1\r\n\ - \r\n", - uri.host().unwrap_or_else(|| panic!("No host in {}", uri)), - uri.port().unwrap_or_else(|| panic!("No port in {}", uri)) - ) - .into_bytes(); - connection.write_all(buffer.as_ref()).await?; + let mut buffer = Vec::new(); + buffer.extend_from_slice(b"CONNECT "); + buffer.extend_from_slice(connect_host.as_bytes()); + buffer.push(b':'); + buffer.extend_from_slice(connect_port.to_string().as_bytes()); + buffer.extend_from_slice(b" HTTP/1.1\r\n\r\n"); + + proxy_connection.write_all(buffer.as_ref()).await?; buffer.resize(buffer.capacity(), 0); let mut offset = 0; loop { - let bytes_read = connection.read(&mut buffer[offset..]).await?; + let bytes_read = proxy_connection.read(&mut buffer[offset..]).await?; if bytes_read == 0 { return Err(io::Error::new(io::ErrorKind::Other, "Early EOF from proxy")); } @@ -36,7 +45,7 @@ pub async fn connect( if status.is_complete() { return match response.code { - Some(200) => Ok(connection), // Proxy says all is well + Some(200) => Ok(proxy_connection), // Proxy says all is well Some(code) => { let reason = response.reason.unwrap_or("no reason"); let msg = format!("Proxy responded with {}: {}", code, reason); @@ -54,3 +63,44 @@ pub async fn connect( } } } + +#[derive(Clone)] +pub struct ProxyTunnel { + proxy_addr: SocketAddr, +} + +impl ProxyTunnel { + pub fn new(addr: T) -> io::Result { + let addr = addr.to_socket_addrs()?.next().ok_or_else(|| { + io::Error::new(io::ErrorKind::InvalidInput, "No socket address given") + })?; + Ok(Self { proxy_addr: addr }) + } +} + +impl Service for ProxyTunnel { + type Response = TcpStream; + type Error = io::Error; + type Future = Pin> + Send>>; + + fn poll_ready(&mut self, _: &mut std::task::Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, url: Uri) -> Self::Future { + let proxy_addr = self.proxy_addr; + let fut = async move { + let host = url + .host() + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "Host is missing"))?; + let port = url + .port() + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "Port is missing"))?; + + let conn = TcpStream::connect(proxy_addr).await?; + connect(conn, host, port.as_u16()).await + }; + + Box::pin(fut) + } +} From 2f05ddfbc20fb04131a2ad1ce68dbc181fe6b7cf Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Fri, 12 Feb 2021 18:19:04 +0100 Subject: [PATCH 026/103] Fix bugs in player --- playback/src/player.rs | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/playback/src/player.rs b/playback/src/player.rs index b5683e5..26eea7f 100644 --- a/playback/src/player.rs +++ b/playback/src/player.rs @@ -327,15 +327,12 @@ impl Player { } pub async fn get_end_of_track_future(&self) { - self.get_player_event_channel() - .filter(|event| { - future::ready(matches!( - event, - PlayerEvent::EndOfTrack { .. } | PlayerEvent::Stopped { .. } - )) - }) - .for_each(|_| future::ready(())) - .await + let mut channel = self.get_player_event_channel(); + while let Some(event) = channel.next().await { + if matches!(event, PlayerEvent::EndOfTrack { .. } | PlayerEvent::Stopped { .. }) { + return; + } + } } pub fn set_sink_event_callback(&self, callback: Option) { @@ -676,14 +673,6 @@ impl PlayerTrackLoader { let bytes_per_second = self.stream_data_rate(format); let play_from_beginning = position_ms == 0; - let key = match self.session.audio_key().request(spotify_id, file_id).await { - Ok(key) => key, - Err(_) => { - error!("Unable to load decryption key"); - return None; - } - }; - // This is only a loop to be able to reload the file if an error occured // while opening a cached file. loop { @@ -712,6 +701,14 @@ impl PlayerTrackLoader { // we need to seek -> we set stream mode after the initial seek. stream_loader_controller.set_random_access_mode(); } + + let key = match self.session.audio_key().request(spotify_id, file_id).await { + Ok(key) => key, + Err(_) => { + error!("Unable to load decryption key"); + return None; + } + }; let mut decrypted_file = AudioDecrypt::new(key, encrypted_file); From b2f1be4374e091267d0676e92870b5f3eb30fa7c Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Fri, 12 Feb 2021 18:25:13 +0100 Subject: [PATCH 027/103] Make `RodioSink` `Send` and improve error handling --- Cargo.lock | 1 + playback/Cargo.toml | 9 ++- playback/src/audio_backend/rodio.rs | 111 ++++++++++++++++++---------- 3 files changed, 78 insertions(+), 43 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6255e76..0034316 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1375,6 +1375,7 @@ dependencies = [ "rodio", "sdl2", "shell-words", + "thiserror", "zerocopy", ] diff --git a/playback/Cargo.toml b/playback/Cargo.toml index 95c4a12..1562219 100644 --- a/playback/Cargo.toml +++ b/playback/Cargo.toml @@ -29,19 +29,22 @@ libpulse-binding = { version = "2.13", optional = true, default-features libpulse-simple-binding = { version = "2.13", optional = true, default-features = false } jack = { version = "0.6", optional = true } libc = { version = "0.2", optional = true } -rodio = { version = "0.13", optional = true, default-features = false } -cpal = { version = "0.13", optional = true } sdl2 = { version = "0.34", optional = true } gstreamer = { version = "0.16", optional = true } gstreamer-app = { version = "0.16", optional = true } glib = { version = "0.10", optional = true } zerocopy = { version = "0.3", optional = true } +# Rodio dependencies +rodio = { version = "0.13", optional = true, default-features = false } +cpal = { version = "0.13", optional = true } +thiserror = { version = "1", optional = true } + [features] alsa-backend = ["alsa"] portaudio-backend = ["portaudio-rs"] pulseaudio-backend = ["libpulse-binding", "libpulse-simple-binding"] jackaudio-backend = ["jack"] -rodio-backend = ["rodio", "cpal"] +rodio-backend = ["rodio", "cpal", "thiserror"] sdl-backend = ["sdl2"] gstreamer-backend = ["gstreamer", "gstreamer-app", "glib", "zerocopy"] diff --git a/playback/src/audio_backend/rodio.rs b/playback/src/audio_backend/rodio.rs index bc10178..0c4b1a4 100644 --- a/playback/src/audio_backend/rodio.rs +++ b/playback/src/audio_backend/rodio.rs @@ -1,20 +1,36 @@ -use super::{Open, Sink}; -extern crate cpal; -extern crate rodio; -use cpal::traits::{DeviceTrait, HostTrait}; use std::process::exit; +use std::{convert::Infallible, sync::mpsc}; use std::{io, thread, time}; +use cpal::traits::{DeviceTrait, HostTrait}; +use thiserror::Error; + +use super::{Open, Sink}; + +#[derive(Debug, Error)] +pub enum RodioError { + #[error("Rodio: no device available")] + NoDeviceAvailable, + #[error("Rodio: device \"{0}\" is not available")] + DeviceNotAvailable(String), + #[error("Rodio play error: {0}")] + PlayError(#[from] rodio::PlayError), + #[error("Rodio stream error: {0}")] + StreamError(#[from] rodio::StreamError), + #[error("Cannot get audio devices: {0}")] + DevicesError(#[from] cpal::DevicesError), +} + pub struct RodioSink { rodio_sink: rodio::Sink, - // We have to keep hold of this object, or the Sink can't play... - #[allow(dead_code)] - stream: rodio::OutputStream, + + // will produce a TryRecvError on the receiver side when it is dropped. + _close_tx: mpsc::SyncSender, } -fn list_formats(ref device: &rodio::Device) { +fn list_formats(device: &rodio::Device) { let default_fmt = match device.default_output_config() { - Ok(fmt) => cpal::SupportedStreamConfig::from(fmt), + Ok(fmt) => fmt, Err(e) => { warn!("Error getting default rodio::Sink config: {}", e); return; @@ -39,8 +55,8 @@ fn list_formats(ref device: &rodio::Device) { } } -fn list_outputs() { - let default_device = get_default_device(); +fn list_outputs_and_exit() -> ! { + let default_device = get_default_device().unwrap(); let default_device_name = default_device.name().expect("cannot get output name"); println!("Default Audio Device:\n {}", default_device_name); list_formats(&default_device); @@ -56,54 +72,69 @@ fn list_outputs() { list_formats(&device); } } + + exit(0) } -fn get_default_device() -> rodio::Device { +fn get_default_device() -> Result { cpal::default_host() .default_output_device() - .expect("no default output device available") + .ok_or(RodioError::NoDeviceAvailable) } -fn match_device(device: Option) -> rodio::Device { - match device { +fn create_sink(device: Option) -> Result<(rodio::Sink, rodio::OutputStream), RodioError> { + let rodio_device = match device { + Some(ask) if &ask == "?" => list_outputs_and_exit(), Some(device_name) => { - if device_name == "?".to_string() { - list_outputs(); - exit(0) - } - for d in cpal::default_host() - .output_devices() - .expect("cannot get list of output devices") - { - if d.name().expect("cannot get output name") == device_name { - return d; - } - } - println!("No output sink matching '{}' found.", device_name); - exit(0) + cpal::default_host() + .output_devices()? + .find(|d| d.name().ok().map_or(false, |name| name == device_name)) // Ignore devices for which getting name fails + .ok_or(RodioError::DeviceNotAvailable(device_name))? } - None => return get_default_device(), - } + None => get_default_device()?, + }; + + let name = rodio_device.name().ok(); + info!( + "Using audio device: {}", + name.as_deref().unwrap_or("(unknown name)") + ); + + let (stream, handle) = rodio::OutputStream::try_from_device(&rodio_device)?; + let sink = rodio::Sink::try_new(&handle)?; + Ok((sink, stream)) } impl Open for RodioSink { fn open(device: Option) -> RodioSink { debug!( "Using rodio sink with cpal host: {:?}", - cpal::default_host().id() + cpal::default_host().id().name() ); - let rodio_device = match_device(device); - debug!("Using cpal device"); - let stream = rodio::OutputStream::try_from_device(&rodio_device) - .expect("Couldn't open output stream."); - debug!("Using rodio stream"); - let sink = rodio::Sink::try_new(&stream.1).expect("Couldn't create output sink."); - debug!("Using rodio sink"); + let (sink_tx, sink_rx) = mpsc::sync_channel(1); + let (close_tx, close_rx) = mpsc::sync_channel(1); + std::thread::spawn(move || match create_sink(device) { + Ok((sink, stream)) => { + sink_tx.send(Ok(sink)).unwrap(); + + close_rx.recv().unwrap_err(); // This will fail as soon as the sender is dropped + debug!("drop rodio::OutputStream"); + drop(stream); + } + Err(e) => { + sink_tx.send(Err(e)).unwrap(); + } + }); + + // Instead of the second `unwrap`, better error handling could be introduced + let sink = sink_rx.recv().unwrap().unwrap(); + + debug!("Rodio sink was created"); RodioSink { rodio_sink: sink, - stream: stream.0, + _close_tx: close_tx, } } } From 689415a6f1580547dae5be592cb21a847114edb1 Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Fri, 12 Feb 2021 19:31:41 +0100 Subject: [PATCH 028/103] Improved error handling in rodio backend --- playback/src/audio_backend/rodio.rs | 103 +++++++++++++++++----------- 1 file changed, 64 insertions(+), 39 deletions(-) diff --git a/playback/src/audio_backend/rodio.rs b/playback/src/audio_backend/rodio.rs index 0c4b1a4..034bd08 100644 --- a/playback/src/audio_backend/rodio.rs +++ b/playback/src/audio_backend/rodio.rs @@ -29,75 +29,100 @@ pub struct RodioSink { } fn list_formats(device: &rodio::Device) { - let default_fmt = match device.default_output_config() { - Ok(fmt) => fmt, - Err(e) => { - warn!("Error getting default rodio::Sink config: {}", e); - return; + match device.default_output_config() { + Ok(cfg) => { + debug!(" Default config:"); + debug!(" {:?}", cfg); } - }; - debug!(" Default config:"); - debug!(" {:?}", default_fmt); - - let mut output_configs = match device.supported_output_configs() { - Ok(f) => f.peekable(), Err(e) => { - warn!("Error getting supported rodio::Sink configs: {}", e); - return; + // Use loglevel debug, since even the output is only debug + debug!("Error getting default rodio::Sink config: {}", e); } }; - if output_configs.peek().is_some() { - debug!(" Available configs:"); - for format in output_configs { - debug!(" {:?}", format); + match device.supported_output_configs() { + Ok(mut cfgs) => { + if let Some(first) = cfgs.next() { + debug!(" Available configs:"); + debug!(" {:?}", first); + } else { + return; + } + + for cfg in cfgs { + debug!(" {:?}", cfg); + } + } + Err(e) => { + debug!("Error getting supported rodio::Sink configs: {}", e); } } } -fn list_outputs_and_exit() -> ! { - let default_device = get_default_device().unwrap(); - let default_device_name = default_device.name().expect("cannot get output name"); - println!("Default Audio Device:\n {}", default_device_name); - list_formats(&default_device); +fn list_outputs() -> Result<(), cpal::DevicesError> { + let mut default_device_name = None; - println!("Other Available Audio Devices:"); - for device in cpal::default_host() - .output_devices() - .expect("cannot get list of output devices") - { - let device_name = device.name().expect("cannot get output name"); - if device_name != default_device_name { - println!(" {}", device_name); - list_formats(&device); + if let Some(default_device) = get_default_device() { + default_device_name = default_device.name().ok(); + println!( + "Default Audio Device:\n {}", + default_device_name.as_deref().unwrap_or("[unknown name]") + ); + + list_formats(&default_device); + + println!("Other Available Audio Devices:"); + } else { + warn!("No default device was found"); + } + + for device in cpal::default_host().output_devices()? { + match device.name() { + Ok(name) if Some(&name) == default_device_name.as_ref() => (), + Ok(name) => { + println!(" {}", name); + list_formats(&device); + } + Err(e) => { + warn!("Cannot get device name: {}", e); + println!(" [unknown name]"); + list_formats(&device); + } } } - exit(0) + Ok(()) } -fn get_default_device() -> Result { - cpal::default_host() - .default_output_device() - .ok_or(RodioError::NoDeviceAvailable) +fn get_default_device() -> Option { + cpal::default_host().default_output_device() } fn create_sink(device: Option) -> Result<(rodio::Sink, rodio::OutputStream), RodioError> { let rodio_device = match device { - Some(ask) if &ask == "?" => list_outputs_and_exit(), + Some(ask) if &ask == "?" => { + let exit_code = match list_outputs() { + Ok(()) => 0, + Err(e) => { + error!("{}", e); + 1 + } + }; + exit(exit_code) + } Some(device_name) => { cpal::default_host() .output_devices()? .find(|d| d.name().ok().map_or(false, |name| name == device_name)) // Ignore devices for which getting name fails .ok_or(RodioError::DeviceNotAvailable(device_name))? } - None => get_default_device()?, + None => get_default_device().ok_or(RodioError::NoDeviceAvailable)?, }; let name = rodio_device.name().ok(); info!( "Using audio device: {}", - name.as_deref().unwrap_or("(unknown name)") + name.as_deref().unwrap_or("[unknown name]") ); let (stream, handle) = rodio::OutputStream::try_from_device(&rodio_device)?; From b77f0a18cef0c893cb48d2b34746287c7c00cdc6 Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Sat, 13 Feb 2021 10:29:00 +0100 Subject: [PATCH 029/103] Fix formatting --- core/src/connection/mod.rs | 2 +- playback/src/player.rs | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/core/src/connection/mod.rs b/core/src/connection/mod.rs index 1ca7316..68e2e7a 100644 --- a/core/src/connection/mod.rs +++ b/core/src/connection/mod.rs @@ -32,7 +32,7 @@ pub async fn connect(addr: String, proxy: &Option) -> io::Result .map_err(|e| { io::Error::new(io::ErrorKind::InvalidInput, format!("Invalid port: {}", e)) })?; - + let host = split .next() .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "Missing port"))?; diff --git a/playback/src/player.rs b/playback/src/player.rs index 26eea7f..6f6a85a 100644 --- a/playback/src/player.rs +++ b/playback/src/player.rs @@ -329,7 +329,10 @@ impl Player { pub async fn get_end_of_track_future(&self) { let mut channel = self.get_player_event_channel(); while let Some(event) = channel.next().await { - if matches!(event, PlayerEvent::EndOfTrack { .. } | PlayerEvent::Stopped { .. }) { + if matches!( + event, + PlayerEvent::EndOfTrack { .. } | PlayerEvent::Stopped { .. } + ) { return; } } @@ -701,7 +704,7 @@ impl PlayerTrackLoader { // we need to seek -> we set stream mode after the initial seek. stream_loader_controller.set_random_access_mode(); } - + let key = match self.session.audio_key().request(spotify_id, file_id).await { Ok(key) => key, Err(_) => { From daf7ecd23a26f124b91c915ee8f39cc3d30b0840 Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Sat, 20 Feb 2021 00:17:18 +0100 Subject: [PATCH 030/103] Migrate librespot-connect to tokio 1.0 --- Cargo.lock | 323 +++++++++++++++++++++++++++++---------- Cargo.toml | 6 +- connect/Cargo.toml | 8 +- connect/src/discovery.rs | 125 ++++++--------- connect/src/spirc.rs | 312 ++++++++++++++++++------------------- core/src/mercury/mod.rs | 72 ++++----- src/lib.rs | 2 +- 7 files changed, 478 insertions(+), 370 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0034316..6b6e7af 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -27,21 +27,44 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "884391ef1066acaa41e766ba8f596341b96e93ce34f9a43e7d24bf0a0eaf0561" dependencies = [ - "aes-soft", - "aesni", + "aes-soft 0.6.4", + "aesni 0.10.0", "cipher", ] +[[package]] +name = "aes-ctr" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2e5b0458ea3beae0d1d8c0f3946564f8e10f90646cf78c06b4351052058d1ee" +dependencies = [ + "aes-soft 0.3.3", + "aesni 0.6.0", + "ctr 0.3.2", + "stream-cipher", +] + [[package]] name = "aes-ctr" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7729c3cde54d67063be556aeac75a81330d802f0259500ca40cb52967f975763" dependencies = [ - "aes-soft", - "aesni", + "aes-soft 0.6.4", + "aesni 0.10.0", "cipher", - "ctr", + "ctr 0.6.0", +] + +[[package]] +name = "aes-soft" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfd7e7ae3f9a1fb5c03b389fc6bb9a51400d0c13053f0dca698c832bfd893a0d" +dependencies = [ + "block-cipher-trait", + "byteorder", + "opaque-debug 0.2.3", ] [[package]] @@ -54,6 +77,17 @@ dependencies = [ "opaque-debug 0.3.0", ] +[[package]] +name = "aesni" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f70a6b5f971e473091ab7cfb5ffac6cde81666c4556751d8d5620ead8abf100" +dependencies = [ + "block-cipher-trait", + "opaque-debug 0.2.3", + "stream-cipher", +] + [[package]] name = "aesni" version = "0.10.0" @@ -223,6 +257,25 @@ dependencies = [ "generic-array 0.12.3", ] +[[package]] +name = "block-cipher-trait" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c924d49bd09e7c06003acda26cd9742e796e34282ec6c1189404dee0c1f4774" +dependencies = [ + "generic-array 0.12.3", +] + +[[package]] +name = "block-modes" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31aa8410095e39fdb732909fb5730a48d5bd7c2e3cd76bd1b07b3dbea130c529" +dependencies = [ + "block-cipher-trait", + "block-padding", +] + [[package]] name = "block-padding" version = "0.1.5" @@ -234,9 +287,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.6.0" +version = "3.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099e596ef14349721d9016f6b80dd3419ea1bf289ab9b44df8e4dfd3a005d5d9" +checksum = "63396b8a4b9de3f4fdfb320ab6080762242f66a8ef174c49d8e19b674db4cdbe" [[package]] name = "byte-tools" @@ -258,9 +311,9 @@ checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" [[package]] name = "cc" -version = "1.0.66" +version = "1.0.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48" +checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd" [[package]] name = "cesu8" @@ -384,13 +437,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3818dfca4b0cb5211a659bbcbb94225b7127407b2b135e650d717bfb78ab10d3" dependencies = [ "cookie", - "idna 0.2.1", + "idna 0.2.2", "log", "publicsuffix", "serde", "serde_json", "time 0.2.25", - "url 2.2.0", + "url 2.2.1", ] [[package]] @@ -462,6 +515,16 @@ dependencies = [ "subtle", ] +[[package]] +name = "ctr" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "022cd691704491df67d25d006fe8eca083098253c4d43516c2206479c58c6736" +dependencies = [ + "block-cipher-trait", + "stream-cipher", +] + [[package]] name = "ctr" version = "0.6.0" @@ -532,6 +595,16 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" +[[package]] +name = "dns-sd" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d748509dea20228f63ba519bf142ce2593396386125b01f5b0d6412dab972087" +dependencies = [ + "libc", + "pkg-config", +] + [[package]] name = "either" version = "1.6.1" @@ -540,9 +613,9 @@ checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" [[package]] name = "env_logger" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26ecb66b4bdca6c1409b40fb255eefc2bd4f6d135dab3c3124f80ffa2a9661e" +checksum = "17392a012ea30ef05a610aa97dfb49496e71c9f676b27879922ea5bdf60d9d3f" dependencies = [ "atty", "humantime", @@ -586,7 +659,7 @@ checksum = "1d34cfa13a63ae058bfa601fe9e313bbdb3746427c1459185464ce0fcf62e1e8" dependencies = [ "cfg-if 1.0.0", "libc", - "redox_syscall 0.2.4", + "redox_syscall", "winapi", ] @@ -598,9 +671,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "form_urlencoded" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ece68d15c92e84fa4f19d3780f1294e5ca82a78a6d515f1efaabcc144688be00" +checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" dependencies = [ "matches", "percent-encoding 2.1.0", @@ -824,9 +897,9 @@ dependencies = [ [[package]] name = "gstreamer" -version = "0.16.5" +version = "0.16.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d50f822055923f1cbede233aa5dfd4ee957cf328fb3076e330886094e11d6cf" +checksum = "9ff5d0f7ff308ae37e6eb47b6ded17785bdea06e438a708cd09e0288c1862f33" dependencies = [ "bitflags", "cfg-if 1.0.0", @@ -974,6 +1047,17 @@ dependencies = [ "digest", ] +[[package]] +name = "hostname" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +dependencies = [ + "libc", + "match_cfg", + "winapi", +] + [[package]] name = "http" version = "0.2.3" @@ -1029,7 +1113,7 @@ dependencies = [ "httparse", "httpdate", "itoa", - "pin-project 1.0.5", + "pin-project", "socket2", "tokio", "tower-service", @@ -1056,15 +1140,36 @@ dependencies = [ [[package]] name = "idna" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de910d521f7cc3135c4de8db1cb910e0b5ed1dc6f57c381cd07e8e661ce10094" +checksum = "89829a5d69c23d348314a7ac337fe39173b61149a9864deabd260983aed48c21" dependencies = [ "matches", "unicode-bidi", "unicode-normalization", ] +[[package]] +name = "if-addrs" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28538916eb3f3976311f5dfbe67b5362d0add1293d0a9cad17debf86f8e3aa48" +dependencies = [ + "if-addrs-sys", + "libc", + "winapi", +] + +[[package]] +name = "if-addrs-sys" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de74b9dd780476e837e5eb5ab7c88b49ed304126e412030a0adba99c8efe79ea" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "indexmap" version = "1.6.1" @@ -1222,6 +1327,24 @@ dependencies = [ "winapi", ] +[[package]] +name = "libmdns" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b276920bfc6c9285e16ffd30ed410487f0185f383483f45a3446afc0554fded" +dependencies = [ + "byteorder", + "futures-util", + "hostname", + "if-addrs", + "log", + "multimap", + "quick-error", + "rand 0.8.3", + "socket2", + "tokio", +] + [[package]] name = "libpulse-binding" version = "2.23.0" @@ -1275,6 +1398,7 @@ name = "librespot" version = "0.1.3" dependencies = [ "librespot-audio", + "librespot-connect", "librespot-core", "librespot-metadata", "librespot-playback", @@ -1285,7 +1409,7 @@ dependencies = [ name = "librespot-audio" version = "0.1.3" dependencies = [ - "aes-ctr", + "aes-ctr 0.6.0", "bit-set", "byteorder", "bytes", @@ -1301,6 +1425,33 @@ dependencies = [ "vorbis", ] +[[package]] +name = "librespot-connect" +version = "0.1.3" +dependencies = [ + "aes-ctr 0.3.0", + "base64 0.13.0", + "block-modes", + "dns-sd", + "futures", + "hmac", + "hyper", + "libmdns", + "librespot-core", + "librespot-playback", + "librespot-protocol", + "log", + "num-bigint", + "protobuf", + "rand 0.7.3", + "serde", + "serde_derive", + "serde_json", + "sha-1", + "tokio", + "url 1.7.2", +] + [[package]] name = "librespot-core" version = "0.1.3" @@ -1434,6 +1585,12 @@ dependencies = [ "libc", ] +[[package]] +name = "match_cfg" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" + [[package]] name = "matches" version = "0.1.8" @@ -1458,9 +1615,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.7.7" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e50ae3f04d169fcc9bde0b547d1c205219b7157e07ded9c5aff03e0637cb3ed7" +checksum = "dc250d6848c90d719ea2ce34546fb5df7af1d3fd189d10bf7bad80bfcebecd95" dependencies = [ "libc", "log", @@ -1485,6 +1642,15 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0419348c027fa7be448d2ae7ea0e4e04c2334c31dc4e74ab29f00a2a7ca69204" +[[package]] +name = "multimap" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1255076139a83bb467426e7f8d0134968a8118844faa755985e077cf31850333" +dependencies = [ + "serde", +] + [[package]] name = "ndk" version = "0.2.1" @@ -1726,14 +1892,14 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ccb628cad4f84851442432c60ad8e1f607e29752d0bf072cbd0baf28aa34272" +checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018" dependencies = [ "cfg-if 1.0.0", "instant", "libc", - "redox_syscall 0.1.57", + "redox_syscall", "smallvec", "winapi", ] @@ -1786,33 +1952,13 @@ dependencies = [ "ucd-trie", ] -[[package]] -name = "pin-project" -version = "0.4.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ffbc8e94b38ea3d2d8ba92aea2983b503cd75d0888d75b86bb37970b5698e15" -dependencies = [ - "pin-project-internal 0.4.27", -] - [[package]] name = "pin-project" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96fa8ebb90271c4477f144354485b8068bd8f6b78b428b01ba892ca26caf0b63" dependencies = [ - "pin-project-internal 1.0.5", -] - -[[package]] -name = "pin-project-internal" -version = "0.4.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65ad2ae56b6abe3a1ee25f15ee605bacadb9a764edaba9c2bf4103800d4a1895" -dependencies = [ - "proc-macro2", - "quote", - "syn", + "pin-project-internal", ] [[package]] @@ -1963,10 +2109,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3bbaa49075179162b49acac1c6aa45fb4dafb5f13cf6794276d77bc7fd95757b" dependencies = [ "error-chain", - "idna 0.2.1", + "idna 0.2.2", "lazy_static", "regex", - "url 2.2.0", + "url 2.2.1", ] [[package]] @@ -1979,10 +2125,16 @@ dependencies = [ ] [[package]] -name = "quote" -version = "1.0.8" +name = "quick-error" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quote" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" dependencies = [ "proc-macro2", ] @@ -2021,7 +2173,7 @@ checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e" dependencies = [ "libc", "rand_chacha 0.3.0", - "rand_core 0.6.1", + "rand_core 0.6.2", "rand_hc 0.3.0", ] @@ -2042,7 +2194,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" dependencies = [ "ppv-lite86", - "rand_core 0.6.1", + "rand_core 0.6.2", ] [[package]] @@ -2071,9 +2223,9 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c026d7df8b298d90ccbbc5190bd04d85e159eaf5576caeacf8741da93ccbd2e5" +checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7" dependencies = [ "getrandom 0.2.2", ] @@ -2093,20 +2245,14 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" dependencies = [ - "rand_core 0.6.1", + "rand_core 0.6.2", ] [[package]] name = "redox_syscall" -version = "0.1.57" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" - -[[package]] -name = "redox_syscall" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ec8ca9416c5ea37062b502703cd7fcb207736bc294f6e0cf367ac6fc234570" +checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9" dependencies = [ "bitflags", ] @@ -2479,6 +2625,15 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" +[[package]] +name = "stream-cipher" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8131256a5896cabcf5eb04f4d6dacbe1aefda854b0d9896e09cb58829ec5638c" +dependencies = [ + "generic-array 0.12.3", +] + [[package]] name = "strsim" version = "0.9.3" @@ -2549,9 +2704,9 @@ dependencies = [ [[package]] name = "tar" -version = "0.4.32" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0313546c01d59e29be4f09687bcb4fb6690cec931cc3607b6aec7a0e417f4cc6" +checksum = "c0bcfbd6a598361fda270d82469fff3d65089dc33e175c9a131f7b4cd395f228" dependencies = [ "filetime", "libc", @@ -2567,7 +2722,7 @@ dependencies = [ "cfg-if 1.0.0", "libc", "rand 0.8.3", - "redox_syscall 0.2.4", + "redox_syscall", "remove_dir_all", "winapi", ] @@ -2583,18 +2738,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.23" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76cc616c6abf8c8928e2fdcc0dbfab37175edd8fb49a4641066ad1364fdab146" +checksum = "e0f4a65597094d4483ddaed134f409b2cb7c1beccf25201a9f73c719254fa98e" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.23" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9be73a2caec27583d0046ef3796c3794f868a5bc813db689eed00c7631275cd1" +checksum = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0" dependencies = [ "proc-macro2", "quote", @@ -2603,9 +2758,9 @@ dependencies = [ [[package]] name = "thread_local" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8208a331e1cb318dd5bd76951d2b8fc48ca38a69f5f4e4af1b6a9f8c6236915" +checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd" dependencies = [ "once_cell", ] @@ -2731,9 +2886,9 @@ checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" [[package]] name = "tracing" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d40a22fd029e33300d8d89a5cc8ffce18bb7c587662f54629e94c9de5487f3" +checksum = "f77d3842f76ca899ff2dbcf231c5c65813dea431301d6eb686279c15c4464f12" dependencies = [ "cfg-if 1.0.0", "pin-project-lite", @@ -2751,11 +2906,11 @@ dependencies = [ [[package]] name = "tracing-futures" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab7bb6f14721aa00656086e9335d363c5c8747bae02ebe32ea2c7dece5689b4c" +checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" dependencies = [ - "pin-project 0.4.27", + "pin-project", "tracing", ] @@ -2836,7 +2991,7 @@ dependencies = [ "once_cell", "qstring", "rustls", - "url 2.2.0", + "url 2.2.1", "webpki", "webpki-roots", ] @@ -2854,12 +3009,12 @@ dependencies = [ [[package]] name = "url" -version = "2.2.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5909f2b0817350449ed73e8bcd81c8c3c8d9a7a5d8acba4b27db277f1868976e" +checksum = "9ccd964113622c8e9322cfac19eb1004a07e636c545f325da085d5cdde6f1f8b" dependencies = [ "form_urlencoded", - "idna 0.2.1", + "idna 0.2.2", "matches", "percent-encoding 2.1.0", ] diff --git a/Cargo.toml b/Cargo.toml index a7ef8ed..fa67c0f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,9 +24,9 @@ path = "src/lib.rs" path = "audio" version = "0.1.3" -# [dependencies.librespot-connect] -# path = "connect" -# version = "0.1.3" +[dependencies.librespot-connect] +path = "connect" +version = "0.1.3" [dependencies.librespot-core] path = "core" diff --git a/connect/Cargo.toml b/connect/Cargo.toml index b5cd4fd..1f73f01 100644 --- a/connect/Cargo.toml +++ b/connect/Cargo.toml @@ -19,8 +19,8 @@ version = "0.1.3" [dependencies] base64 = "0.13" -futures = "0.1" -hyper = "0.12" +futures = "0.3" +hyper = { version = "0.14", features = ["server", "http1"] } log = "0.4" num-bigint = "0.3" protobuf = "~2.14.0" @@ -28,7 +28,7 @@ rand = "0.7" serde = "1.0" serde_derive = "1.0" serde_json = "1.0" -tokio = "0.1" +tokio = { version = "1.0", features = ["macros"] } url = "1.7" sha-1 = "0.8" hmac = "0.7" @@ -36,7 +36,7 @@ aes-ctr = "0.3" block-modes = "0.3" dns-sd = { version = "0.1.3", optional = true } -libmdns = { version = "0.2.7", optional = true } +libmdns = { version = "0.6", optional = true } [features] diff --git a/connect/src/discovery.rs b/connect/src/discovery.rs index f9414ee..9559735 100644 --- a/connect/src/discovery.rs +++ b/connect/src/discovery.rs @@ -2,15 +2,17 @@ use aes_ctr::stream_cipher::generic_array::GenericArray; use aes_ctr::stream_cipher::{NewStreamCipher, SyncStreamCipher}; use aes_ctr::Aes128Ctr; use base64; -use futures::sync::mpsc; -use futures::{Future, Poll, Stream}; +use futures::channel::mpsc; +use futures::{Stream, StreamExt}; use hmac::{Hmac, Mac}; - -use hyper::{ - self, server::conn::Http, service::Service, Body, Method, Request, Response, StatusCode, -}; +use hyper::service::{make_service_fn, service_fn}; +use hyper::{Body, Method, Request, Response, StatusCode}; use sha1::{Digest, Sha1}; +use std::borrow::Cow; +use std::net::{Ipv4Addr, SocketAddr}; +use std::task::{Context, Poll}; + #[cfg(feature = "with-dns-sd")] use dns_sd::DNSService; @@ -21,8 +23,8 @@ use num_bigint::BigUint; use rand; use std::collections::BTreeMap; use std::io; +use std::pin::Pin; use std::sync::Arc; -use tokio::runtime::current_thread::Handle; use url; use librespot_core::authentication::Credentials; @@ -63,13 +65,8 @@ impl Discovery { (discovery, rx) } -} -impl Discovery { - fn handle_get_info( - &self, - _params: &BTreeMap, - ) -> ::futures::Finished, hyper::Error> { + fn handle_get_info(&self, _: BTreeMap, Cow<'_, str>>) -> Response { let public_key = self.0.public_key.to_bytes_be(); let public_key = base64::encode(&public_key); @@ -93,20 +90,20 @@ impl Discovery { }); let body = result.to_string(); - ::futures::finished(Response::new(Body::from(body))) + Response::new(Body::from(body)) } fn handle_add_user( &self, - params: &BTreeMap, - ) -> ::futures::Finished, hyper::Error> { - let username = params.get("userName").unwrap(); + params: BTreeMap, Cow<'_, str>>, + ) -> Response { + let username = params.get("userName").unwrap().as_ref(); let encrypted_blob = params.get("blob").unwrap(); let client_key = params.get("clientKey").unwrap(); - let encrypted_blob = base64::decode(encrypted_blob).unwrap(); + let encrypted_blob = base64::decode(encrypted_blob.as_bytes()).unwrap(); - let client_key = base64::decode(client_key).unwrap(); + let client_key = base64::decode(client_key.as_bytes()).unwrap(); let client_key = BigUint::from_bytes_be(&client_key); let shared_key = util::powm(&client_key, &self.0.private_key, &DH_PRIME); @@ -141,7 +138,7 @@ impl Discovery { }); let body = result.to_string(); - return ::futures::finished(Response::new(Body::from(body))); + return Response::new(Body::from(body)); } let decrypted = { @@ -155,7 +152,7 @@ impl Discovery { }; let credentials = - Credentials::with_blob(username.to_owned(), &decrypted, &self.0.device_id); + Credentials::with_blob(username.to_string(), &decrypted, &self.0.device_id); self.0.tx.unbounded_send(credentials).unwrap(); @@ -166,52 +163,39 @@ impl Discovery { }); let body = result.to_string(); - return ::futures::finished(Response::new(Body::from(body))); + Response::new(Body::from(body)) } - fn not_found(&self) -> ::futures::Finished, hyper::Error> { + fn not_found(&self) -> Response { let mut res = Response::default(); *res.status_mut() = StatusCode::NOT_FOUND; - ::futures::finished(res) + res } -} -impl Service for Discovery { - type ReqBody = Body; - type ResBody = Body; - type Error = hyper::Error; - - type Future = Box, Error = hyper::Error> + Send>; - fn call(&mut self, request: Request<(Self::ReqBody)>) -> Self::Future { + async fn call(self, request: Request) -> hyper::Result> { let mut params = BTreeMap::new(); let (parts, body) = request.into_parts(); if let Some(query) = parts.uri.query() { - params.extend(url::form_urlencoded::parse(query.as_bytes()).into_owned()); + let query_params = url::form_urlencoded::parse(query.as_bytes()); + params.extend(query_params); } if parts.method != Method::GET { debug!("{:?} {:?} {:?}", parts.method, parts.uri.path(), params); } - let this = self.clone(); - Box::new( - body.fold(Vec::new(), |mut acc, chunk| { - acc.extend_from_slice(chunk.as_ref()); - Ok::<_, hyper::Error>(acc) - }) - .map(move |body| { - params.extend(url::form_urlencoded::parse(&body).into_owned()); - params - }) - .and_then(move |params| { - match (parts.method, params.get("action").map(AsRef::as_ref)) { - (Method::GET, Some("getInfo")) => this.handle_get_info(¶ms), - (Method::POST, Some("addUser")) => this.handle_add_user(¶ms), - _ => this.not_found(), - } - }), + let body = hyper::body::to_bytes(body).await?; + + params.extend(url::form_urlencoded::parse(&body)); + + Ok( + match (parts.method, params.get("action").map(AsRef::as_ref)) { + (Method::GET, Some("getInfo")) => self.handle_get_info(params), + (Method::POST, Some("addUser")) => self.handle_add_user(params), + _ => self.not_found(), + }, ) } } @@ -229,39 +213,25 @@ pub struct DiscoveryStream { } pub fn discovery( - handle: &Handle, config: ConnectConfig, device_id: String, port: u16, ) -> io::Result { let (discovery, creds_rx) = Discovery::new(config.clone(), device_id); - let serve = { - let http = Http::new(); - http.serve_addr(&format!("0.0.0.0:{}", port).parse().unwrap(), move || { - Ok(discovery.clone()) - }) - .unwrap() - }; + let address = SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), port); - let s_port = serve.incoming_ref().local_addr().port(); + let make_service = make_service_fn(move |_| { + let discovery = discovery.clone(); + async move { Ok::<_, hyper::Error>(service_fn(move |request| discovery.clone().call(request))) } + }); + + let server = hyper::Server::bind(&address).serve(make_service); + + let s_port = server.local_addr().port(); debug!("Zeroconf server listening on 0.0.0.0:{}", s_port); - let server_future = { - let handle = handle.clone(); - serve - .for_each( - move |connecting: hyper::server::conn::Connecting< - hyper::server::conn::AddrStream, - futures::Failed<_, hyper::Error>, - >| { - handle.spawn(connecting.flatten().then(|_| Ok(()))).unwrap(); - Ok(()) - }, - ) - .then(|_| Ok(())) - }; - handle.spawn(server_future).unwrap(); + tokio::spawn(server); #[cfg(feature = "with-dns-sd")] let svc = DNSService::register( @@ -275,7 +245,7 @@ pub fn discovery( .unwrap(); #[cfg(not(feature = "with-dns-sd"))] - let responder = libmdns::Responder::spawn(&handle)?; + let responder = libmdns::Responder::spawn(&tokio::runtime::Handle::current())?; #[cfg(not(feature = "with-dns-sd"))] let svc = responder.register( @@ -293,9 +263,8 @@ pub fn discovery( impl Stream for DiscoveryStream { type Item = Credentials; - type Error = (); - fn poll(&mut self) -> Poll, Self::Error> { - self.credentials.poll() + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.credentials.poll_next_unpin(cx) } } diff --git a/connect/src/spirc.rs b/connect/src/spirc.rs index 352a3fc..5dc8959 100644 --- a/connect/src/spirc.rs +++ b/connect/src/spirc.rs @@ -1,19 +1,15 @@ -use std; +use std::pin::Pin; use std::time::{SystemTime, UNIX_EPOCH}; -use futures::future; -use futures::sync::mpsc; -use futures::{Async, Future, Poll, Sink, Stream}; -use protobuf::{self, Message}; -use rand; -use rand::seq::SliceRandom; -use serde_json; - use crate::context::StationContext; use crate::playback::mixer::Mixer; use crate::playback::player::{Player, PlayerEvent, PlayerEventChannel}; use crate::protocol; use crate::protocol::spirc::{DeviceState, Frame, MessageType, PlayStatus, State, TrackRef}; +use futures::channel::mpsc; +use futures::future::{self, FusedFuture}; +use futures::stream::FusedStream; +use futures::{Future, FutureExt, Sink, SinkExt, StreamExt}; use librespot_core::config::{ConnectConfig, VolumeCtrl}; use librespot_core::mercury::MercuryError; use librespot_core::session::Session; @@ -21,6 +17,10 @@ use librespot_core::spotify_id::{SpotifyAudioType, SpotifyId, SpotifyIdError}; use librespot_core::util::url_encode; use librespot_core::util::SeqGenerator; use librespot_core::version; +use protobuf::{self, Message}; +use rand; +use rand::seq::SliceRandom; +use serde_json; enum SpircPlayStatus { Stopped, @@ -40,7 +40,11 @@ enum SpircPlayStatus { }, } -pub struct SpircTask { +type BoxedFuture = Pin + Send>>; +type BoxedStream = Pin + Send>>; +type BoxedSink = Pin + Send>>; + +struct SpircTask { player: Player, mixer: Box, config: SpircTaskConfig, @@ -54,15 +58,16 @@ pub struct SpircTask { mixer_started: bool, play_status: SpircPlayStatus, - subscription: Box>, - sender: Box>, + sender_flushed: bool, + subscription: BoxedStream, + sender: BoxedSink, commands: mpsc::UnboundedReceiver, player_events: PlayerEventChannel, shutdown: bool, session: Session, - context_fut: Box>, - autoplay_fut: Box>, + context_fut: BoxedFuture>, + autoplay_fut: BoxedFuture>, context: Option, } @@ -246,7 +251,7 @@ impl Spirc { session: Session, player: Player, mixer: Box, - ) -> (Spirc, SpircTask) { + ) -> (Spirc, impl Future) { debug!("new Spirc[{}]", session.session_id()); let ident = session.device_id().to_owned(); @@ -255,20 +260,23 @@ impl Spirc { debug!("canonical_username: {}", url_encode(&session.username())); let uri = format!("hm://remote/user/{}/", url_encode(&session.username())); - let subscription = session.mercury().subscribe(&uri as &str); - let subscription = subscription - .map(|stream| stream.map_err(|_| MercuryError)) - .flatten_stream(); - let subscription = Box::new(subscription.map(|response| -> Frame { - let data = response.payload.first().unwrap(); - protobuf::parse_from_bytes(data).unwrap() - })); + let subscription = Box::pin( + session + .mercury() + .subscribe(uri.clone()) + .map(Result::unwrap) + .flatten_stream() + .map(|response| -> Frame { + let data = response.payload.first().unwrap(); + protobuf::parse_from_bytes(data).unwrap() + }), + ); - let sender = Box::new( + let sender = Box::pin( session .mercury() .sender(uri) - .with(|frame: Frame| Ok(frame.write_to_bytes().unwrap())), + .with(|frame: Frame| future::ready(Ok(frame.write_to_bytes().unwrap()))), ); let (cmd_tx, cmd_rx) = mpsc::unbounded(); @@ -303,11 +311,12 @@ impl Spirc { commands: cmd_rx, player_events: player_events, + sender_flushed: true, shutdown: false, - session: session.clone(), + session: session, - context_fut: Box::new(future::empty()), - autoplay_fut: Box::new(future::empty()), + context_fut: Box::pin(future::pending()), + autoplay_fut: Box::pin(future::pending()), context: None, }; @@ -317,7 +326,7 @@ impl Spirc { task.hello(); - (spirc, task) + (spirc, task.run()) } pub fn play(&self) { @@ -346,114 +355,80 @@ impl Spirc { } } -impl Future for SpircTask { - type Item = (); - type Error = (); - - fn poll(&mut self) -> Poll<(), ()> { - loop { - let mut progress = false; - - if self.session.is_invalid() { - return Ok(Async::Ready(())); - } - - if !self.shutdown { - match self.subscription.poll().unwrap() { - Async::Ready(Some(frame)) => { - progress = true; - self.handle_frame(frame); - } - Async::Ready(None) => { +impl SpircTask { + async fn run(mut self) { + while !self.session.is_invalid() && !self.shutdown { + tokio::select! { + frame = self.subscription.next() => match frame { + Some(frame) => self.handle_frame(frame), + None => { error!("subscription terminated"); - self.shutdown = true; - self.commands.close(); + break; } - Async::NotReady => (), - } - - match self.commands.poll().unwrap() { - Async::Ready(Some(command)) => { - progress = true; - self.handle_command(command); + }, + cmd = self.commands.next(), if !self.commands.is_terminated() => if let Some(cmd) = cmd { + self.handle_command(cmd); + }, + event = self.player_events.next(), if !self.player_events.is_terminated() => if let Some(event) = event { + self.handle_player_event(event) + }, + result = self.sender.flush(), if !self.sender_flushed => { + if result.is_err() { + error!("Cannot flush spirc event sender."); + break; } - Async::Ready(None) => (), - Async::NotReady => (), - } - match self.player_events.poll() { - Ok(Async::NotReady) => (), - Ok(Async::Ready(None)) => (), - Err(_) => (), - Ok(Async::Ready(Some(event))) => { - progress = true; - self.handle_player_event(event); + self.sender_flushed = true; + }, + context = &mut self.context_fut, if !self.context_fut.is_terminated() => { + match context { + Ok(value) => { + let r_context = serde_json::from_value::(value); + self.context = match r_context { + Ok(context) => { + info!( + "Resolved {:?} tracks from <{:?}>", + context.tracks.len(), + self.state.get_context_uri(), + ); + Some(context) + } + Err(e) => { + error!("Unable to parse JSONContext {:?}", e); + None + } + }; + // It needn't be so verbose - can be as simple as + // if let Some(ref context) = r_context { + // info!("Got {:?} tracks from <{}>", context.tracks.len(), context.uri); + // } + // self.context = r_context; + }, + Err(err) => { + error!("ContextError: {:?}", err) + } } - } - // TODO: Refactor - match self.context_fut.poll() { - Ok(Async::Ready(value)) => { - let r_context = serde_json::from_value::(value.clone()); - self.context = match r_context { - Ok(context) => { - info!( - "Resolved {:?} tracks from <{:?}>", - context.tracks.len(), - self.state.get_context_uri(), - ); - Some(context) - } - Err(e) => { - error!("Unable to parse JSONContext {:?}\n{:?}", e, value); - None - } - }; - // It needn't be so verbose - can be as simple as - // if let Some(ref context) = r_context { - // info!("Got {:?} tracks from <{}>", context.tracks.len(), context.uri); - // } - // self.context = r_context; - - progress = true; - self.context_fut = Box::new(future::empty()); + }, + autoplay = &mut self.autoplay_fut, if !self.autoplay_fut.is_terminated() => { + match autoplay { + Ok(autoplay_station_uri) => { + info!("Autoplay uri resolved to <{:?}>", autoplay_station_uri); + self.context_fut = self.resolve_station(&autoplay_station_uri); + }, + Err(err) => { + error!("AutoplayError: {:?}", err) + } } - Ok(Async::NotReady) => (), - Err(err) => { - self.context_fut = Box::new(future::empty()); - error!("ContextError: {:?}", err) - } - } - - match self.autoplay_fut.poll() { - Ok(Async::Ready(autoplay_station_uri)) => { - info!("Autoplay uri resolved to <{:?}>", autoplay_station_uri); - self.context_fut = self.resolve_station(&autoplay_station_uri); - progress = true; - self.autoplay_fut = Box::new(future::empty()); - } - Ok(Async::NotReady) => (), - Err(err) => { - self.autoplay_fut = Box::new(future::empty()); - error!("AutoplayError: {:?}", err) - } - } - } - - let poll_sender = self.sender.poll_complete().unwrap(); - - // Only shutdown once we've flushed out all our messages - if self.shutdown && poll_sender.is_ready() { - return Ok(Async::Ready(())); - } - - if !progress { - return Ok(Async::NotReady); + }, + else => break } } - } -} -impl SpircTask { + if self.sender.close().await.is_err() { + warn!("Cannot close spirc event sender."); + } + } + fn now_ms(&mut self) -> i64 { let dur = match SystemTime::now().duration_since(UNIX_EPOCH) { Ok(dur) => dur, @@ -1060,52 +1035,53 @@ impl SpircTask { } } - fn resolve_station( - &self, - uri: &str, - ) -> Box> { + fn resolve_station(&self, uri: &str) -> BoxedFuture> { let radio_uri = format!("hm://radio-apollo/v3/stations/{}", uri); self.resolve_uri(&radio_uri) } - fn resolve_autoplay_uri( - &self, - uri: &str, - ) -> Box> { + fn resolve_autoplay_uri(&self, uri: &str) -> BoxedFuture> { let query_uri = format!("hm://autoplay-enabled/query?uri={}", uri); let request = self.session.mercury().get(query_uri); - Box::new(request.and_then(move |response| { - if response.status_code == 200 { + Box::pin( + async { + let response = request.await?; + + if response.status_code == 200 { + let data = response + .payload + .first() + .expect("Empty autoplay uri") + .to_vec(); + let autoplay_uri = String::from_utf8(data).unwrap(); + Ok(autoplay_uri) + } else { + warn!("No autoplay_uri found"); + Err(MercuryError) + } + } + .fuse(), + ) + } + + fn resolve_uri(&self, uri: &str) -> BoxedFuture> { + let request = self.session.mercury().get(uri); + + Box::pin( + async move { + let response = request.await?; + let data = response .payload .first() - .expect("Empty autoplay uri") - .to_vec(); - let autoplay_uri = String::from_utf8(data).unwrap(); - Ok(autoplay_uri) - } else { - warn!("No autoplay_uri found"); - Err(MercuryError) + .expect("Empty payload on context uri"); + let response: serde_json::Value = serde_json::from_slice(&data).unwrap(); + + Ok(response) } - })) - } - - fn resolve_uri( - &self, - uri: &str, - ) -> Box> { - let request = self.session.mercury().get(uri); - - Box::new(request.and_then(move |response| { - let data = response - .payload - .first() - .expect("Empty payload on context uri"); - let response: serde_json::Value = serde_json::from_slice(&data).unwrap(); - - Ok(response) - })) + .fuse(), + ) } fn update_tracks_from_context(&mut self) { @@ -1344,8 +1320,14 @@ impl<'a> CommandSender<'a> { if !self.frame.has_state() && self.spirc.device.get_is_active() { self.frame.set_state(self.spirc.state.clone()); } + let sender = &mut self.spirc.sender; - let send = self.spirc.sender.start_send(self.frame).unwrap(); - assert!(send.is_ready()); + future::poll_fn(|cx| sender.as_mut().poll_ready(cx)) + .now_or_never() + .unwrap() + .unwrap(); + + sender.as_mut().start_send(self.frame).unwrap(); + self.spirc.sender_flushed = false; } } diff --git a/core/src/mercury/mod.rs b/core/src/mercury/mod.rs index 72360c9..4baa674 100644 --- a/core/src/mercury/mod.rs +++ b/core/src/mercury/mod.rs @@ -102,46 +102,48 @@ impl MercuryManager { MercurySender::new(self.clone(), uri.into()) } - pub async fn subscribe>( + pub fn subscribe>( &self, uri: T, - ) -> Result, MercuryError> { + ) -> impl Future, MercuryError>> + 'static + { let uri = uri.into(); - let response = self - .request(MercuryRequest { - method: MercuryMethod::SUB, - uri: uri.clone(), - content_type: None, - payload: Vec::new(), - }) - .await?; - - let (tx, rx) = mpsc::unbounded(); - - let manager = self.clone(); - - manager.lock(move |inner| { - if !inner.invalid { - debug!("subscribed uri={} count={}", uri, response.payload.len()); - if !response.payload.is_empty() { - // Old subscription protocol, watch the provided list of URIs - for sub in response.payload { - let mut sub: protocol::pubsub::Subscription = - protobuf::parse_from_bytes(&sub).unwrap(); - let sub_uri = sub.take_uri(); - - debug!("subscribed sub_uri={}", sub_uri); - - inner.subscriptions.push((sub_uri, tx.clone())); - } - } else { - // New subscription protocol, watch the requested URI - inner.subscriptions.push((uri, tx)); - } - } + let request = self.request(MercuryRequest { + method: MercuryMethod::SUB, + uri: uri.clone(), + content_type: None, + payload: Vec::new(), }); - Ok(rx) + let manager = self.clone(); + async move { + let response = request.await?; + + let (tx, rx) = mpsc::unbounded(); + + manager.lock(move |inner| { + if !inner.invalid { + debug!("subscribed uri={} count={}", uri, response.payload.len()); + if !response.payload.is_empty() { + // Old subscription protocol, watch the provided list of URIs + for sub in response.payload { + let mut sub: protocol::pubsub::Subscription = + protobuf::parse_from_bytes(&sub).unwrap(); + let sub_uri = sub.take_uri(); + + debug!("subscribed sub_uri={}", sub_uri); + + inner.subscriptions.push((sub_uri, tx.clone())); + } + } else { + // New subscription protocol, watch the requested URI + inner.subscriptions.push((uri, tx)); + } + } + }); + + Ok(rx) + } } pub(crate) fn dispatch(&self, cmd: u8, mut data: Bytes) { diff --git a/src/lib.rs b/src/lib.rs index 4304e18..7cdd317 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,7 @@ #![crate_name = "librespot"] pub extern crate librespot_audio as audio; -// pub extern crate librespot_connect as connect; +pub extern crate librespot_connect as connect; pub extern crate librespot_core as core; pub extern crate librespot_metadata as metadata; pub extern crate librespot_playback as playback; From 2c81aaaf4e440a41ca5c2a37f0f760e70958951b Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Sat, 20 Feb 2021 20:59:57 +0100 Subject: [PATCH 031/103] Implement MercurySender not as sink --- connect/src/spirc.rs | 39 +++++++-------------------- core/src/mercury/sender.rs | 55 ++++++++++++-------------------------- 2 files changed, 27 insertions(+), 67 deletions(-) diff --git a/connect/src/spirc.rs b/connect/src/spirc.rs index 5dc8959..2e3694e 100644 --- a/connect/src/spirc.rs +++ b/connect/src/spirc.rs @@ -9,9 +9,9 @@ use crate::protocol::spirc::{DeviceState, Frame, MessageType, PlayStatus, State, use futures::channel::mpsc; use futures::future::{self, FusedFuture}; use futures::stream::FusedStream; -use futures::{Future, FutureExt, Sink, SinkExt, StreamExt}; +use futures::{Future, FutureExt, StreamExt}; use librespot_core::config::{ConnectConfig, VolumeCtrl}; -use librespot_core::mercury::MercuryError; +use librespot_core::mercury::{MercuryError, MercurySender}; use librespot_core::session::Session; use librespot_core::spotify_id::{SpotifyAudioType, SpotifyId, SpotifyIdError}; use librespot_core::util::url_encode; @@ -42,7 +42,6 @@ enum SpircPlayStatus { type BoxedFuture = Pin + Send>>; type BoxedStream = Pin + Send>>; -type BoxedSink = Pin + Send>>; struct SpircTask { player: Player, @@ -58,9 +57,8 @@ struct SpircTask { mixer_started: bool, play_status: SpircPlayStatus, - sender_flushed: bool, subscription: BoxedStream, - sender: BoxedSink, + sender: MercurySender, commands: mpsc::UnboundedReceiver, player_events: PlayerEventChannel, @@ -272,12 +270,7 @@ impl Spirc { }), ); - let sender = Box::pin( - session - .mercury() - .sender(uri) - .with(|frame: Frame| future::ready(Ok(frame.write_to_bytes().unwrap()))), - ); + let sender = session.mercury().sender(uri); let (cmd_tx, cmd_rx) = mpsc::unbounded(); @@ -311,7 +304,6 @@ impl Spirc { commands: cmd_rx, player_events: player_events, - sender_flushed: true, shutdown: false, session: session, @@ -372,13 +364,9 @@ impl SpircTask { event = self.player_events.next(), if !self.player_events.is_terminated() => if let Some(event) = event { self.handle_player_event(event) }, - result = self.sender.flush(), if !self.sender_flushed => { - if result.is_err() { - error!("Cannot flush spirc event sender."); - break; - } - - self.sender_flushed = true; + result = self.sender.flush(), if !self.sender.is_flushed() => if result.is_err() { + error!("Cannot flush spirc event sender."); + break; }, context = &mut self.context_fut, if !self.context_fut.is_terminated() => { match context { @@ -424,8 +412,8 @@ impl SpircTask { } } - if self.sender.close().await.is_err() { - warn!("Cannot close spirc event sender."); + if self.sender.flush().await.is_err() { + warn!("Cannot flush spirc event sender."); } } @@ -1320,14 +1308,7 @@ impl<'a> CommandSender<'a> { if !self.frame.has_state() && self.spirc.device.get_is_active() { self.frame.set_state(self.spirc.state.clone()); } - let sender = &mut self.spirc.sender; - future::poll_fn(|cx| sender.as_mut().poll_ready(cx)) - .now_or_never() - .unwrap() - .unwrap(); - - sender.as_mut().start_send(self.frame).unwrap(); - self.spirc.sender_flushed = false; + self.spirc.sender.send(self.frame.write_to_bytes().unwrap()); } } diff --git a/core/src/mercury/sender.rs b/core/src/mercury/sender.rs index 860c2f3..e276bcf 100644 --- a/core/src/mercury/sender.rs +++ b/core/src/mercury/sender.rs @@ -1,5 +1,4 @@ -use futures::Sink; -use std::{collections::VecDeque, pin::Pin, task::Context}; +use std::collections::VecDeque; use super::*; @@ -18,6 +17,22 @@ impl MercurySender { pending: VecDeque::new(), } } + + pub fn is_flushed(&self) -> bool { + self.pending.is_empty() + } + + pub fn send(&mut self, item: Vec) { + let task = self.mercury.send(self.uri.clone(), item); + self.pending.push_back(task); + } + + pub async fn flush(&mut self) -> Result<(), MercuryError> { + for fut in self.pending.drain(..) { + fut.await?; + } + Ok(()) + } } impl Clone for MercurySender { @@ -29,39 +44,3 @@ impl Clone for MercurySender { } } } - -impl Sink> for MercurySender { - type Error = MercuryError; - - fn poll_ready(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } - - fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.poll_flush(cx) - } - - fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - loop { - match self.pending.front_mut() { - Some(task) => { - match Pin::new(task).poll(cx) { - Poll::Ready(Err(x)) => return Poll::Ready(Err(x)), - Poll::Pending => return Poll::Pending, - _ => (), - }; - } - None => { - return Poll::Ready(Ok(())); - } - } - self.pending.pop_front(); - } - } - - fn start_send(mut self: Pin<&mut Self>, item: Vec) -> Result<(), Self::Error> { - let task = self.mercury.send(self.uri.clone(), item); - self.pending.push_back(task); - Ok(()) - } -} From 1c4d57c6da9631397021b822ef24624374648c68 Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Sat, 20 Feb 2021 21:26:52 +0100 Subject: [PATCH 032/103] Add shutdown to discovery server --- connect/src/discovery.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/connect/src/discovery.rs b/connect/src/discovery.rs index 9559735..62310b2 100644 --- a/connect/src/discovery.rs +++ b/connect/src/discovery.rs @@ -2,7 +2,7 @@ use aes_ctr::stream_cipher::generic_array::GenericArray; use aes_ctr::stream_cipher::{NewStreamCipher, SyncStreamCipher}; use aes_ctr::Aes128Ctr; use base64; -use futures::channel::mpsc; +use futures::channel::{mpsc, oneshot}; use futures::{Stream, StreamExt}; use hmac::{Hmac, Mac}; use hyper::service::{make_service_fn, service_fn}; @@ -10,6 +10,7 @@ use hyper::{Body, Method, Request, Response, StatusCode}; use sha1::{Digest, Sha1}; use std::borrow::Cow; +use std::convert::Infallible; use std::net::{Ipv4Addr, SocketAddr}; use std::task::{Context, Poll}; @@ -204,12 +205,14 @@ impl Discovery { pub struct DiscoveryStream { credentials: mpsc::UnboundedReceiver, _svc: DNSService, + _close_tx: oneshot::Sender, } #[cfg(not(feature = "with-dns-sd"))] pub struct DiscoveryStream { credentials: mpsc::UnboundedReceiver, _svc: libmdns::Service, + _close_tx: oneshot::Sender, } pub fn discovery( @@ -218,6 +221,7 @@ pub fn discovery( port: u16, ) -> io::Result { let (discovery, creds_rx) = Discovery::new(config.clone(), device_id); + let (close_tx, close_rx) = oneshot::channel(); let address = SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), port); @@ -231,7 +235,10 @@ pub fn discovery( let s_port = server.local_addr().port(); debug!("Zeroconf server listening on 0.0.0.0:{}", s_port); - tokio::spawn(server); + tokio::spawn(server.with_graceful_shutdown(async { + close_rx.await.unwrap_err(); + debug!("Shutting down discovery server"); + })); #[cfg(feature = "with-dns-sd")] let svc = DNSService::register( @@ -258,6 +265,7 @@ pub fn discovery( Ok(DiscoveryStream { credentials: creds_rx, _svc: svc, + _close_tx: close_tx, }) } From 007e653f3d2f3f84ab6c88c847ee84d2a35d7df1 Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Sat, 20 Feb 2021 22:14:15 +0100 Subject: [PATCH 033/103] Restore original blocking player behaviour --- core/src/session.rs | 15 ++++- playback/src/player.rs | 130 ++++++++++++++++++++--------------------- 2 files changed, 78 insertions(+), 67 deletions(-) diff --git a/core/src/session.rs b/core/src/session.rs index fd70679..b0eca0c 100644 --- a/core/src/session.rs +++ b/core/src/session.rs @@ -39,6 +39,8 @@ struct SessionInternal { mercury: OnceCell, cache: Option>, + handle: tokio::runtime::Handle, + session_id: usize, } @@ -65,7 +67,13 @@ impl Session { cache.save_credentials(&reusable_credentials); } - let session = Session::create(conn, config, cache, reusable_credentials.username); + let session = Session::create( + conn, + config, + cache, + reusable_credentials.username, + tokio::runtime::Handle::current(), + ); Ok(session) } @@ -75,6 +83,7 @@ impl Session { config: SessionConfig, cache: Option, username: String, + handle: tokio::runtime::Handle, ) -> Session { let (sink, stream) = transport.split(); @@ -100,6 +109,8 @@ impl Session { channel: OnceCell::new(), mercury: OnceCell::new(), + handle, + session_id: session_id, })); @@ -139,7 +150,7 @@ impl Session { T: Future + Send + 'static, T::Output: Send + 'static, { - tokio::spawn(task); + self.0.handle.spawn(task); } fn debug_info(&self) { diff --git a/playback/src/player.rs b/playback/src/player.rs index 6f6a85a..3ee5c98 100644 --- a/playback/src/player.rs +++ b/playback/src/player.rs @@ -7,7 +7,6 @@ use crate::audio::{ use crate::audio_backend::Sink; use crate::config::NormalisationType; use crate::config::{Bitrate, PlayerConfig}; -use crate::librespot_core::tokio; use crate::metadata::{AudioItem, FileFormat}; use crate::mixer::AudioFilter; use librespot_core::session::Session; @@ -15,25 +14,22 @@ use librespot_core::spotify_id::SpotifyId; use librespot_core::util::SeqGenerator; use byteorder::{LittleEndian, ReadBytesExt}; -use futures::{ - channel::{mpsc, oneshot}, - future, Future, Stream, StreamExt, -}; -use std::io::{Read, Seek, SeekFrom}; -use std::mem; +use futures::channel::{mpsc, oneshot}; +use futures::{future, Future, Stream, StreamExt, TryFutureExt}; +use std::borrow::Cow; + +use std::cmp::max; +use std::io::{self, Read, Seek, SeekFrom}; +use std::pin::Pin; +use std::task::{Context, Poll}; use std::time::{Duration, Instant}; -use std::{borrow::Cow, io}; -use std::{ - cmp::max, - pin::Pin, - task::{Context, Poll}, -}; +use std::{mem, thread}; const PRELOAD_NEXT_TRACK_BEFORE_END_DURATION_MS: u32 = 30000; pub struct Player { commands: Option>, - task_handle: Option>, + thread_handle: Option>, play_request_id_generator: SeqGenerator, } @@ -251,33 +247,33 @@ impl Player { let (cmd_tx, cmd_rx) = mpsc::unbounded(); let (event_sender, event_receiver) = mpsc::unbounded(); - debug!("new Player[{}]", session.session_id()); + let handle = thread::spawn(move || { + debug!("new Player[{}]", session.session_id()); - let internal = PlayerInternal { - session: session, - config: config, - commands: cmd_rx, + let internal = PlayerInternal { + session: session, + config: config, + commands: cmd_rx, - state: PlayerState::Stopped, - preload: PlayerPreload::None, - sink: sink_builder(), - sink_status: SinkStatus::Closed, - sink_event_callback: None, - audio_filter: audio_filter, - event_senders: [event_sender].to_vec(), - }; + state: PlayerState::Stopped, + preload: PlayerPreload::None, + sink: sink_builder(), + sink_status: SinkStatus::Closed, + sink_event_callback: None, + audio_filter: audio_filter, + event_senders: [event_sender].to_vec(), + }; - // While PlayerInternal is written as a future, it still contains blocking code. - // It must be run by using wait() in a dedicated thread. - let handle = tokio::spawn(async move { - internal.await; + // While PlayerInternal is written as a future, it still contains blocking code. + // It must be run by using wait() in a dedicated thread. + futures::executor::block_on(internal); debug!("PlayerInternal thread finished."); }); ( Player { commands: Some(cmd_tx), - task_handle: Some(handle), + thread_handle: Some(handle), play_request_id_generator: SeqGenerator::new(0), }, event_receiver, @@ -351,13 +347,11 @@ impl Drop for Player { fn drop(&mut self) { debug!("Shutting down player thread ..."); self.commands = None; - if let Some(handle) = self.task_handle.take() { - tokio::spawn(async { - match handle.await { - Ok(_) => (), - Err(_) => error!("Player thread panicked!"), - } - }); + if let Some(handle) = self.thread_handle.take() { + match handle.join() { + Ok(_) => (), + Err(_) => error!("Player thread panicked!"), + } } } } @@ -436,15 +430,23 @@ impl PlayerState { #[allow(dead_code)] fn is_stopped(&self) -> bool { - matches!(self, Self::Stopped) + use self::PlayerState::*; + match *self { + Stopped => true, + _ => false, + } } fn is_loading(&self) -> bool { - matches!(self, Self::Loading { .. }) + use self::PlayerState::*; + match *self { + Loading { .. } => true, + _ => false, + } } fn decoder(&mut self) -> Option<&mut Decoder> { - use PlayerState::*; + use self::PlayerState::*; match *self { Stopped | EndOfTrack { .. } | Loading { .. } => None, Paused { @@ -1243,9 +1245,10 @@ impl PlayerInternal { loaded_track .stream_loader_controller .set_random_access_mode(); - let _ = tokio::task::block_in_place(|| { - loaded_track.decoder.seek(position_ms as i64) - }); + let _ = loaded_track.decoder.seek(position_ms as i64); // This may be blocking. + // But most likely the track is fully + // loaded already because we played + // to the end of it. loaded_track.stream_loader_controller.set_stream_mode(); loaded_track.stream_position_pcm = Self::position_ms_to_pcm(position_ms); } @@ -1278,7 +1281,7 @@ impl PlayerInternal { // we can use the current decoder. Ensure it's at the correct position. if Self::position_ms_to_pcm(position_ms) != *stream_position_pcm { stream_loader_controller.set_random_access_mode(); - let _ = tokio::task::block_in_place(|| decoder.seek(position_ms as i64)); + let _ = decoder.seek(position_ms as i64); // This may be blocking. stream_loader_controller.set_stream_mode(); *stream_position_pcm = Self::position_ms_to_pcm(position_ms); } @@ -1346,9 +1349,7 @@ impl PlayerInternal { loaded_track .stream_loader_controller .set_random_access_mode(); - let _ = tokio::task::block_in_place(|| { - loaded_track.decoder.seek(position_ms as i64) - }); + let _ = loaded_track.decoder.seek(position_ms as i64); // This may be blocking loaded_track.stream_loader_controller.set_stream_mode(); } self.start_playback(track_id, play_request_id, *loaded_track, play); @@ -1563,7 +1564,7 @@ impl PlayerInternal { } } - pub fn load_track( + fn load_track( &self, spotify_id: SpotifyId, position_ms: u32, @@ -1574,22 +1575,23 @@ impl PlayerInternal { // easily. Instead we spawn a thread to do the work and return a one-shot channel as the // future to work with. - let session = self.session.clone(); - let config = self.config.clone(); + let loader = PlayerTrackLoader { + session: self.session.clone(), + config: self.config.clone(), + }; - async move { - let loader = PlayerTrackLoader { session, config }; + let (result_tx, result_rx) = oneshot::channel(); - let (result_tx, result_rx) = oneshot::channel(); - - tokio::spawn(async move { - if let Some(data) = loader.load_track(spotify_id, position_ms).await { + std::thread::spawn(move || { + futures::executor::block_on(loader.load_track(spotify_id, position_ms)).and_then( + move |data| { let _ = result_tx.send(data); - } - }); + Some(()) + }, + ); + }); - result_rx.await.map_err(|_| ()) - } + result_rx.map_err(|_| ()) } fn preload_data_before_playback(&mut self) { @@ -1615,9 +1617,7 @@ impl PlayerInternal { * bytes_per_second as f64) as usize, (READ_AHEAD_BEFORE_PLAYBACK_SECONDS * bytes_per_second as f64) as usize, ); - tokio::task::block_in_place(|| { - stream_loader_controller.fetch_next_blocking(wait_for_data_length) - }); + stream_loader_controller.fetch_next_blocking(wait_for_data_length); } } } From 220061e1581dd1c6889f8d302b3f747f5a68c8d4 Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Sun, 21 Feb 2021 11:08:34 +0100 Subject: [PATCH 034/103] Migrate application to tokio 1.0 --- Cargo.lock | 57 ++++ Cargo.toml | 24 +- src/main.rs | 600 ++++++++++++++++++++++++++++++++++++ src/player_event_handler.rs | 36 ++- 4 files changed, 697 insertions(+), 20 deletions(-) create mode 100644 src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 6b6e7af..50234e9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -805,6 +805,15 @@ dependencies = [ "version_check", ] +[[package]] +name = "getopts" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" +dependencies = [ + "unicode-width", +] + [[package]] name = "getrandom" version = "0.1.16" @@ -1037,6 +1046,12 @@ dependencies = [ "libc", ] +[[package]] +name = "hex" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35" + [[package]] name = "hmac" version = "0.7.1" @@ -1397,12 +1412,26 @@ dependencies = [ name = "librespot" version = "0.1.3" dependencies = [ + "base64 0.13.0", + "env_logger", + "futures", + "getopts", + "hex", + "hyper", "librespot-audio", "librespot-connect", "librespot-core", "librespot-metadata", "librespot-playback", "librespot-protocol", + "log", + "num-bigint", + "protobuf", + "rand 0.7.3", + "rpassword", + "sha-1", + "tokio", + "url 1.7.2", ] [[package]] @@ -2314,6 +2343,16 @@ dependencies = [ "cpal", ] +[[package]] +name = "rpassword" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffc936cf8a7ea60c58f030fd36a612a48f440610214dc54bc36431f9ea0c3efb" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "rustc-demangle" version = "0.1.18" @@ -2532,6 +2571,15 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" +[[package]] +name = "signal-hook-registry" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16f1d0fef1604ba8f7a073c7e701f213e056707210e9020af4528e0101ce11a6" +dependencies = [ + "libc", +] + [[package]] name = "slab" version = "0.4.2" @@ -2840,8 +2888,11 @@ dependencies = [ "memchr", "mio", "num_cpus", + "once_cell", "pin-project-lite", + "signal-hook-registry", "tokio-macros", + "winapi", ] [[package]] @@ -2956,6 +3007,12 @@ version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796" +[[package]] +name = "unicode-width" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" + [[package]] name = "unicode-xid" version = "0.2.1" diff --git a/Cargo.toml b/Cargo.toml index fa67c0f..e4f9c51 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,10 +15,10 @@ edition = "2018" name = "librespot" path = "src/lib.rs" -# [[bin]] -# name = "librespot" -# path = "src/main.rs" -# doc = false +[[bin]] +name = "librespot" +path = "src/main.rs" +doc = false [dependencies.librespot-audio] path = "audio" @@ -44,6 +44,22 @@ version = "0.1.3" path = "protocol" version = "0.1.3" +[dependencies] +base64 = "0.13" +env_logger = {version = "0.8", default-features = false, features = ["termcolor","humantime","atty"]} +futures = "0.3" +getopts = "0.2" +hyper = "0.14" +log = "0.4" +num-bigint = "0.3" +protobuf = "~2.14.0" +rand = "0.7" +rpassword = "5.0" +tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros", "signal", "process"] } +url = "1.7" +sha-1 = "0.8" +hex = "0.4" + [features] alsa-backend = ["librespot-playback/alsa-backend"] portaudio-backend = ["librespot-playback/portaudio-backend"] diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..1392c20 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,600 @@ +use futures::{channel::mpsc::UnboundedReceiver, future::FusedFuture, FutureExt, StreamExt}; +use librespot_playback::player::PlayerEvent; +use log::{error, info, warn}; +use sha1::{Digest, Sha1}; +use std::path::Path; +use std::process::exit; +use std::str::FromStr; +use std::{env, time::Instant}; +use std::{ + io::{stderr, Write}, + pin::Pin, +}; +use url::Url; + +use librespot::core::authentication::{get_credentials, Credentials}; +use librespot::core::cache::Cache; +use librespot::core::config::{ConnectConfig, DeviceType, SessionConfig, VolumeCtrl}; +use librespot::core::session::Session; +use librespot::core::version; + +use librespot::connect::spirc::Spirc; +use librespot::playback::audio_backend::{self, Sink, BACKENDS}; +use librespot::playback::config::{Bitrate, NormalisationType, PlayerConfig}; +use librespot::playback::mixer::{self, Mixer, MixerConfig}; +use librespot::playback::player::Player; + +mod player_event_handler; + +use player_event_handler::{emit_sink_event, run_program_on_events}; + +fn device_id(name: &str) -> String { + hex::encode(Sha1::digest(name.as_bytes())) +} + +fn usage(program: &str, opts: &getopts::Options) -> String { + let brief = format!("Usage: {} [options]", program); + opts.usage(&brief) +} + +fn setup_logging(verbose: bool) { + let mut builder = env_logger::Builder::new(); + match env::var("RUST_LOG") { + Ok(config) => { + builder.parse_filters(&config); + builder.init(); + + if verbose { + warn!("`--verbose` flag overidden by `RUST_LOG` environment variable"); + } + } + Err(_) => { + if verbose { + builder.parse_filters("libmdns=info,librespot=trace"); + } else { + builder.parse_filters("libmdns=info,librespot=info"); + } + builder.init(); + } + } +} + +fn list_backends() { + println!("Available Backends : "); + for (&(name, _), idx) in BACKENDS.iter().zip(0..) { + if idx == 0 { + println!("- {} (default)", name); + } else { + println!("- {}", name); + } + } +} + +#[derive(Clone)] +struct Setup { + backend: fn(Option) -> Box, + device: Option, + + mixer: fn(Option) -> Box, + + cache: Option, + player_config: PlayerConfig, + session_config: SessionConfig, + connect_config: ConnectConfig, + mixer_config: MixerConfig, + credentials: Option, + enable_discovery: bool, + zeroconf_port: u16, + player_event_program: Option, + emit_sink_events: bool, +} + +fn setup(args: &[String]) -> Setup { + let mut opts = getopts::Options::new(); + opts.optopt( + "c", + "cache", + "Path to a directory where files will be cached.", + "CACHE", + ).optopt( + "", + "system-cache", + "Path to a directory where system files (credentials, volume) will be cached. Can be different from cache option value", + "SYTEMCACHE", + ).optflag("", "disable-audio-cache", "Disable caching of the audio data.") + .reqopt("n", "name", "Device name", "NAME") + .optopt("", "device-type", "Displayed device type", "DEVICE_TYPE") + .optopt( + "b", + "bitrate", + "Bitrate (96, 160 or 320). Defaults to 160", + "BITRATE", + ) + .optopt( + "", + "onevent", + "Run PROGRAM when playback is about to begin.", + "PROGRAM", + ) + .optflag("", "emit-sink-events", "Run program set by --onevent before sink is opened and after it is closed.") + .optflag("v", "verbose", "Enable verbose output") + .optopt("u", "username", "Username to sign in with", "USERNAME") + .optopt("p", "password", "Password", "PASSWORD") + .optopt("", "proxy", "HTTP proxy to use when connecting", "PROXY") + .optopt("", "ap-port", "Connect to AP with specified port. If no AP with that port are present fallback AP will be used. Available ports are usually 80, 443 and 4070", "AP_PORT") + .optflag("", "disable-discovery", "Disable discovery mode") + .optopt( + "", + "backend", + "Audio backend to use. Use '?' to list options", + "BACKEND", + ) + .optopt( + "", + "device", + "Audio device to use. Use '?' to list options if using portaudio or alsa", + "DEVICE", + ) + .optopt("", "mixer", "Mixer to use (alsa or softvol)", "MIXER") + .optopt( + "m", + "mixer-name", + "Alsa mixer name, e.g \"PCM\" or \"Master\". Defaults to 'PCM'", + "MIXER_NAME", + ) + .optopt( + "", + "mixer-card", + "Alsa mixer card, e.g \"hw:0\" or similar from `aplay -l`. Defaults to 'default' ", + "MIXER_CARD", + ) + .optopt( + "", + "mixer-index", + "Alsa mixer index, Index of the cards mixer. Defaults to 0", + "MIXER_INDEX", + ) + .optflag( + "", + "mixer-linear-volume", + "Disable alsa's mapped volume scale (cubic). Default false", + ) + .optopt( + "", + "initial-volume", + "Initial volume in %, once connected (must be from 0 to 100)", + "VOLUME", + ) + .optopt( + "", + "zeroconf-port", + "The port the internal server advertised over zeroconf uses.", + "ZEROCONF_PORT", + ) + .optflag( + "", + "enable-volume-normalisation", + "Play all tracks at the same volume", + ) + .optopt( + "", + "normalisation-gain-type", + "Specify the normalisation gain type to use - [track, album]. Default is album.", + "GAIN_TYPE", + ) + .optopt( + "", + "normalisation-pregain", + "Pregain (dB) applied by volume normalisation", + "PREGAIN", + ) + .optopt( + "", + "volume-ctrl", + "Volume control type - [linear, log, fixed]. Default is logarithmic", + "VOLUME_CTRL" + ) + .optflag( + "", + "autoplay", + "autoplay similar songs when your music ends.", + ) + .optflag( + "", + "disable-gapless", + "disable gapless playback.", + ); + + let matches = match opts.parse(&args[1..]) { + Ok(m) => m, + Err(f) => { + writeln!( + stderr(), + "error: {}\n{}", + f.to_string(), + usage(&args[0], &opts) + ) + .unwrap(); + exit(1); + } + }; + + let verbose = matches.opt_present("verbose"); + setup_logging(verbose); + + info!( + "librespot {} ({}). Built on {}. Build ID: {}", + version::short_sha(), + version::commit_date(), + version::short_now(), + version::build_id() + ); + + let backend_name = matches.opt_str("backend"); + if backend_name == Some("?".into()) { + list_backends(); + exit(0); + } + + let backend = audio_backend::find(backend_name).expect("Invalid backend"); + + let device = matches.opt_str("device"); + if device == Some("?".into()) { + backend(device); + exit(0); + } + + let mixer_name = matches.opt_str("mixer"); + let mixer = mixer::find(mixer_name.as_ref()).expect("Invalid mixer"); + + let mixer_config = MixerConfig { + card: matches + .opt_str("mixer-card") + .unwrap_or_else(|| String::from("default")), + mixer: matches + .opt_str("mixer-name") + .unwrap_or_else(|| String::from("PCM")), + index: matches + .opt_str("mixer-index") + .map(|index| index.parse::().unwrap()) + .unwrap_or(0), + mapped_volume: !matches.opt_present("mixer-linear-volume"), + }; + + let cache = { + let audio_dir; + let system_dir; + if matches.opt_present("disable-audio-cache") { + audio_dir = None; + system_dir = matches + .opt_str("system-cache") + .or_else(|| matches.opt_str("c")) + .map(|p| p.into()); + } else { + let cache_dir = matches.opt_str("c"); + audio_dir = cache_dir + .as_ref() + .map(|p| AsRef::::as_ref(p).join("files")); + system_dir = matches + .opt_str("system-cache") + .or(cache_dir) + .map(|p| p.into()); + } + + match Cache::new(system_dir, audio_dir) { + Ok(cache) => Some(cache), + Err(e) => { + warn!("Cannot create cache: {}", e); + None + } + } + }; + + let initial_volume = matches + .opt_str("initial-volume") + .map(|volume| { + let volume = volume.parse::().unwrap(); + if volume > 100 { + panic!("Initial volume must be in the range 0-100"); + } + (volume as i32 * 0xFFFF / 100) as u16 + }) + .or_else(|| cache.as_ref().and_then(Cache::volume)) + .unwrap_or(0x8000); + + let zeroconf_port = matches + .opt_str("zeroconf-port") + .map(|port| port.parse::().unwrap()) + .unwrap_or(0); + + let name = matches.opt_str("name").unwrap(); + + let credentials = { + let cached_credentials = cache.as_ref().and_then(Cache::credentials); + + let password = |username: &String| -> String { + write!(stderr(), "Password for {}: ", username).unwrap(); + stderr().flush().unwrap(); + rpassword::read_password().unwrap() + }; + + get_credentials( + matches.opt_str("username"), + matches.opt_str("password"), + cached_credentials, + password, + ) + }; + + let session_config = { + let device_id = device_id(&name); + + SessionConfig { + user_agent: version::version_string(), + device_id: device_id, + proxy: matches.opt_str("proxy").or(std::env::var("http_proxy").ok()).map( + |s| { + match Url::parse(&s) { + Ok(url) => { + if url.host().is_none() || url.port_or_known_default().is_none() { + panic!("Invalid proxy url, only urls on the format \"http://host:port\" are allowed"); + } + + if url.scheme() != "http" { + panic!("Only unsecure http:// proxies are supported"); + } + url + }, + Err(err) => panic!("Invalid proxy url: {}, only urls on the format \"http://host:port\" are allowed", err) + } + }, + ), + ap_port: matches + .opt_str("ap-port") + .map(|port| port.parse::().expect("Invalid port")), + } + }; + + let player_config = { + let bitrate = matches + .opt_str("b") + .as_ref() + .map(|bitrate| Bitrate::from_str(bitrate).expect("Invalid bitrate")) + .unwrap_or(Bitrate::default()); + let gain_type = matches + .opt_str("normalisation-gain-type") + .as_ref() + .map(|gain_type| { + NormalisationType::from_str(gain_type).expect("Invalid normalisation type") + }) + .unwrap_or(NormalisationType::default()); + PlayerConfig { + bitrate: bitrate, + gapless: !matches.opt_present("disable-gapless"), + normalisation: matches.opt_present("enable-volume-normalisation"), + normalisation_type: gain_type, + normalisation_pregain: matches + .opt_str("normalisation-pregain") + .map(|pregain| pregain.parse::().expect("Invalid pregain float value")) + .unwrap_or(PlayerConfig::default().normalisation_pregain), + } + }; + + let connect_config = { + let device_type = matches + .opt_str("device-type") + .as_ref() + .map(|device_type| DeviceType::from_str(device_type).expect("Invalid device type")) + .unwrap_or(DeviceType::default()); + + let volume_ctrl = matches + .opt_str("volume-ctrl") + .as_ref() + .map(|volume_ctrl| VolumeCtrl::from_str(volume_ctrl).expect("Invalid volume ctrl type")) + .unwrap_or(VolumeCtrl::default()); + + ConnectConfig { + name: name, + device_type: device_type, + volume: initial_volume, + volume_ctrl: volume_ctrl, + autoplay: matches.opt_present("autoplay"), + } + }; + + let enable_discovery = !matches.opt_present("disable-discovery"); + + Setup { + backend: backend, + cache: cache, + session_config: session_config, + player_config: player_config, + connect_config: connect_config, + credentials: credentials, + device: device, + enable_discovery: enable_discovery, + zeroconf_port: zeroconf_port, + mixer: mixer, + mixer_config: mixer_config, + player_event_program: matches.opt_str("onevent"), + emit_sink_events: matches.opt_present("emit-sink-events"), + } +} + +#[tokio::main] +async fn main() { + if env::var("RUST_BACKTRACE").is_err() { + env::set_var("RUST_BACKTRACE", "full") + } + + let args: Vec = std::env::args().collect(); + let setupp = setup(&args); + + let mut last_credentials = None; + let mut spirc: Option = None; + let mut spirc_task: Option> = None; + let mut player_event_channel: Option> = None; + let mut auto_connect_times: Vec = vec![]; + let mut discovery = None; + let mut connecting: Pin>> = + Box::pin(futures::future::pending()); + + if setupp.enable_discovery { + let config = setupp.connect_config.clone(); + let device_id = setupp.session_config.device_id.clone(); + + discovery = Some( + librespot_connect::discovery::discovery(config, device_id, setupp.zeroconf_port) + .unwrap(), + ); + } + + if let Some(credentials) = setupp.credentials { + last_credentials = Some(credentials.clone()); + connecting = Box::pin( + Session::connect( + setupp.session_config.clone(), + credentials, + setupp.cache.clone(), + ) + .fuse(), + ); + } + + loop { + tokio::select! { + credentials = async { discovery.as_mut().unwrap().next().await }, if discovery.is_some() => { + match credentials { + Some(credentials) => { + last_credentials = Some(credentials.clone()); + auto_connect_times.clear(); + + if let Some(spirc) = spirc.take() { + spirc.shutdown(); + } + if let Some(spirc_task) = spirc_task.take() { + // Continue shutdown in its own task + tokio::spawn(spirc_task); + } + + connecting = Box::pin(Session::connect( + setupp.session_config.clone(), + credentials, + setupp.cache.clone(), + ).fuse()); + }, + None => { + warn!("Discovery stopped!"); + discovery = None; + } + } + }, + session = &mut connecting, if !connecting.is_terminated() => match session { + Ok(session) => { + let mixer_config = setupp.mixer_config.clone(); + let mixer = (setupp.mixer)(Some(mixer_config)); + let player_config = setupp.player_config.clone(); + let connect_config = setupp.connect_config.clone(); + + let audio_filter = mixer.get_audio_filter(); + let backend = setupp.backend; + let device = setupp.device.clone(); + let (player, event_channel) = + Player::new(player_config, session.clone(), audio_filter, move || { + (backend)(device) + }); + + if setupp.emit_sink_events { + if let Some(player_event_program) = setupp.player_event_program.clone() { + player.set_sink_event_callback(Some(Box::new(move |sink_status| { + match emit_sink_event(sink_status, &player_event_program) { + Ok(e) if e.success() => (), + Ok(e) => { + if let Some(code) = e.code() { + warn!("Sink event prog returned exit code {}", code); + } else { + warn!("Sink event prog returned failure"); + } + } + Err(e) => { + warn!("Emitting sink event failed: {}", e); + } + } + }))); + } + }; + + let (spirc_, spirc_task_) = Spirc::new(connect_config, session, player, mixer); + + spirc = Some(spirc_); + spirc_task = Some(Box::pin(spirc_task_)); + player_event_channel = Some(event_channel); + }, + Err(e) => { + warn!("Connection failed: {}", e); + } + }, + _ = async { spirc_task.as_mut().unwrap().await }, if spirc_task.is_some() => { + spirc_task = None; + + warn!("Spirc shut down unexpectedly"); + while !auto_connect_times.is_empty() + && ((Instant::now() - auto_connect_times[0]).as_secs() > 600) + { + let _ = auto_connect_times.remove(0); + } + + if let Some(credentials) = last_credentials.clone() { + if auto_connect_times.len() >= 5 { + warn!("Spirc shut down too often. Not reconnecting automatically."); + } else { + auto_connect_times.push(Instant::now()); + + connecting = Box::pin(Session::connect( + setupp.session_config.clone(), + credentials, + setupp.cache.clone(), + ).fuse()); + } + } + }, + event = async { player_event_channel.as_mut().unwrap().next().await }, if player_event_channel.is_some() => match event { + Some(event) => { + if let Some(program) = &setupp.player_event_program { + if let Some(child) = run_program_on_events(event, program) { + let mut child = child.expect("program failed to start"); + + tokio::spawn(async move { + match child.wait().await { + Ok(status) if !status.success() => error!("child exited with status {:?}", status.code()), + Err(e) => error!("failed to wait on child process: {}", e), + _ => {} + } + }); + } + } + }, + None => { + player_event_channel = None; + } + }, + _ = tokio::signal::ctrl_c() => { + break; + } + } + } + + info!("Gracefully shutting down"); + + // Shutdown spirc if necessary + if let Some(spirc) = spirc { + spirc.shutdown(); + + if let Some(mut spirc_task) = spirc_task { + tokio::select! { + _ = tokio::signal::ctrl_c() => (), + _ = spirc_task.as_mut() => () + } + } + } +} diff --git a/src/player_event_handler.rs b/src/player_event_handler.rs index 102cf78..361e6b1 100644 --- a/src/player_event_handler.rs +++ b/src/player_event_handler.rs @@ -2,22 +2,12 @@ use librespot::playback::player::PlayerEvent; use log::info; use std::collections::HashMap; use std::io; -use std::process::Command; -use tokio_process::{Child, CommandExt}; +use std::process::{Command, ExitStatus}; -use futures::Future; use librespot::playback::player::SinkStatus; +use tokio::process::{Child as AsyncChild, Command as AsyncCommand}; -fn run_program(program: &str, env_vars: HashMap<&str, String>) -> io::Result { - let mut v: Vec<&str> = program.split_whitespace().collect(); - info!("Running {:?} with environment variables {:?}", v, env_vars); - Command::new(&v.remove(0)) - .args(&v) - .envs(env_vars.iter()) - .spawn_async() -} - -pub fn run_program_on_events(event: PlayerEvent, onevent: &str) -> Option> { +pub fn run_program_on_events(event: PlayerEvent, onevent: &str) -> Option> { let mut env_vars = HashMap::new(); match event { PlayerEvent::Changed { @@ -68,10 +58,18 @@ pub fn run_program_on_events(event: PlayerEvent, onevent: &str) -> Option return None, } - Some(run_program(onevent, env_vars)) + + let mut v: Vec<&str> = onevent.split_whitespace().collect(); + info!("Running {:?} with environment variables {:?}", v, env_vars); + Some( + AsyncCommand::new(&v.remove(0)) + .args(&v) + .envs(env_vars.iter()) + .spawn(), + ) } -pub fn emit_sink_event(sink_status: SinkStatus, onevent: &str) { +pub fn emit_sink_event(sink_status: SinkStatus, onevent: &str) -> io::Result { let mut env_vars = HashMap::new(); env_vars.insert("PLAYER_EVENT", "sink".to_string()); let sink_status = match sink_status { @@ -80,6 +78,12 @@ pub fn emit_sink_event(sink_status: SinkStatus, onevent: &str) { SinkStatus::Closed => "closed", }; env_vars.insert("SINK_STATUS", sink_status.to_string()); + let mut v: Vec<&str> = onevent.split_whitespace().collect(); + info!("Running {:?} with environment variables {:?}", v, env_vars); - let _ = run_program(onevent, env_vars).and_then(|child| child.wait()); + Command::new(&v.remove(0)) + .args(&v) + .envs(env_vars.iter()) + .spawn()? + .wait() } From 1fc5267a71d05f2bc36477041774bfe4d178096a Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Tue, 23 Feb 2021 14:24:03 +0100 Subject: [PATCH 035/103] Revert "Merge pull request #548 from Lcchy/rodiojack-backend" This reverts commit f483075b2cde3e443929873e5abb2f072bac48a3, reversing changes made to ea8ece36d929f3a1e4325139db5a94591ffbf67e. --- COMPILING.md | 1 - Cargo.lock | 828 ++++++++++++---------------- Cargo.toml | 1 - README.md | 1 - playback/Cargo.toml | 1 - playback/src/audio_backend/mod.rs | 20 +- playback/src/audio_backend/rodio.rs | 117 +--- 7 files changed, 360 insertions(+), 609 deletions(-) diff --git a/COMPILING.md b/COMPILING.md index 40eefb3..7b3467e 100644 --- a/COMPILING.md +++ b/COMPILING.md @@ -46,7 +46,6 @@ Depending on the chosen backend, specific development libraries are required. |PortAudio | `portaudio19-dev` | `portaudio-devel` | `portaudio` | |PulseAudio | `libpulse-dev` | `pulseaudio-libs-devel` | | |JACK | `libjack-dev` | `jack-audio-connection-kit-devel` | | -|JACK over Rodio | `libjack-dev` | `jack-audio-connection-kit-devel` | - | |SDL | `libsdl2-dev` | `SDL2-devel` | | |Pipe | - | - | - | diff --git a/Cargo.lock b/Cargo.lock index efaae37..074ecbf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,9 +2,9 @@ # It is not intended for manual editing. [[package]] name = "addr2line" -version = "0.14.1" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a55f82cfe485775d02112886f4169bde0c5894d75e79ead7eafe7e40a25e45f7" +checksum = "7c0929d69e78dd9bf5408269919fcbcaeb2e35e5d43e5815517cdc6a8e11a423" dependencies = [ "gimli", ] @@ -78,9 +78,9 @@ dependencies = [ [[package]] name = "alsa-sys" -version = "0.3.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db8fee663d06c4e303404ef5f40488a53e062f89ba8bfed81f42325aafad1527" +checksum = "d5a0559bcd3f7a482690d98be41c08a43e92f669b179433e95ddf5e8b8fd36a3" dependencies = [ "libc", "pkg-config", @@ -88,9 +88,15 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.38" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afddf7f520a80dbf76e6f50a35bca42a2331ef227a28b3b6dc5c2e2338d114b1" +checksum = "2c0df63cb2955042487fad3aefd2c6e3ae7389ac5dc1beb28921de0b69f779d4" + +[[package]] +name = "arc-swap" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d25d88fd6b8041580a654f9d0c581a047baee2b3efee13275f2fc392fc75034" [[package]] name = "ascii" @@ -111,15 +117,15 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.0.1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" +checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" [[package]] name = "backtrace" -version = "0.3.56" +version = "0.3.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d117600f438b1707d4e4ae15d3595657288f8235a0eb593e80ecc98ab34e1bc" +checksum = "ef5140344c85b01f9bbb4d4b7288a8aa4b3287ccef913a14bcc78a1063623598" dependencies = [ "addr2line", "cfg-if 1.0.0", @@ -129,12 +135,6 @@ dependencies = [ "rustc-demangle", ] -[[package]] -name = "base-x" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4521f3e3d031370679b3b140beb36dfe4801b09ac77e30c61941f97df3ef28b" - [[package]] name = "base64" version = "0.9.3" @@ -145,6 +145,21 @@ dependencies = [ "safemem", ] +[[package]] +name = "base64" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" +dependencies = [ + "byteorder", +] + +[[package]] +name = "base64" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" + [[package]] name = "base64" version = "0.13.0" @@ -153,12 +168,13 @@ checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" [[package]] name = "bindgen" -version = "0.56.0" +version = "0.53.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2da379dbebc0b76ef63ca68d8fc6e71c0f13e59432e0987e508c1820e6ab5239" +checksum = "c72a978d268b1d70b0e963217e60fdabd9523a941457a6c42a7315d15c7e89e5" dependencies = [ "bitflags", "cexpr", + "cfg-if 0.1.10", "clang-sys", "lazy_static", "lazycell", @@ -181,9 +197,9 @@ dependencies = [ [[package]] name = "bit-vec" -version = "0.6.3" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" +checksum = "5f0dc55f2d8a1a85650ac47858bb001b4c0dd73d79e3c455a842925e68d29cd3" [[package]] name = "bitflags" @@ -239,9 +255,9 @@ checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" [[package]] name = "bumpalo" -version = "3.6.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099e596ef14349721d9016f6b80dd3419ea1bf289ab9b44df8e4dfd3a005d5d9" +checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820" [[package]] name = "byte-tools" @@ -251,9 +267,9 @@ checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" [[package]] name = "byteorder" -version = "1.4.2" +version = "1.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b" +checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" [[package]] name = "bytes" @@ -267,15 +283,15 @@ dependencies = [ [[package]] name = "bytes" -version = "1.0.1" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" +checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" [[package]] name = "cc" -version = "1.0.66" +version = "1.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48" +checksum = "95752358c8f7552394baf48cd82695b345628ad3f170d607de3ca03b8dacca15" [[package]] name = "cesu8" @@ -306,22 +322,20 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.19" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +checksum = "c74d84029116787153e02106bf53e66828452a4b325cc8652b788b5967c0a0b6" dependencies = [ - "libc", "num-integer", "num-traits", - "time 0.1.43", - "winapi 0.3.9", + "time", ] [[package]] name = "chunked_transfer" -version = "1.4.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fff857943da45f546682664a79488be82e69e43c1a7a2307679ab9afb3a66d2e" +checksum = "7477065d45a8fe57167bf3cf8bcd3729b54cfcb81cca49bda2d038ea89ae82ca" [[package]] name = "cipher" @@ -334,13 +348,13 @@ dependencies = [ [[package]] name = "clang-sys" -version = "1.0.3" +version = "0.29.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0659001ab56b791be01d4b729c44376edc6718cf389a502e579b77b758f3296c" +checksum = "fe6837df1d5cba2397b835c8530f51723267e16abbf83892e9e5af4f0e5dd10a" dependencies = [ "glob", "libc", - "libloading", + "libloading 0.5.2", ] [[package]] @@ -352,6 +366,15 @@ dependencies = [ "bitflags", ] +[[package]] +name = "cloudabi" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4344512281c643ae7638bbabc3af17a11307803ec8f0fcad9fae512a8bf36467" +dependencies = [ + "bitflags", +] + [[package]] name = "combine" version = "3.8.1" @@ -367,45 +390,23 @@ dependencies = [ [[package]] name = "combine" -version = "4.5.2" +version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc4369b5e4c0cddf64ad8981c0111e7df4f7078f4d6ba98fb31f2e17c4c57b7e" +checksum = "b9417a0c314565e2abffaece67e95a8cb51f9238cd39f3764d9dfdf09e72b20c" dependencies = [ - "bytes 1.0.1", + "bytes 0.5.6", "memchr", + "pin-project-lite", ] -[[package]] -name = "const_fn" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28b9d6de7f49e22cf97ad17fc4036ece69300032f45f78f30b4a4482cdc3f4a6" - [[package]] name = "cookie" -version = "0.14.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784ad0fbab4f3e9cef09f20e0aea6000ae08d2cb98ac4c0abc53df18803d702f" -dependencies = [ - "percent-encoding 2.1.0", - "time 0.2.25", - "version_check", -] - -[[package]] -name = "cookie_store" version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3818dfca4b0cb5211a659bbcbb94225b7127407b2b135e650d717bfb78ab10d3" +checksum = "888604f00b3db336d2af898ec3c1d5d0ddf5e6d462220f2ededc33a87ac4bbd5" dependencies = [ - "cookie", - "idna 0.2.1", - "log 0.4.14", - "publicsuffix", - "serde", - "serde_json", - "time 0.2.25", - "url 2.2.0", + "time", + "url 1.7.2", ] [[package]] @@ -426,9 +427,9 @@ dependencies = [ [[package]] name = "coreaudio-sys" -version = "0.2.8" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b7e3347be6a09b46aba228d6608386739fb70beff4f61e07422da87b0bb31fa" +checksum = "d6570ee6e089131e928d5ec9236db9e818aa3cf850f48b0eec6ef700571271d4" dependencies = [ "bindgen", ] @@ -442,7 +443,6 @@ dependencies = [ "alsa", "core-foundation-sys", "coreaudio-rs", - "jack", "jni 0.17.0", "js-sys", "lazy_static", @@ -453,7 +453,7 @@ dependencies = [ "nix", "oboe", "parking_lot 0.11.1", - "stdweb 0.1.3", + "stdweb", "thiserror", "web-sys", "winapi 0.3.9", @@ -597,9 +597,9 @@ dependencies = [ [[package]] name = "derivative" -version = "2.2.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +checksum = "cb582b60359da160a9477ee80f15c8d784c477e69c217ef2cdd4169c24ea380f" dependencies = [ "proc-macro2", "quote", @@ -624,12 +624,6 @@ dependencies = [ "generic-array 0.14.4", ] -[[package]] -name = "discard" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" - [[package]] name = "dns-sd" version = "0.1.3" @@ -654,15 +648,15 @@ checksum = "f26ecb66b4bdca6c1409b40fb255eefc2bd4f6d135dab3c3124f80ffa2a9661e" dependencies = [ "atty", "humantime", - "log 0.4.14", + "log 0.4.11", "termcolor", ] [[package]] name = "error-chain" -version = "0.12.4" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" +checksum = "d371106cc88ffdfb1eabd7111e432da544f16f3e2d7bf1dfe8bf575f1df045cd" dependencies = [ "backtrace", "version_check", @@ -676,9 +670,9 @@ checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" [[package]] name = "fetch_unroll" -version = "0.2.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8d44807d562d137f063cbfe209da1c3f9f2fa8375e11166ef495daab7b847f9" +checksum = "b5c55005e95bbe15f5f72a73b6597d0dc82ddc97ffe2ca097a99dcd591fefbca" dependencies = [ "libflate", "tar", @@ -687,13 +681,13 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.14" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d34cfa13a63ae058bfa601fe9e313bbdb3746427c1459185464ce0fcf62e1e8" +checksum = "0c122a393ea57648015bf06fbd3d372378992e86b9ff5a7a497b076a28c79efe" dependencies = [ "cfg-if 1.0.0", "libc", - "redox_syscall 0.2.4", + "redox_syscall", "winapi 0.3.9", ] @@ -737,24 +731,24 @@ checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" [[package]] name = "futures" -version = "0.1.30" +version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c7e4c2612746b0df8fed4ce0c69156021b704c9aefa360311c04e6e9e002eed" +checksum = "1b980f2816d6ee8673b6517b52cb0e808a180efc92e5c19d02cdda79066703ef" [[package]] name = "futures-channel" -version = "0.3.12" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2d31b7ec7efab6eefc7c57233bb10b847986139d88cc2f5a02a1ae6871a1846" +checksum = "f366ad74c28cca6ba456d95e6422883cfb4b252a83bed929c83abfdbbf2967d5" dependencies = [ "futures-core", ] [[package]] name = "futures-core" -version = "0.3.12" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79e5145dde8da7d1b3892dad07a9c98fc04bc39892b1ecc9692cf53e2b780a65" +checksum = "59f5fff90fd5d971f936ad674802482ba441b6f09ba5e15fd8b39145582ca399" [[package]] name = "futures-cpupool" @@ -768,9 +762,9 @@ dependencies = [ [[package]] name = "futures-executor" -version = "0.3.12" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9e59fdc009a4b3096bf94f740a0f2424c082521f20a9b08c5c07c48d90fd9b9" +checksum = "10d6bb888be1153d3abeb9006b11b02cf5e9b209fda28693c31ae1e4e012e314" dependencies = [ "futures-core", "futures-task", @@ -779,9 +773,9 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.12" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c287d25add322d9f9abdcdc5927ca398917996600182178774032e9f8258fedd" +checksum = "d0b5a30a4328ab5473878237c447333c093297bded83a4983d10f4deea240d39" dependencies = [ "proc-macro-hack", "proc-macro2", @@ -791,29 +785,29 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.12" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caf5c69029bda2e743fddd0582d1083951d65cc9539aebf8812f36c3491342d6" +checksum = "3f2032893cb734c7a05d85ce0cc8b8c4075278e93b24b66f9de99d6eb0fa8acc" [[package]] name = "futures-task" -version = "0.3.12" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13de07eb8ea81ae445aca7b69f5f7bf15d7bf4912d8ca37d6645c77ae8a58d86" +checksum = "bdb66b5f09e22019b1ab0830f7785bcea8e7a42148683f99214f73f8ec21a626" dependencies = [ "once_cell", ] [[package]] name = "futures-util" -version = "0.3.12" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "632a8cd0f2a4b3fdea1657f08bde063848c3bd00f9bbf6e256b8be78802e624b" +checksum = "8764574ff08b701a084482c3c7031349104b07ac897393010494beaa18ce32c6" dependencies = [ "futures-core", "futures-macro", "futures-task", - "pin-project-lite", + "pin-project", "pin-utils", "proc-macro-hack", "proc-macro-nested", @@ -856,24 +850,13 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.1.16" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 0.1.10", "libc", - "wasi 0.9.0+wasi-snapshot-preview1", -] - -[[package]] -name = "getrandom" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8" -dependencies = [ - "cfg-if 1.0.0", - "libc", - "wasi 0.10.2+wasi-snapshot-preview1", + "wasi", ] [[package]] @@ -1044,18 +1027,18 @@ dependencies = [ [[package]] name = "heck" -version = "0.3.2" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cbf45460356b7deeb5e3415b5563308c0a9b057c85e12b06ad551f98d0a6ac" +checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" dependencies = [ "unicode-segmentation", ] [[package]] name = "hermit-abi" -version = "0.1.18" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" +checksum = "3deed196b6e7f9e44a2ae8d94225d80302d81208b1bb673fd21fe634645c85a9" dependencies = [ "libc", ] @@ -1089,15 +1072,15 @@ dependencies = [ [[package]] name = "httparse" -version = "1.3.5" +version = "1.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "615caabe2c3160b313d52ccc905335f4ed5f10881dd63dc5699d47e90be85691" +checksum = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" [[package]] name = "humantime" -version = "2.1.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +checksum = "3c1ad908cc71012b7bea4d0c53ba96a8cba9962f048fa68d143376143d863b7a" [[package]] name = "hyper" @@ -1112,12 +1095,12 @@ dependencies = [ "httparse", "iovec", "language-tags", - "log 0.4.14", + "log 0.4.11", "mime", "net2", "percent-encoding 1.0.1", "relay", - "time 0.1.43", + "time", "tokio-core", "tokio-io", "tokio-proto", @@ -1158,9 +1141,9 @@ dependencies = [ [[package]] name = "idna" -version = "0.2.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de910d521f7cc3135c4de8db1cb910e0b5ed1dc6f57c381cd07e8e661ce10094" +checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" dependencies = [ "matches", "unicode-bidi", @@ -1169,9 +1152,9 @@ dependencies = [ [[package]] name = "if-addrs" -version = "0.6.5" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28538916eb3f3976311f5dfbe67b5362d0add1293d0a9cad17debf86f8e3aa48" +checksum = "f12906406f12abf5569643c46b29aec78313dc1537b17dd5c5250169790c4db9" dependencies = [ "if-addrs-sys", "libc", @@ -1180,9 +1163,9 @@ dependencies = [ [[package]] name = "if-addrs-sys" -version = "0.3.2" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de74b9dd780476e837e5eb5ab7c88b49ed304126e412030a0adba99c8efe79ea" +checksum = "9e2556f16544202bcfe0aa5d20a01a6b815f736b136b3ad76dc547ee6b5bb1df" dependencies = [ "cc", "libc", @@ -1217,9 +1200,9 @@ dependencies = [ [[package]] name = "itoa" -version = "0.4.7" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" +checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" [[package]] name = "jack" @@ -1235,13 +1218,13 @@ dependencies = [ [[package]] name = "jack-sys" -version = "0.2.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e1d6ab7ada402b6a27912a2b86504be62a48c58313c886fe72a059127acb4d7" +checksum = "c0d4ca501477fd3cd93a36df581046e5d6338ed826cf7e9b8d302603521e6cc3" dependencies = [ "lazy_static", "libc", - "libloading", + "libloading 0.4.3", ] [[package]] @@ -1254,7 +1237,7 @@ dependencies = [ "combine 3.8.1", "error-chain", "jni-sys", - "log 0.4.14", + "log 0.4.11", "walkdir", ] @@ -1265,10 +1248,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36bcc950632e48b86da402c5c077590583da5ac0d480103611d5374e7c967a3c" dependencies = [ "cesu8", - "combine 4.5.2", + "combine 4.4.0", "error-chain", "jni-sys", - "log 0.4.14", + "log 0.4.11", "walkdir", ] @@ -1280,9 +1263,9 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "js-sys" -version = "0.3.47" +version = "0.3.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cfb73131c35423a367daf8cbd24100af0d077668c8c2943f0e7dd775fef0f65" +checksum = "cf3d7383929f7c9c7c2d0fa596f325832df98c3704f2c60553080f7127a58175" dependencies = [ "wasm-bindgen", ] @@ -1311,9 +1294,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "lazycell" -version = "1.3.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" +checksum = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" [[package]] name = "lewton" @@ -1323,40 +1306,45 @@ checksum = "777b48df9aaab155475a83a7df3070395ea1ac6902f5cd062b8f2b028075c030" dependencies = [ "byteorder", "ogg", - "tinyvec", + "tinyvec 1.1.1", ] [[package]] name = "libc" -version = "0.2.85" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ccac4b00700875e6a07c6cde370d44d32fa01c5a65cdd2fca6858c479d28bb3" +checksum = "bd7d4bd64732af4bf3a67f367c27df8520ad7e230c5817b8ff485864d80242b9" [[package]] name = "libflate" -version = "1.0.3" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389de7875e06476365974da3e7ff85d55f1972188ccd9f6020dd7c8156e17914" +checksum = "d9135df43b1f5d0e333385cb6e7897ecd1a43d7d11b91ac003f4d2c2d2401fdd" dependencies = [ "adler32", "crc32fast", - "libflate_lz77", "rle-decode-fast", + "take_mut", ] [[package]] -name = "libflate_lz77" -version = "1.0.0" +name = "libloading" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3286f09f7d4926fc486334f28d8d2e6ebe4f7f9994494b6dab27ddfad2c9b11b" +checksum = "fd38073de8f7965d0c17d30546d4bb6da311ab428d1c7a3fc71dff7f9d4979b9" +dependencies = [ + "kernel32-sys", + "lazy_static", + "winapi 0.2.8", +] [[package]] name = "libloading" -version = "0.6.7" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "351a32417a12d5f7e82c368a66781e307834dae04c6ce0cd4456d52989229883" +checksum = "f2b111a074963af1d37a139918ac6d49ad1d0d5e47f72fd55388619691a7d753" dependencies = [ - "cfg-if 1.0.0", + "cc", "winapi 0.3.9", ] @@ -1370,7 +1358,7 @@ dependencies = [ "futures", "hostname", "if-addrs", - "log 0.4.14", + "log 0.4.11", "multimap", "net2", "quick-error", @@ -1380,11 +1368,10 @@ dependencies = [ [[package]] name = "libpulse-binding" -version = "2.23.0" +version = "2.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2405f806801527dfb3d2b6d48a282cdebe9a1b41b0652e0d7b5bad81dbc700e" +checksum = "1e8f85a42300c868de4849bb72eda5a65cea08c3ca61396b72c2d7c28a87f055" dependencies = [ - "bitflags", "libc", "libpulse-sys", "num-derive", @@ -1394,9 +1381,9 @@ dependencies = [ [[package]] name = "libpulse-simple-binding" -version = "2.23.0" +version = "2.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a574975292db859087c3957b9182f7d53278553f06bddaa2099c90e4ac3a0ee0" +checksum = "a047f4502997eed57b3e9d8e71f2b860da91a20bb7e15c65d1f183a7b4fb1226" dependencies = [ "libpulse-binding", "libpulse-simple-sys", @@ -1405,9 +1392,9 @@ dependencies = [ [[package]] name = "libpulse-simple-sys" -version = "1.16.1" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "468cf582b7b022c0d1b266fefc7fc8fa7b1ddcb61214224f2f105c95a9c2d5c1" +checksum = "9b72cb239bc4de6858fa0bbad27419e72cd4466f079ca56f21d94b0a712ab02e" dependencies = [ "libpulse-sys", "pkg-config", @@ -1415,9 +1402,9 @@ dependencies = [ [[package]] name = "libpulse-sys" -version = "1.18.0" +version = "1.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf17e9832643c4f320c42b7d78b2c0510f45aa5e823af094413b94e45076ba82" +checksum = "706e95c4b87ebb81c1e7763c74bf7d5ba897208f1a8aa5fc7bea8298dee8f2ca" dependencies = [ "libc", "num-derive", @@ -1442,7 +1429,7 @@ dependencies = [ "librespot-metadata", "librespot-playback", "librespot-protocol", - "log 0.4.14", + "log 0.4.11", "num-bigint", "protobuf", "rand 0.7.3", @@ -1467,7 +1454,7 @@ dependencies = [ "lewton", "librespot-core", "librespot-tremor", - "log 0.4.14", + "log 0.4.11", "num-bigint", "num-traits", "ogg", @@ -1490,7 +1477,7 @@ dependencies = [ "librespot-core", "librespot-playback", "librespot-protocol", - "log 0.4.14", + "log 0.4.11", "num-bigint", "protobuf", "rand 0.7.3", @@ -1518,7 +1505,7 @@ dependencies = [ "hyper-proxy", "lazy_static", "librespot-protocol", - "log 0.4.14", + "log 0.4.11", "num-bigint", "num-integer", "num-traits", @@ -1547,7 +1534,7 @@ dependencies = [ "librespot-core", "librespot-protocol", "linear-map", - "log 0.4.14", + "log 0.4.11", "protobuf", ] @@ -1569,7 +1556,7 @@ dependencies = [ "librespot-audio", "librespot-core", "librespot-metadata", - "log 0.4.14", + "log 0.4.11", "portaudio-rs", "rodio", "sdl2", @@ -1629,16 +1616,16 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" dependencies = [ - "log 0.4.14", + "log 0.4.11", ] [[package]] name = "log" -version = "0.4.14" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 0.1.10", ] [[package]] @@ -1670,15 +1657,15 @@ checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" [[package]] name = "memchr" -version = "2.3.4" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" +checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" [[package]] name = "memoffset" -version = "0.5.6" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa" +checksum = "c198b026e1bbf08a937e94c6c60f9ec4a2267f5b0d2eec9c1b21b061ce2be55f" dependencies = [ "autocfg", ] @@ -1701,9 +1688,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.6.23" +version = "0.6.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4" +checksum = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430" dependencies = [ "cfg-if 0.1.10", "fuchsia-zircon", @@ -1711,8 +1698,8 @@ dependencies = [ "iovec", "kernel32-sys", "libc", - "log 0.4.14", - "miow 0.2.2", + "log 0.4.11", + "miow 0.2.1", "net2", "slab 0.4.2", "winapi 0.2.8", @@ -1724,9 +1711,9 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0840c1c50fd55e521b247f949c241c9997709f23bd7f023b9762cd561e935656" dependencies = [ - "log 0.4.14", + "log 0.4.11", "mio", - "miow 0.3.6", + "miow 0.3.5", "winapi 0.3.9", ] @@ -1743,9 +1730,9 @@ dependencies = [ [[package]] name = "miow" -version = "0.2.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" +checksum = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" dependencies = [ "kernel32-sys", "net2", @@ -1755,9 +1742,9 @@ dependencies = [ [[package]] name = "miow" -version = "0.3.6" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a33c1b55807fbed163481b5ba66db4b2fa6cde694a5027be10fb724206c5897" +checksum = "07b88fb9795d4d36d62a012dfbf49a8f5cf12751f36d31a9dbe66d528e58979e" dependencies = [ "socket2", "winapi 0.3.9", @@ -1771,9 +1758,9 @@ checksum = "0419348c027fa7be448d2ae7ea0e4e04c2334c31dc4e74ab29f00a2a7ca69204" [[package]] name = "multimap" -version = "0.8.2" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1255076139a83bb467426e7f8d0134968a8118844faa755985e077cf31850333" +checksum = "d8883adfde9756c1d30b0f519c9b8c502a94b41ac62f696453c37c7fc0a958ce" dependencies = [ "serde", ] @@ -1798,7 +1785,7 @@ checksum = "bdf399b8b7a39c6fb153c4ec32c72fd5fe789df24a647f229c239aa7adb15241" dependencies = [ "lazy_static", "libc", - "log 0.4.14", + "log 0.4.11", "ndk", "ndk-macro", "ndk-sys", @@ -1825,9 +1812,9 @@ checksum = "c44922cb3dbb1c70b5e5f443d63b64363a898564d739ba5198e3a9138442868d" [[package]] name = "net2" -version = "0.2.37" +version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae" +checksum = "2ba7c918ac76704fb42afcbbb43891e72731f3dcca3bef2a19786297baf14af7" dependencies = [ "cfg-if 0.1.10", "libc", @@ -1881,9 +1868,9 @@ dependencies = [ [[package]] name = "num-integer" -version = "0.1.44" +version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b" dependencies = [ "autocfg", "num-traits", @@ -1902,9 +1889,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.14" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611" dependencies = [ "autocfg", ] @@ -1943,9 +1930,9 @@ dependencies = [ [[package]] name = "object" -version = "0.23.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9a7ab5d64814df0fe4a4b5ead45ed6c5f181ee3ff04ba344313a6c80446c5d4" +checksum = "8d3b63360ec3cb337817c2dbd47ab4a0f170d285d8e5a2064600f3def1402397" [[package]] name = "oboe" @@ -2027,7 +2014,7 @@ checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" dependencies = [ "instant", "lock_api 0.4.2", - "parking_lot_core 0.8.2", + "parking_lot_core 0.8.0", ] [[package]] @@ -2037,25 +2024,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" dependencies = [ "cfg-if 0.1.10", - "cloudabi", + "cloudabi 0.0.3", "libc", - "redox_syscall 0.1.57", + "redox_syscall", "rustc_version", - "smallvec 0.6.14", + "smallvec 0.6.13", "winapi 0.3.9", ] [[package]] name = "parking_lot_core" -version = "0.8.2" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ccb628cad4f84851442432c60ad8e1f607e29752d0bf072cbd0baf28aa34272" +checksum = "c361aa727dd08437f2f1447be8b59a33b0edd15e0fcee698f935613d9efbca9b" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 0.1.10", + "cloudabi 0.1.0", "instant", "libc", - "redox_syscall 0.1.57", - "smallvec 1.6.1", + "redox_syscall", + "smallvec 1.5.0", "winapi 0.3.9", ] @@ -2094,10 +2082,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" [[package]] -name = "pin-project-lite" -version = "0.2.4" +name = "pin-project" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439697af366c49a6d0a010c56a0d97685bc140ce0d377b13a2ea2aa42d64a827" +checksum = "12e3a6cdbfe94a5e4572812a0201f8c0ed98c1c452c7b8563ce2276988ef9c17" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a0ffd45cf79d88737d7cc85bfd5d2894bee1139b356e616fe85dc389c61aaf7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c917123afa01924fc84bb20c4c03f004d9c38e5127e3c039bbf7f4b9c76a2f6b" [[package]] name = "pin-utils" @@ -2107,9 +2115,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.19" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" +checksum = "d36492546b6af1463394d46f0c834346f31548646f6ba10849802c9c9a27ac33" [[package]] name = "portaudio-rs" @@ -2134,9 +2142,9 @@ dependencies = [ [[package]] name = "ppv-lite86" -version = "0.2.10" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" +checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea" [[package]] name = "pretty-hex" @@ -2179,21 +2187,21 @@ dependencies = [ [[package]] name = "proc-macro-hack" -version = "0.5.19" +version = "0.5.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" +checksum = "7e0456befd48169b9f13ef0f0ad46d492cf9d2dbb918bcf38e01eed4ce3ec5e4" [[package]] name = "proc-macro-nested" -version = "0.1.7" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" +checksum = "eba180dafb9038b050a4c280019bbedf9f2467b61e5d892dcad585bb57aadc5a" [[package]] name = "proc-macro2" -version = "1.0.24" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +checksum = "04f5f085b5d71e2188cb8271e5da0161ad52c3f227a661a3c135fdf28e258b12" dependencies = [ "unicode-xid", ] @@ -2223,19 +2231,6 @@ dependencies = [ "protobuf-codegen", ] -[[package]] -name = "publicsuffix" -version = "1.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bbaa49075179162b49acac1c6aa45fb4dafb5f13cf6794276d77bc7fd95757b" -dependencies = [ - "error-chain", - "idna 0.2.1", - "lazy_static", - "regex", - "url 2.2.0", -] - [[package]] name = "qstring" version = "0.7.2" @@ -2253,9 +2248,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quote" -version = "1.0.8" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df" +checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" dependencies = [ "proc-macro2", ] @@ -2289,23 +2284,11 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" dependencies = [ - "getrandom 0.1.16", + "getrandom", "libc", - "rand_chacha 0.2.2", + "rand_chacha", "rand_core 0.5.1", - "rand_hc 0.2.0", -] - -[[package]] -name = "rand" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e" -dependencies = [ - "libc", - "rand_chacha 0.3.0", - "rand_core 0.6.1", - "rand_hc 0.3.0", + "rand_hc", ] [[package]] @@ -2318,16 +2301,6 @@ dependencies = [ "rand_core 0.5.1", ] -[[package]] -name = "rand_chacha" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.1", -] - [[package]] name = "rand_core" version = "0.3.1" @@ -2349,16 +2322,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" dependencies = [ - "getrandom 0.1.16", -] - -[[package]] -name = "rand_core" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c026d7df8b298d90ccbbc5190bd04d85e159eaf5576caeacf8741da93ccbd2e5" -dependencies = [ - "getrandom 0.2.2", + "getrandom", ] [[package]] @@ -2370,15 +2334,6 @@ dependencies = [ "rand_core 0.5.1", ] -[[package]] -name = "rand_hc" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" -dependencies = [ - "rand_core 0.6.1", -] - [[package]] name = "rdrand" version = "0.4.0" @@ -2394,29 +2349,20 @@ version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" -[[package]] -name = "redox_syscall" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ec8ca9416c5ea37062b502703cd7fcb207736bc294f6e0cf367ac6fc234570" -dependencies = [ - "bitflags", -] - [[package]] name = "regex" -version = "1.4.3" +version = "1.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a" +checksum = "9c3780fcf44b193bc4d09f36d2a3c87b251da4a046c87795a0d35f4f927ad8e6" dependencies = [ "regex-syntax", ] [[package]] name = "regex-syntax" -version = "0.6.22" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581" +checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8" [[package]] name = "relay" @@ -2438,9 +2384,9 @@ dependencies = [ [[package]] name = "ring" -version = "0.16.20" +version = "0.16.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +checksum = "70017ed5c555d79ee3538fc63ca09c70ad8f317dcadc1adc2c496b60c22bb24f" dependencies = [ "cc", "libc", @@ -2468,9 +2414,9 @@ dependencies = [ [[package]] name = "rpassword" -version = "5.0.1" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffc936cf8a7ea60c58f030fd36a612a48f440610214dc54bc36431f9ea0c3efb" +checksum = "d755237fc0f99d98641540e66abac8bc46a0652f19148ac9e21de2da06b326c9" dependencies = [ "libc", "winapi 0.3.9", @@ -2499,12 +2445,12 @@ dependencies = [ [[package]] name = "rustls" -version = "0.19.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "064fd21ff87c6e87ed4506e68beb42459caa4a0e2eb144932e6776768556980b" +checksum = "b25a18b1bf7387f0145e7f8324e700805aade3842dd3db2e74e4cdeb4677c09e" dependencies = [ - "base64 0.13.0", - "log 0.4.14", + "base64 0.10.1", + "log 0.4.11", "ring", "sct", "webpki", @@ -2593,18 +2539,15 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.123" +version = "1.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d5161132722baa40d802cc70b15262b98258453e85e5d1d365c757c73869ae" -dependencies = [ - "serde_derive", -] +checksum = "5317f7588f0a5078ee60ef675ef96735a1442132dc645eb1d12c018620ed8cd3" [[package]] name = "serde_derive" -version = "1.0.123" +version = "1.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9391c295d64fc0abb2c556bad848f33cb8296276b1ad2677d1ae1ace4f258f31" +checksum = "2a0be94b04690fbaed37cddffc5c134bf537c8e3329d53e982fe04c374978f8e" dependencies = [ "proc-macro2", "quote", @@ -2613,9 +2556,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.61" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fceb2595057b6891a4ee808f70054bd2d12f0e97f1cbb78689b59f676df325a" +checksum = "3433e879a558dde8b5e8feb2a04899cf34fdde1fafb894687e52105fc1162ac3" dependencies = [ "itoa", "ryu", @@ -2647,12 +2590,6 @@ dependencies = [ "opaque-debug 0.3.0", ] -[[package]] -name = "sha1" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" - [[package]] name = "shannon" version = "0.2.0" @@ -2676,10 +2613,11 @@ checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" [[package]] name = "signal-hook-registry" -version = "1.3.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16f1d0fef1604ba8f7a073c7e701f213e056707210e9020af4528e0101ce11a6" +checksum = "94f478ede9f64724c5d173d7bb56099ec3e2d9fc2774aac65d34b8b890405f41" dependencies = [ + "arc-swap", "libc", ] @@ -2703,27 +2641,28 @@ checksum = "4c8cbcd6df1e117c2210e13ab5109635ad68a929fcbb8964dc965b76cb5ee013" [[package]] name = "smallvec" -version = "0.6.14" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97fcaeba89edba30f044a10c6a3cc39df9c3f17d7cd829dd1446cab35f890e0" +checksum = "f7b0758c52e15a8b5e3691eae6cc559f08eee9406e548a4477ba4e67770a82b6" dependencies = [ "maybe-uninit", ] [[package]] name = "smallvec" -version = "1.6.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" +checksum = "7acad6f34eb9e8a259d3283d1e8c1d34d7415943d4895f65cc73813c7396fc85" [[package]] name = "socket2" -version = "0.3.19" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e" +checksum = "03088793f677dce356f3ccc2edb1b314ad191ab702a5de3faf49304f7e104918" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 0.1.10", "libc", + "redox_syscall", "winapi 0.3.9", ] @@ -2733,70 +2672,12 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" -[[package]] -name = "standback" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2beb4d1860a61f571530b3f855a1b538d0200f7871c63331ecd6f17b1f014f8" -dependencies = [ - "version_check", -] - [[package]] name = "stdweb" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef5430c8e36b713e13b48a9f709cc21e046723fe44ce34587b73a830203b533e" -[[package]] -name = "stdweb" -version = "0.4.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" -dependencies = [ - "discard", - "rustc_version", - "stdweb-derive", - "stdweb-internal-macros", - "stdweb-internal-runtime", - "wasm-bindgen", -] - -[[package]] -name = "stdweb-derive" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" -dependencies = [ - "proc-macro2", - "quote", - "serde", - "serde_derive", - "syn", -] - -[[package]] -name = "stdweb-internal-macros" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" -dependencies = [ - "base-x", - "proc-macro2", - "quote", - "serde", - "serde_derive", - "serde_json", - "sha1", - "syn", -] - -[[package]] -name = "stdweb-internal-runtime" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" - [[package]] name = "strsim" version = "0.9.3" @@ -2829,9 +2710,9 @@ checksum = "1e81da0851ada1f3e9d4312c704aa4f8806f0f9d69faaf8df2f3464b4a9437c2" [[package]] name = "syn" -version = "1.0.60" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081" +checksum = "fb7f4c519df8c117855e19dd8cc851e89eb746fe7a73f0157e0d95fdec5369b0" dependencies = [ "proc-macro2", "quote", @@ -2872,53 +2753,60 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b157868d8ac1f56b64604539990685fa7611d8fa9e5476cf0c02cf34d32917c5" [[package]] -name = "tar" -version = "0.4.32" +name = "take_mut" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0313546c01d59e29be4f09687bcb4fb6690cec931cc3607b6aec7a0e417f4cc6" +checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" + +[[package]] +name = "tar" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "489997b7557e9a43e192c527face4feacc78bfbe6eed67fd55c4c9e381cba290" dependencies = [ "filetime", "libc", + "redox_syscall", "xattr", ] [[package]] name = "tempfile" -version = "3.2.0" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" +checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 0.1.10", "libc", - "rand 0.8.3", - "redox_syscall 0.2.4", + "rand 0.7.3", + "redox_syscall", "remove_dir_all", "winapi 0.3.9", ] [[package]] name = "termcolor" -version = "1.1.2" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" +checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f" dependencies = [ "winapi-util", ] [[package]] name = "thiserror" -version = "1.0.23" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76cc616c6abf8c8928e2fdcc0dbfab37175edd8fb49a4641066ad1364fdab146" +checksum = "318234ffa22e0920fe9a40d7b8369b5f649d490980cf7aadcf1eb91594869b42" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.23" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9be73a2caec27583d0046ef3796c3794f868a5bc813db689eed00c7631275cd1" +checksum = "cae2447b6282786c3493999f40a9be2a6ad20cb8bd268b0a0dbf5a065535c0ab" dependencies = [ "proc-macro2", "quote", @@ -2936,42 +2824,10 @@ dependencies = [ ] [[package]] -name = "time" -version = "0.2.25" +name = "tinyvec" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1195b046942c221454c2539395f85413b33383a067449d78aab2b7b052a142f7" -dependencies = [ - "const_fn", - "libc", - "standback", - "stdweb 0.4.20", - "time-macros", - "version_check", - "winapi 0.3.9", -] - -[[package]] -name = "time-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957e9c6e26f12cb6d0dd7fc776bb67a706312e7299aed74c8dd5b17ebb27e2f1" -dependencies = [ - "proc-macro-hack", - "time-macros-impl", -] - -[[package]] -name = "time-macros-impl" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5c3be1edfad6027c69f5491cf4cb310d1a71ecd6af742788c6ff8bced86b8fa" -dependencies = [ - "proc-macro-hack", - "proc-macro2", - "quote", - "standback", - "syn", -] +checksum = "53953d2d3a5ad81d9f844a32f14ebb121f50b650cd59d0ee2a07cf13c617efed" [[package]] name = "tinyvec" @@ -3025,14 +2881,14 @@ dependencies = [ [[package]] name = "tokio-core" -version = "0.1.18" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87b1395334443abca552f63d4f61d0486f12377c2ba8b368e523f89e828cffd4" +checksum = "aeeffbbb94209023feaef3c196a41cbcdafa06b4a6f893f68779bb5e53796f71" dependencies = [ "bytes 0.4.12", "futures", "iovec", - "log 0.4.14", + "log 0.4.11", "mio", "scoped-tls", "tokio", @@ -3081,7 +2937,7 @@ checksum = "57fc868aae093479e3131e3d165c93b1c7474109d13c90ec0dda2a1bbfff0674" dependencies = [ "bytes 0.4.12", "futures", - "log 0.4.14", + "log 0.4.11", ] [[package]] @@ -3094,7 +2950,7 @@ dependencies = [ "futures", "lazy_static", "libc", - "log 0.4.14", + "log 0.4.11", "mio", "mio-named-pipes", "tokio-io", @@ -3130,7 +2986,7 @@ dependencies = [ "crossbeam-utils 0.7.2", "futures", "lazy_static", - "log 0.4.14", + "log 0.4.11", "mio", "num_cpus", "parking_lot 0.9.0", @@ -3201,7 +3057,7 @@ dependencies = [ "crossbeam-utils 0.7.2", "futures", "lazy_static", - "log 0.4.14", + "log 0.4.11", "num_cpus", "slab 0.4.2", "tokio-executor", @@ -3227,7 +3083,7 @@ checksum = "e2a0b10e610b39c38b031a2fcab08e4b82f16ece36504988dcbd81dbba650d82" dependencies = [ "bytes 0.4.12", "futures", - "log 0.4.14", + "log 0.4.11", "mio", "tokio-codec", "tokio-io", @@ -3244,7 +3100,7 @@ dependencies = [ "futures", "iovec", "libc", - "log 0.4.14", + "log 0.4.11", "mio", "mio-uds", "tokio-codec", @@ -3254,9 +3110,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.8" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +checksum = "75cf45bb0bef80604d001caaec0d09da99611b3c0fd39d3080468875cdb65645" dependencies = [ "serde", ] @@ -3293,11 +3149,11 @@ dependencies = [ [[package]] name = "unicode-normalization" -version = "0.1.16" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a13e63ab62dbe32aeee58d1c5408d35c36c392bba5d9d3142287219721afe606" +checksum = "6fb19cf769fa8c6a80a162df694621ebeb4dafb606470b2b2fce0be40a98a977" dependencies = [ - "tinyvec", + "tinyvec 0.3.3", ] [[package]] @@ -3335,16 +3191,14 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "ureq" -version = "1.5.4" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "294b85ef5dbc3670a72e82a89971608a1fcc4ed5c7c5a2895230d31a95f0569b" +checksum = "801125e6d1ba6864cf3a5a92cfb2f0b0a3ee73e40602a0cd206ad2f3c040aa96" dependencies = [ - "base64 0.13.0", + "base64 0.11.0", "chunked_transfer", "cookie", - "cookie_store", - "log 0.4.14", - "once_cell", + "lazy_static", "qstring", "rustls", "url 2.2.0", @@ -3370,18 +3224,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5909f2b0817350449ed73e8bcd81c8c3c8d9a7a5d8acba4b27db277f1868976e" dependencies = [ "form_urlencoded", - "idna 0.2.1", + "idna 0.2.0", "matches", "percent-encoding 2.1.0", ] [[package]] name = "uuid" -version = "0.8.2" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" +checksum = "9fde2f6a4bea1d6e007c4ad38c6839fa71cbb63b6dbf5b595aa38dc9b1093c11" dependencies = [ - "getrandom 0.2.2", + "rand 0.7.3", ] [[package]] @@ -3426,11 +3280,11 @@ dependencies = [ [[package]] name = "vorbis-sys" -version = "0.1.1" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd9ed6ef5361a85e68ccc005961d995c2d44e31f0816f142025f2ca2383dfbfd" +checksum = "3a0a8d7034313748da1d84b0adfa501f83f9ec83250f37fbacfa92a3580327c4" dependencies = [ - "cc", + "gcc", "libc", "ogg-sys", "pkg-config", @@ -3467,7 +3321,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a05d9d966753fa4b5c8db73fcab5eed4549cfe0e1e4e66911e5564a0085c35d1" dependencies = [ "futures", - "log 0.4.14", + "log 0.4.11", "try-lock", ] @@ -3477,17 +3331,11 @@ version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" -[[package]] -name = "wasi" -version = "0.10.2+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" - [[package]] name = "wasm-bindgen" -version = "0.2.70" +version = "0.2.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55c0f7123de74f0dab9b7d00fd614e7b19349cd1e2f5252bbe9b1754b59433be" +checksum = "3cd364751395ca0f68cafb17666eee36b63077fb5ecd972bbcd74c90c4bf736e" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen-macro", @@ -3495,13 +3343,13 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.70" +version = "0.2.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bc45447f0d4573f3d65720f636bbcc3dd6ce920ed704670118650bcd47764c7" +checksum = "1114f89ab1f4106e5b55e688b828c0ab0ea593a1ea7c094b141b14cbaaec2d62" dependencies = [ "bumpalo", "lazy_static", - "log 0.4.14", + "log 0.4.11", "proc-macro2", "quote", "syn", @@ -3510,9 +3358,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.70" +version = "0.2.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b8853882eef39593ad4174dd26fc9865a64e84026d223f63bb2c42affcbba2c" +checksum = "7a6ac8995ead1f084a8dea1e65f194d0973800c7f571f6edd70adf06ecf77084" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3520,9 +3368,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.70" +version = "0.2.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4133b5e7f2a531fa413b3a1695e925038a05a71cf67e87dafa295cb645a01385" +checksum = "b5a48c72f299d80557c7c62e37e7225369ecc0c963964059509fbafe917c7549" dependencies = [ "proc-macro2", "quote", @@ -3533,15 +3381,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.70" +version = "0.2.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd4945e4943ae02d15c13962b38a5b1e81eadd4b71214eee75af64a4d6a4fd64" +checksum = "7e7811dd7f9398f14cc76efd356f98f03aa30419dea46aa810d71e819fc97158" [[package]] name = "web-sys" -version = "0.3.47" +version = "0.3.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c40dc691fc48003eba817c38da7113c15698142da971298003cac3ef175680b3" +checksum = "222b1ef9334f92a21d3fb53dc3fd80f30836959a90f9274a626d7e06315ba3c3" dependencies = [ "js-sys", "wasm-bindgen", @@ -3549,9 +3397,9 @@ dependencies = [ [[package]] name = "webpki" -version = "0.21.4" +version = "0.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" +checksum = "ab146130f5f790d45f82aeeb09e55a256573373ec64409fc19a6fb82fb1032ae" dependencies = [ "ring", "untrusted", @@ -3559,9 +3407,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.21.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82015b7e0b8bad8185994674a13a93306bea76cf5a16c5a181382fd3a5ec2376" +checksum = "91cd5736df7f12a964a5067a12c62fa38e1bd8080aff1f80bc29be7c80d19ab4" dependencies = [ "webpki", ] diff --git a/Cargo.toml b/Cargo.toml index 0b25459..a898b40 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,7 +63,6 @@ alsa-backend = ["librespot-playback/alsa-backend"] portaudio-backend = ["librespot-playback/portaudio-backend"] pulseaudio-backend = ["librespot-playback/pulseaudio-backend"] jackaudio-backend = ["librespot-playback/jackaudio-backend"] -rodiojack-backend = ["librespot-playback/rodiojack-backend"] rodio-backend = ["librespot-playback/rodio-backend"] sdl-backend = ["librespot-playback/sdl-backend"] gstreamer-backend = ["librespot-playback/gstreamer-backend"] diff --git a/README.md b/README.md index 33b2b76..e7611aa 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,6 @@ ALSA PortAudio PulseAudio JACK -JACK over Rodio SDL Pipe ``` diff --git a/playback/Cargo.toml b/playback/Cargo.toml index b8995a4..69dcc3c 100644 --- a/playback/Cargo.toml +++ b/playback/Cargo.toml @@ -42,7 +42,6 @@ alsa-backend = ["alsa"] portaudio-backend = ["portaudio-rs"] pulseaudio-backend = ["libpulse-binding", "libpulse-simple-binding"] jackaudio-backend = ["jack"] -rodiojack-backend = ["rodio", "cpal/jack"] rodio-backend = ["rodio", "cpal"] sdl-backend = ["sdl2"] gstreamer-backend = ["gstreamer", "gstreamer-app", "glib", "zerocopy"] diff --git a/playback/src/audio_backend/mod.rs b/playback/src/audio_backend/mod.rs index 3f5dae8..6bda393 100644 --- a/playback/src/audio_backend/mod.rs +++ b/playback/src/audio_backend/mod.rs @@ -35,28 +35,15 @@ mod jackaudio; #[cfg(feature = "jackaudio-backend")] use self::jackaudio::JackSink; -#[cfg(all( - feature = "rodiojack-backend", - not(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd")) -))] -compile_error!("Rodio JACK backend is currently only supported on linux."); - -#[cfg(all( - feature = "rodiojack-backend", - any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd") -))] -use self::rodio::JackRodioSink; - #[cfg(feature = "gstreamer-backend")] mod gstreamer; #[cfg(feature = "gstreamer-backend")] use self::gstreamer::GstreamerSink; -#[cfg(any(feature = "rodio-backend", feature = "rodiojack-backend"))] +#[cfg(feature = "rodio-backend")] mod rodio; #[cfg(feature = "rodio-backend")] use self::rodio::RodioSink; - #[cfg(feature = "sdl-backend")] mod sdl; #[cfg(feature = "sdl-backend")] @@ -77,11 +64,6 @@ pub const BACKENDS: &'static [(&'static str, fn(Option) -> Box ("pulseaudio", mk_sink::), #[cfg(feature = "jackaudio-backend")] ("jackaudio", mk_sink::), - #[cfg(all( - feature = "rodiojack-backend", - any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd") - ))] - ("rodiojack", mk_sink::), #[cfg(feature = "gstreamer-backend")] ("gstreamer", mk_sink::), #[cfg(feature = "rodio-backend")] diff --git a/playback/src/audio_backend/rodio.rs b/playback/src/audio_backend/rodio.rs index 3b920c3..b20c53b 100644 --- a/playback/src/audio_backend/rodio.rs +++ b/playback/src/audio_backend/rodio.rs @@ -13,17 +13,6 @@ pub struct RodioSink { stream: rodio::OutputStream, } -#[cfg(all( - feature = "rodiojack-backend", - any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd") -))] -pub struct JackRodioSink { - jackrodio_sink: rodio::Sink, - // We have to keep hold of this object, or the Sink can't play... - #[allow(dead_code)] - stream: rodio::OutputStream, -} - fn list_formats(ref device: &rodio::Device) { let default_fmt = match device.default_output_config() { Ok(fmt) => cpal::SupportedStreamConfig::from(fmt), @@ -51,19 +40,17 @@ fn list_formats(ref device: &rodio::Device) { } } -fn list_outputs(ref host: &cpal::Host) { - let default_device = get_default_device(host); +fn list_outputs() { + let default_device = get_default_device(); let default_device_name = default_device.name().expect("cannot get output name"); println!("Default Audio Device:\n {}", default_device_name); list_formats(&default_device); println!("Other Available Audio Devices:"); - - let found_devices = host.output_devices().expect(&format!( - "Cannot get list of output devices of Host: {:?}", - host.id() - )); - for device in found_devices { + for device in cpal::default_host() + .output_devices() + .expect("cannot get list of output devices") + { let device_name = device.name().expect("cannot get output name"); if device_name != default_device_name { println!(" {}", device_name); @@ -72,24 +59,23 @@ fn list_outputs(ref host: &cpal::Host) { } } -fn get_default_device(ref host: &cpal::Host) -> rodio::Device { - host.default_output_device() +fn get_default_device() -> rodio::Device { + cpal::default_host() + .default_output_device() .expect("no default output device available") } -fn match_device(ref host: &cpal::Host, device: Option) -> rodio::Device { +fn match_device(device: Option) -> rodio::Device { match device { Some(device_name) => { if device_name == "?".to_string() { - list_outputs(host); + list_outputs(); exit(0) } - - let found_devices = host.output_devices().expect(&format!( - "Cannot get list of output devices of Host: {:?}", - host.id() - )); - for d in found_devices { + for d in cpal::default_host() + .output_devices() + .expect("cannot get list of output devices") + { if d.name().expect("cannot get output name") == device_name { return d; } @@ -97,16 +83,18 @@ fn match_device(ref host: &cpal::Host, device: Option) -> rodio::Device println!("No output sink matching '{}' found.", device_name); exit(0) } - None => return get_default_device(host), + None => return get_default_device(), } } impl Open for RodioSink { fn open(device: Option) -> RodioSink { - let host = cpal::default_host(); - debug!("Using rodio sink with cpal host: {:?}", host.id()); + debug!( + "Using rodio sink with cpal host: {:?}", + cpal::default_host().id() + ); - let rodio_device = match_device(&host, device); + let rodio_device = match_device(device); debug!("Using cpal device"); let stream = rodio::OutputStream::try_from_device(&rodio_device) .expect("Couldn't open output stream."); @@ -121,36 +109,6 @@ impl Open for RodioSink { } } -#[cfg(all( - feature = "rodiojack-backend", - any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd") -))] -impl Open for JackRodioSink { - fn open(device: Option) -> JackRodioSink { - let host = cpal::host_from_id( - cpal::available_hosts() - .into_iter() - .find(|id| *id == cpal::HostId::Jack) - .expect("Jack Host not found"), - ) - .expect("Jack Host not found"); - debug!("Using jack rodio sink with cpal Jack host"); - - let rodio_device = match_device(&host, device); - debug!("Using cpal device"); - let stream = rodio::OutputStream::try_from_device(&rodio_device) - .expect("Couldn't open output stream."); - debug!("Using jack rodio stream"); - let sink = rodio::Sink::try_new(&stream.1).expect("Couldn't create output sink."); - debug!("Using jack rodio sink"); - - JackRodioSink { - jackrodio_sink: sink, - stream: stream.0, - } - } -} - impl Sink for RodioSink { fn start(&mut self) -> io::Result<()> { // More similar to an "unpause" than "play". Doesn't undo "stop". @@ -179,36 +137,3 @@ impl Sink for RodioSink { Ok(()) } } - -#[cfg(all( - feature = "rodiojack-backend", - any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd") -))] -impl Sink for JackRodioSink { - fn start(&mut self) -> io::Result<()> { - // More similar to an "unpause" than "play". Doesn't undo "stop". - // self.rodio_sink.play(); - Ok(()) - } - - fn stop(&mut self) -> io::Result<()> { - // This will immediately stop playback, but the sink is then unusable. - // We just have to let the current buffer play till the end. - // self.rodio_sink.stop(); - Ok(()) - } - - fn write(&mut self, data: &[i16]) -> io::Result<()> { - let source = rodio::buffer::SamplesBuffer::new(2, 44100, data); - self.jackrodio_sink.append(source); - - // Chunk sizes seem to be about 256 to 3000 ish items long. - // Assuming they're on average 1628 then a half second buffer is: - // 44100 elements --> about 27 chunks - while self.jackrodio_sink.len() > 26 { - // sleep and wait for rodio to drain a bit - thread::sleep(time::Duration::from_millis(10)); - } - Ok(()) - } -} From c0942f14e8868c3c0836438ed1d92cc698c8d38c Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Tue, 23 Feb 2021 15:05:02 +0100 Subject: [PATCH 036/103] Restore rodiojack support Probably more simple than the previous approach which doubles the code: Instead of implementing the `Open` trait, we simply use custom SinkBuilder, one for the default host, and one for the "jack" host. --- .github/workflows/test.yml | 1 + COMPILING.md | 1 + Cargo.lock | 1 + Cargo.toml | 1 + README.md | 1 + playback/Cargo.toml | 1 + playback/src/audio_backend/mod.rs | 9 +-- playback/src/audio_backend/rodio.rs | 94 +++++++++++++++++------------ 8 files changed, 65 insertions(+), 44 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4ad4b40..c20fe1c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -78,6 +78,7 @@ jobs: - run: cargo build --locked --no-default-features --features "portaudio-backend" - run: cargo build --locked --no-default-features --features "pulseaudio-backend" - run: cargo build --locked --no-default-features --features "jackaudio-backend" + - run: cargo build --locked --no-default-features --features "rodiojack-backend" - run: cargo build --locked --no-default-features --features "rodio-backend" - run: cargo build --locked --no-default-features --features "sdl-backend" - run: cargo build --locked --no-default-features --features "gstreamer-backend" diff --git a/COMPILING.md b/COMPILING.md index 7b3467e..40eefb3 100644 --- a/COMPILING.md +++ b/COMPILING.md @@ -46,6 +46,7 @@ Depending on the chosen backend, specific development libraries are required. |PortAudio | `portaudio19-dev` | `portaudio-devel` | `portaudio` | |PulseAudio | `libpulse-dev` | `pulseaudio-libs-devel` | | |JACK | `libjack-dev` | `jack-audio-connection-kit-devel` | | +|JACK over Rodio | `libjack-dev` | `jack-audio-connection-kit-devel` | - | |SDL | `libsdl2-dev` | `SDL2-devel` | | |Pipe | - | - | - | diff --git a/Cargo.lock b/Cargo.lock index 33dbb92..1e8176f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -433,6 +433,7 @@ dependencies = [ "alsa", "core-foundation-sys", "coreaudio-rs", + "jack", "jni 0.17.0", "js-sys", "lazy_static", diff --git a/Cargo.toml b/Cargo.toml index 21c010c..d34189e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -66,6 +66,7 @@ portaudio-backend = ["librespot-playback/portaudio-backend"] pulseaudio-backend = ["librespot-playback/pulseaudio-backend"] jackaudio-backend = ["librespot-playback/jackaudio-backend"] rodio-backend = ["librespot-playback/rodio-backend"] +rodiojack-backend = ["librespot-playback/rodiojack-backend"] sdl-backend = ["librespot-playback/sdl-backend"] gstreamer-backend = ["librespot-playback/gstreamer-backend"] diff --git a/README.md b/README.md index e7611aa..7102c28 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,7 @@ ALSA PortAudio PulseAudio JACK +JACK over Rodio SDL Pipe ``` diff --git a/playback/Cargo.toml b/playback/Cargo.toml index acb20c4..2759ae0 100644 --- a/playback/Cargo.toml +++ b/playback/Cargo.toml @@ -46,5 +46,6 @@ portaudio-backend = ["portaudio-rs"] pulseaudio-backend = ["libpulse-binding", "libpulse-simple-binding"] jackaudio-backend = ["jack"] rodio-backend = ["rodio", "cpal", "thiserror"] +rodiojack-backend = ["rodio", "cpal/jack", "thiserror"] sdl-backend = ["sdl2"] gstreamer-backend = ["gstreamer", "gstreamer-app", "glib", "zerocopy"] diff --git a/playback/src/audio_backend/mod.rs b/playback/src/audio_backend/mod.rs index 50031a4..214ede8 100644 --- a/playback/src/audio_backend/mod.rs +++ b/playback/src/audio_backend/mod.rs @@ -42,10 +42,9 @@ mod gstreamer; #[cfg(feature = "gstreamer-backend")] use self::gstreamer::GstreamerSink; -#[cfg(feature = "rodio-backend")] +#[cfg(any(feature = "rodio-backend", feature = "rodiojack-backend"))] mod rodio; -#[cfg(feature = "rodio-backend")] -use self::rodio::RodioSink; + #[cfg(feature = "sdl-backend")] mod sdl; #[cfg(feature = "sdl-backend")] @@ -69,7 +68,9 @@ pub const BACKENDS: &'static [(&'static str, SinkBuilder)] = &[ #[cfg(feature = "gstreamer-backend")] ("gstreamer", mk_sink::), #[cfg(feature = "rodio-backend")] - ("rodio", mk_sink::), + ("rodio", rodio::mk_rodio), + #[cfg(feature = "rodiojack-backend")] + ("rodiojack", rodio::mk_rodiojack), #[cfg(feature = "sdl-backend")] ("sdl", mk_sink::), ("pipe", mk_sink::), diff --git a/playback/src/audio_backend/rodio.rs b/playback/src/audio_backend/rodio.rs index 1b7a8b8..56e19b6 100644 --- a/playback/src/audio_backend/rodio.rs +++ b/playback/src/audio_backend/rodio.rs @@ -5,9 +5,28 @@ use std::{io, thread, time}; use cpal::traits::{DeviceTrait, HostTrait}; use thiserror::Error; -use super::{Open, Sink}; +use super::Sink; use crate::audio::AudioPacket; +#[cfg(all( + feature = "rodiojack-backend", + not(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd")) +))] +compile_error!("Rodio JACK backend is currently only supported on linux."); + +#[cfg(feature = "rodio-backend")] +pub fn mk_rodio(device: Option) -> Box { + Box::new(open(cpal::default_host(), device)) +} + +#[cfg(feature = "rodiojack-backend")] +pub fn mk_rodiojack(device: Option) -> Box { + Box::new(open( + cpal::host_from_id(cpal::HostId::Jack).unwrap(), + device, + )) +} + #[derive(Debug, Error)] pub enum RodioError { #[error("Rodio: no device available")] @@ -60,10 +79,10 @@ fn list_formats(device: &rodio::Device) { } } -fn list_outputs() -> Result<(), cpal::DevicesError> { +fn list_outputs(host: &cpal::Host) -> Result<(), cpal::DevicesError> { let mut default_device_name = None; - if let Some(default_device) = get_default_device() { + if let Some(default_device) = host.default_output_device() { default_device_name = default_device.name().ok(); println!( "Default Audio Device:\n {}", @@ -77,7 +96,7 @@ fn list_outputs() -> Result<(), cpal::DevicesError> { warn!("No default device was found"); } - for device in cpal::default_host().output_devices()? { + for device in host.output_devices()? { match device.name() { Ok(name) if Some(&name) == default_device_name.as_ref() => (), Ok(name) => { @@ -95,14 +114,13 @@ fn list_outputs() -> Result<(), cpal::DevicesError> { Ok(()) } -fn get_default_device() -> Option { - cpal::default_host().default_output_device() -} - -fn create_sink(device: Option) -> Result<(rodio::Sink, rodio::OutputStream), RodioError> { +fn create_sink( + host: &cpal::Host, + device: Option, +) -> Result<(rodio::Sink, rodio::OutputStream), RodioError> { let rodio_device = match device { Some(ask) if &ask == "?" => { - let exit_code = match list_outputs() { + let exit_code = match list_outputs(host) { Ok(()) => 0, Err(e) => { error!("{}", e); @@ -112,12 +130,13 @@ fn create_sink(device: Option) -> Result<(rodio::Sink, rodio::OutputStre exit(exit_code) } Some(device_name) => { - cpal::default_host() - .output_devices()? + host.output_devices()? .find(|d| d.name().ok().map_or(false, |name| name == device_name)) // Ignore devices for which getting name fails .ok_or(RodioError::DeviceNotAvailable(device_name))? } - None => get_default_device().ok_or(RodioError::NoDeviceAvailable)?, + None => host + .default_output_device() + .ok_or(RodioError::NoDeviceAvailable)?, }; let name = rodio_device.name().ok(); @@ -131,37 +150,32 @@ fn create_sink(device: Option) -> Result<(rodio::Sink, rodio::OutputStre Ok((sink, stream)) } -impl Open for RodioSink { - fn open(device: Option) -> RodioSink { - debug!( - "Using rodio sink with cpal host: {:?}", - cpal::default_host().id().name() - ); +pub fn open(host: cpal::Host, device: Option) -> RodioSink { + debug!("Using rodio sink with cpal host: {}", host.id().name()); - let (sink_tx, sink_rx) = mpsc::sync_channel(1); - let (close_tx, close_rx) = mpsc::sync_channel(1); + let (sink_tx, sink_rx) = mpsc::sync_channel(1); + let (close_tx, close_rx) = mpsc::sync_channel(1); - std::thread::spawn(move || match create_sink(device) { - Ok((sink, stream)) => { - sink_tx.send(Ok(sink)).unwrap(); + std::thread::spawn(move || match create_sink(&host, device) { + Ok((sink, stream)) => { + sink_tx.send(Ok(sink)).unwrap(); - close_rx.recv().unwrap_err(); // This will fail as soon as the sender is dropped - debug!("drop rodio::OutputStream"); - drop(stream); - } - Err(e) => { - sink_tx.send(Err(e)).unwrap(); - } - }); - - // Instead of the second `unwrap`, better error handling could be introduced - let sink = sink_rx.recv().unwrap().unwrap(); - - debug!("Rodio sink was created"); - RodioSink { - rodio_sink: sink, - _close_tx: close_tx, + close_rx.recv().unwrap_err(); // This will fail as soon as the sender is dropped + debug!("drop rodio::OutputStream"); + drop(stream); } + Err(e) => { + sink_tx.send(Err(e)).unwrap(); + } + }); + + // Instead of the second `unwrap`, better error handling could be introduced + let sink = sink_rx.recv().unwrap().unwrap(); + + debug!("Rodio sink was created"); + RodioSink { + rodio_sink: sink, + _close_tx: close_tx, } } From 9253be7bc9675c266000a06fac218f444b63d66c Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Wed, 10 Feb 2021 22:40:33 +0100 Subject: [PATCH 037/103] Small refactor of librespot-core * Remove default impl for `SessionConfig` * Move util mod to single file * Restore privacy of mods * Move `fn get_credentials` to application * Remove `extern crate` statements --- Cargo.lock | 10 ---------- core/Cargo.toml | 1 - core/build.rs | 3 --- core/src/authentication.rs | 24 ---------------------- core/src/config.rs | 15 -------------- core/src/lib.rs | 30 +++------------------------- core/src/{util/mod.rs => util.rs} | 0 src/main.rs | 33 ++++++++++++++++++++++++++----- 8 files changed, 31 insertions(+), 85 deletions(-) rename core/src/{util/mod.rs => util.rs} (100%) diff --git a/Cargo.lock b/Cargo.lock index 1e8176f..c9bcef5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1475,7 +1475,6 @@ dependencies = [ "tokio-util", "tower-service", "url 1.7.2", - "uuid", "vergen", ] @@ -2990,15 +2989,6 @@ dependencies = [ "percent-encoding 2.1.0", ] -[[package]] -name = "uuid" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" -dependencies = [ - "getrandom 0.2.2", -] - [[package]] name = "vergen" version = "3.2.0" diff --git a/core/Cargo.toml b/core/Cargo.toml index 36d7998..673637a 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -40,7 +40,6 @@ tokio = { version = "1.0", features = ["io-util", "rt-multi-thread"] } tokio-util = { version = "0.6", features = ["codec"] } tower-service = "0.3" url = "1.7" -uuid = { version = "0.8", features = ["v4"] } [build-dependencies] rand = "0.7" diff --git a/core/build.rs b/core/build.rs index e8c71e4..83f5047 100644 --- a/core/build.rs +++ b/core/build.rs @@ -1,6 +1,3 @@ -extern crate rand; -extern crate vergen; - use rand::distributions::Alphanumeric; use rand::Rng; use vergen::{generate_cargo_keys, ConstantsFlags}; diff --git a/core/src/authentication.rs b/core/src/authentication.rs index 5394ff3..fa57040 100644 --- a/core/src/authentication.rs +++ b/core/src/authentication.rs @@ -142,30 +142,6 @@ where base64::decode(&v).map_err(|e| serde::de::Error::custom(e.to_string())) } -pub fn get_credentials String>( - username: Option, - password: Option, - cached_credentials: Option, - prompt: F, -) -> Option { - match (username, password, cached_credentials) { - (Some(username), Some(password), _) => Some(Credentials::with_password(username, password)), - - (Some(ref username), _, Some(ref credentials)) if *username == credentials.username => { - Some(credentials.clone()) - } - - (Some(username), None, _) => Some(Credentials::with_password( - username.clone(), - prompt(&username), - )), - - (None, _, Some(credentials)) => Some(credentials), - - (None, _, None) => None, - } -} - error_chain! { types { AuthenticationError, AuthenticationErrorKind, AuthenticationResultExt, AuthenticationResult; diff --git a/core/src/config.rs b/core/src/config.rs index 60cb66e..469b935 100644 --- a/core/src/config.rs +++ b/core/src/config.rs @@ -1,9 +1,6 @@ use std::fmt; use std::str::FromStr; use url::Url; -use uuid::Uuid; - -use crate::version; #[derive(Clone, Debug)] pub struct SessionConfig { @@ -13,18 +10,6 @@ pub struct SessionConfig { pub ap_port: Option, } -impl Default for SessionConfig { - fn default() -> SessionConfig { - let device_id = Uuid::new_v4().to_hyphenated().to_string(); - SessionConfig { - user_agent: version::version_string(), - device_id: device_id, - proxy: None, - ap_port: None, - } - } -} - #[derive(Clone, Copy, Debug, Hash, PartialOrd, Ord, PartialEq, Eq)] pub enum DeviceType { Unknown = 0, diff --git a/core/src/lib.rs b/core/src/lib.rs index 25ce541..6c180c2 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -8,43 +8,19 @@ extern crate serde_derive; extern crate pin_project_lite; #[macro_use] extern crate error_chain; -extern crate aes; -extern crate base64; -extern crate byteorder; -extern crate bytes; -extern crate futures; -extern crate hmac; -extern crate httparse; -extern crate hyper; -extern crate num_bigint; -extern crate num_integer; -extern crate num_traits; -extern crate once_cell; -extern crate pbkdf2; -extern crate protobuf; -extern crate rand; -extern crate serde; -extern crate serde_json; -extern crate sha1; -extern crate shannon; -pub extern crate tokio; -extern crate tokio_util; -extern crate tower_service; -extern crate url; -extern crate uuid; -extern crate librespot_protocol as protocol; +use librespot_protocol as protocol; #[macro_use] mod component; -pub mod apresolve; +mod apresolve; pub mod audio_key; pub mod authentication; pub mod cache; pub mod channel; pub mod config; -pub mod connection; +mod connection; pub mod diffie_hellman; pub mod keymaster; pub mod mercury; diff --git a/core/src/util/mod.rs b/core/src/util.rs similarity index 100% rename from core/src/util/mod.rs rename to core/src/util.rs diff --git a/src/main.rs b/src/main.rs index 71041f5..31ef300 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,7 +12,7 @@ use std::{ }; use url::Url; -use librespot::core::authentication::{get_credentials, Credentials}; +use librespot::core::authentication::Credentials; use librespot::core::cache::Cache; use librespot::core::config::{ConnectConfig, DeviceType, SessionConfig, VolumeCtrl}; use librespot::core::session::Session; @@ -70,6 +70,29 @@ fn list_backends() { } } +pub fn get_credentials Option>( + username: Option, + password: Option, + cached_credentials: Option, + prompt: F, +) -> Option { + if let Some(username) = username { + if let Some(password) = password { + return Some(Credentials::with_password(username, password)); + } + + match cached_credentials { + Some(credentials) if username == credentials.username => Some(credentials), + _ => { + let password = prompt(&username)?; + Some(Credentials::with_password(username, password)) + } + } + } else { + cached_credentials + } +} + #[derive(Clone)] struct Setup { backend: fn(Option) -> Box, @@ -317,10 +340,10 @@ fn setup(args: &[String]) -> Setup { let credentials = { let cached_credentials = cache.as_ref().and_then(Cache::credentials); - let password = |username: &String| -> String { - write!(stderr(), "Password for {}: ", username).unwrap(); - stderr().flush().unwrap(); - rpassword::read_password().unwrap() + let password = |username: &String| -> Option { + write!(stderr(), "Password for {}: ", username).ok()?; + stderr().flush().ok()?; + rpassword::read_password().ok() }; get_credentials( From 8cff10e983bcf87b44de01229e2e5154a125369e Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Wed, 10 Feb 2021 22:50:08 +0100 Subject: [PATCH 038/103] Put apresolve behind feature flag --- Cargo.lock | 2 +- Cargo.toml | 5 +- core/Cargo.toml | 10 ++-- core/src/apresolve.rs | 103 ++++++++++++++++++++++------------------ core/src/lib.rs | 2 + core/src/proxytunnel.rs | 103 +++++++++++++++++++++------------------- 6 files changed, 124 insertions(+), 101 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c9bcef5..282888d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1450,6 +1450,7 @@ dependencies = [ "base64", "byteorder", "bytes", + "cfg-if 1.0.0", "env_logger", "error-chain", "futures", @@ -1473,7 +1474,6 @@ dependencies = [ "shannon", "tokio", "tokio-util", - "tower-service", "url 1.7.2", "vergen", ] diff --git a/Cargo.toml b/Cargo.toml index d34189e..617ae08 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,6 +61,9 @@ sha-1 = "0.8" hex = "0.4" [features] +apresolve = ["librespot-core/apresolve"] +apresolve-http2 = ["librespot-core/apresolve-http2"] + alsa-backend = ["librespot-playback/alsa-backend"] portaudio-backend = ["librespot-playback/portaudio-backend"] pulseaudio-backend = ["librespot-playback/pulseaudio-backend"] @@ -75,7 +78,7 @@ with-vorbis = ["librespot-audio/with-vorbis"] # with-dns-sd = ["librespot-connect/with-dns-sd"] -default = ["librespot-playback/rodio-backend"] +default = ["rodio-backend", "apresolve"] [package.metadata.deb] maintainer = "librespot-org" diff --git a/core/Cargo.toml b/core/Cargo.toml index 673637a..f1a15eb 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -17,11 +17,12 @@ aes = "0.6" base64 = "0.13" byteorder = "1.4" bytes = "1.0" +cfg-if = "1" error-chain = { version = "0.12", default-features = false } futures = { version = "0.3", features = ["bilock", "unstable"] } hmac = "0.10" httparse = "1.3" -hyper = { version = "0.14", features = ["client", "tcp", "http1", "http2"] } +hyper = { version = "0.14", optional = true, features = ["client", "tcp", "http1"] } log = "0.4" num-bigint = "0.3" num-integer = "0.1" @@ -38,7 +39,6 @@ sha-1 = "0.9" shannon = "0.2.0" tokio = { version = "1.0", features = ["io-util", "rt-multi-thread"] } tokio-util = { version = "0.6", features = ["codec"] } -tower-service = "0.3" url = "1.7" [build-dependencies] @@ -47,4 +47,8 @@ vergen = "3.0.4" [dev-dependencies] env_logger = "*" -tokio = {version = "1.0", features = ["macros"] } \ No newline at end of file +tokio = {version = "1.0", features = ["macros"] } + +[features] +apresolve = ["hyper"] +apresolve-http2 = ["apresolve", "hyper/http2"] diff --git a/core/src/apresolve.rs b/core/src/apresolve.rs index 81340c9..cd354d8 100644 --- a/core/src/apresolve.rs +++ b/core/src/apresolve.rs @@ -1,61 +1,72 @@ const AP_FALLBACK: &'static str = "ap.spotify.com:443"; -const APRESOLVE_ENDPOINT: &'static str = "http://apresolve.spotify.com:80"; -use hyper::{Body, Client, Method, Request, Uri}; -use std::error::Error; use url::Url; -use crate::proxytunnel::ProxyTunnel; +cfg_if! { + if #[cfg(feature = "apresolve")] { + const APRESOLVE_ENDPOINT: &'static str = "http://apresolve.spotify.com:80"; -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct APResolveData { - ap_list: Vec, -} + use std::error::Error; -async fn apresolve(proxy: &Option, ap_port: &Option) -> Result> { - let port = ap_port.unwrap_or(443); + use hyper::{Body, Client, Method, Request, Uri}; - let req = Request::builder() - .method(Method::GET) - .uri( - APRESOLVE_ENDPOINT - .parse::() - .expect("invalid AP resolve URL"), - ) - .body(Body::empty())?; + use crate::proxytunnel::ProxyTunnel; - let response = if let Some(url) = proxy { - Client::builder() - .build(ProxyTunnel::new(url)?) - .request(req) - .await? - } else { - Client::new().request(req).await? - }; + #[derive(Clone, Debug, Serialize, Deserialize)] + pub struct APResolveData { + ap_list: Vec, + } - let body = hyper::body::to_bytes(response.into_body()).await?; - let data: APResolveData = serde_json::from_slice(body.as_ref())?; + async fn apresolve(proxy: &Option, ap_port: &Option) -> Result> { + let port = ap_port.unwrap_or(443); - let ap = if ap_port.is_some() || proxy.is_some() { - data.ap_list.into_iter().find_map(|ap| { - if ap.parse::().ok()?.port()? == port { - Some(ap) + let req = Request::builder() + .method(Method::GET) + .uri( + APRESOLVE_ENDPOINT + .parse::() + .expect("invalid AP resolve URL"), + ) + .body(Body::empty())?; + + let response = if let Some(url) = proxy { + Client::builder() + .build(ProxyTunnel::new(url)?) + .request(req) + .await? } else { - None + Client::new().request(req).await? + }; + + let body = hyper::body::to_bytes(response.into_body()).await?; + let data: APResolveData = serde_json::from_slice(body.as_ref())?; + + let ap = if ap_port.is_some() || proxy.is_some() { + data.ap_list.into_iter().find_map(|ap| { + if ap.parse::().ok()?.port()? == port { + Some(ap) + } else { + None + } + }) + } else { + data.ap_list.into_iter().next() } - }) + .ok_or("empty AP List")?; + + Ok(ap) + } + + pub async fn apresolve_or_fallback(proxy: &Option, ap_port: &Option) -> String { + apresolve(proxy, ap_port).await.unwrap_or_else(|e| { + warn!("Failed to resolve Access Point: {}", e); + warn!("Using fallback \"{}\"", AP_FALLBACK); + AP_FALLBACK.into() + }) + } } else { - data.ap_list.into_iter().next() + pub async fn apresolve_or_fallback(_: &Option, _: &Option) -> String { + AP_FALLBACK.to_string() + } } - .ok_or("empty AP List")?; - - Ok(ap) -} - -pub async fn apresolve_or_fallback(proxy: &Option, ap_port: &Option) -> String { - apresolve(proxy, ap_port).await.unwrap_or_else(|e| { - warn!("Failed to resolve Access Point: {}", e); - warn!("Using fallback \"{}\"", AP_FALLBACK); - AP_FALLBACK.into() - }) } diff --git a/core/src/lib.rs b/core/src/lib.rs index 6c180c2..65fa898 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -3,6 +3,8 @@ #[macro_use] extern crate log; #[macro_use] +extern crate cfg_if; +#[macro_use] extern crate serde_derive; #[macro_use] extern crate pin_project_lite; diff --git a/core/src/proxytunnel.rs b/core/src/proxytunnel.rs index c2033c8..158d314 100644 --- a/core/src/proxytunnel.rs +++ b/core/src/proxytunnel.rs @@ -1,16 +1,6 @@ -use futures::Future; -use hyper::Uri; -use std::{ - io, - net::{SocketAddr, ToSocketAddrs}, - pin::Pin, - task::Poll, -}; -use tokio::{ - io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}, - net::TcpStream, -}; -use tower_service::Service; +use std::io; + +use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; pub async fn connect( mut proxy_connection: T, @@ -64,43 +54,56 @@ pub async fn connect( } } -#[derive(Clone)] -pub struct ProxyTunnel { - proxy_addr: SocketAddr, -} +cfg_if! { + if #[cfg(feature = "apresolve")] { + use std::future::Future; + use std::net::{SocketAddr, ToSocketAddrs}; + use std::pin::Pin; + use std::task::Poll; -impl ProxyTunnel { - pub fn new(addr: T) -> io::Result { - let addr = addr.to_socket_addrs()?.next().ok_or_else(|| { - io::Error::new(io::ErrorKind::InvalidInput, "No socket address given") - })?; - Ok(Self { proxy_addr: addr }) - } -} - -impl Service for ProxyTunnel { - type Response = TcpStream; - type Error = io::Error; - type Future = Pin> + Send>>; - - fn poll_ready(&mut self, _: &mut std::task::Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } - - fn call(&mut self, url: Uri) -> Self::Future { - let proxy_addr = self.proxy_addr; - let fut = async move { - let host = url - .host() - .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "Host is missing"))?; - let port = url - .port() - .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "Port is missing"))?; - - let conn = TcpStream::connect(proxy_addr).await?; - connect(conn, host, port.as_u16()).await - }; - - Box::pin(fut) + use hyper::service::Service; + use hyper::Uri; + use tokio::net::TcpStream; + + #[derive(Clone)] + pub struct ProxyTunnel { + proxy_addr: SocketAddr, + } + + impl ProxyTunnel { + pub fn new(addr: T) -> io::Result { + let addr = addr.to_socket_addrs()?.next().ok_or_else(|| { + io::Error::new(io::ErrorKind::InvalidInput, "No socket address given") + })?; + Ok(Self { proxy_addr: addr }) + } + } + + impl Service for ProxyTunnel { + type Response = TcpStream; + type Error = io::Error; + type Future = Pin> + Send>>; + + fn poll_ready(&mut self, _: &mut std::task::Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, url: Uri) -> Self::Future { + let proxy_addr = self.proxy_addr; + let fut = async move { + let host = url + .host() + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "Host is missing"))?; + let port = url + .port() + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "Port is missing"))?; + + let conn = TcpStream::connect(proxy_addr).await?; + connect(conn, host, port.as_u16()).await + }; + + Box::pin(fut) + } + } } } From 10827bd6a8a2a098b35b5d6ad766ef34d460d3b6 Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Wed, 10 Feb 2021 22:54:35 +0100 Subject: [PATCH 039/103] Clean up dependencies of librespot-core * Use sub-crates of future * Remove unnecessary pin-project * Removed unused crates and features * Replace futures channels by tokio channels * Use serde's "derive" feature flag instead of serde_derive --- Cargo.lock | 20 ++++++++++++++++---- core/Cargo.toml | 17 +++++++++-------- core/build.rs | 6 +++--- core/src/apresolve.rs | 1 + core/src/audio_key.rs | 2 +- core/src/authentication.rs | 5 ++++- core/src/channel.rs | 22 ++++++++++++---------- core/src/connection/mod.rs | 2 +- core/src/keymaster.rs | 2 ++ core/src/lib.rs | 4 ---- core/src/mercury/mod.rs | 34 +++++++++++++++++----------------- core/src/session.rs | 23 +++++++++++++++-------- 12 files changed, 81 insertions(+), 57 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 282888d..7fe8387 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1453,7 +1453,9 @@ dependencies = [ "cfg-if 1.0.0", "env_logger", "error-chain", - "futures", + "futures-core", + "futures-sink", + "futures-util", "hmac", "httparse", "hyper", @@ -1464,15 +1466,14 @@ dependencies = [ "num-traits", "once_cell", "pbkdf2", - "pin-project-lite", "protobuf", - "rand 0.7.3", + "rand 0.8.3", "serde", - "serde_derive", "serde_json", "sha-1 0.9.4", "shannon", "tokio", + "tokio-stream", "tokio-util", "url 1.7.2", "vergen", @@ -2819,6 +2820,17 @@ dependencies = [ "syn", ] +[[package]] +name = "tokio-stream" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1981ad97df782ab506a1f43bf82c967326960d278acf3bf8279809648c3ff3ea" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + [[package]] name = "tokio-util" version = "0.6.3" diff --git a/core/Cargo.toml b/core/Cargo.toml index f1a15eb..85f3be6 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -19,7 +19,9 @@ byteorder = "1.4" bytes = "1.0" cfg-if = "1" error-chain = { version = "0.12", default-features = false } -futures = { version = "0.3", features = ["bilock", "unstable"] } +futures-core = { version = "0.3", default-features = false } +futures-sink = { version = "0.3", default-features = false } +futures-util = { version = "0.3", default-features = false, features = ["alloc", "bilock", "unstable", "sink"] } hmac = "0.10" httparse = "1.3" hyper = { version = "0.14", optional = true, features = ["client", "tcp", "http1"] } @@ -28,21 +30,20 @@ num-bigint = "0.3" num-integer = "0.1" num-traits = "0.2" once_cell = "1.5.2" -pbkdf2 = { version = "0.7", default_features = false, features = ["hmac"] } -pin-project-lite = "0.2.4" +pbkdf2 = { version = "0.7", default-features = false, features = ["hmac"] } protobuf = "~2.14.0" -rand = "0.7" -serde = "1.0" -serde_derive = "1.0" +rand = "0.8" +serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" sha-1 = "0.9" shannon = "0.2.0" -tokio = { version = "1.0", features = ["io-util", "rt-multi-thread"] } +tokio = { version = "1.0", features = ["io-util", "net", "rt", "sync"] } +tokio-stream = "0.1" tokio-util = { version = "0.6", features = ["codec"] } url = "1.7" [build-dependencies] -rand = "0.7" +rand = "0.8" vergen = "3.0.4" [dev-dependencies] diff --git a/core/build.rs b/core/build.rs index 83f5047..0fc2933 100644 --- a/core/build.rs +++ b/core/build.rs @@ -7,10 +7,10 @@ fn main() { flags.toggle(ConstantsFlags::REBUILD_ON_HEAD_CHANGE); generate_cargo_keys(ConstantsFlags::all()).expect("Unable to generate the cargo keys!"); - let mut rng = rand::thread_rng(); - let build_id: String = ::std::iter::repeat(()) - .map(|()| rng.sample(Alphanumeric)) + let build_id: String = rand::thread_rng() + .sample_iter(Alphanumeric) .take(8) + .map(char::from) .collect(); println!("cargo:rustc-env=VERGEN_BUILD_ID={}", build_id); } diff --git a/core/src/apresolve.rs b/core/src/apresolve.rs index cd354d8..7698691 100644 --- a/core/src/apresolve.rs +++ b/core/src/apresolve.rs @@ -9,6 +9,7 @@ cfg_if! { use std::error::Error; use hyper::{Body, Client, Method, Request, Uri}; + use serde::{Serialize, Deserialize}; use crate::proxytunnel::ProxyTunnel; diff --git a/core/src/audio_key.rs b/core/src/audio_key.rs index b9f0c23..3bce1c7 100644 --- a/core/src/audio_key.rs +++ b/core/src/audio_key.rs @@ -1,8 +1,8 @@ use byteorder::{BigEndian, ByteOrder, WriteBytesExt}; use bytes::Bytes; -use futures::channel::oneshot; use std::collections::HashMap; use std::io::Write; +use tokio::sync::oneshot; use crate::spotify_id::{FileId, SpotifyId}; use crate::util::SeqGenerator; diff --git a/core/src/authentication.rs b/core/src/authentication.rs index fa57040..ff477df 100644 --- a/core/src/authentication.rs +++ b/core/src/authentication.rs @@ -1,10 +1,13 @@ +use std::io::{self, Read}; +use std::ops::FnOnce; + use aes::Aes192; use byteorder::{BigEndian, ByteOrder}; use hmac::Hmac; use pbkdf2::pbkdf2; use protobuf::ProtobufEnum; +use serde::{Deserialize, Serialize}; use sha1::{Digest, Sha1}; -use std::io::{self, Read}; use crate::protocol::authentication::AuthenticationType; use crate::protocol::keyexchange::{APLoginFailed, ErrorCode}; diff --git a/core/src/channel.rs b/core/src/channel.rs index 7ada05d..54eee18 100644 --- a/core/src/channel.rs +++ b/core/src/channel.rs @@ -1,12 +1,14 @@ +use std::collections::HashMap; +use std::pin::Pin; +use std::task::{Context, Poll}; +use std::time::Instant; + use byteorder::{BigEndian, ByteOrder}; use bytes::Bytes; -use futures::{channel::mpsc, lock::BiLock, Stream, StreamExt}; -use std::{ - collections::HashMap, - pin::Pin, - task::{Context, Poll}, - time::Instant, -}; +use futures_core::Stream; +use futures_util::lock::BiLock; +use futures_util::StreamExt; +use tokio::sync::mpsc; use crate::util::SeqGenerator; @@ -46,7 +48,7 @@ enum ChannelState { impl ChannelManager { pub fn allocate(&self) -> (u16, Channel) { - let (tx, rx) = mpsc::unbounded(); + let (tx, rx) = mpsc::unbounded_channel(); let seq = self.lock(|inner| { let seq = inner.sequence.get(); @@ -85,7 +87,7 @@ impl ChannelManager { inner.download_measurement_bytes += data.len(); if let Entry::Occupied(entry) = inner.channels.entry(id) { - let _ = entry.get().unbounded_send((cmd, data)); + let _ = entry.get().send((cmd, data)); } }); } @@ -105,7 +107,7 @@ impl ChannelManager { impl Channel { fn recv_packet(&mut self, cx: &mut Context<'_>) -> Poll> { - let (cmd, packet) = match self.receiver.poll_next_unpin(cx) { + let (cmd, packet) = match self.receiver.poll_recv(cx) { Poll::Pending => return Poll::Pending, Poll::Ready(o) => o.ok_or(ChannelError)?, }; diff --git a/core/src/connection/mod.rs b/core/src/connection/mod.rs index 68e2e7a..6bdbde6 100644 --- a/core/src/connection/mod.rs +++ b/core/src/connection/mod.rs @@ -4,7 +4,7 @@ mod handshake; pub use self::codec::APCodec; pub use self::handshake::handshake; -use futures::{SinkExt, StreamExt}; +use futures_util::{SinkExt, StreamExt}; use protobuf::{self, Message}; use std::io; use std::net::ToSocketAddrs; diff --git a/core/src/keymaster.rs b/core/src/keymaster.rs index 87b3f1e..8c3c00a 100644 --- a/core/src/keymaster.rs +++ b/core/src/keymaster.rs @@ -1,3 +1,5 @@ +use serde::Deserialize; + use crate::{mercury::MercuryError, session::Session}; #[derive(Deserialize, Debug, Clone)] diff --git a/core/src/lib.rs b/core/src/lib.rs index 65fa898..54f83f1 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -5,10 +5,6 @@ extern crate log; #[macro_use] extern crate cfg_if; #[macro_use] -extern crate serde_derive; -#[macro_use] -extern crate pin_project_lite; -#[macro_use] extern crate error_chain; use librespot_protocol as protocol; diff --git a/core/src/mercury/mod.rs b/core/src/mercury/mod.rs index 4baa674..537ff2c 100644 --- a/core/src/mercury/mod.rs +++ b/core/src/mercury/mod.rs @@ -1,14 +1,17 @@ +use std::collections::HashMap; +use std::future::Future; +use std::mem; +use std::pin::Pin; +use std::task::Context; +use std::task::Poll; + +use byteorder::{BigEndian, ByteOrder}; +use bytes::Bytes; +use tokio::sync::{mpsc, oneshot}; + use crate::protocol; use crate::util::url_encode; use crate::util::SeqGenerator; -use byteorder::{BigEndian, ByteOrder}; -use bytes::Bytes; -use futures::{ - channel::{mpsc, oneshot}, - Future, -}; -use std::{collections::HashMap, task::Poll}; -use std::{mem, pin::Pin, task::Context}; mod types; pub use self::types::*; @@ -31,18 +34,15 @@ pub struct MercuryPending { callback: Option>>, } -pin_project! { - pub struct MercuryFuture { - #[pin] - receiver: oneshot::Receiver> - } +pub struct MercuryFuture { + receiver: oneshot::Receiver>, } impl Future for MercuryFuture { type Output = Result; - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - match self.project().receiver.poll(cx) { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + match Pin::new(&mut self.receiver).poll(cx) { Poll::Ready(Ok(x)) => Poll::Ready(x), Poll::Ready(Err(_)) => Poll::Ready(Err(MercuryError)), Poll::Pending => Poll::Pending, @@ -119,7 +119,7 @@ impl MercuryManager { async move { let response = request.await?; - let (tx, rx) = mpsc::unbounded(); + let (tx, rx) = mpsc::unbounded_channel(); manager.lock(move |inner| { if !inner.invalid { @@ -221,7 +221,7 @@ impl MercuryManager { // if send fails, remove from list of subs // TODO: send unsub message - sub.unbounded_send(response.clone()).is_ok() + sub.send(response.clone()).is_ok() } else { // URI doesn't match true diff --git a/core/src/session.rs b/core/src/session.rs index b0eca0c..858a0b6 100644 --- a/core/src/session.rs +++ b/core/src/session.rs @@ -1,14 +1,19 @@ +use std::future::Future; +use std::io; +use std::pin::Pin; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, RwLock, Weak}; +use std::task::Context; use std::task::Poll; use std::time::{SystemTime, UNIX_EPOCH}; -use std::{io, pin::Pin, task::Context}; - -use once_cell::sync::OnceCell; use byteorder::{BigEndian, ByteOrder}; use bytes::Bytes; -use futures::{channel::mpsc, Future, FutureExt, StreamExt, TryStream, TryStreamExt}; +use futures_core::TryStream; +use futures_util::{FutureExt, StreamExt, TryStreamExt}; +use once_cell::sync::OnceCell; +use tokio::sync::mpsc; +use tokio_stream::wrappers::UnboundedReceiverStream; use crate::apresolve::apresolve_or_fallback; use crate::audio_key::AudioKeyManager; @@ -87,7 +92,7 @@ impl Session { ) -> Session { let (sink, stream) = transport.split(); - let (sender_tx, sender_rx) = mpsc::unbounded(); + let (sender_tx, sender_rx) = mpsc::unbounded_channel(); let session_id = SESSION_COUNTER.fetch_add(1, Ordering::Relaxed); debug!("new Session[{}]", session_id); @@ -114,11 +119,13 @@ impl Session { session_id: session_id, })); - let sender_task = sender_rx.map(Ok::<_, io::Error>).forward(sink); + let sender_task = UnboundedReceiverStream::new(sender_rx) + .map(Ok) + .forward(sink); let receiver_task = DispatchTask(stream, session.weak()); let task = - futures::future::join(sender_task, receiver_task).map(|_| io::Result::<_>::Ok(())); + futures_util::future::join(sender_task, receiver_task).map(|_| io::Result::<_>::Ok(())); tokio::spawn(task); session } @@ -193,7 +200,7 @@ impl Session { } pub fn send_packet(&self, cmd: u8, data: Vec) { - self.0.tx_connection.unbounded_send((cmd, data)).unwrap(); + self.0.tx_connection.send((cmd, data)).unwrap(); } pub fn cache(&self) -> Option<&Arc> { From a6ed6857d25e0eff8fd16bf14d17f5e2be6bf644 Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Wed, 10 Feb 2021 23:03:33 +0100 Subject: [PATCH 040/103] Clean up dependencies in librespot-metadata * Replaced LinearMap by HashMap * Removed unnecessary dependencies * Removed "extern crate"s --- Cargo.lock | 8 -------- metadata/Cargo.toml | 2 -- metadata/src/lib.rs | 18 ++++++------------ 3 files changed, 6 insertions(+), 22 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7fe8387..94aea40 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1485,10 +1485,8 @@ version = "0.1.6" dependencies = [ "async-trait", "byteorder", - "futures", "librespot-core", "librespot-protocol", - "linear-map", "log", "protobuf", ] @@ -1542,12 +1540,6 @@ dependencies = [ "pkg-config", ] -[[package]] -name = "linear-map" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfae20f6b19ad527b550c223fddc3077a547fc70cda94b9b566575423fd303ee" - [[package]] name = "lock_api" version = "0.4.2" diff --git a/metadata/Cargo.toml b/metadata/Cargo.toml index 6baae5d..f3087b8 100644 --- a/metadata/Cargo.toml +++ b/metadata/Cargo.toml @@ -10,8 +10,6 @@ edition = "2018" [dependencies] async-trait = "0.1" byteorder = "1.3" -futures = "0.3" -linear-map = "1.2" protobuf = "~2.14.0" log = "0.4" diff --git a/metadata/src/lib.rs b/metadata/src/lib.rs index f71bae9..8faa027 100644 --- a/metadata/src/lib.rs +++ b/metadata/src/lib.rs @@ -1,26 +1,20 @@ #![allow(clippy::unused_io_amount)] #![allow(clippy::redundant_field_names)] + #[macro_use] extern crate log; #[macro_use] extern crate async_trait; -extern crate byteorder; -extern crate futures; -extern crate linear_map; -extern crate protobuf; - -extern crate librespot_core; -extern crate librespot_protocol as protocol; - pub mod cover; -use linear_map::LinearMap; +use std::collections::HashMap; use librespot_core::mercury::MercuryError; use librespot_core::session::Session; use librespot_core::spotify_id::{FileId, SpotifyAudioType, SpotifyId}; +use librespot_protocol as protocol; pub use crate::protocol::metadata::AudioFile_Format as FileFormat; @@ -64,7 +58,7 @@ where pub struct AudioItem { pub id: SpotifyId, pub uri: String, - pub files: LinearMap, + pub files: HashMap, pub name: String, pub duration: i32, pub available: bool, @@ -143,7 +137,7 @@ pub struct Track { pub duration: i32, pub album: SpotifyId, pub artists: Vec, - pub files: LinearMap, + pub files: HashMap, pub alternatives: Vec, pub available: bool, } @@ -165,7 +159,7 @@ pub struct Episode { pub duration: i32, pub language: String, pub show: SpotifyId, - pub files: LinearMap, + pub files: HashMap, pub covers: Vec, pub available: bool, pub explicit: bool, From 746e6c863eeb58a0dc55ed468edac3d8e85823f7 Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Wed, 10 Feb 2021 23:07:59 +0100 Subject: [PATCH 041/103] Put lewton behind feature flag --- Cargo.lock | 1 + Cargo.toml | 3 ++- audio/Cargo.toml | 6 ++++-- audio/src/lib.rs | 32 ++++++++++++++++++++++---------- 4 files changed, 29 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 94aea40..55bc1f7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1402,6 +1402,7 @@ dependencies = [ "bit-set", "byteorder", "bytes", + "cfg-if 1.0.0", "futures", "lewton", "librespot-core", diff --git a/Cargo.toml b/Cargo.toml index 617ae08..a26db2e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -75,10 +75,11 @@ gstreamer-backend = ["librespot-playback/gstreamer-backend"] with-tremor = ["librespot-audio/with-tremor"] with-vorbis = ["librespot-audio/with-vorbis"] +with-lewton = ["librespot-audio/with-lewton"] # with-dns-sd = ["librespot-connect/with-dns-sd"] -default = ["rodio-backend", "apresolve"] +default = ["rodio-backend", "apresolve", "with-lewton"] [package.metadata.deb] maintainer = "librespot-org" diff --git a/audio/Cargo.toml b/audio/Cargo.toml index 96af08f..01d81f0 100644 --- a/audio/Cargo.toml +++ b/audio/Cargo.toml @@ -15,18 +15,20 @@ aes-ctr = "0.6" bit-set = "0.5" byteorder = "1.4" bytes = "1.0" +cfg-if = "1" futures = "0.3" -lewton = "0.10" -ogg = "0.8" log = "0.4" num-bigint = "0.3" num-traits = "0.2" +ogg = "0.8" pin-project-lite = "0.2.4" tempfile = "3.1" +lewton = { version = "0.10", optional = true } librespot-tremor = { version = "0.2.0", optional = true } vorbis = { version ="0.0.14", optional = true } [features] +with-lewton = ["lewton"] with-tremor = ["librespot-tremor"] with-vorbis = ["vorbis"] diff --git a/audio/src/lib.rs b/audio/src/lib.rs index 3f22ac5..8b3a8df 100644 --- a/audio/src/lib.rs +++ b/audio/src/lib.rs @@ -19,11 +19,29 @@ extern crate librespot_core; mod decrypt; mod fetch; -#[cfg(not(any(feature = "with-tremor", feature = "with-vorbis")))] -mod lewton_decoder; -#[cfg(any(feature = "with-tremor", feature = "with-vorbis"))] -mod libvorbis_decoder; +use cfg_if::cfg_if; + +#[cfg(any( + all(feature = "with-lewton", feature = "with-tremor"), + all(feature = "with-vorbis", feature = "with-tremor"), + all(feature = "with-lewton", feature = "with-vorbis") +))] +compile_error!("Cannot use two decoders at the same time."); + +cfg_if! { + if #[cfg(feature = "with-lewton")] { + mod lewton_decoder; + pub use lewton_decoder::{VorbisDecoder, VorbisError}; + } else if #[cfg(any(feature = "with-tremor", feature = "with-vorbis"))] { + mod libvorbis_decoder; + pub use crate::libvorbis_decoder::{VorbisDecoder, VorbisError}; + } else { + compile_error!("Must choose a vorbis decoder."); + } +} + mod passthrough_decoder; +pub use passthrough_decoder::{PassthroughDecoder, PassthroughError}; mod range_set; @@ -63,12 +81,6 @@ impl AudioPacket { } } -#[cfg(not(any(feature = "with-tremor", feature = "with-vorbis")))] -pub use crate::lewton_decoder::{VorbisDecoder, VorbisError}; -#[cfg(any(feature = "with-tremor", feature = "with-vorbis"))] -pub use libvorbis_decoder::{VorbisDecoder, VorbisError}; -pub use passthrough_decoder::{PassthroughDecoder, PassthroughError}; - #[derive(Debug)] pub enum AudioError { PassthroughError(PassthroughError), From b83976a8ec29f7b34ddcdadb02254dd8f90f7e2a Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Wed, 10 Feb 2021 23:13:32 +0100 Subject: [PATCH 042/103] Remove "extern crate"s from librespot-audio --- audio/src/lewton_decoder.rs | 12 +++++------- audio/src/lib.rs | 11 ----------- audio/src/libvorbis_decoder.rs | 4 +--- 3 files changed, 6 insertions(+), 21 deletions(-) diff --git a/audio/src/lewton_decoder.rs b/audio/src/lewton_decoder.rs index 1addaa0..086ea57 100644 --- a/audio/src/lewton_decoder.rs +++ b/audio/src/lewton_decoder.rs @@ -1,6 +1,4 @@ -extern crate lewton; - -use self::lewton::inside_ogg::OggStreamReader; +use lewton::inside_ogg::OggStreamReader; use super::{AudioDecoder, AudioError, AudioPacket}; use std::error; @@ -32,10 +30,10 @@ where } fn next_packet(&mut self) -> Result, AudioError> { - use self::lewton::audio::AudioReadError::AudioIsHeader; - use self::lewton::OggReadError::NoCapturePatternFound; - use self::lewton::VorbisError::BadAudio; - use self::lewton::VorbisError::OggError; + use lewton::audio::AudioReadError::AudioIsHeader; + use lewton::OggReadError::NoCapturePatternFound; + use lewton::VorbisError::BadAudio; + use lewton::VorbisError::OggError; loop { match self.0.read_dec_packet_itl() { Ok(Some(packet)) => return Ok(Some(AudioPacket::Samples(packet))), diff --git a/audio/src/lib.rs b/audio/src/lib.rs index 8b3a8df..9bb6f8e 100644 --- a/audio/src/lib.rs +++ b/audio/src/lib.rs @@ -5,17 +5,6 @@ extern crate log; #[macro_use] extern crate pin_project_lite; -extern crate aes_ctr; -extern crate bit_set; -extern crate byteorder; -extern crate bytes; -extern crate futures; -extern crate num_bigint; -extern crate num_traits; -extern crate tempfile; - -extern crate librespot_core; - mod decrypt; mod fetch; diff --git a/audio/src/libvorbis_decoder.rs b/audio/src/libvorbis_decoder.rs index 8aced55..eeef8ab 100644 --- a/audio/src/libvorbis_decoder.rs +++ b/audio/src/libvorbis_decoder.rs @@ -1,7 +1,5 @@ #[cfg(feature = "with-tremor")] -extern crate librespot_tremor as vorbis; -#[cfg(not(feature = "with-tremor"))] -extern crate vorbis; +use librespot_tremor as vorbis; use super::{AudioDecoder, AudioError, AudioPacket}; use std::error; From 5c42d2e879ae7b72c710a68ff992dfd792477d65 Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Wed, 10 Feb 2021 23:18:13 +0100 Subject: [PATCH 043/103] Clean up dependencies in librespot-audio * Remove unused deps * Use futures-util instead of futures * Replace futures channels by tokio channels * Remove unnecessary pin_project * Reordered "use" statements --- Cargo.lock | 22 ++---------- audio/Cargo.toml | 7 ++-- audio/src/fetch.rs | 78 ++++++++++++++++++------------------------ audio/src/lib.rs | 2 -- audio/src/range_set.rs | 2 +- 5 files changed, 39 insertions(+), 72 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 55bc1f7..a8577d1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -180,21 +180,6 @@ dependencies = [ "shlex", ] -[[package]] -name = "bit-set" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de" -dependencies = [ - "bit-vec", -] - -[[package]] -name = "bit-vec" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" - [[package]] name = "bitflags" version = "1.2.1" @@ -1399,20 +1384,17 @@ name = "librespot-audio" version = "0.1.6" dependencies = [ "aes-ctr", - "bit-set", "byteorder", "bytes", "cfg-if 1.0.0", - "futures", + "futures-util", "lewton", "librespot-core", "librespot-tremor", "log", - "num-bigint", - "num-traits", "ogg", - "pin-project-lite", "tempfile", + "tokio", "vorbis", ] diff --git a/audio/Cargo.toml b/audio/Cargo.toml index 01d81f0..d8c0eea 100644 --- a/audio/Cargo.toml +++ b/audio/Cargo.toml @@ -12,17 +12,14 @@ version = "0.1.6" [dependencies] aes-ctr = "0.6" -bit-set = "0.5" byteorder = "1.4" bytes = "1.0" cfg-if = "1" -futures = "0.3" log = "0.4" -num-bigint = "0.3" -num-traits = "0.2" +futures-util = { version = "0.3", default_features = false } ogg = "0.8" -pin-project-lite = "0.2.4" tempfile = "3.1" +tokio = { version = "1", features = ["sync"] } lewton = { version = "0.10", optional = true } librespot-tremor = { version = "0.2.0", optional = true } diff --git a/audio/src/fetch.rs b/audio/src/fetch.rs index 286a2b8..0ec9b01 100644 --- a/audio/src/fetch.rs +++ b/audio/src/fetch.rs @@ -1,30 +1,23 @@ -use crate::range_set::{Range, RangeSet}; +use std::cmp::{max, min}; +use std::fs; +use std::future::Future; +use std::io::{self, Read, Seek, SeekFrom, Write}; +use std::pin::Pin; +use std::sync::atomic::{self, AtomicUsize}; +use std::sync::{Arc, Condvar, Mutex}; +use std::task::{Context, Poll}; +use std::time::{Duration, Instant}; + use byteorder::{BigEndian, ByteOrder, WriteBytesExt}; use bytes::Bytes; -use futures::{ - channel::{mpsc, oneshot}, - future, -}; -use futures::{Future, Stream, StreamExt, TryFutureExt, TryStreamExt}; - -use std::fs; -use std::io::{self, Read, Seek, SeekFrom, Write}; -use std::sync::{Arc, Condvar, Mutex}; -use std::task::Poll; -use std::time::{Duration, Instant}; -use std::{ - cmp::{max, min}, - pin::Pin, - task::Context, -}; -use tempfile::NamedTempFile; - -use futures::channel::mpsc::unbounded; +use futures_util::{future, StreamExt, TryFutureExt, TryStreamExt}; use librespot_core::channel::{Channel, ChannelData, ChannelError, ChannelHeaders}; use librespot_core::session::Session; use librespot_core::spotify_id::FileId; -use std::sync::atomic; -use std::sync::atomic::AtomicUsize; +use tempfile::NamedTempFile; +use tokio::sync::{mpsc, oneshot}; + +use crate::range_set::{Range, RangeSet}; const MINIMUM_DOWNLOAD_SIZE: usize = 1024 * 16; // The minimum size of a block that is requested from the Spotify servers in one request. @@ -96,6 +89,7 @@ pub enum AudioFile { Streaming(AudioFileStreaming), } +#[derive(Debug)] enum StreamLoaderCommand { Fetch(Range), // signal the stream loader to fetch a range of the file RandomAccessMode(), // optimise download strategy for random access @@ -147,7 +141,7 @@ impl StreamLoaderController { fn send_stream_loader_command(&mut self, command: StreamLoaderCommand) { if let Some(ref mut channel) = self.channel_tx { // ignore the error in case the channel has been closed already. - let _ = channel.unbounded_send(command); + let _ = channel.send(command); } } @@ -191,7 +185,7 @@ impl StreamLoaderController { // We can't use self.fetch here because self can't be borrowed mutably, so we access the channel directly. if let Some(ref mut channel) = self.channel_tx { // ignore the error in case the channel has been closed already. - let _ = channel.unbounded_send(StreamLoaderCommand::Fetch(range)); + let _ = channel.send(StreamLoaderCommand::Fetch(range)); } } } @@ -387,7 +381,7 @@ impl AudioFileStreaming { //let (seek_tx, seek_rx) = mpsc::unbounded(); let (stream_loader_command_tx, stream_loader_command_rx) = - mpsc::unbounded::(); + mpsc::unbounded_channel::(); let fetcher = AudioFileFetch::new( session.clone(), @@ -490,12 +484,12 @@ async fn audio_file_fetch_receive_data( duration_ms = duration.as_millis() as u64; } let _ = file_data_tx - .unbounded_send(ReceivedData::ResponseTimeMs(duration_ms as usize)); + .send(ReceivedData::ResponseTimeMs(duration_ms as usize)); measure_ping_time = false; } let data_size = data.len(); let _ = file_data_tx - .unbounded_send(ReceivedData::Data(PartialFileData { + .send(ReceivedData::Data(PartialFileData { offset: data_offset, data: data, })); @@ -696,21 +690,17 @@ async fn audio_file_fetch( future::select_all(vec![f1, f2, f3]).await }*/ -pin_project! { - struct AudioFileFetch { - session: Session, - shared: Arc, - output: Option, +struct AudioFileFetch { + session: Session, + shared: Arc, + output: Option, - file_data_tx: mpsc::UnboundedSender, - #[pin] - file_data_rx: mpsc::UnboundedReceiver, + file_data_tx: mpsc::UnboundedSender, + file_data_rx: mpsc::UnboundedReceiver, - #[pin] - stream_loader_command_rx: mpsc::UnboundedReceiver, - complete_tx: Option>, - network_response_times_ms: Vec, - } + stream_loader_command_rx: mpsc::UnboundedReceiver, + complete_tx: Option>, + network_response_times_ms: Vec, } impl AudioFileFetch { @@ -725,7 +715,7 @@ impl AudioFileFetch { stream_loader_command_rx: mpsc::UnboundedReceiver, complete_tx: oneshot::Sender, ) -> AudioFileFetch { - let (file_data_tx, file_data_rx) = unbounded::(); + let (file_data_tx, file_data_rx) = mpsc::unbounded_channel::(); { let requested_range = Range::new(0, initial_data_length); @@ -863,7 +853,7 @@ impl AudioFileFetch { fn poll_file_data_rx(&mut self, cx: &mut Context<'_>) -> Poll<()> { loop { - match Pin::new(&mut self.file_data_rx).poll_next(cx) { + match self.file_data_rx.poll_recv(cx) { Poll::Ready(None) => return Poll::Ready(()), Poll::Ready(Some(ReceivedData::ResponseTimeMs(response_time_ms))) => { trace!("Ping time estimated as: {} ms.", response_time_ms); @@ -939,7 +929,7 @@ impl AudioFileFetch { fn poll_stream_loader_command_rx(&mut self, cx: &mut Context<'_>) -> Poll<()> { loop { - match Pin::new(&mut self.stream_loader_command_rx).poll_next(cx) { + match self.stream_loader_command_rx.poll_recv(cx) { Poll::Ready(None) => return Poll::Ready(()), Poll::Ready(Some(cmd)) => match cmd { StreamLoaderCommand::Fetch(request) => { @@ -1059,7 +1049,7 @@ impl Read for AudioFileStreaming { for &range in ranges_to_request.iter() { self.stream_loader_command_tx - .unbounded_send(StreamLoaderCommand::Fetch(range)) + .send(StreamLoaderCommand::Fetch(range)) .unwrap(); } diff --git a/audio/src/lib.rs b/audio/src/lib.rs index 9bb6f8e..099fb4a 100644 --- a/audio/src/lib.rs +++ b/audio/src/lib.rs @@ -2,8 +2,6 @@ #[macro_use] extern crate log; -#[macro_use] -extern crate pin_project_lite; mod decrypt; mod fetch; diff --git a/audio/src/range_set.rs b/audio/src/range_set.rs index d01d888..31ce650 100644 --- a/audio/src/range_set.rs +++ b/audio/src/range_set.rs @@ -2,7 +2,7 @@ use std::cmp::{max, min}; use std::fmt; use std::slice::Iter; -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug)] pub struct Range { pub start: usize, pub length: usize, From 5aeb733ad973537e7855a144abf0c64bad54c7e5 Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Mon, 22 Feb 2021 09:55:40 +0100 Subject: [PATCH 044/103] Clean up dependencies in librespot-playback * Use futures-util instead of futures * Use tokio channels instead of futures channels * Removed "extern crate"s --- Cargo.lock | 4 +++- playback/Cargo.toml | 4 +++- playback/src/lib.rs | 36 +++------------------------- playback/src/player.rs | 53 +++++++++++++++++++++--------------------- 4 files changed, 36 insertions(+), 61 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a8577d1..1d68711 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1481,7 +1481,8 @@ dependencies = [ "alsa", "byteorder", "cpal", - "futures", + "futures-executor", + "futures-util", "glib", "gstreamer", "gstreamer-app", @@ -1498,6 +1499,7 @@ dependencies = [ "sdl2", "shell-words", "thiserror", + "tokio", "zerocopy", ] diff --git a/playback/Cargo.toml b/playback/Cargo.toml index 2759ae0..e32ee7b 100644 --- a/playback/Cargo.toml +++ b/playback/Cargo.toml @@ -18,10 +18,12 @@ path = "../metadata" version = "0.1.6" [dependencies] -futures = "0.3" +futures-executor = { version = "0.3", default_features = false } +futures-util = { version = "0.3", default_features = false } log = "0.4" byteorder = "1.4" shell-words = "1.0.0" +tokio = { version = "1", features = ["sync"] } alsa = { version = "0.4", optional = true } portaudio-rs = { version = "0.3", optional = true } diff --git a/playback/src/lib.rs b/playback/src/lib.rs index f460643..9613f2e 100644 --- a/playback/src/lib.rs +++ b/playback/src/lib.rs @@ -1,39 +1,9 @@ #[macro_use] extern crate log; -extern crate byteorder; -extern crate futures; -extern crate shell_words; - -#[cfg(feature = "alsa-backend")] -extern crate alsa; - -#[cfg(feature = "portaudio-backend")] -extern crate portaudio_rs; - -#[cfg(feature = "pulseaudio-backend")] -extern crate libpulse_binding; -#[cfg(feature = "pulseaudio-backend")] -extern crate libpulse_simple_binding; - -#[cfg(feature = "jackaudio-backend")] -extern crate jack; - -#[cfg(feature = "gstreamer-backend")] -extern crate glib; -#[cfg(feature = "gstreamer-backend")] -extern crate gstreamer as gst; -#[cfg(feature = "gstreamer-backend")] -extern crate gstreamer_app as gst_app; -#[cfg(feature = "gstreamer-backend")] -extern crate zerocopy; - -#[cfg(feature = "sdl-backend")] -extern crate sdl2; - -extern crate librespot_audio as audio; -extern crate librespot_core; -extern crate librespot_metadata as metadata; +use librespot_audio as audio; +use librespot_core as core; +use librespot_metadata as metadata; pub mod audio_backend; pub mod config; diff --git a/playback/src/player.rs b/playback/src/player.rs index 861f91b..bc4a1dd 100644 --- a/playback/src/player.rs +++ b/playback/src/player.rs @@ -1,3 +1,16 @@ +use std::borrow::Cow; +use std::cmp::max; +use std::future::Future; +use std::io::{self, Read, Seek, SeekFrom}; +use std::pin::Pin; +use std::task::{Context, Poll}; +use std::time::{Duration, Instant}; +use std::{mem, thread}; + +use byteorder::{LittleEndian, ReadBytesExt}; +use futures_util::{future, TryFutureExt}; +use tokio::sync::{mpsc, oneshot}; + use crate::audio::{AudioDecoder, AudioError, AudioPacket, PassthroughDecoder, VorbisDecoder}; use crate::audio::{AudioDecrypt, AudioFile, StreamLoaderController}; use crate::audio::{ @@ -7,23 +20,11 @@ use crate::audio::{ use crate::audio_backend::Sink; use crate::config::NormalisationType; use crate::config::{Bitrate, PlayerConfig}; +use crate::core::session::Session; +use crate::core::spotify_id::SpotifyId; +use crate::core::util::SeqGenerator; use crate::metadata::{AudioItem, FileFormat}; use crate::mixer::AudioFilter; -use librespot_core::session::Session; -use librespot_core::spotify_id::SpotifyId; -use librespot_core::util::SeqGenerator; - -use byteorder::{LittleEndian, ReadBytesExt}; -use futures::channel::{mpsc, oneshot}; -use futures::{future, Future, Stream, StreamExt, TryFutureExt}; -use std::borrow::Cow; - -use std::cmp::max; -use std::io::{self, Read, Seek, SeekFrom}; -use std::pin::Pin; -use std::task::{Context, Poll}; -use std::time::{Duration, Instant}; -use std::{mem, thread}; const PRELOAD_NEXT_TRACK_BEFORE_END_DURATION_MS: u32 = 30000; @@ -244,8 +245,8 @@ impl Player { where F: FnOnce() -> Box + Send + 'static, { - let (cmd_tx, cmd_rx) = mpsc::unbounded(); - let (event_sender, event_receiver) = mpsc::unbounded(); + let (cmd_tx, cmd_rx) = mpsc::unbounded_channel(); + let (event_sender, event_receiver) = mpsc::unbounded_channel(); let handle = thread::spawn(move || { debug!("new Player[{}]", session.session_id()); @@ -265,8 +266,8 @@ impl Player { }; // While PlayerInternal is written as a future, it still contains blocking code. - // It must be run by using wait() in a dedicated thread. - futures::executor::block_on(internal); + // It must be run by using block_on() in a dedicated thread. + futures_executor::block_on(internal); debug!("PlayerInternal thread finished."); }); @@ -281,7 +282,7 @@ impl Player { } fn command(&self, cmd: PlayerCommand) { - self.commands.as_ref().unwrap().unbounded_send(cmd).unwrap(); + self.commands.as_ref().unwrap().send(cmd).unwrap(); } pub fn load(&mut self, track_id: SpotifyId, start_playing: bool, position_ms: u32) -> u64 { @@ -317,14 +318,14 @@ impl Player { } pub fn get_player_event_channel(&self) -> PlayerEventChannel { - let (event_sender, event_receiver) = mpsc::unbounded(); + let (event_sender, event_receiver) = mpsc::unbounded_channel(); self.command(PlayerCommand::AddEventSender(event_sender)); event_receiver } - pub async fn get_end_of_track_future(&self) { + pub async fn await_end_of_track(&self) { let mut channel = self.get_player_event_channel(); - while let Some(event) = channel.next().await { + while let Some(event) = channel.recv().await { if matches!( event, PlayerEvent::EndOfTrack { .. } | PlayerEvent::Stopped { .. } @@ -797,7 +798,7 @@ impl Future for PlayerInternal { let mut all_futures_completed_or_not_ready = true; // process commands that were sent to us - let cmd = match Pin::new(&mut self.commands).poll_next(cx) { + let cmd = match self.commands.poll_recv(cx) { Poll::Ready(None) => return Poll::Ready(()), // client has disconnected - shut down. Poll::Ready(Some(cmd)) => { all_futures_completed_or_not_ready = false; @@ -1580,7 +1581,7 @@ impl PlayerInternal { fn send_event(&mut self, event: PlayerEvent) { let mut index = 0; while index < self.event_senders.len() { - match self.event_senders[index].unbounded_send(event.clone()) { + match self.event_senders[index].send(event.clone()) { Ok(_) => index += 1, Err(_) => { self.event_senders.remove(index); @@ -1608,7 +1609,7 @@ impl PlayerInternal { let (result_tx, result_rx) = oneshot::channel(); std::thread::spawn(move || { - futures::executor::block_on(loader.load_track(spotify_id, position_ms)).and_then( + futures_executor::block_on(loader.load_track(spotify_id, position_ms)).and_then( move |data| { let _ = result_tx.send(data); Some(()) From 45f42acb8299119df0ca094524081dbfb80e4303 Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Mon, 22 Feb 2021 09:58:08 +0100 Subject: [PATCH 045/103] Refactor 'find_available_alternatives' --- playback/Cargo.toml | 2 +- playback/src/player.rs | 29 ++++++++++++++--------------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/playback/Cargo.toml b/playback/Cargo.toml index e32ee7b..96a0d26 100644 --- a/playback/Cargo.toml +++ b/playback/Cargo.toml @@ -19,7 +19,7 @@ version = "0.1.6" [dependencies] futures-executor = { version = "0.3", default_features = false } -futures-util = { version = "0.3", default_features = false } +futures-util = { version = "0.3", default_features = false, features = ["alloc"] } log = "0.4" byteorder = "1.4" shell-words = "1.0.0" diff --git a/playback/src/player.rs b/playback/src/player.rs index bc4a1dd..9b2c712 100644 --- a/playback/src/player.rs +++ b/playback/src/player.rs @@ -1,4 +1,3 @@ -use std::borrow::Cow; use std::cmp::max; use std::future::Future; use std::io::{self, Read, Seek, SeekFrom}; @@ -8,7 +7,8 @@ use std::time::{Duration, Instant}; use std::{mem, thread}; use byteorder::{LittleEndian, ReadBytesExt}; -use futures_util::{future, TryFutureExt}; +use futures_util::stream::futures_unordered::FuturesUnordered; +use futures_util::{future, StreamExt, TryFutureExt}; use tokio::sync::{mpsc, oneshot}; use crate::audio::{AudioDecoder, AudioError, AudioPacket, PassthroughDecoder, VorbisDecoder}; @@ -576,21 +576,20 @@ struct PlayerTrackLoader { } impl PlayerTrackLoader { - async fn find_available_alternative<'a, 'b>( - &'a self, - audio: &'b AudioItem, - ) -> Option> { + async fn find_available_alternative(&self, audio: AudioItem) -> Option { if audio.available { - Some(Cow::Borrowed(audio)) + Some(audio) } else if let Some(alternatives) = &audio.alternatives { - let alternatives = alternatives + let alternatives: FuturesUnordered<_> = alternatives .iter() - .map(|alt_id| AudioItem::get_audio_item(&self.session, *alt_id)); - let alternatives = future::try_join_all(alternatives).await.unwrap(); + .map(|alt_id| AudioItem::get_audio_item(&self.session, *alt_id)) + .collect(); + alternatives - .into_iter() - .find(|alt| alt.available) - .map(Cow::Owned) + .filter_map(|x| future::ready(x.ok())) + .filter(|x| future::ready(x.available)) + .next() + .await } else { None } @@ -630,10 +629,10 @@ impl PlayerTrackLoader { info!("Loading <{}> with Spotify URI <{}>", audio.name, audio.uri); - let audio = match self.find_available_alternative(&audio).await { + let audio = match self.find_available_alternative(audio).await { Some(audio) => audio, None => { - warn!("<{}> is not available", audio.uri); + warn!("<{}> is not available", spotify_id.to_uri()); return None; } }; From 27f308b82f46ce5c72eb4b3c66181046978479de Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Sat, 13 Feb 2021 11:53:23 +0100 Subject: [PATCH 046/103] Replace error_chain by thiserror --- Cargo.lock | 2 +- core/Cargo.toml | 2 +- core/src/authentication.rs | 36 ---------------------- core/src/connection/mod.rs | 63 ++++++++++++++++++++++++++++++-------- core/src/lib.rs | 2 -- core/src/session.rs | 13 ++++++-- 6 files changed, 63 insertions(+), 55 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1d68711..d8f80b4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1435,7 +1435,6 @@ dependencies = [ "bytes", "cfg-if 1.0.0", "env_logger", - "error-chain", "futures-core", "futures-sink", "futures-util", @@ -1455,6 +1454,7 @@ dependencies = [ "serde_json", "sha-1 0.9.4", "shannon", + "thiserror", "tokio", "tokio-stream", "tokio-util", diff --git a/core/Cargo.toml b/core/Cargo.toml index 85f3be6..0ab4e39 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -18,7 +18,6 @@ base64 = "0.13" byteorder = "1.4" bytes = "1.0" cfg-if = "1" -error-chain = { version = "0.12", default-features = false } futures-core = { version = "0.3", default-features = false } futures-sink = { version = "0.3", default-features = false } futures-util = { version = "0.3", default-features = false, features = ["alloc", "bilock", "unstable", "sink"] } @@ -37,6 +36,7 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" sha-1 = "0.9" shannon = "0.2.0" +thiserror = "1" tokio = { version = "1.0", features = ["io-util", "net", "rt", "sync"] } tokio-stream = "0.1" tokio-util = { version = "0.6", features = ["codec"] } diff --git a/core/src/authentication.rs b/core/src/authentication.rs index ff477df..544dda4 100644 --- a/core/src/authentication.rs +++ b/core/src/authentication.rs @@ -1,5 +1,4 @@ use std::io::{self, Read}; -use std::ops::FnOnce; use aes::Aes192; use byteorder::{BigEndian, ByteOrder}; @@ -10,7 +9,6 @@ use serde::{Deserialize, Serialize}; use sha1::{Digest, Sha1}; use crate::protocol::authentication::AuthenticationType; -use crate::protocol::keyexchange::{APLoginFailed, ErrorCode}; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Credentials { @@ -144,37 +142,3 @@ where let v: String = serde::Deserialize::deserialize(de)?; base64::decode(&v).map_err(|e| serde::de::Error::custom(e.to_string())) } - -error_chain! { - types { - AuthenticationError, AuthenticationErrorKind, AuthenticationResultExt, AuthenticationResult; - } - - foreign_links { - Io(::std::io::Error); - } - - errors { - BadCredentials { - description("Bad credentials") - display("Authentication failed with error: Bad credentials") - } - PremiumAccountRequired { - description("Premium account required") - display("Authentication failed with error: Premium account required") - } - } -} - -impl From for AuthenticationError { - fn from(login_failure: APLoginFailed) -> Self { - let error_code = login_failure.get_error_code(); - match error_code { - ErrorCode::BadCredentials => Self::from_kind(AuthenticationErrorKind::BadCredentials), - ErrorCode::PremiumAccountRequired => { - Self::from_kind(AuthenticationErrorKind::PremiumAccountRequired) - } - _ => format!("Authentication failed with error: {:?}", error_code).into(), - } - } -} diff --git a/core/src/connection/mod.rs b/core/src/connection/mod.rs index 6bdbde6..a07f9a2 100644 --- a/core/src/connection/mod.rs +++ b/core/src/connection/mod.rs @@ -4,21 +4,60 @@ mod handshake; pub use self::codec::APCodec; pub use self::handshake::handshake; -use futures_util::{SinkExt, StreamExt}; -use protobuf::{self, Message}; -use std::io; +use std::io::{self, ErrorKind}; use std::net::ToSocketAddrs; + +use futures_util::{SinkExt, StreamExt}; +use protobuf::{self, Message, ProtobufError}; +use thiserror::Error; use tokio::net::TcpStream; use tokio_util::codec::Framed; use url::Url; -use crate::authentication::{AuthenticationError, Credentials}; +use crate::authentication::Credentials; +use crate::protocol::keyexchange::{APLoginFailed, ErrorCode}; +use crate::proxytunnel; use crate::version; -use crate::proxytunnel; - pub type Transport = Framed; +fn login_error_message(code: &ErrorCode) -> &'static str { + pub use ErrorCode::*; + match code { + ProtocolError => "Protocol error", + TryAnotherAP => "Try another AP", + BadConnectionId => "Bad connection id", + TravelRestriction => "Travel restriction", + PremiumAccountRequired => "Premium account required", + BadCredentials => "Bad credentials", + CouldNotValidateCredentials => "Could not validate credentials", + AccountExists => "Account exists", + ExtraVerificationRequired => "Extra verification required", + InvalidAppKey => "Invalid app key", + ApplicationBanned => "Application banned", + } +} + +#[derive(Debug, Error)] +pub enum AuthenticationError { + #[error("Login failed with reason: {}", login_error_message(.0))] + LoginFailed(ErrorCode), + #[error("Authentication failed: {0}")] + IoError(#[from] io::Error), +} + +impl From for AuthenticationError { + fn from(e: ProtobufError) -> Self { + io::Error::new(ErrorKind::InvalidData, e).into() + } +} + +impl From for AuthenticationError { + fn from(login_failure: APLoginFailed) -> Self { + Self::LoginFailed(login_failure.get_error_code()) + } +} + pub async fn connect(addr: String, proxy: &Option) -> io::Result { let socket = if let Some(proxy) = proxy { info!("Using proxy \"{}\"", proxy); @@ -66,7 +105,6 @@ pub async fn authenticate( device_id: &str, ) -> Result { use crate::protocol::authentication::{APWelcome, ClientResponseEncrypted, CpuFamily, Os}; - use crate::protocol::keyexchange::APLoginFailed; let mut packet = ClientResponseEncrypted::new(); packet @@ -101,7 +139,7 @@ pub async fn authenticate( let (cmd, data) = transport.next().await.expect("EOF")?; match cmd { 0xac => { - let welcome_data: APWelcome = protobuf::parse_from_bytes(data.as_ref()).unwrap(); + let welcome_data: APWelcome = protobuf::parse_from_bytes(data.as_ref())?; let reusable_credentials = Credentials { username: welcome_data.get_canonical_username().to_owned(), @@ -111,12 +149,13 @@ pub async fn authenticate( Ok(reusable_credentials) } - 0xad => { - let error_data: APLoginFailed = protobuf::parse_from_bytes(data.as_ref()).unwrap(); + let error_data: APLoginFailed = protobuf::parse_from_bytes(data.as_ref())?; Err(error_data.into()) } - - _ => panic!("Unexpected packet {:?}", cmd), + _ => { + let msg = format!("Received invalid packet: {}", cmd); + Err(io::Error::new(ErrorKind::InvalidData, msg).into()) + } } } diff --git a/core/src/lib.rs b/core/src/lib.rs index 54f83f1..4ebe858 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -4,8 +4,6 @@ extern crate log; #[macro_use] extern crate cfg_if; -#[macro_use] -extern crate error_chain; use librespot_protocol as protocol; diff --git a/core/src/session.rs b/core/src/session.rs index 858a0b6..9eaff3e 100644 --- a/core/src/session.rs +++ b/core/src/session.rs @@ -12,6 +12,7 @@ use bytes::Bytes; use futures_core::TryStream; use futures_util::{FutureExt, StreamExt, TryStreamExt}; use once_cell::sync::OnceCell; +use thiserror::Error; use tokio::sync::mpsc; use tokio_stream::wrappers::UnboundedReceiverStream; @@ -21,10 +22,16 @@ use crate::authentication::Credentials; use crate::cache::Cache; use crate::channel::ChannelManager; use crate::config::SessionConfig; -use crate::connection; +use crate::connection::{self, AuthenticationError}; use crate::mercury::MercuryManager; -pub use crate::authentication::{AuthenticationError, AuthenticationErrorKind}; +#[derive(Debug, Error)] +pub enum SessionError { + #[error(transparent)] + AuthenticationError(#[from] AuthenticationError), + #[error("Cannot create session: {0}")] + IoError(#[from] io::Error), +} struct SessionData { country: String, @@ -59,7 +66,7 @@ impl Session { config: SessionConfig, credentials: Credentials, cache: Option, - ) -> Result { + ) -> Result { let ap = apresolve_or_fallback(&config.proxy, &config.ap_port).await; info!("Connecting to AP \"{}\"", ap); From f9c0e26f6df22fa0bbb1e37e9546c5ed778195ec Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Sat, 13 Feb 2021 15:38:05 +0100 Subject: [PATCH 047/103] Simplify code --- audio/src/fetch.rs | 77 ++++++++++++++++++++++------------------------ 1 file changed, 36 insertions(+), 41 deletions(-) diff --git a/audio/src/fetch.rs b/audio/src/fetch.rs index 0ec9b01..ccbb898 100644 --- a/audio/src/fetch.rs +++ b/audio/src/fetch.rs @@ -449,7 +449,7 @@ enum ReceivedData { async fn audio_file_fetch_receive_data( shared: Arc, file_data_tx: mpsc::UnboundedSender, - data_rx: ChannelData, + mut data_rx: ChannelData, initial_data_offset: usize, initial_request_length: usize, request_sent_time: Instant, @@ -465,49 +465,44 @@ async fn audio_file_fetch_receive_data( .number_of_open_requests .fetch_add(1, atomic::Ordering::SeqCst); - enum TryFoldErr { - ChannelError, - FinishEarly, - } + let result = loop { + let data = match data_rx.next().await { + Some(Ok(data)) => data, + Some(Err(e)) => break Err(e), + None => break Ok(()), + }; - let result = data_rx - .map_err(|_| TryFoldErr::ChannelError) - .try_for_each(|data| { - if measure_ping_time { - let duration = Instant::now() - request_sent_time; - let duration_ms: u64; - if 0.001 * (duration.as_millis() as f64) - > MAXIMUM_ASSUMED_PING_TIME_SECONDS - { - duration_ms = (MAXIMUM_ASSUMED_PING_TIME_SECONDS * 1000.0) as u64; - } else { - duration_ms = duration.as_millis() as u64; - } - let _ = file_data_tx - .send(ReceivedData::ResponseTimeMs(duration_ms as usize)); - measure_ping_time = false; - } - let data_size = data.len(); - let _ = file_data_tx - .send(ReceivedData::Data(PartialFileData { - offset: data_offset, - data: data, - })); - data_offset += data_size; - if request_length < data_size { - warn!("Data receiver for range {} (+{}) received more data from server than requested.", initial_data_offset, initial_request_length); - request_length = 0; + if measure_ping_time { + let duration = Instant::now() - request_sent_time; + let duration_ms: u64; + if 0.001 * (duration.as_millis() as f64) > MAXIMUM_ASSUMED_PING_TIME_SECONDS { + duration_ms = (MAXIMUM_ASSUMED_PING_TIME_SECONDS * 1000.0) as u64; } else { - request_length -= data_size; + duration_ms = duration.as_millis() as u64; } + let _ = file_data_tx.send(ReceivedData::ResponseTimeMs(duration_ms as usize)); + measure_ping_time = false; + } + let data_size = data.len(); + let _ = file_data_tx.send(ReceivedData::Data(PartialFileData { + offset: data_offset, + data: data, + })); + data_offset += data_size; + if request_length < data_size { + warn!( + "Data receiver for range {} (+{}) received more data from server than requested.", + initial_data_offset, initial_request_length + ); + request_length = 0; + } else { + request_length -= data_size; + } - future::ready(if request_length == 0 { - Err(TryFoldErr::FinishEarly) - } else { - Ok(()) - }) - }) - .await; + if request_length == 0 { + break Ok(()); + } + }; if request_length > 0 { let missing_range = Range::new(data_offset, request_length); @@ -521,7 +516,7 @@ async fn audio_file_fetch_receive_data( .number_of_open_requests .fetch_sub(1, atomic::Ordering::SeqCst); - if let Err(TryFoldErr::ChannelError) = result { + if result.is_err() { warn!( "Error from channel for data receiver for range {} (+{}).", initial_data_offset, initial_request_length From d064ffc670a28962eb73c50e04ad001c1a478e08 Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Sun, 21 Feb 2021 19:38:40 +0100 Subject: [PATCH 048/103] Use tokio channels and fix compilation errors --- Cargo.lock | 3 ++- Cargo.toml | 2 +- connect/Cargo.toml | 3 ++- connect/src/spirc.rs | 39 ++++++++++++++++++++++----------------- src/main.rs | 5 +++-- 5 files changed, 30 insertions(+), 22 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d8f80b4..16da85e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1416,12 +1416,13 @@ dependencies = [ "log", "num-bigint", "protobuf", - "rand 0.7.3", + "rand 0.8.3", "serde", "serde_derive", "serde_json", "sha-1 0.9.4", "tokio", + "tokio-stream", "url 1.7.2", ] diff --git a/Cargo.toml b/Cargo.toml index a26db2e..93866e2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,7 +55,7 @@ num-bigint = "0.3" protobuf = "~2.14.0" rand = "0.7" rpassword = "5.0" -tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros", "signal", "process"] } +tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros", "signal", "sync", "process"] } url = "1.7" sha-1 = "0.8" hex = "0.4" diff --git a/connect/Cargo.toml b/connect/Cargo.toml index 1ca6d7d..4997c5f 100644 --- a/connect/Cargo.toml +++ b/connect/Cargo.toml @@ -27,12 +27,13 @@ hyper = { version = "0.14", features = ["server", "http1"] } log = "0.4" num-bigint = "0.3" protobuf = "~2.14.0" -rand = "0.7" +rand = "0.8" serde = "1.0" serde_derive = "1.0" serde_json = "1.0" sha-1 = "0.9" tokio = { version = "1.0", features = ["macros"] } +tokio-stream = { version = "0.1" } url = "1.7" dns-sd = { version = "0.1.3", optional = true } diff --git a/connect/src/spirc.rs b/connect/src/spirc.rs index 5030759..dd495d8 100644 --- a/connect/src/spirc.rs +++ b/connect/src/spirc.rs @@ -6,7 +6,7 @@ use crate::playback::mixer::Mixer; use crate::playback::player::{Player, PlayerEvent, PlayerEventChannel}; use crate::protocol; use crate::protocol::spirc::{DeviceState, Frame, MessageType, PlayStatus, State, TrackRef}; -use futures::channel::mpsc; + use futures::future::{self, FusedFuture}; use futures::stream::FusedStream; use futures::{Future, FutureExt, StreamExt}; @@ -21,6 +21,8 @@ use protobuf::{self, Message}; use rand; use rand::seq::SliceRandom; use serde_json; +use tokio::sync::mpsc; +use tokio_stream::wrappers::UnboundedReceiverStream; enum SpircPlayStatus { Stopped, @@ -59,8 +61,8 @@ struct SpircTask { subscription: BoxedStream, sender: MercurySender, - commands: mpsc::UnboundedReceiver, - player_events: PlayerEventChannel, + commands: Option>, + player_events: Option, shutdown: bool, session: Session, @@ -263,6 +265,7 @@ impl Spirc { .mercury() .subscribe(uri.clone()) .map(Result::unwrap) + .map(UnboundedReceiverStream::new) .flatten_stream() .map(|response| -> Frame { let data = response.payload.first().unwrap(); @@ -272,7 +275,7 @@ impl Spirc { let sender = session.mercury().sender(uri); - let (cmd_tx, cmd_rx) = mpsc::unbounded(); + let (cmd_tx, cmd_rx) = mpsc::unbounded_channel(); let volume = config.volume; let task_config = SpircTaskConfig { @@ -301,8 +304,8 @@ impl Spirc { subscription: subscription, sender: sender, - commands: cmd_rx, - player_events: player_events, + commands: Some(cmd_rx), + player_events: Some(player_events), shutdown: false, session: session, @@ -322,34 +325,36 @@ impl Spirc { } pub fn play(&self) { - let _ = self.commands.unbounded_send(SpircCommand::Play); + let _ = self.commands.send(SpircCommand::Play); } pub fn play_pause(&self) { - let _ = self.commands.unbounded_send(SpircCommand::PlayPause); + let _ = self.commands.send(SpircCommand::PlayPause); } pub fn pause(&self) { - let _ = self.commands.unbounded_send(SpircCommand::Pause); + let _ = self.commands.send(SpircCommand::Pause); } pub fn prev(&self) { - let _ = self.commands.unbounded_send(SpircCommand::Prev); + let _ = self.commands.send(SpircCommand::Prev); } pub fn next(&self) { - let _ = self.commands.unbounded_send(SpircCommand::Next); + let _ = self.commands.send(SpircCommand::Next); } pub fn volume_up(&self) { - let _ = self.commands.unbounded_send(SpircCommand::VolumeUp); + let _ = self.commands.send(SpircCommand::VolumeUp); } pub fn volume_down(&self) { - let _ = self.commands.unbounded_send(SpircCommand::VolumeDown); + let _ = self.commands.send(SpircCommand::VolumeDown); } pub fn shutdown(&self) { - let _ = self.commands.unbounded_send(SpircCommand::Shutdown); + let _ = self.commands.send(SpircCommand::Shutdown); } } impl SpircTask { async fn run(mut self) { while !self.session.is_invalid() && !self.shutdown { + let commands = self.commands.as_mut(); + let player_events = self.player_events.as_mut(); tokio::select! { frame = self.subscription.next() => match frame { Some(frame) => self.handle_frame(frame), @@ -358,10 +363,10 @@ impl SpircTask { break; } }, - cmd = self.commands.next(), if !self.commands.is_terminated() => if let Some(cmd) = cmd { + cmd = async { commands.unwrap().recv().await }, if commands.is_some() => if let Some(cmd) = cmd { self.handle_command(cmd); }, - event = self.player_events.next(), if !self.player_events.is_terminated() => if let Some(event) = event { + event = async { player_events.unwrap().recv().await }, if player_events.is_some() => if let Some(event) = event { self.handle_player_event(event) }, result = self.sender.flush(), if !self.sender.is_flushed() => if result.is_err() { @@ -508,7 +513,7 @@ impl SpircTask { SpircCommand::Shutdown => { CommandSender::new(self, MessageType::kMessageTypeGoodbye).send(); self.shutdown = true; - self.commands.close(); + self.commands.as_mut().map(|rx| rx.close()); } } } diff --git a/src/main.rs b/src/main.rs index 31ef300..7882203 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -use futures::{channel::mpsc::UnboundedReceiver, future::FusedFuture, FutureExt, StreamExt}; +use futures::{future::FusedFuture, FutureExt, StreamExt}; use librespot_playback::player::PlayerEvent; use log::{error, info, warn}; use sha1::{Digest, Sha1}; @@ -10,6 +10,7 @@ use std::{ io::{stderr, Write}, pin::Pin, }; +use tokio::sync::mpsc::UnboundedReceiver; use url::Url; use librespot::core::authentication::Credentials; @@ -589,7 +590,7 @@ async fn main() { } } }, - event = async { player_event_channel.as_mut().unwrap().next().await }, if player_event_channel.is_some() => match event { + event = async { player_event_channel.as_mut().unwrap().recv().await }, if player_event_channel.is_some() => match event { Some(event) => { if let Some(program) = &setupp.player_event_program { if let Some(child) = run_program_on_events(event, program) { From 59c556635ebbd20897e8c739e3395da2decd5742 Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Sun, 21 Feb 2021 19:38:44 +0100 Subject: [PATCH 049/103] Clean up librespot-connect dependencies --- Cargo.lock | 4 ++-- connect/Cargo.toml | 33 ++++++++++++++++++--------------- connect/src/context.rs | 4 ++-- connect/src/discovery.rs | 12 ++++++------ connect/src/lib.rs | 31 +++---------------------------- connect/src/spirc.rs | 23 +++++++++++------------ 6 files changed, 42 insertions(+), 65 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 16da85e..1aaf970 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1406,7 +1406,8 @@ dependencies = [ "base64", "block-modes", "dns-sd", - "futures", + "futures-core", + "futures-util", "hmac", "hyper", "libmdns", @@ -1418,7 +1419,6 @@ dependencies = [ "protobuf", "rand 0.8.3", "serde", - "serde_derive", "serde_json", "sha-1 0.9.4", "tokio", diff --git a/connect/Cargo.toml b/connect/Cargo.toml index 4997c5f..f1d4a38 100644 --- a/connect/Cargo.toml +++ b/connect/Cargo.toml @@ -7,39 +7,42 @@ license = "MIT" repository = "https://github.com/librespot-org/librespot" edition = "2018" -[dependencies.librespot-core] -path = "../core" -version = "0.1.6" -[dependencies.librespot-playback] -path = "../playback" -version = "0.1.6" -[dependencies.librespot-protocol] -path = "../protocol" -version = "0.1.6" - [dependencies] aes-ctr = "0.6" base64 = "0.13" block-modes = "0.7" -futures = "0.3" +futures-core = "0.3" +futures-util = "0.3" hmac = "0.10" hyper = { version = "0.14", features = ["server", "http1"] } log = "0.4" num-bigint = "0.3" protobuf = "~2.14.0" rand = "0.8" -serde = "1.0" -serde_derive = "1.0" +serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" sha-1 = "0.9" -tokio = { version = "1.0", features = ["macros"] } +tokio = { version = "1.0", features = ["macros", "sync"] } tokio-stream = { version = "0.1" } url = "1.7" dns-sd = { version = "0.1.3", optional = true } libmdns = { version = "0.6", optional = true } +[dependencies.librespot-core] +path = "../core" +version = "0.1.6" + +[dependencies.librespot-playback] +path = "../playback" +version = "0.1.6" + +[dependencies.librespot-protocol] +path = "../protocol" +version = "0.1.6" [features] -default = ["libmdns"] +with-libmdns = ["libmdns"] with-dns-sd = ["dns-sd"] + +default = ["with-libmdns"] diff --git a/connect/src/context.rs b/connect/src/context.rs index 5a94f6c..63a2aeb 100644 --- a/connect/src/context.rs +++ b/connect/src/context.rs @@ -1,7 +1,7 @@ +use crate::core::spotify_id::SpotifyId; use crate::protocol::spirc::TrackRef; -use librespot_core::spotify_id::SpotifyId; -use serde; +use serde::Deserialize; #[derive(Deserialize, Debug)] pub struct StationContext { diff --git a/connect/src/discovery.rs b/connect/src/discovery.rs index 2951b38..1c94ecc 100644 --- a/connect/src/discovery.rs +++ b/connect/src/discovery.rs @@ -1,13 +1,13 @@ use aes_ctr::cipher::generic_array::GenericArray; use aes_ctr::cipher::{NewStreamCipher, SyncStreamCipher}; use aes_ctr::Aes128Ctr; -use base64; -use futures::channel::{mpsc, oneshot}; -use futures::{Stream, StreamExt}; +use futures_core::Stream; use hmac::{Hmac, Mac, NewMac}; use hyper::service::{make_service_fn, service_fn}; use hyper::{Body, Method, Request, Response, StatusCode}; +use serde_json::json; use sha1::{Digest, Sha1}; +use tokio::sync::{mpsc, oneshot}; use std::borrow::Cow; use std::convert::Infallible; @@ -50,7 +50,7 @@ impl Discovery { config: ConnectConfig, device_id: String, ) -> (Discovery, mpsc::UnboundedReceiver) { - let (tx, rx) = mpsc::unbounded(); + let (tx, rx) = mpsc::unbounded_channel(); let key_data = util::rand_vec(&mut rand::thread_rng(), 95); let private_key = BigUint::from_bytes_be(&key_data); @@ -155,7 +155,7 @@ impl Discovery { let credentials = Credentials::with_blob(username.to_string(), &decrypted, &self.0.device_id); - self.0.tx.unbounded_send(credentials).unwrap(); + self.0.tx.send(credentials).unwrap(); let result = json!({ "status": 101, @@ -273,6 +273,6 @@ impl Stream for DiscoveryStream { type Item = Credentials; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.credentials.poll_next_unpin(cx) + self.credentials.poll_recv(cx) } } diff --git a/connect/src/lib.rs b/connect/src/lib.rs index 4777760..600dd03 100644 --- a/connect/src/lib.rs +++ b/connect/src/lib.rs @@ -1,34 +1,9 @@ #[macro_use] extern crate log; -#[macro_use] -extern crate serde_json; -#[macro_use] -extern crate serde_derive; -extern crate serde; -extern crate base64; -extern crate futures; -extern crate hyper; -extern crate num_bigint; -extern crate protobuf; -extern crate rand; -extern crate tokio; -extern crate url; - -extern crate aes_ctr; -extern crate block_modes; -extern crate hmac; -extern crate sha1; - -#[cfg(feature = "with-dns-sd")] -extern crate dns_sd; - -#[cfg(not(feature = "with-dns-sd"))] -extern crate libmdns; - -extern crate librespot_core; -extern crate librespot_playback as playback; -extern crate librespot_protocol as protocol; +use librespot_core as core; +use librespot_playback as playback; +use librespot_protocol as protocol; pub mod context; pub mod discovery; diff --git a/connect/src/spirc.rs b/connect/src/spirc.rs index dd495d8..5afefe7 100644 --- a/connect/src/spirc.rs +++ b/connect/src/spirc.rs @@ -1,26 +1,25 @@ +use std::future::Future; use std::pin::Pin; use std::time::{SystemTime, UNIX_EPOCH}; use crate::context::StationContext; +use crate::core::config::{ConnectConfig, VolumeCtrl}; +use crate::core::mercury::{MercuryError, MercurySender}; +use crate::core::session::Session; +use crate::core::spotify_id::{SpotifyAudioType, SpotifyId, SpotifyIdError}; +use crate::core::util::url_encode; +use crate::core::util::SeqGenerator; +use crate::core::version; use crate::playback::mixer::Mixer; use crate::playback::player::{Player, PlayerEvent, PlayerEventChannel}; use crate::protocol; use crate::protocol::spirc::{DeviceState, Frame, MessageType, PlayStatus, State, TrackRef}; -use futures::future::{self, FusedFuture}; -use futures::stream::FusedStream; -use futures::{Future, FutureExt, StreamExt}; -use librespot_core::config::{ConnectConfig, VolumeCtrl}; -use librespot_core::mercury::{MercuryError, MercurySender}; -use librespot_core::session::Session; -use librespot_core::spotify_id::{SpotifyAudioType, SpotifyId, SpotifyIdError}; -use librespot_core::util::url_encode; -use librespot_core::util::SeqGenerator; -use librespot_core::version; +use futures_util::future::{self, FusedFuture}; +use futures_util::stream::FusedStream; +use futures_util::{FutureExt, StreamExt}; use protobuf::{self, Message}; -use rand; use rand::seq::SliceRandom; -use serde_json; use tokio::sync::mpsc; use tokio_stream::wrappers::UnboundedReceiverStream; From 18179e73eccd547b0a6bd1272922aa597ab7c9f8 Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Sun, 21 Feb 2021 19:38:49 +0100 Subject: [PATCH 050/103] Remove unused dependencies and fix feature flags --- Cargo.lock | 208 ++++++-------------------------------------- Cargo.toml | 9 +- connect/Cargo.toml | 6 +- core/Cargo.toml | 1 - playback/Cargo.toml | 2 +- src/main.rs | 5 +- 6 files changed, 35 insertions(+), 196 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1aaf970..426d4fd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -51,7 +51,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be14c7498ea50828a38d0e24a765ed2effe92a705885b57d029cd67d45744072" dependencies = [ "cipher", - "opaque-debug 0.3.0", + "opaque-debug", ] [[package]] @@ -61,7 +61,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea2e11f5e94c2f7d386164cc2aa1f97823fed6f259e486940a71c174dd01b0ce" dependencies = [ "cipher", - "opaque-debug 0.3.0", + "opaque-debug", ] [[package]] @@ -186,25 +186,13 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" -[[package]] -name = "block-buffer" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" -dependencies = [ - "block-padding 0.1.5", - "byte-tools", - "byteorder", - "generic-array 0.12.3", -] - [[package]] name = "block-buffer" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ - "generic-array 0.14.4", + "generic-array", ] [[package]] @@ -213,19 +201,10 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57a0e8073e8baa88212fb5823574c02ebccb395136ba9a164ab89379ec6072f0" dependencies = [ - "block-padding 0.2.1", + "block-padding", "cipher", ] -[[package]] -name = "block-padding" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" -dependencies = [ - "byte-tools", -] - [[package]] name = "block-padding" version = "0.2.1" @@ -238,12 +217,6 @@ version = "3.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63396b8a4b9de3f4fdfb320ab6080762242f66a8ef174c49d8e19b674db4cdbe" -[[package]] -name = "byte-tools" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" - [[package]] name = "byteorder" version = "1.4.2" @@ -314,7 +287,7 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801" dependencies = [ - "generic-array 0.14.4", + "generic-array", ] [[package]] @@ -456,7 +429,7 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4857fd85a0c34b3c3297875b747c1e02e06b6a0ea32dd892d8192b9ce0813ea6" dependencies = [ - "generic-array 0.14.4", + "generic-array", "subtle", ] @@ -515,22 +488,13 @@ dependencies = [ "syn", ] -[[package]] -name = "digest" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" -dependencies = [ - "generic-array 0.12.3", -] - [[package]] name = "digest" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" dependencies = [ - "generic-array 0.14.4", + "generic-array", ] [[package]] @@ -578,12 +542,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "fake-simd" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" - [[package]] name = "fetch_unroll" version = "0.2.2" @@ -623,21 +581,6 @@ dependencies = [ "percent-encoding 2.1.0", ] -[[package]] -name = "futures" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f55667319111d593ba876406af7c409c0ebb44dc4be6132a783ccf163ea14c1" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - [[package]] name = "futures-channel" version = "0.3.13" @@ -645,7 +588,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c2dd2df839b57db9ab69c2c9d8f3e8c81984781937fe2807dc6dcf3b2ad2939" dependencies = [ "futures-core", - "futures-sink", ] [[package]] @@ -665,12 +607,6 @@ dependencies = [ "futures-util", ] -[[package]] -name = "futures-io" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71c2c65c57704c32f5241c1223167c2c3294fd34ac020c807ddbe6db287ba59" - [[package]] name = "futures-macro" version = "0.3.13" @@ -701,13 +637,10 @@ version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1812c7ab8aedf8d6f2701a43e1243acdbcc2b36ab26e2ad421eb99ac963d96d1" dependencies = [ - "futures-channel", "futures-core", - "futures-io", "futures-macro", "futures-sink", "futures-task", - "memchr", "pin-project-lite", "pin-utils", "proc-macro-hack", @@ -721,15 +654,6 @@ version = "0.3.55" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" -[[package]] -name = "generic-array" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" -dependencies = [ - "typenum", -] - [[package]] name = "generic-array" version = "0.14.4" @@ -749,17 +673,6 @@ dependencies = [ "unicode-width", ] -[[package]] -name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if 1.0.0", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", -] - [[package]] name = "getrandom" version = "0.2.2" @@ -768,7 +681,7 @@ checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8" dependencies = [ "cfg-if 1.0.0", "libc", - "wasi 0.10.2+wasi-snapshot-preview1", + "wasi", ] [[package]] @@ -994,7 +907,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15" dependencies = [ "crypto-mac", - "digest 0.9.0", + "digest", ] [[package]] @@ -1300,7 +1213,7 @@ dependencies = [ "log", "multimap", "quick-error", - "rand 0.8.3", + "rand", "socket2", "tokio", ] @@ -1359,7 +1272,7 @@ version = "0.1.6" dependencies = [ "base64", "env_logger", - "futures", + "futures-util", "getopts", "hex", "hyper", @@ -1370,11 +1283,8 @@ dependencies = [ "librespot-playback", "librespot-protocol", "log", - "num-bigint", - "protobuf", - "rand 0.7.3", "rpassword", - "sha-1 0.8.2", + "sha-1", "tokio", "url 1.7.2", ] @@ -1417,10 +1327,10 @@ dependencies = [ "log", "num-bigint", "protobuf", - "rand 0.8.3", + "rand", "serde", "serde_json", - "sha-1 0.9.4", + "sha-1", "tokio", "tokio-stream", "url 1.7.2", @@ -1437,7 +1347,6 @@ dependencies = [ "cfg-if 1.0.0", "env_logger", "futures-core", - "futures-sink", "futures-util", "hmac", "httparse", @@ -1450,10 +1359,10 @@ dependencies = [ "once_cell", "pbkdf2", "protobuf", - "rand 0.8.3", + "rand", "serde", "serde_json", - "sha-1 0.9.4", + "sha-1", "shannon", "thiserror", "tokio", @@ -1835,12 +1744,6 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ad167a2f54e832b82dbe003a046280dceffe5227b5f79e08e363a29638cfddd" -[[package]] -name = "opaque-debug" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" - [[package]] name = "opaque-debug" version = "0.3.0" @@ -2102,19 +2005,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc 0.2.0", -] - [[package]] name = "rand" version = "0.8.3" @@ -2122,19 +2012,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e" dependencies = [ "libc", - "rand_chacha 0.3.0", - "rand_core 0.6.2", - "rand_hc 0.3.0", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", + "rand_chacha", + "rand_core", + "rand_hc", ] [[package]] @@ -2144,16 +2024,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" dependencies = [ "ppv-lite86", - "rand_core 0.6.2", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.16", + "rand_core", ] [[package]] @@ -2162,16 +2033,7 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7" dependencies = [ - "getrandom 0.2.2", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core 0.5.1", + "getrandom", ] [[package]] @@ -2180,7 +2042,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" dependencies = [ - "rand_core 0.6.2", + "rand_core", ] [[package]] @@ -2420,29 +2282,17 @@ dependencies = [ "serde", ] -[[package]] -name = "sha-1" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" -dependencies = [ - "block-buffer 0.7.3", - "digest 0.8.1", - "fake-simd", - "opaque-debug 0.2.3", -] - [[package]] name = "sha-1" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfebf75d25bd900fd1e7d11501efab59bc846dbc76196839663e6637bba9f25f" dependencies = [ - "block-buffer 0.9.0", + "block-buffer", "cfg-if 1.0.0", "cpuid-bool", - "digest 0.9.0", - "opaque-debug 0.3.0", + "digest", + "opaque-debug", ] [[package]] @@ -2661,7 +2511,7 @@ checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" dependencies = [ "cfg-if 1.0.0", "libc", - "rand 0.8.3", + "rand", "redox_syscall", "remove_dir_all", "winapi", @@ -3066,12 +2916,6 @@ dependencies = [ "try-lock", ] -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - [[package]] name = "wasi" version = "0.10.2+wasi-snapshot-preview1" diff --git a/Cargo.toml b/Cargo.toml index 93866e2..6e5ca03 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,18 +47,15 @@ version = "0.1.6" [dependencies] base64 = "0.13" env_logger = {version = "0.8", default-features = false, features = ["termcolor","humantime","atty"]} -futures = "0.3" +futures-util = { version = "0.3", default_features = false } getopts = "0.2" +hex = "0.4" hyper = "0.14" log = "0.4" -num-bigint = "0.3" -protobuf = "~2.14.0" -rand = "0.7" rpassword = "5.0" tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros", "signal", "sync", "process"] } url = "1.7" -sha-1 = "0.8" -hex = "0.4" +sha-1 = "0.9" [features] apresolve = ["librespot-core/apresolve"] diff --git a/connect/Cargo.toml b/connect/Cargo.toml index f1d4a38..5547646 100644 --- a/connect/Cargo.toml +++ b/connect/Cargo.toml @@ -12,9 +12,9 @@ aes-ctr = "0.6" base64 = "0.13" block-modes = "0.7" futures-core = "0.3" -futures-util = "0.3" +futures-util = { version = "0.3", default_features = false } hmac = "0.10" -hyper = { version = "0.14", features = ["server", "http1"] } +hyper = { version = "0.14", features = ["server", "http1", "tcp"] } log = "0.4" num-bigint = "0.3" protobuf = "~2.14.0" @@ -22,7 +22,7 @@ rand = "0.8" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" sha-1 = "0.9" -tokio = { version = "1.0", features = ["macros", "sync"] } +tokio = { version = "1.0", features = ["macros", "rt", "sync"] } tokio-stream = { version = "0.1" } url = "1.7" diff --git a/core/Cargo.toml b/core/Cargo.toml index 0ab4e39..5c5fa60 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -19,7 +19,6 @@ byteorder = "1.4" bytes = "1.0" cfg-if = "1" futures-core = { version = "0.3", default-features = false } -futures-sink = { version = "0.3", default-features = false } futures-util = { version = "0.3", default-features = false, features = ["alloc", "bilock", "unstable", "sink"] } hmac = "0.10" httparse = "1.3" diff --git a/playback/Cargo.toml b/playback/Cargo.toml index 96a0d26..03675de 100644 --- a/playback/Cargo.toml +++ b/playback/Cargo.toml @@ -18,7 +18,7 @@ path = "../metadata" version = "0.1.6" [dependencies] -futures-executor = { version = "0.3", default_features = false } +futures-executor = "0.3" futures-util = { version = "0.3", default_features = false, features = ["alloc"] } log = "0.4" byteorder = "1.4" diff --git a/src/main.rs b/src/main.rs index 7882203..5de83de 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -use futures::{future::FusedFuture, FutureExt, StreamExt}; +use futures_util::{future, FutureExt, StreamExt}; use librespot_playback::player::PlayerEvent; use log::{error, info, warn}; use sha1::{Digest, Sha1}; @@ -468,8 +468,7 @@ async fn main() { let mut player_event_channel: Option> = None; let mut auto_connect_times: Vec = vec![]; let mut discovery = None; - let mut connecting: Pin>> = - Box::pin(futures::future::pending()); + let mut connecting: Pin>> = Box::pin(future::pending()); if setupp.enable_discovery { let config = setupp.connect_config.clone(); From b606d8c661fd6ad6f5cdcedfb04eadbd644b2d3a Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Sun, 21 Feb 2021 19:38:52 +0100 Subject: [PATCH 051/103] Replace "extern crate"s --- src/lib.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 7cdd317..7722e93 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,8 @@ #![crate_name = "librespot"] -pub extern crate librespot_audio as audio; -pub extern crate librespot_connect as connect; -pub extern crate librespot_core as core; -pub extern crate librespot_metadata as metadata; -pub extern crate librespot_playback as playback; -pub extern crate librespot_protocol as protocol; +pub use librespot_audio as audio; +pub use librespot_connect as connect; +pub use librespot_core as core; +pub use librespot_metadata as metadata; +pub use librespot_playback as playback; +pub use librespot_protocol as protocol; From f22b41956f55c481d270c41ca17656027296bfe1 Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Mon, 22 Feb 2021 11:26:12 +0100 Subject: [PATCH 052/103] Update url crate to 2.1 --- Cargo.lock | 54 +++++++++----------------------------- Cargo.toml | 2 +- connect/Cargo.toml | 2 +- core/Cargo.toml | 2 +- core/src/apresolve.rs | 2 +- core/src/connection/mod.rs | 4 +-- 6 files changed, 19 insertions(+), 47 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 426d4fd..65493a2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -336,7 +336,7 @@ version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "784ad0fbab4f3e9cef09f20e0aea6000ae08d2cb98ac4c0abc53df18803d702f" dependencies = [ - "percent-encoding 2.1.0", + "percent-encoding", "time 0.2.25", "version_check", ] @@ -348,13 +348,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3818dfca4b0cb5211a659bbcbb94225b7127407b2b135e650d717bfb78ab10d3" dependencies = [ "cookie", - "idna 0.2.2", + "idna", "log", "publicsuffix", "serde", "serde_json", "time 0.2.25", - "url 2.2.1", + "url", ] [[package]] @@ -578,7 +578,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" dependencies = [ "matches", - "percent-encoding 2.1.0", + "percent-encoding", ] [[package]] @@ -990,17 +990,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" -[[package]] -name = "idna" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" -dependencies = [ - "matches", - "unicode-bidi", - "unicode-normalization", -] - [[package]] name = "idna" version = "0.2.2" @@ -1286,7 +1275,7 @@ dependencies = [ "rpassword", "sha-1", "tokio", - "url 1.7.2", + "url", ] [[package]] @@ -1333,7 +1322,7 @@ dependencies = [ "sha-1", "tokio", "tokio-stream", - "url 1.7.2", + "url", ] [[package]] @@ -1368,7 +1357,7 @@ dependencies = [ "tokio", "tokio-stream", "tokio-util", - "url 1.7.2", + "url", "vergen", ] @@ -1797,12 +1786,6 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" -[[package]] -name = "percent-encoding" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" - [[package]] name = "percent-encoding" version = "2.1.0" @@ -1975,10 +1958,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3bbaa49075179162b49acac1c6aa45fb4dafb5f13cf6794276d77bc7fd95757b" dependencies = [ "error-chain", - "idna 0.2.2", + "idna", "lazy_static", "regex", - "url 2.2.1", + "url", ] [[package]] @@ -1987,7 +1970,7 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d464fae65fff2680baf48019211ce37aaec0c78e9264c84a3e484717f965104e" dependencies = [ - "percent-encoding 2.1.0", + "percent-encoding", ] [[package]] @@ -2801,22 +2784,11 @@ dependencies = [ "once_cell", "qstring", "rustls", - "url 2.2.1", + "url", "webpki", "webpki-roots", ] -[[package]] -name = "url" -version = "1.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a" -dependencies = [ - "idna 0.1.5", - "matches", - "percent-encoding 1.0.1", -] - [[package]] name = "url" version = "2.2.1" @@ -2824,9 +2796,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ccd964113622c8e9322cfac19eb1004a07e636c545f325da085d5cdde6f1f8b" dependencies = [ "form_urlencoded", - "idna 0.2.2", + "idna", "matches", - "percent-encoding 2.1.0", + "percent-encoding", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 6e5ca03..6b8cbee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,7 +54,7 @@ hyper = "0.14" log = "0.4" rpassword = "5.0" tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros", "signal", "sync", "process"] } -url = "1.7" +url = "2.1" sha-1 = "0.9" [features] diff --git a/connect/Cargo.toml b/connect/Cargo.toml index 5547646..b03de88 100644 --- a/connect/Cargo.toml +++ b/connect/Cargo.toml @@ -24,7 +24,7 @@ serde_json = "1.0" sha-1 = "0.9" tokio = { version = "1.0", features = ["macros", "rt", "sync"] } tokio-stream = { version = "0.1" } -url = "1.7" +url = "2.1" dns-sd = { version = "0.1.3", optional = true } libmdns = { version = "0.6", optional = true } diff --git a/core/Cargo.toml b/core/Cargo.toml index 5c5fa60..373e308 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -39,7 +39,7 @@ thiserror = "1" tokio = { version = "1.0", features = ["io-util", "net", "rt", "sync"] } tokio-stream = "0.1" tokio-util = { version = "0.6", features = ["codec"] } -url = "1.7" +url = "2.1" [build-dependencies] rand = "0.8" diff --git a/core/src/apresolve.rs b/core/src/apresolve.rs index 7698691..2086d3b 100644 --- a/core/src/apresolve.rs +++ b/core/src/apresolve.rs @@ -32,7 +32,7 @@ cfg_if! { let response = if let Some(url) = proxy { Client::builder() - .build(ProxyTunnel::new(url)?) + .build(ProxyTunnel::new(&url.socket_addrs(|| None)?[..])?) .request(req) .await? } else { diff --git a/core/src/connection/mod.rs b/core/src/connection/mod.rs index a07f9a2..b715d35 100644 --- a/core/src/connection/mod.rs +++ b/core/src/connection/mod.rs @@ -76,8 +76,8 @@ pub async fn connect(addr: String, proxy: &Option) -> io::Result .next() .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "Missing port"))?; - let socket_addr = proxy.to_socket_addrs().and_then(|mut iter| { - iter.next().ok_or_else(|| { + let socket_addr = proxy.socket_addrs(|| None).and_then(|addrs| { + addrs.into_iter().next().ok_or_else(|| { io::Error::new( io::ErrorKind::NotFound, "Can't resolve proxy server address", From 8dc1e80633cccebed2a28224d8e43b19047d747e Mon Sep 17 00:00:00 2001 From: Philippe G Date: Sat, 27 Feb 2021 14:59:53 -0800 Subject: [PATCH 053/103] separated stream for each seek --- audio/src/passthrough_decoder.rs | 171 +++++++++++++++++-------------- 1 file changed, 92 insertions(+), 79 deletions(-) diff --git a/audio/src/passthrough_decoder.rs b/audio/src/passthrough_decoder.rs index 3a01101..6fab78e 100644 --- a/audio/src/passthrough_decoder.rs +++ b/audio/src/passthrough_decoder.rs @@ -5,75 +5,32 @@ use std::fmt; use std::io::{Read, Seek}; use std::time::{SystemTime, UNIX_EPOCH}; -fn write_headers( - rdr: &mut PacketReader, - wtr: &mut PacketWriter>, -) -> Result { - let mut stream_serial: u32 = SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_millis() as u32; - - // search for ident, comment, setup - get_header(1, rdr, wtr, &mut stream_serial, PacketWriteEndInfo::EndPage)?; - get_header( - 3, - rdr, - wtr, - &mut stream_serial, - PacketWriteEndInfo::NormalPacket, - )?; - get_header(5, rdr, wtr, &mut stream_serial, PacketWriteEndInfo::EndPage)?; - - // remove un-needed packets - rdr.delete_unread_packets(); - return Ok(stream_serial); -} - -fn get_header( - code: u8, - rdr: &mut PacketReader, - wtr: &mut PacketWriter>, - stream_serial: &mut u32, - info: PacketWriteEndInfo, -) -> Result +fn get_header(code: u8, rdr: &mut PacketReader) -> Result, PassthroughError> where T: Read + Seek, { let pck: Packet = rdr.read_packet_expected()?; - // set a unique serial number - if pck.stream_serial() != 0 { - *stream_serial = pck.stream_serial(); - } - let pkt_type = pck.data[0]; debug!("Vorbis header type{}", &pkt_type); - // all headers are mandatory if pkt_type != code { return Err(PassthroughError(OggReadError::InvalidData)); } - // headers keep original granule number - let absgp_page = pck.absgp_page(); - wtr.write_packet( - pck.data.into_boxed_slice(), - *stream_serial, - info, - absgp_page, - ) - .unwrap(); - - return Ok(*stream_serial); + return Ok(pck.data.into_boxed_slice()); } pub struct PassthroughDecoder { rdr: PacketReader, wtr: PacketWriter>, - lastgp_page: Option, - absgp_page: u64, + eos: bool, + bos: bool, + ofsgp_page: u64, stream_serial: u32, + ident: Box<[u8]>, + comment: Box<[u8]>, + setup: Box<[u8]>, } pub struct PassthroughError(ogg::OggReadError); @@ -82,17 +39,31 @@ impl PassthroughDecoder { /// Constructs a new Decoder from a given implementation of `Read + Seek`. pub fn new(rdr: R) -> Result { let mut rdr = PacketReader::new(rdr); - let mut wtr = PacketWriter::new(Vec::new()); + let stream_serial = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_millis() as u32; - let stream_serial = write_headers(&mut rdr, &mut wtr)?; info!("Starting passthrough track with serial {}", stream_serial); + // search for ident, comment, setup + let ident = get_header(1, &mut rdr)?; + let comment = get_header(3, &mut rdr)?; + let setup = get_header(5, &mut rdr)?; + + // remove un-needed packets + rdr.delete_unread_packets(); + return Ok(PassthroughDecoder { rdr, - wtr, - lastgp_page: Some(0), - absgp_page: 0, + wtr: PacketWriter::new(Vec::new()), + ofsgp_page: 0, stream_serial, + ident, + comment, + setup, + eos: false, + bos: false, }); } } @@ -100,52 +71,94 @@ impl PassthroughDecoder { impl AudioDecoder for PassthroughDecoder { fn seek(&mut self, ms: i64) -> Result<(), AudioError> { info!("Seeking to {}", ms); - self.lastgp_page = match ms { - 0 => Some(0), - _ => None, - }; + + // add an eos to previous stream if missing + if self.bos == true && self.eos == false { + match self.rdr.read_packet() { + Ok(Some(pck)) => { + let absgp_page = pck.absgp_page() - self.ofsgp_page; + self.wtr + .write_packet( + pck.data.into_boxed_slice(), + self.stream_serial, + PacketWriteEndInfo::EndStream, + absgp_page, + ) + .unwrap(); + } + _ => warn! {"Cannot write EoS after seeking"}, + }; + } + + self.eos = false; + self.bos = false; + self.ofsgp_page = 0; + self.stream_serial += 1; // hard-coded to 44.1 kHz match self.rdr.seek_absgp(None, (ms * 44100 / 1000) as u64) { - Ok(_) => return Ok(()), + Ok(_) => { + // need to set some offset for next_page() + let pck = self.rdr.read_packet().unwrap().unwrap(); + self.ofsgp_page = pck.absgp_page(); + debug!("Seek to offset page {}", self.ofsgp_page); + return Ok(()); + } Err(err) => return Err(AudioError::PassthroughError(err.into())), } } fn next_packet(&mut self) -> Result, AudioError> { - let mut skip = self.lastgp_page.is_none(); + // write headers if we are (re)starting + if self.bos == false { + self.wtr + .write_packet( + self.ident.clone(), + self.stream_serial, + PacketWriteEndInfo::EndPage, + 0, + ) + .unwrap(); + self.wtr + .write_packet( + self.comment.clone(), + self.stream_serial, + PacketWriteEndInfo::NormalPacket, + 0, + ) + .unwrap(); + self.wtr + .write_packet( + self.setup.clone(), + self.stream_serial, + PacketWriteEndInfo::EndPage, + 0, + ) + .unwrap(); + self.bos = true; + debug!("Wrote Ogg headers"); + } + loop { let pck = match self.rdr.read_packet() { Ok(Some(pck)) => pck, - Ok(None) | Err(OggReadError::NoCapturePatternFound) => { info!("end of streaming"); return Ok(None); } - Err(err) => return Err(AudioError::PassthroughError(err.into())), }; let pckgp_page = pck.absgp_page(); - let lastgp_page = self.lastgp_page.get_or_insert(pckgp_page); - // consume packets till next page to get a granule reference - if skip { - if *lastgp_page == pckgp_page { - debug!("skipping packet"); - continue; - } - skip = false; - info!("skipped at {}", pckgp_page); + // skip till we have audio and a calculable granule position + if pckgp_page == 0 || pckgp_page == self.ofsgp_page { + continue; } - // now we can calculate absolute granule - self.absgp_page += pckgp_page - *lastgp_page; - self.lastgp_page = Some(pckgp_page); - // set packet type let inf = if pck.last_in_stream() { - self.lastgp_page = Some(0); + self.eos = true; PacketWriteEndInfo::EndStream } else if pck.last_in_page() { PacketWriteEndInfo::EndPage @@ -158,7 +171,7 @@ impl AudioDecoder for PassthroughDecoder { pck.data.into_boxed_slice(), self.stream_serial, inf, - self.absgp_page, + pckgp_page - self.ofsgp_page, ) .unwrap(); From 6a33eb4efa72f2062af9153a4fb0a75ac40e2ab3 Mon Sep 17 00:00:00 2001 From: Evan Cameron Date: Sun, 28 Feb 2021 21:37:22 -0500 Subject: [PATCH 054/103] minor cleanup --- audio/src/fetch.rs | 59 +++++++++--------------- audio/src/lewton_decoder.rs | 10 ++-- audio/src/lib.rs | 6 +-- audio/src/passthrough_decoder.rs | 14 +++--- audio/src/range_set.rs | 23 ++++----- connect/src/discovery.rs | 23 ++++----- core/src/apresolve.rs | 4 +- core/src/authentication.rs | 8 ++-- core/src/channel.rs | 6 +-- core/src/diffie_hellman.rs | 4 +- core/src/mercury/mod.rs | 42 ++++++++--------- core/src/mercury/sender.rs | 4 +- core/src/session.rs | 9 +--- core/src/spotify_id.rs | 8 ++-- core/tests/connect.rs | 59 ++++++++++++------------ metadata/src/lib.rs | 2 +- playback/src/audio_backend/gstreamer.rs | 8 ++-- playback/src/audio_backend/jackaudio.rs | 2 +- playback/src/audio_backend/mod.rs | 2 +- playback/src/audio_backend/pulseaudio.rs | 4 +- playback/src/audio_backend/rodio.rs | 1 - playback/src/audio_backend/sdl.rs | 2 +- playback/src/audio_backend/subprocess.rs | 4 +- playback/src/mixer/alsamixer.rs | 15 +++--- playback/src/player.rs | 24 +++------- src/main.rs | 35 +++++++------- src/player_event_handler.rs | 6 +-- 27 files changed, 172 insertions(+), 212 deletions(-) diff --git a/audio/src/fetch.rs b/audio/src/fetch.rs index ccbb898..5fdf9e7 100644 --- a/audio/src/fetch.rs +++ b/audio/src/fetch.rs @@ -138,19 +138,19 @@ impl StreamLoaderController { }) } - fn send_stream_loader_command(&mut self, command: StreamLoaderCommand) { - if let Some(ref mut channel) = self.channel_tx { + fn send_stream_loader_command(&self, command: StreamLoaderCommand) { + if let Some(ref channel) = self.channel_tx { // ignore the error in case the channel has been closed already. let _ = channel.send(command); } } - pub fn fetch(&mut self, range: Range) { + pub fn fetch(&self, range: Range) { // signal the stream loader to fetch a range of the file self.send_stream_loader_command(StreamLoaderCommand::Fetch(range)); } - pub fn fetch_blocking(&mut self, mut range: Range) { + pub fn fetch_blocking(&self, mut range: Range) { // signal the stream loader to tech a range of the file and block until it is loaded. // ensure the range is within the file's bounds. @@ -182,47 +182,43 @@ impl StreamLoaderController { { // For some reason, the requested range is neither downloaded nor requested. // This could be due to a network error. Request it again. - // We can't use self.fetch here because self can't be borrowed mutably, so we access the channel directly. - if let Some(ref mut channel) = self.channel_tx { - // ignore the error in case the channel has been closed already. - let _ = channel.send(StreamLoaderCommand::Fetch(range)); - } + self.fetch(range); } } } } - pub fn fetch_next(&mut self, length: usize) { + pub fn fetch_next(&self, length: usize) { if let Some(ref shared) = self.stream_shared { let range = Range { start: shared.read_position.load(atomic::Ordering::Relaxed), - length: length, + length, }; self.fetch(range) } } - pub fn fetch_next_blocking(&mut self, length: usize) { + pub fn fetch_next_blocking(&self, length: usize) { if let Some(ref shared) = self.stream_shared { let range = Range { start: shared.read_position.load(atomic::Ordering::Relaxed), - length: length, + length, }; self.fetch_blocking(range); } } - pub fn set_random_access_mode(&mut self) { + pub fn set_random_access_mode(&self) { // optimise download strategy for random access self.send_stream_loader_command(StreamLoaderCommand::RandomAccessMode()); } - pub fn set_stream_mode(&mut self) { + pub fn set_stream_mode(&self) { // optimise download strategy for streaming self.send_stream_loader_command(StreamLoaderCommand::StreamMode()); } - pub fn close(&mut self) { + pub fn close(&self) { // terminate stream loading and don't load any more data for this file. self.send_stream_loader_command(StreamLoaderCommand::Close()); } @@ -230,11 +226,8 @@ impl StreamLoaderController { pub struct AudioFileStreaming { read_file: fs::File, - position: u64, - stream_loader_command_tx: mpsc::UnboundedSender, - shared: Arc, } @@ -332,10 +325,7 @@ impl AudioFile { } pub fn is_cached(&self) -> bool { - match self { - AudioFile::Cached { .. } => true, - _ => false, - } + matches!(self, AudioFile::Cached { .. }) } } @@ -359,7 +349,7 @@ impl AudioFileStreaming { let size = BigEndian::read_u32(&data) as usize * 4; let shared = Arc::new(AudioFileShared { - file_id: file_id, + file_id, file_size: size, stream_data_rate: streaming_data_rate, cond: Condvar::new(), @@ -396,11 +386,10 @@ impl AudioFileStreaming { session.spawn(fetcher); Ok(AudioFileStreaming { - read_file: read_file, + read_file, position: 0, - //seek: seek_tx, - stream_loader_command_tx: stream_loader_command_tx, - shared: shared, + stream_loader_command_tx, + shared, }) } } @@ -486,7 +475,7 @@ async fn audio_file_fetch_receive_data( let data_size = data.len(); let _ = file_data_tx.send(ReceivedData::Data(PartialFileData { offset: data_offset, - data: data, + data, })); data_offset += data_size; if request_length < data_size { @@ -728,14 +717,12 @@ impl AudioFileFetch { )); AudioFileFetch { - session: session, - shared: shared, + session, + shared, output: Some(output), - - file_data_tx: file_data_tx, - file_data_rx: file_data_rx, - - stream_loader_command_rx: stream_loader_command_rx, + file_data_tx, + file_data_rx, + stream_loader_command_rx, complete_tx: Some(complete_tx), network_response_times_ms: Vec::new(), } diff --git a/audio/src/lewton_decoder.rs b/audio/src/lewton_decoder.rs index 086ea57..698cc64 100644 --- a/audio/src/lewton_decoder.rs +++ b/audio/src/lewton_decoder.rs @@ -1,6 +1,7 @@ +use super::{AudioDecoder, AudioError, AudioPacket}; + use lewton::inside_ogg::OggStreamReader; -use super::{AudioDecoder, AudioError, AudioPacket}; use std::error; use std::fmt; use std::io::{Read, Seek}; @@ -24,16 +25,15 @@ where fn seek(&mut self, ms: i64) -> Result<(), AudioError> { let absgp = ms * 44100 / 1000; match self.0.seek_absgp_pg(absgp as u64) { - Ok(_) => return Ok(()), - Err(err) => return Err(AudioError::VorbisError(err.into())), + Ok(_) => Ok(()), + Err(err) => Err(AudioError::VorbisError(err.into())), } } fn next_packet(&mut self) -> Result, AudioError> { use lewton::audio::AudioReadError::AudioIsHeader; use lewton::OggReadError::NoCapturePatternFound; - use lewton::VorbisError::BadAudio; - use lewton::VorbisError::OggError; + use lewton::VorbisError::{BadAudio, OggError}; loop { match self.0.read_dec_packet_itl() { Ok(Some(packet)) => return Ok(Some(AudioPacket::Samples(packet))), diff --git a/audio/src/lib.rs b/audio/src/lib.rs index 099fb4a..80f1097 100644 --- a/audio/src/lib.rs +++ b/audio/src/lib.rs @@ -1,4 +1,4 @@ -#![allow(clippy::unused_io_amount)] +#![allow(clippy::unused_io_amount, clippy::too_many_arguments)] #[macro_use] extern crate log; @@ -85,13 +85,13 @@ impl fmt::Display for AudioError { impl From for AudioError { fn from(err: VorbisError) -> AudioError { - AudioError::VorbisError(VorbisError::from(err)) + AudioError::VorbisError(err) } } impl From for AudioError { fn from(err: PassthroughError) -> AudioError { - AudioError::PassthroughError(PassthroughError::from(err)) + AudioError::PassthroughError(err) } } diff --git a/audio/src/passthrough_decoder.rs b/audio/src/passthrough_decoder.rs index 3a01101..25802e4 100644 --- a/audio/src/passthrough_decoder.rs +++ b/audio/src/passthrough_decoder.rs @@ -27,7 +27,7 @@ fn write_headers( // remove un-needed packets rdr.delete_unread_packets(); - return Ok(stream_serial); + Ok(stream_serial) } fn get_header( @@ -65,7 +65,7 @@ where ) .unwrap(); - return Ok(*stream_serial); + Ok(*stream_serial) } pub struct PassthroughDecoder { @@ -87,13 +87,13 @@ impl PassthroughDecoder { let stream_serial = write_headers(&mut rdr, &mut wtr)?; info!("Starting passthrough track with serial {}", stream_serial); - return Ok(PassthroughDecoder { + Ok(PassthroughDecoder { rdr, wtr, lastgp_page: Some(0), absgp_page: 0, stream_serial, - }); + }) } } @@ -107,8 +107,8 @@ impl AudioDecoder for PassthroughDecoder { // hard-coded to 44.1 kHz match self.rdr.seek_absgp(None, (ms * 44100 / 1000) as u64) { - Ok(_) => return Ok(()), - Err(err) => return Err(AudioError::PassthroughError(err.into())), + Ok(_) => Ok(()), + Err(err) => Err(AudioError::PassthroughError(err.into())), } } @@ -164,7 +164,7 @@ impl AudioDecoder for PassthroughDecoder { let data = self.wtr.inner_mut(); - if data.len() > 0 { + if !data.is_empty() { let result = AudioPacket::OggData(std::mem::take(data)); return Ok(Some(result)); } diff --git a/audio/src/range_set.rs b/audio/src/range_set.rs index 31ce650..f74058a 100644 --- a/audio/src/range_set.rs +++ b/audio/src/range_set.rs @@ -16,14 +16,11 @@ impl fmt::Display for Range { impl Range { pub fn new(start: usize, length: usize) -> Range { - return Range { - start: start, - length: length, - }; + Range { start, length } } pub fn end(&self) -> usize { - return self.start + self.length; + self.start + self.length } } @@ -50,7 +47,7 @@ impl RangeSet { } pub fn is_empty(&self) -> bool { - return self.ranges.is_empty(); + self.ranges.is_empty() } pub fn len(&self) -> usize { @@ -58,11 +55,11 @@ impl RangeSet { } pub fn get_range(&self, index: usize) -> Range { - return self.ranges[index].clone(); + self.ranges[index] } pub fn iter(&self) -> Iter { - return self.ranges.iter(); + self.ranges.iter() } pub fn contains(&self, value: usize) -> bool { @@ -73,7 +70,7 @@ impl RangeSet { return true; } } - return false; + false } pub fn contained_length_from_value(&self, value: usize) -> usize { @@ -84,7 +81,7 @@ impl RangeSet { return range.end() - value; } } - return 0; + 0 } #[allow(dead_code)] @@ -144,7 +141,7 @@ impl RangeSet { pub fn union(&self, other: &RangeSet) -> RangeSet { let mut result = self.clone(); result.add_range_set(other); - return result; + result } pub fn subtract_range(&mut self, range: &Range) { @@ -204,7 +201,7 @@ impl RangeSet { pub fn minus(&self, other: &RangeSet) -> RangeSet { let mut result = self.clone(); result.subtract_range_set(other); - return result; + result } pub fn intersection(&self, other: &RangeSet) -> RangeSet { @@ -240,6 +237,6 @@ impl RangeSet { } } - return result; + result } } diff --git a/connect/src/discovery.rs b/connect/src/discovery.rs index 1c94ecc..df4d48e 100644 --- a/connect/src/discovery.rs +++ b/connect/src/discovery.rs @@ -5,34 +5,31 @@ use futures_core::Stream; use hmac::{Hmac, Mac, NewMac}; use hyper::service::{make_service_fn, service_fn}; use hyper::{Body, Method, Request, Response, StatusCode}; +use num_bigint::BigUint; use serde_json::json; use sha1::{Digest, Sha1}; use tokio::sync::{mpsc, oneshot}; -use std::borrow::Cow; -use std::convert::Infallible; -use std::net::{Ipv4Addr, SocketAddr}; -use std::task::{Context, Poll}; - #[cfg(feature = "with-dns-sd")] use dns_sd::DNSService; #[cfg(not(feature = "with-dns-sd"))] use libmdns; -use num_bigint::BigUint; -use rand; -use std::collections::BTreeMap; -use std::io; -use std::pin::Pin; -use std::sync::Arc; -use url; - use librespot_core::authentication::Credentials; use librespot_core::config::ConnectConfig; use librespot_core::diffie_hellman::{DH_GENERATOR, DH_PRIME}; use librespot_core::util; +use std::borrow::Cow; +use std::collections::BTreeMap; +use std::convert::Infallible; +use std::io; +use std::net::{Ipv4Addr, SocketAddr}; +use std::pin::Pin; +use std::sync::Arc; +use std::task::{Context, Poll}; + type HmacSha1 = Hmac; #[derive(Clone)] diff --git a/core/src/apresolve.rs b/core/src/apresolve.rs index 2086d3b..531a3e0 100644 --- a/core/src/apresolve.rs +++ b/core/src/apresolve.rs @@ -1,10 +1,10 @@ -const AP_FALLBACK: &'static str = "ap.spotify.com:443"; +const AP_FALLBACK: &str = "ap.spotify.com:443"; use url::Url; cfg_if! { if #[cfg(feature = "apresolve")] { - const APRESOLVE_ENDPOINT: &'static str = "http://apresolve.spotify.com:80"; + const APRESOLVE_ENDPOINT: &str = "http://apresolve.spotify.com:80"; use std::error::Error; diff --git a/core/src/authentication.rs b/core/src/authentication.rs index 544dda4..2839353 100644 --- a/core/src/authentication.rs +++ b/core/src/authentication.rs @@ -27,7 +27,7 @@ pub struct Credentials { impl Credentials { pub fn with_password(username: String, password: String) -> Credentials { Credentials { - username: username, + username, auth_type: AuthenticationType::AUTHENTICATION_USER_PASS, auth_data: password.into_bytes(), } @@ -103,9 +103,9 @@ impl Credentials { let auth_data = read_bytes(&mut cursor).unwrap(); Credentials { - username: username, - auth_type: auth_type, - auth_data: auth_data, + username, + auth_type, + auth_data, } } } diff --git a/core/src/channel.rs b/core/src/channel.rs index 54eee18..4e73b61 100644 --- a/core/src/channel.rs +++ b/core/src/channel.rs @@ -93,7 +93,7 @@ impl ChannelManager { } pub fn get_download_rate_estimate(&self) -> usize { - return self.lock(|inner| inner.download_rate_estimate); + self.lock(|inner| inner.download_rate_estimate) } pub(crate) fn shutdown(&self) { @@ -139,7 +139,7 @@ impl Stream for Channel { match self.state.clone() { ChannelState::Closed => panic!("Polling already terminated channel"), ChannelState::Header(mut data) => { - if data.len() == 0 { + if data.is_empty() { data = match self.recv_packet(cx) { Poll::Ready(Ok(x)) => x, Poll::Ready(Err(x)) => return Poll::Ready(Some(Err(x))), @@ -168,7 +168,7 @@ impl Stream for Channel { Poll::Ready(Err(x)) => return Poll::Ready(Some(Err(x))), Poll::Pending => return Poll::Pending, }; - if data.len() == 0 { + if data.is_empty() { self.receiver.close(); self.state = ChannelState::Closed; return Poll::Ready(None); diff --git a/core/src/diffie_hellman.rs b/core/src/diffie_hellman.rs index 358901b..8544809 100644 --- a/core/src/diffie_hellman.rs +++ b/core/src/diffie_hellman.rs @@ -30,8 +30,8 @@ impl DHLocalKeys { let public_key = util::powm(&DH_GENERATOR, &private_key, &DH_PRIME); DHLocalKeys { - private_key: private_key, - public_key: public_key, + private_key, + public_key, } } diff --git a/core/src/mercury/mod.rs b/core/src/mercury/mod.rs index 537ff2c..1a68e15 100644 --- a/core/src/mercury/mod.rs +++ b/core/src/mercury/mod.rs @@ -211,30 +211,28 @@ impl MercuryManager { if let Some(cb) = pending.callback { let _ = cb.send(Err(MercuryError)); } - } else { - if cmd == 0xb5 { - self.lock(|inner| { - let mut found = false; - inner.subscriptions.retain(|&(ref prefix, ref sub)| { - if response.uri.starts_with(prefix) { - found = true; + } else if cmd == 0xb5 { + self.lock(|inner| { + let mut found = false; + inner.subscriptions.retain(|&(ref prefix, ref sub)| { + if response.uri.starts_with(prefix) { + found = true; - // if send fails, remove from list of subs - // TODO: send unsub message - sub.send(response.clone()).is_ok() - } else { - // URI doesn't match - true - } - }); - - if !found { - debug!("unknown subscription uri={}", response.uri); + // if send fails, remove from list of subs + // TODO: send unsub message + sub.send(response.clone()).is_ok() + } else { + // URI doesn't match + true } - }) - } else if let Some(cb) = pending.callback { - let _ = cb.send(Ok(response)); - } + }); + + if !found { + debug!("unknown subscription uri={}", response.uri); + } + }) + } else if let Some(cb) = pending.callback { + let _ = cb.send(Ok(response)); } } diff --git a/core/src/mercury/sender.rs b/core/src/mercury/sender.rs index e276bcf..383d449 100644 --- a/core/src/mercury/sender.rs +++ b/core/src/mercury/sender.rs @@ -12,8 +12,8 @@ impl MercurySender { // TODO: pub(super) when stable pub(crate) fn new(mercury: MercuryManager, uri: String) -> MercurySender { MercurySender { - mercury: mercury, - uri: uri, + mercury, + uri, pending: VecDeque::new(), } } diff --git a/core/src/session.rs b/core/src/session.rs index 9eaff3e..53bbabd 100644 --- a/core/src/session.rs +++ b/core/src/session.rs @@ -105,25 +105,20 @@ impl Session { debug!("new Session[{}]", session_id); let session = Session(Arc::new(SessionInternal { - config: config, + config, data: RwLock::new(SessionData { country: String::new(), canonical_username: username, invalid: false, time_delta: 0, }), - tx_connection: sender_tx, - cache: cache.map(Arc::new), - audio_key: OnceCell::new(), channel: OnceCell::new(), mercury: OnceCell::new(), - handle, - - session_id: session_id, + session_id, })); let sender_task = UnboundedReceiverStream::new(sender_rx) diff --git a/core/src/spotify_id.rs b/core/src/spotify_id.rs index 17327b4..09d1097 100644 --- a/core/src/spotify_id.rs +++ b/core/src/spotify_id.rs @@ -45,7 +45,7 @@ impl SpotifyId { const SIZE_BASE16: usize = 32; const SIZE_BASE62: usize = 22; - fn as_track(n: u128) -> SpotifyId { + fn track(n: u128) -> SpotifyId { SpotifyId { id: n, audio_type: SpotifyAudioType::Track, @@ -71,7 +71,7 @@ impl SpotifyId { dst += p; } - Ok(SpotifyId::as_track(dst)) + Ok(SpotifyId::track(dst)) } /// Parses a base62 encoded [Spotify ID] into a `SpotifyId`. @@ -94,7 +94,7 @@ impl SpotifyId { dst += p; } - Ok(SpotifyId::as_track(dst)) + Ok(SpotifyId::track(dst)) } /// Creates a `SpotifyId` from a copy of `SpotifyId::SIZE` (16) bytes in big-endian order. @@ -102,7 +102,7 @@ impl SpotifyId { /// The resulting `SpotifyId` will default to a `SpotifyAudioType::TRACK`. pub fn from_raw(src: &[u8]) -> Result { match src.try_into() { - Ok(dst) => Ok(SpotifyId::as_track(u128::from_be_bytes(dst))), + Ok(dst) => Ok(SpotifyId::track(u128::from_be_bytes(dst))), Err(_) => Err(SpotifyIdError), } } diff --git a/core/tests/connect.rs b/core/tests/connect.rs index 4ea2a1f..b7bc29f 100644 --- a/core/tests/connect.rs +++ b/core/tests/connect.rs @@ -1,33 +1,34 @@ use librespot_core::*; -#[cfg(test)] -mod tests { - use super::*; - // Test AP Resolve - use apresolve::apresolve_or_fallback; - #[tokio::test] - async fn test_ap_resolve() { - env_logger::init(); - let ap = apresolve_or_fallback(&None, &None).await; - println!("AP: {:?}", ap); - } +// TODO: test is broken +// #[cfg(test)] +// mod tests { +// use super::*; +// // Test AP Resolve +// use apresolve::apresolve_or_fallback; +// #[tokio::test] +// async fn test_ap_resolve() { +// env_logger::init(); +// let ap = apresolve_or_fallback(&None, &None).await; +// println!("AP: {:?}", ap); +// } - // Test connect - use authentication::Credentials; - use config::SessionConfig; - #[tokio::test] - async fn test_connection() -> Result<(), Box> { - println!("Running connection test"); - let ap = apresolve_or_fallback(&None, &None).await; - let credentials = Credentials::with_password(String::from("test"), String::from("test")); - let session_config = SessionConfig::default(); - let proxy = None; +// // Test connect +// use authentication::Credentials; +// use config::SessionConfig; +// #[tokio::test] +// async fn test_connection() -> Result<(), Box> { +// println!("Running connection test"); +// let ap = apresolve_or_fallback(&None, &None).await; +// let credentials = Credentials::with_password(String::from("test"), String::from("test")); +// let session_config = SessionConfig::default(); +// let proxy = None; - println!("Connecting to AP \"{}\"", ap); - let mut connection = connection::connect(ap, &proxy).await?; - let rc = connection::authenticate(&mut connection, credentials, &session_config.device_id) - .await?; - println!("Authenticated as \"{}\"", rc.username); - Ok(()) - } -} +// println!("Connecting to AP \"{}\"", ap); +// let mut connection = connection::connect(ap, &proxy).await?; +// let rc = connection::authenticate(&mut connection, credentials, &session_config.device_id) +// .await?; +// println!("Authenticated as \"{}\"", rc.username); +// Ok(()) +// } +// } diff --git a/metadata/src/lib.rs b/metadata/src/lib.rs index 8faa027..75c07f8 100644 --- a/metadata/src/lib.rs +++ b/metadata/src/lib.rs @@ -292,7 +292,7 @@ impl Metadata for Playlist { .get_items() .iter() .map(|item| { - let uri_split = item.get_uri().split(":"); + let uri_split = item.get_uri().split(':'); let uri_parts: Vec<&str> = uri_split.collect(); SpotifyId::from_base62(uri_parts[2]).unwrap() }) diff --git a/playback/src/audio_backend/gstreamer.rs b/playback/src/audio_backend/gstreamer.rs index 6be6dd7..4678bfb 100644 --- a/playback/src/audio_backend/gstreamer.rs +++ b/playback/src/audio_backend/gstreamer.rs @@ -2,9 +2,10 @@ use super::{Open, Sink}; use crate::audio::AudioPacket; use gst::prelude::*; use gst::*; +use zerocopy::*; + use std::sync::mpsc::{sync_channel, SyncSender}; use std::{io, thread}; -use zerocopy::*; #[allow(dead_code)] pub struct GstreamerSink { @@ -91,10 +92,7 @@ impl Open for GstreamerSink { .set_state(gst::State::Playing) .expect("Unable to set the pipeline to the `Playing` state"); - GstreamerSink { - tx: tx, - pipeline: pipeline, - } + GstreamerSink { tx, pipeline } } } diff --git a/playback/src/audio_backend/jackaudio.rs b/playback/src/audio_backend/jackaudio.rs index 4699c18..e659d54 100644 --- a/playback/src/audio_backend/jackaudio.rs +++ b/playback/src/audio_backend/jackaudio.rs @@ -60,7 +60,7 @@ impl Open for JackSink { JackSink { send: tx, - active_client: active_client, + active_client, } } } diff --git a/playback/src/audio_backend/mod.rs b/playback/src/audio_backend/mod.rs index 214ede8..c816a6d 100644 --- a/playback/src/audio_backend/mod.rs +++ b/playback/src/audio_backend/mod.rs @@ -56,7 +56,7 @@ use self::pipe::StdoutSink; mod subprocess; use self::subprocess::SubprocessSink; -pub const BACKENDS: &'static [(&'static str, SinkBuilder)] = &[ +pub const BACKENDS: &[(&str, SinkBuilder)] = &[ #[cfg(feature = "alsa-backend")] ("alsa", mk_sink::), #[cfg(feature = "portaudio-backend")] diff --git a/playback/src/audio_backend/pulseaudio.rs b/playback/src/audio_backend/pulseaudio.rs index 11ea026..bc2be90 100644 --- a/playback/src/audio_backend/pulseaudio.rs +++ b/playback/src/audio_backend/pulseaudio.rs @@ -26,8 +26,8 @@ impl Open for PulseAudioSink { PulseAudioSink { s: None, - ss: ss, - device: device, + ss, + device, } } } diff --git a/playback/src/audio_backend/rodio.rs b/playback/src/audio_backend/rodio.rs index 56e19b6..338dfbb 100644 --- a/playback/src/audio_backend/rodio.rs +++ b/playback/src/audio_backend/rodio.rs @@ -43,7 +43,6 @@ pub enum RodioError { pub struct RodioSink { rodio_sink: rodio::Sink, - // will produce a TryRecvError on the receiver side when it is dropped. _close_tx: mpsc::SyncSender, } diff --git a/playback/src/audio_backend/sdl.rs b/playback/src/audio_backend/sdl.rs index 27d650f..47cd225 100644 --- a/playback/src/audio_backend/sdl.rs +++ b/playback/src/audio_backend/sdl.rs @@ -29,7 +29,7 @@ impl Open for SdlSink { .open_queue(None, &desired_spec) .expect("Could not open SDL audio device"); - SdlSink { queue: queue } + SdlSink { queue } } } diff --git a/playback/src/audio_backend/subprocess.rs b/playback/src/audio_backend/subprocess.rs index 0dd2563..9b24e21 100644 --- a/playback/src/audio_backend/subprocess.rs +++ b/playback/src/audio_backend/subprocess.rs @@ -1,6 +1,8 @@ use super::{Open, Sink}; use crate::audio::AudioPacket; + use shell_words::split; + use std::io::{self, Write}; use std::mem; use std::process::{Child, Command, Stdio}; @@ -15,7 +17,7 @@ impl Open for SubprocessSink { fn open(shell_command: Option) -> SubprocessSink { if let Some(shell_command) = shell_command { SubprocessSink { - shell_command: shell_command, + shell_command, child: None, } } else { diff --git a/playback/src/mixer/alsamixer.rs b/playback/src/mixer/alsamixer.rs index 1e00cc8..d9dbe31 100644 --- a/playback/src/mixer/alsamixer.rs +++ b/playback/src/mixer/alsamixer.rs @@ -1,10 +1,7 @@ use super::AudioFilter; use super::{Mixer, MixerConfig}; -use std; use std::error::Error; -use alsa; - const SND_CTL_TLV_DB_GAIN_MUTE: i64 = -9999999; #[derive(Clone)] @@ -72,14 +69,14 @@ impl AlsaMixer { } Ok(AlsaMixer { - config: config, + config, params: AlsaMixerVolumeParams { - min: min, - max: max, + min, + max, range: (max - min) as f64, - min_db: min_db, - max_db: max_db, - has_switch: has_switch, + min_db, + max_db, + has_switch, }, }) } diff --git a/playback/src/player.rs b/playback/src/player.rs index 9b2c712..7c200b2 100644 --- a/playback/src/player.rs +++ b/playback/src/player.rs @@ -252,8 +252,8 @@ impl Player { debug!("new Player[{}]", session.session_id()); let internal = PlayerInternal { - session: session, - config: config, + session, + config, commands: cmd_rx, state: PlayerState::Stopped, @@ -261,7 +261,7 @@ impl Player { sink: sink_builder(), sink_status: SinkStatus::Closed, sink_event_callback: None, - audio_filter: audio_filter, + audio_filter, event_senders: [event_sender].to_vec(), }; @@ -432,18 +432,12 @@ impl PlayerState { #[allow(dead_code)] fn is_stopped(&self) -> bool { use self::PlayerState::*; - match *self { - Stopped => true, - _ => false, - } + matches!(self, Stopped) } fn is_loading(&self) -> bool { use self::PlayerState::*; - match *self { - Loading { .. } => true, - _ => false, - } + matches!(self, Loading { .. }) } fn decoder(&mut self) -> Option<&mut Decoder> { @@ -697,7 +691,7 @@ impl PlayerTrackLoader { }; let is_cached = encrypted_file.is_cached(); - let mut stream_loader_controller = encrypted_file.get_stream_loader_controller(); + let stream_loader_controller = encrypted_file.get_stream_loader_controller(); if play_from_beginning { // No need to seek -> we stream from the beginning @@ -908,11 +902,7 @@ impl Future for PlayerInternal { .as_millis() as i64 - stream_position_millis as i64; - if lag > 1000 { - true - } else { - false - } + lag > 1000 } }; if notify_about_position { diff --git a/src/main.rs b/src/main.rs index 5de83de..7bddfaa 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,6 +2,23 @@ use futures_util::{future, FutureExt, StreamExt}; use librespot_playback::player::PlayerEvent; use log::{error, info, warn}; use sha1::{Digest, Sha1}; +use tokio::sync::mpsc::UnboundedReceiver; +use url::Url; + +use librespot::connect::spirc::Spirc; +use librespot::core::authentication::Credentials; +use librespot::core::cache::Cache; +use librespot::core::config::{ConnectConfig, DeviceType, SessionConfig, VolumeCtrl}; +use librespot::core::session::Session; +use librespot::core::version; +use librespot::playback::audio_backend::{self, Sink, BACKENDS}; +use librespot::playback::config::{Bitrate, NormalisationType, PlayerConfig}; +use librespot::playback::mixer::{self, Mixer, MixerConfig}; +use librespot::playback::player::Player; + +mod player_event_handler; +use player_event_handler::{emit_sink_event, run_program_on_events}; + use std::path::Path; use std::process::exit; use std::str::FromStr; @@ -10,24 +27,6 @@ use std::{ io::{stderr, Write}, pin::Pin, }; -use tokio::sync::mpsc::UnboundedReceiver; -use url::Url; - -use librespot::core::authentication::Credentials; -use librespot::core::cache::Cache; -use librespot::core::config::{ConnectConfig, DeviceType, SessionConfig, VolumeCtrl}; -use librespot::core::session::Session; -use librespot::core::version; - -use librespot::connect::spirc::Spirc; -use librespot::playback::audio_backend::{self, Sink, BACKENDS}; -use librespot::playback::config::{Bitrate, NormalisationType, PlayerConfig}; -use librespot::playback::mixer::{self, Mixer, MixerConfig}; -use librespot::playback::player::Player; - -mod player_event_handler; - -use player_event_handler::{emit_sink_event, run_program_on_events}; fn device_id(name: &str) -> String { hex::encode(Sha1::digest(name.as_bytes())) diff --git a/src/player_event_handler.rs b/src/player_event_handler.rs index 361e6b1..4c75128 100644 --- a/src/player_event_handler.rs +++ b/src/player_event_handler.rs @@ -1,12 +1,12 @@ use librespot::playback::player::PlayerEvent; +use librespot::playback::player::SinkStatus; use log::info; +use tokio::process::{Child as AsyncChild, Command as AsyncCommand}; + use std::collections::HashMap; use std::io; use std::process::{Command, ExitStatus}; -use librespot::playback::player::SinkStatus; -use tokio::process::{Child as AsyncChild, Command as AsyncCommand}; - pub fn run_program_on_events(event: PlayerEvent, onevent: &str) -> Option> { let mut env_vars = HashMap::new(); match event { From 3388508141bbbfe97f978381e2878bd898ceaa6c Mon Sep 17 00:00:00 2001 From: Evan Cameron Date: Sun, 28 Feb 2021 22:09:46 -0500 Subject: [PATCH 055/103] use current_thread --- src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 7bddfaa..6f04ac1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -452,7 +452,7 @@ fn setup(args: &[String]) -> Setup { } } -#[tokio::main] +#[tokio::main(flavor = "current_thread")] async fn main() { if env::var("RUST_BACKTRACE").is_err() { env::set_var("RUST_BACKTRACE", "full") From 5616004dbec3836b9382494312766eda6c3186de Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Wed, 10 Mar 2021 22:32:24 +0100 Subject: [PATCH 056/103] Fix many clippy lints ...and other small improvements --- README.md | 2 +- connect/src/discovery.rs | 5 +---- connect/src/spirc.rs | 18 ++++++++++-------- core/src/channel.rs | 9 +++++---- core/src/connection/handshake.rs | 8 ++++---- playback/src/mixer/mod.rs | 4 +++- playback/src/player.rs | 28 +++++++++++++--------------- src/main.rs | 18 ++++++------------ 8 files changed, 43 insertions(+), 49 deletions(-) diff --git a/README.md b/README.md index 7102c28..33b2b76 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ ALSA PortAudio PulseAudio JACK -JACK over Rodio +JACK over Rodio SDL Pipe ``` diff --git a/connect/src/discovery.rs b/connect/src/discovery.rs index df4d48e..e03b159 100644 --- a/connect/src/discovery.rs +++ b/connect/src/discovery.rs @@ -13,9 +13,6 @@ use tokio::sync::{mpsc, oneshot}; #[cfg(feature = "with-dns-sd")] use dns_sd::DNSService; -#[cfg(not(feature = "with-dns-sd"))] -use libmdns; - use librespot_core::authentication::Credentials; use librespot_core::config::ConnectConfig; use librespot_core::diffie_hellman::{DH_GENERATOR, DH_PRIME}; @@ -127,7 +124,7 @@ impl Discovery { let mut h = HmacSha1::new_varkey(&checksum_key).expect("HMAC can take key of any size"); h.update(encrypted); - if let Err(_) = h.verify(cksum) { + if h.verify(cksum).is_err() { warn!("Login error for user {:?}: MAC mismatch", username); let result = json!({ "status": 102, diff --git a/connect/src/spirc.rs b/connect/src/spirc.rs index 5afefe7..6869dd8 100644 --- a/connect/src/spirc.rs +++ b/connect/src/spirc.rs @@ -426,8 +426,8 @@ impl SpircTask { Ok(dur) => dur, Err(err) => err.duration(), }; - (dur.as_secs() as i64 + self.session.time_delta()) * 1000 - + (dur.subsec_nanos() / 1000_000) as i64 + + dur.as_millis() as i64 + 1000 * self.session.time_delta() } fn ensure_mixer_started(&mut self) { @@ -512,7 +512,9 @@ impl SpircTask { SpircCommand::Shutdown => { CommandSender::new(self, MessageType::kMessageTypeGoodbye).send(); self.shutdown = true; - self.commands.as_mut().map(|rx| rx.close()); + if let Some(rx) = self.commands.as_mut() { + rx.close() + } } } } @@ -620,7 +622,7 @@ impl SpircTask { ); if frame.get_ident() == self.ident - || (frame.get_recipient().len() > 0 && !frame.get_recipient().contains(&self.ident)) + || (!frame.get_recipient().is_empty() && !frame.get_recipient().contains(&self.ident)) { return; } @@ -639,7 +641,7 @@ impl SpircTask { self.update_tracks(&frame); - if self.state.get_track().len() > 0 { + if !self.state.get_track().is_empty() { let start_playing = frame.get_state().get_status() == PlayStatus::kPlayStatusPlay; self.load_track(start_playing, frame.get_state().get_position_ms()); @@ -862,7 +864,7 @@ impl SpircTask { fn preview_next_track(&mut self) -> Option { self.get_track_id_to_play_from_playlist(self.state.get_playing_track_index() + 1) - .and_then(|(track_id, _)| Some(track_id)) + .map(|(track_id, _)| track_id) } fn handle_preload_next_track(&mut self) { @@ -981,7 +983,7 @@ impl SpircTask { }; // Reinsert queued tracks after the new playing track. let mut pos = (new_index + 1) as usize; - for track in queue_tracks.into_iter() { + for track in queue_tracks { self.state.mut_track().insert(pos, track); pos += 1; } @@ -1120,7 +1122,7 @@ impl SpircTask { } self.state.set_playing_track_index(index); - self.state.set_track(tracks.into_iter().cloned().collect()); + self.state.set_track(tracks.iter().cloned().collect()); self.state.set_context_uri(context_uri); // has_shuffle/repeat seem to always be true in these replace msgs, // but to replicate the behaviour of the Android client we have to diff --git a/core/src/channel.rs b/core/src/channel.rs index 4e73b61..387b396 100644 --- a/core/src/channel.rs +++ b/core/src/channel.rs @@ -192,11 +192,12 @@ impl Stream for ChannelData { }; loop { - let x = match channel.poll_next_unpin(cx) { + let event = match channel.poll_next_unpin(cx) { Poll::Ready(x) => x.transpose()?, Poll::Pending => return Poll::Pending, }; - match x { + + match event { Some(ChannelEvent::Header(..)) => (), Some(ChannelEvent::Data(data)) => return Poll::Ready(Some(Ok(data))), None => return Poll::Ready(None), @@ -214,12 +215,12 @@ impl Stream for ChannelHeaders { Poll::Pending => return Poll::Pending, }; - let x = match channel.poll_next_unpin(cx) { + let event = match channel.poll_next_unpin(cx) { Poll::Ready(x) => x.transpose()?, Poll::Pending => return Poll::Pending, }; - match x { + match event { Some(ChannelEvent::Header(id, data)) => Poll::Ready(Some(Ok((id, data)))), Some(ChannelEvent::Data(..)) | None => Poll::Ready(None), } diff --git a/core/src/connection/handshake.rs b/core/src/connection/handshake.rs index 02d7713..67a786e 100644 --- a/core/src/connection/handshake.rs +++ b/core/src/connection/handshake.rs @@ -104,11 +104,11 @@ where Ok(message) } -async fn read_into_accumulator<'a, T: AsyncRead + Unpin>( - connection: &mut T, +async fn read_into_accumulator<'a, 'b, T: AsyncRead + Unpin>( + connection: &'a mut T, size: usize, - acc: &'a mut Vec, -) -> io::Result<&'a mut [u8]> { + acc: &'b mut Vec, +) -> io::Result<&'b mut [u8]> { let offset = acc.len(); acc.resize(offset + size, 0); diff --git a/playback/src/mixer/mod.rs b/playback/src/mixer/mod.rs index 325c1e1..9d9a817 100644 --- a/playback/src/mixer/mod.rs +++ b/playback/src/mixer/mod.rs @@ -42,11 +42,13 @@ impl Default for MixerConfig { pub mod softmixer; use self::softmixer::SoftMixer; +type MixerFn = fn(Option) -> Box; + fn mk_sink(device: Option) -> Box { Box::new(M::open(device)) } -pub fn find>(name: Option) -> Option) -> Box> { +pub fn find>(name: Option) -> Option { match name.as_ref().map(AsRef::as_ref) { None | Some("softvol") => Some(mk_sink::), #[cfg(feature = "alsa-backend")] diff --git a/playback/src/player.rs b/playback/src/player.rs index 7c200b2..6c9e516 100644 --- a/playback/src/player.rs +++ b/playback/src/player.rs @@ -196,13 +196,12 @@ struct NormalisationData { impl NormalisationData { fn parse_from_file(mut file: T) -> io::Result { const SPOTIFY_NORMALIZATION_HEADER_START_OFFSET: u64 = 144; - file.seek(SeekFrom::Start(SPOTIFY_NORMALIZATION_HEADER_START_OFFSET)) - .unwrap(); + file.seek(SeekFrom::Start(SPOTIFY_NORMALIZATION_HEADER_START_OFFSET))?; - let track_gain_db = file.read_f32::().unwrap(); - let track_peak = file.read_f32::().unwrap(); - let album_gain_db = file.read_f32::().unwrap(); - let album_peak = file.read_f32::().unwrap(); + let track_gain_db = file.read_f32::()?; + let track_peak = file.read_f32::()?; + let album_gain_db = file.read_f32::()?; + let album_peak = file.read_f32::()?; let r = NormalisationData { track_gain_db: track_gain_db, @@ -889,8 +888,7 @@ impl Future for PlayerInternal { if !passthrough { if let Some(ref packet) = packet { - *stream_position_pcm = - *stream_position_pcm + (packet.samples().len() / 2) as u64; + *stream_position_pcm += (packet.samples().len() / 2) as u64; let stream_position_millis = Self::position_pcm_to_ms(*stream_position_pcm); @@ -1111,7 +1109,9 @@ impl PlayerInternal { editor.modify_stream(data) } - if self.config.normalisation && normalisation_factor != 1.0 { + if self.config.normalisation + && f32::abs(normalisation_factor - 1.0) > f32::EPSILON + { for x in data.iter_mut() { *x = (*x as f32 * normalisation_factor) as i16; } @@ -1598,12 +1598,10 @@ impl PlayerInternal { let (result_tx, result_rx) = oneshot::channel(); std::thread::spawn(move || { - futures_executor::block_on(loader.load_track(spotify_id, position_ms)).and_then( - move |data| { - let _ = result_tx.send(data); - Some(()) - }, - ); + let data = futures_executor::block_on(loader.load_track(spotify_id, position_ms)); + if let Some(data) = data { + let _ = result_tx.send(data); + } }); result_rx.map_err(|_| ()) diff --git a/src/main.rs b/src/main.rs index 6f04ac1..ff9e564 100644 --- a/src/main.rs +++ b/src/main.rs @@ -236,13 +236,7 @@ fn setup(args: &[String]) -> Setup { let matches = match opts.parse(&args[1..]) { Ok(m) => m, Err(f) => { - writeln!( - stderr(), - "error: {}\n{}", - f.to_string(), - usage(&args[0], &opts) - ) - .unwrap(); + eprintln!("error: {}\n{}", f.to_string(), usage(&args[0], &opts)); exit(1); } }; @@ -360,7 +354,7 @@ fn setup(args: &[String]) -> Setup { SessionConfig { user_agent: version::version_string(), device_id: device_id, - proxy: matches.opt_str("proxy").or(std::env::var("http_proxy").ok()).map( + proxy: matches.opt_str("proxy").or_else(|| std::env::var("http_proxy").ok()).map( |s| { match Url::parse(&s) { Ok(url) => { @@ -390,14 +384,14 @@ fn setup(args: &[String]) -> Setup { .opt_str("b") .as_ref() .map(|bitrate| Bitrate::from_str(bitrate).expect("Invalid bitrate")) - .unwrap_or(Bitrate::default()); + .unwrap_or_default(); let gain_type = matches .opt_str("normalisation-gain-type") .as_ref() .map(|gain_type| { NormalisationType::from_str(gain_type).expect("Invalid normalisation type") }) - .unwrap_or(NormalisationType::default()); + .unwrap_or_default(); PlayerConfig { bitrate: bitrate, gapless: !matches.opt_present("disable-gapless"), @@ -416,13 +410,13 @@ fn setup(args: &[String]) -> Setup { .opt_str("device-type") .as_ref() .map(|device_type| DeviceType::from_str(device_type).expect("Invalid device type")) - .unwrap_or(DeviceType::default()); + .unwrap_or_default(); let volume_ctrl = matches .opt_str("volume-ctrl") .as_ref() .map(|volume_ctrl| VolumeCtrl::from_str(volume_ctrl).expect("Invalid volume ctrl type")) - .unwrap_or(VolumeCtrl::default()); + .unwrap_or_default(); ConnectConfig { name: name, From 059b9029ded1ac7d08efdef3ec1e841d33b0b5ef Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Wed, 10 Mar 2021 22:39:01 +0100 Subject: [PATCH 057/103] Remove redundant field names --- connect/src/discovery.rs | 10 +++++----- connect/src/spirc.rs | 19 ++++++++----------- metadata/src/lib.rs | 27 +++++++++++++-------------- playback/src/player.rs | 23 ++++++++++------------- src/main.rs | 32 ++++++++++++++++---------------- 5 files changed, 52 insertions(+), 59 deletions(-) diff --git a/connect/src/discovery.rs b/connect/src/discovery.rs index e03b159..7bb36a2 100644 --- a/connect/src/discovery.rs +++ b/connect/src/discovery.rs @@ -51,11 +51,11 @@ impl Discovery { let public_key = util::powm(&DH_GENERATOR, &private_key, &DH_PRIME); let discovery = Discovery(Arc::new(DiscoveryInner { - config: config, - device_id: device_id, - private_key: private_key, - public_key: public_key, - tx: tx, + config, + device_id, + private_key, + public_key, + tx, })); (discovery, rx) diff --git a/connect/src/spirc.rs b/connect/src/spirc.rs index 6869dd8..f111e54 100644 --- a/connect/src/spirc.rs +++ b/connect/src/spirc.rs @@ -287,27 +287,27 @@ impl Spirc { let player_events = player.get_player_event_channel(); let mut task = SpircTask { - player: player, - mixer: mixer, + player, + mixer, config: task_config, sequence: SeqGenerator::new(1), - ident: ident, + ident, - device: device, + device, state: initial_state(), play_request_id: None, mixer_started: false, play_status: SpircPlayStatus::Stopped, - subscription: subscription, - sender: sender, + subscription, + sender, commands: Some(cmd_rx), player_events: Some(player_events), shutdown: false, - session: session, + session, context_fut: Box::pin(future::pending()), autoplay_fut: Box::pin(future::pending()), @@ -1293,10 +1293,7 @@ impl<'a> CommandSender<'a> { frame.set_typ(cmd); frame.set_device_state(spirc.device.clone()); frame.set_state_update_id(spirc.now_ms()); - CommandSender { - spirc: spirc, - frame: frame, - } + CommandSender { spirc, frame } } fn recipient(mut self, recipient: &'a str) -> CommandSender { diff --git a/metadata/src/lib.rs b/metadata/src/lib.rs index 75c07f8..2c982ec 100644 --- a/metadata/src/lib.rs +++ b/metadata/src/lib.rs @@ -1,5 +1,4 @@ #![allow(clippy::unused_io_amount)] -#![allow(clippy::redundant_field_names)] #[macro_use] extern crate log; @@ -85,7 +84,7 @@ impl AudioFiles for Track { async fn get_audio_item(session: &Session, id: SpotifyId) -> Result { let item = Self::get(session, id).await?; Ok(AudioItem { - id: id, + id, uri: format!("spotify:track:{}", id.to_base62()), files: item.files, name: item.name, @@ -102,7 +101,7 @@ impl AudioFiles for Episode { let item = Self::get(session, id).await?; Ok(AudioItem { - id: id, + id, uri: format!("spotify:episode:{}", id.to_base62()), files: item.files, name: item.name, @@ -222,8 +221,8 @@ impl Metadata for Track { name: msg.get_name().to_owned(), duration: msg.get_duration(), album: SpotifyId::from_raw(msg.get_album().get_gid()).unwrap(), - artists: artists, - files: files, + artists, + files, alternatives: msg .get_alternative() .iter() @@ -272,9 +271,9 @@ impl Metadata for Album { Album { id: SpotifyId::from_raw(msg.get_gid()).unwrap(), name: msg.get_name().to_owned(), - artists: artists, - tracks: tracks, - covers: covers, + artists, + tracks, + covers, } } } @@ -309,7 +308,7 @@ impl Metadata for Playlist { Playlist { revision: msg.get_revision().to_vec(), name: msg.get_attributes().get_name().to_owned(), - tracks: tracks, + tracks, user: msg.get_owner_username().to_string(), } } @@ -342,7 +341,7 @@ impl Metadata for Artist { Artist { id: SpotifyId::from_raw(msg.get_gid()).unwrap(), name: msg.get_name().to_owned(), - top_tracks: top_tracks, + top_tracks, } } } @@ -388,8 +387,8 @@ impl Metadata for Episode { duration: msg.get_duration().to_owned(), language: msg.get_language().to_owned(), show: SpotifyId::from_raw(msg.get_show().get_gid()).unwrap(), - covers: covers, - files: files, + covers, + files, available: parse_restrictions(msg.get_restriction(), &country, "premium"), explicit: msg.get_explicit().to_owned(), } @@ -427,8 +426,8 @@ impl Metadata for Show { id: SpotifyId::from_raw(msg.get_gid()).unwrap(), name: msg.get_name().to_owned(), publisher: msg.get_publisher().to_owned(), - episodes: episodes, - covers: covers, + episodes, + covers, } } } diff --git a/playback/src/player.rs b/playback/src/player.rs index 6c9e516..0d2380e 100644 --- a/playback/src/player.rs +++ b/playback/src/player.rs @@ -204,10 +204,10 @@ impl NormalisationData { let album_peak = file.read_f32::()?; let r = NormalisationData { - track_gain_db: track_gain_db, - track_peak: track_peak, - album_gain_db: album_gain_db, - album_peak: album_peak, + track_gain_db, + track_peak, + album_gain_db, + album_peak, }; Ok(r) @@ -1164,8 +1164,8 @@ impl PlayerInternal { }); self.state = PlayerState::Playing { - track_id: track_id, - play_request_id: play_request_id, + track_id, + play_request_id, decoder: loaded_track.decoder, normalisation_factor: loaded_track.normalisation_factor, stream_loader_controller: loaded_track.stream_loader_controller, @@ -1181,8 +1181,8 @@ impl PlayerInternal { self.ensure_sink_stopped(false); self.state = PlayerState::Paused { - track_id: track_id, - play_request_id: play_request_id, + track_id, + play_request_id, decoder: loaded_track.decoder, normalisation_factor: loaded_track.normalisation_factor, stream_loader_controller: loaded_track.stream_loader_controller, @@ -1229,7 +1229,7 @@ impl PlayerInternal { track_id: old_track_id, .. } => self.send_event(PlayerEvent::Changed { - old_track_id: old_track_id, + old_track_id, new_track_id: track_id, }), PlayerState::Stopped => self.send_event(PlayerEvent::Started { @@ -1726,10 +1726,7 @@ struct Subfile { impl Subfile { pub fn new(mut stream: T, offset: u64) -> Subfile { stream.seek(SeekFrom::Start(offset)).unwrap(); - Subfile { - stream: stream, - offset: offset, - } + Subfile { stream, offset } } } diff --git a/src/main.rs b/src/main.rs index ff9e564..5b445b2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -353,7 +353,7 @@ fn setup(args: &[String]) -> Setup { SessionConfig { user_agent: version::version_string(), - device_id: device_id, + device_id, proxy: matches.opt_str("proxy").or_else(|| std::env::var("http_proxy").ok()).map( |s| { match Url::parse(&s) { @@ -393,7 +393,7 @@ fn setup(args: &[String]) -> Setup { }) .unwrap_or_default(); PlayerConfig { - bitrate: bitrate, + bitrate, gapless: !matches.opt_present("disable-gapless"), normalisation: matches.opt_present("enable-volume-normalisation"), normalisation_type: gain_type, @@ -419,10 +419,10 @@ fn setup(args: &[String]) -> Setup { .unwrap_or_default(); ConnectConfig { - name: name, - device_type: device_type, + name, + device_type, volume: initial_volume, - volume_ctrl: volume_ctrl, + volume_ctrl, autoplay: matches.opt_present("autoplay"), } }; @@ -430,17 +430,17 @@ fn setup(args: &[String]) -> Setup { let enable_discovery = !matches.opt_present("disable-discovery"); Setup { - backend: backend, - cache: cache, - session_config: session_config, - player_config: player_config, - connect_config: connect_config, - credentials: credentials, - device: device, - enable_discovery: enable_discovery, - zeroconf_port: zeroconf_port, - mixer: mixer, - mixer_config: mixer_config, + backend, + cache, + session_config, + player_config, + connect_config, + credentials, + device, + enable_discovery, + zeroconf_port, + mixer, + mixer_config, player_event_program: matches.opt_str("onevent"), emit_sink_events: matches.opt_present("emit-sink-events"), } From e71a004e93d9376bb30834416cdd9248ad521246 Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Sun, 28 Feb 2021 11:36:14 +0100 Subject: [PATCH 058/103] Refactor AudioFileFetch using async/await Previously, polling `AudioFileFetch` consisted of three parts: Handling stream loader commands, handling received data, and triggering preloading in stream mode when the number of open requests is sufficiently small. The first steps use channels which are polled, and if something's available, it's handled. The third step is executed on every call of `poll`. The first two could easily be refactored using a `tokio::select!`-loop. Therefore, counting the number of open requests was also refactored to fit into this scheme. They were previously counted using a shared `AtomicUsize`. Now, the number of open requests is stored exclusively in `AudioFileFetch`, increased on starting a request, and decreased by an oneshot channel that is fired when a request is finished. This allows us to `select` that channel in the loop too, and since loading ahead makes only sense if the number of open requests decreases, the third step is only executed in this case. `AudioFileFetch` does not implement `Future` anymore, but is rather used as helper struct in an async fn `audio_file_fetch`. --- audio/src/fetch.rs | 515 +++++++++++++++------------------------------ 1 file changed, 172 insertions(+), 343 deletions(-) diff --git a/audio/src/fetch.rs b/audio/src/fetch.rs index 5fdf9e7..da34309 100644 --- a/audio/src/fetch.rs +++ b/audio/src/fetch.rs @@ -1,11 +1,8 @@ use std::cmp::{max, min}; use std::fs; -use std::future::Future; use std::io::{self, Read, Seek, SeekFrom, Write}; -use std::pin::Pin; use std::sync::atomic::{self, AtomicUsize}; use std::sync::{Arc, Condvar, Mutex}; -use std::task::{Context, Poll}; use std::time::{Duration, Instant}; use byteorder::{BigEndian, ByteOrder, WriteBytesExt}; @@ -236,7 +233,7 @@ struct AudioFileDownloadStatus { downloaded: RangeSet, } -#[derive(Copy, Clone)] +#[derive(Copy, Clone, PartialEq, Eq)] enum DownloadStrategy { RandomAccess(), Streaming(), @@ -249,7 +246,6 @@ struct AudioFileShared { cond: Condvar, download_status: Mutex, download_strategy: Mutex, - number_of_open_requests: AtomicUsize, ping_time_ms: AtomicUsize, read_position: AtomicUsize, } @@ -358,7 +354,6 @@ impl AudioFileStreaming { downloaded: RangeSet::new(), }), download_strategy: Mutex::new(DownloadStrategy::RandomAccess()), // start with random access mode until someone tells us otherwise - number_of_open_requests: AtomicUsize::new(0), ping_time_ms: AtomicUsize::new(0), read_position: AtomicUsize::new(0), }); @@ -373,7 +368,7 @@ impl AudioFileStreaming { let (stream_loader_command_tx, stream_loader_command_rx) = mpsc::unbounded_channel::(); - let fetcher = AudioFileFetch::new( + session.spawn(audio_file_fetch( session.clone(), shared.clone(), initial_data_rx, @@ -382,9 +377,8 @@ impl AudioFileStreaming { write_file, stream_loader_command_rx, complete_tx, - ); + )); - session.spawn(fetcher); Ok(AudioFileStreaming { read_file, position: 0, @@ -442,17 +436,11 @@ async fn audio_file_fetch_receive_data( initial_data_offset: usize, initial_request_length: usize, request_sent_time: Instant, + mut measure_ping_time: bool, + finish_tx: mpsc::UnboundedSender<()>, ) { let mut data_offset = initial_data_offset; let mut request_length = initial_request_length; - let mut measure_ping_time = shared - .number_of_open_requests - .load(atomic::Ordering::SeqCst) - == 0; - - shared - .number_of_open_requests - .fetch_add(1, atomic::Ordering::SeqCst); let result = loop { let data = match data_rx.next().await { @@ -501,9 +489,7 @@ async fn audio_file_fetch_receive_data( shared.cond.notify_all(); } - shared - .number_of_open_requests - .fetch_sub(1, atomic::Ordering::SeqCst); + let _ = finish_tx.send(()); if result.is_err() { warn!( @@ -517,162 +503,6 @@ async fn audio_file_fetch_receive_data( ); } } -/* -async fn audio_file_fetch( - session: Session, - shared: Arc, - initial_data_rx: ChannelData, - initial_request_sent_time: Instant, - initial_data_length: usize, - - output: NamedTempFile, - stream_loader_command_rx: mpsc::UnboundedReceiver, - complete_tx: oneshot::Sender, -) { - let (file_data_tx, file_data_rx) = unbounded::(); - - let requested_range = Range::new(0, initial_data_length); - let mut download_status = shared.download_status.lock().unwrap(); - download_status.requested.add_range(&requested_range); - - session.spawn(audio_file_fetch_receive_data( - shared.clone(), - file_data_tx.clone(), - initial_data_rx, - 0, - initial_data_length, - initial_request_sent_time, - )); - - let mut network_response_times_ms: Vec::new(); - - let f1 = file_data_rx.map(|x| Ok::<_, ()>(x)).try_for_each(|x| { - match x { - ReceivedData::ResponseTimeMs(response_time_ms) => { - trace!("Ping time estimated as: {} ms.", response_time_ms); - - // record the response time - network_response_times_ms.push(response_time_ms); - - // prune old response times. Keep at most three. - while network_response_times_ms.len() > 3 { - network_response_times_ms.remove(0); - } - - // stats::median is experimental. So we calculate the median of up to three ourselves. - let ping_time_ms: usize = match network_response_times_ms.len() { - 1 => network_response_times_ms[0] as usize, - 2 => { - ((network_response_times_ms[0] + network_response_times_ms[1]) / 2) as usize - } - 3 => { - let mut times = network_response_times_ms.clone(); - times.sort(); - times[1] - } - _ => unreachable!(), - }; - - // store our new estimate for everyone to see - shared - .ping_time_ms - .store(ping_time_ms, atomic::Ordering::Relaxed); - } - ReceivedData::Data(data) => { - output - .as_mut() - .unwrap() - .seek(SeekFrom::Start(data.offset as u64)) - .unwrap(); - output - .as_mut() - .unwrap() - .write_all(data.data.as_ref()) - .unwrap(); - - let mut full = false; - - { - let mut download_status = shared.download_status.lock().unwrap(); - - let received_range = Range::new(data.offset, data.data.len()); - download_status.downloaded.add_range(&received_range); - shared.cond.notify_all(); - - if download_status.downloaded.contained_length_from_value(0) - >= shared.file_size - { - full = true; - } - - drop(download_status); - } - - if full { - self.finish(); - return future::ready(Err(())); - } - } - } - future::ready(Ok(())) - }); - - let f2 = stream_loader_command_rx.map(Ok::<_, ()>).try_for_each(|x| { - match cmd { - StreamLoaderCommand::Fetch(request) => { - self.download_range(request.start, request.length); - } - StreamLoaderCommand::RandomAccessMode() => { - *(shared.download_strategy.lock().unwrap()) = DownloadStrategy::RandomAccess(); - } - StreamLoaderCommand::StreamMode() => { - *(shared.download_strategy.lock().unwrap()) = DownloadStrategy::Streaming(); - } - StreamLoaderCommand::Close() => return future::ready(Err(())), - } - Ok(()) - }); - - let f3 = future::poll_fn(|_| { - if let DownloadStrategy::Streaming() = self.get_download_strategy() { - let number_of_open_requests = shared - .number_of_open_requests - .load(atomic::Ordering::SeqCst); - let max_requests_to_send = - MAX_PREFETCH_REQUESTS - min(MAX_PREFETCH_REQUESTS, number_of_open_requests); - - if max_requests_to_send > 0 { - let bytes_pending: usize = { - let download_status = shared.download_status.lock().unwrap(); - download_status - .requested - .minus(&download_status.downloaded) - .len() - }; - - let ping_time_seconds = - 0.001 * shared.ping_time_ms.load(atomic::Ordering::Relaxed) as f64; - let download_rate = session.channel().get_download_rate_estimate(); - - let desired_pending_bytes = max( - (PREFETCH_THRESHOLD_FACTOR * ping_time_seconds * shared.stream_data_rate as f64) - as usize, - (FAST_PREFETCH_THRESHOLD_FACTOR * ping_time_seconds * download_rate as f64) - as usize, - ); - - if bytes_pending < desired_pending_bytes { - self.pre_fetch_more_data( - desired_pending_bytes - bytes_pending, - max_requests_to_send, - ); - } - } - } - Poll::Pending - }); - future::select_all(vec![f1, f2, f3]).await -}*/ struct AudioFileFetch { session: Session, @@ -680,54 +510,21 @@ struct AudioFileFetch { output: Option, file_data_tx: mpsc::UnboundedSender, - file_data_rx: mpsc::UnboundedReceiver, - - stream_loader_command_rx: mpsc::UnboundedReceiver, complete_tx: Option>, network_response_times_ms: Vec, + number_of_open_requests: usize, + + download_finish_tx: mpsc::UnboundedSender<()>, +} + +// Might be replaced by enum from std once stable +#[derive(PartialEq, Eq)] +enum ControlFlow { + Break, + Continue, } impl AudioFileFetch { - fn new( - session: Session, - shared: Arc, - initial_data_rx: ChannelData, - initial_request_sent_time: Instant, - initial_data_length: usize, - - output: NamedTempFile, - stream_loader_command_rx: mpsc::UnboundedReceiver, - complete_tx: oneshot::Sender, - ) -> AudioFileFetch { - let (file_data_tx, file_data_rx) = mpsc::unbounded_channel::(); - - { - let requested_range = Range::new(0, initial_data_length); - let mut download_status = shared.download_status.lock().unwrap(); - download_status.requested.add_range(&requested_range); - } - - session.spawn(audio_file_fetch_receive_data( - shared.clone(), - file_data_tx.clone(), - initial_data_rx, - 0, - initial_data_length, - initial_request_sent_time, - )); - - AudioFileFetch { - session, - shared, - output: Some(output), - file_data_tx, - file_data_rx, - stream_loader_command_rx, - complete_tx: Some(complete_tx), - network_response_times_ms: Vec::new(), - } - } - fn get_download_strategy(&mut self) -> DownloadStrategy { *(self.shared.download_strategy.lock().unwrap()) } @@ -785,7 +582,11 @@ impl AudioFileFetch { range.start, range.length, Instant::now(), + self.number_of_open_requests == 0, + self.download_finish_tx.clone(), )); + + self.number_of_open_requests += 1; } } @@ -833,103 +634,86 @@ impl AudioFileFetch { } } - fn poll_file_data_rx(&mut self, cx: &mut Context<'_>) -> Poll<()> { - loop { - match self.file_data_rx.poll_recv(cx) { - Poll::Ready(None) => return Poll::Ready(()), - Poll::Ready(Some(ReceivedData::ResponseTimeMs(response_time_ms))) => { - trace!("Ping time estimated as: {} ms.", response_time_ms); + fn handle_file_data(&mut self, data: ReceivedData) -> ControlFlow { + match data { + ReceivedData::ResponseTimeMs(response_time_ms) => { + trace!("Ping time estimated as: {} ms.", response_time_ms); - // record the response time - self.network_response_times_ms.push(response_time_ms); + // record the response time + self.network_response_times_ms.push(response_time_ms); - // prune old response times. Keep at most three. - while self.network_response_times_ms.len() > 3 { - self.network_response_times_ms.remove(0); - } - - // stats::median is experimental. So we calculate the median of up to three ourselves. - let ping_time_ms: usize = match self.network_response_times_ms.len() { - 1 => self.network_response_times_ms[0] as usize, - 2 => { - ((self.network_response_times_ms[0] - + self.network_response_times_ms[1]) - / 2) as usize - } - 3 => { - let mut times = self.network_response_times_ms.clone(); - times.sort_unstable(); - times[1] - } - _ => unreachable!(), - }; - - // store our new estimate for everyone to see - self.shared - .ping_time_ms - .store(ping_time_ms, atomic::Ordering::Relaxed); + // prune old response times. Keep at most three. + while self.network_response_times_ms.len() > 3 { + self.network_response_times_ms.remove(0); } - Poll::Ready(Some(ReceivedData::Data(data))) => { - self.output - .as_mut() - .unwrap() - .seek(SeekFrom::Start(data.offset as u64)) - .unwrap(); - self.output - .as_mut() - .unwrap() - .write_all(data.data.as_ref()) - .unwrap(); - let mut full = false; - - { - let mut download_status = self.shared.download_status.lock().unwrap(); - - let received_range = Range::new(data.offset, data.data.len()); - download_status.downloaded.add_range(&received_range); - self.shared.cond.notify_all(); - - if download_status.downloaded.contained_length_from_value(0) - >= self.shared.file_size - { - full = true; - } - - drop(download_status); + // stats::median is experimental. So we calculate the median of up to three ourselves. + let ping_time_ms: usize = match self.network_response_times_ms.len() { + 1 => self.network_response_times_ms[0] as usize, + 2 => { + ((self.network_response_times_ms[0] + self.network_response_times_ms[1]) + / 2) as usize } - - if full { - self.finish(); - return Poll::Ready(()); + 3 => { + let mut times = self.network_response_times_ms.clone(); + times.sort_unstable(); + times[1] } + _ => unreachable!(), + }; + + // store our new estimate for everyone to see + self.shared + .ping_time_ms + .store(ping_time_ms, atomic::Ordering::Relaxed); + } + ReceivedData::Data(data) => { + self.output + .as_mut() + .unwrap() + .seek(SeekFrom::Start(data.offset as u64)) + .unwrap(); + self.output + .as_mut() + .unwrap() + .write_all(data.data.as_ref()) + .unwrap(); + + let mut download_status = self.shared.download_status.lock().unwrap(); + + let received_range = Range::new(data.offset, data.data.len()); + download_status.downloaded.add_range(&received_range); + self.shared.cond.notify_all(); + + let full = download_status.downloaded.contained_length_from_value(0) + >= self.shared.file_size; + + drop(download_status); + + if full { + self.finish(); + return ControlFlow::Break; } - Poll::Pending => return Poll::Pending, } } + ControlFlow::Continue } - fn poll_stream_loader_command_rx(&mut self, cx: &mut Context<'_>) -> Poll<()> { - loop { - match self.stream_loader_command_rx.poll_recv(cx) { - Poll::Ready(None) => return Poll::Ready(()), - Poll::Ready(Some(cmd)) => match cmd { - StreamLoaderCommand::Fetch(request) => { - self.download_range(request.start, request.length); - } - StreamLoaderCommand::RandomAccessMode() => { - *(self.shared.download_strategy.lock().unwrap()) = - DownloadStrategy::RandomAccess(); - } - StreamLoaderCommand::StreamMode() => { - *(self.shared.download_strategy.lock().unwrap()) = - DownloadStrategy::Streaming(); - } - StreamLoaderCommand::Close() => return Poll::Ready(()), - }, - Poll::Pending => return Poll::Pending, + fn handle_stream_loader_command(&mut self, cmd: StreamLoaderCommand) -> ControlFlow { + match cmd { + StreamLoaderCommand::Fetch(request) => { + self.download_range(request.start, request.length); } + StreamLoaderCommand::RandomAccessMode() => { + *(self.shared.download_strategy.lock().unwrap()) = DownloadStrategy::RandomAccess(); + } + StreamLoaderCommand::StreamMode() => { + *(self.shared.download_strategy.lock().unwrap()) = DownloadStrategy::Streaming(); + self.trigger_preload(); + } + StreamLoaderCommand::Close() => return ControlFlow::Break, } + ControlFlow::Continue } fn finish(&mut self) { @@ -939,57 +723,102 @@ impl AudioFileFetch { output.seek(SeekFrom::Start(0)).unwrap(); let _ = complete_tx.send(output); } + + fn trigger_preload(&mut self) { + if self.number_of_open_requests >= MAX_PREFETCH_REQUESTS { + return; + } + + let max_requests_to_send = MAX_PREFETCH_REQUESTS - self.number_of_open_requests; + + let bytes_pending: usize = { + let download_status = self.shared.download_status.lock().unwrap(); + download_status + .requested + .minus(&download_status.downloaded) + .len() + }; + + let ping_time_seconds = + 0.001 * self.shared.ping_time_ms.load(atomic::Ordering::Relaxed) as f64; + let download_rate = self.session.channel().get_download_rate_estimate(); + + let desired_pending_bytes = max( + (PREFETCH_THRESHOLD_FACTOR * ping_time_seconds * self.shared.stream_data_rate as f64) + as usize, + (FAST_PREFETCH_THRESHOLD_FACTOR * ping_time_seconds * download_rate as f64) as usize, + ); + + if bytes_pending < desired_pending_bytes { + self.pre_fetch_more_data(desired_pending_bytes - bytes_pending, max_requests_to_send); + } + } } -impl Future for AudioFileFetch { - type Output = (); - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> { - if let Poll::Ready(()) = self.poll_stream_loader_command_rx(cx) { - return Poll::Ready(()); - } +async fn audio_file_fetch( + session: Session, + shared: Arc, + initial_data_rx: ChannelData, + initial_request_sent_time: Instant, + initial_data_length: usize, - if let Poll::Ready(()) = self.poll_file_data_rx(cx) { - return Poll::Ready(()); - } + output: NamedTempFile, + mut stream_loader_command_rx: mpsc::UnboundedReceiver, + complete_tx: oneshot::Sender, +) { + let (file_data_tx, mut file_data_rx) = mpsc::unbounded_channel(); + let (download_finish_tx, mut download_finish_rx) = mpsc::unbounded_channel(); - if let DownloadStrategy::Streaming() = self.get_download_strategy() { - let number_of_open_requests = self - .shared - .number_of_open_requests - .load(atomic::Ordering::SeqCst); - let max_requests_to_send = - MAX_PREFETCH_REQUESTS - min(MAX_PREFETCH_REQUESTS, number_of_open_requests); + { + let requested_range = Range::new(0, initial_data_length); + let mut download_status = shared.download_status.lock().unwrap(); + download_status.requested.add_range(&requested_range); + } - if max_requests_to_send > 0 { - let bytes_pending: usize = { - let download_status = self.shared.download_status.lock().unwrap(); - download_status - .requested - .minus(&download_status.downloaded) - .len() - }; + session.spawn(audio_file_fetch_receive_data( + shared.clone(), + file_data_tx.clone(), + initial_data_rx, + 0, + initial_data_length, + initial_request_sent_time, + true, + download_finish_tx.clone(), + )); - let ping_time_seconds = - 0.001 * self.shared.ping_time_ms.load(atomic::Ordering::Relaxed) as f64; - let download_rate = self.session.channel().get_download_rate_estimate(); + let mut fetch = AudioFileFetch { + session, + shared, + output: Some(output), - let desired_pending_bytes = max( - (PREFETCH_THRESHOLD_FACTOR - * ping_time_seconds - * self.shared.stream_data_rate as f64) as usize, - (FAST_PREFETCH_THRESHOLD_FACTOR * ping_time_seconds * download_rate as f64) - as usize, - ); + file_data_tx, + complete_tx: Some(complete_tx), + network_response_times_ms: Vec::new(), + number_of_open_requests: 1, - if bytes_pending < desired_pending_bytes { - self.pre_fetch_more_data( - desired_pending_bytes - bytes_pending, - max_requests_to_send, - ); + download_finish_tx, + }; + + loop { + tokio::select! { + cmd = stream_loader_command_rx.recv() => { + if cmd.map_or(true, |cmd| fetch.handle_stream_loader_command(cmd) == ControlFlow::Break) { + break; + } + }, + data = file_data_rx.recv() => { + if data.map_or(true, |data| fetch.handle_file_data(data) == ControlFlow::Break) { + break; + } + }, + _ = download_finish_rx.recv() => { + fetch.number_of_open_requests -= 1; + + if fetch.get_download_strategy() == DownloadStrategy::Streaming() { + fetch.trigger_preload(); } } } - Poll::Pending } } From ca255c17f06801ad9b79259233bd7a6b952b3942 Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Sun, 28 Feb 2021 11:36:15 +0100 Subject: [PATCH 059/103] Split file fetch.rs --- audio/src/{fetch.rs => fetch/mod.rs} | 444 +------------------------- audio/src/fetch/receive.rs | 455 +++++++++++++++++++++++++++ 2 files changed, 461 insertions(+), 438 deletions(-) rename audio/src/{fetch.rs => fetch/mod.rs} (55%) create mode 100644 audio/src/fetch/receive.rs diff --git a/audio/src/fetch.rs b/audio/src/fetch/mod.rs similarity index 55% rename from audio/src/fetch.rs rename to audio/src/fetch/mod.rs index da34309..c19fac2 100644 --- a/audio/src/fetch.rs +++ b/audio/src/fetch/mod.rs @@ -1,19 +1,21 @@ +mod receive; + use std::cmp::{max, min}; use std::fs; -use std::io::{self, Read, Seek, SeekFrom, Write}; +use std::io::{self, Read, Seek, SeekFrom}; use std::sync::atomic::{self, AtomicUsize}; use std::sync::{Arc, Condvar, Mutex}; use std::time::{Duration, Instant}; -use byteorder::{BigEndian, ByteOrder, WriteBytesExt}; -use bytes::Bytes; +use byteorder::{BigEndian, ByteOrder}; use futures_util::{future, StreamExt, TryFutureExt, TryStreamExt}; -use librespot_core::channel::{Channel, ChannelData, ChannelError, ChannelHeaders}; +use librespot_core::channel::{ChannelData, ChannelError, ChannelHeaders}; use librespot_core::session::Session; use librespot_core::spotify_id::FileId; use tempfile::NamedTempFile; use tokio::sync::{mpsc, oneshot}; +use self::receive::{audio_file_fetch, request_range}; use crate::range_set::{Range, RangeSet}; const MINIMUM_DOWNLOAD_SIZE: usize = 1024 * 16; @@ -388,440 +390,6 @@ impl AudioFileStreaming { } } -fn request_range(session: &Session, file: FileId, offset: usize, length: usize) -> Channel { - assert!( - offset % 4 == 0, - "Range request start positions must be aligned by 4 bytes." - ); - assert!( - length % 4 == 0, - "Range request range lengths must be aligned by 4 bytes." - ); - let start = offset / 4; - let end = (offset + length) / 4; - - let (id, channel) = session.channel().allocate(); - - let mut data: Vec = Vec::new(); - data.write_u16::(id).unwrap(); - data.write_u8(0).unwrap(); - data.write_u8(1).unwrap(); - data.write_u16::(0x0000).unwrap(); - data.write_u32::(0x00000000).unwrap(); - data.write_u32::(0x00009C40).unwrap(); - data.write_u32::(0x00020000).unwrap(); - data.write(&file.0).unwrap(); - data.write_u32::(start as u32).unwrap(); - data.write_u32::(end as u32).unwrap(); - - session.send_packet(0x8, data); - - channel -} - -struct PartialFileData { - offset: usize, - data: Bytes, -} - -enum ReceivedData { - ResponseTimeMs(usize), - Data(PartialFileData), -} - -async fn audio_file_fetch_receive_data( - shared: Arc, - file_data_tx: mpsc::UnboundedSender, - mut data_rx: ChannelData, - initial_data_offset: usize, - initial_request_length: usize, - request_sent_time: Instant, - mut measure_ping_time: bool, - finish_tx: mpsc::UnboundedSender<()>, -) { - let mut data_offset = initial_data_offset; - let mut request_length = initial_request_length; - - let result = loop { - let data = match data_rx.next().await { - Some(Ok(data)) => data, - Some(Err(e)) => break Err(e), - None => break Ok(()), - }; - - if measure_ping_time { - let duration = Instant::now() - request_sent_time; - let duration_ms: u64; - if 0.001 * (duration.as_millis() as f64) > MAXIMUM_ASSUMED_PING_TIME_SECONDS { - duration_ms = (MAXIMUM_ASSUMED_PING_TIME_SECONDS * 1000.0) as u64; - } else { - duration_ms = duration.as_millis() as u64; - } - let _ = file_data_tx.send(ReceivedData::ResponseTimeMs(duration_ms as usize)); - measure_ping_time = false; - } - let data_size = data.len(); - let _ = file_data_tx.send(ReceivedData::Data(PartialFileData { - offset: data_offset, - data, - })); - data_offset += data_size; - if request_length < data_size { - warn!( - "Data receiver for range {} (+{}) received more data from server than requested.", - initial_data_offset, initial_request_length - ); - request_length = 0; - } else { - request_length -= data_size; - } - - if request_length == 0 { - break Ok(()); - } - }; - - if request_length > 0 { - let missing_range = Range::new(data_offset, request_length); - - let mut download_status = shared.download_status.lock().unwrap(); - download_status.requested.subtract_range(&missing_range); - shared.cond.notify_all(); - } - - let _ = finish_tx.send(()); - - if result.is_err() { - warn!( - "Error from channel for data receiver for range {} (+{}).", - initial_data_offset, initial_request_length - ); - } else if request_length > 0 { - warn!( - "Data receiver for range {} (+{}) received less data from server than requested.", - initial_data_offset, initial_request_length - ); - } -} - -struct AudioFileFetch { - session: Session, - shared: Arc, - output: Option, - - file_data_tx: mpsc::UnboundedSender, - complete_tx: Option>, - network_response_times_ms: Vec, - number_of_open_requests: usize, - - download_finish_tx: mpsc::UnboundedSender<()>, -} - -// Might be replaced by enum from std once stable -#[derive(PartialEq, Eq)] -enum ControlFlow { - Break, - Continue, -} - -impl AudioFileFetch { - fn get_download_strategy(&mut self) -> DownloadStrategy { - *(self.shared.download_strategy.lock().unwrap()) - } - - fn download_range(&mut self, mut offset: usize, mut length: usize) { - if length < MINIMUM_DOWNLOAD_SIZE { - length = MINIMUM_DOWNLOAD_SIZE; - } - - // ensure the values are within the bounds and align them by 4 for the spotify protocol. - if offset >= self.shared.file_size { - return; - } - - if length == 0 { - return; - } - - if offset + length > self.shared.file_size { - length = self.shared.file_size - offset; - } - - if offset % 4 != 0 { - length += offset % 4; - offset -= offset % 4; - } - - if length % 4 != 0 { - length += 4 - (length % 4); - } - - let mut ranges_to_request = RangeSet::new(); - ranges_to_request.add_range(&Range::new(offset, length)); - - let mut download_status = self.shared.download_status.lock().unwrap(); - - ranges_to_request.subtract_range_set(&download_status.downloaded); - ranges_to_request.subtract_range_set(&download_status.requested); - - for range in ranges_to_request.iter() { - let (_headers, data) = request_range( - &self.session, - self.shared.file_id, - range.start, - range.length, - ) - .split(); - - download_status.requested.add_range(range); - - self.session.spawn(audio_file_fetch_receive_data( - self.shared.clone(), - self.file_data_tx.clone(), - data, - range.start, - range.length, - Instant::now(), - self.number_of_open_requests == 0, - self.download_finish_tx.clone(), - )); - - self.number_of_open_requests += 1; - } - } - - fn pre_fetch_more_data(&mut self, bytes: usize, max_requests_to_send: usize) { - let mut bytes_to_go = bytes; - let mut requests_to_go = max_requests_to_send; - - while bytes_to_go > 0 && requests_to_go > 0 { - // determine what is still missing - let mut missing_data = RangeSet::new(); - missing_data.add_range(&Range::new(0, self.shared.file_size)); - { - let download_status = self.shared.download_status.lock().unwrap(); - missing_data.subtract_range_set(&download_status.downloaded); - missing_data.subtract_range_set(&download_status.requested); - } - - // download data from after the current read position first - let mut tail_end = RangeSet::new(); - let read_position = self.shared.read_position.load(atomic::Ordering::Relaxed); - tail_end.add_range(&Range::new( - read_position, - self.shared.file_size - read_position, - )); - let tail_end = tail_end.intersection(&missing_data); - - if !tail_end.is_empty() { - let range = tail_end.get_range(0); - let offset = range.start; - let length = min(range.length, bytes_to_go); - self.download_range(offset, length); - requests_to_go -= 1; - bytes_to_go -= length; - } else if !missing_data.is_empty() { - // ok, the tail is downloaded, download something fom the beginning. - let range = missing_data.get_range(0); - let offset = range.start; - let length = min(range.length, bytes_to_go); - self.download_range(offset, length); - requests_to_go -= 1; - bytes_to_go -= length; - } else { - return; - } - } - } - - fn handle_file_data(&mut self, data: ReceivedData) -> ControlFlow { - match data { - ReceivedData::ResponseTimeMs(response_time_ms) => { - trace!("Ping time estimated as: {} ms.", response_time_ms); - - // record the response time - self.network_response_times_ms.push(response_time_ms); - - // prune old response times. Keep at most three. - while self.network_response_times_ms.len() > 3 { - self.network_response_times_ms.remove(0); - } - - // stats::median is experimental. So we calculate the median of up to three ourselves. - let ping_time_ms: usize = match self.network_response_times_ms.len() { - 1 => self.network_response_times_ms[0] as usize, - 2 => { - ((self.network_response_times_ms[0] + self.network_response_times_ms[1]) - / 2) as usize - } - 3 => { - let mut times = self.network_response_times_ms.clone(); - times.sort_unstable(); - times[1] - } - _ => unreachable!(), - }; - - // store our new estimate for everyone to see - self.shared - .ping_time_ms - .store(ping_time_ms, atomic::Ordering::Relaxed); - } - ReceivedData::Data(data) => { - self.output - .as_mut() - .unwrap() - .seek(SeekFrom::Start(data.offset as u64)) - .unwrap(); - self.output - .as_mut() - .unwrap() - .write_all(data.data.as_ref()) - .unwrap(); - - let mut download_status = self.shared.download_status.lock().unwrap(); - - let received_range = Range::new(data.offset, data.data.len()); - download_status.downloaded.add_range(&received_range); - self.shared.cond.notify_all(); - - let full = download_status.downloaded.contained_length_from_value(0) - >= self.shared.file_size; - - drop(download_status); - - if full { - self.finish(); - return ControlFlow::Break; - } - } - } - ControlFlow::Continue - } - - fn handle_stream_loader_command(&mut self, cmd: StreamLoaderCommand) -> ControlFlow { - match cmd { - StreamLoaderCommand::Fetch(request) => { - self.download_range(request.start, request.length); - } - StreamLoaderCommand::RandomAccessMode() => { - *(self.shared.download_strategy.lock().unwrap()) = DownloadStrategy::RandomAccess(); - } - StreamLoaderCommand::StreamMode() => { - *(self.shared.download_strategy.lock().unwrap()) = DownloadStrategy::Streaming(); - self.trigger_preload(); - } - StreamLoaderCommand::Close() => return ControlFlow::Break, - } - ControlFlow::Continue - } - - fn finish(&mut self) { - let mut output = self.output.take().unwrap(); - let complete_tx = self.complete_tx.take().unwrap(); - - output.seek(SeekFrom::Start(0)).unwrap(); - let _ = complete_tx.send(output); - } - - fn trigger_preload(&mut self) { - if self.number_of_open_requests >= MAX_PREFETCH_REQUESTS { - return; - } - - let max_requests_to_send = MAX_PREFETCH_REQUESTS - self.number_of_open_requests; - - let bytes_pending: usize = { - let download_status = self.shared.download_status.lock().unwrap(); - download_status - .requested - .minus(&download_status.downloaded) - .len() - }; - - let ping_time_seconds = - 0.001 * self.shared.ping_time_ms.load(atomic::Ordering::Relaxed) as f64; - let download_rate = self.session.channel().get_download_rate_estimate(); - - let desired_pending_bytes = max( - (PREFETCH_THRESHOLD_FACTOR * ping_time_seconds * self.shared.stream_data_rate as f64) - as usize, - (FAST_PREFETCH_THRESHOLD_FACTOR * ping_time_seconds * download_rate as f64) as usize, - ); - - if bytes_pending < desired_pending_bytes { - self.pre_fetch_more_data(desired_pending_bytes - bytes_pending, max_requests_to_send); - } - } -} - -async fn audio_file_fetch( - session: Session, - shared: Arc, - initial_data_rx: ChannelData, - initial_request_sent_time: Instant, - initial_data_length: usize, - - output: NamedTempFile, - mut stream_loader_command_rx: mpsc::UnboundedReceiver, - complete_tx: oneshot::Sender, -) { - let (file_data_tx, mut file_data_rx) = mpsc::unbounded_channel(); - let (download_finish_tx, mut download_finish_rx) = mpsc::unbounded_channel(); - - { - let requested_range = Range::new(0, initial_data_length); - let mut download_status = shared.download_status.lock().unwrap(); - download_status.requested.add_range(&requested_range); - } - - session.spawn(audio_file_fetch_receive_data( - shared.clone(), - file_data_tx.clone(), - initial_data_rx, - 0, - initial_data_length, - initial_request_sent_time, - true, - download_finish_tx.clone(), - )); - - let mut fetch = AudioFileFetch { - session, - shared, - output: Some(output), - - file_data_tx, - complete_tx: Some(complete_tx), - network_response_times_ms: Vec::new(), - number_of_open_requests: 1, - - download_finish_tx, - }; - - loop { - tokio::select! { - cmd = stream_loader_command_rx.recv() => { - if cmd.map_or(true, |cmd| fetch.handle_stream_loader_command(cmd) == ControlFlow::Break) { - break; - } - }, - data = file_data_rx.recv() => { - if data.map_or(true, |data| fetch.handle_file_data(data) == ControlFlow::Break) { - break; - } - }, - _ = download_finish_rx.recv() => { - fetch.number_of_open_requests -= 1; - - if fetch.get_download_strategy() == DownloadStrategy::Streaming() { - fetch.trigger_preload(); - } - } - } - } -} - impl Read for AudioFileStreaming { fn read(&mut self, output: &mut [u8]) -> io::Result { let offset = self.position as usize; diff --git a/audio/src/fetch/receive.rs b/audio/src/fetch/receive.rs new file mode 100644 index 0000000..17f884f --- /dev/null +++ b/audio/src/fetch/receive.rs @@ -0,0 +1,455 @@ +use std::cmp::{max, min}; +use std::io::{Seek, SeekFrom, Write}; +use std::sync::{atomic, Arc}; +use std::time::Instant; + +use byteorder::{BigEndian, WriteBytesExt}; +use bytes::Bytes; +use futures_util::StreamExt; +use librespot_core::channel::{Channel, ChannelData}; +use librespot_core::session::Session; +use librespot_core::spotify_id::FileId; +use tempfile::NamedTempFile; +use tokio::sync::{mpsc, oneshot}; + +use crate::range_set::{Range, RangeSet}; + +use super::{AudioFileShared, DownloadStrategy, StreamLoaderCommand}; +use super::{ + FAST_PREFETCH_THRESHOLD_FACTOR, MAXIMUM_ASSUMED_PING_TIME_SECONDS, MAX_PREFETCH_REQUESTS, + MINIMUM_DOWNLOAD_SIZE, PREFETCH_THRESHOLD_FACTOR, +}; + +pub fn request_range(session: &Session, file: FileId, offset: usize, length: usize) -> Channel { + assert!( + offset % 4 == 0, + "Range request start positions must be aligned by 4 bytes." + ); + assert!( + length % 4 == 0, + "Range request range lengths must be aligned by 4 bytes." + ); + let start = offset / 4; + let end = (offset + length) / 4; + + let (id, channel) = session.channel().allocate(); + + let mut data: Vec = Vec::new(); + data.write_u16::(id).unwrap(); + data.write_u8(0).unwrap(); + data.write_u8(1).unwrap(); + data.write_u16::(0x0000).unwrap(); + data.write_u32::(0x00000000).unwrap(); + data.write_u32::(0x00009C40).unwrap(); + data.write_u32::(0x00020000).unwrap(); + data.write(&file.0).unwrap(); + data.write_u32::(start as u32).unwrap(); + data.write_u32::(end as u32).unwrap(); + + session.send_packet(0x8, data); + + channel +} + +struct PartialFileData { + offset: usize, + data: Bytes, +} + +enum ReceivedData { + ResponseTimeMs(usize), + Data(PartialFileData), +} + +async fn receive_data( + shared: Arc, + file_data_tx: mpsc::UnboundedSender, + mut data_rx: ChannelData, + initial_data_offset: usize, + initial_request_length: usize, + request_sent_time: Instant, + mut measure_ping_time: bool, + finish_tx: mpsc::UnboundedSender<()>, +) { + let mut data_offset = initial_data_offset; + let mut request_length = initial_request_length; + + let result = loop { + let data = match data_rx.next().await { + Some(Ok(data)) => data, + Some(Err(e)) => break Err(e), + None => break Ok(()), + }; + + if measure_ping_time { + let duration = Instant::now() - request_sent_time; + let duration_ms: u64; + if 0.001 * (duration.as_millis() as f64) > MAXIMUM_ASSUMED_PING_TIME_SECONDS { + duration_ms = (MAXIMUM_ASSUMED_PING_TIME_SECONDS * 1000.0) as u64; + } else { + duration_ms = duration.as_millis() as u64; + } + let _ = file_data_tx.send(ReceivedData::ResponseTimeMs(duration_ms as usize)); + measure_ping_time = false; + } + let data_size = data.len(); + let _ = file_data_tx.send(ReceivedData::Data(PartialFileData { + offset: data_offset, + data, + })); + data_offset += data_size; + if request_length < data_size { + warn!( + "Data receiver for range {} (+{}) received more data from server than requested.", + initial_data_offset, initial_request_length + ); + request_length = 0; + } else { + request_length -= data_size; + } + + if request_length == 0 { + break Ok(()); + } + }; + + if request_length > 0 { + let missing_range = Range::new(data_offset, request_length); + + let mut download_status = shared.download_status.lock().unwrap(); + download_status.requested.subtract_range(&missing_range); + shared.cond.notify_all(); + } + + let _ = finish_tx.send(()); + + if result.is_err() { + warn!( + "Error from channel for data receiver for range {} (+{}).", + initial_data_offset, initial_request_length + ); + } else if request_length > 0 { + warn!( + "Data receiver for range {} (+{}) received less data from server than requested.", + initial_data_offset, initial_request_length + ); + } +} + +struct AudioFileFetch { + session: Session, + shared: Arc, + output: Option, + + file_data_tx: mpsc::UnboundedSender, + complete_tx: Option>, + network_response_times_ms: Vec, + number_of_open_requests: usize, + + download_finish_tx: mpsc::UnboundedSender<()>, +} + +// Might be replaced by enum from std once stable +#[derive(PartialEq, Eq)] +enum ControlFlow { + Break, + Continue, +} + +impl AudioFileFetch { + fn get_download_strategy(&mut self) -> DownloadStrategy { + *(self.shared.download_strategy.lock().unwrap()) + } + + fn download_range(&mut self, mut offset: usize, mut length: usize) { + if length < MINIMUM_DOWNLOAD_SIZE { + length = MINIMUM_DOWNLOAD_SIZE; + } + + // ensure the values are within the bounds and align them by 4 for the spotify protocol. + if offset >= self.shared.file_size { + return; + } + + if length == 0 { + return; + } + + if offset + length > self.shared.file_size { + length = self.shared.file_size - offset; + } + + if offset % 4 != 0 { + length += offset % 4; + offset -= offset % 4; + } + + if length % 4 != 0 { + length += 4 - (length % 4); + } + + let mut ranges_to_request = RangeSet::new(); + ranges_to_request.add_range(&Range::new(offset, length)); + + let mut download_status = self.shared.download_status.lock().unwrap(); + + ranges_to_request.subtract_range_set(&download_status.downloaded); + ranges_to_request.subtract_range_set(&download_status.requested); + + for range in ranges_to_request.iter() { + let (_headers, data) = request_range( + &self.session, + self.shared.file_id, + range.start, + range.length, + ) + .split(); + + download_status.requested.add_range(range); + + self.session.spawn(receive_data( + self.shared.clone(), + self.file_data_tx.clone(), + data, + range.start, + range.length, + Instant::now(), + self.number_of_open_requests == 0, + self.download_finish_tx.clone(), + )); + + self.number_of_open_requests += 1; + } + } + + fn pre_fetch_more_data(&mut self, bytes: usize, max_requests_to_send: usize) { + let mut bytes_to_go = bytes; + let mut requests_to_go = max_requests_to_send; + + while bytes_to_go > 0 && requests_to_go > 0 { + // determine what is still missing + let mut missing_data = RangeSet::new(); + missing_data.add_range(&Range::new(0, self.shared.file_size)); + { + let download_status = self.shared.download_status.lock().unwrap(); + missing_data.subtract_range_set(&download_status.downloaded); + missing_data.subtract_range_set(&download_status.requested); + } + + // download data from after the current read position first + let mut tail_end = RangeSet::new(); + let read_position = self.shared.read_position.load(atomic::Ordering::Relaxed); + tail_end.add_range(&Range::new( + read_position, + self.shared.file_size - read_position, + )); + let tail_end = tail_end.intersection(&missing_data); + + if !tail_end.is_empty() { + let range = tail_end.get_range(0); + let offset = range.start; + let length = min(range.length, bytes_to_go); + self.download_range(offset, length); + requests_to_go -= 1; + bytes_to_go -= length; + } else if !missing_data.is_empty() { + // ok, the tail is downloaded, download something fom the beginning. + let range = missing_data.get_range(0); + let offset = range.start; + let length = min(range.length, bytes_to_go); + self.download_range(offset, length); + requests_to_go -= 1; + bytes_to_go -= length; + } else { + return; + } + } + } + + fn handle_file_data(&mut self, data: ReceivedData) -> ControlFlow { + match data { + ReceivedData::ResponseTimeMs(response_time_ms) => { + trace!("Ping time estimated as: {} ms.", response_time_ms); + + // record the response time + self.network_response_times_ms.push(response_time_ms); + + // prune old response times. Keep at most three. + while self.network_response_times_ms.len() > 3 { + self.network_response_times_ms.remove(0); + } + + // stats::median is experimental. So we calculate the median of up to three ourselves. + let ping_time_ms: usize = match self.network_response_times_ms.len() { + 1 => self.network_response_times_ms[0] as usize, + 2 => { + ((self.network_response_times_ms[0] + self.network_response_times_ms[1]) + / 2) as usize + } + 3 => { + let mut times = self.network_response_times_ms.clone(); + times.sort_unstable(); + times[1] + } + _ => unreachable!(), + }; + + // store our new estimate for everyone to see + self.shared + .ping_time_ms + .store(ping_time_ms, atomic::Ordering::Relaxed); + } + ReceivedData::Data(data) => { + self.output + .as_mut() + .unwrap() + .seek(SeekFrom::Start(data.offset as u64)) + .unwrap(); + self.output + .as_mut() + .unwrap() + .write_all(data.data.as_ref()) + .unwrap(); + + let mut download_status = self.shared.download_status.lock().unwrap(); + + let received_range = Range::new(data.offset, data.data.len()); + download_status.downloaded.add_range(&received_range); + self.shared.cond.notify_all(); + + let full = download_status.downloaded.contained_length_from_value(0) + >= self.shared.file_size; + + drop(download_status); + + if full { + self.finish(); + return ControlFlow::Break; + } + } + } + ControlFlow::Continue + } + + fn handle_stream_loader_command(&mut self, cmd: StreamLoaderCommand) -> ControlFlow { + match cmd { + StreamLoaderCommand::Fetch(request) => { + self.download_range(request.start, request.length); + } + StreamLoaderCommand::RandomAccessMode() => { + *(self.shared.download_strategy.lock().unwrap()) = DownloadStrategy::RandomAccess(); + } + StreamLoaderCommand::StreamMode() => { + *(self.shared.download_strategy.lock().unwrap()) = DownloadStrategy::Streaming(); + self.trigger_preload(); + } + StreamLoaderCommand::Close() => return ControlFlow::Break, + } + ControlFlow::Continue + } + + fn finish(&mut self) { + let mut output = self.output.take().unwrap(); + let complete_tx = self.complete_tx.take().unwrap(); + + output.seek(SeekFrom::Start(0)).unwrap(); + let _ = complete_tx.send(output); + } + + fn trigger_preload(&mut self) { + if self.number_of_open_requests >= MAX_PREFETCH_REQUESTS { + return; + } + + let max_requests_to_send = MAX_PREFETCH_REQUESTS - self.number_of_open_requests; + + let bytes_pending: usize = { + let download_status = self.shared.download_status.lock().unwrap(); + download_status + .requested + .minus(&download_status.downloaded) + .len() + }; + + let ping_time_seconds = + 0.001 * self.shared.ping_time_ms.load(atomic::Ordering::Relaxed) as f64; + let download_rate = self.session.channel().get_download_rate_estimate(); + + let desired_pending_bytes = max( + (PREFETCH_THRESHOLD_FACTOR * ping_time_seconds * self.shared.stream_data_rate as f64) + as usize, + (FAST_PREFETCH_THRESHOLD_FACTOR * ping_time_seconds * download_rate as f64) as usize, + ); + + if bytes_pending < desired_pending_bytes { + self.pre_fetch_more_data(desired_pending_bytes - bytes_pending, max_requests_to_send); + } + } +} + +pub(super) async fn audio_file_fetch( + session: Session, + shared: Arc, + initial_data_rx: ChannelData, + initial_request_sent_time: Instant, + initial_data_length: usize, + + output: NamedTempFile, + mut stream_loader_command_rx: mpsc::UnboundedReceiver, + complete_tx: oneshot::Sender, +) { + let (file_data_tx, mut file_data_rx) = mpsc::unbounded_channel(); + let (download_finish_tx, mut download_finish_rx) = mpsc::unbounded_channel(); + + { + let requested_range = Range::new(0, initial_data_length); + let mut download_status = shared.download_status.lock().unwrap(); + download_status.requested.add_range(&requested_range); + } + + session.spawn(receive_data( + shared.clone(), + file_data_tx.clone(), + initial_data_rx, + 0, + initial_data_length, + initial_request_sent_time, + true, + download_finish_tx.clone(), + )); + + let mut fetch = AudioFileFetch { + session, + shared, + output: Some(output), + + file_data_tx, + complete_tx: Some(complete_tx), + network_response_times_ms: Vec::new(), + number_of_open_requests: 1, + + download_finish_tx, + }; + + loop { + tokio::select! { + cmd = stream_loader_command_rx.recv() => { + if cmd.map_or(true, |cmd| fetch.handle_stream_loader_command(cmd) == ControlFlow::Break) { + break; + } + }, + data = file_data_rx.recv() => { + if data.map_or(true, |data| fetch.handle_file_data(data) == ControlFlow::Break) { + break; + } + }, + _ = download_finish_rx.recv() => { + fetch.number_of_open_requests -= 1; + + if fetch.get_download_strategy() == DownloadStrategy::Streaming() { + fetch.trigger_preload(); + } + } + } + } +} From f29e5212c402074c1a12eb493af7e5d4c966dcdf Mon Sep 17 00:00:00 2001 From: Roderick van Domburg Date: Wed, 24 Feb 2021 21:39:42 +0100 Subject: [PATCH 060/103] High-resolution volume control and normalisation - Store and output samples as 32-bit floats instead of 16-bit integers. This provides 24-25 bits of transparency, allowing for 42-48 dB of headroom to do volume control and normalisation without throwing away bits or dropping dynamic range below 96 dB CD quality. - Perform volume control and normalisation in 64-bit arithmetic. - Add a dynamic limiter with configurable threshold, attack time, release or decay time, and steepness for the sigmoid transfer function. This mimics the native Spotify limiter, offering greater dynamic range than the old limiter, that just reduced overall gain to prevent clipping. - Make the configurable threshold also apply to the old limiter, which is still available. Resolves: librespot-org/librespot#608 --- audio/src/lewton_decoder.rs | 7 +- audio/src/lib.rs | 4 +- audio/src/libvorbis_decoder.rs | 11 +- playback/src/audio_backend/alsa.rs | 20 +-- playback/src/audio_backend/gstreamer.rs | 2 +- playback/src/audio_backend/jackaudio.rs | 14 +- playback/src/audio_backend/pipe.rs | 2 +- playback/src/audio_backend/portaudio.rs | 6 +- playback/src/audio_backend/pulseaudio.rs | 11 +- playback/src/audio_backend/rodio.rs | 2 +- playback/src/audio_backend/sdl.rs | 4 +- playback/src/audio_backend/subprocess.rs | 2 +- playback/src/config.rs | 33 ++++ playback/src/mixer/mod.rs | 2 +- playback/src/mixer/softmixer.rs | 5 +- playback/src/player.rs | 183 +++++++++++++++++++++-- src/main.rs | 72 ++++++++- 17 files changed, 327 insertions(+), 53 deletions(-) diff --git a/audio/src/lewton_decoder.rs b/audio/src/lewton_decoder.rs index 1addaa0..8e7d089 100644 --- a/audio/src/lewton_decoder.rs +++ b/audio/src/lewton_decoder.rs @@ -37,8 +37,11 @@ where use self::lewton::VorbisError::BadAudio; use self::lewton::VorbisError::OggError; loop { - match self.0.read_dec_packet_itl() { - Ok(Some(packet)) => return Ok(Some(AudioPacket::Samples(packet))), + match self + .0 + .read_dec_packet_generic::>() + { + Ok(Some(packet)) => return Ok(Some(AudioPacket::Samples(packet.samples))), Ok(None) => return Ok(None), Err(BadAudio(AudioIsHeader)) => (), diff --git a/audio/src/lib.rs b/audio/src/lib.rs index fd76407..c4d862b 100644 --- a/audio/src/lib.rs +++ b/audio/src/lib.rs @@ -33,12 +33,12 @@ pub use fetch::{ use std::fmt; pub enum AudioPacket { - Samples(Vec), + Samples(Vec), OggData(Vec), } impl AudioPacket { - pub fn samples(&self) -> &[i16] { + pub fn samples(&self) -> &[f32] { match self { AudioPacket::Samples(s) => s, AudioPacket::OggData(_) => panic!("can't return OggData on samples"), diff --git a/audio/src/libvorbis_decoder.rs b/audio/src/libvorbis_decoder.rs index 8aced55..e7ccc98 100644 --- a/audio/src/libvorbis_decoder.rs +++ b/audio/src/libvorbis_decoder.rs @@ -39,7 +39,16 @@ where fn next_packet(&mut self) -> Result, AudioError> { loop { match self.0.packets().next() { - Some(Ok(packet)) => return Ok(Some(AudioPacket::Samples(packet.data))), + Some(Ok(packet)) => { + // Losslessly represent [-32768, 32767] to [-1.0, 1.0] while maintaining DC linearity. + return Ok(Some(AudioPacket::Samples( + packet + .data + .iter() + .map(|sample| ((*sample as f64 + 0.5) / (0x7FFF as f64 + 0.5)) as f32) + .collect(), + ))); + } None => return Ok(None), Some(Err(vorbis::VorbisError::Hole)) => (), diff --git a/playback/src/audio_backend/alsa.rs b/playback/src/audio_backend/alsa.rs index bf7b137..9bc17fe 100644 --- a/playback/src/audio_backend/alsa.rs +++ b/playback/src/audio_backend/alsa.rs @@ -8,13 +8,13 @@ use std::ffi::CString; use std::io; use std::process::exit; -const PREFERED_PERIOD_SIZE: Frames = 5512; // Period of roughly 125ms +const PREFERRED_PERIOD_SIZE: Frames = 11025; // Period of roughly 125ms const BUFFERED_PERIODS: Frames = 4; pub struct AlsaSink { pcm: Option, device: String, - buffer: Vec, + buffer: Vec, } fn list_outputs() { @@ -36,19 +36,19 @@ fn list_outputs() { fn open_device(dev_name: &str) -> Result<(PCM, Frames), Box> { let pcm = PCM::new(dev_name, Direction::Playback, false)?; - let mut period_size = PREFERED_PERIOD_SIZE; + let mut period_size = PREFERRED_PERIOD_SIZE; // http://www.linuxjournal.com/article/6735?page=0,1#N0x19ab2890.0x19ba78d8 // latency = period_size * periods / (rate * bytes_per_frame) - // For 16 Bit stereo data, one frame has a length of four bytes. - // 500ms = buffer_size / (44100 * 4) - // buffer_size_bytes = 0.5 * 44100 / 4 + // For stereo samples encoded as 32-bit floats, one frame has a length of eight bytes. + // 500ms = buffer_size / (44100 * 8) + // buffer_size_bytes = 0.5 * 44100 / 8 // buffer_size_frames = 0.5 * 44100 = 22050 { - // Set hardware parameters: 44100 Hz / Stereo / 16 bit + // Set hardware parameters: 44100 Hz / Stereo / 32-bit float let hwp = HwParams::any(&pcm)?; hwp.set_access(Access::RWInterleaved)?; - hwp.set_format(Format::s16())?; + hwp.set_format(Format::float())?; hwp.set_rate(44100, ValueOr::Nearest)?; hwp.set_channels(2)?; period_size = hwp.set_period_size_near(period_size, ValueOr::Greater)?; @@ -114,7 +114,7 @@ impl Sink for AlsaSink { let pcm = self.pcm.as_mut().unwrap(); // Write any leftover data in the period buffer // before draining the actual buffer - let io = pcm.io_i16().unwrap(); + let io = pcm.io_f32().unwrap(); match io.writei(&self.buffer[..]) { Ok(_) => (), Err(err) => pcm.try_recover(err, false).unwrap(), @@ -138,7 +138,7 @@ impl Sink for AlsaSink { processed_data += data_to_buffer; if self.buffer.len() == self.buffer.capacity() { let pcm = self.pcm.as_mut().unwrap(); - let io = pcm.io_i16().unwrap(); + let io = pcm.io_f32().unwrap(); match io.writei(&self.buffer) { Ok(_) => (), Err(err) => pcm.try_recover(err, false).unwrap(), diff --git a/playback/src/audio_backend/gstreamer.rs b/playback/src/audio_backend/gstreamer.rs index 6be6dd7..1ad3631 100644 --- a/playback/src/audio_backend/gstreamer.rs +++ b/playback/src/audio_backend/gstreamer.rs @@ -15,7 +15,7 @@ pub struct GstreamerSink { impl Open for GstreamerSink { fn open(device: Option) -> GstreamerSink { gst::init().expect("Failed to init gstreamer!"); - let pipeline_str_preamble = r#"appsrc caps="audio/x-raw,format=S16LE,layout=interleaved,channels=2,rate=44100" block=true max-bytes=4096 name=appsrc0 "#; + let pipeline_str_preamble = r#"appsrc caps="audio/x-raw,format=F32,layout=interleaved,channels=2,rate=44100" block=true max-bytes=4096 name=appsrc0 "#; let pipeline_str_rest = r#" ! audioconvert ! autoaudiosink"#; let pipeline_str: String = match device { Some(x) => format!("{}{}", pipeline_str_preamble, x), diff --git a/playback/src/audio_backend/jackaudio.rs b/playback/src/audio_backend/jackaudio.rs index 4699c18..e95933f 100644 --- a/playback/src/audio_backend/jackaudio.rs +++ b/playback/src/audio_backend/jackaudio.rs @@ -7,20 +7,18 @@ use std::io; use std::sync::mpsc::{sync_channel, Receiver, SyncSender}; pub struct JackSink { - send: SyncSender, + send: SyncSender, + // We have to keep hold of this object, or the Sink can't play... + #[allow(dead_code)] active_client: AsyncClient<(), JackData>, } pub struct JackData { - rec: Receiver, + rec: Receiver, port_l: Port, port_r: Port, } -fn pcm_to_f32(sample: i16) -> f32 { - sample as f32 / 32768.0 -} - impl ProcessHandler for JackData { fn process(&mut self, _: &Client, ps: &ProcessScope) -> Control { // get output port buffers @@ -33,8 +31,8 @@ impl ProcessHandler for JackData { let buf_size = buf_r.len(); for i in 0..buf_size { - buf_r[i] = pcm_to_f32(queue_iter.next().unwrap_or(0)); - buf_l[i] = pcm_to_f32(queue_iter.next().unwrap_or(0)); + buf_r[i] = queue_iter.next().unwrap_or(0.0); + buf_l[i] = queue_iter.next().unwrap_or(0.0); } Control::Continue } diff --git a/playback/src/audio_backend/pipe.rs b/playback/src/audio_backend/pipe.rs index 210c0ce..5516ee9 100644 --- a/playback/src/audio_backend/pipe.rs +++ b/playback/src/audio_backend/pipe.rs @@ -32,7 +32,7 @@ impl Sink for StdoutSink { AudioPacket::Samples(data) => unsafe { slice::from_raw_parts( data.as_ptr() as *const u8, - data.len() * mem::size_of::(), + data.len() * mem::size_of::(), ) }, AudioPacket::OggData(data) => data, diff --git a/playback/src/audio_backend/portaudio.rs b/playback/src/audio_backend/portaudio.rs index 0e25021..0b8eac0 100644 --- a/playback/src/audio_backend/portaudio.rs +++ b/playback/src/audio_backend/portaudio.rs @@ -8,8 +8,8 @@ use std::process::exit; use std::time::Duration; pub struct PortAudioSink<'a>( - Option>, - StreamParameters, + Option>, + StreamParameters, ); fn output_devices() -> Box> { @@ -65,7 +65,7 @@ impl<'a> Open for PortAudioSink<'a> { device: device_idx, channel_count: 2, suggested_latency: latency, - data: 0i16, + data: 0.0, }; PortAudioSink(None, params) diff --git a/playback/src/audio_backend/pulseaudio.rs b/playback/src/audio_backend/pulseaudio.rs index 11ea026..4dca210 100644 --- a/playback/src/audio_backend/pulseaudio.rs +++ b/playback/src/audio_backend/pulseaudio.rs @@ -3,6 +3,7 @@ use crate::audio::AudioPacket; use libpulse_binding::{self as pulse, stream::Direction}; use libpulse_simple_binding::Simple; use std::io; +use std::mem; const APP_NAME: &str = "librespot"; const STREAM_NAME: &str = "Spotify endpoint"; @@ -18,7 +19,7 @@ impl Open for PulseAudioSink { debug!("Using PulseAudio sink"); let ss = pulse::sample::Spec { - format: pulse::sample::Format::S16le, + format: pulse::sample::Format::F32le, channels: 2, // stereo rate: 44100, }; @@ -68,13 +69,13 @@ impl Sink for PulseAudioSink { fn write(&mut self, packet: &AudioPacket) -> io::Result<()> { if let Some(s) = &self.s { - // SAFETY: An i16 consists of two bytes, so that the given slice can be interpreted - // as a byte array of double length. Each byte pointer is validly aligned, and so - // is the newly created slice. + // SAFETY: An f32 consists of four bytes, so that the given slice can be interpreted + // as a byte array of four. Each byte pointer is validly aligned, and so is the newly + // created slice. let d: &[u8] = unsafe { std::slice::from_raw_parts( packet.samples().as_ptr() as *const u8, - packet.samples().len() * 2, + packet.samples().len() * mem::size_of::(), ) }; diff --git a/playback/src/audio_backend/rodio.rs b/playback/src/audio_backend/rodio.rs index 3b920c3..6c996e8 100644 --- a/playback/src/audio_backend/rodio.rs +++ b/playback/src/audio_backend/rodio.rs @@ -198,7 +198,7 @@ impl Sink for JackRodioSink { Ok(()) } - fn write(&mut self, data: &[i16]) -> io::Result<()> { + fn write(&mut self, data: &[f32]) -> io::Result<()> { let source = rodio::buffer::SamplesBuffer::new(2, 44100, data); self.jackrodio_sink.append(source); diff --git a/playback/src/audio_backend/sdl.rs b/playback/src/audio_backend/sdl.rs index 27d650f..727615d 100644 --- a/playback/src/audio_backend/sdl.rs +++ b/playback/src/audio_backend/sdl.rs @@ -3,7 +3,7 @@ use crate::audio::AudioPacket; use sdl2::audio::{AudioQueue, AudioSpecDesired}; use std::{io, thread, time}; -type Channel = i16; +type Channel = f32; pub struct SdlSink { queue: AudioQueue, @@ -47,7 +47,7 @@ impl Sink for SdlSink { } fn write(&mut self, packet: &AudioPacket) -> io::Result<()> { - while self.queue.size() > (2 * 2 * 44_100) { + while self.queue.size() > (2 * 4 * 44_100) { // sleep and wait for sdl thread to drain the queue a bit thread::sleep(time::Duration::from_millis(10)); } diff --git a/playback/src/audio_backend/subprocess.rs b/playback/src/audio_backend/subprocess.rs index 0dd2563..123e023 100644 --- a/playback/src/audio_backend/subprocess.rs +++ b/playback/src/audio_backend/subprocess.rs @@ -48,7 +48,7 @@ impl Sink for SubprocessSink { let data: &[u8] = unsafe { slice::from_raw_parts( packet.samples().as_ptr() as *const u8, - packet.samples().len() * mem::size_of::(), + packet.samples().len() * mem::size_of::(), ) }; if let Some(child) = &mut self.child { diff --git a/playback/src/config.rs b/playback/src/config.rs index 31f6362..be15b26 100644 --- a/playback/src/config.rs +++ b/playback/src/config.rs @@ -48,12 +48,40 @@ impl Default for NormalisationType { } } +#[derive(Clone, Debug, PartialEq)] +pub enum NormalisationMethod { + Basic, + Dynamic, +} + +impl FromStr for NormalisationMethod { + type Err = (); + fn from_str(s: &str) -> Result { + match s { + "basic" => Ok(NormalisationMethod::Basic), + "dynamic" => Ok(NormalisationMethod::Dynamic), + _ => Err(()), + } + } +} + +impl Default for NormalisationMethod { + fn default() -> NormalisationMethod { + NormalisationMethod::Dynamic + } +} + #[derive(Clone, Debug)] pub struct PlayerConfig { pub bitrate: Bitrate, pub normalisation: bool, pub normalisation_type: NormalisationType, + pub normalisation_method: NormalisationMethod, pub normalisation_pregain: f32, + pub normalisation_threshold: f32, + pub normalisation_attack: f32, + pub normalisation_release: f32, + pub normalisation_steepness: f32, pub gapless: bool, pub passthrough: bool, } @@ -64,7 +92,12 @@ impl Default for PlayerConfig { bitrate: Bitrate::default(), normalisation: false, normalisation_type: NormalisationType::default(), + normalisation_method: NormalisationMethod::default(), normalisation_pregain: 0.0, + normalisation_threshold: -1.0, + normalisation_attack: 0.005, + normalisation_release: 0.1, + normalisation_steepness: 1.0, gapless: true, passthrough: false, } diff --git a/playback/src/mixer/mod.rs b/playback/src/mixer/mod.rs index 325c1e1..3424526 100644 --- a/playback/src/mixer/mod.rs +++ b/playback/src/mixer/mod.rs @@ -12,7 +12,7 @@ pub trait Mixer: Send { } pub trait AudioFilter { - fn modify_stream(&self, data: &mut [i16]); + fn modify_stream(&self, data: &mut [f32]); } #[cfg(feature = "alsa-backend")] diff --git a/playback/src/mixer/softmixer.rs b/playback/src/mixer/softmixer.rs index 28e1cf5..ec8ed6b 100644 --- a/playback/src/mixer/softmixer.rs +++ b/playback/src/mixer/softmixer.rs @@ -35,11 +35,12 @@ struct SoftVolumeApplier { } impl AudioFilter for SoftVolumeApplier { - fn modify_stream(&self, data: &mut [i16]) { + fn modify_stream(&self, data: &mut [f32]) { let volume = self.volume.load(Ordering::Relaxed) as u16; if volume != 0xFFFF { + let volume_factor = volume as f64 / 0xFFFF as f64; for x in data.iter_mut() { - *x = (*x as i32 * volume as i32 / 0xFFFF) as i16; + *x = (*x as f64 * volume_factor) as f32; } } } diff --git a/playback/src/player.rs b/playback/src/player.rs index 9b4eefb..c7edf3a 100644 --- a/playback/src/player.rs +++ b/playback/src/player.rs @@ -9,7 +9,7 @@ use std::mem; use std::thread; use std::time::{Duration, Instant}; -use crate::config::{Bitrate, NormalisationType, PlayerConfig}; +use crate::config::{Bitrate, NormalisationMethod, NormalisationType, PlayerConfig}; use librespot_core::session::Session; use librespot_core::spotify_id::SpotifyId; @@ -26,6 +26,7 @@ use crate::metadata::{AudioItem, FileFormat}; use crate::mixer::AudioFilter; const PRELOAD_NEXT_TRACK_BEFORE_END_DURATION_MS: u32 = 30000; +const SAMPLES_PER_SECOND: u32 = 44100 * 2; pub struct Player { commands: Option>, @@ -54,6 +55,13 @@ struct PlayerInternal { sink_event_callback: Option, audio_filter: Option>, event_senders: Vec>, + + limiter_active: bool, + limiter_attack_counter: u32, + limiter_release_counter: u32, + limiter_peak_sample: f32, + limiter_factor: f32, + limiter_strength: f32, } enum PlayerCommand { @@ -185,7 +193,7 @@ impl PlayerEvent { pub type PlayerEventChannel = futures::sync::mpsc::UnboundedReceiver; #[derive(Clone, Copy, Debug)] -struct NormalisationData { +pub struct NormalisationData { track_gain_db: f32, track_peak: f32, album_gain_db: f32, @@ -193,6 +201,14 @@ struct NormalisationData { } impl NormalisationData { + pub fn db_to_ratio(db: f32) -> f32 { + return f32::powf(10.0, db / 20.0); + } + + pub fn ratio_to_db(ratio: f32) -> f32 { + return ratio.log10() * 20.0; + } + fn parse_from_file(mut file: T) -> Result { const SPOTIFY_NORMALIZATION_HEADER_START_OFFSET: u64 = 144; file.seek(SeekFrom::Start(SPOTIFY_NORMALIZATION_HEADER_START_OFFSET)) @@ -218,17 +234,44 @@ impl NormalisationData { NormalisationType::Album => [data.album_gain_db, data.album_peak], NormalisationType::Track => [data.track_gain_db, data.track_peak], }; - let mut normalisation_factor = - f32::powf(10.0, (gain_db + config.normalisation_pregain) / 20.0); - if normalisation_factor * gain_peak > 1.0 { - warn!("Reducing normalisation factor to prevent clipping. Please add negative pregain to avoid."); - normalisation_factor = 1.0 / gain_peak; + let normalisation_power = gain_db + config.normalisation_pregain; + let mut normalisation_factor = Self::db_to_ratio(normalisation_power); + + if normalisation_factor * gain_peak > config.normalisation_threshold { + let limited_normalisation_factor = config.normalisation_threshold / gain_peak; + let limited_normalisation_power = Self::ratio_to_db(limited_normalisation_factor); + + if config.normalisation_method == NormalisationMethod::Basic { + warn!("Limiting gain to {:.2} for the duration of this track to stay under normalisation threshold.", limited_normalisation_power); + normalisation_factor = limited_normalisation_factor; + } else { + warn!( + "This track will at its peak be subject to {:.2} dB of dynamic limiting.", + normalisation_power - limited_normalisation_power + ); + } + + warn!("Please lower pregain to avoid."); } debug!("Normalisation Data: {:?}", data); debug!("Normalisation Type: {:?}", config.normalisation_type); - debug!("Applied normalisation factor: {}", normalisation_factor); + debug!( + "Normalisation Threshold: {:.1}", + Self::ratio_to_db(config.normalisation_threshold) + ); + debug!("Normalisation Method: {:?}", config.normalisation_method); + debug!("Normalisation Factor: {}", normalisation_factor); + + if config.normalisation_method == NormalisationMethod::Dynamic { + debug!("Normalisation Attack: {:?}", config.normalisation_attack); + debug!("Normalisation Release: {:?}", config.normalisation_release); + debug!( + "Normalisation Steepness: {:?}", + config.normalisation_steepness + ); + } normalisation_factor } @@ -262,6 +305,13 @@ impl Player { sink_event_callback: None, audio_filter: audio_filter, event_senders: [event_sender].to_vec(), + + limiter_active: false, + limiter_attack_counter: 0, + limiter_release_counter: 0, + limiter_peak_sample: 0.0, + limiter_factor: 1.0, + limiter_strength: 0.0, }; // While PlayerInternal is written as a future, it still contains blocking code. @@ -1113,9 +1163,111 @@ impl PlayerInternal { editor.modify_stream(data) } - if self.config.normalisation && normalisation_factor != 1.0 { - for x in data.iter_mut() { - *x = (*x as f32 * normalisation_factor) as i16; + if self.config.normalisation + && (normalisation_factor != 1.0 + || self.config.normalisation_method != NormalisationMethod::Basic) + { + for sample in data.iter_mut() { + let mut actual_normalisation_factor = normalisation_factor; + if self.config.normalisation_method == NormalisationMethod::Dynamic + { + if self.limiter_active { + // "S"-shaped curve with a configurable steepness during attack and release: + // - > 1.0 yields soft knees at start and end, steeper in between + // - 1.0 yields a linear function from 0-100% + // - between 0.0 and 1.0 yields hard knees at start and end, flatter in between + // - 0.0 yields a step response to 50%, causing distortion + // - Rates < 0.0 invert the limiter and are invalid + let mut shaped_limiter_strength = self.limiter_strength; + if shaped_limiter_strength > 0.0 + && shaped_limiter_strength < 1.0 + { + shaped_limiter_strength = 1.0 + / (1.0 + + f32::powf( + shaped_limiter_strength + / (1.0 - shaped_limiter_strength), + -1.0 * self.config.normalisation_steepness, + )); + } + actual_normalisation_factor = + (1.0 - shaped_limiter_strength) * normalisation_factor + + shaped_limiter_strength * self.limiter_factor; + }; + + // Always check for peaks, even when the limiter is already active. + // There may be even higher peaks than we initially targeted. + // Check against the normalisation factor that would be applied normally. + let abs_sample = + ((*sample as f64 * normalisation_factor as f64) as f32) + .abs(); + if abs_sample > self.config.normalisation_threshold { + self.limiter_active = true; + if self.limiter_release_counter > 0 { + // A peak was encountered while releasing the limiter; + // synchronize with the current release limiter strength. + self.limiter_attack_counter = (((SAMPLES_PER_SECOND + as f32 + * self.config.normalisation_release) + - self.limiter_release_counter as f32) + / (self.config.normalisation_release + / self.config.normalisation_attack)) + as u32; + self.limiter_release_counter = 0; + } + + self.limiter_attack_counter = + self.limiter_attack_counter.saturating_add(1); + self.limiter_strength = self.limiter_attack_counter as f32 + / (SAMPLES_PER_SECOND as f32 + * self.config.normalisation_attack); + + if abs_sample > self.limiter_peak_sample { + self.limiter_peak_sample = abs_sample; + self.limiter_factor = + self.config.normalisation_threshold + / self.limiter_peak_sample; + } + } else if self.limiter_active { + if self.limiter_attack_counter > 0 { + // Release may start within the attack period, before + // the limiter reached full strength. For that reason + // start the release by synchronizing with the current + // attack limiter strength. + self.limiter_release_counter = (((SAMPLES_PER_SECOND + as f32 + * self.config.normalisation_attack) + - self.limiter_attack_counter as f32) + * (self.config.normalisation_release + / self.config.normalisation_attack)) + as u32; + self.limiter_attack_counter = 0; + } + + self.limiter_release_counter = + self.limiter_release_counter.saturating_add(1); + + if self.limiter_release_counter + > (SAMPLES_PER_SECOND as f32 + * self.config.normalisation_release) + as u32 + { + self.reset_limiter(); + } else { + self.limiter_strength = ((SAMPLES_PER_SECOND as f32 + * self.config.normalisation_release) + - self.limiter_release_counter as f32) + / (SAMPLES_PER_SECOND as f32 + * self.config.normalisation_release); + } + } + } + + // Extremely sharp attacks, however unlikely, *may* still clip and provide + // undefined results, so strictly enforce output within [-1.0, 1.0]. + *sample = (*sample as f64 * actual_normalisation_factor as f64) + .clamp(-1.0, 1.0) + as f32; } } } @@ -1146,6 +1298,15 @@ impl PlayerInternal { } } + fn reset_limiter(&mut self) { + self.limiter_active = false; + self.limiter_release_counter = 0; + self.limiter_attack_counter = 0; + self.limiter_peak_sample = 0.0; + self.limiter_factor = 1.0; + self.limiter_strength = 0.0; + } + fn start_playback( &mut self, track_id: SpotifyId, diff --git a/src/main.rs b/src/main.rs index 5360315..91a5865 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,13 +22,15 @@ use librespot::core::version; use librespot::connect::discovery::{discovery, DiscoveryStream}; use librespot::connect::spirc::{Spirc, SpircTask}; use librespot::playback::audio_backend::{self, Sink, BACKENDS}; -use librespot::playback::config::{Bitrate, NormalisationType, PlayerConfig}; +use librespot::playback::config::{Bitrate, NormalisationMethod, NormalisationType, PlayerConfig}; use librespot::playback::mixer::{self, Mixer, MixerConfig}; -use librespot::playback::player::{Player, PlayerEvent}; +use librespot::playback::player::{NormalisationData, Player, PlayerEvent}; mod player_event_handler; use crate::player_event_handler::{emit_sink_event, run_program_on_events}; +const MILLIS: f32 = 1000.0; + fn device_id(name: &str) -> String { hex::encode(Sha1::digest(name.as_bytes())) } @@ -188,6 +190,12 @@ fn setup(args: &[String]) -> Setup { "enable-volume-normalisation", "Play all tracks at the same volume", ) + .optopt( + "", + "normalisation-method", + "Specify the normalisation method to use - [basic, dynamic]. Default is dynamic.", + "NORMALISATION_METHOD", + ) .optopt( "", "normalisation-gain-type", @@ -200,6 +208,30 @@ fn setup(args: &[String]) -> Setup { "Pregain (dB) applied by volume normalisation", "PREGAIN", ) + .optopt( + "", + "normalisation-threshold", + "Threshold (dBFS) to prevent clipping. Default is -1.0.", + "THRESHOLD", + ) + .optopt( + "", + "normalisation-attack", + "Attack time (ms) in which the dynamic limiter is reducing gain. Default is 5.", + "ATTACK", + ) + .optopt( + "", + "normalisation-release", + "Release or decay time (ms) in which the dynamic limiter is restoring gain. Default is 100.", + "RELEASE", + ) + .optopt( + "", + "normalisation-steepness", + "Steepness of the dynamic limiting curve. Default is 1.0.", + "STEEPNESS", + ) .optopt( "", "volume-ctrl", @@ -390,15 +422,51 @@ fn setup(args: &[String]) -> Setup { NormalisationType::from_str(gain_type).expect("Invalid normalisation type") }) .unwrap_or(NormalisationType::default()); + let normalisation_method = matches + .opt_str("normalisation-method") + .as_ref() + .map(|gain_type| { + NormalisationMethod::from_str(gain_type).expect("Invalid normalisation method") + }) + .unwrap_or(NormalisationMethod::default()); PlayerConfig { bitrate: bitrate, gapless: !matches.opt_present("disable-gapless"), normalisation: matches.opt_present("enable-volume-normalisation"), + normalisation_method: normalisation_method, normalisation_type: gain_type, normalisation_pregain: matches .opt_str("normalisation-pregain") .map(|pregain| pregain.parse::().expect("Invalid pregain float value")) .unwrap_or(PlayerConfig::default().normalisation_pregain), + normalisation_threshold: NormalisationData::db_to_ratio( + matches + .opt_str("normalisation-threshold") + .map(|threshold| { + threshold + .parse::() + .expect("Invalid threshold float value") + }) + .unwrap_or(PlayerConfig::default().normalisation_threshold), + ), + normalisation_attack: matches + .opt_str("normalisation-attack") + .map(|attack| attack.parse::().expect("Invalid attack float value")) + .unwrap_or(PlayerConfig::default().normalisation_attack * MILLIS) + / MILLIS, + normalisation_release: matches + .opt_str("normalisation-release") + .map(|release| release.parse::().expect("Invalid release float value")) + .unwrap_or(PlayerConfig::default().normalisation_release * MILLIS) + / MILLIS, + normalisation_steepness: matches + .opt_str("normalisation-steepness") + .map(|steepness| { + steepness + .parse::() + .expect("Invalid steepness float value") + }) + .unwrap_or(PlayerConfig::default().normalisation_steepness), passthrough, } }; From 1672eb87abd22f9c45e3086b8a2eb93c8c8fe6ed Mon Sep 17 00:00:00 2001 From: Roderick van Domburg Date: Tue, 2 Mar 2021 20:21:05 +0100 Subject: [PATCH 061/103] Fix build on Rust < 1.50.0 --- playback/src/player.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/playback/src/player.rs b/playback/src/player.rs index c7edf3a..b6b8ad5 100644 --- a/playback/src/player.rs +++ b/playback/src/player.rs @@ -1263,11 +1263,16 @@ impl PlayerInternal { } } + *sample = + (*sample as f64 * actual_normalisation_factor as f64) as f32; + // Extremely sharp attacks, however unlikely, *may* still clip and provide // undefined results, so strictly enforce output within [-1.0, 1.0]. - *sample = (*sample as f64 * actual_normalisation_factor as f64) - .clamp(-1.0, 1.0) - as f32; + if *sample < -1.0 { + *sample = -1.0; + } else if *sample > 1.0 { + *sample = 1.0; + } } } } From 5257be7824e0fd2c76cf889f88f82047080fed90 Mon Sep 17 00:00:00 2001 From: Roderick van Domburg Date: Fri, 12 Mar 2021 23:05:38 +0100 Subject: [PATCH 062/103] Add command-line option to set F32 or S16 bit output Usage: `--format {F32|S16}`. Default is F32. - Implemented for all backends, except for JACK audio which itself only supports 32-bit output at this time. Setting JACK audio to S16 will panic and instruct the user to set output to F32. - The F32 default works fine for Rodio on macOS, but not on Raspian 10 with Alsa as host. Therefore users on Linux systems are warned to set output to S16 in case of garbled sound with Rodio. This seems an issue with cpal incorrectly detecting the output stream format. - While at it, DRY up lots of code in the backends and by that virtue, also enable OggData passthrough on the subprocess backend. - I tested Rodio, ALSA, pipe and subprocess quite a bit, and call on others to join in and test the other backends. --- audio/src/lib.rs | 7 + playback/Cargo.toml | 4 +- playback/src/audio_backend/alsa.rs | 88 ++++++---- playback/src/audio_backend/gstreamer.rs | 55 +++--- playback/src/audio_backend/jackaudio.rs | 25 +-- playback/src/audio_backend/mod.rs | 48 +++++- playback/src/audio_backend/pipe.rs | 55 +++--- playback/src/audio_backend/portaudio.rs | 113 +++++++++---- playback/src/audio_backend/pulseaudio.rs | 42 ++--- playback/src/audio_backend/rodio.rs | 202 ++++++++++------------- playback/src/audio_backend/sdl.rs | 87 +++++++--- playback/src/audio_backend/subprocess.rs | 24 +-- playback/src/config.rs | 24 +++ playback/src/player.rs | 14 +- src/main.rs | 30 +++- 15 files changed, 504 insertions(+), 314 deletions(-) diff --git a/audio/src/lib.rs b/audio/src/lib.rs index c4d862b..8a9f88f 100644 --- a/audio/src/lib.rs +++ b/audio/src/lib.rs @@ -58,6 +58,13 @@ impl AudioPacket { AudioPacket::OggData(d) => d.is_empty(), } } + + pub fn f32_to_s16(samples: &[f32]) -> Vec { + samples + .iter() + .map(|sample| (*sample as f64 * (0x7FFF as f64 + 0.5) - 0.5) as i16) + .collect() + } } #[cfg(not(any(feature = "with-tremor", feature = "with-vorbis")))] diff --git a/playback/Cargo.toml b/playback/Cargo.toml index b8995a4..67e06be 100644 --- a/playback/Cargo.toml +++ b/playback/Cargo.toml @@ -35,7 +35,7 @@ sdl2 = { version = "0.34", optional = true } gstreamer = { version = "0.16", optional = true } gstreamer-app = { version = "0.16", optional = true } glib = { version = "0.10", optional = true } -zerocopy = { version = "0.3", optional = true } +zerocopy = { version = "0.3" } [features] alsa-backend = ["alsa"] @@ -45,4 +45,4 @@ jackaudio-backend = ["jack"] rodiojack-backend = ["rodio", "cpal/jack"] rodio-backend = ["rodio", "cpal"] sdl-backend = ["sdl2"] -gstreamer-backend = ["gstreamer", "gstreamer-app", "glib", "zerocopy"] +gstreamer-backend = ["gstreamer", "gstreamer-app", "glib" ] diff --git a/playback/src/audio_backend/alsa.rs b/playback/src/audio_backend/alsa.rs index 9bc17fe..92b71f4 100644 --- a/playback/src/audio_backend/alsa.rs +++ b/playback/src/audio_backend/alsa.rs @@ -1,18 +1,21 @@ use super::{Open, Sink}; use crate::audio::AudioPacket; +use crate::config::AudioFormat; +use crate::player::{NUM_CHANNELS, SAMPLES_PER_SECOND, SAMPLE_RATE}; use alsa::device_name::HintIter; use alsa::pcm::{Access, Format, Frames, HwParams, PCM}; use alsa::{Direction, Error, ValueOr}; use std::cmp::min; use std::ffi::CString; -use std::io; use std::process::exit; +use std::{io, mem}; -const PREFERRED_PERIOD_SIZE: Frames = 11025; // Period of roughly 125ms -const BUFFERED_PERIODS: Frames = 4; +const BUFFERED_LATENCY: f32 = 0.125; // seconds +const BUFFERED_PERIODS: u8 = 4; pub struct AlsaSink { pcm: Option, + format: AudioFormat, device: String, buffer: Vec, } @@ -34,25 +37,28 @@ fn list_outputs() { } } -fn open_device(dev_name: &str) -> Result<(PCM, Frames), Box> { +fn open_device(dev_name: &str, format: AudioFormat) -> Result<(PCM, Frames), Box> { let pcm = PCM::new(dev_name, Direction::Playback, false)?; - let mut period_size = PREFERRED_PERIOD_SIZE; + let (alsa_format, sample_size) = match format { + AudioFormat::F32 => (Format::float(), mem::size_of::()), + AudioFormat::S16 => (Format::s16(), mem::size_of::()), + }; + // http://www.linuxjournal.com/article/6735?page=0,1#N0x19ab2890.0x19ba78d8 // latency = period_size * periods / (rate * bytes_per_frame) - // For stereo samples encoded as 32-bit floats, one frame has a length of eight bytes. - // 500ms = buffer_size / (44100 * 8) - // buffer_size_bytes = 0.5 * 44100 / 8 - // buffer_size_frames = 0.5 * 44100 = 22050 - { - // Set hardware parameters: 44100 Hz / Stereo / 32-bit float - let hwp = HwParams::any(&pcm)?; + // For stereo samples encoded as 32-bit float, one frame has a length of eight bytes. + let mut period_size = ((SAMPLES_PER_SECOND * sample_size as u32) as f32 + * (BUFFERED_LATENCY / BUFFERED_PERIODS as f32)) as i32; + // Set hardware parameters: 44100 Hz / stereo / 32-bit float or 16-bit signed integer + { + let hwp = HwParams::any(&pcm)?; hwp.set_access(Access::RWInterleaved)?; - hwp.set_format(Format::float())?; - hwp.set_rate(44100, ValueOr::Nearest)?; - hwp.set_channels(2)?; + hwp.set_format(alsa_format)?; + hwp.set_rate(SAMPLE_RATE, ValueOr::Nearest)?; + hwp.set_channels(NUM_CHANNELS as u32)?; period_size = hwp.set_period_size_near(period_size, ValueOr::Greater)?; - hwp.set_buffer_size_near(period_size * BUFFERED_PERIODS)?; + hwp.set_buffer_size_near(period_size * BUFFERED_PERIODS as i32)?; pcm.hw_params(&hwp)?; let swp = pcm.sw_params_current()?; @@ -64,12 +70,12 @@ fn open_device(dev_name: &str) -> Result<(PCM, Frames), Box> { } impl Open for AlsaSink { - fn open(device: Option) -> AlsaSink { - info!("Using alsa sink"); + fn open(device: Option, format: AudioFormat) -> AlsaSink { + info!("Using Alsa sink with format: {:?}", format); let name = match device.as_ref().map(AsRef::as_ref) { Some("?") => { - println!("Listing available alsa outputs"); + println!("Listing available Alsa outputs:"); list_outputs(); exit(0) } @@ -80,6 +86,7 @@ impl Open for AlsaSink { AlsaSink { pcm: None, + format: format, device: name, buffer: vec![], } @@ -89,12 +96,13 @@ impl Open for AlsaSink { impl Sink for AlsaSink { fn start(&mut self) -> io::Result<()> { if self.pcm.is_none() { - let pcm = open_device(&self.device); + let pcm = open_device(&self.device, self.format); match pcm { Ok((p, period_size)) => { self.pcm = Some(p); // Create a buffer for all samples for a full period - self.buffer = Vec::with_capacity((period_size * 2) as usize); + self.buffer = + Vec::with_capacity((period_size * BUFFERED_PERIODS as i32) as usize); } Err(e) => { error!("Alsa error PCM open {}", e); @@ -111,14 +119,10 @@ impl Sink for AlsaSink { fn stop(&mut self) -> io::Result<()> { { - let pcm = self.pcm.as_mut().unwrap(); // Write any leftover data in the period buffer // before draining the actual buffer - let io = pcm.io_f32().unwrap(); - match io.writei(&self.buffer[..]) { - Ok(_) => (), - Err(err) => pcm.try_recover(err, false).unwrap(), - } + self.write_buf().expect("could not flush buffer"); + let pcm = self.pcm.as_mut().unwrap(); pcm.drain().unwrap(); } self.pcm = None; @@ -137,12 +141,7 @@ impl Sink for AlsaSink { .extend_from_slice(&data[processed_data..processed_data + data_to_buffer]); processed_data += data_to_buffer; if self.buffer.len() == self.buffer.capacity() { - let pcm = self.pcm.as_mut().unwrap(); - let io = pcm.io_f32().unwrap(); - match io.writei(&self.buffer) { - Ok(_) => (), - Err(err) => pcm.try_recover(err, false).unwrap(), - } + self.write_buf().expect("could not append to buffer"); self.buffer.clear(); } } @@ -150,3 +149,26 @@ impl Sink for AlsaSink { Ok(()) } } + +impl AlsaSink { + fn write_buf(&mut self) -> io::Result<()> { + let pcm = self.pcm.as_mut().unwrap(); + let io_result = match self.format { + AudioFormat::F32 => { + let io = pcm.io_f32().unwrap(); + io.writei(&self.buffer) + } + AudioFormat::S16 => { + let io = pcm.io_i16().unwrap(); + let buf_s16: Vec = AudioPacket::f32_to_s16(&self.buffer); + io.writei(&buf_s16[..]) + } + }; + match io_result { + Ok(_) => (), + Err(err) => pcm.try_recover(err, false).unwrap(), + }; + + Ok(()) + } +} diff --git a/playback/src/audio_backend/gstreamer.rs b/playback/src/audio_backend/gstreamer.rs index 1ad3631..17ad86e 100644 --- a/playback/src/audio_backend/gstreamer.rs +++ b/playback/src/audio_backend/gstreamer.rs @@ -1,21 +1,29 @@ -use super::{Open, Sink}; +use super::{Open, Sink, SinkAsBytes}; use crate::audio::AudioPacket; +use crate::config::AudioFormat; +use crate::player::{NUM_CHANNELS, SAMPLE_RATE}; use gst::prelude::*; use gst::*; use std::sync::mpsc::{sync_channel, SyncSender}; use std::{io, thread}; -use zerocopy::*; +use zerocopy::AsBytes; #[allow(dead_code)] pub struct GstreamerSink { tx: SyncSender>, pipeline: gst::Pipeline, + format: AudioFormat, } impl Open for GstreamerSink { - fn open(device: Option) -> GstreamerSink { - gst::init().expect("Failed to init gstreamer!"); - let pipeline_str_preamble = r#"appsrc caps="audio/x-raw,format=F32,layout=interleaved,channels=2,rate=44100" block=true max-bytes=4096 name=appsrc0 "#; + fn open(device: Option, format: AudioFormat) -> GstreamerSink { + info!("Using GStreamer sink with format: {:?}", format); + + gst::init().expect("failed to init GStreamer!"); + let pipeline_str_preamble = format!( + r#"appsrc caps="audio/x-raw,format={:?},layout=interleaved,channels={},rate={}" block=true max-bytes=4096 name=appsrc0 "#, + format, NUM_CHANNELS, SAMPLE_RATE + ); let pipeline_str_rest = r#" ! audioconvert ! autoaudiosink"#; let pipeline_str: String = match device { Some(x) => format!("{}{}", pipeline_str_preamble, x), @@ -27,25 +35,25 @@ impl Open for GstreamerSink { let pipelinee = gst::parse_launch(&*pipeline_str).expect("Couldn't launch pipeline; likely a GStreamer issue or an error in the pipeline string you specified in the 'device' argument to librespot."); let pipeline = pipelinee .dynamic_cast::() - .expect("Couldn't cast pipeline element at runtime!"); - let bus = pipeline.get_bus().expect("Couldn't get bus from pipeline"); + .expect("couldn't cast pipeline element at runtime!"); + let bus = pipeline.get_bus().expect("couldn't get bus from pipeline"); let mainloop = glib::MainLoop::new(None, false); let appsrce: gst::Element = pipeline .get_by_name("appsrc0") - .expect("Couldn't get appsrc from pipeline"); + .expect("couldn't get appsrc from pipeline"); let appsrc: gst_app::AppSrc = appsrce .dynamic_cast::() - .expect("Couldn't cast AppSrc element at runtime!"); + .expect("couldn't cast AppSrc element at runtime!"); let bufferpool = gst::BufferPool::new(); - let appsrc_caps = appsrc.get_caps().expect("Couldn't get appsrc caps"); + let appsrc_caps = appsrc.get_caps().expect("couldn't get appsrc caps"); let mut conf = bufferpool.get_config(); conf.set_params(Some(&appsrc_caps), 8192, 0, 0); bufferpool .set_config(conf) - .expect("Couldn't configure the buffer pool"); + .expect("couldn't configure the buffer pool"); bufferpool .set_active(true) - .expect("Couldn't activate buffer pool"); + .expect("couldn't activate buffer pool"); let (tx, rx) = sync_channel::>(128); thread::spawn(move || { @@ -57,7 +65,7 @@ impl Open for GstreamerSink { mutbuf.set_size(data.len()); mutbuf .copy_from_slice(0, data.as_bytes()) - .expect("Failed to copy from slice"); + .expect("failed to copy from slice"); let _eat = appsrc.push_buffer(okbuffer); } } @@ -83,33 +91,32 @@ impl Open for GstreamerSink { glib::Continue(true) }) - .expect("Failed to add bus watch"); + .expect("failed to add bus watch"); thread_mainloop.run(); }); pipeline .set_state(gst::State::Playing) - .expect("Unable to set the pipeline to the `Playing` state"); + .expect("unable to set the pipeline to the `Playing` state"); GstreamerSink { tx: tx, pipeline: pipeline, + format: format, } } } impl Sink for GstreamerSink { - fn start(&mut self) -> io::Result<()> { - Ok(()) - } - fn stop(&mut self) -> io::Result<()> { - Ok(()) - } - fn write(&mut self, packet: &AudioPacket) -> io::Result<()> { + start_stop_noop!(); + sink_as_bytes!(); +} + +impl SinkAsBytes for GstreamerSink { + fn write_bytes(&mut self, data: &[u8]) -> io::Result<()> { // Copy expensively (in to_vec()) to avoid thread synchronization - let deighta: &[u8] = packet.samples().as_bytes(); self.tx - .send(deighta.to_vec()) + .send(data.to_vec()) .expect("tx send failed in write function"); Ok(()) } diff --git a/playback/src/audio_backend/jackaudio.rs b/playback/src/audio_backend/jackaudio.rs index e95933f..295941a 100644 --- a/playback/src/audio_backend/jackaudio.rs +++ b/playback/src/audio_backend/jackaudio.rs @@ -1,10 +1,12 @@ use super::{Open, Sink}; use crate::audio::AudioPacket; +use crate::config::AudioFormat; +use crate::player::NUM_CHANNELS; use jack::{ AsyncClient, AudioOut, Client, ClientOptions, Control, Port, ProcessHandler, ProcessScope, }; -use std::io; use std::sync::mpsc::{sync_channel, Receiver, SyncSender}; +use std::{io, mem}; pub struct JackSink { send: SyncSender, @@ -39,8 +41,15 @@ impl ProcessHandler for JackData { } impl Open for JackSink { - fn open(client_name: Option) -> JackSink { - info!("Using jack sink!"); + fn open(client_name: Option, format: AudioFormat) -> JackSink { + info!("Using JACK sink with format {:?}", format); + + if format != AudioFormat::F32 { + panic!( + "JACK sink only supports 32-bit floating point output. Use `--format {:?}`", + AudioFormat::F32 + ); + } let client_name = client_name.unwrap_or("librespot".to_string()); let (client, _status) = @@ -48,7 +57,7 @@ impl Open for JackSink { let ch_r = client.register_port("out_0", AudioOut::default()).unwrap(); let ch_l = client.register_port("out_1", AudioOut::default()).unwrap(); // buffer for samples from librespot (~10ms) - let (tx, rx) = sync_channel(2 * 1024 * 4); + let (tx, rx) = sync_channel::(NUM_CHANNELS as usize * 1024 * mem::size_of::()); let jack_data = JackData { rec: rx, port_l: ch_l, @@ -64,13 +73,7 @@ impl Open for JackSink { } impl Sink for JackSink { - fn start(&mut self) -> io::Result<()> { - Ok(()) - } - - fn stop(&mut self) -> io::Result<()> { - Ok(()) - } + start_stop_noop!(); fn write(&mut self, packet: &AudioPacket) -> io::Result<()> { for s in packet.samples().iter() { diff --git a/playback/src/audio_backend/mod.rs b/playback/src/audio_backend/mod.rs index 3f5dae8..550ebb8 100644 --- a/playback/src/audio_backend/mod.rs +++ b/playback/src/audio_backend/mod.rs @@ -1,8 +1,9 @@ use crate::audio::AudioPacket; +use crate::config::AudioFormat; use std::io; pub trait Open { - fn open(_: Option) -> Self; + fn open(_: Option, format: AudioFormat) -> Self; } pub trait Sink { @@ -11,8 +12,42 @@ pub trait Sink { fn write(&mut self, packet: &AudioPacket) -> io::Result<()>; } -fn mk_sink(device: Option) -> Box { - Box::new(S::open(device)) +pub trait SinkAsBytes { + fn write_bytes(&mut self, data: &[u8]) -> io::Result<()>; +} + +fn mk_sink(device: Option, format: AudioFormat) -> Box { + Box::new(S::open(device, format)) +} + +// reuse code for various backends +macro_rules! sink_as_bytes { + () => { + fn write(&mut self, packet: &AudioPacket) -> io::Result<()> { + use zerocopy::AsBytes; + match packet { + AudioPacket::Samples(samples) => match self.format { + AudioFormat::F32 => self.write_bytes(samples.as_bytes()), + AudioFormat::S16 => { + let samples_s16 = AudioPacket::f32_to_s16(samples); + self.write_bytes(samples_s16.as_bytes()) + } + }, + AudioPacket::OggData(samples) => self.write_bytes(samples), + } + } + }; +} + +macro_rules! start_stop_noop { + () => { + fn start(&mut self) -> io::Result<()> { + Ok(()) + } + fn stop(&mut self) -> io::Result<()> { + Ok(()) + } + }; } #[cfg(feature = "alsa-backend")] @@ -68,7 +103,10 @@ use self::pipe::StdoutSink; mod subprocess; use self::subprocess::SubprocessSink; -pub const BACKENDS: &'static [(&'static str, fn(Option) -> Box)] = &[ +pub const BACKENDS: &'static [( + &'static str, + fn(Option, AudioFormat) -> Box, +)] = &[ #[cfg(feature = "alsa-backend")] ("alsa", mk_sink::), #[cfg(feature = "portaudio-backend")] @@ -92,7 +130,7 @@ pub const BACKENDS: &'static [(&'static str, fn(Option) -> Box ("subprocess", mk_sink::), ]; -pub fn find(name: Option) -> Option) -> Box> { +pub fn find(name: Option) -> Option, AudioFormat) -> Box> { if let Some(name) = name { BACKENDS .iter() diff --git a/playback/src/audio_backend/pipe.rs b/playback/src/audio_backend/pipe.rs index 5516ee9..3a90d06 100644 --- a/playback/src/audio_backend/pipe.rs +++ b/playback/src/audio_backend/pipe.rs @@ -1,46 +1,39 @@ -use super::{Open, Sink}; +use super::{Open, Sink, SinkAsBytes}; use crate::audio::AudioPacket; +use crate::config::AudioFormat; use std::fs::OpenOptions; use std::io::{self, Write}; -use std::mem; -use std::slice; -pub struct StdoutSink(Box); +pub struct StdoutSink { + output: Box, + format: AudioFormat, +} impl Open for StdoutSink { - fn open(path: Option) -> StdoutSink { - if let Some(path) = path { - let file = OpenOptions::new().write(true).open(path).unwrap(); - StdoutSink(Box::new(file)) - } else { - StdoutSink(Box::new(io::stdout())) + fn open(path: Option, format: AudioFormat) -> StdoutSink { + info!("Using pipe sink with format: {:?}", format); + + let output: Box = match path { + Some(path) => Box::new(OpenOptions::new().write(true).open(path).unwrap()), + _ => Box::new(io::stdout()), + }; + + StdoutSink { + output: output, + format: format, } } } impl Sink for StdoutSink { - fn start(&mut self) -> io::Result<()> { - Ok(()) - } - - fn stop(&mut self) -> io::Result<()> { - Ok(()) - } - - fn write(&mut self, packet: &AudioPacket) -> io::Result<()> { - let data: &[u8] = match packet { - AudioPacket::Samples(data) => unsafe { - slice::from_raw_parts( - data.as_ptr() as *const u8, - data.len() * mem::size_of::(), - ) - }, - AudioPacket::OggData(data) => data, - }; - - self.0.write_all(data)?; - self.0.flush()?; + start_stop_noop!(); + sink_as_bytes!(); +} +impl SinkAsBytes for StdoutSink { + fn write_bytes(&mut self, data: &[u8]) -> io::Result<()> { + self.output.write_all(data)?; + self.output.flush()?; Ok(()) } } diff --git a/playback/src/audio_backend/portaudio.rs b/playback/src/audio_backend/portaudio.rs index 0b8eac0..70caedd 100644 --- a/playback/src/audio_backend/portaudio.rs +++ b/playback/src/audio_backend/portaudio.rs @@ -1,5 +1,7 @@ use super::{Open, Sink}; use crate::audio::AudioPacket; +use crate::config::AudioFormat; +use crate::player::{NUM_CHANNELS, SAMPLE_RATE}; use portaudio_rs; use portaudio_rs::device::{get_default_output_index, DeviceIndex, DeviceInfo}; use portaudio_rs::stream::*; @@ -7,10 +9,16 @@ use std::io; use std::process::exit; use std::time::Duration; -pub struct PortAudioSink<'a>( - Option>, - StreamParameters, -); +pub enum PortAudioSink<'a> { + F32( + Option>, + StreamParameters, + ), + S16( + Option>, + StreamParameters, + ), +} fn output_devices() -> Box> { let count = portaudio_rs::device::get_count().unwrap(); @@ -40,8 +48,8 @@ fn find_output(device: &str) -> Option { } impl<'a> Open for PortAudioSink<'a> { - fn open(device: Option) -> PortAudioSink<'a> { - debug!("Using PortAudio sink"); + fn open(device: Option, format: AudioFormat) -> PortAudioSink<'a> { + info!("Using PortAudio sink with format: {:?}", format); portaudio_rs::initialize().unwrap(); @@ -53,7 +61,7 @@ impl<'a> Open for PortAudioSink<'a> { Some(device) => find_output(device), None => get_default_output_index(), } - .expect("Could not find device"); + .expect("could not find device"); let info = portaudio_rs::device::get_info(device_idx); let latency = match info { @@ -61,46 +69,87 @@ impl<'a> Open for PortAudioSink<'a> { None => Duration::new(0, 0), }; - let params = StreamParameters { - device: device_idx, - channel_count: 2, - suggested_latency: latency, - data: 0.0, - }; - - PortAudioSink(None, params) + macro_rules! open_sink { + ($sink: expr, $data: expr) => {{ + let params = StreamParameters { + device: device_idx, + channel_count: NUM_CHANNELS as u32, + suggested_latency: latency, + data: $data, + }; + $sink(None, params) + }}; + } + match format { + AudioFormat::F32 => open_sink!(PortAudioSink::F32, 0.0), + AudioFormat::S16 => open_sink!(PortAudioSink::S16, 0), + } } } impl<'a> Sink for PortAudioSink<'a> { fn start(&mut self) -> io::Result<()> { - if self.0.is_none() { - self.0 = Some( - Stream::open( - None, - Some(self.1), - 44100.0, - FRAMES_PER_BUFFER_UNSPECIFIED, - StreamFlags::empty(), - None, - ) - .unwrap(), - ); + macro_rules! start_sink { + ($stream: ident, $parameters: ident) => {{ + if $stream.is_none() { + *$stream = Some( + Stream::open( + None, + Some(*$parameters), + SAMPLE_RATE as f64, + FRAMES_PER_BUFFER_UNSPECIFIED, + StreamFlags::empty(), + None, + ) + .unwrap(), + ); + } + $stream.as_mut().unwrap().start().unwrap() + }}; } + match self { + PortAudioSink::F32(stream, parameters) => start_sink!(stream, parameters), + PortAudioSink::S16(stream, parameters) => start_sink!(stream, parameters), + }; - self.0.as_mut().unwrap().start().unwrap(); Ok(()) } + fn stop(&mut self) -> io::Result<()> { - self.0.as_mut().unwrap().stop().unwrap(); - self.0 = None; + macro_rules! stop_sink { + ($stream: expr) => {{ + $stream.as_mut().unwrap().stop().unwrap(); + *$stream = None; + }}; + } + match self { + PortAudioSink::F32(stream, _parameters) => stop_sink!(stream), + PortAudioSink::S16(stream, _parameters) => stop_sink!(stream), + }; + Ok(()) } + fn write(&mut self, packet: &AudioPacket) -> io::Result<()> { - match self.0.as_mut().unwrap().write(packet.samples()) { + macro_rules! write_sink { + ($stream: expr, $samples: expr) => { + $stream.as_mut().unwrap().write($samples) + }; + } + let result = match self { + PortAudioSink::F32(stream, _parameters) => { + let samples = packet.samples(); + write_sink!(stream, &samples) + } + PortAudioSink::S16(stream, _parameters) => { + let samples_s16: Vec = AudioPacket::f32_to_s16(packet.samples()); + write_sink!(stream, &samples_s16) + } + }; + match result { Ok(_) => (), Err(portaudio_rs::PaError::OutputUnderflowed) => error!("PortAudio write underflow"), - Err(e) => panic!("PA Error {}", e), + Err(e) => panic!("PortAudio error {}", e), }; Ok(()) diff --git a/playback/src/audio_backend/pulseaudio.rs b/playback/src/audio_backend/pulseaudio.rs index 4dca210..8c1e8e8 100644 --- a/playback/src/audio_backend/pulseaudio.rs +++ b/playback/src/audio_backend/pulseaudio.rs @@ -1,9 +1,10 @@ -use super::{Open, Sink}; +use super::{Open, Sink, SinkAsBytes}; use crate::audio::AudioPacket; +use crate::config::AudioFormat; +use crate::player::{NUM_CHANNELS, SAMPLE_RATE}; use libpulse_binding::{self as pulse, stream::Direction}; use libpulse_simple_binding::Simple; use std::io; -use std::mem; const APP_NAME: &str = "librespot"; const STREAM_NAME: &str = "Spotify endpoint"; @@ -12,16 +13,22 @@ pub struct PulseAudioSink { s: Option, ss: pulse::sample::Spec, device: Option, + format: AudioFormat, } impl Open for PulseAudioSink { - fn open(device: Option) -> PulseAudioSink { - debug!("Using PulseAudio sink"); + fn open(device: Option, format: AudioFormat) -> PulseAudioSink { + info!("Using PulseAudio sink with format: {:?}", format); + + let pulse_format = match format { + AudioFormat::F32 => pulse::sample::Format::F32le, + AudioFormat::S16 => pulse::sample::Format::S16le, + }; let ss = pulse::sample::Spec { - format: pulse::sample::Format::F32le, - channels: 2, // stereo - rate: 44100, + format: pulse_format, + channels: NUM_CHANNELS, + rate: SAMPLE_RATE, }; debug_assert!(ss.is_valid()); @@ -29,6 +36,7 @@ impl Open for PulseAudioSink { s: None, ss: ss, device: device, + format: format, } } } @@ -67,19 +75,13 @@ impl Sink for PulseAudioSink { Ok(()) } - fn write(&mut self, packet: &AudioPacket) -> io::Result<()> { - if let Some(s) = &self.s { - // SAFETY: An f32 consists of four bytes, so that the given slice can be interpreted - // as a byte array of four. Each byte pointer is validly aligned, and so is the newly - // created slice. - let d: &[u8] = unsafe { - std::slice::from_raw_parts( - packet.samples().as_ptr() as *const u8, - packet.samples().len() * mem::size_of::(), - ) - }; + sink_as_bytes!(); +} - match s.write(d) { +impl SinkAsBytes for PulseAudioSink { + fn write_bytes(&mut self, data: &[u8]) -> io::Result<()> { + if let Some(s) = &self.s { + match s.write(data) { Ok(_) => Ok(()), Err(e) => Err(io::Error::new( io::ErrorKind::BrokenPipe, @@ -89,7 +91,7 @@ impl Sink for PulseAudioSink { } else { Err(io::Error::new( io::ErrorKind::NotConnected, - "Not connected to pulseaudio", + "Not connected to PulseAudio", )) } } diff --git a/playback/src/audio_backend/rodio.rs b/playback/src/audio_backend/rodio.rs index 6c996e8..7571aa2 100644 --- a/playback/src/audio_backend/rodio.rs +++ b/playback/src/audio_backend/rodio.rs @@ -2,33 +2,90 @@ use super::{Open, Sink}; extern crate cpal; extern crate rodio; use crate::audio::AudioPacket; +use crate::config::AudioFormat; use cpal::traits::{DeviceTrait, HostTrait}; use std::process::exit; use std::{io, thread, time}; -pub struct RodioSink { - rodio_sink: rodio::Sink, - // We have to keep hold of this object, or the Sink can't play... - #[allow(dead_code)] - stream: rodio::OutputStream, +// most code is shared between RodioSink and JackRodioSink +macro_rules! rodio_sink { + ($name: ident) => { + pub struct $name { + rodio_sink: rodio::Sink, + // We have to keep hold of this object, or the Sink can't play... + #[allow(dead_code)] + stream: rodio::OutputStream, + format: AudioFormat, + } + + impl Sink for $name { + start_stop_noop!(); + + fn write(&mut self, packet: &AudioPacket) -> io::Result<()> { + let samples = packet.samples(); + match self.format { + AudioFormat::F32 => { + let source = rodio::buffer::SamplesBuffer::new(2, 44100, samples); + self.rodio_sink.append(source) + } + AudioFormat::S16 => { + let samples_s16: Vec = AudioPacket::f32_to_s16(samples); + let source = rodio::buffer::SamplesBuffer::new(2, 44100, samples_s16); + self.rodio_sink.append(source) + } + }; + + // Chunk sizes seem to be about 256 to 3000 ish items long. + // Assuming they're on average 1628 then a half second buffer is: + // 44100 elements --> about 27 chunks + while self.rodio_sink.len() > 26 { + // sleep and wait for rodio to drain a bit + thread::sleep(time::Duration::from_millis(10)); + } + Ok(()) + } + } + + impl $name { + fn open_sink(host: &cpal::Host, device: Option, format: AudioFormat) -> $name { + if format != AudioFormat::S16 { + #[cfg(target_os = "linux")] + { + warn!("Rodio output to Alsa is known to cause garbled sound on output formats other than 16-bit signed integer."); + warn!("Consider using `--backend alsa` OR `--format {:?}`", AudioFormat::S16); + } + } + + let rodio_device = match_device(&host, device); + debug!("Using cpal device"); + let stream = rodio::OutputStream::try_from_device(&rodio_device) + .expect("couldn't open output stream."); + debug!("Using Rodio stream"); + let sink = rodio::Sink::try_new(&stream.1).expect("couldn't create output sink."); + debug!("Using Rodio sink"); + + $name { + rodio_sink: sink, + stream: stream.0, + format: format, + } + } + } + }; } +rodio_sink!(RodioSink); #[cfg(all( feature = "rodiojack-backend", any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd") ))] -pub struct JackRodioSink { - jackrodio_sink: rodio::Sink, - // We have to keep hold of this object, or the Sink can't play... - #[allow(dead_code)] - stream: rodio::OutputStream, -} +rodio_sink!(JackRodioSink); fn list_formats(ref device: &rodio::Device) { let default_fmt = match device.default_output_config() { Ok(fmt) => cpal::SupportedStreamConfig::from(fmt), Err(e) => { - warn!("Error getting default rodio::Sink config: {}", e); + warn!("Error getting default Rodio output config: {}", e); return; } }; @@ -38,13 +95,13 @@ fn list_formats(ref device: &rodio::Device) { let mut output_configs = match device.supported_output_configs() { Ok(f) => f.peekable(), Err(e) => { - warn!("Error getting supported rodio::Sink configs: {}", e); + warn!("Error getting supported Rodio output configs: {}", e); return; } }; if output_configs.peek().is_some() { - debug!(" Available configs:"); + debug!(" Available output configs:"); for format in output_configs { debug!(" {:?}", format); } @@ -54,13 +111,13 @@ fn list_formats(ref device: &rodio::Device) { fn list_outputs(ref host: &cpal::Host) { let default_device = get_default_device(host); let default_device_name = default_device.name().expect("cannot get output name"); - println!("Default Audio Device:\n {}", default_device_name); + println!("Default audio device:\n {}", default_device_name); list_formats(&default_device); - println!("Other Available Audio Devices:"); + println!("Other available audio devices:"); let found_devices = host.output_devices().expect(&format!( - "Cannot get list of output devices of Host: {:?}", + "Cannot get list of output devices of host: {:?}", host.id() )); for device in found_devices { @@ -86,7 +143,7 @@ fn match_device(ref host: &cpal::Host, device: Option) -> rodio::Device } let found_devices = host.output_devices().expect(&format!( - "Cannot get list of output devices of Host: {:?}", + "cannot get list of output devices of host: {:?}", host.id() )); for d in found_devices { @@ -102,22 +159,14 @@ fn match_device(ref host: &cpal::Host, device: Option) -> rodio::Device } impl Open for RodioSink { - fn open(device: Option) -> RodioSink { + fn open(device: Option, format: AudioFormat) -> RodioSink { let host = cpal::default_host(); - debug!("Using rodio sink with cpal host: {:?}", host.id()); - - let rodio_device = match_device(&host, device); - debug!("Using cpal device"); - let stream = rodio::OutputStream::try_from_device(&rodio_device) - .expect("Couldn't open output stream."); - debug!("Using rodio stream"); - let sink = rodio::Sink::try_new(&stream.1).expect("Couldn't create output sink."); - debug!("Using rodio sink"); - - RodioSink { - rodio_sink: sink, - stream: stream.0, - } + info!( + "Using Rodio sink with format {:?} and cpal host: {:?}", + format, + host.id() + ); + Self::open_sink(&host, device, format) } } @@ -126,89 +175,18 @@ impl Open for RodioSink { any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd") ))] impl Open for JackRodioSink { - fn open(device: Option) -> JackRodioSink { + fn open(device: Option, format: AudioFormat) -> JackRodioSink { let host = cpal::host_from_id( cpal::available_hosts() .into_iter() .find(|id| *id == cpal::HostId::Jack) - .expect("Jack Host not found"), + .expect("JACK host not found"), ) - .expect("Jack Host not found"); - debug!("Using jack rodio sink with cpal Jack host"); - - let rodio_device = match_device(&host, device); - debug!("Using cpal device"); - let stream = rodio::OutputStream::try_from_device(&rodio_device) - .expect("Couldn't open output stream."); - debug!("Using jack rodio stream"); - let sink = rodio::Sink::try_new(&stream.1).expect("Couldn't create output sink."); - debug!("Using jack rodio sink"); - - JackRodioSink { - jackrodio_sink: sink, - stream: stream.0, - } - } -} - -impl Sink for RodioSink { - fn start(&mut self) -> io::Result<()> { - // More similar to an "unpause" than "play". Doesn't undo "stop". - // self.rodio_sink.play(); - Ok(()) - } - - fn stop(&mut self) -> io::Result<()> { - // This will immediately stop playback, but the sink is then unusable. - // We just have to let the current buffer play till the end. - // self.rodio_sink.stop(); - Ok(()) - } - - fn write(&mut self, packet: &AudioPacket) -> io::Result<()> { - let source = rodio::buffer::SamplesBuffer::new(2, 44100, packet.samples()); - self.rodio_sink.append(source); - - // Chunk sizes seem to be about 256 to 3000 ish items long. - // Assuming they're on average 1628 then a half second buffer is: - // 44100 elements --> about 27 chunks - while self.rodio_sink.len() > 26 { - // sleep and wait for rodio to drain a bit - thread::sleep(time::Duration::from_millis(10)); - } - Ok(()) - } -} - -#[cfg(all( - feature = "rodiojack-backend", - any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd") -))] -impl Sink for JackRodioSink { - fn start(&mut self) -> io::Result<()> { - // More similar to an "unpause" than "play". Doesn't undo "stop". - // self.rodio_sink.play(); - Ok(()) - } - - fn stop(&mut self) -> io::Result<()> { - // This will immediately stop playback, but the sink is then unusable. - // We just have to let the current buffer play till the end. - // self.rodio_sink.stop(); - Ok(()) - } - - fn write(&mut self, data: &[f32]) -> io::Result<()> { - let source = rodio::buffer::SamplesBuffer::new(2, 44100, data); - self.jackrodio_sink.append(source); - - // Chunk sizes seem to be about 256 to 3000 ish items long. - // Assuming they're on average 1628 then a half second buffer is: - // 44100 elements --> about 27 chunks - while self.jackrodio_sink.len() > 26 { - // sleep and wait for rodio to drain a bit - thread::sleep(time::Duration::from_millis(10)); - } - Ok(()) + .expect("JACK host not found"); + info!( + "Using JACK Rodio sink with format {:?} and cpal JACK host", + format + ); + Self::open_sink(&host, device, format) } } diff --git a/playback/src/audio_backend/sdl.rs b/playback/src/audio_backend/sdl.rs index 727615d..6e52b32 100644 --- a/playback/src/audio_backend/sdl.rs +++ b/playback/src/audio_backend/sdl.rs @@ -1,57 +1,98 @@ use super::{Open, Sink}; use crate::audio::AudioPacket; +use crate::config::AudioFormat; +use crate::player::{NUM_CHANNELS, SAMPLE_RATE}; use sdl2::audio::{AudioQueue, AudioSpecDesired}; -use std::{io, thread, time}; +use std::{io, mem, thread, time}; -type Channel = f32; - -pub struct SdlSink { - queue: AudioQueue, +pub enum SdlSink { + F32(AudioQueue), + S16(AudioQueue), } impl Open for SdlSink { - fn open(device: Option) -> SdlSink { - debug!("Using SDL sink"); + fn open(device: Option, format: AudioFormat) -> SdlSink { + info!("Using SDL sink with format: {:?}", format); if device.is_some() { panic!("SDL sink does not support specifying a device name"); } - let ctx = sdl2::init().expect("Could not init SDL"); - let audio = ctx.audio().expect("Could not init SDL audio subsystem"); + let ctx = sdl2::init().expect("could not initialize SDL"); + let audio = ctx + .audio() + .expect("could not initialize SDL audio subsystem"); let desired_spec = AudioSpecDesired { - freq: Some(44_100), - channels: Some(2), + freq: Some(SAMPLE_RATE as i32), + channels: Some(NUM_CHANNELS), samples: None, }; - let queue = audio - .open_queue(None, &desired_spec) - .expect("Could not open SDL audio device"); - SdlSink { queue: queue } + macro_rules! open_sink { + ($sink: expr, $type: ty) => {{ + let queue: AudioQueue<$type> = audio + .open_queue(None, &desired_spec) + .expect("could not open SDL audio device"); + $sink(queue) + }}; + } + match format { + AudioFormat::F32 => open_sink!(SdlSink::F32, f32), + AudioFormat::S16 => open_sink!(SdlSink::S16, i16), + } } } impl Sink for SdlSink { fn start(&mut self) -> io::Result<()> { - self.queue.clear(); - self.queue.resume(); + macro_rules! start_sink { + ($queue: expr) => {{ + $queue.clear(); + $queue.resume(); + }}; + } + match self { + SdlSink::F32(queue) => start_sink!(queue), + SdlSink::S16(queue) => start_sink!(queue), + }; Ok(()) } fn stop(&mut self) -> io::Result<()> { - self.queue.pause(); - self.queue.clear(); + macro_rules! stop_sink { + ($queue: expr) => {{ + $queue.pause(); + $queue.clear(); + }}; + } + match self { + SdlSink::F32(queue) => stop_sink!(queue), + SdlSink::S16(queue) => stop_sink!(queue), + }; Ok(()) } fn write(&mut self, packet: &AudioPacket) -> io::Result<()> { - while self.queue.size() > (2 * 4 * 44_100) { - // sleep and wait for sdl thread to drain the queue a bit - thread::sleep(time::Duration::from_millis(10)); + macro_rules! drain_sink { + ($queue: expr, $size: expr) => {{ + // sleep and wait for sdl thread to drain the queue a bit + while $queue.size() > (NUM_CHANNELS as u32 * $size as u32 * SAMPLE_RATE) { + thread::sleep(time::Duration::from_millis(10)); + } + }}; } - self.queue.queue(packet.samples()); + match self { + SdlSink::F32(queue) => { + drain_sink!(queue, mem::size_of::()); + queue.queue(packet.samples()) + } + SdlSink::S16(queue) => { + drain_sink!(queue, mem::size_of::()); + let samples_s16: Vec = AudioPacket::f32_to_s16(packet.samples()); + queue.queue(&samples_s16) + } + }; Ok(()) } } diff --git a/playback/src/audio_backend/subprocess.rs b/playback/src/audio_backend/subprocess.rs index 123e023..586bb75 100644 --- a/playback/src/audio_backend/subprocess.rs +++ b/playback/src/audio_backend/subprocess.rs @@ -1,22 +1,25 @@ -use super::{Open, Sink}; +use super::{Open, Sink, SinkAsBytes}; use crate::audio::AudioPacket; +use crate::config::AudioFormat; use shell_words::split; use std::io::{self, Write}; -use std::mem; use std::process::{Child, Command, Stdio}; -use std::slice; pub struct SubprocessSink { shell_command: String, child: Option, + format: AudioFormat, } impl Open for SubprocessSink { - fn open(shell_command: Option) -> SubprocessSink { + fn open(shell_command: Option, format: AudioFormat) -> SubprocessSink { + info!("Using subprocess sink with format: {:?}", format); + if let Some(shell_command) = shell_command { SubprocessSink { shell_command: shell_command, child: None, + format: format, } } else { panic!("subprocess sink requires specifying a shell command"); @@ -44,16 +47,15 @@ impl Sink for SubprocessSink { Ok(()) } - fn write(&mut self, packet: &AudioPacket) -> io::Result<()> { - let data: &[u8] = unsafe { - slice::from_raw_parts( - packet.samples().as_ptr() as *const u8, - packet.samples().len() * mem::size_of::(), - ) - }; + sink_as_bytes!(); +} + +impl SinkAsBytes for SubprocessSink { + fn write_bytes(&mut self, data: &[u8]) -> io::Result<()> { if let Some(child) = &mut self.child { let child_stdin = child.stdin.as_mut().unwrap(); child_stdin.write_all(data)?; + child_stdin.flush()?; } Ok(()) } diff --git a/playback/src/config.rs b/playback/src/config.rs index be15b26..e1ed8dc 100644 --- a/playback/src/config.rs +++ b/playback/src/config.rs @@ -1,3 +1,4 @@ +use std::convert::TryFrom; use std::str::FromStr; #[derive(Clone, Copy, Debug, Hash, PartialOrd, Ord, PartialEq, Eq)] @@ -25,6 +26,29 @@ impl Default for Bitrate { } } +#[derive(Clone, Copy, Debug, Hash, PartialOrd, Ord, PartialEq, Eq)] +pub enum AudioFormat { + F32, + S16, +} + +impl TryFrom<&String> for AudioFormat { + type Error = (); + fn try_from(s: &String) -> Result { + match s.to_uppercase().as_str() { + "F32" => Ok(AudioFormat::F32), + "S16" => Ok(AudioFormat::S16), + _ => unimplemented!(), + } + } +} + +impl Default for AudioFormat { + fn default() -> AudioFormat { + AudioFormat::F32 + } +} + #[derive(Clone, Debug)] pub enum NormalisationType { Album, diff --git a/playback/src/player.rs b/playback/src/player.rs index b6b8ad5..dbc0969 100644 --- a/playback/src/player.rs +++ b/playback/src/player.rs @@ -25,8 +25,12 @@ use crate::audio_backend::Sink; use crate::metadata::{AudioItem, FileFormat}; use crate::mixer::AudioFilter; +pub const SAMPLE_RATE: u32 = 44100; +pub const NUM_CHANNELS: u8 = 2; +pub const SAMPLES_PER_SECOND: u32 = SAMPLE_RATE as u32 * NUM_CHANNELS as u32; + const PRELOAD_NEXT_TRACK_BEFORE_END_DURATION_MS: u32 = 30000; -const SAMPLES_PER_SECOND: u32 = 44100 * 2; +const DB_VOLTAGE_RATIO: f32 = 20.0; pub struct Player { commands: Option>, @@ -202,11 +206,11 @@ pub struct NormalisationData { impl NormalisationData { pub fn db_to_ratio(db: f32) -> f32 { - return f32::powf(10.0, db / 20.0); + return f32::powf(10.0, db / DB_VOLTAGE_RATIO); } pub fn ratio_to_db(ratio: f32) -> f32 { - return ratio.log10() * 20.0; + return ratio.log10() * DB_VOLTAGE_RATIO; } fn parse_from_file(mut file: T) -> Result { @@ -937,8 +941,8 @@ impl Future for PlayerInternal { if !self.config.passthrough { if let Some(ref packet) = packet { - *stream_position_pcm = - *stream_position_pcm + (packet.samples().len() / 2) as u64; + *stream_position_pcm = *stream_position_pcm + + (packet.samples().len() / NUM_CHANNELS as usize) as u64; let stream_position_millis = Self::position_pcm_to_ms(*stream_position_pcm); diff --git a/src/main.rs b/src/main.rs index 91a5865..a7cd8b3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,6 +2,7 @@ use futures::sync::mpsc::UnboundedReceiver; use futures::{Async, Future, Poll, Stream}; use log::{error, info, trace, warn}; use sha1::{Digest, Sha1}; +use std::convert::TryFrom; use std::env; use std::io::{stderr, Write}; use std::mem; @@ -22,7 +23,9 @@ use librespot::core::version; use librespot::connect::discovery::{discovery, DiscoveryStream}; use librespot::connect::spirc::{Spirc, SpircTask}; use librespot::playback::audio_backend::{self, Sink, BACKENDS}; -use librespot::playback::config::{Bitrate, NormalisationMethod, NormalisationType, PlayerConfig}; +use librespot::playback::config::{ + AudioFormat, Bitrate, NormalisationMethod, NormalisationType, PlayerConfig, +}; use librespot::playback::mixer::{self, Mixer, MixerConfig}; use librespot::playback::player::{NormalisationData, Player, PlayerEvent}; @@ -85,7 +88,8 @@ fn print_version() { #[derive(Clone)] struct Setup { - backend: fn(Option) -> Box, + format: AudioFormat, + backend: fn(Option, AudioFormat) -> Box, device: Option, mixer: fn(Option) -> Box, @@ -149,6 +153,12 @@ fn setup(args: &[String]) -> Setup { "Audio device to use. Use '?' to list options if using portaudio or alsa", "DEVICE", ) + .optopt( + "", + "format", + "Output format (F32 or S16). Defaults to F32", + "FORMAT", + ) .optopt("", "mixer", "Mixer to use (alsa or softvol)", "MIXER") .optopt( "m", @@ -292,9 +302,15 @@ fn setup(args: &[String]) -> Setup { let backend = audio_backend::find(backend_name).expect("Invalid backend"); + let format = matches + .opt_str("format") + .as_ref() + .map(|format| AudioFormat::try_from(format).expect("Invalid output format")) + .unwrap_or(AudioFormat::default()); + let device = matches.opt_str("device"); if device == Some("?".into()) { - backend(device); + backend(device, format); exit(0); } @@ -496,6 +512,7 @@ fn setup(args: &[String]) -> Setup { let enable_discovery = !matches.opt_present("disable-discovery"); Setup { + format: format, backend: backend, cache: cache, session_config: session_config, @@ -517,7 +534,8 @@ struct Main { player_config: PlayerConfig, session_config: SessionConfig, connect_config: ConnectConfig, - backend: fn(Option) -> Box, + format: AudioFormat, + backend: fn(Option, AudioFormat) -> Box, device: Option, mixer: fn(Option) -> Box, mixer_config: MixerConfig, @@ -547,6 +565,7 @@ impl Main { session_config: setup.session_config, player_config: setup.player_config, connect_config: setup.connect_config, + format: setup.format, backend: setup.backend, device: setup.device, mixer: setup.mixer, @@ -626,11 +645,12 @@ impl Future for Main { let connect_config = self.connect_config.clone(); let audio_filter = mixer.get_audio_filter(); + let format = self.format; let backend = self.backend; let device = self.device.clone(); let (player, event_channel) = Player::new(player_config, session.clone(), audio_filter, move || { - (backend)(device) + (backend)(device, format) }); if self.emit_sink_events { From 6379926eb4c2dd786ccde075c6f1779aae3238ca Mon Sep 17 00:00:00 2001 From: Roderick van Domburg Date: Fri, 12 Mar 2021 23:18:18 +0100 Subject: [PATCH 063/103] Fix example --- examples/play.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/play.rs b/examples/play.rs index 4ba4c5b..2c239ef 100644 --- a/examples/play.rs +++ b/examples/play.rs @@ -5,7 +5,7 @@ use librespot::core::authentication::Credentials; use librespot::core::config::SessionConfig; use librespot::core::session::Session; use librespot::core::spotify_id::SpotifyId; -use librespot::playback::config::PlayerConfig; +use librespot::playback::config::{AudioFormat, PlayerConfig}; use librespot::playback::audio_backend; use librespot::playback::player::Player; @@ -16,6 +16,7 @@ fn main() { let session_config = SessionConfig::default(); let player_config = PlayerConfig::default(); + let audio_format = AudioFormat::default(); let args: Vec<_> = env::args().collect(); if args.len() != 4 { @@ -35,7 +36,7 @@ fn main() { .unwrap(); let (mut player, _) = Player::new(player_config, session.clone(), None, move || { - (backend)(None) + (backend)(None, audio_format) }); player.load(track, true, 0); From a4ef174fd00bac3d2be779653ccf35617aa0f379 Mon Sep 17 00:00:00 2001 From: Roderick van Domburg Date: Fri, 12 Mar 2021 23:48:41 +0100 Subject: [PATCH 064/103] Fix Alsa backend for 64-bit systems --- playback/src/audio_backend/alsa.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/playback/src/audio_backend/alsa.rs b/playback/src/audio_backend/alsa.rs index 92b71f4..ce758dc 100644 --- a/playback/src/audio_backend/alsa.rs +++ b/playback/src/audio_backend/alsa.rs @@ -11,7 +11,7 @@ use std::process::exit; use std::{io, mem}; const BUFFERED_LATENCY: f32 = 0.125; // seconds -const BUFFERED_PERIODS: u8 = 4; +const BUFFERED_PERIODS: Frames = 4; pub struct AlsaSink { pcm: Option, @@ -48,7 +48,7 @@ fn open_device(dev_name: &str, format: AudioFormat) -> Result<(PCM, Frames), Box // latency = period_size * periods / (rate * bytes_per_frame) // For stereo samples encoded as 32-bit float, one frame has a length of eight bytes. let mut period_size = ((SAMPLES_PER_SECOND * sample_size as u32) as f32 - * (BUFFERED_LATENCY / BUFFERED_PERIODS as f32)) as i32; + * (BUFFERED_LATENCY / BUFFERED_PERIODS as f32)) as Frames; // Set hardware parameters: 44100 Hz / stereo / 32-bit float or 16-bit signed integer { @@ -58,7 +58,7 @@ fn open_device(dev_name: &str, format: AudioFormat) -> Result<(PCM, Frames), Box hwp.set_rate(SAMPLE_RATE, ValueOr::Nearest)?; hwp.set_channels(NUM_CHANNELS as u32)?; period_size = hwp.set_period_size_near(period_size, ValueOr::Greater)?; - hwp.set_buffer_size_near(period_size * BUFFERED_PERIODS as i32)?; + hwp.set_buffer_size_near(period_size * BUFFERED_PERIODS)?; pcm.hw_params(&hwp)?; let swp = pcm.sw_params_current()?; @@ -101,8 +101,7 @@ impl Sink for AlsaSink { Ok((p, period_size)) => { self.pcm = Some(p); // Create a buffer for all samples for a full period - self.buffer = - Vec::with_capacity((period_size * BUFFERED_PERIODS as i32) as usize); + self.buffer = Vec::with_capacity((period_size * BUFFERED_PERIODS) as usize); } Err(e) => { error!("Alsa error PCM open {}", e); From 5f26a745d7dbe085cb52f4e433ae3438f54c5f95 Mon Sep 17 00:00:00 2001 From: Roderick van Domburg Date: Sat, 13 Mar 2021 23:43:24 +0100 Subject: [PATCH 065/103] Add support for S32 output format While at it, add a small tweak when converting "silent" samples from float to integer. This ensures 0.0 converts to 0 and vice versa. --- audio/src/lib.rs | 25 ++++++++++++++++++++---- audio/src/libvorbis_decoder.rs | 8 +++++++- playback/src/audio_backend/alsa.rs | 6 ++++++ playback/src/audio_backend/mod.rs | 4 ++++ playback/src/audio_backend/portaudio.rs | 19 ++++++++++++++---- playback/src/audio_backend/pulseaudio.rs | 1 + playback/src/audio_backend/rodio.rs | 23 ++++++++++++++-------- playback/src/audio_backend/sdl.rs | 9 +++++++++ playback/src/config.rs | 2 ++ src/main.rs | 2 +- 10 files changed, 81 insertions(+), 18 deletions(-) diff --git a/audio/src/lib.rs b/audio/src/lib.rs index 8a9f88f..cafadae 100644 --- a/audio/src/lib.rs +++ b/audio/src/lib.rs @@ -37,6 +37,22 @@ pub enum AudioPacket { OggData(Vec), } +// Losslessly represent [-1.0, 1.0] to [$type::MIN, $type::MAX] while maintaining DC linearity. +macro_rules! convert_samples_to { + ($type: ident, $samples: expr) => { + $samples + .iter() + .map(|sample| { + if *sample == 0.0 { + 0 as $type + } else { + (*sample as f64 * (std::$type::MAX as f64 + 0.5) - 0.5) as $type + } + }) + .collect() + }; +} + impl AudioPacket { pub fn samples(&self) -> &[f32] { match self { @@ -59,11 +75,12 @@ impl AudioPacket { } } + pub fn f32_to_s32(samples: &[f32]) -> Vec { + convert_samples_to!(i32, samples) + } + pub fn f32_to_s16(samples: &[f32]) -> Vec { - samples - .iter() - .map(|sample| (*sample as f64 * (0x7FFF as f64 + 0.5) - 0.5) as i16) - .collect() + convert_samples_to!(i16, samples) } } diff --git a/audio/src/libvorbis_decoder.rs b/audio/src/libvorbis_decoder.rs index e7ccc98..449caae 100644 --- a/audio/src/libvorbis_decoder.rs +++ b/audio/src/libvorbis_decoder.rs @@ -45,7 +45,13 @@ where packet .data .iter() - .map(|sample| ((*sample as f64 + 0.5) / (0x7FFF as f64 + 0.5)) as f32) + .map(|sample| { + if *sample == 0 { + 0.0 + } else { + ((*sample as f64 + 0.5) / (0x7FFF as f64 + 0.5)) as f32 + } + }) .collect(), ))); } diff --git a/playback/src/audio_backend/alsa.rs b/playback/src/audio_backend/alsa.rs index ce758dc..4d9f19e 100644 --- a/playback/src/audio_backend/alsa.rs +++ b/playback/src/audio_backend/alsa.rs @@ -41,6 +41,7 @@ fn open_device(dev_name: &str, format: AudioFormat) -> Result<(PCM, Frames), Box let pcm = PCM::new(dev_name, Direction::Playback, false)?; let (alsa_format, sample_size) = match format { AudioFormat::F32 => (Format::float(), mem::size_of::()), + AudioFormat::S32 => (Format::s32(), mem::size_of::()), AudioFormat::S16 => (Format::s16(), mem::size_of::()), }; @@ -157,6 +158,11 @@ impl AlsaSink { let io = pcm.io_f32().unwrap(); io.writei(&self.buffer) } + AudioFormat::S32 => { + let io = pcm.io_i32().unwrap(); + let buf_s32: Vec = AudioPacket::f32_to_s32(&self.buffer); + io.writei(&buf_s32[..]) + } AudioFormat::S16 => { let io = pcm.io_i16().unwrap(); let buf_s16: Vec = AudioPacket::f32_to_s16(&self.buffer); diff --git a/playback/src/audio_backend/mod.rs b/playback/src/audio_backend/mod.rs index 550ebb8..bc10e88 100644 --- a/playback/src/audio_backend/mod.rs +++ b/playback/src/audio_backend/mod.rs @@ -28,6 +28,10 @@ macro_rules! sink_as_bytes { match packet { AudioPacket::Samples(samples) => match self.format { AudioFormat::F32 => self.write_bytes(samples.as_bytes()), + AudioFormat::S32 => { + let samples_s32 = AudioPacket::f32_to_s32(samples); + self.write_bytes(samples_s32.as_bytes()) + } AudioFormat::S16 => { let samples_s16 = AudioPacket::f32_to_s16(samples); self.write_bytes(samples_s16.as_bytes()) diff --git a/playback/src/audio_backend/portaudio.rs b/playback/src/audio_backend/portaudio.rs index 70caedd..a7aa38c 100644 --- a/playback/src/audio_backend/portaudio.rs +++ b/playback/src/audio_backend/portaudio.rs @@ -14,6 +14,10 @@ pub enum PortAudioSink<'a> { Option>, StreamParameters, ), + S32( + Option>, + StreamParameters, + ), S16( Option>, StreamParameters, @@ -70,19 +74,20 @@ impl<'a> Open for PortAudioSink<'a> { }; macro_rules! open_sink { - ($sink: expr, $data: expr) => {{ + ($sink: expr, $type: ty) => {{ let params = StreamParameters { device: device_idx, channel_count: NUM_CHANNELS as u32, suggested_latency: latency, - data: $data, + data: 0.0 as $type, }; $sink(None, params) }}; } match format { - AudioFormat::F32 => open_sink!(PortAudioSink::F32, 0.0), - AudioFormat::S16 => open_sink!(PortAudioSink::S16, 0), + AudioFormat::F32 => open_sink!(PortAudioSink::F32, f32), + AudioFormat::S32 => open_sink!(PortAudioSink::S32, i32), + AudioFormat::S16 => open_sink!(PortAudioSink::S16, i16), } } } @@ -109,6 +114,7 @@ impl<'a> Sink for PortAudioSink<'a> { } match self { PortAudioSink::F32(stream, parameters) => start_sink!(stream, parameters), + PortAudioSink::S32(stream, parameters) => start_sink!(stream, parameters), PortAudioSink::S16(stream, parameters) => start_sink!(stream, parameters), }; @@ -124,6 +130,7 @@ impl<'a> Sink for PortAudioSink<'a> { } match self { PortAudioSink::F32(stream, _parameters) => stop_sink!(stream), + PortAudioSink::S32(stream, _parameters) => stop_sink!(stream), PortAudioSink::S16(stream, _parameters) => stop_sink!(stream), }; @@ -141,6 +148,10 @@ impl<'a> Sink for PortAudioSink<'a> { let samples = packet.samples(); write_sink!(stream, &samples) } + PortAudioSink::S32(stream, _parameters) => { + let samples_s32: Vec = AudioPacket::f32_to_s32(packet.samples()); + write_sink!(stream, &samples_s32) + } PortAudioSink::S16(stream, _parameters) => { let samples_s16: Vec = AudioPacket::f32_to_s16(packet.samples()); write_sink!(stream, &samples_s16) diff --git a/playback/src/audio_backend/pulseaudio.rs b/playback/src/audio_backend/pulseaudio.rs index 8c1e8e8..a2d89f2 100644 --- a/playback/src/audio_backend/pulseaudio.rs +++ b/playback/src/audio_backend/pulseaudio.rs @@ -22,6 +22,7 @@ impl Open for PulseAudioSink { let pulse_format = match format { AudioFormat::F32 => pulse::sample::Format::F32le, + AudioFormat::S32 => pulse::sample::Format::S32le, AudioFormat::S16 => pulse::sample::Format::S16le, }; diff --git a/playback/src/audio_backend/rodio.rs b/playback/src/audio_backend/rodio.rs index 7571aa2..97e03ec 100644 --- a/playback/src/audio_backend/rodio.rs +++ b/playback/src/audio_backend/rodio.rs @@ -7,6 +7,8 @@ use cpal::traits::{DeviceTrait, HostTrait}; use std::process::exit; use std::{io, thread, time}; +const FORMAT_NOT_SUPPORTED: &'static str = "Rodio currently does not support that output format"; + // most code is shared between RodioSink and JackRodioSink macro_rules! rodio_sink { ($name: ident) => { @@ -27,12 +29,13 @@ macro_rules! rodio_sink { AudioFormat::F32 => { let source = rodio::buffer::SamplesBuffer::new(2, 44100, samples); self.rodio_sink.append(source) - } + }, AudioFormat::S16 => { let samples_s16: Vec = AudioPacket::f32_to_s16(samples); let source = rodio::buffer::SamplesBuffer::new(2, 44100, samples_s16); self.rodio_sink.append(source) - } + }, + _ => panic!(FORMAT_NOT_SUPPORTED), }; // Chunk sizes seem to be about 256 to 3000 ish items long. @@ -48,12 +51,16 @@ macro_rules! rodio_sink { impl $name { fn open_sink(host: &cpal::Host, device: Option, format: AudioFormat) -> $name { - if format != AudioFormat::S16 { - #[cfg(target_os = "linux")] - { - warn!("Rodio output to Alsa is known to cause garbled sound on output formats other than 16-bit signed integer."); - warn!("Consider using `--backend alsa` OR `--format {:?}`", AudioFormat::S16); - } + match format { + AudioFormat::F32 => { + #[cfg(target_os = "linux")] + { + warn!("Rodio output to Alsa is known to cause garbled sound on output formats other than 16-bit signed integer."); + warn!("Consider using `--backend alsa` OR `--format {:?}`", AudioFormat::S16); + } + }, + AudioFormat::S16 => {}, + _ => panic!(FORMAT_NOT_SUPPORTED), } let rodio_device = match_device(&host, device); diff --git a/playback/src/audio_backend/sdl.rs b/playback/src/audio_backend/sdl.rs index 6e52b32..ef8c183 100644 --- a/playback/src/audio_backend/sdl.rs +++ b/playback/src/audio_backend/sdl.rs @@ -7,6 +7,7 @@ use std::{io, mem, thread, time}; pub enum SdlSink { F32(AudioQueue), + S32(AudioQueue), S16(AudioQueue), } @@ -39,6 +40,7 @@ impl Open for SdlSink { } match format { AudioFormat::F32 => open_sink!(SdlSink::F32, f32), + AudioFormat::S32 => open_sink!(SdlSink::S32, i32), AudioFormat::S16 => open_sink!(SdlSink::S16, i16), } } @@ -54,6 +56,7 @@ impl Sink for SdlSink { } match self { SdlSink::F32(queue) => start_sink!(queue), + SdlSink::S32(queue) => start_sink!(queue), SdlSink::S16(queue) => start_sink!(queue), }; Ok(()) @@ -68,6 +71,7 @@ impl Sink for SdlSink { } match self { SdlSink::F32(queue) => stop_sink!(queue), + SdlSink::S32(queue) => stop_sink!(queue), SdlSink::S16(queue) => stop_sink!(queue), }; Ok(()) @@ -87,6 +91,11 @@ impl Sink for SdlSink { drain_sink!(queue, mem::size_of::()); queue.queue(packet.samples()) } + SdlSink::S32(queue) => { + drain_sink!(queue, mem::size_of::()); + let samples_s32: Vec = AudioPacket::f32_to_s32(packet.samples()); + queue.queue(&samples_s32) + } SdlSink::S16(queue) => { drain_sink!(queue, mem::size_of::()); let samples_s16: Vec = AudioPacket::f32_to_s16(packet.samples()); diff --git a/playback/src/config.rs b/playback/src/config.rs index e1ed8dc..8077158 100644 --- a/playback/src/config.rs +++ b/playback/src/config.rs @@ -29,6 +29,7 @@ impl Default for Bitrate { #[derive(Clone, Copy, Debug, Hash, PartialOrd, Ord, PartialEq, Eq)] pub enum AudioFormat { F32, + S32, S16, } @@ -37,6 +38,7 @@ impl TryFrom<&String> for AudioFormat { fn try_from(s: &String) -> Result { match s.to_uppercase().as_str() { "F32" => Ok(AudioFormat::F32), + "S32" => Ok(AudioFormat::S32), "S16" => Ok(AudioFormat::S16), _ => unimplemented!(), } diff --git a/src/main.rs b/src/main.rs index a7cd8b3..b4cfc43 100644 --- a/src/main.rs +++ b/src/main.rs @@ -156,7 +156,7 @@ fn setup(args: &[String]) -> Setup { .optopt( "", "format", - "Output format (F32 or S16). Defaults to F32", + "Output format (F32, S32 or S16). Defaults to F32", "FORMAT", ) .optopt("", "mixer", "Mixer to use (alsa or softvol)", "MIXER") From 309e26456ef2d381cf0a1338ec45fac2f3b25665 Mon Sep 17 00:00:00 2001 From: Roderick van Domburg Date: Sun, 14 Mar 2021 14:28:16 +0100 Subject: [PATCH 066/103] Rename steepness to knee --- playback/src/config.rs | 4 ++-- playback/src/player.rs | 9 +++------ src/main.rs | 18 +++++++----------- 3 files changed, 12 insertions(+), 19 deletions(-) diff --git a/playback/src/config.rs b/playback/src/config.rs index 8077158..312f170 100644 --- a/playback/src/config.rs +++ b/playback/src/config.rs @@ -107,7 +107,7 @@ pub struct PlayerConfig { pub normalisation_threshold: f32, pub normalisation_attack: f32, pub normalisation_release: f32, - pub normalisation_steepness: f32, + pub normalisation_knee: f32, pub gapless: bool, pub passthrough: bool, } @@ -123,7 +123,7 @@ impl Default for PlayerConfig { normalisation_threshold: -1.0, normalisation_attack: 0.005, normalisation_release: 0.1, - normalisation_steepness: 1.0, + normalisation_knee: 1.0, gapless: true, passthrough: false, } diff --git a/playback/src/player.rs b/playback/src/player.rs index dbc0969..0a573c9 100644 --- a/playback/src/player.rs +++ b/playback/src/player.rs @@ -271,10 +271,7 @@ impl NormalisationData { if config.normalisation_method == NormalisationMethod::Dynamic { debug!("Normalisation Attack: {:?}", config.normalisation_attack); debug!("Normalisation Release: {:?}", config.normalisation_release); - debug!( - "Normalisation Steepness: {:?}", - config.normalisation_steepness - ); + debug!("Normalisation Knee: {:?}", config.normalisation_knee); } normalisation_factor @@ -1176,7 +1173,7 @@ impl PlayerInternal { if self.config.normalisation_method == NormalisationMethod::Dynamic { if self.limiter_active { - // "S"-shaped curve with a configurable steepness during attack and release: + // "S"-shaped curve with a configurable knee during attack and release: // - > 1.0 yields soft knees at start and end, steeper in between // - 1.0 yields a linear function from 0-100% // - between 0.0 and 1.0 yields hard knees at start and end, flatter in between @@ -1191,7 +1188,7 @@ impl PlayerInternal { + f32::powf( shaped_limiter_strength / (1.0 - shaped_limiter_strength), - -1.0 * self.config.normalisation_steepness, + -1.0 * self.config.normalisation_knee, )); } actual_normalisation_factor = diff --git a/src/main.rs b/src/main.rs index b4cfc43..bf553a8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -238,9 +238,9 @@ fn setup(args: &[String]) -> Setup { ) .optopt( "", - "normalisation-steepness", - "Steepness of the dynamic limiting curve. Default is 1.0.", - "STEEPNESS", + "normalisation-knee", + "Knee steepness of the dynamic limiter. Default is 1.0.", + "KNEE", ) .optopt( "", @@ -475,14 +475,10 @@ fn setup(args: &[String]) -> Setup { .map(|release| release.parse::().expect("Invalid release float value")) .unwrap_or(PlayerConfig::default().normalisation_release * MILLIS) / MILLIS, - normalisation_steepness: matches - .opt_str("normalisation-steepness") - .map(|steepness| { - steepness - .parse::() - .expect("Invalid steepness float value") - }) - .unwrap_or(PlayerConfig::default().normalisation_steepness), + normalisation_knee: matches + .opt_str("normalisation-knee") + .map(|knee| knee.parse::().expect("Invalid knee float value")) + .unwrap_or(PlayerConfig::default().normalisation_knee), passthrough, } }; From 9dcaeee6d445c0942eac6dd8abc74a2f53486c17 Mon Sep 17 00:00:00 2001 From: Roderick van Domburg Date: Tue, 16 Mar 2021 20:22:00 +0100 Subject: [PATCH 067/103] Default to S16 output --- playback/src/audio_backend/jackaudio.rs | 8 ++------ playback/src/config.rs | 2 +- src/main.rs | 2 +- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/playback/src/audio_backend/jackaudio.rs b/playback/src/audio_backend/jackaudio.rs index 295941a..2412d07 100644 --- a/playback/src/audio_backend/jackaudio.rs +++ b/playback/src/audio_backend/jackaudio.rs @@ -42,14 +42,10 @@ impl ProcessHandler for JackData { impl Open for JackSink { fn open(client_name: Option, format: AudioFormat) -> JackSink { - info!("Using JACK sink with format {:?}", format); - if format != AudioFormat::F32 { - panic!( - "JACK sink only supports 32-bit floating point output. Use `--format {:?}`", - AudioFormat::F32 - ); + warn!("JACK currently does not support {:?} output", format); } + info!("Using JACK sink with format {:?}", AudioFormat::F32); let client_name = client_name.unwrap_or("librespot".to_string()); let (client, _status) = diff --git a/playback/src/config.rs b/playback/src/config.rs index 312f170..7348b7b 100644 --- a/playback/src/config.rs +++ b/playback/src/config.rs @@ -47,7 +47,7 @@ impl TryFrom<&String> for AudioFormat { impl Default for AudioFormat { fn default() -> AudioFormat { - AudioFormat::F32 + AudioFormat::S16 } } diff --git a/src/main.rs b/src/main.rs index bf553a8..07b85b3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -156,7 +156,7 @@ fn setup(args: &[String]) -> Setup { .optopt( "", "format", - "Output format (F32, S32 or S16). Defaults to F32", + "Output format (F32, S32 or S16). Defaults to S16", "FORMAT", ) .optopt("", "mixer", "Mixer to use (alsa or softvol)", "MIXER") From 770ea15498a0f1cfc7b9986f0954f4150258c29f Mon Sep 17 00:00:00 2001 From: Roderick van Domburg Date: Wed, 17 Mar 2021 00:00:27 +0100 Subject: [PATCH 068/103] Add support for S24 and S24_3 output formats --- Cargo.lock | 1 + audio/Cargo.toml | 1 + audio/src/lib.rs | 33 ++++++++++++--- playback/src/audio_backend/alsa.rs | 52 ++++++++++-------------- playback/src/audio_backend/gstreamer.rs | 19 ++++++--- playback/src/audio_backend/jackaudio.rs | 2 +- playback/src/audio_backend/mod.rs | 8 ++++ playback/src/audio_backend/pipe.rs | 2 +- playback/src/audio_backend/portaudio.rs | 38 +++++++++++------ playback/src/audio_backend/pulseaudio.rs | 5 ++- playback/src/audio_backend/rodio.rs | 8 ++-- playback/src/audio_backend/sdl.rs | 36 ++++++++++------ playback/src/config.rs | 28 ++++++++++--- src/main.rs | 2 +- 14 files changed, 155 insertions(+), 80 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9a2e42e..2296cfe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1473,6 +1473,7 @@ dependencies = [ "ogg", "tempfile", "vorbis", + "zerocopy", ] [[package]] diff --git a/audio/Cargo.toml b/audio/Cargo.toml index b7e6e35..06f1dda 100644 --- a/audio/Cargo.toml +++ b/audio/Cargo.toml @@ -22,6 +22,7 @@ log = "0.4" num-bigint = "0.3" num-traits = "0.2" tempfile = "3.1" +zerocopy = "0.3" librespot-tremor = { version = "0.2.0", optional = true } vorbis = { version ="0.0.14", optional = true } diff --git a/audio/src/lib.rs b/audio/src/lib.rs index cafadae..86c5b4a 100644 --- a/audio/src/lib.rs +++ b/audio/src/lib.rs @@ -31,23 +31,35 @@ pub use fetch::{ READ_AHEAD_DURING_PLAYBACK_ROUNDTRIPS, READ_AHEAD_DURING_PLAYBACK_SECONDS, }; use std::fmt; +use zerocopy::AsBytes; pub enum AudioPacket { Samples(Vec), OggData(Vec), } +#[derive(AsBytes, Copy, Clone, Debug)] +#[allow(non_camel_case_types)] +#[repr(transparent)] +pub struct i24([u8; 3]); +impl i24 { + fn pcm_from_i32(sample: i32) -> Self { + // drop the least significant byte + let [a, b, c, _d] = (sample >> 8).to_le_bytes(); + i24([a, b, c]) + } +} + // Losslessly represent [-1.0, 1.0] to [$type::MIN, $type::MAX] while maintaining DC linearity. macro_rules! convert_samples_to { ($type: ident, $samples: expr) => { + convert_samples_to!($type, $samples, 0) + }; + ($type: ident, $samples: expr, $shift: expr) => { $samples .iter() .map(|sample| { - if *sample == 0.0 { - 0 as $type - } else { - (*sample as f64 * (std::$type::MAX as f64 + 0.5) - 0.5) as $type - } + (*sample as f64 * (std::$type::MAX as f64 + 0.5) - 0.5) as $type >> $shift }) .collect() }; @@ -79,6 +91,17 @@ impl AudioPacket { convert_samples_to!(i32, samples) } + pub fn f32_to_s24(samples: &[f32]) -> Vec { + convert_samples_to!(i32, samples, 8) + } + + pub fn f32_to_s24_3(samples: &[f32]) -> Vec { + Self::f32_to_s32(samples) + .iter() + .map(|sample| i24::pcm_from_i32(*sample)) + .collect() + } + pub fn f32_to_s16(samples: &[f32]) -> Vec { convert_samples_to!(i16, samples) } diff --git a/playback/src/audio_backend/alsa.rs b/playback/src/audio_backend/alsa.rs index 4d9f19e..35d0ab1 100644 --- a/playback/src/audio_backend/alsa.rs +++ b/playback/src/audio_backend/alsa.rs @@ -1,4 +1,4 @@ -use super::{Open, Sink}; +use super::{Open, Sink, SinkAsBytes}; use crate::audio::AudioPacket; use crate::config::AudioFormat; use crate::player::{NUM_CHANNELS, SAMPLES_PER_SECOND, SAMPLE_RATE}; @@ -7,8 +7,8 @@ use alsa::pcm::{Access, Format, Frames, HwParams, PCM}; use alsa::{Direction, Error, ValueOr}; use std::cmp::min; use std::ffi::CString; +use std::io; use std::process::exit; -use std::{io, mem}; const BUFFERED_LATENCY: f32 = 0.125; // seconds const BUFFERED_PERIODS: Frames = 4; @@ -17,7 +17,7 @@ pub struct AlsaSink { pcm: Option, format: AudioFormat, device: String, - buffer: Vec, + buffer: Vec, } fn list_outputs() { @@ -39,16 +39,18 @@ fn list_outputs() { fn open_device(dev_name: &str, format: AudioFormat) -> Result<(PCM, Frames), Box> { let pcm = PCM::new(dev_name, Direction::Playback, false)?; - let (alsa_format, sample_size) = match format { - AudioFormat::F32 => (Format::float(), mem::size_of::()), - AudioFormat::S32 => (Format::s32(), mem::size_of::()), - AudioFormat::S16 => (Format::s16(), mem::size_of::()), + let alsa_format = match format { + AudioFormat::F32 => Format::float(), + AudioFormat::S32 => Format::s32(), + AudioFormat::S24 => Format::s24(), + AudioFormat::S24_3 => Format::S243LE, + AudioFormat::S16 => Format::s16(), }; // http://www.linuxjournal.com/article/6735?page=0,1#N0x19ab2890.0x19ba78d8 // latency = period_size * periods / (rate * bytes_per_frame) // For stereo samples encoded as 32-bit float, one frame has a length of eight bytes. - let mut period_size = ((SAMPLES_PER_SECOND * sample_size as u32) as f32 + let mut period_size = ((SAMPLES_PER_SECOND * format.size() as u32) as f32 * (BUFFERED_LATENCY / BUFFERED_PERIODS as f32)) as Frames; // Set hardware parameters: 44100 Hz / stereo / 32-bit float or 16-bit signed integer @@ -85,7 +87,7 @@ impl Open for AlsaSink { } .to_string(); - AlsaSink { + Self { pcm: None, format: format, device: name, @@ -102,7 +104,9 @@ impl Sink for AlsaSink { Ok((p, period_size)) => { self.pcm = Some(p); // Create a buffer for all samples for a full period - self.buffer = Vec::with_capacity((period_size * BUFFERED_PERIODS) as usize); + self.buffer = Vec::with_capacity( + period_size as usize * BUFFERED_PERIODS as usize * self.format.size(), + ); } Err(e) => { error!("Alsa error PCM open {}", e); @@ -121,7 +125,7 @@ impl Sink for AlsaSink { { // Write any leftover data in the period buffer // before draining the actual buffer - self.write_buf().expect("could not flush buffer"); + self.write_bytes(&[]).expect("could not flush buffer"); let pcm = self.pcm.as_mut().unwrap(); pcm.drain().unwrap(); } @@ -129,9 +133,12 @@ impl Sink for AlsaSink { Ok(()) } - fn write(&mut self, packet: &AudioPacket) -> io::Result<()> { + sink_as_bytes!(); +} + +impl SinkAsBytes for AlsaSink { + fn write_bytes(&mut self, data: &[u8]) -> io::Result<()> { let mut processed_data = 0; - let data = packet.samples(); while processed_data < data.len() { let data_to_buffer = min( self.buffer.capacity() - self.buffer.len(), @@ -153,23 +160,8 @@ impl Sink for AlsaSink { impl AlsaSink { fn write_buf(&mut self) -> io::Result<()> { let pcm = self.pcm.as_mut().unwrap(); - let io_result = match self.format { - AudioFormat::F32 => { - let io = pcm.io_f32().unwrap(); - io.writei(&self.buffer) - } - AudioFormat::S32 => { - let io = pcm.io_i32().unwrap(); - let buf_s32: Vec = AudioPacket::f32_to_s32(&self.buffer); - io.writei(&buf_s32[..]) - } - AudioFormat::S16 => { - let io = pcm.io_i16().unwrap(); - let buf_s16: Vec = AudioPacket::f32_to_s16(&self.buffer); - io.writei(&buf_s16[..]) - } - }; - match io_result { + let io = pcm.io_bytes(); + match io.writei(&self.buffer) { Ok(_) => (), Err(err) => pcm.try_recover(err, false).unwrap(), }; diff --git a/playback/src/audio_backend/gstreamer.rs b/playback/src/audio_backend/gstreamer.rs index 17ad86e..3695857 100644 --- a/playback/src/audio_backend/gstreamer.rs +++ b/playback/src/audio_backend/gstreamer.rs @@ -18,11 +18,18 @@ pub struct GstreamerSink { impl Open for GstreamerSink { fn open(device: Option, format: AudioFormat) -> GstreamerSink { info!("Using GStreamer sink with format: {:?}", format); - gst::init().expect("failed to init GStreamer!"); + + // GStreamer calls S24 and S24_3 different from the rest of the world + let gst_format = match format { + AudioFormat::S24 => "S24_32".to_string(), + AudioFormat::S24_3 => "S24".to_string(), + _ => format!("{:?}", format), + }; + let pipeline_str_preamble = format!( - r#"appsrc caps="audio/x-raw,format={:?},layout=interleaved,channels={},rate={}" block=true max-bytes=4096 name=appsrc0 "#, - format, NUM_CHANNELS, SAMPLE_RATE + "appsrc caps=\"audio/x-raw,format={}LE,layout=interleaved,channels={},rate={}\" block=true max-bytes=4096 name=appsrc0 ", + gst_format, NUM_CHANNELS, SAMPLE_RATE ); let pipeline_str_rest = r#" ! audioconvert ! autoaudiosink"#; let pipeline_str: String = match device { @@ -47,7 +54,7 @@ impl Open for GstreamerSink { let bufferpool = gst::BufferPool::new(); let appsrc_caps = appsrc.get_caps().expect("couldn't get appsrc caps"); let mut conf = bufferpool.get_config(); - conf.set_params(Some(&appsrc_caps), 8192, 0, 0); + conf.set_params(Some(&appsrc_caps), 2048 * format.size() as u32, 0, 0); bufferpool .set_config(conf) .expect("couldn't configure the buffer pool"); @@ -55,7 +62,7 @@ impl Open for GstreamerSink { .set_active(true) .expect("couldn't activate buffer pool"); - let (tx, rx) = sync_channel::>(128); + let (tx, rx) = sync_channel::>(64 * format.size()); thread::spawn(move || { for data in rx { let buffer = bufferpool.acquire_buffer(None); @@ -99,7 +106,7 @@ impl Open for GstreamerSink { .set_state(gst::State::Playing) .expect("unable to set the pipeline to the `Playing` state"); - GstreamerSink { + Self { tx: tx, pipeline: pipeline, format: format, diff --git a/playback/src/audio_backend/jackaudio.rs b/playback/src/audio_backend/jackaudio.rs index 2412d07..05c6c31 100644 --- a/playback/src/audio_backend/jackaudio.rs +++ b/playback/src/audio_backend/jackaudio.rs @@ -61,7 +61,7 @@ impl Open for JackSink { }; let active_client = AsyncClient::new(client, (), jack_data).unwrap(); - JackSink { + Self { send: tx, active_client: active_client, } diff --git a/playback/src/audio_backend/mod.rs b/playback/src/audio_backend/mod.rs index bc10e88..9c46dbe 100644 --- a/playback/src/audio_backend/mod.rs +++ b/playback/src/audio_backend/mod.rs @@ -32,6 +32,14 @@ macro_rules! sink_as_bytes { let samples_s32 = AudioPacket::f32_to_s32(samples); self.write_bytes(samples_s32.as_bytes()) } + AudioFormat::S24 => { + let samples_s24 = AudioPacket::f32_to_s24(samples); + self.write_bytes(samples_s24.as_bytes()) + } + AudioFormat::S24_3 => { + let samples_s24_3 = AudioPacket::f32_to_s24_3(samples); + self.write_bytes(samples_s24_3.as_bytes()) + } AudioFormat::S16 => { let samples_s16 = AudioPacket::f32_to_s16(samples); self.write_bytes(samples_s16.as_bytes()) diff --git a/playback/src/audio_backend/pipe.rs b/playback/src/audio_backend/pipe.rs index 3a90d06..ae77e32 100644 --- a/playback/src/audio_backend/pipe.rs +++ b/playback/src/audio_backend/pipe.rs @@ -18,7 +18,7 @@ impl Open for StdoutSink { _ => Box::new(io::stdout()), }; - StdoutSink { + Self { output: output, format: format, } diff --git a/playback/src/audio_backend/portaudio.rs b/playback/src/audio_backend/portaudio.rs index a7aa38c..213f2d0 100644 --- a/playback/src/audio_backend/portaudio.rs +++ b/playback/src/audio_backend/portaudio.rs @@ -18,6 +18,10 @@ pub enum PortAudioSink<'a> { Option>, StreamParameters, ), + S24( + Option>, + StreamParameters, + ), S16( Option>, StreamParameters, @@ -85,9 +89,13 @@ impl<'a> Open for PortAudioSink<'a> { }}; } match format { - AudioFormat::F32 => open_sink!(PortAudioSink::F32, f32), - AudioFormat::S32 => open_sink!(PortAudioSink::S32, i32), - AudioFormat::S16 => open_sink!(PortAudioSink::S16, i16), + AudioFormat::F32 => open_sink!(Self::F32, f32), + AudioFormat::S32 => open_sink!(Self::S32, i32), + AudioFormat::S24 => open_sink!(Self::S24, i32), + AudioFormat::S24_3 => { + unimplemented!("PortAudio currently does not support S24_3 output") + } + AudioFormat::S16 => open_sink!(Self::S16, i16), } } } @@ -113,9 +121,10 @@ impl<'a> Sink for PortAudioSink<'a> { }}; } match self { - PortAudioSink::F32(stream, parameters) => start_sink!(stream, parameters), - PortAudioSink::S32(stream, parameters) => start_sink!(stream, parameters), - PortAudioSink::S16(stream, parameters) => start_sink!(stream, parameters), + Self::F32(stream, parameters) => start_sink!(stream, parameters), + Self::S32(stream, parameters) => start_sink!(stream, parameters), + Self::S24(stream, parameters) => start_sink!(stream, parameters), + Self::S16(stream, parameters) => start_sink!(stream, parameters), }; Ok(()) @@ -129,9 +138,10 @@ impl<'a> Sink for PortAudioSink<'a> { }}; } match self { - PortAudioSink::F32(stream, _parameters) => stop_sink!(stream), - PortAudioSink::S32(stream, _parameters) => stop_sink!(stream), - PortAudioSink::S16(stream, _parameters) => stop_sink!(stream), + Self::F32(stream, _parameters) => stop_sink!(stream), + Self::S32(stream, _parameters) => stop_sink!(stream), + Self::S24(stream, _parameters) => stop_sink!(stream), + Self::S16(stream, _parameters) => stop_sink!(stream), }; Ok(()) @@ -144,15 +154,19 @@ impl<'a> Sink for PortAudioSink<'a> { }; } let result = match self { - PortAudioSink::F32(stream, _parameters) => { + Self::F32(stream, _parameters) => { let samples = packet.samples(); write_sink!(stream, &samples) } - PortAudioSink::S32(stream, _parameters) => { + Self::S32(stream, _parameters) => { let samples_s32: Vec = AudioPacket::f32_to_s32(packet.samples()); write_sink!(stream, &samples_s32) } - PortAudioSink::S16(stream, _parameters) => { + Self::S24(stream, _parameters) => { + let samples_s24: Vec = AudioPacket::f32_to_s24(packet.samples()); + write_sink!(stream, &samples_s24) + } + Self::S16(stream, _parameters) => { let samples_s16: Vec = AudioPacket::f32_to_s16(packet.samples()); write_sink!(stream, &samples_s16) } diff --git a/playback/src/audio_backend/pulseaudio.rs b/playback/src/audio_backend/pulseaudio.rs index a2d89f2..16800eb 100644 --- a/playback/src/audio_backend/pulseaudio.rs +++ b/playback/src/audio_backend/pulseaudio.rs @@ -20,9 +20,12 @@ impl Open for PulseAudioSink { fn open(device: Option, format: AudioFormat) -> PulseAudioSink { info!("Using PulseAudio sink with format: {:?}", format); + // PulseAudio calls S24 and S24_3 different from the rest of the world let pulse_format = match format { AudioFormat::F32 => pulse::sample::Format::F32le, AudioFormat::S32 => pulse::sample::Format::S32le, + AudioFormat::S24 => pulse::sample::Format::S24_32le, + AudioFormat::S24_3 => pulse::sample::Format::S24le, AudioFormat::S16 => pulse::sample::Format::S16le, }; @@ -33,7 +36,7 @@ impl Open for PulseAudioSink { }; debug_assert!(ss.is_valid()); - PulseAudioSink { + Self { s: None, ss: ss, device: device, diff --git a/playback/src/audio_backend/rodio.rs b/playback/src/audio_backend/rodio.rs index 97e03ec..5262a9c 100644 --- a/playback/src/audio_backend/rodio.rs +++ b/playback/src/audio_backend/rodio.rs @@ -7,8 +7,6 @@ use cpal::traits::{DeviceTrait, HostTrait}; use std::process::exit; use std::{io, thread, time}; -const FORMAT_NOT_SUPPORTED: &'static str = "Rodio currently does not support that output format"; - // most code is shared between RodioSink and JackRodioSink macro_rules! rodio_sink { ($name: ident) => { @@ -35,7 +33,7 @@ macro_rules! rodio_sink { let source = rodio::buffer::SamplesBuffer::new(2, 44100, samples_s16); self.rodio_sink.append(source) }, - _ => panic!(FORMAT_NOT_SUPPORTED), + _ => unimplemented!(), }; // Chunk sizes seem to be about 256 to 3000 ish items long. @@ -60,7 +58,7 @@ macro_rules! rodio_sink { } }, AudioFormat::S16 => {}, - _ => panic!(FORMAT_NOT_SUPPORTED), + _ => unimplemented!("Rodio currently only supports F32 and S16 formats"), } let rodio_device = match_device(&host, device); @@ -71,7 +69,7 @@ macro_rules! rodio_sink { let sink = rodio::Sink::try_new(&stream.1).expect("couldn't create output sink."); debug!("Using Rodio sink"); - $name { + Self { rodio_sink: sink, stream: stream.0, format: format, diff --git a/playback/src/audio_backend/sdl.rs b/playback/src/audio_backend/sdl.rs index ef8c183..32d710f 100644 --- a/playback/src/audio_backend/sdl.rs +++ b/playback/src/audio_backend/sdl.rs @@ -8,6 +8,7 @@ use std::{io, mem, thread, time}; pub enum SdlSink { F32(AudioQueue), S32(AudioQueue), + S24(AudioQueue), S16(AudioQueue), } @@ -16,7 +17,7 @@ impl Open for SdlSink { info!("Using SDL sink with format: {:?}", format); if device.is_some() { - panic!("SDL sink does not support specifying a device name"); + warn!("SDL sink does not support specifying a device name"); } let ctx = sdl2::init().expect("could not initialize SDL"); @@ -39,9 +40,11 @@ impl Open for SdlSink { }}; } match format { - AudioFormat::F32 => open_sink!(SdlSink::F32, f32), - AudioFormat::S32 => open_sink!(SdlSink::S32, i32), - AudioFormat::S16 => open_sink!(SdlSink::S16, i16), + AudioFormat::F32 => open_sink!(Self::F32, f32), + AudioFormat::S32 => open_sink!(Self::S32, i32), + AudioFormat::S24 => open_sink!(Self::S24, i32), + AudioFormat::S24_3 => unimplemented!("SDL currently does not support S24_3 output"), + AudioFormat::S16 => open_sink!(Self::S16, i16), } } } @@ -55,9 +58,10 @@ impl Sink for SdlSink { }}; } match self { - SdlSink::F32(queue) => start_sink!(queue), - SdlSink::S32(queue) => start_sink!(queue), - SdlSink::S16(queue) => start_sink!(queue), + Self::F32(queue) => start_sink!(queue), + Self::S32(queue) => start_sink!(queue), + Self::S24(queue) => start_sink!(queue), + Self::S16(queue) => start_sink!(queue), }; Ok(()) } @@ -70,9 +74,10 @@ impl Sink for SdlSink { }}; } match self { - SdlSink::F32(queue) => stop_sink!(queue), - SdlSink::S32(queue) => stop_sink!(queue), - SdlSink::S16(queue) => stop_sink!(queue), + Self::F32(queue) => stop_sink!(queue), + Self::S32(queue) => stop_sink!(queue), + Self::S24(queue) => stop_sink!(queue), + Self::S16(queue) => stop_sink!(queue), }; Ok(()) } @@ -87,16 +92,21 @@ impl Sink for SdlSink { }}; } match self { - SdlSink::F32(queue) => { + Self::F32(queue) => { drain_sink!(queue, mem::size_of::()); queue.queue(packet.samples()) } - SdlSink::S32(queue) => { + Self::S32(queue) => { drain_sink!(queue, mem::size_of::()); let samples_s32: Vec = AudioPacket::f32_to_s32(packet.samples()); queue.queue(&samples_s32) } - SdlSink::S16(queue) => { + Self::S24(queue) => { + drain_sink!(queue, mem::size_of::()); + let samples_s24: Vec = AudioPacket::f32_to_s24(packet.samples()); + queue.queue(&samples_s24) + } + Self::S16(queue) => { drain_sink!(queue, mem::size_of::()); let samples_s16: Vec = AudioPacket::f32_to_s16(packet.samples()); queue.queue(&samples_s16) diff --git a/playback/src/config.rs b/playback/src/config.rs index 7348b7b..630c140 100644 --- a/playback/src/config.rs +++ b/playback/src/config.rs @@ -1,4 +1,6 @@ +use crate::audio::i24; use std::convert::TryFrom; +use std::mem; use std::str::FromStr; #[derive(Clone, Copy, Debug, Hash, PartialOrd, Ord, PartialEq, Eq)] @@ -30,6 +32,8 @@ impl Default for Bitrate { pub enum AudioFormat { F32, S32, + S24, + S24_3, S16, } @@ -37,17 +41,31 @@ impl TryFrom<&String> for AudioFormat { type Error = (); fn try_from(s: &String) -> Result { match s.to_uppercase().as_str() { - "F32" => Ok(AudioFormat::F32), - "S32" => Ok(AudioFormat::S32), - "S16" => Ok(AudioFormat::S16), - _ => unimplemented!(), + "F32" => Ok(Self::F32), + "S32" => Ok(Self::S32), + "S24" => Ok(Self::S24), + "S24_3" => Ok(Self::S24_3), + "S16" => Ok(Self::S16), + _ => Err(()), } } } impl Default for AudioFormat { fn default() -> AudioFormat { - AudioFormat::S16 + Self::S16 + } +} + +impl AudioFormat { + // not used by all backends + #[allow(dead_code)] + pub fn size(&self) -> usize { + match self { + Self::S24_3 => mem::size_of::(), + Self::S16 => mem::size_of::(), + _ => mem::size_of::(), + } } } diff --git a/src/main.rs b/src/main.rs index 07b85b3..7426e2c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -156,7 +156,7 @@ fn setup(args: &[String]) -> Setup { .optopt( "", "format", - "Output format (F32, S32 or S16). Defaults to S16", + "Output format (F32, S32, S24, S24_3 or S16). Defaults to S16", "FORMAT", ) .optopt("", "mixer", "Mixer to use (alsa or softvol)", "MIXER") From b94879de62f3fbf11a38aa2fa5df11b24e9998b8 Mon Sep 17 00:00:00 2001 From: Roderick van Domburg Date: Thu, 18 Mar 2021 20:51:53 +0100 Subject: [PATCH 069/103] Fix GStreamer buffer pool size [ref #660 review] --- playback/src/audio_backend/gstreamer.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/playback/src/audio_backend/gstreamer.rs b/playback/src/audio_backend/gstreamer.rs index 3695857..d3c736a 100644 --- a/playback/src/audio_backend/gstreamer.rs +++ b/playback/src/audio_backend/gstreamer.rs @@ -26,10 +26,12 @@ impl Open for GstreamerSink { AudioFormat::S24_3 => "S24".to_string(), _ => format!("{:?}", format), }; + let sample_size = format.size(); + let gst_bytes = 2048 * sample_size; let pipeline_str_preamble = format!( - "appsrc caps=\"audio/x-raw,format={}LE,layout=interleaved,channels={},rate={}\" block=true max-bytes=4096 name=appsrc0 ", - gst_format, NUM_CHANNELS, SAMPLE_RATE + "appsrc caps=\"audio/x-raw,format={}LE,layout=interleaved,channels={},rate={}\" block=true max-bytes={} name=appsrc0 ", + gst_format, NUM_CHANNELS, SAMPLE_RATE, gst_bytes ); let pipeline_str_rest = r#" ! audioconvert ! autoaudiosink"#; let pipeline_str: String = match device { @@ -54,7 +56,7 @@ impl Open for GstreamerSink { let bufferpool = gst::BufferPool::new(); let appsrc_caps = appsrc.get_caps().expect("couldn't get appsrc caps"); let mut conf = bufferpool.get_config(); - conf.set_params(Some(&appsrc_caps), 2048 * format.size() as u32, 0, 0); + conf.set_params(Some(&appsrc_caps), 4096 * sample_size as u32, 0, 0); bufferpool .set_config(conf) .expect("couldn't configure the buffer pool"); @@ -62,7 +64,7 @@ impl Open for GstreamerSink { .set_active(true) .expect("couldn't activate buffer pool"); - let (tx, rx) = sync_channel::>(64 * format.size()); + let (tx, rx) = sync_channel::>(64 * sample_size); thread::spawn(move || { for data in rx { let buffer = bufferpool.acquire_buffer(None); From a1326ba9f45c5d6f61950f95a87a5642421ad2a9 Mon Sep 17 00:00:00 2001 From: Roderick van Domburg Date: Thu, 18 Mar 2021 22:06:43 +0100 Subject: [PATCH 070/103] First round of refactoring - DRY-ups - Remove incorrect optimization attempt in the libvorbis decoder, that skewed 0.0 samples non-linear - PortAudio and SDL backends do not support S24 output. The PortAudio bindings could, but not through this API. --- audio/src/libvorbis_decoder.rs | 8 +------ playback/src/audio_backend/alsa.rs | 2 -- playback/src/audio_backend/jackaudio.rs | 6 ++--- playback/src/audio_backend/portaudio.rs | 17 +++----------- playback/src/audio_backend/sdl.rs | 13 +++-------- playback/src/config.rs | 30 ++++++++++++------------- 6 files changed, 25 insertions(+), 51 deletions(-) diff --git a/audio/src/libvorbis_decoder.rs b/audio/src/libvorbis_decoder.rs index 449caae..e7ccc98 100644 --- a/audio/src/libvorbis_decoder.rs +++ b/audio/src/libvorbis_decoder.rs @@ -45,13 +45,7 @@ where packet .data .iter() - .map(|sample| { - if *sample == 0 { - 0.0 - } else { - ((*sample as f64 + 0.5) / (0x7FFF as f64 + 0.5)) as f32 - } - }) + .map(|sample| ((*sample as f64 + 0.5) / (0x7FFF as f64 + 0.5)) as f32) .collect(), ))); } diff --git a/playback/src/audio_backend/alsa.rs b/playback/src/audio_backend/alsa.rs index 35d0ab1..fc2a775 100644 --- a/playback/src/audio_backend/alsa.rs +++ b/playback/src/audio_backend/alsa.rs @@ -52,8 +52,6 @@ fn open_device(dev_name: &str, format: AudioFormat) -> Result<(PCM, Frames), Box // For stereo samples encoded as 32-bit float, one frame has a length of eight bytes. let mut period_size = ((SAMPLES_PER_SECOND * format.size() as u32) as f32 * (BUFFERED_LATENCY / BUFFERED_PERIODS as f32)) as Frames; - - // Set hardware parameters: 44100 Hz / stereo / 32-bit float or 16-bit signed integer { let hwp = HwParams::any(&pcm)?; hwp.set_access(Access::RWInterleaved)?; diff --git a/playback/src/audio_backend/jackaudio.rs b/playback/src/audio_backend/jackaudio.rs index 05c6c31..ed6ae1f 100644 --- a/playback/src/audio_backend/jackaudio.rs +++ b/playback/src/audio_backend/jackaudio.rs @@ -5,8 +5,8 @@ use crate::player::NUM_CHANNELS; use jack::{ AsyncClient, AudioOut, Client, ClientOptions, Control, Port, ProcessHandler, ProcessScope, }; +use std::io; use std::sync::mpsc::{sync_channel, Receiver, SyncSender}; -use std::{io, mem}; pub struct JackSink { send: SyncSender, @@ -53,7 +53,7 @@ impl Open for JackSink { let ch_r = client.register_port("out_0", AudioOut::default()).unwrap(); let ch_l = client.register_port("out_1", AudioOut::default()).unwrap(); // buffer for samples from librespot (~10ms) - let (tx, rx) = sync_channel::(NUM_CHANNELS as usize * 1024 * mem::size_of::()); + let (tx, rx) = sync_channel::(NUM_CHANNELS as usize * 1024 * format.size()); let jack_data = JackData { rec: rx, port_l: ch_l, @@ -75,7 +75,7 @@ impl Sink for JackSink { for s in packet.samples().iter() { let res = self.send.send(*s); if res.is_err() { - error!("jackaudio: cannot write to channel"); + error!("cannot write to channel"); } } Ok(()) diff --git a/playback/src/audio_backend/portaudio.rs b/playback/src/audio_backend/portaudio.rs index 213f2d0..fca305e 100644 --- a/playback/src/audio_backend/portaudio.rs +++ b/playback/src/audio_backend/portaudio.rs @@ -18,10 +18,6 @@ pub enum PortAudioSink<'a> { Option>, StreamParameters, ), - S24( - Option>, - StreamParameters, - ), S16( Option>, StreamParameters, @@ -91,11 +87,10 @@ impl<'a> Open for PortAudioSink<'a> { match format { AudioFormat::F32 => open_sink!(Self::F32, f32), AudioFormat::S32 => open_sink!(Self::S32, i32), - AudioFormat::S24 => open_sink!(Self::S24, i32), - AudioFormat::S24_3 => { - unimplemented!("PortAudio currently does not support S24_3 output") - } AudioFormat::S16 => open_sink!(Self::S16, i16), + _ => { + unimplemented!("PortAudio currently does not support {:?} output", format) + } } } } @@ -123,7 +118,6 @@ impl<'a> Sink for PortAudioSink<'a> { match self { Self::F32(stream, parameters) => start_sink!(stream, parameters), Self::S32(stream, parameters) => start_sink!(stream, parameters), - Self::S24(stream, parameters) => start_sink!(stream, parameters), Self::S16(stream, parameters) => start_sink!(stream, parameters), }; @@ -140,7 +134,6 @@ impl<'a> Sink for PortAudioSink<'a> { match self { Self::F32(stream, _parameters) => stop_sink!(stream), Self::S32(stream, _parameters) => stop_sink!(stream), - Self::S24(stream, _parameters) => stop_sink!(stream), Self::S16(stream, _parameters) => stop_sink!(stream), }; @@ -162,10 +155,6 @@ impl<'a> Sink for PortAudioSink<'a> { let samples_s32: Vec = AudioPacket::f32_to_s32(packet.samples()); write_sink!(stream, &samples_s32) } - Self::S24(stream, _parameters) => { - let samples_s24: Vec = AudioPacket::f32_to_s24(packet.samples()); - write_sink!(stream, &samples_s24) - } Self::S16(stream, _parameters) => { let samples_s16: Vec = AudioPacket::f32_to_s16(packet.samples()); write_sink!(stream, &samples_s16) diff --git a/playback/src/audio_backend/sdl.rs b/playback/src/audio_backend/sdl.rs index 32d710f..6452373 100644 --- a/playback/src/audio_backend/sdl.rs +++ b/playback/src/audio_backend/sdl.rs @@ -8,7 +8,6 @@ use std::{io, mem, thread, time}; pub enum SdlSink { F32(AudioQueue), S32(AudioQueue), - S24(AudioQueue), S16(AudioQueue), } @@ -42,9 +41,10 @@ impl Open for SdlSink { match format { AudioFormat::F32 => open_sink!(Self::F32, f32), AudioFormat::S32 => open_sink!(Self::S32, i32), - AudioFormat::S24 => open_sink!(Self::S24, i32), - AudioFormat::S24_3 => unimplemented!("SDL currently does not support S24_3 output"), AudioFormat::S16 => open_sink!(Self::S16, i16), + _ => { + unimplemented!("SDL currently does not support {:?} output", format) + } } } } @@ -60,7 +60,6 @@ impl Sink for SdlSink { match self { Self::F32(queue) => start_sink!(queue), Self::S32(queue) => start_sink!(queue), - Self::S24(queue) => start_sink!(queue), Self::S16(queue) => start_sink!(queue), }; Ok(()) @@ -76,7 +75,6 @@ impl Sink for SdlSink { match self { Self::F32(queue) => stop_sink!(queue), Self::S32(queue) => stop_sink!(queue), - Self::S24(queue) => stop_sink!(queue), Self::S16(queue) => stop_sink!(queue), }; Ok(()) @@ -101,11 +99,6 @@ impl Sink for SdlSink { let samples_s32: Vec = AudioPacket::f32_to_s32(packet.samples()); queue.queue(&samples_s32) } - Self::S24(queue) => { - drain_sink!(queue, mem::size_of::()); - let samples_s24: Vec = AudioPacket::f32_to_s24(packet.samples()); - queue.queue(&samples_s24) - } Self::S16(queue) => { drain_sink!(queue, mem::size_of::()); let samples_s16: Vec = AudioPacket::f32_to_s16(packet.samples()); diff --git a/playback/src/config.rs b/playback/src/config.rs index 630c140..95c9709 100644 --- a/playback/src/config.rs +++ b/playback/src/config.rs @@ -14,17 +14,17 @@ impl FromStr for Bitrate { type Err = (); fn from_str(s: &str) -> Result { match s { - "96" => Ok(Bitrate::Bitrate96), - "160" => Ok(Bitrate::Bitrate160), - "320" => Ok(Bitrate::Bitrate320), + "96" => Ok(Self::Bitrate96), + "160" => Ok(Self::Bitrate160), + "320" => Ok(Self::Bitrate320), _ => Err(()), } } } impl Default for Bitrate { - fn default() -> Bitrate { - Bitrate::Bitrate160 + fn default() -> Self { + Self::Bitrate160 } } @@ -52,7 +52,7 @@ impl TryFrom<&String> for AudioFormat { } impl Default for AudioFormat { - fn default() -> AudioFormat { + fn default() -> Self { Self::S16 } } @@ -64,7 +64,7 @@ impl AudioFormat { match self { Self::S24_3 => mem::size_of::(), Self::S16 => mem::size_of::(), - _ => mem::size_of::(), + _ => mem::size_of::(), // S32 and S24 are both stored in i32 } } } @@ -79,16 +79,16 @@ impl FromStr for NormalisationType { type Err = (); fn from_str(s: &str) -> Result { match s { - "album" => Ok(NormalisationType::Album), - "track" => Ok(NormalisationType::Track), + "album" => Ok(Self::Album), + "track" => Ok(Self::Track), _ => Err(()), } } } impl Default for NormalisationType { - fn default() -> NormalisationType { - NormalisationType::Album + fn default() -> Self { + Self::Album } } @@ -102,16 +102,16 @@ impl FromStr for NormalisationMethod { type Err = (); fn from_str(s: &str) -> Result { match s { - "basic" => Ok(NormalisationMethod::Basic), - "dynamic" => Ok(NormalisationMethod::Dynamic), + "basic" => Ok(Self::Basic), + "dynamic" => Ok(Self::Dynamic), _ => Err(()), } } } impl Default for NormalisationMethod { - fn default() -> NormalisationMethod { - NormalisationMethod::Dynamic + fn default() -> Self { + Self::Dynamic } } From 001d3ca1cf70fdbfef9703aa9cc47f43b050aa76 Mon Sep 17 00:00:00 2001 From: Roderick van Domburg Date: Fri, 19 Mar 2021 22:28:55 +0100 Subject: [PATCH 071/103] Bump Alsa, cpal and GStreamer crates --- Cargo.lock | 584 ++++---------------------------------------- playback/Cargo.toml | 2 +- 2 files changed, 52 insertions(+), 534 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2296cfe..2d71176 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,26 +1,5 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -[[package]] -name = "addr2line" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a55f82cfe485775d02112886f4169bde0c5894d75e79ead7eafe7e40a25e45f7" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e" - -[[package]] -name = "adler32" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" - [[package]] name = "aes" version = "0.6.0" @@ -66,9 +45,9 @@ dependencies = [ [[package]] name = "alsa" -version = "0.4.3" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb213f6b3e4b1480a60931ca2035794aa67b73103d254715b1db7b70dcb3c934" +checksum = "75c4da790adcb2ce5e758c064b4f3ec17a30349f9961d3e5e6c9688b052a9e18" dependencies = [ "alsa-sys", "bitflags", @@ -92,12 +71,6 @@ version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "afddf7f520a80dbf76e6f50a35bca42a2331ef227a28b3b6dc5c2e2338d114b1" -[[package]] -name = "ascii" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eab1c04a571841102f5345a8fc0f6bb3d31c315dec879b5c6e42e40ce7ffa34e" - [[package]] name = "atty" version = "0.2.14" @@ -115,26 +88,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" -[[package]] -name = "backtrace" -version = "0.3.55" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef5140344c85b01f9bbb4d4b7288a8aa4b3287ccef913a14bcc78a1063623598" -dependencies = [ - "addr2line", - "cfg-if 1.0.0", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] - -[[package]] -name = "base-x" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4521f3e3d031370679b3b140beb36dfe4801b09ac77e30c61941f97df3ef28b" - [[package]] name = "base64" version = "0.9.3" @@ -276,6 +229,9 @@ name = "cc" version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48" +dependencies = [ + "jobserver", +] [[package]] name = "cesu8" @@ -313,16 +269,10 @@ dependencies = [ "libc", "num-integer", "num-traits", - "time 0.1.43", + "time", "winapi 0.3.9", ] -[[package]] -name = "chunked_transfer" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fff857943da45f546682664a79488be82e69e43c1a7a2307679ab9afb3a66d2e" - [[package]] name = "cipher" version = "0.2.5" @@ -352,19 +302,6 @@ dependencies = [ "bitflags", ] -[[package]] -name = "combine" -version = "3.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da3da6baa321ec19e1cc41d31bf599f00c783d0517095cdaf0332e3fe8d20680" -dependencies = [ - "ascii", - "byteorder", - "either", - "memchr", - "unreachable", -] - [[package]] name = "combine" version = "4.5.2" @@ -375,39 +312,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "const_fn" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28b9d6de7f49e22cf97ad17fc4036ece69300032f45f78f30b4a4482cdc3f4a6" - -[[package]] -name = "cookie" -version = "0.14.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784ad0fbab4f3e9cef09f20e0aea6000ae08d2cb98ac4c0abc53df18803d702f" -dependencies = [ - "percent-encoding 2.1.0", - "time 0.2.25", - "version_check", -] - -[[package]] -name = "cookie_store" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3818dfca4b0cb5211a659bbcbb94225b7127407b2b135e650d717bfb78ab10d3" -dependencies = [ - "cookie", - "idna 0.2.1", - "log 0.4.14", - "publicsuffix", - "serde", - "serde_json", - "time 0.2.25", - "url 2.2.0", -] - [[package]] name = "core-foundation-sys" version = "0.6.2" @@ -416,9 +320,9 @@ checksum = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b" [[package]] name = "coreaudio-rs" -version = "0.9.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f229761965dad3e9b11081668a6ea00f1def7aa46062321b5ec245b834f6e491" +checksum = "11894b20ebfe1ff903cbdc52259693389eea03b94918a2def2c30c3bf227ad88" dependencies = [ "bitflags", "coreaudio-sys", @@ -435,15 +339,15 @@ dependencies = [ [[package]] name = "cpal" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05631e2089dfa5d3b6ea1cfbbfd092e2ee5deeb69698911bc976b28b746d3657" +checksum = "840981d3f30230d9120328d64be72319dbbedabb61bcd4c370a54cdd051238ac" dependencies = [ "alsa", "core-foundation-sys", "coreaudio-rs", "jack", - "jni 0.17.0", + "jni", "js-sys", "lazy_static", "libc", @@ -453,7 +357,7 @@ dependencies = [ "nix", "oboe", "parking_lot 0.11.1", - "stdweb 0.1.3", + "stdweb", "thiserror", "web-sys", "winapi 0.3.9", @@ -465,15 +369,6 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634" -[[package]] -name = "crc32fast" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" -dependencies = [ - "cfg-if 1.0.0", -] - [[package]] name = "crossbeam-deque" version = "0.7.3" @@ -624,12 +519,6 @@ dependencies = [ "generic-array 0.14.4", ] -[[package]] -name = "discard" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" - [[package]] name = "dns-sd" version = "0.1.3" @@ -664,7 +553,6 @@ version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" dependencies = [ - "backtrace", "version_check", ] @@ -674,45 +562,12 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" -[[package]] -name = "fetch_unroll" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8d44807d562d137f063cbfe209da1c3f9f2fa8375e11166ef495daab7b847f9" -dependencies = [ - "libflate", - "tar", - "ureq", -] - -[[package]] -name = "filetime" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d34cfa13a63ae058bfa601fe9e313bbdb3746427c1459185464ce0fcf62e1e8" -dependencies = [ - "cfg-if 1.0.0", - "libc", - "redox_syscall 0.2.4", - "winapi 0.3.9", -] - [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -[[package]] -name = "form_urlencoded" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ece68d15c92e84fa4f19d3780f1294e5ca82a78a6d515f1efaabcc144688be00" -dependencies = [ - "matches", - "percent-encoding 2.1.0", -] - [[package]] name = "fuchsia-cprng" version = "0.1.1" @@ -876,12 +731,6 @@ dependencies = [ "wasi 0.10.2+wasi-snapshot-preview1", ] -[[package]] -name = "gimli" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6503fe142514ca4799d4c26297c4248239fe8838d827db6bd6065c6ed29a6ce" - [[package]] name = "glib" version = "0.10.3" @@ -1115,9 +964,9 @@ dependencies = [ "log 0.4.14", "mime", "net2", - "percent-encoding 1.0.1", + "percent-encoding", "relay", - "time 0.1.43", + "time", "tokio-core", "tokio-io", "tokio-proto", @@ -1156,17 +1005,6 @@ dependencies = [ "unicode-normalization", ] -[[package]] -name = "idna" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de910d521f7cc3135c4de8db1cb910e0b5ed1dc6f57c381cd07e8e661ce10094" -dependencies = [ - "matches", - "unicode-bidi", - "unicode-normalization", -] - [[package]] name = "if-addrs" version = "0.6.5" @@ -1246,29 +1084,15 @@ dependencies = [ [[package]] name = "jni" -version = "0.14.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1981310da491a4f0f815238097d0d43d8072732b5ae5f8bd0d8eadf5bf245402" +checksum = "24967112a1e4301ca5342ea339763613a37592b8a6ce6cf2e4494537c7a42faf" dependencies = [ "cesu8", - "combine 3.8.1", - "error-chain", - "jni-sys", - "log 0.4.14", - "walkdir", -] - -[[package]] -name = "jni" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36bcc950632e48b86da402c5c077590583da5ac0d480103611d5374e7c967a3c" -dependencies = [ - "cesu8", - "combine 4.5.2", - "error-chain", + "combine", "jni-sys", "log 0.4.14", + "thiserror", "walkdir", ] @@ -1278,6 +1102,15 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" +[[package]] +name = "jobserver" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c71313ebb9439f74b00d9d2dcec36440beaf57a6aa0623068441dd7cd81a7f2" +dependencies = [ + "libc", +] + [[package]] name = "js-sys" version = "0.3.47" @@ -1328,27 +1161,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.85" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ccac4b00700875e6a07c6cde370d44d32fa01c5a65cdd2fca6858c479d28bb3" - -[[package]] -name = "libflate" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389de7875e06476365974da3e7ff85d55f1972188ccd9f6020dd7c8156e17914" -dependencies = [ - "adler32", - "crc32fast", - "libflate_lz77", - "rle-decode-fast", -] - -[[package]] -name = "libflate_lz77" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3286f09f7d4926fc486334f28d8d2e6ebe4f7f9994494b6dab27ddfad2c9b11b" +checksum = "ba4aede83fc3617411dc6993bc8c70919750c1c257c6ca6a502aed6e0e2394ae" [[package]] name = "libloading" @@ -1452,7 +1267,7 @@ dependencies = [ "tokio-io", "tokio-process", "tokio-signal", - "url 1.7.2", + "url", ] [[package]] @@ -1500,7 +1315,7 @@ dependencies = [ "serde_json", "sha-1 0.9.3", "tokio-core", - "url 1.7.2", + "url", ] [[package]] @@ -1534,7 +1349,7 @@ dependencies = [ "tokio-codec", "tokio-core", "tokio-io", - "url 1.7.2", + "url", "uuid", "vergen", ] @@ -1690,16 +1505,6 @@ version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" -[[package]] -name = "miniz_oxide" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f2d26ec3309788e423cfbf68ad1800f061638098d76a83681af979dc4eda19d" -dependencies = [ - "adler", - "autocfg", -] - [[package]] name = "mio" version = "0.6.23" @@ -1781,9 +1586,9 @@ dependencies = [ [[package]] name = "ndk" -version = "0.2.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eb167c1febed0a496639034d0c76b3b74263636045db5489eee52143c246e73" +checksum = "8794322172319b972f528bf90c6b467be0079f1fa82780ffb431088e741a73ab" dependencies = [ "jni-sys", "ndk-sys", @@ -1793,9 +1598,9 @@ dependencies = [ [[package]] name = "ndk-glue" -version = "0.2.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdf399b8b7a39c6fb153c4ec32c72fd5fe789df24a647f229c239aa7adb15241" +checksum = "c5caf0c24d51ac1c905c27d4eda4fa0635bbe0de596b8f79235e0b17a4d29385" dependencies = [ "lazy_static", "libc", @@ -1837,15 +1642,14 @@ dependencies = [ [[package]] name = "nix" -version = "0.15.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b2e0b4f3320ed72aaedb9a5ac838690a8047c7b275da22711fddff4f8a14229" +checksum = "fa9b4819da1bc61c0ea48b63b7bc8604064dd43013e7cc325df098d49cd7c18a" dependencies = [ "bitflags", "cc", - "cfg-if 0.1.10", + "cfg-if 1.0.0", "libc", - "void", ] [[package]] @@ -1922,9 +1726,9 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.4.3" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca565a7df06f3d4b485494f25ba05da1435950f4dc263440eda7a6fa9b8e36e4" +checksum = "226b45a5c2ac4dd696ed30fa6b94b057ad909c7b7fc2e0d0808192bced894066" dependencies = [ "derivative", "num_enum_derive", @@ -1932,9 +1736,9 @@ dependencies = [ [[package]] name = "num_enum_derive" -version = "0.4.3" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffa5a33ddddfee04c0283a7653987d634e880347e96b5b2ed64de07efb59db9d" +checksum = "1c0fd9eba1d5db0994a239e09c1be402d35622277e35468ba891aa5e3188ce7e" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -1942,19 +1746,13 @@ dependencies = [ "syn", ] -[[package]] -name = "object" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d3b63360ec3cb337817c2dbd47ab4a0f170d285d8e5a2064600f3def1402397" - [[package]] name = "oboe" -version = "0.3.1" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aadc2b0867bdbb9a81c4d99b9b682958f49dbea1295a81d2f646cca2afdd9fc" +checksum = "4cfb2390bddb9546c0f7448fd1d2abdd39e6075206f960991eb28c7fa7f126c4" dependencies = [ - "jni 0.14.0", + "jni", "ndk", "ndk-glue", "num-derive", @@ -1964,11 +1762,11 @@ dependencies = [ [[package]] name = "oboe-sys" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ff7a51600eabe34e189eec5c995a62f151d8d97e5fbca39e87ca738bb99b82" +checksum = "fe069264d082fc820dfa172f79be3f2e088ecfece9b1c47b0c9fd838d2bef103" dependencies = [ - "fetch_unroll", + "cc", ] [[package]] @@ -2088,12 +1886,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" -[[package]] -name = "percent-encoding" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" - [[package]] name = "pin-project-lite" version = "0.2.4" @@ -2224,28 +2016,6 @@ dependencies = [ "protobuf-codegen", ] -[[package]] -name = "publicsuffix" -version = "1.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bbaa49075179162b49acac1c6aa45fb4dafb5f13cf6794276d77bc7fd95757b" -dependencies = [ - "error-chain", - "idna 0.2.1", - "lazy_static", - "regex", - "url 2.2.0", -] - -[[package]] -name = "qstring" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d464fae65fff2680baf48019211ce37aaec0c78e9264c84a3e484717f965104e" -dependencies = [ - "percent-encoding 2.1.0", -] - [[package]] name = "quick-error" version = "1.2.3" @@ -2437,27 +2207,6 @@ dependencies = [ "winapi 0.3.9", ] -[[package]] -name = "ring" -version = "0.16.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" -dependencies = [ - "cc", - "libc", - "once_cell", - "spin", - "untrusted", - "web-sys", - "winapi 0.3.9", -] - -[[package]] -name = "rle-decode-fast" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cabe4fa914dec5870285fa7f71f602645da47c486e68486d2b4ceb4a343e90ac" - [[package]] name = "rodio" version = "0.13.0" @@ -2477,12 +2226,6 @@ dependencies = [ "winapi 0.3.9", ] -[[package]] -name = "rustc-demangle" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e3bad0ee36814ca07d7968269dd4b7ec89ec2da10c4bb613928d3077083c232" - [[package]] name = "rustc-hash" version = "1.1.0" @@ -2498,19 +2241,6 @@ dependencies = [ "semver", ] -[[package]] -name = "rustls" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "064fd21ff87c6e87ed4506e68beb42459caa4a0e2eb144932e6776768556980b" -dependencies = [ - "base64 0.13.0", - "log 0.4.14", - "ring", - "sct", - "webpki", -] - [[package]] name = "ryu" version = "1.0.5" @@ -2544,16 +2274,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" -[[package]] -name = "sct" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3042af939fca8c3453b7af0f1c66e533a15a86169e39de2657310ade8f98d3c" -dependencies = [ - "ring", - "untrusted", -] - [[package]] name = "sdl2" version = "0.34.3" @@ -2597,9 +2317,6 @@ name = "serde" version = "1.0.123" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92d5161132722baa40d802cc70b15262b98258453e85e5d1d365c757c73869ae" -dependencies = [ - "serde_derive", -] [[package]] name = "serde_derive" @@ -2648,12 +2365,6 @@ dependencies = [ "opaque-debug 0.3.0", ] -[[package]] -name = "sha1" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" - [[package]] name = "shannon" version = "0.2.0" @@ -2728,76 +2439,12 @@ dependencies = [ "winapi 0.3.9", ] -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - -[[package]] -name = "standback" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2beb4d1860a61f571530b3f855a1b538d0200f7871c63331ecd6f17b1f014f8" -dependencies = [ - "version_check", -] - [[package]] name = "stdweb" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef5430c8e36b713e13b48a9f709cc21e046723fe44ce34587b73a830203b533e" -[[package]] -name = "stdweb" -version = "0.4.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" -dependencies = [ - "discard", - "rustc_version", - "stdweb-derive", - "stdweb-internal-macros", - "stdweb-internal-runtime", - "wasm-bindgen", -] - -[[package]] -name = "stdweb-derive" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" -dependencies = [ - "proc-macro2", - "quote", - "serde", - "serde_derive", - "syn", -] - -[[package]] -name = "stdweb-internal-macros" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" -dependencies = [ - "base-x", - "proc-macro2", - "quote", - "serde", - "serde_derive", - "serde_json", - "sha1", - "syn", -] - -[[package]] -name = "stdweb-internal-runtime" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" - [[package]] name = "strsim" version = "0.9.3" @@ -2872,17 +2519,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b157868d8ac1f56b64604539990685fa7611d8fa9e5476cf0c02cf34d32917c5" -[[package]] -name = "tar" -version = "0.4.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0313546c01d59e29be4f09687bcb4fb6690cec931cc3607b6aec7a0e417f4cc6" -dependencies = [ - "filetime", - "libc", - "xattr", -] - [[package]] name = "tempfile" version = "3.2.0" @@ -2936,44 +2572,6 @@ dependencies = [ "winapi 0.3.9", ] -[[package]] -name = "time" -version = "0.2.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1195b046942c221454c2539395f85413b33383a067449d78aab2b7b052a142f7" -dependencies = [ - "const_fn", - "libc", - "standback", - "stdweb 0.4.20", - "time-macros", - "version_check", - "winapi 0.3.9", -] - -[[package]] -name = "time-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957e9c6e26f12cb6d0dd7fc776bb67a706312e7299aed74c8dd5b17ebb27e2f1" -dependencies = [ - "proc-macro-hack", - "time-macros-impl", -] - -[[package]] -name = "time-macros-impl" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5c3be1edfad6027c69f5491cf4cb310d1a71ecd6af742788c6ff8bced86b8fa" -dependencies = [ - "proc-macro-hack", - "proc-macro2", - "quote", - "standback", - "syn", -] - [[package]] name = "tinyvec" version = "1.1.1" @@ -3319,61 +2917,15 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" -[[package]] -name = "unreachable" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" -dependencies = [ - "void", -] - -[[package]] -name = "untrusted" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" - -[[package]] -name = "ureq" -version = "1.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "294b85ef5dbc3670a72e82a89971608a1fcc4ed5c7c5a2895230d31a95f0569b" -dependencies = [ - "base64 0.13.0", - "chunked_transfer", - "cookie", - "cookie_store", - "log 0.4.14", - "once_cell", - "qstring", - "rustls", - "url 2.2.0", - "webpki", - "webpki-roots", -] - [[package]] name = "url" version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a" dependencies = [ - "idna 0.1.5", + "idna", "matches", - "percent-encoding 1.0.1", -] - -[[package]] -name = "url" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5909f2b0817350449ed73e8bcd81c8c3c8d9a7a5d8acba4b27db277f1868976e" -dependencies = [ - "form_urlencoded", - "idna 0.2.1", - "matches", - "percent-encoding 2.1.0", + "percent-encoding", ] [[package]] @@ -3407,12 +2959,6 @@ version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" -[[package]] -name = "void" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" - [[package]] name = "vorbis" version = "0.0.14" @@ -3548,25 +3094,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "webpki" -version = "0.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "webpki-roots" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82015b7e0b8bad8185994674a13a93306bea76cf5a16c5a181382fd3a5ec2376" -dependencies = [ - "webpki", -] - [[package]] name = "winapi" version = "0.2.8" @@ -3620,15 +3147,6 @@ dependencies = [ "winapi-build", ] -[[package]] -name = "xattr" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "244c3741f4240ef46274860397c7c74e50eb23624996930e484c16679633a54c" -dependencies = [ - "libc", -] - [[package]] name = "zerocopy" version = "0.3.0" diff --git a/playback/Cargo.toml b/playback/Cargo.toml index 67e06be..952ecde 100644 --- a/playback/Cargo.toml +++ b/playback/Cargo.toml @@ -23,7 +23,7 @@ log = "0.4" byteorder = "1.3" shell-words = "1.0.0" -alsa = { version = "0.4", optional = true } +alsa = { version = "0.5", optional = true } portaudio-rs = { version = "0.3", optional = true } libpulse-binding = { version = "2.13", optional = true, default-features = false } libpulse-simple-binding = { version = "2.13", optional = true, default-features = false } From 86dbaa8ed53c5b76414d1a577f680d2a65dd2c89 Mon Sep 17 00:00:00 2001 From: philippe44 Date: Sat, 20 Mar 2021 12:11:49 -0700 Subject: [PATCH 072/103] true/false don't need to be explicit Co-authored-by: Johannesd3 <51954457+Johannesd3@users.noreply.github.com> --- audio/src/passthrough_decoder.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/audio/src/passthrough_decoder.rs b/audio/src/passthrough_decoder.rs index 6fab78e..082f691 100644 --- a/audio/src/passthrough_decoder.rs +++ b/audio/src/passthrough_decoder.rs @@ -73,7 +73,7 @@ impl AudioDecoder for PassthroughDecoder { info!("Seeking to {}", ms); // add an eos to previous stream if missing - if self.bos == true && self.eos == false { + if self.bos && !self.eos { match self.rdr.read_packet() { Ok(Some(pck)) => { let absgp_page = pck.absgp_page() - self.ofsgp_page; From 74b2fea33814b8ea190343d8ab6a7cd1f5c6f9c2 Mon Sep 17 00:00:00 2001 From: Roderick van Domburg Date: Sun, 21 Mar 2021 22:16:47 +0100 Subject: [PATCH 073/103] Refactor sample conversion into separate struct --- audio/src/lib.rs | 67 +++++++++++++------------ playback/src/audio_backend/mod.rs | 9 ++-- playback/src/audio_backend/portaudio.rs | 15 +++--- playback/src/audio_backend/rodio.rs | 9 ++-- playback/src/audio_backend/sdl.rs | 14 +++--- 5 files changed, 61 insertions(+), 53 deletions(-) diff --git a/audio/src/lib.rs b/audio/src/lib.rs index 86c5b4a..fe3b5c9 100644 --- a/audio/src/lib.rs +++ b/audio/src/lib.rs @@ -38,33 +38,6 @@ pub enum AudioPacket { OggData(Vec), } -#[derive(AsBytes, Copy, Clone, Debug)] -#[allow(non_camel_case_types)] -#[repr(transparent)] -pub struct i24([u8; 3]); -impl i24 { - fn pcm_from_i32(sample: i32) -> Self { - // drop the least significant byte - let [a, b, c, _d] = (sample >> 8).to_le_bytes(); - i24([a, b, c]) - } -} - -// Losslessly represent [-1.0, 1.0] to [$type::MIN, $type::MAX] while maintaining DC linearity. -macro_rules! convert_samples_to { - ($type: ident, $samples: expr) => { - convert_samples_to!($type, $samples, 0) - }; - ($type: ident, $samples: expr, $shift: expr) => { - $samples - .iter() - .map(|sample| { - (*sample as f64 * (std::$type::MAX as f64 + 0.5) - 0.5) as $type >> $shift - }) - .collect() - }; -} - impl AudioPacket { pub fn samples(&self) -> &[f32] { match self { @@ -86,23 +59,53 @@ impl AudioPacket { AudioPacket::OggData(d) => d.is_empty(), } } +} - pub fn f32_to_s32(samples: &[f32]) -> Vec { +#[derive(AsBytes, Copy, Clone, Debug)] +#[allow(non_camel_case_types)] +#[repr(transparent)] +pub struct i24([u8; 3]); +impl i24 { + fn pcm_from_i32(sample: i32) -> Self { + // drop the least significant byte + let [a, b, c, _d] = (sample >> 8).to_le_bytes(); + i24([a, b, c]) + } +} + +// Losslessly represent [-1.0, 1.0] to [$type::MIN, $type::MAX] while maintaining DC linearity. +macro_rules! convert_samples_to { + ($type: ident, $samples: expr) => { + convert_samples_to!($type, $samples, 0) + }; + ($type: ident, $samples: expr, $drop_bits: expr) => { + $samples + .iter() + .map(|sample| { + (*sample as f64 * (std::$type::MAX as f64 + 0.5) - 0.5) as $type >> $drop_bits + }) + .collect() + }; +} + +pub struct SamplesConverter {} +impl SamplesConverter { + pub fn to_s32(samples: &[f32]) -> Vec { convert_samples_to!(i32, samples) } - pub fn f32_to_s24(samples: &[f32]) -> Vec { + pub fn to_s24(samples: &[f32]) -> Vec { convert_samples_to!(i32, samples, 8) } - pub fn f32_to_s24_3(samples: &[f32]) -> Vec { - Self::f32_to_s32(samples) + pub fn to_s24_3(samples: &[f32]) -> Vec { + Self::to_s32(samples) .iter() .map(|sample| i24::pcm_from_i32(*sample)) .collect() } - pub fn f32_to_s16(samples: &[f32]) -> Vec { + pub fn to_s16(samples: &[f32]) -> Vec { convert_samples_to!(i16, samples) } } diff --git a/playback/src/audio_backend/mod.rs b/playback/src/audio_backend/mod.rs index 9c46dbe..94b6a52 100644 --- a/playback/src/audio_backend/mod.rs +++ b/playback/src/audio_backend/mod.rs @@ -24,24 +24,25 @@ fn mk_sink(device: Option, format: AudioFormat macro_rules! sink_as_bytes { () => { fn write(&mut self, packet: &AudioPacket) -> io::Result<()> { + use crate::audio::{i24, SamplesConverter}; use zerocopy::AsBytes; match packet { AudioPacket::Samples(samples) => match self.format { AudioFormat::F32 => self.write_bytes(samples.as_bytes()), AudioFormat::S32 => { - let samples_s32 = AudioPacket::f32_to_s32(samples); + let samples_s32: &[i32] = &SamplesConverter::to_s32(samples); self.write_bytes(samples_s32.as_bytes()) } AudioFormat::S24 => { - let samples_s24 = AudioPacket::f32_to_s24(samples); + let samples_s24: &[i32] = &SamplesConverter::to_s24(samples); self.write_bytes(samples_s24.as_bytes()) } AudioFormat::S24_3 => { - let samples_s24_3 = AudioPacket::f32_to_s24_3(samples); + let samples_s24_3: &[i24] = &SamplesConverter::to_s24_3(samples); self.write_bytes(samples_s24_3.as_bytes()) } AudioFormat::S16 => { - let samples_s16 = AudioPacket::f32_to_s16(samples); + let samples_s16: &[i16] = &SamplesConverter::to_s16(samples); self.write_bytes(samples_s16.as_bytes()) } }, diff --git a/playback/src/audio_backend/portaudio.rs b/playback/src/audio_backend/portaudio.rs index fca305e..f29bac2 100644 --- a/playback/src/audio_backend/portaudio.rs +++ b/playback/src/audio_backend/portaudio.rs @@ -1,5 +1,5 @@ use super::{Open, Sink}; -use crate::audio::AudioPacket; +use crate::audio::{AudioPacket, SamplesConverter}; use crate::config::AudioFormat; use crate::player::{NUM_CHANNELS, SAMPLE_RATE}; use portaudio_rs; @@ -146,18 +146,19 @@ impl<'a> Sink for PortAudioSink<'a> { $stream.as_mut().unwrap().write($samples) }; } + + let samples = packet.samples(); let result = match self { Self::F32(stream, _parameters) => { - let samples = packet.samples(); - write_sink!(stream, &samples) + write_sink!(stream, samples) } Self::S32(stream, _parameters) => { - let samples_s32: Vec = AudioPacket::f32_to_s32(packet.samples()); - write_sink!(stream, &samples_s32) + let samples_s32: &[i32] = &SamplesConverter::to_s32(samples); + write_sink!(stream, samples_s32) } Self::S16(stream, _parameters) => { - let samples_s16: Vec = AudioPacket::f32_to_s16(packet.samples()); - write_sink!(stream, &samples_s16) + let samples_s16: &[i16] = &SamplesConverter::to_s16(samples); + write_sink!(stream, samples_s16) } }; match result { diff --git a/playback/src/audio_backend/rodio.rs b/playback/src/audio_backend/rodio.rs index 5262a9c..2fc4fbd 100644 --- a/playback/src/audio_backend/rodio.rs +++ b/playback/src/audio_backend/rodio.rs @@ -1,8 +1,9 @@ use super::{Open, Sink}; extern crate cpal; extern crate rodio; -use crate::audio::AudioPacket; +use crate::audio::{AudioPacket, SamplesConverter}; use crate::config::AudioFormat; +use crate::player::{NUM_CHANNELS, SAMPLE_RATE}; use cpal::traits::{DeviceTrait, HostTrait}; use std::process::exit; use std::{io, thread, time}; @@ -25,12 +26,12 @@ macro_rules! rodio_sink { let samples = packet.samples(); match self.format { AudioFormat::F32 => { - let source = rodio::buffer::SamplesBuffer::new(2, 44100, samples); + let source = rodio::buffer::SamplesBuffer::new(NUM_CHANNELS as u16, SAMPLE_RATE, samples); self.rodio_sink.append(source) }, AudioFormat::S16 => { - let samples_s16: Vec = AudioPacket::f32_to_s16(samples); - let source = rodio::buffer::SamplesBuffer::new(2, 44100, samples_s16); + let samples_s16: &[i16] = &SamplesConverter::to_s16(samples); + let source = rodio::buffer::SamplesBuffer::new(NUM_CHANNELS as u16, SAMPLE_RATE, samples_s16); self.rodio_sink.append(source) }, _ => unimplemented!(), diff --git a/playback/src/audio_backend/sdl.rs b/playback/src/audio_backend/sdl.rs index 6452373..b1b4c2e 100644 --- a/playback/src/audio_backend/sdl.rs +++ b/playback/src/audio_backend/sdl.rs @@ -1,5 +1,5 @@ use super::{Open, Sink}; -use crate::audio::AudioPacket; +use crate::audio::{AudioPacket, SamplesConverter}; use crate::config::AudioFormat; use crate::player::{NUM_CHANNELS, SAMPLE_RATE}; use sdl2::audio::{AudioQueue, AudioSpecDesired}; @@ -89,20 +89,22 @@ impl Sink for SdlSink { } }}; } + + let samples = packet.samples(); match self { Self::F32(queue) => { drain_sink!(queue, mem::size_of::()); - queue.queue(packet.samples()) + queue.queue(samples) } Self::S32(queue) => { + let samples_s32: &[i32] = &SamplesConverter::to_s32(samples); drain_sink!(queue, mem::size_of::()); - let samples_s32: Vec = AudioPacket::f32_to_s32(packet.samples()); - queue.queue(&samples_s32) + queue.queue(samples_s32) } Self::S16(queue) => { + let samples_s16: &[i16] = &SamplesConverter::to_s16(samples); drain_sink!(queue, mem::size_of::()); - let samples_s16: Vec = AudioPacket::f32_to_s16(packet.samples()); - queue.queue(&samples_s16) + queue.queue(samples_s16) } }; Ok(()) From ec1ec59b8a33d24d69018350da0851ef310e2732 Mon Sep 17 00:00:00 2001 From: ThouCheese Date: Sat, 6 Mar 2021 01:29:08 +0100 Subject: [PATCH 074/103] update examples Re-add default impl to SessionConfig and make Credentials::with_password generic over Into add docs for Credential reintroduce old Default impl for SessionConfig use the third argument for the track-to-play rather than a testing id --- Cargo.lock | 12 ++++++++++++ core/Cargo.toml | 1 + core/src/authentication.rs | 18 +++++++++++++++--- core/src/config.rs | 12 ++++++++++++ examples/get_token.rs | 22 ++++++++-------------- examples/play.rs | 26 ++++++++++---------------- examples/playlist_tracks.rs | 24 +++++++++--------------- 7 files changed, 67 insertions(+), 48 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 65493a2..461095f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,7 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + [[package]] name = "addr2line" version = "0.14.1" @@ -1358,6 +1360,7 @@ dependencies = [ "tokio-stream", "tokio-util", "url", + "uuid", "vergen", ] @@ -2801,6 +2804,15 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "uuid" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" +dependencies = [ + "getrandom", +] + [[package]] name = "vergen" version = "3.2.0" diff --git a/core/Cargo.toml b/core/Cargo.toml index 373e308..9b2c6a5 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -40,6 +40,7 @@ tokio = { version = "1.0", features = ["io-util", "net", "rt", "sync"] } tokio-stream = "0.1" tokio-util = { version = "0.6", features = ["codec"] } url = "2.1" +uuid = { version = "0.8", default-features = false, features = ["v4"] } [build-dependencies] rand = "0.8" diff --git a/core/src/authentication.rs b/core/src/authentication.rs index 2839353..2992abc 100644 --- a/core/src/authentication.rs +++ b/core/src/authentication.rs @@ -10,6 +10,7 @@ use sha1::{Digest, Sha1}; use crate::protocol::authentication::AuthenticationType; +/// The credentials are used to log into the Spotify API. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Credentials { pub username: String, @@ -25,11 +26,22 @@ pub struct Credentials { } impl Credentials { - pub fn with_password(username: String, password: String) -> Credentials { + /// Intialize these credentials from a username and a password. + /// + /// ### Example + /// ```rust + /// use librespot_core::authentication::Credentials; + /// + /// let creds = Credentials::with_password("my account", "my password"); + /// ``` + pub fn with_password( + username: impl Into, + password: impl Into, + ) -> Credentials { Credentials { - username, + username: username.into(), auth_type: AuthenticationType::AUTHENTICATION_USER_PASS, - auth_data: password.into_bytes(), + auth_data: password.into().into_bytes(), } } diff --git a/core/src/config.rs b/core/src/config.rs index 469b935..2692464 100644 --- a/core/src/config.rs +++ b/core/src/config.rs @@ -10,6 +10,18 @@ pub struct SessionConfig { pub ap_port: Option, } +impl Default for SessionConfig { + fn default() -> SessionConfig { + let device_id = uuid::Uuid::new_v4().to_hyphenated().to_string(); + SessionConfig { + user_agent: crate::version::version_string(), + device_id, + proxy: None, + ap_port: None, + } + } +} + #[derive(Clone, Copy, Debug, Hash, PartialOrd, Ord, PartialEq, Eq)] pub enum DeviceType { Unknown = 0, diff --git a/examples/get_token.rs b/examples/get_token.rs index d722e99..15b97bc 100644 --- a/examples/get_token.rs +++ b/examples/get_token.rs @@ -1,5 +1,4 @@ use std::env; -use tokio_core::reactor::Core; use librespot::core::authentication::Credentials; use librespot::core::config::SessionConfig; @@ -9,29 +8,24 @@ use librespot::core::session::Session; const SCOPES: &str = "streaming,user-read-playback-state,user-modify-playback-state,user-read-currently-playing"; -fn main() { - let mut core = Core::new().unwrap(); - let handle = core.handle(); - +#[tokio::main] +async fn main() { let session_config = SessionConfig::default(); let args: Vec<_> = env::args().collect(); if args.len() != 4 { - println!("Usage: {} USERNAME PASSWORD CLIENT_ID", args[0]); + eprintln!("Usage: {} USERNAME PASSWORD CLIENT_ID", args[0]); + return; } - let username = args[1].to_owned(); - let password = args[2].to_owned(); - let client_id = &args[3]; println!("Connecting.."); - let credentials = Credentials::with_password(username, password); - let session = core - .run(Session::connect(session_config, credentials, None, handle)) - .unwrap(); + let credentials = Credentials::with_password(&args[1], &args[2]); + let session = Session::connect(session_config, credentials, None).await.unwrap(); println!( "Token: {:#?}", - core.run(keymaster::get_token(&session, &client_id, SCOPES)) + keymaster::get_token(&session, &args[3], SCOPES) + .await .unwrap() ); } diff --git a/examples/play.rs b/examples/play.rs index 4ba4c5b..9b1988a 100644 --- a/examples/play.rs +++ b/examples/play.rs @@ -1,47 +1,41 @@ use std::env; -use tokio_core::reactor::Core; use librespot::core::authentication::Credentials; use librespot::core::config::SessionConfig; use librespot::core::session::Session; use librespot::core::spotify_id::SpotifyId; use librespot::playback::config::PlayerConfig; - use librespot::playback::audio_backend; use librespot::playback::player::Player; -fn main() { - let mut core = Core::new().unwrap(); - let handle = core.handle(); - +#[tokio::main] +async fn main() { let session_config = SessionConfig::default(); let player_config = PlayerConfig::default(); let args: Vec<_> = env::args().collect(); if args.len() != 4 { - println!("Usage: {} USERNAME PASSWORD TRACK", args[0]); + eprintln!("Usage: {} USERNAME PASSWORD TRACK", args[0]); + return; } - let username = args[1].to_owned(); - let password = args[2].to_owned(); - let credentials = Credentials::with_password(username, password); + let credentials = Credentials::with_password(&args[1], &args[2]); let track = SpotifyId::from_base62(&args[3]).unwrap(); let backend = audio_backend::find(None).unwrap(); println!("Connecting .."); - let session = core - .run(Session::connect(session_config, credentials, None, handle)) - .unwrap(); + let session = Session::connect(session_config, credentials, None).await.unwrap(); - let (mut player, _) = Player::new(player_config, session.clone(), None, move || { - (backend)(None) + let (mut player, _) = Player::new(player_config, session, None, move || { + backend(None) }); player.load(track, true, 0); println!("Playing..."); - core.run(player.get_end_of_track_future()).unwrap(); + + player.await_end_of_track().await; println!("Done"); } diff --git a/examples/playlist_tracks.rs b/examples/playlist_tracks.rs index fc288d1..7bd95ae 100644 --- a/examples/playlist_tracks.rs +++ b/examples/playlist_tracks.rs @@ -1,6 +1,5 @@ use env_logger; use std::env; -use tokio_core::reactor::Core; use librespot::core::authentication::Credentials; use librespot::core::config::SessionConfig; @@ -8,35 +7,30 @@ use librespot::core::session::Session; use librespot::core::spotify_id::SpotifyId; use librespot::metadata::{Metadata, Playlist, Track}; -fn main() { +#[tokio::main] +async fn main() { env_logger::init(); - let mut core = Core::new().unwrap(); - let handle = core.handle(); - let session_config = SessionConfig::default(); let args: Vec<_> = env::args().collect(); if args.len() != 4 { - println!("Usage: {} USERNAME PASSWORD PLAYLIST", args[0]); + eprintln!("Usage: {} USERNAME PASSWORD PLAYLIST", args[0]); + return; } - let username = args[1].to_owned(); - let password = args[2].to_owned(); - let credentials = Credentials::with_password(username, password); + let credentials = Credentials::with_password(&args[1], &args[2]); - let uri_split = args[3].split(":"); + let uri_split = args[3].split(':'); let uri_parts: Vec<&str> = uri_split.collect(); println!("{}, {}, {}", uri_parts[0], uri_parts[1], uri_parts[2]); let plist_uri = SpotifyId::from_base62(uri_parts[2]).unwrap(); - let session = core - .run(Session::connect(session_config, credentials, None, handle)) - .unwrap(); + let session = Session::connect(session_config, credentials, None).await.unwrap(); - let plist = core.run(Playlist::get(&session, plist_uri)).unwrap(); + let plist = Playlist::get(&session, plist_uri).await.unwrap(); println!("{:?}", plist); for track_id in plist.tracks { - let plist_track = core.run(Track::get(&session, track_id)).unwrap(); + let plist_track = Track::get(&session, track_id).await.unwrap(); println!("track: {} ", plist_track.name); } } From 95fedf53573c09683b49de4801bcbc46345ff9f4 Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Wed, 17 Mar 2021 21:24:28 +0100 Subject: [PATCH 075/103] Add back hyper-proxy --- Cargo.lock | 101 ++++++++++++++++++----------- Cargo.toml | 1 - core/Cargo.toml | 6 +- core/src/apresolve.rs | 129 ++++++++++++++++++------------------- core/src/connection/mod.rs | 55 +++++++++------- core/src/lib.rs | 15 ++++- core/src/proxytunnel.rs | 62 ++---------------- core/src/session.rs | 6 +- 8 files changed, 178 insertions(+), 197 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 65493a2..c654e70 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -581,6 +581,21 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "futures" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f55667319111d593ba876406af7c409c0ebb44dc4be6132a783ccf163ea14c1" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + [[package]] name = "futures-channel" version = "0.3.13" @@ -588,6 +603,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c2dd2df839b57db9ab69c2c9d8f3e8c81984781937fe2807dc6dcf3b2ad2939" dependencies = [ "futures-core", + "futures-sink", ] [[package]] @@ -607,6 +623,12 @@ dependencies = [ "futures-util", ] +[[package]] +name = "futures-io" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71c2c65c57704c32f5241c1223167c2c3294fd34ac020c807ddbe6db287ba59" + [[package]] name = "futures-macro" version = "0.3.13" @@ -637,10 +659,13 @@ version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1812c7ab8aedf8d6f2701a43e1243acdbcc2b36ab26e2ad421eb99ac963d96d1" dependencies = [ + "futures-channel", "futures-core", + "futures-io", "futures-macro", "futures-sink", "futures-task", + "memchr", "pin-project-lite", "pin-utils", "proc-macro-hack", @@ -851,30 +876,29 @@ dependencies = [ ] [[package]] -name = "h2" -version = "0.3.0" +name = "headers" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b67e66362108efccd8ac053abafc8b7a8d86a37e6e48fc4f6f7485eb5e9e6a5" +checksum = "f0b7591fb62902706ae8e7aaff416b1b0fa2c0fd0878b46dc13baa3712d8a855" dependencies = [ + "base64", + "bitflags", "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", + "headers-core", "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", - "tracing-futures", + "mime", + "sha-1", + "time 0.1.43", ] [[package]] -name = "hashbrown" -version = "0.9.1" +name = "headers-core" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" +checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" +dependencies = [ + "http", +] [[package]] name = "heck" @@ -970,7 +994,6 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2", "http", "http-body", "httparse", @@ -984,6 +1007,21 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-proxy" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca815a891b24fdfb243fa3239c86154392b0953ee584aa1a2a1f66d20cbe75cc" +dependencies = [ + "bytes", + "futures", + "headers", + "http", + "hyper", + "tokio", + "tower-service", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -1022,16 +1060,6 @@ dependencies = [ "libc", ] -[[package]] -name = "indexmap" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb1fa934250de4de8aef298d81c729a7d33d8c239daa3a7575e6b92bfc7313b" -dependencies = [ - "autocfg", - "hashbrown", -] - [[package]] name = "instant" version = "0.1.9" @@ -1333,13 +1361,14 @@ dependencies = [ "base64", "byteorder", "bytes", - "cfg-if 1.0.0", "env_logger", "futures-core", "futures-util", "hmac", + "http", "httparse", "hyper", + "hyper-proxy", "librespot-protocol", "log", "num-bigint", @@ -1469,6 +1498,12 @@ version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + [[package]] name = "miniz_oxide" version = "0.4.3" @@ -2691,16 +2726,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "tracing-futures" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" -dependencies = [ - "pin-project", - "tracing", -] - [[package]] name = "try-lock" version = "0.2.3" diff --git a/Cargo.toml b/Cargo.toml index 6b8cbee..8f3e44c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,7 +59,6 @@ sha-1 = "0.9" [features] apresolve = ["librespot-core/apresolve"] -apresolve-http2 = ["librespot-core/apresolve-http2"] alsa-backend = ["librespot-playback/alsa-backend"] portaudio-backend = ["librespot-playback/portaudio-backend"] diff --git a/core/Cargo.toml b/core/Cargo.toml index 373e308..ff4d286 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -17,12 +17,13 @@ aes = "0.6" base64 = "0.13" byteorder = "1.4" bytes = "1.0" -cfg-if = "1" futures-core = { version = "0.3", default-features = false } futures-util = { version = "0.3", default-features = false, features = ["alloc", "bilock", "unstable", "sink"] } hmac = "0.10" httparse = "1.3" +http = "0.2" hyper = { version = "0.14", optional = true, features = ["client", "tcp", "http1"] } +hyper-proxy = { version = "0.9.1", optional = true, default-features = false } log = "0.4" num-bigint = "0.3" num-integer = "0.1" @@ -50,5 +51,4 @@ env_logger = "*" tokio = {version = "1.0", features = ["macros"] } [features] -apresolve = ["hyper"] -apresolve-http2 = ["apresolve", "hyper/http2"] +apresolve = ["hyper", "hyper-proxy"] diff --git a/core/src/apresolve.rs b/core/src/apresolve.rs index 531a3e0..c954dab 100644 --- a/core/src/apresolve.rs +++ b/core/src/apresolve.rs @@ -1,73 +1,68 @@ -const AP_FALLBACK: &str = "ap.spotify.com:443"; +use std::error::Error; +use hyper::client::HttpConnector; +use hyper::{Body, Client, Method, Request, Uri}; +use hyper_proxy::{Intercept, Proxy, ProxyConnector}; +use serde::Deserialize; use url::Url; -cfg_if! { - if #[cfg(feature = "apresolve")] { - const APRESOLVE_ENDPOINT: &str = "http://apresolve.spotify.com:80"; +use super::AP_FALLBACK; - use std::error::Error; +const APRESOLVE_ENDPOINT: &str = "http://apresolve.spotify.com:80"; - use hyper::{Body, Client, Method, Request, Uri}; - use serde::{Serialize, Deserialize}; - - use crate::proxytunnel::ProxyTunnel; - - #[derive(Clone, Debug, Serialize, Deserialize)] - pub struct APResolveData { - ap_list: Vec, - } - - async fn apresolve(proxy: &Option, ap_port: &Option) -> Result> { - let port = ap_port.unwrap_or(443); - - let req = Request::builder() - .method(Method::GET) - .uri( - APRESOLVE_ENDPOINT - .parse::() - .expect("invalid AP resolve URL"), - ) - .body(Body::empty())?; - - let response = if let Some(url) = proxy { - Client::builder() - .build(ProxyTunnel::new(&url.socket_addrs(|| None)?[..])?) - .request(req) - .await? - } else { - Client::new().request(req).await? - }; - - let body = hyper::body::to_bytes(response.into_body()).await?; - let data: APResolveData = serde_json::from_slice(body.as_ref())?; - - let ap = if ap_port.is_some() || proxy.is_some() { - data.ap_list.into_iter().find_map(|ap| { - if ap.parse::().ok()?.port()? == port { - Some(ap) - } else { - None - } - }) - } else { - data.ap_list.into_iter().next() - } - .ok_or("empty AP List")?; - - Ok(ap) - } - - pub async fn apresolve_or_fallback(proxy: &Option, ap_port: &Option) -> String { - apresolve(proxy, ap_port).await.unwrap_or_else(|e| { - warn!("Failed to resolve Access Point: {}", e); - warn!("Using fallback \"{}\"", AP_FALLBACK); - AP_FALLBACK.into() - }) - } - } else { - pub async fn apresolve_or_fallback(_: &Option, _: &Option) -> String { - AP_FALLBACK.to_string() - } - } +#[derive(Clone, Debug, Deserialize)] +struct APResolveData { + ap_list: Vec, +} + +async fn try_apresolve( + proxy: Option<&Url>, + ap_port: Option, +) -> Result> { + let port = ap_port.unwrap_or(443); + + let mut req = Request::new(Body::empty()); + *req.method_mut() = Method::GET; + // panic safety: APRESOLVE_ENDPOINT above is valid url. + *req.uri_mut() = APRESOLVE_ENDPOINT.parse().expect("invalid AP resolve URL"); + + let response = if let Some(url) = proxy { + // Panic safety: all URLs are valid URIs + let uri = url.to_string().parse().unwrap(); + let proxy = Proxy::new(Intercept::All, uri); + let connector = HttpConnector::new(); + let proxy_connector = ProxyConnector::from_proxy_unsecured(connector, proxy); + Client::builder() + .build(proxy_connector) + .request(req) + .await? + } else { + Client::new().request(req).await? + }; + + let body = hyper::body::to_bytes(response.into_body()).await?; + let data: APResolveData = serde_json::from_slice(body.as_ref())?; + + let ap = if ap_port.is_some() || proxy.is_some() { + data.ap_list.into_iter().find_map(|ap| { + if ap.parse::().ok()?.port()? == port { + Some(ap) + } else { + None + } + }) + } else { + data.ap_list.into_iter().next() + } + .ok_or("empty AP List")?; + + Ok(ap) +} + +pub async fn apresolve(proxy: Option<&Url>, ap_port: Option) -> String { + try_apresolve(proxy, ap_port).await.unwrap_or_else(|e| { + warn!("Failed to resolve Access Point: {}", e); + warn!("Using fallback \"{}\"", AP_FALLBACK); + AP_FALLBACK.into() + }) } diff --git a/core/src/connection/mod.rs b/core/src/connection/mod.rs index b715d35..ab35366 100644 --- a/core/src/connection/mod.rs +++ b/core/src/connection/mod.rs @@ -58,25 +58,11 @@ impl From for AuthenticationError { } } -pub async fn connect(addr: String, proxy: &Option) -> io::Result { - let socket = if let Some(proxy) = proxy { - info!("Using proxy \"{}\"", proxy); +pub async fn connect(addr: String, proxy: Option<&Url>) -> io::Result { + let socket = if let Some(proxy_url) = proxy { + info!("Using proxy \"{}\"", proxy_url); - let mut split = addr.rsplit(':'); - - let port = split - .next() - .unwrap() // will never panic, split iterator contains at least one element - .parse() - .map_err(|e| { - io::Error::new(io::ErrorKind::InvalidInput, format!("Invalid port: {}", e)) - })?; - - let host = split - .next() - .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "Missing port"))?; - - let socket_addr = proxy.socket_addrs(|| None).and_then(|addrs| { + let socket_addr = proxy_url.socket_addrs(|| None).and_then(|addrs| { addrs.into_iter().next().ok_or_else(|| { io::Error::new( io::ErrorKind::NotFound, @@ -86,13 +72,34 @@ pub async fn connect(addr: String, proxy: &Option) -> io::Result })?; let socket = TcpStream::connect(&socket_addr).await?; - proxytunnel::connect(socket, host, port).await? - } else { - let socket_addr = addr.to_socket_addrs().and_then(|mut iter| { - iter.next().ok_or_else(|| { - io::Error::new(io::ErrorKind::NotFound, "Can't resolve server address") - }) + let uri = addr.parse::().map_err(|_| { + io::Error::new( + io::ErrorKind::InvalidData, + "Can't parse access point address", + ) })?; + let host = uri.host().ok_or_else(|| { + io::Error::new( + io::ErrorKind::InvalidInput, + "The access point address contains no hostname", + ) + })?; + let port = uri.port().ok_or_else(|| { + io::Error::new( + io::ErrorKind::InvalidInput, + "The access point address contains no port", + ) + })?; + + proxytunnel::proxy_connect(socket, host, port.as_str()).await? + } else { + let socket_addr = addr.to_socket_addrs()?.next().ok_or_else(|| { + io::Error::new( + io::ErrorKind::NotFound, + "Can't resolve access point address", + ) + })?; + TcpStream::connect(&socket_addr).await? }; diff --git a/core/src/lib.rs b/core/src/lib.rs index 4ebe858..320967f 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -2,15 +2,12 @@ #[macro_use] extern crate log; -#[macro_use] -extern crate cfg_if; use librespot_protocol as protocol; #[macro_use] mod component; -mod apresolve; pub mod audio_key; pub mod authentication; pub mod cache; @@ -25,3 +22,15 @@ pub mod session; pub mod spotify_id; pub mod util; pub mod version; + +const AP_FALLBACK: &str = "ap.spotify.com:443"; + +#[cfg(feature = "apresolve")] +mod apresolve; + +#[cfg(not(feature = "apresolve"))] +mod apresolve { + pub async fn apresolve(_: Option<&url::Url>, _: Option) -> String { + return super::AP_FALLBACK.into(); + } +} diff --git a/core/src/proxytunnel.rs b/core/src/proxytunnel.rs index 158d314..6f1587f 100644 --- a/core/src/proxytunnel.rs +++ b/core/src/proxytunnel.rs @@ -2,16 +2,16 @@ use std::io; use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; -pub async fn connect( +pub async fn proxy_connect( mut proxy_connection: T, connect_host: &str, - connect_port: u16, + connect_port: &str, ) -> io::Result { let mut buffer = Vec::new(); buffer.extend_from_slice(b"CONNECT "); buffer.extend_from_slice(connect_host.as_bytes()); buffer.push(b':'); - buffer.extend_from_slice(connect_port.to_string().as_bytes()); + buffer.extend_from_slice(connect_port.as_bytes()); buffer.extend_from_slice(b" HTTP/1.1\r\n\r\n"); proxy_connection.write_all(buffer.as_ref()).await?; @@ -49,61 +49,7 @@ pub async fn connect( } if offset >= buffer.len() { - buffer.resize(buffer.len() * 2, 0); - } - } -} - -cfg_if! { - if #[cfg(feature = "apresolve")] { - use std::future::Future; - use std::net::{SocketAddr, ToSocketAddrs}; - use std::pin::Pin; - use std::task::Poll; - - use hyper::service::Service; - use hyper::Uri; - use tokio::net::TcpStream; - - #[derive(Clone)] - pub struct ProxyTunnel { - proxy_addr: SocketAddr, - } - - impl ProxyTunnel { - pub fn new(addr: T) -> io::Result { - let addr = addr.to_socket_addrs()?.next().ok_or_else(|| { - io::Error::new(io::ErrorKind::InvalidInput, "No socket address given") - })?; - Ok(Self { proxy_addr: addr }) - } - } - - impl Service for ProxyTunnel { - type Response = TcpStream; - type Error = io::Error; - type Future = Pin> + Send>>; - - fn poll_ready(&mut self, _: &mut std::task::Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } - - fn call(&mut self, url: Uri) -> Self::Future { - let proxy_addr = self.proxy_addr; - let fut = async move { - let host = url - .host() - .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "Host is missing"))?; - let port = url - .port() - .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "Port is missing"))?; - - let conn = TcpStream::connect(proxy_addr).await?; - connect(conn, host, port.as_u16()).await - }; - - Box::pin(fut) - } + buffer.resize(buffer.len() + 100, 0); } } } diff --git a/core/src/session.rs b/core/src/session.rs index 53bbabd..d7e478f 100644 --- a/core/src/session.rs +++ b/core/src/session.rs @@ -16,7 +16,7 @@ use thiserror::Error; use tokio::sync::mpsc; use tokio_stream::wrappers::UnboundedReceiverStream; -use crate::apresolve::apresolve_or_fallback; +use crate::apresolve::apresolve; use crate::audio_key::AudioKeyManager; use crate::authentication::Credentials; use crate::cache::Cache; @@ -67,10 +67,10 @@ impl Session { credentials: Credentials, cache: Option, ) -> Result { - let ap = apresolve_or_fallback(&config.proxy, &config.ap_port).await; + let ap = apresolve(config.proxy.as_ref(), config.ap_port).await; info!("Connecting to AP \"{}\"", ap); - let mut conn = connection::connect(ap, &config.proxy).await?; + let mut conn = connection::connect(ap, config.proxy.as_ref()).await?; let reusable_credentials = connection::authenticate(&mut conn, credentials, &config.device_id).await?; From bfca1ec15eae36637b55dac9f7080d8289100546 Mon Sep 17 00:00:00 2001 From: Roderick van Domburg Date: Sat, 27 Mar 2021 21:13:14 +0100 Subject: [PATCH 076/103] Minor code improvements and crates bump --- Cargo.lock | 16 ++++++++-------- audio/Cargo.toml | 4 ++-- playback/Cargo.toml | 4 ++-- playback/src/audio_backend/portaudio.rs | 25 +++++++++++++------------ playback/src/audio_backend/rodio.rs | 12 ++++++------ 5 files changed, 31 insertions(+), 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2d71176..f40f4dc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -795,9 +795,9 @@ dependencies = [ [[package]] name = "gstreamer" -version = "0.16.5" +version = "0.16.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d50f822055923f1cbede233aa5dfd4ee957cf328fb3076e330886094e11d6cf" +checksum = "9ff5d0f7ff308ae37e6eb47b6ded17785bdea06e438a708cd09e0288c1862f33" dependencies = [ "bitflags", "cfg-if 1.0.0", @@ -1061,9 +1061,9 @@ checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" [[package]] name = "jack" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c1871c91fa65aa328f3bedbaa54a6e5d1de009264684c153eb708ba933aa6f5" +checksum = "2deb4974bd7e6b2fb7784f27fa13d819d11292b3b004dce0185ec08163cf686a" dependencies = [ "bitflags", "jack-sys", @@ -1073,9 +1073,9 @@ dependencies = [ [[package]] name = "jack-sys" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e1d6ab7ada402b6a27912a2b86504be62a48c58313c886fe72a059127acb4d7" +checksum = "57983f0d72dfecf2b719ed39bc9cacd85194e1a94cb3f9146009eff9856fef41" dependencies = [ "lazy_static", "libc", @@ -1161,9 +1161,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.90" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba4aede83fc3617411dc6993bc8c70919750c1c257c6ca6a502aed6e0e2394ae" +checksum = "8916b1f6ca17130ec6568feccee27c156ad12037880833a3b842a823236502e7" [[package]] name = "libloading" diff --git a/audio/Cargo.toml b/audio/Cargo.toml index 06f1dda..2d67f4c 100644 --- a/audio/Cargo.toml +++ b/audio/Cargo.toml @@ -24,8 +24,8 @@ num-traits = "0.2" tempfile = "3.1" zerocopy = "0.3" -librespot-tremor = { version = "0.2.0", optional = true } -vorbis = { version ="0.0.14", optional = true } +librespot-tremor = { version = "0.2", optional = true } +vorbis = { version ="0.0", optional = true } [features] with-tremor = ["librespot-tremor"] diff --git a/playback/Cargo.toml b/playback/Cargo.toml index 952ecde..07e3179 100644 --- a/playback/Cargo.toml +++ b/playback/Cargo.toml @@ -25,8 +25,8 @@ shell-words = "1.0.0" alsa = { version = "0.5", optional = true } portaudio-rs = { version = "0.3", optional = true } -libpulse-binding = { version = "2.13", optional = true, default-features = false } -libpulse-simple-binding = { version = "2.13", optional = true, default-features = false } +libpulse-binding = { version = "2", optional = true, default-features = false } +libpulse-simple-binding = { version = "2", optional = true, default-features = false } jack = { version = "0.6", optional = true } libc = { version = "0.2", optional = true } rodio = { version = "0.13", optional = true, default-features = false } diff --git a/playback/src/audio_backend/portaudio.rs b/playback/src/audio_backend/portaudio.rs index f29bac2..35f7852 100644 --- a/playback/src/audio_backend/portaudio.rs +++ b/playback/src/audio_backend/portaudio.rs @@ -98,7 +98,7 @@ impl<'a> Open for PortAudioSink<'a> { impl<'a> Sink for PortAudioSink<'a> { fn start(&mut self) -> io::Result<()> { macro_rules! start_sink { - ($stream: ident, $parameters: ident) => {{ + (ref mut $stream: ident, ref $parameters: ident) => {{ if $stream.is_none() { *$stream = Some( Stream::open( @@ -115,10 +115,11 @@ impl<'a> Sink for PortAudioSink<'a> { $stream.as_mut().unwrap().start().unwrap() }}; } + match self { - Self::F32(stream, parameters) => start_sink!(stream, parameters), - Self::S32(stream, parameters) => start_sink!(stream, parameters), - Self::S16(stream, parameters) => start_sink!(stream, parameters), + Self::F32(stream, parameters) => start_sink!(ref mut stream, ref parameters), + Self::S32(stream, parameters) => start_sink!(ref mut stream, ref parameters), + Self::S16(stream, parameters) => start_sink!(ref mut stream, ref parameters), }; Ok(()) @@ -126,15 +127,15 @@ impl<'a> Sink for PortAudioSink<'a> { fn stop(&mut self) -> io::Result<()> { macro_rules! stop_sink { - ($stream: expr) => {{ + (ref mut $stream: ident) => {{ $stream.as_mut().unwrap().stop().unwrap(); *$stream = None; }}; } match self { - Self::F32(stream, _parameters) => stop_sink!(stream), - Self::S32(stream, _parameters) => stop_sink!(stream), - Self::S16(stream, _parameters) => stop_sink!(stream), + Self::F32(stream, _parameters) => stop_sink!(ref mut stream), + Self::S32(stream, _parameters) => stop_sink!(ref mut stream), + Self::S16(stream, _parameters) => stop_sink!(ref mut stream), }; Ok(()) @@ -142,7 +143,7 @@ impl<'a> Sink for PortAudioSink<'a> { fn write(&mut self, packet: &AudioPacket) -> io::Result<()> { macro_rules! write_sink { - ($stream: expr, $samples: expr) => { + (ref mut $stream: expr, $samples: expr) => { $stream.as_mut().unwrap().write($samples) }; } @@ -150,15 +151,15 @@ impl<'a> Sink for PortAudioSink<'a> { let samples = packet.samples(); let result = match self { Self::F32(stream, _parameters) => { - write_sink!(stream, samples) + write_sink!(ref mut stream, samples) } Self::S32(stream, _parameters) => { let samples_s32: &[i32] = &SamplesConverter::to_s32(samples); - write_sink!(stream, samples_s32) + write_sink!(ref mut stream, samples_s32) } Self::S16(stream, _parameters) => { let samples_s16: &[i16] = &SamplesConverter::to_s16(samples); - write_sink!(stream, samples_s16) + write_sink!(ref mut stream, samples_s16) } }; match result { diff --git a/playback/src/audio_backend/rodio.rs b/playback/src/audio_backend/rodio.rs index 2fc4fbd..6e914ea 100644 --- a/playback/src/audio_backend/rodio.rs +++ b/playback/src/audio_backend/rodio.rs @@ -27,14 +27,14 @@ macro_rules! rodio_sink { match self.format { AudioFormat::F32 => { let source = rodio::buffer::SamplesBuffer::new(NUM_CHANNELS as u16, SAMPLE_RATE, samples); - self.rodio_sink.append(source) + self.rodio_sink.append(source); }, AudioFormat::S16 => { let samples_s16: &[i16] = &SamplesConverter::to_s16(samples); let source = rodio::buffer::SamplesBuffer::new(NUM_CHANNELS as u16, SAMPLE_RATE, samples_s16); - self.rodio_sink.append(source) + self.rodio_sink.append(source); }, - _ => unimplemented!(), + _ => unreachable!(), }; // Chunk sizes seem to be about 256 to 3000 ish items long. @@ -64,15 +64,15 @@ macro_rules! rodio_sink { let rodio_device = match_device(&host, device); debug!("Using cpal device"); - let stream = rodio::OutputStream::try_from_device(&rodio_device) + let (stream, stream_handle) = rodio::OutputStream::try_from_device(&rodio_device) .expect("couldn't open output stream."); debug!("Using Rodio stream"); - let sink = rodio::Sink::try_new(&stream.1).expect("couldn't create output sink."); + let sink = rodio::Sink::try_new(&stream_handle).expect("couldn't create output sink."); debug!("Using Rodio sink"); Self { rodio_sink: sink, - stream: stream.0, + stream: stream, format: format, } } From cdbce21e71398e05f042ed2c6bf759402b3f6ac8 Mon Sep 17 00:00:00 2001 From: Roderick van Domburg Date: Sat, 27 Mar 2021 21:42:10 +0100 Subject: [PATCH 077/103] Make S16 to F32 sample conversion less magical --- audio/src/libvorbis_decoder.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/audio/src/libvorbis_decoder.rs b/audio/src/libvorbis_decoder.rs index e7ccc98..e86fc00 100644 --- a/audio/src/libvorbis_decoder.rs +++ b/audio/src/libvorbis_decoder.rs @@ -45,7 +45,7 @@ where packet .data .iter() - .map(|sample| ((*sample as f64 + 0.5) / (0x7FFF as f64 + 0.5)) as f32) + .map(|sample| ((*sample as f64 + 0.5) / (std::i16::MAX as f64 + 0.5)) as f32) .collect(), ))); } From a200b259164f534cdcb151fbd57d0eefbb9ad48e Mon Sep 17 00:00:00 2001 From: Roderick van Domburg Date: Sat, 27 Mar 2021 21:44:01 +0100 Subject: [PATCH 078/103] Fix formatting --- audio/src/libvorbis_decoder.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/audio/src/libvorbis_decoder.rs b/audio/src/libvorbis_decoder.rs index e86fc00..5ad48f0 100644 --- a/audio/src/libvorbis_decoder.rs +++ b/audio/src/libvorbis_decoder.rs @@ -45,7 +45,9 @@ where packet .data .iter() - .map(|sample| ((*sample as f64 + 0.5) / (std::i16::MAX as f64 + 0.5)) as f32) + .map(|sample| { + ((*sample as f64 + 0.5) / (std::i16::MAX as f64 + 0.5)) as f32 + }) .collect(), ))); } From cc60dc11dc15ed8316aac83cf3108a6e79c50d23 Mon Sep 17 00:00:00 2001 From: Roderick van Domburg Date: Sat, 27 Mar 2021 22:52:43 +0100 Subject: [PATCH 079/103] Fix buffer size in JACK Audio backend --- playback/src/audio_backend/jackaudio.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/playback/src/audio_backend/jackaudio.rs b/playback/src/audio_backend/jackaudio.rs index ed6ae1f..7449cc1 100644 --- a/playback/src/audio_backend/jackaudio.rs +++ b/playback/src/audio_backend/jackaudio.rs @@ -53,7 +53,7 @@ impl Open for JackSink { let ch_r = client.register_port("out_0", AudioOut::default()).unwrap(); let ch_l = client.register_port("out_1", AudioOut::default()).unwrap(); // buffer for samples from librespot (~10ms) - let (tx, rx) = sync_channel::(NUM_CHANNELS as usize * 1024 * format.size()); + let (tx, rx) = sync_channel::(NUM_CHANNELS as usize * 1024 * AudioFormat::F32.size()); let jack_data = JackData { rec: rx, port_l: ch_l, From d252eeedc542c7933cbf3a8c3db11b23f1a8f1dd Mon Sep 17 00:00:00 2001 From: Roderick van Domburg Date: Sat, 27 Mar 2021 22:53:05 +0100 Subject: [PATCH 080/103] Warn about broken backends --- playback/src/audio_backend/portaudio.rs | 3 +++ playback/src/audio_backend/rodio.rs | 3 +-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/playback/src/audio_backend/portaudio.rs b/playback/src/audio_backend/portaudio.rs index 35f7852..5faff6c 100644 --- a/playback/src/audio_backend/portaudio.rs +++ b/playback/src/audio_backend/portaudio.rs @@ -55,6 +55,9 @@ impl<'a> Open for PortAudioSink<'a> { fn open(device: Option, format: AudioFormat) -> PortAudioSink<'a> { info!("Using PortAudio sink with format: {:?}", format); + warn!("This backend is known to panic on several platforms."); + warn!("Consider using some other backend, or better yet, contributing a fix."); + portaudio_rs::initialize().unwrap(); let device_idx = match device.as_ref().map(AsRef::as_ref) { diff --git a/playback/src/audio_backend/rodio.rs b/playback/src/audio_backend/rodio.rs index 6e914ea..908099f 100644 --- a/playback/src/audio_backend/rodio.rs +++ b/playback/src/audio_backend/rodio.rs @@ -54,8 +54,7 @@ macro_rules! rodio_sink { AudioFormat::F32 => { #[cfg(target_os = "linux")] { - warn!("Rodio output to Alsa is known to cause garbled sound on output formats other than 16-bit signed integer."); - warn!("Consider using `--backend alsa` OR `--format {:?}`", AudioFormat::S16); + warn!("Rodio output to Alsa is known to cause garbled sound, consider using `--backend alsa`"); } }, AudioFormat::S16 => {}, From 07d710e14f9a1aca94db7d35d33f4f65327a741a Mon Sep 17 00:00:00 2001 From: Roderick van Domburg Date: Wed, 31 Mar 2021 20:41:09 +0200 Subject: [PATCH 081/103] Use AudioFormat size for SDL --- playback/src/audio_backend/sdl.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/playback/src/audio_backend/sdl.rs b/playback/src/audio_backend/sdl.rs index b1b4c2e..7e071dd 100644 --- a/playback/src/audio_backend/sdl.rs +++ b/playback/src/audio_backend/sdl.rs @@ -3,7 +3,7 @@ use crate::audio::{AudioPacket, SamplesConverter}; use crate::config::AudioFormat; use crate::player::{NUM_CHANNELS, SAMPLE_RATE}; use sdl2::audio::{AudioQueue, AudioSpecDesired}; -use std::{io, mem, thread, time}; +use std::{io, thread, time}; pub enum SdlSink { F32(AudioQueue), @@ -93,17 +93,17 @@ impl Sink for SdlSink { let samples = packet.samples(); match self { Self::F32(queue) => { - drain_sink!(queue, mem::size_of::()); + drain_sink!(queue, AudioFormat::F32.size()); queue.queue(samples) } Self::S32(queue) => { let samples_s32: &[i32] = &SamplesConverter::to_s32(samples); - drain_sink!(queue, mem::size_of::()); + drain_sink!(queue, AudioFormat::S32.size()); queue.queue(samples_s32) } Self::S16(queue) => { let samples_s16: &[i16] = &SamplesConverter::to_s16(samples); - drain_sink!(queue, mem::size_of::()); + drain_sink!(queue, AudioFormat::S16.size()); queue.queue(samples_s16) } }; From 78bc621ebba467208358519f58e8bab7d9eafec8 Mon Sep 17 00:00:00 2001 From: Roderick van Domburg Date: Mon, 5 Apr 2021 21:30:40 +0200 Subject: [PATCH 082/103] Move SamplesConverter into convert.rs --- audio/src/convert.rs | 50 ++++++++++++++++++++++++++++++++++++++++++ audio/src/lib.rs | 52 ++------------------------------------------ 2 files changed, 52 insertions(+), 50 deletions(-) create mode 100644 audio/src/convert.rs diff --git a/audio/src/convert.rs b/audio/src/convert.rs new file mode 100644 index 0000000..74a4d8f --- /dev/null +++ b/audio/src/convert.rs @@ -0,0 +1,50 @@ +use zerocopy::AsBytes; + +#[derive(AsBytes, Copy, Clone, Debug)] +#[allow(non_camel_case_types)] +#[repr(transparent)] +pub struct i24([u8; 3]); +impl i24 { + fn pcm_from_i32(sample: i32) -> Self { + // drop the least significant byte + let [a, b, c, _d] = (sample >> 8).to_le_bytes(); + i24([a, b, c]) + } +} + +// Losslessly represent [-1.0, 1.0] to [$type::MIN, $type::MAX] while maintaining DC linearity. +macro_rules! convert_samples_to { + ($type: ident, $samples: expr) => { + convert_samples_to!($type, $samples, 0) + }; + ($type: ident, $samples: expr, $drop_bits: expr) => { + $samples + .iter() + .map(|sample| { + (*sample as f64 * (std::$type::MAX as f64 + 0.5) - 0.5) as $type >> $drop_bits + }) + .collect() + }; +} + +pub struct SamplesConverter {} +impl SamplesConverter { + pub fn to_s32(samples: &[f32]) -> Vec { + convert_samples_to!(i32, samples) + } + + pub fn to_s24(samples: &[f32]) -> Vec { + convert_samples_to!(i32, samples, 8) + } + + pub fn to_s24_3(samples: &[f32]) -> Vec { + Self::to_s32(samples) + .iter() + .map(|sample| i24::pcm_from_i32(*sample)) + .collect() + } + + pub fn to_s16(samples: &[f32]) -> Vec { + convert_samples_to!(i16, samples) + } +} diff --git a/audio/src/lib.rs b/audio/src/lib.rs index fe3b5c9..44f732b 100644 --- a/audio/src/lib.rs +++ b/audio/src/lib.rs @@ -13,6 +13,7 @@ extern crate tempfile; extern crate librespot_core; +mod convert; mod decrypt; mod fetch; @@ -24,6 +25,7 @@ mod passthrough_decoder; mod range_set; +pub use convert::{i24, SamplesConverter}; pub use decrypt::AudioDecrypt; pub use fetch::{AudioFile, AudioFileOpen, StreamLoaderController}; pub use fetch::{ @@ -31,7 +33,6 @@ pub use fetch::{ READ_AHEAD_DURING_PLAYBACK_ROUNDTRIPS, READ_AHEAD_DURING_PLAYBACK_SECONDS, }; use std::fmt; -use zerocopy::AsBytes; pub enum AudioPacket { Samples(Vec), @@ -61,55 +62,6 @@ impl AudioPacket { } } -#[derive(AsBytes, Copy, Clone, Debug)] -#[allow(non_camel_case_types)] -#[repr(transparent)] -pub struct i24([u8; 3]); -impl i24 { - fn pcm_from_i32(sample: i32) -> Self { - // drop the least significant byte - let [a, b, c, _d] = (sample >> 8).to_le_bytes(); - i24([a, b, c]) - } -} - -// Losslessly represent [-1.0, 1.0] to [$type::MIN, $type::MAX] while maintaining DC linearity. -macro_rules! convert_samples_to { - ($type: ident, $samples: expr) => { - convert_samples_to!($type, $samples, 0) - }; - ($type: ident, $samples: expr, $drop_bits: expr) => { - $samples - .iter() - .map(|sample| { - (*sample as f64 * (std::$type::MAX as f64 + 0.5) - 0.5) as $type >> $drop_bits - }) - .collect() - }; -} - -pub struct SamplesConverter {} -impl SamplesConverter { - pub fn to_s32(samples: &[f32]) -> Vec { - convert_samples_to!(i32, samples) - } - - pub fn to_s24(samples: &[f32]) -> Vec { - convert_samples_to!(i32, samples, 8) - } - - pub fn to_s24_3(samples: &[f32]) -> Vec { - Self::to_s32(samples) - .iter() - .map(|sample| i24::pcm_from_i32(*sample)) - .collect() - } - - pub fn to_s16(samples: &[f32]) -> Vec { - convert_samples_to!(i16, samples) - } -} - #[cfg(not(any(feature = "with-tremor", feature = "with-vorbis")))] pub use crate::lewton_decoder::{VorbisDecoder, VorbisError}; #[cfg(any(feature = "with-tremor", feature = "with-vorbis"))] From 928a6736538da12e509ec55a626b29bb9e9d54da Mon Sep 17 00:00:00 2001 From: Roderick van Domburg Date: Mon, 5 Apr 2021 23:14:02 +0200 Subject: [PATCH 083/103] DRY up constructors --- playback/src/audio_backend/alsa.rs | 2 +- playback/src/audio_backend/gstreamer.rs | 2 +- playback/src/audio_backend/jackaudio.rs | 2 +- playback/src/audio_backend/pipe.rs | 2 +- playback/src/audio_backend/pulseaudio.rs | 2 +- playback/src/audio_backend/sdl.rs | 2 +- playback/src/audio_backend/subprocess.rs | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/playback/src/audio_backend/alsa.rs b/playback/src/audio_backend/alsa.rs index fc2a775..1d55187 100644 --- a/playback/src/audio_backend/alsa.rs +++ b/playback/src/audio_backend/alsa.rs @@ -71,7 +71,7 @@ fn open_device(dev_name: &str, format: AudioFormat) -> Result<(PCM, Frames), Box } impl Open for AlsaSink { - fn open(device: Option, format: AudioFormat) -> AlsaSink { + fn open(device: Option, format: AudioFormat) -> Self { info!("Using Alsa sink with format: {:?}", format); let name = match device.as_ref().map(AsRef::as_ref) { diff --git a/playback/src/audio_backend/gstreamer.rs b/playback/src/audio_backend/gstreamer.rs index d3c736a..d59677a 100644 --- a/playback/src/audio_backend/gstreamer.rs +++ b/playback/src/audio_backend/gstreamer.rs @@ -16,7 +16,7 @@ pub struct GstreamerSink { } impl Open for GstreamerSink { - fn open(device: Option, format: AudioFormat) -> GstreamerSink { + fn open(device: Option, format: AudioFormat) -> Self { info!("Using GStreamer sink with format: {:?}", format); gst::init().expect("failed to init GStreamer!"); diff --git a/playback/src/audio_backend/jackaudio.rs b/playback/src/audio_backend/jackaudio.rs index 7449cc1..24a94a3 100644 --- a/playback/src/audio_backend/jackaudio.rs +++ b/playback/src/audio_backend/jackaudio.rs @@ -41,7 +41,7 @@ impl ProcessHandler for JackData { } impl Open for JackSink { - fn open(client_name: Option, format: AudioFormat) -> JackSink { + fn open(client_name: Option, format: AudioFormat) -> Self { if format != AudioFormat::F32 { warn!("JACK currently does not support {:?} output", format); } diff --git a/playback/src/audio_backend/pipe.rs b/playback/src/audio_backend/pipe.rs index ae77e32..6948db9 100644 --- a/playback/src/audio_backend/pipe.rs +++ b/playback/src/audio_backend/pipe.rs @@ -10,7 +10,7 @@ pub struct StdoutSink { } impl Open for StdoutSink { - fn open(path: Option, format: AudioFormat) -> StdoutSink { + fn open(path: Option, format: AudioFormat) -> Self { info!("Using pipe sink with format: {:?}", format); let output: Box = match path { diff --git a/playback/src/audio_backend/pulseaudio.rs b/playback/src/audio_backend/pulseaudio.rs index 16800eb..507e453 100644 --- a/playback/src/audio_backend/pulseaudio.rs +++ b/playback/src/audio_backend/pulseaudio.rs @@ -17,7 +17,7 @@ pub struct PulseAudioSink { } impl Open for PulseAudioSink { - fn open(device: Option, format: AudioFormat) -> PulseAudioSink { + fn open(device: Option, format: AudioFormat) -> Self { info!("Using PulseAudio sink with format: {:?}", format); // PulseAudio calls S24 and S24_3 different from the rest of the world diff --git a/playback/src/audio_backend/sdl.rs b/playback/src/audio_backend/sdl.rs index 7e071dd..0a3fd43 100644 --- a/playback/src/audio_backend/sdl.rs +++ b/playback/src/audio_backend/sdl.rs @@ -12,7 +12,7 @@ pub enum SdlSink { } impl Open for SdlSink { - fn open(device: Option, format: AudioFormat) -> SdlSink { + fn open(device: Option, format: AudioFormat) -> Self { info!("Using SDL sink with format: {:?}", format); if device.is_some() { diff --git a/playback/src/audio_backend/subprocess.rs b/playback/src/audio_backend/subprocess.rs index 586bb75..bebb6ea 100644 --- a/playback/src/audio_backend/subprocess.rs +++ b/playback/src/audio_backend/subprocess.rs @@ -12,7 +12,7 @@ pub struct SubprocessSink { } impl Open for SubprocessSink { - fn open(shell_command: Option, format: AudioFormat) -> SubprocessSink { + fn open(shell_command: Option, format: AudioFormat) -> Self { info!("Using subprocess sink with format: {:?}", format); if let Some(shell_command) = shell_command { From 7c3d89112dc0cdb4a8105592268bbb6fe9f035f8 Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Wed, 31 Mar 2021 20:05:32 +0200 Subject: [PATCH 084/103] Fix clippy warnings --- audio/src/passthrough_decoder.rs | 2 +- core/src/apresolve.rs | 4 ++-- core/src/config.rs | 18 +++++++++--------- core/src/connection/codec.rs | 12 ++++++------ core/src/connection/handshake.rs | 10 +++++----- core/src/connection/mod.rs | 4 ++-- core/src/diffie_hellman.rs | 8 ++++---- core/src/mercury/mod.rs | 6 +++--- core/src/mercury/types.rs | 22 +++++++++++----------- core/src/spotify_id.rs | 6 +++--- src/main.rs | 4 +++- 11 files changed, 49 insertions(+), 47 deletions(-) diff --git a/audio/src/passthrough_decoder.rs b/audio/src/passthrough_decoder.rs index d519baf..e064cba 100644 --- a/audio/src/passthrough_decoder.rs +++ b/audio/src/passthrough_decoder.rs @@ -110,7 +110,7 @@ impl AudioDecoder for PassthroughDecoder { fn next_packet(&mut self) -> Result, AudioError> { // write headers if we are (re)starting - if self.bos == false { + if !self.bos { self.wtr .write_packet( self.ident.clone(), diff --git a/core/src/apresolve.rs b/core/src/apresolve.rs index c954dab..85baba6 100644 --- a/core/src/apresolve.rs +++ b/core/src/apresolve.rs @@ -11,7 +11,7 @@ use super::AP_FALLBACK; const APRESOLVE_ENDPOINT: &str = "http://apresolve.spotify.com:80"; #[derive(Clone, Debug, Deserialize)] -struct APResolveData { +struct ApResolveData { ap_list: Vec, } @@ -41,7 +41,7 @@ async fn try_apresolve( }; let body = hyper::body::to_bytes(response.into_body()).await?; - let data: APResolveData = serde_json::from_slice(body.as_ref())?; + let data: ApResolveData = serde_json::from_slice(body.as_ref())?; let ap = if ap_port.is_some() || proxy.is_some() { data.ap_list.into_iter().find_map(|ap| { diff --git a/core/src/config.rs b/core/src/config.rs index a9e5ea2..9c70c25 100644 --- a/core/src/config.rs +++ b/core/src/config.rs @@ -29,9 +29,9 @@ pub enum DeviceType { Tablet = 2, Smartphone = 3, Speaker = 4, - TV = 5, - AVR = 6, - STB = 7, + Tv = 5, + Avr = 6, + Stb = 7, AudioDongle = 8, GameConsole = 9, CastAudio = 10, @@ -54,9 +54,9 @@ impl FromStr for DeviceType { "tablet" => Ok(Tablet), "smartphone" => Ok(Smartphone), "speaker" => Ok(Speaker), - "tv" => Ok(TV), - "avr" => Ok(AVR), - "stb" => Ok(STB), + "tv" => Ok(Tv), + "avr" => Ok(Avr), + "stb" => Ok(Stb), "audiodongle" => Ok(AudioDongle), "gameconsole" => Ok(GameConsole), "castaudio" => Ok(CastAudio), @@ -80,9 +80,9 @@ impl fmt::Display for DeviceType { Tablet => f.write_str("Tablet"), Smartphone => f.write_str("Smartphone"), Speaker => f.write_str("Speaker"), - TV => f.write_str("TV"), - AVR => f.write_str("AVR"), - STB => f.write_str("STB"), + Tv => f.write_str("TV"), + Avr => f.write_str("AVR"), + Stb => f.write_str("STB"), AudioDongle => f.write_str("AudioDongle"), GameConsole => f.write_str("GameConsole"), CastAudio => f.write_str("CastAudio"), diff --git a/core/src/connection/codec.rs b/core/src/connection/codec.rs index ead07b6..299220f 100644 --- a/core/src/connection/codec.rs +++ b/core/src/connection/codec.rs @@ -13,7 +13,7 @@ enum DecodeState { Payload(u8, usize), } -pub struct APCodec { +pub struct ApCodec { encode_nonce: u32, encode_cipher: Shannon, @@ -22,9 +22,9 @@ pub struct APCodec { decode_state: DecodeState, } -impl APCodec { - pub fn new(send_key: &[u8], recv_key: &[u8]) -> APCodec { - APCodec { +impl ApCodec { + pub fn new(send_key: &[u8], recv_key: &[u8]) -> ApCodec { + ApCodec { encode_nonce: 0, encode_cipher: Shannon::new(send_key), @@ -35,7 +35,7 @@ impl APCodec { } } -impl Encoder<(u8, Vec)> for APCodec { +impl Encoder<(u8, Vec)> for ApCodec { type Error = io::Error; fn encode(&mut self, item: (u8, Vec), buf: &mut BytesMut) -> io::Result<()> { @@ -60,7 +60,7 @@ impl Encoder<(u8, Vec)> for APCodec { } } -impl Decoder for APCodec { +impl Decoder for ApCodec { type Item = (u8, Bytes); type Error = io::Error; diff --git a/core/src/connection/handshake.rs b/core/src/connection/handshake.rs index 67a786e..879df67 100644 --- a/core/src/connection/handshake.rs +++ b/core/src/connection/handshake.rs @@ -7,16 +7,16 @@ use std::io; use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; use tokio_util::codec::{Decoder, Framed}; -use super::codec::APCodec; -use crate::diffie_hellman::DHLocalKeys; +use super::codec::ApCodec; +use crate::diffie_hellman::DhLocalKeys; use crate::protocol; use crate::protocol::keyexchange::{APResponseMessage, ClientHello, ClientResponsePlaintext}; use crate::util; pub async fn handshake( mut connection: T, -) -> io::Result> { - let local_keys = DHLocalKeys::random(&mut thread_rng()); +) -> io::Result> { + let local_keys = DhLocalKeys::random(&mut thread_rng()); let gc = local_keys.public_key(); let mut accumulator = client_hello(&mut connection, gc).await?; let message: APResponseMessage = recv_packet(&mut connection, &mut accumulator).await?; @@ -29,7 +29,7 @@ pub async fn handshake( let shared_secret = local_keys.shared_secret(&remote_key); let (challenge, send_key, recv_key) = compute_keys(&shared_secret, &accumulator); - let codec = APCodec::new(&send_key, &recv_key); + let codec = ApCodec::new(&send_key, &recv_key); client_response(&mut connection, challenge).await?; diff --git a/core/src/connection/mod.rs b/core/src/connection/mod.rs index 56d579b..d8a4012 100644 --- a/core/src/connection/mod.rs +++ b/core/src/connection/mod.rs @@ -1,7 +1,7 @@ mod codec; mod handshake; -pub use self::codec::APCodec; +pub use self::codec::ApCodec; pub use self::handshake::handshake; use std::io::{self, ErrorKind}; @@ -19,7 +19,7 @@ use crate::protocol::keyexchange::{APLoginFailed, ErrorCode}; use crate::proxytunnel; use crate::version; -pub type Transport = Framed; +pub type Transport = Framed; fn login_error_message(code: &ErrorCode) -> &'static str { pub use ErrorCode::*; diff --git a/core/src/diffie_hellman.rs b/core/src/diffie_hellman.rs index 8544809..42d1fd9 100644 --- a/core/src/diffie_hellman.rs +++ b/core/src/diffie_hellman.rs @@ -17,19 +17,19 @@ pub static DH_PRIME: Lazy = Lazy::new(|| { ]) }); -pub struct DHLocalKeys { +pub struct DhLocalKeys { private_key: BigUint, public_key: BigUint, } -impl DHLocalKeys { - pub fn random(rng: &mut R) -> DHLocalKeys { +impl DhLocalKeys { + pub fn random(rng: &mut R) -> DhLocalKeys { let key_data = util::rand_vec(rng, 95); let private_key = BigUint::from_bytes_be(&key_data); let public_key = util::powm(&DH_GENERATOR, &private_key, &DH_PRIME); - DHLocalKeys { + DhLocalKeys { private_key, public_key, } diff --git a/core/src/mercury/mod.rs b/core/src/mercury/mod.rs index 1a68e15..b920e7e 100644 --- a/core/src/mercury/mod.rs +++ b/core/src/mercury/mod.rs @@ -82,7 +82,7 @@ impl MercuryManager { pub fn get>(&self, uri: T) -> MercuryFuture { self.request(MercuryRequest { - method: MercuryMethod::GET, + method: MercuryMethod::Get, uri: uri.into(), content_type: None, payload: Vec::new(), @@ -91,7 +91,7 @@ impl MercuryManager { pub fn send>(&self, uri: T, data: Vec) -> MercuryFuture { self.request(MercuryRequest { - method: MercuryMethod::SEND, + method: MercuryMethod::Send, uri: uri.into(), content_type: None, payload: vec![data], @@ -109,7 +109,7 @@ impl MercuryManager { { let uri = uri.into(); let request = self.request(MercuryRequest { - method: MercuryMethod::SUB, + method: MercuryMethod::Sub, uri: uri.clone(), content_type: None, payload: Vec::new(), diff --git a/core/src/mercury/types.rs b/core/src/mercury/types.rs index 57cedce..402a954 100644 --- a/core/src/mercury/types.rs +++ b/core/src/mercury/types.rs @@ -6,10 +6,10 @@ use crate::protocol; #[derive(Debug, PartialEq, Eq)] pub enum MercuryMethod { - GET, - SUB, - UNSUB, - SEND, + Get, + Sub, + Unsub, + Send, } #[derive(Debug)] @@ -33,10 +33,10 @@ pub struct MercuryError; impl ToString for MercuryMethod { fn to_string(&self) -> String { match *self { - MercuryMethod::GET => "GET", - MercuryMethod::SUB => "SUB", - MercuryMethod::UNSUB => "UNSUB", - MercuryMethod::SEND => "SEND", + MercuryMethod::Get => "GET", + MercuryMethod::Sub => "SUB", + MercuryMethod::Unsub => "UNSUB", + MercuryMethod::Send => "SEND", } .to_owned() } @@ -45,9 +45,9 @@ impl ToString for MercuryMethod { impl MercuryMethod { pub fn command(&self) -> u8 { match *self { - MercuryMethod::GET | MercuryMethod::SEND => 0xb2, - MercuryMethod::SUB => 0xb3, - MercuryMethod::UNSUB => 0xb4, + MercuryMethod::Get | MercuryMethod::Send => 0xb2, + MercuryMethod::Sub => 0xb3, + MercuryMethod::Unsub => 0xb4, } } } diff --git a/core/src/spotify_id.rs b/core/src/spotify_id.rs index 09d1097..801c6ac 100644 --- a/core/src/spotify_id.rs +++ b/core/src/spotify_id.rs @@ -18,9 +18,9 @@ impl From<&str> for SpotifyAudioType { } } -impl Into<&str> for SpotifyAudioType { - fn into(self) -> &'static str { - match self { +impl From for &str { + fn from(audio_type: SpotifyAudioType) -> &'static str { + match audio_type { SpotifyAudioType::Track => "track", SpotifyAudioType::Podcast => "episode", SpotifyAudioType::NonPlayable => "unknown", diff --git a/src/main.rs b/src/main.rs index 1f67793..cd2a786 100644 --- a/src/main.rs +++ b/src/main.rs @@ -345,7 +345,9 @@ fn setup(args: &[String]) -> Setup { .map(|port| port.parse::().unwrap()) .unwrap_or(0); - let name = matches.opt_str("name").unwrap_or("Librespot".to_string()); + let name = matches + .opt_str("name") + .unwrap_or_else(|| "Librespot".to_string()); let credentials = { let cached_credentials = cache.as_ref().and_then(Cache::credentials); From 11ce29077e661100fa71580dbcb9dda6a4950932 Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Thu, 1 Apr 2021 18:10:42 +0200 Subject: [PATCH 085/103] Fix formatting --- core/src/authentication.rs | 7 ++----- examples/get_token.rs | 4 +++- examples/play.rs | 10 +++++----- examples/playlist_tracks.rs | 4 +++- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/core/src/authentication.rs b/core/src/authentication.rs index 2992abc..db787bb 100644 --- a/core/src/authentication.rs +++ b/core/src/authentication.rs @@ -31,13 +31,10 @@ impl Credentials { /// ### Example /// ```rust /// use librespot_core::authentication::Credentials; - /// + /// /// let creds = Credentials::with_password("my account", "my password"); /// ``` - pub fn with_password( - username: impl Into, - password: impl Into, - ) -> Credentials { + pub fn with_password(username: impl Into, password: impl Into) -> Credentials { Credentials { username: username.into(), auth_type: AuthenticationType::AUTHENTICATION_USER_PASS, diff --git a/examples/get_token.rs b/examples/get_token.rs index 15b97bc..636155e 100644 --- a/examples/get_token.rs +++ b/examples/get_token.rs @@ -20,7 +20,9 @@ async fn main() { println!("Connecting.."); let credentials = Credentials::with_password(&args[1], &args[2]); - let session = Session::connect(session_config, credentials, None).await.unwrap(); + let session = Session::connect(session_config, credentials, None) + .await + .unwrap(); println!( "Token: {:#?}", diff --git a/examples/play.rs b/examples/play.rs index 9b1988a..b4f2b68 100644 --- a/examples/play.rs +++ b/examples/play.rs @@ -4,8 +4,8 @@ use librespot::core::authentication::Credentials; use librespot::core::config::SessionConfig; use librespot::core::session::Session; use librespot::core::spotify_id::SpotifyId; -use librespot::playback::config::PlayerConfig; use librespot::playback::audio_backend; +use librespot::playback::config::PlayerConfig; use librespot::playback::player::Player; #[tokio::main] @@ -25,11 +25,11 @@ async fn main() { let backend = audio_backend::find(None).unwrap(); println!("Connecting .."); - let session = Session::connect(session_config, credentials, None).await.unwrap(); + let session = Session::connect(session_config, credentials, None) + .await + .unwrap(); - let (mut player, _) = Player::new(player_config, session, None, move || { - backend(None) - }); + let (mut player, _) = Player::new(player_config, session, None, move || backend(None)); player.load(track, true, 0); diff --git a/examples/playlist_tracks.rs b/examples/playlist_tracks.rs index 7bd95ae..e96938c 100644 --- a/examples/playlist_tracks.rs +++ b/examples/playlist_tracks.rs @@ -25,7 +25,9 @@ async fn main() { let plist_uri = SpotifyId::from_base62(uri_parts[2]).unwrap(); - let session = Session::connect(session_config, credentials, None).await.unwrap(); + let session = Session::connect(session_config, credentials, None) + .await + .unwrap(); let plist = Playlist::get(&session, plist_uri).await.unwrap(); println!("{:?}", plist); From d0ea9631d2c9e0b1be5198061280a3894bce9287 Mon Sep 17 00:00:00 2001 From: Roderick van Domburg Date: Fri, 9 Apr 2021 19:31:26 +0200 Subject: [PATCH 086/103] Optimize requantizer to work in `f32`, then round --- audio/src/convert.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/audio/src/convert.rs b/audio/src/convert.rs index 74a4d8f..e291c80 100644 --- a/audio/src/convert.rs +++ b/audio/src/convert.rs @@ -21,7 +21,16 @@ macro_rules! convert_samples_to { $samples .iter() .map(|sample| { - (*sample as f64 * (std::$type::MAX as f64 + 0.5) - 0.5) as $type >> $drop_bits + // Losslessly represent [-1.0, 1.0] to [$type::MIN, $type::MAX] + // while maintaining DC linearity. There is nothing to be gained + // by doing this in f64, as the significand of a f32 is 24 bits, + // just like the maximum bit depth we are converting to. + let int_value = *sample * (std::$type::MAX as f32 + 0.5) - 0.5; + + // Casting floats to ints truncates by default, which results + // in larger quantization error than rounding arithmetically. + // Flooring is faster, but again with larger error. + int_value.round() as $type >> $drop_bits }) .collect() }; From 222f9bbd01994f55160c3d226641c9e258eee7fd Mon Sep 17 00:00:00 2001 From: Roderick van Domburg Date: Fri, 9 Apr 2021 20:01:21 +0200 Subject: [PATCH 087/103] Bump playback crates to the latest supporting Rust 1.41.1 For Rodio, this fixes garbled sound on some but not all Alsa hosts. --- Cargo.lock | 20 ++++++++++---------- playback/Cargo.toml | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f40f4dc..837df2f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -339,9 +339,9 @@ dependencies = [ [[package]] name = "cpal" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "840981d3f30230d9120328d64be72319dbbedabb61bcd4c370a54cdd051238ac" +checksum = "8351ddf2aaa3c583fa388029f8b3d26f3c7035a20911fdd5f2e2ed7ab57dad25" dependencies = [ "alsa", "core-foundation-sys", @@ -1161,9 +1161,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.91" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8916b1f6ca17130ec6568feccee27c156ad12037880833a3b842a823236502e7" +checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41" [[package]] name = "libloading" @@ -1195,9 +1195,9 @@ dependencies = [ [[package]] name = "libpulse-binding" -version = "2.23.0" +version = "2.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2405f806801527dfb3d2b6d48a282cdebe9a1b41b0652e0d7b5bad81dbc700e" +checksum = "db951f37898e19a6785208e3a290261e0f1a8e086716be596aaad684882ca8e3" dependencies = [ "bitflags", "libc", @@ -2209,9 +2209,9 @@ dependencies = [ [[package]] name = "rodio" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9683532495146e98878d4948fa1a1953f584cd923f2a5f5c26b7a8701b56943" +checksum = "b65c2eda643191f6d1bb12ea323a9db8d9ba95374e9be3780b5a9fb5cfb8520f" dependencies = [ "cpal", ] @@ -2288,9 +2288,9 @@ dependencies = [ [[package]] name = "sdl2-sys" -version = "0.34.3" +version = "0.34.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d81feded049b9c14eceb4a4f6d596a98cebbd59abdba949c5552a015466d33" +checksum = "4cb164f53dbcad111de976bbf1f3083d3fcdeda88da9cfa281c70822720ee3da" dependencies = [ "cfg-if 0.1.10", "libc", diff --git a/playback/Cargo.toml b/playback/Cargo.toml index 07e3179..0666259 100644 --- a/playback/Cargo.toml +++ b/playback/Cargo.toml @@ -31,7 +31,7 @@ jack = { version = "0.6", optional = true } libc = { version = "0.2", optional = true } rodio = { version = "0.13", optional = true, default-features = false } cpal = { version = "0.13", optional = true } -sdl2 = { version = "0.34", optional = true } +sdl2 = { version = "0.34.3", optional = true } gstreamer = { version = "0.16", optional = true } gstreamer-app = { version = "0.16", optional = true } glib = { version = "0.10", optional = true } @@ -45,4 +45,4 @@ jackaudio-backend = ["jack"] rodiojack-backend = ["rodio", "cpal/jack"] rodio-backend = ["rodio", "cpal"] sdl-backend = ["sdl2"] -gstreamer-backend = ["gstreamer", "gstreamer-app", "glib" ] +gstreamer-backend = ["gstreamer", "gstreamer-app", "glib"] From 7ddb1a20bb21ceae17821bba2f3714377d8b1cc3 Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Wed, 10 Mar 2021 22:55:40 +0100 Subject: [PATCH 088/103] Reuse librespot-core's Diffie Hellman in discovery --- Cargo.lock | 1 - connect/Cargo.toml | 1 - connect/src/discovery.rs | 27 +++++++++------------------ 3 files changed, 9 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a43939d..023add2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1344,7 +1344,6 @@ dependencies = [ "librespot-playback", "librespot-protocol", "log", - "num-bigint", "protobuf", "rand", "serde", diff --git a/connect/Cargo.toml b/connect/Cargo.toml index b03de88..1331b62 100644 --- a/connect/Cargo.toml +++ b/connect/Cargo.toml @@ -16,7 +16,6 @@ futures-util = { version = "0.3", default_features = false } hmac = "0.10" hyper = { version = "0.14", features = ["server", "http1", "tcp"] } log = "0.4" -num-bigint = "0.3" protobuf = "~2.14.0" rand = "0.8" serde = { version = "1.0", features = ["derive"] } diff --git a/connect/src/discovery.rs b/connect/src/discovery.rs index 7bb36a2..383035c 100644 --- a/connect/src/discovery.rs +++ b/connect/src/discovery.rs @@ -5,7 +5,6 @@ use futures_core::Stream; use hmac::{Hmac, Mac, NewMac}; use hyper::service::{make_service_fn, service_fn}; use hyper::{Body, Method, Request, Response, StatusCode}; -use num_bigint::BigUint; use serde_json::json; use sha1::{Digest, Sha1}; use tokio::sync::{mpsc, oneshot}; @@ -15,8 +14,7 @@ use dns_sd::DNSService; use librespot_core::authentication::Credentials; use librespot_core::config::ConnectConfig; -use librespot_core::diffie_hellman::{DH_GENERATOR, DH_PRIME}; -use librespot_core::util; +use librespot_core::diffie_hellman::DhLocalKeys; use std::borrow::Cow; use std::collections::BTreeMap; @@ -34,8 +32,7 @@ struct Discovery(Arc); struct DiscoveryInner { config: ConnectConfig, device_id: String, - private_key: BigUint, - public_key: BigUint, + keys: DhLocalKeys, tx: mpsc::UnboundedSender, } @@ -46,15 +43,10 @@ impl Discovery { ) -> (Discovery, mpsc::UnboundedReceiver) { let (tx, rx) = mpsc::unbounded_channel(); - let key_data = util::rand_vec(&mut rand::thread_rng(), 95); - let private_key = BigUint::from_bytes_be(&key_data); - let public_key = util::powm(&DH_GENERATOR, &private_key, &DH_PRIME); - let discovery = Discovery(Arc::new(DiscoveryInner { config, device_id, - private_key, - public_key, + keys: DhLocalKeys::random(&mut rand::thread_rng()), tx, })); @@ -62,8 +54,7 @@ impl Discovery { } fn handle_get_info(&self, _: BTreeMap, Cow<'_, str>>) -> Response { - let public_key = self.0.public_key.to_bytes_be(); - let public_key = base64::encode(&public_key); + let public_key = base64::encode(&self.0.keys.public_key()); let result = json!({ "status": 101, @@ -98,16 +89,16 @@ impl Discovery { let encrypted_blob = base64::decode(encrypted_blob.as_bytes()).unwrap(); - let client_key = base64::decode(client_key.as_bytes()).unwrap(); - let client_key = BigUint::from_bytes_be(&client_key); - - let shared_key = util::powm(&client_key, &self.0.private_key, &DH_PRIME); + let shared_key = self + .0 + .keys + .shared_secret(&base64::decode(client_key.as_bytes()).unwrap()); let iv = &encrypted_blob[0..16]; let encrypted = &encrypted_blob[16..encrypted_blob.len() - 20]; let cksum = &encrypted_blob[encrypted_blob.len() - 20..encrypted_blob.len()]; - let base_key = Sha1::digest(&shared_key.to_bytes_be()); + let base_key = Sha1::digest(&shared_key); let base_key = &base_key[..16]; let checksum_key = { From 9378ae5b6f5ff5a6700e3882058a69a18d547d70 Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Thu, 18 Mar 2021 17:52:39 +0100 Subject: [PATCH 089/103] Bump num-bigint dependency --- Cargo.lock | 4 ++-- core/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 023add2..3991775 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1633,9 +1633,9 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.3.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e9a41747ae4633fce5adffb4d2e81ffc5e89593cb19917f8fb2cc5ff76507bf" +checksum = "4e0d047c1062aa51e256408c560894e5251f08925980e53cf1aa5bd00eec6512" dependencies = [ "autocfg", "num-integer", diff --git a/core/Cargo.toml b/core/Cargo.toml index 7aabb1b..4e9b03c 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -25,7 +25,7 @@ http = "0.2" hyper = { version = "0.14", optional = true, features = ["client", "tcp", "http1"] } hyper-proxy = { version = "0.9.1", optional = true, default-features = false } log = "0.4" -num-bigint = "0.3" +num-bigint = "0.4" num-integer = "0.1" num-traits = "0.2" once_cell = "1.5.2" From e688e7e886791b98e5af0271f5ec552c6a699cca Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Thu, 18 Mar 2021 17:51:50 +0100 Subject: [PATCH 090/103] Almost eliminate util module --- Cargo.lock | 3 +++ connect/Cargo.toml | 1 + connect/src/spirc.rs | 7 +++-- core/Cargo.toml | 3 ++- core/src/connection/handshake.rs | 8 +++--- core/src/diffie_hellman.rs | 38 +++++++++++++++++--------- core/src/lib.rs | 2 ++ core/src/mercury/mod.rs | 18 ++++++++++--- core/src/util.rs | 46 -------------------------------- 9 files changed, 59 insertions(+), 67 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3991775..9b66a41 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1335,6 +1335,7 @@ dependencies = [ "base64", "block-modes", "dns-sd", + "form_urlencoded", "futures-core", "futures-util", "hmac", @@ -1363,6 +1364,7 @@ dependencies = [ "byteorder", "bytes", "env_logger", + "form_urlencoded", "futures-core", "futures-util", "hmac", @@ -1640,6 +1642,7 @@ dependencies = [ "autocfg", "num-integer", "num-traits", + "rand", ] [[package]] diff --git a/connect/Cargo.toml b/connect/Cargo.toml index 1331b62..052cf6a 100644 --- a/connect/Cargo.toml +++ b/connect/Cargo.toml @@ -11,6 +11,7 @@ edition = "2018" aes-ctr = "0.6" base64 = "0.13" block-modes = "0.7" +form_urlencoded = "1.0" futures-core = "0.3" futures-util = { version = "0.3", default_features = false } hmac = "0.10" diff --git a/connect/src/spirc.rs b/connect/src/spirc.rs index 1a7f64e..4fcb025 100644 --- a/connect/src/spirc.rs +++ b/connect/src/spirc.rs @@ -7,7 +7,6 @@ use crate::core::config::{ConnectConfig, VolumeCtrl}; use crate::core::mercury::{MercuryError, MercurySender}; use crate::core::session::Session; use crate::core::spotify_id::{SpotifyAudioType, SpotifyId, SpotifyIdError}; -use crate::core::util::url_encode; use crate::core::util::SeqGenerator; use crate::core::version; use crate::playback::mixer::Mixer; @@ -244,6 +243,10 @@ fn volume_to_mixer(volume: u16, volume_ctrl: &VolumeCtrl) -> u16 { } } +fn url_encode(bytes: impl AsRef<[u8]>) -> String { + form_urlencoded::byte_serialize(bytes.as_ref()).collect() +} + impl Spirc { pub fn new( config: ConnectConfig, @@ -256,7 +259,7 @@ impl Spirc { let ident = session.device_id().to_owned(); // Uri updated in response to issue #288 - debug!("canonical_username: {}", url_encode(&session.username())); + debug!("canonical_username: {}", &session.username()); let uri = format!("hm://remote/user/{}/", url_encode(&session.username())); let subscription = Box::pin( diff --git a/core/Cargo.toml b/core/Cargo.toml index 4e9b03c..29f4f33 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -17,6 +17,7 @@ aes = "0.6" base64 = "0.13" byteorder = "1.4" bytes = "1.0" +form_urlencoded = "1.0" futures-core = { version = "0.3", default-features = false } futures-util = { version = "0.3", default-features = false, features = ["alloc", "bilock", "unstable", "sink"] } hmac = "0.10" @@ -25,7 +26,7 @@ http = "0.2" hyper = { version = "0.14", optional = true, features = ["client", "tcp", "http1"] } hyper-proxy = { version = "0.9.1", optional = true, default-features = false } log = "0.4" -num-bigint = "0.4" +num-bigint = { version = "0.4", features = ["rand"] } num-integer = "0.1" num-traits = "0.2" once_cell = "1.5.2" diff --git a/core/src/connection/handshake.rs b/core/src/connection/handshake.rs index 879df67..6f802ab 100644 --- a/core/src/connection/handshake.rs +++ b/core/src/connection/handshake.rs @@ -1,7 +1,7 @@ use byteorder::{BigEndian, ByteOrder, WriteBytesExt}; use hmac::{Hmac, Mac, NewMac}; use protobuf::{self, Message}; -use rand::thread_rng; +use rand::{thread_rng, RngCore}; use sha1::Sha1; use std::io; use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; @@ -11,7 +11,6 @@ use super::codec::ApCodec; use crate::diffie_hellman::DhLocalKeys; use crate::protocol; use crate::protocol::keyexchange::{APResponseMessage, ClientHello, ClientResponsePlaintext}; -use crate::util; pub async fn handshake( mut connection: T, @@ -40,6 +39,9 @@ async fn client_hello(connection: &mut T, gc: Vec) -> io::Result> where T: AsyncWrite + Unpin, { + let mut client_nonce = vec![0; 0x10]; + thread_rng().fill_bytes(&mut client_nonce); + let mut packet = ClientHello::new(); packet .mut_build_info() @@ -59,7 +61,7 @@ where .mut_login_crypto_hello() .mut_diffie_hellman() .set_server_keys_known(1); - packet.set_client_nonce(util::rand_vec(&mut thread_rng(), 0x10)); + packet.set_client_nonce(client_nonce); packet.set_padding(vec![0x1e]); let mut buffer = vec![0, 4]; diff --git a/core/src/diffie_hellman.rs b/core/src/diffie_hellman.rs index 42d1fd9..57caa02 100644 --- a/core/src/diffie_hellman.rs +++ b/core/src/diffie_hellman.rs @@ -1,11 +1,11 @@ -use num_bigint::BigUint; +use num_bigint::{BigUint, RandBigInt}; +use num_integer::Integer; +use num_traits::{One, Zero}; use once_cell::sync::Lazy; -use rand::Rng; +use rand::{CryptoRng, Rng}; -use crate::util; - -pub static DH_GENERATOR: Lazy = Lazy::new(|| BigUint::from_bytes_be(&[0x02])); -pub static DH_PRIME: Lazy = Lazy::new(|| { +static DH_GENERATOR: Lazy = Lazy::new(|| BigUint::from_bytes_be(&[0x02])); +static DH_PRIME: Lazy = Lazy::new(|| { BigUint::from_bytes_be(&[ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc9, 0x0f, 0xda, 0xa2, 0x21, 0x68, 0xc2, 0x34, 0xc4, 0xc6, 0x62, 0x8b, 0x80, 0xdc, 0x1c, 0xd1, 0x29, 0x02, 0x4e, 0x08, 0x8a, 0x67, @@ -17,17 +17,31 @@ pub static DH_PRIME: Lazy = Lazy::new(|| { ]) }); +fn powm(base: &BigUint, exp: &BigUint, modulus: &BigUint) -> BigUint { + let mut base = base.clone(); + let mut exp = exp.clone(); + let mut result: BigUint = One::one(); + + while !exp.is_zero() { + if exp.is_odd() { + result = (result * &base) % modulus; + } + exp >>= 1; + base = (&base * &base) % modulus; + } + + result +} + pub struct DhLocalKeys { private_key: BigUint, public_key: BigUint, } impl DhLocalKeys { - pub fn random(rng: &mut R) -> DhLocalKeys { - let key_data = util::rand_vec(rng, 95); - - let private_key = BigUint::from_bytes_be(&key_data); - let public_key = util::powm(&DH_GENERATOR, &private_key, &DH_PRIME); + pub fn random(rng: &mut R) -> DhLocalKeys { + let private_key = rng.gen_biguint(95 * 8); + let public_key = powm(&DH_GENERATOR, &private_key, &DH_PRIME); DhLocalKeys { private_key, @@ -40,7 +54,7 @@ impl DhLocalKeys { } pub fn shared_secret(&self, remote_key: &[u8]) -> Vec { - let shared_key = util::powm( + let shared_key = powm( &BigUint::from_bytes_be(remote_key), &self.private_key, &DH_PRIME, diff --git a/core/src/lib.rs b/core/src/lib.rs index 320967f..bb3e21d 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -14,12 +14,14 @@ pub mod cache; pub mod channel; pub mod config; mod connection; +#[doc(hidden)] pub mod diffie_hellman; pub mod keymaster; pub mod mercury; mod proxytunnel; pub mod session; pub mod spotify_id; +#[doc(hidden)] pub mod util; pub mod version; diff --git a/core/src/mercury/mod.rs b/core/src/mercury/mod.rs index b920e7e..ef04e98 100644 --- a/core/src/mercury/mod.rs +++ b/core/src/mercury/mod.rs @@ -10,7 +10,6 @@ use bytes::Bytes; use tokio::sync::{mpsc, oneshot}; use crate::protocol; -use crate::util::url_encode; use crate::util::SeqGenerator; mod types; @@ -199,7 +198,7 @@ impl MercuryManager { let header: protocol::mercury::Header = protobuf::parse_from_bytes(&header_data).unwrap(); let response = MercuryResponse { - uri: url_encode(header.get_uri()), + uri: header.get_uri().to_string(), status_code: header.get_status_code(), payload: pending.parts, }; @@ -214,8 +213,21 @@ impl MercuryManager { } else if cmd == 0xb5 { self.lock(|inner| { let mut found = false; + + // TODO: This is just a workaround to make utf-8 encoded usernames work. + // A better solution would be to use an uri struct and urlencode it directly + // before sending while saving the subscription under its unencoded form. + let mut uri_split = response.uri.split('/'); + + let encoded_uri = std::iter::once(uri_split.next().unwrap().to_string()) + .chain(uri_split.map(|component| { + form_urlencoded::byte_serialize(component.as_bytes()).collect::() + })) + .collect::>() + .join("/"); + inner.subscriptions.retain(|&(ref prefix, ref sub)| { - if response.uri.starts_with(prefix) { + if encoded_uri.starts_with(prefix) { found = true; // if send fails, remove from list of subs diff --git a/core/src/util.rs b/core/src/util.rs index c55d960..df9ea71 100644 --- a/core/src/util.rs +++ b/core/src/util.rs @@ -1,50 +1,4 @@ -use num_bigint::BigUint; -use num_integer::Integer; -use num_traits::{One, Zero}; -use rand::Rng; use std::mem; -use std::ops::{Mul, Rem, Shr}; - -pub fn rand_vec(rng: &mut G, size: usize) -> Vec { - ::std::iter::repeat(()) - .map(|()| rng.gen()) - .take(size) - .collect() -} - -pub fn url_encode(inp: &str) -> String { - let mut encoded = String::new(); - - for c in inp.as_bytes().iter() { - match *c as char { - 'A'..='Z' | 'a'..='z' | '0'..='9' | '-' | '_' | '.' | '~' | ':' | '/' => { - encoded.push(*c as char) - } - c => encoded.push_str(format!("%{:02X}", c as u32).as_str()), - }; - } - - encoded -} - -pub fn powm(base: &BigUint, exp: &BigUint, modulus: &BigUint) -> BigUint { - let mut base = base.clone(); - let mut exp = exp.clone(); - let mut result: BigUint = One::one(); - - while !exp.is_zero() { - if exp.is_odd() { - result = result.mul(&base).rem(modulus); - } - exp = exp.shr(1); - base = (&base).mul(&base).rem(modulus); - } - - result -} - -pub trait ReadSeek: ::std::io::Read + ::std::io::Seek {} -impl ReadSeek for T {} pub trait Seq { fn next(&self) -> Self; From cb8c9c2afa6880c95ee29f1e403a7f291aaf6013 Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Thu, 1 Apr 2021 18:20:42 +0200 Subject: [PATCH 091/103] Enable apresolve always in binary Librespot-connect uses hyper anyway, so no one needs to disable it only to reduce the number of dependencies. Furthermore, when using another backend, people will use --no-default-features and will forget to enable the apresolve feature again. --- Cargo.toml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8f3e44c..7939b76 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,7 @@ version = "0.1.6" [dependencies.librespot-core] path = "core" version = "0.1.6" +features = ["apresolve"] [dependencies.librespot-metadata] path = "metadata" @@ -58,8 +59,6 @@ url = "2.1" sha-1 = "0.9" [features] -apresolve = ["librespot-core/apresolve"] - alsa-backend = ["librespot-playback/alsa-backend"] portaudio-backend = ["librespot-playback/portaudio-backend"] pulseaudio-backend = ["librespot-playback/pulseaudio-backend"] @@ -75,7 +74,7 @@ with-lewton = ["librespot-audio/with-lewton"] # with-dns-sd = ["librespot-connect/with-dns-sd"] -default = ["rodio-backend", "apresolve", "with-lewton"] +default = ["rodio-backend", "with-lewton"] [package.metadata.deb] maintainer = "librespot-org" From b7350b71da72251b57c9449a94b722b0a1420b01 Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Fri, 9 Apr 2021 16:38:43 +0200 Subject: [PATCH 092/103] Restore previous feature flags Some of the feature flags librespot uses are not really additive but rather mutual exclusive. A previous attempt to improve the situation had other drawbacks, so it's better to postpone a decision and restore the old behaviour. --- Cargo.toml | 5 ++--- audio/Cargo.toml | 3 +-- audio/src/lib.rs | 15 +++------------ connect/Cargo.toml | 4 +--- 4 files changed, 7 insertions(+), 20 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7939b76..14e33a8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -70,11 +70,10 @@ gstreamer-backend = ["librespot-playback/gstreamer-backend"] with-tremor = ["librespot-audio/with-tremor"] with-vorbis = ["librespot-audio/with-vorbis"] -with-lewton = ["librespot-audio/with-lewton"] -# with-dns-sd = ["librespot-connect/with-dns-sd"] +with-dns-sd = ["librespot-connect/with-dns-sd"] -default = ["rodio-backend", "with-lewton"] +default = ["rodio-backend"] [package.metadata.deb] maintainer = "librespot-org" diff --git a/audio/Cargo.toml b/audio/Cargo.toml index d8c0eea..5dec789 100644 --- a/audio/Cargo.toml +++ b/audio/Cargo.toml @@ -15,17 +15,16 @@ aes-ctr = "0.6" byteorder = "1.4" bytes = "1.0" cfg-if = "1" +lewton = "0.10" log = "0.4" futures-util = { version = "0.3", default_features = false } ogg = "0.8" tempfile = "3.1" tokio = { version = "1", features = ["sync"] } -lewton = { version = "0.10", optional = true } librespot-tremor = { version = "0.2.0", optional = true } vorbis = { version ="0.0.14", optional = true } [features] -with-lewton = ["lewton"] with-tremor = ["librespot-tremor"] with-vorbis = ["vorbis"] diff --git a/audio/src/lib.rs b/audio/src/lib.rs index 80f1097..35ec4dd 100644 --- a/audio/src/lib.rs +++ b/audio/src/lib.rs @@ -8,22 +8,13 @@ mod fetch; use cfg_if::cfg_if; -#[cfg(any( - all(feature = "with-lewton", feature = "with-tremor"), - all(feature = "with-vorbis", feature = "with-tremor"), - all(feature = "with-lewton", feature = "with-vorbis") -))] -compile_error!("Cannot use two decoders at the same time."); - cfg_if! { - if #[cfg(feature = "with-lewton")] { - mod lewton_decoder; - pub use lewton_decoder::{VorbisDecoder, VorbisError}; - } else if #[cfg(any(feature = "with-tremor", feature = "with-vorbis"))] { + if #[cfg(any(feature = "with-tremor", feature = "with-vorbis"))] { mod libvorbis_decoder; pub use crate::libvorbis_decoder::{VorbisDecoder, VorbisError}; } else { - compile_error!("Must choose a vorbis decoder."); + mod lewton_decoder; + pub use lewton_decoder::{VorbisDecoder, VorbisError}; } } diff --git a/connect/Cargo.toml b/connect/Cargo.toml index 052cf6a..689efd3 100644 --- a/connect/Cargo.toml +++ b/connect/Cargo.toml @@ -16,6 +16,7 @@ futures-core = "0.3" futures-util = { version = "0.3", default_features = false } hmac = "0.10" hyper = { version = "0.14", features = ["server", "http1", "tcp"] } +libmdns = "0.6" log = "0.4" protobuf = "~2.14.0" rand = "0.8" @@ -27,7 +28,6 @@ tokio-stream = { version = "0.1" } url = "2.1" dns-sd = { version = "0.1.3", optional = true } -libmdns = { version = "0.6", optional = true } [dependencies.librespot-core] path = "../core" @@ -42,7 +42,5 @@ path = "../protocol" version = "0.1.6" [features] -with-libmdns = ["libmdns"] with-dns-sd = ["dns-sd"] -default = ["with-libmdns"] From 9a3a6668568bb1169a50f6cbc9e7cd1964d21916 Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Fri, 9 Apr 2021 16:43:05 +0200 Subject: [PATCH 093/103] Bump MSRV to 1.45 --- .github/workflows/test.yml | 2 +- COMPILING.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9d1250f..907a6eb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -41,7 +41,7 @@ jobs: matrix: os: [ubuntu-latest] toolchain: - - 1.41.1 # MSRV (Minimum supported rust version) + - 1.45 # MSRV (Minimum supported rust version) - stable - beta experimental: [false] diff --git a/COMPILING.md b/COMPILING.md index 4320cdb..539dc69 100644 --- a/COMPILING.md +++ b/COMPILING.md @@ -13,7 +13,7 @@ curl https://sh.rustup.rs -sSf | sh Follow any prompts it gives you to install Rust. Once that’s done, Rust's standard tools should be setup and ready to use. -*Note: The current minimum required Rust version at the time of writing is 1.41, you can find the current minimum version specified in the `.github/workflow/test.yml` file.* +*Note: The current minimum required Rust version at the time of writing is 1.45, you can find the current minimum version specified in the `.github/workflow/test.yml` file.* #### Additional Rust tools - `rustfmt` To ensure a consistent codebase, we utilise [`rustfmt`](https://github.com/rust-lang/rustfmt), which is installed by default with `rustup` these days, else it can be installed manually with: From 5435ab3270fc889e2c217803906f775f5d36f5d0 Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Thu, 1 Apr 2021 18:41:01 +0200 Subject: [PATCH 094/103] Fix compile errors in backends fe37186 added the restriction that `Sink`s must be `Send`. It turned out later that this restrictions was unnecessary, and since some `Sink`s aren't `Send` yet, this restriction is lifted again. librespot-org/librespot#601 refactored the `RodioSink` in order to make it `Send`. These changes are partly reverted in favour of the initial simpler design. Furthermore, there were some compile errors in the gstreamer backend which are hereby fixed. --- playback/src/audio_backend/gstreamer.rs | 15 +++++++------ playback/src/audio_backend/mod.rs | 4 ++-- playback/src/audio_backend/rodio.rs | 29 +++++-------------------- playback/src/player.rs | 4 ++-- src/main.rs | 2 +- 5 files changed, 18 insertions(+), 36 deletions(-) diff --git a/playback/src/audio_backend/gstreamer.rs b/playback/src/audio_backend/gstreamer.rs index 4678bfb..e2b52d7 100644 --- a/playback/src/audio_backend/gstreamer.rs +++ b/playback/src/audio_backend/gstreamer.rs @@ -1,7 +1,9 @@ use super::{Open, Sink}; use crate::audio::AudioPacket; + use gst::prelude::*; -use gst::*; +use gstreamer as gst; +use gstreamer_app as gst_app; use zerocopy::*; use std::sync::mpsc::{sync_channel, SyncSender}; @@ -52,14 +54,13 @@ impl Open for GstreamerSink { thread::spawn(move || { for data in rx { let buffer = bufferpool.acquire_buffer(None); - if !buffer.is_err() { - let mut okbuffer = buffer.unwrap(); - let mutbuf = okbuffer.make_mut(); + if let Ok(mut buffer) = buffer { + let mutbuf = buffer.make_mut(); mutbuf.set_size(data.len()); mutbuf .copy_from_slice(0, data.as_bytes()) .expect("Failed to copy from slice"); - let _eat = appsrc.push_buffer(okbuffer); + let _eat = appsrc.push_buffer(buffer); } } }); @@ -69,8 +70,8 @@ impl Open for GstreamerSink { let watch_mainloop = thread_mainloop.clone(); bus.add_watch(move |_, msg| { match msg.view() { - MessageView::Eos(..) => watch_mainloop.quit(), - MessageView::Error(err) => { + gst::MessageView::Eos(..) => watch_mainloop.quit(), + gst::MessageView::Error(err) => { println!( "Error from {:?}: {} ({:?})", err.get_src().map(|s| s.get_path_string()), diff --git a/playback/src/audio_backend/mod.rs b/playback/src/audio_backend/mod.rs index c816a6d..5fa518e 100644 --- a/playback/src/audio_backend/mod.rs +++ b/playback/src/audio_backend/mod.rs @@ -11,9 +11,9 @@ pub trait Sink { fn write(&mut self, packet: &AudioPacket) -> io::Result<()>; } -pub type SinkBuilder = fn(Option) -> Box; +pub type SinkBuilder = fn(Option) -> Box; -fn mk_sink(device: Option) -> Box { +fn mk_sink(device: Option) -> Box { Box::new(S::open(device)) } diff --git a/playback/src/audio_backend/rodio.rs b/playback/src/audio_backend/rodio.rs index 338dfbb..8e44821 100644 --- a/playback/src/audio_backend/rodio.rs +++ b/playback/src/audio_backend/rodio.rs @@ -1,5 +1,4 @@ use std::process::exit; -use std::{convert::Infallible, sync::mpsc}; use std::{io, thread, time}; use cpal::traits::{DeviceTrait, HostTrait}; @@ -15,12 +14,12 @@ use crate::audio::AudioPacket; compile_error!("Rodio JACK backend is currently only supported on linux."); #[cfg(feature = "rodio-backend")] -pub fn mk_rodio(device: Option) -> Box { +pub fn mk_rodio(device: Option) -> Box { Box::new(open(cpal::default_host(), device)) } #[cfg(feature = "rodiojack-backend")] -pub fn mk_rodiojack(device: Option) -> Box { +pub fn mk_rodiojack(device: Option) -> Box { Box::new(open( cpal::host_from_id(cpal::HostId::Jack).unwrap(), device, @@ -43,8 +42,7 @@ pub enum RodioError { pub struct RodioSink { rodio_sink: rodio::Sink, - // will produce a TryRecvError on the receiver side when it is dropped. - _close_tx: mpsc::SyncSender, + _stream: rodio::OutputStream, } fn list_formats(device: &rodio::Device) { @@ -152,29 +150,12 @@ fn create_sink( pub fn open(host: cpal::Host, device: Option) -> RodioSink { debug!("Using rodio sink with cpal host: {}", host.id().name()); - let (sink_tx, sink_rx) = mpsc::sync_channel(1); - let (close_tx, close_rx) = mpsc::sync_channel(1); - - std::thread::spawn(move || match create_sink(&host, device) { - Ok((sink, stream)) => { - sink_tx.send(Ok(sink)).unwrap(); - - close_rx.recv().unwrap_err(); // This will fail as soon as the sender is dropped - debug!("drop rodio::OutputStream"); - drop(stream); - } - Err(e) => { - sink_tx.send(Err(e)).unwrap(); - } - }); - - // Instead of the second `unwrap`, better error handling could be introduced - let sink = sink_rx.recv().unwrap().unwrap(); + let (sink, stream) = create_sink(&host, device).unwrap(); debug!("Rodio sink was created"); RodioSink { rodio_sink: sink, - _close_tx: close_tx, + _stream: stream, } } diff --git a/playback/src/player.rs b/playback/src/player.rs index 0d2380e..497a4a1 100644 --- a/playback/src/player.rs +++ b/playback/src/player.rs @@ -50,7 +50,7 @@ struct PlayerInternal { state: PlayerState, preload: PlayerPreload, - sink: Box, + sink: Box, sink_status: SinkStatus, sink_event_callback: Option, audio_filter: Option>, @@ -242,7 +242,7 @@ impl Player { sink_builder: F, ) -> (Player, PlayerEventChannel) where - F: FnOnce() -> Box + Send + 'static, + F: FnOnce() -> Box + Send + 'static, { let (cmd_tx, cmd_rx) = mpsc::unbounded_channel(); let (event_sender, event_receiver) = mpsc::unbounded_channel(); diff --git a/src/main.rs b/src/main.rs index cd2a786..54057ae 100644 --- a/src/main.rs +++ b/src/main.rs @@ -105,7 +105,7 @@ fn print_version() { #[derive(Clone)] struct Setup { - backend: fn(Option) -> Box, + backend: fn(Option) -> Box, device: Option, mixer: fn(Option) -> Box, From 690e0d2e10f942d1243ea2bae1ca902ffc322cb6 Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Fri, 2 Apr 2021 14:44:42 +0200 Subject: [PATCH 095/103] Add simple tests to librespot-core The first test checks whether apresolve works. A second test tries to create a Spotify sessions with fake credentials and asserts that an error is returned. --- core/src/apresolve.rs | 23 +++++++++++++++++++++ core/tests/connect.rs | 48 +++++++++++++++---------------------------- 2 files changed, 39 insertions(+), 32 deletions(-) diff --git a/core/src/apresolve.rs b/core/src/apresolve.rs index 85baba6..b11e275 100644 --- a/core/src/apresolve.rs +++ b/core/src/apresolve.rs @@ -66,3 +66,26 @@ pub async fn apresolve(proxy: Option<&Url>, ap_port: Option) -> String { AP_FALLBACK.into() }) } + +#[cfg(test)] +mod test { + use std::net::ToSocketAddrs; + + use super::try_apresolve; + + #[tokio::test] + async fn test_apresolve() { + let ap = try_apresolve(None, None).await.unwrap(); + + // Assert that the result contains a valid host and port + ap.to_socket_addrs().unwrap().next().unwrap(); + } + + #[tokio::test] + async fn test_apresolve_port_443() { + let ap = try_apresolve(None, Some(443)).await.unwrap(); + + let port = ap.to_socket_addrs().unwrap().next().unwrap().port(); + assert_eq!(port, 443); + } +} diff --git a/core/tests/connect.rs b/core/tests/connect.rs index b7bc29f..4f1dbe6 100644 --- a/core/tests/connect.rs +++ b/core/tests/connect.rs @@ -1,34 +1,18 @@ -use librespot_core::*; +use librespot_core::authentication::Credentials; +use librespot_core::config::SessionConfig; +use librespot_core::session::Session; -// TODO: test is broken -// #[cfg(test)] -// mod tests { -// use super::*; -// // Test AP Resolve -// use apresolve::apresolve_or_fallback; -// #[tokio::test] -// async fn test_ap_resolve() { -// env_logger::init(); -// let ap = apresolve_or_fallback(&None, &None).await; -// println!("AP: {:?}", ap); -// } +#[tokio::test] +async fn test_connection() { + let result = Session::connect( + SessionConfig::default(), + Credentials::with_password("test", "test"), + None, + ) + .await; -// // Test connect -// use authentication::Credentials; -// use config::SessionConfig; -// #[tokio::test] -// async fn test_connection() -> Result<(), Box> { -// println!("Running connection test"); -// let ap = apresolve_or_fallback(&None, &None).await; -// let credentials = Credentials::with_password(String::from("test"), String::from("test")); -// let session_config = SessionConfig::default(); -// let proxy = None; - -// println!("Connecting to AP \"{}\"", ap); -// let mut connection = connection::connect(ap, &proxy).await?; -// let rc = connection::authenticate(&mut connection, credentials, &session_config.device_id) -// .await?; -// println!("Authenticated as \"{}\"", rc.username); -// Ok(()) -// } -// } + match result { + Ok(_) => panic!("Authentication succeeded despite of bad credentials."), + Err(e) => assert_eq!(e.to_string(), "Login failed with reason: Bad credentials"), + }; +} From ff499825e0ce509b5d49669ac328e4ba85073da4 Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Fri, 9 Apr 2021 17:00:56 +0200 Subject: [PATCH 096/103] Add missing feature flag to tokio --- audio/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/audio/Cargo.toml b/audio/Cargo.toml index 5dec789..2cd801a 100644 --- a/audio/Cargo.toml +++ b/audio/Cargo.toml @@ -20,7 +20,7 @@ log = "0.4" futures-util = { version = "0.3", default_features = false } ogg = "0.8" tempfile = "3.1" -tokio = { version = "1", features = ["sync"] } +tokio = { version = "1", features = ["sync", "macros"] } librespot-tremor = { version = "0.2.0", optional = true } vorbis = { version ="0.0.14", optional = true } From 317e5864729e07f9d7b2150d928f95c2424d8ad2 Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Fri, 9 Apr 2021 17:01:30 +0200 Subject: [PATCH 097/103] Improve CI Run the tests and add build for Windows. --- .github/workflows/test.yml | 137 ++++++++++++++++++++++++++++--------- 1 file changed, 104 insertions(+), 33 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 907a6eb..f06445e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -4,36 +4,49 @@ name: test on: push: branches: [master, dev] - paths: ['**.rs', '**.toml', '**.lock', '**.yml'] + paths: + [ + "**.rs", + "Cargo.toml", + "/Cargo.lock", + "/rustfmt.toml", + "/.github/workflows", + ] pull_request: - branches: [master, dev] - paths: ['**.rs', '**.toml', '**.lock', '**.yml'] + paths: + [ + "**.rs", + "Cargo.toml", + "/Cargo.lock", + "/rustfmt.toml", + "/.github/workflows", + ] + schedule: + # Run CI every week + - cron: "00 01 * * 0" + +env: + RUST_BACKTRACE: 1 jobs: fmt: - name: 'Rust: format check' - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false - matrix: - # Only run the formatting check for stable - include: - - os: ubuntu-latest - toolchain: stable + name: rustfmt + runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v2 - name: Install toolchain uses: actions-rs/toolchain@v1 with: - # Use default profile to get rustfmt - profile: default - toolchain: ${{ matrix.toolchain }} + profile: minimal + toolchain: stable override: true - - run: cargo fmt --verbose --all -- --check + components: rustfmt + - run: cargo fmt --all -- --check - test: + test-linux: needs: fmt + name: cargo +${{ matrix.toolchain }} build (${{ matrix.os }}) runs-on: ${{ matrix.os }} continue-on-error: ${{ matrix.experimental }} strategy: @@ -45,7 +58,7 @@ jobs: - stable - beta experimental: [false] - # Ignore failures in nightly, not ideal, but necessary + # Ignore failures in nightly include: - os: ubuntu-latest toolchain: nightly @@ -53,12 +66,19 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v2 + - name: Install toolchain uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: ${{ matrix.toolchain }} override: true + + - name: Get Rustc version + id: get-rustc-version + run: echo "::set-output name=version::$(rustc -V)" + shell: bash + - name: Cache Rust dependencies uses: actions/cache@v2 with: @@ -67,21 +87,65 @@ jobs: ~/.cargo/registry/cache ~/.cargo/git target - key: ${{ runner.os }}-build-${{ hashFiles('**/Cargo.lock') }} + key: ${{ runner.os }}-${{ steps.get-rustc-version.outputs.version }}-${{ hashFiles('**/Cargo.lock') }} + - name: Install developer package dependencies - run: sudo apt-get update && sudo apt-get install libpulse-dev portaudio19-dev libasound2-dev libsdl2-dev gstreamer1.0-dev libgstreamer-plugins-base1.0-dev - - run: cargo build --locked --no-default-features - - run: cargo build --locked --examples - - run: cargo build --locked --no-default-features --features "with-tremor" - - run: cargo build --locked --no-default-features --features "with-vorbis" - - run: cargo build --locked --no-default-features --features "alsa-backend" - - run: cargo build --locked --no-default-features --features "portaudio-backend" - - run: cargo build --locked --no-default-features --features "pulseaudio-backend" - - run: cargo build --locked --no-default-features --features "jackaudio-backend" - - run: cargo build --locked --no-default-features --features "rodiojack-backend" - - run: cargo build --locked --no-default-features --features "rodio-backend" - - run: cargo build --locked --no-default-features --features "sdl-backend" - - run: cargo build --locked --no-default-features --features "gstreamer-backend" + run: sudo apt-get update && sudo apt-get install libpulse-dev portaudio19-dev libasound2-dev libsdl2-dev gstreamer1.0-dev libgstreamer-plugins-base1.0-dev libavahi-compat-libdnssd-dev + + - run: cargo build --workspace --examples + - run: cargo test --workspace + + - run: cargo install cargo-hack + - run: cargo hack --workspace --remove-dev-deps + - run: cargo build -p librespot-core --no-default-features + - run: cargo build -p librespot-core + - run: cargo hack build --each-feature -p librespot-audio + - run: cargo build -p librespot-connect + - run: cargo build -p librespot-connect --no-default-features --features with-dns-sd + - run: cargo hack build --locked --each-feature + + test-windows: + needs: fmt + name: cargo build (${{ matrix.os }}) + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [windows-latest] + toolchain: [stable] + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Install toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.toolchain }} + profile: minimal + override: true + + - name: Get Rustc version + id: get-rustc-version + run: echo "::set-output name=version::$(rustc -V)" + shell: bash + + - name: Cache Rust dependencies + uses: actions/cache@v2 + with: + path: | + ~/.cargo/registry/index + ~/.cargo/registry/cache + ~/.cargo/git + target + key: ${{ runner.os }}-${{ steps.get-rustc-version.outputs.version }}-${{ hashFiles('**/Cargo.lock') }} + + - run: cargo build --workspace --examples + - run: cargo test --workspace + + - run: cargo install cargo-hack + - run: cargo hack --workspace --remove-dev-deps + - run: cargo build --no-default-features + - run: cargo build test-cross-arm: needs: fmt @@ -97,6 +161,7 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v2 + - name: Install toolchain uses: actions-rs/toolchain@v1 with: @@ -104,6 +169,12 @@ jobs: target: ${{ matrix.target }} toolchain: ${{ matrix.toolchain }} override: true + + - name: Get Rustc version + id: get-rustc-version + run: echo "::set-output name=version::$(rustc -V)" + shell: bash + - name: Cache Rust dependencies uses: actions/cache@v2 with: @@ -112,7 +183,7 @@ jobs: ~/.cargo/registry/cache ~/.cargo/git target - key: ${{ runner.os }}-build-${{ hashFiles('**/Cargo.lock') }} + key: ${{ runner.os }}-${{ matrix.target }}-${{ steps.get-rustc-version.outputs.version }}-${{ hashFiles('**/Cargo.lock') }} - name: Install cross run: cargo install cross || true - name: Build From a576194b0ecf7ec825e5a474d69fcde8bc9b0400 Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Sat, 10 Apr 2021 13:31:42 +0200 Subject: [PATCH 098/103] Fix bug in rodio backend --- playback/src/audio_backend/rodio.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/playback/src/audio_backend/rodio.rs b/playback/src/audio_backend/rodio.rs index 98494db..035cd32 100644 --- a/playback/src/audio_backend/rodio.rs +++ b/playback/src/audio_backend/rodio.rs @@ -199,8 +199,6 @@ impl Sink for RodioSink { } _ => unreachable!(), }; - let source = rodio::buffer::SamplesBuffer::new(2, 44100, packet.samples()); - self.rodio_sink.append(source); // Chunk sizes seem to be about 256 to 3000 ish items long. // Assuming they're on average 1628 then a half second buffer is: From b4f9ae31e2832ba6c0bd753eeb36902dd2cb07cc Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Sat, 10 Apr 2021 14:06:41 +0200 Subject: [PATCH 099/103] Fix clippy warnings --- audio/src/convert.rs | 37 ++++++++++++------------- audio/src/lib.rs | 3 +- playback/src/audio_backend/alsa.rs | 8 ++---- playback/src/audio_backend/jackaudio.rs | 2 +- playback/src/audio_backend/mod.rs | 10 +++---- playback/src/audio_backend/portaudio.rs | 7 ++--- playback/src/audio_backend/rodio.rs | 4 +-- playback/src/audio_backend/sdl.rs | 6 ++-- playback/src/config.rs | 2 +- playback/src/mixer/alsamixer.rs | 7 ++--- playback/src/player.rs | 6 ++-- src/main.rs | 4 +-- 12 files changed, 44 insertions(+), 52 deletions(-) diff --git a/audio/src/convert.rs b/audio/src/convert.rs index e291c80..450910b 100644 --- a/audio/src/convert.rs +++ b/audio/src/convert.rs @@ -36,24 +36,21 @@ macro_rules! convert_samples_to { }; } -pub struct SamplesConverter {} -impl SamplesConverter { - pub fn to_s32(samples: &[f32]) -> Vec { - convert_samples_to!(i32, samples) - } - - pub fn to_s24(samples: &[f32]) -> Vec { - convert_samples_to!(i32, samples, 8) - } - - pub fn to_s24_3(samples: &[f32]) -> Vec { - Self::to_s32(samples) - .iter() - .map(|sample| i24::pcm_from_i32(*sample)) - .collect() - } - - pub fn to_s16(samples: &[f32]) -> Vec { - convert_samples_to!(i16, samples) - } +pub fn to_s32(samples: &[f32]) -> Vec { + convert_samples_to!(i32, samples) +} + +pub fn to_s24(samples: &[f32]) -> Vec { + convert_samples_to!(i32, samples, 8) +} + +pub fn to_s24_3(samples: &[f32]) -> Vec { + to_s32(samples) + .iter() + .map(|sample| i24::pcm_from_i32(*sample)) + .collect() +} + +pub fn to_s16(samples: &[f32]) -> Vec { + convert_samples_to!(i16, samples) } diff --git a/audio/src/lib.rs b/audio/src/lib.rs index e0a4858..b587f03 100644 --- a/audio/src/lib.rs +++ b/audio/src/lib.rs @@ -3,7 +3,7 @@ #[macro_use] extern crate log; -mod convert; +pub mod convert; mod decrypt; mod fetch; @@ -24,7 +24,6 @@ pub use passthrough_decoder::{PassthroughDecoder, PassthroughError}; mod range_set; -pub use convert::{i24, SamplesConverter}; pub use decrypt::AudioDecrypt; pub use fetch::{AudioFile, StreamLoaderController}; pub use fetch::{ diff --git a/playback/src/audio_backend/alsa.rs b/playback/src/audio_backend/alsa.rs index 1d55187..54fed31 100644 --- a/playback/src/audio_backend/alsa.rs +++ b/playback/src/audio_backend/alsa.rs @@ -87,7 +87,7 @@ impl Open for AlsaSink { Self { pcm: None, - format: format, + format, device: name, buffer: vec![], } @@ -146,7 +146,7 @@ impl SinkAsBytes for AlsaSink { .extend_from_slice(&data[processed_data..processed_data + data_to_buffer]); processed_data += data_to_buffer; if self.buffer.len() == self.buffer.capacity() { - self.write_buf().expect("could not append to buffer"); + self.write_buf(); self.buffer.clear(); } } @@ -156,14 +156,12 @@ impl SinkAsBytes for AlsaSink { } impl AlsaSink { - fn write_buf(&mut self) -> io::Result<()> { + fn write_buf(&mut self) { let pcm = self.pcm.as_mut().unwrap(); let io = pcm.io_bytes(); match io.writei(&self.buffer) { Ok(_) => (), Err(err) => pcm.try_recover(err, false).unwrap(), }; - - Ok(()) } } diff --git a/playback/src/audio_backend/jackaudio.rs b/playback/src/audio_backend/jackaudio.rs index b38c901..aca2edd 100644 --- a/playback/src/audio_backend/jackaudio.rs +++ b/playback/src/audio_backend/jackaudio.rs @@ -47,7 +47,7 @@ impl Open for JackSink { } info!("Using JACK sink with format {:?}", AudioFormat::F32); - let client_name = client_name.unwrap_or("librespot".to_string()); + let client_name = client_name.unwrap_or_else(|| "librespot".to_string()); let (client, _status) = Client::new(&client_name[..], ClientOptions::NO_START_SERVER).unwrap(); let ch_r = client.register_port("out_0", AudioOut::default()).unwrap(); diff --git a/playback/src/audio_backend/mod.rs b/playback/src/audio_backend/mod.rs index 24f5f85..72659f1 100644 --- a/playback/src/audio_backend/mod.rs +++ b/playback/src/audio_backend/mod.rs @@ -26,25 +26,25 @@ fn mk_sink(device: Option, format: AudioFormat macro_rules! sink_as_bytes { () => { fn write(&mut self, packet: &AudioPacket) -> io::Result<()> { - use crate::audio::{i24, SamplesConverter}; + use crate::audio::convert::{self, i24}; use zerocopy::AsBytes; match packet { AudioPacket::Samples(samples) => match self.format { AudioFormat::F32 => self.write_bytes(samples.as_bytes()), AudioFormat::S32 => { - let samples_s32: &[i32] = &SamplesConverter::to_s32(samples); + let samples_s32: &[i32] = &convert::to_s32(samples); self.write_bytes(samples_s32.as_bytes()) } AudioFormat::S24 => { - let samples_s24: &[i32] = &SamplesConverter::to_s24(samples); + let samples_s24: &[i32] = &convert::to_s24(samples); self.write_bytes(samples_s24.as_bytes()) } AudioFormat::S24_3 => { - let samples_s24_3: &[i24] = &SamplesConverter::to_s24_3(samples); + let samples_s24_3: &[i24] = &convert::to_s24_3(samples); self.write_bytes(samples_s24_3.as_bytes()) } AudioFormat::S16 => { - let samples_s16: &[i16] = &SamplesConverter::to_s16(samples); + let samples_s16: &[i16] = &convert::to_s16(samples); self.write_bytes(samples_s16.as_bytes()) } }, diff --git a/playback/src/audio_backend/portaudio.rs b/playback/src/audio_backend/portaudio.rs index 5faff6c..234a9af 100644 --- a/playback/src/audio_backend/portaudio.rs +++ b/playback/src/audio_backend/portaudio.rs @@ -1,8 +1,7 @@ use super::{Open, Sink}; -use crate::audio::{AudioPacket, SamplesConverter}; +use crate::audio::{convert, AudioPacket}; use crate::config::AudioFormat; use crate::player::{NUM_CHANNELS, SAMPLE_RATE}; -use portaudio_rs; use portaudio_rs::device::{get_default_output_index, DeviceIndex, DeviceInfo}; use portaudio_rs::stream::*; use std::io; @@ -157,11 +156,11 @@ impl<'a> Sink for PortAudioSink<'a> { write_sink!(ref mut stream, samples) } Self::S32(stream, _parameters) => { - let samples_s32: &[i32] = &SamplesConverter::to_s32(samples); + let samples_s32: &[i32] = &convert::to_s32(samples); write_sink!(ref mut stream, samples_s32) } Self::S16(stream, _parameters) => { - let samples_s16: &[i16] = &SamplesConverter::to_s16(samples); + let samples_s16: &[i16] = &convert::to_s16(samples); write_sink!(ref mut stream, samples_s16) } }; diff --git a/playback/src/audio_backend/rodio.rs b/playback/src/audio_backend/rodio.rs index 035cd32..65436a3 100644 --- a/playback/src/audio_backend/rodio.rs +++ b/playback/src/audio_backend/rodio.rs @@ -5,7 +5,7 @@ use cpal::traits::{DeviceTrait, HostTrait}; use thiserror::Error; use super::Sink; -use crate::audio::{AudioPacket, SamplesConverter}; +use crate::audio::{convert, AudioPacket}; use crate::config::AudioFormat; use crate::player::{NUM_CHANNELS, SAMPLE_RATE}; @@ -189,7 +189,7 @@ impl Sink for RodioSink { self.rodio_sink.append(source); } AudioFormat::S16 => { - let samples_s16: &[i16] = &SamplesConverter::to_s16(samples); + let samples_s16: &[i16] = &convert::to_s16(samples); let source = rodio::buffer::SamplesBuffer::new( NUM_CHANNELS as u16, SAMPLE_RATE, diff --git a/playback/src/audio_backend/sdl.rs b/playback/src/audio_backend/sdl.rs index 0a3fd43..2956653 100644 --- a/playback/src/audio_backend/sdl.rs +++ b/playback/src/audio_backend/sdl.rs @@ -1,5 +1,5 @@ use super::{Open, Sink}; -use crate::audio::{AudioPacket, SamplesConverter}; +use crate::audio::{convert, AudioPacket}; use crate::config::AudioFormat; use crate::player::{NUM_CHANNELS, SAMPLE_RATE}; use sdl2::audio::{AudioQueue, AudioSpecDesired}; @@ -97,12 +97,12 @@ impl Sink for SdlSink { queue.queue(samples) } Self::S32(queue) => { - let samples_s32: &[i32] = &SamplesConverter::to_s32(samples); + let samples_s32: &[i32] = &convert::to_s32(samples); drain_sink!(queue, AudioFormat::S32.size()); queue.queue(samples_s32) } Self::S16(queue) => { - let samples_s16: &[i16] = &SamplesConverter::to_s16(samples); + let samples_s16: &[i16] = &convert::to_s16(samples); drain_sink!(queue, AudioFormat::S16.size()); queue.queue(samples_s16) } diff --git a/playback/src/config.rs b/playback/src/config.rs index 95c9709..f8f0289 100644 --- a/playback/src/config.rs +++ b/playback/src/config.rs @@ -1,4 +1,4 @@ -use crate::audio::i24; +use crate::audio::convert::i24; use std::convert::TryFrom; use std::mem; use std::str::FromStr; diff --git a/playback/src/mixer/alsamixer.rs b/playback/src/mixer/alsamixer.rs index d9dbe31..5e0a963 100644 --- a/playback/src/mixer/alsamixer.rs +++ b/playback/src/mixer/alsamixer.rs @@ -33,13 +33,12 @@ impl AlsaMixer { let mixer = alsa::mixer::Mixer::new(&config.card, false)?; let sid = alsa::mixer::SelemId::new(&config.mixer, config.index); - let selem = mixer.find_selem(&sid).expect( - format!( + let selem = mixer.find_selem(&sid).unwrap_or_else(|| { + panic!( "Couldn't find simple mixer control for {},{}", &config.mixer, &config.index, ) - .as_str(), - ); + }); let (min, max) = selem.get_playback_volume_range(); let (min_db, max_db) = selem.get_playback_db_range(); let hw_mix = selem diff --git a/playback/src/player.rs b/playback/src/player.rs index f53abbf..3f0778f 100644 --- a/playback/src/player.rs +++ b/playback/src/player.rs @@ -206,11 +206,11 @@ pub struct NormalisationData { impl NormalisationData { pub fn db_to_ratio(db: f32) -> f32 { - return f32::powf(10.0, db / DB_VOLTAGE_RATIO); + f32::powf(10.0, db / DB_VOLTAGE_RATIO) } pub fn ratio_to_db(ratio: f32) -> f32 { - return ratio.log10() * DB_VOLTAGE_RATIO; + ratio.log10() * DB_VOLTAGE_RATIO } fn parse_from_file(mut file: T) -> io::Result { @@ -1161,7 +1161,7 @@ impl PlayerInternal { } if self.config.normalisation - && (normalisation_factor != 1.0 + && (f32::abs(normalisation_factor - 1.0) < f32::EPSILON || self.config.normalisation_method != NormalisationMethod::Basic) { for sample in data.iter_mut() { diff --git a/src/main.rs b/src/main.rs index 4abe4e5..318e175 100644 --- a/src/main.rs +++ b/src/main.rs @@ -322,7 +322,7 @@ fn setup(args: &[String]) -> Setup { .opt_str("format") .as_ref() .map(|format| AudioFormat::try_from(format).expect("Invalid output format")) - .unwrap_or(AudioFormat::default()); + .unwrap_or_default(); let device = matches.opt_str("device"); if device == Some("?".into()) { @@ -470,7 +470,7 @@ fn setup(args: &[String]) -> Setup { bitrate, gapless: !matches.opt_present("disable-gapless"), normalisation: matches.opt_present("enable-volume-normalisation"), - normalisation_method: normalisation_method, + normalisation_method, normalisation_type: gain_type, normalisation_pregain: matches .opt_str("normalisation-pregain") From 3e9aee1d46e9bc90d2b7c321a5f8d7c823c98788 Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Sat, 10 Apr 2021 15:08:39 +0200 Subject: [PATCH 100/103] Renamed variable --- src/main.rs | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/main.rs b/src/main.rs index 318e175..78e7e2f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -128,7 +128,7 @@ struct Setup { emit_sink_events: bool, } -fn setup(args: &[String]) -> Setup { +fn get_setup(args: &[String]) -> Setup { let mut opts = getopts::Options::new(); opts.optopt( "c", @@ -553,7 +553,7 @@ async fn main() { } let args: Vec = std::env::args().collect(); - let setupp = setup(&args); + let setup = get_setup(&args); let mut last_credentials = None; let mut spirc: Option = None; @@ -563,23 +563,23 @@ async fn main() { let mut discovery = None; let mut connecting: Pin>> = Box::pin(future::pending()); - if setupp.enable_discovery { - let config = setupp.connect_config.clone(); - let device_id = setupp.session_config.device_id.clone(); + if setup.enable_discovery { + let config = setup.connect_config.clone(); + let device_id = setup.session_config.device_id.clone(); discovery = Some( - librespot_connect::discovery::discovery(config, device_id, setupp.zeroconf_port) + librespot_connect::discovery::discovery(config, device_id, setup.zeroconf_port) .unwrap(), ); } - if let Some(credentials) = setupp.credentials { + if let Some(credentials) = setup.credentials { last_credentials = Some(credentials.clone()); connecting = Box::pin( Session::connect( - setupp.session_config.clone(), + setup.session_config.clone(), credentials, - setupp.cache.clone(), + setup.cache.clone(), ) .fuse(), ); @@ -602,9 +602,9 @@ async fn main() { } connecting = Box::pin(Session::connect( - setupp.session_config.clone(), + setup.session_config.clone(), credentials, - setupp.cache.clone(), + setup.cache.clone(), ).fuse()); }, None => { @@ -615,22 +615,22 @@ async fn main() { }, session = &mut connecting, if !connecting.is_terminated() => match session { Ok(session) => { - let mixer_config = setupp.mixer_config.clone(); - let mixer = (setupp.mixer)(Some(mixer_config)); - let player_config = setupp.player_config.clone(); - let connect_config = setupp.connect_config.clone(); + let mixer_config = setup.mixer_config.clone(); + let mixer = (setup.mixer)(Some(mixer_config)); + let player_config = setup.player_config.clone(); + let connect_config = setup.connect_config.clone(); let audio_filter = mixer.get_audio_filter(); - let format = setupp.format; - let backend = setupp.backend; - let device = setupp.device.clone(); + let format = setup.format; + let backend = setup.backend; + let device = setup.device.clone(); let (player, event_channel) = Player::new(player_config, session.clone(), audio_filter, move || { (backend)(device, format) }); - if setupp.emit_sink_events { - if let Some(player_event_program) = setupp.player_event_program.clone() { + if setup.emit_sink_events { + if let Some(player_event_program) = setup.player_event_program.clone() { player.set_sink_event_callback(Some(Box::new(move |sink_status| { match emit_sink_event(sink_status, &player_event_program) { Ok(e) if e.success() => (), @@ -676,16 +676,16 @@ async fn main() { auto_connect_times.push(Instant::now()); connecting = Box::pin(Session::connect( - setupp.session_config.clone(), + setup.session_config.clone(), credentials, - setupp.cache.clone(), + setup.cache.clone(), ).fuse()); } } }, event = async { player_event_channel.as_mut().unwrap().recv().await }, if player_event_channel.is_some() => match event { Some(event) => { - if let Some(program) = &setupp.player_event_program { + if let Some(program) = &setup.player_event_program { if let Some(child) = run_program_on_events(event, program) { let mut child = child.expect("program failed to start"); From 4c2fc61d64694e06b62c3a81d28e2bc183aa1be4 Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Mon, 12 Apr 2021 22:54:32 +0200 Subject: [PATCH 101/103] Better joining of session tasks --- core/src/session.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/core/src/session.rs b/core/src/session.rs index d7e478f..388ef39 100644 --- a/core/src/session.rs +++ b/core/src/session.rs @@ -10,7 +10,7 @@ use std::time::{SystemTime, UNIX_EPOCH}; use byteorder::{BigEndian, ByteOrder}; use bytes::Bytes; use futures_core::TryStream; -use futures_util::{FutureExt, StreamExt, TryStreamExt}; +use futures_util::{future, StreamExt, TryStreamExt}; use once_cell::sync::OnceCell; use thiserror::Error; use tokio::sync::mpsc; @@ -126,9 +126,14 @@ impl Session { .forward(sink); let receiver_task = DispatchTask(stream, session.weak()); - let task = - futures_util::future::join(sender_task, receiver_task).map(|_| io::Result::<_>::Ok(())); - tokio::spawn(task); + tokio::spawn(async move { + let result = future::try_join(sender_task, receiver_task).await; + + if let Err(e) = result { + error!("{}", e); + } + }); + session } From 7226bfd55ab72fb3d35a35f3bc38582975a8bec1 Mon Sep 17 00:00:00 2001 From: Roderick van Domburg Date: Thu, 15 Apr 2021 08:42:19 +0200 Subject: [PATCH 102/103] Remove warning for Rodio on Alsa (fixed upstream) (#696) --- playback/src/audio_backend/rodio.rs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/playback/src/audio_backend/rodio.rs b/playback/src/audio_backend/rodio.rs index 65436a3..ebbdd25 100644 --- a/playback/src/audio_backend/rodio.rs +++ b/playback/src/audio_backend/rodio.rs @@ -152,19 +152,14 @@ fn create_sink( } pub fn open(host: cpal::Host, device: Option, format: AudioFormat) -> RodioSink { - debug!( - "Using rodio sink with format {:?} and cpal host: {}", + info!( + "Using Rodio sink with format {:?} and cpal host: {}", format, host.id().name() ); - match format { - AudioFormat::F32 => { - #[cfg(target_os = "linux")] - warn!("Rodio output to Alsa is known to cause garbled sound, consider using `--backend alsa`") - } - AudioFormat::S16 => (), - _ => unimplemented!("Rodio currently only supports F32 and S16 formats"), + if format != AudioFormat::S16 && format != AudioFormat::F32 { + unimplemented!("Rodio currently only supports F32 and S16 formats"); } let (sink, stream) = create_sink(&host, device).unwrap(); From 4925adb4f103cea20f38eee1db9f09b0d3e07e75 Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Tue, 13 Apr 2021 20:34:26 +0200 Subject: [PATCH 103/103] Fix broken streaming mode --- audio/src/fetch/mod.rs | 2 + audio/src/fetch/receive.rs | 96 +++++++++++++++++--------------------- 2 files changed, 46 insertions(+), 52 deletions(-) diff --git a/audio/src/fetch/mod.rs b/audio/src/fetch/mod.rs index c19fac2..8e076eb 100644 --- a/audio/src/fetch/mod.rs +++ b/audio/src/fetch/mod.rs @@ -248,6 +248,7 @@ struct AudioFileShared { cond: Condvar, download_status: Mutex, download_strategy: Mutex, + number_of_open_requests: AtomicUsize, ping_time_ms: AtomicUsize, read_position: AtomicUsize, } @@ -356,6 +357,7 @@ impl AudioFileStreaming { downloaded: RangeSet::new(), }), download_strategy: Mutex::new(DownloadStrategy::RandomAccess()), // start with random access mode until someone tells us otherwise + number_of_open_requests: AtomicUsize::new(0), ping_time_ms: AtomicUsize::new(0), read_position: AtomicUsize::new(0), }); diff --git a/audio/src/fetch/receive.rs b/audio/src/fetch/receive.rs index 17f884f..0f056c9 100644 --- a/audio/src/fetch/receive.rs +++ b/audio/src/fetch/receive.rs @@ -68,12 +68,16 @@ async fn receive_data( initial_data_offset: usize, initial_request_length: usize, request_sent_time: Instant, - mut measure_ping_time: bool, - finish_tx: mpsc::UnboundedSender<()>, ) { let mut data_offset = initial_data_offset; let mut request_length = initial_request_length; + let old_number_of_request = shared + .number_of_open_requests + .fetch_add(1, atomic::Ordering::SeqCst); + + let mut measure_ping_time = old_number_of_request == 0; + let result = loop { let data = match data_rx.next().await { Some(Ok(data)) => data, @@ -121,7 +125,9 @@ async fn receive_data( shared.cond.notify_all(); } - let _ = finish_tx.send(()); + shared + .number_of_open_requests + .fetch_sub(1, atomic::Ordering::SeqCst); if result.is_err() { warn!( @@ -144,9 +150,6 @@ struct AudioFileFetch { file_data_tx: mpsc::UnboundedSender, complete_tx: Option>, network_response_times_ms: Vec, - number_of_open_requests: usize, - - download_finish_tx: mpsc::UnboundedSender<()>, } // Might be replaced by enum from std once stable @@ -214,11 +217,7 @@ impl AudioFileFetch { range.start, range.length, Instant::now(), - self.number_of_open_requests == 0, - self.download_finish_tx.clone(), )); - - self.number_of_open_requests += 1; } } @@ -341,7 +340,6 @@ impl AudioFileFetch { } StreamLoaderCommand::StreamMode() => { *(self.shared.download_strategy.lock().unwrap()) = DownloadStrategy::Streaming(); - self.trigger_preload(); } StreamLoaderCommand::Close() => return ControlFlow::Break, } @@ -355,36 +353,6 @@ impl AudioFileFetch { output.seek(SeekFrom::Start(0)).unwrap(); let _ = complete_tx.send(output); } - - fn trigger_preload(&mut self) { - if self.number_of_open_requests >= MAX_PREFETCH_REQUESTS { - return; - } - - let max_requests_to_send = MAX_PREFETCH_REQUESTS - self.number_of_open_requests; - - let bytes_pending: usize = { - let download_status = self.shared.download_status.lock().unwrap(); - download_status - .requested - .minus(&download_status.downloaded) - .len() - }; - - let ping_time_seconds = - 0.001 * self.shared.ping_time_ms.load(atomic::Ordering::Relaxed) as f64; - let download_rate = self.session.channel().get_download_rate_estimate(); - - let desired_pending_bytes = max( - (PREFETCH_THRESHOLD_FACTOR * ping_time_seconds * self.shared.stream_data_rate as f64) - as usize, - (FAST_PREFETCH_THRESHOLD_FACTOR * ping_time_seconds * download_rate as f64) as usize, - ); - - if bytes_pending < desired_pending_bytes { - self.pre_fetch_more_data(desired_pending_bytes - bytes_pending, max_requests_to_send); - } - } } pub(super) async fn audio_file_fetch( @@ -399,7 +367,6 @@ pub(super) async fn audio_file_fetch( complete_tx: oneshot::Sender, ) { let (file_data_tx, mut file_data_rx) = mpsc::unbounded_channel(); - let (download_finish_tx, mut download_finish_rx) = mpsc::unbounded_channel(); { let requested_range = Range::new(0, initial_data_length); @@ -414,8 +381,6 @@ pub(super) async fn audio_file_fetch( 0, initial_data_length, initial_request_sent_time, - true, - download_finish_tx.clone(), )); let mut fetch = AudioFileFetch { @@ -426,9 +391,6 @@ pub(super) async fn audio_file_fetch( file_data_tx, complete_tx: Some(complete_tx), network_response_times_ms: Vec::new(), - number_of_open_requests: 1, - - download_finish_tx, }; loop { @@ -442,12 +404,42 @@ pub(super) async fn audio_file_fetch( if data.map_or(true, |data| fetch.handle_file_data(data) == ControlFlow::Break) { break; } - }, - _ = download_finish_rx.recv() => { - fetch.number_of_open_requests -= 1; + } + } - if fetch.get_download_strategy() == DownloadStrategy::Streaming() { - fetch.trigger_preload(); + if fetch.get_download_strategy() == DownloadStrategy::Streaming() { + let number_of_open_requests = fetch + .shared + .number_of_open_requests + .load(atomic::Ordering::SeqCst); + if number_of_open_requests < MAX_PREFETCH_REQUESTS { + let max_requests_to_send = MAX_PREFETCH_REQUESTS - number_of_open_requests; + + let bytes_pending: usize = { + let download_status = fetch.shared.download_status.lock().unwrap(); + download_status + .requested + .minus(&download_status.downloaded) + .len() + }; + + let ping_time_seconds = + 0.001 * fetch.shared.ping_time_ms.load(atomic::Ordering::Relaxed) as f64; + let download_rate = fetch.session.channel().get_download_rate_estimate(); + + let desired_pending_bytes = max( + (PREFETCH_THRESHOLD_FACTOR + * ping_time_seconds + * fetch.shared.stream_data_rate as f64) as usize, + (FAST_PREFETCH_THRESHOLD_FACTOR * ping_time_seconds * download_rate as f64) + as usize, + ); + + if bytes_pending < desired_pending_bytes { + fetch.pre_fetch_more_data( + desired_pending_bytes - bytes_pending, + max_requests_to_send, + ); } } }