aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNathan Perry <avaglir@gmail.com>2019-02-17 19:40:27 -0500
committerNathan Perry <avaglir@gmail.com>2019-02-17 19:40:27 -0500
commit3eecbaf1ff02122506ee8ee8c65e02ff1325aae0 (patch)
treeee887ed1f84d3e90bd5a20a75c279d6333c09593
parente9e3458225cfc0fdc20a7d2960ef15d4de349802 (diff)
use flate2 to compress audio
-rw-r--r--Cargo.lock1
-rw-r--r--Cargo.toml1
-rw-r--r--src/audio/play_queue.rs15
-rw-r--r--src/audio/ytdl.rs89
-rw-r--r--src/commands/meme.rs29
-rw-r--r--src/main.rs7
6 files changed, 102 insertions, 40 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 6025765..40a4324 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -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)",
diff --git a/Cargo.toml b/Cargo.toml
index 83a4f3f..83dad90 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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,