Skip to content

Commit ed746da

Browse files
committed
fix: work around many conversion failures
thanks to the community for submitting their files, this should make conversions more reliable now!
1 parent 8abb761 commit ed746da

File tree

3 files changed

+143
-5
lines changed

3 files changed

+143
-5
lines changed

src/converter/format.rs

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ impl Conversion {
7878
gpu: &ConverterGPU,
7979
bitrate: u64,
8080
fps: u32,
81+
job: &super::job::Job,
8182
) -> anyhow::Result<Vec<String>> {
8283
let conversion_opts: Vec<String> = match self.to {
8384
ConverterFormat::MP4
@@ -95,14 +96,40 @@ impl Conversion {
9596
let encoder = self
9697
.accelerated_or_default_codec(gpu, &["h264"], "libx264")
9798
.await;
98-
vec![
99-
"-c:v".to_string(),
100-
encoder,
99+
100+
let mut args = vec!["-c:v".to_string(), encoder.clone()];
101+
102+
let (width, height) = job.resolution().await?;
103+
let is_4k = width >= 3840 || height >= 2160;
104+
let pix_fmt = job.pix_fmt().await?;
105+
106+
// convert to 8bit if 10bit (h264_nvenc does not support 10bit)
107+
// could probably use h265 instead?
108+
let is_10bit = pix_fmt.contains("10le") || pix_fmt.contains("10be");
109+
if is_10bit {
110+
args.extend(["-pix_fmt".to_string(), "yuv420p".to_string()]);
111+
}
112+
113+
if is_4k {
114+
args.extend(["-level:v".to_string(), "5.2".to_string()]);
115+
if fps > 120 {
116+
args.extend(["-r".to_string(), "120".to_string()]);
117+
}
118+
}
119+
120+
// scale to 160:-1 if width is less than 160
121+
if width < 160 {
122+
args.extend(["-vf".to_string(), "scale=160:-1".to_string()]);
123+
}
124+
125+
args.extend([
101126
"-c:a".to_string(),
102127
"aac".to_string(),
103128
"-strict".to_string(),
104129
"experimental".to_string(),
105-
]
130+
]);
131+
132+
args
106133
}
107134

108135
ConverterFormat::GIF => {
@@ -225,6 +252,8 @@ impl Conversion {
225252
"25".to_string(),
226253
"-block_size".to_string(),
227254
"882".to_string(),
255+
"-strict".to_string(),
256+
"-1".to_string(),
228257
],
229258
};
230259

src/converter/job.rs

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,115 @@ impl Job {
167167
let (bitrate, fps) = (self.bitrate().await?, self.fps().await?);
168168
Ok((bitrate, fps))
169169
}
170+
171+
pub async fn resolution(&self) -> anyhow::Result<(u32, u32)> {
172+
let path = format!("input/{}.{}", self.id, self.from);
173+
174+
let output = Command::new("ffprobe")
175+
.args([
176+
"-v",
177+
"error",
178+
"-select_streams",
179+
"v:0",
180+
"-show_entries",
181+
"stream=width,height",
182+
"-of",
183+
"csv=s=x:p=0",
184+
&path,
185+
])
186+
.output()
187+
.await?;
188+
189+
let res_str = String::from_utf8(output.stdout)?;
190+
let mut parts = res_str.trim().split('x');
191+
let width = parts
192+
.next()
193+
.ok_or_else(|| anyhow::anyhow!("failed to get width"))?
194+
.parse::<u32>()?;
195+
let height = parts
196+
.next()
197+
.ok_or_else(|| anyhow::anyhow!("failed to get height"))?
198+
.parse::<u32>()?;
199+
200+
Ok((width, height))
201+
}
202+
203+
pub async fn pix_fmt(&self) -> anyhow::Result<String> {
204+
let path = format!("input/{}.{}", self.id, self.from);
205+
206+
let output = Command::new("ffprobe")
207+
.args([
208+
"-v",
209+
"error",
210+
"-select_streams",
211+
"v:0",
212+
"-show_entries",
213+
"stream=pix_fmt",
214+
"-of",
215+
"default=nokey=1:noprint_wrappers=1",
216+
&path,
217+
])
218+
.output()
219+
.await?;
220+
221+
let pix_fmt = String::from_utf8(output.stdout)?
222+
.lines()
223+
.next()
224+
.ok_or_else(|| anyhow::anyhow!("failed to get pixel format"))?
225+
.to_string();
226+
227+
Ok(pix_fmt)
228+
}
229+
230+
pub async fn codecs(&self) -> anyhow::Result<(String, String)> {
231+
let path = format!("input/{}.{}", self.id, self.from);
232+
233+
// Video codec
234+
let output = Command::new("ffprobe")
235+
.args([
236+
"-v",
237+
"error",
238+
"-select_streams",
239+
"v:0",
240+
"-show_entries",
241+
"stream=codec_name",
242+
"-of",
243+
"default=nokey=1:noprint_wrappers=1",
244+
&path,
245+
])
246+
.output()
247+
.await?;
248+
249+
let video_codec = String::from_utf8(output.stdout)?
250+
.lines()
251+
.next()
252+
.unwrap_or("none")
253+
.to_string();
254+
255+
// Audio codec
256+
let output = Command::new("ffprobe")
257+
.args([
258+
"-v",
259+
"error",
260+
"-select_streams",
261+
"a:0",
262+
"-show_entries",
263+
"stream=codec_name",
264+
"-of",
265+
"default=nokey=1:noprint_wrappers=1",
266+
&path,
267+
])
268+
.output()
269+
.await?;
270+
271+
let audio_codec = String::from_utf8(output.stdout)?
272+
.lines()
273+
.next()
274+
.unwrap_or("none")
275+
.to_string();
276+
277+
Ok((video_codec, audio_codec))
278+
}
170279
}
171280

172281
#[derive(Debug, Serialize, Deserialize)]

src/converter/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ impl Converter {
5353
let (bitrate, fps) = job.bitrate_and_fps().await?;
5454
let args = self
5555
.conversion
56-
.to_args(&self.speed, gpu, bitrate, fps)
56+
.to_args(&self.speed, gpu, bitrate, fps, job)
5757
.await?;
5858
let args = args.iter().map(|s| s.as_str()).collect::<Vec<&str>>();
5959
let args = args.as_slice();

0 commit comments

Comments
 (0)