mirror of
https://github.com/crunchy-labs/crunchy-cli.git
synced 2026-01-21 12:12:00 -06:00
Use rust-native chromaprint port instead of ffmpeg
This commit is contained in:
parent
d3a227f364
commit
7837295f9b
5 changed files with 79 additions and 96 deletions
96
Cargo.lock
generated
96
Cargo.lock
generated
|
|
@ -179,18 +179,6 @@ version = "3.16.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
|
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bytemuck"
|
|
||||||
version = "1.15.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "byteorder"
|
|
||||||
version = "1.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytes"
|
name = "bytes"
|
||||||
version = "1.6.0"
|
version = "1.6.0"
|
||||||
|
|
@ -381,8 +369,6 @@ dependencies = [
|
||||||
"fs2",
|
"fs2",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"http",
|
"http",
|
||||||
"image",
|
|
||||||
"image_hasher",
|
|
||||||
"indicatif",
|
"indicatif",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"log",
|
"log",
|
||||||
|
|
@ -391,6 +377,7 @@ dependencies = [
|
||||||
"regex",
|
"regex",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"rustls-native-certs",
|
"rustls-native-certs",
|
||||||
|
"rusty-chromaprint",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_plain",
|
"serde_plain",
|
||||||
|
|
@ -951,32 +938,6 @@ dependencies = [
|
||||||
"unicode-normalization",
|
"unicode-normalization",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "image"
|
|
||||||
version = "0.25.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "fd54d660e773627692c524beaad361aca785a4f9f5730ce91f42aabe5bce3d11"
|
|
||||||
dependencies = [
|
|
||||||
"bytemuck",
|
|
||||||
"byteorder",
|
|
||||||
"num-traits",
|
|
||||||
"zune-core",
|
|
||||||
"zune-jpeg",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "image_hasher"
|
|
||||||
version = "2.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9481465fe767d92494987319b0b447a5829edf57f09c52bf8639396abaaeaf78"
|
|
||||||
dependencies = [
|
|
||||||
"base64 0.22.0",
|
|
||||||
"image",
|
|
||||||
"rustdct",
|
|
||||||
"serde",
|
|
||||||
"transpose",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "1.9.3"
|
version = "1.9.3"
|
||||||
|
|
@ -1417,6 +1378,15 @@ dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "realfft"
|
||||||
|
version = "3.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "953d9f7e5cdd80963547b456251296efc2626ed4e3cbf36c869d9564e0220571"
|
||||||
|
dependencies = [
|
||||||
|
"rustfft",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_users"
|
name = "redox_users"
|
||||||
version = "0.4.5"
|
version = "0.4.5"
|
||||||
|
|
@ -1531,21 +1501,24 @@ version = "0.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b833d8d034ea094b1ea68aa6d5c740e0d04bad9d16568d08ba6f76823a114316"
|
checksum = "b833d8d034ea094b1ea68aa6d5c740e0d04bad9d16568d08ba6f76823a114316"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rubato"
|
||||||
|
version = "0.14.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e6dd52e80cfc21894deadf554a5673002938ae4625f7a283e536f9cf7c17b0d5"
|
||||||
|
dependencies = [
|
||||||
|
"num-complex",
|
||||||
|
"num-integer",
|
||||||
|
"num-traits",
|
||||||
|
"realfft",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-demangle"
|
name = "rustc-demangle"
|
||||||
version = "0.1.23"
|
version = "0.1.23"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
|
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustdct"
|
|
||||||
version = "0.7.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8b61555105d6a9bf98797c063c362a1d24ed8ab0431655e38f1cf51e52089551"
|
|
||||||
dependencies = [
|
|
||||||
"rustfft",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustfft"
|
name = "rustfft"
|
||||||
version = "6.2.0"
|
version = "6.2.0"
|
||||||
|
|
@ -1628,6 +1601,16 @@ dependencies = [
|
||||||
"untrusted",
|
"untrusted",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rusty-chromaprint"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1755646867c36ecb391776deaa0b557a76d3badf20c142de7282630c34b20440"
|
||||||
|
dependencies = [
|
||||||
|
"rubato",
|
||||||
|
"rustfft",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.17"
|
version = "1.0.17"
|
||||||
|
|
@ -2501,18 +2484,3 @@ name = "zeroize"
|
||||||
version = "1.8.0"
|
version = "1.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "63381fa6624bf92130a6b87c0d07380116f80b565c42cf0d754136f0238359ef"
|
checksum = "63381fa6624bf92130a6b87c0d07380116f80b565c42cf0d754136f0238359ef"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "zune-core"
|
|
||||||
version = "0.4.12"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "zune-jpeg"
|
|
||||||
version = "0.4.11"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ec866b44a2a1fd6133d363f073ca1b179f438f99e7e5bfb1e33f7181facfe448"
|
|
||||||
dependencies = [
|
|
||||||
"zune-core",
|
|
||||||
]
|
|
||||||
|
|
|
||||||
|
|
@ -24,14 +24,13 @@ derive_setters = "0.1"
|
||||||
futures-util = { version = "0.3", features = ["io"] }
|
futures-util = { version = "0.3", features = ["io"] }
|
||||||
fs2 = "0.4"
|
fs2 = "0.4"
|
||||||
http = "1.1"
|
http = "1.1"
|
||||||
image = { version = "0.25", features = ["jpeg"], default-features = false }
|
|
||||||
image_hasher = "2.0"
|
|
||||||
indicatif = "0.17"
|
indicatif = "0.17"
|
||||||
lazy_static = "1.4"
|
lazy_static = "1.4"
|
||||||
log = { version = "0.4", features = ["std"] }
|
log = { version = "0.4", features = ["std"] }
|
||||||
num_cpus = "1.16"
|
num_cpus = "1.16"
|
||||||
regex = "1.10"
|
regex = "1.10"
|
||||||
reqwest = { version = "0.12", features = ["socks", "stream"] }
|
reqwest = { version = "0.12", features = ["socks", "stream"] }
|
||||||
|
rusty-chromaprint = "0.2"
|
||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
serde_plain = "1.0"
|
serde_plain = "1.0"
|
||||||
|
|
|
||||||
|
|
@ -281,6 +281,7 @@ impl Downloader {
|
||||||
format_id: i,
|
format_id: i,
|
||||||
path,
|
path,
|
||||||
locale: locale.clone(),
|
locale: locale.clone(),
|
||||||
|
sample_rate: stream_data.sampling_rate().unwrap(),
|
||||||
video_idx: i,
|
video_idx: i,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
|
use std::io::Read;
|
||||||
|
use std::process::Stdio;
|
||||||
use std::{
|
use std::{
|
||||||
cmp,
|
cmp,
|
||||||
collections::{HashMap, HashSet},
|
collections::{HashMap, HashSet},
|
||||||
|
mem,
|
||||||
ops::Not,
|
ops::Not,
|
||||||
path::Path,
|
path::Path,
|
||||||
process::Command,
|
process::Command,
|
||||||
|
|
@ -12,6 +15,7 @@ use log::debug;
|
||||||
use tempfile::TempPath;
|
use tempfile::TempPath;
|
||||||
|
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
|
use rusty_chromaprint::{Configuration, Fingerprinter};
|
||||||
|
|
||||||
use super::fmt::format_time_delta;
|
use super::fmt::format_time_delta;
|
||||||
|
|
||||||
|
|
@ -19,6 +23,7 @@ pub struct SyncAudio {
|
||||||
pub format_id: usize,
|
pub format_id: usize,
|
||||||
pub path: TempPath,
|
pub path: TempPath,
|
||||||
pub locale: Locale,
|
pub locale: Locale,
|
||||||
|
pub sample_rate: u32,
|
||||||
pub video_idx: usize,
|
pub video_idx: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -43,11 +48,12 @@ pub fn sync_audios(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
formats.insert(audio.format_id);
|
formats.insert(audio.format_id);
|
||||||
sync_audios.push((audio.format_id, &audio.path));
|
sync_audios.push((audio.format_id, &audio.path, audio.sample_rate));
|
||||||
chromaprints.insert(
|
chromaprints.insert(
|
||||||
audio.format_id,
|
audio.format_id,
|
||||||
generate_chromaprint(
|
generate_chromaprint(
|
||||||
&audio.path,
|
&audio.path,
|
||||||
|
audio.sample_rate,
|
||||||
&TimeDelta::zero(),
|
&TimeDelta::zero(),
|
||||||
&TimeDelta::zero(),
|
&TimeDelta::zero(),
|
||||||
&TimeDelta::zero(),
|
&TimeDelta::zero(),
|
||||||
|
|
@ -112,6 +118,7 @@ pub fn sync_audios(
|
||||||
for sync_audio in &sync_audios {
|
for sync_audio in &sync_audios {
|
||||||
let chromaprint = generate_chromaprint(
|
let chromaprint = generate_chromaprint(
|
||||||
sync_audio.1,
|
sync_audio.1,
|
||||||
|
sync_audio.2,
|
||||||
&start,
|
&start,
|
||||||
&end,
|
&end,
|
||||||
initial_offsets.get(&sync_audio.0).unwrap(),
|
initial_offsets.get(&sync_audio.0).unwrap(),
|
||||||
|
|
@ -127,7 +134,7 @@ pub fn sync_audios(
|
||||||
);
|
);
|
||||||
chromaprints.insert(
|
chromaprints.insert(
|
||||||
base_audio.0,
|
base_audio.0,
|
||||||
generate_chromaprint(base_audio.1, &start, &end, &base_offset)?,
|
generate_chromaprint(base_audio.1, base_audio.2, &start, &end, &base_offset)?,
|
||||||
);
|
);
|
||||||
for audio in &sync_audios {
|
for audio in &sync_audios {
|
||||||
let initial_offset = initial_offsets.get(&audio.0).copied().unwrap();
|
let initial_offset = initial_offsets.get(&audio.0).copied().unwrap();
|
||||||
|
|
@ -207,6 +214,7 @@ fn find_offset(
|
||||||
|
|
||||||
fn generate_chromaprint(
|
fn generate_chromaprint(
|
||||||
input_file: &Path,
|
input_file: &Path,
|
||||||
|
sample_rate: u32,
|
||||||
start: &TimeDelta,
|
start: &TimeDelta,
|
||||||
end: &TimeDelta,
|
end: &TimeDelta,
|
||||||
offset: &TimeDelta,
|
offset: &TimeDelta,
|
||||||
|
|
@ -218,6 +226,9 @@ fn generate_chromaprint(
|
||||||
offset_argument = offset;
|
offset_argument = offset;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut printer = Fingerprinter::new(&Configuration::preset_test1());
|
||||||
|
printer.start(sample_rate, 2)?;
|
||||||
|
|
||||||
let mut command = Command::new("ffmpeg");
|
let mut command = Command::new("ffmpeg");
|
||||||
command
|
command
|
||||||
.arg("-hide_banner")
|
.arg("-hide_banner")
|
||||||
|
|
@ -232,30 +243,42 @@ fn generate_chromaprint(
|
||||||
.args(["-itsoffset", format_time_delta(offset_argument).as_str()])
|
.args(["-itsoffset", format_time_delta(offset_argument).as_str()])
|
||||||
.args(["-i", input_file.to_string_lossy().to_string().as_str()])
|
.args(["-i", input_file.to_string_lossy().to_string().as_str()])
|
||||||
.args(["-ac", "2"])
|
.args(["-ac", "2"])
|
||||||
.args(["-f", "chromaprint"])
|
.args([
|
||||||
.args(["-fp_format", "raw"])
|
"-f",
|
||||||
|
if cfg!(target_endian = "big") {
|
||||||
|
"s16be"
|
||||||
|
} else {
|
||||||
|
"s16le"
|
||||||
|
},
|
||||||
|
])
|
||||||
.arg("-");
|
.arg("-");
|
||||||
|
|
||||||
let extract_output = command.output()?;
|
let mut handle = command
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.stderr(Stdio::piped())
|
||||||
|
.spawn()?;
|
||||||
|
|
||||||
if !extract_output.status.success() {
|
// the stdout is read in chunks because keeping all the raw audio data in memory would take up
|
||||||
bail!(
|
// a significant amount of space
|
||||||
"{}",
|
let mut stdout = handle.stdout.take().unwrap();
|
||||||
String::from_utf8_lossy(extract_output.stderr.as_slice())
|
let mut buf: [u8; 32_000] = [0; 32_000];
|
||||||
);
|
while handle.try_wait()?.is_none() {
|
||||||
|
loop {
|
||||||
|
let read_bytes = stdout.read(&mut buf)?;
|
||||||
|
if read_bytes == 0 {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
let raw_chromaprint = extract_output.stdout.as_slice();
|
let data: [i16; 16_000] = unsafe { mem::transmute(buf) };
|
||||||
let length = raw_chromaprint.len();
|
printer.consume(&data[0..(read_bytes / 2)])
|
||||||
if length % 4 != 0 {
|
|
||||||
bail!("chromaprint bytes should be a multiple of 4");
|
|
||||||
}
|
}
|
||||||
let mut chromaprint = Vec::with_capacity(length / 4);
|
|
||||||
for i in 0..length / 4 {
|
|
||||||
chromaprint.push(as_u32_le(
|
|
||||||
raw_chromaprint[i * 4..i * 4 + 4].try_into().unwrap(),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
Ok(chromaprint)
|
|
||||||
|
if !handle.wait()?.success() {
|
||||||
|
bail!("{}", std::io::read_to_string(handle.stderr.unwrap())?)
|
||||||
|
}
|
||||||
|
|
||||||
|
printer.finish();
|
||||||
|
return Ok(printer.fingerprint().into());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compare_chromaprints(
|
fn compare_chromaprints(
|
||||||
|
|
@ -407,11 +430,3 @@ fn timestamps_to_ranges(mut timestamps: Vec<f64>) -> Option<Vec<TimeRange>> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_u32_le(array: &[u8; 4]) -> u32 {
|
|
||||||
#![allow(arithmetic_overflow)]
|
|
||||||
(array[0] as u32)
|
|
||||||
| ((array[1] as u32) << 8)
|
|
||||||
| ((array[2] as u32) << 16)
|
|
||||||
| ((array[3] as u32) << 24)
|
|
||||||
}
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue