laravel5.5集成FFmpeg,redis队列异步视频转码

1、 laravel PHP-FFmpeg 扩展 下载地址: https://github.com/PHP-FFMpeg/PHP-FFMpeg

2、安装方法:

$ composer require php-ffmpeg/php-ffmpeg

3、使用:

安装 redis

$ composer require predis/predis

配置 .env 文件:

QUEUE_DRIVER=redis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=
REDIS_PORT=6379

更改 config/queue.php

'redis' => [
            'driver' => 'redis',
            'connection' => 'default',
            'queue' => '{default}',
            'retry_after' => 36000,//这里设置10个小时转码消耗的最大时间
//注意:
//参数项 --timeout 的值应该是中小于配置项retry_after的值
//这是为了确保队列进程总在任务重试以前关闭。
//如果 --timeout 比retry_after 大,则你的任务可能被执行两次。

创建任务:

$ php artisan make:job MakeCoding

编辑 App/Jobs/MakeCoding.php :

<?php
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Config;
use App\Models\Element;//你的素材模型 自己定义
class MakeCoding implements ShouldQueue
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
     * The number of seconds the job can run before timing out.
     * @var int
    public $timeout = 30000; //最大超时时间
    public $element_id; //素材id
    public $relapath; //素材路径
    public $chakge_path; //更改后的路径
    public $tries = 3;//最大失败次数
     * Create a new job instance.
     * @return void
    public function __construct($relapath, $element_id)
        $this->relapath    = $relapath;
        $this->element_id  = $element_id;
     * Execute the job.
     * @return void
    public function handle()
        $path              = public_path() . DIRECTORY_SEPARATOR . $this->relapath;//文件保存在public下
        $save_path         = explode('.', $path)[0] . '.mp4';
        $this->chakge_path = explode('.', $this->relapath)[0] . '.mp4';
        $ffmpeg_path       = Config::get("phpFFmpeg.ffmpeg"); //ffmpeg运行的路径
        $ffprobe_path      = Config::get("phpFFmpeg.ffprobe"); //ffprobe运行路径
        $ffmpeg = \FFMpeg\FFMpeg::create(array(
            'ffmpeg.binaries'  => $ffmpeg_path,
            'ffprobe.binaries' => $ffprobe_path,
            'timeout'          => 30000, // The timeout for the underlying process
            'ffmpeg.threads'   => 12, // The number of threads that FFMpeg should use
        $default = ini_get('max_execution_time');//取得php最大超时时间,相应的修改apache或nginx的配置文件中的超时时间,和文件上传大小限制
        set_time_limit(-1);//设置成永不超时
        $video  = $ffmpeg->open($path);
        $format = new \FFMpeg\Format\Video\X264('libmp3lame', 'libx264');
        $format->on('progress', function ($video, $format, $percentage) {
            echo $percentage . '% ';//转码进度
            if ($percentage >= 99) {//有的文件转码到99%会认为完成了
          //1、删除源文件 @unlink($path); //2、修改数据库文件路径信息转码完成,自己定义 @Element::where('id', $this->element_id)->update(['path' => $this->chakge_path, 'trans_status' => '1']); $video->save($format, $save_path); set_time_limit($default);//恢复php的最大超时时间

添加配置文件 config/phpFFmpeg.php:

<?php
    //windows
    return [
        'ffmpeg' =>'C:/xampp/php/ffmpeg/bin/ffmpeg.exe',
        'ffprobe' =>'C:/xampp/php/ffmpeg/bin/ffprobe.exe',   
 /*linux
    return [
        'ffmpeg' =>'/usr/local/ffmpeg/bin/ffmpeg',
        'ffprobe' =>'/usr/local/ffmpeg/bin/ffprobe',   
    ];*/
  • FFmpeg执行文件 linux版下载地址:http://pan.baidu.com/s/1c2GUOgC  提取码:n61c
  • 在你的项目目录鼠标 Shift+右键 在此打开命令行 运行队列进程:

    $ php artisan queue:work

    编辑你的上传文件的 Controller:

    <?php
        use App\Jobs\MakeCoding;
        //这里写伪代码
        //1、上传文件
        //2、保存到数据库
        //3、如果是上传视频,并且不是mp4格式的,那么进行转码
        //4、发送任务 参数:素材的真实保存路径和素材id
        if ($type == 'video' && 'mp4' != $ext) {
            dispatch(new MakeCoding($realpath, $element_id));
    

     更多使用教程,看github文档说明。

    下面是整理的一个 IOHelper :

      1 <?php
      2 /**
      3  * IO公共操作类
      4  */
      5 class IOHelper
      7     /**
      8      * 获取音频或视频信息
      9      * @param  [string] $path     [文件路径]
     10      * @return [array]  result    [文件信息]
     11      */
     12     public static function getMediaInfo($path)
     13     {
     14         $result = array();
     15         $cmd    = sprintf('/home/samba/ffmpeg/bin/ffprobe -v quiet -print_format json -show_format -show_streams "%s" 2>&1', $path);//你的FFMpeg执行文件地址自己定义
     16         exec($cmd, $arr);
     17         $obj = json_decode(implode($arr));
     19         if ($obj && property_exists($obj, "format")) {
     20             $type           = $obj->format->format_name;
     21             $result["Type"] = $type;
     23             if (false === stripos($type, "_pipe")) {
     24                 $result["Duration"]        = (int) $obj->format->duration; //持续时间
     25                 $result["DisplayDuration"] = self::formatTime($obj->format->duration); //格式化后的时间
     26                 $result["byte_size"]       = (int) $obj->format->size; //字节大小
     27                 $result["format_size"]     = self::formatSize($obj->format->size); //格式化后的大小
     28             }
     30             if ("mp3" != $type) {
     31                 foreach ($obj->streams as $stream) {
     32                     //视频流和音频流
     33
    
    
    
    
        
                         if ($stream->codec_type == "video") {
     34                         //找到视频流
     35                         $result["Resolution"] = sprintf("%sx%s", @$stream->width, @$stream->height); //分辨率
     36                         break;
     37                     }
     38                 }
     39             }
     40         }
     42         return $result;
     43     }
     45     /**
     46      * 返回格式化后的时间格式
     47      */
     48     private static function formatTime($sec)
     49     {
     50         $sec           = $sec % (24 * 3600);
     51         $hours         = floor($sec / 3600);
     52         $remainSeconds = $sec % 3600;
     53         $minutes       = floor($remainSeconds / 60);
     54         $seconds       = intval($sec - $hours * 3600 - $minutes * 60);
     56         return sprintf("%s:%s:%s", str_pad($hours, 2, "0", STR_PAD_LEFT), str_pad($minutes, 2, "0", STR_PAD_LEFT), str_pad($seconds, 2, "0", STR_PAD_LEFT));
     57     }
     59     /**
     60      * 返回格式化后的文件尺寸
     61      */
     62     public static function formatSize($bytes, $decimals = 2)
     63     {
     64         $size       = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];
     65         $factor     = floor((strlen($bytes) - 1) / 3);
     66         $byte       = $bytes / pow(1024, $factor);
     67         $formatSize = floor($byte * 100) / 100;
     69         return sprintf("%.{$decimals}f", $formatSize) . @$size[$factor];
     70     }
     71     /**
     72      * *
     73      * @return boolean [是否是windows系统]
     74      */
     75     public static function isWin()
     76     {
     77         if (substr(PHP_OS, 0, 3) === 'WIN') {
     78             return true;
     79         }
     80         return false;
     81     }
     82     /**
     83      * 判断目录是否为空
     84      * @param  [string] $dir [目录路径]
     85      * @return [bool]        [true/false]
     86      */
     87     public static function isEmptyDir($dir)
     88     {
     89         if (!is_dir($dir)) {
     90             return "$dir is not a directory";
     91         }
     92         $handle = opendir($dir);
     93         while (($file = readdir($handle)) !== false) {
     94             if ($file != "." && $file != "..") {
     95                 closedir($handle);
     96                 return false;
     97             }
     98         }
     99         closedir($handle);
    101         return true;
    102     }
    104     /**
    105      * 删除一个目录
    106      * @param  [string] $dir [目录路径]
    107      * @return [type]      [description]
    108      */
    109     public static function removeDir($dir) 
    110     {
    111         if (! is_dir($dir)) 
    112         {
    113             return "$dir is not a directory";
    114         }
    115         $handle = opendir($dir);
    116         while (($file = readdir($handle)) !== false) 
    117         {
    118             if ($file != "." && $file != "..") 
    119             {
    120                 is_dir("$dir/$file") ? removeDir("$dir/$file") : unlink("$dir/$file");
    121             }
    122         }
    123         if (readdir($handle) == false) 
    124         {
    125             closedir($handle);
    126             rmdir($dir);
    127         }
    128     }
    130     /**
    131      * 生成一个文件
    132      * @param  [type] $path [description]
    133      * @param  [type] $data [description]
    134      * @return [type]       [description]
    135      */
    136     public static function writeFile ($path, $data)
    137     {
    138         //$dir = str_replace('/'.basename($path),'',$path);
    139         //@mkdir($dir,0777,true);
    140         $fp = fopen($path, 'w');
    141         if(fwrite($fp, $data)){
    142             return true;
    143         }
    144         fclose($fp);
    145         return false;
    146     }
    148     /**
    149      * 自定义获取文件md5
    150      * @param  [string] $file [文件路径]
    151      * @return [string]       [md5值]
    152      */
    153     public static function md5($file)
    154     {
    155         if(! $fp = fopen($file, 'r')){
    156             return false;
    157         }
    159         $size = filesize($file);
    160         // 1024*1024 = 1MB
    161         if (1048576 > $size) {
    162             return  md5_file($file);
    163         }
    165         $file_path = '';
    166         $file_path_temp = '';
    168         fseek($fp, 0); // 1 - 2012
    169         $file_path .= fread($fp, 2012);
    171         fseek($fp, $size / 2 - 1999); // size/2 - 1999
    172         $file_path .= fread($fp, 1999);
    174         fseek($fp, -2010, SEEK_END); // -2010
    175         $file_path .= fread($fp, 2010);
    177         fclose($fp);
    179         //自己定义你的临时文件路径
    180         $file_path_temp = sprintf("%s/%s_%s", public_path(), time(), basename($file));
    182         if(! $fp_temp = fopen($file_path_temp, "wb")){
    183             return false;
    184         }
    185         //写入截取的文件片段
    186         fwrite($fp_temp, $file_path);
    187         fclose($fp_temp);
    189         $md5 = md5_file($file_path_temp);
    190         unlink($file_path_temp);
    192         return $md5;
    193     }
    194 }