1

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.

duruburak
  • 181
  • 14
  • 1
    Take a look at my [answer](https://stackoverflow.com/a/67409107/4926757). There is also the [following example](https://github.com/kkroening/ffmpeg-python/blob/master/examples/show_progress.py) that shows a progress bar. I don't see an issue regarding multithreading, except from the part of displaying the progress. – Rotem Mar 08 '23 at 20:22
  • I did, thanks. It helped a lot. I'm trying to implement your code to my situation, and I'm kinda struggling. My situation is I wanna merge a video and an audio with their default encodings. Could you please help me out? You can post it as an answer if you'd like so that I can accept it as a solution. @Rotem +++ – duruburak Mar 09 '23 at 22:55
  • Your code: `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 input.mp4'), stdout=sp.PIPE).stdout` `process = sp.Popen(shlex.split('ffmpeg -y -loglevel error -i input.mp4 -acodec libvorbis -vcodec libvpx-vp9 -crf 20 -pix_fmt yuv420p -progress pipe:1 output.webm'), stdout=sp.PIPE)` – duruburak Mar 09 '23 at 22:56
  • I tried this and failed: `data = sp.run(shlex.split('ffprobe -v error -count_packets -show_entries stream=nb_read_packets -of csv=p=0 -of json -i vid.mp4 -i aud.mp3'), stdout=sp.PIPE).stdout` `process = sp.Popen(shlex.split('ffmpeg -y -loglevel error -i vid.mp4 -i aud.mp3 -progress pipe:1 output.mp4'), stdout=sp.PIPE)` – duruburak Mar 09 '23 at 22:57
  • 1
    `count_packets` is used for counting video frames. Audio packets have different meaning. Assuming the audio and video length is the same, use the video only for estimating the length (remove `-i aud.mp3`). In case the length is not the same, or there is only audio, use the duration as in the following [example](https://github.com/kkroening/ffmpeg-python/blob/master/examples/show_progress.py). Use FFprobe twice - once for video and once for audio and take the minimum (or the maximum)... – Rotem Mar 10 '23 at 09:59

1 Answers1

0

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()
duruburak
  • 181
  • 14