ffmpeg可以显示进度条吗?

ffmpeg可以显示进度条吗?,ffmpeg,progress-bar,Ffmpeg,Progress Bar,我正在使用ffmpeg将.avi文件转换为.flv文件。由于转换文件需要很长时间,我想显示一个进度条。有人能告诉我怎么做吗 我知道ffmpeg必须以某种方式将进度输出到文本文件中,并且必须使用ajax调用读取进度。但是如何让ffmpeg将进度输出到文本文件?调用php的系统函数会阻塞该线程,因此需要生成一个HTTP请求来执行转换,另一个轮询请求用于读取生成的txt文件 或者,更好的是,客户端提交视频进行转换,然后另一个进程负责执行转换。这样,在等待系统调用终止时,客户端的连接不会超时。轮询的方式

我正在使用ffmpeg将.avi文件转换为.flv文件。由于转换文件需要很长时间,我想显示一个进度条。有人能告诉我怎么做吗


我知道ffmpeg必须以某种方式将进度输出到文本文件中,并且必须使用ajax调用读取进度。但是如何让ffmpeg将进度输出到文本文件?

调用php的系统函数会阻塞该线程,因此需要生成一个HTTP请求来执行转换,另一个轮询请求用于读取生成的txt文件


或者,更好的是,客户端提交视频进行转换,然后另一个进程负责执行转换。这样,在等待系统调用终止时,客户端的连接不会超时。轮询的方式与上述相同

FFmpeg使用stdout输出媒体数据,使用stderr记录/进度信息。您只需将stderr重定向到能够处理它的进程的文件或stdin

对于unix shell,这类似于:

ffmpeg {ffmpeg arguments} 2> logFile

无论如何,您必须将ffmpeg作为一个单独的线程或进程运行。

有一个俄文版本的说明如何解决您的问题

关键是在编码之前捕获
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--

我已经玩了几天了。“ffmpegprogress”有帮助,但我的设置很难使用,代码也很难阅读

要显示ffmpeg的进度,需要执行以下操作:

  • 从php运行ffmpeg命令,不要等待响应(对我来说,这是最难的部分)
  • 告诉ffmpeg将其输出发送到文件
  • 从前端(AJAX、Flash等)直接点击该文件或可以从ffmpeg输出中提取进度的php文件
  • 以下是我解决每个部分的方法:

    一,。 我从“ffmpegprogress”中得到了以下想法。这就是他所做的:一个PHP文件通过http套接字调用另一个PHP文件。第二个文件实际运行“exec”,第一个文件挂起。对我来说,他的实现太复杂了。他用的是“fsockopen”。我喜欢卷发。以下是我所做的:

    $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部分已解决

    顺便说一句,一些人建议使用“nohup”命令来实现这一点。但这似乎对我不起作用

    *还有!服务器上有一个可以直接在命令行上执行代码的php文件是一个明显的安全风险。您应该有一个密码,或者以某种方式对post数据进行编码

    二,。 上面的“exec.php”脚本还必须告诉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:

    三,。 从前端调用一个php脚本,给出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;
    

    希望这对其他人有所帮助。

    第二个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");
    
    工作

    尝试:

    1>路径”到“1>路径”或“2>路径”到“2>路径


    我花了一段时间才弄明白。他总是失败。当我改为无空格时,它工作了。

    javascript应该告诉php开始转换[1],然后执行[2]


    [1]php:开始转换并将状态写入文件(见上文):


    对于第二部分,我们只需要javascript来读取文件。 下面的示例使用dojo.request for 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);
    
    var\u进度=功能(i){
    i++;
    //这必须是[1]中指定的.txt文件的路径:
    var logfile='path/to/output.txt';
    /*(示例需要dojo)*/
    request.post(日志文件).then(函数(内容){
    //AJAX的成功
    风险值持续时间=0,时间=0,进度=0;
    var-resArr=[];
    //获取源的持续时间
    var matches=(content)?content.match(/Duration:(.*),start:/):[];
    如果(匹配。长度>0){
    var rawDuration=匹配项[1];
    //将持续时间从00:00:00.00转换为秒。
    var ar=rawDuration.split(“:”).reverse();
    持续时间=parseFloat(ar[0]);
    如果(ar[1])持续时间+=parseInt(ar[1])*60;
    如果(ar[2])持续时间+=parseInt(ar[2])*60*60;
    //抓紧时间
    matches=content.match(/time=(.*)比特率/g);
    console.log(匹配项);
    如果(匹配。长度>0){
    var rawTime=matches.pop();
    //如果有多个匹配项,则需要
    if(lang.isArray(rawTime)){
    rawTime=rawTime.pop().replace('time=','').replace('bitrate','');
    }否则{
    rawTime=rawTime.replace('time=','').replace('bitrate','');
    }
    //将原始时间从00:00:00.00转换为秒。
    ar=rawTime.split(“:”).reverse();
    时间=parseFloat(ar[0]);
    如果(ar[1])time+=parseInt(ar[1])*60;
    如果(ar[2])time+=parseInt(ar[2])*60*60;
    //计算进度
    
    exec("ffmpeg -i path/to/input.mov path/to/output.flv 1>path/to/output.txt 2>&1");
    
    exec("ffmpeg -i path/to/input.mov path/to/output.flv 1>path/to/output.txt 2>&1");
    
    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);
    
    ffmpeg -i input.avi {arguments}
    
    pv input.avi | ffmpeg -i pipe:0 -v warning {arguments}
    
    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
    
    ffmpeg -v warning -hide_banner -stats ${your_params}
    
    import subprocess
    
    cmd = 'ffpb -i 400MB.mp4 400MB.avi'
    
    process = subprocess.Popen(
        cmd,
        stdout=subprocess.PIPE,
        stderr=subprocess.STDOUT,
        shell=True,
        encoding='utf-8',
        errors='replace'
    )
    
    while True:
        realtime_output = process.stdout.readline()
    
        if realtime_output == '' and process.poll() is not None:
            break
    
        if realtime_output:
            print(realtime_output.strip(), flush=True)
            print('Push status to Redis...')