服务器之家

服务器之家 > 正文

PHP调用ffmpeg对视频截图并拼接脚本

时间:2019-10-26 14:52     来源/作者:lijiao

PHP脚本调用ffmpeg对视频截图并拼接,供大家参考,具体内容如下

PHP调用ffmpeg对视频截图并拼接脚本

目前支持MKV,MPG,MP4等常见格式的视频,其他格式有待测试

12P 一张截图平均生成时间 1.64s 100个视频,大概需要2分半左右

9P 一张截图平均生成时间 1.13s 100个视频,大概需要2分钟左右

6P 一张截图平均生成时间 0.86s 100个视频,大概需要1分半左右

3P 一张截图平均生成时间 0.54s 100个视频,大概需要1分钟左右

  1. <?php  
  2. define('DS', DIRECTORY_SEPARATOR);  
  3. date_default_timezone_set("Asia/Shanghai");  
  4. class FileLoader  
  5. {  
  6.   //路径变量  
  7.   private $rootdir    = '';  
  8.   private $tmp      = "tmp";      //tmp 目录  
  9.   private $source     = "mpg";      //source 目录  
  10.   private $destination  = "screenshoot";  //目标截图路径  
  11.   private $emptyImageName = "empty.jpg";   //合成的背景图  
  12.   //文件数组   
  13.   private $maxShoots   = 12;        //最大的截图数  
  14.   private $videoInfo   = NULL;  
  15.   private $files     = array();     //文件数  
  16.   private $fileArray   = array();       
  17.   private $extensionArray = array("mpg","mkv","mp4","avi","3gp","mov");  //支持的格式  
  18.   private $timeArray   = array("00:00:10","00:00:20","00:00:30","00:01:00","00:01:30","00:02:00","00:02:30","00:03:00","00:03:30","00:03:40","00:03:50","00:04:00");  
  19.   //统计变量  
  20.   private $timeStart   = 0;  
  21.   private $timeEnd    = 0;  
  22.   private $fileCount   = 0;  
  23.   private $successCount  = 0;  
  24.   private $failedCount  = 0;  
  25.      
  26.   /**  
  27.   *  初始化信息  
  28.   */  
  29.    function __construct()  
  30.   {  
  31.     file_put_contents("log.txt","");  
  32.     $this->rootdir = dirname(__FILE__);    
  33.     $count = count($this->timeArray);  
  34.            
  35.     for($i=1;$i<=$count;$i++)  
  36.     {  
  37.       $ii = $i-1;  
  38.       $this->fileArray[$ii] = $this->tmp.DS.$i.".jpg";  
  39.     }  
  40.   }  
  41.       
  42.   /**  
  43.   *  当前时间,精确到小数点  
  44.   */ 
  45.   private static function microtime_float()  
  46.   {  
  47.     list($usec, $sec)= explode(" ", microtime());  
  48.     return ((float)$usec + (float)$sec);  
  49.   }  
  50.      
  51.   /**  
  52.   *  00:00:00 时间转秒  
  53.   */ 
  54.   private static function timeToSec($time)   
  55.   {  
  56.      $p = explode(':',$time);  
  57.      $c = count($p);  
  58.      if ($c>1)  
  59.      {  
  60.         $hour  = intval($p[0]);  
  61.         $minute = intval($p[1]);  
  62.         $sec   = intval($p[2]);  
  63.      }  
  64.      else 
  65.      {  
  66.         throw new Exception('error time format');  
  67.      }  
  68.      $secs = $hour * 3600 + $minute * 60 + $sec;  
  69.      return $secs;   
  70.   }  
  71.      
  72.   /**  
  73.   *  00:00:00 时间转秒  
  74.   */ 
  75.   private static function secToTime($time)   
  76.   {  
  77.        
  78.     $hour = floor($time/3600);  
  79.     $min = floor(($time - $hour * 3600)/60);  
  80.     $sec = $time % 60;  
  81.     $timeStr = sprintf("%02d:%02d:%02d",$hour,$min,$sec);  
  82.     return $timeStr;     
  83.   }  
  84.       
  85.   /**  
  86.   *  获取全部文件  
  87.   */ 
  88.    private function getFiles($dir)  
  89.   {  
  90.     $files = array();  
  91.     $dir = rtrim($dir, "/\\") . DS;  
  92.     $dh = opendir($dir);  
  93.     if ($dh == false) { return $files; }  
  94.    
  95.     while (($file = readdir($dh)) != false)  
  96.     {  
  97.       if ($file{0} == '.') { continue; }  
  98.    
  99.       $path = $dir . $file;  
  100.       if (is_dir($path))  
  101.       {  
  102.         $files = array_merge($files, $this->getFiles($path));  
  103.       }  
  104.       elseif (is_file($path))  
  105.       {  
  106.         $files[] = $path;  
  107.       }  
  108.     }  
  109.     closedir($dh);  
  110.     return $files;  
  111.   }  
  112.      
  113.   /**  
  114.   *  搜索路径  
  115.   */ 
  116.   public function searchDir($sourcePath = NULL)  
  117.   {  
  118.        
  119.     $this->timeStart = $this->microtime_float();  
  120.    
  121.     if ($sourcePath)   
  122.     {  
  123.       $this->rootdir = $sourcePath;  
  124.     }   
  125.        
  126.     if (file_exists($this->rootdir) && is_dir($this->rootdir))  
  127.     {  
  128.       $this->files = $this->getFiles($this->rootdir.DS.$this->source);        
  129.     }  
  130.      
  131.     $this->fileCount = count($this->files);  
  132.      
  133.     foreach ($this->files as $path)  
  134.     {  
  135.       $fi = pathinfo($path);  
  136.       $flag = array_search(strtolower($fi['extension']),$this->extensionArray);  
  137.       if (!$flag) continue;  
  138.       $this->getScreenShoot(basename($path));  
  139.     }  
  140.        
  141.     $this->timeEnd = $this->microtime_float();  
  142.     $time = $this->timeEnd - $this->timeStart;  
  143.        
  144.     if($this->fileCount > 0)  
  145.     {  
  146.       $str = sprintf("[TOTAL]: Cost Time:%8s | Total File:[%d] | Successed:[%d] | Failed:[%d] | Speed:%.2fs per file\n",$this->secToTime($time),$this->fileCount,$this->successCount,$this->failedCount,$time/$this->fileCount);  
  147.       file_put_contents("log.txt",$str,FILE_APPEND);   
  148.     }  
  149.     else 
  150.     {  
  151.       $str = sprintf("[TOTAL]: Cost Time:%8s | Total File:[%d] | Successed:[%d] | Failed:[%d] | Speed:%.2fs per file\n",$this->secToTime($time),$this->fileCount,$this->successCount,$this->failedCount,0);  
  152.       file_put_contents("log.txt",$str,FILE_APPEND);  
  153.     }    
  154.        
  155.   }  
  156.      
  157.   /**  
  158.   *  获取视频信息  
  159.   */ 
  160.   private function getVideoInfo($file){  
  161.       $re = array();  
  162.       exec(".".DS."ffmpeg -i {$file} 2>&1", $re);  
  163.       $info = implode("\n", $re);  
  164.          
  165.       if(preg_match("/No such file or directory/i", $info))  
  166.       {  
  167.         return false;  
  168.       }  
  169.          
  170.       if(preg_match("/Invalid data/i", $info)){  
  171.         return false;  
  172.       }  
  173.        
  174.       $match = array();  
  175.       preg_match("/\d{2,}x\d+/", $info, $match);  
  176.       list($width, $height) = explode("x", $match[0]);  
  177.        
  178.       $match = array();  
  179.       preg_match("/Duration:(.*?),/", $info, $match);  
  180.       if($match)  
  181.       {  
  182.         $duration = date("H:i:s", strtotime($match[1]));  
  183.       }else 
  184.       {  
  185.         $duration = NULL;  
  186.       }    
  187.            
  188.       $match = array();  
  189.       preg_match("/bitrate:(.*kb\/s)/", $info, $match);  
  190.       $bitrate = $match[1];  
  191.        
  192.       if(!$width && !$height && !$duration && !$bitrate){  
  193.         return false;  
  194.       }else{  
  195.         return array(  
  196.           "file" => $file,  
  197.           "width" => $width,  
  198.           "height" => $height,  
  199.           "duration" => $duration,  
  200.           "bitrate" => $bitrate,  
  201.           "secends" => $this->timeToSec($duration)  
  202.         );  
  203.       }  
  204.     }  
  205.      
  206.      
  207.      
  208.   /**  
  209.   *  设置截图时间  
  210.   */  
  211.   private function setShootSecends($secends,$useDefault = NO)  
  212.   {    
  213.        
  214.     if($useDefault)  
  215.     {  
  216.       if($secends<18)  
  217.       {  
  218.         $time = 1;  
  219.       }else 
  220.       {  
  221.         $time = 5;  
  222.       }    
  223.          
  224.       $range = floor(($secends - $time)/ ($this->maxShoots));  
  225.       if ($range < 1)   
  226.       {  
  227.         $range = 1;  
  228.       }  
  229.          
  230.       $this->timeArray = array();  
  231.       for($i=0;$i<$this->maxShoots;$i++)  
  232.       {  
  233.         $this->timeArray[$i] = $this->secToTime($time);  
  234.         $time = $time + $range;  
  235.         if ($time > $secends) break;  
  236.       }  
  237.     }  
  238.   }  
  239.      
  240.   /**  
  241.   *  拼接图片  
  242.   */ 
  243.   private function getFixedPhoto($fileName)  
  244.   {  
  245.      
  246.     $target = $this->rootdir.DS.$this->emptyImageName;//背景图片  
  247.     $target_img = Imagecreatefromjpeg($target);  
  248.     $source= array();  
  249.    
  250.     foreach ($this->fileArray as $k=>$v)  
  251.     {  
  252.       $source[$k]['source'] = Imagecreatefromjpeg($v);  
  253.       $source[$k]['size'] = getimagesize($v);  
  254.     }  
  255.    
  256.     $tmpx=5;  
  257.     $tmpy=5;//图片之间的间距  
  258.     for ($i=0; $i< count($this->timeArray); $i++)  
  259.     {    
  260.       imagecopy($target_img,$source[$i]['source'],$tmpx,$tmpy,0,0,$source[$i]['size'][0],$source[$i]['size'][1]);  
  261.       $target_img = $this->setTimeLabel($target_img,$tmpx,$tmpy,$source[$i]['size'][0],$source[$i]['size'][1],$this->timeArray[$i]);    
  262.       $tmpx = $tmpx+ $source[$i]['size'][0];  
  263.       $tmpx = $tmpx+5;  
  264.       if(($i+1) %3 == 0){  
  265.         $tmpy = $tmpy+$source[$i]['size'][1];  
  266.         $tmpy = $tmpy+5;  
  267.         $tmpx=5;  
  268.       }  
  269.     }  
  270.        
  271.     $target_img = $this->setVideoInfoLabel($target_img,$tmpx,$tmpy,$this->videoInfo);  
  272.     Imagejpeg($target_img,$this->rootdir.DS.$this->destination.DS.$fileName.'.jpg');   
  273.   }  
  274.      
  275.   /**  
  276.   *  设置时间刻度标签  
  277.   */ 
  278.   private function setTimeLabel($image,$image_x,$image_y,$image_w,$image_h,$img_text)  
  279.   {  
  280.      imagealphablending($image,true);  
  281.      //设定颜色  
  282.      $color=imagecolorallocate($image,255,255,255);  
  283.      $ttf_im=imagettfbbox(30 ,0,"Arial.ttf",$this->img_text);  
  284.      $w = $ttf_im[2] - $ttf_im[6];   
  285.      $h = $ttf_im[3] - $ttf_im[7];   
  286.      unset($ttf_im);  
  287.         
  288.      $txt_y   =$image_y+$image_h+$h-5;  
  289.      $txt_x   =$image_x+$w+5;  
  290.          
  291.      imagettftext($image,30,0,$txt_x,$txt_y,$color,"Arial.ttf",$img_text);  
  292.      return $image;   
  293.    }  
  294.       
  295.   /**  
  296.   *  设置视频信息标签  
  297.   */ 
  298.    private function setVideoInfoLabel($image,$txt_x,$txt_y,$videoInfo)  
  299.    {  
  300.      imagealphablending($image,true);  
  301.      
  302.      $color=imagecolorallocate($image,0,0,0);  
  303.     
  304.      imagettftext($image,32,0,100,2000+30,$color,"FZLTHJW.ttf","FileName:".basename($videoInfo["file"]));  
  305.      imagettftext($image,32,0,1600,2000+30,$color,"Arial.ttf","Size:".$videoInfo["width"]."x".$videoInfo["height"]);  
  306.      imagettftext($image,32,0,100,2000+120,$color,"Arial.ttf","Duration:".$videoInfo["duration"]);  
  307.      imagettftext($image,32,0,1600,2000+120,$color,"Arial.ttf","Bitrate:".$videoInfo["bitrate"]);  
  308.      return $image;   
  309.   }  
  310.       
  311.   /**  
  312.   *  屏幕截图  
  313.   */ 
  314.   public function getScreenShoot($fileName)  
  315.   {  
  316.     $fi = pathinfo($fileName);  
  317.     $this->videoInfo = $this->getVideoInfo($this->rootdir.DS.$this->source.DS.$fileName);  
  318.     if($this->videoInfo)  
  319.     {  
  320.       $this->setShootSecends($this->videoInfo["secends"]);  
  321.        
  322.       for ($i=0; $i< count($this->timeArray); $i++ )  
  323.       {  
  324.         $cmd=".".DS."ffmpeg -ss ". $this->timeArray[$i] ." -i ". $this->rootdir.DS.$this->source.DS.$fileName ." -y -f image2 -s 720*480 -vframes 1 ".$this->rootdir.DS.$this->fileArray[$i];    
  325.         exec($cmd,$out,$status);    
  326.       }  
  327.       $this->getFixedPhoto($fileName);  
  328.          
  329.       $str = sprintf("[%s]:OK...........[%s][%2dP]%-30s\n",date("y-m-d h:i:s",time()),$this->videoInfo["duration"],count($this->timeArray),$fileName);  
  330.       file_put_contents("log.txt",$str,FILE_APPEND);  
  331.       $this->successCount += 1;  
  332.     }else 
  333.     {  
  334.       $str = sprintf("[%s]:FAILED.................................[%s][%2dP]%-30s\n",date("y-m-d h:i:s",time()),$this->videoInfo["duration"],count($this->timeArray),$fileName);  
  335.       file_put_contents("log.txt",$str,FILE_APPEND);   
  336.       $this->failedCount += 1;  
  337.     }  
  338.   }  
  339.      
  340.   /**  
  341.   *  TODO:  
  342.   *  截取图片,  
  343.   *  需要配置ffmpeg-php,比较麻烦,  
  344.   *  但是这个类确实挺好用的。  
  345.   */ 
  346.   public function getScreenShoot2($fileName)  
  347.   {  
  348.     if(extension_loaded('ffmpeg')){//判断ffmpeg是否载入   
  349.       $mov = new ffmpeg_movie($this->rootdir.DS.$this->source.DS.$fileName);//视频的路径   
  350.       $count = $mov->getFrameCount();  
  351.       $ff_frame = $mov->getFrame(floor($count/2));   
  352.       if($ff_frame)  
  353.       {  
  354.         $gd_image = $ff_frame->toGDImage();       
  355.         $img=$this->rootdir.DS."test.jpg";//要生成图片的绝对路径   
  356.         imagejpeg($gd_image, $img);//创建jpg图像   
  357.         imagedestroy($gd_image);//销毁一图像   
  358.       }  
  359.     }else{   
  360.       echo "ffmpeg没有载入";   
  361.     }   
  362.   }  
  363. }  
  364.    
  365. $fileLoader = new FileLoader();  
  366. $fileLoader->searchDir();  
  367. ?>  

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

相关文章

热门资讯

玄元剑仙肉身有什么用 玄元剑仙肉身境界等级划分
玄元剑仙肉身有什么用 玄元剑仙肉身境界等级划分 2019-06-21
男生常说24816是什么意思?女生说13579是什么意思?
男生常说24816是什么意思?女生说13579是什么意思? 2019-09-17
华为nova5pro和p30pro哪个好 华为nova5pro和华为p30pro对比详情
华为nova5pro和p30pro哪个好 华为nova5pro和华为p30pro对比详情 2019-06-22
超A是什么意思 你好a表达的是什么
超A是什么意思 你好a表达的是什么 2019-06-06
抖音撒撒累累是什么歌 撒撒累累张艺兴歌曲名字
抖音撒撒累累是什么歌 撒撒累累张艺兴歌曲名字 2019-06-05
返回顶部