PHP的ffmpeg使用

来源:互联网 发布:mysql两表关联update 编辑:程序博客网 时间:2024/05/21 15:44

因为项目需求,同事写了一个ffmpeg的类,但是因为临时来了一个其他的项目,所以这个未完成的类就交给我来完成,所以我就把这个类的完成过程记录一下

<?phpinclude('FFmpegSupport.php');//http://blog.csdn.net/doublefi123/article/details/24325159 基本用法class FFmpegManage{    /**     * ffmpeg默认结构:ffmpeg  [全局选项] [输入文件选项] -i (输入文件路径) [输出文件选项] (输出文件路径)     * 其中[]括号里的是可选,()括号里的是必选的     */    private $ExecString = 'ffmpeg.exe '; //命令语句    private $GlobalOptions = ''; //全局选项    private $ErrMessage = array(); //错误信息    private $InputFileOptionsString = ''; //输入文件选项    private $InputFileOptions = array(); //输入文件选项数组    private $InputFilePath = array(); //输入文件路径    private $OutFileOptions = ''; //输出文件选项    private $OutFilePath = ''; //输出文件路径    private $OutFileTybe = ''; //输入文件的后缀名    //可配置参数    private $config = array(        'startTime'=>1,        //文件开始时间        'fmt'=>1,               //文件格式        'codec'=>1,             //解码器        'audioCodec'=>1,       //音频解码器        'bitRate'=>1,           //比特率        'sampleRate'=>1,        //采样率        'audioChannels'=>1,    //声道数        'endTime'=>1,           //结束时间        'timeLength'=>1,        //文件时间长度    );    /**        $Config = array(            'inputFilePath'            =>  array(),   //输入路径(不能为空)            'outputFilePath'                => '',     //输出路径(不能为空)            //输入文件选项(为空时不执行)            'fileOption'        =>array(                'studyTime'             => '',               //文件开始时间                'fmt'                    => '',              //文件格式                'codec'                  => '',             //解码器                'audioCodec'            => '',              //音频解码器                'bitRate'               => '',              //比特率                'sampleRate'            => '',              //采样率                'audioChannels'         => '',             //声道数                'endTime'                => '',            //结束时间                'timeLength'             => '',            //文件时间长度            ),        );     **/    public function setConfig($config){        @$fileOption = $config['fileOption'];        @$inputFilePath =  $config['inputFilePath'];        @$outputFilePath =  $config['outputFilePath'];        //判断是否有输入或输出路径        if(empty($inputFilePath) && empty($outputFilePath)){            $this->setErrMessage('inputFilePath and outputFilePath is null');        }        //获取输入文件路径        if(!is_array($inputFilePath)){            $filePathArray = explode('+',$inputFilePath);        }else{            $filePathArray = $inputFilePath;        }        if(!empty($fileOption))            //遍历输入文件路径            foreach($filePathArray as $filePath){                //将配置信息进行遍历                foreach($fileOption as $key => $value){                    //判断配置信息是否正确,如果不正确跳过                    if(!empty($this->config[$key]) && !empty($value)){                        //加载配置                        $this->$key($value);                    }                }                //判断文件是否能进行写入                if( file_exists( $filePath ) ){                    //将输入配置与输入文件进行关联                    $this->InputFilePath[] = '-i ' . $filePath . ' ';                    $this->InputFileOptions[] = '-i ' . $filePath . ' '.$this->InputFileOptionsString;                    $this->InputFileOptionsString = '';                }else{                    $this->setErrMessage('Input File is not Exist!!');                }            }        $this->OutFileTybe = substr( $outputFilePath , -4 );        $this->OutFilePath = $outputFilePath . ' ';        return $this;    }    /**     * 设置ffmpeg执行时对所有选项都回答yes,例文件已存在,要覆盖的时候程序会等待回答yes or on ,有时候不添加会出错     * @return $this     */    public function setAnswerAllYes(){        $this->GlobalOptions .= ' -y ';        return $this;    }////////////////////////////配置信息载入////////////////////////////    /**     * 强制设定文件的格式,需要使用ffmpeg当前版本支持的名称(缺省使用扩展名称)     * @param $_fileType String 文件类型     */    private function fmt( $_fileType ){         empty( $_fileType ) ?             $this->setErrMessage( 'File Type is empty!!'):             $this->InputFileOptionsString .= '-f ' . $_fileType . ' ';    }    /**     * 设置输入文件的起始时间点,在此时间点开始读取数据 ,     * @param $_time int 起始时间(单位:秒s)     */    private function startTime( $_time ){        $_time = (int)$_time;        empty($_time) && ($_time < 0) ?            $this->setErrMessage( " $_time must be biger than 0"):            $this->InputFileOptionsString .= '-ss ' . $_time . ' ';    }    /**     * 指定解码器,需输入此版本支持的解码器     * @param $_codecName     */    private function codec( $_codecName ){        empty( \FFmpegSupport::$InputProtocolsArray[ $_codecName ] )  && empty( \FFmpegSupport::$OutputProtocolsArray[ $_codecName ] ) ?            $this->setErrMessage( 'This versions ffmpeg is not support this codec!!' ):            $this->InputFileOptionsString .= '-c ' . $_codecName . ' ';    }    /**     *指定音频解码器     * @param $_audioCodecName String 解码器名称,需输入此版本ffmpeg支持的解码器     */    private function audioCodec( $_audioCodecName ){        empty( \FFmpegSupport::$InputProtocolsArray[ $_audioCodecName ] )  && empty( \FFmpegSupport::$OutputProtocolsArray[ $_audioCodecName ] ) ?            $this->setErrMessage( 'This versions ffmpeg is not support this codec!!' ):            $this->InputFileOptionsString .= '-acodec ' . $_audioCodecName . ' ';    }    /**     * 设置音频流的采样率     * 可以使用的采样率     * 8,000 Hz - 电话所用采样率, 对于人的说话已经足够     * 11,025 Hz     * 22,050 Hz - 无线电广播所用采样率     * 32,000 Hz - miniDV 数码视频 camcorder、DAT (LP mode)所用采样率     * 44,100 Hz - 音频 CD, 也常用于 MPEG-1 音频(VCD, SVCD, MP3)所用采样率     * 47,250 Hz - 商用 PCM 录音机所用采样率     * 48,000 Hz - miniDV、数字电视、DVD、DAT、电影和专业音频所用的数字声音所用采样率     * 50,000 Hz - 商用数字录音机所用采样率     * 96,000 或者 192,000 Hz - DVD-Audio、一些 LPCM DVD 音轨、BD-ROM(蓝光盘)音轨、和 HD-DVD (高清晰度 DVD)音轨所用所用采样率     * 2.8224 MHz - Direct Stream Digital 的 1 位 sigma-delta modulation 过程所用采样率。     * @param $_audioSampleRate int 音频采样率,单位Hz     */    private function sampleRate( $_audioSampleRate ){         $_audioSampleRate < 1 ?             $this->setErrMessage( 'AudioSampleRate can not smaller than 0!!'):             $this->InputFileOptionsString .= '-ar ' . $_audioSampleRate . ' ';    }    /**     * 设置音频流的比特率     * @param $_audioBitRate int 音频比特率,单位bps     */    private function bitRate( $_audioBitRate ){        $_audioBitRate < 1 ?            $this->setErrMessage( 'BitRate can not smaller than 0!!'):            $this->InputFileOptionsString .= '-ab ' . $_audioBitRate . ' ';    }    /**     * 设置音频流的声道数目     * @param $_channels int 通道数目整数,大于等于1     */    private function audioChannels( $_channels ){        $_channels<1 ?            $this->setErrMessage( 'Channels count must be biger than 0!!'):            $this->InputFileOptionsString .= '-ac ' . $_channels . ' ';    }    /**     * 设置终止时间点     * @param $_endTimePoint String 终止时间点 hh:mm:ss     */    private function endTime( $_endTimePoint ){        $_endTimePoint = trim( $_endTimePoint );        $_endTimePoint < 0 ?            $this->setErrMessage( 'Time can not be less than 0!!'):            $this->OutFileOptions .= '-to ' . $_endTimePoint . ' ';    }    /**     * 设置时间长度     * @param $_time String 时间长度 hh:mm:ss     * @return $this Class 此类     */    private function timeLength( $_timeLen ){        $_timeLen = trim( $_timeLen );        $_timeLen < 0 ?            $this->setErrMessage( 'Time can not be less than 0!!'):            $this->OutFileOptions .= '-t ' . $_timeLen . ' ';    }////////////////////////////处理音频////////////////////////////    /**     * 修改音频的音量     * @param $_volume int 你需要设置的音频音量     * @return $this  Class 此类     */    public function changeAudioVolume( $_volume ){        ( (int)$_volume < 10 )?            $this->setErrMessage( 'Volume can not smaller than 10!!'):            $this->OutFileOptions .= '-vol ' . $_volume . ' ';        return $this;    }    /**     * 合并多音轨     * @return $this Class 此类     */    public function audioComplex(  ){        if( sizeof( $this->InputFilePath ) < 2 ){            $this->setErrMessage( 'In this Complex function, The number of input files can not smaller than 2!!');        }elseif( $this->OutFileTybe == 'amr' ){            $this->setErrMessage( 'The outfile can not be amr type!!');        }else{            $this->OutFileOptions .= '-filter_complex join=inputs=2: ';        }        return $this;    }    /**     * 多音轨合并(比如将BGM与人声结合)      * @return $this     */    public function audioAmix(){        count($this->InputFilePath) < 2 ?            $this->setErrMessage('File number is less than 2'):            $this->OutFileOptions .= '-filter_complex amix=inputs=2:duration=first:dropout_transition=2 ';        return $this;    }    /**     * 拼接两个音频文件     * @return $this Class 此类     */    public function audioJoin(  ){        ( sizeof( $this->InputFilePath ) < 2 )?            $this->setErrMessage( 'The number of input files can not smaller than 2!!' ):            $this->OutFileOptions .= '-filter_complex acrossfade=d=10:c1=exp:c2=exp ';        return $this;    }    /**     * 改变音频的速率     * @param $_speed int 你需要设置的速率 分数格式 7/10     * @return $this Class 此类     */    public function changeAudioSpeed( $_speed ){        $_speed = trim( $_speed );        ( $_speed <= 0 && empty($_speed))?            $this->ErrMessage[] = 'The slowdown speed can not smaller than 0 or =0!!':            $this->OutFileOptions .= '-filter:a atempo=' . $_speed . ' ';        return $this;    }    /**     * 改变原音频的声调,改变声音     * @param $_rateHz rateHz     */    public function audioChangeVoice( $_rateHz ){        $_rateHz = trim( $_rateHz );        ( $_rateHz < 100 )?            $this->ErrMessage[] = 'The rate size cannot be lower than 100Hz!!':            $this->OutFileOptions .= '-filter_complex asetrate=r=' . $_rateHz . ' ';        return $this;    }////////////////////////////处理视频////////////////////////////    /**     * 裁剪视频     * @param $_Property Array 包括以下元素:'operation':操作,'width':要裁剪的宽,'height':要裁剪的高,'offestX':水平偏移,'offestY':垂直偏移     * @return $this     */    public function videoCrop( $_Property ){        if(empty($_Property) && !is_array($_Property)){            $this->setErrMessage('The parameter is incorrect');        }else {            switch ($_Property['operation']) {                case 'normal'://普通操作,即要输入裁剪的宽高和开始裁剪的xy值                    $this->OutFileOptions .= '-filter_complex crop=w=' . $_Property['width'] . ':h=' . $_Property['height'] . ':x=' . $_Property['offestX'] . ':y=' . $_Property['offestY'] . ' ';                    break;                case 'centerWH'://从中心开始裁剪多宽和多高,需要输入裁剪的宽高值                    $this->OutFileOptions .= '-filter_complex crop=' . $_Property['width'] . ':' . $_Property['height'] . ' ';                    break;                case 'center'://从中心开始裁剪,裁剪的宽高值由程序控制                    $this->OutFileOptions .= '-filter_complex crop=out_w=in_h crop=in_h ';                    break;                case 'boder'://据上下多少,左右多少进行裁剪,需输入裁剪的宽高值,此时宽高值为:距离边界多少像素值                    $this->OutFileOptions .= '-filter_complex crop="in_w-2 ' . $_Property['width'] . ':in_h-2 ' . $_Property['height'] . '" ';                    break;                case 'shake':                    $this->OutFileOptions .= "-filter_complex crop='in_w/2:in_h/2:(in_w-out_w)/2+((in_w-out_w)/2)*sin(n/10):(in_h-out_h)/2+((in_h-out_h)/2)*sin(n/7)' ";                    break;            }        }        return $this;    }    /**     * 在视频上画网格     * @param $_boxWidthCount  int  水平多少个格, 默认3     * @param $_boxHeightCount int  垂直多少个格,默认3     * @param $_thickness int 网格线的宽度,默认2     * @param $_color String 网格颜色名称,默认yellow,详解:https://xdsnet.gitbooks.io/other-doc-cn-ffmpeg/content/ffmpeg-doc-cn-37.html     * @param $_transparent 网格透明度,0~1 ,支持一位小数,默认1     * @return $this Class 此类     */    public function videoDrawGrid( $_boxWidthCount = "" , $_boxHeightCount ="" , $_thickness ="" , $_color ="" , $_transparent =""){        if( empty( $_color ) ){            $_color = 'yellow';        }        if( empty( $_transparent ) ){            $_transparent = 1;        }        if( empty( $_thickness ) ){            $_thickness = 2;        }        if( empty( $_boxWidthCount ) ){            $_boxWidthCount = 3;        }        if( empty( $_boxHeightCount ) ){            $_boxHeightCount = 3;        }        $this->OutFileOptions .= '-filter_complex drawgrid=width='.$_boxWidthCount . ':height=' . $_boxHeightCount . ':thickness=' . $_thickness . ':color=' . $_color . '@' . $_transparent . ' ';        return $this;    }    /**     * 水平翻转视频     * @return $this     */    public function videoHFlip(){        $this->OutFileOptions .= '-vf "hflip" ';        return $this;    }    /**     * 垂直翻转视频     * @return $this     */    public function videoVFlip(){        $this->OutFileOptions .= '-vf "vflip" ';        return $this;    }    /**     * 旋转视频     * @param $_dir  int 角度(单位:90度)     * @return $this Class 此类     */    public function videoTranspose( $_dir ){        if(empty($_dir)){            $_dir = 90;        }        $this->OutFileOptions .= '-filter_complex transpose=dir=' . $_dir . ' ';        return $this;    }    /**     * 拼接多个视频     * 需要php的ffmpeg扩展     * @return $this Class 此类     */    public function videoConcat(){        if(empty(get_extension_funcs('ffmpeg'))){            $this->setErrMessage('ffmpeg Extension does not exist');        }else{            if( empty( $this->InputFilePath ) ){                $this->ErrMessage[] = 'Line 583 , The inputFiles count empty!!';            }            if( sizeof( $this->InputFilePath ) < 2 ){                $this->ErrMessage[] = 'Line 586 , The inputFiles count must be bigger than 1!!';            }            $biggerWidth = 0;            $biggerHeight = 0;            for( $i = 0 ; $i < sizeof( $this->InputFilePath ) ; $i++  ){                $ffmpeg = new \ffmpeg_movie( $this->InputFilePath[$i] );                if( $ffmpeg->getFrameWidth() > $biggerWidth ){                    $biggerWidth = $ffmpeg->getFrameWidth();                }                if( $ffmpeg->getFrameHeight() > $biggerHeight ){                    $biggerWidth = $ffmpeg->getFrameHeight();                }            }            for( $i = 0 ; $i < sizeof( $this->InputFilePath ) ; $i++  ){                $ffmpeg = new \ffmpeg_movie( $this->InputFilePath[$i] );                if( $ffmpeg->getFrameHeight() == $biggerHeight && $ffmpeg->getFrameWidth() == $biggerWidth ){                    continue;                }else{                    $heightDifference = $ffmpeg->getFrameHeight() - $biggerHeight;                    $widthDifference = $ffmpeg->getFrameWidth() - $biggerWidth;                    if( $heightDifference !== 0 ){                        $heightDifference = $heightDifference/2;                    }                    if( $widthDifference !== 0 ){                        $widthDifference = $widthDifference/2;                    }                    $savePath = substr( $this->InputFilePath[$i] , 0 , strrpos( $this->InputFilePath[$i] , '.' , 0 )  ) . '1' . substr( $this->InputFilePath[$i] , strrpos( $this->InputFilePath[$i] , '.' , 0 ) + 1  );                    $ffmpegStr = 'ffmpeg -y -i ' . $this->InputFilePath[$i] . ' -vf pad=' . $biggerWidth . ':' . $biggerHeight . ':' . $widthDifference . ':' . $heightDifference . ':black ' . $savePath;                    $this->execute( $ffmpegStr );                    $this->InputFilePath[$i] = $savePath;                }            }            $this->OutFileOptions .= "-filter_complex concat=n=" . sizeof( $this->InputFilePath ) . ' ';        }        return $this;    }    /**     * 添加水印,水印图片为png图片,透明度用绘图软件调整     * @param $_operation  string 操作类型,有normal(正常:需输入x,y偏移值),lefttop(左上角),righttop(右上角),leftbottom(左下角),rightbottom(右下角),center(中心)     * @param $_offestX  (x偏移值)     * @param $_offestY  (y偏移值)     * @return $this  Class 此类     */    public function videoOverlay( $_operation , $_offestX , $_offestY ){        switch( $_operation ){            case 'normal':                $offestX = $_offestX;                $offestY = $_offestY;                break;            case 'lefttop':                $offestX = 0;                $offestY = 0;                break;            case 'righttop':                $offestX = 'main_w-overlay_w';                $offestY = 0;                break;            case 'leftbottom':                $offestX = 'main_w-overlay_w';                $offestY = 'main_h-overlay_h';                break;            case 'rightbottom':                $offestX = 0;                $offestY = 'main_h-overlay_h';                break;            case 'center':                $offestX = '(main_w-overlay_w)/2';                $offestY = '(main_h-overlay_h)/2';                break;            default:                $offestX = 0;                $offestY = 0;                break;        }        $this->OutFileOptions .= "-filter_complex overlay=" . $offestX . ':' . $offestY . ' ';        return $this;    }    public function setAphaser(  ){        $this->OutFileOptions .= "-filter_complex blend=all_mode=normal ";        return $this;    }    /**     * 修改视频速率(只修改视频,音频依旧是原速率)     * @param $_speed   速率     * @return $this  Class 此类     */    public function videoSetpts($_speed){        (empty($_speed))?            $this->ErrMessage[] = 'videoSpeed is null':            $this->OutFileOptions .= "-filter:v setpts=PTS*('.$_speed.')' ";        return $this;    }    /**     * 同时修改音频与视频速率     */        public function changeVideoSpeed($_speed){        $_speed = explode('/',$_speed);        if(empty($_speed)){            $this->ErrMessage[] = 'speed is null';        }elseif(($_speed[0]/$_speed[1] > 2) || ($_speed[0]/$_speed[1] < 1/2)){            $this->ErrMessage[] = 'The rate is only 2/1(2.0) to 1/2(0.5)';        }else{            $videoSpeed = '('.$_speed[0].'/'.$_speed[1].')';            $audioSpeed = '('.$_speed[1].'/'.$_speed[0].')';            $this->OutFileOptions .= "-filter_complex [0:v]setpts=".$videoSpeed."*PTS[v];[0:a]atempo=".$audioSpeed."[a] -map [v] -map [a] ";        }        return $this;    }    /**     * 视频单独分离     * @return $this  Class 此类     */    public function separateVideo(){        $this->OutFileOptions .= '-vcodec copy -an ';        return $this;    }    /**     * 音频单独分离     * @return $this  Class 此类     */    public function separateAudio(){        $this->OutFileOptions .= '-acodec copy -vn ';        return $this;    }    /**     * 修改视频尺寸   参考 640x480     * @param $Width    视频宽度     * @param $Height   视频高度     * @return $this     */    public function changeAudioSize($Width,$Height){        (empty($Width) || empty($Height))?            $this->ErrMessage[] = 'width or height is null':            $this->OutFileOptions .= '-s '.$Width.'x'.$Height.' ';        return $this;    }    /*     * 转换黑白     */    public function videoBlackWhite(){        $this->OutFileOptions .= '-vf lutyuv="u=128:v=128" ';        return $this;    }    /**     * 生成gif     * @return $this     */    public function makeGif(){        $this->OutFileOptions .='-pix_fmt rgb24 ';        return $this;    }    ////////////////////////////错误处理,运行ffmpeg方法////////////////////////////    /**     * 获取执行错误信息     * @return array 错误信息,包括错误行数和错误内容     */    public function setErrMessage($message){        $this->ErrMessage[] = $message;    }    public function getErrorMessage(){        return($this->ErrMessage);    }    /**     * 执行ffmpeg命令     * @return string 执行结果信息     */    public function exec(  ){        if(!empty($this->ErrMessage)){            return false;        }        //amr文件输出方式跟其他文件不同,需要分开处理        if( $this->OutFileTybe == 'amr' ){            $acIndex = strrpos( $this->OutFileOptions , '-ac' , 0 );            $arIndex = strrpos( $this->OutFileOptions , '-ar' , 0 );            $this->OutFileOptions =  $acIndex === false ? $this->OutFileOptions . ' -ac 1 ' : $this->OutFileOptions;            $this->OutFileOptions =  $arIndex === false ? $this->OutFileOptions . ' -ar 8000 ' : $this->OutFileOptions;        }        //输入文件内容        $inputString = '';        //将输入条件与对应文件进行匹配        foreach ($this->InputFileOptions as $value) {            $inputString .= $value;        }        //将执行语句拼接        $this->ExecString .=  $this->GlobalOptions . $inputString . $this->OutFileOptions . $this->OutFilePath;        //执行ffmpeg命令        exec( $this->ExecString,$execInfo,$execCode);        //执行后清空本次执行选项,防止干扰下次使用        $this->ExecString = 'ffmpeg ';        $this->GlobalOptions = '';        $this->InputFileOptionsString = '';        $this->InputFileOptions = array();        $this->InputFilePath = array();        $this->OutFileOptions = '';        $this->OutFilePath = '';        $this->OutFileTybe = '';        //检查exec语句运行是否成功,如果不成功返回失败        if(!is_array($execInfo) && $execCode!=0){            $this->setErrMessage('exec error!!');            return false;        }else {            return true;        }    }    /**     * 此方法用来解决无法用户无法通过浏览器执行exec命令(此方法只能在windos上使用)     * @param $batPath bat文件路径     * @return bool     */    public function execute($batPath){        if(!empty($this->ErrMessage)){            return false;        }        if( $this->OutFileTybe == 'amr' ){            $acIndex = strrpos( $this->OutFileOptions , '-ac' , 0 );            $arIndex = strrpos( $this->OutFileOptions , '-ar' , 0 );            $this->OutFileOptions =  $acIndex === false ? $this->OutFileOptions . ' -ac 1 ' : $this->OutFileOptions;            $this->OutFileOptions =  $arIndex === false ? $this->OutFileOptions . ' -ar 8000 ' : $this->OutFileOptions;        }        //输入文件内容        $inputString = '';        //将输入条件与对应文件进行匹配        foreach ($this->InputFileOptions as $value) {            $inputString .= $value;        }        //将执行语句拼接        $this->ExecString .=  $this->GlobalOptions . $inputString . $this->OutFileOptions . $this->OutFilePath;        $bat = fopen($batPath,'w+');        if(!$bat){            $this->setErrMessage('batFile can not open');            return false;        }        fwrite($bat, $this->ExecString);        fclose($bat);        exec($batPath,$execInfo,$execCode);        $this->ExecString = 'ffmpeg ';        $this->GlobalOptions = '';        $this->InputFileOptionsString = '';        $this->InputFileOptions = array();        $this->InputFilePath = array();        $this->OutFileOptions = '';        $this->OutFilePath = '';        $this->OutFileTybe = '';        //检查exec语句运行是否成功,如果不成功返回失败        if(!is_array($execInfo) && $execCode!=0){            $this->setErrMessage('exec error!!');            return false;        }else {            return true;        }    }}

有些功能有冲突是不能同时使用的,比如音频分离跟修改速率是不能同时执行的
ffmpeg是一个功能十分强大的开源库,我这里只列举一部分功能,而且这个类还有许多可以优化的地方,这个类写出来主要是为了抛砖引玉 :D

下面附上ffmpeg参数说明
http://www.cnblogs.com/chen1987lei/archive/2010/12/03/1895242.html

0 0