PHP使用ffmpeg压缩视频

简介:视频压缩,PHP实现在线压缩视频,PHP开发视频压缩在线

PHP本身没有提供压缩视频的函数,但是我们可以利用ffmpeg这个工具对视频进行压缩。

视频压缩主要分为五个类别进行压缩:

  1. 压缩分辨率:例如视频的分辨率是720x1280,可以压缩分辨率为360x640
  2. 压缩帧数:通过压缩视频的帧数来压缩,可以压缩视频的帧数到1秒10帧甚至1秒5帧
  3. 压缩比特率:压缩视频的码率,要求不高的话可以压缩到700kb/s左右
  4. 压缩音频码率:压缩视频的音频码率,一般可以压缩到128kb/s或者64kb/s
  5. 修改视频编码方式:现在普遍是H.264编码(ffmpeg对应的是libx264),已经是最优编码。

FFMPEG获取视频的信息

我们可以直接使用 ffmpeg 中的 ffprobe 来获取视频的信息。命令如下:

ffprobe -i video.mp4

输出信息如下:

如果是PHP代码执行上述命令,我们很难获取自己想要的信息。ffmpeg提供了格式化的参数,可以利用这一点获取json格式的信息。命令参数如下:

ffprobe -v error -show_streams -print_format json 视频路径

上面的参数一个都不能少哦!

-show_streams:表示返回视频流相关的信息,比如宽高,比特率,帧率等

-print_format:指定返回的格式

返回的是一个二维数组,具体的大家可以自己执行一下看看具体的内容。现在就可以遍历二维数组获取视频相关信息了

FFMPEG视频压缩

先来看看视频压缩的命令:

ffmpeg -hwaccel auto -r 20 -i 视频路径 -c:v libx264 -b:v 862k -s 512x512 -b:a 64k -y 输出视频的路径

注意:上面获取视频信息是ffprobe,这里是ffmpeg 

输出路径不能与源视频的路径相关,否则会直接报错

-r 指定输出视频的帧率,即每一秒显示的帧数

-c:v 指定视频编码格式。这个是-vcodec的缩写或者说别名,推荐:libx264

-b:v 指定视频的比特率

-s 指定输出视频的宽高即分辨率。格式:宽x注意宽和高必须是2的倍数,否则会报错

-b:a 指定音频码:64k 

有了执行命令,我们就可以通过PHP去执行这些命令,从而实现视频压缩。PHP中有很多执行shell脚本命令的参数,我个人使用的是 shell_exec 

如果使用shell_exec函数执行命令时,可能会报错,提示该函数已被禁用。我们可以修改php的配置文件,找到 disable_functions 配置项,将shell_exec函数移除重启php-fpm即可

关于php配置文件,我们可以通过 php --ini 查看配置文件具体路径

下面是我个人封装的一个ffmpeg视频压缩的类库,大家可以借鉴一下:

<?php

class FFmpegHelper
{
    protected static $ffmpeg_path = 'ffmpeg';
    protected static $ffprobe_path = 'ffprobe';

    protected static function execShell($command)
    {
        if (function_exists('shell_exec')){
            return shell_exec($command);
        }
        throw new \Exception('无法执行命令行');
    }

    /**
     * 根据最大值计算视频的分辨率
     * @param $width 视频的宽度
     * @param $height 视频的高度
     * @param int $max 视频最大宽高
     * @return array
     */
    public static function getMaxResolution($width,$height,$max=600)
    {
        if(max($width,$height)>$max){
            if($height>$width){
                $width = intval(round($max * $width / $height));
                if($width%2==1) $width++;
                return ['width'=>$width,'height'=>$max];
            }
            $height = intval(round($max * $height / $width));
            if($height%2==1) $height++;
            return ['width'=>$max,'height'=>$height];
        }
        return ['width'=>$width,'height'=>$height];
    }

    /**
     * 获取视频的分辨率信息
     * @param string $video_path 视频路径
     * @return array|null
     * @throws \Exception
     */
    public static function getVideoResolution($video_path)
    {
        $command = "%s -v error -show_streams -print_format json %s";
        $command = sprintf($command,self::$ffprobe_path,$video_path);
        $streamsData = self::execShell($command);
        $streamsInfo = json_decode($streamsData,true);
        // 遍历流信息以找到视频流
        foreach ($streamsInfo['streams'] as $stream) {
            if ($stream['codec_type'] === 'video') {
                // 返回分辨率信息
                return [
                    'width' => $stream['width'], //宽
                    'height' => $stream['height'], //高
                    'bit_rate' => $stream['bit_rate'], //比特率
                    'r_frame_rate' => $stream['r_frame_rate'] //帧率
                ];
            }
        }
        return null;
    }

    /**
     * 视频压缩
     * @param string $source_path 原视频的路径
     * @param string $output_path 压缩后输出视频的路径,不能与原视频路径相同
     * @return string|null
     * @throws \Exception
     */
    public static function imgCompress($source_path,$output_path)
    {
        $resolution = self::getVideoResolution($source_path);
        if($resolution){
            $resolution = self::getMaxResolution($resolution['width'],$resolution['height']);
            $command = "%s -hide_banner -loglevel error -hwaccel auto -r 20 -i %s -c:v libx264 -b:v 862k -s %sx%s -y %s";
            $command = sprintf($command,self::$ffmpeg_path,$source_path,$resolution['width'],$resolution['height'],$output_path);
        }else{
            $command = "%s -hide_banner -loglevel error -hwaccel auto -r 20 -i %s -c:v libx264 -b:v 862k -y %s";
            $command = sprintf($command,self::$ffmpeg_path,$source_path,$output_path);
        }
        self::execShell($command);
        return is_file($output_path);
    }
}

获取视频分辨率信息时,可能会出错,所以我们需要兼容一下。

由于我们无法知道是否正确的执行成功,我这里使用is_file来检查一遍生成后的视频文件是否存在,存在表示执行成功,否则执行失败。

如果不想自己去封装ffmpeg,又不想使用我上面封装的代码,可以使用php-ffmpeg来执行视频压缩命令。具体的操作,可以看看我之前的文章【php-ffmpeg相关操作简介

有遗漏或者不对的可以在我的公众号留言哦

编程经验共享公众号二维码

编程经验共享公众号二维码
更多内容关注公众号
Copyright © 2021 编程经验共享 赣ICP备2021010401号-1