FFmpeg

  FFmpeg是一款命令行式的跨平台的影音流处理工具。Linux静态编译版本可以在这里下载

视频切片

  一个完整的视频,长度一般有好几个小时。想要分布式处理,最简单的思路当然是先在server端将完整的视频切割成多个小的视频块,然后将这些块分配到转码集群client端,转码完成后再传回并进行合并。
一开始想通过:
-ss position (input/output)
      When used as an input option (before -i), seeks in this input file to position. Note that in most formats it is not possible to seek exactly, so ffmpeg will seek to the closest seek point before position. When transcoding and -accurate_seek is enabled (the default), this extra segment between the seek point and position will be decoded and discarded. When doing stream copy or when -noaccurate_seek is used, it will be preserved.

  像这样:

1
ffmpeg -ss 00:00:00 -t 00:00:30 -i input.mp4 -vcodec copy -acodec copy output.mp4

  可以将input.mp4的前30秒无损截取出来,输出为output.mp4。因为-vcodec和-acodec均为copy,所以该操作对于x264编码基本相当于二进制流的拷贝复制,截取速度就是硬盘的读写速度。
  所以天真的以为只需要一个脚本重复调用该命令行,修改其中的起始时间参数就可以达到切片的效果。
  然而sometimes naive,在实际测试的时候发现最终切出来的各个视频片段并非连续的,两个相邻切片中间会有一些帧数的跳跃,切完之后再合并在一起完全没法看。
  原因在官方说明里也写了:
Note that in most formats it is not possible to seek exactly, so ffmpeg will seek to the closest seek point before position.
  按照-ss和时间长度截取片段时,并非严格按照精确时间来截取的,而是按照关键帧来截取的,这就导致相邻两个切片并非连续的。

按关键帧进行分割

  通过简单的-ss来进行按时间分割不适合分布式转码的切片加工,只能通过按关键帧进行分割了,继续去官方文档找有没有关键帧切割的解决方案。

-f segment,决定就是你了

1
2
ffmpeg -i input.mp4 -acodec copy -vcodec copy \
  -f segment -segment_time 20 -reset_timestamps 1 -map 0:0 -map 0:1 %d.mp4

  以上命令行可以达到按关键帧分割视频的效果,各切片间可以完美衔接。
-segment_time参数指定每个切片的大致时长
-reset_timestamps设置每个切片的时间戳从零开始,不设置的话播放起来会有问题
-map指定提取的通道,比如某些多音轨的视频就会有多个音轨通道,一般0:0和0:1是默认的audio通道和video通道
%d.mp4指定输出切片的命名格式


  切片完成,将一部时长为2h37min的视频切成了141个片段


  检测片段0.mp4,与原视频编码一致。

视频转码

  FFmpeg支持各种格式的转码,最简单的压制成720p.x264

1
ffmpeg -i input.mp4 -vcodec libx264 -s 1280x720 output.mp4

  更多的还可以指定-crf参数或者别的参数达到自己的需求。

切片合并

  构建filelist.txt,按"file '0.mp4'"的格式顺序写入各切片的文件名。

  然后执行以下命令就可以将各切片合并成一个完整的视频

1
ffmpeg -f concat -i filelist.txt -c copy output.mp4