FFmpeg入门:概述与编译

1、概述

与FFmpeg初识,大约是在六年前。那时公司要求做这么一个功能:运营编辑人员通过管理后台上传本地视频文件或粘贴其他视频网站的详情页地址,获取到视频后,通过FFmpeg来转码为各种清晰度的视频。那时刚毕业不久,只略懂PHP和JAVA,音视频的知识更是一穷二白,只能通过JAVA调用系统命令行的方式使用FFmpeg,最终因为性能等问题,项目流产了。再次相遇是两年前,为提升ExoPlayer播放器的兼容性,为其添加视频软解功能。这次打开FFmpeg的姿势稍微帅一点,会使用FFmpeg API来做转码。恍恍惚惚这么多年,一直都是FFmpeg的看客,只在门口晃悠。

近来想稍微系统地学习一番FFmpeg,发现国内关于FFmpeg的相关资料甚少。可参考的有:

  • 官方文档:这里能了解到最新FFmpeg动态
  • 雷神的博客:相信国内大多数音视频开发者或多或少看过他的博文,运行过他的代码。雷神已故,甚是可惜。FFmpeg迭代很快,其中不少API使用已经过时,希望有更多的人延续雷神的分享精神。
  • 《FFmpeg从入门到精通》:本书侧重于命令行的使用,API甚少。作者应该也是大神,只是过分惜墨,导致文章内容平铺。

没有经过严格正确性验证的代码或文章,都是耍流氓。我要开始耍流氓了:

FFmpeg:A complete, cross-platform solution to record, convert and stream audio and video。稍微展开就是:

  • 它是功能完备的,性能优越的,支持跨平台的音视频处理框架。
  • record:音视频录制,更多地指将音视频保存到本地文件
  • convert:转换,包括多媒体文件格式转换,音视频编码转换,视频缩放和色彩格式转换,音频处理等
  • stream:流化音视频,更多指协议层的文件流,多媒体流等

下面是FFmpeg框架的分层模型:

FFmpeg分层模型
  • 协议层:该层处理流媒体协议的数据解析与封装,包括http,rtmp,rtsp,file等

  • 容器层:该层处理多媒体容器的解析和封装,包括mp4,flv,mkv等

  • 编解码层:该层负责音视频编解码,包括h264,h265,mp3,aac等

  • 原始数据层:该层负责原始音视频数据的处理,如视频像素格式转换,缩放,裁剪,过滤,音频重采样,过滤等,处理对象是pcm,yuv,rgb等原始数据。

  • 设备层:负责音视频播放及采集

看到这个是否联想到 TCP/IP协议族的网络分层模型,从顶到底分别是应用层,传输层,网络层,链路层,物理层。但两者的设计有个很大的不同点。网络分层模型中,普通应用开发者只能在应用层做文章;而FFmpeg分层模型相对自由灵活,每层都提供了相对解耦的类库:

  • livavformat 作用于协议层和容器层,依赖于libavcodec。
  • libavcodec 作用于编解码层。
  • ibswscale,libswresample,libavfilter作用于原始数据层。
  • libavdevice 作用于设备层。
  • livavutil 是基础公共模块,上面各个类库都会依赖于它。

每个模块的详细说明,可查看官方文档的 Libraries Documentation ,其中的常用的模块是livavutil,libavcodec及libavformat。常规的多媒体开发(不一定基于FFmpeg)都能映射到上面的FFmpeg分层模型中:如

  • 单看左边,一路下来就是播放器的实现流程。
  • 单看右边,一路上去就是直播主播端的推流流程。
  • 若是不涉及改变编码的文件格式转换,只在容器层就可实现。
  • 若只想给已有的播放器,添加更多的解码格式,只需使用到其中的编解码层。

2、编译

使用FFmpeg API的方式开发,编译源码是必然的。首先从官网下载最新的稳定版本(当前是4.1),切记不要使用主干的最新代码。原因有二:其一,主干代码经常更新,有不稳定的可能,其二,使用固定版本,方便交流和定位问题。

FFmpeg编译是我们首先需要面对的难题,基本分为下面几种情况:

  1. 通过编译FFmpeg源码的方式,安装FFmpeg。
  2. 将FFmpeg移植到移动平台(Android和iOS)的交叉编译。
  3. 将第三方类库集成到FFmpeg,如集成x264,fdk-aac。
  4. 本机编译相关类库,及在本机环境使用FFmpeg API。

经常能看到描述FFmpeg编译的文章,其下会有不少读者抱怨,我完全按你的编译配置及编译脚本执行,就是编译失败,或者执行失败。喂虾米!大体原因不外:

  • 编译环境是否匹配,包括操作系统,预安装的编译工具及特定平台的编译配置。可参考官方编译说明
  • FFmpeg版本是否一致。不同版本支持的配置项也有可能不一样。
  • 需求是否一样。FFmpeg是支持裁剪编译的。作者的编译选项可能未包含你的需要的功能。

接下来,来看一个我在mac平台上编译FFmpeg 4.1的脚本,主要用于在mac上调试运行FFmpeg的开发示例。

COMMON_OPTIONS=" \
    --disable-doc \
    --disable-programs \
    --disable-everything \
    --disable-avdevice \
    --disable-postproc \
    --disable-avfilter \
    --disable-symver \
    --disable-avresample \
    --disable-audiotoolbox \
    --disable-videotoolbox \
    --disable-appkit \
    --disable-bzlib \
    --disable-iconv \
    --disable-securetransport \
    --disable-avfoundation \
    --disable-coreimage \
    --disable-sdl2 \
    --disable-zlib \
    --enable-decoder=h264 \
    --enable-decoder=aac \
    --enable-demuxer=mov \
    --enable-demuxer=flv \
    --enable-demuxer=rtsp \
    --enable-demuxer=mp3 \
    --enable-demuxer=h264 \
    --enable-demuxer=aac \
    --enable-muxer=mp4 \
    --enable-muxer=flv \
    --enable-muxer=h264 \
    --enable-muxer=adts \
    --enable-muxer=mp3 \
    --enable-protocol=rtmp \
    --enable-protocol=file \
    --enable-bsf=aac_adtstoasc \
    --enable-bsf=h264_mp4toannexb \
    --enable-bsf=hevc_mp4toannexb \
    " && \
./configure \
    --prefix='out' \
    ${COMMON_OPTIONS} \
    && \
make -j4 && make install && make clean

将其放置于FFmpeg源码根目录下(比如命名为build_pc.sh)。执行如下终端命令:

chmod a+x ./build_pc.sh //使build_pc.sh为可执行文件
./build_pc.sh //执行构建脚本

稍等片刻,顺利的话,便可在源码根目录下看到输出文件out,其下包括头文件目录include,类库文件目录lib及示例文件目录share。仔细观察,可发现其中也就四条命令:

编译流程
  • configure 编译裁剪配置
  • make 执行编译
  • make install 执行安装,就是将相关编译好的程序,类库以及头文件示例代码拷贝到—prefix 指定的目录(上例为out目录)
  • make clean 清理编译过程产生的临时文件

FFmpeg是个庞大的类库,最好根据我们的需求,进行编译选项配置。执行如下命令,可查看到所有配置项。

./configure --help

其中常用的配置如下:

Help options:
  --list-decoders          show all available decoders
  --list-encoders          show all available encoders
  --list-hwaccels          show all available hardware accelerators
  --list-demuxers          show all available demuxers
  --list-muxers            show all available muxers
  --list-parsers           show all available parsers
  --list-protocols         show all available protocols
  --list-bsfs              show all available bitstream filters
  --list-indevs            show all available input devices
  --list-outdevs           show all available output devices
  --list-filters           show all available filters

Standard options:
  --prefix=PREFIX          install in PREFIX [/usr/local]

Configuration options:
  --disable-static         do not build static libraries [no]
  --enable-shared          build shared libraries [no]

Program options:
  --disable-programs       do not build command line programs
  --disable-ffmpeg         disable ffmpeg build
  --disable-ffplay         disable ffplay build
  --disable-ffprobe        disable ffprobe build

Documentation options:
  --disable-doc            do not build documentation

Component options:
  --disable-avdevice       disable libavdevice build
  --disable-avcodec        disable libavcodec build
  --disable-avformat       disable libavformat build
  --disable-swresample     disable libswresample build
  --disable-swscale        disable libswscale build
  --disable-postproc       disable libpostproc build
  --disable-avfilter       disable libavfilter build
  --enable-avresample      enable libavresample build (deprecated) [no]

Individual component options:
  --disable-everything     disable all components listed below
  --enable-encoder=NAME    enable encoder NAME
  --enable-decoder=NAME    enable decoder NAME
  --enable-muxer=NAME      enable muxer NAME
  --enable-demuxer=NAME    enable demuxer NAME
  --enable-parser=NAME     enable parser NAME
  --enable-bsf=NAME        enable bitstream filter NAME
  --enable-protocol=NAME   enable protocol NAME
  --enable-filter=NAME     enable filter NAME

External library support:
  --enable-libfdk-aac      enable AAC de/encoding via libfdk-aac [no]
  --enable-libmp3lame      enable MP3 encoding via libmp3lame [no]
  --enable-libx264         enable H.264 encoding via x264 [no]
  --enable-libx265         enable HEVC encoding via x265 [no]

Toolchain options:
  --arch=ARCH              select architecture []
  --cpu=CPU                select the minimum required CPU (affects
                           instruction selection, may crash on older CPUs)
  --cross-prefix=PREFIX    use PREFIX for compilation tools []
  --sysroot=PATH           root of cross-build tree
  --sysinclude=PATH        location of cross-build system headers
  --target-os=OS           compiler targets OS []

Optimization options (experts only):
  --disable-asm            disable all assembly optimizations
  --disable-neon           disable NEON optimizations

通常使用“禁大开小”的配置策略。如下是常见的禁止配置:

--disable-doc // 禁止输出文档
--disable-programs // 禁止编译执行程序 ffmpeg ffprobe ffplay
--disable-everything // 禁止所有的encoder,decoder,muxer,demuxer,parser,bsf,protocol及filter
--disable-avdevice // 禁止相关模块 这些模块在上面的分层模型提及
--disable-postproc
--disable-avfilter

根据实际的需求,再开启相关配置:

--enable-decoder=h264
--enable-decoder=aac
--enable-demuxer=mov
--enable-demuxer=flv
--enable-protocol=file

默认是只编译静态库,可通过 --enable-shared 开启动态库的编译。默认会将编译输出到 /usr/local目录,可配置--prefix=输出目录 来修改。Toolchain options通用用于交叉编译,可参考ExoPlayer集成FFmpeg

在执行编译脚本时,最开始的输出日志是最关键的,可以看出是否符合你的预期:

install prefix            out
source path               .
C compiler                gcc
C library                 
ARCH                      x86 (generic)
...
static                    yes
shared                    no
...

External libraries:

External libraries providing hardware acceleration:

Libraries:
avcodec           avformat          avutil            swresample        swscale

Programs:

Enabled decoders:
aac           h264

Enabled encoders:

Enabled hwaccels:

Enabled parsers:
mpegaudio

Enabled demuxers:
aac           flv               mov               mpegts            rtsp
asf           h264              mp3               rm

Enabled muxers:
adts              flv               h264              mov           mp3           mp4

Enabled protocols:
file              http              rtmp              rtp           tcp           udp

Enabled filters:

Enabled bsfs:
aac_adtstoasc         h264_mp4toannexb      hevc_mp4toannexb          null

Enabled indevs:

Enabled outdevs:

以上就是FFmpeg编译的基本脉络。若你能一次编译成功,且能在项目中成功运行的,那你可以去买彩票了。通常或多或少有平台相关的问题,先查找官方编译说明,未果再google一下。

3、小试牛刀

了解了FFmpeg的概貌及编译后,接下来就可使用相关的API。官方API文档,及官方示例是学习FFmpeg API的最佳资料。以此为基点,慢慢张开,若有疑问,可查看源码,亦可从网上需求答案。可用下面示例验证我们编译的类库包括哪些组件:

// 输出版本信息,编译配置等
std::cout << "version:" << av_version_info() << " avformat:" << avformat_version() << " avcodec:"
    << avcodec_version() << " avutil:" << avutil_version() << std::endl;
std::cout << "license:" << avformat_license() << std::endl;
std::cout << "configuration:" << avformat_configuration() << std::endl;

void *opaque = NULL;
// 获取所有协议
std::cout << "=== input protocols ===" << std::endl;
const char *protocol = avio_enum_protocols(&opaque, 0);
while (protocol!=NULL){
    std::cout << protocol << std::endl;
    protocol = avio_enum_protocols(&opaque, 0);
}

std::cout << "=== output protocols ===" << std::endl;
protocol = avio_enum_protocols(&opaque, 1);
while (protocol!=NULL){
    std::cout << protocol << std::endl;
    protocol = avio_enum_protocols(&opaque, 1);
}

// 获取所有的解封装器
std::cout << "=== demuxer ===" << std::endl;
const AVInputFormat *inputFormat = av_demuxer_iterate(&opaque);
while (inputFormat != NULL) {
    std::cout << inputFormat->name << std::endl;
    inputFormat = av_demuxer_iterate(&opaque);
}

// 获取所有封装器
std::cout << "=== muxer ===" << std::endl;
opaque = NULL;
const AVOutputFormat *outputFormat = av_muxer_iterate(&opaque);
while (outputFormat != NULL) {
    std::cout << outputFormat->name << std::endl;
    outputFormat = av_muxer_iterate(&opaque);
}

// 获取所有编码器
std::cout << "=== encoder ===" << std::endl;
opaque = NULL;
const AVCodec *avCodec = av_codec_iterate(&opaque);
while (avCodec != NULL) {
    if(av_codec_is_encoder(avCodec)){
        std::cout << avCodec->name << std::endl;
    }
    avCodec = av_codec_iterate(&opaque);
}

// 获取所有编码器
std::cout << "=== decoder ===" << std::endl;
opaque = NULL;
avCodec = av_codec_iterate(&opaque);
while (avCodec != NULL) {
    if(av_codec_is_decoder(avCodec)){
        std::cout << avCodec->name << std::endl;
    }
    avCodec = av_codec_iterate(&opaque);
}

// 获取所有bsf
std::cout << "=== bsf ===" << std::endl;
opaque = NULL;
const AVBitStreamFilter *bsf = av_bsf_iterate(&opaque);
while (bsf != NULL) {
    std::cout << bsf->name << std::endl;
    bsf = av_bsf_iterate(&opaque);
}

FFmpeg使用C语言编写,其API是一些核心函数和关键结构体的集合。组织方式相对松散,新手容易迷路。再细看,发现其是基于对象(结构体)及上下文的方式来贯穿多媒体处理的会话周期。在官方示例中能发现各个应用场景的使用基本套路。

后续文章以分享这些示例的学习笔记为主,与君共勉。。

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

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,619评论 4 59
  • 不少家长都感到为了孩子付出很多:要花钱给孩子买这买那,要花时间接送孩子上学放学、上辅导班等等。父母在教育孩子方面,...
    子态阅读 294评论 0 0
  • 在当下吃鸡如此大火的情况下 男女逛街什么的早已成为过去 而男女吃鸡,反倒成为了约会首选 某大神就用红蓝圆珠笔漫画的...
    Doris_KK8阅读 328评论 0 0
  • 雨打娇荷心难平, 又见流水载落红。 只道此雨世所罕, 未曾放任雨中游。 tulipjia去年同期作品,那是一场罕见...
    tulipjia阅读 176评论 0 6
  • 李扬打了个呵欠,抬手看了看表,然后打开了摄像机。 “就这样就可以了吧?”他对着正坐在不远处的王猛山大声说道。 王猛...
    楚游尘阅读 672评论 0 51