本文将引导你使用 CEF 官方工具编译出目前(2019年9月5日)最新版本 CEF ,包含详细的步骤和常见问题,编译完成后的 CEF 具备完整功能的 cef_sandbox.lib 和完整的多媒体功能(如常用的 MP3 MP4 FLV AVI 等)支持。来吧,先让我们找一台高性能电脑。

准备工作

  1. 安装 Visual Studio 2017 最新版本,安装时全部选择为默认路径
  2. 100G 以上 SSD 硬盘 + 高性能 CPU(如 Intel 8 系列以上带 K 的 CPU)内存建议 16G
  3. 设置系统虚拟内存页面文件大小为自动(否则可能会编译过程中报错)
  4. 一个稳定的 VPN 网络可以让你快速下载 CEF 和 Chromium 代码

下载代码

根据官方资料,首先在你磁盘空间充足的分区创建几个文件夹,比如我放到了 e:\code 目录下,保证这个目录所在的分区是 SSD 硬盘,否则你会被折磨死。 depot_tools 文件夹来自于官方资料中,automate 目录下存放官方资料中提供的 automate-git.py,需要注意的是,depot_tools 压缩包下载完成后用右键菜单解压,因为里面有一个 .git 文件夹有些压缩包工具会给隐藏掉,如果手动去选择文件拖拽到指定目录会导致解压的时候版本管理信息就不在了,所以这里直接右键解压到指定目录就好了。解压完成后,将 depot_tools 所在目录添加到系统的环境变量 %PATH% 中(最好放到第一位)。 接下来,运行解压出来的 depot_tools\update_depot_tools.bat 此时会更新 depot_tools 工具到最新版本,当更新完成后,在 chromium_git 目录下创建一个名为 update.bat 的批处理文件,内容如下:

1
2
3
4
set GN_DEFINES=ffmpeg_branding=Chrome proprietary_codecs=true is_official_build=true
set GN_ARGUMENTS=--ide=vs2017 --sln=cef --filters=//cef/*
:: 只更新代码
python ..\automate\automate-git.py --download-dir=e:\code\chromium_git --depot-tools-dir=e:\code\depot_tools --branch=3809 --no-build --no-distrib --force-clean

批处理的作用就是去下载 CEF 和 Chromium 代码并重置到你指定的版本,不立即编译也不做打包操作,批处理中的各个参数及具体意思参看如下内容:

1)GN_DEFINES 参数介绍:

  • ffmpeg_brandingproprietary_codecs 表示开启多媒体编解码支持,但默认仅支持一小部分,后面介绍如何支持更多。
  • is_official_build 决定了是否是编译正式版本,指定该参数为 true 基本上都是为了产品发布使用,同时也会在创建解决方案的时候生成带有 sandbox 的解决方案(如 Release_GN_x86_sandbox),而不指定这个参数是没有的。如果你不是为了贡献代码,那默认这个都加上吧。
  • use_jumbo_build 官方资料默认指定,表示是否启用试验性的 jumbo 编译,编译过程会加快很多(至少快 1 小时),但是占用 CPU 和内存(尤其是内存)会剧增。
  • is_component_build 官方资料默认指定,但我们没有开启,这个参数表示是否启用组件化编译,设置为 true 以后,base、ffmpeg 等等都会被编译为动态库,使用时也是动态链接,编译出来的 cef_sandbox.lib 只有几兆大小,并且你需要复制很多 dll 文件到项目目录下才能运行。

2)automate-git.py 参数介绍

  • --branch 表示你要下载哪个版本的代码,CEF 每个版本都有固定的分支,你去 CEF 项目页查看分支名称指定即可,这里我们编译 2019年9月份目前最新的版本 3809。
  • --no-build 表示只下载代码而不编译,这里只为下载代码,我们还要修改支持多媒体的参数,所以不进行编译
  • --no-distrib 不执行打包项目,这里只为下载代码,我们还要修改支持多媒体的参数,所以不进行打包
  • --force-clean 如果你曾经执行过这个脚本,可能会出错,则加上这个参数,它执行清理残留文件(你也可以手动在 chromium 源码目录执行 git clean -xdf 来清理目录中的多余内容)。
  • automate-git.py 的其他参数可以手动执行 python automate-git.py --help 来查看

执行上面的脚本如果没有意外,代码就已经下载好了,并且自动切换到了你要打包的分支并下载了所依赖的第三方库。下载速度依赖你的网络环境,最好是稳定、高速的 VPN 场景:

编译项目

要开启更多多媒体编解码的支持,需要去修改 ffmpeg 的头文件,与以前的版本不太一样的是,新版本 ffmpeg 的配置全部改为单独的宏了,用编辑器打开 chromium_git\chromium\src\third_party\ffmpeg\chromium\config\Chrome\win\ia32\config.h 头文件,修改你要开启的编解码能力宏为 1 即可,我修改了如下内容,你根据你的需要搜索修改就可以了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#define CONFIG_FLV_DECODER 1
#define CONFIG_H263_DECODER 1
#define CONFIG_H263I_DECODER 1
#define CONFIG_MPEG4_DECODER 1
#define CONFIG_MPEGVIDEO_DECODER 1
#define CONFIG_MSMPEG4V1_DECODER 1
#define CONFIG_MSMPEG4V2_DECODER 1
#define CONFIG_MSMPEG4V3_DECODER 1
#define CONFIG_RV10_DECODER 1
#define CONFIG_RV20_DECODER 1
#define CONFIG_RV30_DECODER 1
#define CONFIG_RV40_DECODER 1
#define CONFIG_AC3_DECODER 1
#define CONFIG_AMRNB_DECODER 1
#define CONFIG_AMRWB_DECODER 1
#define CONFIG_COOK_DECODER 1
#define CONFIG_SIPR_DECODER 1
#define CONFIG_FLV_ENCODER 1
#define CONFIG_H263_ENCODER 1
#define CONFIG_MPEG4_ENCODER 1
#define CONFIG_MSMPEG4V2_ENCODER 1
#define CONFIG_MSMPEG4V3_ENCODER 1
#define CONFIG_RV10_ENCODER 1
#define CONFIG_RV20_ENCODER 1
#define CONFIG_AAC_ENCODER 1
#define CONFIG_AC3_ENCODER 1
#define CONFIG_AC3_PARSER 1
#define CONFIG_COOK_PARSER 1
#define CONFIG_H263_PARSER 1
#define CONFIG_MPEG4VIDEO_PARSER 1
#define CONFIG_MPEGVIDEO_PARSER 1
#define CONFIG_RV30_PARSER 1
#define CONFIG_RV40_PARSER 1
#define CONFIG_SIPR_PARSER 1
#define CONFIG_AC3_DEMUXER 1
#define CONFIG_AMR_DEMUXER 1
#define CONFIG_AMRNB_DEMUXER 1
#define CONFIG_AMRWB_DEMUXER 1
#define CONFIG_AVI_DEMUXER 1
#define CONFIG_AVISYNTH_DEMUXER 1
#define CONFIG_FLV_DEMUXER 1
#define CONFIG_H263_DEMUXER 1
#define CONFIG_H264_DEMUXER 1
#define CONFIG_MPEGTS_DEMUXER 1
#define CONFIG_MPEGTSRAW_DEMUXER 1
#define CONFIG_MPEGVIDEO_DEMUXER 1
#define CONFIG_RM_DEMUXER 1
#define CONFIG_AC3_MUXER 1
#define CONFIG_AMR_MUXER 1
#define CONFIG_AVI_MUXER 1
#define CONFIG_FLV_MUXER 1
#define CONFIG_H263_MUXER 1
#define CONFIG_H264_MUXER 1
#define CONFIG_MPEGTS_MUXER 1
#define CONFIG_RM_MUXER 1

这里需要注意的是,#define CONFIG_SIPR_PARSER 1 宏一定要打开,否则会在编译时期报错,找不到指定变量,就像下图一样: 修改完 ffmpeg 的配置以后,我们就开始创建工程了,在 update.bat 相同目录下创建一个 create.bat,官方资料中说是创建在 chromium_git\chromium\src\cef 目录下的,我为了方便管理修改了一下执行目录,并且把上面修改过的 config.h 头文件放到与 create.bat 同一级目录下,这样在创建解决方案前可以自动拷贝这个头文件过去,避免出错重新编译时还要手动修改一遍,create.bat 内容如下:

1
2
3
4
5
6
7
8
9
set GN_DEFINES=ffmpeg_branding=Chrome proprietary_codecs=true is_official_build=true
set GN_ARGUMENTS=--ide=vs2017 --sln=cef --filters=//cef/*

rem 拷贝增加了多媒体配置的头文件到 ffmpeg 目录下
copy config.h .\chromium\src\third_party\ffmpeg\chromium\config\Chrome\win\ia32 /y

rem 进入 cef 目录创建项目解决方案
cd .\chromium\src\cef
call cef_create_projects.bat

脚本中设置了一些宏定义,并拷贝了 ffmpeg 的头文件到三方库目录覆盖原来的头文件,最后进入 chromium_git\chromium\src\cef 目录执行了 cef_create_projects.bat 创建项目,执行这个批处理后如果顺利则会出现如下提示: 此时工程文件都创建好了,你可以在 chromium_git\chromium\src\out 目录下看到这些工程目录: 根据你的需要编译指定版本,命令提示符切换当前目录到 chromium_git\chromium\src 目录下,根据你的需要执行 Release 还是 Debug 版本的编译:

1
ninja -C out\Release_GN_x86 cef

其中 Release_GN_x86 就是 out 目录下的目录名,根据你自己的需要编译不同版本的就可以了,单独编译 Debug 是无法使用脚本去打包的,脚本中打包要么是单独的 Release,要么就是 Debug + Release 打包。编译速度视机器性能而定,编译过程中消耗 CPU 和内存比较多,所以这个期间你这台电脑也别想做其他的事情了,老老实实的放在那里让它编译。 我小米笔记本 i7-8550U + 16G + m.2 SSD + Windows 10 17763,大概耗时 6 小时左右编译完成。编译过程中报错不要慌张,执行的命令是增量编译的,修正完错误再执行相同的命令继续编译即可,比如上面碰到的 ffmpeg 缺少配置导致编译出错的问题。下面为编译完成的结果: 这里其实已经可以使用了,但是如果你希望链接 cef_sandbox.lib,你还要单独编译一下 Release_GN_x86_sandbox,这个编译速度很快,大概十几分钟就完成了。 我尝试过如果只编译 Release_GN_x86 而不编译 Release_GN_x86_sandbox 的情况下,制作出来的二进制包中,cef_sandbox.lib 仅有 60MB+。而且连接时还会出现很多链接错误,如下所示:

1
2
3
4
5
6
7
8
1>cef_sandbox.lib(work_queue_sets.obj) : error LNK2001: 无法解析的外部符号 "protected: void __thiscall std::__1::__vector_base_common<1>::__throw_length_error(void)const " (?__throw_length_error@?$__vector_base_common@$00@__1@std@@IBEXXZ)
1>cef_sandbox.lib(scoped_defer_task_posting.obj) : error LNK2001: 无法解析的外部符号 "protected: void __thiscall std::__1::__vector_base_common<1>::__throw_length_error(void)const " (?__throw_length_error@?$__vector_base_common@$00@__1@std@@IBEXXZ)
1>cef_sandbox.lib(priority_queue.obj) : error LNK2001: 无法解析的外部符号 "protected: void __thiscall std::__1::__vector_base_common<1>::__throw_length_error(void)const " (?__throw_length_error@?$__vector_base_common@$00@__1@std@@IBEXXZ)
1>cef_sandbox.lib(worker_thread_stack.obj) : error LNK2001: 无法解析的外部符号 "protected: void __thiscall std::__1::__vector_base_common<1>::__throw_length_error(void)const " (?__throw_length_error@?$__vector_base_common@$00@__1@std@@IBEXXZ)
1>cef_sandbox.lib(pooled_single_thread_task_runner_manager.obj) : error LNK2001: 无法解析的外部符号 "protected: void __thiscall std::__1::__vector_base_common<1>::__throw_length_error(void)const " (?__throw_length_error@?$__vector_base_common@$00@__1@std@@IBEXXZ)
1>cef_sandbox.lib(thread_group_impl.obj) : error LNK2001: 无法解析的外部符号 "protected: void __thiscall std::__1::__vector_base_common<1>::__throw_length_error(void)const " (?__throw_length_error@?$__vector_base_common@$00@__1@std@@IBEXXZ)
1>cef_sandbox.lib(pooled_parallel_task_runner.obj) : error LNK2001: 无法解析的外部符号 "protected: void __thiscall std::__1::__vector_base_common<1>::__throw_length_error(void)const " (?__throw_length_error@?$__vector_base_common@$00@__1@std@@IBEXXZ)
1>cef_sandbox.lib(sequence_local_storage_map.obj) : error LNK2001: 无法解析的外部符号 "protected: void __thiscall std::__1::__vector_base_common<1>::__throw_length_error(void)const " (?__throw_length_error@?$__vector_base_common@$00@__1@std@@IBEXXZ)

Release_GN_x86 和 Release_GN_x86_sandbox 因为 libcef 和 sandbox 编译所需的参数不同,所以分开了两个目录,始末请参考这个 Issue:cef_sandbox.lib Visual Studio link errors in 3770 branch on Windows 但如果将 Release_GN_x86 和 Release_GN_x86_sandbox 全部编译后再打包,那发布出来的 cef_sandbox.lib 与官方下载地址中的大小基本差不多,都是 80MB+。 通过打包脚本打印的日志看合并 cef_sandbox.lib 的源也是使用的 chromium_git\chromium\src\out\Release_GN_x86_sandbox 目录下的,而二进制文件则是从 chromium_git\chromium\src\out\Release_GN_x86 目录下复制的,如下所示: 编译 cef_sandbox.lib 命令如下(注意后面的参数,不是 cef 了,而是 cef_sandbox,目录也是用的是带有 sandbox 的目录):

1
ninja -C out\Release_GN_x86_sandbox cef_sandbox

打包工程

编译完 Release 版本后开始打包操作,首先切换当前目录到 chromium_git\chromium\src\cef\tools ,然后使用如下命令进行打包:

1
2
:: --minimal 表示仅发布 Release 版本,不包含 Debug
.\make_distrib.bat --ninja-build --minimal

几乎所有脚本文件都有命令行参数介绍的,如果你有什么需求想看看脚本是否支持,执行脚本的时候后面加上 –help 就可以看到了。打包结束后返回的结果: E:\code\chromium_git\chromium\src\cef\binary_distrib 目录下你就可以看到打包过的文件了。