From 09e506ed668c582f4a9c6af928465d222c967392 Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Tue, 9 Feb 2021 19:42:56 +0100 Subject: [PATCH 1/8] Replace version functions by constants --- connect/src/spirc.rs | 2 +- core/src/config.rs | 2 +- core/src/connection/mod.rs | 6 ++--- core/src/version.rs | 50 ++++++++------------------------------ src/main.rs | 10 ++++---- 5 files changed, 20 insertions(+), 50 deletions(-) diff --git a/connect/src/spirc.rs b/connect/src/spirc.rs index 352a3fcf..33d74366 100644 --- a/connect/src/spirc.rs +++ b/connect/src/spirc.rs @@ -102,7 +102,7 @@ fn initial_state() -> State { fn initial_device_state(config: ConnectConfig) -> DeviceState { { let mut msg = DeviceState::new(); - msg.set_sw_version(version::version_string()); + msg.set_sw_version(version::VERSION_STRING.to_string()); msg.set_is_active(false); msg.set_can_play(true); msg.set_volume(0); diff --git a/core/src/config.rs b/core/src/config.rs index 12c1b2ed..def30f6d 100644 --- a/core/src/config.rs +++ b/core/src/config.rs @@ -17,7 +17,7 @@ impl Default for SessionConfig { fn default() -> SessionConfig { let device_id = Uuid::new_v4().to_hyphenated().to_string(); SessionConfig { - user_agent: version::version_string(), + user_agent: version::VERSION_STRING.to_string(), device_id: device_id, proxy: None, ap_port: None, diff --git a/core/src/connection/mod.rs b/core/src/connection/mod.rs index ffc6d0f4..eccd5355 100644 --- a/core/src/connection/mod.rs +++ b/core/src/connection/mod.rs @@ -88,11 +88,11 @@ pub fn authenticate( .mut_system_info() .set_system_information_string(format!( "librespot_{}_{}", - version::short_sha(), - version::build_id() + version::SHA_SHORT, + version::BUILD_ID )); packet.mut_system_info().set_device_id(device_id); - packet.set_version_string(version::version_string()); + packet.set_version_string(version::VERSION_STRING.to_string()); let cmd = 0xab; let data = packet.write_to_bytes().unwrap(); diff --git a/core/src/version.rs b/core/src/version.rs index cd7fa042..d15f296b 100644 --- a/core/src/version.rs +++ b/core/src/version.rs @@ -1,44 +1,14 @@ -pub fn version_string() -> String { - format!("librespot-{}", short_sha()) -} +/// Version string of the form "librespot-" +pub const VERSION_STRING: &str = concat!("librespot-", env!("VERGEN_SHA_SHORT")); -// Generate a timestamp representing now (UTC) in RFC3339 format. -pub fn now() -> &'static str { - env!("VERGEN_BUILD_TIMESTAMP") -} +/// Generate a timstamp string representing now (UTC). +pub const BUILD_DATE: &str = env!("VERGEN_BUILD_DATE"); -// Generate a timstamp string representing now (UTC). -pub fn short_now() -> &'static str { - env!("VERGEN_BUILD_DATE") -} +/// Short sha of the latest git commit. +pub const SHA_SHORT: &str = env!("VERGEN_SHA_SHORT"); -// Generate a SHA string -pub fn sha() -> &'static str { - env!("VERGEN_SHA") -} +/// Date of the latest git commit. +pub const COMMIT_DATE: &str = env!("VERGEN_COMMIT_DATE"); -// Generate a short SHA string -pub fn short_sha() -> &'static str { - env!("VERGEN_SHA_SHORT") -} - -// Generate the commit date string -pub fn commit_date() -> &'static str { - env!("VERGEN_COMMIT_DATE") -} - -// Generate the target triple string -pub fn target() -> &'static str { - env!("VERGEN_TARGET_TRIPLE") -} - -// Generate a semver string -pub fn semver() -> &'static str { - // env!("VERGEN_SEMVER") - env!("CARGO_PKG_VERSION") -} - -// Generate a random build id. -pub fn build_id() -> &'static str { - env!("VERGEN_BUILD_ID") -} +/// A random build id. +pub const BUILD_ID: &str = env!("VERGEN_BUILD_ID"); diff --git a/src/main.rs b/src/main.rs index 4c57808f..b1101098 100644 --- a/src/main.rs +++ b/src/main.rs @@ -225,10 +225,10 @@ fn setup(args: &[String]) -> Setup { info!( "librespot {} ({}). Built on {}. Build ID: {}", - version::short_sha(), - version::commit_date(), - version::short_now(), - version::build_id() + version::SHA_SHORT, + version::COMMIT_DATE, + version::BUILD_DATE, + version::BUILD_ID ); let backend_name = matches.opt_str("backend"); @@ -329,7 +329,7 @@ fn setup(args: &[String]) -> Setup { let device_id = device_id(&name); SessionConfig { - user_agent: version::version_string(), + user_agent: version::VERSION_STRING.to_string(), device_id: device_id, proxy: matches.opt_str("proxy").or(std::env::var("http_proxy").ok()).map( |s| { From 85be0d075a93d167476b355c4a93602c808ba689 Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Fri, 12 Feb 2021 20:21:07 +0100 Subject: [PATCH 2/8] Adjust documentation --- core/src/version.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/version.rs b/core/src/version.rs index d15f296b..62778450 100644 --- a/core/src/version.rs +++ b/core/src/version.rs @@ -1,7 +1,7 @@ /// Version string of the form "librespot-" pub const VERSION_STRING: &str = concat!("librespot-", env!("VERGEN_SHA_SHORT")); -/// Generate a timstamp string representing now (UTC). +/// Generate a timestamp string representing the build date (UTC). pub const BUILD_DATE: &str = env!("VERGEN_BUILD_DATE"); /// Short sha of the latest git commit. From bce4858b9e0bd5308376dcba90c17f14c74a969c Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Sat, 13 Feb 2021 18:43:24 +0100 Subject: [PATCH 3/8] Add semver constant, rename "build id" env var --- core/build.rs | 3 ++- core/src/version.rs | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/core/build.rs b/core/build.rs index e8c71e4a..4877ff46 100644 --- a/core/build.rs +++ b/core/build.rs @@ -15,5 +15,6 @@ fn main() { .map(|()| rng.sample(Alphanumeric)) .take(8) .collect(); - println!("cargo:rustc-env=VERGEN_BUILD_ID={}", build_id); + + println!("cargo:rustc-env=LIBRESPOT_BUILD_ID={}", build_id); } diff --git a/core/src/version.rs b/core/src/version.rs index 62778450..ef553463 100644 --- a/core/src/version.rs +++ b/core/src/version.rs @@ -10,5 +10,8 @@ pub const SHA_SHORT: &str = env!("VERGEN_SHA_SHORT"); /// Date of the latest git commit. pub const COMMIT_DATE: &str = env!("VERGEN_COMMIT_DATE"); +/// Librespot crate version. +pub const SEMVER: &str = env!("CARGO_PKG_VERSION"); + /// A random build id. -pub const BUILD_ID: &str = env!("VERGEN_BUILD_ID"); +pub const BUILD_ID: &str = env!("LIBRESPOT_BUILD_ID"); From c8e45ab6903d299d46acaee71d91ceec63a09457 Mon Sep 17 00:00:00 2001 From: Johannes Dertmann Date: Wed, 17 Feb 2021 15:13:57 +0100 Subject: [PATCH 4/8] Modified startup message --- src/main.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main.rs b/src/main.rs index b1101098..f1c02946 100644 --- a/src/main.rs +++ b/src/main.rs @@ -224,11 +224,11 @@ fn setup(args: &[String]) -> Setup { setup_logging(verbose); info!( - "librespot {} ({}). Built on {}. Build ID: {}", - version::SHA_SHORT, - version::COMMIT_DATE, - version::BUILD_DATE, - version::BUILD_ID + "librespot {semver} {sha} (Built on {build_date}, Build ID: {build_id})", + semver = version::SEMVER, + sha = version::SHA_SHORT, + build_date = version::BUILD_DATE, + build_id = version::BUILD_ID ); let backend_name = matches.opt_str("backend"); From 58bd339e90ddf6f8a53dd23f8deb00392b9868af Mon Sep 17 00:00:00 2001 From: Johannes Dertmann Date: Thu, 18 Feb 2021 09:53:46 +0100 Subject: [PATCH 5/8] Restore MSRV to 1.41 --- .github/workflows/test.yml | 2 +- COMPILING.md | 2 +- Cargo.lock | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4ad4b406..adbd10c4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -41,7 +41,7 @@ jobs: matrix: os: [ubuntu-latest] toolchain: - - 1.42.0 # MSRV (Minimum supported rust version) + - 1.41.1 # MSRV (Minimum supported rust version) - stable - beta experimental: [false] diff --git a/COMPILING.md b/COMPILING.md index 40eefb39..4320cdbb 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.40.0, 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.41, 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: diff --git a/Cargo.lock b/Cargo.lock index efaae37a..9a2e42ea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -117,9 +117,9 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" [[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", @@ -1943,9 +1943,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" From 4beb3d5e60dd4992691a6ac896aab4911a86cd2b Mon Sep 17 00:00:00 2001 From: Sasha Hilton Date: Tue, 23 Feb 2021 18:35:57 +0000 Subject: [PATCH 6/8] Add version string CLI parameter, set name to optional parameter, default to 'librespot' --- src/main.rs | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index f1c02946..58391b1b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -71,6 +71,16 @@ fn list_backends() { } } +fn print_version() { + println!( + "librespot {semver} {sha} (Built on {build_date}, Build ID: {build_id})", + semver = version::SEMVER, + sha = version::SHA_SHORT, + build_date = version::BUILD_DATE, + build_id = version::BUILD_ID + ); +} + #[derive(Clone)] struct Setup { backend: fn(Option) -> Box, @@ -103,7 +113,7 @@ fn setup(args: &[String]) -> Setup { "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("n", "name", "Device name", "NAME") .optopt("", "device-type", "Displayed device type", "DEVICE_TYPE") .optopt( "b", @@ -119,6 +129,7 @@ fn setup(args: &[String]) -> Setup { ) .optflag("", "emit-sink-events", "Run program set by --onevent before sink is opened and after it is closed.") .optflag("v", "verbose", "Enable verbose output") + .optflag("V", "version", "Display librespot version string") .optopt("u", "username", "Username to sign in with", "USERNAME") .optopt("p", "password", "Password", "PASSWORD") .optopt("", "proxy", "HTTP proxy to use when connecting", "PROXY") @@ -220,6 +231,11 @@ fn setup(args: &[String]) -> Setup { } }; + if matches.opt_present("version") { + print_version(); + exit(0); + } + let verbose = matches.opt_present("verbose"); setup_logging(verbose); @@ -306,7 +322,7 @@ fn setup(args: &[String]) -> Setup { .map(|port| port.parse::().unwrap()) .unwrap_or(0); - let name = matches.opt_str("name").unwrap(); + let name = matches.opt_str("name").unwrap_or("Librespot".to_string()); let credentials = { let cached_credentials = cache.as_ref().and_then(Cache::credentials); From 8dc1e80633cccebed2a28224d8e43b19047d747e Mon Sep 17 00:00:00 2001 From: Philippe G Date: Sat, 27 Feb 2021 14:59:53 -0800 Subject: [PATCH 7/8] 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 3a011011..6fab78ec 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 86dbaa8ed53c5b76414d1a577f680d2a65dd2c89 Mon Sep 17 00:00:00 2001 From: philippe44 Date: Sat, 20 Mar 2021 12:11:49 -0700 Subject: [PATCH 8/8] 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 6fab78ec..082f6915 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;