Fix progress reporting and desktop responsiveness during encode
The progress bar barely moved and the window showed "not responding" during long encodes: ffmpeg's carriage-return stats line flushes rarely over a pipe, and the transcode saturated the CPU. - Drive progress from ffmpeg's structured `-progress pipe:1` output (`-stats_period 0.5 -nostats`) and parse out_time/progress in process_stdout, giving steady frequent updates (verified ~2/sec via the live executor pipe vs ~1 per whole encode before). - Run the encoder under `nice -n 10` so the compositor keeps a CPU slice (avconv gets the nice prefix too). Also round pad/crop targets to even dimensions and use positive crop offsets, fixing an odd-height (e.g. 1079) MPEG-2 error surfaced when burning in subtitles on a 1080p source. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
6cddd72792
commit
7f574897c7
2 changed files with 79 additions and 13 deletions
|
|
@ -148,6 +148,10 @@ class avconv(devedeng.avbase.avbase):
|
||||||
else:
|
else:
|
||||||
self.final_length = video_length
|
self.final_length = video_length
|
||||||
self.command_var = []
|
self.command_var = []
|
||||||
|
# lower priority so the desktop stays responsive during the transcode
|
||||||
|
self.command_var.append("nice")
|
||||||
|
self.command_var.append("-n")
|
||||||
|
self.command_var.append("10")
|
||||||
self.command_var.append("avconv")
|
self.command_var.append("avconv")
|
||||||
if start_offset and start_offset > 0:
|
if start_offset and start_offset > 0:
|
||||||
self.command_var.append("-ss")
|
self.command_var.append("-ss")
|
||||||
|
|
@ -228,16 +232,19 @@ class avconv(devedeng.avbase.avbase):
|
||||||
if (file_project.width_midle != file_project.original_width) or (file_project.height_midle != file_project.original_height):
|
if (file_project.width_midle != file_project.original_width) or (file_project.height_midle != file_project.original_height):
|
||||||
if (cmd_line != ""):
|
if (cmd_line != ""):
|
||||||
cmd_line += ",fifo,"
|
cmd_line += ",fifo,"
|
||||||
x = int((file_project.width_midle -
|
# MPEG-2 / yuv420p needs even dimensions; round to even so the
|
||||||
file_project.original_width) / 2)
|
# aspect math can't produce an odd pad/crop target (e.g. 1079).
|
||||||
y = int((file_project.height_midle -
|
mid_w = file_project.width_midle - (file_project.width_midle % 2)
|
||||||
file_project.original_height) / 2)
|
mid_h = file_project.height_midle - (file_project.height_midle % 2)
|
||||||
|
x = int((mid_w - file_project.original_width) / 2)
|
||||||
|
y = int((mid_h - file_project.original_height) / 2)
|
||||||
if (x > 0) or (y > 0):
|
if (x > 0) or (y > 0):
|
||||||
cmd_line += "pad=" + str(file_project.width_midle) + ":" + str(
|
cmd_line += "pad=" + str(mid_w) + ":" + str(
|
||||||
file_project.height_midle) + ":" + str(x) + ":" + str(y) + ":0x000000"
|
mid_h) + ":" + str(x) + ":" + str(y) + ":0x000000"
|
||||||
else:
|
else:
|
||||||
cmd_line += "crop=" + str(file_project.width_midle) + ":" + str(
|
# crop x/y are the (positive) top-left offset into the source
|
||||||
file_project.height_midle) + ":" + str(x) + ":" + str(y)
|
cmd_line += "crop=" + str(mid_w) + ":" + str(
|
||||||
|
mid_h) + ":" + str(abs(x)) + ":" + str(abs(y))
|
||||||
|
|
||||||
if (file_project.width_final != file_project.width_midle) or (file_project.height_final != file_project.height_midle):
|
if (file_project.width_final != file_project.width_midle) or (file_project.height_final != file_project.height_midle):
|
||||||
if (cmd_line != ""):
|
if (cmd_line != ""):
|
||||||
|
|
|
||||||
|
|
@ -176,7 +176,20 @@ class ffmpeg(devedeng.executor.executor):
|
||||||
else:
|
else:
|
||||||
self.final_length = video_length
|
self.final_length = video_length
|
||||||
self.command_var = []
|
self.command_var = []
|
||||||
|
# run the encoder at a lower priority so the desktop/GUI stays
|
||||||
|
# responsive while a heavy transcode saturates the CPU
|
||||||
|
self.command_var.append("nice")
|
||||||
|
self.command_var.append("-n")
|
||||||
|
self.command_var.append("10")
|
||||||
self.command_var.append("ffmpeg")
|
self.command_var.append("ffmpeg")
|
||||||
|
# emit structured, steadily-paced progress on stdout (pipe:1) instead of
|
||||||
|
# the carriage-return stats line, which barely updates over a pipe; this
|
||||||
|
# is what keeps the progress bar moving (see process_stdout)
|
||||||
|
self.command_var.append("-progress")
|
||||||
|
self.command_var.append("pipe:1")
|
||||||
|
self.command_var.append("-stats_period")
|
||||||
|
self.command_var.append("0.5")
|
||||||
|
self.command_var.append("-nostats")
|
||||||
# input seek (for splitting a long movie across discs); placed before
|
# input seek (for splitting a long movie across discs); placed before
|
||||||
# -i so ffmpeg seeks quickly and the output starts at offset 0
|
# -i so ffmpeg seeks quickly and the output starts at offset 0
|
||||||
if start_offset and start_offset > 0:
|
if start_offset and start_offset > 0:
|
||||||
|
|
@ -263,12 +276,18 @@ class ffmpeg(devedeng.executor.executor):
|
||||||
if (file_project.width_midle != file_project.original_width) or (file_project.height_midle != file_project.original_height):
|
if (file_project.width_midle != file_project.original_width) or (file_project.height_midle != file_project.original_height):
|
||||||
if (cmd_line != ""):
|
if (cmd_line != ""):
|
||||||
cmd_line += self._filter_sep
|
cmd_line += self._filter_sep
|
||||||
x = int((file_project.width_midle - file_project.original_width) / 2)
|
# MPEG-2 / yuv420p needs even dimensions; the aspect math can
|
||||||
y = int((file_project.height_midle - file_project.original_height) / 2)
|
# yield an odd width/height (e.g. 1079), which makes ffmpeg
|
||||||
|
# error or corrupt a frame. Round the pad/crop target to even.
|
||||||
|
mid_w = file_project.width_midle - (file_project.width_midle % 2)
|
||||||
|
mid_h = file_project.height_midle - (file_project.height_midle % 2)
|
||||||
|
x = int((mid_w - file_project.original_width) / 2)
|
||||||
|
y = int((mid_h - file_project.original_height) / 2)
|
||||||
if (x > 0) or (y > 0):
|
if (x > 0) or (y > 0):
|
||||||
cmd_line += "pad=" + str(file_project.width_midle) + ":" + str(file_project.height_midle) + ":" + str(x) + ":" + str(y) + ":0x000000"
|
cmd_line += "pad=" + str(mid_w) + ":" + str(mid_h) + ":" + str(x) + ":" + str(y) + ":0x000000"
|
||||||
else:
|
else:
|
||||||
cmd_line += "crop=" + str(file_project.width_midle) + ":" + str(file_project.height_midle) + ":" + str(x) + ":" + str(y)
|
# crop x/y are the (positive) top-left offset into the source
|
||||||
|
cmd_line += "crop=" + str(mid_w) + ":" + str(mid_h) + ":" + str(abs(x)) + ":" + str(abs(y))
|
||||||
|
|
||||||
if (file_project.width_final != file_project.width_midle) or (file_project.height_final != file_project.height_midle):
|
if (file_project.width_final != file_project.width_midle) or (file_project.height_final != file_project.height_midle):
|
||||||
if (cmd_line != ""):
|
if (cmd_line != ""):
|
||||||
|
|
@ -706,9 +725,49 @@ class ffmpeg(devedeng.executor.executor):
|
||||||
return (final_path)
|
return (final_path)
|
||||||
|
|
||||||
def process_stdout(self, data):
|
def process_stdout(self, data):
|
||||||
|
# ffmpeg -progress emits newline-delimited key=value pairs on stdout,
|
||||||
|
# e.g. out_time=00:01:23.456 / out_time_ms=83456000 / progress=continue.
|
||||||
|
# This updates steadily over a pipe, unlike the carriage-return stats.
|
||||||
|
if self.progress_bar is None:
|
||||||
|
return
|
||||||
|
for line in data:
|
||||||
|
line = line.strip()
|
||||||
|
if line.startswith("out_time_ms=") or line.startswith("out_time_us="):
|
||||||
|
try:
|
||||||
|
micros = float(line.split("=", 1)[1])
|
||||||
|
except (ValueError, IndexError):
|
||||||
|
continue
|
||||||
|
seconds = micros / 1000000.0
|
||||||
|
self._set_progress_fraction(seconds)
|
||||||
|
elif line.startswith("out_time="):
|
||||||
|
value = line.split("=", 1)[1]
|
||||||
|
parts = value.split(":")
|
||||||
|
seconds = 0.0
|
||||||
|
ok = True
|
||||||
|
for e in parts:
|
||||||
|
try:
|
||||||
|
seconds = seconds * 60.0 + float(e)
|
||||||
|
except ValueError:
|
||||||
|
ok = False
|
||||||
|
break
|
||||||
|
if ok:
|
||||||
|
self._set_progress_fraction(seconds)
|
||||||
|
elif line == "progress=end":
|
||||||
|
self.progress_bar[1].set_fraction(1.0)
|
||||||
|
self.progress_bar[1].set_text("100.0%")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def _set_progress_fraction(self, seconds):
|
||||||
|
if (self.progress_bar is None) or (not self.final_length):
|
||||||
|
return
|
||||||
|
t = seconds / float(self.final_length)
|
||||||
|
if t < 0.0:
|
||||||
|
t = 0.0
|
||||||
|
if t > 1.0:
|
||||||
|
t = 1.0
|
||||||
|
self.progress_bar[1].set_fraction(t)
|
||||||
|
self.progress_bar[1].set_text("%.1f%%" % (100.0 * t))
|
||||||
|
|
||||||
def process_stderr(self, data):
|
def process_stderr(self, data):
|
||||||
|
|
||||||
pos = data[0].find("time=")
|
pos = data[0].find("time=")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue