diff options
| author | Nathan Perry <avaglir@gmail.com> | 2019-02-17 19:40:27 -0500 |
|---|---|---|
| committer | Nathan Perry <avaglir@gmail.com> | 2019-02-17 19:40:27 -0500 |
| commit | 3eecbaf1ff02122506ee8ee8c65e02ff1325aae0 (patch) | |
| tree | ee887ed1f84d3e90bd5a20a75c279d6333c09593 | |
| parent | e9e3458225cfc0fdc20a7d2960ef15d4de349802 (diff) | |
use flate2 to compress audio
| -rw-r--r-- | Cargo.lock | 1 | ||||
| -rw-r--r-- | Cargo.toml | 1 | ||||
| -rw-r--r-- | src/audio/play_queue.rs | 15 | ||||
| -rw-r--r-- | src/audio/ytdl.rs | 89 | ||||
| -rw-r--r-- | src/commands/meme.rs | 29 | ||||
| -rw-r--r-- | src/main.rs | 7 |
6 files changed, 102 insertions, 40 deletions
@@ -1726,6 +1726,7 @@ dependencies = [ "either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "fern 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)", + "flate2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -28,6 +28,7 @@ regex = "1.1.0" clap = "2.32.0" itertools = "0.8.0" serde_json = "~1.0" +flate2 = "^1.0" [dependencies.serenity] default-features = false diff --git a/src/audio/play_queue.rs b/src/audio/play_queue.rs index deb4e2c..119b8c5 100644 --- a/src/audio/play_queue.rs +++ b/src/audio/play_queue.rs @@ -1,13 +1,18 @@ use std::{ + collections::VecDeque, + io::Cursor, sync::{Arc, RwLock}, thread, - collections::VecDeque, time::Duration, }; -use typemap::Key; use either::{Left, Right}; -use serenity::prelude::*; +use flate2::bufread::DeflateDecoder; +use serenity::{ + prelude::*, + voice, +}; +use typemap::Key; use crate::{ audio::{ @@ -16,8 +21,8 @@ use crate::{ ytdl, }, commands::{ - sound_levels::DEFAULT_VOLUME, send, + sound_levels::DEFAULT_VOLUME, }, must_env_lookup, TARGET_GUILD_ID, @@ -103,7 +108,7 @@ impl PlayQueue { } }, Right(ref vec) => { - ::serenity::voice::pcm(true, ::std::io::Cursor::new(vec.clone())) + voice::pcm(true, DeflateDecoder::new(Cursor::new(vec.clone()))) } }; diff --git a/src/audio/ytdl.rs b/src/audio/ytdl.rs index 8384db4..e88d8f8 100644 --- a/src/audio/ytdl.rs +++ b/src/audio/ytdl.rs @@ -6,15 +6,14 @@ use std::{ Result as IoResult, }, process::{ + Child, Command, Stdio, - Child, }, }; use chrono::Duration; use serde_json::Value; - use serenity::{ voice::{ AudioSource, @@ -25,7 +24,6 @@ use serenity::{ use crate::Result; - struct ChildContainer(Child); impl Read for ChildContainer { @@ -35,14 +33,53 @@ impl Read for ChildContainer { } impl Drop for ChildContainer { - fn drop (&mut self) { + fn drop(&mut self) { if let Err(e) = self.0.kill() { - debug!("[Voice] Error awaiting child process: {:?}", e); + debug!("Error awaiting child process: {:?}", e); + } + } +} + +pub(crate) trait CodecInfo { + fn ffmpeg_opts() -> &'static[&'static str]; +} + +pub(crate) struct Pcm {} +pub(crate) struct Opus {} + +impl CodecInfo for Pcm { + #[inline] + fn ffmpeg_opts() -> &'static[&'static str] { + lazy_static! { + static ref OPTS: Vec<&'static str> = vec! [ + "-f", "s16le", + "-acodec", "pcm_s16le", + ]; + } + + &*OPTS + } +} + +impl CodecInfo for Opus { + #[inline] + fn ffmpeg_opts() -> &'static[&'static str] { + lazy_static! { + static ref OPTS: Vec<&'static str> = vec! [ +// "-f", "s16le", + "-acodec", "libopus", + "-sample_fmt", "s16", + "-vbr", "off", +// "-b:a 96k", +// "-vn", + ]; } + + &*OPTS } } -pub fn ytdl_reader(uri: &str, start: Option<Duration>, end: Option<Duration>) -> Result<Box<dyn Read + Send>> { +pub fn ytdl_url(uri: &str) -> Result<String> { let args = [ "-f", "webm[abr>0]/bestaudio/best", @@ -67,26 +104,24 @@ pub fn ytdl_reader(uri: &str, start: Option<Duration>, end: Option<Duration>) -> other => return Err(VoiceError::YouTubeDLProcessing(other).into()), }; - let uri = match obj.remove("url") { + match obj.remove("url") { Some(v) => match v { - Value::String(uri) => uri, - other => return Err(VoiceError::YouTubeDLUrl(other).into()), + Value::String(uri) => Ok(uri), + other => Err(VoiceError::YouTubeDLUrl(other).into()), }, - None => return Err(VoiceError::YouTubeDLUrl(Value::Object(obj)).into()), - }; + None => Err(VoiceError::YouTubeDLUrl(Value::Object(obj)).into()), + } +} +pub(crate) fn ffmpeg_dl<T: CodecInfo>(uri: &str, start: Option<Duration>, end: Option<Duration>, size_limit: Option<usize>) -> Result<Box<dyn Read + Send>> { let start = start.unwrap_or(Duration::zero()); let start_str = format!("{:02}:{:02}:{:02}", start.num_hours(), start.num_minutes() % 60, start.num_seconds() % 60); let mut opts = vec! [ - "-f", - "s16le", "-ac", "2", // force stereo -- this may cause issues "-ar", "48000", - "-acodec", - "pcm_s16le", "-ss", &start_str, ] @@ -94,21 +129,28 @@ pub fn ytdl_reader(uri: &str, start: Option<Duration>, end: Option<Duration>) -> .map(|s| s.to_owned()) .collect::<Vec<_>>(); - match end { - Some(e) => { - opts.push("-to".to_owned()); - opts.push(format!("{:02}:{:02}:{:02}", e.num_hours(), e.num_minutes() % 60, e.num_seconds() % 60)); - }, - _ => {}, + let codec_opts = T::ffmpeg_opts().into_iter().map(|&s| s.to_owned()).collect::<Vec<_>>(); + opts.extend(codec_opts); + + if let Some(limit) = size_limit { + opts.push("-fs".to_owned()); + opts.push(format!("{}", limit)); + } + + if let Some(e) = end { + opts.push("-to".to_owned()); + opts.push(format!("{:02}:{:02}:{:02}", e.num_hours(), e.num_minutes() % 60, e.num_seconds() % 60)); } opts.push("-".to_owned()); + debug!("ffmpeg -i \"{}\" {}", uri, opts.join(" ")); + let command = Command::new("ffmpeg") .arg("-i") .arg(uri) .args(opts) - .stderr(Stdio::null()) + .stderr(Stdio::piped()) .stdin(Stdio::null()) .stdout(Stdio::piped()) .spawn()?; @@ -117,6 +159,7 @@ pub fn ytdl_reader(uri: &str, start: Option<Duration>, end: Option<Duration>) -> } pub fn ytdl(uri: &str, start: Option<Duration>, end: Option<Duration>) -> Result<Box<AudioSource>> { - let command = ytdl_reader(uri, start, end)?; + let youtube_uri = ytdl_url(uri)?; + let command = ffmpeg_dl::<Pcm>(&youtube_uri, start, end, None)?; Ok(pcm(true, command)) } diff --git a/src/commands/meme.rs b/src/commands/meme.rs index 922091c..160a652 100644 --- a/src/commands/meme.rs +++ b/src/commands/meme.rs @@ -5,9 +5,12 @@ use std::{ use diesel::PgConnection; use failure::Error; +use flate2::{ + bufread::DeflateEncoder, + Compression, +}; use lazy_static::lazy_static; use rand::{Rng, thread_rng}; -use url::Url; use serenity::{ builder::CreateMessage, framework::standard::Args, @@ -15,18 +18,21 @@ use serenity::{ model::channel::Message, prelude::*, }; +use url::Url; + +use audio::ytdl_url; use crate::{ audio::{ CtxExt, + ffmpeg_dl, + Opus, + parse_times, + Pcm, PlayArgs, PlayQueue, - ytdl_reader, - parse_times, - }, - commands::{ - send, }, + commands::send, db::*, Result, }; @@ -148,7 +154,11 @@ pub fn addaudiomeme(_: &mut Context, msg: &Message, mut args: Args) -> Result<() let opts = elems[1..].join(" "); let (start, end) = parse_times(opts); - let audio_reader = BufReader::new(ytdl_reader(audio_link.as_str(), start, end)?); + let youtube_url = ytdl_url(audio_link.as_str())?; + let mut audio_reader = DeflateEncoder::new( + BufReader::new(ffmpeg_dl::<Pcm>(&youtube_url, start, end, None)?), + Compression::best(), + ); let text = match args.multiple_quoted::<String>() { Ok(text) => text.join(" "), @@ -168,9 +178,10 @@ pub fn addaudiomeme(_: &mut Context, msg: &Message, mut args: Args) -> Result<() .ok(); let mut audio_data = Vec::new(); - audio_reader.take(5 * 1024 * 1024).read_to_end(&mut audio_data)?; + let bytes = audio_reader.read_to_end(&mut audio_data)?; - if audio_data.len() == 0 { + if bytes == 0 { + debug!("read 0 bytes from audio reader"); return send(msg.channel_id, "🔇🔇🔇🔕🔕🔕🔕🔕🔇🔕🔕🔇🔕🔕📣📢📣📢📣", msg.tts); } diff --git a/src/main.rs b/src/main.rs index 708d778..5162015 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,17 +8,18 @@ extern crate dotenv; extern crate either; #[macro_use] extern crate failure; extern crate fern; +extern crate flate2; +#[cfg_attr(test, macro_use)] extern crate itertools; #[macro_use] extern crate lazy_static; #[macro_use] extern crate log; extern crate rand; extern crate regex; +extern crate serde_json; extern crate serenity; extern crate sha1; +extern crate time; extern crate typemap; extern crate url; -#[cfg_attr(test, macro_use)] extern crate itertools; -extern crate time; -extern crate serde_json; use std::{ thread, |
