aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/audio/play_queue.rs69
-rw-r--r--src/audio/ytdl.rs19
-rw-r--r--src/commands/meme.rs88
-rw-r--r--src/main.rs3
4 files changed, 98 insertions, 81 deletions
diff --git a/src/audio/play_queue.rs b/src/audio/play_queue.rs
index 0ddcb93..fcbb4f9 100644
--- a/src/audio/play_queue.rs
+++ b/src/audio/play_queue.rs
@@ -1,16 +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 opus::{
- Channels,
- Decoder as OpusDecoder,
-};
use serenity::{
prelude::*,
voice,
@@ -62,7 +59,6 @@ impl PlayQueue {
thread::spawn(move || {
let queue_lck = Arc::clone(&queue);
let voice_manager = voice_manager;
- let mut opus_dec = OpusDecoder::new(48000, Channels::Stereo).unwrap();
loop {
thread::sleep(Duration::from_millis(250));
@@ -98,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,
@@ -111,24 +107,53 @@ impl PlayQueue {
}
}
},
- Right(ref v) => {
- let mut out = Vec::new();
+ Right(ref vec) => {
+ 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());
- let mut acc: usize = 0;
- while acc < v.len() {
- dbg!(acc);
- let mut wr = vec![0i16; 960];
- match opus_dec.decode(&v[acc..], &mut wr, true) {
- Ok(len) => acc += len,
+ for line in stderr.lines() {
+ let line = line.unwrap();
- Err(e) => {
- info!("decoding opus packet: {}", e);
- break;
- },
+ 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());
- voice::pcm(true, Cursor::new(out))
+ 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 e4fbde8..913be00 100644
--- a/src/commands/meme.rs
+++ b/src/commands/meme.rs
@@ -1,21 +1,15 @@
use std::{
- io::{BufReader, Read},
+ io::Read,
+ process::{
+ Command,
+ Stdio,
+ },
sync::RwLock,
};
-use byteorder::{
- ByteOrder,
- NativeEndian,
-};
use diesel::PgConnection;
use failure::Error;
use lazy_static::lazy_static;
-use opus::{
- Application,
- Bitrate,
- Channels,
- Encoder as OpusEncoder,
-};
use rand::{Rng, thread_rng};
use serenity::{
builder::CreateMessage,
@@ -31,9 +25,7 @@ use audio::ytdl_url;
use crate::{
audio::{
CtxExt,
- ffmpeg_dl,
parse_times,
- Pcm,
PlayArgs,
PlayQueue,
},
@@ -160,7 +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 = BufReader::new(ffmpeg_dl::<Pcm>(&youtube_url, start, end, None)?);
+
+ 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(" "),
@@ -179,44 +202,15 @@ pub fn addaudiomeme(_: &mut Context, msg: &Message, mut args: Args) -> Result<()
})
.ok();
- let i16_data: Vec<i16> = {
- let mut audio_data = Vec::new();
- let bytes = audio_reader.read_to_end(&mut audio_data)?;
-
- let mut i16_data = Vec::with_capacity(bytes / 2);
- i16_data.resize(bytes / 2, 0);
-
- NativeEndian::read_i16_into(&audio_data, &mut i16_data);
-
- i16_data
- };
-
- let mut enc = OpusEncoder::new(48000, Channels::Stereo, Application::Audio)?;
- enc.set_vbr(false)?;
- enc.set_bitrate(Bitrate::Bits(96 * 1024))?;
-
- let mut out = Vec::new();
- for elem in i16_data.chunks(960) {
- let elem = if elem.len() == 960 {
- elem
- } else if elem.len() > 480 {
- &elem[..480]
- } else if elem.len() > 240 {
- &elem[..240]
- } else {
- continue
- };
-
- let mut encoded = enc.encode_vec(elem, 1200)?;
- out.append(&mut encoded);
- }
+ let mut audio_data = Vec::new();
+ let bytes = audio_reader.read_to_end(&mut audio_data)?;
- if out.len() == 0 {
+ if bytes == 0 {
debug!("read 0 bytes from audio reader");
return send(msg.channel_id, "🔇🔇🔇🔕🔕🔕🔕🔕🔇🔕🔕🔇🔕🔕📣📢📣📢📣", msg.tts);
}
- let audio_id = Audio::create(&conn, out, msg.author.id.0)?;
+ let audio_id = Audio::create(&conn, audio_data, msg.author.id.0)?;
NewMeme {
title,
diff --git a/src/main.rs b/src/main.rs
index 3a5aeac..5637ecd 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,6 +1,6 @@
#![feature(impl_trait_in_bindings)]
+#![feature(try_trait)]
-extern crate byteorder;
extern crate chrono;
#[cfg(feature = "diesel")]
#[macro_use] extern crate diesel;
@@ -12,7 +12,6 @@ extern crate fern;
#[cfg_attr(test, macro_use)] extern crate itertools;
#[macro_use] extern crate lazy_static;
#[macro_use] extern crate log;
-extern crate opus;
extern crate rand;
extern crate regex;
extern crate serde_json;