Improve FFmpeg video thumbnail fallback handling
This commit is contained in:
parent
26fa3a5301
commit
2e6518bf4b
@ -26,6 +26,8 @@ IMAGE_EXTENSIONS = {
|
||||
".webp",
|
||||
}
|
||||
VIDEO_EXTENSIONS = {
|
||||
".3gp",
|
||||
".avi",
|
||||
".m4v",
|
||||
".mkv",
|
||||
".mov",
|
||||
@ -97,19 +99,17 @@ def _render_video_thumbnail(attachment: TaskAttachment) -> bytes:
|
||||
output_path = output_file.name
|
||||
|
||||
filter_value = "scale=640:280:force_original_aspect_ratio=increase,crop=640:280"
|
||||
command = [
|
||||
ffmpeg,
|
||||
"-hide_banner",
|
||||
"-loglevel",
|
||||
"error",
|
||||
"-ss",
|
||||
"0.5",
|
||||
"-i",
|
||||
input_path,
|
||||
command_prefix = [ffmpeg, "-hide_banner", "-loglevel", "error"]
|
||||
command_suffix = [
|
||||
"-map",
|
||||
"0:v:0",
|
||||
"-frames:v",
|
||||
"1",
|
||||
"-vf",
|
||||
filter_value,
|
||||
"-an",
|
||||
"-sn",
|
||||
"-dn",
|
||||
"-c:v",
|
||||
"libwebp",
|
||||
"-quality",
|
||||
@ -117,22 +117,33 @@ def _render_video_thumbnail(attachment: TaskAttachment) -> bytes:
|
||||
"-y",
|
||||
output_path,
|
||||
]
|
||||
result = subprocess.run(
|
||||
command,
|
||||
capture_output=True,
|
||||
check=False,
|
||||
timeout=30,
|
||||
)
|
||||
if result.returncode != 0:
|
||||
command[5] = "0"
|
||||
result = subprocess.run(
|
||||
command,
|
||||
capture_output=True,
|
||||
check=False,
|
||||
timeout=30,
|
||||
)
|
||||
if result.returncode != 0:
|
||||
raise RuntimeError(result.stderr.decode("utf-8", errors="replace"))
|
||||
commands = [
|
||||
# Fast input seek works for ordinary MP4/WebM files.
|
||||
[*command_prefix, "-ss", "0.5", "-i", input_path, *command_suffix],
|
||||
# Accurate output seek handles fragmented MP4/MOV screen recordings
|
||||
# whose first keyframe cannot be reached by input seeking.
|
||||
[*command_prefix, "-i", input_path, "-ss", "0.5", *command_suffix],
|
||||
# Some recordings start on a non-zero timeline. In that case use the
|
||||
# first frame FFmpeg can decode instead of requiring a timestamp.
|
||||
[*command_prefix, "-i", input_path, *command_suffix],
|
||||
]
|
||||
last_error = "FFmpeg did not produce a video thumbnail."
|
||||
for command in commands:
|
||||
try:
|
||||
result = subprocess.run(
|
||||
command,
|
||||
capture_output=True,
|
||||
check=False,
|
||||
timeout=30,
|
||||
)
|
||||
except subprocess.TimeoutExpired as error:
|
||||
last_error = str(error)
|
||||
continue
|
||||
if result.returncode == 0 and Path(output_path).stat().st_size > 0:
|
||||
break
|
||||
last_error = result.stderr.decode("utf-8", errors="replace")
|
||||
else:
|
||||
raise RuntimeError(last_error)
|
||||
return Path(output_path).read_bytes()
|
||||
finally:
|
||||
if temporary_input and input_path:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user