简介:VLC 2.0.4 Windows 编译手册是一份面向希望在 Windows 平台下从源码编译 VLC 的开发者的实操指引。VLC 是 VideoLAN 开源项目旗下的跨平台流媒体服务器和多媒体播放器,支持 WMV、ASF、MPG、AVI、H.264 等多种格式;手册以资源包内单个 doc 文档(共 1 个文件,267KB)完整记录了编译环境搭建、组件安装和源码编译的关键步骤。内容依次覆盖 Msys、MSYS-DTK、MinGW、Git、wget 等工具链的版本选择与安装位置,Lua 库编译,VLC 源码下载,以及 configure 文件修改、contrib 配置等核心环节,并标注了各组件来源,便于对照操作。目前已有 322 人学习/下载;对于不熟悉开源项目交叉编译的开发者,可将这份文档作为从零搭建环境的参考,显著减少中间环节的踩坑时间。
1. 为什么 VLC 2.0.4 的 Windows 编译值得自己跑一遍
很多人卡在 VLC 2.0.4 的 Windows 编译上,是因为它并不只是"点一下编译"这么简单:VLC 的构建系统由 autotools 驱动,configure 脚本需要 POSIX shell、perl、m4 这一整个工具链,而 Windows 原生环境给不了这些。这份编译手册的价值,在于它把散落在 SourceForge 各处的依赖包、版本号、目录约束串成了可复现步骤,从安装 MSYS 到生成 vlc-x.x.x 目录,中间每一步都给了明确改法。我自己在 Windows 7 上完整走过一遍,改过三个源码文件、动过四处打包脚本,最终产出了一个可以拷走就用的免安装绿色版。它适合正在研究 VLC 源码、想裁剪模块,或者需要交付定制播放器的人;新手按顺序执行能跑通,熟手则能从这里看出 2.0.4 时代工具链的边界在哪里。
2. 编译环境搭建:MSYS、MinGW 与工具链版本匹配的几个硬约束
先花两分钟理解这套环境为什么长这样,后面踩坑会少很多。VLC 2.0.4 的 configure 脚本需要 POSIX shell 来做功能探测,需要 perl 来跑 autotools 辅助脚本,需要 m4 来展开宏,这些在 Windows 的 cmd 里都不存在。MSYS 1.0 提供一个轻量 POSIX 运行层,MinGW 提供 Windows 原生编译器,两者组合起来,configure 才能在 Windows 上生成 Makefile 并交给 gcc 编译。当年社区选这条路而不是 Cygwin,是因为 Cygwin 生成的二进制会依赖 cygwin1.dll,而 MSYS + MinGW 产出的程序是真正原生的 Win32 程序,可以直接分发。
2.1 MSYS、MSYS-DTK、TDM-GCC 安装顺序与目录强制约束
安装顺序是 MSYS 1.0.11 → MSYS-DTK 1.0.1 → TDM-GCC 4.7.1-2 → mingw-utils 0.3,顺序不能乱。MSYS 1.0.11 是基础,安装向导走到最后一步会弹出一个 DOS 命令行窗口,那是 MSYS 自带的 bash 环境,后续所有操作都在它里面做。DTK 1.0.1 是 MSYS 的开发工具包,bison、flex、gperf 都在里面,安装目录必须与 MSYS 完全一致,原因很简单:DTK 会把文件直接合并进 MSYS 的目录树,路径不一致等于白装,工具链根本找不到这些命令。
TDM-GCC 4.7.1-2 是当时 MinGW32 社区常用的集成发行版,比裸装 MinGW 省去一堆手工配置。安装时目标目录要指向 C:\MSys\1.0\mingw,这样 MSYS 里的 /mingw/bin 才能直接看到 gcc 和 g++,而 VLC 的 configure 会在路径里按这个名字找编译器。mingw-utils 0.3 是个 tar.gz 包,解压后把 bin 目录整体复制到 Msys\1.0 下,里面提供的是 strip、ar 等二进制工具的运行时,contrib 的 libtool 脚本会额外调用它们。
注意:MSYS 1.0 是 32 位运行环境。你的操作系统是 64 位也没关系,但它内部按 32 位路径处理,安装路径和用户目录不能有空格,也不能有中文。
2.2 Git、wget、AutoTools、Glib 与 pkg-config 的放置位置
这节本质上是在回答一个问题:configure 的功能检测会调用哪些外部命令,这些命令必须提前躺在 MSYS 能找到的位置。Git 要装到 Msys\1.0\mingw\Git,因为后续 contrib 拉取源码时 git.exe 的路径几乎写死在这里。wget 1.11.4-1 装到 Msys\1.0\mingw——手册在这一步原文写的是"将 Git 程序安装到",实际是 wget 的笔误,wget 的作用是 contrib 预编译包缺失时自动下载 tarball。
AutoTools 这边需要六个文件:autoconf-2.68-1、automake-1.11.1-1、libtool-2.4-1、libcrypt-1.1_1-2、perl-5.8.8-1、m4-1.4.14-1,全部解压合并到 Msys\1.0。版本不能换新的,原因很现实:2.0.4 的 configure.ac 用的是 automake 1.11 时代的旧语法,新版 automake 会把 AM_INIT_AUTOMAKE 的参数校验直接变成 fatal error;libtool 2.4 与 MSYS 运行时的配对也是当年测试过的组合,升级 libtool 容易引入 libltdl 加载异常。
Glib 和 pkg-config_0.23-3、gettext-runtime_0.18.1.1-2 同样解压到 Msys\1.0。pkg-config 是 VLC 插件系统找 glib、gtk、libxml2 的 .pc 文件的入口,没有它 configure 会跳过一大堆插件的检测。下表是完整的环境清单,照着版本号找文件就对了。
| 组件 | 手册使用版本 | 放置位置 | 作用 |
|---|---|---|---|
| MSYS | 1.0.11 | C:\MSys | POSIX 运行层 |
| MSYS-DTK | 1.0.1 | C:\MSys | bison/flex 等开发工具 |
| TDM-GCC | 4.7.1-2 | C:\MSys\1.0\mingw | C/C++ 编译器 |
| mingw-utils | 0.3 | Msys\1.0 | strip/ar 等二进制工具 |
| autoconf | 2.68-1 | Msys\1.0 | 生成 configure |
| automake | 1.11.1-1 | Msys\1.0 | 生成 Makefile.in |
| libtool | 2.4-1 | Msys\1.0 | 共享库链接机制 |
| perl | 5.8.8-1 | Msys\1.0 | autotools 运行依赖 |
| m4 | 1.4.14-1 | Msys\1.0 | 宏展开器 |
| pkg-config | 0.23-3 | Msys\1.0 | 解析 .pc 文件 |
2.3 手工补件:whoami、coreutils、libiconv 与 msys.bat 的 PKG_CONFIG_PATH
这一个环境里有三个必须手工补的"小零件",缺一个 configure 都会莫名其妙断掉。第一个是 whoami:在 C:\MSys\1.0\bin 下新建一个无扩展名的文件,名字就叫 whoami,里面只有一行内容:
echo '<username>'把<username>替换成你当前的 Windows 用户名。configure 在生成 config.h 时要取用户名拼进 VLC_COMPILED_BY 常量,而 MSYS 基础包没有完整的 whoami 实现,于是手册用一个假命令顶替。注意单引号要保留,这样 echo 输出时不会把用户名当参数解析。
第二个是 coreutils、libiconv、libintl 三个 MSYS 扩展包,从 SourceForge 的 MSYS/BaseSystem 页面下载对应版本,解压后把文件复制到 Msys\1.0\bin。libintl 缺失是 configure 阶段最常见的 "cannot find libintl" 元凶,iconv 缺失则会在编译某些 access 模块时出现链接错误。
第三个是修改 msys.bat。在if "%1" == "GOTO:" goto %2这一行上面,加上一行环境变量设置:
set PKG_CONFIG_PATH=/home/jb/vlc-2.0/contrib/i586-mingw32msvc/lib/pkgconfig这行代码的作用是每次启动 MSYS 时自动把 pkg-config 的搜索路径指向后面要解压的 contrib 预编译库。注意这里用的是正斜杠路径,由 MSYS 内部转换成 Windows 路径,pkg-config 自己能处理。不要把它加进 Windows 系统环境变量,因为这是 MSYS 风格路径,放进系统变量会干扰其他 Windows 程序对 PKG_CONFIG_PATH 的解析。
3. 源码接进 MSYS:contrib 预编译库与三个必改文件
VLC 的依赖库都是靠 contrib 体系管理起来的。2.0.4 有两条路:让 contrib 自动下载每个依赖的源码并现场编译,或者直接用官方预编译包 vlc-contrib-i586-mingw32msvc-latest.tar.bz2。手册走的是后者,省掉几十个依赖库的源码编译时间,但代价是路径非常挑剔,下面的每个修改都是为预编译包铺路。
3.1 contrib 目录规划与 bootstrap/Makefile 路径改写
先规划目录:在 /home/jb 下创建 vlc-2.0 目录,把 VLC 源码解压进去,再把 vlc-contrib-i586-mingw32msvc-latest.tar.bz2 复制到 contrib 目录下;如果 contrib 目录里没有这个压缩包,bootstrap 阶段会自动下载。用户目录之所以必须是 jb,是因为手册第 11 步的 bootstrap 和后面一批 Makefile 都硬编码了 /home/jb 这个路径,换成别的用户名会拉到不存在的位置。
修改 contrib 下的 bootstrap 文件,两处:
# 文件: /home/jb/vlc-2.0/contrib/bootstrap BUILD=i586-mingw32msvc # 将文件中所有 "../../contrib/" 替换成 "../contrib" sed -i 's#../../contrib/#../contrib/#g' bootstrap这里的 BUILD 指定 MinGW 三元组,决定后面 Makefile 生成的目标目录名;sed 替换把两层的相对路径引用改成单层,是因为手册把 contrib 从源码树内移到了与源码根目录平级的位置。不改的话,bootstrap 去找 tarballs 时路径不存在,make prebuilt 会直接失败。
执行./bootstrap生成 Makefile 之后,打开 Makefile 再改一段路径配置:
TOPSRC ?= ../contrib TOPDST ?= .. SRC := $(TOPSRC)/src TARBALLS := $(TOPSRC)/tarballs PATH := $(abspath ../extras/tools/build/bin):$(PATH)TOPSRC 和 TOPDST 决定依赖源码包和 tar 包的位置,SRC、TARBALLS 是 contrib 内部约定的两个子目录,PATH 把 extras/tools/build/bin 提前是为了让 contrib 构建能找到 perl、wget 等工具。这三处与 bootstrap 里的修改是配套的,改完执行make prebuilt,解压预编译 contrib 并打印处理记录,看到 completed 信息即可继续。
3.2 三个必改文件:vlc_windows_interfaces.h、configure.ac、configure.sh
这三个文件不改,configure 阶段也能蒙混过去,但会在编译中段或链接阶段炸出来。第一个是源码里的 include/vlc_windows_interfaces.h,在#include <objbase.h>下面加一行:
#include <objbase.h> #undef small原因是 Windows SDK 的 windef.h 把small定义成了宏,而 VLC 源码里 small 是普通变量名,宏展开后编译器会报expected unqualified-id before 'small'。这个文件被大量模块间接 include,所以要在 objbase.h 之后统一取消宏,让宏先定义再被取消,顺序不能反。
第二个是 configure.ac。找到这行:
AC_DEFINE_UNQUOTED(VLC_COMPILE_HOST, "`hostname -f 2> /dev/null || hostname`", [host which ran configure])改成:
AC_DEFINE_UNQUOTED(VLC_COMPILE_HOST, "`hostname`", [host which ran configure])GNU 环境下的 hostname 支持-f取 FQDN,MSYS 自带的 hostname.exe 不支持,-f参数会让命令返回错误。AC_DEFINE_UNQUOTED 会把这个值写进 config.h 作为 VLC_COMPILE_HOST,不改不影响编译,但生成的版本信息里会带一条诡异字符串。这个问题的隐蔽之处在于 configure 是交叉编译环境,不会因为 hostname 失败中止。
第三个是 extras/package/win32/configure.sh。在--disable-dirac行下面追加三行:
--host=i586-pc-mingw32msvc --with-contrib=contrib/i586-mingw32msvc --disable-nls--host告诉 configure 我们是在给 32 位 Windows 目标生成代码;--with-contrib把预编译 contrib 的 i586-mingw32msvc 目录暴露给 configure 的库探测;--disable-nls先跳过 gettext 的多语言构建,省掉一大批国际化和编码问题。想在产物里带中文语言包,后期可以单独把 nls 打开重跑 configure,不建议第一次就开。
3.3 change_prefix.sh 执行时机与 libpng 软链接修复
make prebuilt 完成后,进入 contrib\i586-mingw32msvc 目录,修改 change_prefix.sh,在文件处理循环里加一行打印:
echo "processing: $file"这个脚本是跨平台的。在 macOS 上,if 分支里的 otool 和 install_name_tool 用来改写 Mach-O 二进制里的动态库路径;在 Windows/MSYS 上这两个命令不存在,实际生效的是 elif 分支:对扩展名是 .pc 和 .la 的文本文件做前缀替换。预编译包在其他机器上生成时库路径是写死的,change_prefix.sh 做的事就是路径归一化。echo 那行加在 for 循环体开头,只是让我们在长达一两小时的执行过程中能看到进度,属于无副作用的安全修改。
执行sh change_prefix.sh,耐心等。期间如果出现mv: cannot move './change_prefix.sh.tmp' to './change_prefix.sh': Permission denied,属于 Windows 下临时文件被句柄占用的假阳性,忽略即可;中断了重跑一次 sh 会继续处理剩余文件。
最后一步修复版本差异:进入 contrib\i586-mingw32msvc\lib,把 libpng15.a 和 libpng15.la 重命名为 libpng.a 和 libpng.la。原因是预编译包里的 libtool 按带版本号规则命名,但 VLC 源码树里有模块的 Makefile 直接写-lpng,链接器按 libpng15 找不到匹配项,改名是为了让-lpng能命中。
4. 编译实操:bootstrap、configure 与打包前必须动的 Makefile
环境补齐、源码文件改完,终于进入真正的编译阶段。但别急着 make,编译前还有一次文件准备,以及两处 Makefile 修补,否则 make 会在很后面才崩,浪费几个小时。
4.1 从 bootstrap 到 configure 的命令序列与参数
编译源码之前,要把一批可执行文件复制到 Msys\1.0\bin:Git.exe、moc.exe、rcc.exe、uic.exe、7z.exe(或 zip.exe)、msys-intl-8.dll,其中 moc.exe、rcc.exe、uic.exe 这三个在源码树 contrib\i586-mingw32msvc\bin 目录下。复制完后,把 contrib\i586-mingw32msvc\bin 下不带后缀的 moc、rcc、uic 三个文件改名为 moc1、rcc1、uic1。
这个操作背后的逻辑是:configure 生成的 Makefile 里写的是 Unix 风格无后缀的 moc、rcc、uic 命令,而 Windows 下同目录同时存在无后缀文件和有后缀 .exe 文件时,shell 的解析顺序会不确定。改名保留原文件,让两边各司其职。都做完建议重启一次系统,把 MSYS 进程持有的文件句柄锁清干净再往下走。
正式命令序列:
cd /home/jb/vlc-2.0 PATH=/home/jb/vlc-2.0/contrib/i586-mingw32msvc/bin:$PATH cd vlc ./bootstrap sh extras/package/win32/configure.sh第一条进入工作区;第二条把 contrib 预编译库的 bin 目录提前到 PATH 最前面,保证 configure 探测 Qt、glib 等库时能找到刚复制进来的 exe;第三条进入源码根目录;第四条执行 bootstrap 生成 configure 脚本;第五条通过官方 win32 配置脚本跑 configure,它内部已经带上 3.2 节加的三个参数。如果想追加自定义选项,直接在 configure.sh 后面附加,例如--disable-lua、--disable-upnp。
--disable-lua在 Windows 构建里很实用:2.0.4 的 Lua 扩展经常因为 Lua 头文件路径问题失败,不做二次开发就关掉。--disable-upnp同理,UPnP 的 SDK 探测很挑版本,失败会导致 configure 阶段警告甚至中止。
4.2 access 模块与根 Makefile 的四处修改
configure 跑完后先别急着重启 make,打开两个 Makefile 修补再编译。
第一处:modules/access/Makefile,搜索@DLOPEN_LDFLAGS@并删除。2.0.4 的 access 模块 Makefile 里这个占位符没有对应的 configure 变量定义,链接时 gcc 会把@DLOPEN_LDFLAGS@当一个输入文件名传给 ld,报错No such file or directory。删掉后 access 模块的链接命令恢复干净。
第二处:根目录 Makefile 的package-win-common:目标,一共四处修改。第一处是 sed 命令:
# 原:sed -i.orig -e 's%share/osdmenu%osdmenu%g' -e 's%/%\\%g' "$$file"; \ # 改:sed -e 's%share/osdmenu%osdmenu%g' -e 's%/%\\%g' "$$file"; \-i.orig表示原地修改并生成 .orig 备份,但打包阶段输入的只读文件会触发Text file busy,改用sed -e输出到标准流就绕开了这个权限问题。
第二处是 manifest 路径:
# 原:cp "$(top_builddir)/npapi-vlc/npapi /npvlc.dll.manifest" "$(win32_destdir)/" # 改:cp "$(top_builddir)/npapi-vlc/npapi/package/npvlc.dll.manifest" "$(win32_destdir)/"npvlc.dll.manifest 实际位于 npapi/package 子目录,原路径少了一层。
第三处是 gcc 运行时库的拷贝:
# 原:cp $${gcc_lib_dir}/libstdc++-6.dll $${gcc_lib_dir}/libgcc_s_sjlj-1.dll "$(win32_destdir)/" ; true # 改:cp "/mingw/bin/libstdc++-6.dll" "/mingw/bin/libgcc_s_sjlj-1.dll" "$(win32_destdir)/" ; truegcc_lib_dir这个变量在 2.0.4 的 Makefile 里没有赋值,展开后是空串;/mingw/bin是 TDM-GCC 在 MSYS 里的固定位置,直接写死比猜变量稳。
第四处是 activex SDK 文件:
# 原:cd $(top_builddir)/npapi-vlc && cp activex/README.TXT share/test.html "$(win32_destdir)/sdk/activex/" # 改:cd $(top_builddir)/npapi-vlc && cp activex/README.TXT share/test/test.html "$(win32_destdir)/sdk/activex/"test.html 在源码树里的实际位置是 share/test/ 下的子目录。
4.3 编译与打包:make、package-win32 系列命令
前面全部就位后,执行:
make首次编译耗时很长,日志刷屏是正常的。失败时先看第一条 error,通常是某个模块的库路径不对。两个最常见的自救手段:回到预编译 contrib 章节换最新包;或者在 configure 阶段加--disable-upnp后make clean && make。UPnP 检测是 2.0.4 在 Windows 上最容易挂的点,没有硬需求就关掉。
编译通过后,打包命令按需求选:
| 命令 | 产物 | 适用场景 |
|---|---|---|
| make package-win32-common | vlc-x.x.x/ 目录 | 自用、拷贝分发 |
| make package-win32-zip | zip 压缩包 | 网络分发 |
| make package-win32-7zip | 7z 压缩包 | 网络分发 |
| make package-win32 | NSIS 安装程序 | 需 NSIS 在默认安装位置 |
| make package-win32-common-debug | 带调试符号的目录 | gdb 调试 |
注意 1.2 分支这些目标名略有变化,运行grep ':' Makefile | grep package-win32看看当前 Makefile 实际提供了哪些目标,再决定用哪个。
5. 避坑指南:路径、权限与链接问题的 5 条血泪经验
整个流程我完整跑过好几遍,下面这几条是每次换机器都会再犯的。每条按"现象 → 原因 → 解决"写,照着排查即可。
5.1 configure 报 cannot find libintl
现象:configure 进行到 gettext 检测时中断,提示 libintl 找不到。
原因:gettext-runtime 没解压进 Msys\1.0\bin,或者 msys.bat 里 PKG_CONFIG_PATH 指到了不存在的目录。
解决:重新解压 gettext-runtime_0.18.1.1-2 和 libiconv 对应版本到 Msys\1.0\bin;确认 msys.bat 里的 set PKG_CONFIG_PATH 路径拼写正确。改完重开 msys.bat,先执行pkg-config --list-all | grep glib确认能列出内容再继续。
5.2 编译中段报 "expected unqualified-id before 'small'"
现象:modules/access 等目录编译到某个 cpp 文件时突然崩掉。
原因:Windows 头文件里small被定义为宏,展开后破坏了 VLC 源码里的合法变量名。
解决:确认 vlc_windows_interfaces.h 里#undef small加在#include <objbase.h>之后。顺序很重要,宏要先定义再取消。
5.3 change_prefix.sh 中途 Permission denied
现象:mv 临时文件到 change_prefix.sh 时失败,脚本中断。
原因:Windows 文件被其他进程句柄占用,或者杀毒软件扫描拦截了 rename 操作。
解决:假阳性,忽略并重跑sh change_prefix.sh,脚本会继续处理剩余文件。注意必须在 msys.bat 打开的窗口里执行,在 cmd 里直接调 sh 的权限模型不对,同样会触发该问题。
5.4 package-win32-common 找不到 npvlc.dll.manifest 或 test.html
现象:make 进入打包阶段时报 manifest 文件不存在。
原因:打包脚本写的路径是旧目录结构。
解决:按 4.2 节的第 2、4 处修改。改完直接重跑make package-win32-common,不会重新编译全部源码,只重做打包阶段。
5.5 username 以 u 或 x 开头时 config.h 的 VLC_COMPILED_BY 无效
现象:vlc.exe 能编译出来,但版本信息乱码,或启动异常。
原因:C 字符串里\u、\x是转义序列。configure 把用户名写进 VLC_COMPILED_BY 常量时没做转义处理,编译器按转义符解析出了不可读字符。
解决:手工打开 config.h,找到 VLC_COMPILED_BY,把里面的单反斜杠全部改成双反斜杠\\,保存后重新 make,不用整个工程重来。
注意:MSYS 安装路径和用户目录不要有空格和中文。configure 里有大量路径拼接依赖 shell 分词,空格会直接翻车。
6. 验证产物与二次进阶:确认编译成功后再裁剪
6.1 产物验证与绿色版使用
make package-win32-common 完成后,源码目录下会出现 vlc-2.0.4 这样的子目录,里面 vlc.exe、libvlc.dll、plugins 目录都在。第一件事是验证主程序:
./vlc.exe --version能看到版本号和编译时间就说明主程序正常。然后再看 plugins 目录里有没有上百个 .dll——VLC 的插件是按需加载的,缺了某个 dll 编译时不会报错,运行到对应功能才失败,所以列表里文件的完整性比单个 exe 能否启动更重要。
这个目录本身就是免安装版本:拷到其他 Windows 机器直接运行 vlc.exe,不写注册表。官方没有单独的 portable 包,自编译的目录恰好可以当绿色版用,这也是很多人自己走编译流程的直接动机。
6.2 二次进阶:调试版与裁剪
调试用make package-win32-common-debug,生成的目录保留符号文件,在 MSYS 里跑gdb vlc.exe就能看到崩溃栈。裁剪则回到 configure 阶段:追加--disable-lua、--disable-upnp等参数缩小插件体积,不需要 Qt 界面就把 modules/gui/qt 整个排除掉,依赖越少,后续升级维护越省心。
从那以后我每次编译 VLC,都会强制走一遍"先检查 config.h 的用户名转义、再跑 make package-win32-common、最后执行 vlc.exe --version"这三步检查。改配置比改代码更容易翻车,稳定复现比追求新版本重要。希望帮到你。
本文还有配套的精品资源,点击获取