Search code examples
pythonffmpegffmpeg-python

How to get real-time information from ffmpeg-python process?


I've seen some people achieving to scrape out live progress data from ffmpeg subprocess. But for non-command line execution, how could this be accomplished?

For example I want to store this real-time information on the command line output line by line or get the progress percentage to the completion. enter image description here

import ffmpeg
import threading

def ffmpeg_func(path1, path2, path3):
    global out, err
    video_part = ffmpeg.input(path1)
    audio_part = ffmpeg.input(path2)
    ffm = ffmpeg.output(audio_part, video_part, path3).overwrite_output().run_async(pipe_stdout=True)
    out, err = ffm.communicate()
    
threading.Thread(target=ffmpeg_func, args=(<<vid_part_path>>, <<audio_part_path>>, <<output_path>>)).start()

I used threading.Thread because I'm intending to execute multiple ffmpeg process at the same time.


Solution

  • Thanks to @Rotem, I implemented his or her answer to my code.

    import subprocess as sp
    import shlex
    import json
    from threading import Thread
    import time
    
    
    def progress_reader(procs, q):
        while True:
            if procs.poll() is not None:
                break
    
            progress_text = procs.stdout.readline()
    
            if progress_text is None:
                break
    
            progress_text = progress_text.decode("utf-8")
    
            if progress_text.startswith("frame="):
                frame = int(progress_text.partition('=')[-1])
                q[0] = frame
    
    data = sp.run(shlex.split('ffprobe -v error -select_streams v:0 -count_packets -show_entries stream=nb_read_packets -of csv=p=0 -of json vid.mp4'), stdout=sp.PIPE).stdout
    dict = json.loads(data)
    tot_n_frames = float(dict['streams'][0]['nb_read_packets'])
    
    process = sp.Popen(shlex.split('ffmpeg -y -loglevel error -i vid.mp4 -i aud.mp3 -progress pipe:1 output.mp4'), stdout=sp.PIPE)
    
    q = [0]
    progress_reader_thread = Thread(target=progress_reader, args=(process, q))
    progress_reader_thread.start()
    
    while True:
        if process.poll() is not None:
            break
    
        time.sleep(1)
    
        n_frame = q[0]
        progress_percent = (n_frame/tot_n_frames)*100
        print(f'Progress [%]: {progress_percent:.2f}')
    
    process.stdout.close()
    progress_reader_thread.join()
    process.wait()