Подпроцесс FFMPEG и Pythons

Я пытаюсь написать gui для FFMPEG. Я использую подпроцесс pythons для создания процесса ffmpeg для каждого преобразования, которое я хочу. Это отлично работает, но я также хотел бы получить способ получить прогресс преобразования, независимо от того, провалился он или нет и т. д. Я решил, что могу сделать это, обратившись к stdout процесса следующим образом:

вызов subprocess.Popen()

# Convert - Calls FFMPEG with current settings. (in a seperate
# thread.)
def convert(self):
    # Check if options are valid
    if self.input == "" or self.output == "":
        return False

# Make the command string
ffmpegString = self.makeString()

# Try to open with these settings
try:
    self.ffmpeg = subprocess.Popen(ffmpegString, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
except OSError:
    self.error.append("OSError: ")
except ValueError:
    self.error.append("ValueError: Couldn't call FFMPEG with these parameters")

# Convert process should be running now.

и значение stdout:

convert = Convert()
convert.input = "test.ogv"
convert.output = "test.mp4"
convert.output_size = (0, 0)

convert.convert()

while 1:
    print convert.ffmpeg.stdout.readline()

это работает, но статус ffmpeg не отображается. Я предполагаю, что это так. что-то делать с тем, как ffmpeg обновляет его. Есть ли способ получить к нему доступ?

6 ответов


Я часто замечал проблемы с чтением стандартного вывода (или даже стандартной ошибки!) с подпроцессом, из-за проблем буферизации, которые трудно победить. Мое любимое решение, когда мне нужно прочитать такой stdout/stderr из подпроцесса, - это переключиться на использование вместо subprocess, pexpect (или, в Windows, wexpect).


просто добавьте, universal_newlines=True для вашего подпроцесса.Линия к popen.

cmd="ffmpeg -i in.mp4 -y out.avi"
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,universal_newlines=True)
for line in process.stdout:
    print(line)

теперь у вас есть линия в цикле, как:

frame= 1900 fps=453 q=18.6 Lsize=    3473kB time=00:01:16.08 bitrate= 373.9kbits/s

используйте значение time= для определения прогресса в процентах.


Я думаю, вы не можете использовать readline, потому что ffmpeg никогда не печатает одну строку, статус обновляется путем записи \r (возврат carrige), а затем снова пишет строку.

size=      68kB time=0.39 bitrate=1412.1kbits/s    \rsize=    2786kB time=16.17 bitrate=1411.2kbits/s    \rsize=    5472kB time=31.76 bitrate=1411.2kbits/s    \r\n

Если вы изучите строку выше, вы заметите, что есть только один \n, и который печатается при преобразовании файла.


поскольку ffmpeg записывает данные unflushed в stderr, вы должны установить файловый дескриптор stderr в non-blocking с помощью fcntl.

    fcntl.fcntl(
        pipe.stderr.fileno(),
        fcntl.F_SETFL,
        fcntl.fcntl(pipe.stderr.fileno(), fcntl.F_GETFL) | os.O_NONBLOCK,
    )

а затем цикл с помощью select для чтения данных

    while True:
        readx = select.select([pipe.stderr.fileno()], [], [])[0]
        if readx:
            chunk = pipe.stderr.read()

для полного примера go здесь.


FFMPEG:

ffmpeg выведите весь текст состояния (что вы видите, когда вы запускаете его вручную в командной строке) в интерфейсе stderr. Чтобы захватить вывод из ffmpeg, вам нужно следить за интерфейсом stderr - или перенаправлять его, как пример.

Проверьте выход на stderr:

вот еще один способ попробовать и прочитать из stderr, а не перенаправлять его при вызове К popen

на к popen класс в Python есть файловый объект под названием stderr, вы получите к нему доступ так же, как и к stdout. Я думаю, что ваша петля будет выглядеть примерно так:

while 1:
    print convert.ffmpeg.stdout.readline()
    print convert.ffmpeg.stderr.readline()

отказ от ответственности: я не тестировал это в Python, но я сделал сопоставимое приложение с использованием Java.


ffmpegCommand='''
ffmpeg
-f lavfi
-i anullsrc=channel_layout=1c:sample_rate=11025
-rtsp_transport tcp
-rtsp_transport udp
-rtsp_transport http
-thread_queue_size 32000
-i rtsp://xxx.xxx.xxx.xxx:554/user=admin&password=xxx&channel=1&stream=1.sdp?real_stream
-reconnect 1
-reconnect_at_eof 1
-reconnect_streamed 1
-reconnect_delay_max 4294
-tune zerolatency
-c:v copy
-c:a aac
-bufsize 6000k
-f flv rtmp://a.rtmp.youtube.com/live2/xxx-xxx-xxx-xxx'''
cmd=ffmpegCommand.split()
# "universal newline support" This will cause to interpret \n, \r\n and \r     equally, each as a newline.

p = subprocess.Popen(cmd, stderr=subprocess.PIPE, universal_newlines=True)
while True:    
        print(p.stderr.readline().rstrip('\r\n'))