2021-09-14 01:22:42 +00:00
|
|
|
use crate::{
|
|
|
|
error::{Error, UploadError},
|
2021-10-21 00:00:41 +00:00
|
|
|
process::Process,
|
2021-09-14 01:22:42 +00:00
|
|
|
};
|
2021-09-04 00:53:53 +00:00
|
|
|
use actix_web::web::Bytes;
|
|
|
|
use tokio::{io::AsyncRead, process::Command};
|
2021-09-14 01:22:42 +00:00
|
|
|
use tracing::instrument;
|
2021-08-26 02:46:11 +00:00
|
|
|
|
2021-08-31 02:19:47 +00:00
|
|
|
pub(crate) enum InputFormat {
|
|
|
|
Gif,
|
|
|
|
Mp4,
|
|
|
|
}
|
|
|
|
|
2021-09-14 01:22:42 +00:00
|
|
|
#[derive(Debug)]
|
2021-08-28 22:15:14 +00:00
|
|
|
pub(crate) enum ThumbnailFormat {
|
|
|
|
Jpeg,
|
2021-08-31 02:19:47 +00:00
|
|
|
// Webp,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl InputFormat {
|
|
|
|
fn as_format(&self) -> &'static str {
|
|
|
|
match self {
|
|
|
|
InputFormat::Gif => "gif_pipe",
|
|
|
|
InputFormat::Mp4 => "mp4",
|
|
|
|
}
|
|
|
|
}
|
2021-08-28 22:15:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ThumbnailFormat {
|
|
|
|
fn as_codec(&self) -> &'static str {
|
|
|
|
match self {
|
|
|
|
ThumbnailFormat::Jpeg => "mjpeg",
|
2021-08-31 02:19:47 +00:00
|
|
|
// ThumbnailFormat::Webp => "webp",
|
2021-08-28 22:15:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn as_format(&self) -> &'static str {
|
|
|
|
match self {
|
|
|
|
ThumbnailFormat::Jpeg => "singlejpeg",
|
2021-08-31 02:19:47 +00:00
|
|
|
// ThumbnailFormat::Webp => "webp",
|
2021-08-28 22:15:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-04 00:53:53 +00:00
|
|
|
pub(crate) fn to_mp4_bytes(
|
|
|
|
input: Bytes,
|
|
|
|
input_format: InputFormat,
|
|
|
|
) -> std::io::Result<impl AsyncRead + Unpin> {
|
2021-09-14 01:22:42 +00:00
|
|
|
let process = Process::run(
|
|
|
|
"ffmpeg",
|
|
|
|
&[
|
|
|
|
"-f",
|
|
|
|
input_format.as_format(),
|
|
|
|
"-i",
|
|
|
|
"pipe:",
|
|
|
|
"-movflags",
|
|
|
|
"faststart+frag_keyframe+empty_moov",
|
|
|
|
"-pix_fmt",
|
|
|
|
"yuv420p",
|
|
|
|
"-vf",
|
|
|
|
"scale=trunc(iw/2)*2:trunc(ih/2)*2",
|
|
|
|
"-an",
|
|
|
|
"-codec",
|
|
|
|
"h264",
|
|
|
|
"-f",
|
|
|
|
"mp4",
|
|
|
|
"pipe:",
|
|
|
|
],
|
|
|
|
)?;
|
2021-09-04 00:53:53 +00:00
|
|
|
|
|
|
|
Ok(process.bytes_read(input).unwrap())
|
|
|
|
}
|
|
|
|
|
2021-09-14 01:22:42 +00:00
|
|
|
#[instrument(name = "Create video thumbnail", skip(from, to))]
|
2021-08-28 22:15:14 +00:00
|
|
|
pub(crate) async fn thumbnail<P1, P2>(
|
|
|
|
from: P1,
|
|
|
|
to: P2,
|
|
|
|
format: ThumbnailFormat,
|
2021-09-14 01:22:42 +00:00
|
|
|
) -> Result<(), Error>
|
2021-08-26 02:46:11 +00:00
|
|
|
where
|
|
|
|
P1: AsRef<std::path::Path>,
|
|
|
|
P2: AsRef<std::path::Path>,
|
|
|
|
{
|
2021-09-14 01:22:42 +00:00
|
|
|
let command = "ffmpeg";
|
|
|
|
let first_arg = "-i";
|
|
|
|
let args = [
|
|
|
|
"-vframes",
|
|
|
|
"1",
|
|
|
|
"-codec",
|
|
|
|
format.as_codec(),
|
|
|
|
"-f",
|
|
|
|
format.as_format(),
|
|
|
|
];
|
|
|
|
|
|
|
|
tracing::info!(
|
|
|
|
"Spawning command: {} {} {:?} {:?} {:?}",
|
|
|
|
command,
|
|
|
|
first_arg,
|
|
|
|
from.as_ref(),
|
|
|
|
args,
|
|
|
|
to.as_ref()
|
|
|
|
);
|
|
|
|
|
|
|
|
let mut child = Command::new(command)
|
|
|
|
.arg(first_arg)
|
|
|
|
.arg(from.as_ref())
|
|
|
|
.args(args)
|
|
|
|
.arg(to.as_ref())
|
2021-08-26 02:46:11 +00:00
|
|
|
.spawn()?;
|
|
|
|
|
|
|
|
let status = child.wait().await?;
|
|
|
|
|
|
|
|
if !status.success() {
|
2021-09-14 01:22:42 +00:00
|
|
|
return Err(UploadError::Status.into());
|
2021-08-26 02:46:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|