一些ffmpeg处理视频笔记。

裁剪黑边

主要使用 ffmpeg 的 crop 以及 cropdetect 参数。

视频左上角为坐标(0,0),单位为像素。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
ffmpeg -i input.mp4 -vf "cropdetect=24:16:0" dummy.mp4

# 可以得到如下所示信息
# [Parsed_cropdetect_0 @ 0x7fab586781c0] x1:36 x2:1883 y1:0 y2:1079 w:1840 h:1072 x:40 y:4 pts:3584 t:0.233333 crop=1840:1072:40:4

# Ctrl + C 停止,复制 crop=1840:1072:40:4 用于后面步骤
# 注意视频黑边 是否会变化,否则需取舍使用哪些参数。

# 也可以使用 ffplay 预览,不用'压制'即可得到 crop 信息。
ffplay input.mp4 -vf cropdetect=24:16:0

# 后面的参数 24 16 0 为默认值,更多请移步 [官方文档](https://www.ffmpeg.org/ffmpeg-filters.html#cropdetect)

关于 crop,第一个 1840 表示裁剪出来的矩形长1840像素,宽1072像素,从(40,0)坐标处开始裁剪。crop官方文档

除了使用 ffmpeg 的 cropdetect 参数外,可以截取一张视频图片,使用 GIMP 等图像工具,手动测量得到需要的参数。 以 GIMP 为例: 1. 拉出参考线,Ctrl+鼠标滚轮放大,M 移动工具; 2. Shift + M 换到测量工具,Ctrl 拉出水平垂直测量线。

notes-for-ffmpeg-2019-6-2-18-7-13.png

notes-for-ffmpeg-2019-6-2-18-7-28.png

去水印

和裁剪黑边类似,使用 delogo 命令,官方文档

同样需要定位位置,使用上面提到的 GIMP 测量坐标。 delogo=x=1742:y=992:w=132:h=40,如其意,x y坐标起点(左上角0、0),w h宽和高。 一次可以定义多个去水印位置,使用 , 分隔,放置在滤镜参数里面。例子如下:

1
2
3
4

ffmpeg -y -i in.mp4 -vf "delogo=x=1472:y=35:w=408:h=42,delogo=x=1742:y=992:w=132:h=40,crop=1840:1040:40:0" \
-metadata comment='made by rachpt' -max_muxing_queue_size 1024 out.mp4

两个水印,加上裁剪黑边。

效果对比如下:

notes-for-ffmpeg-2019-6-2-18-10-25.png

notes-for-ffmpeg-2019-6-2-18-10-38.png

notes-for-ffmpeg-2019-6-2-18-10-54.png

倍速

详见: https://github.com/rachpt/shell-scripts/blob/master/speed_up_video.sh

视频

1.7 倍速:

1
2
3
ffmpeg -i "$i" -r 30 -c:v libx264 -preset fast \
-filter_complex "[0:v]setpts=10/17 *PTS[v];[0:a]atempo=1.7[a]" \
-map "[v]" -map "[a]" "../encode/$i" -y

音频

如上,[0:a]atempo=1.7[a]

字幕

使用shell来完成,

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
for i in `grep -Eo '([0-9]{2}:){2}[0-9]{2},[0-9]{3}' "$file"` ;do 
  a=${i:0:2};
  b=${i:3:2};
  c=${i:6:2};
  d=${i:9:3};
  # 匹配视频 1.7 倍速
  total=`echo "scale=0;((($a*60+$b)*60+$c)*1000+$d)/1.7"|bc`;
  e=$((total % 1000));f=$((total / 1000));
  g=$((f % 60));
  h=$((f / 60));x=$((h % 60));
  y=$((h / 60)) ;
  j=`printf "%02d:%02d:%02d,%03d\n" $y $x $g $e`;
  sed -i "s/$i/$j/" "$file";
done 

# 最后封装在一起参数
ffmpeg -i in.mp4 -i sub.srt -c:s mov_text -c:v copy -c:a copy out.mp4

音视频提取与合并

提取

1
2
3
4
# 提取视频
ffmpeg -i input.mp4 -c:v copy -an video.mp4
# 提取音频
ffmpeg -i input.mp4 -c:a copy -vn audio.m4a

其中 v 视频,a 音频。

合并

1
2
3
4
5
6
7
8
9
# 视频 音频 (字幕)分别单文件
ffmpeg -i input.mp4 -i audio.m4a -i sub.srt -c:v copy -c:a copy -c:s mov_text output.mp4
# 视频里面有音轨,需要替换音轨
ffmpeg -i input.mp4 -i audio.m4a -c:v copy -c:a copy -map 0✌️0 -map 1🅰️0 output.mp4
# 追加音轨,再加一个  -map 0🅰️0

# 封装多字幕
ffmpeg -i input.mp4 -i chs.srt -i eng.srt -c copy -map 0:0 -map 0:1 -map 1:0 -map 2:0 \
-c:s mov_text -c:s mov_text output.mp4

map 参数第一个表示 输出文件,0 第一个,1 第二个,v/a 和上面提到的一样,表示视频 / 音频,最后面的数字表示输入文件里面的第几个视频 / 音频 轨道。通常一个视频 一个视频轨道一个音频轨道,这个需要自己看 mediainfo

删除压制信息

删除视频文件 mediainfo 里面的 Writing library 和 Encoding settings, 添加如下参数

1
2
3
4
5
6
7
8
# x265
-x265-params no-info=1

# x264 (ffmpeg 4.0+)
-bsf:v 'filter_units=remove_types=6'

# 删除 General 里面的附加信息
-map_metadata -1

参考文档一文档二

停止播放

ffplay 使用 -t 时间 可以只播放一小段,但是并不会到时间后退出播放,而是会处于等待状态。 添加 -autoexit 参数即可解决该问题。

其他

  • 使用 -ss 0:0:0 -t 1:0:0 后可能会遇到下面的报错,
1
Too many packets buffered for output stream 0:1.

只需要多加一个 -max_muxing_queue_size 1024 参数在其中即可解决,详见这里

  • 隐藏 版本信息编译参数

添加参数 -hide_banner 即可。

  • libx265 2pass 遇到如下错误,卡住不动
1
2
3
cur_dts is invalid (this is harmless if it occurs once at the start per stream)
在不同机器上,不同版本 ffmpeg 使用相同的压制命令却不会遇到这个问题。
另外,换用 libx264 不会遇到此问题。

多番查证,可能是原视频某些信息不全所致。 最终发现是 使用的 缩放 (-s 1280x720 或者 滤镜 - vf scale=-2:720) 参数导致的,去掉缩放,2pass 不会遇到此问题。

最后曲线救国,先将原文件使用 ffmpeg -c copy 相同容器复制一份,使用新生成的文件成功缩放压制。

  • 缩放警告
1
[swscaler @ 0x558c475f32c0] Warning: data is not aligned! This can lead to a speed loss

修改 宽度 为 16 的倍数。参考文档