ffmpeg提取最后一个关键帧

例子:某文件夹中全部是mp4视频,对每个视频提取最后一个关键帧,并保存为同名jpg,一行代码实现

以下方法要根据实际ffmpeg版本修改,不同版本返回的参数和格式都是不一样的,比如sed "s/.*pos:\([0-9]\+\).*/\1/"在5.1.1版本中需要使用sed "s/.*pos:[ ]*\([0-9]\+\).*/\1/",而时间戳为sed "s/.*pts_time:\([0-9]\+\).*/\1/"

方法1:

for i in /path/to/folder/*.mp4; do ffmpeg -i "$i" -vf "select='eq(pict_type,I)'" -skip_frame nokey -frame_pts 1 -f image2 -vframes 1 "${i%.*}.jpg"; done

说明:采用for do done的方式,比较好理解,其中ffmpeg中命令的意思是:找到视频中的关键帧,-vf "select='eq(pict_type,I)'" 选项指定了只选择关键帧,-skip_frame nokey 选项指定跳过非关键帧,-frame_pts 1 选项指定根据时间戳获取最后一个关键帧,-f image2 选项指定输出图像格式为 JPEG,-vframes 1 选项指定仅提取一帧,"${i%.*}.jpg" 则指定输出文件名为输入文件名去掉扩展名后加上 .jpg

注意,如果修改jpg的保存地址,例如保存在a/b/c中,则最后jpg处修改为a/b/c/"$(basename "$i" .mp4)".jpg

方法2:

for i in /path/to/folder/*.mp4; do ffmpeg -i "$i"  -vf "select='eq(pict_type,I)',showinfo" -f null - 2>&1 | grep "pict_type:I" | tail -n 1 | sed "s/.*pts_time:\([0-9]\+\).*/\1/" | xargs -I{} ffmpeg -i "$i" -vf "select='eq(pict_type,I)'" -ss {} -vframes 1  "${i%.*}.jpg" done

说明:这个方法是找到关键帧,然后提取每个帧的起始时间,然后取最后一个帧的时间,然后提取

setpts 是 FFmpeg 中用于设置视频帧时间戳的滤镜。在命令中使用 setpts=N/TB 参数,其中 N 表示帧时间戳(PTS)的新值,TB 表示时间基准(timebase),即单位时间所包含的时间长度。这里的 N/TB 就是新的时间戳的值,其单位是秒。

使用 setpts=N/TB 参数可以改变视频帧的播放速度,因为它可以改变视频帧的时间戳,从而影响视频帧的播放顺序和速度。

对于本题中的命令,使用 setpts=N/TB 参数可以让 FFmpeg 从视频中提取关键帧并设置时间戳,将时间戳设置为关键帧出现的时间点,保证提取的关键帧是视频中真实存在的帧,而不是重新编码后的帧。这样,在提取关键帧的同时,可以保证提取出来的帧不会因为时间戳的问题出现问题。

方法3:

ls /path/to/folder/*.mp4 | xargs -I{} sh -c 'ffmpeg -i "{}" -vf "select=eq(pict_type\,I)" -ss $(ffmpeg -i "{}" -vf "select=eq(pict_type\,I),showinfo" -f null - 2>&1 | grep "pict_type:I" | tail -n 1 | sed "s/.*pts_time:\([0-9]\+\).*/\1/") -vframes 1 "${1%.*}.jpg"' sh {} \;

说明:该方法采用ls的方式,所以xargs中需要采用sh -c 

注意,如果修改jpg的保存地址,例如保存在a/b/c中,则最后jpg处修改为a/b/c/"$(basename "$1" .mp4)".jpg

posted @ 2023-02-20 13:51  海_纳百川  阅读(1007)  评论(0编辑  收藏  举报
本站总访问量