ffmpeg 抓取視頻截圖,生成小圖片


需要做視頻分享網站,這個在php中查找相關資料后 說是要使用ffmpeg工具,這個工具既提供了命令行驅動的接口(ffmpeg.exe),(從任何支持系統調用的語言中都可以與之交互如:java,php ,.net等),還提供了專門針對php的模塊即 php-ffmpeg.dll (還有配套的幾個其他dll文件)需要添加到 php的ext目錄,在php.ini配置文件中啟用加載該模塊(網上有很多這樣的例子)。但由於我使用的是 php5.3沒有合適的php-ffmpeg所以只能用這種方法了,不過命令行驅動的好處在於 其跨平台(學會命令行)在任何其他語言(如java中)也是很好改造的。所以這個類就這樣誕生了,我只用其中的生成截圖函數,至於視頻轉換,只是用來研究的,太費時了! 生成小圖片也是可以的,但是最好別用(自己算縮放比,還要打開一個進程),我在前面提供了一個生成小圖片的類,可以用那個

ffmpeg工具集(包括其他命令行程序)可在網上下載(google一下第一個一般就是官網),分別有widows版本,和其他nix平台的版本,如果不嫌費事,或者對編譯成dll文件很在行,那么可以編譯源碼。或者下載使用二進制文件

<?php
/**
* @author yiqing95
*?@copyright?Copyright?(c)?2010,yiqing95.gicp.net
* FFmpeg工具類 封裝常用的ffmpeg命令 為使用者提供一個
* 易用的接口,應同時提供靜態類使用方法和類實例使用方法
* 由於時間較緊,未編寫靜態方法,其實下面實例方法稍改造
* 就能提供實例方法。具體命令行用法可參考ffmpeg官方文檔
* 可自行改造,添加方法。
*該類是在 ffmpeg_php.dll無法使用的條件下,逼不得已
* 才這么做的,如果你機器上可以用ffmpeg_php.dll建議用
* 那個,里面的類提供了很好用的方法。
* 其中的視頻轉換,太慢了,我都不做轉換直接用源格式,只用其中
* 的截圖函數而已,jquery 提供的 media插件可播放任何格式,所以
* 沒必要費時的在那轉換,除非強迫使用flv格式
* (另外記着格式轉換時最費cpu的事情,
* 我測試過其中的轉換方法cpu利用率80%以上-----你就別干其他事了)
*
* 使用ffmpeg.exe方式生成視頻截圖代碼(在window2003和windowXP下都好使,我當時就是這樣處理的)
* 注意:在2003下由於存在權限問題,不能直接在php中使用exec調用ffmpeg命令,必須使用COM
* $phpwsh=new COM("Wscript.Shell") or die("Create Wscript.Shell Failed!");
* $exec=$phpwsh->exec("cmd.exe /c ffmpeg.exe -i D:/FFmpeg/test.flv -f image2 -ss 5 -s 120*90 -vframes 1 D:/FFmpeg/test.jpg");
* 本項目中 ffmpeg存放路徑:
*     用法如:
*     $ffUtil = new FFmpegUtil(".././ffmpeg/bin");
*     $ffUtil->convert2flv("Uploads/Videos/me.3gp", "Uploads/Videos/me.gif");
*     $ffUtil->getThumbnailFormVideo("Uploads/Videos/me.3gp","Uploads/Videos/me.gif");
*
*/

class FFmpegUtil{
/**
   *工具安裝的路徑,bin路徑 全稱,不然加載到環境變量也行,
   * 這樣 ffmpegHome 就給個空串不然要給出全稱路徑
   * echo realpath("../.")."\ffmpeg\bin\ffmpeg"; 用realpath函數可求出
   * 當前文件的全稱路徑,這樣來根據ffmpeg存放的位置來構造絕對路徑
   * @var string
   */
private $ffmpegHome;
/**
   * ffmpeg.exe存放目錄路徑
   * 可用命令行的全路徑比如:d://ffmpeg/bin/ffmpeg
   * @var string
   */
private $cmdHome;
public function __construct($ffmpegHome){
   //如果給的路徑中還有:認為是絕對路徑
    if(strpos($ffmpegHome, ":") === false){
      //不含盤符的路徑認為是相對路徑 ,轉為絕對路徑
      $this->ffmpegHome = realpath($ffmpegHome);
    }else{
      //否則給的是絕對路徑,不用轉化
      $this->ffmpegHome = $ffmpegHome;
}
$this->cmdHome = $this->ffmpegHome .DIRECTORY_SEPARATOR ."ffmpeg";
}
/**
* 返回FFmpeg.exe的絕對路徑
* 幫助調試用
* @return string
*/
public function getFFmpegHome(){
return $this->ffmpegHome;
}

public function getFFmpegCmdPath(){
   return $this->cmdHome;
}

/**
* 轉換成flv文件 方法經過測試,理論上的異步不起作用
* @param string $inFile 輸入文件 ,可以是任何ffmpeg支持的視頻格式
* @param string $outFile 輸出文件 ,理論上也可以是ffmpeg支持的格式
*                但這個方法既然都這樣叫了,你就最好給個xxx.flv吧!^-^
* @param $async bool 是否同步,默認是同步的,這樣保證執行完文件后使用$outFile不會出錯
*                不然使用異步方式 該方法不阻塞立即返回但是如果想顯示可能有點問題
*/
public function convert2flv( $inFile, $outFile , $async = false )
{
@unlink( $outFile );
if(!is_file( $inFile )){
     throw new Exception("不是一個文件");
     return;
}
   //realpath(dirname($testFile)).DIRECTORY_SEPARATOR.basename($testFile);
$srcFile = $inFile; //原始文件路徑
$destFile = $outFile; //目標文件路徑

//相對路徑的處理
if(strpos($srcFile, ":") === false){
        $srcFile = realpath(dirname($inFile)).DIRECTORY_SEPARATOR.basename($inFile);
}
   //相對路徑的處理
if(strpos($destFile, ":") === false){
        $destFile = realpath(dirname($outFile)).DIRECTORY_SEPARATOR.basename($outFile);
}

//-ar 參數是控制音頻取樣率的 默認是44100 Hz這個是最高音質(cd音質)
$cmd = $this->cmdHome." -v 0 -i $srcFile -ar 11025 $destFile 2>&1";

//只所以用管道 而不用exec 下面有例子,是因為一個是同步,一個是異步!!!
if($async === false){
    $fh = popen( $cmd, "r" );
    while( fgets( $fh ) ) { /*讀取管道輸出阻塞*/}
    pclose( $fh );
}else{
   //異步執行,方法不阻塞 理論上是實際不是

    pclose(popen( $cmd, "r" ));
    //exec($cmd);
}
}

/**
* 從視頻流獲取截圖
* @param string $videoFile 視頻文件路徑,相對或絕對,但絕對路徑沒測試過
* @param string $thumbImg 目標截圖文件的路徑(全稱),
* @param number $startSecond 從第幾秒截取
* @param number $width 高度
* @param number $height 寬度
* @return bool 沒啥用弄着玩,異常就夠了 ,還沒有學習進程交互的太多知識
*               這里可能出錯的就是轉換出錯,從另一個進程(ffmpeg)管道流中
*               分析錯誤並反饋給用戶,
*/
public function getThumbnailFormVideo( $videoFile, $thumbImg , $startSecond=6 ,$width=400 ,$height=300 )
{
@unlink( $thumbImg );
if(!is_file( $videoFile )){
     throw new Exception("不是一個文件");
     return false;
}

$srcFile = $videoFile; //原始文件路徑
$destFile = $thumbImg; //目標文件路徑

//相對路徑的處理
if(strpos($srcFile, ":") === false){
        $srcFile = realpath(dirname($videoFile)).DIRECTORY_SEPARATOR.basename($videoFile);
}
   //相對路徑的處理
if(strpos($destFile, ":") === false){
        $destFile = realpath(dirname($thumbImg)).DIRECTORY_SEPARATOR.basename($thumbImg);
}

$cmd = $this->cmdHome." -i $srcFile -pix_fmt rgb24 -vframes 1 -ss $startSecond -s {$width}x{$height} $destFile 2>&1";
$fh = popen( $cmd, "r" );
while( fgets( $fh ) ) { }
pclose( $fh );
}

/**
* 從圖片得到小圖標,這里沒有計算等比縮放因子,
* 請自己計算,這只不過是告訴你 ffmpeg可以生成小圖片
* 本類意在轉 flv格式,以及從視流抓取某個楨畫面,無損
* 這個函數只是測試而已,但確實可用,
* 就是高度和寬度要自己給合適了不然圖像會變形!!!
* @param <type> $srcImg
* @param <type> $destImg
* @param <type> $width
* @param <type> $height
* @return <type>
*/
public function getThumbnailFromImg( $srcImg, $destImg,$width=200,$height=150 ){
@unlink( $destImg ); //如果存在先刪掉
//判斷給定的文件是否正常
if(!is_file($srcImg)){
     throw new Exception("不是一個文件");
     return;
}
$srcFile = $srcImg; //原始文件路徑
$destFile = $destImg; //目標文件路徑
//相對路徑的處理
if(strpos($srcFile, ":") === false){
        $srcFile = realpath(dirname($srcFile)).DIRECTORY_SEPARATOR.basename($srcFile);
}
   //相對路徑的處理
if(strpos($destFile, ":") === false){
        $destFile = realpath(dirname($destFile)).DIRECTORY_SEPARATOR.basename($destFile);
}

$cmd = $this->cmdHome." -i $srcFile -pix_fmt rgb24 -s {$width}*{$height} $destFile";
$fh = popen( $cmd, "r" );
//耗盡管道的輸出流
while( fgets( $fh ) ) { }
pclose( $fh );
}

/**
* 使用方法
* @param string $upFile 剛上傳的視頻文件
* @param string $fname 要保存的目標文件(FLV)格式的隨便給個合法的名字,可以不帶flv后綴
* @param String $destDir 要保存的目標文件夾
*/
public function flv_import( $upFile, $fname ,$destDir)
{
$fname = preg_replace( '/\..*$/', '', basename( $fname ) );
$flvpath = "$fname.flv";
$thumbpath = "$fname.gif";


$fname = preg_replace( '/\..*$/', '', basename( $fname ) );
$flvpath = "$fname.flv";
$thumbpath = "$fname.gif";

$this->convert2flv( $upfile, "$destDir".DIRECTORY_SEPARATOR."$flvpath" );
$this->getThumbnailFormVideo( $upfile, "$destDir".DIRECTORY_SEPARATOR."$thumbpath" );

//分別存儲 視頻和音頻文件到某個位置
   /**
    * 'INSERT INTO movies VALUES ( $title, $flvpath, $thumbpath, 300, 200 ))' ;
    */
}
//下面是另一種參考實現 以供參考

//轉換為Flv
private function makeFlv($video_file,$flv_file)
{
//判斷給定的文件是否正常
if(!is_file($video_file)){
return false;
}
//這個是執行消息 需要反饋的話弄個實例變量即可,一般沒用
$flv_msg;
    $flv_cmd="ffmpeg -i ".$video_file." ".$flv_file;
    exec($flv_cmd,$flv_msg);
}
//創建flv視頻的圖片
/**
* 參數意義自明
* @param <type> $flv_file
* @param <type> $flv_pic_file
* @param <type> $width
* @param <type> $height
* @return <type>
*/
private function makeFlvPic($flv_file,$flv_pic_file,$width=350,$height=240)
{
//判斷給定的文件是否正常
if(!is_file($video_file)){
return false;
}
$flv_msg;
    $flv_pic_cmd="ffmpeg -i ".$flv_file.
       " -y -f image2 ".
       " -ss 1 ".
       " -t 0.001 ".
       " -s {$width}x{$height} ".$flv_pic_file;
    exec($flv_pic_cmd,$flv_msg);
}

}
/*
*附上ffmpeg.exe參數說明:
ffmpeg -i "e:\test.flv" -f image2 -ss 5 -s 120*90 -vframes 1 "e:\test.jpg"
ffmpeg // 命令
-i "f:\test.flv" // 輸入文件
-f image2 -ss 6 -s 120*90 -vframes 1
// 可選參數: -f表示輸出文件格式、
// -ss表示相對於文件開始處的時間偏移值, 即從6秒出開始截圖
// -s表示截圖的的大小
// -vframes表示截圖的楨數
"f:\test.jpg" // 輸出文件
上面的意思就是,從test.flv中的第5秒開始截1幀大小為120*90保存成名為test.jpg的圖片
*
*
*/
?>

多媒體插件下載:http://www.open-lib.com/html/Type/172-1.html

注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
  © 2014-2022 ITdaan.com 联系我们: