ffmpeg是开源的、功能强大的视频、音频、图像处理工具,在最近的工作中,需要从视频中抽取图像,并准确计算每张图片对应在视频播放中的时间点。在网上找了一圈发现,所有的参考资料基本分为两种:
1、通过c语言调用ffmpeg进行抽帧,无奈技术太渣,此路不通
2、通过ffmpeg命令进行抽帧,这种方式无论抽取关键帧还是按照时间间隔(隔 N 秒抽一帧)抽帧都很方便,速度也快
但是找了一圈就是没有如何计算图像在视频中时间点的方法,今天在此对于如何使用ffmpeg命令抽帧(关键帧、时间间隔抽帧),以及如何计算图片时间点做一个详细的总结。
- 安装ffmpeg
抽帧第一步,安装ffmpeg。下面介绍在Ununtu 16.04系统上快速安装ffmpeg工具的方法:
1、下载 yasm-1.3.0.tar.gz 和 ffmpeg-4.0.2.tar.bz2 压缩包,下载地址
2、在压缩包的同一目录下创建 install_ffmpeg.sh 脚本文件,脚本功能是解压压缩包并进行编译安装,脚本执行约15分钟,执行完成后使用 ffmpeg -version 检查是否安装成功
#!/bin/bash
current_path=$(`dirname $0`;pwd)
cd $current_path
# install yasm
tar xvf yasm-1.3.0.tar.gz
cd yasm-1.3.0
./configure && make && make install
# install ffmpeg
cd ..
tar jxvf ffmpeg-4.0.2.tar.bz2
cd ffmpeg-4.0.2
./configure --prefix=/usr/local/ffmpeg
make && make install
# 添加环境变量
sed -i '$a PATH=$PATH:/usr/local/ffmpeg/bin' ~/.bashrc
sed -i '$a export PATH' ~/.bashrc
source ~/.bashrc # 设置生效
# ffmpeg -version # 查看版本
- 抽取关键帧和时间点计算
1、关键帧抽帧命令:ffmpeg -i xxx.mp4 -vf select="eq(pict_type\,PICT_TYPE_I)" -vsync 2 image/%09d.jpg -loglevel debug 2>&1 | grep -i "pict_type:I" | awk -F" " '{print $6}'| awk -F":" '{print $2}' > image/temp.txt (备注:抽取视频xxx.mp4的关键帧放在当前目录的image文件夹中,图片编号长度为9位数字, -loglevel debug设置是输出抽帧日志,并过滤pict_type:I关键帧信息保存到image目录的temp.txt 文件中)
2、经过步骤1处理后,image目录下保存有抽帧的结果图片,并且temp.txt文件中保存每个图像对应的时间点,一一对应,内容如下:
240.000000
241.600000
244.000000
246.400000
nan
251.200000
253.240000
255.000000
257.160000
3、由于ffmpeg日志打印存在问题,导致temp.txt文件中丢失了部分时间点(如上述的nan),约占总数的1%,对于这个问题暂时没找到解决方法,可以采用上一帧的时间点代替
- 时间间隔抽帧和时间点计算
方法一:
1、按照时间间隔抽帧命令:ffmpeg -i xxx.mp4 -vf fps=1/4 image/%09d.jpg,使用该命令对xxx.mp4文件抽帧,抽帧间隔为4秒,如果想每秒抽取多帧,可以使用命令:ffmpeg -i xxx.mp4 -vf fps=2 image/%09d.jpg(每秒抽2帧)
2、抽帧时间点计算
对于按照时间间隔抽帧的时间点计算,直观上理解通过图片 编号 * 时间间隔 就可以计算,但是实践发现,ffmpeg按照时间间隔抽帧,抽取的第一张图片对应的时间与抽帧间隔有关,总结规律如下:
process_offset = {
"1" : 0, "2" : 0,
"3" : 1, "4" : 1,
"5" : 2, "6" : 2,
"7" : 3, "8" : 3,
"9" : 4, "10" : 4
}
即:当抽帧间隔为1秒、2秒时,第一张图片对应的时间点为第0秒;当抽帧间隔为3秒、4秒时,第一张图片对应的时间点位第1秒;依次类推。因此,在上述第一张图片存在时间偏移的基础上,计算每张图片的时间点公式如下:
seconds = (image_count - 1) * SPEED + process_offset[SPEED],其中SPEED为抽帧速度,image_count为图片编号(从1开始)
方法二:
1、获取视频的time_base
ffprobe -v quiet -show_streams -select_streams v -i xxx.mp4 | grep time_base= | grep time_base | grep -v codec | awk -F"/" '{print $2}'
2、按照时间间隔抽帧,并获取每张图片的pts时间
ffmpeg -i xxx.mp4 -f image2 -vf fps=fps=1/4 image/%09d.jpg -loglevel debug 2>&1 | grep 'Read frame with' | grep -v 'out pts 0' | grep -v 'd=' | awk -F" " '{print $9}' | sort -t ',' -k 2,2 -u | awk -F"," '{print $1}' > temp.txt
3、计算图片时间点(秒)
使用第二步产生的每个pts值除以第一步得到的 time_base,就是每张图片对应的时间点