2009-04-14 156 views
45

我正在使用ffmpeg將.avi文件轉換爲.flv文件。由於轉換文件需要很長時間,我想要顯示進度條。有人能指導我如何去做同樣的事情嗎?ffmpeg可以顯示進度條嗎?

我知道,ffmpeg莫名其妙地必須在文本文件中輸出進度,我必須使用ajax調用來讀取它。但是,如何讓ffmpeg將進度輸出到文本文件?

非常感謝。

回答

1

調用線程的php系統函數塊,因此您需要生成一個HTTP請求來執行轉換,另一個用於讀取正在生成的txt文件的輪詢。

或者更好的是,客戶提交視頻進行轉換,然後另一個進程負責執行轉換。這樣客戶端的連接在等待系統調用終止時不會超時。輪詢以與上述相同的方式完成。

+3

做,但爲什麼要使用一個文本文件?有一個更好的方法。你不需要爲每個上傳文件創建一個新的文本文件嗎? – Scarface 2011-06-25 23:10:51

19

FFmpeg使用stdout輸出媒體數據和stderr記錄/進度信息。你只需將stderr重定向到一個文件或者一個能夠處理它的進程的標準輸入。

過UNIX shell這是一樣的東西:

ffmpeg {ffmpeg arguments} 2> logFile 

ffmpeg {ffmpeg arguments} 2| processFFmpegLog 

無論如何,你必須運行的ffmpeg作爲一個單獨的線程或進程。

+2

如果我問這個問題,我會很樂意接受你的答案。 – Riduidel 2010-11-30 08:02:44

+0

@mouviciel,你知道我如何在.NET中實現這一點嗎?我試過你說的,它不打印出一個文件,當文件改變時我怎麼能得到通知? – Shimmy 2011-09-04 23:43:17

+2

對不起,我不知道.NET。 – mouviciel 2011-09-05 11:12:51

20

俄語中有一個article,它描述瞭如何解決您的問題。

重點是在編碼前捕獲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-- 
+3

我去了希望與俄羅斯有點糾結的文章,但它卻令人驚訝地容易閱讀。 – 2016-02-19 01:53:12

+0

這應該被接受回答 – 2016-11-20 21:37:11

26

我一直在玩這個幾天。這個「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部分解決了。

順便說一句 - 有人建議使用「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>路徑/到/ output.txt的2> & 1」。我不是命令行專家,但從我可以告訴這行說「發送正常輸出到這個文件,併發送錯誤到同一個地方」。看看這個URL的更多信息:http://tldp.org/LDP/abs/html/io-redirection.html

3. 從前端調用一個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; 

希望這可以幫助別人。

1

第二個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"); 

工作。

嘗試:

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

1>路徑」 到 「1>路徑」 OR 「2>路徑」 到 「2>路徑

我花了有一段時間想出來。 FFMPEG保持失敗。工作時,我改變了沒有空間。

1

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或香草或什麼,以及:

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

這很簡單,如果你使用pipeview命令。要做到這一點,變換

ffmpeg -i input.avi {arguments} 

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

無需進入編碼!

5

您可以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