如何用FFMpeg生成视频

标签: ffmpeg 视频 | 发表时间:2022-02-09 09:09 | 作者:言淦
出处:https://juejin.cn/backend

前言

FFMpeg读做“FF Mpeg”, “FF”指的是 “Fast Forward”,而“Mpeg”指的是 Moving Picture Experts Group(动态图像专家组)。

根据官方介绍,FFMpeg是一个完整的、跨平台的音频和视频录制、转换和流媒体解决方案。简单来说,只要涉及 音视频开发,基本绕不开这个工具。

一、快速入门

FFMpeg快速入门的话,建议查看阮一峰老师的《 FFmpeg 视频处理入门教程》,里面讲述了音视频处理的一些基本概念,比如FFMpeg支持的 容器编码格式以及 编码器;还有就是讲述 FFMpeg的常见用法,比如查看文件信息、转换编码格式、提取音频等。

二、音视频基础知识

我自己在使用FFMpeg的时候发现,想要把FFMpeg用得明白,一些基本的音视频基础知识的了解还是很有必要的,所以在这里做下总结。

现在短视频那么火,相信大家也是 常看,而一个视频的构成其实也不复杂,就是 图像、音频、字幕的一个组合。

对于 图像,它有两个概念需要区分好,分别是 图像格式色彩空间。图像格式就是图片压缩编码以及存储的方式,比如我们常见的 JPEGPNG。色彩空间是颜色的数学描述方式,根据不同的表示方法分为不同的色彩模型,最常用的色彩模型有三类, RGB(用于计算机图形学), YUV(用于视频系统), CMYK(用于彩色印刷)。(后面会经常看到YUV)

对于 音频,也有两个概念比较重要,一个是采集到的原始音频数据(比如PCM),另一个是压缩后的音频数据,比如AAC,后面也会经常看到。

对于 字幕,常见的有三种格式,分别是 srtssaaas

srt字幕即文本格式字幕,它算是最简单的字幕了,因为它仅由 时间和字幕内容构成,比如下面:

  # 第一行是编号,表示第几个字幕
# 第二行是时间范围,精确到毫秒
# 第三话就是显示的文本内容

0
00:00:00,000 --> 00:00:01,000
假设张三携带10万美刀进行投资

1
00:00:02,000 --> 00:00:03,000
兑换成人民币后,银行就多了10万美刀的外汇

ssa字幕是比srt字幕更先进的字幕文件格式,而与它比较类似的ass字幕其实就是ssa字幕的plus版本,ass字幕的实质是 SSA v4.00+,是基于SSA 4.00+编码构建的。下面是ass字幕的具体内容:

  # 这是从上面的srt字幕转换得到的ass字幕
# Script Info:包含脚本的头部和总体信息
# V4+ Styles:包含了所有样式的定义
# Events:包含了所有脚本的事件,有字幕、注释、图片等
[Script Info]
; Script generated by FFmpeg/Lavc58.91.100
ScriptType: v4.00+
PlayResX: 384
PlayResY: 288
ScaledBorderAndShadow: yes

[V4+ Styles]
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
Style: Default,Arial,16,&Hffffff,&Hffffff,&H0,&H0,0,0,0,0,100,100,0,0,1,1,0,2,10,10,10,0

[Events]
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
Dialogue: 0,0:00:00.00,0:00:01.00,Default,,0,0,0,,假设张三携带10万美刀进行投资
Dialogue: 0,0:00:02.00,0:00:03.00,Default,,0,0,0,,兑换成人民币后,银行就多了10万美刀的外汇

三、一个视频的构建

我之所以要用FFMpeg,源于我想通过图片生成视频,并加上音频和字幕,从而构成一个完成的视频,所以下面我主要说说在构建时的一些心路历程(坑)。

3.1 项目结构

本次实践生成的音视频都会上传到Github,可以点击这里查看:

  # 项目结构
$ tree -l -L 1
.
├── add_audio  # 添加音频
├── add_caption # 添加字幕
└── img_to_video # 图片转视频

3.2 图片生成视频

为了方便展示,我从网上随便找了一张图片:

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d40ba2c8f61e4f299577d1723c6f94b1~tplv-k3u1fbpfcp-zoom-1.image

图片转视频的命令如下:

  $ ffmpeg -r 25 -i img001.jpg -vcodec libx264 -pix_fmt yuv420p one_img_to_video.mp4
...
[libx264 @ 0x7faf5b809200] i8c dc,h,v,p: 65% 19%  9%  7%
[libx264 @ 0x7faf5b809200] kb/s:8960.40

下面是各个参数的逐个解析:

  • -r:rate,用于设定 视频帧率。视频帧率即每秒显示帧数,常见的有30FPS、25FPS或者24FPS。本次设定为25FPS,即每秒有25张图片。
  • -i:input,即输入源文件。
  • -vcodec:video codec,即视频的编码格式,常见的有H.264,即 libx264
  • -pix_fmt:pixel formats,即像素格式, yuv420p是上文提到的YUV中的一种。
  • one_img_to_video.mp4:最后输出的文件名。

生成之后的视频,可以看到 时长非常短(0秒),这是因为帧率设定是25,但是只输入了一张图片,图片数不够,所以生成的视频时长非常短。

解决办法有两种:一是降低帧率(不推荐),二是增加图片数量( 推荐)。

我一开始是通过降低帧率来提高时长(我的需求是同一张图片要显示10秒左右),因为25FPS就是一秒25张图片,那如果设置为0.1FPS,等同于 1张图片10秒,测试如下:

  $ ffmpeg -r 0.1 -i img001.jpg -vcodec libx264 -pix_fmt yuv420p one_img_to_video_small_rate.mp4

通过下图,可以看到 延长时长的目的确实达到了,但是这种方式生成的MP4其实是有问题的,不仅剪辑软件无法支持(比如剪映),在添加音频、字幕的时候也非常奇怪(血的教训)。

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5b5f3ccdbaee402792b752da27331275~tplv-k3u1fbpfcp-zoom-1.image

第二种方式是增加图片数量,这也是我使用剪映之后发现的,因为 与剪映拖动图片增加视频长度的原理是一致的:

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/907322f73aa1421ab0eab767d53d1f83~tplv-k3u1fbpfcp-zoom-1.image

批量增加图片可以随便写个脚本就可以得到,但是图片的数量需要计算一下,比如一个时长10秒,帧率25FPS的视频就需要 10 x 25 = 250张图片:

  # 输入为多张图片时,可使用这种写法
# %03d 其实就是 001、002、003...100
$ cd img_to_video

$ ffmpeg -r 25 -i img/img%03d.jpg -vcodec libx264 -pix_fmt yuv420p multi_img_to_video.mp4

这里可能有人会疑惑,为什么每次我都会带上 -pix_fmt yuv420p参数?这其实也是一个坑,因为如果不加这个参数,有些软件没办法识别生成的MP4文件,比如Mac 的 QuickTime Player

原因可以从 官方文档得到,因为我们生成视频的方式其实是通过图像序列(一系列的图片)的方式,对应的编码类型为 image2,这也是为什么有时在一些文章上可以看到他们的命令比上述命令多了 -f image2参数(加不加都无所谓)。在这种编码下,默认的 pix_fmt参数并不是 yuv420p,而是通过第一张图片得到,而JPG图片用的都是 RGB,所以最终生成的视频无法识别。

3.2 视频添加音频

通过上面的方式生成的视频是没有声音的,所以我们需要通过FFMpeg为其加上音频。

有时候我们得到的音频格式并不是 MP3,而是 WAV,这时我们可以通过下面的命令进行转换:

  $ ffmpeg -i input.wav -vn -ar 44100 -ac 2 -b:a 192k output.mp3

-i: 上文也提到过,即我们的输入文件

-vn:禁用视频,确保没有视频被包括在内

-ar:设置音频采样频率。对于输出流,它默认设置为相应的输入流的频率。对于输入流,这个选项只对音频抓取设备和原始解复用器有意义,并被映射到相应的解复用器选项中。

-ac:设置音频通道的数量。这里为2是为了确保它是立体声(2个通道)。对于输出流,它默认设置为输入音频通道的数量。对于输入流,这个选项只对音频抓取设备和原始解复用器有意义,并被映射到相应的解复用器选项中。

-b:a:将音频比特率(audio bitrate)转换为精确的192kbit/秒

上面的解释涉及到 解复用这个术语,那什么是解复用呢?当我们打开一个多媒体文件之后,第一步就是解复用,称之为Demux。为什么需要这一步,这一步究竟是做什么的?我们知道在一个多媒体文件中,既包括音频也包括视频,而且音频和视频都是分开进行压缩的,因为音频和视频的压缩算法不一样,既然压缩算法不一样,那么肯定解码也不一样,所以需要对音频和视频分别进行解码。虽然音频和视频是分开进行压缩的,但是为了传输过程的方便,还是将压缩过的音频和视频捆绑在一起进行传输。所以我们解码的第一步就是将这些绑在一起的音频和视频流分开来,也就是传说中的**解复用。**简单来说,解复用这一步就是将音频流和视频流分开,方便后续解码。

转换之后就可以为视频添加音频了,这里使用的视频是上文生成的图片视频(注意添加音频也能用wav格式,只不过我习惯用mp3)

  # 拷贝视频
$ cp img_to_video/multi_img_to_video.mp4 add_audio/input.mp4

# 添加音频有多种方式:
# 方式一:流拷贝(不推荐)
# 这种方式没有编解码的过程,只有解复用,所以速度很快,目前亲测不成功,不太建议
$ ffmpeg -i input.mp4 -i input.mp3 -codec copy audio_copy.mp4

# 方式二:手动选择特定流(不推荐,亲测无效)
$ ffmpeg -i input.mp4 -i input.mp3 -map 0:v -map 1:a -c copy audio_manually.mp4

# 方式三:重新编码(亲测有效)
$ ffmpeg -i input.mp4 -i input.mp3 -c:a aac -c:v libx264 audio_recode.mp4

# 有时候我们的音频长度大于视频长度,比如本次音频长度为20s,视频长度为10s,使用上面的命令会把视频长度拉长到20s
# 如果想要音频长度与视频长度保持一致,可加上 -shortest 参数
$ ffmpeg -i input.mp4 -i input.mp3 -c:a aac -c:v libx264  -shortest audio_recode_short.mp4

3.3 视频添加字幕

添加完音频后,就可以添加字幕了,关于字幕转换工具,可以自己手写一个,也可以用现成的,比如下面这个:

TXT to SRT Converter

使用起来也非常方便,每一行就是一行字幕,最后设置好起始时间就可以了(不一定与实际朗读匹配):

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7337142736134c72ab1b37e2b2239c48~tplv-k3u1fbpfcp-zoom-1.image

添加srt字幕的命令如下:

  # 拷贝之前生成好的视频
$ cp add_audio/audio_recode.mp4 add_caption/input.mp4

# 添加字幕
$ ffmpeg -i input.mp4 -vf subtitles=input.srt video_with_srt.mp4

# 有时候可能会遇到下面的报错:Too many packets buffered for output stream 0:1
# 该异常抛出的原因是有些视频数据有问题,导致视频处理过快,容器封装时队列溢出
# 可以通过增大容器封装队列大小来解决,比如设置最大封装队列的大小为1024
$ ffmpeg -i input.mp4 -vf subtitles=input.srt -max_muxing_queue_size 1024 video_with_srt.mp4 

有时候我们需要自定义字幕的样式,或者字幕的位置,这时可以先把srt字幕转换为ass字幕,再做调整。如果你安装了FFMpeg,一行命令就能完成转换,如果没有安装,也可以用一些在线工具实现,比如 字幕酱

FFMpeg转换命令:

  $ ffmpeg -i input.srt output.ass

添加ass字幕命令:

  
$ ffmpeg -i input.mp4 -vf "ass=output.ass" video_with_ass.mp4

最终效果如下:

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5d784de9975640b088d3355606954213~tplv-k3u1fbpfcp-zoom-1.image

如果想要控制字幕使用的文字、文字大小、以及显示位置等,则需要修改 [V4+ Styles]里面的内容:

  # 一共分为两行,第一行是字段名,第二行是字段值
# Fontname:字型
# Fontsize:字体大小
# MarginL:字幕距左边的距离,取值范围是0-PlayResX的数值
# MarginR:字幕距右边的距离,取值范围是0-PlayResX的数值
# MarginV:字幕高度,取值范围是0-PlayResY的数值
[V4+ Styles]
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
Style: Default,Arial,16,&Hffffff,&Hffffff,&H0,&H0,0,0,0,0,100,100,0,0,1,1,0,2,10,10,10,0

注:其他参数的说明可参考 这篇文章

假设我要把字幕大小改为20、且字幕往上移动,则对应的改动如下:

  [V4+ Styles]
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
Style: Default,Arial,20,&Hffffff,&Hffffff,&H0,&H0,0,0,0,0,100,100,0,0,1,1,0,2,10,10,50,0

最后重新添加即可:

  $ ffmpeg -i input.mp4 -vf "ass=new.ass" video_with_new_ass.mp4

最终效果如下:

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ba17714af3f44dbcb2f46b41b4cc0118~tplv-k3u1fbpfcp-zoom-1.image

写在最后

以上就是如何用FFMpeg构建完成视频的全流程了,希望对大家有所帮助!

参考教程

FFmpeg 视频处理入门教程
FFmpeg Formats Documentation
ffmpeg图片视频互转
Convert audio files to mp3 using ffmpeg
FFMPEG深入理解
TXT to SRT Converter
在线字幕格式转换工具
解决FFmpeg抛出的"Too many packets buffered for output stream 0:1."

相关 [ffmpeg 视频] 推荐:

ffmpeg裁剪合并视频

- - inJava
这里裁剪是指时间轴裁剪,不是空间裁剪. 比如说,你想把视频的从一分20秒开始,30秒的视频裁剪出来,保存成一个视频. ffmpeg提供简单的命令参数:. -ss 开始时间,如: 00:00:20,表示从20秒开始;. -t 时长,如: 00:00:10,表示截取10秒长的视频;. -i 输入,后面是空格,紧跟着就是输入视频文件;.

ffmpeg 视频转码例子集合

- - 开源软件 - ITeye博客
-muxrate 复用码率,设置之后整体码率模式才是CBR. -c:v mpeg2video MPEG2视频编码. -flags ildct+ilme 隔行扫描. -top 隔行扫描前场/后场优先模式 ,1是前场(顶场),0是后场(底场). -streamid 设置视频、音频PID,0视频,1音频.

如何用FFMpeg生成视频

- - 掘金 后端
FFMpeg读做“FF Mpeg”, “FF”指的是 “Fast Forward”,而“Mpeg”指的是 Moving Picture Experts Group(动态图像专家组). 根据官方介绍,FFMpeg是一个完整的、跨平台的音频和视频录制、转换和流媒体解决方案. 简单来说,只要涉及 音视频开发,基本绕不开这个工具.

ffmpeg 录制udp电视信号,视频文件

- - 开源软件 - ITeye博客
ffmpeg -i udp://@:6980  -map 0:p:254  -acodec copy -vcodec copy -sameq  254.ts -map 0:p:255  -acodec copy -vcodec copy -sameq   255.ts  -map 0:0 -map 0:0.

使用ffmpeg合并视频文件的三种方法

- - biAji HeRe
ffmpeg合并视频的方法有三种. 其实在ffmpeg的 FAQ文档中有比较详细的说明. 使用concat协议进行视频文件的合并. 这种方式的适用场景是:视频容器是MPEG-1, MPEG-2 PS或DV等可以直接进行合并的. 换句话说,其实可以直接用cat或者copy之类的命令来对视频直接进行合并.

[原]FFmpeg续篇:截取视频片段转成GIF动画

- - 呦呦鹿鸣
前段时间写过 一篇文章,介绍了FFmpeg的几个常用的命令行. 最近,项目里需要做一个把视频片段转成GIF动画的功能,便于用户分享到微博. 惊奇地发现,原来强大的FFmpeg是支持的. 可以简单地执行下面的命令行:. 意思是:将D:\Media目录下的源文件bear.wmv,从第25秒的位置开始,截取10秒长度的视频转成GIF文件,保存为D:\a.gif.

视频直播方案(nginx-rtmp-module ffmpeg) - Andrew's BlogAndrew's Blog

- -
视频直播方案(nginx-rtmp-module ffmpeg). 本文将介绍如何从零搭建流媒体服务器作为直播方案. 一般视频录像板或者网络摄像头仅支持RTSP服务,或自己的私有协议. 因为RTSP是基于TCP的协议一般浏览器是无法支持创建Socket与其他服务相连前端无法播放. 私有协议仅能在IE浏览器下调用OCX插件播放视频,兼容性太差.

利用ffmpeg和SDL实现一个跨android版本的音视频播放器

- - ITeye博客
绝对原创:转载请注明出处. 1:从 http://www.libsdl.org/tmp/下载SDL for android 版本(我下载的是 SDL-1.3.tar.gz ). 2:从 https://github.com/havlenapetr/FFMpeg下载 havlenapetr 的ffmpeg for android版本.

采用FFmpeg从视频中提取音频(声音)保存为mp3文件

- - 行业应用 - ITeye博客
采用FFmpeg从视频中提取音频(声音)保存为mp3文件. 采用FFmpeg从视频中提取音频(声音)保存为mp3文件. 作者:雨水,日期:2016年1月9日 CSDN博客:http://blog.csdn.net/gobitan. 摘要:看到好的视频文件,如果想把其中的音频(声音)提取出来. 网络上肯定有不少类似工具,但作为技术人员提到音视频处理,肯定会想到FFmpeg.

OpenAI Whisper + FFmpeg + TTS:动态实现跨语言视频音频翻译 - 掘金

- -
本文作者系360奇舞团前端开发工程师. 本文介绍了如何结合 OpenAI Whisper、FFmpeg 和 TTS(Text-to-Speech)技术,以实现将视频翻译为其他语言并更换声音的过程. 我们将探讨如何使用 OpenAI Whisper 进行语音识别和翻译,然后使用 FFmpeg 提取视频音轨和处理视频,最后使用 TTS 技术生成新的语音并替换原视频的音轨.