feat: add ability to choose video codec
This commit is contained in:
parent
8f3c7e4052
commit
299ad0c9dc
2 changed files with 61 additions and 16 deletions
|
@ -39,6 +39,23 @@ pub struct VideoMeta {
|
||||||
pub duration_us: usize,
|
pub duration_us: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Clone, Copy)]
|
||||||
|
pub enum Codec {
|
||||||
|
AV1,
|
||||||
|
#[default]
|
||||||
|
VP9,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<String> for Codec {
|
||||||
|
fn from(value: String) -> Self {
|
||||||
|
match value.as_str() {
|
||||||
|
"AV1" => Self::AV1,
|
||||||
|
"VP9" => Self::VP9,
|
||||||
|
_ => Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn get_video_meta(path: &Path) -> Result<VideoMeta> {
|
pub async fn get_video_meta(path: &Path) -> Result<VideoMeta> {
|
||||||
let output = Command::new("ffprobe")
|
let output = Command::new("ffprobe")
|
||||||
.args([
|
.args([
|
||||||
|
@ -74,25 +91,26 @@ pub async fn get_video_meta(path: &Path) -> Result<VideoMeta> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn convert_video(path: &Path, sender: Sender<ProgressMessage>) -> Result<PathBuf> {
|
pub async fn convert_video(
|
||||||
|
path: &Path,
|
||||||
|
out_filename: Option<String>,
|
||||||
|
out_codec: Codec,
|
||||||
|
merge_tracks: bool,
|
||||||
|
sender: Sender<ProgressMessage>,
|
||||||
|
) -> Result<PathBuf> {
|
||||||
let out_path = PathBuf::from("/tmp/out.webm");
|
let out_path = PathBuf::from("/tmp/out.webm");
|
||||||
|
|
||||||
|
let codec_args: &[&str] = match out_codec {
|
||||||
|
Codec::AV1 => &["-c:v", "libsvtav1"],
|
||||||
|
Codec::VP9 => &["-c:v", "libvpx-vp9", "-row-mt", "1"],
|
||||||
|
};
|
||||||
|
|
||||||
let mut child = Command::new("ffmpeg")
|
let mut child = Command::new("ffmpeg")
|
||||||
.arg("-i")
|
.arg("-i")
|
||||||
.arg(path)
|
.arg(path)
|
||||||
.args([
|
.args(["-c:a", "libopus", "-b:a", "96k"])
|
||||||
"-c:a",
|
.args(codec_args)
|
||||||
"libopus",
|
.args(["-loglevel", "error", "-progress", "-", "-nostats"])
|
||||||
"-b:a",
|
|
||||||
"96k",
|
|
||||||
"-c:v",
|
|
||||||
"libsvtav1",
|
|
||||||
"-loglevel",
|
|
||||||
"error",
|
|
||||||
"-progress",
|
|
||||||
"-",
|
|
||||||
"-nostats",
|
|
||||||
])
|
|
||||||
.arg(&out_path)
|
.arg(&out_path)
|
||||||
.stdout(Stdio::piped())
|
.stdout(Stdio::piped())
|
||||||
.spawn()?;
|
.spawn()?;
|
||||||
|
|
31
src/main.rs
31
src/main.rs
|
@ -44,6 +44,7 @@ impl Config {
|
||||||
enum Message {
|
enum Message {
|
||||||
OpenFilePicker,
|
OpenFilePicker,
|
||||||
SetOutFilename(String),
|
SetOutFilename(String),
|
||||||
|
SetOutCodec(String),
|
||||||
SetMergeTracks(bool),
|
SetMergeTracks(bool),
|
||||||
SetFolder(ZiplineFolder),
|
SetFolder(ZiplineFolder),
|
||||||
StartTheProcess,
|
StartTheProcess,
|
||||||
|
@ -83,6 +84,7 @@ struct UploadInfo {
|
||||||
config: Config,
|
config: Config,
|
||||||
video_path: Option<PathBuf>,
|
video_path: Option<PathBuf>,
|
||||||
out_filename: Option<String>,
|
out_filename: Option<String>,
|
||||||
|
out_codec: ffmpeg::Codec,
|
||||||
merge_tracks: bool,
|
merge_tracks: bool,
|
||||||
folder: Option<ZiplineFolder>,
|
folder: Option<ZiplineFolder>,
|
||||||
}
|
}
|
||||||
|
@ -94,6 +96,7 @@ struct Tyrolienne {
|
||||||
total: usize,
|
total: usize,
|
||||||
video_path: Option<PathBuf>,
|
video_path: Option<PathBuf>,
|
||||||
out_filename: Option<String>,
|
out_filename: Option<String>,
|
||||||
|
out_codec: ffmpeg::Codec,
|
||||||
merge_tracks: bool,
|
merge_tracks: bool,
|
||||||
folder: Option<ZiplineFolder>,
|
folder: Option<ZiplineFolder>,
|
||||||
dialog: Controller<Dialog>,
|
dialog: Controller<Dialog>,
|
||||||
|
@ -112,6 +115,7 @@ impl Tyrolienne {
|
||||||
UploadInfo {
|
UploadInfo {
|
||||||
config: self.config.clone(),
|
config: self.config.clone(),
|
||||||
out_filename: self.out_filename.clone(),
|
out_filename: self.out_filename.clone(),
|
||||||
|
out_codec: self.out_codec,
|
||||||
merge_tracks: self.merge_tracks,
|
merge_tracks: self.merge_tracks,
|
||||||
video_path: self.video_path.clone(),
|
video_path: self.video_path.clone(),
|
||||||
folder: self.folder.clone(),
|
folder: self.folder.clone(),
|
||||||
|
@ -170,6 +174,20 @@ impl AsyncComponent for Tyrolienne {
|
||||||
connect_changed[sender] => move |e| sender.input(Message::SetOutFilename(e.text().into())),
|
connect_changed[sender] => move |e| sender.input(Message::SetOutFilename(e.text().into())),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
adw::ComboRow {
|
||||||
|
set_title: "Output video codec",
|
||||||
|
set_model: Some(>k::StringList::new(&["VP9", "AV1"])),
|
||||||
|
connect_selected_item_notify[sender] => move |r| {
|
||||||
|
println!("{:?}", r.selected_item());
|
||||||
|
if let Some(item) = r
|
||||||
|
.selected_item()
|
||||||
|
.and_then(|i| i.downcast::<gtk::StringObject>().ok())
|
||||||
|
{
|
||||||
|
sender.input(Message::SetOutCodec(item.into()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
adw::SwitchRow {
|
adw::SwitchRow {
|
||||||
set_title: "Merge audio tracks",
|
set_title: "Merge audio tracks",
|
||||||
set_active: true,
|
set_active: true,
|
||||||
|
@ -180,7 +198,7 @@ impl AsyncComponent for Tyrolienne {
|
||||||
set_title: "Zipline folder",
|
set_title: "Zipline folder",
|
||||||
set_model: Some(&folder_store),
|
set_model: Some(&folder_store),
|
||||||
set_expression: Some(&folder_expression),
|
set_expression: Some(&folder_expression),
|
||||||
connect_activated[sender] => move |r| {
|
connect_selected_item_notify[sender] => move |r| {
|
||||||
if let Some(item) = r
|
if let Some(item) = r
|
||||||
.selected_item()
|
.selected_item()
|
||||||
.and_then(|i| i.downcast::<GtkZiplineFolder>().ok())
|
.and_then(|i| i.downcast::<GtkZiplineFolder>().ok())
|
||||||
|
@ -230,6 +248,7 @@ impl AsyncComponent for Tyrolienne {
|
||||||
total: 1,
|
total: 1,
|
||||||
video_path: None,
|
video_path: None,
|
||||||
out_filename: None,
|
out_filename: None,
|
||||||
|
out_codec: ffmpeg::Codec::VP9,
|
||||||
merge_tracks: true,
|
merge_tracks: true,
|
||||||
folder: None,
|
folder: None,
|
||||||
dialog: Dialog::builder()
|
dialog: Dialog::builder()
|
||||||
|
@ -293,6 +312,7 @@ impl AsyncComponent for Tyrolienne {
|
||||||
Message::SetOutFilename(name) => {
|
Message::SetOutFilename(name) => {
|
||||||
self.out_filename = if name.is_empty() { None } else { Some(name) }
|
self.out_filename = if name.is_empty() { None } else { Some(name) }
|
||||||
}
|
}
|
||||||
|
Message::SetOutCodec(codec) => self.out_codec = codec.into(),
|
||||||
Message::SetMergeTracks(m) => self.merge_tracks = m,
|
Message::SetMergeTracks(m) => self.merge_tracks = m,
|
||||||
Message::SetFolder(folder) => {
|
Message::SetFolder(folder) => {
|
||||||
self.folder = (folder.id != GtkZiplineFolder::NONE_ID).then_some(folder)
|
self.folder = (folder.id != GtkZiplineFolder::NONE_ID).then_some(folder)
|
||||||
|
@ -406,7 +426,14 @@ async fn the_process(app: UploadInfo, sender: &Sender<ProgressMessage>) -> Resul
|
||||||
let video_meta = ffmpeg::get_video_meta(video_path).await?;
|
let video_meta = ffmpeg::get_video_meta(video_path).await?;
|
||||||
sender.emit(ProgressMessage::SetTotal(video_meta.duration_us));
|
sender.emit(ProgressMessage::SetTotal(video_meta.duration_us));
|
||||||
|
|
||||||
let out_path = ffmpeg::convert_video(video_path, sender.clone()).await?;
|
let out_path = ffmpeg::convert_video(
|
||||||
|
video_path,
|
||||||
|
app.out_filename,
|
||||||
|
app.out_codec,
|
||||||
|
app.merge_tracks,
|
||||||
|
sender.clone(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
sender.emit(ProgressMessage::SetStep(Step::Uploading));
|
sender.emit(ProgressMessage::SetStep(Step::Uploading));
|
||||||
if let Some(folder) = app.folder.as_ref() {
|
if let Some(folder) = app.folder.as_ref() {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue