2017-02-03 2 views
3

Я пытаюсь обработать программный процесс Perl с выходом ffmpeg encode, однако моя тестовая программа только, кажется, получает вывод ffmpeg в периодических кусках, поэтому я предполагаю, что есть какой-то вид буферизации. Как я могу заставить его обрабатывать его в режиме реального времени?Как остановить perl-буферизацию ffmpeg output

Моя тестовая программа (команда tr там, потому что я думал, что, может быть, возврат каретки FFmpeg, причиняли Perl, чтобы увидеть одну большую длинную линию или что-то):

#!/usr/bin/perl 

$i = "test.mkv"; # big file, long encode time 
$o = "test.mp4"; 

open(F, "-|", "ffmpeg -y -i '$i' '$o' 2>&1 | tr '\r' '\n'") 
     or die "oh no"; 

while(<F>) { 
     print "A12345: $_"; # some random text so i know the output was processed in perl 
} 

Все отлично работает, когда я заменить ffmpeg команду с этот скрипт:

#!/bin/bash 

echo "hello"; 

for i in `seq 1 10`; do 
     sleep 1; 
     echo "hello $i"; 
done 

echo "bye"; 

При использовании приведенного выше сценария я вижу выход каждую секунду, когда это происходит. С ffmpeg он составляет примерно 5-10 секунд или до тех пор, пока он не выйдет, и будет выводить иногда по 100 строк на каждый вывод.

Я пробовал использовать программу unbuffer впереди ffmpeg в вызове команды, но, похоже, это не имеет никакого эффекта. Возможно, это 2>&1, что может быть буферизацией? Любая помощь очень ценится.

Если вы не знакомы с выходом FFmpeg, он выдает кучу информации файлов и прочего в STDOUT, а затем при кодировании выводит такие строки

frame= 332 fps= 93 q=28.0 size=  528kB time=00:00:13.33 bitrate= 324.2kbits/s speed=3.75x 

, которые начинаются с кареткой возвращает вместо новых линий (отсюда tr) на STDERR (отсюда 2>&1).

+0

Попробуйте 'открытой (F, '- |: unix', "ffmpeg ......") ' –

+0

@ChankeyPathak Я попробовал добавить: unix, но, к сожалению, это не похоже ни на что другое. –

+0

@SebastianKing: Вы понимаете, что делают '2> & 1'? Почему вы положили его туда? – Borodin

ответ

1

Проблема, что tr был буферизацию вывода, и unbuffer, применялся только до ffmpeg в начале команды. Это означает, что unbuffer не смог повлиять на трубу на perl.

Это может быть исправлено с помощью unbuffer -p который предназначен для unbuffering водопроводной команды (<cmd1> | unbuffer -p <cmd2> unbuffers как cmd1 и cmd2).

Однако, я также заменил unbuffer с stdbuf -i0 -o0 -eL, чтобы удалить зависимость от expect пакета, потому что это большой и ненужный пакет, когда stdbuf поставляется в стандартной комплектации. Кроме того, опция -eL делает буферную строку stdbuf, которая, как я полагаю, должна повысить эффективность ЦП, чем отсутствие какого-либо буфера. Полный рабочий код ниже:

#!/usr/bin/perl 

$i = "test.mkv"; # big file, long encode time 
$o = "test.mp4"; 

open(F, "-|", "ffmpeg -y -i '$i' '$o' 2>&1 | stdbuf -i0 -o0 -eL tr '\r' '\n'") 
     or die "oh no"; 

while(<F>) { 
     print "A12345: $_"; # some random text so i know the output was processed in perl 
} 

Поскольку этот вопрос был дан ответ в комментариях и не правильный ответ не был отправлен, я сделал это вики ответ, кредит должен пойти @Borodin

+1

Спасибо за это. Я хотел написать решение, но это гораздо более полезно, чем то, что я бы написал. Вы должны принять свой ответ, как только сможете, пометить вопрос закрытым. – Borodin

0

Прежде всего, буферизация вывода - это то, что выполняет тот, кто делает вывод.

Большинство программ буферизуют их вывод, если они не подключены к терминалу, поэтому, если у вас нет одной из немногих программ, которые предоставляют возможность отключить буферизацию, единственный доступный вариант - обмануть их, используя вместо этого псевдо-tty трубы. Это то, что делает unbuffer.

Проблема в этом случае заключается в том, что существуют две программы, выполняющие буферизацию вывода: ffmpeg и tr. Вам нужно будет убедить обоих избежать буферизации. Вы должны использовать unbuffer для каждого! Надеюсь, это убедит их не буферизировать.

К сожалению, ... | unbuffer ... не работает. (Не знаю почему.) Так что избавитесь от tr, используя sysread вместо <>.

my $buf = ''; 
while (1) { 
    my $rv = sysread($PIPE, $buf, 64*1024, length($buf)); 
    die $! if !defined($rv); 
    last if !$rv; 

    while ($buf =~ s/^([^\r\n]*)[\r\n]//) { 
     my $line = $1; 
     print("[$line]\n"); 
    } 
} 

print("[$buf]\n") if $buf ne ""; 

Конечно, все это предполагает, что ffmpeg флеши это буфера после отправки возврата каретки, поскольку unbuffer, вероятно, не поможет с этим. (Конечно, если это так, то unbuffer, вероятно, вообще не нужен. Просто избегая tr путем переключения на sysread следует сделать трюк.)

+0

Использование 'unbuffer ffmpeg -y -i '$ i' '$ o' 2> & 1 | unbuffer tr '\ r' '\ n'' не дает мне никакого вывода, даже если я запустил его в оболочке, я даже попробовал «echo» hi »| unbuffer tr '\ r' '\ n'' и не получить результат, что я делаю неправильно? С командой echo он не возвращается, он просто ничего не выводит и остается включенным. –

+0

Действительно. (Протестировано с помощью 'echo" hi "| unbuffer perl -pe1', чтобы убедиться, что это не имеет никакого отношения к' tr'.) См. Обновление. – ikegami

Смежные вопросы