Programing

ffmpeg가 진행률 표시 줄을 표시 할 수 있습니까?

lottogame 2021. 1. 7. 07:33
반응형

ffmpeg가 진행률 표시 줄을 표시 할 수 있습니까?


ffmpeg를 사용하여 .avi 파일을 .flv 파일로 변환하고 있습니다. 파일 변환에 시간이 오래 걸리므로 진행률 표시 줄을 표시하고 싶습니다. 누군가가 같은 방법으로 나를 안내해 주시겠습니까?

나는 ffmpeg가 어떻게 든 텍스트 파일에 진행 상황을 출력해야하고 ajax 호출을 사용하여 읽어야한다는 것을 알고 있습니다. 그러나 진행 상황을 텍스트 파일로 출력하기 위해 ffmpeg를 어떻게 얻습니까?

대단히 감사합니다.


나는 이것을 며칠 동안 가지고 놀았습니다. 그 "ffmpegprogress"가 도움이되었지만 내 설정 작업을하기가 매우 어려웠고 코드를 읽기가 어려웠습니다.

ffmpeg의 진행 상황을 표시하려면 다음을 수행해야합니다.

  1. 응답을 기다리지 않고 PHP에서 ffmpeg 명령을 실행하십시오 (나에게 이것은 가장 어려운 부분이었습니다)
  2. ffmpeg에게 출력을 파일로 보내도록 지시하십시오.
  3. 프런트 엔드 (AJAX, Flash 등)에서 해당 파일을 직접 누르거나 ffmpeg의 출력에서 ​​진행 상황을 가져올 수있는 php 파일을 누르십시오.

각 부분을 해결 한 방법은 다음과 같습니다.

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"스크립트는 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. 프런트 엔드에서 output.txt 파일의 위치를 ​​제공하는 php 스크립트를 호출합니다. 그러면 해당 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;

이것이 누군가를 돕기를 바랍니다.


기사 가 문제를 해결하는 방법에 대해 설명 러시아어는.

요점은 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--

FFmpeg는 미디어 데이터를 출력하기 위해 stdout을 사용하고 로깅 / 진행 정보를 위해 stderr를 사용합니다. stderr를 파일 또는 처리 할 수있는 프로세스의 stdin으로 리디렉션하면됩니다.

유닉스 쉘을 사용하면 다음과 같습니다.

ffmpeg {ffmpeg arguments} 2> logFile

또는

ffmpeg {ffmpeg arguments} 2| processFFmpegLog

어쨌든 ffmpeg를 별도의 스레드 또는 프로세스로 실행해야합니다.


pipeview 명령을 사용하면 매우 간단합니다. 이렇게하려면 변환

ffmpeg -i input.avi {arguments}

...에

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

코딩을 할 필요가 없습니다!


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

javascript는 PHP에 [1] 변환을 시작하고 [2]를 수행하도록 지시해야합니다.


[1] php : 변환 시작 및 파일에 상태 쓰기 (위 참조) :

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

두 번째 부분 에서는 파일을 읽기 위해 자바 스크립트 만 필요 합니다. 다음 예제는 AJAX에 대해 dojo.request를 사용하지만 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);

안타깝게도 ffmpeg그 자체로는 여전히 진행률 표시 줄을 표시 할 수 없습니다. 또한 앞서 언급 한 bash 또는 python 기반 stop-gap 솔루션 중 상당수가 오래되고 작동하지 않게되었습니다.

따라서 새로운 ffmpeg-progressbar-cli 를 사용해 보는 것이 좋습니다 .

ffmpeg-progressbar-cli screencast

It's a wrapper for the ffmpeg executable, showing a colored, centered progress bar and the remaining time.

Also, it's open-source, based on Node.js and actively developed, handling most of the mentioned quirks (full disclosure: i'm its current lead developer).


Calling php's system function blocks that thread, so you'll need to spawn off 1 HTTP request for performing the conversion, and another polling one for reading the txt file, that's being generated.

Or, better yet, clients submit the video for conversion and then another process is made responsible for performing the conversion. That way the client's connection won't timeout while waiting for the system call to terminate. Polling is done in the same way as above.


Had problems with the second php part. So I am using this instead:

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

Outputs perfectly.

Would like to see something for multiple uploads for another progress bar. This passing for the current file for one percentage. Then an overall progress bar. Almost there.

Also, if people are having a hard time getting:

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

To work.

Try:

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

"1> path" to "1>path" OR "2> path" to "2>path"

Took me awhile to figure it out. FFMPEG kept failing. Worked when I changed to no space.


If you just need hide all info and show default progress like ffmpeg in last line, you can use -stats option:

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

ReferenceURL : https://stackoverflow.com/questions/747982/can-ffmpeg-show-a-progress-bar

반응형