php實(shí)現(xiàn)ffmpeg處理視頻的實(shí)踐
最近有一個項目需要使用ffmpeg處理視頻,這里我寫了一個demo,方便我們來實(shí)現(xiàn)視頻操作
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 出現(xiàn)時間
*/
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 聲音插入延時秒數(shù)
*/
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';
/**
* 視頻旋轉(zhuǎn)
* @param $source string 需要旋轉(zhuǎn)的視頻
* @param $saveFile string 旋轉(zhuǎn)后視頻
* @param $rotate string 旋轉(zhuǎn)角度
*/
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;
}
/**
* 視頻轉(zhuǎn)碼
* @param $source string 需要轉(zhuǎn)碼的視頻/音頻
* @param $saveFile string 轉(zhuǎn)碼后的視頻/音頻
*/
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;
}
/**
* 創(chuàng)建一個臨時文件
*/
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]; //轉(zhuǎn)換播放時間為秒數(shù)
}
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),
];
}
}
使用簡單示例
這里注意如果無法執(zhí)行ffmpeg,實(shí)例化時需要傳入ffmpeg的安裝地址,例如linux下ffmpeg安裝地址為/usr/local/ffmepg,那么實(shí)例化時需要傳入/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//文字出現(xiàn)時間(秒)
);
2:將視頻設(shè)為靜音
$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',//視頻
);
到此這篇關(guān)于php實(shí)現(xiàn)ffmpeg處理視頻的實(shí)踐的文章就介紹到這了,更多相關(guān)php ffmpeg處理視頻內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
基于php偽靜態(tài)的實(shí)現(xiàn)詳細(xì)介紹
本篇文章介紹了,基于php偽靜態(tài)的實(shí)現(xiàn)詳細(xì)分析。需要的朋友參考下2013-04-04
PHP+MySql實(shí)現(xiàn)一個簡單的留言板
留言板是接觸WEB開發(fā)的基礎(chǔ),寫一個留言板需要知道前端的一些基礎(chǔ)標(biāo)簽,對數(shù)據(jù)庫有一個了解會基礎(chǔ)SQL語言,PHP基礎(chǔ)知識,前段基礎(chǔ)+數(shù)據(jù)庫基礎(chǔ)+PHP基礎(chǔ)=>留言板2020-07-07
tp5框架使用composer實(shí)現(xiàn)日志記錄功能示例
這篇文章主要介紹了tp5框架使用composer實(shí)現(xiàn)日志記錄功能,結(jié)合實(shí)例形式分析了thinkPHP5框架composer安裝及日志記錄相關(guān)操作技巧,需要的朋友可以參考下2019-01-01
基于laravel-admin 后臺 列表標(biāo)簽背景的使用方法
今天小編就為大家分享一篇基于laravel-admin 后臺 列表標(biāo)簽背景的使用方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-10-10
phpstorm遠(yuǎn)程連接服務(wù)器并實(shí)時更新發(fā)布代碼(thinkphp6.0.7)
這篇文章主要介紹了phpstorm遠(yuǎn)程連接服務(wù)器并實(shí)時更新發(fā)布代碼(thinkphp6.0.7),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-03-03

