SDL 不是播放器,是「窗口 + 显卡 + 事件」的跨平台封装。 它只认像素,不认协议、不认时间戳——所以它和 FFmpeg 分工刚好不重叠。
前两节把帧拿到手了,接下来缺一个「把帧放上屏幕」的东西。 选 SDL 而不是 Qt/MFC,是因为它最小:两个函数就能显示一帧,没有控件、没有布局、 没有事件循环的复杂度。 Qt 那一套在 3.4 讲,那一节的重点是信号槽,不是渲染。
一、它到底封装了什么
SDL 底下对接的是各平台自己的东西,上面给你一套统一接口。
| 平台 | 窗口 | 渲染 |
|---|---|---|
| Windows | Win32 窗口 | Direct3D/OpenGL |
| Linux | X11/Wayland | OpenGL/软件 |
| macOS | Cocoa | Metal/OpenGL |
这张表就是「跨平台」的全部含义:你的代码不碰任何一个平台 API,SDL 在编译时选一套。 代价是它只能做「大家都有的功能」——SDLRenderer 没有抗锯齿、没有变色、没有混合模式, 这些得自己上 OpenGL。
二、视频驱动和渲染驱动是两回事
D28 数出来:视频驱动 2 个,渲染驱动 6 个。 这两个数不是一回事。
| 概念 | 管什么 | 沙箱实测 |
|---|---|---|
| 视频驱动 | 怎么开窗口、怎么收事件 | windows/dummy |
| 渲染驱动 | 怎么把像素送进显卡 | 6 个,5 硬件 1 软件 |
「视频驱动只有 2 个」不是 SDL 弱,是因为开了 dummy 之后,平台相关的只剩 windows, 另一个是万能兜底。 而渲染驱动有 6 个,说明它们和窗口是解耦的——同一套绘制代码能在 D3D 和 OpenGL 上跑。
这里有个必须知道的坑:dummy 下能列出 direct3d11,但真正建渲染器时会退到 software。
「列表里有」不等于「能用」。
三、为什么选 SDL 做教学载体
因为它把「播放器」这件事削到只剩骨架。
| 框架 | 显示一帧要几行 | 适合 |
|---|---|---|
| SDL2 | 三行 | 看清原理 |
| Qt | 五行(还要 moc) | 做界面 |
| MFC | 十几行(还有设备上下文) | 维护老项目 |
第一行那三行就是 D32 干的事:建纹理、UpdateTexture、RenderCopy。
没有控件、没有布局、没有资源文件。
这正是学习时想要的:屏幕上出现东西之前,你要理解的东西越少越好。
四、版本号那点事
D28 报出运行时 2.32.10、编译时 2.30.10——不一致。
SDL 有两条版本线在这里交叉:
| 概念 | 从哪来 | 不一致的后果 |
|---|---|
| 运行时版本 | SDL_GetVersion,实际的 DLL | 函数表可能错位 |
| 编译时版本 | 头文件里的宏 | 调不存在的函数 |
两者差一个小版本一般没事(SDL 承诺主版本内兼容),差一个大版本一定出事: SDL1 和 SDL2 的函数名前缀都不一样。 pysdl2 的做法是启动时甩一条警告,看到别忽略。
这条经验放之四海:任何「头文件和动态库不是同一份」的场景,症状都是「随机崩溃」, 而不是「编译失败」。
五、动手 Demo:初始化 SDL,数一遍驱动
开一个 dummy 窗口,把版本、视频驱动、渲染驱动全列出来。 这一段是环境体检:后面所有 SDL 的 Demo 都建立在这上面。
import sdl2, ctypes
sdl2.SDL_Init(sdl2.SDL_INIT_VIDEO | sdl2.SDL_INIT_TIMER)
v = sdl2.SDL_version()
sdl2.SDL_GetVersion(ctypes.byref(v))
print(v.major, v.minor, v.patch) # 运行时
print(sdl2.SDL_MAJOR_VERSION, sdl2.SDL_MINOR_VERSION) # 编译时
for i in range(sdl2.SDL_GetNumVideoDrivers()):
print(sdl2.SDL_GetVideoDriver(i))
for i in range(sdl2.SDL_GetNumRenderDrivers()):
info = sdl2.SDL_RendererInfo()
sdl2.SDL_GetRenderDriverInfo(i, ctypes.byref(info))
print(info.name, info.flags)
实跑输出:
--------------------------------------------------------------------
SDL2 是什么——先把它初始化,再数数它带几个驱动
--------------------------------------------------------------------
**SDL 不是播放器,是「跨平台的窗口和显卡接口」。** 它只负责把一帧像素推到屏幕上,
解码和同步一概不管。
SDL 版本:**2.32.10**
编译时版本:2.30.10
**上面这两个数就不一样**:运行时 2.32.10,编译时 2.30.10。这台机器上就是不一致的,
pysdl2 启动时会甩一条警告出来——**看到这条警告别忽略**,函数表错位导致的崩溃很难查。
视频驱动共 **2 个**:
windows/dummy
当前用的是:**dummy**(沙箱里强制设了 dummy)
渲染驱动共 **6 个**:
渲染驱动 硬件加速 纯软件
--------------------------------------
direct3d 是 —
direct3d11 是 —
direct3d12 是 —
opengl 是 —
opengles2 是 —
software — 是
**这张表说明一件重要的事**:`dummy` 视频驱动下能列出硬件渲染驱动,
但真正建窗口时会退到 software。
**所以「驱动列表里有 direct3d11」不等于「能用 direct3d11」**——要看建立之后实际拿到的是哪个。
2.32.10 对 2.30.10,这台机器上就是不一致的。我用的是 pysdl2 0.9.17 配 pysdl2-dll 2.32.10, 两条版本线本来就没法完全对齐,这也是那行警告的来源。 实际跑下来没出问题,因为用到的都是稳定 API。但这是运气,不是保证——上线前还是对齐一下。
渲染驱动那张表里,direct3d 到 opengles2 全是硬件加速,只有 software 是纯软件。 但 dummy 窗口下实际会退到 software——所以「这台机器能硬件加速」这句结论, 在沙箱里是验不出来的。 我只能报「列表里有」,不能报「真的用上了」。 这一点必须说清。
30 秒实验
- 把环境变量
SDL_VIDEODRIVER改成windows,看会怎样——沙箱里没有真实桌面会话, 大概率建窗口失败。 - 用
SDL_GetNumRenderDrivers的索引逐个建渲染器,看哪个真能建成。 - 查一下
SDL_GetRevision(),它是源码版本号,和SDL_GetVersion不是一回事。
常见误区
- 以为视频驱动越多越好。 沙箱里只有 windows 和 dummy,但一样能跑——驱动数量反映的是平台, 不是能力。
- 以为列表里有 direct3d11 就能用。 dummy 下会退到 software, 「能列出」和「能建起来」是两件事。
- 以为 SDL 版本不一致一定崩。 主版本内不一致通常没事,但看到警告别忽略——崩起来是随机的。
自测三问(点开对答案)
1. 视频驱动和渲染驱动分别管什么? —— 视频驱动管开窗口和收事件(windows/dummy), 渲染驱动管把像素送进显卡(direct3d11/opengl/software)。 2. 为什么沙箱里能列出 direct3d11 却用不上? —— 驱动列表来自编译进来的支持,建立渲染器时才真正初始化。 dummy 窗口下没有真实的显示设备,会退到 software。 3. 运行时版本和编译时版本不一致,为什么会崩? —— 头文件声明的函数布局和 DLL 里实际的不一样,按旧布局调用就会错位——症状是随机崩溃, 不是编译失败。下一节开始配环境:先把 Windows 上的 VS 走一遍,再说 Qt 和 Linux。
- ☐ (本节完,下一节 03.03.02 VS 2015搭建SDL2开发环境)