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:
<?phpnamespace 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; //素材idpublic $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//windowsreturn ['ffmpeg' =>'C:/xampp/php/ffmpeg/bin/ffmpeg.exe','ffprobe' =>'C:/xampp/php/ffmpeg/bin/ffprobe.exe', ];/*linuxreturn ['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:
<?phpuse App\Jobs\MakeCoding;//这里写伪代码//1、上传文件//2、保存到数据库//3、如果是上传视频,并且不是mp4格式的,那么进行转码//4、发送任务 参数:素材的真实保存路径和素材idif ($type == 'video' && 'mp4' != $ext) {dispatch(new MakeCoding($realpath, $element_id));}
完成~!
更多使用教程,看github文档说明。
下面是整理的一个 IOHelper :
1 <?php 2 /** 3 * IO公共操作类 4 */ 5 class IOHelper 6 { 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)); 18 19 if ($obj && property_exists($obj, "format")) { 20 $type = $obj->format->format_name; 21 $result["Type"] = $type; 22 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 } 29 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 } 41 42 return $result; 43 } 44 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); 55 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 } 58 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; 68 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); 100 101 return true; 102 } 103 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 } 129 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 } 147 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 } 158 159 $size = filesize($file); 160 // 1024*1024 = 1MB 161 if (1048576 > $size) { 162 return md5_file($file); 163 } 164 165 $file_path = ''; 166 $file_path_temp = ''; 167 168 fseek($fp, 0); // 1 - 2012 169 $file_path .= fread($fp, 2012); 170 171 fseek($fp, $size / 2 - 1999); // size/2 - 1999 172 $file_path .= fread($fp, 1999); 173 174 fseek($fp, -2010, SEEK_END); // -2010 175 $file_path .= fread($fp, 2010); 176 177 fclose($fp); 178 179 //自己定义你的临时文件路径 180 $file_path_temp = sprintf("%s/%s_%s", public_path(), time(), basename($file)); 181 182 if(! $fp_temp = fopen($file_path_temp, "wb")){ 183 return false; 184 } 185 //写入截取的文件片段 186 fwrite($fp_temp, $file_path); 187 fclose($fp_temp); 188 189 $md5 = md5_file($file_path_temp); 190 unlink($file_path_temp); 191 192 return $md5; 193 } 194 }