@@ -58,10 +58,15 @@ namespace {
5858
5959 if (progress_callback)
6060 progress_callback ();
61-
62- line.clear ();
61+ }
62+ else {
63+ // not a frame update - e.g. a \r-terminated status line from a
64+ // TensorRT engine build. surface it as a log line instead of
65+ // silently gluing it onto whatever comes next.
66+ state->report_log_line (line);
6367 }
6468
69+ line.clear ();
6570 continue ;
6671 }
6772
@@ -70,6 +75,8 @@ namespace {
7075
7176 DEBUG_LOG (" [vspipe error] {}" , line);
7277
78+ state->report_log_line (line);
79+
7380 line.clear ();
7481 continue ;
7582 }
@@ -80,6 +87,7 @@ namespace {
8087 if (!line.empty ()) {
8188 vspipe_errors << line << ' \n ' ;
8289 DEBUG_LOG (" [vspipe error] {}" , line);
90+ state->report_log_line (line);
8391 }
8492 }
8593
@@ -199,14 +207,22 @@ tl::expected<rendering::detail::PipelineResult, rendering::RenderError> renderin
199207 std::thread ffmpeg_stderr_thread (pump_ffmpeg_stderr, std::ref (ffmpeg_stderr), std::ref (ffmpeg_errors));
200208 std::thread ffmpeg_stdout_thread (extract_jpeg_stream, std::ref (ffmpeg_stdout), state);
201209
210+ // vspipe's tensorrt backend shells out to trtexec as a *grandchild* process
211+ // (blur.py -> vsmlrt.py -> subprocess.run). a plain child.terminate() only
212+ // kills vspipe itself, leaving trtexec running an engine build in the
213+ // background after cancel. put vspipe in a process group (job object on
214+ // windows) so terminating the group takes the whole tree down with it.
215+ bp::group vspipe_group;
216+
202217 auto vspipe_process = u::run_command (
203218 blur.vspipe_path ,
204219 commands.vspipe_video ,
205220 env,
206221 bp::std_out > vspipe_stdout,
207222 bp::std_err > vspipe_stderr,
208- bp::std_in < bp::null // stdin is an invalid handle otherwise, which breaks
209- // subprocess.run(stdout=sys.stderr) in rife-trt (FUN!)
223+ bp::std_in < bp::null, // stdin is an invalid handle otherwise, which breaks
224+ // subprocess.run(stdout=sys.stderr) in rife-trt (FUN!)
225+ vspipe_group
210226 );
211227
212228 auto ffmpeg_process =
@@ -226,7 +242,12 @@ tl::expected<rendering::detail::PipelineResult, rendering::RenderError> renderin
226242 bool killed = false ;
227243 while (ffmpeg_process.running ()) {
228244 if (state->wants_stop ()) {
229- vspipe_process.terminate ();
245+ // non-throwing: terminate() invalidates the group handle, and we
246+ // terminate it again unconditionally below - a throwing call there
247+ // on an already-invalid handle would escape as a generic error
248+ // instead of a clean "stopped" result.
249+ std::error_code ec;
250+ vspipe_group.terminate (ec);
230251 ffmpeg_process.terminate ();
231252 killed = true ;
232253 break ;
@@ -240,8 +261,11 @@ tl::expected<rendering::detail::PipelineResult, rendering::RenderError> renderin
240261 std::this_thread::sleep_for (std::chrono::milliseconds (50 ));
241262 }
242263
243- // stop stuff if they're stuck
244- vspipe_process.terminate ();
264+ // stop stuff if they're stuck (no-op if already terminated above)
265+ {
266+ std::error_code ec;
267+ vspipe_group.terminate (ec);
268+ }
245269
246270 // wait for threads to finish
247271 if (ffmpeg_stdout_thread.joinable ())
0 commit comments