如何多线程ffmpeg-python进程?



我正在制作一个取消ffmpeg视频的按钮&按下音频合并过程。但是,一旦ffmpeg开始执行,while循环就不会继续循环,而在ffmpeg完成该过程后,while循环将继续循环。我真的看不清楚,如果是重复的,很抱歉。

我知道代码看起来很傻,但我有点注定,任何帮助将非常感激。提前谢谢。

from tkinter import *
import ffmpeg
import threading
def start_ffmpeg_thread(audio_part, video_part, path):
threading.Thread(target=start_ffmpeg, args=(audio_part, video_part, path)).start()
def start_ffmpeg(audio_part, video_part, path):
while True:
if is_cancelled:
break
threading.Thread(target=ffmpeg_func, args=(audio_part, video_part, path)).start()
def ffmpeg_func(audio_part, video_part, path):
ffmpeg.output(audio_part, video_part, path).run(overwrite_output=True)
def cancel_ffmpeg():
global is_cancelled
is_cancelled = True

is_cancelled = False
root = Tk()
video_part = ffmpeg.input("<path_video_part>")
audio_part = ffmpeg.input("<path_audio_part>")
path = "<path>"
button_1 = Button(root, text="Start", command=lambda: start_ffmpeg_thread(audio_part, video_part, path))
button_1.pack(pady=30, padx=30)
button_2 = Button(root, text="Stop", command=cancel_ffmpeg)
button_2.pack(pady=30, padx=30)
root.mainloop()

为了优雅地关闭FFmpeg子进程,我们可以将'q'写入FFmpeg子进程的stdin管道,如下文所述。

为了有可能写入'q',我们可以执行FFmpeg子进程如下:

ffmpeg_process = ffmpeg.output(audio_part, video_part, path).overwrite_output().run_async(pipe_stdin=True)

ffmpeg_process允许我们访问子进程并终止它。
为了保持代码简单,我们可以将ffmpeg_process声明为global变量。

更新ffmpeg_func方法:

def ffmpeg_func(audio_part, video_part, path):
global ffmpeg_process
ffmpeg_process = ffmpeg.output(audio_part, video_part, path).overwrite_output().run_async(pipe_stdin=True)

更新start_ffmpeg方法:

def start_ffmpeg(audio_part, video_part, path):
global ffmpeg_process
# Allow only one instance of FFmpeg to be executed - ffmpeg_process = None in the first time, and ffmpeg_process.poll() is not None when FFmpeg is not running
if (ffmpeg_process is None) or ffmpeg_process.poll():
threading.Thread(target=ffmpeg_func, args=(audio_part, video_part, path)).start()

上面的代码只允许在FFmpeg未运行时执行。


更新cancel_ffmpeg方法:

def cancel_ffmpeg():
global ffmpeg_process
#Check if FFmpeg sub-process is running
if (ffmpeg_process is not None) and (ffmpeg_process.poll() is None):
# Terminate FFmpeg gracefully
ffmpeg_process.stdin.write('q'.encode("GBK"))  # Simulate user pressing 'q' key
ffmpeg_process.communicate()
ffmpeg_process.wait()
ffmpeg_process = None

注意:

你的start_ffmpeg实现在循环中执行threading.Thread,并且在循环中执行FFmpeg子进程。

def start_ffmpeg(audio_part, video_part, path):
while True:
if is_cancelled:
break
threading.Thread(target=ffmpeg_func, args=(audio_part, video_part, path)).start()

我们必须从上面的方法中删除while True


更新的代码示例:

from tkinter import *
import ffmpeg
import threading
def start_ffmpeg_thread(audio_part, video_part, path):
threading.Thread(target=start_ffmpeg, args=(audio_part, video_part, path)).start()
def start_ffmpeg(audio_part, video_part, path):
#while True:
#    if is_cancelled:
#        break
global ffmpeg_process
# Allow only one instance of FFmpeg to be executed - ffmpeg_process = None in the first time, and ffmpeg_process.poll() is not None when FFmpeg is not running
if (ffmpeg_process is None) or ffmpeg_process.poll():
threading.Thread(target=ffmpeg_func, args=(audio_part, video_part, path)).start()
def ffmpeg_func(audio_part, video_part, path):
global ffmpeg_process
#ffmpeg.output(audio_part, video_part, path).run(overwrite_output=True)
ffmpeg_process = ffmpeg.output(audio_part, video_part, path).overwrite_output().run_async(pipe_stdin=True)

def cancel_ffmpeg():
#global is_cancelled
#is_cancelled = True
global ffmpeg_process
#Check if FFmpeg sub-process is running
if (ffmpeg_process is not None) and (ffmpeg_process.poll() is None):
# Terminate FFmpeg gracefully
ffmpeg_process.stdin.write('q'.encode("GBK"))  # Simulate user pressing 'q' key
ffmpeg_process.communicate()
ffmpeg_process.wait()
ffmpeg_process = None
#is_cancelled = False
ffmpeg_process = None
root = Tk()
video_part = ffmpeg.input("bunny_1080p_60fps.mp4")
audio_part = ffmpeg.input("bunny_1080p_60fps.mp4")
path = "output.mp4"
button_1 = Button(root, text="Start", command=lambda: start_ffmpeg_thread(audio_part, video_part, path))
button_1.pack(pady=30, padx=30)
button_2 = Button(root, text="Stop", command=cancel_ffmpeg)
button_2.pack(pady=30, padx=30)
root.mainloop()

如果您计划允许FFmpeg子进程的多个实例,您可以将ffmpeg_process插入到列表中,以便跟踪所有子进程。
要一次关闭所有子进程,只需迭代列表。

最新更新