php如何截取出视频中的指定帧作为图片
php如何截取出视频中的指定帧作为图片
一、总结
一句话总结:截取视频指定帧为图片,php ffmpeg扩展已经完美实现,并且php ffmpeg是开源的
二、php如何截取出视频中的指定帧作为图片
截取视频指定帧为图片,php ffmpeg扩展已经完美实现:
1
2
3
4
5
6
|
$movie = new ffmpeg_movie( $video_filePath ); $ff_frame = $movie ->getFrame(1); $gd_image = $ff_frame ->toGDImage(); $img = "./test.jpg" ; imagejpeg( $gd_image , $img ); imagedestroy( $gd_image ); |
然而问题来了,智能手机拍摄的视频,由于拍摄方向不同,视频会被旋转,并带上meta信息rotate,当你相对视频截取frame图片的时候,如果有rotate信息的视频,frame也是旋转的,因此你需要将截取的图片相应的旋转。
然后php ffmpeg扩展并无法获知rotation信息(php ffmpeg扩展文档),但可以通过ffmpeg命令行获取:
/usr/local/ffmpeg/bin/ffprobe test.mp4 -show_streams | grep rotate
用php简单封装下如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
function get_video_orientation( $video_path ) { $cmd = "/usr/local/ffmpeg/bin/ffprobe " . $video_path . " -show_streams 2>/dev/null" ; $result = shell_exec( $cmd ); $orientation = 0; if ( strpos ( $result , 'TAG:rotate' ) !== FALSE) { $result = explode ( "\n" , $result ); foreach ( $result as $line ) { if ( strpos ( $line , 'TAG:rotate' ) !== FALSE) { $stream_info = explode ( "=" , $line ); $orientation = $stream_info [1]; } } } return $orientation ; } |
使用imagerotate()函数就可以旋转截图:
1
2
3
4
5
6
7
8
9
|
$movie = new ffmpeg_movie( $video_filePath ); $frame = $movie ->getFrame(1); $gd = $frame ->toGDImage(); if ( $orientation = $this ->get_video_orientation( $video_filePath )) { $gd = imagerotate( $gd , 360- $orientation , 0); } $img = "./test.jpg" ; imagejpeg( $gd , $img ); imagedestroy( $gd_image ); |
最后还有一个麻烦事,不是所有的播放器和浏览器都可对video识别orientation并自动rotate,如果你想对视频进行旋转,可通过ffmpeg命令解决:
/usr/local/ffmpeg/bin/ffmpeg -i
input.mp4 -vf 'transpose=3' -metadata:s:v:0 rotate=0
参考:php截取视频指定帧为图片_php技巧_脚本之家
https://www.jb51.net/article/84359.htm
三、ffmpeg-php扩展
php视频缩略图,较常用的是ffmpeg-php
1: 安装 ffmpeg
ffmpeg的下载链接 http://ffmpeg.org/download.html
解压安装包
tar -jxvf ffmpeg-x.x.tar.bz2
进入目录
cd ffmpeg-x.x
编译安装
./configure --enable-shared && make && make install
安装完成之后 执行 ffmpeg -version
如果能够出现类似下列信息,说明ffmpeg安装成功。
ffmpeg version 2.5.11 Copyright (c) 2000-2016 the FFmpeg developers
built on Apr 17 2017 16:47:15 with gcc 4.8.5 (GCC) 20150623 (Red Hat 4.8.5-11)
configuration: --enable-shared
libavutil 54. 15.100 / 54. 15.100
libavcodec 56. 13.100 / 56. 13.100
libavformat 56. 15.102 / 56. 15.102
libavdevice 56. 3.100 / 56. 3.100
libavfilter 5. 2.103 / 5. 2.103
libswscale 3. 1.101 / 3. 1.101
libswresample 1. 1.100 / 1. 1.100
2 安装ffmpeg-php
官方的下载链接
https://sourceforge.net/projects/ffmpeg-php/files/ffmpeg-php/
官方版本已经很久很久不更新了,我在centos 7 + php5.6&php7.1 centos6.5+php5 试了都不行。在configure完 make的时候会报错。
后来在github上找了一个版本。在centos7.2+php5.5.6 上编译安装成功 (centos7 + php7.1还是不行)
地址: git clone https://github.com/tony2001/ffmpeg-php.git
也可以访问我的百度云盘进行下载
https://pan.baidu.com/s/1skQTVlj
进入 ffmpeg-php目录 进行编译扩展
/usr/local/php/bin/phpize
./configure --with-php-config=/usr/local/php/bin/php-config
make && make install
注意(如果make之后出现错误,那就是ffmpeg-php版本的问题,别再折腾了,换版本吧(或者降低php版本试试))。
在php的配置文件 php.ini中 添加
extension=ffmpeg.so
重启php。在 phpinfo()中查看有无ffmpeg信息。
有的话就OK
或者执行
/usr/local/php/bin/php -i |grep ffmpeg
要是有输出的话OK
https://www.cnblogs.com/jkklearn/p/6737467.html
二、PHP的ffmpeg使用
因为项目需求,同事写了一个ffmpeg的类,但是因为临时来了一个其他的项目,所以这个未完成的类就交给我来完成,所以我就把这个类的完成过程记录一下
<?php
include('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
https://blog.csdn.net/qq_20329253/article/details/51420661
php如何截取出视频中的指定帧作为图片的更多相关文章
- iOS获取视频中的指定帧的两种方法
方法一 :AVFoundation #import <AVFoundation/AVFoundation.h> - (UIImage *)thumbnailImageForVideo:(N ...
- 机器学习进阶-目标追踪-SSD多进程执行 1.cv2.dnn.readnetFromCaffe(用于读取已经训练好的caffe模型) 2.delib.correlation_tracker(生成追踪器) 5.cv2.writer(将图片写入视频中) 6.cv2.dnn.blobFromImage(图片归一化) 10.multiprocessing.process(生成进程)
1. cv2.dnn.readNetFromCaffe(prototxt, model) 用于进行SSD网络的caffe框架的加载 参数说明:prototxt表示caffe网络的结构文本,model ...
- python+opencv选出视频中一帧再利用鼠标回调实现图像上画矩形框
最近因为要实现模板匹配,需要在视频中选中一个目标,然后框出(即作为模板),对其利用模板匹配的方法进行检测.于是需要首先选出视频中的一帧,但是在利用摄像头读视频的过程中我唯一能想到的方法就是: 1.在视 ...
- php使用ffmpeg向视频中添加文字字幕
这篇文章主要介绍了PHP使用ffmpeg给视频增加字幕显示的方法,实例分析了php操作ffmpeg给视频增加字母的技巧,具有一定参考借鉴价值,需要的朋友可以参考下. 本文实例讲述了PHP使用ffmpe ...
- node.js+react全栈实践-Form中按照指定路径上传文件并
书接上回,讲到“使用同一个新增弹框”中有未解决的问题,比如复杂的字段,文件,图片上传,这一篇就解决文件上传的问题.这里的场景是在新增弹出框中要上传一个图片,并且这个上传组件放在一个Form中,和其他文 ...
- 音视频中的PTS和DTS及同步
视频的播放过程可以简单理解为一帧一帧的画面按照时间顺序呈现出来的过程,就像在一个本子的每一页画上画,然后快速翻动的感觉. 但是在实际应用中,并不是每一帧都是完整的画面,因为如果每一帧画面都 ...
- ffmpeg截取一段视频中一段视频
ffmpeg -i ./plutopr.mp4 -vcodec copy -acodec copy -ss 00:00:10 -to 00:00:15 ./cutout1.mp4 -y -ss ti ...
- 找出二叉查找树中指定结点的”下一个"结点(也即中序后继)
设计一个算法.找出二叉查找树中指定结点的"下一个"结点(也即中序后继).能够假定每一个结点都含有指向父结点的连接. watermark/2/text/aHR0cDovL2Jsb2c ...
- String substring(int start,int end)截取当前字符串中指定范围内的字符串
package seday01;/** * String substring(int start,int end) * 截取当前字符串中指定范围内的字符串. * java api有一个特点:通常用两个 ...
随机推荐
- POJ-3660 Cow Contest Floyd传递闭包的应用
题目链接:https://cn.vjudge.net/problem/POJ-3660 题意 有n头牛,每头牛都有一定的能力值,能力值高的牛一定可以打败能力值低的牛 现给出几头牛的能力值相对高低 问在 ...
- luogu P1375 小猫(卡特兰数)
题意 (n<=200000) 题解 把DP转移方程写出来,这不是卡特兰数吗?然后就解决了. 做完这题我发现 DP真是一个好东西. (公式连乘所以中间要加mod要不爆longlong了) #inc ...
- root of factory hierarchy
项目编译错误! project---->clean
- 学习参考《父与子的编程之旅python【第二版】》高清中文版PDF+高清英文版PDF+源代码
对于初步接触编程语言的朋友,推荐看一看<父与子的编程之旅第2版>,对于完全编程零基础的很友好! 图文并茂,过多的文字堆垒很容易让人产生厌倦情绪,也更容易让人产生放弃的想法.使用了大量插图, ...
- 《Craking the Coding interview》python实现---02
###题目:翻转一个字符串###思路:从字符串的最后一位开始,依次取###实现:伪代码.函数.类实现#伪代码: #01string=sNew_s=""for i in range( ...
- FarPoint自动换行
单元格自动换行 FarPoint.Win.Spread.CellType.TextCellType mType = new FarPoint.Win.Spread.CellType.TextCellT ...
- 【我们都爱Paul Hegarty】斯坦福IOS8公开课个人笔记3 Xcode、Auto Layout及MVC
继续上一话中的计算器Demo.上一话讲到类必须被初始化.类中的属性也必须被初始化,所以你不能仅仅声明而不给它一个处置,那么问题来了,我们从storyboard中拖拽的@IBOutlet为什么仅仅有声明 ...
- POJ1284 Primitive Roots (原根)
题目链接:http://poj.org/problem?id=1284 题目描述: 题目大意: 一个质数原根的个数 题解: 结论题 一个数n的原根的个数等于$\varphi(\varphi(n))$ ...
- POJ 3273 二分答案
思路:二分答案经典题吧....注意边界就OK了 //By SiriusRen #include <cstdio> #include <algorithm> using name ...
- SparkCore基础(一)
* SparkCore基础(一) 学习Spark,首先要熟悉Scala,当然你说你会Python或者Java能不能玩Spark?能!但是不推荐,首推Scala,因为Scala非常便捷,而且Scala有 ...