ffmpeg Progress Bar - процент кодирования в PHP

Я написал целую систему на PHP и bash на сервере для конвертации и потокового видео в HTML5 на моем VPS. Преобразование выполняется ffmpeg в фоновом режиме, а содержимое выводится в block.txt.

Посмотрев на следующие посты:

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

а также

индикатор выполнения кодирования видео ffmpeg

среди прочего, я не могу найти работающий пример.

Мне нужно захватить в настоящее время закодированный прогресс в процентах.

Первый пост, который я связал выше, дает:

$log = @file_get_contents('block.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)));

echo $percent_extracted;

Переменная $cent_extracted повторяет ноль, и, поскольку математика не является моей сильной стороной, я действительно не знаю, как здесь развиваться.

Вот одна строка из вывода ffmpeg из block.txt (если это полезно)

время =00:19:25,16 битрейт = 823,0 кбит / с кадр =27963 к / с = 7 q=0,0 размер = 117085 кБ время =00:19:25,33 битрейт = 823,1 кбит / с кадр =27967 к / с = 7 q=0,0 размер = 117085 кБ время =00:19:25,49 битрейт = 823,0 кбит / с кадр = 27971 кадр / с = 7 q=0,0 размер = 117126 кБ

Пожалуйста, помогите мне вывести этот процент, как только я смогу создать свой собственный индикатор выполнения. Благодарю.

3 ответа

Решение

Хорошо, я нашел то, что мне было нужно - и, надеюсь, это поможет кому-то еще!

Прежде всего, вы хотите вывести данные ffmpeg в текстовый файл на сервере.

ffmpeg -i path/to/input.mov -vcodec videocodec -acodec audiocodec path/to/output.flv 1> block.txt 2>&1

Итак, вывод ffmpeg - это block.txt. Теперь в PHP, давайте сделаем это!

$content = @file_get_contents('../block.txt');

if($content){
    //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 time in the file that is already encoded
    preg_match_all("/time=(.*?) bitrate/", $content, $matches);

    $rawTime = array_pop($matches);

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

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

    //calculate the progress
    $progress = round(($time/$duration) * 100);

    echo "Duration: " . $duration . "<br>";
    echo "Current Time: " . $time . "<br>";
    echo "Progress: " . $progress . "%";

}

Это выводит процент оставшегося времени.

Вы можете сделать это единственным фрагментом текста, отображаемым на странице, а с другой страницы вы можете выполнить AJAX-запрос, используя jQuery, чтобы захватить этот фрагмент текста и вывести его в div, например, для обновления на вашей странице каждый раз. 10 секунд.:)

У ffmpeg теперь есть опция хода выполнения, которая упрощает анализ выходных данных.

ffmpeg -progress block.txt -i путь / к /input.mov -vcodec видеокодек -acodec аудиокодек путь / к /output.flv 2>&1

Перед тем, как начать кодирование, вы можете получить общее количество фреймов и много другой информации с этим (это то, что будет сделано с bash. Я программист на Perl, поэтому я не знаю, как вы получите информацию в свой PHP скрипт).

eval $ (ffprobe -of flat = s = _ -show_entries stream = высота, ширина,nb_frames, продолжительность, путь кодового_имя /to/input.mov);
ширина =${streams_stream_0_width};
высота =${streams_stream_0_height};
кадров = $ {streams_stream_0_nb_frames};
videoduration = $ {streams_stream_0_duration};
audioduration = $ {streams_stream_1_duration};
кодек = $ {streams_stream_0_codec_name};
echo $ width, $ height, $ frames, $ videoduration, $ audioduration, $ codec;

-of flate = s = _ говорит помещать каждое имя = значение в отдельной строке. -show_entries говорит ему показывать записи из следующего (поток для -show_streams, формат для -show_format и т. д.) stream=... говорит, что показывает эти элементы из вывода -show_streams. Попробуйте следующее, чтобы увидеть, что доступно:

ffprobe -show_streams path / to / input.mov

Вывод в файл прогресса добавляется примерно раз в секунду. Содержимое после завершения кодирования выглядит следующим образом. В моем сценарии раз в секунду я помещаю файл в массив и перебираю массив в обратном порядке, используя только то, что находится между первыми [последними перед обращением] двумя найденными мной строками "прогресса", так что я использую самая свежая информация из конца файла. Там могут быть лучшие способы. Это из mp4 без звука, поэтому есть только один поток.

кадр =86
кадров в секунду = 0.0
stream_0_0_q = 23,0
TOTAL_SIZE =103173
out_time_ms = 1120000
OUT_time=00:00:01,120000
dup_frames = 0
drop_frames = 0
прогресс = продолжить
рамы =142
кадров в секунду =140,9
stream_0_0_q = 23,0
TOTAL_SIZE = 415861
out_time_ms = 3360000
OUT_time=00:00:03,360000
dup_frames = 0
drop_frames = 0
прогресс = продолжить
рамы =185
кадров в секунду =121,1
stream_0_0_q = 23,0
TOTAL_SIZE = 1268982
out_time_ms = 5080000
OUT_time=00:00:05,080000
dup_frames = 0
drop_frames = 0
прогресс = продолжить
кадр =225
кадров в секунду = 110,9
stream_0_0_q = 23,0
TOTAL_SIZE = 2366000
out_time_ms = 6680000
OUT_time=00:00:06,680000
dup_frames = 0
drop_frames = 0
прогресс = продолжить
кадр =262
кадров в секунду =103,4
stream_0_0_q = 23,0
TOTAL_SIZE = 3810570
out_time_ms = 8160000
OUT_time=00:00:08,160000
dup_frames = 0
drop_frames = 0
прогресс = продолжить
кадр =299
=84,9 кадров в секунду
stream_0_0_q=-1.0
TOTAL_SIZE = 6710373
out_time_ms = 11880000
OUT_time=00:00:11,880000
dup_frames = 0
drop_frames = 0
прогресс = конец

Если javascript обновляет ваш индикатор выполнения, javascript может выполнить шаг 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 result = {};

    // 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);
        }

        result.status = 200;
        result.duration = duration;
        result.current  = time;
        result.progress = progress;

        console.log(result);

        /* 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);
Другие вопросы по тегам