How to hide console output of FFmpeg in Python?

5.8k Views Asked by At

I was working on a YouTube video downloader Python program.

I want to encode downloaded data to other media formats for this job i used FFmpeg and FFmpeg-Python (Package to use FFmpeg in Python).

Everything is Fine but i want to ask that how can i disable FFmpeg Output on the console ?

Here is some Pic of my Program :-

Main Graphical Interface

But this console often appears when my program starts encoding, suppressing the main GUI :-

FFMPEG - OUTPUT

If you know any solution for my problem then please give me some solution. It is my first time that i am trying Stackoverflow for my problem.

THANKS IN ADVANCE !!!!!

4

There are 4 best solutions below

0
On

Add "from subprocess import CREATE_NO_WINDOW" and use "creationflags=CREATE_NO_WINDOW" for Popen. Below is updated part of "_run.py" code from ffmpeg-python library, that worked for me.

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
    )
3
On

It has been 1 year and 8 months since you have asked this question, you might already have a solution for that. However, I found a solution to solve your problem.

You can solve this problem by modifying the original ffmpeg code when you package your python program.

First, find your ffmpeg lib folder, if you install with the default location, you can check your libs here: C:\Users\User\AppData\Local\Programs\Python\Python310\Lib\site-packages\ffmpeg.

Second, find _probe.py and modify codes, here is the code that already got modified, any change is written in the comments. You need to Popen add 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']

Then, go to _run.py. You need to add shell=True, modify stdin=subprocess.PIPE or modify pipe_stdin=True (The code section below is just a part of the code):

@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
    )
0
On

Bradley's answer worked for to stop console flashes after compiling with pyinstaller. However, I wasn't comfortable updating the ffmpeg-python library itself since it would be overwritten when there was an update from PIP, and felt a little hacky just in general.

I ended up hi-jacking the functions to use within my class and used those directly, which also did the trick. I think it's safer but still carries its own risks if the library is updated in a way that conflicts with the hijacked functions.

  """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)

Then to use

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
2
On

set loglevel to quiet

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