ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

自编译CEF x86版开启H264支持:集成实践与避坑指南

自编译CEF x86版开启H264支持:集成实践与避坑指南 简介面向需要Windows 32位环境下内嵌浏览器的开发者这份编译好的CEF Release包提供了完整的Chromium 81.0.4044.113对应版本并额外支持H264硬件编解码可直接用于音视频播放场景。包内共1514个文件包含483个头文件、312个CC源文件、327个Obj中间文件、14个Dll动态库、8个Lib导入库以及Pak资源等既有可直接调用的运行库与导入库也保留了构建过程生成的源文件、日志和中间产物便于二次开发或排错时回溯。压缩包大小223.51MB已有964人下载学习。值得留意的是CEF官方编译时识别RAR格式此处提供ZIP仅为方便提取使用者按需转换即可。对从事桌面客户端、在线视频或混合应用开发的工程师而言这份带H264支持且免去漫长编译流程的二进制包能显著提升集成效率。 搞桌面端开发尤其做 Windows 客户端的老哥们对 CEFChromium Embedded Framework应该不会陌生。项目里只要嵌一个网页或者要做协议拦截、JS 互调、离屏渲染基本绕不开它。但 CEF 有个老问题官方自动构建包默认不带 H264 解码。你网页里放一个 MP4在 CEF 里要么黑屏要么一直转圈放在生产环境里就是事故。所以我这次直接给团队编了一个支持 H264 的 x86 版本基于 Chromium 81.0.4044.113顺手把 Release 包整理出来。这个过程说大不大说小不小真要自己从零跑一遍编译流程没有一周时间下不来。这篇就围绕这个包展开为什么偏偏选 81 这个版本x86 在今天还有没有意义H264 支持到底是怎么打开的以及拿到 Release 包之后怎么正确集成、怎么处理进程管理、怎么排查黑屏闪退这些高频问题。适合正在做桌面混合开发、工控机 WebUI或者老项目还被迫停留在 32 位依赖库的兄弟们参考。1. 为什么需要单独编译CEF、x86、H264 三者是绑在一起的1.1 官方 CEF 为什么默认没有 H264先把底层原因说透。Chromium 本身是开源的但 H264、AAC 这些编解码器涉及 MPEG-LA 和 Via Licensing 的专利授权。Google 在正式版 Chrome 里是买了授权、带着这些解码器一起发布的但 CEF 的开源构建版为了规避专利风险默认不开启相关编译开关。所以你在官方构建站点拉下来的二进制直接播放 MP4 基本是废的。这里要区分两个概念CEF 的构建分“官方 Build”和“第三方补丁 Build”。官方 Build 虽然也叫 CEF但 proprietary_codecs 这个开关默认关闭。我这个包是在源码层面把 ffmpeg 的 proprietary codecs 打开后重新编出来的。开启的关键是 GN 参数里同时设置proprietary_codecstrue和ffmpeg_brandingChrome这两个必须成对出现只开其中一个编出来的包还是不带 H264 解码能力。1.2 都什么年代了为什么还选 x86很多朋友第一反应是现在谁还编译 x86这不是给自己找麻烦吗但真实场景里 x86 仍然有需求而且不少。我这次编译 x86 版本的原因很直接目标设备是分布在各个现场的工控机和一体机很多设备内存只有 4G甚至有的还在用 Win7另外项目里关联的某个扫码 DLL 和加密狗驱动只提供 32 位版本主程序一旦定位成 x86CEF 也必须是 x86混着用会导致子进程加载失败或者直接崩溃。平台一致性在 CEF 集成里没有任何商量余地。你主程序是 32 位却动态加载了一个 64 位的 libcef.dll启动时可能不报错但一创建浏览器实例就崩。反过来64 位主程序加载 32 位 CEF 也不会好到哪去。所以不是 x86 有多好而是业务链条里有 32 位依赖整个链路就得统一走 32 位。1.3 Chromium 81.0.4044.113 选型理由选版本这件事我觉得比选架构更需要讲究。CEF 一直跟着 Chromium 版本走新版本功能多但体积、系统要求、API 变动也跟着涨。我最终定在 81.0.4044.113主要看中三点CEF API 稳定和后续版本差异不算大但系统要求比新版宽容很多对 Win7 和低配工控机友好。包体相对可控libcef.dll 一百多 MB加上资源文件整体分发起来压力不大对比 Chromium 100 系列动辄 150MB 起步离线更新更方便。81 这个版本对 WebRTC、CSS 特性的支持已经够用项目里做 WebUI 或者数据可视化大屏完全没问题。当然如果你要跑最新前端框架或者需要新版 DevTools 协议里的某些功能可以再往上选版本。但对大多数“用 CEF 包一个业务页面”的场景81 是不错的平衡点。2. 拿到 Release 包之后先搞清楚目录和文件2.1 关键文件分别是什么职责解压之后你会看到一堆文件和目录很多人直接把整个目录拖进项目就开始跑然后遇到各种莫名其妙的问题。其实这些文件各有分工了解它们才能做好后续的裁剪和分发。核心文件就这几个文件/目录职责能不能删libcef.dllCEF 核心库所有浏览器内核逻辑都在里面必留cef.pak / resources.pakUI 资源和浏览器资源必留chrome_100_percent.pak / chrome_200_percent.pak不同 DPI 下的界面资源必留icudtl.dat国际化数据ICU 运行需要必留v8_context_snapshot.bin / snapshot_blob.binV8 引擎启动快照必留删了启动即崩locales各语言本地化资源按需精简swiftshader软件渲染后端保留*.exe / *.dll子进程和辅助文件按需保留我之前裁过一次 locale只留zh-CN和en-US包体直接少了十几 MB。如果你面对的是纯内网环境的离线分发这步能省不少带宽。但要注意icudtl.dat和 V8 快照这两个文件删掉一个 CEf 就会启动失败而且错误提示不明显经常是白屏或者直接闪退。2.2 子进程文件为什么要和主程序放一起CEF 是多进程架构浏览器进程、渲染进程、GPU 进程、网络进程都是独立的但子进程默认复用主程序的可执行文件通过启动参数里的--type区分角色。所以你会发现跑起来之后进程列表里出现多个同名进程这就是“CEF 进程怎么这么多”的根源。这里有个重要的细节如果你设置了browser_subprocess_path子进程可以指定到别的 exe但架构必须和主程序一致。如果你拿到 x86 包却在一个 64 位进程里调用子进程会启动失败渲染区域一片空白。我建议默认不设置这个路径让子进程跟着主程序走最省事。2.3 我做了哪些裁剪和瘦身除了 locale我打包时还做了三件事删除所有.pdb调试符号和.map文件。这些文件只在崩溃时抓 dump 有用发布环境不需要体积能省掉几十 MB。保留swiftshader。很多工控机没有独立显卡GPU 加速不可用CEF 会退回软件渲染如果删了swiftshader页面上所有 WebGL 和 CSS 动画都会出问题。保留snapshot_blob.bin。之前在某台机器上为了“精简”误删过一次结果所有页面打开都白屏排查了半天才发现是它。3. 集成到项目里的完整实操初始化、视频播放、进程退出3.1 标准初始化和路径设置CEF 集成第一步是初始化C 项目里一般这样写#include include/cef_app.h #include include/cef_client.h // 在 WinMain 入口处 CefMainArgs args(hInstance); CefSettings settings; settings.multi_threaded_message_loop true; settings.no_sandbox true; // 内网工具型项目通常关闭沙箱省去一堆权限问题 // 设置资源路径 CefString(settings.resources_dir_path) L./cef; CefString(settings.locales_dir_path) L./cef/locales; // 初始化 void* sandbox_info nullptr; CefInitialize(args, settings, app.get(), sandbox_info);重点在于resources_dir_path和locales_dir_path必须指向 Release 包解压后实际的位置。很多人习惯把 libcef.dll 拖到 exe 目录却忘了把cef.pak和locales一起带上结果初始化时报Failed to load libcef.dll或者找不到资源文件。3.2 验证 H264 是否生效的几个土办法编译完一个带 H264 的包最紧张的就是验证到底能不能播视频。不用写太多代码直接在 CEF 里加载一个测试页面就行。第一个办法写一个简单的 HTML放一个video标签src 指向一个 H264 编码的 MP4 文件能正常出画面、有声音就说明解码链路通了。第二个办法在 CEF 页面里执行window.chrome.loadTimes()看内核信息或者打开chrome://gpu查看媒体解码能力如果你看到VideoDecode: H.264一栏是 enabled说明 H264 解码已经可用。这里我踩过一个小坑光验证视频能播还不够还要验证--enable-featuresPlatformHEVCDecoderSupport这类参数有没有影响。如果你的业务需要播 H265那是另一套东西CEF 81 默认不带 H265别指望同一个包能通吃。3.3 CEF 进程退不掉的根因与正确关闭姿势热词里有个高频问题cef 进程如何关掉。说实话我调这个问题调了挺久。CEF 主程序退出后后台经常残留好几个子进程尤其是 renderer 子进程一直占着 CPU 和内存。根因其实是关闭顺序不对。CEF 的正确退出流程是这样关闭所有 Browser 窗口触发每个窗口的DoClose和BeforeClose事件。退出消息循环比如调用CefQuitMessageLoop()。最后调用CefShutdown()回收全局资源。很多人直接在收到退出消息时调CefShutdown()但此时浏览器窗口还没完全释放子进程自然不会被清理。还有一个细节在BeforeClose里不要立刻释放 CefBrowser 实例要等它真正关闭完毕再清理否则会出现野指针。如果你遇到实在退不干净的极端情况可以用任务管理器强制结束和主进程同名的所有进程命令是taskkill /F /IM YourApp.exe /T/T参数会连带结束所有子进程。但这是兜底手段正常项目的目标应该是让主进程退出时自动带走所有子进程。3.4 x86 包的分发注意事项分发包的时候有几个细节必须处理否则目标机器上跑不起来。第一x86 版的 CEF 依赖 VS2015/2017/2019 的 VC 运行库目标机器没装 VC Redist x86启动时报0xc000007b或者提示找不到vcruntime140.dll。我的建议是把vcruntime140.dll、msvcp140.dll一起放进程序目录或者在安装包里带上 VC Redist x86 静默安装参数。第二路径问题。CEF 对带空格的路径支持还可以但某些第三方插件和 subprocess 会有问题。内网项目尽量别把程序放在中文路径下避免各种诡异错误。第三杀毒软件误报。CEF 的 exe 会动态生成临时文件并启动子进程有些杀软会拦截。签名不是必须的但做内网工具的话建议至少加一个自家公司的代码签名证书能省很多运维麻烦。4. 常见问题排查与避坑实录4.1 黑屏、白屏、渲染不出来CEF 集成的第一大坑就是白屏。我遇到的情况九成是icudtl.dat缺失、cef.pak路径配错、或者子进程启动失败。另一个高频原因是 GPU 进程崩溃特别是无显卡的虚拟机或远程桌面环境。如果是 GPU 进程的问题可以在初始化前通过命令行参数强制走软件渲染CefString(settings.command_line_args_disabled) ; CefRefPtrCefCommandLine command_line CefCommandLine::CreateCommandLine(); command_line-AppendSwitch(disable-gpu); command_line-AppendSwitch(disable-gpu-compositing);这里要注意不要同时加disable-software-rasterizer否则连软件渲染都被禁了页面会一直白屏。正确做法是让 CEF 退回swiftshader软件渲染虽然性能掉一些但能保证页面出来。4.2 0xc000007b 错误x86 最容易踩的坑0xc000007b这个错误码遇到过的兄弟应该都有印象。它本质上是 DLL 加载失败通常是 32 位和 64 位混用导致的。比如你主程序是 x86却把 64 位的某个依赖库放进了程序目录。排查思路三步走先确认libcef.dll是 32 位还是 64 位用 dumpbin 或者 Dependencies 工具看一眼再确认 VC 运行库装的是 x86 版本最后排查其他第三方 DLL 是否有 x64 混入。这个过程看起来简单但真出问题时很容易让人绕圈子。4.3 播放 H264 视频异常的音画问题如果你的包已经带 H264 支持视频却还是有声音没画面或者有画面没声音多半是解码链路里某个环节被关了。我之前遇到过网页里video标签播放视频音频正常画面一直黑着查了很久发现是 GPU 进程没起来视频帧没有被合成器输出。另一个典型问题是视频画面卡住但播放进度在走。这种一般是硬件解码和软件解码切换出了问题在CefSettings里关闭硬件加速强制走软件解码通常能解决。反过来如果你的机器 GPU 比较强建议保留硬件加速CPU 占用会低很多。4.4 多开、内存占用和强制清理有些项目需要同时开多个 CEF 实例每个实例都带一套子进程内存占用会成倍上涨。CEF 81 的 x86 版单个实例空载开一个空白页内存大概 80 到 120 MB再加上实际页面很容易跑到 300 MB 以上。如果业务允许尽量复用同一个浏览器实例用多页面而不是多进程。强制清理方面除了前面提到的事件回调方式还可以在程序里维护一个进程列表在退出时用 Windows API 枚举子进程并依次结束。但要注意枚举进程时不要把所有同名进程都杀了如果用户同时开了多个你公司产品杀错进程会出大问题。最稳妥的做法是记录父进程 PID只结束自己派生的子进程。5. 后续扩展x64 迁移、Linux 交叉编译和离线分发建议5.1 x64 包和 x86 包要注意的差异如果项目以后要迁移 x64直接换一个 x64 的 Release 包代码层面基本不用大改但要注意三点第一所有原生插件、第三方库必须换成 64 位第二注册表访问位置会从WOW6432Node切到原生路径涉及配置读取的逻辑要校验第三VC 运行库从 x86 换成 x64 版本。从我自己的经验看x86 迁 x64 最大的工作量从来不在 CEF 本身而在业务代码里的指针、句柄、类型转换。如果项目从设计之初就用了size_t和LPARAM这些平台自适应类型迁移会顺畅很多。5.2 离线更新和数据同步的落地思路用了这个 Release 包的项目多半是内网部署离线更新是绕不开的话题。我的建议是把 CEF 相关文件单独放一个目录升级时先校验文件版本再整体替换避免新旧文件混在一起。内网环境带宽有限可以做增量包只替换变动过的文件比如locales没变就不用再传一遍。另外如果目标机器有 SSD加载速度会明显好于机械硬盘老工控机如果还是机械硬盘建议把 CEF 目录放在离系统盘较近的路径减少寻道开销。最后再分享一个实际操作中的体会每次拿到新版本 CEF先在开发机完整跑一遍“初始化-打开页面-播放视频-退出程序”的流程确认没有子进程残留再发出去。这个动作看着简单但能省掉运维那边一大堆反馈。CEF 集成这种事儿稳定的版本比追新更重要只要满足业务需求尽量少折腾。本文还有配套的精品资源点击获取
返回列表