ffmpeg能显示进度条吗?
我使用ffmpeg将.avi文件转换为.flv文件。 由于转换文件需要很长时间,我想显示一个进度条。 有人可以请指导我如何去相同的。
我知道,ffmpeg莫名其妙地必须在文本文件中输出进度,我必须使用ajax调用来读取它。 但是,我如何得到ffmpeg输出到文本文件的进度?
非常感谢你。
我一直在玩这个几天。 这个“ffmpegprogress”的东西有帮助,但是很难和我一起工作,而且很难阅读代码。
为了显示ffmpeg的进度,您需要执行以下操作:
- 从php运行ffmpeg命令,而不用等待响应(对我来说,这是最难的部分)
- 告诉ffmpeg将其输出发送到一个文件
- 从前端(AJAX,Flash,不pipe)直接打这个文件还是一个可以从ffmpeg的输出中取出进度的php文件。
以下是我解决每个部分的方法:
我从“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部分解决了。
顺便说一句 – 有人build议使用“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> path / to / 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;
希望这有助于某人。
FFmpeg使用stdout输出媒体数据,使用stderr输出日志/进度信息。 你只需要将stderrredirect到一个文件或者一个能够处理它的进程的标准input。
有了unixshell,这就像是:
ffmpeg {ffmpeg arguments} 2> logFile
要么
ffmpeg {ffmpeg arguments} 2| processFFmpegLog
无论如何,你必须运行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--
如果使用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
调用线程的php的系统function块,所以你需要产生一个HTTP请求进行转换,另一轮询读取正在生成的txt文件。
或者,更好的是,客户提交video进行转换,然后由另一个过程负责执行转换。 这样客户端的连接在等待系统调用终止的时候不会超时。 轮询以与上述相同的方式完成。
有第二个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");
上class。
尝试:
exec("ffmpeg -i path/to/input.mov path/to/output.flv 1>path/to/output.txt 2>&1");
“ 1>path ”到“ 1>path ”或“ 2>path ”到“ 2>path ”
花了一段时间弄清楚了。 FFMPEG保持失败。 工作时,我改变了没有空间。
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或者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);