ffmpeg 进度条 - PHP 中的编码百分比

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11441517/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-25 00:31:44  来源:igfitidea点击:

ffmpeg Progress Bar - Encoding Percentage in PHP

phpregexparsingffmpeg

提问by Jimbo

I've written a whole system in PHP and bash on the server to convert and stream videos in HTML5 on my VPS. The conversion is done by ffmpeg in the background and the contents is output to block.txt.

我在服务器上用 PHP 和 bash 编写了一个完整的系统,以在我的 VPS 上转换和流式传输 HTML5 格式的视频。转换由 ffmpeg 在后台完成,内容输出到block.txt

Having looked at the following posts:

看了以下帖子:

Can ffmpeg show a progress bar?

ffmpeg 可以显示进度条吗?

and

ffmpeg video encoding progress bar

ffmpeg 视频编码进度条

amongst others, I can't find a working example.

其中,我找不到一个有效的例子。

I need to grab the currently encoded progress as a percentage.

我需要以百分比形式获取当前编码的进度。

The first post I linked above gives:

我上面链接的第一篇文章给出了:

$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;

The $percent_extracted variable echoes zero, and as maths is not my strong point, I really don't know how to progress here.

$percent_extracted 变量回显零,而且由于数学不是我的强项,我真的不知道如何在这里取得进展。

Here's one line from the ffmpeg output from block.txt (if it's helpful)

这是来自 block.txt 的 ffmpeg 输出中的一行(如果有帮助的话)

time=00:19:25.16 bitrate= 823.0kbits/s frame=27963 fps= 7 q=0.0 size= 117085kB time=00:19:25.33 bitrate= 823.1kbits/s frame=27967 fps= 7 q=0.0 size= 117085kB time=00:19:25.49 bitrate= 823.0kbits/s frame=27971 fps= 7 q=0.0 size= 117126kB

时间=00:19:25.16 比特率= 823.0kbits/s 帧=27963 fps= 7 q=0.0 大小= 117085kB 时间=00:19:25.33 比特率= 823.1kbits/s 帧=27967 fps= 7 大小 q=10kB.7时间=00:19:25.49 比特率= 823.0kbits/s 帧=27971 fps= 7 q=0.0 大小= 117126kB

Please help me output this percentage, once done I can create my own progress bar. Thanks.

请帮我输出这个百分比,完成后我可以创建自己的进度条。谢谢。

回答by Jimbo

Okay, I've found what I needed - and hopefully this helps someone else as well!

好的,我找到了我需要的东西 - 希望这也能帮助其他人!

First and foremost, you want to output the ffmpeg data to a text file on the server.

首先,您希望将 ffmpeg 数据输出到服务器上的文本文件。

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

So, the ffmpeg output is block.txt. Now in PHP, let's do this!

所以,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 . "%";

}

This outputs the percentage of time left.

这将输出剩余时间的百分比。

You can have this as the only piece of text echoed out to a page, and from another page you can perform an AJAX request using jQuery to grab this piece of text and output it into a div, for example, to update on your page every 10 seconds. :)

你可以把它作为唯一一段回显到页面的文本,从另一个页面你可以使用 jQuery 执行 AJAX 请求来获取这段文本并将其输出到一个 div 中,例如,在你的页面上更新10 秒。:)

回答by Fred McIntyre

ffmpeg now has a progress option, which gives output more easily parsed.

ffmpeg 现在有一个进度选项,可以让输出更容易解析。

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

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

Before you start encoding you can get the total frames, and a lot of other info with this (this is what would be done with bash. I'm a Perl programmer so I don't know how you'd get the info into your PHP script).

在开始编码之前,您可以获得总帧数以及许多其他信息(这是使用 bash 完成的操作。我是一名 Perl 程序员,所以我不知道您如何将这些信息输入到您的PHP 脚本)。

eval $(ffprobe -of flat=s=_ -show_entries stream=height,width,nb_frames,duration,codec_name path/to/input.mov);
width=${streams_stream_0_width};
height=${streams_stream_0_height};
frames=${streams_stream_0_nb_frames};
videoduration=${streams_stream_0_duration};
audioduration=${streams_stream_1_duration};
codec=${streams_stream_0_codec_name};
echo $width,$height,$frames,$videoduration,$audioduration,$codec;

eval $(ffprobe -of flat=s=_ -show_entries stream=height,width,nb_frames,duration,codec_name path/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};
回声 $width,$height,$frames,$videoduration,$audioduration,$codec;

-of flate=s=_ says to put each name=value on a separate line. -show_entries tells it to show the entries from what follows (stream for -show_streams, format for -show_format, etc.) stream=... says to show those items from the -show_streams output. Try the following to see what is available:

-of flate=s=_ 表示将每个 name=value 放在单独的行上。-show_entries 告诉它显示后面的条目(-show_streams 的流,-show_format 的格式等)stream=... 表示从 -show_streams 输出显示这些项目。尝试以下操作以查看可用内容:

ffprobe -show_streams path/to/input.mov

ffprobe -show_streams path/to/input.mov

The output to the progress file is added to approximately once a second. Content, after the encoding is finished, looks like the following. In my script, once a second I am putting the file into an array, and traversing the array in reverse order, using only what is between the first [last before reversal] two "progress" lines I find, so that I am using the most recent info from the end of the file. There may be better ways. This is from an mp4 with no audio so there is only one stream.

进度文件的输出大约每秒添加一次。编码完成后的内容如下所示。在我的脚本中,我每秒将文件放入一个数组中,并以相反的顺序遍历数组,仅使用我找到的第一个 [last before reversal] 两个“progress”行之间的内容,以便我使用文件末尾的最新信息。可能有更好的方法。这是来自没有音频的 mp4,因此只有一个流。

frame=86
fps=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
progress=continue
frame=142
fps=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
progress=continue
frame=185
fps=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
progress=continue
frame=225
fps=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
progress=continue
frame=262
fps=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
progress=continue
frame=299
fps=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
progress=end

帧= 86
fps的= 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
FPS = 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 =
FPS = 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
FPS = 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
FPS = 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
progress=continue
frame=299
fps=84.9
stream_0_0_q=-1.0
total_size=6710373
out_time_ms=11880000
out_time=00:00:
10_frames=
progress_frames=
progress_frames= 84.9

回答by sebilasse

if javascript updates your progress bar, javascript could perform step 2 "directly" :

如果 javascript 更新您的进度条,javascript 可以“直接”执行第 2 步:

[this example requires dojo]

[这个例子需要dojo]



1php:start conversion and write status to a textfile - example syntax:

1php:开始转换并将状态写入文本文件 - 示例语法:

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


For the second part we need just javascriptto read the file. The following example uses dojo.request for AJAX, but you could use jQuery or vanilla or whatever as well :

对于第二部分,我们需要javascript来读取文件。以下示例将 dojo.request 用于 AJAX,但您也可以使用 jQuery 或 vanilla 或其他任何内容:

[2]js:grab the progress from the file:

[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);