资讯动态

CEF+VS2019下H264/H265视频黑屏排查与编译配置

发布时间:2026/10/24 2:48:24 来源:尧图企业网站定制
简介面向 Windows 64 位桌面客户端开发者的 CEF 预编译与源码混合包基于 Visual Studio 2019 环境构建主要解决在自有应用中嵌入 Chromium 内核并直接播放 H264/H265 视频的问题适用于在线教育、直播、视频会议等对播放性能要求较高的场景需要一定的 C 与 CMake 基础。压缩包共 1055 个文件、约 278.47MB其中 539 个 .h 头文件与 359 个 .cc 源文件构成核心接口与实现58 个 .pak 为 Chromium 运行时资源14 个 dll 及 lib 提供可直接链接的二进制库另含 gypi 路径配置、cmake 模块、CMakeLists.txt 构建脚本与说明文档。已有 486 人学习下载。读者可借助 include 目录与 CMake 脚本完成二次编译和定制裁剪用 tests 中的 urlrequest、v8、scheme_handler 等单元测试验证内核功能并按说明文档快速完成环境搭建与合规使用。1. 黑屏那一刻问题不在你的代码一个模拟项目 X 需要在桌面客户端里嵌一个浏览器去看实时视频流选型定了 CEF机器是 Windows 10 x64编译器 VS2019。包下下来、工程跑起来、页面打开了一切看起来都对唯独video标签区域是纯黑的控制台只留一句The element has no supported sources。切到 H265 的流连错误都不给直接静默。这套 CEF vs2019 H264/265 的组合卡人的地方从来不是工程能不能编过而是编过之后视频放不出来——而它偏偏是国内桌面客户端里最常见的一种内嵌方案监控、会议、教育、医疗影像都在用。装之前先把版本、工具集和编解码开关对齐后面能省掉一整天的排查。2. 环境先摊开CEF 分支、VS2019 与工具链对齐CEF 这个东西的坑有一半出在“装”之前版本选错、工具集错位、磁盘没留够等到编译到 80% 才报错返工成本极高。所以先要把环境固定下来再考虑 H264/H265 的事。2.1 版本号拆开看三段式 CEF 版本怎么读CEF 的下载包文件名长这样cef_binary_109.1.18g8c1b0e5chromium-109.0.5414.120_windows64拆成四段看109.1.18是 CEF 自己的版本g8c1b0e5是对应的代码提交号chromium-109.0.5414.120是它内嵌的 Chromium 内核版本最后的windows64是平台。真正决定你视频能不能放的是第三段——Chromium 的主版本号因为 H264 软解走的是 Chromium 内置的 ffmpegH265 走的是平台硬解通路两者都跟内核版本强相关。选分支的原则不复杂跟着项目里已有的浏览器基线走。宿主页面如果用了某个较新的 CSS 特性或者 Web API内核版本不能太低反过来如果业务页面是几年前写的用一个太新的分支反而可能碰到行为变更。我的习惯是先确定 Chromium 主版本再去 CEF 下载页找对应的分支号分支号就是 Chromium 的主版本号比如 109 分支对应内核 109。这里有一个容易忽略的版本坑较新的 Chromium 分支在 Windows 上已经把官方构建工具链切到了更高版本的 MSVC二进制分发包也可能是用新工具集编出来的。你用 VS2019 去链接如果运行库和异常模型对不上表现是编译通过、链接通过、运行到CefInitialize直接崩日志里什么都留不下。所以“支持 VS2019”这句话要和具体分支绑在一起看不能笼统地说 CEF 支持 VS2019。关注点建议取值说明平台windows6432 位包不建议用于视频场景内存很快吃满构建类型ReleaseDebug 版 libcef 体积巨大且性能差只用于排查工具集v142VS2019与二进制分发包保持一致别混 v143磁盘源码编译预留 120GB 以上检出加中间产物SSD 更稳路径全英文、无空格、层级浅D:\cef这类最省事提示路径里有中文或者空格ninja 在某些环节会报找不到文件的错而这种错往往指向的是一个看起来完全无关的 .obj 文件很难往路径上想。2.2 VS2019 侧要打勾的工作负载与 Windows SDKVS2019 安装器里只需要三样东西但每一样都不能少“使用 C 的桌面开发”工作负载这是基础。单个组件里的“MSVC v142 - VS 2019 C x64/x86 生成工具”确认版本号不低于 14.29。“Windows 10 SDK”选 10.0.19041 或更新CEF 的头文件里会用到较新的 SDK 定义。命令行装的话可以这样vs_buildtools.exe换成你本地的安装器路径# 静默安装 C 桌面开发工作负载与指定 SDK vs_buildtools.exe --quiet --wait --norestart --add Microsoft.VisualStudio.Workload.VCTools --add Microsoft.VisualStudio.Component.VC.Tools.x86.x64 --add Microsoft.VisualStudio.Component.Windows10SDK.19041 --includeRecommended--quiet --wait让安装器不弹窗并且等它装完再返回适合脚本化--add后面是组件 ID--includeRecommended会把该工作负载的推荐组件一起带上省得回头补。装完在“Developer Command Prompt for VS 2019”里敲cl能出版本号就说明工具链通了。运行库设置这一项要单独说。CEF 的二进制分发包是带着自己的运行库配置编出来的宿主工程如果和它不一致跨模块传std::string或者new/delete跨 DLL 的时候会出现很难查的崩溃。稳妥的做法是先把 CEF 自带的cefclient示例编出来跑通然后把它的“C/C → 代码生成 → 运行库”设置原样抄到自己的工程里两边保持一致别自己发挥。2.3 依赖件清单编译前必须到位的四样东西走二进制分发的话只装 VS2019 就够了一旦决定自己编源码分发拿 H265就得把下面这套凑齐。工具版本要求用途自检命令Python3.8 及以上3.11 也可跑automate-git.py和 gclientpython --versionGit2.30 以上拉取 Chromium 与 CEF 源码git --versionCMake3.16 以上生成 VS 解决方案cmake --versiondepot_tools最新提供 gclient、gn、ninjawhere gclientdepot_tools 要放进PATH而且必须排在系统 Python 前面否则 gclient 会去用一个没装依赖的系统 Python 然后报一堆模块找不到。我一般会把它放在路径最前面并在同一次终端会话里完成所有构建动作避免开了新窗口环境变量丢了。# 把 depot_tools 放到 PATH 最前面并固定 Python 版本优先级 $env:PATH D:\depot_tools; $env:PATH $env:DEPOT_TOOLS_WIN_TOOLCHAIN 0 # 使用本机已装的 VS而不是下载工具链 gclient --versionDEPOT_TOOLS_WIN_TOOLCHAIN0这一行是关键它告诉构建系统用本机安装的 Visual Studio不去额外下载一整套工具链。不设这个变量第一次同步会莫名其妙多花很长时间而且下载下来的工具链版本未必和你的 VS2019 对得上。3. 拿到带 H264/H265 的包验证、拉源码、改 gn环境齐了接下来是这篇的核心怎么确定手里这份 CEF 到底支不支持 H264不支持又怎么自己编一个支持的。3.1 十分钟验证手上的二进制到底带不带 H264不要靠猜也不要靠看文档直接在 CEF 里跑一段探测脚本。新建一个probe.html用cefclient打开它video idv controls muted/video script const v document.getElementById(v); // canPlayType 返回 probably / maybe / 空串即不支持 const probes { h264: video/mp4; codecsavc1.42E01E, h265_hvc1: video/mp4; codecshvc1.1.6.L93.B0, h265_hev1: video/mp4; codecshev1.1.6.L93.B0 }; for (const [name, type] of Object.entries(probes)) { console.log(name, JSON.stringify(v.canPlayType(type))); } /scriptcanPlayType是浏览器暴露的能力查询接口第一个参数是 MIME 加 codecs 参数。avc1.42E01E是 H264 Baseline 的一个典型 profile-level-idhvc1和hev1是 H265 的两种封装标识两者都要测因为有的构建只认其中一种。返回值probably表示有较大概率能解maybe表示封装认得但编码不确定空串就是明确不支持。跑起来的命令很简单cefclient.exe --urlfile:///D:/test/probe.html --no-sandbox--no-sandbox只是排查阶段图省事正式发布别带。打开后按 F12 看 Console或者直接看cefclient的输出窗口。这一步能省掉大量无效劳动。如果 H264 返回probably、H265 返回空串说明这份二进制带了专有编解码但没开 HEVC你只需要在宿主程序里补一个命令行开关如果 H264 也是空串那这份包就是编解码被裁掉的版本只有换包或者自己编两条路。3.2 拉源码分发automate-git.py 的两次执行自己编是绕不开的尤其是要 H265。CEF 提供了一个包装脚本automate-git.py它内部会调 gclient 同步 Chromium 加 CEF 两套代码再调 gn 和 ninja。第一次执行负责“把代码拉下来”# 在 CEF 源码目录里执行D:\cef 是下载根目录 python automate-git.py --download-dird:\cef ^ --branch5414 ^ --x64-build ^ --no-debug-build ^ --no-distrib ^ --force-clean参数逐个说--branch5414是 Chromium 的分支号写 5414 对应的就是内核 109这个号和你在下载页看到的后缀是一致的--x64-build只构建 64 位省一半时间--no-debug-build跳过 Debug 版那个版本编译慢、体积大、平时用不上--no-distrib表示这次只同步和生成构建目录先不打包分发包因为我们要在下一步改参数--force-clean在目录已存在时先清掉第一次跑可以加后面增量构建时要去掉。这个命令第一次跑要几个小时取决于网络和磁盘。它中途会反复输出同步进度只要没有报错退出就让它跑。跑完之后你会得到一个d:\cef\chromium\src\out\Release_GN_x64\目录里面有个args.gn这就是下一步要动的地方。注意如果中途断了别删目录重来直接去掉--force-clean再执行一次gclient 会续传。真正需要重来的情况是args.gn被写坏或者 out 目录里的 ninja 文件损坏那时候删掉out\Release_GN_x64重生成比删整个源码树快得多。3.3 gn 参数三个开关决定视频能不能放打开args.gn把下面这些写进去。gn 是 Chromium 的构建配置语言这个文件就是构建参数的唯一入口# 构建类型与目标平台 is_debug false is_official_build true target_cpu x64 # 关键一启用专有编解码H264/AAC proprietary_codecs true # 关键二让 ffmpeg 走 Chrome 的完整品牌配置否则 H264 解析器会被裁掉 ffmpeg_branding Chrome # 关键三打开平台 HEVC 解码通路H265 enable_platform_hevc true enable_hevc_parser_and_hw_decoder true前两个参数是一组必须同时出现。proprietary_codecs打开的是开关但真正决定 ffmpeg 里保留哪些解析器的是ffmpeg_branding——设成Chromium时 ffmpeg 会裁掉 H264 相关实现设成Chrome才完整保留。很多人只加了第一个参数编完发现 H264 还是不支持就是卡在这里。后两个参数跟 H265 有关不同分支上参数名可能略有差异。稳妥的做法是先确认参数在当前分支存在再写进去# 列出当前分支所有与 hevc、codec 相关的可用参数 gn args out\Release_GN_x64 --list | findstr /i hevc codec ffmpeggn args --list会打印每个参数的默认值、类型和说明findstr过滤出关键词命中的行。如果某个参数在列表里根本不存在说明这个分支还没引入它写进去会直接报Unknown argument这时候要么换分支要么就只能接受 H265 走软解或者干脆不支持。参数改完第二次执行脚本这次让它真正开始编python automate-git.py --download-dird:\cef ^ --branch5414 ^ --x64-build ^ --no-debug-build ^ --no-chromium-update ^ --no-depot-tools-update ^ --build-targetcefclient--no-chromium-update和--no-depot-tools-update是增量构建的关键跳过源码同步直接用已经拉好的代码和刚改过的args.gn编译。--build-targetcefclient只编示例程序验证阶段够用确认能放视频之后再把目标换成完整分发。3.4 编译产物与目录结构编完之后产物集中在d:\cef\chromium\src\out\Release_GN_x64\下真正要拿走的是这几类文件作用漏了会怎样libcef.dll全部浏览器逻辑程序起不来libcef.lib链接用导入库链接期报 LNK2019cef_sandbox.lib沙箱支持开沙箱时链接失败chrome_elf.dll崩溃捕获与 hook启动即崩无日志icudtl.dat国际化数据中文页面乱码cef.pak/cef_100_percent.pak内置资源界面样式丢失locales\*.pak语言包界面英文或空白snapshot_blob.bin/v8_context_snapshot.binV8 快照启动变慢或直接失败vk_swiftshader.dll/vulkan-1.dll软件渲染回退无 GPU 机器白屏判断编解码有没有编进去不用反编译直接把这个新编出来的libcef.dll替换到cefclient目录里再跑一次 3.1 的探测页。H264 和 H265 都返回probably说明编解码链路通了可以进入工程接入阶段。4. 接进 VS2019 工程CMake、落位与 CefSettings包对了接下来是把它塞进你自己的工程。这一步的失败率比编译还高因为大部分问题不是链接错误而是运行时找不到文件。4.1 用 CMake 生成 v142 的 x64 解决方案CEF 官方推荐用 CMake 管理工程二进制分发包里自带cmake/目录和FindCEF.cmake直接用就行。顶层CMakeLists.txt可以这样写cmake_minimum_required(VERSION 3.16) project(MyCefHost CXX) # 指向解压后的 CEF 分发目录路径别带空格 set(CEF_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/cef_binary_windows64) list(APPEND CMAKE_MODULE_PATH ${CEF_ROOT}/cmake) find_package(CEF REQUIRED) # cef_dll_wrapper 是 CEF 的 C 封装层必须一起编 add_subdirectory(${CEF_ROOT}/libcef_dll libcef_dll_wrapper) add_executable(MyCefHost WIN32 main.cpp) target_link_libraries(MyCefHost PRIVATE libcef_lib libcef_dll_wrapper)find_package(CEF REQUIRED)会去读CEF_ROOT下的目录结构自动把include、Release这些路径配好add_subdirectory那行是把libcef_dll_wrapper作为子工程编进来这个库是 CEF 的 C 接口层不编它你会缺一堆CefRefPtr相关的符号WIN32关键字让生成的是窗口子系统程序不带控制台黑框。生成解决方案cmake -G Visual Studio 16 2019 -A x64 -S . -B build-G指定生成器Visual Studio 16 2019对应的就是 v142 工具集-A x64指定目标架构这两个参数必须和 CEF 分发包的架构一致混了会在链接期报一长串符号不匹配。生成完打开build\MyCefHost.sln就能编。4.2 运行时文件落位少一个 dll 就白屏这是最容易翻车的一步。CEF 的运行时文件必须和 exe 在同一层或者按约定放子目录少一个的表现往往不是报错而是白屏或者静默退出。我的做法是写一个拷贝脚本每次构建后自动执行# 把 CEF 运行时文件拷到输出目录构建后自动跑 $src D:\cef\chromium\src\out\Release_GN_x64 $dst .\build\Release Copy-Item $src\libcef.dll,$src\chrome_elf.dll,$src\icudtl.dat $dst -Force Copy-Item $src\*.pak,$src\*.bin,$src\*.dll $dst -Force # locales 目录要整个拷只拷 en-US.pak 会导致部分页面空白 Copy-Item $src\locales $dst -Recurse -Force-Force覆盖同名文件避免增量构建时用了旧 dlllocales目录整个拷是因为 CEF 初始化时会按系统语言去找对应的 pak找不到就回退到 en-US如果连 en-US 都没有界面会大面积空白而且不会有任何错误提示。还有一个常被忽略的点vk_swiftshader.dll和vulkan-1.dll。有独显的机器用不上但客户现场大量是核显或者远程桌面环境缺了这两个页面渲染会退化成白屏而且日志里只留一句 GPU 进程启动失败。4.3 CefSettings 与命令行开关把 HEVC 打开前面的 gn 参数决定“能力有没有编进去”CefSettings和命令行开关决定“这次运行要不要用”。初始化部分大致这样#include include/cef_app.h #include include/cef_command_line.h class MyApp : public CefApp { public: void OnBeforeCommandLineProcessing( const CefString process_type, CefRefPtrCefCommandLine command_line) override { // 只在浏览器进程追加渲染进程重复追加会告警 if (process_type.empty()) { command_line-AppendSwitchWithValue( enable-features, PlatformHEVCDecoderSupport); // 允许无用户手势自动播放监控类场景必开 command_line-AppendSwitchWithValue( autoplay-policy, no-user-gesture-required); } } IMPLEMENT_REFCOUNTING(MyApp); }; int main(int argc, char* argv[]) { CefMainArgs main_args(GetModuleHandle(nullptr)); CefRefPtrMyApp app(new MyApp()); CefSettings settings; settings.multi_threaded_message_loop true; // Windows 下用独立消息循环 settings.log_severity LOGSEVERITY_INFO; // 排查期开 INFO CefString(settings.log_file).FromASCII(cef_debug.log); CefString(settings.locales_dir_path).FromASCII(locales); CefString(settings.resources_dir_path).FromASCII(.); if (!CefInitialize(main_args, settings, app.get(), nullptr)) { return 1; // 初始化失败基本都在 cef_debug.log 里 } // ... 创建窗口、加载页面 CefShutdown(); return 0; }OnBeforeCommandLineProcessing里判断process_type.empty()很重要这个回调在浏览器进程和各个子进程里都会走一遍不加判断会在渲染进程里重复追加同一个开关日志里会刷一片警告。enable-features的值如果不止一个用逗号拼在一起比如PlatformHEVCDecoderSupport,UseSkiaRenderer写成两次AppendSwitchWithValue后一次会覆盖前一次。multi_threaded_message_loop设成 true 之后CEF 会自己起线程跑消息循环你的主线程可以继续做原来的事代价是所有 CEF 接口调用要自己保证线程安全。如果是纯 CEF 应用设成 false 自己跑CefRunMessageLoop也行。log_severity平时设LOGSEVERITY_WARNING排查期临时调到INFO日志文件会大得很快别一直开着。5. 排查装完之后视频还是放不出来的五种现场前面每一步都按文档走了视频还是不放这时候就别再从零重装了按现象对号入座效率最高。5.1 现象一canPlayType 返回空串H264 也不支持现象探测页里 h264 和 h265 都返回空字符串video报no supported sources。原因这份libcef.dll在编译时没有打开proprietary_codecs或者ffmpeg_branding还是默认的Chromium。二进制分发包如果是精简编解码的版本也会是同样的表现。解决换成 3.3 的args.gn自己编一份编完先验证libcef.dll再往工程里放。不要试图在运行时用命令行开关补救——ffmpeg 里没有那个解码器开关加得再多也变不出来。5.2 现象二H264 能放H265 提示不支持现象h264 返回probablyh265 两个 codecs 都是空串或者maybe。原因gn 里少了enable_platform_hevc相关参数或者宿主程序没追加PlatformHEVCDecoderSupport这个 feature 开关。还有一种是机器本身没装 HEVC 解码组件Windows 上硬解 H265 依赖系统侧的扩展。解决先确认gn args --list里参数存在并且已设成 true再确认OnBeforeCommandLineProcessing里确实追加了 feature 开关。两步都对还是不行在目标机器上装一下系统的 HEVC 解码扩展再试。软件解码这条路在 CEF 里默认不走因为专利和体积的原因不要指望它。5.3 现象三程序启动直接退出没有窗口也没有日志现象双击 exe进程在任务管理器里闪一下就没了cef_debug.log都没生成。原因八成是运行时文件缺失最常见的是chrome_elf.dll和icudtl.dat没拷。这两个文件缺失时 CEF 在初始化早期就退出日志系统还没来得及建立。解决按 4.2 的清单逐个核对输出目录特别是chrome_elf.dll、icudtl.dat、snapshot_blob.bin。用dumpbin /dependents MyCefHost.exe看依赖缺的直接补。另外一个隐蔽原因是 exe 所在路径有中文CEF 加载 pak 文件时路径编码出错同样表现为静默退出。5.4 现象四Debug 能跑Release 崩在 CefInitialize现象同一个工程Debug 配置正常Release 一运行就崩调用栈指向CefInitialize里面。原因运行库设置不一致。CEF 分发包和宿主工程的运行库配置不匹配时跨模块的内存分配和释放会落到不同的堆上Debug 因为堆检查宽松侥幸不崩Release 就原形毕露。解决打开cefclient示例工程的属性页把“C/C → 代码生成 → 运行库”抄过来两边保持一致。同时确认“C/C → 语言 → 异常处理”和_ITERATOR_DEBUG_LEVEL也一致这几个宏在 Debug/Release 之间不一致时STL 容器跨模块传递会直接崩。5.5 现象五视频能放但是花屏、绿屏或者只有声音现象canPlayType都返回probably视频也确实在播画面却是绿的或者卡成马赛克。原因GPU 渲染路径和显卡驱动的问题。CEF 默认会用 GPU 合成某些老显卡或者远程桌面会话下硬解和合成器打架。解决先在命令行加--disable-gpu跑一次。如果关掉 GPU 就正常说明是渲染路径问题可以退一步用--disable-gpu-compositing只关合成保留解码能力或者把vk_swiftshader.dll补齐让 CEF 在 GPU 不可用时能回退到软件渲染。远程桌面场景我一般直接给用户加启动参数关掉 GPU 合成稳定比性能重要。6. 验证跑通三条断言和一张清单装完的最后一件事不是“看起来能跑”而是留下可以重复执行的断言。我习惯在宿主程序里内置一个隐藏的诊断页用三条断言判定这套环境是否合格// 三条断言全部为 true 才算这套 CEF 装对了 const ok264 document.createElement(video).canPlayType(video/mp4; codecsavc1.42E01E) probably; const ok265 [hvc1.1.6.L93.B0, hev1.1.6.L93.B0] .some(c document.createElement(video).canPlayType(video/mp4; codecs${c}) ! ); const okPlay await new Promise(resolve { const v document.createElement(video); v.src window.__DIAG_STREAM__; // 由宿主注入的一段本地 mp4 v.oncanplay () resolve(true); v.onerror () resolve(false); v.load(); }); console.log([diag], { ok264, ok265, okPlay });第一条验证 H264 解析器在第二条验证 H265 的两种封装标识至少认一种第三条是端到端的真实解码播放前两条过了但第三条失败问题一定在渲染路径或者流本身不在编解码。把这三条做成启动时可选执行的诊断以后换机器、换驱动、升级 CEF 版本都能第一时间定位。配套的检查清单我贴在工程 README 里每次出包前过一遍检查项判定方式不合格的表现运行时文件完整对比 4.2 的清单启动闪退、白屏编解码开关诊断页前两条断言视频区域纯黑端到端播放第三条断言有声音无画面日志可读cef_debug.log有内容崩溃查不到原因沙箱可用带沙箱启动一次正式环境权限问题日志这一项单独强调一次正式发布把log_severity调回WARNING但一定要保留log_file的配置把日志写到用户目录下而不是程序目录。程序装在Program Files里的时候往当前目录写日志会因为没有写权限而静默失败等到客户现场崩了你手里一个字节的证据都没有。从那以后我每次接新的 CEF 项目第一件事都是把这三条断言和这张清单跑一遍再谈业务功能。这套 CEF vs2019 H264/265 的组合难的不是哪一步操作而是把“编解码能力”和“运行时文件”两件事分开验证清楚——混在一起排查很容易在一个已经正确的方向上反复重装。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑