如何在Python中隐藏FFmpeg的控制台输出?

6

我正在开发一个 YouTube 视频下载器 的 Python 程序。

为了将下载的数据编码成其他媒体格式,我使用了 FFmpegFFmpeg-Python(在 Python 中使用 FFmpeg 的软件包)。

一切都很好,但我想问一下如何在控制台上禁用 FFmpeg 输出

这是我的程序截图:

主界面

但是当我的程序开始编码时,控制台经常会出现并覆盖主 GUI:

FFMPEG - OUTPUT

如果您知道解决方法,请提供一些解决方案。 这是我第一次尝试在 Stackoverflow 上寻求帮助。

提前感谢!


1
问题只是在ffmpeg的输出上还是在编码期间主GUI的冻结? - sqz
我在使用ffmpeg时遇到了一个问题。当我的主GUI仍然正常工作时,这个ffmpeg输出控制台突然出现在我的主GUI上。 - Shiven Saini
1
如果您只是想抑制ffmpeg输出的日志消息,您可以使用帖子传递标志以减少冗长性。或者,您可以使用pythonw.exe启动程序,以完全摆脱shell。 - sqz
我这里也有完全相同的问题。你找到解决方案了吗? - padmalcom
你找到解决方案了吗?如果没有的话,我的提供的答案对我有用,所以如果你将其标记为解决方案,那就太好了。 - Dark Lord
4个回答

4
自你提问以来已经过去了1年8个月,您可能已经有了解决方案。 但是,我找到了解决您问题的方法。
当您打包Python程序时,可以通过修改原始ffmpeg代码来解决此问题。
首先,找到您的ffmpeg lib文件夹,如果您使用默认位置安装,则可以在此处检查您的库:C:\ Users \ User \ AppData \ Local \ Programs \ Python \ Python310 \ Lib \ site-packages \ ffmpeg。
其次,找到_probe.py并修改代码,这里是已经修改过的代码,任何更改都写在注释中。 您需要添加args:shell = True,stdin = subprocess.PIPE。
import json
import subprocess
from ._run import Error
from ._utils import convert_kwargs_to_cmd_line_args


def probe(filename, cmd='ffprobe', **kwargs):
    """Run ffprobe on the specified file and return a JSON representation of the output.

    Raises:
        :class:`ffmpeg.Error`: if ffprobe returns a non-zero exit code,
        an :class:`Error` is returned with a generic error message.
        The stderr output can be retrieved by accessing the
        ``stderr`` property of the exception.
    """
    args = [cmd, '-show_format', '-show_streams', '-of', 'json']
    args += convert_kwargs_to_cmd_line_args(kwargs)
    args += [filename]

    # Original: p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    # Popen add args: shell=True, stdin=subprocess.PIPE,

    p = subprocess.Popen(args, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    out, err = p.communicate()
    if p.returncode != 0:
        raise Error('ffprobe', out, err)
    return json.loads(out.decode('utf-8'))


__all__ = ['probe']

然后,进入_run.py文件。你需要添加shell=True,修改stdin=subprocess.PIPE或修改pipe_stdin=True(下面的代码部分仅为代码的一部分):

@output_operator()
def run_async(
    stream_spec,
    cmd='ffmpeg',
    pipe_stdin=False,
    pipe_stdout=False,
    pipe_stderr=False,
    quiet=False,
    overwrite_output=False,
):
    """Asynchronously invoke ffmpeg for the supplied node graph.

Args:
    pipe_stdin: if True, connect pipe to subprocess stdin (to be
        used with ``pipe:`` ffmpeg inputs).
    pipe_stdout: if True, connect pipe to subprocess stdout (to be
        used with ``pipe:`` ffmpeg outputs).
    pipe_stderr: if True, connect pipe to subprocess stderr.
    quiet: shorthand for setting ``capture_stdout`` and
        ``capture_stderr``.
    **kwargs: keyword-arguments passed to ``get_args()`` (e.g.
        ``overwrite_output=True``).

Returns:
    A `subprocess Popen`_ object representing the child process.

Examples:
    Run and stream input::

        process = (
            ffmpeg
            .input('pipe:', format='rawvideo', pix_fmt='rgb24', s='{}x{}'.format(width, height))
            .output(out_filename, pix_fmt='yuv420p')
            .overwrite_output()
            .run_async(pipe_stdin=True)
        )
        process.communicate(input=input_data)

    Run and capture output::

        process = (
            ffmpeg
            .input(in_filename)
            .output('pipe':, format='rawvideo', pix_fmt='rgb24')
            .run_async(pipe_stdout=True, pipe_stderr=True)
        )
        out, err = process.communicate()

    Process video frame-by-frame using numpy::

        process1 = (
            ffmpeg
            .input(in_filename)
            .output('pipe:', format='rawvideo', pix_fmt='rgb24')
            .run_async(pipe_stdout=True)
        )

        process2 = (
            ffmpeg
            .input('pipe:', format='rawvideo', pix_fmt='rgb24', s='{}x{}'.format(width, height))
            .output(out_filename, pix_fmt='yuv420p')
            .overwrite_output()
            .run_async(pipe_stdin=True)
        )

        while True:
            in_bytes = process1.stdout.read(width * height * 3)
            if not in_bytes:
                break
            in_frame = (
                np
                .frombuffer(in_bytes, np.uint8)
                .reshape([height, width, 3])
            )
            out_frame = in_frame * 0.3
            process2.stdin.write(
                frame
                .astype(np.uint8)
                .tobytes()
            )

        process2.stdin.close()
        process1.wait()
        process2.wait()

.. _subprocess Popen: https://docs.python.org/3/library/subprocess.html#popen-objects
"""
    args = compile(stream_spec, cmd, overwrite_output=overwrite_output)
    stdin_stream = subprocess.PIPE if pipe_stdin else None
    stdout_stream = subprocess.PIPE if pipe_stdout or quiet else None
    stderr_stream = subprocess.PIPE if pipe_stderr or quiet else None

    # Original: return subprocess.Popen(
    #           args, stdin=pipe_stdin, stdout=stdout_stream, stderr=stderr_stream)    
    # Add shell=True, modify stdin=subprocess.PIPE or modify pipe_stdin=True

    return subprocess.Popen(
        args, shell=True, stdin=subprocess.PIPE, stdout=stdout_stream, stderr=stderr_stream
    )

2
你好!请在回答中指出您正在更改Python库,并且这个解决方案将在下一个FFmpeg库的pip更新中被覆盖。 - valentinmk
谢谢,帮了我大忙。你能否解释一下这个代码的作用,以及为什么它不再显示ffmpeg弹窗?希望这个功能能够在ffmpeg-python更新中实现。你应该被标记为正确答案。 - Dark Lord
这对我没用。 - duruburak

4

将日志级别设置为安静

ffmpeg.input(file).output(filename, loglevel="quiet").run()

0

添加 "from subprocess import CREATE_NO_WINDOW" 并使用 "creationflags=CREATE_NO_WINDOW" 用于 Popen。下面是 ffmpeg-python 库中 "_run.py" 代码的更新部分,对我有效。

from subprocess import CREATE_NO_WINDOW

@output_operator()
def run_async(
    stream_spec,
    cmd='ffmpeg',
    pipe_stdin=False,
    pipe_stdout=False,
    pipe_stderr=False,
    quiet=False,
    overwrite_output=False,
):

    args = compile(stream_spec, cmd, overwrite_output=overwrite_output)
    stdin_stream = subprocess.PIPE if pipe_stdin else None
    stdout_stream = subprocess.PIPE if pipe_stdout or quiet else None
    stderr_stream = subprocess.PIPE if pipe_stderr or quiet else None
    return subprocess.Popen(
        args, stdin=subprocess.PIPE, stdout=stdout_stream, stderr=stderr_stream, creationflags=CREATE_NO_WINDOW
    )

0

Bradley的答案可以解决使用pyinstaller编译后控制台闪烁的问题。然而,我不太舒服直接更新ffmpeg-python库本身,因为当PIP有更新时它会被覆盖,而且总体上感觉有点hacky。

最终,我选择劫持函数并在我的类中直接使用它们,这也解决了问题。我认为这样更安全,但如果库以与劫持函数冲突的方式进行更新,则仍存在风险。

  """Run OS command
  Function to merge video and
  subtitle file(s) into an MKV
  """
  def run_os_command(self, os_command):
    subprocess.call(os_command, shell=True)

  """FFmpeg probe hi-jack
  Customized arguments to Popen to
  prevent console flashes after
  compiled with PyInstaller
  """
  def ffmpeg_probe(self, video_input_path):
      command = ['ffprobe', '-show_format', '-show_streams', '-of', 'json']
      command += [video_input_path]

      process = subprocess.Popen(
        command,
        shell=True,
        stdin=subprocess.PIPE,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE
      )
      out, err = process.communicate()
      if process.returncode != 0:
          raise Exception(f"ffprobe error: {err}")

      return json.loads(out.decode('utf-8'))

  """FFmpeg run hi-jack
  Uses argument compiler from
  library but alternate sub-
  process method to run command
  to prevent console flashes.
  """
  def ffmpeg_run(self, stream):
    os_command = ffmpeg.compile(stream, 'ffmpeg', overwrite_output=True)

    return self.run_os_command(os_command)

然后使用

probe = ffmpeg_probe(video_input_path) # use like ffmpeg.probe()
ffmpeg_run(stream) # use like ffmpeg.run() can update the function if you pass more than stream

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接