Подтвердить что ты не робот

Может ли ffmpeg показать индикатор выполнения?

Я конвертирую файл .avi в .flv файл, используя ffmpeg. Поскольку для преобразования файла требуется много времени, я бы хотел отобразить индикатор выполнения. Может ли кто-нибудь посоветовать мне, как идти примерно то же самое.

Я знаю, что ffmpeg каким-то образом должен выводить прогресс в текстовом файле, и мне нужно его прочитать с помощью вызовов ajax. Но как мне получить ffmpeg для вывода прогресса в текстовый файл?

Большое спасибо.

4b9b3361

Ответ 1

Я играю с этим в течение нескольких дней. Эта вещь "ffmpegprogress" помогла, но было очень сложно работать с моей настройкой и читать код непросто.

Чтобы показать прогресс ffmpeg, вам необходимо сделать следующее:

  • запустите команду ffmpeg с php, не дожидаясь ответа (для меня это была самая сложная часть)
  • сообщить ffmpeg, чтобы отправить его в файл
  • с передней стороны (AJAX, Flash, независимо) попадает либо в этот файл напрямую, либо в файл php, который может вывести прогресс из вывода ffmpeg.

Вот как я решил каждую часть:

1. Я получил следующую идею от "ffmpegprogress". Это он и сделал: один PHP файл вызывает другого через http-сокет. Второй на самом деле запускает "exec", и первый файл просто зависает на нем. Для меня его реализация была слишком сложной. Он использовал "fsockopen". Мне нравится CURL. Итак, вот что я сделал:

$url = "http://".$_SERVER["HTTP_HOST"]."/path/to/exec/exec.php";
curl_setopt($curlH, CURLOPT_URL, $url);
$postData = "&cmd=".urlencode($cmd);
$postData .= "&outFile=".urlencode("path/to/output.txt");
curl_setopt($curlH, CURLOPT_POST, TRUE);
curl_setopt($curlH, CURLOPT_POSTFIELDS, $postData);

curl_setopt($curlH, CURLOPT_RETURNTRANSFER, TRUE);

// # this is the key!
curl_setopt($curlH, CURLOPT_TIMEOUT, 1);
$result = curl_exec($curlH);

Установка CURLOPT_TIMEOUT в 1 означает, что для ответа будет ждать 1 секунда. Предпочтительно, чтобы это было ниже. Существует также CURLOPT_TIMEOUT_MS, который занимает миллисекунды, но для меня это не сработало.

Через 1 секунду CURL зависает, но команда exec все еще работает. Часть 1 решена.

BTW - Несколько человек предлагали использовать команду nohup для этого. Но, похоже, это не работало для меня.

* ТАКЖЕ! Наличие php файла на вашем сервере, который может выполнять код непосредственно в командной строке, является очевидным угрозой безопасности. Вы должны иметь пароль или кодировать данные сообщения каким-то образом.

2. Вышеупомянутый "exec.php" script должен также указывать ffmpeg для вывода в файл. Здесь код для этого:

exec("ffmpeg -i path/to/input.mov path/to/output.flv 1> path/to/output.txt 2>&1");

Обратите внимание на "1 > путь /to/output.txt 2 > & 1". Я не эксперт в командной строке, но из того, что я могу сказать, эта строка говорит "отправить нормальный вывод в этот файл и отправить ошибки в одно и то же место". Проверьте этот url для получения дополнительной информации: http://tldp.org/LDP/abs/html/io-redirection.html

3. Из внешнего интерфейса вызовите php script, указав ему местоположение файла output.txt. Затем этот файл php вытащит прогресс из текстового файла. Вот как я это сделал:

// # get duration of source
preg_match("/Duration: (.*?), start:/", $content, $matches);

$rawDuration = $matches[1];

// # rawDuration is in 00:00:00.00 format. This converts it to seconds.
$ar = array_reverse(explode(":", $rawDuration));
$duration = floatval($ar[0]);
if (!empty($ar[1])) $duration += intval($ar[1]) * 60;
if (!empty($ar[2])) $duration += intval($ar[2]) * 60 * 60;


// # get the current time
preg_match_all("/time=(.*?) bitrate/", $content, $matches); 

$last = array_pop($matches);
// # this is needed if there is more than one match
if (is_array($last)) {
    $last = array_pop($last);
}

$curTime = floatval($last);


// # finally, progress is easy
$progress = $curTime/$duration;

Надеюсь, это поможет кому-то.

Ответ 2

В статье на английском языке описывается, как решить вашу проблему.

Цель состоит в том, чтобы уловить значение Duration перед кодированием и уловить значения time=... во время кодирования.

--skipped--
Duration: 00:00:24.9, start: 0.000000, bitrate: 331 kb/s
--skipped--
frame=   41 q=7.0 size=     116kB time=1.6 bitrate= 579.7kbits/s
frame=   78 q=12.0 size=     189kB time=3.1 bitrate= 497.2kbits/s
frame=  115 q=13.0 size=     254kB time=4.6 bitrate= 452.3kbits/s
--skipped--

Ответ 3

FFmpeg использует stdout для вывода медиаданных и stderr для ведения журнала/информации о ходе работы. Вам просто нужно перенаправить stderr в файл или в stdin процесса, способного обрабатывать его.

С оболочкой unix это что-то вроде:

ffmpeg {ffmpeg arguments} 2> logFile

или

ffmpeg {ffmpeg arguments} 2| processFFmpegLog

В любом случае вам нужно запустить ffmpeg как отдельный поток или процесс.

Ответ 4

Это очень просто, если вы используете команду pipeview. Чтобы сделать это, преобразуйте

ffmpeg -i input.avi {arguments}

к

pv input.avi | ffmpeg -i pipe:0 -v warning {arguments}

Не нужно вдаваться в кодирование!

Ответ 5

Вы можете сделать это с помощью аргумента ffmpeg -progress и nc

WATCHER_PORT=9998

DURATION= $(ffprobe -select_streams v:0 -show_entries "stream=duration" \
    -of compact $INPUT_FILE | sed 's!.*=\(.*\)!\1!g')

nc -l $WATCHER_PORT | while read; do
    sed -n 's/out_time=\(.*\)/\1 of $DURATION/p')
done &

ffmpeg -y -i $INPUT_FILE -progress localhost:$WATCHER_PORT $OUTPUT_ARGS

Ответ 6

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

Таким образом, я рекомендую попробовать совершенно новый ffmpeg-progressbar-cli :

ffmpeg-progressbar-cli screencast

Это оболочка для исполняемого файла ffmpeg, показывающая цветной индикатор выполнения по центру и оставшееся время.

Кроме того, он с открытым исходным кодом, основанный на Node.js и активно развивающийся, обрабатывает большинство из упомянутых причуд (полное раскрытие: я в настоящее время являюсь его ведущим разработчиком).

Ответ 7

javascript должен сказать php, чтобы начать преобразование [1], а затем сделать [2]...


[1] php: начать преобразование и записать статус в файл (см. выше):

exec("ffmpeg -i path/to/input.mov path/to/output.flv 1>path/to/output.txt 2>&1");

Для второй части нам нужен просто javascript, чтобы прочитать файл. В следующем примере используется dojo.request для AJAX, но вы можете использовать jQuery или vanilla или что-то еще:

[2] js: захватить прогресс из файла:

var _progress = function(i){
    i++;
    // THIS MUST BE THE PATH OF THE .txt FILE SPECIFIED IN [1] : 
    var logfile = 'path/to/output.txt';

/* (example requires dojo) */

request.post(logfile).then( function(content){
// AJAX success
    var duration = 0, time = 0, progress = 0;
    var resArr = [];

    // get duration of source
    var matches = (content) ? content.match(/Duration: (.*?), start:/) : [];
    if( matches.length>0 ){
        var rawDuration = matches[1];
        // convert rawDuration from 00:00:00.00 to seconds.
        var ar = rawDuration.split(":").reverse();
        duration = parseFloat(ar[0]);
        if (ar[1]) duration += parseInt(ar[1]) * 60;
        if (ar[2]) duration += parseInt(ar[2]) * 60 * 60;

        // get the time 
        matches = content.match(/time=(.*?) bitrate/g);
        console.log( matches );

        if( matches.length>0 ){
            var rawTime = matches.pop();
            // needed if there is more than one match
            if (lang.isArray(rawTime)){ 
                rawTime = rawTime.pop().replace('time=','').replace(' bitrate',''); 
            } else {
                rawTime = rawTime.replace('time=','').replace(' bitrate','');
            }

            // convert rawTime from 00:00:00.00 to seconds.
            ar = rawTime.split(":").reverse();
            time = parseFloat(ar[0]);
            if (ar[1]) time += parseInt(ar[1]) * 60;
            if (ar[2]) time += parseInt(ar[2]) * 60 * 60;

            //calculate the progress
            progress = Math.round((time/duration) * 100);
        }

        resArr['status'] = 200;
        resArr['duration'] = duration;
        resArr['current']  = time;
        resArr['progress'] = progress;

        console.log(resArr);

        /* UPDATE YOUR PROGRESSBAR HERE with above values ... */

        if(progress==0 && i>20){
            // TODO err - giving up after 8 sec. no progress - handle progress errors here
            console.log('{"status":-400, "error":"there is no progress while we tried to encode the video" }'); 
            return;
        } else if(progress<100){ 
            setTimeout(function(){ _progress(i); }, 400);
        }
    } else if( content.indexOf('Permission denied') > -1) {
        // TODO - err - ffmpeg is not executable ...
        console.log('{"status":-400, "error":"ffmpeg : Permission denied, either for ffmpeg or upload location ..." }');    
    } 
},
function(err){
// AJAX error
    if(i<20){
        // retry
        setTimeout(function(){ _progress(0); }, 400);
    } else {
        console.log('{"status":-400, "error":"there is no progress while we tried to encode the video" }');
        console.log( err ); 
    }
    return; 
});

}
setTimeout(function(){ _progress(0); }, 800);

Ответ 8

Вызов системных функций php блокирует этот поток, поэтому вам нужно будет развернуть 1 HTTP-запрос для выполнения преобразования и другой опрос для чтения файла txt, который создается.

Или, еще лучше, клиенты отправляют видео для преобразования, а затем другой процесс ответственен за выполнение преобразования. Таким образом, клиентское соединение не будет отключено во время ожидания завершения системного вызова. Опрос выполняется так же, как указано выше.

Ответ 9

Были проблемы со второй частью php. Поэтому я использую это вместо:

    $log = @file_get_contents($txt);
    preg_match("/Duration:([^,]+)/", $log, $matches);
    list($hours,$minutes,$seconds,$mili) = split(":",$matches[1]);
    $seconds = (($hours * 3600) + ($minutes * 60) + $seconds);
    $seconds = round($seconds);

    $page = join("",file("$txt"));
    $kw = explode("time=", $page);
    $last = array_pop($kw);
    $values = explode(' ', $last);
    $curTime = round($values[0]);
    $percent_extracted = round((($curTime * 100)/($seconds)));

Вывод отлично.

Хотелось бы увидеть что-то для нескольких загрузок для другого индикатора выполнения. Это передача текущего файла за один процент. Затем общий индикатор выполнения. Почти там.

Кроме того, если люди с трудом получают:

exec("ffmpeg -i path/to/input.mov path/to/output.flv 1> path/to/output.txt 2>&1");

Чтобы работать.

Try:

exec("ffmpeg -i path/to/input.mov path/to/output.flv 1>path/to/output.txt 2>&1");

" 1 > путь" до " 1 > путь" ИЛИ " 2 > путь" до " 2 > путь"

Принял меня немного, чтобы понять это. FFMPEG не работает. Работал, когда я не занимал места.

Ответ 10

Если вам просто нужно скрыть всю информацию и показать прогресс по умолчанию, например, ffmpeg в последней строке, вы можете использовать -stats:

ffmpeg -v warning -hide_banner -stats ${your_params}