diff options
| author | Nathan Perry <avaglir@gmail.com> | 2019-02-18 00:39:27 -0500 |
|---|---|---|
| committer | Nathan Perry <avaglir@gmail.com> | 2019-02-18 00:39:27 -0500 |
| commit | e589e6ee73bcdf25e1e73640792aa95ab92667f2 (patch) | |
| tree | 0a2b270d9de4b97df22f884f1a90234a824a1212 | |
| parent | 3eecbaf1ff02122506ee8ee8c65e02ff1325aae0 (diff) | |
encode/decode audio memes with ffmpeg to improve storage
| -rw-r--r-- | Cargo.lock | 1 | ||||
| -rw-r--r-- | Cargo.toml | 1 | ||||
| -rw-r--r-- | src/audio/play_queue.rs | 55 | ||||
| -rw-r--r-- | src/audio/ytdl.rs | 19 | ||||
| -rw-r--r-- | src/commands/meme.rs | 49 | ||||
| -rw-r--r-- | src/main.rs | 2 |
6 files changed, 97 insertions, 30 deletions
@@ -1726,7 +1726,6 @@ 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,7 +28,6 @@ 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 119b8c5..fcbb4f9 100644 --- a/src/audio/play_queue.rs +++ b/src/audio/play_queue.rs @@ -1,13 +1,13 @@ use std::{ collections::VecDeque, - io::Cursor, + io::{self, BufRead, BufReader, Cursor}, + process, sync::{Arc, RwLock}, thread, time::Duration, }; use either::{Left, Right}; -use flate2::bufread::DeflateDecoder; use serenity::{ prelude::*, voice, @@ -94,9 +94,9 @@ impl PlayQueue { } let mut queue = queue_lck.write().unwrap(); - let item = queue.queue.pop_front().unwrap(); + let mut item = queue.queue.pop_front().unwrap(); - let src = match item.data { + let src = match &mut item.data { Left(ref url) => { match ytdl(url, item.start, item.end) { Ok(src) => src, @@ -108,7 +108,52 @@ impl PlayQueue { } }, Right(ref vec) => { - voice::pcm(true, DeflateDecoder::new(Cursor::new(vec.clone()))) + let mut transcoder = process::Command::new("ffmpeg") + .args(&[ + "-format", "opus", + "-i", "pipe:0", + "-acodec", "pcm_s16le", + "-f", "s16le", + "-" + ]) + .stdin(process::Stdio::piped()) + .stdout(process::Stdio::piped()) + .stderr(process::Stdio::piped()) + .spawn() + .expect("unable to call ffmpeg"); + + let process::Child { + stdin, + stderr, + stdout, + .. + } = transcoder; + + thread::spawn(move || { + let stderr = BufReader::new(stderr.unwrap()); + + for line in stderr.lines() { + let line = line.unwrap(); + + trace!("{}", line); + } + }); + + let v = vec.clone(); + thread::spawn(move || { + if let Err(e) = io::copy(&mut Cursor::new(v), &mut stdin.unwrap()) { + use std::io::ErrorKind; + if e.kind() == ErrorKind::BrokenPipe { + debug!("ffmpeg closed unexpectedly"); + } else { + error!("copying audio to ffmpeg {}", e); + } + } + }); + + let result = voice::pcm(true, stdout.unwrap()); + + result } }; diff --git a/src/audio/ytdl.rs b/src/audio/ytdl.rs index e88d8f8..8239683 100644 --- a/src/audio/ytdl.rs +++ b/src/audio/ytdl.rs @@ -45,6 +45,8 @@ pub(crate) trait CodecInfo { } pub(crate) struct Pcm {} + +#[allow(dead_code)] pub(crate) struct Opus {} impl CodecInfo for Pcm { @@ -66,12 +68,9 @@ impl CodecInfo for Opus { fn ffmpeg_opts() -> &'static[&'static str] { lazy_static! { static ref OPTS: Vec<&'static str> = vec! [ -// "-f", "s16le", + "-f", "opus", "-acodec", "libopus", - "-sample_fmt", "s16", - "-vbr", "off", -// "-b:a 96k", -// "-vn", + "-b:a 96k", ]; } @@ -129,6 +128,11 @@ pub(crate) fn ffmpeg_dl<T: CodecInfo>(uri: &str, start: Option<Duration>, end: O .map(|s| s.to_owned()) .collect::<Vec<_>>(); + 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)); + } + let codec_opts = T::ffmpeg_opts().into_iter().map(|&s| s.to_owned()).collect::<Vec<_>>(); opts.extend(codec_opts); @@ -137,11 +141,6 @@ pub(crate) fn ffmpeg_dl<T: CodecInfo>(uri: &str, start: Option<Duration>, end: O 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(" ")); diff --git a/src/commands/meme.rs b/src/commands/meme.rs index 160a652..913be00 100644 --- a/src/commands/meme.rs +++ b/src/commands/meme.rs @@ -1,14 +1,14 @@ use std::{ - io::{BufReader, Read}, + io::Read, + process::{ + Command, + Stdio, + }, sync::RwLock, }; use diesel::PgConnection; use failure::Error; -use flate2::{ - bufread::DeflateEncoder, - Compression, -}; use lazy_static::lazy_static; use rand::{Rng, thread_rng}; use serenity::{ @@ -25,10 +25,7 @@ use audio::ytdl_url; use crate::{ audio::{ CtxExt, - ffmpeg_dl, - Opus, parse_times, - Pcm, PlayArgs, PlayQueue, }, @@ -155,10 +152,38 @@ pub fn addaudiomeme(_: &mut Context, msg: &Message, mut args: Args) -> Result<() let (start, end) = parse_times(opts); 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 duration_opts = if let Some(e) = end { + vec! [ + "-ss".to_owned(), start.map_or_else( + || "00:00:00".to_owned(), + |s| format!("{:02}:{:02}:{:02}", s.num_hours(), s.num_minutes() % 60, s.num_seconds() % 60) + ), + + "-to".to_owned(), format!("{:02}:{:02}:{:02}", e.num_hours(), e.num_minutes() % 60, e.num_seconds() % 60), + ] + } else { + vec! [] + }; + + let ffmpeg_command = Command::new("ffmpeg") + .arg("-i") + .arg(youtube_url) + .args(duration_opts) + .args(&[ + "-ac", "2", + "-ar", "48000", + "-f", "opus", + "-acodec", "libopus", + "-b:a", "96k", + "-", + ]) + .stdout(Stdio::piped()) + .stderr(Stdio::null()) + .stdin(Stdio::null()) + .spawn()?; + + let mut audio_reader = ffmpeg_command.stdout.unwrap(); let text = match args.multiple_quoted::<String>() { Ok(text) => text.join(" "), diff --git a/src/main.rs b/src/main.rs index 5162015..5637ecd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ #![feature(impl_trait_in_bindings)] +#![feature(try_trait)] extern crate chrono; #[cfg(feature = "diesel")] @@ -8,7 +9,6 @@ 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; |
