FFmpeg 的介绍与使用

FFmpeg:Fast Forward Moving Picture Experts Group

FFmpeg 命令行工具的使用

在 FFmpeg 官网 可以下载对应平台的可执行程序包,比如 Windows 平台的下载地址是:https://ffmpeg.zeranoe.com/builds/,下载解压后就能在 bin 文件夹下能看到三个可执行程序:ffmpeg、ffplay、ffprobe,配置好环境变量后即可使用。

ffprobe

ffprobe 是用于查看媒体文件头信息的工具,常用命令有:

ffprobe INPUT

ffprobe -show_format INPUT

ffprobe -show_streams INPUT

ffprobe -show_frames INPUT

ffprobe -show_packets INPUT

...

1

2

3

4

5

6

show_format 用于查看文件格式、时长、码率等信息,较为简略;

show_streams 用于查看视频流、音频流的信息,包括编码器、帧率、采样率、宽高、像素格式、采样格式、码率、时长、总帧率等等,较为详细,是最常用的一个功能;

show_frames、show_packets 用于查看每一帧(解码前/解码后)的信息,可以配合 select_streams 使用;

关于 ffprobe 的更多命令可以查看官网文档:ffprobe Documentation

ffplay

ffplay 是用于播放媒体文件的工具,常用命令有:

ffplay INPUT -loop 10 // 循环播放 10 次

ffplay INPUT -ast 1 // 播放视频中的第一路音频流

ffplay INPUT -vst 1 // 播放视频中的第一路视频流

ffplay INPUT -x WIDTH -y HEIGHT // 指定宽高播放

...

1

2

3

4

5

播放原始音频/视频数据:

ffplay INPUT.pcm -f s16le -channels 2 -ar 44100

ffplay -f rawvideo -pixel_format yuv420p -s 480*480 INPUT.yuv(或 rgb)

1

2

指定播放过程中音视频同步的方式:

// 以音频(或视频、或外部时钟)作为基准进行音视频同步

ffplay INPUT -sync audio(或 video、ext)

1

2

关于 ffplay 的更多命令可以查看官网文档:ffplay Documentation

ffmpeg

ffmpeg 是强大的媒体文件转换工具,常用于转码,可选命令非常多,编码器、视频时长、帧率、分辨率、像素格式、采样格式、码率、裁剪选项、声道数等等都可以自由选择:

ffmpeg -i INPUT -codec:v h264 -codec:a aac -s 644x360 OUTPUT

1

更详细的内容可以查看官网文档:ffmpeg Documentation

通用选项

还有一些命令可以用于查看 ffmpeg 支持的编解码器、像素格式、采样格式等信息,即上面 3 个文档都有的 Generic options 部分,上述三个命令都可以查看这些信息,比如:

ffmpeg -encoders

ffprobe -sample_fmts

ffplay -pix_fmts

1

2

3

FFmpeg 源码结构

主要组成

这部分直接引用自雷神的博客:FFMPEG中最关键的结构体之间的关系

FFMPEG中结构体很多。最关键的结构体可以分成以下几类:

a) 解协议(http,rtsp,rtmp,mms)

AVIOContext,URLProtocol,URLContext主要存储视音频使用的协议的类型以及状态。URLProtocol存储输入视音频使用的封装格式。每种协议都对应一个URLProtocol结构。(注意:FFMPEG中文件也被当做一种协议“file”)

b) 解封装(flv,avi,rmvb,mp4)

AVFormatContext主要存储视音频封装格式中包含的信息;AVInputFormat存储输入视音频使用的封装格式。每种视音频封装格式都对应一个AVInputFormat 结构。

c) 解码(h264,mpeg2,aac,mp3)

每个AVStream存储一个视频/音频流的相关数据;每个AVStream对应一个AVCodecContext,存储该视频/音频流使用解码方式的相关数据;每个AVCodecContext中对应一个AVCodec,包含该视频/音频对应的解码器。每种解码器都对应一个AVCodec结构。

d) 存数据

视频的话,一般每个 AVPacket 是一帧;音频可能有好几帧

解码前数据:AVPacket(h264, aac)

解码后数据:AVFrame(yuv, pcm)

模块简介

AVUtil:核心工具库,最基础的模块之一,其它模块经常依赖该库做一些基本的音视频处理操作,比如 av_image_fill_arrays(填充原始图像数据到 AVFrame)、av_image_get_buffer_size(根据图像宽高、格式获取填充该图像需要的字节数)、av_get_pix_fmt_name(获取像素格式的名称) 等等。

AVFormat:文件格式和协议库,最重要的模块之一,封装了 Protocol 层和 Demuxer、Muxer 层。常用于读写文件及文件信息,比如 avformat_write_header(写文件头)、av_write_trailer(写文件尾)、av_read_frame (从文件中读取一帧编码后的图像/音频数据)、av_write_frame(往文件中写一帧编码后的图像/音频数据)、av_seek_frame(给定一个时间戳,移动读指针到对应位置)等等。

AVCodec:编解码库,最重要的模块之一。FFmpeg 默认不会添加 libx264、FDK-AAC 等库,但 FFmpeg 可以像一个平台一样,将其它第三方的 Codec 以插件的形式添加进来,并为开发者提供统一的接口。编解码需要用到的函数基本都在该库中,比如 avcodec_find_decoder(找到对应的第三方解码器)、avcodec_decode_video2(使用对应的解码器解码一帧图像/音频数据)。

AVFilter:滤镜库。该模块提供了包括音频特效和视频特效的处理,比如把 “drawbox=10:20:200:60:red@0.5” 这条命令,传递给函数 avfilter_graph_parse() 解析,并传递原始图像数据到该 filter 中,就能在图像坐标为 (10, 20) 的点上生成一个宽高为 (200, 60) 、透明度为 0.5 的红色矩形。

以上是 FFmpeg 最常用的四个库,此外还有 AVDevice(输入输出设备库)、SwrResample(音频重采样,可转换音频的声道数、数据格式、采样率等格式)、SWScale(可用于处理像素格式转换的库)等就不一一介绍了,实际上,到对应的 FFmpeg 源代码对应的头文件看看都提供了什么函数,就大致能了解该库的作用了。

在 FFmpeg 中,还有一个类型的 filter 为 bit stream filter,顾名思义,该 filter 处理的是流数据,主要用于某些格式的封装转换行为。比如 AAC 编码,常见的有两种封装格式,一种是 ADTS 的流,一种是封装在 MPEG4 里面的格式,这种格式会在每一帧前面拼接一个由声道、采样率等信息组成的头,该 filter 中的一个类型,名为 aac_adtstoasc,可以很方便地把 ADTS 转换为另一种格式,常用于编码过程中。与之相对应的,H264 也有两种封装格式,一种是 MP4 封装的格式,一种是裸的 H264 格式(一般称为 annexb 封装格式),用于转换的 filter 名为 h264_mp4toannexb。若要使用这两个 filter,需要在编译 ffmpeg 的时候开启:

--enable-bsf=aac_adtstoasc

--enable-bsf=h264_mp4toannexb

1

2

常用 API 分析

通用 API

av_register_all

编译配置(–enable、–disable) FFmpeg 的时候,会生成两个文件:config.mk、config.h。config.mk 会实际上就是 makefile 文件需要包含进去的子模块,会作用在编译阶段,帮助开发者编译出正确的库;而 config.h 是作用在运行阶段,这一阶段将确保需要注册哪些容器以及编解码格式到 FFmpeg 框架中。所以该函数的内部实现会先调用 avcodec_register_all 来注册所有 config.h 里面开放的编解码器,然后会注册所有的 Muxer 和 Demuxer,最后注册所有的 Protocol。如此,在 config 的过程中,enable、disable 的选项就作用到了运行时,该函数的源码分析涉及的源文件包括 url.c、alformats.c、mux.c、format.c 等文件。

av_find_codec

在 avcodec_register_all 函数里面已经把编码器和解码器都存放到了一个链表中,因此都是从该链表进行遍历查找

avcodec_open2 分析

打开编解码其的时候就会用到该函数,参数有三个,第一个是 AVCodecContext,如果想要传入私有参数,比如 preset、tune、profile,则可以为设置到 priv_data 参数中。具体到函数实现时,它会找到对应的实现文件,比如,如果打开的是 libx264 编码器,那么实际上的 Codec 为 libx264.c 中的 ff_libx264_encoder,Codec 的生命周期方法就会委托给该结构体对应的函数指针所指向的函数。open 对应的就是 init 函数指针所指向的函数,该函数会调用具体的编码库的 API,并以对应的 AVCodecContext 中的 priv_data 来填充对应第三方库所需要的私有参数。

av_codec_close 分析

和 open 类似,找到对应的实现文件中的 close 函数指针所指向的函数,然后该函数会调用对应第三方库的 API 来关闭掉对应编码库。

其实 FFmpeg 所做的事情就是透明化所有的编码库,用自己的封装来为开发者提供统一的接口,开发者只需要在打开编解码库时指定编解码器的 ID 即可,之后编码、解码、关闭资源都会找到对应的实现文件去做具体的事情。

解码时用到的 API

avformat_open_input

该函数会根据提供的的文件路径判断文件的格式,继而决定使用哪一个 Demuxer。比如,如果是 flv 文件,那么 Demuxer 就会使用对应的 ff_flv_demuxer,之后关键的生命周期方法 read_header、read_packet、read_seek、read_close 都会使用 ff_flv_demuxer 中函数指针指定的函数。read_header 函数会将 AVStream 结构体构造好。

avformat_find_stream_info

这个函数非常重要,该方法的作用是将所有 Stream 的 MetaData 信息填充好,方法内部会先查找对应的解码器,并打开,紧接着利用 Demuxer 中的 read_packet 函数读取一段数据进行解码,解码数据越多,分析出的流数据就会越准确,本地资源会比较快,网络资源则较慢。该函数提供了几个参数可以控制读取数据的长度,分别为:probe_size、max_analyze、fps_probe_size,这几个参数的值越小,读取速度越快,信息则相对不够准确。

av_read_frame

该方法读取出来的数据是 AVPacket,该函数的实现首先会委托到 Demuxer 的 read_packet 方法,然后在该函数中把未处理完的压缩数据进行缓存处理。

avcodec_decode

如果要解码 H264,会找到 ff_h264_decoder,其中最重要的三个声明周期方法为 init、decode、close。

编码时用到的 API

avformat_alloc_output_context

和 avformat_open_input 类似,该函数最终会找到对应的格式复制给 AVFormatContext 中的 oformat。

avio_open2

该方法首先调用函数 ffurl_open,构造除 URLContext,这个结构体包含了 URLProtocol,接着调用 avio_alloc_context 方法,分配除 AVIOContext 结构体,并将上一步构造出来的 URLProtocol 传递进来,然后复制给 AVFormatContext 的属性。

编码步骤其实是解码的一个逆过程,解码过程中的 av_find_stream_info 对应到编码就是 avformat_new_stream 和 av_format_write_header,该步骤会将音频流或视频流的信息填充好,分配出 AVStream 结构体。read_header 则对应于 av_write_header,再之后是 av_write_frame、av_write_tailer,注意,av_write_header 和 av_write_tailer 必须成对出现。avcodec_encode 等函数就不一一介绍了。

一些概念

PTS 和 DTS

DTS(Decoding Time Stamp):解码时间戳,这个时间戳的意义在于告诉播放器该在什么时候解码这一帧图像

PTS(Presentation Time Stamp):显示时间戳,这个时间戳用来告诉播放器该在什么时候显示这一帧图像

DTS、PTS 是在编码的时候由编码器生成的,当视频流中没有 B 帧时,通常 DTS 和 PTS 的顺序一致。但如果有 B 帧时,解码顺序和播放顺序就有区别了。

比如一个视频中,帧的显示顺序是:I B B P,现在我们需要在解码 B 帧时知道 P 帧中信息,因此这几帧在视频流中的顺序可能是:I P B B,这时候就体现出 DTS 和 PTS 的作用了。DTS 告诉我们该按什么顺序解码这几帧图像,PTS 告诉我们该按什么顺序显示这几帧图像。顺序大概如下:

PTS: 1 4 2 3

DTS: 1 2 3 4

Stream: I P B B

1

2

3

time_base

时间单位,比如帧率为 30帧/s 的视频,time_base 就等于 1/30,此时 pts * time_base 就等于当前帧显示的时间

音视频的同步

音频也有 DTS、PTS 的概念,但是音频没有 B 帧,不需要双向预测,所以音频帧的 DTS、PTS 顺序是一致的。

音视频同步的方式一般有三种:同步视频到音频、同步音频到视频、同步音频和视频到外部时钟。 因为音频帧的 DTS、PTS 顺序一致,因此一般情况下,都采用同步视频到音频的方式。

gop

group of picture,可以认为是两个 I 帧之间的间隔,通常设置为帧率的两倍即可

参考帧

最多可以设置为 16 个, 通常,该值越大,压缩率越高,但大于6后对压缩率的贡献很低,推荐默认值 6。

align

很多图像相关的函数中都有这个参数,是 YUV 通道对齐时的字节单位,如果不确定,可以设为 1,但效率可能不太高。

参考:what is ‘linesize alignment’ meaning?

码率控制

主要参考:H.264 Video Encoding Guide

在 x264 中,码率控制方式主要有(通常只推荐 CRF、Two-Pass ABR 两种):

CQP(Constant Quantization Parameter )。值越大,压缩率越大,质量越低。使用方法(虽然这里使用的是命令行的形式,但在编写代码时也有参考价值):

ffmpeg -i <input> -c:v libx264 -qp 23 <output>

ffmpeg -i <input> -c:v libx265 -x265-params qp=23 <output>

1

2

仅适用于视频编码研究,除非确切知道自己在做什么,否则不要使用这种模式。

ABR(Average Bitrate)。使用方法:

fmpeg -i <input> -c:v libx264 -b:v 1M <output>

ffmpeg -i <input> -c:v libx265 -b:v 1M <output>

ffmpeg -i <input> -c:v libvpx-vp9 -b:v 1M <output>

1

2

3

避免使用这种模式!x264 开发者甚至说永远不要用它,因为质量不可控。

CBR(Constant BitRate)。

使用:

ffmpeg -i <input> -c:v libx264 -x264-params "nal-hrd=cbr:force-cfr=1" -b:v 1M -minrate 1M -maxrate 1M -bufsize 2M <output>

// VP9

ffmpeg -i <input> -c:v libvpx-vp9 -b:v 1M -maxrate 1M -minrate 1M <output>

1

2

3

输出文件需要是 .ts 文件,因为 MP4 不支持 NAL 填充,如果输入文件易于编码,使用这种方式会浪费带宽,适用于视频直播。

CRF(Constant Rate Factor)。

使用方法:

ffmpeg -i <input> -c:v libx264 -crf 23 <output>

ffmpeg -i <input> -c:v libx265 -crf 28 <output>

ffmpeg -i <input> -c:v libvpx-vp9 -crf 30 -b:v 0 <output>

1

2

3

x264 默认值是 23(推荐 17 ~ 28),x265 是28(推荐 24 ~ 34),可用值是 0 到 51。值越小编码质量越好,码率越高(每 -6,比特率加倍),值等于 0 时则为无损编码。注意:如果不是基于 ffmpeg 的播放器,可能无法解码无损压缩的视频。

很方便,但比特率、文件大小不好控制 ,适用于文件存储或需要实现最好的质量的场景;不适用于流媒体。

在代码中使用时,通过 av_dict_set 设置到 AVDictionary 中,并作为参数传递给 avcodec_open2 即可

Two-Pass ABR

用于限制输出文件的大小,比如预期视频文件有 10min(600s),200 MB:

200 * 8192 / 600 = ~2730 Kb

2730 - 128(音频常用的比特率) = 2602 kb

1

2

那么:

ffmpeg -y -i input -c:v libx264 -b:v 2600k -pass 1 -c:a aac -b:a 128k -f mp4 /dev/null && \

ffmpeg -i input -c:v libx264 -b:v 2600k -pass 2 -c:a aac -b:a 128k output.mp4

1

2

如果是 Windows 环境, /dev/null 换为 NUL,\ 换为 ^

但我一直不知道在代码中应该怎么使用这种模式,如果有了解的请指教。

VBV(Video Buffer Verifier)

可以确保最大码率限制在一个范围里,对于流式传输非常有用,可以配合到 (2-Pass)ABR 或 CRF 模式一起使用,使用方法:

// crf & vbv

ffmpeg -i <input> -c:v libx264 -crf 23 -maxrate 1M -bufsize 2M <output>

ffmpeg -i <input> -c:v libx265 -crf 28 -x265-params vbv-maxrate=1000:vbv-bufsize=2000 <output>

ffmpeg -i <input> -c:v libvpx-vp9 -crf 30 -b:v 2M <output>

// 2-pass abr & vbv

ffmpeg -i <input> -c:v libx264 -b:v 1M -maxrate 1M -bufsize 2M -pass 1 -f mp4 /dev/null

ffmpeg -i <input> -c:v libx264 -b:v 1M -maxrate 1M -bufsize 2M -pass 2 <output>

ffmpeg -i <input> -c:v libx265 -b:v 1M -x265-params pass=1:vbv-maxrate=1000:vbv-bufsize=2000 -f mp4 /dev/null

ffmpeg -i <input> -c:v libx265 -b:v 1M -x265-params pass=2:vbv-maxrate=1000:vbv-bufsize=2000 <output>

ffmpeg -i <input> -c:v libvpx-vp9 -b:v 1M -maxrate 1M -bufsize 2M -pass 1 -f webm /dev/null

ffmpeg -i <input> -c:v libvpx-vp9 -b:v 1M -maxrate 1M -bufsize 2M -pass 2 <output>

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

编写代码的话,指定 AVCodecContext 的 rc_max_rate 和 rc_buffer_size 即可。bufsize 根据你希望比特率获得多大的可变性而设置,默认为 maxrate 的两倍,如果想限制流的比特率,可以设置为 maxrate 的一半。

配合 CRF 模式使用的时候,如果设置的 crf 值过低,视频码率可能超出 -maxrate 的时候,编码器会自动调整 crf,避免出现较大的码率波动。然而,x264 不会严格控制你指定的最大码率,除非使用 2 pass 模式。

preset、tune

如果参数的设置有些复杂,可以使用 preset、tune 参数(编写代码时可以通过 av_dict_set 设置),preset 是一个用于控制编码速度压缩比的选项集合,编码速度越慢,压缩率越高。可选值有:

ultrafast

superfast

veryfast

faster

fast

medium – default preset

slow

slower

veryslow

1

2

3

4

5

6

7

8

9

编码速度对比:

tune 可根据输入情况(不同的视频类型等)调整设置(比如对画面作出一些优化),可选值有:

film        – use for high quality movie content; lowers deblocking

animation  – good for cartoons; uses higher deblocking and more reference frames

grain      – preserves the grain structure in old, grainy film material(纹理调整)

stillimage  – good for slideshow-like content(适用于 ppt 等内容)

fastdecode  – allows faster decoding by disabling certain filters

zerolatency – good for fast encoding and low-latency streaming(适用于快速编码及低延迟流)

————————————————

版权声明:本文为CSDN博主「zouzhiheng」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/u011330638/article/details/82392268

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容