关于php:php实现ffmpeg处理视频

208次阅读

共计 7996 个字符,预计需要花费 20 分钟才能阅读完成。

最近有一个我的项目须要应用 ffmpeg 解决视频,这里我写了一个 demo,不便咱们来实现视频操作

ffmpeg 操作 demo

<?php

namespace common\helpers;

use common\models\Config;
use common\models\VideoApiLog;
use Yii;
use yii\helpers\ArrayHelper;
use common\helpers\Universal;
use yii\helpers\FileHelper;
use yii\httpclient\Client;
use yii\web\ServerErrorHttpException;

/**
 * ffmpeg 视频解决
 *
 * @author wangjian
 * @since 0.1
 */
class FfmpegVideo
{

    public $ffmpeg = 'ffmpeg';


    public function __construct($ffmpeg = null)
    {if ($ffmpeg) {$this->ffmpeg = $ffmpeg;}
    }


    /**
     * 增加视频文字滚动
     * @param $source string 视频
     * @param $saveFile string 保留文件
     * @param $text string 水印文字
     * @param array $options 水印款式
     * @param int $step 每秒步长
     * @param int $star 呈现工夫
     */
    public function titleMod($source, $saveFile, $text, $options = [], $step = 20, $star = 0)
    {
        $command = $this->ffmpeg .'-y -i'. $source .'-async 1 -metadata:s:v:0 start_time=0 -vf';

        $fonts = Yii::getAlias('@webroot') . "/fonts/simsun.ttc";
        $fonts = str_replace('\\', '/', $fonts);
        $fonts = str_replace(':', '\\:', $fonts);
        $command .= '"drawtext=fontfile=\''. $fonts .'\': text=\''. $text .'\'';

        foreach ($options as $key => $value) {$command .= ':' . $key . '=' . $value;}

        $command .= ':x=\'if(gte(t,'. $star .'),((t-'. $star .') * '. $step .'),NAN)\'';

        $command .= '" ';
        $command .= $saveFile;
        exec($command, $output, $result_code);
        if ($result_code == 0) {return true;}
        return  false;
    }


    /**
     * 图片水印
     * @param $source string 视频
     * @param $saveFile string 保留文件
     * @param $waterImage string 水印图片
     * @param $left integer 水印程度地位
     * @param $top integer 水印垂直地位
     * @param null $star 水印开始工夫
     * @param null $duration 水印时长
     */
    public function imageWater($source, $saveFile, $waterImage, $left, $top, $star = null, $duration = null)
    {$waterImage = str_replace('\\', '/', $waterImage);
        $waterImage = str_replace(':', '\\:', $waterImage);
        $command = $this->ffmpeg . '-y -i'. $source .'-vf"movie=\''. $waterImage .'\'[watermark];';
        $command .= '[in][watermark] overlay='. $left .':'. $top;
        if ($star) {$end = ($duration) ? $star + $duration : $star;
            $command .= ':enable=\'between(t,'. $star .','. $end .')\'';
        }
        $command .= '[out]" ' . $saveFile;
        exec($command, $output, $result_code);
        if ($result_code == 0) {return true;}
        return  false;
    }


    /**
     * 给视频增加文字水印
     * @param $source string 视频
     * @param $saveFile string 保留文件
     * @param $text string 水印文字
     * @param array $options 水印款式
     * @param null $star 水印开始工夫
     * @param null $duration 水印时长
     */
    public function titleWater($source, $saveFile, $text, $options = [], $star = null, $duration = null)
    {
        $command = $this->ffmpeg .'-y -i'. $source .'-vf';
        $fonts = Yii::getAlias('@webroot') . "/fonts/STZHONGS.TTF";
        $fonts = str_replace('\\', '/', $fonts);
        $fonts = str_replace(':', '\\:', $fonts);
        $command .= '"drawtext=fontfile=\''. $fonts .'\': text=\''. $text .'\'';

        foreach ($options as $key => $value) {$command .= ':' . $key . '=' . $value;}
        if ($star) {$end = ($duration) ? $star + $duration : $star;
            $command .= ':enable=\'between(t,'. $star .','. $end .')\'';
        }
        $command .= '" ';
        $command .= $saveFile;

        exec($command, $output, $result_code);
        if ($result_code == 0) {return true;}
        return  false;
    }

    /**
     * 将音频合并到视频中
     * @param $videoFile string 视频文件
     * @param $audioFile string 音频文件
     * @param $saveFile string 保留文件
     * @param $delay integer 声音插入延时秒数
     */
    public function mergeVideoAudio($videoFile, $audioFile, $saveFile, $delay = null)
    {
        $delayTime = 0;
        if ($delay) {$delayTime = $delay * 1000;}
        $command =  $this->ffmpeg . '-y -i'. $audioFile .'-i'. $videoFile .'-c:v copy -c:a aac -strict experimental -filter_complex"[0]adelay='. $delayTime .'|'. $delayTime .'[del1],[1][del1]amix" ' . $saveFile;
        exec($command, $output, $result_code);
        if ($result_code == 0) {return true;}
        return  false;
    }


    /**
     * 静音
     */
    public function audioMute($source, $saveFile)
    {
        $command =  $this->ffmpeg . '-y -i'. $source .'-filter:a"volume=0" ' . $saveFile;
        exec($command, $output, $result_code);
        if ($result_code == 0) {return true;}
        return  false;
    }


    /**
     * 提取视频的音频
     * @param $source string 须要提取声音的视频
     * @param $saveFile string 提取声音后保留的音频
     * @return bool
     */
    public function collectAudio($source, $saveFile)
    {
        $command =  $this->ffmpeg . '-y -i'. $source .'-vn -acodec copy' . $saveFile;
        exec($command, $output, $result_code);
        if ($result_code == 0) {return true;}
        return  false;
    }

    /**
     * 去除视频声音
     * @param $source string 须要去除声音的视频
     * @param $saveFile string 去除声音后保留的视频
     */
    public function removeAudio($source, $saveFile)
    {
        $command =  $this->ffmpeg . '-y -i'. $source .'-an' . $saveFile;

        exec($command, $output, $result_code);
        if ($result_code == 0) {return true;}
        return  false;
    }

    /**
     * 视频拼接
     * @param $sources array 须要拼接的视频 / 音频
     * @param $saveFile string 拼接后的视频 / 音频
     */
    public function spliceVideo($sources, $saveFile)
    {$commands = [];
        $temporaryFile = [];
        $basePath = sys_get_temp_dir();
        $index = 0;
        foreach ($sources as $i => $source) {
            $file = $basePath . '/' . $i . '.ts';
            $commands[$index] = $this->ffmpeg . '-y -i'. $source .'-vcodec copy -acodec copy -vbsf h264_mp4toannexb' . $file;
            $temporaryFile[] = $file;
            $index++;
        }
        $commands[$index] = $this->ffmpeg . '-y -i"concat:'. implode('|', $temporaryFile) .'"  -acodec copy -vcodec copy -absf aac_adtstoasc ' . $saveFile;
        foreach ($commands as $command) {exec($command, $output, $result_code);
        }

        foreach ($temporaryFile as $file) {@unlink($file);
        }
        return true;

    }


    /**
     * 视频剪切
     * @param $source string 须要剪切视频 / 音频
     * @param $saveFile string 剪切后保留视频 / 音频
     * @param $star string 剪切开始工夫
     * @param null $duration string 剪切时长
     */
    public function clipVideo($source, $saveFile, $star, $duration = null)
    {
        $command = $this->ffmpeg . '-y -ss'. $star;
        if ($duration) {$command .= '-t'. $duration;}
        $command .= '-i'. $source .'-acodec copy' . $saveFile;
        exec($command, $output, $result_code);
        if ($result_code == 0) {return true;}
        return  false;
    }

    const ROTATE_90 = 'transpose=1';
    const ROTATE_180 = 'hflip,vflip';
    const ROTATE_270 = 'transpose=2';

    /**
     * 视频旋转
     * @param $source string 须要旋转的视频
     * @param $saveFile string 旋转后视频
     * @param $rotate string 旋转角度
     */
    public function transposeVideo($source, $saveFile, $rotate)
    {

        $command = $this->ffmpeg . '-y -i' . $source . '-vf""transpose=1"" ' . $saveFile;
        exec($command, $output, $result_code);
        if ($result_code == 0) {return true;}
        return  false;
    }

    /**
     * 视频转码
     * @param $source string 须要转码的视频 / 音频
     * @param $saveFile string 转码后的视频 / 音频
     */
    public function acodecVideo($source, $saveFile)
    {
        $command = $this->ffmpeg . '-y -i'. $source .'-acodec copy -vcodec copy -f mp4' . $saveFile;
        exec($command, $output, $result_code);
        if ($result_code == 0) {return true;}
        return  false;
    }

    /**
     * 视频拼接
     * @param $sources array 须要拼接的视频 / 音频
     * @param $saveFile string 拼接后的视频 / 音频
     */
    public function concatVideo($sources, $saveFile)
    {$file = $this->createTemporaryFile();
        $fileStream = @fopen($file, 'w');
        if($fileStream === false) {throw new ServerErrorHttpException('Cannot open the temporary file.');
        }

        $count_videos = 0;
        if(is_array($sources) && (count($sources) > 0)) {foreach ($sources as $videoPath) {
                $line = "";
                if($count_videos != 0)
                    $line .= "\n";
                $line .= "file'". str_replace('\\','/',$videoPath) ."'";

                fwrite($fileStream, $line);
                $count_videos++;
            }
        }
        else {throw new ServerErrorHttpException('The list of videos is not a valid array.');
        }

        $command = $this->ffmpeg .'-y -f concat -safe 0 -i'. $file . '-c copy' . $saveFile;
        exec($command, $output, $result_code);
        fclose($fileStream);
        @unlink($file);// 删除文件
        if ($result_code == 0) {return true;}
        return  false;
    }

    /**
     * 创立一个临时文件
     */
    public function createTemporaryFile()
    {$basePath = sys_get_temp_dir();
        if (false === $file = @tempnam($basePath, null)) {throw new ServerErrorHttpException('Unable to generate a temporary filename');
        }
        return $file;
    }

    /**
     * 获取视频信息
     * @param $source string 须要获取时长的资源
     */
    public function getAttributes($source)
    {ob_start();
        $command = $this->ffmpeg . '-i"'. $source .'"2>&1';
        passthru($command);
        $getContent = ob_get_contents();
        ob_end_clean();
        $duration = 0;
        $widht = 0;
        $height = 0;
        if (preg_match("/Duration: (.*?), start: (.*?), bitrate: (\d*) kb\/s/", $getContent, $match)) {$matchs = explode(':', $match[1]);
            $duration = $matchs[0] * 3600 + $matchs[1] * 60 + $matchs[2]; // 转换播放工夫为秒数
        }

        if (preg_match("/Video: (.*?), (.*?), (.*?)[,\s]/", $getContent, $match)) {$matchs = explode('x', $match[3]);
            $widht = $matchs[0];
            $height = $matchs[1];
        }

        return ['duration' => intval($duration),
            'widht' => intval($widht),
            'height' => intval($height),
        ];
    }

}

应用简略示例

这里留神如果无奈执行 ffmpeg,实例化时须要传入 ffmpeg 的装置地址,例如 linux 下 ffmpeg 装置地址为 /usr/local/ffmepg,那么实例化时须要传入 /usr/local/ffmpeg/bin/ffmpeg

1:给视频增加文字

$ffmpeg = new FfmpegVideo();
$ffmpeg ->titleWater(
    'XXX',// 原视频
    'XXX',// 解决后保留视频
    'XXX',// 文字
    [
        'x' => 30,// 程度间隔
        'y' => 30,// 垂直距离
        'fontsize' => 20,// 文字大小
        'fontcolor' => 'red',// 文字色彩
        'shadowy' => 2,// 文字暗影
    ],
    200,// 每秒挪动步长
    2// 文字呈现工夫 (秒)
);

2:将视频设为静音

$ffmpeg = new FfmpegVideo();
$ffmpeg->audioMute(
    'XXX',// 原视频
    'XXX',// 解决后保留视频
);

3:视频裁剪

$ffmpeg = new FfmpegVideo();
$ffmpeg->clipVideo(
    'XXX',// 原视频
    'XXX',// 解决后保留视频
    0,// 裁剪开始工夫
    10// 裁剪时长
);

4:视频拼接

$ffmpeg = new FfmpegVideo();
$ffmpeg->concatVideo(['XXX', 'XXX'],// 须要拼接的视频
    'XXX',// 解决后保留视频
);

5:将音频合并到视频中

$ffmpeg = new FfmpegVideo();
$ffmpeg->mergeVideoAudio(
    'XXX',// 视频
    'XXX',// 音频
    'XXX',// 解决后保留视频
    0// 音频插入视频延时工夫 (秒)
);

6:获取视频信息 (长,宽,时长)

$ffmpeg = new FfmpegVideo();
$ffmpeg->getAttributes('XXX',// 视频);

其余办法可查看 demo

正文完
 0