FFmpeg视频编码进度条

6
当我在浏览器中运行转换时,页面只会显示白色的空白区域。只有在转换过程完成后,页面才会加载。
请建议如何实现一个进度条,在视频转换过程中向用户展示进度。
我在我的php脚本中有这个内容。
exec("ffmpeg -i filename.flv -sameq -ab 128 -s 640x480 filename.mp4");

那么,我应该如何修改这个脚本,以便获取进度详细信息并将其保存到文件中或直接输出到页面上。请问有人能给我一个完整的脚本/代码来详细解释吗?因为我认为我无法得到完整的答案,所以我对该怎么做感到困惑。

5个回答

4
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 或原生 JavaScript 等其他工具库:
[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);

3

虽然对于小文件来说,最好选择简单的ajax指示器,但对于大文件(>50-80 MB),您可以这样做:

您可以通过PHP读取FFMPEG返回值。ffmpeg(最后几行)会返回以下内容:

Press [q] to stop encoding
frame= 1850 fps=115 q=31.0 Lsize=    5789kB time=74.00 bitrate= 640.8kbits/s   
video:5135kB audio:580kB

time=74.00是当前文件时间(而非执行时间)。您可以使用一些正则表达式来解析该值,并进行一些数学计算,以获得百分比完成进度条。

如果您不知道文件时间长度。FFMPEG的前几行会返回此信息:

Input #0, flv, from 'cf_video_3728.flv':
  Duration: 00:01:14.13, start: 0.000000, bitrate: 864 kb/s

你可以解析持续时间并获取总时间。
希望这能帮到你。

1
如果您正在使用PHP-FFMpeg,他们已经添加了一个on函数,您可以监听进度并执行回调。您可以在视频格式容器上设置它,如下所示:
$format = new Format\Video\X264();
$format->on('progress', function ($video, $format, $percentage) {
    echo "$percentage % transcoded";
});

在文档https://github.com/PHP-FFMpeg/PHP-FFMpeg中可以找到更多信息。


0

我不相信这是可能的。

问题在于您需要让您的 PHP 脚本知道当前 ffmpeg 处理状态。如果您通过命令行进行 ffmpeg 转换,则会向用户显示进度,但是当您使用类似于 exec 的 PHP 命令生成 ffmpeg 时,您无法访问该输出流。

与其尝试向用户显示真正的“进度指示器”,我建议您只是在页面上加入一个等待指示器。您可以找到许多适用于此目的的图像,或使用 http://ajaxload.info/ 等工具生成图像。


这样做可能会很尴尬且效率低下,但我想你应该能够将ffmpeg输出重定向到一个临时文件中,然后再由php读取。不过我更倾向于使用等待指示器 - 更简单。 - Ultimate Gobblement

0

你可能会对COMET感兴趣。

这是一种编程技术,它允许你将信息从服务器推送到浏览器,而无需浏览器请求。

这里有一篇文章,介绍如何通过使用隐藏的iframe在PHP中实现它。

http://www.zeitoun.net/articles/comet_and_php/start

也许你可以将这个与 Stewie 提供的关于读取 ffmpeg 返回值的答案结合使用。

基本上我同意,Comet D可以让我的答案更完整,任何推送技术都应该被优先考虑。 - sebilasse

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接